node composition plugin architecture

The Node Composition Plugin (NCP) is introduced here in
its most simple form: The interaction with the Node Drivers
is still completely missing.

This patch is intended to collect the whole internal API
between the NCP and the Node Drivers, taking also
into account the new NodeDriverContext.

Partially implements blueprint node-centric-chain-plugin

Change-Id: I0f791d2be8b5ef5d9bf7a297e0dbdc0248350edd
This commit is contained in:
Ivar Lazzaro 2015-06-08 19:54:10 -07:00
parent 2527cdd688
commit 657306d199
18 changed files with 908 additions and 353 deletions

View File

@ -85,8 +85,8 @@ class PolicyTargetGroupInUse(GroupPolicyBadRequest):
class InvalidPortForPTG(GroupPolicyBadRequest):
message = _("Subnet %(port_subnet_id)s of port %(port_id)s does not "
"match subnet %(ptg_subnet_id)s of Policy Target Group "
"%(policy_target_group_id)s.")
"match subnet %(ptg_subnet_id)s of Policy Target Group "
"%(policy_target_group_id)s.")
class InvalidSubnetForPTG(GroupPolicyBadRequest):

View File

@ -461,10 +461,22 @@ class GroupPolicyPlugin(group_policy_mapping_db.GroupPolicyMappingDbPlugin):
context, policy_target_group_id)
pt_ids = policy_target_group['policy_targets']
for pt in self.get_policy_targets(context, {'id': pt_ids}):
if pt['port_id']:
if self._is_port_bound(pt['port_id']):
if pt['port_id'] and self._is_port_bound(pt['port_id']):
raise gp_exc.PolicyTargetGroupInUse(
policy_target_group=policy_target_group_id)
policy_context = p_context.PolicyTargetGroupContext(
self, context, policy_target_group)
self.policy_driver_manager.delete_policy_target_group_precommit(
policy_context)
# Disassociate all the PRSs first, this will trigger service chains
# deletion.
self.update_policy_target_group(
context, policy_target_group_id,
{'policy_target_group': {'provided_policy_rule_sets': {},
'consumed_policy_rule_sets': {}}})
with session.begin(subtransactions=True):
for pt_id in pt_ids:
# We will allow PTG deletion if all PTs are unused.
# We could have cleaned these opportunistically in
@ -472,10 +484,6 @@ class GroupPolicyPlugin(group_policy_mapping_db.GroupPolicyMappingDbPlugin):
# such that either all unused PTs are deleted
# or nothing is.
self.delete_policy_target(context, pt_id)
policy_context = p_context.PolicyTargetGroupContext(
self, context, policy_target_group)
self.policy_driver_manager.delete_policy_target_group_precommit(
policy_context)
super(GroupPolicyPlugin, self).delete_policy_target_group(
context, policy_target_group_id)

View File

@ -0,0 +1,26 @@
# 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_config import cfg
service_chain_opts = [
cfg.ListOpt('node_drivers',
default=['node_dummy'],
help=_("An ordered list of service chain node drivers "
"entrypoints to be loaded from the "
"gbpservice.neutron.servicechain.ncp_drivers "
"namespace."))
]
cfg.CONF.register_opts(service_chain_opts, "node_composition_plugin")

View File

@ -0,0 +1,177 @@
# 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 neutron import context as n_context
from neutron import manager
from neutron.plugins.common import constants as pconst
from gbpservice.neutron.extensions import group_policy
def get_gbp_plugin():
return manager.NeutronManager.get_service_plugins().get("GROUP_POLICY")
def get_node_driver_context(sc_plugin, context, sc_instance,
current_node, original_node=None,
management_group=None, service_targets=None):
specs = sc_plugin.get_servicechain_specs(
context, filters={'id': sc_instance['servicechain_specs']})
provider = _ptg_or_ep(context, sc_instance['provider_ptg_id'])
consumer = _ptg_or_ep(context, sc_instance['consumer_ptg_id'])
current_profile = sc_plugin.get_service_profile(
context, current_node['service_profile_id'])
original_profile = sc_plugin.get_service_profile(
context,
original_node['service_profile_id']) if original_node else None
return NodeDriverContext(sc_plugin=sc_plugin,
context=context,
service_chain_instance=sc_instance,
service_chain_specs=specs,
current_service_chain_node=current_node,
current_service_profile=current_profile,
provider_group=provider,
consumer_group=consumer,
management_group=management_group,
original_service_chain_node=original_node,
original_service_profile=original_profile,
service_targets=service_targets)
def _ptg_or_ep(context, group_id):
group = None
if group_id:
try:
group = get_gbp_plugin().get_policy_target_group(context, group_id)
except group_policy.PolicyTargetGroupNotFound:
# Could be EP
context.session.rollback()
group = get_gbp_plugin().get_external_policy(context, group_id)
return group
class NodeDriverContext(object):
""" Context passed down to NCC Node Drivers."""
def __init__(self, sc_plugin, context, service_chain_instance,
service_chain_specs, current_service_chain_node,
current_service_profile, provider_group, consumer_group=None,
management_group=None, original_service_chain_node=None,
original_service_profile=None, service_targets=None):
self._gbp_plugin = get_gbp_plugin()
self._sc_plugin = sc_plugin
self._plugin_context = context
self._admin_context = None
self._service_chain_instance = service_chain_instance
self._current_service_chain_node = current_service_chain_node
self._current_service_profile = current_service_profile
self._original_service_chain_node = original_service_chain_node
self._original_service_profile = original_service_profile
self._service_targets = service_targets
self._service_chain_specs = service_chain_specs
self._provider_group = provider_group
self._consumer_group = consumer_group
self._management_group = management_group
self._relevant_specs = None
self._core_plugin = manager.NeutronManager.get_plugin()
self._l3_plugin = manager.NeutronManager.get_service_plugins().get(
pconst.L3_ROUTER_NAT)
@property
def gbp_plugin(self):
return self._gbp_plugin
@property
def sc_plugin(self):
return self._sc_plugin
@property
def core_plugin(self):
return self._core_plugin
@property
def l3_plugin(self):
return self._l3_plugin
@property
def plugin_context(self):
return self._plugin_context
@property
def plugin_session(self):
return self._plugin_context.session
@property
def session(self):
return self.plugin_session
@property
def admin_context(self):
if not self._admin_context:
self._admin_context = n_context.get_admin_context()
return self._admin_context
@property
def admin_session(self):
return self.admin_context.session
@property
def instance(self):
return self._service_chain_instance
@property
def current_node(self):
return self._current_service_chain_node
@property
def current_profile(self):
return self._current_service_profile
@property
def original_node(self):
return self._original_service_chain_node
@property
def original_profile(self):
return self._original_service_profile
@property
def relevant_specs(self):
"""Get specs on the SCI containing this particular Node."""
if not self._relevant_specs:
self._relevant_specs = [x for x in self._service_chain_specs if
self.current_node['id'] in x['nodes']]
return self._relevant_specs
@property
def service_targets(self):
""" Returns the service targets assigned for this service if any.
The result looks like the following:
{
"provider": [pt_uuids],
"consumer": [pt_uuids],
"management": [pt_uuids],
}
"""
return self._service_targets
@property
def provider(self):
return self._provider_group
@property
def consumer(self):
return self._consumer_group
@property
def management(self):
return self._management_group

