Admin or Provider tenant to own implicit SCIs

Whenever a Redirect action is provided/consumed GBP, the
implicitly created SCI could be owned by different tenants
depending on the actor triggering it.
To make this consistent, this patch proposes to have a single
configurable admin tenant that will own all the chain resources.
When the said tenant is not configured, the provider PTG's
tenant will be used instead.

Closes-Bug: 1432816
(cherry picked from commit 3becb34638)

Conflicts:
	gbpservice/network/neutronv2/local_api.py
	gbpservice/neutron/services/grouppolicy/drivers/resource_mapping.py

Change-Id: I1344356d4192dbe25695993545dd156d893805cc
This commit is contained in:
Sumit Naiksatam
2015-03-20 16:53:11 -07:00
parent 44e1f7ad71
commit debadf861e
26 changed files with 1345 additions and 872 deletions

View File

@@ -2,3 +2,14 @@
# DNS nameservers to be used configured in the PTG subnets by this driver. # DNS nameservers to be used configured in the PTG subnets by this driver.
# dns_nameservers = 8.8.8.7, 8.8.8.8 # dns_nameservers = 8.8.8.7, 8.8.8.8
# Chain owner username. If set, will be used in place of the Neutron service
# admin for retrieving tenant owner information through Keystone.
# chain_owner_user = <username>
# Chain owner password.
# chain_owner_password = <secret>
# Name of the Tenant that will own the service chain instances for this driver.
# Leave empty for provider owned chains.
# chain_owner_tenant_name = <tenant_name>

View File

@@ -0,0 +1,15 @@
[simplechain]
# Heat server address to create services specified in the service chain
# heat_uri = http://localhost:8004/v1
# CA file for heatclient to verify server certificates
# heat_ca_certificates_file =
# Boolean to control ignoring SSL errors on the heat url
# heat_api_insecure = False
# Number of attempts to retry for stack deletion
# stack_delete_retries = 5
# Wait time between two successive stack delete retries
# stack_delete_retry_wait = 3

View File

@@ -0,0 +1,6 @@
[servicechain]
# An ordered list of service chain drivers entrypoints to be loaded from the
# gbpservice.neutron.servicechain.servicechain_drivers namespace.
# servicechain_drivers=simplechain_driver,oneconvergence_servicechain_driver

View File

@@ -12,6 +12,8 @@
import contextlib import contextlib
from neutron import context as n_ctx
from oslo_config import cfg
from oslo_log import log as logging from oslo_log import log as logging
from oslo_utils import importutils from oslo_utils import importutils
from stevedore import driver from stevedore import driver
@@ -44,6 +46,12 @@ def load_plugin(namespace, plugin):
return plugin_class() return plugin_class()
def admin_context(context):
admin_context = n_ctx.get_admin_context()
admin_context._session = context.session
return admin_context
class DictClass(dict): class DictClass(dict):
def __getattr__(self, item): def __getattr__(self, item):
@@ -51,3 +59,20 @@ class DictClass(dict):
__setattr__ = dict.__setattr__ __setattr__ = dict.__setattr__
__delattr__ = dict.__delattr__ __delattr__ = dict.__delattr__
def get_keystone_creds():
keystone_conf = cfg.CONF.keystone_authtoken
user = keystone_conf.admin_user
pw = keystone_conf.admin_password
tenant = keystone_conf.admin_tenant_name
if keystone_conf.get('auth_uri'):
auth_url = keystone_conf.auth_uri.rstrip('/')
if not auth_url.endswith('/v2.0'):
auth_url += '/v2.0'
else:
auth_url = ('%s://%s:%s/v2.0' % (
keystone_conf.auth_protocol,
keystone_conf.auth_host,
keystone_conf.auth_port))
return user, pw, tenant, auth_url + '/'

View File

@@ -1355,6 +1355,11 @@ class GroupPolicyDbPlugin(gpolicy.GroupPolicyPluginBase,
marker_obj=marker_obj, marker_obj=marker_obj,
page_reverse=page_reverse) page_reverse=page_reverse)
@log.log
def get_l3_policies_count(self, context, filters=None):
return self._get_collection_count(context, L3Policy,
filters=filters)
@log.log @log.log
def create_network_service_policy(self, context, network_service_policy): def create_network_service_policy(self, context, network_service_policy):
nsp = network_service_policy['network_service_policy'] nsp = network_service_policy['network_service_policy']

View File

@@ -234,6 +234,11 @@ class GroupPolicyMappingDbPlugin(gpdb.GroupPolicyDbPlugin):
context.session.add(pt_db) context.session.add(pt_db)
return self._make_policy_target_dict(pt_db) return self._make_policy_target_dict(pt_db)
@log.log
def get_policy_targets_count(self, context, filters=None):
return self._get_collection_count(context, PolicyTargetMapping,
filters=filters)
@log.log @log.log
def get_policy_targets(self, context, filters=None, fields=None, def get_policy_targets(self, context, filters=None, fields=None,
sorts=None, limit=None, marker=None, sorts=None, limit=None, marker=None,
@@ -331,6 +336,11 @@ class GroupPolicyMappingDbPlugin(gpdb.GroupPolicyDbPlugin):
marker_obj=marker_obj, marker_obj=marker_obj,
page_reverse=page_reverse) page_reverse=page_reverse)
@log.log
def get_l2_policies_count(self, context, filters=None):
return self._get_collection_count(context, L2PolicyMapping,
filters=filters)
@log.log @log.log
def create_l3_policy(self, context, l3_policy): def create_l3_policy(self, context, l3_policy):
l3p = l3_policy['l3_policy'] l3p = l3_policy['l3_policy']
@@ -426,6 +436,10 @@ class GroupPolicyMappingDbPlugin(gpdb.GroupPolicyDbPlugin):
marker_obj=marker_obj, marker_obj=marker_obj,
page_reverse=page_reverse) page_reverse=page_reverse)
def get_external_segments_count(self, context, filters=None):
return self._get_collection_count(context, ExternalSegmentMapping,
filters=filters)
@log.log @log.log
def create_nat_pool(self, context, nat_pool): def create_nat_pool(self, context, nat_pool):
np = nat_pool['nat_pool'] np = nat_pool['nat_pool']

View File

@@ -1 +1 @@
1fadeb573886 c2a9d04c8cef

View File

@@ -0,0 +1,40 @@
# Copyright 2014 OpenStack Foundation
#
# 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.
#
"""Admin owns SCI (admin_owns_sci)
Revision ID: c2a9d04c8cef
"""
# revision identifiers, used by Alembic.
revision = 'c2a9d04c8cef'
down_revision = '1fadeb573886'
from alembic import op
import sqlalchemy as sa
def upgrade():
op.add_column(
'gpm_ptgs_servicechain_mapping',
sa.Column('tenant_id', sa.String(length=255), nullable=True)
)
def downgrade():
op.drop_column('gpm_ptgs_servicechain_mapping', 'tenant_id')

View File

