16b105f7f5
This patch adds multi-manager support to the NSX v3 plugin thereby enabling the ability to specify multiple NSX managers for the v3 plugin. This implementation supports the same basic features as the MH multi-manager support does including; timeouts, retries, keep-alive, etc.. The approach in a nutshell is to introduce a "proxy" class which looks like a requests or requests.Session object and can be used in place of requests in the NSX REST API client. Under the covers this class handles management of endpoint selection and connectivity. Also note that with this patch your devstack local rc / conf no longer needs to specify NSX_CONTROLLERS when using the v3 plugin. Instead a comma list of managers is supported on the NSX_MANAGERS devstack var. Closes-Bug: #1524046 Change-Id: I433a4b9ea73de0680d64d86e2f826c092adfba87
233 lines
7.4 KiB
Python
233 lines
7.4 KiB
Python
# Copyright (c) 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 requests
|
|
import urlparse
|
|
|
|
from oslo_serialization import jsonutils
|
|
from oslo_utils import uuidutils
|
|
from vmware_nsx.common import nsx_constants
|
|
|
|
|
|
FAKE_NAME = "fake_name"
|
|
DEFAULT_TIER0_ROUTER_UUID = "efad0078-9204-4b46-a2d8-d4dd31ed448f"
|
|
FAKE_MANAGER = "fake_manager_ip"
|
|
|
|
|
|
def make_fake_switch(switch_uuid=None, tz_uuid=None, name=FAKE_NAME):
|
|
if not switch_uuid:
|
|
switch_uuid = uuidutils.generate_uuid()
|
|
if not tz_uuid:
|
|
tz_uuid = uuidutils.generate_uuid()
|
|
|
|
fake_switch = {
|
|
"id": switch_uuid,
|
|
"display_name": name,
|
|
"resource_type": "LogicalSwitch",
|
|
"address_bindings": [],
|
|
"transport_zone_id": tz_uuid,
|
|
"replication_mode": nsx_constants.MTEP,
|
|
"admin_state": nsx_constants.ADMIN_STATE_UP,
|
|
"vni": 50056,
|
|
"switching_profile_ids": [
|
|
{
|
|
"value": "64814784-7896-3901-9741-badeff705639",
|
|
"key": "IpDiscoverySwitchingProfile"
|
|
},
|
|
{
|
|
"value": "fad98876-d7ff-11e4-b9d6-1681e6b88ec1",
|
|
"key": "SpoofGuardSwitchingProfile"
|
|
},
|
|
{
|
|
"value": "93b4b7e8-f116-415d-a50c-3364611b5d09",
|
|
"key": "PortMirroringSwitchingProfile"
|
|
},
|
|
{
|
|
"value": "fbc4fb17-83d9-4b53-a286-ccdf04301888",
|
|
"key": "SwitchSecuritySwitchingProfile"
|
|
},
|
|
{
|
|
"value": "f313290b-eba8-4262-bd93-fab5026e9495",
|
|
"key": "QosSwitchingProfile"
|
|
}
|
|
],
|
|
}
|
|
return fake_switch
|
|
|
|
|
|
def get_resource(resource):
|
|
return {'id': resource.split('/')[-1]}
|
|
|
|
|
|
def create_resource(resource, data):
|
|
data['id'] = uuidutils.generate_uuid()
|
|
return data
|
|
|
|
|
|
def update_resource(resource, data):
|
|
return resource
|
|
|
|
|
|
def delete_resource(resource):
|
|
pass
|
|
|
|
|
|
class MockRequestsResponse(object):
|
|
def __init__(self, status_code, content=None):
|
|
self.status_code = status_code
|
|
self.content = content
|
|
|
|
def json(self):
|
|
return jsonutils.loads(self.content)
|
|
|
|
|
|
class MockRequestSessionApi(object):
|
|
|
|
def __init__(self):
|
|
self._store = {}
|
|
|
|
def _format_uri(self, uri):
|
|
uri = urlparse.urlparse(uri).path
|
|
while uri.endswith('/'):
|
|
uri = uri[:-1]
|
|
while uri.startswith('/'):
|
|
uri = uri[1:]
|
|
if not self._is_uuid_uri(uri):
|
|
uri = "%s/" % uri
|
|
return uri
|
|
|
|
def _is_uuid_uri(self, uri):
|
|
return uuidutils.is_uuid_like(
|
|
urlparse.urlparse(uri).path.split('/')[-1])
|
|
|
|
def _query(self, search_key, copy=True):
|
|
items = []
|
|
for uri, obj in self._store.items():
|
|
if uri.startswith(search_key):
|
|
items.append(obj.copy() if copy else obj)
|
|
return items
|
|
|
|
def _build_response(self, url, content=None,
|
|
status=requests.codes.ok, **kwargs):
|
|
if type(content) is list:
|
|
content = {
|
|
'result_count': len(content),
|
|
'results': content
|
|
}
|
|
|
|
if (content is not None and kwargs.get('headers', {}).get(
|
|
'Content-Type') == 'application/json'):
|
|
content = jsonutils.dumps(content)
|
|
|
|
return MockRequestsResponse(status, content=content)
|
|
|
|
def _get_content(self, **kwargs):
|
|
content = kwargs.get('data', None)
|
|
if content and kwargs.get('headers', {}).get(
|
|
'Content-Type') == 'application/json':
|
|
content = jsonutils.loads(content)
|
|
return content
|
|
|
|
def get(self, url, **kwargs):
|
|
url = self._format_uri(url)
|
|
|
|
if self._is_uuid_uri(url):
|
|
item = self._store.get(url)
|
|
code = requests.codes.ok if item else requests.codes.not_found
|
|
return self._build_response(
|
|
url, content=item, status=code, **kwargs)
|
|
|
|
return self._build_response(
|
|
url, content=self._query(url), status=requests.codes.ok, **kwargs)
|
|
|
|
def _create(self, url, content, **kwargs):
|
|
resource_id = content.get('id')
|
|
if resource_id and self._store.get("%s%s" % (url, resource_id)):
|
|
return self._build_response(
|
|
url, content=None, status=requests.codes.bad, **kwargs)
|
|
|
|
resource_id = resource_id or uuidutils.generate_uuid()
|
|
content['id'] = resource_id
|
|
|
|
self._store["%s%s" % (url, resource_id)] = content.copy()
|
|
return content
|
|
|
|
def post(self, url, **kwargs):
|
|
parsed_url = urlparse.urlparse(url)
|
|
url = self._format_uri(url)
|
|
|
|
if self._is_uuid_uri(url):
|
|
if self._store.get(url) is None:
|
|
return self._build_response(
|
|
url, content=None, status=requests.codes.bad, **kwargs)
|
|
|
|
body = self._get_content(**kwargs)
|
|
if body is None:
|
|
return self._build_response(
|
|
url, content=None, status=requests.codes.bad, **kwargs)
|
|
|
|
response_content = None
|
|
|
|
url_queries = urlparse.parse_qs(parsed_url.query)
|
|
if 'create_multiple' in url_queries.get('action', []):
|
|
response_content = {}
|
|
for resource_name, resource_body in body.items():
|
|
for new_resource in resource_body:
|
|
created_resource = self._create(
|
|
url, new_resource, **kwargs)
|
|
if response_content.get(resource_name, None) is None:
|
|
response_content[resource_name] = []
|
|
response_content[resource_name].append(created_resource)
|
|
else:
|
|
response_content = self._create(url, body, **kwargs)
|
|
|
|
if isinstance(response_content, MockRequestsResponse):
|
|
return response_content
|
|
|
|
return self._build_response(
|
|
url, content=response_content,
|
|
status=requests.codes.created, **kwargs)
|
|
|
|
def put(self, url, **kwargs):
|
|
url = self._format_uri(url)
|
|
|
|
item = {}
|
|
if self._is_uuid_uri(url):
|
|
item = self._store.get(url, None)
|
|
if item is None:
|
|
return self._build_response(
|
|
url, content=None,
|
|
status=requests.codes.not_found, **kwargs)
|
|
|
|
body = self._get_content(**kwargs)
|
|
if body is None:
|
|
return self._build_response(
|
|
url, content=None, status=requests.codes.bad, **kwargs)
|
|
|
|
item.update(body)
|
|
self._store[url] = item
|
|
return self._build_response(
|
|
url, content=item, status=requests.codes.ok, **kwargs)
|
|
|
|
def delete(self, url, **kwargs):
|
|
url = self._format_uri(url)
|
|
|
|
if not self._store.get(url):
|
|
return self._build_response(
|
|
url, content=None, status=requests.codes.not_found, **kwargs)
|
|
|
|
del self._store[url]
|
|
return self._build_response(
|
|
url, content=None, status=requests.codes.ok, **kwargs)
|