Implement MidoNet Neutron plugin for Havana

Implement L2, L3, security groups, metadata server support for
MidoNet Neutron plugin for Havana.

blueprint midonet-plugin-havana

Change-Id: I0dd1a2ca17d760443c4c7a464a66b6d0a2cf194a
This commit is contained in:
Rossella Sblendido 2013-08-24 00:17:38 +00:00
parent b944ed485c
commit dc5e0338bf
11 changed files with 1663 additions and 1289 deletions

View File

@ -15,5 +15,5 @@
# Virtual provider router ID # Virtual provider router ID
# provider_router_id = 00112233-0011-0011-0011-001122334455 # provider_router_id = 00112233-0011-0011-0011-001122334455
# Virtual metadata router ID # Path to midonet host uuid file
# metadata_router_id = ffeeddcc-ffee-ffee-ffee-ffeeddccbbaa # midonet_host_uuid_path = /etc/midolman/host_uuid.properties

View File

@ -0,0 +1,16 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (C) 2013 Midokura PTE LTD
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

View File

@ -0,0 +1,140 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (C) 2013 Midokura PTE LTD
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Rossella Sblendido, Midokura Japan KK
# @author: Tomoe Sugihara, Midokura Japan KK
# @author: Ryu Ishimoto, Midokura Japan KK
from midonetclient import api
from oslo.config import cfg
from webob import exc as w_exc
from neutron.agent.linux import dhcp
from neutron.agent.linux import interface
from neutron.agent.linux import ip_lib
from neutron.openstack.common import log as logging
from neutron.plugins.midonet.common import config # noqa
LOG = logging.getLogger(__name__)
class DhcpNoOpDriver(dhcp.DhcpLocalProcess):
@classmethod
def existing_dhcp_networks(cls, conf, root_helper):
"""Return a list of existing networks ids that we have configs for."""
return []
@classmethod
def check_version(cls):
"""Execute version checks on DHCP server."""
return float(1.0)
def disable(self, retain_port=False):
"""Disable DHCP for this network."""
if not retain_port:
self.device_delegate.destroy(self.network, self.interface_name)
self._remove_config_files()
def release_lease(self, mac_address, removed_ips):
pass
def reload_allocations(self):
"""Force the DHCP server to reload the assignment database."""
pass
def spawn_process(self):
pass
class MidonetInterfaceDriver(interface.LinuxInterfaceDriver):
def __init__(self, conf):
super(MidonetInterfaceDriver, self).__init__(conf)
# Read config values
midonet_conf = conf.MIDONET
midonet_uri = midonet_conf.midonet_uri
admin_user = midonet_conf.username
admin_pass = midonet_conf.password
admin_project_id = midonet_conf.project_id
self.mido_api = api.MidonetApi(midonet_uri, admin_user,
admin_pass,
project_id=admin_project_id)
def _get_host_uuid(self):
"""Get MidoNet host id from host_uuid.properties file."""
f = open(cfg.CONF.MIDONET.midonet_host_uuid_path)
lines = f.readlines()
host_uuid = filter(lambda x: x.startswith('host_uuid='),
lines)[0].strip()[len('host_uuid='):]
return host_uuid
def plug(self, network_id, port_id, device_name, mac_address,
bridge=None, namespace=None, prefix=None):
"""This method is called by the Dhcp agent or by the L3 agent
when a new network is created
"""
if not ip_lib.device_exists(device_name,
self.root_helper,
namespace=namespace):
ip = ip_lib.IPWrapper(self.root_helper)
tap_name = device_name.replace(prefix or 'tap', 'tap')
# Create ns_dev in a namespace if one is configured.
root_dev, ns_dev = ip.add_veth(tap_name,
device_name,
namespace2=namespace)
ns_dev.link.set_address(mac_address)
# Add an interface created by ovs to the namespace.
namespace_obj = ip.ensure_namespace(namespace)
namespace_obj.add_device_to_namespace(ns_dev)
ns_dev.link.set_up()
root_dev.link.set_up()
vport_id = port_id
host_dev_name = device_name
# create if-vport mapping.
host_uuid = self._get_host_uuid()
try:
host = self.mido_api.get_host(host_uuid)
except w_exc.HTTPError as e:
LOG.error(_('Failed to create a if-vport mapping on host=%s'),
host_uuid)
raise e
try:
self.mido_api.host.add_host_interface_port(
host, vport_id, host_dev_name)
except w_exc.HTTPError as e:
LOG.warn(_('Faild binding vport=%(vport) to device=%(device)'),
{"vport": vport_id, "device": host_dev_name})
else:
LOG.warn(_("Device %s already exists"), device_name)
def unplug(self, device_name, bridge=None, namespace=None, prefix=None):
# the port will be deleted by the dhcp agent that will call the plugin
device = ip_lib.IPDevice(device_name,
self.root_helper,
namespace)
device.link.delete()
LOG.debug(_("Unplugged interface '%s'"), device_name)
ip_lib.IPWrapper(
self.root_helper, namespace).garbage_collect_namespace()

