From c3b2c2a9a22bac5cf17813c589444d3abebaa23b Mon Sep 17 00:00:00 2001 From: Wojciech Dec Date: Tue, 16 Aug 2016 19:27:01 +0200 Subject: Adding Mitaka networking-old module with the ODL topology based port binding resolution mechanism from https://review.openstack.org/333186 Change-Id: I10d400aac9bb639c146527f0f93e6925cb74d9de Signed-off-by: Wojciech Dec --- .../tests/unit/ml2/test_pseudo_agentdb_binding.py | 334 +++++++++++++++++++++ 1 file changed, 334 insertions(+) create mode 100644 networking-odl/networking_odl/tests/unit/ml2/test_pseudo_agentdb_binding.py (limited to 'networking-odl/networking_odl/tests/unit/ml2/test_pseudo_agentdb_binding.py') diff --git a/networking-odl/networking_odl/tests/unit/ml2/test_pseudo_agentdb_binding.py b/networking-odl/networking_odl/tests/unit/ml2/test_pseudo_agentdb_binding.py new file mode 100644 index 0000000..d69150c --- /dev/null +++ b/networking-odl/networking_odl/tests/unit/ml2/test_pseudo_agentdb_binding.py @@ -0,0 +1,334 @@ +# Copyright (c) 2016 OpenStack Foundation +# All Rights Reserved. +# +# 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. + +from copy import deepcopy +import mock +from os import path as os_path +from string import Template + +from neutron.extensions import portbindings +from neutron.plugins.common import constants +from neutron.plugins.ml2 import config +from neutron.plugins.ml2 import driver_api as api +from neutron.plugins.ml2 import driver_context as ctx +from neutron_lib import constants as n_const + +from networking_odl.ml2 import pseudo_agentdb_binding +from networking_odl.tests import base + +AGENTDB_BINARY = 'neutron-odlagent-portbinding' +L2_TYPE = "ODL L2" + + +class TestPseudoAgentDBBindingController(base.DietTestCase): + """Test class for AgentDBPortBinding.""" + + # test data hostconfig and hostconfig-dbget + sample_odl_hconfigs = {"hostconfigs": {"hostconfig": [ + {"host-id": "devstack", + "host-type": "ODL L2", + "config": """{"supported_vnic_types": [ + {"vnic_type": "normal", "vif_type": "ovs", + "vif_details": {}}], + "allowed_network_types": [ + "local", "vlan", "vxlan", "gre"], + "bridge_mappings": {"physnet1": "br-ex"}}"""} + ]}} + + # Test data for string interpolation of substitutable identifers + # e.g. $PORT_ID identifier in the configurations JSON string below shall + # be substituted with portcontext.current['id'] eliminating the check + # for specific vif_type making port-binding truly switch agnostic. + # Refer: Python string templates and interpolation (string.Template) + sample_hconf_str_tmpl_subs_vpp = { + "host": "devstack", # host-id in ODL JSON + "agent_type": "ODL L2", # host-type in ODL JSON + # config in ODL JSON + "configurations": """{"supported_vnic_types": [ + {"vnic_type": "normal", "vif_type": "vhostuser", + "vif_details": { + "uuid": "TEST_UUID", + "has_datapath_type_netdev": true, + "support_vhost_user": true, + "port_prefix": "socket_", + "vhostuser_socket_dir": "/tmp", + "vhostuser_ovs_plug": true, + "vhostuser_mode": "server", + "vhostuser_socket": + "/tmp/socket_$PORT_ID" + }}], + "allowed_network_types": [ + "local", "vlan", "vxlan", "gre"], + "bridge_mappings": {"physnet1": "br-ex"}}""" + } + + sample_hconf_str_tmpl_subs_ovs = { + "host": "devstack", # host-id in ODL JSON + "agent_type": "ODL L2", # host-type in ODL JSON + # config in ODL JSON + "configurations": """{"supported_vnic_types": [ + {"vnic_type": "normal", "vif_type": "vhostuser", + "vif_details": { + "uuid": "TEST_UUID", + "has_datapath_type_netdev": true, + "support_vhost_user": true, + "port_prefix": "vhu_", + "vhostuser_socket_dir": "/var/run/openvswitch", + "vhostuser_ovs_plug": true, + "vhostuser_mode": "client", + "vhostuser_socket": + "/var/run/openvswitch/vhu_$PORT_ID" + }}], + "allowed_network_types": [ + "local", "vlan", "vxlan", "gre"], + "bridge_mappings": {"physnet1": "br-ex"}}""" + } + + sample_hconf_str_tmpl_nosubs = { + "host": "devstack", # host-id in ODL JSON + "agent_type": "ODL L2", # host-type in ODL JSON + # config in ODL JSON + "configurations": """{"supported_vnic_types": [ + {"vnic_type": "normal", "vif_type": "ovs", + "vif_details": { + "uuid": "TEST_UUID", + "has_datapath_type_netdev": true, + "support_vhost_user": true, + "port_prefix": "socket_", + "vhostuser_socket_dir": "/tmp", + "vhostuser_ovs_plug": true, + "vhostuser_mode": "server", + "vhostuser_socket": + "/var/run/openvswitch/PORT_NOSUBS" + }}], + "allowed_network_types": [ + "local", "vlan", "vxlan", "gre"], + "bridge_mappings": {"physnet1": "br-ex"}}""" + } + + # Test data for vanilla OVS + sample_hconfig_dbget_ovs = {"configurations": {"supported_vnic_types": [ + {"vnic_type": "normal", "vif_type": portbindings.VIF_TYPE_OVS, + "vif_details": { + "some_test_details": None + }}], + "allowed_network_types": ["local", "vlan", "vxlan", "gre"], + "bridge_mappings": {"physnet1": "br-ex"}}} + + # Test data for OVS-DPDK + sample_hconfig_dbget_ovs_dpdk = {"configurations": { + "supported_vnic_types": [{ + "vnic_type": "normal", + "vif_type": portbindings.VIF_TYPE_VHOST_USER, + "vif_details": { + "uuid": "TEST_UUID", + "has_datapath_type_netdev": True, + "support_vhost_user": True, + "port_prefix": "vhu_", + # Assumption: /var/run mounted as tmpfs + "vhostuser_socket_dir": "/var/run/openvswitch", + "vhostuser_ovs_plug": True, + "vhostuser_mode": "client", + "vhostuser_socket": "/var/run/openvswitch/vhu_$PORT_ID"}}], + "allowed_network_types": ["local", "vlan", "vxlan", "gre"], + "bridge_mappings": {"physnet1": "br-ex"}}} + + # Test data for VPP + sample_hconfig_dbget_vpp = {"configurations": {"supported_vnic_types": [ + {"vnic_type": "normal", "vif_type": portbindings.VIF_TYPE_VHOST_USER, + "vif_details": { + "uuid": "TEST_UUID", + "has_datapath_type_netdev": True, + "support_vhost_user": True, + "port_prefix": "socket_", + "vhostuser_socket_dir": "/tmp", + "vhostuser_ovs_plug": True, + "vhostuser_mode": "server", + "vhostuser_socket": "/tmp/socket_$PORT_ID" + }}], + "allowed_network_types": ["local", "vlan", "vxlan", "gre"], + "bridge_mappings": {"physnet1": "br-ex"}}} + + # test data valid and invalid segments + test_valid_segment = { + api.ID: 'API_ID', + api.NETWORK_TYPE: constants.TYPE_LOCAL, + api.SEGMENTATION_ID: 'API_SEGMENTATION_ID', + api.PHYSICAL_NETWORK: 'API_PHYSICAL_NETWORK'} + + test_invalid_segment = { + api.ID: 'API_ID', + api.NETWORK_TYPE: constants.TYPE_NONE, + api.SEGMENTATION_ID: 'API_SEGMENTATION_ID', + api.PHYSICAL_NETWORK: 'API_PHYSICAL_NETWORK'} + + def setUp(self): + """Setup test.""" + super(TestPseudoAgentDBBindingController, self).setUp() + + config.cfg.CONF.set_override('url', + 'http://localhost:8080' + '/controller/nb/v2/neutron', 'ml2_odl') + + fake_agents_db = mock.MagicMock() + fake_agents_db.create_or_update_agent = mock.MagicMock() + + self.mgr = pseudo_agentdb_binding.PseudoAgentDBBindingController( + db_plugin=fake_agents_db) + + def test_make_hostconf_uri(self): + """test make uri.""" + test_path = '/restconf/neutron:neutron/hostconfigs' + expected = "http://localhost:8080/restconf/neutron:neutron/hostconfigs" + test_uri = self.mgr._make_hostconf_uri(path=test_path) + + self.assertEqual(expected, test_uri) + + def test_update_agents_db(self): + """test agent update.""" + self.mgr._update_agents_db( + hostconfigs=self.sample_odl_hconfigs['hostconfigs']['hostconfig']) + self.mgr.agents_db.create_or_update_agent.assert_called_once() + + def test_is_valid_segment(self): + """Validate the _check_segment method.""" + all_network_types = [constants.TYPE_FLAT, constants.TYPE_GRE, + constants.TYPE_LOCAL, constants.TYPE_VXLAN, + constants.TYPE_VLAN, constants.TYPE_NONE] + + valid_types = { + network_type + for network_type in all_network_types + if self.mgr._is_valid_segment({api.NETWORK_TYPE: network_type}, { + 'allowed_network_types': [ + constants.TYPE_LOCAL, constants.TYPE_GRE, + constants.TYPE_VXLAN, constants.TYPE_VLAN]})} + + self.assertEqual({ + constants.TYPE_LOCAL, constants.TYPE_GRE, constants.TYPE_VXLAN, + constants.TYPE_VLAN}, valid_types) + + def test_bind_port_with_vif_type_ovs(self): + """test bind_port with vanilla ovs.""" + port_context = self._fake_port_context( + fake_segments=[self.test_invalid_segment, self.test_valid_segment]) + + vif_type = portbindings.VIF_TYPE_OVS + vif_details = {'some_test_details': None} + + self.mgr._hconfig_bind_port( + port_context, self.sample_hconfig_dbget_ovs) + + port_context.set_binding.assert_called_once_with( + self.test_valid_segment[api.ID], vif_type, + vif_details, status=n_const.PORT_STATUS_ACTIVE) + + def _set_pass_vif_details(self, port_context, vif_details): + """extract vif_details and update vif_details if needed.""" + vhostuser_socket_dir = vif_details.get( + 'vhostuser_socket_dir', '/var/run/openvswitch') + port_spec = vif_details.get( + 'port_prefix', 'vhu_') + port_context.current['id'] + socket_path = os_path.join(vhostuser_socket_dir, port_spec) + vif_details.update({portbindings.VHOST_USER_SOCKET: socket_path}) + + return vif_details + + def test_bind_port_with_vif_type_vhost_user(self): + """test bind_port with ovs-dpdk.""" + port_context = self._fake_port_context( + fake_segments=[self.test_invalid_segment, self.test_valid_segment], + host_agents=[deepcopy(self.sample_hconf_str_tmpl_subs_ovs)]) + + self.mgr.bind_port(port_context) + + pass_vif_type = portbindings.VIF_TYPE_VHOST_USER + pass_vif_details = self.sample_hconfig_dbget_ovs_dpdk[ + 'configurations']['supported_vnic_types'][0]['vif_details'] + self._set_pass_vif_details(port_context, pass_vif_details) + + port_context.set_binding.assert_called_once_with( + self.test_valid_segment[api.ID], pass_vif_type, + pass_vif_details, status=n_const.PORT_STATUS_ACTIVE) + + def test_bind_port_with_vif_type_vhost_user_vpp(self): + """test bind_port with vpp.""" + port_context = self._fake_port_context( + fake_segments=[self.test_invalid_segment, self.test_valid_segment], + host_agents=[deepcopy(self.sample_hconf_str_tmpl_subs_vpp)]) + + self.mgr.bind_port(port_context) + + pass_vif_type = portbindings.VIF_TYPE_VHOST_USER + pass_vif_details = self.sample_hconfig_dbget_vpp['configurations'][ + 'supported_vnic_types'][0]['vif_details'] + self._set_pass_vif_details(port_context, pass_vif_details) + + port_context.set_binding.assert_called_once_with( + self.test_valid_segment[api.ID], pass_vif_type, + pass_vif_details, status=n_const.PORT_STATUS_ACTIVE) + + def test_bind_port_without_valid_segment(self): + """test bind_port without a valid segment.""" + port_context = self._fake_port_context( + fake_segments=[self.test_invalid_segment]) + + self.mgr._hconfig_bind_port( + port_context, self.sample_hconfig_dbget_ovs) + + port_context.set_binding.assert_not_called() + + def test_no_str_template_substitution_in_configuration_string(self): + """Test for no identifier substituion in config JSON string.""" + port_context = self._fake_port_context( + fake_segments=[self.test_invalid_segment, self.test_valid_segment]) + + hconf_dict = self.mgr._substitute_hconfig_tmpl( + port_context, self.sample_hconf_str_tmpl_nosubs) + + test_string = hconf_dict['configurations'][ + 'supported_vnic_types'][0][ + 'vif_details'][portbindings.VHOST_USER_SOCKET] + + expected_str = '/var/run/openvswitch/PORT_NOSUBS' + + self.assertEqual(expected_str, test_string) + + def test_str_template_substitution_in_configuration_string(self): + """Test for identifier substitution in config JSON string.""" + port_context = self._fake_port_context( + fake_segments=[self.test_invalid_segment, self.test_valid_segment]) + + hconf_dict = self.mgr._substitute_hconfig_tmpl( + port_context, self.sample_hconf_str_tmpl_subs_vpp) + + test_string = hconf_dict['configurations'][ + 'supported_vnic_types'][0][ + 'vif_details'][portbindings.VHOST_USER_SOCKET] + + expected_str = Template('/tmp/socket_$PORT_ID') + expected_str = expected_str.safe_substitute({ + 'PORT_ID': port_context.current['id']}) + + self.assertEqual(expected_str, test_string) + + def _fake_port_context(self, fake_segments, host_agents=None): + network = mock.MagicMock(spec=api.NetworkContext) + return mock.MagicMock( + spec=ctx.PortContext, + current={'id': 'CONTEXT_ID', + portbindings.VNIC_TYPE: portbindings.VNIC_NORMAL}, + segments_to_bind=fake_segments, network=network, + host_agents=lambda agent_type: host_agents) -- cgit 1.2.3-korg