@@ -34,7 +34,6 @@ from gbpservice.neutron.services.grouppolicy.common import (
# The code below is a monkey patch of key Neutron's modules. This is needed for # The code below is a monkey patch of key Neutron's modules. This is needed for
# the GBP service to be loaded correctly. GBP extensions' path is added # the GBP service to be loaded correctly. GBP extensions' path is added
# to Neutron's so that it's found at extension scanning time. # to Neutron's so that it's found at extension scanning time.
extensions.append_api_extensions_path(gbpservice.neutron.extensions.__path__) extensions.append_api_extensions_path(gbpservice.neutron.extensions.__path__)
constants.GROUP_POLICY = "GROUP_POLICY" constants.GROUP_POLICY = "GROUP_POLICY"
constants.COMMON_PREFIXES["GROUP_POLICY"] = "/grouppolicy" constants.COMMON_PREFIXES["GROUP_POLICY"] = "/grouppolicy"

View File

@@ -11,6 +11,7 @@
# under the License. # under the License.
from neutron.db import l3_db from neutron.db import l3_db
from neutron.db import securitygroups_db
# Monkey patch create floatingip to allow subnet_id to be specified. # Monkey patch create floatingip to allow subnet_id to be specified.
@@ -94,3 +95,37 @@ def get_assoc_data(self, context, fip, floating_network_id):
return fip['port_id'], internal_ip_address, router_id return fip['port_id'], internal_ip_address, router_id
l3_db.L3_NAT_dbonly_mixin.get_assoc_data = get_assoc_data l3_db.L3_NAT_dbonly_mixin.get_assoc_data = get_assoc_data
# REVISIT(ivar): Neutron adds a tenant filter on SG lookup for a given port,
# this breaks our service chain plumbing model so for now we should monkey
# patch the specific method. A follow up with the Neutron team is needed to
# figure out the reason for this and how to proceed for future releases.
def _get_security_groups_on_port(self, context, port):
"""Check that all security groups on port belong to tenant.
:returns: all security groups IDs on port belonging to tenant.
"""
p = port['port']
if not securitygroups_db.attributes.is_attr_set(
p.get(securitygroups_db.ext_sg.SECURITYGROUPS)):
return
if p.get('device_owner') and p['device_owner'].startswith('network:'):
return
port_sg = p.get(securitygroups_db.ext_sg.SECURITYGROUPS, [])
filters = {'id': port_sg}
valid_groups = set(g['id'] for g in
self.get_security_groups(context, fields=['id'],
filters=filters))
requested_groups = set(port_sg)
port_sg_missing = requested_groups - valid_groups
if port_sg_missing:
raise securitygroups_db.ext_sg.SecurityGroupNotFound(
id=', '.join(port_sg_missing))
return requested_groups
securitygroups_db.SecurityGroupDbMixin._get_security_groups_on_port = (
_get_security_groups_on_port)

View File

@@ -13,6 +13,8 @@
import netaddr import netaddr
import operator import operator
from keystoneclient import exceptions as k_exceptions
from keystoneclient.v2_0 import client as k_client
from neutron.api.v2 import attributes from neutron.api.v2 import attributes
from neutron.common import constants as const from neutron.common import constants as const
from neutron.common import exceptions as n_exc from neutron.common import exceptions as n_exc
@@ -25,6 +27,7 @@ from neutron.extensions import securitygroup as ext_sg
from oslo_config import cfg from oslo_config import cfg
from oslo_log import log as logging from oslo_log import log as logging
from oslo_serialization import jsonutils from oslo_serialization import jsonutils
from oslo_utils import excutils
import sqlalchemy as sa import sqlalchemy as sa
from gbpservice.common import utils from gbpservice.common import utils
@@ -41,6 +44,24 @@ from gbpservice.neutron.services.grouppolicy.common import exceptions as exc
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
DEFAULT_SG_PREFIX = 'gbp_%s' DEFAULT_SG_PREFIX = 'gbp_%s'
group_policy_opts = [
cfg.StrOpt('chain_owner_user',
help=_("Chain owner username. If set, will be used in "
"place of the Neutron service admin for retrieving "
"tenant owner information through Keystone."),
default=''),
cfg.StrOpt('chain_owner_password',
help=_("Chain owner password."), default='',
secret=True),
cfg.StrOpt('chain_owner_tenant_name',
help=_("Name of the Tenant that will own the service chain "
"instances for this driver. Leave empty for provider "
"owned chains."), default=''),
]
cfg.CONF.register_opts(group_policy_opts, "resource_mapping")
opts = [ opts = [
cfg.ListOpt('dns_nameservers', cfg.ListOpt('dns_nameservers',
@@ -102,7 +123,7 @@ class PolicyRuleSetSGsMapping(model_base.BASEV2):
sa.ForeignKey('securitygroups.id')) sa.ForeignKey('securitygroups.id'))
class PtgServiceChainInstanceMapping(model_base.BASEV2): class PtgServiceChainInstanceMapping(model_base.BASEV2, models_v2.HasTenant):
"""Policy Target Group to ServiceChainInstance mapping DB.""" """Policy Target Group to ServiceChainInstance mapping DB."""
__tablename__ = 'gpm_ptgs_servicechain_mapping' __tablename__ = 'gpm_ptgs_servicechain_mapping'
@@ -174,6 +195,41 @@ class ResourceMappingDriver(api.PolicyDriver, local_api.LocalAPI):
@log.log @log.log
def initialize(self): def initialize(self):
self._cached_agent_notifier = None self._cached_agent_notifier = None
self.chain_owner = ResourceMappingDriver.chain_tenant_id(reraise=True)
@staticmethod
def chain_tenant_id(reraise=False):
keystone = ResourceMappingDriver.chain_tenant_keystone_client()
if keystone:
tenant = cfg.CONF.resource_mapping.chain_owner_tenant_name
try:
# Can it be retrieved directly, without a further keystone
# call?
tenant = keystone.tenants.find(name=tenant)
return tenant.id
except k_exceptions.NotFound:
with excutils.save_and_reraise_exception(reraise=reraise):
LOG.error(_('No tenant with name %s exists.'), tenant)
except k_exceptions.NoUniqueMatch:
with excutils.save_and_reraise_exception(reraise=reraise):
LOG.error(_('Multiple tenants matches found for %s'),
tenant)
@staticmethod
def chain_tenant_keystone_client():
chain_user = cfg.CONF.resource_mapping.chain_owner_user
user, pwd, tenant, auth_url = utils.get_keystone_creds()
user = (chain_user or user)
pwd = (cfg.CONF.resource_mapping.chain_owner_password or
(pwd if not chain_user else ''))
# Tenant must be configured in the resource_mapping section, provider
# owner will be used otherwise.
tenant = cfg.CONF.resource_mapping.chain_owner_tenant_name
if tenant:
return k_client.Client(username=user, password=pwd,
auth_url=auth_url)
def _reject_shared(self, object, type): def _reject_shared(self, object, type):
if object.get('shared'): if object.get('shared'):
@@ -513,7 +569,6 @@ class ResourceMappingDriver(api.PolicyDriver, local_api.LocalAPI):
"network_service_policy_id") "network_service_policy_id")
if not network_service_policy_id: if not network_service_policy_id:
return return
nsp = context._plugin.get_network_service_policy( nsp = context._plugin.get_network_service_policy(
context._plugin_context, network_service_policy_id) context._plugin_context, network_service_policy_id)
nsp_params = nsp.get("network_service_params") nsp_params = nsp.get("network_service_params")
@@ -940,11 +995,16 @@ class ResourceMappingDriver(api.PolicyDriver, local_api.LocalAPI):
context.original['direction'] != context.current['direction']): context.original['direction'] != context.current['direction']):
sc_instances = ( sc_instances = (
self._servicechain_plugin.get_servicechain_instances( self._servicechain_plugin.get_servicechain_instances(
context._plugin_context, context._plugin_context.elevated(),
filters={'classifier_id': [context.current['id']]})) filters={'classifier_id': [context.current['id']]}))
for sc_instance in sc_instances: for sc_instance in sc_instances:
cmap = self._get_ptg_servicechain_mapping(
context._plugin_context.session,
servicechain_instance_id=sc_instance['id'])
ctx = self._get_chain_admin_context(context._plugin_context,
cmap[0].tenant_id)
self._servicechain_plugin.notify_chain_parameters_updated( self._servicechain_plugin.notify_chain_parameters_updated(
context._plugin_context, sc_instance['id']) ctx, sc_instance['id'])
@log.log @log.log
def delete_policy_classifier_precommit(self, context): def delete_policy_classifier_precommit(self, context):
@@ -1864,10 +1924,11 @@ class ResourceMappingDriver(api.PolicyDriver, local_api.LocalAPI):
x == context.original['action_value'] else x == context.original['action_value'] else
x for x in old_specs] x for x in old_specs]
self._update_servicechain_instance( self._update_servicechain_instance(
context, servicechain_instance.servicechain_instance_id, context._plugin_context,
servicechain_instance.servicechain_instance_id,
sc_specs=new_specs) sc_specs=new_specs)
def _update_servicechain_instance(self, context, sc_instance_id, def _update_servicechain_instance(self, plugin_context, sc_instance_id,
classifier_id=None, sc_specs=None): classifier_id=None, sc_specs=None):
sc_instance_update_data = {} sc_instance_update_data = {}
if sc_specs: if sc_specs:
@@ -1875,7 +1936,9 @@ class ResourceMappingDriver(api.PolicyDriver, local_api.LocalAPI):
if classifier_id: if classifier_id:
sc_instance_update_data.update({'classifier_id': classifier_id}) sc_instance_update_data.update({'classifier_id': classifier_id})
super(ResourceMappingDriver, self)._update_servicechain_instance( super(ResourceMappingDriver, self)._update_servicechain_instance(
context._plugin_context, sc_instance_id, sc_instance_update_data) self._get_chain_admin_context(
plugin_context, instance_id=sc_instance_id),
sc_instance_id, sc_instance_update_data)
def _get_rule_ids_for_actions(self, context, action_id): def _get_rule_ids_for_actions(self, context, action_id):
policy_rule_qry = context.session.query( policy_rule_qry = context.session.query(
@@ -1948,42 +2011,42 @@ class ResourceMappingDriver(api.PolicyDriver, local_api.LocalAPI):
context, ptg_providing_prs, ptg_consuming_prs, context, ptg_providing_prs, ptg_consuming_prs,
spec_id, spec_id,
parent_spec_id, classifier_id, parent_spec_id, classifier_id,
hierarchial_classifier_mismatch) hierarchial_classifier_mismatch, policy_rule_set)
def _create_or_update_chain(self, context, provider, consumer, spec_id, def _create_or_update_chain(self, context, provider, consumer, spec_id,
parent_spec_id, classifier_id, parent_spec_id, classifier_id,
hierarchial_classifier_mismatch): hierarchial_classifier_mismatch, prs_id):
ptg_chain_map = self._get_ptg_servicechain_mapping( ptg_chain_map = self._get_ptg_servicechain_mapping(
context._plugin_context.session, provider, consumer) context._plugin_context.session, provider, consumer)
if ptg_chain_map: if ptg_chain_map:
if hierarchial_classifier_mismatch or not spec_id: if hierarchial_classifier_mismatch or not spec_id:
self._delete_servicechain_instance( ctx = self._get_chain_admin_context(
context._plugin_context, context._plugin_context,
ptg_chain_map[0].servicechain_instance_id) tenant_id=ptg_chain_map[0].tenant_id)
self._delete_servicechain_instance(
ctx, ptg_chain_map[0].servicechain_instance_id)
else: else:
sc_specs = [spec_id] sc_specs = [spec_id]
if parent_spec_id: if parent_spec_id:
sc_specs.insert(0, parent_spec_id) sc_specs.insert(0, parent_spec_id)
# One Chain between a unique pair of provider and consumer # One Chain between a unique pair of provider and consumer
self._update_servicechain_instance( self._update_servicechain_instance(
context, context._plugin_context,
ptg_chain_map[0].servicechain_instance_id, ptg_chain_map[0].servicechain_instance_id,
classifier_id=classifier_id, classifier_id=classifier_id,
sc_specs=sc_specs) sc_specs=sc_specs)
elif spec_id and not hierarchial_classifier_mismatch: elif spec_id and not hierarchial_classifier_mismatch:
sc_instance = self._create_servicechain_instance( self._create_servicechain_instance(context, spec_id,
context, spec_id, parent_spec_id, provider,
parent_spec_id, provider, consumer, classifier_id, prs_id)
consumer, classifier_id)
self._set_ptg_servicechain_instance_mapping(
context._plugin_context.session,
provider, consumer,
sc_instance['id'])
def _cleanup_redirect_action(self, context): def _cleanup_redirect_action(self, context):
for ptg_chain in context.ptg_chain_map: for ptg_chain in context.ptg_chain_map:
ctx = self._get_chain_admin_context(context._plugin_context,
tenant_id=ptg_chain.tenant_id)
self._delete_servicechain_instance( self._delete_servicechain_instance(
context._plugin_context, ptg_chain.servicechain_instance_id) ctx, ptg_chain.servicechain_instance_id)
def _restore_ip_to_allocation_pool(self, context, subnet_id, ip_address): def _restore_ip_to_allocation_pool(self, context, subnet_id, ip_address):
# TODO(Magesh):Pass subnets and loop on subnets. Better to add logic # TODO(Magesh):Pass subnets and loop on subnets. Better to add logic
@@ -2035,18 +2098,30 @@ class ResourceMappingDriver(api.PolicyDriver, local_api.LocalAPI):
def _create_servicechain_instance(self, context, servicechain_spec, def _create_servicechain_instance(self, context, servicechain_spec,
parent_servicechain_spec, parent_servicechain_spec,
provider_ptg_id, consumer_ptg_id, provider_ptg_id, consumer_ptg_id,
classifier_id, classifier_id, policy_rule_set,
config_params=None): config_params=None):
sc_spec = [servicechain_spec] sc_spec = [servicechain_spec]
if parent_servicechain_spec: if parent_servicechain_spec:
sc_spec.insert(0, parent_servicechain_spec) sc_spec.insert(0, parent_servicechain_spec)
config_param_values = {} config_param_values = {}
ptg = context._plugin.get_policy_target_group( provider_ptg = context._plugin.get_policy_target_group(
context._plugin_context, provider_ptg_id) utils.admin_context(context._plugin_context), provider_ptg_id)
network_service_policy_id = ptg.get("network_service_policy_id") p_ctx = self._get_chain_admin_context(
context._plugin_context,
provider_tenant_id=provider_ptg['tenant_id'])
session = context._plugin_context.session
if consumer_ptg_id:
try:
consumer_ptg = context._plugin.get_policy_target_group(
p_ctx, consumer_ptg_id)
except gp_ext.PolicyTargetGroupNotFound:
consumer_ptg = context._plugin.get_external_policy(
p_ctx, consumer_ptg_id)
network_service_policy_id = provider_ptg.get(
"network_service_policy_id")
if network_service_policy_id: if network_service_policy_id:
nsp = context._plugin.get_network_service_policy( nsp = context._plugin.get_network_service_policy(
context._plugin_context, network_service_policy_id) p_ctx, network_service_policy_id)
service_params = nsp.get("network_service_params") service_params = nsp.get("network_service_params")
for service_parameter in service_params: for service_parameter in service_params:
param_type = service_parameter.get("type") param_type = service_parameter.get("type")
@@ -2055,7 +2130,7 @@ class ResourceMappingDriver(api.PolicyDriver, local_api.LocalAPI):
key = service_parameter.get("name") key = service_parameter.get("name")
servicepolicy_ptg_ip_map = ( servicepolicy_ptg_ip_map = (
self._get_ptg_policy_ipaddress_mapping( self._get_ptg_policy_ipaddress_mapping(
context._plugin_context.session, provider_ptg_id)) session, provider_ptg_id))
servicepolicy_ip = servicepolicy_ptg_ip_map.get( servicepolicy_ip = servicepolicy_ptg_ip_map.get(
"ipaddress") "ipaddress")
config_param_values[key] = servicepolicy_ip config_param_values[key] = servicepolicy_ip
@@ -2069,8 +2144,11 @@ class ResourceMappingDriver(api.PolicyDriver, local_api.LocalAPI):
for fip_map in fip_maps: for fip_map in fip_maps:
servicepolicy_fip_ids.append(fip_map.floatingip_id) servicepolicy_fip_ids.append(fip_map.floatingip_id)
config_param_values[key] = servicepolicy_fip_ids config_param_values[key] = servicepolicy_fip_ids
attrs = {'tenant_id': context.current['tenant_id'], name = 'gbp_%s_%s_%s' % (policy_rule_set['name'], provider_ptg['name'],
'name': 'gbp_' + ptg['name'], consumer_ptg['name'] if consumer_ptg else '')
attrs = {'tenant_id': p_ctx.tenant,
'name': name,
'description': "", 'description': "",
'servicechain_specs': sc_spec, 'servicechain_specs': sc_spec,
'provider_ptg_id': provider_ptg_id, 'provider_ptg_id': provider_ptg_id,
@@ -2078,9 +2156,13 @@ class ResourceMappingDriver(api.PolicyDriver, local_api.LocalAPI):
'management_ptg_id': None, 'management_ptg_id': None,
'classifier_id': classifier_id, 'classifier_id': classifier_id,
'config_param_values': jsonutils.dumps(config_param_values)} 'config_param_values': jsonutils.dumps(config_param_values)}
return super( sc_instance = super(
ResourceMappingDriver, self)._create_servicechain_instance( ResourceMappingDriver, self)._create_servicechain_instance(
context._plugin_context, attrs) p_ctx, attrs)
self._set_ptg_servicechain_instance_mapping(
session, provider_ptg_id, consumer_ptg_id, sc_instance['id'],
p_ctx.tenant)
return sc_instance
# Do Not Pass floating_ip_address to this method until after Kilo Release # Do Not Pass floating_ip_address to this method until after Kilo Release
def _create_floatingip(self, plugin_context, tenant_id, ext_net_id, def _create_floatingip(self, plugin_context, tenant_id, ext_net_id,
@@ -2549,27 +2631,36 @@ class ResourceMappingDriver(api.PolicyDriver, local_api.LocalAPI):
def _set_ptg_servicechain_instance_mapping(self, session, provider_ptg_id, def _set_ptg_servicechain_instance_mapping(self, session, provider_ptg_id,
consumer_ptg_id, consumer_ptg_id,
servicechain_instance_id): servicechain_instance_id,
provider_tenant_id):
with session.begin(subtransactions=True): with session.begin(subtransactions=True):
mapping = PtgServiceChainInstanceMapping( mapping = PtgServiceChainInstanceMapping(
provider_ptg_id=provider_ptg_id, provider_ptg_id=provider_ptg_id,
consumer_ptg_id=consumer_ptg_id, consumer_ptg_id=consumer_ptg_id,
servicechain_instance_id=servicechain_instance_id) servicechain_instance_id=servicechain_instance_id,
tenant_id=provider_tenant_id)
session.add(mapping) session.add(mapping)
def _get_ptg_servicechain_mapping(self, session, provider_ptg_id, def _get_ptg_servicechain_mapping(self, session, provider_ptg_id=None,
consumer_ptg_id): consumer_ptg_id=None, tenant_id=None,
servicechain_instance_id=None):
with session.begin(subtransactions=True): with session.begin(subtransactions=True):
query = session.query(PtgServiceChainInstanceMapping) query = session.query(PtgServiceChainInstanceMapping)
if provider_ptg_id: if provider_ptg_id:
query = query.filter_by(provider_ptg_id=provider_ptg_id) query = query.filter_by(provider_ptg_id=provider_ptg_id)
if consumer_ptg_id: if consumer_ptg_id:
query = query.filter_by(consumer_ptg_id=consumer_ptg_id) query = query.filter_by(consumer_ptg_id=consumer_ptg_id)
if servicechain_instance_id:
query = query.filter_by(
servicechain_instance_id=servicechain_instance_id)
if tenant_id:
query = query.filter_by(consumer_ptg_id=tenant_id)
all = query.all() all = query.all()
return [utils.DictClass([('provider_ptg_id', x.provider_ptg_id), return [utils.DictClass([('provider_ptg_id', x.provider_ptg_id),
('consumer_ptg_id', x.consumer_ptg_id), ('consumer_ptg_id', x.consumer_ptg_id),
('servicechain_instance_id', ('servicechain_instance_id',
x.servicechain_instance_id)]) x.servicechain_instance_id),
('tenant_id', x.tenant_id)])
for x in all] for x in all]
def _get_ep_cidr_list(self, context, ep): def _get_ep_cidr_list(self, context, ep):
@@ -2746,3 +2837,25 @@ class ResourceMappingDriver(api.PolicyDriver, local_api.LocalAPI):
ptg_subnet_id=",".join(ptg.get('subnets')), ptg_subnet_id=",".join(ptg.get('subnets')),
port_subnet_id=port_subnet_id, port_subnet_id=port_subnet_id,
policy_target_group_id=ptg_id) policy_target_group_id=ptg_id)
def _get_chain_admin_context(self, plugin_context, tenant_id=None,
provider_tenant_id=None, instance_id=None):
ctx = plugin_context.elevated()
# REVISIT(Ivar): Any particular implication when a provider owned PT
# exist in the consumer PTG? Especially when the consumer PTG belongs
# to another tenant? We may want to consider a strong convention
# for reference plumbers to absolutely avoid this kind of inter tenant
# object creation when the owner is the provider (in which case, the
# context can as well be a normal context without admin capabilities).
ctx.tenant_id = None
if instance_id:
cmap = self._get_ptg_servicechain_mapping(
ctx.session, servicechain_instance_id=instance_id)
if cmap:
ctx.tenant_id = cmap[0].tenant_id
if not ctx.tenant_id:
ctx.tenant_id = tenant_id or self.chain_owner or provider_tenant_id
if self.chain_owner == ctx.tenant_id:
ctx.auth_token = self.chain_tenant_keystone_client().get_token(
self.chain_owner)
return ctx

