Add support SFC encapsulation from networking-sfc

This feature allows choosing SFC encapsulation between MPLS
and NSH (default MPLS). It also apply SFC proxy to make use
of correlation.

Change-Id: Ia5eda02df415c9e3f6f035068176d60a14ffb68f
This commit is contained in:
Cong Phuoc Hoang 2018-11-29 04:14:24 +09:00
parent 4b9bcfeeef
commit 0e7fffbc77
12 changed files with 324 additions and 59 deletions

View File

@ -0,0 +1,56 @@
#!/bin/bash
#
# 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.
echo "Deleting VNF forwarding graph VNFFG1"
for vnffg in VNFFG1; do
vnffg_id=$(openstack vnf graph list | grep $vnffg | awk '{print $2}')
if [ -n "$vnffg_id" ]; then
openstack vnf graph delete $vnffg_id
fi
done
sleep 5
echo "Deleting VNFs"
for vnf_name in VNF1 VNF2; do
vnf_id=$(openstack vnf list | grep $vnf_name | awk '{print $2}')
if [ -n "$vnf_id" ]; then
openstack vnf delete $vnf_id
fi
done
echo "Deleting VNF descriptors"
for vnfd_name in VNFD1 VNFD2; do
vnfd_id=$(openstack vnf descriptor list | grep $vnfd_name | awk '{print $2}')
if [ -n "$vnfd_id" ]; then
openstack vnf descriptor delete $vnfd_id
fi
done
echo "Deleting http_client and http_server"
for server_name in http_client http_server; do
server_id=$(openstack server list | grep $server_name | awk '{print $2}')
if [ -n "$server_id" ]; then
openstack server delete $server_id
fi
done
sleep 5
echo "Deleting VIM0"
vim_id=$(openstack vim list | grep VIM0 | awk '{print $2}')
if [ -n "$vim_id" ]; then
openstack vim delete $vim_id
fi

View File

