Merge "nsx v3 port security plumbing"

This commit is contained in:
Jenkins 2015-09-22 08:28:08 +00:00 committed by Gerrit Code Review
commit 3c1499b839
5 changed files with 806 additions and 70 deletions

View File

@ -1,4 +1,4 @@
# Copyright 2015 OpenStack Foundation
# Copyright 2015 VMware, Inc.
# All Rights Reserved
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@ -12,14 +12,13 @@
# 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
from neutron.i18n import _LW, _
from oslo_config import cfg
from oslo_log import log
from oslo_serialization import jsonutils
import requests
from requests import auth
from neutron.i18n import _LW
from vmware_nsx.common import exceptions as nsx_exc
LOG = log.getLogger(__name__)
@ -28,88 +27,197 @@ ERRORS = {requests.codes.NOT_FOUND: nsx_exc.ResourceNotFound,
requests.codes.PRECONDITION_FAILED: nsx_exc.StaleRevision}
def _get_manager_endpoint():
manager = _get_manager_ip()
username = cfg.CONF.nsx_v3.nsx_user
password = cfg.CONF.nsx_v3.nsx_password
verify_cert = not cfg.CONF.nsx_v3.insecure
return "https://%s" % manager, username, password, verify_cert
class RESTClient(object):
_VERB_RESP_CODES = {
'get': [requests.codes.ok],
'post': [requests.codes.created, requests.codes.ok],
'put': [requests.codes.ok],
'delete': [requests.codes.ok]
}
def _get_manager_ip():
# NOTE: In future this may return the IP address from a pool
manager = cfg.CONF.nsx_v3.nsx_manager
return manager
def __init__(self, host_ip=None, user_name=None,
password=None, insecure=None,
url_prefix=None, default_headers=None,
cert_file=None):
self._host_ip = host_ip
self._user_name = user_name
self._password = password
self._insecure = insecure if insecure is not None else False
self._url_prefix = url_prefix or ""
self._default_headers = default_headers or {}
self._cert_file = cert_file
self._session = requests.Session()
self._session.auth = (self._user_name, self._password)
if not insecure and self._cert_file:
self._session.cert = self._cert_file
def _validate_result(result, expected, operation):
if result.status_code not in expected:
if (result.status_code == requests.codes.bad):
def new_client_for(self, *uri_segments):
uri = "%s/%s" % (self._url_prefix, '/'.join(uri_segments))
uri = uri.replace('//', '/')
return self.__class__(
host_ip=self._host_ip, user_name=self._user_name,
password=self._password, insecure=self._insecure,
url_prefix=uri,
default_headers=self._default_headers,
cert_file=self._cert_file)
@property
def validate_certificate(self):
return not self._insecure
def list(self, headers=None):
return self.url_list('')
def get(self, uuid, headers=None):
return self.url_get(uuid, headers=headers)
def delete(self, uuid, headers=None):
return self.url_delete(uuid, headers=headers)
def update(self, uuid, body=None, headers=None):
return self.url_put(uuid, body, headers=headers)
def create(self, body=None, headers=None):
return self.url_post('', body, headers=headers)
def url_list(self, url, headers=None):
return self.url_get(url, headers=headers)
def url_get(self, url, headers=None):
return self._rest_call(url, method='GET', headers=headers)
def url_delete(self, url, headers=None):
return self._rest_call(url, method='DELETE', headers=headers)
def url_put(self, url, body, headers=None):
return self._rest_call(url, method='PUT', body=body, headers=headers)
def url_post(self, url, body, headers=None):
return self._rest_call(url, method='POST', body=body, headers=headers)
def _validate_result(self, result, expected, operation):
if result.status_code not in expected:
LOG.warning(_LW("The HTTP request returned error code "
"%(result)d, whereas %(expected)s response "
"codes were expected. Response body %(body)s"),
{'result': result.status_code,
'expected': '/'.join([str(code)
for code in expected]),
'body': result.json()})
else:
LOG.warning(_LW("The HTTP request returned error code "
"%(result)d, whereas %(expected)s response "
"codes were expected."),
{'result': result.status_code,
'expected': '/'.join([str(code)
for code in expected])})
manager_ip = _get_manager_ip()
'body': result.json() if result.content else ''})
manager_error = ERRORS.get(result.status_code, nsx_exc.ManagerError)
raise manager_error(manager=manager_ip, operation=operation)
manager_error = ERRORS.get(
result.status_code, nsx_exc.ManagerError)
raise manager_error(manager=self._host_ip, operation=operation)
@classmethod
def merge_headers(cls, *headers):
merged = {}
for header in headers:
if header:
merged.update(header)
return merged
def _build_url(self, uri):
uri = ("/%s/%s" % (self._url_prefix, uri)).replace('//', '/')
return ("https://%s%s" % (self._host_ip, uri)).strip('/')
def _rest_call(self, url, method='GET', body=None, headers=None):
request_headers = headers.copy() if headers else {}
request_headers.update(self._default_headers)
request_url = self._build_url(url)
do_request = getattr(self._session, method.lower())
LOG.debug("REST call: %s %s\nHeaders: %s\nBody: %s",
method, request_url, request_headers, body)
result = do_request(
request_url,
verify=self.validate_certificate,
data=body,
headers=request_headers,
cert=self._cert_file)
self._validate_result(
result, RESTClient._VERB_RESP_CODES[method.lower()],
_("%(verb)s %(url)s") % {'verb': method, 'url': request_url})
return result
def get_resource(resource, **params):
manager, user, password, verify = _get_manager_endpoint()
url = manager + "/api/v1/%s" % resource
headers = {'Accept': 'application/json'}
result = requests.get(url, auth=auth.HTTPBasicAuth(user, password),
verify=verify, headers=headers,
cert=cfg.CONF.nsx_v3.ca_file,
params=params)
_validate_result(result, [requests.codes.ok],
_("reading resource: %s") % resource)
return result.json()
class JSONRESTClient(RESTClient):
_DEFAULT_HEADERS = {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
def __init__(self, host_ip=None, user_name=None,
password=None, insecure=None,
url_prefix=None, default_headers=None,
cert_file=None):
super(JSONRESTClient, self).__init__(
host_ip=host_ip, user_name=user_name,
password=password, insecure=insecure,
url_prefix=url_prefix,
default_headers=RESTClient.merge_headers(
JSONRESTClient._DEFAULT_HEADERS, default_headers),
cert_file=cert_file)
def _rest_call(self, *args, **kwargs):
if kwargs.get('body') is not None:
kwargs['body'] = jsonutils.dumps(kwargs['body'])
result = super(JSONRESTClient, self)._rest_call(*args, **kwargs)
return result.json() if result.content else result
class NSX3Client(JSONRESTClient):
_NSX_V1_API_PREFIX = '/api/v1/'
def __init__(self, host_ip=None, user_name=None,
password=None, insecure=None,
url_prefix=None, default_headers=None,
cert_file=None):
url_prefix = url_prefix or NSX3Client._NSX_V1_API_PREFIX
if (url_prefix and not url_prefix.startswith(
NSX3Client._NSX_V1_API_PREFIX)):
url_prefix = "%s/%s" % (NSX3Client._NSX_V1_API_PREFIX,
url_prefix or '')
host_ip = host_ip or cfg.CONF.nsx_v3.nsx_manager
user_name = user_name or cfg.CONF.nsx_v3.nsx_user
password = password or cfg.CONF.nsx_v3.nsx_password
cert_file = cert_file or cfg.CONF.nsx_v3.ca_file
insecure = (insecure if insecure is not None
else cfg.CONF.nsx_v3.insecure)
super(NSX3Client, self).__init__(
host_ip=host_ip, user_name=user_name,
password=password, insecure=insecure,
url_prefix=url_prefix,
default_headers=default_headers,
cert_file=cert_file)
# NOTE(boden): tmp until all refs use client class
def get_resource(resource):
return NSX3Client().get(resource)
# NOTE(boden): tmp until all refs use client class
def create_resource(resource, data):
manager, user, password, verify = _get_manager_endpoint()
url = manager + "/api/v1/%s" % resource
headers = {'Content-Type': 'application/json',
'Accept': 'application/json'}
result = requests.post(url, auth=auth.HTTPBasicAuth(user, password),
verify=verify, headers=headers,
data=jsonutils.dumps(data),
cert=cfg.CONF.nsx_v3.ca_file)
_validate_result(result, [requests.codes.created, requests.codes.ok],
_("creating resource at: %s") % resource)
return result.json()
return NSX3Client(url_prefix=resource).create(body=data)
# NOTE(boden): tmp until all refs use client class
def update_resource(resource, data):
manager, user, password, verify = _get_manager_endpoint()
url = manager + "/api/v1/%s" % resource
headers = {'Content-Type': 'application/json',
'Accept': 'application/json'}
result = requests.put(url, auth=auth.HTTPBasicAuth(user, password),
verify=verify, headers=headers,
data=jsonutils.dumps(data),
cert=cfg.CONF.nsx_v3.ca_file)
_validate_result(result, [requests.codes.ok],
_("updating resource: %s") % resource)
return result.json()
return NSX3Client().update(resource, body=data)
# NOTE(boden): tmp until all refs use client class
def delete_resource(resource):
manager, user, password, verify = _get_manager_endpoint()
url = manager + "/api/v1/%s" % resource
result = requests.delete(url, auth=auth.HTTPBasicAuth(user, password),
verify=verify, cert=cfg.CONF.nsx_v3.ca_file)
_validate_result(result, [requests.codes.ok],
_("deleting resource: %s") % resource)
return NSX3Client().delete(resource)

View File

@ -0,0 +1,107 @@
# 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
import six
@six.add_metaclass(abc.ABCMeta)
class AbstractRESTResource(object):
def __init__(self, rest_client, *args, **kwargs):
self._client = rest_client.new_client_for(self.uri_segment)
@abc.abstractproperty
def uri_segment(self):
pass
def list(self):
return self._client.list()
def get(self, uuid):
return self._client.get(uuid)
def delete(self, uuid):
return self._client.delete(uuid)
@abc.abstractmethod
def create(self, *args, **kwargs):
pass
@abc.abstractmethod
def update(self, uuid, *args, **kwargs):
pass
def find_by_display_name(self, display_name):
found = []
for resource in self.list()['results']:
if resource['display_name'] == display_name:
found.append(resource)
return found
class SwitchingProfileTypes(object):
IP_DISCOVERY = 'IpDiscoverySwitchingProfile'
PORT_MIRRORING = 'PortMirroringSwitchingProfile'
QOS = 'QosSwitchingProfile'
SPOOF_GUARD = 'SpoofGuardSwitchingProfile'
class WhiteListAddressTypes(object):
PORT = 'LPORT_BINDINGS'
SWITCH = 'LSWITCH_BINDINGS'
class SwitchingProfile(AbstractRESTResource):
@property
def uri_segment(self):
return 'switching-profiles'
def create(self, profile_type, display_name=None,
description=None, **api_args):
body = {
'resource_type': profile_type,
'display_name': display_name or '',
'description': description or ''
}
body.update(api_args)
return self._client.create(body=body)
def update(self, uuid, profile_type, **api_args):
body = {
'resource_type': profile_type
}
body.update(api_args)
return self._client.update(uuid, body=body)
def create_spoofguard_profile(self, display_name,
description,
whitelist_ports=False,
whitelist_switches=False,
tags=None):
whitelist_providers = []
if whitelist_ports:
whitelist_providers.append(WhiteListAddressTypes.PORT)
if whitelist_switches:
whitelist_providers.append(WhiteListAddressTypes.SWITCH)
return self.create(SwitchingProfileTypes.SPOOF_GUARD,
display_name=display_name,
description=description,
white_list_providers=whitelist_providers,
tags=tags or [])

View File

@ -12,8 +12,7 @@
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from oslo_serialization import jsonutils
from oslo_utils import uuidutils
from vmware_nsx.common import exceptions as nsx_exc
@ -419,3 +418,12 @@ class NsxV3Mock(object):
def update_logical_router_advertisement(self, logical_router_id, **kwargs):
# TODO(berlin): implement this latter.
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)

