371 lines
15 KiB
Python
371 lines
15 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 copy
|
|
|
|
from oslo_log import helpers as log_helpers
|
|
from oslo_log import log as logging
|
|
from oslo_utils import excutils
|
|
|
|
from neutron_lib import exceptions as n_exc
|
|
|
|
from vmware_nsx._i18n import _
|
|
from vmware_nsx.common import exceptions as nsxv_exc
|
|
from vmware_nsx.common import locking
|
|
from vmware_nsx.db import nsxv_db
|
|
from vmware_nsx.plugins.nsx_v.vshield.common import exceptions as vcns_exc
|
|
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_v import lbaas_common as lb_v_common
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
def listener_to_edge_app_profile(listener, edge_cert_id):
|
|
edge_app_profile = {
|
|
'insertXForwardedFor': False,
|
|
'name': listener['id'],
|
|
'serverSslEnabled': False,
|
|
'sslPassthrough': False,
|
|
'template': lb_const.PROTOCOL_MAP[listener['protocol']],
|
|
}
|
|
|
|
if (listener['protocol'] == lb_const.LB_PROTOCOL_HTTPS or
|
|
listener['protocol'] == lb_const.LB_PROTOCOL_TERMINATED_HTTPS):
|
|
if edge_cert_id:
|
|
edge_app_profile['clientSsl'] = {
|
|
'caCertificate': [],
|
|
'clientAuth': 'ignore',
|
|
'crlCertificate': [],
|
|
'serviceCertificate': [edge_cert_id]}
|
|
else:
|
|
edge_app_profile['sslPassthrough'] = True
|
|
|
|
if (listener.get('default_pool') and
|
|
listener['default_pool'].get('session_persistence')):
|
|
pool_sess_persist = listener['default_pool']['session_persistence']
|
|
sess_persist_type = pool_sess_persist['type']
|
|
persistence = {
|
|
'method':
|
|
lb_const.SESSION_PERSISTENCE_METHOD_MAP.get(
|
|
sess_persist_type)}
|
|
|
|
if (sess_persist_type in
|
|
lb_const.SESSION_PERSISTENCE_COOKIE_MAP):
|
|
cookie_name = pool_sess_persist.get('cookie_name', None)
|
|
if cookie_name is None:
|
|
cookie_name = lb_const.SESSION_PERSISTENCE_DEFAULT_COOKIE_NAME
|
|
persistence.update({
|
|
'cookieName': cookie_name,
|
|
'cookieMode': lb_const.SESSION_PERSISTENCE_COOKIE_MAP[
|
|
sess_persist_type]})
|
|
|
|
edge_app_profile['persistence'] = persistence
|
|
|
|
return edge_app_profile
|
|
|
|
|
|
def listener_to_edge_vse(context, listener, vip_address, default_pool,
|
|
app_profile_id):
|
|
if listener['connection_limit']:
|
|
connection_limit = max(0, listener['connection_limit'])
|
|
else:
|
|
connection_limit = 0
|
|
|
|
vse = {
|
|
'name': 'vip_' + listener['id'],
|
|
'description': listener['description'],
|
|
'ipAddress': vip_address,
|
|
'protocol': lb_const.PROTOCOL_MAP[listener['protocol']],
|
|
'port': listener['protocol_port'],
|
|
'connectionLimit': connection_limit,
|
|
'defaultPoolId': default_pool,
|
|
'accelerationEnabled': (
|
|
listener['protocol'] == lb_const.LB_PROTOCOL_TCP),
|
|
'applicationProfileId': app_profile_id,
|
|
'enabled': listener['admin_state_up']}
|
|
|
|
# Add the L7 policies
|
|
if listener['l7_policies']:
|
|
app_rule_ids = []
|
|
for pol in listener['l7_policies']:
|
|
binding = nsxv_db.get_nsxv_lbaas_l7policy_binding(
|
|
context.session, pol['id'])
|
|
if binding:
|
|
app_rule_ids.append(binding['edge_app_rule_id'])
|
|
vse['applicationRuleId'] = app_rule_ids
|
|
|
|
return vse
|
|
|
|
|
|
def update_app_profile(vcns, context, listener, edge_id, edge_cert_id=None):
|
|
lb_id = listener['loadbalancer_id']
|
|
listener_binding = nsxv_db.get_nsxv_lbaas_listener_binding(
|
|
context.session, lb_id, listener['id'])
|
|
if not listener_binding:
|
|
return
|
|
app_profile_id = listener_binding['app_profile_id']
|
|
app_profile = listener_to_edge_app_profile(listener, edge_cert_id)
|
|
with locking.LockManager.get_lock(edge_id):
|
|
vcns.update_app_profile(
|
|
edge_id, app_profile_id, app_profile)
|
|
return app_profile_id
|
|
|
|
|
|
class EdgeListenerManagerFromDict(base_mgr.EdgeLoadbalancerBaseManager):
|
|
@log_helpers.log_method_call
|
|
def __init__(self, vcns_driver):
|
|
super(EdgeListenerManagerFromDict, self).__init__(vcns_driver)
|
|
|
|
def _upload_certificate(self, context, edge_id, cert_id, certificate):
|
|
cert_binding = nsxv_db.get_nsxv_lbaas_certificate_binding(
|
|
context.session, cert_id, edge_id)
|
|
if cert_binding:
|
|
return cert_binding['edge_cert_id']
|
|
|
|
request = {
|
|
'pemEncoding': certificate.get('certificate'),
|
|
'privateKey': certificate.get('private_key')}
|
|
passphrase = certificate.get('passphrase')
|
|
if passphrase:
|
|
request['passphrase'] = passphrase
|
|
cert_obj = self.vcns.upload_edge_certificate(edge_id, request)[1]
|
|
cert_list = cert_obj.get('certificates', {})
|
|
if cert_list:
|
|
if len(cert_list) > 1:
|
|
LOG.warning(
|
|
'Certificate object contains multiple certificates. '
|
|
'Using first signed certificate of the bundle')
|
|
edge_cert_id = None
|
|
for cert in cert_list:
|
|
if cert['certificateType'] == 'certificate_signed':
|
|
edge_cert_id = cert['objectId']
|
|
break
|
|
if not edge_cert_id:
|
|
error = _("No signed certificate found in certificate bundle")
|
|
raise nsxv_exc.NsxPluginException(err_msg=error)
|
|
else:
|
|
error = _("Failed to upload a certificate to edge %s") % edge_id
|
|
raise nsxv_exc.NsxPluginException(err_msg=error)
|
|
nsxv_db.add_nsxv_lbaas_certificate_binding(
|
|
context.session, cert_id, edge_id, edge_cert_id)
|
|
return edge_cert_id
|
|
|
|
def create(self, context, listener, completor, certificate=None):
|
|
default_pool = None
|
|
|
|
lb_id = listener['loadbalancer_id']
|
|
lb_binding = nsxv_db.get_nsxv_lbaas_loadbalancer_binding(
|
|
context.session, lb_id)
|
|
edge_id = lb_binding['edge_id']
|
|
|
|
# Validate the listener protocol
|
|
if (listener.get('protocol') and
|
|
listener['protocol'] not in lb_const.PROTOCOL_MAP):
|
|
completor(success=False)
|
|
msg = (_("Listener protocol %s is not supported") %
|
|
listener['protocol'])
|
|
raise n_exc.BadRequest(resource='edge-lbaas', msg=msg)
|
|
|
|
if listener.get('default_pool') and listener['default_pool'].get('id'):
|
|
pool_binding = nsxv_db.get_nsxv_lbaas_pool_binding(
|
|
context.session, lb_id, listener['default_pool']['id'])
|
|
if pool_binding:
|
|
default_pool = pool_binding['edge_pool_id']
|
|
|
|
edge_cert_id = None
|
|
if certificate:
|
|
try:
|
|
edge_cert_id = self._upload_certificate(
|
|
context, edge_id, certificate['ref'],
|
|
certificate)
|
|
except Exception:
|
|
with excutils.save_and_reraise_exception():
|
|
completor(success=False)
|
|
|
|
app_profile = listener_to_edge_app_profile(listener, edge_cert_id)
|
|
app_profile_id = None
|
|
|
|
try:
|
|
with locking.LockManager.get_lock(edge_id):
|
|
h = (self.vcns.create_app_profile(edge_id, app_profile))[0]
|
|
app_profile_id = lb_v_common.extract_resource_id(h['location'])
|
|
except vcns_exc.VcnsApiException:
|
|
with excutils.save_and_reraise_exception():
|
|
completor(success=False)
|
|
LOG.error('Failed to create app profile on edge: %s',
|
|
lb_binding['edge_id'])
|
|
|
|
vse = listener_to_edge_vse(context, listener,
|
|
lb_binding['vip_address'],
|
|
default_pool,
|
|
app_profile_id)
|
|
|
|
try:
|
|
with locking.LockManager.get_lock(edge_id):
|
|
h = self.vcns.create_vip(edge_id, vse)[0]
|
|
edge_vse_id = lb_v_common.extract_resource_id(h['location'])
|
|
|
|
nsxv_db.add_nsxv_lbaas_listener_binding(context.session,
|
|
lb_id,
|
|
listener['id'],
|
|
app_profile_id,
|
|
edge_vse_id)
|
|
completor(success=True)
|
|
|
|
except vcns_exc.VcnsApiException:
|
|
with excutils.save_and_reraise_exception():
|
|
completor(success=False)
|
|
LOG.error('Failed to create vip on Edge: %s', edge_id)
|
|
self.vcns.delete_app_profile(edge_id, app_profile_id)
|
|
|
|
def update(self, context, old_listener, new_listener, completor,
|
|
certificate=None):
|
|
default_pool = None
|
|
if (new_listener.get('default_pool') and
|
|
new_listener['default_pool'].get('id')):
|
|
pool_binding = nsxv_db.get_nsxv_lbaas_pool_binding(
|
|
context.session, new_listener['loadbalancer_id'],
|
|
new_listener['default_pool']['id'])
|
|
if pool_binding:
|
|
default_pool = pool_binding['edge_pool_id']
|
|
else:
|
|
LOG.error("Couldn't find pool binding for pool %s",
|
|
new_listener['default_pool']['id'])
|
|
|
|
lb_id = new_listener['loadbalancer_id']
|
|
listener_binding = nsxv_db.get_nsxv_lbaas_listener_binding(
|
|
context.session, lb_id, new_listener['id'])
|
|
lb_binding = nsxv_db.get_nsxv_lbaas_loadbalancer_binding(
|
|
context.session, lb_id)
|
|
edge_id = lb_binding['edge_id']
|
|
|
|
edge_cert_id = None
|
|
if certificate:
|
|
if (lb_common.get_listener_cert_ref(old_listener) !=
|
|
lb_common.get_listener_cert_ref(new_listener)):
|
|
try:
|
|
edge_cert_id = self._upload_certificate(
|
|
context, edge_id,
|
|
certificate['ref'],
|
|
certificate)
|
|
except Exception:
|
|
with excutils.save_and_reraise_exception():
|
|
completor(success=False)
|
|
else:
|
|
cert_binding = nsxv_db.get_nsxv_lbaas_certificate_binding(
|
|
context.session, certificate['ref'],
|
|
edge_id)
|
|
edge_cert_id = cert_binding['edge_cert_id']
|
|
|
|
try:
|
|
app_profile_id = update_app_profile(
|
|
self.vcns, context, new_listener,
|
|
edge_id, edge_cert_id=edge_cert_id)
|
|
vse = listener_to_edge_vse(context, new_listener,
|
|
lb_binding['vip_address'],
|
|
default_pool,
|
|
app_profile_id)
|
|
|
|
with locking.LockManager.get_lock(edge_id):
|
|
self.vcns.update_vip(edge_id, listener_binding['vse_id'], vse)
|
|
|
|
completor(success=True)
|
|
except vcns_exc.VcnsApiException:
|
|
with excutils.save_and_reraise_exception():
|
|
completor(success=False)
|
|
LOG.error('Failed to update app profile on edge: %s',
|
|
edge_id)
|
|
|
|
def delete(self, context, listener, completor):
|
|
lb_id = listener['loadbalancer_id']
|
|
listener_binding = nsxv_db.get_nsxv_lbaas_listener_binding(
|
|
context.session, lb_id, listener['id'])
|
|
lb_binding = nsxv_db.get_nsxv_lbaas_loadbalancer_binding(
|
|
context.session, lb_id)
|
|
|
|
if lb_binding and listener_binding:
|
|
edge_id = lb_binding['edge_id']
|
|
edge_vse_id = listener_binding['vse_id']
|
|
app_profile_id = listener_binding['app_profile_id']
|
|
|
|
try:
|
|
with locking.LockManager.get_lock(edge_id):
|
|
self.vcns.delete_vip(edge_id, edge_vse_id)
|
|
|
|
except (vcns_exc.ResourceNotFound, vcns_exc.RequestBad):
|
|
LOG.error('vip not found on edge: %s', edge_id)
|
|
except vcns_exc.VcnsApiException:
|
|
LOG.error('Failed to delete vip on edge: %s', edge_id)
|
|
|
|
try:
|
|
with locking.LockManager.get_lock(edge_id):
|
|
self.vcns.delete_app_profile(edge_id, app_profile_id)
|
|
except (vcns_exc.ResourceNotFound, vcns_exc.RequestBad):
|
|
LOG.error('app profile not found on edge: %s', edge_id)
|
|
except vcns_exc.VcnsApiException:
|
|
LOG.error('Failed to delete app profile on Edge: %s', edge_id)
|
|
|
|
nsxv_db.del_nsxv_lbaas_listener_binding(context.session, lb_id,
|
|
listener['id'])
|
|
|
|
completor(success=True)
|
|
|
|
def delete_cascade(self, context, listener, completor):
|
|
self.delete(context, listener, completor)
|
|
|
|
|
|
def stats_getter(context, core_plugin, ignore_list=None):
|
|
"""Update Octavia statistics for each listener (virtual server)"""
|
|
stat_list = []
|
|
vcns = core_plugin.nsx_v.vcns
|
|
# go over all LB edges
|
|
bindings = nsxv_db.get_nsxv_lbaas_loadbalancer_bindings(context.session)
|
|
for binding in bindings:
|
|
lb_id = binding['loadbalancer_id']
|
|
if ignore_list and lb_id in ignore_list:
|
|
continue
|
|
edge_id = binding['edge_id']
|
|
|
|
try:
|
|
lb_stats = vcns.get_loadbalancer_statistics(edge_id)
|
|
|
|
virtual_servers_stats = lb_stats[1].get('virtualServer', [])
|
|
for vs_stats in virtual_servers_stats:
|
|
# Get the stats of the virtual server
|
|
stats = copy.copy(lb_const.LB_EMPTY_STATS)
|
|
stats['bytes_in'] += vs_stats.get('bytesIn', 0)
|
|
stats['bytes_out'] += vs_stats.get('bytesOut', 0)
|
|
stats['active_connections'] += vs_stats.get('curSessions', 0)
|
|
stats['total_connections'] += vs_stats.get('totalSessions', 0)
|
|
stats['request_errors'] = 0 # currently unsupported
|
|
|
|
# Find the listener Id
|
|
vs_id = vs_stats.get('virtualServerId')
|
|
list_bind = nsxv_db.get_nsxv_lbaas_listener_binding_by_vse(
|
|
context.session, lb_id, vs_id)
|
|
if not list_bind:
|
|
continue
|
|
stats['id'] = list_bind['listener_id']
|
|
|
|
stat_list.append(stats)
|
|
|
|
except vcns_exc.VcnsApiException as e:
|
|
LOG.warning('Failed to read load balancer statistics for %s: %s',
|
|
edge_id, e)
|
|
|
|
return stat_list
|