NSX v3 multi-manager
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
This commit is contained in:
parent
4924166aad
commit
16b105f7f5
@ -98,17 +98,18 @@ function neutron_plugin_configure_service {
|
||||
Q_L3_ENABLED=True
|
||||
Q_L3_ROUTER_PER_TENANT=True
|
||||
fi
|
||||
# NSX_CONTROLLERS must be a comma separated string
|
||||
if [[ "$NSX_CONTROLLERS" != "" ]]; then
|
||||
iniset /$Q_PLUGIN_CONF_FILE DEFAULT nsx_controllers $NSX_CONTROLLERS
|
||||
# NSX_MANAGER must be a comma separated string
|
||||
if [[ "$NSX_MANAGERS" != "" ]]; then
|
||||
_nsxv3_ini_set nsx_managers $NSX_MANAGERS
|
||||
elif [[ "$NSX_MANAGER" != "" ]]; then
|
||||
_nsxv3_ini_set nsx_managers $NSX_MANAGER
|
||||
else
|
||||
die $LINENO "The VMware NSX plugin needs at least an NSX controller."
|
||||
die $LINENO "The VMware NSX plugin needs at least one NSX manager."
|
||||
fi
|
||||
if [[ "$NSX_L2GW_DRIVER" != "" ]]; then
|
||||
iniset /$Q_PLUGIN_CONF_FILE DEFAULT nsx_l2gw_driver $NSX_L2GW_DRIVER
|
||||
fi
|
||||
_nsxv3_ini_set default_tier0_router_uuid $DEFAULT_TIER0_ROUTER_UUID
|
||||
_nsxv3_ini_set nsx_manager $NSX_MANAGER "The VMWare NSX plugin needs a NSX manager."
|
||||
_nsxv3_ini_set nsx_user $NSX_USER
|
||||
_nsxv3_ini_set nsx_password $NSX_PASSWORD
|
||||
_nsxv3_ini_set retries $NSX_RETRIES
|
||||
|
25
etc/nsx.ini
25
etc/nsx.ini
@ -315,8 +315,12 @@
|
||||
# metadata_shared_secret =
|
||||
|
||||
[nsx_v3]
|
||||
# IP address of NSX manager
|
||||
# nsx_manager = 1.2.3.4
|
||||
# IP address of one or more NSX managers separated by commas.
|
||||
# The IP address should be of the form:
|
||||
# [<scheme>://]<ip_adress>[:<port>]
|
||||
# If scheme is not provided https is used. If port is not provided
|
||||
# port 80 is used for http and port 443 for https.
|
||||
# nsx_managers = 1.2.3.4
|
||||
|
||||
# User name of NSX Manager
|
||||
# nsx_user = admin
|
||||
@ -340,12 +344,25 @@
|
||||
|
||||
# Specify a CA bundle file to use in verifying the NSX Manager
|
||||
# server certificate. This option is ignored if "insecure" is set to True.
|
||||
# If "insecure" is set to False and ca_file is unset, the system root CAs
|
||||
# will be used to verify the server certificate.
|
||||
# ca_file =
|
||||
|
||||
# If true, the NSX Manager server certificate is not verified. If false,
|
||||
# then the default CA truststore is used for verification.
|
||||
# If true, the NSX Manager server certificate is not verified. If false
|
||||
# the CA bundle specified via "ca_file" will be used or if unsest the
|
||||
# default system root CAs will be used.
|
||||
# insecure = True
|
||||
|
||||
# The time in seconds before aborting an HTTP request to a NSX manager.
|
||||
# http_timeout = 75
|
||||
|
||||
# Maxiumum number of connection connections to each NSX manager.
|
||||
# concurrent_connections = 10
|
||||
|
||||
# The amount of time in seconds to wait before ensuring connectivity to
|
||||
# the NSX manager if no manager connection has been used.
|
||||
# conn_idle_timeout = 60
|
||||
|
||||
# UUID of the default tier0 router that will be used for connecting to
|
||||
# tier1 logical routers and configuring external network
|
||||
# default_tier0_router_uuid = 412983fd-9016-45e5-93f2-48ba2a931225
|
||||
|
@ -176,8 +176,12 @@ nsx_v3_opts = [
|
||||
default='default',
|
||||
secret=True,
|
||||
help=_('Password for the NSX manager')),
|
||||
cfg.StrOpt('nsx_manager',
|
||||
help=_('IP address of the NSX manager')),
|
||||
cfg.ListOpt('nsx_managers',
|
||||
deprecated_name='nsx_manager',
|
||||
help=_('IP address of one or more NSX managers separated '
|
||||
'by commas. The IP address can optionally specify a '
|
||||
'scheme (e.g. http or https) and port using the format '
|
||||
'<scheme>://<ip_address>:<port>')),
|
||||
cfg.StrOpt('default_overlay_tz_uuid',
|
||||
deprecated_name='default_tz_uuid',
|
||||
help=_("This is the UUID of the default NSX overlay transport "
|
||||
@ -200,13 +204,30 @@ nsx_v3_opts = [
|
||||
help=_('Maximum number of times to retry API request')),
|
||||
cfg.StrOpt('ca_file',
|
||||
help=_('Specify a CA bundle file to use in verifying the NSX '
|
||||
'Manager server certificate.')),
|
||||
'Manager server certificate. This option is ignored if '
|
||||
'"insecure" is set to True. If "insecure" is set to '
|
||||
'False and ca_file is unset, the system root CAs will '
|
||||
'be used to verify the server certificate.')),
|
||||
cfg.BoolOpt('insecure',
|
||||
default=True,
|
||||
help=_('If true, the NSX Manager server certificate is not '
|
||||
'verified. If false, then the default CA truststore is '
|
||||
'used for verification. This option is ignored if '
|
||||
'"ca_file" is set.')),
|
||||
'verified. If false the CA bundle specified via '
|
||||
'"ca_file" will be used or if unsest the default '
|
||||
'system root CAs will be used.')),
|
||||
cfg.IntOpt('http_timeout',
|
||||
default=75,
|
||||
help=_('Time before aborting a HTTP request to a '
|
||||
'NSX manager.')),
|
||||
cfg.IntOpt('concurrent_connections', default=10,
|
||||
help=_("Maximum concurrent connections to each NSX "
|
||||
"manager.")),
|
||||
cfg.IntOpt('conn_idle_timeout',
|
||||
default=60,
|
||||
help=_('Ensure connectivity to the NSX manager if a connection '
|
||||
'is not used within timeout seconds.')),
|
||||
cfg.IntOpt('redirects',
|
||||
default=2,
|
||||
help=_('Number of times a HTTP redirect should be followed.')),
|
||||
cfg.StrOpt('default_tier0_router_uuid',
|
||||
help=_("Default tier0 router identifier")),
|
||||
]
|
||||
|
@ -56,8 +56,9 @@ def delete_resource_by_values(resource, skip_not_found=True, **kwargs):
|
||||
err_msg = (_("No resource in %(res)s matched for values: "
|
||||
"%(values)s") % {'res': resource,
|
||||
'values': kwargs})
|
||||
raise nsx_exc.ResourceNotFound(manager=client._get_manager_ip(),
|
||||
operation=err_msg)
|
||||
raise nsx_exc.ResourceNotFound(
|
||||
manager=client._get_nsx_managers_from_conf(),
|
||||
operation=err_msg)
|
||||
elif matched_num > 1:
|
||||
LOG.warning(_LW("%(num)s resources in %(res)s matched for values: "
|
||||
"%(values)s"), {'num': matched_num,
|
||||
|
@ -14,11 +14,11 @@
|
||||
# under the License.
|
||||
#
|
||||
import requests
|
||||
import urlparse
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
from vmware_nsx._i18n import _, _LW
|
||||
from vmware_nsx.common import exceptions as nsx_exc
|
||||
|
||||
@ -37,37 +37,19 @@ class RESTClient(object):
|
||||
'delete': [requests.codes.ok]
|
||||
}
|
||||
|
||||
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
|
||||
def __init__(self, connection, url_prefix=None,
|
||||
default_headers=None):
|
||||
self._conn = connection
|
||||
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 new_client_for(self, *uri_segments):
|
||||
uri = "%s/%s" % (self._url_prefix, '/'.join(uri_segments))
|
||||
uri = uri.replace('//', '/')
|
||||
uri = self._build_url('/'.join(uri_segments))
|
||||
|
||||
return self.__class__(
|
||||
host_ip=self._host_ip, user_name=self._user_name,
|
||||
password=self._password, insecure=self._insecure,
|
||||
self._conn,
|
||||
url_prefix=uri,
|
||||
default_headers=self._default_headers,
|
||||
cert_file=self._cert_file)
|
||||
|
||||
@property
|
||||
def validate_certificate(self):
|
||||
return not self._insecure
|
||||
default_headers=self._default_headers)
|
||||
|
||||
def list(self, headers=None):
|
||||
return self.url_list('')
|
||||
@ -115,7 +97,7 @@ class RESTClient(object):
|
||||
if type(result_msg) is dict:
|
||||
result_msg = result_msg.get('error_message', result_msg)
|
||||
raise manager_error(
|
||||
manager=self._host_ip,
|
||||
manager=_get_nsx_managers_from_conf(),
|
||||
operation=operation,
|
||||
details=result_msg)
|
||||
|
||||
@ -128,25 +110,28 @@ class RESTClient(object):
|
||||
return merged
|
||||
|
||||
def _build_url(self, uri):
|
||||
uri = ("/%s/%s" % (self._url_prefix, uri)).replace('//', '/')
|
||||
return ("https://%s%s" % (self._host_ip, uri)).strip('/')
|
||||
prefix = urlparse.urlparse(self._url_prefix)
|
||||
uri = ("/%s/%s" % (prefix.path, uri)).replace('//', '/').strip('/')
|
||||
if prefix.netloc:
|
||||
uri = "%s/%s" % (prefix.netloc, uri)
|
||||
if prefix.scheme:
|
||||
uri = "%s://%s" % (prefix.scheme, uri)
|
||||
return uri
|
||||
|
||||
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())
|
||||
do_request = getattr(self._conn, 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)
|
||||
headers=request_headers)
|
||||
|
||||
self._validate_result(
|
||||
result, RESTClient._VERB_RESP_CODES[method.lower()],
|
||||
@ -161,18 +146,14 @@ class JSONRESTClient(RESTClient):
|
||||
'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):
|
||||
def __init__(self, connection, url_prefix=None,
|
||||
default_headers=None):
|
||||
|
||||
super(JSONRESTClient, self).__init__(
|
||||
host_ip=host_ip, user_name=user_name,
|
||||
password=password, insecure=insecure,
|
||||
connection,
|
||||
url_prefix=url_prefix,
|
||||
default_headers=RESTClient.merge_headers(
|
||||
JSONRESTClient._DEFAULT_HEADERS, default_headers),
|
||||
cert_file=cert_file)
|
||||
JSONRESTClient._DEFAULT_HEADERS, default_headers))
|
||||
|
||||
def _rest_call(self, *args, **kwargs):
|
||||
if kwargs.get('body') is not None:
|
||||
@ -183,60 +164,64 @@ class JSONRESTClient(RESTClient):
|
||||
|
||||
class NSX3Client(JSONRESTClient):
|
||||
|
||||
_NSX_V1_API_PREFIX = '/api/v1/'
|
||||
_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):
|
||||
def __init__(self, connection, url_prefix=None,
|
||||
default_headers=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)
|
||||
if url_prefix and NSX3Client._NSX_V1_API_PREFIX not in url_prefix:
|
||||
if url_prefix.startswith('http'):
|
||||
url_prefix += '/' + NSX3Client._NSX_V1_API_PREFIX
|
||||
else:
|
||||
url_prefix = "%s/%s" % (NSX3Client._NSX_V1_API_PREFIX,
|
||||
url_prefix or '')
|
||||
|
||||
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)
|
||||
connection, url_prefix=url_prefix,
|
||||
default_headers=default_headers)
|
||||
|
||||
|
||||
# NOTE(boden): tmp until all refs use client class
|
||||
def _get_client(client, *args, **kwargs):
|
||||
return client or NSX3Client(*args, **kwargs)
|
||||
# TODO(boden): remove mod level fns and vars below
|
||||
_DEFAULT_API_CLUSTER = None
|
||||
|
||||
|
||||
def _get_default_api_cluster():
|
||||
global _DEFAULT_API_CLUSTER
|
||||
if _DEFAULT_API_CLUSTER is None:
|
||||
# removes circular ref between client / cluster
|
||||
import vmware_nsx.nsxlib.v3.cluster as nsx_cluster
|
||||
_DEFAULT_API_CLUSTER = nsx_cluster.NSXClusteredAPI()
|
||||
return _DEFAULT_API_CLUSTER
|
||||
|
||||
|
||||
def _set_default_api_cluster(cluster):
|
||||
global _DEFAULT_API_CLUSTER
|
||||
old = _DEFAULT_API_CLUSTER
|
||||
_DEFAULT_API_CLUSTER = cluster
|
||||
return old
|
||||
|
||||
|
||||
def _get_client(client):
|
||||
return client or NSX3Client(_get_default_api_cluster())
|
||||
|
||||
|
||||
# NOTE(shihli): tmp until all refs use client class
|
||||
def _get_manager_ip(client=None):
|
||||
# NOTE: In future this may return the IP address from a pool
|
||||
return (client._host_ip if client is not None
|
||||
else cfg.CONF.nsx_v3.nsx_manager)
|
||||
def _get_nsx_managers_from_conf():
|
||||
return cfg.CONF.nsx_v3.nsx_managers
|
||||
|
||||
|
||||
# NOTE(boden): tmp until all refs use client class
|
||||
def get_resource(resource, client=None):
|
||||
return _get_client(client).get(resource)
|
||||
|
||||
|
||||
# NOTE(boden): tmp until all refs use client class
|
||||
def create_resource(resource, data, client=None):
|
||||
return _get_client(client).url_post(resource, body=data)
|
||||
|
||||
|
||||
# NOTE(boden): tmp until all refs use client class
|
||||
def update_resource(resource, data, client=None):
|
||||
return _get_client(client).update(resource, body=data)
|
||||
|
||||
|
||||
# NOTE(boden): tmp until all refs use client class
|
||||
def delete_resource(resource, client=None):
|
||||
return _get_client(client).delete(resource)
|
||||
|
479
vmware_nsx/nsxlib/v3/cluster.py
Normal file
479
vmware_nsx/nsxlib/v3/cluster.py
Normal file
@ -0,0 +1,479 @@
|
||||
# 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 contextlib
|
||||
import copy
|
||||
import datetime
|
||||
import eventlet
|
||||
import logging
|
||||
import random
|
||||
import requests
|
||||
import six
|
||||
import urlparse
|
||||
|
||||
from eventlet import greenpool
|
||||
from eventlet import pools
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
from oslo_service import loopingcall
|
||||
from requests import adapters
|
||||
from vmware_nsx._i18n import _, _LI, _LW
|
||||
from vmware_nsx.common import exceptions as nsx_err
|
||||
from vmware_nsx.common import exceptions as nsx_exc
|
||||
from vmware_nsx.nsxlib.v3 import client as nsx_client
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
# disable warning message for each HTTP retry
|
||||
logging.getLogger(
|
||||
"requests.packages.urllib3.connectionpool").setLevel(logging.ERROR)
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class AbstractHTTPProvider(object):
|
||||
"""Interface for providers of HTTP connections which
|
||||
are responsible for creating and validating connections
|
||||
for their underlying HTTP support.
|
||||
"""
|
||||
|
||||
@property
|
||||
def default_scheme(self):
|
||||
return 'https'
|
||||
|
||||
@abc.abstractproperty
|
||||
def provider_id(self):
|
||||
"""A unique string name for this provider."""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def validate_connection(self, cluster_api, endpoint, conn):
|
||||
"""Validate the said connection for the given endpoint and cluster.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def new_connection(self, cluster_api, provider):
|
||||
"""Create a new http connection for the said cluster and
|
||||
cluster provider. The actual connection should duck type
|
||||
requests.Session http methods (get(), put(), etc.).
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class TimeoutSession(requests.Session):
|
||||
"""Extends requests.Session to support timeout
|
||||
at the session level.
|
||||
"""
|
||||
|
||||
def __init__(self, timeout=cfg.CONF.nsx_v3.http_timeout):
|
||||
self.timeout = timeout
|
||||
super(TimeoutSession, self).__init__()
|
||||
|
||||
# wrapper timeouts at the session level
|
||||
# see: https://goo.gl/xNk7aM
|
||||
def request(self, *args, **kwargs):
|
||||
if 'timeout' not in kwargs:
|
||||
kwargs['timeout'] = self.timeout
|
||||
return super(TimeoutSession, self).request(*args, **kwargs)
|
||||
|
||||
|
||||
class NSXRequestsHTTPProvider(AbstractHTTPProvider):
|
||||
"""Concrete implementation of AbstractHTTPProvider
|
||||
using requests.Session() as the underlying connection.
|
||||
"""
|
||||
|
||||
@property
|
||||
def provider_id(self):
|
||||
return "%s-%s" % (requests.__title__, requests.__version__)
|
||||
|
||||
def validate_connection(self, cluster_api, endpoint, conn):
|
||||
client = nsx_client.NSX3Client(conn, url_prefix=endpoint.provider.url)
|
||||
zones = client.get('transport-zones')
|
||||
if not zones or zones['result_count'] <= 0:
|
||||
msg = _("No transport zones found "
|
||||
"for '%s'") % endpoint.provider.url
|
||||
LOG.warning(msg)
|
||||
raise nsx_exc.ResourceNotFound(
|
||||
manager=endpoint.provider.url, operation=msg)
|
||||
|
||||
def new_connection(self, cluster_api, provider):
|
||||
session = TimeoutSession(cluster_api.http_timeout)
|
||||
session.auth = (cluster_api.username, cluster_api.password)
|
||||
# NSX v3 doesn't use redirects
|
||||
session.max_redirects = 0
|
||||
|
||||
session.verify = not cluster_api.insecure
|
||||
if session.verify and cluster_api.ca_file:
|
||||
# verify using the said ca bundle path
|
||||
session.verify = cluster_api.ca_file
|
||||
|
||||
# we are pooling with eventlet in the cluster class
|
||||
adapter = adapters.HTTPAdapter(
|
||||
pool_connections=1, pool_maxsize=1,
|
||||
max_retries=cluster_api.retries,
|
||||
pool_block=False)
|
||||
session.mount('http://', adapter)
|
||||
session.mount('https://', adapter)
|
||||
|
||||
return session
|
||||
|
||||
|
||||
class ClusterHealth(object):
|
||||
"""Indicator of overall cluster health with respect
|
||||
to the connectivity of the clusters managed endpoints.
|
||||
"""
|
||||
# all endpoints are UP
|
||||
GREEN = 'GREEN'
|
||||
# at least 1 endpoint is UP, but 1 or more are DOWN
|
||||
ORANGE = 'ORANGE'
|
||||
# all endpoints are DOWN
|
||||
RED = 'RED'
|
||||
|
||||
|
||||
class EndpointState(object):
|
||||
"""Tracks the connectivity state for a said endpoint.
|
||||
"""
|
||||
# no UP or DOWN state recorded yet
|
||||
INITIALIZED = 'INITIALIZED'
|
||||
# endpoint has been validate and is good
|
||||
UP = 'UP'
|
||||
# endpoint can't be reached or validated
|
||||
DOWN = 'DOWN'
|
||||
|
||||
|
||||
class Provider(object):
|
||||
"""Data holder for a provider which has a unique id
|
||||
and a connection URL.
|
||||
"""
|
||||
|
||||
def __init__(self, provider_id, provider_url):
|
||||
self.id = provider_id
|
||||
self.url = provider_url
|
||||
|
||||
def __str__(self):
|
||||
return str(self.url)
|
||||
|
||||
|
||||
class Endpoint(object):
|
||||
"""A single NSX manager endpoint (host) which includes
|
||||
related information such as the endpoint's provider,
|
||||
state, etc.. A pool is used to hold connections to the
|
||||
endpoint which are doled out when proxying HTTP methods
|
||||
to the underlying connections.
|
||||
"""
|
||||
|
||||
def __init__(self, provider, pool):
|
||||
self.provider = provider
|
||||
self.pool = pool
|
||||
self._state = EndpointState.INITIALIZED
|
||||
self._last_updated = datetime.datetime.now()
|
||||
|
||||
@property
|
||||
def last_updated(self):
|
||||
return self._last_updated
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
return self._state
|
||||
|
||||
def set_state(self, state):
|
||||
if self.state != state:
|
||||
LOG.info(_LI("Endpoint '%(ep)s' changing from state"
|
||||
" '%(old)s' to '%(new)s'"),
|
||||
{'ep': self.provider,
|
||||
'old': self.state,
|
||||
'new': state})
|
||||
old_state = self._state
|
||||
self._state = state
|
||||
|
||||
self._last_updated = datetime.datetime.now()
|
||||
|
||||
return old_state
|
||||
|
||||
def __str__(self):
|
||||
return "[%s] %s" % (self.state, self.provider)
|
||||
|
||||
|
||||
class EndpointConnection(object):
|
||||
"""Simple data holder which contains an endpoint and
|
||||
a connection for that endpoint.
|
||||
"""
|
||||
|
||||
def __init__(self, endpoint, connection):
|
||||
self.endpoint = endpoint
|
||||
self.connection = connection
|
||||
|
||||
|
||||
class ClusteredAPI(object):
|
||||
"""Duck types the major HTTP based methods of a
|
||||
requests.Session such as get(), put(), post(), etc.
|
||||
and transparently proxies those calls to one of
|
||||
its managed NSX manager endpoints.
|
||||
"""
|
||||
_HTTP_VERBS = ['get', 'delete', 'head', 'put', 'post', 'patch', 'create']
|
||||
|
||||
def __init__(self, providers,
|
||||
http_provider,
|
||||
min_conns_per_pool=1,
|
||||
max_conns_per_pool=500,
|
||||
keepalive_interval=33):
|
||||
|
||||
self._http_provider = http_provider
|
||||
self._keepalive_interval = keepalive_interval
|
||||
|
||||
def _create_conn(p):
|
||||
def _conn():
|
||||
# called when a pool needs to create a new connection
|
||||
return self._http_provider.new_connection(self, p)
|
||||
return _conn
|
||||
|
||||
self._endpoints = {}
|
||||
for provider in providers:
|
||||
pool = pools.Pool(
|
||||
min_size=min_conns_per_pool,
|
||||
max_size=max_conns_per_pool,
|
||||
order_as_stack=True,
|
||||
create=_create_conn(provider))
|
||||
|
||||
endpoint = Endpoint(provider, pool)
|
||||
self._endpoints[provider.id] = endpoint
|
||||
|
||||
# duck type to proxy http invocations
|
||||
for method in ClusteredAPI._HTTP_VERBS:
|
||||
setattr(self, method, self._proxy_stub(method))
|
||||
|
||||
LOG.debug("Initializing API endpoints")
|
||||
conns = greenpool.GreenPool()
|
||||
for endpoint in self._endpoints.values():
|
||||
conns.spawn(self._validate, endpoint)
|
||||
eventlet.sleep(0)
|
||||
while conns.running():
|
||||
if (self.health == ClusterHealth.GREEN
|
||||
or self.health == ClusterHealth.ORANGE):
|
||||
# only wait for 1 or more endpoints to reduce init time
|
||||
break
|
||||
eventlet.sleep(0.5)
|
||||
|
||||
for endpoint in self._endpoints.values():
|
||||
# dynamic loop for each endpoint to ensure connectivity
|
||||
loop = loopingcall.DynamicLoopingCall(
|
||||
self._endpoint_keepalive, endpoint)
|
||||
loop.start(initial_delay=self._keepalive_interval,
|
||||
periodic_interval_max=self._keepalive_interval,
|
||||
stop_on_exception=False)
|
||||
|
||||
LOG.debug("Done initializing API endpoint(s). "
|
||||
"API cluster health: %s", self.health)
|
||||
|
||||
def _endpoint_keepalive(self, endpoint):
|
||||
delta = datetime.datetime.now() - endpoint.last_updated
|
||||
if delta.seconds >= self._keepalive_interval:
|
||||
# TODO(boden): backoff on validation failure
|
||||
self._validate(endpoint)
|
||||
return self._keepalive_interval
|
||||
return self._keepalive_interval - delta.seconds
|
||||
|
||||
@property
|
||||
def providers(self):
|
||||
return [ep.provider for ep in self._endpoints.values()]
|
||||
|
||||
@property
|
||||
def endpoints(self):
|
||||
return copy.copy(self._endpoints)
|
||||
|
||||
@property
|
||||
def http_provider(self):
|
||||
return self._http_provider
|
||||
|
||||
@property
|
||||
def health(self):
|
||||
down = 0
|
||||
up = 0
|
||||
for endpoint in self._endpoints.values():
|
||||
if endpoint.state != EndpointState.UP:
|
||||
down += 1
|
||||
else:
|
||||
up += 1
|
||||
|
||||
if down == len(self._endpoints):
|
||||
return ClusterHealth.RED
|
||||
return (ClusterHealth.GREEN
|
||||
if up == len(self._endpoints)
|
||||
else ClusterHealth.ORANGE)
|
||||
|
||||
def revalidate_endpoints(self):
|
||||
# validate each endpoint in serial
|
||||
for endpoint in self._endpoints.values():
|
||||
self._validate(endpoint)
|
||||
|
||||
def _validate(self, endpoint):
|
||||
try:
|
||||
with endpoint.pool.item() as conn:
|
||||
self._http_provider.validate_connection(self, endpoint, conn)
|
||||
endpoint.set_state(EndpointState.UP)
|
||||
LOG.debug("Validated API cluster endpoint: %s", endpoint)
|
||||
except Exception as e:
|
||||
endpoint.set_state(EndpointState.DOWN)
|
||||
LOG.warning(_LW("Failed to validate API cluster endpoint "
|
||||
"'%(ep)s' due to: %(err)s"),
|
||||
{'ep': endpoint, 'err': e})
|
||||
|
||||
def _select_endpoint(self, revalidate=False):
|
||||
connected = {}
|
||||
for provider_id, endpoint in self._endpoints.items():
|
||||
if endpoint.state == EndpointState.UP:
|
||||
connected[provider_id] = endpoint
|
||||
if endpoint.pool.free():
|
||||
# connection can be used now
|
||||
return endpoint
|
||||
|
||||
if not connected and revalidate:
|
||||
LOG.debug("All endpoints DOWN; revalidating.")
|
||||
# endpoints may have become available, try to revalidate
|
||||
self.revalidate_endpoints()
|
||||
return self._select_endpoint(revalidate=False)
|
||||
|
||||
# no free connections; randomly select a connected endpoint
|
||||
# which will likely wait on pool.item() until a connection frees up
|
||||
return (connected[random.choice(connected.keys())]
|
||||
if connected else None)
|
||||
|
||||
def endpoint_for_connection(self, conn):
|
||||
# check all endpoint pools
|
||||
for endpoint in self._endpoints.values():
|
||||
if (conn in endpoint.pool.channel.queue or
|
||||
conn in endpoint.pool.free_items):
|
||||
return endpoint
|
||||
|
||||
@property
|
||||
def cluster_id(self):
|
||||
return ','.join([str(ep.provider.url)
|
||||
for ep in self._endpoints.values()])
|
||||
|
||||
@contextlib.contextmanager
|
||||
def connection(self):
|
||||
with self.endpoint_connection() as conn_data:
|
||||
yield conn_data.connection
|
||||
|
||||
@contextlib.contextmanager
|
||||
def endpoint_connection(self):
|
||||
endpoint = self._select_endpoint(revalidate=True)
|
||||
if not endpoint:
|
||||
raise nsx_err.ServiceClusterUnavailable(
|
||||
cluster_id=self.cluster_id)
|
||||
|
||||
if endpoint.pool.free() == 0:
|
||||
LOG.info(_LI("API endpoint %(ep)s at connection "
|
||||
"capacity %(max)s and has %(waiting)s waiting"),
|
||||
{'ep': endpoint,
|
||||
'max': endpoint.pool.max_size,
|
||||
'waiting': endpoint.pool.waiting()})
|
||||
# pool.item() will wait if pool has 0 free
|
||||
with endpoint.pool.item() as conn:
|
||||
yield EndpointConnection(endpoint, conn)
|
||||
|
||||
def _proxy_stub(self, proxy_for):
|
||||
def _call_proxy(url, *args, **kwargs):
|
||||
return self._proxy(proxy_for, url, *args, **kwargs)
|
||||
return _call_proxy
|
||||
|
||||
def _proxy(self, proxy_for, uri, *args, **kwargs):
|
||||
# proxy http request call to an avail endpoint
|
||||
with self.endpoint_connection() as conn_data:
|
||||
conn = conn_data.connection
|
||||
endpoint = conn_data.endpoint
|
||||
|
||||
# http conn must support requests style interface
|
||||
do_request = getattr(conn, proxy_for)
|
||||
|
||||
if not uri.startswith('/'):
|
||||
uri = "/%s" % uri
|
||||
url = "%s%s" % (endpoint.provider.url, uri)
|
||||
try:
|
||||
LOG.debug("API cluster proxy %s %s to %s",
|
||||
proxy_for.upper(), uri, url)
|
||||
# call the actual connection method to do the
|
||||
# http request/response over the wire
|
||||
response = do_request(url, *args, **kwargs)
|
||||
endpoint.set_state(EndpointState.UP)
|
||||
|
||||
return response
|
||||
except Exception as e:
|
||||
LOG.warning(_LW("Request failed due to: %s"), e)
|
||||
endpoint.set_state(EndpointState.DOWN)
|
||||
# retry until exhausting endpoints
|
||||
return self._proxy(proxy_for, uri, *args, **kwargs)
|
||||
|
||||
|
||||
class NSXClusteredAPI(ClusteredAPI):
|
||||
"""Extends ClusteredAPI to get conf values and setup the
|
||||
NSX v3 cluster.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
username=None,
|
||||
password=None,
|
||||
retries=None,
|
||||
insecure=None,
|
||||
ca_file=None,
|
||||
concurrent_connections=None,
|
||||
http_timeout=None,
|
||||
conn_idle_timeout=None,
|
||||
http_provider=None):
|
||||
self.username = username or cfg.CONF.nsx_v3.nsx_user
|
||||
self.password = password or cfg.CONF.nsx_v3.nsx_password
|
||||
self.retries = retries or cfg.CONF.nsx_v3.retries
|
||||
self.insecure = insecure or cfg.CONF.nsx_v3.insecure
|
||||
self.ca_file = ca_file or cfg.CONF.nsx_v3.ca_file
|
||||
self.conns_per_pool = (concurrent_connections or
|
||||
cfg.CONF.nsx_v3.concurrent_connections)
|
||||
self.http_timeout = http_timeout or cfg.CONF.nsx_v3.http_timeout
|
||||
self.conn_idle_timeout = (conn_idle_timeout or
|
||||
cfg.CONF.nsx_v3.conn_idle_timeout)
|
||||
|
||||
self._http_provider = http_provider or NSXRequestsHTTPProvider()
|
||||
|
||||
super(NSXClusteredAPI, self).__init__(
|
||||
self._build_conf_providers(),
|
||||
self._http_provider,
|
||||
max_conns_per_pool=self.conns_per_pool,
|
||||
keepalive_interval=self.conn_idle_timeout)
|
||||
|
||||
LOG.debug("Created NSX clustered API with '%s' "
|
||||
"provider", self._http_provider.provider_id)
|
||||
|
||||
def _build_conf_providers(self):
|
||||
|
||||
def _schemed_url(uri):
|
||||
uri = uri.strip('/')
|
||||
return urlparse.urlparse(
|
||||
uri if uri.startswith('http') else
|
||||
"%s://%s" % (self._http_provider.default_scheme, uri))
|
||||
|
||||
conf_urls = cfg.CONF.nsx_v3.nsx_managers[:]
|
||||
urls = []
|
||||
providers = []
|
||||
|
||||
for conf_url in conf_urls:
|
||||
conf_url = _schemed_url(conf_url)
|
||||
if conf_url in urls:
|
||||
LOG.warning(_LW("'%s' already defined in configuration file. "
|
||||
"Skipping."), urlparse.urlunparse(conf_url))
|
||||
continue
|
||||
urls.append(conf_url)
|
||||
providers.append(Provider(
|
||||
conf_url.netloc, urlparse.urlunparse(conf_url)))
|
||||
return providers
|
@ -368,8 +368,9 @@ class LogicalRouterPort(AbstractRESTResource):
|
||||
else:
|
||||
err_msg = (_("Logical router link port not found on logical "
|
||||
"switch %s") % logical_switch_id)
|
||||
raise nsx_exc.ResourceNotFound(manager=client._get_manager_ip(),
|
||||
operation=err_msg)
|
||||
raise nsx_exc.ResourceNotFound(
|
||||
manager=client._get_nsx_managers_from_conf(),
|
||||
operation=err_msg)
|
||||
|
||||
def update_by_lswitch_id(self, logical_router_id, ls_id, **payload):
|
||||
port = self.get_by_lswitch_id(ls_id)
|
||||
@ -390,5 +391,5 @@ class LogicalRouterPort(AbstractRESTResource):
|
||||
if port['resource_type'] == nsx_constants.LROUTERPORT_LINKONTIER1:
|
||||
return port
|
||||
raise nsx_exc.ResourceNotFound(
|
||||
manager=client._get_manager_ip(),
|
||||
manager=client._get_nsx_managers_from_conf(),
|
||||
operation="get router link port")
|
||||
|
@ -66,6 +66,7 @@ from vmware_nsx.common import utils
|
||||
from vmware_nsx.db import db as nsx_db
|
||||
from vmware_nsx.nsxlib import v3 as nsxlib
|
||||
from vmware_nsx.nsxlib.v3 import client as nsx_client
|
||||
from vmware_nsx.nsxlib.v3 import cluster as nsx_cluster
|
||||
from vmware_nsx.nsxlib.v3 import dfw_api as firewall
|
||||
from vmware_nsx.nsxlib.v3 import resources as nsx_resources
|
||||
from vmware_nsx.nsxlib.v3 import router
|
||||
@ -110,6 +111,10 @@ class NsxV3Plugin(addr_pair_db.AllowedAddressPairsMixin,
|
||||
super(NsxV3Plugin, self).__init__()
|
||||
LOG.info(_LI("Starting NsxV3Plugin"))
|
||||
|
||||
self._api_cluster = nsx_cluster.NSXClusteredAPI()
|
||||
self._nsx_client = nsx_client.NSX3Client(self._api_cluster)
|
||||
nsx_client._set_default_api_cluster(self._api_cluster)
|
||||
|
||||
self.base_binding_dict = {
|
||||
pbin.VIF_TYPE: pbin.VIF_TYPE_OVS,
|
||||
pbin.VIF_DETAILS: {
|
||||
@ -119,7 +124,7 @@ class NsxV3Plugin(addr_pair_db.AllowedAddressPairsMixin,
|
||||
self.tier0_groups_dict = {}
|
||||
self._setup_dhcp()
|
||||
self._start_rpc_notifiers()
|
||||
self._nsx_client = nsx_client.NSX3Client()
|
||||
|
||||
self._port_client = nsx_resources.LogicalPort(self._nsx_client)
|
||||
self.nsgroup_container, self.default_section = (
|
||||
security.init_nsgroup_container_and_default_section_rules())
|
||||
|
@ -192,6 +192,9 @@ class MockRequestSessionApi(object):
|
||||
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)
|
||||
|
@ -12,6 +12,7 @@
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import mock
|
||||
import six
|
||||
|
||||
from neutron.api.v2 import attributes
|
||||
@ -39,6 +40,7 @@ from oslo_utils import uuidutils
|
||||
|
||||
from vmware_nsx.common import utils
|
||||
from vmware_nsx.nsxlib.v3 import client as nsx_client
|
||||
from vmware_nsx.nsxlib.v3 import cluster as nsx_cluster
|
||||
from vmware_nsx.plugins.nsx_v3 import plugin as nsx_plugin
|
||||
from vmware_nsx.tests import unit as vmware
|
||||
from vmware_nsx.tests.unit.nsx_v3 import mocks as nsx_v3_mocks
|
||||
@ -54,26 +56,65 @@ class NsxV3PluginTestCaseMixin(test_plugin.NeutronDbPluginV2TestCase,
|
||||
def setUp(self, plugin=PLUGIN_NAME,
|
||||
ext_mgr=None,
|
||||
service_plugins=None):
|
||||
|
||||
self._patchers = []
|
||||
|
||||
self.mock_api = nsx_v3_mocks.MockRequestSessionApi()
|
||||
self.client = nsx_client.NSX3Client()
|
||||
nsxlib_testcase.NsxClientTestCase.setup_conf_overrides()
|
||||
self.cluster = nsx_cluster.NSXClusteredAPI(
|
||||
http_provider=nsxlib_testcase.MemoryMockAPIProvider(self.mock_api))
|
||||
|
||||
def mock_client_module(mod):
|
||||
mocked = nsxlib_testcase.NsxClientTestCase.mocked_session_module(
|
||||
mod, self.client,
|
||||
mock_session=self.mock_api)
|
||||
mocked.start()
|
||||
self._patchers.append(mocked)
|
||||
self.cluster.revalidate_endpoints()
|
||||
|
||||
mock_client_module(nsx_plugin.security.firewall)
|
||||
mock_client_module(nsx_plugin.router.nsxlib)
|
||||
mock_client_module(nsx_plugin)
|
||||
def _patch_object(*args, **kwargs):
|
||||
patcher = mock.patch.object(*args, **kwargs)
|
||||
patcher.start()
|
||||
self._patchers.append(patcher)
|
||||
|
||||
def _new_cluster(*args, **kwargs):
|
||||
return self.cluster
|
||||
|
||||
self.mocked_rest_fns(
|
||||
nsx_plugin.security.firewall, 'nsxclient',
|
||||
mock_cluster=self.cluster)
|
||||
self.mocked_rest_fns(
|
||||
nsx_plugin.router.nsxlib, 'client', mock_cluster=self.cluster)
|
||||
|
||||
mock_client_module = mock.Mock()
|
||||
mock_cluster_module = mock.Mock()
|
||||
mocked_client = self.new_mocked_client(
|
||||
nsx_client.NSX3Client, mock_cluster=self.cluster)
|
||||
mock_cluster_module.NSXClusteredAPI.return_value = self.cluster
|
||||
mock_client_module.NSX3Client.return_value = mocked_client
|
||||
_patch_object(nsx_plugin, 'nsx_client', new=mock_client_module)
|
||||
_patch_object(nsx_plugin, 'nsx_cluster', new=mock_cluster_module)
|
||||
|
||||
super(NsxV3PluginTestCaseMixin, self).setUp(plugin=plugin,
|
||||
ext_mgr=ext_mgr)
|
||||
|
||||
self.maxDiff = None
|
||||
|
||||
# populate pre-existing mock resources
|
||||
cluster_id = uuidutils.generate_uuid()
|
||||
self.mock_api.post(
|
||||
'api/v1/logical-routers',
|
||||
data=jsonutils.dumps({
|
||||
'display_name': nsx_v3_mocks.DEFAULT_TIER0_ROUTER_UUID,
|
||||
'router_type': "TIER0",
|
||||
'id': nsx_v3_mocks.DEFAULT_TIER0_ROUTER_UUID,
|
||||
'edge_cluster_id': cluster_id}),
|
||||
headers=nsx_client.JSONRESTClient._DEFAULT_HEADERS)
|
||||
|
||||
self.mock_api.post(
|
||||
'api/v1/edge-clusters',
|
||||
data=jsonutils.dumps({
|
||||
'id': cluster_id,
|
||||
'members': [
|
||||
{'member_index': 0},
|
||||
{'member_index': 1}
|
||||
]}),
|
||||
headers=nsx_client.JSONRESTClient._DEFAULT_HEADERS)
|
||||
|
||||
def tearDown(self):
|
||||
for patcher in self._patchers:
|
||||
patcher.stop()
|
||||
@ -215,27 +256,6 @@ class TestL3NatTestCase(L3NatTest,
|
||||
service_plugins=None):
|
||||
super(TestL3NatTestCase, self).setUp(plugin=plugin, ext_mgr=ext_mgr)
|
||||
|
||||
cluster_id = uuidutils.generate_uuid()
|
||||
|
||||
self.mock_api.post(
|
||||
'api/v1/logical-routers',
|
||||
data=jsonutils.dumps({
|
||||
'display_name': nsx_v3_mocks.DEFAULT_TIER0_ROUTER_UUID,
|
||||
'router_type': "TIER0",
|
||||
'id': nsx_v3_mocks.DEFAULT_TIER0_ROUTER_UUID,
|
||||
'edge_cluster_id': cluster_id}),
|
||||
headers=nsx_client.JSONRESTClient._DEFAULT_HEADERS)
|
||||
|
||||
self.mock_api.post(
|
||||
'api/v1/edge-clusters',
|
||||
data=jsonutils.dumps({
|
||||
'id': cluster_id,
|
||||
'members': [
|
||||
{'member_index': 0},
|
||||
{'member_index': 1}
|
||||
]}),
|
||||
headers=nsx_client.JSONRESTClient._DEFAULT_HEADERS)
|
||||
|
||||
def _test_create_l3_ext_network(
|
||||
self, physical_network=nsx_v3_mocks.DEFAULT_TIER0_ROUTER_UUID):
|
||||
name = 'l3_ext_net'
|
||||
|
@ -13,20 +13,19 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
import contextlib
|
||||
import copy
|
||||
import mock
|
||||
import types
|
||||
import unittest
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import uuidutils
|
||||
from vmware_nsx.nsxlib.v3 import client as nsx_client
|
||||
from vmware_nsx.tests.unit.nsx_v3 import mocks
|
||||
from vmware_nsx.nsxlib.v3 import cluster as nsx_cluster
|
||||
|
||||
NSX_USER = 'admin'
|
||||
NSX_PASSWORD = 'default'
|
||||
NSX_MANAGER = '1.2.3.4'
|
||||
NSX_INSECURE = True
|
||||
NSX_INSECURE = False
|
||||
NSX_CERT = '/opt/stack/certs/nsx.pem'
|
||||
|
||||
V3_CLIENT_PKG = 'vmware_nsx.nsxlib.v3.client'
|
||||
@ -35,26 +34,45 @@ BRIDGE_FNS = ['create_resource', 'delete_resource',
|
||||
|
||||
|
||||
class NsxLibTestCase(unittest.TestCase):
|
||||
def setUp(self, *args, **kwargs):
|
||||
super(NsxLibTestCase, self).setUp()
|
||||
cfg.CONF.set_override('nsx_user', NSX_USER)
|
||||
cfg.CONF.set_override('nsx_password', NSX_PASSWORD)
|
||||
cfg.CONF.set_override('default_tz_uuid',
|
||||
uuidutils.generate_uuid())
|
||||
cfg.CONF.set_override('nsx_controllers', ['11.9.8.7', '11.9.8.77'])
|
||||
|
||||
@classmethod
|
||||
def setup_conf_overrides(cls):
|
||||
cfg.CONF.set_override('default_overlay_tz_uuid',
|
||||
uuidutils.generate_uuid(), 'nsx_v3')
|
||||
cfg.CONF.set_override('nsx_user', NSX_USER, 'nsx_v3')
|
||||
cfg.CONF.set_override('nsx_password', NSX_PASSWORD, 'nsx_v3')
|
||||
cfg.CONF.set_override('nsx_manager', NSX_MANAGER, 'nsx_v3')
|
||||
cfg.CONF.set_override('nsx_managers', [NSX_MANAGER], 'nsx_v3')
|
||||
cfg.CONF.set_override('insecure', NSX_INSECURE, 'nsx_v3')
|
||||
cfg.CONF.set_override('ca_file', NSX_CERT, 'nsx_v3')
|
||||
|
||||
def setUp(self, *args, **kwargs):
|
||||
super(NsxLibTestCase, self).setUp()
|
||||
NsxClientTestCase.setup_conf_overrides()
|
||||
|
||||
# print diffs when assert comparisons fail
|
||||
self.maxDiff = None
|
||||
|
||||
|
||||
# NOTE(boden): a lot of the hackery and magic below can be removed
|
||||
# once we move all v3 rest function calls to OO based on rest resource
|
||||
class MemoryMockAPIProvider(nsx_cluster.AbstractHTTPProvider):
|
||||
"""Acts as a HTTP provider for mocking which is backed
|
||||
by a MockRequestSessionApi.
|
||||
"""
|
||||
|
||||
def __init__(self, mock_session_api):
|
||||
self._store = mock_session_api
|
||||
|
||||
@property
|
||||
def provider_id(self):
|
||||
return "Memory mock API"
|
||||
|
||||
def validate_connection(self, cluster_api, endpoint, conn):
|
||||
return
|
||||
|
||||
def new_connection(self, cluster_api, provider):
|
||||
# all callers use the same backing
|
||||
return self._store
|
||||
|
||||
|
||||
class NsxClientTestCase(NsxLibTestCase):
|
||||
|
||||
class MockBridge(object):
|
||||
@ -77,114 +95,145 @@ class NsxClientTestCase(NsxLibTestCase):
|
||||
return nsx_client.create_resource(
|
||||
resource, data, client=self._client)
|
||||
|
||||
def new_client(
|
||||
self, clazz, host_ip=NSX_MANAGER,
|
||||
user_name=NSX_USER,
|
||||
password=NSX_PASSWORD,
|
||||
insecure=NSX_INSECURE,
|
||||
url_prefix=None,
|
||||
default_headers=None,
|
||||
cert_file=NSX_CERT):
|
||||
class MockNSXClusteredAPI(nsx_cluster.NSXClusteredAPI):
|
||||
|
||||
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)
|
||||
def __init__(self, session_response=None):
|
||||
super(NsxClientTestCase.MockNSXClusteredAPI, self).__init__(
|
||||
http_provider=NsxClientTestCase.MockHTTPProvider(
|
||||
session_response=session_response))
|
||||
self._record = mock.Mock()
|
||||
|
||||
@contextlib.contextmanager
|
||||
def mocked_client(self, client, mock_validate=True):
|
||||
session = client._session
|
||||
with mock.patch.object(session, 'get') as _get:
|
||||
with mock.patch.object(session, 'post') as _post:
|
||||
with mock.patch.object(session, 'delete') as _delete:
|
||||
with mock.patch.object(session, 'put') as _put:
|
||||
rep = {
|
||||
'get': _get,
|
||||
'put': _put,
|
||||
'delete': _delete,
|
||||
'post': _post
|
||||
}
|
||||
if mock_validate:
|
||||
with mock.patch.object(client, '_validate_result'):
|
||||
yield rep
|
||||
else:
|
||||
yield rep
|
||||
def record_call(self, request, **kwargs):
|
||||
verb = request.method.lower()
|
||||
|
||||
@contextlib.contextmanager
|
||||
def mocked_resource(self, resource, mock_validate=True):
|
||||
with self.mocked_client(resource._client,
|
||||
mock_validate=mock_validate) as _client:
|
||||
yield _client
|
||||
# filter out requests specific attributes
|
||||
checked_kwargs = copy.copy(kwargs)
|
||||
del checked_kwargs['proxies']
|
||||
del checked_kwargs['stream']
|
||||
del checked_kwargs['timeout']
|
||||
if 'allow_redirects' in checked_kwargs:
|
||||
del checked_kwargs['allow_redirects']
|
||||
|
||||
@contextlib.contextmanager
|
||||
def mocked_client_bridge(self, client, module, attr, mock_validate=True):
|
||||
mocked_bridge = NsxClientTestCase.MockBridge(client)
|
||||
mocked_bridge.JSONRESTClient = nsx_client.JSONRESTClient
|
||||
with self.mocked_client(client, mock_validate=mock_validate) as mocked:
|
||||
with mock.patch.object(module, attr, new=mocked_bridge):
|
||||
yield mocked
|
||||
for attr in ['url', 'body']:
|
||||
checked_kwargs[attr] = getattr(request, attr, None)
|
||||
|
||||
@classmethod
|
||||
def patch_client_module(cls, in_module, fn_map):
|
||||
mock_client = mock.Mock()
|
||||
for name, clazz in in_module.__dict__.items():
|
||||
if (isinstance(clazz, types.ModuleType) and
|
||||
clazz.__name__ == V3_CLIENT_PKG):
|
||||
for fn_name in BRIDGE_FNS:
|
||||
mock_call = fn_map.get(fn_name, getattr(mocks, fn_name))
|
||||
setattr(mock_client, fn_name, mock_call)
|
||||
for fn_name, fn_call in fn_map.items():
|
||||
if fn_name not in BRIDGE_FNS:
|
||||
setattr(mock_client, fn_name, fn_call)
|
||||
return mock.patch.object(in_module, name, new=mock_client)
|
||||
return None
|
||||
# remove headers we don't need to verify
|
||||
checked_kwargs['headers'] = copy.copy(request.headers)
|
||||
for header in ['Accept-Encoding', 'User-Agent',
|
||||
'Connection', 'Authorization',
|
||||
'Content-Length']:
|
||||
if header in checked_kwargs['headers']:
|
||||
del checked_kwargs['headers'][header]
|
||||
|
||||
@classmethod
|
||||
def mocked_session_module(cls, in_module, with_client,
|
||||
mock_session=None):
|
||||
mock_session = mock_session or mocks.MockRequestSessionApi()
|
||||
with_client._session = mock_session
|
||||
checked_kwargs['headers'] = request.headers
|
||||
|
||||
def _call_client(fn_name):
|
||||
def _client(*args, **kwargs):
|
||||
client_fn = getattr(nsx_client, fn_name)
|
||||
kwargs['client'] = with_client
|
||||
return client_fn(*args, **kwargs)
|
||||
return _client
|
||||
# record the call in the mock object
|
||||
method = getattr(self._record, verb)
|
||||
method(**checked_kwargs)
|
||||
|
||||
def _proxy_new_client_for(client):
|
||||
def assert_called_once(self, verb, **kwargs):
|
||||
mock_call = getattr(self._record, verb.lower())
|
||||
mock_call.assert_called_once_with(**kwargs)
|
||||
|
||||
new_client_fn = client.new_client_for
|
||||
@property
|
||||
def recorded_calls(self):
|
||||
return self._record
|
||||
|
||||
def _new_client_for(*uri_segs):
|
||||
new_client = new_client_fn(*uri_segs)
|
||||
new_client._session = mock_session
|
||||
new_client.new_client_for = _proxy_new_client_for(new_client)
|
||||
return new_client
|
||||
return _new_client_for
|
||||
class MockHTTPProvider(nsx_cluster.NSXRequestsHTTPProvider):
|
||||
|
||||
def _proxy_init(class_name):
|
||||
client_init = getattr(nsx_client, class_name)
|
||||
def __init__(self, session_response=None):
|
||||
super(NsxClientTestCase.MockHTTPProvider, self).__init__()
|
||||
self._session_response = session_response
|
||||
|
||||
def _init_client(*args, **kwargs):
|
||||
if (not args and not kwargs and
|
||||
with_client.__class__.__name__ == class_name):
|
||||
return with_client
|
||||
def new_connection(self, cluster_api, provider):
|
||||
# wrapper the session so we can intercept and record calls
|
||||
session = super(NsxClientTestCase.MockHTTPProvider,
|
||||
self).new_connection(cluster_api, provider)
|
||||
|
||||
client = client_init(*args, **kwargs)
|
||||
client._session = mock_session
|
||||
return client
|
||||
mock_adapter = mock.Mock()
|
||||
session_send = session.send
|
||||
|
||||
return _init_client
|
||||
def _adapter_send(request, **kwargs):
|
||||
# record calls at the requests HTTP adapter level
|
||||
mock_response = mock.Mock()
|
||||
mock_response.history = None
|
||||
# needed to bypass requests internal checks for mock
|
||||
mock_response.raw._original_response = {}
|
||||
|
||||
fn_map = {}
|
||||
for fn in BRIDGE_FNS:
|
||||
fn_map[fn] = _call_client(fn)
|
||||
# record the request for later verification
|
||||
cluster_api.record_call(request, **kwargs)
|
||||
return mock_response
|
||||
|
||||
fn_map['NSX3Client'] = _proxy_init('NSX3Client')
|
||||
fn_map['JSONRESTClient'] = _proxy_init('JSONRESTClient')
|
||||
fn_map['RESTClient'] = _proxy_init('RESTClient')
|
||||
def _session_send(request, **kwargs):
|
||||
# calls at the Session level
|
||||
if self._session_response:
|
||||
# consumer has setup a response for the session
|
||||
cluster_api.record_call(request, **kwargs)
|
||||
return self._session_response
|
||||
|
||||
with_client.new_client_for = _proxy_new_client_for(with_client)
|
||||
# bypass requests redirect handling for mock
|
||||
kwargs['allow_redirects'] = False
|
||||
|
||||
return cls.patch_client_module(in_module, fn_map)
|
||||
# session send will end up calling adapter send
|
||||
return session_send(request, **kwargs)
|
||||
|
||||
mock_adapter.send = _adapter_send
|
||||
session.send = _session_send
|
||||
|
||||
def _mock_adapter(*args, **kwargs):
|
||||
# use our mock adapter rather than requests adapter
|
||||
return mock_adapter
|
||||
|
||||
session.get_adapter = _mock_adapter
|
||||
return session
|
||||
|
||||
def validate_connection(self, cluster_api, endpoint, conn):
|
||||
assert conn is not None
|
||||
|
||||
def mock_nsx_clustered_api(self, session_response=None):
|
||||
return NsxClientTestCase.MockNSXClusteredAPI(
|
||||
session_response=session_response)
|
||||
|
||||
def mocked_resource(self, resource_class, mock_validate=True,
|
||||
session_response=None):
|
||||
mocked = resource_class(nsx_client.NSX3Client(
|
||||
self.mock_nsx_clustered_api(session_response=session_response)))
|
||||
if mock_validate:
|
||||
mock.patch.object(mocked._client, '_validate_result').start()
|
||||
|
||||
return mocked
|
||||
|
||||
def new_mocked_client(self, client_class, mock_validate=True,
|
||||
session_response=None, mock_cluster=None,
|
||||
**kwargs):
|
||||
client = client_class(mock_cluster or self.mock_nsx_clustered_api(
|
||||
session_response=session_response), **kwargs)
|
||||
|
||||
if mock_validate:
|
||||
mock.patch.object(client, '_validate_result').start()
|
||||
|
||||
new_client_for = client.new_client_for
|
||||
|
||||
def _new_client_for(*args, **kwargs):
|
||||
sub_client = new_client_for(*args, **kwargs)
|
||||
if mock_validate:
|
||||
mock.patch.object(sub_client, '_validate_result').start()
|
||||
return sub_client
|
||||
|
||||
client.new_client_for = _new_client_for
|
||||
|
||||
return client
|
||||
|
||||
def mocked_rest_fns(self, module, attr, mock_validate=True,
|
||||
mock_cluster=None):
|
||||
client = nsx_client.NSX3Client(
|
||||
mock_cluster or self.mock_nsx_clustered_api())
|
||||
mocked_fns = NsxClientTestCase.MockBridge(client)
|
||||
mocked_fns.JSONRESTClient = nsx_client.JSONRESTClient
|
||||
|
||||
if mock_validate:
|
||||
mock.patch.object(client, '_validate_result').start()
|
||||
|
||||
mock.patch.object(module, attr, new=mocked_fns).start()
|
||||
|
||||
return mocked_fns
|
||||
|
@ -13,6 +13,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
import copy
|
||||
|
||||
from oslo_log import log
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
@ -26,304 +28,282 @@ LOG = log.getLogger(__name__)
|
||||
|
||||
CLIENT_PKG = 'vmware_nsx.nsxlib.v3.client'
|
||||
|
||||
DFT_ACCEPT_HEADERS = {
|
||||
'Accept': '*/*'
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
def _headers(**kwargs):
|
||||
headers = copy.copy(DFT_ACCEPT_HEADERS)
|
||||
headers.update(kwargs)
|
||||
return headers
|
||||
|
||||
|
||||
def assert_call(verb, client_or_resource,
|
||||
url, verify=nsxlib_testcase.NSX_CERT,
|
||||
data=None, headers=DFT_ACCEPT_HEADERS):
|
||||
nsx_client = client_or_resource
|
||||
if getattr(nsx_client, '_client', None) is not None:
|
||||
nsx_client = nsx_client._client
|
||||
cluster = nsx_client._conn
|
||||
cluster.assert_called_once(
|
||||
verb,
|
||||
**{'url': url, 'verify': verify, 'body': data,
|
||||
'headers': headers, 'cert': None})
|
||||
|
||||
|
||||
def assert_json_call(verb, client_or_resource, url,
|
||||
verify=nsxlib_testcase.NSX_CERT,
|
||||
data=None,
|
||||
headers=client.JSONRESTClient._DEFAULT_HEADERS):
|
||||
return assert_call(verb, client_or_resource, url,
|
||||
verify=verify, data=data,
|
||||
headers=headers)
|
||||
|
||||
|
||||
class NsxV3RESTClientTestCase(nsxlib_testcase.NsxClientTestCase):
|
||||
|
||||
def test_client_conf_init(self):
|
||||
api = self.new_client(client.RESTClient)
|
||||
self.assertEqual((
|
||||
nsxlib_testcase.NSX_USER, nsxlib_testcase.NSX_PASSWORD),
|
||||
api._session.auth)
|
||||
self.assertEqual(nsxlib_testcase.NSX_MANAGER, api._host_ip)
|
||||
self.assertEqual(nsxlib_testcase.NSX_CERT, 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((
|
||||
nsxlib_testcase.NSX_USER, 'mypass'),
|
||||
api._session.auth)
|
||||
self.assertEqual('11.12.13.14', api._host_ip)
|
||||
self.assertEqual(nsxlib_testcase.NSX_CERT, api._cert_file)
|
||||
|
||||
def test_client_url_prefix(self):
|
||||
api = self.new_client(client.RESTClient, url_prefix='/cloud/api')
|
||||
with self.mocked_client(api) as mocked:
|
||||
mock_get = mocked.get('get')
|
||||
mock_get.return_value = {}
|
||||
api.list()
|
||||
api = self.new_mocked_client(client.RESTClient,
|
||||
url_prefix='/cloud/api')
|
||||
|
||||
assert_session_call(
|
||||
mocked.get('get'),
|
||||
'https://1.2.3.4/cloud/api',
|
||||
False, None, {}, nsxlib_testcase.NSX_CERT)
|
||||
api.list()
|
||||
|
||||
mock_get.reset_mock()
|
||||
assert_call(
|
||||
'get', api,
|
||||
'https://1.2.3.4/cloud/api')
|
||||
|
||||
api.url_list('v1/ports')
|
||||
assert_session_call(
|
||||
mock_get,
|
||||
'https://1.2.3.4/cloud/api/v1/ports', False, None, {},
|
||||
nsxlib_testcase.NSX_CERT)
|
||||
api = self.new_mocked_client(client.RESTClient,
|
||||
url_prefix='/cloud/api')
|
||||
|
||||
api.url_list('v1/ports')
|
||||
|
||||
assert_call(
|
||||
'get', api,
|
||||
'https://1.2.3.4/cloud/api/v1/ports')
|
||||
|
||||
def test_client_headers(self):
|
||||
default_headers = {'Content-Type': 'application/golang'}
|
||||
api = self.new_client(
|
||||
api = self.new_mocked_client(
|
||||
client.RESTClient, default_headers=default_headers,
|
||||
url_prefix='/v1/api')
|
||||
|
||||
with self.mocked_client(api) as mocked:
|
||||
mock_get = mocked.get('get')
|
||||
api.list()
|
||||
|
||||
mock_get.return_value = {}
|
||||
assert_call(
|
||||
'get', api,
|
||||
'https://1.2.3.4/v1/api',
|
||||
headers=_headers(**default_headers))
|
||||
|
||||
api.list()
|
||||
api = self.new_mocked_client(
|
||||
client.RESTClient,
|
||||
default_headers=default_headers,
|
||||
url_prefix='/v1/api')
|
||||
|
||||
assert_session_call(
|
||||
mock_get,
|
||||
'https://1.2.3.4/v1/api',
|
||||
False, None, default_headers, nsxlib_testcase.NSX_CERT)
|
||||
|
||||
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,
|
||||
nsxlib_testcase.NSX_CERT)
|
||||
method_headers = {'X-API-Key': 'strong-crypt'}
|
||||
api.url_list('ports/33', headers=method_headers)
|
||||
method_headers.update(default_headers)
|
||||
assert_call(
|
||||
'get', api,
|
||||
'https://1.2.3.4/v1/api/ports/33',
|
||||
headers=_headers(**method_headers))
|
||||
|
||||
def test_client_for(self):
|
||||
api = self.new_client(client.RESTClient, url_prefix='api/v1/')
|
||||
api = self.new_mocked_client(client.RESTClient, url_prefix='api/v1/')
|
||||
sub_api = api.new_client_for('switch/ports')
|
||||
|
||||
with self.mocked_client(sub_api) as mocked:
|
||||
sub_api.get('11a2b')
|
||||
sub_api.get('11a2b')
|
||||
|
||||
assert_session_call(
|
||||
mocked.get('get'),
|
||||
'https://1.2.3.4/api/v1/switch/ports/11a2b',
|
||||
False, None, {}, nsxlib_testcase.NSX_CERT)
|
||||
assert_call(
|
||||
'get', sub_api,
|
||||
'https://1.2.3.4/api/v1/switch/ports/11a2b')
|
||||
|
||||
def test_client_list(self):
|
||||
api = self.new_client(client.RESTClient, url_prefix='api/v1/ports')
|
||||
with self.mocked_client(api) as mocked:
|
||||
api.list()
|
||||
api = self.new_mocked_client(client.RESTClient,
|
||||
url_prefix='api/v1/ports')
|
||||
api.list()
|
||||
|
||||
assert_session_call(
|
||||
mocked.get('get'),
|
||||
'https://1.2.3.4/api/v1/ports',
|
||||
False, None, {}, nsxlib_testcase.NSX_CERT)
|
||||
assert_call(
|
||||
'get', api,
|
||||
'https://1.2.3.4/api/v1/ports')
|
||||
|
||||
def test_client_get(self):
|
||||
api = self.new_client(client.RESTClient, url_prefix='api/v1/ports')
|
||||
with self.mocked_client(api) as mocked:
|
||||
api.get('unique-id')
|
||||
api = self.new_mocked_client(client.RESTClient,
|
||||
url_prefix='api/v1/ports')
|
||||
api.get('unique-id')
|
||||
|
||||
assert_session_call(
|
||||
mocked.get('get'),
|
||||
'https://1.2.3.4/api/v1/ports/unique-id',
|
||||
False, None, {}, nsxlib_testcase.NSX_CERT)
|
||||
assert_call(
|
||||
'get', api,
|
||||
'https://1.2.3.4/api/v1/ports/unique-id')
|
||||
|
||||
def test_client_delete(self):
|
||||
api = self.new_client(client.RESTClient, url_prefix='api/v1/ports')
|
||||
with self.mocked_client(api) as mocked:
|
||||
api.delete('unique-id')
|
||||
api = self.new_mocked_client(client.RESTClient,
|
||||
url_prefix='api/v1/ports')
|
||||
api.delete('unique-id')
|
||||
|
||||
assert_session_call(
|
||||
mocked.get('delete'),
|
||||
'https://1.2.3.4/api/v1/ports/unique-id',
|
||||
False, None, {}, nsxlib_testcase.NSX_CERT)
|
||||
assert_call(
|
||||
'delete', api,
|
||||
'https://1.2.3.4/api/v1/ports/unique-id')
|
||||
|
||||
def test_client_update(self):
|
||||
api = self.new_client(client.RESTClient, url_prefix='api/v1/ports')
|
||||
with self.mocked_client(api) as mocked:
|
||||
api.update('unique-id', {'name': 'a-new-name'})
|
||||
api = self.new_mocked_client(client.RESTClient,
|
||||
url_prefix='api/v1/ports')
|
||||
api.update('unique-id', jsonutils.dumps({'name': 'a-new-name'}))
|
||||
|
||||
assert_session_call(
|
||||
mocked.get('put'),
|
||||
'https://1.2.3.4/api/v1/ports/unique-id',
|
||||
False, {'name': 'a-new-name'},
|
||||
{}, nsxlib_testcase.NSX_CERT)
|
||||
assert_call(
|
||||
'put', api,
|
||||
'https://1.2.3.4/api/v1/ports/unique-id',
|
||||
data=jsonutils.dumps({'name': 'a-new-name'}))
|
||||
|
||||
def test_client_create(self):
|
||||
api = self.new_client(client.RESTClient, url_prefix='api/v1/ports')
|
||||
with self.mocked_client(api) as mocked:
|
||||
api.create({'resource-name': 'port1'})
|
||||
api = self.new_mocked_client(client.RESTClient,
|
||||
url_prefix='api/v1/ports')
|
||||
api.create(jsonutils.dumps({'resource-name': 'port1'}))
|
||||
|
||||
assert_session_call(
|
||||
mocked.get('post'),
|
||||
'https://1.2.3.4/api/v1/ports',
|
||||
False, {'resource-name': 'port1'},
|
||||
{}, nsxlib_testcase.NSX_CERT)
|
||||
assert_call(
|
||||
'post', api,
|
||||
'https://1.2.3.4/api/v1/ports',
|
||||
data=jsonutils.dumps({'resource-name': 'port1'}))
|
||||
|
||||
def test_client_url_list(self):
|
||||
api = self.new_client(client.RESTClient, url_prefix='api/v1/ports')
|
||||
with self.mocked_client(api) as mocked:
|
||||
api.url_list('/connections', {'Content-Type': 'application/json'})
|
||||
api = self.new_mocked_client(client.RESTClient,
|
||||
url_prefix='api/v1/ports')
|
||||
|
||||
assert_session_call(
|
||||
mocked.get('get'),
|
||||
'https://1.2.3.4/api/v1/ports/connections',
|
||||
False, None,
|
||||
{'Content-Type': 'application/json'},
|
||||
nsxlib_testcase.NSX_CERT)
|
||||
json_headers = {'Content-Type': 'application/json'}
|
||||
|
||||
api.url_list('/connections', json_headers)
|
||||
|
||||
assert_call(
|
||||
'get', api,
|
||||
'https://1.2.3.4/api/v1/ports/connections',
|
||||
headers=_headers(**json_headers))
|
||||
|
||||
def test_client_url_get(self):
|
||||
api = self.new_client(client.RESTClient, url_prefix='api/v1/ports')
|
||||
with self.mocked_client(api) as mocked:
|
||||
api.url_get('connections/1')
|
||||
api = self.new_mocked_client(client.RESTClient,
|
||||
url_prefix='api/v1/ports')
|
||||
api.url_get('connections/1')
|
||||
|
||||
assert_session_call(
|
||||
mocked.get('get'),
|
||||
'https://1.2.3.4/api/v1/ports/connections/1',
|
||||
False, None, {}, nsxlib_testcase.NSX_CERT)
|
||||
assert_call(
|
||||
'get', api,
|
||||
'https://1.2.3.4/api/v1/ports/connections/1')
|
||||
|
||||
def test_client_url_delete(self):
|
||||
api = self.new_client(client.RESTClient, url_prefix='api/v1/ports')
|
||||
with self.mocked_client(api) as mocked:
|
||||
api.url_delete('1')
|
||||
api = self.new_mocked_client(client.RESTClient,
|
||||
url_prefix='api/v1/ports')
|
||||
api.url_delete('1')
|
||||
|
||||
assert_session_call(
|
||||
mocked.get('delete'),
|
||||
'https://1.2.3.4/api/v1/ports/1',
|
||||
False, None, {}, nsxlib_testcase.NSX_CERT)
|
||||
assert_call(
|
||||
'delete', api,
|
||||
'https://1.2.3.4/api/v1/ports/1')
|
||||
|
||||
def test_client_url_put(self):
|
||||
api = self.new_client(client.RESTClient, url_prefix='api/v1/ports')
|
||||
with self.mocked_client(api) as mocked:
|
||||
api.url_put('connections/1', {'name': 'conn1'})
|
||||
api = self.new_mocked_client(client.RESTClient,
|
||||
url_prefix='api/v1/ports')
|
||||
api.url_put('connections/1', jsonutils.dumps({'name': 'conn1'}))
|
||||
|
||||
assert_session_call(
|
||||
mocked.get('put'),
|
||||
'https://1.2.3.4/api/v1/ports/connections/1',
|
||||
False, {'name': 'conn1'},
|
||||
{}, nsxlib_testcase.NSX_CERT)
|
||||
assert_call(
|
||||
'put', api,
|
||||
'https://1.2.3.4/api/v1/ports/connections/1',
|
||||
data=jsonutils.dumps({'name': 'conn1'}))
|
||||
|
||||
def test_client_url_post(self):
|
||||
api = self.new_client(client.RESTClient, url_prefix='api/v1/ports')
|
||||
with self.mocked_client(api) as mocked:
|
||||
api.url_post('1/connections', {'name': 'conn1'})
|
||||
api = self.new_mocked_client(client.RESTClient,
|
||||
url_prefix='api/v1/ports')
|
||||
api.url_post('1/connections', jsonutils.dumps({'name': 'conn1'}))
|
||||
|
||||
assert_session_call(
|
||||
mocked.get('post'),
|
||||
'https://1.2.3.4/api/v1/ports/1/connections',
|
||||
False, {'name': 'conn1'},
|
||||
{}, nsxlib_testcase.NSX_CERT)
|
||||
assert_call(
|
||||
'post', api,
|
||||
'https://1.2.3.4/api/v1/ports/1/connections',
|
||||
data=jsonutils.dumps({'name': 'conn1'}))
|
||||
|
||||
def test_client_validate_result(self):
|
||||
|
||||
api = self.new_client(client.RESTClient)
|
||||
with self.mocked_client(api, mock_validate=False) as mocked:
|
||||
def _verb_response_code(http_verb, status_code):
|
||||
response = mocks.MockRequestsResponse(
|
||||
status_code, None)
|
||||
for _verb in ['get', 'post', 'put', 'delete']:
|
||||
mocked.get(_verb).return_value = response
|
||||
client_call = getattr(api, "url_%s" % http_verb)
|
||||
client_call('', None)
|
||||
def _verb_response_code(http_verb, status_code):
|
||||
response = mocks.MockRequestsResponse(
|
||||
status_code, 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)
|
||||
client_api = self.new_mocked_client(
|
||||
client.RESTClient, mock_validate=False,
|
||||
session_response=response)
|
||||
|
||||
client_call = getattr(client_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(nsxlib_testcase.NsxClientTestCase):
|
||||
|
||||
def test_json_request(self):
|
||||
api = self.new_client(client.JSONRESTClient, url_prefix='api/v2/nat')
|
||||
with self.mocked_client(api) as mocked:
|
||||
mock_post = mocked.get('post')
|
||||
mock_post.return_value = mocks.MockRequestsResponse(
|
||||
200, jsonutils.dumps({'result': {'ok': 200}}))
|
||||
resp = mocks.MockRequestsResponse(
|
||||
200, jsonutils.dumps({'result': {'ok': 200}}))
|
||||
|
||||
resp = api.create(body={'name': 'mgmt-egress'})
|
||||
api = self.new_mocked_client(client.JSONRESTClient,
|
||||
session_response=resp,
|
||||
url_prefix='api/v2/nat')
|
||||
|
||||
assert_session_call(
|
||||
mock_post,
|
||||
'https://1.2.3.4/api/v2/nat',
|
||||
False, jsonutils.dumps({'name': 'mgmt-egress'}),
|
||||
client.JSONRESTClient._DEFAULT_HEADERS,
|
||||
nsxlib_testcase.NSX_CERT)
|
||||
resp = api.create(body={'name': 'mgmt-egress'})
|
||||
|
||||
self.assertEqual(resp, {'result': {'ok': 200}})
|
||||
assert_json_call(
|
||||
'post', api,
|
||||
'https://1.2.3.4/api/v2/nat',
|
||||
data=jsonutils.dumps({'name': 'mgmt-egress'}))
|
||||
|
||||
self.assertEqual(resp, {'result': {'ok': 200}})
|
||||
|
||||
|
||||
class NsxV3APIClientTestCase(nsxlib_testcase.NsxClientTestCase):
|
||||
|
||||
def test_api_call(self):
|
||||
api = self.new_client(client.NSX3Client)
|
||||
with self.mocked_client(api) as mocked:
|
||||
api.get('ports')
|
||||
api = self.new_mocked_client(client.NSX3Client)
|
||||
api.get('ports')
|
||||
|
||||
assert_session_call(
|
||||
mocked.get('get'),
|
||||
'https://1.2.3.4/api/v1/ports',
|
||||
False, None,
|
||||
client.JSONRESTClient._DEFAULT_HEADERS,
|
||||
nsxlib_testcase.NSX_CERT)
|
||||
assert_json_call(
|
||||
'get', api,
|
||||
'https://1.2.3.4/api/v1/ports')
|
||||
|
||||
|
||||
# NOTE(boden): remove this when tmp brigding removed
|
||||
class NsxV3APIClientBridgeTestCase(nsxlib_testcase.NsxClientTestCase):
|
||||
|
||||
def test_get_resource(self):
|
||||
api = self.new_client(client.NSX3Client)
|
||||
with self.mocked_client(api) as mocked:
|
||||
client.get_resource('ports', client=api)
|
||||
api = self.new_mocked_client(client.NSX3Client)
|
||||
client.get_resource('ports', client=api)
|
||||
|
||||
assert_session_call(
|
||||
mocked.get('get'),
|
||||
'https://1.2.3.4/api/v1/ports',
|
||||
False, None,
|
||||
client.JSONRESTClient._DEFAULT_HEADERS,
|
||||
nsxlib_testcase.NSX_CERT)
|
||||
assert_json_call(
|
||||
'get', api,
|
||||
'https://1.2.3.4/api/v1/ports')
|
||||
|
||||
def test_create_resource(self):
|
||||
api = self.new_client(client.NSX3Client)
|
||||
with self.mocked_client(api) as mocked:
|
||||
client.create_resource(
|
||||
'ports', {'resource-name': 'port1'},
|
||||
client=api)
|
||||
api = self.new_mocked_client(client.NSX3Client)
|
||||
client.create_resource(
|
||||
'ports', {'resource-name': 'port1'},
|
||||
client=api)
|
||||
|
||||
assert_session_call(
|
||||
mocked.get('post'),
|
||||
'https://1.2.3.4/api/v1/ports',
|
||||
False, jsonutils.dumps({'resource-name': 'port1'}),
|
||||
client.JSONRESTClient._DEFAULT_HEADERS,
|
||||
nsxlib_testcase.NSX_CERT)
|
||||
assert_json_call(
|
||||
'post', api,
|
||||
'https://1.2.3.4/api/v1/ports',
|
||||
data=jsonutils.dumps({'resource-name': 'port1'}))
|
||||
|
||||
def test_update_resource(self):
|
||||
api = self.new_client(client.NSX3Client)
|
||||
with self.mocked_client(api) as mocked:
|
||||
client.update_resource(
|
||||
'ports/1', {'name': 'a-new-name'}, client=api)
|
||||
api = self.new_mocked_client(client.NSX3Client)
|
||||
client.update_resource(
|
||||
'ports/1', {'name': 'a-new-name'}, client=api)
|
||||
|
||||
assert_session_call(
|
||||
mocked.get('put'),
|
||||
'https://1.2.3.4/api/v1/ports/1',
|
||||
False, jsonutils.dumps({'name': 'a-new-name'}),
|
||||
client.JSONRESTClient._DEFAULT_HEADERS,
|
||||
nsxlib_testcase.NSX_CERT)
|
||||
assert_json_call(
|
||||
'put', api,
|
||||
'https://1.2.3.4/api/v1/ports/1',
|
||||
data=jsonutils.dumps({'name': 'a-new-name'}))
|
||||
|
||||
def test_delete_resource(self):
|
||||
api = self.new_client(client.NSX3Client)
|
||||
with self.mocked_client(api) as mocked:
|
||||
client.delete_resource('ports/11', client=api)
|
||||
api = self.new_mocked_client(client.NSX3Client)
|
||||
client.delete_resource('ports/11', client=api)
|
||||
|
||||
assert_session_call(
|
||||
mocked.get('delete'),
|
||||
'https://1.2.3.4/api/v1/ports/11',
|
||||
False, None, client.JSONRESTClient._DEFAULT_HEADERS,
|
||||
nsxlib_testcase.NSX_CERT)
|
||||
assert_json_call(
|
||||
'delete', api,
|
||||
'https://1.2.3.4/api/v1/ports/11')
|
||||
|
172
vmware_nsx/tests/unit/nsxlib/v3/test_cluster.py
Normal file
172
vmware_nsx/tests/unit/nsxlib/v3/test_cluster.py
Normal file
@ -0,0 +1,172 @@
|
||||
# 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 urlparse
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_serialization import jsonutils
|
||||
from vmware_nsx.common import exceptions as nsx_exc
|
||||
from vmware_nsx.nsxlib.v3 import client
|
||||
from vmware_nsx.nsxlib.v3 import cluster
|
||||
from vmware_nsx.tests.unit.nsx_v3 import mocks
|
||||
from vmware_nsx.tests.unit.nsxlib.v3 import nsxlib_testcase
|
||||
|
||||
|
||||
def _validate_conn_up(*args, **kwargs):
|
||||
return
|
||||
|
||||
|
||||
def _validate_conn_down(*args, **kwargs):
|
||||
raise Exception()
|
||||
|
||||
|
||||
class RequestsHTTPProviderTestCase(nsxlib_testcase.NsxClientTestCase):
|
||||
|
||||
def test_new_connection(self):
|
||||
mock_api = mock.Mock()
|
||||
mock_api.username = 'nsxuser'
|
||||
mock_api.password = 'nsxpassword'
|
||||
mock_api.retries = 100
|
||||
mock_api.insecure = True
|
||||
mock_api.ca_file = None
|
||||
mock_api.http_timeout = 99
|
||||
mock_api.conn_idle_timeout = 39
|
||||
provider = cluster.NSXRequestsHTTPProvider()
|
||||
session = provider.new_connection(
|
||||
mock_api, cluster.Provider('9.8.7.6', 'https://9.8.7.6'))
|
||||
|
||||
self.assertEqual(session.auth, ('nsxuser', 'nsxpassword'))
|
||||
self.assertEqual(session.verify, False)
|
||||
self.assertEqual(session.cert, None)
|
||||
self.assertEqual(session.adapters['https://'].max_retries.total, 100)
|
||||
self.assertEqual(session.timeout, 99)
|
||||
|
||||
def test_validate_connection(self):
|
||||
mock_conn = mocks.MockRequestSessionApi()
|
||||
mock_ep = mock.Mock()
|
||||
mock_ep.provider.url = 'https://1.2.3.4'
|
||||
provider = cluster.NSXRequestsHTTPProvider()
|
||||
self.assertRaises(nsx_exc.ResourceNotFound,
|
||||
provider.validate_connection,
|
||||
mock.Mock(), mock_ep, mock_conn)
|
||||
|
||||
mock_conn.post('api/v1/transport-zones',
|
||||
data=jsonutils.dumps({'id': 'dummy-tz'}),
|
||||
headers=client.JSONRESTClient._DEFAULT_HEADERS)
|
||||
provider.validate_connection(mock.Mock(), mock_ep, mock_conn)
|
||||
|
||||
|
||||
class NsxV3ClusteredAPITestCase(nsxlib_testcase.NsxClientTestCase):
|
||||
|
||||
def _assert_providers(self, cluster_api, provider_tuples):
|
||||
self.assertEqual(len(cluster_api.providers), len(provider_tuples))
|
||||
|
||||
def _assert_provider(pid, purl):
|
||||
for provider in cluster_api.providers:
|
||||
if provider.id == pid and provider.url == purl:
|
||||
return
|
||||
self.fail("Provider: %s not found" % pid)
|
||||
|
||||
for provider_tuple in provider_tuples:
|
||||
_assert_provider(provider_tuple[0], provider_tuple[1])
|
||||
|
||||
def test_conf_providers_no_scheme(self):
|
||||
conf_managers = ['8.9.10.11', '9.10.11.12:4433']
|
||||
cfg.CONF.set_override(
|
||||
'nsx_managers', conf_managers, 'nsx_v3')
|
||||
|
||||
mock_provider = mock.Mock()
|
||||
mock_provider.default_scheme = 'https'
|
||||
mock_provider.validate_connection = _validate_conn_up
|
||||
|
||||
api = cluster.NSXClusteredAPI(http_provider=mock_provider)
|
||||
|
||||
self._assert_providers(
|
||||
api, [(p, "https://%s" % p) for p in conf_managers])
|
||||
|
||||
def test_conf_providers_with_scheme(self):
|
||||
conf_managers = ['http://8.9.10.11:8080', 'https://9.10.11.12:4433']
|
||||
cfg.CONF.set_override(
|
||||
'nsx_managers', conf_managers, 'nsx_v3')
|
||||
|
||||
mock_provider = mock.Mock()
|
||||
mock_provider.default_scheme = 'https'
|
||||
mock_provider.validate_connection = _validate_conn_up
|
||||
|
||||
api = cluster.NSXClusteredAPI(http_provider=mock_provider)
|
||||
|
||||
self._assert_providers(
|
||||
api, [(urlparse.urlparse(p).netloc, p) for p in conf_managers])
|
||||
|
||||
def test_conns_per_pool(self):
|
||||
cfg.CONF.set_override(
|
||||
'concurrent_connections', 11, 'nsx_v3')
|
||||
conf_managers = ['8.9.10.11', '9.10.11.12:4433']
|
||||
cfg.CONF.set_override(
|
||||
'nsx_managers', conf_managers, 'nsx_v3')
|
||||
|
||||
mock_provider = mock.Mock()
|
||||
mock_provider.default_scheme = 'https'
|
||||
mock_provider.validate_connection = _validate_conn_up
|
||||
|
||||
api = cluster.NSXClusteredAPI(http_provider=mock_provider)
|
||||
|
||||
for ep_id, ep in api.endpoints.items():
|
||||
self.assertEqual(ep.pool.max_size, 11)
|
||||
|
||||
|
||||
class ClusteredAPITestCase(nsxlib_testcase.NsxClientTestCase):
|
||||
|
||||
def _test_health(self, validate_fn, expected_health):
|
||||
conf_managers = ['8.9.10.11', '9.10.11.12']
|
||||
cfg.CONF.set_override(
|
||||
'nsx_managers', conf_managers, 'nsx_v3')
|
||||
|
||||
mock_provider = mock.Mock()
|
||||
mock_provider.default_scheme = 'https'
|
||||
|
||||
mock_provider.validate_connection = validate_fn
|
||||
api = cluster.NSXClusteredAPI(http_provider=mock_provider)
|
||||
self.assertEqual(api.health, expected_health)
|
||||
|
||||
def test_orange_health(self):
|
||||
|
||||
def _validate(cluster_api, endpoint, conn):
|
||||
if endpoint.provider.id == '8.9.10.11':
|
||||
raise Exception()
|
||||
|
||||
self._test_health(_validate, cluster.ClusterHealth.ORANGE)
|
||||
|
||||
def test_green_health(self):
|
||||
self._test_health(_validate_conn_up, cluster.ClusterHealth.GREEN)
|
||||
|
||||
def test_red_health(self):
|
||||
self._test_health(_validate_conn_down, cluster.ClusterHealth.RED)
|
||||
|
||||
def test_cluster_unavailable(self):
|
||||
conf_managers = ['8.9.10.11', '9.10.11.12', '10.11.12.13']
|
||||
cfg.CONF.set_override(
|
||||
'nsx_managers', conf_managers, 'nsx_v3')
|
||||
|
||||
mock_provider = mock.Mock()
|
||||
mock_provider.default_scheme = 'https'
|
||||
|
||||
mock_provider.validate_connection = _validate_conn_down
|
||||
api = cluster.NSXClusteredAPI(http_provider=mock_provider)
|
||||
|
||||
self.assertEqual(len(api.endpoints), 3)
|
||||
self.assertRaises(nsx_exc.ServiceClusterUnavailable,
|
||||
api.get, 'api/v1/transport-zones')
|
@ -17,13 +17,11 @@ from oslo_log import log
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
from vmware_nsx.nsxlib import v3 as nsxlib
|
||||
from vmware_nsx.nsxlib.v3 import client
|
||||
from vmware_nsx.tests.unit.nsx_v3 import test_constants as test_constants_v3
|
||||
from vmware_nsx.tests.unit.nsxlib.v3 import nsxlib_testcase
|
||||
from vmware_nsx.tests.unit.nsxlib.v3 import test_client
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
_JSON_HEADERS = client.JSONRESTClient._DEFAULT_HEADERS
|
||||
|
||||
|
||||
class NsxLibQosTestCase(nsxlib_testcase.NsxClientTestCase):
|
||||
@ -47,54 +45,46 @@ class NsxLibQosTestCase(nsxlib_testcase.NsxClientTestCase):
|
||||
"""
|
||||
Test creating a qos-switching profile returns the correct response
|
||||
"""
|
||||
api = self.new_client(client.NSX3Client)
|
||||
with self.mocked_client_bridge(api, nsxlib, 'client') as mocked:
|
||||
nsxlib.create_qos_switching_profile(
|
||||
qos_marking="untrusted", dscp=25, tags=[],
|
||||
name=test_constants_v3.FAKE_NAME,
|
||||
description=test_constants_v3.FAKE_NAME)
|
||||
api = self.mocked_rest_fns(nsxlib, 'client')
|
||||
|
||||
test_client.assert_session_call(
|
||||
mocked.get('post'),
|
||||
'https://1.2.3.4/api/v1/switching-profiles',
|
||||
False,
|
||||
jsonutils.dumps(self._body(qos_marking='UNTRUSTED', dscp=25),
|
||||
sort_keys=True),
|
||||
_JSON_HEADERS,
|
||||
nsxlib_testcase.NSX_CERT)
|
||||
nsxlib.create_qos_switching_profile(
|
||||
qos_marking="untrusted", dscp=25, tags=[],
|
||||
name=test_constants_v3.FAKE_NAME,
|
||||
description=test_constants_v3.FAKE_NAME)
|
||||
|
||||
test_client.assert_json_call(
|
||||
'post', api,
|
||||
'https://1.2.3.4/api/v1/switching-profiles',
|
||||
data=jsonutils.dumps(self._body(qos_marking='UNTRUSTED', dscp=25),
|
||||
sort_keys=True))
|
||||
|
||||
def test_create_qos_switching_profile_trusted(self):
|
||||
"""
|
||||
Test creating a qos-switching profile returns the correct response
|
||||
"""
|
||||
api = self.new_client(client.NSX3Client)
|
||||
with self.mocked_client_bridge(api, nsxlib, 'client') as mocked:
|
||||
nsxlib.create_qos_switching_profile(
|
||||
qos_marking="trusted", dscp=0, tags=[],
|
||||
name=test_constants_v3.FAKE_NAME,
|
||||
description=test_constants_v3.FAKE_NAME)
|
||||
api = self.mocked_rest_fns(nsxlib, 'client')
|
||||
|
||||
test_client.assert_session_call(
|
||||
mocked.get('post'),
|
||||
'https://1.2.3.4/api/v1/switching-profiles',
|
||||
False,
|
||||
jsonutils.dumps(self._body(qos_marking='trusted', dscp=0),
|
||||
sort_keys=True),
|
||||
_JSON_HEADERS,
|
||||
nsxlib_testcase.NSX_CERT)
|
||||
nsxlib.create_qos_switching_profile(
|
||||
qos_marking="trusted", dscp=0, tags=[],
|
||||
name=test_constants_v3.FAKE_NAME,
|
||||
description=test_constants_v3.FAKE_NAME)
|
||||
|
||||
test_client.assert_json_call(
|
||||
'post', api,
|
||||
'https://1.2.3.4/api/v1/switching-profiles',
|
||||
data=jsonutils.dumps(self._body(qos_marking='trusted', dscp=0),
|
||||
sort_keys=True))
|
||||
|
||||
def test_delete_qos_switching_profile(self):
|
||||
"""
|
||||
Test deleting qos-switching-profile
|
||||
"""
|
||||
api = self.new_client(client.NSX3Client)
|
||||
with self.mocked_client_bridge(api, nsxlib, 'client') as mocked:
|
||||
nsxlib.delete_qos_switching_profile(
|
||||
test_constants_v3.FAKE_QOS_PROFILE['id'])
|
||||
test_client.assert_session_call(
|
||||
mocked.get('delete'),
|
||||
'https://1.2.3.4/api/v1/switching-profiles/%s'
|
||||
% test_constants_v3.FAKE_QOS_PROFILE['id'],
|
||||
False, None,
|
||||
_JSON_HEADERS,
|
||||
nsxlib_testcase.NSX_CERT)
|
||||
api = self.mocked_rest_fns(nsxlib, 'client')
|
||||
|
||||
nsxlib.delete_qos_switching_profile(
|
||||
test_constants_v3.FAKE_QOS_PROFILE['id'])
|
||||
|
||||
test_client.assert_json_call(
|
||||
'delete', api,
|
||||
'https://1.2.3.4/api/v1/switching-profiles/%s'
|
||||
% test_constants_v3.FAKE_QOS_PROFILE['id'])
|
||||
|
@ -17,7 +17,6 @@ import mock
|
||||
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
from vmware_nsx.nsxlib.v3 import client
|
||||
from vmware_nsx.nsxlib.v3 import resources
|
||||
from vmware_nsx.tests.unit.nsx_v3 import mocks
|
||||
from vmware_nsx.tests.unit.nsx_v3 import test_constants as test_constants_v3
|
||||
@ -31,22 +30,24 @@ profile_types = resources.SwitchingProfileTypes
|
||||
|
||||
class TestSwitchingProfileTestCase(nsxlib_testcase.NsxClientTestCase):
|
||||
|
||||
def test_switching_profile_create(self):
|
||||
api = resources.SwitchingProfile(client.NSX3Client())
|
||||
with self.mocked_resource(api) as mocked:
|
||||
api.create(profile_types.PORT_MIRRORING,
|
||||
'pm-profile', 'port mirror prof')
|
||||
def _mocked_switching_profile(self, session_response=None):
|
||||
return self.mocked_resource(
|
||||
resources.SwitchingProfile, session_response=session_response)
|
||||
|
||||
test_client.assert_session_call(
|
||||
mocked.get('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'
|
||||
}, sort_keys=True),
|
||||
client.JSONRESTClient._DEFAULT_HEADERS,
|
||||
nsxlib_testcase.NSX_CERT)
|
||||
def test_switching_profile_create(self):
|
||||
mocked_resource = self._mocked_switching_profile()
|
||||
|
||||
mocked_resource.create(profile_types.PORT_MIRRORING,
|
||||
'pm-profile', 'port mirror prof')
|
||||
|
||||
test_client.assert_json_call(
|
||||
'post', mocked_resource,
|
||||
'https://1.2.3.4/api/v1/switching-profiles',
|
||||
data=jsonutils.dumps({
|
||||
'resource_type': profile_types.PORT_MIRRORING,
|
||||
'display_name': 'pm-profile',
|
||||
'description': 'port mirror prof'
|
||||
}, sort_keys=True))
|
||||
|
||||
def test_switching_profile_update(self):
|
||||
|
||||
@ -61,19 +62,18 @@ class TestSwitchingProfileTestCase(nsxlib_testcase.NsxClientTestCase):
|
||||
}
|
||||
]
|
||||
|
||||
api = resources.SwitchingProfile(client.NSX3Client())
|
||||
with self.mocked_resource(api) as mocked:
|
||||
api.update('a12bc1', profile_types.PORT_MIRRORING, tags=tags)
|
||||
mocked_resource = self._mocked_switching_profile()
|
||||
|
||||
test_client.assert_session_call(
|
||||
mocked.get('put'),
|
||||
'https://1.2.3.4/api/v1/switching-profiles/a12bc1',
|
||||
False, jsonutils.dumps({
|
||||
'resource_type': profile_types.PORT_MIRRORING,
|
||||
'tags': tags
|
||||
}, sort_keys=True),
|
||||
client.JSONRESTClient._DEFAULT_HEADERS,
|
||||
nsxlib_testcase.NSX_CERT)
|
||||
mocked_resource.update(
|
||||
'a12bc1', profile_types.PORT_MIRRORING, tags=tags)
|
||||
|
||||
test_client.assert_json_call(
|
||||
'put', mocked_resource,
|
||||
'https://1.2.3.4/api/v1/switching-profiles/a12bc1',
|
||||
data=jsonutils.dumps({
|
||||
'resource_type': profile_types.PORT_MIRRORING,
|
||||
'tags': tags
|
||||
}, sort_keys=True))
|
||||
|
||||
def test_spoofgaurd_profile_create(self):
|
||||
|
||||
@ -88,25 +88,22 @@ class TestSwitchingProfileTestCase(nsxlib_testcase.NsxClientTestCase):
|
||||
}
|
||||
]
|
||||
|
||||
api = resources.SwitchingProfile(client.NSX3Client())
|
||||
with self.mocked_resource(api) as mocked:
|
||||
api.create_spoofguard_profile(
|
||||
'neutron-spoof', 'spoofguard-for-neutron',
|
||||
whitelist_ports=True, tags=tags)
|
||||
mocked_resource = self._mocked_switching_profile()
|
||||
|
||||
test_client.assert_session_call(
|
||||
mocked.get('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
|
||||
}, sort_keys=True),
|
||||
client.JSONRESTClient._DEFAULT_HEADERS,
|
||||
nsxlib_testcase.NSX_CERT)
|
||||
mocked_resource.create_spoofguard_profile(
|
||||
'neutron-spoof', 'spoofguard-for-neutron',
|
||||
whitelist_ports=True, tags=tags)
|
||||
|
||||
test_client.assert_json_call(
|
||||
'post', mocked_resource,
|
||||
'https://1.2.3.4/api/v1/switching-profiles',
|
||||
data=jsonutils.dumps({
|
||||
'resource_type': profile_types.SPOOF_GUARD,
|
||||
'display_name': 'neutron-spoof',
|
||||
'description': 'spoofguard-for-neutron',
|
||||
'white_list_providers': ['LPORT_BINDINGS'],
|
||||
'tags': tags
|
||||
}, sort_keys=True))
|
||||
|
||||
def test_create_dhcp_profile(self):
|
||||
|
||||
@ -121,39 +118,36 @@ class TestSwitchingProfileTestCase(nsxlib_testcase.NsxClientTestCase):
|
||||
}
|
||||
]
|
||||
|
||||
api = resources.SwitchingProfile(client.NSX3Client())
|
||||
with self.mocked_resource(api) as mocked:
|
||||
api.create_dhcp_profile(
|
||||
'neutron-dhcp', 'dhcp-for-neutron',
|
||||
tags=tags)
|
||||
mocked_resource = self._mocked_switching_profile()
|
||||
|
||||
test_client.assert_session_call(
|
||||
mocked.get('post'),
|
||||
'https://1.2.3.4/api/v1/switching-profiles',
|
||||
False,
|
||||
jsonutils.dumps({
|
||||
'bpdu_filter': {
|
||||
'enabled': False,
|
||||
'white_list': []
|
||||
},
|
||||
'resource_type': profile_types.SWITCH_SECURITY,
|
||||
'display_name': 'neutron-dhcp',
|
||||
'description': 'dhcp-for-neutron',
|
||||
'tags': tags,
|
||||
'dhcp_filter': {
|
||||
'client_block_enabled': False,
|
||||
'server_block_enabled': False
|
||||
},
|
||||
'rate_limits': {
|
||||
'enabled': False,
|
||||
'rx_broadcast': 0,
|
||||
'tx_broadcast': 0,
|
||||
'rx_multicast': 0,
|
||||
'tx_multicast': 0
|
||||
}
|
||||
}, sort_keys=True),
|
||||
client.JSONRESTClient._DEFAULT_HEADERS,
|
||||
nsxlib_testcase.NSX_CERT)
|
||||
mocked_resource.create_dhcp_profile(
|
||||
'neutron-dhcp', 'dhcp-for-neutron',
|
||||
tags=tags)
|
||||
|
||||
test_client.assert_json_call(
|
||||
'post', mocked_resource,
|
||||
'https://1.2.3.4/api/v1/switching-profiles',
|
||||
data=jsonutils.dumps({
|
||||
'bpdu_filter': {
|
||||
'enabled': False,
|
||||
'white_list': []
|
||||
},
|
||||
'resource_type': profile_types.SWITCH_SECURITY,
|
||||
'display_name': 'neutron-dhcp',
|
||||
'description': 'dhcp-for-neutron',
|
||||
'tags': tags,
|
||||
'dhcp_filter': {
|
||||
'client_block_enabled': False,
|
||||
'server_block_enabled': False
|
||||
},
|
||||
'rate_limits': {
|
||||
'enabled': False,
|
||||
'rx_broadcast': 0,
|
||||
'tx_broadcast': 0,
|
||||
'rx_multicast': 0,
|
||||
'tx_multicast': 0
|
||||
}
|
||||
}, sort_keys=True))
|
||||
|
||||
def test_find_by_display_name(self):
|
||||
resp_resources = {
|
||||
@ -163,35 +157,39 @@ class TestSwitchingProfileTestCase(nsxlib_testcase.NsxClientTestCase):
|
||||
{'display_name': 'resource-3'}
|
||||
]
|
||||
}
|
||||
api = resources.SwitchingProfile(client.NSX3Client())
|
||||
with self.mocked_resource(api) as mocked:
|
||||
mock_get = mocked.get('get')
|
||||
mock_get.return_value = mocks.MockRequestsResponse(
|
||||
200, jsonutils.dumps(resp_resources))
|
||||
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'))
|
||||
session_response = mocks.MockRequestsResponse(
|
||||
200, jsonutils.dumps(resp_resources))
|
||||
mocked_resource = self._mocked_switching_profile(
|
||||
session_response=session_response)
|
||||
|
||||
mock_get.reset_mock()
|
||||
self.assertEqual([{'display_name': 'resource-1'}],
|
||||
mocked_resource.find_by_display_name('resource-1'))
|
||||
self.assertEqual([{'display_name': 'resource-2'}],
|
||||
mocked_resource.find_by_display_name('resource-2'))
|
||||
self.assertEqual([{'display_name': 'resource-3'}],
|
||||
mocked_resource.find_by_display_name('resource-3'))
|
||||
|
||||
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))
|
||||
self.assertEqual(resp_resources['results'],
|
||||
api.find_by_display_name('resource-1'))
|
||||
resp_resources = {
|
||||
'results': [
|
||||
{'display_name': 'resource-1'},
|
||||
{'display_name': 'resource-1'},
|
||||
{'display_name': 'resource-1'}
|
||||
]
|
||||
}
|
||||
session_response = mocks.MockRequestsResponse(
|
||||
200, jsonutils.dumps(resp_resources))
|
||||
mocked_resource = self._mocked_switching_profile(
|
||||
session_response=session_response)
|
||||
self.assertEqual(resp_resources['results'],
|
||||
mocked_resource.find_by_display_name('resource-1'))
|
||||
|
||||
|
||||
class LogicalPortTestCase(nsxlib_testcase.NsxClientTestCase):
|
||||
|
||||
def _mocked_lport(self, session_response=None):
|
||||
return self.mocked_resource(
|
||||
resources.LogicalPort, session_response=session_response)
|
||||
|
||||
def test_create_logical_port(self):
|
||||
"""
|
||||
Test creating a port returns the correct response and 200 status
|
||||
@ -217,39 +215,31 @@ class LogicalPortTestCase(nsxlib_testcase.NsxClientTestCase):
|
||||
|
||||
fake_port['address_bindings'] = binding_repr
|
||||
|
||||
api = resources.LogicalPort(client.NSX3Client())
|
||||
with self.mocked_resource(api) as mocked:
|
||||
mocked_resource = self._mocked_lport()
|
||||
|
||||
mocked.get('post').return_value = mocks.MockRequestsResponse(
|
||||
200, jsonutils.dumps(fake_port))
|
||||
switch_profile = resources.SwitchingProfile
|
||||
mocked_resource.create(
|
||||
fake_port['logical_switch_id'],
|
||||
fake_port['attachment']['id'],
|
||||
address_bindings=pkt_classifiers,
|
||||
switch_profile_ids=switch_profile.build_switch_profile_ids(
|
||||
mock.Mock(), *profile_dicts))
|
||||
|
||||
switch_profile = resources.SwitchingProfile
|
||||
result = api.create(
|
||||
fake_port['logical_switch_id'],
|
||||
fake_port['attachment']['id'],
|
||||
address_bindings=pkt_classifiers,
|
||||
switch_profile_ids=switch_profile.build_switch_profile_ids(
|
||||
mock.Mock(), *profile_dicts))
|
||||
resp_body = {
|
||||
'logical_switch_id': fake_port['logical_switch_id'],
|
||||
'switching_profile_ids': fake_port['switching_profile_ids'],
|
||||
'attachment': {
|
||||
'attachment_type': 'VIF',
|
||||
'id': fake_port['attachment']['id']
|
||||
},
|
||||
'admin_state': 'UP',
|
||||
'address_bindings': fake_port['address_bindings']
|
||||
}
|
||||
|
||||
resp_body = {
|
||||
'logical_switch_id': fake_port['logical_switch_id'],
|
||||
'switching_profile_ids': fake_port['switching_profile_ids'],
|
||||
'attachment': {
|
||||
'attachment_type': 'VIF',
|
||||
'id': fake_port['attachment']['id']
|
||||
},
|
||||
'admin_state': 'UP',
|
||||
'address_bindings': fake_port['address_bindings']
|
||||
}
|
||||
|
||||
self.assertEqual(fake_port, result)
|
||||
test_client.assert_session_call(
|
||||
mocked.get('post'),
|
||||
'https://1.2.3.4/api/v1/logical-ports',
|
||||
False,
|
||||
jsonutils.dumps(resp_body, sort_keys=True),
|
||||
client.JSONRESTClient._DEFAULT_HEADERS,
|
||||
nsxlib_testcase.NSX_CERT)
|
||||
test_client.assert_json_call(
|
||||
'post', mocked_resource,
|
||||
'https://1.2.3.4/api/v1/logical-ports',
|
||||
data=jsonutils.dumps(resp_body, sort_keys=True))
|
||||
|
||||
def test_create_logical_port_admin_down(self):
|
||||
"""
|
||||
@ -257,144 +247,112 @@ class LogicalPortTestCase(nsxlib_testcase.NsxClientTestCase):
|
||||
"""
|
||||
fake_port = test_constants_v3.FAKE_PORT
|
||||
fake_port['admin_state'] = "DOWN"
|
||||
api = resources.LogicalPort(client.NSX3Client())
|
||||
with self.mocked_resource(api) as mocked:
|
||||
mocked.get('post').return_value = mocks.MockRequestsResponse(
|
||||
200, jsonutils.dumps(fake_port))
|
||||
|
||||
result = api.create(
|
||||
test_constants_v3.FAKE_PORT['logical_switch_id'],
|
||||
test_constants_v3.FAKE_PORT['attachment']['id'],
|
||||
tags={}, admin_state=False)
|
||||
mocked_resource = self._mocked_lport(
|
||||
session_response=mocks.MockRequestsResponse(
|
||||
200, jsonutils.dumps(fake_port)))
|
||||
|
||||
self.assertEqual(fake_port, result)
|
||||
result = mocked_resource.create(
|
||||
test_constants_v3.FAKE_PORT['logical_switch_id'],
|
||||
test_constants_v3.FAKE_PORT['attachment']['id'],
|
||||
tags={}, admin_state=False)
|
||||
|
||||
self.assertEqual(fake_port, result)
|
||||
|
||||
def test_delete_logical_port(self):
|
||||
"""
|
||||
Test deleting port
|
||||
"""
|
||||
api = resources.LogicalPort(client.NSX3Client())
|
||||
with self.mocked_resource(api) as mocked:
|
||||
mocked.get('delete').return_value = mocks.MockRequestsResponse(
|
||||
200, None)
|
||||
mocked_resource = self._mocked_lport()
|
||||
|
||||
uuid = test_constants_v3.FAKE_PORT['id']
|
||||
result = api.delete(uuid)
|
||||
self.assertIsNone(result.content)
|
||||
test_client.assert_session_call(
|
||||
mocked.get('delete'),
|
||||
'https://1.2.3.4/api/v1/logical-ports/%s?detach=true' % uuid,
|
||||
False,
|
||||
None,
|
||||
client.JSONRESTClient._DEFAULT_HEADERS,
|
||||
nsxlib_testcase.NSX_CERT)
|
||||
uuid = test_constants_v3.FAKE_PORT['id']
|
||||
mocked_resource.delete(uuid)
|
||||
test_client.assert_json_call(
|
||||
'delete', mocked_resource,
|
||||
'https://1.2.3.4/api/v1/logical-ports/%s?detach=true' % uuid)
|
||||
|
||||
|
||||
class LogicalRouterTestCase(nsxlib_testcase.NsxClientTestCase):
|
||||
|
||||
def _mocked_lrouter(self, session_response=None):
|
||||
return self.mocked_resource(
|
||||
resources.LogicalRouter, session_response=session_response)
|
||||
|
||||
def test_create_logical_router(self):
|
||||
"""
|
||||
Test creating a router returns the correct response and 201 status
|
||||
"""
|
||||
fake_router = test_constants_v3.FAKE_ROUTER.copy()
|
||||
|
||||
api = resources.LogicalRouter(client.NSX3Client())
|
||||
with self.mocked_resource(api) as mocked:
|
||||
mocked.get('post').return_value = mocks.MockRequestsResponse(
|
||||
201, jsonutils.dumps(fake_router))
|
||||
router = self._mocked_lrouter()
|
||||
|
||||
tier0_router = True
|
||||
result = api.create(fake_router['display_name'], None, None,
|
||||
tier0_router)
|
||||
tier0_router = True
|
||||
router.create(fake_router['display_name'], None, None, tier0_router)
|
||||
|
||||
data = {
|
||||
'display_name': fake_router['display_name'],
|
||||
'router_type': 'TIER0' if tier0_router else 'TIER1',
|
||||
'tags': None
|
||||
}
|
||||
data = {
|
||||
'display_name': fake_router['display_name'],
|
||||
'router_type': 'TIER0' if tier0_router else 'TIER1',
|
||||
'tags': None
|
||||
}
|
||||
|
||||
self.assertEqual(fake_router, result)
|
||||
test_client.assert_session_call(
|
||||
mocked.get('post'),
|
||||
'https://1.2.3.4/api/v1/logical-routers',
|
||||
False,
|
||||
jsonutils.dumps(data, sort_keys=True),
|
||||
client.JSONRESTClient._DEFAULT_HEADERS,
|
||||
nsxlib_testcase.NSX_CERT)
|
||||
test_client.assert_json_call(
|
||||
'post', router,
|
||||
'https://1.2.3.4/api/v1/logical-routers',
|
||||
data=jsonutils.dumps(data, sort_keys=True))
|
||||
|
||||
def test_delete_logical_router(self):
|
||||
"""
|
||||
Test deleting router
|
||||
"""
|
||||
api = resources.LogicalRouter(client.NSX3Client())
|
||||
with self.mocked_resource(api) as mocked:
|
||||
mocked.get('delete').return_value = mocks.MockRequestsResponse(
|
||||
200, None)
|
||||
|
||||
uuid = test_constants_v3.FAKE_ROUTER['id']
|
||||
result = api.delete(uuid)
|
||||
self.assertIsNone(result.content)
|
||||
test_client.assert_session_call(
|
||||
mocked.get('delete'),
|
||||
'https://1.2.3.4/api/v1/logical-routers/%s' % uuid,
|
||||
False,
|
||||
None,
|
||||
client.JSONRESTClient._DEFAULT_HEADERS,
|
||||
nsxlib_testcase.NSX_CERT)
|
||||
router = self._mocked_lrouter()
|
||||
uuid = test_constants_v3.FAKE_ROUTER['id']
|
||||
router.delete(uuid)
|
||||
test_client.assert_json_call(
|
||||
'delete', router,
|
||||
'https://1.2.3.4/api/v1/logical-routers/%s' % uuid)
|
||||
|
||||
|
||||
class LogicalRouterPortTestCase(nsxlib_testcase.NsxClientTestCase):
|
||||
|
||||
def _mocked_lrport(self, session_response=None):
|
||||
return self.mocked_resource(
|
||||
resources.LogicalRouterPort, session_response=session_response)
|
||||
|
||||
def test_create_logical_router_port(self):
|
||||
"""
|
||||
Test creating a router port returns the correct response and 201 status
|
||||
"""
|
||||
fake_router_port = test_constants_v3.FAKE_ROUTER_PORT.copy()
|
||||
|
||||
api = resources.LogicalRouterPort(client.NSX3Client())
|
||||
with self.mocked_resource(api) as mocked:
|
||||
mocked.get('post').return_value = mocks.MockRequestsResponse(
|
||||
201, jsonutils.dumps(fake_router_port))
|
||||
lrport = self._mocked_lrport()
|
||||
|
||||
result = api.create(fake_router_port['logical_router_id'],
|
||||
fake_router_port['display_name'],
|
||||
fake_router_port['resource_type'],
|
||||
None, None, None)
|
||||
lrport.create(fake_router_port['logical_router_id'],
|
||||
fake_router_port['display_name'],
|
||||
fake_router_port['resource_type'],
|
||||
None, None, None)
|
||||
|
||||
data = {
|
||||
'display_name': fake_router_port['display_name'],
|
||||
'logical_router_id': fake_router_port['logical_router_id'],
|
||||
'resource_type': fake_router_port['resource_type']
|
||||
}
|
||||
data = {
|
||||
'display_name': fake_router_port['display_name'],
|
||||
'logical_router_id': fake_router_port['logical_router_id'],
|
||||
'resource_type': fake_router_port['resource_type']
|
||||
}
|
||||
|
||||
self.assertEqual(fake_router_port, result)
|
||||
test_client.assert_session_call(
|
||||
mocked.get('post'),
|
||||
'https://1.2.3.4/api/v1/logical-router-ports',
|
||||
False,
|
||||
jsonutils.dumps(data, sort_keys=True),
|
||||
client.JSONRESTClient._DEFAULT_HEADERS,
|
||||
nsxlib_testcase.NSX_CERT)
|
||||
test_client.assert_json_call(
|
||||
'post', lrport,
|
||||
'https://1.2.3.4/api/v1/logical-router-ports',
|
||||
data=jsonutils.dumps(data, sort_keys=True))
|
||||
|
||||
def test_delete_logical_router_port(self):
|
||||
"""
|
||||
Test deleting router port
|
||||
"""
|
||||
api = resources.LogicalRouterPort(client.NSX3Client())
|
||||
with self.mocked_resource(api) as mocked:
|
||||
mocked.get('delete').return_value = mocks.MockRequestsResponse(
|
||||
200, None)
|
||||
lrport = self._mocked_lrport()
|
||||
|
||||
uuid = test_constants_v3.FAKE_ROUTER_PORT['id']
|
||||
result = api.delete(uuid)
|
||||
self.assertIsNone(result.content)
|
||||
test_client.assert_session_call(
|
||||
mocked.get('delete'),
|
||||
'https://1.2.3.4/api/v1/logical-router-ports/%s' % uuid,
|
||||
False,
|
||||
None,
|
||||
client.JSONRESTClient._DEFAULT_HEADERS,
|
||||
nsxlib_testcase.NSX_CERT)
|
||||
uuid = test_constants_v3.FAKE_ROUTER_PORT['id']
|
||||
lrport.delete(uuid)
|
||||
test_client.assert_json_call(
|
||||
'delete', lrport,
|
||||
'https://1.2.3.4/api/v1/logical-router-ports/%s' % uuid)
|
||||
|
||||
def test_get_logical_router_port_by_router_id(self):
|
||||
"""
|
||||
@ -403,22 +361,17 @@ class LogicalRouterPortTestCase(nsxlib_testcase.NsxClientTestCase):
|
||||
fake_router_port = test_constants_v3.FAKE_ROUTER_PORT.copy()
|
||||
resp_resources = {'results': [fake_router_port]}
|
||||
|
||||
api = resources.LogicalRouterPort(client.NSX3Client())
|
||||
with self.mocked_resource(api) as mocked:
|
||||
mocked.get('get').return_value = mocks.MockRequestsResponse(
|
||||
200, jsonutils.dumps(resp_resources))
|
||||
lrport = self._mocked_lrport(
|
||||
session_response=mocks.MockRequestsResponse(
|
||||
200, jsonutils.dumps(resp_resources)))
|
||||
|
||||
router_id = fake_router_port['logical_router_id']
|
||||
result = api.get_by_router_id(router_id)
|
||||
self.assertEqual(fake_router_port, result[0])
|
||||
test_client.assert_session_call(
|
||||
mocked.get('get'),
|
||||
'https://1.2.3.4/api/v1/logical-router-ports/?'
|
||||
'logical_router_id=%s' % router_id,
|
||||
False,
|
||||
None,
|
||||
client.JSONRESTClient._DEFAULT_HEADERS,
|
||||
nsxlib_testcase.NSX_CERT)
|
||||
router_id = fake_router_port['logical_router_id']
|
||||
result = lrport.get_by_router_id(router_id)
|
||||
self.assertEqual(fake_router_port, result[0])
|
||||
test_client.assert_json_call(
|
||||
'get', lrport,
|
||||
'https://1.2.3.4/api/v1/logical-router-ports/?'
|
||||
'logical_router_id=%s' % router_id)
|
||||
|
||||
def test_get_logical_router_port_by_switch_id(self):
|
||||
"""
|
||||
@ -430,19 +383,13 @@ class LogicalRouterPortTestCase(nsxlib_testcase.NsxClientTestCase):
|
||||
'results': [fake_router_port]
|
||||
}
|
||||
|
||||
api = resources.LogicalRouterPort(client.NSX3Client())
|
||||
with self.mocked_resource(api) as mocked:
|
||||
mocked.get('get').return_value = mocks.MockRequestsResponse(
|
||||
200, jsonutils.dumps(resp_resources))
|
||||
lrport = self._mocked_lrport(
|
||||
session_response=mocks.MockRequestsResponse(
|
||||
200, jsonutils.dumps(resp_resources)))
|
||||
|
||||
switch_id = test_constants_v3.FAKE_SWITCH_UUID
|
||||
result = api.get_by_lswitch_id(switch_id)
|
||||
self.assertEqual(fake_router_port, result)
|
||||
test_client.assert_session_call(
|
||||
mocked.get('get'),
|
||||
'https://1.2.3.4/api/v1/logical-router-ports/?'
|
||||
'logical_switch_id=%s' % switch_id,
|
||||
False,
|
||||
None,
|
||||
client.JSONRESTClient._DEFAULT_HEADERS,
|
||||
nsxlib_testcase.NSX_CERT)
|
||||
switch_id = test_constants_v3.FAKE_SWITCH_UUID
|
||||
lrport.get_by_lswitch_id(switch_id)
|
||||
test_client.assert_json_call(
|
||||
'get', lrport,
|
||||
'https://1.2.3.4/api/v1/logical-router-ports/?'
|
||||
'logical_switch_id=%s' % switch_id)
|
||||
|
@ -46,65 +46,59 @@ class NsxLibSwitchTestCase(nsxlib_testcase.NsxClientTestCase):
|
||||
"""
|
||||
Test creating a switch returns the correct response and 200 status
|
||||
"""
|
||||
api = self.new_client(nsxlib.client.NSX3Client)
|
||||
with self.mocked_client_bridge(api, nsxlib, 'client') as mocked:
|
||||
nsxlib.create_logical_switch(
|
||||
nsx_v3_mocks.FAKE_NAME, NsxLibSwitchTestCase._tz_id, [])
|
||||
test_client.assert_session_call(
|
||||
mocked.get('post'),
|
||||
'https://1.2.3.4/api/v1/logical-switches',
|
||||
False, jsonutils.dumps(self._create_body(), sort_keys=True),
|
||||
nsxlib.client.JSONRESTClient._DEFAULT_HEADERS,
|
||||
nsxlib_testcase.NSX_CERT)
|
||||
api = self.mocked_rest_fns(nsxlib, 'client')
|
||||
|
||||
nsxlib.create_logical_switch(
|
||||
nsx_v3_mocks.FAKE_NAME, NsxLibSwitchTestCase._tz_id, [])
|
||||
|
||||
test_client.assert_json_call(
|
||||
'post', api,
|
||||
'https://1.2.3.4/api/v1/logical-switches',
|
||||
data=jsonutils.dumps(self._create_body(), sort_keys=True))
|
||||
|
||||
def test_create_logical_switch_admin_down(self):
|
||||
"""
|
||||
Test creating switch with admin_state down
|
||||
"""
|
||||
api = self.new_client(nsxlib.client.NSX3Client)
|
||||
with self.mocked_client_bridge(api, nsxlib, 'client') as mocked:
|
||||
nsxlib.create_logical_switch(
|
||||
nsx_v3_mocks.FAKE_NAME, NsxLibSwitchTestCase._tz_id,
|
||||
[], admin_state=False)
|
||||
test_client.assert_session_call(
|
||||
mocked.get('post'),
|
||||
'https://1.2.3.4/api/v1/logical-switches',
|
||||
False,
|
||||
jsonutils.dumps(self._create_body(
|
||||
admin_state=nsx_constants.ADMIN_STATE_DOWN),
|
||||
sort_keys=True),
|
||||
nsxlib.client.JSONRESTClient._DEFAULT_HEADERS,
|
||||
nsxlib_testcase.NSX_CERT)
|
||||
api = self.mocked_rest_fns(nsxlib, 'client')
|
||||
|
||||
nsxlib.create_logical_switch(
|
||||
nsx_v3_mocks.FAKE_NAME, NsxLibSwitchTestCase._tz_id,
|
||||
[], admin_state=False)
|
||||
|
||||
test_client.assert_json_call(
|
||||
'post', api,
|
||||
'https://1.2.3.4/api/v1/logical-switches',
|
||||
data=jsonutils.dumps(self._create_body(
|
||||
admin_state=nsx_constants.ADMIN_STATE_DOWN),
|
||||
sort_keys=True))
|
||||
|
||||
def test_create_logical_switch_vlan(self):
|
||||
"""
|
||||
Test creating switch with provider:network_type VLAN
|
||||
"""
|
||||
api = self.new_client(nsxlib.client.NSX3Client)
|
||||
with self.mocked_client_bridge(api, nsxlib, 'client') as mocked:
|
||||
nsxlib.create_logical_switch(
|
||||
nsx_v3_mocks.FAKE_NAME, NsxLibSwitchTestCase._tz_id,
|
||||
[], vlan_id='123')
|
||||
test_client.assert_session_call(
|
||||
mocked.get('post'),
|
||||
'https://1.2.3.4/api/v1/logical-switches',
|
||||
False, jsonutils.dumps(self._create_body(vlan_id='123'),
|
||||
sort_keys=True),
|
||||
nsxlib.client.JSONRESTClient._DEFAULT_HEADERS,
|
||||
nsxlib_testcase.NSX_CERT)
|
||||
api = self.mocked_rest_fns(nsxlib, 'client')
|
||||
|
||||
nsxlib.create_logical_switch(
|
||||
nsx_v3_mocks.FAKE_NAME, NsxLibSwitchTestCase._tz_id,
|
||||
[], vlan_id='123')
|
||||
|
||||
test_client.assert_json_call(
|
||||
'post', api,
|
||||
'https://1.2.3.4/api/v1/logical-switches',
|
||||
data=jsonutils.dumps(self._create_body(vlan_id='123'),
|
||||
sort_keys=True))
|
||||
|
||||
def test_delete_logical_switch(self):
|
||||
"""
|
||||
Test deleting switch
|
||||
"""
|
||||
api = self.new_client(nsxlib.client.NSX3Client)
|
||||
with self.mocked_client_bridge(api, nsxlib, 'client') as mocked:
|
||||
fake_switch = nsx_v3_mocks.make_fake_switch()
|
||||
nsxlib.delete_logical_switch(fake_switch['id'])
|
||||
test_client.assert_session_call(
|
||||
mocked.get('delete'),
|
||||
'https://1.2.3.4/api/v1/logical-switches/%s'
|
||||
'?detach=true&cascade=true' % fake_switch['id'],
|
||||
False, None,
|
||||
nsxlib.client.JSONRESTClient._DEFAULT_HEADERS,
|
||||
nsxlib_testcase.NSX_CERT)
|
||||
api = self.mocked_rest_fns(nsxlib, 'client')
|
||||
|
||||
fake_switch = nsx_v3_mocks.make_fake_switch()
|
||||
nsxlib.delete_logical_switch(fake_switch['id'])
|
||||
|
||||
test_client.assert_json_call(
|
||||
'delete', api,
|
||||
'https://1.2.3.4/api/v1/logical-switches/%s'
|
||||
'?detach=true&cascade=true' % fake_switch['id'])
|
||||
|
@ -47,9 +47,6 @@ class TestNsxV3L2GatewayDriver(test_l2gw_db.L2GWTestCase,
|
||||
|
||||
self.core_plugin = importutils.import_object(NSX_V3_PLUGIN_CLASS)
|
||||
|
||||
self.core_plugin._nsx_client = self.client
|
||||
self.core_plugin._port_client._client._session = self.mock_api
|
||||
|
||||
self.driver = nsx_v3_driver.NsxV3Driver()
|
||||
self.l2gw_plugin = l2gw_plugin.NsxL2GatewayPlugin()
|
||||
self.context = context.get_admin_context()
|
||||
|
Loading…
Reference in New Issue
Block a user