diff --git a/etc/quantum/plugins/linuxbridge/linuxbridge_conf.ini b/etc/quantum/plugins/linuxbridge/linuxbridge_conf.ini index c9fe85c9f..be0bb22a0 100644 --- a/etc/quantum/plugins/linuxbridge/linuxbridge_conf.ini +++ b/etc/quantum/plugins/linuxbridge/linuxbridge_conf.ini @@ -58,6 +58,12 @@ reconnect_interval = 2 # Agent's polling interval in seconds polling_interval = 2 +# (BoolOpt) Enable server RPC compatibility with old (pre-havana) +# agents. +# +# Default: rpc_support_old_agents = True +# Example: rpc_support_old_agents = False + [SECURITYGROUP] # Firewall driver for realizing quantum security group function firewall_driver = quantum.agent.linux.iptables_firewall.IptablesFirewallDriver diff --git a/quantum/plugins/linuxbridge/agent/linuxbridge_quantum_agent.py b/quantum/plugins/linuxbridge/agent/linuxbridge_quantum_agent.py index ef5cec669..7e6bce517 100755 --- a/quantum/plugins/linuxbridge/agent/linuxbridge_quantum_agent.py +++ b/quantum/plugins/linuxbridge/agent/linuxbridge_quantum_agent.py @@ -253,21 +253,26 @@ class LinuxBridgeManager: return bridge_name def ensure_physical_in_bridge(self, network_id, + network_type, physical_network, - vlan_id): + segmentation_id): physical_interface = self.interface_mappings.get(physical_network) if not physical_interface: LOG.error(_("No mapping for physical network %s"), physical_network) return - if int(vlan_id) == lconst.FLAT_VLAN_ID: + if network_type == lconst.TYPE_FLAT: return self.ensure_flat_bridge(network_id, physical_interface) - else: + elif network_type == lconst.TYPE_VLAN: return self.ensure_vlan_bridge(network_id, physical_interface, - vlan_id) + segmentation_id) + else: + LOG.error(_("Unknown network_type %(network_type)s for network " + "%(network_id)s."), {network_type: network_type, + network_id: network_id}) - def add_tap_interface(self, network_id, physical_network, vlan_id, - tap_device_name): + def add_tap_interface(self, network_id, network_type, physical_network, + segmentation_id, tap_device_name): """Add tap interface. If a VIF has been plugged into a network, this function will @@ -279,11 +284,12 @@ class LinuxBridgeManager: return False bridge_name = self.get_bridge_name(network_id) - if int(vlan_id) == lconst.LOCAL_VLAN_ID: + if network_type == lconst.TYPE_LOCAL: self.ensure_local_bridge(network_id) elif not self.ensure_physical_in_bridge(network_id, + network_type, physical_network, - vlan_id): + segmentation_id): return False # Check if device needs to be added to bridge @@ -305,11 +311,11 @@ class LinuxBridgeManager: LOG.debug(msg) return True - def add_interface(self, network_id, physical_network, vlan_id, - port_id): + def add_interface(self, network_id, network_type, physical_network, + segmentation_id, port_id): tap_device_name = self.get_tap_device_name(port_id) - return self.add_tap_interface(network_id, - physical_network, vlan_id, + return self.add_tap_interface(network_id, network_type, + physical_network, segmentation_id, tap_device_name) def delete_vlan_bridge(self, bridge_name): @@ -433,12 +439,20 @@ class LinuxBridgeRpcCallbacks(sg_rpc.SecurityGroupAgentRpcCallbackMixin): self.sg_agent.refresh_firewall() if port['admin_state_up']: - vlan_id = kwargs.get('vlan_id') + network_type = kwargs.get('network_type') + if network_type: + segmentation_id = kwargs.get('segmentation_id') + else: + # compatibility with pre-Havana RPC vlan_id encoding + vlan_id = kwargs.get('vlan_id') + (network_type, + segmentation_id) = lconst.interpret_vlan_id(vlan_id) physical_network = kwargs.get('physical_network') # create the networking for the port self.agent.br_mgr.add_interface(port['network_id'], + network_type, physical_network, - vlan_id, + segmentation_id, port['id']) # update plugin about port status self.agent.plugin_rpc.update_device_up(self.context, @@ -569,9 +583,18 @@ class LinuxBridgeQuantumAgentRPC(sg_rpc.SecurityGroupAgentRpcMixin): {'device': device, 'details': details}) if details['admin_state_up']: # create the networking for the port + network_type = details.get('network_type') + if network_type: + segmentation_id = details.get('segmentation_id') + else: + # compatibility with pre-Havana RPC vlan_id encoding + vlan_id = details.get('vlan_id') + (network_type, + segmentation_id) = lconst.interpret_vlan_id(vlan_id) self.br_mgr.add_interface(details['network_id'], + network_type, details['physical_network'], - details['vlan_id'], + segmentation_id, details['port_id']) else: self.remove_port_binding(details['network_id'], diff --git a/quantum/plugins/linuxbridge/common/config.py b/quantum/plugins/linuxbridge/common/config.py index dea8d685d..99eccc20b 100644 --- a/quantum/plugins/linuxbridge/common/config.py +++ b/quantum/plugins/linuxbridge/common/config.py @@ -46,6 +46,9 @@ agent_opts = [ cfg.IntOpt('polling_interval', default=2, help=_("The number of seconds the agent will wait between " "polling for local device changes.")), + #TODO(rkukura): Change default to False before havana rc1 + cfg.BoolOpt('rpc_support_old_agents', default=True, + help=_("Enable server RPC compatibility with old agents")), ] diff --git a/quantum/plugins/linuxbridge/common/constants.py b/quantum/plugins/linuxbridge/common/constants.py index 2ae1412ab..24614af8c 100644 --- a/quantum/plugins/linuxbridge/common/constants.py +++ b/quantum/plugins/linuxbridge/common/constants.py @@ -25,3 +25,16 @@ TYPE_FLAT = 'flat' TYPE_VLAN = 'vlan' TYPE_LOCAL = 'local' TYPE_NONE = 'none' + + +# TODO(rkukura): Eventually remove this function, which provides +# temporary backward compatibility with pre-Havana RPC and DB vlan_id +# encoding. +def interpret_vlan_id(vlan_id): + """Return (network_type, segmentation_id) tuple for encoded vlan_id.""" + if vlan_id == LOCAL_VLAN_ID: + return (TYPE_LOCAL, None) + elif vlan_id == FLAT_VLAN_ID: + return (TYPE_FLAT, None) + else: + return (TYPE_VLAN, vlan_id) diff --git a/quantum/plugins/linuxbridge/lb_quantum_plugin.py b/quantum/plugins/linuxbridge/lb_quantum_plugin.py index 7240120da..b3bf65cdc 100644 --- a/quantum/plugins/linuxbridge/lb_quantum_plugin.py +++ b/quantum/plugins/linuxbridge/lb_quantum_plugin.py @@ -86,12 +86,17 @@ class LinuxBridgeRpcCallbacks(dhcp_rpc_base.DhcpRpcCallbackMixin, if port: binding = db.get_network_binding(db_api.get_session(), port['network_id']) + (network_type, + segmentation_id) = constants.interpret_vlan_id(binding.vlan_id) entry = {'device': device, + 'network_type': network_type, 'physical_network': binding.physical_network, - 'vlan_id': binding.vlan_id, + 'segmentation_id': segmentation_id, 'network_id': port['network_id'], 'port_id': port['id'], 'admin_state_up': port['admin_state_up']} + if cfg.CONF.AGENT.rpc_support_old_agents: + entry['vlan_id'] = binding.vlan_id new_status = (q_const.PORT_STATUS_ACTIVE if port['admin_state_up'] else q_const.PORT_STATUS_DOWN) if port['status'] != new_status: @@ -165,11 +170,15 @@ class AgentNotifierApi(proxy.RpcProxy, topic=self.topic_network_delete) def port_update(self, context, port, physical_network, vlan_id): - self.fanout_cast(context, - self.make_msg('port_update', - port=port, - physical_network=physical_network, - vlan_id=vlan_id), + network_type, segmentation_id = constants.interpret_vlan_id(vlan_id) + kwargs = {'port': port, + 'network_type': network_type, + 'physical_network': physical_network, + 'segmentation_id': segmentation_id} + if cfg.CONF.AGENT.rpc_support_old_agents: + kwargs['vlan_id'] = vlan_id + msg = self.make_msg('port_update', **kwargs) + self.fanout_cast(context, msg, topic=self.topic_port_update) diff --git a/quantum/tests/unit/linuxbridge/test_defaults.py b/quantum/tests/unit/linuxbridge/test_defaults.py index d58bfbceb..c33e9549e 100644 --- a/quantum/tests/unit/linuxbridge/test_defaults.py +++ b/quantum/tests/unit/linuxbridge/test_defaults.py @@ -24,6 +24,8 @@ class ConfigurationTest(base.BaseTestCase): def test_defaults(self): self.assertEqual(2, cfg.CONF.AGENT.polling_interval) + self.assertEqual(True, + cfg.CONF.AGENT.rpc_support_old_agents) self.assertEqual('sudo', cfg.CONF.AGENT.root_helper) self.assertEqual('local', diff --git a/quantum/tests/unit/linuxbridge/test_lb_quantum_agent.py b/quantum/tests/unit/linuxbridge/test_lb_quantum_agent.py index f399fee25..3a8923a1f 100644 --- a/quantum/tests/unit/linuxbridge/test_lb_quantum_agent.py +++ b/quantum/tests/unit/linuxbridge/test_lb_quantum_agent.py @@ -41,6 +41,7 @@ class TestLinuxBridge(base.BaseTestCase): def test_ensure_physical_in_bridge_invalid(self): result = self.linux_bridge.ensure_physical_in_bridge('network_id', + lconst.TYPE_VLAN, 'physnetx', 7) self.assertFalse(result) @@ -49,14 +50,14 @@ class TestLinuxBridge(base.BaseTestCase): with mock.patch.object(self.linux_bridge, 'ensure_flat_bridge') as flat_bridge_func: self.linux_bridge.ensure_physical_in_bridge( - 'network_id', 'physnet1', lconst.FLAT_VLAN_ID) + 'network_id', lconst.TYPE_FLAT, 'physnet1', None) self.assertTrue(flat_bridge_func.called) def test_ensure_physical_in_bridge_vlan(self): with mock.patch.object(self.linux_bridge, 'ensure_vlan_bridge') as vlan_bridge_func: self.linux_bridge.ensure_physical_in_bridge( - 'network_id', 'physnet1', 7) + 'network_id', lconst.TYPE_VLAN, 'physnet1', 7) self.assertTrue(vlan_bridge_func.called) @@ -338,16 +339,19 @@ class TestLinuxBridgeManager(base.BaseTestCase): def test_ensure_physical_in_bridge(self): self.assertFalse( - self.lbm.ensure_physical_in_bridge("123", "phys", "1") + self.lbm.ensure_physical_in_bridge("123", lconst.TYPE_VLAN, + "phys", "1") ) with mock.patch.object(self.lbm, "ensure_flat_bridge") as flbr_fn: self.assertTrue( - self.lbm.ensure_physical_in_bridge("123", "physnet1", "-1") + self.lbm.ensure_physical_in_bridge("123", lconst.TYPE_FLAT, + "physnet1", None) ) self.assertTrue(flbr_fn.called) with mock.patch.object(self.lbm, "ensure_vlan_bridge") as vlbr_fn: self.assertTrue( - self.lbm.ensure_physical_in_bridge("123", "physnet1", "1") + self.lbm.ensure_physical_in_bridge("123", lconst.TYPE_VLAN, + "physnet1", "1") ) self.assertTrue(vlbr_fn.called) @@ -355,7 +359,8 @@ class TestLinuxBridgeManager(base.BaseTestCase): with mock.patch.object(self.lbm, "device_exists") as de_fn: de_fn.return_value = False self.assertFalse( - self.lbm.add_tap_interface("123", "physnet1", "1", "tap1") + self.lbm.add_tap_interface("123", lconst.TYPE_VLAN, + "physnet1", "1", "tap1") ) de_fn.return_value = True @@ -366,25 +371,33 @@ class TestLinuxBridgeManager(base.BaseTestCase): ) as (en_fn, exec_fn, get_br): exec_fn.return_value = False get_br.return_value = True - self.assertTrue(self.lbm.add_tap_interface("123", "physnet1", - "-2", "tap1")) + self.assertTrue(self.lbm.add_tap_interface("123", + lconst.TYPE_LOCAL, + "physnet1", None, + "tap1")) en_fn.assert_called_with("123") get_br.return_value = False exec_fn.return_value = True - self.assertFalse(self.lbm.add_tap_interface("123", "physnet1", - "-2", "tap1")) + self.assertFalse(self.lbm.add_tap_interface("123", + lconst.TYPE_LOCAL, + "physnet1", None, + "tap1")) with mock.patch.object(self.lbm, "ensure_physical_in_bridge") as ens_fn: ens_fn.return_value = False - self.assertFalse(self.lbm.add_tap_interface("123", "physnet1", - "1", "tap1")) + self.assertFalse(self.lbm.add_tap_interface("123", + lconst.TYPE_VLAN, + "physnet1", "1", + "tap1")) def test_add_interface(self): with mock.patch.object(self.lbm, "add_tap_interface") as add_tap: - self.lbm.add_interface("123", "physnet-1", "1", "234") - add_tap.assert_called_with("123", "physnet-1", "1", "tap234") + self.lbm.add_interface("123", lconst.TYPE_VLAN, "physnet-1", + "1", "234") + add_tap.assert_called_with("123", lconst.TYPE_VLAN, "physnet-1", + "1", "tap234") def test_delete_vlan_bridge(self): with contextlib.nested( @@ -508,9 +521,47 @@ class TestLinuxBridgeRpcCallbacks(base.BaseTestCase): self.lb_rpc.port_update("unused_context", port=port, vlan_id="1", physical_network="physnet1") self.assertFalse(reffw_fn.called) - addif_fn.assert_called_with(port["network_id"], + addif_fn.assert_called_with(port["network_id"], lconst.TYPE_VLAN, "physnet1", "1", port["id"]) + self.lb_rpc.port_update("unused_context", port=port, + network_type=lconst.TYPE_VLAN, + segmentation_id="2", + physical_network="physnet1") + self.assertFalse(reffw_fn.called) + addif_fn.assert_called_with(port["network_id"], lconst.TYPE_VLAN, + "physnet1", "2", port["id"]) + + self.lb_rpc.port_update("unused_context", port=port, + vlan_id=lconst.FLAT_VLAN_ID, + physical_network="physnet1") + self.assertFalse(reffw_fn.called) + addif_fn.assert_called_with(port["network_id"], lconst.TYPE_FLAT, + "physnet1", None, port["id"]) + + self.lb_rpc.port_update("unused_context", port=port, + network_type=lconst.TYPE_FLAT, + segmentation_id=None, + physical_network="physnet1") + self.assertFalse(reffw_fn.called) + addif_fn.assert_called_with(port["network_id"], lconst.TYPE_FLAT, + "physnet1", None, port["id"]) + + self.lb_rpc.port_update("unused_context", port=port, + vlan_id=lconst.LOCAL_VLAN_ID, + physical_network=None) + self.assertFalse(reffw_fn.called) + addif_fn.assert_called_with(port["network_id"], lconst.TYPE_LOCAL, + None, None, port["id"]) + + self.lb_rpc.port_update("unused_context", port=port, + network_type=lconst.TYPE_LOCAL, + segmentation_id=None, + physical_network=None) + self.assertFalse(reffw_fn.called) + addif_fn.assert_called_with(port["network_id"], lconst.TYPE_LOCAL, + None, None, port["id"]) + port["admin_state_up"] = False port["security_groups"] = True getbr_fn.return_value = "br0" diff --git a/quantum/tests/unit/linuxbridge/test_rpcapi.py b/quantum/tests/unit/linuxbridge/test_rpcapi.py index 939a54143..1a54b0f14 100644 --- a/quantum/tests/unit/linuxbridge/test_rpcapi.py +++ b/quantum/tests/unit/linuxbridge/test_rpcapi.py @@ -18,6 +18,7 @@ Unit Tests for linuxbridge rpc """ +from oslo.config import cfg import stubout from quantum.agent import rpc as agent_rpc @@ -29,11 +30,12 @@ from quantum.tests import base class rpcApiTestCase(base.BaseTestCase): - - def _test_lb_api(self, rpcapi, topic, method, rpc_method, **kwargs): + def _test_lb_api(self, rpcapi, topic, method, rpc_method, + expected_msg=None, **kwargs): ctxt = context.RequestContext('fake_user', 'fake_project') expected_retval = 'foo' if method == 'call' else None - expected_msg = rpcapi.make_msg(method, **kwargs) + if not expected_msg: + expected_msg = rpcapi.make_msg(method, **kwargs) expected_msg['version'] = rpcapi.BASE_RPC_API_VERSION if rpc_method == 'cast' and method == 'run_instance': kwargs['call'] = False @@ -52,11 +54,11 @@ class rpcApiTestCase(base.BaseTestCase): retval = getattr(rpcapi, method)(ctxt, **kwargs) - self.assertEqual(retval, expected_retval) + self.assertEqual(expected_retval, retval) expected_args = [ctxt, topic, expected_msg] for arg, expected_arg in zip(self.fake_args, expected_args): - self.assertEqual(arg, expected_arg) + self.assertEqual(expected_arg, arg) def test_delete_network(self): rpcapi = plb.AgentNotifierApi(topics.AGENT) @@ -68,12 +70,38 @@ class rpcApiTestCase(base.BaseTestCase): network_id='fake_request_spec') def test_port_update(self): + cfg.CONF.set_override('rpc_support_old_agents', False, 'AGENT') rpcapi = plb.AgentNotifierApi(topics.AGENT) + expected_msg = rpcapi.make_msg('port_update', + port='fake_port', + network_type='vlan', + physical_network='fake_net', + segmentation_id='fake_vlan_id') self._test_lb_api(rpcapi, topics.get_topic_name(topics.AGENT, topics.PORT, topics.UPDATE), 'port_update', rpc_method='fanout_cast', + expected_msg=expected_msg, + port='fake_port', + physical_network='fake_net', + vlan_id='fake_vlan_id') + + def test_port_update_old_agent(self): + cfg.CONF.set_override('rpc_support_old_agents', True, 'AGENT') + rpcapi = plb.AgentNotifierApi(topics.AGENT) + expected_msg = rpcapi.make_msg('port_update', + port='fake_port', + network_type='vlan', + physical_network='fake_net', + segmentation_id='fake_vlan_id', + vlan_id='fake_vlan_id') + self._test_lb_api(rpcapi, + topics.get_topic_name(topics.AGENT, + topics.PORT, + topics.UPDATE), + 'port_update', rpc_method='fanout_cast', + expected_msg=expected_msg, port='fake_port', physical_network='fake_net', vlan_id='fake_vlan_id')