diff --git a/vmware_nsx/common/config.py b/vmware_nsx/common/config.py index 6944d8fc4e..91eab39e62 100644 --- a/vmware_nsx/common/config.py +++ b/vmware_nsx/common/config.py @@ -686,6 +686,27 @@ nsxv_az_opts = [ help=_("(Optional) Defines edge pool's management range for " "the availability zone. If not defined, the global one " "will be used")), + cfg.StrOpt('mgt_net_moid', + help=_('(Optional) Portgroup MoRef ID for metadata proxy ' + 'management network')), + cfg.ListOpt('mgt_net_proxy_ips', + default=[], + help=_('(Optional) Comma separated list of management network ' + 'IP addresses for metadata proxy.')), + cfg.StrOpt('mgt_net_proxy_netmask', + help=_("(Optional) Management network netmask for metadata " + "proxy.")), + cfg.StrOpt('mgt_net_default_gateway', + help=_("(Optional) Management network default gateway for " + "metadata proxy.")), + cfg.StrOpt('external_network', + help=_('(Optional) Network ID for physical network ' + 'connectivity')), + cfg.StrOpt('vdn_scope_id', + help=_('(Optional) Network scope ID for VXLAN virtual wires')), + cfg.StrOpt('dvs_id', + help=_('(Optional) DVS MoRef ID for DVS connected to ' + 'Management / Edge cluster')), ] # Register the configuration options diff --git a/vmware_nsx/db/migration/alembic_migrations/versions/CONTRACT_HEAD b/vmware_nsx/db/migration/alembic_migrations/versions/CONTRACT_HEAD index da53a12ef3..590d3d8da9 100644 --- a/vmware_nsx/db/migration/alembic_migrations/versions/CONTRACT_HEAD +++ b/vmware_nsx/db/migration/alembic_migrations/versions/CONTRACT_HEAD @@ -1 +1 @@ -5c8f451290b7 \ No newline at end of file +14a89ddf96e2 \ No newline at end of file diff --git a/vmware_nsx/db/migration/alembic_migrations/versions/ocata/contract/14a89ddf96e2_add_az_internal_network.py b/vmware_nsx/db/migration/alembic_migrations/versions/ocata/contract/14a89ddf96e2_add_az_internal_network.py new file mode 100644 index 0000000000..3999545687 --- /dev/null +++ b/vmware_nsx/db/migration/alembic_migrations/versions/ocata/contract/14a89ddf96e2_add_az_internal_network.py @@ -0,0 +1,43 @@ +# Copyright 2017 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. + +"""NSX Adds a 'availability_zone' attribute to internal-networks table + +Revision ID: 14a89ddf96e2 +Revises: 5c8f451290b7 +Create Date: 2017-02-05 14:34:21.163418 + +""" + +# revision identifiers, used by Alembic. +revision = '14a89ddf96e2' +down_revision = '5c8f451290b7' + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.engine import reflection + + +def upgrade(): + table_name = 'nsxv_internal_networks' + # Add the new column + op.add_column(table_name, sa.Column( + 'availability_zone', sa.String(36), server_default='default')) + # replace the old primary key constraint with a new one for both + # purpose & az + inspector = reflection.Inspector.from_engine(op.get_bind()) + pk_constraint = inspector.get_pk_constraint(table_name) + op.drop_constraint(pk_constraint.get('name'), table_name, type_='primary') + op.create_primary_key(None, table_name, + ['network_purpose', 'availability_zone']) diff --git a/vmware_nsx/db/nsxv_db.py b/vmware_nsx/db/nsxv_db.py index cb14231ad4..88be7ffcaa 100644 --- a/vmware_nsx/db/nsxv_db.py +++ b/vmware_nsx/db/nsxv_db.py @@ -131,6 +131,12 @@ def get_edge_availability_zone(session, edge_id): return binding['availability_zone'] +def get_router_availability_zone(session, router_id): + binding = get_nsxv_router_binding(session, router_id) + if binding: + return binding['availability_zone'] + + def clean_edge_router_binding(session, edge_id): with session.begin(subtransactions=True): (session.query(nsxv_models.NsxvRouterBinding). @@ -220,14 +226,16 @@ def allocate_edge_vnic(session, edge_id, network_id): raise nsx_exc.NsxPluginException(err_msg=msg) -def allocate_edge_vnic_with_tunnel_index(session, edge_id, network_id): +def allocate_edge_vnic_with_tunnel_index(session, edge_id, network_id, + availability_zone): """Allocate an available edge vnic with tunnel index to network.""" # TODO(berlin): temporary solution to let metadata and dhcp use # different vnics - net_list = get_nsxv_internal_network( - session, constants.InternalEdgePurposes.INTER_EDGE_PURPOSE) - metadata_net_id = net_list[0]['network_id'] if net_list else None + int_net = get_nsxv_internal_network( + session, constants.InternalEdgePurposes.INTER_EDGE_PURPOSE, + availability_zone) + metadata_net_id = int_net['network_id'] if int_net else None with session.begin(subtransactions=True): query = session.query(nsxv_models.NsxvEdgeVnicBinding) @@ -353,36 +361,44 @@ def clean_edge_dhcp_static_bindings_by_edge(session, edge_id): edge_id=edge_id).delete() -def create_nsxv_internal_network(session, network_purpose, network_id): +def create_nsxv_internal_network(session, network_purpose, + availability_zone, network_id): with session.begin(subtransactions=True): try: network = nsxv_models.NsxvInternalNetworks( network_purpose=network_purpose, - network_id=network_id) + network_id=network_id, + availability_zone=availability_zone) session.add(network) except db_exc.DBDuplicateEntry: with excutils.save_and_reraise_exception(): - LOG.exception(_LE("Duplicate internal network for purpose %s"), - network_purpose) + LOG.exception(_LE("Duplicate internal network for purpose " + "%(p)s and availabiltiy zone %(az)s"), + {'p': network_purpose, + 'az': availability_zone}) -def get_nsxv_internal_network(session, network_purpose): +def get_nsxv_internal_network(session, network_purpose, availability_zone): + with session.begin(subtransactions=True): + net_list = (session.query(nsxv_models.NsxvInternalNetworks). + filter_by(network_purpose=network_purpose, + availability_zone=availability_zone).all()) + if net_list: + # Should have only one results as purpose+az are the keys + return net_list[0] + + +def get_nsxv_internal_networks(session, network_purpose): with session.begin(subtransactions=True): return (session.query(nsxv_models.NsxvInternalNetworks). filter_by(network_purpose=network_purpose).all()) -def update_nsxv_internal_network(session, network_purpose, network_id): - with session.begin(subtransactions=True): - nets = get_nsxv_internal_network(session, network_purpose) - for net in nets: - net['network_id'] = network_id - - -def delete_nsxv_internal_network(session, network_purpose): +def delete_nsxv_internal_network(session, network_purpose, network_id): with session.begin(subtransactions=True): return (session.query(nsxv_models.NsxvInternalNetworks). - filter_by(network_purpose=network_purpose).delete()) + filter_by(network_purpose=network_purpose, + network_id=network_id).delete()) def create_nsxv_internal_edge(session, ext_ip_address, purpose, router_id): diff --git a/vmware_nsx/db/nsxv_models.py b/vmware_nsx/db/nsxv_models.py index 16123ed698..6c7900c692 100644 --- a/vmware_nsx/db/nsxv_models.py +++ b/vmware_nsx/db/nsxv_models.py @@ -79,7 +79,6 @@ class NsxvEdgeDhcpStaticBinding(model_base.BASEV2, models.TimestampMixin): class NsxvInternalNetworks(model_base.BASEV2, models.TimestampMixin): """Represents internal networks between NSXV plugin elements.""" - __tablename__ = 'nsxv_internal_networks' network_purpose = sa.Column( @@ -89,11 +88,12 @@ class NsxvInternalNetworks(model_base.BASEV2, models.TimestampMixin): network_id = sa.Column(sa.String(36), sa.ForeignKey("networks.id", ondelete="CASCADE"), nullable=True) + availability_zone = sa.Column(sa.String(36), + primary_key=True) class NsxvInternalEdges(model_base.BASEV2, models.TimestampMixin): """Represents internal Edge appliances for NSXV plugin operations.""" - __tablename__ = 'nsxv_internal_edges' ext_ip_address = sa.Column(sa.String(64), primary_key=True) diff --git a/vmware_nsx/plugins/nsx_v/availability_zones.py b/vmware_nsx/plugins/nsx_v/availability_zones.py index 1e28f60aa8..45bf278e23 100644 --- a/vmware_nsx/plugins/nsx_v/availability_zones.py +++ b/vmware_nsx/plugins/nsx_v/availability_zones.py @@ -71,6 +71,17 @@ class ConfiguredAvailabilityZone(object): # using the global ones instead. self.ha_placement_random = cfg.CONF.nsxv.ha_placement_random self.backup_edge_pool = cfg.CONF.nsxv.backup_edge_pool + self.external_network = cfg.CONF.nsxv.external_network + self.vdn_scope_id = cfg.CONF.nsxv.vdn_scope_id + self.dvs_id = cfg.CONF.nsxv.dvs_id + + # No support for metadata per az + self.az_metadata_support = False + self.mgt_net_moid = None + self.mgt_net_proxy_ips = [] + self.mgt_net_proxy_netmask = None + self.mgt_net_default_gateway = None + elif config_line: # Newer configuration - the name of the availability zone can be # used to get the rest of the configuration for this AZ @@ -113,6 +124,57 @@ class ConfiguredAvailabilityZone(object): if not self.backup_edge_pool: self.backup_edge_pool = cfg.CONF.nsxv.backup_edge_pool + self.external_network = az_info.get('external_network') + if not self.external_network: + self.external_network = cfg.CONF.nsxv.external_network + + self.vdn_scope_id = az_info.get('vdn_scope_id') + if not self.vdn_scope_id: + self.vdn_scope_id = cfg.CONF.nsxv.vdn_scope_id + + self.dvs_id = az_info.get('dvs_id') + if not self.dvs_id: + self.dvs_id = cfg.CONF.nsxv.dvs_id + + # Support for metadata per az only if configured, and different + # from the global one + self.mgt_net_proxy_ips = az_info.get('mgt_net_proxy_ips') + if self.mgt_net_proxy_ips: + # make sure there are no over lapping ips with the + # global configuration + if (set(self.mgt_net_proxy_ips) & + set(cfg.CONF.nsxv.mgt_net_proxy_ips)): + raise nsx_exc.NsxInvalidConfiguration( + opt_name="mgt_net_proxy_ips", + opt_value='None', + reason=(_("mgt_net_proxy_ips for availability zone " + "%s must be different from global one") % + self.name)) + + self.az_metadata_support = True + self.mgt_net_moid = az_info.get('mgt_net_moid') + if not self.mgt_net_moid: + self.mgt_net_moid = cfg.CONF.nsxv.mgt_net_moid + + self.mgt_net_proxy_netmask = az_info.get( + 'mgt_net_proxy_netmask') + if not self.mgt_net_proxy_netmask: + self.mgt_net_proxy_netmask = ( + cfg.CONF.nsxv.mgt_net_proxy_netmask) + + self.mgt_net_default_gateway = az_info.get( + 'mgt_net_default_gateway') + if not self.mgt_net_default_gateway: + self.mgt_net_default_gateway = ( + cfg.CONF.nsxv.mgt_net_default_gateway) + + else: + self.az_metadata_support = False + self.mgt_net_moid = None + self.mgt_net_proxy_ips = [] + self.mgt_net_proxy_netmask = None + self.mgt_net_default_gateway = None + else: # use the default configuration self.name = DEFAULT_NAME @@ -122,6 +184,23 @@ class ConfiguredAvailabilityZone(object): self.ha_datastore_id = cfg.CONF.nsxv.ha_datastore_id self.ha_placement_random = cfg.CONF.nsxv.ha_placement_random self.backup_edge_pool = cfg.CONF.nsxv.backup_edge_pool + self.az_metadata_support = True + self.mgt_net_moid = cfg.CONF.nsxv.mgt_net_moid + self.mgt_net_proxy_ips = cfg.CONF.nsxv.mgt_net_proxy_ips + self.mgt_net_proxy_netmask = cfg.CONF.nsxv.mgt_net_proxy_netmask + self.mgt_net_default_gateway = ( + cfg.CONF.nsxv.mgt_net_default_gateway) + self.external_network = cfg.CONF.nsxv.external_network + self.vdn_scope_id = cfg.CONF.nsxv.vdn_scope_id + self.dvs_id = cfg.CONF.nsxv.dvs_id + + def is_default(self): + return self.name == DEFAULT_NAME + + def supports_metadata(self): + # Return True if this az has it's own metadata configuration + # If False - it uses the global metadata (if defined) + return self.az_metadata_support class ConfiguredAvailabilityZones(object): @@ -147,6 +226,14 @@ class ConfiguredAvailabilityZones(object): resources.append(az.datastore_id) if az.ha_datastore_id: resources.append(az.ha_datastore_id) + if az.mgt_net_moid: + resources.append(az.mgt_net_moid) + if az.external_network: + resources.append(az.external_network) + if az.vdn_scope_id: + resources.append(az.vdn_scope_id) + if az.mgt_net_moid: + resources.append(az.mgt_net_moid) return resources def get_availability_zone(self, name): 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 06399a2b5b..f3ea90bbb1 100644 --- a/vmware_nsx/plugins/nsx_v/drivers/distributed_router_driver.py +++ b/vmware_nsx/plugins/nsx_v/drivers/distributed_router_driver.py @@ -197,7 +197,7 @@ class RouterDistributedDriver(router_driver.RouterBaseDriver): # Update external vnic if addr or mask is changed if orgaddr != newaddr or orgmask != newmask: - edge_utils.update_external_interface( + self.edge_manager.update_external_interface( self.nsx_v, context, plr_id, new_ext_net_id, newaddr, newmask) 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 04935302be..b14c46920d 100644 --- a/vmware_nsx/plugins/nsx_v/drivers/exclusive_router_driver.py +++ b/vmware_nsx/plugins/nsx_v/drivers/exclusive_router_driver.py @@ -41,8 +41,9 @@ class RouterExclusiveDriver(router_driver.RouterBaseDriver): context, lrouter, dist=False, appliance_size=appliance_size, availability_zone=availability_zone) if allow_metadata: - self.plugin.metadata_proxy_handler.configure_router_edge( - context, lrouter['id']) + self.plugin.get_metadata_proxy_handler( + availability_zone.name).configure_router_edge( + context, lrouter['id']) def update_router(self, context, router_id, router): r = router['router'] @@ -78,8 +79,10 @@ class RouterExclusiveDriver(router_driver.RouterBaseDriver): def detach_router(self, context, router_id, router): LOG.debug("Detach exclusive router id %s", router_id) self.edge_manager.unbind_router_on_edge(context, router_id) - metadata_proxy_handler = self.plugin.metadata_proxy_handler - if metadata_proxy_handler: + if self.plugin.metadata_proxy_handler: + az = self.get_router_az_by_id(context, router_id) + metadata_proxy_handler = self.plugin.get_metadata_proxy_handler( + az.name) metadata_proxy_handler.cleanup_router_edge(context, router_id) def _build_router_data_from_db(self, router_db, router): @@ -130,8 +133,9 @@ class RouterExclusiveDriver(router_driver.RouterBaseDriver): def delete_router(self, context, router_id): if self.plugin.metadata_proxy_handler: - self.plugin.metadata_proxy_handler.cleanup_router_edge( - context, router_id) + az = self.get_router_az_by_id(context, router_id) + md_proxy = self.plugin.get_metadata_proxy_handler(az.name) + md_proxy.cleanup_router_edge(context, router_id) self.edge_manager.delete_lrouter(context, router_id, dist=False) def update_routes(self, context, router_id, nexthop): @@ -168,7 +172,7 @@ class RouterExclusiveDriver(router_driver.RouterBaseDriver): # Update external vnic if addr or mask is changed if orgaddr != newaddr or orgmask != newmask or force_update: - edge_utils.update_external_interface( + self.edge_manager.update_external_interface( self.nsx_v, context, router_id, new_ext_net_id, newaddr, newmask) 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 7097063d33..33647bc5c5 100644 --- a/vmware_nsx/plugins/nsx_v/drivers/shared_router_driver.py +++ b/vmware_nsx/plugins/nsx_v/drivers/shared_router_driver.py @@ -274,7 +274,7 @@ class RouterSharedDriver(router_driver.RouterBaseDriver): 'primaryAddress: %s, netmask: %s, nexthop: %s, secondary: ' '%s.', ext_net_id, gateway_primary_addr, gateway_mask, gateway_nexthop, secondary) - edge_utils.update_external_interface( + self.edge_manager.update_external_interface( self.nsx_v, context, target_router_id, ext_net_id, gateway_primary_addr, gateway_mask, secondary) @@ -552,7 +552,7 @@ class RouterSharedDriver(router_driver.RouterBaseDriver): ports_qry = context.session.query(models_v2.Port) all_gw_ports = ports_qry.filter_by( device_owner=l3_db.DEVICE_OWNER_ROUTER_GW).all() - metadata_nets = nsxv_db.get_nsxv_internal_network( + metadata_nets = nsxv_db.get_nsxv_internal_networks( context.session, vcns_const.InternalEdgePurposes.INTER_EDGE_PURPOSE) metadata_net_ids = [metadata_net['network_id'] @@ -592,10 +592,10 @@ class RouterSharedDriver(router_driver.RouterBaseDriver): conflict_router_ids, conflict_network_ids, intf_num, az) # configure metadata service on the router. - metadata_proxy_handler = self.plugin.metadata_proxy_handler - if metadata_proxy_handler and new: - metadata_proxy_handler.configure_router_edge(context, - router_id) + if self.plugin.metadata_proxy_handler and new: + md_proxy_handler = self.plugin.get_metadata_proxy_handler( + az.name) + md_proxy_handler.configure_router_edge(context, router_id) edge_id = edge_utils.get_router_edge_id(context, router_id) with locking.LockManager.get_lock(str(edge_id)): # add all internal interfaces of the router on edge @@ -618,11 +618,13 @@ class RouterSharedDriver(router_driver.RouterBaseDriver): flavor_id, edge_id) def _unbind_router_on_edge(self, context, router_id): + az = self.get_router_az_by_id(context, router_id) self.edge_manager.reconfigure_shared_edge_metadata_port( context, router_id) self.edge_manager.unbind_router_on_edge(context, router_id) - metadata_proxy_handler = self.plugin.metadata_proxy_handler - if metadata_proxy_handler: + if self.plugin.metadata_proxy_handler: + metadata_proxy_handler = self.plugin.get_metadata_proxy_handler( + az.name) metadata_proxy_handler.cleanup_router_edge(context, router_id) def _add_router_services_on_available_edge(self, context, router_id): diff --git a/vmware_nsx/plugins/nsx_v/md_proxy.py b/vmware_nsx/plugins/nsx_v/md_proxy.py index 27bfa2dbf4..51b56c4255 100644 --- a/vmware_nsx/plugins/nsx_v/md_proxy.py +++ b/vmware_nsx/plugins/nsx_v/md_proxy.py @@ -92,22 +92,26 @@ def get_router_fw_rules(): return fw_rules -def get_db_internal_edge_ips(context): +def get_db_internal_edge_ips(context, az_name): ip_list = [] edge_list = nsxv_db.get_nsxv_internal_edges_by_purpose( context.session, vcns_const.InternalEdgePurposes.INTER_EDGE_PURPOSE) if edge_list: - ip_list = [edge['ext_ip_address'] for edge in edge_list] + # Take only the edges on this availability zone + ip_list = [edge['ext_ip_address'] for edge in edge_list + if nsxv_db.get_router_availability_zone( + context.session, edge['router_id']) == az_name] return ip_list class NsxVMetadataProxyHandler(object): - - def __init__(self, nsxv_plugin): + """A metadata proxy handler for a specific availability zone""" + def __init__(self, nsxv_plugin, availability_zone): self.nsxv_plugin = nsxv_plugin context = neutron_context.get_admin_context() + self.az = availability_zone # Init cannot run concurrently on multiple nodes with locking.LockManager.get_lock('nsx-metadata-init'): @@ -119,11 +123,14 @@ class NsxVMetadataProxyHandler(object): def _create_metadata_internal_network(self, context, cidr): # Neutron requires a network to have some tenant_id tenant_id = nsxv_constants.INTERNAL_TENANT_ID - - net_data = {'network': {'name': 'inter-edge-net', + net_name = 'inter-edge-net' + if not self.az.is_default(): + net_name = '%s-%s' % (net_name, self.az.name) + net_data = {'network': {'name': net_name, 'admin_state_up': True, 'port_security_enabled': False, 'shared': False, + 'availability_zone_hints': [self.az.name], 'tenant_id': tenant_id}} net = self.nsxv_plugin.create_network(context, net_data) @@ -145,17 +152,21 @@ class NsxVMetadataProxyHandler(object): return net['id'], subnet['id'] + def _get_internal_net_by_az(self, context): + # Get the internal network for the current az + int_net = nsxv_db.get_nsxv_internal_network( + context.session, + vcns_const.InternalEdgePurposes.INTER_EDGE_PURPOSE, + self.az.name) + + if int_net: + return int_net['network_id'] + def _get_internal_network_and_subnet(self, context): - internal_net = None - internal_subnet = None # Try to find internal net, internal subnet. If not found, create new - net_list = nsxv_db.get_nsxv_internal_network( - context.session, - vcns_const.InternalEdgePurposes.INTER_EDGE_PURPOSE) - - if net_list: - internal_net = net_list[0]['network_id'] + internal_net = self._get_internal_net_by_az(context) + internal_subnet = None if internal_net: internal_subnet = self.nsxv_plugin.get_subnets( @@ -173,7 +184,8 @@ class NsxVMetadataProxyHandler(object): except Exception as e: nsxv_db.delete_nsxv_internal_network( context.session, - vcns_const.InternalEdgePurposes.INTER_EDGE_PURPOSE) + vcns_const.InternalEdgePurposes.INTER_EDGE_PURPOSE, + internal_net) # if network is created, clean up if internal_net: @@ -188,6 +200,7 @@ class NsxVMetadataProxyHandler(object): nsxv_db.create_nsxv_internal_network( context.session, nsxv_constants.INTER_EDGE_PURPOSE, + self.az.name, internal_net) else: error = _('Metadata initialization is incomplete on ' @@ -223,29 +236,30 @@ class NsxVMetadataProxyHandler(object): def _get_proxy_edges(self, context): proxy_edge_ips = [] - db_edge_ips = get_db_internal_edge_ips(context) - if len(db_edge_ips) > len(cfg.CONF.nsxv.mgt_net_proxy_ips): - error = _('Number of configured metadata proxy IPs is smaller ' - 'than number of Edges which are already provisioned') + db_edge_ips = get_db_internal_edge_ips(context, self.az.name) + if len(db_edge_ips) > len(self.az.mgt_net_proxy_ips): + error = (_('Number of configured metadata proxy IPs is smaller ' + 'than number of Edges which are already provisioned ' + 'for availability zone %s'), self.az.name) raise nsxv_exc.NsxPluginException(err_msg=error) pool = eventlet.GreenPool(min(MAX_INIT_THREADS, - len(cfg.CONF.nsxv.mgt_net_proxy_ips))) + len(self.az.mgt_net_proxy_ips))) # Edge IPs that exist in both lists have to be validated that their # Edge appliance settings are valid for edge_inner_ip in pool.imap( self._setup_proxy_edge_route_and_connectivity, - list(set(db_edge_ips) & set(cfg.CONF.nsxv.mgt_net_proxy_ips))): + list(set(db_edge_ips) & set(self.az.mgt_net_proxy_ips))): proxy_edge_ips.append(edge_inner_ip) # Edges that exist only in the CFG list, should be paired with Edges # that exist only in the DB list. The existing Edge from the list will # be reconfigured to match the new config edge_to_convert_ips = ( - list(set(db_edge_ips) - set(cfg.CONF.nsxv.mgt_net_proxy_ips))) + list(set(db_edge_ips) - set(self.az.mgt_net_proxy_ips))) edge_ip_to_set = ( - list(set(cfg.CONF.nsxv.mgt_net_proxy_ips) - set(db_edge_ips))) + list(set(self.az.mgt_net_proxy_ips) - set(db_edge_ips))) if edge_to_convert_ips: if cfg.CONF.nsxv.metadata_initializer: @@ -283,6 +297,12 @@ class NsxVMetadataProxyHandler(object): rtr_id = self._get_edge_rtr_id_by_ext_ip(context, rtr_ext_ip) if not edge_id: edge_id = self._get_edge_id_by_rtr_id(context, rtr_id) + if not rtr_id or not edge_id: + # log this error and return without the ip, but don't fail + LOG.error(_LE("Failed find edge for router %(rtr_id)s with ip " + "%(rtr_ext_ip)s"), + {'rtr_id': rtr_id, 'rtr_ext_ip': rtr_ext_ip}) + return # Read and validate DGW. If different, replace with new value try: @@ -297,11 +317,11 @@ class NsxVMetadataProxyHandler(object): dgw = routes.get('defaultRoute', {}).get('gatewayAddress') - if dgw != cfg.CONF.nsxv.mgt_net_default_gateway: + if dgw != self.az.mgt_net_default_gateway: if cfg.CONF.nsxv.metadata_initializer: self.nsxv_plugin._update_routes( context, rtr_id, - cfg.CONF.nsxv.mgt_net_default_gateway) + self.az.mgt_net_default_gateway) else: error = _('Metadata initialization is incomplete on ' 'initializer node') @@ -314,16 +334,16 @@ class NsxVMetadataProxyHandler(object): ).get('addressGroups', {} )[0].get('primaryAddress') cur_pgroup = if_data['portgroupId'] - if (if_data and cur_pgroup != cfg.CONF.nsxv.mgt_net_moid + if (if_data and cur_pgroup != self.az.mgt_net_moid or cur_ip != rtr_ext_ip): if cfg.CONF.nsxv.metadata_initializer: self.nsxv_plugin.nsx_v.update_interface( rtr_id, edge_id, vcns_const.EXTERNAL_VNIC_INDEX, - cfg.CONF.nsxv.mgt_net_moid, + self.az.mgt_net_moid, address=rtr_ext_ip, - netmask=cfg.CONF.nsxv.mgt_net_proxy_netmask, + netmask=self.az.mgt_net_proxy_netmask, secondary=[]) else: error = _('Metadata initialization is incomplete on ' @@ -397,12 +417,17 @@ class NsxVMetadataProxyHandler(object): context = neutron_context.get_admin_context() rtr_id = None + try: + rtr_name = 'metadata_proxy_router' + if not self.az.is_default(): + rtr_name = '%s-%s' % (rtr_name, self.az.name) router_data = { 'router': { - 'name': 'metadata_proxy_router', + 'name': rtr_name, 'admin_state_up': True, 'router_type': 'exclusive', + 'availability_zone_hints': [self.az.name], 'tenant_id': nsxv_constants.INTERNAL_TENANT_ID}} rtr = self.nsxv_plugin.create_router( @@ -417,9 +442,9 @@ class NsxVMetadataProxyHandler(object): rtr['id'], edge_id, vcns_const.EXTERNAL_VNIC_INDEX, - cfg.CONF.nsxv.mgt_net_moid, + self.az.mgt_net_moid, address=rtr_ext_ip, - netmask=cfg.CONF.nsxv.mgt_net_proxy_netmask, + netmask=self.az.mgt_net_proxy_netmask, secondary=[]) port_data = { @@ -465,10 +490,10 @@ class NsxVMetadataProxyHandler(object): {'firewall_rule_list': firewall_rules}, allow_external=False) - if cfg.CONF.nsxv.mgt_net_default_gateway: + if self.az.mgt_net_default_gateway: self.nsxv_plugin._update_routes( context, rtr_id, - cfg.CONF.nsxv.mgt_net_default_gateway) + self.az.mgt_net_default_gateway) nsxv_db.create_nsxv_internal_edge( context.session, rtr_ext_ip, diff --git a/vmware_nsx/plugins/nsx_v/plugin.py b/vmware_nsx/plugins/nsx_v/plugin.py index 4c364705dc..848a4bcf42 100644 --- a/vmware_nsx/plugins/nsx_v/plugin.py +++ b/vmware_nsx/plugins/nsx_v/plugin.py @@ -292,11 +292,27 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, and cfg.CONF.nsxv.mgt_net_proxy_ips and cfg.CONF.nsxv.mgt_net_proxy_netmask) if has_metadata_cfg: - self.metadata_proxy_handler = ( - nsx_v_md_proxy.NsxVMetadataProxyHandler(self)) + # Init md_proxy handler per availability zone + self.metadata_proxy_handler = {} + az_list = self._availability_zones_data.list_availability_zones() + for name in az_list: + az = self._availability_zones_data.get_availability_zone(name) + # create metadata handler only if the az supports it. + # if not, the global one will be used + if az.supports_metadata(): + self.metadata_proxy_handler[name] = ( + nsx_v_md_proxy.NsxVMetadataProxyHandler(self, az)) self.init_is_complete = True + def get_metadata_proxy_handler(self, az_name): + if not self.metadata_proxy_handler: + return None + if az_name in self.metadata_proxy_handler: + return self.metadata_proxy_handler[az_name] + # fallback to the global handler + return self.metadata_proxy_handler[nsx_az.DEFAULT_NAME] + def add_vms_to_service_insertion(self, sg_id): def _add_vms_to_service_insertion(*args, **kwargs): @@ -545,13 +561,27 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, if err_msg: raise n_exc.InvalidInput(error_message=err_msg) - def _validate_physical_network(self, physical_network): - dvs_ids = self._get_dvs_ids(physical_network) + def _validate_physical_network(self, physical_network, default_dvs): + dvs_ids = self._get_dvs_ids(physical_network, default_dvs) for dvs_id in dvs_ids: if not self.nsx_v.vcns.validate_dvs(dvs_id): raise nsx_exc.NsxResourceNotFound(res_name='dvs_id', res_id=dvs_id) + def _get_network_az_from_net_data(self, net_data): + if az_ext.AZ_HINTS in net_data and net_data[az_ext.AZ_HINTS]: + return self._availability_zones_data.get_availability_zone( + net_data[az_ext.AZ_HINTS][0]) + return self.get_default_az() + + def _get_network_az_dvs_id(self, net_data): + az = self._get_network_az_from_net_data(net_data) + return az.dvs_id + + def _get_network_vdn_scope_id(self, net_data): + az = self._get_network_az_from_net_data(net_data) + return az.vdn_scope_id + def _validate_provider_create(self, context, network): if not validators.is_attr_set(network.get(mpnet.SEGMENTS)): return @@ -563,6 +593,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, network_type_set = validators.is_attr_set(network_type) segmentation_id_set = validators.is_attr_set(segmentation_id) physical_network_set = validators.is_attr_set(physical_network) + az_dvs = self._get_network_az_dvs_id(network) err_msg = None if not network_type_set: @@ -572,7 +603,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, err_msg = _("Segmentation ID cannot be specified with " "flat network type") if physical_network_set: - self._validate_physical_network(physical_network) + self._validate_physical_network(physical_network, az_dvs) elif network_type == c_utils.NsxVNetworkTypes.VLAN: if not segmentation_id_set: err_msg = _("Segmentation ID must be specified with " @@ -589,8 +620,11 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, bindings = nsxv_db.get_network_bindings_by_vlanid( context.session, segmentation_id) if bindings: - phy_uuid = (physical_network if physical_network_set - else self.dvs_id) + if physical_network_set: + phy_uuid = physical_network + else: + # use the fvs_id of the availability zone + phy_uuid = az_dvs for binding in bindings: if binding['phy_uuid'] == phy_uuid: raise n_exc.VlanIdInUse( @@ -598,7 +632,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, physical_network=phy_uuid) # Verify whether the DVSes exist in the backend. if physical_network_set: - self._validate_physical_network(physical_network) + self._validate_physical_network(physical_network, az_dvs) elif network_type == c_utils.NsxVNetworkTypes.VXLAN: # Currently unable to set the segmentation id @@ -810,7 +844,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, self._update_network_teaming(dvs_id, net_data['id'], c) return c - def _get_dvs_ids(self, physical_network): + def _get_dvs_ids(self, physical_network, default_dvs): """Extract DVS-IDs provided in the physical network field. If physical network attribute is not set, return the pre configured @@ -818,7 +852,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, to a list of unique DVS-IDs. """ if not validators.is_attr_set(physical_network): - return [self.dvs_id] + return [default_dvs] # Return unique DVS-IDs only and ignore duplicates return list(set( dvs.strip() for dvs in physical_network.split(',') if dvs)) @@ -946,7 +980,11 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, return self.get_default_az() def get_network_az(self, context, network_id): - network = self.get_network(context, network_id) + try: + network = self.get_network(context, network_id) + except Exception: + return self.get_default_az() + return self.get_network_or_router_az(network) def get_router_az(self, router): @@ -995,7 +1033,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, virtual_wire = {"name": net_data['id'], "tenantId": "virtual wire tenant"} config_spec = {"virtualWireCreateSpec": virtual_wire} - vdn_scope_id = self.vdn_scope_id + vdn_scope_id = self._get_network_vdn_scope_id(net_data) if provider_type is not None: segment = net_data[mpnet.SEGMENTS][0] if validators.is_attr_set( @@ -1020,7 +1058,8 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, # Retrieve the list of dvs-ids from physical network. # If physical_network attr is not set, retrieve a list # consisting of a single dvs-id pre-configured in nsx.ini - dvs_ids = self._get_dvs_ids(physical_network) + az_dvs = self._get_network_az_dvs_id(net_data) + dvs_ids = self._get_dvs_ids(physical_network, az_dvs) dvs_net_ids = [] # Save the list of netmorefs from the backend net_morefs = [] @@ -1062,10 +1101,8 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, # update the network with the availability zone hints if az_ext.AZ_HINTS in net_data: - self.validate_availability_zones(context, 'network', - net_data[az_ext.AZ_HINTS]) az_hints = az_ext.convert_az_list_to_string( - net_data[az_ext.AZ_HINTS]) + net_data[az_ext.AZ_HINTS]) super(NsxVPluginV2, self).update_network(context, new_net['id'], {'network': {az_ext.AZ_HINTS: az_hints}}) @@ -1089,7 +1126,9 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, physical_net_set = validators.is_attr_set( physical_network) if not physical_net_set: - physical_network = self.dvs_id + # Use the dvs_id of the availability zone + physical_network = self._get_network_az_dvs_id( + net_data) net_bindings.append(nsxv_db.add_network_binding( context.session, new_net['id'], network_type, @@ -1190,8 +1229,9 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, context.session, dhcp_edge['edge_id']) if rtr_binding: rtr_id = rtr_binding['router_id'] - self.metadata_proxy_handler.cleanup_router_edge( - context, rtr_id) + az_name = rtr_binding['availability_zone'] + md_proxy = self.get_metadata_proxy_handler(az_name) + md_proxy.cleanup_router_edge(context, rtr_id) else: self.edge_manager.reconfigure_shared_edge_metadata_port( context, (vcns_const.DHCP_EDGE_PREFIX + net_id)[:36]) @@ -1307,7 +1347,8 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, [db_utils.resource_fields(network, fields) for network in networks]) - def _raise_if_updates_provider_attributes(self, original_network, attrs): + def _raise_if_updates_provider_attributes(self, original_network, attrs, + az_dvs): """Raise exception if provider attributes are present. For the NSX-V we want to allow changing the physical network of @@ -1322,11 +1363,12 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, and not validators.is_attr_set( attrs.get(providernet.SEGMENTATION_ID))): self._validate_physical_network( - attrs[providernet.PHYSICAL_NETWORK]) + attrs[providernet.PHYSICAL_NETWORK], az_dvs) return providernet._raise_if_updates_provider_attributes(attrs) - def _update_vlan_network_dvs_ids(self, network, new_physical_network): + def _update_vlan_network_dvs_ids(self, network, new_physical_network, + az_dvs): """Update the dvs ids of a vlan provider network The new values will be added to the current ones. @@ -1345,9 +1387,9 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, dvs_pg_mappings = {} current_dvs_ids = set(self._get_dvs_ids( - network[providernet.PHYSICAL_NETWORK])) + network[providernet.PHYSICAL_NETWORK], az_dvs)) new_dvs_ids = set(self._get_dvs_ids( - new_physical_network)) + new_physical_network, az_dvs)) additinal_dvs_ids = new_dvs_ids - current_dvs_ids if not additinal_dvs_ids: @@ -1374,8 +1416,9 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, def update_network(self, context, id, network): net_attrs = network['network'] orig_net = self.get_network(context, id) + az_dvs = self._get_network_az_dvs_id(orig_net) self._raise_if_updates_provider_attributes( - orig_net, net_attrs) + orig_net, net_attrs, az_dvs) if net_attrs.get("admin_state_up") is False: raise NotImplementedError(_("admin_state_up=False networks " "are not supported.")) @@ -1402,7 +1445,8 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, (updated_morefs, new_dvs_pg_mappings) = self._update_vlan_network_dvs_ids( orig_net, - net_attrs[providernet.PHYSICAL_NETWORK]) + net_attrs[providernet.PHYSICAL_NETWORK], + az_dvs) if updated_morefs: new_dvs = list(new_dvs_pg_mappings.values()) net_morefs.extend(new_dvs) @@ -2436,15 +2480,24 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, if binding: return binding['edge_id'] + def _get_edge_id_and_az_by_rtr_id(self, context, rtr_id): + binding = nsxv_db.get_nsxv_router_binding( + context.session, + rtr_id) + + if binding: + return binding['edge_id'], binding['availability_zone'] + def _update_dhcp_service_new_edge(self, context, resource_id): - edge_id = self._get_edge_id_by_rtr_id(context, resource_id) + edge_id, az_name = self._get_edge_id_and_az_by_rtr_id( + context, resource_id) if edge_id: with locking.LockManager.get_lock(str(edge_id)): if self.metadata_proxy_handler: - LOG.debug('Update metadata for resource %s', - resource_id) - self.metadata_proxy_handler.configure_router_edge( - context, resource_id) + LOG.debug('Update metadata for resource %s az=%s', + resource_id, az_name) + md_proxy = self.get_metadata_proxy_handler(az_name) + md_proxy.configure_router_edge(context, resource_id) self.setup_dhcp_edge_fw_rules(context, self, resource_id) @@ -2839,23 +2892,29 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, return self._get_network_availability_zones(context, net_db) def _get_network_availability_zones(self, context, net_db): - """Return availability zones which a network belongs to.""" + """Return availability zones which a network belongs to. + + Return only the actual az the dhcp edge is deployed on. + If there is no edge - the availability zones list is empty. + """ resource_id = (vcns_const.DHCP_EDGE_PREFIX + net_db["id"])[:36] dhcp_edge_binding = nsxv_db.get_nsxv_router_binding( context.session, resource_id) if dhcp_edge_binding: - edge_id = dhcp_edge_binding['edge_id'] - return [self._get_availability_zone_name_by_edge( - context, edge_id)] + return [dhcp_edge_binding['availability_zone']] return [] def get_router_availability_zones(self, router): - """Return availability zones which a router belongs to.""" + """Return availability zones which a router belongs to. + + Return only the actual az the router edge is deployed on. + If there is no edge - the availability zones list is empty. + """ context = n_context.get_admin_context() - edge_id = self._get_edge_id_by_rtr_id(context, router["id"]) - if edge_id: - return [self._get_availability_zone_name_by_edge( - context, edge_id)] + binding = nsxv_db.get_nsxv_router_binding( + context.session, router['id']) + if binding: + return [binding['availability_zone']] return [] def _process_router_flavor_create(self, context, router_db, r): @@ -3192,7 +3251,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, secondary = self._get_floatingips_by_router(context, router['id']) if not router_id: router_id = router['id'] - edge_utils.update_external_interface( + self.edge_manager.update_external_interface( self.nsx_v, context, router_id, ext_net_id, addr, mask, secondary) diff --git a/vmware_nsx/plugins/nsx_v/vshield/edge_appliance_driver.py b/vmware_nsx/plugins/nsx_v/vshield/edge_appliance_driver.py index ec8dfd1f65..14b7b47b9a 100644 --- a/vmware_nsx/plugins/nsx_v/vshield/edge_appliance_driver.py +++ b/vmware_nsx/plugins/nsx_v/vshield/edge_appliance_driver.py @@ -397,11 +397,11 @@ class EdgeApplianceDriver(object): if not dist: vnic_external = self._assemble_edge_vnic( constants.EXTERNAL_VNIC_NAME, constants.EXTERNAL_VNIC_INDEX, - self.external_network, type="uplink") + availability_zone.external_network, type="uplink") edge['vnics']['vnics'].append(vnic_external) else: edge['mgmtInterface'] = { - 'connectedToId': self.external_network, + 'connectedToId': availability_zone.external_network, 'name': "mgmtInterface"} if internal_network: vnic_inside = self._assemble_edge_vnic( @@ -470,11 +470,11 @@ class EdgeApplianceDriver(object): if not dist: vnic_external = self._assemble_edge_vnic( constants.EXTERNAL_VNIC_NAME, constants.EXTERNAL_VNIC_INDEX, - self.external_network, type="uplink") + availability_zone.external_network, type="uplink") edge['vnics']['vnics'].append(vnic_external) else: edge['mgmtInterface'] = { - 'connectedToId': self.external_network, + 'connectedToId': availability_zone.external_network, 'name': "mgmtInterface"} if internal_network: diff --git a/vmware_nsx/plugins/nsx_v/vshield/edge_utils.py b/vmware_nsx/plugins/nsx_v/vshield/edge_utils.py index 203dda08b3..2dda2e65ae 100644 --- a/vmware_nsx/plugins/nsx_v/vshield/edge_utils.py +++ b/vmware_nsx/plugins/nsx_v/vshield/edge_utils.py @@ -184,7 +184,6 @@ class EdgeManager(object): self._worker_pool_pid = None self._worker_pool = None self.nsxv_manager = nsxv_manager - self.dvs_id = cfg.CONF.nsxv.dvs_id self._availability_zones = nsx_az.ConfiguredAvailabilityZones() self.edge_pool_dicts = self._parse_backup_edge_pool_opt() self.nsxv_plugin = nsxv_manager.callbacks.plugin @@ -444,10 +443,11 @@ class EdgeManager(object): return router_binding backup_router_bindings.remove(router_binding) - def _get_physical_provider_network(self, context, network_id): + def _get_physical_provider_network(self, context, network_id, az_dvs): bindings = nsxv_db.get_network_bindings(context.session, network_id) - # Set the return value as global DVS-ID of the mgmt/edge cluster - phys_net = self.dvs_id + # Set the return value as the availability zone DVS-ID of the + # mgmt/edge cluster + phys_net = az_dvs network_type = None if bindings: binding = bindings[0] @@ -471,7 +471,9 @@ class EdgeManager(object): def _create_sub_interface(self, context, network_id, network_name, tunnel_index, address_groups, port_group_id=None): - vcns_network_id = _retrieve_nsx_switch_id(context, network_id) + az = self.plugin.get_network_az(context, network_id) + vcns_network_id = _retrieve_nsx_switch_id(context, network_id, + az.name) if port_group_id is None: portgroup = {'vlanId': 0, 'networkName': network_name, @@ -479,7 +481,7 @@ class EdgeManager(object): 'networkType': 'Isolation'} config_spec = {'networkSpec': portgroup} dvs_id, network_type = self._get_physical_provider_network( - context, network_id) + context, network_id, az.dvs_id) pg, port_group_id = self.nsxv_manager.vcns.create_port_group( dvs_id, config_spec) # Ensure that the portgroup has the correct teaming @@ -522,10 +524,11 @@ class EdgeManager(object): header, _ = self.nsxv_manager.vcns.delete_interface(edge_id, vnic_index) if port_group_id: + az = self.plugin.get_network_az(context, network_id) dvs_id, net_type = self._get_physical_provider_network( - context, network_id) + context, network_id, az.dvs_id) self.nsxv_manager.delete_port_group(dvs_id, - port_group_id) + port_group_id) else: self.nsxv_manager.vcns.update_interface(edge_id, vnic_config) except nsxapi_exc.VcnsApiException: @@ -546,17 +549,20 @@ class EdgeManager(object): """Delete the router binding or clean the edge appliance.""" resource_id = (vcns_const.DHCP_EDGE_PREFIX + network_id)[:36] - bindings = nsxv_db.get_nsxv_router_bindings(context.session) - all_dhcp_edges = {binding['router_id']: binding['edge_id'] for - binding in bindings if binding['router_id']. - startswith(vcns_const.DHCP_EDGE_PREFIX)} - for router_id in all_dhcp_edges: - if (router_id != resource_id and - all_dhcp_edges[router_id] == edge_id): + bindings = nsxv_db.get_nsxv_router_bindings_by_edge( + context.session, edge_id) + all_edge_dhcp_entries = [binding['router_id'] for + binding in bindings if binding['router_id']. + startswith(vcns_const.DHCP_EDGE_PREFIX)] + for router_id in all_edge_dhcp_entries: + if (router_id != resource_id): + # There are additional networks on this DHCP edge. + # just delete the binding one and not the edge itself nsxv_db.delete_nsxv_router_binding(context.session, resource_id) return - self._free_dhcp_edge_appliance(context, network_id) + az_name = bindings[0]['availability_zone'] if bindings else '' + self._free_dhcp_edge_appliance(context, network_id, az_name) def _addr_groups_convert_to_ipset(self, address_groups): cidr_list = [] @@ -823,12 +829,13 @@ class EdgeManager(object): appliance_size=appliance_size, availability_zone=availability_zone) - def _free_dhcp_edge_appliance(self, context, network_id): + def _free_dhcp_edge_appliance(self, context, network_id, az_name): router_id = (vcns_const.DHCP_EDGE_PREFIX + network_id)[:36] # if there are still metadata ports on this edge - delete them now - metadata_proxy_handler = self.plugin.metadata_proxy_handler - if metadata_proxy_handler: + if self.plugin.metadata_proxy_handler: + metadata_proxy_handler = self.plugin.get_metadata_proxy_handler( + az_name) metadata_proxy_handler.cleanup_router_edge(context, router_id, warn=True) @@ -1183,23 +1190,31 @@ class EdgeManager(object): appliance_size=app_size, availability_zone=availability_zone.name) nsxv_db.allocate_edge_vnic_with_tunnel_index( - context.session, edge_id, network_id) + context.session, edge_id, network_id, + availability_zone.name) def reconfigure_shared_edge_metadata_port(self, context, org_router_id): if not self.plugin.metadata_proxy_handler: return - net_list = nsxv_db.get_nsxv_internal_network( - context.session, - vcns_const.InternalEdgePurposes.INTER_EDGE_PURPOSE) - - if not net_list: + org_binding = nsxv_db.get_nsxv_router_binding(context.session, + org_router_id) + if not org_binding: return - internal_net = net_list[0]['network_id'] + az_name = org_binding['availability_zone'] + int_net = nsxv_db.get_nsxv_internal_network( + context.session, + vcns_const.InternalEdgePurposes.INTER_EDGE_PURPOSE, + az_name) + + if not int_net: + return + # Query the ports of this internal network + internal_nets = [int_net['network_id']] ports = self.nsxv_plugin.get_ports( context, filters={'device_id': [org_router_id], - 'network_id': [internal_net]}) + 'network_id': internal_nets}) if not ports: LOG.debug('No metadata ports found for %s', org_router_id) @@ -1208,20 +1223,16 @@ class EdgeManager(object): LOG.debug('Expecting one metadata port for %s. Found %d ports', org_router_id, len(ports)) - org_binding = nsxv_db.get_nsxv_router_binding(context.session, - org_router_id) - - if org_binding: - edge_id = org_binding['edge_id'] - bindings = nsxv_db.get_nsxv_router_bindings( - context.session, filters={'edge_id': [edge_id]}) - for binding in bindings: - if binding['router_id'] != org_router_id: - for port in ports: - self.plugin.update_port( - context, port['id'], - {'port': {'device_id': binding['router_id']}}) - return + edge_id = org_binding['edge_id'] + bindings = nsxv_db.get_nsxv_router_bindings( + context.session, filters={'edge_id': [edge_id]}) + for binding in bindings: + if binding['router_id'] != org_router_id: + for port in ports: + self.plugin.update_port( + context, port['id'], + {'port': {'device_id': binding['router_id']}}) + return def allocate_new_dhcp_edge(self, context, network_id, resource_id, availability_zone): @@ -1231,7 +1242,8 @@ class EdgeManager(object): new_edge = nsxv_db.get_nsxv_router_binding(context.session, resource_id) nsxv_db.allocate_edge_vnic_with_tunnel_index( - context.session, new_edge['edge_id'], network_id) + context.session, new_edge['edge_id'], network_id, + availability_zone.name) return new_edge['edge_id'] def create_dhcp_edge_service(self, context, network_id, @@ -1421,9 +1433,9 @@ class EdgeManager(object): # Attach to DHCP Edge dhcp_edge_id = self.allocate_new_dhcp_edge( context, network_id, resource_id, availability_zone) - - self.plugin.metadata_proxy_handler.configure_router_edge( - context, resource_id) + md_proxy = self.plugin.get_metadata_proxy_handler( + availability_zone.name) + md_proxy.configure_router_edge(context, resource_id) with locking.LockManager.get_lock(str(dhcp_edge_id)): self.plugin.setup_dhcp_edge_fw_rules( context, self.plugin, resource_id) @@ -1659,7 +1671,7 @@ class EdgeManager(object): virtual_wire = {"name": lswitch_name, "tenantId": "virtual wire tenant"} config_spec = {"virtualWireCreateSpec": virtual_wire} - vdn_scope_id = cfg.CONF.nsxv.vdn_scope_id + vdn_scope_id = availability_zone.vdn_scope_id h, lswitch_id = self.nsxv_manager.vcns.create_virtual_wire( vdn_scope_id, config_spec) @@ -1997,6 +2009,83 @@ class EdgeManager(object): metainfo = self.plugin.get_flavor_metainfo(context, flavor_id) return metainfo.get('syslog') + def update_external_interface( + self, nsxv_manager, context, router_id, ext_net_id, + ipaddr, netmask, secondary=None): + with locking.LockManager.get_lock(str(router_id)): + self._update_external_interface(nsxv_manager, context, router_id, + ext_net_id, ipaddr, netmask, + secondary=secondary) + + def _update_external_interface( + self, nsxv_manager, context, router_id, ext_net_id, + ipaddr, netmask, secondary=None): + secondary = secondary or [] + binding = nsxv_db.get_nsxv_router_binding(context.session, router_id) + + # If no binding was found, no interface to update - exit + if not binding: + LOG.error(_LE('Edge binding not found for router %s'), router_id) + return + + net_bindings = nsxv_db.get_network_bindings( + context.session, ext_net_id) + if not net_bindings: + az_name = binding.availability_zone + az = self._availability_zones.get_availability_zone(az_name) + vcns_network_id = az.external_network + else: + vcns_network_id = net_bindings[0].phy_uuid + + # reorganize external vnic's address groups + if netmask: + address_groups = [] + addr_list = [] + for str_cidr in netmask: + ip_net = netaddr.IPNetwork(str_cidr) + address_group = {'primaryAddress': None, + 'subnetPrefixLength': str(ip_net.prefixlen)} + if (ipaddr not in addr_list and + _check_ipnet_ip(ip_net, ipaddr)): + address_group['primaryAddress'] = ipaddr + addr_list.append(ipaddr) + for sec_ip in secondary: + if (sec_ip not in addr_list and + _check_ipnet_ip(ip_net, sec_ip)): + if not address_group['primaryAddress']: + address_group['primaryAddress'] = sec_ip + else: + if not address_group.get('secondaryAddresses'): + address_group['secondaryAddresses'] = { + 'ipAddress': [sec_ip], + 'type': 'secondary_addresses'} + else: + address_group['secondaryAddresses'][ + 'ipAddress'].append(sec_ip) + addr_list.append(sec_ip) + if address_group['primaryAddress']: + address_groups.append(address_group) + if ipaddr not in addr_list: + LOG.error(_LE("primary address %s of ext vnic is not " + "configured"), ipaddr) + if secondary: + missed_ip_sec = set(secondary) - set(addr_list) + if missed_ip_sec: + LOG.error(_LE("secondary address %s of ext vnic are not " + "configured"), str(missed_ip_sec)) + nsxv_manager.update_interface(router_id, binding['edge_id'], + vcns_const.EXTERNAL_VNIC_INDEX, + vcns_network_id, + address_groups=address_groups) + + else: + nsxv_manager.update_interface(router_id, binding['edge_id'], + vcns_const.EXTERNAL_VNIC_INDEX, + vcns_network_id, + address=ipaddr, + netmask=netmask, + secondary=secondary) + def create_lrouter(nsxv_manager, context, lrouter, lswitch=None, dist=False, availability_zone=None): @@ -2040,7 +2129,7 @@ def remove_irrelevant_keys_from_edge_request(edge_request): edge_request.pop(key, None) -def _retrieve_nsx_switch_id(context, network_id): +def _retrieve_nsx_switch_id(context, network_id, az_name): """Helper method to retrieve backend switch ID.""" bindings = nsxv_db.get_network_bindings(context.session, network_id) if bindings: @@ -2053,8 +2142,10 @@ def _retrieve_nsx_switch_id(context, network_id): else: # If network is of type VLAN and multiple dvs associated with # one neutron network, retrieve the logical network id for the - # edge/mgmt cluster's DVS. - dvs_id = cfg.CONF.nsxv.dvs_id + # edge/mgmt cluster's DVS from the networks availability zone. + azs = nsx_az.ConfiguredAvailabilityZones() + az = azs.get_availability_zone(az_name) + dvs_id = az.dvs_id return nsx_db.get_nsx_switch_id_for_dvs( context.session, network_id, dvs_id) # Get the physical port group /wire id of the network id @@ -2215,15 +2306,6 @@ def clear_gateway(nsxv_manager, context, router_id): return update_gateway(nsxv_manager, context, router_id, None) -def update_external_interface( - nsxv_manager, context, router_id, ext_net_id, - ipaddr, netmask, secondary=None): - with locking.LockManager.get_lock(str(router_id)): - _update_external_interface(nsxv_manager, context, router_id, - ext_net_id, ipaddr, netmask, - secondary=secondary) - - def _check_ipnet_ip(ipnet, ip_address): """Check one ip is valid ip from ipnet.""" ip = netaddr.IPAddress(ip_address) @@ -2234,73 +2316,6 @@ def _check_ipnet_ip(ipnet, ip_address): return False -def _update_external_interface( - nsxv_manager, context, router_id, ext_net_id, - ipaddr, netmask, secondary=None): - secondary = secondary or [] - binding = nsxv_db.get_nsxv_router_binding(context.session, router_id) - - # If no binding was found, no interface to update - exit - if not binding: - LOG.error(_LE('Edge binding not found for router %s'), router_id) - return - - net_bindings = nsxv_db.get_network_bindings(context.session, ext_net_id) - if not net_bindings: - vcns_network_id = nsxv_manager.external_network - else: - vcns_network_id = net_bindings[0].phy_uuid - - # reorganize external vnic's address groups - if netmask: - address_groups = [] - addr_list = [] - for str_cidr in netmask: - ip_net = netaddr.IPNetwork(str_cidr) - address_group = {'primaryAddress': None, - 'subnetPrefixLength': str(ip_net.prefixlen)} - if (ipaddr not in addr_list and - _check_ipnet_ip(ip_net, ipaddr)): - address_group['primaryAddress'] = ipaddr - addr_list.append(ipaddr) - for sec_ip in secondary: - if (sec_ip not in addr_list and - _check_ipnet_ip(ip_net, sec_ip)): - if not address_group['primaryAddress']: - address_group['primaryAddress'] = sec_ip - else: - if not address_group.get('secondaryAddresses'): - address_group['secondaryAddresses'] = { - 'ipAddress': [sec_ip], - 'type': 'secondary_addresses'} - else: - address_group['secondaryAddresses'][ - 'ipAddress'].append(sec_ip) - addr_list.append(sec_ip) - if address_group['primaryAddress']: - address_groups.append(address_group) - if ipaddr not in addr_list: - LOG.error(_LE("primary address %s of ext vnic is not " - "configured"), ipaddr) - if secondary: - missed_ip_sec = set(secondary) - set(addr_list) - if missed_ip_sec: - LOG.error(_LE("secondary address %s of ext vnic are not " - "configured"), str(missed_ip_sec)) - nsxv_manager.update_interface(router_id, binding['edge_id'], - vcns_const.EXTERNAL_VNIC_INDEX, - vcns_network_id, - address_groups=address_groups) - - else: - nsxv_manager.update_interface(router_id, binding['edge_id'], - vcns_const.EXTERNAL_VNIC_INDEX, - vcns_network_id, - address=ipaddr, - netmask=netmask, - secondary=secondary) - - def update_internal_interface(nsxv_manager, context, router_id, int_net_id, address_groups, is_connected=True): with locking.LockManager.get_lock(str(router_id)): @@ -2311,15 +2326,18 @@ def update_internal_interface(nsxv_manager, context, router_id, int_net_id, def _update_internal_interface(nsxv_manager, context, router_id, int_net_id, address_groups, is_connected=True): - # Get the pg/wire id of the network id - vcns_network_id = _retrieve_nsx_switch_id(context, int_net_id) - LOG.debug("Network id %(network_id)s corresponding ref is : " - "%(net_moref)s", {'network_id': int_net_id, - 'net_moref': vcns_network_id}) # Get edge id binding = nsxv_db.get_nsxv_router_binding(context.session, router_id) edge_id = binding['edge_id'] + + # Get the pg/wire id of the network id + az_name = binding['availability_zone'] + vcns_network_id = _retrieve_nsx_switch_id(context, int_net_id, az_name) + LOG.debug("Network id %(network_id)s corresponding ref is : " + "%(net_moref)s", {'network_id': int_net_id, + 'net_moref': vcns_network_id}) + edge_vnic_binding = nsxv_db.get_edge_vnic_binding( context.session, edge_id, int_net_id) # if edge_vnic_binding is None, then first select one available @@ -2345,14 +2363,17 @@ def add_vdr_internal_interface(nsxv_manager, context, router_id, def _add_vdr_internal_interface(nsxv_manager, context, router_id, int_net_id, address_groups, is_connected=True): - # Get the pg/wire id of the network id - vcns_network_id = _retrieve_nsx_switch_id(context, int_net_id) - LOG.debug("Network id %(network_id)s corresponding ref is : " - "%(net_moref)s", {'network_id': int_net_id, - 'net_moref': vcns_network_id}) # Get edge id binding = nsxv_db.get_nsxv_router_binding(context.session, router_id) edge_id = binding['edge_id'] + + # Get the pg/wire id of the network id + az_name = binding['availability_zone'] + vcns_network_id = _retrieve_nsx_switch_id(context, int_net_id, az_name) + LOG.debug("Network id %(network_id)s corresponding ref is : " + "%(net_moref)s", {'network_id': int_net_id, + 'net_moref': vcns_network_id}) + edge_vnic_binding = nsxv_db.get_edge_vnic_binding( context.session, edge_id, int_net_id) if not edge_vnic_binding: @@ -2378,15 +2399,17 @@ def update_vdr_internal_interface(nsxv_manager, context, router_id, int_net_id, def _update_vdr_internal_interface(nsxv_manager, context, router_id, int_net_id, address_groups, is_connected=True): + # Get edge id + binding = nsxv_db.get_nsxv_router_binding(context.session, router_id) + edge_id = binding['edge_id'] + # Get the pg/wire id of the network id - vcns_network_id = _retrieve_nsx_switch_id(context, int_net_id) + az_name = binding['availability_zone'] + vcns_network_id = _retrieve_nsx_switch_id(context, int_net_id, az_name) LOG.debug("Network id %(network_id)s corresponding ref is : " "%(net_moref)s", {'network_id': int_net_id, 'net_moref': vcns_network_id}) - # Get edge id - binding = nsxv_db.get_nsxv_router_binding(context.session, router_id) - edge_id = binding['edge_id'] edge_vnic_binding = nsxv_db.get_edge_vnic_binding( context.session, edge_id, int_net_id) nsxv_manager.update_vdr_internal_interface( @@ -2402,12 +2425,6 @@ def delete_interface(nsxv_manager, context, router_id, network_id, dist=False): def _delete_interface(nsxv_manager, context, router_id, network_id, dist=False): - # Get the pg/wire id of the network id - vcns_network_id = _retrieve_nsx_switch_id(context, network_id) - LOG.debug("Network id %(network_id)s corresponding ref is : " - "%(net_moref)s", {'network_id': network_id, - 'net_moref': vcns_network_id}) - # Get edge id binding = nsxv_db.get_nsxv_router_binding(context.session, router_id) if not binding: @@ -2416,6 +2433,14 @@ def _delete_interface(nsxv_manager, context, router_id, network_id, return edge_id = binding['edge_id'] + + # Get the pg/wire id of the network id + az_name = binding['availability_zone'] + vcns_network_id = _retrieve_nsx_switch_id(context, network_id, az_name) + LOG.debug("Network id %(network_id)s corresponding ref is : " + "%(net_moref)s", {'network_id': network_id, + 'net_moref': vcns_network_id}) + edge_vnic_binding = nsxv_db.get_edge_vnic_binding( context.session, edge_id, network_id) if not edge_vnic_binding: diff --git a/vmware_nsx/plugins/nsx_v/vshield/vcns_driver.py b/vmware_nsx/plugins/nsx_v/vshield/vcns_driver.py index 68ecf2eabc..c8a741df2b 100644 --- a/vmware_nsx/plugins/nsx_v/vshield/vcns_driver.py +++ b/vmware_nsx/plugins/nsx_v/vshield/vcns_driver.py @@ -42,7 +42,6 @@ class VcnsDriver(edge_appliance_driver.EdgeApplianceDriver, self.insecure = cfg.CONF.nsxv.insecure self.datacenter_moid = cfg.CONF.nsxv.datacenter_moid self.deployment_container_id = cfg.CONF.nsxv.deployment_container_id - self.external_network = cfg.CONF.nsxv.external_network self._pid = None self._task_manager = None self.vcns = vcns.Vcns(self.vcns_uri, self.vcns_user, self.vcns_passwd, diff --git a/vmware_nsx/shell/admin/plugins/nsxv/resources/metadata.py b/vmware_nsx/shell/admin/plugins/nsxv/resources/metadata.py index 32f72cbdd8..981702a92a 100644 --- a/vmware_nsx/shell/admin/plugins/nsxv/resources/metadata.py +++ b/vmware_nsx/shell/admin/plugins/nsxv/resources/metadata.py @@ -22,9 +22,11 @@ from neutron.db import models_v2 from oslo_config import cfg from vmware_nsx._i18n import _LE, _LI +from vmware_nsx.common import config from vmware_nsx.common import locking from vmware_nsx.common import nsxv_constants from vmware_nsx.db import nsxv_db +from vmware_nsx.plugins.nsx_v import availability_zones as nsx_az from vmware_nsx.plugins.nsx_v import md_proxy from vmware_nsx.plugins.nsx_v.vshield.common import constants as vcns_constants from vmware_nsx.plugins.nsx_v.vshield import nsxv_loadbalancer as nsxv_lb @@ -42,26 +44,48 @@ nsxv = utils.get_nsxv_client() @admin_utils.output_header def nsx_redo_metadata_cfg(resource, event, trigger, **kwargs): edgeapi = utils.NeutronDbClient() - net_list = nsxv_db.get_nsxv_internal_network( + + config.register_nsxv_azs(cfg.CONF, cfg.CONF.nsxv.availability_zones) + conf_az = nsx_az.ConfiguredAvailabilityZones() + az_list = conf_az.list_availability_zones() + for name in az_list: + az = conf_az.get_availability_zone(name) + if az.supports_metadata(): + nsx_redo_metadata_cfg_for_az(az, edgeapi) + else: + LOG.info(_LI("Skipping availability zone: %s - no metadata " + "configuration"), az.name) + + +def nsx_redo_metadata_cfg_for_az(az, edgeapi): + LOG.info(_LI("Updating MetaData for availability zone: %s"), az.name) + + # Get the list of internal networks for this AZ + db_net = nsxv_db.get_nsxv_internal_network( edgeapi.context.session, - vcns_constants.InternalEdgePurposes.INTER_EDGE_PURPOSE) + vcns_constants.InternalEdgePurposes.INTER_EDGE_PURPOSE, + az.name) internal_net = None internal_subnet = None - if net_list: - internal_net = net_list[0]['network_id'] + if db_net: + internal_net = db_net['network_id'] internal_subnet = edgeapi.context.session.query( models_v2.Subnet).filter_by( network_id=internal_net).first().get('id') + # Get the list of internal edges for this AZ edge_list = nsxv_db.get_nsxv_internal_edges_by_purpose( edgeapi.context.session, vcns_constants.InternalEdgePurposes.INTER_EDGE_PURPOSE) + edge_az_list = [edge for edge in edge_list if + nsxv_db.get_router_availability_zone( + edgeapi.context.session, edge['router_id']) == az.name] - md_rtr_ids = [edge['router_id'] for edge in edge_list] + md_rtr_ids = [edge['router_id'] for edge in edge_az_list] edge_internal_ips = [] - for edge in edge_list: + for edge in edge_az_list: edge_internal_port = edgeapi.context.session.query( models_v2.Port).filter_by(network_id=internal_net, device_id=edge['router_id']).first() @@ -79,7 +103,8 @@ def nsx_redo_metadata_cfg(resource, event, trigger, **kwargs): router_bindings = nsxv_db.get_nsxv_router_bindings( edgeapi.context.session, - filters={'edge_type': [nsxv_constants.SERVICE_EDGE]}) + filters={'edge_type': [nsxv_constants.SERVICE_EDGE], + 'availability_zones': az.name}) edge_ids = list(set([binding['edge_id'] for binding in router_bindings if (binding['router_id'] not in set(md_rtr_ids) and not binding['router_id'].startswith( diff --git a/vmware_nsx/shell/admin/plugins/nsxv/resources/routers.py b/vmware_nsx/shell/admin/plugins/nsxv/resources/routers.py index ea19df33bc..9b5a71ef44 100644 --- a/vmware_nsx/shell/admin/plugins/nsxv/resources/routers.py +++ b/vmware_nsx/shell/admin/plugins/nsxv/resources/routers.py @@ -110,10 +110,11 @@ def nsx_recreate_router_edge(resource, event, trigger, **kwargs): # Go over all the relevant routers for router in routers: router_id = router['id'] + az_name = router['availability_zone'] # clean up other objects related to this router if plugin.metadata_proxy_handler: - plugin.metadata_proxy_handler.cleanup_router_edge( - context, router_id) + md_proxy = plugin.get_metadata_proxy_handler(az_name) + md_proxy.cleanup_router_edge(context, router_id) # attach the router to a new edge appliance_size = router.get(routersize.ROUTER_SIZE) diff --git a/vmware_nsx/tests/unit/nsx_v/test_availability_zones.py b/vmware_nsx/tests/unit/nsx_v/test_availability_zones.py index f32837ff4b..cf441f618d 100644 --- a/vmware_nsx/tests/unit/nsx_v/test_availability_zones.py +++ b/vmware_nsx/tests/unit/nsx_v/test_availability_zones.py @@ -34,11 +34,23 @@ class NsxvAvailabilityZonesTestCase(base.BaseTestCase): self.group_name = 'az:%s' % self.az_name config.register_nsxv_azs(cfg.CONF, [self.az_name]) cfg.CONF.set_override("ha_placement_random", True, group="nsxv") + cfg.CONF.set_override("mgt_net_proxy_ips", ["2.2.2.2"], group="nsxv") + cfg.CONF.set_override("dvs_id", "dvs-1", group="nsxv") - def _config_az(self, resource_pool_id="respool", datastore_id="datastore", - edge_ha=True, ha_datastore_id="hastore", + def _config_az(self, + resource_pool_id="respool", + datastore_id="datastore", + edge_ha=True, + ha_datastore_id="hastore", + backup_edge_pool=DEF_AZ_POOL, ha_placement_random=False, - backup_edge_pool=DEF_AZ_POOL): + mgt_net_moid="portgroup-407", + mgt_net_proxy_ips=["1.1.1.1"], + mgt_net_proxy_netmask="255.255.255.0", + mgt_net_default_gateway="2.2.2.2", + external_network="network-17", + vdn_scope_id="vdnscope-1", + dvs_id="dvs-2"): cfg.CONF.set_override("resource_pool_id", resource_pool_id, group=self.group_name) cfg.CONF.set_override("datastore_id", datastore_id, @@ -55,6 +67,29 @@ class NsxvAvailabilityZonesTestCase(base.BaseTestCase): if backup_edge_pool is not None: cfg.CONF.set_override("backup_edge_pool", backup_edge_pool, group=self.group_name) + if mgt_net_moid is not None: + cfg.CONF.set_override("mgt_net_moid", mgt_net_moid, + group=self.group_name) + if mgt_net_proxy_ips is not None: + cfg.CONF.set_override("mgt_net_proxy_ips", mgt_net_proxy_ips, + group=self.group_name) + if mgt_net_proxy_netmask is not None: + cfg.CONF.set_override("mgt_net_proxy_netmask", + mgt_net_proxy_netmask, + group=self.group_name) + if mgt_net_default_gateway is not None: + cfg.CONF.set_override("mgt_net_default_gateway", + mgt_net_default_gateway, + group=self.group_name) + if external_network is not None: + cfg.CONF.set_override("external_network", external_network, + group=self.group_name) + if vdn_scope_id is not None: + cfg.CONF.set_override("vdn_scope_id", vdn_scope_id, + group=self.group_name) + if dvs_id is not None: + cfg.CONF.set_override("dvs_id", dvs_id, + group=self.group_name) def test_simple_availability_zone(self): self._config_az() @@ -62,10 +97,18 @@ class NsxvAvailabilityZonesTestCase(base.BaseTestCase): self.assertEqual(self.az_name, az.name) self.assertEqual("respool", az.resource_pool) self.assertEqual("datastore", az.datastore_id) - self.assertEqual(True, az.edge_ha) + self.assertTrue(az.edge_ha) self.assertEqual("hastore", az.ha_datastore_id) - self.assertEqual(False, az.ha_placement_random) + self.assertFalse(az.ha_placement_random) self.assertEqual(DEF_AZ_POOL, az.backup_edge_pool) + self.assertEqual("portgroup-407", az.mgt_net_moid) + self.assertEqual(["1.1.1.1"], az.mgt_net_proxy_ips) + self.assertEqual("255.255.255.0", az.mgt_net_proxy_netmask) + self.assertEqual("2.2.2.2", az.mgt_net_default_gateway) + self.assertEqual("network-17", az.external_network) + self.assertEqual("vdnscope-1", az.vdn_scope_id) + self.assertEqual("dvs-2", az.dvs_id) + self.assertTrue(az.az_metadata_support) def test_availability_zone_no_edge_ha(self): self._config_az(edge_ha=False) @@ -73,9 +116,9 @@ class NsxvAvailabilityZonesTestCase(base.BaseTestCase): self.assertEqual(self.az_name, az.name) self.assertEqual("respool", az.resource_pool) self.assertEqual("datastore", az.datastore_id) - self.assertEqual(False, az.edge_ha) - self.assertEqual(None, az.ha_datastore_id) - self.assertEqual(False, az.ha_placement_random) + self.assertFalse(az.edge_ha) + self.assertIsNone(az.ha_datastore_id) + self.assertFalse(az.ha_placement_random) def test_availability_zone_no_ha_datastore(self): self._config_az(ha_datastore_id=None) @@ -83,9 +126,9 @@ class NsxvAvailabilityZonesTestCase(base.BaseTestCase): self.assertEqual(self.az_name, az.name) self.assertEqual("respool", az.resource_pool) self.assertEqual("datastore", az.datastore_id) - self.assertEqual(True, az.edge_ha) - self.assertEqual(None, az.ha_datastore_id) - self.assertEqual(False, az.ha_placement_random) + self.assertTrue(az.edge_ha) + self.assertIsNone(az.ha_datastore_id) + self.assertFalse(az.ha_placement_random) def test_missing_group_section(self): self.assertRaises( @@ -113,9 +156,9 @@ class NsxvAvailabilityZonesTestCase(base.BaseTestCase): self.assertEqual(self.az_name, az.name) self.assertEqual("respool", az.resource_pool) self.assertEqual("datastore", az.datastore_id) - self.assertEqual(False, az.edge_ha) - self.assertEqual(None, az.ha_datastore_id) - self.assertEqual(False, az.ha_placement_random) + self.assertFalse(az.edge_ha) + self.assertIsNone(az.ha_datastore_id) + self.assertFalse(az.ha_placement_random) def test_availability_zone_missing_edge_placement(self): self._config_az(ha_placement_random=None) @@ -123,10 +166,10 @@ class NsxvAvailabilityZonesTestCase(base.BaseTestCase): self.assertEqual(self.az_name, az.name) self.assertEqual("respool", az.resource_pool) self.assertEqual("datastore", az.datastore_id) - self.assertEqual(True, az.edge_ha) + self.assertTrue(az.edge_ha) self.assertEqual("hastore", az.ha_datastore_id) # ha_placement_random should have the global value - self.assertEqual(True, az.ha_placement_random) + self.assertTrue(az.ha_placement_random) def test_availability_zone_missing_backup_pool(self): self._config_az(backup_edge_pool=None) @@ -135,6 +178,28 @@ class NsxvAvailabilityZonesTestCase(base.BaseTestCase): # Should use the global configuration instead self.assertEqual(DEF_GLOBAL_POOL, az.backup_edge_pool) + def test_availability_zone_missing_metadata(self): + self._config_az(mgt_net_proxy_ips=None) + az = nsx_az.ConfiguredAvailabilityZone(self.az_name) + self.assertIsNone(az.mgt_net_moid) + self.assertEqual([], az.mgt_net_proxy_ips) + self.assertIsNone(az.mgt_net_proxy_netmask) + self.assertIsNone(az.mgt_net_default_gateway) + self.assertFalse(az.az_metadata_support) + + def test_availability_zone_same_metadata(self): + self._config_az(mgt_net_proxy_ips=["2.2.2.2"]) + self.assertRaises( + nsx_exc.NsxInvalidConfiguration, + nsx_az.ConfiguredAvailabilityZone, + self.az_name) + + self._config_az(mgt_net_proxy_ips=["2.2.2.2", "3.3.3.3"]) + self.assertRaises( + nsx_exc.NsxInvalidConfiguration, + nsx_az.ConfiguredAvailabilityZone, + self.az_name) + class NsxvAvailabilityZonesOldTestCase(base.BaseTestCase): """Test old way of configuring the availability zones @@ -142,16 +207,31 @@ class NsxvAvailabilityZonesOldTestCase(base.BaseTestCase): using a one-line configuration instead of different dynamic sections """ + def setUp(self): + super(NsxvAvailabilityZonesOldTestCase, self).setUp() + cfg.CONF.set_override("mgt_net_proxy_ips", ["2.2.2.2"], group="nsxv") + cfg.CONF.set_override("dvs_id", "dvs-1", group="nsxv") + def test_simple_availability_zone(self): az = nsx_az.ConfiguredAvailabilityZone( "name:respool:datastore:true:hastore") self.assertEqual("name", az.name) self.assertEqual("respool", az.resource_pool) self.assertEqual("datastore", az.datastore_id) - self.assertEqual(True, az.edge_ha) + self.assertTrue(az.edge_ha) self.assertEqual("hastore", az.ha_datastore_id) - self.assertEqual(False, az.ha_placement_random) + self.assertFalse(az.ha_placement_random) self.assertEqual(DEF_GLOBAL_POOL, az.backup_edge_pool) + # should get the global configuration (which is empty now) + self.assertIsNone(az.external_network) + self.assertIsNone(az.vdn_scope_id) + self.assertEqual("dvs-1", az.dvs_id) + # no metadata per az support + self.assertFalse(az.az_metadata_support) + self.assertIsNone(az.mgt_net_moid) + self.assertEqual([], az.mgt_net_proxy_ips) + self.assertIsNone(az.mgt_net_proxy_netmask) + self.assertIsNone(az.mgt_net_default_gateway) def test_availability_zone_without_ha_datastore(self): az = nsx_az.ConfiguredAvailabilityZone( @@ -159,7 +239,7 @@ class NsxvAvailabilityZonesOldTestCase(base.BaseTestCase): self.assertEqual("name", az.name) self.assertEqual("respool", az.resource_pool) self.assertEqual("datastore", az.datastore_id) - self.assertEqual(True, az.edge_ha) + self.assertTrue(az.edge_ha) self.assertIsNone(az.ha_datastore_id) def test_availability_zone_without_edge_ha(self): @@ -168,7 +248,7 @@ class NsxvAvailabilityZonesOldTestCase(base.BaseTestCase): self.assertEqual("name", az.name) self.assertEqual("respool", az.resource_pool) self.assertEqual("datastore", az.datastore_id) - self.assertEqual(False, az.edge_ha) + self.assertFalse(az.edge_ha) self.assertIsNone(az.ha_datastore_id) def test_availability_fail_long_name(self): diff --git a/vmware_nsx/tests/unit/nsx_v/test_plugin.py b/vmware_nsx/tests/unit/nsx_v/test_plugin.py index f5c101b04e..4a33682766 100644 --- a/vmware_nsx/tests/unit/nsx_v/test_plugin.py +++ b/vmware_nsx/tests/unit/nsx_v/test_plugin.py @@ -176,6 +176,9 @@ class NsxVPluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase): plugin_instance.real_get_edge = plugin_instance._get_edge_id_by_rtr_id plugin_instance._get_edge_id_by_rtr_id = mock.Mock() plugin_instance._get_edge_id_by_rtr_id.return_value = False + plugin_instance._get_edge_id_and_az_by_rtr_id = mock.Mock() + plugin_instance._get_edge_id_and_az_by_rtr_id.return_value = ( + False, False) plugin_instance.edge_manager.is_dhcp_opt_enabled = True # call init_complete manually. The event is not called in unit tests plugin_instance.init_complete(None, None, {}) @@ -505,26 +508,28 @@ class TestNetworksV2(test_plugin.TestNetworksV2, NsxVPluginV2TestCase): def test_get_dvs_ids_for_multiple_dvs_vlan_network(self): p = directory.get_plugin() + default_dvs = 'fake_dvs_id' # If no DVS-ID is provided as part of physical network, return # global DVS-ID configured in nsx.ini physical_network = constants.ATTR_NOT_SPECIFIED - self.assertEqual(['fake_dvs_id'], p._get_dvs_ids(physical_network)) + self.assertEqual(['fake_dvs_id'], p._get_dvs_ids( + physical_network, default_dvs)) # If DVS-IDs are provided as part of physical network as a comma # separated string, return them as a list of DVS-IDs. physical_network = 'dvs-1,dvs-2, dvs-3' expected_dvs_ids = ['dvs-1', 'dvs-2', 'dvs-3'] self.assertEqual(expected_dvs_ids, - sorted(p._get_dvs_ids(physical_network))) + sorted(p._get_dvs_ids(physical_network, default_dvs))) # Ignore extra commas ',' in the physical_network attribute. physical_network = ',,,dvs-1,dvs-2,, dvs-3,' expected_dvs_ids = ['dvs-1', 'dvs-2', 'dvs-3'] self.assertEqual(expected_dvs_ids, - sorted(p._get_dvs_ids(physical_network))) + sorted(p._get_dvs_ids(physical_network, default_dvs))) # Ignore duplicate DVS-IDs in the physical_network attribute. physical_network = ',,,dvs-1,dvs-2,, dvs-2,' expected_dvs_ids = ['dvs-1', 'dvs-2'] self.assertEqual(expected_dvs_ids, - sorted(p._get_dvs_ids(physical_network))) + sorted(p._get_dvs_ids(physical_network, default_dvs))) def test_create_vxlan_with_tz_provider_network(self): name = 'provider_net_vxlan' @@ -3311,6 +3316,9 @@ class NsxVTestSecurityGroup(ext_sg.TestSecurityGroups, plugin_instance = directory.get_plugin() plugin_instance._get_edge_id_by_rtr_id = mock.Mock() plugin_instance._get_edge_id_by_rtr_id.return_value = False + plugin_instance._get_edge_id_and_az_by_rtr_id = mock.Mock() + plugin_instance._get_edge_id_and_az_by_rtr_id.return_value = ( + False, False) def test_list_ports_security_group(self): with self.network() as n: diff --git a/vmware_nsx/tests/unit/nsx_v/vshield/fake_vcns.py b/vmware_nsx/tests/unit/nsx_v/vshield/fake_vcns.py index 6616a666ba..4e2f557be5 100644 --- a/vmware_nsx/tests/unit/nsx_v/vshield/fake_vcns.py +++ b/vmware_nsx/tests/unit/nsx_v/vshield/fake_vcns.py @@ -249,7 +249,7 @@ class FakeVcns(object): response = { 'label': 'vNic_1', 'name': 'internal1', - 'address_groups': {'address_groups': []}, + 'addressGroups': {'addressGroups': []}, 'mtu': 1500, 'type': 'trunk', 'subInterfaces': {'subInterfaces': []},