NFP - Node Driver

Change-Id: I4404ae2ecd25893ea4154d621cb140a2aa0abd8c
Implements: blueprint gbp-network-services-framework
Co-Authored-By: ashutosh mishra <mca.ashu4@gmail.com>
Co-Authored-By: ahmed khan <ahmed.khan@oneconvergence.com>
Co-Authored-By: Akash Deep <akash.deep@oneconvergence.com>
This commit is contained in:
YogeshRajmane 2016-03-29 00:31:38 +05:30 committed by ashutosh mishra
parent ba4ac0103b
commit 3270fb5ea4
6 changed files with 1716 additions and 1 deletions

View File

@ -1 +1 @@
54ee8e8d205a
c1aab79622fe

View File

@ -0,0 +1,43 @@
# 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.
#
"""ncp_node_instance_nf_mapping
Revision ID: c1aab79622fe
Revises: 54ee8e8d205a
"""
# revision identifiers, used by Alembic.
revision = 'c1aab79622fe'
down_revision = '54ee8e8d205a'
from alembic import op
import sqlalchemy as sa
def upgrade(active_plugins=None, options=None):
op.create_table(
'ncp_node_instance_network_function_mappings',
sa.Column('sc_instance_id', sa.String(length=36), nullable=False),
sa.Column('sc_node_id', sa.String(length=36), nullable=False),
sa.Column('network_function_id', sa.String(length=36), nullable=False),
sa.PrimaryKeyConstraint('sc_instance_id',
'sc_node_id',
'network_function_id'),
)
def downgrade(active_plugins=None, options=None):
pass

View File

