vmware-nsx/vmware_nsx/services/lbaas/nsx_p/implementation/pool_mgr.py

250 lines
11 KiB
Python

# Copyright 2018 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 functools
from neutron_lib import exceptions as n_exc
from oslo_log import log as logging
from oslo_utils import excutils
from vmware_nsx._i18n import _
from vmware_nsx.services.lbaas import base_mgr
from vmware_nsx.services.lbaas import lb_common
from vmware_nsx.services.lbaas import lb_const
from vmware_nsx.services.lbaas.nsx_p.implementation import lb_utils
from vmware_nsxlib.v3 import exceptions as nsxlib_exc
from vmware_nsxlib.v3 import utils
LOG = logging.getLogger(__name__)
class EdgePoolManagerFromDict(base_mgr.NsxpLoadbalancerBaseManager):
def _get_pool_kwargs(self, name=None, tags=None, algorithm=None,
description=None):
kwargs = {
'snat_translation': {'type': "LBSnatAutoMap"}}
if name:
kwargs['name'] = name
if tags:
kwargs['tags'] = tags
if algorithm:
kwargs['algorithm'] = algorithm
if description:
kwargs['description'] = description
return kwargs
def _get_pool_tags(self, context, pool):
return lb_utils.get_tags(self.core_plugin, pool['id'],
lb_const.LB_POOL_TYPE, pool['tenant_id'],
context.project_name)
def _remove_persistence(self, vs_data):
lb_utils.delete_persistence_profile(
self.core_plugin.nsxpolicy,
vs_data.get('lb_persistence_profile_path', ''))
def _process_vs_update(self, context, pool, pool_id, switch_type,
listener, completor):
vs_client = self.core_plugin.nsxpolicy.load_balancer.virtual_server
try:
# Process pool persistence profile and
# create/update/delete profile for virtual server
vs_data = vs_client.get(listener['id'])
if pool and pool_id:
(persistence_profile_id,
post_process_func) = lb_utils.setup_session_persistence(
self.core_plugin.nsxpolicy, pool,
self._get_pool_tags(context, pool),
switch_type, listener, vs_data)
else:
post_process_func = functools.partial(
self._remove_persistence, vs_data)
persistence_profile_id = None
except nsxlib_exc.ManagerError:
with excutils.save_and_reraise_exception():
completor(success=False)
LOG.error("Failed to configure session persistence "
"profile for pool %(pool_id)s",
{'pool_id': pool['id']})
try:
# Update persistence profile and pool on virtual server
vs_client.update(
listener['id'],
pool_id=pool_id,
lb_persistence_profile_id=persistence_profile_id)
LOG.debug("Updated NSX virtual server %(vs_id)s with "
"pool %(pool_id)s and persistence profile %(prof)s",
{'vs_id': listener['id'], 'pool_id': pool['id'],
'prof': persistence_profile_id})
if post_process_func:
post_process_func()
except nsxlib_exc.ManagerError:
with excutils.save_and_reraise_exception():
completor(success=False)
LOG.error('Failed to attach pool %s to virtual '
'server %s', pool['id'], listener['id'])
def create(self, context, pool, completor):
pool_client = self.core_plugin.nsxpolicy.load_balancer.lb_pool
pool_name = utils.get_name_and_uuid(pool['name'] or 'pool', pool['id'])
tags = self._get_pool_tags(context, pool)
description = pool.get('description')
lb_algorithm = lb_const.LB_POOL_ALGORITHM_MAP.get(pool['lb_algorithm'])
if pool.get('listeners') and len(pool['listeners']) > 1:
completor(success=False)
msg = (_('Failed to create pool: Multiple listeners are not '
'supported.'))
raise n_exc.BadRequest(resource='lbaas-pool', msg=msg)
# NOTE(salv-orlando): Guard against accidental compat breakages
try:
listener = pool.get('listener') or pool.get('listeners', [])[0]
except IndexError:
# If listeners is an empty list we hit this exception
listener = None
# Perform additional validation for session persistence before
# creating resources in the backend
lb_common.validate_session_persistence(pool, listener, completor)
try:
kwargs = self._get_pool_kwargs(
pool_name, tags, lb_algorithm, description)
pool_client.create_or_overwrite(lb_pool_id=pool['id'], **kwargs)
except nsxlib_exc.ManagerError:
completor(success=False)
msg = (_('Failed to create pool on NSX backend: %(pool)s') %
{'pool': pool['id']})
raise n_exc.BadRequest(resource='lbaas-pool', msg=msg)
# The pool object can be created with either --listener or
# --loadbalancer option. If listener is present, the virtual server
# will be updated with the pool. Otherwise, just return. The binding
# will be added later when the pool is associated with layer7 rule.
# FIXME(salv-orlando): This two-step process can leave a zombie pool on
# NSX if the VS update operation fails
if listener:
self._process_vs_update(context, pool, pool['id'], False,
listener, completor)
completor(success=True)
def update(self, context, old_pool, new_pool, completor):
pool_client = self.core_plugin.nsxpolicy.load_balancer.lb_pool
pool_name = None
tags = None
lb_algorithm = None
description = None
if new_pool.get('listeners') and len(new_pool['listeners']) > 1:
completor(success=False)
msg = (_('Failed to update pool %s: Multiple listeners are not '
'supported.') % new_pool['id'])
raise n_exc.BadRequest(resource='lbaas-pool', msg=msg)
if new_pool['name'] != old_pool['name']:
pool_name = utils.get_name_and_uuid(new_pool['name'] or 'pool',
new_pool['id'])
tags = self._get_pool_tags(context, new_pool)
if new_pool['lb_algorithm'] != old_pool['lb_algorithm']:
lb_algorithm = lb_const.LB_POOL_ALGORITHM_MAP.get(
new_pool['lb_algorithm'])
if new_pool.get('description') != old_pool.get('description'):
description = new_pool['description']
# NOTE(salv-orlando): Guard against accidental compat breakages
try:
listener = (new_pool.get('listener') or
new_pool.get('listeners', [])[0])
except IndexError:
# If listeners is an empty list we hit this exception
listener = None
# Perform additional validation for session persistence before
# operating on resources in the backend
lb_common.validate_session_persistence(new_pool, listener, completor)
try:
kwargs = self._get_pool_kwargs(pool_name, tags, lb_algorithm,
description)
pool_client.update(lb_pool_id=new_pool['id'], **kwargs)
if (listener and new_pool['session_persistence'] !=
old_pool['session_persistence']):
switch_type = lb_common.session_persistence_type_changed(
new_pool, old_pool)
self._process_vs_update(context, new_pool, new_pool['id'],
switch_type, listener, completor)
completor(success=True)
except Exception as e:
with excutils.save_and_reraise_exception():
completor(success=False)
LOG.error('Failed to update pool %(pool)s with '
'error %(error)s',
{'pool': old_pool['id'], 'error': e})
def delete(self, context, pool, completor):
pool_client = self.core_plugin.nsxpolicy.load_balancer.lb_pool
# NOTE(salv-orlando): Guard against accidental compat breakages
try:
listener = pool.get('listener') or pool.get('listeners', [])[0]
except IndexError:
# If listeners is an empty list we hit this exception
listener = None
if listener:
try:
self._process_vs_update(
context, pool, None, False, listener, completor)
except Exception as e:
LOG.error('Disassociation of listener %(lsn)s from pool '
'%(pool)s failed with error %(err)s',
{'lsn': listener['id'],
'pool': pool['id'],
'err': e})
try:
pool_client.delete(pool['id'])
except nsxlib_exc.ResourceNotFound:
pass
except nsxlib_exc.ManagerError:
completor(success=False)
msg = (_('Failed to delete lb pool from nsx: %(pool)s') %
{'pool': pool['id']})
raise n_exc.BadRequest(resource='lbaas-pool', msg=msg)
# Delete the attached health monitor as well
if pool.get('healthmonitor'):
hm = pool['healthmonitor']
monitor_client = lb_utils.get_monitor_policy_client(
self.core_plugin.nsxpolicy.load_balancer, hm)
try:
monitor_client.delete(hm['id'])
except nsxlib_exc.ResourceNotFound:
pass
except nsxlib_exc.ManagerError as exc:
completor(success=False)
msg = _('Failed to delete monitor %(monitor)s from backend '
'with exception %(exc)s') % {'monitor': hm['id'],
'exc': exc}
raise n_exc.BadRequest(resource='lbaas-pool', msg=msg)
completor(success=True)
def delete_cascade(self, context, pool, completor):
# Listeners were already deleted
pool['listeners'] = []
pool['listener'] = None
pool['listener_id'] = None
self.delete(context, pool, completor)