Add reusing feature when creating VNFFG.

Currently, when a VNFFG is created, Tacker also create port-pair,
port-pair-group, port-chain and flow-classifier. There are no
reusing ability in this approach.

This patch will check the port-id of a VNF is using in other VNFFG
or not. If it is used in a VNFFG, the existing port-pair-group will
be added to the new port-chain.

Therefore, we can create 2 VNFFGs that can go through one VNF.

This patch also clean up network resource such as port pair, port
pair group and flow classifier if there are failures.

Change-Id: I648693103dd7eab7a4b10cde89f11766a9232e8d
Closes-Bug: #1746721
This commit is contained in:
Cong Phuoc Hoang 2018-03-20 16:44:05 +09:00
parent 0adfefadab
commit d07d5448d3
6 changed files with 534 additions and 94 deletions

View 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

View File

@ -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.

View 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]

View File

@ -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")

View File

@ -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)

View File

@ -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):