@ -0,0 +1,907 @@
# 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 eventlet
from eventlet import greenpool
from keystoneclient import exceptions as k_exceptions
from keystoneclient.v2_0 import client as keyclient
from neutron._i18n import _LE
from neutron._i18n import _LI
from neutron.common import exceptions as n_exc
from neutron.common import rpc as n_rpc
from neutron.db import model_base
from neutron.plugins.common import constants as pconst
from oslo_config import cfg
from oslo_log import log as logging
import oslo_messaging
from oslo_serialization import jsonutils
from oslo_utils import excutils
import sqlalchemy as sa
from sqlalchemy.orm.exc import NoResultFound
from gbpservice.common import utils
from gbpservice.neutron.services.servicechain.plugins.ncp import (
exceptions as exc)
from gbpservice.neutron.services.servicechain.plugins.ncp import driver_base
from gbpservice.neutron.services.servicechain.plugins.ncp import plumber_base
from gbpservice.nfp.common import constants as nfp_constants
from gbpservice.nfp.common import topics as nfp_rpc_topics
NFP_NODE_DRIVER_OPTS = [
cfg.BoolOpt('is_service_admin_owned',
help=_("Parameter to indicate whether the Service VM has to "
"be owned by the Admin"),
default=False),
cfg.IntOpt('service_create_timeout',
default=600,
help=_("Seconds to wait for service creation "
"to complete")),
cfg.IntOpt('service_delete_timeout',
default=120,
help=_("Seconds to wait for service deletion "
"to complete")),
]
# REVISIT(ashu): Can we use is_service_admin_owned config from RMD
cfg.CONF.register_opts(NFP_NODE_DRIVER_OPTS, "nfp_node_driver")
LOG = logging.getLogger(__name__)
class InvalidServiceType(exc.NodeCompositionPluginBadRequest):
message = _("The NFP Node driver only supports the services "
"VPN, Firewall and LB in a Service Chain")
class ServiceProfileRequired(exc.NodeCompositionPluginBadRequest):
message = _("A Service profile is required in Service node")
class NodeVendorMismatch(exc.NodeCompositionPluginBadRequest):
message = _("The NFP Node driver only handles nodes which have service "
"profile with vendor name %(vendor)s")
class DuplicateServiceTypeInChain(exc.NodeCompositionPluginBadRequest):
message = _("The NFP Node driver does not support duplicate "
"service types in same chain")
class RequiredProfileAttributesNotSet(exc.NodeCompositionPluginBadRequest):
message = _("The required attributes in service profile are not present")
class InvalidNodeOrderInChain(exc.NodeCompositionPluginBadRequest):
message = _("The NFP Node driver does not support the order "
"of nodes defined in the current service chain spec, "
"order should be : %(node_order)s")
class UnSupportedServiceProfile(exc.NodeCompositionPluginBadRequest):
message = _("The NFP Node driver does not support this service "
"profile with service type %(service_type)s and vendor "
"%(vendor)s")
class UnSupportedInsertionMode(exc.NodeCompositionPluginBadRequest):
message = _("The NFP Node driver supports only L3 Insertion "
"mode")
class ServiceInfoNotAvailableOnUpdate(n_exc.NeutronException):
message = _("Service information is not available with Service Manager "
"on node update")
class VipNspNotSetonProvider(n_exc.NeutronException):
message = _("Network Service policy for VIP IP address is not configured "
"on the Providing Group")
class NodeInstanceDeleteFailed(n_exc.NeutronException):
message = _("Node instance delete failed in NFP Node driver")
class NodeInstanceCreateFailed(n_exc.NeutronException):
message = _("Node instance create failed in NFP Node driver")
class NodeInstanceUpdateFailed(n_exc.NeutronException):
message = _("Node instance update failed in NFP Node driver")
class ServiceNodeInstanceNetworkFunctionMapping(model_base.BASEV2):
"""ServiceChainInstance to NFP network function mapping."""
__tablename__ = 'ncp_node_instance_network_function_mappings'
sc_instance_id = sa.Column(sa.String(36),
nullable=False, primary_key=True)
sc_node_id = sa.Column(sa.String(36),
nullable=False, primary_key=True)
network_function_id = sa.Column(sa.String(36),
nullable=False, primary_key=True)
class NFPClientApi(object):
""" Client side of the NFP Framework user """
RPC_API_VERSION = '1.0'
def __init__(self, topic):
target = oslo_messaging.Target(
topic=topic, version=self.RPC_API_VERSION)
self.client = n_rpc.get_client(target)
def create_network_function(self, context, network_function):
cctxt = self.client.prepare(
fanout=False, topic=nfp_rpc_topics.NFP_NSO_TOPIC)
return cctxt.call(
context,
'create_network_function',
network_function=network_function)
def delete_network_function(self, context, network_function_id):
cctxt = self.client.prepare(version=self.RPC_API_VERSION)
return cctxt.call(
context,
'delete_network_function',
network_function_id=network_function_id)
def update_network_function(self, context, network_function_id, config):
cctxt = self.client.prepare(version=self.RPC_API_VERSION)
return cctxt.call(
context,
'update_network_function',
network_function_id=network_function_id,
config=config)
def get_network_function(self, context, network_function_id):
cctxt = self.client.prepare(version=self.RPC_API_VERSION)
return cctxt.call(
context,
'get_network_function',
network_function_id=network_function_id)
def consumer_ptg_added_notification(self, context, network_function_id,
policy_target_group):
cctxt = self.client.prepare(version=self.RPC_API_VERSION)
return cctxt.call(context,
'consumer_ptg_added_notification',
network_function_id=network_function_id,
policy_target_group=policy_target_group)
def consumer_ptg_removed_notification(self, context, network_function_id,
policy_target_group):
cctxt = self.client.prepare(version=self.RPC_API_VERSION)
return cctxt.call(context,
'consumer_ptg_removed_notification',
network_function_id=network_function_id,
policy_target_group=policy_target_group)
def policy_target_added_notification(self, context, network_function_id,
policy_target):
cctxt = self.client.prepare(version=self.RPC_API_VERSION)
return cctxt.call(context,
'policy_target_added_notification',
network_function_id=network_function_id,
policy_target=policy_target)
def policy_target_removed_notification(self, context, network_function_id,
policy_target):
cctxt = self.client.prepare(version=self.RPC_API_VERSION)
return cctxt.call(context,
'policy_target_removed_notification',
network_function_id=network_function_id,
policy_target=policy_target)
class NFPNodeDriver(driver_base.NodeDriverBase):
SUPPORTED_SERVICE_TYPES = [
pconst.LOADBALANCER, pconst.FIREWALL, pconst.VPN,
pconst.LOADBALANCERV2]
SUPPORTED_SERVICE_VENDOR_MAPPING = {
pconst.LOADBALANCERV2: [nfp_constants.HAPROXY_LBAASV2],
pconst.LOADBALANCER: [nfp_constants.HAPROXY_VENDOR],
pconst.FIREWALL: [nfp_constants.VYOS_VENDOR, nfp_constants.NFP_VENDOR],
pconst.VPN: [nfp_constants.VYOS_VENDOR],
}
vendor_name = nfp_constants.NFP_VENDOR.upper()
required_heat_resources = {
pconst.LOADBALANCERV2: ['OS::Neutron::LBaaS::LoadBalancer',
'OS::Neutron::LBaaS::Listener',
'OS::Neutron::LBaaS::Pool'],
pconst.LOADBALANCER: ['OS::Neutron::LoadBalancer',
'OS::Neutron::Pool'],
pconst.FIREWALL: ['OS::Neutron::Firewall',
'OS::Neutron::FirewallPolicy'],
pconst.VPN: ['OS::Neutron::VPNService'],
}
initialized = False
def __init__(self):
super(NFPNodeDriver, self).__init__()
self._lbaas_plugin = None
self.thread_pool = greenpool.GreenPool(10)
self.active_threads = []
self.sc_node_count = 0
@property
def name(self):
return self._name
def initialize(self, name):
self.initialized = True
self._name = name
if cfg.CONF.nfp_node_driver.is_service_admin_owned:
self.resource_owner_tenant_id = self._resource_owner_tenant_id()
else:
self.resource_owner_tenant_id = None
self._setup_rpc()
def _setup_rpc(self):
self.nfp_notifier = NFPClientApi(nfp_rpc_topics.NFP_NSO_TOPIC)
def _parse_service_flavor_string(self, service_flavor_str):
service_details = {}
if ',' not in service_flavor_str:
service_details['device_type'] = 'nova'
service_details['service_vendor'] = service_flavor_str
else:
service_flavor_dict = dict(item.split('=') for item
in service_flavor_str.split(','))
service_details = {key.strip(): value.strip() for key, value
in service_flavor_dict.iteritems()}
return service_details
def get_plumbing_info(self, context):
context._plugin_context = self._get_resource_owner_context(
context._plugin_context)
service_type = context.current_profile['service_type']
service_flavor_str = context.current_profile['service_flavor']
service_details = self._parse_service_flavor_string(service_flavor_str)
if service_details['device_type'] == 'None':
return {}
# Management PTs are managed by NFP since it supports hosting multiple
# logical services in a single device
plumbing_request = {'management': [], 'provider': [{}],
'consumer': [{}]}
if service_type in [pconst.FIREWALL, pconst.VPN]:
plumbing_request['plumbing_type'] = (
nfp_constants.GATEWAY_TYPE)
else: # Loadbalancer which is one arm
plumbing_request['consumer'] = []
plumbing_request['plumbing_type'] = (
nfp_constants.ENDPOINT_TYPE)
LOG.info(_LI("Requesting plumber for %(plumbing_request)s PTs for "
"service type %(service_type)s"),
{'plumbing_request': plumbing_request,
'service_type': service_type})
return plumbing_request
def validate_create(self, context):
if not context.current_profile:
raise ServiceProfileRequired()
if (not context.current_profile['vendor'] or not
context.current_profile['insertion_mode'] or not
context.current_profile['service_type'] or not
context.current_profile['service_flavor']):
raise RequiredProfileAttributesNotSet()
if context.current_profile['vendor'] != self.vendor_name:
raise NodeVendorMismatch(vendor=self.vendor_name)
if (context.current_profile['insertion_mode'].lower() !=
nfp_constants.L3_INSERTION_MODE):
raise UnSupportedInsertionMode()
if context.current_profile['service_type'] not in (
self.SUPPORTED_SERVICE_TYPES):
raise InvalidServiceType()
service_vendor = self._parse_service_flavor_string(
context.current_profile['service_flavor'])['service_vendor']
if (service_vendor.lower() not in
self.SUPPORTED_SERVICE_VENDOR_MAPPING[
context.current_profile['service_type']]):
raise UnSupportedServiceProfile(
service_type=context.current_profile['service_type'],
vendor=context.current_profile['vendor'])
self._is_node_order_in_spec_supported(context)
def validate_update(self, context):
if not context.original_node: # PT create/delete notifications
return
if context.current_node and not context.current_profile:
raise ServiceProfileRequired()
if context.current_profile['vendor'] != self.vendor_name:
raise NodeVendorMismatch(vendor=self.vendor_name)
if (context.current_profile['insertion_mode'].lower() !=
nfp_constants.L3_INSERTION_MODE):
raise UnSupportedInsertionMode()
if context.current_profile['service_type'] not in (
self.SUPPORTED_SERVICE_TYPES):
raise InvalidServiceType()
service_vendor = self._parse_service_flavor_string(
context.current_profile['service_flavor'])['service_vendor']
if (service_vendor.lower() not in
self.SUPPORTED_SERVICE_VENDOR_MAPPING[
context.current_profile['service_type']]):
raise UnSupportedServiceProfile(
service_type=context.current_profile['service_type'],
vendor=context.current_profile['vendor'])
def _wait(self, thread):
try:
result = thread.wait()
return result
except Exception as e:
self.active_threads = []
raise e
def create(self, context):
context._plugin_context = self._get_resource_owner_context(
context._plugin_context)
network_function_id = self._create_network_function(context)
self._set_node_instance_network_function_map(
context.plugin_session, context.current_node['id'],
context.instance['id'], network_function_id)
# Check for NF status in a separate thread
LOG.debug("Spawning thread for nf ACTIVE poll")
gth = self.thread_pool.spawn(
self._wait_for_network_function_operation_completion,
context, network_function_id, operation=nfp_constants.CREATE)
self.active_threads.append(gth)
LOG.debug("Active Threads count (%d), sc_node_count (%d)" % (
len(self.active_threads), self.sc_node_count))
self.sc_node_count -= 1
# At last wait for the threads to complete, success/failure/timeout
if self.sc_node_count == 0:
self.thread_pool.waitall()
# Get the results
for gth in self.active_threads:
self._wait(gth)
self.active_threads = []
def update(self, context):
context._plugin_context = self._get_resource_owner_context(
context._plugin_context)
network_function_map = self._get_node_instance_network_function_map(
context.plugin_session,
context.current_node['id'],
context.instance['id'])
if not all([network_function_map, context.original_node.get('config'),
context.current_node.get('config')]):
return
network_function_id = network_function_map.network_function_id
self._update(context, network_function_id)
self._wait_for_network_function_operation_completion(
context, network_function_id, operation=nfp_constants.UPDATE)
def delete(self, context):
context._plugin_context = self._get_resource_owner_context(
context._plugin_context)
network_function_map = self._get_node_instance_network_function_map(
context.plugin_session,
context.current_node['id'],
context.instance['id'])
if not network_function_map:
return
network_function_id = network_function_map.network_function_id
try:
self.nfp_notifier.delete_network_function(
context=context.plugin_context,
network_function_id=network_function_id)
except Exception:
LOG.exception(_LE("Delete Network service Failed"))
self._wait_for_network_function_delete_completion(
context, network_function_id)
self._delete_node_instance_network_function_map(
context.plugin_session,
context.current_node['id'],
context.instance['id'])
def update_policy_target_added(self, context, policy_target):
if context.current_profile['service_type'] in [pconst.LOADBALANCER,
pconst.LOADBALANCERV2]:
if self._is_service_target(policy_target):
return
context._plugin_context = self._get_resource_owner_context(
context._plugin_context)
network_function_map =\
self._get_node_instance_network_function_map(
context.plugin_session,
context.current_node['id'],
context.instance['id'])
if network_function_map:
network_function_id = network_function_map.network_function_id
self.nfp_notifier.policy_target_added_notification(
context.plugin_context, network_function_id, policy_target)
self._wait_for_network_function_operation_completion(
context, network_function_id,
operation=nfp_constants.UPDATE)
def update_policy_target_removed(self, context, policy_target):
if context.current_profile['service_type'] in [pconst.LOADBALANCER,
pconst.LOADBALANCERV2]:
if self._is_service_target(policy_target):
return
context._plugin_context = self._get_resource_owner_context(
context._plugin_context)
network_function_map = (
self._get_node_instance_network_function_map(
context.plugin_session,
context.current_node['id'],
context.instance['id']))
if network_function_map:
network_function_id = network_function_map.network_function_id
self.nfp_notifier.policy_target_removed_notification(
context.plugin_context, network_function_id, policy_target)
self._wait_for_network_function_operation_completion(
context, network_function_id,
operation=nfp_constants.UPDATE)
def notify_chain_parameters_updated(self, context):
pass # We are not using the classifier specified in redirect Rule
def update_node_consumer_ptg_added(self, context, policy_target_group):
if context.current_profile['service_type'] == pconst.FIREWALL:
context._plugin_context = self._get_resource_owner_context(
context._plugin_context)
network_function_map = (
self._get_node_instance_network_function_map(
context.plugin_session,
context.current_node['id'],
context.instance['id']))
if network_function_map:
network_function_id = network_function_map.network_function_id
self.nfp_notifier.consumer_ptg_added_notification(
context.plugin_context,
network_function_id,
policy_target_group)
self._wait_for_network_function_operation_completion(
context, network_function_id,
operation=nfp_constants.UPDATE)
def update_node_consumer_ptg_removed(self, context, policy_target_group):
if context.current_profile['service_type'] == pconst.FIREWALL:
context._plugin_context = self._get_resource_owner_context(
context._plugin_context)
network_function_map = (
self._get_node_instance_network_function_map(
context.plugin_session,
context.current_node['id'],
context.instance['id']))
if network_function_map:
network_function_id = network_function_map.network_function_id
self.nfp_notifier.consumer_ptg_removed_notification(
context.plugin_context,
network_function_id,
policy_target_group)
self._wait_for_network_function_operation_completion(
context, network_function_id,
operation=nfp_constants.UPDATE)
def _wait_for_network_function_delete_completion(self, context,
network_function_id):
time_waited = 0
network_function = None
while time_waited < cfg.CONF.nfp_node_driver.service_delete_timeout:
network_function = self.nfp_notifier.get_network_function(
context.plugin_context, network_function_id)
if not network_function:
break
eventlet.sleep(5)
time_waited = time_waited + 5
if network_function:
LOG.error(_LE("Delete network function %(network_function)s "
"failed"),
{'network_function': network_function_id})
raise NodeInstanceDeleteFailed()
def _wait_for_network_function_operation_completion(self, context,
network_function_id,
operation):
time_waited = 0
network_function = None
timeout = cfg.CONF.nfp_node_driver.service_create_timeout
while time_waited < timeout:
network_function = self.nfp_notifier.get_network_function(
context.plugin_context, network_function_id)
if not network_function:
LOG.error(_LE("Failed to retrieve network function"))
eventlet.sleep(5)
time_waited = time_waited + 5
continue
else:
LOG.info(_LI("%(operation)s network function result: "
"%(network_function)s"),
{'network_function': network_function,
'operation': operation})
if (network_function['status'] == nfp_constants.ACTIVE or
network_function['status'] == nfp_constants.ERROR):
break
eventlet.sleep(5)
time_waited = time_waited + 5
LOG.info(_LI("%(operation)s Got network function result: "
"%(network_function)s"),
{'network_function': network_function,
'operation': operation})
if network_function['status'] != nfp_constants.ACTIVE:
LOG.error(_LE("%(operation)s network function"
"%(network_function)s "
"failed. Status: %(status)s"),
{'network_function': network_function_id,
'status': network_function['status'],
'operation': operation})
if operation.lower() == nfp_constants.CREATE:
raise NodeInstanceCreateFailed()
elif operation.lower() == nfp_constants.UPDATE:
raise NodeInstanceUpdateFailed()
def _is_service_target(self, policy_target):
if policy_target['name'] and (policy_target['name'].startswith(
plumber_base.SERVICE_TARGET_NAME_PREFIX) or
policy_target['name'].startswith('tscp_endpoint_service') or
policy_target['name'].startswith('vip_pt')):
return True
else:
return False
def _resource_owner_tenant_id(self):
user, pwd, tenant, auth_url = utils.get_keystone_creds()
keystoneclient = keyclient.Client(username=user, password=pwd,
auth_url=auth_url)
try:
tenant = keystoneclient.tenants.find(name=tenant)
return tenant.id
except k_exceptions.NotFound:
with excutils.save_and_reraise_exception(reraise=True):
LOG.error(_LE('No tenant with name %s exists.'), tenant)
except k_exceptions.NoUniqueMatch:
with excutils.save_and_reraise_exception(reraise=True):
LOG.error(_LE('Multiple tenants matches found for %s'), tenant)
def _get_resource_owner_context(self, plugin_context):
# REVISIT(AKASH) Need to revisit as this api is not needed
# with present scenarios
'''
if cfg.CONF.nfp_node_driver.is_service_admin_owned:
resource_owner_context = plugin_context.elevated()
resource_owner_context.tenant_id = self.resource_owner_tenant_id
user, pwd, ignore_tenant, auth_url = utils.get_keystone_creds()
keystoneclient = keyclient.Client(username=user, password=pwd,
auth_url=auth_url)
resource_owner_context.auth_token = keystoneclient.get_token(
self.resource_owner_tenant_id)
return resource_owner_context
else:
return plugin_context
'''
return plugin_context
def _update(self, context, network_function_id):
if (context.original_node['config'] != context.current_node['config']):
try:
self.nfp_notifier.update_network_function(
context=context.plugin_context,
network_function_id=network_function_id,
config=context.current_node['config'])
except Exception:
LOG.exception(_LE("Update Network service Failed for "
"network function: %(nf_id)s"),
{'nf_id': network_function_id})
else:
LOG.info(_LI("No action to take on update"))
def _get_service_targets(self, context):
service_type = context.current_profile['service_type']
provider_service_targets = []
consumer_service_targets = []
service_flavor_str = context.current_profile['service_flavor']
service_details = self._parse_service_flavor_string(service_flavor_str)
service_targets = context.get_service_targets()
# Bug with NCP. For create, its not setting service targets in context
if not service_targets:
service_targets = context.get_service_targets(update=True)
if not service_targets:
return {}
for service_target in service_targets:
if service_target.relationship == nfp_constants.CONSUMER:
consumer_service_targets.append(service_target)
elif service_target.relationship == nfp_constants.PROVIDER:
provider_service_targets.append(service_target)
LOG.debug("provider targets: %s consumer targets %s" % (
provider_service_targets, consumer_service_targets))
if (service_details['device_type'] != 'None' and (
not provider_service_targets or (service_type in
[pconst.FIREWALL, pconst.VPN] and not consumer_service_targets))):
LOG.error(_LE("Service Targets are not created for the Node "
"of service_type %(service_type)s"),
{'service_type': service_type})
raise Exception("Service Targets are not created for the Node")
service_target_info = {
'provider_ports': [],
'provider_subnet': None,
'provider_pts': [],
'provider_pt_objs': [],
'provider_ptg': [],
'consumer_ports': [],
'consumer_subnet': None,
'consumer_pts': [],
'consumer_pt_objs': [],
'consumer_ptg': []}
for service_target in provider_service_targets:
policy_target = context.gbp_plugin.get_policy_target(
context.plugin_context, service_target.policy_target_id)
policy_target_group = context.gbp_plugin.get_policy_target_group(
context.plugin_context,
policy_target['policy_target_group_id'])
port = context.core_plugin.get_port(
context.plugin_context, policy_target['port_id'])
port['ip_address'] = port['fixed_ips'][0]['ip_address']
subnet = context.core_plugin.get_subnet(
context.plugin_context, port['fixed_ips'][0]['subnet_id'])
service_target_info['provider_ports'].append(port)
service_target_info['provider_subnet'] = subnet
service_target_info['provider_pts'].append(policy_target['id'])
service_target_info['provider_pt_objs'].append(policy_target)
service_target_info['provider_ptg'].append(policy_target_group)
for service_target in consumer_service_targets:
policy_target = context.gbp_plugin.get_policy_target(
context.plugin_context, service_target.policy_target_id)
policy_target_group = context.gbp_plugin.get_policy_target_group(
context.plugin_context,
policy_target['policy_target_group_id'])
port = context.core_plugin.get_port(
context.plugin_context, policy_target['port_id'])
port['ip_address'] = port['fixed_ips'][0]['ip_address']
subnet = context.core_plugin.get_subnet(
context.plugin_context, port['fixed_ips'][0]['subnet_id'])
service_target_info['consumer_ports'].append(port)
service_target_info['consumer_subnet'] = subnet
service_target_info['consumer_pts'].append(policy_target['id'])
service_target_info['consumer_pt_objs'].append(policy_target)
service_target_info['consumer_ptg'].append(policy_target_group)
return service_target_info
# Needs a better algorithm
def _is_node_order_in_spec_supported(self, context):
current_specs = context.relevant_specs
service_type_list_in_chain = []
node_list = []
for spec in current_specs:
node_list.extend(spec['nodes'])
self.sc_node_count = len(node_list)
for node_id in node_list:
node_info = context.sc_plugin.get_servicechain_node(
context.plugin_context, node_id)
profile = context.sc_plugin.get_service_profile(
context.plugin_context, node_info['service_profile_id'])
service_type_list_in_chain.append(profile['service_type'])
if len(service_type_list_in_chain) != len(
set(service_type_list_in_chain)):
raise DuplicateServiceTypeInChain()
allowed_chain_combinations = [
[pconst.VPN],
[pconst.VPN, pconst.FIREWALL],
[pconst.VPN, pconst.FIREWALL, pconst.LOADBALANCER],
[pconst.VPN, pconst.FIREWALL, pconst.LOADBALANCERV2],
[pconst.FIREWALL],
[pconst.FIREWALL, pconst.LOADBALANCER],
[pconst.FIREWALL, pconst.LOADBALANCERV2],
[pconst.LOADBALANCER],
[pconst.LOADBALANCERV2]]
if service_type_list_in_chain not in allowed_chain_combinations:
raise InvalidNodeOrderInChain(
node_order=allowed_chain_combinations)
def _get_consumers_for_provider(self, context, provider):
'''
{
consuming_ptgs_details: [{'ptg': <>, 'subnets': <>}]
consuming_eps_details: []
}
'''
consuming_ptgs_details = []
consuming_eps_details = []
if not provider['provided_policy_rule_sets']:
return consuming_ptgs_details, consuming_eps_details
provided_prs_id = provider['provided_policy_rule_sets'][0]
provided_prs = context.gbp_plugin.get_policy_rule_set(
context.plugin_context, provided_prs_id)
consuming_ptg_ids = provided_prs['consuming_policy_target_groups']
consuming_ep_ids = provided_prs['consuming_external_policies']
consuming_ptgs = context.gbp_plugin.get_policy_target_groups(
context.plugin_context, filters={'id': consuming_ptg_ids})
consuming_eps_details = context.gbp_plugin.get_external_policies(
context.plugin_context, filters={'id': consuming_ep_ids})
for ptg in consuming_ptgs:
subnet_ids = ptg['subnets']
subnets = context.core_plugin.get_subnets(
context.plugin_context, filters={'id': subnet_ids})
consuming_ptgs_details.append({'ptg': ptg, 'subnets': subnets})
return consuming_ptgs_details, consuming_eps_details
def _create_network_function(self, context):
"""
nfp_create_nf_data :-
{'resource_owner_context': <>,
'service_chain_instance': <>,
'service_chain_node': <>,
'service_profile': <>,
'service_config': context.current_node.get('config'),
'provider': {'pt':<>, 'ptg':<>, 'port':<>, 'subnet':<>},
'consumer': {'pt':<>, 'ptg':<>, 'port':<>, 'subnet':<>},
'management': {'pt':<>, 'ptg':<>, 'port':<>, 'subnet':<>},
'management_ptg_id': <>,
'network_function_mode': nfp_constants.GBP_MODE,
'tenant_id': <>,
'consuming_ptgs_details': [],
'consuming_eps_details': []
}
"""
nfp_create_nf_data = {}
sc_instance = context.instance
service_targets = self._get_service_targets(context)
consuming_ptgs_details = []
consuming_eps_details = []
if service_targets:
consuming_ptgs_details, consuming_eps_details = \
self._get_consumers_for_provider(context,
service_targets['provider_ptg'][0])
if context.current_profile['service_type'] in [pconst.LOADBALANCER,
pconst.LOADBALANCERV2]:
config_param_values = sc_instance.get('config_param_values', {})
if config_param_values:
config_param_values = jsonutils.loads(config_param_values)
vip_ip = config_param_values.get('vip_ip')
if not vip_ip:
raise VipNspNotSetonProvider()
if service_targets:
for provider_port in service_targets['provider_ports']:
provider_port['allowed_address_pairs'] = [
{'ip_address': vip_ip}]
port = {
'port': provider_port
}
context.core_plugin.update_port(
context.plugin_context, provider_port['id'], port)
provider = {
'pt': service_targets.get('provider_pt_objs', [None])[0],
'ptg': service_targets.get('provider_ptg', [None])[0],
'port': service_targets.get('provider_ports', [None])[0],
'subnet': service_targets.get('provider_subnet', None),
'port_model': nfp_constants.GBP_PORT,
'port_classification': nfp_constants.PROVIDER}
consumer_pt = None
consumer_ptg = None
consumer_ports = None
if service_targets.get('consumer_pt_objs'):
consumer_pt = service_targets.get('consumer_pt_objs')[0]
if service_targets.get('consumer_ptg'):
consumer_ptg = service_targets.get('consumer_ptg')[0]
if service_targets.get('consumer_ports'):
consumer_ports = service_targets.get('consumer_ports')[0]
consumer = {
'pt': consumer_pt,
'ptg': consumer_ptg,
'port': consumer_ports,
'subnet': service_targets.get('consumer_subnet', None),
'port_model': nfp_constants.GBP_PORT,
'port_classification': nfp_constants.CONSUMER}
management = {
'pt': None,
'ptg': None,
'port': None,
'subnet': None,
'port_model': nfp_constants.GBP_NETWORK,
'port_classification': nfp_constants.MANAGEMENT}
nfp_create_nf_data = {
'resource_owner_context': context._plugin_context.to_dict(),
'service_chain_instance': sc_instance,
'service_chain_node': context.current_node,
'service_profile': context.current_profile,
'service_config': context.current_node.get('config'),
'provider': provider,
'consumer': consumer,
'management': management,
'management_ptg_id': sc_instance['management_ptg_id'],
'network_function_mode': nfp_constants.GBP_MODE,
'tenant_id': context.provider['tenant_id'],
'consuming_ptgs_details': consuming_ptgs_details,
'consuming_eps_details': consuming_eps_details}
return self.nfp_notifier.create_network_function(
context.plugin_context, network_function=nfp_create_nf_data)['id']
def _set_node_instance_network_function_map(
self, session, sc_node_id, sc_instance_id, network_function_id):
with session.begin(subtransactions=True):
sc_node_instance_ns_map = (
ServiceNodeInstanceNetworkFunctionMapping(
sc_node_id=sc_node_id,
sc_instance_id=sc_instance_id,
network_function_id=network_function_id))
session.add(sc_node_instance_ns_map)
def _get_node_instance_network_function_map(self, session, sc_node_id=None,
sc_instance_id=None):
try:
with session.begin(subtransactions=True):
query = session.query(
ServiceNodeInstanceNetworkFunctionMapping)
if sc_node_id:
query = query.filter_by(sc_node_id=sc_node_id)
if sc_instance_id:
query = query.filter_by(sc_instance_id=sc_instance_id)
return query.first()
except NoResultFound:
return None
def _delete_node_instance_network_function_map(self, session, sc_node_id,
sc_instance_id):
with session.begin(subtransactions=True):
sc_node_instance_ns_maps = (
session.query(ServiceNodeInstanceNetworkFunctionMapping).
filter_by(sc_node_id=sc_node_id).
filter_by(sc_instance_id=sc_instance_id).
all())
for sc_node_instance_ns_map in sc_node_instance_ns_maps:
session.delete(sc_node_instance_ns_map)