View File

@@ -131,7 +131,7 @@ class GroupPolicyPlugin(group_policy_mapping_db.GroupPolicyMappingDbPlugin):
for linked in linked_objects: for linked in linked_objects:
link_ids.add(linked['id']) link_ids.add(linked['id'])
GroupPolicyPlugin._verify_sharing_consistency( GroupPolicyPlugin._verify_sharing_consistency(
obj, linked, identity, ref_type) obj, linked, identity, ref_type, context.is_admin)
# Check for missing references # Check for missing references
missing = set(ids) - link_ids missing = set(ids) - link_ids
if missing: if missing:
@@ -288,20 +288,22 @@ class GroupPolicyPlugin(group_policy_mapping_db.GroupPolicyMappingDbPlugin):
spec = self.servicechain_plugin.get_servicechain_spec( spec = self.servicechain_plugin.get_servicechain_spec(
context, action['action_value']) context, action['action_value'])
GroupPolicyPlugin._verify_sharing_consistency( GroupPolicyPlugin._verify_sharing_consistency(
action, spec, 'polocy_action', 'servicechain_spec') action, spec, 'policy_action', 'servicechain_spec',
context.is_admin)
@staticmethod @staticmethod
def _verify_sharing_consistency(primary, reference, primary_type, def _verify_sharing_consistency(primary, reference, primary_type,
reference_type): reference_type, is_admin):
if not reference.get('shared'): if not reference.get('shared'):
if primary.get('shared'): if primary.get('shared'):
raise gp_exc.SharedResourceReferenceError( raise gp_exc.SharedResourceReferenceError(
res_type=primary_type, res_id=primary['id'], res_type=primary_type, res_id=primary['id'],
ref_type=reference_type, ref_id=reference['id']) ref_type=reference_type, ref_id=reference['id'])
if primary.get('tenant_id') != reference.get('tenant_id'): if not is_admin:
raise gp_exc.InvalidCrossTenantReference( if primary.get('tenant_id') != reference.get('tenant_id'):
res_type=primary_type, res_id=primary['id'], raise gp_exc.InvalidCrossTenantReference(
ref_type=reference_type, ref_id=reference['id']) res_type=primary_type, res_id=primary['id'],
ref_type=reference_type, ref_id=reference['id'])
def __init__(self): def __init__(self):
self.extension_manager = ext_manager.ExtensionManager() self.extension_manager = ext_manager.ExtensionManager()
@@ -484,7 +486,8 @@ class GroupPolicyPlugin(group_policy_mapping_db.GroupPolicyMappingDbPlugin):
policy_target_group = self.get_policy_target_group( policy_target_group = self.get_policy_target_group(
context, policy_target_group_id) context, policy_target_group_id)
pt_ids = policy_target_group['policy_targets'] pt_ids = policy_target_group['policy_targets']
for pt in self.get_policy_targets(context, {'id': pt_ids}): for pt in self.get_policy_targets(context.elevated(),
{'id': pt_ids}):
if pt['port_id'] and self._is_port_bound(pt['port_id']) or ( if pt['port_id'] and self._is_port_bound(pt['port_id']) or (
self._is_service_target(context, pt['id'])): self._is_service_target(context, pt['id'])):
raise gp_exc.PolicyTargetGroupInUse( raise gp_exc.PolicyTargetGroupInUse(

View File

@@ -15,6 +15,7 @@ import time
from heatclient import client as heat_client from heatclient import client as heat_client
from heatclient import exc as heat_exc from heatclient import exc as heat_exc
from keystoneclient.v2_0 import client as keyclient
from neutron.common import log from neutron.common import log
from neutron.db import model_base from neutron.db import model_base
from neutron import manager from neutron import manager
@@ -416,11 +417,16 @@ class HeatClient:
def __init__(self, context, password=None): def __init__(self, context, password=None):
api_version = "1" api_version = "1"
endpoint = "%s/%s" % (cfg.CONF.simplechain.heat_uri, context.tenant) self.tenant = context.tenant
self._keystone = None
endpoint = "%s/%s" % (cfg.CONF.simplechain.heat_uri, self.tenant)
kwargs = { kwargs = {
'token': context.auth_token, 'token': self._get_auth_token(self.tenant),
'username': context.user_name, 'username': context.user_name,
'password': password 'password': password,
'cacert': cfg.CONF.simplechain.heat_ca_certificates_file,
'insecure': cfg.CONF.simplechain.heat_api_insecure
} }
self.client = heat_client.Client(api_version, endpoint, **kwargs) self.client = heat_client.Client(api_version, endpoint, **kwargs)
self.stacks = self.client.stacks self.stacks = self.client.stacks
@@ -445,3 +451,25 @@ class HeatClient:
def get(self, stack_id): def get(self, stack_id):
return self.stacks.get(stack_id) return self.stacks.get(stack_id)
@property
def keystone(self):
if not self._keystone:
keystone_conf = cfg.CONF.keystone_authtoken
if keystone_conf.get('auth_uri'):
auth_url = keystone_conf.auth_uri
else:
auth_url = ('%s://%s:%s/v2.0/' % (
keystone_conf.auth_protocol,
keystone_conf.auth_host,
keystone_conf.auth_port))
user = (keystone_conf.get('admin_user') or keystone_conf.username)
pw = (keystone_conf.get('admin_password') or
keystone_conf.password)
self._keystone = keyclient.Client(
username=user, password=pw, auth_url=auth_url,
tenant_id=self.tenant)
return self._keystone
def _get_auth_token(self, tenant):
return self.keystone.get_token(tenant)

View File

@@ -10,10 +10,10 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from neutron import context as n_context
from neutron import manager from neutron import manager
from neutron.plugins.common import constants as pconst from neutron.plugins.common import constants as pconst
from gbpservice.common import utils
from gbpservice.neutron.services.servicechain.plugins.ncp import model from gbpservice.neutron.services.servicechain.plugins.ncp import model
@@ -23,26 +23,26 @@ def get_gbp_plugin():
def get_node_driver_context(sc_plugin, context, sc_instance, def get_node_driver_context(sc_plugin, context, sc_instance,
current_node, original_node=None, current_node, original_node=None,
service_targets=None): management_group=None, service_targets=None):
admin_context = utils.admin_context(context)
specs = sc_plugin.get_servicechain_specs( specs = sc_plugin.get_servicechain_specs(
context, filters={'id': sc_instance['servicechain_specs']}) admin_context, filters={'id': sc_instance['servicechain_specs']})
position = _calculate_node_position(specs, current_node['id']) position = _calculate_node_position(specs, current_node['id'])
provider, _ = _get_ptg_or_ep( provider, _ = _get_ptg_or_ep(
context, sc_instance['provider_ptg_id']) admin_context, sc_instance['provider_ptg_id'])
consumer, is_consumer_external = _get_ptg_or_ep( consumer, is_consumer_external = _get_ptg_or_ep(
context, sc_instance['consumer_ptg_id']) admin_context, sc_instance['consumer_ptg_id'])
management, _ = _get_ptg_or_ep(context, sc_instance['management_ptg_id']) management, _ = _get_ptg_or_ep(context, sc_instance['management_ptg_id'])
classifier = get_gbp_plugin().get_policy_classifier( classifier = get_gbp_plugin().get_policy_classifier(
context, sc_instance['classifier_id']) admin_context, sc_instance['classifier_id'])
current_profile = sc_plugin.get_service_profile( current_profile = sc_plugin.get_service_profile(
context, current_node['service_profile_id']) admin_context, current_node['service_profile_id'])
original_profile = sc_plugin.get_service_profile( original_profile = sc_plugin.get_service_profile(
context, admin_context,
original_node['service_profile_id']) if original_node else None original_node['service_profile_id']) if original_node else None
if not service_targets: if not service_targets:
service_targets = model.get_service_targets( service_targets = model.get_service_targets(
context.session, servicechain_instance_id=sc_instance['id'], admin_context.session, servicechain_instance_id=sc_instance['id'],
position=position, servicechain_node_id=current_node['id']) position=position, servicechain_node_id=current_node['id'])
return NodeDriverContext(sc_plugin=sc_plugin, return NodeDriverContext(sc_plugin=sc_plugin,
@@ -147,7 +147,7 @@ class NodeDriverContext(object):
@property @property
def admin_context(self): def admin_context(self):
if not self._admin_context: if not self._admin_context:
self._admin_context = n_context.get_admin_context() self._admin_context = utils.admin_context(self.plugin_context)
return self._admin_context return self._admin_context
@property @property

View File

@@ -283,7 +283,9 @@ class NodeCompositionPlugin(servicechain_db.ServiceChainDbPlugin,
self._update_chains_pt_modified(context, policy_target, 'removed') self._update_chains_pt_modified(context, policy_target, 'removed')
def _update_chains_pt_modified(self, context, policy_target, action): def _update_chains_pt_modified(self, context, policy_target, action):
scis = self._get_instances_from_policy_target(context, policy_target) admin_context = utils.admin_context(context)
scis = self._get_instances_from_policy_target(
admin_context, policy_target)
for sci in scis: for sci in scis:
updaters = self._get_scheduled_drivers(context, sci, 'update') updaters = self._get_scheduled_drivers(context, sci, 'update')
@@ -317,6 +319,7 @@ class NodeCompositionPlugin(servicechain_db.ServiceChainDbPlugin,
"failed, %s"), ex.message) "failed, %s"), ex.message)
def _get_instance_nodes(self, context, instance): def _get_instance_nodes(self, context, instance):
context = utils.admin_context(context)
if not instance['servicechain_specs']: if not instance['servicechain_specs']:
return [] return []
specs = self.get_servicechain_spec( specs = self.get_servicechain_spec(
@@ -324,6 +327,7 @@ class NodeCompositionPlugin(servicechain_db.ServiceChainDbPlugin,
return self.get_servicechain_nodes(context, {'id': specs['nodes']}) return self.get_servicechain_nodes(context, {'id': specs['nodes']})
def _get_node_instances(self, context, node): def _get_node_instances(self, context, node):
context = utils.admin_context(context)
specs = self.get_servicechain_specs( specs = self.get_servicechain_specs(
context, {'id': node['servicechain_specs']}) context, {'id': node['servicechain_specs']})
result = [] result = []

View File

@@ -113,8 +113,8 @@ class NodePlumberBase(object):
for pt in pts: for pt in pts:
try: try:
gbp_plugin.delete_policy_target(context, pt.policy_target_id, gbp_plugin.delete_policy_target(
notify_sc=False) context.elevated(), pt.policy_target_id, notify_sc=False)
except group_policy.PolicyTargetNotFound as ex: except group_policy.PolicyTargetNotFound as ex:
LOG.debug(ex.message) LOG.debug(ex.message)
@@ -134,7 +134,7 @@ class NodePlumberBase(object):
instance['id']), instance['id']),
'name': '', 'port_id': None} 'name': '', 'port_id': None}
data.update(target) data.update(target)
pt = gbp_plugin.create_policy_target(context, pt = gbp_plugin.create_policy_target(context.elevated(),
{'policy_target': data}, {'policy_target': data},
notify_sc=False) notify_sc=False)
model.set_service_target(part_context, pt['id'], relationship) model.set_service_target(part_context, pt['id'], relationship)

View File

@@ -56,21 +56,14 @@
"get_nat_pool": "rule:admin_or_owner or rule:shared_np", "get_nat_pool": "rule:admin_or_owner or rule:shared_np",
"create_servicechain_node": "", "create_servicechain_node": "",
"create_servicechain_node:shared": "rule:admin_only",
"get_servicechain_node": "rule:admin_or_owner or rule:shared_scn", "get_servicechain_node": "rule:admin_or_owner or rule:shared_scn",
"update_servicechain_node:shared": "rule:admin_only",
"create_servicechain_spec": "", "create_servicechain_spec": "",
"create_servicechain_spec:shared": "rule:admin_only",
"get_servicechain_spec": "rule:admin_or_owner or rule:shared_scs", "get_servicechain_spec": "rule:admin_or_owner or rule:shared_scs",
"update_servicechain_spec:shared": "rule:admin_only",
"create_servicechain_instance": "", "create_servicechain_instance": "",
"get_servicechain_instance": "rule:admin_or_owner", "get_servicechain_instance": "rule:admin_or_owner",
"update_servicechain_instance:shared": "rule:admin_only",
"create_service_profile": "", "create_service_profile": "",
"create_service_profile:shared": "rule:admin_only", "get_service_profile": "rule:admin_or_owner or rule:shared_sp"
"get_service_profile": "rule:admin_or_owner or rule:shared_sp",
"update_service_profile:shared": "rule:admin_only"
} }

