diff --git a/.pylintrc b/.pylintrc index d183d03d06..fdb3ea162d 100644 --- a/.pylintrc +++ b/.pylintrc @@ -106,6 +106,7 @@ disable= consider-using-with, unused-private-member, arguments-renamed, + redefined-outer-name, [BASIC] # Variable names can be 1 to 31 characters long, with lowercase and underscores @@ -136,7 +137,7 @@ additional-builtins=_ [CLASSES] # List of interface methods to ignore, separated by a comma. -ignore-iface-methods= +#ignore-iface-methods= [IMPORTS] # Deprecated modules which should not be used, separated by a comma diff --git a/lower-constraints.txt b/lower-constraints.txt index 98518b61e6..177be0c4c5 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -54,7 +54,6 @@ requests==2.14.2 sphinx==3.3.0 SQLAlchemy==1.2.0 stestr==1.0.0 -stevedore==2.0.1 tenacity==6.0.0 testscenarios==0.4 testtools==2.2.0 diff --git a/vmware_nsx/db/nsx_portbindings_db.py b/vmware_nsx/db/nsx_portbindings_db.py index 185f56112c..61733a90fa 100644 --- a/vmware_nsx/db/nsx_portbindings_db.py +++ b/vmware_nsx/db/nsx_portbindings_db.py @@ -27,12 +27,12 @@ from neutron_lib import exceptions from neutron_lib.plugins import directory from neutron_lib.plugins import utils as p_utils -from neutron.db import portbindings_db as pbin_db from neutron.plugins.ml2 import models as pbin_model from vmware_nsx._i18n import _ from vmware_nsx.common import nsx_constants from vmware_nsx.common import utils as c_utils from vmware_nsx.db import nsxv_db +from vmware_nsx.db import portbindings_db as pbin_db from vmware_nsx.extensions import projectpluginmap diff --git a/vmware_nsx/db/portbinding.py b/vmware_nsx/db/portbinding.py new file mode 100644 index 0000000000..72cee32de6 --- /dev/null +++ b/vmware_nsx/db/portbinding.py @@ -0,0 +1,36 @@ +# Copyright 2013 IBM Corp. +# 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. + +# This model class is not used by any vmware NSX plugin but is needed for +# correct operations of the Port Bindings DB Mixin class + +from neutron_lib.db import model_base +import sqlalchemy as sa +from sqlalchemy import orm + +from neutron.db import models_v2 + + +class PortBindingPort(model_base.BASEV2): + port_id = sa.Column(sa.String(36), + sa.ForeignKey('ports.id', ondelete="CASCADE"), + primary_key=True) + host = sa.Column(sa.String(255), nullable=False) + port = orm.relationship( + models_v2.Port, load_on_pending=True, + backref=orm.backref("portbinding", + lazy='joined', uselist=False, + cascade='delete')) + revises_on_change = ('port', ) diff --git a/vmware_nsx/db/portbindings_db.py b/vmware_nsx/db/portbindings_db.py new file mode 100644 index 0000000000..9eead7dd1f --- /dev/null +++ b/vmware_nsx/db/portbindings_db.py @@ -0,0 +1,109 @@ +# Copyright 2013 IBM Corp. +# 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_lib.api.definitions import port as port_def +from neutron_lib.api.definitions import portbindings +from neutron_lib.api import validators +from neutron_lib.db import api as db_api +from neutron_lib.db import model_query +from neutron_lib.db import resource_extend +from neutron_lib.plugins import directory + +from neutron.db import models_v2 + +from vmware_nsx.db import portbinding as pmodels + + +def _port_model_hook(context, original_model, query): + query = query.outerjoin( + pmodels.PortBindingPort, + (original_model.id == pmodels.PortBindingPort.port_id)) + return query + + +def _port_result_filter_hook(query, filters): + values = filters and filters.get(portbindings.HOST_ID, []) + if not values: + return query + query = query.filter(pmodels.PortBindingPort.host.in_(values)) + return query + + +@resource_extend.has_resource_extenders +class PortBindingMixin(object): + + def __new__(cls, *args, **kwargs): + model_query.register_hook( + models_v2.Port, + "portbindings_port", + query_hook=_port_model_hook, + filter_hook=None, + result_filters=_port_result_filter_hook) + return super(PortBindingMixin, cls).__new__(cls, *args, **kwargs) + + def _process_portbindings_create_and_update(self, context, port_data, + port): + binding_profile = port.get(portbindings.PROFILE) + binding_profile_set = validators.is_attr_set(binding_profile) + if not binding_profile_set and binding_profile is not None: + del port[portbindings.PROFILE] + + binding_vnic = port.get(portbindings.VNIC_TYPE) + binding_vnic_set = validators.is_attr_set(binding_vnic) + if not binding_vnic_set and binding_vnic is not None: + del port[portbindings.VNIC_TYPE] + # REVISIT(irenab) Add support for vnic_type for plugins that + # can handle more than one type. + # Currently implemented for ML2 plugin that does not use + # PortBindingMixin. + + host = port_data.get(portbindings.HOST_ID) + host_set = validators.is_attr_set(host) + with db_api.CONTEXT_WRITER.using(context): + bind_port = context.session.query( + pmodels.PortBindingPort).filter_by(port_id=port['id']).first() + if host_set: + if not bind_port: + context.session.add( + pmodels.PortBindingPort(port_id=port['id'], host=host)) + else: + bind_port.host = host + else: + host = bind_port.host if bind_port else None + self._extend_port_dict_binding_host(port, host) + + def get_port_host(self, context, port_id): + with db_api.CONTEXT_READER.using(context): + bind_port = ( + context.session.query(pmodels.PortBindingPort.host). + filter_by(port_id=port_id). + first() + ) + return bind_port.host if bind_port else None + + def _extend_port_dict_binding_host(self, port_res, host): + port_res[portbindings.HOST_ID] = host + + def extend_port_dict_binding(self, port_res, port_db): + host = port_db.portbinding.host if port_db.portbinding else None + self._extend_port_dict_binding_host(port_res, host) + + @staticmethod + @resource_extend.extends([port_def.COLLECTION_NAME]) + def _extend_port_dict_binding(port_res, port_db): + plugin = directory.get_plugin() + if not isinstance(plugin, PortBindingMixin): + return + plugin.extend_port_dict_binding(port_res, port_db) diff --git a/vmware_nsx/plugins/common_v3/plugin.py b/vmware_nsx/plugins/common_v3/plugin.py index 417165f57e..ee2a06d50c 100644 --- a/vmware_nsx/plugins/common_v3/plugin.py +++ b/vmware_nsx/plugins/common_v3/plugin.py @@ -39,7 +39,6 @@ from neutron.db import l3_db from neutron.db import l3_gwmode_db from neutron.db.models import securitygroup as securitygroup_model from neutron.db import models_v2 -from neutron.db import portbindings_db from neutron.db import portsecurity_db from neutron.db.quota import driver_nolock # noqa from neutron.db import securitygroups_db @@ -83,6 +82,7 @@ from vmware_nsx.db import extended_security_group as extended_sec from vmware_nsx.db import extended_security_group_rule as extend_sg_rule from vmware_nsx.db import maclearning as mac_db from vmware_nsx.db import nsx_portbindings_db as pbin_db +from vmware_nsx.db import portbindings_db from vmware_nsx.extensions import advancedserviceproviders as as_providers from vmware_nsx.extensions import maclearning as mac_ext from vmware_nsx.extensions import providersecuritygroup as provider_sg diff --git a/vmware_nsx/plugins/dvs/plugin.py b/vmware_nsx/plugins/dvs/plugin.py index ff961ce065..45e8b8daba 100644 --- a/vmware_nsx/plugins/dvs/plugin.py +++ b/vmware_nsx/plugins/dvs/plugin.py @@ -43,7 +43,6 @@ from neutron.db import external_net_db from neutron.db import l3_db from neutron.db.models import securitygroup as securitygroup_model from neutron.db import models_v2 -from neutron.db import portbindings_db from neutron.db import portsecurity_db from neutron.db import securitygroups_db from neutron.db import vlantransparent_db as vlan_ext_db @@ -59,6 +58,7 @@ from vmware_nsx.common import nsx_constants from vmware_nsx.common import utils as c_utils from vmware_nsx.db import db as nsx_db from vmware_nsx.db import nsxv_db +from vmware_nsx.db import portbindings_db from vmware_nsx.dhcp_meta import modes as dhcpmeta_modes from vmware_nsx.dvs import dvs from vmware_nsx.dvs import dvs_utils diff --git a/vmware_nsx/plugins/nsx_p/plugin.py b/vmware_nsx/plugins/nsx_p/plugin.py index 95c4bd5732..942154a565 100644 --- a/vmware_nsx/plugins/nsx_p/plugin.py +++ b/vmware_nsx/plugins/nsx_p/plugin.py @@ -2759,7 +2759,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base): method() def _update_router_gw_info(self, context, router_id, info, - called_from=None): + request_body, called_from=None): # Get the original data of the router GW router = self._get_router(context, router_id) orig_info = self._get_router_gw_info(context, router_id) @@ -2779,8 +2779,9 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base): [sub['network_id'] for sub in router_subnets]) # First update the neutron DB + # We do not need to pass a request body super(NsxPolicyPlugin, self)._update_router_gw_info( - context, router_id, info, router=router) + context, router_id, info, None, router=router) router = self._get_router(context, router_id) # Get the new tier0 of the updated router (or None if GW was removed) @@ -2868,7 +2869,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base): LOG.error("Rolling back router %s GW info update because " "of NSX failure %s", router_id, e) super(NsxPolicyPlugin, self)._update_router_gw_info( - context, router_id, orig_info, router=router) + context, router_id, orig_info, None, router) def _update_router_advertisement_rules(self, router_id, subnets, advertise_ipv6): @@ -2934,7 +2935,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base): if gw_info and gw_info != const.ATTR_NOT_SPECIFIED: try: self._update_router_gw_info(context, router['id'], gw_info, - called_from="create") + None, called_from="create") except (db_exc.DBError, nsx_lib_exc.NsxLibException): with excutils.save_and_reraise_exception(): LOG.error("Failed to set gateway info for router " @@ -2953,7 +2954,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base): if gw_info: try: self._update_router_gw_info(context, router_id, {}, - called_from="delete") + None, called_from="delete") except nsx_lib_exc.NsxLibException as e: LOG.error("Failed to remove router %s gw info before " "deletion, but going on with the deletion anyway: " @@ -3030,8 +3031,8 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base): context, router_id, router) # Update the policy backend + added_routes = removed_routes = False try: - added_routes = removed_routes = False # Updating name & description if 'name' in router_data or 'description' in router_data: router_name = utils.get_name_and_uuid( diff --git a/vmware_nsx/plugins/nsx_v/drivers/distributed_router_driver.py b/vmware_nsx/plugins/nsx_v/drivers/distributed_router_driver.py index 300ec44075..c7685a841d 100644 --- a/vmware_nsx/plugins/nsx_v/drivers/distributed_router_driver.py +++ b/vmware_nsx/plugins/nsx_v/drivers/distributed_router_driver.py @@ -169,8 +169,9 @@ class RouterDistributedDriver(router_driver.RouterBaseDriver): # verify the edge was deployed before calling super code. tlr_edge_id = self._get_edge_id_or_raise(context, router_id) + # Pass None request body to function super(nsx_v.NsxVPluginV2, self.plugin)._update_router_gw_info( - context, router_id, info, router=router) + context, router_id, info, None, router=router) router = self.plugin._get_router(context, router_id) new_ext_net_id = router.gw_port_id and router.gw_port.network_id diff --git a/vmware_nsx/plugins/nsx_v/drivers/exclusive_router_driver.py b/vmware_nsx/plugins/nsx_v/drivers/exclusive_router_driver.py index 29a9ccf92e..5a07641fcf 100644 --- a/vmware_nsx/plugins/nsx_v/drivers/exclusive_router_driver.py +++ b/vmware_nsx/plugins/nsx_v/drivers/exclusive_router_driver.py @@ -136,7 +136,7 @@ class RouterExclusiveDriver(router_driver.RouterBaseDriver): gw_info = {'network_id': external_net_id, 'enable_snat': router_db.enable_snat} self.plugin._update_router_gw_info( - context, router_id, gw_info, force_update=True) + context, router_id, gw_info, None, force_update=True) def delete_router(self, context, router_id): edge_id, az_name = self.plugin._get_edge_id_and_az_by_rtr_id( @@ -179,9 +179,9 @@ class RouterExclusiveDriver(router_driver.RouterBaseDriver): orgaddr, orgmask, orgnexthop = ( self.plugin._get_external_attachment_info( context, router)) - + # We do not need to pass request_body to this function super(nsx_v.NsxVPluginV2, self.plugin)._update_router_gw_info( - context, router_id, info, router=router) + context, router_id, info, None, router=router) router = self.plugin._get_router(context, router_id) new_ext_net_id = router.gw_port_id and router.gw_port.network_id diff --git a/vmware_nsx/plugins/nsx_v/drivers/shared_router_driver.py b/vmware_nsx/plugins/nsx_v/drivers/shared_router_driver.py index aee1bebbb7..8c82c1a237 100644 --- a/vmware_nsx/plugins/nsx_v/drivers/shared_router_driver.py +++ b/vmware_nsx/plugins/nsx_v/drivers/shared_router_driver.py @@ -84,7 +84,8 @@ class RouterSharedDriver(router_driver.RouterBaseDriver): context, router_id, router) if gw_info != constants.ATTR_NOT_SPECIFIED: - self.plugin._update_router_gw_info(context, router_id, gw_info) + self.plugin._update_router_gw_info(context, router_id, + gw_info, None) if 'admin_state_up' in r: # If router was deployed on a different edge then # admin-state-up is already updated on the new edge. @@ -700,8 +701,9 @@ class RouterSharedDriver(router_driver.RouterBaseDriver): router = self.plugin._get_router(context, router_id) edge_id = edge_utils.get_router_edge_id(context, router_id) if not edge_id: + # Pass a None request_body since we do not need it super(nsx_v.NsxVPluginV2, self.plugin)._update_router_gw_info( - context, router_id, info, router=router) + context, router_id, info, None, router=router) # UPDATE gw info only if the router has been attached to an edge else: is_migrated = False @@ -714,7 +716,7 @@ class RouterSharedDriver(router_driver.RouterBaseDriver): self.plugin._get_external_attachment_info( context, router)) super(nsx_v.NsxVPluginV2, self.plugin)._update_router_gw_info( - context, router_id, info, router=router) + context, router_id, info, None, router=router) router = self.plugin._get_router(context, router_id) new_ext_net_id = (router.gw_port_id and router.gw_port.network_id) diff --git a/vmware_nsx/plugins/nsx_v/plugin.py b/vmware_nsx/plugins/nsx_v/plugin.py index 56a9ad11f9..c3ebebe72f 100644 --- a/vmware_nsx/plugins/nsx_v/plugin.py +++ b/vmware_nsx/plugins/nsx_v/plugin.py @@ -1349,6 +1349,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, # the original exception. for dvsm, netm in dvs_pg_mappings.items(): self._delete_backend_network(netm, dvsm) + predefined = None try: net_data[psec.PORTSECURITY] = net_data.get(psec.PORTSECURITY, True) if not cfg.CONF.nsxv.spoofguard_enabled: @@ -3506,7 +3507,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, self.metadata_proxy_handler)) if gw_info != constants.ATTR_NOT_SPECIFIED and gw_info: self._update_router_gw_info( - context, lrouter['id'], gw_info) + context, lrouter['id'], gw_info, None) except Exception: LOG.exception("Failed to create router %s", router) with excutils.save_and_reraise_exception(): @@ -3769,7 +3770,8 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, edge_utils.update_routes(self.nsx_v, context, router_id, routes, nexthop) - def _update_current_gw_port(self, context, router_id, router, ext_ips): + def _update_current_gw_port(self, context, router_id, router, ext_ips, + _request_body): """Override this function in order not to call plugins' update_port since the actual backend work was already done by the router driver, and it may cause a deadlock. @@ -3790,7 +3792,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, 'updated_port': updated_port })) - def _update_router_gw_info(self, context, router_id, info, + def _update_router_gw_info(self, context, router_id, info, request_body, is_routes_update=False, force_update=False): with db_api.CONTEXT_WRITER.using(context): diff --git a/vmware_nsx/plugins/nsx_v3/plugin.py b/vmware_nsx/plugins/nsx_v3/plugin.py index e8b1a49a3b..51f92270f3 100644 --- a/vmware_nsx/plugins/nsx_v3/plugin.py +++ b/vmware_nsx/plugins/nsx_v3/plugin.py @@ -903,8 +903,8 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base, vlt)) is_backend_network = True + rollback_network = False try: - rollback_network = False with db_api.CONTEXT_WRITER.using(context): # Create network in Neutron created_net = super(NsxV3Plugin, self).create_network(context, @@ -2007,7 +2007,7 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base, LOG.info("Deleted service router for %s (NSX logical router %s)", router_id, nsx_router_id) - def _update_router_gw_info(self, context, router_id, info): + def _update_router_gw_info(self, context, router_id, info, _request_body): router = self._get_router(context, router_id) org_tier0_uuid = self._get_tier0_uuid_by_router(context, router) org_enable_snat = router.enable_snat @@ -2028,8 +2028,9 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base, # TODO(berlin): For nonat use case, we actually don't need a gw port # which consumes one external ip. But after looking at the DB logic # and we need to make a big change so don't touch it at present. + # NOTE: We do not need to pass request_body to the function below super(NsxV3Plugin, self)._update_router_gw_info( - context, router_id, info, router=router) + context, router_id, info, None, router=router) router = self._get_router(context, router_id) new_tier0_uuid = self._get_tier0_uuid_by_router(context, router) @@ -2184,7 +2185,8 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base, if gw_info and gw_info != const.ATTR_NOT_SPECIFIED: try: - self._update_router_gw_info(context, router['id'], gw_info) + self._update_router_gw_info(context, router['id'], + gw_info, None) except (db_exc.DBError, nsx_lib_exc.ManagerError): with excutils.save_and_reraise_exception(): LOG.error("Failed to set gateway info for router " @@ -2203,7 +2205,7 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base, interface=None) gw_info = self._get_router_gw_info(context, router_id) if gw_info: - self._update_router_gw_info(context, router_id, {}) + self._update_router_gw_info(context, router_id, {}, None) nsx_router_id = nsx_db.get_nsx_router_id(context.session, router_id) super(NsxV3Plugin, self).delete_router(context, router_id) diff --git a/vmware_nsx/services/fwaas/common/fwaas_callbacks_v2.py b/vmware_nsx/services/fwaas/common/fwaas_callbacks_v2.py index 62685a8434..ffd3543e78 100644 --- a/vmware_nsx/services/fwaas/common/fwaas_callbacks_v2.py +++ b/vmware_nsx/services/fwaas/common/fwaas_callbacks_v2.py @@ -43,6 +43,16 @@ class DummyAgentApi(object): class NsxFwaasCallbacksV2(firewall_l3_agent_v2.L3WithFWaaS): """Common NSX RPC callbacks for Firewall As A Service - V2.""" + + # Mock implementation of the l3 agent extension interface. + # This is needed as the base class extends l3 agent extension + + def update_network(self, context, data): + pass + + def ha_state_change(self, context, data): + pass + def __init__(self, with_rpc): # The super code needs a configuration object with the neutron host # and an agent_mode, which our driver doesn't use. diff --git a/vmware_nsx/tests/unit/nsx_v3/test_plugin.py b/vmware_nsx/tests/unit/nsx_v3/test_plugin.py index 417c119fca..2d7836f77f 100644 --- a/vmware_nsx/tests/unit/nsx_v3/test_plugin.py +++ b/vmware_nsx/tests/unit/nsx_v3/test_plugin.py @@ -995,6 +995,9 @@ class TestSubnetsV2(common_v3.NsxV3TestSubnets, NsxV3PluginTestCaseMixin): def test_create_subnet_ipv6_slaac_with_db_reference_error(self): self.skipTest('No DHCP v6 Support yet') + def test_update_subnet_the_same_gw_as_in_use_by_router_ipv6(self): + self.skipTest('No SLAAC/DHCPv6 Support yet') + class TestPortsV2(common_v3.NsxV3SubnetMixin, common_v3.NsxV3TestPorts, NsxV3PluginTestCaseMixin,