View File

@ -0,0 +1,145 @@
# 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.
import abc
import six
@six.add_metaclass(abc.ABCMeta)
class NodeDriverBase(object):
"""Node Driver Base class for Node Composition Plugin (NCP).
A Node Driver is the fundamental unit of the NCP service chain plugin.
It is invoked every time an operation has to be executed on Service Node
instances (eg. services that are part of a deployed chain)
which the Node Driver is capable of deploy, destroy and update.
The Node Driver may expose resource needs to the NCP plugin, that will
make sure that the NodeDriverContext is enriched with all that's needed by
the driver.
"""
@abc.abstractmethod
def initialize(self):
"""Perform driver initialization.
Called after all drivers have been loaded and the database has
been initialized. No abstract methods defined below will be
called prior to this method being called.
"""
pass
@abc.abstractmethod
def get_plumbing_info(self, context):
""" Tells the NCP Plugin which kind of plumbing is needed by the Node.
The plumbing info is defined as a collection of needed policy targets
on a specific role, this may vary based on the node
(obtained from the NodeDriverContext) that the specific driver is asked
to deploy. An example of plumbing info is the following:
{
"management": <list of updated PT body dicts, one for each needed>,
"provider": <list of updated PT body dicts, one for each needed>,
"consumer": <list of updated PT body dicts, one for each needed>
}
The role (key of the above dictionary) specifies in which "side" the
policy target has to exist. Depending on the kind of chaining the
Neutron port could actually be placed somewhere else! The value
is a list of attributes intended to override the PT body. This could
be used, for example, for providing explicit Neutron Ports when the
driver requires it or for establishing a naming convention for the PTs.
An empty dictionary will be mostly used in this case, which will
indicate a basic PT creation:
{
"management": [{}], # One PT needed in the management
"provider": [{}, {port_id: 'a'}], # Two PT needed in the provider
"consumer": [] # Zero PT needed in the consumer
}
"""
pass
@abc.abstractmethod
def validate_create(self, context):
"""Validate whether a SCN can be processed or not for creation.
This method is intended as a indicative measure of whether the NCP
plugin should use this specific driver for scheduling a given node.
A successful validation is a prerequisite but doesn't guarantee that
this driver will ultimately be chosen.
:param context: NodeDriverContext instance describing the service chain
and the specific node to be processed by this driver.
"""
pass
@abc.abstractmethod
def validate_update(self, context):
"""Validate whether a SCN can be processed or not.
This method will be called whenever a specific Node owned by this
driver needs to be updated. It should be used to verify whether the
Driver is capable of enforcing the update or not.
:param context: NodeDriverContext instance describing the service chain
and the specific node to be processed by this driver.
"""
pass
@abc.abstractmethod
def create(self, context):
"""Instantiate a Service Chain Node based on the chain context.
This method will be called at Service Chain instantiation time by the
NCP plugin. Every scheduled Node Driver will be assigned a Node of the
chain that has to be deployed based on the node definition and the
service chain context. The same driver could be called multiple times
on different nodes of the same chain.
The datapath is expected to work according to the user intent at the
end of the chain instantiation.
:param context: NodeDriverContext instance describing the service chain
and the specific node to be processed by this driver.
"""
pass
@abc.abstractmethod
def delete(self, context):
"""Destroy a deployed Service Chain Node.
This method will be called when a Service Chain Instance is destroyed
or in case of node rescheduling. The driver is expected to undeploy the
specific node and free the owned resources. Freeing the resources
created by the NCP plugin as a consequence of the plumbing_info
method belongs to the NCP plugin, and it is in charge of disposing
them if needed.
:param context: NodeDriverContext instance describing the service chain
and the specific node to be processed by this driver.
"""
pass
@abc.abstractmethod
def update(self, context):
"""Update a deployed Service Chain Node.
Some changes in the Service Chain Node could need modifications in all
its instances. This method will be used in order to synchronize the
service configuration with the user expectation.
The original node definition is provided in the context in order to
calculate the difference if needed.
:param context: NodeDriverContext instance describing the service chain
and the specific node to be processed by this driver.
"""
pass

View File

@ -0,0 +1,57 @@
# 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_config import cfg
from oslo_log import log as logging
import stevedore
from gbpservice.neutron.services.servicechain.plugins.ncp import config # noqa
LOG = logging.getLogger(__name__)
class NodeDriverManager(stevedore.named.NamedExtensionManager):
"""Route servicechain APIs to servicechain node drivers.
"""
def __init__(self):
# Registered node drivers, keyed by name.
self.drivers = {}
# Ordered list of node drivers.
self.ordered_drivers = []
names = cfg.CONF.node_composition_plugin.node_drivers
LOG.info(_("Configured service chain node driver names: %s"), names)
super(NodeDriverManager,
self).__init__(
'gbpservice.neutron.servicechain.ncp_drivers', names,
invoke_on_load=True, name_order=True)
LOG.info(_("Loaded service chain node driver names: %s"), self.names())
self._register_drivers()
def _register_drivers(self):
"""Register all service chain node drivers."""
for ext in self:
self.drivers[ext.name] = ext
self.ordered_drivers.append(ext)
LOG.info(_("Registered service chain node drivers: %s"),
[driver.name for driver in self.ordered_drivers])
def initialize(self):
"""Initialize all the service chain node drivers."""
self.native_bulk_support = True
for driver in self.ordered_drivers:
LOG.info(_("Initializing service chain node drivers '%s'"),
driver.name)
driver.obj.initialize()
self.native_bulk_support &= getattr(driver.obj,
'native_bulk_support', True)

