Policies in yaml
Implement functions allowing to define object_level policies in model's yaml file Change-Id: I4a4b70edf95c56d8dba7ee669d5ccc8bd387c4d8
This commit is contained in:
parent
224ebeab8f
commit
ecd29999cb
|
@ -1,4 +1,9 @@
|
|||
{
|
||||
"COMMENT": "This file is no longer needed, but for historical record !!!",
|
||||
"COMMENT": "The policy.json file in /etc/proton directory should contain",
|
||||
"COMMENT": "empty json object: {}",
|
||||
|
||||
"COMMENT": "This first part is moved to code in policies/base.py",
|
||||
"context_is_admin": "role:admin",
|
||||
"owner": "tenant_id:%(tenant_id)s",
|
||||
"admin_or_owner": "rule:context_is_admin or rule:owner",
|
||||
|
@ -9,6 +14,7 @@
|
|||
"regular_user": "",
|
||||
"default": "rule:admin_or_owner",
|
||||
|
||||
"COMMENT": "The rest of policies are defined in YAML",
|
||||
"create_ports": "rule:admin_or_network_owner",
|
||||
"get_ports": "rule:admin_or_owner",
|
||||
"update_ports": "rule:admin_or_network_owner",
|
||||
|
|
|
@ -13,14 +13,17 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import json
|
||||
import webob
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_policy import policy as oslo_policy
|
||||
# from oslo_utils import excutils
|
||||
from pecan import hooks
|
||||
from pecan.routing import lookup_controller
|
||||
|
||||
from gluon import constants as gluon_constants
|
||||
from gluon.particleGenerator.ApiGenerator import API_OBJECT_CLASSES
|
||||
from gluon import policy
|
||||
|
||||
# from gluon._i18n import _
|
||||
|
@ -37,7 +40,7 @@ class PolicyHook(hooks.PecanHook):
|
|||
if state.request.method not in ('GET', 'POST', 'PUT', 'DELETE'):
|
||||
return
|
||||
|
||||
method = gluon_constants.ACTION_MAP[state.request.method]
|
||||
method = generateMethod(state)
|
||||
|
||||
path_info = state.request.path_info
|
||||
|
||||
|
@ -49,15 +52,17 @@ class PolicyHook(hooks.PecanHook):
|
|||
if not resource:
|
||||
return
|
||||
|
||||
action = "%s_%s" % (method, resource)
|
||||
service = path_info.split("/")[2]
|
||||
action = "%s:%s_%s" % (service, method, resource)
|
||||
|
||||
gluon_context = state.request.context.get('gluon_context')
|
||||
|
||||
policy.init()
|
||||
|
||||
target = generateTarget(state, service, resource)
|
||||
try:
|
||||
policy.enforce(
|
||||
gluon_context, action, None)
|
||||
gluon_context, action, target)
|
||||
except oslo_policy.PolicyNotAuthorized as e:
|
||||
raise webob.exc.HTTPForbidden(str(e))
|
||||
|
||||
|
@ -65,3 +70,49 @@ class PolicyHook(hooks.PecanHook):
|
|||
# This method could be used for implementing access control
|
||||
# at the attribute level.
|
||||
return
|
||||
|
||||
|
||||
# The policy enforce function requires target parameter
|
||||
# oslo_policy doc descripbes target param as: "As much information about the
|
||||
# object being operated on as possible"
|
||||
# For delete and get, prefetch data from database and put tenant_id into target
|
||||
# For post and put, get all user inputs from request body and put into target
|
||||
def generateTarget(state, service, resource):
|
||||
target = {}
|
||||
method = state.request.method
|
||||
if method in ('GET', 'DELETE') and state.arguments.args:
|
||||
api_object_class = API_OBJECT_CLASSES[service][resource]
|
||||
key = state.arguments.args[0]
|
||||
obj = api_object_class.get_from_db(key)
|
||||
tenant_id = obj.tenant_id
|
||||
target['tenant_id'] = tenant_id
|
||||
if method in ('POST', 'PUT'):
|
||||
request_body = json.loads(state.request.body)
|
||||
target = request_body
|
||||
return target
|
||||
|
||||
|
||||
# method could be 'get' or 'list' for the http get method
|
||||
# If there is a key specified, method is 'get'
|
||||
# If there is no key specified, method is 'list
|
||||
def generateMethod(state):
|
||||
method = state.request.method
|
||||
if method == 'GET' and not state.arguments.args:
|
||||
method = 'list'
|
||||
else:
|
||||
method = gluon_constants.ACTION_MAP[state.request.method]
|
||||
return method
|
||||
|
||||
|
||||
def findContrller(state):
|
||||
path = state.request.path_info
|
||||
pathList = path.split('/')[1:]
|
||||
controller = state.app.root
|
||||
resource = state.request.context.get('resource')
|
||||
if pathList:
|
||||
for item in pathList:
|
||||
controller = getattr(controller, item)
|
||||
if resource == item:
|
||||
return controller
|
||||
else:
|
||||
return controller
|
||||
|
|
|
@ -110,6 +110,9 @@ class ApiManager(object):
|
|||
retry -= 1
|
||||
return ret_val
|
||||
|
||||
# TODO(JinLi) This code now is hard-coded to create default interface when
|
||||
# creating ports. Need to change the hardcoded code to support all types
|
||||
# of YAML in future.
|
||||
def create_ports(self, api_class, values):
|
||||
ret_obj = api_class.create_in_db(values)
|
||||
#
|
||||
|
@ -132,6 +135,7 @@ class ApiManager(object):
|
|||
name = 'default'
|
||||
data = {'id': values.get('id'),
|
||||
'port_id': values.get('id'),
|
||||
"tenant_id": values.get('tenant_id', ''),
|
||||
'name': name,
|
||||
'segmentation_type': 'none',
|
||||
'segmentation_id': 0}
|
||||
|
|
|
@ -6,17 +6,28 @@ objects:
|
|||
type: uuid
|
||||
primary: true
|
||||
description: "UUID of Object"
|
||||
tenant_id:
|
||||
type: uuid
|
||||
required: true
|
||||
description: "UUID of Tenant"
|
||||
name:
|
||||
type: string
|
||||
length: 64
|
||||
description: "Descriptive name of Object"
|
||||
policies:
|
||||
create:
|
||||
role: "rule:admin_or_owner"
|
||||
delete:
|
||||
role: "rule:admin_or_owner"
|
||||
list:
|
||||
role: "rule:admin"
|
||||
get:
|
||||
role: "rule:admin_or_owner"
|
||||
update:
|
||||
role: "rule:admin_or_owner"
|
||||
BasePort:
|
||||
extends: BaseObject
|
||||
attributes:
|
||||
tenant_id:
|
||||
type: uuid
|
||||
required: true
|
||||
description: "UUID of Tenant owning this Port"
|
||||
mac_address:
|
||||
type: string
|
||||
length: 18
|
||||
|
@ -108,6 +119,10 @@ objects:
|
|||
description: "Description of Service"
|
||||
BaseServiceBinding:
|
||||
attributes:
|
||||
tenant_id:
|
||||
type: uuid
|
||||
required: true
|
||||
description: "UUID of Tenant"
|
||||
interface_id:
|
||||
type: uuid
|
||||
required: true
|
||||
|
@ -116,4 +131,15 @@ objects:
|
|||
service_id:
|
||||
type: uuid
|
||||
required: true
|
||||
description: "Pointer to Service instance"
|
||||
description: "Pointer to Service instance"
|
||||
policies:
|
||||
create:
|
||||
role: "rule:admin_or_owner"
|
||||
delete:
|
||||
role: "rule:admin_or_owner"
|
||||
list:
|
||||
role: "rule:admin"
|
||||
get:
|
||||
role: "rule:admin_or_owner"
|
||||
update:
|
||||
role: "rule:admin_or_owner"
|
||||
|
|
|
@ -73,6 +73,10 @@ objects:
|
|||
name: vpnafconfig
|
||||
plural_name: vpnafconfigs
|
||||
attributes:
|
||||
tenant_id:
|
||||
type: uuid
|
||||
required: true
|
||||
description: "UUID of Tenant"
|
||||
vrf_rt_value:
|
||||
required: true
|
||||
type: string
|
||||
|
@ -95,6 +99,17 @@ objects:
|
|||
type: string
|
||||
length: 32
|
||||
description: "Route target export policy"
|
||||
policies:
|
||||
create:
|
||||
role: "rule:admin_or_owner"
|
||||
delete:
|
||||
role: "rule:admin_or_owner"
|
||||
get:
|
||||
role: "rule:admin_or_owner"
|
||||
list:
|
||||
role: "rule:admin"
|
||||
update:
|
||||
role: "rule:admin_or_owner"
|
||||
BGPPeering:
|
||||
api:
|
||||
name: bgppeering
|
||||
|
|
|
@ -35,6 +35,7 @@ class MyData(object):
|
|||
|
||||
ApiGenData = MyData()
|
||||
ApiGenData.svc_controllers = {}
|
||||
API_OBJECT_CLASSES = {}
|
||||
|
||||
|
||||
class ProtonVersion(APIBase):
|
||||
|
@ -170,6 +171,8 @@ class APIGenerator(object):
|
|||
return controller
|
||||
|
||||
def create_api(self, root, service_name, db_models):
|
||||
global API_OBJECT_CLASSES
|
||||
API_OBJECT_CLASSES[service_name] = {}
|
||||
self.db_models = db_models
|
||||
self.service_name = service_name
|
||||
self.controllers = {}
|
||||
|
@ -200,6 +203,11 @@ class APIGenerator(object):
|
|||
# api_name
|
||||
api_name = table_data['api']['plural_name']
|
||||
|
||||
# Store each api_object_class created for the api.
|
||||
# As in some situations, policy authorization will need to
|
||||
# call an api_object_class to prefetch data
|
||||
API_OBJECT_CLASSES[service_name][api_name] = api_object_class
|
||||
|
||||
# primary_key_type
|
||||
p_type, p_vals, p_fmt = self.get_primary_key_type(table_data)
|
||||
primary_key_type = self.translate_model_to_api_type(p_type,
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
# Copyright 2017, AT&T
|
||||
#
|
||||
# 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.
|
||||
|
||||
from oslo_log._i18n import _
|
||||
from oslo_log import log as logging
|
||||
from oslo_policy import policy as oslo_policy
|
||||
|
||||
from gluon.particleGenerator import generator
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def policy_name(service, resource, action):
|
||||
re = "%s:%s_%ss"
|
||||
return re % (service, action, resource.lower())
|
||||
|
||||
|
||||
def generatePolicies(service_list):
|
||||
policies = []
|
||||
for service in service_list:
|
||||
model = generator.load_model_for_service(service)
|
||||
generator.validate_policies(model)
|
||||
for obj_name, obj_val in model['api_objects'].items():
|
||||
actions = obj_val.get('policies')
|
||||
for action, rule in actions.items():
|
||||
name = policy_name(service, obj_name, action)
|
||||
policy = oslo_policy.RuleDefault(name, rule.get('role'))
|
||||
|
||||
LOG.info('%(n)s : %(r)s' % dict(n=name, r=rule.get('role')))
|
||||
|
||||
policies.append(policy)
|
||||
return policies
|
|
@ -44,6 +44,32 @@ def raise_obj_error(obj_name, format_str, val_tuple):
|
|||
raise_format_error("Object: %s, %s", (obj_name, str))
|
||||
|
||||
|
||||
# Check if policies are defined for each object and actions are valid.
|
||||
# Does not verify the content of the policy for undefined rules and cyclic
|
||||
# rules. The content of policies will be verified by calling the oslo_policy's
|
||||
# load_rules funtions after all policies are registered.
|
||||
def validate_policies(model):
|
||||
'''Each api object should have policies defined'''
|
||||
|
||||
allowed_actions = ['create', 'delete', 'get', 'list', 'update']
|
||||
for obj_name, obj_val in model['api_objects'].items():
|
||||
if 'policies' not in obj_val:
|
||||
raise_obj_error(obj_name,
|
||||
'%s has no policies defined.',
|
||||
(obj_name))
|
||||
policies = obj_val.get('policies')
|
||||
for action, policy in policies.items():
|
||||
if action not in allowed_actions:
|
||||
raise_obj_error(obj_name,
|
||||
'In %s policies, %s is not a valid action.',
|
||||
(obj_name, action))
|
||||
for action in allowed_actions:
|
||||
if action not in policies:
|
||||
raise_obj_error(obj_name,
|
||||
'In %s policies, action %s is not defined.',
|
||||
(obj_name, action))
|
||||
|
||||
|
||||
def validate_attributes(obj_name, obj, model):
|
||||
props = ['type', 'primary', 'description', 'required',
|
||||
'length', 'values', 'format', 'min', 'max']
|
||||
|
|
|
@ -16,10 +16,16 @@
|
|||
|
||||
import itertools
|
||||
|
||||
from gluon.particleGenerator import generator
|
||||
from gluon.particleGenerator import PolicyGenerator
|
||||
from gluon.policies import base
|
||||
from gluon.policies import net_l3vpn
|
||||
|
||||
|
||||
def list_rules():
|
||||
service_list = generator.get_service_list()
|
||||
return itertools.chain(
|
||||
base.list_rules()
|
||||
base.list_rules(),
|
||||
PolicyGenerator.generatePolicies(service_list)
|
||||
# net_l3vpn.list_rules()
|
||||
)
|
||||
|
|
|
@ -16,6 +16,17 @@
|
|||
|
||||
from oslo_policy import policy
|
||||
|
||||
|
||||
RULE_CONTEXT_IS_ADMIN = 'rule:context_is_admin'
|
||||
RULE_OWNER = 'rule:owner'
|
||||
RULE_ADMIN_OR_OWNER = 'rule:admin_or_owner'
|
||||
RULE_CONTEXT_IS_ADVSVC = 'rule:context_is_advsvc'
|
||||
RULE_ADMIN_OR_NETWORK_OWNER = 'rule:admin_or_network_owner'
|
||||
RULE_ADMIN_OWNER_OR_NETWORK_OWNER = 'rule:admin_owner_or_network_owner'
|
||||
RULE_ADMIN_ONLY = 'rule:admin_only'
|
||||
RULE_REGULAR_USER = 'rule:regular_user'
|
||||
RULE_DEFAULT = 'rule:default'
|
||||
|
||||
rules = [
|
||||
policy.RuleDefault('context_is_admin', 'role:admin'),
|
||||
policy.RuleDefault('owner', 'tenant_id:%(tenant_id)s'),
|
||||
|
|
|
@ -0,0 +1,214 @@
|
|||
# Copyright (c) 2016 OpenStack Foundation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
from oslo_policy import policy
|
||||
|
||||
from gluon.policies import base
|
||||
|
||||
|
||||
# TODO(JinLi) This file is NOT used, it is an example of moving policy to code.
|
||||
# Unlike other Openstack projects whose api has a fix set of restControllers,
|
||||
# Gluon dynamically generates its restControllers from yaml files. If Gluon
|
||||
# follows the policy in code approach, Gluon users will need to modify source
|
||||
# code by adding similar files like this one. And then call the list_rules
|
||||
# function inside the gluon.policies.__init__.py
|
||||
#
|
||||
# Gluon takes a different approach by defining policies inside the yaml file of
|
||||
# a model, so that users do not need to modify any source code
|
||||
#
|
||||
# If user prefers to use plicy in code, they can use this file. And create
|
||||
# similar file for new service.
|
||||
net_l3vpn_policies = [
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:create_dataplanetunnels',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:get_dataplanetunnels',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:update_dataplanetunnels',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:get_one_dataplanetunnels',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:delete_dataplanetunnels',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:create_bgppeerings',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:get_bgppeerings',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:update_bgppeerings',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:get_one_bgppeerings',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:delete_bgppeerings',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:create_vpnafconfigs',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:get_vpnafconfigs',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:update_vpnafconfigs',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:get_one_vpnafconfigs',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:delete_vpnafconfigs',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:create_vpnservices',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:get_vpnservices',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:update_vpnservices',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:get_one_vpnservices',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:delete_vpnservices',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:create_interfaces',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:get_interfaces',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:update_interfaces',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:get_one_interfaces',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:delete_interfaces',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:create_vpnbindings',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:get_vpnbindings',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:update_vpnbindings',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:get_one_vpnbindings',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:delete_vpnbindings',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:create_ports',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:get_ports',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:update_ports',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:get_one_ports',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='net-l3vpn:delete_ports',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='net-l3vpn policy'
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def list_rules():
|
||||
return net_l3vpn_policies
|
Loading…
Reference in New Issue