View File

@ -0,0 +1,752 @@
# 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 mock
from neutron.db import api as db_api
from neutron.db import model_base
from neutron.plugins.common import constants
from oslo_serialization import jsonutils
import webob
from gbpservice.neutron.services.servicechain.plugins.ncp import (
plugin as ncp_plugin)
from gbpservice.neutron.services.servicechain.plugins.ncp import config # noqa
from gbpservice.neutron.services.servicechain.plugins.ncp.node_drivers import (
nfp_node_driver as nfp_node_driver)
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)
from gbpservice.neutron.tests.unit.services.servicechain.ncp import (
test_ncp_plugin as test_ncp_plugin)
SERVICE_DELETE_TIMEOUT = 15
SVC_MANAGEMENT_PTG = 'foo'
class ServiceChainNCPTestPlugin(ncp_plugin.NodeCompositionPlugin):
supported_extension_aliases = ['servicechain'] + (
test_group_policy_db.UNSUPPORTED_REQUIRED_EXTS)
path_prefix = "/servicechain"
SC_PLUGIN_KLASS = (ServiceChainNCPTestPlugin.__module__ + '.' +
ServiceChainNCPTestPlugin.__name__)
CORE_PLUGIN = test_gp_driver.CORE_PLUGIN
GP_PLUGIN_KLASS = (
"gbpservice.neutron.services.grouppolicy.plugin.GroupPolicyPlugin"
)
class NFPNodeDriverTestCase(
test_base.TestGroupPolicyPluginGroupResources,
test_ncp_plugin.NodeCompositionPluginTestMixin):
DEFAULT_VPN_CONFIG_DICT = {
"heat_template_version": "2013-05-23",
"description": "Creates new vpn service",
"parameters": {
"RouterId": {
"type": "string", "description": "Router ID"
},
"Subnet": {
"type": "string", "description": "Subnet id"
},
"ClientAddressPoolCidr": {
"type": "string", "description": "Pool"
},
"ServiceDescription": {
"type": "string", "description": "fip;tunnel_local-cidr"
}
},
"resources": {
"SSLVPNConnection": {
"type": "OS::Neutron::SSLVPNConnection",
"properties": {
"credential_id": "",
"client_address_pool_cidr": {
"get_param": "ClientAddressPoolCidr"
},
"name": "vtun0",
"vpnservice_id": {
"get_resource": "VPNService"
},
"admin_state_up": 'true'
}
},
"VPNService": {
"type": "OS::Neutron::VPNService",
"properties": {
"router_id": {
"get_param": "RouterId"
},
"subnet_id": {
"get_param": "Subnet"
},
"admin_state_up": 'true',
"description": {
"get_param": "ServiceDescription"
},
"name": "VPNService"
}
}
}
}
DEFAULT_VPN_CONFIG = jsonutils.dumps(DEFAULT_VPN_CONFIG_DICT)
DEFAULT_LB_CONFIG_DICT = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"test_pool": {
"Type": "OS::Neutron::Pool",
"Properties": {
"admin_state_up": True,
"description": "Haproxy pool from teplate",
"lb_method": "ROUND_ROBIN",
"monitors": [{"Ref": "HttpHM"}],
"name": "Haproxy pool",
"protocol": "HTTP",
"subnet_id": {"Ref": "Subnet"},
"vip": {
"subnet": {"Ref": "192.168.100.0"},
"address": {"Ref": "192.168.100.2"},
"name": "Haproxy vip",
"protocol_port": 80,
"connection_limit": -1,
"admin_state_up": True,
"description": "Haproxy vip from template"
}
}
},
"test_lb": {
"Type": "OS::Neutron::LoadBalancer",
"Properties": {
"pool_id": {"Ref": "HaproxyPool"},
"protocol_port": 80
}
}
}
}
DEFAULT_LB_CONFIG = jsonutils.dumps(DEFAULT_LB_CONFIG_DICT)
DEFAULT_FW_CONFIG_DICT = {
"heat_template_version": "2013-05-23",
"resources": {
'test_fw': {
"type": "OS::Neutron::Firewall",
"properties": {
"admin_state_up": True,
"firewall_policy_id": {
"get_resource": "Firewall_policy"},
"name": "testFirewall",
"description": "test Firewall"
}
},
'test_fw_policy': {
"type": "OS::Neutron::FirewallPolicy",
"properties": {
"shared": False,
"description": "test firewall policy",
"name": "testFWPolicy",
"firewall_rules": [{
"get_resource": "Rule_1"}],
"audited": True
}
}
}
}
DEFAULT_FW_CONFIG = jsonutils.dumps(DEFAULT_FW_CONFIG_DICT)
SERVICE_PROFILE_VENDOR = 'NFP'
def _create_service_profile(self, **kwargs):
if not kwargs.get('insertion_mode'):
kwargs['insertion_mode'] = 'l3'
if not kwargs.get('service_flavor'):
if kwargs['service_type'] == 'LOADBALANCER':
kwargs['service_flavor'] = 'haproxy'
else:
kwargs['service_flavor'] = 'vyos'
return super(NFPNodeDriverTestCase, self)._create_service_profile(
**kwargs)
def setUp(self):
config.cfg.CONF.set_override('service_delete_timeout',
SERVICE_DELETE_TIMEOUT,
group='nfp_node_driver')
config.cfg.CONF.set_override(
'extension_drivers', ['proxy_group'], group='group_policy')
config.cfg.CONF.set_override('node_drivers', ['nfp_node_driver'],
group='node_composition_plugin')
config.cfg.CONF.set_override('node_plumber', 'stitching_plumber',
group='node_composition_plugin')
config.cfg.CONF.set_override('policy_drivers',
['implicit_policy', 'resource_mapping',
'chain_mapping'],
group='group_policy')
super(NFPNodeDriverTestCase, self).setUp(
core_plugin=CORE_PLUGIN,
gp_plugin=GP_PLUGIN_KLASS,
sc_plugin=SC_PLUGIN_KLASS)
engine = db_api.get_engine()
model_base.BASEV2.metadata.create_all(engine)
def test_manager_initialized(self):
mgr = self.plugin.driver_manager
self.assertIsInstance(mgr.ordered_drivers[0].obj,
nfp_node_driver.NFPNodeDriver)
for driver in mgr.ordered_drivers:
self.assertTrue(driver.obj.initialized)
def _nfp_create_profiled_servicechain_node(
self, service_type=constants.LOADBALANCER, shared_profile=False,
profile_tenant_id=None, profile_id=None,
service_flavor=None, **kwargs):
if not profile_id:
prof = self.create_service_profile(
service_type=service_type,
shared=shared_profile,
vendor=self.SERVICE_PROFILE_VENDOR,
insertion_mode='l3', service_flavor='haproxy',
tenant_id=profile_tenant_id or self._tenant_id)[
'service_profile']
else:
prof = self.get_service_profile(profile_id)
service_config = kwargs.get('config')
if not service_config or service_config == '{}':
if service_type == constants.FIREWALL:
kwargs['config'] = self.DEFAULT_FW_CONFIG
else:
kwargs['config'] = self.DEFAULT_LB_CONFIG
return self.create_servicechain_node(
service_profile_id=prof['id'], **kwargs)
def _create_simple_fw_service_chain(self, number_of_nodes=1,
service_type='FIREWALL'):
prof = self.create_service_profile(
service_type=service_type,
vendor=self.SERVICE_PROFILE_VENDOR,
insertion_mode='l3', service_flavor='vyos')['service_profile']
node_ids = []
for x in xrange(number_of_nodes):
node_ids.append(self.create_servicechain_node(
service_profile_id=prof['id'],
config=self.DEFAULT_FW_CONFIG,
expected_res_status=201)['servicechain_node']['id'])
return self._nfp_create_chain_with_nodes(node_ids)
def _nfp_create_chain_with_nodes(self, node_ids=None):
node_ids = node_ids or []
spec = self.create_servicechain_spec(
nodes=node_ids,
expected_res_status=201)['servicechain_spec']
prs = self._create_redirect_prs(spec['id'])['policy_rule_set']
provider = self.create_policy_target_group(
provided_policy_rule_sets={prs['id']: ''})['policy_target_group']
with mock.patch.object(nfp_node_driver.NFPClientApi,
"consumer_ptg_added_notification") as ptg_added:
consumer = self.create_policy_target_group(
consumed_policy_rule_sets={prs['id']: ''})[
'policy_target_group']
ptg_added.assert_called_once_with(mock.ANY,
mock.ANY, mock.ANY)
return provider, consumer, prs
def test_spec_parameters(self):
pass
def test_spec_ordering_list_servicechain_instances(self):
pass
class TestServiceChainInstance(NFPNodeDriverTestCase):
def test_node_create(self):
with mock.patch.object(nfp_node_driver.NFPClientApi,
"create_network_function") as create_nf:
with mock.patch.object(nfp_node_driver.NFPClientApi,
"get_network_function") as get_nf:
create_nf.return_value = {
'id': '126231632163'
}
get_nf.return_value = {
'id': '126231632163',
'status': 'ACTIVE'
}
self._create_simple_fw_service_chain()
create_nf.assert_called_once_with(
mock.ANY,
network_function=mock.ANY)
get_nf.assert_called_with(mock.ANY, mock.ANY)
def _test_node_update(self):
with mock.patch.object(nfp_node_driver.NFPClientApi,
"create_network_function") as create_nf:
with mock.patch.object(nfp_node_driver.NFPClientApi,
"get_network_function") as get_nf:
with mock.patch.object(nfp_node_driver.NFPClientApi,
"update_service_config") as update_svc_config:
create_nf.return_value = {
'id': '126231632163'
}
get_nf.return_value = {
'id': '126231632163',
'status': 'ACTIVE'
}
prof = self.create_service_profile(
service_type=constants.FIREWALL,
vendor=self.SERVICE_PROFILE_VENDOR,
insertion_mode='l3',
service_flavor='vyos')['service_profile']
self.create_policy_target_group(
name='foo')['policy_target_group']
node = self.create_servicechain_node(
service_profile_id=prof['id'],
config=self.DEFAULT_FW_CONFIG,
expected_res_status=201)['servicechain_node']
self._nfp_create_chain_with_nodes(node_ids=[node['id']])
self.update_servicechain_node(
node['id'],
name='newname',
expected_res_status=200)
create_nf.assert_called_once_with(
mock.ANY,
network_function=mock.ANY)
get_nf.assert_called_once_with(mock.ANY, mock.ANY)
update_svc_config.assert_called_once_with()
def test_node_delete(self):
with mock.patch.object(nfp_node_driver.NFPClientApi,
"create_network_function") as create_nf:
with mock.patch.object(nfp_node_driver.NFPClientApi,
'get_network_function') as get_nf:
get_nf.return_value = {
'id': '126231632163',
'status': 'ACTIVE'
}
create_nf.return_value = {
'id': '126231632163'
}
prof = self.create_service_profile(
service_type=constants.FIREWALL,
vendor=self.SERVICE_PROFILE_VENDOR,
insertion_mode='l3',
service_flavor='vyos')['service_profile']
node_id = self.create_servicechain_node(
service_profile_id=prof['id'],
config=self.DEFAULT_FW_CONFIG,
expected_res_status=201)['servicechain_node'][
'id']
spec = self.create_servicechain_spec(
nodes=[node_id],
expected_res_status=201)['servicechain_spec']
prs = self._create_redirect_prs(spec['id'])['policy_rule_set']
provider = self.create_policy_target_group(
provided_policy_rule_sets={prs['id']: ''})[
'policy_target_group']
create_nf.assert_called_once_with(
mock.ANY,
network_function=mock.ANY)
with mock.patch.object(nfp_node_driver.NFPClientApi,
"get_network_function") as get_nf:
with mock.patch.object(nfp_node_driver.NFPClientApi,
"delete_network_function") as delete_nf:
get_nf.return_value = None
self.delete_policy_target_group(
provider['id'], expected_res_status=204)
expected_plugin_context = mock.ANY
expected_network_function_id = mock.ANY
expected_plugin_context = mock.ANY
get_nf.assert_called_once_with(
expected_plugin_context,
expected_network_function_id)
delete_nf.assert_called_once_with(
context=mock.ANY,
network_function_id=mock.ANY)
def test_wait_for_network_function_delete_completion(self):
with mock.patch.object(nfp_node_driver.NFPClientApi,
"create_network_function") as create_nf:
with mock.patch.object(nfp_node_driver.NFPClientApi,
'get_network_function') as get_nf:
get_nf.return_value = {
'id': '126231632163',
'status': 'ACTIVE'
}
create_nf.return_value = {
'id': '126231632163'
}
prof = self.create_service_profile(
service_type=constants.FIREWALL,
vendor=self.SERVICE_PROFILE_VENDOR,
insertion_mode='l3',
service_flavor='vyos')['service_profile']
node_id = self.create_servicechain_node(
service_profile_id=prof['id'],
config=self.DEFAULT_FW_CONFIG,
expected_res_status=201)['servicechain_node'][
'id']
spec = self.create_servicechain_spec(
nodes=[node_id],
expected_res_status=201)['servicechain_spec']
prs = self._create_redirect_prs(spec['id'])['policy_rule_set']
provider = self.create_policy_target_group(
provided_policy_rule_sets={prs['id']: ''})[
'policy_target_group']
create_nf.assert_called_once_with(
mock.ANY,
network_function=mock.ANY)
with mock.patch.object(nfp_node_driver.NFPClientApi,
'delete_network_function') as delete_nf:
with mock.patch.object(nfp_node_driver.NFPClientApi,
'get_network_function') as get_nf:
delete_nf.return_value = None
get_nf.return_value = None
# Removing the PRSs will make the PTG deletable again
self.update_policy_target_group(
provider['id'],
provided_policy_rule_sets={},
expected_res_status=200)
self.delete_policy_target_group(provider['id'],
expected_res_status=204)
delete_nf.assert_called_once_with(context=mock.ANY,
network_function_id=mock.ANY)
get_nf.assert_called_once_with(mock.ANY, mock.ANY)
def _create_policy_target_port(self, policy_target_group_id):
pt = self.create_policy_target(
policy_target_group_id=policy_target_group_id)['policy_target']
req = self.new_show_request('ports', pt['port_id'], fmt=self.fmt)
port = self.deserialize(self.fmt,
req.get_response(self.api))['port']
return (pt, port)
def test_lb_node_create(self, consumer_external=False):
with mock.patch.object(nfp_node_driver.NFPClientApi,
"create_network_function") as create_nf:
with mock.patch.object(nfp_node_driver.NFPClientApi,
'get_network_function') as get_nf:
get_nf.return_value = {
'id': '126231632163',
'status': 'ACTIVE'
}
create_nf.return_value = {
'id': '126231632163'
}
node_id = self._nfp_create_profiled_servicechain_node(
service_type=constants.LOADBALANCER)['servicechain_node'][
'id']
spec = self.create_servicechain_spec(
nodes=[node_id],
expected_res_status=201)['servicechain_spec']
prs = self._create_redirect_prs(spec['id'])['policy_rule_set']
params = [{'type': 'ip_single', 'name': 'vip_ip',
'value': 'self_subnet'}]
nsp = self.create_network_service_policy(
network_service_params=params)
network_service_policy_id = nsp['network_service_policy']['id']
provider = self.create_policy_target_group(
network_service_policy_id=network_service_policy_id,
provided_policy_rule_sets={prs['id']: ''})[
'policy_target_group']
with mock.patch.object(nfp_node_driver.NFPClientApi,
"policy_target_added_notification") as pt_added:
# Verify notification issued for created PT in the provider
_, port = self._create_policy_target_port(provider['id'])
pt_added.assert_called_once_with(mock.ANY, mock.ANY,
mock.ANY)
if consumer_external:
self._create_external_policy(prs['id'])
else:
self.create_policy_target_group(
consumed_policy_rule_sets={prs['id']: ''})
create_nf.assert_called_once_with(
mock.ANY,
network_function=mock.ANY)
get_nf.assert_called_with(mock.ANY, mock.ANY)
def test_invalid_service_type_rejected(self):
node_used = self._nfp_create_profiled_servicechain_node(
service_type="test")['servicechain_node']
spec_used = self.create_servicechain_spec(
nodes=[node_used['id']])['servicechain_spec']
provider = self.create_policy_target_group()['policy_target_group']
classifier = self.create_policy_classifier()['policy_classifier']
res = self.create_servicechain_instance(
provider_ptg_id=provider['id'],
classifier_id=classifier['id'],
servicechain_specs=[spec_used['id']],
expected_res_status=webob.exc.HTTPBadRequest.code)
self.assertEqual('NoDriverAvailableForAction',
res['NeutronError']['type'])
def test_is_node_order_in_spec_supported(self):
lb_prof = self.create_service_profile(
service_type=constants.LOADBALANCER,
vendor=self.SERVICE_PROFILE_VENDOR,
insertion_mode='l3',
service_flavor='haproxy')['service_profile']
vpn_prof = self.create_service_profile(
service_type=constants.VPN,
vendor=self.SERVICE_PROFILE_VENDOR,
insertion_mode='l3',
service_flavor='vyos')['service_profile']
vpn_node = self.create_servicechain_node(
service_profile_id=vpn_prof['id'],
config=self.DEFAULT_VPN_CONFIG,
expected_res_status=201)['servicechain_node']
lb_node = self.create_servicechain_node(
service_profile_id=lb_prof['id'],
config=self.DEFAULT_LB_CONFIG,
expected_res_status=201)['servicechain_node']
node_ids = [lb_node['id'], vpn_node['id']]
spec = self.create_servicechain_spec(
nodes=node_ids,
expected_res_status=201)['servicechain_spec']
provider = self.create_policy_target_group()['policy_target_group']
classifier = self.create_policy_classifier()['policy_classifier']
res = self.create_servicechain_instance(
provider_ptg_id=provider['id'],
classifier_id=classifier['id'],
servicechain_specs=[spec['id']],
expected_res_status=webob.exc.HTTPBadRequest.code)
self.assertEqual('NoDriverAvailableForAction',
res['NeutronError']['type'])
def test_validate_update(self):
with mock.patch.object(nfp_node_driver.NFPClientApi,
"create_network_function") as create_nf:
with mock.patch.object(nfp_node_driver.NFPClientApi,
"get_network_function") as get_nf:
create_nf.return_value = {
'id': '126231632163'
}
get_nf.return_value = {
'id': '126231632163',
'status': 'ACTIVE'
}
fw_prof = self.create_service_profile(
service_type=constants.FIREWALL,
vendor=self.SERVICE_PROFILE_VENDOR,
insertion_mode='l3',
service_flavor='vyos')['service_profile']
fw_node = self.create_servicechain_node(
service_profile_id=fw_prof['id'],
config=self.DEFAULT_FW_CONFIG,
expected_res_status=201)['servicechain_node']
node_ids = [fw_node['id']]
spec = self.create_servicechain_spec(
nodes=node_ids,
expected_res_status=201)['servicechain_spec']
provider = self.create_policy_target_group()[
'policy_target_group']
classifier = self.create_policy_classifier()[
'policy_classifier']
servicechain_instance = self.create_servicechain_instance(
provider_ptg_id=provider['id'],
classifier_id=classifier['id'],
servicechain_specs=[spec['id']])[
'servicechain_instance']
fw_prof = self.create_service_profile(
service_type='test',
vendor=self.SERVICE_PROFILE_VENDOR,
insertion_mode='l3',
service_flavor='vyos')['service_profile']
fw_node = self.create_servicechain_node(
service_profile_id=fw_prof['id'],
config=self.DEFAULT_FW_CONFIG,
expected_res_status=201)['servicechain_node']
node_ids = [fw_node['id']]
spec = self.create_servicechain_spec(
nodes=node_ids,
expected_res_status=201)['servicechain_spec']
create_nf.assert_called_once_with(
mock.ANY,
network_function=mock.ANY)
with mock.patch.object(nfp_node_driver.NFPClientApi,
"get_network_function") as get_nf:
with mock.patch.object(nfp_node_driver.NFPClientApi,
"delete_network_function") as delete_nf:
get_nf.return_value = None
res = self.update_servicechain_instance(
servicechain_instance['id'],
servicechain_specs=[spec['id']],
expected_res_status=webob.exc.HTTPBadRequest.code)
get_nf.assert_called_once_with(mock.ANY, mock.ANY)
delete_nf.assert_called_once_with(context=mock.ANY,
network_function_id=mock.ANY)
self.assertEqual('NoDriverAvailableForAction',
res['NeutronError']['type'])
def test_update_node_consumer_ptg_added(self):
with mock.patch.object(nfp_node_driver.NFPClientApi,
"create_network_function") as create_nf:
with mock.patch.object(nfp_node_driver.NFPClientApi,
'get_network_function') as get_nf:
get_nf.return_value = {
'id': '126231632163',
'status': 'ACTIVE'
}
create_nf.return_value = {
'id': '126231632163'
}
prof = self.create_service_profile(
service_type=constants.FIREWALL,
vendor=self.SERVICE_PROFILE_VENDOR,
insertion_mode='l3',
service_flavor='vyos')['service_profile']
node_id = self.create_servicechain_node(
service_profile_id=prof['id'],
config=self.DEFAULT_FW_CONFIG,
expected_res_status=201)['servicechain_node'][
'id']
spec = self.create_servicechain_spec(
nodes=[node_id],
expected_res_status=201)['servicechain_spec']
prs = self._create_redirect_prs(spec['id'])['policy_rule_set']
self.create_policy_target_group(
provided_policy_rule_sets={prs['id']: ''})[
'policy_target_group']
create_nf.assert_called_once_with(
mock.ANY,
network_function=mock.ANY)
get_nf.assert_called_once_with(mock.ANY, mock.ANY)
with mock.patch.object(nfp_node_driver.NFPClientApi,
"consumer_ptg_added_notification") as ptg_added:
self.create_policy_target_group(
consumed_policy_rule_sets={prs['id']: ''})[
'policy_target_group']
ptg_added.assert_called_once_with(mock.ANY,
mock.ANY, mock.ANY)
def _test_update_node_consumer_ptg_removed(self):
with mock.patch.object(nfp_node_driver.NFPClientApi,
"create_network_function") as create_nf:
with mock.patch.object(nfp_node_driver.NFPClientApi,
'get_network_function') as get_nf:
get_nf.return_value = {
'id': '126231632163',
'status': 'ACTIVE'
}
create_nf.return_value = {
'id': '126231632163'
}
prof = self.create_service_profile(
service_type=constants.FIREWALL,
vendor=self.SERVICE_PROFILE_VENDOR,
insertion_mode='l3',
service_flavor='vyos')['service_profile']
node_id = self.create_servicechain_node(
service_profile_id=prof['id'],
config=self.DEFAULT_FW_CONFIG,
expected_res_status=201)['servicechain_node'][
'id']
spec = self.create_servicechain_spec(
nodes=[node_id],
expected_res_status=201)['servicechain_spec']
prs = self._create_redirect_prs(spec['id'])['policy_rule_set']
self.create_policy_target_group(
provided_policy_rule_sets={prs['id']: ''})[
'policy_target_group']
with mock.patch.object(nfp_node_driver.NFPClientApi,
"consumer_ptg_added_notification") as ptg_added:
consumer = self.create_policy_target_group(
consumed_policy_rule_sets={prs['id']: ''})[
'policy_target_group']
ptg_added.assert_called_once_with(mock.ANY, mock.ANY,
mock.ANY)
create_nf.assert_called_once_with(
mock.ANY,
network_function=mock.ANY)
get_nf.assert_called_once_with(mock.ANY, mock.ANY)
with mock.patch.object(nfp_node_driver.NFPClientApi,
"consumer_ptg_removed_notification") as ptg_removed:
self.delete_policy_target_group(
consumer['id'], expected_res_status=204)
ptg_removed.assert_called_once_with(mock.ANY, mock.ANY, mock.ANY)
def test_policy_target_add_remove(self):
prof = self._create_service_profile(
service_type='LOADBALANCER',
vendor=self.SERVICE_PROFILE_VENDOR,
insertion_mode='l3', service_flavor='haproxy')['service_profile']
node = self.create_servicechain_node(
service_profile_id=prof['id'],
config=self.DEFAULT_LB_CONFIG,
expected_res_status=201)['servicechain_node']
spec = self.create_servicechain_spec(
nodes=[node['id']],
expected_res_status=201)['servicechain_spec']
prs = self._create_redirect_prs(spec['id'])['policy_rule_set']
with mock.patch.object(nfp_node_driver.NFPClientApi,
"create_network_function") as create_nf:
with mock.patch.object(nfp_node_driver.NFPClientApi,
'get_network_function') as get_nf:
get_nf.return_value = {
'id': '126231632163',
'status': 'ACTIVE'
}
create_nf.return_value = {
'id': '126231632163'
}
params = [{'type': 'ip_single', 'name': 'vip_ip',
'value': 'self_subnet'}]
nsp = self.create_network_service_policy(
network_service_params=params)
network_service_policy_id = nsp['network_service_policy'][
'id']
provider = self.create_policy_target_group(
network_service_policy_id=network_service_policy_id,
provided_policy_rule_sets={prs['id']: ''})[
'policy_target_group']
self.create_policy_target_group(
consumed_policy_rule_sets={prs['id']: ''})
with mock.patch.object(nfp_node_driver.NFPClientApi,
"policy_target_added_notification") as pt_added:
# Verify notification issued for created PT in the provider
pt = self.create_policy_target(
policy_target_group_id=provider['id'])[
'policy_target']
create_nf.assert_called_once_with(
mock.ANY,
network_function=mock.ANY)
get_nf.assert_called_with(mock.ANY, mock.ANY)
pt_added.assert_called_once_with(mock.ANY, mock.ANY,
mock.ANY)
# Verify notification issued for deleted PT in the provider
with mock.patch.object(nfp_node_driver.NFPClientApi,
"policy_target_removed_notification") as pt_removed:
self.delete_policy_target(pt['id'])
pt_removed.assert_called_once_with(mock.ANY, mock.ANY, mock.ANY)