View File

@ -0,0 +1,48 @@
# 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 neutron.common import log
from gbpservice.neutron.services.servicechain.plugins.ncp import driver_base
class NoopNodeDriver(driver_base.NodeDriverBase):
initialized = False
@log.log
def initialize(self):
self.initialized = True
@log.log
def get_plumbing_info(self, context):
pass
@log.log
def validate_create(self, context):
pass
@log.log
def validate_update(self, context):
pass
@log.log
def create(self, context):
pass
@log.log
def delete(self, context):
pass
@log.log
def update(self, context):
pass

View File

@ -0,0 +1,32 @@
# 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 import log as logging
from gbpservice.neutron.db import servicechain_db
from gbpservice.neutron.services.servicechain.plugins.ncp import (
node_driver_manager as manager)
LOG = logging.getLogger(__name__)
class NodeCompositionPlugin(servicechain_db.ServiceChainDbPlugin):
"""Implementation of the Service Chain Plugin.
"""
supported_extension_aliases = ["servicechain"]
def __init__(self):
self.driver_manager = manager.NodeDriverManager()
super(NodeCompositionPlugin, self).__init__()
self.driver_manager.initialize()

View File

@ -293,6 +293,7 @@ def get_update_nat_pool_attrs():
return {'name': 'new_name'}
# Service Chain
@gbp_attributes
def get_create_service_profile_default_attrs():
return {'name': '', 'description': ''}
@ -314,6 +315,92 @@ def get_update_service_profile_attrs():
}
@gbp_attributes
def get_create_servicechain_node_default_attrs():
return {
'name': '',
'description': '',
'config': '{}',
'service_type': None,
'shared': False,
}
@gbp_attributes
def get_create_servicechain_node_attrs():
return {
'name': 'servicechain1',
'service_profile_id': _uuid(),
'tenant_id': _uuid(),
'description': 'test servicechain node',
'config': '{}',
'service_type': None,
'shared': True,
}
@gbp_attributes
def get_update_servicechain_node_attrs():
return {
'name': 'new_name',
}
@gbp_attributes
def get_create_servicechain_spec_default_attrs():
return {
'name': '',
'description': '',
'nodes': [],
'shared': False,
}
@gbp_attributes
def get_create_servicechain_spec_attrs():
return {
'name': 'servicechainspec1',
'nodes': [_uuid(), _uuid()],
'tenant_id': _uuid(),
'description': 'test servicechain spec',
'shared': True,
}
@gbp_attributes
def get_update_servicechain_spec_attrs():
return {
'name': 'new_name',
'nodes': [_uuid()]
}
@gbp_attributes
def get_create_servicechain_instance_default_attrs():
return {'name': '', 'description': '', 'config_param_values': "{}"}
@gbp_attributes
def get_create_servicechain_instance_attrs():
return {
'name': 'servicechaininstance1',
'servicechain_specs': [_uuid()],
'tenant_id': _uuid(),
'provider_ptg_id': _uuid(),
'consumer_ptg_id': _uuid(),
'classifier_id': _uuid(),
'config_param_values': "{}",
'description': 'test servicechain instance'
}
def get_update_servicechain_instance_attrs():
return {
'name': 'new_name',
'servicechain_specs': [_uuid()]
}
def get_resource_plural(resource):
if resource.endswith('y'):
resource_plural = resource.replace('y', 'ies')

View File

