VNFFG: neutron networking-sfc driver and plugin fixes
Implements: blueprint tacker-vnffg This patch implements the networking-sfc driver for VNFFG SFC driver and necessary plugin side changes. Change-Id: I531db5c65d7ed3b1adeeb30606e067aa600a957c Signed-off-by: Tim Rozet <trozet@redhat.com> Co-Authored-By: Sridhar Ramaswamy <srics.r@gmail.com>
This commit is contained in:
parent
d3a2fbc861
commit
6f71a86167
|
@ -51,6 +51,7 @@ FIXED_RANGE=${FIXED_RANGE:-15.0.0.0/24}
|
||||||
|
|
||||||
|
|
||||||
enable_plugin tacker https://git.openstack.org/openstack/tacker master
|
enable_plugin tacker https://git.openstack.org/openstack/tacker master
|
||||||
|
enable_plugin networking-sfc git://git.openstack.org/openstack/networking-sfc master
|
||||||
|
|
||||||
enable_service n-novnc
|
enable_service n-novnc
|
||||||
enable_service n-cauth
|
enable_service n-cauth
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
tosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0
|
||||||
|
|
||||||
|
description: Demo example
|
||||||
|
|
||||||
|
metadata:
|
||||||
|
template_name: sample-tosca-vnfd1
|
||||||
|
|
||||||
|
topology_template:
|
||||||
|
node_templates:
|
||||||
|
VDU1:
|
||||||
|
type: tosca.nodes.nfv.VDU.Tacker
|
||||||
|
capabilities:
|
||||||
|
nfv_compute:
|
||||||
|
properties:
|
||||||
|
num_cpus: 1
|
||||||
|
mem_size: 512 MB
|
||||||
|
disk_size: 1 GB
|
||||||
|
properties:
|
||||||
|
image: cirros-0.3.4-x86_64-uec
|
||||||
|
availability_zone: nova
|
||||||
|
mgmt_driver: noop
|
||||||
|
config: |
|
||||||
|
param0: key1
|
||||||
|
param1: key2
|
||||||
|
|
||||||
|
CP11:
|
||||||
|
type: tosca.nodes.nfv.CP.Tacker
|
||||||
|
properties:
|
||||||
|
management: true
|
||||||
|
order: 0
|
||||||
|
anti_spoofing_protection: false
|
||||||
|
requirements:
|
||||||
|
- virtualLink:
|
||||||
|
node: VL11
|
||||||
|
- virtualBinding:
|
||||||
|
node: VDU1
|
||||||
|
|
||||||
|
CP12:
|
||||||
|
type: tosca.nodes.nfv.CP.Tacker
|
||||||
|
properties:
|
||||||
|
order: 1
|
||||||
|
anti_spoofing_protection: false
|
||||||
|
requirements:
|
||||||
|
- virtualLink:
|
||||||
|
node: VL12
|
||||||
|
- virtualBinding:
|
||||||
|
node: VDU1
|
||||||
|
|
||||||
|
CP13:
|
||||||
|
type: tosca.nodes.nfv.CP.Tacker
|
||||||
|
properties:
|
||||||
|
order: 2
|
||||||
|
anti_spoofing_protection: false
|
||||||
|
requirements:
|
||||||
|
- virtualLink:
|
||||||
|
node: VL13
|
||||||
|
- virtualBinding:
|
||||||
|
node: VDU1
|
||||||
|
|
||||||
|
VL11:
|
||||||
|
type: tosca.nodes.nfv.VL
|
||||||
|
properties:
|
||||||
|
network_name: net_mgmt
|
||||||
|
vendor: Tacker
|
||||||
|
|
||||||
|
VL12:
|
||||||
|
type: tosca.nodes.nfv.VL
|
||||||
|
properties:
|
||||||
|
network_name: net0
|
||||||
|
vendor: Tacker
|
||||||
|
|
||||||
|
VL13:
|
||||||
|
type: tosca.nodes.nfv.VL
|
||||||
|
properties:
|
||||||
|
network_name: net1
|
||||||
|
vendor: Tacker
|
|
@ -0,0 +1,76 @@
|
||||||
|
tosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0
|
||||||
|
|
||||||
|
description: Demo example
|
||||||
|
|
||||||
|
metadata:
|
||||||
|
template_name: sample-tosca-vnfd1
|
||||||
|
|
||||||
|
topology_template:
|
||||||
|
node_templates:
|
||||||
|
VDU1:
|
||||||
|
type: tosca.nodes.nfv.VDU.Tacker
|
||||||
|
capabilities:
|
||||||
|
nfv_compute:
|
||||||
|
properties:
|
||||||
|
num_cpus: 1
|
||||||
|
mem_size: 512 MB
|
||||||
|
disk_size: 1 GB
|
||||||
|
properties:
|
||||||
|
image: cirros-0.3.4-x86_64-uec
|
||||||
|
availability_zone: nova
|
||||||
|
mgmt_driver: noop
|
||||||
|
config: |
|
||||||
|
param0: key1
|
||||||
|
param1: key2
|
||||||
|
|
||||||
|
CP21:
|
||||||
|
type: tosca.nodes.nfv.CP.Tacker
|
||||||
|
properties:
|
||||||
|
management: true
|
||||||
|
order: 0
|
||||||
|
anti_spoofing_protection: false
|
||||||
|
requirements:
|
||||||
|
- virtualLink:
|
||||||
|
node: VL21
|
||||||
|
- virtualBinding:
|
||||||
|
node: VDU1
|
||||||
|
|
||||||
|
CP22:
|
||||||
|
type: tosca.nodes.nfv.CP.Tacker
|
||||||
|
properties:
|
||||||
|
order: 1
|
||||||
|
anti_spoofing_protection: false
|
||||||
|
requirements:
|
||||||
|
- virtualLink:
|
||||||
|
node: VL22
|
||||||
|
- virtualBinding:
|
||||||
|
node: VDU1
|
||||||
|
|
||||||
|
CP23:
|
||||||
|
type: tosca.nodes.nfv.CP.Tacker
|
||||||
|
properties:
|
||||||
|
order: 2
|
||||||
|
anti_spoofing_protection: false
|
||||||
|
requirements:
|
||||||
|
- virtualLink:
|
||||||
|
node: VL23
|
||||||
|
- virtualBinding:
|
||||||
|
node: VDU1
|
||||||
|
|
||||||
|
VL21:
|
||||||
|
type: tosca.nodes.nfv.VL
|
||||||
|
properties:
|
||||||
|
network_name: net_mgmt
|
||||||
|
vendor: Tacker
|
||||||
|
|
||||||
|
VL22:
|
||||||
|
type: tosca.nodes.nfv.VL
|
||||||
|
properties:
|
||||||
|
network_name: net0
|
||||||
|
vendor: Tacker
|
||||||
|
|
||||||
|
VL23:
|
||||||
|
type: tosca.nodes.nfv.VL
|
||||||
|
properties:
|
||||||
|
network_name: net1
|
||||||
|
vendor: Tacker
|
|
@ -0,0 +1,39 @@
|
||||||
|
tosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0
|
||||||
|
|
||||||
|
description: Sample VNFFG template
|
||||||
|
|
||||||
|
topology_template:
|
||||||
|
description: Sample VNFFG template
|
||||||
|
|
||||||
|
node_templates:
|
||||||
|
|
||||||
|
Forwarding_path1:
|
||||||
|
type: tosca.nodes.nfv.FP.Tacker
|
||||||
|
description: creates path (CP12->CP22)
|
||||||
|
properties:
|
||||||
|
id: 51
|
||||||
|
policy:
|
||||||
|
type: ACL
|
||||||
|
criteria:
|
||||||
|
- network_src_port_id: 640dfd77-c92b-45a3-b8fc-22712de480e1
|
||||||
|
- destination_port_range: 80-1024
|
||||||
|
- ip_proto: 6
|
||||||
|
- ip_dst_prefix: 192.168.1.2/24
|
||||||
|
path:
|
||||||
|
- forwarder: VNFD1
|
||||||
|
capability: CP12
|
||||||
|
- forwarder: VNFD2
|
||||||
|
capability: CP22
|
||||||
|
|
||||||
|
groups:
|
||||||
|
VNFFG1:
|
||||||
|
type: tosca.groups.nfv.VNFFG
|
||||||
|
description: HTTP to Corporate Net
|
||||||
|
properties:
|
||||||
|
vendor: tacker
|
||||||
|
version: 1.0
|
||||||
|
number_of_endpoints: 5
|
||||||
|
dependent_virtual_link: [VL12,VL22]
|
||||||
|
connection_point: [CP12,CP22]
|
||||||
|
constituent_vnfs: [VNFD1,VNFD2]
|
||||||
|
members: [Forwarding_path1]
|
|
@ -17,10 +17,12 @@
|
||||||
import os
|
import os
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
from keystoneauth1 import identity
|
||||||
|
from keystoneauth1 import session
|
||||||
from keystoneclient.auth.identity import v2
|
from keystoneclient.auth.identity import v2
|
||||||
from keystoneclient.auth.identity import v3
|
from keystoneclient.auth.identity import v3
|
||||||
from keystoneclient import exceptions
|
from keystoneclient import exceptions
|
||||||
from keystoneclient import session
|
from neutronclient.common import exceptions as nc_exceptions
|
||||||
from neutronclient.v2_0 import client as neutron_client
|
from neutronclient.v2_0 import client as neutron_client
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
@ -30,6 +32,7 @@ from tacker.agent.linux import utils as linux_utils
|
||||||
from tacker.common import log
|
from tacker.common import log
|
||||||
from tacker.extensions import nfvo
|
from tacker.extensions import nfvo
|
||||||
from tacker.nfvo.drivers.vim import abstract_vim_driver
|
from tacker.nfvo.drivers.vim import abstract_vim_driver
|
||||||
|
from tacker.nfvo.drivers.vnffg import abstract_vnffg_driver
|
||||||
from tacker.vnfm import keystone
|
from tacker.vnfm import keystone
|
||||||
|
|
||||||
|
|
||||||
|
@ -56,12 +59,27 @@ _VALID_RESOURCE_TYPES = {'network': {'client': neutron_client.Client,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FC_MAP = {'name': 'name',
|
||||||
|
'description': 'description',
|
||||||
|
'eth_type': 'ethertype',
|
||||||
|
'ip_src_prefix': 'source_ip_prefix',
|
||||||
|
'ip_dst_prefix': 'destination_ip_prefix',
|
||||||
|
'source_port_min': 'source_port_range_min',
|
||||||
|
'source_port_max': 'source_port_range_max',
|
||||||
|
'destination_port_min': 'destination_port_range_min',
|
||||||
|
'destination_port_max': 'destination_port_range_max',
|
||||||
|
'network_src_port_id': 'logical_source_port',
|
||||||
|
'network_dst_port_id': 'logical_destination_port'}
|
||||||
|
|
||||||
|
CONNECTION_POINT = 'connection_points'
|
||||||
|
|
||||||
|
|
||||||
def config_opts():
|
def config_opts():
|
||||||
return [('vim_keys', OPTS), ('vim_monitor', OPENSTACK_OPTS)]
|
return [('vim_keys', OPTS), ('vim_monitor', OPENSTACK_OPTS)]
|
||||||
|
|
||||||
|
|
||||||
class OpenStack_Driver(abstract_vim_driver.VimAbstractDriver):
|
class OpenStack_Driver(abstract_vim_driver.VimAbstractDriver,
|
||||||
|
abstract_vnffg_driver.VnffgAbstractDriver):
|
||||||
"""Driver for OpenStack VIM
|
"""Driver for OpenStack VIM
|
||||||
|
|
||||||
OpenStack driver handles interactions with local as well as
|
OpenStack driver handles interactions with local as well as
|
||||||
|
@ -274,3 +292,263 @@ class OpenStack_Driver(abstract_vim_driver.VimAbstractDriver):
|
||||||
auth_plugin = self._get_auth_plugin(keystone_version, **auth_cred)
|
auth_plugin = self._get_auth_plugin(keystone_version, **auth_cred)
|
||||||
sess = session.Session(auth=auth_plugin)
|
sess = session.Session(auth=auth_plugin)
|
||||||
return client_type(session=sess)
|
return client_type(session=sess)
|
||||||
|
|
||||||
|
def create_flow_classifier(self, name, fc, symmetrical=False,
|
||||||
|
auth_attr=None):
|
||||||
|
def _translate_ip_protocol(ip_proto):
|
||||||
|
if ip_proto == '1':
|
||||||
|
return 'icmp'
|
||||||
|
elif ip_proto == '6':
|
||||||
|
return 'tcp'
|
||||||
|
elif ip_proto == '17':
|
||||||
|
return 'udp'
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if not auth_attr:
|
||||||
|
LOG.warning(_("auth information required for n-sfc driver"))
|
||||||
|
return None
|
||||||
|
|
||||||
|
if symmetrical:
|
||||||
|
LOG.warning(_("n-sfc driver does not support symmetrical"))
|
||||||
|
raise NotImplementedError('symmetrical chain not supported')
|
||||||
|
LOG.debug(_('fc passed is %s'), fc)
|
||||||
|
sfc_classifier_params = {}
|
||||||
|
for field in fc:
|
||||||
|
if field in FC_MAP:
|
||||||
|
sfc_classifier_params[FC_MAP[field]] = fc[field]
|
||||||
|
elif field == 'ip_proto':
|
||||||
|
protocol = _translate_ip_protocol(str(fc[field]))
|
||||||
|
if not protocol:
|
||||||
|
raise ValueError('protocol %s not supported' % fc[field])
|
||||||
|
sfc_classifier_params['protocol'] = protocol
|
||||||
|
else:
|
||||||
|
LOG.warning(_("flow classifier %s not supported by "
|
||||||
|
"networking-sfc driver"), field)
|
||||||
|
|
||||||
|
LOG.debug(_('sfc_classifier_params is %s'), sfc_classifier_params)
|
||||||
|
if len(sfc_classifier_params) > 0:
|
||||||
|
neutronclient_ = NeutronClient(auth_attr)
|
||||||
|
|
||||||
|
fc_id = neutronclient_.flow_classifier_create(
|
||||||
|
sfc_classifier_params)
|
||||||
|
return fc_id
|
||||||
|
|
||||||
|
raise ValueError('empty match field for input flow classifier')
|
||||||
|
|
||||||
|
def create_chain(self, name, fc_id, vnfs, symmetrical=False,
|
||||||
|
auth_attr=None):
|
||||||
|
if not auth_attr:
|
||||||
|
LOG.warning(_("auth information required for n-sfc driver"))
|
||||||
|
return None
|
||||||
|
|
||||||
|
if symmetrical:
|
||||||
|
LOG.warning(_("n-sfc driver does not support symmetrical"))
|
||||||
|
raise NotImplementedError('symmetrical chain not supported')
|
||||||
|
|
||||||
|
neutronclient_ = NeutronClient(auth_attr)
|
||||||
|
port_pair_group_list = []
|
||||||
|
for vnf in vnfs:
|
||||||
|
# TODO(s3wong): once scaling is in place and VNFFG supports it
|
||||||
|
# that model needs to be implemented to concatenate all
|
||||||
|
# port-pairs into the port-pair-group
|
||||||
|
# port pair group could include port-pairs from different VNFs
|
||||||
|
port_pair_group = {}
|
||||||
|
port_pair_group['name'] = vnf['name'] + '-port-pair-group'
|
||||||
|
port_pair_group['description'] = \
|
||||||
|
'port pair group for %s' % vnf['name']
|
||||||
|
port_pair_group['port_pairs'] = []
|
||||||
|
if CONNECTION_POINT not in vnf:
|
||||||
|
LOG.warning(_("Chain creation failed due to missing "
|
||||||
|
"connection point info in VNF "
|
||||||
|
"%(vnfname)s"), {'vnfname': vnf['name']})
|
||||||
|
return None
|
||||||
|
cp_list = vnf[CONNECTION_POINT]
|
||||||
|
num_cps = len(cp_list)
|
||||||
|
if num_cps != 1 and num_cps != 2:
|
||||||
|
LOG.warning(_("Chain creation failed due to wrong number of "
|
||||||
|
"connection points: expected [1 | 2], got "
|
||||||
|
"%(cps)d"), {'cps': num_cps})
|
||||||
|
return None
|
||||||
|
port_pair = {}
|
||||||
|
port_pair['name'] = vnf['name'] + '-connection-points'
|
||||||
|
port_pair['description'] = 'port pair for %s' % vnf['name']
|
||||||
|
if num_cps == 1:
|
||||||
|
port_pair['ingress'] = cp_list[0]
|
||||||
|
port_pair['egress'] = cp_list[0]
|
||||||
|
else:
|
||||||
|
port_pair['ingress'] = cp_list[0]
|
||||||
|
port_pair['egress'] = cp_list[1]
|
||||||
|
port_pair_id = neutronclient_.port_pair_create(port_pair)
|
||||||
|
if not port_pair_id:
|
||||||
|
LOG.warning(_("Chain creation failed due to port pair creation"
|
||||||
|
" failed for vnf %(vnf)s"), {'vnf': vnf['name']})
|
||||||
|
return None
|
||||||
|
port_pair_group['port_pairs'].append(port_pair_id)
|
||||||
|
port_pair_group_id = \
|
||||||
|
neutronclient_.port_pair_group_create(port_pair_group)
|
||||||
|
if not port_pair_group_id:
|
||||||
|
LOG.warning(_("Chain creation failed due to port pair group "
|
||||||
|
"creation failed for vnf "
|
||||||
|
"%(vnf)s"), {'vnf': vnf['name']})
|
||||||
|
return None
|
||||||
|
port_pair_group_list.append(port_pair_group_id)
|
||||||
|
|
||||||
|
# TODO(s3wong): should the chain name be given as a parameter?
|
||||||
|
port_chain = {}
|
||||||
|
port_chain['name'] = name + '-port-chain'
|
||||||
|
port_chain['description'] = 'port-chain for Tacker VNFFG'
|
||||||
|
port_chain['port_pair_groups'] = port_pair_group_list
|
||||||
|
port_chain['flow_classifiers'] = [fc_id]
|
||||||
|
return neutronclient_.port_chain_create(port_chain)
|
||||||
|
|
||||||
|
def update_chain(self, chain_id, fc_ids, vnfs,
|
||||||
|
symmetrical=False, auth_attr=None):
|
||||||
|
# TODO(s3wong): chain can be updated either for
|
||||||
|
# the list of fc and/or list of port-pair-group
|
||||||
|
# since n-sfc driver does NOT track the ppg id
|
||||||
|
# it will look it up (or reconstruct) from
|
||||||
|
# networking-sfc DB --- but the caveat is that
|
||||||
|
# the VNF name MUST be unique
|
||||||
|
LOG.warning(_("n-sfc driver does not support sf chain update"))
|
||||||
|
raise NotImplementedError('sf chain update not supported')
|
||||||
|
|
||||||
|
def delete_chain(self, chain_id, auth_attr=None):
|
||||||
|
if not auth_attr:
|
||||||
|
LOG.warning(_("auth information required for n-sfc driver"))
|
||||||
|
return None
|
||||||
|
|
||||||
|
neutronclient_ = NeutronClient(auth_attr)
|
||||||
|
neutronclient_.port_chain_delete(chain_id)
|
||||||
|
|
||||||
|
def update_flow_classifier(self, fc_id, fc,
|
||||||
|
symmetrical=False, auth_attr=None):
|
||||||
|
if not auth_attr:
|
||||||
|
LOG.warning(_("auth information required for n-sfc driver"))
|
||||||
|
return None
|
||||||
|
|
||||||
|
if symmetrical:
|
||||||
|
LOG.warning(_("n-sfc driver does not support symmetrical"))
|
||||||
|
raise NotImplementedError('symmetrical chain not supported')
|
||||||
|
|
||||||
|
# for now, the only parameters allowed for flow-classifier-update
|
||||||
|
# is 'name' and/or 'description'
|
||||||
|
|
||||||
|
sfc_classifier_params = {}
|
||||||
|
sfc_classifier_params['name'] = fc['name']
|
||||||
|
sfc_classifier_params['description'] = fc['description']
|
||||||
|
|
||||||
|
LOG.debug(_('sfc_classifier_params is %s'), sfc_classifier_params)
|
||||||
|
|
||||||
|
neutronclient_ = NeutronClient(auth_attr)
|
||||||
|
return neutronclient_.flow_classifier_update(fc_id,
|
||||||
|
sfc_classifier_params)
|
||||||
|
|
||||||
|
def delete_flow_classifier(self, fc_id, auth_attr=None):
|
||||||
|
if not auth_attr:
|
||||||
|
LOG.warning(_("auth information required for n-sfc driver"))
|
||||||
|
raise EnvironmentError('auth attribute required for'
|
||||||
|
' networking-sfc driver')
|
||||||
|
|
||||||
|
neutronclient_ = NeutronClient(auth_attr)
|
||||||
|
neutronclient_.flow_classifier_delete(fc_id)
|
||||||
|
|
||||||
|
|
||||||
|
class NeutronClient(object):
|
||||||
|
"""Neutron Client class for networking-sfc driver"""
|
||||||
|
|
||||||
|
def __init__(self, auth_attr):
|
||||||
|
auth = identity.Password(**auth_attr)
|
||||||
|
sess = session.Session(auth=auth)
|
||||||
|
self.client = neutron_client.Client(session=sess)
|
||||||
|
|
||||||
|
def flow_classifier_create(self, fc_dict):
|
||||||
|
LOG.debug(_("fc_dict passed is {fc_dict}").format(fc_dict=fc_dict))
|
||||||
|
fc = self.client.create_flow_classifier({'flow_classifier': fc_dict})
|
||||||
|
if fc:
|
||||||
|
return fc['flow_classifier']['id']
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def flow_classifier_update(self, fc_id, update_fc):
|
||||||
|
update_fc_dict = {'flow_classifier': update_fc}
|
||||||
|
return self.client.update_flow_classifier(fc_id, update_fc_dict)
|
||||||
|
|
||||||
|
def flow_classifier_delete(self, fc_id):
|
||||||
|
try:
|
||||||
|
self.client.delete_flow_classifier(fc_id)
|
||||||
|
except nc_exceptions.NotFound:
|
||||||
|
LOG.warning(_("fc %s not found") % fc_id)
|
||||||
|
raise ValueError('fc %s not found' % fc_id)
|
||||||
|
|
||||||
|
def port_pair_create(self, port_pair_dict):
|
||||||
|
try:
|
||||||
|
pp = self.client.create_port_pair({'port_pair': port_pair_dict})
|
||||||
|
except nc_exceptions.BadRequest as e:
|
||||||
|
LOG.error(_("create port pair returns %s") % e)
|
||||||
|
raise ValueError(str(e))
|
||||||
|
|
||||||
|
if pp and len(pp):
|
||||||
|
return pp['port_pair']['id']
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def port_pair_delete(self, port_pair_id):
|
||||||
|
try:
|
||||||
|
self.client.delete_port_pair(port_pair_id)
|
||||||
|
except nc_exceptions.NotFound:
|
||||||
|
LOG.warning(_('port pair %s not found') % port_pair_id)
|
||||||
|
raise ValueError('port pair %s not found' % port_pair_id)
|
||||||
|
|
||||||
|
def port_pair_group_create(self, ppg_dict):
|
||||||
|
try:
|
||||||
|
ppg = self.client.create_port_pair_group(
|
||||||
|
{'port_pair_group': ppg_dict})
|
||||||
|
except nc_exceptions.BadRequest as e:
|
||||||
|
LOG.warning(_('create port pair group returns %s') % e)
|
||||||
|
raise ValueError(str(e))
|
||||||
|
|
||||||
|
if ppg and len(ppg):
|
||||||
|
return ppg['port_pair_group']['id']
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def port_pair_group_delete(self, ppg_id):
|
||||||
|
try:
|
||||||
|
self.client.delete_port_pair_group(ppg_id)
|
||||||
|
except nc_exceptions.NotFound:
|
||||||
|
LOG.warning(_('port pair group %s not found') % ppg_id)
|
||||||
|
raise ValueError('port pair group %s not found' % ppg_id)
|
||||||
|
|
||||||
|
def port_chain_create(self, port_chain_dict):
|
||||||
|
try:
|
||||||
|
pc = self.client.create_port_chain(
|
||||||
|
{'port_chain': port_chain_dict})
|
||||||
|
except nc_exceptions.BadRequest as e:
|
||||||
|
LOG.warning(_('create port chain returns %s') % e)
|
||||||
|
raise ValueError(str(e))
|
||||||
|
|
||||||
|
if pc and len(pc):
|
||||||
|
return pc['port_chain']['id']
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def port_chain_delete(self, port_chain_id):
|
||||||
|
try:
|
||||||
|
port_chain = self.client.show_port_chain(port_chain_id)
|
||||||
|
if port_chain:
|
||||||
|
self.client.delete_port_chain(port_chain_id)
|
||||||
|
ppg_list = port_chain['port_chain'].get('port_pair_groups')
|
||||||
|
if ppg_list and len(ppg_list):
|
||||||
|
for i in xrange(0, len(ppg_list)):
|
||||||
|
ppg = self.client.show_port_pair_group(ppg_list[i])
|
||||||
|
if ppg:
|
||||||
|
self.client.delete_port_pair_group(ppg_list[i])
|
||||||
|
port_pairs = ppg['port_pair_group']['port_pairs']
|
||||||
|
if port_pairs and len(port_pairs):
|
||||||
|
for j in xrange(0, len(port_pairs)):
|
||||||
|
pp_id = port_pairs[j]
|
||||||
|
self.client.delete_port_pair(pp_id)
|
||||||
|
except nc_exceptions.NotFound:
|
||||||
|
LOG.warning(_('port chain %s not found') % port_chain_id)
|
||||||
|
raise ValueError('port chain %s not found' % port_chain_id)
|
||||||
|
|
|
@ -35,7 +35,8 @@ class VnffgAbstractDriver(extensions.PluginInterface):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def create_chain(self, fc_id, vnfs, symmetrical=False, auth_attr=None):
|
def create_chain(self, name, fc_id, vnfs, symmetrical=False,
|
||||||
|
auth_attr=None):
|
||||||
"""Create service function chain and returns an ID"""
|
"""Create service function chain and returns an ID"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -51,7 +52,8 @@ class VnffgAbstractDriver(extensions.PluginInterface):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def create_flow_classifier(self, fc, symmetrical=False, auth_attr=None):
|
def create_flow_classifier(self, name, fc, symmetrical=False,
|
||||||
|
auth_attr=None):
|
||||||
"""Create flow classifier and returns an ID"""
|
"""Create flow classifier and returns an ID"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ class VNFFGNoop(abstract_driver.SfcAbstractDriver):
|
||||||
return 'VNFFG Noop driver'
|
return 'VNFFG Noop driver'
|
||||||
|
|
||||||
@log.log
|
@log.log
|
||||||
def create_chain(self, fc_id, vnfs, auth_attr=None):
|
def create_chain(self, name, fc_id, vnfs, auth_attr=None):
|
||||||
instance_id = str(uuid.uuid4())
|
instance_id = str(uuid.uuid4())
|
||||||
self._instances.add(instance_id)
|
self._instances.add(instance_id)
|
||||||
return instance_id
|
return instance_id
|
||||||
|
@ -56,7 +56,7 @@ class VNFFGNoop(abstract_driver.SfcAbstractDriver):
|
||||||
self._instances.remove(chain_id)
|
self._instances.remove(chain_id)
|
||||||
|
|
||||||
@log.log
|
@log.log
|
||||||
def create_flow_classifier(self, fc, auth_attr=None):
|
def create_flow_classifier(self, name, fc, auth_attr=None):
|
||||||
instance_id = str(uuid.uuid4())
|
instance_id = str(uuid.uuid4())
|
||||||
self._instances.add(instance_id)
|
self._instances.add(instance_id)
|
||||||
return instance_id
|
return instance_id
|
||||||
|
|
|
@ -14,10 +14,12 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import os
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
from cryptography import fernet
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslo_utils import excutils
|
from oslo_utils import excutils
|
||||||
|
@ -39,6 +41,7 @@ from toscaparser import tosca_template
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
|
||||||
def config_opts():
|
def config_opts():
|
||||||
|
@ -216,25 +219,28 @@ class NfvoPlugin(nfvo_db.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin):
|
||||||
sfc = super(NfvoPlugin, self).get_sfc(context, nfp['chain_id'])
|
sfc = super(NfvoPlugin, self).get_sfc(context, nfp['chain_id'])
|
||||||
match = super(NfvoPlugin, self).get_classifier(context,
|
match = super(NfvoPlugin, self).get_classifier(context,
|
||||||
nfp['classifier_id'],
|
nfp['classifier_id'],
|
||||||
fields='match')
|
fields='match')['match']
|
||||||
# grab the first VNF to check it's VIM type
|
# grab the first VNF to check it's VIM type
|
||||||
# we have already checked that all VNFs are in the same VIM
|
# we have already checked that all VNFs are in the same VIM
|
||||||
vim_auth = self._get_vim_from_vnf(context,
|
vim_obj = self._get_vim_from_vnf(context,
|
||||||
list(vnffg_dict[
|
list(vnffg_dict[
|
||||||
'vnf_mapping'].values())[0]
|
'vnf_mapping'].values())[0])
|
||||||
)
|
|
||||||
# TODO(trozet): figure out what auth info we actually need to pass
|
# TODO(trozet): figure out what auth info we actually need to pass
|
||||||
# to the driver. Is it a session, or is full vim obj good enough?
|
# to the driver. Is it a session, or is full vim obj good enough?
|
||||||
driver_type = vim_auth['type']
|
driver_type = vim_obj['type']
|
||||||
try:
|
try:
|
||||||
fc_id = self._vim_drivers.invoke(driver_type,
|
fc_id = self._vim_drivers.invoke(driver_type,
|
||||||
'create_flow_classifier',
|
'create_flow_classifier',
|
||||||
fc=match, auth_attr=vim_auth,
|
name=vnffg_dict['name'],
|
||||||
|
fc=match,
|
||||||
|
auth_attr=vim_obj['auth_cred'],
|
||||||
symmetrical=sfc['symmetrical'])
|
symmetrical=sfc['symmetrical'])
|
||||||
sfc_id = self._vim_drivers.invoke(driver_type, 'create_chain',
|
sfc_id = self._vim_drivers.invoke(driver_type,
|
||||||
|
'create_chain',
|
||||||
|
name=vnffg_dict['name'],
|
||||||
vnfs=sfc['chain'], fc_id=fc_id,
|
vnfs=sfc['chain'], fc_id=fc_id,
|
||||||
symmetrical=sfc['symmetrical'],
|
symmetrical=sfc['symmetrical'],
|
||||||
auth_attr=vim_auth)
|
auth_attr=vim_obj['auth_cred'])
|
||||||
except Exception:
|
except Exception:
|
||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
self.delete_vnffg(context, vnffg_id=vnffg_dict['id'])
|
self.delete_vnffg(context, vnffg_id=vnffg_dict['id'])
|
||||||
|
@ -276,24 +282,23 @@ class NfvoPlugin(nfvo_db.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin):
|
||||||
LOG.debug(_('chain update: %s'), chain)
|
LOG.debug(_('chain update: %s'), chain)
|
||||||
sfc['chain'] = chain
|
sfc['chain'] = chain
|
||||||
sfc['symmetrical'] = new_vnffg['symmetrical']
|
sfc['symmetrical'] = new_vnffg['symmetrical']
|
||||||
vim_auth = self._get_vim_from_vnf(context,
|
vim_obj = self._get_vim_from_vnf(context,
|
||||||
list(vnffg_dict[
|
list(vnffg_dict[
|
||||||
'vnf_mapping'].values())[0]
|
'vnf_mapping'].values())[0])
|
||||||
)
|
driver_type = vim_obj['type']
|
||||||
driver_type = vim_auth['type']
|
|
||||||
try:
|
try:
|
||||||
# we don't support updating the match criteria in first iteration
|
# we don't support updating the match criteria in first iteration
|
||||||
# so this is essentially a noop. Good to keep for future use
|
# so this is essentially a noop. Good to keep for future use
|
||||||
# though.
|
# though.
|
||||||
self._vim_drivers.invoke(driver_type, 'update_flow_classifier',
|
self._vim_drivers.invoke(driver_type, 'update_flow_classifier',
|
||||||
fc_id=fc['instance_id'], fc=fc['match'],
|
fc_id=fc['instance_id'], fc=fc['match'],
|
||||||
auth_attr=vim_auth,
|
auth_attr=vim_obj['auth_cred'],
|
||||||
symmetrical=new_vnffg['symmetrical'])
|
symmetrical=new_vnffg['symmetrical'])
|
||||||
self._vim_drivers.invoke(driver_type, 'update_chain',
|
self._vim_drivers.invoke(driver_type, 'update_chain',
|
||||||
vnfs=sfc['chain'],
|
vnfs=sfc['chain'],
|
||||||
fc_ids=[fc['instance_id']],
|
fc_ids=[fc['instance_id']],
|
||||||
chain_id=sfc['instance_id'],
|
chain_id=sfc['instance_id'],
|
||||||
auth_attr=vim_auth,
|
auth_attr=vim_obj['auth_cred'],
|
||||||
symmetrical=new_vnffg['symmetrical'])
|
symmetrical=new_vnffg['symmetrical'])
|
||||||
except Exception:
|
except Exception:
|
||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
|
@ -321,21 +326,20 @@ class NfvoPlugin(nfvo_db.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin):
|
||||||
|
|
||||||
fc = super(NfvoPlugin, self).get_classifier(context,
|
fc = super(NfvoPlugin, self).get_classifier(context,
|
||||||
nfp['classifier_id'])
|
nfp['classifier_id'])
|
||||||
vim_auth = self._get_vim_from_vnf(context,
|
vim_obj = self._get_vim_from_vnf(context,
|
||||||
list(vnffg_dict[
|
list(vnffg_dict[
|
||||||
'vnf_mapping'].values())[0]
|
'vnf_mapping'].values())[0])
|
||||||
)
|
driver_type = vim_obj['type']
|
||||||
driver_type = vim_auth['type']
|
|
||||||
try:
|
try:
|
||||||
if sfc['instance_id'] is not None:
|
if sfc['instance_id'] is not None:
|
||||||
self._vim_drivers.invoke(driver_type, 'delete_chain',
|
self._vim_drivers.invoke(driver_type, 'delete_chain',
|
||||||
chain_id=sfc['instance_id'],
|
chain_id=sfc['instance_id'],
|
||||||
auth_attr=vim_auth)
|
auth_attr=vim_obj['auth_cred'])
|
||||||
if fc['instance_id'] is not None:
|
if fc['instance_id'] is not None:
|
||||||
self._vim_drivers.invoke(driver_type,
|
self._vim_drivers.invoke(driver_type,
|
||||||
'delete_flow_classifier',
|
'delete_flow_classifier',
|
||||||
fc_id=fc['instance_id'],
|
fc_id=fc['instance_id'],
|
||||||
auth_attr=vim_auth)
|
auth_attr=vim_obj['auth_cred'])
|
||||||
except Exception:
|
except Exception:
|
||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
vnffg_dict['status'] = constants.ERROR
|
vnffg_dict['status'] = constants.ERROR
|
||||||
|
@ -353,11 +357,37 @@ class NfvoPlugin(nfvo_db.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin):
|
||||||
"""
|
"""
|
||||||
vnfm_plugin = manager.TackerManager.get_service_plugins()['VNFM']
|
vnfm_plugin = manager.TackerManager.get_service_plugins()['VNFM']
|
||||||
vim_id = vnfm_plugin.get_vnf(context, vnf_id, fields=['vim_id'])
|
vim_id = vnfm_plugin.get_vnf(context, vnf_id, fields=['vim_id'])
|
||||||
vim_obj = self.get_vim(context, vim_id['vim_id'])
|
vim_obj = self.get_vim(context, vim_id['vim_id'], mask_password=False)
|
||||||
|
vim_auth = vim_obj['auth_cred']
|
||||||
|
vim_auth['password'] = self._decode_vim_auth(vim_obj['id'],
|
||||||
|
vim_auth['password'].
|
||||||
|
encode('utf-8'))
|
||||||
|
vim_auth['auth_url'] = vim_obj['auth_url']
|
||||||
if vim_obj is None:
|
if vim_obj is None:
|
||||||
raise nfvo.VimFromVnfNotFoundException(vnf_id=vnf_id)
|
raise nfvo.VimFromVnfNotFoundException(vnf_id=vnf_id)
|
||||||
|
|
||||||
return vim_obj
|
return vim_obj
|
||||||
|
|
||||||
|
def _decode_vim_auth(self, vim_id, cred):
|
||||||
|
"""Decode Vim credentials
|
||||||
|
|
||||||
|
Decrypt VIM cred. using Fernet Key
|
||||||
|
"""
|
||||||
|
vim_key = self._find_vim_key(vim_id)
|
||||||
|
f = fernet.Fernet(vim_key)
|
||||||
|
if not f:
|
||||||
|
LOG.warning(_('Unable to decode VIM auth'))
|
||||||
|
raise nfvo.VimNotFoundException('Unable to decode VIM auth key')
|
||||||
|
return f.decrypt(cred)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _find_vim_key(vim_id):
|
||||||
|
key_file = os.path.join(CONF.vim_keys.openstack, vim_id)
|
||||||
|
LOG.debug(_('Attempting to open key file for vim id %s'), vim_id)
|
||||||
|
with open(key_file, 'r') as f:
|
||||||
|
return f.read()
|
||||||
|
LOG.warning(_('VIM id invalid or key not found for %s'), vim_id)
|
||||||
|
|
||||||
def _vim_resource_name_to_id(self, context, resource, name, vnf_id):
|
def _vim_resource_name_to_id(self, context, resource, name, vnf_id):
|
||||||
"""Converts a VIM resource name to its ID
|
"""Converts a VIM resource name to its ID
|
||||||
|
|
||||||
|
@ -367,10 +397,10 @@ class NfvoPlugin(nfvo_db.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin):
|
||||||
the classifier will apply to
|
the classifier will apply to
|
||||||
:return: ID of the resource name
|
:return: ID of the resource name
|
||||||
"""
|
"""
|
||||||
vim_auth = self._get_vim_from_vnf(context, vnf_id)
|
vim_obj = self._get_vim_from_vnf(context, vnf_id)
|
||||||
driver_type = vim_auth['type']
|
driver_type = vim_obj['type']
|
||||||
return self._vim_drivers.invoke(driver_type,
|
return self._vim_drivers.invoke(driver_type,
|
||||||
'get_vim_resource_id',
|
'get_vim_resource_id',
|
||||||
vim_auth=vim_auth,
|
vim_auth=vim_obj['auth_cred'],
|
||||||
resource_type=resource,
|
resource_type=resource,
|
||||||
resource_name=name)
|
resource_name=name)
|
||||||
|
|
234
tacker/tests/unit/vm/nfvo/drivers/vnffg/sfc_drivers/networking-sfc/test_n_sfc.py
Executable file
234
tacker/tests/unit/vm/nfvo/drivers/vnffg/sfc_drivers/networking-sfc/test_n_sfc.py
Executable file
|
@ -0,0 +1,234 @@
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from tacker import context
|
||||||
|
from tacker.nfvo.drivers.vim import openstack_driver
|
||||||
|
from tacker.tests.unit import base
|
||||||
|
from tacker.tests.unit.db import utils
|
||||||
|
|
||||||
|
|
||||||
|
class FakeNeutronClient(mock.Mock):
|
||||||
|
def __init__(self):
|
||||||
|
super(FakeNeutronClient, self).__init__()
|
||||||
|
self.__fc_dict = {}
|
||||||
|
self.__pp_dict = {}
|
||||||
|
self.__ppg_dict = {}
|
||||||
|
self.__chain_dict = {}
|
||||||
|
|
||||||
|
def flow_classifier_create(self, fc_create_dict):
|
||||||
|
fc_id = uuid.uuid4()
|
||||||
|
self.__fc_dict[fc_id] = fc_create_dict
|
||||||
|
return fc_id
|
||||||
|
|
||||||
|
def show_flow_classifier(self, fc_dict):
|
||||||
|
fc_name = fc_dict['name']
|
||||||
|
for fc_id in self.__fc_dict:
|
||||||
|
fc = self.__fc_dict[fc_id]
|
||||||
|
if fc_name == fc['name']:
|
||||||
|
return {'id': fc_id}
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def flow_classifier_update(self, fc_id, fc_update_dict):
|
||||||
|
if fc_id not in self.__fc_dict:
|
||||||
|
return None
|
||||||
|
self.__fc_dict[fc_id] = fc_update_dict
|
||||||
|
return fc_update_dict
|
||||||
|
|
||||||
|
def flow_classifier_delete(self, fc_id):
|
||||||
|
if fc_id not in self.__fc_dict:
|
||||||
|
raise ValueError('fc not found')
|
||||||
|
self.__fc_dict.pop(fc_id)
|
||||||
|
|
||||||
|
def port_pair_create(self, port_pair):
|
||||||
|
pp_id = uuid.uuid4()
|
||||||
|
self.__pp_dict[pp_id] = port_pair
|
||||||
|
return pp_id
|
||||||
|
|
||||||
|
def show_port_pair(self, port_pair_dict):
|
||||||
|
input_pp_name = port_pair_dict['name']
|
||||||
|
for pp_id in self.__pp_dict:
|
||||||
|
port_pair = self.__pp_dict[pp_id]
|
||||||
|
if port_pair['name'] == input_pp_name:
|
||||||
|
return {'id': pp_id}
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def port_pair_group_create(self, port_pair_group):
|
||||||
|
ppg_id = uuid.uuid4()
|
||||||
|
self.__ppg_dict[ppg_id] = port_pair_group
|
||||||
|
return ppg_id
|
||||||
|
|
||||||
|
def show_port_pair_group(self, port_pair_group_dict):
|
||||||
|
input_ppg_name = port_pair_group_dict['name']
|
||||||
|
for ppg_id in self.__ppg_dict:
|
||||||
|
port_pair_group = self.__ppg_dict[ppg_id]
|
||||||
|
if port_pair_group['name'] == input_ppg_name:
|
||||||
|
return {'id': ppg_id}
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def port_chain_create(self, port_chain):
|
||||||
|
chain_id = uuid.uuid4()
|
||||||
|
self.__chain_dict[chain_id] = port_chain
|
||||||
|
return chain_id
|
||||||
|
|
||||||
|
def show_port_chain(self, port_chain_dict):
|
||||||
|
input_chain_name = port_chain_dict['name']
|
||||||
|
for chain_id in self.__chain_dict:
|
||||||
|
port_chain = self.__chain_dict[chain_id]
|
||||||
|
if port_chain['name'] == input_chain_name:
|
||||||
|
return {'id': chain_id}
|
||||||
|
return None
|
||||||
|
|
||||||
|
def port_chain_delete(self, chain_id):
|
||||||
|
if chain_id not in self.__chain_dict:
|
||||||
|
raise ValueError('port chain delete failed')
|
||||||
|
self.__chain_dict.pop(chain_id)
|
||||||
|
|
||||||
|
|
||||||
|
class TestChainSFC(base.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestChainSFC, self).setUp()
|
||||||
|
self.context = context.get_admin_context()
|
||||||
|
self.sfc_driver = openstack_driver.OpenStack_Driver()
|
||||||
|
self._mock_neutron_client()
|
||||||
|
self.addCleanup(mock.patch.stopall)
|
||||||
|
|
||||||
|
def _mock_neutron_client(self):
|
||||||
|
self.neutron_client = mock.Mock(wraps=FakeNeutronClient())
|
||||||
|
fake_neutron_client = mock.Mock()
|
||||||
|
fake_neutron_client.return_value = self.neutron_client
|
||||||
|
self._mock(
|
||||||
|
'tacker.nfvo.drivers.vim.openstack_driver.'
|
||||||
|
'NeutronClient',
|
||||||
|
fake_neutron_client)
|
||||||
|
|
||||||
|
def _mock(self, target, new=mock.DEFAULT):
|
||||||
|
patcher = mock.patch(target, new)
|
||||||
|
return patcher.start()
|
||||||
|
|
||||||
|
def test_create_flow_classifier(self):
|
||||||
|
flow_classifier = {'name': 'fake_fc',
|
||||||
|
'source_port_range': '2005-2010',
|
||||||
|
'ip_proto': 6,
|
||||||
|
'destination_port_range': '80-180'}
|
||||||
|
result = self.sfc_driver.\
|
||||||
|
create_flow_classifier(name='fake_ffg', fc=flow_classifier,
|
||||||
|
auth_attr=utils.get_vim_auth_obj())
|
||||||
|
self.assertIsNotNone(result)
|
||||||
|
|
||||||
|
def test_update_flow_classifier(self):
|
||||||
|
flow_classifier = {'name': 'next_fake_fc',
|
||||||
|
'description': 'fake flow-classifier',
|
||||||
|
'source_port_range': '2005-2010',
|
||||||
|
'ip_proto': 6,
|
||||||
|
'destination_port_range': '80-180'}
|
||||||
|
fc_id = self.sfc_driver.\
|
||||||
|
create_flow_classifier(name='fake_ffg', fc=flow_classifier,
|
||||||
|
auth_attr=utils.get_vim_auth_obj())
|
||||||
|
|
||||||
|
self.assertIsNotNone(fc_id)
|
||||||
|
|
||||||
|
flow_classifier['description'] = 'next fake flow-classifier'
|
||||||
|
|
||||||
|
result = self.sfc_driver.\
|
||||||
|
update_flow_classifier(fc_id=fc_id,
|
||||||
|
fc=flow_classifier,
|
||||||
|
auth_attr=utils.get_vim_auth_obj())
|
||||||
|
self.assertIsNotNone(result)
|
||||||
|
|
||||||
|
def test_delete_flow_classifier(self):
|
||||||
|
flow_classifier = {'name': 'another_fake_fc',
|
||||||
|
'description': 'another flow-classifier',
|
||||||
|
'source_port_range': '1999-2005',
|
||||||
|
'ip_proto': 6,
|
||||||
|
'destination_port_range': '80-100'}
|
||||||
|
fc_id = self.sfc_driver.\
|
||||||
|
create_flow_classifier(name='fake_ffg', fc=flow_classifier,
|
||||||
|
auth_attr=utils.get_vim_auth_obj())
|
||||||
|
|
||||||
|
self.assertIsNotNone(fc_id)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.sfc_driver.\
|
||||||
|
delete_flow_classifier(fc_id=fc_id,
|
||||||
|
auth_attr=utils.get_vim_auth_obj())
|
||||||
|
except Exception:
|
||||||
|
self.assertTrue(True)
|
||||||
|
|
||||||
|
def test_create_chain(self):
|
||||||
|
auth_attr = utils.get_vim_auth_obj()
|
||||||
|
flow_classifier = {'name': 'test_create_chain_fc',
|
||||||
|
'description': 'fc for testing create chain',
|
||||||
|
'source_port_range': '1997-2008',
|
||||||
|
'ip_proto': 6,
|
||||||
|
'destination_port_range': '80-100'}
|
||||||
|
fc_id = self.sfc_driver.\
|
||||||
|
create_flow_classifier(name='fake_ffg', fc=flow_classifier,
|
||||||
|
auth_attr=auth_attr)
|
||||||
|
|
||||||
|
self.assertIsNotNone(fc_id)
|
||||||
|
|
||||||
|
vnf_1 = {'name': 'test_create_chain_vnf_1',
|
||||||
|
'connection_points': [uuid.uuid4(), uuid.uuid4()]}
|
||||||
|
vnf_2 = {'name': 'test_create_chain_vnf_2',
|
||||||
|
'connection_points': [uuid.uuid4(), uuid.uuid4()]}
|
||||||
|
vnf_3 = {'name': 'test_create_chain_vnf_3',
|
||||||
|
'connection_points': [uuid.uuid4(), uuid.uuid4()]}
|
||||||
|
vnfs = [vnf_1, vnf_2, vnf_3]
|
||||||
|
|
||||||
|
result = self.sfc_driver.create_chain(name='fake_ffg',
|
||||||
|
fc_id=fc_id,
|
||||||
|
vnfs=vnfs,
|
||||||
|
auth_attr=auth_attr)
|
||||||
|
|
||||||
|
self.assertIsNotNone(result)
|
||||||
|
|
||||||
|
def test_delete_chain(self):
|
||||||
|
auth_attr = utils.get_vim_auth_obj()
|
||||||
|
flow_classifier = {'name': 'test_delete_chain_fc',
|
||||||
|
'description': 'fc for testing delete chain',
|
||||||
|
'source_port_range': '1000-2000',
|
||||||
|
'ip_proto': 6,
|
||||||
|
'destination_port_range': '80-180'}
|
||||||
|
fc_id = self.sfc_driver.\
|
||||||
|
create_flow_classifier(name='fake_ffg', fc=flow_classifier,
|
||||||
|
auth_attr=auth_attr)
|
||||||
|
|
||||||
|
self.assertIsNotNone(fc_id)
|
||||||
|
|
||||||
|
vnf_1 = {'name': 'test_delete_chain_vnf_1',
|
||||||
|
'connection_points': [uuid.uuid4(), uuid.uuid4()]}
|
||||||
|
vnf_2 = {'name': 'test_delete_chain_vnf_2',
|
||||||
|
'connection_points': [uuid.uuid4(), uuid.uuid4()]}
|
||||||
|
vnf_3 = {'name': 'test_delete_chain_vnf_3',
|
||||||
|
'connection_points': [uuid.uuid4(), uuid.uuid4()]}
|
||||||
|
vnfs = [vnf_1, vnf_2, vnf_3]
|
||||||
|
|
||||||
|
chain_id = self.sfc_driver.create_chain(name='fake_ffg',
|
||||||
|
fc_id=fc_id,
|
||||||
|
vnfs=vnfs,
|
||||||
|
auth_attr=auth_attr)
|
||||||
|
|
||||||
|
self.assertIsNotNone(chain_id)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.sfc_driver.delete_chain(chain_id,
|
||||||
|
auth_attr=auth_attr)
|
||||||
|
except Exception:
|
||||||
|
self.assertTrue(True)
|
|
@ -35,9 +35,10 @@ SECRET_PASSWORD = '***'
|
||||||
|
|
||||||
|
|
||||||
def dummy_get_vim(*args, **kwargs):
|
def dummy_get_vim(*args, **kwargs):
|
||||||
vim_auth = utils.get_vim_auth_obj()
|
vim_obj = dict()
|
||||||
vim_auth['type'] = 'openstack'
|
vim_obj['auth_cred'] = utils.get_vim_auth_obj()
|
||||||
return vim_auth
|
vim_obj['type'] = 'openstack'
|
||||||
|
return vim_obj
|
||||||
|
|
||||||
|
|
||||||
class FakeDriverManager(mock.Mock):
|
class FakeDriverManager(mock.Mock):
|
||||||
|
@ -355,6 +356,7 @@ class TestNfvoPlugin(db_base.SqlTestCase):
|
||||||
self.assertIn('status', result)
|
self.assertIn('status', result)
|
||||||
self.assertEqual('PENDING_CREATE', result['status'])
|
self.assertEqual('PENDING_CREATE', result['status'])
|
||||||
self._driver_manager.invoke.assert_called_with(mock.ANY, mock.ANY,
|
self._driver_manager.invoke.assert_called_with(mock.ANY, mock.ANY,
|
||||||
|
name=mock.ANY,
|
||||||
vnfs=mock.ANY,
|
vnfs=mock.ANY,
|
||||||
fc_id=mock.ANY,
|
fc_id=mock.ANY,
|
||||||
auth_attr=mock.ANY,
|
auth_attr=mock.ANY,
|
||||||
|
@ -375,6 +377,7 @@ class TestNfvoPlugin(db_base.SqlTestCase):
|
||||||
self.assertIn('status', result)
|
self.assertIn('status', result)
|
||||||
self.assertEqual('PENDING_CREATE', result['status'])
|
self.assertEqual('PENDING_CREATE', result['status'])
|
||||||
self._driver_manager.invoke.assert_called_with(mock.ANY, mock.ANY,
|
self._driver_manager.invoke.assert_called_with(mock.ANY, mock.ANY,
|
||||||
|
name=mock.ANY,
|
||||||
vnfs=mock.ANY,
|
vnfs=mock.ANY,
|
||||||
fc_id=mock.ANY,
|
fc_id=mock.ANY,
|
||||||
auth_attr=mock.ANY,
|
auth_attr=mock.ANY,
|
||||||
|
|
Loading…
Reference in New Issue