View File

@@ -16,8 +16,9 @@ import os
import webob.exc import webob.exc
from neutron.api import extensions from neutron.api import extensions
from neutron.api.v2 import attributes as nattr
from neutron import context from neutron import context
from neutron.db import api as db_api
from neutron.db import model_base
from neutron import manager from neutron import manager
from neutron.openstack.common import uuidutils from neutron.openstack.common import uuidutils
from neutron.plugins.common import constants from neutron.plugins.common import constants
@@ -27,7 +28,9 @@ from oslo_config import cfg
from oslo_utils import importutils from oslo_utils import importutils
from gbpservice.neutron.db.grouppolicy import group_policy_db as gpdb from gbpservice.neutron.db.grouppolicy import group_policy_db as gpdb
from gbpservice.neutron.db import servicechain_db as svcchain_db
from gbpservice.neutron.extensions import group_policy as gpolicy from gbpservice.neutron.extensions import group_policy as gpolicy
from gbpservice.neutron.extensions import servicechain as service_chain
import gbpservice.neutron.tests import gbpservice.neutron.tests
from gbpservice.neutron.tests.unit import common as cm from gbpservice.neutron.tests.unit import common as cm
@@ -36,6 +39,7 @@ JSON_FORMAT = 'json'
_uuid = uuidutils.generate_uuid _uuid = uuidutils.generate_uuid
TESTDIR = os.path.dirname(os.path.abspath(gbpservice.neutron.tests.__file__)) TESTDIR = os.path.dirname(os.path.abspath(gbpservice.neutron.tests.__file__))
ETCDIR = os.path.join(TESTDIR, 'etc') ETCDIR = os.path.join(TESTDIR, 'etc')
CHAIN_TENANT_ID = 'chain_owner'
class ApiManagerMixin(object): class ApiManagerMixin(object):
@@ -123,21 +127,31 @@ class ApiManagerMixin(object):
class GroupPolicyDBTestBase(ApiManagerMixin): class GroupPolicyDBTestBase(ApiManagerMixin):
resource_prefix_map = dict( resource_prefix_map = dict(
(k, constants.COMMON_PREFIXES[constants.SERVICECHAIN])
for k in service_chain.RESOURCE_ATTRIBUTE_MAP.keys())
resource_prefix_map.update(dict(
(k, constants.COMMON_PREFIXES[constants.GROUP_POLICY]) (k, constants.COMMON_PREFIXES[constants.GROUP_POLICY])
for k in gpolicy.RESOURCE_ATTRIBUTE_MAP.keys() for k in gpolicy.RESOURCE_ATTRIBUTE_MAP.keys()
) ))
fmt = JSON_FORMAT fmt = JSON_FORMAT
def __getattr__(self, item): def __getattr__(self, item):
# Verify is an update of a proper GBP object # Verify is an update of a proper GBP object
def _is_sc_resource(plural):
return plural in service_chain.RESOURCE_ATTRIBUTE_MAP
def _is_gbp_resource(plural): def _is_gbp_resource(plural):
return plural in gpolicy.RESOURCE_ATTRIBUTE_MAP return plural in gpolicy.RESOURCE_ATTRIBUTE_MAP
def _is_valid_resource(plural):
return _is_gbp_resource(plural) or _is_sc_resource(plural)
# Update Method # Update Method
if item.startswith('update_'): if item.startswith('update_'):
resource = item[len('update_'):] resource = item[len('update_'):]
plural = cm.get_resource_plural(resource) plural = cm.get_resource_plural(resource)
if _is_gbp_resource(plural): if _is_valid_resource(plural):
def update_wrapper(id, **kwargs): def update_wrapper(id, **kwargs):
return self._update_resource(id, resource, **kwargs) return self._update_resource(id, resource, **kwargs)
return update_wrapper return update_wrapper
@@ -145,7 +159,7 @@ class GroupPolicyDBTestBase(ApiManagerMixin):
if item.startswith('show_'): if item.startswith('show_'):
resource = item[len('show_'):] resource = item[len('show_'):]
plural = cm.get_resource_plural(resource) plural = cm.get_resource_plural(resource)
if _is_gbp_resource(plural): if _is_valid_resource(plural):
def show_wrapper(id, **kwargs): def show_wrapper(id, **kwargs):
return self._show_resource(id, plural, **kwargs) return self._show_resource(id, plural, **kwargs)
return show_wrapper return show_wrapper
@@ -153,7 +167,7 @@ class GroupPolicyDBTestBase(ApiManagerMixin):
if item.startswith('create_'): if item.startswith('create_'):
resource = item[len('create_'):] resource = item[len('create_'):]
plural = cm.get_resource_plural(resource) plural = cm.get_resource_plural(resource)
if _is_gbp_resource(plural): if _is_valid_resource(plural):
def create_wrapper(**kwargs): def create_wrapper(**kwargs):
return self._create_resource(resource, **kwargs) return self._create_resource(resource, **kwargs)
return create_wrapper return create_wrapper
@@ -161,13 +175,51 @@ class GroupPolicyDBTestBase(ApiManagerMixin):
if item.startswith('delete_'): if item.startswith('delete_'):
resource = item[len('delete_'):] resource = item[len('delete_'):]
plural = cm.get_resource_plural(resource) plural = cm.get_resource_plural(resource)
if _is_gbp_resource(plural): if _is_valid_resource(plural):
def delete_wrapper(id, **kwargs): def delete_wrapper(id, **kwargs):
return self._delete_resource(id, plural, **kwargs) return self._delete_resource(id, plural, **kwargs)
return delete_wrapper return delete_wrapper
raise AttributeError raise AttributeError
def _get_resource_plural(self, resource):
if resource.endswith('y'):
resource_plural = resource.replace('y', 'ies')
else:
resource_plural = resource + 's'
return resource_plural
def _test_list_resources(self, resource, items,
neutron_context=None,
query_params=None):
resource_plural = self._get_resource_plural(resource)
res = self._list(resource_plural,
neutron_context=neutron_context,
query_params=query_params)
params = None
if query_params:
params = query_params.split('&')
params = dict((x.split('=')[0], x.split('=')[1].split(','))
for x in params)
count = getattr(self.plugin, 'get_%s_count' % resource_plural)(
neutron_context or context.get_admin_context(), params)
self.assertEqual(len(res[resource_plural]), count)
resource = resource.replace('-', '_')
self.assertEqual(sorted([i['id'] for i in res[resource_plural]]),
sorted([i[resource]['id'] for i in items]))
def _create_profiled_servicechain_node(
self, service_type=constants.LOADBALANCER, shared_profile=False,
profile_tenant_id=None, **kwargs):
prof = self.create_service_profile(
service_type=service_type,
shared=shared_profile,
tenant_id=profile_tenant_id or self._tenant_id)['service_profile']
return self.create_servicechain_node(
service_profile_id=prof['id'], **kwargs)
class GroupPolicyDBTestPlugin(gpdb.GroupPolicyDbPlugin): class GroupPolicyDBTestPlugin(gpdb.GroupPolicyDbPlugin):
@@ -178,26 +230,42 @@ DB_GP_PLUGIN_KLASS = (GroupPolicyDBTestPlugin.__module__ + '.' +
GroupPolicyDBTestPlugin.__name__) GroupPolicyDBTestPlugin.__name__)
class ServiceChainDBTestPlugin(svcchain_db.ServiceChainDbPlugin):
supported_extension_aliases = ['servicechain']
DB_SC_PLUGIN_KLASS = (ServiceChainDBTestPlugin.__module__ + '.' +
ServiceChainDBTestPlugin.__name__)
class GroupPolicyDbTestCase(GroupPolicyDBTestBase, class GroupPolicyDbTestCase(GroupPolicyDBTestBase,
test_db_base_plugin_v2.NeutronDbPluginV2TestCase): test_db_base_plugin_v2.NeutronDbPluginV2TestCase):
def setUp(self, core_plugin=None, gp_plugin=None, service_plugins=None, def setUp(self, core_plugin=None, sc_plugin=None, service_plugins=None,
ext_mgr=None): ext_mgr=None, gp_plugin=None):
if not gp_plugin: sc_plugin = sc_plugin or DB_SC_PLUGIN_KLASS
gp_plugin = DB_GP_PLUGIN_KLASS gp_plugin = gp_plugin or DB_GP_PLUGIN_KLASS
if not service_plugins: if not service_plugins:
service_plugins = {'gp_plugin_name': gp_plugin} service_plugins = {
nattr.PLURALS['nat_pools'] = 'nat_pool' 'l3_plugin_name': 'router',
'gp_plugin_name': gp_plugin,
'sc_plugin_name': sc_plugin}
test_policy_file = ETCDIR + "/test-policy.json"
cfg.CONF.set_override('policy_file', test_policy_file)
super(GroupPolicyDbTestCase, self).setUp( super(GroupPolicyDbTestCase, self).setUp(
plugin=core_plugin, ext_mgr=ext_mgr, plugin=core_plugin, ext_mgr=ext_mgr,
service_plugins=service_plugins service_plugins=service_plugins
) )
self.plugin = importutils.import_object(gp_plugin) self.plugin = importutils.import_object(gp_plugin)
self._sc_plugin = importutils.import_object(sc_plugin)
if not ext_mgr: if not ext_mgr:
ext_mgr = extensions.PluginAwareExtensionManager.get_instance() ext_mgr = extensions.PluginAwareExtensionManager.get_instance()
self.ext_api = test_extensions.setup_extensions_middleware(ext_mgr) self.ext_api = test_extensions.setup_extensions_middleware(ext_mgr)
test_policy_file = ETCDIR + "/test-policy.json" engine = db_api.get_engine()
cfg.CONF.set_override('policy_file', test_policy_file) model_base.BASEV2.metadata.create_all(engine)
plugins = manager.NeutronManager.get_service_plugins() plugins = manager.NeutronManager.get_service_plugins()
self._gbp_plugin = plugins.get(constants.GROUP_POLICY) self._gbp_plugin = plugins.get(constants.GROUP_POLICY)