@ -37,52 +37,7 @@ TESTDIR = os.path.dirname(os.path.abspath(gbpservice.neutron.tests.__file__))
ETCDIR = os.path.join(TESTDIR, 'etc')
class GroupPolicyDBTestBase(object):
resource_prefix_map = 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_gbp_resource(plural):
return plural in gpolicy.RESOURCE_ATTRIBUTE_MAP
# Update Method
if item.startswith('update_'):
resource = item[len('update_'):]
plural = cm.get_resource_plural(resource)
if _is_gbp_resource(plural):
def update_wrapper(id, **kwargs):
return self._update_gbp_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_gbp_resource(plural):
def show_wrapper(id, **kwargs):
return self._show_gbp_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_gbp_resource(plural):
def create_wrapper(**kwargs):
return self._create_gbp_resource(resource, **kwargs)
return create_wrapper
# Delete Method
if item.startswith('delete_'):
resource = item[len('delete_'):]
plural = cm.get_resource_plural(resource)
if _is_gbp_resource(plural):
def delete_wrapper(id, **kwargs):
return self._delete_gbp_resource(id, plural, **kwargs)
return delete_wrapper
raise AttributeError
class ApiManagerMixin(object):
def _test_list_resources(self, resource, items,
neutron_context=None,
@ -96,8 +51,8 @@ class GroupPolicyDBTestBase(object):
self.assertEqual(sorted([i['id'] for i in res[resource_plural]]),
sorted([i[resource]['id'] for i in items]))
def _create_gbp_resource(self, type, expected_res_status=None,
is_admin_context=False, **kwargs):
def _create_resource(self, type, expected_res_status=None,
is_admin_context=False, **kwargs):
plural = cm.get_resource_plural(type)
defaults = getattr(cm,
'get_create_%s_default_attrs' % type)()
@ -119,7 +74,7 @@ class GroupPolicyDBTestBase(object):
return self.deserialize(self.fmt, res)
def _update_gbp_resource(
def _update_resource(
self, id, type, expected_res_status=None, is_admin_context=False,
**kwargs):
plural = cm.get_resource_plural(type)
@ -138,8 +93,8 @@ class GroupPolicyDBTestBase(object):
raise webob.exc.HTTPClientError(code=res.status_int)
return self.deserialize(self.fmt, res)
def _show_gbp_resource(self, id, plural, expected_res_status=None,
is_admin_context=False, tenant_id=None):
def _show_resource(self, id, plural, expected_res_status=None,
is_admin_context=False, tenant_id=None):
req = self.new_show_request(plural, id, fmt=self.fmt)
req.environ['neutron.context'] = context.Context(
'', tenant_id or self._tenant_id, is_admin_context)
@ -151,8 +106,8 @@ class GroupPolicyDBTestBase(object):
raise webob.exc.HTTPClientError(code=res.status_int)
return self.deserialize(self.fmt, res)
def _delete_gbp_resource(self, id, plural, is_admin_context=False,
expected_res_status=None, tenant_id=None):
def _delete_resource(self, id, plural, is_admin_context=False,
expected_res_status=None, tenant_id=None):
req = self.new_delete_request(plural, id)
req.environ['neutron.context'] = context.Context(
'', tenant_id or self._tenant_id, is_admin_context)
@ -161,6 +116,56 @@ class GroupPolicyDBTestBase(object):
self.assertEqual(res.status_int, expected_res_status)
elif res.status_int >= webob.exc.HTTPClientError.code:
raise webob.exc.HTTPClientError(code=res.status_int)
if res.status_int != 204:
return self.deserialize(self.fmt, res)
class GroupPolicyDBTestBase(ApiManagerMixin):
resource_prefix_map = 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_gbp_resource(plural):
return plural in gpolicy.RESOURCE_ATTRIBUTE_MAP
# Update Method
if item.startswith('update_'):
resource = item[len('update_'):]
plural = cm.get_resource_plural(resource)
if _is_gbp_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_gbp_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_gbp_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_gbp_resource(plural):
def delete_wrapper(id, **kwargs):
return self._delete_resource(id, plural, **kwargs)
return delete_wrapper
raise AttributeError
class GroupPolicyDBTestPlugin(gpdb.GroupPolicyDbPlugin):
@ -1115,7 +1120,7 @@ class TestGroupResources(GroupPolicyDbTestCase):
def _test_create_and_show(self, type, attrs, expected=None):
plural = cm.get_resource_plural(type)
res = self._create_gbp_resource(type, None, False, **attrs)
res = self._create_resource(type, None, False, **attrs)
expected = expected or attrs
for k, v in expected.iteritems():
self.assertEqual(v, res[type][k])

View File

@ -24,20 +24,71 @@ from neutron.tests.unit.db import test_db_base_plugin_v2
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.services.servicechain.common import constants as sccon
from gbpservice.neutron.tests.unit import common as cm
from gbpservice.neutron.tests.unit.db.grouppolicy import test_group_policy_db
JSON_FORMAT = 'json'
class ServiceChainDBTestBase(object):
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()
)
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')
@ -58,188 +109,6 @@ class ServiceChainDBTestBase(object):
self.assertEqual(sorted([i['id'] for i in res[resource_plural]]),
sorted([i[resource]['id'] for i in items]))
def _get_test_service_profile_attrs(
self, name='sp1', description='test sp',
service_type=constants.LOADBALANCER, vendor='',
insertion_mode=sccon.INSERTION_MODE_L3, service_flavor=''):
attrs = {'name': name, 'description': description,
'service_type': service_type,
'vendor': vendor, 'insertion_mode': insertion_mode,
'service_flavor': service_flavor,
'tenant_id': self._tenant_id}
return attrs
def _get_test_servicechain_node_attrs(self, name='scn1',
description='test scn',
service_profile_id=None,
config="{}", shared=False):
attrs = {'name': name, 'description': description,
'service_profile_id': service_profile_id,
'config': config,
'tenant_id': self._tenant_id,
'shared': shared}
return attrs
def _get_test_servicechain_spec_attrs(self, name='scs1',
description='test scs',
nodes=None, shared=False):
node_ids = []
if nodes:
node_ids = [node_id for node_id in nodes]
attrs = {'name': name, 'description': description,
'tenant_id': self._tenant_id,
'nodes': node_ids, 'shared': shared}
return attrs
def _get_test_servicechain_instance_attrs(self, name='sci1',
description='test sci',
config_param_values="{}",
servicechain_specs=[],
provider_ptg_id=None,
consumer_ptg_id=None,
classifier_id=None):
attrs = {'name': name, 'description': description,
'tenant_id': self._tenant_id,
'config_param_values': config_param_values,
'servicechain_specs': servicechain_specs,
'provider_ptg_id': provider_ptg_id,
'consumer_ptg_id': consumer_ptg_id,
'classifier_id': classifier_id}
return attrs
def create_service_profile(self, service_type=constants.FIREWALL,
insertion_mode=sccon.INSERTION_MODE_L3,
vendor='', service_flavor='',
expected_res_status=None, **kwargs):
defaults = {'name': 'sp1', 'description': 'test sp'}
defaults.update(kwargs)
data = {'service_profile': {'service_type': service_type,
'service_flavor': service_flavor,
'tenant_id': self._tenant_id,
'insertion_mode': insertion_mode,
'vendor': vendor}}
data['service_profile'].update(defaults)
scn_req = self.new_create_request('service_profiles', data, self.fmt)
scn_res = scn_req.get_response(self.ext_api)
if expected_res_status:
self.assertEqual(scn_res.status_int, expected_res_status)
elif scn_res.status_int >= webob.exc.HTTPClientError.code:
raise webob.exc.HTTPClientError(code=scn_res.status_int)
scn = self.deserialize(self.fmt, scn_res)
return scn
def create_servicechain_node(self, service_profile_id=None,
config="{}", expected_res_status=None,
**kwargs):
defaults = {'name': 'scn1', 'description': 'test scn', 'shared': False}
defaults.update(kwargs)
data = {'servicechain_node': {'service_profile_id': service_profile_id,
'tenant_id': self._tenant_id,
'config': config}}
data['servicechain_node'].update(defaults)
scn_req = self.new_create_request('servicechain_nodes', data, self.fmt)
scn_res = scn_req.get_response(self.ext_api)
if expected_res_status:
self.assertEqual(scn_res.status_int, expected_res_status)
elif scn_res.status_int >= webob.exc.HTTPClientError.code:
raise webob.exc.HTTPClientError(code=scn_res.status_int)
scn = self.deserialize(self.fmt, scn_res)
return scn
def create_servicechain_spec(self, nodes=None, expected_res_status=None,
**kwargs):
defaults = {'name': 'scs1', 'description': 'test scs', 'shared': False}
defaults.update(kwargs)
data = {'servicechain_spec': {'tenant_id': self._tenant_id,
'nodes': nodes}}
data['servicechain_spec'].update(defaults)
scs_req = self.new_create_request('servicechain_specs', data, self.fmt)
scs_res = scs_req.get_response(self.ext_api)
if expected_res_status:
self.assertEqual(expected_res_status, scs_res.status_int)
elif scs_res.status_int >= webob.exc.HTTPClientError.code:
raise webob.exc.HTTPClientError(code=scs_res.status_int)
scs = self.deserialize(self.fmt, scs_res)
return scs
def test_create_servicechain_specs_same_node(self):
template1 = '{"key1":"value1"}'
sp = self.create_service_profile()['service_profile']
scn = self.create_servicechain_node(
config=template1, service_profile_id=sp['id'])
scn_id = scn['servicechain_node']['id']
spec1 = {"servicechain_spec": {'name': 'scs1',
'tenant_id': self._tenant_id,
'nodes': [scn_id]}}
spec_req = self.new_create_request('servicechain_specs',
spec1,
self.fmt)
spec_res = spec_req.get_response(self.ext_api)
self.assertEqual(webob.exc.HTTPCreated.code, spec_res.status_int)
res = self.deserialize(self.fmt, spec_res)
self.assertIn('servicechain_spec', res)
self.assertEqual([scn_id], res['servicechain_spec']['nodes'])
spec2 = {"servicechain_spec": {'name': 'scs2',
'tenant_id': self._tenant_id,
'nodes': [scn_id]}}
spec_req = self.new_create_request('servicechain_specs',
spec2,
self.fmt)
spec_res = spec_req.get_response(self.ext_api)
self.assertEqual(webob.exc.HTTPCreated.code, spec_res.status_int)
res = self.deserialize(self.fmt, spec_res)
self.assertIn('servicechain_spec', res)
self.assertEqual([scn_id], res['servicechain_spec']['nodes'])
def create_servicechain_instance(self, servicechain_specs=[],
config_param_values=
'{"key": "value"}',
provider_ptg_id=None,
consumer_ptg_id=None,
classifier_id=None,
expected_res_status=None, **kwargs):
defaults = {'name': 'sci1', 'description': 'test sci'}
defaults.update(kwargs)
data = {'servicechain_instance':
{'config_param_values': config_param_values,
'servicechain_specs': servicechain_specs,
'tenant_id': self._tenant_id,
'provider_ptg_id': provider_ptg_id,
'consumer_ptg_id': consumer_ptg_id,
'classifier_id': classifier_id}}
data['servicechain_instance'].update(defaults)
sci_req = self.new_create_request('servicechain_instances',
data, self.fmt)
sci_res = sci_req.get_response(self.ext_api)
if expected_res_status:
self.assertEqual(expected_res_status, sci_res.status_int)
elif sci_res.status_int >= webob.exc.HTTPClientError.code:
raise webob.exc.HTTPClientError(code=sci_res.status_int)
sci = self.deserialize(self.fmt, sci_res)
return sci
def _create_profiled_servicechain_node(
self, service_type=constants.LOADBALANCER, shared_profile=False,
profile_tenant_id=None, **kwargs):
@ -267,11 +136,11 @@ class ServiceChainDbTestCase(ServiceChainDBTestBase,
test_db_base_plugin_v2.NeutronDbPluginV2TestCase):
def setUp(self, core_plugin=None, sc_plugin=None, service_plugins=None,
ext_mgr=None):
ext_mgr=None, gp_plugin=None):
if not sc_plugin:
sc_plugin = DB_GP_PLUGIN_KLASS
if not service_plugins:
service_plugins = {'gp_plugin_name': GP_PLUGIN_KLASS,
service_plugins = {'gp_plugin_name': gp_plugin or GP_PLUGIN_KLASS,
'sc_plugin_name': sc_plugin}
super(ServiceChainDbTestCase, self).setUp(
@ -285,6 +154,36 @@ class ServiceChainDbTestCase(ServiceChainDBTestBase,
engine = db_api.get_engine()
model_base.BASEV2.metadata.create_all(engine)
def test_create_servicechain_specs_same_node(self):
template1 = '{"key1":"value1"}'
sp = self.create_service_profile(
service_type=constants.FIREWALL)['service_profile']
scn = self.create_servicechain_node(
config=template1, service_profile_id=sp['id'])
scn_id = scn['servicechain_node']['id']
spec1 = {"servicechain_spec": {'name': 'scs1',
'tenant_id': self._tenant_id,
'nodes': [scn_id]}}
spec_req = self.new_create_request('servicechain_specs',
spec1,
self.fmt)
spec_res = spec_req.get_response(self.ext_api)
self.assertEqual(webob.exc.HTTPCreated.code, spec_res.status_int)
res = self.deserialize(self.fmt, spec_res)
self.assertIn('servicechain_spec', res)
self.assertEqual([scn_id], res['servicechain_spec']['nodes'])
spec2 = {"servicechain_spec": {'name': 'scs2',
'tenant_id': self._tenant_id,
'nodes': [scn_id]}}
spec_req = self.new_create_request('servicechain_specs',
spec2,
self.fmt)
spec_res = spec_req.get_response(self.ext_api)
self.assertEqual(webob.exc.HTTPCreated.code, spec_res.status_int)
res = self.deserialize(self.fmt, spec_res)
self.assertIn('servicechain_spec', res)
self.assertEqual([scn_id], res['servicechain_spec']['nodes'])
class TestServiceChainResources(ServiceChainDbTestCase):
@ -299,8 +198,8 @@ class TestServiceChainResources(ServiceChainDbTestCase):
self.assertEqual(v, res[resource][k])
def test_create_and_show_servicechain_node(self):
profile = self.create_service_profile()
attrs = self._get_test_servicechain_node_attrs(
profile = self.create_service_profile(service_type=constants.FIREWALL)
attrs = cm.get_create_servicechain_node_default_attrs(
service_profile_id=profile['service_profile']['id'],
config="config1")
@ -329,8 +228,8 @@ class TestServiceChainResources(ServiceChainDbTestCase):
def test_update_servicechain_node(self):
name = 'new_servicechain_node'
description = 'new desc'
profile = self.create_service_profile()
attrs = self._get_test_servicechain_node_attrs(
profile = self.create_service_profile(service_type=constants.FIREWALL)
attrs = cm.get_create_servicechain_node_default_attrs(
name=name, description=description,
service_profile_id=profile['service_profile']['id'])
@ -380,7 +279,8 @@ class TestServiceChainResources(ServiceChainDbTestCase):
scn = self._create_profiled_servicechain_node()
scn_id = scn['servicechain_node']['id']
attrs = self._get_test_servicechain_spec_attrs(name, nodes=[scn_id])
attrs = cm.get_create_servicechain_spec_default_attrs(
name=name, nodes=[scn_id])
scs = self.create_servicechain_spec(name=name, nodes=[scn_id])
@ -397,10 +297,10 @@ class TestServiceChainResources(ServiceChainDbTestCase):
scn1_id = scn1['servicechain_node']['id']
scn2 = self._create_profiled_servicechain_node()
scn2_id = scn2['servicechain_node']['id']
attrs = self._get_test_servicechain_spec_attrs(
name, nodes=[scn1_id, scn2_id])
attrs = cm.get_create_servicechain_spec_default_attrs(
name=name, nodes=[scn1_id, scn2_id])
scs = self.create_servicechain_spec(
name=name, nodes=[scn1_id, scn2_id])
name=name, nodes=[scn1_id, scn2_id])
for k, v in attrs.iteritems():
self.assertEqual(v, scs['servicechain_spec'][k])
@ -444,9 +344,8 @@ class TestServiceChainResources(ServiceChainDbTestCase):
description = 'new desc'
scn_id = self._create_profiled_servicechain_node()[
'servicechain_node']['id']
attrs = self._get_test_servicechain_spec_attrs(name=name,
description=description,
nodes=[scn_id])
attrs = cm.get_create_servicechain_spec_default_attrs(
name=name, description=description, nodes=[scn_id])
scs = self.create_servicechain_spec()
data = {'servicechain_spec': {'name': name, 'description': description,
'nodes': [scn_id]}}
@ -517,7 +416,7 @@ class TestServiceChainResources(ServiceChainDbTestCase):
policy_target_group_id = uuidutils.generate_uuid()
classifier_id = uuidutils.generate_uuid()
config_param_values = "{}"
attrs = self._get_test_servicechain_instance_attrs(
attrs = cm.get_create_servicechain_instance_default_attrs(
servicechain_specs=[scs_id],
provider_ptg_id=policy_target_group_id,
consumer_ptg_id=policy_target_group_id,
@ -588,7 +487,7 @@ class TestServiceChainResources(ServiceChainDbTestCase):
provider_ptg_id = uuidutils.generate_uuid()
consumer_ptg_id = uuidutils.generate_uuid()
classifier_id = uuidutils.generate_uuid()
attrs = self._get_test_servicechain_instance_attrs(
attrs = cm.get_create_servicechain_instance_default_attrs(
name=name, description=description, servicechain_specs=[scs_id],
provider_ptg_id=provider_ptg_id, consumer_ptg_id=consumer_ptg_id,
classifier_id=classifier_id,
@ -627,7 +526,7 @@ class TestServiceChainResources(ServiceChainDbTestCase):
ctx, sci_id)
def test_create_and_show_service_profile(self):
attrs = self._get_test_service_profile_attrs(
attrs = cm.get_create_service_profile_default_attrs(
service_type=constants.FIREWALL, vendor="vendor1")
scn = self.create_service_profile(
@ -640,20 +539,23 @@ class TestServiceChainResources(ServiceChainDbTestCase):
scn['service_profile']['id'], attrs)
def test_list_service_profile(self):
scns = [self.create_service_profile(name='sp1', description='sp'),
self.create_service_profile(name='sp2', description='sp'),
self.create_service_profile(name='sp3', description='sp')]
scns = [self.create_service_profile(name='sp1', description='sp',
service_type='LOADBALANCER'),
self.create_service_profile(name='sp2', description='sp',
service_type='LOADBALANCER'),
self.create_service_profile(name='sp3', description='sp',
service_type='LOADBALANCER')]
self._test_list_resources('service_profile', scns,
query_params='description=sp')
def test_update_service_profile(self):
name = 'new_service_profile'
description = 'new desc'
attrs = self._get_test_service_profile_attrs(
attrs = cm.get_create_service_profile_default_attrs(
name=name, description=description,
service_type=constants.FIREWALL)
scn = self.create_service_profile()
scn = self.create_service_profile(service_type=constants.FIREWALL)
data = {'service_profile': {'name': name,
'description': description}}
@ -670,7 +572,7 @@ class TestServiceChainResources(ServiceChainDbTestCase):
def test_delete_service_profile(self):
ctx = context.get_admin_context()
sp = self.create_service_profile()
sp = self.create_service_profile(service_type='LOADBALANCER')
sp_id = sp['service_profile']['id']
scn = self.create_servicechain_node(service_profile_id=sp_id)

View File

@ -0,0 +1,131 @@
# 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 neutron import context as n_context
from neutron.db import api as db_api
from neutron.db import model_base
from oslo_config import cfg
from gbpservice.neutron.services.servicechain.plugins.ncp import (
context as ncp_context)
import gbpservice.neutron.services.servicechain.plugins.ncp.config # noqa
from gbpservice.neutron.services.servicechain.plugins.ncp.node_drivers import (
dummy_driver as dummy_driver)
from gbpservice.neutron.tests.unit.services.servicechain import (
test_servicechain_plugin as test_base)
SC_PLUGIN_KLASS = (
"gbpservice.neutron.services.servicechain.plugins.ncp.plugin."
"NodeCompositionPlugin")
CORE_PLUGIN = ('gbpservice.neutron.tests.unit.services.grouppolicy.'
'test_resource_mapping.NoL3NatSGTestPlugin')
GP_PLUGIN_KLASS = (
"gbpservice.neutron.services.grouppolicy.plugin.GroupPolicyPlugin"
)
class NodeCompositionPluginTestCase(
test_base.TestGroupPolicyPluginGroupResources):
def setUp(self, core_plugin=None, gp_plugin=None, node_drivers=None):
if node_drivers:
cfg.CONF.set_override('node_drivers', node_drivers,
group='node_composition_chain')
super(NodeCompositionPluginTestCase, self).setUp(
core_plugin=core_plugin or CORE_PLUGIN,
gp_plugin=gp_plugin or GP_PLUGIN_KLASS,
sc_plugin=SC_PLUGIN_KLASS)
engine = db_api.get_engine()
model_base.BASEV2.metadata.create_all(engine)
def test_node_shared(self):
pass
def test_profile_shared(self):
pass
def test_spec_shared(self):
pass
def test_context_attributes(self):
# Verify Context attributes for simple config
plugin_context = n_context.get_admin_context()
profile = self.create_service_profile(
service_type="TYPE")['service_profile']
node = self.create_servicechain_node(
service_profile_id=profile['id'], config='{}')['servicechain_node']
spec = self.create_servicechain_spec(
nodes=[node['id']])['servicechain_spec']
provider = self.create_policy_target_group()['policy_target_group']
consumer = self.create_policy_target_group()['policy_target_group']
management = self.create_policy_target_group()['policy_target_group']
instance = self.create_servicechain_instance(
provider_ptg_id=provider['id'], consumer_ptg_id=consumer['id'],
servicechain_specs=[spec['id']])['servicechain_instance']
# Verify created without errors
ctx = ncp_context.get_node_driver_context(
self.plugin, plugin_context, instance, node,
management_group=management)
self.assertIsNotNone(ctx.gbp_plugin)
self.assertIsNotNone(ctx.sc_plugin)
self.assertIsNotNone(ctx.core_plugin)
self.assertIsNotNone(ctx.plugin_context)
self.assertIsNotNone(ctx.plugin_session)
self.assertIsNotNone(ctx.session)
self.assertIsNotNone(ctx.admin_context)
self.assertIsNotNone(ctx.admin_session)
self.assertEqual(ctx.instance, instance)
self.assertEqual(ctx.provider, provider)
self.assertEqual(ctx.consumer, consumer)
self.assertEqual(ctx.management, management)
self.assertEqual(ctx.management, management)
self.assertEqual(ctx.relevant_specs, [spec])
del ctx.current_profile['nodes']
self.assertEqual(ctx.current_profile, profile)
self.assertIsNone(ctx.original_node)
self.assertIsNone(ctx.service_targets)
def test_context_relevant_specs(self):
plugin_context = n_context.get_admin_context()
node_used = self._create_profiled_servicechain_node(
service_type="TYPE", config='{}')['servicechain_node']
spec_used = self.create_servicechain_spec(
nodes=[node_used['id']])['servicechain_spec']
node_unused = self._create_profiled_servicechain_node(
service_type="TYPE", config='{}')['servicechain_node']
spec_unused = self.create_servicechain_spec(
nodes=[node_unused['id']])['servicechain_spec']
provider = self.create_policy_target_group()['policy_target_group']
instance = self.create_servicechain_instance(
provider_ptg_id=provider['id'],
servicechain_specs=[spec_used['id'],
spec_unused['id']])['servicechain_instance']
self.assertEqual(len(instance['servicechain_specs']), 2)
ctx = ncp_context.get_node_driver_context(
self.plugin, plugin_context, instance, node_used)
self.assertEqual(ctx.relevant_specs, [spec_used])
class TestNcpNodeDriverManager(NodeCompositionPluginTestCase):
def test_manager_initialized(self):
mgr = self.plugin.driver_manager
self.assertIsInstance(mgr.ordered_drivers[0].obj,
dummy_driver.NoopNodeDriver)
for driver in mgr.ordered_drivers:
self.assertTrue(driver.obj.initialized)

View File

@ -29,16 +29,17 @@ SC_PLUGIN_KLASS = (
class ServiceChainPluginTestCase(test_servicechain_db.ServiceChainDbTestCase):
def setUp(self, core_plugin=None, sc_plugin=None):
def setUp(self, core_plugin=None, sc_plugin=None, gp_plugin=None):
if not sc_plugin:
sc_plugin = SC_PLUGIN_KLASS
super(ServiceChainPluginTestCase, self).setUp(core_plugin=core_plugin,
sc_plugin=sc_plugin)
sc_plugin=sc_plugin,
gp_plugin=gp_plugin)
class TestGroupPolicyPluginGroupResources(
ServiceChainPluginTestCase,
test_servicechain_db.TestServiceChainResources):
ServiceChainPluginTestCase,
test_servicechain_db.TestServiceChainResources):
def test_spec_shared(self):
# Shared spec can only point shared nodes
@ -55,7 +56,7 @@ class TestGroupPolicyPluginGroupResources(
'LOADBALANCER', shared=False, profile_tenant_id='nonadmin',
tenant_id='nonadmin')['servicechain_node']
self.create_servicechain_spec(nodes=[node['id']], shared=True,
expected_res_status=400)
expected_res_status=404)
self.create_servicechain_spec(nodes=[node['id']], shared=True,
tenant_id='nonadmin',
expected_res_status=400)
@ -80,7 +81,7 @@ class TestGroupPolicyPluginGroupResources(
tenant_id='admin')['service_profile']
self.create_servicechain_node(
service_profile_id=prof['id'], shared=True,
expected_res_status=400)
expected_res_status=404)
self.create_servicechain_node(
service_profile_id=prof['id'], shared=True,
tenant_id='admin', expected_res_status=400)

View File

@ -20,6 +20,7 @@ from neutron.tests.unit.extensions import base as test_extensions_base
from webob import exc
from gbpservice.neutron.extensions import servicechain
from gbpservice.neutron.tests.unit import common as cm
_uuid = uuidutils.generate_uuid
@ -63,30 +64,6 @@ class ServiceChainExtensionTestCase(test_extensions_base.ExtensionTestCase):
self.assertIn('servicechain_node', res)
self.assertEqual(expected_value, res['servicechain_node'])
def _get_create_servicechain_node_default_attrs(self):
return {
'name': '',
'description': '',
'config': '',
'shared': False
}
def _get_create_servicechain_node_attrs(self):
return {
'name': 'servicechain1',
'service_profile_id': _uuid(),
'tenant_id': _uuid(),
'description': 'test servicechain node',
'config': 'test_config',
'shared': True,
'service_type': None,
}
def _get_update_servicechain_node_attrs(self):
return {
'name': 'new_name',
}
def test_create_servicechain_node_with_defaults(self):
servicechain_node_id = _uuid()
data = {
@ -97,7 +74,7 @@ class ServiceChainExtensionTestCase(test_extensions_base.ExtensionTestCase):
'service_type': None,
}
}
default_attrs = self._get_create_servicechain_node_default_attrs()
default_attrs = cm.get_create_servicechain_node_default_attrs()
default_data = copy.copy(data)
default_data['servicechain_node'].update(default_attrs)
expected_value = dict(default_data['servicechain_node'])
@ -108,7 +85,7 @@ class ServiceChainExtensionTestCase(test_extensions_base.ExtensionTestCase):
def test_create_servicechain_node(self):
servicechain_node_id = _uuid()
data = {
'servicechain_node': self._get_create_servicechain_node_attrs()
'servicechain_node': cm.get_create_servicechain_node_attrs()
}
expected_value = dict(data['servicechain_node'])
expected_value['id'] = servicechain_node_id
@ -149,7 +126,7 @@ class ServiceChainExtensionTestCase(test_extensions_base.ExtensionTestCase):
def test_update_servicechain_node(self):
servicechain_node_id = _uuid()
update_data = {
'servicechain_node': self._get_update_servicechain_node_attrs()
'servicechain_node': cm.get_update_servicechain_node_attrs()
}
expected_value = {'tenant_id': _uuid(), 'id': servicechain_node_id}
self.instance.update_servicechain_node.return_value = expected_value
@ -186,29 +163,6 @@ class ServiceChainExtensionTestCase(test_extensions_base.ExtensionTestCase):
self.assertIn('servicechain_spec', res)
self.assertEqual(expected_value, res['servicechain_spec'])
def _get_create_servicechain_spec_default_attrs(self):
return {
'name': '',
'description': '',
'nodes': [],
'shared': False,
}
def _get_create_servicechain_spec_attrs(self):
return {
'name': 'servicechainspec1',
'nodes': [_uuid(), _uuid()],
'tenant_id': _uuid(),
'description': 'test servicechain spec',
'shared': True
}
def _get_update_servicechain_spec_attrs(self):
return {
'name': 'new_name',
'nodes': [_uuid()]
}
def test_create_servicechain_spec_with_defaults(self):
servicechain_spec_id = _uuid()
data = {
@ -216,7 +170,7 @@ class ServiceChainExtensionTestCase(test_extensions_base.ExtensionTestCase):
'nodes': [_uuid(), _uuid()], 'tenant_id': _uuid()
}
}
default_attrs = self._get_create_servicechain_spec_default_attrs()
default_attrs = cm.get_create_servicechain_spec_default_attrs()
default_data = copy.copy(data)
default_data['servicechain_spec'].update(default_attrs)
expected_value = dict(default_data['servicechain_spec'])
@ -227,7 +181,7 @@ class ServiceChainExtensionTestCase(test_extensions_base.ExtensionTestCase):
def test_create_servicechain_spec(self):
servicechain_spec_id = _uuid()
data = {
'servicechain_spec': self._get_create_servicechain_spec_attrs()
'servicechain_spec': cm.get_create_servicechain_spec_attrs()
}
expected_value = dict(data['servicechain_spec'])
expected_value['id'] = servicechain_spec_id
@ -267,7 +221,7 @@ class ServiceChainExtensionTestCase(test_extensions_base.ExtensionTestCase):
def test_update_servicechain_spec(self):
servicechain_spec_id = _uuid()
update_data = {
'servicechain_spec': self._get_update_servicechain_spec_attrs()
'servicechain_spec': cm.get_update_servicechain_spec_attrs()
}
expected_value = {'tenant_id': _uuid(), 'id': servicechain_spec_id}
self.instance.update_servicechain_spec.return_value = expected_value
@ -305,27 +259,6 @@ class ServiceChainExtensionTestCase(test_extensions_base.ExtensionTestCase):
self.assertIn('servicechain_instance', res)
self.assertEqual(expected_value, res['servicechain_instance'])
def _get_create_servicechain_instance_default_attrs(self):
return {'name': '', 'description': '', 'config_param_values': "{}"}
def _get_create_servicechain_instance_attrs(self):
return {
'name': 'servicechaininstance1',
'servicechain_specs': [_uuid()],
'tenant_id': _uuid(),
'provider_ptg_id': _uuid(),
'consumer_ptg_id': _uuid(),
'classifier_id': _uuid(),
'config_param_values': "{}",
'description': 'test servicechain instance'
}
def _get_update_servicechain_instance_attrs(self):
return {
'name': 'new_name',
'servicechain_specs': [_uuid()]
}
def test_create_servicechain_instance_with_defaults(self):
servicechain_instance_id = _uuid()
data = {
@ -337,7 +270,7 @@ class ServiceChainExtensionTestCase(test_extensions_base.ExtensionTestCase):
'classifier_id': _uuid(),
}
}
default_attrs = self._get_create_servicechain_instance_default_attrs()
default_attrs = cm.get_create_servicechain_instance_default_attrs()
default_data = copy.copy(data)
default_data['servicechain_instance'].update(default_attrs)
expected_value = dict(default_data['servicechain_instance'])
@ -349,7 +282,7 @@ class ServiceChainExtensionTestCase(test_extensions_base.ExtensionTestCase):
def test_create_servicechain_instance(self):
servicechain_instance_id = _uuid()
data = {'servicechain_instance':
self._get_create_servicechain_instance_attrs()}
cm.get_create_servicechain_instance_attrs()}
expected_value = dict(data['servicechain_instance'])
expected_value['id'] = servicechain_instance_id
@ -389,7 +322,7 @@ class ServiceChainExtensionTestCase(test_extensions_base.ExtensionTestCase):
def test_update_servicechain_instance(self):
servicechain_instance_id = _uuid()
update_data = {'servicechain_instance':
self._get_update_servicechain_instance_attrs()}
cm.get_update_servicechain_instance_attrs()}
expected_value = {'tenant_id': _uuid(), 'id': servicechain_instance_id}
self.instance.update_servicechain_instance.return_value = (
expected_value)

View File

@ -38,6 +38,7 @@ neutron.service_plugins =
group_policy = gbpservice.neutron.services.grouppolicy.plugin:GroupPolicyPlugin
servicechain = gbpservice.neutron.services.servicechain.plugins.msc.plugin:ServiceChainPlugin
msc = gbpservice.neutron.services.servicechain.plugins.msc.plugin:ServiceChainPlugin
ncp = gbpservice.neutron.services.servicechain.plugins.ncp.plugin:NodeCompositionPlugin
gbpservice.neutron.group_policy.extension_drivers =
test = gbpservice.neutron.tests.unit.services.grouppolicy.test_extension_driver_api:TestExtensionDriver
gbpservice.neutron.group_policy.policy_drivers =
@ -56,6 +57,8 @@ gbpservice.neutron.servicechain.servicechain_drivers =
dummy = gbpservice.neutron.services.servicechain.plugins.msc.drivers.dummy_driver:NoopDriver
simplechain_driver = gbpservice.neutron.services.servicechain.plugins.msc.drivers.simplechain_driver:SimpleChainDriver
oneconvergence_servicechain_driver = gbpservice.neutron.services.servicechain.plugins.msc.drivers.oneconvergence_servicechain_driver:OneconvergenceServiceChainDriver
gbpservice.neutron.servicechain.ncp_drivers =
node_dummy = gbpservice.neutron.services.servicechain.plugins.ncp.node_drivers.dummy_driver:NoopNodeDriver
[build_sphinx]
source-dir = doc/source