VMWare NSXv: Common components
For Kilo, it was decided to split vendor code into stackforge repos, while services (*aas) code is moved to neutron-*aas repos. Some components are common to VMWare NSXv neutron plugin and services plugins. As we do not want to add dependencies between the neutron-lbaas, neutron-fwaas repos and the stackforge/vmware-nsx repo, (nor duplicate the code), we move these common components to oslo.vmware under oslo.vmware.network Change-Id: If3c98bc9431253768914316db6b48d1777518353 Partially-Implements: blueprint vmware-nsx-v
This commit is contained in:
parent
c50d927c07
commit
3356e2e147
0
oslo_vmware/network/__init__.py
Normal file
0
oslo_vmware/network/__init__.py
Normal file
0
oslo_vmware/network/nsx/__init__.py
Normal file
0
oslo_vmware/network/nsx/__init__.py
Normal file
0
oslo_vmware/network/nsx/nsxv/__init__.py
Normal file
0
oslo_vmware/network/nsx/nsxv/__init__.py
Normal file
0
oslo_vmware/network/nsx/nsxv/api/__init__.py
Normal file
0
oslo_vmware/network/nsx/nsxv/api/__init__.py
Normal file
598
oslo_vmware/network/nsx/nsxv/api/api.py
Normal file
598
oslo_vmware/network/nsx/nsxv/api/api.py
Normal file
@ -0,0 +1,598 @@
|
||||
# Copyright 2015 VMware, Inc
|
||||
#
|
||||
# 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 logging
|
||||
import time
|
||||
import xml.etree.ElementTree as et
|
||||
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
from oslo_vmware._i18n import _LI
|
||||
from oslo_vmware.network.nsx.nsxv.api import api_helper
|
||||
from oslo_vmware.network.nsx.nsxv.common import exceptions
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
HTTP_GET = "GET"
|
||||
HTTP_POST = "POST"
|
||||
HTTP_DELETE = "DELETE"
|
||||
HTTP_PUT = "PUT"
|
||||
URI_PREFIX = "/api/4.0/edges"
|
||||
|
||||
# FwaaS constants
|
||||
FIREWALL_SERVICE = "firewall/config"
|
||||
FIREWALL_RULE_RESOURCE = "rules"
|
||||
|
||||
# NSXv Constants
|
||||
FIREWALL_PREFIX = '/api/4.0/firewall/globalroot-0/config'
|
||||
SECURITYGROUP_PREFIX = '/api/2.0/services/securitygroup'
|
||||
VDN_PREFIX = '/api/2.0/vdn'
|
||||
SERVICES_PREFIX = '/api/2.0/services'
|
||||
|
||||
# LbaaS Constants
|
||||
LOADBALANCER_SERVICE = "loadbalancer/config"
|
||||
VIP_RESOURCE = "virtualservers"
|
||||
POOL_RESOURCE = "pools"
|
||||
MONITOR_RESOURCE = "monitors"
|
||||
APP_PROFILE_RESOURCE = "applicationprofiles"
|
||||
APP_RULE_RESOURCE = "applicationrules"
|
||||
|
||||
# IPsec VPNaaS Constants
|
||||
IPSEC_VPN_SERVICE = 'ipsec/config'
|
||||
|
||||
# Dhcp constants
|
||||
DHCP_SERVICE = "dhcp/config"
|
||||
DHCP_BINDING_RESOURCE = "bindings"
|
||||
|
||||
|
||||
class NsxvApi(object):
|
||||
|
||||
def __init__(self, address, user, password, retries=2):
|
||||
self.address = address
|
||||
self.user = user
|
||||
self.password = password
|
||||
self.retries = retries
|
||||
self.jsonapi_client = api_helper.NsxvApiHelper(address, user,
|
||||
password, 'json')
|
||||
self.xmlapi_client = api_helper.NsxvApiHelper(address, user,
|
||||
password, 'xml')
|
||||
|
||||
def _client_request(self, client, method, uri, params, headers,
|
||||
encode_params):
|
||||
retries = max(self.retries, 1)
|
||||
delay = 0.5
|
||||
for attempt in range(1, retries + 1):
|
||||
if attempt != 1:
|
||||
time.sleep(delay)
|
||||
delay = min(2 * delay, 60)
|
||||
try:
|
||||
return client(method, uri, params, headers, encode_params)
|
||||
except exceptions.ServiceConflict as e:
|
||||
if attempt == retries:
|
||||
raise e
|
||||
LOG.info(_LI('NSXv: conflict on request. Trying again.'))
|
||||
|
||||
def do_request(self, method, uri, params=None, format='json', **kwargs):
|
||||
LOG.debug("NsxvApi('%(method)s', '%(uri)s', '%(body)s')", {
|
||||
'method': method,
|
||||
'uri': uri,
|
||||
'body': params})
|
||||
headers = kwargs.get('headers')
|
||||
encode_params = kwargs.get('encode', True)
|
||||
if format == 'json':
|
||||
_client = self.jsonapi_client.request
|
||||
else:
|
||||
_client = self.xmlapi_client.request
|
||||
header, content = self._client_request(_client, method, uri, params,
|
||||
headers, encode_params)
|
||||
if content == '':
|
||||
return header, {}
|
||||
if kwargs.get('decode', True):
|
||||
content = jsonutils.loads(content)
|
||||
return header, content
|
||||
|
||||
def deploy_edge(self, request):
|
||||
uri = URI_PREFIX + "?async=true"
|
||||
return self.do_request(HTTP_POST, uri, request, decode=False)
|
||||
|
||||
def update_edge(self, edge_id, request):
|
||||
uri = "%s/%s?async=true" % (URI_PREFIX, edge_id)
|
||||
return self.do_request(HTTP_PUT, uri, request, decode=False)
|
||||
|
||||
def get_edge_id(self, job_id):
|
||||
uri = URI_PREFIX + "/jobs/%s" % job_id
|
||||
return self.do_request(HTTP_GET, uri, decode=True)
|
||||
|
||||
def get_edge_jobs(self, edge_id):
|
||||
uri = URI_PREFIX + "/%s/jobs" % edge_id
|
||||
return self.do_request(HTTP_GET, uri, decode=True)
|
||||
|
||||
def get_edge_deploy_status(self, edge_id):
|
||||
uri = URI_PREFIX + "/%s/status?getlatest=false" % edge_id
|
||||
return self.do_request(HTTP_GET, uri, decode="True")
|
||||
|
||||
def delete_edge(self, edge_id):
|
||||
uri = "%s/%s" % (URI_PREFIX, edge_id)
|
||||
return self.do_request(HTTP_DELETE, uri)
|
||||
|
||||
def add_vdr_internal_interface(self, edge_id, interface):
|
||||
uri = "%s/%s/interfaces?action=patch&async=true" % (URI_PREFIX,
|
||||
edge_id)
|
||||
return self.do_request(HTTP_POST, uri, interface, decode=True)
|
||||
|
||||
def update_vdr_internal_interface(
|
||||
self, edge_id, interface_index, interface):
|
||||
uri = "%s/%s/interfaces/%s?async=true" % (URI_PREFIX, edge_id,
|
||||
interface_index)
|
||||
return self.do_request(HTTP_PUT, uri, interface, decode=True)
|
||||
|
||||
def delete_vdr_internal_interface(self, edge_id, interface_index):
|
||||
uri = "%s/%s/interfaces/%d?async=true" % (URI_PREFIX, edge_id,
|
||||
interface_index)
|
||||
return self.do_request(HTTP_DELETE, uri, decode=True)
|
||||
|
||||
def get_interfaces(self, edge_id):
|
||||
uri = "%s/%s/vnics" % (URI_PREFIX, edge_id)
|
||||
return self.do_request(HTTP_GET, uri, decode=True)
|
||||
|
||||
def update_interface(self, edge_id, vnic):
|
||||
uri = "%s/%s/vnics/%d?async=true" % (URI_PREFIX, edge_id,
|
||||
vnic['index'])
|
||||
return self.do_request(HTTP_PUT, uri, vnic, decode=True)
|
||||
|
||||
def delete_interface(self, edge_id, vnic_index):
|
||||
uri = "%s/%s/vnics/%d?async=true" % (URI_PREFIX, edge_id, vnic_index)
|
||||
return self.do_request(HTTP_DELETE, uri, decode=True)
|
||||
|
||||
def get_nat_config(self, edge_id):
|
||||
uri = "%s/%s/nat/config" % (URI_PREFIX, edge_id)
|
||||
return self.do_request(HTTP_GET, uri, decode=True)
|
||||
|
||||
def update_nat_config(self, edge_id, nat):
|
||||
uri = "%s/%s/nat/config?async=true" % (URI_PREFIX, edge_id)
|
||||
return self.do_request(HTTP_PUT, uri, nat, decode=True)
|
||||
|
||||
def delete_nat_rule(self, edge_id, rule_id):
|
||||
uri = "%s/%s/nat/config/rules/%s" % (URI_PREFIX, edge_id, rule_id)
|
||||
return self.do_request(HTTP_DELETE, uri, decode=True)
|
||||
|
||||
def get_edge_status(self, edge_id):
|
||||
uri = "%s/%s/status?getlatest=false" % (URI_PREFIX, edge_id)
|
||||
return self.do_request(HTTP_GET, uri, decode=True)
|
||||
|
||||
def get_edges(self):
|
||||
uri = URI_PREFIX
|
||||
return self.do_request(HTTP_GET, uri, decode=True)
|
||||
|
||||
def get_edge_interfaces(self, edge_id):
|
||||
uri = "%s/%s/interfaces" % (URI_PREFIX, edge_id)
|
||||
return self.do_request(HTTP_GET, uri, decode=True)
|
||||
|
||||
def update_routes(self, edge_id, routes):
|
||||
uri = "%s/%s/routing/config/static?async=true" % (URI_PREFIX, edge_id)
|
||||
return self.do_request(HTTP_PUT, uri, routes)
|
||||
|
||||
def create_lswitch(self, lsconfig):
|
||||
uri = "/api/ws.v1/lswitch"
|
||||
return self.do_request(HTTP_POST, uri, lsconfig, decode=True)
|
||||
|
||||
def delete_lswitch(self, lswitch_id):
|
||||
uri = "/api/ws.v1/lswitch/%s" % lswitch_id
|
||||
return self.do_request(HTTP_DELETE, uri)
|
||||
|
||||
def get_loadbalancer_config(self, edge_id):
|
||||
uri = self._build_uri_path(edge_id, LOADBALANCER_SERVICE)
|
||||
return self.do_request(HTTP_GET, uri, decode=True)
|
||||
|
||||
def enable_service_loadbalancer(self, edge_id, config):
|
||||
uri = self._build_uri_path(edge_id, LOADBALANCER_SERVICE)
|
||||
return self.do_request(HTTP_PUT, uri, config)
|
||||
|
||||
def update_firewall(self, edge_id, fw_req):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, FIREWALL_SERVICE)
|
||||
uri += '?async=true'
|
||||
return self.do_request(HTTP_PUT, uri, fw_req)
|
||||
|
||||
def delete_firewall(self, edge_id):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, FIREWALL_SERVICE, None)
|
||||
uri += '?async=true'
|
||||
return self.do_request(HTTP_DELETE, uri)
|
||||
|
||||
def update_firewall_rule(self, edge_id, vcns_rule_id, fwr_req):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, FIREWALL_SERVICE,
|
||||
FIREWALL_RULE_RESOURCE,
|
||||
vcns_rule_id)
|
||||
return self.do_request(HTTP_PUT, uri, fwr_req)
|
||||
|
||||
def delete_firewall_rule(self, edge_id, vcns_rule_id):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, FIREWALL_SERVICE,
|
||||
FIREWALL_RULE_RESOURCE,
|
||||
vcns_rule_id)
|
||||
return self.do_request(HTTP_DELETE, uri)
|
||||
|
||||
def add_firewall_rule_above(self, edge_id, ref_vcns_rule_id, fwr_req):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, FIREWALL_SERVICE,
|
||||
FIREWALL_RULE_RESOURCE)
|
||||
uri += "?aboveRuleId=" + ref_vcns_rule_id
|
||||
return self.do_request(HTTP_POST, uri, fwr_req)
|
||||
|
||||
def add_firewall_rule(self, edge_id, fwr_req):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, FIREWALL_SERVICE,
|
||||
FIREWALL_RULE_RESOURCE)
|
||||
return self.do_request(HTTP_POST, uri, fwr_req)
|
||||
|
||||
def get_firewall(self, edge_id):
|
||||
uri = self._build_uri_path(edge_id, FIREWALL_SERVICE)
|
||||
return self.do_request(HTTP_GET, uri, decode=True)
|
||||
|
||||
def get_firewall_rule(self, edge_id, vcns_rule_id):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, FIREWALL_SERVICE,
|
||||
FIREWALL_RULE_RESOURCE,
|
||||
vcns_rule_id)
|
||||
return self.do_request(HTTP_GET, uri, decode=True)
|
||||
|
||||
#
|
||||
# Edge LBAAS call helper
|
||||
#
|
||||
def create_vip(self, edge_id, vip_new):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, LOADBALANCER_SERVICE,
|
||||
VIP_RESOURCE)
|
||||
return self.do_request(HTTP_POST, uri, vip_new)
|
||||
|
||||
def get_vip(self, edge_id, vip_vseid):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, LOADBALANCER_SERVICE,
|
||||
VIP_RESOURCE, vip_vseid)
|
||||
return self.do_request(HTTP_GET, uri, decode=True)
|
||||
|
||||
def update_vip(self, edge_id, vip_vseid, vip_new):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, LOADBALANCER_SERVICE,
|
||||
VIP_RESOURCE, vip_vseid)
|
||||
return self.do_request(HTTP_PUT, uri, vip_new)
|
||||
|
||||
def delete_vip(self, edge_id, vip_vseid):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, LOADBALANCER_SERVICE,
|
||||
VIP_RESOURCE, vip_vseid)
|
||||
return self.do_request(HTTP_DELETE, uri)
|
||||
|
||||
def create_pool(self, edge_id, pool_new):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, LOADBALANCER_SERVICE,
|
||||
POOL_RESOURCE)
|
||||
return self.do_request(HTTP_POST, uri, pool_new)
|
||||
|
||||
def get_pool(self, edge_id, pool_vseid):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, LOADBALANCER_SERVICE,
|
||||
POOL_RESOURCE, pool_vseid)
|
||||
return self.do_request(HTTP_GET, uri, decode=True)
|
||||
|
||||
def update_pool(self, edge_id, pool_vseid, pool_new):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, LOADBALANCER_SERVICE,
|
||||
POOL_RESOURCE, pool_vseid)
|
||||
return self.do_request(HTTP_PUT, uri, pool_new)
|
||||
|
||||
def delete_pool(self, edge_id, pool_vseid):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, LOADBALANCER_SERVICE,
|
||||
POOL_RESOURCE, pool_vseid)
|
||||
return self.do_request(HTTP_DELETE, uri)
|
||||
|
||||
def create_health_monitor(self, edge_id, monitor_new):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, LOADBALANCER_SERVICE,
|
||||
MONITOR_RESOURCE)
|
||||
return self.do_request(HTTP_POST, uri, monitor_new)
|
||||
|
||||
def get_health_monitor(self, edge_id, monitor_vseid):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, LOADBALANCER_SERVICE,
|
||||
MONITOR_RESOURCE, monitor_vseid)
|
||||
return self.do_request(HTTP_GET, uri, decode=True)
|
||||
|
||||
def update_health_monitor(self, edge_id, monitor_vseid, monitor_new):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, LOADBALANCER_SERVICE,
|
||||
MONITOR_RESOURCE,
|
||||
monitor_vseid)
|
||||
return self.do_request(HTTP_PUT, uri, monitor_new)
|
||||
|
||||
def delete_health_monitor(self, edge_id, monitor_vseid):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, LOADBALANCER_SERVICE,
|
||||
MONITOR_RESOURCE,
|
||||
monitor_vseid)
|
||||
return self.do_request(HTTP_DELETE, uri)
|
||||
|
||||
def create_app_profile(self, edge_id, app_profile):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, LOADBALANCER_SERVICE,
|
||||
APP_PROFILE_RESOURCE)
|
||||
return self.do_request(HTTP_POST, uri, app_profile)
|
||||
|
||||
def update_app_profile(self, edge_id, app_profileid, app_profile):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, LOADBALANCER_SERVICE,
|
||||
APP_PROFILE_RESOURCE, app_profileid)
|
||||
return self.do_request(HTTP_PUT, uri, app_profile)
|
||||
|
||||
def delete_app_profile(self, edge_id, app_profileid):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, LOADBALANCER_SERVICE,
|
||||
APP_PROFILE_RESOURCE,
|
||||
app_profileid)
|
||||
return self.do_request(HTTP_DELETE, uri)
|
||||
|
||||
def create_app_rule(self, edge_id, app_rule):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, LOADBALANCER_SERVICE,
|
||||
APP_RULE_RESOURCE)
|
||||
return self.do_request(HTTP_POST, uri, app_rule)
|
||||
|
||||
def update_app_rule(self, edge_id, app_ruleid, app_rule):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, LOADBALANCER_SERVICE,
|
||||
APP_RULE_RESOURCE, app_ruleid)
|
||||
return self.do_request(HTTP_PUT, uri, app_rule)
|
||||
|
||||
def delete_app_rule(self, edge_id, app_ruleid):
|
||||
uri = self._build_uri_path(
|
||||
edge_id, LOADBALANCER_SERVICE,
|
||||
APP_RULE_RESOURCE,
|
||||
app_ruleid)
|
||||
return self.do_request(HTTP_DELETE, uri)
|
||||
|
||||
def update_ipsec_config(self, edge_id, ipsec_config):
|
||||
uri = self._build_uri_path(edge_id, IPSEC_VPN_SERVICE)
|
||||
return self.do_request(HTTP_PUT, uri, ipsec_config)
|
||||
|
||||
def delete_ipsec_config(self, edge_id):
|
||||
uri = self._build_uri_path(edge_id, IPSEC_VPN_SERVICE)
|
||||
return self.do_request(HTTP_DELETE, uri)
|
||||
|
||||
def get_ipsec_config(self, edge_id):
|
||||
uri = self._build_uri_path(edge_id, IPSEC_VPN_SERVICE)
|
||||
return self.do_request(HTTP_GET, uri)
|
||||
|
||||
def create_virtual_wire(self, vdn_scope_id, request):
|
||||
"""Creates a VXLAN virtual wire
|
||||
|
||||
The method will return the virtual wire ID.
|
||||
"""
|
||||
uri = '/api/2.0/vdn/scopes/%s/virtualwires' % vdn_scope_id
|
||||
return self.do_request(HTTP_POST, uri, request, format='xml',
|
||||
decode=False)
|
||||
|
||||
def delete_virtual_wire(self, virtualwire_id):
|
||||
"""Deletes a virtual wire."""
|
||||
uri = '/api/2.0/vdn/virtualwires/%s' % virtualwire_id
|
||||
return self.do_request(HTTP_DELETE, uri, format='xml')
|
||||
|
||||
def create_port_group(self, dvs_id, request):
|
||||
"""Creates a port group on a DVS
|
||||
|
||||
The method will return the port group ID.
|
||||
"""
|
||||
uri = '/api/2.0/xvs/switches/%s/networks' % dvs_id
|
||||
return self.do_request(HTTP_POST, uri, request, format='xml',
|
||||
decode=False)
|
||||
|
||||
def delete_port_group(self, dvs_id, portgroup_id):
|
||||
"""Deletes a portgroup."""
|
||||
uri = '/api/2.0/xvs/switches/%s/networks/%s' % (dvs_id,
|
||||
portgroup_id)
|
||||
return self.do_request(HTTP_DELETE, uri, format='xml', decode=False)
|
||||
|
||||
def query_interface(self, edge_id, vnic_index):
|
||||
uri = "%s/%s/vnics/%d" % (URI_PREFIX, edge_id, vnic_index)
|
||||
return self.do_request(HTTP_GET, uri, decode=True)
|
||||
|
||||
def reconfigure_dhcp_service(self, edge_id, request_config):
|
||||
"""Reconfigure dhcp static bindings in the created Edge."""
|
||||
uri = "/api/4.0/edges/%s/dhcp/config?async=true" % edge_id
|
||||
|
||||
return self.do_request(HTTP_PUT, uri, request_config)
|
||||
|
||||
def query_dhcp_configuration(self, edge_id):
|
||||
"""Query DHCP configuration from the specific edge."""
|
||||
uri = "/api/4.0/edges/%s/dhcp/config" % edge_id
|
||||
return self.do_request(HTTP_GET, uri)
|
||||
|
||||
def create_dhcp_binding(self, edge_id, request_config):
|
||||
"""Append one dhcp static binding on the edge."""
|
||||
uri = self._build_uri_path(edge_id,
|
||||
DHCP_SERVICE, DHCP_BINDING_RESOURCE,
|
||||
is_async=True)
|
||||
return self.do_request(HTTP_POST, uri, request_config, decode=False)
|
||||
|
||||
def delete_dhcp_binding(self, edge_id, binding_id):
|
||||
"""Delete one dhcp static binding on the edge."""
|
||||
uri = self._build_uri_path(edge_id,
|
||||
DHCP_SERVICE, DHCP_BINDING_RESOURCE,
|
||||
binding_id, is_async=True)
|
||||
return self.do_request(HTTP_DELETE, uri, decode=False)
|
||||
|
||||
def create_security_group(self, request):
|
||||
"""Creates a security group container in nsx.
|
||||
|
||||
The method will return the security group ID.
|
||||
"""
|
||||
uri = '%s/globalroot-0' % SECURITYGROUP_PREFIX
|
||||
return self.do_request(HTTP_POST, uri, request, format='xml',
|
||||
decode=False)
|
||||
|
||||
def delete_security_group(self, securitygroup_id):
|
||||
"""Deletes a security group container."""
|
||||
uri = '%s/%s?force=true' % (SECURITYGROUP_PREFIX, securitygroup_id)
|
||||
return self.do_request(HTTP_DELETE, uri, format='xml', decode=False)
|
||||
|
||||
def create_section(self, type, request):
|
||||
"""Creates a layer 3 or layer 2 section in nsx rule table.
|
||||
|
||||
The method will return the uri to newly created section.
|
||||
"""
|
||||
if type == 'ip':
|
||||
sec_type = 'layer3sections'
|
||||
else:
|
||||
sec_type = 'layer2sections'
|
||||
uri = '%s/%s?autoSaveDraft=false' % (FIREWALL_PREFIX, sec_type)
|
||||
return self.do_request(HTTP_POST, uri, request, format='xml',
|
||||
decode=False, encode=False)
|
||||
|
||||
def update_section(self, section_uri, request, h):
|
||||
"""Replaces a section in nsx rule table."""
|
||||
uri = '%s?autoSaveDraft=false' % section_uri
|
||||
headers = self._get_section_header(section_uri, h)
|
||||
return self.do_request(HTTP_PUT, uri, request, format='xml',
|
||||
decode=False, encode=False, headers=headers)
|
||||
|
||||
def delete_section(self, section_uri):
|
||||
"""Deletes a section in nsx rule table."""
|
||||
uri = '%s?autoSaveDraft=false' % section_uri
|
||||
return self.do_request(HTTP_DELETE, uri, format='xml', decode=False)
|
||||
|
||||
def get_section(self, section_uri):
|
||||
return self.do_request(HTTP_GET, section_uri, format='xml',
|
||||
decode=False)
|
||||
|
||||
def get_section_id(self, section_name):
|
||||
"""Retrieve the id of a section from nsx."""
|
||||
uri = FIREWALL_PREFIX
|
||||
h, section_list = self.do_request(HTTP_GET, uri, decode=False,
|
||||
format='xml')
|
||||
root = et.fromstring(section_list)
|
||||
|
||||
for elem in root.findall('.//*'):
|
||||
if elem.tag == 'section' and elem.attrib['name'] == section_name:
|
||||
return elem.attrib['id']
|
||||
|
||||
def update_section_by_id(self, id, type, request):
|
||||
"""Update a section while building its uri from the id."""
|
||||
if type == 'ip':
|
||||
sec_type = 'layer3sections'
|
||||
else:
|
||||
sec_type = 'layer2sections'
|
||||
section_uri = '%s/%s/%s' % (FIREWALL_PREFIX, sec_type, id)
|
||||
self.update_section(section_uri, request, h=None)
|
||||
|
||||
def _get_section_header(self, section_uri, h=None):
|
||||
if h is None:
|
||||
h, c = self.do_request(HTTP_GET, section_uri, format='xml',
|
||||
decode=False)
|
||||
etag = h['etag']
|
||||
headers = {'If-Match': etag}
|
||||
return headers
|
||||
|
||||
def remove_rule_from_section(self, section_uri, rule_id):
|
||||
"""Deletes a rule from nsx section table."""
|
||||
uri = '%s/rules/%s?autoSaveDraft=false' % (section_uri, rule_id)
|
||||
headers = self._get_section_header(section_uri)
|
||||
return self.do_request(HTTP_DELETE, uri, format='xml',
|
||||
headers=headers)
|
||||
|
||||
def add_member_to_security_group(self, security_group_id, member_id):
|
||||
"""Adds a vnic member to nsx security group."""
|
||||
uri = '%s/%s/members/%s' % (SECURITYGROUP_PREFIX,
|
||||
security_group_id, member_id)
|
||||
return self.do_request(HTTP_PUT, uri, format='xml', decode=False)
|
||||
|
||||
def remove_member_from_security_group(self, security_group_id,
|
||||
member_id):
|
||||
"""Removes a vnic member from nsx security group."""
|
||||
uri = '%s/%s/members/%s' % (SECURITYGROUP_PREFIX,
|
||||
security_group_id, member_id)
|
||||
return self.do_request(HTTP_DELETE, uri, format='xml', decode=False)
|
||||
|
||||
def _build_uri_path(self, edge_id,
|
||||
service,
|
||||
resource=None,
|
||||
resource_id=None,
|
||||
parent_resource_id=None,
|
||||
fields=None,
|
||||
relations=None,
|
||||
filters=None,
|
||||
types=None,
|
||||
is_attachment=False,
|
||||
is_async=False):
|
||||
uri_prefix = "%s/%s/%s" % (URI_PREFIX, edge_id, service)
|
||||
if resource:
|
||||
res_path = resource + (resource_id and "/%s" % resource_id or '')
|
||||
uri_path = "%s/%s" % (uri_prefix, res_path)
|
||||
else:
|
||||
uri_path = uri_prefix
|
||||
if is_async:
|
||||
return uri_path + "?async=true"
|
||||
else:
|
||||
return uri_path
|
||||
|
||||
def _scopingobjects_lookup(self, type_name, object_id):
|
||||
uri = '%s/usermgmt/scopingobjects' % SERVICES_PREFIX
|
||||
h, so_list = self.do_request(HTTP_GET, uri, decode=False,
|
||||
format='xml')
|
||||
|
||||
root = et.fromstring(so_list)
|
||||
|
||||
for elem in root.findall('.//*'):
|
||||
if(elem.tag == 'object'
|
||||
and elem.find('objectTypeName').text == type_name
|
||||
and elem.find('objectId').text == object_id):
|
||||
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def validate_datacenter_moid(self, object_id):
|
||||
return self._scopingobjects_lookup('Datacenter', object_id)
|
||||
|
||||
def validate_network(self, object_id):
|
||||
return (self._scopingobjects_lookup('Network', object_id) or
|
||||
self._scopingobjects_lookup('DistributedVirtualPortgroup',
|
||||
object_id) or
|
||||
self._scopingobjects_lookup('VirtualWire', object_id))
|
||||
|
||||
def validate_vdn_scope(self, object_id):
|
||||
uri = '%s/scopes' % VDN_PREFIX
|
||||
h, scope_list = self.do_request(HTTP_GET, uri, decode=False,
|
||||
format='xml')
|
||||
|
||||
root = et.fromstring(scope_list)
|
||||
for elem in root.findall('.//*'):
|
||||
if elem.tag == 'objectId' and elem.text == object_id:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def validate_dvs(self, object_id):
|
||||
uri = '%s/switches' % VDN_PREFIX
|
||||
h, dvs_list = self.do_request(HTTP_GET, uri, decode=False,
|
||||
format='xml')
|
||||
|
||||
root = et.fromstring(dvs_list)
|
||||
for elem in root.findall('.//*'):
|
||||
if elem.tag == 'objectId' and elem.text == object_id:
|
||||
return True
|
||||
|
||||
return False
|
117
oslo_vmware/network/nsx/nsxv/api/api_helper.py
Normal file
117
oslo_vmware/network/nsx/nsxv/api/api_helper.py
Normal file
@ -0,0 +1,117 @@
|
||||
# Copyright 2015 VMware, Inc
|
||||
#
|
||||
# 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 base64
|
||||
|
||||
import eventlet
|
||||
from oslo_serialization import jsonutils
|
||||
import six
|
||||
|
||||
from oslo_vmware.network.nsx.nsxv.common import exceptions
|
||||
|
||||
|
||||
httplib2 = eventlet.import_patched('httplib2')
|
||||
|
||||
|
||||
def _xmldump(obj):
|
||||
"""Sort of imporved xml creation method.
|
||||
|
||||
This converts the dict to xml with following assumptions:
|
||||
keys starting with _(underscore) are to be used as attributes and not
|
||||
elements keys starting with @ are to there so that dict can be made.
|
||||
The keys are not part of any xml schema.
|
||||
"""
|
||||
|
||||
config = ""
|
||||
attr = ""
|
||||
if isinstance(obj, dict):
|
||||
for key, value in six.iteritems(obj):
|
||||
if (key.startswith('_')):
|
||||
attr += ' %s="%s"' % (key[1:], value)
|
||||
else:
|
||||
a, x = _xmldump(value)
|
||||
if (key.startswith('@')):
|
||||
cfg = "%s" % (x)
|
||||
else:
|
||||
cfg = "<%s%s>%s</%s>" % (key, a, x, key)
|
||||
|
||||
config += cfg
|
||||
elif isinstance(obj, list):
|
||||
for value in obj:
|
||||
a, x = _xmldump(value)
|
||||
attr += a
|
||||
config += x
|
||||
else:
|
||||
config = obj
|
||||
|
||||
return attr, config
|
||||
|
||||
|
||||
def xmldumps(obj):
|
||||
attr, xml = _xmldump(obj)
|
||||
return xml
|
||||
|
||||
|
||||
class NsxvApiHelper(object):
|
||||
errors = {
|
||||
303: exceptions.ResourceRedirect,
|
||||
400: exceptions.RequestBad,
|
||||
403: exceptions.Forbidden,
|
||||
404: exceptions.ResourceNotFound,
|
||||
409: exceptions.ServiceConflict,
|
||||
415: exceptions.MediaTypeUnsupport,
|
||||
503: exceptions.ServiceUnavailable
|
||||
}
|
||||
|
||||
def __init__(self, address, user, password, format='json'):
|
||||
self.authToken = base64.b64encode(
|
||||
str.encode("%s:%s" % (user, password))).decode('ascii')
|
||||
self.user = user
|
||||
self.passwd = password
|
||||
self.address = address
|
||||
self.format = format
|
||||
if format == 'json':
|
||||
self.encode = jsonutils.dumps
|
||||
else:
|
||||
self.encode = xmldumps
|
||||
|
||||
def _http_request(self, uri, method, body, headers):
|
||||
http = httplib2.Http()
|
||||
http.disable_ssl_certificate_validation = True
|
||||
return http.request(uri, method, body=body, headers=headers)
|
||||
|
||||
def request(self, method, uri, params=None, headers=None,
|
||||
encodeparams=True):
|
||||
uri = self.address + uri
|
||||
if headers is None:
|
||||
headers = {}
|
||||
|
||||
headers['Content-Type'] = 'application/' + self.format
|
||||
headers['Accept'] = 'application/' + self.format,
|
||||
headers['Authorization'] = 'Basic ' + self.authToken
|
||||
|
||||
if encodeparams is True:
|
||||
body = self.encode(params) if params else None
|
||||
else:
|
||||
body = params if params else None
|
||||
header, response = self._http_request(uri, method,
|
||||
body=body, headers=headers)
|
||||
status = int(header['status'])
|
||||
if 200 <= status < 300:
|
||||
return header, response
|
||||
if status in self.errors:
|
||||
cls = self.errors[status]
|
||||
else:
|
||||
cls = exceptions.NsxvApiException
|
||||
raise cls(uri=uri, status=status, header=header, response=response)
|
0
oslo_vmware/network/nsx/nsxv/common/__init__.py
Normal file
0
oslo_vmware/network/nsx/nsxv/common/__init__.py
Normal file
97
oslo_vmware/network/nsx/nsxv/common/exceptions.py
Normal file
97
oslo_vmware/network/nsx/nsxv/common/exceptions.py
Normal file
@ -0,0 +1,97 @@
|
||||
# Copyright 2015 VMware, Inc
|
||||
#
|
||||
# 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_utils import excutils
|
||||
|
||||
from oslo_vmware._i18n import _
|
||||
|
||||
|
||||
class NsxvException(Exception):
|
||||
"""Base Neutron Exception.
|
||||
|
||||
To correctly use this class, inherit from it and define
|
||||
a 'message' property. That message will get printf'd
|
||||
with the keyword arguments provided to the constructor.
|
||||
"""
|
||||
message = _("An unknown exception occurred.")
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
try:
|
||||
super(NsxvException, self).__init__(self.message % kwargs)
|
||||
self.msg = self.message % kwargs
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception() as ctxt:
|
||||
if not self.use_fatal_exceptions():
|
||||
ctxt.reraise = False
|
||||
# at least get the core message out if something happened
|
||||
super(NsxvException, self).__init__(self.message)
|
||||
|
||||
def __unicode__(self):
|
||||
return unicode(self.msg)
|
||||
|
||||
def use_fatal_exceptions(self):
|
||||
return False
|
||||
|
||||
|
||||
class NsxvGeneralException(NsxvException):
|
||||
def __init__(self, message):
|
||||
self.message = message
|
||||
super(NsxvGeneralException, self).__init__()
|
||||
|
||||
|
||||
class NsxvBadRequest(NsxvException):
|
||||
message = _('Bad %(resource)s request: %(msg)s')
|
||||
|
||||
|
||||
class NsxvNotFound(NsxvException):
|
||||
message = _('%(resource)s not found: %(msg)s')
|
||||
|
||||
|
||||
class NsxvApiException(NsxvException):
|
||||
message = _("An unknown exception %(status)s occurred: %(response)s.")
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(NsxvApiException, self).__init__(**kwargs)
|
||||
|
||||
self.status = kwargs.get('status')
|
||||
self.header = kwargs.get('header')
|
||||
self.response = kwargs.get('response')
|
||||
|
||||
|
||||
class ResourceRedirect(NsxvApiException):
|
||||
message = _("Resource %(uri)s has been redirected")
|
||||
|
||||
|
||||
class RequestBad(NsxvApiException):
|
||||
message = _("Request %(uri)s is Bad, response %(response)s")
|
||||
|
||||
|
||||
class Forbidden(NsxvApiException):
|
||||
message = _("Forbidden: %(uri)s")
|
||||
|
||||
|
||||
class ResourceNotFound(NsxvApiException):
|
||||
message = _("Resource %(uri)s not found")
|
||||
|
||||
|
||||
class MediaTypeUnsupport(NsxvApiException):
|
||||
message = _("Media Type %(uri)s is not supported")
|
||||
|
||||
|
||||
class ServiceUnavailable(NsxvApiException):
|
||||
message = _("Service Unavailable: %(uri)s")
|
||||
|
||||
|
||||
class ServiceConflict(NsxvApiException):
|
||||
message = _("Concurrent object access error: %(uri)s")
|
0
oslo_vmware/network/nsx/nsxv/objects/__init__.py
Normal file
0
oslo_vmware/network/nsx/nsxv/objects/__init__.py
Normal file
67
oslo_vmware/network/nsx/nsxv/objects/edge_cfg_obj.py
Normal file
67
oslo_vmware/network/nsx/nsxv/objects/edge_cfg_obj.py
Normal file
@ -0,0 +1,67 @@
|
||||
# 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 abc
|
||||
|
||||
from oslo_serialization import jsonutils
|
||||
import six
|
||||
|
||||
from oslo_vmware.network.nsx.nsxv.api import api
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class NsxvEdgeCfgObj(object):
|
||||
|
||||
def __init__(self):
|
||||
return
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_service_name(self):
|
||||
return
|
||||
|
||||
@abc.abstractmethod
|
||||
def serializable_payload(self):
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
def get_object(nsxv_api, edge_id, service_name):
|
||||
uri = "%s/%s/%s" % (api.URI_PREFIX,
|
||||
edge_id,
|
||||
service_name)
|
||||
|
||||
h, v = nsxv_api.do_request(
|
||||
api.HTTP_GET,
|
||||
uri,
|
||||
decode=True)
|
||||
|
||||
return v
|
||||
|
||||
def submit_to_backend(self, nsxv_api, edge_id, async=True):
|
||||
uri = "%s/%s/%s/config" % (api.URI_PREFIX,
|
||||
edge_id,
|
||||
self.get_service_name())
|
||||
|
||||
if async:
|
||||
uri += '?async=true'
|
||||
|
||||
payload = jsonutils.dumps(self.serializable_payload(), sort_keys=True)
|
||||
|
||||
if payload:
|
||||
return nsxv_api.do_request(
|
||||
api.HTTP_PUT,
|
||||
uri,
|
||||
payload,
|
||||
format='json',
|
||||
encode=False)
|
392
oslo_vmware/network/nsx/nsxv/objects/loadbalancer.py
Normal file
392
oslo_vmware/network/nsx/nsxv/objects/loadbalancer.py
Normal file
@ -0,0 +1,392 @@
|
||||
# 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 logging
|
||||
|
||||
from oslo_vmware.network.nsx.nsxv.objects import edge_cfg_obj
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NsxvLoadbalancer(edge_cfg_obj.NsxvEdgeCfgObj):
|
||||
|
||||
SERVICE_NAME = 'loadbalancer'
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
enabled=True,
|
||||
enable_service_insertion=False,
|
||||
acceleration_enabled=False):
|
||||
super(NsxvLoadbalancer, self).__init__()
|
||||
self.payload = {
|
||||
'enabled': enabled,
|
||||
'enableServiceInsertion': enable_service_insertion,
|
||||
'accelerationEnabled': acceleration_enabled}
|
||||
self.virtual_servers = {}
|
||||
|
||||
def get_service_name(self):
|
||||
return self.SERVICE_NAME
|
||||
|
||||
def add_virtual_server(self, virtual_server):
|
||||
self.virtual_servers[virtual_server.payload['name']] = virtual_server
|
||||
|
||||
def del_virtual_server(self, name):
|
||||
self.virtual_servers.pop(name, None)
|
||||
|
||||
def serializable_payload(self):
|
||||
virt_servers = []
|
||||
app_profiles = []
|
||||
app_rules = []
|
||||
pools = []
|
||||
monitors = []
|
||||
|
||||
virt_id = 1
|
||||
app_prof_id = 1
|
||||
app_rule_id = 1
|
||||
pool_id = 1
|
||||
monitor_id = 1
|
||||
member_id = 1
|
||||
|
||||
for virtual_server in self.virtual_servers.values():
|
||||
s_virt = virtual_server.payload.copy()
|
||||
s_virt['virtualServerId'] = 'virtualServer-%d' % virt_id
|
||||
virt_id += 1
|
||||
|
||||
# Setup app profile
|
||||
s_app_prof = virtual_server.app_profile.payload.copy()
|
||||
s_app_prof['applicationProfileId'] = ('applicationProfile-%d' %
|
||||
app_prof_id)
|
||||
app_profiles.append(s_app_prof)
|
||||
app_prof_id += 1
|
||||
|
||||
# Bind virtual server to app profile
|
||||
s_virt['applicationProfileId'] = s_app_prof['applicationProfileId']
|
||||
|
||||
# Setup app rules
|
||||
if virtual_server.app_rules.values():
|
||||
s_virt['applicationRuleId'] = []
|
||||
for app_rule in virtual_server.app_rules.values():
|
||||
s_app_rule = app_rule.payload.copy()
|
||||
s_app_rule['applicationRuleId'] = ('applicationRule-%d' %
|
||||
app_rule_id)
|
||||
app_rule_id += 1
|
||||
|
||||
# Add to LB object, bind to virtual server
|
||||
app_rules.append(s_app_rule)
|
||||
s_virt['applicationRuleId'].append(
|
||||
s_app_rule['applicationRuleId'])
|
||||
|
||||
# Setup pools
|
||||
s_pool = virtual_server.default_pool.payload.copy()
|
||||
s_pool['poolId'] = 'pool-%d' % pool_id
|
||||
pool_id += 1
|
||||
pools.append(s_pool)
|
||||
|
||||
# Add pool members
|
||||
s_pool['member'] = []
|
||||
for member in virtual_server.default_pool.members.values():
|
||||
s_m = member.payload.copy()
|
||||
s_m['memberId'] = 'member-%d' % member_id
|
||||
member_id += 1
|
||||
s_pool['member'].append(s_m)
|
||||
|
||||
# Bind pool to virtual server
|
||||
s_virt['defaultPoolId'] = s_pool['poolId']
|
||||
|
||||
s_pool['monitorId'] = []
|
||||
# Add monitors
|
||||
for monitor in virtual_server.default_pool.monitors.values():
|
||||
s_mon = monitor.payload.copy()
|
||||
s_mon['monitorId'] = 'monitor-%d' % monitor_id
|
||||
monitor_id += 1
|
||||
|
||||
s_pool['monitorId'].append(s_mon['monitorId'])
|
||||
|
||||
monitors.append(s_mon)
|
||||
|
||||
virt_servers.append(s_virt)
|
||||
|
||||
payload = self.payload.copy()
|
||||
payload['applicationProfile'] = app_profiles
|
||||
if app_rules:
|
||||
payload['applicationRule'] = app_rules
|
||||
payload['monitor'] = monitors
|
||||
payload['pool'] = pools
|
||||
payload['virtualServer'] = virt_servers
|
||||
payload['featureType'] = 'loadbalancer_4.0'
|
||||
|
||||
return payload
|
||||
|
||||
@staticmethod
|
||||
def get_loadbalancer(nsxv_api, edge_id):
|
||||
edge_lb = edge_cfg_obj.NsxvEdgeCfgObj.get_object(
|
||||
nsxv_api,
|
||||
edge_id,
|
||||
NsxvLoadbalancer.SERVICE_NAME)
|
||||
|
||||
lb_obj = NsxvLoadbalancer(
|
||||
edge_lb['enabled'],
|
||||
edge_lb['enableServiceInsertion'],
|
||||
edge_lb['accelerationEnabled'])
|
||||
|
||||
# Construct loadbalancer objects
|
||||
for virt_srvr in edge_lb['virtualServer']:
|
||||
v_s = NsxvLBVirtualServer(
|
||||
virt_srvr['name'],
|
||||
virt_srvr['ipAddress'],
|
||||
virt_srvr['port'],
|
||||
virt_srvr['protocol'],
|
||||
virt_srvr['enabled'],
|
||||
virt_srvr['accelerationEnabled'],
|
||||
virt_srvr['connectionLimit'])
|
||||
|
||||
# Find application profile objects, attach to virtual server
|
||||
for app_prof in edge_lb['applicationProfile']:
|
||||
if (virt_srvr['applicationProfileId']
|
||||
== app_prof['applicationProfileId']):
|
||||
a_p = NsxvLBAppProfile(
|
||||
app_prof['name'],
|
||||
app_prof['serverSslEnabled'],
|
||||
app_prof['sslPassthrough'],
|
||||
app_prof['template'],
|
||||
app_prof['insertXForwardedFor'])
|
||||
|
||||
if app_prof['persistence']:
|
||||
a_p.set_persistence(
|
||||
True,
|
||||
app_prof['persistence']['method'],
|
||||
app_prof['persistence'].get('cookieName'),
|
||||
app_prof['persistence'].get('cookieMode'),
|
||||
app_prof['persistence'].get('expire'))
|
||||
|
||||
v_s.set_app_profile(a_p)
|
||||
|
||||
# Find default pool, attach to virtual server
|
||||
for pool in edge_lb['pool']:
|
||||
if virt_srvr['defaultPoolId'] == pool['poolId']:
|
||||
p = NsxvLBPool(
|
||||
pool['name'],
|
||||
pool['algorithm'],
|
||||
pool['transparent'])
|
||||
|
||||
# Add pool members to pool
|
||||
for member in pool['member']:
|
||||
m = NsxvLBPoolMember(
|
||||
member['name'],
|
||||
member['ipAddress'],
|
||||
member['port'],
|
||||
member['monitorPort'],
|
||||
member['condition'],
|
||||
member['weight'],
|
||||
member['minConn'],
|
||||
member['maxConn'])
|
||||
|
||||
p.add_member(m)
|
||||
|
||||
# Add monitors to pool
|
||||
for mon in edge_lb['monitor']:
|
||||
if mon['monitorId'] in pool['monitorId']:
|
||||
m = NsxvLBMonitor(
|
||||
mon['name'],
|
||||
mon['interval'],
|
||||
mon['maxRetries'],
|
||||
mon['method'],
|
||||
mon['timeout'],
|
||||
mon['type'],
|
||||
mon['url'])
|
||||
|
||||
p.add_monitor(m)
|
||||
|
||||
v_s.set_default_pool(p)
|
||||
|
||||
# Add application rules to virtual server
|
||||
for rule in edge_lb['applicationRule']:
|
||||
if rule['applicationRuleId'] in virt_srvr['applicationRuleId']:
|
||||
r = NsxvLBAppRule(
|
||||
rule['name'],
|
||||
rule['script'])
|
||||
|
||||
v_s.add_app_rule(r)
|
||||
|
||||
lb_obj.add_virtual_server(v_s)
|
||||
|
||||
return lb_obj
|
||||
|
||||
|
||||
class NsxvLBAppProfile():
|
||||
def __init__(
|
||||
self,
|
||||
name,
|
||||
server_ssl_enabled=False,
|
||||
ssl_pass_through=False,
|
||||
template='TCP',
|
||||
insert_xff=False,
|
||||
persist=False,
|
||||
persist_method='cookie',
|
||||
persist_cookie_name='JSESSIONID',
|
||||
persist_cookie_mode='insert',
|
||||
persist_expire=30):
|
||||
self.payload = {
|
||||
'name': name,
|
||||
'serverSslEnabled': server_ssl_enabled,
|
||||
'sslPassthrough': ssl_pass_through,
|
||||
'template': template,
|
||||
'insertXForwardedFor': insert_xff}
|
||||
|
||||
if persist:
|
||||
self.payload['persistence'] = {
|
||||
'method': persist_method,
|
||||
'expire': persist_expire
|
||||
}
|
||||
if persist_cookie_mode == 'cookie':
|
||||
self.payload['persistence']['cookieMode'] = persist_cookie_mode
|
||||
self.payload['persistence']['cookieName'] = persist_cookie_name
|
||||
|
||||
def set_persistence(
|
||||
self,
|
||||
persist=False,
|
||||
persist_method='cookie',
|
||||
persist_cookie_name='JSESSIONID',
|
||||
persist_cookie_mode='insert',
|
||||
persist_expire=30):
|
||||
|
||||
if persist:
|
||||
self.payload['persistence'] = {
|
||||
'method': persist_method,
|
||||
'expire': persist_expire
|
||||
}
|
||||
if persist_cookie_mode == 'cookie':
|
||||
self.payload['persistence']['cookieMode'] = persist_cookie_mode
|
||||
self.payload['persistence']['cookieName'] = persist_cookie_name
|
||||
|
||||
else:
|
||||
self.payload.pop('persistence', None)
|
||||
|
||||
|
||||
class NsxvLBAppRule(object):
|
||||
def __init__(self, name, script):
|
||||
self.payload = {
|
||||
'name': name,
|
||||
'script': script}
|
||||
|
||||
|
||||
class NsxvLBVirtualServer(object):
|
||||
def __init__(
|
||||
self,
|
||||
name,
|
||||
ip_address,
|
||||
port=80,
|
||||
protocol='HTTP',
|
||||
enabled=True,
|
||||
acceleration_enabled=False,
|
||||
connection_limit=0,
|
||||
enable_service_insertion=False):
|
||||
self.payload = {
|
||||
'name': name,
|
||||
'ipAddress': ip_address,
|
||||
'port': port,
|
||||
'protocol': protocol,
|
||||
'enabled': enabled,
|
||||
'accelerationEnabled': acceleration_enabled,
|
||||
'connectionLimit': connection_limit,
|
||||
'enableServiceInsertion': enable_service_insertion}
|
||||
|
||||
self.app_rules = {}
|
||||
self.app_profile = None
|
||||
self.default_pool = None
|
||||
|
||||
def add_app_rule(self, app_rule):
|
||||
self.app_rules[app_rule.payload['name']] = app_rule
|
||||
|
||||
def del_app_rule(self, name):
|
||||
self.app_rules.pop(name, None)
|
||||
|
||||
def set_default_pool(self, pool):
|
||||
self.default_pool = pool
|
||||
|
||||
def set_app_profile(self, app_profile):
|
||||
self.app_profile = app_profile
|
||||
|
||||
|
||||
class NsxvLBMonitor(object):
|
||||
def __init__(
|
||||
self,
|
||||
name,
|
||||
interval=10,
|
||||
max_retries=3,
|
||||
method='GET',
|
||||
timeout=15,
|
||||
mon_type='http',
|
||||
url='/'):
|
||||
self.payload = {
|
||||
'name': name,
|
||||
'interval': interval,
|
||||
'maxRetries': max_retries,
|
||||
'method': method,
|
||||
'timeout': timeout,
|
||||
'type': mon_type,
|
||||
'url': url}
|
||||
|
||||
|
||||
class NsxvLBPoolMember(object):
|
||||
def __init__(
|
||||
self,
|
||||
name,
|
||||
ip_address,
|
||||
port,
|
||||
monitor_port=None,
|
||||
condition='enabled',
|
||||
weight=1,
|
||||
min_conn=0,
|
||||
max_conn=0):
|
||||
|
||||
self.payload = {
|
||||
'name': name,
|
||||
'ipAddress': ip_address,
|
||||
'port': port,
|
||||
'monitorPort': monitor_port,
|
||||
'condition': condition,
|
||||
'weight': weight,
|
||||
'minConn': min_conn,
|
||||
'maxConn': max_conn}
|
||||
|
||||
|
||||
class NsxvLBPool(object):
|
||||
def __init__(
|
||||
self,
|
||||
name,
|
||||
algorithm='round-robin',
|
||||
transparent=False):
|
||||
self.payload = {
|
||||
'name': name,
|
||||
'algorithm': algorithm,
|
||||
'transparent': transparent}
|
||||
|
||||
self.members = {}
|
||||
self.monitors = {}
|
||||
|
||||
def add_member(self, member):
|
||||
self.members[member.payload['name']] = member
|
||||
|
||||
def del_member(self, name):
|
||||
self.members.pop(name, None)
|
||||
|
||||
def add_monitor(self, monitor):
|
||||
self.monitors[monitor.payload['name']] = monitor
|
||||
|
||||
def del_monitor(self, name):
|
||||
self.monitors.pop(name, None)
|
@ -12,6 +12,7 @@ iso8601>=0.1.9
|
||||
six>=1.7.0
|
||||
|
||||
oslo.i18n>=1.3.0 # Apache-2.0
|
||||
oslo.serialization>=1.2.0 # Apache-2.0
|
||||
oslo.utils>=1.2.0 # Apache-2.0
|
||||
Babel>=1.3
|
||||
|
||||
@ -20,5 +21,7 @@ PyYAML>=3.1.0
|
||||
|
||||
suds-jurko>=0.6
|
||||
eventlet>=0.16.1
|
||||
httplib2>=0.7.5
|
||||
requests>=2.2.0,!=2.4.0
|
||||
urllib3>=1.8.3
|
||||
oslo.concurrency>=0.3.0,!=0.4.0 # Apache-2.0
|
||||
|
@ -14,6 +14,7 @@ iso8601>=0.1.9
|
||||
six>=1.7.0
|
||||
|
||||
oslo.i18n>=1.3.0 # Apache-2.0
|
||||
oslo.serialization>=1.2.0 # Apache-2.0
|
||||
oslo.utils>=1.2.0 # Apache-2.0
|
||||
Babel>=1.3
|
||||
|
||||
@ -22,6 +23,7 @@ PyYAML>=3.1.0
|
||||
|
||||
suds>=0.4
|
||||
eventlet>=0.16.1
|
||||
httplib2>=0.7.5
|
||||
requests>=2.2.0,!=2.4.0
|
||||
urllib3>=1.8.3
|
||||
oslo.concurrency>=1.4.1 # Apache-2.0
|
||||
|
0
tests/network/__init__.py
Normal file
0
tests/network/__init__.py
Normal file
0
tests/network/nsx/__init__.py
Normal file
0
tests/network/nsx/__init__.py
Normal file
0
tests/network/nsx/nsxv/__init__.py
Normal file
0
tests/network/nsx/nsxv/__init__.py
Normal file
1216
tests/network/nsx/nsxv/test_nsxv_api.py
Normal file
1216
tests/network/nsx/nsxv/test_nsxv_api.py
Normal file
File diff suppressed because it is too large
Load Diff
117
tests/network/nsx/nsxv/test_nsxv_api_helper.py
Normal file
117
tests/network/nsx/nsxv/test_nsxv_api_helper.py
Normal file
@ -0,0 +1,117 @@
|
||||
# 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 mock
|
||||
|
||||
from oslo_vmware.network.nsx.nsxv.api import api_helper
|
||||
from oslo_vmware.network.nsx.nsxv.common import exceptions
|
||||
from tests import base
|
||||
|
||||
|
||||
class NsxvApiHelperTestCase(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(NsxvApiHelperTestCase, self).setUp()
|
||||
|
||||
def test_get_success(self):
|
||||
helper = api_helper.NsxvApiHelper(
|
||||
'http://10.0.0.1',
|
||||
'testuser',
|
||||
'testpass')
|
||||
|
||||
h = {'status': '200',
|
||||
'connection': 'keep-alive',
|
||||
'content-type': 'text/html; charset=utf-8'}
|
||||
v = '<html></html>'
|
||||
|
||||
with mock.patch.object(helper, '_http_request',
|
||||
return_value=(h, v)) as mock_request:
|
||||
helper.request('GET', '/test/index.html')
|
||||
|
||||
mock_request.assert_has_calls([
|
||||
mock.call().request(
|
||||
'http://10.0.0.1/test/index.html', 'GET', body=None,
|
||||
headers={'Content-Type': 'application/json',
|
||||
'Authorization':
|
||||
'Basic dGVzdHVzZXI6dGVzdHBhc3M=',
|
||||
'Accept': ('application/json',)})])
|
||||
|
||||
def test_get_fail(self):
|
||||
helper = api_helper.NsxvApiHelper(
|
||||
'http://10.0.0.1',
|
||||
'testuser',
|
||||
'testpass')
|
||||
|
||||
h = {'status': '404',
|
||||
'connection': 'keep-alive',
|
||||
'content-type': 'text/html; charset=utf-8'}
|
||||
v = '<html></html>'
|
||||
|
||||
with mock.patch.object(helper, '_http_request',
|
||||
return_value=(h, v)):
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.ResourceNotFound,
|
||||
helper.request, 'GET', '/test/index.html')
|
||||
|
||||
def test_put_json(self):
|
||||
helper = api_helper.NsxvApiHelper(
|
||||
'http://10.0.0.1',
|
||||
'testuser',
|
||||
'testpass',
|
||||
'json')
|
||||
|
||||
h = {'status': '201',
|
||||
'connection': 'keep-alive',
|
||||
'content-type': 'application/json'}
|
||||
v = '<html></html>'
|
||||
obj = {'test1': 'testing123'}
|
||||
with mock.patch.object(helper, '_http_request',
|
||||
return_value=(h, v)) as mock_request:
|
||||
helper.request('PUT', '/test/index.html', obj)
|
||||
|
||||
mock_request.assert_has_calls([
|
||||
mock.call().request(
|
||||
'http://10.0.0.1/test/index.html', 'PUT',
|
||||
body='{"test1": "testing123"}',
|
||||
headers={
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Basic dGVzdHVzZXI6dGVzdHBhc3M=',
|
||||
'Accept': ('application/json',)})])
|
||||
|
||||
def test_put_xml(self):
|
||||
helper = api_helper.NsxvApiHelper(
|
||||
'http://10.0.0.1',
|
||||
'testuser',
|
||||
'testpass',
|
||||
'xml')
|
||||
|
||||
h = {'status': '201',
|
||||
'connection': 'keep-alive',
|
||||
'content-type': 'application/xml'}
|
||||
v = '<html></html>'
|
||||
obj = {'test1': 'testing123'}
|
||||
with mock.patch.object(helper, '_http_request',
|
||||
return_value=(h, v)) as mock_request:
|
||||
helper.request('PUT', '/test/index.html', obj)
|
||||
|
||||
mock_request.assert_has_calls([
|
||||
mock.call().request(
|
||||
'http://10.0.0.1/test/index.html', 'PUT',
|
||||
body='<test1>testing123</test1>',
|
||||
headers={
|
||||
'Content-Type': 'application/xml',
|
||||
'Authorization': 'Basic dGVzdHVzZXI6dGVzdHBhc3M=',
|
||||
'Accept': ('application/xml',)})])
|
98
tests/network/nsx/nsxv/test_nsxv_loadbalancer.py
Normal file
98
tests/network/nsx/nsxv/test_nsxv_loadbalancer.py
Normal file
@ -0,0 +1,98 @@
|
||||
# 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 json
|
||||
|
||||
import mock
|
||||
|
||||
from oslo_vmware.network.nsx.nsxv.api import api as nsxv_api
|
||||
from oslo_vmware.network.nsx.nsxv.objects import loadbalancer as nsxv_lb
|
||||
from tests import base
|
||||
|
||||
|
||||
class NsxvLoadbalancerTestCase(base.TestCase):
|
||||
|
||||
EDGE_OBJ_JSON = (
|
||||
'{"accelerationEnabled":false,"applicationProfile":[{'
|
||||
'"applicationProfileId":"applicationProfile-1","insertXForwardedFor":'
|
||||
'false,"name":"MDSrvProxy","persistence":{"cookieMode":"insert",'
|
||||
'"cookieName":"JSESSIONID","expire":"30","method":"cookie"},'
|
||||
'"serverSslEnabled":false,"sslPassthrough":false,"template":"HTTP"}],'
|
||||
'"applicationRule":[],"enableServiceInsertion":false,"enabled":true,'
|
||||
'"featureType":"loadbalancer_4.0","logging":{"enable":false,'
|
||||
'"logLevel":"info"},"monitor":[{"interval":10,"maxRetries":3,"method":'
|
||||
'"GET","monitorId":"monitor-1","name":"MDSrvMon","timeout":15,"type":'
|
||||
'"http","url":"/"}],"pool":[{"algorithm":"round-robin",'
|
||||
'"applicationRuleId":[],"member":[{"condition":"enabled","ipAddress":'
|
||||
'"192.168.0.39","maxConn":0,"memberId":"member-1","minConn":0,'
|
||||
'"monitorPort":8775,"name":"Member-1","port":8775,"weight":1}],'
|
||||
'"monitorId":["monitor-1"],"name":"MDSrvPool","poolId":"pool-1",'
|
||||
'"transparent":false}],"version":6,"virtualServer":[{'
|
||||
'"accelerationEnabled":false,"applicationProfileId":'
|
||||
'"applicationProfile-1","applicationRuleId":[],"connectionLimit":0,'
|
||||
'"defaultPoolId":"pool-1","enableServiceInsertion":false,'
|
||||
'"enabled":true,"ipAddress":"169.254.0.3","name":"MdSrv",'
|
||||
'"port":"8775","protocol":"http","virtualServerId":'
|
||||
'"virtualServer-1"}]}')
|
||||
|
||||
OUT_OBJ_JSON = (
|
||||
'{"accelerationEnabled": false, "applicationProfile": [{'
|
||||
'"applicationProfileId": "applicationProfile-1", '
|
||||
'"insertXForwardedFor": false, "name": "MDSrvProxy", "persistence": '
|
||||
'{"expire": "30", "method": "cookie"}, "serverSslEnabled": false, '
|
||||
'"sslPassthrough": false, "template": "HTTP"}],'
|
||||
' "enableServiceInsertion": false, "enabled": true, "featureType": '
|
||||
'"loadbalancer_4.0", "monitor": [{"interval": 10, "maxRetries": 3, '
|
||||
'"method": "GET", "monitorId": "monitor-1", "name": "MDSrvMon", '
|
||||
'"timeout": 15, "type": "http", "url": "/"}], "pool": [{"algorithm":'
|
||||
' "round-robin", "member": [{"condition": "enabled", "ipAddress": '
|
||||
'"192.168.0.39", "maxConn": 0, "memberId": "member-1", "minConn": 0, '
|
||||
'"monitorPort": 8775, "name": "Member-1", "port": 8775, "weight": 1}],'
|
||||
' "monitorId": ["monitor-1"], "name": "MDSrvPool", "poolId": "pool-1",'
|
||||
' "transparent": false}], "virtualServer": [{"accelerationEnabled": '
|
||||
'false, "applicationProfileId": "applicationProfile-1", '
|
||||
'"connectionLimit": 0, "defaultPoolId": "pool-1", '
|
||||
'"enableServiceInsertion": false, "enabled": true, "ipAddress": '
|
||||
'"169.254.0.3", "name": "MdSrv", "port": "8775", "protocol": '
|
||||
'"http", "virtualServerId": "virtualServer-1"}]}')
|
||||
|
||||
LB_URI = '/api/4.0/edges/%s/loadbalancer/config?async=true'
|
||||
EDGE_1 = 'edge-x'
|
||||
EDGE_2 = 'edge-y'
|
||||
|
||||
def setUp(self):
|
||||
super(NsxvLoadbalancerTestCase, self).setUp()
|
||||
self._lb = nsxv_lb.NsxvLoadbalancer()
|
||||
self._nsxv = nsxv_api.NsxvApi(None, None, None, None)
|
||||
|
||||
def test_edge_loadbalancer(self):
|
||||
h = None
|
||||
v = json.loads(self.EDGE_OBJ_JSON)
|
||||
|
||||
with mock.patch.object(self._nsxv, 'do_request',
|
||||
return_value=(h, v)) as mock_do_request:
|
||||
# Retrieve Edge LB
|
||||
lb = nsxv_lb.NsxvLoadbalancer.get_loadbalancer(
|
||||
self._nsxv, self.EDGE_1)
|
||||
|
||||
# Repost Edge LB
|
||||
lb.submit_to_backend(self._nsxv, self.EDGE_2)
|
||||
|
||||
mock_do_request.assert_called_with(
|
||||
nsxv_api.HTTP_PUT,
|
||||
self.LB_URI % self.EDGE_2,
|
||||
self.OUT_OBJ_JSON,
|
||||
format='json',
|
||||
encode=False)
|
Loading…
Reference in New Issue
Block a user