View File

@@ -210,6 +210,7 @@ class TestMappedGroupResourceAttrs(GroupPolicyMappingDbTestCase):
ports = [port1['port']['id'], port2['port']['id']] ports = [port1['port']['id'], port2['port']['id']]
pts = [self.create_policy_target(port_id=ports[0]), pts = [self.create_policy_target(port_id=ports[0]),
self.create_policy_target(port_id=ports[1])] self.create_policy_target(port_id=ports[1])]
self._test_list_resources('policy_target', pts)
self._test_list_resources('policy_target', [pts[0]], self._test_list_resources('policy_target', [pts[0]],
query_params='port_id=' + ports[0]) query_params='port_id=' + ports[0])
@@ -221,8 +222,10 @@ class TestMappedGroupResourceAttrs(GroupPolicyMappingDbTestCase):
l2_policies = [self.create_l2_policy(network_id=networks[0]), l2_policies = [self.create_l2_policy(network_id=networks[0]),
self.create_l2_policy(network_id=networks[1])] self.create_l2_policy(network_id=networks[1])]
self._test_list_resources( self._test_list_resources(
'l2_policy', [l2_policies[0]], 'l2_policy', l2_policies)
query_params='network_id=' + networks[0]) self._test_list_resources(
'l2_policy', [l2_policies[0]],
query_params='network_id=' + networks[0])
def test_list_es(self): def test_list_es(self):
with self.subnet(cidr='10.10.1.0/24') as subnet1: with self.subnet(cidr='10.10.1.0/24') as subnet1:
@@ -232,5 +235,7 @@ class TestMappedGroupResourceAttrs(GroupPolicyMappingDbTestCase):
self.create_external_segment(subnet_id=subnets[0]), self.create_external_segment(subnet_id=subnets[0]),
self.create_external_segment(subnet_id=subnets[1])] self.create_external_segment(subnet_id=subnets[1])]
self._test_list_resources( self._test_list_resources(
'external_segment', [external_segments[0]], 'external_segment', external_segments)
query_params='subnet_id=' + subnets[0]) self._test_list_resources(
'external_segment', [external_segments[0]],
query_params='subnet_id=' + subnets[0])

