mirror of
https://github.com/OpenNebula/one.git
synced 2026-11-03 02:28:46 +09:00
This PR adds Group VLAN authorization rules for Virtual Network self-provisioning
through VN Template instantiation and VNET updates derived from a VN Template.
The feature lets administrators define VLAN usage policies at group level. These policies are
evaluated when users instantiate VN Templates or update VNETs that were instantiated from a VN
Template. Direct VNET allocation is not treated as the tenant self-provisioning path; the intended
multi-tenant model is to expose network creation through reservations and VN Template
instantiation, while direct VNET allocation remains an admin-controlled operation.
Group VLAN rules are stored under `VLAN_RULES` as `RULE` vector attributes.
Rule format:
```txt
RULE = [
ID = "5,6,100-200",
SCOPE = "CVLAN",
VNTEMPLATE = "10,20-30"
]
Fields:
- ID: VLAN values allowed by the rule. Supports single values and ranges, comma-separated.
- Examples: "5", "5,6,100-200"
- SCOPE: VLAN attribute scope controlled by the rule.
- Supported scopes: VLAN_ID, OUTER_VLAN_ID, CVLAN, VLAN_TAGGED_ID, ANY
- VNTEMPLATE: optional VN Template IDs where the rule applies.
- Supports single values and ranges, comma-separated.
- -1 or missing value means the rule applies to any VN Template.
Examples:
RULE = [
ID = "100-200",
SCOPE = "VLAN_ID",
VNTEMPLATE = "10"
]
Allows VLAN_ID values from 100 to 200 when instantiating or updating VNETs derived from VN Template
10.
RULE = [
ID = "300,400-450",
SCOPE = "ANY",
VNTEMPLATE = "-1"
]
Allows the listed VLAN IDs/ranges for any supported VLAN scope and any VN Template.
Rules are parsed into an internal structured representation when group VLAN data is loaded or
updated. Each rule is normalized into:
- scope
- VLAN ID ranges
- VN Template ID ranges
This avoids reparsing raw template attributes during authorization checks.
Validation is applied to these input scopes:
- VLAN_ID
- OUTER_VLAN_ID
- CVLAN
- VLAN_TAGGED_ID
VLAN_ID and OUTER_VLAN_ID are validated as single integer values. CVLAN and VLAN_TAGGED_ID accept
range expressions such as 5,6-10.
For each requested VLAN value or range:
1. The input is parsed into one or more test ranges.
2. Rules are scanned for matching scope, including ANY.
3. Rules are filtered by VN Template ID:
- empty/-1 VNTEMPLATE matches all templates
- explicit ranges match only included template IDs
4. A requested range is accepted only if it is fully covered by a single rule range.
5. The check passes when all requested ranges are covered.
Automatic VLAN selection is rejected when the user belongs to a group with VLAN rules matching the
target VN Template, including global rules. This avoids bypassing explicit group VLAN policy
through automatic allocation.
VNETs instantiated from VN Templates now preserve the source VN Template ID as a first-class VNET
attribute:
<VNET>
...
<TEMPLATE_ID>...</TEMPLATE_ID>
...
</VNET>
TEMPLATE_ID = -1 means the VNET was created directly and was not instantiated from a VN Template.
This ID is immutable from user template updates:
- it is removed from the VNET template body on insert/update
- it is serialized as a top-level VNET attribute
- update operations use the stored value to validate VLAN authorization
A database migrator moves legacy TEMPLATE/TEMPLATE_ID values to top-level VNET/TEMPLATE_ID.
The tenant self-provisioning model for VNETs is based on controlled entry points:
- VNET reservations
- VN Template instantiation
Group VLAN authorization is enforced on VN Template instantiation and on updates to VNETs that were
instantiated from a VN Template.
Direct VNET allocation is not considered the tenant-facing self-provisioning path. It should remain
admin-controlled, as unrestricted VNET creation can expose other sensitive network attributes such
as PHYDEV, BRIDGE, and driver-specific configuration in addition to VLAN IDs.
Existing VNETs created from VN Templates may have the source template ID stored inside TEMPLATE/
TEMPLATE_ID. The migration step moves that value to top-level VNET/TEMPLATE_ID and removes it from
the template body.
This PR also updates the AR operations (add/del/update) so tenants can easily managed the VNET instantiations.
Existing directly-created VNETs will have no source VN Template and are treated as TEMPLATE_ID =
-1.
Existing group objects without VLAN rules are initialized with an empty VLAN_RULES section,
preserving current behavior until rules are explicitly configured.
Configuration compatibility:
- VLAN_ID, CVLANS, and AR/VLAN_ID are no longer expected to be handled through generic VNET
restricted attributes for the VN Template instantiation flow.
- OneCfg migration/update handling for configuration files will be addressed separately before
release.
co-authored-by: Pavel Czerny <pczerny@opennebula.io>
co-authored-by: Miguel E. Ruiz <mruiz@opennebula.io>
co-authored-by: dcarracedo <dcarracedo@opennebula.io>
296 lines
8.6 KiB
C++
296 lines
8.6 KiB
C++
/* -------------------------------------------------------------------------- */
|
|
/* Copyright 2002-2026, OpenNebula Project, OpenNebula Systems */
|
|
/* */
|
|
/* Licensed under the Apache License, Version 2.0 (the "License"); you may */
|
|
/* not use this file except in compliance with the License. You may obtain */
|
|
/* a copy of the License at */
|
|
/* */
|
|
/* http://www.apache.org/licenses/LICENSE-2.0 */
|
|
/* */
|
|
/* Unless required by applicable law or agreed to in writing, software */
|
|
/* distributed under the License is distributed on an "AS IS" BASIS, */
|
|
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
|
|
/* See the License for the specific language governing permissions and */
|
|
/* limitations under the License. */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
#ifndef GROUP_VLANS_H_
|
|
#define GROUP_VLANS_H_
|
|
|
|
#include "ObjectSQL.h"
|
|
#include "Template.h"
|
|
#include "OneDB.h"
|
|
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
class ObjectXML;
|
|
|
|
/**
|
|
* The GroupVlans class.
|
|
*/
|
|
class GroupVlans : public ObjectSQL
|
|
{
|
|
public:
|
|
GroupVlans(): ObjectSQL(), oid(-1), vlan_template(false, '=', "VLAN_RULES") {};
|
|
|
|
virtual ~GroupVlans() {};
|
|
|
|
/**
|
|
* Parsed representation of a group VLAN rule. It normalizes the SCOPE,
|
|
* VLAN ID ranges, and optional VN Template ranges so validation and
|
|
* authorization checks do not need to parse the raw VectorAttribute.
|
|
*/
|
|
class VlanRule
|
|
{
|
|
public:
|
|
/**
|
|
* VLAN rule scope
|
|
*/
|
|
enum Scope
|
|
{
|
|
VLAN_ID = 0,
|
|
OUTER_VLAN_ID = 1,
|
|
CVLAN = 2,
|
|
VLAN_TAGGED_ID = 3,
|
|
ANY = 4,
|
|
UNDEFINED = 255
|
|
};
|
|
|
|
/**
|
|
* Return the string representation of a Scope
|
|
* @param scope the scope
|
|
* @return the string
|
|
*/
|
|
static std::string scope_to_str(Scope scope)
|
|
{
|
|
switch (scope)
|
|
{
|
|
case VLAN_ID: return "VLAN_ID" ; break;
|
|
case OUTER_VLAN_ID: return "OUTER_VLAN_ID" ; break;
|
|
case CVLAN: return "CVLAN" ; break;
|
|
case VLAN_TAGGED_ID: return "VLAN_TAGGED_ID" ; break;
|
|
case ANY: return "ANY" ; break;
|
|
default: return "";
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Return the Scope for its string representation
|
|
* @param str_scope string representing the scope
|
|
* @return the Scope
|
|
*/
|
|
static Scope str_to_scope(std::string& str_scope);
|
|
|
|
VlanRule() = default;
|
|
|
|
/**
|
|
* Parses a range into its vector format
|
|
*
|
|
* @return false if the range is not valid
|
|
*/
|
|
static bool parse_range(const std::string& range_s,
|
|
std::vector<std::pair<int, int>>& ranges,
|
|
std::string& error_str);
|
|
|
|
/**
|
|
* Parse a VLAN rule from a VectorAttribute.
|
|
* @param rule group VLAN rule, including ID, SCOPE and optionally
|
|
* VNTEMPLATE
|
|
* @param error_str error message if the rule cannot be parsed
|
|
*
|
|
* Example: RULE = [ ID="5,6,100-200", SCOPE="CVLAN" ] will generate
|
|
* ranges [{5,5}, {6,6}, {100,200}] and scope CVLAN.
|
|
*
|
|
* Note: reversed ranges are considered valid, e.g. 300-100 is parsed
|
|
* as {100,300}.
|
|
*/
|
|
bool build_rule(const VectorAttribute * rule, std::string& error_str);
|
|
|
|
Scope scope() const
|
|
{
|
|
return _scope;
|
|
}
|
|
|
|
const std::vector<std::pair<int, int>>& ranges() const
|
|
{
|
|
return _ranges;
|
|
}
|
|
|
|
const std::vector<std::pair<int, int>>& vntemplates() const
|
|
{
|
|
return _vntemplates;
|
|
}
|
|
|
|
private:
|
|
static bool parse_range(const VectorAttribute* rule,
|
|
const std::string& attr,
|
|
std::vector<std::pair<int, int>>& ranges,
|
|
std::string& error_str);
|
|
|
|
Scope _scope = UNDEFINED;
|
|
|
|
std::vector<std::pair<int, int>> _ranges;
|
|
|
|
std::vector<std::pair<int, int>> _vntemplates;
|
|
};
|
|
|
|
|
|
/**
|
|
* Reads the ObjectSQL (identified with its OID) from the database.
|
|
* @param oid the Group oid
|
|
* @param db pointer to the db
|
|
* @return 0 on success
|
|
*/
|
|
int select(int _oid, SqlDB *db)
|
|
{
|
|
oid = _oid;
|
|
return select(db);
|
|
};
|
|
|
|
/**
|
|
* Writes the VLAN rules in the database.
|
|
* @param db pointer to the db
|
|
* @return 0 on success
|
|
*/
|
|
int insert(int _oid, SqlDB *db, std::string& error_str)
|
|
{
|
|
oid = _oid;
|
|
return insert(db, error_str);
|
|
};
|
|
|
|
/**
|
|
* Writes/updates the VLAN rules fields in the database.
|
|
* @param db pointer to the db
|
|
* @return 0 on success
|
|
*/
|
|
int update(int _oid, SqlDB *db)
|
|
{
|
|
oid = _oid;
|
|
return update(db);
|
|
}
|
|
|
|
/**
|
|
* Removes the VLAN rules from the database.
|
|
* @param db pointer to the db
|
|
* @return 0 on success
|
|
*/
|
|
int drop(SqlDB * db) override;
|
|
|
|
/**
|
|
* Bootstraps the database table for group vlans
|
|
* @return 0 on success
|
|
*/
|
|
static int bootstrap(SqlDB * db)
|
|
{
|
|
std::ostringstream oss(one_db::group_vlans_db_bootstrap);
|
|
|
|
return db->exec_local_wr(oss);
|
|
};
|
|
|
|
/**
|
|
* Set the VLAN rules
|
|
* @param tmpl contains the VLAN rules
|
|
* @param error describes error when setting the rules
|
|
*
|
|
* @return 0 on success, -1 otherwise
|
|
*/
|
|
int set(Template *tmpl, std::string& error);
|
|
|
|
/**
|
|
* Generates a string representation of the VLAN rules in XML format
|
|
* @param xml the string to store the XML
|
|
* @return the same xml string to use it in << compounds
|
|
*/
|
|
std::string& to_xml(std::string& xml) const;
|
|
|
|
/**
|
|
* Builds VLAN rules object from an XML string
|
|
* @param xml_str the XML string
|
|
* @return 0 if success
|
|
*/
|
|
int from_xml(const std::string& xml_str);
|
|
|
|
/**
|
|
* Check if any VLAN rule applies to a VN Template ID
|
|
* @param vntemplate_id the VN Template ID
|
|
* @return true if there are rules for this VN Template
|
|
*/
|
|
bool has_vntemplate_rules(int vntemplate_id) const;
|
|
|
|
/**
|
|
* Check if a value for the given scope is allowed by these rules
|
|
* @param scope VLAN rule scope
|
|
* @param value VLAN value or ranges
|
|
* @param vntemplate_id VN Template ID
|
|
* @param error_str error message if the input cannot be parsed
|
|
* @return true if allowed
|
|
*/
|
|
bool check(VlanRule::Scope scope,
|
|
const std::string& value,
|
|
int vntemplate_id,
|
|
std::string& error_str) const;
|
|
|
|
private:
|
|
/**
|
|
* Group oid. Must be set before a DB write operation
|
|
*/
|
|
int oid;
|
|
|
|
/**
|
|
* The VLAN rules template
|
|
*/
|
|
Template vlan_template;
|
|
|
|
/**
|
|
* Parsed VLAN rules
|
|
*/
|
|
std::vector<VlanRule> _rules;
|
|
|
|
/**
|
|
* Reads the ObjectSQL (identified with its OID) from the database.
|
|
* @param db pointer to the db
|
|
* @return 0 on success
|
|
*/
|
|
int select(SqlDB * db) override;
|
|
|
|
/**
|
|
* Writes the VLAN rules in the database.
|
|
* @param db pointer to the db
|
|
* @return 0 on success
|
|
*/
|
|
int insert(SqlDB *db, std::string& error_str) override
|
|
{
|
|
return insert_replace(db, false, error_str);
|
|
};
|
|
|
|
/**
|
|
* Writes/updates the VLAN rules fields in the database.
|
|
* @param db pointer to the db
|
|
* @return 0 on success
|
|
*/
|
|
int update(SqlDB *db) override
|
|
{
|
|
std::string error_str;
|
|
return insert_replace(db, true, error_str);
|
|
}
|
|
|
|
/**
|
|
* Callback function to read a GroupVlans object
|
|
*/
|
|
int select_cb(void *nil, int num, char **values, char **names);
|
|
|
|
/**
|
|
* Execute an INSERT or REPLACE Sql query.
|
|
*/
|
|
int insert_replace(SqlDB *db, bool replace, std::string& error_str);
|
|
|
|
/**
|
|
* Builds the internal representation of the VlanRules
|
|
*/
|
|
bool build_rules(std::string& error_str);
|
|
|
|
};
|
|
|
|
#endif /*GROUP_VLANS_H_*/
|