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")
|
message = _("%(message)s")
|
||||||
|
|
||||||
|
|
||||||
|
class CreateChainException(exceptions.TackerException):
|
||||||
|
message = _("%(message)s")
|
||||||
|
|
||||||
|
|
||||||
class UpdateClassifierException(exceptions.TackerException):
|
class UpdateClassifierException(exceptions.TackerException):
|
||||||
message = _("%(message)s")
|
message = _("%(message)s")
|
||||||
|
|
||||||
|
@ -394,17 +394,19 @@ class OpenStack_Driver(abstract_vim_driver.VimAbstractDriver,
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
neutronclient_ = NeutronClient(auth_attr)
|
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 = []
|
port_pair_group_list = []
|
||||||
|
new_ppgs = []
|
||||||
|
new_pps = []
|
||||||
|
|
||||||
|
try:
|
||||||
for vnf in vnfs:
|
for vnf in vnfs:
|
||||||
# TODO(s3wong): once scaling is in place and VNFFG supports it
|
# TODO(s3wong): once scaling is in place and VNFFG supports it
|
||||||
# that model needs to be implemented to concatenate all
|
# that model needs to be implemented to concatenate all
|
||||||
# port-pairs into the port-pair-group
|
# port-pairs into the port-pair-group
|
||||||
# port pair group could include port-pairs from different VNFs
|
# 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:
|
if CONNECTION_POINT not in vnf:
|
||||||
LOG.warning("Chain creation failed due to missing "
|
LOG.warning("Chain creation failed due to missing "
|
||||||
"connection point info in VNF "
|
"connection point info in VNF "
|
||||||
@ -412,35 +414,74 @@ class OpenStack_Driver(abstract_vim_driver.VimAbstractDriver,
|
|||||||
return None
|
return None
|
||||||
cp_list = vnf[CONNECTION_POINT]
|
cp_list = vnf[CONNECTION_POINT]
|
||||||
num_cps = len(cp_list)
|
num_cps = len(cp_list)
|
||||||
if num_cps != 1 and num_cps != 2:
|
if num_cps not in [1, 2]:
|
||||||
LOG.warning("Chain creation failed due to wrong number of "
|
LOG.warning("Chain creation failed due to wrong number of "
|
||||||
"connection points: expected [1 | 2], got "
|
"connection points: expected [1 | 2], got "
|
||||||
"%(cps)d", {'cps': num_cps})
|
"%(cps)d", {'cps': num_cps})
|
||||||
return None
|
return None
|
||||||
port_pair = {}
|
|
||||||
port_pair['name'] = vnf['name'] + '-connection-points'
|
|
||||||
port_pair['description'] = 'port pair for %s' % vnf['name']
|
|
||||||
if num_cps == 1:
|
if num_cps == 1:
|
||||||
port_pair['ingress'] = cp_list[0]
|
ingress = cp_list[0]
|
||||||
port_pair['egress'] = cp_list[0]
|
egress = cp_list[0]
|
||||||
else:
|
else:
|
||||||
port_pair['ingress'] = cp_list[0]
|
ingress = cp_list[0]
|
||||||
port_pair['egress'] = cp_list[1]
|
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)
|
port_pair_id = neutronclient_.port_pair_create(port_pair)
|
||||||
if not port_pair_id:
|
if not port_pair_id:
|
||||||
LOG.warning("Chain creation failed due to port pair creation"
|
LOG.warning("Chain creation failed due to port pair"
|
||||||
" failed for vnf %(vnf)s", {'vnf': vnf['name']})
|
"creation failed for vnf %(vnf)s",
|
||||||
|
{'vnf': vnf['name']})
|
||||||
return None
|
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['port_pairs'].append(port_pair_id)
|
||||||
port_pair_group_id = \
|
port_pair_group_id = \
|
||||||
neutronclient_.port_pair_group_create(port_pair_group)
|
neutronclient_.port_pair_group_create(port_pair_group)
|
||||||
|
new_ppgs.append(port_pair_group_id)
|
||||||
if not port_pair_group_id:
|
if not port_pair_group_id:
|
||||||
LOG.warning("Chain creation failed due to port pair group "
|
LOG.warning("Chain creation failed due to port pair group "
|
||||||
"creation failed for vnf "
|
"creation failed for vnf "
|
||||||
"%(vnf)s", {'vnf': vnf['name']})
|
"%(vnf)s", {'vnf': vnf['name']})
|
||||||
return None
|
raise nfvo.CreateChainException(
|
||||||
|
message="Failed to create port-pair-group")
|
||||||
port_pair_group_list.append(port_pair_group_id)
|
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?
|
# TODO(s3wong): should the chain name be given as a parameter?
|
||||||
port_chain = {}
|
port_chain = {}
|
||||||
port_chain['name'] = name + '-port-chain'
|
port_chain['name'] = name + '-port-chain'
|
||||||
@ -473,8 +514,12 @@ class OpenStack_Driver(abstract_vim_driver.VimAbstractDriver,
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
neutronclient_ = NeutronClient(auth_attr)
|
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 = []
|
new_ppgs = []
|
||||||
updated_port_chain = dict()
|
updated_port_chain = dict()
|
||||||
|
|
||||||
pc_info = neutronclient_.port_chain_show(chain_id)
|
pc_info = neutronclient_.port_chain_show(chain_id)
|
||||||
if set(fc_ids) != set(pc_info['port_chain']['flow_classifiers']):
|
if set(fc_ids) != set(pc_info['port_chain']['flow_classifiers']):
|
||||||
updated_port_chain['flow_classifiers'] = fc_ids
|
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'])
|
old_ppg_id = old_ppgs_dict.pop(vnf['name'])
|
||||||
new_ppgs.append(old_ppg_id)
|
new_ppgs.append(old_ppg_id)
|
||||||
else:
|
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:
|
if CONNECTION_POINT not in vnf:
|
||||||
LOG.warning("Chain update failed due to missing "
|
LOG.warning("Chain update failed due to missing "
|
||||||
"connection point info in VNF "
|
"connection point info in VNF "
|
||||||
@ -504,30 +544,47 @@ class OpenStack_Driver(abstract_vim_driver.VimAbstractDriver,
|
|||||||
message="Connection point not found")
|
message="Connection point not found")
|
||||||
cp_list = vnf[CONNECTION_POINT]
|
cp_list = vnf[CONNECTION_POINT]
|
||||||
num_cps = len(cp_list)
|
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 "
|
LOG.warning("Chain update failed due to wrong number "
|
||||||
"of connection points: expected [1 | 2],"
|
"of connection points: expected [1 | 2],"
|
||||||
"got %(cps)d", {'cps': num_cps})
|
"got %(cps)d", {'cps': num_cps})
|
||||||
raise nfvo.UpdateChainException(
|
raise nfvo.UpdateChainException(
|
||||||
message="Invalid number of connection points")
|
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:
|
if num_cps == 1:
|
||||||
port_pair['ingress'] = cp_list[0]
|
ingress = cp_list[0]
|
||||||
port_pair['egress'] = cp_list[0]
|
egress = cp_list[0]
|
||||||
else:
|
else:
|
||||||
port_pair['ingress'] = cp_list[0]
|
ingress = cp_list[0]
|
||||||
port_pair['egress'] = cp_list[1]
|
egress = cp_list[1]
|
||||||
port_pair_id = neutronclient_.port_pair_create(port_pair)
|
|
||||||
|
# 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:
|
if not port_pair_id:
|
||||||
LOG.warning("Chain update failed due to port pair "
|
LOG.warning("Chain update failed due to port pair "
|
||||||
"creation failed for "
|
"creation failed for "
|
||||||
"vnf %(vnf)s", {'vnf': vnf['name']})
|
"vnf %(vnf)s", {'vnf': vnf['name']})
|
||||||
raise nfvo.UpdateChainException(
|
raise nfvo.UpdateChainException(
|
||||||
message="Failed to create port-pair")
|
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['port_pairs'].append(port_pair_id)
|
||||||
port_pair_group_id = \
|
port_pair_group_id = neutronclient_.\
|
||||||
neutronclient_.port_pair_group_create(port_pair_group)
|
port_pair_group_create(port_pair_group)
|
||||||
if not port_pair_group_id:
|
if not port_pair_group_id:
|
||||||
LOG.warning("Chain update failed due to port pair "
|
LOG.warning("Chain update failed due to port pair "
|
||||||
"group creation failed for vnf "
|
"group creation failed for vnf "
|
||||||
@ -537,8 +594,15 @@ class OpenStack_Driver(abstract_vim_driver.VimAbstractDriver,
|
|||||||
raise nfvo.UpdateChainException(
|
raise nfvo.UpdateChainException(
|
||||||
message="Failed to create port-pair-group")
|
message="Failed to create port-pair-group")
|
||||||
new_ppgs.append(port_pair_group_id)
|
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:
|
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
|
raise e
|
||||||
|
|
||||||
updated_port_chain['port_pair_groups'] = new_ppgs
|
updated_port_chain['port_pair_groups'] = new_ppgs
|
||||||
@ -547,11 +611,15 @@ class OpenStack_Driver(abstract_vim_driver.VimAbstractDriver,
|
|||||||
pc_id = neutronclient_.port_chain_update(chain_id,
|
pc_id = neutronclient_.port_chain_update(chain_id,
|
||||||
updated_port_chain)
|
updated_port_chain)
|
||||||
except (nc_exceptions.BadRequest, nfvo.UpdateChainException) as e:
|
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
|
raise e
|
||||||
for ppg_name in old_ppgs_dict:
|
for ppg_name in old_ppgs_dict:
|
||||||
ppg_info = neutronclient_. \
|
ppg_info = neutronclient_. \
|
||||||
port_pair_group_show(old_ppgs_dict[ppg_name])
|
port_pair_group_show(old_ppgs_dict[ppg_name])
|
||||||
|
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])
|
neutronclient_.port_pair_group_delete(old_ppgs_dict[ppg_name])
|
||||||
port_pairs = ppg_info['port_pair_group']['port_pairs']
|
port_pairs = ppg_info['port_pair_group']['port_pairs']
|
||||||
if port_pairs and len(port_pairs):
|
if port_pairs and len(port_pairs):
|
||||||
@ -700,12 +768,17 @@ class OpenStack_Driver(abstract_vim_driver.VimAbstractDriver,
|
|||||||
return self.get_mistral_client(auth_dict) \
|
return self.get_mistral_client(auth_dict) \
|
||||||
.workflows.delete(workflow_id)
|
.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:
|
if new_ppgs:
|
||||||
for item in new_ppgs:
|
for item in new_ppgs:
|
||||||
if item not in past_ppgs_dict.values():
|
if item not in past_ppgs_dict.values():
|
||||||
new_ppg_info = neutronclient.port_pair_group_show(
|
ppg_inuse = self.valid_ppg_for_multiple_chain(
|
||||||
item)
|
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)
|
neutronclient.port_pair_group_delete(item)
|
||||||
new_port_pairs = new_ppg_info['port_pair_group'][
|
new_port_pairs = new_ppg_info['port_pair_group'][
|
||||||
'port_pairs']
|
'port_pairs']
|
||||||
@ -713,6 +786,36 @@ class OpenStack_Driver(abstract_vim_driver.VimAbstractDriver,
|
|||||||
for j in xrange(0, len(new_port_pairs)):
|
for j in xrange(0, len(new_port_pairs)):
|
||||||
new_pp_id = new_port_pairs[j]
|
new_pp_id = new_port_pairs[j]
|
||||||
neutronclient.port_pair_delete(new_pp_id)
|
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):
|
class NeutronClient(object):
|
||||||
@ -727,7 +830,7 @@ class NeutronClient(object):
|
|||||||
|
|
||||||
def flow_classifier_show(self, fc_id):
|
def flow_classifier_show(self, fc_id):
|
||||||
try:
|
try:
|
||||||
fc = self.client.show_flow_classifier(fc_id)
|
fc = self.client.show_sfc_flow_classifier(fc_id)
|
||||||
if fc is None:
|
if fc is None:
|
||||||
raise ValueError('classifier %s not found' % fc_id)
|
raise ValueError('classifier %s not found' % fc_id)
|
||||||
return fc
|
return fc
|
||||||
@ -768,6 +871,10 @@ class NeutronClient(object):
|
|||||||
else:
|
else:
|
||||||
return None
|
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):
|
def port_pair_delete(self, port_pair_id):
|
||||||
try:
|
try:
|
||||||
self.client.delete_sfc_port_pair(port_pair_id)
|
self.client.delete_sfc_port_pair(port_pair_id)
|
||||||
@ -788,6 +895,10 @@ class NeutronClient(object):
|
|||||||
else:
|
else:
|
||||||
return None
|
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):
|
def port_pair_group_delete(self, ppg_id):
|
||||||
try:
|
try:
|
||||||
self.client.delete_sfc_port_pair_group(ppg_id)
|
self.client.delete_sfc_port_pair_group(ppg_id)
|
||||||
@ -813,13 +924,27 @@ class NeutronClient(object):
|
|||||||
port_chain = self.client.show_sfc_port_chain(port_chain_id)
|
port_chain = self.client.show_sfc_port_chain(port_chain_id)
|
||||||
if port_chain:
|
if port_chain:
|
||||||
self.client.delete_sfc_port_chain(port_chain_id)
|
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')
|
ppg_list = port_chain['port_chain'].get('port_pair_groups')
|
||||||
if ppg_list and len(ppg_list):
|
if ppg_list and len(ppg_list):
|
||||||
for i in xrange(0, len(ppg_list)):
|
for i in xrange(0, len(ppg_list)):
|
||||||
ppg = self.client.show_sfc_port_pair_group(ppg_list[i])
|
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:
|
if ppg:
|
||||||
self.client.delete_sfc_port_pair_group(ppg_list[i])
|
self.client.delete_sfc_port_pair_group(
|
||||||
port_pairs = ppg['port_pair_group']['port_pairs']
|
ppg_list[i])
|
||||||
|
port_pairs = \
|
||||||
|
ppg['port_pair_group']['port_pairs']
|
||||||
if port_pairs and len(port_pairs):
|
if port_pairs and len(port_pairs):
|
||||||
for j in xrange(0, len(port_pairs)):
|
for j in xrange(0, len(port_pairs)):
|
||||||
pp_id = port_pairs[j]
|
pp_id = port_pairs[j]
|
||||||
@ -841,6 +966,10 @@ class NeutronClient(object):
|
|||||||
raise nfvo.UpdateChainException(message="Failed to update "
|
raise nfvo.UpdateChainException(message="Failed to update "
|
||||||
"port-chain")
|
"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):
|
def port_chain_show(self, port_chain_id):
|
||||||
try:
|
try:
|
||||||
port_chain = self.client.show_sfc_port_chain(port_chain_id)
|
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
|
self.__pp_dict[pp_id] = port_pair
|
||||||
return pp_id
|
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):
|
def show_port_pair(self, port_pair_dict):
|
||||||
input_pp_name = port_pair_dict['name']
|
input_pp_name = port_pair_dict['name']
|
||||||
for pp_id in self.__pp_dict:
|
for pp_id in self.__pp_dict:
|
||||||
@ -73,6 +78,10 @@ class FakeNeutronClient(mock.Mock):
|
|||||||
self.__ppg_dict[ppg_id] = port_pair_group
|
self.__ppg_dict[ppg_id] = port_pair_group
|
||||||
return ppg_id
|
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):
|
def show_port_pair_group(self, port_pair_group_dict):
|
||||||
input_ppg_name = port_pair_group_dict['name']
|
input_ppg_name = port_pair_group_dict['name']
|
||||||
for ppg_id in self.__ppg_dict:
|
for ppg_id in self.__ppg_dict:
|
||||||
@ -100,6 +109,11 @@ class FakeNeutronClient(mock.Mock):
|
|||||||
raise ValueError('port chain delete failed')
|
raise ValueError('port chain delete failed')
|
||||||
self.__chain_dict.pop(chain_id)
|
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):
|
class TestChainSFC(base.TestCase):
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user