diff --git a/oslo_vmware/network/__init__.py b/oslo_vmware/network/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/oslo_vmware/network/nsx/__init__.py b/oslo_vmware/network/nsx/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/oslo_vmware/network/nsx/nsxv/__init__.py b/oslo_vmware/network/nsx/nsxv/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/oslo_vmware/network/nsx/nsxv/api/__init__.py b/oslo_vmware/network/nsx/nsxv/api/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/oslo_vmware/network/nsx/nsxv/api/api.py b/oslo_vmware/network/nsx/nsxv/api/api.py
new file mode 100644
index 00000000..ffa220b8
--- /dev/null
+++ b/oslo_vmware/network/nsx/nsxv/api/api.py
@@ -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
diff --git a/oslo_vmware/network/nsx/nsxv/api/api_helper.py b/oslo_vmware/network/nsx/nsxv/api/api_helper.py
new file mode 100644
index 00000000..3ea33150
--- /dev/null
+++ b/oslo_vmware/network/nsx/nsxv/api/api_helper.py
@@ -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)
diff --git a/oslo_vmware/network/nsx/nsxv/common/__init__.py b/oslo_vmware/network/nsx/nsxv/common/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/oslo_vmware/network/nsx/nsxv/common/exceptions.py b/oslo_vmware/network/nsx/nsxv/common/exceptions.py
new file mode 100644
index 00000000..aa517f97
--- /dev/null
+++ b/oslo_vmware/network/nsx/nsxv/common/exceptions.py
@@ -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")
diff --git a/oslo_vmware/network/nsx/nsxv/objects/__init__.py b/oslo_vmware/network/nsx/nsxv/objects/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/oslo_vmware/network/nsx/nsxv/objects/edge_cfg_obj.py b/oslo_vmware/network/nsx/nsxv/objects/edge_cfg_obj.py
new file mode 100644
index 00000000..95bc3e00
--- /dev/null
+++ b/oslo_vmware/network/nsx/nsxv/objects/edge_cfg_obj.py
@@ -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)
diff --git a/oslo_vmware/network/nsx/nsxv/objects/loadbalancer.py b/oslo_vmware/network/nsx/nsxv/objects/loadbalancer.py
new file mode 100644
index 00000000..5feb5120
--- /dev/null
+++ b/oslo_vmware/network/nsx/nsxv/objects/loadbalancer.py
@@ -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)
diff --git a/requirements-py3.txt b/requirements-py3.txt
index 8988bcbb..90c97353 100644
--- a/requirements-py3.txt
+++ b/requirements-py3.txt
@@ -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
diff --git a/requirements.txt b/requirements.txt
index dfb723a6..cbaee504 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -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
diff --git a/tests/network/__init__.py b/tests/network/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/network/nsx/__init__.py b/tests/network/nsx/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/network/nsx/nsxv/__init__.py b/tests/network/nsx/nsxv/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/network/nsx/nsxv/test_nsxv_api.py b/tests/network/nsx/nsxv/test_nsxv_api.py
new file mode 100644
index 00000000..5bd516bc
--- /dev/null
+++ b/tests/network/nsx/nsxv/test_nsxv_api.py
@@ -0,0 +1,1216 @@
+# 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
+from oslo_vmware.network.nsx.nsxv.api import api_helper
+from oslo_vmware.network.nsx.nsxv.common import exceptions
+from tests import base
+
+
+class NsxvApiTestCase(base.TestCase):
+
+ def setUp(self):
+ super(NsxvApiTestCase, self).setUp()
+ self.nsxv_api = api.NsxvApi('http://10.0.0.1', 'testuser', 'testpwd',
+ retries=2)
+
+ def _test_helper(self, nsxv_api, http_method, uri, data, h, v,
+ retval, decode, headers, *args):
+ with mock.patch.object(api_helper.NsxvApiHelper, 'request',
+ return_value=(h, v)) as mock_request:
+
+ h1, v1 = nsxv_api(*args)
+ mock_request.assert_has_calls([
+ mock.call().request(http_method, uri,
+ data, headers, decode)])
+
+ self.assertEqual(h, h1)
+ if retval is not None:
+ self.assertEqual(retval, v1)
+ else:
+ self.assertEqual(v, v1)
+
+ def test_deploy_edge(self):
+ h = {'status': '200', 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = [{'test1': 'test123'}]
+ self._test_helper(
+ self.nsxv_api.deploy_edge,
+ 'POST',
+ '/api/4.0/edges?async=true',
+ json.loads(v),
+ h, v, None, True, None, *args)
+
+ def test_update_edge(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', json.loads(v)]
+ self._test_helper(
+ self.nsxv_api.update_edge,
+ 'PUT',
+ '/api/4.0/edges/edge-x?async=true',
+ json.loads(v),
+ h, v, None, True, None, *args)
+
+ def test_get_edge_id(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['jobdata-15779']
+ self._test_helper(
+ self.nsxv_api.get_edge_id,
+ 'GET',
+ '/api/4.0/edges/jobs/jobdata-15779',
+ None, h, v, json.loads(v), True, None, *args)
+
+ def test_get_edge_jobs(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x']
+ self._test_helper(
+ self.nsxv_api.get_edge_jobs,
+ 'GET',
+ '/api/4.0/edges/edge-x/jobs',
+ None, h, v, json.loads(v), True, None, *args)
+
+ def test_get_edge_deploy_status(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x']
+ self._test_helper(
+ self.nsxv_api.get_edge_deploy_status,
+ 'GET',
+ '/api/4.0/edges/edge-x/status?getlatest=false',
+ None, h, v, json.loads(v), True, None, *args)
+
+ def test_delete_edge(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x']
+ self._test_helper(
+ self.nsxv_api.delete_edge,
+ 'DELETE',
+ '/api/4.0/edges/edge-x',
+ None, h, v, json.loads(v), True, None, *args)
+
+ def test_add_vdr_internal_interface(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', 1]
+ self._test_helper(
+ self.nsxv_api.add_vdr_internal_interface,
+ 'POST',
+ '/api/4.0/edges/edge-x/interfaces?action=patch&async=true',
+ 1, h, v, json.loads(v), True, None, *args)
+
+ def test_update_vdr_internal_interface(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', 1, json.loads(v)]
+ self._test_helper(
+ self.nsxv_api.update_vdr_internal_interface,
+ 'PUT',
+ '/api/4.0/edges/edge-x/interfaces/1?async=true',
+ json.loads(v), h, v, json.loads(v), True, None, *args)
+
+ def test_delete_vdr_internal_interface(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', 1]
+ self._test_helper(
+ self.nsxv_api.delete_vdr_internal_interface,
+ 'DELETE',
+ '/api/4.0/edges/edge-x/interfaces/1?async=true',
+ None, h, v, json.loads(v), True, None, *args)
+
+ def test_get_interfaces(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x']
+ self._test_helper(
+ self.nsxv_api.get_interfaces,
+ 'GET',
+ '/api/4.0/edges/edge-x/vnics',
+ None, h, v, json.loads(v), True, None, *args)
+
+ def test_update_interface(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"index": 1, "test1": "test123"}'
+
+ args = ['edge-x', json.loads(v)]
+ self._test_helper(
+ self.nsxv_api.update_interface,
+ 'PUT',
+ '/api/4.0/edges/edge-x/vnics/1?async=true',
+ json.loads(v), h, v, json.loads(v), True, None, *args)
+
+ def test_delete_interface(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"index": 1, "test1": "test123"}'
+
+ args = ['edge-x', 1]
+ self._test_helper(
+ self.nsxv_api.delete_interface,
+ 'DELETE',
+ '/api/4.0/edges/edge-x/vnics/1?async=true',
+ None, h, v, json.loads(v), True, None, *args)
+
+ def test_get_nat_config(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x']
+ self._test_helper(
+ self.nsxv_api.get_nat_config,
+ 'GET',
+ '/api/4.0/edges/edge-x/nat/config',
+ None, h, v, json.loads(v), True, None, *args)
+
+ def test_update_nat_config(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', json.loads(v)]
+ self._test_helper(
+ self.nsxv_api.update_nat_config,
+ 'PUT',
+ '/api/4.0/edges/edge-x/nat/config?async=true',
+ json.loads(v), h, v, json.loads(v), True, None, *args)
+
+ def test_delete_nat_rule(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', 1]
+ self._test_helper(
+ self.nsxv_api.delete_nat_rule,
+ 'DELETE',
+ '/api/4.0/edges/edge-x/nat/config/rules/1',
+ None, h, v, json.loads(v), True, None, *args)
+
+ def test_get_edge_status(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x']
+ self._test_helper(
+ self.nsxv_api.get_edge_status,
+ 'GET',
+ '/api/4.0/edges/edge-x/status?getlatest=false',
+ None, h, v, json.loads(v), True, None, *args)
+
+ def test_get_edges(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = []
+ self._test_helper(
+ self.nsxv_api.get_edges,
+ 'GET',
+ '/api/4.0/edges',
+ None, h, v, json.loads(v), True, None, *args)
+
+ def test_get_edge_interfaces(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x']
+ self._test_helper(
+ self.nsxv_api.get_edge_interfaces,
+ 'GET',
+ '/api/4.0/edges/edge-x/interfaces',
+ None, h, v, json.loads(v), True, None, *args)
+
+ def test_update_routes(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', json.loads(v)]
+ self._test_helper(
+ self.nsxv_api.update_routes,
+ 'PUT',
+ '/api/4.0/edges/edge-x/routing/config/static?async=true',
+ json.loads(v), h, v, json.loads(v), True, None, *args)
+
+ def test_create_lswitch(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = [json.loads(v)]
+ self._test_helper(
+ self.nsxv_api.create_lswitch,
+ 'POST',
+ '/api/ws.v1/lswitch',
+ json.loads(v), h, v, json.loads(v), True, None, *args)
+
+ def test_delete_lswitch(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = [123]
+ self._test_helper(
+ self.nsxv_api.delete_lswitch,
+ 'DELETE',
+ '/api/ws.v1/lswitch/123',
+ None, h, v, json.loads(v), True, None, *args)
+
+ def test_get_loadbalancer_config(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x']
+ self._test_helper(
+ self.nsxv_api.get_loadbalancer_config,
+ 'GET',
+ '/api/4.0/edges/edge-x/loadbalancer/config',
+ None, h, v, json.loads(v), True, None, *args)
+
+ def test_enable_service_loadbalancer(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', json.loads(v)]
+ self._test_helper(
+ self.nsxv_api.enable_service_loadbalancer,
+ 'PUT',
+ '/api/4.0/edges/edge-x/loadbalancer/config',
+ json.loads(v), h, v, json.loads(v), True, None, *args)
+
+ def test_update_firewall(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', json.loads(v)]
+ self._test_helper(
+ self.nsxv_api.update_firewall,
+ 'PUT',
+ '/api/4.0/edges/edge-x/firewall/config?async=true',
+ json.loads(v), h, v, json.loads(v), True, None, *args)
+
+ def test_delete_firewall(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x']
+ self._test_helper(
+ self.nsxv_api.delete_firewall,
+ 'DELETE',
+ '/api/4.0/edges/edge-x/firewall/config?async=true',
+ None, h, v, json.loads(v), True, None, *args)
+
+ def test_update_firewall_rule(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', '123', json.loads(v)]
+ self._test_helper(
+ self.nsxv_api.update_firewall_rule,
+ 'PUT',
+ '/api/4.0/edges/edge-x/firewall/config/rules/123',
+ json.loads(v), h, v, json.loads(v), True, None, *args)
+
+ def test_delete_firewall_rule(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', '123']
+ self._test_helper(
+ self.nsxv_api.delete_firewall_rule,
+ 'DELETE',
+ '/api/4.0/edges/edge-x/firewall/config/rules/123',
+ None, h, v, json.loads(v), True, None, *args)
+
+ def test_add_firewall_rule_above(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', '123', json.loads(v)]
+ self._test_helper(
+ self.nsxv_api.add_firewall_rule_above,
+ 'POST',
+ '/api/4.0/edges/edge-x/firewall/config/rules?aboveRuleId=123',
+ json.loads(v), h, v, json.loads(v), True, None, *args)
+
+ def test_add_firewall_rule(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', json.loads(v)]
+ self._test_helper(
+ self.nsxv_api.add_firewall_rule,
+ 'POST',
+ '/api/4.0/edges/edge-x/firewall/config/rules',
+ json.loads(v), h, v, json.loads(v), True, None, *args)
+
+ def test_get_firewall(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x']
+ self._test_helper(
+ self.nsxv_api.get_firewall,
+ 'GET',
+ '/api/4.0/edges/edge-x/firewall/config',
+ None, h, v, json.loads(v), True, None, *args)
+
+ def test_get_firewall_rule(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', 123]
+ self._test_helper(
+ self.nsxv_api.get_firewall_rule,
+ 'GET',
+ '/api/4.0/edges/edge-x/firewall/config/rules/123',
+ None, h, v, json.loads(v), True, None, *args)
+
+ def test_create_vip(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', json.loads(v)]
+ self._test_helper(
+ self.nsxv_api.create_vip,
+ 'POST',
+ '/api/4.0/edges/edge-x/loadbalancer/config/virtualservers',
+ json.loads(v), h, v, json.loads(v), True, None, *args)
+
+ def test_get_vip(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', 123]
+ self._test_helper(
+ self.nsxv_api.get_vip,
+ 'GET',
+ '/api/4.0/edges/edge-x/loadbalancer/config/virtualservers/123',
+ None, h, v, json.loads(v), True, None, *args)
+
+ def test_update_vip(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', 123, json.loads(v)]
+ self._test_helper(
+ self.nsxv_api.update_vip,
+ 'PUT',
+ '/api/4.0/edges/edge-x/loadbalancer/config/virtualservers/123',
+ json.loads(v), h, v, json.loads(v), True, None, *args)
+
+ def test_delete_vip(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', 123]
+ self._test_helper(
+ self.nsxv_api.delete_vip,
+ 'DELETE',
+ '/api/4.0/edges/edge-x/loadbalancer/config/virtualservers/123',
+ None, h, v, json.loads(v), True, None, *args)
+
+ def test_create_pool(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', json.loads(v)]
+ self._test_helper(
+ self.nsxv_api.create_pool,
+ 'POST',
+ '/api/4.0/edges/edge-x/loadbalancer/config/pools',
+ json.loads(v), h, v, json.loads(v), True, None, *args)
+
+ def test_get_pool(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', 123]
+ self._test_helper(
+ self.nsxv_api.get_pool,
+ 'GET',
+ '/api/4.0/edges/edge-x/loadbalancer/config/pools/123',
+ None, h, v, json.loads(v), True, None, *args)
+
+ def test_update_pool(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', 123, json.loads(v)]
+ self._test_helper(
+ self.nsxv_api.update_pool,
+ 'PUT',
+ '/api/4.0/edges/edge-x/loadbalancer/config/pools/123',
+ json.loads(v), h, v, json.loads(v), True, None, *args)
+
+ def test_delete_pool(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', 123]
+ self._test_helper(
+ self.nsxv_api.delete_pool,
+ 'DELETE',
+ '/api/4.0/edges/edge-x/loadbalancer/config/pools/123',
+ None, h, v, json.loads(v), True, None, *args)
+
+ def test_create_health_monitor(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', json.loads(v)]
+ self._test_helper(
+ self.nsxv_api.create_health_monitor,
+ 'POST',
+ '/api/4.0/edges/edge-x/loadbalancer/config/monitors',
+ json.loads(v), h, v, json.loads(v), True, None, *args)
+
+ def test_get_health_monitor(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', 123]
+ self._test_helper(
+ self.nsxv_api.get_health_monitor,
+ 'GET',
+ '/api/4.0/edges/edge-x/loadbalancer/config/monitors/123',
+ None, h, v, json.loads(v), True, None, *args)
+
+ def test_update_health_monitor(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', 123, json.loads(v)]
+ self._test_helper(
+ self.nsxv_api.update_health_monitor,
+ 'PUT',
+ '/api/4.0/edges/edge-x/loadbalancer/config/monitors/123',
+ json.loads(v), h, v, json.loads(v), True, None, *args)
+
+ def test_delete_health_monitor(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', 123]
+ self._test_helper(
+ self.nsxv_api.delete_health_monitor,
+ 'DELETE',
+ '/api/4.0/edges/edge-x/loadbalancer/config/monitors/123',
+ None, h, v, json.loads(v), True, None, *args)
+
+ def test_create_app_profile(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', json.loads(v)]
+ self._test_helper(
+ self.nsxv_api.create_app_profile,
+ 'POST',
+ '/api/4.0/edges/edge-x/loadbalancer/config/applicationprofiles',
+ json.loads(v), h, v, json.loads(v), True, None, *args)
+
+ def test_update_app_profile(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', 1, json.loads(v)]
+ self._test_helper(
+ self.nsxv_api.update_app_profile,
+ 'PUT',
+ '/api/4.0/edges/edge-x/loadbalancer/config/applicationprofiles/1',
+ json.loads(v), h, v, json.loads(v), True, None, *args)
+
+ def test_delete_app_profile(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', 1]
+ self._test_helper(
+ self.nsxv_api.delete_app_profile,
+ 'DELETE',
+ '/api/4.0/edges/edge-x/loadbalancer/config/applicationprofiles/1',
+ None, h, v, json.loads(v), True, None, *args)
+
+ def test_create_app_rule(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', json.loads(v)]
+ self._test_helper(
+ self.nsxv_api.create_app_rule,
+ 'POST',
+ '/api/4.0/edges/edge-x/loadbalancer/config/applicationrules',
+ json.loads(v), h, v, json.loads(v), True, None, *args)
+
+ def test_update_app_rule(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', 123, json.loads(v)]
+ self._test_helper(
+ self.nsxv_api.update_app_rule,
+ 'PUT',
+ '/api/4.0/edges/edge-x/loadbalancer/config/applicationrules/123',
+ json.loads(v), h, v, json.loads(v), True, None, *args)
+
+ def test_delete_app_rule(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', 123]
+ self._test_helper(
+ self.nsxv_api.delete_app_rule,
+ 'DELETE',
+ '/api/4.0/edges/edge-x/loadbalancer/config/applicationrules/123',
+ None, h, v, json.loads(v), True, None, *args)
+
+ def test_update_ipsec_config(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', json.loads(v)]
+ self._test_helper(
+ self.nsxv_api.update_ipsec_config,
+ 'PUT',
+ '/api/4.0/edges/edge-x/ipsec/config',
+ json.loads(v), h, v, json.loads(v), True, None, *args)
+
+ def test_delete_ipsec_config(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x']
+ self._test_helper(
+ self.nsxv_api.delete_ipsec_config,
+ 'DELETE',
+ '/api/4.0/edges/edge-x/ipsec/config',
+ None, h, v, json.loads(v), True, None, *args)
+
+ def test_get_ipsec_config(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x']
+ self._test_helper(
+ self.nsxv_api.get_ipsec_config,
+ 'GET',
+ '/api/4.0/edges/edge-x/ipsec/config',
+ None, h, v, json.loads(v), True, None, *args)
+
+ def test_create_virtual_wire(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/xml'}
+ v = 'test123'
+
+ args = ['vdn-scope-x', {'test1': 'test123'}]
+ self._test_helper(
+ self.nsxv_api.create_virtual_wire,
+ 'POST',
+ '/api/2.0/vdn/scopes/vdn-scope-x/virtualwires',
+ {'test1': 'test123'}, h, v, None, True, None, *args)
+
+ def test_delete_virtual_wire(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/xml'}
+
+ args = [123]
+ self._test_helper(
+ self.nsxv_api.delete_virtual_wire,
+ 'DELETE',
+ '/api/2.0/vdn/virtualwires/123',
+ None, h, '', {}, True, None, *args)
+
+ def test_create_port_group(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/xml'}
+ v = 'test123'
+
+ args = [123, {'test1': 'test123'}]
+ self._test_helper(
+ self.nsxv_api.create_port_group,
+ 'POST',
+ '/api/2.0/xvs/switches/123/networks',
+ {'test1': 'test123'}, h, v, None, True, None, *args)
+
+ def test_delete_port_group(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+
+ args = [123, 456]
+ self._test_helper(
+ self.nsxv_api.delete_port_group,
+ 'DELETE',
+ '/api/2.0/xvs/switches/123/networks/456',
+ None, h, '', {}, True, None, *args)
+
+ def test_query_interface(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', 123]
+ self._test_helper(
+ self.nsxv_api.query_interface,
+ 'GET',
+ '/api/4.0/edges/edge-x/vnics/123',
+ None, h, v, json.loads(v), True, None, *args)
+
+ def test_reconfigure_dhcp_service(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', json.loads(v)]
+ self._test_helper(
+ self.nsxv_api.reconfigure_dhcp_service,
+ 'PUT',
+ '/api/4.0/edges/edge-x/dhcp/config?async=true',
+ json.loads(v), h, v, json.loads(v), True, None, *args)
+
+ def test_query_dhcp_configuration(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x']
+ self._test_helper(
+ self.nsxv_api.query_dhcp_configuration,
+ 'GET',
+ '/api/4.0/edges/edge-x/dhcp/config',
+ None, h, v, json.loads(v), True, None, *args)
+
+ def test_create_dhcp_binding(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+ v = '{"test1": "test123"}'
+
+ args = ['edge-x', json.loads(v)]
+ self._test_helper(
+ self.nsxv_api.create_dhcp_binding,
+ 'POST',
+ '/api/4.0/edges/edge-x/dhcp/config/bindings?async=true',
+ json.loads(v), h, None, None, True, None, *args)
+
+ def test_delete_dhcp_binding(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+
+ args = ['edge-x', 456]
+ self._test_helper(
+ self.nsxv_api.delete_dhcp_binding,
+ 'DELETE',
+ '/api/4.0/edges/edge-x/dhcp/config/bindings/456?async=true',
+ None, h, '', {}, True, None, *args)
+
+ def test_create_security_group(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/xml'}
+ v = 'test123'
+
+ args = [{'test1': 'test123'}]
+ self._test_helper(
+ self.nsxv_api.create_security_group,
+ 'POST',
+ '/api/2.0/services/securitygroup/globalroot-0',
+ {'test1': 'test123'}, h, v, None, True, None, *args)
+
+ def test_delete_security_group(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}
+
+ args = [123]
+ self._test_helper(
+ self.nsxv_api.delete_security_group,
+ 'DELETE',
+ '/api/2.0/services/securitygroup/123?force=true',
+ None, h, '', {}, True, None, *args)
+
+ def test_create_section_ip(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/xml'}
+ v = 'test123'
+
+ args = ['ip', {'test1': 'test123'}]
+ self._test_helper(
+ self.nsxv_api.create_section,
+ 'POST',
+ '/api/4.0/firewall/globalroot-0/'
+ 'config/layer3sections?autoSaveDraft=false',
+ {'test1': 'test123'}, h, v, None, False, None, *args)
+
+ def test_create_section_eth(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/xml'}
+ v = 'test123'
+
+ args = ['eth', {'test1': 'test123'}]
+ self._test_helper(
+ self.nsxv_api.create_section,
+ 'POST',
+ '/api/4.0/firewall/globalroot-0/'
+ 'config/layer2sections?autoSaveDraft=false',
+ {'test1': 'test123'}, h, v, None, False, None, *args)
+
+ def test_update_section(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/xml'}
+ v = 'test123'
+
+ args = ['/api/4.0/firewall/globalroot-0/config/layer3sections/123',
+ {'test1': 'test123'}, {'etag': '1234'}]
+ self._test_helper(
+ self.nsxv_api.update_section,
+ 'PUT',
+ '/api/4.0/firewall/globalroot-0/config/'
+ 'layer3sections/123?autoSaveDraft=false',
+ {'test1': 'test123'}, h, v, None, False, {'If-Match': '1234'},
+ *args)
+
+ def test_delete_section(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/xml'}
+
+ args = ['/api/4.0/firewall/globalroot-0/config/layer3sections/123s']
+ self._test_helper(
+ self.nsxv_api.delete_section,
+ 'DELETE',
+ '/api/4.0/firewall/globalroot-0/config/'
+ 'layer3sections/123s?autoSaveDraft=false',
+ None, h, '', {}, True, None, *args)
+
+ def test_get_section(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/xml'}
+ v = 'test123'
+
+ args = ['/api/4.0/firewall/globalroot-0/config/layer3sections/123']
+ self._test_helper(
+ self.nsxv_api.get_section,
+ 'GET',
+ '/api/4.0/firewall/globalroot-0/config/layer3sections/123',
+ None, h, v, v, True, None, *args)
+
+ def test_get_section_id(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/xml'}
+ v = (''
+ ''
+ ''
+ ''
+ ''
+ '')
+
+ with mock.patch.object(api_helper.NsxvApiHelper, 'request',
+ return_value=(h, v)) as mock_request:
+
+ ret = self.nsxv_api.get_section_id('test5')
+ mock_request.assert_has_calls([
+ mock.call().request(
+ 'GET',
+ '/api/4.0/firewall/globalroot-0/config',
+ None, None, True)])
+
+ self.assertEqual(ret, '5')
+
+ with mock.patch.object(api_helper.NsxvApiHelper, 'request',
+ return_value=(h, v)) as mock_request:
+
+ ret = self.nsxv_api.get_section_id('test4')
+ mock_request.assert_has_calls([
+ mock.call().request(
+ 'GET',
+ '/api/4.0/firewall/globalroot-0/config',
+ None, None, True)])
+
+ self.assertEqual(ret, '4')
+
+ def _fake_request_for_test_update_section_by_id(
+ self, method, uri, params=None, headers=None, encodeparams=True):
+ # If this is the call from _get_section_header() act accordingly
+ v = '{"test1": "test123"}'
+
+ if(method == 'GET'
+ and uri == ('/api/4.0/firewall/globalroot-0/config/'
+ 'layer3sections/test4')
+ and params is None and headers is None and encodeparams):
+ return {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json',
+ 'etag': '1234'}, ''
+ # That is the call from update_section_by_id()
+ elif(method == 'PUT'
+ and uri == ('/api/4.0/firewall/globalroot-0/config/'
+ 'layer3sections/test4?autoSaveDraft=false')
+ and headers == {'If-Match': '1234'}
+ and params == json.loads(v)):
+ return {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}, ''
+ else:
+ self.fail()
+
+ def test_update_section_by_id(self):
+ v = '{"test1": "test123"}'
+
+ with mock.patch.object(
+ api_helper.NsxvApiHelper, 'request',
+ self._fake_request_for_test_update_section_by_id):
+ ret = self.nsxv_api.update_section_by_id(
+ 'test4', 'ip', json.loads(v))
+ self.assertEqual(ret, None)
+
+ def _fake_request_for_test_remove_rule_from_section(
+ self, method, uri, params=None, headers=None, encodeparams=True):
+ # If this is the call from _get_section_header() act accordingly
+
+ if(method == 'GET'
+ and uri == ('/api/4.0/firewall/globalroot-0/config/'
+ 'layer3sections/123')
+ and params is None and headers is None and encodeparams):
+ return {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json',
+ 'etag': '1234'}, ''
+ # That is the call from remove_rule_from_section()
+ elif(method == 'DELETE'
+ and uri == ('/api/4.0/firewall/globalroot-0/config/layer3sections'
+ '/123/rules/1234?autoSaveDraft=false')
+ and headers == {'If-Match': '1234'}
+ and params is None):
+ return {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}, ''
+ else:
+ self.fail()
+
+ def test_remove_rule_from_section(self):
+ with mock.patch.object(
+ api_helper.NsxvApiHelper, 'request',
+ self._fake_request_for_test_remove_rule_from_section):
+ h, v = self.nsxv_api.remove_rule_from_section(
+ '/api/4.0/firewall/globalroot-0/config/layer3sections/123',
+ '1234')
+ self.assertEqual(h, {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'})
+ self.assertEqual(v, {})
+
+ def test_add_member_to_security_group(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/xml'}
+ v = 'test123'
+
+ args = ['123', '456']
+ self._test_helper(
+ self.nsxv_api.add_member_to_security_group,
+ 'PUT',
+ '/api/2.0/services/securitygroup/123/members/456',
+ None, h, v, None, True, None, *args)
+
+ def test_remove_member_from_security_group(self):
+ h = {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/xml'}
+
+ args = ['123', '456']
+ self._test_helper(
+ self.nsxv_api.remove_member_from_security_group,
+ 'DELETE',
+ '/api/2.0/services/securitygroup/123/members/456',
+ None, h, None, None, True, None, *args)
+
+
+class NsxvApiRetryTestCase(base.TestCase):
+
+ def setUp(self):
+ super(NsxvApiRetryTestCase, self).setUp()
+ self.nsxv_api = api.NsxvApi('http://10.0.0.1', 'testuser', 'testpwd',
+ retries=2)
+
+ def _fake_request_1_retry(
+ self, method, uri, params=None, headers=None, encodeparams=True):
+ """Throw exception on 1st call, succeed on second attempt."""
+ if(method == 'POST'
+ and uri == '/api/4.0/edges?async=true'
+ and params == {'test1': 'test123'}
+ and headers is None and encodeparams):
+ if self._ctr == 1:
+ self._ctr += 1
+ raise exceptions.ServiceConflict
+ elif self._ctr == 2:
+ return {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'}, ''
+ else:
+ self.fail()
+
+ def test_deploy_edge_1_retry(self):
+ self._ctr = 1
+
+ with mock.patch.object(
+ api_helper.NsxvApiHelper, 'request',
+ self._fake_request_1_retry):
+ h, v = self.nsxv_api.deploy_edge({'test1': 'test123'})
+ self.assertEqual(h, {'status': '200',
+ 'connection': 'keep-alive',
+ 'content-type': 'application/json'})
+ self.assertEqual(v, {})
+
+ def _fake_request_2_retries(
+ self, method, uri, params=None, headers=None, encodeparams=True):
+ """Always fail, just valiadate header params."""
+ if(method == 'POST'
+ and uri == '/api/4.0/edges?async=true'
+ and params == {'test1': 'test123'}
+ and headers is None and encodeparams):
+ raise exceptions.ServiceConflict
+ else:
+ self.fail()
+
+ def test_deploy_edge_2_retries_fail(self):
+ self._ctr = 1
+
+ with mock.patch.object(
+ api_helper.NsxvApiHelper, 'request',
+ self._fake_request_2_retries):
+ self.assertRaises(exceptions.ServiceConflict,
+ self.nsxv_api.deploy_edge, {'test1': 'test123'})
+
+
+class NsxvConfigValidationTestCase(base.TestCase):
+
+ SWITCHES_XML = (
+ ''
+ 'dvs-15'
+ '')
+
+ SCOPINGOBJECTS_XML = (
+ '')
+
+ VDNSCOPES_XML = (
+ ''
+ 'vdnscope-1')
+
+ @mock.patch.object(api_helper, 'NsxvApiHelper')
+ def setUp(self, _mock_client):
+ super(NsxvConfigValidationTestCase, self).setUp()
+ self._nsxv_api = api.NsxvApi(None, None, None)
+
+ def test_validate_dvs_success(self):
+ h = None
+ v = self.SWITCHES_XML
+
+ with mock.patch.object(self._nsxv_api, 'do_request',
+ return_value=(h, v)):
+ self.assertTrue(self._nsxv_api.validate_dvs('dvs-15'))
+
+ def test_validate_dvs_fail(self):
+ h = None
+ v = self.SWITCHES_XML
+
+ with mock.patch.object(self._nsxv_api, 'do_request',
+ return_value=(h, v)):
+ self.assertFalse(self._nsxv_api.validate_dvs('dvs-14'))
+
+ def test_validate_datacenter_moid_success(self):
+ h = None
+ v = self.SCOPINGOBJECTS_XML
+
+ with mock.patch.object(self._nsxv_api, 'do_request',
+ return_value=(h, v)):
+ self.assertTrue(self._nsxv_api.validate_datacenter_moid(
+ 'datacenter-2'))
+
+ def test_validate_datacenter_moid_fail(self):
+ h = None
+ v = self.SCOPINGOBJECTS_XML
+
+ with mock.patch.object(self._nsxv_api, 'do_request',
+ return_value=(h, v)):
+ self.assertFalse(self._nsxv_api.validate_datacenter_moid(
+ 'network-23'))
+
+ def test_validate_network_success(self):
+ h = None
+ v = self.SCOPINGOBJECTS_XML
+
+ with mock.patch.object(self._nsxv_api, 'do_request',
+ return_value=(h, v)):
+ self.assertTrue(self._nsxv_api.validate_network(
+ 'network-23'))
+
+ def test_validate_network_fail(self):
+ h = None
+ v = self.SCOPINGOBJECTS_XML
+
+ with mock.patch.object(self._nsxv_api, 'do_request',
+ return_value=(h, v)):
+ self.assertFalse(self._nsxv_api.validate_network(
+ 'network-24'))
+
+ def test_validate_vdn_scope_success(self):
+ h = None
+ v = self.VDNSCOPES_XML
+
+ with mock.patch.object(self._nsxv_api, 'do_request',
+ return_value=(h, v)):
+ self.assertTrue(self._nsxv_api.validate_vdn_scope(
+ 'vdnscope-1'))
+
+ def test_validate_vdn_scope_fail(self):
+ h = None
+ v = self.VDNSCOPES_XML
+
+ with mock.patch.object(self._nsxv_api, 'do_request',
+ return_value=(h, v)):
+ self.assertFalse(self._nsxv_api.validate_vdn_scope(
+ 'vdnscope-2'))
diff --git a/tests/network/nsx/nsxv/test_nsxv_api_helper.py b/tests/network/nsx/nsxv/test_nsxv_api_helper.py
new file mode 100644
index 00000000..c58207de
--- /dev/null
+++ b/tests/network/nsx/nsxv/test_nsxv_api_helper.py
@@ -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 = ''
+
+ 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 = ''
+
+ 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 = ''
+ 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 = ''
+ 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='testing123',
+ headers={
+ 'Content-Type': 'application/xml',
+ 'Authorization': 'Basic dGVzdHVzZXI6dGVzdHBhc3M=',
+ 'Accept': ('application/xml',)})])
diff --git a/tests/network/nsx/nsxv/test_nsxv_loadbalancer.py b/tests/network/nsx/nsxv/test_nsxv_loadbalancer.py
new file mode 100644
index 00000000..b0f0b922
--- /dev/null
+++ b/tests/network/nsx/nsxv/test_nsxv_loadbalancer.py
@@ -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)