From 4c9c88601a060eb3a25b6e8dd0c98e04809f49c0 Mon Sep 17 00:00:00 2001 From: Jaume Devesa Date: Wed, 24 Dec 2014 09:45:05 +0000 Subject: [PATCH] Midonet plugin decomposition Remove the midonet plugin code and add the requirements.txt file to set the dependency to the vendor plugin. Dependency is not pinned: we will use the newest library during the Kilo cycle and we will submit a patch with the pinned version a bit before the Kilo code freeze. Vendor plugin code is available here: https://github.com/midonet/python-neutron-plugin-midonet Plugin already available in pypi: https://pypi.python.org/pypi/neutron-plugin-midonet/ DocImpact Partially-implements: blueprint core-vendor-decomposition Closes-bug: #1408339 Change-Id: I867ab6753cd45abb65e850cd6aaef7bb0c5bbddb --- neutron/plugins/midonet/agent/__init__.py | 0 .../plugins/midonet/agent/midonet_driver.py | 46 - neutron/plugins/midonet/common/__init__.py | 0 neutron/plugins/midonet/common/config.py | 42 - neutron/plugins/midonet/common/net_util.py | 64 - neutron/plugins/midonet/midonet_lib.py | 691 --------- neutron/plugins/midonet/plugin.py | 1250 +---------------- neutron/plugins/midonet/requirements.txt | 1 + neutron/tests/unit/midonet/__init__.py | 0 .../tests/unit/midonet/etc/midonet.ini.test | 16 - neutron/tests/unit/midonet/mock_lib.py | 261 ---- .../tests/unit/midonet/test_midonet_driver.py | 52 - .../tests/unit/midonet/test_midonet_lib.py | 188 --- .../tests/unit/midonet/test_midonet_plugin.py | 249 ---- 14 files changed, 21 insertions(+), 2839 deletions(-) delete mode 100644 neutron/plugins/midonet/agent/__init__.py delete mode 100644 neutron/plugins/midonet/agent/midonet_driver.py delete mode 100644 neutron/plugins/midonet/common/__init__.py delete mode 100644 neutron/plugins/midonet/common/config.py delete mode 100644 neutron/plugins/midonet/common/net_util.py delete mode 100644 neutron/plugins/midonet/midonet_lib.py create mode 100644 neutron/plugins/midonet/requirements.txt delete mode 100644 neutron/tests/unit/midonet/__init__.py delete mode 100644 neutron/tests/unit/midonet/etc/midonet.ini.test delete mode 100644 neutron/tests/unit/midonet/mock_lib.py delete mode 100644 neutron/tests/unit/midonet/test_midonet_driver.py delete mode 100644 neutron/tests/unit/midonet/test_midonet_lib.py delete mode 100644 neutron/tests/unit/midonet/test_midonet_plugin.py diff --git a/neutron/plugins/midonet/agent/__init__.py b/neutron/plugins/midonet/agent/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutron/plugins/midonet/agent/midonet_driver.py b/neutron/plugins/midonet/agent/midonet_driver.py deleted file mode 100644 index 29930b868..000000000 --- a/neutron/plugins/midonet/agent/midonet_driver.py +++ /dev/null @@ -1,46 +0,0 @@ -# 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. - -from neutron.agent.linux import dhcp -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_manager.destroy(self.network, self.interface_name) - self._remove_config_files() - - def reload_allocations(self): - """Force the DHCP server to reload the assignment database.""" - pass - - def spawn_process(self): - pass diff --git a/neutron/plugins/midonet/common/__init__.py b/neutron/plugins/midonet/common/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutron/plugins/midonet/common/config.py b/neutron/plugins/midonet/common/config.py deleted file mode 100644 index d4db12ef0..000000000 --- a/neutron/plugins/midonet/common/config.py +++ /dev/null @@ -1,42 +0,0 @@ -# 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. - -from oslo.config import cfg - -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.')), - cfg.StrOpt('provider_router_id', - help=_('Virtual provider router ID.')), - cfg.StrOpt('mode', - default='dev', - 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')) -] - - -cfg.CONF.register_opts(midonet_opts, "MIDONET") diff --git a/neutron/plugins/midonet/common/net_util.py b/neutron/plugins/midonet/common/net_util.py deleted file mode 100644 index 69706d396..000000000 --- a/neutron/plugins/midonet/common/net_util.py +++ /dev/null @@ -1,64 +0,0 @@ -# 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. - - -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 - - if isinstance(protocol, int): - return protocol - - mapping = { - constants.PROTO_NAME_TCP: constants.PROTO_NUM_TCP, - constants.PROTO_NAME_UDP: constants.PROTO_NUM_UDP, - constants.PROTO_NAME_ICMP: constants.PROTO_NUM_ICMP - } - return mapping.get(protocol.lower()) diff --git a/neutron/plugins/midonet/midonet_lib.py b/neutron/plugins/midonet/midonet_lib.py deleted file mode 100644 index be806c1c2..000000000 --- a/neutron/plugins/midonet/midonet_lib.py +++ /dev/null @@ -1,691 +0,0 @@ -# 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. - -from midonetclient import exc -from webob import exc as w_exc - -from neutron.common import exceptions as n_exc -from neutron.i18n import _LW -from neutron.openstack.common import log as logging -from neutron.plugins.midonet.common import net_util - -LOG = logging.getLogger(__name__) - - -def handle_api_error(fn): - """Wrapper for methods that throws custom exceptions.""" - def wrapped(*args, **kwargs): - try: - return fn(*args, **kwargs) - except (w_exc.HTTPException, - exc.MidoApiConnectionError) as ex: - raise MidonetApiException(msg=ex) - return wrapped - - -class MidonetResourceNotFound(n_exc.NotFound): - message = _('MidoNet %(resource_type)s %(id)s could not be found') - - -class MidonetApiException(n_exc.NeutronException): - message = _("MidoNet API error: %(msg)s") - - -class MidoClient(object): - - def __init__(self, mido_api): - self.mido_api = mido_api - - @classmethod - def _fill_dto(cls, dto, fields): - for field_name, field_value in fields.iteritems(): - # We assume the setters are named the - # same way as the attributes themselves. - try: - getattr(dto, field_name)(field_value) - except AttributeError: - pass - return dto - - @classmethod - def _create_dto(cls, dto, fields): - return cls._fill_dto(dto, fields).create() - - @classmethod - def _update_dto(cls, dto, fields): - return cls._fill_dto(dto, fields).update() - - @handle_api_error - def create_bridge(self, **kwargs): - """Create a new bridge - - :param kwargs: configuration of the new bridge - :returns: newly created bridge - """ - LOG.debug("MidoClient.create_bridge called: " - "kwargs=%(kwargs)s", {'kwargs': kwargs}) - return self._create_dto(self.mido_api.add_bridge(), kwargs) - - @handle_api_error - def delete_bridge(self, id): - """Delete a bridge - - :param id: id of the bridge - """ - LOG.debug("MidoClient.delete_bridge called: id=%(id)s", {'id': id}) - return self.mido_api.delete_bridge(id) - - @handle_api_error - def get_bridge(self, id): - """Get a bridge - - :param id: id of the bridge - :returns: requested bridge. None if bridge does not exist. - """ - LOG.debug("MidoClient.get_bridge called: id=%s", id) - try: - return self.mido_api.get_bridge(id) - except w_exc.HTTPNotFound: - raise MidonetResourceNotFound(resource_type='Bridge', id=id) - - @handle_api_error - def update_bridge(self, id, **kwargs): - """Update a bridge of the given id with the new fields - - :param id: id of the bridge - :param kwargs: the fields to update and their values - :returns: bridge object - """ - LOG.debug("MidoClient.update_bridge called: " - "id=%(id)s, kwargs=%(kwargs)s", - {'id': id, 'kwargs': kwargs}) - try: - return self._update_dto(self.mido_api.get_bridge(id), kwargs) - except w_exc.HTTPNotFound: - raise MidonetResourceNotFound(resource_type='Bridge', id=id) - - @handle_api_error - def create_dhcp(self, bridge, gateway_ip, cidr, host_rts=None, - dns_servers=None): - """Create a new DHCP entry - - :param bridge: bridge object to add dhcp to - :param gateway_ip: IP address of gateway - :param cidr: subnet represented as x.x.x.x/y - :param host_rts: list of routes set in the host - :param dns_servers: list of dns servers - :returns: newly created dhcp - """ - LOG.debug("MidoClient.create_dhcp called: bridge=%(bridge)s, " - "cidr=%(cidr)s, gateway_ip=%(gateway_ip)s, " - "host_rts=%(host_rts)s, dns_servers=%(dns_servers)s", - {'bridge': bridge, 'cidr': cidr, 'gateway_ip': gateway_ip, - 'host_rts': host_rts, 'dns_servers': dns_servers}) - self.mido_api.add_bridge_dhcp(bridge, gateway_ip, cidr, - host_rts=host_rts, - dns_nservers=dns_servers) - - @handle_api_error - def add_dhcp_host(self, bridge, cidr, ip, mac): - """Add DHCP host entry - - :param bridge: bridge the DHCP is configured for - :param cidr: subnet represented as x.x.x.x/y - :param ip: IP address - :param mac: MAC address - """ - LOG.debug("MidoClient.add_dhcp_host called: bridge=%(bridge)s, " - "cidr=%(cidr)s, ip=%(ip)s, mac=%(mac)s", - {'bridge': bridge, 'cidr': cidr, 'ip': ip, 'mac': mac}) - subnet = bridge.get_dhcp_subnet(net_util.subnet_str(cidr)) - if subnet is None: - raise MidonetApiException(msg=_("Tried to add to" - "non-existent DHCP")) - - subnet.add_dhcp_host().ip_addr(ip).mac_addr(mac).create() - - @handle_api_error - def remove_dhcp_host(self, bridge, cidr, ip, mac): - """Remove DHCP host entry - - :param bridge: bridge the DHCP is configured for - :param cidr: subnet represented as x.x.x.x/y - :param ip: IP address - :param mac: MAC address - """ - LOG.debug("MidoClient.remove_dhcp_host called: bridge=%(bridge)s, " - "cidr=%(cidr)s, ip=%(ip)s, mac=%(mac)s", - {'bridge': bridge, 'cidr': cidr, 'ip': ip, 'mac': mac}) - subnet = bridge.get_dhcp_subnet(net_util.subnet_str(cidr)) - if subnet is None: - LOG.warn(_LW("Tried to delete mapping from non-existent subnet")) - return - - for dh in subnet.get_dhcp_hosts(): - if dh.get_mac_addr() == mac and dh.get_ip_addr() == ip: - LOG.debug("MidoClient.remove_dhcp_host: Deleting %(dh)r", - {"dh": dh}) - dh.delete() - - @handle_api_error - def delete_dhcp_host(self, bridge_id, cidr, ip, mac): - """Delete DHCP host entry - - :param bridge_id: id of the bridge of the DHCP - :param cidr: subnet represented as x.x.x.x/y - :param ip: IP address - :param mac: MAC address - """ - LOG.debug("MidoClient.delete_dhcp_host called: " - "bridge_id=%(bridge_id)s, cidr=%(cidr)s, ip=%(ip)s, " - "mac=%(mac)s", {'bridge_id': bridge_id, - 'cidr': cidr, - 'ip': ip, 'mac': mac}) - bridge = self.get_bridge(bridge_id) - self.remove_dhcp_host(bridge, net_util.subnet_str(cidr), ip, mac) - - @handle_api_error - def delete_dhcp(self, bridge, cidr): - """Delete a DHCP entry - - :param bridge: bridge to remove DHCP from - :param cidr: subnet represented as x.x.x.x/y - """ - LOG.debug("MidoClient.delete_dhcp called: bridge=%(bridge)s, " - "cidr=%(cidr)s", - {'bridge': bridge, 'cidr': cidr}) - dhcp_subnets = bridge.get_dhcp_subnets() - net_addr, net_len = net_util.net_addr(cidr) - if not dhcp_subnets: - raise MidonetApiException( - msg=_("Tried to delete non-existent DHCP")) - for dhcp in dhcp_subnets: - if (dhcp.get_subnet_prefix() == net_addr and - dhcp.get_subnet_length() == str(net_len)): - dhcp.delete() - break - - @handle_api_error - def delete_port(self, id, delete_chains=False): - """Delete a port - - :param id: id of the port - """ - LOG.debug("MidoClient.delete_port called: id=%(id)s, " - "delete_chains=%(delete_chains)s", - {'id': id, 'delete_chains': delete_chains}) - if delete_chains: - self.delete_port_chains(id) - - self.mido_api.delete_port(id) - - @handle_api_error - def get_port(self, id): - """Get a port - - :param id: id of the port - :returns: requested port. None if it does not exist - """ - LOG.debug("MidoClient.get_port called: id=%(id)s", {'id': id}) - try: - return self.mido_api.get_port(id) - except w_exc.HTTPNotFound: - raise MidonetResourceNotFound(resource_type='Port', id=id) - - @handle_api_error - def add_bridge_port(self, bridge, **kwargs): - """Add a port on a bridge - - :param bridge: bridge to add a new port to - :param kwargs: configuration of the new port - :returns: newly created port - """ - LOG.debug("MidoClient.add_bridge_port called: " - "bridge=%(bridge)s, kwargs=%(kwargs)s", - {'bridge': bridge, 'kwargs': kwargs}) - return self._create_dto(self.mido_api.add_bridge_port(bridge), kwargs) - - @handle_api_error - def update_port(self, id, **kwargs): - """Update a port of the given id with the new fields - - :param id: id of the port - :param kwargs: the fields to update and their values - """ - LOG.debug("MidoClient.update_port called: " - "id=%(id)s, kwargs=%(kwargs)s", - {'id': id, 'kwargs': kwargs}) - try: - return self._update_dto(self.mido_api.get_port(id), kwargs) - except w_exc.HTTPNotFound: - raise MidonetResourceNotFound(resource_type='Port', id=id) - - @handle_api_error - def add_router_port(self, router, **kwargs): - """Add a new port to an existing router. - - :param router: router to add a new port to - :param kwargs: configuration of the new port - :returns: newly created port - """ - return self._create_dto(self.mido_api.add_router_port(router), kwargs) - - @handle_api_error - def create_router(self, **kwargs): - """Create a new router - - :param kwargs: configuration of the new router - :returns: newly created router - """ - LOG.debug("MidoClient.create_router called: " - "kwargs=%(kwargs)s", {'kwargs': kwargs}) - return self._create_dto(self.mido_api.add_router(), kwargs) - - @handle_api_error - def delete_router(self, id): - """Delete a router - - :param id: id of the router - """ - LOG.debug("MidoClient.delete_router called: id=%(id)s", {'id': id}) - return self.mido_api.delete_router(id) - - @handle_api_error - def get_router(self, id): - """Get a router with the given id - - :param id: id of the router - :returns: requested router object. None if it does not exist. - """ - LOG.debug("MidoClient.get_router called: id=%(id)s", {'id': id}) - try: - return self.mido_api.get_router(id) - except w_exc.HTTPNotFound: - raise MidonetResourceNotFound(resource_type='Router', id=id) - - @handle_api_error - def update_router(self, id, **kwargs): - """Update a router of the given id with the new name - - :param id: id of the router - :param kwargs: the fields to update and their values - :returns: router object - """ - LOG.debug("MidoClient.update_router called: " - "id=%(id)s, kwargs=%(kwargs)s", - {'id': id, 'kwargs': kwargs}) - try: - return self._update_dto(self.mido_api.get_router(id), kwargs) - except w_exc.HTTPNotFound: - raise MidonetResourceNotFound(resource_type='Router', id=id) - - @handle_api_error - def delete_route(self, id): - return self.mido_api.delete_route(id) - - @handle_api_error - def add_dhcp_route_option(self, bridge, cidr, gw_ip, dst_ip): - """Add Option121 route to subnet - - :param bridge: Bridge to add the option route to - :param cidr: subnet represented as x.x.x.x/y - :param gw_ip: IP address of the next hop - :param dst_ip: IP address of the destination, in x.x.x.x/y format - """ - LOG.debug("MidoClient.add_dhcp_route_option called: " - "bridge=%(bridge)s, cidr=%(cidr)s, gw_ip=%(gw_ip)s" - "dst_ip=%(dst_ip)s", - {"bridge": bridge, "cidr": cidr, "gw_ip": gw_ip, - "dst_ip": dst_ip}) - subnet = bridge.get_dhcp_subnet(net_util.subnet_str(cidr)) - if subnet is None: - raise MidonetApiException( - msg=_("Tried to access non-existent DHCP")) - prefix, length = dst_ip.split("/") - routes = [{'destinationPrefix': prefix, 'destinationLength': length, - 'gatewayAddr': gw_ip}] - cur_routes = subnet.get_opt121_routes() - if cur_routes: - routes = routes + cur_routes - subnet.opt121_routes(routes).update() - - @handle_api_error - def link(self, port, peer_id): - """Link a port to a given peerId.""" - self.mido_api.link(port, peer_id) - - @handle_api_error - def delete_port_routes(self, routes, port_id): - """Remove routes whose next hop port is the given port ID.""" - for route in routes: - if route.get_next_hop_port() == port_id: - self.mido_api.delete_route(route.get_id()) - - @handle_api_error - def get_router_routes(self, router_id): - """Get all routes for the given router.""" - return self.mido_api.get_router_routes(router_id) - - @handle_api_error - def unlink(self, port): - """Unlink a port - - :param port: port object - """ - LOG.debug("MidoClient.unlink called: port=%(port)s", - {'port': port}) - if port.get_peer_id(): - self.mido_api.unlink(port) - else: - LOG.warn(_LW("Attempted to unlink a port that was not linked. %s"), - port.get_id()) - - @handle_api_error - def remove_rules_by_property(self, tenant_id, chain_name, key, value): - """Remove all the rules that match the provided key and value.""" - LOG.debug("MidoClient.remove_rules_by_property called: " - "tenant_id=%(tenant_id)s, chain_name=%(chain_name)s" - "key=%(key)s, value=%(value)s", - {'tenant_id': tenant_id, 'chain_name': chain_name, - 'key': key, 'value': value}) - chain = self.get_chain_by_name(tenant_id, chain_name) - if chain is None: - raise MidonetResourceNotFound(resource_type='Chain', - id=chain_name) - - for r in chain.get_rules(): - if key in r.get_properties(): - if r.get_properties()[key] == value: - self.mido_api.delete_rule(r.get_id()) - - @handle_api_error - def add_router_chains(self, router, inbound_chain_name, - outbound_chain_name): - """Create chains for a new router. - - Creates inbound and outbound chains for the router with the given - names, and the new chains are set on the router. - - :param router: router to set chains for - :param inbound_chain_name: Name of the inbound chain - :param outbound_chain_name: Name of the outbound chain - """ - LOG.debug("MidoClient.create_router_chains called: " - "router=%(router)s, inbound_chain_name=%(in_chain)s, " - "outbound_chain_name=%(out_chain)s", - {"router": router, "in_chain": inbound_chain_name, - "out_chain": outbound_chain_name}) - tenant_id = router.get_tenant_id() - - inbound_chain = self.mido_api.add_chain().tenant_id(tenant_id).name( - inbound_chain_name,).create() - outbound_chain = self.mido_api.add_chain().tenant_id(tenant_id).name( - outbound_chain_name).create() - - # set chains to in/out filters - router.inbound_filter_id(inbound_chain.get_id()).outbound_filter_id( - outbound_chain.get_id()).update() - return inbound_chain, outbound_chain - - @handle_api_error - def delete_router_chains(self, id): - """Deletes chains of a router. - - :param id: router ID to delete chains of - """ - LOG.debug("MidoClient.delete_router_chains called: " - "id=%(id)s", {'id': id}) - router = self.get_router(id) - if (router.get_inbound_filter_id()): - self.mido_api.delete_chain(router.get_inbound_filter_id()) - - if (router.get_outbound_filter_id()): - self.mido_api.delete_chain(router.get_outbound_filter_id()) - - @handle_api_error - def delete_port_chains(self, id): - """Deletes chains of a port. - - :param id: port ID to delete chains of - """ - LOG.debug("MidoClient.delete_port_chains called: " - "id=%(id)s", {'id': id}) - port = self.get_port(id) - if (port.get_inbound_filter_id()): - self.mido_api.delete_chain(port.get_inbound_filter_id()) - - if (port.get_outbound_filter_id()): - self.mido_api.delete_chain(port.get_outbound_filter_id()) - - @handle_api_error - def get_link_port(self, router, peer_router_id): - """Setup a route on the router to the next hop router.""" - LOG.debug("MidoClient.get_link_port called: " - "router=%(router)s, peer_router_id=%(peer_router_id)s", - {'router': router, 'peer_router_id': peer_router_id}) - # Find the port linked between the two routers - link_port = None - for p in router.get_peer_ports(): - if p.get_device_id() == peer_router_id: - link_port = p - break - return link_port - - @handle_api_error - def add_router_route(self, router, type='Normal', - src_network_addr=None, src_network_length=None, - dst_network_addr=None, dst_network_length=None, - next_hop_port=None, next_hop_gateway=None, - weight=100): - """Setup a route on the router.""" - return self.mido_api.add_router_route( - router, type=type, src_network_addr=src_network_addr, - src_network_length=src_network_length, - dst_network_addr=dst_network_addr, - dst_network_length=dst_network_length, - next_hop_port=next_hop_port, next_hop_gateway=next_hop_gateway, - weight=weight) - - @handle_api_error - def add_static_nat(self, tenant_id, chain_name, from_ip, to_ip, port_id, - nat_type='dnat', **kwargs): - """Add a static NAT entry - - :param tenant_id: owner fo the chain to add a NAT to - :param chain_name: name of the chain to add a NAT to - :param from_ip: IP to translate from - :param from_ip: IP to translate from - :param to_ip: IP to translate to - :param port_id: port to match on - :param nat_type: 'dnat' or 'snat' - """ - LOG.debug("MidoClient.add_static_nat called: " - "tenant_id=%(tenant_id)s, chain_name=%(chain_name)s, " - "from_ip=%(from_ip)s, to_ip=%(to_ip)s, " - "port_id=%(port_id)s, nat_type=%(nat_type)s", - {'tenant_id': tenant_id, 'chain_name': chain_name, - 'from_ip': from_ip, 'to_ip': to_ip, - 'port_id': port_id, 'nat_type': nat_type}) - if nat_type not in ['dnat', 'snat']: - raise ValueError(_("Invalid NAT type passed in %s") % nat_type) - - chain = self.get_chain_by_name(tenant_id, chain_name) - nat_targets = [] - nat_targets.append( - {'addressFrom': to_ip, 'addressTo': to_ip, - 'portFrom': 0, 'portTo': 0}) - - rule = chain.add_rule().type(nat_type).flow_action('accept').position( - 1).nat_targets(nat_targets).properties(kwargs) - - if nat_type == 'dnat': - rule = rule.nw_dst_address(from_ip).nw_dst_length(32).in_ports( - [port_id]) - else: - rule = rule.nw_src_address(from_ip).nw_src_length(32).out_ports( - [port_id]) - - return rule.create() - - @handle_api_error - def add_dynamic_snat(self, tenant_id, pre_chain_name, post_chain_name, - snat_ip, port_id, **kwargs): - """Add SNAT masquerading rule - - MidoNet requires two rules on the router, one to do NAT to a range of - ports, and another to retrieve back the original IP in the return - flow. - """ - pre_chain = self.get_chain_by_name(tenant_id, pre_chain_name) - post_chain = self.get_chain_by_name(tenant_id, post_chain_name) - - pre_chain.add_rule().nw_dst_address(snat_ip).nw_dst_length( - 32).type('rev_snat').flow_action('accept').in_ports( - [port_id]).properties(kwargs).position(1).create() - - nat_targets = [] - nat_targets.append( - {'addressFrom': snat_ip, 'addressTo': snat_ip, - 'portFrom': 1, 'portTo': 65535}) - - post_chain.add_rule().type('snat').flow_action( - 'accept').nat_targets(nat_targets).out_ports( - [port_id]).properties(kwargs).position(1).create() - - @handle_api_error - def remove_static_route(self, router, ip): - """Remove static route for the IP - - :param router: next hop router to remove the routes to - :param ip: IP address of the route to remove - """ - LOG.debug("MidoClient.remote_static_route called: " - "router=%(router)s, ip=%(ip)s", - {'router': router, 'ip': ip}) - for r in router.get_routes(): - if (r.get_dst_network_addr() == ip and - r.get_dst_network_length() == 32): - self.mido_api.delete_route(r.get_id()) - - @handle_api_error - def update_port_chains(self, port, inbound_chain_id, outbound_chain_id): - """Bind inbound and outbound chains to the port.""" - LOG.debug("MidoClient.update_port_chains called: port=%(port)s" - "inbound_chain_id=%(inbound_chain_id)s, " - "outbound_chain_id=%(outbound_chain_id)s", - {"port": port, "inbound_chain_id": inbound_chain_id, - "outbound_chain_id": outbound_chain_id}) - port.inbound_filter_id(inbound_chain_id).outbound_filter_id( - outbound_chain_id).update() - - @handle_api_error - def create_chain(self, tenant_id, name): - """Create a new chain.""" - LOG.debug("MidoClient.create_chain called: tenant_id=%(tenant_id)s " - " name=%(name)s", {"tenant_id": tenant_id, "name": name}) - return self.mido_api.add_chain().tenant_id(tenant_id).name( - name).create() - - @handle_api_error - def delete_chain(self, id): - """Delete chain matching the ID.""" - LOG.debug("MidoClient.delete_chain called: id=%(id)s", {"id": id}) - self.mido_api.delete_chain(id) - - @handle_api_error - def delete_chains_by_names(self, tenant_id, names): - """Delete chains matching the names given for a tenant.""" - LOG.debug("MidoClient.delete_chains_by_names called: " - "tenant_id=%(tenant_id)s names=%(names)s ", - {"tenant_id": tenant_id, "names": names}) - chains = self.mido_api.get_chains({'tenant_id': tenant_id}) - for c in chains: - if c.get_name() in names: - self.mido_api.delete_chain(c.get_id()) - - @handle_api_error - def get_chain_by_name(self, tenant_id, name): - """Get the chain by its name.""" - LOG.debug("MidoClient.get_chain_by_name called: " - "tenant_id=%(tenant_id)s name=%(name)s ", - {"tenant_id": tenant_id, "name": name}) - for c in self.mido_api.get_chains({'tenant_id': tenant_id}): - if c.get_name() == name: - return c - return None - - @handle_api_error - def get_port_group_by_name(self, tenant_id, name): - """Get the port group by name.""" - LOG.debug("MidoClient.get_port_group_by_name called: " - "tenant_id=%(tenant_id)s name=%(name)s ", - {"tenant_id": tenant_id, "name": name}) - for p in self.mido_api.get_port_groups({'tenant_id': tenant_id}): - if p.get_name() == name: - return p - return None - - @handle_api_error - def create_port_group(self, tenant_id, name): - """Create a port group - - Create a new port group for a given name and ID. - """ - LOG.debug("MidoClient.create_port_group called: " - "tenant_id=%(tenant_id)s name=%(name)s", - {"tenant_id": tenant_id, "name": name}) - return self.mido_api.add_port_group().tenant_id(tenant_id).name( - name).create() - - @handle_api_error - def delete_port_group_by_name(self, tenant_id, name): - """Delete port group matching the name given for a tenant.""" - LOG.debug("MidoClient.delete_port_group_by_name called: " - "tenant_id=%(tenant_id)s name=%(name)s ", - {"tenant_id": tenant_id, "name": name}) - pgs = self.mido_api.get_port_groups({'tenant_id': tenant_id}) - for pg in pgs: - if pg.get_name() == name: - LOG.debug("Deleting pg %(id)s", {"id": pg.get_id()}) - self.mido_api.delete_port_group(pg.get_id()) - - @handle_api_error - def add_port_to_port_group_by_name(self, tenant_id, name, port_id): - """Add a port to a port group with the given name.""" - LOG.debug("MidoClient.add_port_to_port_group_by_name called: " - "tenant_id=%(tenant_id)s name=%(name)s " - "port_id=%(port_id)s", - {"tenant_id": tenant_id, "name": name, "port_id": port_id}) - pg = self.get_port_group_by_name(tenant_id, name) - if pg is None: - raise MidonetResourceNotFound(resource_type='PortGroup', id=name) - - pg = pg.add_port_group_port().port_id(port_id).create() - return pg - - @handle_api_error - def remove_port_from_port_groups(self, port_id): - """Remove a port binding from all the port groups.""" - LOG.debug("MidoClient.remove_port_from_port_groups called: " - "port_id=%(port_id)s", {"port_id": port_id}) - port = self.get_port(port_id) - for pg in port.get_port_groups(): - pg.delete() - - @handle_api_error - def add_chain_rule(self, chain, action='accept', **kwargs): - """Create a new accept chain rule.""" - self.mido_api.add_chain_rule(chain, action, **kwargs) diff --git a/neutron/plugins/midonet/plugin.py b/neutron/plugins/midonet/plugin.py index b538625fe..0082ecd32 100644 --- a/neutron/plugins/midonet/plugin.py +++ b/neutron/plugins/midonet/plugin.py @@ -14,1246 +14,36 @@ # License for the specific language governing permissions and limitations # under the License. -import functools - -from midonetclient import api -from midonetclient import exc -from midonetclient.neutron import client as n_client from oslo.config import cfg -from oslo.utils import excutils -from sqlalchemy.orm import exc as sa_exc -from webob import exc as w_exc - -from neutron.api.rpc.handlers import dhcp_rpc -from neutron.api.rpc.handlers import metadata_rpc -from neutron.api.v2 import attributes -from neutron.common import constants -from neutron.common import exceptions as n_exc -from neutron.common import rpc as n_rpc -from neutron.common import topics -from neutron.db import agents_db -from neutron.db import agentschedulers_db -from neutron.db import db_base_plugin_v2 -from neutron.db import external_net_db -from neutron.db import l3_db -from neutron.db import models_v2 -from neutron.db import portbindings_db -from neutron.db import securitygroups_db -from neutron.extensions import external_net as ext_net -from neutron.extensions import l3 -from neutron.extensions import portbindings -from neutron.extensions import securitygroup as ext_sg -from neutron.i18n import _LE, _LW -from neutron.openstack.common import log as logging -from neutron.plugins.midonet.common import config # noqa -from neutron.plugins.midonet.common import net_util -from neutron.plugins.midonet import midonet_lib - -LOG = logging.getLogger(__name__) - -EXTERNAL_GW_INFO = l3.EXTERNAL_GW_INFO - -METADATA_DEFAULT_IP = "169.254.169.254/32" -OS_FLOATING_IP_RULE_KEY = 'OS_FLOATING_IP' -OS_SG_RULE_KEY = 'OS_SG_RULE_ID' -OS_TENANT_ROUTER_RULE_KEY = 'OS_TENANT_ROUTER_RULE' -PRE_ROUTING_CHAIN_NAME = "OS_PRE_ROUTING_%s" -PORT_INBOUND_CHAIN_NAME = "OS_PORT_%s_INBOUND" -PORT_OUTBOUND_CHAIN_NAME = "OS_PORT_%s_OUTBOUND" -POST_ROUTING_CHAIN_NAME = "OS_POST_ROUTING_%s" -SG_INGRESS_CHAIN_NAME = "OS_SG_%s_INGRESS" -SG_EGRESS_CHAIN_NAME = "OS_SG_%s_EGRESS" -SG_PORT_GROUP_NAME = "OS_PG_%s" -SNAT_RULE = 'SNAT' - - -def handle_api_error(fn): - """Wrapper for methods that throws custom exceptions.""" - @functools.wraps(fn) - def wrapped(*args, **kwargs): - try: - return fn(*args, **kwargs) - except (w_exc.HTTPException, exc.MidoApiConnectionError) as ex: - raise MidonetApiException(msg=ex) - return wrapped - - -class MidonetApiException(n_exc.NeutronException): - message = _("MidoNet API error: %(msg)s") - - -def _get_nat_ips(type, fip): - """Get NAT IP address information. - - From the route type given, determine the source and target IP addresses - from the provided floating IP DB object. - """ - if type == 'pre-routing': - return fip["floating_ip_address"], fip["fixed_ip_address"] - elif type == 'post-routing': - return fip["fixed_ip_address"], fip["floating_ip_address"] - else: - raise ValueError(_("Invalid nat_type %s") % type) - - -def _nat_chain_names(router_id): - """Get the chain names for NAT. - - These names are used to associate MidoNet chains to the NAT rules - applied to the router. For each of these, there are two NAT types, - 'dnat' and 'snat' that are returned as keys, and the corresponding - chain names as their values. - """ - pre_routing_name = PRE_ROUTING_CHAIN_NAME % router_id - post_routing_name = POST_ROUTING_CHAIN_NAME % router_id - return {'pre-routing': pre_routing_name, 'post-routing': post_routing_name} - - -def _sg_chain_names(sg_id): - """Get the chain names for security group. - - These names are used to associate a security group to MidoNet chains. - There are two names for ingress and egress security group directions. - """ - ingress = SG_INGRESS_CHAIN_NAME % sg_id - egress = SG_EGRESS_CHAIN_NAME % sg_id - return {'ingress': ingress, 'egress': egress} - - -def _port_chain_names(port_id): - """Get the chain names for a port. - - These are chains to hold security group chains. - """ - inbound = PORT_INBOUND_CHAIN_NAME % port_id - outbound = PORT_OUTBOUND_CHAIN_NAME % port_id - return {'inbound': inbound, 'outbound': outbound} - - -def _sg_port_group_name(sg_id): - """Get the port group name for security group.. - - This name is used to associate a security group to MidoNet port groups. - """ - return SG_PORT_GROUP_NAME % sg_id - - -def _rule_direction(sg_direction): - """Convert the SG direction to MidoNet direction - MidoNet terms them 'inbound' and 'outbound' instead of 'ingress' and - 'egress'. Also, the direction is reversed since MidoNet sees it - from the network port's point of view, not the VM's. - """ - if sg_direction == 'ingress': - return 'outbound' - elif sg_direction == 'egress': - return 'inbound' - else: - raise ValueError(_("Unrecognized direction %s") % sg_direction) +from midonet.neutron import plugin +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.')) +] -def _is_router_interface_port(port): - """Check whether the given port is a router interface port.""" - device_owner = port['device_owner'] - return (device_owner in l3_db.DEVICE_OWNER_ROUTER_INTF) +cfg.CONF.register_opts(midonet_opts, "MIDONET") -def _is_router_gw_port(port): - """Check whether the given port is a router gateway port.""" - device_owner = port['device_owner'] - return (device_owner in l3_db.DEVICE_OWNER_ROUTER_GW) +class MidonetPluginV2(plugin.MidonetMixin): -def _is_vif_port(port): - """Check whether the given port is a standard VIF port.""" - device_owner = port['device_owner'] - return (not _is_dhcp_port(port) and - device_owner not in (l3_db.DEVICE_OWNER_ROUTER_GW, - l3_db.DEVICE_OWNER_ROUTER_INTF)) - - -def _is_dhcp_port(port): - """Check whether the given port is a DHCP port.""" - device_owner = port['device_owner'] - return device_owner.startswith(constants.DEVICE_OWNER_DHCP) - - -def _check_resource_exists(func, id, name, raise_exc=False): - """Check whether the given resource exists in MidoNet data store.""" - try: - func(id) - except midonet_lib.MidonetResourceNotFound as exc: - LOG.error(_LE("There is no %(name)s with ID %(id)s in MidoNet."), - {"name": name, "id": id}) - if raise_exc: - raise MidonetPluginException(msg=exc) - - -class MidonetPluginException(n_exc.NeutronException): - message = _("%(msg)s") - - -class MidonetPluginV2(db_base_plugin_v2.NeutronDbPluginV2, - portbindings_db.PortBindingMixin, - external_net_db.External_net_db_mixin, - l3_db.L3_NAT_db_mixin, - agentschedulers_db.DhcpAgentSchedulerDbMixin, - securitygroups_db.SecurityGroupDbMixin): - + vendor_extensions = plugin.MidonetMixin.supported_extension_aliases supported_extension_aliases = ['external-net', 'router', 'security-group', 'agent', 'dhcp_agent_scheduler', 'binding', - 'quotas'] - __native_bulk_support = False + 'quotas'] + vendor_extensions + + __native_bulk_support = True def __init__(self): super(MidonetPluginV2, self).__init__() - # Read config values - midonet_conf = cfg.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.provider_router_id = midonet_conf.provider_router_id - self.provider_router = None - - self.api_cli = n_client.MidonetClient(midonet_conf.midonet_uri, - midonet_conf.username, - midonet_conf.password, - project_id=midonet_conf.project_id) - self.mido_api = api.MidonetApi(midonet_uri, admin_user, - admin_pass, - project_id=admin_project_id) - self.client = midonet_lib.MidoClient(self.mido_api) - - # self.provider_router_id should have been set. - if self.provider_router_id is None: - msg = _('provider_router_id should be configured in the plugin ' - 'config file') - LOG.exception(msg) - raise MidonetPluginException(msg=msg) - - self.setup_rpc() - - self.base_binding_dict = { - portbindings.VIF_TYPE: portbindings.VIF_TYPE_MIDONET, - portbindings.VIF_DETAILS: { - # TODO(rkukura): Replace with new VIF security details - portbindings.CAP_PORT_FILTER: - 'security-group' in self.supported_extension_aliases}} - - def _get_provider_router(self): - if self.provider_router is None: - self.provider_router = self.client.get_router( - self.provider_router_id) - return self.provider_router - - def _dhcp_mappings(self, context, fixed_ips, mac): - for fixed_ip in fixed_ips: - subnet = self._get_subnet(context, fixed_ip["subnet_id"]) - if subnet["ip_version"] == 6: - # TODO(ryu) handle IPv6 - continue - if not subnet["enable_dhcp"]: - # Skip if DHCP is disabled - continue - yield subnet['cidr'], fixed_ip["ip_address"], mac - - def _metadata_subnets(self, context, fixed_ips): - for fixed_ip in fixed_ips: - subnet = self._get_subnet(context, fixed_ip["subnet_id"]) - if subnet["ip_version"] == 6: - continue - yield subnet['cidr'], fixed_ip["ip_address"] - - def _initialize_port_chains(self, port, in_chain, out_chain, sg_ids): - - tenant_id = port["tenant_id"] - - position = 1 - # mac spoofing protection - self._add_chain_rule(in_chain, action='drop', - dl_src=port["mac_address"], inv_dl_src=True, - position=position) - - # ip spoofing protection - for fixed_ip in port["fixed_ips"]: - position += 1 - self._add_chain_rule(in_chain, action="drop", - src_addr=fixed_ip["ip_address"] + "/32", - inv_nw_src=True, dl_type=0x0800, # IPv4 - position=position) - - # conntrack - position += 1 - self._add_chain_rule(in_chain, action='accept', - match_forward_flow=True, - position=position) - - # Reset the position to process egress - position = 1 - - # Add rule for SGs - if sg_ids: - for sg_id in sg_ids: - chain_name = _sg_chain_names(sg_id)["ingress"] - chain = self.client.get_chain_by_name(tenant_id, chain_name) - self._add_chain_rule(out_chain, action='jump', - jump_chain_id=chain.get_id(), - jump_chain_name=chain_name, - position=position) - position += 1 - - # add reverse flow matching at the end - self._add_chain_rule(out_chain, action='accept', - match_return_flow=True, - position=position) - position += 1 - - # fall back DROP rule at the end except for ARP - self._add_chain_rule(out_chain, action='drop', - dl_type=0x0806, # ARP - inv_dl_type=True, position=position) - - def _bind_port_to_sgs(self, context, port, sg_ids): - self._process_port_create_security_group(context, port, sg_ids) - if sg_ids is not None: - for sg_id in sg_ids: - pg_name = _sg_port_group_name(sg_id) - self.client.add_port_to_port_group_by_name( - port["tenant_id"], pg_name, port["id"]) - - def _unbind_port_from_sgs(self, context, port_id): - self._delete_port_security_group_bindings(context, port_id) - self.client.remove_port_from_port_groups(port_id) - - def _create_accept_chain_rule(self, context, sg_rule, chain=None): - direction = sg_rule["direction"] - tenant_id = sg_rule["tenant_id"] - sg_id = sg_rule["security_group_id"] - chain_name = _sg_chain_names(sg_id)[direction] - - if chain is None: - chain = self.client.get_chain_by_name(tenant_id, chain_name) - - pg_id = None - if sg_rule["remote_group_id"] is not None: - pg_name = _sg_port_group_name(sg_id) - pg = self.client.get_port_group_by_name(tenant_id, pg_name) - pg_id = pg.get_id() - - props = {OS_SG_RULE_KEY: str(sg_rule["id"])} - - # Determine source or destination address by looking at direction - src_pg_id = dst_pg_id = None - src_addr = dst_addr = None - src_port_to = dst_port_to = None - src_port_from = dst_port_from = None - if direction == "egress": - dst_pg_id = pg_id - dst_addr = sg_rule["remote_ip_prefix"] - dst_port_from = sg_rule["port_range_min"] - dst_port_to = sg_rule["port_range_max"] - else: - src_pg_id = pg_id - src_addr = sg_rule["remote_ip_prefix"] - src_port_from = sg_rule["port_range_min"] - src_port_to = sg_rule["port_range_max"] - - return self._add_chain_rule( - chain, action='accept', port_group_src=src_pg_id, - port_group_dst=dst_pg_id, - src_addr=src_addr, src_port_from=src_port_from, - src_port_to=src_port_to, - dst_addr=dst_addr, dst_port_from=dst_port_from, - dst_port_to=dst_port_to, - nw_proto=net_util.get_protocol_value(sg_rule["protocol"]), - dl_type=net_util.get_ethertype_value(sg_rule["ethertype"]), - properties=props) - - def _remove_nat_rules(self, context, fip): - router = self.client.get_router(fip["router_id"]) - self.client.remove_static_route(self._get_provider_router(), - fip["floating_ip_address"]) - - chain_names = _nat_chain_names(router.get_id()) - for _type, name in chain_names.iteritems(): - self.client.remove_rules_by_property( - router.get_tenant_id(), name, - OS_FLOATING_IP_RULE_KEY, fip["id"]) - - def setup_rpc(self): - # RPC support - self.topic = topics.PLUGIN - self.conn = n_rpc.create_connection(new=True) - self.endpoints = [dhcp_rpc.DhcpRpcCallback(), - agents_db.AgentExtRpcCallback(), - metadata_rpc.MetadataRpcCallback()] - self.conn.create_consumer(self.topic, self.endpoints, - fanout=False) - # Consume from all consumers in threads - self.conn.consume_in_threads() - - def create_subnet(self, context, subnet): - """Create Neutron subnet. - - Creates a Neutron subnet and a DHCP entry in MidoNet bridge. - """ - LOG.debug("MidonetPluginV2.create_subnet called: subnet=%r", subnet) - - s = subnet["subnet"] - net = super(MidonetPluginV2, self).get_network( - context, subnet['subnet']['network_id'], fields=None) - - session = context.session - with session.begin(subtransactions=True): - sn_entry = super(MidonetPluginV2, self).create_subnet(context, - subnet) - bridge = self.client.get_bridge(sn_entry['network_id']) - - gateway_ip = s['gateway_ip'] - cidr = s['cidr'] - if s['enable_dhcp']: - dns_nameservers = None - host_routes = None - if s['dns_nameservers'] is not attributes.ATTR_NOT_SPECIFIED: - dns_nameservers = s['dns_nameservers'] - - if s['host_routes'] is not attributes.ATTR_NOT_SPECIFIED: - host_routes = s['host_routes'] - - self.client.create_dhcp(bridge, gateway_ip, cidr, - host_rts=host_routes, - dns_servers=dns_nameservers) - - # For external network, link the bridge to the provider router. - if net['router:external']: - self._link_bridge_to_gw_router( - bridge, self._get_provider_router(), gateway_ip, cidr) - - LOG.debug("MidonetPluginV2.create_subnet exiting: sn_entry=%r", - sn_entry) - return sn_entry - - def delete_subnet(self, context, id): - """Delete Neutron subnet. - - Delete neutron network and its corresponding MidoNet bridge. - """ - LOG.debug("MidonetPluginV2.delete_subnet called: id=%s", id) - subnet = super(MidonetPluginV2, self).get_subnet(context, id, - fields=None) - net = super(MidonetPluginV2, self).get_network(context, - subnet['network_id'], - fields=None) - session = context.session - with session.begin(subtransactions=True): - - super(MidonetPluginV2, self).delete_subnet(context, id) - bridge = self.client.get_bridge(subnet['network_id']) - if subnet['enable_dhcp']: - self.client.delete_dhcp(bridge, subnet['cidr']) - - # If the network is external, clean up routes, links, ports - if net[ext_net.EXTERNAL]: - self._unlink_bridge_from_gw_router( - bridge, self._get_provider_router()) - - LOG.debug("MidonetPluginV2.delete_subnet exiting") - - @handle_api_error - def create_network(self, context, network): - """Create Neutron network. - - Create a new Neutron network and its corresponding MidoNet bridge. - """ - LOG.debug('MidonetPluginV2.create_network called: network=%r', - network) - - net_data = network['network'] - tenant_id = self._get_tenant_id_for_create(context, net_data) - net_data['tenant_id'] = tenant_id - self._ensure_default_security_group(context, tenant_id) - - with context.session.begin(subtransactions=True): - net = super(MidonetPluginV2, self).create_network(context, network) - self._process_l3_create(context, net, net_data) - self.api_cli.create_network(net) - - LOG.debug("MidonetPluginV2.create_network exiting: net=%r", net) - return net - - @handle_api_error - def update_network(self, context, id, network): - """Update Neutron network. - - Update an existing Neutron network and its corresponding MidoNet - bridge. - """ - LOG.debug("MidonetPluginV2.update_network called: id=%(id)r, " - "network=%(network)r", {'id': id, 'network': network}) - - with context.session.begin(subtransactions=True): - net = super(MidonetPluginV2, self).update_network( - context, id, network) - self._process_l3_update(context, net, network['network']) - self.api_cli.update_network(id, net) - - LOG.debug("MidonetPluginV2.update_network exiting: net=%r", net) - return net - - @handle_api_error - def delete_network(self, context, id): - """Delete a network and its corresponding MidoNet bridge.""" - LOG.debug("MidonetPluginV2.delete_network called: id=%r", id) - - with context.session.begin(subtransactions=True): - self._process_l3_delete(context, id) - super(MidonetPluginV2, self).delete_network(context, id) - self.api_cli.delete_network(id) - - LOG.debug("MidonetPluginV2.delete_network exiting: id=%r", id) - - def create_port(self, context, port): - """Create a L2 port in Neutron/MidoNet.""" - LOG.debug("MidonetPluginV2.create_port called: port=%r", port) - port_data = port['port'] - - # Create a bridge port in MidoNet and set the bridge port ID as the - # port ID in Neutron. - bridge = self.client.get_bridge(port_data["network_id"]) - tenant_id = bridge.get_tenant_id() - asu = port_data.get("admin_state_up", True) - bridge_port = self.client.add_bridge_port(bridge, - admin_state_up=asu) - port_data["id"] = bridge_port.get_id() - - try: - session = context.session - with session.begin(subtransactions=True): - # Create a Neutron port - new_port = super(MidonetPluginV2, self).create_port(context, - port) - port_data.update(new_port) - self._ensure_default_security_group_on_port(context, - port) - if _is_vif_port(port_data): - # Bind security groups to the port - sg_ids = self._get_security_groups_on_port(context, port) - self._bind_port_to_sgs(context, new_port, sg_ids) - - # Create port chains - port_chains = {} - for d, name in _port_chain_names( - new_port["id"]).iteritems(): - port_chains[d] = self.client.create_chain(tenant_id, - name) - - self._initialize_port_chains(port_data, - port_chains['inbound'], - port_chains['outbound'], - sg_ids) - - # Update the port with the chain - self.client.update_port_chains( - bridge_port, port_chains["inbound"].get_id(), - port_chains["outbound"].get_id()) - - # DHCP mapping is only for VIF ports - for cidr, ip, mac in self._dhcp_mappings( - context, port_data["fixed_ips"], - port_data["mac_address"]): - self.client.add_dhcp_host(bridge, cidr, ip, mac) - - elif _is_dhcp_port(port_data): - # For DHCP port, add a metadata route - for cidr, ip in self._metadata_subnets( - context, port_data["fixed_ips"]): - self.client.add_dhcp_route_option(bridge, cidr, ip, - METADATA_DEFAULT_IP) - - self._process_portbindings_create_and_update(context, - port_data, new_port) - except Exception as ex: - # Try removing the MidoNet port before raising an exception. - with excutils.save_and_reraise_exception(): - LOG.error(_LE("Failed to create a port on network %(net_id)s: " - "%(err)s"), - {"net_id": port_data["network_id"], "err": ex}) - self.client.delete_port(bridge_port.get_id()) - - LOG.debug("MidonetPluginV2.create_port exiting: port=%r", new_port) - return new_port - - def get_port(self, context, id, fields=None): - """Retrieve port.""" - LOG.debug("MidonetPluginV2.get_port called: id=%(id)s " - "fields=%(fields)r", {'id': id, 'fields': fields}) - port = super(MidonetPluginV2, self).get_port(context, id, fields) - # Check if the port exists in MidoNet DB - try: - self.client.get_port(id) - except midonet_lib.MidonetResourceNotFound as exc: - LOG.error(_LE("There is no port with ID %(id)s in MidoNet."), - {"id": id}) - port['status'] = constants.PORT_STATUS_ERROR - raise exc - LOG.debug("MidonetPluginV2.get_port exiting: port=%r", port) - return port - - def get_ports(self, context, filters=None, fields=None): - """List neutron ports and verify that they exist in MidoNet.""" - LOG.debug("MidonetPluginV2.get_ports called: filters=%(filters)s " - "fields=%(fields)r", - {'filters': filters, 'fields': fields}) - ports = super(MidonetPluginV2, self).get_ports(context, filters, - fields) - return ports - - def delete_port(self, context, id, l3_port_check=True): - """Delete a neutron port and corresponding MidoNet bridge port.""" - LOG.debug("MidonetPluginV2.delete_port called: id=%(id)s " - "l3_port_check=%(l3_port_check)r", - {'id': id, 'l3_port_check': l3_port_check}) - # if needed, check to see if this is a port owned by - # and l3-router. If so, we should prevent deletion. - if l3_port_check: - self.prevent_l3_port_deletion(context, id) - - self.disassociate_floatingips(context, id) - port = self.get_port(context, id) - device_id = port['device_id'] - # If this port is for router interface/gw, unlink and delete. - if _is_router_interface_port(port): - self._unlink_bridge_from_router(device_id, id) - elif _is_router_gw_port(port): - # Gateway removed - # Remove all the SNAT rules that are tagged. - router = self._get_router(context, device_id) - tenant_id = router["tenant_id"] - chain_names = _nat_chain_names(device_id) - for _type, name in chain_names.iteritems(): - self.client.remove_rules_by_property( - tenant_id, name, OS_TENANT_ROUTER_RULE_KEY, - SNAT_RULE) - # Remove the default routes and unlink - self._remove_router_gateway(port['device_id']) - - self.client.delete_port(id, delete_chains=True) - try: - for cidr, ip, mac in self._dhcp_mappings( - context, port["fixed_ips"], port["mac_address"]): - self.client.delete_dhcp_host(port["network_id"], cidr, ip, - mac) - except Exception: - LOG.error(_LE("Failed to delete DHCP mapping for port %(id)s"), - {"id": id}) - - super(MidonetPluginV2, self).delete_port(context, id) - - def update_port(self, context, id, port): - """Handle port update, including security groups and fixed IPs.""" - with context.session.begin(subtransactions=True): - - # Get the port and save the fixed IPs - old_port = self._get_port(context, id) - net_id = old_port["network_id"] - mac = old_port["mac_address"] - old_ips = old_port["fixed_ips"] - # update the port DB - p = super(MidonetPluginV2, self).update_port(context, id, port) - - if "admin_state_up" in port["port"]: - asu = port["port"]["admin_state_up"] - mido_port = self.client.update_port(id, admin_state_up=asu) - - # If we're changing the admin_state_up flag and the port is - # associated with a router, then we also need to update the - # peer port. - if _is_router_interface_port(p): - self.client.update_port(mido_port.get_peer_id(), - admin_state_up=asu) - - new_ips = p["fixed_ips"] - if new_ips: - bridge = self.client.get_bridge(net_id) - # If it's a DHCP port, add a route to reach the MD server - if _is_dhcp_port(p): - for cidr, ip in self._metadata_subnets( - context, new_ips): - self.client.add_dhcp_route_option( - bridge, cidr, ip, METADATA_DEFAULT_IP) - else: - # IPs have changed. Re-map the DHCP entries - for cidr, ip, mac in self._dhcp_mappings( - context, old_ips, mac): - self.client.remove_dhcp_host( - bridge, cidr, ip, mac) - - for cidr, ip, mac in self._dhcp_mappings( - context, new_ips, mac): - self.client.add_dhcp_host( - bridge, cidr, ip, mac) - - if (self._check_update_deletes_security_groups(port) or - self._check_update_has_security_groups(port)): - self._unbind_port_from_sgs(context, p["id"]) - sg_ids = self._get_security_groups_on_port(context, port) - self._bind_port_to_sgs(context, p, sg_ids) - - self._process_portbindings_create_and_update(context, - port['port'], - p) - return p - - def create_router(self, context, router): - """Handle router creation. - - When a new Neutron router is created, its corresponding MidoNet router - is also created. In MidoNet, this router is initialized with chains - for inbound and outbound traffic, which will be used to hold other - chains that include various rules, such as NAT. - - :param router: Router information provided to create a new router. - """ - - # NOTE(dcahill): Similar to the NSX plugin, we completely override - # this method in order to be able to use the MidoNet ID as Neutron ID - # TODO(dcahill): Propose upstream patch for allowing - # 3rd parties to specify IDs as we do with l2 plugin - LOG.debug("MidonetPluginV2.create_router called: router=%(router)s", - {"router": router}) - r = router['router'] - tenant_id = self._get_tenant_id_for_create(context, r) - r['tenant_id'] = tenant_id - mido_router = self.client.create_router(**r) - mido_router_id = mido_router.get_id() - - try: - has_gw_info = False - if EXTERNAL_GW_INFO in r: - has_gw_info = True - gw_info = r.pop(EXTERNAL_GW_INFO) - with context.session.begin(subtransactions=True): - # pre-generate id so it will be available when - # configuring external gw port - router_db = l3_db.Router(id=mido_router_id, - tenant_id=tenant_id, - name=r['name'], - admin_state_up=r['admin_state_up'], - status="ACTIVE") - context.session.add(router_db) - if has_gw_info: - self._update_router_gw_info(context, router_db['id'], - gw_info) - - router_data = self._make_router_dict(router_db) - - except Exception: - # Try removing the midonet router - with excutils.save_and_reraise_exception(): - self.client.delete_router(mido_router_id) - - # Create router chains - chain_names = _nat_chain_names(mido_router_id) - try: - self.client.add_router_chains(mido_router, - chain_names["pre-routing"], - chain_names["post-routing"]) - except Exception: - # Set the router status to Error - with context.session.begin(subtransactions=True): - r = self._get_router(context, router_data["id"]) - router_data['status'] = constants.NET_STATUS_ERROR - r['status'] = router_data['status'] - context.session.add(r) - - LOG.debug("MidonetPluginV2.create_router exiting: " - "router_data=%(router_data)s.", - {"router_data": router_data}) - return router_data - - def _set_router_gateway(self, id, gw_router, gw_ip): - """Set router uplink gateway - - :param ID: ID of the router - :param gw_router: gateway router to link to - :param gw_ip: gateway IP address - """ - LOG.debug("MidonetPluginV2.set_router_gateway called: id=%(id)s, " - "gw_router=%(gw_router)s, gw_ip=%(gw_ip)s", - {'id': id, 'gw_router': gw_router, 'gw_ip': gw_ip}), - - router = self.client.get_router(id) - - # Create a port in the gw router - gw_port = self.client.add_router_port(gw_router, - port_address='169.254.255.1', - network_address='169.254.255.0', - network_length=30) - - # Create a port in the router - port = self.client.add_router_port(router, - port_address='169.254.255.2', - network_address='169.254.255.0', - network_length=30) - - # Link them - self.client.link(gw_port, port.get_id()) - - # Add a route for gw_ip to bring it down to the router - self.client.add_router_route(gw_router, type='Normal', - src_network_addr='0.0.0.0', - src_network_length=0, - dst_network_addr=gw_ip, - dst_network_length=32, - next_hop_port=gw_port.get_id(), - weight=100) - - # Add default route to uplink in the router - self.client.add_router_route(router, type='Normal', - src_network_addr='0.0.0.0', - src_network_length=0, - dst_network_addr='0.0.0.0', - dst_network_length=0, - next_hop_port=port.get_id(), - weight=100) - - def _remove_router_gateway(self, id): - """Clear router gateway - - :param ID: ID of the router - """ - LOG.debug("MidonetPluginV2.remove_router_gateway called: " - "id=%(id)s", {'id': id}) - router = self.client.get_router(id) - - # delete the port that is connected to the gateway router - for p in router.get_ports(): - if p.get_port_address() == '169.254.255.2': - peer_port_id = p.get_peer_id() - if peer_port_id is not None: - self.client.unlink(p) - self.client.delete_port(peer_port_id) - - # delete default route - for r in router.get_routes(): - if (r.get_dst_network_addr() == '0.0.0.0' and - r.get_dst_network_length() == 0): - self.client.delete_route(r.get_id()) - - def update_router(self, context, id, router): - """Handle router updates.""" - LOG.debug("MidonetPluginV2.update_router called: id=%(id)s " - "router=%(router)r", {"id": id, "router": router}) - - router_data = router["router"] - - # Check if the update included changes to the gateway. - gw_updated = l3_db.EXTERNAL_GW_INFO in router_data - with context.session.begin(subtransactions=True): - - # Update the Neutron DB - r = super(MidonetPluginV2, self).update_router(context, id, - router) - tenant_id = r["tenant_id"] - if gw_updated: - if (l3_db.EXTERNAL_GW_INFO in r and - r[l3_db.EXTERNAL_GW_INFO] is not None): - # Gateway created - gw_port_neutron = self._get_port( - context.elevated(), r["gw_port_id"]) - gw_ip = gw_port_neutron['fixed_ips'][0]['ip_address'] - - # First link routers and set up the routes - self._set_router_gateway(r["id"], - self._get_provider_router(), - gw_ip) - gw_port_midonet = self.client.get_link_port( - self._get_provider_router(), r["id"]) - - # Get the NAT chains and add dynamic SNAT rules. - chain_names = _nat_chain_names(r["id"]) - props = {OS_TENANT_ROUTER_RULE_KEY: SNAT_RULE} - self.client.add_dynamic_snat(tenant_id, - chain_names['pre-routing'], - chain_names['post-routing'], - gw_ip, - gw_port_midonet.get_id(), - **props) - - self.client.update_router(id, **router_data) - - LOG.debug("MidonetPluginV2.update_router exiting: router=%r", r) - return r - - def delete_router(self, context, id): - """Handler for router deletion. - - Deleting a router on Neutron simply means deleting its corresponding - router in MidoNet. - - :param id: router ID to remove - """ - LOG.debug("MidonetPluginV2.delete_router called: id=%s", id) - - self.client.delete_router_chains(id) - self.client.delete_router(id) - - super(MidonetPluginV2, self).delete_router(context, id) - - def _link_bridge_to_gw_router(self, bridge, gw_router, gw_ip, cidr): - """Link a bridge to the gateway router - - :param bridge: bridge - :param gw_router: gateway router to link to - :param gw_ip: IP address of gateway - :param cidr: network CIDR - """ - net_addr, net_len = net_util.net_addr(cidr) - - # create a port on the gateway router - gw_port = self.client.add_router_port(gw_router, port_address=gw_ip, - network_address=net_addr, - network_length=net_len) - - # create a bridge port, then link it to the router. - port = self.client.add_bridge_port(bridge) - self.client.link(gw_port, port.get_id()) - - # add a route for the subnet in the gateway router - self.client.add_router_route(gw_router, type='Normal', - src_network_addr='0.0.0.0', - src_network_length=0, - dst_network_addr=net_addr, - dst_network_length=net_len, - next_hop_port=gw_port.get_id(), - weight=100) - - def _unlink_bridge_from_gw_router(self, bridge, gw_router): - """Unlink a bridge from the gateway router - - :param bridge: bridge to unlink - :param gw_router: gateway router to unlink from - """ - # Delete routes and unlink the router and the bridge. - routes = self.client.get_router_routes(gw_router.get_id()) - - bridge_ports_to_delete = [ - p for p in gw_router.get_peer_ports() - if p.get_device_id() == bridge.get_id()] - - for p in bridge.get_peer_ports(): - if p.get_device_id() == gw_router.get_id(): - # delete the routes going to the bridge - for r in routes: - if r.get_next_hop_port() == p.get_id(): - self.client.delete_route(r.get_id()) - self.client.unlink(p) - self.client.delete_port(p.get_id()) - - # delete bridge port - for port in bridge_ports_to_delete: - self.client.delete_port(port.get_id()) - - def _link_bridge_to_router(self, router, bridge_port, net_addr, net_len, - gw_ip, metadata_gw_ip): - router_port = self.client.add_router_port( - router, network_length=net_len, network_address=net_addr, - port_address=gw_ip, admin_state_up=bridge_port['admin_state_up']) - self.client.link(router_port, bridge_port['id']) - self.client.add_router_route(router, type='Normal', - src_network_addr='0.0.0.0', - src_network_length=0, - dst_network_addr=net_addr, - dst_network_length=net_len, - next_hop_port=router_port.get_id(), - weight=100) - - if metadata_gw_ip: - # Add a route for the metadata server. - # Not all VM images supports DHCP option 121. Add a route for the - # Metadata server in the router to forward the packet to the bridge - # that will send them to the Metadata Proxy. - md_net_addr, md_net_len = net_util.net_addr(METADATA_DEFAULT_IP) - self.client.add_router_route( - router, type='Normal', src_network_addr=net_addr, - src_network_length=net_len, - dst_network_addr=md_net_addr, - dst_network_length=md_net_len, - next_hop_port=router_port.get_id(), - next_hop_gateway=metadata_gw_ip) - - def _unlink_bridge_from_router(self, router_id, bridge_port_id): - """Unlink a bridge from a router.""" - - # Remove the routes to the port and unlink the port - bridge_port = self.client.get_port(bridge_port_id) - routes = self.client.get_router_routes(router_id) - self.client.delete_port_routes(routes, bridge_port.get_peer_id()) - self.client.unlink(bridge_port) - - def add_router_interface(self, context, router_id, interface_info): - """Handle router linking with network.""" - LOG.debug("MidonetPluginV2.add_router_interface called: " - "router_id=%(router_id)s " - "interface_info=%(interface_info)r", - {'router_id': router_id, 'interface_info': interface_info}) - - with context.session.begin(subtransactions=True): - info = super(MidonetPluginV2, self).add_router_interface( - context, router_id, interface_info) - - try: - subnet = self._get_subnet(context, info["subnet_id"]) - cidr = subnet["cidr"] - net_addr, net_len = net_util.net_addr(cidr) - router = self.client.get_router(router_id) - - # Get the metadata GW IP - metadata_gw_ip = None - rport_qry = context.session.query(models_v2.Port) - dhcp_ports = rport_qry.filter_by( - network_id=subnet["network_id"], - device_owner=constants.DEVICE_OWNER_DHCP).all() - if dhcp_ports and dhcp_ports[0].fixed_ips: - metadata_gw_ip = dhcp_ports[0].fixed_ips[0].ip_address - else: - LOG.warn(_LW("DHCP agent is not working correctly. No port " - "to reach the Metadata server on this network")) - # Link the router and the bridge - port = super(MidonetPluginV2, self).get_port(context, - info["port_id"]) - self._link_bridge_to_router(router, port, net_addr, - net_len, subnet["gateway_ip"], - metadata_gw_ip) - except Exception: - LOG.error(_LE("Failed to create MidoNet resources to add router " - "interface. info=%(info)s, router_id=%(router_id)s"), - {"info": info, "router_id": router_id}) - with excutils.save_and_reraise_exception(): - with context.session.begin(subtransactions=True): - self.remove_router_interface(context, router_id, info) - - LOG.debug("MidonetPluginV2.add_router_interface exiting: " - "info=%r", info) - return info - - def _assoc_fip(self, fip): - router = self.client.get_router(fip["router_id"]) - link_port = self.client.get_link_port( - self._get_provider_router(), router.get_id()) - self.client.add_router_route( - self._get_provider_router(), - src_network_addr='0.0.0.0', - src_network_length=0, - dst_network_addr=fip["floating_ip_address"], - dst_network_length=32, - next_hop_port=link_port.get_peer_id()) - props = {OS_FLOATING_IP_RULE_KEY: fip['id']} - tenant_id = router.get_tenant_id() - chain_names = _nat_chain_names(router.get_id()) - for chain_type, name in chain_names.items(): - src_ip, target_ip = _get_nat_ips(chain_type, fip) - if chain_type == 'pre-routing': - nat_type = 'dnat' - else: - nat_type = 'snat' - self.client.add_static_nat(tenant_id, name, src_ip, - target_ip, - link_port.get_id(), - nat_type, **props) - - def create_floatingip(self, context, floatingip): - session = context.session - with session.begin(subtransactions=True): - fip = super(MidonetPluginV2, self).create_floatingip( - context, floatingip) - if fip['port_id']: - self._assoc_fip(fip) - return fip - - def update_floatingip(self, context, id, floatingip): - """Handle floating IP association and disassociation.""" - LOG.debug("MidonetPluginV2.update_floatingip called: id=%(id)s " - "floatingip=%(floatingip)s ", - {'id': id, 'floatingip': floatingip}) - - session = context.session - with session.begin(subtransactions=True): - if floatingip['floatingip']['port_id']: - fip = super(MidonetPluginV2, self).update_floatingip( - context, id, floatingip) - - self._assoc_fip(fip) - - # disassociate floating IP - elif floatingip['floatingip']['port_id'] is None: - fip = super(MidonetPluginV2, self).get_floatingip(context, id) - self._remove_nat_rules(context, fip) - super(MidonetPluginV2, self).update_floatingip(context, id, - floatingip) - - LOG.debug("MidonetPluginV2.update_floating_ip exiting: fip=%s", fip) - return fip - - def disassociate_floatingips(self, context, port_id): - """Disassociate floating IPs (if any) from this port.""" - try: - fip_qry = context.session.query(l3_db.FloatingIP) - fip_dbs = fip_qry.filter_by(fixed_port_id=port_id) - for fip_db in fip_dbs: - self._remove_nat_rules(context, fip_db) - except sa_exc.NoResultFound: - pass - - super(MidonetPluginV2, self).disassociate_floatingips(context, port_id) - - def create_security_group(self, context, security_group, default_sg=False): - """Create security group. - - Create a new security group, including the default security group. - In MidoNet, this means creating a pair of chains, inbound and outbound, - as well as a new port group. - """ - LOG.debug("MidonetPluginV2.create_security_group called: " - "security_group=%(security_group)s " - "default_sg=%(default_sg)s ", - {'security_group': security_group, 'default_sg': default_sg}) - - sg = security_group.get('security_group') - tenant_id = self._get_tenant_id_for_create(context, sg) - if not default_sg: - self._ensure_default_security_group(context, tenant_id) - - # Create the Neutron sg first - sg = super(MidonetPluginV2, self).create_security_group( - context, security_group, default_sg) - - try: - # Process the MidoNet side - self.client.create_port_group(tenant_id, - _sg_port_group_name(sg["id"])) - chain_names = _sg_chain_names(sg["id"]) - chains = {} - for direction, chain_name in chain_names.iteritems(): - c = self.client.create_chain(tenant_id, chain_name) - chains[direction] = c - - # Create all the rules for this SG. Only accept rules are created - for r in sg['security_group_rules']: - self._create_accept_chain_rule(context, r, - chain=chains[r['direction']]) - except Exception: - LOG.error(_LE("Failed to create MidoNet resources for sg %(sg)r"), - {"sg": sg}) - with excutils.save_and_reraise_exception(): - with context.session.begin(subtransactions=True): - sg = self._get_security_group(context, sg["id"]) - context.session.delete(sg) - - LOG.debug("MidonetPluginV2.create_security_group exiting: sg=%r", - sg) - return sg - - def delete_security_group(self, context, id): - """Delete chains for Neutron security group.""" - LOG.debug("MidonetPluginV2.delete_security_group called: id=%s", id) - - with context.session.begin(subtransactions=True): - sg = super(MidonetPluginV2, self).get_security_group(context, id) - if not sg: - raise ext_sg.SecurityGroupNotFound(id=id) - - if sg["name"] == 'default' and not context.is_admin: - raise ext_sg.SecurityGroupCannotRemoveDefault() - - sg_id = sg['id'] - filters = {'security_group_id': [sg_id]} - if super(MidonetPluginV2, self)._get_port_security_group_bindings( - context, filters): - raise ext_sg.SecurityGroupInUse(id=sg_id) - - # Delete MidoNet Chains and portgroup for the SG - tenant_id = sg['tenant_id'] - self.client.delete_chains_by_names( - tenant_id, _sg_chain_names(sg["id"]).values()) - - self.client.delete_port_group_by_name( - tenant_id, _sg_port_group_name(sg["id"])) - - super(MidonetPluginV2, self).delete_security_group(context, id) - - def create_security_group_rule(self, context, security_group_rule): - """Create a security group rule - - Create a security group rule in the Neutron DB and corresponding - MidoNet resources in its data store. - """ - LOG.debug("MidonetPluginV2.create_security_group_rule called: " - "security_group_rule=%(security_group_rule)r", - {'security_group_rule': security_group_rule}) - - with context.session.begin(subtransactions=True): - rule = super(MidonetPluginV2, self).create_security_group_rule( - context, security_group_rule) - - self._create_accept_chain_rule(context, rule) - - LOG.debug("MidonetPluginV2.create_security_group_rule exiting: " - "rule=%r", rule) - return rule - - def delete_security_group_rule(self, context, sg_rule_id): - """Delete a security group rule - - Delete a security group rule from the Neutron DB and corresponding - MidoNet resources from its data store. - """ - LOG.debug("MidonetPluginV2.delete_security_group_rule called: " - "sg_rule_id=%s", sg_rule_id) - with context.session.begin(subtransactions=True): - rule = super(MidonetPluginV2, self).get_security_group_rule( - context, sg_rule_id) - - if not rule: - raise ext_sg.SecurityGroupRuleNotFound(id=sg_rule_id) - - sg = self._get_security_group(context, - rule["security_group_id"]) - chain_name = _sg_chain_names(sg["id"])[rule["direction"]] - self.client.remove_rules_by_property(rule["tenant_id"], chain_name, - OS_SG_RULE_KEY, - str(rule["id"])) - super(MidonetPluginV2, self).delete_security_group_rule( - context, sg_rule_id) - - def _add_chain_rule(self, chain, action, **kwargs): - - nw_proto = kwargs.get("nw_proto") - src_addr = kwargs.pop("src_addr", None) - dst_addr = kwargs.pop("dst_addr", None) - src_port_from = kwargs.pop("src_port_from", None) - src_port_to = kwargs.pop("src_port_to", None) - dst_port_from = kwargs.pop("dst_port_from", None) - dst_port_to = kwargs.pop("dst_port_to", None) - - # Convert to the keys and values that midonet client understands - if src_addr: - kwargs["nw_src_addr"], kwargs["nw_src_length"] = net_util.net_addr( - src_addr) - - if dst_addr: - kwargs["nw_dst_addr"], kwargs["nw_dst_length"] = net_util.net_addr( - dst_addr) - - kwargs["tp_src"] = {"start": src_port_from, "end": src_port_to} - - kwargs["tp_dst"] = {"start": dst_port_from, "end": dst_port_to} - - if nw_proto == 1: # ICMP - # Overwrite port fields regardless of the direction - kwargs["tp_src"] = {"start": src_port_from, "end": src_port_from} - kwargs["tp_dst"] = {"start": dst_port_to, "end": dst_port_to} - - return self.client.add_chain_rule(chain, action=action, **kwargs) diff --git a/neutron/plugins/midonet/requirements.txt b/neutron/plugins/midonet/requirements.txt new file mode 100644 index 000000000..fb9c6a570 --- /dev/null +++ b/neutron/plugins/midonet/requirements.txt @@ -0,0 +1 @@ +neutron-plugin-midonet diff --git a/neutron/tests/unit/midonet/__init__.py b/neutron/tests/unit/midonet/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutron/tests/unit/midonet/etc/midonet.ini.test b/neutron/tests/unit/midonet/etc/midonet.ini.test deleted file mode 100644 index 8e4fc847f..000000000 --- a/neutron/tests/unit/midonet/etc/midonet.ini.test +++ /dev/null @@ -1,16 +0,0 @@ -[midonet] - -# MidoNet API server URI -midonet_uri = http://localhost:8080/midonet-api - -# MidoNet admin username -username = admin - -# MidoNet admin password -password = passw0rd - -# Virtual provider router ID -provider_router_id = 00112233-0011-0011-0011-001122334455 - -# Virtual metadata router ID -metadata_router_id = ffeeddcc-ffee-ffee-ffee-ffeeddccbbaa diff --git a/neutron/tests/unit/midonet/mock_lib.py b/neutron/tests/unit/midonet/mock_lib.py deleted file mode 100644 index 8013ae5db..000000000 --- a/neutron/tests/unit/midonet/mock_lib.py +++ /dev/null @@ -1,261 +0,0 @@ -# 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. - -import mock -import uuid - - -def get_bridge_mock(id=None, **kwargs): - if id is None: - id = str(uuid.uuid4()) - - bridge = mock.Mock() - bridge.get_id.return_value = id - bridge.get_tenant_id.return_value = kwargs.get("tenant_id", "test-tenant") - bridge.get_name.return_value = kwargs.get("name", "net") - bridge.get_ports.return_value = [] - bridge.get_peer_ports.return_value = [] - bridge.get_admin_state_up.return_value = kwargs.get("admin_state_up", True) - return bridge - - -def get_bridge_port_mock(id=None, bridge_id=None, **kwargs): - if id is None: - id = str(uuid.uuid4()) - if bridge_id is None: - bridge_id = str(uuid.uuid4()) - - port = mock.Mock() - port.get_id.return_value = id - port.get_bridge_id.return_value = bridge_id - port.get_admin_state_up.return_value = kwargs.get("admin_state_up", True) - port.get_type.return_value = "Bridge" - port.create.return_value = port - return port - - -def get_chain_mock(id=None, tenant_id='test-tenant', name='chain', - rules=None): - if id is None: - id = str(uuid.uuid4()) - - if rules is None: - rules = [] - - chain = mock.Mock() - chain.get_id.return_value = id - chain.get_tenant_id.return_value = tenant_id - chain.get_name.return_value = name - chain.get_rules.return_value = rules - return chain - - -def get_port_group_mock(id=None, tenant_id='test-tenant', name='pg'): - if id is None: - id = str(uuid.uuid4()) - - port_group = mock.Mock() - port_group.get_id.return_value = id - port_group.get_tenant_id.return_value = tenant_id - port_group.get_name.return_value = name - return port_group - - -def get_router_mock(id=None, **kwargs): - if id is None: - id = str(uuid.uuid4()) - - router = mock.Mock() - router.get_id.return_value = id - router.get_tenant_id.return_value = kwargs.get("tenant_id", "test-tenant") - router.get_name.return_value = kwargs.get("name", "router") - router.get_ports.return_value = [] - router.get_peer_ports.return_value = [] - router.get_routes.return_value = [] - router.get_admin_state_up.return_value = kwargs.get("admin_state_up", True) - return router - - -def get_rule_mock(id=None, chain_id=None, properties=None): - if id is None: - id = str(uuid.uuid4()) - - if chain_id is None: - chain_id = str(uuid.uuid4()) - - if properties is None: - properties = {} - - rule = mock.Mock() - rule.get_id.return_value = id - rule.get_chain_id.return_value = chain_id - rule.get_properties.return_value = properties - return rule - - -def get_subnet_mock(bridge_id=None, gateway_ip='10.0.0.1', - subnet_prefix='10.0.0.0', subnet_len=int(24)): - if bridge_id is None: - bridge_id = str(uuid.uuid4()) - - subnet = mock.Mock() - subnet.get_id.return_value = subnet_prefix + '/' + str(subnet_len) - subnet.get_bridge_id.return_value = bridge_id - subnet.get_default_gateway.return_value = gateway_ip - subnet.get_subnet_prefix.return_value = subnet_prefix - subnet.get_subnet_length.return_value = subnet_len - return subnet - - -class MidonetLibMockConfig(object): - - def __init__(self, inst): - self.inst = inst - - def _create_bridge(self, **kwargs): - return get_bridge_mock(**kwargs) - - def _create_router(self, **kwargs): - return get_router_mock(**kwargs) - - 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, **kwargs): - return get_bridge_port_mock(bridge_id=bridge.get_id(), **kwargs) - - def _get_bridge(self, id): - return get_bridge_mock(id=id) - - def _get_port(self, id): - return get_bridge_port_mock(id=id) - - def _get_router(self, id): - return get_router_mock(id=id) - - def _update_bridge(self, id, **kwargs): - return get_bridge_mock(id=id, **kwargs) - - def setup(self): - # Bridge methods side effects - self.inst.create_bridge.side_effect = self._create_bridge - self.inst.get_bridge.side_effect = self._get_bridge - self.inst.update_bridge.side_effect = self._update_bridge - - # Subnet methods side effects - self.inst.create_subnet.side_effect = self._create_subnet - - # Port methods side effects - 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 - self.inst.create_router.side_effect = self._create_router - self.inst.get_router.side_effect = self._get_router - - -class MidoClientMockConfig(object): - - def __init__(self, inst): - self.inst = inst - self.chains_in = None - self.port_groups_in = None - self.chains_out = None - self.rules_out = None - self.port_groups_out = None - - def _get_query_tenant_id(self, query): - if query is not None and query['tenant_id']: - tenant_id = query['tenant_id'] - else: - tenant_id = 'test-tenant' - return tenant_id - - 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 [] - - tenant_id = self._get_query_tenant_id(query) - self.chains_out = [] - self.rules_out = [] - for chain in self.chains_in: - chain_id = chain['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'])) - self.rules_out += rule_mocks - - self.chains_out.append(get_chain_mock(id=chain_id, - name=chain['name'], - tenant_id=tenant_id, - rules=rule_mocks)) - return self.chains_out - - def _get_port_groups(self, query=None): - if not self.port_groups_in: - return [] - - tenant_id = self._get_query_tenant_id(query) - self.port_groups_out = [] - for port_group in self.port_groups_in: - self.port_groups_out.append(get_port_group_mock( - id=port_group['id'], name=port_group['name'], - tenant_id=tenant_id)) - return self.port_groups_out - - def _get_router(self, id): - return get_router_mock(id=id) - - def _add_bridge_port(self, bridge): - return get_bridge_port_mock(bridge_id=bridge.get_id()) - - 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 - self.inst.add_bridge_port.side_effect = self._add_bridge_port diff --git a/neutron/tests/unit/midonet/test_midonet_driver.py b/neutron/tests/unit/midonet/test_midonet_driver.py deleted file mode 100644 index 9816cf8a3..000000000 --- a/neutron/tests/unit/midonet/test_midonet_driver.py +++ /dev/null @@ -1,52 +0,0 @@ -# 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. - -import mock - -from neutron.agent.common import config -from neutron.agent.linux import dhcp -from neutron.common import config as base_config -import neutron.plugins.midonet.agent.midonet_driver as driver -from neutron.tests import base - - -class FakeNetwork(object): - id = 'aaaabbbb-cccc-dddd-eeee-ffff00001111' - namespace = 'qdhcp-ns' - - -class TestDhcpNoOpDriver(base.BaseTestCase): - def setUp(self): - super(TestDhcpNoOpDriver, self).setUp() - self.conf = config.setup_conf() - config.register_interface_driver_opts_helper(self.conf) - self.conf.register_opts(base_config.core_opts) - self.conf.register_opts(dhcp.OPTS) - self.conf.enable_isolated_metadata = True - self.conf.use_namespaces = True - instance = mock.patch("neutron.agent.linux.dhcp.DeviceManager") - self.mock_mgr = instance.start() - self.makedirs = mock.patch('os.makedirs').start() - - def test_disable_no_retain_port(self): - dhcp_driver = driver.DhcpNoOpDriver(self.conf, FakeNetwork()) - dhcp_driver.disable(retain_port=False) - self.assertTrue(self.mock_mgr.return_value.destroy.called) - - def test_disable_retain_port(self): - dhcp_driver = driver.DhcpNoOpDriver(self.conf, FakeNetwork()) - dhcp_driver.disable(retain_port=True) - self.assertFalse(self.mock_mgr.return_value.destroy.called) diff --git a/neutron/tests/unit/midonet/test_midonet_lib.py b/neutron/tests/unit/midonet/test_midonet_lib.py deleted file mode 100644 index b581493ea..000000000 --- a/neutron/tests/unit/midonet/test_midonet_lib.py +++ /dev/null @@ -1,188 +0,0 @@ -# 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. -import sys - -import mock -import testtools -import webob.exc as w_exc - -from neutron.openstack.common import uuidutils -with mock.patch.dict(sys.modules, {'midonetclient': mock.Mock()}): - from neutron.plugins.midonet import midonet_lib -import neutron.tests.unit.midonet.mock_lib as mock_lib - - -def _create_test_chain(id, name, tenant_id): - return {'id': id, 'name': name, 'tenant_id': tenant_id} - - -def _create_test_port_group(id, name, tenant_id): - return {"id": id, "name": name, "tenant_id": tenant_id} - - -class MidoClientTestCase(testtools.TestCase): - - def setUp(self): - super(MidoClientTestCase, self).setUp() - self._tenant_id = 'test-tenant' - self.mock_api = mock.Mock() - self.mock_api_cfg = mock_lib.MidoClientMockConfig(self.mock_api) - self.mock_api_cfg.setup() - self.client = midonet_lib.MidoClient(self.mock_api) - - def test_delete_chains_by_names(self): - - 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_delete_port_group_by_name(self): - - 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) - - 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) - - def test_create_dhcp(self): - - bridge = mock.Mock() - - gateway_ip = "192.168.1.1" - cidr = "192.168.1.0/24" - host_rts = [{'destination': '10.0.0.0/24', 'nexthop': '10.0.0.1'}, - {'destination': '10.0.1.0/24', 'nexthop': '10.0.1.1'}] - dns_servers = ["8.8.8.8", "8.8.4.4"] - - dhcp_call = mock.call.add_bridge_dhcp(bridge, gateway_ip, cidr, - host_rts=host_rts, - dns_nservers=dns_servers) - - self.client.create_dhcp(bridge, gateway_ip, cidr, host_rts=host_rts, - dns_servers=dns_servers) - self.mock_api.assert_has_calls([dhcp_call]) - - def test_delete_dhcp(self): - - bridge = mock.Mock() - subnet1 = mock.Mock() - subnet1.get_subnet_prefix.return_value = "10.0.0.0" - subnet1.get_subnet_length.return_value = "16" - subnet2 = mock.Mock() - subnet2.get_subnet_prefix.return_value = "10.0.0.0" - subnet2.get_subnet_length.return_value = "24" - subnets = mock.MagicMock(return_value=[subnet1, subnet2]) - bridge.get_dhcp_subnets.side_effect = subnets - self.client.delete_dhcp(bridge, "10.0.0.0/24") - bridge.assert_has_calls(mock.call.get_dhcp_subnets) - self.assertFalse(subnet1.delete.called) - subnet2.delete.assert_called_once_with() - - def test_add_dhcp_host(self): - - 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()] - - 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_add_dhcp_route_option(self): - - bridge = mock.Mock() - subnet = bridge.get_dhcp_subnet.return_value - subnet.get_opt121_routes.return_value = None - dhcp_subnet_call = mock.call.get_dhcp_subnet("10.0.0.0_24") - dst_ip = "10.0.0.3/24" - gw_ip = "10.0.0.1" - prefix, length = dst_ip.split("/") - routes = [{'destinationPrefix': prefix, 'destinationLength': length, - 'gatewayAddr': gw_ip}] - opt121_routes_call = dhcp_subnet_call.opt121_routes(routes) - calls = [dhcp_subnet_call, opt121_routes_call, - opt121_routes_call.update()] - - self.client.add_dhcp_route_option(bridge, "10.0.0.0/24", - gw_ip, dst_ip) - bridge.assert_has_calls(calls, any_order=True) - - def test_get_router_error(self): - self.mock_api.get_router.side_effect = w_exc.HTTPInternalServerError() - self.assertRaises(midonet_lib.MidonetApiException, - self.client.get_router, uuidutils.generate_uuid()) - - def test_get_router_not_found(self): - self.mock_api.get_router.side_effect = w_exc.HTTPNotFound() - self.assertRaises(midonet_lib.MidonetResourceNotFound, - self.client.get_router, uuidutils.generate_uuid()) - - 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_bridge(self): - bridge_id = uuidutils.generate_uuid() - - bridge = self.client.get_bridge(bridge_id) - - self.assertIsNotNone(bridge) - self.assertEqual(bridge.get_id(), bridge_id) - self.assertTrue(bridge.get_admin_state_up()) - - def test_add_bridge_port(self): - bridge_id = uuidutils.generate_uuid() - - bridge = self.client.get_bridge(bridge_id) - - self.assertIsNotNone(bridge) - - port = self.client.add_bridge_port(bridge) - - self.assertEqual(bridge.get_id(), port.get_bridge_id()) - self.assertTrue(port.get_admin_state_up()) - - def test_get_router(self): - router_id = uuidutils.generate_uuid() - - router = self.client.get_router(router_id) - - self.assertIsNotNone(router) - self.assertEqual(router.get_id(), router_id) - self.assertTrue(router.get_admin_state_up()) diff --git a/neutron/tests/unit/midonet/test_midonet_plugin.py b/neutron/tests/unit/midonet/test_midonet_plugin.py deleted file mode 100644 index 6a2478295..000000000 --- a/neutron/tests/unit/midonet/test_midonet_plugin.py +++ /dev/null @@ -1,249 +0,0 @@ -# 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. - -import mock -import os -import sys - -import neutron.common.test_lib as test_lib -from neutron.extensions import portbindings -from neutron.tests.unit import _test_extension_portbindings as test_bindings -import neutron.tests.unit.midonet.mock_lib as mock_lib -import neutron.tests.unit.test_db_plugin as test_plugin -import neutron.tests.unit.test_extension_security_group as sg -import neutron.tests.unit.test_l3_plugin as test_l3_plugin - -MIDOKURA_PKG_PATH = "neutron.plugins.midonet.plugin" -MIDONET_PLUGIN_NAME = ('%s.MidonetPluginV2' % MIDOKURA_PKG_PATH) - - -class MidonetPluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase): - - def setUp(self, - plugin=MIDONET_PLUGIN_NAME, - ext_mgr=None, - service_plugins=None): - self.mock_api = mock.patch( - 'neutron.plugins.midonet.midonet_lib.MidoClient') - etc_path = os.path.join(os.path.dirname(__file__), 'etc') - test_lib.test_config['config_files'] = [os.path.join( - etc_path, 'midonet.ini.test')] - - p = mock.patch.dict(sys.modules, {'midonetclient': mock.Mock()}) - p.start() - # dict patches must be explicitly stopped - self.addCleanup(p.stop) - self.instance = self.mock_api.start() - mock_cfg = mock_lib.MidonetLibMockConfig(self.instance.return_value) - mock_cfg.setup() - - self.midoclient_mock = mock.MagicMock() - self.midoclient_mock.midonetclient.neutron.client.return_value = True - modules = { - 'midonetclient': self.midoclient_mock, - 'midonetclient.neutron': self.midoclient_mock.neutron, - 'midonetclient.neutron.client': self.midoclient_mock.client, - } - - self.module_patcher = mock.patch.dict('sys.modules', modules) - self.module_patcher.start() - self.addCleanup(self.module_patcher.stop) - - # import midonetclient here because it needs proper mock objects to be - # assigned to this module first. 'midoclient_mock' object is the - # mock object used for this module. - from midonetclient.neutron.client import MidonetClient - client_class = MidonetClient - self.mock_class = client_class() - - super(MidonetPluginV2TestCase, self).setUp(plugin=plugin) - - -class TestMidonetNetworksV2(test_plugin.TestNetworksV2, - MidonetPluginV2TestCase): - - pass - - -class TestMidonetL3NatTestCase(MidonetPluginV2TestCase, - test_l3_plugin.L3NatDBIntTestCase): - def setUp(self, - plugin=MIDONET_PLUGIN_NAME, - ext_mgr=None, - service_plugins=None): - super(TestMidonetL3NatTestCase, self).setUp(plugin=plugin, - ext_mgr=None, - service_plugins=None) - - def test_floatingip_with_invalid_create_port(self): - self._test_floatingip_with_invalid_create_port(MIDONET_PLUGIN_NAME) - - def test_floatingip_assoc_no_port(self): - with self.subnet(cidr='200.0.0.0/24') as public_sub: - self._set_net_external(public_sub['subnet']['network_id']) - res = super(TestMidonetL3NatTestCase, self)._create_floatingip( - self.fmt, public_sub['subnet']['network_id']) - # Cleanup - floatingip = self.deserialize(self.fmt, res) - self._delete('floatingips', floatingip['floatingip']['id']) - self.assertFalse(self.instance.return_value.add_static_nat.called) - - def test_floatingip_assoc_with_port(self): - with self.subnet(cidr='200.0.0.0/24') as public_sub: - self._set_net_external(public_sub['subnet']['network_id']) - with self.port() as private_port: - with self.router() as r: - # We need to hook up the private subnet to the external - # network in order to associate the fip. - sid = private_port['port']['fixed_ips'][0]['subnet_id'] - private_sub = {'subnet': {'id': sid}} - self._add_external_gateway_to_router( - r['router']['id'], - public_sub['subnet']['network_id']) - - # Check that get_link_port was called - if not, Source NAT - # will not be set up correctly on the MidoNet side - self.assertTrue( - self.instance.return_value.get_link_port.called) - - self._router_interface_action('add', r['router']['id'], - private_sub['subnet']['id'], - None) - - # Create the fip. - res = super(TestMidonetL3NatTestCase, - self)._create_floatingip( - self.fmt, - public_sub['subnet']['network_id'], - port_id=private_port['port']['id']) - - # Cleanup the resources used for the test - floatingip = self.deserialize(self.fmt, res) - self._delete('floatingips', floatingip['floatingip']['id']) - self._remove_external_gateway_from_router( - r['router']['id'], - public_sub['subnet']['network_id']) - self._router_interface_action('remove', - r['router']['id'], - private_sub['subnet']['id'], - None) - self.assertTrue(self.instance.return_value.add_static_nat.called) - - def test_delete_ext_net_with_disassociated_floating_ips(self): - pass - - -class TestMidonetSecurityGroupsTestCase(sg.SecurityGroupDBTestCase): - - _plugin_name = ('%s.MidonetPluginV2' % MIDOKURA_PKG_PATH) - - def setUp(self): - self.mock_api = mock.patch( - 'neutron.plugins.midonet.midonet_lib.MidoClient') - etc_path = os.path.join(os.path.dirname(__file__), 'etc') - test_lib.test_config['config_files'] = [os.path.join( - etc_path, 'midonet.ini.test')] - - self.instance = self.mock_api.start() - mock_cfg = mock_lib.MidonetLibMockConfig(self.instance.return_value) - mock_cfg.setup() - p = mock.patch.dict(sys.modules, {'midonetclient': mock.Mock()}) - p.start() - # dict patches must be explicitly stopped - self.addCleanup(p.stop) - self.midoclient_mock = mock.MagicMock() - self.midoclient_mock.midonetclient.neutron.client.return_value = True - modules = { - 'midonetclient': self.midoclient_mock, - 'midonetclient.neutron': self.midoclient_mock.neutron, - 'midonetclient.neutron.client': self.midoclient_mock.client, - } - - self.module_patcher = mock.patch.dict('sys.modules', modules) - self.module_patcher.start() - self.addCleanup(self.module_patcher.stop) - - # import midonetclient here because it needs proper mock objects to be - # assigned to this module first. 'midoclient_mock' object is the - # mock object used for this module. - from midonetclient.neutron.client import MidonetClient - client_class = MidonetClient - self.mock_class = client_class() - - super(TestMidonetSecurityGroupsTestCase, self).setUp(self._plugin_name) - - -class TestMidonetSecurityGroup(sg.TestSecurityGroups, - TestMidonetSecurityGroupsTestCase): - - pass - - -class TestMidonetSubnetsV2(test_plugin.TestSubnetsV2, - MidonetPluginV2TestCase): - - # IPv6 is not supported by MidoNet yet. Ignore tests that attempt to - # create IPv6 subnet. - def test_create_subnet_inconsistent_ipv6_cidrv4(self): - pass - - def test_create_subnet_inconsistent_ipv6_dns_v4(self): - pass - - def test_create_subnet_with_v6_allocation_pool(self): - pass - - def test_update_subnet_inconsistent_ipv6_gatewayv4(self): - pass - - def test_update_subnet_inconsistent_ipv6_hostroute_dst_v4(self): - pass - - def test_update_subnet_inconsistent_ipv6_hostroute_np_v4(self): - pass - - def test_create_subnet_inconsistent_ipv6_gatewayv4(self): - pass - - def test_create_subnet_dhcp_disabled(self): - super(TestMidonetSubnetsV2, self)._test_create_subnet( - enable_dhcp=False) - self.assertFalse(self.instance.return_value.create_dhcp.called) - - -class TestMidonetPortsV2(test_plugin.TestPortsV2, - MidonetPluginV2TestCase): - - # IPv6 is not supported by MidoNet yet. Ignore tests that attempt to - # create IPv6 subnet. - - def test_requested_subnet_id_v4_and_v6(self): - pass - - def test_vif_port_binding(self): - with self.port(name='myname') as port: - self.assertEqual('midonet', port['port']['binding:vif_type']) - self.assertTrue(port['port']['admin_state_up']) - - -class TestMidonetPluginPortBinding(test_bindings.PortBindingsTestCase, - MidonetPluginV2TestCase): - - VIF_TYPE = portbindings.VIF_TYPE_MIDONET - HAS_PORT_FILTER = True - - def setUp(self): - super(TestMidonetPluginPortBinding, self).setUp() -- 2.45.2