diff --git a/run_tests.sh b/run_tests.sh index b2d5e84d96..3496ed798b 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -196,7 +196,7 @@ function run_pep8_changed { } -TESTRTESTS="python -m neutron.openstack.common.lockutils python setup.py testr" +TESTRTESTS="python setup.py testr" if [ $never_venv -eq 0 ] then diff --git a/setup.cfg b/setup.cfg index 73e91edfec..d5c28dc487 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,7 +23,11 @@ packages = vmware_nsx namespace_packages = vmware_nsx - +[entry_points] +vmware_nsx.neutron.nsxv.router_type_drivers = + shared = vmware_nsx.neutron.plugins.vmware.plugins.nsx_v_drivers.shared_router_driver:RouterSharedDriver + distributed = vmware_nsx.neutron.plugins.vmware.plugins.nsx_v_drivers.distributed_router_driver:RouterDistributedDriver + exclusive = vmware_nsx.neutron.plugins.vmware.plugins.nsx_v_drivers.exclusive_router_driver:RouterExclusiveDriver [build_sphinx] source-dir = doc/source build-dir = doc/build diff --git a/vmware_nsx/neutron/plugins/vmware/common/config.py b/vmware_nsx/neutron/plugins/vmware/common/config.py index 21c1672149..7cc4eb9780 100644 --- a/vmware_nsx/neutron/plugins/vmware/common/config.py +++ b/vmware_nsx/neutron/plugins/vmware/common/config.py @@ -239,7 +239,11 @@ nsxv_opts = [ cfg.BoolOpt('spoofguard_enabled', default=True, help=_("If True then plugin will use NSXV spoofguard " - "component for port-security feature.")) + "component for port-security feature.")), + cfg.ListOpt('tenant_router_types', + default=['shared', 'distributed', 'exclusive'], + help=_("Ordered list of router_types to allocate as tenant " + "routers.")) ] # Register the configuration options diff --git a/vmware_nsx/neutron/plugins/vmware/common/exceptions.py b/vmware_nsx/neutron/plugins/vmware/common/exceptions.py index 94cfdb27ad..5959ff23ad 100644 --- a/vmware_nsx/neutron/plugins/vmware/common/exceptions.py +++ b/vmware_nsx/neutron/plugins/vmware/common/exceptions.py @@ -105,3 +105,12 @@ class LsnMigrationConflict(n_exc.Conflict): class LsnConfigurationConflict(NsxPluginException): message = _("Configuration conflict on Logical Service Node %(lsn_id)s") + + +class DvsNotFound(n_exc.NotFound): + message = _('Unable to find DVS %(dvs)s') + + +class NoRouterAvailable(n_exc.ResourceExhausted): + message = _("Unable to create the router. " + "No tenant router is available for allocation.") diff --git a/vmware_nsx/neutron/plugins/vmware/dbexts/routertype.py b/vmware_nsx/neutron/plugins/vmware/dbexts/routertype.py new file mode 100644 index 0000000000..4b9bf8c8be --- /dev/null +++ b/vmware_nsx/neutron/plugins/vmware/dbexts/routertype.py @@ -0,0 +1,30 @@ +# Copyright 2014 VMware, Inc. 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.plugins.vmware.extensions import routertype as rt_rtr + +from vmware_nsx.neutron.plugins.vmware.dbexts import ( + distributedrouter as dist_rtr) + + +class RouterType_mixin(dist_rtr.DistributedRouter_mixin): + """Mixin class to enable Router type support.""" + + nsx_attributes = ( + dist_rtr.DistributedRouter_mixin.nsx_attributes + [{ + 'name': rt_rtr.ROUTER_TYPE, + 'default': False + }]) diff --git a/vmware_nsx/neutron/plugins/vmware/plugins/managers.py b/vmware_nsx/neutron/plugins/vmware/plugins/managers.py new file mode 100644 index 0000000000..292c01eba6 --- /dev/null +++ b/vmware_nsx/neutron/plugins/vmware/plugins/managers.py @@ -0,0 +1,92 @@ +# Copyright 2014 VMware, Inc. +# 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 +import stevedore + +from neutron.openstack.common import log +from neutron.plugins.vmware.common import exceptions as nsx_exc + + +LOG = log.getLogger(__name__) +ROUTER_TYPE_DRIVERS = ["distributed", "exclusive", "shared"] + + +class RouterTypeManager(stevedore.named.NamedExtensionManager): + """Manage router segment types using drivers.""" + + def __init__(self, plugin): + # Mapping from type name to DriverManager + self.drivers = {} + + LOG.info(_("Configured router type driver names: %s"), + ROUTER_TYPE_DRIVERS) + super(RouterTypeManager, self).__init__( + 'vmware_nsx.neutron.nsxv.router_type_drivers', + ROUTER_TYPE_DRIVERS, + invoke_on_load=True, + invoke_args=(plugin,)) + LOG.info(_("Loaded type driver names: %s"), self.names()) + self._register_types() + self._check_tenant_router_types(cfg.CONF.nsxv.tenant_router_types) + + def _register_types(self): + for ext in self: + router_type = ext.obj.get_type() + if router_type in self.drivers: + LOG.error(_("Type driver '%(new_driver)s' ignored because " + "type driver '%(old_driver)s' is already " + "registered for type '%(type)s'"), + {'new_driver': ext.name, + 'old_driver': self.drivers[router_type].name, + 'type': router_type}) + else: + self.drivers[router_type] = ext + LOG.info(_("Registered types: %s"), self.drivers.keys()) + + def _check_tenant_router_types(self, types): + self.tenant_router_types = [] + for router_type in types: + if router_type in self.drivers: + self.tenant_router_types.append(router_type) + else: + msg = _("No type driver for tenant router_type: %s. " + "Service terminated!") % router_type + LOG.error(msg) + raise SystemExit(msg) + LOG.info(_("Tenant router_types: %s"), self.tenant_router_types) + + def get_tenant_router_driver(self, context, router_type): + driver = self.drivers.get(router_type) + if driver: + return driver.obj + raise nsx_exc.NoRouterAvailable() + + def decide_tenant_router_type(self, context, router_type=None): + if router_type is None: + for rt in self.tenant_router_types: + driver = self.drivers.get(rt) + if driver: + return rt + raise nsx_exc.NoRouterAvailable() + elif context.is_admin: + driver = self.drivers.get(router_type) + if driver: + return router_type + elif router_type in self.tenant_router_types: + driver = self.drivers.get(router_type) + if driver: + return router_type + raise nsx_exc.NoRouterAvailable() diff --git a/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v.py b/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v.py index 842db90cd8..e85925444e 100644 --- a/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v.py +++ b/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v.py @@ -58,13 +58,12 @@ from vmware_nsx.neutron.plugins import vmware from vmware_nsx.neutron.plugins.vmware.common import config # noqa from vmware_nsx.neutron.plugins.vmware.common import utils as c_utils from vmware_nsx.neutron.plugins.vmware.dbexts import ( - distributedrouter as dist_rtr) + routertype as rt_rtr) from vmware_nsx.neutron.plugins.vmware.dbexts import db as nsx_db from vmware_nsx.neutron.plugins.vmware.dbexts import nsxv_db from vmware_nsx.neutron.plugins.vmware.dbexts import vnic_index_db +from vmware_nsx.neutron.plugins.vmware.plugins import managers from vmware_nsx.neutron.plugins.vmware.plugins import nsx_v_md_proxy -from vmware_nsx.neutron.plugins.vmware.vshield.common import ( - constants as vcns_const) from vmware_nsx.neutron.plugins.vmware.vshield.common import ( exceptions as vsh_exc) from vmware_nsx.neutron.plugins.vmware.vshield import edge_utils @@ -77,7 +76,7 @@ PORTGROUP_PREFIX = 'dvportgroup' class NsxVPluginV2(agents_db.AgentDbMixin, db_base_plugin_v2.NeutronDbPluginV2, - dist_rtr.DistributedRouter_mixin, + rt_rtr.RouterType_mixin, external_net_db.External_net_db_mixin, extraroute_db.ExtraRoute_db_mixin, l3_gwmode_db.L3_NAT_db_mixin, @@ -98,6 +97,7 @@ class NsxVPluginV2(agents_db.AgentDbMixin, "extraroute", "router", "security-group", + "nsxv-router-type", "vnic-index", "advanced-service-providers"] @@ -130,6 +130,7 @@ class NsxVPluginV2(agents_db.AgentDbMixin, self.sg_container_id = self._create_security_group_container() self._validate_config() self._create_cluster_default_fw_rules() + self._router_managers = managers.RouterTypeManager(self) has_metadata_cfg = ( cfg.CONF.nsxv.nova_metadata_ips @@ -151,6 +152,37 @@ class NsxVPluginV2(agents_db.AgentDbMixin, h, container_id = self.nsx_v.vcns.create_security_group(container) return container_id + def _find_router_driver(self, context, router_id): + router_db = self._get_router(context, router_id) + return self._get_router_driver(context, router_db) + + def _get_router_driver(self, context, router_db): + router_type_dict = {} + self._extend_nsx_router_dict(router_type_dict, router_db) + router_type = None + if router_type_dict.get("distributed", False): + router_type = "distributed" + else: + router_type = router_type_dict.get("router_type") + return self._router_managers.get_tenant_router_driver( + context, router_type) + + def _decide_router_type(self, context, r): + router_type = None + if attr.is_attr_set(r.get("distributed")) and r.get("distributed"): + router_type = "distributed" + elif attr.is_attr_set(r.get("router_type")): + router_type = r.get("router_type") + + router_type = self._router_managers.decide_tenant_router_type( + context, router_type) + if router_type == "distributed": + r["distributed"] = True + r["router_type"] = "exclusive" + else: + r["distributed"] = False + r["router_type"] = router_type + def _create_cluster_default_fw_rules(self): # default cluster rules rules = [{'name': 'Default DHCP rule for OS Security Groups', @@ -1160,34 +1192,25 @@ class NsxVPluginV2(agents_db.AgentDbMixin, gw_info = self._extract_external_gw(context, router) lrouter = super(NsxVPluginV2, self).create_router(context, router) r = router['router'] - distributed = r.get('distributed') - r['distributed'] = attr.is_attr_set(distributed) and distributed - self.edge_manager.create_lrouter(context, lrouter, - dist=r['distributed']) + self._decide_router_type(context, r) with context.session.begin(subtransactions=True): router_db = self._get_router(context, lrouter['id']) self._process_nsx_router_create(context, router_db, r) + router_driver = self._get_router_driver(context, router_db) + router_driver.create_router( + context, lrouter, + allow_metadata=(allow_metadata and self.metadata_proxy_handler)) if gw_info is not None: - self._update_router_gw_info(context, lrouter['id'], gw_info) - if (not r['distributed'] - and allow_metadata - and self.metadata_proxy_handler): - self.metadata_proxy_handler.configure_router_edge(lrouter['id']) + router_driver._update_router_gw_info( + context, lrouter['id'], gw_info) return self.get_router(context, lrouter['id']) def update_router(self, context, router_id, router): # TODO(berlin): admin_state_up update if router['router'].get('admin_state_up') is False: LOG.warning(_LW("admin_state_up=False router is not supported.")) - gw_info = self._extract_external_gw(context, router, is_extract=False) - router_updated = super(NsxVPluginV2, self).update_router( - context, router_id, router) - # here is used to handle routes which tenant updates. - if gw_info is None: - router_db = self._get_router(context, router_id) - nexthop = self._get_external_attachment_info(context, router_db)[2] - self._update_routes(context, router_id, nexthop) - return router_updated + router_driver = self._find_router_driver(context, router_id) + return router_driver.update_router(context, router_id, router) def _check_router_in_use(self, context, router_id): with context.session.begin(subtransactions=True): @@ -1207,10 +1230,8 @@ class NsxVPluginV2(agents_db.AgentDbMixin, def delete_router(self, context, id): self._check_router_in_use(context, id) - distributed = self.get_router(context, id).get('distributed', False) - if self.metadata_proxy_handler and not distributed: - self.metadata_proxy_handler.cleanup_router_edge(id) - self.edge_manager.delete_lrouter(context, id, dist=distributed) + router_driver = self._find_router_driver(context, id) + router_driver.delete_router(context, id) super(NsxVPluginV2, self).delete_router(context, id) def _get_external_attachment_info(self, context, router): @@ -1258,101 +1279,9 @@ class NsxVPluginV2(agents_db.AgentDbMixin, edge_utils.update_routes(self.nsx_v, context, router_id, routes, nexthop) - def _update_routes_on_plr(self, context, router_id, plr_id, newnexthop): - subnets = self._find_router_subnets_cidrs( - context.elevated(), router_id) - routes = [] - for subnet in subnets: - routes.append({ - 'destination': subnet, - 'nexthop': (vcns_const.INTEGRATION_LR_IPADDRESS. - split('/')[0]) - }) - edge_utils.update_routes_on_plr(self.nsx_v, context, - plr_id, router_id, routes, - nexthop=newnexthop) - def _update_router_gw_info(self, context, router_id, info): - router = self._get_router(context, router_id) - org_ext_net_id = router.gw_port_id and router.gw_port.network_id - org_enable_snat = router.enable_snat - orgaddr, orgmask, orgnexthop = self._get_external_attachment_info( - context, router) - - super(NsxVPluginV2, self)._update_router_gw_info( - context, router_id, info, router=router) - - new_ext_net_id = router.gw_port_id and router.gw_port.network_id - new_enable_snat = router.enable_snat - newaddr, newmask, newnexthop = self._get_external_attachment_info( - context, router) - - router_dict = self._make_router_dict(router) - if not router_dict.get('distributed', False): - if new_ext_net_id != org_ext_net_id and orgnexthop: - # network changed, so need to remove default gateway before - # vnic can be configured - LOG.debug("Delete default gateway %s", orgnexthop) - edge_utils.clear_gateway(self.nsx_v, context, router_id) - # Delete SNAT rules - if org_enable_snat: - edge_utils.clear_nat_rules(self.nsx_v, context, router_id) - - # Update external vnic if addr or mask is changed - if orgaddr != newaddr or orgmask != newmask: - edge_utils.update_external_interface( - self.nsx_v, context, router_id, - new_ext_net_id, newaddr, newmask) - - # Update SNAT rules if ext net changed and snat enabled - # or ext net not changed but snat is changed. - if ((new_ext_net_id != org_ext_net_id and - newnexthop and new_enable_snat) or - (new_ext_net_id == org_ext_net_id and - new_enable_snat != org_enable_snat)): - self._update_nat_rules(context, router) - - # Update static routes in all. - self._update_routes(context, router_id, newnexthop) - else: - plr_id = self.edge_manager.get_plr_by_tlr_id(context, router_id) - if not new_ext_net_id: - if plr_id: - # delete all plr relative conf - self.edge_manager.delete_plr_by_tlr_id( - context, plr_id, router_id) - else: - # Connecting one plr to the tlr if new_ext_net_id is not None. - if not plr_id: - plr_id = self.edge_manager.create_plr_with_tlr_id( - context, router_id, router_dict.get('name')) - if new_ext_net_id != org_ext_net_id and orgnexthop: - # network changed, so need to remove default gateway and - # all static routes before vnic can be configured - edge_utils.clear_gateway(self.nsx_v, context, plr_id) - # Delete SNAT rules - if org_enable_snat: - edge_utils.clear_nat_rules(self.nsx_v, context, plr_id) - - # Update external vnic if addr or mask is changed - if orgaddr != newaddr or orgmask != newmask: - edge_utils.update_external_interface( - self.nsx_v, context, plr_id, - new_ext_net_id, newaddr, newmask) - - # Update SNAT rules if ext net changed and snat enabled - # or ext net not changed but snat is changed. - if ((new_ext_net_id != org_ext_net_id and - newnexthop and new_enable_snat) or - (new_ext_net_id == org_ext_net_id and - new_enable_snat != org_enable_snat)): - self._update_nat_rules(context, router, plr_id) - # Open firewall flows on plr - self._update_subnets_and_dnat_firewall( - context, router, router_id=plr_id) - # Update static routes of plr - self._update_routes_on_plr( - context, router_id, plr_id, newnexthop) + router_driver = self._find_router_driver(context, router_id) + router_driver._update_router_gw_info(context, router_id, info) def _get_router_interface_ports_by_network( self, context, router_id, network_id): @@ -1429,96 +1358,14 @@ class NsxVPluginV2(agents_db.AgentDbMixin, self.nsx_v, context, router_id, snat, dnat) def add_router_interface(self, context, router_id, interface_info): - info = super(NsxVPluginV2, self).add_router_interface( + router_driver = self._find_router_driver(context, router_id) + return router_driver.add_router_interface( context, router_id, interface_info) - router_db = self._get_router(context, router_id) - router = self._make_router_dict(router_db) - distributed = router.get('distributed', False) - subnet = self.get_subnet(context, info['subnet_id']) - network_id = subnet['network_id'] - - address_groups = self._get_address_groups( - context, router_id, network_id) - if not distributed: - edge_utils.update_internal_interface( - self.nsx_v, context, router_id, network_id, address_groups) - else: - try: - edge_utils.add_vdr_internal_interface( - self.nsx_v, context, router_id, network_id, address_groups) - except n_exc.BadRequest: - with excutils.save_and_reraise_exception(): - super(NsxVPluginV2, self).remove_router_interface( - context, router_id, interface_info) - # Update edge's firewall rules to accept subnets flows. - self._update_subnets_and_dnat_firewall(context, router_db) - - if router_db.gw_port and router_db.enable_snat: - if not distributed: - # Update Nat rules on external edge vnic - self._update_nat_rules(context, router_db) - else: - plr_id = self.edge_manager.get_plr_by_tlr_id( - context, router_id) - self._update_nat_rules(context, router_db, plr_id) - # Open firewall flows on plr - self._update_subnets_and_dnat_firewall( - context, router_db, router_id=plr_id) - # Update static routes of plr - nexthop = self._get_external_attachment_info( - context, router_db)[2] - self._update_routes_on_plr( - context, router_id, plr_id, nexthop) - return info - def remove_router_interface(self, context, router_id, interface_info): - info = super(NsxVPluginV2, self).remove_router_interface( + router_driver = self._find_router_driver(context, router_id) + return router_driver.remove_router_interface( context, router_id, interface_info) - router_db = self._get_router(context, router_id) - router = self._make_router_dict(router_db) - distributed = router.get('distributed', False) - - subnet = self.get_subnet(context, info['subnet_id']) - network_id = subnet['network_id'] - if router_db.gw_port and router_db.enable_snat: - if not distributed: - # First update nat rules - self._update_nat_rules(context, router_db) - else: - plr_id = self.edge_manager.get_plr_by_tlr_id( - context, router_id) - self._update_nat_rules(context, router_db, plr_id) - # Open firewall flows on plr - self._update_subnets_and_dnat_firewall( - context, router_db, router_id=plr_id) - # Update static routes of plr - nexthop = self._get_external_attachment_info( - context, router_db)[2] - nexthop = self._get_external_attachment_info( - context, router_db)[2] - self._update_routes_on_plr( - context, router_id, plr_id, nexthop) - - ports = self._get_router_interface_ports_by_network( - context, router_id, network_id) - self._update_subnets_and_dnat_firewall(context, router_db) - # No subnet on the network connects to the edge vnic - if not ports: - edge_utils.delete_interface(self.nsx_v, context, - router_id, network_id, - dist=distributed) - else: - address_groups = self._get_address_groups( - context, router_id, network_id) - if not distributed: - edge_utils.update_internal_interface(self.nsx_v, context, - router_id, network_id, - address_groups) - else: - edge_utils.update_vdr_internal_interface( - self.nsx_v, context, router_id, network_id, address_groups) - return info def _get_floatingips_by_router(self, context, router_id): fip_qry = context.session.query(l3_db.FloatingIP) @@ -1546,6 +1393,10 @@ class NsxVPluginV2(agents_db.AgentDbMixin, floatingip_db['status'] = status self.update_floatingip_status(context, floatingip_db['id'], status) + def _update_edge_router(self, context, router_id): + router_driver = self._find_router_driver(context, router_id) + router_driver._update_edge_router(context, router_id) + def create_floatingip(self, context, floatingip): fip_db = super(NsxVPluginV2, self).create_floatingip( context, floatingip) @@ -1583,19 +1434,6 @@ class NsxVPluginV2(agents_db.AgentDbMixin, self._set_floatingip_status(context, fip_db) return fip_db - def _update_edge_router(self, context, router_id): - router = self._get_router(context, router_id) - distributed = self._make_router_dict(router).get( - 'distributed', False) - if distributed: - plr_id = self.edge_manager.get_plr_by_tlr_id(context, router_id) - else: - plr_id = None - self._update_external_interface(context, router, router_id=plr_id) - self._update_nat_rules(context, router, router_id=plr_id) - self._update_subnets_and_dnat_firewall(context, router, - router_id=plr_id) - def delete_floatingip(self, context, id): fip_db = self._get_floatingip(context, id) router_id = None @@ -1603,21 +1441,7 @@ class NsxVPluginV2(agents_db.AgentDbMixin, router_id = fip_db.router_id super(NsxVPluginV2, self).delete_floatingip(context, id) if router_id: - router = self._get_router(context, router_id) - distributed = self._make_router_dict(router).get( - 'distributed', False) - if not distributed: - self._update_subnets_and_dnat_firewall(context, router) - self._update_nat_rules(context, router) - self._update_external_interface(context, router) - else: - plr_id = self.edge_manager.get_plr_by_tlr_id(context, - router_id) - self._update_subnets_and_dnat_firewall( - context, router, router_id=plr_id) - self._update_nat_rules(context, router, router_id=plr_id) - self._update_external_interface( - context, router, router_id=plr_id) + self._update_edge_router(context, router_id) def disassociate_floatingips(self, context, port_id): router_id = None @@ -1632,21 +1456,7 @@ class NsxVPluginV2(agents_db.AgentDbMixin, router_id = None super(NsxVPluginV2, self).disassociate_floatingips(context, port_id) if router_id: - router = self._get_router(context, router_id) - distributed = self._make_router_dict(router).get( - 'distributed', False) - if not distributed: - self._update_subnets_and_dnat_firewall(context, router) - self._update_nat_rules(context, router) - self._update_external_interface(context, router) - else: - plr_id = self.edge_manager.get_plr_by_tlr_id(context, - router_id) - self._update_subnets_and_dnat_firewall( - context, router, router_id=plr_id) - self._update_nat_rules(context, router, router_id=plr_id) - self._update_external_interface( - context, router, router_id=plr_id) + self._update_edge_router(context, router_id) def _update_subnets_and_dnat_firewall(self, context, router, router_id=None, allow_external=True): @@ -1659,8 +1469,8 @@ class NsxVPluginV2(agents_db.AgentDbMixin, fake_subnet_fw_rule = { 'action': 'allow', 'enabled': True, - 'source_ip_address': subnet_cidrs, - 'destination_ip_address': subnet_cidrs} + 'source_ip_address': sorted(subnet_cidrs), + 'destination_ip_address': sorted(subnet_cidrs)} fake_fw_rules.append(fake_subnet_fw_rule) _, dnat_rules = self._get_nat_rules(context, router) @@ -1674,7 +1484,7 @@ class NsxVPluginV2(agents_db.AgentDbMixin, fake_dnat_fw_rule = { 'action': 'allow', 'enabled': True, - 'destination_ip_address': dnat_cidrs} + 'destination_ip_address': sorted(dnat_cidrs)} fake_fw_rules.append(fake_dnat_fw_rule) # TODO(berlin): Add fw rules if fw service is supported fake_fw = {'firewall_rule_list': fake_fw_rules} diff --git a/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v_drivers/__init__.py b/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v_drivers/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v_drivers/abstract_router_driver.py b/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v_drivers/abstract_router_driver.py new file mode 100644 index 0000000000..cc8abf7fbb --- /dev/null +++ b/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v_drivers/abstract_router_driver.py @@ -0,0 +1,66 @@ +# Copyright 2014 VMware, Inc +# +# 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 abc + +import six + + +@six.add_metaclass(abc.ABCMeta) +class RouterAbstractDriver(object): + """Abstract router driver that expose API for nsxv plugin.""" + + @abc.abstractmethod + def get_type(self): + pass + + @abc.abstractmethod + def create_router(self, context, lrouter): + pass + + @abc.abstractmethod + def update_router(self, context, router_id, router): + pass + + @abc.abstractmethod + def delete_router(self, context, router_id): + pass + + @abc.abstractmethod + def update_routes(self, context, router_id, nexthop): + pass + + @abc.abstractmethod + def _update_router_gw_info(self, context, router_id, info): + pass + + @abc.abstractmethod + def add_router_interface(self, context, router_id, interface_info): + pass + + @abc.abstractmethod + def remove_router_interface(self, context, router_id, interface_info): + pass + + @abc.abstractmethod + def _update_edge_router(self, context, router_id): + pass + + +class RouterBaseDriver(RouterAbstractDriver): + + def __init__(self, plugin): + self.plugin = plugin + self.nsx_v = plugin.nsx_v + self.edge_manager = plugin.edge_manager diff --git a/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v_drivers/distributed_router_driver.py b/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v_drivers/distributed_router_driver.py new file mode 100644 index 0000000000..56c0ef640f --- /dev/null +++ b/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v_drivers/distributed_router_driver.py @@ -0,0 +1,194 @@ +# Copyright 2014 VMware, Inc +# +# 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.utils import excutils + +from neutron.common import exceptions as n_exc + +from vmware_nsx.neutron.plugins.vmware.plugins import nsx_v +from vmware_nsx.neutron.plugins.vmware.plugins.nsx_v_drivers import ( + abstract_router_driver as router_driver) +from vmware_nsx.neutron.plugins.vmware.vshield.common import ( + constants as vcns_const) +from vmware_nsx.neutron.plugins.vmware.vshield import edge_utils + + +class RouterDistributedDriver(router_driver.RouterBaseDriver): + + def get_type(self): + return "distributed" + + def _update_routes_on_plr(self, context, router_id, plr_id, newnexthop): + subnets = self.plugin._find_router_subnets_cidrs( + context.elevated(), router_id) + routes = [] + for subnet in subnets: + routes.append({ + 'destination': subnet, + 'nexthop': (vcns_const.INTEGRATION_LR_IPADDRESS. + split('/')[0]) + }) + edge_utils.update_routes_on_plr(self.nsx_v, context, + plr_id, router_id, routes, + nexthop=newnexthop) + + def create_router(self, context, lrouter, allow_metadata=True): + self.edge_manager.create_lrouter(context, lrouter, dist=True) + # TODO(kobi) can't configure metadata service on VDR at present. + + def update_router(self, context, router_id, router): + return super(nsx_v.NsxVPluginV2, self.plugin).update_router( + context, router_id, router) + + def delete_router(self, context, router_id): + self.edge_manager.delete_lrouter(context, router_id, dist=True) + + def update_routes(self, context, router_id, nexthop): + # Since there may be an internal plr connected to the VDR affecting + # static routes, static routes feature should be avoided on VDR now. + pass + + def _update_router_gw_info(self, context, router_id, info): + router = self.plugin._get_router(context, router_id) + org_ext_net_id = router.gw_port_id and router.gw_port.network_id + org_enable_snat = router.enable_snat + orgaddr, orgmask, orgnexthop = ( + self.plugin._get_external_attachment_info( + context, router)) + + super(nsx_v.NsxVPluginV2, self.plugin)._update_router_gw_info( + context, router_id, info, router=router) + + new_ext_net_id = router.gw_port_id and router.gw_port.network_id + new_enable_snat = router.enable_snat + newaddr, newmask, newnexthop = ( + self.plugin._get_external_attachment_info( + context, router)) + + plr_id = self.edge_manager.get_plr_by_tlr_id(context, router_id) + if not new_ext_net_id: + if plr_id: + # delete all plr relative conf + self.edge_manager.delete_plr_by_tlr_id( + context, plr_id, router_id) + else: + # Connecting plr to the tlr if new_ext_net_id is not None. + if not plr_id: + plr_id = self.edge_manager.create_plr_with_tlr_id( + context, router_id, router.get('name')) + if new_ext_net_id != org_ext_net_id and orgnexthop: + # network changed, so need to remove default gateway + # and all static routes before vnic can be configured + edge_utils.clear_gateway(self.nsx_v, context, plr_id) + # Delete SNAT rules + if org_enable_snat: + edge_utils.clear_nat_rules(self.nsx_v, context, + plr_id) + + # Update external vnic if addr or mask is changed + if orgaddr != newaddr or orgmask != newmask: + edge_utils.update_external_interface( + self.nsx_v, context, plr_id, + new_ext_net_id, newaddr, newmask) + + # Update SNAT rules if ext net changed and snat enabled + # or ext net not changed but snat is changed. + if ((new_ext_net_id != org_ext_net_id and + newnexthop and new_enable_snat) or + (new_ext_net_id == org_ext_net_id and + new_enable_snat != org_enable_snat)): + self.plugin._update_nat_rules(context, router, plr_id) + # Open firewall flows on plr + self.plugin._update_subnets_and_dnat_firewall( + context, router, router_id=plr_id) + # Update static routes of plr + self._update_routes_on_plr( + context, router_id, plr_id, newnexthop) + + def add_router_interface(self, context, router_id, interface_info): + info = super(nsx_v.NsxVPluginV2, self.plugin).add_router_interface( + context, router_id, interface_info) + + router_db = self.plugin._get_router(context, router_id) + subnet = self.plugin.get_subnet(context, info['subnet_id']) + network_id = subnet['network_id'] + address_groups = self.plugin._get_address_groups( + context, router_id, network_id) + try: + edge_utils.add_vdr_internal_interface( + self.nsx_v, context, router_id, + network_id, address_groups) + except n_exc.BadRequest: + with excutils.save_and_reraise_exception(): + super(nsx_v.NsxVPluginV2, self.plugin).remove_router_interface( + context, router_id, interface_info) + # Update edge's firewall rules to accept subnets flows. + self.plugin._update_subnets_and_dnat_firewall(context, router_db) + + if router_db.gw_port and router_db.enable_snat: + plr_id = self.edge_manager.get_plr_by_tlr_id(context, router_id) + self.plugin._update_nat_rules(context, router_db, plr_id) + # Open firewall flows on plr + self.plugin._update_subnets_and_dnat_firewall( + context, router_db, router_id=plr_id) + # Update static routes of plr + nexthop = self.plugin._get_external_attachment_info( + context, router_db)[2] + self._update_routes_on_plr( + context, router_id, plr_id, nexthop) + return info + + def remove_router_interface(self, context, router_id, interface_info): + info = super(nsx_v.NsxVPluginV2, self.plugin).remove_router_interface( + context, router_id, interface_info) + router_db = self.plugin._get_router(context, router_id) + subnet = self.plugin.get_subnet(context, info['subnet_id']) + network_id = subnet['network_id'] + if router_db.gw_port and router_db.enable_snat: + plr_id = self.edge_manager.get_plr_by_tlr_id( + context, router_id) + self.plugin._update_nat_rules(context, router_db, plr_id) + # Open firewall flows on plr + self.plugin._update_subnets_and_dnat_firewall( + context, router_db, router_id=plr_id) + # Update static routes of plr + nexthop = self.plugin._get_external_attachment_info( + context, router_db)[2] + self._update_routes_on_plr( + context, router_id, plr_id, nexthop) + + ports = self.plugin._get_router_interface_ports_by_network( + context, router_id, network_id) + self.plugin._update_subnets_and_dnat_firewall(context, router_db) + # No subnet on the network connects to the edge vnic + if not ports: + edge_utils.delete_interface(self.nsx_v, context, + router_id, network_id, + dist=True) + else: + address_groups = self.plugin._get_address_groups( + context, router_id, network_id) + edge_utils.update_vdr_internal_interface( + self.nsx_v, context, router_id, + network_id, address_groups) + return info + + def _update_edge_router(self, context, router_id): + router = self.plugin._get_router(context, router_id) + plr_id = self.edge_manager.get_plr_by_tlr_id(context, router_id) + self.plugin._update_external_interface( + context, router, router_id=plr_id) + self.plugin._update_nat_rules(context, router, router_id=plr_id) + self.plugin._update_subnets_and_dnat_firewall(context, router, + router_id=plr_id) diff --git a/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v_drivers/exclusive_router_driver.py b/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v_drivers/exclusive_router_driver.py new file mode 100644 index 0000000000..b0ee1e6b68 --- /dev/null +++ b/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v_drivers/exclusive_router_driver.py @@ -0,0 +1,148 @@ +# Copyright 2014 VMware, Inc +# +# 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.openstack.common import log as logging + +from vmware_nsx.neutron.plugins.vmware.plugins import nsx_v +from vmware_nsx.neutron.plugins.vmware.plugins.nsx_v_drivers import ( + abstract_router_driver as router_driver) +from vmware_nsx.neutron.plugins.vmware.vshield import edge_utils + +LOG = logging.getLogger(__name__) + + +class RouterExclusiveDriver(router_driver.RouterBaseDriver): + + def get_type(self): + return "exclusive" + + def create_router(self, context, lrouter, allow_metadata=True): + self.edge_manager.create_lrouter(context, lrouter, dist=False) + if allow_metadata: + self.plugin.metadata_proxy_handler.configure_router_edge( + lrouter['id']) + + def update_router(self, context, router_id, router): + gw_info = self.plugin._extract_external_gw(context, router, + is_extract=False) + router_updated = super(nsx_v.NsxVPluginV2, self.plugin).update_router( + context, router_id, router) + # here is used to handle routes which tenant updates. + if gw_info is None: + router_db = self.plugin._get_router(context, router_id) + nexthop = self.plugin._get_external_attachment_info( + context, router_db)[2] + self.update_routes(context, router_id, nexthop) + return router_updated + + def delete_router(self, context, router_id): + self.edge_manager.delete_lrouter(context, router_id, dist=False) + if self.plugin.metadata_proxy_handler: + self.plugin.metadata_proxy_handler.cleanup_router_edge(router_id) + + def update_routes(self, context, router_id, nexthop): + self.plugin._update_routes(context, router_id, nexthop) + + def _update_router_gw_info(self, context, router_id, info): + router = self.plugin._get_router(context, router_id) + org_ext_net_id = router.gw_port_id and router.gw_port.network_id + org_enable_snat = router.enable_snat + orgaddr, orgmask, orgnexthop = ( + self.plugin._get_external_attachment_info( + context, router)) + + super(nsx_v.NsxVPluginV2, self.plugin)._update_router_gw_info( + context, router_id, info, router=router) + + new_ext_net_id = router.gw_port_id and router.gw_port.network_id + new_enable_snat = router.enable_snat + newaddr, newmask, newnexthop = ( + self.plugin._get_external_attachment_info( + context, router)) + + if new_ext_net_id != org_ext_net_id and orgnexthop: + # network changed, so need to remove default gateway before + # vnic can be configured + LOG.debug("Delete default gateway %s", orgnexthop) + edge_utils.clear_gateway(self.nsx_v, context, router_id) + # Delete SNAT rules + if org_enable_snat: + edge_utils.clear_nat_rules(self.nsx_v, context, router_id) + + # Update external vnic if addr or mask is changed + if orgaddr != newaddr or orgmask != newmask: + edge_utils.update_external_interface( + self.nsx_v, context, router_id, + new_ext_net_id, newaddr, newmask) + + # Update SNAT rules if ext net changed and snat enabled + # or ext net not changed but snat is changed. + if ((new_ext_net_id != org_ext_net_id and + newnexthop and new_enable_snat) or + (new_ext_net_id == org_ext_net_id and + new_enable_snat != org_enable_snat)): + self.plugin._update_nat_rules(context, router) + + # Update static routes in all. + self.plugin._update_routes(context, router_id, newnexthop) + + def add_router_interface(self, context, router_id, interface_info): + info = super(nsx_v.NsxVPluginV2, self.plugin).add_router_interface( + context, router_id, interface_info) + + router_db = self.plugin._get_router(context, router_id) + subnet = self.plugin.get_subnet(context, info['subnet_id']) + network_id = subnet['network_id'] + address_groups = self.plugin._get_address_groups( + context, router_id, network_id) + edge_utils.update_internal_interface( + self.nsx_v, context, router_id, network_id, address_groups) + # Update edge's firewall rules to accept subnets flows. + self.plugin._update_subnets_and_dnat_firewall(context, router_db) + + if router_db.gw_port and router_db.enable_snat: + # Update Nat rules on external edge vnic + self.plugin._update_nat_rules(context, router_db) + return info + + def remove_router_interface(self, context, router_id, interface_info): + info = super(nsx_v.NsxVPluginV2, self.plugin).remove_router_interface( + context, router_id, interface_info) + router_db = self.plugin._get_router(context, router_id) + subnet = self.plugin.get_subnet(context, info['subnet_id']) + network_id = subnet['network_id'] + if router_db.gw_port and router_db.enable_snat: + # First update nat rules + self.plugin._update_nat_rules(context, router_db) + ports = self.plugin._get_router_interface_ports_by_network( + context, router_id, network_id) + self.plugin._update_subnets_and_dnat_firewall(context, router_db) + # No subnet on the network connects to the edge vnic + if not ports: + edge_utils.delete_interface(self.nsx_v, context, + router_id, network_id, + dist=False) + else: + address_groups = self.plugin._get_address_groups( + context, router_id, network_id) + edge_utils.update_internal_interface(self.nsx_v, context, + router_id, network_id, + address_groups) + return info + + def _update_edge_router(self, context, router_id): + router = self.plugin._get_router(context, router_id) + self.plugin._update_external_interface(context, router) + self.plugin._update_nat_rules(context, router) + self.plugin._update_subnets_and_dnat_firewall(context, router) diff --git a/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v_drivers/shared_router_driver.py b/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v_drivers/shared_router_driver.py new file mode 100644 index 0000000000..23fa3eb34c --- /dev/null +++ b/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v_drivers/shared_router_driver.py @@ -0,0 +1,60 @@ +# Copyright 2014 VMware, Inc +# +# 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 vmware_nsx.neutron.plugins.vmware.plugins import nsx_v +from vmware_nsx.neutron.plugins.vmware.plugins.nsx_v_drivers import ( + abstract_router_driver as router_driver) + + +class RouterSharedDriver(router_driver.RouterBaseDriver): + + def get_type(self): + return "shared" + + def create_router(self, context, lrouter, allow_metadata=True): + pass + + def update_router(self, context, router_id, router): + return super(nsx_v.NsxVPluginV2, self.plugin).update_router( + context, router_id, router) + + def delete_router(self, context, router_id): + pass + + def update_routes(self, context, router_id, nexthop): + #TODO(berlin) do non-exclusive router op. + pass + + def _update_router_gw_info(self, context, router_id, info): + #TODO(berlin) do non-exclusive router op. + router = self.plugin._get_router(context, router_id) + super(nsx_v.NsxVPluginV2, self.plugin)._update_router_gw_info( + context, router_id, info, router=router) + pass + + def add_router_interface(self, context, router_id, interface_info): + #TODO(berlin): add router interface. + info = super(nsx_v.NsxVPluginV2, self.plugin).add_router_interface( + context, router_id, interface_info) + return info + + def remove_router_interface(self, context, router_id, interface_info): + #TODO(berlin) do non-exclusive router op. + info = super(nsx_v.NsxVPluginV2, self.plugin).remove_router_interface( + context, router_id, interface_info) + return info + + def _update_edge_router(self, context, router_id): + #TODO(berlin) do non-exclusive router op. + pass diff --git a/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v_md_proxy.py b/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v_md_proxy.py index 2a6ee7cc50..a485bd0820 100644 --- a/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v_md_proxy.py +++ b/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v_md_proxy.py @@ -223,6 +223,7 @@ class NsxVMetadataProxyHandler: 'router': { 'name': 'metadata_proxy_router', 'admin_state_up': True, + 'router_type': 'exclusive', 'tenant_id': None}} rtr = self.nsxv_plugin.create_router( diff --git a/vmware_nsx/neutron/plugins/vmware/vshield/edge_firewall_driver.py b/vmware_nsx/neutron/plugins/vmware/vshield/edge_firewall_driver.py index 5d43e271ad..7a839a421e 100644 --- a/vmware_nsx/neutron/plugins/vmware/vshield/edge_firewall_driver.py +++ b/vmware_nsx/neutron/plugins/vmware/vshield/edge_firewall_driver.py @@ -142,7 +142,8 @@ class EdgeFirewallDriver(db_base_plugin_v2.NeutronDbPluginV2): vcns_rules.append( {'action': "accept", 'enabled': True, - 'destination': {'vnicGroupId': ["external"]}}) + 'destination': {'vnicGroupId': ["external"]}, + 'ruleTag': ruleTag}) return { 'featureType': "firewall_4.0", 'firewallRules': { diff --git a/vmware_nsx/neutron/tests/unit/vmware/test_nsx_v_plugin.py b/vmware_nsx/neutron/tests/unit/vmware/test_nsx_v_plugin.py index a152c742e5..e433595815 100644 --- a/vmware_nsx/neutron/tests/unit/vmware/test_nsx_v_plugin.py +++ b/vmware_nsx/neutron/tests/unit/vmware/test_nsx_v_plugin.py @@ -37,6 +37,8 @@ from neutron import manager from neutron.openstack.common import uuidutils from neutron.plugins.vmware.extensions import ( vnicindex as ext_vnic_idx) +from neutron.plugins.vmware.extensions import routertype as router_type + from neutron.tests.unit import _test_extension_portbindings as test_bindings import neutron.tests.unit.test_db_plugin as test_plugin import neutron.tests.unit.test_extension_allowedaddresspairs as test_addr_pair @@ -45,6 +47,7 @@ import neutron.tests.unit.test_extension_portsecurity as test_psec import neutron.tests.unit.test_extension_security_group as ext_sg import neutron.tests.unit.test_l3_plugin as test_l3_plugin from neutron.tests.unit import testlib_api + from vmware_nsx.neutron.plugins.vmware.dbexts import nsxv_db from vmware_nsx.neutron.plugins.vmware.vshield.common import ( constants as vcns_const) @@ -1033,6 +1036,8 @@ class TestL3ExtensionManager(object): l3_ext_gw_mode.EXTENDED_ATTRIBUTES_2_0.get(key, {})) l3.RESOURCE_ATTRIBUTE_MAP[key].update( dist_router.EXTENDED_ATTRIBUTES_2_0.get(key, {})) + l3.RESOURCE_ATTRIBUTE_MAP[key].update( + router_type.EXTENDED_ATTRIBUTES_2_0.get(key, {})) # Finally add l3 resources to the global attribute map attributes.RESOURCE_ATTRIBUTE_MAP.update( l3.RESOURCE_ATTRIBUTE_MAP) @@ -1074,11 +1079,11 @@ class L3NatTest(test_l3_plugin.L3BaseForIntTests, NsxVPluginV2TestCase): ext_mgr = ext_mgr or TestL3ExtensionManager() super(L3NatTest, self).setUp( plugin=plugin, ext_mgr=ext_mgr, service_plugins=service_plugins) - plugin_instance = manager.NeutronManager.get_plugin() + self.plugin_instance = manager.NeutronManager.get_plugin() self._plugin_name = "%s.%s" % ( - plugin_instance.__module__, - plugin_instance.__class__.__name__) - self._plugin_class = plugin_instance.__class__ + self.plugin_instance.__module__, + self.plugin_instance.__class__.__name__) + self._plugin_class = self.plugin_instance.__class__ def tearDown(self): plugin = manager.NeutronManager.get_plugin() @@ -1116,9 +1121,31 @@ class L3NatTest(test_l3_plugin.L3BaseForIntTests, NsxVPluginV2TestCase): self._delete('routers', router['router']['id']) -class TestL3NatTestCase(L3NatTest, - test_l3_plugin.L3NatDBIntTestCase, - NsxVPluginV2TestCase): +class TestExclusiveRouterTestCase(L3NatTest, + test_l3_plugin.L3NatDBIntTestCase, + NsxVPluginV2TestCase): + def _create_router(self, fmt, tenant_id, name=None, + admin_state_up=None, set_context=False, + arg_list=None, **kwargs): + data = {'router': {'tenant_id': tenant_id}} + if name: + data['router']['name'] = name + if admin_state_up: + data['router']['admin_state_up'] = admin_state_up + for arg in (('admin_state_up', 'tenant_id') + (arg_list or ())): + # Arg must be present and not empty + if arg in kwargs and kwargs[arg]: + data['router'][arg] = kwargs[arg] + + data['router']['router_type'] = kwargs.get('router_type', 'exclusive') + + router_req = self.new_create_request('routers', data, fmt) + if set_context and tenant_id: + # create a specific auth context for this request + router_req.environ['neutron.context'] = context.Context( + '', tenant_id) + + return router_req.get_response(self.ext_api) def _test_create_l3_ext_network(self, vlan_id=0): name = 'l3_ext_net' @@ -1247,7 +1274,7 @@ class TestL3NatTestCase(L3NatTest, self._test_floatingip_with_invalid_create_port(self._plugin_name) def test_floatingip_update(self): - super(TestL3NatTestCase, self).test_floatingip_update( + super(TestExclusiveRouterTestCase, self).test_floatingip_update( constants.FLOATINGIP_STATUS_DOWN) def test_floatingip_disassociate(self):