View File

@@ -13,19 +13,11 @@
import webob.exc import webob.exc
from neutron.api import extensions
from neutron import context from neutron import context
from neutron.db import api as db_api
from neutron.db import model_base
from neutron.openstack.common import uuidutils from neutron.openstack.common import uuidutils
from neutron.plugins.common import constants from neutron.plugins.common import constants
from neutron.tests.unit.api import test_extensions
from neutron.tests.unit.db import test_db_base_plugin_v2
from oslo_config import cfg from oslo_config import cfg
from oslo_utils import importutils
from gbpservice.neutron.db import servicechain_db as svcchain_db
from gbpservice.neutron.extensions import group_policy as gpolicy
from gbpservice.neutron.extensions import servicechain as service_chain from gbpservice.neutron.extensions import servicechain as service_chain
from gbpservice.neutron.tests.unit import common as cm from gbpservice.neutron.tests.unit import common as cm
from gbpservice.neutron.tests.unit.db.grouppolicy import test_group_policy_db from gbpservice.neutron.tests.unit.db.grouppolicy import test_group_policy_db
@@ -33,135 +25,20 @@ from gbpservice.neutron.tests.unit.db.grouppolicy import test_group_policy_db
JSON_FORMAT = 'json' JSON_FORMAT = 'json'
class ServiceChainDBTestBase(test_group_policy_db.ApiManagerMixin):
resource_prefix_map = dict(
(k, constants.COMMON_PREFIXES[constants.SERVICECHAIN])
for k in service_chain.RESOURCE_ATTRIBUTE_MAP.keys())
resource_prefix_map.update(dict(
(k, constants.COMMON_PREFIXES[constants.GROUP_POLICY])
for k in gpolicy.RESOURCE_ATTRIBUTE_MAP.keys()
))
fmt = JSON_FORMAT
def __getattr__(self, item):
# Verify is an update of a proper GBP object
def _is_sc_resource(plural):
return plural in service_chain.RESOURCE_ATTRIBUTE_MAP
def _is_gbp_resource(plural):
return plural in gpolicy.RESOURCE_ATTRIBUTE_MAP
def _is_valid_resource(plural):
return _is_gbp_resource(plural) or _is_sc_resource(plural)
# Update Method
if item.startswith('update_'):
resource = item[len('update_'):]
plural = cm.get_resource_plural(resource)
if _is_valid_resource(plural):
def update_wrapper(id, **kwargs):
return self._update_resource(id, resource, **kwargs)
return update_wrapper
# Show Method
if item.startswith('show_'):
resource = item[len('show_'):]
plural = cm.get_resource_plural(resource)
if _is_valid_resource(plural):
def show_wrapper(id, **kwargs):
return self._show_resource(id, plural, **kwargs)
return show_wrapper
# Create Method
if item.startswith('create_'):
resource = item[len('create_'):]
plural = cm.get_resource_plural(resource)
if _is_valid_resource(plural):
def create_wrapper(**kwargs):
return self._create_resource(resource, **kwargs)
return create_wrapper
# Delete Method
if item.startswith('delete_'):
resource = item[len('delete_'):]
plural = cm.get_resource_plural(resource)
if _is_valid_resource(plural):
def delete_wrapper(id, **kwargs):
return self._delete_resource(id, plural, **kwargs)
return delete_wrapper
raise AttributeError
def _get_resource_plural(self, resource):
if resource.endswith('y'):
resource_plural = resource.replace('y', 'ies')
else:
resource_plural = resource + 's'
return resource_plural
def _test_list_resources(self, resource, items,
neutron_context=None,
query_params=None):
resource_plural = self._get_resource_plural(resource)
res = self._list(resource_plural,
neutron_context=neutron_context,
query_params=query_params)
params = query_params.split('&')
params = dict((x.split('=')[0], x.split('=')[1].split(','))
for x in params)
count = getattr(self.plugin, 'get_%s_count' % resource_plural)(
neutron_context or context.get_admin_context(), params)
self.assertEqual(len(res[resource_plural]), count)
resource = resource.replace('-', '_')
self.assertEqual(sorted([i['id'] for i in res[resource_plural]]),
sorted([i[resource]['id'] for i in items]))
def _create_profiled_servicechain_node(
self, service_type=constants.LOADBALANCER, shared_profile=False,
profile_tenant_id=None, **kwargs):
prof = self.create_service_profile(
service_type=service_type,
shared=shared_profile,
tenant_id=profile_tenant_id or self._tenant_id)['service_profile']
return self.create_servicechain_node(
service_profile_id=prof['id'], **kwargs)
class ServiceChainDBTestPlugin(svcchain_db.ServiceChainDbPlugin):
supported_extension_aliases = ['servicechain']
DB_GP_PLUGIN_KLASS = (ServiceChainDBTestPlugin.__module__ + '.' +
ServiceChainDBTestPlugin.__name__)
GP_PLUGIN_KLASS = ( GP_PLUGIN_KLASS = (
"gbpservice.neutron.services.grouppolicy.plugin.GroupPolicyPlugin") "gbpservice.neutron.services.grouppolicy.plugin.GroupPolicyPlugin")
class ServiceChainDbTestCase(ServiceChainDBTestBase, class ServiceChainDbTestCase(test_group_policy_db.GroupPolicyDbTestCase):
test_db_base_plugin_v2.NeutronDbPluginV2TestCase):
def setUp(self, core_plugin=None, sc_plugin=None, service_plugins=None, def setUp(self, core_plugin=None, sc_plugin=None, service_plugins=None,
ext_mgr=None, gp_plugin=None): ext_mgr=None, gp_plugin=None):
if not sc_plugin:
sc_plugin = DB_GP_PLUGIN_KLASS
if not service_plugins:
service_plugins = {
'l3_plugin_name': 'router',
'gp_plugin_name': gp_plugin or GP_PLUGIN_KLASS,
'sc_plugin_name': sc_plugin}
super(ServiceChainDbTestCase, self).setUp( super(ServiceChainDbTestCase, self).setUp(
plugin=core_plugin, ext_mgr=ext_mgr, gp_plugin=gp_plugin or GP_PLUGIN_KLASS, core_plugin=core_plugin,
service_plugins=service_plugins sc_plugin=sc_plugin, service_plugins=service_plugins,
) ext_mgr=ext_mgr)
self.plugin = importutils.import_object(sc_plugin) self.plugin = self._sc_plugin
if not ext_mgr:
ext_mgr = extensions.PluginAwareExtensionManager.get_instance()
self.ext_api = test_extensions.setup_extensions_middleware(ext_mgr)
engine = db_api.get_engine()
model_base.BASEV2.metadata.create_all(engine)
class TestServiceChainResources(ServiceChainDbTestCase): class TestServiceChainResources(ServiceChainDbTestCase):

