
Current resource def for Lb App Profiles (HTTP, TCP, UDP) and their counterparts in MP are incomplete and messy. Refactored related logic to better separate different attributes for each profile type. Change-Id: I11d0752484700cb55168a2fca5bf1a88926c8b81
516 lines
21 KiB
Python
516 lines
21 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 oslo_log import log as logging
|
|
|
|
from vmware_nsxlib.v3 import exceptions as nsxlib_exc
|
|
from vmware_nsxlib.v3 import nsx_constants
|
|
from vmware_nsxlib.v3 import utils
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class ApplicationProfileTypes(object):
|
|
"""LoadBalancer Application Profile types"""
|
|
|
|
HTTP = "LbHttpProfile"
|
|
FAST_TCP = "LbFastTcpProfile"
|
|
FAST_UDP = "LbFastUdpProfile"
|
|
|
|
|
|
class PersistenceProfileTypes(object):
|
|
"""LoadBalancer Persistence Profile types"""
|
|
|
|
COOKIE = "LbCookiePersistenceProfile"
|
|
SOURCE_IP = "LbSourceIpPersistenceProfile"
|
|
|
|
|
|
class MonitorTypes(object):
|
|
"""LoadBalancer Monitor types"""
|
|
|
|
HTTP = "LbHttpMonitor"
|
|
HTTPS = "LbHttpsMonitor"
|
|
ICMP = "LbIcmpMonitor"
|
|
PASSIVE = "LbPassiveMonitor"
|
|
TCP = "LbTcpMonitor"
|
|
UDP = "LbUdpMonitor"
|
|
|
|
|
|
class LoadBalancerBase(utils.NsxLibApiBase):
|
|
resource = ''
|
|
|
|
@staticmethod
|
|
def _build_args(body, display_name=None, description=None, tags=None,
|
|
resource_type=None, **kwargs):
|
|
if display_name:
|
|
body['display_name'] = display_name
|
|
if description:
|
|
body['description'] = description
|
|
if tags:
|
|
body['tags'] = tags
|
|
if resource_type:
|
|
body['resource_type'] = resource_type
|
|
body.update(kwargs)
|
|
return body
|
|
|
|
def add_to_list(self, resource_id, item_id, item_key):
|
|
"""Add item_id to resource item_key list
|
|
|
|
:param resource_id: resource id, e.g. pool_id, virtual_server_id
|
|
:param item_id: item to be added to the list
|
|
:param item_key: item list in the resource, e.g. rule_ids in
|
|
virtual server
|
|
:return: client update response
|
|
"""
|
|
# Using internal method so we can access max_attempts in the decorator
|
|
@utils.retry_upon_exception(
|
|
nsxlib_exc.StaleRevision,
|
|
max_attempts=self.client.max_attempts)
|
|
def do_update():
|
|
object_url = self.resource + '/' + resource_id
|
|
body = self.client.get(object_url)
|
|
if item_key in body:
|
|
item_list = body[item_key]
|
|
if item_id not in item_list:
|
|
item_list.append(item_id)
|
|
else:
|
|
LOG.error('Item %s is already in resource %s',
|
|
item_id, item_key)
|
|
return body
|
|
else:
|
|
item_list = [item_id]
|
|
body[item_key] = item_list
|
|
return self.client.update(object_url, body)
|
|
return do_update()
|
|
|
|
def remove_from_list(self, resource_id, item_id, item_key):
|
|
"""Remove item_id from resource item_key list
|
|
|
|
:param resource_id: resource id, e.g. pool_id, virtual_server_id
|
|
:param item_id: item to be removed from the list
|
|
:param item_key: item list in the resource, e.g. rule_ids in
|
|
virtual server
|
|
:return: client update response
|
|
"""
|
|
# Using internal method so we can access max_attempts in the decorator
|
|
@utils.retry_upon_exception(
|
|
nsxlib_exc.StaleRevision,
|
|
max_attempts=self.client.max_attempts)
|
|
def do_update():
|
|
object_url = self.resource + '/' + resource_id
|
|
body = self.client.get(object_url)
|
|
item_list = body.get(item_key)
|
|
if item_list and item_id in item_list:
|
|
item_list.remove(item_id)
|
|
body[item_key] = item_list
|
|
return self.client.update(object_url, body)
|
|
else:
|
|
ops = ('removing item %s from resource %s %s as it is not in '
|
|
'the list', item_id, item_key, item_list)
|
|
raise nsxlib_exc.ResourceNotFound(
|
|
manager=self.client.nsx_api_managers, operation=ops)
|
|
return do_update()
|
|
|
|
def create(self, display_name=None, description=None, tags=None,
|
|
resource_type=None, **kwargs):
|
|
orig_body = {}
|
|
body = self._build_args(orig_body, display_name, description, tags,
|
|
resource_type, **kwargs)
|
|
return self.client.create(self.resource, body)
|
|
|
|
def list(self):
|
|
return self.client.list(resource=self.resource)
|
|
|
|
def get(self, object_id):
|
|
object_url = self.resource + '/' + object_id
|
|
return self.client.get(object_url)
|
|
|
|
def update(self, object_id, display_name=None, description=None,
|
|
tags=None, resource_type=None, **kwargs):
|
|
# Using internal method so we can access max_attempts in the decorator
|
|
@utils.retry_upon_exception(
|
|
nsxlib_exc.StaleRevision,
|
|
max_attempts=self.client.max_attempts)
|
|
def do_update():
|
|
object_url = self.resource + '/' + object_id
|
|
orig_body = self.client.get(object_url)
|
|
body = self._build_args(orig_body, display_name, description, tags,
|
|
resource_type, **kwargs)
|
|
return self.client.update(object_url, body)
|
|
return do_update()
|
|
|
|
def delete(self, object_id):
|
|
object_url = self.resource + '/' + object_id
|
|
return self.client.delete(object_url)
|
|
|
|
|
|
class ApplicationProfile(LoadBalancerBase):
|
|
resource = 'loadbalancer/application-profiles'
|
|
|
|
@staticmethod
|
|
def _build_args(body, display_name=None, description=None, tags=None,
|
|
resource_type=None, **kwargs):
|
|
if display_name:
|
|
body['display_name'] = display_name
|
|
if description:
|
|
body['description'] = description
|
|
if tags:
|
|
body['tags'] = tags
|
|
if resource_type is None:
|
|
return body
|
|
if resource_type in [ApplicationProfileTypes.HTTP,
|
|
ApplicationProfileTypes.FAST_TCP,
|
|
ApplicationProfileTypes.FAST_UDP]:
|
|
body['resource_type'] = resource_type
|
|
extra_args = ['idle_timeout']
|
|
if resource_type == ApplicationProfileTypes.HTTP:
|
|
extra_args.extend(
|
|
['http_redirect_to', 'http_redirect_to_https', 'ntlm',
|
|
'request_body_size', 'request_header_size',
|
|
'response_header_size', 'response_timeout',
|
|
'x_forwarded_for'])
|
|
elif resource_type == ApplicationProfileTypes.FAST_TCP:
|
|
extra_args.extend(
|
|
['close_timeout', 'ha_flow_mirroring_enabled'])
|
|
elif resource_type == ApplicationProfileTypes.FAST_UDP:
|
|
extra_args.extend(['flow_mirroring_enabled'])
|
|
return utils.build_extra_args(body, extra_args, **kwargs)
|
|
else:
|
|
raise nsxlib_exc.InvalidInput(
|
|
operation='create_application_profile',
|
|
arg_val=resource_type,
|
|
arg_name='resource_type')
|
|
|
|
|
|
class PersistenceProfile(LoadBalancerBase):
|
|
resource = 'loadbalancer/persistence-profiles'
|
|
|
|
@staticmethod
|
|
def _build_args(body, display_name=None, description=None, tags=None,
|
|
resource_type=None, **kwargs):
|
|
if display_name:
|
|
body['display_name'] = display_name
|
|
if description:
|
|
body['description'] = description
|
|
if tags:
|
|
body['tags'] = tags
|
|
if resource_type == PersistenceProfileTypes.COOKIE:
|
|
body['resource_type'] = resource_type
|
|
extra_args = ['cookie_domain', 'cookie_fallback', 'cookie_garble',
|
|
'cookie_mode', 'cookie_name', 'cookie_path',
|
|
'cookie_time']
|
|
return utils.build_extra_args(body, extra_args, **kwargs)
|
|
elif resource_type == PersistenceProfileTypes.SOURCE_IP:
|
|
body['resource_type'] = resource_type
|
|
extra_args = ['ha_persistence_mirroring_enabled', 'purge',
|
|
'timeout']
|
|
return utils.build_extra_args(body, extra_args, **kwargs)
|
|
else:
|
|
raise nsxlib_exc.InvalidInput(
|
|
operation='create_persistence_profile',
|
|
arg_val=resource_type,
|
|
arg_name='resource_type')
|
|
|
|
|
|
class Rule(LoadBalancerBase):
|
|
resource = 'loadbalancer/rules'
|
|
|
|
|
|
class ClientSslProfile(LoadBalancerBase):
|
|
resource = 'loadbalancer/client-ssl-profiles'
|
|
|
|
|
|
class ServerSslProfile(LoadBalancerBase):
|
|
resource = 'loadbalancer/server-ssl-profiles'
|
|
|
|
|
|
class Monitor(LoadBalancerBase):
|
|
resource = 'loadbalancer/monitors'
|
|
|
|
@staticmethod
|
|
def _build_args(body, display_name=None, description=None, tags=None,
|
|
resource_type=None, **kwargs):
|
|
if display_name:
|
|
body['display_name'] = display_name
|
|
if description:
|
|
body['description'] = description
|
|
if tags:
|
|
body['tags'] = tags
|
|
if resource_type == MonitorTypes.HTTP:
|
|
body['resource_type'] = resource_type
|
|
extra_args = ['fall_count', 'interval', 'monitor_port',
|
|
'request_body', 'request_method', 'request_url',
|
|
'request_version', 'response_body',
|
|
'response_status_codes', 'rise_count', 'timeout']
|
|
return utils.build_extra_args(body, extra_args, **kwargs)
|
|
elif resource_type == MonitorTypes.HTTPS:
|
|
body['resource_type'] = resource_type
|
|
extra_args = ['certificate_chain_depth', 'ciphers',
|
|
'client_certificate_id', 'fall_count', 'interval',
|
|
'monitor_port', 'protocols', 'request_body',
|
|
'request_method', 'request_url', 'request_version',
|
|
'response_body', 'response_status_codes',
|
|
'rise_count', 'server_auth', 'server_auth_ca_ids',
|
|
'server_auth_crl_ids', 'timeout']
|
|
return utils.build_extra_args(body, extra_args, **kwargs)
|
|
elif resource_type == MonitorTypes.ICMP:
|
|
body['resource_type'] = resource_type
|
|
extra_args = ['data_length', 'fall_count', 'interval',
|
|
'monitor_port', 'rise_count', 'timeout']
|
|
return utils.build_extra_args(body, extra_args, **kwargs)
|
|
elif resource_type == MonitorTypes.PASSIVE:
|
|
body['resource_type'] = resource_type
|
|
extra_args = ['max_fails', 'timeout']
|
|
return utils.build_extra_args(body, extra_args, **kwargs)
|
|
elif (resource_type == MonitorTypes.TCP or
|
|
resource_type == MonitorTypes.UDP):
|
|
body['resource_type'] = resource_type
|
|
extra_args = ['fall_count', 'interval', 'monitor_port', 'receive',
|
|
'rise_count', 'send', 'timeout']
|
|
return utils.build_extra_args(body, extra_args, **kwargs)
|
|
else:
|
|
raise nsxlib_exc.InvalidInput(
|
|
operation='create_monitor',
|
|
arg_val=resource_type,
|
|
arg_name='resource_type')
|
|
|
|
|
|
class Pool(LoadBalancerBase):
|
|
resource = 'loadbalancer/pools'
|
|
|
|
def update_pool_with_members(self, pool_id, members):
|
|
# Using internal method so we can access max_attempts in the decorator
|
|
@utils.retry_upon_exception(
|
|
nsxlib_exc.StaleRevision,
|
|
max_attempts=self.client.max_attempts)
|
|
def do_update():
|
|
object_url = self.resource + '/' + pool_id
|
|
body = self.client.get(object_url)
|
|
body['members'] = members
|
|
return self.client.update(object_url, body)
|
|
return do_update()
|
|
|
|
def add_monitor_to_pool(self, pool_id, monitor_id):
|
|
self.add_to_list(pool_id, monitor_id, 'active_monitor_ids')
|
|
|
|
def remove_monitor_from_pool(self, pool_id, monitor_id):
|
|
self.remove_from_list(pool_id, monitor_id, 'active_monitor_ids')
|
|
|
|
|
|
class VirtualServer(LoadBalancerBase):
|
|
resource = 'loadbalancer/virtual-servers'
|
|
|
|
def update_virtual_server_with_pool(self, virtual_server_id, pool_id):
|
|
# Using internal method so we can access max_attempts in the decorator
|
|
@utils.retry_upon_exception(
|
|
nsxlib_exc.StaleRevision,
|
|
max_attempts=self.client.max_attempts)
|
|
def do_update():
|
|
object_url = self.resource + '/' + virtual_server_id
|
|
body = self.client.get(object_url)
|
|
body['pool_id'] = pool_id
|
|
return self.client.update(object_url, body)
|
|
return do_update()
|
|
|
|
def update_virtual_server_with_profiles(self, virtual_server_id,
|
|
application_profile_id=None,
|
|
persistence_profile_id=None,
|
|
ip_protocol=None):
|
|
# Using internal method so we can access max_attempts in the decorator
|
|
@utils.retry_upon_exception(
|
|
nsxlib_exc.StaleRevision,
|
|
max_attempts=self.client.max_attempts)
|
|
def do_update():
|
|
object_url = self.resource + '/' + virtual_server_id
|
|
body = self.client.get(object_url)
|
|
if application_profile_id:
|
|
body['application_profile_id'] = application_profile_id
|
|
if persistence_profile_id:
|
|
body['persistence_profile_id'] = persistence_profile_id
|
|
# In case the application profile is updated and its protocol
|
|
# is updated as well, backend requires us to pass the new
|
|
# protocol in the virtual server body.
|
|
if ip_protocol:
|
|
body['ip_protocol'] = ip_protocol
|
|
return self.client.update(object_url, body)
|
|
return do_update()
|
|
|
|
def update_virtual_server_with_vip(self, virtual_server_id, vip):
|
|
# Using internal method so we can access max_attempts in the decorator
|
|
@utils.retry_upon_exception(
|
|
nsxlib_exc.StaleRevision,
|
|
max_attempts=self.client.max_attempts)
|
|
def do_update():
|
|
object_url = self.resource + '/' + virtual_server_id
|
|
body = self.client.get(object_url)
|
|
body['ip_address'] = vip
|
|
return self.client.update(object_url, body)
|
|
return do_update()
|
|
|
|
def add_rule(self, vs_id, rule_id):
|
|
self.add_to_list(vs_id, rule_id, 'rule_ids')
|
|
|
|
def remove_rule(self, vs_id, rule_id):
|
|
self.remove_from_list(vs_id, rule_id, 'rule_ids')
|
|
|
|
def add_client_ssl_profile_binding(self, virtual_server_id,
|
|
ssl_profile_id, default_certificate_id,
|
|
sni_certificate_ids=None, **kwargs):
|
|
# Using internal method so we can access max_attempts in the decorator
|
|
@utils.retry_upon_exception(
|
|
nsxlib_exc.StaleRevision,
|
|
max_attempts=self.client.max_attempts)
|
|
def do_update():
|
|
binding = {'ssl_profile_id': ssl_profile_id,
|
|
'default_certificate_id': default_certificate_id}
|
|
if sni_certificate_ids:
|
|
binding.update({'sni_certificate_ids': sni_certificate_ids})
|
|
|
|
valid_args = ['client_auth_ca_ids', 'client_auth_crl_ids',
|
|
'certificate_chain_depth', 'client_auth']
|
|
# Remove the args that is not in the valid_args list or the
|
|
# keyword argument doesn't have value.
|
|
for arg in kwargs:
|
|
if arg in valid_args and kwargs.get(arg):
|
|
binding[arg] = kwargs.get(arg)
|
|
object_url = self.resource + '/' + virtual_server_id
|
|
body = self.client.get(object_url)
|
|
body['client_ssl_profile_binding'] = binding
|
|
return self.client.update(object_url, body)
|
|
return do_update()
|
|
|
|
def add_server_ssl_profile_binding(self, virtual_server_id,
|
|
ssl_profile_id, **kwargs):
|
|
# Using internal method so we can access max_attempts in the decorator
|
|
@utils.retry_upon_exception(
|
|
nsxlib_exc.StaleRevision,
|
|
max_attempts=self.client.max_attempts)
|
|
def do_update():
|
|
binding = {'ssl_profile_id': ssl_profile_id}
|
|
|
|
valid_args = ['server_auth_ca_ids', 'server_auth_crl_ids',
|
|
'certificate_chain_depth', 'server_auth',
|
|
'client_certificate_id']
|
|
# Remove the args that is not in the valid_args list or the
|
|
# keyword argument doesn't have value.
|
|
for arg in kwargs:
|
|
if arg in valid_args and kwargs.get(arg):
|
|
binding[arg] = kwargs[arg]
|
|
object_url = self.resource + '/' + virtual_server_id
|
|
body = self.client.get(object_url)
|
|
body['server_ssl_profile_binding'] = binding
|
|
return self.client.update(object_url, body)
|
|
return do_update()
|
|
|
|
|
|
class Service(LoadBalancerBase):
|
|
resource = 'loadbalancer/services'
|
|
|
|
def _build_args(self, body, display_name=None, description=None,
|
|
tags=None, resource_type=None, **kwargs):
|
|
if display_name:
|
|
body['display_name'] = display_name
|
|
if description:
|
|
body['description'] = description
|
|
if tags:
|
|
body['tags'] = tags
|
|
if resource_type:
|
|
body['resource_type'] = resource_type
|
|
|
|
if ('relax_scale_validation' in kwargs and
|
|
not self.nsxlib.feature_supported(
|
|
nsx_constants.FEATURE_RELAX_SCALE_VALIDATION)):
|
|
kwargs.pop('relax_scale_validation')
|
|
LOG.warning("Ignoring relax_scale_validation for new "
|
|
"lb service %s: this feature is not supported.",
|
|
display_name)
|
|
body.update(kwargs)
|
|
return body
|
|
|
|
def update_service_with_virtual_servers(self, service_id,
|
|
virtual_server_ids):
|
|
# Using internal method so we can access max_attempts in the decorator
|
|
@utils.retry_upon_exception(
|
|
nsxlib_exc.StaleRevision,
|
|
max_attempts=self.client.max_attempts)
|
|
def do_update():
|
|
object_url = self.resource + '/' + service_id
|
|
body = self.client.get(object_url)
|
|
body['virtual_server_ids'] = virtual_server_ids
|
|
return self.client.update(object_url, body)
|
|
return do_update()
|
|
|
|
def update_service_with_attachment(self, service_id, logical_router_id,
|
|
tags=None):
|
|
# Using internal method so we can access max_attempts in the decorator
|
|
@utils.retry_upon_exception(
|
|
nsxlib_exc.StaleRevision,
|
|
max_attempts=self.client.max_attempts)
|
|
def do_update():
|
|
object_url = self.resource + '/' + service_id
|
|
body = self.client.get(object_url)
|
|
body['attachment'] = {'target_id': logical_router_id,
|
|
'target_type': 'LogicalRouter'}
|
|
if tags is not None:
|
|
body['tags'] = tags
|
|
return self.client.update(object_url, body)
|
|
return do_update()
|
|
|
|
def add_virtual_server(self, service_id, vs_id):
|
|
self.add_to_list(service_id, vs_id, 'virtual_server_ids')
|
|
|
|
def remove_virtual_server(self, service_id, vs_id):
|
|
self.remove_from_list(service_id, vs_id, 'virtual_server_ids')
|
|
|
|
def get_router_lb_service(self, nsx_router_id):
|
|
lb_services = self.list()['results']
|
|
for service in lb_services:
|
|
if service.get('attachment'):
|
|
if service['attachment']['target_id'] == nsx_router_id:
|
|
return service
|
|
|
|
def get_status(self, service_id):
|
|
object_url = '%s/%s/%s' % (self.resource, service_id, 'status')
|
|
return self.client.get(object_url)
|
|
|
|
def get_virtual_servers_status(self, service_id):
|
|
object_url = '%s/%s/%s/%s' % (self.resource, service_id,
|
|
'virtual-servers', 'status')
|
|
return self.client.get(object_url)
|
|
|
|
def get_stats(self, service_id, source='realtime'):
|
|
object_url = '%s/%s/%s?source=%s' % (self.resource, service_id,
|
|
'statistics', source)
|
|
return self.client.get(object_url)
|
|
|
|
def get_usage(self, service_id):
|
|
object_url = '%s/%s/%s' % (self.resource, service_id, 'usage')
|
|
return self.client.get(object_url)
|
|
|
|
|
|
class LoadBalancer(object):
|
|
"""This is the class that have all load balancer resource clients"""
|
|
|
|
def __init__(self, client, nsxlib_config=None, nsxlib=None):
|
|
self.service = Service(client, nsxlib_config, nsxlib)
|
|
self.virtual_server = VirtualServer(client, nsxlib_config)
|
|
self.pool = Pool(client, nsxlib_config)
|
|
self.monitor = Monitor(client, nsxlib_config)
|
|
self.application_profile = ApplicationProfile(client, nsxlib_config)
|
|
self.persistence_profile = PersistenceProfile(client, nsxlib_config)
|
|
self.client_ssl_profile = ClientSslProfile(client, nsxlib_config)
|
|
self.server_ssl_profile = ServerSslProfile(client, nsxlib_config)
|
|
self.rule = Rule(client, nsxlib_config)
|