@ -0,0 +1,64 @@
#!/bin/bash
#
# 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.
network_name='net0'
network_id=$(openstack network list | grep $network_name | awk '{print $2}')
if [ -z "$network_id" ]; then
echo "Creating network net0"
openstack network create $network_name --provider-network-type=vxlan --provider-segment 1005
openstack subnet create --network $network_name --subnet-range 10.0.10.0/24 subnet-test
network_id=$(openstack network list | grep $network_name | awk '{print $2}')
fi
echo "Creating HTTP client"
openstack server create --flavor m1.tiny --image cirros-0.4.0-x86_64-disk --nic net-id=$network_id http_client
echo "Creating HTTP server"
openstack server create --flavor m1.tiny --image cirros-0.4.0-x86_64-disk --nic net-id=$network_id http_server
sleep 15
ip_src=$(openstack server list | grep http_client | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+')
network_source_port_id=$(openstack port list | grep $ip_src | awk '{print $2}')
ip_dst=$(openstack server list | grep http_server | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+')
network_dest_port_id=$(openstack port list | grep $ip_dst | awk '{print $2}')
echo "Creating/ Updating ns_param.yaml file"
cat > ../../samples/tosca-templates/vnffgd/vnffg-param-file.yaml << EOL
net_src_port_id: ${network_source_port_id}
ip_dst_pre: ${ip_dst}/24
net_dst_port_id: ${network_dest_port_id}
dst_port_range: 80-80
EOL
vim_default=$(openstack vim list | grep openstack | awk '{print $10}')
if [ "$vim_default" != "True" ]; then
echo "Creating default VIM"
cat > ./vim_config.yaml << EOL
auth_url: $OS_AUTH_URL
username: $OS_USERNAME
password: $OS_PASSWORD
project_name: $OS_PROJECT_NAME
project_domain_name: $OS_PROJECT_DOMAIN_ID
user_domain_name: $OS_USER_DOMAIN_ID
EOL
openstack vim register --config-file vim_config.yaml --is-default VIM0
rm ./vim_config.yaml
fi
echo "Create VNF1 and VNF2"
openstack vnf descriptor create --vnfd-file ../../samples/tosca-templates/vnffgd/tosca-vnffg-vnfd1.yaml VNFD1
openstack vnf create --vnfd-name VNFD1 --vim-name VIM0 VNF1
openstack vnf descriptor create --vnfd-file ../../samples/tosca-templates/vnffgd/tosca-vnffg-vnfd2.yaml VNFD2
openstack vnf create --vnfd-name VNFD2 --vim-name VIM0 VNF2

View File

@ -121,6 +121,7 @@ without "symmetrical", you can ommit "network_dst_port_id" and "ip_dst_prefix".
properties:
id: 51
symmetrical: true
correlation: nsh
policy:
type: ACL
criteria:
@ -134,14 +135,24 @@ without "symmetrical", you can ommit "network_dst_port_id" and "ip_dst_prefix".
path:
- forwarder: VNFD1
capability: CP12
sfc_encap: True
- forwarder: VNFD2
capability: CP22
sfc_encap: False
In above template, users can set **symmetrical** in properties of a forwarding
path create symmetrical VNFFG. If this property is not set, **symmetrical**
will be specified by **--symmetrical** in create VNFFG command (default value
is False).
In other hand, by setting **correlation** in properties let users can choose
SFC encapsulation between MPLS or NSH (default: MPLS). If sfc_encap is True,
port pair's correlation is set to same value with **correlation** to make use
of **correlation** that SFC Encapsulation provides, otherwise port pair's
correlation is set to None to install SFC proxy `SFC_PROXY`_. Detailed
information about SFC encapsulation can be found in Networking-SFC project
`SFC_ENCAPSULATION`_
You can use the sample VNFFGD template for symmetrical feature (in port chain)
such as this `link <https://github.com/openstack/tacker/tree/master/samples/
tosca-templates/vnffgd/tosca-vnffgd-symmetrical-sample.yaml>`_.
@ -298,6 +309,20 @@ Using the below command query the list of existing VNFFG templates.
| 95d9-0199253db72e | myvnffg | ACTIVE | bd7829bf-85de-4f3b-960a-8482028bfb34|
+--------------------+---------+--------+-------------+--------+--------------+
To verify result, user can get information of port chain in networking-sfc:
.. code-block:: console
$ openstack sfc port chain list --fit-width
+-----------------------+-------------------+-----------------------+-----------------------+-----------------------+----------+
| ID | Name | Port Pair Groups | Flow Classifiers | Chain Parameters | Chain ID |
+-----------------------+-------------------+-----------------------+-----------------------+-----------------------+----------+
| 60d1a3ee-8455-415e- | VNFFG1-port-chain | [u'87d7d4c2-d2d1-4a99 | [u'02fa422c-9f40-4092 | {u'symmetric': False, | 51 |
| 8ff1-e0b6b9f9f277 | | -81fb-f3d5f51dd919', | -a8b0-c04355116e5e'] | u'correlation': | |
| | | u'81213b4c-0e5e-445d- | | u'nsh'} | |
| | | add6-dea2bf55078f'] | | | |
+-----------------------+-------------------+-----------------------+-----------------------+-----------------------+----------+
After the user located the VNFFG the subsequent action is to update it.
Based on the appropriate choice, update VNFFG template.
@ -394,6 +419,7 @@ By using the below VNFFGD template we can update the exisitng VNFFG.
properties:
id: 52
symmetrical: false
correlation: nsh
policy:
type: ACL
criteria:
@ -406,8 +432,10 @@ By using the below VNFFGD template we can update the exisitng VNFFG.
path:
- forwarder: VNFD1
capability: CP1
sfc_encap: True
- forwarder: VNFD2
capability: CP2
sfc_encap: False
groups:
VNFFG1:
@ -458,3 +486,5 @@ Known Issues and Limitations
.. _VNF1: https://github.com/openstack/tacker/blob/master/samples/tosca-templates/vnffgd/tosca-vnffg-vnfd1.yaml
.. _VNF2: https://github.com/openstack/tacker/blob/master/samples/tosca-templates/vnffgd/tosca-vnffg-vnfd2.yaml
.. _SFC_PROXY: https://tools.ietf.org/html/rfc8300
.. _SFC_ENCAPSULATION: https://docs.openstack.org/networking-sfc/latest/contributor/ietf_sfc_encapsulation.html

View File

@ -0,0 +1,6 @@
---
features:
- |
Add support SFC encapsulation from networking-sfc. This feature allows
choosing SFC encapsulation between MPLS and NSH (default MPLS). It also
apply SFC proxy to make use of correlation.

View File

@ -25,6 +25,7 @@ topology_template:
user_data_format: RAW
user_data: |
#!/bin/sh
echo 1 > /proc/sys/net/ipv4/ip_forward
cat << EOF >> /etc/network/interfaces
auto eth1
iface eth1 inet dhcp

View File

@ -25,6 +25,7 @@ topology_template:
user_data_format: RAW
user_data: |
#!/bin/sh
echo 1 > /proc/sys/net/ipv4/ip_forward
cat << EOF >> /etc/network/interfaces
auto eth1
iface eth1 inet dhcp

View File

@ -0,0 +1,61 @@
tosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0
description: Sample VNFFG template
topology_template:
inputs:
net_src_port_id:
type: string
description: Port UUID of source VM.
dst_port_range:
type: string
description: Destination port range
ip_dst_pre:
type: string
description: Cidr format of destination ip.
net_dst_port_id:
type: string
description: Port UUID of dest VM.
node_templates:
Forwarding_path1:
type: tosca.nodes.nfv.FP.TackerV2
description: creates path (CP12->CP22)
properties:
id: 51
correlation: mpls
policy:
type: ACL
criteria:
- name: block_tcp
classifier:
network_src_port_id: { get_input: net_src_port_id }
destination_port_range: { get_input: dst_port_range }
ip_proto: 6
ip_dst_prefix: { get_input: ip_dst_pre }
network_dst_port_id: { get_input: net_dst_port_id }
path:
- forwarder: VNFD1
capability: CP12
sfc_encap: True
- forwarder: VNFD2
capability: CP22
sfc_encap: True
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]

View File

@ -502,8 +502,10 @@ class VnffgPluginDbMixin(vnffg.VNFFGPluginBase, db_base.CommonDbMixin):
'capability'], vnf_id=vnf_mapping[element['forwarder']])
# Check if this is a new VNF entry in the chain
if element['forwarder'] != prev_forwarder:
chain_list.append({'name': vnf_info['name'],
CP: [vnf_cp]})
chain_list.append(
{'name': vnf_info['name'],
CP: [vnf_cp],
'sfc_encap': element.get('sfc_encap', True)})
prev_forwarder = element['forwarder']
# Must be an egress CP
else:
@ -512,7 +514,6 @@ class VnffgPluginDbMixin(vnffg.VNFFGPluginBase, db_base.CommonDbMixin):
'forwarder'])
else:
chain_list[-1][CP].append(vnf_cp)
return chain_list
@staticmethod
@ -1339,6 +1340,14 @@ class VnffgPluginDbMixin(vnffg.VNFFGPluginBase, db_base.CommonDbMixin):
self.delete_vnffgd(context, vnffgd_id)
def _get_symmetrical_template(self, context, vnffg):
fp_prop = self._get_fp_properties(context, vnffg)
return fp_prop.get('symmetrical', False)
def _get_correlation_template(self, context, vnffg):
fp_prop = self._get_fp_properties(context, vnffg)
return fp_prop.get('correlation', 'mpls')
def _get_fp_properties(self, context, vnffg):
vnffgd_topo = None
if vnffg.get('vnffgd_template'):
vnffgd_topo = vnffg['vnffgd_template']['topology_template']
@ -1349,7 +1358,7 @@ class VnffgPluginDbMixin(vnffg.VNFFGPluginBase, db_base.CommonDbMixin):
vnffg_name = list(vnffgd_topo['groups'].keys())[0]
nfp_name = vnffgd_topo['groups'][vnffg_name]['members'][0]
fp_prop = vnffgd_topo['node_templates'][nfp_name]['properties']
return fp_prop.get('symmetrical', None)
return fp_prop
def _make_template_dict(self, template, fields=None):
res = {}

