f5cdef72a6
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
303 lines
13 KiB
Python
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
|