Merge "Add reusing feature when creating VNFFG."
This commit is contained in:
commit
6bea5c85f7
247
doc/source/user/vnffg_usage_guide_advanced.rst
Normal file
247
doc/source/user/vnffg_usage_guide_advanced.rst
Normal file
@ -0,0 +1,247 @@
|
||||
..
|
||||
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.
|
||||
|
||||
.. _ref-vnffg:
|
||||
|
||||
=========================================
|
||||
VNF Forwarding Graph Advanced Usage Guide
|
||||
=========================================
|
||||
|
||||
This section cover some examples about advanced VNFFG usage guide in Tacker.
|
||||
Users should have some basic knowledge about VNFFG [#f1]_, before following
|
||||
this document.
|
||||
|
||||
Two VNFFGs go through one VNF
|
||||
=============================
|
||||
|
||||
This scenario describes an example, where 2 VNFFGs go through one virtual
|
||||
network functions (VNFs). The VNFFG1 creates the chain from VNF1 and VNF2, and
|
||||
VNFFG2 creates the chain from VNF1. VNF1 belongs to both of VNFFGs.
|
||||
|
||||
Bellow example is the template of VNFFG1 [#f2]_, in **properties** of
|
||||
Forwarding_path1, the **path** shows that VNFFG1 will go through VNF1 and VNF2.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
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.TackerV2
|
||||
description: creates path (CP12->CP22)
|
||||
properties:
|
||||
id: 51
|
||||
policy:
|
||||
type: ACL
|
||||
criteria:
|
||||
- name: block_tcp
|
||||
classifier:
|
||||
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: 2
|
||||
dependent_virtual_link: [VL12,VL22]
|
||||
connection_point: [CP12,CP22]
|
||||
constituent_vnfs: [VNFD1,VNFD2]
|
||||
members: [Forwarding_path1]
|
||||
|
||||
Similar to the above example, VNFFG2 template [#f3]_ only create a chain that
|
||||
goes through VNF1.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
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.TackerV2
|
||||
description: creates path (CP12->CP22)
|
||||
properties:
|
||||
id: 51
|
||||
policy:
|
||||
type: ACL
|
||||
criteria:
|
||||
- name: block_tcp
|
||||
classifier:
|
||||
network_src_port_id: 640dfd77-c92b-45a3-b8fc-22712de480e1
|
||||
destination_port_range: 8080-8080
|
||||
ip_proto: 6
|
||||
ip_dst_prefix: 192.168.1.2/24
|
||||
path:
|
||||
- forwarder: VNFD1
|
||||
capability: CP12
|
||||
|
||||
groups:
|
||||
VNFFG1:
|
||||
type: tosca.groups.nfv.VNFFG
|
||||
description: HTTP to Corporate Net
|
||||
properties:
|
||||
vendor: tacker
|
||||
version: 1.0
|
||||
number_of_endpoints: 1
|
||||
dependent_virtual_link: [VL12]
|
||||
connection_point: [CP12]
|
||||
constituent_vnfs: [VNFD1]
|
||||
members: [Forwarding_path1]
|
||||
|
||||
For testing VNF Forwarding graph feature, we create 2 servers, **http_client**
|
||||
and **http_server**. The example uses **net0** to create VNFFG on that network.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ net_id=$(openstack network list | grep net0 | awk '{print $2}')
|
||||
$ openstack server create --flavor m1.tiny --image cirros-0.3.5-x86_64-disk --nic net-id=$net_id http_client
|
||||
$ openstack server create --flavor m1.tiny --image cirros-0.3.5-x86_64-disk --nic net-id=$net_id http_server
|
||||
|
||||
To get information about neutron ports of **http_client** and **http_server**
|
||||
that are used for classifying traffics, user can use openstack commands to
|
||||
get these information.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ client_ip=$(openstack server list | grep http_client | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+')
|
||||
$ network_source_port_id=$(openstack port list | grep $client_ip | awk '{print $2}')
|
||||
$ ip_dst=$(openstack server list | grep http_server | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+')
|
||||
|
||||
.. note::
|
||||
|
||||
Please update parameters in **classifier** such as **network_src_port_id**,
|
||||
**destination_port_range**, **ip_proto**, **ip_dst_prefix** to meet your
|
||||
environment, which is described in VNFFG usage guide [#f4]_. Classifiers
|
||||
must be different to each others.
|
||||
|
||||
We assume that **http_server** and **http_client** are already created.
|
||||
VNF1 and VNF2 templates are shown in [#f5]_ and [#f6]_.
|
||||
|
||||
Once the OpenStack VIM is ready, firstly we can create VNFs (VNF1 and VNF2)
|
||||
from VNFD templates. For easily, we put all VNFD templates and VNFFGD templates
|
||||
in [#f7]_.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ openstack vnf descriptor create --vnfd-file tosca-vnffg-vnfd1.yaml VNFD1
|
||||
$ openstack vnf create --vnfd-name VNFD1 --vim-name VIM0 VNF1
|
||||
$ openstack vnf descriptor create --vnfd-file tosca-vnffg-vnfd2.yaml VNFD2
|
||||
$ openstack vnf create --vnfd-name VNFD2 --vim-name VIM0 VNF2
|
||||
|
||||
To create VNFFG, we should get the **vnf id** for VNF1 and VNF2:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ openstack vnf list --fit-width
|
||||
+-------------------------------+------+---------------------------+--------+-------------------------------+----------------------------------+
|
||||
| ID | Name | Mgmt Url | Status | VIM ID | VNFD ID |
|
||||
+-------------------------------+------+---------------------------+--------+-------------------------------+----------------------------------+
|
||||
| 2ea1b577-89c1-478e-bedd- | VNF2 | {"VDU1": "192.168.120.5"} | ACTIVE | 45fecf6f-9080-4e8b-953c- | 69a4d2d4-9329-4807-9c59-09cb8d95 |
|
||||
| 76f69951e840 | | | | d9556e6ad2cb | c612 |
|
||||
| 45c6c5ee-517e- | VNF1 | {"VDU1": "192.168.120.9"} | ACTIVE | 45fecf6f-9080-4e8b-953c- | f4cb1509-c216-47a1-b76e- |
|
||||
| 4fc3-ad41-6e86c38d1a63 | | | | d9556e6ad2cb | e419f8ae6534 |
|
||||
+-------------------------------+------+---------------------------+--------+-------------------------------+----------------------------------+
|
||||
|
||||
Then we can create VNFFG1 that goes through VNF1, VNF2 and VNFFG2 that only
|
||||
goes through VNF1. We need to record **vnf id** to provide them in create
|
||||
VNFFG commands.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ openstack vnf graph descriptor create --vnffgd-file tosca-vnffgd-sample.yaml VNFFGD1
|
||||
$ openstack vnf graph descriptor create --vnffgd-file tosca-vnffgd-sample-VNF1.yaml VNFFGD2
|
||||
$ openstack vnf graph create --vnffgd-name VNFFGD1 --vnf-mapping VNFD1:45c6c5ee-517e-4fc3-ad41-6e86c38d1a63,VNFD2:2ea1b577-89c1-478e-bedd-76f69951e840 VNFFG1
|
||||
$ openstack vnf graph create --vnffgd-name VNFFGD2 --vnf-mapping VNFD1:45c6c5ee-517e-4fc3-ad41-6e86c38d1a63 VNFFG2
|
||||
|
||||
To check the VNFFG works fine, we can use list VNFFG and neutron port chain
|
||||
list to see what happens.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ openstack vnf graph list
|
||||
+--------------------------------------+--------+--------------------------------------+--------+
|
||||
| ID | Name | VNFFGD ID | Status |
|
||||
+--------------------------------------+--------+--------------------------------------+--------+
|
||||
| 097dcd5d-b329-44da-9693-8e56ff3612e3 | VNFFG1 | 75f53d50-f79b-446b-b9bd-48e26c3a87d0 | ACTIVE |
|
||||
| e33bc531-e124-432a-abf3-bec4f66ec15b | VNFFG2 | ead1a94f-2bf9-41fd-b543-8446cdbaac83 | ACTIVE |
|
||||
+--------------------------------------+--------+--------------------------------------+--------+
|
||||
|
||||
$ openstack sfc port chain list --fit-width
|
||||
+----------------------------+-------------------+----------------------------+----------------------------+----------------------------+----------+
|
||||
| ID | Name | Port Pair Groups | Flow Classifiers | Chain Parameters | Chain ID |
|
||||
+----------------------------+-------------------+----------------------------+----------------------------+----------------------------+----------+
|
||||
| 03ca4afb-8fc9-4337-94a3-98 | VNFFG1-port-chain | [u'1f71e5b1-b8f2-4f1b- | [u'46fd7fac-29f9-49cd-8959 | {u'symmetric': False, | 1 |
|
||||
| c90c5511a8 | | 9c85-6a6f31cf9906', | -42078d873487'] | u'correlation': u'mpls'} | |
|
||||
| | | u'8be115b1-7422-417b- | | | |
|
||||
| | | abc0-49b194d432cf'] | | | |
|
||||
| 24cbfa45-4033-4f95-aaa0-dd | VNFFG2-port-chain | [u'1f71e5b1-b8f2-4f1b- | [u'6c94dd78-9cda-4891-ad71 | {u'symmetric': False, | 2 |
|
||||
| 690ee1e9af | | 9c85-6a6f31cf9906'] | -81a46354356e'] | u'correlation': u'mpls'} | |
|
||||
+----------------------------+-------------------+----------------------------+----------------------------+----------------------------+----------+
|
||||
|
||||
We can see that, both of port chain will go through port pair group with the id
|
||||
**1f71e5b1-b8f2-4f1b-9c85-6a6f31cf9906**, that is created from neutron port of
|
||||
VNF1.
|
||||
|
||||
If user delete VNFFG1, the VNFFG2 is not affected because the port pair group
|
||||
**1f71e5b1-b8f2-4f1b-9c85-6a6f31cf9906** is not deleted.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ openstack vnf graph delete VNFFG1
|
||||
All specified vnffg(s) deleted successfully
|
||||
$ openstack vnf graph list
|
||||
+--------------------------------------+--------+--------------------------------------+--------+
|
||||
| ID | Name | VNFFGD ID | Status |
|
||||
+--------------------------------------+--------+--------------------------------------+--------+
|
||||
| e33bc531-e124-432a-abf3-bec4f66ec15b | VNFFG2 | ead1a94f-2bf9-41fd-b543-8446cdbaac83 | ACTIVE |
|
||||
+--------------------------------------+--------+--------------------------------------+--------+
|
||||
$ openstack sfc port chain list --fit-width
|
||||
+----------------------------+-------------------+----------------------------+----------------------------+----------------------------+----------+
|
||||
| ID | Name | Port Pair Groups | Flow Classifiers | Chain Parameters | Chain ID |
|
||||
+----------------------------+-------------------+----------------------------+----------------------------+----------------------------+----------+
|
||||
| 24cbfa45-4033-4f95-aaa0-dd | VNFFG2-port-chain | [u'1f71e5b1-b8f2-4f1b- | [u'6c94dd78-9cda-4891-ad71 | {u'symmetric': False, | 2 |
|
||||
| 690ee1e9af | | 9c85-6a6f31cf9906'] | -81a46354356e'] | u'correlation': u'mpls'} | |
|
||||
+----------------------------+-------------------+----------------------------+----------------------------+----------------------------+----------+
|
||||
|
||||
|
||||
.. rubric:: Footnotes
|
||||
|
||||
.. [#f1] https://docs.openstack.org/tacker/latest/user/vnffg_usage_guide.html
|
||||
.. [#f2] https://github.com/openstack/tacker/blob/master/samples/tosca-templates/vnffgd/tosca-vnffgd-sample.yaml
|
||||
.. [#f3] https://github.com/openstack/tacker/blob/master/samples/tosca-templates/vnffgd/tosca-vnffgd-sample-VNF1.yaml
|
||||
.. [#f4] https://docs.openstack.org/tacker/latest/user/vnffg_usage_guide.html
|
||||
.. [#f5] https://github.com/openstack/tacker/blob/master/samples/tosca-templates/vnffgd/tosca-vnffg-vnfd1.yaml
|
||||
.. [#f6] https://github.com/openstack/tacker/blob/master/samples/tosca-templates/vnffgd/tosca-vnffg-vnfd2.yaml
|
||||
.. [#f7] https://github.com/openstack/tacker/blob/master/samples/tosca-templates/vnffgd
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Currently, when a VNFFG is created, Tacker create new port-pairs,
|
||||
port-pair-groups, port-chains and flow-classifiers. This patch will
|
||||
let a new VNFFG use existing port-pair-group if it already existed.
|
||||
User can create several VNFFGs that can go through a VNF.
|
39
samples/tosca-templates/vnffgd/tosca-vnffgd-sample-VNF1.yaml
Normal file
39
samples/tosca-templates/vnffgd/tosca-vnffgd-sample-VNF1.yaml
Normal file
@ -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.TackerV2
|
||||
description: creates path CP12
|
||||
properties:
|
||||
id: 51
|
||||
policy:
|
||||
type: ACL
|
||||
criteria:
|
||||
- name: block_tcp
|
||||
classifier:
|
||||
network_src_port_id: 640dfd77-c92b-45a3-b8fc-22712de480e1
|
||||
destination_port_range: 8080-8080
|
||||
ip_proto: 6
|
||||
ip_dst_prefix: 192.168.1.2/24
|
||||
path:
|
||||
- forwarder: VNFD1
|
||||
capability: CP12
|
||||
|
||||
groups:
|
||||
VNFFG1:
|
||||
type: tosca.groups.nfv.VNFFG
|
||||
description: HTTP to Corporate Net
|
||||
properties:
|
||||
vendor: tacker
|
||||
version: 1.0
|
||||
number_of_endpoints: 1
|
||||
dependent_virtual_link: [VL12]
|
||||
connection_point: [CP12]
|
||||
constituent_vnfs: [VNFD1]
|
||||
members: [Forwarding_path1]
|
@ -260,6 +260,10 @@ class UpdateChainException(exceptions.TackerException):
|
||||
message = _("%(message)s")
|
||||
|
||||
|
||||
class CreateChainException(exceptions.TackerException):
|
||||
message = _("%(message)s")
|
||||
|
||||
|
||||
class UpdateClassifierException(exceptions.TackerException):
|
||||
message = _("%(message)s")
|
||||
|
||||
|
@ -394,52 +394,93 @@ class OpenStack_Driver(abstract_vim_driver.VimAbstractDriver,
|
||||
return None
|
||||
|
||||
neutronclient_ = NeutronClient(auth_attr)
|
||||
port_pairs_list = neutronclient_.port_pair_list()
|
||||
port_pair_groups_list = neutronclient_.port_pair_group_list()
|
||||
port_chains_list = neutronclient_.port_chain_list()
|
||||
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)
|
||||
new_ppgs = []
|
||||
new_pps = []
|
||||
|
||||
try:
|
||||
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
|
||||
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 not in [1, 2]:
|
||||
LOG.warning("Chain creation failed due to wrong number of "
|
||||
"connection points: expected [1 | 2], got "
|
||||
"%(cps)d", {'cps': num_cps})
|
||||
return None
|
||||
if num_cps == 1:
|
||||
ingress = cp_list[0]
|
||||
egress = cp_list[0]
|
||||
else:
|
||||
ingress = cp_list[0]
|
||||
egress = cp_list[1]
|
||||
|
||||
# valid_port_in_use function is used to find out the
|
||||
# port_pair_group_id of the existing port pair group
|
||||
# which was created by ingress and egress of current VNF
|
||||
port_pair_group_id = self.valid_port_in_use(
|
||||
ingress, egress, port_pairs_list, port_pair_groups_list)
|
||||
if not port_pair_group_id:
|
||||
# create the new port pair group if it is not existed
|
||||
port_pair = dict()
|
||||
port_pair['name'] = vnf['name'] + '-connection-points'
|
||||
port_pair['description'] = 'port pair for ' + vnf['name']
|
||||
port_pair['ingress'] = ingress
|
||||
port_pair['egress'] = egress
|
||||
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
|
||||
new_pps.append(port_pair_id)
|
||||
port_pair_group = {}
|
||||
port_pair_group['name'] = vnf['name'] + '-port-pair-group'
|
||||
port_pair_group['description'] = \
|
||||
'port pair group for ' + vnf['name']
|
||||
port_pair_group['port_pairs'] = []
|
||||
port_pair_group['port_pairs'].append(port_pair_id)
|
||||
port_pair_group_id = \
|
||||
neutronclient_.port_pair_group_create(port_pair_group)
|
||||
new_ppgs.append(port_pair_group_id)
|
||||
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']})
|
||||
raise nfvo.CreateChainException(
|
||||
message="Failed to create port-pair-group")
|
||||
port_pair_group_list.append(port_pair_group_id)
|
||||
|
||||
# Check list port pair group between new port chain and the
|
||||
# existing port chains. Networking-sfc does not allow to create
|
||||
# two port chains with the same port pair groups and the same order
|
||||
for pc in port_chains_list['port_chains']:
|
||||
ppg_list = pc['port_pair_groups']
|
||||
if ppg_list == port_pair_group_list:
|
||||
# raise exception when the Vnffg path is already existing
|
||||
raise nfvo.CreateChainException(
|
||||
message="Vnffg path already exists")
|
||||
except nfvo.CreateChainException as e:
|
||||
# clean neutron resources such as port pair, port pair group and
|
||||
# flow classifier if we create it
|
||||
for ppg in new_ppgs:
|
||||
neutronclient_.port_pair_group_delete(ppg)
|
||||
for pp in new_pps:
|
||||
neutronclient_.port_pair_delete(pp)
|
||||
for fc_id in fc_ids:
|
||||
neutronclient_.flow_classifier_delete(fc_id)
|
||||
raise e
|
||||
|
||||
# TODO(s3wong): should the chain name be given as a parameter?
|
||||
port_chain = {}
|
||||
@ -473,8 +514,12 @@ class OpenStack_Driver(abstract_vim_driver.VimAbstractDriver,
|
||||
return None
|
||||
|
||||
neutronclient_ = NeutronClient(auth_attr)
|
||||
port_pairs_list = neutronclient_.port_pair_list()
|
||||
port_pair_groups_list = neutronclient_.port_pair_group_list()
|
||||
port_chains_list = neutronclient_.port_chain_list()
|
||||
new_ppgs = []
|
||||
updated_port_chain = dict()
|
||||
|
||||
pc_info = neutronclient_.port_chain_show(chain_id)
|
||||
if set(fc_ids) != set(pc_info['port_chain']['flow_classifiers']):
|
||||
updated_port_chain['flow_classifiers'] = fc_ids
|
||||
@ -491,11 +536,6 @@ class OpenStack_Driver(abstract_vim_driver.VimAbstractDriver,
|
||||
old_ppg_id = old_ppgs_dict.pop(vnf['name'])
|
||||
new_ppgs.append(old_ppg_id)
|
||||
else:
|
||||
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 update failed due to missing "
|
||||
"connection point info in VNF "
|
||||
@ -504,30 +544,47 @@ class OpenStack_Driver(abstract_vim_driver.VimAbstractDriver,
|
||||
message="Connection point not found")
|
||||
cp_list = vnf[CONNECTION_POINT]
|
||||
num_cps = len(cp_list)
|
||||
if num_cps != 1 and num_cps != 2:
|
||||
if num_cps not in [1, 2]:
|
||||
LOG.warning("Chain update failed due to wrong number "
|
||||
"of connection points: expected [1 | 2],"
|
||||
"got %(cps)d", {'cps': num_cps})
|
||||
raise nfvo.UpdateChainException(
|
||||
message="Invalid number of connection points")
|
||||
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]
|
||||
ingress = cp_list[0]
|
||||
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 update failed due to port pair "
|
||||
"creation failed for "
|
||||
"vnf %(vnf)s", {'vnf': vnf['name']})
|
||||
raise nfvo.UpdateChainException(
|
||||
message="Failed to create port-pair")
|
||||
port_pair_group['port_pairs'].append(port_pair_id)
|
||||
port_pair_group_id = \
|
||||
neutronclient_.port_pair_group_create(port_pair_group)
|
||||
ingress = cp_list[0]
|
||||
egress = cp_list[1]
|
||||
|
||||
# valid_port_in_use function is used to find out the
|
||||
# port_pair_group_id of the existing port pair group
|
||||
# which was created by ingress and egress of current VNF
|
||||
port_pair_group_id = self.valid_port_in_use(
|
||||
ingress, egress, port_pairs_list,
|
||||
port_pair_groups_list)
|
||||
if not port_pair_group_id:
|
||||
port_pair['name'] = vnf['name'] + '-connection-points'
|
||||
port_pair['description'] = \
|
||||
'port pair for ' + vnf['name']
|
||||
port_pair['ingress'] = ingress
|
||||
port_pair['egress'] = egress
|
||||
port_pair_id = neutronclient_.port_pair_create(
|
||||
port_pair)
|
||||
if not port_pair_id:
|
||||
LOG.warning("Chain update failed due to port pair "
|
||||
"creation failed for "
|
||||
"vnf %(vnf)s", {'vnf': vnf['name']})
|
||||
raise nfvo.UpdateChainException(
|
||||
message="Failed to create port-pair")
|
||||
port_pair_group['name'] = \
|
||||
vnf['name'] + '-port-pair-group'
|
||||
port_pair_group['description'] = \
|
||||
'port pair group for ' + vnf['name']
|
||||
port_pair_group['port_pairs'] = []
|
||||
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 update failed due to port pair "
|
||||
"group creation failed for vnf "
|
||||
@ -537,8 +594,15 @@ class OpenStack_Driver(abstract_vim_driver.VimAbstractDriver,
|
||||
raise nfvo.UpdateChainException(
|
||||
message="Failed to create port-pair-group")
|
||||
new_ppgs.append(port_pair_group_id)
|
||||
for pc in port_chains_list['port_chains']:
|
||||
ppg_list = pc['port_pair_groups']
|
||||
if ppg_list == new_ppgs:
|
||||
# raise exception when the Vnffg path already exists
|
||||
nfvo.UpdateChainException(
|
||||
message="Vnffg path already exists")
|
||||
except nfvo.UpdateChainException as e:
|
||||
self._delete_ppgs_and_pps(neutronclient_, new_ppgs, past_ppgs_dict)
|
||||
self._delete_ppgs_and_pps(neutronclient_, new_ppgs,
|
||||
past_ppgs_dict, port_chains_list, fc_ids)
|
||||
raise e
|
||||
|
||||
updated_port_chain['port_pair_groups'] = new_ppgs
|
||||
@ -547,17 +611,21 @@ class OpenStack_Driver(abstract_vim_driver.VimAbstractDriver,
|
||||
pc_id = neutronclient_.port_chain_update(chain_id,
|
||||
updated_port_chain)
|
||||
except (nc_exceptions.BadRequest, nfvo.UpdateChainException) as e:
|
||||
self._delete_ppgs_and_pps(neutronclient_, new_ppgs, past_ppgs_dict)
|
||||
self._delete_ppgs_and_pps(neutronclient_, new_ppgs,
|
||||
past_ppgs_dict, port_chains_list)
|
||||
raise e
|
||||
for ppg_name in old_ppgs_dict:
|
||||
ppg_info = neutronclient_. \
|
||||
port_pair_group_show(old_ppgs_dict[ppg_name])
|
||||
neutronclient_.port_pair_group_delete(old_ppgs_dict[ppg_name])
|
||||
port_pairs = ppg_info['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]
|
||||
neutronclient_.port_pair_delete(pp_id)
|
||||
ppg_inuse = self.valid_ppg_for_multiple_chain(
|
||||
ppg_info['port_pair_group']['id'], port_chains_list)
|
||||
if not ppg_inuse:
|
||||
neutronclient_.port_pair_group_delete(old_ppgs_dict[ppg_name])
|
||||
port_pairs = ppg_info['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]
|
||||
neutronclient_.port_pair_delete(pp_id)
|
||||
return pc_id
|
||||
|
||||
def delete_chain(self, chain_id, auth_attr=None):
|
||||
@ -700,19 +768,54 @@ class OpenStack_Driver(abstract_vim_driver.VimAbstractDriver,
|
||||
return self.get_mistral_client(auth_dict) \
|
||||
.workflows.delete(workflow_id)
|
||||
|
||||
def _delete_ppgs_and_pps(self, neutronclient, new_ppgs, past_ppgs_dict):
|
||||
def _delete_ppgs_and_pps(self, neutronclient, new_ppgs,
|
||||
past_ppgs_dict, pcs_list, fc_ids):
|
||||
if new_ppgs:
|
||||
for item in new_ppgs:
|
||||
if item not in past_ppgs_dict.values():
|
||||
new_ppg_info = neutronclient.port_pair_group_show(
|
||||
item)
|
||||
neutronclient.port_pair_group_delete(item)
|
||||
new_port_pairs = new_ppg_info['port_pair_group'][
|
||||
'port_pairs']
|
||||
if new_port_pairs and len(new_port_pairs):
|
||||
for j in xrange(0, len(new_port_pairs)):
|
||||
new_pp_id = new_port_pairs[j]
|
||||
neutronclient.port_pair_delete(new_pp_id)
|
||||
ppg_inuse = self.valid_ppg_for_multiple_chain(
|
||||
item, pcs_list)
|
||||
if not ppg_inuse:
|
||||
# clean port pair and port pair group if
|
||||
# it is not in used
|
||||
new_ppg_info = neutronclient.port_pair_group_show(item)
|
||||
neutronclient.port_pair_group_delete(item)
|
||||
new_port_pairs = new_ppg_info['port_pair_group'][
|
||||
'port_pairs']
|
||||
if new_port_pairs and len(new_port_pairs):
|
||||
for j in xrange(0, len(new_port_pairs)):
|
||||
new_pp_id = new_port_pairs[j]
|
||||
neutronclient.port_pair_delete(new_pp_id)
|
||||
# clean flow classifiers
|
||||
for fc_id in fc_ids:
|
||||
neutronclient.flow_classifier_delete(fc_id)
|
||||
|
||||
def valid_port_in_use(self, ingress, egress, pps_list, ppgs_list):
|
||||
# This function checks the the ports are used or not and return the
|
||||
# port pair group id of these ports
|
||||
port_pair_list = pps_list['port_pairs']
|
||||
port_pair_group_list = ppgs_list['port_pair_groups']
|
||||
port_pair_id = None
|
||||
port_pair_group_id = None
|
||||
for pp in port_pair_list:
|
||||
if (ingress == pp['ingress']) and (egress == pp['egress']):
|
||||
port_pair_id = pp['id']
|
||||
break
|
||||
if port_pair_id:
|
||||
for ppg in port_pair_group_list:
|
||||
if port_pair_id in ppg['port_pairs']:
|
||||
port_pair_group_id = ppg['id']
|
||||
break
|
||||
return port_pair_group_id
|
||||
|
||||
def valid_ppg_for_multiple_chain(self, ppg_id, pcs_list):
|
||||
# This function returns True if a ppg belongs to more than one
|
||||
# port chain. If not return False.
|
||||
count = 0
|
||||
for pc in pcs_list['port_chains']:
|
||||
if ppg_id in pc['port_pair_groups']:
|
||||
count = count + 1
|
||||
return True if count > 1 else False
|
||||
|
||||
|
||||
class NeutronClient(object):
|
||||
@ -727,7 +830,7 @@ class NeutronClient(object):
|
||||
|
||||
def flow_classifier_show(self, fc_id):
|
||||
try:
|
||||
fc = self.client.show_flow_classifier(fc_id)
|
||||
fc = self.client.show_sfc_flow_classifier(fc_id)
|
||||
if fc is None:
|
||||
raise ValueError('classifier %s not found' % fc_id)
|
||||
return fc
|
||||
@ -768,6 +871,10 @@ class NeutronClient(object):
|
||||
else:
|
||||
return None
|
||||
|
||||
def port_pair_list(self):
|
||||
pp_list = self.client.list_sfc_port_pairs()
|
||||
return pp_list
|
||||
|
||||
def port_pair_delete(self, port_pair_id):
|
||||
try:
|
||||
self.client.delete_sfc_port_pair(port_pair_id)
|
||||
@ -788,6 +895,10 @@ class NeutronClient(object):
|
||||
else:
|
||||
return None
|
||||
|
||||
def port_pair_group_list(self):
|
||||
ppg_list = self.client.list_sfc_port_pair_groups()
|
||||
return ppg_list
|
||||
|
||||
def port_pair_group_delete(self, ppg_id):
|
||||
try:
|
||||
self.client.delete_sfc_port_pair_group(ppg_id)
|
||||
@ -813,17 +924,31 @@ class NeutronClient(object):
|
||||
port_chain = self.client.show_sfc_port_chain(port_chain_id)
|
||||
if port_chain:
|
||||
self.client.delete_sfc_port_chain(port_chain_id)
|
||||
port_chain_list = \
|
||||
self.client.list_sfc_port_chains()['port_chains']
|
||||
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_sfc_port_pair_group(ppg_list[i])
|
||||
if ppg:
|
||||
self.client.delete_sfc_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_sfc_port_pair(pp_id)
|
||||
ppg_in_use = False
|
||||
# Firstly, Tacker delete port chain, if a port pair
|
||||
# group still belong to other port chains, Tacker
|
||||
# will mark it as in_use and does not delete it.
|
||||
for pc in port_chain_list:
|
||||
if ppg_list[i] in pc['port_pair_groups']:
|
||||
ppg_in_use = True
|
||||
break
|
||||
if not ppg_in_use:
|
||||
ppg = self.client.show_sfc_port_pair_group(
|
||||
ppg_list[i])
|
||||
if ppg:
|
||||
self.client.delete_sfc_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_sfc_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)
|
||||
@ -841,6 +966,10 @@ class NeutronClient(object):
|
||||
raise nfvo.UpdateChainException(message="Failed to update "
|
||||
"port-chain")
|
||||
|
||||
def port_chain_list(self):
|
||||
pc_list = self.client.list_sfc_port_chains()
|
||||
return pc_list
|
||||
|
||||
def port_chain_show(self, port_chain_id):
|
||||
try:
|
||||
port_chain = self.client.show_sfc_port_chain(port_chain_id)
|
||||
|
@ -59,6 +59,11 @@ class FakeNeutronClient(mock.Mock):
|
||||
self.__pp_dict[pp_id] = port_pair
|
||||
return pp_id
|
||||
|
||||
def port_pair_list(self):
|
||||
pp = {'port_pairs': [{'ingress': 'abc',
|
||||
'egress': 'xyz'}]}
|
||||
return pp
|
||||
|
||||
def show_port_pair(self, port_pair_dict):
|
||||
input_pp_name = port_pair_dict['name']
|
||||
for pp_id in self.__pp_dict:
|
||||
@ -73,6 +78,10 @@ class FakeNeutronClient(mock.Mock):
|
||||
self.__ppg_dict[ppg_id] = port_pair_group
|
||||
return ppg_id
|
||||
|
||||
def port_pair_group_list(self):
|
||||
value = {'port_pair_groups': []}
|
||||
return value
|
||||
|
||||
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:
|
||||
@ -100,6 +109,11 @@ class FakeNeutronClient(mock.Mock):
|
||||
raise ValueError('port chain delete failed')
|
||||
self.__chain_dict.pop(chain_id)
|
||||
|
||||
def port_chain_list(self):
|
||||
pc = {'port_chains': [{'port_pair_groups': ['random_id1',
|
||||
'random_id2']}]}
|
||||
return pc
|
||||
|
||||
|
||||
class TestChainSFC(base.TestCase):
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user