View File

@ -0,0 +1,16 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (C) 2013 Midokura PTE LTD
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

View File

@ -35,12 +35,12 @@ midonet_opts = [
cfg.StrOpt('provider_router_id', cfg.StrOpt('provider_router_id',
default=None, default=None,
help=_('Virtual provider router ID.')), help=_('Virtual provider router ID.')),
cfg.StrOpt('metadata_router_id',
default=None,
help=_('Virtual metadata router ID.')),
cfg.StrOpt('mode', cfg.StrOpt('mode',
default='dev', default='dev',
help=_('Operational mode. Internal dev use only.')) help=_('Operational mode. Internal dev use only.')),
cfg.StrOpt('midonet_host_uuid_path',
default='/etc/midolman/host_uuid.properties',
help=_('Path to midonet host uuid file'))
] ]

View File

@ -0,0 +1,64 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (C) 2013 Midokura PTE LTD
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Ryu Ishimoto, Midokura Japan KK
from neutron.common import constants
def subnet_str(cidr):
"""Convert the cidr string to x.x.x.x_y format
:param cidr: CIDR in x.x.x.x/y format
"""
if cidr is None:
return None
return cidr.replace("/", "_")
def net_addr(addr):
"""Get network address prefix and length from a given address."""
if addr is None:
return (None, None)
nw_addr, nw_len = addr.split('/')
nw_len = int(nw_len)
return nw_addr, nw_len
def get_ethertype_value(ethertype):
"""Convert string representation of ethertype to the numerical."""
if ethertype is None:
return None
mapping = {
'ipv4': 0x0800,
'ipv6': 0x86DD,
'arp': 0x806
}
return mapping.get(ethertype.lower())
def get_protocol_value(protocol):
"""Convert string representation of protocol to the numerical."""
if protocol is None:
return None
mapping = {
'tcp': constants.TCP_PROTOCOL,
'udp': constants.UDP_PROTOCOL,
'icmp': constants.ICMP_PROTOCOL
}
return mapping.get(protocol.lower())

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -62,26 +62,6 @@ def get_chain_mock(id=None, tenant_id='test-tenant', name='chain',
return chain return chain
def get_exterior_bridge_port_mock(id=None, bridge_id=None):
if id is None:
id = str(uuid.uuid4())
if bridge_id is None:
bridge_id = str(uuid.uuid4())
return get_bridge_port_mock(id=id, bridge_id=bridge_id,
type='ExteriorBridge')
def get_interior_bridge_port_mock(id=None, bridge_id=None):
if id is None:
id = str(uuid.uuid4())
if bridge_id is None:
bridge_id = str(uuid.uuid4())
return get_bridge_port_mock(id=id, bridge_id=bridge_id,
type='InteriorBridge')
def get_port_group_mock(id=None, tenant_id='test-tenant', name='pg'): def get_port_group_mock(id=None, tenant_id='test-tenant', name='pg'):
if id is None: if id is None:
id = str(uuid.uuid4()) id = str(uuid.uuid4())
@ -143,22 +123,19 @@ class MidonetLibMockConfig():
def _create_bridge(self, tenant_id, name): def _create_bridge(self, tenant_id, name):
return get_bridge_mock(tenant_id=tenant_id, name=name) return get_bridge_mock(tenant_id=tenant_id, name=name)
def _create_exterior_bridge_port(self, bridge):
return get_exterior_bridge_port_mock(bridge_id=bridge.get_id())
def _create_interior_bridge_port(self, bridge):
return get_interior_bridge_port_mock(bridge_id=bridge.get_id())
def _create_subnet(self, bridge, gateway_ip, subnet_prefix, subnet_len): def _create_subnet(self, bridge, gateway_ip, subnet_prefix, subnet_len):
return get_subnet_mock(bridge.get_id(), gateway_ip=gateway_ip, return get_subnet_mock(bridge.get_id(), gateway_ip=gateway_ip,
subnet_prefix=subnet_prefix, subnet_prefix=subnet_prefix,
subnet_len=subnet_len) subnet_len=subnet_len)
def _add_bridge_port(self, bridge):
return get_bridge_port_mock(bridge_id=bridge.get_id())
def _get_bridge(self, id): def _get_bridge(self, id):
return get_bridge_mock(id=id) return get_bridge_mock(id=id)
def _get_port(self, id): def _get_port(self, id):
return get_exterior_bridge_port_mock(id=id) return get_bridge_port_mock(id=id)
def _get_router(self, id): def _get_router(self, id):
return get_router_mock(id=id) return get_router_mock(id=id)
@ -176,10 +153,8 @@ class MidonetLibMockConfig():
self.inst.create_subnet.side_effect = self._create_subnet self.inst.create_subnet.side_effect = self._create_subnet
# Port methods side effects # Port methods side effects
ex_bp = self.inst.create_exterior_bridge_port ex_bp = self.inst.add_bridge_port
ex_bp.side_effect = self._create_exterior_bridge_port ex_bp.side_effect = self._add_bridge_port
in_bp = self.inst.create_interior_bridge_port
in_bp.side_effect = self._create_interior_bridge_port
self.inst.get_port.side_effect = self._get_port self.inst.get_port.side_effect = self._get_port
# Router methods side effects # Router methods side effects
@ -206,6 +181,26 @@ class MidoClientMockConfig():
def _get_bridge(self, id): def _get_bridge(self, id):
return get_bridge_mock(id=id) return get_bridge_mock(id=id)
def _get_chain(self, id, query=None):
if not self.chains_in:
return []
tenant_id = self._get_query_tenant_id(query)
for chain in self.chains_in:
chain_id = chain['id']
if chain_id is id:
rule_mocks = []
if 'rules' in chain:
for rule in chain['rules']:
rule_mocks.append(
get_rule_mock(id=rule['id'],
chain_id=id,
properties=rule['properties']))
return get_chain_mock(id=chain_id, name=chain['name'],
tenant_id=tenant_id, rules=rule_mocks)
return None
def _get_chains(self, query=None): def _get_chains(self, query=None):
if not self.chains_in: if not self.chains_in:
return [] return []
@ -249,5 +244,6 @@ class MidoClientMockConfig():
def setup(self): def setup(self):
self.inst.get_bridge.side_effect = self._get_bridge self.inst.get_bridge.side_effect = self._get_bridge
self.inst.get_chains.side_effect = self._get_chains self.inst.get_chains.side_effect = self._get_chains
self.inst.get_chain.side_effect = self._get_chain
self.inst.get_port_groups.side_effect = self._get_port_groups self.inst.get_port_groups.side_effect = self._get_port_groups
self.inst.get_router.side_effect = self._get_router self.inst.get_router.side_effect = self._get_router

View File

@ -0,0 +1,116 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (C) 2012 Midokura Japan K.K.
# Copyright (C) 2013 Midokura PTE LTD
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Rossella Sblendido, Midokura Japan KK
import mock
from oslo.config import cfg
import sys
sys.modules["midonetclient"] = mock.Mock()
from neutron.agent.common import config
from neutron.agent.linux import interface
from neutron.agent.linux import ip_lib
from neutron.agent.linux import utils
from neutron.openstack.common import uuidutils
import neutron.plugins.midonet.agent.midonet_driver as driver
from neutron.tests import base
class MidoInterfaceDriverTestCase(base.BaseTestCase):
def setUp(self):
self.conf = config.setup_conf()
self.conf.register_opts(interface.OPTS)
config.register_root_helper(self.conf)
self.ip_dev_p = mock.patch.object(ip_lib, 'IPDevice')
self.ip_dev = self.ip_dev_p.start()
self.ip_p = mock.patch.object(ip_lib, 'IPWrapper')
self.ip = self.ip_p.start()
self.device_exists_p = mock.patch.object(ip_lib, 'device_exists')
self.device_exists = self.device_exists_p.start()
self.api_p = mock.patch.object(sys.modules["midonetclient"].api,
'MidonetApi')
self.api = self.api_p.start()
self.addCleanup(mock.patch.stopall)
midonet_opts = [
cfg.StrOpt('midonet_uri',
default='http://localhost:8080/midonet-api',
help=_('MidoNet API server URI.')),
cfg.StrOpt('username', default='admin',
help=_('MidoNet admin username.')),
cfg.StrOpt('password', default='passw0rd',
secret=True,
help=_('MidoNet admin password.')),
cfg.StrOpt('project_id',
default='77777777-7777-7777-7777-777777777777',
help=_('ID of the project that MidoNet admin user'
'belongs to.'))
]
self.conf.register_opts(midonet_opts, "MIDONET")
self.driver = driver.MidonetInterfaceDriver(self.conf)
self.network_id = uuidutils.generate_uuid()
self.port_id = uuidutils.generate_uuid()
self.device_name = "tap0"
self.mac_address = "aa:bb:cc:dd:ee:ff"
self.bridge = "br-test"
self.namespace = "ns-test"
super(MidoInterfaceDriverTestCase, self).setUp()
def test_plug(self):
def device_exists(dev, root_helper=None, namespace=None):
return False
self.device_exists.side_effect = device_exists
root_dev = mock.Mock()
ns_dev = mock.Mock()
self.ip().add_veth = mock.Mock(return_value=(root_dev, ns_dev))
self.driver._get_host_uuid = mock.Mock(
return_value=uuidutils.generate_uuid())
with mock.patch.object(utils, 'execute'):
self.driver.plug(
self.network_id, self.port_id,
self.device_name, self.mac_address,
self.bridge, self.namespace)
expected = [mock.call(), mock.call('sudo'),
mock.call().add_veth(self.device_name,
self.device_name,
namespace2=self.namespace),
mock.call().ensure_namespace(self.namespace),
mock.call().ensure_namespace().add_device_to_namespace(
mock.ANY)]
ns_dev.assert_has_calls(
[mock.call.link.set_address(self.mac_address)])
root_dev.assert_has_calls([mock.call.link.set_up()])
ns_dev.assert_has_calls([mock.call.link.set_up()])
self.ip.assert_has_calls(expected, True)
host = mock.Mock()
self.api().get_host = mock.Mock(return_value=host)
self.api.assert_has_calls([mock.call().add_host_interface_port])
def test_unplug(self):
with mock.patch.object(utils, 'execute'):
self.driver.unplug(self.device_name, self.bridge, self.namespace)
self.ip_dev.assert_has_calls([
mock.call(self.device_name, self.driver.root_helper,
self.namespace),
mock.call().link.delete()])
self.ip.assert_has_calls(mock.call().garbage_collect_namespace())

View File

@ -19,7 +19,6 @@
# @author: Ryu Ishimoto, Midokura Japan KK # @author: Ryu Ishimoto, Midokura Japan KK
# @author: Tomoe Sugihara, Midokura Japan KK # @author: Tomoe Sugihara, Midokura Japan KK
import mock import mock
import testtools import testtools
import webob.exc as w_exc import webob.exc as w_exc
@ -33,55 +32,8 @@ def _create_test_chain(id, name, tenant_id):
return {'id': id, 'name': name, 'tenant_id': tenant_id} return {'id': id, 'name': name, 'tenant_id': tenant_id}
def _create_test_port_group(sg_id, sg_name, id, tenant_id): def _create_test_port_group(id, name, tenant_id):
return {"id": id, "name": "OS_SG_%s_%s" % (sg_id, sg_name), return {"id": id, "name": name, "tenant_id": tenant_id}
"tenant_id": tenant_id}
def _create_test_router_in_chain(router_id, id, tenant_id):
name = "OS_ROUTER_IN_%s" % router_id
return _create_test_chain(id, name, tenant_id)
def _create_test_router_out_chain(router_id, id, tenant_id):
name = "OS_ROUTER_OUT_%s" % router_id
return _create_test_chain(id, name, tenant_id)
def _create_test_rule(id, chain_id, properties):
return {"id": id, "chain_id": chain_id, "properties": properties}
def _create_test_sg_in_chain(sg_id, sg_name, id, tenant_id):
if sg_name:
name = "OS_SG_%s_%s_IN" % (sg_id, sg_name)
else:
name = "OS_SG_%s_IN" % sg_id
return _create_test_chain(id, name, tenant_id)
def _create_test_sg_out_chain(sg_id, sg_name, id, tenant_id):
if sg_name:
name = "OS_SG_%s_%s_OUT" % (sg_id, sg_name)
else:
name = "OS_SG_%s_OUT" % sg_id
return _create_test_chain(id, name, tenant_id)
def _create_test_sg_rule(tenant_id, sg_id, id,
direction="egress", protocol="tcp", port_min=1,
port_max=65535, src_ip='192.168.1.0/24',
src_group_id=None, ethertype=0x0800, properties=None):
return {"tenant_id": tenant_id, "security_group_id": sg_id,
"id": id, "direction": direction, "protocol": protocol,
"remote_ip_prefix": src_ip, "remote_group_id": src_group_id,
"port_range_min": port_min, "port_range_max": port_max,
"ethertype": ethertype, "external_id": None}
def _create_test_sg_chain_rule(id, chain_id, sg_rule_id):
props = {"os_sg_rule_id": sg_rule_id}
return _create_test_rule(id, chain_id, props)
class MidoClientTestCase(testtools.TestCase): class MidoClientTestCase(testtools.TestCase):
@ -94,137 +46,59 @@ class MidoClientTestCase(testtools.TestCase):
self.mock_api_cfg.setup() self.mock_api_cfg.setup()
self.client = midonet_lib.MidoClient(self.mock_api) self.client = midonet_lib.MidoClient(self.mock_api)
def test_create_for_sg(self): def test_delete_chains_by_names(self):
sg_id = uuidutils.generate_uuid()
sg_name = 'test-sg'
calls = [mock.call.add_chain().tenant_id(self._tenant_id),
mock.call.add_port_group().tenant_id(self._tenant_id)]
self.client.create_for_sg(self._tenant_id, sg_id, sg_name) tenant_id = uuidutils.generate_uuid()
chain1_id = uuidutils.generate_uuid()
chain1 = _create_test_chain(chain1_id, "chain1", tenant_id)
chain2_id = uuidutils.generate_uuid()
chain2 = _create_test_chain(chain2_id, "chain2", tenant_id)
calls = [mock.call.delete_chain(chain1_id),
mock.call.delete_chain(chain2_id)]
self.mock_api_cfg.chains_in = [chain2, chain1]
self.client.delete_chains_by_names(tenant_id, ["chain1", "chain2"])
self.mock_api.assert_has_calls(calls, any_order=True) self.mock_api.assert_has_calls(calls, any_order=True)
def test_create_for_sg_rule(self): def test_delete_port_group_by_name(self):
sg_id = uuidutils.generate_uuid()
sg_name = 'test-sg'
in_chain_id = uuidutils.generate_uuid()
out_chain_id = uuidutils.generate_uuid()
self.mock_api_cfg.chains_in = [
_create_test_sg_in_chain(sg_id, sg_name, in_chain_id,
self._tenant_id),
_create_test_sg_out_chain(sg_id, sg_name, out_chain_id,
self._tenant_id)]
sg_rule_id = uuidutils.generate_uuid() tenant_id = uuidutils.generate_uuid()
sg_rule = _create_test_sg_rule(self._tenant_id, sg_id, sg_rule_id) pg1_id = uuidutils.generate_uuid()
pg1 = _create_test_port_group(pg1_id, "pg1", tenant_id)
pg2_id = uuidutils.generate_uuid()
pg2 = _create_test_port_group(pg2_id, "pg2", tenant_id)
props = {"os_sg_rule_id": sg_rule_id} self.mock_api_cfg.port_groups_in = [pg1, pg2]
calls = [mock.call.add_rule().port_group(None).type( self.client.delete_port_group_by_name(tenant_id, "pg1")
'accept').nw_proto(6).nw_src_address( self.mock_api.delete_port_group.assert_called_once_with(pg1_id)
'192.168.1.0').nw_src_length(24).tp_src_start(
None).tp_src_end(None).tp_dst_start(1).tp_dst_end(
65535).properties(props).create()]
self.client.create_for_sg_rule(sg_rule) def test_create_dhcp(self):
# Egress chain rule added bridge = mock.Mock()
self.mock_api_cfg.chains_out[0].assert_has_calls(calls) gw_call = mock.call.add_dhcp_subnet().default_gateway("192.168.1.1")
subnet_prefix_call = gw_call.subnet_prefix("192.168.1.0")
subnet_length_call = subnet_prefix_call.subnet_length(24)
def test_create_router_chains(self): calls = [gw_call, subnet_prefix_call, subnet_length_call,
router = mock_lib.get_router_mock(tenant_id=self._tenant_id) subnet_length_call.create()]
api_calls = [mock.call.add_chain().tenant_id(self._tenant_id)]
router_calls = [
mock.call.inbound_filter_id().outbound_filter_id().update()]
self.client.create_router_chains(router) self.client.create_dhcp(bridge, "192.168.1.1", "192.168.1.0/24")
bridge.assert_has_calls(calls, any_order=True)
self.mock_api.assert_has_calls(api_calls) def test_add_dhcp_host(self):
router.assert_has_calls(router_calls)
def test_delete_for_sg(self): bridge = mock.Mock()
sg_id = uuidutils.generate_uuid() dhcp_subnet_call = mock.call.get_dhcp_subnet("10.0.0.0_24")
sg_name = 'test-sg' ip_addr_call = dhcp_subnet_call.add_dhcp_host().ip_addr("10.0.0.10")
in_chain_id = uuidutils.generate_uuid() mac_addr_call = ip_addr_call.mac_addr("2A:DB:6B:8C:19:99")
out_chain_id = uuidutils.generate_uuid() calls = [dhcp_subnet_call, ip_addr_call, mac_addr_call,
pg_id = uuidutils.generate_uuid() mac_addr_call.create()]
self.mock_api_cfg.chains_in = [
_create_test_sg_in_chain(sg_id, sg_name, in_chain_id,
self._tenant_id),
_create_test_sg_out_chain(sg_id, sg_name, out_chain_id,
self._tenant_id)]
self.mock_api_cfg.port_groups_in = [
_create_test_port_group(sg_id, sg_name, pg_id, self._tenant_id)]
calls = [mock.call.get_chains({"tenant_id": self._tenant_id}), self.client.add_dhcp_host(bridge, "10.0.0.0/24", "10.0.0.10",
mock.call.delete_chain(in_chain_id), "2A:DB:6B:8C:19:99")
mock.call.delete_chain(out_chain_id), bridge.assert_has_calls(calls, any_order=True)
mock.call.get_port_groups({"tenant_id": self._tenant_id}),
mock.call.delete_port_group(pg_id)]
self.client.delete_for_sg(self._tenant_id, sg_id, sg_name)
self.mock_api.assert_has_calls(calls)
def test_delete_for_sg_rule(self):
sg_id = uuidutils.generate_uuid()
sg_name = 'test-sg'
in_chain_id = uuidutils.generate_uuid()
out_chain_id = uuidutils.generate_uuid()
self.mock_api_cfg.chains_in = [
_create_test_sg_in_chain(sg_id, sg_name, in_chain_id,
self._tenant_id),
_create_test_sg_out_chain(sg_id, sg_name, out_chain_id,
self._tenant_id)]
rule_id = uuidutils.generate_uuid()
sg_rule_id = uuidutils.generate_uuid()
rule = _create_test_sg_chain_rule(rule_id, in_chain_id, sg_rule_id)
self.mock_api_cfg.chains_in[0]['rules'] = [rule]
sg_rule = _create_test_sg_rule(self._tenant_id, sg_id, sg_rule_id)
self.client.delete_for_sg_rule(sg_rule)
self.mock_api.delete_rule.assert_called_once_with(rule_id)
def test_get_bridge(self):
bridge_id = uuidutils.generate_uuid()
bridge = self.client.get_bridge(bridge_id)
self.assertIsNotNone(bridge)
self.assertEqual(bridge.get_id(), bridge_id)
def test_get_bridge_error(self):
self.mock_api.get_bridge.side_effect = w_exc.HTTPInternalServerError()
self.assertRaises(midonet_lib.MidonetApiException,
self.client.get_bridge, uuidutils.generate_uuid())
def test_get_bridge_not_found(self):
self.mock_api.get_bridge.side_effect = w_exc.HTTPNotFound()
self.assertRaises(midonet_lib.MidonetResourceNotFound,
self.client.get_bridge, uuidutils.generate_uuid())
def test_get_port_groups_for_sg(self):
sg_id = uuidutils.generate_uuid()
pg_id = uuidutils.generate_uuid()
self.mock_api_cfg.port_groups_in = [
_create_test_port_group(sg_id, 'test-sg', pg_id, self._tenant_id)]
pg = self.client.get_port_groups_for_sg(self._tenant_id, sg_id)
self.assertIsNotNone(pg)
self.assertEqual(pg.get_id(), pg_id)
def _create_test_rule(self, tenant_id, sg_id, rule_id, direction="egress",
protocol="tcp", port_min=1, port_max=65535,
src_ip='192.168.1.0/24', src_group_id=None,
ethertype=0x0800):
return {"tenant_id": tenant_id, "security_group_id": sg_id,
"rule_id": rule_id, "direction": direction,
"protocol": protocol,
"remote_ip_prefix": src_ip, "remote_group_id": src_group_id,
"port_range_min": port_min, "port_range_max": port_max,
"ethertype": ethertype, "id": rule_id, "external_id": None}
def test_get_router_error(self): def test_get_router_error(self):
self.mock_api.get_router.side_effect = w_exc.HTTPInternalServerError() self.mock_api.get_router.side_effect = w_exc.HTTPInternalServerError()
@ -236,43 +110,20 @@ class MidoClientTestCase(testtools.TestCase):
self.assertRaises(midonet_lib.MidonetResourceNotFound, self.assertRaises(midonet_lib.MidonetResourceNotFound,
self.client.get_router, uuidutils.generate_uuid()) self.client.get_router, uuidutils.generate_uuid())
def test_get_router_chains(self): def test_get_bridge_error(self):
router_id = uuidutils.generate_uuid() self.mock_api.get_bridge.side_effect = w_exc.HTTPInternalServerError()
in_chain_id = uuidutils.generate_uuid() self.assertRaises(midonet_lib.MidonetApiException,
out_chain_id = uuidutils.generate_uuid() self.client.get_bridge, uuidutils.generate_uuid())
self.mock_api_cfg.chains_in = [
_create_test_router_in_chain(router_id, in_chain_id,
self._tenant_id),
_create_test_router_out_chain(router_id, out_chain_id,
self._tenant_id)]
chains = self.client.get_router_chains(self._tenant_id, router_id) def test_get_bridge_not_found(self):
self.mock_api.get_bridge.side_effect = w_exc.HTTPNotFound()
self.assertRaises(midonet_lib.MidonetResourceNotFound,
self.client.get_bridge, uuidutils.generate_uuid())
self.mock_api.assert_has_calls(mock.call.get_chains( def test_get_bridge(self):
{"tenant_id": self._tenant_id})) bridge_id = uuidutils.generate_uuid()
self.assertEqual(len(chains), 2)
self.assertIn('in', chains)
self.assertIn('out', chains)
self.assertEqual(chains['in'].get_id(), in_chain_id)
self.assertEqual(chains['out'].get_id(), out_chain_id)
def test_get_sg_chains(self): bridge = self.client.get_bridge(bridge_id)
sg_id = uuidutils.generate_uuid()
sg_name = 'test-sg'
in_chain_id = uuidutils.generate_uuid()
out_chain_id = uuidutils.generate_uuid()
self.mock_api_cfg.chains_in = [
_create_test_sg_in_chain(sg_id, sg_name, in_chain_id,
self._tenant_id),
_create_test_sg_out_chain(sg_id, sg_name, out_chain_id,
self._tenant_id)]
chains = self.client.get_sg_chains(self._tenant_id, sg_id) self.assertIsNotNone(bridge)
self.assertEqual(bridge.get_id(), bridge_id)
self.mock_api.assert_has_calls(mock.call.get_chains(
{"tenant_id": self._tenant_id}))
self.assertEqual(len(chains), 2)
self.assertIn('in', chains)
self.assertIn('out', chains)
self.assertEqual(chains['in'].get_id(), in_chain_id)
self.assertEqual(chains['out'].get_id(), out_chain_id)