View File

@ -0,0 +1,369 @@
# 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
import vmware_nsx.common.exceptions as exep
import vmware_nsx.tests.unit.vmware.nsx_v3_mocks as mocks
from oslo_config import cfg
from oslo_log import log
from oslo_serialization import jsonutils
from vmware_nsx.nsxlib.v3 import client
from vmware_nsx.tests.unit.vmware.nsxlib.v3 import nsxlib_testcase
LOG = log.getLogger(__name__)
CLIENT_PKG = 'vmware_nsx.nsxlib.v3.client'
def assert_session_call(mock_call, url, verify, data, headers, cert):
mock_call.assert_called_once_with(
url, verify=verify, data=data, headers=headers, cert=cert)
class BaseClientTestCase(nsxlib_testcase.NsxLibTestCase):
nsx_manager = '1.2.3.4'
nsx_user = 'testuser'
nsx_password = 'pass123'
ca_file = '/path/to/ca.pem'
insecure = True
def setUp(self):
cfg.CONF.set_override(
'nsx_manager', BaseClientTestCase.nsx_manager, 'nsx_v3')
cfg.CONF.set_override(
'nsx_user', BaseClientTestCase.nsx_user, 'nsx_v3')
cfg.CONF.set_override(
'nsx_password', BaseClientTestCase.nsx_password, 'nsx_v3')
cfg.CONF.set_override(
'ca_file', BaseClientTestCase.ca_file, 'nsx_v3')
cfg.CONF.set_override(
'insecure', BaseClientTestCase.insecure, 'nsx_v3')
super(BaseClientTestCase, self).setUp()
def new_client(
self, clazz, host_ip=nsx_manager,
user_name=nsx_user, password=nsx_password,
insecure=insecure, url_prefix=None,
default_headers=None, cert_file=ca_file):
return clazz(host_ip=host_ip, user_name=user_name,
password=password, insecure=insecure,
url_prefix=url_prefix, default_headers=default_headers,
cert_file=cert_file)
class NsxV3RESTClientTestCase(BaseClientTestCase):
def test_client_conf_init(self):
api = self.new_client(client.RESTClient)
self.assertEqual((
BaseClientTestCase.nsx_user, BaseClientTestCase.nsx_password),
api._session.auth)
self.assertEqual(BaseClientTestCase.nsx_manager, api._host_ip)
self.assertEqual(BaseClientTestCase.ca_file, api._cert_file)
def test_client_params_init(self):
api = self.new_client(
client.RESTClient, host_ip='11.12.13.14', password='mypass')
self.assertEqual((
BaseClientTestCase.nsx_user, 'mypass'),
api._session.auth)
self.assertEqual('11.12.13.14', api._host_ip)
self.assertEqual(BaseClientTestCase.ca_file, api._cert_file)
@mock.patch("%s.%s" % (CLIENT_PKG, 'requests.Session.get'))
@mock.patch(CLIENT_PKG + '.RESTClient._validate_result')
def test_client_url_prefix(self, mock_validate, mock_get):
mock_get.return_value = {}
api = self.new_client(client.RESTClient, url_prefix='/cloud/api')
api.list()
assert_session_call(
mock_get,
'https://1.2.3.4/cloud/api',
False, None, {}, BaseClientTestCase.ca_file)
mock_get.reset_mock()
api.url_list('v1/ports')
assert_session_call(
mock_get,
'https://1.2.3.4/cloud/api/v1/ports', False, None, {},
BaseClientTestCase.ca_file)
@mock.patch("%s.%s" % (CLIENT_PKG, 'requests.Session.get'))
@mock.patch(CLIENT_PKG + '.RESTClient._validate_result')
def test_client_headers(self, mock_validate, mock_get):
default_headers = {'Content-Type': 'application/golang'}
mock_get.return_value = {}
api = self.new_client(
client.RESTClient, default_headers=default_headers,
url_prefix='/v1/api')
api.list()
assert_session_call(
mock_get,
'https://1.2.3.4/v1/api',
False, None, default_headers, BaseClientTestCase.ca_file)
mock_get.reset_mock()
method_headers = {'X-API-Key': 'strong-crypt'}
api.url_list('ports/33', headers=method_headers)
method_headers.update(default_headers)
assert_session_call(
mock_get,
'https://1.2.3.4/v1/api/ports/33', False, None,
method_headers,
BaseClientTestCase.ca_file)
@mock.patch("%s.%s" % (CLIENT_PKG, 'requests.Session.get'))
@mock.patch(CLIENT_PKG + '.RESTClient._validate_result')
def test_client_for(self, mock_validate, mock_get):
api = self.new_client(client.RESTClient, url_prefix='api/v1/')
sub_api = api.new_client_for('switch/ports')
sub_api.get('11a2b')
assert_session_call(
mock_get,
'https://1.2.3.4/api/v1/switch/ports/11a2b',
False, None, {}, BaseClientTestCase.ca_file)
@mock.patch("%s.%s" % (CLIENT_PKG, 'requests.Session.get'))
@mock.patch(CLIENT_PKG + '.RESTClient._validate_result')
def test_client_list(self, mock_validate, mock_get):
api = self.new_client(client.RESTClient, url_prefix='api/v1/ports')
api.list()
assert_session_call(
mock_get,
'https://1.2.3.4/api/v1/ports',
False, None, {}, BaseClientTestCase.ca_file)
@mock.patch("%s.%s" % (CLIENT_PKG, 'requests.Session.get'))
@mock.patch(CLIENT_PKG + '.RESTClient._validate_result')
def test_client_get(self, mock_validate, mock_get):
api = self.new_client(client.RESTClient, url_prefix='api/v1/ports')
api.get('unique-id')
assert_session_call(
mock_get,
'https://1.2.3.4/api/v1/ports/unique-id',
False, None, {}, BaseClientTestCase.ca_file)
@mock.patch("%s.%s" % (CLIENT_PKG, 'requests.Session.delete'))
@mock.patch(CLIENT_PKG + '.RESTClient._validate_result')
def test_client_delete(self, mock_validate, mock_delete):
api = self.new_client(client.RESTClient, url_prefix='api/v1/ports')
api.delete('unique-id')
assert_session_call(
mock_delete,
'https://1.2.3.4/api/v1/ports/unique-id',
False, None, {}, BaseClientTestCase.ca_file)
@mock.patch("%s.%s" % (CLIENT_PKG, 'requests.Session.put'))
@mock.patch(CLIENT_PKG + '.RESTClient._validate_result')
def test_client_update(self, mock_validate, mock_put):
api = self.new_client(client.RESTClient, url_prefix='api/v1/ports')
api.update('unique-id', {'name': 'a-new-name'})
assert_session_call(
mock_put,
'https://1.2.3.4/api/v1/ports/unique-id',
False, {'name': 'a-new-name'},
{}, BaseClientTestCase.ca_file)
@mock.patch("%s.%s" % (CLIENT_PKG, 'requests.Session.post'))
@mock.patch(CLIENT_PKG + '.RESTClient._validate_result')
def test_client_create(self, mock_validate, mock_post):
api = self.new_client(client.RESTClient, url_prefix='api/v1/ports')
api.create({'resource-name': 'port1'})
assert_session_call(
mock_post,
'https://1.2.3.4/api/v1/ports',
False, {'resource-name': 'port1'},
{}, BaseClientTestCase.ca_file)
@mock.patch("%s.%s" % (CLIENT_PKG, 'requests.Session.get'))
@mock.patch(CLIENT_PKG + '.RESTClient._validate_result')
def test_client_url_list(self, mock_validate, mock_get):
api = self.new_client(client.RESTClient, url_prefix='api/v1/ports')
api.url_list('/connections', {'Content-Type': 'application/json'})
assert_session_call(
mock_get,
'https://1.2.3.4/api/v1/ports/connections',
False, None,
{'Content-Type': 'application/json'},
BaseClientTestCase.ca_file)
@mock.patch("%s.%s" % (CLIENT_PKG, 'requests.Session.get'))
@mock.patch(CLIENT_PKG + '.RESTClient._validate_result')
def test_client_url_get(self, mock_validate, mock_get):
api = self.new_client(client.RESTClient, url_prefix='api/v1/ports')
api.url_get('connections/1')
assert_session_call(
mock_get,
'https://1.2.3.4/api/v1/ports/connections/1',
False, None, {}, BaseClientTestCase.ca_file)
@mock.patch("%s.%s" % (CLIENT_PKG, 'requests.Session.delete'))
@mock.patch(CLIENT_PKG + '.RESTClient._validate_result')
def test_client_url_delete(self, mock_validate, mock_delete):
api = self.new_client(client.RESTClient, url_prefix='api/v1/ports')
api.url_delete('1')
assert_session_call(
mock_delete,
'https://1.2.3.4/api/v1/ports/1',
False, None, {}, BaseClientTestCase.ca_file)
@mock.patch("%s.%s" % (CLIENT_PKG, 'requests.Session.put'))
@mock.patch(CLIENT_PKG + '.RESTClient._validate_result')
def test_client_url_put(self, mock_validate, mock_put):
api = self.new_client(client.RESTClient, url_prefix='api/v1/ports')
api.url_put('connections/1', {'name': 'conn1'})
assert_session_call(
mock_put,
'https://1.2.3.4/api/v1/ports/connections/1',
False, {'name': 'conn1'},
{}, BaseClientTestCase.ca_file)
@mock.patch("%s.%s" % (CLIENT_PKG, 'requests.Session.post'))
@mock.patch(CLIENT_PKG + '.RESTClient._validate_result')
def test_client_url_post(self, mock_validate, mock_post):
api = self.new_client(client.RESTClient, url_prefix='api/v1/ports')
api.url_post('1/connections', {'name': 'conn1'})
assert_session_call(
mock_post,
'https://1.2.3.4/api/v1/ports/1/connections',
False, {'name': 'conn1'},
{}, BaseClientTestCase.ca_file)
@mock.patch("%s.%s" % (CLIENT_PKG, 'requests.Session.get'))
@mock.patch("%s.%s" % (CLIENT_PKG, 'requests.Session.put'))
@mock.patch("%s.%s" % (CLIENT_PKG, 'requests.Session.post'))
@mock.patch("%s.%s" % (CLIENT_PKG, 'requests.Session.delete'))
def test_client_validate_result(self, *args):
def _verb_response_code(http_verb, status_code):
response = mocks.MockRequestsResponse(status_code, None)
api = self.new_client(client.RESTClient)
for mocked in args:
mocked.return_value = response
client_call = getattr(api, "url_%s" % http_verb)
client_call('', None)
for verb in ['get', 'post', 'put', 'delete']:
for code in client.RESTClient._VERB_RESP_CODES.get(verb):
_verb_response_code(verb, code)
self.assertRaises(
exep.ManagerError, _verb_response_code, verb, 500)
class NsxV3JSONClientTestCase(BaseClientTestCase):
@mock.patch("%s.%s" % (CLIENT_PKG, 'requests.Session.post'))
@mock.patch(CLIENT_PKG + '.RESTClient._validate_result')
def test_json_request(self, mock_validate, mock_post):
mock_post.return_value = mocks.MockRequestsResponse(
200, jsonutils.dumps({'result': {'ok': 200}}))
api = self.new_client(client.JSONRESTClient, url_prefix='api/v2/nat')
resp = api.create(body={'name': 'mgmt-egress'})
assert_session_call(
mock_post,
'https://1.2.3.4/api/v2/nat',
False, jsonutils.dumps({'name': 'mgmt-egress'}),
client.JSONRESTClient._DEFAULT_HEADERS,
BaseClientTestCase.ca_file)
self.assertEqual(resp, {'result': {'ok': 200}})
class NsxV3APIClientTestCase(BaseClientTestCase):
@mock.patch("%s.%s" % (CLIENT_PKG, 'requests.Session.get'))
@mock.patch(CLIENT_PKG + '.RESTClient._validate_result')
def test_api_call(self, mock_validate, mock_get):
api = self.new_client(client.NSX3Client)
api.get('ports')
assert_session_call(
mock_get,
'https://1.2.3.4/api/v1/ports',
False, None,
client.JSONRESTClient._DEFAULT_HEADERS,
NsxV3APIClientTestCase.ca_file)
# NOTE(boden): remove this when tmp brigding removed
class NsxV3APIClientBridgeTestCase(BaseClientTestCase):
@mock.patch("%s.%s" % (CLIENT_PKG, 'requests.Session.get'))
@mock.patch(CLIENT_PKG + '.RESTClient._validate_result')
def test_get_resource(self, mock_validate, mock_get):
client.get_resource('ports')
assert_session_call(
mock_get,
'https://1.2.3.4/api/v1/ports',
False, None,
client.JSONRESTClient._DEFAULT_HEADERS,
NsxV3APIClientTestCase.ca_file)
@mock.patch("%s.%s" % (CLIENT_PKG, 'requests.Session.post'))
@mock.patch(CLIENT_PKG + '.RESTClient._validate_result')
def test_create_resource(self, mock_validate, mock_post):
client.create_resource('ports', {'resource-name': 'port1'})
assert_session_call(
mock_post,
'https://1.2.3.4/api/v1/ports',
False, jsonutils.dumps({'resource-name': 'port1'}),
client.JSONRESTClient._DEFAULT_HEADERS,
BaseClientTestCase.ca_file)
@mock.patch("%s.%s" % (CLIENT_PKG, 'requests.Session.put'))
@mock.patch(CLIENT_PKG + '.RESTClient._validate_result')
def test_update_resource(self, mock_validate, mock_put):
client.update_resource('ports/1', {'name': 'a-new-name'})
assert_session_call(
mock_put,
'https://1.2.3.4/api/v1/ports/1',
False, jsonutils.dumps({'name': 'a-new-name'}),
client.JSONRESTClient._DEFAULT_HEADERS,
BaseClientTestCase.ca_file)
@mock.patch("%s.%s" % (CLIENT_PKG, 'requests.Session.delete'))
@mock.patch(CLIENT_PKG + '.RESTClient._validate_result')
def test_delete_resource(self, mock_validate, mock_delete):
client.delete_resource('ports/11')
assert_session_call(
mock_delete,
'https://1.2.3.4/api/v1/ports/11',
False, None, client.JSONRESTClient._DEFAULT_HEADERS,
BaseClientTestCase.ca_file)

