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:
parent
b944ed485c
commit
dc5e0338bf
@ -15,5 +15,5 @@
|
||||
# Virtual provider router ID
|
||||
# provider_router_id = 00112233-0011-0011-0011-001122334455
|
||||
|
||||
# Virtual metadata router ID
|
||||
# metadata_router_id = ffeeddcc-ffee-ffee-ffee-ffeeddccbbaa
|
||||
# Path to midonet host uuid file
|
||||
# midonet_host_uuid_path = /etc/midolman/host_uuid.properties
|
||||
|
16
neutron/plugins/midonet/agent/__init__.py
Normal file
16
neutron/plugins/midonet/agent/__init__.py
Normal 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.
|
140
neutron/plugins/midonet/agent/midonet_driver.py
Normal file
140
neutron/plugins/midonet/agent/midonet_driver.py
Normal 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()
|
16
neutron/plugins/midonet/common/__init__.py
Normal file
16
neutron/plugins/midonet/common/__init__.py
Normal 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.
|
@ -35,12 +35,12 @@ midonet_opts = [
|
||||
cfg.StrOpt('provider_router_id',
|
||||
default=None,
|
||||
help=_('Virtual provider router ID.')),
|
||||
cfg.StrOpt('metadata_router_id',
|
||||
default=None,
|
||||
help=_('Virtual metadata router ID.')),
|
||||
cfg.StrOpt('mode',
|
||||
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'))
|
||||
]
|
||||
|
||||
|
64
neutron/plugins/midonet/common/net_util.py
Normal file
64
neutron/plugins/midonet/common/net_util.py
Normal 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
@ -62,26 +62,6 @@ def get_chain_mock(id=None, tenant_id='test-tenant', name='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'):
|
||||
if id is None:
|
||||
id = str(uuid.uuid4())
|
||||
@ -143,22 +123,19 @@ class MidonetLibMockConfig():
|
||||
def _create_bridge(self, tenant_id, 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):
|
||||
return get_subnet_mock(bridge.get_id(), gateway_ip=gateway_ip,
|
||||
subnet_prefix=subnet_prefix,
|
||||
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):
|
||||
return get_bridge_mock(id=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):
|
||||
return get_router_mock(id=id)
|
||||
@ -176,10 +153,8 @@ class MidonetLibMockConfig():
|
||||
self.inst.create_subnet.side_effect = self._create_subnet
|
||||
|
||||
# Port methods side effects
|
||||
ex_bp = self.inst.create_exterior_bridge_port
|
||||
ex_bp.side_effect = self._create_exterior_bridge_port
|
||||
in_bp = self.inst.create_interior_bridge_port
|
||||
in_bp.side_effect = self._create_interior_bridge_port
|
||||
ex_bp = self.inst.add_bridge_port
|
||||
ex_bp.side_effect = self._add_bridge_port
|
||||
self.inst.get_port.side_effect = self._get_port
|
||||
|
||||
# Router methods side effects
|
||||
@ -206,6 +181,26 @@ class MidoClientMockConfig():
|
||||
def _get_bridge(self, 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):
|
||||
if not self.chains_in:
|
||||
return []
|
||||
@ -249,5 +244,6 @@ class MidoClientMockConfig():
|
||||
def setup(self):
|
||||
self.inst.get_bridge.side_effect = self._get_bridge
|
||||
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_router.side_effect = self._get_router
|
||||
|
116
neutron/tests/unit/midonet/test_midonet_driver.py
Normal file
116
neutron/tests/unit/midonet/test_midonet_driver.py
Normal 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())
|
@ -19,7 +19,6 @@
|
||||
# @author: Ryu Ishimoto, Midokura Japan KK
|
||||
# @author: Tomoe Sugihara, Midokura Japan KK
|
||||
|
||||
|
||||
import mock
|
||||
import testtools
|
||||
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}
|
||||
|
||||
|
||||
def _create_test_port_group(sg_id, sg_name, id, tenant_id):
|
||||
return {"id": id, "name": "OS_SG_%s_%s" % (sg_id, sg_name),
|
||||
"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)
|
||||
def _create_test_port_group(id, name, tenant_id):
|
||||
return {"id": id, "name": name, "tenant_id": tenant_id}
|
||||
|
||||
|
||||
class MidoClientTestCase(testtools.TestCase):
|
||||
@ -94,137 +46,59 @@ class MidoClientTestCase(testtools.TestCase):
|
||||
self.mock_api_cfg.setup()
|
||||
self.client = midonet_lib.MidoClient(self.mock_api)
|
||||
|
||||
def test_create_for_sg(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)]
|
||||
def test_delete_chains_by_names(self):
|
||||
|
||||
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)
|
||||
|
||||
def test_create_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)]
|
||||
def test_delete_port_group_by_name(self):
|
||||
|
||||
sg_rule_id = uuidutils.generate_uuid()
|
||||
sg_rule = _create_test_sg_rule(self._tenant_id, sg_id, sg_rule_id)
|
||||
tenant_id = uuidutils.generate_uuid()
|
||||
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}
|
||||
calls = [mock.call.add_rule().port_group(None).type(
|
||||
'accept').nw_proto(6).nw_src_address(
|
||||
'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.mock_api_cfg.port_groups_in = [pg1, pg2]
|
||||
self.client.delete_port_group_by_name(tenant_id, "pg1")
|
||||
self.mock_api.delete_port_group.assert_called_once_with(pg1_id)
|
||||
|
||||
self.client.create_for_sg_rule(sg_rule)
|
||||
def test_create_dhcp(self):
|
||||
|
||||
# Egress chain rule added
|
||||
self.mock_api_cfg.chains_out[0].assert_has_calls(calls)
|
||||
bridge = mock.Mock()
|
||||
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):
|
||||
router = mock_lib.get_router_mock(tenant_id=self._tenant_id)
|
||||
api_calls = [mock.call.add_chain().tenant_id(self._tenant_id)]
|
||||
router_calls = [
|
||||
mock.call.inbound_filter_id().outbound_filter_id().update()]
|
||||
calls = [gw_call, subnet_prefix_call, subnet_length_call,
|
||||
subnet_length_call.create()]
|
||||
|
||||
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)
|
||||
router.assert_has_calls(router_calls)
|
||||
def test_add_dhcp_host(self):
|
||||
|
||||
def test_delete_for_sg(self):
|
||||
sg_id = uuidutils.generate_uuid()
|
||||
sg_name = 'test-sg'
|
||||
in_chain_id = uuidutils.generate_uuid()
|
||||
out_chain_id = uuidutils.generate_uuid()
|
||||
pg_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)]
|
||||
self.mock_api_cfg.port_groups_in = [
|
||||
_create_test_port_group(sg_id, sg_name, pg_id, self._tenant_id)]
|
||||
bridge = mock.Mock()
|
||||
dhcp_subnet_call = mock.call.get_dhcp_subnet("10.0.0.0_24")
|
||||
ip_addr_call = dhcp_subnet_call.add_dhcp_host().ip_addr("10.0.0.10")
|
||||
mac_addr_call = ip_addr_call.mac_addr("2A:DB:6B:8C:19:99")
|
||||
calls = [dhcp_subnet_call, ip_addr_call, mac_addr_call,
|
||||
mac_addr_call.create()]
|
||||
|
||||
calls = [mock.call.get_chains({"tenant_id": self._tenant_id}),
|
||||
mock.call.delete_chain(in_chain_id),
|
||||
mock.call.delete_chain(out_chain_id),
|
||||
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}
|
||||
self.client.add_dhcp_host(bridge, "10.0.0.0/24", "10.0.0.10",
|
||||
"2A:DB:6B:8C:19:99")
|
||||
bridge.assert_has_calls(calls, any_order=True)
|
||||
|
||||
def test_get_router_error(self):
|
||||
self.mock_api.get_router.side_effect = w_exc.HTTPInternalServerError()
|
||||
@ -236,43 +110,20 @@ class MidoClientTestCase(testtools.TestCase):
|
||||
self.assertRaises(midonet_lib.MidonetResourceNotFound,
|
||||
self.client.get_router, uuidutils.generate_uuid())
|
||||
|
||||
def test_get_router_chains(self):
|
||||
router_id = uuidutils.generate_uuid()
|
||||
in_chain_id = uuidutils.generate_uuid()
|
||||
out_chain_id = 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)]
|
||||
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())
|
||||
|
||||
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(
|
||||
{"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)
|
||||
def test_get_bridge(self):
|
||||
bridge_id = uuidutils.generate_uuid()
|
||||
|
||||
def test_get_sg_chains(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)]
|
||||
bridge = self.client.get_bridge(bridge_id)
|
||||
|
||||
chains = self.client.get_sg_chains(self._tenant_id, sg_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)
|
||||
self.assertIsNotNone(bridge)
|
||||
self.assertEqual(bridge.get_id(), bridge_id)
|
||||
|
Loading…
Reference in New Issue
Block a user