View File

@ -84,6 +84,7 @@ FC_MAP = {'name': 'name',
'network_dst_port_id': 'logical_destination_port'}
CONNECTION_POINT = 'connection_points'
SFC_ENCAP = 'sfc_encap'
def config_opts():
@ -386,7 +387,7 @@ class OpenStack_Driver(abstract_vim_driver.VimAbstractDriver,
raise ValueError('empty match field for input flow classifier')
def create_chain(self, name, path_id, fc_ids, vnfs, symmetrical=False,
auth_attr=None):
correlation='mpls', auth_attr=None):
if not auth_attr:
LOG.warning("auth information required for n-sfc driver")
return None
@ -424,6 +425,12 @@ class OpenStack_Driver(abstract_vim_driver.VimAbstractDriver,
ingress = cp_list[0]
egress = cp_list[1]
# If sfc_encap is True, pp_corr is set to correlation to
# make use of correlation, otherwise pp_corr is set to None
# to install SFC proxy
sfc_encap = vnf.get(SFC_ENCAP, True)
pp_corr = correlation if sfc_encap else None
# 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
@ -436,6 +443,8 @@ class OpenStack_Driver(abstract_vim_driver.VimAbstractDriver,
port_pair['description'] = 'port pair for ' + vnf['name']
port_pair['ingress'] = ingress
port_pair['egress'] = egress
port_pair['service_function_parameters'] = {
'correlation': pp_corr}
port_pair_id = neutronclient_.port_pair_create(port_pair)
if not port_pair_id:
LOG.warning("Chain creation failed due to port pair"
@ -488,9 +497,9 @@ class OpenStack_Driver(abstract_vim_driver.VimAbstractDriver,
port_chain['description'] = 'port-chain for Tacker VNFFG'
port_chain['port_pair_groups'] = port_pair_group_list
port_chain['flow_classifiers'] = fc_ids
if symmetrical:
port_chain['chain_parameters'] = {}
port_chain['chain_parameters']['symmetric'] = True
port_chain['chain_parameters'] = {}
port_chain['chain_parameters']['symmetric'] = symmetrical
port_chain['chain_parameters']['correlation'] = correlation
return neutronclient_.port_chain_create(port_chain)
def update_chain(self, chain_id, fc_ids, vnfs,

View File

@ -345,11 +345,13 @@ class NfvoPlugin(nfvo_db_plugin.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin,
name_match_list.append(classifier_dict)
# grab the first VNF to check it's VIM type
# we have already checked that all VNFs are in the same VIM
vim_obj = self._get_vim_from_vnf(context,
list(vnffg_dict[
'vnf_mapping'].values())[0])
vim_obj = self._get_vim_from_vnf(
context, list(vnffg_dict['vnf_mapping'].values())[0])
# TODO(trozet): figure out what auth info we actually need to pass
# to the driver. Is it a session, or is full vim obj good enough?
correlation = super(NfvoPlugin, self)._get_correlation_template(
context, vnffg_info)
driver_type = vim_obj['type']
try:
fc_ids = []
@ -366,6 +368,7 @@ class NfvoPlugin(nfvo_db_plugin.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin,
vnfs=sfc['chain'],
fc_ids=fc_ids,
symmetrical=sfc['symmetrical'],
correlation=correlation,
auth_attr=vim_obj['auth_cred'])
except Exception:
with excutils.save_and_reraise_exception():

View File

@ -651,14 +651,15 @@ class TestNfvoPlugin(db_base.SqlTestCase):
self.assertIn('id', result)
self.assertIn('status', result)
self.assertEqual('PENDING_CREATE', result['status'])
self._driver_manager.invoke.assert_called_with(mock.ANY, mock.ANY,
name=mock.ANY,
path_id=mock.ANY,
vnfs=mock.ANY,
fc_ids=mock.ANY,
auth_attr=mock.ANY,
symmetrical=mock.ANY
)
self._driver_manager.invoke.assert_called_with(
mock.ANY, mock.ANY,
name=mock.ANY,
path_id=mock.ANY,
vnfs=mock.ANY,
fc_ids=mock.ANY,
auth_attr=mock.ANY,
symmetrical=mock.ANY,
correlation=mock.ANY)
def test_create_vnffg_abstract_types(self):
with patch.object(TackerManager, 'get_service_plugins') as \
@ -673,14 +674,15 @@ class TestNfvoPlugin(db_base.SqlTestCase):
self.assertIn('id', result)
self.assertIn('status', result)
self.assertEqual('PENDING_CREATE', result['status'])
self._driver_manager.invoke.assert_called_with(mock.ANY, mock.ANY,
name=mock.ANY,
path_id=mock.ANY,
vnfs=mock.ANY,
fc_ids=mock.ANY,
auth_attr=mock.ANY,
symmetrical=mock.ANY
)
self._driver_manager.invoke.assert_called_with(
mock.ANY, mock.ANY,
name=mock.ANY,
path_id=mock.ANY,
vnfs=mock.ANY,
fc_ids=mock.ANY,
auth_attr=mock.ANY,
symmetrical=mock.ANY,
correlation=mock.ANY)
@mock.patch('tacker.nfvo.nfvo_plugin.NfvoPlugin.create_vnffgd')
def test_create_vnffg_abstract_types_inline(self, mock_create_vnffgd):
@ -700,14 +702,15 @@ class TestNfvoPlugin(db_base.SqlTestCase):
self.assertEqual('PENDING_CREATE', result['status'])
self.assertEqual('dummy_vnffg_inline', result['name'])
mock_create_vnffgd.assert_called_once_with(mock.ANY, mock.ANY)
self._driver_manager.invoke.assert_called_with(mock.ANY, mock.ANY,
name=mock.ANY,
path_id=mock.ANY,
vnfs=mock.ANY,
fc_ids=mock.ANY,
auth_attr=mock.ANY,
symmetrical=mock.ANY
)
self._driver_manager.invoke.assert_called_with(
mock.ANY, mock.ANY,
name=mock.ANY,
path_id=mock.ANY,
vnfs=mock.ANY,
fc_ids=mock.ANY,
auth_attr=mock.ANY,
symmetrical=mock.ANY,
correlation=mock.ANY)
def test_create_vnffg_param_values(self):
with patch.object(TackerManager, 'get_service_plugins') as \
@ -722,14 +725,15 @@ class TestNfvoPlugin(db_base.SqlTestCase):
self.assertIn('id', result)
self.assertIn('status', result)
self.assertEqual('PENDING_CREATE', result['status'])
self._driver_manager.invoke.assert_called_with(mock.ANY, mock.ANY,
name=mock.ANY,
path_id=mock.ANY,
vnfs=mock.ANY,
fc_ids=mock.ANY,
auth_attr=mock.ANY,
symmetrical=mock.ANY
)
self._driver_manager.invoke.assert_called_with(
mock.ANY, mock.ANY,
name=mock.ANY,
path_id=mock.ANY,
vnfs=mock.ANY,
fc_ids=mock.ANY,
auth_attr=mock.ANY,
symmetrical=mock.ANY,
correlation=mock.ANY)
def test_create_vnffg_no_classifier(self):
with patch.object(TackerManager, 'get_service_plugins') as \
@ -744,14 +748,15 @@ class TestNfvoPlugin(db_base.SqlTestCase):
self.assertIn('id', result)
self.assertIn('status', result)
self.assertEqual('PENDING_CREATE', result['status'])
self._driver_manager.invoke.assert_called_with(mock.ANY, mock.ANY,
name=mock.ANY,
path_id=mock.ANY,
vnfs=mock.ANY,
fc_ids=mock.ANY,
auth_attr=mock.ANY,
symmetrical=mock.ANY
)
self._driver_manager.invoke.assert_called_with(
mock.ANY, mock.ANY,
name=mock.ANY,
path_id=mock.ANY,
vnfs=mock.ANY,
fc_ids=mock.ANY,
auth_attr=mock.ANY,
symmetrical=mock.ANY,
correlation=mock.ANY)
def test_create_vnffg_param_value_format_error(self):
with patch.object(TackerManager, 'get_service_plugins') as \
@ -786,14 +791,16 @@ class TestNfvoPlugin(db_base.SqlTestCase):
self.assertIn('id', result)
self.assertIn('status', result)
self.assertEqual('PENDING_CREATE', result['status'])
self._driver_manager.invoke.assert_called_with(mock.ANY, mock.ANY,
name=mock.ANY,
path_id=mock.ANY,
vnfs=mock.ANY,
fc_ids=mock.ANY,
auth_attr=mock.ANY,
symmetrical=mock.ANY
)
self._driver_manager.invoke.assert_called_with(
mock.ANY, mock.ANY,
name=mock.ANY,
path_id=mock.ANY,
vnfs=mock.ANY,
fc_ids=mock.ANY,
auth_attr=mock.ANY,
symmetrical=mock.ANY,
correlation=mock.ANY
)
def test_create_vnffg_duplicate_criteria(self):
with patch.object(TackerManager, 'get_service_plugins') as \

View File

@ -7,6 +7,10 @@ data_types:
capability:
type: string
required: true
sfc_encap:
type: boolean
required: false
default: true
tosca.nfv.datatypes.aclType:
properties:
@ -308,6 +312,13 @@ node_types:
symmetrical:
type: boolean
required: false
default: false
correlation:
type: string
required: false
constraints:
- valid_values: [ mpls, nsh ]
default: mpls
policy:
type: tosca.nfv.datatypes.policyTypeV2
required: false
@ -327,6 +338,13 @@ node_types:
symmetrical:
type: boolean
required: false
default: false
correlation:
type: string
required: false
constraints:
- valid_values: [ mpls, nsh ]
default: mpls
policy:
type: tosca.nfv.datatypes.policyType
required: true