View File

@ -0,0 +1,144 @@
# 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
import vmware_nsx.tests.unit.vmware.nsx_v3_mocks as mocks
from oslo_serialization import jsonutils
from vmware_nsx.nsxlib.v3 import client
from vmware_nsx.nsxlib.v3 import resources
from vmware_nsx.tests.unit.vmware.nsxlib.v3 import test_client
CLIENT_PKG = test_client.CLIENT_PKG
profile_types = resources.SwitchingProfileTypes
class TestSwitchingProfileTestCase(test_client.BaseClientTestCase):
@mock.patch("%s.%s" % (CLIENT_PKG, 'requests.Session.post'))
@mock.patch(CLIENT_PKG + '.RESTClient._validate_result')
def test_switching_profile_create(self, mock_validate, mock_post):
api = resources.SwitchingProfile(client.NSX3Client())
api.create(profile_types.PORT_MIRRORING,
'pm-profile', 'port mirror prof')
test_client.assert_session_call(
mock_post,
'https://1.2.3.4/api/v1/switching-profiles',
False, jsonutils.dumps({
'resource_type': profile_types.PORT_MIRRORING,
'display_name': 'pm-profile',
'description': 'port mirror prof'
}),
client.JSONRESTClient._DEFAULT_HEADERS,
test_client.BaseClientTestCase.ca_file)
@mock.patch("%s.%s" % (CLIENT_PKG, 'requests.Session.put'))
@mock.patch(CLIENT_PKG + '.RESTClient._validate_result')
def test_switching_profile_update(self, mock_validate, mock_put):
tags = [
{
'scope': 'os-tid',
'tag': 'tenant-1'
},
{
'scope': 'os-api-version',
'tag': '2.1.1.0'
}
]
api = resources.SwitchingProfile(client.NSX3Client())
api.update('a12bc1', profile_types.PORT_MIRRORING, tags=tags)
test_client.assert_session_call(
mock_put,
'https://1.2.3.4/api/v1/switching-profiles/a12bc1',
False, jsonutils.dumps({
'resource_type': profile_types.PORT_MIRRORING,
'tags': tags
}),
client.JSONRESTClient._DEFAULT_HEADERS,
test_client.BaseClientTestCase.ca_file)
@mock.patch("%s.%s" % (CLIENT_PKG, 'requests.Session.post'))
@mock.patch(CLIENT_PKG + '.RESTClient._validate_result')
def test_spoofgaurd_profile_create(self, mock_validate, mock_post):
tags = [
{
'scope': 'os-tid',
'tag': 'tenant-1'
},
{
'scope': 'os-api-version',
'tag': '2.1.1.0'
}
]
api = resources.SwitchingProfile(client.NSX3Client())
api.create_spoofguard_profile(
'neutron-spoof', 'spoofguard-for-neutron',
whitelist_ports=True, tags=tags)
test_client.assert_session_call(
mock_post,
'https://1.2.3.4/api/v1/switching-profiles',
False,
jsonutils.dumps({
'resource_type': profile_types.SPOOF_GUARD,
'display_name': 'neutron-spoof',
'description': 'spoofguard-for-neutron',
'white_list_providers': ['LPORT_BINDINGS'],
'tags': tags
}),
client.JSONRESTClient._DEFAULT_HEADERS,
test_client.BaseClientTestCase.ca_file)
@mock.patch("%s.%s" % (CLIENT_PKG, 'requests.Session.get'))
@mock.patch(CLIENT_PKG + '.RESTClient._validate_result')
def test_find_by_display_name(self, mock_validate, mock_get):
resp_resources = {
'results': [
{'display_name': 'resource-1'},
{'display_name': 'resource-2'},
{'display_name': 'resource-3'}
]
}
mock_get.return_value = mocks.MockRequestsResponse(
200, jsonutils.dumps(resp_resources))
api = resources.SwitchingProfile(client.NSX3Client())
self.assertEqual([{'display_name': 'resource-1'}],
api.find_by_display_name('resource-1'))
self.assertEqual([{'display_name': 'resource-2'}],
api.find_by_display_name('resource-2'))
self.assertEqual([{'display_name': 'resource-3'}],
api.find_by_display_name('resource-3'))
mock_get.reset_mock()
resp_resources = {
'results': [
{'display_name': 'resource-1'},
{'display_name': 'resource-1'},
{'display_name': 'resource-1'}
]
}
mock_get.return_value = mocks.MockRequestsResponse(
200, jsonutils.dumps(resp_resources))
api = resources.SwitchingProfile(client.NSX3Client())
self.assertEqual(resp_resources['results'],
api.find_by_display_name('resource-1'))