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:
Isaku Yamahata 2012-07-28 00:27:30 +09:00
parent de1d7d9537
commit 82187a476e
5 changed files with 115 additions and 0 deletions

View File

@ -15,6 +15,8 @@ state_path = /opt/stack/data
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.
@ -29,6 +31,9 @@ db_connection = mysql://root:password@localhost/ovs_quantum?charset=utf8
# 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

View File

@ -34,6 +34,9 @@ OPTS = [
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')
]
@ -164,3 +167,27 @@ class BridgeInterfaceDriver(LinuxInterfaceDriver):
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)

View File

@ -82,6 +82,10 @@ class OVSBridge:
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)

View File

@ -217,3 +217,72 @@ class TestBridgeInterfaceDriver(TestBase):
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)

View File

@ -144,6 +144,16 @@ class OVS_Lib_Test(unittest.TestCase):
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'