293 lines
14 KiB
Python
293 lines
14 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 neutron_lib import exceptions as n_exc
|
|
from oslo_config import cfg
|
|
from oslo_log import helpers as log_helpers
|
|
from oslo_log import log as logging
|
|
from oslo_utils import excutils
|
|
|
|
from vmware_nsx._i18n import _
|
|
from vmware_nsx.common import exceptions as nsx_exc
|
|
from vmware_nsx.db import db as nsx_db
|
|
from vmware_nsx.services.lbaas import base_mgr
|
|
from vmware_nsx.services.lbaas import lb_const
|
|
from vmware_nsx.services.lbaas.nsx_v3 import lb_utils
|
|
from vmware_nsxlib.v3 import exceptions as nsxlib_exc
|
|
from vmware_nsxlib.v3 import utils
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class EdgeListenerManager(base_mgr.Nsxv3LoadbalancerBaseManager):
|
|
@log_helpers.log_method_call
|
|
def __init__(self):
|
|
super(EdgeListenerManager, self).__init__()
|
|
|
|
def _get_virtual_server_kwargs(self, context, listener, vs_name, tags,
|
|
app_profile_id, certificate=None):
|
|
# If loadbalancer vip_port already has floating ip, use floating
|
|
# IP as the virtual server VIP address. Else, use the loadbalancer
|
|
# vip_address directly on virtual server.
|
|
filters = {'port_id': [listener.loadbalancer.vip_port_id]}
|
|
floating_ips = self.core_plugin.get_floatingips(context,
|
|
filters=filters)
|
|
if floating_ips:
|
|
lb_vip_address = floating_ips[0]['floating_ip_address']
|
|
else:
|
|
lb_vip_address = listener.loadbalancer.vip_address
|
|
kwargs = {'enabled': listener.admin_state_up,
|
|
'ip_address': lb_vip_address,
|
|
'port': listener.protocol_port,
|
|
'application_profile_id': app_profile_id}
|
|
if vs_name:
|
|
kwargs['display_name'] = vs_name
|
|
if tags:
|
|
kwargs['tags'] = tags
|
|
if listener.connection_limit != -1:
|
|
kwargs['max_concurrent_connections'] = \
|
|
listener.connection_limit
|
|
if listener.default_pool_id:
|
|
pool_binding = nsx_db.get_nsx_lbaas_pool_binding(
|
|
context.session, listener.loadbalancer.id,
|
|
listener.default_pool_id)
|
|
if pool_binding:
|
|
kwargs['pool_id'] = pool_binding.get('lb_pool_id')
|
|
ssl_profile_binding = self._get_ssl_profile_binding(
|
|
tags, certificate=certificate)
|
|
if (listener.protocol == lb_const.LB_PROTOCOL_TERMINATED_HTTPS and
|
|
ssl_profile_binding):
|
|
kwargs.update(ssl_profile_binding)
|
|
return kwargs
|
|
|
|
def _get_ssl_profile_binding(self, tags, certificate=None):
|
|
tm_client = self.core_plugin.nsxlib.trust_management
|
|
if certificate:
|
|
# First check if NSX already has certificate with same pem.
|
|
# If so, use that certificate for ssl binding. Otherwise,
|
|
# create a new certificate on NSX.
|
|
cert_ids = tm_client.find_cert_with_pem(
|
|
certificate.get_certificate())
|
|
if cert_ids:
|
|
nsx_cert_id = cert_ids[0]
|
|
else:
|
|
nsx_cert_id = tm_client.create_cert(
|
|
certificate.get_certificate(),
|
|
private_key=certificate.get_private_key(),
|
|
passphrase=certificate.get_private_key_passphrase(),
|
|
tags=tags)
|
|
return {
|
|
'client_ssl_profile_binding': {
|
|
'ssl_profile_id': self.core_plugin.client_ssl_profile,
|
|
'default_certificate_id': nsx_cert_id
|
|
}
|
|
}
|
|
|
|
def _get_listener_tags(self, context, listener):
|
|
tags = lb_utils.get_tags(self.core_plugin, listener.id,
|
|
lb_const.LB_LISTENER_TYPE,
|
|
listener.tenant_id,
|
|
context.project_name)
|
|
tags.append({'scope': 'os-lbaas-lb-name',
|
|
'tag': listener.loadbalancer.name[:utils.MAX_TAG_LEN]})
|
|
tags.append({'scope': 'os-lbaas-lb-id',
|
|
'tag': listener.loadbalancer_id})
|
|
return tags
|
|
|
|
@log_helpers.log_method_call
|
|
def create(self, context, listener, certificate=None):
|
|
lb_id = listener.loadbalancer_id
|
|
load_balancer = self.core_plugin.nsxlib.load_balancer
|
|
app_client = load_balancer.application_profile
|
|
vs_client = load_balancer.virtual_server
|
|
service_client = load_balancer.service
|
|
vs_name = utils.get_name_and_uuid(listener.name or 'listener',
|
|
listener.id)
|
|
tags = self._get_listener_tags(context, listener)
|
|
|
|
app_profile_kwargs = {}
|
|
if (listener.protocol == lb_const.LB_PROTOCOL_HTTP or
|
|
listener.protocol == lb_const.LB_PROTOCOL_TERMINATED_HTTPS):
|
|
profile_type = lb_const.LB_HTTP_PROFILE
|
|
if cfg.CONF.nsx_v3.lbaas_inject_xff_header:
|
|
app_profile_kwargs['x_forwarded_for'] = 'INSERT'
|
|
|
|
elif (listener.protocol == lb_const.LB_PROTOCOL_TCP or
|
|
listener.protocol == lb_const.LB_PROTOCOL_HTTPS):
|
|
profile_type = lb_const.LB_TCP_PROFILE
|
|
else:
|
|
msg = (_('Cannot create listener %(listener)s with '
|
|
'protocol %(protocol)s') %
|
|
{'listener': listener.id,
|
|
'protocol': listener.protocol})
|
|
raise n_exc.BadRequest(resource='lbaas-listener', msg=msg)
|
|
try:
|
|
app_profile = app_client.create(
|
|
display_name=vs_name, resource_type=profile_type, tags=tags,
|
|
**app_profile_kwargs)
|
|
app_profile_id = app_profile['id']
|
|
kwargs = self._get_virtual_server_kwargs(
|
|
context, listener, vs_name, tags, app_profile_id, certificate)
|
|
virtual_server = vs_client.create(**kwargs)
|
|
except nsxlib_exc.ManagerError:
|
|
self.lbv2_driver.listener.failed_completion(context, listener)
|
|
msg = _('Failed to create virtual server at NSX backend')
|
|
raise n_exc.BadRequest(resource='lbaas-listener', msg=msg)
|
|
|
|
# If there is already lb:lb_service binding, add the virtual
|
|
# server to the lb service
|
|
binding = nsx_db.get_nsx_lbaas_loadbalancer_binding(
|
|
context.session, lb_id)
|
|
if binding:
|
|
lb_service_id = binding['lb_service_id']
|
|
try:
|
|
service_client.add_virtual_server(lb_service_id,
|
|
virtual_server['id'])
|
|
except nsxlib_exc.ManagerError:
|
|
self.lbv2_driver.listener.failed_completion(context, listener)
|
|
msg = _('Failed to add virtual server to lb service '
|
|
'at NSX backend')
|
|
raise n_exc.BadRequest(resource='lbaas-listener', msg=msg)
|
|
|
|
nsx_db.add_nsx_lbaas_listener_binding(
|
|
context.session, lb_id, listener.id, app_profile_id,
|
|
virtual_server['id'])
|
|
self.lbv2_driver.listener.successful_completion(
|
|
context, listener)
|
|
|
|
@log_helpers.log_method_call
|
|
def update(self, context, old_listener, new_listener, certificate=None):
|
|
vs_client = self.core_plugin.nsxlib.load_balancer.virtual_server
|
|
app_client = self.core_plugin.nsxlib.load_balancer.application_profile
|
|
vs_name = None
|
|
tags = None
|
|
if new_listener.name != old_listener.name:
|
|
vs_name = utils.get_name_and_uuid(new_listener.name or 'listener',
|
|
new_listener.id)
|
|
tags = self._get_listener_tags(context, new_listener)
|
|
|
|
binding = nsx_db.get_nsx_lbaas_listener_binding(
|
|
context.session, old_listener.loadbalancer_id, old_listener.id)
|
|
if not binding:
|
|
msg = (_('Cannot find listener %(listener)s binding on NSX '
|
|
'backend'), {'listener': old_listener.id})
|
|
raise n_exc.BadRequest(resource='lbaas-listener', msg=msg)
|
|
try:
|
|
vs_id = binding['lb_vs_id']
|
|
app_profile_id = binding['app_profile_id']
|
|
updated_kwargs = self._get_virtual_server_kwargs(
|
|
context, new_listener, vs_name, tags, app_profile_id,
|
|
certificate)
|
|
vs_client.update(vs_id, **updated_kwargs)
|
|
if vs_name:
|
|
app_client.update(app_profile_id, display_name=vs_name,
|
|
tags=tags)
|
|
self.lbv2_driver.listener.successful_completion(context,
|
|
new_listener)
|
|
except Exception as e:
|
|
with excutils.save_and_reraise_exception():
|
|
self.lbv2_driver.listener.failed_completion(
|
|
context, new_listener)
|
|
LOG.error('Failed to update listener %(listener)s with '
|
|
'error %(error)s',
|
|
{'listener': old_listener.id, 'error': e})
|
|
|
|
@log_helpers.log_method_call
|
|
def delete(self, context, listener):
|
|
lb_id = listener.loadbalancer_id
|
|
load_balancer = self.core_plugin.nsxlib.load_balancer
|
|
service_client = load_balancer.service
|
|
vs_client = load_balancer.virtual_server
|
|
app_client = load_balancer.application_profile
|
|
|
|
binding = nsx_db.get_nsx_lbaas_listener_binding(
|
|
context.session, lb_id, listener.id)
|
|
if binding:
|
|
vs_id = binding['lb_vs_id']
|
|
app_profile_id = binding['app_profile_id']
|
|
lb_binding = nsx_db.get_nsx_lbaas_loadbalancer_binding(
|
|
context.session, lb_id)
|
|
if lb_binding:
|
|
try:
|
|
lbs_id = lb_binding.get('lb_service_id')
|
|
lb_service = service_client.get(lbs_id)
|
|
vs_list = lb_service.get('virtual_server_ids')
|
|
if vs_list and vs_id in vs_list:
|
|
service_client.remove_virtual_server(lbs_id, vs_id)
|
|
except (nsxlib_exc.ResourceNotFound,
|
|
nsx_exc.NsxResourceNotFound):
|
|
LOG.error('Loadbalancing service %s not found at backend' %
|
|
lbs_id)
|
|
except nsxlib_exc.ManagerError:
|
|
self.lbv2_driver.listener.failed_completion(context,
|
|
listener)
|
|
msg = (_('Failed to remove virtual server: %(listener)s '
|
|
'from lb service %(lbs)s') %
|
|
{'listener': listener.id, 'lbs': lbs_id})
|
|
raise n_exc.BadRequest(resource='lbaas-listener', msg=msg)
|
|
try:
|
|
if listener.default_pool_id:
|
|
vs_client.update(vs_id, pool_id='')
|
|
# Update pool binding to disassociate virtual server
|
|
pool_binding = nsx_db.get_nsx_lbaas_pool_binding(
|
|
context.session, lb_id, listener.default_pool_id)
|
|
if pool_binding:
|
|
nsx_db.update_nsx_lbaas_pool_binding(
|
|
context.session, lb_id, listener.default_pool_id,
|
|
None)
|
|
vs_client.delete(vs_id)
|
|
except (nsxlib_exc.ResourceNotFound, nsx_exc.NsxResourceNotFound):
|
|
LOG.error("virtual server not found on nsx: %(vs)s" %
|
|
{'vs': vs_id})
|
|
except nsxlib_exc.ManagerError:
|
|
self.lbv2_driver.listener.failed_completion(context,
|
|
listener)
|
|
msg = (_('Failed to delete virtual server: %(listener)s') %
|
|
{'listener': listener.id})
|
|
raise n_exc.BadRequest(resource='lbaas-listener', msg=msg)
|
|
try:
|
|
app_client.delete(app_profile_id)
|
|
except (nsxlib_exc.ResourceNotFound, nsx_exc.NsxResourceNotFound):
|
|
LOG.error("application profile not found on nsx: %s" %
|
|
app_profile_id)
|
|
except nsxlib_exc.ManagerError:
|
|
self.lbv2_driver.listener.failed_completion(context,
|
|
listener)
|
|
msg = (_('Failed to delete application profile: %(app)s') %
|
|
{'app': app_profile_id})
|
|
raise n_exc.BadRequest(resource='lbaas-listener', msg=msg)
|
|
|
|
# Delete imported NSX cert if there is any
|
|
cert_tags = [{'scope': lb_const.LB_LISTENER_TYPE,
|
|
'tag': listener.id}]
|
|
results = self.core_plugin.nsxlib.search_by_tags(
|
|
tags=cert_tags)
|
|
# Only delete object related to certificate used by listener
|
|
for obj in results['results']:
|
|
if obj.get('resource_type') in lb_const.LB_CERT_RESOURCE_TYPE:
|
|
tm_client = self.core_plugin.nsxlib.trust_management
|
|
try:
|
|
tm_client.delete_cert(obj['id'])
|
|
except nsxlib_exc.ManagerError:
|
|
LOG.error("Exception thrown when trying to delete "
|
|
"certificate: %(cert)s",
|
|
{'cert': obj['id']})
|
|
|
|
nsx_db.delete_nsx_lbaas_listener_binding(
|
|
context.session, lb_id, listener.id)
|
|
|
|
self.lbv2_driver.listener.successful_completion(
|
|
context, listener, delete=True)
|