
Contexts are not thread safe and therefore, methods which are called as thread entry point should create their own context, and pass to any called methods which are using contexts. Change-Id: Ia8629c211807972d228358893a7b787c55b5be7f
709 lines
27 KiB
Python
709 lines
27 KiB
Python
# Copyright 2014 VMware, Inc.
|
|
# All Rights Reserved
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import eventlet
|
|
import hashlib
|
|
import hmac
|
|
|
|
import netaddr
|
|
from neutron import context as neutron_context
|
|
from neutron_lib import constants
|
|
from oslo_config import cfg
|
|
from oslo_log import log as logging
|
|
|
|
from vmware_nsx._i18n import _, _LE, _LW
|
|
from vmware_nsx.common import exceptions as nsxv_exc
|
|
from vmware_nsx.common import locking
|
|
from vmware_nsx.common import nsxv_constants
|
|
from vmware_nsx.common import utils
|
|
from vmware_nsx.db import nsxv_db
|
|
from vmware_nsx.plugins.nsx_v.vshield import (
|
|
nsxv_loadbalancer as nsxv_lb)
|
|
from vmware_nsx.plugins.nsx_v.vshield.common import (
|
|
constants as vcns_const)
|
|
from vmware_nsx.plugins.nsx_v.vshield.common import exceptions
|
|
from vmware_nsx.plugins.nsx_v.vshield import edge_utils
|
|
from vmware_nsx.services.lbaas.nsx_v import lbaas_common
|
|
|
|
METADATA_POOL_NAME = 'MDSrvPool'
|
|
METADATA_VSE_NAME = 'MdSrv'
|
|
METADATA_IP_ADDR = '169.254.169.254'
|
|
METADATA_TCP_PORT = 80
|
|
METADATA_HTTPS_PORT = 443
|
|
METADATA_HTTPS_VIP_PORT = 8775
|
|
INTERNAL_SUBNET = '169.254.128.0/17'
|
|
MAX_INIT_THREADS = 3
|
|
|
|
NET_WAIT_INTERVAL = 240
|
|
NET_CHECK_INTERVAL = 10
|
|
EDGE_WAIT_INTERVAL = 900
|
|
EDGE_CHECK_INTERVAL = 10
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
DEFAULT_EDGE_FIREWALL_RULE = {
|
|
'name': 'VSERule',
|
|
'enabled': True,
|
|
'action': 'allow',
|
|
'source_vnic_groups': ['vse']}
|
|
|
|
|
|
def get_router_fw_rules():
|
|
# build the allowed destination ports list
|
|
int_ports = [METADATA_TCP_PORT,
|
|
METADATA_HTTPS_PORT,
|
|
METADATA_HTTPS_VIP_PORT]
|
|
str_ports = [str(p) for p in int_ports]
|
|
# the list of ports can be extended by configuration
|
|
if cfg.CONF.nsxv.metadata_service_allowed_ports:
|
|
str_ports = str_ports + cfg.CONF.nsxv.metadata_service_allowed_ports
|
|
separator = ','
|
|
dest_ports = separator.join(str_ports)
|
|
|
|
fw_rules = [
|
|
DEFAULT_EDGE_FIREWALL_RULE,
|
|
{
|
|
'name': 'MDServiceIP',
|
|
'enabled': True,
|
|
'action': 'allow',
|
|
'destination_ip_address': [METADATA_IP_ADDR],
|
|
'protocol': 'tcp',
|
|
'destination_port': dest_ports
|
|
},
|
|
{
|
|
'name': 'MDInterEdgeNet',
|
|
'enabled': True,
|
|
'action': 'deny',
|
|
'destination_ip_address': [INTERNAL_SUBNET]
|
|
}]
|
|
|
|
return fw_rules
|
|
|
|
|
|
def get_db_internal_edge_ips(context):
|
|
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]
|
|
return ip_list
|
|
|
|
|
|
class NsxVMetadataProxyHandler(object):
|
|
|
|
def __init__(self, nsxv_plugin):
|
|
self.nsxv_plugin = nsxv_plugin
|
|
context = neutron_context.get_admin_context()
|
|
|
|
# Init cannot run concurrently on multiple nodes
|
|
with locking.LockManager.get_lock('nsx-metadata-init'):
|
|
self.internal_net, self.internal_subnet = (
|
|
self._get_internal_network_and_subnet(context))
|
|
|
|
self.proxy_edge_ips = self._get_proxy_edges(context)
|
|
|
|
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',
|
|
'admin_state_up': True,
|
|
'port_security_enabled': False,
|
|
'shared': False,
|
|
'tenant_id': tenant_id}}
|
|
net = self.nsxv_plugin.create_network(context, net_data)
|
|
|
|
subnet_data = {'subnet':
|
|
{'cidr': cidr,
|
|
'name': 'inter-edge-subnet',
|
|
'gateway_ip': constants.ATTR_NOT_SPECIFIED,
|
|
'allocation_pools': constants.ATTR_NOT_SPECIFIED,
|
|
'ip_version': 4,
|
|
'dns_nameservers': constants.ATTR_NOT_SPECIFIED,
|
|
'host_routes': constants.ATTR_NOT_SPECIFIED,
|
|
'enable_dhcp': False,
|
|
'network_id': net['id'],
|
|
'tenant_id': tenant_id}}
|
|
|
|
subnet = self.nsxv_plugin.create_subnet(
|
|
context,
|
|
subnet_data)
|
|
|
|
return net['id'], subnet['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']
|
|
|
|
if internal_net:
|
|
internal_subnet = self.nsxv_plugin.get_subnets(
|
|
context,
|
|
fields=['id'],
|
|
filters={'network_id': [internal_net]})[0]['id']
|
|
|
|
if internal_net is None or internal_subnet is None:
|
|
if cfg.CONF.nsxv.metadata_initializer:
|
|
# Couldn't find net, subnet - create new
|
|
try:
|
|
internal_net, internal_subnet = (
|
|
self._create_metadata_internal_network(
|
|
context, INTERNAL_SUBNET))
|
|
except Exception as e:
|
|
nsxv_db.delete_nsxv_internal_network(
|
|
context.session,
|
|
vcns_const.InternalEdgePurposes.INTER_EDGE_PURPOSE)
|
|
|
|
# if network is created, clean up
|
|
if internal_net:
|
|
self.nsxv_plugin.delete_network(context,
|
|
internal_net)
|
|
|
|
LOG.exception(_LE("Exception %s while creating internal "
|
|
"network for metadata service"), e)
|
|
return
|
|
|
|
# Update the new network_id in DB
|
|
nsxv_db.create_nsxv_internal_network(
|
|
context.session,
|
|
nsxv_constants.INTER_EDGE_PURPOSE,
|
|
internal_net)
|
|
else:
|
|
error = _('Metadata initialization is incomplete on '
|
|
'initializer node')
|
|
raise nsxv_exc.NsxPluginException(err_msg=error)
|
|
|
|
return internal_net, internal_subnet
|
|
|
|
def _get_edge_internal_ip(self, context, rtr_id):
|
|
filters = {
|
|
'network_id': [self.internal_net],
|
|
'device_id': [rtr_id]}
|
|
ports = self.nsxv_plugin.get_ports(context, filters=filters)
|
|
if ports:
|
|
return ports[0]['fixed_ips'][0]['ip_address']
|
|
else:
|
|
LOG.error(_LE("No port found for metadata for %s"), rtr_id)
|
|
|
|
def _get_edge_rtr_id_by_ext_ip(self, context, edge_ip):
|
|
rtr_list = nsxv_db.get_nsxv_internal_edge(
|
|
context.session, edge_ip)
|
|
if rtr_list:
|
|
return rtr_list[0]['router_id']
|
|
|
|
def _get_edge_id_by_rtr_id(self, context, rtr_id):
|
|
binding = nsxv_db.get_nsxv_router_binding(
|
|
context.session,
|
|
rtr_id)
|
|
|
|
if binding:
|
|
return binding['edge_id']
|
|
|
|
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')
|
|
raise nsxv_exc.NsxPluginException(err_msg=error)
|
|
|
|
pool = eventlet.GreenPool(min(MAX_INIT_THREADS,
|
|
len(cfg.CONF.nsxv.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))):
|
|
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)))
|
|
edge_ip_to_set = (
|
|
list(set(cfg.CONF.nsxv.mgt_net_proxy_ips) - set(db_edge_ips)))
|
|
|
|
if edge_to_convert_ips:
|
|
if cfg.CONF.nsxv.metadata_initializer:
|
|
for edge_inner_ip in pool.imap(
|
|
self._setup_proxy_edge_external_interface_ip,
|
|
zip(edge_to_convert_ips, edge_ip_to_set)):
|
|
proxy_edge_ips.append(edge_inner_ip)
|
|
else:
|
|
error = _('Metadata initialization is incomplete on '
|
|
'initializer node')
|
|
raise nsxv_exc.NsxPluginException(err_msg=error)
|
|
|
|
# Edges that exist in the CFG list but do not have a matching DB
|
|
# element will be created.
|
|
remaining_cfg_ips = edge_ip_to_set[len(edge_to_convert_ips):]
|
|
if remaining_cfg_ips:
|
|
if cfg.CONF.nsxv.metadata_initializer:
|
|
for edge_inner_ip in pool.imap(
|
|
self._setup_new_proxy_edge, remaining_cfg_ips):
|
|
proxy_edge_ips.append(edge_inner_ip)
|
|
|
|
pool.waitall()
|
|
else:
|
|
error = _('Metadata initialization is incomplete on '
|
|
'initializer node')
|
|
raise nsxv_exc.NsxPluginException(err_msg=error)
|
|
|
|
return proxy_edge_ips
|
|
|
|
def _setup_proxy_edge_route_and_connectivity(self, rtr_ext_ip,
|
|
rtr_id=None, edge_id=None):
|
|
# Use separate context per each as we use this in tread context
|
|
context = neutron_context.get_admin_context()
|
|
if not rtr_id:
|
|
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)
|
|
|
|
# Read and validate DGW. If different, replace with new value
|
|
try:
|
|
# This may fail if the edge was deleted on backend
|
|
h, routes = self.nsxv_plugin.nsx_v.vcns.get_routes(edge_id)
|
|
except exceptions.ResourceNotFound as e:
|
|
# log this error and return without the ip, but don't fail
|
|
LOG.error(_LE("Failed to get routes for metadata proxy edge "
|
|
"%(edge)s: %(err)s"),
|
|
{'edge': edge_id, 'err': e})
|
|
return
|
|
|
|
dgw = routes.get('defaultRoute', {}).get('gatewayAddress')
|
|
|
|
if dgw != cfg.CONF.nsxv.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)
|
|
else:
|
|
error = _('Metadata initialization is incomplete on '
|
|
'initializer node')
|
|
raise nsxv_exc.NsxPluginException(err_msg=error)
|
|
|
|
# Read and validate connectivity
|
|
h, if_data = self.nsxv_plugin.nsx_v.get_interface(
|
|
edge_id, vcns_const.EXTERNAL_VNIC_INDEX)
|
|
cur_ip = if_data.get('addressGroups', {}
|
|
).get('addressGroups', {}
|
|
)[0].get('primaryAddress')
|
|
cur_pgroup = if_data['portgroupId']
|
|
if (if_data and cur_pgroup != cfg.CONF.nsxv.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,
|
|
address=rtr_ext_ip,
|
|
netmask=cfg.CONF.nsxv.mgt_net_proxy_netmask,
|
|
secondary=[])
|
|
else:
|
|
error = _('Metadata initialization is incomplete on '
|
|
'initializer node')
|
|
raise nsxv_exc.NsxPluginException(err_msg=error)
|
|
|
|
# Read and validate LB pool member configuration
|
|
# When the Nova IP address is changed in the ini file, we should apply
|
|
# this change to the LB pool
|
|
lb_obj = nsxv_lb.NsxvLoadbalancer.get_loadbalancer(
|
|
self.nsxv_plugin.nsx_v.vcns, edge_id)
|
|
|
|
vs = lb_obj.virtual_servers.get(METADATA_VSE_NAME)
|
|
if vs:
|
|
md_members = {member.payload['ipAddress']: member.payload['name']
|
|
for member in vs.default_pool.members.values()}
|
|
|
|
if len(cfg.CONF.nsxv.nova_metadata_ips) == len(md_members):
|
|
m_ips = md_members.keys()
|
|
m_to_convert = (list(set(m_ips) -
|
|
set(cfg.CONF.nsxv.nova_metadata_ips)))
|
|
m_ip_to_set = (list(set(cfg.CONF.nsxv.nova_metadata_ips)
|
|
- set(m_ips)))
|
|
|
|
for m_ip in m_to_convert:
|
|
m_name = md_members[m_ip]
|
|
vs.default_pool.members[m_name].payload['ipAddress'] = (
|
|
m_ip_to_set.pop())
|
|
else:
|
|
error = _('Number of metadata members should not change')
|
|
raise nsxv_exc.NsxPluginException(err_msg=error)
|
|
|
|
try:
|
|
# This may fail if the edge is powered off right now
|
|
lb_obj.submit_to_backend(self.nsxv_plugin.nsx_v.vcns, edge_id)
|
|
except exceptions.RequestBad as e:
|
|
# log the error and continue
|
|
LOG.error(_LE("Failed to update load balancer on metadata "
|
|
"proxy edge %(edge)s: %(err)s"),
|
|
{'edge': edge_id, 'err': e})
|
|
|
|
edge_ip = self._get_edge_internal_ip(context, rtr_id)
|
|
|
|
if edge_ip:
|
|
return edge_ip
|
|
|
|
def _setup_proxy_edge_external_interface_ip(self, rtr_ext_ips):
|
|
# Use separate context per each as we use this in tread context
|
|
context = neutron_context.get_admin_context()
|
|
|
|
rtr_old_ext_ip, rtr_new_ext_ip = rtr_ext_ips
|
|
|
|
rtr_id = self._get_edge_rtr_id_by_ext_ip(context, rtr_old_ext_ip)
|
|
edge_id = self._get_edge_id_by_rtr_id(context, rtr_id)
|
|
|
|
# Replace DB entry as we cannot update the table PK
|
|
nsxv_db.delete_nsxv_internal_edge(context.session, rtr_old_ext_ip)
|
|
|
|
edge_ip = self._setup_proxy_edge_route_and_connectivity(
|
|
rtr_new_ext_ip, rtr_id, edge_id)
|
|
|
|
nsxv_db.create_nsxv_internal_edge(
|
|
context.session, rtr_new_ext_ip,
|
|
vcns_const.InternalEdgePurposes.INTER_EDGE_PURPOSE, rtr_id)
|
|
|
|
if edge_ip:
|
|
return edge_ip
|
|
|
|
def _setup_new_proxy_edge(self, rtr_ext_ip):
|
|
# Use separate context per each as we use this in tread context
|
|
context = neutron_context.get_admin_context()
|
|
|
|
rtr_id = None
|
|
try:
|
|
router_data = {
|
|
'router': {
|
|
'name': 'metadata_proxy_router',
|
|
'admin_state_up': True,
|
|
'router_type': 'exclusive',
|
|
'tenant_id': None}}
|
|
|
|
rtr = self.nsxv_plugin.create_router(
|
|
context,
|
|
router_data,
|
|
allow_metadata=False)
|
|
|
|
rtr_id = rtr['id']
|
|
edge_id = self._get_edge_id_by_rtr_id(context, rtr_id)
|
|
|
|
self.nsxv_plugin.nsx_v.update_interface(
|
|
rtr['id'],
|
|
edge_id,
|
|
vcns_const.EXTERNAL_VNIC_INDEX,
|
|
cfg.CONF.nsxv.mgt_net_moid,
|
|
address=rtr_ext_ip,
|
|
netmask=cfg.CONF.nsxv.mgt_net_proxy_netmask,
|
|
secondary=[])
|
|
|
|
port_data = {
|
|
'port': {
|
|
'network_id': self.internal_net,
|
|
'name': None,
|
|
'admin_state_up': True,
|
|
'device_id': rtr_id,
|
|
'device_owner': constants.DEVICE_OWNER_ROUTER_INTF,
|
|
'fixed_ips': constants.ATTR_NOT_SPECIFIED,
|
|
'mac_address': constants.ATTR_NOT_SPECIFIED,
|
|
'port_security_enabled': False,
|
|
'tenant_id': None}}
|
|
|
|
port = self.nsxv_plugin.create_port(context, port_data)
|
|
|
|
address_groups = self._get_address_groups(
|
|
context, self.internal_net, rtr_id, is_proxy=True)
|
|
|
|
edge_ip = port['fixed_ips'][0]['ip_address']
|
|
edge_utils.update_internal_interface(
|
|
self.nsxv_plugin.nsx_v, context, rtr_id,
|
|
self.internal_net, address_groups)
|
|
|
|
self._setup_metadata_lb(rtr_id,
|
|
port['fixed_ips'][0]['ip_address'],
|
|
cfg.CONF.nsxv.nova_metadata_port,
|
|
cfg.CONF.nsxv.nova_metadata_port,
|
|
cfg.CONF.nsxv.nova_metadata_ips,
|
|
proxy_lb=True)
|
|
|
|
firewall_rules = [
|
|
DEFAULT_EDGE_FIREWALL_RULE,
|
|
{
|
|
'action': 'allow',
|
|
'enabled': True,
|
|
'source_ip_address': [INTERNAL_SUBNET]}]
|
|
|
|
edge_utils.update_firewall(
|
|
self.nsxv_plugin.nsx_v,
|
|
context,
|
|
rtr_id,
|
|
{'firewall_rule_list': firewall_rules},
|
|
allow_external=False)
|
|
|
|
if cfg.CONF.nsxv.mgt_net_default_gateway:
|
|
self.nsxv_plugin._update_routes(
|
|
context, rtr_id,
|
|
cfg.CONF.nsxv.mgt_net_default_gateway)
|
|
|
|
nsxv_db.create_nsxv_internal_edge(
|
|
context.session, rtr_ext_ip,
|
|
vcns_const.InternalEdgePurposes.INTER_EDGE_PURPOSE, rtr_id)
|
|
|
|
return edge_ip
|
|
|
|
except Exception as e:
|
|
LOG.exception(_LE("Exception %s while creating internal edge "
|
|
"for metadata service"), e)
|
|
|
|
ports = self.nsxv_plugin.get_ports(
|
|
context, filters={'device_id': [rtr_id]})
|
|
|
|
for port in ports:
|
|
self.nsxv_plugin.delete_port(context, port['id'],
|
|
l3_port_check=True,
|
|
nw_gw_port_check=True)
|
|
|
|
nsxv_db.delete_nsxv_internal_edge(
|
|
context.session,
|
|
rtr_ext_ip)
|
|
|
|
if rtr_id:
|
|
self.nsxv_plugin.delete_router(context, rtr_id)
|
|
|
|
def _get_address_groups(self, context, network_id, device_id, is_proxy):
|
|
|
|
filters = {'network_id': [network_id],
|
|
'device_id': [device_id]}
|
|
ports = self.nsxv_plugin.get_ports(context, filters=filters)
|
|
|
|
subnets = self.nsxv_plugin.get_subnets(context, filters=filters)
|
|
|
|
address_groups = []
|
|
for subnet in subnets:
|
|
address_group = {}
|
|
net = netaddr.IPNetwork(subnet['cidr'])
|
|
address_group['subnetMask'] = str(net.netmask)
|
|
address_group['subnetPrefixLength'] = str(net.prefixlen)
|
|
for port in ports:
|
|
fixed_ips = port['fixed_ips']
|
|
for fip in fixed_ips:
|
|
s_id = fip['subnet_id']
|
|
ip_addr = fip['ip_address']
|
|
if s_id == subnet['id'] and netaddr.valid_ipv4(ip_addr):
|
|
address_group['primaryAddress'] = ip_addr
|
|
break
|
|
|
|
# For Edge appliances which aren't the metadata proxy Edge
|
|
# we add the metadata IP address
|
|
if not is_proxy and network_id == self.internal_net:
|
|
address_group['secondaryAddresses'] = {
|
|
'type': 'secondary_addresses',
|
|
'ipAddress': [METADATA_IP_ADDR]}
|
|
|
|
address_groups.append(address_group)
|
|
return address_groups
|
|
|
|
def _create_ssl_cert(self, edge_id=None):
|
|
# Create a self signed certificate in the backend if both Cert details
|
|
# and private key are not supplied in nsx.ini
|
|
if (not cfg.CONF.nsxv.metadata_nova_client_cert and
|
|
not cfg.CONF.nsxv.metadata_nova_client_priv_key):
|
|
h = self.nsxv_plugin.nsx_v.vcns.create_csr(edge_id)[0]
|
|
# Extract the CSR ID from header
|
|
csr_id = lbaas_common.extract_resource_id(h['location'])
|
|
# Create a self signed certificate
|
|
cert = self.nsxv_plugin.nsx_v.vcns.create_csr_cert(csr_id)[1]
|
|
cert_id = cert['objectId']
|
|
else:
|
|
# Raise an error if either the Cert path or the private key is not
|
|
# configured
|
|
error = None
|
|
if not cfg.CONF.nsxv.metadata_nova_client_cert:
|
|
error = _('Metadata certificate path not configured')
|
|
elif not cfg.CONF.nsxv.metadata_nova_client_priv_key:
|
|
error = _('Metadata client private key not configured')
|
|
if error:
|
|
raise nsxv_exc.NsxPluginException(err_msg=error)
|
|
pem_encoding = utils.read_file(
|
|
cfg.CONF.nsxv.metadata_nova_client_cert)
|
|
priv_key = utils.read_file(
|
|
cfg.CONF.nsxv.metadata_nova_client_priv_key)
|
|
request = {
|
|
'pemEncoding': pem_encoding,
|
|
'privateKey': priv_key}
|
|
cert = self.nsxv_plugin.nsx_v.vcns.upload_edge_certificate(
|
|
edge_id, request)[1]
|
|
cert_id = cert.get('certificates')[0]['objectId']
|
|
return cert_id
|
|
|
|
def _setup_metadata_lb(self, rtr_id, vip, v_port, s_port, member_ips,
|
|
proxy_lb=False, context=None):
|
|
|
|
if context is None:
|
|
context = neutron_context.get_admin_context()
|
|
|
|
edge_id = self._get_edge_id_by_rtr_id(context, rtr_id)
|
|
LOG.debug('Setting up Edge device %s', edge_id)
|
|
|
|
lb_obj = nsxv_lb.NsxvLoadbalancer()
|
|
|
|
protocol = 'HTTP'
|
|
ssl_pass_through = False
|
|
cert_id = None
|
|
# Set protocol to HTTPS with default port of 443 if metadata_insecure
|
|
# is set to False.
|
|
if not cfg.CONF.nsxv.metadata_insecure:
|
|
protocol = 'HTTPS'
|
|
if proxy_lb:
|
|
v_port = METADATA_HTTPS_VIP_PORT
|
|
else:
|
|
v_port = METADATA_HTTPS_PORT
|
|
# Create the certificate on the backend
|
|
cert_id = self._create_ssl_cert(edge_id)
|
|
ssl_pass_through = proxy_lb
|
|
mon_type = protocol if proxy_lb else 'tcp'
|
|
# Create virtual server
|
|
virt_srvr = nsxv_lb.NsxvLBVirtualServer(
|
|
name=METADATA_VSE_NAME,
|
|
ip_address=vip,
|
|
protocol=protocol,
|
|
port=v_port)
|
|
|
|
# For router Edge, we add X-LB-Proxy-ID header
|
|
if not proxy_lb:
|
|
md_app_rule = nsxv_lb.NsxvLBAppRule(
|
|
'insert-mdp',
|
|
'reqadd X-Metadata-Provider:' + edge_id)
|
|
virt_srvr.add_app_rule(md_app_rule)
|
|
|
|
# When shared proxy is configured, insert authentication string
|
|
if cfg.CONF.nsxv.metadata_shared_secret:
|
|
signature = hmac.new(
|
|
cfg.CONF.nsxv.metadata_shared_secret,
|
|
edge_id,
|
|
hashlib.sha256).hexdigest()
|
|
sign_app_rule = nsxv_lb.NsxvLBAppRule(
|
|
'insert-auth',
|
|
'reqadd X-Metadata-Provider-Signature:' + signature)
|
|
virt_srvr.add_app_rule(sign_app_rule)
|
|
|
|
# Create app profile
|
|
# XFF is inserted in router LBs
|
|
app_profile = nsxv_lb.NsxvLBAppProfile(
|
|
name='MDSrvProxy',
|
|
template=protocol,
|
|
server_ssl_enabled=not cfg.CONF.nsxv.metadata_insecure,
|
|
ssl_pass_through=ssl_pass_through,
|
|
insert_xff=not proxy_lb,
|
|
client_ssl_cert=cert_id)
|
|
|
|
virt_srvr.set_app_profile(app_profile)
|
|
|
|
# Create pool, members and monitor
|
|
pool = nsxv_lb.NsxvLBPool(
|
|
name=METADATA_POOL_NAME)
|
|
|
|
monitor = nsxv_lb.NsxvLBMonitor(name='MDSrvMon',
|
|
mon_type=mon_type.lower())
|
|
pool.add_monitor(monitor)
|
|
|
|
i = 0
|
|
for member_ip in member_ips:
|
|
i += 1
|
|
member = nsxv_lb.NsxvLBPoolMember(
|
|
name='Member-%d' % i,
|
|
ip_address=member_ip,
|
|
port=s_port,
|
|
monitor_port=s_port)
|
|
pool.add_member(member)
|
|
|
|
virt_srvr.set_default_pool(pool)
|
|
lb_obj.add_virtual_server(virt_srvr)
|
|
|
|
lb_obj.submit_to_backend(self.nsxv_plugin.nsx_v.vcns, edge_id)
|
|
|
|
def configure_router_edge(self, rtr_id, context):
|
|
ctx = context.elevated()
|
|
# Connect router interface to inter-edge network
|
|
port_data = {
|
|
'port': {
|
|
'network_id': self.internal_net,
|
|
'name': None,
|
|
'admin_state_up': True,
|
|
'device_id': rtr_id,
|
|
'device_owner': constants.DEVICE_OWNER_ROUTER_GW,
|
|
'fixed_ips': constants.ATTR_NOT_SPECIFIED,
|
|
'mac_address': constants.ATTR_NOT_SPECIFIED,
|
|
'port_security_enabled': False,
|
|
'tenant_id': None}}
|
|
|
|
self.nsxv_plugin.create_port(ctx, port_data)
|
|
|
|
address_groups = self._get_address_groups(
|
|
ctx,
|
|
self.internal_net,
|
|
rtr_id,
|
|
is_proxy=False)
|
|
|
|
if context is None:
|
|
context = self.context
|
|
|
|
edge_utils.update_internal_interface(
|
|
self.nsxv_plugin.nsx_v,
|
|
context,
|
|
rtr_id,
|
|
self.internal_net,
|
|
address_groups=address_groups)
|
|
|
|
self._setup_metadata_lb(rtr_id,
|
|
METADATA_IP_ADDR,
|
|
METADATA_TCP_PORT,
|
|
cfg.CONF.nsxv.nova_metadata_port,
|
|
self.proxy_edge_ips,
|
|
proxy_lb=False,
|
|
context=context)
|
|
|
|
def cleanup_router_edge(self, context, rtr_id, warn=False):
|
|
filters = {
|
|
'network_id': [self.internal_net],
|
|
'device_id': [rtr_id]}
|
|
ports = self.nsxv_plugin.get_ports(context, filters=filters)
|
|
|
|
if ports:
|
|
if warn:
|
|
LOG.warning(_LW("cleanup_router_edge found port %(port)s for "
|
|
"router %(router)s - deleting it now."),
|
|
{'port': ports[0]['id'], 'router': rtr_id})
|
|
try:
|
|
self.nsxv_plugin.delete_port(
|
|
context, ports[0]['id'],
|
|
l3_port_check=False)
|
|
except Exception as e:
|
|
LOG.error(_LE("Failed to delete md_proxy port %(port)s: "
|
|
"%(e)s"), {'port': ports[0]['id'], 'e': e})
|