View File

@ -28,6 +28,12 @@ PROVIDER = "provider"
CONSUMER = "consumer"
MANAGEMENT = "management"
MONITOR = "monitoring"
GATEWAY_TYPE = "gateway"
ENDPOINT_TYPE = "endpoint"
CREATE = "create"
UPDATE = "update"
DELETE = "delete"
ACTIVE_PORT = "ACTIVE"
STANDBY_PORT = "STANDBY"
@ -66,3 +72,9 @@ HEALTHMONITOR_RESOURCE = 'healthmonitor'
INTERFACE_RESOURCE = 'interfaces'
ROUTES_RESOURCE = 'routes'
MANAGEMENT_INTERFACE_NAME = 'mgmt_interface'
VYOS_VENDOR = 'vyos'
HAPROXY_VENDOR = 'haproxy'
HAPROXY_LBAASV2 = 'haproxy_lbaasv2'
NFP_VENDOR = 'nfp'
L3_INSERTION_MODE = "l3"

View File

@ -67,6 +67,7 @@ gbpservice.neutron.servicechain.servicechain_drivers =
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
nfp_node_driver = gbpservice.neutron.services.servicechain.plugins.ncp.node_drivers.nfp_node_driver:NFPNodeDriver
gbpservice.neutron.servicechain.ncp_plumbers =
dummy_plumber = gbpservice.neutron.services.servicechain.plugins.ncp.node_plumbers.dummy_plumber:NoopPlumber
stitching_plumber = gbpservice.neutron.services.servicechain.plugins.ncp.node_plumbers.traffic_stitching_plumber:TrafficStitchingPlumber