dhcp-agent: Ryu plugin support for dhcp agent
This patch adds Ryu support to dhcp-agent. fixes bug 1030830 Ryu devstack support is available at https://review.openstack.org/#/c/10117/ Change-Id: I3f5fbe8600b4b674834e317e158bac1856b0349c Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
This commit is contained in:
		| @@ -15,6 +15,8 @@ state_path = /opt/stack/data | |||||||
| interface_driver = quantum.agent.linux.interface.OVSInterfaceDriver | interface_driver = quantum.agent.linux.interface.OVSInterfaceDriver | ||||||
| # LinuxBridge | # LinuxBridge | ||||||
| #interface_driver = quantum.agent.linux.interface.BridgeInterfaceDriver | #interface_driver = quantum.agent.linux.interface.BridgeInterfaceDriver | ||||||
|  | # Ryu | ||||||
|  | #interface_driver = quantum.agent.linux.interface.RyuInterfaceDriver | ||||||
|  |  | ||||||
| # The agent can use other DHCP drivers.  Dnsmasq is the simplest and requires | # The agent can use other DHCP drivers.  Dnsmasq is the simplest and requires | ||||||
| # no additional setup of the DHCP server. | # no additional setup of the DHCP server. | ||||||
| @@ -29,6 +31,9 @@ db_connection = mysql://root:password@localhost/ovs_quantum?charset=utf8 | |||||||
| # The database used by the LinuxBridge Quantum plugin | # The database used by the LinuxBridge Quantum plugin | ||||||
| #db_connection = mysql://root:password@localhost/quantum_linux_bridge | #db_connection = mysql://root:password@localhost/quantum_linux_bridge | ||||||
|  |  | ||||||
|  | # The database used by the Ryu Quantum plugin | ||||||
|  | #db_connection = mysql://root:password@localhost/ryu_quantum | ||||||
|  |  | ||||||
| # The Quantum user information for accessing the Quantum API. | # The Quantum user information for accessing the Quantum API. | ||||||
| auth_url = http://localhost:35357/v2.0 | auth_url = http://localhost:35357/v2.0 | ||||||
| auth_region = RegionOne | auth_region = RegionOne | ||||||
|   | |||||||
| @@ -34,6 +34,9 @@ OPTS = [ | |||||||
|                help='Name of Open vSwitch bridge to use'), |                help='Name of Open vSwitch bridge to use'), | ||||||
|     cfg.StrOpt('network_device_mtu', |     cfg.StrOpt('network_device_mtu', | ||||||
|                help='MTU setting for device.'), |                help='MTU setting for device.'), | ||||||
|  |     cfg.StrOpt('ryu_api_host', | ||||||
|  |                default='127.0.0.1:8080', | ||||||
|  |                help='Openflow Ryu REST API host:port') | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -164,3 +167,27 @@ class BridgeInterfaceDriver(LinuxInterfaceDriver): | |||||||
|         except RuntimeError: |         except RuntimeError: | ||||||
|             LOG.error(_("Failed unplugging interface '%s'") % |             LOG.error(_("Failed unplugging interface '%s'") % | ||||||
|                       device_name) |                       device_name) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class RyuInterfaceDriver(OVSInterfaceDriver): | ||||||
|  |     """Driver for creating a Ryu OVS interface.""" | ||||||
|  |  | ||||||
|  |     def __init__(self, conf): | ||||||
|  |         super(RyuInterfaceDriver, self).__init__(conf) | ||||||
|  |  | ||||||
|  |         from ryu.app.client import OFPClient | ||||||
|  |         LOG.debug('ryu rest host %s', self.conf.ryu_api_host) | ||||||
|  |         self.ryu_client = OFPClient(self.conf.ryu_api_host) | ||||||
|  |  | ||||||
|  |         self.check_bridge_exists(self.conf.ovs_integration_bridge) | ||||||
|  |         self.ovs_br = ovs_lib.OVSBridge(self.conf.ovs_integration_bridge, | ||||||
|  |                                         self.conf.root_helper) | ||||||
|  |         self.datapath_id = self.ovs_br.get_datapath_id() | ||||||
|  |  | ||||||
|  |     def plug(self, network_id, port_id, device_name, mac_address): | ||||||
|  |         """Plug in the interface.""" | ||||||
|  |         super(RyuInterfaceDriver, self).plug(network_id, port_id, device_name, | ||||||
|  |                                              mac_address) | ||||||
|  |  | ||||||
|  |         port_no = self.ovs_br.get_port_ofport(device_name) | ||||||
|  |         self.ryu_client.create_port(network_id, self.datapath_id, port_no) | ||||||
|   | |||||||
| @@ -82,6 +82,10 @@ class OVSBridge: | |||||||
|     def get_port_ofport(self, port_name): |     def get_port_ofport(self, port_name): | ||||||
|         return self.db_get_val("Interface", port_name, "ofport") |         return self.db_get_val("Interface", port_name, "ofport") | ||||||
|  |  | ||||||
|  |     def get_datapath_id(self): | ||||||
|  |         return self.db_get_val('Bridge', | ||||||
|  |                                self.br_name, 'datapath_id').strip('"') | ||||||
|  |  | ||||||
|     def _build_flow_expr_arr(self, **kwargs): |     def _build_flow_expr_arr(self, **kwargs): | ||||||
|         flow_expr_arr = [] |         flow_expr_arr = [] | ||||||
|         is_delete_expr = kwargs.get('delete', False) |         is_delete_expr = kwargs.get('delete', False) | ||||||
|   | |||||||
| @@ -217,3 +217,72 @@ class TestBridgeInterfaceDriver(TestBase): | |||||||
|  |  | ||||||
|         self.ip_dev.assert_has_calls([mock.call('tap0', 'sudo'), |         self.ip_dev.assert_has_calls([mock.call('tap0', 'sudo'), | ||||||
|                                       mock.call().link.delete()]) |                                       mock.call().link.delete()]) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class TestRyuInterfaceDriver(TestBase): | ||||||
|  |     def setUp(self): | ||||||
|  |         super(TestRyuInterfaceDriver, self).setUp() | ||||||
|  |         self.ryu_mod = mock.Mock() | ||||||
|  |         self.ryu_app_mod = self.ryu_mod.app | ||||||
|  |         self.ryu_app_client = self.ryu_app_mod.client | ||||||
|  |         self.ryu_mod_p = mock.patch.dict('sys.modules', | ||||||
|  |                                          {'ryu': self.ryu_mod, | ||||||
|  |                                           'ryu.app': self.ryu_app_mod, | ||||||
|  |                                           'ryu.app.client': | ||||||
|  |                                           self.ryu_app_client}) | ||||||
|  |         self.ryu_mod_p.start() | ||||||
|  |  | ||||||
|  |     def tearDown(self): | ||||||
|  |         self.ryu_mod_p.stop() | ||||||
|  |         super(TestRyuInterfaceDriver, self).tearDown() | ||||||
|  |  | ||||||
|  |     @staticmethod | ||||||
|  |     def _device_exists(dev, root_helper=None): | ||||||
|  |         return dev == 'br-int' | ||||||
|  |  | ||||||
|  |     _vsctl_cmd_init = ['ovs-vsctl', '--timeout=2', | ||||||
|  |                        'get', 'Bridge', 'br-int', 'datapath_id'] | ||||||
|  |  | ||||||
|  |     def test_init(self): | ||||||
|  |         with mock.patch.object(utils, 'execute') as execute: | ||||||
|  |             self.device_exists.side_effect = self._device_exists | ||||||
|  |             interface.RyuInterfaceDriver(self.conf) | ||||||
|  |             execute.assert_called_once_with(self._vsctl_cmd_init, | ||||||
|  |                                             root_helper='sudo') | ||||||
|  |  | ||||||
|  |         self.ryu_app_client.OFPClient.assert_called_once_with('127.0.0.1:8080') | ||||||
|  |  | ||||||
|  |     def test_plug(self): | ||||||
|  |         vsctl_cmd_plug = ['ovs-vsctl', '--', '--may-exist', 'add-port', | ||||||
|  |                           'br-int', 'tap0', '--', 'set', 'Interface', 'tap0', | ||||||
|  |                           'type=internal', '--', 'set', 'Interface', 'tap0', | ||||||
|  |                           'external-ids:iface-id=port-1234', '--', 'set', | ||||||
|  |                           'Interface', 'tap0', | ||||||
|  |                           'external-ids:iface-status=active', '--', 'set', | ||||||
|  |                           'Interface', 'tap0', | ||||||
|  |                           'external-ids:attached-mac=aa:bb:cc:dd:ee:ff'] | ||||||
|  |         vsctl_cmd_ofport = ['ovs-vsctl', '--timeout=2', | ||||||
|  |                             'get', 'Interface', 'tap0', 'ofport'] | ||||||
|  |  | ||||||
|  |         with mock.patch.object(utils, 'execute') as execute: | ||||||
|  |             self.device_exists.side_effect = self._device_exists | ||||||
|  |             ryu = interface.RyuInterfaceDriver(self.conf) | ||||||
|  |  | ||||||
|  |             ryu.plug('01234567-1234-1234-99', | ||||||
|  |                      'port-1234', | ||||||
|  |                      'tap0', | ||||||
|  |                      'aa:bb:cc:dd:ee:ff') | ||||||
|  |  | ||||||
|  |             execute.assert_has_calls([mock.call(self._vsctl_cmd_init, | ||||||
|  |                                                 root_helper='sudo')]) | ||||||
|  |             execute.assert_has_calls([mock.call(vsctl_cmd_plug, 'sudo')]) | ||||||
|  |             execute.assert_has_calls([mock.call(vsctl_cmd_ofport, | ||||||
|  |                                                 root_helper='sudo')]) | ||||||
|  |  | ||||||
|  |         self.ryu_app_client.OFPClient.assert_called_once_with('127.0.0.1:8080') | ||||||
|  |  | ||||||
|  |         expected = [mock.call('sudo'), | ||||||
|  |                     mock.call().device('tap0'), | ||||||
|  |                     mock.call().device().link.set_address('aa:bb:cc:dd:ee:ff'), | ||||||
|  |                     mock.call().device().link.set_up()] | ||||||
|  |         self.ip.assert_has_calls(expected) | ||||||
|   | |||||||
| @@ -144,6 +144,16 @@ class OVS_Lib_Test(unittest.TestCase): | |||||||
|         self.assertEqual(self.br.get_port_ofport(pname), ofport) |         self.assertEqual(self.br.get_port_ofport(pname), ofport) | ||||||
|         self.mox.VerifyAll() |         self.mox.VerifyAll() | ||||||
|  |  | ||||||
|  |     def test_get_datapath_id(self): | ||||||
|  |         datapath_id = '"0000b67f4fbcc149"' | ||||||
|  |         utils.execute(["ovs-vsctl", self.TO, "get", | ||||||
|  |                        "Bridge", self.BR_NAME, "datapath_id"], | ||||||
|  |                       root_helper=self.root_helper).AndReturn(datapath_id) | ||||||
|  |         self.mox.ReplayAll() | ||||||
|  |  | ||||||
|  |         self.assertEqual(self.br.get_datapath_id(), datapath_id.strip('"')) | ||||||
|  |         self.mox.VerifyAll() | ||||||
|  |  | ||||||
|     def test_count_flows(self): |     def test_count_flows(self): | ||||||
|         utils.execute(["ovs-ofctl", "dump-flows", self.BR_NAME], |         utils.execute(["ovs-ofctl", "dump-flows", self.BR_NAME], | ||||||
|                       root_helper=self.root_helper).AndReturn('ignore' |                       root_helper=self.root_helper).AndReturn('ignore' | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Isaku Yamahata
					Isaku Yamahata