Remove monolithic service chain plugin and drivers

This plugin is subsumed by the Node Composition plugin
starting in the liberty cycle.

Switching to the NCP as default invalidated some UTs (since
NCP does not support more than one service_chain_spec
per service_chain_instance). These tests are being skipped.

Change-Id: I03383145eaa72681695e12649f731ba1a6b8bad8
This commit is contained in:
Sumit Naiksatam 2017-06-02 14:31:20 -07:00
parent 07349a898f
commit ce69f5ed66
19 changed files with 68 additions and 1625 deletions

View File

@ -1,15 +0,0 @@
[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

@ -1,6 +0,0 @@
[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

@ -1,15 +0,0 @@
[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

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

View File

@ -1,106 +0,0 @@
# 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.
class ServiceChainContext(object):
"""ServiceChain context base class."""
def __init__(self, plugin, plugin_context):
self._plugin = plugin
self._plugin_context = plugin_context
class ServiceChainNodeContext(ServiceChainContext):
def __init__(self, plugin, plugin_context, sc_node,
original_sc_node=None):
super(ServiceChainNodeContext, self).__init__(plugin, plugin_context)
self._sc_node = sc_node
self._profile = None
if self._sc_node['service_profile_id']:
self._profile = self._plugin.get_service_profile(
self._plugin_context, self._sc_node['service_profile_id'])
self._original_sc_node = original_sc_node
self._original_profile = None
if (self._original_sc_node and
self._original_sc_node['service_profile_id']):
self._original_profile = self._plugin.get_service_profile(
self._plugin_context,
self._original_sc_node['service_profile_id'])
@property
def current(self):
return self._sc_node
@property
def original(self):
return self._original_sc_node
@property
def current_profile(self):
return self._profile
@property
def original_profile(self):
return self._original_profile
class ServiceChainSpecContext(ServiceChainContext):
def __init__(self, plugin, plugin_context, sc_spec,
original_sc_spec=None):
super(ServiceChainSpecContext, self).__init__(plugin, plugin_context)
self._sc_spec = sc_spec
self._original_sc_spec = original_sc_spec
@property
def current(self):
return self._sc_spec
@property
def original(self):
return self._original_sc_spec
class ServiceChainInstanceContext(ServiceChainContext):
def __init__(self, plugin, plugin_context, sc_instance,
original_sc_instance=None):
super(ServiceChainInstanceContext, self).__init__(plugin,
plugin_context)
self._sc_instance = sc_instance
self._original_sc_instance = original_sc_instance
@property
def current(self):
return self._sc_instance
@property
def original(self):
return self._original_sc_instance
class ServiceProfileContext(ServiceChainContext):
def __init__(self, plugin, plugin_context, profile,
original_profile=None):
super(ServiceProfileContext, self).__init__(plugin, plugin_context)
self._profile = profile
self._original_profile = original_profile
@property
def current(self):
return self._profile
@property
def original(self):
return self._original_profile

View File

@ -1,184 +0,0 @@
# 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
import stevedore
from gbpservice._i18n import _LE
from gbpservice._i18n import _LI
from gbpservice.neutron.services.servicechain.common import (
exceptions as sc_exc)
LOG = log.getLogger(__name__)
cfg.CONF.import_opt(
'servicechain_drivers',
'gbpservice.neutron.services.servicechain.plugins.msc.config',
group='servicechain')
class DriverManager(stevedore.named.NamedExtensionManager):
"""Route servicechain APIs to servicechain drivers.
"""
def __init__(self):
# Registered servicechain drivers, keyed by name.
self.drivers = {}
# Ordered list of servicechain drivers, defining
# the order in which the drivers are called.
self.ordered_drivers = []
LOG.info(_LI("Configured servicechain driver names: %s"),
cfg.CONF.servicechain.servicechain_drivers)
super(DriverManager,
self).__init__(
'gbpservice.neutron.servicechain.servicechain_drivers',
cfg.CONF.servicechain.servicechain_drivers,
invoke_on_load=True, name_order=True)
LOG.info(_LI("Loaded servicechain driver names: %s"), self.names())
self._register_drivers()
def _register_drivers(self):
"""Register all servicechain drivers.
This method should only be called once in the DriverManager
constructor.
"""
for ext in self:
self.drivers[ext.name] = ext
self.ordered_drivers.append(ext)
LOG.info(_LI("Registered servicechain drivers: %s"),
[driver.name for driver in self.ordered_drivers])
def initialize(self):
# ServiceChain bulk operations requires each driver to support them
self.native_bulk_support = True
for driver in self.ordered_drivers:
LOG.info(_LI("Initializing servicechain driver '%s'"), driver.name)
driver.obj.initialize()
self.native_bulk_support &= getattr(driver.obj,
'native_bulk_support', True)
def _call_on_drivers(self, method_name, context):
"""Helper method for calling a method across all servicechain drivers.
:param method_name: name of the method to call
:param context: context parameter to pass to each method call
:param continue_on_failure: whether or not to continue to call
all servicechain drivers once one has raised an exception
:raises: neutron.services.servicechain.common.ServiceChainDriverError
if any servicechain driver call fails.
"""
error = False
for driver in self.ordered_drivers:
try:
getattr(driver.obj, method_name)(context)
except sc_exc.ServiceChainException:
# This is an exception for the user.
raise
except Exception:
# This is an internal failure.
LOG.exception(
_LE("ServiceChain driver '%(name)s' failed in %(method)s"),
{'name': driver.name, 'method': method_name}
)
error = True
if error:
raise sc_exc.ServiceChainDriverError(
method=method_name
)
def create_servicechain_node_precommit(self, context):
self._call_on_drivers("create_servicechain_node_precommit", context)
def create_servicechain_node_postcommit(self, context):
self._call_on_drivers("create_servicechain_node_postcommit", context)
def update_servicechain_node_precommit(self, context):
self._call_on_drivers("update_servicechain_node_precommit", context)
def update_servicechain_node_postcommit(self, context):
self._call_on_drivers("update_servicechain_node_postcommit", context)
def delete_servicechain_node_precommit(self, context):
self._call_on_drivers("delete_servicechain_node_precommit", context)
def delete_servicechain_node_postcommit(self, context):
self._call_on_drivers("delete_servicechain_node_postcommit", context)
def create_servicechain_spec_precommit(self, context):
self._call_on_drivers("create_servicechain_spec_precommit", context)
def create_servicechain_spec_postcommit(self, context):
self._call_on_drivers("create_servicechain_spec_postcommit", context)
def update_servicechain_spec_precommit(self, context):
self._call_on_drivers("update_servicechain_spec_precommit", context)
def update_servicechain_spec_postcommit(self, context):
self._call_on_drivers("update_servicechain_spec_postcommit", context)
def delete_servicechain_spec_precommit(self, context):
self._call_on_drivers("delete_servicechain_spec_precommit", context)
def delete_servicechain_spec_postcommit(self, context):
self._call_on_drivers("delete_servicechain_spec_postcommit", context)
def create_servicechain_instance_precommit(self, context):
self._call_on_drivers("create_servicechain_instance_precommit",
context)
def create_servicechain_instance_postcommit(self, context):
self._call_on_drivers("create_servicechain_instance_postcommit",
context)
def update_servicechain_instance_precommit(self, context):
self._call_on_drivers("update_servicechain_instance_precommit",
context)
def update_servicechain_instance_postcommit(self, context):
self._call_on_drivers("update_servicechain_instance_postcommit",
context)
def delete_servicechain_instance_precommit(self, context):
self._call_on_drivers("delete_servicechain_instance_precommit",
context)
def delete_servicechain_instance_postcommit(self, context):
self._call_on_drivers("delete_servicechain_instance_postcommit",
context)
def create_service_profile_precommit(self, context):
self._call_on_drivers("create_service_profile_precommit",
context)
def create_service_profile_postcommit(self, context):
self._call_on_drivers("create_service_profile_postcommit",
context)
def update_service_profile_precommit(self, context):
self._call_on_drivers("update_service_profile_precommit",
context)
def update_service_profile_postcommit(self, context):
self._call_on_drivers("update_service_profile_postcommit",
context)
def delete_service_profile_precommit(self, context):
self._call_on_drivers("delete_service_profile_precommit",
context)
def delete_service_profile_postcommit(self, context):
self._call_on_drivers("delete_service_profile_postcommit",
context)

View File

@ -1,116 +0,0 @@
# 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 helpers as log
class NoopDriver(object):
@log.log_method_call
def initialize(self):
pass
@log.log_method_call
def create_servicechain_node_precommit(self, context):
pass
@log.log_method_call
def create_servicechain_node_postcommit(self, context):
pass
@log.log_method_call
def update_servicechain_node_precommit(self, context):
pass
@log.log_method_call
def update_servicechain_node_postcommit(self, context):
pass
@log.log_method_call
def delete_servicechain_node_precommit(self, context):
pass
@log.log_method_call
def delete_servicechain_node_postcommit(self, context):
pass
@log.log_method_call
def create_servicechain_spec_precommit(self, context):
pass
@log.log_method_call
def create_servicechain_spec_postcommit(self, context):
pass
@log.log_method_call
def update_servicechain_spec_precommit(self, context):
pass
@log.log_method_call
def update_servicechain_spec_postcommit(self, context):
pass
@log.log_method_call
def delete_servicechain_spec_precommit(self, context):
pass
@log.log_method_call
def delete_servicechain_spec_postcommit(self, context):
pass
@log.log_method_call
def create_servicechain_instance_precommit(self, context):
pass
@log.log_method_call
def create_servicechain_instance_postcommit(self, context):
pass
@log.log_method_call
def update_servicechain_instance_precommit(self, context):
pass
@log.log_method_call
def update_servicechain_instance_postcommit(self, context):
pass
@log.log_method_call
def delete_servicechain_instance_precommit(self, context):
pass
@log.log_method_call
def delete_servicechain_instance_postcommit(self, context):
pass
@log.log_method_call
def create_service_profile_precommit(self, context):
pass
@log.log_method_call
def create_service_profile_postcommit(self, context):
pass
@log.log_method_call
def update_service_profile_precommit(self, context):
pass
@log.log_method_call
def update_service_profile_postcommit(self, context):
pass
@log.log_method_call
def delete_service_profile_precommit(self, context):
pass
@log.log_method_call
def delete_service_profile_postcommit(self, context):
pass

View File

@ -1,482 +0,0 @@
# 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 ast
import time
from heatclient import client as heat_client
from heatclient import exc as heat_exc
from keystoneclient.v2_0 import client as keyclient
from neutron import manager
from neutron.plugins.common import constants as pconst
from neutron_lib.db import model_base
from oslo_config import cfg
from oslo_log import helpers as log
from oslo_log import log as logging
from oslo_serialization import jsonutils
import sqlalchemy as sa
from gbpservice._i18n import _LE
from gbpservice._i18n import _LW
from gbpservice.neutron.services.servicechain.common import exceptions as exc
LOG = logging.getLogger(__name__)
cfg.CONF.import_group('keystone_authtoken', 'keystonemiddleware.auth_token')
service_chain_opts = [
cfg.IntOpt('stack_delete_retries',
default=5,
help=_("Number of attempts to retry for stack deletion")),
cfg.IntOpt('stack_delete_retry_wait',
default=3,
help=_("Wait time between two successive stack delete "
"retries")),
cfg.StrOpt('heat_uri',
default='http://localhost:8004/v1',
help=_("Heat server address to create services "
"specified in the service chain.")),
cfg.StrOpt('heat_ca_certificates_file', default=None,
help=_('CA file for heatclient to verify server certificates')),
cfg.BoolOpt('heat_api_insecure', default=False,
help=_("If True, ignore any SSL validation issues")),
]
cfg.CONF.register_opts(service_chain_opts, "simplechain")
# Service chain API supported Values
sc_supported_type = [pconst.LOADBALANCER, pconst.FIREWALL]
STACK_DELETE_RETRIES = cfg.CONF.simplechain.stack_delete_retries
STACK_DELETE_RETRY_WAIT = cfg.CONF.simplechain.stack_delete_retry_wait
class ServiceChainInstanceStack(model_base.BASEV2):
"""ServiceChainInstance stacks owned by the servicechain driver."""
__tablename__ = 'sc_instance_stacks'
instance_id = sa.Column(sa.String(36),
nullable=False, primary_key=True)
stack_id = sa.Column(sa.String(36),
nullable=False, primary_key=True)
class SimpleChainDriver(object):
@log.log_method_call
def initialize(self):
pass
@log.log_method_call
def create_servicechain_node_precommit(self, context):
if context.current['service_profile_id'] is None:
if context.current['service_type'] not in sc_supported_type:
raise exc.InvalidServiceTypeForReferenceDriver()
elif context.current['service_type']:
LOG.warning(_LW('Both service_profile_id and service_type are'
'specified, service_type will be ignored.'))
@log.log_method_call
def create_servicechain_node_postcommit(self, context):
pass
@log.log_method_call
def update_servicechain_node_precommit(self, context):
if (context.original['config'] != context.current['config']):
filters = {'servicechain_spec': context.original[
'servicechain_specs']}
sc_instances = context._plugin.get_servicechain_instances(
context._plugin_context, filters)
if sc_instances:
raise exc.NodeUpdateNotSupported()
@log.log_method_call
def update_servicechain_node_postcommit(self, context):
pass
@log.log_method_call
def delete_servicechain_node_precommit(self, context):
pass
@log.log_method_call
def delete_servicechain_node_postcommit(self, context):
pass
@log.log_method_call
def create_servicechain_spec_precommit(self, context):
pass
@log.log_method_call
def create_servicechain_spec_postcommit(self, context):
pass
@log.log_method_call
def update_servicechain_spec_precommit(self, context):
pass
@log.log_method_call
def update_servicechain_spec_postcommit(self, context):
if context.original['nodes'] != context.current['nodes']:
filters = {'servicechain_spec': [context.original['id']]}
sc_instances = context._plugin.get_servicechain_instances(
context._plugin_context, filters)
for sc_instance in sc_instances:
self._update_servicechain_instance(context,
sc_instance,
context._sc_spec)
@log.log_method_call
def delete_servicechain_spec_precommit(self, context):
pass
@log.log_method_call
def delete_servicechain_spec_postcommit(self, context):
pass
@log.log_method_call
def create_servicechain_instance_precommit(self, context):
pass
@log.log_method_call
def create_servicechain_instance_postcommit(self, context):
sc_instance = context.current
sc_spec_ids = sc_instance.get('servicechain_specs')
for sc_spec_id in sc_spec_ids:
sc_spec = context._plugin.get_servicechain_spec(
context._plugin_context, sc_spec_id)
sc_node_ids = sc_spec.get('nodes')
self._create_servicechain_instance_stacks(context, sc_node_ids,
sc_instance, sc_spec)
@log.log_method_call
def update_servicechain_instance_precommit(self, context):
pass
@log.log_method_call
def update_servicechain_instance_postcommit(self, context):
original_spec_ids = context.original.get('servicechain_specs')
new_spec_ids = context.current.get('servicechain_specs')
if set(original_spec_ids) != set(new_spec_ids):
for new_spec_id in new_spec_ids:
newspec = context._plugin.get_servicechain_spec(
context._plugin_context, new_spec_id)
self._update_servicechain_instance(context, context.current,
newspec)
@log.log_method_call
def delete_servicechain_instance_precommit(self, context):
pass
@log.log_method_call
def delete_servicechain_instance_postcommit(self, context):
self._delete_servicechain_instance_stacks(context._plugin_context,
context.current['id'])
@log.log_method_call
def create_service_profile_precommit(self, context):
if context.current['service_type'] not in sc_supported_type:
raise exc.InvalidServiceTypeForReferenceDriver()
@log.log_method_call
def create_service_profile_postcommit(self, context):
pass
@log.log_method_call
def update_service_profile_precommit(self, context):
pass
@log.log_method_call
def update_service_profile_postcommit(self, context):
pass
@log.log_method_call
def delete_service_profile_precommit(self, context):
pass
@log.log_method_call
def delete_service_profile_postcommit(self, context):
pass
def _get_ptg(self, context, ptg_id):
return self._get_resource(self._grouppolicy_plugin,
context._plugin_context,
'policy_target_group',
ptg_id)
def _get_pt(self, context, pt_id):
return self._get_resource(self._grouppolicy_plugin,
context._plugin_context,
'policy_target',
pt_id)
def _get_port(self, context, port_id):
return self._get_resource(self._core_plugin,
context._plugin_context,
'port',
port_id)
def _get_ptg_subnet(self, context, ptg_id):
ptg = self._get_ptg(context, ptg_id)
return ptg.get("subnets")[0]
def _get_member_ips(self, context, ptg_id):
ptg = self._get_ptg(context, ptg_id)
pt_ids = ptg.get("policy_targets")
member_addresses = []
for pt_id in pt_ids:
pt = self._get_pt(context, pt_id)
port_id = pt.get("port_id")
port = self._get_port(context, port_id)
ipAddress = port.get('fixed_ips')[0].get("ip_address")
member_addresses.append(ipAddress)
return member_addresses
def _fetch_template_and_params(self, context, sc_instance,
sc_spec, sc_node):
stack_template = sc_node.get('config')
# TODO(magesh):Raise an exception ??
if not stack_template:
LOG.error(_LE("Service Config is not defined for the service"
" chain Node"))
return
stack_template = jsonutils.loads(stack_template)
config_param_values = sc_instance.get('config_param_values', {})
stack_params = {}
# config_param_values has the parameters for all Nodes. Only apply
# the ones relevant for this Node
if config_param_values:
config_param_values = jsonutils.loads(config_param_values)
config_param_names = sc_spec.get('config_param_names', [])
if config_param_names:
config_param_names = ast.literal_eval(config_param_names)
# This service chain driver knows how to fill in two parameter values
# for the template at present.
# 1)Subnet -> Provider PTG subnet is used
# 2)PoolMemberIPs -> List of IP Addresses of all PTs in Provider PTG
# TODO(magesh):Process on the basis of ResourceType rather than Name
# eg: Type: OS::Neutron::PoolMember
# Variable number of pool members is not handled yet. We may have to
# dynamically modify the template json to achieve that
member_ips = []
provider_ptg_id = sc_instance.get("provider_ptg_id")
# If we have the key "PoolMemberIP*" in template input parameters,
# fetch the list of IPs of all PTs in the PTG
for key in config_param_names or []:
if "PoolMemberIP" in key:
member_ips = self._get_member_ips(context, provider_ptg_id)
break
member_count = 0
for key in config_param_names or []:
if "PoolMemberIP" in key:
value = (member_ips[member_count]
if len(member_ips) > member_count else '0')
member_count = member_count + 1
config_param_values[key] = value
elif key == "Subnet":
value = self._get_ptg_subnet(context, provider_ptg_id)
config_param_values[key] = value
node_params = (stack_template.get('Parameters')
or stack_template.get('parameters'))
if node_params:
for parameter in config_param_values.keys():
if parameter in node_params.keys():
stack_params[parameter] = config_param_values[parameter]
return (stack_template, stack_params)
def _create_servicechain_instance_stacks(self, context, sc_node_ids,
sc_instance, sc_spec):
heatclient = HeatClient(context._plugin_context)
for sc_node_id in sc_node_ids:
sc_node = context._plugin.get_servicechain_node(
context._plugin_context, sc_node_id)
stack_template, stack_params = self._fetch_template_and_params(
context, sc_instance, sc_spec, sc_node)
stack_name = ("stack_" + sc_instance['name'] + sc_node['name'] +
sc_instance['id'][:8])
stack = heatclient.create(
stack_name.replace(" ", ""), stack_template, stack_params)
self._insert_chain_stack_db(
context._plugin_context.session, sc_instance['id'],
stack['stack']['id'])
def _delete_servicechain_instance_stacks(self, context, instance_id):
stack_ids = self._get_chain_stacks(context.session, instance_id)
heatclient = HeatClient(context)
for stack in stack_ids:
heatclient.delete(stack.stack_id)
for stack in stack_ids:
self._wait_for_stack_delete(heatclient, stack.stack_id)
self._delete_chain_stacks_db(context.session, instance_id)
# Wait for the heat stack to be deleted for a maximum of 15 seconds
# we check the status every 3 seconds and call sleep again
# This is required because cleanup of subnet fails when the stack created
# some ports on the subnet and the resource delete is not completed by
# the time subnet delete is triggered by Resource Mapping driver
def _wait_for_stack_delete(self, heatclient, stack_id):
stack_delete_retries = STACK_DELETE_RETRIES
while True:
try:
stack = heatclient.get(stack_id)
if stack.stack_status == 'DELETE_COMPLETE':
return
elif stack.stack_status == 'DELETE_FAILED':
heatclient.delete(stack_id)
except Exception:
LOG.exception(_LE(
"Service Chain Instance cleanup may not have "
"happened because Heat API request failed "
"while waiting for the stack %(stack)s to be "
"deleted"), {'stack': stack_id})
return
else:
time.sleep(STACK_DELETE_RETRY_WAIT)
stack_delete_retries = stack_delete_retries - 1
if stack_delete_retries == 0:
LOG.warning(_LW(
"Resource cleanup for service chain instance"
" is not completed within %(wait)s seconds"
" as deletion of Stack %(stack)s is not"
" completed"),
{'wait': (STACK_DELETE_RETRIES *
STACK_DELETE_RETRY_WAIT),
'stack': stack_id})
return
else:
continue
def _get_instance_by_spec_id(self, context, spec_id):
filters = {'servicechain_spec': [spec_id]}
return context._plugin.get_servicechain_instances(
context._plugin_context, filters)
def _update_servicechain_instance(self, context, sc_instance, newspec):
self._delete_servicechain_instance_stacks(context._plugin_context,
sc_instance['id'])
sc_node_ids = newspec.get('nodes')
self._create_servicechain_instance_stacks(context,
sc_node_ids,
sc_instance,
newspec)
def _delete_chain_stacks_db(self, session, sc_instance_id):
with session.begin(subtransactions=True):
stacks = session.query(ServiceChainInstanceStack
).filter_by(instance_id=sc_instance_id
).all()
for stack in stacks:
session.delete(stack)
def _insert_chain_stack_db(self, session, sc_instance_id, stack_id):
with session.begin(subtransactions=True):
chainstack = ServiceChainInstanceStack(
instance_id=sc_instance_id,
stack_id=stack_id)
session.add(chainstack)
def _get_chain_stacks(self, session, sc_instance_id):
with session.begin(subtransactions=True):
stack_ids = session.query(ServiceChainInstanceStack.stack_id
).filter_by(instance_id=sc_instance_id
).all()
return stack_ids
def _get_resource(self, plugin, context, resource, resource_id):
obj_getter = getattr(plugin, 'get_' + resource)
obj = obj_getter(context, resource_id)
return obj
@property
def _core_plugin(self):
# REVISIT(Magesh): Need initialization method after all
# plugins are loaded to grab and store plugin.
return manager.NeutronManager.get_plugin()
@property
def _grouppolicy_plugin(self):
# REVISIT(Magesh): Need initialization method after all
# plugins are loaded to grab and store plugin.
plugins = manager.NeutronManager.get_service_plugins()
grouppolicy_plugin = plugins.get(pconst.GROUP_POLICY)
if not grouppolicy_plugin:
LOG.error(_LE("No Grouppolicy service plugin found."))
raise exc.ServiceChainDeploymentError()
return grouppolicy_plugin
class HeatClient(object):
def __init__(self, context, password=None):
api_version = "1"
self.tenant = context.tenant
self._keystone = None
endpoint = "%s/%s" % (cfg.CONF.simplechain.heat_uri, self.tenant)
kwargs = {
'token': self._get_auth_token(self.tenant),
'username': context.user_name,
'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.stacks = self.client.stacks
def create(self, name, data, parameters=None):
fields = {
'stack_name': name,
'timeout_mins': 10,
'disable_rollback': True,
'password': data.get('password')
}
fields['template'] = data
fields['parameters'] = parameters
return self.stacks.create(**fields)
def delete(self, stack_id):
try:
self.stacks.delete(stack_id)
except heat_exc.HTTPNotFound:
LOG.warning(_LW(
"Stack %(stack)s created by service chain driver is "
"not found at cleanup"), {'stack': stack_id})
def get(self, 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

@ -1,315 +0,0 @@
# 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.plugins.common import constants as pconst
from oslo_log import helpers as log
from oslo_log import log as logging
from oslo_utils import excutils
from gbpservice._i18n import _LE
import gbpservice.neutron.db.servicechain_db as servicechain_db
from gbpservice.neutron.services.grouppolicy.common import constants as gp_cts
from gbpservice.neutron.services.servicechain.plugins.msc import (
context as servicechain_context)
from gbpservice.neutron.services.servicechain.plugins.msc import (
driver_manager as manager)
from gbpservice.neutron.services.servicechain.plugins import sharing
LOG = logging.getLogger(__name__)
class ServiceChainPlugin(servicechain_db.ServiceChainDbPlugin,
sharing.SharingMixin):
"""Implementation of the Service Chain Plugin.
"""
supported_extension_aliases = ["servicechain"]
path_prefix = gp_cts.GBP_PREFIXES[pconst.SERVICECHAIN]
def __init__(self):
self.driver_manager = manager.DriverManager()
super(ServiceChainPlugin, self).__init__()
self.driver_manager.initialize()
@log.log_method_call
def create_servicechain_node(self, context, servicechain_node):
session = context.session
with session.begin(subtransactions=True):
result = super(ServiceChainPlugin, self).create_servicechain_node(
context, servicechain_node)
self._validate_shared_create(context, result, 'servicechain_node')
sc_context = servicechain_context.ServiceChainNodeContext(
self, context, result)
self.driver_manager.create_servicechain_node_precommit(
sc_context)
try:
self.driver_manager.create_servicechain_node_postcommit(
sc_context)
except Exception:
with excutils.save_and_reraise_exception():
LOG.error(_LE("driver_manager.create_servicechain_postcommit "
"failed, deleting servicechain_node %s"),
result['id'])
self.delete_servicechain_node(context, result['id'])
return result
@log.log_method_call
def update_servicechain_node(self, context, servicechain_node_id,
servicechain_node):
session = context.session
with session.begin(subtransactions=True):
original_sc_node = self.get_servicechain_node(
context, servicechain_node_id)
updated_sc_node = super(ServiceChainPlugin,
self).update_servicechain_node(
context, servicechain_node_id,
servicechain_node,
set_params=True)
self._validate_shared_update(context, original_sc_node,
updated_sc_node, 'servicechain_node')
sc_context = servicechain_context.ServiceChainNodeContext(
self, context, updated_sc_node,
original_sc_node=original_sc_node)
self.driver_manager.update_servicechain_node_precommit(
sc_context)
self.driver_manager.update_servicechain_node_postcommit(sc_context)
return updated_sc_node
@log.log_method_call
def delete_servicechain_node(self, context, servicechain_node_id):
session = context.session
with session.begin(subtransactions=True):
sc_node = self.get_servicechain_node(context,
servicechain_node_id)
sc_context = servicechain_context.ServiceChainNodeContext(
self, context, sc_node)
self.driver_manager.delete_servicechain_node_precommit(
sc_context)
super(ServiceChainPlugin, self).delete_servicechain_node(
context, servicechain_node_id)
try:
self.driver_manager.delete_servicechain_node_postcommit(
sc_context)
except Exception:
LOG.exception(_LE("delete_servicechain_node_postcommit failed "
"for servicechain_node %s"),
servicechain_node_id)
@log.log_method_call
def create_servicechain_spec(self, context, servicechain_spec):
session = context.session
with session.begin(subtransactions=True):
result = super(ServiceChainPlugin, self).create_servicechain_spec(
context, servicechain_spec)
self._validate_shared_create(context, result, 'servicechain_spec')
sc_context = servicechain_context.ServiceChainSpecContext(
self, context, result)
self.driver_manager.create_servicechain_spec_precommit(
sc_context)
try:
self.driver_manager.create_servicechain_spec_postcommit(sc_context)
except Exception:
with excutils.save_and_reraise_exception():
LOG.error(_LE("driver_manager.create_servicechain_postcommit "
"failed, deleting servicechain_spec %s"),
result['id'])
self.delete_servicechain_spec(context, result['id'])
return result
@log.log_method_call
def update_servicechain_spec(self, context, servicechain_spec_id,
servicechain_spec):
session = context.session
with session.begin(subtransactions=True):
original_sc_spec = self.get_servicechain_spec(
context, servicechain_spec_id)
updated_sc_spec = super(ServiceChainPlugin,
self).update_servicechain_spec(
context, servicechain_spec_id,
servicechain_spec)
self._validate_shared_update(context, original_sc_spec,
updated_sc_spec, 'servicechain_spec')
sc_context = servicechain_context.ServiceChainSpecContext(
self, context, updated_sc_spec,
original_sc_spec=original_sc_spec)
self.driver_manager.update_servicechain_spec_precommit(
sc_context)
self.driver_manager.update_servicechain_spec_postcommit(sc_context)
return updated_sc_spec
@log.log_method_call
def delete_servicechain_spec(self, context, servicechain_spec_id):
session = context.session
with session.begin(subtransactions=True):
sc_spec = self.get_servicechain_spec(context,
servicechain_spec_id)
sc_context = servicechain_context.ServiceChainSpecContext(
self, context, sc_spec)
self.driver_manager.delete_servicechain_spec_precommit(
sc_context)
super(ServiceChainPlugin, self).delete_servicechain_spec(
context, servicechain_spec_id)
try:
self.driver_manager.delete_servicechain_spec_postcommit(sc_context)
except Exception:
LOG.exception(_LE("delete_servicechain_spec_postcommit failed "
"for servicechain_spec %s"),
servicechain_spec_id)
@log.log_method_call
def create_servicechain_instance(self, context, servicechain_instance):
session = context.session
with session.begin(subtransactions=True):
result = super(ServiceChainPlugin,
self).create_servicechain_instance(
context, servicechain_instance)
sc_context = servicechain_context.ServiceChainInstanceContext(
self, context, result)
self.driver_manager.create_servicechain_instance_precommit(
sc_context)
try:
self.driver_manager.create_servicechain_instance_postcommit(
sc_context)
except Exception:
with excutils.save_and_reraise_exception():
LOG.error(_LE(
"driver_manager.create_servicechain_instance_postcommit "
"failed, deleting servicechain_instance %s"),
result['id'])
self.delete_servicechain_instance(context, result['id'])
return result
@log.log_method_call
def update_servicechain_instance(self, context,
servicechain_instance_id,
servicechain_instance):
session = context.session
with session.begin(subtransactions=True):
original_sc_instance = self.get_servicechain_instance(
context, servicechain_instance_id)
updated_sc_instance = super(ServiceChainPlugin,
self).update_servicechain_instance(
context, servicechain_instance_id,
servicechain_instance)
sc_context = servicechain_context.ServiceChainInstanceContext(
self, context, updated_sc_instance,
original_sc_instance=original_sc_instance)
self.driver_manager.update_servicechain_instance_precommit(
sc_context)
self.driver_manager.update_servicechain_instance_postcommit(
sc_context)
return updated_sc_instance
@log.log_method_call
def delete_servicechain_instance(self, context, servicechain_instance_id):
session = context.session
with session.begin(subtransactions=True):
sc_instance = self.get_servicechain_instance(
context,
servicechain_instance_id)
sc_context = servicechain_context.ServiceChainInstanceContext(
self, context, sc_instance)
self.driver_manager.delete_servicechain_instance_precommit(
sc_context)
super(ServiceChainPlugin, self).delete_servicechain_instance(
context, servicechain_instance_id)
try:
self.driver_manager.delete_servicechain_instance_postcommit(
sc_context)
except Exception:
LOG.exception(_LE("delete_servicechain_instance_postcommit failed "
"for servicechain_instance %s"),
servicechain_instance_id)
@log.log_method_call
def create_service_profile(self, context, service_profile):
session = context.session
with session.begin(subtransactions=True):
result = super(ServiceChainPlugin,
self).create_service_profile(
context, service_profile)
self._validate_shared_create(context, result, 'service_profile')
sc_context = servicechain_context.ServiceProfileContext(
self, context, result)
self.driver_manager.create_service_profile_precommit(
sc_context)
try:
self.driver_manager.create_service_profile_postcommit(
sc_context)
except Exception:
with excutils.save_and_reraise_exception():
LOG.error(_LE(
"driver_manager.create_service_profile_postcommit "
"failed, deleting service_profile %s"), result['id'])
self.delete_service_profile(context, result['id'])
return result
@log.log_method_call
def update_service_profile(self, context, service_profile_id,
service_profile):
session = context.session
with session.begin(subtransactions=True):
original_profile = self.get_service_profile(
context, service_profile_id)
updated_profile = super(ServiceChainPlugin,
self).update_service_profile(
context, service_profile_id, service_profile)
self._validate_shared_update(context, original_profile,
updated_profile, 'service_profile')
sc_context = servicechain_context.ServiceProfileContext(
self, context, updated_profile,
original_profile=original_profile)
self.driver_manager.update_service_profile_precommit(
sc_context)
self.driver_manager.update_service_profile_postcommit(
sc_context)
return updated_profile
@log.log_method_call
def delete_service_profile(self, context, service_profile_id):
session = context.session
with session.begin(subtransactions=True):
profile = self.get_service_profile(
context, service_profile_id)
sc_context = servicechain_context.ServiceProfileContext(
self, context, profile)
self.driver_manager.delete_service_profile_precommit(
sc_context)
super(ServiceChainPlugin, self).delete_service_profile(
context, service_profile_id)
try:
self.driver_manager.delete_service_profile_postcommit(
sc_context)
except Exception:
LOG.exception(_LE("delete_service_profile_postcommit failed "
"for service_profile %s"),
service_profile_id)

View File

@ -35,8 +35,8 @@ DB_GP_PLUGIN_KLASS = (GroupPolicyMappingDBTestPlugin.__module__ + '.' +
GroupPolicyMappingDBTestPlugin.__name__)
SC_PLUGIN_KLASS = (
"gbpservice.neutron.services.servicechain.plugins.msc.plugin."
"ServiceChainPlugin")
"gbpservice.neutron.services.servicechain.plugins.ncp.plugin."
"NodeCompositionPlugin")
class GroupPolicyMappingDbTestCase(tgpdb.GroupPolicyDbTestCase,

View File

@ -20,7 +20,7 @@ from neutron_lib.db import model_base
import webob.exc
from gbpservice.neutron.services.grouppolicy import config
from gbpservice.neutron.services.servicechain.plugins.msc import (
from gbpservice.neutron.services.servicechain.plugins.ncp import (
config as sc_cfg)
from gbpservice.neutron.tests.unit.services.grouppolicy import (
test_grouppolicy_plugin as test_plugin)
@ -40,8 +40,11 @@ class CommonNeutronBaseTestCase(test_plugin.GroupPolicyPluginTestBase):
config.cfg.CONF.set_override('policy_drivers',
policy_drivers,
group='group_policy')
sc_cfg.cfg.CONF.set_override('servicechain_drivers',
['dummy'], group='servicechain')
sc_cfg.cfg.CONF.set_override('node_drivers',
['dummy_driver'],
group='node_composition_plugin')
sc_cfg.cfg.CONF.set_override('node_plumber', 'dummy_plumber',
group='node_composition_plugin')
config.cfg.CONF.set_override('allow_overlapping_ips', True)
super(CommonNeutronBaseTestCase, self).setUp(core_plugin=core_plugin,
l3_plugin=l3_plugin,

View File

@ -30,6 +30,7 @@ from neutron.tests.unit.plugins.ml2 import test_plugin as n_test_plugin
from neutron_lib import constants as cst
from neutron_lib.db import model_base
from oslo_utils import uuidutils
import unittest2
import webob.exc
from gbpservice.common import utils
@ -42,7 +43,7 @@ from gbpservice.neutron.services.grouppolicy import config
from gbpservice.neutron.services.grouppolicy.drivers import chain_mapping
from gbpservice.neutron.services.grouppolicy.drivers import nsp_manager
from gbpservice.neutron.services.grouppolicy.drivers import resource_mapping
from gbpservice.neutron.services.servicechain.plugins.msc import (
from gbpservice.neutron.services.servicechain.plugins.ncp import (
config as sc_cfg)
from gbpservice.neutron.tests.unit.db.grouppolicy import test_group_policy_db
from gbpservice.neutron.tests.unit.services.grouppolicy import (
@ -79,8 +80,8 @@ class ResourceMappingTestCase(test_plugin.GroupPolicyPluginTestCase):
config.cfg.CONF.set_override('policy_drivers',
policy_drivers,
group='group_policy')
sc_cfg.cfg.CONF.set_override('servicechain_drivers',
['dummy'], group='servicechain')
sc_cfg.cfg.CONF.set_override('node_drivers', ['node_dummy'],
group='node_composition_plugin')
config.cfg.CONF.set_override('allow_overlapping_ips', True)
super(ResourceMappingTestCase, self).setUp(core_plugin=core_plugin,
ml2_options=ml2_options,
@ -2776,6 +2777,9 @@ class TestServiceChain(ResourceMappingTestCase):
sc_instances_provider_ptg_ids)
self.assertEqual([], sc_instance_update.call_args_list)
# This test is being skipped because the NCP plugin does not support
# multiple servicechain_specs per servicechain_instance
@unittest2.skip('skipping')
def test_action_spec_value_update(self):
scs1_id = self._create_servicechain_spec()
action_id, classifier_id, policy_rule_id = (
@ -3000,6 +3004,9 @@ class TestServiceChain(ResourceMappingTestCase):
self.assertEqual(
0, len(sc_instances['servicechain_instances']))
# This test is being skipped because the NCP plugin does not support
# multiple servicechain_specs per servicechain_instance
@unittest2.skip('skipping')
def test_parent_ruleset_update_for_redirect(self):
scs_id = self._create_servicechain_spec()
_, classifier_id, policy_rule_id = self._create_tcp_redirect_rule(
@ -3050,6 +3057,9 @@ class TestServiceChain(ResourceMappingTestCase):
self._verify_ptg_delete_cleanup_chain(provider_ptg_id)
# This test is being skipped because the NCP plugin does not support
# multiple servicechain_specs per servicechain_instance
@unittest2.skip('skipping')
def test_enforce_parent_redirect_after_ptg_create(self):
scs_id = self._create_servicechain_spec()
_, classifier_id, policy_rule_id = self._create_tcp_redirect_rule(
@ -3110,6 +3120,9 @@ class TestServiceChain(ResourceMappingTestCase):
self._verify_ptg_delete_cleanup_chain(provider_ptg_id)
# This test is being skipped because the NCP plugin does not support
# multiple servicechain_specs per servicechain_instance
@unittest2.skip('skipping')
def test_hierarchical_redirect(self):
scs_id = self._create_servicechain_spec()
action_id, classifier_id, policy_rule_id = (
@ -3194,6 +3207,9 @@ class TestServiceChain(ResourceMappingTestCase):
self._verify_ptg_delete_cleanup_chain(provider_ptg2_id)
# This test is being skipped because the NCP plugin does not support
# multiple servicechain_specs per servicechain_instance
@unittest2.skip('skipping')
def test_rule_update_hierarchial_prs(self):
scs_id = self._create_servicechain_spec()
action_id, classifier_id, policy_rule_id = (
@ -3294,6 +3310,9 @@ class TestServiceChain(ResourceMappingTestCase):
[parent_scs_id, scs2_id], classifier_id=classifier_id)
self._verify_ptg_delete_cleanup_chain(provider_ptg_id)
# This test is being skipped because the NCP plugin does not support
# multiple servicechain_specs per servicechain_instance
@unittest2.skip('skipping')
def test_redirect_multiple_ptgs_single_prs(self):
scs_id = self._create_servicechain_spec()
_, _, policy_rule_id = self._create_tcp_redirect_rule(

View File

@ -18,28 +18,28 @@ from neutron import context as n_ctx
from oslo_config import cfg
from oslo_serialization import jsonutils
from gbpservice.neutron.services.servicechain.plugins.msc import (
plugin as msc_plugin)
from gbpservice.neutron.services.servicechain.plugins.msc import context
from gbpservice.neutron.services.servicechain.plugins.ncp import (
plugin as ncp_plugin)
from gbpservice.neutron.services.servicechain.plugins.ncp import context
from gbpservice.neutron.tests.unit.db.grouppolicy import (
test_servicechain_db as test_servicechain_db)
from gbpservice.neutron.tests.unit.db.grouppolicy import test_group_policy_db
cfg.CONF.import_opt(
'servicechain_drivers',
'gbpservice.neutron.services.servicechain.plugins.msc.config',
group='servicechain')
'node_drivers',
'gbpservice.neutron.services.servicechain.plugins.ncp.config',
group='node_composition_plugin')
class ServiceChainMSCTestPlugin(msc_plugin.ServiceChainPlugin):
class ServiceChainNCPTestPlugin(ncp_plugin.NodeCompositionPlugin):
supported_extension_aliases = ['servicechain'] + (
test_group_policy_db.UNSUPPORTED_REQUIRED_EXTS)
path_prefix = "/servicechain"
SC_PLUGIN_KLASS = (ServiceChainMSCTestPlugin.__module__ + '.' +
ServiceChainMSCTestPlugin.__name__)
SC_PLUGIN_KLASS = (ServiceChainNCPTestPlugin.__module__ + '.' +
ServiceChainNCPTestPlugin.__name__)
class ServiceChainPluginTestCase(test_servicechain_db.ServiceChainDbTestCase):
@ -51,7 +51,7 @@ class ServiceChainPluginTestCase(test_servicechain_db.ServiceChainDbTestCase):
gp_plugin=gp_plugin)
class TestGroupPolicyPluginGroupResources(
class BaseTestGroupPolicyPluginGroupResources(
ServiceChainPluginTestCase,
test_servicechain_db.TestServiceChainResources):
@ -159,13 +159,14 @@ class TestGroupPolicyPluginGroupResources(
current = self.create_servicechain_node(
service_profile_id=prof['id'],
expected_res_status=201)['servicechain_node']
ctx = context.ServiceChainNodeContext(self.plugin, plugin_context,
current)
ctx = context.NodeDriverContext(self.plugin, plugin_context,
None, None, current, 0,
prof, None)
self.assertIsNone(ctx.original)
self.assertIsNone(ctx.original_node)
self.assertIsNone(ctx.original_profile)
self.assertEqual(ctx.current['id'], current['id'])
self.assertEqual(ctx.current_profile['id'], prof['id'])
self.assertEqual(ctx.current_node, current)
self.assertEqual(ctx.current_profile, prof)
# Original node with profile
@ -174,13 +175,15 @@ class TestGroupPolicyPluginGroupResources(
original = self.create_servicechain_node(
service_profile_id=prof2['id'],
expected_res_status=201)['servicechain_node']
ctx = context.ServiceChainNodeContext(self.plugin, plugin_context,
current, original)
ctx = context.NodeDriverContext(
self.plugin, plugin_context, None, None, current, 0,
prof, None, original_service_chain_node=original,
original_service_profile=prof2)
self.assertEqual(ctx.original['id'], original['id'])
self.assertEqual(ctx.original_profile['id'], prof2['id'])
self.assertEqual(ctx.current['id'], current['id'])
self.assertEqual(ctx.current_profile['id'], prof['id'])
self.assertEqual(ctx.original_node, original)
self.assertEqual(ctx.original_profile, prof2)
self.assertEqual(ctx.current_node, current)
self.assertEqual(ctx.current_profile, prof)
def test_node_context_no_profile(self):
@ -191,23 +194,25 @@ class TestGroupPolicyPluginGroupResources(
current = self.create_servicechain_node(
service_type='TEST',
expected_res_status=201)['servicechain_node']
ctx = context.ServiceChainNodeContext(self.plugin, plugin_context,
current)
ctx = context.NodeDriverContext(self.plugin, plugin_context,
None, None, current, 0,
None, None)
self.assertIsNone(ctx.original)
self.assertIsNone(ctx.original_node)
self.assertIsNone(ctx.original_profile)
self.assertEqual(ctx.current['id'], current['id'])
self.assertEqual(ctx.current_node, current)
self.assertIsNone(ctx.current_profile)
original = self.create_servicechain_node(
service_type='TEST',
expected_res_status=201)['servicechain_node']
ctx = context.ServiceChainNodeContext(self.plugin, plugin_context,
current, original)
ctx = context.NodeDriverContext(
self.plugin, plugin_context, None, None, current, 0,
None, None, original_service_chain_node=original)
self.assertEqual(ctx.original['id'], original['id'])
self.assertEqual(ctx.original_node, original)
self.assertIsNone(ctx.original_profile)
self.assertEqual(ctx.current['id'], current['id'])
self.assertEqual(ctx.current_node, current)
self.assertIsNone(ctx.current_profile)
def test_spec_parameters(self):

View File

@ -39,7 +39,7 @@ from gbpservice.neutron.tests.unit.db.grouppolicy import test_group_policy_db
from gbpservice.neutron.tests.unit.services.grouppolicy import (
test_resource_mapping as test_gp_driver)
from gbpservice.neutron.tests.unit.services.servicechain import (
test_servicechain_plugin as test_base)
base_test_servicechain_plugin as test_base)
class ServiceChainNCPTestPlugin(ncp_plugin.NodeCompositionPlugin):
@ -125,7 +125,7 @@ class NodeCompositionPluginTestMixin(object):
class NodeCompositionPluginTestCase(
test_base.TestGroupPolicyPluginGroupResources,
test_base.BaseTestGroupPolicyPluginGroupResources,
NodeCompositionPluginTestMixin):
def setUp(self, core_plugin=None, gp_plugin=None, node_drivers=None,

View File

@ -28,7 +28,7 @@ from gbpservice.neutron.tests.unit.db.grouppolicy import test_group_policy_db
from gbpservice.neutron.tests.unit.services.grouppolicy import (
test_resource_mapping as test_gp_driver)
from gbpservice.neutron.tests.unit.services.servicechain import (
test_servicechain_plugin as test_base)
base_test_servicechain_plugin as test_base)
from gbpservice.neutron.tests.unit.services.servicechain.ncp import (
test_ncp_plugin as test_ncp_plugin)
@ -52,7 +52,7 @@ GP_PLUGIN_KLASS = (
class NFPNodeDriverTestCase(
test_base.TestGroupPolicyPluginGroupResources,
test_base.BaseTestGroupPolicyPluginGroupResources,
test_ncp_plugin.NodeCompositionPluginTestMixin):
DEFAULT_VPN_CONFIG_DICT = {

View File

@ -1,311 +0,0 @@
# 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 heatclient
import mock
from neutron.plugins.common import constants
from oslo_serialization import jsonutils
from oslo_utils import uuidutils
import webob
from gbpservice.neutron.services.servicechain.plugins.msc import config
from gbpservice.neutron.services.servicechain.plugins.msc.drivers import (
simplechain_driver as simplechain_driver)
from gbpservice.neutron.tests.unit.services.servicechain import (
test_servicechain_plugin as test_servicechain_plugin)
STACK_DELETE_RETRIES = 5
STACK_DELETE_RETRY_WAIT = 3
class MockStackObject(object):
def __init__(self, status):
self.stack_status = status
class MockHeatClientFunctions(object):
def delete(self, stack_id):
raise heatclient.exc.HTTPNotFound()
def create(self, **fields):
return {'stack': {'id': uuidutils.generate_uuid()}}
def get(self, stack_id):
return MockStackObject('DELETE_COMPLETE')
def update(self, *args, **fields):
return {'stack': {'id': uuidutils.generate_uuid()}}
class MockHeatClient(object):
def __init__(self, api_version, endpoint, **kwargs):
self.stacks = MockHeatClientFunctions()
class SimpleChainDriverTestCase(
test_servicechain_plugin.ServiceChainPluginTestCase):
def setUp(self):
config.cfg.CONF.set_override('servicechain_drivers',
['simplechain_driver'],
group='servicechain')
config.cfg.CONF.set_override('stack_delete_retries',
STACK_DELETE_RETRIES,
group='simplechain')
config.cfg.CONF.set_override('stack_delete_retry_wait',
STACK_DELETE_RETRY_WAIT,
group='simplechain')
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):
def test_invalid_service_type_rejected(self):
res = self.create_service_profile(
service_type="test",
expected_res_status=webob.exc.HTTPBadRequest.code)
self.assertEqual('InvalidServiceTypeForReferenceDriver',
res['NeutronError']['type'])
def test_chain_node_create_success(self):
res = self._create_profiled_servicechain_node(
service_type=constants.FIREWALL, config='{}',
expected_res_status=webob.exc.HTTPCreated.code)
self.assertEqual('{}', res['servicechain_node']['config'])
def test_in_use_node_config_update_rejected(self):
node = self.create_servicechain_node(
service_type=constants.FIREWALL, config='{}',
expected_res_status=webob.exc.HTTPCreated.code)[
'servicechain_node']
self.assertEqual('{}', node['config'])
spec = self.create_servicechain_spec(
nodes=[node['id']],
expected_res_status=webob.exc.HTTPCreated.code)[
'servicechain_spec']
with mock.patch.object(simplechain_driver.HeatClient,
'create') as stack_create:
stack_create.return_value = {'stack': {
'id': uuidutils.generate_uuid()}}
self.create_servicechain_instance(servicechain_specs=[spec['id']])
res = self.update_servicechain_node(
node['id'],
config='{"key": "value"}',
expected_res_status=webob.exc.HTTPBadRequest.code)
self.assertEqual('NodeUpdateNotSupported',
res['NeutronError']['type'])
def test_chain_spec_update(self):
template1 = '{"key1":"value1"}'
scn = self._create_profiled_servicechain_node(config=template1)
scn1_name = scn['servicechain_node']['name']
scn_id = scn['servicechain_node']['id']
name = "scs1"
template2 = '{"key2":"value2"}'
scn2 = self._create_profiled_servicechain_node(config=template2)
scn2_id = scn2['servicechain_node']['id']
scn2_name = scn2['servicechain_node']['name']
scs = self.create_servicechain_spec(name=name, nodes=[scn_id])
sc_spec_id = scs['servicechain_spec']['id']
stack1 = {'stack': {'id': uuidutils.generate_uuid()}}
stack2 = {'stack': {'id': uuidutils.generate_uuid()}}
stack3 = {'stack': {'id': uuidutils.generate_uuid()}}
expected_create_calls = []
expected_delete_calls = []
with mock.patch.object(simplechain_driver.HeatClient,
'create') as stack_create:
with mock.patch.object(simplechain_driver.HeatClient,
'delete') as stack_delete:
stack_create.return_value = stack1
instance1_name = "sc_instance_1"
sc_instance1 = self.create_servicechain_instance(
name=instance1_name,
servicechain_specs=[sc_spec_id])
sci1_id = sc_instance1['servicechain_instance']['id']
self.assertEqual([sc_spec_id],
sc_instance1['servicechain_instance'][
'servicechain_specs'])
stack_name = ("stack_" + instance1_name + scn1_name +
sci1_id[:8])
expected_create_calls.append(
mock.call(stack_name,
jsonutils.loads(template1), {}))
stack_create.return_value = stack2
instance2_name = "sc_instance_2"
sc_instance2 = self.create_servicechain_instance(
name=instance2_name,
servicechain_specs=[sc_spec_id])
sci2_id = sc_instance2['servicechain_instance']['id']
self.assertEqual(
[sc_spec_id],
sc_instance2['servicechain_instance'][
'servicechain_specs'])
stack_name = ("stack_" + instance2_name + scn1_name +
sci2_id[:8])
expected_create_calls.append(
mock.call(stack_name, jsonutils.loads(template1), {}))
#Now perform an update of the spec
new_spec = {'servicechain_spec': {'nodes': [scn2_id]}}
stack_create.return_value = stack3
req = self.new_update_request(
'servicechain_specs', new_spec, sc_spec_id)
res = req.get_response(self.ext_api)
self.assertEqual(webob.exc.HTTPOk.code, res.status_int)
# The two existing stacks will be deleted and two new stacks
# will be created
expected_delete_calls.append(mock.call(stack1['stack']['id']))
expected_delete_calls.append(mock.call(stack2['stack']['id']))
stack_name = ("stack_" + instance1_name + scn2_name +
sci1_id[:8])
expected_create_calls.append(
mock.call(stack_name, jsonutils.loads(template2), {}))
stack_name = ("stack_" + instance2_name + scn2_name +
sci2_id[:8])
expected_create_calls.append(
mock.call(stack_name, jsonutils.loads(template2), {}))
self.assertEqual(expected_delete_calls,
stack_delete.call_args_list)
self.assertEqual(expected_create_calls,
stack_create.call_args_list)
def test_chain_instance_create(self):
name = "scs1"
scn = self._create_profiled_servicechain_node()
scn_id = scn['servicechain_node']['id']
scs = self.create_servicechain_spec(name=name, nodes=[scn_id])
sc_spec_id = scs['servicechain_spec']['id']
with mock.patch.object(simplechain_driver.HeatClient,
'create') as stack_create:
stack_create.return_value = {'stack': {
'id': uuidutils.generate_uuid()}}
sc_instance = self.create_servicechain_instance(
name="sc_instance_1",
servicechain_specs=[sc_spec_id])
expected_stack_name = (
"stack_" + "sc_instance_1" +
scn['servicechain_node']['name'] +
sc_instance['servicechain_instance']['id'][:8])
self.assertEqual(
[sc_spec_id],
sc_instance['servicechain_instance']['servicechain_specs'])
stack_create.assert_called_once_with(
expected_stack_name, mock.ANY, mock.ANY)
def test_chain_instance_delete(self):
name = "scs1"
scn = self._create_profiled_servicechain_node()
scn_id = scn['servicechain_node']['id']
scs = self.create_servicechain_spec(name=name, nodes=[scn_id])
sc_spec_id = scs['servicechain_spec']['id']
with mock.patch.object(simplechain_driver.HeatClient,
'create') as stack_create:
stack_create.return_value = {'stack': {
'id': uuidutils.generate_uuid()}}
sc_instance = self.create_servicechain_instance(
name="sc_instance_1",
servicechain_specs=[sc_spec_id])
self.assertEqual([sc_spec_id],
sc_instance['servicechain_instance']['servicechain_specs'])
with mock.patch.object(simplechain_driver.HeatClient,
'delete'):
req = self.new_delete_request(
'servicechain_instances',
sc_instance['servicechain_instance']['id'])
res = req.get_response(self.ext_api)
self.assertEqual(webob.exc.HTTPNoContent.code, res.status_int)
def test_wait_stack_delete_for_instance_delete(self):
name = "scs1"
scn = self._create_profiled_servicechain_node()
scn_id = scn['servicechain_node']['id']
scs = self.create_servicechain_spec(name=name, nodes=[scn_id])
sc_spec_id = scs['servicechain_spec']['id']
with mock.patch.object(simplechain_driver.HeatClient,
'create') as stack_create:
stack_create.return_value = {'stack': {
'id': uuidutils.generate_uuid()}}
sc_instance = self.create_servicechain_instance(
name="sc_instance_1",
servicechain_specs=[sc_spec_id])
self.assertEqual([sc_spec_id],
sc_instance['servicechain_instance']['servicechain_specs'])
# Verify that as part of delete service chain instance we call
# get method for heat stack 5 times before giving up if the state
# does not become DELETE_COMPLETE
with mock.patch.object(simplechain_driver.HeatClient,
'delete') as stack_delete:
with mock.patch.object(simplechain_driver.HeatClient,
'get') as stack_get:
stack_get.return_value = MockStackObject('PENDING_DELETE')
req = self.new_delete_request(
'servicechain_instances',
sc_instance['servicechain_instance']['id'])
res = req.get_response(self.ext_api)
self.assertEqual(webob.exc.HTTPNoContent.code,
res.status_int)
stack_delete.assert_called_once_with(mock.ANY)
self.assertEqual(STACK_DELETE_RETRIES,
stack_get.call_count)
# Create and delete another service chain instance and verify that
# we call get method for heat stack only once if the stack state
# is DELETE_COMPLETE
sc_instance = self.create_servicechain_instance(
name="sc_instance_1",
servicechain_specs=[sc_spec_id])
self.assertEqual(
[sc_spec_id],
sc_instance['servicechain_instance']['servicechain_specs'])
with mock.patch.object(simplechain_driver.HeatClient,
'delete') as stack_delete:
with mock.patch.object(simplechain_driver.HeatClient,
'get') as stack_get:
stack_get.return_value = MockStackObject(
'DELETE_COMPLETE')
req = self.new_delete_request(
'servicechain_instances',
sc_instance['servicechain_instance']['id'])
res = req.get_response(self.ext_api)
self.assertEqual(webob.exc.HTTPNoContent.code,
res.status_int)
stack_delete.assert_called_once_with(mock.ANY)
self.assertEqual(1, stack_get.call_count)
def test_stack_not_found_ignored(self):
name = "scs1"
scn = self._create_profiled_servicechain_node()
scn_id = scn['servicechain_node']['id']
scs = self.create_servicechain_spec(name=name, nodes=[scn_id])
sc_spec_id = scs['servicechain_spec']['id']
mock.patch(heatclient.__name__ + ".client.Client",
new=MockHeatClient).start()
sc_instance = self.create_servicechain_instance(
name="sc_instance_1",
servicechain_specs=[sc_spec_id])
req = self.new_delete_request(
'servicechain_instances',
sc_instance['servicechain_instance']['id'])
res = req.get_response(self.ext_api)
self.assertEqual(webob.exc.HTTPNoContent.code, res.status_int)

View File

@ -25,9 +25,6 @@ packages =
data_files =
etc/group-based-policy/policy.d =
etc/policy.json
etc/servicechain/plugins/msc =
etc/servicechain/plugins/msc/servicechain.ini
etc/servicechain/plugins/msc/simplechain.ini
etc/group-based-policy/drivers =
etc/drivers/implicit_policy.ini
etc/drivers/resource_mapping.ini
@ -47,8 +44,6 @@ neutron.core_plugins =
ml2plus = gbpservice.neutron.plugins.ml2plus.plugin:Ml2PlusPlugin
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
apic_gbp_l3 = gbpservice.neutron.services.l3_router.l3_apic:ApicGBPL3ServicePlugin
nfp_fwaas = gbpservice.contrib.nfp.service_plugins.firewall.nfp_fwaas_plugin:NFPFirewallPlugin
@ -77,9 +72,6 @@ neutron.ml2.extension_drivers =
apic_aim = gbpservice.neutron.plugins.ml2plus.drivers.apic_aim.extension_driver:ApicExtensionDriver
test_ml2plus = gbpservice.neutron.tests.unit.plugins.ml2plus.drivers.extension_test:TestExtensionDriver
testdb_ml2plus = gbpservice.neutron.tests.unit.plugins.ml2plus.drivers.extension_test:TestDBExtensionDriver
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
gbpservice.neutron.servicechain.ncp_drivers =
node_dummy = gbpservice.neutron.services.servicechain.plugins.ncp.node_drivers.dummy_driver:NoopNodeDriver
heat_node_driver = gbpservice.neutron.services.servicechain.plugins.ncp.node_drivers.heat_node_driver:HeatNodeDriver