summaryrefslogtreecommitdiffstats
path: root/networking-odl/networking_odl/tests/unit/ml2/test_pseudo_agentdb_binding.py
diff options
context:
space:
mode:
Diffstat (limited to 'networking-odl/networking_odl/tests/unit/ml2/test_pseudo_agentdb_binding.py')
-rw-r--r--networking-odl/networking_odl/tests/unit/ml2/test_pseudo_agentdb_binding.py334
1 files changed, 334 insertions, 0 deletions
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)