View File

@@ -149,16 +149,6 @@ class ApicMappingTestCase(
self.driver.apic_manager.ext_net_dict.update( self.driver.apic_manager.ext_net_dict.update(
self._build_external_dict(x[0], x[1])) self._build_external_dict(x[0], x[1]))
def _check_call_list(self, expected, observed):
for call in expected:
self.assertTrue(call in observed,
msg='Call not found, expected:\n%s\nobserved:'
'\n%s' % (str(call), str(observed)))
observed.remove(call)
self.assertFalse(
len(observed),
msg='There are more calls than expected: %s' % str(observed))
def _create_simple_policy_rule(self, direction='bi', protocol='tcp', def _create_simple_policy_rule(self, direction='bi', protocol='tcp',
port_range=80, shared=False, port_range=80, shared=False,
action_type='allow', action_value=None): action_type='allow', action_value=None):

View File

@@ -853,8 +853,8 @@ class TestNatPool(GroupPolicyPluginTestCase):
class TestPolicyTarget(GroupPolicyPluginTestCase): class TestPolicyTarget(GroupPolicyPluginTestCase):
def _test_cross_tenant_fails(self, is_admin=False): def _test_cross_tenant(self, is_admin=False):
status = {False: 404, True: 400} status = {False: 404, True: 201}
ptg = self.create_policy_target_group( ptg = self.create_policy_target_group(
expected_res_status=201, tenant_id='tenant', expected_res_status=201, tenant_id='tenant',
is_admin_context=is_admin)['policy_target_group'] is_admin_context=is_admin)['policy_target_group']
@@ -865,9 +865,6 @@ class TestPolicyTarget(GroupPolicyPluginTestCase):
if not is_admin: if not is_admin:
self.assertEqual( self.assertEqual(
'GbpResourceNotFound', res['NeutronError']['type']) 'GbpResourceNotFound', res['NeutronError']['type'])
else:
self.assertEqual(
'InvalidCrossTenantReference', res['NeutronError']['type'])
# Create EP without PTG # Create EP without PTG
pt = self.create_policy_target( pt = self.create_policy_target(
@@ -875,28 +872,25 @@ class TestPolicyTarget(GroupPolicyPluginTestCase):
is_admin_context=is_admin)['policy_target'] is_admin_context=is_admin)['policy_target']
# Update PT fails # Update PT fails
res = self.update_policy_target(pt['id'], res = self.update_policy_target(
expected_res_status=status[is_admin], pt['id'], tenant_id='another', policy_target_group_id=ptg['id'],
tenant_id='another', expected_res_status=status[is_admin] if not is_admin else 200,
policy_target_group_id=ptg['id'], is_admin_context=is_admin)
is_admin_context=is_admin)
if not is_admin: if not is_admin:
self.assertEqual( self.assertEqual(
'GbpResourceNotFound', res['NeutronError']['type']) 'GbpResourceNotFound', res['NeutronError']['type'])
else:
self.assertEqual(
'InvalidCrossTenantReference', res['NeutronError']['type'])
def test_cross_tenant_fails(self): def test_cross_tenant_fails(self):
self._test_cross_tenant_fails() self._test_cross_tenant()
def test_cross_tenant_fails_admin(self): def test_cross_tenant_admin(self):
self._test_cross_tenant_fails(True) self._test_cross_tenant(True)
class TestPolicyAction(GroupPolicyPluginTestCase): class TestPolicyAction(GroupPolicyPluginTestCase):
def test_redirect_value_fails(self): def test_redirect_value(self):
scs_id = self._create_servicechain_spec( scs_id = self._create_servicechain_spec(
node_types=['FIREWALL_TRANSPARENT']) node_types=['FIREWALL_TRANSPARENT'])
res = self.create_policy_action(action_type='redirect', res = self.create_policy_action(action_type='redirect',
@@ -913,9 +907,7 @@ class TestPolicyAction(GroupPolicyPluginTestCase):
res = self.create_policy_action( res = self.create_policy_action(
action_type='redirect', action_value=scs_id, tenant_id='different', action_type='redirect', action_value=scs_id, tenant_id='different',
expected_res_status=400, is_admin_context=True) expected_res_status=201, is_admin_context=True)
self.assertEqual(
'InvalidCrossTenantReference', res['NeutronError']['type'])
res = self.create_policy_action( res = self.create_policy_action(
action_type='redirect', action_value=scs_id, action_type='redirect', action_value=scs_id,
@@ -929,7 +921,7 @@ class TestPolicyAction(GroupPolicyPluginTestCase):
node_types=['FIREWALL_TRANSPARENT'], shared=True) node_types=['FIREWALL_TRANSPARENT'], shared=True)
self.create_policy_action( self.create_policy_action(
action_type='redirect', action_value=scs_id, shared=True, action_type='redirect', action_value=scs_id, shared=True,
expected_res_status=201)['policy_action'] expected_res_status=201)
data = {'servicechain_spec': {'shared': False}} data = {'servicechain_spec': {'shared': False}}
scs_req = self.new_update_request( scs_req = self.new_update_request(
SERVICECHAIN_SPECS, data, scs_id, self.fmt) SERVICECHAIN_SPECS, data, scs_id, self.fmt)

View File

@@ -172,6 +172,17 @@ class TestPolicyRuleSet(OneConvergenceGBPDriverTestCase,
pass pass
class TestServiceChain(OneConvergenceGBPDriverTestCase,
test_resource_mapping.TestServiceChain):
pass
class TestServiceChainAdminOwner(
OneConvergenceGBPDriverTestCase,
test_resource_mapping.TestServiceChainAdminOwner):
pass
class TestPolicyAction(OneConvergenceGBPDriverTestCase, class TestPolicyAction(OneConvergenceGBPDriverTestCase,
test_resource_mapping.TestPolicyAction): test_resource_mapping.TestPolicyAction):
pass pass

View File

@@ -47,6 +47,7 @@ CORE_PLUGIN = ('gbpservice.neutron.tests.unit.services.grouppolicy.'
GP_PLUGIN_KLASS = ( GP_PLUGIN_KLASS = (
"gbpservice.neutron.services.grouppolicy.plugin.GroupPolicyPlugin" "gbpservice.neutron.services.grouppolicy.plugin.GroupPolicyPlugin"
) )
CHAIN_TENANT_ID = 'sci_owner'
class NodeCompositionPluginTestCase( class NodeCompositionPluginTestCase(
@@ -155,7 +156,8 @@ class NodeCompositionPluginTestCase(
provider = self.create_policy_target_group()['policy_target_group'] provider = self.create_policy_target_group()['policy_target_group']
consumer = self.create_policy_target_group()['policy_target_group'] consumer = self.create_policy_target_group()['policy_target_group']
management = self.create_policy_target_group( management = self.create_policy_target_group(
service_management=True)['policy_target_group'] service_management=True,
is_admin_context=True)['policy_target_group']
classifier = self.create_policy_classifier()['policy_classifier'] classifier = self.create_policy_classifier()['policy_classifier']
instance = self.create_servicechain_instance( instance = self.create_servicechain_instance(
@@ -600,7 +602,7 @@ class NodeCompositionPluginTestCase(
self.create_policy_target_group( self.create_policy_target_group(
provided_policy_rule_sets={prs['id']: ''}) provided_policy_rule_sets={prs['id']: ''})
self.create_policy_target_group( self.create_policy_target_group(
consumed_policy_rule_sets={prs['id']: ''})['policy_target_group'] consumed_policy_rule_sets={prs['id']: ''})
instances = self._list('servicechain_instances')[ instances = self._list('servicechain_instances')[
'servicechain_instances'] 'servicechain_instances']
self.assertEqual(1, len(instances)) self.assertEqual(1, len(instances))
@@ -679,6 +681,9 @@ class AgnosticChainPlumberTestCase(NodeCompositionPluginTestCase):
self.driver.get_plumbing_info = mock.Mock() self.driver.get_plumbing_info = mock.Mock()
self.driver.get_plumbing_info.return_value = {} self.driver.get_plumbing_info.return_value = {}
def _assert_service_target_tenant(self, policy_target, provider):
self.assertEqual(provider['tenant_id'], policy_target['tenant_id'])
def _create_simple_chain(self): def _create_simple_chain(self):
node = self._create_profiled_servicechain_node( node = self._create_profiled_servicechain_node(
service_type="LOADBALANCER", service_type="LOADBALANCER",
@@ -718,9 +723,11 @@ class AgnosticChainPlumberTestCase(NodeCompositionPluginTestCase):
for target in targets: for target in targets:
self.assertEqual(node['id'], target.servicechain_node_id) self.assertEqual(node['id'], target.servicechain_node_id)
pt = self.show_policy_target( pt = self.show_policy_target(
target.policy_target_id)['policy_target'] target.policy_target_id,
is_admin_context=True)['policy_target']
self.assertEqual(prov_cons[target.relationship]['id'], self.assertEqual(prov_cons[target.relationship]['id'],
pt['policy_target_group_id']) pt['policy_target_group_id'])
self._assert_service_target_tenant(pt, provider)
self.assertNotEqual(old_relationship, target.relationship) self.assertNotEqual(old_relationship, target.relationship)
old_relationship = target.relationship old_relationship = target.relationship
@@ -731,7 +738,8 @@ class AgnosticChainPlumberTestCase(NodeCompositionPluginTestCase):
self.assertEqual(0, len(new_targets)) self.assertEqual(0, len(new_targets))
for target in targets: for target in targets:
self.show_policy_target( self.show_policy_target(
target.policy_target_id, expected_res_status=404) target.policy_target_id, is_admin_context=True,
expected_res_status=404)
def test_pt_override(self): def test_pt_override(self):
context = n_context.get_admin_context() context = n_context.get_admin_context()
@@ -742,7 +750,8 @@ class AgnosticChainPlumberTestCase(NodeCompositionPluginTestCase):
targets = model.get_service_targets(context.session) targets = model.get_service_targets(context.session)
self.assertEqual(1, len(targets)) self.assertEqual(1, len(targets))
pt = self.show_policy_target( pt = self.show_policy_target(
targets[0].policy_target_id)['policy_target'] targets[0].policy_target_id,
is_admin_context=True)['policy_target']
self.assertEqual(test_name, pt['name']) self.assertEqual(test_name, pt['name'])
def test_ptg_delete(self): def test_ptg_delete(self):
@@ -839,3 +848,29 @@ class TestQuotasForServiceChain(test_base.ServiceChainPluginTestCase):
self.assertRaises(webob.exc.HTTPClientError, self.assertRaises(webob.exc.HTTPClientError,
self.create_policy_target_group, self.create_policy_target_group,
consumed_policy_rule_sets={prs['id']: ''}) consumed_policy_rule_sets={prs['id']: ''})
class AgnosticChainPlumberAdminOwner(AgnosticChainPlumberTestCase):
def setUp(self):
mock.patch('gbpservice.neutron.services.grouppolicy.drivers.'
'resource_mapping.ResourceMappingDriver.'
'chain_tenant_keystone_client').start()
res = mock.patch('gbpservice.neutron.services.grouppolicy.drivers.'
'resource_mapping.ResourceMappingDriver.'
'chain_tenant_id').start()
res.return_value = CHAIN_TENANT_ID
super(AgnosticChainPlumberAdminOwner, self).setUp()
def _assert_service_target_tenant(self, policy_target, provider):
self.assertEqual(CHAIN_TENANT_ID, policy_target['tenant_id'])
def test_update_service_chain(self):
# This directly updates the SCI, which requires the right tenant to be
# done
pass
def test_instance_update(self):
# This directly updates the SCI, which requires the right tenant to be
# done
pass

View File

@@ -64,6 +64,10 @@ class SimpleChainDriverTestCase(
STACK_DELETE_RETRY_WAIT, STACK_DELETE_RETRY_WAIT,
group='simplechain') group='simplechain')
super(SimpleChainDriverTestCase, self).setUp() super(SimpleChainDriverTestCase, self).setUp()
key_client = mock.patch(
'gbpservice.neutron.services.servicechain.plugins.msc.drivers.'
'simplechain_driver.HeatClient._get_auth_token').start()
key_client.return_value = 'mysplendidtoken'
class TestServiceChainInstance(SimpleChainDriverTestCase): class TestServiceChainInstance(SimpleChainDriverTestCase):