vmware-nsx/vmware_nsx/plugins/nsx_p/availability_zones.py
Anna Khmelnitsky f5cdef72a6 Validate transport zone type upon neutron startup
Upon startup, the plugin validates that configured default tzs
exist on backend, however does not validate their type. This
change adds type validation (OVERLAY or VLAN), and throws startup
exception if type is incorrect.
In addition, this change adds null validation and removes dead
code.

Change-Id: Ibeff164eb03fec9141326c24b0c069f0e16a1e7b
2021-04-28 23:59:48 +00:00

303 lines
13 KiB
Python

# Copyright 2017 VMware, Inc.
# All Rights Reserved
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_config import cfg
from oslo_log import log
from vmware_nsx.common import availability_zones as common_az
from vmware_nsx.common import config
from vmware_nsx.common import exceptions as nsx_exc
from vmware_nsx.plugins.common_v3 import availability_zones as v3_az
from vmware_nsx.plugins.nsx_p import utils
from vmware_nsxlib.v3 import exceptions as nsx_lib_exc
from vmware_nsxlib.v3 import nsx_constants
from vmware_nsxlib.v3.policy import utils as p_utils
LOG = log.getLogger(__name__)
DEFAULT_NAME = common_az.DEFAULT_NAME + 'p'
class NsxPAvailabilityZone(v3_az.NsxV3AvailabilityZone):
def get_az_opts(self):
return config.get_nsxp_az_opts(self.name)
def init_defaults(self):
# use the default configuration
self.metadata_proxy = cfg.CONF.nsx_p.metadata_proxy
self.dhcp_profile = cfg.CONF.nsx_p.dhcp_profile
self.native_metadata_route = cfg.CONF.nsx_p.native_metadata_route
self.default_overlay_tz = cfg.CONF.nsx_p.default_overlay_tz
self.default_vlan_tz = cfg.CONF.nsx_p.default_vlan_tz
self.default_tier0_router = cfg.CONF.nsx_p.default_tier0_router
self.dns_domain = cfg.CONF.nsx_p.dns_domain
self.nameservers = cfg.CONF.nsx_p.nameservers
self.edge_cluster = cfg.CONF.nsx_p.edge_cluster
def _init_default_resource(self, nsxpolicy, resource_api, config_name,
filter_list_results=None,
auto_config=False,
is_mandatory=True,
search_scope=None):
# NOTE(annak): we may need to generalize this for API calls
# requiring path ids
name_or_id = getattr(self, config_name)
err_msg = (_("Could not find %(res)s %(id)s for availability "
"zone %(az)s") % {
'res': config_name,
'id': name_or_id,
'az': self.name})
if not name_or_id:
if auto_config:
# If the field not specified, the system will auto-configure
# in case only single resource is present
resources = resource_api.list()
if filter_list_results:
resources = filter_list_results(resources)
if len(resources) == 1:
return resources[0]['id']
if is_mandatory:
if self.is_default():
raise cfg.RequiredOptError(config_name,
group=cfg.OptGroup('nsx_p'))
msg = (_("No %(res)s provided for availability "
"zone %(az)s") % {
'res': config_name,
'az': self.name})
raise nsx_exc.NsxPluginException(err_msg=msg)
return None
# If filtering was specified, we need to ensure the configured
# resource matches the filter
def verify_resource_matches_filter(result):
if filter_list_results:
exists = filter_list_results([result])
if not exists:
LOG.error("Resource %s doesn't match config "
"requirement for %s" % (name_or_id, config_name))
if self.is_default():
raise cfg.RequiredOptError(config_name,
group=cfg.OptGroup('nsx_p'))
raise nsx_exc.NsxPluginException(err_msg=err_msg)
try:
# Check if the configured value is the ID
resource = resource_api.get(name_or_id, silent=True)
verify_resource_matches_filter(resource)
return name_or_id
except nsx_lib_exc.ResourceNotFound:
# Search by tags
if search_scope:
resource_type = resource_api.entry_def.resource_type()
resource_id = nsxpolicy.get_id_by_resource_and_tag(
resource_type,
search_scope,
name_or_id)
if resource_id:
return resource_id
# Check if the configured value is the name
resource = resource_api.get_by_name(name_or_id)
if resource:
verify_resource_matches_filter(resource)
return resource['id']
# Resource not found
if self.is_default():
raise cfg.RequiredOptError(config_name,
group=cfg.OptGroup('nsx_p'))
raise nsx_exc.NsxPluginException(err_msg=err_msg)
def translate_configured_names_to_uuids(self, nsxpolicy, nsxlib=None,
search_scope=None):
super(NsxPAvailabilityZone, self).translate_configured_names_to_uuids(
nsxpolicy)
self._default_overlay_tz_uuid = self._init_default_resource(
nsxpolicy, nsxpolicy.transport_zone, 'default_overlay_tz',
auto_config=True, is_mandatory=True,
filter_list_results=lambda tzs: [
tz for tz in tzs if tz['tz_type'].startswith('OVERLAY')],
search_scope=search_scope)
self._default_vlan_tz_uuid = self._init_default_resource(
nsxpolicy, nsxpolicy.transport_zone, 'default_vlan_tz',
auto_config=True, is_mandatory=False,
filter_list_results=lambda tzs: [
tz for tz in tzs if tz['tz_type'].startswith('VLAN')],
search_scope=search_scope)
self._default_tier0_router = self._init_default_resource(
nsxpolicy, nsxpolicy.tier0, 'default_tier0_router',
auto_config=True, is_mandatory=True,
search_scope=search_scope)
self._edge_cluster_uuid = self._init_default_resource(
nsxpolicy, nsxpolicy.edge_cluster, 'edge_cluster',
auto_config=False, is_mandatory=False,
search_scope=search_scope)
# Init dhcp config from policy or MP
self.use_policy_dhcp = False
if (nsxpolicy.feature_supported(
nsx_constants.FEATURE_NSX_POLICY_DHCP)):
try:
self._policy_dhcp_server_config = self._init_default_resource(
nsxpolicy, nsxpolicy.dhcp_server_config, 'dhcp_profile',
auto_config=False, is_mandatory=False,
search_scope=search_scope)
if self._policy_dhcp_server_config:
self.use_policy_dhcp = True
except Exception:
# Not found. try as MP profile
pass
self._native_dhcp_profile_uuid = None
if not self.use_policy_dhcp and nsxlib:
self._translate_dhcp_profile(nsxlib, search_scope=search_scope)
self.use_policy_md = False
if (nsxpolicy.feature_supported(
nsx_constants.FEATURE_NSX_POLICY_MDPROXY)):
# Try to initialize md-proxy from the policy
try:
self._native_md_proxy_uuid = self._init_default_resource(
nsxpolicy, nsxpolicy.md_proxy, 'metadata_proxy',
auto_config=True, is_mandatory=True,
search_scope=search_scope)
LOG.info("NSX-P az using policy MD proxy: %s",
self._native_md_proxy_uuid)
self.use_policy_md = True
except Exception:
LOG.info("NSX-P az could not use policy MD proxy. Using MP "
"one instead")
if not self.use_policy_md:
# Try to initialize md-proxy from the MP
if nsxlib:
self._translate_metadata_proxy(
nsxlib, search_scope=search_scope)
LOG.info("NSX-P az using MP MD proxy: %s",
self._native_md_proxy_uuid)
else:
self._native_md_proxy_uuid = None
def _validate_tz(self, nsxpolicy, nsxlib, obj_type, obj_id, ec_uuid):
try:
obj_tzs = utils.get_edge_cluster_tzs(nsxpolicy, nsxlib, ec_uuid)
except nsx_lib_exc.ResourceNotFound as e:
# Do not fail plugin init if this code fails
LOG.warning("Failed to get edge cluster %s transport zones: %s",
ec_uuid, e)
return
if self._default_overlay_tz_uuid not in obj_tzs:
msg = (_("%(type)s %(id)s of availability zone %(az)s with edge "
"cluster %(ec)s does not match the default overlay tz "
"%(tz)s") % {
'type': obj_type,
'id': obj_id,
'ec': ec_uuid,
'tz': self._default_overlay_tz_uuid,
'az': self.name})
raise nsx_exc.NsxPluginException(err_msg=msg)
if (self._default_vlan_tz_uuid and
self._default_vlan_tz_uuid not in obj_tzs):
msg = (_("%(type)s %(id)s of availability zone %(az)s with edge "
"cluster %(ec)s does not match the default vlan tz "
"%(tz)s") % {
'type': obj_type,
'id': obj_id,
'ec': ec_uuid,
'tz': self._default_vlan_tz_uuid,
'az': self.name})
raise nsx_exc.NsxPluginException(err_msg=msg)
def validate_availability_zone(self, nsxpolicy, nsxlib=None):
"""Validate that all the components of this AZ are connected"""
if not nsxlib:
LOG.warning("Cannot validate availability zone %s without "
"passthrough api", self.name)
return
# Validate tier0 TZ match the default ones
tier0_ec_path = nsxpolicy.tier0.get_edge_cluster_path(
self._default_tier0_router)
if not tier0_ec_path:
msg = (_("Tier0 %(id)s of availability zone %(az)s does not have "
"an edge cluster") % {
'id': self._default_tier0_router,
'az': self.name})
raise nsx_exc.NsxPluginException(err_msg=msg)
tier0_ec_uuid = p_utils.path_to_id(tier0_ec_path)
self._validate_tz(nsxpolicy, nsxlib, 'Tier0',
self._default_tier0_router,
tier0_ec_uuid)
if self.use_policy_dhcp:
dhcp_ec_path = nsxpolicy.dhcp_server_config.get(
self._policy_dhcp_server_config).get('edge_cluster_path')
if dhcp_ec_path:
dhcp_ec = p_utils.path_to_id(dhcp_ec_path)
if dhcp_ec != tier0_ec_uuid:
self._validate_tz(nsxpolicy, nsxlib, 'DHCP server config',
self._policy_dhcp_server_config,
dhcp_ec)
elif self._native_dhcp_profile_uuid:
dhcp_ec = nsxlib.native_dhcp_profile.get(
self._native_dhcp_profile_uuid).get('edge_cluster_id')
if dhcp_ec != tier0_ec_uuid:
self._validate_tz(nsxpolicy, nsxlib, 'DHCP profile',
self._native_dhcp_profile_uuid,
dhcp_ec)
if self._native_md_proxy_uuid:
# Validate that the edge cluster of the MD proxy (MP or policy one)
# match the configured TZs
if self.use_policy_md:
md_ec_path = nsxpolicy.md_proxy.get(
self._native_md_proxy_uuid).get('edge_cluster_path')
md_ec = p_utils.path_to_id(md_ec_path)
else:
md_ec = nsxlib.native_md_proxy.get(
self._native_md_proxy_uuid).get('edge_cluster_id')
if md_ec != tier0_ec_uuid:
self._validate_tz(nsxpolicy, nsxlib, 'MD Proxy',
self._native_md_proxy_uuid,
md_ec)
class NsxPAvailabilityZones(common_az.ConfiguredAvailabilityZones):
default_name = DEFAULT_NAME
def __init__(self):
default_azs = cfg.CONF.default_availability_zones
super(NsxPAvailabilityZones, self).__init__(
cfg.CONF.nsx_p.availability_zones,
NsxPAvailabilityZone,
default_availability_zones=default_azs)
self.non_default_dns_domain = self.dns_domain_configured_non_default()
def dns_domain_configured_non_default(self):
for az in self.availability_zones.values():
if az.dns_domain and az.dns_domain != cfg.CONF.nsx_p.dns_domain:
return True
return False