
Logging was broken in updata VIP operation Change-Id: Iec45cd01c9e810956ddd7c16c1e26517da4ce77a
886 lines
36 KiB
Python
886 lines
36 KiB
Python
# Copyright 2015 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 xml.etree.ElementTree as et
|
|
|
|
import netaddr
|
|
from oslo_log import log as logging
|
|
from oslo_utils import excutils
|
|
|
|
from neutron.common import exceptions as n_exc
|
|
from neutron.i18n import _LE
|
|
from neutron import manager
|
|
from neutron.plugins.common import constants
|
|
from vmware_nsx.neutron.plugins.vmware.common import locking
|
|
from vmware_nsx.neutron.plugins.vmware.dbexts import nsxv_db
|
|
from vmware_nsx.neutron.plugins.vmware.vshield.common import (
|
|
exceptions as nsxv_exc)
|
|
from vmware_nsx.neutron.plugins.vmware.vshield import vcns as nsxv_api
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
LB_METHOD_ROUND_ROBIN = 'ROUND_ROBIN'
|
|
LB_METHOD_LEAST_CONNECTIONS = 'LEAST_CONNECTIONS'
|
|
LB_METHOD_SOURCE_IP = 'SOURCE_IP'
|
|
|
|
LB_PROTOCOL_TCP = 'TCP'
|
|
LB_PROTOCOL_HTTP = 'HTTP'
|
|
LB_PROTOCOL_HTTPS = 'HTTPS'
|
|
|
|
LB_HEALTH_MONITOR_PING = 'PING'
|
|
LB_HEALTH_MONITOR_TCP = 'TCP'
|
|
LB_HEALTH_MONITOR_HTTP = 'HTTP'
|
|
LB_HEALTH_MONITOR_HTTPS = 'HTTPS'
|
|
|
|
LB_SESSION_PERSISTENCE_SOURCE_IP = 'SOURCE_IP'
|
|
LB_SESSION_PERSISTENCE_HTTP_COOKIE = 'HTTP_COOKIE'
|
|
LB_SESSION_PERSISTENCE_APP_COOKIE = 'APP_COOKIE'
|
|
|
|
BALANCE_MAP = {
|
|
LB_METHOD_ROUND_ROBIN: 'round-robin',
|
|
LB_METHOD_LEAST_CONNECTIONS: 'leastconn',
|
|
LB_METHOD_SOURCE_IP: 'ip-hash'}
|
|
|
|
PROTOCOL_MAP = {
|
|
LB_PROTOCOL_TCP: 'tcp',
|
|
LB_PROTOCOL_HTTP: 'http',
|
|
LB_PROTOCOL_HTTPS: 'tcp'}
|
|
|
|
HEALTH_MONITOR_MAP = {
|
|
LB_HEALTH_MONITOR_PING: 'icmp',
|
|
LB_HEALTH_MONITOR_TCP: 'tcp',
|
|
LB_HEALTH_MONITOR_HTTP: 'http',
|
|
LB_HEALTH_MONITOR_HTTPS: 'tcp'}
|
|
|
|
SESSION_PERSISTENCE_METHOD_MAP = {
|
|
LB_SESSION_PERSISTENCE_SOURCE_IP: 'sourceip',
|
|
LB_SESSION_PERSISTENCE_APP_COOKIE: 'cookie',
|
|
LB_SESSION_PERSISTENCE_HTTP_COOKIE: 'cookie'}
|
|
|
|
SESSION_PERSISTENCE_COOKIE_MAP = {
|
|
LB_SESSION_PERSISTENCE_APP_COOKIE: 'app',
|
|
LB_SESSION_PERSISTENCE_HTTP_COOKIE: 'insert'}
|
|
|
|
LBAAS_FW_SECTION_NAME = 'LBaaS FW Rules'
|
|
|
|
MEMBER_ID_PFX = 'member-'
|
|
|
|
|
|
def convert_lbaas_pool(lbaas_pool):
|
|
"""
|
|
Transform OpenStack pool dict to NSXv pool dict.
|
|
"""
|
|
edge_pool = {
|
|
'name': 'pool_' + lbaas_pool['id'],
|
|
'description': lbaas_pool.get('description',
|
|
lbaas_pool.get('name')),
|
|
'algorithm': BALANCE_MAP.get(
|
|
lbaas_pool.get('lb_method'), 'round-robin'),
|
|
'transparent': False
|
|
}
|
|
return edge_pool
|
|
|
|
|
|
def convert_lbaas_app_profile(name, sess_persist, protocol):
|
|
"""
|
|
Create app profile dict for lbaas VIP.
|
|
|
|
Neutron-lbaas VIP objects breaks into an application profile object, and
|
|
a virtual server object in NSXv.
|
|
"""
|
|
vcns_app_profile = {
|
|
'insertXForwardedFor': False,
|
|
'name': name,
|
|
'serverSslEnabled': False,
|
|
'sslPassthrough': False,
|
|
'template': protocol,
|
|
}
|
|
# Since SSL Termination is not supported right now, so just use
|
|
# sslPassthrough method if the protocol is HTTPS.
|
|
if protocol == LB_PROTOCOL_HTTPS:
|
|
vcns_app_profile['sslPassthrough'] = True
|
|
|
|
persist_type = sess_persist.get('type')
|
|
if persist_type:
|
|
# If protocol is not HTTP, only source_ip is supported
|
|
if (protocol != LB_PROTOCOL_HTTP and
|
|
persist_type != LB_SESSION_PERSISTENCE_SOURCE_IP):
|
|
msg = (_('Invalid %(protocol)s persistence method: %(type)s') %
|
|
{'protocol': protocol,
|
|
'type': persist_type})
|
|
raise n_exc.BadRequest(resource='edge-lbaas', msg=msg)
|
|
persistence = {
|
|
'method': SESSION_PERSISTENCE_METHOD_MAP.get(persist_type)}
|
|
if persist_type in SESSION_PERSISTENCE_COOKIE_MAP:
|
|
persistence.update({
|
|
'cookieName': sess_persist.get('cookie_name',
|
|
'default_cookie_name'),
|
|
'cookieMode': SESSION_PERSISTENCE_COOKIE_MAP[persist_type]})
|
|
|
|
vcns_app_profile['persistence'] = persistence
|
|
return vcns_app_profile
|
|
|
|
|
|
def convert_lbaas_vip(vip, app_profile_id, pool_mapping):
|
|
"""
|
|
Transform OpenStack VIP dict to NSXv virtual server dict.
|
|
"""
|
|
pool_id = pool_mapping['edge_pool_id']
|
|
return {
|
|
'name': 'vip_' + vip['id'],
|
|
'description': vip['description'],
|
|
'ipAddress': vip['address'],
|
|
'protocol': vip.get('protocol'),
|
|
'port': vip['protocol_port'],
|
|
'connectionLimit': max(0, vip.get('connection_limit')),
|
|
'defaultPoolId': pool_id,
|
|
'applicationProfileId': app_profile_id}
|
|
|
|
|
|
def convert_lbaas_member(member):
|
|
"""
|
|
Transform OpenStack pool member dict to NSXv pool member dict.
|
|
"""
|
|
return {
|
|
'ipAddress': member['address'],
|
|
'weight': member['weight'],
|
|
'port': member['protocol_port'],
|
|
'monitorPort': member['protocol_port'],
|
|
'name': get_member_id(member['id']),
|
|
'condition': 'enabled' if member['admin_state_up'] else 'disabled'}
|
|
|
|
|
|
def convert_lbaas_monitor(monitor):
|
|
"""
|
|
Transform OpenStack health monitor dict to NSXv health monitor dict.
|
|
"""
|
|
mon = {
|
|
'type': HEALTH_MONITOR_MAP.get(
|
|
monitor['type'], 'icmp'),
|
|
'interval': monitor['delay'],
|
|
'timeout': monitor['timeout'],
|
|
'maxRetries': monitor['max_retries'],
|
|
'name': monitor['id']}
|
|
|
|
if monitor.get('http_method'):
|
|
mon['method'] = monitor['http_method']
|
|
|
|
if monitor.get('url_path'):
|
|
mon['url'] = monitor['url_path']
|
|
return mon
|
|
|
|
|
|
def extract_resource_id(location_uri):
|
|
"""
|
|
Edge assigns an ID for each resource that is being created:
|
|
it is postfixes the uri specified in the Location header.
|
|
This ID should be used while updating/deleting this resource.
|
|
"""
|
|
uri_elements = location_uri.split('/')
|
|
return uri_elements[-1]
|
|
|
|
|
|
def get_subnet_primary_ip(ip_addr, address_groups):
|
|
"""
|
|
Retrieve the primary IP of an interface that's attached to the same subnet.
|
|
"""
|
|
addr_group = find_address_in_same_subnet(ip_addr, address_groups)
|
|
return addr_group['primaryAddress'] if addr_group else None
|
|
|
|
|
|
def find_address_in_same_subnet(ip_addr, address_groups):
|
|
"""
|
|
Lookup an address group with a matching subnet to ip_addr.
|
|
If found, return address_group.
|
|
"""
|
|
for address_group in address_groups['addressGroups']:
|
|
net_addr = '%(primaryAddress)s/%(subnetPrefixLength)s' % address_group
|
|
if netaddr.IPAddress(ip_addr) in netaddr.IPNetwork(net_addr):
|
|
return address_group
|
|
|
|
|
|
def add_address_to_address_groups(ip_addr, address_groups):
|
|
"""
|
|
Add ip_addr as a secondary IP address to an address group which belongs to
|
|
the same subnet.
|
|
"""
|
|
address_group = find_address_in_same_subnet(
|
|
ip_addr, address_groups)
|
|
if address_group:
|
|
sec_addr = address_group.get('secondaryAddresses')
|
|
if not sec_addr:
|
|
sec_addr = {
|
|
'type': 'secondary_addresses',
|
|
'ipAddress': [ip_addr]}
|
|
else:
|
|
sec_addr['ipAddress'].append(ip_addr)
|
|
address_group['secondaryAddresses'] = sec_addr
|
|
return True
|
|
return False
|
|
|
|
|
|
def del_address_from_address_groups(ip_addr, address_groups):
|
|
"""
|
|
Delete ip_addr from secondary address list in address groups.
|
|
"""
|
|
address_group = find_address_in_same_subnet(ip_addr, address_groups)
|
|
if address_group:
|
|
sec_addr = address_group.get('secondaryAddresses')
|
|
if sec_addr and ip_addr in sec_addr['ipAddress']:
|
|
sec_addr['ipAddress'].remove(ip_addr)
|
|
return True
|
|
return False
|
|
|
|
|
|
def get_member_id(member_id):
|
|
return MEMBER_ID_PFX + member_id
|
|
|
|
|
|
class EdgeLbDriver(object):
|
|
def __init__(self):
|
|
super(EdgeLbDriver, self).__init__()
|
|
LOG.debug('Initializing Edge loadbalancer')
|
|
# self.vcns is initialized by subclass
|
|
self.vcns = None
|
|
self._fw_section_id = None
|
|
self._lb_plugin = None
|
|
self._lb_driver_prop = None
|
|
|
|
def _get_lb_plugin(self):
|
|
if not self._lb_plugin:
|
|
loaded_plugins = manager.NeutronManager().get_service_plugins()
|
|
self._lb_plugin = loaded_plugins[constants.LOADBALANCER]
|
|
return self._lb_plugin
|
|
|
|
@property
|
|
def _lb_driver(self):
|
|
if not self._lb_driver_prop:
|
|
plugin = self._get_lb_plugin()
|
|
self._lb_driver_prop = plugin.drivers['vmwareedge']
|
|
|
|
return self._lb_driver_prop
|
|
|
|
def _get_lbaas_fw_section_id(self):
|
|
if not self._fw_section_id:
|
|
# Avoid concurrent creation of section by multiple neutron
|
|
# instances
|
|
with locking.LockManager.get_lock('lbaas-section-creation',
|
|
external=True):
|
|
fw_section_id = self.vcns.get_section_id(LBAAS_FW_SECTION_NAME)
|
|
if not fw_section_id:
|
|
section = et.Element('section')
|
|
section.attrib['name'] = LBAAS_FW_SECTION_NAME
|
|
sect = self.vcns.create_section('ip',
|
|
et.tostring(section))[1]
|
|
fw_section_id = et.fromstring(sect).attrib['id']
|
|
self._fw_section_id = fw_section_id
|
|
return self._fw_section_id
|
|
|
|
def _get_lb_edge_id(self, context, subnet_id):
|
|
"""
|
|
Grab the id of an Edge appliance that is connected to subnet_id.
|
|
"""
|
|
subnet = self.callbacks.plugin.get_subnet(context, subnet_id)
|
|
net_id = subnet.get('network_id')
|
|
filters = {'network_id': [net_id],
|
|
'device_owner': ['network:router_interface']}
|
|
attached_routers = self.callbacks.plugin.get_ports(
|
|
context.elevated(), filters=filters,
|
|
fields=['device_id'])
|
|
|
|
for attached_router in attached_routers:
|
|
router = self.callbacks.plugin.get_router(
|
|
context, attached_router['device_id'])
|
|
if router['router_type'] == 'exclusive':
|
|
rtr_bindings = nsxv_db.get_nsxv_router_binding(
|
|
context.session, router['id'])
|
|
return rtr_bindings['edge_id']
|
|
|
|
def _vip_as_secondary_ip(self, edge_id, vip, handler):
|
|
with locking.LockManager.get_lock(edge_id, external=True):
|
|
r = self.vcns.get_interfaces(edge_id)[1]
|
|
vnics = r.get('vnics', [])
|
|
for vnic in vnics:
|
|
if vnic['type'] == 'trunk':
|
|
for sub_interface in vnic.get('subInterfaces').get(
|
|
'subInterfaces'):
|
|
address_groups = sub_interface.get('addressGroups')
|
|
if handler(vip, address_groups):
|
|
self.vcns.update_interface(edge_id, vnic)
|
|
return True
|
|
else:
|
|
address_groups = vnic.get('addressGroups')
|
|
if handler(vip, address_groups):
|
|
self.vcns.update_interface(edge_id, vnic)
|
|
return True
|
|
return False
|
|
|
|
def _add_vip_as_secondary_ip(self, edge_id, vip):
|
|
"""
|
|
Edge appliance requires that a VIP will be configured as a primary
|
|
or a secondary IP address on an interface.
|
|
To do so, we locate an interface which is connected to the same subnet
|
|
that vip belongs to.
|
|
This can be a regular interface, on a sub-interface on a trunk.
|
|
"""
|
|
if not self._vip_as_secondary_ip(
|
|
edge_id, vip, add_address_to_address_groups):
|
|
|
|
msg = _('Failed to add VIP %(vip)s as secondary IP on '
|
|
'Edge %(edge_id)s') % {'vip': vip, 'edge_id': edge_id}
|
|
raise n_exc.BadRequest(resource='edge-lbaas', msg=msg)
|
|
|
|
def _del_vip_as_secondary_ip(self, edge_id, vip):
|
|
"""
|
|
While removing vip, delete the secondary interface from Edge config.
|
|
"""
|
|
if not self._vip_as_secondary_ip(
|
|
edge_id, vip, del_address_from_address_groups):
|
|
|
|
msg = _('Failed to delete VIP %(vip)s as secondary IP on '
|
|
'Edge %(edge_id)s') % {'vip': vip, 'edge_id': edge_id}
|
|
raise n_exc.BadRequest(resource='edge-lbaas', msg=msg)
|
|
|
|
def _get_edge_ips(self, edge_id):
|
|
edge_ips = []
|
|
r = self.vcns.get_interfaces(edge_id)[1]
|
|
vnics = r.get('vnics', [])
|
|
for vnic in vnics:
|
|
if vnic['type'] == 'trunk':
|
|
for sub_interface in vnic.get('subInterfaces').get(
|
|
'subInterfaces'):
|
|
address_groups = sub_interface.get('addressGroups')
|
|
for address_group in address_groups['addressGroups']:
|
|
edge_ips.append(address_group['primaryAddress'])
|
|
|
|
else:
|
|
address_groups = vnic.get('addressGroups')
|
|
for address_group in address_groups['addressGroups']:
|
|
edge_ips.append(address_group['primaryAddress'])
|
|
return edge_ips
|
|
|
|
def _update_pool_fw_rule(self, context, pool_id, edge_id,
|
|
operation=None, address=None):
|
|
edge_ips = self._get_edge_ips(edge_id)
|
|
|
|
plugin = self._get_lb_plugin()
|
|
with locking.LockManager.get_lock('lbaas-fw-section', external=True):
|
|
members = plugin.get_members(
|
|
context,
|
|
filters={'pool_id': [pool_id]},
|
|
fields=['address'])
|
|
member_ips = [member['address'] for member in members]
|
|
if operation == 'add' and address not in member_ips:
|
|
member_ips.append(address)
|
|
elif operation == 'del' and address in member_ips:
|
|
member_ips.remove(address)
|
|
|
|
section_uri = '%s/%s/%s' % (nsxv_api.FIREWALL_PREFIX,
|
|
'layer3sections',
|
|
self._get_lbaas_fw_section_id())
|
|
xml_section = self.vcns.get_section(section_uri)[1]
|
|
section = et.fromstring(xml_section)
|
|
pool_rule = None
|
|
for rule in section.iter('rule'):
|
|
if rule.find('name').text == pool_id:
|
|
pool_rule = rule
|
|
if member_ips:
|
|
pool_rule.find('sources').find('source').find(
|
|
'value').text = (','.join(edge_ips))
|
|
pool_rule.find('destinations').find(
|
|
'destination').find('value').text = ','.join(
|
|
member_ips)
|
|
else:
|
|
section.remove(pool_rule)
|
|
break
|
|
|
|
if member_ips and pool_rule is None:
|
|
pool_rule = et.SubElement(section, 'rule')
|
|
et.SubElement(pool_rule, 'name').text = pool_id
|
|
et.SubElement(pool_rule, 'action').text = 'allow'
|
|
sources = et.SubElement(pool_rule, 'sources')
|
|
sources.attrib['excluded'] = 'false'
|
|
source = et.SubElement(sources, 'source')
|
|
et.SubElement(source, 'type').text = 'Ipv4Address'
|
|
et.SubElement(source, 'value').text = ','.join(edge_ips)
|
|
|
|
destinations = et.SubElement(pool_rule, 'destinations')
|
|
destinations.attrib['excluded'] = 'false'
|
|
destination = et.SubElement(destinations, 'destination')
|
|
et.SubElement(destination, 'type').text = 'Ipv4Address'
|
|
et.SubElement(destination, 'value').text = ','.join(member_ips)
|
|
|
|
self.vcns.update_section(section_uri,
|
|
et.tostring(section, encoding="us-ascii"),
|
|
None)
|
|
|
|
def _add_vip_fw_rule(self, edge_id, vip_id, ip_address):
|
|
fw_rule = {
|
|
'firewallRules': [
|
|
{'action': 'accept', 'destination': {
|
|
'ipAddress': [ip_address]},
|
|
'enabled': True,
|
|
'name': vip_id}]}
|
|
|
|
with locking.LockManager.get_lock(edge_id, external=True):
|
|
h = self.vcns.add_firewall_rule(edge_id, fw_rule)[0]
|
|
fw_rule_id = extract_resource_id(h['location'])
|
|
|
|
return fw_rule_id
|
|
|
|
def _del_vip_fw_rule(self, edge_id, vip_fw_rule_id):
|
|
with locking.LockManager.get_lock(edge_id, external=True):
|
|
self.vcns.delete_firewall_rule(edge_id, vip_fw_rule_id)
|
|
|
|
def create_pool(self, context, pool):
|
|
LOG.debug('Creating pool %s', pool)
|
|
edge_id = self._get_lb_edge_id(context, pool['subnet_id'])
|
|
|
|
if edge_id is None:
|
|
self._lb_driver.pool_failed(context, pool)
|
|
msg = _(
|
|
'No suitable Edge found for subnet %s') % pool['subnet_id']
|
|
raise n_exc.BadRequest(resource='edge-lbaas', msg=msg)
|
|
|
|
edge_pool = convert_lbaas_pool(pool)
|
|
try:
|
|
with locking.LockManager.get_lock(edge_id, external=True):
|
|
h = self.vcns.create_pool(edge_id, edge_pool)[0]
|
|
edge_pool_id = extract_resource_id(h['location'])
|
|
self._lb_driver.create_pool_successful(
|
|
context, pool, edge_id, edge_pool_id)
|
|
|
|
except nsxv_exc.VcnsApiException:
|
|
with excutils.save_and_reraise_exception():
|
|
self._lb_driver.pool_failed(context, pool)
|
|
LOG.error(_LE('Failed to create pool %s'), pool['id'])
|
|
|
|
def update_pool(self, context, old_pool, pool, pool_mapping):
|
|
LOG.debug('Updating pool %s to %s', old_pool, pool)
|
|
edge_pool = convert_lbaas_pool(pool)
|
|
try:
|
|
with locking.LockManager.get_lock(pool_mapping['edge_id'],
|
|
external=True):
|
|
self.vcns.update_pool(pool_mapping['edge_id'],
|
|
pool_mapping['edge_pool_id'],
|
|
edge_pool)
|
|
self._lb_driver.pool_successful(context, pool)
|
|
|
|
except nsxv_exc.VcnsApiException:
|
|
with excutils.save_and_reraise_exception():
|
|
self._lb_driver.pool_failed(context, pool)
|
|
LOG.error(_LE('Failed to update pool %s'), pool['id'])
|
|
|
|
def delete_pool(self, context, pool, pool_mapping):
|
|
LOG.debug('Deleting pool %s', pool)
|
|
|
|
if pool_mapping:
|
|
try:
|
|
with locking.LockManager.get_lock(pool_mapping['edge_id'],
|
|
external=True):
|
|
self.vcns.delete_pool(pool_mapping['edge_id'],
|
|
pool_mapping['edge_pool_id'])
|
|
except nsxv_exc.VcnsApiException:
|
|
with excutils.save_and_reraise_exception():
|
|
self._lb_driver.pool_failed(context, pool)
|
|
LOG.error(_LE('Failed to delete pool %s'), pool['id'])
|
|
else:
|
|
LOG.error(_LE('No mapping found for pool %s'), pool['id'])
|
|
|
|
self._lb_driver.delete_pool_successful(context, pool)
|
|
|
|
def create_vip(self, context, vip, pool_mapping):
|
|
LOG.debug('Create VIP %s', vip)
|
|
|
|
app_profile = convert_lbaas_app_profile(
|
|
vip['id'], vip.get('session_persistence') or {},
|
|
vip.get('protocol'))
|
|
|
|
if not pool_mapping:
|
|
msg = _('Pool %s in not mapped to any Edge appliance') % (
|
|
vip['pool_id'])
|
|
raise n_exc.BadRequest(resource='edge-lbaas', msg=msg)
|
|
edge_id = pool_mapping['edge_id']
|
|
|
|
app_profile_id = None
|
|
try:
|
|
with locking.LockManager.get_lock(edge_id, external=True):
|
|
h = (self.vcns.create_app_profile(edge_id, app_profile))[0]
|
|
app_profile_id = extract_resource_id(h['location'])
|
|
except nsxv_exc.VcnsApiException:
|
|
with excutils.save_and_reraise_exception():
|
|
self._lb_driver.vip_failed(context, vip)
|
|
LOG.error(_LE('Failed to create app profile on edge: %s'),
|
|
edge_id)
|
|
|
|
edge_vip = convert_lbaas_vip(vip, app_profile_id, pool_mapping)
|
|
try:
|
|
self._add_vip_as_secondary_ip(edge_id, vip['address'])
|
|
with locking.LockManager.get_lock(edge_id, external=True):
|
|
h = self.vcns.create_vip(edge_id, edge_vip)[0]
|
|
edge_vip_id = extract_resource_id(h['location'])
|
|
edge_fw_rule_id = self._add_vip_fw_rule(edge_id, vip['id'],
|
|
vip['address'])
|
|
self._lb_driver.create_vip_successful(
|
|
context, vip, edge_id, app_profile_id, edge_vip_id,
|
|
edge_fw_rule_id)
|
|
|
|
except nsxv_exc.VcnsApiException:
|
|
with excutils.save_and_reraise_exception():
|
|
self._lb_driver.vip_failed(context, vip)
|
|
LOG.error(_LE('Failed to create vip on Edge: %s'), edge_id)
|
|
with locking.LockManager.get_lock(edge_id, external=True):
|
|
self.vcns.delete_app_profile(edge_id, app_profile_id)
|
|
|
|
def update_vip(self, context, old_vip, vip, pool_mapping, vip_mapping):
|
|
LOG.debug('Updating VIP %s to %s', old_vip, vip)
|
|
|
|
edge_id = vip_mapping['edge_id']
|
|
edge_vip_id = vip_mapping['edge_vse_id']
|
|
app_profile_id = vip_mapping['edge_app_profile_id']
|
|
app_profile = convert_lbaas_app_profile(
|
|
vip['name'], vip.get('session_persistence') or {},
|
|
vip.get('protocol'))
|
|
try:
|
|
with locking.LockManager.get_lock(edge_id, external=True):
|
|
self.vcns.update_app_profile(edge_id, app_profile_id,
|
|
app_profile)
|
|
except nsxv_exc.VcnsApiException:
|
|
with excutils.save_and_reraise_exception():
|
|
self._lb_driver.vip_failed(context, vip)
|
|
LOG.error(_LE('Failed to update app profile on edge: %s'),
|
|
edge_id)
|
|
|
|
edge_vip = convert_lbaas_vip(vip, app_profile_id, pool_mapping)
|
|
try:
|
|
with locking.LockManager.get_lock(edge_id, external=True):
|
|
self.vcns.update_vip(edge_id, edge_vip_id, edge_vip)
|
|
self._lb_driver.vip_successful(context, vip)
|
|
except nsxv_exc.VcnsApiException:
|
|
with excutils.save_and_reraise_exception():
|
|
self._lb_driver.vip_failed(context, vip)
|
|
LOG.error(_LE('Failed to update vip on edge: %s'), edge_id)
|
|
|
|
def delete_vip(self, context, vip, vip_mapping):
|
|
LOG.debug('Deleting VIP %s', vip)
|
|
|
|
if not vip_mapping:
|
|
LOG.error(_LE('No mapping found for vip %s'), vip['id'])
|
|
else:
|
|
edge_id = vip_mapping['edge_id']
|
|
edge_vse_id = vip_mapping['edge_vse_id']
|
|
app_profile_id = vip_mapping['edge_app_profile_id']
|
|
|
|
try:
|
|
with locking.LockManager.get_lock(edge_id, external=True):
|
|
self.vcns.delete_vip(edge_id, edge_vse_id)
|
|
self._del_vip_as_secondary_ip(edge_id, vip['address'])
|
|
self._del_vip_fw_rule(edge_id, vip_mapping['edge_fw_rule_id'])
|
|
except nsxv_exc.ResourceNotFound:
|
|
LOG.error(_LE('vip not found on edge: %s'), edge_id)
|
|
except nsxv_exc.VcnsApiException:
|
|
with excutils.save_and_reraise_exception():
|
|
self._lb_driver.vip_failed(context, vip)
|
|
LOG.error(
|
|
_LE('Failed to delete vip on edge: %s'), edge_id)
|
|
|
|
try:
|
|
with locking.LockManager.get_lock(edge_id, external=True):
|
|
self.vcns.delete_app_profile(edge_id, app_profile_id)
|
|
except nsxv_exc.ResourceNotFound:
|
|
LOG.error(_LE('app profile not found on edge: %s'), edge_id)
|
|
except nsxv_exc.VcnsApiException:
|
|
with excutils.save_and_reraise_exception():
|
|
self._lb_driver.vip_failed(context, vip)
|
|
LOG.error(
|
|
_LE('Failed to delete app profile on Edge: %s'),
|
|
edge_id)
|
|
|
|
self._lb_driver.delete_vip_successful(context, vip)
|
|
|
|
def create_member(self, context, member, pool_mapping):
|
|
LOG.debug('Creating member %s', member)
|
|
|
|
with locking.LockManager.get_lock(pool_mapping['edge_id'],
|
|
external=True):
|
|
edge_pool = self.vcns.get_pool(pool_mapping['edge_id'],
|
|
pool_mapping['edge_pool_id'])[1]
|
|
edge_member = convert_lbaas_member(member)
|
|
|
|
if edge_pool['member']:
|
|
edge_pool['member'].append(edge_member)
|
|
else:
|
|
edge_pool['member'] = [edge_member]
|
|
|
|
try:
|
|
self.vcns.update_pool(
|
|
pool_mapping['edge_id'],
|
|
pool_mapping['edge_pool_id'],
|
|
edge_pool)
|
|
|
|
self._update_pool_fw_rule(context, member['pool_id'],
|
|
pool_mapping['edge_id'],
|
|
'add',
|
|
member['address'])
|
|
self._lb_driver.member_successful(context, member)
|
|
|
|
except nsxv_exc.VcnsApiException:
|
|
with excutils.save_and_reraise_exception():
|
|
self._lb_driver.member_failed(context, member)
|
|
LOG.error(_LE('Failed to create member on edge: %s'),
|
|
pool_mapping['edge_id'])
|
|
|
|
def update_member(self, context, old_member, member, pool_mapping):
|
|
LOG.debug('Updating member %s to %s', old_member, member)
|
|
|
|
with locking.LockManager.get_lock(pool_mapping['edge_id'],
|
|
external=True):
|
|
edge_pool = self.vcns.get_pool(pool_mapping['edge_id'],
|
|
pool_mapping['edge_pool_id'])[1]
|
|
|
|
edge_member = convert_lbaas_member(member)
|
|
for i, m in enumerate(edge_pool['member']):
|
|
if m['name'] == get_member_id(member['id']):
|
|
edge_pool['member'][i] = edge_member
|
|
break
|
|
|
|
try:
|
|
self.vcns.update_pool(pool_mapping['edge_id'],
|
|
pool_mapping['edge_pool_id'],
|
|
edge_pool)
|
|
self._lb_driver.member_successful(context, member)
|
|
except nsxv_exc.VcnsApiException:
|
|
with excutils.save_and_reraise_exception():
|
|
self._lb_driver.member_failed(context, member)
|
|
LOG.error(_LE('Failed to update member on edge: %s'),
|
|
pool_mapping['edge_id'])
|
|
|
|
def delete_member(self, context, member, pool_mapping):
|
|
LOG.debug('Deleting member %s', member)
|
|
|
|
if pool_mapping:
|
|
with locking.LockManager.get_lock(pool_mapping['edge_id'],
|
|
external=True):
|
|
edge_pool = self.vcns.get_pool(
|
|
pool_mapping['edge_id'],
|
|
pool_mapping['edge_pool_id'])[1]
|
|
|
|
for i, m in enumerate(edge_pool['member']):
|
|
if m['name'] == get_member_id(member['id']):
|
|
edge_pool['member'].pop(i)
|
|
break
|
|
|
|
try:
|
|
self.vcns.update_pool(pool_mapping['edge_id'],
|
|
pool_mapping['edge_pool_id'],
|
|
edge_pool)
|
|
self._update_pool_fw_rule(context, member['pool_id'],
|
|
pool_mapping['edge_id'],
|
|
'del',
|
|
member['address'])
|
|
except nsxv_exc.VcnsApiException:
|
|
with excutils.save_and_reraise_exception():
|
|
self._lb_driver.member_failed(context, member)
|
|
LOG.error(_LE('Failed to update member on edge: %s'),
|
|
pool_mapping['edge_id'])
|
|
|
|
lb_plugin = self._get_lb_plugin()
|
|
lb_plugin._delete_db_member(context, member['id'])
|
|
|
|
def create_pool_health_monitor(self, context, health_monitor, pool_id,
|
|
pool_mapping, mon_mappings):
|
|
LOG.debug('Create HM %s', health_monitor)
|
|
|
|
edge_mon_id = None
|
|
with locking.LockManager.get_lock(pool_mapping['edge_id'],
|
|
external=True):
|
|
# 1st, we find if we already have a pool with the same monitor, on
|
|
# the same Edge appliance.
|
|
# If there is no pool on this Edge which is already associated with
|
|
# this monitor, create this monitor on Edge
|
|
if mon_mappings:
|
|
edge_mon_id = mon_mappings['edge_monitor_id']
|
|
else:
|
|
edge_monitor = convert_lbaas_monitor(health_monitor)
|
|
try:
|
|
h = self.vcns.create_health_monitor(
|
|
pool_mapping['edge_id'], edge_monitor)[0]
|
|
edge_mon_id = extract_resource_id(h['location'])
|
|
|
|
except nsxv_exc.VcnsApiException:
|
|
self._lb_driver.pool_health_monitor_failed(context,
|
|
health_monitor,
|
|
pool_id)
|
|
with excutils.save_and_reraise_exception():
|
|
LOG.error(
|
|
_LE('Failed to associate monitor on edge: %s'),
|
|
pool_mapping['edge_id'])
|
|
|
|
try:
|
|
# Associate monitor with Edge pool
|
|
edge_pool = self.vcns.get_pool(pool_mapping['edge_id'],
|
|
pool_mapping['edge_pool_id'])[1]
|
|
if edge_pool['monitorId']:
|
|
edge_pool['monitorId'].append(edge_mon_id)
|
|
else:
|
|
edge_pool['monitorId'] = [edge_mon_id]
|
|
|
|
self.vcns.update_pool(pool_mapping['edge_id'],
|
|
pool_mapping['edge_pool_id'],
|
|
edge_pool)
|
|
|
|
except nsxv_exc.VcnsApiException:
|
|
with excutils.save_and_reraise_exception():
|
|
self._lb_driver.pool_health_monitor_failed(context,
|
|
health_monitor,
|
|
pool_id)
|
|
LOG.error(
|
|
_LE('Failed to associate monitor on edge: %s'),
|
|
pool_mapping['edge_id'])
|
|
|
|
self._lb_driver.create_pool_health_monitor_successful(
|
|
context, health_monitor, pool_id, pool_mapping['edge_id'],
|
|
edge_mon_id)
|
|
|
|
def update_pool_health_monitor(self, context, old_health_monitor,
|
|
health_monitor, pool_id, mon_mapping):
|
|
LOG.debug('Update HM %s to %s', old_health_monitor, health_monitor)
|
|
|
|
edge_monitor = convert_lbaas_monitor(health_monitor)
|
|
|
|
try:
|
|
with locking.LockManager.get_lock(mon_mapping['edge_id'],
|
|
external=True):
|
|
self.vcns.update_health_monitor(
|
|
mon_mapping['edge_id'],
|
|
mon_mapping['edge_monitor_id'],
|
|
edge_monitor)
|
|
|
|
except nsxv_exc.VcnsApiException:
|
|
with excutils.save_and_reraise_exception():
|
|
self._lb_driver.pool_health_monitor_failed(context,
|
|
health_monitor,
|
|
pool_id)
|
|
LOG.error(
|
|
_LE('Failed to update monitor on edge: %s'),
|
|
mon_mapping['edge_id'])
|
|
|
|
self._lb_driver.pool_health_monitor_successful(context,
|
|
health_monitor,
|
|
pool_id)
|
|
|
|
def delete_pool_health_monitor(self, context, health_monitor, pool_id,
|
|
pool_mapping, mon_mapping):
|
|
LOG.debug('Deleting HM %s', health_monitor)
|
|
|
|
edge_id = pool_mapping['edge_id']
|
|
if not mon_mapping:
|
|
return
|
|
|
|
with locking.LockManager.get_lock(pool_mapping['edge_id'],
|
|
external=True):
|
|
edge_pool = self.vcns.get_pool(edge_id,
|
|
pool_mapping['edge_pool_id'])[1]
|
|
edge_pool['monitorId'].remove(mon_mapping['edge_monitor_id'])
|
|
|
|
try:
|
|
self.vcns.update_pool(edge_id,
|
|
pool_mapping['edge_pool_id'],
|
|
edge_pool)
|
|
except nsxv_exc.VcnsApiException:
|
|
with excutils.save_and_reraise_exception():
|
|
self._lb_driver.pool_health_monitor_failed(context,
|
|
health_monitor,
|
|
pool_id)
|
|
LOG.error(
|
|
_LE('Failed to delete monitor mapping on edge: %s'),
|
|
mon_mapping['edge_id'])
|
|
|
|
# If this monitor is not used on this edge anymore, delete it
|
|
if not edge_pool['monitorId']:
|
|
try:
|
|
self.vcns.delete_health_monitor(
|
|
mon_mapping['edge_id'],
|
|
mon_mapping['edge_monitor_id'])
|
|
except nsxv_exc.VcnsApiException:
|
|
with excutils.save_and_reraise_exception():
|
|
self._lb_driver.pool_health_monitor_failed(
|
|
context, health_monitor, pool_id)
|
|
LOG.error(
|
|
_LE('Failed to delete monitor on edge: %s'),
|
|
mon_mapping['edge_id'])
|
|
|
|
self._lb_driver.delete_pool_health_monitor_successful(
|
|
context, health_monitor, pool_id, mon_mapping)
|
|
|
|
def stats(self, context, pool_id, pool_mapping):
|
|
LOG.debug('Retrieving stats for pool %s', pool_id)
|
|
|
|
try:
|
|
lb_stats = self.vcns.get_loadbalancer_statistics(
|
|
pool_mapping['edge_id'])
|
|
|
|
except nsxv_exc.VcnsApiException:
|
|
with excutils.save_and_reraise_exception():
|
|
LOG.error(
|
|
_LE('Failed to read load balancer statistics, edge: %s'),
|
|
pool_mapping['edge_id'])
|
|
|
|
pools_stats = lb_stats[1].get('pool', [])
|
|
plugin = self._get_lb_plugin()
|
|
members = plugin.get_members(
|
|
context,
|
|
filters={'pool_id': [pool_id]},
|
|
fields=['id', 'status'])
|
|
member_map = {m['id']: m['status'] for m in members}
|
|
|
|
for pool_stats in pools_stats:
|
|
if pool_stats['poolId'] == pool_mapping['edge_pool_id']:
|
|
stats = {'bytes_in': pool_stats.get('bytesIn', 0),
|
|
'bytes_out': pool_stats.get('bytesOut', 0),
|
|
'active_connections':
|
|
pool_stats.get('curSessions', 0),
|
|
'total_connections':
|
|
pool_stats.get('totalSessions', 0)}
|
|
|
|
member_stats = {}
|
|
for member in pool_stats.get('member', []):
|
|
member_id = member['name'][len(MEMBER_ID_PFX):]
|
|
if member_map[member_id] != 'ERROR':
|
|
member_stats[member_id] = {
|
|
'status': ('INACTIVE'
|
|
if member['status'] == 'DOWN'
|
|
else 'ACTIVE')}
|
|
|
|
stats['members'] = member_stats
|
|
return stats
|
|
|
|
return {'bytes_in': 0,
|
|
'bytes_out': 0,
|
|
'active_connections': 0,
|
|
'total_connections': 0}
|
|
|
|
def is_edge_in_use(self, edge_id):
|
|
return self._lb_driver.is_edge_in_use(edge_id)
|
|
|
|
def is_subnet_in_use(self, context, subnet_id):
|
|
plugin = self._get_lb_plugin()
|
|
if plugin:
|
|
pools = plugin.get_pools(context,
|
|
filters={'subnet_id': [subnet_id]})
|
|
if pools:
|
|
return True
|