interface_driver = quantum.agent.linux.interface.OVSInterfaceDriver
# LinuxBridge
#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
# no additional setup of the DHCP server.
# The database used by the LinuxBridge Quantum plugin
#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.
auth_url = http://localhost:35357/v2.0
auth_region = RegionOne
help='Name of Open vSwitch bridge to use'),
cfg.StrOpt('network_device_mtu',
help='MTU setting for device.'),
+ cfg.StrOpt('ryu_api_host',
+ default='127.0.0.1:8080',
+ help='Openflow Ryu REST API host:port')
]
except RuntimeError:
LOG.error(_("Failed unplugging interface '%s'") %
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)
def get_port_ofport(self, port_name):
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):
flow_expr_arr = []
is_delete_expr = kwargs.get('delete', False)
self.ip_dev.assert_has_calls([mock.call('tap0', 'sudo'),
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)
self.assertEqual(self.br.get_port_ofport(pname), ofport)
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):
utils.execute(["ovs-ofctl", "dump-flows", self.BR_NAME],
root_helper=self.root_helper).AndReturn('ignore'