Merge "dhcp-agent: Ryu plugin support for dhcp agent"
This commit is contained in:
commit
314a2deb66
@ -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'
|
||||||
|
Loading…
Reference in New Issue
Block a user