dd43e222e3
- When doing a deletion is a async task we create a new context to deal with that deletion - We add some additional logs Change-Id: I9a707d1292858428ab51295167360bfa26513430
852 lines
34 KiB
Python
852 lines
34 KiB
Python
# Copyright 2013 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.
|
|
|
|
from distutils import version
|
|
import random
|
|
import time
|
|
|
|
from neutron_lib import constants as lib_const
|
|
from neutron_lib import context as q_context
|
|
from oslo_config import cfg
|
|
from oslo_log import log as logging
|
|
from oslo_serialization import jsonutils
|
|
from oslo_utils import excutils
|
|
from sqlalchemy.orm import exc as sa_exc
|
|
|
|
from vmware_nsx._i18n import _
|
|
from vmware_nsx.common import exceptions as nsxv_exc
|
|
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.common import constants
|
|
from vmware_nsx.plugins.nsx_v.vshield.common import exceptions
|
|
from vmware_nsx.plugins.nsx_v.vshield import edge_utils
|
|
from vmware_nsx.plugins.nsx_v.vshield.tasks import (
|
|
constants as task_constants)
|
|
from vmware_nsx.plugins.nsx_v.vshield.tasks import tasks
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class EdgeApplianceDriver(object):
|
|
def __init__(self):
|
|
super(EdgeApplianceDriver, self).__init__()
|
|
# store the last task per edge that has the latest config
|
|
self.updated_task = {
|
|
'nat': {},
|
|
'route': {},
|
|
}
|
|
random.seed()
|
|
|
|
def _assemble_edge(self, name, appliance_size="compact",
|
|
deployment_container_id=None, datacenter_moid=None,
|
|
enable_aesni=True, dist=False,
|
|
enable_fips=False, remote_access=False,
|
|
edge_ha=False):
|
|
edge = {
|
|
'name': name,
|
|
'fqdn': None,
|
|
'enableAesni': enable_aesni,
|
|
'enableFips': enable_fips,
|
|
'featureConfigs': {
|
|
'features': [
|
|
{
|
|
'featureType': 'firewall_4.0',
|
|
'globalConfig': {
|
|
'tcpTimeoutEstablished': 7200
|
|
}
|
|
}
|
|
]
|
|
},
|
|
'cliSettings': {
|
|
'remoteAccess': remote_access
|
|
},
|
|
'autoConfiguration': {
|
|
'enabled': False,
|
|
'rulePriority': 'high'
|
|
},
|
|
'appliances': {
|
|
'applianceSize': appliance_size
|
|
},
|
|
}
|
|
if not dist:
|
|
edge['type'] = "gatewayServices"
|
|
edge['vnics'] = {'vnics': []}
|
|
else:
|
|
edge['type'] = "distributedRouter"
|
|
edge['interfaces'] = {'interfaces': []}
|
|
|
|
if deployment_container_id:
|
|
edge['appliances']['deploymentContainerId'] = (
|
|
deployment_container_id)
|
|
if datacenter_moid:
|
|
edge['datacenterMoid'] = datacenter_moid
|
|
|
|
if not dist and edge_ha:
|
|
self._enable_high_availability(edge)
|
|
|
|
return edge
|
|
|
|
def _select_datastores(self, availability_zone):
|
|
primary_ds = availability_zone.datastore_id
|
|
secondary_ds = availability_zone.ha_datastore_id
|
|
if availability_zone.ha_placement_random:
|
|
# we want to switch primary and secondary datastores
|
|
# half of the times, to balance it
|
|
if random.random() > 0.5:
|
|
primary_ds = availability_zone.ha_datastore_id
|
|
secondary_ds = availability_zone.datastore_id
|
|
return primary_ds, secondary_ds
|
|
|
|
def _assemble_edge_appliances(self, availability_zone):
|
|
appliances = []
|
|
if availability_zone.ha_datastore_id and availability_zone.edge_ha:
|
|
# create appliance with HA
|
|
primary_ds, secondary_ds = self._select_datastores(
|
|
availability_zone)
|
|
appliances.append(self._assemble_edge_appliance(
|
|
availability_zone.resource_pool,
|
|
primary_ds))
|
|
appliances.append(self._assemble_edge_appliance(
|
|
availability_zone.resource_pool,
|
|
secondary_ds))
|
|
elif availability_zone.datastore_id:
|
|
# Single datastore
|
|
appliances.append(self._assemble_edge_appliance(
|
|
availability_zone.resource_pool,
|
|
availability_zone.datastore_id))
|
|
return appliances
|
|
|
|
def _assemble_edge_appliance(self, resource_pool_id, datastore_id):
|
|
appliance = {}
|
|
if resource_pool_id:
|
|
appliance['resourcePoolId'] = resource_pool_id
|
|
if datastore_id:
|
|
appliance['datastoreId'] = datastore_id
|
|
return appliance
|
|
|
|
def _assemble_edge_vnic(self, name, index, portgroup_id, tunnel_index=-1,
|
|
primary_address=None, subnet_mask=None,
|
|
secondary=None,
|
|
type="internal",
|
|
enable_proxy_arp=False,
|
|
enable_send_redirects=True,
|
|
is_connected=True,
|
|
mtu=1500,
|
|
address_groups=None):
|
|
vnic = {
|
|
'index': index,
|
|
'name': name,
|
|
'type': type,
|
|
'portgroupId': portgroup_id,
|
|
'mtu': mtu,
|
|
'enableProxyArp': enable_proxy_arp,
|
|
'enableSendRedirects': enable_send_redirects,
|
|
'isConnected': is_connected
|
|
}
|
|
if address_groups is None:
|
|
address_groups = []
|
|
if not address_groups:
|
|
if primary_address and subnet_mask:
|
|
address_group = {
|
|
'primaryAddress': primary_address,
|
|
'subnetMask': subnet_mask
|
|
}
|
|
if secondary:
|
|
address_group['secondaryAddresses'] = {
|
|
'ipAddress': secondary,
|
|
'type': 'secondary_addresses'
|
|
}
|
|
|
|
vnic['addressGroups'] = {
|
|
'addressGroups': [address_group]
|
|
}
|
|
else:
|
|
vnic['subInterfaces'] = {'subInterfaces': address_groups}
|
|
else:
|
|
if tunnel_index < 0:
|
|
vnic['addressGroups'] = {'addressGroups': address_groups}
|
|
else:
|
|
vnic['subInterfaces'] = {'subInterfaces': address_groups}
|
|
|
|
return vnic
|
|
|
|
def _assemble_vdr_interface(self, portgroup_id,
|
|
primary_address=None, subnet_mask=None,
|
|
secondary=None,
|
|
type="internal",
|
|
is_connected=True,
|
|
mtu=1500,
|
|
address_groups=None):
|
|
interface = {
|
|
'type': type,
|
|
'connectedToId': portgroup_id,
|
|
'mtu': mtu,
|
|
'isConnected': is_connected
|
|
}
|
|
if address_groups is None:
|
|
address_groups = []
|
|
if not address_groups:
|
|
if primary_address and subnet_mask:
|
|
address_group = {
|
|
'primaryAddress': primary_address,
|
|
'subnetMask': subnet_mask
|
|
}
|
|
if secondary:
|
|
address_group['secondaryAddresses'] = {
|
|
'ipAddress': secondary,
|
|
'type': 'secondary_addresses'
|
|
}
|
|
|
|
interface['addressGroups'] = {
|
|
'addressGroups': [address_group]
|
|
}
|
|
else:
|
|
interface['addressGroups'] = {'addressGroups': address_groups}
|
|
interfaces = {'interfaces': [interface]}
|
|
|
|
return interfaces
|
|
|
|
def _edge_status_to_level(self, status):
|
|
if status == 'GREEN':
|
|
status_level = constants.RouterStatus.ROUTER_STATUS_ACTIVE
|
|
elif status in ('GREY', 'YELLOW'):
|
|
status_level = constants.RouterStatus.ROUTER_STATUS_DOWN
|
|
else:
|
|
status_level = constants.RouterStatus.ROUTER_STATUS_ERROR
|
|
return status_level
|
|
|
|
def _enable_loadbalancer(self, edge):
|
|
if (not edge.get('featureConfigs') or
|
|
not edge['featureConfigs'].get('features')):
|
|
edge['featureConfigs'] = {'features': []}
|
|
edge['featureConfigs']['features'].append(
|
|
{'featureType': 'loadbalancer_4.0',
|
|
'enabled': True})
|
|
|
|
def _enable_high_availability(self, edge):
|
|
if (not edge.get('featureConfigs') or
|
|
not edge['featureConfigs'].get('features')):
|
|
edge['featureConfigs'] = {'features': []}
|
|
edge['featureConfigs']['features'].append(
|
|
{'featureType': 'highavailability_4.0',
|
|
'enabled': True})
|
|
|
|
def get_edge_status(self, edge_id):
|
|
try:
|
|
response = self.vcns.get_edge_status(edge_id)[1]
|
|
status_level = self._edge_status_to_level(
|
|
response['edgeStatus'])
|
|
except exceptions.VcnsApiException as e:
|
|
LOG.error("VCNS: Failed to get edge %(edge_id)s status: "
|
|
"Reason: %(reason)s",
|
|
{'edge_id': edge_id, 'reason': e.response})
|
|
status_level = constants.RouterStatus.ROUTER_STATUS_ERROR
|
|
try:
|
|
desc = jsonutils.loads(e.response)
|
|
if desc.get('errorCode') == (
|
|
constants.VCNS_ERROR_CODE_EDGE_NOT_RUNNING):
|
|
status_level = constants.RouterStatus.ROUTER_STATUS_DOWN
|
|
except ValueError:
|
|
LOG.error('Error code not present. %s', e.response)
|
|
|
|
return status_level
|
|
|
|
def get_interface(self, edge_id, vnic_index):
|
|
# get vnic interface address groups
|
|
try:
|
|
return self.vcns.query_interface(edge_id, vnic_index)
|
|
except exceptions.VcnsApiException:
|
|
with excutils.save_and_reraise_exception():
|
|
LOG.exception("NSXv: Failed to query vnic %s", vnic_index)
|
|
|
|
def update_interface(self, router_id, edge_id, index, network,
|
|
tunnel_index=-1, address=None, netmask=None,
|
|
secondary=None, is_connected=True,
|
|
address_groups=None):
|
|
LOG.debug("VCNS: update vnic %(index)d: %(addr)s %(netmask)s", {
|
|
'index': index, 'addr': address, 'netmask': netmask})
|
|
if index == constants.EXTERNAL_VNIC_INDEX:
|
|
name = constants.EXTERNAL_VNIC_NAME
|
|
intf_type = 'uplink'
|
|
else:
|
|
name = constants.INTERNAL_VNIC_NAME + str(index)
|
|
if tunnel_index < 0:
|
|
intf_type = 'internal'
|
|
else:
|
|
intf_type = 'trunk'
|
|
|
|
config = self._assemble_edge_vnic(
|
|
name, index, network, tunnel_index,
|
|
address, netmask, secondary, type=intf_type,
|
|
address_groups=address_groups, is_connected=is_connected)
|
|
|
|
self.vcns.update_interface(edge_id, config)
|
|
|
|
def add_vdr_internal_interface(self, edge_id,
|
|
network, address=None, netmask=None,
|
|
secondary=None, address_groups=None,
|
|
type="internal", is_connected=True):
|
|
LOG.debug("Add VDR interface on edge: %s", edge_id)
|
|
if address_groups is None:
|
|
address_groups = []
|
|
interface_req = (
|
|
self._assemble_vdr_interface(network, address, netmask, secondary,
|
|
address_groups=address_groups,
|
|
is_connected=is_connected, type=type))
|
|
self.vcns.add_vdr_internal_interface(edge_id, interface_req)
|
|
header, response = self.vcns.get_edge_interfaces(edge_id)
|
|
for interface in response['interfaces']:
|
|
if interface['connectedToId'] == network:
|
|
vnic_index = int(interface['index'])
|
|
return vnic_index
|
|
|
|
def update_vdr_internal_interface(self, edge_id, index, network,
|
|
address_groups=None, is_connected=True):
|
|
if not address_groups:
|
|
address_groups = []
|
|
interface = {
|
|
'type': 'internal',
|
|
'connectedToId': network,
|
|
'mtu': 1500,
|
|
'isConnected': is_connected,
|
|
'addressGroups': {'addressGroup': address_groups}
|
|
}
|
|
interface_req = {'interface': interface}
|
|
try:
|
|
header, response = self.vcns.update_vdr_internal_interface(
|
|
edge_id, index, interface_req)
|
|
except exceptions.VcnsApiException:
|
|
with excutils.save_and_reraise_exception():
|
|
LOG.exception("Failed to update vdr interface on edge: "
|
|
"%s", edge_id)
|
|
|
|
def delete_vdr_internal_interface(self, edge_id, interface_index):
|
|
LOG.debug("Delete VDR interface on edge: %s", edge_id)
|
|
try:
|
|
header, response = self.vcns.delete_vdr_internal_interface(
|
|
edge_id, interface_index)
|
|
except exceptions.VcnsApiException:
|
|
with excutils.save_and_reraise_exception():
|
|
LOG.exception("Failed to delete vdr interface on edge: "
|
|
"%s",
|
|
edge_id)
|
|
|
|
def delete_interface(self, router_id, edge_id, index):
|
|
LOG.debug("Deleting vnic %(vnic_index)s: on edge %(edge_id)s",
|
|
{'vnic_index': index, 'edge_id': edge_id})
|
|
try:
|
|
self.vcns.delete_interface(edge_id, index)
|
|
except exceptions.ResourceNotFound:
|
|
LOG.error('Failed to delete vnic %(vnic_index)s on edge '
|
|
'%(edge_id)s: edge was not found',
|
|
{'vnic_index': index,
|
|
'edge_id': edge_id})
|
|
except exceptions.VcnsApiException:
|
|
with excutils.save_and_reraise_exception():
|
|
LOG.exception("Failed to delete vnic %(vnic_index)s: "
|
|
"on edge %(edge_id)s",
|
|
{'vnic_index': index,
|
|
'edge_id': edge_id})
|
|
|
|
LOG.debug("Deletion complete vnic %(vnic_index)s: on edge %(edge_id)s",
|
|
{'vnic_index': index, 'edge_id': edge_id})
|
|
|
|
def deploy_edge(self, context, router_id, name, internal_network,
|
|
dist=False, loadbalancer_enable=True,
|
|
appliance_size=nsxv_constants.LARGE,
|
|
availability_zone=None, deploy_metadata=False):
|
|
|
|
edge_name = name
|
|
edge = self._assemble_edge(
|
|
edge_name, datacenter_moid=availability_zone.datacenter_moid,
|
|
deployment_container_id=self.deployment_container_id,
|
|
appliance_size=appliance_size, remote_access=False, dist=dist,
|
|
edge_ha=availability_zone.edge_ha)
|
|
appliances = self._assemble_edge_appliances(availability_zone)
|
|
if appliances:
|
|
edge['appliances']['appliances'] = appliances
|
|
|
|
if not dist:
|
|
vnic_external = self._assemble_edge_vnic(
|
|
constants.EXTERNAL_VNIC_NAME, constants.EXTERNAL_VNIC_INDEX,
|
|
availability_zone.external_network, type="uplink")
|
|
edge['vnics']['vnics'].append(vnic_external)
|
|
else:
|
|
edge['mgmtInterface'] = {
|
|
'connectedToId': availability_zone.external_network,
|
|
'name': "mgmtInterface"}
|
|
if internal_network:
|
|
vnic_inside = self._assemble_edge_vnic(
|
|
constants.INTERNAL_VNIC_NAME, constants.INTERNAL_VNIC_INDEX,
|
|
internal_network,
|
|
edge_utils.get_vdr_transit_network_plr_address(),
|
|
edge_utils.get_vdr_transit_network_netmask(),
|
|
type="internal")
|
|
edge['vnics']['vnics'].append(vnic_inside)
|
|
|
|
# If default login credentials for Edge are set, configure accordingly
|
|
if (cfg.CONF.nsxv.edge_appliance_user and
|
|
cfg.CONF.nsxv.edge_appliance_password):
|
|
edge['cliSettings'].update({
|
|
'userName': cfg.CONF.nsxv.edge_appliance_user,
|
|
'password': cfg.CONF.nsxv.edge_appliance_password})
|
|
|
|
if not dist and loadbalancer_enable:
|
|
self._enable_loadbalancer(edge)
|
|
|
|
edge_id = None
|
|
try:
|
|
header = self.vcns.deploy_edge(edge)[0]
|
|
edge_id = header.get('location', '/').split('/')[-1]
|
|
|
|
if edge_id:
|
|
nsxv_db.update_nsxv_router_binding(
|
|
context.session, router_id, edge_id=edge_id)
|
|
if not dist:
|
|
# Init Edge vnic binding
|
|
nsxv_db.init_edge_vnic_binding(
|
|
context.session, edge_id)
|
|
else:
|
|
if router_id:
|
|
nsxv_db.update_nsxv_router_binding(
|
|
context.session, router_id,
|
|
status=lib_const.ERROR)
|
|
error = _('Failed to deploy edge')
|
|
raise nsxv_exc.NsxPluginException(err_msg=error)
|
|
|
|
self.callbacks.complete_edge_creation(
|
|
context, edge_id, name, router_id, dist, True,
|
|
availability_zone=availability_zone,
|
|
deploy_metadata=deploy_metadata)
|
|
|
|
except exceptions.VcnsApiException:
|
|
self.callbacks.complete_edge_creation(
|
|
context, edge_id, name, router_id, dist, False,
|
|
availability_zone=availability_zone)
|
|
with excutils.save_and_reraise_exception():
|
|
LOG.exception("NSXv: deploy edge failed.")
|
|
return edge_id
|
|
|
|
def update_edge(self, context, router_id, edge_id, name, internal_network,
|
|
dist=False, loadbalancer_enable=True,
|
|
appliance_size=nsxv_constants.LARGE,
|
|
set_errors=False, availability_zone=None):
|
|
"""Update edge name."""
|
|
edge = self._assemble_edge(
|
|
name, datacenter_moid=availability_zone.datacenter_moid,
|
|
deployment_container_id=self.deployment_container_id,
|
|
appliance_size=appliance_size, remote_access=False, dist=dist,
|
|
edge_ha=availability_zone.edge_ha)
|
|
edge['id'] = edge_id
|
|
appliances = self._assemble_edge_appliances(availability_zone)
|
|
if appliances:
|
|
edge['appliances']['appliances'] = appliances
|
|
|
|
if not dist:
|
|
vnic_external = self._assemble_edge_vnic(
|
|
constants.EXTERNAL_VNIC_NAME, constants.EXTERNAL_VNIC_INDEX,
|
|
availability_zone.external_network, type="uplink")
|
|
edge['vnics']['vnics'].append(vnic_external)
|
|
else:
|
|
edge['mgmtInterface'] = {
|
|
'connectedToId': availability_zone.external_network,
|
|
'name': "mgmtInterface"}
|
|
|
|
if internal_network:
|
|
internal_vnic = self._assemble_edge_vnic(
|
|
constants.INTERNAL_VNIC_NAME, constants.INTERNAL_VNIC_INDEX,
|
|
internal_network,
|
|
edge_utils.get_vdr_transit_network_plr_address(),
|
|
edge_utils.get_vdr_transit_network_netmask(),
|
|
type="internal")
|
|
edge['vnics']['vnics'].append(internal_vnic)
|
|
if not dist and loadbalancer_enable:
|
|
self._enable_loadbalancer(edge)
|
|
|
|
try:
|
|
self.vcns.update_edge(edge_id, edge)
|
|
self.callbacks.complete_edge_update(
|
|
context, edge_id, router_id, True, set_errors)
|
|
except exceptions.VcnsApiException as e:
|
|
LOG.error("Failed to update edge: %s",
|
|
e.response)
|
|
self.callbacks.complete_edge_update(
|
|
context, edge_id, router_id, False, set_errors)
|
|
return False
|
|
|
|
return True
|
|
|
|
def rename_edge(self, edge_id, name):
|
|
"""rename edge."""
|
|
try:
|
|
# First get the current edge structure
|
|
# [0] is the status, [1] is the body
|
|
edge = self.vcns.get_edge(edge_id)[1]
|
|
if edge['name'] == name:
|
|
LOG.debug('Edge %s is already named %s', edge_id, name)
|
|
return
|
|
# remove some data that will make the update fail
|
|
edge_utils.remove_irrelevant_keys_from_edge_request(edge)
|
|
# set the new name in the request
|
|
edge['name'] = name
|
|
# update the edge
|
|
self.vcns.update_edge(edge_id, edge)
|
|
except exceptions.VcnsApiException as e:
|
|
LOG.error("Failed to rename edge: %s",
|
|
e.response)
|
|
|
|
def resize_edge(self, edge_id, size):
|
|
"""update the size of a router edge."""
|
|
try:
|
|
# First get the current edge structure
|
|
# [0] is the status, [1] is the body
|
|
edge = self.vcns.get_edge(edge_id)[1]
|
|
if edge.get('appliances'):
|
|
if edge['appliances']['applianceSize'] == size:
|
|
LOG.debug('Edge %s is already with size %s',
|
|
edge_id, size)
|
|
return
|
|
ver = self.vcns.get_version()
|
|
if version.LooseVersion(ver) < version.LooseVersion('6.2.3'):
|
|
# remove some data that will make the update fail
|
|
edge_utils.remove_irrelevant_keys_from_edge_request(edge)
|
|
|
|
# set the new size in the request
|
|
edge['appliances']['applianceSize'] = size
|
|
# update the edge
|
|
self.vcns.update_edge(edge_id, edge)
|
|
except exceptions.VcnsApiException as e:
|
|
LOG.error("Failed to resize edge: %s", e.response)
|
|
|
|
def delete_edge(self, context, router_id, edge_id, dist=False):
|
|
LOG.debug("Deleting edge %s", edge_id)
|
|
if context is None:
|
|
context = q_context.get_admin_context()
|
|
try:
|
|
LOG.debug("Deleting router binding %s", router_id)
|
|
nsxv_db.delete_nsxv_router_binding(context.session, router_id)
|
|
if not dist:
|
|
LOG.debug("Deleting vnic bindings for edge %s", edge_id)
|
|
nsxv_db.clean_edge_vnic_binding(context.session, edge_id)
|
|
except sa_exc.NoResultFound:
|
|
LOG.warning("Router Binding for %s not found", router_id)
|
|
|
|
if edge_id:
|
|
try:
|
|
self.vcns.delete_edge(edge_id)
|
|
return True
|
|
except exceptions.ResourceNotFound:
|
|
return True
|
|
except exceptions.VcnsApiException as e:
|
|
LOG.exception("VCNS: Failed to delete %(edge_id)s:\n"
|
|
"%(response)s",
|
|
{'edge_id': edge_id, 'response': e.response})
|
|
return False
|
|
except Exception:
|
|
LOG.exception("VCNS: Failed to delete %s", edge_id)
|
|
return False
|
|
|
|
def _assemble_nat_rule(self, action, original_address,
|
|
translated_address,
|
|
vnic_index=None,
|
|
enabled=True,
|
|
protocol='any',
|
|
original_port='any',
|
|
translated_port='any'):
|
|
nat_rule = {}
|
|
nat_rule['action'] = action
|
|
if vnic_index is not None:
|
|
nat_rule['vnic'] = vnic_index
|
|
nat_rule['originalAddress'] = original_address
|
|
nat_rule['translatedAddress'] = translated_address
|
|
nat_rule['enabled'] = enabled
|
|
nat_rule['protocol'] = protocol
|
|
nat_rule['originalPort'] = original_port
|
|
nat_rule['translatedPort'] = translated_port
|
|
|
|
return nat_rule
|
|
|
|
def get_nat_config(self, edge_id):
|
|
try:
|
|
return self.vcns.get_nat_config(edge_id)[1]
|
|
except exceptions.VcnsApiException as e:
|
|
LOG.exception("VCNS: Failed to get nat config:\n%s",
|
|
e.response)
|
|
raise e
|
|
|
|
def update_nat_rules(self, edge_id, snats, dnats, indices=None):
|
|
LOG.debug("VCNS: update nat rule\n"
|
|
"SNAT:%(snat)s\n"
|
|
"DNAT:%(dnat)s\n"
|
|
"INDICES: %(index)s\n", {
|
|
'snat': snats, 'dnat': dnats, 'index': indices})
|
|
nat_rules = []
|
|
|
|
for dnat in dnats:
|
|
vnic_index = None
|
|
if 'vnic_index' in dnat:
|
|
vnic_index = dnat['vnic_index']
|
|
if vnic_index or not indices:
|
|
# we are adding a predefined index or
|
|
# adding to all interfaces
|
|
nat_rules.append(self._assemble_nat_rule(
|
|
'dnat', dnat['dst'], dnat['translated'],
|
|
vnic_index=vnic_index
|
|
))
|
|
nat_rules.append(self._assemble_nat_rule(
|
|
'snat', dnat['translated'], dnat['dst'],
|
|
vnic_index=vnic_index
|
|
))
|
|
else:
|
|
for index in indices:
|
|
nat_rules.append(self._assemble_nat_rule(
|
|
'dnat', dnat['dst'], dnat['translated'],
|
|
vnic_index=index
|
|
))
|
|
nat_rules.append(self._assemble_nat_rule(
|
|
'snat', dnat['translated'], dnat['dst'],
|
|
vnic_index=index
|
|
))
|
|
|
|
for snat in snats:
|
|
vnic_index = None
|
|
if 'vnic_index' in snat:
|
|
vnic_index = snat['vnic_index']
|
|
if vnic_index or not indices:
|
|
# we are adding a predefined index
|
|
# or adding to all interfaces
|
|
nat_rules.append(self._assemble_nat_rule(
|
|
'snat', snat['src'], snat['translated'],
|
|
vnic_index=vnic_index
|
|
))
|
|
else:
|
|
for index in indices:
|
|
nat_rules.append(self._assemble_nat_rule(
|
|
'snat', snat['src'], snat['translated'],
|
|
vnic_index=index
|
|
))
|
|
|
|
nat = {
|
|
'featureType': 'nat',
|
|
'rules': {
|
|
'natRulesDtos': nat_rules
|
|
}
|
|
}
|
|
|
|
try:
|
|
self.vcns.update_nat_config(edge_id, nat)
|
|
return True
|
|
except exceptions.VcnsApiException as e:
|
|
LOG.exception("VCNS: Failed to create snat rule:\n%s",
|
|
e.response)
|
|
return False
|
|
|
|
def update_routes(self, edge_id, gateway, routes):
|
|
if gateway:
|
|
gateway = gateway.split('/')[0]
|
|
|
|
static_routes = []
|
|
for route in routes:
|
|
if route.get('vnic_index') is None:
|
|
static_routes.append({
|
|
"description": "",
|
|
"vnic": constants.INTERNAL_VNIC_INDEX,
|
|
"network": route['cidr'],
|
|
"nextHop": route['nexthop']
|
|
})
|
|
else:
|
|
static_routes.append({
|
|
"description": "",
|
|
"vnic": route['vnic_index'],
|
|
"network": route['cidr'],
|
|
"nextHop": route['nexthop']
|
|
})
|
|
request = {
|
|
"staticRoutes": {
|
|
"staticRoutes": static_routes
|
|
}
|
|
}
|
|
if gateway:
|
|
request["defaultRoute"] = {
|
|
"description": "default-gateway",
|
|
"gatewayAddress": gateway
|
|
}
|
|
try:
|
|
self.vcns.update_routes(edge_id, request)
|
|
return True
|
|
except exceptions.VcnsApiException as e:
|
|
LOG.exception("VCNS: Failed to update routes:\n%s",
|
|
e.response)
|
|
return False
|
|
|
|
def create_lswitch(self, name, tz_config, tags=None,
|
|
port_isolation=False, replication_mode="service"):
|
|
lsconfig = {
|
|
'display_name': utils.check_and_truncate(name),
|
|
"tags": tags or [],
|
|
"type": "LogicalSwitchConfig",
|
|
"_schema": "/ws.v1/schema/LogicalSwitchConfig",
|
|
"transport_zones": tz_config
|
|
}
|
|
if port_isolation is bool:
|
|
lsconfig["port_isolation_enabled"] = port_isolation
|
|
if replication_mode:
|
|
lsconfig["replication_mode"] = replication_mode
|
|
|
|
response = self.vcns.create_lswitch(lsconfig)[1]
|
|
return response
|
|
|
|
def delete_lswitch(self, lswitch_id):
|
|
self.vcns.delete_lswitch(lswitch_id)
|
|
|
|
def get_loadbalancer_config(self, edge_id):
|
|
try:
|
|
header, response = self.vcns.get_loadbalancer_config(
|
|
edge_id)
|
|
except exceptions.VcnsApiException:
|
|
with excutils.save_and_reraise_exception():
|
|
LOG.exception("Failed to get service config")
|
|
return response
|
|
|
|
def enable_service_loadbalancer(self, edge_id):
|
|
config = self.get_loadbalancer_config(
|
|
edge_id)
|
|
if not config['enabled']:
|
|
config['enabled'] = True
|
|
try:
|
|
self.vcns.enable_service_loadbalancer(edge_id, config)
|
|
except exceptions.VcnsApiException:
|
|
with excutils.save_and_reraise_exception():
|
|
LOG.exception("Failed to enable loadbalancer "
|
|
"service config")
|
|
|
|
def _delete_port_group(self, task):
|
|
try:
|
|
self.vcns.delete_port_group(
|
|
task.userdata['dvs_id'],
|
|
task.userdata['port_group_id'])
|
|
except Exception as e:
|
|
LOG.error('Unable to delete %(pg)s exception %(ex)s',
|
|
{'pg': task.userdata['port_group_id'],
|
|
'ex': e})
|
|
return task_constants.TaskStatus.ERROR
|
|
return task_constants.TaskStatus.COMPLETED
|
|
|
|
def _retry_task(self, task):
|
|
delay = 0.5
|
|
max_retries = max(cfg.CONF.nsxv.retries, 1)
|
|
args = task.userdata.get('args', [])
|
|
kwargs = task.userdata.get('kwargs', {})
|
|
retry_number = task.userdata['retry_number']
|
|
retry_command = task.userdata['retry_command']
|
|
try:
|
|
retry_command(*args, **kwargs)
|
|
except Exception as exc:
|
|
LOG.debug("Task %(name)s retry %(retry)s failed %(exc)s",
|
|
{'name': task.name,
|
|
'exc': exc,
|
|
'retry': retry_number})
|
|
retry_number += 1
|
|
if retry_number > max_retries:
|
|
with excutils.save_and_reraise_exception():
|
|
LOG.exception("Failed to %s", task.name)
|
|
else:
|
|
task.userdata['retry_number'] = retry_number
|
|
# Sleep twice as long as the previous retry
|
|
tts = (2 ** (retry_number - 1)) * delay
|
|
time.sleep(min(tts, 60))
|
|
return task_constants.TaskStatus.PENDING
|
|
LOG.info("Task %(name)s completed.", {'name': task.name})
|
|
return task_constants.TaskStatus.COMPLETED
|
|
|
|
def delete_port_group(self, dvs_id, port_group_id):
|
|
task_name = 'delete-port-group-%s-%s' % (port_group_id, dvs_id)
|
|
userdata = {'retry_number': 1,
|
|
'retry_command': self.vcns.delete_port_group,
|
|
'args': [dvs_id, port_group_id]}
|
|
task = tasks.Task(task_name, port_group_id,
|
|
self._retry_task,
|
|
status_callback=self._retry_task,
|
|
userdata=userdata)
|
|
self.task_manager.add(task)
|
|
|
|
def delete_virtual_wire(self, vw_id):
|
|
task_name = 'delete-virtualwire-%s' % vw_id
|
|
userdata = {'retry_number': 1,
|
|
'retry_command': self.vcns.delete_virtual_wire,
|
|
'args': [vw_id]}
|
|
task = tasks.Task(task_name, vw_id,
|
|
self._retry_task,
|
|
status_callback=self._retry_task,
|
|
userdata=userdata)
|
|
self.task_manager.add(task)
|
|
|
|
def create_bridge(self, device_name, bridge):
|
|
try:
|
|
self.vcns.create_bridge(device_name, bridge)
|
|
except exceptions.VcnsApiException:
|
|
with excutils.save_and_reraise_exception():
|
|
LOG.exception("Failed to create bridge in the %s",
|
|
device_name)
|
|
|
|
def delete_bridge(self, device_name):
|
|
try:
|
|
self.vcns.delete_bridge(device_name)
|
|
except exceptions.VcnsApiException:
|
|
LOG.exception("Failed to delete bridge in the %s",
|
|
device_name)
|
|
|
|
def update_edge_ha(self, edge_id):
|
|
ha_request = {
|
|
'featureType': "highavailability_4.0",
|
|
'enabled': True}
|
|
self.vcns.enable_ha(edge_id, ha_request)
|
|
|
|
def update_edge_syslog(self, edge_id, syslog_config, router_id):
|
|
if 'server_ip' not in syslog_config:
|
|
LOG.warning("Server IP missing in syslog config for %s",
|
|
router_id)
|
|
return
|
|
|
|
protocol = syslog_config.get('protocol', 'tcp')
|
|
if protocol not in ['tcp', 'udp']:
|
|
LOG.warning("Invalid protocol in syslog config for %s",
|
|
router_id)
|
|
return
|
|
|
|
loglevel = syslog_config.get('log_level')
|
|
if loglevel and loglevel not in edge_utils.SUPPORTED_EDGE_LOG_LEVELS:
|
|
LOG.warning("Invalid loglevel in syslog config for %s",
|
|
router_id)
|
|
return
|
|
|
|
server_ip = syslog_config['server_ip']
|
|
request = {'featureType': 'syslog',
|
|
'protocol': protocol,
|
|
'serverAddresses': {'ipAddress': [server_ip],
|
|
'type': 'IpAddressesDto'}}
|
|
|
|
# edge allows up to 2 syslog servers
|
|
if 'server2_ip' in syslog_config:
|
|
request['serverAddresses']['ipAddress'].append(
|
|
syslog_config['server2_ip'])
|
|
|
|
self.vcns.update_edge_syslog(edge_id, request)
|
|
|
|
# update log level for routing in separate API call
|
|
if loglevel:
|
|
edge_utils.update_edge_loglevel(self.vcns, edge_id,
|
|
'routing', loglevel)
|