From 43ef5172abe0208046f9dd273e2c9d8154cc5dc8 Mon Sep 17 00:00:00 2001 From: Marcos Lobo Date: Thu, 23 Jan 2014 11:50:48 +0100 Subject: [PATCH 001/845] session_persistence: invalid str2dict value: u'' Can't receive empty session_persistence dict to update the vip. It would raise the following error: $ neutron lb-vip-update vip --session_persistence type=dict "" usage: neutron [--session_persistence SESSION_PERSISTENCE] neutron: error: argument --session_persistence: invalid str2dict value: u'' Change-Id: Ice43bbe19138be6af85b47c2090359d22c70b29f Closes-Bug: #1262582 --- neutronclient/common/utils.py | 2 ++ neutronclient/tests/unit/test_utils.py | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/neutronclient/common/utils.py b/neutronclient/common/utils.py index be4cd05cb..c655dc21e 100644 --- a/neutronclient/common/utils.py +++ b/neutronclient/common/utils.py @@ -149,6 +149,8 @@ def str2dict(strdict): :param strdict: key1=value1,key2=value2 ''' _info = {} + if not strdict: + return _info for kv_str in strdict.split(","): k, v = kv_str.split("=", 1) _info.update({k: v}) diff --git a/neutronclient/tests/unit/test_utils.py b/neutronclient/tests/unit/test_utils.py index 69facdcd4..ae7c93cd2 100644 --- a/neutronclient/tests/unit/test_utils.py +++ b/neutronclient/tests/unit/test_utils.py @@ -38,6 +38,14 @@ def test_string_to_dictionary(self): expected = {'key1': 'value1', 'key2': 'value2'} self.assertEqual(expected, utils.str2dict(input_str)) + def test_none_string_to_dictionary(self): + input_str = '' + expected = {} + self.assertEqual(expected, utils.str2dict(input_str)) + input_str = None + expected = {} + self.assertEqual(expected, utils.str2dict(input_str)) + def test_get_dict_item_properties(self): item = {'name': 'test_name', 'id': 'test_id'} fields = ('name', 'id') From eef61d7dc5555a62543e423d3b7954cef0306137 Mon Sep 17 00:00:00 2001 From: Sascha Peilicke Date: Tue, 19 Nov 2013 10:26:41 +0100 Subject: [PATCH 002/845] Support building wheels (PEP-427) Universal is used to identify pure-Python module(by bdist_wheel). For these, it is sufficient to build a wheel with _any_ Python ABI version and publish that to PyPI (by whatever means). Change-Id: I3dd06b63d2fdcce8961f87fe1d71be052cb0fdf0 --- setup.cfg | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup.cfg b/setup.cfg index d8224c91f..50dba19d7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -34,3 +34,6 @@ console_scripts = all_files = 1 build-dir = doc/build source-dir = doc/source + +[wheel] +universal = 1 From f8e23de0dd61bf599b33f5fcdf5ba0158bc3d74e Mon Sep 17 00:00:00 2001 From: Evgeny Fedoruk Date: Tue, 28 Jan 2014 23:57:01 -0800 Subject: [PATCH 003/845] Extending quota support neutron LBaaS entities Note: This change is a continuation of the abandoned change https://review.openstack.org/#/c/59192/ Previous change was abandoned due to rebase problem. Extending quota mechanism to support neutron LBaaS entities. Adding quota for vips, pools, members and health monitors. This is one of four changes related to the BP. This one is for python-neutronclient package. Another one is for neutron project, another for tempest project and another one for horizon/openstack-dashboard project. See blueprint neutron-quota-extension for another two changes. Change-Id: I2bfb974d513bd21b4dac6c90ddc6ede19797caf9 Implements: blueprint neutron-quota-extension --- neutronclient/neutron/v2_0/quota.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/neutronclient/neutron/v2_0/quota.py b/neutronclient/neutron/v2_0/quota.py index c449c2fda..8e85edb67 100644 --- a/neutronclient/neutron/v2_0/quota.py +++ b/neutronclient/neutron/v2_0/quota.py @@ -175,6 +175,19 @@ def get_parser(self, prog_name): parser.add_argument( '--security-group-rule', metavar='security_group_rules', help=_('The limit of security groups rules')) + parser.add_argument( + '--vip', metavar='vips', + help=_('the limit of vips')) + parser.add_argument( + '--pool', metavar='pools', + help=_('the limit of pools')) + parser.add_argument( + '--member', metavar='members', + help=_('the limit of pool members')) + parser.add_argument( + '--health-monitor', metavar='health_monitors', + help=_('the limit of health monitors')) + return parser def _validate_int(self, name, value): @@ -189,7 +202,8 @@ def _validate_int(self, name, value): def args2body(self, parsed_args): quota = {} for resource in ('network', 'subnet', 'port', 'router', 'floatingip', - 'security_group', 'security_group_rule'): + 'security_group', 'security_group_rule', + 'vip', 'pool', 'member', 'health_monitor'): if getattr(parsed_args, resource): quota[resource] = self._validate_int( resource, From ab24db61340ccfd1160e6d39066f9541a411a179 Mon Sep 17 00:00:00 2001 From: Wu Wenxiang Date: Wed, 1 Jan 2014 11:10:06 +0800 Subject: [PATCH 004/845] Remove unused imports Remove unused imports in doc/source/conf.py Change-Id: Ia21f8952b36ae74b1c2d3bfd10c0a4a04a390faa Closes-Bug: #1265266 --- doc/source/conf.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index b3afe8db9..91d218643 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -1,9 +1,6 @@ # -*- coding: utf-8 -*- # -import sys -import os - project = 'python-neutronclient' # -- General configuration --------------------------------------------- From 27d238d2954ff27b1c10f899479e54ed1ec00703 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Wed, 19 Mar 2014 10:37:08 +0900 Subject: [PATCH 005/845] Work around pypy testing issue A recent release of setuptools appears to have introduced a bug that causes the pypy gate tests to fail. This applies a temporary workaround that should restore pypy testing while the root cause of the problem is researched. The work around is suggested in the comment in bug 1290562. Change-Id: Iac2ec604447b1638550219cd2e407daf188f8ede Partial-Bug: 1290562 --- tox.ini | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tox.ini b/tox.ini index a498e5261..65630888b 100644 --- a/tox.ini +++ b/tox.ini @@ -14,6 +14,11 @@ deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt commands = python setup.py testr --testr-args='{posargs}' +[testenv:pypy] +deps = setuptools<3.2 + -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt + [testenv:pep8] commands = flake8 distribute = false From a9f6ce4066eb523250d45689356f5b3dd5f93195 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Sun, 23 Mar 2014 05:45:35 +0900 Subject: [PATCH 006/845] Suppress stdout/stderr in test_shell test_shell is refactored to check stderr in addition to stdout and suppress stdout/stderr. In addition, test_auth tries API call to keystone, so related methods are mocked out now to avoid it. Change-Id: Id43ad905c5c13da9015d035267eaa86429e60ff3 Closes-Bug: #1161849 --- neutronclient/tests/unit/test_shell.py | 77 +++++++++++++++----------- 1 file changed, 45 insertions(+), 32 deletions(-) diff --git a/neutronclient/tests/unit/test_shell.py b/neutronclient/tests/unit/test_shell.py index 23f1d2dfc..e5fe54252 100644 --- a/neutronclient/tests/unit/test_shell.py +++ b/neutronclient/tests/unit/test_shell.py @@ -15,6 +15,7 @@ import argparse import cStringIO +import logging import os import re import sys @@ -24,7 +25,7 @@ import testtools from testtools import matchers -from neutronclient.common import exceptions +from neutronclient.common import clientmanager from neutronclient import shell as openstack_shell @@ -37,11 +38,6 @@ DEFAULT_URL = 'http://quantum.example.org:9696/' -class NoExitArgumentParser(argparse.ArgumentParser): - def error(self, message): - raise exceptions.CommandError(message) - - class ShellTest(testtools.TestCase): FAKE_ENV = { @@ -51,10 +47,6 @@ class ShellTest(testtools.TestCase): 'OS_TENANT_NAME': DEFAULT_TENANT_NAME, 'OS_AUTH_URL': DEFAULT_AUTH_URL} - def _tolerant_shell(self, cmd): - t_shell = openstack_shell.NeutronShell('2.0') - t_shell.run(cmd.split()) - # Patch os.environ to avoid required auth info. def setUp(self): super(ShellTest, self).setUp() @@ -64,65 +56,87 @@ def setUp(self): fixtures.EnvironmentVariable( var, self.FAKE_ENV[var])) - # Make a fake shell object, a helping wrapper to call it, and a quick - # way of asserting that certain API calls were made. - global shell, _shell, assert_called, assert_called_anytime - _shell = openstack_shell.NeutronShell('2.0') - shell = lambda cmd: _shell.run(cmd.split()) - - def shell(self, argstr): - orig = sys.stdout + def shell(self, argstr, check=False): + orig = (sys.stdout, sys.stderr) clean_env = {} _old_env, os.environ = os.environ, clean_env.copy() try: sys.stdout = cStringIO.StringIO() + sys.stderr = cStringIO.StringIO() _shell = openstack_shell.NeutronShell('2.0') _shell.run(argstr.split()) except SystemExit: exc_type, exc_value, exc_traceback = sys.exc_info() self.assertEqual(exc_value.code, 0) finally: - out = sys.stdout.getvalue() + stdout = sys.stdout.getvalue() + stderr = sys.stderr.getvalue() sys.stdout.close() - sys.stdout = orig + sys.stderr.close() + sys.stdout, sys.stderr = orig os.environ = _old_env - return out + return stdout, stderr def test_run_unknown_command(self): - openstack_shell.NeutronShell('2.0').run('fake') + self.useFixture(fixtures.FakeLogger(level=logging.DEBUG)) + stdout, stderr = self.shell('fake', check=True) + self.assertFalse(stdout) + self.assertEqual("Unknown command ['fake']", stderr.strip()) def test_help(self): required = 'usage:' - help_text = self.shell('help') + help_text, stderr = self.shell('help') self.assertThat( help_text, matchers.MatchesRegex(required)) + self.assertFalse(stderr) def test_help_on_subcommand(self): required = [ '.*?^usage: .* quota-list'] - stdout = self.shell('help quota-list') + stdout, stderr = self.shell('help quota-list') for r in required: self.assertThat( stdout, matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE)) + self.assertFalse(stderr) def test_help_command(self): required = 'usage:' - help_text = self.shell('help network-create') + help_text, stderr = self.shell('help network-create') self.assertThat( help_text, matchers.MatchesRegex(required)) + self.assertFalse(stderr) def test_unknown_auth_strategy(self): - self.shell('--os-auth-strategy fake quota-list') + self.useFixture(fixtures.FakeLogger(level=logging.DEBUG)) + stdout, stderr = self.shell('--os-auth-strategy fake quota-list') + self.assertFalse(stdout) + self.assertEqual('You must provide a service URL via ' + 'either --os-url or env[OS_URL]', stderr.strip()) def test_auth(self): - self.shell(' --os-username test' - ' --os-password test' - ' --os-tenant-name test' - ' --os-auth-url http://127.0.0.1:5000/' - ' --os-auth-strategy keystone quota-list') + neutron_shell = openstack_shell.NeutronShell('2.0') + self.addCleanup(self.mox.UnsetStubs) + self.mox.StubOutWithMock(clientmanager.ClientManager, '__init__') + self.mox.StubOutWithMock(neutron_shell, 'run_subcommand') + clientmanager.ClientManager.__init__( + token='', url='', auth_url='http://127.0.0.1:5000/', + tenant_name='test', tenant_id='tenant_id', username='test', + password='test', region_name='', api_version={'network': '2.0'}, + auth_strategy='keystone', service_type='network', + endpoint_type='publicURL', insecure=False, ca_cert=None, + log_credentials=True) + neutron_shell.run_subcommand(['quota-list']) + self.mox.ReplayAll() + cmdline = ('--os-username test ' + '--os-password test ' + '--os-tenant-name test ' + '--os-auth-url http://127.0.0.1:5000/ ' + '--os-auth-strategy keystone quota-list') + neutron_shell.run(cmdline.split()) + self.mox.VerifyAll() def test_build_option_parser(self): neutron_shell = openstack_shell.NeutronShell('2.0') @@ -132,7 +146,6 @@ def test_build_option_parser(self): def test_main_with_unicode(self): self.mox.StubOutClassWithMocks(openstack_shell, 'NeutronShell') qshell_mock = openstack_shell.NeutronShell('2.0') - #self.mox.StubOutWithMock(qshell_mock, 'run') unicode_text = u'\u7f51\u7edc' argv = ['net-list', unicode_text, unicode_text.encode('utf-8')] qshell_mock.run([u'net-list', unicode_text, From ecad086e5964cc67c8c261861bcc988fe2a3bffb Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Sat, 22 Mar 2014 19:50:08 +0900 Subject: [PATCH 007/845] Rearrange neutronclient exceptions for more easy use - Map exceptions from neutron server into corresponding client exceptions automatically (without exception mapping dict). An exception from Neutron server 'SomeException' will be mapped to an exception 'SomeExceptionClient' ('Client' suffix) if it is defined. - If no corresponding exception is defined in client side, an exception per response code will be used. Previously NeutronClientException was used for all cases and a user of client library cannot know exception type without checking exception.status_code. It allows client lib users (such as Horizon) to handle exceptions more easily. - All exceptions from client libarry inherit NeutronClientException and all CLI related exceptions inherits NeutronCLIError now. - Remove unused exceptions (including Quantum v1 related) - MalformedRequestBody is renamed to MalformedResponseBody because it is raised in deserializer and deserializer is used to parse not request but response. Closes-Bug: #1296148 Change-Id: I99b9560b3afaf5884fd00353323267da450338fa --- neutronclient/client.py | 7 +- neutronclient/common/exceptions.py | 171 ++++++++++++++----------- neutronclient/common/serializer.py | 4 +- neutronclient/tests/unit/test_cli20.py | 39 ++++-- neutronclient/v2_0/client.py | 26 ++-- 5 files changed, 145 insertions(+), 102 deletions(-) diff --git a/neutronclient/client.py b/neutronclient/client.py index a8f8d78d7..342b1b9cf 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -80,10 +80,11 @@ def url_for(self, attr=None, filter_value=None, if not matching_endpoints: raise exceptions.EndpointNotFound() elif len(matching_endpoints) > 1: - raise exceptions.AmbiguousEndpoints(message=matching_endpoints) + raise exceptions.AmbiguousEndpoints( + matching_endpoints=matching_endpoints) else: if endpoint_type not in matching_endpoints[0]: - raise exceptions.EndpointTypeNotFound(message=endpoint_type) + raise exceptions.EndpointTypeNotFound(type_=endpoint_type) return matching_endpoints[0][endpoint_type] @@ -274,7 +275,7 @@ def _get_endpoint_url(self): endpoint.get('region') == self.region_name): if self.endpoint_type not in endpoint: raise exceptions.EndpointTypeNotFound( - message=self.endpoint_type) + type_=self.endpoint_type) return endpoint[self.endpoint_type] raise exceptions.EndpointNotFound() diff --git a/neutronclient/common/exceptions.py b/neutronclient/common/exceptions.py index d7e3848be..f4085d319 100644 --- a/neutronclient/common/exceptions.py +++ b/neutronclient/common/exceptions.py @@ -17,13 +17,21 @@ """ Neutron base exception handling. + +Exceptions are classified into three categories: +* Exceptions corresponding to exceptions from neutron server: + This type of exceptions should inherit one of exceptions + in HTTP_EXCEPTION_MAP. +* Exceptions from client library: + This type of exceptions should inherit NeutronClientException. +* Exceptions from CLI code: + This type of exceptions should inherit NeutronCLIError. """ class NeutronException(Exception): """Base Neutron Exception - Taken from nova.exception.NovaException To correctly use this class, inherit from it and define a 'message' property. That message will get printf'd with the keyword arguments provided to the constructor. @@ -31,10 +39,11 @@ class NeutronException(Exception): """ message = _("An unknown exception occurred.") - def __init__(self, **kwargs): + def __init__(self, message=None, **kwargs): + if message: + self.message = message try: self._error_string = self.message % kwargs - except Exception: # at least get the core message out if something happened self._error_string = self.message @@ -43,95 +52,124 @@ def __str__(self): return self._error_string -class NotFound(NeutronException): - pass +class NeutronClientException(NeutronException): + """Base exception which exceptions from Neutron are mapped into. + NOTE: on the client side, we use different exception types in order + to allow client library users to handle server exceptions in try...except + blocks. The actual error message is the one generated on the server side. + """ -class NeutronClientException(NeutronException): + def __init__(self, message=None, **kwargs): + if 'status_code' in kwargs: + self.status_code = kwargs['status_code'] + super(NeutronClientException, self).__init__(message, **kwargs) - def __init__(self, **kwargs): - message = kwargs.get('message') - self.status_code = kwargs.get('status_code', 0) - if message: - self.message = message - super(NeutronClientException, self).__init__(**kwargs) +# Base exceptions from Neutron -# NOTE: on the client side, we use different exception types in order -# to allow client library users to handle server exceptions in try...except -# blocks. The actual error message is the one generated on the server side -class NetworkNotFoundClient(NeutronClientException): - pass +class BadRequest(NeutronClientException): + status_code = 400 -class PortNotFoundClient(NeutronClientException): - pass +class Unauthorized(NeutronClientException): + status_code = 401 + message = _("Unauthorized: bad credentials.") -class MalformedResponseBody(NeutronException): - message = _("Malformed response body: %(reason)s") +class Forbidden(NeutronClientException): + status_code = 403 + message = _("Forbidden: your credentials don't give you access to this " + "resource.") + + +class NotFound(NeutronClientException): + status_code = 404 + + +class Conflict(NeutronClientException): + status_code = 409 + + +class InternalServerError(NeutronClientException): + status_code = 500 -class StateInvalidClient(NeutronClientException): +class ServiceUnavailable(NeutronClientException): + status_code = 503 + + +HTTP_EXCEPTION_MAP = { + 400: BadRequest, + 401: Unauthorized, + 403: Forbidden, + 404: NotFound, + 409: Conflict, + 500: InternalServerError, + 503: ServiceUnavailable, +} + + +# Exceptions mapped to Neutron server exceptions +# These are defined if a user of client library needs specific exception. +# Exception name should be + 'Client' +# e.g., NetworkNotFound -> NetworkNotFoundClient + +class NetworkNotFoundClient(NotFound): pass -class NetworkInUseClient(NeutronClientException): +class PortNotFoundClient(NotFound): pass -class PortInUseClient(NeutronClientException): +class StateInvalidClient(BadRequest): pass -class IpAddressInUseClient(NeutronClientException): +class NetworkInUseClient(Conflict): pass -class AlreadyAttachedClient(NeutronClientException): +class PortInUseClient(Conflict): pass -class IpAddressGenerationFailureClient(NeutronClientException): +class IpAddressInUseClient(Conflict): pass -class ExternalIpAddressExhaustedClient(NeutronClientException): +# TODO(amotoki): It is unused in Neutron, but it is referred to +# in Horizon code. After Horizon code is updated, remove it. +class AlreadyAttachedClient(Conflict): pass -class Unauthorized(NeutronClientException): - message = _("Unauthorized: bad credentials.") +class IpAddressGenerationFailureClient(Conflict): + pass -class Forbidden(NeutronClientException): - message = _("Forbidden: your credentials don't give you access to this " - "resource.") +class ExternalIpAddressExhaustedClient(BadRequest): + pass + + +# Exceptions from client library + +class NoAuthURLProvided(Unauthorized): + message = _("auth_url was not provided to the Neutron client") class EndpointNotFound(NeutronClientException): - """Could not find Service or Region in Service Catalog.""" message = _("Could not find Service or Region in Service Catalog.") class EndpointTypeNotFound(NeutronClientException): - """Could not find endpoint type in Service Catalog.""" - - def __str__(self): - msg = _("Could not find endpoint type %s in Service Catalog.") - return msg % repr(self.message) + message = _("Could not find endpoint type %(type_)s in Service Catalog.") class AmbiguousEndpoints(NeutronClientException): - """Found more than one matching endpoint in Service Catalog.""" - - def __str__(self): - return _("AmbiguousEndpoints: %s") % repr(self.message) - - -class NeutronCLIError(NeutronClientException): - """Exception raised when command line parsing fails.""" - pass + message = _("Found more than one matching endpoint in Service Catalog: " + "%(matching_endpoints)") class RequestURITooLong(NeutronClientException): @@ -146,47 +184,36 @@ class ConnectionFailed(NeutronClientException): message = _("Connection to neutron failed: %(reason)s") -class BadInputError(Exception): - """Error resulting from a client sending bad input to a server.""" - pass - +class SslCertificateValidationError(NeutronClientException): + message = _("SSL certificate validation has failed: %(reason)s") -class NoAuthURLProvided(BadInputError): - message = _("auth_url was not provided to the Neutron client") +class MalformedResponseBody(NeutronClientException): + message = _("Malformed response body: %(reason)s") -class Error(Exception): - def __init__(self, message=None): - super(Error, self).__init__(message) +class InvalidContentType(NeutronClientException): + message = _("Invalid content type %(content_type)s.") -class MalformedRequestBody(NeutronException): - message = _("Malformed request body: %(reason)s") +# Command line exceptions -class Invalid(Error): +class NeutronCLIError(NeutronException): + """Exception raised when command line parsing fails.""" pass -class InvalidContentType(Invalid): - message = _("Invalid content type %(content_type)s.") +class CommandError(NeutronCLIError): + pass -class UnsupportedVersion(Exception): +class UnsupportedVersion(NeutronCLIError): """Indicates that the user is trying to use an unsupported version of the API """ pass -class CommandError(Exception): - pass - - -class NeutronClientNoUniqueMatch(NeutronClientException): +class NeutronClientNoUniqueMatch(NeutronCLIError): message = _("Multiple %(resource)s matches found for name '%(name)s'," " use an ID to be more specific.") - - -class SslCertificateValidationError(NeutronClientException): - message = _("SSL certificate validation has failed: %(reason)s") diff --git a/neutronclient/common/serializer.py b/neutronclient/common/serializer.py index b8d6d81ed..a7202e1e7 100644 --- a/neutronclient/common/serializer.py +++ b/neutronclient/common/serializer.py @@ -224,7 +224,7 @@ def _from_json(self, datastring): return jsonutils.loads(datastring) except ValueError: msg = _("Cannot understand JSON") - raise exception.MalformedRequestBody(reason=msg) + raise exception.MalformedResponseBody(reason=msg) def default(self, datastring): return {'body': self._from_json(datastring)} @@ -297,7 +297,7 @@ def _from_xml(self, datastring): parseError = True if parseError: msg = _("Cannot understand XML") - raise exception.MalformedRequestBody(reason=msg) + raise exception.MalformedResponseBody(reason=msg) else: raise diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 47b4a115d..9159a6a40 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -557,12 +557,26 @@ def test_exception_handler_v20_ip_address_in_use(self): 'IpAddressInUse', err_msg, '') def test_exception_handler_v20_neutron_known_error(self): - error_msg = 'Network not found' - error_detail = 'Network detail' - self._test_exception_handler_v20( - exceptions.NetworkNotFoundClient, 404, - error_msg + '\n' + error_detail, - 'NetworkNotFound', error_msg, error_detail) + known_error_map = [ + ('NetworkNotFound', exceptions.NetworkNotFoundClient, 404), + ('PortNotFound', exceptions.PortNotFoundClient, 404), + ('NetworkInUse', exceptions.NetworkInUseClient, 409), + ('PortInUse', exceptions.PortInUseClient, 409), + ('StateInvalid', exceptions.StateInvalidClient, 400), + ('IpAddressInUse', exceptions.IpAddressInUseClient, 409), + ('IpAddressGenerationFailure', + exceptions.IpAddressGenerationFailureClient, 409), + ('ExternalIpAddressExhausted', + exceptions.ExternalIpAddressExhaustedClient, 400), + ] + + error_msg = 'dummy exception message' + error_detail = 'sample detail' + for server_exc, client_exc, status_code in known_error_map: + self._test_exception_handler_v20( + client_exc, status_code, + error_msg + '\n' + error_detail, + server_exc, error_msg, error_detail) def test_exception_handler_v20_neutron_known_error_without_detail(self): error_msg = 'Network not found' @@ -572,11 +586,20 @@ def test_exception_handler_v20_neutron_known_error_without_detail(self): error_msg, 'NetworkNotFound', error_msg, error_detail) - def test_exception_handler_v20_neutron_unknown_error(self): + def test_exception_handler_v20_unknown_error_to_per_code_exception(self): + for status_code, client_exc in exceptions.HTTP_EXCEPTION_MAP.items(): + error_msg = 'Unknown error' + error_detail = 'This is detail' + self._test_exception_handler_v20( + client_exc, status_code, + error_msg + '\n' + error_detail, + 'UnknownError', error_msg, error_detail) + + def test_exception_handler_v20_neutron_unknown_status_code(self): error_msg = 'Unknown error' error_detail = 'This is detail' self._test_exception_handler_v20( - exceptions.NeutronClientException, 400, + exceptions.NeutronClientException, 501, error_msg + '\n' + error_detail, 'UnknownError', error_msg, error_detail) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 7805bc1a4..bd5f3c401 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -31,18 +31,6 @@ _logger = logging.getLogger(__name__) -NEUTRON_ERRORS = { - 'NetworkNotFound': exceptions.NetworkNotFoundClient, - 'NetworkInUse': exceptions.NetworkInUseClient, - 'PortNotFound': exceptions.PortNotFoundClient, - 'RequestedStateInvalid': exceptions.StateInvalidClient, - 'PortInUse': exceptions.PortInUseClient, - 'IpAddressInUse': exceptions.IpAddressInUseClient, - 'AlreadyAttached': exceptions.AlreadyAttachedClient, - 'IpAddressGenerationFailure': exceptions.IpAddressGenerationFailureClient, - 'ExternalIpAddressExhausted': exceptions.ExternalIpAddressExhaustedClient, -} - def exception_handler_v20(status_code, error_content): """Exception handler for API v2.0 client @@ -70,11 +58,15 @@ def exception_handler_v20(status_code, error_content): except Exception: bad_neutron_error_flag = True if not bad_neutron_error_flag: - try: - # raise the appropriate error! - raise NEUTRON_ERRORS[error_type](message=error_message, - status_code=status_code) - except KeyError: + # If corresponding exception is defined, use it. + client_exc = getattr(exceptions, '%sClient' % error_type, None) + # Otherwise look up per status-code client exception + if not client_exc: + client_exc = exceptions.HTTP_EXCEPTION_MAP.get(status_code) + if client_exc: + raise client_exc(message=error_message, + status_code=status_code) + else: raise exceptions.NeutronClientException( status_code=status_code, message=error_message) else: From 54d15e7ec0a0347aa6243cb15502508cb6f1ea29 Mon Sep 17 00:00:00 2001 From: armando-migliaccio Date: Tue, 25 Mar 2014 07:19:33 -0700 Subject: [PATCH 008/845] Show the unknown auth stratey in neutron client This can help unveil some useful information to be used during a troubleshooting process. Change-Id: I23657988fe31f5c552f17d6963de89c608273b44 Closes-bug: 1297309 --- neutronclient/client.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/neutronclient/client.py b/neutronclient/client.py index a8f8d78d7..cdc0fb2b6 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -217,7 +217,8 @@ def _extract_service_catalog(self, body): def authenticate(self): if self.auth_strategy != 'keystone': - raise exceptions.Unauthorized(message=_('Unknown auth strategy')) + err_msg = _('Unknown auth strategy: %s') % self.auth_strategy + raise exceptions.Unauthorized(message=err_msg) if self.tenant_id: body = {'auth': {'passwordCredentials': {'username': self.username, From 663f8bd2cf5825ff587b2b10bf5d4a4bfc082534 Mon Sep 17 00:00:00 2001 From: Justin Hammond Date: Wed, 20 Nov 2013 16:26:42 +0000 Subject: [PATCH 009/845] Adds support for os-auth-strategy=noauth The noauth strategy will not attempt to look for a token and will not use a token if one is given. This strategy will also not attempt to find a catalog and therefore the endpoint must be specified (either in the constructor or using --os-url). If the noauth strategy is specified and the endpoint is not it will fail with a descriptive message. A small amount of branching logic was added to do_request to only add the X-Auth-Token when the auth strategy is keystone. There is a blueprint (see: pluggable-neutronclient-auth) which should implement this logic in a more elegant way. A basic test was made to ensure that the auth strategy acted as designed. Implements: blueprint support-neutronclient-noauth Change-Id: Id875ce6a4dfd1f932aaab2c25d430ed60e4713bb --- neutronclient/client.py | 23 +++++++++++++++---- neutronclient/tests/unit/test_auth.py | 32 +++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/neutronclient/client.py b/neutronclient/client.py index cdc0fb2b6..2212114f8 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -187,6 +187,8 @@ def do_request(self, url, method, **kwargs): # re-authenticate and try again. If it still fails, bail. try: kwargs.setdefault('headers', {}) + if self.auth_token is None: + self.auth_token = "" kwargs['headers']['X-Auth-Token'] = self.auth_token resp, body = self._cs_request(self.endpoint_url + url, method, **kwargs) @@ -215,10 +217,7 @@ def _extract_service_catalog(self, body): service_type=self.service_type, endpoint_type=self.endpoint_type) - def authenticate(self): - if self.auth_strategy != 'keystone': - err_msg = _('Unknown auth strategy: %s') % self.auth_strategy - raise exceptions.Unauthorized(message=err_msg) + def _authenticate_keystone(self): if self.tenant_id: body = {'auth': {'passwordCredentials': {'username': self.username, @@ -256,6 +255,22 @@ def authenticate(self): resp_body = None self._extract_service_catalog(resp_body) + def _authenticate_noauth(self): + if not self.endpoint_url: + message = _('For "noauth" authentication strategy, the endpoint ' + 'must be specified either in the constructor or ' + 'using --os-url') + raise exceptions.Unauthorized(message=message) + + def authenticate(self): + if self.auth_strategy == 'keystone': + self._authenticate_keystone() + elif self.auth_strategy == 'noauth': + self._authenticate_noauth() + else: + err_msg = _('Unknown auth strategy: %s') % self.auth_strategy + raise exceptions.Unauthorized(message=err_msg) + def _get_endpoint_url(self): if self.auth_url is None: raise exceptions.NoAuthURLProvided() diff --git a/neutronclient/tests/unit/test_auth.py b/neutronclient/tests/unit/test_auth.py index b81d23614..84867d756 100644 --- a/neutronclient/tests/unit/test_auth.py +++ b/neutronclient/tests/unit/test_auth.py @@ -36,6 +36,7 @@ ENDPOINT_OVERRIDE = 'otherurl' TOKEN = 'tokentoken' REGION = 'RegionTest' +NOAUTH = 'noauth' KS_TOKEN_RESULT = { 'access': { @@ -67,6 +68,37 @@ } +class CLITestAuthNoAuth(testtools.TestCase): + + def setUp(self): + """Prepare the test environment.""" + super(CLITestAuthNoAuth, self).setUp() + self.mox = mox.Mox() + self.client = client.HTTPClient(username=USERNAME, + tenant_name=TENANT_NAME, + password=PASSWORD, + endpoint_url=ENDPOINT_URL, + auth_strategy=NOAUTH, + region_name=REGION) + self.addCleanup(self.mox.VerifyAll) + self.addCleanup(self.mox.UnsetStubs) + + def test_get_noauth(self): + self.mox.StubOutWithMock(self.client, "request") + + res200 = self.mox.CreateMock(httplib2.Response) + res200.status = 200 + + self.client.request( + mox.StrContains(ENDPOINT_URL + '/resource'), 'GET', + headers=mox.IsA(dict), + ).AndReturn((res200, '')) + self.mox.ReplayAll() + + self.client.do_request('/resource', 'GET') + self.assertEqual(self.client.endpoint_url, ENDPOINT_URL) + + class CLITestAuthKeystone(testtools.TestCase): # Auth Body expected when using tenant name From 82833e4e3bdcca3bac4de128c8afd746ef57f895 Mon Sep 17 00:00:00 2001 From: Andres Buraschi Date: Mon, 31 Mar 2014 16:06:20 -0300 Subject: [PATCH 010/845] Improvements in neutron_test sanity tests script Neutron test 'sanity' script was fixed and cleanup functionality was added, to allow a complete test cycle for create/update/delete methods. Change-Id: I4f611f68c100356a672dac0851d7339736c73f71 Closes-Bug: #1274945 --- neutron_test.sh | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/neutron_test.sh b/neutron_test.sh index 53aee7cf4..f2dae36a6 100755 --- a/neutron_test.sh +++ b/neutron_test.sh @@ -4,20 +4,34 @@ function die() { local exitcode=$? set +o xtrace echo $@ + cleanup exit $exitcode } +net_name=mynet1 +subnet_name=mysubnet1 +port_name=myport1 +function cleanup() { + echo Removing test port, subnet and net... + neutron port-delete $port_name + neutron subnet-delete $subnet_name + neutron net-delete $net_name +} + noauth_tenant_id=me -if [ $1 == 'noauth' ]; then +if [ "$1" == "noauth" ]; then NOAUTH="--tenant_id $noauth_tenant_id" else NOAUTH= fi +echo "NOTE: User should be admin in order to perform all operations." +sleep 3 + FORMAT=" --request-format xml" # test the CRUD of network -network=mynet1 +network=$net_name neutron net-create $FORMAT $NOAUTH $network || die "fail to create network $network" temp=`neutron net-list $FORMAT -- --name $network --fields id | wc -l` echo $temp @@ -36,8 +50,8 @@ neutron net-update $FORMAT $network_id --admin_state_up True || die "fail to neutron net-list $FORMAT -c id -- --id fakeid || die "fail to list networks with column selection on empty list" # test the CRUD of subnet -subnet=mysubnet1 -cidr=10.0.1.3/24 +subnet=$subnet_name +cidr=10.0.1.0/24 neutron subnet-create $FORMAT $NOAUTH $network $cidr --name $subnet || die "fail to create subnet $subnet" tempsubnet=`neutron subnet-list $FORMAT -- --name $subnet --fields id | wc -l` echo $tempsubnet @@ -49,11 +63,11 @@ echo "ID of subnet with name $subnet is $subnet_id" neutron subnet-show $FORMAT $subnet || die "fail to show subnet $subnet" neutron subnet-show $FORMAT $subnet_id || die "fail to show subnet $subnet_id" -neutron subnet-update $FORMAT $subnet --dns_namesevers host1 || die "fail to update subnet $subnet" -neutron subnet-update $FORMAT $subnet_id --dns_namesevers host2 || die "fail to update subnet $subnet_id" +neutron subnet-update $FORMAT $subnet --dns_nameservers list=true 1.1.1.11 1.1.1.12 || die "fail to update subnet $subnet" +neutron subnet-update $FORMAT $subnet_id --dns_nameservers list=true 2.2.2.21 2.2.2.22 || die "fail to update subnet $subnet_id" # test the crud of ports -port=myport1 +port=$port_name neutron port-create $FORMAT $NOAUTH $network --name $port || die "fail to create port $port" tempport=`neutron port-list $FORMAT -- --name $port --fields id | wc -l` echo $tempport @@ -126,3 +140,7 @@ else fi neutron quota-list $FORMAT || die "fail to update quota for self" fi + +cleanup +echo "Success! :)" + From f1a2d5eb10b29384769a0d65c9a134e8748a648b Mon Sep 17 00:00:00 2001 From: Thomas Herve Date: Mon, 3 Mar 2014 11:37:37 +0100 Subject: [PATCH 011/845] Return response status reason on error Upon server error, return the status reason if the body is empty, to help debug server issues. Change-Id: I29aef075081965bb8ecacf45a166ca9dbd4cbb8d Closes-Bug: #1287110 --- neutronclient/tests/unit/test_cli20.py | 30 ++++++++++++++++++++++---- neutronclient/v2_0/client.py | 2 ++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 40a1e5799..bfcb9b497 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -48,8 +48,9 @@ def make_string(self): class MyResp(object): - def __init__(self, status): + def __init__(self, status, reason=None): self.status = status + self.reason = reason class MyApp(object): @@ -485,8 +486,8 @@ def _test_update_resource_action(self, resource, cmd, myid, action, args, self.assertIn(myid, _str) -class ClientV2UnicodeTestJson(CLITestV20Base): - def test_do_request(self): +class ClientV2TestJson(CLITestV20Base): + def test_do_request_unicode(self): self.client.format = self.format self.mox.StubOutWithMock(self.client.httpclient, "request") unicode_text = u'\u7f51\u7edc' @@ -520,8 +521,29 @@ def test_do_request(self): # test response with unicode self.assertEqual(res_body, body) + def test_do_request_error_without_response_body(self): + self.client.format = self.format + self.mox.StubOutWithMock(self.client.httpclient, "request") + params = {'test': 'value'} + expect_query = urllib.urlencode(params) + self.client.httpclient.auth_token = 'token' + + self.client.httpclient.request( + end_url('/test', query=expect_query, format=self.format), + 'PUT', body='', + headers=mox.ContainsKeyValue('X-Auth-Token', 'token') + ).AndReturn((MyResp(400, 'An error'), '')) + + self.mox.ReplayAll() + error = self.assertRaises(exceptions.NeutronClientException, + self.client.do_request, 'PUT', '/test', + body='', params=params) + self.assertEqual("An error", str(error)) + self.mox.VerifyAll() + self.mox.UnsetStubs() + -class ClientV2UnicodeTestXML(ClientV2UnicodeTestJson): +class ClientV2UnicodeTestXML(ClientV2TestJson): format = 'xml' diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 99ff6e2f9..d22d625f4 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -1161,6 +1161,8 @@ def do_request(self, method, action, body=None, headers=None, params=None): httplib.NO_CONTENT): return self.deserialize(replybody, status_code) else: + if not replybody: + replybody = resp.reason self._handle_fault_response(status_code, replybody) def get_auth_info(self): From 27f04bffa480211470658e876658f3e46ddcf123 Mon Sep 17 00:00:00 2001 From: OpenStack Jenkins Date: Mon, 7 Apr 2014 21:16:14 +0000 Subject: [PATCH 012/845] Updated from global requirements Change-Id: I35dcfdd24067e6bd37f970d8ab73d416fbe55f96 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 9ad25b113..cc0ed0f12 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -pbr>=0.6,<1.0 +pbr>=0.6,!=0.7,<1.0 argparse cliff>=1.4.3 httplib2>=0.7.5 From ce60624a2fab0345b038bf653eeae7c0a7d815d0 Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Wed, 19 Feb 2014 19:34:30 +0100 Subject: [PATCH 013/845] Python3: fix syntax issue in _encode_item() In Python 3, this is not a valid syntax: def _encode_item((k, v)): ... Pass a single parameter to the function, and start by unpacking. Change-Id: I73743073113a4696b9adc79bdb646575794c1e2b --- neutronclient/common/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/neutronclient/common/utils.py b/neutronclient/common/utils.py index 532d7ea7d..208912182 100644 --- a/neutronclient/common/utils.py +++ b/neutronclient/common/utils.py @@ -190,7 +190,8 @@ def safe_encode_list(data): def safe_encode_dict(data): - def _encode_item((k, v)): + def _encode_item(item): + k, v = item if isinstance(v, list): return (k, safe_encode_list(v)) elif isinstance(v, dict): From 320e014eff413c5b81ad006087029854bb7d34c6 Mon Sep 17 00:00:00 2001 From: Akihiro MOTOKI Date: Mon, 2 Sep 2013 05:15:01 +0900 Subject: [PATCH 014/845] Support packet_filter extension in NEC plugin blueprint nec-packet-filter-cli Also adds common validators. Change-Id: Ia431c2268e9a654e10dc9cf740288fb746825d13 --- neutronclient/common/validators.py | 69 ++++ neutronclient/neutron/v2_0/nec/__init__.py | 0 .../neutron/v2_0/nec/packetfilter.py | 243 ++++++++++++++ neutronclient/shell.py | 6 + .../tests/unit/test_cli20_packetfilter.py | 298 ++++++++++++++++++ neutronclient/tests/unit/test_validators.py | 101 ++++++ neutronclient/v2_0/client.py | 33 +- requirements.txt | 1 + 8 files changed, 750 insertions(+), 1 deletion(-) create mode 100644 neutronclient/common/validators.py create mode 100644 neutronclient/neutron/v2_0/nec/__init__.py create mode 100644 neutronclient/neutron/v2_0/nec/packetfilter.py create mode 100644 neutronclient/tests/unit/test_cli20_packetfilter.py create mode 100644 neutronclient/tests/unit/test_validators.py diff --git a/neutronclient/common/validators.py b/neutronclient/common/validators.py new file mode 100644 index 000000000..032444c82 --- /dev/null +++ b/neutronclient/common/validators.py @@ -0,0 +1,69 @@ +# Copyright 2014 NEC Corporation +# 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 netaddr + +from neutronclient.common import exceptions +from neutronclient.openstack.common.gettextutils import _ + + +def validate_int_range(parsed_args, attr_name, min_value=None, max_value=None): + val = getattr(parsed_args, attr_name, None) + if val is None: + return + try: + if not isinstance(val, int): + int_val = int(val, 0) + else: + int_val = val + if ((min_value is None or min_value <= int_val) and + (max_value is None or int_val <= max_value)): + return + except (ValueError, TypeError): + pass + + if min_value is not None and max_value is not None: + msg = (_('%(attr_name)s "%(val)s" should be an integer ' + '[%(min)i:%(max)i].') % + {'attr_name': attr_name.replace('_', '-'), + 'val': val, 'min': min_value, 'max': max_value}) + elif min_value is not None: + msg = (_('%(attr_name)s "%(val)s" should be an integer ' + 'greater than or equal to %(min)i.') % + {'attr_name': attr_name.replace('_', '-'), + 'val': val, 'min': min_value}) + elif max_value is not None: + msg = (_('%(attr_name)s "%(val)s" should be an integer ' + 'smaller than or equal to %(max)i.') % + {'attr_name': attr_name.replace('_', '-'), + 'val': val, 'max': max_value}) + else: + msg = (_('%(attr_name)s "%(val)s" should be an integer.') % + {'attr_name': attr_name.replace('_', '-'), + 'val': val}) + + raise exceptions.CommandError(msg) + + +def validate_ip_subnet(parsed_args, attr_name): + val = getattr(parsed_args, attr_name) + if not val: + return + try: + netaddr.IPNetwork(val) + except (netaddr.AddrFormatError, ValueError): + raise exceptions.CommandError( + (_('%(attr_name)s "%(val)s" is not a valid CIDR.') % + {'attr_name': attr_name.replace('_', '-'), 'val': val})) diff --git a/neutronclient/neutron/v2_0/nec/__init__.py b/neutronclient/neutron/v2_0/nec/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/neutron/v2_0/nec/packetfilter.py b/neutronclient/neutron/v2_0/nec/packetfilter.py new file mode 100644 index 000000000..c9e1f6ebd --- /dev/null +++ b/neutronclient/neutron/v2_0/nec/packetfilter.py @@ -0,0 +1,243 @@ +# Copyright 2014 NEC Corporation +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import logging + +from neutronclient.common import exceptions +from neutronclient.common import validators +from neutronclient.neutron import v2_0 as neutronV20 +from neutronclient.openstack.common.gettextutils import _ + + +class ListPacketFilter(neutronV20.ListCommand): + """List packet filters that belong to a given tenant.""" + + resource = 'packet_filter' + log = logging.getLogger(__name__ + '.ListPacketFilter') + list_columns = ['id', 'name', 'action', 'priority', 'summary'] + pagination_support = True + sorting_support = True + + def extend_list(self, data, parsed_args): + for d in data: + val = [] + proto_eth_type = [] + if d.get('protocol'): + proto_eth_type.append('protocol: %s' % d['protocol'].upper()) + if d.get('eth_type'): + proto_eth_type.append('eth_type: %s' % d['eth_type']) + if proto_eth_type: + val.append(', '.join(proto_eth_type)) + val.append('network: ' + d['network_id']) + if d.get('in_port'): + val.append('in_port: ' + d['in_port']) + source = [str(d.get(field)) for field + in ['src_mac', 'src_cidr', 'src_port'] if d.get(field)] + if source: + val.append('source: ' + ' '.join(source)) + dest = [str(d.get(field)) for field + in ['dst_mac', 'dst_cidr', 'dst_port'] if d.get(field)] + if dest: + val.append('destination: ' + ' '.join(dest)) + d['summary'] = '\n'.join(val) + + +class ShowPacketFilter(neutronV20.ShowCommand): + """Show information of a given packet filter.""" + + resource = 'packet_filter' + log = logging.getLogger(__name__ + '.ShowPacketFilter') + + +class PacketFilterOptionMixin(object): + def add_known_arguments(self, parser): + mode = self._get_mode() + if not mode: + return + mode_create = mode == 'create' + + if mode_create: + parser.add_argument( + '--admin-state-down', + dest='admin_state', action='store_false', + help=_('Set Admin State Up to false')) + else: + parser.add_argument( + '--admin-state', choices=['True', 'False'], + help=_('Set a value of Admin State Up')) + + parser.add_argument( + '--name', + help=_('Name of this packet filter')) + + if mode_create: + parser.add_argument( + '--in-port', metavar='PORT', + help=_('Name or ID of the input port')) + + parser.add_argument( + '--src-mac', + help=_('Source MAC address')) + parser.add_argument( + '--dst-mac', + help=_('Destination MAC address')) + parser.add_argument( + '--eth-type', + help=_('Ether Type. Integer [0:65535] (hex or decimal).' + ' E.g., 0x0800 (IPv4), 0x0806 (ARP), 0x86DD (IPv6)')) + parser.add_argument( + '--protocol', + help=_('IP Protocol.' + ' Protocol name or integer.' + ' Recognized names are icmp, tcp, udp, arp' + ' (case insensitive).' + ' Integer should be [0:255] (decimal or hex).')) + parser.add_argument( + '--src-cidr', + help=_('Source IP address CIDR')) + parser.add_argument( + '--dst-cidr', + help=_('Destination IP address CIDR')) + parser.add_argument( + '--src-port', + help=_('Source port address')) + parser.add_argument( + '--dst-port', + help=_('Destination port address')) + + default_priority = '30000' if mode_create else None + parser.add_argument( + '--priority', metavar='PRIORITY', + default=default_priority, + help=(_('Priority of the filter. Integer of [0:65535].%s') + % (' Default: 30000.' if mode_create else ''))) + + default_action = 'allow' if mode_create else None + parser.add_argument( + '--action', + choices=['allow', 'drop'], + default=default_action, + help=(_('Action of the filter.%s') + % (' Default: allow' if mode_create else ''))) + + if mode_create: + parser.add_argument( + 'network', metavar='NETWORK', + help=_('network to which this packet filter is applied')) + + def _get_mode(self): + klass = self.__class__.__name__.lower() + if klass.startswith('create'): + mode = 'create' + elif klass.startswith('update'): + mode = 'update' + else: + mode = None + return mode + + def validate_fields(self, parsed_args): + self._validate_protocol(parsed_args.protocol) + validators.validate_int_range(parsed_args, 'priority', 0, 0xffff) + validators.validate_int_range(parsed_args, 'src_port', 0, 0xffff) + validators.validate_int_range(parsed_args, 'dst_port', 0, 0xffff) + validators.validate_ip_subnet(parsed_args, 'src_cidr') + validators.validate_ip_subnet(parsed_args, 'dst_cidr') + + def _validate_protocol(self, protocol): + if not protocol or protocol == 'action=clear': + return + try: + protocol = int(protocol, 0) + if 0 <= protocol <= 255: + return + except ValueError: + # Use string as a protocol name + # Exact check will be done in the server side. + return + msg = (_('protocol %s should be either of name ' + '(tcp, udp, icmp, arp; ' + 'case insensitive) or integer [0:255] (decimal or hex).') % + protocol) + raise exceptions.CommandError(msg) + + +class CreatePacketFilter(PacketFilterOptionMixin, + neutronV20.CreateCommand): + """Create a packet filter for a given tenant.""" + + resource = 'packet_filter' + log = logging.getLogger(__name__ + '.CreatePacketFilter') + + def args2body(self, parsed_args): + self.validate_fields(parsed_args) + + _network_id = neutronV20.find_resourceid_by_name_or_id( + self.get_client(), 'network', parsed_args.network) + body = {'network_id': _network_id, + 'admin_state_up': parsed_args.admin_state} + if parsed_args.in_port: + _port_id = neutronV20.find_resourceid_by_name_or_id( + self.get_client(), 'port', parsed_args.in_port) + body['in_port'] = _port_id + + neutronV20.update_dict( + parsed_args, body, + ['action', 'priority', 'name', + 'eth_type', 'protocol', 'src_mac', 'dst_mac', + 'src_cidr', 'dst_cidr', 'src_port', 'dst_port']) + + return {self.resource: body} + + +class UpdatePacketFilter(PacketFilterOptionMixin, + neutronV20.UpdateCommand): + """Update packet filter's information.""" + + resource = 'packet_filter' + log = logging.getLogger(__name__ + '.UpdatePacketFilter') + + def args2body(self, parsed_args): + self.validate_fields(parsed_args) + + body = {} + if parsed_args.admin_state: + body['admin_state_up'] = (parsed_args.admin_state == 'True') + + # fields which allows None + for attr in ['eth_type', 'protocol', 'src_mac', 'dst_mac', + 'src_cidr', 'dst_cidr', 'src_port', 'dst_port']: + if not hasattr(parsed_args, attr): + continue + val = getattr(parsed_args, attr) + if val is None: + continue + if val == '' or val == 'action=clear': + body[attr] = None + else: + body[attr] = val + + for attr in ['action', 'priority', 'name']: + if (hasattr(parsed_args, attr) and + getattr(parsed_args, attr) is not None): + body[attr] = getattr(parsed_args, attr) + + return {self.resource: body} + + +class DeletePacketFilter(neutronV20.DeleteCommand): + """Delete a given packet filter.""" + + resource = 'packet_filter' + log = logging.getLogger(__name__ + '.DeletePacketFilter') diff --git a/neutronclient/shell.py b/neutronclient/shell.py index b86b36990..05e91ac52 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -44,6 +44,7 @@ from neutronclient.neutron.v2_0.lb import pool as lb_pool from neutronclient.neutron.v2_0.lb import vip as lb_vip from neutronclient.neutron.v2_0 import metering +from neutronclient.neutron.v2_0.nec import packetfilter from neutronclient.neutron.v2_0 import netpartition from neutronclient.neutron.v2_0 import network from neutronclient.neutron.v2_0 import networkprofile @@ -271,6 +272,11 @@ def env(*_vars, **kwargs): 'nuage-netpartition-show': netpartition.ShowNetPartition, 'nuage-netpartition-create': netpartition.CreateNetPartition, 'nuage-netpartition-delete': netpartition.DeleteNetPartition, + 'nec-packet-filter-list': packetfilter.ListPacketFilter, + 'nec-packet-filter-show': packetfilter.ShowPacketFilter, + 'nec-packet-filter-create': packetfilter.CreatePacketFilter, + 'nec-packet-filter-update': packetfilter.UpdatePacketFilter, + 'nec-packet-filter-delete': packetfilter.DeletePacketFilter, } COMMANDS = {'2.0': COMMAND_V2} diff --git a/neutronclient/tests/unit/test_cli20_packetfilter.py b/neutronclient/tests/unit/test_cli20_packetfilter.py new file mode 100644 index 000000000..2cb78e6e2 --- /dev/null +++ b/neutronclient/tests/unit/test_cli20_packetfilter.py @@ -0,0 +1,298 @@ +# Copyright 2014 NEC Corporation. +# 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 sys + +import mox + +from neutronclient.common import exceptions +from neutronclient.neutron.v2_0.nec import packetfilter as pf +from neutronclient import shell +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20PacketFilterJSON(test_cli20.CLITestV20Base): + def test_create_packetfilter_with_mandatory_params(self): + """Create packetfilter: packetfilter1.""" + resource = 'packet_filter' + cmd = pf.CreatePacketFilter(test_cli20.MyApp(sys.stdout), None) + name = 'packetfilter1' + myid = 'myid' + args = ['--priority', '30000', '--action', 'allow', 'net1'] + position_names = ['network_id', 'action', 'priority'] + position_values = ['net1', 'allow', '30000'] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_create_packetfilter_with_all_params(self): + """Create packetfilter: packetfilter1.""" + resource = 'packet_filter' + cmd = pf.CreatePacketFilter(test_cli20.MyApp(sys.stdout), None) + name = 'packetfilter1' + myid = 'myid' + args = ['--name', name, + '--admin-state-down', + '--in-port', 'port1', + '--src-mac', '00:11:22:33:44:55', + '--dst-mac', 'aa:bb:cc:dd:ee:ff', + '--eth-type', '0x0800', + '--protocol', 'tcp', + '--src-cidr', '10.1.1.0/24', + '--dst-cidr', '10.2.2.0/24', + '--src-port', '40001', + '--dst-port', '4000', + '--priority', '30000', + '--action', 'drop', 'net1'] + params = {'network_id': 'net1', + 'action': 'drop', + 'priority': '30000', + 'name': name, + 'admin_state_up': False, + 'in_port': 'port1', + 'src_mac': '00:11:22:33:44:55', + 'dst_mac': 'aa:bb:cc:dd:ee:ff', + 'eth_type': '0x0800', + 'protocol': 'tcp', + 'src_cidr': '10.1.1.0/24', + 'dst_cidr': '10.2.2.0/24', + 'src_port': '40001', + 'dst_port': '4000', + } + position_names = sorted(params) + position_values = [params[k] for k in sorted(params)] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_list_packetfilters_detail(self): + """list packetfilters: -D.""" + resources = "packet_filters" + cmd = pf.ListPacketFilter(test_cli20.MyApp(sys.stdout), None) + response_contents = [{'id': 'myid1', 'network_id': 'net1'}, + {'id': 'myid2', 'network_id': 'net2'}] + self._test_list_resources(resources, cmd, True, + response_contents=response_contents) + + def _stubout_extend_list(self): + self.mox.StubOutWithMock(pf.ListPacketFilter, "extend_list") + pf.ListPacketFilter.extend_list(mox.IsA(list), mox.IgnoreArg()) + + def test_list_packetfilters_pagination(self): + resources = "packet_filters" + cmd = pf.ListPacketFilter(test_cli20.MyApp(sys.stdout), None) + self._stubout_extend_list() + self._test_list_resources_with_pagination(resources, cmd) + + def test_list_packetfilters_sort(self): + """list packetfilters: --sort-key name --sort-key id --sort-key asc + --sort-key desc + """ + resources = "packet_filters" + cmd = pf.ListPacketFilter(test_cli20.MyApp(sys.stdout), None) + self._stubout_extend_list() + self._test_list_resources(resources, cmd, + sort_key=["name", "id"], + sort_dir=["asc", "desc"]) + + def test_list_packetfilters_limit(self): + """list packetfilters: -P.""" + resources = "packet_filters" + cmd = pf.ListPacketFilter(test_cli20.MyApp(sys.stdout), None) + self._stubout_extend_list() + self._test_list_resources(resources, cmd, page_size=1000) + + def test_update_packetfilter(self): + """Update packetfilter: myid --name myname --tags a b.""" + resource = 'packet_filter' + cmd = pf.UpdatePacketFilter(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--name', 'myname'], + {'name': 'myname'} + ) + + def test_update_packetfilter_with_all_params(self): + resource = 'packet_filter' + cmd = pf.UpdatePacketFilter(test_cli20.MyApp(sys.stdout), None) + name = 'packetfilter1' + args = ['--name', name, + '--admin-state', 'True', + '--src-mac', '00:11:22:33:44:55', + '--dst-mac', 'aa:bb:cc:dd:ee:ff', + '--eth-type', '0x0800', + '--protocol', 'tcp', + '--src-cidr', '10.1.1.0/24', + '--dst-cidr', '10.2.2.0/24', + '--src-port', '40001', + '--dst-port', '4000', + '--priority', '30000', + '--action', 'drop', + 'myid' + ] + params = {'action': 'drop', + 'priority': '30000', + 'name': name, + 'admin_state_up': True, + 'src_mac': '00:11:22:33:44:55', + 'dst_mac': 'aa:bb:cc:dd:ee:ff', + 'eth_type': '0x0800', + 'protocol': 'tcp', + 'src_cidr': '10.1.1.0/24', + 'dst_cidr': '10.2.2.0/24', + 'src_port': '40001', + 'dst_port': '4000', + } + # position_names = sorted(params) + # position_values = [params[k] for k in sorted(params)] + self._test_update_resource(resource, cmd, 'myid', + args, params) + + def test_update_packetfilter_admin_state_false(self): + resource = 'packet_filter' + cmd = pf.UpdatePacketFilter(test_cli20.MyApp(sys.stdout), None) + args = ['--admin-state', 'False', 'myid'] + params = {'admin_state_up': False} + self._test_update_resource(resource, cmd, 'myid', + args, params) + + def test_update_packetfilter_exception(self): + """Update packetfilter: myid.""" + resource = 'packet_filter' + cmd = pf.UpdatePacketFilter(test_cli20.MyApp(sys.stdout), None) + exc = self.assertRaises(exceptions.CommandError, + self._test_update_resource, + resource, cmd, 'myid', ['myid'], {}) + self.assertEqual('Must specify new values to update packet_filter', + unicode(exc)) + + def test_delete_packetfilter(self): + """Delete packetfilter: myid.""" + resource = 'packet_filter' + cmd = pf.DeletePacketFilter(test_cli20.MyApp(sys.stdout), None) + myid = 'myid' + args = [myid] + self._test_delete_resource(resource, cmd, myid, args) + + def test_show_packetfilter(self): + """Show packetfilter: myid.""" + resource = 'packet_filter' + cmd = pf.ShowPacketFilter(test_cli20.MyApp(sys.stdout), None) + args = ['--fields', 'id', '--fields', 'name', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, args, + ['id', 'name']) + + +class CLITestV20PacketFilterXML(CLITestV20PacketFilterJSON): + format = 'xml' + + +class CLITestV20PacketFilterValidateParam(test_cli20.CLITestV20Base): + def _test_create_packetfilter_pass_validation(self, cmdline=None, + params=None, base_args=None): + resource = 'packet_filter' + cmd = pf.CreatePacketFilter(test_cli20.MyApp(sys.stdout), None) + name = 'packetfilter1' + myid = 'myid' + if base_args is None: + args = '--priority 30000 --action allow net1'.split() + else: + args = base_args.split() + if cmdline: + args += cmdline.split() + _params = {'network_id': 'net1', + 'action': 'allow', + 'priority': '30000'} + if params: + _params.update(params) + position_names = sorted(_params) + position_values = [_params[k] for k in sorted(_params)] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def _test_create_packetfilter_negative_validation(self, cmdline): + resource = 'packet_filter' + cmd = pf.CreatePacketFilter(test_cli20.MyApp(sys.stdout), None) + self.mox.StubOutWithMock(cmd, "get_client") + self.mox.StubOutWithMock(self.client.httpclient, "request") + cmd.get_client().MultipleTimes().AndReturn(self.client) + cmd_parser = cmd.get_parser('create_' + resource) + args = cmdline.split() + self.assertRaises(exceptions.CommandError, + shell.run_command, + cmd, cmd_parser, args) + + def test_create_pf_hex_priority(self): + self._test_create_packetfilter_pass_validation( + base_args='--priority 0xffff --action allow net1', + params={'priority': '0xffff'}) + + def test_create_pf_hex_src_port(self): + self._test_create_packetfilter_pass_validation( + cmdline='--src-port 0xffff', params={'src_port': '0xffff'}) + + def test_create_pf_hex_dst_port(self): + self._test_create_packetfilter_pass_validation( + cmdline='--dst-port 0xffff', params={'dst_port': '0xffff'}) + + def test_create_pf_ip_proto_zero(self): + self._test_create_packetfilter_pass_validation( + cmdline='--protocol 0', params={'protocol': '0'}) + + def test_create_pf_ip_proto_max_hex(self): + self._test_create_packetfilter_pass_validation( + cmdline='--protocol 0xff', params={'protocol': '0xff'}) + + def test_create_pf_ip_proto_with_names(self): + for proto in ['tcp', 'xxxx']: + self._test_create_packetfilter_pass_validation( + cmdline='--protocol ' + proto, params={'protocol': proto}) + + def test_create_pf_negative_priority(self): + self._test_create_packetfilter_negative_validation( + '--priority -1 --action allow net1') + + def test_create_pf_too_big_priority(self): + self._test_create_packetfilter_negative_validation( + '--priority 65536 --action allow net1') + + def test_create_pf_negative_src_port(self): + self._test_create_packetfilter_negative_validation( + '--src-port -1 --priority 20000 --action allow net1') + + def test_create_pf_too_big_src_port(self): + self._test_create_packetfilter_negative_validation( + '--src-port 65536 --priority 20000 --action allow net1') + + def test_create_pf_negative_dst_port(self): + self._test_create_packetfilter_negative_validation( + '--dst-port -1 --priority 20000 --action allow net1') + + def test_create_pf_too_big_dst_port(self): + self._test_create_packetfilter_negative_validation( + '--dst-port 65536 --priority 20000 --action allow net1') + + def test_create_pf_negative_protocol(self): + self._test_create_packetfilter_negative_validation( + '--protocol -1 --priority 20000 --action allow net1') + + def test_create_pf_too_big_hex_protocol(self): + self._test_create_packetfilter_negative_validation( + '--protocol 0x100 --priority 20000 --action allow net1') + + def test_create_pf_invalid_src_cidr(self): + self._test_create_packetfilter_negative_validation( + '--src-cidr invalid --priority 20000 --action allow net1') + + def test_create_pf_invalid_dst_cidr(self): + self._test_create_packetfilter_negative_validation( + '--dst-cidr invalid --priority 20000 --action allow net1') diff --git a/neutronclient/tests/unit/test_validators.py b/neutronclient/tests/unit/test_validators.py new file mode 100644 index 000000000..619acb817 --- /dev/null +++ b/neutronclient/tests/unit/test_validators.py @@ -0,0 +1,101 @@ +# Copyright 2014 NEC Corporation +# 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 testtools + +from neutronclient.common import exceptions +from neutronclient.common import validators + + +class FakeParsedArgs(): + pass + + +class ValidatorTest(testtools.TestCase): + + def _test_validate_int(self, attr_val, attr_name='attr1', + min_value=1, max_value=10): + obj = FakeParsedArgs() + setattr(obj, attr_name, attr_val) + ret = validators.validate_int_range(obj, attr_name, + min_value, max_value) + # Come here only if there is no exception. + self.assertIsNone(ret) + + def _test_validate_int_error(self, attr_val, expected_msg, + attr_name='attr1', expected_exc=None, + min_value=1, max_value=10): + if expected_exc is None: + expected_exc = exceptions.CommandError + e = self.assertRaises(expected_exc, + self._test_validate_int, + attr_val, attr_name, min_value, max_value) + self.assertEqual(expected_msg, str(e)) + + def test_validate_int_min_max(self): + self._test_validate_int(1) + self._test_validate_int(10) + self._test_validate_int('1') + self._test_validate_int('10') + self._test_validate_int('0x0a') + + self._test_validate_int_error( + 0, 'attr1 "0" should be an integer [1:10].') + self._test_validate_int_error( + 11, 'attr1 "11" should be an integer [1:10].') + self._test_validate_int_error( + '0x10', 'attr1 "0x10" should be an integer [1:10].') + + def test_validate_int_min_only(self): + self._test_validate_int(1, max_value=None) + self._test_validate_int(10, max_value=None) + self._test_validate_int(11, max_value=None) + self._test_validate_int_error( + 0, 'attr1 "0" should be an integer greater than or equal to 1.', + max_value=None) + + def test_validate_int_max_only(self): + self._test_validate_int(0, min_value=None) + self._test_validate_int(1, min_value=None) + self._test_validate_int(10, min_value=None) + self._test_validate_int_error( + 11, 'attr1 "11" should be an integer smaller than or equal to 10.', + min_value=None) + + def test_validate_int_no_limit(self): + self._test_validate_int(0, min_value=None, max_value=None) + self._test_validate_int(1, min_value=None, max_value=None) + self._test_validate_int(10, min_value=None, max_value=None) + self._test_validate_int(11, min_value=None, max_value=None) + self._test_validate_int_error( + 'abc', 'attr1 "abc" should be an integer.', + min_value=None, max_value=None) + + def _test_validate_subnet(self, attr_val, attr_name='attr1'): + obj = FakeParsedArgs() + setattr(obj, attr_name, attr_val) + ret = validators.validate_ip_subnet(obj, attr_name) + # Come here only if there is no exception. + self.assertIsNone(ret) + + def test_validate_ip_subnet(self): + self._test_validate_subnet('192.168.2.0/24') + self._test_validate_subnet('192.168.2.3/20') + self._test_validate_subnet('192.168.2.1') + + e = self.assertRaises(exceptions.CommandError, + self._test_validate_subnet, + '192.168.2.256') + self.assertEqual('attr1 "192.168.2.256" is not a valid CIDR.', str(e)) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index d29732041..c1d441b6f 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -201,6 +201,9 @@ class Client(object): metering_label_path = "/metering/metering-labels/%s" metering_label_rules_path = "/metering/metering-label-rules" metering_label_rule_path = "/metering/metering-label-rules/%s" + packet_filters_path = "/packet_filters" + packet_filter_path = "/packet_filters/%s" + DHCP_NETS = '/dhcp-networks' DHCP_AGENTS = '/dhcp-agents' L3_ROUTERS = '/l3-routers' @@ -240,7 +243,8 @@ class Client(object): 'firewalls': 'firewall', 'metering_labels': 'metering_label', 'metering_label_rules': 'metering_label_rule', - 'net_partitions': 'net_partition' + 'net_partitions': 'net_partition', + 'packet_filters': 'packet_filter', } # 8192 Is the default max URI len for eventlet.wsgi.server MAX_URI_LEN = 8192 @@ -1155,6 +1159,33 @@ def delete_net_partition(self, netpartition): """Delete the network partition.""" return self.delete(self.net_partition_path % netpartition) + @APIParamsCall + def create_packet_filter(self, body=None): + """Create a new packet filter.""" + return self.post(self.packet_filters_path, body=body) + + @APIParamsCall + def update_packet_filter(self, packet_filter_id, body=None): + """Update a packet filter.""" + return self.put(self.packet_filter_path % packet_filter_id, body=body) + + @APIParamsCall + def list_packet_filters(self, retrieve_all=True, **_params): + """Fetch a list of all packet filters for a tenant.""" + return self.list('packet_filters', self.packet_filters_path, + retrieve_all, **_params) + + @APIParamsCall + def show_packet_filter(self, packet_filter_id, **_params): + """Fetch information of a certain packet filter.""" + return self.get(self.packet_filter_path % packet_filter_id, + params=_params) + + @APIParamsCall + def delete_packet_filter(self, packet_filter_id): + """Delete the specified packet filter.""" + return self.delete(self.packet_filter_path % packet_filter_id) + def __init__(self, **kwargs): """Initialize a new client for the Neutron v2.0 API.""" super(Client, self).__init__() diff --git a/requirements.txt b/requirements.txt index cc0ed0f12..09b4537d2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,7 @@ argparse cliff>=1.4.3 httplib2>=0.7.5 iso8601>=0.1.9 +netaddr>=0.7.6 simplejson>=2.0.9 six>=1.5.2 Babel>=1.3 From 70f2e7113ebd46bac9e778ba1318d30a542852bd Mon Sep 17 00:00:00 2001 From: armando-migliaccio Date: Tue, 15 Apr 2014 13:46:12 -0700 Subject: [PATCH 015/845] Use requests module for HTTP/HTTPS This change introduces the use of requests in lieu of httplib2 to ensure proper handling of SSL termination. Implements: blueprint tls-verify Change-Id: If182f2addf26421873b8c3d2b60f8cba9b7a9450 --- neutronclient/client.py | 63 ++++++++++++++------------ neutronclient/common/utils.py | 5 +- neutronclient/tests/unit/test_auth.py | 44 ++++++++---------- neutronclient/tests/unit/test_cli20.py | 7 +-- neutronclient/tests/unit/test_http.py | 11 ++--- neutronclient/tests/unit/test_ssl.py | 8 ++-- neutronclient/v2_0/client.py | 14 +++--- requirements.txt | 1 + 8 files changed, 79 insertions(+), 74 deletions(-) diff --git a/neutronclient/client.py b/neutronclient/client.py index 82bd410de..c79cd0beb 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -21,7 +21,7 @@ import logging import os -import httplib2 +import requests from neutronclient.common import exceptions from neutronclient.common import utils @@ -29,15 +29,15 @@ _logger = logging.getLogger(__name__) -# httplib2 retries requests on socket.timeout which -# is not idempotent and can lead to orhan objects. -# See: https://code.google.com/p/httplib2/issues/detail?id=124 -httplib2.RETRIES = 1 - if os.environ.get('NEUTRONCLIENT_DEBUG'): ch = logging.StreamHandler() _logger.setLevel(logging.DEBUG) _logger.addHandler(ch) + _requests_log_level = logging.DEBUG +else: + _requests_log_level = logging.WARNING + +logging.getLogger("requests").setLevel(_requests_log_level) class ServiceCatalog(object): @@ -89,7 +89,7 @@ def url_for(self, attr=None, filter_value=None, return matching_endpoints[0][endpoint_type] -class HTTPClient(httplib2.Http): +class HTTPClient(object): """Handles the REST calls and responses, include authn.""" USER_AGENT = 'python-neutronclient' @@ -102,7 +102,6 @@ def __init__(self, username=None, tenant_name=None, tenant_id=None, auth_strategy='keystone', ca_cert=None, log_credentials=False, service_type='network', **kwargs): - super(HTTPClient, self).__init__(timeout=timeout, ca_certs=ca_cert) self.username = username self.tenant_name = tenant_name @@ -112,6 +111,7 @@ def __init__(self, username=None, tenant_name=None, tenant_id=None, self.service_type = service_type self.endpoint_type = endpoint_type self.region_name = region_name + self.timeout = timeout self.auth_token = token self.auth_tenant_id = None self.auth_user_id = None @@ -119,8 +119,10 @@ def __init__(self, username=None, tenant_name=None, tenant_id=None, self.endpoint_url = endpoint_url self.auth_strategy = auth_strategy self.log_credentials = log_credentials - # httplib2 overrides - self.disable_ssl_certificate_validation = insecure + if insecure: + self.verify_cert = False + else: + self.verify_cert = ca_cert if ca_cert else True def _cs_request(self, *args, **kwargs): kargs = {} @@ -147,7 +149,7 @@ def _cs_request(self, *args, **kwargs): utils.http_log_req(_logger, args, log_kargs) try: resp, body = self.request(*args, **kargs) - except httplib2.SSLHandshakeError as e: + except requests.exceptions.SSLError as e: raise exceptions.SslCertificateValidationError(reason=e) except Exception as e: # Wrap the low-level connection error (socket timeout, redirect @@ -155,11 +157,6 @@ def _cs_request(self, *args, **kwargs): # connection exception (it is excepted in the upper layers of code) _logger.debug("throwing ConnectionFailed : %s", e) raise exceptions.ConnectionFailed(reason=e) - finally: - # Temporary Fix for gate failures. RPC calls and HTTP requests - # seem to be stepping on each other resulting in bogus fd's being - # picked up for making http requests - self.connections.clear() utils.http_log_resp(_logger, resp, body) status_code = self.get_status_code(resp) if status_code == 401: @@ -181,6 +178,22 @@ def authenticate_and_fetch_endpoint_url(self): elif not self.endpoint_url: self.endpoint_url = self._get_endpoint_url() + def request(self, url, method, **kwargs): + kwargs.setdefault('headers', kwargs.get('headers', {})) + kwargs['headers']['User-Agent'] = self.USER_AGENT + kwargs['headers']['Accept'] = 'application/json' + if 'body' in kwargs: + kwargs['headers']['Content-Type'] = 'application/json' + kwargs['data'] = kwargs['body'] + del kwargs['body'] + resp = requests.request( + method, + url, + verify=self.verify_cert, + **kwargs) + + return resp, resp.text + def do_request(self, url, method, **kwargs): self.authenticate_and_fetch_endpoint_url() # Perform the request once. If we get a 401 back then it @@ -234,16 +247,10 @@ def _authenticate_keystone(self): raise exceptions.NoAuthURLProvided() token_url = self.auth_url + "/tokens" - - # Make sure we follow redirects when trying to reach Keystone - tmp_follow_all_redirects = self.follow_all_redirects - self.follow_all_redirects = True - try: - resp, resp_body = self._cs_request(token_url, "POST", - body=json.dumps(body), - content_type="application/json") - finally: - self.follow_all_redirects = tmp_follow_all_redirects + resp, resp_body = self._cs_request(token_url, "POST", + body=json.dumps(body), + content_type="application/json", + allow_redirects=True) status_code = self.get_status_code(resp) if status_code != 200: raise exceptions.Unauthorized(message=resp_body) @@ -305,10 +312,10 @@ def get_auth_info(self): def get_status_code(self, response): """Returns the integer status code from the response. - Either a Webob.Response (used in testing) or httplib.Response + Either a Webob.Response (used in testing) or requests.Response is returned. """ if hasattr(response, 'status_int'): return response.status_int else: - return response.status + return response.status_code diff --git a/neutronclient/common/utils.py b/neutronclient/common/utils.py index 338839ded..4478e8a24 100644 --- a/neutronclient/common/utils.py +++ b/neutronclient/common/utils.py @@ -178,7 +178,10 @@ def http_log_req(_logger, args, kwargs): def http_log_resp(_logger, resp, body): if not _logger.isEnabledFor(logging.DEBUG): return - _logger.debug(_("RESP:%(resp)s %(body)s\n"), {'resp': resp, 'body': body}) + _logger.debug(_("RESP:%(code)s %(headers)s %(body)s\n"), + {'code': resp.status_code, + 'headers': resp.headers, + 'body': body}) def _safe_encode_without_obj(data): diff --git a/neutronclient/tests/unit/test_auth.py b/neutronclient/tests/unit/test_auth.py index 84867d756..485d51132 100644 --- a/neutronclient/tests/unit/test_auth.py +++ b/neutronclient/tests/unit/test_auth.py @@ -15,11 +15,11 @@ # import copy -import httplib2 import json import uuid import mox +import requests import testtools from neutronclient import client @@ -68,6 +68,13 @@ } +def get_response(status_code, headers=None): + response = mox.Mox().CreateMock(requests.Response) + response.headers = headers or {} + response.status_code = status_code + return response + + class CLITestAuthNoAuth(testtools.TestCase): def setUp(self): @@ -86,8 +93,7 @@ def setUp(self): def test_get_noauth(self): self.mox.StubOutWithMock(self.client, "request") - res200 = self.mox.CreateMock(httplib2.Response) - res200.status = 200 + res200 = get_response(200) self.client.request( mox.StrContains(ENDPOINT_URL + '/resource'), 'GET', @@ -136,8 +142,7 @@ def test_reused_token_get_auth_info(self): def test_get_token(self): self.mox.StubOutWithMock(self.client, "request") - res200 = self.mox.CreateMock(httplib2.Response) - res200.status = 200 + res200 = get_response(200) self.client.request( AUTH_URL + '/tokens', 'POST', @@ -159,10 +164,8 @@ def test_refresh_token(self): self.client.auth_token = TOKEN self.client.endpoint_url = ENDPOINT_URL - res200 = self.mox.CreateMock(httplib2.Response) - res200.status = 200 - res401 = self.mox.CreateMock(httplib2.Response) - res401.status = 401 + res200 = get_response(200) + res401 = get_response(401) # If a token is expired, neutron server retruns 401 self.client.request( @@ -187,8 +190,7 @@ def test_refresh_token_no_auth_url(self): self.client.auth_token = TOKEN self.client.endpoint_url = ENDPOINT_URL - res401 = self.mox.CreateMock(httplib2.Response) - res401.status = 401 + res401 = get_response(401) # If a token is expired, neutron server returns 401 self.client.request( @@ -212,8 +214,7 @@ def test_get_endpoint_url(self): self.client.auth_token = TOKEN - res200 = self.mox.CreateMock(httplib2.Response) - res200.status = 200 + res200 = get_response(200) self.client.request( mox.StrContains(AUTH_URL + '/tokens/%s/endpoints' % TOKEN), 'GET', @@ -236,9 +237,7 @@ def test_use_given_endpoint_url(self): self.mox.StubOutWithMock(self.client, "request") self.client.auth_token = TOKEN - - res200 = self.mox.CreateMock(httplib2.Response) - res200.status = 200 + res200 = get_response(200) self.client.request( mox.StrContains(ENDPOINT_OVERRIDE + '/resource'), 'GET', @@ -255,9 +254,7 @@ def test_get_endpoint_url_other(self): self.mox.StubOutWithMock(self.client, "request") self.client.auth_token = TOKEN - - res200 = self.mox.CreateMock(httplib2.Response) - res200.status = 200 + res200 = get_response(200) self.client.request( mox.StrContains(AUTH_URL + '/tokens/%s/endpoints' % TOKEN), 'GET', @@ -274,10 +271,8 @@ def test_get_endpoint_url_failed(self): self.client.auth_token = TOKEN - res200 = self.mox.CreateMock(httplib2.Response) - res200.status = 200 - res401 = self.mox.CreateMock(httplib2.Response) - res401.status = 401 + res200 = get_response(200) + res401 = get_response(401) self.client.request( mox.StrContains(AUTH_URL + '/tokens/%s/endpoints' % TOKEN), 'GET', @@ -433,8 +428,7 @@ def verify_credentials(body): self.mox.StubOutWithMock(self.client, "request") self.mox.StubOutWithMock(utils, "http_log_req") - res200 = self.mox.CreateMock(httplib2.Response) - res200.status = 200 + res200 = get_response(200) utils.http_log_req(mox.IgnoreArg(), mox.IgnoreArg(), mox.Func( verify_no_credentials)) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index b9cc2d5ba..51ff0bf00 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -48,8 +48,9 @@ def make_string(self): class MyResp(object): - def __init__(self, status, reason=None): - self.status = status + def __init__(self, status_code, headers=None, reason=None): + self.status_code = status_code + self.headers = headers or {} self.reason = reason @@ -533,7 +534,7 @@ def test_do_request_error_without_response_body(self): end_url('/test', query=expect_query, format=self.format), 'PUT', body='', headers=mox.ContainsKeyValue('X-Auth-Token', 'token') - ).AndReturn((MyResp(400, 'An error'), '')) + ).AndReturn((MyResp(400, reason='An error'), '')) self.mox.ReplayAll() error = self.assertRaises(exceptions.NeutronClientException, diff --git a/neutronclient/tests/unit/test_http.py b/neutronclient/tests/unit/test_http.py index 1114f33be..77a3a4a75 100644 --- a/neutronclient/tests/unit/test_http.py +++ b/neutronclient/tests/unit/test_http.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -import httplib2 import mox import testtools @@ -33,13 +32,13 @@ def setUp(self): super(TestHTTPClient, self).setUp() self.mox = mox.Mox() - self.mox.StubOutWithMock(httplib2.Http, 'request') + self.mox.StubOutWithMock(HTTPClient, 'request') self.addCleanup(self.mox.UnsetStubs) self.http = HTTPClient(token=AUTH_TOKEN, endpoint_url=END_URL) def test_request_error(self): - httplib2.Http.request( + HTTPClient.request( URL, METHOD, headers=mox.IgnoreArg() ).AndRaise(Exception('error msg')) self.mox.ReplayAll() @@ -54,7 +53,7 @@ def test_request_error(self): def test_request_success(self): rv_should_be = MyResp(200), 'test content' - httplib2.Http.request( + HTTPClient.request( URL, METHOD, headers=mox.IgnoreArg() ).AndReturn(rv_should_be) self.mox.ReplayAll() @@ -64,7 +63,7 @@ def test_request_success(self): def test_request_unauthorized(self): rv_should_be = MyResp(401), 'unauthorized message' - httplib2.Http.request( + HTTPClient.request( URL, METHOD, headers=mox.IgnoreArg() ).AndReturn(rv_should_be) self.mox.ReplayAll() @@ -76,7 +75,7 @@ def test_request_unauthorized(self): def test_request_forbidden_is_returned_to_caller(self): rv_should_be = MyResp(403), 'forbidden message' - httplib2.Http.request( + HTTPClient.request( URL, METHOD, headers=mox.IgnoreArg() ).AndReturn(rv_should_be) self.mox.ReplayAll() diff --git a/neutronclient/tests/unit/test_ssl.py b/neutronclient/tests/unit/test_ssl.py index 9f3970dd5..81f831ba7 100644 --- a/neutronclient/tests/unit/test_ssl.py +++ b/neutronclient/tests/unit/test_ssl.py @@ -14,8 +14,8 @@ # under the License. import fixtures -import httplib2 import mox +import requests import testtools from neutronclient.client import HTTPClient @@ -126,10 +126,10 @@ def test_client_manager_properly_creates_httpclient_instance(self): def test_proper_exception_is_raised_when_cert_validation_fails(self): http = HTTPClient(token=AUTH_TOKEN, endpoint_url=END_URL) - self.mox.StubOutWithMock(httplib2.Http, 'request') - httplib2.Http.request( + self.mox.StubOutWithMock(HTTPClient, 'request') + HTTPClient.request( URL, METHOD, headers=mox.IgnoreArg() - ).AndRaise(httplib2.SSLHandshakeError) + ).AndRaise(requests.exceptions.SSLError) self.mox.ReplayAll() self.assertRaises( diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index c1d441b6f..cf1a214f6 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -14,11 +14,11 @@ # under the License. # -import httplib import logging import time import urllib +import requests import six.moves.urllib.parse as urlparse from neutronclient import client @@ -1231,10 +1231,10 @@ def do_request(self, method, action, body=None, headers=None, params=None): self.httpclient.content_type = self.content_type() resp, replybody = self.httpclient.do_request(action, method, body=body) status_code = self.get_status_code(resp) - if status_code in (httplib.OK, - httplib.CREATED, - httplib.ACCEPTED, - httplib.NO_CONTENT): + if status_code in (requests.codes.ok, + requests.codes.created, + requests.codes.accepted, + requests.codes.no_content): return self.deserialize(replybody, status_code) else: if not replybody: @@ -1247,13 +1247,13 @@ def get_auth_info(self): def get_status_code(self, response): """Returns the integer status code from the response. - Either a Webob.Response (used in testing) or httplib.Response + Either a Webob.Response (used in testing) or requests.Response is returned. """ if hasattr(response, 'status_int'): return response.status_int else: - return response.status + return response.status_code def serialize(self, data): """Serializes a dictionary into either xml or json. diff --git a/requirements.txt b/requirements.txt index 09b4537d2..970993aa0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,6 +4,7 @@ cliff>=1.4.3 httplib2>=0.7.5 iso8601>=0.1.9 netaddr>=0.7.6 +requests>=1.1 simplejson>=2.0.9 six>=1.5.2 Babel>=1.3 From ed5ea512ca481478c95f3bc0f8120d102e4da9c7 Mon Sep 17 00:00:00 2001 From: Phil Day Date: Thu, 6 Mar 2014 13:07:49 +0000 Subject: [PATCH 016/845] Allow user ID for authentication In Keystone V3 user names are no longer necessarily unique across domains. A user can still authenticate a user in the non default domain via the V2 API providng they use IDs instead of names. Tenant_ID is already supported, this change adds support for user ID Closes-Bug: #1299807 Change-Id: I22fdd9a6749f7dfbdd2dc8313fddc81e5ea0b753 --- neutronclient/client.py | 19 +++++++++++------- neutronclient/common/clientmanager.py | 9 +++++++-- neutronclient/shell.py | 16 +++++++++++---- neutronclient/tests/unit/test_auth.py | 27 ++++++++++++++++---------- neutronclient/tests/unit/test_shell.py | 4 +++- neutronclient/tests/unit/test_ssl.py | 2 ++ neutronclient/v2_0/client.py | 1 + 7 files changed, 54 insertions(+), 24 deletions(-) diff --git a/neutronclient/client.py b/neutronclient/client.py index 82bd410de..b96f2564a 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -94,7 +94,8 @@ class HTTPClient(httplib2.Http): USER_AGENT = 'python-neutronclient' - def __init__(self, username=None, tenant_name=None, tenant_id=None, + def __init__(self, username=None, user_id=None, + tenant_name=None, tenant_id=None, password=None, auth_url=None, token=None, region_name=None, timeout=None, endpoint_url=None, insecure=False, @@ -105,6 +106,7 @@ def __init__(self, username=None, tenant_name=None, tenant_id=None, super(HTTPClient, self).__init__(timeout=timeout, ca_certs=ca_cert) self.username = username + self.user_id = user_id self.tenant_name = tenant_name self.tenant_id = tenant_id self.password = password @@ -219,15 +221,18 @@ def _extract_service_catalog(self, body): endpoint_type=self.endpoint_type) def _authenticate_keystone(self): + if self.user_id: + creds = {'userId': self.user_id, + 'password': self.password} + else: + creds = {'username': self.username, + 'password': self.password} + if self.tenant_id: - body = {'auth': {'passwordCredentials': - {'username': self.username, - 'password': self.password, }, + body = {'auth': {'passwordCredentials': creds, 'tenantId': self.tenant_id, }, } else: - body = {'auth': {'passwordCredentials': - {'username': self.username, - 'password': self.password, }, + body = {'auth': {'passwordCredentials': creds, 'tenantName': self.tenant_name, }, } if self.auth_url is None: diff --git a/neutronclient/common/clientmanager.py b/neutronclient/common/clientmanager.py index b36d21e5a..db6883b86 100644 --- a/neutronclient/common/clientmanager.py +++ b/neutronclient/common/clientmanager.py @@ -52,8 +52,11 @@ class ClientManager(object): def __init__(self, token=None, url=None, auth_url=None, endpoint_type=None, - tenant_name=None, tenant_id=None, - username=None, password=None, + tenant_name=None, + tenant_id=None, + username=None, + user_id=None, + password=None, region_name=None, api_version=None, auth_strategy=None, @@ -70,6 +73,7 @@ def __init__(self, token=None, url=None, self._tenant_name = tenant_name self._tenant_id = tenant_id self._username = username + self._user_id = user_id self._password = password self._region_name = region_name self._api_version = api_version @@ -84,6 +88,7 @@ def initialize(self): if not self._url: httpclient = client.HTTPClient( username=self._username, + user_id=self._user_id, tenant_name=self._tenant_name, tenant_id=self._tenant_id, password=self._password, diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 05e91ac52..c1cba5c5b 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -398,7 +398,7 @@ def build_option_parser(self, description, version): parser.add_argument( '--os-tenant-id', metavar='', default=env('OS_TENANT_ID'), - help=_('Authentication tenant name (Env: OS_TENANT_ID)')) + help=_('Authentication tenant ID (Env: OS_TENANT_ID)')) parser.add_argument( '--os-username', metavar='', @@ -408,6 +408,11 @@ def build_option_parser(self, description, version): '--os_username', help=argparse.SUPPRESS) + parser.add_argument( + '--os-user-id', metavar='', + default=env('OS_USER_ID'), + help=_('Authentication user ID (Env: OS_USER_ID)')) + parser.add_argument( '--os-password', metavar='', default=utils.env('OS_PASSWORD'), @@ -590,10 +595,12 @@ def authenticate_user(self): else: # Validate password flow auth - if not self.options.os_username: + if (not self.options.os_username + and not self.options.os_user_id): raise exc.CommandError( - _("You must provide a username via" - " either --os-username or env[OS_USERNAME]")) + _("You must provide a username or user ID via" + " --os-username, env[OS_USERNAME] or" + " --os-user_id, env[OS_USER_ID]")) if not self.options.os_password: raise exc.CommandError( @@ -624,6 +631,7 @@ def authenticate_user(self): tenant_name=self.options.os_tenant_name, tenant_id=self.options.os_tenant_id, username=self.options.os_username, + user_id=self.options.os_user_id, password=self.options.os_password, region_name=self.options.os_region_name, api_version=self.api_version, diff --git a/neutronclient/tests/unit/test_auth.py b/neutronclient/tests/unit/test_auth.py index 84867d756..5cbd3e89b 100644 --- a/neutronclient/tests/unit/test_auth.py +++ b/neutronclient/tests/unit/test_auth.py @@ -28,8 +28,9 @@ USERNAME = 'testuser' +USER_ID = 'testuser_id' TENANT_NAME = 'testtenant' -TENANT_ID = 'testtenantid' +TENANT_ID = 'testtenant_id' PASSWORD = 'password' AUTH_URL = 'authurl' ENDPOINT_URL = 'localurl' @@ -101,8 +102,10 @@ def test_get_noauth(self): class CLITestAuthKeystone(testtools.TestCase): - # Auth Body expected when using tenant name - auth_type = 'tenantName' + # Auth Body expected + auth_body = ('{"auth": {"tenantName": "testtenant", ' + '"passwordCredentials": ' + '{"username": "testuser", "password": "password"}}}') def setUp(self): """Prepare the test environment.""" @@ -121,7 +124,6 @@ def test_reused_token_get_auth_info(self): instantiated with predefined token. """ client_ = client.HTTPClient(username=USERNAME, - tenant_id=TENANT_ID, tenant_name=TENANT_NAME, token=TOKEN, password=PASSWORD, @@ -141,7 +143,7 @@ def test_get_token(self): self.client.request( AUTH_URL + '/tokens', 'POST', - body=mox.StrContains(self.auth_type), headers=mox.IsA(dict) + body=self.auth_body, headers=mox.IsA(dict) ).AndReturn((res200, json.dumps(KS_TOKEN_RESULT))) self.client.request( mox.StrContains(ENDPOINT_URL + '/resource'), 'GET', @@ -453,13 +455,15 @@ def verify_credentials(body): class CLITestAuthKeystoneWithId(CLITestAuthKeystone): - # Auth Body expected when using tenant Id - auth_type = 'tenantId' + # Auth Body expected + auth_body = ('{"auth": {"passwordCredentials": ' + '{"password": "password", "userId": "testuser_id"}, ' + '"tenantId": "testtenant_id"}}') def setUp(self): """Prepare the test environment.""" super(CLITestAuthKeystoneWithId, self).setUp() - self.client = client.HTTPClient(username=USERNAME, + self.client = client.HTTPClient(user_id=USER_ID, tenant_id=TENANT_ID, password=PASSWORD, auth_url=AUTH_URL, @@ -468,13 +472,16 @@ def setUp(self): class CLITestAuthKeystoneWithIdandName(CLITestAuthKeystone): - # Auth Body expected when using tenant Id - auth_type = 'tenantId' + # Auth Body expected + auth_body = ('{"auth": {"passwordCredentials": ' + '{"password": "password", "userId": "testuser_id"}, ' + '"tenantId": "testtenant_id"}}') def setUp(self): """Prepare the test environment.""" super(CLITestAuthKeystoneWithIdandName, self).setUp() self.client = client.HTTPClient(username=USERNAME, + user_id=USER_ID, tenant_id=TENANT_ID, tenant_name=TENANT_NAME, password=PASSWORD, diff --git a/neutronclient/tests/unit/test_shell.py b/neutronclient/tests/unit/test_shell.py index e5fe54252..9c50fe938 100644 --- a/neutronclient/tests/unit/test_shell.py +++ b/neutronclient/tests/unit/test_shell.py @@ -117,13 +117,15 @@ def test_unknown_auth_strategy(self): 'either --os-url or env[OS_URL]', stderr.strip()) def test_auth(self): + #import pdb; pdb.set_trace() neutron_shell = openstack_shell.NeutronShell('2.0') self.addCleanup(self.mox.UnsetStubs) self.mox.StubOutWithMock(clientmanager.ClientManager, '__init__') self.mox.StubOutWithMock(neutron_shell, 'run_subcommand') clientmanager.ClientManager.__init__( token='', url='', auth_url='http://127.0.0.1:5000/', - tenant_name='test', tenant_id='tenant_id', username='test', + tenant_name='test', tenant_id='tenant_id', + username='test', user_id='', password='test', region_name='', api_version={'network': '2.0'}, auth_strategy='keystone', service_type='network', endpoint_type='publicURL', insecure=False, ca_cert=None, diff --git a/neutronclient/tests/unit/test_ssl.py b/neutronclient/tests/unit/test_ssl.py index 9f3970dd5..a53e33a33 100644 --- a/neutronclient/tests/unit/test_ssl.py +++ b/neutronclient/tests/unit/test_ssl.py @@ -61,6 +61,7 @@ def test_ca_cert_passed(self): token=mox.IgnoreArg(), url=mox.IgnoreArg(), username=mox.IgnoreArg(), + user_id=mox.IgnoreArg(), log_credentials=mox.IgnoreArg(), ) openstack_shell.NeutronShell.interact().AndReturn(0) @@ -91,6 +92,7 @@ def test_ca_cert_passed_as_env_var(self): token=mox.IgnoreArg(), url=mox.IgnoreArg(), username=mox.IgnoreArg(), + user_id=mox.IgnoreArg(), log_credentials=mox.IgnoreArg(), ) openstack_shell.NeutronShell.interact().AndReturn(0) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index c1d441b6f..9f14652c6 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -108,6 +108,7 @@ class Client(object): """Client for the OpenStack Neutron v2.0 API. :param string username: Username for authentication. (optional) + :param string user_id: User ID for authentication. (optional) :param string password: Password for authentication. (optional) :param string token: Token for authentication. (optional) :param string tenant_name: Tenant name. (optional) From e135df710868b2c75fff6d6c0ad8a4f3b0118269 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Wed, 23 Apr 2014 17:02:02 +0000 Subject: [PATCH 017/845] Updated from global requirements Change-Id: Ic71ae06e070f186ede1bdecd685498fb5a5c3103 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 09b4537d2..1ead9e75a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,5 +5,5 @@ httplib2>=0.7.5 iso8601>=0.1.9 netaddr>=0.7.6 simplejson>=2.0.9 -six>=1.5.2 +six>=1.6.0 Babel>=1.3 From 3bfc24ab460ea3c848574eda96a2a39082e137c1 Mon Sep 17 00:00:00 2001 From: Matt Riedemann Date: Tue, 29 Apr 2014 09:12:25 -0700 Subject: [PATCH 018/845] Remove httplib2 requirement There are no imports from httplib2 and everything seems to be converted over to using the requests library now, so remove the extraneous dependency. Change-Id: I6a2a9543ea8f5da29494e1dba7659ec02a43deea --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e548bf83f..7c118d3a3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,6 @@ pbr>=0.6,!=0.7,<1.0 argparse cliff>=1.4.3 -httplib2>=0.7.5 iso8601>=0.1.9 netaddr>=0.7.6 requests>=1.1 From 45ffced148c7415ac1be00a733e7a5a55b7afc33 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Wed, 30 Apr 2014 02:46:40 +0000 Subject: [PATCH 019/845] Updated from global requirements Change-Id: Ie4d964feb6111f173338e510ed83d7f1d79f7dbb --- setup.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/setup.py b/setup.py index 70c2b3f32..736375744 100644 --- a/setup.py +++ b/setup.py @@ -17,6 +17,14 @@ # THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT import setuptools +# In python < 2.7.4, a lazy loading of package `pbr` will break +# setuptools if some other modules registered functions in `atexit`. +# solution from: http://bugs.python.org/issue15881#msg170215 +try: + import multiprocessing # noqa +except ImportError: + pass + setuptools.setup( setup_requires=['pbr'], pbr=True) From 91bea9479d8b3ab4912c4b4c5e03f05e67b81a5a Mon Sep 17 00:00:00 2001 From: armando-migliaccio Date: Tue, 22 Apr 2014 16:16:54 -0700 Subject: [PATCH 020/845] Suppress expected help messages caused by wrong CLI inputs There are negative tests for the NSX gateway feature. This patch introduces the ability to suppress the warning messages caused by running commands with wrong parameters. This keeps the console output clean of spurious errors. Change-Id: Idc78442969b3e06ecef9999a7edfc6544ed3a376 Closes-bug: #1311348 --- neutronclient/tests/unit/test_cli20.py | 14 ++++++++++++++ .../unit/test_cli20_nsx_networkgateway.py | 18 ++++++++++-------- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 51ff0bf00..3f896adfc 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -16,8 +16,11 @@ import urllib +import contextlib +import cStringIO import fixtures import mox +import sys import testtools from neutronclient.common import constants @@ -32,6 +35,17 @@ ENDURL = 'localurl' +@contextlib.contextmanager +def capture_std_streams(): + fake_stdout, fake_stderr = cStringIO.StringIO(), cStringIO.StringIO() + stdout, stderr = sys.stdout, sys.stderr + try: + sys.stdout, sys.stderr = fake_stdout, fake_stderr + yield fake_stdout, fake_stderr + finally: + sys.stdout, sys.stderr = stdout, stderr + + class FakeStdout: def __init__(self): diff --git a/neutronclient/tests/unit/test_cli20_nsx_networkgateway.py b/neutronclient/tests/unit/test_cli20_nsx_networkgateway.py index 2ff321e8e..9db388faa 100644 --- a/neutronclient/tests/unit/test_cli20_nsx_networkgateway.py +++ b/neutronclient/tests/unit/test_cli20_nsx_networkgateway.py @@ -146,10 +146,11 @@ def _test_create_gateway_device(self, args.extend(['--client-certificate', client_certificate_file]) args.append(name) if must_raise: - self.assertRaises( - SystemExit, self._test_create_resource, - self.dev_resource, cmd, name, myid, args, - position_names, position_values, extra_body=extra_body) + with test_cli20.capture_std_streams(): + self.assertRaises( + SystemExit, self._test_create_resource, + self.dev_resource, cmd, name, myid, args, + position_names, position_values, extra_body=extra_body) else: self._test_create_resource( self.dev_resource, cmd, name, myid, args, @@ -213,10 +214,11 @@ def process_arg(argname, arg): args.extend(['--client-certificate-file', client_certificate_file]) if must_raise: - self.assertRaises( - SystemExit, self._test_update_resource, - self.dev_resource, cmd, myid, args, - extrafields=extra_body) + with test_cli20.capture_std_streams(): + self.assertRaises( + SystemExit, self._test_update_resource, + self.dev_resource, cmd, myid, args, + extrafields=extra_body) else: self._test_update_resource( self.dev_resource, cmd, myid, args, From f8bb29232184edf63bcf2cc1aacb7de1a57fbf0b Mon Sep 17 00:00:00 2001 From: Christian Berendt Date: Mon, 28 Apr 2014 13:17:04 +0200 Subject: [PATCH 021/845] enable sorting support for agent listing Change-Id: Ic11d0f9a0a53d765f85f8ffe3f12d50455236394 Closes-Bug: #1313607 --- neutronclient/neutron/v2_0/agent.py | 1 + 1 file changed, 1 insertion(+) diff --git a/neutronclient/neutron/v2_0/agent.py b/neutronclient/neutron/v2_0/agent.py index af7b6cfbf..ed813b918 100644 --- a/neutronclient/neutron/v2_0/agent.py +++ b/neutronclient/neutron/v2_0/agent.py @@ -33,6 +33,7 @@ class ListAgent(neutronV20.ListCommand): log = logging.getLogger(__name__ + '.ListAgent') list_columns = ['id', 'agent_type', 'host', 'alive', 'admin_state_up'] _formatters = {'heartbeat_timestamp': _format_timestamp} + sorting_support = True def extend_list(self, data, parsed_args): for agent in data: From 44d45203e828b4812638da970dc9acb9ff4065d9 Mon Sep 17 00:00:00 2001 From: Ihar Hrachyshka Date: Tue, 27 May 2014 10:22:49 +0200 Subject: [PATCH 022/845] Synced jsonutils from oslo-incubator The sync includes change that drastically enhances performance on Python 2.6 with fresh simplejson library installed. The latest commit in oslo-incubator: - 0f4586c0076183c6356eec682c8a593648125abd Change-Id: Ib3dc0b713ed90396919feba018772243b3b9c90f Closes-Bug: 1314129 --- neutronclient/openstack/common/__init__.py | 17 +++++++++++ neutronclient/openstack/common/jsonutils.py | 33 +++++++++++++++------ 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/neutronclient/openstack/common/__init__.py b/neutronclient/openstack/common/__init__.py index e69de29bb..d1223eaf7 100644 --- a/neutronclient/openstack/common/__init__.py +++ b/neutronclient/openstack/common/__init__.py @@ -0,0 +1,17 @@ +# +# 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 six + + +six.add_move(six.MovedModule('mox', 'mox', 'mox3.mox')) diff --git a/neutronclient/openstack/common/jsonutils.py b/neutronclient/openstack/common/jsonutils.py index 2930dc33b..b1a617bef 100644 --- a/neutronclient/openstack/common/jsonutils.py +++ b/neutronclient/openstack/common/jsonutils.py @@ -31,17 +31,29 @@ ''' +import codecs import datetime import functools import inspect import itertools -import json -import types -import xmlrpclib +import sys + +if sys.version_info < (2, 7): + # On Python <= 2.6, json module is not C boosted, so try to use + # simplejson module if available + try: + import simplejson as json + except ImportError: + import json +else: + import json import six +import six.moves.xmlrpc_client as xmlrpclib +from neutronclient.openstack.common import gettextutils from neutronclient.openstack.common import importutils +from neutronclient.openstack.common import strutils from neutronclient.openstack.common import timeutils netaddr = importutils.try_import("netaddr") @@ -52,7 +64,8 @@ inspect.iscode, inspect.isbuiltin, inspect.isroutine, inspect.isabstract] -_simple_types = (types.NoneType, int, basestring, bool, float, long) +_simple_types = (six.string_types + six.integer_types + + (type(None), bool, float)) def to_primitive(value, convert_instances=False, convert_datetime=True, @@ -117,7 +130,7 @@ def to_primitive(value, convert_instances=False, convert_datetime=True, level=level, max_depth=max_depth) if isinstance(value, dict): - return dict((k, recursive(v)) for k, v in value.iteritems()) + return dict((k, recursive(v)) for k, v in six.iteritems(value)) elif isinstance(value, (list, tuple)): return [recursive(lv) for lv in value] @@ -129,6 +142,8 @@ def to_primitive(value, convert_instances=False, convert_datetime=True, if convert_datetime and isinstance(value, datetime.datetime): return timeutils.strtime(value) + elif isinstance(value, gettextutils.Message): + return value.data elif hasattr(value, 'iteritems'): return recursive(dict(value.iteritems()), level=level + 1) elif hasattr(value, '__iter__'): @@ -153,12 +168,12 @@ def dumps(value, default=to_primitive, **kwargs): return json.dumps(value, default=default, **kwargs) -def loads(s): - return json.loads(s) +def loads(s, encoding='utf-8'): + return json.loads(strutils.safe_decode(s, encoding)) -def load(s): - return json.load(s) +def load(fp, encoding='utf-8'): + return json.load(codecs.getreader(encoding)(fp)) try: From cbdd56da309ab0b610ed1f383f11bea26fa7b8c0 Mon Sep 17 00:00:00 2001 From: jichenjc Date: Tue, 22 Apr 2014 14:20:02 +0800 Subject: [PATCH 023/845] Add OverQuotaClient as exception to neutronclient In nova, there is no way to explictly knows whether the neutron fails due to over quota or no more floating ips and fixed ips. so nova need to check whether the return value is 409 in order to raise an over quota exception. It's easier in neutronclient to raise the exception then nova will handle it. Related-Bug: #1210598 Change-Id: I8788993578ac872da9f676fe3e2fb8f98414289d --- neutronclient/common/exceptions.py | 4 ++++ neutronclient/tests/unit/test_cli20.py | 1 + 2 files changed, 5 insertions(+) diff --git a/neutronclient/common/exceptions.py b/neutronclient/common/exceptions.py index f4085d319..65f1c26e4 100644 --- a/neutronclient/common/exceptions.py +++ b/neutronclient/common/exceptions.py @@ -139,6 +139,10 @@ class IpAddressInUseClient(Conflict): pass +class OverQuotaClient(Conflict): + pass + + # TODO(amotoki): It is unused in Neutron, but it is referred to # in Horizon code. After Horizon code is updated, remove it. class AlreadyAttachedClient(Conflict): diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 51ff0bf00..e49605d3e 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -591,6 +591,7 @@ def test_exception_handler_v20_neutron_known_error(self): exceptions.IpAddressGenerationFailureClient, 409), ('ExternalIpAddressExhausted', exceptions.ExternalIpAddressExhaustedClient, 400), + ('OverQuota', exceptions.OverQuotaClient, 409), ] error_msg = 'dummy exception message' From 8845ed254acbbd5ee1ff02688ea482996c58ef5d Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sat, 31 May 2014 21:40:38 -0700 Subject: [PATCH 024/845] Removed now unnecesary workaround for PyPy Related-bug: #1290562 Change-Id: I3fbfdcecfa19593729b1cb14685201207abc64a6 --- tox.ini | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tox.ini b/tox.ini index 65630888b..a498e5261 100644 --- a/tox.ini +++ b/tox.ini @@ -14,11 +14,6 @@ deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt commands = python setup.py testr --testr-args='{posargs}' -[testenv:pypy] -deps = setuptools<3.2 - -r{toxinidir}/requirements.txt - -r{toxinidir}/test-requirements.txt - [testenv:pep8] commands = flake8 distribute = false From 9230e6f42aee09cd74d1be01a9317c560a19a8ee Mon Sep 17 00:00:00 2001 From: Carl Baldwin Date: Wed, 28 May 2014 16:29:19 +0000 Subject: [PATCH 025/845] Add ability to update certain attributes in a subnet In testing the ability to update allocation_pools, I found that it was not possible to do it. This commits adds the ability to update those attributes which are allowed in a PUT operation. Added --enable-dhcp to the list of options. The reason for adding it was so that the help message would clearly show that it can be used with a subnet-update command. Otherwise, the end user may be confused thinking there is no way to use subnet-update to enable dhcp for a subnet where it is disabled. DocImpact Closes-Bug: #1324189 Change-Id: I52d05ec2284b6910a33df4a5cb7dc3888737acb6 --- neutronclient/neutron/v2_0/subnet.py | 126 +++++++++++------- neutronclient/tests/unit/test_cli20_subnet.py | 45 +++++++ 2 files changed, 121 insertions(+), 50 deletions(-) diff --git a/neutronclient/neutron/v2_0/subnet.py b/neutronclient/neutron/v2_0/subnet.py index de0569f30..869fa1f4a 100644 --- a/neutronclient/neutron/v2_0/subnet.py +++ b/neutronclient/neutron/v2_0/subnet.py @@ -47,6 +47,72 @@ def _format_host_routes(subnet): return '' +def add_updatable_arguments(parser): + parser.add_argument( + '--name', + help=_('Name of this subnet')) + parser.add_argument( + '--gateway', metavar='GATEWAY_IP', + help=_('Gateway ip of this subnet')) + parser.add_argument( + '--no-gateway', + action='store_true', + help=_('No distribution of gateway')) + parser.add_argument( + '--allocation-pool', metavar='start=IP_ADDR,end=IP_ADDR', + action='append', dest='allocation_pools', type=utils.str2dict, + help=_('Allocation pool IP addresses for this subnet ' + '(This option can be repeated)')) + parser.add_argument( + '--allocation_pool', + action='append', dest='allocation_pools', type=utils.str2dict, + help=argparse.SUPPRESS) + parser.add_argument( + '--host-route', metavar='destination=CIDR,nexthop=IP_ADDR', + action='append', dest='host_routes', type=utils.str2dict, + help=_('Additional route (This option can be repeated)')) + parser.add_argument( + '--dns-nameserver', metavar='DNS_NAMESERVER', + action='append', dest='dns_nameservers', + help=_('DNS name server for this subnet ' + '(This option can be repeated)')) + parser.add_argument( + '--disable-dhcp', + action='store_true', + help=_('Disable DHCP for this subnet')) + parser.add_argument( + '--enable-dhcp', + action='store_true', + help=_('Enable DHCP for this subnet')) + + +def updatable_args2body(parsed_args, body): + if parsed_args.gateway and parsed_args.no_gateway: + raise exceptions.CommandError(_("--gateway option and " + "--no-gateway option can " + "not be used same time")) + if parsed_args.disable_dhcp and parsed_args.enable_dhcp: + raise exceptions.CommandError(_("--enable-dhcp and --disable-dhcp can " + "not be used in the same command.")) + + if parsed_args.no_gateway: + body['subnet'].update({'gateway_ip': None}) + if parsed_args.gateway: + body['subnet'].update({'gateway_ip': parsed_args.gateway}) + if parsed_args.name: + body['subnet'].update({'name': parsed_args.name}) + if parsed_args.disable_dhcp: + body['subnet'].update({'enable_dhcp': False}) + if parsed_args.enable_dhcp: + body['subnet'].update({'enable_dhcp': True}) + if parsed_args.allocation_pools: + body['subnet']['allocation_pools'] = parsed_args.allocation_pools + if parsed_args.host_routes: + body['subnet']['host_routes'] = parsed_args.host_routes + if parsed_args.dns_nameservers: + body['subnet']['dns_nameservers'] = parsed_args.dns_nameservers + + class ListSubnet(neutronV20.ListCommand): """List subnets that belong to a given tenant.""" @@ -74,9 +140,7 @@ class CreateSubnet(neutronV20.CreateCommand): log = logging.getLogger(__name__ + '.CreateSubnet') def add_known_arguments(self, parser): - parser.add_argument( - '--name', - help=_('Name of this subnet')) + add_updatable_arguments(parser) parser.add_argument( '--ip-version', type=int, @@ -87,35 +151,6 @@ def add_known_arguments(self, parser): type=int, choices=[4, 6], help=argparse.SUPPRESS) - parser.add_argument( - '--gateway', metavar='GATEWAY_IP', - help=_('Gateway ip of this subnet')) - parser.add_argument( - '--no-gateway', - action='store_true', - help=_('No distribution of gateway')) - parser.add_argument( - '--allocation-pool', metavar='start=IP_ADDR,end=IP_ADDR', - action='append', dest='allocation_pools', type=utils.str2dict, - help=_('Allocation pool IP addresses for this subnet ' - '(This option can be repeated)')) - parser.add_argument( - '--allocation_pool', - action='append', dest='allocation_pools', type=utils.str2dict, - help=argparse.SUPPRESS) - parser.add_argument( - '--host-route', metavar='destination=CIDR,nexthop=IP_ADDR', - action='append', dest='host_routes', type=utils.str2dict, - help=_('Additional route (This option can be repeated)')) - parser.add_argument( - '--dns-nameserver', metavar='DNS_NAMESERVER', - action='append', dest='dns_nameservers', - help=_('DNS name server for this subnet ' - '(This option can be repeated)')) - parser.add_argument( - '--disable-dhcp', - action='store_true', - help=_('Disable DHCP for this subnet')) parser.add_argument( 'network_id', metavar='NETWORK', help=_('Network id or name this subnet belongs to')) @@ -130,26 +165,9 @@ def args2body(self, parsed_args): 'network_id': _network_id, 'ip_version': parsed_args.ip_version, }, } - if parsed_args.gateway and parsed_args.no_gateway: - raise exceptions.CommandError(_("--gateway option and " - "--no-gateway option can " - "not be used same time")) - if parsed_args.no_gateway: - body['subnet'].update({'gateway_ip': None}) - if parsed_args.gateway: - body['subnet'].update({'gateway_ip': parsed_args.gateway}) + updatable_args2body(parsed_args, body) if parsed_args.tenant_id: body['subnet'].update({'tenant_id': parsed_args.tenant_id}) - if parsed_args.name: - body['subnet'].update({'name': parsed_args.name}) - if parsed_args.disable_dhcp: - body['subnet'].update({'enable_dhcp': False}) - if parsed_args.allocation_pools: - body['subnet']['allocation_pools'] = parsed_args.allocation_pools - if parsed_args.host_routes: - body['subnet']['host_routes'] = parsed_args.host_routes - if parsed_args.dns_nameservers: - body['subnet']['dns_nameservers'] = parsed_args.dns_nameservers return body @@ -166,3 +184,11 @@ class UpdateSubnet(neutronV20.UpdateCommand): resource = 'subnet' log = logging.getLogger(__name__ + '.UpdateSubnet') + + def add_known_arguments(self, parser): + add_updatable_arguments(parser) + + def args2body(self, parsed_args): + body = {'subnet': {}} + updatable_args2body(parsed_args, body) + return body diff --git a/neutronclient/tests/unit/test_cli20_subnet.py b/neutronclient/tests/unit/test_cli20_subnet.py index 57b5901fc..ffbfd851a 100644 --- a/neutronclient/tests/unit/test_cli20_subnet.py +++ b/neutronclient/tests/unit/test_cli20_subnet.py @@ -16,6 +16,7 @@ import sys +from neutronclient.common import exceptions from neutronclient.neutron.v2_0 import subnet from neutronclient.tests.unit import test_cli20 @@ -72,6 +73,24 @@ def test_create_subnet_with_bad_gateway_option(self): return self.fail('No exception for bad gateway option') + def test_create_subnet_with_enable_and_disable_dhcp(self): + """Create sbunet: --enable-dhcp and --disable-dhcp.""" + resource = 'subnet' + cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + netid = 'netid' + cidr = 'cidrvalue' + args = ['--enable-dhcp', '--disable-dhcp', netid, cidr] + position_names = ['ip_version', 'network_id', 'cidr', 'gateway_ip'] + position_values = [4, netid, cidr, None] + try: + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + except exceptions.CommandError: + return + self.fail('No exception for --enable-dhcp --disable-dhcp option') + def test_create_subnet_tenant(self): """Create subnet: --tenant_id tenantid netid cidr.""" resource = 'subnet' @@ -380,6 +399,32 @@ def test_update_subnet_known_option_after_id(self): {'name': 'myname', } ) + def test_update_subnet_allocation_pools(self): + """Update subnet: myid --name myname --tags a b.""" + resource = 'subnet' + cmd = subnet.UpdateSubnet(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--allocation-pool', + 'start=1.2.0.2,end=1.2.0.127', + '--request-format', 'json'], + {'allocation_pools': [{'start': '1.2.0.2', + 'end': '1.2.0.127'}]} + ) + + def test_update_subnet_enable_disable_dhcp(self): + """Update sbunet: --enable-dhcp and --disable-dhcp.""" + resource = 'subnet' + cmd = subnet.UpdateSubnet(test_cli20.MyApp(sys.stdout), None) + try: + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--name', 'myname', + '--enable-dhcp', '--disable-dhcp'], + {'name': 'myname', } + ) + except exceptions.CommandError: + return + self.fail('No exception for --enable-dhcp --disable-dhcp option') + def test_show_subnet(self): """Show subnet: --fields id --fields name myid.""" resource = 'subnet' From e42245eb07b5d8f19c4fedb3e39490975c8e7e10 Mon Sep 17 00:00:00 2001 From: Christian Berendt Date: Mon, 28 Apr 2014 14:45:59 +0200 Subject: [PATCH 026/845] added new column binary to the listing of agents Change-Id: I057185ef2b6e248271597e81a6bef44c2ad8d148 Closes-Bug: #1313702 --- neutronclient/neutron/v2_0/agent.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/neutronclient/neutron/v2_0/agent.py b/neutronclient/neutron/v2_0/agent.py index ed813b918..884d443fc 100644 --- a/neutronclient/neutron/v2_0/agent.py +++ b/neutronclient/neutron/v2_0/agent.py @@ -31,7 +31,8 @@ class ListAgent(neutronV20.ListCommand): resource = 'agent' log = logging.getLogger(__name__ + '.ListAgent') - list_columns = ['id', 'agent_type', 'host', 'alive', 'admin_state_up'] + list_columns = ['id', 'agent_type', 'host', 'alive', 'admin_state_up', + 'binary'] _formatters = {'heartbeat_timestamp': _format_timestamp} sorting_support = True From 99f45d1e618f535919cf0bf18a8caf18d29a5b63 Mon Sep 17 00:00:00 2001 From: Christian Berendt Date: Mon, 28 Apr 2014 13:55:42 +0200 Subject: [PATCH 027/845] removed usage of '... can be repeated.' Thanks to Diane Fleming for the wording. Change-Id: Ida509a838a76f7484b004d59b2866f4dbf2075b6 --- neutronclient/neutron/v2_0/__init__.py | 17 +++++++++-------- .../neutron/v2_0/nsx/networkgateway.py | 4 ++-- neutronclient/neutron/v2_0/port.py | 16 ++++++++-------- neutronclient/shell.py | 2 +- 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index dd0cab3b9..3f89fe52e 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -106,8 +106,8 @@ def add_show_list_common_argument(parser): parser.add_argument( '-F', '--field', dest='fields', metavar='FIELD', - help=_('Specify the field(s) to be returned by server,' - ' can be repeated'), + help=_('Specify the field(s) to be returned by server. You can ' + 'repeat this option.'), action='append', default=[]) @@ -126,16 +126,17 @@ def add_sorting_argument(parser): '--sort-key', dest='sort_key', metavar='FIELD', action='append', - help=_("Sort list by specified fields (This option can be repeated), " - "The number of sort_dir and sort_key should match each other, " - "more sort_dir specified will be omitted, less will be filled " - "with asc as default direction "), + help=_("Sorts the list by the specified fields in the specified " + "directions. You can repeat this option, but you must " + "specify an equal number of sort_dir and sort_key values. " + "Extra sort_dir options are ignored. Missing sort_dir options " + "use the default asc value."), default=[]) parser.add_argument( '--sort-dir', dest='sort_dir', metavar='{asc,desc}', - help=_("Sort list in specified directions " - "(This option can be repeated)"), + help=_("Sorts the list in the specified direction. You can repeat " + "this option."), action='append', default=[], choices=['asc', 'desc']) diff --git a/neutronclient/neutron/v2_0/nsx/networkgateway.py b/neutronclient/neutron/v2_0/nsx/networkgateway.py index 6947d4cca..0152eaf72 100644 --- a/neutronclient/neutron/v2_0/nsx/networkgateway.py +++ b/neutronclient/neutron/v2_0/nsx/networkgateway.py @@ -178,8 +178,8 @@ def add_known_arguments(self, parser): parser.add_argument( '--device', metavar='id=ID,interface_name=NAME_OR_ID', action='append', - help=_('Device info for this gateway, ' - 'can be repeated for multiple devices for HA gateways')) + help=_('Device info for this gateway. You can repeat this ' + 'option for multiple devices for HA gateways')) def args2body(self, parsed_args): body = {self.resource: { diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index c7036af00..3cb321b21 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -80,8 +80,8 @@ def add_arguments_secgroup(self, parser): group_sg.add_argument( '--security-group', metavar='SECURITY_GROUP', default=[], action='append', dest='security_groups', - help=_('Security group associated with the port ' - '(This option can be repeated)')) + help=_('Security group associated with the port. You can ' + 'repeat this option.')) group_sg.add_argument( '--no-security-groups', action='store_true', @@ -108,8 +108,8 @@ def add_arguments_extradhcpopt(self, parser): action='append', dest='extra_dhcp_opts', help=_('Extra dhcp options to be assigned to this port: ' - 'opt_name=,opt_value=, ' - '(This option can be repeated.)')) + 'opt_name=,opt_value=. You can ' + 'repeat this option.')) def args2body_extradhcpopt(self, parsed_args, port): ops = [] @@ -119,8 +119,8 @@ def args2body_extradhcpopt(self, parsed_args, port): # both must be thrown out. opt_ele = {} edo_err_msg = _("Invalid --extra-dhcp-opt option, can only be: " - "opt_name=,opt_value=, " - "(This option can be repeated.") + "opt_name=,opt_value=. " + "You can repeat this option.") for opt in parsed_args.extra_dhcp_opts: if opt.split('=')[0] in ['opt_value', 'opt_name']: opt_ele.update(utils.str2dict(opt)) @@ -174,8 +174,8 @@ def add_known_arguments(self, parser): '--fixed-ip', metavar='subnet_id=SUBNET,ip_address=IP_ADDR', action='append', help=_('Desired IP and/or subnet for this port: ' - 'subnet_id=,ip_address=, ' - '(This option can be repeated.)')) + 'subnet_id=,ip_address=.' + 'You can repeat this option.')) parser.add_argument( '--fixed_ip', action='append', diff --git a/neutronclient/shell.py b/neutronclient/shell.py index c1cba5c5b..31e23193d 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -355,7 +355,7 @@ def build_option_parser(self, description, version): dest='verbose_level', default=self.DEFAULT_VERBOSE_LEVEL, help=_('Increase verbosity of output and show tracebacks on' - ' errors. Can be repeated.')) + ' errors. You can repeat this option.')) parser.add_argument( '-q', '--quiet', action='store_const', From 864be88ef9b33e1f74e13c225f1a3b4cbf17bce3 Mon Sep 17 00:00:00 2001 From: Yaguang Tang Date: Thu, 20 Mar 2014 18:48:38 +0800 Subject: [PATCH 028/845] Make neutronclient parse keystone v3 endpoints correctly Import keystone access module to handle various verion of keystone catalog. Change-Id: Ie8de15da6341cdb3af73c0fa8753f7e754bf6275 Closes-bug: 1295056 --- neutronclient/client.py | 64 +++------------------ neutronclient/tests/unit/test_auth.py | 83 +-------------------------- requirements.txt | 1 + 3 files changed, 10 insertions(+), 138 deletions(-) diff --git a/neutronclient/client.py b/neutronclient/client.py index cd71e1b4f..7379edb72 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -21,6 +21,7 @@ import logging import os +from keystoneclient import access import requests from neutronclient.common import exceptions @@ -40,55 +41,6 @@ logging.getLogger("requests").setLevel(_requests_log_level) -class ServiceCatalog(object): - """Helper methods for dealing with a Keystone Service Catalog.""" - - def __init__(self, resource_dict): - self.catalog = resource_dict - - def get_token(self): - """Fetch token details from service catalog.""" - token = {'id': self.catalog['access']['token']['id'], - 'expires': self.catalog['access']['token']['expires'], } - try: - token['user_id'] = self.catalog['access']['user']['id'] - token['tenant_id'] = ( - self.catalog['access']['token']['tenant']['id']) - except Exception: - # just leave the tenant and user out if it doesn't exist - pass - return token - - def url_for(self, attr=None, filter_value=None, - service_type='network', endpoint_type='publicURL'): - """Fetch the URL from the Neutron service for - a particular endpoint type. If none given, return - publicURL. - """ - - catalog = self.catalog['access'].get('serviceCatalog', []) - matching_endpoints = [] - for service in catalog: - if service['type'] != service_type: - continue - - endpoints = service['endpoints'] - for endpoint in endpoints: - if not filter_value or endpoint.get(attr) == filter_value: - matching_endpoints.append(endpoint) - - if not matching_endpoints: - raise exceptions.EndpointNotFound() - elif len(matching_endpoints) > 1: - raise exceptions.AmbiguousEndpoints( - matching_endpoints=matching_endpoints) - else: - if endpoint_type not in matching_endpoints[0]: - raise exceptions.EndpointTypeNotFound(type_=endpoint_type) - - return matching_endpoints[0][endpoint_type] - - class HTTPClient(object): """Handles the REST calls and responses, include authn.""" @@ -219,14 +171,12 @@ def do_request(self, url, method, **kwargs): def _extract_service_catalog(self, body): """Set the client's service catalog from the response data.""" - self.service_catalog = ServiceCatalog(body) - try: - sc = self.service_catalog.get_token() - self.auth_token = sc['id'] - self.auth_tenant_id = sc.get('tenant_id') - self.auth_user_id = sc.get('user_id') - except KeyError: - raise exceptions.Unauthorized() + self.auth_ref = access.AccessInfo.factory(body=body) + self.service_catalog = self.auth_ref.service_catalog + self.auth_token = self.auth_ref.auth_token + self.auth_tenant_id = self.auth_ref.tenant_id + self.auth_user_id = self.auth_ref.user_id + if not self.endpoint_url: self.endpoint_url = self.service_catalog.url_for( attr='region', filter_value=self.region_name, diff --git a/neutronclient/tests/unit/test_auth.py b/neutronclient/tests/unit/test_auth.py index 585458e15..ae077c8ad 100644 --- a/neutronclient/tests/unit/test_auth.py +++ b/neutronclient/tests/unit/test_auth.py @@ -18,6 +18,7 @@ import json import uuid +from keystoneclient import exceptions as k_exceptions import mox import requests import testtools @@ -291,86 +292,6 @@ def test_get_endpoint_url_failed(self): self.mox.ReplayAll() self.client.do_request('/resource', 'GET') - def test_url_for(self): - resources = copy.deepcopy(KS_TOKEN_RESULT) - - endpoints = resources['access']['serviceCatalog'][0]['endpoints'][0] - endpoints['publicURL'] = 'public' - endpoints['internalURL'] = 'internal' - endpoints['adminURL'] = 'admin' - catalog = client.ServiceCatalog(resources) - - # endpoint_type not specified - url = catalog.url_for(attr='region', - filter_value=REGION) - self.assertEqual('public', url) - - # endpoint type specified (3 cases) - url = catalog.url_for(attr='region', - filter_value=REGION, - endpoint_type='adminURL') - self.assertEqual('admin', url) - - url = catalog.url_for(attr='region', - filter_value=REGION, - endpoint_type='publicURL') - self.assertEqual('public', url) - - url = catalog.url_for(attr='region', - filter_value=REGION, - endpoint_type='internalURL') - self.assertEqual('internal', url) - - # endpoint_type requested does not exist. - self.assertRaises(exceptions.EndpointTypeNotFound, - catalog.url_for, - attr='region', - filter_value=REGION, - endpoint_type='privateURL') - - # Test scenario with url_for when the service catalog only has publicURL. - def test_url_for_only_public_url(self): - resources = copy.deepcopy(KS_TOKEN_RESULT) - catalog = client.ServiceCatalog(resources) - - # Remove endpoints from the catalog. - endpoints = resources['access']['serviceCatalog'][0]['endpoints'][0] - del endpoints['internalURL'] - del endpoints['adminURL'] - endpoints['publicURL'] = 'public' - - # Use publicURL when specified explicitly. - url = catalog.url_for(attr='region', - filter_value=REGION, - endpoint_type='publicURL') - self.assertEqual('public', url) - - # Use publicURL when specified explicitly. - url = catalog.url_for(attr='region', - filter_value=REGION) - self.assertEqual('public', url) - - # Test scenario with url_for when the service catalog only has adminURL. - def test_url_for_only_admin_url(self): - resources = copy.deepcopy(KS_TOKEN_RESULT) - catalog = client.ServiceCatalog(resources) - endpoints = resources['access']['serviceCatalog'][0]['endpoints'][0] - del endpoints['internalURL'] - del endpoints['publicURL'] - endpoints['adminURL'] = 'admin' - - # Use publicURL when specified explicitly. - url = catalog.url_for(attr='region', - filter_value=REGION, - endpoint_type='adminURL') - self.assertEqual('admin', url) - - # But not when nothing is specified. - self.assertRaises(exceptions.EndpointTypeNotFound, - catalog.url_for, - attr='region', - filter_value=REGION) - def test_endpoint_type(self): resources = copy.deepcopy(KS_TOKEN_RESULT) endpoints = resources['access']['serviceCatalog'][0]['endpoints'][0] @@ -415,7 +336,7 @@ def test_endpoint_type(self): username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD, auth_url=AUTH_URL, region_name=REGION, endpoint_type='privateURL') - self.assertRaises(exceptions.EndpointTypeNotFound, + self.assertRaises(k_exceptions.EndpointNotFound, self.client._extract_service_catalog, resources) diff --git a/requirements.txt b/requirements.txt index 7c118d3a3..c09cd7b7e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,6 +4,7 @@ cliff>=1.4.3 iso8601>=0.1.9 netaddr>=0.7.6 requests>=1.1 +python-keystoneclient>=0.9.0 simplejson>=2.0.9 six>=1.6.0 Babel>=1.3 From 8801efcb15bc601d02675234165a8d286456ec5b Mon Sep 17 00:00:00 2001 From: Carlos Goncalves Date: Tue, 10 Jun 2014 18:33:11 +0100 Subject: [PATCH 029/845] Set firewall_rules only after appending all rules The firewall_rules attribute was being re-set per each firewall rule. Instead if should only be set once when all firewall rules are appended to _firewall_rules. A minor PEP E128 (continuation line under-lined) is also being fixed here. Change-Id: I9db6d3dc0ad11a3194513d80f15151c29aa019cd Closes-Bug: 1328625 --- neutronclient/neutron/v2_0/fw/firewallpolicy.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/neutronclient/neutron/v2_0/fw/firewallpolicy.py b/neutronclient/neutron/v2_0/fw/firewallpolicy.py index 5e08e6955..56885b3a3 100644 --- a/neutronclient/neutron/v2_0/fw/firewallpolicy.py +++ b/neutronclient/neutron/v2_0/fw/firewallpolicy.py @@ -77,7 +77,7 @@ def add_known_arguments(self, parser): parser.add_argument( '--firewall-rules', type=string.split, help=_('Ordered list of whitespace-delimited firewall rule ' - 'names or IDs; e.g., --firewall-rules \"rule1 rule2\"')) + 'names or IDs; e.g., --firewall-rules \"rule1 rule2\"')) parser.add_argument( '--audited', action='store_true', @@ -91,10 +91,10 @@ def args2body(self, parsed_args): _firewall_rules.append( neutronv20.find_resourceid_by_name_or_id( self.get_client(), 'firewall_rule', f)) - body = {self.resource: { - 'firewall_rules': _firewall_rules, - }, - } + body = {self.resource: { + 'firewall_rules': _firewall_rules, + }, + } else: body = {self.resource: {}} neutronv20.update_dict(parsed_args, body[self.resource], From 875fa2309d34a27735554f20eb7f62a5311eec70 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Mon, 23 Jun 2014 05:34:44 +0000 Subject: [PATCH 030/845] Updated from global requirements Change-Id: I9c576388ddd5882e82adfb20b9097fe2979b1fb2 --- requirements.txt | 4 ++-- test-requirements.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index c09cd7b7e..25626cff2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,10 @@ pbr>=0.6,!=0.7,<1.0 argparse -cliff>=1.4.3 +cliff>=1.6.0 iso8601>=0.1.9 netaddr>=0.7.6 requests>=1.1 python-keystoneclient>=0.9.0 simplejson>=2.0.9 -six>=1.6.0 +six>=1.7.0 Babel>=1.3 diff --git a/test-requirements.txt b/test-requirements.txt index 402590b90..40d89ac53 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -6,6 +6,6 @@ discover fixtures>=0.3.14 mox>=0.5.3 python-subunit>=0.0.18 -sphinx>=1.1.2,<1.2 +sphinx>=1.1.2,!=1.2.0,<1.3 testrepository>=0.0.18 testtools>=0.9.34 From 73e608d3545f0a4a307b1e89f0f676993318b442 Mon Sep 17 00:00:00 2001 From: Christian Berendt Date: Mon, 12 May 2014 18:18:16 +0200 Subject: [PATCH 031/845] debug level logs should not be translated According to the OpenStack translation policy available at https://wiki.openstack.org/wiki/LoggingStandards debug messages should not be translated. Like mentioned in several changes in Nova by garyk this is to help prioritize log translation. Change-Id: I639624cbe64749085654b32ac85596fdae577230 --- neutronclient/common/serializer.py | 2 +- neutronclient/common/utils.py | 4 ++-- neutronclient/shell.py | 2 +- neutronclient/v2_0/client.py | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/neutronclient/common/serializer.py b/neutronclient/common/serializer.py index a7202e1e7..091543c4f 100644 --- a/neutronclient/common/serializer.py +++ b/neutronclient/common/serializer.py @@ -191,7 +191,7 @@ def _to_xml_node(self, parent, metadata, nodename, data, used_prefixes): result.set( constants.TYPE_ATTR, constants.TYPE_FLOAT) - LOG.debug(_("Data %(data)s type is %(type)s"), + LOG.debug("Data %(data)s type is %(type)s", {'data': data, 'type': type(data)}) if isinstance(data, str): diff --git a/neutronclient/common/utils.py b/neutronclient/common/utils.py index 419470ffb..3fe025a2d 100644 --- a/neutronclient/common/utils.py +++ b/neutronclient/common/utils.py @@ -172,13 +172,13 @@ def http_log_req(_logger, args, kwargs): if 'body' in kwargs and kwargs['body']: string_parts.append(" -d '%s'" % (kwargs['body'])) string_parts = safe_encode_list(string_parts) - _logger.debug(_("\nREQ: %s\n"), "".join(string_parts)) + _logger.debug("\nREQ: %s\n", "".join(string_parts)) def http_log_resp(_logger, resp, body): if not _logger.isEnabledFor(logging.DEBUG): return - _logger.debug(_("RESP:%(code)s %(headers)s %(body)s\n"), + _logger.debug("RESP:%(code)s %(headers)s %(body)s\n", {'code': resp.status_code, 'headers': resp.headers, 'body': body}) diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 31e23193d..79887a6f6 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -666,7 +666,7 @@ def initialize_app(self, argv): def clean_up(self, cmd, result, err): self.log.debug('clean_up %s', cmd.__class__.__name__) if err: - self.log.debug(_('Got an error: %s'), unicode(err)) + self.log.debug('Got an error: %s', unicode(err)) def configure_logging(self): """Create logging handlers for any log output.""" diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 7b6b1ce3e..002f3f24a 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -1199,7 +1199,7 @@ def __init__(self, **kwargs): def _handle_fault_response(self, status_code, response_body): # Create exception with HTTP status code and message - _logger.debug(_("Error message: %s"), response_body) + _logger.debug("Error message: %s", response_body) # Add deserialized error message to exception arguments try: des_error_body = self.deserialize(response_body, status_code) @@ -1301,7 +1301,7 @@ def retry_request(self, method, action, body=None, except exceptions.ConnectionFailed: # Exception has already been logged by do_request() if i < self.retries: - _logger.debug(_('Retrying connection to Neutron service')) + _logger.debug('Retrying connection to Neutron service') time.sleep(self.retry_interval) raise exceptions.ConnectionFailed(reason=_("Maximum attempts reached")) From 85cb5300889d17e779b2a300cf2ff4c9b8574f87 Mon Sep 17 00:00:00 2001 From: liu-sheng Date: Tue, 24 Jun 2014 09:53:17 +0800 Subject: [PATCH 032/845] Silences the output in CLI for connection info This change try to reduce the useless urllib3 connection info in CLI by set the log level of urllib3.connectionpool. Change-Id: I9d21d29ae0274133099dd05a14e5211d77ffe721 Closes-Bug: #1333485 --- neutronclient/shell.py | 1 + 1 file changed, 1 insertion(+) diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 31e23193d..2bcb691ad 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -686,6 +686,7 @@ def configure_logging(self): formatter = logging.Formatter(self.DEBUG_MESSAGE_FORMAT) else: formatter = logging.Formatter(self.CONSOLE_MESSAGE_FORMAT) + logging.getLogger('urllib3.connectionpool').setLevel(logging.WARNING) console.setFormatter(formatter) root_logger.addHandler(console) return From 181a9c7be689668b89c28c3e49b91a238b5b7843 Mon Sep 17 00:00:00 2001 From: Angus Lees Date: Fri, 6 Jun 2014 17:32:08 +1000 Subject: [PATCH 033/845] Ensure .status_code is defined for all NeutronClientExceptions nova contains plenty of code like: except neutron_client_exc.NeutronClientException as e: if e.status_code == 409: ... This change declares a fallback of 0 for exceptions without a more explicit status code. This was the behaviour prior to Change-Id I99b9560b3afaf5884fd00353323267da450338fa Change-Id: Ib736205066fd5cd9738fd3fb3b64f6590803cf39 --- neutronclient/common/exceptions.py | 2 ++ neutronclient/tests/unit/test_cli20.py | 35 ++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/neutronclient/common/exceptions.py b/neutronclient/common/exceptions.py index f4085d319..c7c0b8261 100644 --- a/neutronclient/common/exceptions.py +++ b/neutronclient/common/exceptions.py @@ -60,6 +60,8 @@ class NeutronClientException(NeutronException): blocks. The actual error message is the one generated on the server side. """ + status_code = 0 + def __init__(self, message=None, **kwargs): if 'status_code' in kwargs: self.status_code = kwargs['status_code'] diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 3f896adfc..98451e4c4 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -20,6 +20,7 @@ import cStringIO import fixtures import mox +import requests import sys import testtools @@ -669,3 +670,37 @@ def test_exception_handler_v20_default_fallback(self): exceptions.NeutronClientException, 500, expected_msg=expected_msg, error_content=error_content) + + def test_exception_status(self): + e = exceptions.BadRequest() + self.assertEqual(e.status_code, 400) + + e = exceptions.BadRequest(status_code=499) + self.assertEqual(e.status_code, 499) + + # SslCertificateValidationError has no explicit status_code, + # but should have a 'safe' defined fallback. + e = exceptions.SslCertificateValidationError() + self.assertIsNotNone(e.status_code) + + e = exceptions.SslCertificateValidationError(status_code=599) + self.assertEqual(e.status_code, 599) + + def test_connection_failed(self): + self.mox.StubOutWithMock(self.client.httpclient, 'request') + self.client.httpclient.auth_token = 'token' + + self.client.httpclient.request( + end_url('/test'), 'GET', + headers=mox.ContainsKeyValue('X-Auth-Token', 'token') + ).AndRaise(requests.exceptions.ConnectionError('Connection refused')) + + self.mox.ReplayAll() + + error = self.assertRaises(exceptions.ConnectionFailed, + self.client.get, '/test') + # NB: ConnectionFailed has no explicit status_code, so this + # tests that there is a fallback defined. + self.assertIsNotNone(error.status_code) + self.mox.VerifyAll() + self.mox.UnsetStubs() From a604029380c9ae7d458498ff3cc82469f6ce8a7d Mon Sep 17 00:00:00 2001 From: Andreas Jaeger Date: Sun, 20 Apr 2014 19:37:32 +0200 Subject: [PATCH 034/845] Improve help strings Several changes to make help strings consistent: * End options with "." * Add missing space between lines * Capitalize first word of help strings * Improve wording of text * Make strings consistent. * Use "VPN service" Closes-Bug: #1321871 Change-Id: I8fdc356574d680c222b8a26dcc5ad8e3a2b4e4f1 --- neutronclient/neutron/v2_0/__init__.py | 18 +++++----- neutronclient/neutron/v2_0/agentscheduler.py | 28 +++++++-------- neutronclient/neutron/v2_0/credential.py | 8 ++--- neutronclient/neutron/v2_0/extension.py | 2 +- neutronclient/neutron/v2_0/floatingip.py | 34 +++++++++---------- neutronclient/neutron/v2_0/fw/firewall.py | 10 +++--- .../neutron/v2_0/fw/firewallpolicy.py | 16 ++++----- neutronclient/neutron/v2_0/fw/firewallrule.py | 23 +++++++------ .../neutron/v2_0/lb/healthmonitor.py | 16 ++++----- neutronclient/neutron/v2_0/lb/member.py | 10 +++--- neutronclient/neutron/v2_0/lb/pool.py | 14 ++++---- neutronclient/neutron/v2_0/lb/vip.py | 18 +++++----- neutronclient/neutron/v2_0/metering.py | 14 ++++---- neutronclient/neutron/v2_0/netpartition.py | 2 +- neutronclient/neutron/v2_0/network.py | 6 ++-- neutronclient/neutron/v2_0/networkprofile.py | 14 ++++---- .../neutron/v2_0/nsx/networkgateway.py | 24 ++++++------- neutronclient/neutron/v2_0/nsx/qos_queue.py | 14 ++++---- neutronclient/neutron/v2_0/policyprofile.py | 4 +-- neutronclient/neutron/v2_0/port.py | 14 ++++---- neutronclient/neutron/v2_0/quota.py | 30 ++++++++-------- neutronclient/neutron/v2_0/router.py | 18 +++++----- neutronclient/neutron/v2_0/securitygroup.py | 24 ++++++------- neutronclient/neutron/v2_0/subnet.py | 22 ++++++------ neutronclient/neutron/v2_0/vpn/ikepolicy.py | 16 ++++----- .../neutron/v2_0/vpn/ipsec_site_connection.py | 22 ++++++------ neutronclient/neutron/v2_0/vpn/ipsecpolicy.py | 18 +++++----- neutronclient/neutron/v2_0/vpn/utils.py | 6 ++-- neutronclient/neutron/v2_0/vpn/vpnservice.py | 20 +++++------ neutronclient/shell.py | 31 +++++++++-------- neutronclient/v2_0/client.py | 10 +++--- 31 files changed, 255 insertions(+), 251 deletions(-) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index 3f89fe52e..b67c04823 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -91,7 +91,7 @@ def find_resourceid_by_name_or_id(client, resource, name_or_id): def add_show_list_common_argument(parser): parser.add_argument( '-D', '--show-details', - help=_('Show detailed info'), + help=_('Show detailed info.'), action='store_true', default=False, ) parser.add_argument( @@ -117,7 +117,7 @@ def add_pagination_argument(parser): '-P', '--page-size', dest='page_size', metavar='SIZE', type=int, help=_("Specify retrieve unit of each request, then split one request " - "to several requests"), + "to several requests."), default=None) @@ -356,7 +356,7 @@ def get_parser(self, prog_name): parser = super(NeutronCommand, self).get_parser(prog_name) parser.add_argument( '--request-format', - help=_('The xml or json request format'), + help=_('The xml or json request format.'), default='json', choices=['json', 'xml', ], ) parser.add_argument( @@ -401,7 +401,7 @@ def get_parser(self, prog_name): parser = super(CreateCommand, self).get_parser(prog_name) parser.add_argument( '--tenant-id', metavar='TENANT_ID', - help=_('The owner tenant ID'), ) + help=_('The owner tenant ID.'), ) parser.add_argument( '--tenant_id', help=argparse.SUPPRESS) @@ -444,7 +444,7 @@ def get_parser(self, prog_name): parser = super(UpdateCommand, self).get_parser(prog_name) parser.add_argument( 'id', metavar=self.resource.upper(), - help=_('ID or name of %s to update') % self.resource) + help=_('ID or name of %s to update.') % self.resource) self.add_known_arguments(parser) return parser @@ -491,9 +491,9 @@ class DeleteCommand(NeutronCommand): def get_parser(self, prog_name): parser = super(DeleteCommand, self).get_parser(prog_name) if self.allow_names: - help_str = _('ID or name of %s to delete') + help_str = _('ID or name of %s to delete.') else: - help_str = _('ID of %s to delete') + help_str = _('ID of %s to delete.') parser.add_argument( 'id', metavar=self.resource.upper(), help=help_str % self.resource) @@ -631,9 +631,9 @@ def get_parser(self, prog_name): parser = super(ShowCommand, self).get_parser(prog_name) add_show_list_common_argument(parser) if self.allow_names: - help_str = _('ID or name of %s to look up') + help_str = _('ID or name of %s to look up.') else: - help_str = _('ID of %s to look up') + help_str = _('ID of %s to look up.') parser.add_argument( 'id', metavar=self.resource.upper(), help=help_str % self.resource) diff --git a/neutronclient/neutron/v2_0/agentscheduler.py b/neutronclient/neutron/v2_0/agentscheduler.py index d202a105b..03d245420 100644 --- a/neutronclient/neutron/v2_0/agentscheduler.py +++ b/neutronclient/neutron/v2_0/agentscheduler.py @@ -36,10 +36,10 @@ def get_parser(self, prog_name): parser = super(AddNetworkToDhcpAgent, self).get_parser(prog_name) parser.add_argument( 'dhcp_agent', - help=_('ID of the DHCP agent')) + help=_('ID of the DHCP agent.')) parser.add_argument( 'network', - help=_('Network to add')) + help=_('Network to add.')) return parser def run(self, parsed_args): @@ -62,10 +62,10 @@ def get_parser(self, prog_name): parser = super(RemoveNetworkFromDhcpAgent, self).get_parser(prog_name) parser.add_argument( 'dhcp_agent', - help=_('ID of the DHCP agent')) + help=_('ID of the DHCP agent.')) parser.add_argument( 'network', - help=_('Network to remove')) + help=_('Network to remove.')) return parser def run(self, parsed_args): @@ -91,7 +91,7 @@ def get_parser(self, prog_name): self).get_parser(prog_name) parser.add_argument( 'dhcp_agent', - help=_('ID of the DHCP agent')) + help=_('ID of the DHCP agent.')) return parser def call_server(self, neutron_client, search_opts, parsed_args): @@ -114,7 +114,7 @@ def get_parser(self, prog_name): self).get_parser(prog_name) parser.add_argument( 'network', - help=_('Network to query')) + help=_('Network to query.')) return parser def extend_list(self, data, parsed_args): @@ -139,10 +139,10 @@ def get_parser(self, prog_name): parser = super(AddRouterToL3Agent, self).get_parser(prog_name) parser.add_argument( 'l3_agent', - help=_('ID of the L3 agent')) + help=_('ID of the L3 agent.')) parser.add_argument( 'router', - help=_('Router to add')) + help=_('Router to add.')) return parser def run(self, parsed_args): @@ -166,10 +166,10 @@ def get_parser(self, prog_name): parser = super(RemoveRouterFromL3Agent, self).get_parser(prog_name) parser.add_argument( 'l3_agent', - help=_('ID of the L3 agent')) + help=_('ID of the L3 agent.')) parser.add_argument( 'router', - help=_('Router to remove')) + help=_('Router to remove.')) return parser def run(self, parsed_args): @@ -199,7 +199,7 @@ def get_parser(self, prog_name): self).get_parser(prog_name) parser.add_argument( 'l3_agent', - help=_('ID of the L3 agent to query')) + help=_('ID of the L3 agent to query.')) return parser def call_server(self, neutron_client, search_opts, parsed_args): @@ -221,7 +221,7 @@ def get_parser(self, prog_name): parser = super(ListL3AgentsHostingRouter, self).get_parser(prog_name) parser.add_argument('router', - help=_('Router to query')) + help=_('Router to query.')) return parser def extend_list(self, data, parsed_args): @@ -250,7 +250,7 @@ def get_parser(self, prog_name): parser = super(ListPoolsOnLbaasAgent, self).get_parser(prog_name) parser.add_argument( 'lbaas_agent', - help=_('ID of the loadbalancer agent to query')) + help=_('ID of the loadbalancer agent to query.')) return parser def call_server(self, neutron_client, search_opts, parsed_args): @@ -275,7 +275,7 @@ def get_parser(self, prog_name): parser = super(GetLbaasAgentHostingPool, self).get_parser(prog_name) parser.add_argument('pool', - help=_('Pool to query')) + help=_('Pool to query.')) return parser def extend_list(self, data, parsed_args): diff --git a/neutronclient/neutron/v2_0/credential.py b/neutronclient/neutron/v2_0/credential.py index 066215eb4..2e8c347e3 100644 --- a/neutronclient/neutron/v2_0/credential.py +++ b/neutronclient/neutron/v2_0/credential.py @@ -44,16 +44,16 @@ class CreateCredential(neutronV20.CreateCommand): def add_known_arguments(self, parser): parser.add_argument( 'credential_name', - help=_('Name/Ip address for Credential')) + help=_('Name/IP address for credential.')) parser.add_argument( 'credential_type', - help=_('Type of the Credential')) + help=_('Type of the credential.')) parser.add_argument( '--username', - help=_('Username for the credential')) + help=_('Username for the credential.')) parser.add_argument( '--password', - help=_('Password for the credential')) + help=_('Password for the credential.')) def args2body(self, parsed_args): body = {'credential': { diff --git a/neutronclient/neutron/v2_0/extension.py b/neutronclient/neutron/v2_0/extension.py index 599165955..0170c177e 100644 --- a/neutronclient/neutron/v2_0/extension.py +++ b/neutronclient/neutron/v2_0/extension.py @@ -40,5 +40,5 @@ def get_parser(self, prog_name): cmd_base.add_show_list_common_argument(parser) parser.add_argument( 'id', metavar='EXT-ALIAS', - help=_('The extension alias')) + help=_('The extension alias.')) return parser diff --git a/neutronclient/neutron/v2_0/floatingip.py b/neutronclient/neutron/v2_0/floatingip.py index fb2c2f759..3873fff8b 100644 --- a/neutronclient/neutron/v2_0/floatingip.py +++ b/neutronclient/neutron/v2_0/floatingip.py @@ -24,7 +24,7 @@ class ListFloatingIP(neutronV20.ListCommand): - """List floating ips that belong to a given tenant.""" + """List floating IPs that belong to a given tenant.""" resource = 'floatingip' log = logging.getLogger(__name__ + '.ListFloatingIP') @@ -35,7 +35,7 @@ class ListFloatingIP(neutronV20.ListCommand): class ShowFloatingIP(neutronV20.ShowCommand): - """Show information of a given floating ip.""" + """Show information of a given floating IP.""" resource = 'floatingip' log = logging.getLogger(__name__ + '.ShowFloatingIP') @@ -43,7 +43,7 @@ class ShowFloatingIP(neutronV20.ShowCommand): class CreateFloatingIP(neutronV20.CreateCommand): - """Create a floating ip for a given tenant.""" + """Create a floating IP for a given tenant.""" resource = 'floatingip' log = logging.getLogger(__name__ + '.CreateFloatingIP') @@ -51,17 +51,17 @@ class CreateFloatingIP(neutronV20.CreateCommand): def add_known_arguments(self, parser): parser.add_argument( 'floating_network_id', metavar='FLOATING_NETWORK', - help=_('Network name or id to allocate floating IP from')) + help=_('Network name or ID to allocate floating IP from.')) parser.add_argument( '--port-id', - help=_('ID of the port to be associated with the floatingip')) + help=_('ID of the port to be associated with the floating IP.')) parser.add_argument( '--port_id', help=argparse.SUPPRESS) parser.add_argument( '--fixed-ip-address', - help=_('IP address on the port (only required if port has multiple' - 'IPs)')) + help=_('IP address on the port (only required if port has ' + 'multiple IPs).')) parser.add_argument( '--fixed_ip_address', help=argparse.SUPPRESS) @@ -81,7 +81,7 @@ def args2body(self, parsed_args): class DeleteFloatingIP(neutronV20.DeleteCommand): - """Delete a given floating ip.""" + """Delete a given floating IP.""" log = logging.getLogger(__name__ + '.DeleteFloatingIP') resource = 'floatingip' @@ -89,7 +89,7 @@ class DeleteFloatingIP(neutronV20.DeleteCommand): class AssociateFloatingIP(neutronV20.NeutronCommand): - """Create a mapping between a floating ip and a fixed ip.""" + """Create a mapping between a floating IP and a fixed IP.""" api = 'network' log = logging.getLogger(__name__ + '.AssociateFloatingIP') @@ -99,15 +99,15 @@ def get_parser(self, prog_name): parser = super(AssociateFloatingIP, self).get_parser(prog_name) parser.add_argument( 'floatingip_id', metavar='FLOATINGIP_ID', - help=_('ID of the floating IP to associate')) + help=_('ID of the floating IP to associate.')) parser.add_argument( 'port_id', metavar='PORT', help=_('ID or name of the port to be associated with the ' - 'floatingip')) + 'floating IP.')) parser.add_argument( '--fixed-ip-address', - help=_('IP address on the port (only required if port has multiple' - 'IPs)')) + help=_('IP address on the port (only required if port has ' + 'multiple IPs).')) parser.add_argument( '--fixed_ip_address', help=argparse.SUPPRESS) @@ -124,12 +124,12 @@ def run(self, parsed_args): update_dict['fixed_ip_address'] = parsed_args.fixed_ip_address neutron_client.update_floatingip(parsed_args.floatingip_id, {'floatingip': update_dict}) - print(_('Associated floatingip %s') % parsed_args.floatingip_id, + print(_('Associated floating IP %s') % parsed_args.floatingip_id, file=self.app.stdout) class DisassociateFloatingIP(neutronV20.NeutronCommand): - """Remove a mapping from a floating ip to a fixed ip. + """Remove a mapping from a floating IP to a fixed IP. """ api = 'network' @@ -140,7 +140,7 @@ def get_parser(self, prog_name): parser = super(DisassociateFloatingIP, self).get_parser(prog_name) parser.add_argument( 'floatingip_id', metavar='FLOATINGIP_ID', - help=_('ID of the floating IP to associate')) + help=_('ID of the floating IP to associate.')) return parser def run(self, parsed_args): @@ -149,5 +149,5 @@ def run(self, parsed_args): neutron_client.format = parsed_args.request_format neutron_client.update_floatingip(parsed_args.floatingip_id, {'floatingip': {'port_id': None}}) - print(_('Disassociated floatingip %s') % parsed_args.floatingip_id, + print(_('Disassociated floating IP %s') % parsed_args.floatingip_id, file=self.app.stdout) diff --git a/neutronclient/neutron/v2_0/fw/firewall.py b/neutronclient/neutron/v2_0/fw/firewall.py index 1d693b6f9..a60376c1c 100644 --- a/neutronclient/neutron/v2_0/fw/firewall.py +++ b/neutronclient/neutron/v2_0/fw/firewall.py @@ -50,23 +50,23 @@ class CreateFirewall(neutronv20.CreateCommand): def add_known_arguments(self, parser): parser.add_argument( 'firewall_policy_id', metavar='POLICY', - help=_('Firewall policy id')) + help=_('Firewall policy ID.')) parser.add_argument( '--name', - help=_('Name for the firewall')) + help=_('Name for the firewall.')) parser.add_argument( '--description', - help=_('Description for the firewall rule')) + help=_('Description for the firewall rule.')) parser.add_argument( '--shared', action='store_true', - help=_('Set shared to True (default False)'), + help=_('Set shared to True (default is False).'), default=argparse.SUPPRESS) parser.add_argument( '--admin-state-down', dest='admin_state', action='store_false', - help=_('Set admin state up to false')) + help=_('Set admin state up to false.')) def args2body(self, parsed_args): _policy_id = neutronv20.find_resourceid_by_name_or_id( diff --git a/neutronclient/neutron/v2_0/fw/firewallpolicy.py b/neutronclient/neutron/v2_0/fw/firewallpolicy.py index 5e08e6955..7e7b9586d 100644 --- a/neutronclient/neutron/v2_0/fw/firewallpolicy.py +++ b/neutronclient/neutron/v2_0/fw/firewallpolicy.py @@ -64,15 +64,15 @@ def add_known_arguments(self, parser): parser.add_argument( 'name', metavar='NAME', - help=_('Name for the firewall policy')) + help=_('Name for the firewall policy.')) parser.add_argument( '--description', - help=_('Description for the firewall policy')) + help=_('Description for the firewall policy.')) parser.add_argument( '--shared', dest='shared', action='store_true', - help=_('To create a shared policy'), + help=_('Create a shared policy.'), default=argparse.SUPPRESS) parser.add_argument( '--firewall-rules', type=string.split, @@ -81,7 +81,7 @@ def add_known_arguments(self, parser): parser.add_argument( '--audited', action='store_true', - help=_('To set audited to True'), + help=_('Sets audited to True.'), default=argparse.SUPPRESS) def args2body(self, parsed_args): @@ -156,15 +156,15 @@ def get_parser(self, prog_name): parser.add_argument( '--insert-before', metavar='FIREWALL_RULE', - help=_('Insert before this rule')) + help=_('Insert before this rule.')) parser.add_argument( '--insert-after', metavar='FIREWALL_RULE', - help=_('Insert after this rule')) + help=_('Insert after this rule.')) parser.add_argument( 'firewall_rule_id', metavar='FIREWALL_RULE', - help=_('New rule to insert')) + help=_('New rule to insert.')) self.add_known_arguments(parser) return parser @@ -205,7 +205,7 @@ def get_parser(self, prog_name): parser.add_argument( 'firewall_rule_id', metavar='FIREWALL_RULE', - help=_('Firewall rule to remove from policy')) + help=_('Firewall rule to remove from policy.')) self.add_known_arguments(parser) return parser diff --git a/neutronclient/neutron/v2_0/fw/firewallrule.py b/neutronclient/neutron/v2_0/fw/firewallrule.py index 072a074c0..366fdca03 100644 --- a/neutronclient/neutron/v2_0/fw/firewallrule.py +++ b/neutronclient/neutron/v2_0/fw/firewallrule.py @@ -76,43 +76,44 @@ class CreateFirewallRule(neutronv20.CreateCommand): def add_known_arguments(self, parser): parser.add_argument( '--name', - help=_('Name for the firewall rule')) + help=_('Name for the firewall rule.')) parser.add_argument( '--description', - help=_('Description for the firewall rule')) + help=_('Description for the firewall rule.')) parser.add_argument( '--shared', dest='shared', action='store_true', - help=_('Set shared to True (default False)'), + help=_('Set shared to True (default is False).'), default=argparse.SUPPRESS) parser.add_argument( '--source-ip-address', - help=_('Source ip address or subnet')) + help=_('Source IP address or subnet.')) parser.add_argument( '--destination-ip-address', - help=_('Destination ip address or subnet')) + help=_('Destination IP address or subnet.')) parser.add_argument( '--source-port', - help=_('Source port (integer in [1, 65535] or range in a:b)')) + help=_('Source port (integer in [1, 65535] or range in a:b).')) parser.add_argument( '--destination-port', - help=_('Destination port (integer in [1, 65535] or range in a:b)')) + help=_('Destination port (integer in [1, 65535] or range in ' + 'a:b).')) parser.add_argument( '--disabled', dest='enabled', action='store_false', - help=_('To disable this rule'), + help=_('To disable this rule.'), default=argparse.SUPPRESS) parser.add_argument( '--protocol', choices=['tcp', 'udp', 'icmp', 'any'], required=True, - help=_('Protocol for the firewall rule')) + help=_('Protocol for the firewall rule.')) parser.add_argument( '--action', required=True, choices=['allow', 'deny'], - help=_('Action for the firewall rule')) + help=_('Action for the firewall rule.')) def args2body(self, parsed_args): body = { @@ -140,7 +141,7 @@ def add_known_arguments(self, parser): parser.add_argument( '--protocol', choices=['tcp', 'udp', 'icmp', 'any'], required=False, - help=_('Protocol for the firewall rule')) + help=_('Protocol for the firewall rule.')) def args2body(self, parsed_args): body = { diff --git a/neutronclient/neutron/v2_0/lb/healthmonitor.py b/neutronclient/neutron/v2_0/lb/healthmonitor.py index c3b9b1909..22cbb8d8c 100644 --- a/neutronclient/neutron/v2_0/lb/healthmonitor.py +++ b/neutronclient/neutron/v2_0/lb/healthmonitor.py @@ -51,7 +51,7 @@ def add_known_arguments(self, parser): parser.add_argument( '--admin-state-down', dest='admin_state', action='store_false', - help=_('Set admin state up to false')) + help=_('Set admin state up to false.')) parser.add_argument( '--expected-codes', help=_('The list of HTTP status codes expected in ' @@ -59,7 +59,7 @@ def add_known_arguments(self, parser): 'attribute can contain one value, ' 'or a list of values separated by comma, ' 'or a range of values (e.g. "200-299"). If this attribute ' - 'is not specified, it defaults to "200". ')) + 'is not specified, it defaults to "200".')) parser.add_argument( '--http-method', help=_('The HTTP method used for requests by the monitor of type ' @@ -68,7 +68,7 @@ def add_known_arguments(self, parser): '--url-path', help=_('The HTTP path used in the HTTP request used by the monitor' ' to test a member health. This must be a string ' - 'beginning with a / (forward slash)')) + 'beginning with a / (forward slash).')) parser.add_argument( '--delay', required=True, @@ -87,7 +87,7 @@ def add_known_arguments(self, parser): parser.add_argument( '--type', required=True, choices=['PING', 'TCP', 'HTTP', 'HTTPS'], - help=_('One of predefined health monitor types')) + help=_('One of the predefined health monitor types.')) def args2body(self, parsed_args): body = { @@ -130,10 +130,10 @@ def get_parser(self, prog_name): parser = super(AssociateHealthMonitor, self).get_parser(prog_name) parser.add_argument( 'health_monitor_id', metavar='HEALTH_MONITOR_ID', - help=_('Health monitor to associate')) + help=_('Health monitor to associate.')) parser.add_argument( 'pool_id', metavar='POOL', - help=_('ID of the pool to be associated with the health monitor')) + help=_('ID of the pool to be associated with the health monitor.')) return parser def run(self, parsed_args): @@ -158,10 +158,10 @@ def get_parser(self, prog_name): parser = super(DisassociateHealthMonitor, self).get_parser(prog_name) parser.add_argument( 'health_monitor_id', metavar='HEALTH_MONITOR_ID', - help=_('Health monitor to associate')) + help=_('Health monitor to associate.')) parser.add_argument( 'pool_id', metavar='POOL', - help=_('ID of the pool to be associated with the health monitor')) + help=_('ID of the pool to be associated with the health monitor.')) return parser def run(self, parsed_args): diff --git a/neutronclient/neutron/v2_0/lb/member.py b/neutronclient/neutron/v2_0/lb/member.py index d053c8a80..2de016b2a 100644 --- a/neutronclient/neutron/v2_0/lb/member.py +++ b/neutronclient/neutron/v2_0/lb/member.py @@ -50,23 +50,23 @@ class CreateMember(neutronV20.CreateCommand): def add_known_arguments(self, parser): parser.add_argument( 'pool_id', metavar='POOL', - help=_('Pool id or name this vip belongs to')) + help=_('Pool ID or name this vip belongs to.')) parser.add_argument( '--admin-state-down', dest='admin_state', action='store_false', - help=_('Set admin state up to false')) + help=_('Set admin state up to false.')) parser.add_argument( '--weight', - help=_('Weight of pool member in the pool (default:1, [0..256])')) + help=_('Weight of pool member in the pool (default:1, [0..256]).')) parser.add_argument( '--address', required=True, - help=_('IP address of the pool member on the pool network. ')) + help=_('IP address of the pool member on the pool network.')) parser.add_argument( '--protocol-port', required=True, help=_('Port on which the pool member listens for requests or ' - 'connections. ')) + 'connections.')) def args2body(self, parsed_args): _pool_id = neutronV20.find_resourceid_by_name_or_id( diff --git a/neutronclient/neutron/v2_0/lb/pool.py b/neutronclient/neutron/v2_0/lb/pool.py index aa4d9f727..5de916bfc 100644 --- a/neutronclient/neutron/v2_0/lb/pool.py +++ b/neutronclient/neutron/v2_0/lb/pool.py @@ -55,33 +55,33 @@ def add_known_arguments(self, parser): parser.add_argument( '--admin-state-down', dest='admin_state', action='store_false', - help=_('Set admin state up to false')) + help=_('Set admin state up to false.')) parser.add_argument( '--description', - help=_('Description of the pool')) + help=_('Description of the pool.')) parser.add_argument( '--lb-method', required=True, choices=['ROUND_ROBIN', 'LEAST_CONNECTIONS', 'SOURCE_IP'], help=_('The algorithm used to distribute load between the members ' - 'of the pool')) + 'of the pool.')) parser.add_argument( '--name', required=True, - help=_('The name of the pool')) + help=_('The name of the pool.')) parser.add_argument( '--protocol', required=True, choices=['HTTP', 'HTTPS', 'TCP'], - help=_('Protocol for balancing')) + help=_('Protocol for balancing.')) parser.add_argument( '--subnet-id', metavar='SUBNET', required=True, help=_('The subnet on which the members of the pool will be ' - 'located')) + 'located.')) parser.add_argument( '--provider', - help=_('Provider name of loadbalancer service')) + help=_('Provider name of loadbalancer service.')) def args2body(self, parsed_args): _subnet_id = neutronV20.find_resourceid_by_name_or_id( diff --git a/neutronclient/neutron/v2_0/lb/vip.py b/neutronclient/neutron/v2_0/lb/vip.py index cf5a57c8c..45e6d97fa 100644 --- a/neutronclient/neutron/v2_0/lb/vip.py +++ b/neutronclient/neutron/v2_0/lb/vip.py @@ -49,38 +49,38 @@ class CreateVip(neutronV20.CreateCommand): def add_known_arguments(self, parser): parser.add_argument( 'pool_id', metavar='POOL', - help=_('Pool id or name this vip belongs to')) + help=_('Pool ID or name this vip belongs to.')) parser.add_argument( '--address', - help=_('IP address of the vip')) + help=_('IP address of the vip.')) parser.add_argument( '--admin-state-down', dest='admin_state', action='store_false', - help=_('Set admin state up to false')) + help=_('Set admin state up to false.')) parser.add_argument( '--connection-limit', help=_('The maximum number of connections per second allowed for ' - 'the vip. Positive integer or -1 for unlimited (default)')) + 'the vip. Positive integer or -1 for unlimited (default).')) parser.add_argument( '--description', - help=_('Description of the vip')) + help=_('Description of the vip.')) parser.add_argument( '--name', required=True, - help=_('Name of the vip')) + help=_('Name of the vip.')) parser.add_argument( '--protocol-port', required=True, help=_('TCP port on which to listen for client traffic that is ' - 'associated with the vip address')) + 'associated with the vip address.')) parser.add_argument( '--protocol', required=True, choices=['TCP', 'HTTP', 'HTTPS'], - help=_('Protocol for balancing')) + help=_('Protocol for balancing.')) parser.add_argument( '--subnet-id', metavar='SUBNET', required=True, - help=_('The subnet on which to allocate the vip address')) + help=_('The subnet on which to allocate the vip address.')) def args2body(self, parsed_args): _pool_id = neutronV20.find_resourceid_by_name_or_id( diff --git a/neutronclient/neutron/v2_0/metering.py b/neutronclient/neutron/v2_0/metering.py index 66fa29f5e..1bce239d9 100644 --- a/neutronclient/neutron/v2_0/metering.py +++ b/neutronclient/neutron/v2_0/metering.py @@ -47,14 +47,14 @@ class CreateMeteringLabel(neutronv20.CreateCommand): def add_known_arguments(self, parser): parser.add_argument( 'name', metavar='NAME', - help=_('Name of metering label to create')) + help=_('Name of metering label to create.')) parser.add_argument( '--description', - help=_('Description of metering label to create')) + help=_('Description of metering label to create.')) parser.add_argument( '--shared', action='store_true', - help=_('Set the label as shared')) + help=_('Set the label as shared.')) def args2body(self, parsed_args): body = {'metering_label': { @@ -105,18 +105,18 @@ class CreateMeteringLabelRule(neutronv20.CreateCommand): def add_known_arguments(self, parser): parser.add_argument( 'label_id', metavar='LABEL', - help=_('Id or Name of the label')) + help=_('Id or Name of the label.')) parser.add_argument( 'remote_ip_prefix', metavar='REMOTE_IP_PREFIX', - help=_('CIDR to match on')) + help=_('CIDR to match on.')) parser.add_argument( '--direction', default='ingress', choices=['ingress', 'egress'], - help=_('Direction of traffic, default:ingress')) + help=_('Direction of traffic, default: ingress.')) parser.add_argument( '--excluded', action='store_true', - help=_('Exclude this cidr from the label, default:not excluded')) + help=_('Exclude this CIDR from the label, default: not excluded.')) def args2body(self, parsed_args): neutron_client = self.get_client() diff --git a/neutronclient/neutron/v2_0/netpartition.py b/neutronclient/neutron/v2_0/netpartition.py index b8310584b..fafbfe01b 100644 --- a/neutronclient/neutron/v2_0/netpartition.py +++ b/neutronclient/neutron/v2_0/netpartition.py @@ -45,7 +45,7 @@ class CreateNetPartition(CreateCommand): def add_known_arguments(self, parser): parser.add_argument( 'name', metavar='name', - help='Name of NetPartition to create') + help='Name of netpartition to create.') def args2body(self, parsed_args): body = {'net_partition': {'name': parsed_args.name}, } diff --git a/neutronclient/neutron/v2_0/network.py b/neutronclient/neutron/v2_0/network.py index c7c4c168d..ffdaef6f5 100644 --- a/neutronclient/neutron/v2_0/network.py +++ b/neutronclient/neutron/v2_0/network.py @@ -114,7 +114,7 @@ def add_known_arguments(self, parser): parser.add_argument( '--admin-state-down', dest='admin_state', action='store_false', - help=_('Set Admin State Up to false')) + help=_('Set admin state up to false.')) parser.add_argument( '--admin_state_down', dest='admin_state', action='store_false', @@ -122,11 +122,11 @@ def add_known_arguments(self, parser): parser.add_argument( '--shared', action='store_true', - help=_('Set the network as shared'), + help=_('Set the network as shared.'), default=argparse.SUPPRESS) parser.add_argument( 'name', metavar='NAME', - help=_('Name of network to create')) + help=_('Name of network to create.')) def args2body(self, parsed_args): body = {'network': { diff --git a/neutronclient/neutron/v2_0/networkprofile.py b/neutronclient/neutron/v2_0/networkprofile.py index 1fcf3be4a..4dc0be6a5 100644 --- a/neutronclient/neutron/v2_0/networkprofile.py +++ b/neutronclient/neutron/v2_0/networkprofile.py @@ -53,23 +53,23 @@ class CreateNetworkProfile(neutronV20.CreateCommand): def add_known_arguments(self, parser): parser.add_argument('name', - help=_('Name for Network Profile')) + help=_('Name for network profile.')) parser.add_argument('segment_type', choices=SEGMENT_TYPE_CHOICES, - help='Segment type') + help='Segment type.') # TODO(Abhishek): Check on sub-type choices depending on segment_type parser.add_argument('--sub_type', help=_('Sub-type for the segment. Available sub-' 'types for overlay segments: native, enhanced; ' 'For trunk segments: vlan, overlay.')) parser.add_argument('--segment_range', - help=_('Range for the Segment')) + help=_('Range for the segment.')) parser.add_argument('--physical_network', - help=_('Name for the Physical Network')) + help=_('Name for the physical network.')) parser.add_argument('--multicast_ip_range', - help=_('Multicast IPv4 Range')) + help=_('Multicast IPv4 range.')) parser.add_argument("--add-tenant", - help=_("Add tenant to the network profile")) + help=_("Add tenant to the network profile.")) def args2body(self, parsed_args): body = {'network_profile': {'name': parsed_args.name}} @@ -118,7 +118,7 @@ class UpdateNetworkProfileV2(neutronV20.NeutronCommand): def get_parser(self, prog_name): parser = super(UpdateNetworkProfileV2, self).get_parser(prog_name) parser.add_argument("--remove-tenant", - help="Remove tenant from the network profile") + help="Remove tenant from the network profile.") return parser def run(self, parsed_args): diff --git a/neutronclient/neutron/v2_0/nsx/networkgateway.py b/neutronclient/neutron/v2_0/nsx/networkgateway.py index 0152eaf72..3a6e1fefe 100644 --- a/neutronclient/neutron/v2_0/nsx/networkgateway.py +++ b/neutronclient/neutron/v2_0/nsx/networkgateway.py @@ -26,15 +26,15 @@ DEV_RESOURCE = 'gateway_device' CONNECTOR_TYPE_HELP = _("Type of the transport zone connector to use for this " "device. Valid values are gre, stt, ipsecgre, " - "ipsecstt, and bridge. Defaults to stt") + "ipsecstt, and bridge. Defaults to stt.") CONNECTOR_IP_HELP = _("IP address for this device's transport connector. " "It must correspond to the IP address of the interface " - "used for tenant traffic on the NSX gateway node") + "used for tenant traffic on the NSX gateway node.") CLIENT_CERT_HELP = _("PEM certificate used by the NSX gateway transport node " - "to authenticate with the NSX controller") + "to authenticate with the NSX controller.") CLIENT_CERT_FILE_HELP = _("File containing the PEM certificate used by the " "NSX gateway transport node to authenticate with " - "the NSX controller") + "the NSX controller.") class ListGatewayDevice(neutronV20.ListCommand): @@ -85,7 +85,7 @@ class CreateGatewayDevice(neutronV20.CreateCommand): def add_known_arguments(self, parser): parser.add_argument( 'name', metavar='NAME', - help='Name of network gateway device to create') + help='Name of network gateway device to create.') parser.add_argument( '--connector-type', default='stt', @@ -119,7 +119,7 @@ class UpdateGatewayDevice(neutronV20.UpdateCommand): def add_known_arguments(self, parser): parser.add_argument( '--name', metavar='NAME', - help='New name for network gateway device') + help='New name for network gateway device.') parser.add_argument( '--connector-type', required=False, @@ -174,12 +174,12 @@ class CreateNetworkGateway(neutronV20.CreateCommand): def add_known_arguments(self, parser): parser.add_argument( 'name', metavar='NAME', - help=_('Name of network gateway to create')) + help=_('Name of network gateway to create.')) parser.add_argument( '--device', metavar='id=ID,interface_name=NAME_OR_ID', action='append', help=_('Device info for this gateway. You can repeat this ' - 'option for multiple devices for HA gateways')) + 'option for multiple devices for HA gateways.')) def args2body(self, parsed_args): body = {self.resource: { @@ -219,18 +219,18 @@ def get_parser(self, prog_name): self).get_parser(prog_name) parser.add_argument( 'net_gateway_id', metavar='NET-GATEWAY-ID', - help=_('ID of the network gateway')) + help=_('ID of the network gateway.')) parser.add_argument( 'network_id', metavar='NETWORK-ID', - help=_('ID of the internal network to connect on the gateway')) + help=_('ID of the internal network to connect on the gateway.')) parser.add_argument( '--segmentation-type', help=_('L2 segmentation strategy on the external side of ' - 'the gateway (e.g.: VLAN, FLAT)')) + 'the gateway (e.g.: VLAN, FLAT).')) parser.add_argument( '--segmentation-id', help=_('Identifier for the L2 segment on the external side ' - 'of the gateway')) + 'of the gateway.')) return parser def retrieve_ids(self, client, args): diff --git a/neutronclient/neutron/v2_0/nsx/qos_queue.py b/neutronclient/neutron/v2_0/nsx/qos_queue.py index e3eff3006..393f6b233 100644 --- a/neutronclient/neutron/v2_0/nsx/qos_queue.py +++ b/neutronclient/neutron/v2_0/nsx/qos_queue.py @@ -45,24 +45,24 @@ class CreateQoSQueue(neutronV20.CreateCommand): def add_known_arguments(self, parser): parser.add_argument( 'name', metavar='NAME', - help=_('Name of queue')) + help=_('Name of queue.')) parser.add_argument( '--min', - help=_('min-rate')), + help=_('Minimum rate.')), parser.add_argument( '--max', - help=_('max-rate')), + help=_('Maximum rate.')), parser.add_argument( '--qos-marking', - help=_('QOS marking untrusted/trusted')), + help=_('QOS marking as untrusted or trusted.')), parser.add_argument( '--default', default=False, - help=_('If true all ports created with be the size of this queue' - ' if queue is not specified')), + help=_('If true all created ports will be the size of this queue, ' + 'if queue is not specified')), parser.add_argument( '--dscp', - help=_('Differentiated Services Code Point')), + help=_('Differentiated Services Code Point.')), def args2body(self, parsed_args): params = {'name': parsed_args.name, diff --git a/neutronclient/neutron/v2_0/policyprofile.py b/neutronclient/neutron/v2_0/policyprofile.py index af38c1f6e..8ba616b42 100644 --- a/neutronclient/neutron/v2_0/policyprofile.py +++ b/neutronclient/neutron/v2_0/policyprofile.py @@ -58,9 +58,9 @@ class UpdatePolicyProfileV2(neutronV20.UpdateCommand): def get_parser(self, prog_name): parser = super(UpdatePolicyProfileV2, self).get_parser(prog_name) parser.add_argument("--add-tenant", - help=_("Add tenant to the policy profile")) + help=_("Add tenant to the policy profile.")) parser.add_argument("--remove-tenant", - help=_("Remove tenant from the policy profile")) + help=_("Remove tenant from the policy profile.")) return parser def run(self, parsed_args): diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index 3cb321b21..4e95b0b53 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -55,7 +55,7 @@ def get_parser(self, prog_name): parser = super(ListRouterPort, self).get_parser(prog_name) parser.add_argument( 'id', metavar='router', - help=_('ID or name of router to look up')) + help=_('ID or name of router to look up.')) return parser def get_data(self, parsed_args): @@ -85,7 +85,7 @@ def add_arguments_secgroup(self, parser): group_sg.add_argument( '--no-security-groups', action='store_true', - help=_('Associate no security groups with the port')) + help=_('Associate no security groups with the port.')) def _resolv_sgid(self, secgroup): return neutronV20.find_resourceid_by_name_or_id( @@ -149,24 +149,24 @@ class CreatePort(neutronV20.CreateCommand, UpdatePortSecGroupMixin, def add_known_arguments(self, parser): parser.add_argument( '--name', - help=_('Name of this port')) + help=_('Name of this port.')) parser.add_argument( '--admin-state-down', dest='admin_state', action='store_false', - help=_('Set admin state up to false')) + help=_('Set admin state up to false.')) parser.add_argument( '--admin_state_down', dest='admin_state', action='store_false', help=argparse.SUPPRESS) parser.add_argument( '--mac-address', - help=_('MAC address of this port')) + help=_('MAC address of this port.')) parser.add_argument( '--mac_address', help=argparse.SUPPRESS) parser.add_argument( '--device-id', - help=_('Device id of this port')) + help=_('Device ID of this port.')) parser.add_argument( '--device_id', help=argparse.SUPPRESS) @@ -186,7 +186,7 @@ def add_known_arguments(self, parser): parser.add_argument( 'network_id', metavar='NETWORK', - help=_('Network id or name this port belongs to')) + help=_('Network ID or name this port belongs to.')) def args2body(self, parsed_args): _network_id = neutronV20.find_resourceid_by_name_or_id( diff --git a/neutronclient/neutron/v2_0/quota.py b/neutronclient/neutron/v2_0/quota.py index 8825c4846..e13a26b3f 100644 --- a/neutronclient/neutron/v2_0/quota.py +++ b/neutronclient/neutron/v2_0/quota.py @@ -44,7 +44,7 @@ def get_parser(self, prog_name): parser = super(DeleteQuota, self).get_parser(prog_name) parser.add_argument( '--tenant-id', metavar='tenant-id', - help=_('The owner tenant ID')) + help=_('The owner tenant ID.')) parser.add_argument( '--tenant_id', help=argparse.SUPPRESS) @@ -96,7 +96,7 @@ def get_data(self, parsed_args): class ShowQuota(neutronV20.NeutronCommand, show.ShowOne): - """Show quotas of a given tenant + """Show quotas of a given tenant. """ api = 'network' @@ -107,7 +107,7 @@ def get_parser(self, prog_name): parser = super(ShowQuota, self).get_parser(prog_name) parser.add_argument( '--tenant-id', metavar='tenant-id', - help=_('The owner tenant ID')) + help=_('The owner tenant ID.')) parser.add_argument( '--tenant_id', help=argparse.SUPPRESS) @@ -152,43 +152,43 @@ def get_parser(self, prog_name): parser = super(UpdateQuota, self).get_parser(prog_name) parser.add_argument( '--tenant-id', metavar='tenant-id', - help=_('The owner tenant ID')) + help=_('The owner tenant ID.')) parser.add_argument( '--tenant_id', help=argparse.SUPPRESS) parser.add_argument( '--network', metavar='networks', - help=_('The limit of networks')) + help=_('The limit of networks.')) parser.add_argument( '--subnet', metavar='subnets', - help=_('The limit of subnets')) + help=_('The limit of subnets.')) parser.add_argument( '--port', metavar='ports', - help=_('The limit of ports')) + help=_('The limit of ports.')) parser.add_argument( '--router', metavar='routers', - help=_('The limit of routers')) + help=_('The limit of routers.')) parser.add_argument( '--floatingip', metavar='floatingips', - help=_('The limit of floating IPs')) + help=_('The limit of floating IPs.')) parser.add_argument( '--security-group', metavar='security_groups', - help=_('The limit of security groups')) + help=_('The limit of security groups.')) parser.add_argument( '--security-group-rule', metavar='security_group_rules', - help=_('The limit of security groups rules')) + help=_('The limit of security groups rules.')) parser.add_argument( '--vip', metavar='vips', - help=_('the limit of vips')) + help=_('The limit of vips.')) parser.add_argument( '--pool', metavar='pools', - help=_('the limit of pools')) + help=_('The limit of pools.')) parser.add_argument( '--member', metavar='members', - help=_('the limit of pool members')) + help=_('The limit of pool members.')) parser.add_argument( '--health-monitor', metavar='health_monitors', - help=_('the limit of health monitors')) + help=_('The limit of health monitors.')) return parser diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index 83f872988..67a4fe1d1 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -61,17 +61,17 @@ def add_known_arguments(self, parser): parser.add_argument( '--admin-state-down', dest='admin_state', action='store_false', - help=_('Set Admin State Up to false')) + help=_('Set admin state up to false.')) parser.add_argument( '--admin_state_down', dest='admin_state', action='store_false', help=argparse.SUPPRESS) parser.add_argument( 'name', metavar='NAME', - help=_('Name of router to create')) + help=_('Name of router to create.')) parser.add_argument( 'distributed', action='store_true', - help=_('Create a distributed router (VMware NSX plugin only)')) + help=_('Create a distributed router (VMware NSX plugin only).')) def args2body(self, parsed_args): body = {'router': { @@ -112,13 +112,13 @@ def get_parser(self, prog_name): parser = super(RouterInterfaceCommand, self).get_parser(prog_name) parser.add_argument( 'router_id', metavar='router-id', - help=_('ID of the router')) + help=_('ID of the router.')) parser.add_argument( 'interface', metavar='INTERFACE', help=_('The format is "SUBNET|subnet=SUBNET|port=PORT". ' 'Either a subnet or port must be specified. ' 'Both ID and name are accepted as SUBNET or PORT. ' - 'Note that "subnet=" can be omitted when specifying subnet.')) + 'Note that "subnet=" can be omitted when specifying a subnet.')) return parser def run(self, parsed_args): @@ -184,13 +184,13 @@ def get_parser(self, prog_name): parser = super(SetGatewayRouter, self).get_parser(prog_name) parser.add_argument( 'router_id', metavar='router-id', - help=_('ID of the router')) + help=_('ID of the router.')) parser.add_argument( 'external_network_id', metavar='external-network-id', - help=_('ID of the external network for the gateway')) + help=_('ID of the external network for the gateway.')) parser.add_argument( '--disable-snat', action='store_true', - help=_('Disable Source NAT on the router gateway')) + help=_('Disable source NAT on the router gateway.')) return parser def run(self, parsed_args): @@ -220,7 +220,7 @@ def get_parser(self, prog_name): parser = super(RemoveGatewayRouter, self).get_parser(prog_name) parser.add_argument( 'router_id', metavar='router-id', - help=_('ID of the router')) + help=_('ID of the router.')) return parser def run(self, parsed_args): diff --git a/neutronclient/neutron/v2_0/securitygroup.py b/neutronclient/neutron/v2_0/securitygroup.py index b59eba5cc..7e66e8a3c 100644 --- a/neutronclient/neutron/v2_0/securitygroup.py +++ b/neutronclient/neutron/v2_0/securitygroup.py @@ -48,10 +48,10 @@ class CreateSecurityGroup(neutronV20.CreateCommand): def add_known_arguments(self, parser): parser.add_argument( 'name', metavar='NAME', - help=_('Name of security group')) + help=_('Name of security group.')) parser.add_argument( '--description', - help=_('Description of security group')) + help=_('Description of security group.')) def args2body(self, parsed_args): body = {'security_group': { @@ -81,10 +81,10 @@ class UpdateSecurityGroup(neutronV20.UpdateCommand): def add_known_arguments(self, parser): parser.add_argument( '--name', - help=_('Name of security group')) + help=_('Name of security group.')) parser.add_argument( '--description', - help=_('Description of security group')) + help=_('Description of security group.')) def args2body(self, parsed_args): body = {'security_group': {}} @@ -113,7 +113,7 @@ def get_parser(self, prog_name): parser = super(ListSecurityGroupRule, self).get_parser(prog_name) parser.add_argument( '--no-nameconv', action='store_true', - help=_('Do not convert security group ID to its name')) + help=_('Do not convert security group ID to its name.')) return parser @staticmethod @@ -183,39 +183,39 @@ class CreateSecurityGroupRule(neutronV20.CreateCommand): def add_known_arguments(self, parser): parser.add_argument( 'security_group_id', metavar='SECURITY_GROUP', - help=_('Security group name or id to add rule.')) + help=_('Security group name or ID to add rule.')) parser.add_argument( '--direction', default='ingress', choices=['ingress', 'egress'], - help=_('Direction of traffic: ingress/egress')) + help=_('Direction of traffic: ingress/egress.')) parser.add_argument( '--ethertype', default='IPv4', help=_('IPv4/IPv6')) parser.add_argument( '--protocol', - help=_('Protocol of packet')) + help=_('Protocol of packet.')) parser.add_argument( '--port-range-min', - help=_('Starting port range')) + help=_('Starting port range.')) parser.add_argument( '--port_range_min', help=argparse.SUPPRESS) parser.add_argument( '--port-range-max', - help=_('Ending port range')) + help=_('Ending port range.')) parser.add_argument( '--port_range_max', help=argparse.SUPPRESS) parser.add_argument( '--remote-ip-prefix', - help=_('CIDR to match on')) + help=_('CIDR to match on.')) parser.add_argument( '--remote_ip_prefix', help=argparse.SUPPRESS) parser.add_argument( '--remote-group-id', metavar='REMOTE_GROUP', - help=_('Remote security group name or id to apply rule')) + help=_('Remote security group name or ID to apply rule.')) parser.add_argument( '--remote_group_id', help=argparse.SUPPRESS) diff --git a/neutronclient/neutron/v2_0/subnet.py b/neutronclient/neutron/v2_0/subnet.py index 869fa1f4a..b9c637bea 100644 --- a/neutronclient/neutron/v2_0/subnet.py +++ b/neutronclient/neutron/v2_0/subnet.py @@ -50,19 +50,19 @@ def _format_host_routes(subnet): def add_updatable_arguments(parser): parser.add_argument( '--name', - help=_('Name of this subnet')) + help=_('Name of this subnet.')) parser.add_argument( '--gateway', metavar='GATEWAY_IP', - help=_('Gateway ip of this subnet')) + help=_('Gateway IP of this subnet.')) parser.add_argument( '--no-gateway', action='store_true', - help=_('No distribution of gateway')) + help=_('No distribution of gateway.')) parser.add_argument( '--allocation-pool', metavar='start=IP_ADDR,end=IP_ADDR', action='append', dest='allocation_pools', type=utils.str2dict, help=_('Allocation pool IP addresses for this subnet ' - '(This option can be repeated)')) + '(This option can be repeated).')) parser.add_argument( '--allocation_pool', action='append', dest='allocation_pools', type=utils.str2dict, @@ -70,20 +70,20 @@ def add_updatable_arguments(parser): parser.add_argument( '--host-route', metavar='destination=CIDR,nexthop=IP_ADDR', action='append', dest='host_routes', type=utils.str2dict, - help=_('Additional route (This option can be repeated)')) + help=_('Additional route (This option can be repeated).')) parser.add_argument( '--dns-nameserver', metavar='DNS_NAMESERVER', action='append', dest='dns_nameservers', help=_('DNS name server for this subnet ' - '(This option can be repeated)')) + '(This option can be repeated).')) parser.add_argument( '--disable-dhcp', action='store_true', - help=_('Disable DHCP for this subnet')) + help=_('Disable DHCP for this subnet.')) parser.add_argument( '--enable-dhcp', action='store_true', - help=_('Enable DHCP for this subnet')) + help=_('Enable DHCP for this subnet.')) def updatable_args2body(parsed_args, body): @@ -145,7 +145,7 @@ def add_known_arguments(self, parser): '--ip-version', type=int, default=4, choices=[4, 6], - help=_('IP version with default 4')) + help=_('IP version to use, default is 4.')) parser.add_argument( '--ip_version', type=int, @@ -153,10 +153,10 @@ def add_known_arguments(self, parser): help=argparse.SUPPRESS) parser.add_argument( 'network_id', metavar='NETWORK', - help=_('Network id or name this subnet belongs to')) + help=_('Network ID or name this subnet belongs to.')) parser.add_argument( 'cidr', metavar='CIDR', - help=_('CIDR of subnet to create')) + help=_('CIDR of subnet to create.')) def args2body(self, parsed_args): _network_id = neutronV20.find_resourceid_by_name_or_id( diff --git a/neutronclient/neutron/v2_0/vpn/ikepolicy.py b/neutronclient/neutron/v2_0/vpn/ikepolicy.py index 2f15e5245..1191c5232 100644 --- a/neutronclient/neutron/v2_0/vpn/ikepolicy.py +++ b/neutronclient/neutron/v2_0/vpn/ikepolicy.py @@ -25,7 +25,7 @@ class ListIKEPolicy(neutronv20.ListCommand): - """List IKEPolicies that belong to a tenant.""" + """List IKE policies that belong to a tenant.""" resource = 'ikepolicy' log = logging.getLogger(__name__ + '.ListIKEPolicy') @@ -37,14 +37,14 @@ class ListIKEPolicy(neutronv20.ListCommand): class ShowIKEPolicy(neutronv20.ShowCommand): - """Show information of a given IKEPolicy.""" + """Show information of a given IKE policy.""" resource = 'ikepolicy' log = logging.getLogger(__name__ + '.ShowIKEPolicy') class CreateIKEPolicy(neutronv20.CreateCommand): - """Create an IKEPolicy.""" + """Create an IKE policy.""" resource = 'ikepolicy' log = logging.getLogger(__name__ + '.CreateIKEPolicy') @@ -57,14 +57,14 @@ def add_known_arguments(self, parser): '--auth-algorithm', default='sha1', choices=['sha1'], help=_('Authentication algorithm in lowercase. ' - 'default:sha1')) + 'Default:sha1')) parser.add_argument( '--encryption-algorithm', default='aes-128', choices=['3des', 'aes-128', 'aes-192', 'aes-256'], - help=_('Encryption Algorithm in lowercase, default:aes-128')) + help=_('Encryption algorithm in lowercase, default:aes-128')) parser.add_argument( '--phase1-negotiation-mode', default='main', choices=['main'], @@ -84,7 +84,7 @@ def add_known_arguments(self, parser): help=vpn_utils.lifetime_help("IKE")) parser.add_argument( 'name', metavar='NAME', - help=_('Name of the IKE Policy')) + help=_('Name of the IKE policy.')) def args2body(self, parsed_args): @@ -108,7 +108,7 @@ def args2body(self, parsed_args): class UpdateIKEPolicy(neutronv20.UpdateCommand): - """Update a given IKE Policy.""" + """Update a given IKE policy.""" resource = 'ikepolicy' log = logging.getLogger(__name__ + '.UpdateIKEPolicy') @@ -131,7 +131,7 @@ def args2body(self, parsed_args): class DeleteIKEPolicy(neutronv20.DeleteCommand): - """Delete a given IKE Policy.""" + """Delete a given IKE policy.""" resource = 'ikepolicy' log = logging.getLogger(__name__ + '.DeleteIKEPolicy') diff --git a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py index 35ca19e65..ad8c851de 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py +++ b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py @@ -54,7 +54,7 @@ class ShowIPsecSiteConnection(neutronv20.ShowCommand): class CreateIPsecSiteConnection(neutronv20.CreateCommand): - """Create an IPsecSiteConnection.""" + """Create an IPsec site connection.""" resource = 'ipsec_site_connection' log = logging.getLogger(__name__ + '.CreateIPsecSiteConnection') @@ -62,13 +62,13 @@ def add_known_arguments(self, parser): parser.add_argument( '--admin-state-down', default=True, action='store_false', - help=_('Set admin state up to false')) + help=_('Set admin state up to false.')) parser.add_argument( '--name', - help=_('Set friendly name for the connection')) + help=_('Set friendly name for the connection.')) parser.add_argument( '--description', - help=_('Set a description for the connection')) + help=_('Set a description for the connection.')) parser.add_argument( '--mtu', default='1500', @@ -82,19 +82,19 @@ def add_known_arguments(self, parser): '--dpd', metavar="action=ACTION,interval=INTERVAL,timeout=TIMEOUT", type=utils.str2dict, - help=vpn_utils.dpd_help("IPsec Connection")) + help=vpn_utils.dpd_help("IPsec connection.")) parser.add_argument( '--vpnservice-id', metavar='VPNSERVICE', required=True, - help=_('VPNService instance id associated with this connection')) + help=_('VPN service instance ID associated with this connection.')) parser.add_argument( '--ikepolicy-id', metavar='IKEPOLICY', required=True, - help=_('IKEPolicy id associated with this connection')) + help=_('IKE policy ID associated with this connection.')) parser.add_argument( '--ipsecpolicy-id', metavar='IPSECPOLICY', required=True, - help=_('IPsecPolicy id associated with this connection')) + help=_('IPsec policy ID associated with this connection.')) parser.add_argument( '--peer-address', required=True, @@ -108,11 +108,11 @@ def add_known_arguments(self, parser): '--peer-cidr', action='append', dest='peer_cidrs', required=True, - help=_('Remote subnet(s) in CIDR format')) + help=_('Remote subnet(s) in CIDR format.')) parser.add_argument( '--psk', required=True, - help=_('Pre-Shared Key string')) + help=_('Pre-shared key string.')) def args2body(self, parsed_args): _vpnservice_id = neutronv20.find_resourceid_by_name_or_id( @@ -173,7 +173,7 @@ def add_known_arguments(self, parser): '--dpd', metavar="action=ACTION,interval=INTERVAL,timeout=TIMEOUT", type=utils.str2dict, - help=vpn_utils.dpd_help("IPsec Connection")) + help=vpn_utils.dpd_help("IPsec connection")) def args2body(self, parsed_args): body = {'ipsec_site_connection': { diff --git a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py index a8a43238c..65d25414b 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py +++ b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py @@ -36,14 +36,14 @@ class ListIPsecPolicy(neutronv20.ListCommand): class ShowIPsecPolicy(neutronv20.ShowCommand): - """Show information of a given ipsecpolicy.""" + """Show information of a given IPsec policy.""" resource = 'ipsecpolicy' log = logging.getLogger(__name__ + '.ShowIPsecPolicy') class CreateIPsecPolicy(neutronv20.CreateCommand): - """Create an ipsecpolicy.""" + """Create an IPsec policy.""" resource = 'ipsecpolicy' log = logging.getLogger(__name__ + '.CreateIPsecPolicy') @@ -51,11 +51,11 @@ class CreateIPsecPolicy(neutronv20.CreateCommand): def add_known_arguments(self, parser): parser.add_argument( '--description', - help=_('Description of the IPsecPolicy')) + help=_('Description of the IPsec policy.')) parser.add_argument( '--transform-protocol', default='esp', choices=['esp', 'ah', 'ah-esp'], - help=_('Transform Protocol in lowercase, default:esp')) + help=_('Transform protocol in lowercase, default:esp')) parser.add_argument( '--auth-algorithm', default='sha1', choices=['sha1'], @@ -66,11 +66,11 @@ def add_known_arguments(self, parser): 'aes-128', 'aes-192', 'aes-256'], - help=_('Encryption Algorithm in lowercase, default:aes-128')) + help=_('Encryption algorithm in lowercase, default:aes-128')) parser.add_argument( '--encapsulation-mode', default='tunnel', choices=['tunnel', 'transport'], - help=_('Encapsulation Mode in lowercase, default:tunnel')) + help=_('Encapsulation mode in lowercase, default:tunnel')) parser.add_argument( '--pfs', default='group5', choices=['group2', 'group5', 'group14'], @@ -82,7 +82,7 @@ def add_known_arguments(self, parser): help=vpn_utils.lifetime_help("IPsec")) parser.add_argument( 'name', metavar='NAME', - help=_('Name of the IPsecPolicy')) + help=_('Name of the IPsec policy.')) def args2body(self, parsed_args): @@ -108,7 +108,7 @@ def args2body(self, parsed_args): class UpdateIPsecPolicy(neutronv20.UpdateCommand): - """Update a given ipsec policy.""" + """Update a given IPsec policy.""" resource = 'ipsecpolicy' log = logging.getLogger(__name__ + '.UpdateIPsecPolicy') @@ -131,7 +131,7 @@ def args2body(self, parsed_args): class DeleteIPsecPolicy(neutronv20.DeleteCommand): - """Delete a given ipsecpolicy.""" + """Delete a given IPsec policy.""" resource = 'ipsecpolicy' log = logging.getLogger(__name__ + '.DeleteIPsecPolicy') diff --git a/neutronclient/neutron/v2_0/vpn/utils.py b/neutronclient/neutron/v2_0/vpn/utils.py index fa1091161..832a6a2c7 100644 --- a/neutronclient/neutron/v2_0/vpn/utils.py +++ b/neutronclient/neutron/v2_0/vpn/utils.py @@ -98,14 +98,14 @@ def validate_lifetime_dict(lifetime_dict): def lifetime_help(policy): - lifetime = _("%s Lifetime Attributes." - "'units'-seconds,default:seconds. " + lifetime = _("%s lifetime attributes. " + "'units'-seconds, default:seconds. " "'value'-non negative integer, default:3600.") % policy return lifetime def dpd_help(policy): - dpd = _(" %s Dead Peer Detection Attributes. " + dpd = _(" %s Dead Peer Detection attributes." " 'action'-hold,clear,disabled,restart,restart-by-peer." " 'interval' and 'timeout' are non negative integers. " " 'interval' should be less than 'timeout' value. " diff --git a/neutronclient/neutron/v2_0/vpn/vpnservice.py b/neutronclient/neutron/v2_0/vpn/vpnservice.py index 87bec2fee..bcd0c9053 100644 --- a/neutronclient/neutron/v2_0/vpn/vpnservice.py +++ b/neutronclient/neutron/v2_0/vpn/vpnservice.py @@ -23,7 +23,7 @@ class ListVPNService(neutronv20.ListCommand): - """List VPNService configurations that belong to a given tenant.""" + """List VPN service configurations that belong to a given tenant.""" resource = 'vpnservice' log = logging.getLogger(__name__ + '.ListVPNService') @@ -36,14 +36,14 @@ class ListVPNService(neutronv20.ListCommand): class ShowVPNService(neutronv20.ShowCommand): - """Show information of a given VPNService.""" + """Show information of a given VPN service.""" resource = 'vpnservice' log = logging.getLogger(__name__ + '.ShowVPNService') class CreateVPNService(neutronv20.CreateCommand): - """Create a VPNService.""" + """Create a VPN service.""" resource = 'vpnservice' log = logging.getLogger(__name__ + '.CreateVPNService') @@ -51,19 +51,19 @@ def add_known_arguments(self, parser): parser.add_argument( '--admin-state-down', dest='admin_state', action='store_false', - help=_('Set admin state up to false')) + help=_('Set admin state up to false.')) parser.add_argument( '--name', - help=_('Set a name for the vpnservice')) + help=_('Set a name for the VPN service.')) parser.add_argument( '--description', - help=_('Set a description for the vpnservice')) + help=_('Set a description for the VPN service.')) parser.add_argument( 'router', metavar='ROUTER', - help=_('Router unique identifier for the vpnservice')) + help=_('Router unique identifier for the VPN service.')) parser.add_argument( 'subnet', metavar='SUBNET', - help=_('Subnet unique identifier for the vpnservice deployment')) + help=_('Subnet unique identifier for the VPN service deployment.')) def args2body(self, parsed_args): _subnet_id = neutronv20.find_resourceid_by_name_or_id( @@ -84,14 +84,14 @@ def args2body(self, parsed_args): class UpdateVPNService(neutronv20.UpdateCommand): - """Update a given VPNService.""" + """Update a given VPN service.""" resource = 'vpnservice' log = logging.getLogger(__name__ + '.UpdateVPNService') class DeleteVPNService(neutronv20.DeleteCommand): - """Delete a given VPNService.""" + """Delete a given VPN service.""" resource = 'vpnservice' log = logging.getLogger(__name__ + '.DeleteVPNService') diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 31e23193d..850c283e0 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -361,20 +361,20 @@ def build_option_parser(self, description, version): action='store_const', dest='verbose_level', const=0, - help=_('Suppress output except warnings and errors')) + help=_('Suppress output except warnings and errors.')) parser.add_argument( '-h', '--help', action=HelpAction, nargs=0, default=self, # tricky - help=_("Show this help message and exit")) + help=_("Show this help message and exit.")) # Global arguments parser.add_argument( '--os-auth-strategy', metavar='', default=env('OS_AUTH_STRATEGY', default='keystone'), - help=_('Authentication strategy (Env: OS_AUTH_STRATEGY' - ', default keystone). For now, any other value will' - ' disable the authentication')) + help=_('Authentication strategy, defaults to ' + 'env[OS_AUTH_STRATEGY] or keystone. For now, any ' + 'other value will disable the authentication.')) parser.add_argument( '--os_auth_strategy', help=argparse.SUPPRESS) @@ -382,7 +382,7 @@ def build_option_parser(self, description, version): parser.add_argument( '--os-auth-url', metavar='', default=env('OS_AUTH_URL'), - help=_('Authentication URL (Env: OS_AUTH_URL)')) + help=_('Authentication URL, defaults to env[OS_AUTH_URL].')) parser.add_argument( '--os_auth_url', help=argparse.SUPPRESS) @@ -390,7 +390,8 @@ def build_option_parser(self, description, version): parser.add_argument( '--os-tenant-name', metavar='', default=env('OS_TENANT_NAME'), - help=_('Authentication tenant name (Env: OS_TENANT_NAME)')) + help=_('Authentication tenant name, defaults to ' + 'env[OS_TENANT_NAME].')) parser.add_argument( '--os_tenant_name', help=argparse.SUPPRESS) @@ -398,12 +399,13 @@ def build_option_parser(self, description, version): parser.add_argument( '--os-tenant-id', metavar='', default=env('OS_TENANT_ID'), - help=_('Authentication tenant ID (Env: OS_TENANT_ID)')) + help=_('Authentication tenant ID, defaults to ' + 'env[OS_TENANT_ID].')) parser.add_argument( '--os-username', metavar='', default=utils.env('OS_USERNAME'), - help=_('Authentication username (Env: OS_USERNAME)')) + help=_('Authentication username, defaults to env[OS_USERNAME].')) parser.add_argument( '--os_username', help=argparse.SUPPRESS) @@ -416,7 +418,7 @@ def build_option_parser(self, description, version): parser.add_argument( '--os-password', metavar='', default=utils.env('OS_PASSWORD'), - help=_('Authentication password (Env: OS_PASSWORD)')) + help=_('Authentication password, defaults to env[OS_PASSWORD].')) parser.add_argument( '--os_password', help=argparse.SUPPRESS) @@ -424,7 +426,8 @@ def build_option_parser(self, description, version): parser.add_argument( '--os-region-name', metavar='', default=env('OS_REGION_NAME'), - help=_('Authentication region name (Env: OS_REGION_NAME)')) + help=_('Authentication region name, defaults to ' + 'env[OS_REGION_NAME].')) parser.add_argument( '--os_region_name', help=argparse.SUPPRESS) @@ -432,7 +435,7 @@ def build_option_parser(self, description, version): parser.add_argument( '--os-token', metavar='', default=env('OS_TOKEN'), - help=_('Defaults to env[OS_TOKEN]')) + help=_('Authentication token, defaults to env[OS_TOKEN].')) parser.add_argument( '--os_token', help=argparse.SUPPRESS) @@ -450,7 +453,7 @@ def build_option_parser(self, description, version): parser.add_argument( '--os-url', metavar='', default=env('OS_URL'), - help=_('Defaults to env[OS_URL]')) + help=_('Defaults to env[OS_URL].')) parser.add_argument( '--os_url', help=argparse.SUPPRESS) @@ -461,7 +464,7 @@ def build_option_parser(self, description, version): default=env('OS_CACERT', default=None), help=_("Specify a CA bundle file to use in " "verifying a TLS (https) server certificate. " - "Defaults to env[OS_CACERT]")) + "Defaults to env[OS_CACERT].")) parser.add_argument( '--insecure', diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 7b6b1ce3e..bed9baf62 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -512,28 +512,28 @@ def show_security_group_rule(self, security_group_rule, **_params): @APIParamsCall def list_vpnservices(self, retrieve_all=True, **_params): - """Fetches a list of all configured VPNServices for a tenant.""" + """Fetches a list of all configured VPN services for a tenant.""" return self.list('vpnservices', self.vpnservices_path, retrieve_all, **_params) @APIParamsCall def show_vpnservice(self, vpnservice, **_params): - """Fetches information of a specific VPNService.""" + """Fetches information of a specific VPN service.""" return self.get(self.vpnservice_path % (vpnservice), params=_params) @APIParamsCall def create_vpnservice(self, body=None): - """Creates a new VPNService.""" + """Creates a new VPN service.""" return self.post(self.vpnservices_path, body=body) @APIParamsCall def update_vpnservice(self, vpnservice, body=None): - """Updates a VPNService.""" + """Updates a VPN service.""" return self.put(self.vpnservice_path % (vpnservice), body=body) @APIParamsCall def delete_vpnservice(self, vpnservice): - """Deletes the specified VPNService.""" + """Deletes the specified VPN service.""" return self.delete(self.vpnservice_path % (vpnservice)) @APIParamsCall From 58e48df0f8ce5821a3979ca8407415bf7c23859e Mon Sep 17 00:00:00 2001 From: Dirk Mueller Date: Thu, 13 Feb 2014 11:13:12 +0100 Subject: [PATCH 035/845] Switch over to mox3 mox3 is a Python 3.x compatible drop-in and allows to continue with the porting effort to Python 3.x with minimal code changes. Closes-Bug: #1283482 Change-Id: Ib052f1b87e312481e4c7c8d775ece94f53e7106b --- neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py | 2 +- neutronclient/tests/unit/lb/test_cli20_healthmonitor.py | 2 +- neutronclient/tests/unit/lb/test_cli20_pool.py | 2 +- neutronclient/tests/unit/test_auth.py | 2 +- neutronclient/tests/unit/test_cli20.py | 2 +- neutronclient/tests/unit/test_cli20_network.py | 2 +- neutronclient/tests/unit/test_cli20_nsx_networkgateway.py | 2 +- neutronclient/tests/unit/test_cli20_packetfilter.py | 2 +- neutronclient/tests/unit/test_cli20_port.py | 2 +- neutronclient/tests/unit/test_cli20_securitygroup.py | 2 +- neutronclient/tests/unit/test_http.py | 2 +- neutronclient/tests/unit/test_name_or_id.py | 2 +- neutronclient/tests/unit/test_shell.py | 2 +- neutronclient/tests/unit/test_ssl.py | 2 +- test-requirements.txt | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py b/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py index d217c91ce..8912c5e22 100644 --- a/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py +++ b/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py @@ -18,7 +18,7 @@ import sys -import mox +from mox3 import mox from neutronclient.neutron.v2_0.fw import firewallpolicy from neutronclient import shell diff --git a/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py b/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py index ab2986e43..09c5234c6 100644 --- a/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py +++ b/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py @@ -18,7 +18,7 @@ import sys -import mox +from mox3 import mox from neutronclient.neutron.v2_0.lb import healthmonitor from neutronclient.tests.unit import test_cli20 diff --git a/neutronclient/tests/unit/lb/test_cli20_pool.py b/neutronclient/tests/unit/lb/test_cli20_pool.py index 56def65af..c293fa87f 100644 --- a/neutronclient/tests/unit/lb/test_cli20_pool.py +++ b/neutronclient/tests/unit/lb/test_cli20_pool.py @@ -18,7 +18,7 @@ import sys -import mox +from mox3 import mox from neutronclient.neutron.v2_0.lb import pool from neutronclient.tests.unit import test_cli20 diff --git a/neutronclient/tests/unit/test_auth.py b/neutronclient/tests/unit/test_auth.py index ae077c8ad..34ef71d4e 100644 --- a/neutronclient/tests/unit/test_auth.py +++ b/neutronclient/tests/unit/test_auth.py @@ -19,7 +19,7 @@ import uuid from keystoneclient import exceptions as k_exceptions -import mox +from mox3 import mox import requests import testtools diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 71130111e..7ceb5cc86 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -19,7 +19,7 @@ import contextlib import cStringIO import fixtures -import mox +from mox3 import mox import requests import sys import testtools diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index 5e9be2a69..1e1b9a9fe 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -15,7 +15,7 @@ import sys -import mox +from mox3 import mox from neutronclient.common import exceptions from neutronclient.common import utils diff --git a/neutronclient/tests/unit/test_cli20_nsx_networkgateway.py b/neutronclient/tests/unit/test_cli20_nsx_networkgateway.py index 9db388faa..8a52a281c 100644 --- a/neutronclient/tests/unit/test_cli20_nsx_networkgateway.py +++ b/neutronclient/tests/unit/test_cli20_nsx_networkgateway.py @@ -16,7 +16,7 @@ import sys -import mox +from mox3 import mox from neutronclient.neutron.v2_0.nsx import networkgateway as nwgw from neutronclient.tests.unit import test_cli20 diff --git a/neutronclient/tests/unit/test_cli20_packetfilter.py b/neutronclient/tests/unit/test_cli20_packetfilter.py index 2cb78e6e2..9d7546d96 100644 --- a/neutronclient/tests/unit/test_cli20_packetfilter.py +++ b/neutronclient/tests/unit/test_cli20_packetfilter.py @@ -15,7 +15,7 @@ import sys -import mox +from mox3 import mox from neutronclient.common import exceptions from neutronclient.neutron.v2_0.nec import packetfilter as pf diff --git a/neutronclient/tests/unit/test_cli20_port.py b/neutronclient/tests/unit/test_cli20_port.py index 0f41f247f..ee43c7568 100644 --- a/neutronclient/tests/unit/test_cli20_port.py +++ b/neutronclient/tests/unit/test_cli20_port.py @@ -16,7 +16,7 @@ import sys -import mox +from mox3 import mox from neutronclient.neutron.v2_0 import port from neutronclient import shell diff --git a/neutronclient/tests/unit/test_cli20_securitygroup.py b/neutronclient/tests/unit/test_cli20_securitygroup.py index 3ffd0232b..e6127ff9b 100644 --- a/neutronclient/tests/unit/test_cli20_securitygroup.py +++ b/neutronclient/tests/unit/test_cli20_securitygroup.py @@ -16,7 +16,7 @@ import sys -import mox +from mox3 import mox from neutronclient.neutron.v2_0 import securitygroup from neutronclient.tests.unit import test_cli20 diff --git a/neutronclient/tests/unit/test_http.py b/neutronclient/tests/unit/test_http.py index 77a3a4a75..f2a6675a5 100644 --- a/neutronclient/tests/unit/test_http.py +++ b/neutronclient/tests/unit/test_http.py @@ -13,7 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. -import mox +from mox3 import mox import testtools from neutronclient.client import HTTPClient diff --git a/neutronclient/tests/unit/test_name_or_id.py b/neutronclient/tests/unit/test_name_or_id.py index 5866f3209..7ddf80428 100644 --- a/neutronclient/tests/unit/test_name_or_id.py +++ b/neutronclient/tests/unit/test_name_or_id.py @@ -16,7 +16,7 @@ import uuid -import mox +from mox3 import mox import testtools from neutronclient.common import exceptions diff --git a/neutronclient/tests/unit/test_shell.py b/neutronclient/tests/unit/test_shell.py index 9c50fe938..b0bb82af1 100644 --- a/neutronclient/tests/unit/test_shell.py +++ b/neutronclient/tests/unit/test_shell.py @@ -21,7 +21,7 @@ import sys import fixtures -import mox +from mox3 import mox import testtools from testtools import matchers diff --git a/neutronclient/tests/unit/test_ssl.py b/neutronclient/tests/unit/test_ssl.py index 91d052589..be6e3a2dd 100644 --- a/neutronclient/tests/unit/test_ssl.py +++ b/neutronclient/tests/unit/test_ssl.py @@ -14,7 +14,7 @@ # under the License. import fixtures -import mox +from mox3 import mox import requests import testtools diff --git a/test-requirements.txt b/test-requirements.txt index 402590b90..164fbf7cc 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -4,7 +4,7 @@ cliff-tablib>=1.0 coverage>=3.6 discover fixtures>=0.3.14 -mox>=0.5.3 +mox3>=0.7.0 python-subunit>=0.0.18 sphinx>=1.1.2,<1.2 testrepository>=0.0.18 From 2e6f2381991bc9c3e91bf5d46f8be40ebc7a85b0 Mon Sep 17 00:00:00 2001 From: Christian Berendt Date: Fri, 27 Jun 2014 13:52:46 +0200 Subject: [PATCH 036/845] Changed 'xml' to 'XML' Like Lana Brindley mentioned in https://review.openstack.org/#/c/102900/ 'xml' should be 'XML' instead. Change-Id: If500ec9325eff98d58810b69c6fc84211e20638a --- neutronclient/common/serializer.py | 8 ++++---- neutronclient/v2_0/client.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/neutronclient/common/serializer.py b/neutronclient/common/serializer.py index 091543c4f..21092b7a8 100644 --- a/neutronclient/common/serializer.py +++ b/neutronclient/common/serializer.py @@ -67,9 +67,9 @@ class XMLDictSerializer(DictSerializer): def __init__(self, metadata=None, xmlns=None): """XMLDictSerializer constructor. - :param metadata: information needed to deserialize xml into + :param metadata: information needed to deserialize XML into a dictionary. - :param xmlns: XML namespace to include with serialized xml + :param xmlns: XML namespace to include with serialized XML """ super(XMLDictSerializer, self).__init__() self.metadata = metadata or {} @@ -123,7 +123,7 @@ def to_xml_string(self, node, used_prefixes, has_atom=False): return etree.tostring(node, encoding='UTF-8') #NOTE (ameade): the has_atom should be removed after all of the - # xml serializers and view builders have been updated to the current + # XML serializers and view builders have been updated to the current # spec that required all responses include the xmlns:atom, the has_atom # flag is to prevent current tests from breaking def _add_xmlns(self, node, used_prefixes, has_atom=False): @@ -235,7 +235,7 @@ class XMLDeserializer(TextDeserializer): def __init__(self, metadata=None): """XMLDeserializer constructor. - :param metadata: information needed to deserialize xml into + :param metadata: information needed to deserialize XML into a dictionary. """ super(XMLDeserializer, self).__init__() diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 65d1816e8..60070de91 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -1257,7 +1257,7 @@ def get_status_code(self, response): return response.status_code def serialize(self, data): - """Serializes a dictionary into either xml or json. + """Serializes a dictionary into either XML or json. A dictionary with a single key can be passed and it can contain any structure. @@ -1272,7 +1272,7 @@ def serialize(self, data): type(data)) def deserialize(self, data, status_code): - """Deserializes an xml or json string into a dictionary.""" + """Deserializes an XML or json string into a dictionary.""" if status_code == 204: return data return serializer.Serializer(self.get_attr_metadata()).deserialize( From 8a2349ac00bb041f19966e1650793626082309c8 Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Mon, 30 Jun 2014 12:00:44 +0200 Subject: [PATCH 037/845] Sync with oslo This brings some bugfixes for Python 3 support. Sync with 468afcb335a98dcfdc1520a73937982107bc2758 Change-Id: Ida73ae17b945e084a52b7024a91707f7729adfa1 --- .../openstack/common/gettextutils.py | 562 ++++++++++++------ neutronclient/openstack/common/importutils.py | 14 +- neutronclient/openstack/common/jsonutils.py | 8 +- neutronclient/openstack/common/strutils.py | 107 ++-- neutronclient/openstack/common/timeutils.py | 38 +- 5 files changed, 480 insertions(+), 249 deletions(-) diff --git a/neutronclient/openstack/common/gettextutils.py b/neutronclient/openstack/common/gettextutils.py index 25432d677..22243e156 100644 --- a/neutronclient/openstack/common/gettextutils.py +++ b/neutronclient/openstack/common/gettextutils.py @@ -23,22 +23,122 @@ """ import copy +import functools import gettext -import logging +import locale +from logging import handlers import os -import re -import UserString from babel import localedata import six -_localedir = os.environ.get('neutronclient'.upper() + '_LOCALEDIR') -_t = gettext.translation('neutronclient', localedir=_localedir, fallback=True) - _AVAILABLE_LANGUAGES = {} + +# FIXME(dhellmann): Remove this when moving to oslo.i18n. USE_LAZY = False +class TranslatorFactory(object): + """Create translator functions + """ + + def __init__(self, domain, lazy=False, localedir=None): + """Establish a set of translation functions for the domain. + + :param domain: Name of translation domain, + specifying a message catalog. + :type domain: str + :param lazy: Delays translation until a message is emitted. + Defaults to False. + :type lazy: Boolean + :param localedir: Directory with translation catalogs. + :type localedir: str + """ + self.domain = domain + self.lazy = lazy + if localedir is None: + localedir = os.environ.get(domain.upper() + '_LOCALEDIR') + self.localedir = localedir + + def _make_translation_func(self, domain=None): + """Return a new translation function ready for use. + + Takes into account whether or not lazy translation is being + done. + + The domain can be specified to override the default from the + factory, but the localedir from the factory is always used + because we assume the log-level translation catalogs are + installed in the same directory as the main application + catalog. + + """ + if domain is None: + domain = self.domain + if self.lazy: + return functools.partial(Message, domain=domain) + t = gettext.translation( + domain, + localedir=self.localedir, + fallback=True, + ) + if six.PY3: + return t.gettext + return t.ugettext + + @property + def primary(self): + "The default translation function." + return self._make_translation_func() + + def _make_log_translation_func(self, level): + return self._make_translation_func(self.domain + '-log-' + level) + + @property + def log_info(self): + "Translate info-level log messages." + return self._make_log_translation_func('info') + + @property + def log_warning(self): + "Translate warning-level log messages." + return self._make_log_translation_func('warning') + + @property + def log_error(self): + "Translate error-level log messages." + return self._make_log_translation_func('error') + + @property + def log_critical(self): + "Translate critical-level log messages." + return self._make_log_translation_func('critical') + + +# NOTE(dhellmann): When this module moves out of the incubator into +# oslo.i18n, these global variables can be moved to an integration +# module within each application. + +# Create the global translation functions. +_translators = TranslatorFactory('neutronclient') + +# The primary translation function using the well-known name "_" +_ = _translators.primary + +# Translators for log levels. +# +# The abbreviated names are meant to reflect the usual use of a short +# name like '_'. The "L" is for "log" and the other letter comes from +# the level. +_LI = _translators.log_info +_LW = _translators.log_warning +_LE = _translators.log_error +_LC = _translators.log_critical + +# NOTE(dhellmann): End of globals that will move to the application's +# integration module. + + def enable_lazy(): """Convenience function for configuring _() to use lazy gettext @@ -47,17 +147,18 @@ def enable_lazy(): your project is importing _ directly instead of using the gettextutils.install() way of importing the _ function. """ - global USE_LAZY + # FIXME(dhellmann): This function will be removed in oslo.i18n, + # because the TranslatorFactory makes it superfluous. + global _, _LI, _LW, _LE, _LC, USE_LAZY + tf = TranslatorFactory('neutronclient', lazy=True) + _ = tf.primary + _LI = tf.log_info + _LW = tf.log_warning + _LE = tf.log_error + _LC = tf.log_critical USE_LAZY = True -def _(msg): - if USE_LAZY: - return Message(msg, 'neutronclient') - else: - return _t.ugettext(msg) - - def install(domain, lazy=False): """Install a _() function using the given translation domain. @@ -77,180 +178,159 @@ def install(domain, lazy=False): any available locale. """ if lazy: - # NOTE(mrodden): Lazy gettext functionality. - # - # The following introduces a deferred way to do translations on - # messages in OpenStack. We override the standard _() function - # and % (format string) operation to build Message objects that can - # later be translated when we have more information. - # - # Also included below is an example LocaleHandler that translates - # Messages to an associated locale, effectively allowing many logs, - # each with their own locale. - - def _lazy_gettext(msg): - """Create and return a Message object. - - Lazy gettext function for a given domain, it is a factory method - for a project/module to get a lazy gettext function for its own - translation domain (i.e. nova, glance, cinder, etc.) - - Message encapsulates a string so that we can translate - it later when needed. - """ - return Message(msg, domain) - - import __builtin__ - __builtin__.__dict__['_'] = _lazy_gettext + from six import moves + tf = TranslatorFactory(domain, lazy=True) + moves.builtins.__dict__['_'] = tf.primary else: localedir = '%s_LOCALEDIR' % domain.upper() - gettext.install(domain, - localedir=os.environ.get(localedir), - unicode=True) - - -class Message(UserString.UserString, object): - """Class used to encapsulate translatable messages.""" - def __init__(self, msg, domain): - # _msg is the gettext msgid and should never change - self._msg = msg - self._left_extra_msg = '' - self._right_extra_msg = '' - self.params = None - self.locale = None - self.domain = domain - - @property - def data(self): - # NOTE(mrodden): this should always resolve to a unicode string - # that best represents the state of the message currently - - localedir = os.environ.get(self.domain.upper() + '_LOCALEDIR') - if self.locale: - lang = gettext.translation(self.domain, - localedir=localedir, - languages=[self.locale], - fallback=True) - else: - # use system locale for translations - lang = gettext.translation(self.domain, - localedir=localedir, - fallback=True) - - full_msg = (self._left_extra_msg + - lang.ugettext(self._msg) + - self._right_extra_msg) - - if self.params is not None: - full_msg = full_msg % self.params - - return six.text_type(full_msg) - - def _save_dictionary_parameter(self, dict_param): - full_msg = self.data - # look for %(blah) fields in string; - # ignore %% and deal with the - # case where % is first character on the line - keys = re.findall('(?:[^%]|^)?%\((\w*)\)[a-z]', full_msg) - - # if we don't find any %(blah) blocks but have a %s - if not keys and re.findall('(?:[^%]|^)%[a-z]', full_msg): - # apparently the full dictionary is the parameter - params = copy.deepcopy(dict_param) + if six.PY3: + gettext.install(domain, + localedir=os.environ.get(localedir)) else: - params = {} - for key in keys: - try: - params[key] = copy.deepcopy(dict_param[key]) - except TypeError: - # cast uncopyable thing to unicode string - params[key] = unicode(dict_param[key]) + gettext.install(domain, + localedir=os.environ.get(localedir), + unicode=True) - return params - def _save_parameters(self, other): - # we check for None later to see if - # we actually have parameters to inject, - # so encapsulate if our parameter is actually None - if other is None: - self.params = (other, ) - elif isinstance(other, dict): - self.params = self._save_dictionary_parameter(other) - else: - # fallback to casting to unicode, - # this will handle the problematic python code-like - # objects that cannot be deep-copied - try: - self.params = copy.deepcopy(other) - except TypeError: - self.params = unicode(other) +class Message(six.text_type): + """A Message object is a unicode object that can be translated. - return self + Translation of Message is done explicitly using the translate() method. + For all non-translation intents and purposes, a Message is simply unicode, + and can be treated as such. + """ - # overrides to be more string-like - def __unicode__(self): - return self.data + def __new__(cls, msgid, msgtext=None, params=None, + domain='neutronclient', *args): + """Create a new Message object. - def __str__(self): - return self.data.encode('utf-8') + In order for translation to work gettext requires a message ID, this + msgid will be used as the base unicode text. It is also possible + for the msgid and the base unicode text to be different by passing + the msgtext parameter. + """ + # If the base msgtext is not given, we use the default translation + # of the msgid (which is in English) just in case the system locale is + # not English, so that the base text will be in that locale by default. + if not msgtext: + msgtext = Message._translate_msgid(msgid, domain) + # We want to initialize the parent unicode with the actual object that + # would have been plain unicode if 'Message' was not enabled. + msg = super(Message, cls).__new__(cls, msgtext) + msg.msgid = msgid + msg.domain = domain + msg.params = params + return msg + + def translate(self, desired_locale=None): + """Translate this message to the desired locale. + + :param desired_locale: The desired locale to translate the message to, + if no locale is provided the message will be + translated to the system's default locale. + + :returns: the translated message in unicode + """ - def __getstate__(self): - to_copy = ['_msg', '_right_extra_msg', '_left_extra_msg', - 'domain', 'params', 'locale'] - new_dict = self.__dict__.fromkeys(to_copy) - for attr in to_copy: - new_dict[attr] = copy.deepcopy(self.__dict__[attr]) + translated_message = Message._translate_msgid(self.msgid, + self.domain, + desired_locale) + if self.params is None: + # No need for more translation + return translated_message + + # This Message object may have been formatted with one or more + # Message objects as substitution arguments, given either as a single + # argument, part of a tuple, or as one or more values in a dictionary. + # When translating this Message we need to translate those Messages too + translated_params = _translate_args(self.params, desired_locale) + + translated_message = translated_message % translated_params + + return translated_message + + @staticmethod + def _translate_msgid(msgid, domain, desired_locale=None): + if not desired_locale: + system_locale = locale.getdefaultlocale() + # If the system locale is not available to the runtime use English + if not system_locale[0]: + desired_locale = 'en_US' + else: + desired_locale = system_locale[0] + + locale_dir = os.environ.get(domain.upper() + '_LOCALEDIR') + lang = gettext.translation(domain, + localedir=locale_dir, + languages=[desired_locale], + fallback=True) + if six.PY3: + translator = lang.gettext + else: + translator = lang.ugettext - return new_dict + translated_message = translator(msgid) + return translated_message - def __setstate__(self, state): - for (k, v) in state.items(): - setattr(self, k, v) + def __mod__(self, other): + # When we mod a Message we want the actual operation to be performed + # by the parent class (i.e. unicode()), the only thing we do here is + # save the original msgid and the parameters in case of a translation + params = self._sanitize_mod_params(other) + unicode_mod = super(Message, self).__mod__(params) + modded = Message(self.msgid, + msgtext=unicode_mod, + params=params, + domain=self.domain) + return modded + + def _sanitize_mod_params(self, other): + """Sanitize the object being modded with this Message. + + - Add support for modding 'None' so translation supports it + - Trim the modded object, which can be a large dictionary, to only + those keys that would actually be used in a translation + - Snapshot the object being modded, in case the message is + translated, it will be used as it was when the Message was created + """ + if other is None: + params = (other,) + elif isinstance(other, dict): + # Merge the dictionaries + # Copy each item in case one does not support deep copy. + params = {} + if isinstance(self.params, dict): + for key, val in self.params.items(): + params[key] = self._copy_param(val) + for key, val in other.items(): + params[key] = self._copy_param(val) + else: + params = self._copy_param(other) + return params + + def _copy_param(self, param): + try: + return copy.deepcopy(param) + except Exception: + # Fallback to casting to unicode this will handle the + # python code-like objects that can't be deep-copied + return six.text_type(param) - # operator overloads def __add__(self, other): - copied = copy.deepcopy(self) - copied._right_extra_msg += other.__str__() - return copied + msg = _('Message objects do not support addition.') + raise TypeError(msg) def __radd__(self, other): - copied = copy.deepcopy(self) - copied._left_extra_msg += other.__str__() - return copied + return self.__add__(other) - def __mod__(self, other): - # do a format string to catch and raise - # any possible KeyErrors from missing parameters - self.data % other - copied = copy.deepcopy(self) - return copied._save_parameters(other) - - def __mul__(self, other): - return self.data * other - - def __rmul__(self, other): - return other * self.data - - def __getitem__(self, key): - return self.data[key] - - def __getslice__(self, start, end): - return self.data.__getslice__(start, end) - - def __getattribute__(self, name): - # NOTE(mrodden): handle lossy operations that we can't deal with yet - # These override the UserString implementation, since UserString - # uses our __class__ attribute to try and build a new message - # after running the inner data string through the operation. - # At that point, we have lost the gettext message id and can just - # safely resolve to a string instead. - ops = ['capitalize', 'center', 'decode', 'encode', - 'expandtabs', 'ljust', 'lstrip', 'replace', 'rjust', 'rstrip', - 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill'] - if name in ops: - return getattr(self.data, name) - else: - return UserString.UserString.__getattribute__(self, name) + if six.PY2: + def __str__(self): + # NOTE(luisg): Logging in python 2.6 tries to str() log records, + # and it expects specifically a UnicodeError in order to proceed. + msg = _('Message objects do not support str() because they may ' + 'contain non-ascii characters. ' + 'Please use unicode() or translate() instead.') + raise UnicodeError(msg) def get_available_languages(domain): @@ -272,49 +352,147 @@ def get_available_languages(domain): # NOTE(luisg): Babel <1.0 used a function called list(), which was # renamed to locale_identifiers() in >=1.0, the requirements master list # requires >=0.9.6, uncapped, so defensively work with both. We can remove - # this check when the master list updates to >=1.0, and all projects udpate + # this check when the master list updates to >=1.0, and update all projects list_identifiers = (getattr(localedata, 'list', None) or getattr(localedata, 'locale_identifiers')) locale_identifiers = list_identifiers() + for i in locale_identifiers: if find(i) is not None: language_list.append(i) + + # NOTE(luisg): Babel>=1.0,<1.3 has a bug where some OpenStack supported + # locales (e.g. 'zh_CN', and 'zh_TW') aren't supported even though they + # are perfectly legitimate locales: + # https://github.com/mitsuhiko/babel/issues/37 + # In Babel 1.3 they fixed the bug and they support these locales, but + # they are still not explicitly "listed" by locale_identifiers(). + # That is why we add the locales here explicitly if necessary so that + # they are listed as supported. + aliases = {'zh': 'zh_CN', + 'zh_Hant_HK': 'zh_HK', + 'zh_Hant': 'zh_TW', + 'fil': 'tl_PH'} + for (locale, alias) in six.iteritems(aliases): + if locale in language_list and alias not in language_list: + language_list.append(alias) + _AVAILABLE_LANGUAGES[domain] = language_list return copy.copy(language_list) -def get_localized_message(message, user_locale): - """Gets a localized version of the given message in the given locale.""" +def translate(obj, desired_locale=None): + """Gets the translated unicode representation of the given object. + + If the object is not translatable it is returned as-is. + If the locale is None the object is translated to the system locale. + + :param obj: the object to translate + :param desired_locale: the locale to translate the message to, if None the + default system locale will be used + :returns: the translated object in unicode, or the original object if + it could not be translated + """ + message = obj + if not isinstance(message, Message): + # If the object to translate is not already translatable, + # let's first get its unicode representation + message = six.text_type(obj) if isinstance(message, Message): - if user_locale: - message.locale = user_locale - return unicode(message) - else: - return message + # Even after unicoding() we still need to check if we are + # running with translatable unicode before translating + return message.translate(desired_locale) + return obj + +def _translate_args(args, desired_locale=None): + """Translates all the translatable elements of the given arguments object. -class LocaleHandler(logging.Handler): - """Handler that can have a locale associated to translate Messages. + This method is used for translating the translatable values in method + arguments which include values of tuples or dictionaries. + If the object is not a tuple or a dictionary the object itself is + translated if it is translatable. - A quick example of how to utilize the Message class above. - LocaleHandler takes a locale and a target logging.Handler object - to forward LogRecord objects to after translating the internal Message. + If the locale is None the object is translated to the system locale. + + :param args: the args to translate + :param desired_locale: the locale to translate the args to, if None the + default system locale will be used + :returns: a new args object with the translated contents of the original """ + if isinstance(args, tuple): + return tuple(translate(v, desired_locale) for v in args) + if isinstance(args, dict): + translated_dict = {} + for (k, v) in six.iteritems(args): + translated_v = translate(v, desired_locale) + translated_dict[k] = translated_v + return translated_dict + return translate(args, desired_locale) + - def __init__(self, locale, target): - """Initialize a LocaleHandler +class TranslationHandler(handlers.MemoryHandler): + """Handler that translates records before logging them. + + The TranslationHandler takes a locale and a target logging.Handler object + to forward LogRecord objects to after translating them. This handler + depends on Message objects being logged, instead of regular strings. + + The handler can be configured declaratively in the logging.conf as follows: + + [handlers] + keys = translatedlog, translator + + [handler_translatedlog] + class = handlers.WatchedFileHandler + args = ('/var/log/api-localized.log',) + formatter = context + + [handler_translator] + class = openstack.common.log.TranslationHandler + target = translatedlog + args = ('zh_CN',) + + If the specified locale is not available in the system, the handler will + log in the default locale. + """ + + def __init__(self, locale=None, target=None): + """Initialize a TranslationHandler :param locale: locale to use for translating messages :param target: logging.Handler object to forward LogRecord objects to after translation """ - logging.Handler.__init__(self) + # NOTE(luisg): In order to allow this handler to be a wrapper for + # other handlers, such as a FileHandler, and still be able to + # configure it using logging.conf, this handler has to extend + # MemoryHandler because only the MemoryHandlers' logging.conf + # parsing is implemented such that it accepts a target handler. + handlers.MemoryHandler.__init__(self, capacity=0, target=target) self.locale = locale - self.target = target + + def setFormatter(self, fmt): + self.target.setFormatter(fmt) def emit(self, record): - if isinstance(record.msg, Message): - # set the locale and resolve to a string - record.msg.locale = self.locale + # We save the message from the original record to restore it + # after translation, so other handlers are not affected by this + original_msg = record.msg + original_args = record.args + + try: + self._translate_and_log_record(record) + finally: + record.msg = original_msg + record.args = original_args + + def _translate_and_log_record(self, record): + record.msg = translate(record.msg, self.locale) + + # In addition to translating the message, we also need to translate + # arguments that were passed to the log method that were not part + # of the main message e.g., log.info(_('Some message %s'), this_one)) + record.args = _translate_args(record.args, self.locale) self.target.emit(record) diff --git a/neutronclient/openstack/common/importutils.py b/neutronclient/openstack/common/importutils.py index 7ab07b88e..0ab88e19d 100644 --- a/neutronclient/openstack/common/importutils.py +++ b/neutronclient/openstack/common/importutils.py @@ -19,17 +19,16 @@ import sys import traceback -from neutronclient.openstack.common.gettextutils import _ def import_class(import_str): """Returns a class from a string including module and class.""" mod_str, _sep, class_str = import_str.rpartition('.') + __import__(mod_str) try: - __import__(mod_str) return getattr(sys.modules[mod_str], class_str) - except (ValueError, AttributeError): - raise ImportError(_('Class %s cannot be found (%s)') % + except AttributeError: + raise ImportError('Class %s cannot be found (%s)' % (class_str, traceback.format_exception(*sys.exc_info()))) @@ -59,6 +58,13 @@ def import_module(import_str): return sys.modules[import_str] +def import_versioned_module(version, submodule=None): + module = 'neutronclient.v%s' % version + if submodule: + module = '.'.join((module, submodule)) + return import_module(module) + + def try_import(import_str, default=None): """Try to import a module and if it fails return default.""" try: diff --git a/neutronclient/openstack/common/jsonutils.py b/neutronclient/openstack/common/jsonutils.py index b1a617bef..d53b1e99d 100644 --- a/neutronclient/openstack/common/jsonutils.py +++ b/neutronclient/openstack/common/jsonutils.py @@ -168,12 +168,12 @@ def dumps(value, default=to_primitive, **kwargs): return json.dumps(value, default=default, **kwargs) -def loads(s, encoding='utf-8'): - return json.loads(strutils.safe_decode(s, encoding)) +def loads(s, encoding='utf-8', **kwargs): + return json.loads(strutils.safe_decode(s, encoding), **kwargs) -def load(fp, encoding='utf-8'): - return json.load(codecs.getreader(encoding)(fp)) +def load(fp, encoding='utf-8', **kwargs): + return json.load(codecs.getreader(encoding)(fp), **kwargs) try: diff --git a/neutronclient/openstack/common/strutils.py b/neutronclient/openstack/common/strutils.py index b2e4dcb35..730373665 100644 --- a/neutronclient/openstack/common/strutils.py +++ b/neutronclient/openstack/common/strutils.py @@ -17,25 +17,31 @@ System-level utilities and helper functions. """ +import math import re import sys import unicodedata import six -from neutronclient.openstack.common.gettextutils import _ # noqa +from neutronclient.openstack.common.gettextutils import _ -# Used for looking up extensions of text -# to their 'multiplied' byte amount -BYTE_MULTIPLIERS = { - '': 1, - 't': 1024 ** 4, - 'g': 1024 ** 3, - 'm': 1024 ** 2, - 'k': 1024, +UNIT_PREFIX_EXPONENT = { + 'k': 1, + 'K': 1, + 'Ki': 1, + 'M': 2, + 'Mi': 2, + 'G': 3, + 'Gi': 3, + 'T': 4, + 'Ti': 4, +} +UNIT_SYSTEM_INFO = { + 'IEC': (1024, re.compile(r'(^[-+]?\d*\.?\d+)([KMGT]i?)?(b|bit|B)$')), + 'SI': (1000, re.compile(r'(^[-+]?\d*\.?\d+)([kMGT])?(b|bit|B)$')), } -BYTE_REGEX = re.compile(r'(^-?\d+)(\D*)') TRUE_STRINGS = ('1', 't', 'true', 'on', 'y', 'yes') FALSE_STRINGS = ('0', 'f', 'false', 'off', 'n', 'no') @@ -58,12 +64,12 @@ def int_from_bool_as_string(subject): return bool_from_string(subject) and 1 or 0 -def bool_from_string(subject, strict=False): +def bool_from_string(subject, strict=False, default=False): """Interpret a string as a boolean. A case-insensitive match is performed such that strings matching 't', 'true', 'on', 'y', 'yes', or '1' are considered True and, when - `strict=False`, anything else is considered False. + `strict=False`, anything else returns the value specified by 'default'. Useful for JSON-decoded stuff and config file parsing. @@ -72,7 +78,7 @@ def bool_from_string(subject, strict=False): Strings yielding False are 'f', 'false', 'off', 'n', 'no', or '0'. """ if not isinstance(subject, six.string_types): - subject = str(subject) + subject = six.text_type(subject) lowered = subject.strip().lower() @@ -88,20 +94,21 @@ def bool_from_string(subject, strict=False): 'acceptable': acceptable} raise ValueError(msg) else: - return False + return default def safe_decode(text, incoming=None, errors='strict'): - """Decodes incoming str using `incoming` if they're not already unicode. + """Decodes incoming text/bytes string using `incoming` if they're not + already unicode. :param incoming: Text's current encoding :param errors: Errors handling policy. See here for valid values http://docs.python.org/2/library/codecs.html :returns: text or a unicode `incoming` encoded representation of it. - :raises TypeError: If text is not an isntance of str + :raises TypeError: If text is not an instance of str """ - if not isinstance(text, six.string_types): + if not isinstance(text, (six.string_types, six.binary_type)): raise TypeError("%s can't be decoded" % type(text)) if isinstance(text, six.text_type): @@ -131,7 +138,7 @@ def safe_decode(text, incoming=None, errors='strict'): def safe_encode(text, incoming=None, encoding='utf-8', errors='strict'): - """Encodes incoming str/unicode using `encoding`. + """Encodes incoming text/bytes string using `encoding`. If incoming is not specified, text is expected to be encoded with current python's default encoding. (`sys.getdefaultencoding`) @@ -142,10 +149,10 @@ def safe_encode(text, incoming=None, values http://docs.python.org/2/library/codecs.html :returns: text or a bytestring `encoding` encoded representation of it. - :raises TypeError: If text is not an isntance of str + :raises TypeError: If text is not an instance of str """ - if not isinstance(text, six.string_types): - raise TypeError(_("%s can't be encoded") % type(text).capitalize()) + if not isinstance(text, (six.string_types, six.binary_type)): + raise TypeError("%s can't be encoded" % type(text)) if not incoming: incoming = (sys.stdin.encoding or @@ -157,38 +164,54 @@ def safe_encode(text, incoming=None, # Decode text before encoding it with `encoding` text = safe_decode(text, incoming, errors) return text.encode(encoding, errors) + else: + return text + - return text +def string_to_bytes(text, unit_system='IEC', return_int=False): + """Converts a string into an float representation of bytes. + The units supported for IEC :: -def to_bytes(text, default=0): - """Converts a string into an integer of bytes. + Kb(it), Kib(it), Mb(it), Mib(it), Gb(it), Gib(it), Tb(it), Tib(it) + KB, KiB, MB, MiB, GB, GiB, TB, TiB - Looks at the last characters of the text to determine - what conversion is needed to turn the input text into a byte number. - Supports "B, K(B), M(B), G(B), and T(B)". (case insensitive) + The units supported for SI :: + + kb(it), Mb(it), Gb(it), Tb(it) + kB, MB, GB, TB + + Note that the SI unit system does not support capital letter 'K' :param text: String input for bytes size conversion. - :param default: Default return value when text is blank. + :param unit_system: Unit system for byte size conversion. + :param return_int: If True, returns integer representation of text + in bytes. (default: decimal) + :returns: Numerical representation of text in bytes. + :raises ValueError: If text has an invalid value. """ - match = BYTE_REGEX.search(text) + try: + base, reg_ex = UNIT_SYSTEM_INFO[unit_system] + except KeyError: + msg = _('Invalid unit system: "%s"') % unit_system + raise ValueError(msg) + match = reg_ex.match(text) if match: - magnitude = int(match.group(1)) - mult_key_org = match.group(2) - if not mult_key_org: - return magnitude - elif text: + magnitude = float(match.group(1)) + unit_prefix = match.group(2) + if match.group(3) in ['b', 'bit']: + magnitude /= 8 + else: msg = _('Invalid string format: %s') % text - raise TypeError(msg) + raise ValueError(msg) + if not unit_prefix: + res = magnitude else: - return default - mult_key = mult_key_org.lower().replace('b', '', 1) - multiplier = BYTE_MULTIPLIERS.get(mult_key) - if multiplier is None: - msg = _('Unknown byte multiplier: %s') % mult_key_org - raise TypeError(msg) - return magnitude * multiplier + res = magnitude * pow(base, UNIT_PREFIX_EXPONENT[unit_prefix]) + if return_int: + return int(math.ceil(res)) + return res def to_slug(value, incoming=None, errors="strict"): diff --git a/neutronclient/openstack/common/timeutils.py b/neutronclient/openstack/common/timeutils.py index 14fc4fbd8..386534ba3 100644 --- a/neutronclient/openstack/common/timeutils.py +++ b/neutronclient/openstack/common/timeutils.py @@ -19,6 +19,7 @@ import calendar import datetime +import time import iso8601 import six @@ -47,9 +48,9 @@ def parse_isotime(timestr): try: return iso8601.parse_date(timestr) except iso8601.ParseError as e: - raise ValueError(unicode(e)) + raise ValueError(six.text_type(e)) except TypeError as e: - raise ValueError(unicode(e)) + raise ValueError(six.text_type(e)) def strtime(at=None, fmt=PERFECT_TIME_FORMAT): @@ -76,6 +77,9 @@ def is_older_than(before, seconds): """Return True if before is older than seconds.""" if isinstance(before, six.string_types): before = parse_strtime(before).replace(tzinfo=None) + else: + before = before.replace(tzinfo=None) + return utcnow() - before > datetime.timedelta(seconds=seconds) @@ -83,11 +87,19 @@ def is_newer_than(after, seconds): """Return True if after is newer than seconds.""" if isinstance(after, six.string_types): after = parse_strtime(after).replace(tzinfo=None) + else: + after = after.replace(tzinfo=None) + return after - utcnow() > datetime.timedelta(seconds=seconds) def utcnow_ts(): """Timestamp version of our utcnow function.""" + if utcnow.override_time is None: + # NOTE(kgriffs): This is several times faster + # than going through calendar.timegm(...) + return int(time.time()) + return calendar.timegm(utcnow().timetuple()) @@ -102,19 +114,22 @@ def utcnow(): def iso8601_from_timestamp(timestamp): - """Returns a iso8601 formated date from timestamp.""" + """Returns an iso8601 formatted date from timestamp.""" return isotime(datetime.datetime.utcfromtimestamp(timestamp)) utcnow.override_time = None -def set_time_override(override_time=datetime.datetime.utcnow()): +def set_time_override(override_time=None): """Overrides utils.utcnow. Make it return a constant time or a list thereof, one at a time. + + :param override_time: datetime instance or list thereof. If not + given, defaults to the current UTC time. """ - utcnow.override_time = override_time + utcnow.override_time = override_time or datetime.datetime.utcnow() def advance_time_delta(timedelta): @@ -167,6 +182,15 @@ def delta_seconds(before, after): datetime objects (as a float, to microsecond resolution). """ delta = after - before + return total_seconds(delta) + + +def total_seconds(delta): + """Return the total seconds of datetime.timedelta object. + + Compute total seconds of datetime.timedelta, datetime.timedelta + doesn't have method total_seconds in Python2.6, calculate it manually. + """ try: return delta.total_seconds() except AttributeError: @@ -177,8 +201,8 @@ def delta_seconds(before, after): def is_soon(dt, window): """Determines if time is going to happen in the next window seconds. - :params dt: the time - :params window: minimum seconds to remain to consider the time not soon + :param dt: the time + :param window: minimum seconds to remain to consider the time not soon :return: True if expiration is within the given duration """ From a84b2bea73d2b1f4310496b5956791c0545ce11b Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Sun, 23 Mar 2014 05:45:35 +0900 Subject: [PATCH 038/845] Suppress outputs in test_cli20_nsx_networkgateway The output is from argparse and we need to capture stderr in setUp(). oslotest.base.BaseTestCase is used as the base class of test_cli20 to setup stdout/stderr capture. Closes-Bug: #1296151 Change-Id: I86c50efcf451242c2c1ec13ac5def19d82c1e172 --- neutronclient/tests/unit/test_cli20.py | 4 ++-- test-requirements.txt | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 7ceb5cc86..40502f2c8 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -20,9 +20,9 @@ import cStringIO import fixtures from mox3 import mox +from oslotest import base import requests import sys -import testtools from neutronclient.common import constants from neutronclient.common import exceptions @@ -156,7 +156,7 @@ def __repr__(self): return str(self.lhs) -class CLITestV20Base(testtools.TestCase): +class CLITestV20Base(base.BaseTestCase): format = 'json' test_id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' diff --git a/test-requirements.txt b/test-requirements.txt index 71c9e9f15..b7b56956f 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -5,6 +5,7 @@ coverage>=3.6 discover fixtures>=0.3.14 mox3>=0.7.0 +oslotest python-subunit>=0.0.18 sphinx>=1.1.2,!=1.2.0,<1.3 testrepository>=0.0.18 From 261588b4ecc54f57523e1289895a9609a7dc5f38 Mon Sep 17 00:00:00 2001 From: Jaume Devesa Date: Mon, 7 Jul 2014 15:45:47 +0000 Subject: [PATCH 039/845] Found a useless comment It seems to be legacy of debug steps done by the developer. Change-Id: I84484619803b3006583b70c7a49039e9c2924ce6 --- neutronclient/neutron/v2_0/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index b67c04823..f11bbb6a6 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -421,7 +421,6 @@ def get_data(self, parsed_args): "create_%s" % self.resource) data = obj_creator(body) self.format_output_data(data) - # {u'network': {u'id': u'e9424a76-6db4-4c93-97b6-ec311cd51f19'}} info = self.resource in data and data[self.resource] or None if info: print(_('Created a new %s:') % self.resource, From d7c5104e99040954cde71e1269c2cfb97e93cd23 Mon Sep 17 00:00:00 2001 From: Kevin Benton Date: Mon, 7 Jul 2014 23:56:14 -0700 Subject: [PATCH 040/845] Pass timeout parameter to requests lib call Pass the timeout paramter set from the httpclient init method to the request library so it has an effect. Closes-Bug: #1338910 Change-Id: Icc818bffe5ca4fb141c976dcea6b44d947b69784 --- neutronclient/client.py | 1 + 1 file changed, 1 insertion(+) diff --git a/neutronclient/client.py b/neutronclient/client.py index 7379edb72..e41511f1d 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -144,6 +144,7 @@ def request(self, url, method, **kwargs): method, url, verify=self.verify_cert, + timeout=self.timeout, **kwargs) return resp, resp.text From 8eeb5789727bd2b5cfe50621bb4dbe45c1b3e247 Mon Sep 17 00:00:00 2001 From: liuqing Date: Tue, 1 Jul 2014 15:33:15 +0800 Subject: [PATCH 041/845] Add CONTRIBUTING.rst There is no CONTRIBUTING.rst file, the patch will add it. Change-Id: Ia54b37845dddcd6d8962f4493d5f9906e5604288 --- CONTRIBUTING.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 CONTRIBUTING.rst diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst new file mode 100644 index 000000000..727569717 --- /dev/null +++ b/CONTRIBUTING.rst @@ -0,0 +1,16 @@ +If you would like to contribute to the development of OpenStack, +you must follow the steps documented at: + + https://wiki.openstack.org/wiki/How_To_Contribute#If_you.27re_a_developer + +Once those steps have been completed, changes to OpenStack +should be submitted for review via the Gerrit tool, following +the workflow documented at: + + https://wiki.openstack.org/GerritWorkflow + +Pull requests submitted through GitHub will be ignored. + +Bugs should be filed on Launchpad, not GitHub: + + https://bugs.launchpad.net/python-neutronclient From 267d1dc63c57145f4c0ec71e92819068714e33b2 Mon Sep 17 00:00:00 2001 From: Swaminathan Vasudevan Date: Thu, 10 Jul 2014 12:20:38 -0700 Subject: [PATCH 042/845] Add CLI Support for DVR This patch modifies the existing python-neutron client to create and list the distributed virutal routers. Based on the user role (admin) will be able to create a distributed virtual router and should be able to see the distributed nature of the router. Partially-implements: blueprint neutron-ovs-dvr DocImpact Change-Id: I96269167525563bdd8a52ff981ee5098f5d68e54 --- neutronclient/neutron/v2_0/router.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index 67a4fe1d1..76f18b140 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -38,7 +38,7 @@ class ListRouter(neutronV20.ListCommand): resource = 'router' log = logging.getLogger(__name__ + '.ListRouter') _formatters = {'external_gateway_info': _format_external_gateway_info, } - list_columns = ['id', 'name', 'external_gateway_info'] + list_columns = ['id', 'name', 'external_gateway_info', 'distributed'] pagination_support = True sorting_support = True @@ -71,7 +71,7 @@ def add_known_arguments(self, parser): help=_('Name of router to create.')) parser.add_argument( 'distributed', action='store_true', - help=_('Create a distributed router (VMware NSX plugin only).')) + help=_('Create a distributed router.')) def args2body(self, parsed_args): body = {'router': { From c8b7734438efdfbaf13f89c3469bc98862ccf171 Mon Sep 17 00:00:00 2001 From: Christian Berendt Date: Fri, 27 Jun 2014 14:38:32 +0200 Subject: [PATCH 043/845] Changed 'json' to 'JSON' Change-Id: Idac5c4850f8bb727e17967532ab0b9b9c8e99068 --- neutronclient/v2_0/client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 60070de91..a102781b2 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -1257,7 +1257,7 @@ def get_status_code(self, response): return response.status_code def serialize(self, data): - """Serializes a dictionary into either XML or json. + """Serializes a dictionary into either XML or JSON. A dictionary with a single key can be passed and it can contain any structure. @@ -1272,7 +1272,7 @@ def serialize(self, data): type(data)) def deserialize(self, data, status_code): - """Deserializes an XML or json string into a dictionary.""" + """Deserializes an XML or JSON string into a dictionary.""" if status_code == 204: return data return serializer.Serializer(self.get_attr_metadata()).deserialize( From 708820e91c0c37d993efd50b5d395c9a07fbc42d Mon Sep 17 00:00:00 2001 From: Andreas Jaeger Date: Fri, 27 Jun 2014 16:18:10 +0200 Subject: [PATCH 044/845] Some edits for help strings Some edits for neutron help strings as noticed by Lana during review of this patch: https://review.openstack.org/#/c/102900/ Change-Id: I4f6cbb3ced85e15c5f4f716aad630090955de31b Co-Authored-By: Lana Brindley --- neutronclient/neutron/v2_0/__init__.py | 4 ++-- .../neutron/v2_0/vpn/ipsec_site_connection.py | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index f11bbb6a6..71e66e7ab 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -91,7 +91,7 @@ def find_resourceid_by_name_or_id(client, resource, name_or_id): def add_show_list_common_argument(parser): parser.add_argument( '-D', '--show-details', - help=_('Show detailed info.'), + help=_('Show detailed information.'), action='store_true', default=False, ) parser.add_argument( @@ -356,7 +356,7 @@ def get_parser(self, prog_name): parser = super(NeutronCommand, self).get_parser(prog_name) parser.add_argument( '--request-format', - help=_('The xml or json request format.'), + help=_('The XML or JSON request format.'), default='json', choices=['json', 'xml', ], ) parser.add_argument( diff --git a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py index ad8c851de..aefdf6a03 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py +++ b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py @@ -34,7 +34,7 @@ def _format_peer_cidrs(ipsec_site_connection): class ListIPsecSiteConnection(neutronv20.ListCommand): - """List IPsecSiteConnections that belong to a given tenant.""" + """List IPsec site connections that belong to a given tenant.""" resource = 'ipsec_site_connection' log = logging.getLogger(__name__ + '.ListIPsecSiteConnection') @@ -47,7 +47,7 @@ class ListIPsecSiteConnection(neutronv20.ListCommand): class ShowIPsecSiteConnection(neutronv20.ShowCommand): - """Show information of a given IPsecSiteConnection.""" + """Show information of a given IPsec site connection.""" resource = 'ipsec_site_connection' log = logging.getLogger(__name__ + '.ShowIPsecSiteConnection') @@ -162,7 +162,7 @@ def args2body(self, parsed_args): class UpdateIPsecSiteConnection(neutronv20.UpdateCommand): - """Update a given IPsecSiteConnection.""" + """Update a given IPsec site connection.""" resource = 'ipsec_site_connection' log = logging.getLogger(__name__ + '.UpdateIPsecSiteConnection') @@ -173,7 +173,7 @@ def add_known_arguments(self, parser): '--dpd', metavar="action=ACTION,interval=INTERVAL,timeout=TIMEOUT", type=utils.str2dict, - help=vpn_utils.dpd_help("IPsec connection")) + help=vpn_utils.dpd_help("IPsec connection.")) def args2body(self, parsed_args): body = {'ipsec_site_connection': { @@ -186,7 +186,7 @@ def args2body(self, parsed_args): class DeleteIPsecSiteConnection(neutronv20.DeleteCommand): - """Delete a given IPsecSiteConnection.""" + """Delete a given IPsec site connection.""" resource = 'ipsec_site_connection' log = logging.getLogger(__name__ + '.DeleteIPsecSiteConnection') From b289695bf08c7be05520ceb5804941f9fe924be2 Mon Sep 17 00:00:00 2001 From: Kevin Benton Date: Sun, 13 Jul 2014 21:11:44 -0700 Subject: [PATCH 045/845] Warn on tiny subnet Log a warning when a user creates a /32 IPv4 subnet since it will be useless. Closes-Bug: #1341040 Change-Id: I6a39919e7ce4a39ba67f1ca79e55708566c086fc --- neutronclient/neutron/v2_0/subnet.py | 5 +++++ neutronclient/tests/unit/test_cli20_subnet.py | 21 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/neutronclient/neutron/v2_0/subnet.py b/neutronclient/neutron/v2_0/subnet.py index b9c637bea..15eec6dcc 100644 --- a/neutronclient/neutron/v2_0/subnet.py +++ b/neutronclient/neutron/v2_0/subnet.py @@ -159,6 +159,11 @@ def add_known_arguments(self, parser): help=_('CIDR of subnet to create.')) def args2body(self, parsed_args): + if parsed_args.ip_version == 4 and parsed_args.cidr.endswith('/32'): + self.log.warning(_("An IPv4 subnet with a /32 CIDR will have " + "only one usable IP address so the device " + "attached to it will not have any IP " + "connectivity.")) _network_id = neutronV20.find_resourceid_by_name_or_id( self.get_client(), 'network', parsed_args.network_id) body = {'subnet': {'cidr': parsed_args.cidr, diff --git a/neutronclient/tests/unit/test_cli20_subnet.py b/neutronclient/tests/unit/test_cli20_subnet.py index ffbfd851a..d28d6f3e5 100644 --- a/neutronclient/tests/unit/test_cli20_subnet.py +++ b/neutronclient/tests/unit/test_cli20_subnet.py @@ -16,6 +16,8 @@ import sys +from mox3 import mox + from neutronclient.common import exceptions from neutronclient.neutron.v2_0 import subnet from neutronclient.tests.unit import test_cli20 @@ -315,6 +317,25 @@ def test_create_subnet_merge_single_single(self): position_names, position_values, tenant_id='tenantid') + def test_create_subnet_max_v4_cidr(self): + """Create subnet: --gateway gateway netid cidr.""" + resource = 'subnet' + cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + netid = 'netid' + cidr = '192.168.0.1/32' + gateway = 'gatewayvalue' + args = ['--gateway', gateway, netid, cidr] + position_names = ['ip_version', 'network_id', 'cidr', 'gateway_ip'] + position_values = [4, netid, cidr, gateway] + self.mox.StubOutWithMock(cmd.log, 'warning') + cmd.log.warning(mox.IgnoreArg()) + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + self.mox.VerifyAll() + self.mox.UnsetStubs() + def test_list_subnets_detail(self): """List subnets: -D.""" resources = "subnets" From bfec80a776a53b554a8f2d023d2974539edef3ce Mon Sep 17 00:00:00 2001 From: Assaf Muller Date: Mon, 21 Jul 2014 16:37:23 +0300 Subject: [PATCH 046/845] Fix CLI support for DVR Partially-implements: blueprint neutron-ovs-dvr Closes-Bug: #1346121 Change-Id: I60675b4f60fe8154af4ab6d8f0e93867cf362a58 --- neutronclient/neutron/v2_0/router.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index 76f18b140..b2d935bdb 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -70,15 +70,15 @@ def add_known_arguments(self, parser): 'name', metavar='NAME', help=_('Name of router to create.')) parser.add_argument( - 'distributed', action='store_true', + '--distributed', + dest='distributed', action='store_true', + default=argparse.SUPPRESS, help=_('Create a distributed router.')) def args2body(self, parsed_args): - body = {'router': { - 'name': parsed_args.name, - 'admin_state_up': parsed_args.admin_state, }, } - if parsed_args.tenant_id: - body['router'].update({'tenant_id': parsed_args.tenant_id}) + body = {self.resource: {'admin_state_up': parsed_args.admin_state}} + neutronV20.update_dict(parsed_args, body[self.resource], + ['name', 'distributed', 'tenant_id']) return body From a2b03db3b6c3dfd7e5c51f22772914d0379c0da3 Mon Sep 17 00:00:00 2001 From: Sudhakar Babu Gariganti Date: Mon, 21 Jul 2014 16:26:42 +0530 Subject: [PATCH 047/845] Fix for CLI message of agent disassociation Modified the CLI output messages Removed network %s to DHCP agent -> Removed network %s from DHCP agent Removed Router %s to L3 agent -> Removed router %s from L3 agent Change-Id: I2df928b5e2edecf1aaf5ee15f95e51a37872239a --- neutronclient/neutron/v2_0/agentscheduler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neutronclient/neutron/v2_0/agentscheduler.py b/neutronclient/neutron/v2_0/agentscheduler.py index 03d245420..cd38c5020 100644 --- a/neutronclient/neutron/v2_0/agentscheduler.py +++ b/neutronclient/neutron/v2_0/agentscheduler.py @@ -76,7 +76,7 @@ def run(self, parsed_args): neutron_client, 'network', parsed_args.network) neutron_client.remove_network_from_dhcp_agent( parsed_args.dhcp_agent, _net_id) - print(_('Removed network %s to DHCP agent') % parsed_args.network, + print(_('Removed network %s from DHCP agent') % parsed_args.network, file=self.app.stdout) @@ -180,7 +180,7 @@ def run(self, parsed_args): neutron_client, 'router', parsed_args.router) neutron_client.remove_router_from_l3_agent( parsed_args.l3_agent, _id) - print(_('Removed Router %s to L3 agent') % parsed_args.router, + print(_('Removed router %s from L3 agent') % parsed_args.router, file=self.app.stdout) From dae498eedbf393ec4fd8eeecb07936b085540b64 Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Thu, 3 Jul 2014 13:10:29 +0200 Subject: [PATCH 048/845] Python 3: use six.iteritems() Change-Id: I9d86dd948bc2241bbb69a04c0024f90b805d7cc8 --- neutronclient/common/utils.py | 4 +++- neutronclient/neutron/v2_0/__init__.py | 11 ++++++----- neutronclient/neutron/v2_0/lb/pool.py | 4 +++- neutronclient/neutron/v2_0/quota.py | 9 +++++---- neutronclient/tests/unit/test_cli20.py | 9 +++++---- .../tests/unit/test_cli20_nsx_networkgateway.py | 3 ++- neutronclient/tests/unit/test_utils.py | 3 ++- 7 files changed, 26 insertions(+), 17 deletions(-) diff --git a/neutronclient/common/utils.py b/neutronclient/common/utils.py index 3fe025a2d..b26166d2b 100644 --- a/neutronclient/common/utils.py +++ b/neutronclient/common/utils.py @@ -23,6 +23,8 @@ import os import sys +import six + from neutronclient.common import _ from neutronclient.common import exceptions from neutronclient.openstack.common import strutils @@ -48,7 +50,7 @@ def to_primitive(value): return o elif isinstance(value, dict): o = {} - for k, v in value.iteritems(): + for k, v in six.iteritems(value): o[k] = to_primitive(v) return o elif isinstance(value, datetime.datetime): diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index f11bbb6a6..565edc69d 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -23,6 +23,7 @@ from cliff.formatters import table from cliff import lister from cliff import show +import six from neutronclient.common import command from neutronclient.common import exceptions @@ -274,7 +275,7 @@ def parse_args_to_dict(values_specs): # populate the parser with arguments _parser = argparse.ArgumentParser(add_help=False) - for opt, optspec in _options.iteritems(): + for opt, optspec in six.iteritems(_options): _parser.add_argument(opt, **optspec) _args = _parser.parse_args(_values_specs) @@ -298,7 +299,7 @@ def _merge_args(qCmd, parsed_args, _extra_values, value_specs): @param values_specs: the unparsed unknown parts """ temp_values = _extra_values.copy() - for key, value in temp_values.iteritems(): + for key, value in six.iteritems(temp_values): if hasattr(parsed_args, key): arg_value = getattr(parsed_args, key) if arg_value is not None and value is not None: @@ -369,7 +370,7 @@ def get_parser(self, prog_name): def format_output_data(self, data): # Modify data to make it more readable if self.resource in data: - for k, v in data[self.resource].iteritems(): + for k, v in six.iteritems(data[self.resource]): if isinstance(v, list): value = '\n'.join(utils.dumps( i, indent=self.json_indent) if isinstance(i, dict) @@ -427,7 +428,7 @@ def get_data(self, parsed_args): file=self.app.stdout) else: info = {'': ''} - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) class UpdateCommand(NeutronCommand): @@ -659,6 +660,6 @@ def get_data(self, parsed_args): self.format_output_data(data) resource = data[self.resource] if self.resource in data: - return zip(*sorted(resource.iteritems())) + return zip(*sorted(six.iteritems(resource))) else: return None diff --git a/neutronclient/neutron/v2_0/lb/pool.py b/neutronclient/neutron/v2_0/lb/pool.py index 5de916bfc..06724342d 100644 --- a/neutronclient/neutron/v2_0/lb/pool.py +++ b/neutronclient/neutron/v2_0/lb/pool.py @@ -18,6 +18,8 @@ import logging +import six + from neutronclient.neutron import v2_0 as neutronV20 from neutronclient.openstack.common.gettextutils import _ @@ -132,6 +134,6 @@ def get_data(self, parsed_args): self.format_output_data(data) stats = data['stats'] if 'stats' in data: - return zip(*sorted(stats.iteritems())) + return zip(*sorted(six.iteritems(stats))) else: return None diff --git a/neutronclient/neutron/v2_0/quota.py b/neutronclient/neutron/v2_0/quota.py index e13a26b3f..2ee462b96 100644 --- a/neutronclient/neutron/v2_0/quota.py +++ b/neutronclient/neutron/v2_0/quota.py @@ -21,6 +21,7 @@ from cliff import lister from cliff import show +import six from neutronclient.common import exceptions from neutronclient.common import utils @@ -124,7 +125,7 @@ def get_data(self, parsed_args): "show_%s" % self.resource) data = obj_shower(tenant_id, **params) if self.resource in data: - for k, v in data[self.resource].iteritems(): + for k, v in six.iteritems(data[self.resource]): if isinstance(v, list): value = "" for _item in v: @@ -137,7 +138,7 @@ def get_data(self, parsed_args): data[self.resource][k] = value elif v is None: data[self.resource][k] = '' - return zip(*sorted(data[self.resource].iteritems())) + return zip(*sorted(six.iteritems(data[self.resource]))) else: return None @@ -230,7 +231,7 @@ def get_data(self, parsed_args): neutron_client) data = obj_updator(tenant_id, body) if self.resource in data: - for k, v in data[self.resource].iteritems(): + for k, v in six.iteritems(data[self.resource]): if isinstance(v, list): value = "" for _item in v: @@ -243,6 +244,6 @@ def get_data(self, parsed_args): data[self.resource][k] = value elif v is None: data[self.resource][k] = '' - return zip(*sorted(data[self.resource].iteritems())) + return zip(*sorted(six.iteritems(data[self.resource]))) else: return None diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 40502f2c8..822475254 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -14,15 +14,16 @@ # under the License. # -import urllib - import contextlib import cStringIO +import sys +import urllib + import fixtures from mox3 import mox from oslotest import base import requests -import sys +import six from neutronclient.common import constants from neutronclient.common import exceptions @@ -112,7 +113,7 @@ def __init__(self, lhs, client): def _com_dict(self, lhs, rhs): if len(lhs) != len(rhs): return False - for key, value in lhs.iteritems(): + for key, value in six.iteritems(lhs): if key not in rhs: return False rhs_value = rhs[key] diff --git a/neutronclient/tests/unit/test_cli20_nsx_networkgateway.py b/neutronclient/tests/unit/test_cli20_nsx_networkgateway.py index 8a52a281c..0d77d4ddf 100644 --- a/neutronclient/tests/unit/test_cli20_nsx_networkgateway.py +++ b/neutronclient/tests/unit/test_cli20_nsx_networkgateway.py @@ -17,6 +17,7 @@ import sys from mox3 import mox +import six from neutronclient.neutron.v2_0.nsx import networkgateway as nwgw from neutronclient.tests.unit import test_cli20 @@ -131,7 +132,7 @@ def _test_create_gateway_device(self, position_names = ['name', ] position_values = [name, ] args = [] - for (k, v) in extra_body.iteritems(): + for (k, v) in six.iteritems(extra_body): if (k == 'client_certificate' and client_certificate_file): v = client_certificate_file k = 'client_certificate_file' diff --git a/neutronclient/tests/unit/test_utils.py b/neutronclient/tests/unit/test_utils.py index 4ccce648a..44c08707f 100644 --- a/neutronclient/tests/unit/test_utils.py +++ b/neutronclient/tests/unit/test_utils.py @@ -16,6 +16,7 @@ import datetime import sys +import six import testtools from neutronclient.common import exceptions @@ -162,7 +163,7 @@ def test_iteritems(self): class IterItemsClass(object): def iteritems(self): - return d.iteritems() + return six.iteritems(d) x = IterItemsClass() p = utils.to_primitive(x) From 4927f74a8e4039d837759cf31f131f86516a035d Mon Sep 17 00:00:00 2001 From: Xuhan Peng Date: Mon, 24 Feb 2014 08:46:21 -0500 Subject: [PATCH 049/845] Create new IPv6 attributes for Subnets by client This is the client implementation of bp ipv6-two-attributes. Add two optional arguments: --ipv6-ra-mode --ipv6-address-mode Partially implements bp ipv6-two-attributes Change-Id: Ia922a9b6bb4e1dbd372174d48f29f5411c7b5674 --- neutronclient/neutron/v2_0/subnet.py | 19 ++++ neutronclient/tests/unit/test_cli20_subnet.py | 93 +++++++++++++++++++ 2 files changed, 112 insertions(+) diff --git a/neutronclient/neutron/v2_0/subnet.py b/neutronclient/neutron/v2_0/subnet.py index 15eec6dcc..4de0058e2 100644 --- a/neutronclient/neutron/v2_0/subnet.py +++ b/neutronclient/neutron/v2_0/subnet.py @@ -84,6 +84,14 @@ def add_updatable_arguments(parser): '--enable-dhcp', action='store_true', help=_('Enable DHCP for this subnet.')) + parser.add_argument( + '--ipv6-ra-mode', + choices=['dhcpv6-stateful', 'dhcpv6-stateless', 'slaac'], + help=_('IPv6 RA (Router Advertisement) mode.')) + parser.add_argument( + '--ipv6-address-mode', + choices=['dhcpv6-stateful', 'dhcpv6-stateless', 'slaac'], + help=_('IPv6 address mode.')) def updatable_args2body(parsed_args, body): @@ -111,6 +119,17 @@ def updatable_args2body(parsed_args, body): body['subnet']['host_routes'] = parsed_args.host_routes if parsed_args.dns_nameservers: body['subnet']['dns_nameservers'] = parsed_args.dns_nameservers + if parsed_args.ipv6_ra_mode: + if parsed_args.ip_version == 4: + raise exceptions.CommandError(_("--ipv6-ra-mode is invalid " + "when --ip-version is 4")) + body['subnet']['ipv6_ra_mode'] = parsed_args.ipv6_ra_mode + if parsed_args.ipv6_address_mode: + if parsed_args.ip_version == 4: + raise exceptions.CommandError(_("--ipv6-address-mode is " + "invalid when --ip-version " + "is 4")) + body['subnet']['ipv6_address_mode'] = parsed_args.ipv6_address_mode class ListSubnet(neutronV20.ListCommand): diff --git a/neutronclient/tests/unit/test_cli20_subnet.py b/neutronclient/tests/unit/test_cli20_subnet.py index d28d6f3e5..fb34003b7 100644 --- a/neutronclient/tests/unit/test_cli20_subnet.py +++ b/neutronclient/tests/unit/test_cli20_subnet.py @@ -336,6 +336,99 @@ def test_create_subnet_max_v4_cidr(self): self.mox.VerifyAll() self.mox.UnsetStubs() + def test_create_subnet_with_ipv6_ra_mode(self): + resource = 'subnet' + cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + netid = 'netid' + cidr = 'prefixvalue' + args = ['--tenant_id', 'tenantid', + '--ip-version', '6', + '--ipv6-ra-mode', 'dhcpv6-stateful', + netid, cidr] + position_names = ['ip_version', 'ipv6_ra_mode', + 'network_id', 'cidr'] + position_values = [6, 'dhcpv6-stateful', netid, cidr] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values, + tenant_id='tenantid') + + def test_create_subnet_with_ipv6_address_mode(self): + resource = 'subnet' + cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + netid = 'netid' + cidr = 'prefixvalue' + args = ['--tenant_id', 'tenantid', + '--ip-version', '6', + '--ipv6-address-mode', 'dhcpv6-stateful', + netid, cidr] + position_names = ['ip_version', 'ipv6_address_mode', + 'network_id', 'cidr'] + position_values = [6, 'dhcpv6-stateful', netid, cidr] + + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values, + tenant_id='tenantid') + + def test_create_subnet_with_ipv6_modes(self): + resource = 'subnet' + cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + netid = 'netid' + cidr = 'prefixvalue' + args = ['--tenant_id', 'tenantid', + '--ip-version', '6', + '--ipv6-address-mode', 'slaac', + '--ipv6-ra-mode', 'slaac', + netid, cidr] + position_names = ['ip_version', 'ipv6_address_mode', + 'ipv6_ra_mode', 'network_id', 'cidr'] + position_values = [6, 'slaac', 'slaac', netid, cidr] + + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values, + tenant_id='tenantid') + + def test_create_subnet_with_ipv6_ra_mode_ipv4(self): + resource = 'subnet' + cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + netid = 'netid' + cidr = 'prefixvalue' + args = ['--tenant_id', 'tenantid', + '--ip-version', '4', + '--ipv6-ra-mode', 'slaac', + netid, cidr] + position_names = ['ip_version', 'ipv6_ra_mode', + 'network_id', 'cidr'] + position_values = [4, None, netid, cidr] + self.assertRaises(exceptions.CommandError, self._test_create_resource, + resource, cmd, name, myid, args, position_names, + position_values, tenant_id='tenantid') + + def test_create_subnet_with_ipv6_address_mode_ipv4(self): + resource = 'subnet' + cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + netid = 'netid' + cidr = 'prefixvalue' + args = ['--tenant_id', 'tenantid', + '--ip-version', '4', + '--ipv6-address-mode', 'slaac', + netid, cidr] + position_names = ['ip_version', 'ipv6_address_mode', + 'network_id', 'cidr'] + position_values = [4, None, netid, cidr] + self.assertRaises(exceptions.CommandError, self._test_create_resource, + resource, cmd, name, myid, args, position_names, + position_values, tenant_id='tenantid') + def test_list_subnets_detail(self): """List subnets: -D.""" resources = "subnets" From 5eeba0ca19a683ef546cacd280a0dd1dfdb4995e Mon Sep 17 00:00:00 2001 From: Matt Riedemann Date: Wed, 23 Jul 2014 09:27:46 -0700 Subject: [PATCH 050/845] Add MacAddressInUseClient exception handling Nova needs a specific client exception defined for the server's MacAddressInUse exception when allocating ports so it can handle the exception properly, otherwise the NeutronClientException with a 409 can be confused with other errors. Partial-Bug: #1347778 Change-Id: Ia02dbc8fe32a43adeb229e3b640b9b33ec0dd6c7 --- neutronclient/common/exceptions.py | 4 ++++ neutronclient/tests/unit/test_cli20.py | 1 + 2 files changed, 5 insertions(+) diff --git a/neutronclient/common/exceptions.py b/neutronclient/common/exceptions.py index 357266072..4c21e62b6 100644 --- a/neutronclient/common/exceptions.py +++ b/neutronclient/common/exceptions.py @@ -155,6 +155,10 @@ class IpAddressGenerationFailureClient(Conflict): pass +class MacAddressInUseClient(Conflict): + pass + + class ExternalIpAddressExhaustedClient(BadRequest): pass diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 40502f2c8..daad6a054 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -604,6 +604,7 @@ def test_exception_handler_v20_neutron_known_error(self): ('IpAddressInUse', exceptions.IpAddressInUseClient, 409), ('IpAddressGenerationFailure', exceptions.IpAddressGenerationFailureClient, 409), + ('MacAddressInUse', exceptions.MacAddressInUseClient, 409), ('ExternalIpAddressExhausted', exceptions.ExternalIpAddressExhaustedClient, 400), ('OverQuota', exceptions.OverQuotaClient, 409), From 8da544dd26edd56aab78bea46565b211ba5e5ec2 Mon Sep 17 00:00:00 2001 From: Steve Martinelli Date: Thu, 24 Jul 2014 18:11:21 -0400 Subject: [PATCH 051/845] Add a tox job for generating docs Add a tox job to make it easier for developers to generate their own docs before submitting a patch. Change-Id: Iab7ff03120675ade8a3e6e6f7b7839be1f58d40c --- tox.ini | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tox.ini b/tox.ini index a498e5261..bac7c7607 100644 --- a/tox.ini +++ b/tox.ini @@ -24,6 +24,10 @@ commands = {posargs} [testenv:cover] commands = python setup.py testr --coverage --testr-args='{posargs}' +[testenv:docs] +commands= + python setup.py build_sphinx + [tox:jenkins] downloadcache = ~/cache/pip From c05eb2941fe23363b953665d8827bab19ee9f53b Mon Sep 17 00:00:00 2001 From: Steve Martinelli Date: Thu, 24 Jul 2014 18:15:55 -0400 Subject: [PATCH 052/845] Update theme for docs The current developer docs theme used is out of sync with the other openstack projects. This patch will update the docs to provide a more consistent look and feel when using developer docs Change-Id: I03985039381c2ba4f81a2015258f7b49bba441d9 --- doc/source/conf.py | 4 ++-- test-requirements.txt | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index 91d218643..0001ce604 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -7,7 +7,7 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx'] +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'oslosphinx'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -35,7 +35,7 @@ # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. -html_theme = 'nature' +# html_theme = 'nature' # Output file base name for HTML help builder. htmlhelp_basename = '%sdoc' % project diff --git a/test-requirements.txt b/test-requirements.txt index b7b56956f..ad976c6cb 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -5,6 +5,7 @@ coverage>=3.6 discover fixtures>=0.3.14 mox3>=0.7.0 +oslosphinx oslotest python-subunit>=0.0.18 sphinx>=1.1.2,!=1.2.0,<1.3 From c46bf95f4cac768184dacc494671d58e21562d56 Mon Sep 17 00:00:00 2001 From: Swaminathan Vasudevan Date: Thu, 24 Jul 2014 10:56:28 -0700 Subject: [PATCH 053/845] Revert "Fix CLI support for DVR" This reverts commit bfec80a776a53b554a8f2d023d2974539edef3ce. This patch needs to be reverted until we resolve the issue with patch 109180. The previous change introduced a situation where we can only override the default server side setting from the client side for "--distributed" when the default is False. But we cannot override when the default is True. This is a requirement for DVR and at the same time we don't want to introduce any new changes that would affect any current plugins. So we will decide on it later and in the mean while I will revert it. Partial-Bug: #1346121 Partial-Bug: #1347960 Change-Id: I261f9e232b1f72ed0cb2f0c1ba218facaba9afcb --- neutronclient/neutron/v2_0/router.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index b2d935bdb..76f18b140 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -70,15 +70,15 @@ def add_known_arguments(self, parser): 'name', metavar='NAME', help=_('Name of router to create.')) parser.add_argument( - '--distributed', - dest='distributed', action='store_true', - default=argparse.SUPPRESS, + 'distributed', action='store_true', help=_('Create a distributed router.')) def args2body(self, parsed_args): - body = {self.resource: {'admin_state_up': parsed_args.admin_state}} - neutronV20.update_dict(parsed_args, body[self.resource], - ['name', 'distributed', 'tenant_id']) + body = {'router': { + 'name': parsed_args.name, + 'admin_state_up': parsed_args.admin_state, }, } + if parsed_args.tenant_id: + body['router'].update({'tenant_id': parsed_args.tenant_id}) return body From 84fb8102d9fbb50b2a61cc7cea9f831f6c05f267 Mon Sep 17 00:00:00 2001 From: Ilya Shakhat Date: Thu, 17 Jul 2014 12:30:11 +0400 Subject: [PATCH 054/845] Python 3: compatibility of StringIO() and dict.iterkeys() Change-Id: Ia671cacbf1852984c41b374986cc2e9581247b99 --- neutronclient/common/serializer.py | 5 +++-- neutronclient/neutron/v2_0/__init__.py | 2 +- neutronclient/tests/unit/test_cli20.py | 3 +-- neutronclient/tests/unit/test_shell.py | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/neutronclient/common/serializer.py b/neutronclient/common/serializer.py index 21092b7a8..e2cd7641b 100644 --- a/neutronclient/common/serializer.py +++ b/neutronclient/common/serializer.py @@ -18,10 +18,11 @@ ### import logging - from xml.etree import ElementTree as etree from xml.parsers import expat +import six + from neutronclient.common import constants from neutronclient.common import exceptions as exception from neutronclient.openstack.common.gettextutils import _ @@ -93,7 +94,7 @@ def default(self, data): root_key = constants.VIRTUAL_ROOT_KEY root_value = None else: - link_keys = [k for k in data.iterkeys() or [] + link_keys = [k for k in six.iterkeys(data) or [] if k.endswith('_links')] if link_keys: links = data.pop(link_keys[0], None) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index 565edc69d..ffe094ba4 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -280,7 +280,7 @@ def parse_args_to_dict(values_specs): _args = _parser.parse_args(_values_specs) result_dict = {} - for opt in _options.iterkeys(): + for opt in six.iterkeys(_options): _opt = opt.split('--', 2)[1] _opt = _opt.replace('-', '_') _value = getattr(_args, _opt) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 822475254..a3fc062b9 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -15,7 +15,6 @@ # import contextlib -import cStringIO import sys import urllib @@ -39,7 +38,7 @@ @contextlib.contextmanager def capture_std_streams(): - fake_stdout, fake_stderr = cStringIO.StringIO(), cStringIO.StringIO() + fake_stdout, fake_stderr = six.StringIO(), six.StringIO() stdout, stderr = sys.stdout, sys.stderr try: sys.stdout, sys.stderr = fake_stdout, fake_stderr diff --git a/neutronclient/tests/unit/test_shell.py b/neutronclient/tests/unit/test_shell.py index b0bb82af1..5f4376cf5 100644 --- a/neutronclient/tests/unit/test_shell.py +++ b/neutronclient/tests/unit/test_shell.py @@ -14,7 +14,6 @@ # under the License. import argparse -import cStringIO import logging import os import re @@ -22,6 +21,7 @@ import fixtures from mox3 import mox +import six import testtools from testtools import matchers @@ -61,8 +61,8 @@ def shell(self, argstr, check=False): clean_env = {} _old_env, os.environ = os.environ, clean_env.copy() try: - sys.stdout = cStringIO.StringIO() - sys.stderr = cStringIO.StringIO() + sys.stdout = six.StringIO() + sys.stderr = six.StringIO() _shell = openstack_shell.NeutronShell('2.0') _shell.run(argstr.split()) except SystemExit: From 5db54ed3015605003a3adfb0f3f60f6828b3547e Mon Sep 17 00:00:00 2001 From: Kevin Benton Date: Tue, 8 Jul 2014 01:13:43 -0700 Subject: [PATCH 055/845] Add a new timeout option to set the HTTP Timeout Adds a new --timeout option and check to the env var OS_NETWORK_TIMEOUT to set the HTTP timeout used for the request to the Neutron backend. DocImpact Closes-Bug: #1338932 Change-Id: I0d9687e671f68c4845af2439abfe581c6dcf020c --- neutronclient/common/clientmanager.py | 5 ++++- neutronclient/shell.py | 7 +++++++ neutronclient/tests/unit/test_shell.py | 25 ++++++++++++++++++++++++- neutronclient/tests/unit/test_ssl.py | 2 ++ 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/neutronclient/common/clientmanager.py b/neutronclient/common/clientmanager.py index db6883b86..69d1b0c4a 100644 --- a/neutronclient/common/clientmanager.py +++ b/neutronclient/common/clientmanager.py @@ -64,6 +64,7 @@ def __init__(self, token=None, url=None, ca_cert=None, log_credentials=False, service_type=None, + timeout=None ): self._token = token self._url = url @@ -82,6 +83,7 @@ def __init__(self, token=None, url=None, self._insecure = insecure self._ca_cert = ca_cert self._log_credentials = log_credentials + self._timeout = timeout return def initialize(self): @@ -98,7 +100,8 @@ def initialize(self): endpoint_type=self._endpoint_type, insecure=self._insecure, ca_cert=self._ca_cert, - log_credentials=self._log_credentials) + log_credentials=self._log_credentials, + timeout=self._timeout) httpclient.authenticate() # Populate other password flow attributes self._token = httpclient.auth_token diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 2369c4bb9..c2f7cd1bd 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -445,6 +445,12 @@ def build_option_parser(self, description, version): default=env('OS_NETWORK_SERVICE_TYPE', default='network'), help=_('Defaults to env[OS_NETWORK_SERVICE_TYPE] or network.')) + parser.add_argument( + '--timeout', metavar='', + default=env('OS_NETWORK_TIMEOUT', default=None), type=float, + help=_('Timeout in seconds to wait for an HTTP response. Defaults ' + 'to env[OS_NETWORK_TIMEOUT] or None if not specified.')) + parser.add_argument( '--endpoint-type', metavar='', default=env('OS_ENDPOINT_TYPE', default='publicURL'), @@ -643,6 +649,7 @@ def authenticate_user(self): endpoint_type=self.options.endpoint_type, insecure=self.options.insecure, ca_cert=self.options.os_cacert, + timeout=self.options.timeout, log_credentials=True) return diff --git a/neutronclient/tests/unit/test_shell.py b/neutronclient/tests/unit/test_shell.py index b0bb82af1..ecda0bfa1 100644 --- a/neutronclient/tests/unit/test_shell.py +++ b/neutronclient/tests/unit/test_shell.py @@ -129,7 +129,7 @@ def test_auth(self): password='test', region_name='', api_version={'network': '2.0'}, auth_strategy='keystone', service_type='network', endpoint_type='publicURL', insecure=False, ca_cert=None, - log_credentials=True) + log_credentials=True, timeout=None) neutron_shell.run_subcommand(['quota-list']) self.mox.ReplayAll() cmdline = ('--os-username test ' @@ -185,3 +185,26 @@ def test_endpoint_environment_variable(self): # --endpoint-type and $OS_ENDPOINT_TYPE namespace = parser.parse_args(['--endpoint-type=admin']) self.assertEqual('admin', namespace.endpoint_type) + + def test_timeout_option(self): + shell = openstack_shell.NeutronShell('2.0') + parser = shell.build_option_parser('descr', '2.0') + + # Neither $OS_ENDPOINT_TYPE nor --endpoint-type + namespace = parser.parse_args([]) + self.assertIsNone(namespace.timeout) + + # --endpoint-type but not $OS_ENDPOINT_TYPE + namespace = parser.parse_args(['--timeout=50']) + self.assertEqual(50, namespace.timeout) + + def test_timeout_environment_variable(self): + fixture = fixtures.EnvironmentVariable("OS_NETWORK_TIMEOUT", + "50") + self.useFixture(fixture) + + shell = openstack_shell.NeutronShell('2.0') + parser = shell.build_option_parser('descr', '2.0') + + namespace = parser.parse_args([]) + self.assertEqual(50, namespace.timeout) diff --git a/neutronclient/tests/unit/test_ssl.py b/neutronclient/tests/unit/test_ssl.py index be6e3a2dd..f22f0d35a 100644 --- a/neutronclient/tests/unit/test_ssl.py +++ b/neutronclient/tests/unit/test_ssl.py @@ -63,6 +63,7 @@ def test_ca_cert_passed(self): username=mox.IgnoreArg(), user_id=mox.IgnoreArg(), log_credentials=mox.IgnoreArg(), + timeout=mox.IgnoreArg(), ) openstack_shell.NeutronShell.interact().AndReturn(0) self.mox.ReplayAll() @@ -94,6 +95,7 @@ def test_ca_cert_passed_as_env_var(self): username=mox.IgnoreArg(), user_id=mox.IgnoreArg(), log_credentials=mox.IgnoreArg(), + timeout=mox.IgnoreArg(), ) openstack_shell.NeutronShell.interact().AndReturn(0) self.mox.ReplayAll() From f9dbbb46e21ef524068efd308efb687c006c72c9 Mon Sep 17 00:00:00 2001 From: Fei Long Wang Date: Mon, 30 Jun 2014 19:07:01 +1200 Subject: [PATCH 056/845] Improve the method find_resourceid_by_name_or_id Now the method find_resourceid_by_name_or_id doesn't honoured the project id. So if a user with admin role want to create an instance for another tenant, and using a a security group which name is duplicated in Neutron. Then the boot will fail. This patch is the fix on neutron client part. It will enhance the method find_resourceid_by_name_or_id to support project id as a parameter so as to get the correct security group id when passing a security gruop name. Change-Id: Ibd1829bd0f22f56c4fa210c67d10e1db5556c033 Closes-Bug: 1335733 --- neutronclient/neutron/v2_0/__init__.py | 13 ++++-- neutronclient/tests/unit/test_name_or_id.py | 47 +++++++++++++++++++++ 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index f11bbb6a6..b52523c62 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -61,10 +61,13 @@ def find_resourceid_by_id(client, resource, resource_id): message=not_found_message, status_code=404) -def _find_resourceid_by_name(client, resource, name): +def _find_resourceid_by_name(client, resource, name, project_id=None): resource_plural = _get_resource_plural(resource, client) obj_lister = getattr(client, "list_%s" % resource_plural) - data = obj_lister(name=name, fields='id') + params = {'name': name, 'fields': 'id'} + if project_id: + params['tenant_id'] = project_id + data = obj_lister(**params) collection = resource_plural info = data[collection] if len(info) > 1: @@ -81,11 +84,13 @@ def _find_resourceid_by_name(client, resource, name): return info[0]['id'] -def find_resourceid_by_name_or_id(client, resource, name_or_id): +def find_resourceid_by_name_or_id(client, resource, name_or_id, + project_id=None): try: return find_resourceid_by_id(client, resource, name_or_id) except exceptions.NeutronClientException: - return _find_resourceid_by_name(client, resource, name_or_id) + return _find_resourceid_by_name(client, resource, name_or_id, + project_id) def add_show_list_common_argument(parser): diff --git a/neutronclient/tests/unit/test_name_or_id.py b/neutronclient/tests/unit/test_name_or_id.py index 7ddf80428..9d876b983 100644 --- a/neutronclient/tests/unit/test_name_or_id.py +++ b/neutronclient/tests/unit/test_name_or_id.py @@ -129,3 +129,50 @@ def test_get_id_from_name_notfound(self): except exceptions.NeutronClientException as ex: self.assertIn('Unable to find', ex.message) self.assertEqual(404, ex.status_code) + + def test_get_id_from_name_multiple_with_project(self): + name = 'web_server' + project = str(uuid.uuid4()) + expect_id = str(uuid.uuid4()) + reses = {'security_groups': + [{'id': expect_id, 'tenant_id': project}]} + resstr = self.client.serialize(reses) + self.mox.StubOutWithMock(self.client.httpclient, "request") + path = getattr(self.client, "security_groups_path") + self.client.httpclient.request( + test_cli20.end_url(path, "fields=id&name=%s&tenant_id=%s" % + (name, project)), + 'GET', + body=None, + headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) + ).AndReturn((test_cli20.MyResp(200), resstr)) + self.mox.ReplayAll() + + observed_id = neutronV20.find_resourceid_by_name_or_id( + self.client, 'security_group', name, project) + + self.assertEqual(expect_id, observed_id) + + def test_get_id_from_name_multiple_with_project_not_found(self): + name = 'web_server' + project = str(uuid.uuid4()) + reses = {'security_groups': + [{'id': str(uuid.uuid4()), 'tenant_id': str(uuid.uuid4())}]} + resstr = self.client.serialize(reses) + self.mox.StubOutWithMock(self.client.httpclient, "request") + path = getattr(self.client, "security_groups_path") + self.client.httpclient.request( + test_cli20.end_url(path, "fields=id&name=%s&tenant_id=%s" % + (name, project)), + 'GET', + body=None, + headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) + ).AndReturn((test_cli20.MyResp(200), resstr)) + self.mox.ReplayAll() + + try: + neutronV20.find_resourceid_by_name_or_id( + self.client, 'security_group', name, project) + except exceptions.NeutronClientException as ex: + self.assertIn('Unable to find', ex.message) + self.assertEqual(404, ex.status_code) From 6260c207fb7d089cc56e08f89fcb755c0086c733 Mon Sep 17 00:00:00 2001 From: Koteswara Rao Kelam Date: Tue, 1 Jul 2014 00:21:29 -0700 Subject: [PATCH 057/845] Remove "--disabled" for firewall create rule Allow "--enabled True/False" and remove "--disabled" for consistency across all commands. Even in other neutron commands, we are using only --enabled option but not --disabled. firewall-rule-show displays only "enabled" attribute and there is no "disabled" attribute. Change-Id: Ie591d827660466a774e431dcb40e04e09773e5f7 Closes-bug: 1327056 --- neutronclient/neutron/v2_0/fw/firewallrule.py | 7 +++---- .../tests/unit/fw/test_cli20_firewallrule.py | 12 ++++++------ 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/neutronclient/neutron/v2_0/fw/firewallrule.py b/neutronclient/neutron/v2_0/fw/firewallrule.py index 366fdca03..ef61feb67 100644 --- a/neutronclient/neutron/v2_0/fw/firewallrule.py +++ b/neutronclient/neutron/v2_0/fw/firewallrule.py @@ -100,10 +100,9 @@ def add_known_arguments(self, parser): help=_('Destination port (integer in [1, 65535] or range in ' 'a:b).')) parser.add_argument( - '--disabled', - dest='enabled', - action='store_false', - help=_('To disable this rule.'), + '--enabled', + dest='enabled', choices=['True', 'False'], + help=_('To enable or disable this rule'), default=argparse.SUPPRESS) parser.add_argument( '--protocol', choices=['tcp', 'udp', 'icmp', 'any'], diff --git a/neutronclient/tests/unit/fw/test_cli20_firewallrule.py b/neutronclient/tests/unit/fw/test_cli20_firewallrule.py index 30c8a0fd5..8f6ef9fa9 100644 --- a/neutronclient/tests/unit/fw/test_cli20_firewallrule.py +++ b/neutronclient/tests/unit/fw/test_cli20_firewallrule.py @@ -34,12 +34,11 @@ def _test_create_firewall_rule_with_mandatory_params(self, enabled): my_id = 'myid' protocol = 'tcp' action = 'allow' - enabled_flag = '--enabled' if enabled else '--disabled' args = ['--tenant-id', tenant_id, '--admin-state-up', '--protocol', protocol, '--action', action, - enabled_flag] + '--enabled', enabled] position_names = [] position_values = [] self._test_create_resource(resource, cmd, name, my_id, args, @@ -48,10 +47,10 @@ def _test_create_firewall_rule_with_mandatory_params(self, enabled): enabled=enabled, tenant_id=tenant_id) def test_create_enabled_firewall_rule_with_mandatory_params(self): - self._test_create_firewall_rule_with_mandatory_params(enabled=True) + self._test_create_firewall_rule_with_mandatory_params(enabled='True') def test_create_disabled_firewall_rule_with_mandatory_params(self): - self._test_create_firewall_rule_with_mandatory_params(enabled=False) + self._test_create_firewall_rule_with_mandatory_params(enabled='False') def _setup_create_firewall_rule_with_all_params(self, protocol='tcp'): """firewall-rule-create with all params set.""" @@ -67,6 +66,7 @@ def _setup_create_firewall_rule_with_all_params(self, protocol='tcp'): action = 'allow' tenant_id = 'my-tenant' my_id = 'myid' + enabled = 'True' args = ['--description', description, '--shared', '--protocol', protocol, @@ -75,7 +75,7 @@ def _setup_create_firewall_rule_with_all_params(self, protocol='tcp'): '--source-port', source_port, '--destination-port', destination_port, '--action', action, - '--enabled', + '--enabled', enabled, '--admin-state-up', '--tenant-id', tenant_id] position_names = [] @@ -90,7 +90,7 @@ def _setup_create_firewall_rule_with_all_params(self, protocol='tcp'): destination_ip_address=destination_ip, source_port=source_port, destination_port=destination_port, - action=action, enabled=True, + action=action, enabled='True', tenant_id=tenant_id) def test_create_firewall_rule_with_all_params(self): From b21cafaa219c4a84cb2babd80e284421a761be30 Mon Sep 17 00:00:00 2001 From: Carl Baldwin Date: Wed, 11 Jun 2014 17:35:57 +0000 Subject: [PATCH 058/845] Remove strict checking of encryption type The server may accept a different set of encryption-algorithm dependending on the service plugin that is being used. Without a better mechanism for communicating this to the client, we decided at the Atlanta summit to just remove it. Change-Id: I658fe00281e9cbd018095969d80f5c09aca6cd38 Closes-Bug: #1322659 --- neutronclient/neutron/v2_0/vpn/ikepolicy.py | 5 +---- neutronclient/neutron/v2_0/vpn/ipsecpolicy.py | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/neutronclient/neutron/v2_0/vpn/ikepolicy.py b/neutronclient/neutron/v2_0/vpn/ikepolicy.py index 1191c5232..90d432dd6 100644 --- a/neutronclient/neutron/v2_0/vpn/ikepolicy.py +++ b/neutronclient/neutron/v2_0/vpn/ikepolicy.py @@ -60,10 +60,7 @@ def add_known_arguments(self, parser): 'Default:sha1')) parser.add_argument( '--encryption-algorithm', - default='aes-128', choices=['3des', - 'aes-128', - 'aes-192', - 'aes-256'], + default='aes-128', help=_('Encryption algorithm in lowercase, default:aes-128')) parser.add_argument( '--phase1-negotiation-mode', diff --git a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py index 65d25414b..6ac182a33 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py +++ b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py @@ -62,10 +62,7 @@ def add_known_arguments(self, parser): help=_('Authentication algorithm in lowercase, default:sha1')) parser.add_argument( '--encryption-algorithm', - default='aes-128', choices=['3des', - 'aes-128', - 'aes-192', - 'aes-256'], + default='aes-128', help=_('Encryption algorithm in lowercase, default:aes-128')) parser.add_argument( '--encapsulation-mode', From 65883ba4eb6fe4cd41faaf7557c63ccf802b09cf Mon Sep 17 00:00:00 2001 From: Jakub Libosvar Date: Thu, 24 Apr 2014 16:58:50 +0200 Subject: [PATCH 059/845] Add option for retry number of connection attempts This patch adds a new option (-r | --retries) to specify how many times the client should attempt to connect to the Neutron server when using idempotent methods (GET, PUT and DELETE). The patch also provides more user-friendly message when it's impossible to connect to Neutron server from CLI and ensures that connection-related exceptions are raised to the caller by default when neutronclient is used as a library. DocImpact Closes-Bug: #1312225 Change-Id: Id74d7cf9a0e8c5d2cd3ee4851c883d5286bea19d --- neutronclient/common/clientmanager.py | 7 +++++-- neutronclient/neutron/client.py | 4 +++- neutronclient/shell.py | 20 ++++++++++++++++++++ neutronclient/tests/unit/test_shell.py | 4 ++-- neutronclient/tests/unit/test_ssl.py | 6 ++++++ neutronclient/v2_0/client.py | 19 +++++++++++++++++-- 6 files changed, 53 insertions(+), 7 deletions(-) diff --git a/neutronclient/common/clientmanager.py b/neutronclient/common/clientmanager.py index 69d1b0c4a..c159e05d3 100644 --- a/neutronclient/common/clientmanager.py +++ b/neutronclient/common/clientmanager.py @@ -64,7 +64,9 @@ def __init__(self, token=None, url=None, ca_cert=None, log_credentials=False, service_type=None, - timeout=None + timeout=None, + retries=0, + raise_errors=True ): self._token = token self._url = url @@ -84,7 +86,8 @@ def __init__(self, token=None, url=None, self._ca_cert = ca_cert self._log_credentials = log_credentials self._timeout = timeout - return + self._retries = retries + self._raise_errors = raise_errors def initialize(self): if not self._url: diff --git a/neutronclient/neutron/client.py b/neutronclient/neutron/client.py index f87c8311b..2dd40dea5 100644 --- a/neutronclient/neutron/client.py +++ b/neutronclient/neutron/client.py @@ -46,7 +46,9 @@ def make_client(instance): token=instance._token, auth_strategy=instance._auth_strategy, insecure=instance._insecure, - ca_cert=instance._ca_cert) + ca_cert=instance._ca_cert, + retries=instance._retries, + raise_errors=instance._raise_errors) return client else: raise exceptions.UnsupportedVersion(_("API version %s is not " diff --git a/neutronclient/shell.py b/neutronclient/shell.py index f1f2e2e06..a3e51080b 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -97,6 +97,17 @@ def env(*_vars, **kwargs): return kwargs.get('default', '') +def check_non_negative_int(value): + try: + value = int(value) + except ValueError: + raise argparse.ArgumentTypeError(_("invalid int value: %r") % value) + if value < 0: + raise argparse.ArgumentTypeError(_("input value %d is negative") % + value) + return value + + COMMAND_V2 = { 'net-list': network.ListNetwork, 'net-external-list': network.ListExternalNetwork, @@ -368,6 +379,13 @@ def build_option_parser(self, description, version): nargs=0, default=self, # tricky help=_("Show this help message and exit.")) + parser.add_argument( + '-r', '--retries', + metavar="NUM", + type=check_non_negative_int, + default=0, + help=_("How many times the request to the Neutron server should " + "be retried if it fails.")) # Global arguments parser.add_argument( '--os-auth-strategy', metavar='', @@ -650,6 +668,8 @@ def authenticate_user(self): insecure=self.options.insecure, ca_cert=self.options.os_cacert, timeout=self.options.timeout, + retries=self.options.retries, + raise_errors=False, log_credentials=True) return diff --git a/neutronclient/tests/unit/test_shell.py b/neutronclient/tests/unit/test_shell.py index ecda0bfa1..70acc5961 100644 --- a/neutronclient/tests/unit/test_shell.py +++ b/neutronclient/tests/unit/test_shell.py @@ -128,8 +128,8 @@ def test_auth(self): username='test', user_id='', password='test', region_name='', api_version={'network': '2.0'}, auth_strategy='keystone', service_type='network', - endpoint_type='publicURL', insecure=False, ca_cert=None, - log_credentials=True, timeout=None) + endpoint_type='publicURL', insecure=False, ca_cert=None, retries=0, + raise_errors=False, log_credentials=True, timeout=None) neutron_shell.run_subcommand(['quota-list']) self.mox.ReplayAll() cmdline = ('--os-username test ' diff --git a/neutronclient/tests/unit/test_ssl.py b/neutronclient/tests/unit/test_ssl.py index f22f0d35a..9c0816d45 100644 --- a/neutronclient/tests/unit/test_ssl.py +++ b/neutronclient/tests/unit/test_ssl.py @@ -62,6 +62,8 @@ def test_ca_cert_passed(self): url=mox.IgnoreArg(), username=mox.IgnoreArg(), user_id=mox.IgnoreArg(), + retries=mox.IgnoreArg(), + raise_errors=mox.IgnoreArg(), log_credentials=mox.IgnoreArg(), timeout=mox.IgnoreArg(), ) @@ -94,6 +96,8 @@ def test_ca_cert_passed_as_env_var(self): url=mox.IgnoreArg(), username=mox.IgnoreArg(), user_id=mox.IgnoreArg(), + retries=mox.IgnoreArg(), + raise_errors=mox.IgnoreArg(), log_credentials=mox.IgnoreArg(), timeout=mox.IgnoreArg(), ) @@ -117,6 +121,8 @@ def test_client_manager_properly_creates_httpclient_instance(self): tenant_name=mox.IgnoreArg(), token=mox.IgnoreArg(), username=mox.IgnoreArg(), + retries=mox.IgnoreArg(), + raise_errors=mox.IgnoreArg(), ) self.mox.ReplayAll() diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index a102781b2..630d41341 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -129,6 +129,12 @@ class Client(object): http requests. (optional) :param bool insecure: SSL certificate validation. (optional) :param string ca_cert: SSL CA bundle file to use. (optional) + :param integer retries: How many times idempotent (GET, PUT, DELETE) + requests to Neutron server should be retried if + they fail (default: 0). + :param bool raise_errors: If True then exceptions caused by connection + failure are propagated to the caller. + (default: True) Example:: @@ -1194,7 +1200,8 @@ def __init__(self, **kwargs): self.version = '2.0' self.format = 'json' self.action_prefix = "/v%s" % (self.version) - self.retries = 0 + self.retries = kwargs.get('retries', 0) + self.raise_errors = kwargs.get('raise_errors', True) self.retry_interval = 1 def _handle_fault_response(self, status_code, response_body): @@ -1303,8 +1310,16 @@ def retry_request(self, method, action, body=None, if i < self.retries: _logger.debug('Retrying connection to Neutron service') time.sleep(self.retry_interval) + elif self.raise_errors: + raise - raise exceptions.ConnectionFailed(reason=_("Maximum attempts reached")) + if self.retries: + msg = (_("Failed to connect to Neutron server after %d attempts") + % max_attempts) + else: + msg = _("Failed to connect Neutron server") + + raise exceptions.ConnectionFailed(reason=msg) def delete(self, action, body=None, headers=None, params=None): return self.retry_request("DELETE", action, body=body, From 4164de2bdb48eaacc58a70790aadb21b78cbf723 Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Tue, 26 Nov 2013 16:01:29 +0900 Subject: [PATCH 060/845] setup logger name of NeutronCommand automatically It is a common pattern to add class XXXCommand(NeutronCommand): log = logging.getLogger(__name__ + '.'). So introduce a metaclass to do that automatically in order to simplify the code a bit. Closes-Bug: #1258028 Change-Id: I7908e3e2c0f3f04d9e57360f6ba22e153d0d35bd --- neutronclient/neutron/v2_0/__init__.py | 16 ++++++- neutronclient/neutron/v2_0/agent.py | 6 --- neutronclient/neutron/v2_0/agentscheduler.py | 15 ------- neutronclient/neutron/v2_0/credential.py | 6 --- neutronclient/neutron/v2_0/extension.py | 4 -- neutronclient/neutron/v2_0/floatingip.py | 7 --- neutronclient/neutron/v2_0/fw/firewall.py | 6 --- .../neutron/v2_0/fw/firewallpolicy.py | 8 ---- neutronclient/neutron/v2_0/fw/firewallrule.py | 6 --- .../neutron/v2_0/lb/healthmonitor.py | 9 ---- neutronclient/neutron/v2_0/lb/member.py | 7 --- neutronclient/neutron/v2_0/lb/pool.py | 7 --- neutronclient/neutron/v2_0/lb/vip.py | 7 --- neutronclient/neutron/v2_0/metering.py | 10 ----- .../neutron/v2_0/nec/packetfilter.py | 7 --- neutronclient/neutron/v2_0/netpartition.py | 6 --- neutronclient/neutron/v2_0/network.py | 7 --- neutronclient/neutron/v2_0/networkprofile.py | 8 ---- .../neutron/v2_0/nsx/networkgateway.py | 16 ------- neutronclient/neutron/v2_0/nsx/qos_queue.py | 6 --- neutronclient/neutron/v2_0/policyprofile.py | 6 --- neutronclient/neutron/v2_0/port.py | 7 --- neutronclient/neutron/v2_0/quota.py | 5 --- neutronclient/neutron/v2_0/router.py | 12 ------ neutronclient/neutron/v2_0/securitygroup.py | 10 ----- neutronclient/neutron/v2_0/servicetype.py | 3 -- neutronclient/neutron/v2_0/subnet.py | 6 --- neutronclient/neutron/v2_0/vpn/ikepolicy.py | 7 --- .../neutron/v2_0/vpn/ipsec_site_connection.py | 7 --- neutronclient/neutron/v2_0/vpn/ipsecpolicy.py | 7 --- neutronclient/neutron/v2_0/vpn/vpnservice.py | 7 --- neutronclient/tests/unit/test_command_meta.py | 43 +++++++++++++++++++ 32 files changed, 58 insertions(+), 226 deletions(-) create mode 100644 neutronclient/tests/unit/test_command_meta.py diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index 6c978f474..130567f00 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -16,6 +16,7 @@ from __future__ import print_function +import abc import argparse import logging import re @@ -341,9 +342,22 @@ def emit_list(self, column_names, data, stdout, parsed_args): stdout.write('\n') +# command.OpenStackCommand is abstract class so that metaclass of +# subclass must be subclass of metaclass of all its base. +# otherwise metaclass conflict exception is raised. +class NeutronCommandMeta(abc.ABCMeta): + def __new__(cls, name, bases, cls_dict): + if 'log' not in cls_dict: + cls_dict['log'] = logging.getLogger( + cls_dict['__module__'] + '.' + name) + return super(NeutronCommandMeta, cls).__new__(cls, + name, bases, cls_dict) + + +@six.add_metaclass(NeutronCommandMeta) class NeutronCommand(command.OpenStackCommand): + api = 'network' - log = logging.getLogger(__name__ + '.NeutronCommand') values_specs = [] json_indent = None diff --git a/neutronclient/neutron/v2_0/agent.py b/neutronclient/neutron/v2_0/agent.py index 884d443fc..5b4887384 100644 --- a/neutronclient/neutron/v2_0/agent.py +++ b/neutronclient/neutron/v2_0/agent.py @@ -14,8 +14,6 @@ # under the License. # -import logging - from neutronclient.neutron import v2_0 as neutronV20 @@ -30,7 +28,6 @@ class ListAgent(neutronV20.ListCommand): """List agents.""" resource = 'agent' - log = logging.getLogger(__name__ + '.ListAgent') list_columns = ['id', 'agent_type', 'host', 'alive', 'admin_state_up', 'binary'] _formatters = {'heartbeat_timestamp': _format_timestamp} @@ -46,7 +43,6 @@ class ShowAgent(neutronV20.ShowCommand): """Show information of a given agent.""" resource = 'agent' - log = logging.getLogger(__name__ + '.ShowAgent') allow_names = False json_indent = 5 @@ -54,7 +50,6 @@ class ShowAgent(neutronV20.ShowCommand): class DeleteAgent(neutronV20.DeleteCommand): """Delete a given agent.""" - log = logging.getLogger(__name__ + '.DeleteAgent') resource = 'agent' allow_names = False @@ -62,6 +57,5 @@ class DeleteAgent(neutronV20.DeleteCommand): class UpdateAgent(neutronV20.UpdateCommand): """Update a given agent.""" - log = logging.getLogger(__name__ + '.UpdateAgent') resource = 'agent' allow_names = False diff --git a/neutronclient/neutron/v2_0/agentscheduler.py b/neutronclient/neutron/v2_0/agentscheduler.py index cd38c5020..db702b5bf 100644 --- a/neutronclient/neutron/v2_0/agentscheduler.py +++ b/neutronclient/neutron/v2_0/agentscheduler.py @@ -16,8 +16,6 @@ from __future__ import print_function -import logging - from neutronclient.neutron import v2_0 as neutronV20 from neutronclient.neutron.v2_0 import network from neutronclient.neutron.v2_0 import router @@ -30,8 +28,6 @@ class AddNetworkToDhcpAgent(neutronV20.NeutronCommand): """Add a network to a DHCP agent.""" - log = logging.getLogger(__name__ + '.AddNetworkToDhcpAgent') - def get_parser(self, prog_name): parser = super(AddNetworkToDhcpAgent, self).get_parser(prog_name) parser.add_argument( @@ -56,7 +52,6 @@ def run(self, parsed_args): class RemoveNetworkFromDhcpAgent(neutronV20.NeutronCommand): """Remove a network from a DHCP agent.""" - log = logging.getLogger(__name__ + '.RemoveNetworkFromDhcpAgent') def get_parser(self, prog_name): parser = super(RemoveNetworkFromDhcpAgent, self).get_parser(prog_name) @@ -83,7 +78,6 @@ def run(self, parsed_args): class ListNetworksOnDhcpAgent(network.ListNetwork): """List the networks on a DHCP agent.""" - log = logging.getLogger(__name__ + '.ListNetworksOnDhcpAgent') unknown_parts_flag = False def get_parser(self, prog_name): @@ -105,7 +99,6 @@ class ListDhcpAgentsHostingNetwork(neutronV20.ListCommand): resource = 'agent' _formatters = {} - log = logging.getLogger(__name__ + '.ListDhcpAgentsHostingNetwork') list_columns = ['id', 'host', 'admin_state_up', 'alive'] unknown_parts_flag = False @@ -133,8 +126,6 @@ def call_server(self, neutron_client, search_opts, parsed_args): class AddRouterToL3Agent(neutronV20.NeutronCommand): """Add a router to a L3 agent.""" - log = logging.getLogger(__name__ + '.AddRouterToL3Agent') - def get_parser(self, prog_name): parser = super(AddRouterToL3Agent, self).get_parser(prog_name) parser.add_argument( @@ -160,8 +151,6 @@ def run(self, parsed_args): class RemoveRouterFromL3Agent(neutronV20.NeutronCommand): """Remove a router from a L3 agent.""" - log = logging.getLogger(__name__ + '.RemoveRouterFromL3Agent') - def get_parser(self, prog_name): parser = super(RemoveRouterFromL3Agent, self).get_parser(prog_name) parser.add_argument( @@ -187,7 +176,6 @@ def run(self, parsed_args): class ListRoutersOnL3Agent(neutronV20.ListCommand): """List the routers on a L3 agent.""" - log = logging.getLogger(__name__ + '.ListRoutersOnL3Agent') _formatters = {'external_gateway_info': router._format_external_gateway_info} list_columns = ['id', 'name', 'external_gateway_info'] @@ -213,7 +201,6 @@ class ListL3AgentsHostingRouter(neutronV20.ListCommand): resource = 'agent' _formatters = {} - log = logging.getLogger(__name__ + '.ListL3AgentsHostingRouter') list_columns = ['id', 'host', 'admin_state_up', 'alive'] unknown_parts_flag = False @@ -240,7 +227,6 @@ def call_server(self, neutron_client, search_opts, parsed_args): class ListPoolsOnLbaasAgent(neutronV20.ListCommand): """List the pools on a loadbalancer agent.""" - log = logging.getLogger(__name__ + '.ListPoolsOnLbaasAgent') list_columns = ['id', 'name', 'lb_method', 'protocol', 'admin_state_up', 'status'] resource = 'pool' @@ -267,7 +253,6 @@ class GetLbaasAgentHostingPool(neutronV20.ListCommand): """ resource = 'agent' - log = logging.getLogger(__name__ + '.GetLbaasAgentHostingPool') list_columns = ['id', 'host', 'admin_state_up', 'alive'] unknown_parts_flag = False diff --git a/neutronclient/neutron/v2_0/credential.py b/neutronclient/neutron/v2_0/credential.py index 2e8c347e3..8dd38a113 100644 --- a/neutronclient/neutron/v2_0/credential.py +++ b/neutronclient/neutron/v2_0/credential.py @@ -11,8 +11,6 @@ # under the License. # -import logging - from neutronclient.neutron import v2_0 as neutronV20 from neutronclient.openstack.common.gettextutils import _ @@ -21,7 +19,6 @@ class ListCredential(neutronV20.ListCommand): """List credentials that belong to a given tenant.""" resource = 'credential' - log = logging.getLogger(__name__ + '.ListCredential') _formatters = {} list_columns = ['credential_id', 'credential_name', 'user_name', 'password', 'type'] @@ -31,7 +28,6 @@ class ShowCredential(neutronV20.ShowCommand): """Show information of a given credential.""" resource = 'credential' - log = logging.getLogger(__name__ + '.ShowCredential') allow_names = False @@ -39,7 +35,6 @@ class CreateCredential(neutronV20.CreateCommand): """Creates a credential.""" resource = 'credential' - log = logging.getLogger(__name__ + '.CreateCredential') def add_known_arguments(self, parser): parser.add_argument( @@ -74,6 +69,5 @@ def args2body(self, parsed_args): class DeleteCredential(neutronV20.DeleteCommand): """Delete a given credential.""" - log = logging.getLogger(__name__ + '.DeleteCredential') resource = 'credential' allow_names = False diff --git a/neutronclient/neutron/v2_0/extension.py b/neutronclient/neutron/v2_0/extension.py index 0170c177e..2c14d72eb 100644 --- a/neutronclient/neutron/v2_0/extension.py +++ b/neutronclient/neutron/v2_0/extension.py @@ -14,8 +14,6 @@ # under the License. # -import logging - from neutronclient.neutron import v2_0 as cmd_base from neutronclient.openstack.common.gettextutils import _ @@ -24,7 +22,6 @@ class ListExt(cmd_base.ListCommand): """List all extensions.""" resource = 'extension' - log = logging.getLogger(__name__ + '.ListExt') list_columns = ['alias', 'name'] @@ -32,7 +29,6 @@ class ShowExt(cmd_base.ShowCommand): """Show information of a given resource.""" resource = "extension" - log = logging.getLogger(__name__ + '.ShowExt') allow_names = False def get_parser(self, prog_name): diff --git a/neutronclient/neutron/v2_0/floatingip.py b/neutronclient/neutron/v2_0/floatingip.py index 3873fff8b..c70960731 100644 --- a/neutronclient/neutron/v2_0/floatingip.py +++ b/neutronclient/neutron/v2_0/floatingip.py @@ -17,7 +17,6 @@ from __future__ import print_function import argparse -import logging from neutronclient.neutron import v2_0 as neutronV20 from neutronclient.openstack.common.gettextutils import _ @@ -27,7 +26,6 @@ class ListFloatingIP(neutronV20.ListCommand): """List floating IPs that belong to a given tenant.""" resource = 'floatingip' - log = logging.getLogger(__name__ + '.ListFloatingIP') list_columns = ['id', 'fixed_ip_address', 'floating_ip_address', 'port_id'] pagination_support = True @@ -38,7 +36,6 @@ class ShowFloatingIP(neutronV20.ShowCommand): """Show information of a given floating IP.""" resource = 'floatingip' - log = logging.getLogger(__name__ + '.ShowFloatingIP') allow_names = False @@ -46,7 +43,6 @@ class CreateFloatingIP(neutronV20.CreateCommand): """Create a floating IP for a given tenant.""" resource = 'floatingip' - log = logging.getLogger(__name__ + '.CreateFloatingIP') def add_known_arguments(self, parser): parser.add_argument( @@ -83,7 +79,6 @@ def args2body(self, parsed_args): class DeleteFloatingIP(neutronV20.DeleteCommand): """Delete a given floating IP.""" - log = logging.getLogger(__name__ + '.DeleteFloatingIP') resource = 'floatingip' allow_names = False @@ -92,7 +87,6 @@ class AssociateFloatingIP(neutronV20.NeutronCommand): """Create a mapping between a floating IP and a fixed IP.""" api = 'network' - log = logging.getLogger(__name__ + '.AssociateFloatingIP') resource = 'floatingip' def get_parser(self, prog_name): @@ -133,7 +127,6 @@ class DisassociateFloatingIP(neutronV20.NeutronCommand): """ api = 'network' - log = logging.getLogger(__name__ + '.DisassociateFloatingIP') resource = 'floatingip' def get_parser(self, prog_name): diff --git a/neutronclient/neutron/v2_0/fw/firewall.py b/neutronclient/neutron/v2_0/fw/firewall.py index a60376c1c..2f0755b54 100644 --- a/neutronclient/neutron/v2_0/fw/firewall.py +++ b/neutronclient/neutron/v2_0/fw/firewall.py @@ -17,7 +17,6 @@ # import argparse -import logging from neutronclient.neutron import v2_0 as neutronv20 from neutronclient.openstack.common.gettextutils import _ @@ -27,7 +26,6 @@ class ListFirewall(neutronv20.ListCommand): """List firewalls that belong to a given tenant.""" resource = 'firewall' - log = logging.getLogger(__name__ + '.ListFirewall') list_columns = ['id', 'name', 'firewall_policy_id'] _formatters = {} pagination_support = True @@ -38,14 +36,12 @@ class ShowFirewall(neutronv20.ShowCommand): """Show information of a given firewall.""" resource = 'firewall' - log = logging.getLogger(__name__ + '.ShowFirewall') class CreateFirewall(neutronv20.CreateCommand): """Create a firewall.""" resource = 'firewall' - log = logging.getLogger(__name__ + '.CreateFirewall') def add_known_arguments(self, parser): parser.add_argument( @@ -86,11 +82,9 @@ class UpdateFirewall(neutronv20.UpdateCommand): """Update a given firewall.""" resource = 'firewall' - log = logging.getLogger(__name__ + '.UpdateFirewall') class DeleteFirewall(neutronv20.DeleteCommand): """Delete a given firewall.""" resource = 'firewall' - log = logging.getLogger(__name__ + '.DeleteFirewall') diff --git a/neutronclient/neutron/v2_0/fw/firewallpolicy.py b/neutronclient/neutron/v2_0/fw/firewallpolicy.py index 7f920b623..0571a88e3 100644 --- a/neutronclient/neutron/v2_0/fw/firewallpolicy.py +++ b/neutronclient/neutron/v2_0/fw/firewallpolicy.py @@ -19,7 +19,6 @@ from __future__ import print_function import argparse -import logging import string from neutronclient.neutron import v2_0 as neutronv20 @@ -39,7 +38,6 @@ class ListFirewallPolicy(neutronv20.ListCommand): """List firewall policies that belong to a given tenant.""" resource = 'firewall_policy' - log = logging.getLogger(__name__ + '.ListFirewallPolicy') list_columns = ['id', 'name', 'firewall_rules'] _formatters = {'firewall_rules': _format_firewall_rules, } @@ -51,14 +49,12 @@ class ShowFirewallPolicy(neutronv20.ShowCommand): """Show information of a given firewall policy.""" resource = 'firewall_policy' - log = logging.getLogger(__name__ + '.ShowFirewallPolicy') class CreateFirewallPolicy(neutronv20.CreateCommand): """Create a firewall policy.""" resource = 'firewall_policy' - log = logging.getLogger(__name__ + '.CreateFirewallPolicy') def add_known_arguments(self, parser): parser.add_argument( @@ -107,21 +103,18 @@ class UpdateFirewallPolicy(neutronv20.UpdateCommand): """Update a given firewall policy.""" resource = 'firewall_policy' - log = logging.getLogger(__name__ + '.UpdateFirewallPolicy') class DeleteFirewallPolicy(neutronv20.DeleteCommand): """Delete a given firewall policy.""" resource = 'firewall_policy' - log = logging.getLogger(__name__ + '.DeleteFirewallPolicy') class FirewallPolicyInsertRule(neutronv20.UpdateCommand): """Insert a rule into a given firewall policy.""" resource = 'firewall_policy' - log = logging.getLogger(__name__ + '.FirewallPolicyInsertRule') def call_api(self, neutron_client, firewall_policy_id, body): return neutron_client.firewall_policy_insert_rule(firewall_policy_id, @@ -184,7 +177,6 @@ class FirewallPolicyRemoveRule(neutronv20.UpdateCommand): """Remove a rule from a given firewall policy.""" resource = 'firewall_policy' - log = logging.getLogger(__name__ + '.FirewallPolicyRemoveRule') def call_api(self, neutron_client, firewall_policy_id, body): return neutron_client.firewall_policy_remove_rule(firewall_policy_id, diff --git a/neutronclient/neutron/v2_0/fw/firewallrule.py b/neutronclient/neutron/v2_0/fw/firewallrule.py index 366fdca03..680a25356 100644 --- a/neutronclient/neutron/v2_0/fw/firewallrule.py +++ b/neutronclient/neutron/v2_0/fw/firewallrule.py @@ -17,7 +17,6 @@ # import argparse -import logging from neutronclient.neutron import v2_0 as neutronv20 from neutronclient.openstack.common.gettextutils import _ @@ -27,7 +26,6 @@ class ListFirewallRule(neutronv20.ListCommand): """List firewall rules that belong to a given tenant.""" resource = 'firewall_rule' - log = logging.getLogger(__name__ + '.ListFirewallRule') list_columns = ['id', 'name', 'firewall_policy_id', 'summary', 'enabled'] pagination_support = True sorting_support = True @@ -64,14 +62,12 @@ class ShowFirewallRule(neutronv20.ShowCommand): """Show information of a given firewall rule.""" resource = 'firewall_rule' - log = logging.getLogger(__name__ + '.ShowFirewallRule') class CreateFirewallRule(neutronv20.CreateCommand): """Create a firewall rule.""" resource = 'firewall_rule' - log = logging.getLogger(__name__ + '.CreateFirewallRule') def add_known_arguments(self, parser): parser.add_argument( @@ -135,7 +131,6 @@ class UpdateFirewallRule(neutronv20.UpdateCommand): """Update a given firewall rule.""" resource = 'firewall_rule' - log = logging.getLogger(__name__ + '.UpdateFirewallRule') def add_known_arguments(self, parser): parser.add_argument( @@ -159,4 +154,3 @@ class DeleteFirewallRule(neutronv20.DeleteCommand): """Delete a given firewall rule.""" resource = 'firewall_rule' - log = logging.getLogger(__name__ + '.DeleteFirewallRule') diff --git a/neutronclient/neutron/v2_0/lb/healthmonitor.py b/neutronclient/neutron/v2_0/lb/healthmonitor.py index 22cbb8d8c..f434c40e4 100644 --- a/neutronclient/neutron/v2_0/lb/healthmonitor.py +++ b/neutronclient/neutron/v2_0/lb/healthmonitor.py @@ -18,8 +18,6 @@ from __future__ import print_function -import logging - from neutronclient.neutron import v2_0 as neutronV20 from neutronclient.openstack.common.gettextutils import _ @@ -28,7 +26,6 @@ class ListHealthMonitor(neutronV20.ListCommand): """List healthmonitors that belong to a given tenant.""" resource = 'health_monitor' - log = logging.getLogger(__name__ + '.ListHealthMonitor') list_columns = ['id', 'type', 'admin_state_up'] pagination_support = True sorting_support = True @@ -38,14 +35,12 @@ class ShowHealthMonitor(neutronV20.ShowCommand): """Show information of a given healthmonitor.""" resource = 'health_monitor' - log = logging.getLogger(__name__ + '.ShowHealthMonitor') class CreateHealthMonitor(neutronV20.CreateCommand): """Create a healthmonitor.""" resource = 'health_monitor' - log = logging.getLogger(__name__ + '.CreateHealthMonitor') def add_known_arguments(self, parser): parser.add_argument( @@ -109,7 +104,6 @@ class UpdateHealthMonitor(neutronV20.UpdateCommand): """Update a given healthmonitor.""" resource = 'health_monitor' - log = logging.getLogger(__name__ + '.UpdateHealthMonitor') allow_names = False @@ -117,13 +111,11 @@ class DeleteHealthMonitor(neutronV20.DeleteCommand): """Delete a given healthmonitor.""" resource = 'health_monitor' - log = logging.getLogger(__name__ + '.DeleteHealthMonitor') class AssociateHealthMonitor(neutronV20.NeutronCommand): """Create a mapping between a health monitor and a pool.""" - log = logging.getLogger(__name__ + '.AssociateHealthMonitor') resource = 'health_monitor' def get_parser(self, prog_name): @@ -151,7 +143,6 @@ def run(self, parsed_args): class DisassociateHealthMonitor(neutronV20.NeutronCommand): """Remove a mapping from a health monitor to a pool.""" - log = logging.getLogger(__name__ + '.DisassociateHealthMonitor') resource = 'health_monitor' def get_parser(self, prog_name): diff --git a/neutronclient/neutron/v2_0/lb/member.py b/neutronclient/neutron/v2_0/lb/member.py index 2de016b2a..1d589f269 100644 --- a/neutronclient/neutron/v2_0/lb/member.py +++ b/neutronclient/neutron/v2_0/lb/member.py @@ -16,8 +16,6 @@ # @author: Ilya Shakhat, Mirantis Inc. # -import logging - from neutronclient.neutron import v2_0 as neutronV20 from neutronclient.openstack.common.gettextutils import _ @@ -26,7 +24,6 @@ class ListMember(neutronV20.ListCommand): """List members that belong to a given tenant.""" resource = 'member' - log = logging.getLogger(__name__ + '.ListMember') list_columns = [ 'id', 'address', 'protocol_port', 'weight', 'admin_state_up', 'status' ] @@ -38,14 +35,12 @@ class ShowMember(neutronV20.ShowCommand): """Show information of a given member.""" resource = 'member' - log = logging.getLogger(__name__ + '.ShowMember') class CreateMember(neutronV20.CreateCommand): """Create a member.""" resource = 'member' - log = logging.getLogger(__name__ + '.CreateMember') def add_known_arguments(self, parser): parser.add_argument( @@ -89,11 +84,9 @@ class UpdateMember(neutronV20.UpdateCommand): """Update a given member.""" resource = 'member' - log = logging.getLogger(__name__ + '.UpdateMember') class DeleteMember(neutronV20.DeleteCommand): """Delete a given member.""" resource = 'member' - log = logging.getLogger(__name__ + '.DeleteMember') diff --git a/neutronclient/neutron/v2_0/lb/pool.py b/neutronclient/neutron/v2_0/lb/pool.py index 06724342d..f491f74b5 100644 --- a/neutronclient/neutron/v2_0/lb/pool.py +++ b/neutronclient/neutron/v2_0/lb/pool.py @@ -16,7 +16,6 @@ # @author: Ilya Shakhat, Mirantis Inc. # -import logging import six @@ -32,7 +31,6 @@ class ListPool(neutronV20.ListCommand): """List pools that belong to a given tenant.""" resource = 'pool' - log = logging.getLogger(__name__ + '.ListPool') list_columns = ['id', 'name', 'provider', 'lb_method', 'protocol', 'admin_state_up', 'status'] _formatters = {'provider': _format_provider} @@ -44,14 +42,12 @@ class ShowPool(neutronV20.ShowCommand): """Show information of a given pool.""" resource = 'pool' - log = logging.getLogger(__name__ + '.ShowPool') class CreatePool(neutronV20.CreateCommand): """Create a pool.""" resource = 'pool' - log = logging.getLogger(__name__ + '.CreatePool') def add_known_arguments(self, parser): parser.add_argument( @@ -104,21 +100,18 @@ class UpdatePool(neutronV20.UpdateCommand): """Update a given pool.""" resource = 'pool' - log = logging.getLogger(__name__ + '.UpdatePool') class DeletePool(neutronV20.DeleteCommand): """Delete a given pool.""" resource = 'pool' - log = logging.getLogger(__name__ + '.DeletePool') class RetrievePoolStats(neutronV20.ShowCommand): """Retrieve stats for a given pool.""" resource = 'pool' - log = logging.getLogger(__name__ + '.RetrievePoolStats') def get_data(self, parsed_args): self.log.debug('run(%s)' % parsed_args) diff --git a/neutronclient/neutron/v2_0/lb/vip.py b/neutronclient/neutron/v2_0/lb/vip.py index 45e6d97fa..8b1108c0d 100644 --- a/neutronclient/neutron/v2_0/lb/vip.py +++ b/neutronclient/neutron/v2_0/lb/vip.py @@ -16,8 +16,6 @@ # @author: Ilya Shakhat, Mirantis Inc. # -import logging - from neutronclient.neutron import v2_0 as neutronV20 from neutronclient.openstack.common.gettextutils import _ @@ -26,7 +24,6 @@ class ListVip(neutronV20.ListCommand): """List vips that belong to a given tenant.""" resource = 'vip' - log = logging.getLogger(__name__ + '.ListVip') list_columns = ['id', 'name', 'algorithm', 'address', 'protocol', 'admin_state_up', 'status'] pagination_support = True @@ -37,14 +34,12 @@ class ShowVip(neutronV20.ShowCommand): """Show information of a given vip.""" resource = 'vip' - log = logging.getLogger(__name__ + '.ShowVip') class CreateVip(neutronV20.CreateCommand): """Create a vip.""" resource = 'vip' - log = logging.getLogger(__name__ + '.CreateVip') def add_known_arguments(self, parser): parser.add_argument( @@ -106,11 +101,9 @@ class UpdateVip(neutronV20.UpdateCommand): """Update a given vip.""" resource = 'vip' - log = logging.getLogger(__name__ + '.UpdateVip') class DeleteVip(neutronV20.DeleteCommand): """Delete a given vip.""" resource = 'vip' - log = logging.getLogger(__name__ + '.DeleteVip') diff --git a/neutronclient/neutron/v2_0/metering.py b/neutronclient/neutron/v2_0/metering.py index 1bce239d9..20ca7572f 100644 --- a/neutronclient/neutron/v2_0/metering.py +++ b/neutronclient/neutron/v2_0/metering.py @@ -14,8 +14,6 @@ # License for the specific language governing permissions and limitations # under the License. -import logging - from neutronclient.neutron import v2_0 as neutronv20 from neutronclient.openstack.common.gettextutils import _ @@ -24,7 +22,6 @@ class ListMeteringLabel(neutronv20.ListCommand): """List metering labels that belong to a given tenant.""" resource = 'metering_label' - log = logging.getLogger(__name__ + '.ListMeteringLabel') list_columns = ['id', 'name', 'description', 'shared'] pagination_support = True sorting_support = True @@ -34,7 +31,6 @@ class ShowMeteringLabel(neutronv20.ShowCommand): """Show information of a given metering label.""" resource = 'metering_label' - log = logging.getLogger(__name__ + '.ShowMeteringLabel') allow_names = True @@ -42,7 +38,6 @@ class CreateMeteringLabel(neutronv20.CreateCommand): """Create a metering label for a given tenant.""" resource = 'metering_label' - log = logging.getLogger(__name__ + '.CreateMeteringLabel') def add_known_arguments(self, parser): parser.add_argument( @@ -74,7 +69,6 @@ def args2body(self, parsed_args): class DeleteMeteringLabel(neutronv20.DeleteCommand): """Delete a given metering label.""" - log = logging.getLogger(__name__ + '.DeleteMeteringLabel') resource = 'metering_label' allow_names = True @@ -83,7 +77,6 @@ class ListMeteringLabelRule(neutronv20.ListCommand): """List metering labels that belong to a given label.""" resource = 'metering_label_rule' - log = logging.getLogger(__name__ + '.ListMeteringLabelRule') list_columns = ['id', 'excluded', 'direction', 'remote_ip_prefix'] pagination_support = True sorting_support = True @@ -93,14 +86,12 @@ class ShowMeteringLabelRule(neutronv20.ShowCommand): """Show information of a given metering label rule.""" resource = 'metering_label_rule' - log = logging.getLogger(__name__ + '.ShowMeteringLabelRule') class CreateMeteringLabelRule(neutronv20.CreateCommand): """Create a metering label rule for a given label.""" resource = 'metering_label_rule' - log = logging.getLogger(__name__ + '.CreateMeteringLabelRule') def add_known_arguments(self, parser): parser.add_argument( @@ -141,5 +132,4 @@ def args2body(self, parsed_args): class DeleteMeteringLabelRule(neutronv20.DeleteCommand): """Delete a given metering label.""" - log = logging.getLogger(__name__ + '.DeleteMeteringLabelRule') resource = 'metering_label_rule' diff --git a/neutronclient/neutron/v2_0/nec/packetfilter.py b/neutronclient/neutron/v2_0/nec/packetfilter.py index c9e1f6ebd..12fe24d22 100644 --- a/neutronclient/neutron/v2_0/nec/packetfilter.py +++ b/neutronclient/neutron/v2_0/nec/packetfilter.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -import logging - from neutronclient.common import exceptions from neutronclient.common import validators from neutronclient.neutron import v2_0 as neutronV20 @@ -25,7 +23,6 @@ class ListPacketFilter(neutronV20.ListCommand): """List packet filters that belong to a given tenant.""" resource = 'packet_filter' - log = logging.getLogger(__name__ + '.ListPacketFilter') list_columns = ['id', 'name', 'action', 'priority', 'summary'] pagination_support = True sorting_support = True @@ -58,7 +55,6 @@ class ShowPacketFilter(neutronV20.ShowCommand): """Show information of a given packet filter.""" resource = 'packet_filter' - log = logging.getLogger(__name__ + '.ShowPacketFilter') class PacketFilterOptionMixin(object): @@ -178,7 +174,6 @@ class CreatePacketFilter(PacketFilterOptionMixin, """Create a packet filter for a given tenant.""" resource = 'packet_filter' - log = logging.getLogger(__name__ + '.CreatePacketFilter') def args2body(self, parsed_args): self.validate_fields(parsed_args) @@ -206,7 +201,6 @@ class UpdatePacketFilter(PacketFilterOptionMixin, """Update packet filter's information.""" resource = 'packet_filter' - log = logging.getLogger(__name__ + '.UpdatePacketFilter') def args2body(self, parsed_args): self.validate_fields(parsed_args) @@ -240,4 +234,3 @@ class DeletePacketFilter(neutronV20.DeleteCommand): """Delete a given packet filter.""" resource = 'packet_filter' - log = logging.getLogger(__name__ + '.DeletePacketFilter') diff --git a/neutronclient/neutron/v2_0/netpartition.py b/neutronclient/neutron/v2_0/netpartition.py index fafbfe01b..4fd060442 100644 --- a/neutronclient/neutron/v2_0/netpartition.py +++ b/neutronclient/neutron/v2_0/netpartition.py @@ -14,8 +14,6 @@ # # @author: Ronak Shah, Nuage Networks, Alcatel-Lucent USA Inc. -import logging - from neutronclient.neutron.v2_0 import CreateCommand from neutronclient.neutron.v2_0 import DeleteCommand from neutronclient.neutron.v2_0 import ListCommand @@ -25,7 +23,6 @@ class ListNetPartition(ListCommand): """List netpartitions that belong to a given tenant.""" resource = 'net_partition' - log = logging.getLogger(__name__ + '.ListNetPartition') list_columns = ['id', 'name'] @@ -33,14 +30,12 @@ class ShowNetPartition(ShowCommand): """Show information of a given netpartition.""" resource = 'net_partition' - log = logging.getLogger(__name__ + '.ShowNetPartition') class CreateNetPartition(CreateCommand): """Create a netpartition for a given tenant.""" resource = 'net_partition' - log = logging.getLogger(__name__ + '.CreateNetPartition') def add_known_arguments(self, parser): parser.add_argument( @@ -56,4 +51,3 @@ class DeleteNetPartition(DeleteCommand): """Delete a given netpartition.""" resource = 'net_partition' - log = logging.getLogger(__name__ + '.DeleteNetPartition') diff --git a/neutronclient/neutron/v2_0/network.py b/neutronclient/neutron/v2_0/network.py index ffdaef6f5..05a8de7d8 100644 --- a/neutronclient/neutron/v2_0/network.py +++ b/neutronclient/neutron/v2_0/network.py @@ -15,7 +15,6 @@ # import argparse -import logging from neutronclient.common import exceptions from neutronclient.neutron import v2_0 as neutronV20 @@ -37,7 +36,6 @@ class ListNetwork(neutronV20.ListCommand): # id=& (with len(uuid)=36) subnet_id_filter_len = 40 resource = 'network' - log = logging.getLogger(__name__ + '.ListNetwork') _formatters = {'subnets': _format_subnets, } list_columns = ['id', 'name', 'subnets'] pagination_support = True @@ -86,7 +84,6 @@ def _get_subnet_list(sub_ids): class ListExternalNetwork(ListNetwork): """List external networks that belong to a given tenant.""" - log = logging.getLogger(__name__ + '.ListExternalNetwork') pagination_support = True sorting_support = True @@ -101,14 +98,12 @@ class ShowNetwork(neutronV20.ShowCommand): """Show information of a given network.""" resource = 'network' - log = logging.getLogger(__name__ + '.ShowNetwork') class CreateNetwork(neutronV20.CreateCommand): """Create a network for a given tenant.""" resource = 'network' - log = logging.getLogger(__name__ + '.CreateNetwork') def add_known_arguments(self, parser): parser.add_argument( @@ -140,12 +135,10 @@ def args2body(self, parsed_args): class DeleteNetwork(neutronV20.DeleteCommand): """Delete a given network.""" - log = logging.getLogger(__name__ + '.DeleteNetwork') resource = 'network' class UpdateNetwork(neutronV20.UpdateCommand): """Update network's information.""" - log = logging.getLogger(__name__ + '.UpdateNetwork') resource = 'network' diff --git a/neutronclient/neutron/v2_0/networkprofile.py b/neutronclient/neutron/v2_0/networkprofile.py index 4dc0be6a5..2b693bd4b 100644 --- a/neutronclient/neutron/v2_0/networkprofile.py +++ b/neutronclient/neutron/v2_0/networkprofile.py @@ -16,8 +16,6 @@ from __future__ import print_function -import logging - from neutronclient.neutron import v2_0 as neutronV20 from neutronclient.neutron.v2_0 import parse_args_to_dict from neutronclient.openstack.common.gettextutils import _ @@ -30,7 +28,6 @@ class ListNetworkProfile(neutronV20.ListCommand): """List network profiles that belong to a given tenant.""" resource = RESOURCE - log = logging.getLogger(__name__ + '.ListNetworkProfile') _formatters = {} list_columns = ['id', 'name', 'segment_type', 'sub_type', 'segment_range', 'physical_network', 'multicast_ip_index', @@ -41,7 +38,6 @@ class ShowNetworkProfile(neutronV20.ShowCommand): """Show information of a given network profile.""" resource = RESOURCE - log = logging.getLogger(__name__ + '.ShowNetworkProfile') allow_names = True @@ -49,7 +45,6 @@ class CreateNetworkProfile(neutronV20.CreateCommand): """Creates a network profile.""" resource = RESOURCE - log = logging.getLogger(__name__ + '.CreateNetworkProfile') def add_known_arguments(self, parser): parser.add_argument('name', @@ -97,7 +92,6 @@ def args2body(self, parsed_args): class DeleteNetworkProfile(neutronV20.DeleteCommand): """Delete a given network profile.""" - log = logging.getLogger(__name__ + '.DeleteNetworkProfile') resource = RESOURCE allow_names = True @@ -106,13 +100,11 @@ class UpdateNetworkProfile(neutronV20.UpdateCommand): """Update network profile's information.""" resource = RESOURCE - log = logging.getLogger(__name__ + '.UpdateNetworkProfile') class UpdateNetworkProfileV2(neutronV20.NeutronCommand): api = 'network' - log = logging.getLogger(__name__ + '.UpdateNetworkProfileV2') resource = RESOURCE def get_parser(self, prog_name): diff --git a/neutronclient/neutron/v2_0/nsx/networkgateway.py b/neutronclient/neutron/v2_0/nsx/networkgateway.py index 3a6e1fefe..0bf810906 100644 --- a/neutronclient/neutron/v2_0/nsx/networkgateway.py +++ b/neutronclient/neutron/v2_0/nsx/networkgateway.py @@ -16,8 +16,6 @@ from __future__ import print_function -import logging - from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronV20 from neutronclient.openstack.common.gettextutils import _ @@ -41,7 +39,6 @@ class ListGatewayDevice(neutronV20.ListCommand): """List network gateway devices for a given tenant.""" resource = DEV_RESOURCE - log = logging.getLogger(__name__ + '.ListGatewayDevice') list_columns = ['id', 'name'] @@ -49,7 +46,6 @@ class ShowGatewayDevice(neutronV20.ShowCommand): """Show information for a given network gateway device.""" resource = DEV_RESOURCE - log = logging.getLogger(__name__ + '.ShowGatewayDevice') def read_cert_file(cert_file): @@ -80,7 +76,6 @@ class CreateGatewayDevice(neutronV20.CreateCommand): """Create a network gateway device.""" resource = DEV_RESOURCE - log = logging.getLogger(__name__ + '.CreateGatewayDevice') def add_known_arguments(self, parser): parser.add_argument( @@ -114,7 +109,6 @@ class UpdateGatewayDevice(neutronV20.UpdateCommand): """Update a network gateway device.""" resource = DEV_RESOURCE - log = logging.getLogger(__name__ + '.UpdateGatewayDevice') def add_known_arguments(self, parser): parser.add_argument( @@ -147,14 +141,12 @@ class DeleteGatewayDevice(neutronV20.DeleteCommand): """Delete a given network gateway device.""" resource = DEV_RESOURCE - log = logging.getLogger(__name__ + '.DeleteGatewayDevice') class ListNetworkGateway(neutronV20.ListCommand): """List network gateways for a given tenant.""" resource = GW_RESOURCE - log = logging.getLogger(__name__ + '.ListNetworkGateway') list_columns = ['id', 'name'] @@ -162,14 +154,12 @@ class ShowNetworkGateway(neutronV20.ShowCommand): """Show information of a given network gateway.""" resource = GW_RESOURCE - log = logging.getLogger(__name__ + '.ShowNetworkGateway') class CreateNetworkGateway(neutronV20.CreateCommand): """Create a network gateway.""" resource = GW_RESOURCE - log = logging.getLogger(__name__ + '.CreateNetworkGateway') def add_known_arguments(self, parser): parser.add_argument( @@ -199,14 +189,12 @@ class DeleteNetworkGateway(neutronV20.DeleteCommand): """Delete a given network gateway.""" resource = GW_RESOURCE - log = logging.getLogger(__name__ + '.DeleteNetworkGateway') class UpdateNetworkGateway(neutronV20.UpdateCommand): """Update the name for a network gateway.""" resource = GW_RESOURCE - log = logging.getLogger(__name__ + '.UpdateNetworkGateway') class NetworkGatewayInterfaceCommand(neutronV20.NeutronCommand): @@ -244,8 +232,6 @@ def retrieve_ids(self, client, args): class ConnectNetworkGateway(NetworkGatewayInterfaceCommand): """Add an internal network interface to a router.""" - log = logging.getLogger(__name__ + '.ConnectNetworkGateway') - def run(self, parsed_args): self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() @@ -265,8 +251,6 @@ def run(self, parsed_args): class DisconnectNetworkGateway(NetworkGatewayInterfaceCommand): """Remove a network from a network gateway.""" - log = logging.getLogger(__name__ + '.DisconnectNetworkGateway') - def run(self, parsed_args): self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() diff --git a/neutronclient/neutron/v2_0/nsx/qos_queue.py b/neutronclient/neutron/v2_0/nsx/qos_queue.py index 393f6b233..e8c8254fc 100644 --- a/neutronclient/neutron/v2_0/nsx/qos_queue.py +++ b/neutronclient/neutron/v2_0/nsx/qos_queue.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -import logging - from neutronclient.neutron import v2_0 as neutronV20 from neutronclient.openstack.common.gettextutils import _ @@ -23,7 +21,6 @@ class ListQoSQueue(neutronV20.ListCommand): """List queues that belong to a given tenant.""" resource = 'qos_queue' - log = logging.getLogger(__name__ + '.ListQoSQueue') list_columns = ['id', 'name', 'min', 'max', 'qos_marking', 'dscp', 'default'] @@ -32,7 +29,6 @@ class ShowQoSQueue(neutronV20.ShowCommand): """Show information of a given queue.""" resource = 'qos_queue' - log = logging.getLogger(__name__ + '.ShowQoSQueue') allow_names = True @@ -40,7 +36,6 @@ class CreateQoSQueue(neutronV20.CreateCommand): """Create a queue.""" resource = 'qos_queue' - log = logging.getLogger(__name__ + '.CreateQoSQueue') def add_known_arguments(self, parser): parser.add_argument( @@ -83,6 +78,5 @@ def args2body(self, parsed_args): class DeleteQoSQueue(neutronV20.DeleteCommand): """Delete a given queue.""" - log = logging.getLogger(__name__ + '.DeleteQoSQueue') resource = 'qos_queue' allow_names = True diff --git a/neutronclient/neutron/v2_0/policyprofile.py b/neutronclient/neutron/v2_0/policyprofile.py index 8ba616b42..580ef8ca7 100644 --- a/neutronclient/neutron/v2_0/policyprofile.py +++ b/neutronclient/neutron/v2_0/policyprofile.py @@ -15,8 +15,6 @@ from __future__ import print_function -import logging - from neutronclient.neutron import v2_0 as neutronV20 from neutronclient.neutron.v2_0 import parse_args_to_dict from neutronclient.openstack.common.gettextutils import _ @@ -28,7 +26,6 @@ class ListPolicyProfile(neutronV20.ListCommand): """List policy profiles that belong to a given tenant.""" resource = RESOURCE - log = logging.getLogger(__name__ + '.ListProfile') _formatters = {} list_columns = ['id', 'name'] @@ -37,7 +34,6 @@ class ShowPolicyProfile(neutronV20.ShowCommand): """Show information of a given policy profile.""" resource = RESOURCE - log = logging.getLogger(__name__ + '.ShowProfile') allow_names = True @@ -45,14 +41,12 @@ class UpdatePolicyProfile(neutronV20.UpdateCommand): """Update policy profile's information.""" resource = RESOURCE - log = logging.getLogger(__name__ + '.UpdatePolicyProfile') class UpdatePolicyProfileV2(neutronV20.UpdateCommand): """Update policy profile's information.""" api = 'network' - log = logging.getLogger(__name__ + '.UpdatePolicyProfileV2') resource = RESOURCE def get_parser(self, prog_name): diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index 4e95b0b53..1cc0e0ca3 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -15,7 +15,6 @@ # import argparse -import logging from neutronclient.common import exceptions from neutronclient.common import utils @@ -34,7 +33,6 @@ class ListPort(neutronV20.ListCommand): """List ports that belong to a given tenant.""" resource = 'port' - log = logging.getLogger(__name__ + '.ListPort') _formatters = {'fixed_ips': _format_fixed_ips, } list_columns = ['id', 'name', 'mac_address', 'fixed_ips'] pagination_support = True @@ -45,7 +43,6 @@ class ListRouterPort(neutronV20.ListCommand): """List ports that belong to a given tenant, with specified router.""" resource = 'port' - log = logging.getLogger(__name__ + '.ListRouterPort') _formatters = {'fixed_ips': _format_fixed_ips, } list_columns = ['id', 'name', 'mac_address', 'fixed_ips'] pagination_support = True @@ -71,7 +68,6 @@ class ShowPort(neutronV20.ShowCommand): """Show information of a given port.""" resource = 'port' - log = logging.getLogger(__name__ + '.ShowPort') class UpdatePortSecGroupMixin(object): @@ -144,7 +140,6 @@ class CreatePort(neutronV20.CreateCommand, UpdatePortSecGroupMixin, """Create a port for a given tenant.""" resource = 'port' - log = logging.getLogger(__name__ + '.CreatePort') def add_known_arguments(self, parser): parser.add_argument( @@ -224,7 +219,6 @@ class DeletePort(neutronV20.DeleteCommand): """Delete a given port.""" resource = 'port' - log = logging.getLogger(__name__ + '.DeletePort') class UpdatePort(neutronV20.UpdateCommand, UpdatePortSecGroupMixin, @@ -232,7 +226,6 @@ class UpdatePort(neutronV20.UpdateCommand, UpdatePortSecGroupMixin, """Update port's information.""" resource = 'port' - log = logging.getLogger(__name__ + '.UpdatePort') def add_known_arguments(self, parser): self.add_arguments_secgroup(parser) diff --git a/neutronclient/neutron/v2_0/quota.py b/neutronclient/neutron/v2_0/quota.py index 2ee462b96..af8076e15 100644 --- a/neutronclient/neutron/v2_0/quota.py +++ b/neutronclient/neutron/v2_0/quota.py @@ -17,7 +17,6 @@ from __future__ import print_function import argparse -import logging from cliff import lister from cliff import show @@ -39,7 +38,6 @@ class DeleteQuota(neutronV20.NeutronCommand): api = 'network' resource = 'quota' - log = logging.getLogger(__name__ + '.DeleteQuota') def get_parser(self, prog_name): parser = super(DeleteQuota, self).get_parser(prog_name) @@ -72,7 +70,6 @@ class ListQuota(neutronV20.NeutronCommand, lister.Lister): api = 'network' resource = 'quota' - log = logging.getLogger(__name__ + '.ListQuota') def get_parser(self, prog_name): parser = super(ListQuota, self).get_parser(prog_name) @@ -102,7 +99,6 @@ class ShowQuota(neutronV20.NeutronCommand, show.ShowOne): """ api = 'network' resource = "quota" - log = logging.getLogger(__name__ + '.ShowQuota') def get_parser(self, prog_name): parser = super(ShowQuota, self).get_parser(prog_name) @@ -147,7 +143,6 @@ class UpdateQuota(neutronV20.NeutronCommand, show.ShowOne): """Define tenant's quotas not to use defaults.""" resource = 'quota' - log = logging.getLogger(__name__ + '.UpdateQuota') def get_parser(self, prog_name): parser = super(UpdateQuota, self).get_parser(prog_name) diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index 76f18b140..2f6a3c8d5 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -17,7 +17,6 @@ from __future__ import print_function import argparse -import logging from neutronclient.common import exceptions from neutronclient.common import utils @@ -36,7 +35,6 @@ class ListRouter(neutronV20.ListCommand): """List routers that belong to a given tenant.""" resource = 'router' - log = logging.getLogger(__name__ + '.ListRouter') _formatters = {'external_gateway_info': _format_external_gateway_info, } list_columns = ['id', 'name', 'external_gateway_info', 'distributed'] pagination_support = True @@ -47,14 +45,12 @@ class ShowRouter(neutronV20.ShowCommand): """Show information of a given router.""" resource = 'router' - log = logging.getLogger(__name__ + '.ShowRouter') class CreateRouter(neutronV20.CreateCommand): """Create a router for a given tenant.""" resource = 'router' - log = logging.getLogger(__name__ + '.CreateRouter') _formatters = {'external_gateway_info': _format_external_gateway_info, } def add_known_arguments(self, parser): @@ -85,14 +81,12 @@ def args2body(self, parsed_args): class DeleteRouter(neutronV20.DeleteCommand): """Delete a given router.""" - log = logging.getLogger(__name__ + '.DeleteRouter') resource = 'router' class UpdateRouter(neutronV20.UpdateCommand): """Update router's information.""" - log = logging.getLogger(__name__ + '.UpdateRouter') resource = 'router' @@ -150,8 +144,6 @@ def run(self, parsed_args): class AddInterfaceRouter(RouterInterfaceCommand): """Add an internal network interface to a router.""" - log = logging.getLogger(__name__ + '.AddInterfaceRouter') - def call_api(self, neutron_client, router_id, body): return neutron_client.add_interface_router(router_id, body) @@ -163,8 +155,6 @@ def success_message(self, router_id, portinfo): class RemoveInterfaceRouter(RouterInterfaceCommand): """Remove an internal network interface from a router.""" - log = logging.getLogger(__name__ + '.RemoveInterfaceRouter') - def call_api(self, neutron_client, router_id, body): return neutron_client.remove_interface_router(router_id, body) @@ -176,7 +166,6 @@ def success_message(self, router_id, portinfo): class SetGatewayRouter(neutronV20.NeutronCommand): """Set the external network gateway for a router.""" - log = logging.getLogger(__name__ + '.SetGatewayRouter') api = 'network' resource = 'router' @@ -212,7 +201,6 @@ def run(self, parsed_args): class RemoveGatewayRouter(neutronV20.NeutronCommand): """Remove an external network gateway from a router.""" - log = logging.getLogger(__name__ + '.RemoveGatewayRouter') api = 'network' resource = 'router' diff --git a/neutronclient/neutron/v2_0/securitygroup.py b/neutronclient/neutron/v2_0/securitygroup.py index 7e66e8a3c..f4d2d151e 100644 --- a/neutronclient/neutron/v2_0/securitygroup.py +++ b/neutronclient/neutron/v2_0/securitygroup.py @@ -15,7 +15,6 @@ # import argparse -import logging from neutronclient.neutron import v2_0 as neutronV20 from neutronclient.openstack.common.gettextutils import _ @@ -25,7 +24,6 @@ class ListSecurityGroup(neutronV20.ListCommand): """List security groups that belong to a given tenant.""" resource = 'security_group' - log = logging.getLogger(__name__ + '.ListSecurityGroup') list_columns = ['id', 'name', 'description'] pagination_support = True sorting_support = True @@ -35,7 +33,6 @@ class ShowSecurityGroup(neutronV20.ShowCommand): """Show information of a given security group.""" resource = 'security_group' - log = logging.getLogger(__name__ + '.ShowSecurityGroup') allow_names = True @@ -43,7 +40,6 @@ class CreateSecurityGroup(neutronV20.CreateCommand): """Create a security group.""" resource = 'security_group' - log = logging.getLogger(__name__ + '.CreateSecurityGroup') def add_known_arguments(self, parser): parser.add_argument( @@ -67,7 +63,6 @@ def args2body(self, parsed_args): class DeleteSecurityGroup(neutronV20.DeleteCommand): """Delete a given security group.""" - log = logging.getLogger(__name__ + '.DeleteSecurityGroup') resource = 'security_group' allow_names = True @@ -75,7 +70,6 @@ class DeleteSecurityGroup(neutronV20.DeleteCommand): class UpdateSecurityGroup(neutronV20.UpdateCommand): """Update a given security group.""" - log = logging.getLogger(__name__ + '.UpdateSecurityGroup') resource = 'security_group' def add_known_arguments(self, parser): @@ -101,7 +95,6 @@ class ListSecurityGroupRule(neutronV20.ListCommand): """List security group rules that belong to a given tenant.""" resource = 'security_group_rule' - log = logging.getLogger(__name__ + '.ListSecurityGroupRule') list_columns = ['id', 'security_group_id', 'direction', 'protocol', 'remote_ip_prefix', 'remote_group_id'] replace_rules = {'security_group_id': 'security_group', @@ -170,7 +163,6 @@ class ShowSecurityGroupRule(neutronV20.ShowCommand): """Show information of a given security group rule.""" resource = 'security_group_rule' - log = logging.getLogger(__name__ + '.ShowSecurityGroupRule') allow_names = False @@ -178,7 +170,6 @@ class CreateSecurityGroupRule(neutronV20.CreateCommand): """Create a security group rule.""" resource = 'security_group_rule' - log = logging.getLogger(__name__ + '.CreateSecurityGroupRule') def add_known_arguments(self, parser): parser.add_argument( @@ -254,6 +245,5 @@ def args2body(self, parsed_args): class DeleteSecurityGroupRule(neutronV20.DeleteCommand): """Delete a given security group rule.""" - log = logging.getLogger(__name__ + '.DeleteSecurityGroupRule') resource = 'security_group_rule' allow_names = False diff --git a/neutronclient/neutron/v2_0/servicetype.py b/neutronclient/neutron/v2_0/servicetype.py index 4666a9706..ec8454f94 100644 --- a/neutronclient/neutron/v2_0/servicetype.py +++ b/neutronclient/neutron/v2_0/servicetype.py @@ -14,8 +14,6 @@ # under the License. # -import logging - from neutronclient.neutron import v2_0 as neutronV20 @@ -23,7 +21,6 @@ class ListServiceProvider(neutronV20.ListCommand): """List service providers.""" resource = 'service_provider' - log = logging.getLogger(__name__ + '.ListServiceProviders') list_columns = ['service_type', 'name', 'default'] _formatters = {} pagination_support = True diff --git a/neutronclient/neutron/v2_0/subnet.py b/neutronclient/neutron/v2_0/subnet.py index 4de0058e2..6ab8ae2c7 100644 --- a/neutronclient/neutron/v2_0/subnet.py +++ b/neutronclient/neutron/v2_0/subnet.py @@ -15,7 +15,6 @@ # import argparse -import logging from neutronclient.common import exceptions from neutronclient.common import utils @@ -136,7 +135,6 @@ class ListSubnet(neutronV20.ListCommand): """List subnets that belong to a given tenant.""" resource = 'subnet' - log = logging.getLogger(__name__ + '.ListSubnet') _formatters = {'allocation_pools': _format_allocation_pools, 'dns_nameservers': _format_dns_nameservers, 'host_routes': _format_host_routes, } @@ -149,14 +147,12 @@ class ShowSubnet(neutronV20.ShowCommand): """Show information of a given subnet.""" resource = 'subnet' - log = logging.getLogger(__name__ + '.ShowSubnet') class CreateSubnet(neutronV20.CreateCommand): """Create a subnet for a given tenant.""" resource = 'subnet' - log = logging.getLogger(__name__ + '.CreateSubnet') def add_known_arguments(self, parser): add_updatable_arguments(parser) @@ -200,14 +196,12 @@ class DeleteSubnet(neutronV20.DeleteCommand): """Delete a given subnet.""" resource = 'subnet' - log = logging.getLogger(__name__ + '.DeleteSubnet') class UpdateSubnet(neutronV20.UpdateCommand): """Update subnet's information.""" resource = 'subnet' - log = logging.getLogger(__name__ + '.UpdateSubnet') def add_known_arguments(self, parser): add_updatable_arguments(parser) diff --git a/neutronclient/neutron/v2_0/vpn/ikepolicy.py b/neutronclient/neutron/v2_0/vpn/ikepolicy.py index 90d432dd6..71df04772 100644 --- a/neutronclient/neutron/v2_0/vpn/ikepolicy.py +++ b/neutronclient/neutron/v2_0/vpn/ikepolicy.py @@ -16,8 +16,6 @@ # @author: Swaminathan Vasudevan, Hewlett-Packard. # -import logging - from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronv20 from neutronclient.neutron.v2_0.vpn import utils as vpn_utils @@ -28,7 +26,6 @@ class ListIKEPolicy(neutronv20.ListCommand): """List IKE policies that belong to a tenant.""" resource = 'ikepolicy' - log = logging.getLogger(__name__ + '.ListIKEPolicy') list_columns = ['id', 'name', 'auth_algorithm', 'encryption_algorithm', 'ike_version', 'pfs'] _formatters = {} @@ -40,14 +37,12 @@ class ShowIKEPolicy(neutronv20.ShowCommand): """Show information of a given IKE policy.""" resource = 'ikepolicy' - log = logging.getLogger(__name__ + '.ShowIKEPolicy') class CreateIKEPolicy(neutronv20.CreateCommand): """Create an IKE policy.""" resource = 'ikepolicy' - log = logging.getLogger(__name__ + '.CreateIKEPolicy') def add_known_arguments(self, parser): parser.add_argument( @@ -108,7 +103,6 @@ class UpdateIKEPolicy(neutronv20.UpdateCommand): """Update a given IKE policy.""" resource = 'ikepolicy' - log = logging.getLogger(__name__ + '.UpdateIKEPolicy') def add_known_arguments(self, parser): parser.add_argument( @@ -131,4 +125,3 @@ class DeleteIKEPolicy(neutronv20.DeleteCommand): """Delete a given IKE policy.""" resource = 'ikepolicy' - log = logging.getLogger(__name__ + '.DeleteIKEPolicy') diff --git a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py index aefdf6a03..6dbeb37b5 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py +++ b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py @@ -16,8 +16,6 @@ # @author: Swaminathan Vasudevan, Hewlett-Packard. # -import logging - from neutronclient.common import exceptions from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronv20 @@ -37,7 +35,6 @@ class ListIPsecSiteConnection(neutronv20.ListCommand): """List IPsec site connections that belong to a given tenant.""" resource = 'ipsec_site_connection' - log = logging.getLogger(__name__ + '.ListIPsecSiteConnection') _formatters = {'peer_cidrs': _format_peer_cidrs} list_columns = [ 'id', 'name', 'peer_address', 'peer_cidrs', 'route_mode', @@ -50,13 +47,11 @@ class ShowIPsecSiteConnection(neutronv20.ShowCommand): """Show information of a given IPsec site connection.""" resource = 'ipsec_site_connection' - log = logging.getLogger(__name__ + '.ShowIPsecSiteConnection') class CreateIPsecSiteConnection(neutronv20.CreateCommand): """Create an IPsec site connection.""" resource = 'ipsec_site_connection' - log = logging.getLogger(__name__ + '.CreateIPsecSiteConnection') def add_known_arguments(self, parser): parser.add_argument( @@ -165,7 +160,6 @@ class UpdateIPsecSiteConnection(neutronv20.UpdateCommand): """Update a given IPsec site connection.""" resource = 'ipsec_site_connection' - log = logging.getLogger(__name__ + '.UpdateIPsecSiteConnection') def add_known_arguments(self, parser): @@ -189,4 +183,3 @@ class DeleteIPsecSiteConnection(neutronv20.DeleteCommand): """Delete a given IPsec site connection.""" resource = 'ipsec_site_connection' - log = logging.getLogger(__name__ + '.DeleteIPsecSiteConnection') diff --git a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py index 6ac182a33..82a527984 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py +++ b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py @@ -15,8 +15,6 @@ # # @author: Swaminathan Vasudevan, Hewlett-Packard. -import logging - from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronv20 from neutronclient.neutron.v2_0.vpn import utils as vpn_utils @@ -27,7 +25,6 @@ class ListIPsecPolicy(neutronv20.ListCommand): """List ipsecpolicies that belongs to a given tenant connection.""" resource = 'ipsecpolicy' - log = logging.getLogger(__name__ + '.ListIPsecPolicy') list_columns = ['id', 'name', 'auth_algorithm', 'encryption_algorithm', 'pfs'] _formatters = {} @@ -39,14 +36,12 @@ class ShowIPsecPolicy(neutronv20.ShowCommand): """Show information of a given IPsec policy.""" resource = 'ipsecpolicy' - log = logging.getLogger(__name__ + '.ShowIPsecPolicy') class CreateIPsecPolicy(neutronv20.CreateCommand): """Create an IPsec policy.""" resource = 'ipsecpolicy' - log = logging.getLogger(__name__ + '.CreateIPsecPolicy') def add_known_arguments(self, parser): parser.add_argument( @@ -108,7 +103,6 @@ class UpdateIPsecPolicy(neutronv20.UpdateCommand): """Update a given IPsec policy.""" resource = 'ipsecpolicy' - log = logging.getLogger(__name__ + '.UpdateIPsecPolicy') def add_known_arguments(self, parser): parser.add_argument( @@ -131,4 +125,3 @@ class DeleteIPsecPolicy(neutronv20.DeleteCommand): """Delete a given IPsec policy.""" resource = 'ipsecpolicy' - log = logging.getLogger(__name__ + '.DeleteIPsecPolicy') diff --git a/neutronclient/neutron/v2_0/vpn/vpnservice.py b/neutronclient/neutron/v2_0/vpn/vpnservice.py index bcd0c9053..4dac74375 100644 --- a/neutronclient/neutron/v2_0/vpn/vpnservice.py +++ b/neutronclient/neutron/v2_0/vpn/vpnservice.py @@ -16,8 +16,6 @@ # @author: Swaminathan Vasudevan, Hewlett-Packard. # -import logging - from neutronclient.neutron import v2_0 as neutronv20 from neutronclient.openstack.common.gettextutils import _ @@ -26,7 +24,6 @@ class ListVPNService(neutronv20.ListCommand): """List VPN service configurations that belong to a given tenant.""" resource = 'vpnservice' - log = logging.getLogger(__name__ + '.ListVPNService') list_columns = [ 'id', 'name', 'router_id', 'status' ] @@ -39,13 +36,11 @@ class ShowVPNService(neutronv20.ShowCommand): """Show information of a given VPN service.""" resource = 'vpnservice' - log = logging.getLogger(__name__ + '.ShowVPNService') class CreateVPNService(neutronv20.CreateCommand): """Create a VPN service.""" resource = 'vpnservice' - log = logging.getLogger(__name__ + '.CreateVPNService') def add_known_arguments(self, parser): parser.add_argument( @@ -87,11 +82,9 @@ class UpdateVPNService(neutronv20.UpdateCommand): """Update a given VPN service.""" resource = 'vpnservice' - log = logging.getLogger(__name__ + '.UpdateVPNService') class DeleteVPNService(neutronv20.DeleteCommand): """Delete a given VPN service.""" resource = 'vpnservice' - log = logging.getLogger(__name__ + '.DeleteVPNService') diff --git a/neutronclient/tests/unit/test_command_meta.py b/neutronclient/tests/unit/test_command_meta.py new file mode 100644 index 000000000..be50ab8ab --- /dev/null +++ b/neutronclient/tests/unit/test_command_meta.py @@ -0,0 +1,43 @@ +# Copyright 2013 Intel +# Copyright 2013 Isaku Yamahata +# +# 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. +# +# @author: Isaku Yamahata, Intel + +import logging + +import testtools +from testtools import helpers + +from neutronclient.neutron import v2_0 as neutronV20 + + +class TestCommandMeta(testtools.TestCase): + def test_neutron_command_meta_defines_log(self): + class FakeCommand(neutronV20.NeutronCommand): + pass + + self.assertTrue(helpers.safe_hasattr(FakeCommand, 'log')) + self.assertIsInstance(FakeCommand.log, logging.getLoggerClass()) + self.assertEqual(FakeCommand.log.name, __name__ + ".FakeCommand") + + def test_neutron_command_log_defined_explicitly(self): + class FakeCommand(neutronV20.NeutronCommand): + log = None + + self.assertTrue(helpers.safe_hasattr(FakeCommand, 'log')) + self.assertIsNone(FakeCommand.log) From a3d0095964b9b15671a74bcf6f0835903d29be6c Mon Sep 17 00:00:00 2001 From: Craig Tracey Date: Sat, 2 Aug 2014 01:09:46 -0400 Subject: [PATCH 061/845] Introduce shadow resources for NeutronCommands With the acceptance of the v2 LBaaS object model we will be required to support both the original v1 (soon to be deprecated) and v2 side-by-side. Not surprisingly these two modelsshare a number of object types (ie. pools, members, etc.) In order to handle this overlap of object names, the API prefixes v1 with /lb whereas v2 is prefixed with /lbaas. This presents a problem for python-neutronclient in that resource name alone (until now) was adequate for the string interpolation that helps derive the method name to be called. Now that we have, for instance, two different "pool" objects, we need a way to handle each uniquely. Therefore, in order to maintain backwards compatibility, and minimize complexity, this change introduces the concept of a shadow resource. This shadow recource is used to direct the client at the correct paths and methods to call internally, get still leverage the same logic that has enabled resources to be manipulated by the de facto resource name. Shadow resources are only necessary when an API is in this particular type of scenario: the deprecation of a previous and overlapping former API. Otherwise, new and existing API's can continue to use the same logic as before. This change also moves the 'resource' attributes from the Neutron subclasses into NeutronCommand itself as they were common to all the subcommands already. Change-Id: I69270debe25aac2659977d66d7887154ef0cdddf Partially-implements: blueprint lbaas-api-and-objmodel-improvement --- neutronclient/neutron/v2_0/__init__.py | 66 +++++++++++++------- neutronclient/tests/unit/test_cli20.py | 83 +++++++++++++++++--------- 2 files changed, 97 insertions(+), 52 deletions(-) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index 6c978f474..74e942ba1 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -44,9 +44,12 @@ def _get_resource_plural(resource, client): return resource + 's' -def find_resourceid_by_id(client, resource, resource_id): +def find_resourceid_by_id(client, resource, resource_id, cmd_resource=None): + if not cmd_resource: + cmd_resource = resource + cmd_resource_plural = _get_resource_plural(cmd_resource, client) resource_plural = _get_resource_plural(resource, client) - obj_lister = getattr(client, "list_%s" % resource_plural) + obj_lister = getattr(client, "list_%s" % cmd_resource_plural) # perform search by id only if we are passing a valid UUID match = re.match(UUID_PATTERN, resource_id) collection = resource_plural @@ -62,9 +65,13 @@ def find_resourceid_by_id(client, resource, resource_id): message=not_found_message, status_code=404) -def _find_resourceid_by_name(client, resource, name, project_id=None): +def _find_resourceid_by_name(client, resource, name, project_id=None, + cmd_resource=None): + if not cmd_resource: + cmd_resource = resource + cmd_resource_plural = _get_resource_plural(cmd_resource, client) resource_plural = _get_resource_plural(resource, client) - obj_lister = getattr(client, "list_%s" % resource_plural) + obj_lister = getattr(client, "list_%s" % cmd_resource_plural) params = {'name': name, 'fields': 'id'} if project_id: params['tenant_id'] = project_id @@ -86,12 +93,13 @@ def _find_resourceid_by_name(client, resource, name, project_id=None): def find_resourceid_by_name_or_id(client, resource, name_or_id, - project_id=None): + project_id=None, cmd_resource=None): try: - return find_resourceid_by_id(client, resource, name_or_id) + return find_resourceid_by_id(client, resource, name_or_id, + cmd_resource) except exceptions.NeutronClientException: return _find_resourceid_by_name(client, resource, name_or_id, - project_id) + project_id, cmd_resource) def add_show_list_common_argument(parser): @@ -346,6 +354,8 @@ class NeutronCommand(command.OpenStackCommand): log = logging.getLogger(__name__ + '.NeutronCommand') values_specs = [] json_indent = None + resource = None + shadow_resource = None def __init__(self, app, app_args): super(NeutronCommand, self).__init__(app, app_args) @@ -355,6 +365,12 @@ def __init__(self, app, app_args): #if hasattr(self, 'formatters'): #self.formatters['table'] = TableFormater() + @property + def cmd_resource(self): + if self.shadow_resource: + return self.shadow_resource + return self.resource + def get_client(self): return self.app.client_manager.neutron @@ -400,7 +416,6 @@ class CreateCommand(NeutronCommand, show.ShowOne): """ api = 'network' - resource = None log = None def get_parser(self, prog_name): @@ -424,7 +439,7 @@ def get_data(self, parsed_args): body = self.args2body(parsed_args) body[self.resource].update(_extra_values) obj_creator = getattr(neutron_client, - "create_%s" % self.resource) + "create_%s" % self.cmd_resource) data = obj_creator(body) self.format_output_data(data) info = self.resource in data and data[self.resource] or None @@ -441,7 +456,6 @@ class UpdateCommand(NeutronCommand): """ api = 'network' - resource = None log = None allow_names = True @@ -467,15 +481,18 @@ def run(self, parsed_args): body[self.resource] = _extra_values if not body[self.resource]: raise exceptions.CommandError( - _("Must specify new values to update %s") % self.resource) + _("Must specify new values to update %s") % + self.cmd_resource) if self.allow_names: _id = find_resourceid_by_name_or_id( - neutron_client, self.resource, parsed_args.id) + neutron_client, self.resource, parsed_args.id, + cmd_resource=self.cmd_resource) else: _id = find_resourceid_by_id( - neutron_client, self.resource, parsed_args.id) + neutron_client, self.resource, parsed_args.id, + self.cmd_resource) obj_updator = getattr(neutron_client, - "update_%s" % self.resource) + "update_%s" % self.cmd_resource) obj_updator(_id, body) print((_('Updated %(resource)s: %(id)s') % {'id': parsed_args.id, 'resource': self.resource}), @@ -489,7 +506,6 @@ class DeleteCommand(NeutronCommand): """ api = 'network' - resource = None log = None allow_names = True @@ -509,12 +525,16 @@ def run(self, parsed_args): neutron_client = self.get_client() neutron_client.format = parsed_args.request_format obj_deleter = getattr(neutron_client, - "delete_%s" % self.resource) + "delete_%s" % self.cmd_resource) if self.allow_names: - _id = find_resourceid_by_name_or_id(neutron_client, self.resource, - parsed_args.id) + params = {'cmd_resource': self.cmd_resource} + _id = find_resourceid_by_name_or_id(neutron_client, + self.resource, + parsed_args.id, + **params) else: _id = parsed_args.id + obj_deleter(_id) print((_('Deleted %(resource)s: %(id)s') % {'id': parsed_args.id, @@ -529,7 +549,6 @@ class ListCommand(NeutronCommand, lister.Lister): """ api = 'network' - resource = None log = None _formatters = {} list_columns = [] @@ -556,7 +575,8 @@ def args2search_opts(self, parsed_args): return search_opts def call_server(self, neutron_client, search_opts, parsed_args): - resource_plural = _get_resource_plural(self.resource, neutron_client) + resource_plural = _get_resource_plural(self.cmd_resource, + neutron_client) obj_lister = getattr(neutron_client, "list_%s" % resource_plural) data = obj_lister(**search_opts) return data @@ -628,7 +648,6 @@ class ShowCommand(NeutronCommand, show.ShowOne): """ api = 'network' - resource = None log = None allow_names = True @@ -656,11 +675,12 @@ def get_data(self, parsed_args): params = {'fields': parsed_args.fields} if self.allow_names: _id = find_resourceid_by_name_or_id(neutron_client, self.resource, - parsed_args.id) + parsed_args.id, + cmd_resource=self.cmd_resource) else: _id = parsed_args.id - obj_shower = getattr(neutron_client, "show_%s" % self.resource) + obj_shower = getattr(neutron_client, "show_%s" % self.cmd_resource) data = obj_shower(_id, **params) self.format_output_data(data) resource = data[self.resource] diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 9fd2d384c..e95d73770 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -163,7 +163,8 @@ class CLITestV20Base(base.BaseTestCase): test_id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' id_field = 'id' - def _find_resourceid(self, client, resource, name_or_id): + def _find_resourceid(self, client, resource, name_or_id, + cmd_resource=None): return name_or_id def _get_attr_metadata(self): @@ -198,11 +199,10 @@ def setUp(self, plurals={}): self._get_attr_metadata)) self.client = client.Client(token=TOKEN, endpoint_url=self.endurl) - def _test_create_resource(self, resource, cmd, - name, myid, args, - position_names, position_values, tenant_id=None, - tags=None, admin_state_up=True, extra_body=None, - **kwargs): + def _test_create_resource(self, resource, cmd, name, myid, args, + position_names, position_values, + tenant_id=None, tags=None, admin_state_up=True, + extra_body=None, cmd_resource=None, **kwargs): self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") cmd.get_client().MultipleTimes().AndReturn(self.client) @@ -213,6 +213,8 @@ def _test_create_resource(self, resource, cmd, 'policy_profile', 'ikepolicy', 'ipsecpolicy', 'metering_label', 'metering_label_rule', 'net_partition'] + if not cmd_resource: + cmd_resource = resource if (resource in non_admin_status_resources): body = {resource: {}, } else: @@ -234,7 +236,7 @@ def _test_create_resource(self, resource, cmd, self.client.format = self.format resstr = self.client.serialize(ress) # url method body - resource_plural = neutronV2_0._get_resource_plural(resource, + resource_plural = neutronV2_0._get_resource_plural(cmd_resource, self.client) path = getattr(self.client, resource_plural + "_path") # Work around for LP #1217791. XML deserializer called from @@ -259,15 +261,19 @@ def _test_create_resource(self, resource, cmd, if name: self.assertIn(name, _str) - def _test_list_columns(self, cmd, resources_collection, - resources_out, args=['-f', 'json']): + def _test_list_columns(self, cmd, resources, + resources_out, args=['-f', 'json'], + cmd_resources=None): self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") cmd.get_client().MultipleTimes().AndReturn(self.client) self.client.format = self.format + if not cmd_resources: + cmd_resources = resources + resstr = self.client.serialize(resources_out) - path = getattr(self.client, resources_collection + "_path") + path = getattr(self.client, cmd_resources + "_path") self.client.httpclient.request( end_url(path, format=self.format), 'GET', body=None, @@ -275,7 +281,7 @@ def _test_list_columns(self, cmd, resources_collection, 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr)) args.extend(['--request-format', self.format]) self.mox.ReplayAll() - cmd_parser = cmd.get_parser("list_" + resources_collection) + cmd_parser = cmd.get_parser("list_" + cmd_resources) shell.run_command(cmd, cmd_parser, args) self.mox.VerifyAll() self.mox.UnsetStubs() @@ -283,10 +289,12 @@ def _test_list_columns(self, cmd, resources_collection, def _test_list_resources(self, resources, cmd, detail=False, tags=[], fields_1=[], fields_2=[], page_size=None, sort_key=[], sort_dir=[], response_contents=None, - base_args=None, path=None): + base_args=None, path=None, cmd_resources=None): self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") cmd.get_client().MultipleTimes().AndReturn(self.client) + if not cmd_resources: + cmd_resources = resources if response_contents is None: contents = [{self.id_field: 'myid1', }, {self.id_field: 'myid2', }, ] @@ -358,7 +366,7 @@ def _test_list_resources(self, resources, cmd, detail=False, tags=[], query += '&' query += 'sort_dir=%s' % dir if path is None: - path = getattr(self.client, resources + "_path") + path = getattr(self.client, cmd_resources + "_path") self.client.httpclient.request( MyUrlComparator(end_url(path, query, format=self.format), self.client), @@ -367,7 +375,7 @@ def _test_list_resources(self, resources, cmd, detail=False, tags=[], headers=mox.ContainsKeyValue( 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr)) self.mox.ReplayAll() - cmd_parser = cmd.get_parser("list_" + resources) + cmd_parser = cmd.get_parser("list_" + cmd_resources) shell.run_command(cmd, cmd_parser, args) self.mox.VerifyAll() self.mox.UnsetStubs() @@ -376,11 +384,15 @@ def _test_list_resources(self, resources, cmd, detail=False, tags=[], self.assertIn('myid1', _str) return _str - def _test_list_resources_with_pagination(self, resources, cmd): + def _test_list_resources_with_pagination(self, resources, cmd, + cmd_resources=None): self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") cmd.get_client().MultipleTimes().AndReturn(self.client) - path = getattr(self.client, resources + "_path") + if not cmd_resources: + cmd_resources = resources + + path = getattr(self.client, cmd_resources + "_path") fake_query = "marker=myid2&limit=2" reses1 = {resources: [{'id': 'myid1', }, {'id': 'myid2', }], @@ -402,18 +414,22 @@ def _test_list_resources_with_pagination(self, resources, cmd): headers=mox.ContainsKeyValue( 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr2)) self.mox.ReplayAll() - cmd_parser = cmd.get_parser("list_" + resources) + cmd_parser = cmd.get_parser("list_" + cmd_resources) args = ['--request-format', self.format] shell.run_command(cmd, cmd_parser, args) self.mox.VerifyAll() self.mox.UnsetStubs() - def _test_update_resource(self, resource, cmd, myid, args, extrafields): + def _test_update_resource(self, resource, cmd, myid, args, extrafields, + cmd_resource=None): self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") cmd.get_client().MultipleTimes().AndReturn(self.client) + if not cmd_resource: + cmd_resource = resource + body = {resource: extrafields} - path = getattr(self.client, resource + "_path") + path = getattr(self.client, cmd_resource + "_path") self.client.format = self.format # Work around for LP #1217791. XML deserializer called from # MyComparator does not decodes XML string correctly. @@ -430,24 +446,28 @@ def _test_update_resource(self, resource, cmd, myid, args, extrafields): 'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), None)) args.extend(['--request-format', self.format]) self.mox.ReplayAll() - cmd_parser = cmd.get_parser("update_" + resource) + cmd_parser = cmd.get_parser("update_" + cmd_resource) shell.run_command(cmd, cmd_parser, args) self.mox.VerifyAll() self.mox.UnsetStubs() _str = self.fake_stdout.make_string() self.assertIn(myid, _str) - def _test_show_resource(self, resource, cmd, myid, args, fields=[]): + def _test_show_resource(self, resource, cmd, myid, args, fields=[], + cmd_resource=None): self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") cmd.get_client().MultipleTimes().AndReturn(self.client) + if not cmd_resource: + cmd_resource = resource + query = "&".join(["fields=%s" % field for field in fields]) expected_res = {resource: {self.id_field: myid, 'name': 'myname', }, } self.client.format = self.format resstr = self.client.serialize(expected_res) - path = getattr(self.client, resource + "_path") + path = getattr(self.client, cmd_resource + "_path") self.client.httpclient.request( end_url(path % myid, query, format=self.format), 'GET', body=None, @@ -455,7 +475,7 @@ def _test_show_resource(self, resource, cmd, myid, args, fields=[]): 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr)) args.extend(['--request-format', self.format]) self.mox.ReplayAll() - cmd_parser = cmd.get_parser("show_" + resource) + cmd_parser = cmd.get_parser("show_" + cmd_resource) shell.run_command(cmd, cmd_parser, args) self.mox.VerifyAll() self.mox.UnsetStubs() @@ -463,11 +483,14 @@ def _test_show_resource(self, resource, cmd, myid, args, fields=[]): self.assertIn(myid, _str) self.assertIn('myname', _str) - def _test_delete_resource(self, resource, cmd, myid, args): + def _test_delete_resource(self, resource, cmd, myid, args, + cmd_resource=None): self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") cmd.get_client().MultipleTimes().AndReturn(self.client) - path = getattr(self.client, resource + "_path") + if not cmd_resource: + cmd_resource = resource + path = getattr(self.client, cmd_resource + "_path") self.client.httpclient.request( end_url(path % myid, format=self.format), 'DELETE', body=None, @@ -475,7 +498,7 @@ def _test_delete_resource(self, resource, cmd, myid, args): 'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), None)) args.extend(['--request-format', self.format]) self.mox.ReplayAll() - cmd_parser = cmd.get_parser("delete_" + resource) + cmd_parser = cmd.get_parser("delete_" + cmd_resource) shell.run_command(cmd, cmd_parser, args) self.mox.VerifyAll() self.mox.UnsetStubs() @@ -483,11 +506,13 @@ def _test_delete_resource(self, resource, cmd, myid, args): self.assertIn(myid, _str) def _test_update_resource_action(self, resource, cmd, myid, action, args, - body, retval=None): + body, retval=None, cmd_resource=None): self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") cmd.get_client().MultipleTimes().AndReturn(self.client) - path = getattr(self.client, resource + "_path") + if not cmd_resource: + cmd_resource = resource + path = getattr(self.client, cmd_resource + "_path") path_action = '%s/%s' % (myid, action) self.client.httpclient.request( end_url(path % path_action, format=self.format), 'PUT', @@ -496,7 +521,7 @@ def _test_update_resource_action(self, resource, cmd, myid, action, args, 'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), retval)) args.extend(['--request-format', self.format]) self.mox.ReplayAll() - cmd_parser = cmd.get_parser("delete_" + resource) + cmd_parser = cmd.get_parser("delete_" + cmd_resource) shell.run_command(cmd, cmd_parser, args) self.mox.VerifyAll() self.mox.UnsetStubs() From 8f38b2e90566bbd62421edfae45cd54500a31042 Mon Sep 17 00:00:00 2001 From: liuweicai Date: Wed, 28 May 2014 10:07:43 +0800 Subject: [PATCH 062/845] Fix listing security group rules It was failing when there are too many security groups, since we're hitting a RequestURITooLong exception. Co-Authored-By: Vincent Untz Co-Authored-By: Ilya Shakhat Change-Id: If94b6a2ed6878efad1224e056ecf43e65e85e821 Closes-Bug: 1271462 --- neutronclient/neutron/v2_0/securitygroup.py | 29 +++++- .../tests/unit/test_cli20_securitygroup.py | 97 +++++++++++++++++++ 2 files changed, 123 insertions(+), 3 deletions(-) diff --git a/neutronclient/neutron/v2_0/securitygroup.py b/neutronclient/neutron/v2_0/securitygroup.py index f4d2d151e..14d25fa01 100644 --- a/neutronclient/neutron/v2_0/securitygroup.py +++ b/neutronclient/neutron/v2_0/securitygroup.py @@ -16,6 +16,7 @@ import argparse +from neutronclient.common import exceptions from neutronclient.neutron import v2_0 as neutronV20 from neutronclient.openstack.common.gettextutils import _ @@ -134,9 +135,31 @@ def extend_list(self, data, parsed_args): for rule in data: for key in self.replace_rules: sec_group_ids.add(rule[key]) - search_opts.update({"id": sec_group_ids}) - secgroups = neutron_client.list_security_groups(**search_opts) - secgroups = secgroups.get('security_groups', []) + sec_group_ids = list(sec_group_ids) + + def _get_sec_group_list(sec_group_ids): + search_opts['id'] = sec_group_ids + return neutron_client.list_security_groups( + **search_opts).get('security_groups', []) + + try: + secgroups = _get_sec_group_list(sec_group_ids) + except exceptions.RequestURITooLong as uri_len_exc: + # Length of a query filter on security group rule id + # id=& (with len(uuid)=36) + sec_group_id_filter_len = 40 + # The URI is too long because of too many sec_group_id filters + # Use the excess attribute of the exception to know how many + # sec_group_id filters can be inserted into a single request + sec_group_count = len(sec_group_ids) + max_size = ((sec_group_id_filter_len * sec_group_count) - + uri_len_exc.excess) + chunk_size = max_size / sec_group_id_filter_len + secgroups = [] + for i in range(0, sec_group_count, chunk_size): + secgroups.extend( + _get_sec_group_list(sec_group_ids[i: i + chunk_size])) + sg_dict = dict([(sg['id'], sg['name']) for sg in secgroups if sg['name']]) for rule in data: diff --git a/neutronclient/tests/unit/test_cli20_securitygroup.py b/neutronclient/tests/unit/test_cli20_securitygroup.py index e6127ff9b..554471d24 100644 --- a/neutronclient/tests/unit/test_cli20_securitygroup.py +++ b/neutronclient/tests/unit/test_cli20_securitygroup.py @@ -17,7 +17,10 @@ import sys from mox3 import mox +import six +from neutronclient.common import exceptions +from neutronclient.common import utils from neutronclient.neutron.v2_0 import securitygroup from neutronclient.tests.unit import test_cli20 @@ -186,6 +189,100 @@ def test_list_security_group_rules(self): mox.IgnoreArg()) self._test_list_resources(resources, cmd, True) + def _test_extend_list(self, mox_calls): + resources = "security_groups" + + data = [{'name': 'default', + 'security_group_id': 'secgroupid%02d' % i, + 'remote_group_id': 'remgroupid%02d' % i} + for i in range(10)] + + cmd = securitygroup.ListSecurityGroupRule( + test_cli20.MyApp(sys.stdout), None) + self.mox.StubOutWithMock(cmd, "get_client") + self.mox.StubOutWithMock(self.client.httpclient, "request") + + cmd.get_client().MultipleTimes().AndReturn(self.client) + path = getattr(self.client, resources + '_path') + mox_calls(path, data) + self.mox.ReplayAll() + known_args, _vs = cmd.get_parser( + 'list' + resources).parse_known_args() + + cmd.extend_list(data, known_args) + self.mox.VerifyAll() + self.mox.UnsetStubs() + + def _build_test_data(self, data, excess=0): + # Length of a query filter on security group rule id + # in these testcases, id='secgroupid%02d' (with len(id)=12) + sec_group_id_filter_len = 12 + + response = [] + replace_rules = {'security_group_id': 'security_group', + 'remote_group_id': 'remote_group'} + + search_opts = {'fields': ['id', 'name']} + sec_group_ids = set() + for rule in data: + for key in replace_rules: + sec_group_ids.add(rule[key]) + response.append({'id': rule[key], 'name': 'default'}) + sec_group_ids = list(sec_group_ids) + + result = [] + + sec_group_count = len(sec_group_ids) + max_size = ((sec_group_id_filter_len * sec_group_count) - excess) + chunk_size = max_size / sec_group_id_filter_len + + for i in range(0, sec_group_count, chunk_size): + search_opts['id'] = sec_group_ids[i: i + chunk_size] + params = utils.safe_encode_dict(search_opts) + resp_str = self.client.serialize({'security_groups': response}) + + result.append({ + 'filter': six.moves.urllib.parse.urlencode(params, doseq=1), + 'response': (test_cli20.MyResp(200), resp_str), + }) + + return result + + def test_extend_list(self): + def mox_calls(path, data): + responses = self._build_test_data(data) + self.client.httpclient.request( + test_cli20.MyUrlComparator(test_cli20.end_url( + path, responses[0]['filter']), self.client), + 'GET', + body=None, + headers=mox.ContainsKeyValue( + 'X-Auth-Token', test_cli20.TOKEN)).AndReturn( + responses[0]['response']) + + self._test_extend_list(mox_calls) + + def test_extend_list_exceed_max_uri_len(self): + def mox_calls(path, data): + # 1 char of extra URI len will cause a split in 2 requests + self.mox.StubOutWithMock(self.client, '_check_uri_length') + self.client._check_uri_length(mox.IgnoreArg()).AndRaise( + exceptions.RequestURITooLong(excess=1)) + responses = self._build_test_data(data, excess=1) + + for item in responses: + self.client._check_uri_length( + mox.IgnoreArg()).AndReturn(None) + self.client.httpclient.request( + test_cli20.end_url(path, item['filter']), + 'GET', + body=None, + headers=mox.ContainsKeyValue( + 'X-Auth-Token', test_cli20.TOKEN)).AndReturn( + item['response']) + + self._test_extend_list(mox_calls) + def test_list_security_group_rules_pagination(self): resources = "security_group_rules" cmd = securitygroup.ListSecurityGroupRule( From 9ee94153db0d96bb7ed995d49ed7d2f155be297e Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Mon, 4 Aug 2014 15:33:38 +0000 Subject: [PATCH 063/845] Updated from global requirements Change-Id: I1d4cf640d826e4d18025738511f94b789bb1b62a --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 25626cff2..6c00c26ae 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ cliff>=1.6.0 iso8601>=0.1.9 netaddr>=0.7.6 requests>=1.1 -python-keystoneclient>=0.9.0 +python-keystoneclient>=0.10.0 simplejson>=2.0.9 six>=1.7.0 Babel>=1.3 From 2203b013fb66808ef280eff0285318ce21d9bc67 Mon Sep 17 00:00:00 2001 From: Bradley Klein Date: Mon, 5 May 2014 15:00:03 -0400 Subject: [PATCH 064/845] Add keystone v3 auth support This change enables the neutron client to use the keystone v3 API (in addition to v2). This allows user domains and tenant/project domains to be specified. This is necessary because keystone v2 API is deprecated as of Icehouse. The keystone session object (and auth plugin) can now be specified and are required to use the keystone v3 API. The existing HTTPClient is retained for backward compatibility. See changes in shell.py for an example of how to construct the session and auth plugin. Implements: blueprint keystone-api-v3-support Change-Id: I9d0395d405b9fbe4db08ad3727f9413be7b82811 --- neutronclient/client.py | 146 +++++++++++- neutronclient/common/clientmanager.py | 15 +- neutronclient/neutron/client.py | 4 +- neutronclient/shell.py | 285 +++++++++++++++++++++--- neutronclient/tests/unit/test_auth.py | 274 ++++++++++++++++++----- neutronclient/tests/unit/test_shell.py | 294 ++++++++++++++++++++++++- neutronclient/tests/unit/test_ssl.py | 41 +++- neutronclient/v2_0/client.py | 8 +- test-requirements.txt | 1 + 9 files changed, 941 insertions(+), 127 deletions(-) diff --git a/neutronclient/client.py b/neutronclient/client.py index e41511f1d..8720f1fbf 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -22,6 +22,7 @@ import os from keystoneclient import access +from keystoneclient.auth.identity.base import BaseIdentityPlugin import requests from neutronclient.common import exceptions @@ -41,11 +42,25 @@ logging.getLogger("requests").setLevel(_requests_log_level) -class HTTPClient(object): - """Handles the REST calls and responses, include authn.""" +class NeutronClientMixin(object): USER_AGENT = 'python-neutronclient' + def get_status_code(self, response): + """Returns the integer status code from the response. + + Either a Webob.Response (used in testing) or requests.Response + is returned. + """ + if hasattr(response, 'status_int'): + return response.status_int + else: + return response.status_code + + +class HTTPClient(NeutronClientMixin): + """Handles the REST calls and responses, include authn.""" + def __init__(self, username=None, user_id=None, tenant_name=None, tenant_id=None, password=None, auth_url=None, @@ -265,13 +280,122 @@ def get_auth_info(self): 'auth_user_id': self.auth_user_id, 'endpoint_url': self.endpoint_url} - def get_status_code(self, response): - """Returns the integer status code from the response. - Either a Webob.Response (used in testing) or requests.Response - is returned. - """ - if hasattr(response, 'status_int'): - return response.status_int - else: - return response.status_code +class SessionClient(NeutronClientMixin): + + def __init__(self, + session, + auth, + interface=None, + service_type=None, + region_name=None): + + self.session = session + self.auth = auth + self.interface = interface + self.service_type = service_type + self.region_name = region_name + self.auth_token = None + self.endpoint_url = None + + def request(self, url, method, **kwargs): + kwargs.setdefault('user_agent', self.USER_AGENT) + kwargs.setdefault('auth', self.auth) + kwargs.setdefault('authenticated', False) + + try: + kwargs['data'] = kwargs.pop('body') + except KeyError: + pass + + endpoint_filter = kwargs.setdefault('endpoint_filter', {}) + endpoint_filter.setdefault('interface', self.interface) + endpoint_filter.setdefault('service_type', self.service_type) + endpoint_filter.setdefault('region_name', self.region_name) + + kwargs = utils.safe_encode_dict(kwargs) + resp = self.session.request(url, method, **kwargs) + return resp, resp.text + + def do_request(self, url, method, **kwargs): + kwargs.setdefault('authenticated', True) + return self.request(url, method, **kwargs) + + def authenticate(self): + # This method is provided for backward compatibility only. + # We only care about setting the service endpoint. + self.endpoint_url = self.session.get_endpoint( + self.auth, + service_type=self.service_type, + region_name=self.region_name, + interface=self.interface) + + def authenticate_and_fetch_endpoint_url(self): + # This method is provided for backward compatibility only. + # We only care about setting the service endpoint. + self.authenticate() + + def get_auth_info(self): + # This method is provided for backward compatibility only. + if not isinstance(self.auth, BaseIdentityPlugin): + msg = ('Auth info not available. Auth plugin is not an identity ' + 'auth plugin.') + raise exceptions.NeutronClientException(message=msg) + access_info = self.auth.get_access(self.session) + endpoint_url = self.auth.get_endpoint(self.session, + service_type=self.service_type, + region_name=self.region_name, + interface=self.interface) + return {'auth_token': access_info.auth_token, + 'auth_tenant_id': access_info.tenant_id, + 'auth_user_id': access_info.user_id, + 'endpoint_url': endpoint_url} + + +# FIXME(bklei): Should refactor this to use kwargs and only +# explicitly list arguments that are not None. +def construct_http_client(username=None, + user_id=None, + tenant_name=None, + tenant_id=None, + password=None, + auth_url=None, + token=None, + region_name=None, + timeout=None, + endpoint_url=None, + insecure=False, + endpoint_type='publicURL', + log_credentials=None, + auth_strategy='keystone', + ca_cert=None, + service_type='network', + session=None, + auth=None): + + if session: + return SessionClient(session=session, + auth=auth, + interface=endpoint_type, + service_type=service_type, + region_name=region_name) + else: + # FIXME(bklei): username and password are now optional. Need + # to test that they were provided in this mode. Should also + # refactor to use kwargs. + return HTTPClient(username=username, + password=password, + tenant_id=tenant_id, + tenant_name=tenant_name, + user_id=user_id, + auth_url=auth_url, + token=token, + endpoint_url=endpoint_url, + insecure=insecure, + timeout=timeout, + region_name=region_name, + endpoint_type=endpoint_type, + service_type=service_type, + ca_cert=ca_cert, + log_credentials=log_credentials, + auth_strategy=auth_strategy) diff --git a/neutronclient/common/clientmanager.py b/neutronclient/common/clientmanager.py index c159e05d3..283bc77bf 100644 --- a/neutronclient/common/clientmanager.py +++ b/neutronclient/common/clientmanager.py @@ -66,7 +66,9 @@ def __init__(self, token=None, url=None, service_type=None, timeout=None, retries=0, - raise_errors=True + raise_errors=True, + session=None, + auth=None, ): self._token = token self._url = url @@ -88,10 +90,13 @@ def __init__(self, token=None, url=None, self._timeout = timeout self._retries = retries self._raise_errors = raise_errors + self._session = session + self._auth = auth + return def initialize(self): if not self._url: - httpclient = client.HTTPClient( + httpclient = client.construct_http_client( username=self._username, user_id=self._user_id, tenant_name=self._tenant_name, @@ -103,8 +108,10 @@ def initialize(self): endpoint_type=self._endpoint_type, insecure=self._insecure, ca_cert=self._ca_cert, - log_credentials=self._log_credentials, - timeout=self._timeout) + timeout=self._timeout, + session=self._session, + auth=self._auth, + log_credentials=self._log_credentials) httpclient.authenticate() # Populate other password flow attributes self._token = httpclient.auth_token diff --git a/neutronclient/neutron/client.py b/neutronclient/neutron/client.py index 2dd40dea5..df7ef2996 100644 --- a/neutronclient/neutron/client.py +++ b/neutronclient/neutron/client.py @@ -48,7 +48,9 @@ def make_client(instance): insecure=instance._insecure, ca_cert=instance._ca_cert, retries=instance._retries, - raise_errors=instance._raise_errors) + raise_errors=instance._raise_errors, + session=instance._session, + auth=instance._auth) return client else: raise exceptions.UnsupportedVersion(_("API version %s is not " diff --git a/neutronclient/shell.py b/neutronclient/shell.py index a3e51080b..ce7bed9c9 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -25,6 +25,13 @@ import os import sys +from keystoneclient.auth.identity import v2 as v2_auth +from keystoneclient.auth.identity import v3 as v3_auth +from keystoneclient import discover +from keystoneclient.openstack.common.apiclient import exceptions as ks_exc +from keystoneclient import session +import six.moves.urllib.parse as urlparse + from cliff import app from cliff import commandmanager @@ -386,13 +393,50 @@ def build_option_parser(self, description, version): default=0, help=_("How many times the request to the Neutron server should " "be retried if it fails.")) - # Global arguments + # FIXME(bklei): this method should come from python-keystoneclient + self._append_global_identity_args(parser) + + return parser + + def _append_global_identity_args(self, parser): + # FIXME(bklei): these are global identity (Keystone) arguments which + # should be consistent and shared by all service clients. Therefore, + # they should be provided by python-keystoneclient. We will need to + # refactor this code once this functionality is available in + # python-keystoneclient. + # + # Note: At that time we'll need to decide if we can just abandon + # the deprecated args (--service-type and --endpoint-type). + + parser.add_argument( + '--os-service-type', metavar='', + default=env('OS_NETWORK_SERVICE_TYPE', default='network'), + help=_('Defaults to env[OS_NETWORK_SERVICE_TYPE] or network.')) + + parser.add_argument( + '--os-endpoint-type', metavar='', + default=env('OS_ENDPOINT_TYPE', default='publicURL'), + help=_('Defaults to env[OS_ENDPOINT_TYPE] or publicURL.')) + + # FIXME(bklei): --service-type is deprecated but kept in for + # backward compatibility. + parser.add_argument( + '--service-type', metavar='', + default=env('OS_NETWORK_SERVICE_TYPE', default='network'), + help=_('DEPRECATED! Use --os-service-type.')) + + # FIXME(bklei): --endpoint-type is deprecated but kept in for + # backward compatibility. + parser.add_argument( + '--endpoint-type', metavar='', + default=env('OS_ENDPOINT_TYPE', default='publicURL'), + help=_('DEPRECATED! Use --os-endpoint-type.')) + parser.add_argument( '--os-auth-strategy', metavar='', default=env('OS_AUTH_STRATEGY', default='keystone'), - help=_('Authentication strategy, defaults to ' - 'env[OS_AUTH_STRATEGY] or keystone. For now, any ' - 'other value will disable the authentication.')) + help=_('DEPRECATED! Only keystone is supported.')) + parser.add_argument( '--os_auth_strategy', help=argparse.SUPPRESS) @@ -405,20 +449,39 @@ def build_option_parser(self, description, version): '--os_auth_url', help=argparse.SUPPRESS) - parser.add_argument( + project_name_group = parser.add_mutually_exclusive_group() + project_name_group.add_argument( '--os-tenant-name', metavar='', default=env('OS_TENANT_NAME'), help=_('Authentication tenant name, defaults to ' 'env[OS_TENANT_NAME].')) + project_name_group.add_argument( + '--os-project-name', + metavar='', + default=utils.env('OS_PROJECT_NAME'), + help='Another way to specify tenant name. ' + 'This option is mutually exclusive with ' + ' --os-tenant-name. ' + 'Defaults to env[OS_PROJECT_NAME].') + parser.add_argument( '--os_tenant_name', help=argparse.SUPPRESS) - parser.add_argument( + project_id_group = parser.add_mutually_exclusive_group() + project_id_group.add_argument( '--os-tenant-id', metavar='', default=env('OS_TENANT_ID'), help=_('Authentication tenant ID, defaults to ' 'env[OS_TENANT_ID].')) + project_id_group.add_argument( + '--os-project-id', + metavar='', + default=utils.env('OS_PROJECT_ID'), + help='Another way to specify tenant ID. ' + 'This option is mutually exclusive with ' + ' --os-tenant-id. ' + 'Defaults to env[OS_PROJECT_ID].') parser.add_argument( '--os-username', metavar='', @@ -433,6 +496,78 @@ def build_option_parser(self, description, version): default=env('OS_USER_ID'), help=_('Authentication user ID (Env: OS_USER_ID)')) + parser.add_argument( + '--os_user_id', + help=argparse.SUPPRESS) + + parser.add_argument( + '--os-user-domain-id', + metavar='', + default=utils.env('OS_USER_DOMAIN_ID'), + help='OpenStack user domain ID. ' + 'Defaults to env[OS_USER_DOMAIN_ID].') + + parser.add_argument( + '--os_user_domain_id', + help=argparse.SUPPRESS) + + parser.add_argument( + '--os-user-domain-name', + metavar='', + default=utils.env('OS_USER_DOMAIN_NAME'), + help='OpenStack user domain name. ' + 'Defaults to env[OS_USER_DOMAIN_NAME].') + + parser.add_argument( + '--os_user_domain_name', + help=argparse.SUPPRESS) + + parser.add_argument( + '--os_project_id', + help=argparse.SUPPRESS) + + parser.add_argument( + '--os_project_name', + help=argparse.SUPPRESS) + + parser.add_argument( + '--os-project-domain-id', + metavar='', + default=utils.env('OS_PROJECT_DOMAIN_ID'), + help='Defaults to env[OS_PROJECT_DOMAIN_ID].') + + parser.add_argument( + '--os-project-domain-name', + metavar='', + default=utils.env('OS_PROJECT_DOMAIN_NAME'), + help='Defaults to env[OS_PROJECT_DOMAIN_NAME].') + + parser.add_argument( + '--os-cert', + metavar='', + default=utils.env('OS_CERT'), + help=_("Path of certificate file to use in SSL " + "connection. This file can optionally be " + "prepended with the private key. Defaults " + "to env[OS_CERT]")) + + parser.add_argument( + '--os-cacert', + metavar='', + default=env('OS_CACERT', default=None), + help=_("Specify a CA bundle file to use in " + "verifying a TLS (https) server certificate. " + "Defaults to env[OS_CACERT]")) + + parser.add_argument( + '--os-key', + metavar='', + default=utils.env('OS_KEY'), + help=_("Path of client key to use in SSL " + "connection. This option is not necessary " + "if your key is prepended to your certificate " + "file. Defaults to env[OS_KEY]")) + parser.add_argument( '--os-password', metavar='', default=utils.env('OS_PASSWORD'), @@ -458,22 +593,12 @@ def build_option_parser(self, description, version): '--os_token', help=argparse.SUPPRESS) - parser.add_argument( - '--service-type', metavar='', - default=env('OS_NETWORK_SERVICE_TYPE', default='network'), - help=_('Defaults to env[OS_NETWORK_SERVICE_TYPE] or network.')) - parser.add_argument( '--timeout', metavar='', default=env('OS_NETWORK_TIMEOUT', default=None), type=float, help=_('Timeout in seconds to wait for an HTTP response. Defaults ' 'to env[OS_NETWORK_TIMEOUT] or None if not specified.')) - parser.add_argument( - '--endpoint-type', metavar='', - default=env('OS_ENDPOINT_TYPE', default='publicURL'), - help=_('Defaults to env[OS_ENDPOINT_TYPE] or publicURL.')) - parser.add_argument( '--os-url', metavar='', default=env('OS_URL'), @@ -482,14 +607,6 @@ def build_option_parser(self, description, version): '--os_url', help=argparse.SUPPRESS) - parser.add_argument( - '--os-cacert', - metavar='', - default=env('OS_CACERT', default=None), - help=_("Specify a CA bundle file to use in " - "verifying a TLS (https) server certificate. " - "Defaults to env[OS_CACERT].")) - parser.add_argument( '--insecure', action='store_true', @@ -499,8 +616,6 @@ def build_option_parser(self, description, version): "not be verified against any certificate authorities. " "This option should be used with caution.")) - return parser - def _bash_completion(self): """Prints all of the commands and options for bash-completion.""" commands = set() @@ -622,6 +737,13 @@ def authenticate_user(self): else: # Validate password flow auth + project_info = (self.options.os_tenant_name or + self.options.os_tenant_id or + (self.options.os_project_name and + (self.options.project_domain_name or + self.options.project_domain_id)) or + self.options.os_project_id) + if (not self.options.os_username and not self.options.os_user_id): raise exc.CommandError( @@ -634,12 +756,19 @@ def authenticate_user(self): _("You must provide a password via" " either --os-password or env[OS_PASSWORD]")) - if (not self.options.os_tenant_name - and not self.options.os_tenant_id): + if (not project_info): + # tenent is deprecated in Keystone v3. Use the latest + # terminology instead. raise exc.CommandError( - _("You must provide a tenant_name or tenant_id via" - " --os-tenant-name, env[OS_TENANT_NAME]" - " --os-tenant-id, or via env[OS_TENANT_ID]")) + _("You must provide a project_id or project_name (" + "with project_domain_name or project_domain_id) " + "via " + " --os-project-id (env[OS_PROJECT_ID])" + " --os-project-name (env[OS_PROJECT_NAME])," + " --os-project-domain-id " + "(env[OS_PROJECT_DOMAIN_ID])" + " --os-project-domain-name " + "(env[OS_PROJECT_DOMAIN_NAME])")) if not self.options.os_auth_url: raise exc.CommandError( @@ -651,6 +780,8 @@ def authenticate_user(self): _("You must provide a service URL via" " either --os-url or env[OS_URL]")) + auth_session = self._get_keystone_session() + self.client_manager = clientmanager.ClientManager( token=self.options.os_token, url=self.options.os_url, @@ -663,13 +794,18 @@ def authenticate_user(self): region_name=self.options.os_region_name, api_version=self.api_version, auth_strategy=self.options.os_auth_strategy, - service_type=self.options.service_type, - endpoint_type=self.options.endpoint_type, + # FIXME (bklei) honor deprecated service_type and + # endpoint type until they are removed + service_type=self.options.os_service_type or + self.options.service_type, + endpoint_type=self.options.os_endpoint_type or self.endpoint_type, insecure=self.options.insecure, ca_cert=self.options.os_cacert, timeout=self.options.timeout, retries=self.options.retries, raise_errors=False, + session=auth_session, + auth=auth_session.auth, log_credentials=True) return @@ -721,6 +857,89 @@ def configure_logging(self): root_logger.addHandler(console) return + def get_v2_auth(self, v2_auth_url): + return v2_auth.Password( + v2_auth_url, + username=self.options.os_username, + password=self.options.os_password, + tenant_id=self.options.os_tenant_id, + tenant_name=self.options.os_tenant_name) + + def get_v3_auth(self, v3_auth_url): + project_id = self.options.os_project_id or self.options.os_tenant_id + project_name = (self.options.os_project_name or + self.options.os_tenant_name) + + return v3_auth.Password( + v3_auth_url, + username=self.options.os_username, + password=self.options.os_password, + user_id=self.options.os_user_id, + user_domain_name=self.options.os_user_domain_name, + user_domain_id=self.options.os_user_domain_id, + project_id=project_id, + project_name=project_name, + project_domain_name=self.options.os_project_domain_name, + project_domain_id=self.options.os_project_domain_id + ) + + def _discover_auth_versions(self, session, auth_url): + # discover the API versions the server is supporting base on the + # given URL + try: + ks_discover = discover.Discover(session=session, auth_url=auth_url) + return (ks_discover.url_for('2.0'), ks_discover.url_for('3.0')) + except ks_exc.ClientException: + # Identity service may not support discover API version. + # Lets try to figure out the API version from the original URL. + url_parts = urlparse.urlparse(auth_url) + (scheme, netloc, path, params, query, fragment) = url_parts + path = path.lower() + if path.startswith('/v3'): + return (None, auth_url) + elif path.startswith('/v2'): + return (auth_url, None) + else: + # not enough information to determine the auth version + msg = _('Unable to determine the Keystone version ' + 'to authenticate with using the given ' + 'auth_url. Identity service may not support API ' + 'version discovery. Please provide a versioned ' + 'auth_url instead.') + raise exc.CommandError(msg) + + def _get_keystone_session(self): + # first create a Keystone session + cacert = self.options.os_cacert or None + cert = self.options.os_cert or None + key = self.options.os_key or None + insecure = self.options.insecure or False + ks_session = session.Session.construct(dict(cacert=cacert, + cert=cert, + key=key, + insecure=insecure)) + # discover the supported keystone versions using the given url + (v2_auth_url, v3_auth_url) = self._discover_auth_versions( + session=ks_session, + auth_url=self.options.os_auth_url) + + # Determine which authentication plugin to use. First inspect the + # auth_url to see the supported version. If both v3 and v2 are + # supported, then use the highest version if possible. + user_domain_name = self.options.os_user_domain_name or None + user_domain_id = self.options.os_user_domain_id or None + project_domain_name = self.options.os_project_domain_name or None + project_domain_id = self.options.os_project_domain_id or None + domain_info = (user_domain_name or user_domain_id or + project_domain_name or project_domain_id) + + if (v2_auth_url and not domain_info) or not v3_auth_url: + ks_session.auth = self.get_v2_auth(v2_auth_url) + else: + ks_session.auth = self.get_v3_auth(v3_auth_url) + + return ks_session + def main(argv=sys.argv[1:]): try: diff --git a/neutronclient/tests/unit/test_auth.py b/neutronclient/tests/unit/test_auth.py index 34ef71d4e..537236a60 100644 --- a/neutronclient/tests/unit/test_auth.py +++ b/neutronclient/tests/unit/test_auth.py @@ -14,18 +14,25 @@ # under the License. # -import copy import json import uuid -from keystoneclient import exceptions as k_exceptions +import httpretty from mox3 import mox import requests import testtools +from keystoneclient.auth.identity import v2 as ks_v2_auth +from keystoneclient.auth.identity import v3 as ks_v3_auth +from keystoneclient import exceptions as ks_exceptions +from keystoneclient.fixture import v2 as ks_v2_fixture +from keystoneclient.fixture import v3 as ks_v3_fixture +from keystoneclient import session + from neutronclient import client from neutronclient.common import exceptions from neutronclient.common import utils +from neutronclient.openstack.common import jsonutils USERNAME = 'testuser' @@ -33,11 +40,14 @@ TENANT_NAME = 'testtenant' TENANT_ID = 'testtenant_id' PASSWORD = 'password' -AUTH_URL = 'authurl' ENDPOINT_URL = 'localurl' +PUBLIC_ENDPOINT_URL = 'public_%s' % ENDPOINT_URL +ADMIN_ENDPOINT_URL = 'admin_%s' % ENDPOINT_URL +INTERNAL_ENDPOINT_URL = 'internal_%s' % ENDPOINT_URL ENDPOINT_OVERRIDE = 'otherurl' TOKEN = 'tokentoken' -REGION = 'RegionTest' +TOKENID = uuid.uuid4().hex +REGION = 'RegionOne' NOAUTH = 'noauth' KS_TOKEN_RESULT = { @@ -69,6 +79,55 @@ }] } +BASE_HOST = 'http://keystone.example.com' +BASE_URL = "%s:5000/" % BASE_HOST +UPDATED = '2013-03-06T00:00:00Z' + +# FIXME (bklei): A future release of keystoneclient will support +# a discovery fixture which can replace these constants and clean +# this up. +V2_URL = "%sv2.0" % BASE_URL +V2_DESCRIBED_BY_HTML = {'href': 'http://docs.openstack.org/api/' + 'openstack-identity-service/2.0/content/', + 'rel': 'describedby', + 'type': 'text/html'} + +V2_DESCRIBED_BY_PDF = {'href': 'http://docs.openstack.org/api/openstack-ident' + 'ity-service/2.0/identity-dev-guide-2.0.pdf', + 'rel': 'describedby', + 'type': 'application/pdf'} + +V2_VERSION = {'id': 'v2.0', + 'links': [{'href': V2_URL, 'rel': 'self'}, + V2_DESCRIBED_BY_HTML, V2_DESCRIBED_BY_PDF], + 'status': 'stable', + 'updated': UPDATED} + +V3_URL = "%sv3" % BASE_URL +V3_MEDIA_TYPES = [{'base': 'application/json', + 'type': 'application/vnd.openstack.identity-v3+json'}, + {'base': 'application/xml', + 'type': 'application/vnd.openstack.identity-v3+xml'}] + +V3_VERSION = {'id': 'v3.0', + 'links': [{'href': V3_URL, 'rel': 'self'}], + 'media-types': V3_MEDIA_TYPES, + 'status': 'stable', + 'updated': UPDATED} + + +def _create_version_entry(version): + return jsonutils.dumps({'version': version}) + + +def _create_version_list(versions): + return jsonutils.dumps({'versions': {'values': versions}}) + + +V3_VERSION_LIST = _create_version_list([V3_VERSION, V2_VERSION]) +V3_VERSION_ENTRY = _create_version_entry(V3_VERSION) +V2_VERSION_ENTRY = _create_version_entry(V2_VERSION) + def get_response(status_code, headers=None): response = mox.Mox().CreateMock(requests.Response) @@ -77,6 +136,49 @@ def get_response(status_code, headers=None): return response +def setup_keystone_v2(): + v2_token = ks_v2_fixture.Token(token_id=TOKENID) + service = v2_token.add_service('network') + service.add_endpoint(PUBLIC_ENDPOINT_URL, region=REGION) + + httpretty.register_uri(httpretty.POST, + '%s/tokens' % (V2_URL), + body=json.dumps(v2_token)) + + auth_session = session.Session() + auth_plugin = ks_v2_auth.Password(V2_URL, 'xx', 'xx') + return auth_session, auth_plugin + + +def setup_keystone_v3(): + httpretty.register_uri(httpretty.GET, + V3_URL, + body=V3_VERSION_ENTRY) + + v3_token = ks_v3_fixture.Token() + service = v3_token.add_service('network') + service.add_standard_endpoints(public=PUBLIC_ENDPOINT_URL, + admin=ADMIN_ENDPOINT_URL, + internal=INTERNAL_ENDPOINT_URL, + region=REGION) + + httpretty.register_uri(httpretty.POST, + '%s/auth/tokens' % (V3_URL), + body=json.dumps(v3_token), + adding_headers={'X-Subject-Token': TOKENID}) + + auth_session = session.Session() + auth_plugin = ks_v3_auth.Password(V3_URL, + username='xx', + user_id='xx', + user_domain_name='xx', + user_domain_id='xx') + return auth_session, auth_plugin + + +AUTH_URL = V2_URL + + class CLITestAuthNoAuth(testtools.TestCase): def setUp(self): @@ -109,20 +211,18 @@ def test_get_noauth(self): class CLITestAuthKeystone(testtools.TestCase): - # Auth Body expected - auth_body = ('{"auth": {"tenantName": "testtenant", ' - '"passwordCredentials": ' - '{"username": "testuser", "password": "password"}}}') - def setUp(self): """Prepare the test environment.""" super(CLITestAuthKeystone, self).setUp() self.mox = mox.Mox() - self.client = client.HTTPClient(username=USERNAME, - tenant_name=TENANT_NAME, - password=PASSWORD, - auth_url=AUTH_URL, - region_name=REGION) + + self.client = client.construct_http_client( + username=USERNAME, + tenant_name=TENANT_NAME, + password=PASSWORD, + auth_url=AUTH_URL, + region_name=REGION) + self.addCleanup(self.mox.VerifyAll) self.addCleanup(self.mox.UnsetStubs) @@ -142,24 +242,30 @@ def test_reused_token_get_auth_info(self): 'endpoint_url': self.client.endpoint_url} self.assertEqual(client_.get_auth_info(), expected) + @httpretty.activate def test_get_token(self): - self.mox.StubOutWithMock(self.client, "request") + auth_session, auth_plugin = setup_keystone_v2() + self.client = client.construct_http_client( + username=USERNAME, + tenant_name=TENANT_NAME, + password=PASSWORD, + auth_url=AUTH_URL, + region_name=REGION, + session=auth_session, + auth=auth_plugin) + + self.mox.StubOutWithMock(self.client, "request") res200 = get_response(200) self.client.request( - AUTH_URL + '/tokens', 'POST', - body=self.auth_body, headers=mox.IsA(dict) - ).AndReturn((res200, json.dumps(KS_TOKEN_RESULT))) - self.client.request( - mox.StrContains(ENDPOINT_URL + '/resource'), 'GET', - headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN) + '/resource', 'GET', + authenticated=True ).AndReturn((res200, '')) + self.mox.ReplayAll() self.client.do_request('/resource', 'GET') - self.assertEqual(self.client.endpoint_url, ENDPOINT_URL) - self.assertEqual(self.client.auth_token, TOKEN) def test_refresh_token(self): self.mox.StubOutWithMock(self.client, "request") @@ -292,53 +398,55 @@ def test_get_endpoint_url_failed(self): self.mox.ReplayAll() self.client.do_request('/resource', 'GET') + @httpretty.activate def test_endpoint_type(self): - resources = copy.deepcopy(KS_TOKEN_RESULT) - endpoints = resources['access']['serviceCatalog'][0]['endpoints'][0] - endpoints['internalURL'] = 'internal' - endpoints['adminURL'] = 'admin' - endpoints['publicURL'] = 'public' + auth_session, auth_plugin = setup_keystone_v3() # Test default behavior is to choose public. - self.client = client.HTTPClient( + self.client = client.construct_http_client( username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD, - auth_url=AUTH_URL, region_name=REGION) + auth_url=AUTH_URL, region_name=REGION, + session=auth_session, auth=auth_plugin) - self.client._extract_service_catalog(resources) - self.assertEqual(self.client.endpoint_url, 'public') + self.client.authenticate() + self.assertEqual(self.client.endpoint_url, PUBLIC_ENDPOINT_URL) # Test admin url - self.client = client.HTTPClient( + self.client = client.construct_http_client( username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD, - auth_url=AUTH_URL, region_name=REGION, endpoint_type='adminURL') + auth_url=AUTH_URL, region_name=REGION, endpoint_type='adminURL', + session=auth_session, auth=auth_plugin) - self.client._extract_service_catalog(resources) - self.assertEqual(self.client.endpoint_url, 'admin') + self.client.authenticate() + self.assertEqual(self.client.endpoint_url, ADMIN_ENDPOINT_URL) # Test public url - self.client = client.HTTPClient( + self.client = client.construct_http_client( username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD, - auth_url=AUTH_URL, region_name=REGION, endpoint_type='publicURL') + auth_url=AUTH_URL, region_name=REGION, endpoint_type='publicURL', + session=auth_session, auth=auth_plugin) - self.client._extract_service_catalog(resources) - self.assertEqual(self.client.endpoint_url, 'public') + self.client.authenticate() + self.assertEqual(self.client.endpoint_url, PUBLIC_ENDPOINT_URL) # Test internal url - self.client = client.HTTPClient( + self.client = client.construct_http_client( username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD, - auth_url=AUTH_URL, region_name=REGION, endpoint_type='internalURL') + auth_url=AUTH_URL, region_name=REGION, endpoint_type='internalURL', + session=auth_session, auth=auth_plugin) - self.client._extract_service_catalog(resources) - self.assertEqual(self.client.endpoint_url, 'internal') + self.client.authenticate() + self.assertEqual(self.client.endpoint_url, INTERNAL_ENDPOINT_URL) # Test url that isn't found in the service catalog - self.client = client.HTTPClient( + self.client = client.construct_http_client( username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD, - auth_url=AUTH_URL, region_name=REGION, endpoint_type='privateURL') + auth_url=AUTH_URL, region_name=REGION, endpoint_type='privateURL', + session=auth_session, auth=auth_plugin) - self.assertRaises(k_exceptions.EndpointNotFound, - self.client._extract_service_catalog, - resources) + self.assertRaises( + ks_exceptions.EndpointNotFound, + self.client.authenticate) def test_strip_credentials_from_log(self): def verify_no_credentials(kwargs): @@ -370,11 +478,6 @@ def verify_credentials(body): class CLITestAuthKeystoneWithId(CLITestAuthKeystone): - # Auth Body expected - auth_body = ('{"auth": {"passwordCredentials": ' - '{"password": "password", "userId": "testuser_id"}, ' - '"tenantId": "testtenant_id"}}') - def setUp(self): """Prepare the test environment.""" super(CLITestAuthKeystoneWithId, self).setUp() @@ -387,11 +490,6 @@ def setUp(self): class CLITestAuthKeystoneWithIdandName(CLITestAuthKeystone): - # Auth Body expected - auth_body = ('{"auth": {"passwordCredentials": ' - '{"password": "password", "userId": "testuser_id"}, ' - '"tenantId": "testtenant_id"}}') - def setUp(self): """Prepare the test environment.""" super(CLITestAuthKeystoneWithIdandName, self).setUp() @@ -402,3 +500,61 @@ def setUp(self): password=PASSWORD, auth_url=AUTH_URL, region_name=REGION) + + +class TestKeystoneClientVersions(testtools.TestCase): + + def setUp(self): + """Prepare the test environment.""" + super(TestKeystoneClientVersions, self).setUp() + self.mox = mox.Mox() + self.addCleanup(self.mox.VerifyAll) + self.addCleanup(self.mox.UnsetStubs) + + @httpretty.activate + def test_v2_auth(self): + auth_session, auth_plugin = setup_keystone_v2() + res200 = get_response(200) + + self.client = client.construct_http_client( + username=USERNAME, + tenant_name=TENANT_NAME, + password=PASSWORD, + auth_url=AUTH_URL, + region_name=REGION, + session=auth_session, + auth=auth_plugin) + + self.mox.StubOutWithMock(self.client, "request") + + self.client.request( + '/resource', 'GET', + authenticated=True + ).AndReturn((res200, '')) + + self.mox.ReplayAll() + self.client.do_request('/resource', 'GET') + + @httpretty.activate + def test_v3_auth(self): + auth_session, auth_plugin = setup_keystone_v3() + res200 = get_response(200) + + self.client = client.construct_http_client( + user_id=USER_ID, + tenant_id=TENANT_ID, + password=PASSWORD, + auth_url=V3_URL, + region_name=REGION, + session=auth_session, + auth=auth_plugin) + + self.mox.StubOutWithMock(self.client, "request") + + self.client.request( + '/resource', 'GET', + authenticated=True + ).AndReturn((res200, '')) + + self.mox.ReplayAll() + self.client.do_request('/resource', 'GET') diff --git a/neutronclient/tests/unit/test_shell.py b/neutronclient/tests/unit/test_shell.py index 0324f1464..3eb372b74 100644 --- a/neutronclient/tests/unit/test_shell.py +++ b/neutronclient/tests/unit/test_shell.py @@ -20,13 +20,19 @@ import sys import fixtures +import httpretty from mox3 import mox import six import testtools from testtools import matchers +from keystoneclient.auth.identity import v2 as v2_auth +from keystoneclient.auth.identity import v3 as v3_auth +from keystoneclient import session + from neutronclient.common import clientmanager from neutronclient import shell as openstack_shell +from neutronclient.tests.unit import test_auth as auth DEFAULT_USERNAME = 'username' @@ -91,6 +97,14 @@ def test_help(self): matchers.MatchesRegex(required)) self.assertFalse(stderr) + def test_bash_completion(self): + required = '.*os_user_domain_id.*' + bash_completion, stderr = self.shell('bash-completion') + self.assertThat( + bash_completion, + matchers.MatchesRegex(required)) + self.assertFalse(stderr) + def test_help_on_subcommand(self): required = [ '.*?^usage: .* quota-list'] @@ -116,27 +130,287 @@ def test_unknown_auth_strategy(self): self.assertEqual('You must provide a service URL via ' 'either --os-url or env[OS_URL]', stderr.strip()) + @httpretty.activate def test_auth(self): - #import pdb; pdb.set_trace() + # emulate Keystone version discovery + httpretty.register_uri(httpretty.GET, + auth.V3_URL, + body=auth.V3_VERSION_ENTRY) + + neutron_shell = openstack_shell.NeutronShell('2.0') + self.addCleanup(self.mox.UnsetStubs) + self.mox.StubOutWithMock(clientmanager.ClientManager, '__init__') + self.mox.StubOutWithMock(neutron_shell, 'run_subcommand') + clientmanager.ClientManager.__init__( + token='', url='', auth_url=auth.V3_URL, + tenant_name='test', tenant_id='tenant_id', + username='test', user_id='', + password='test', region_name='', api_version={'network': '2.0'}, + auth_strategy='keystone', service_type='network', + endpoint_type='publicURL', insecure=False, ca_cert=None, + timeout=None, + raise_errors=False, + retries=0, + auth=mox.IsA(v3_auth.Password), + session=mox.IsA(session.Session), + log_credentials=True) + neutron_shell.run_subcommand(['quota-list']) + self.mox.ReplayAll() + cmdline = ('--os-username test ' + '--os-password test ' + '--os-tenant-name test ' + '--os-auth-url %s ' + '--os-auth-strategy keystone quota-list' + % auth.V3_URL) + neutron_shell.run(cmdline.split()) + self.mox.VerifyAll() + + @httpretty.activate + def test_auth_cert_and_key(self): + # emulate Keystone version discovery + httpretty.register_uri(httpretty.GET, + auth.V3_URL, + body=auth.V3_VERSION_ENTRY) + neutron_shell = openstack_shell.NeutronShell('2.0') self.addCleanup(self.mox.UnsetStubs) self.mox.StubOutWithMock(clientmanager.ClientManager, '__init__') self.mox.StubOutWithMock(neutron_shell, 'run_subcommand') clientmanager.ClientManager.__init__( - token='', url='', auth_url='http://127.0.0.1:5000/', + token='', url='', auth_url=auth.V3_URL, tenant_name='test', tenant_id='tenant_id', username='test', user_id='', password='test', region_name='', api_version={'network': '2.0'}, auth_strategy='keystone', service_type='network', + raise_errors=False, endpoint_type='publicURL', insecure=False, ca_cert=None, retries=0, - raise_errors=False, log_credentials=True, timeout=None) + timeout=None, + auth=mox.IsA(v3_auth.Password), + session=mox.IsA(session.Session), + log_credentials=True) + neutron_shell.run_subcommand(['quota-list']) + self.mox.ReplayAll() + cmdline = ('--os-username test ' + '--os-password test ' + '--os-tenant-name test ' + '--os-cert test ' + '--os-key test ' + '--os-auth-url %s ' + '--os-auth-strategy keystone quota-list' + % auth.V3_URL) + neutron_shell.run(cmdline.split()) + self.mox.VerifyAll() + + @httpretty.activate + def test_v2_auth(self): + # emulate Keystone version discovery + httpretty.register_uri(httpretty.GET, + auth.V2_URL, + body=auth.V2_VERSION_ENTRY) + + neutron_shell = openstack_shell.NeutronShell('2.0') + self.addCleanup(self.mox.UnsetStubs) + self.mox.StubOutWithMock(clientmanager.ClientManager, '__init__') + self.mox.StubOutWithMock(neutron_shell, 'run_subcommand') + clientmanager.ClientManager.__init__( + token='', url='', auth_url=auth.V2_URL, + tenant_name='test', tenant_id='tenant_id', + username='test', user_id='', + password='test', region_name='', api_version={'network': '2.0'}, + auth_strategy='keystone', service_type='network', + endpoint_type='publicURL', insecure=False, ca_cert=None, + timeout=None, + raise_errors=False, + retries=0, + auth=mox.IsA(v2_auth.Password), + session=mox.IsA(session.Session), + log_credentials=True) + neutron_shell.run_subcommand(['quota-list']) + self.mox.ReplayAll() + cmdline = ('--os-username test ' + '--os-password test ' + '--os-tenant-name test ' + '--os-auth-url %s ' + '--os-auth-strategy keystone quota-list' + % auth.V2_URL) + neutron_shell.run(cmdline.split()) + self.mox.VerifyAll() + + @httpretty.activate + def test_failed_auth_version_discovery_v3_auth_url(self): + # emulate Keystone version discovery + httpretty.register_uri(httpretty.GET, + auth.V3_URL, + status=405) + + neutron_shell = openstack_shell.NeutronShell('2.0') + self.addCleanup(self.mox.UnsetStubs) + self.mox.StubOutWithMock(clientmanager.ClientManager, '__init__') + self.mox.StubOutWithMock(neutron_shell, 'run_subcommand') + clientmanager.ClientManager.__init__( + token='', url='', auth_url=auth.V3_URL, + tenant_name='test', tenant_id='tenant_id', + username='test', user_id='', + password='test', region_name='', api_version={'network': '2.0'}, + auth_strategy='keystone', service_type='network', + endpoint_type='publicURL', insecure=False, ca_cert=None, + timeout=None, + raise_errors=False, + retries=0, + auth=mox.IsA(v3_auth.Password), + session=mox.IsA(session.Session), + log_credentials=True) neutron_shell.run_subcommand(['quota-list']) self.mox.ReplayAll() cmdline = ('--os-username test ' '--os-password test ' + '--os-user-domain-name test ' '--os-tenant-name test ' - '--os-auth-url http://127.0.0.1:5000/ ' - '--os-auth-strategy keystone quota-list') + '--os-auth-url %s ' + '--os-auth-strategy keystone quota-list' + % auth.V3_URL) + neutron_shell.run(cmdline.split()) + self.mox.VerifyAll() + + @httpretty.activate + def test_failed_auth_version_discovery_v2_auth_url(self): + # emulate Keystone version discovery + httpretty.register_uri(httpretty.GET, + auth.V2_URL, + status=405) + + neutron_shell = openstack_shell.NeutronShell('2.0') + self.addCleanup(self.mox.UnsetStubs) + self.mox.StubOutWithMock(clientmanager.ClientManager, '__init__') + self.mox.StubOutWithMock(neutron_shell, 'run_subcommand') + clientmanager.ClientManager.__init__( + token='', url='', auth_url=auth.V2_URL, + tenant_name='test', tenant_id='tenant_id', + username='test', user_id='', + password='test', region_name='', api_version={'network': '2.0'}, + auth_strategy='keystone', service_type='network', + endpoint_type='publicURL', insecure=False, ca_cert=None, + timeout=None, + raise_errors=False, + retries=0, + auth=mox.IsA(v2_auth.Password), + session=mox.IsA(session.Session), + log_credentials=True) + neutron_shell.run_subcommand(['quota-list']) + self.mox.ReplayAll() + cmdline = ('--os-username test ' + '--os-password test ' + '--os-tenant-name test ' + '--os-auth-url %s ' + '--os-auth-strategy keystone quota-list' + % auth.V2_URL) + neutron_shell.run(cmdline.split()) + self.mox.VerifyAll() + + @httpretty.activate + def test_auth_version_discovery_v3(self): + # emulate Keystone version discovery + httpretty.register_uri(httpretty.GET, + auth.BASE_URL, + body=auth.V3_VERSION_LIST) + + neutron_shell = openstack_shell.NeutronShell('2.0') + self.addCleanup(self.mox.UnsetStubs) + self.mox.StubOutWithMock(clientmanager.ClientManager, '__init__') + self.mox.StubOutWithMock(neutron_shell, 'run_subcommand') + clientmanager.ClientManager.__init__( + token='', url='', auth_url=auth.BASE_URL, + tenant_name='test', tenant_id='tenant_id', + username='test', user_id='', + password='test', region_name='', api_version={'network': '2.0'}, + auth_strategy='keystone', service_type='network', + endpoint_type='publicURL', insecure=False, ca_cert=None, + timeout=None, + raise_errors=False, + retries=0, + auth=mox.IsA(v3_auth.Password), + session=mox.IsA(session.Session), + log_credentials=True) + neutron_shell.run_subcommand(['quota-list']) + self.mox.ReplayAll() + cmdline = ('--os-username test ' + '--os-password test ' + '--os-user-domain-name test ' + '--os-tenant-name test ' + '--os-auth-url %s ' + '--os-auth-strategy keystone quota-list' + % auth.BASE_URL) + neutron_shell.run(cmdline.split()) + self.mox.VerifyAll() + + @httpretty.activate + def test_auth_version_discovery_v2(self): + # emulate Keystone version discovery + httpretty.register_uri(httpretty.GET, + auth.BASE_URL, + body=auth.V3_VERSION_LIST) + + neutron_shell = openstack_shell.NeutronShell('2.0') + self.addCleanup(self.mox.UnsetStubs) + self.mox.StubOutWithMock(clientmanager.ClientManager, '__init__') + self.mox.StubOutWithMock(neutron_shell, 'run_subcommand') + clientmanager.ClientManager.__init__( + token='', url='', auth_url=auth.BASE_URL, + tenant_name='test', tenant_id='tenant_id', + username='test', user_id='', + password='test', region_name='', api_version={'network': '2.0'}, + auth_strategy='keystone', service_type='network', + endpoint_type='publicURL', insecure=False, ca_cert=None, + timeout=None, + raise_errors=False, + retries=0, + auth=mox.IsA(v2_auth.Password), + session=mox.IsA(session.Session), + log_credentials=True) + neutron_shell.run_subcommand(['quota-list']) + self.mox.ReplayAll() + cmdline = ('--os-username test ' + '--os-password test ' + '--os-tenant-name test ' + '--os-auth-url %s ' + '--os-auth-strategy keystone quota-list' + % auth.BASE_URL) + neutron_shell.run(cmdline.split()) + self.mox.VerifyAll() + + @httpretty.activate + def test_insecure_auth(self): + # emulate Keystone version discovery + httpretty.register_uri(httpretty.GET, + auth.V2_URL, + body=auth.V2_VERSION_ENTRY) + + neutron_shell = openstack_shell.NeutronShell('2.0') + self.addCleanup(self.mox.UnsetStubs) + self.mox.StubOutWithMock(clientmanager.ClientManager, '__init__') + self.mox.StubOutWithMock(neutron_shell, 'run_subcommand') + clientmanager.ClientManager.__init__( + token='', url='', auth_url=auth.V2_URL, + tenant_name='test', tenant_id='tenant_id', + username='test', user_id='', + password='test', region_name='', api_version={'network': '2.0'}, + auth_strategy='keystone', service_type='network', + endpoint_type='publicURL', insecure=True, ca_cert=None, + timeout=None, + raise_errors=False, + retries=0, + auth=mox.IgnoreArg(), + session=mox.IgnoreArg(), + log_credentials=True) + neutron_shell.run_subcommand(['quota-list']) + self.mox.ReplayAll() + cmdline = ('--os-username test ' + '--os-password test ' + '--os-tenant-name test ' + '--insecure ' + '--os-auth-url %s ' + '--os-auth-strategy keystone quota-list' + % auth.V2_URL) neutron_shell.run(cmdline.split()) self.mox.VerifyAll() @@ -162,13 +436,13 @@ def test_endpoint_option(self): shell = openstack_shell.NeutronShell('2.0') parser = shell.build_option_parser('descr', '2.0') - # Neither $OS_ENDPOINT_TYPE nor --endpoint-type + # Neither $OS_ENDPOINT_TYPE nor --os-endpoint-type namespace = parser.parse_args([]) - self.assertEqual('publicURL', namespace.endpoint_type) + self.assertEqual('publicURL', namespace.os_endpoint_type) # --endpoint-type but not $OS_ENDPOINT_TYPE - namespace = parser.parse_args(['--endpoint-type=admin']) - self.assertEqual('admin', namespace.endpoint_type) + namespace = parser.parse_args(['--os-endpoint-type=admin']) + self.assertEqual('admin', namespace.os_endpoint_type) def test_endpoint_environment_variable(self): fixture = fixtures.EnvironmentVariable("OS_ENDPOINT_TYPE", @@ -180,7 +454,7 @@ def test_endpoint_environment_variable(self): # $OS_ENDPOINT_TYPE but not --endpoint-type namespace = parser.parse_args([]) - self.assertEqual("public", namespace.endpoint_type) + self.assertEqual("public", namespace.os_endpoint_type) # --endpoint-type and $OS_ENDPOINT_TYPE namespace = parser.parse_args(['--endpoint-type=admin']) diff --git a/neutronclient/tests/unit/test_ssl.py b/neutronclient/tests/unit/test_ssl.py index 9c0816d45..bc0e50078 100644 --- a/neutronclient/tests/unit/test_ssl.py +++ b/neutronclient/tests/unit/test_ssl.py @@ -14,15 +14,17 @@ # under the License. import fixtures -from mox3 import mox import requests import testtools +import httpretty +from mox3 import mox + from neutronclient.client import HTTPClient from neutronclient.common.clientmanager import ClientManager from neutronclient.common import exceptions from neutronclient import shell as openstack_shell - +from neutronclient.tests.unit import test_auth as auth AUTH_TOKEN = 'test_token' END_URL = 'test_url' @@ -41,7 +43,13 @@ def setUp(self): self.mox = mox.Mox() self.addCleanup(self.mox.UnsetStubs) + @httpretty.activate def test_ca_cert_passed(self): + # emulate Keystone version discovery + httpretty.register_uri(httpretty.GET, + auth.V3_URL, + body=auth.V3_VERSION_ENTRY) + self.mox.StubOutWithMock(ClientManager, '__init__') self.mox.StubOutWithMock(openstack_shell.NeutronShell, 'interact') @@ -66,14 +74,27 @@ def test_ca_cert_passed(self): raise_errors=mox.IgnoreArg(), log_credentials=mox.IgnoreArg(), timeout=mox.IgnoreArg(), + auth=mox.IgnoreArg(), + session=mox.IgnoreArg() ) openstack_shell.NeutronShell.interact().AndReturn(0) self.mox.ReplayAll() - openstack_shell.NeutronShell('2.0').run(['--os-cacert', CA_CERT]) + cmdline = ( + '--os-cacert %s --os-auth-url %s' % + (CA_CERT, auth.V3_URL)) + + openstack_shell.NeutronShell('2.0').run(cmdline.split()) self.mox.VerifyAll() + @httpretty.activate def test_ca_cert_passed_as_env_var(self): + + # emulate Keystone version discovery + httpretty.register_uri(httpretty.GET, + auth.V3_URL, + body=auth.V3_VERSION_ENTRY) + self.useFixture(fixtures.EnvironmentVariable('OS_CACERT', CA_CERT)) self.mox.StubOutWithMock(ClientManager, '__init__') @@ -100,11 +121,15 @@ def test_ca_cert_passed_as_env_var(self): raise_errors=mox.IgnoreArg(), log_credentials=mox.IgnoreArg(), timeout=mox.IgnoreArg(), + auth=mox.IgnoreArg(), + session=mox.IgnoreArg() ) openstack_shell.NeutronShell.interact().AndReturn(0) self.mox.ReplayAll() - openstack_shell.NeutronShell('2.0').run([]) + cmdline = ('--os-auth-url %s' % auth.V3_URL) + openstack_shell.NeutronShell('2.0').run(cmdline.split()) + self.mox.VerifyAll() def test_client_manager_properly_creates_httpclient_instance(self): @@ -121,8 +146,12 @@ def test_client_manager_properly_creates_httpclient_instance(self): tenant_name=mox.IgnoreArg(), token=mox.IgnoreArg(), username=mox.IgnoreArg(), - retries=mox.IgnoreArg(), - raise_errors=mox.IgnoreArg(), + user_id=mox.IgnoreArg(), + tenant_id=mox.IgnoreArg(), + timeout=mox.IgnoreArg(), + log_credentials=mox.IgnoreArg(), + service_type=mox.IgnoreArg(), + endpoint_type=mox.IgnoreArg() ) self.mox.ReplayAll() diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 630d41341..49a49f2ad 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -135,6 +135,8 @@ class Client(object): :param bool raise_errors: If True then exceptions caused by connection failure are propagated to the caller. (default: True) + :param session: Keystone client auth session to use. (optional) + :param auth: Keystone auth plugin to use. (optional) Example:: @@ -1196,12 +1198,12 @@ def delete_packet_filter(self, packet_filter_id): def __init__(self, **kwargs): """Initialize a new client for the Neutron v2.0 API.""" super(Client, self).__init__() - self.httpclient = client.HTTPClient(**kwargs) + self.retries = kwargs.pop('retries', 0) + self.raise_errors = kwargs.pop('raise_errors', True) + self.httpclient = client.construct_http_client(**kwargs) self.version = '2.0' self.format = 'json' self.action_prefix = "/v%s" % (self.version) - self.retries = kwargs.get('retries', 0) - self.raise_errors = kwargs.get('raise_errors', True) self.retry_interval = 1 def _handle_fault_response(self, status_code, response_body): diff --git a/test-requirements.txt b/test-requirements.txt index ad976c6cb..9a4d7e311 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -4,6 +4,7 @@ cliff-tablib>=1.0 coverage>=3.6 discover fixtures>=0.3.14 +httpretty>=0.8.0,!=0.8.1,!=0.8.2 mox3>=0.7.0 oslosphinx oslotest From 5258ec52a8cb426558f8ed1c7f76a984490b0a0b Mon Sep 17 00:00:00 2001 From: Craig Tracey Date: Sat, 2 Aug 2014 10:26:08 -0400 Subject: [PATCH 065/845] Provide support for nested objects With the introduction of the LBaaS v2 API, all objects are capable of normal CRUD operations. Until now these objects were mostly top-level constructs, and in then instances where they weren't, they were manipulated by way of 'actions.' LBaaS v2, as designed, nests members under pools, and supports all normal CRUD operations. This means that in order to take action against a member we need to provide a pool uuid as well as the member uuid. The python-neutronclient currently does not support this, so this change makes to possible to do so. This change adds the capability but does not affect other top-level object implemenations. Change-Id: I38c3b78dc077f1f78ebc7e703fe7277a02f53f7a Partially-implements: blueprint lbaas-api-and-objmodel-improvement --- neutronclient/neutron/v2_0/__init__.py | 61 +++++++++++++++++++------- neutronclient/tests/unit/test_cli20.py | 45 ++++++++++++++----- 2 files changed, 78 insertions(+), 28 deletions(-) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index 74e942ba1..d9493fcd6 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -44,7 +44,8 @@ def _get_resource_plural(resource, client): return resource + 's' -def find_resourceid_by_id(client, resource, resource_id, cmd_resource=None): +def find_resourceid_by_id(client, resource, resource_id, cmd_resource=None, + parent_id=None): if not cmd_resource: cmd_resource = resource cmd_resource_plural = _get_resource_plural(cmd_resource, client) @@ -54,7 +55,10 @@ def find_resourceid_by_id(client, resource, resource_id, cmd_resource=None): match = re.match(UUID_PATTERN, resource_id) collection = resource_plural if match: - data = obj_lister(id=resource_id, fields='id') + if parent_id: + data = obj_lister(parent_id, id=resource_id, fields='id') + else: + data = obj_lister(id=resource_id, fields='id') if data and data[collection]: return data[collection][0]['id'] not_found_message = (_("Unable to find %(resource)s with id " @@ -66,7 +70,7 @@ def find_resourceid_by_id(client, resource, resource_id, cmd_resource=None): def _find_resourceid_by_name(client, resource, name, project_id=None, - cmd_resource=None): + cmd_resource=None, parent_id=None): if not cmd_resource: cmd_resource = resource cmd_resource_plural = _get_resource_plural(cmd_resource, client) @@ -75,7 +79,10 @@ def _find_resourceid_by_name(client, resource, name, project_id=None, params = {'name': name, 'fields': 'id'} if project_id: params['tenant_id'] = project_id - data = obj_lister(**params) + if parent_id: + data = obj_lister(parent_id, **params) + else: + data = obj_lister(**params) collection = resource_plural info = data[collection] if len(info) > 1: @@ -93,13 +100,14 @@ def _find_resourceid_by_name(client, resource, name, project_id=None, def find_resourceid_by_name_or_id(client, resource, name_or_id, - project_id=None, cmd_resource=None): + project_id=None, cmd_resource=None, + parent_id=None): try: return find_resourceid_by_id(client, resource, name_or_id, - cmd_resource) + cmd_resource, parent_id) except exceptions.NeutronClientException: return _find_resourceid_by_name(client, resource, name_or_id, - project_id, cmd_resource) + project_id, cmd_resource, parent_id) def add_show_list_common_argument(parser): @@ -356,6 +364,7 @@ class NeutronCommand(command.OpenStackCommand): json_indent = None resource = None shadow_resource = None + parent_id = None def __init__(self, app, app_args): super(NeutronCommand, self).__init__(app, app_args) @@ -440,7 +449,10 @@ def get_data(self, parsed_args): body[self.resource].update(_extra_values) obj_creator = getattr(neutron_client, "create_%s" % self.cmd_resource) - data = obj_creator(body) + if self.parent_id: + data = obj_creator(self.parent_id, body) + else: + data = obj_creator(body) self.format_output_data(data) info = self.resource in data and data[self.resource] or None if info: @@ -490,10 +502,13 @@ def run(self, parsed_args): else: _id = find_resourceid_by_id( neutron_client, self.resource, parsed_args.id, - self.cmd_resource) - obj_updator = getattr(neutron_client, + self.cmd_resource, self.parent_id) + obj_updater = getattr(neutron_client, "update_%s" % self.cmd_resource) - obj_updator(_id, body) + if self.parent_id: + obj_updater(_id, self.parent_id, body) + else: + obj_updater(_id, body) print((_('Updated %(resource)s: %(id)s') % {'id': parsed_args.id, 'resource': self.resource}), file=self.app.stdout) @@ -527,7 +542,8 @@ def run(self, parsed_args): obj_deleter = getattr(neutron_client, "delete_%s" % self.cmd_resource) if self.allow_names: - params = {'cmd_resource': self.cmd_resource} + params = {'cmd_resource': self.cmd_resource, + 'parent_id': self.parent_id} _id = find_resourceid_by_name_or_id(neutron_client, self.resource, parsed_args.id, @@ -535,7 +551,10 @@ def run(self, parsed_args): else: _id = parsed_args.id - obj_deleter(_id) + if self.parent_id: + obj_deleter(_id, self.parent_id) + else: + obj_deleter(_id) print((_('Deleted %(resource)s: %(id)s') % {'id': parsed_args.id, 'resource': self.resource}), @@ -578,7 +597,10 @@ def call_server(self, neutron_client, search_opts, parsed_args): resource_plural = _get_resource_plural(self.cmd_resource, neutron_client) obj_lister = getattr(neutron_client, "list_%s" % resource_plural) - data = obj_lister(**search_opts) + if self.parent_id: + data = obj_lister(self.parent_id, **search_opts) + else: + data = obj_lister(**search_opts) return data def retrieve_list(self, parsed_args): @@ -674,14 +696,19 @@ def get_data(self, parsed_args): if parsed_args.fields: params = {'fields': parsed_args.fields} if self.allow_names: - _id = find_resourceid_by_name_or_id(neutron_client, self.resource, + _id = find_resourceid_by_name_or_id(neutron_client, + self.resource, parsed_args.id, - cmd_resource=self.cmd_resource) + cmd_resource=self.cmd_resource, + parent_id=self.parent_id) else: _id = parsed_args.id obj_shower = getattr(neutron_client, "show_%s" % self.cmd_resource) - data = obj_shower(_id, **params) + if self.parent_id: + data = obj_shower(_id, self.parent_id, **params) + else: + data = obj_shower(_id, **params) self.format_output_data(data) resource = data[self.resource] if self.resource in data: diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index e95d73770..54eadb4f0 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -164,7 +164,7 @@ class CLITestV20Base(base.BaseTestCase): id_field = 'id' def _find_resourceid(self, client, resource, name_or_id, - cmd_resource=None): + cmd_resource=None, parent_id=None): return name_or_id def _get_attr_metadata(self): @@ -202,7 +202,8 @@ def setUp(self, plurals={}): def _test_create_resource(self, resource, cmd, name, myid, args, position_names, position_values, tenant_id=None, tags=None, admin_state_up=True, - extra_body=None, cmd_resource=None, **kwargs): + extra_body=None, cmd_resource=None, + parent_id=None, **kwargs): self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") cmd.get_client().MultipleTimes().AndReturn(self.client) @@ -239,6 +240,8 @@ def _test_create_resource(self, resource, cmd, name, myid, args, resource_plural = neutronV2_0._get_resource_plural(cmd_resource, self.client) path = getattr(self.client, resource_plural + "_path") + if parent_id: + path = path % parent_id # Work around for LP #1217791. XML deserializer called from # MyComparator does not decodes XML string correctly. if self.format == 'json': @@ -263,7 +266,7 @@ def _test_create_resource(self, resource, cmd, name, myid, args, def _test_list_columns(self, cmd, resources, resources_out, args=['-f', 'json'], - cmd_resources=None): + cmd_resources=None, parent_id=None): self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") cmd.get_client().MultipleTimes().AndReturn(self.client) @@ -274,6 +277,8 @@ def _test_list_columns(self, cmd, resources, resstr = self.client.serialize(resources_out) path = getattr(self.client, cmd_resources + "_path") + if parent_id: + path = path % parent_id self.client.httpclient.request( end_url(path, format=self.format), 'GET', body=None, @@ -289,7 +294,8 @@ def _test_list_columns(self, cmd, resources, def _test_list_resources(self, resources, cmd, detail=False, tags=[], fields_1=[], fields_2=[], page_size=None, sort_key=[], sort_dir=[], response_contents=None, - base_args=None, path=None, cmd_resources=None): + base_args=None, path=None, cmd_resources=None, + parent_id=None): self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") cmd.get_client().MultipleTimes().AndReturn(self.client) @@ -367,6 +373,8 @@ def _test_list_resources(self, resources, cmd, detail=False, tags=[], query += 'sort_dir=%s' % dir if path is None: path = getattr(self.client, cmd_resources + "_path") + if parent_id: + path = path % parent_id self.client.httpclient.request( MyUrlComparator(end_url(path, query, format=self.format), self.client), @@ -385,7 +393,8 @@ def _test_list_resources(self, resources, cmd, detail=False, tags=[], return _str def _test_list_resources_with_pagination(self, resources, cmd, - cmd_resources=None): + cmd_resources=None, + parent_id=None): self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") cmd.get_client().MultipleTimes().AndReturn(self.client) @@ -393,6 +402,8 @@ def _test_list_resources_with_pagination(self, resources, cmd, cmd_resources = resources path = getattr(self.client, cmd_resources + "_path") + if parent_id: + path = path % parent_id fake_query = "marker=myid2&limit=2" reses1 = {resources: [{'id': 'myid1', }, {'id': 'myid2', }], @@ -421,7 +432,7 @@ def _test_list_resources_with_pagination(self, resources, cmd, self.mox.UnsetStubs() def _test_update_resource(self, resource, cmd, myid, args, extrafields, - cmd_resource=None): + cmd_resource=None, parent_id=None): self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") cmd.get_client().MultipleTimes().AndReturn(self.client) @@ -430,6 +441,10 @@ def _test_update_resource(self, resource, cmd, myid, args, extrafields, body = {resource: extrafields} path = getattr(self.client, cmd_resource + "_path") + if parent_id: + path = path % (parent_id, myid) + else: + path = path % myid self.client.format = self.format # Work around for LP #1217791. XML deserializer called from # MyComparator does not decodes XML string correctly. @@ -438,7 +453,7 @@ def _test_update_resource(self, resource, cmd, myid, args, extrafields, else: mox_body = self.client.serialize(body) self.client.httpclient.request( - MyUrlComparator(end_url(path % myid, format=self.format), + MyUrlComparator(end_url(path, format=self.format), self.client), 'PUT', body=mox_body, @@ -454,7 +469,7 @@ def _test_update_resource(self, resource, cmd, myid, args, extrafields, self.assertIn(myid, _str) def _test_show_resource(self, resource, cmd, myid, args, fields=[], - cmd_resource=None): + cmd_resource=None, parent_id=None): self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") cmd.get_client().MultipleTimes().AndReturn(self.client) @@ -468,8 +483,12 @@ def _test_show_resource(self, resource, cmd, myid, args, fields=[], self.client.format = self.format resstr = self.client.serialize(expected_res) path = getattr(self.client, cmd_resource + "_path") + if parent_id: + path = path % (parent_id, myid) + else: + path = path % myid self.client.httpclient.request( - end_url(path % myid, query, format=self.format), 'GET', + end_url(path, query, format=self.format), 'GET', body=None, headers=mox.ContainsKeyValue( 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr)) @@ -484,15 +503,19 @@ def _test_show_resource(self, resource, cmd, myid, args, fields=[], self.assertIn('myname', _str) def _test_delete_resource(self, resource, cmd, myid, args, - cmd_resource=None): + cmd_resource=None, parent_id=None): self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") cmd.get_client().MultipleTimes().AndReturn(self.client) if not cmd_resource: cmd_resource = resource path = getattr(self.client, cmd_resource + "_path") + if parent_id: + path = path % (parent_id, myid) + else: + path = path % (myid) self.client.httpclient.request( - end_url(path % myid, format=self.format), 'DELETE', + end_url(path, format=self.format), 'DELETE', body=None, headers=mox.ContainsKeyValue( 'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), None)) From ef39ff0002d2ad3847b1e41a8261f4245122a0f4 Mon Sep 17 00:00:00 2001 From: Ilya Shakhat Date: Mon, 28 Jul 2014 16:07:32 +0400 Subject: [PATCH 066/845] Fix unit tests to succeed on any PYTHONHASHSEED Many tests rely on order of parameters in URL query, but queries are constructed from dicts and order of parameters is non-deterministic. The issue doesn't occur if PYTHONHASHSEED stays constant, but breaks if random. The fix is needed for tox>1.7 and Py3. Closes-Bug: 1348818 Change-Id: I01145df689e90ccd6100fa82092ba3ed222af8fc --- neutronclient/tests/unit/test_cli20.py | 15 +++++-- neutronclient/tests/unit/test_cli20_agents.py | 2 +- .../tests/unit/test_cli20_network.py | 25 ++++++++---- neutronclient/tests/unit/test_cli20_port.py | 5 ++- .../tests/unit/test_cli20_securitygroup.py | 5 ++- neutronclient/tests/unit/test_name_or_id.py | 40 ++++++++++++++----- 6 files changed, 69 insertions(+), 23 deletions(-) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index ff23a43a0..d230c150d 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -23,6 +23,7 @@ from oslotest import base import requests import six +import six.moves.urllib.parse as urlparse from neutronclient.common import constants from neutronclient.common import exceptions @@ -85,7 +86,13 @@ def __init__(self, lhs, client): self.client = client def equals(self, rhs): - return str(self) == rhs + lhsp = urlparse.urlparse(self.lhs) + rhsp = urlparse.urlparse(rhs) + + return (lhsp.scheme == rhsp.scheme and + lhsp.netloc == rhsp.netloc and + lhsp.path == rhsp.path and + urlparse.parse_qs(lhsp.query) == urlparse.parse_qs(rhsp.query)) def __str__(self): if self.client and self.client.format != FORMAT: @@ -408,7 +415,8 @@ def _test_list_resources_with_pagination(self, resources, cmd, headers=mox.ContainsKeyValue( 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr1)) self.client.httpclient.request( - end_url(path, fake_query, format=self.format), 'GET', + MyUrlComparator(end_url(path, fake_query, format=self.format), + self.client), 'GET', body=None, headers=mox.ContainsKeyValue( 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr2)) @@ -571,7 +579,8 @@ def test_do_request_error_without_response_body(self): self.client.httpclient.auth_token = 'token' self.client.httpclient.request( - end_url('/test', query=expect_query, format=self.format), + MyUrlComparator(end_url( + '/test', query=expect_query, format=self.format), self.client), 'PUT', body='', headers=mox.ContainsKeyValue('X-Auth-Token', 'token') ).AndReturn((MyResp(400, reason='An error'), '')) diff --git a/neutronclient/tests/unit/test_cli20_agents.py b/neutronclient/tests/unit/test_cli20_agents.py index 1e75a92bb..699201955 100644 --- a/neutronclient/tests/unit/test_cli20_agents.py +++ b/neutronclient/tests/unit/test_cli20_agents.py @@ -36,7 +36,7 @@ def test_list_agents(self): self.assertEqual(1, len(returned_agents)) ag = returned_agents[0] self.assertEqual(3, len(ag)) - self.assertEqual("alive", ag.keys()[2]) + self.assertIn("alive", ag.keys()) def test_list_agents_field(self): contents = {'agents': [{'alive': True}]} diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index 1e1b9a9fe..0e6cc6ef2 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -118,7 +118,9 @@ def test_list_nets_empty_with_column(self): args = ['-c', 'id', '--', '--id', 'myfakeid'] path = getattr(self.client, resources + "_path") self.client.httpclient.request( - test_cli20.end_url(path, query), 'GET', + test_cli20.MyUrlComparator(test_cli20.end_url(path, query), + self.client), + 'GET', body=None, headers=mox.ContainsKeyValue( 'X-Auth-Token', @@ -202,7 +204,9 @@ def setup_list_stub(resources, data, query): resp = (test_cli20.MyResp(200), resstr) path = getattr(self.client, resources + '_path') self.client.httpclient.request( - test_cli20.end_url(path, query), 'GET', + test_cli20.MyUrlComparator( + test_cli20.end_url(path, query), self.client), + 'GET', body=None, headers=mox.ContainsKeyValue( 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(resp) @@ -312,7 +316,9 @@ def test_list_external_nets_empty_with_column(self): args = ['-c', 'id', '--', '--id', 'myfakeid'] path = getattr(self.client, resources + "_path") self.client.httpclient.request( - test_cli20.end_url(path, query), 'GET', + test_cli20.MyUrlComparator( + test_cli20.end_url(path, query), self.client), + 'GET', body=None, headers=mox.ContainsKeyValue( 'X-Auth-Token', @@ -377,7 +383,9 @@ def _test_list_external_nets(self, resources, cmd, path = getattr(self.client, resources + "_path") self.client.httpclient.request( - test_cli20.end_url(path, query), 'GET', + test_cli20.MyUrlComparator( + test_cli20.end_url(path, query), self.client), + 'GET', body=None, headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) ).AndReturn((test_cli20.MyResp(200), resstr)) @@ -496,7 +504,8 @@ def test_extend_list(self): def mox_calls(path, data): filters, response = self._build_test_data(data) self.client.httpclient.request( - test_cli20.end_url(path, 'fields=id&fields=cidr' + filters), + test_cli20.MyUrlComparator(test_cli20.end_url( + path, 'fields=id&fields=cidr' + filters), self.client), 'GET', body=None, headers=mox.ContainsKeyValue( @@ -518,8 +527,10 @@ def mox_calls(path, data): filters, response = self._build_test_data(data) self.client._check_uri_length(mox.IgnoreArg()).AndReturn(None) self.client.httpclient.request( - test_cli20.end_url(path, - 'fields=id&fields=cidr%s' % filters), + test_cli20.MyUrlComparator( + test_cli20.end_url( + path, 'fields=id&fields=cidr%s' % filters), + self.client), 'GET', body=None, headers=mox.ContainsKeyValue( diff --git a/neutronclient/tests/unit/test_cli20_port.py b/neutronclient/tests/unit/test_cli20_port.py index ee43c7568..a304c975c 100644 --- a/neutronclient/tests/unit/test_cli20_port.py +++ b/neutronclient/tests/unit/test_cli20_port.py @@ -274,7 +274,10 @@ def _test_list_router_port(self, resources, cmd, query = query and query + '&device_id=%s' or 'device_id=%s' path = getattr(self.client, resources + "_path") self.client.httpclient.request( - test_cli20.end_url(path, query % myid), 'GET', + test_cli20.MyUrlComparator( + test_cli20.end_url(path, query % myid), + self.client), + 'GET', body=None, headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) ).AndReturn((test_cli20.MyResp(200), resstr)) diff --git a/neutronclient/tests/unit/test_cli20_securitygroup.py b/neutronclient/tests/unit/test_cli20_securitygroup.py index e6127ff9b..d483d09e6 100644 --- a/neutronclient/tests/unit/test_cli20_securitygroup.py +++ b/neutronclient/tests/unit/test_cli20_securitygroup.py @@ -235,7 +235,10 @@ def setup_list_stub(resources, data, query): resp = (test_cli20.MyResp(200), resstr) path = getattr(self.client, resources + '_path') self.client.httpclient.request( - test_cli20.end_url(path, query), 'GET', + test_cli20.MyUrlComparator( + test_cli20.end_url(path, query), + self.client), + 'GET', body=None, headers=mox.ContainsKeyValue( 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(resp) diff --git a/neutronclient/tests/unit/test_name_or_id.py b/neutronclient/tests/unit/test_name_or_id.py index 9d876b983..412c45515 100644 --- a/neutronclient/tests/unit/test_name_or_id.py +++ b/neutronclient/tests/unit/test_name_or_id.py @@ -44,7 +44,10 @@ def test_get_id_from_id(self): self.mox.StubOutWithMock(self.client.httpclient, "request") path = getattr(self.client, "networks_path") self.client.httpclient.request( - test_cli20.end_url(path, "fields=id&id=" + _id), 'GET', + test_cli20.MyUrlComparator( + test_cli20.end_url(path, "fields=id&id=" + _id), + self.client), + 'GET', body=None, headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) ).AndReturn((test_cli20.MyResp(200), resstr)) @@ -61,12 +64,18 @@ def test_get_id_from_id_then_name_empty(self): self.mox.StubOutWithMock(self.client.httpclient, "request") path = getattr(self.client, "networks_path") self.client.httpclient.request( - test_cli20.end_url(path, "fields=id&id=" + _id), 'GET', + test_cli20.MyUrlComparator( + test_cli20.end_url(path, "fields=id&id=" + _id), + self.client), + 'GET', body=None, headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) ).AndReturn((test_cli20.MyResp(200), resstr1)) self.client.httpclient.request( - test_cli20.end_url(path, "fields=id&name=" + _id), 'GET', + test_cli20.MyUrlComparator( + test_cli20.end_url(path, "fields=id&name=" + _id), + self.client), + 'GET', body=None, headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) ).AndReturn((test_cli20.MyResp(200), resstr)) @@ -83,7 +92,10 @@ def test_get_id_from_name(self): self.mox.StubOutWithMock(self.client.httpclient, "request") path = getattr(self.client, "networks_path") self.client.httpclient.request( - test_cli20.end_url(path, "fields=id&name=" + name), 'GET', + test_cli20.MyUrlComparator( + test_cli20.end_url(path, "fields=id&name=" + name), + self.client), + 'GET', body=None, headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) ).AndReturn((test_cli20.MyResp(200), resstr)) @@ -100,7 +112,10 @@ def test_get_id_from_name_multiple(self): self.mox.StubOutWithMock(self.client.httpclient, "request") path = getattr(self.client, "networks_path") self.client.httpclient.request( - test_cli20.end_url(path, "fields=id&name=" + name), 'GET', + test_cli20.MyUrlComparator( + test_cli20.end_url(path, "fields=id&name=" + name), + self.client), + 'GET', body=None, headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) ).AndReturn((test_cli20.MyResp(200), resstr)) @@ -118,7 +133,10 @@ def test_get_id_from_name_notfound(self): self.mox.StubOutWithMock(self.client.httpclient, "request") path = getattr(self.client, "networks_path") self.client.httpclient.request( - test_cli20.end_url(path, "fields=id&name=" + name), 'GET', + test_cli20.MyUrlComparator( + test_cli20.end_url(path, "fields=id&name=" + name), + self.client), + 'GET', body=None, headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) ).AndReturn((test_cli20.MyResp(200), resstr)) @@ -140,8 +158,9 @@ def test_get_id_from_name_multiple_with_project(self): self.mox.StubOutWithMock(self.client.httpclient, "request") path = getattr(self.client, "security_groups_path") self.client.httpclient.request( - test_cli20.end_url(path, "fields=id&name=%s&tenant_id=%s" % - (name, project)), + test_cli20.MyUrlComparator( + test_cli20.end_url(path, "fields=id&name=%s&tenant_id=%s" % + (name, project)), self.client), 'GET', body=None, headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) @@ -162,8 +181,9 @@ def test_get_id_from_name_multiple_with_project_not_found(self): self.mox.StubOutWithMock(self.client.httpclient, "request") path = getattr(self.client, "security_groups_path") self.client.httpclient.request( - test_cli20.end_url(path, "fields=id&name=%s&tenant_id=%s" % - (name, project)), + test_cli20.MyUrlComparator( + test_cli20.end_url(path, "fields=id&name=%s&tenant_id=%s" % + (name, project)), self.client), 'GET', body=None, headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) From 16e04a5312bc3aff120fda7deaf0879ad2175165 Mon Sep 17 00:00:00 2001 From: Angus Lees Date: Tue, 12 Aug 2014 14:07:28 +1000 Subject: [PATCH 067/845] Avoid modifying default function arguments Modifying passed arguments inadvertently is an easy pitfall in python. Even more serious is modifying the default value to a function, since that modification will be preserved across future function calls. The typical defense against the latter is to avoid using mutable default values. This change replaces several def foo(bar=[]) with bar=() or similar, and then fixes a few cases that were exposed where the argument was indeed permanently modified. Change-Id: I3d04dcb39e3816942f8e80ce499c47d774a773c6 --- neutronclient/common/utils.py | 5 +++- neutronclient/tests/unit/test_cli20.py | 23 ++++++++++--------- .../tests/unit/test_cli20_network.py | 16 ++++++------- neutronclient/tests/unit/test_cli20_port.py | 8 +++---- .../tests/unit/test_cli20_securitygroup.py | 2 +- 5 files changed, 29 insertions(+), 25 deletions(-) diff --git a/neutronclient/common/utils.py b/neutronclient/common/utils.py index b26166d2b..1c78577f5 100644 --- a/neutronclient/common/utils.py +++ b/neutronclient/common/utils.py @@ -106,7 +106,7 @@ def get_client_class(api_name, version, version_map): return import_class(client_path) -def get_item_properties(item, fields, mixed_case_fields=[], formatters={}): +def get_item_properties(item, fields, mixed_case_fields=(), formatters=None): """Return a tuple containing the item properties. :param item: a single item resource (e.g. Server, Tenant, etc) @@ -115,6 +115,9 @@ def get_item_properties(item, fields, mixed_case_fields=[], formatters={}): :param formatters: dictionary mapping field names to callables to format the values """ + if formatters is None: + formatters = {} + row = [] for field in fields: diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index eedebed68..e14863fa7 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -15,6 +15,7 @@ # import contextlib +import itertools import sys import urllib @@ -174,11 +175,12 @@ def _get_attr_metadata(self): 'xmlns': constants.XML_NS_V20, constants.EXT_NS: {'prefix': 'http://xxxx.yy.com'}} - def setUp(self, plurals={}): + def setUp(self, plurals=None): """Prepare the test environment.""" super(CLITestV20Base, self).setUp() client.Client.EXTED_PLURALS.update(constants.PLURALS) - client.Client.EXTED_PLURALS.update(plurals) + if plurals is not None: + client.Client.EXTED_PLURALS.update(plurals) self.metadata = {'plurals': client.Client.EXTED_PLURALS, 'xmlns': constants.XML_NS_V20, constants.EXT_NS: {'prefix': @@ -264,7 +266,7 @@ def _test_create_resource(self, resource, cmd, name, myid, args, self.assertIn(name, _str) def _test_list_columns(self, cmd, resources, - resources_out, args=['-f', 'json'], + resources_out, args=('-f', 'json'), cmd_resources=None, parent_id=None): self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") @@ -283,16 +285,16 @@ def _test_list_columns(self, cmd, resources, body=None, headers=mox.ContainsKeyValue( 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr)) - args.extend(['--request-format', self.format]) + args = tuple(args) + ('--request-format', self.format) self.mox.ReplayAll() cmd_parser = cmd.get_parser("list_" + cmd_resources) shell.run_command(cmd, cmd_parser, args) self.mox.VerifyAll() self.mox.UnsetStubs() - def _test_list_resources(self, resources, cmd, detail=False, tags=[], - fields_1=[], fields_2=[], page_size=None, - sort_key=[], sort_dir=[], response_contents=None, + def _test_list_resources(self, resources, cmd, detail=False, tags=(), + fields_1=(), fields_2=(), page_size=None, + sort_key=(), sort_dir=(), response_contents=None, base_args=None, path=None, cmd_resources=None, parent_id=None): self.mox.StubOutWithMock(cmd, "get_client") @@ -338,8 +340,7 @@ def _test_list_resources(self, resources, cmd, detail=False, tags=[], args.append(field) if detail: query = query and query + '&verbose=True' or 'verbose=True' - fields_1.extend(fields_2) - for field in fields_1: + for field in itertools.chain(fields_1, fields_2): if query: query += "&fields=" + field else: @@ -361,7 +362,7 @@ def _test_list_resources(self, resources, cmd, detail=False, tags=[], if sort_dir: len_diff = len(sort_key) - len(sort_dir) if len_diff > 0: - sort_dir += ['asc'] * len_diff + sort_dir = tuple(sort_dir) + ('asc',) * len_diff elif len_diff < 0: sort_dir = sort_dir[:len(sort_key)] for dir in sort_dir: @@ -467,7 +468,7 @@ def _test_update_resource(self, resource, cmd, myid, args, extrafields, _str = self.fake_stdout.make_string() self.assertIn(myid, _str) - def _test_show_resource(self, resource, cmd, myid, args, fields=[], + def _test_show_resource(self, resource, cmd, myid, args, fields=(), cmd_resource=None, parent_id=None): self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index 1e1b9a9fe..4b919b6ef 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -13,6 +13,7 @@ # under the License. # +import itertools import sys from mox3 import mox @@ -132,9 +133,9 @@ def test_list_nets_empty_with_column(self): _str = self.fake_stdout.make_string() self.assertEqual('\n', _str) - def _test_list_networks(self, cmd, detail=False, tags=[], - fields_1=[], fields_2=[], page_size=None, - sort_key=[], sort_dir=[]): + def _test_list_networks(self, cmd, detail=False, tags=(), + fields_1=(), fields_2=(), page_size=None, + sort_key=(), sort_dir=()): resources = "networks" self.mox.StubOutWithMock(network.ListNetwork, "extend_list") network.ListNetwork.extend_list(mox.IsA(list), mox.IgnoreArg()) @@ -262,7 +263,7 @@ def test_list_nets_fields(self): fields_1=['a', 'b'], fields_2=['c', 'd']) def _test_list_nets_columns(self, cmd, returned_body, - args=['-f', 'json']): + args=('-f', 'json')): resources = 'networks' self.mox.StubOutWithMock(network.ListNetwork, "extend_list") network.ListNetwork.extend_list(mox.IsA(list), mox.IgnoreArg()) @@ -327,8 +328,8 @@ def test_list_external_nets_empty_with_column(self): self.assertEqual('\n', _str) def _test_list_external_nets(self, resources, cmd, - detail=False, tags=[], - fields_1=[], fields_2=[]): + detail=False, tags=(), + fields_1=(), fields_2=()): self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") self.mox.StubOutWithMock(network.ListNetwork, "extend_list") @@ -357,8 +358,7 @@ def _test_list_external_nets(self, resources, cmd, args.append("--fields") for field in fields_2: args.append(field) - fields_1.extend(fields_2) - for field in fields_1: + for field in itertools.chain(fields_1, fields_2): if query: query += "&fields=" + field else: diff --git a/neutronclient/tests/unit/test_cli20_port.py b/neutronclient/tests/unit/test_cli20_port.py index ee43c7568..ae91f7d42 100644 --- a/neutronclient/tests/unit/test_cli20_port.py +++ b/neutronclient/tests/unit/test_cli20_port.py @@ -14,6 +14,7 @@ # under the License. # +import itertools import sys from mox3 import mox @@ -227,8 +228,8 @@ def test_list_ports_fields(self): fields_1=['a', 'b'], fields_2=['c', 'd']) def _test_list_router_port(self, resources, cmd, - myid, detail=False, tags=[], - fields_1=[], fields_2=[]): + myid, detail=False, tags=(), + fields_1=(), fields_2=()): self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") cmd.get_client().MultipleTimes().AndReturn(self.client) @@ -257,8 +258,7 @@ def _test_list_router_port(self, resources, cmd, args.append("--fields") for field in fields_2: args.append(field) - fields_1.extend(fields_2) - for field in fields_1: + for field in itertools.chain(fields_1, fields_2): if query: query += "&fields=" + field else: diff --git a/neutronclient/tests/unit/test_cli20_securitygroup.py b/neutronclient/tests/unit/test_cli20_securitygroup.py index e6127ff9b..ffc4698c2 100644 --- a/neutronclient/tests/unit/test_cli20_securitygroup.py +++ b/neutronclient/tests/unit/test_cli20_securitygroup.py @@ -227,7 +227,7 @@ def test_show_security_group_rule(self): args, ['id']) def _test_list_security_group_rules_extend(self, data=None, expected=None, - args=[], conv=True, + args=(), conv=True, query_field=False): def setup_list_stub(resources, data, query): reses = {resources: data} From e254392e7de66becb818ee4794379ade6afa7ece Mon Sep 17 00:00:00 2001 From: Angus Lees Date: Tue, 12 Aug 2014 15:36:54 +1000 Subject: [PATCH 068/845] Remove incorrect super() call extension.ShowExt.get_parser called super() with the *parent* class, skipping the parent's own get_parser. This is quite surprising without any additional comments to explain the situation. The only difference between the two get_parsers is the string that appears in the usage help text (EXT_ALIAS vs EXTENSION). Rather than improve the above super() call, this change removes the specialised ShowExt.get_parser() implementation entirely since it appears the generic version is sufficient. Change-Id: I76b901d6a27fbe6f103baf2f03ea17912123a46b --- neutronclient/neutron/v2_0/extension.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/neutronclient/neutron/v2_0/extension.py b/neutronclient/neutron/v2_0/extension.py index 2c14d72eb..24905d59d 100644 --- a/neutronclient/neutron/v2_0/extension.py +++ b/neutronclient/neutron/v2_0/extension.py @@ -15,7 +15,6 @@ # from neutronclient.neutron import v2_0 as cmd_base -from neutronclient.openstack.common.gettextutils import _ class ListExt(cmd_base.ListCommand): @@ -30,11 +29,3 @@ class ShowExt(cmd_base.ShowCommand): resource = "extension" allow_names = False - - def get_parser(self, prog_name): - parser = super(cmd_base.ShowCommand, self).get_parser(prog_name) - cmd_base.add_show_list_common_argument(parser) - parser.add_argument( - 'id', metavar='EXT-ALIAS', - help=_('The extension alias.')) - return parser From 2840bdbe3d2b9a34303c16f3fc6d45473fb13882 Mon Sep 17 00:00:00 2001 From: John Trowbridge Date: Wed, 13 Aug 2014 16:01:52 -0400 Subject: [PATCH 069/845] Adds tty password entry for neutronclient Added functionality from keystoneclient to fallback to the tty for password entry if no password is given via the environment or the --os-password option. Closes-Bug: 1356550 Change-Id: I7d0428a8901cae667ef598aa515e108a3a679540 --- neutronclient/shell.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/neutronclient/shell.py b/neutronclient/shell.py index ce7bed9c9..7f2d11286 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -21,6 +21,7 @@ from __future__ import print_function import argparse +import getpass import logging import os import sys @@ -752,9 +753,20 @@ def authenticate_user(self): " --os-user_id, env[OS_USER_ID]")) if not self.options.os_password: - raise exc.CommandError( - _("You must provide a password via" - " either --os-password or env[OS_PASSWORD]")) + # No password, If we've got a tty, try prompting for it + if hasattr(sys.stdin, 'isatty') and sys.stdin.isatty(): + # Check for Ctl-D + try: + self.options.os_password = getpass.getpass( + 'OS Password: ') + except EOFError: + pass + # No password because we didn't have a tty or the + # user Ctl-D when prompted. + if not self.options.os_password: + raise exc.CommandError( + _("You must provide a password via" + " either --os-password or env[OS_PASSWORD]")) if (not project_info): # tenent is deprecated in Keystone v3. Use the latest From 8f59a306e269fea21d3f5643b11de49c73cacc19 Mon Sep 17 00:00:00 2001 From: Zang MingJie Date: Wed, 6 Aug 2014 21:18:44 +0800 Subject: [PATCH 070/845] Print exception when verbose is over DEBUG_LEVEL Change-Id: I8a2010332be0acfe6762bf3190248fe17f42cadb Closes-bug: #1353496 --- neutronclient/shell.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/neutronclient/shell.py b/neutronclient/shell.py index ce7bed9c9..6f87de1f4 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -665,7 +665,7 @@ def run(self, argv): self.interactive_mode = not remainder self.initialize_app(remainder) except Exception as err: - if self.options.verbose_level == self.DEBUG_LEVEL: + if self.options.verbose_level >= self.DEBUG_LEVEL: self.log.exception(unicode(err)) raise else: @@ -695,24 +695,24 @@ def run_subcommand(self, argv): cmd_parser = cmd.get_parser(full_name) return run_command(cmd, cmd_parser, sub_argv) except Exception as err: - if self.options.verbose_level == self.DEBUG_LEVEL: + if self.options.verbose_level >= self.DEBUG_LEVEL: self.log.exception(unicode(err)) else: self.log.error(unicode(err)) try: self.clean_up(cmd, result, err) except Exception as err2: - if self.options.verbose_level == self.DEBUG_LEVEL: + if self.options.verbose_level >= self.DEBUG_LEVEL: self.log.exception(unicode(err2)) else: self.log.error(_('Could not clean up: %s'), unicode(err2)) - if self.options.verbose_level == self.DEBUG_LEVEL: + if self.options.verbose_level >= self.DEBUG_LEVEL: raise else: try: self.clean_up(cmd, result, None) except Exception as err3: - if self.options.verbose_level == self.DEBUG_LEVEL: + if self.options.verbose_level >= self.DEBUG_LEVEL: self.log.exception(unicode(err3)) else: self.log.error(_('Could not clean up: %s'), unicode(err3)) From d9de6b9ecd800a3147030ecea3a75e47a07394b3 Mon Sep 17 00:00:00 2001 From: Feng Xi Yan Date: Thu, 14 Aug 2014 16:20:58 +0800 Subject: [PATCH 071/845] neutronclient shows low-level logs in console screen Neutron command lines always display some useless information. This should be avoided. This commit modifies the default level of console stream to WARNING. Closes-bug: #1356735 Change-Id: I997dad9ce3fc3eb795e4e44b27d211fa619c1907 --- neutronclient/shell.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/neutronclient/shell.py b/neutronclient/shell.py index ce7bed9c9..a84eef219 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -847,7 +847,13 @@ def configure_logging(self): self.INFO_LEVEL: logging.INFO, self.DEBUG_LEVEL: logging.DEBUG, }.get(self.options.verbose_level, logging.DEBUG) - console.setLevel(console_level) + # The default log level is INFO, in this situation, set the + # log level of the console to WARNING, to avoid displaying + # useless messages. This equals using "--quiet" + if console_level == logging.INFO: + console.setLevel(logging.WARNING) + else: + console.setLevel(console_level) if logging.DEBUG == console_level: formatter = logging.Formatter(self.DEBUG_MESSAGE_FORMAT) else: From 74968bd391f6b76356d5d326b77c9ad3ecd14ee7 Mon Sep 17 00:00:00 2001 From: Abhishek Chanda Date: Thu, 14 Aug 2014 12:44:27 -0700 Subject: [PATCH 072/845] Fix typo in cli help The help message for neutron floatingip-disassociate should say the argument is the ID of the floating IP to disassociate Change-Id: I4564d546ca269ab05199a51ffd11678237a4d56f --- neutronclient/neutron/v2_0/floatingip.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/neutron/v2_0/floatingip.py b/neutronclient/neutron/v2_0/floatingip.py index c70960731..e995bcac0 100644 --- a/neutronclient/neutron/v2_0/floatingip.py +++ b/neutronclient/neutron/v2_0/floatingip.py @@ -133,7 +133,7 @@ def get_parser(self, prog_name): parser = super(DisassociateFloatingIP, self).get_parser(prog_name) parser.add_argument( 'floatingip_id', metavar='FLOATINGIP_ID', - help=_('ID of the floating IP to associate.')) + help=_('ID of the floating IP to disassociate.')) return parser def run(self, parsed_args): From 98d2135dbdcc0210328db57dbb7c8fbd28f596be Mon Sep 17 00:00:00 2001 From: Kevin Benton Date: Mon, 18 Aug 2014 13:54:01 -0700 Subject: [PATCH 073/845] Rename --timeout param to --http-timeout The --timeout parameter was already used by the load balancer subcommand so adding it as a top-level parameter broke the load balancer commands. This patch renames the new --timeout top-level command to --http-timeout so it doesn't conflict with the existing load balancer commands. DocImpact Closes-Bug: #1353536 Change-Id: I3d1dd9537e546191c5905e0aa5415de5308d9c7e --- neutronclient/shell.py | 4 ++-- neutronclient/tests/unit/test_shell.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 6f87de1f4..ea5595bb5 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -594,7 +594,7 @@ def _append_global_identity_args(self, parser): help=argparse.SUPPRESS) parser.add_argument( - '--timeout', metavar='', + '--http-timeout', metavar='', default=env('OS_NETWORK_TIMEOUT', default=None), type=float, help=_('Timeout in seconds to wait for an HTTP response. Defaults ' 'to env[OS_NETWORK_TIMEOUT] or None if not specified.')) @@ -801,7 +801,7 @@ def authenticate_user(self): endpoint_type=self.options.os_endpoint_type or self.endpoint_type, insecure=self.options.insecure, ca_cert=self.options.os_cacert, - timeout=self.options.timeout, + timeout=self.options.http_timeout, retries=self.options.retries, raise_errors=False, session=auth_session, diff --git a/neutronclient/tests/unit/test_shell.py b/neutronclient/tests/unit/test_shell.py index 3eb372b74..3cf2cd5d1 100644 --- a/neutronclient/tests/unit/test_shell.py +++ b/neutronclient/tests/unit/test_shell.py @@ -466,11 +466,11 @@ def test_timeout_option(self): # Neither $OS_ENDPOINT_TYPE nor --endpoint-type namespace = parser.parse_args([]) - self.assertIsNone(namespace.timeout) + self.assertIsNone(namespace.http_timeout) # --endpoint-type but not $OS_ENDPOINT_TYPE - namespace = parser.parse_args(['--timeout=50']) - self.assertEqual(50, namespace.timeout) + namespace = parser.parse_args(['--http-timeout=50']) + self.assertEqual(50, namespace.http_timeout) def test_timeout_environment_variable(self): fixture = fixtures.EnvironmentVariable("OS_NETWORK_TIMEOUT", @@ -481,4 +481,4 @@ def test_timeout_environment_variable(self): parser = shell.build_option_parser('descr', '2.0') namespace = parser.parse_args([]) - self.assertEqual(50, namespace.timeout) + self.assertEqual(50, namespace.http_timeout) From 749a5f5f4df2d0b0358cc7f8914af2290fad31a9 Mon Sep 17 00:00:00 2001 From: Aaron-Zhang231 Date: Sat, 15 Feb 2014 05:56:11 -0800 Subject: [PATCH 074/845] Repeat add-tenant and remove-tenant option in cli For cisco-network-profile-create and cisco-network-profile-update cli, add repeatable add-tenant and remove-tenant options so that user can add and remove multiple tenants in a single cli request. This fix includes changes in neutron, python-neutronclient and horizon. All the changes should be merged at the same time Implements: blueprint cisco-network-profile-multi-tenants-support Change-Id: Ia902426b10d22a757ebbf62249dc28e9909dbff0 --- neutronclient/neutron/v2_0/networkprofile.py | 30 ++++++++++++++++--- .../tests/unit/test_cli20_networkprofile.py | 25 ++++++++++++++++ 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/neutronclient/neutron/v2_0/networkprofile.py b/neutronclient/neutron/v2_0/networkprofile.py index 2b693bd4b..8a6054e07 100644 --- a/neutronclient/neutron/v2_0/networkprofile.py +++ b/neutronclient/neutron/v2_0/networkprofile.py @@ -64,7 +64,9 @@ def add_known_arguments(self, parser): parser.add_argument('--multicast_ip_range', help=_('Multicast IPv4 range.')) parser.add_argument("--add-tenant", - help=_("Add tenant to the network profile.")) + action='append', dest='add_tenants', + help=_("Add tenant to the network profile " + "(This option can be repeated).")) def args2body(self, parsed_args): body = {'network_profile': {'name': parsed_args.name}} @@ -83,9 +85,9 @@ def args2body(self, parsed_args): if parsed_args.multicast_ip_range: body['network_profile'].update({'multicast_ip_range': parsed_args.multicast_ip_range}) - if parsed_args.add_tenant: - body['network_profile'].update({'add_tenant': - parsed_args.add_tenant}) + if parsed_args.add_tenants: + body['network_profile'].update({'add_tenants': + parsed_args.add_tenants}) return body @@ -101,7 +103,27 @@ class UpdateNetworkProfile(neutronV20.UpdateCommand): resource = RESOURCE + def add_known_arguments(self, parser): + parser.add_argument("--remove-tenant", + action='append', dest='remove_tenants', + help=_("Remove tenant from the network profile " + "(This option can be repeated)")) + parser.add_argument("--add-tenant", + action='append', dest='add_tenants', + help=_("Add tenant to the network profile " + "(This option can be repeated)")) + + def args2body(self, parsed_args): + body = {'network_profile': {}} + if parsed_args.remove_tenants: + body['network_profile']['remove_tenants'] = (parsed_args. + remove_tenants) + if parsed_args.add_tenants: + body['network_profile']['add_tenants'] = parsed_args.add_tenants + return body + +# Aaron: This function is deprecated class UpdateNetworkProfileV2(neutronV20.NeutronCommand): api = 'network' diff --git a/neutronclient/tests/unit/test_cli20_networkprofile.py b/neutronclient/tests/unit/test_cli20_networkprofile.py index 21e386282..8b955ed66 100644 --- a/neutronclient/tests/unit/test_cli20_networkprofile.py +++ b/neutronclient/tests/unit/test_cli20_networkprofile.py @@ -108,3 +108,28 @@ def test_list_networkprofile_trunk_detail(self): '--sub_type': 'vlan'}] self._test_list_resources(resources, cmd, True, response_contents=contents) + + def test_create_networkprofile_multi_tenants(self): + """Create networkprofile with mulitple tenants: myid.""" + resource = 'network_profile' + cmd = networkprofile.CreateNetworkProfile(test_cli20. + MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + segment_type = 'vlan' + args = [name, segment_type, '--add-tenant', 'demo', + '--add-tenant', 'admin'] + position_names = ['name', 'segment_type', 'add_tenants'] + position_values = [name, segment_type, ['demo', 'admin']] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_update_networkprofile_multi_tenants(self): + resource = 'network_profile' + cmd = networkprofile.UpdateNetworkProfile(test_cli20. + MyApp(sys.stdout), None) + args = ['myid', '--add-tenant', 'service', '--add-tenant', 'demo', + '--remove-tenant', 'demo'] + extrafields = {'add_tenants': ['service', 'demo'], + 'remove_tenants': ['demo']} + self._test_update_resource(resource, cmd, 'myid', args, extrafields) From 155d325c7b88a8d246e4087078c8023512882462 Mon Sep 17 00:00:00 2001 From: Assaf Muller Date: Thu, 21 Aug 2014 20:51:08 +0300 Subject: [PATCH 075/845] Refactor CreateRouter to use update_dict At this point in time, 'distributed' is a positional argument and thus doesn't need to be moved into the resource body. Change-Id: I1d5c7c360fc76b5da9e145d70ce6d9e7a90d80bc --- neutronclient/neutron/v2_0/router.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index 2f6a3c8d5..758e3a0da 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -70,11 +70,9 @@ def add_known_arguments(self, parser): help=_('Create a distributed router.')) def args2body(self, parsed_args): - body = {'router': { - 'name': parsed_args.name, - 'admin_state_up': parsed_args.admin_state, }, } - if parsed_args.tenant_id: - body['router'].update({'tenant_id': parsed_args.tenant_id}) + body = {self.resource: {'admin_state_up': parsed_args.admin_state}} + neutronV20.update_dict(parsed_args, body[self.resource], + ['name', 'tenant_id']) return body From deb850b7282995d7e3b31210c177a2661578765b Mon Sep 17 00:00:00 2001 From: Assaf Muller Date: Mon, 21 Jul 2014 16:37:23 +0300 Subject: [PATCH 076/845] Fix CLI support for DVR, take 2 Previously --distributed was displayed as positional argument in the help text, but acted as an optional argument with the -- semantics. It now behaves like any other optional argument. A side-effect is that previously you could: 1) neutron router-create --distributed r1 2) neutron router-create --distributed=True r1 3) neutron router-create --distributed=False r1 While now only options 2 and 3 are viable. DocImpact Partially-implements: blueprint neutron-ovs-dvr Closes-Bug: #1346121 Change-Id: I60675b4f60fe8154af4ab6d8f0e93867cf362a59 --- neutronclient/neutron/v2_0/router.py | 7 +++++-- neutronclient/tests/unit/test_cli20_router.py | 7 ++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index 758e3a0da..5d03877a9 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -66,13 +66,16 @@ def add_known_arguments(self, parser): 'name', metavar='NAME', help=_('Name of router to create.')) parser.add_argument( - 'distributed', action='store_true', + '--distributed', + dest='distributed', + choices=['True', 'False'], + default=argparse.SUPPRESS, help=_('Create a distributed router.')) def args2body(self, parsed_args): body = {self.resource: {'admin_state_up': parsed_args.admin_state}} neutronV20.update_dict(parsed_args, body[self.resource], - ['name', 'tenant_id']) + ['name', 'tenant_id', 'distributed']) return body diff --git a/neutronclient/tests/unit/test_cli20_router.py b/neutronclient/tests/unit/test_cli20_router.py index 274819ce2..372c15ff1 100644 --- a/neutronclient/tests/unit/test_cli20_router.py +++ b/neutronclient/tests/unit/test_cli20_router.py @@ -61,17 +61,18 @@ def test_create_router_admin_state(self): admin_state_up=False) def test_create_router_distributed(self): - """Create router: --distributed myname.""" + """Create router: --distributed=True myname.""" resource = 'router' cmd = router.CreateRouter(test_cli20.MyApp(sys.stdout), None) name = 'myname' myid = 'myid' - args = ['--distributed', name, ] + distributed = 'True' + args = ['--distributed', distributed, name, ] position_names = ['name', ] position_values = [name, ] self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values, - distributed=True) + distributed=distributed) def test_list_routers_detail(self): """list routers: -D.""" From a71611f9165bf168706707f06708acbdfa0ba250 Mon Sep 17 00:00:00 2001 From: Stanislav Kudriashev Date: Thu, 21 Aug 2014 16:04:21 +0300 Subject: [PATCH 077/845] Small improve of str2dict function Change-Id: Ifa8974f2cbdac4fdc0cbd47d7ad9910a5bd31cb7 --- neutronclient/common/utils.py | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/neutronclient/common/utils.py b/neutronclient/common/utils.py index b26166d2b..87dec4c53 100644 --- a/neutronclient/common/utils.py +++ b/neutronclient/common/utils.py @@ -138,22 +138,17 @@ def get_item_properties(item, fields, mixed_case_fields=[], formatters={}): def str2bool(strbool): if strbool is None: return None - else: - return strbool.lower() == 'true' + return strbool.lower() == 'true' def str2dict(strdict): - '''Convert key1=value1,key2=value2,... string into dictionary. - - :param strdict: key1=value1,key2=value2 - ''' - _info = {} - if not strdict: - return _info - for kv_str in strdict.split(","): - k, v = kv_str.split("=", 1) - _info.update({k: v}) - return _info + """Convert key1=value1,key2=value2,... string into dictionary. + + :param strdict: key1=value1,key2=value2 + """ + if not strdict: + return {} + return dict([kv.split('=', 1) for kv in strdict.split(',')]) def http_log_req(_logger, args, kwargs): From 48da7226c01e3b69e8a53f9b1e42597c56d15483 Mon Sep 17 00:00:00 2001 From: Stanislav Kudriashev Date: Thu, 21 Aug 2014 15:45:10 +0300 Subject: [PATCH 078/845] Unify doc-strings format Change-Id: If6cf2caeae294db30a8988ab861441211b651580 --- neutronclient/client.py | 2 +- neutronclient/common/clientmanager.py | 6 ++---- neutronclient/common/command.py | 7 +----- neutronclient/common/exceptions.py | 7 +++--- neutronclient/common/serializer.py | 1 - neutronclient/common/utils.py | 4 ++-- neutronclient/neutron/client.py | 4 ++-- neutronclient/neutron/v2_0/__init__.py | 30 +++++++++----------------- neutronclient/v2_0/client.py | 21 ++++++++---------- 9 files changed, 30 insertions(+), 52 deletions(-) diff --git a/neutronclient/client.py b/neutronclient/client.py index 8720f1fbf..1f66ec149 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -59,7 +59,7 @@ def get_status_code(self, response): class HTTPClient(NeutronClientMixin): - """Handles the REST calls and responses, include authn.""" + """Handles the REST calls and responses, include authentication.""" def __init__(self, username=None, user_id=None, tenant_name=None, tenant_id=None, diff --git a/neutronclient/common/clientmanager.py b/neutronclient/common/clientmanager.py index 283bc77bf..e9cc88574 100644 --- a/neutronclient/common/clientmanager.py +++ b/neutronclient/common/clientmanager.py @@ -27,8 +27,7 @@ class ClientCache(object): - """Descriptor class for caching created client handles. - """ + """Descriptor class for caching created client handles.""" def __init__(self, factory): self.factory = factory @@ -42,8 +41,7 @@ def __get__(self, instance, owner): class ClientManager(object): - """Manages access to API clients, including authentication. - """ + """Manages access to API clients, including authentication.""" neutron = ClientCache(neutron_client.make_client) # Provide support for old quantum commands (for example # in stable versions) diff --git a/neutronclient/common/command.py b/neutronclient/common/command.py index 2cd32935a..3d0540744 100644 --- a/neutronclient/common/command.py +++ b/neutronclient/common/command.py @@ -14,16 +14,11 @@ # under the License. # -""" -OpenStack base command -""" - from cliff import command class OpenStackCommand(command.Command): - """Base class for OpenStack commands - """ + """Base class for OpenStack commands.""" api = None diff --git a/neutronclient/common/exceptions.py b/neutronclient/common/exceptions.py index 4c21e62b6..2fed2ca2f 100644 --- a/neutronclient/common/exceptions.py +++ b/neutronclient/common/exceptions.py @@ -30,12 +30,11 @@ class NeutronException(Exception): - """Base Neutron Exception + """Base Neutron Exception. To correctly use this class, inherit from it and define a 'message' property. That message will get printf'd with the keyword arguments provided to the constructor. - """ message = _("An unknown exception occurred.") @@ -218,8 +217,8 @@ class CommandError(NeutronCLIError): class UnsupportedVersion(NeutronCLIError): - """Indicates that the user is trying to use an unsupported - version of the API + """Indicates that the user is trying to use an unsupported version of + the API. """ pass diff --git a/neutronclient/common/serializer.py b/neutronclient/common/serializer.py index e2cd7641b..73f0bec2e 100644 --- a/neutronclient/common/serializer.py +++ b/neutronclient/common/serializer.py @@ -393,7 +393,6 @@ def deserialize(self, datastring, content_type): """Deserialize a string to a dictionary. The string must be in the format of a supported MIME type. - """ return self.get_deserialize_handler(content_type).deserialize( datastring) diff --git a/neutronclient/common/utils.py b/neutronclient/common/utils.py index 87dec4c53..577e963cb 100644 --- a/neutronclient/common/utils.py +++ b/neutronclient/common/utils.py @@ -33,7 +33,7 @@ def env(*vars, **kwargs): """Returns the first environment variable set. - if none are non-empty, defaults to '' or keyword arg default. + If none are non-empty, defaults to '' or keyword arg default. """ for v in vars: value = os.environ.get(v) @@ -87,7 +87,7 @@ def import_class(import_str): def get_client_class(api_name, version, version_map): - """Returns the client class for the requested API version + """Returns the client class for the requested API version. :param api_name: the name of the API, e.g. 'compute', 'image', etc :param version: the requested API version diff --git a/neutronclient/neutron/client.py b/neutronclient/neutron/client.py index df7ef2996..40eb9382a 100644 --- a/neutronclient/neutron/client.py +++ b/neutronclient/neutron/client.py @@ -26,8 +26,7 @@ def make_client(instance): - """Returns an neutron client. - """ + """Returns an neutron client.""" neutron_client = utils.get_client_class( API_NAME, instance._api_version[API_NAME], @@ -60,6 +59,7 @@ def make_client(instance): def Client(api_version, *args, **kwargs): """Return an neutron client. + @param api_version: only 2.0 is supported now """ neutron_client = utils.get_client_class( diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index 20bbbff27..39a566040 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -198,7 +198,7 @@ def _process_previous_argument(current_arg, _value_number, current_type_str, def parse_args_to_dict(values_specs): - '''It is used to analyze the extra command options to command. + """It is used to analyze the extra command options to command. Besides known options and arguments, our commands also support user to put more options to the end of command line. For example, @@ -210,8 +210,7 @@ def parse_args_to_dict(values_specs): value spec is: --key type=int|bool|... value. Type is one of Python built-in types. By default, type is string. The key without value is a bool option. Key with two values will be a list option. - - ''' + """ # values_specs for example: '-- --tag x y --key1 type=int value1' # -- is a pseudo argument @@ -295,7 +294,7 @@ def parse_args_to_dict(values_specs): current_arg, _value_number, current_type_str, _list_flag, _values_specs, _clear_flag, values_specs) - # populate the parser with arguments + # Populate the parser with arguments _parser = argparse.ArgumentParser(add_help=False) for opt, optspec in six.iteritems(_options): _parser.add_argument(opt, **optspec) @@ -334,7 +333,7 @@ def _merge_args(qCmd, parsed_args, _extra_values, value_specs): def update_dict(obj, dict, attributes): - """Update dict with fields from obj.attributes + """Update dict with fields from obj.attributes. :param obj: the object updated into dict :param dict: the result dictionary @@ -434,9 +433,7 @@ def args2body(self, parsed_args): class CreateCommand(NeutronCommand, show.ShowOne): - """Create a resource for a given tenant - - """ + """Create a resource for a given tenant.""" api = 'network' log = None @@ -478,8 +475,7 @@ def get_data(self, parsed_args): class UpdateCommand(NeutronCommand): - """Update resource's information - """ + """Update resource's information.""" api = 'network' log = None @@ -530,9 +526,7 @@ def run(self, parsed_args): class DeleteCommand(NeutronCommand): - """Delete a given resource - - """ + """Delete a given resource.""" api = 'network' log = None @@ -577,9 +571,7 @@ def run(self, parsed_args): class ListCommand(NeutronCommand, lister.Lister): - """List resources that belong to a given tenant - - """ + """List resources that belong to a given tenant.""" api = 'network' log = None @@ -618,7 +610,7 @@ def call_server(self, neutron_client, search_opts, parsed_args): return data def retrieve_list(self, parsed_args): - """Retrieve a list of resources from Neutron server""" + """Retrieve a list of resources from Neutron server.""" neutron_client = self.get_client() neutron_client.format = parsed_args.request_format _extra_values = parse_args_to_dict(self.values_specs) @@ -679,9 +671,7 @@ def get_data(self, parsed_args): class ShowCommand(NeutronCommand, show.ShowOne): - """Show information of a given resource - - """ + """Show information of a given resource.""" api = 'network' log = None diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 49a49f2ad..56185c027 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -33,14 +33,13 @@ def exception_handler_v20(status_code, error_content): - """Exception handler for API v2.0 client + """Exception handler for API v2.0 client. - This routine generates the appropriate - Neutron exception according to the contents of the - response body + This routine generates the appropriate Neutron exception according to + the contents of the response body. - :param status_code: HTTP error status code - :param error_content: deserialized body of error response + :param status_code: HTTP error status code + :param error_content: deserialized body of error response """ error_dict = None if isinstance(error_content, dict): @@ -87,8 +86,7 @@ def exception_handler_v20(status_code, error_content): class APIParamsCall(object): - """A Decorator to add support for format and tenant overriding - and filters + """A Decorator to add support for format and tenant overriding and filters. """ def __init__(self, function): self.function = function @@ -273,8 +271,7 @@ def get_attr_metadata(self): @APIParamsCall def get_quotas_tenant(self, **_params): - """Fetch tenant info in server's context for - following quota operation. + """Fetch tenant info in server's context for following quota operation. """ return self.get(self.quota_path % 'tenant', params=_params) @@ -1268,8 +1265,8 @@ def get_status_code(self, response): def serialize(self, data): """Serializes a dictionary into either XML or JSON. - A dictionary with a single key can be passed and - it can contain any structure. + A dictionary with a single key can be passed and it can contain any + structure. """ if data is None: return None From 9f3ffdf5bcb91425b3947bbf0d1d24c89791859b Mon Sep 17 00:00:00 2001 From: Ilya Shakhat Date: Thu, 28 Aug 2014 16:56:34 +0400 Subject: [PATCH 079/845] Remove unnecessary get_status_code wrapper function Function get_status_code is used to get HTTP status code from either requests.Response (status_code) or Webob.Response (status_int). The latter was removed in 2012 and not needed anymore. Change-Id: Iacd6bbb83ce0d8ec5b8ec73273bc7bf39c9ccc8d --- neutronclient/client.py | 17 ++--------------- neutronclient/v2_0/client.py | 13 +------------ 2 files changed, 3 insertions(+), 27 deletions(-) diff --git a/neutronclient/client.py b/neutronclient/client.py index 8720f1fbf..b9f981286 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -46,17 +46,6 @@ class NeutronClientMixin(object): USER_AGENT = 'python-neutronclient' - def get_status_code(self, response): - """Returns the integer status code from the response. - - Either a Webob.Response (used in testing) or requests.Response - is returned. - """ - if hasattr(response, 'status_int'): - return response.status_int - else: - return response.status_code - class HTTPClient(NeutronClientMixin): """Handles the REST calls and responses, include authn.""" @@ -127,8 +116,7 @@ def _cs_request(self, *args, **kwargs): _logger.debug("throwing ConnectionFailed : %s", e) raise exceptions.ConnectionFailed(reason=e) utils.http_log_resp(_logger, resp, body) - status_code = self.get_status_code(resp) - if status_code == 401: + if resp.status_code == 401: raise exceptions.Unauthorized(message=body) return resp, body @@ -222,8 +210,7 @@ def _authenticate_keystone(self): body=json.dumps(body), content_type="application/json", allow_redirects=True) - status_code = self.get_status_code(resp) - if status_code != 200: + if resp.status_code != 200: raise exceptions.Unauthorized(message=resp_body) if resp_body: try: diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 49a49f2ad..e28d699b7 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -1240,7 +1240,7 @@ def do_request(self, method, action, body=None, headers=None, params=None): body = self.serialize(body) self.httpclient.content_type = self.content_type() resp, replybody = self.httpclient.do_request(action, method, body=body) - status_code = self.get_status_code(resp) + status_code = resp.status_code if status_code in (requests.codes.ok, requests.codes.created, requests.codes.accepted, @@ -1254,17 +1254,6 @@ def do_request(self, method, action, body=None, headers=None, params=None): def get_auth_info(self): return self.httpclient.get_auth_info() - def get_status_code(self, response): - """Returns the integer status code from the response. - - Either a Webob.Response (used in testing) or requests.Response - is returned. - """ - if hasattr(response, 'status_int'): - return response.status_int - else: - return response.status_code - def serialize(self, data): """Serializes a dictionary into either XML or JSON. From f22dbd20446ef5d316ffd3fffb630cf2731b8229 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Sat, 30 Aug 2014 08:36:03 +0000 Subject: [PATCH 080/845] Updated from global requirements Change-Id: I0628d1071955a7f82efd1e1e054fa38ae1dff006 --- requirements.txt | 4 ++-- test-requirements.txt | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/requirements.txt b/requirements.txt index 6c00c26ae..40650f2d4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,8 +3,8 @@ argparse cliff>=1.6.0 iso8601>=0.1.9 netaddr>=0.7.6 -requests>=1.1 +requests>=1.2.1 python-keystoneclient>=0.10.0 -simplejson>=2.0.9 +simplejson>=2.2.0 six>=1.7.0 Babel>=1.3 diff --git a/test-requirements.txt b/test-requirements.txt index 9a4d7e311..44eca620a 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -4,10 +4,10 @@ cliff-tablib>=1.0 coverage>=3.6 discover fixtures>=0.3.14 -httpretty>=0.8.0,!=0.8.1,!=0.8.2 +httpretty>=0.8.0,!=0.8.1,!=0.8.2,!=0.8.3 mox3>=0.7.0 -oslosphinx -oslotest +oslosphinx>=2.2.0.0a2 +oslotest>=1.1.0.0a1 python-subunit>=0.0.18 sphinx>=1.1.2,!=1.2.0,<1.3 testrepository>=0.0.18 From 34e334154c79779a5b16a78aebb235d532950a06 Mon Sep 17 00:00:00 2001 From: Stanislav Kudriashev Date: Thu, 21 Aug 2014 14:42:48 +0300 Subject: [PATCH 081/845] Clean-up shell run and run_subcommand methods Remove else branch of try-catch block since it is never executed. Get rid of passing result to shell clean-up function since it is not used. Change-Id: I9360d1e27fc203672ba0511b58172f5c776bdfb6 --- neutronclient/shell.py | 39 ++++++--------------------------------- 1 file changed, 6 insertions(+), 33 deletions(-) diff --git a/neutronclient/shell.py b/neutronclient/shell.py index ea5595bb5..6ed13e39d 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -671,21 +671,16 @@ def run(self, argv): else: self.log.error(unicode(err)) return 1 - result = 1 if self.interactive_mode: _argv = [sys.argv[0]] sys.argv = _argv - result = self.interact() - else: - result = self.run_subcommand(remainder) - return result + return self.interact() + return self.run_subcommand(remainder) def run_subcommand(self, argv): subcommand = self.command_manager.find_command(argv) cmd_factory, cmd_name, sub_argv = subcommand cmd = cmd_factory(self, self.options) - err = None - result = 1 try: self.prepare_to_run_command(cmd) full_name = (cmd_name @@ -694,29 +689,12 @@ def run_subcommand(self, argv): ) cmd_parser = cmd.get_parser(full_name) return run_command(cmd, cmd_parser, sub_argv) - except Exception as err: - if self.options.verbose_level >= self.DEBUG_LEVEL: - self.log.exception(unicode(err)) - else: - self.log.error(unicode(err)) - try: - self.clean_up(cmd, result, err) - except Exception as err2: - if self.options.verbose_level >= self.DEBUG_LEVEL: - self.log.exception(unicode(err2)) - else: - self.log.error(_('Could not clean up: %s'), unicode(err2)) + except Exception as e: if self.options.verbose_level >= self.DEBUG_LEVEL: + self.log.exception("%s", e) raise - else: - try: - self.clean_up(cmd, result, None) - except Exception as err3: - if self.options.verbose_level >= self.DEBUG_LEVEL: - self.log.exception(unicode(err3)) - else: - self.log.error(_('Could not clean up: %s'), unicode(err3)) - return result + self.log.error("%s", e) + return 1 def authenticate_user(self): """Make sure the user has provided all of the authentication @@ -829,11 +807,6 @@ def initialize_app(self, argv): if self.interactive_mode or cmd_name != 'help': self.authenticate_user() - def clean_up(self, cmd, result, err): - self.log.debug('clean_up %s', cmd.__class__.__name__) - if err: - self.log.debug('Got an error: %s', unicode(err)) - def configure_logging(self): """Create logging handlers for any log output.""" root_logger = logging.getLogger('') From 42731a21e6c2cf22fc0f781409b71afd1b56abf6 Mon Sep 17 00:00:00 2001 From: Jeremy Stanley Date: Wed, 3 Sep 2014 19:05:43 +0000 Subject: [PATCH 082/845] Work toward Python 3.4 support and testing Change-Id: I5ba556b1abb65050cffed76e354c164754552232 --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index bac7c7607..19a63cc68 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py26,py27,py33,pypy,pep8 +envlist = py26,py27,py33,py34,pypy,pep8 minversion = 1.6 skipsdist = True From 54a2d3d866989eff0c2a2f972b5d854fdaa60007 Mon Sep 17 00:00:00 2001 From: Aaron Rosen Date: Wed, 3 Sep 2014 21:36:27 -0700 Subject: [PATCH 083/845] Leverage openstack.common.jsonutils This patch removes our own home brewed json helper functions and leverages the ones from openstack.common.jsonutils instead. Change-Id: Idc5ee0fec56b0a0f337433a830647d4f421a8283 Closes-bug: 1365263 --- neutronclient/common/utils.py | 37 --------- neutronclient/neutron/v2_0/__init__.py | 5 +- neutronclient/neutron/v2_0/port.py | 3 +- neutronclient/neutron/v2_0/quota.py | 5 +- neutronclient/neutron/v2_0/subnet.py | 7 +- neutronclient/tests/unit/test_cli20_agents.py | 6 +- .../tests/unit/test_cli20_network.py | 6 +- neutronclient/tests/unit/test_utils.py | 77 ------------------- 8 files changed, 18 insertions(+), 128 deletions(-) diff --git a/neutronclient/common/utils.py b/neutronclient/common/utils.py index 1c78577f5..af0ddb933 100644 --- a/neutronclient/common/utils.py +++ b/neutronclient/common/utils.py @@ -17,14 +17,10 @@ """Utilities and helper functions.""" -import datetime -import json import logging import os import sys -import six - from neutronclient.common import _ from neutronclient.common import exceptions from neutronclient.openstack.common import strutils @@ -42,39 +38,6 @@ def env(*vars, **kwargs): return kwargs.get('default', '') -def to_primitive(value): - if isinstance(value, list) or isinstance(value, tuple): - o = [] - for v in value: - o.append(to_primitive(v)) - return o - elif isinstance(value, dict): - o = {} - for k, v in six.iteritems(value): - o[k] = to_primitive(v) - return o - elif isinstance(value, datetime.datetime): - return str(value) - elif hasattr(value, 'iteritems'): - return to_primitive(dict(value.iteritems())) - elif hasattr(value, '__iter__'): - return to_primitive(list(value)) - else: - return value - - -def dumps(value, indent=None): - try: - return json.dumps(value, indent=indent) - except TypeError: - pass - return json.dumps(to_primitive(value)) - - -def loads(s): - return json.loads(s) - - def import_class(import_str): """Returns a class from a string including module and class. diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index 20bbbff27..66f19192a 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -30,6 +30,7 @@ from neutronclient.common import exceptions from neutronclient.common import utils from neutronclient.openstack.common.gettextutils import _ +from neutronclient.openstack.common import jsonutils HEX_ELEM = '[0-9A-Fa-f]' UUID_PATTERN = '-'.join([HEX_ELEM + '{8}', HEX_ELEM + '{4}', @@ -416,12 +417,12 @@ def format_output_data(self, data): if self.resource in data: for k, v in six.iteritems(data[self.resource]): if isinstance(v, list): - value = '\n'.join(utils.dumps( + value = '\n'.join(jsonutils.dumps( i, indent=self.json_indent) if isinstance(i, dict) else str(i) for i in v) data[self.resource][k] = value elif isinstance(v, dict): - value = utils.dumps(v, indent=self.json_indent) + value = jsonutils.dumps(v, indent=self.json_indent) data[self.resource][k] = value elif v is None: data[self.resource][k] = '' diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index 1cc0e0ca3..2aad26b82 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -20,11 +20,12 @@ from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronV20 from neutronclient.openstack.common.gettextutils import _ +from neutronclient.openstack.common import jsonutils def _format_fixed_ips(port): try: - return '\n'.join([utils.dumps(ip) for ip in port['fixed_ips']]) + return '\n'.join([jsonutils.dumps(ip) for ip in port['fixed_ips']]) except Exception: return '' diff --git a/neutronclient/neutron/v2_0/quota.py b/neutronclient/neutron/v2_0/quota.py index af8076e15..6e5ad174f 100644 --- a/neutronclient/neutron/v2_0/quota.py +++ b/neutronclient/neutron/v2_0/quota.py @@ -26,6 +26,7 @@ from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronV20 from neutronclient.openstack.common.gettextutils import _ +from neutronclient.openstack.common import jsonutils def get_tenant_id(tenant_id, client): @@ -128,7 +129,7 @@ def get_data(self, parsed_args): if value: value += "\n" if isinstance(_item, dict): - value += utils.dumps(_item) + value += jsonutils.dumps(_item) else: value += str(_item) data[self.resource][k] = value @@ -233,7 +234,7 @@ def get_data(self, parsed_args): if value: value += "\n" if isinstance(_item, dict): - value += utils.dumps(_item) + value += jsonutils.dumps(_item) else: value += str(_item) data[self.resource][k] = value diff --git a/neutronclient/neutron/v2_0/subnet.py b/neutronclient/neutron/v2_0/subnet.py index 6ab8ae2c7..ece163258 100644 --- a/neutronclient/neutron/v2_0/subnet.py +++ b/neutronclient/neutron/v2_0/subnet.py @@ -20,11 +20,12 @@ from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronV20 from neutronclient.openstack.common.gettextutils import _ +from neutronclient.openstack.common import jsonutils def _format_allocation_pools(subnet): try: - return '\n'.join([utils.dumps(pool) for pool in + return '\n'.join([jsonutils.dumps(pool) for pool in subnet['allocation_pools']]) except Exception: return '' @@ -32,7 +33,7 @@ def _format_allocation_pools(subnet): def _format_dns_nameservers(subnet): try: - return '\n'.join([utils.dumps(server) for server in + return '\n'.join([jsonutils.dumps(server) for server in subnet['dns_nameservers']]) except Exception: return '' @@ -40,7 +41,7 @@ def _format_dns_nameservers(subnet): def _format_host_routes(subnet): try: - return '\n'.join([utils.dumps(route) for route in + return '\n'.join([jsonutils.dumps(route) for route in subnet['host_routes']]) except Exception: return '' diff --git a/neutronclient/tests/unit/test_cli20_agents.py b/neutronclient/tests/unit/test_cli20_agents.py index 699201955..710f4ba6c 100644 --- a/neutronclient/tests/unit/test_cli20_agents.py +++ b/neutronclient/tests/unit/test_cli20_agents.py @@ -16,8 +16,8 @@ import sys -from neutronclient.common import utils from neutronclient.neutron.v2_0 import agent +from neutronclient.openstack.common import jsonutils from neutronclient.tests.unit import test_cli20 @@ -32,7 +32,7 @@ def test_list_agents(self): self._test_list_columns(cmd, resources, contents, args) _str = self.fake_stdout.make_string() - returned_agents = utils.loads(_str) + returned_agents = jsonutils.loads(_str) self.assertEqual(1, len(returned_agents)) ag = returned_agents[0] self.assertEqual(3, len(ag)) @@ -48,7 +48,7 @@ def test_list_agents_field(self): self._test_list_columns(cmd, resources, contents, args) _str = self.fake_stdout.make_string() - returned_agents = utils.loads(_str) + returned_agents = jsonutils.loads(_str) self.assertEqual(1, len(returned_agents)) ag = returned_agents[0] self.assertEqual(1, len(ag)) diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index 0b68e1be8..a3b964d4e 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -19,8 +19,8 @@ from mox3 import mox from neutronclient.common import exceptions -from neutronclient.common import utils from neutronclient.neutron.v2_0 import network +from neutronclient.openstack.common import jsonutils from neutronclient import shell from neutronclient.tests.unit import test_cli20 @@ -282,7 +282,7 @@ def test_list_nets_defined_column(self): self._test_list_nets_columns(cmd, returned_body, args=['-f', 'json', '-c', 'id']) _str = self.fake_stdout.make_string() - returned_networks = utils.loads(_str) + returned_networks = jsonutils.loads(_str) self.assertEqual(1, len(returned_networks)) net = returned_networks[0] self.assertEqual(1, len(net)) @@ -296,7 +296,7 @@ def test_list_nets_with_default_column(self): "subnets": []}]} self._test_list_nets_columns(cmd, returned_body) _str = self.fake_stdout.make_string() - returned_networks = utils.loads(_str) + returned_networks = jsonutils.loads(_str) self.assertEqual(1, len(returned_networks)) net = returned_networks[0] self.assertEqual(3, len(net)) diff --git a/neutronclient/tests/unit/test_utils.py b/neutronclient/tests/unit/test_utils.py index 44c08707f..f38783512 100644 --- a/neutronclient/tests/unit/test_utils.py +++ b/neutronclient/tests/unit/test_utils.py @@ -13,10 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. -import datetime import sys -import six import testtools from neutronclient.common import exceptions @@ -107,81 +105,6 @@ def __call__(self, *args, **kwargs): self.assertEqual(('test_name', 'test_id', 'test', 'pass'), act) -class JSONUtilsTestCase(testtools.TestCase): - def test_dumps(self): - self.assertEqual(utils.dumps({'a': 'b'}), '{"a": "b"}') - - def test_dumps_dict_with_date_value(self): - x = datetime.datetime(1920, 2, 3, 4, 5, 6, 7) - res = utils.dumps({1: 'a', 2: x}) - expected = '{"1": "a", "2": "1920-02-03 04:05:06.000007"}' - self.assertEqual(expected, res) - - def test_dumps_dict_with_spaces(self): - x = datetime.datetime(1920, 2, 3, 4, 5, 6, 7) - res = utils.dumps({1: 'a ', 2: x}) - expected = '{"1": "a ", "2": "1920-02-03 04:05:06.000007"}' - self.assertEqual(expected, res) - - def test_loads(self): - self.assertEqual(utils.loads('{"a": "b"}'), {'a': 'b'}) - - -class ToPrimitiveTestCase(testtools.TestCase): - def test_list(self): - self.assertEqual(utils.to_primitive([1, 2, 3]), [1, 2, 3]) - - def test_empty_list(self): - self.assertEqual(utils.to_primitive([]), []) - - def test_tuple(self): - self.assertEqual(utils.to_primitive((1, 2, 3)), [1, 2, 3]) - - def test_empty_tuple(self): - self.assertEqual(utils.to_primitive(()), []) - - def test_dict(self): - self.assertEqual( - utils.to_primitive(dict(a=1, b=2, c=3)), - dict(a=1, b=2, c=3)) - - def test_empty_dict(self): - self.assertEqual(utils.to_primitive({}), {}) - - def test_datetime(self): - x = datetime.datetime(1920, 2, 3, 4, 5, 6, 7) - self.assertEqual( - utils.to_primitive(x), - '1920-02-03 04:05:06.000007') - - def test_iter(self): - x = range(1, 6) - self.assertEqual(utils.to_primitive(x), [1, 2, 3, 4, 5]) - - def test_iteritems(self): - d = {'a': 1, 'b': 2, 'c': 3} - - class IterItemsClass(object): - def iteritems(self): - return six.iteritems(d) - - x = IterItemsClass() - p = utils.to_primitive(x) - self.assertEqual(p, {'a': 1, 'b': 2, 'c': 3}) - - def test_nasties(self): - def foo(): - pass - x = [datetime, foo, dir] - ret = utils.to_primitive(x) - self.assertEqual(len(ret), 3) - - def test_to_primitive_dict_with_date_value(self): - x = datetime.datetime(1920, 2, 3, 4, 5, 6, 7) - res = utils.to_primitive({'a': x}) - self.assertEqual({'a': '1920-02-03 04:05:06.000007'}, res) - - class ImportClassTestCase(testtools.TestCase): def test_import_class(self): dt = utils.import_class('datetime.datetime') From e17e81db7e507bcdb530397e6f9f99d792f96eb0 Mon Sep 17 00:00:00 2001 From: Angus Lees Date: Wed, 13 Aug 2014 10:08:17 +1000 Subject: [PATCH 084/845] Isolate tests from HTTP_PROXY, OS_REGION_NAME env vars CLI and shell tests call into a mock httpretty REST service. If HTTP_PROXY is set, urllib2/requests follows it and fails to hit the mocked httpretty service as expected. Also: The shell tests assumed OS_REGION_NAME was unset, so also clear this. Change-Id: I9a3f4981ee5c32203f9127e9eb8eefe5efd1b6dc Closes-Bug: #1367102 Closes-Bug: #1367105 --- neutronclient/tests/unit/test_auth.py | 4 ++++ neutronclient/tests/unit/test_shell.py | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/neutronclient/tests/unit/test_auth.py b/neutronclient/tests/unit/test_auth.py index 537236a60..42f674582 100644 --- a/neutronclient/tests/unit/test_auth.py +++ b/neutronclient/tests/unit/test_auth.py @@ -17,6 +17,7 @@ import json import uuid +import fixtures import httpretty from mox3 import mox import requests @@ -216,6 +217,9 @@ def setUp(self): super(CLITestAuthKeystone, self).setUp() self.mox = mox.Mox() + for var in ('http_proxy', 'HTTP_PROXY'): + self.useFixture(fixtures.EnvironmentVariableFixture(var)) + self.client = client.construct_http_client( username=USERNAME, tenant_name=TENANT_NAME, diff --git a/neutronclient/tests/unit/test_shell.py b/neutronclient/tests/unit/test_shell.py index 3cf2cd5d1..499e13821 100644 --- a/neutronclient/tests/unit/test_shell.py +++ b/neutronclient/tests/unit/test_shell.py @@ -51,7 +51,11 @@ class ShellTest(testtools.TestCase): 'OS_PASSWORD': DEFAULT_PASSWORD, 'OS_TENANT_ID': DEFAULT_TENANT_ID, 'OS_TENANT_NAME': DEFAULT_TENANT_NAME, - 'OS_AUTH_URL': DEFAULT_AUTH_URL} + 'OS_AUTH_URL': DEFAULT_AUTH_URL, + 'OS_REGION_NAME': None, + 'HTTP_PROXY': None, + 'http_proxy': None, + } # Patch os.environ to avoid required auth info. def setUp(self): From a844d19861c092dcb71288e15752d290c4062e0a Mon Sep 17 00:00:00 2001 From: Andreas Jaeger Date: Mon, 8 Sep 2014 07:09:27 +0200 Subject: [PATCH 085/845] Improve help strings Improve consistency of help strings: Add missing ".", fix wording. Change-Id: If9dba459dcbbf695b9057e82525a83371ceebd8b --- neutronclient/neutron/v2_0/fw/firewallrule.py | 2 +- neutronclient/neutron/v2_0/networkprofile.py | 12 ++++++------ neutronclient/shell.py | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/neutronclient/neutron/v2_0/fw/firewallrule.py b/neutronclient/neutron/v2_0/fw/firewallrule.py index 88f47e932..aa4b90c53 100644 --- a/neutronclient/neutron/v2_0/fw/firewallrule.py +++ b/neutronclient/neutron/v2_0/fw/firewallrule.py @@ -98,7 +98,7 @@ def add_known_arguments(self, parser): parser.add_argument( '--enabled', dest='enabled', choices=['True', 'False'], - help=_('To enable or disable this rule'), + help=_('Whether to enable or disable this rule.'), default=argparse.SUPPRESS) parser.add_argument( '--protocol', choices=['tcp', 'udp', 'icmp', 'any'], diff --git a/neutronclient/neutron/v2_0/networkprofile.py b/neutronclient/neutron/v2_0/networkprofile.py index 8a6054e07..def744184 100644 --- a/neutronclient/neutron/v2_0/networkprofile.py +++ b/neutronclient/neutron/v2_0/networkprofile.py @@ -65,8 +65,8 @@ def add_known_arguments(self, parser): help=_('Multicast IPv4 range.')) parser.add_argument("--add-tenant", action='append', dest='add_tenants', - help=_("Add tenant to the network profile " - "(This option can be repeated).")) + help=_("Add tenant to the network profile. " + "You can repeat this option.")) def args2body(self, parsed_args): body = {'network_profile': {'name': parsed_args.name}} @@ -106,12 +106,12 @@ class UpdateNetworkProfile(neutronV20.UpdateCommand): def add_known_arguments(self, parser): parser.add_argument("--remove-tenant", action='append', dest='remove_tenants', - help=_("Remove tenant from the network profile " - "(This option can be repeated)")) + help=_("Remove tenant from the network profile. " + "You can repeat this option.")) parser.add_argument("--add-tenant", action='append', dest='add_tenants', - help=_("Add tenant to the network profile " - "(This option can be repeated)")) + help=_("Add tenant to the network profile. " + "You can repeat this option.")) def args2body(self, parsed_args): body = {'network_profile': {}} diff --git a/neutronclient/shell.py b/neutronclient/shell.py index ea5595bb5..0617c451b 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -549,7 +549,7 @@ def _append_global_identity_args(self, parser): help=_("Path of certificate file to use in SSL " "connection. This file can optionally be " "prepended with the private key. Defaults " - "to env[OS_CERT]")) + "to env[OS_CERT].")) parser.add_argument( '--os-cacert', @@ -557,7 +557,7 @@ def _append_global_identity_args(self, parser): default=env('OS_CACERT', default=None), help=_("Specify a CA bundle file to use in " "verifying a TLS (https) server certificate. " - "Defaults to env[OS_CACERT]")) + "Defaults to env[OS_CACERT].")) parser.add_argument( '--os-key', @@ -566,7 +566,7 @@ def _append_global_identity_args(self, parser): help=_("Path of client key to use in SSL " "connection. This option is not necessary " "if your key is prepended to your certificate " - "file. Defaults to env[OS_KEY]")) + "file. Defaults to env[OS_KEY].")) parser.add_argument( '--os-password', metavar='', From 9660c7da2761c2d6ac0f67a8ceae5488c52f402b Mon Sep 17 00:00:00 2001 From: Assaf Muller Date: Thu, 14 Aug 2014 15:47:45 +0300 Subject: [PATCH 086/845] Add L3 HA / VRRP support to CLI Change-Id: Ia1f758f5536780a3170d624d61a3b2fa66eff76e Partially-implements: blueprint l3-high-availability --- neutronclient/neutron/v2_0/router.py | 10 ++++++-- neutronclient/tests/unit/test_cli20_router.py | 25 +++++++++++++++---- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index 5d03877a9..cd1cf5123 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -36,7 +36,7 @@ class ListRouter(neutronV20.ListCommand): resource = 'router' _formatters = {'external_gateway_info': _format_external_gateway_info, } - list_columns = ['id', 'name', 'external_gateway_info', 'distributed'] + list_columns = ['id', 'name', 'external_gateway_info', 'distributed', 'ha'] pagination_support = True sorting_support = True @@ -71,11 +71,17 @@ def add_known_arguments(self, parser): choices=['True', 'False'], default=argparse.SUPPRESS, help=_('Create a distributed router.')) + parser.add_argument( + '--ha', + dest='ha', + choices=['True', 'False'], + default=argparse.SUPPRESS, + help=_('Create a highly available router.')) def args2body(self, parsed_args): body = {self.resource: {'admin_state_up': parsed_args.admin_state}} neutronV20.update_dict(parsed_args, body[self.resource], - ['name', 'tenant_id', 'distributed']) + ['name', 'tenant_id', 'distributed', 'ha']) return body diff --git a/neutronclient/tests/unit/test_cli20_router.py b/neutronclient/tests/unit/test_cli20_router.py index 372c15ff1..6320750bf 100644 --- a/neutronclient/tests/unit/test_cli20_router.py +++ b/neutronclient/tests/unit/test_cli20_router.py @@ -60,19 +60,34 @@ def test_create_router_admin_state(self): position_names, position_values, admin_state_up=False) - def test_create_router_distributed(self): - """Create router: --distributed=True myname.""" + def _create_router_distributed_or_ha(self, distributed=None, ha=None): + """Create router: --distributed distributed --ha ha myname.""" resource = 'router' cmd = router.CreateRouter(test_cli20.MyApp(sys.stdout), None) name = 'myname' myid = 'myid' - distributed = 'True' - args = ['--distributed', distributed, name, ] + args = [] + if distributed is not None: + args += ['--distributed', str(distributed)] + if ha is not None: + args += ['--ha', str(ha)] + args.append(name) position_names = ['name', ] position_values = [name, ] + expected = {} + if distributed is not None: + expected['distributed'] = str(distributed) + if ha is not None: + expected['ha'] = str(ha) self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values, - distributed=distributed) + **expected) + + def test_create_router_distributed(self): + self._create_router_distributed_or_ha(distributed=True) + + def test_create_router_ha(self): + self._create_router_distributed_or_ha(ha=True) def test_list_routers_detail(self): """list routers: -D.""" From a9ed96f5c302c0fe4a19321883b0aa35efee99d9 Mon Sep 17 00:00:00 2001 From: Andy McCrae Date: Fri, 12 Sep 2014 11:26:05 +0100 Subject: [PATCH 087/845] Fix to ensure endpoint_type is used by make_client() This will fix an issue where neutronclient is ignoring the endpoint_type passed to the client for v2 auth. * make_client for '2.0' will now pass on the endpoint_type variable Change-Id: I917ebee51254bc3e144f09573fa628d4ca192d13 Closes-Bug: #1368676 --- neutronclient/neutron/client.py | 1 + 1 file changed, 1 insertion(+) diff --git a/neutronclient/neutron/client.py b/neutronclient/neutron/client.py index 40eb9382a..4f0c5b01d 100644 --- a/neutronclient/neutron/client.py +++ b/neutronclient/neutron/client.py @@ -42,6 +42,7 @@ def make_client(instance): region_name=instance._region_name, auth_url=instance._auth_url, endpoint_url=url, + endpoint_type=instance._endpoint_type, token=instance._token, auth_strategy=instance._auth_strategy, insecure=instance._insecure, From 8a30a0c3711ba56c8dcd62f80b8c136a8544db0f Mon Sep 17 00:00:00 2001 From: sridhargaddam Date: Sat, 13 Sep 2014 00:08:29 +0530 Subject: [PATCH 088/845] Replace utils.dumps with jsonutils.dumps In one of the previous patchsets which addresses Bug#1365263 (Idc5ee0fec56b0a0f337433a830647d4f421a8283) utils.dumps was replaced with jsonutils.dumps. This patch addresses the changes in the remaining files. Change-Id: Ie8c28d8cd2006e520d1a10ac26111886f63e12da Closes-bug: 1368835 --- neutronclient/neutron/v2_0/router.py | 4 ++-- neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index 5d03877a9..0f788fb3c 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -19,14 +19,14 @@ import argparse from neutronclient.common import exceptions -from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronV20 from neutronclient.openstack.common.gettextutils import _ +from neutronclient.openstack.common import jsonutils def _format_external_gateway_info(router): try: - return utils.dumps(router['external_gateway_info']) + return jsonutils.dumps(router['external_gateway_info']) except Exception: return '' diff --git a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py index 6dbeb37b5..05a7dbcb6 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py +++ b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py @@ -21,11 +21,12 @@ from neutronclient.neutron import v2_0 as neutronv20 from neutronclient.neutron.v2_0.vpn import utils as vpn_utils from neutronclient.openstack.common.gettextutils import _ +from neutronclient.openstack.common import jsonutils def _format_peer_cidrs(ipsec_site_connection): try: - return '\n'.join([utils.dumps(cidrs) for cidrs in + return '\n'.join([jsonutils.dumps(cidrs) for cidrs in ipsec_site_connection['peer_cidrs']]) except Exception: return '' From b6faa32b24e8f90faa2ebe95383c4e10acc0aced Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Sat, 13 Sep 2014 07:28:06 +0000 Subject: [PATCH 089/845] Updated from global requirements Change-Id: Ia36c61b467b4dc4cfc81439eaf626a9ee383f3a2 --- requirements.txt | 7 +++++-- test-requirements.txt | 5 ++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index 40650f2d4..762894ef6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,12 @@ +# The order of packages is significant, because pip processes them in the order +# of appearance. Changing the order has an impact on the overall integration +# process, which may cause wedges in the gate later. pbr>=0.6,!=0.7,<1.0 argparse cliff>=1.6.0 iso8601>=0.1.9 -netaddr>=0.7.6 -requests>=1.2.1 +netaddr>=0.7.12 +requests>=1.2.1,!=2.4.0 python-keystoneclient>=0.10.0 simplejson>=2.2.0 six>=1.7.0 diff --git a/test-requirements.txt b/test-requirements.txt index 44eca620a..dc32c243c 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,3 +1,6 @@ +# The order of packages is significant, because pip processes them in the order +# of appearance. Changing the order has an impact on the overall integration +# process, which may cause wedges in the gate later. hacking>=0.8.0,<0.9 cliff-tablib>=1.0 @@ -7,7 +10,7 @@ fixtures>=0.3.14 httpretty>=0.8.0,!=0.8.1,!=0.8.2,!=0.8.3 mox3>=0.7.0 oslosphinx>=2.2.0.0a2 -oslotest>=1.1.0.0a1 +oslotest>=1.1.0.0a2 python-subunit>=0.0.18 sphinx>=1.1.2,!=1.2.0,<1.3 testrepository>=0.0.18 From 60e89610974884a37a606e6d2a5936c511a20b18 Mon Sep 17 00:00:00 2001 From: Andreas Jaeger Date: Sat, 13 Sep 2014 09:44:00 +0200 Subject: [PATCH 090/845] Stop using intersphinx Remove intersphinx from the docs build as it triggers network calls that occasionally fail, and we don't really use intersphinx (links other sphinx documents out on the internet) This also removes the requirement for internet access during docs build. This can cause docs jobs to fail if the project errors out on warnings. Change-Id: I71e941e2a639641a662a163c682eb86d51de42fb Related-Bug: #1368910 --- doc/source/conf.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index 0001ce604..9648d80c7 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -7,7 +7,7 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'oslosphinx'] +extensions = ['sphinx.ext.autodoc', 'oslosphinx'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -50,6 +50,3 @@ u'%s Documentation' % project, u'OpenStack Foundation', 'manual'), ] - -# Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'http://docs.python.org/': None} From 69e7ffc19a89a5c0a2af4124ef1046f2905e31b2 Mon Sep 17 00:00:00 2001 From: Cedric Brandily Date: Sun, 14 Sep 2014 17:36:24 +0200 Subject: [PATCH 091/845] Silence iso8601 debug messages in verbose mode In verbose mode, iso8601 package produces the following useless debug messages: DEBUG: iso8601.iso8601 Got u'2014' for 'year' with default None DEBUG: iso8601.iso8601 Got u'09' for 'monthdash' with default 1 DEBUG: iso8601.iso8601 Got 9 for 'month' with default 9 DEBUG: iso8601.iso8601 Got u'14' for 'daydash' with default 1 DEBUG: iso8601.iso8601 Got 14 for 'day' with default 14 DEBUG: iso8601.iso8601 Got u'16' for 'hour' with default None DEBUG: iso8601.iso8601 Got u'31' for 'minute' with default None DEBUG: iso8601.iso8601 Got u'23' for 'second' with default None This change silences them. Change-Id: Ibd0c5bb48b1ab86aaee2c8cb4f27b7e5fa8b4bef --- neutronclient/shell.py | 1 + 1 file changed, 1 insertion(+) diff --git a/neutronclient/shell.py b/neutronclient/shell.py index ea5595bb5..5e883a341 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -852,6 +852,7 @@ def configure_logging(self): formatter = logging.Formatter(self.DEBUG_MESSAGE_FORMAT) else: formatter = logging.Formatter(self.CONSOLE_MESSAGE_FORMAT) + logging.getLogger('iso8601.iso8601').setLevel(logging.WARNING) logging.getLogger('urllib3.connectionpool').setLevel(logging.WARNING) console.setFormatter(formatter) root_logger.addHandler(console) From b81d650c9dcb9277bb703ca2f14b47db6553fcf8 Mon Sep 17 00:00:00 2001 From: Cedric Brandily Date: Mon, 8 Sep 2014 09:58:57 +0200 Subject: [PATCH 092/845] Allow to specify policy by name in firewall-update Currently only firewall policy ids are supported in firewall-update, this change allows to use firewall policy names in firewall-update: neutron firewall-update my-firewall --policy my-new-policy DocImpact Change-Id: I84ad97fc7f1f2e81cad98497d4d983606fa03077 --- neutronclient/neutron/v2_0/fw/firewall.py | 16 +++++++++++++++- .../tests/unit/fw/test_cli20_firewall.py | 8 ++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/neutronclient/neutron/v2_0/fw/firewall.py b/neutronclient/neutron/v2_0/fw/firewall.py index 2f0755b54..06b804037 100644 --- a/neutronclient/neutron/v2_0/fw/firewall.py +++ b/neutronclient/neutron/v2_0/fw/firewall.py @@ -46,7 +46,7 @@ class CreateFirewall(neutronv20.CreateCommand): def add_known_arguments(self, parser): parser.add_argument( 'firewall_policy_id', metavar='POLICY', - help=_('Firewall policy ID.')) + help=_('Firewall policy name or ID.')) parser.add_argument( '--name', help=_('Name for the firewall.')) @@ -83,6 +83,20 @@ class UpdateFirewall(neutronv20.UpdateCommand): resource = 'firewall' + def add_known_arguments(self, parser): + parser.add_argument( + '--policy', metavar='POLICY', + help=_('Firewall policy name or ID.')) + + def args2body(self, parsed_args): + data = {} + if parsed_args.policy: + _policy_id = neutronv20.find_resourceid_by_name_or_id( + self.get_client(), 'firewall_policy', + parsed_args.policy) + data['firewall_policy_id'] = _policy_id + return {self.resource: data} + class DeleteFirewall(neutronv20.DeleteCommand): """Delete a given firewall.""" diff --git a/neutronclient/tests/unit/fw/test_cli20_firewall.py b/neutronclient/tests/unit/fw/test_cli20_firewall.py index 1d24a3342..3b6288b63 100644 --- a/neutronclient/tests/unit/fw/test_cli20_firewall.py +++ b/neutronclient/tests/unit/fw/test_cli20_firewall.py @@ -112,6 +112,14 @@ def test_update_firewall(self): ['myid', '--name', 'newname'], {'name': 'newname', }) + def test_update_firewall_using_policy_name(self): + """firewall-update myid --policy newpolicy.""" + resource = 'firewall' + cmd = firewall.UpdateFirewall(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--policy', 'newpolicy'], + {'firewall_policy_id': 'newpolicy'}) + def test_delete_firewall(self): """firewall-delete my-id.""" resource = 'firewall' From fb83043e152e2181ed2431125bba32d89e3908b7 Mon Sep 17 00:00:00 2001 From: Cedric Brandily Date: Thu, 11 Sep 2014 21:14:18 +0200 Subject: [PATCH 093/845] Correct Content-Type/Accept management in HTTPClient/SessionClient Currently SessionClient.request never specifies Content-Type/Accept headers in requests where HTTPClient.request always specifies json Content-Type/Accept even with "--request-format xml" option ... requests succeed because neutron deduces Content-Type/Accept from url suffix when defined. This change ensures HTTPClient/SessionClient define Content-Type/Accept headers when needed and align them with --request-format option. Closes-Bug: #1368335 Change-Id: I909e055e5e59fa5870a28ebb0f171812acca0745 --- neutronclient/client.py | 62 +++++++++++--------- neutronclient/tests/unit/test_http.py | 82 ++++++++++++++++++++++----- neutronclient/v2_0/client.py | 7 ++- 3 files changed, 110 insertions(+), 41 deletions(-) diff --git a/neutronclient/client.py b/neutronclient/client.py index 02a198de3..d2f3685d3 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -14,6 +14,7 @@ # under the License. # +import abc try: import json except ImportError: @@ -24,6 +25,7 @@ from keystoneclient import access from keystoneclient.auth.identity.base import BaseIdentityPlugin import requests +import six from neutronclient.common import exceptions from neutronclient.common import utils @@ -42,12 +44,34 @@ logging.getLogger("requests").setLevel(_requests_log_level) -class NeutronClientMixin(object): +@six.add_metaclass(abc.ABCMeta) +class AbstractHTTPClient(object): USER_AGENT = 'python-neutronclient' + CONTENT_TYPE = 'application/json' + def request(self, url, method, body=None, content_type=None, headers=None, + **kwargs): + """Request without authentication.""" -class HTTPClient(NeutronClientMixin): + headers = headers or {} + content_type = content_type or self.CONTENT_TYPE + headers.setdefault('Accept', content_type) + if body: + headers.setdefault('Content-Type', content_type) + + return self._request(url, method, body=body, headers=headers, **kwargs) + + @abc.abstractmethod + def do_request(self, url, method, **kwargs): + """Request with authentication.""" + + @abc.abstractmethod + def _request(self, url, method, body=None, headers=None, **kwargs): + """Request without authentication nor headers population.""" + + +class HTTPClient(AbstractHTTPClient): """Handles the REST calls and responses, include authentication.""" def __init__(self, username=None, user_id=None, @@ -73,7 +97,6 @@ def __init__(self, username=None, user_id=None, self.auth_token = token self.auth_tenant_id = None self.auth_user_id = None - self.content_type = 'application/json' self.endpoint_url = endpoint_url self.auth_strategy = auth_strategy self.log_credentials = log_credentials @@ -87,13 +110,6 @@ def _cs_request(self, *args, **kwargs): kargs.setdefault('headers', kwargs.get('headers', {})) kargs['headers']['User-Agent'] = self.USER_AGENT - if 'content_type' in kwargs: - kargs['headers']['Content-Type'] = kwargs['content_type'] - kargs['headers']['Accept'] = kwargs['content_type'] - else: - kargs['headers']['Content-Type'] = self.content_type - kargs['headers']['Accept'] = self.content_type - if 'body' in kwargs: kargs['body'] = kwargs['body'] args = utils.safe_encode_list(args) @@ -135,17 +151,15 @@ def authenticate_and_fetch_endpoint_url(self): elif not self.endpoint_url: self.endpoint_url = self._get_endpoint_url() - def request(self, url, method, **kwargs): - kwargs.setdefault('headers', kwargs.get('headers', {})) - kwargs['headers']['User-Agent'] = self.USER_AGENT - kwargs['headers']['Accept'] = 'application/json' - if 'body' in kwargs: - kwargs['headers']['Content-Type'] = 'application/json' - kwargs['data'] = kwargs['body'] - del kwargs['body'] + def _request(self, url, method, body=None, headers=None, **kwargs): + headers = headers or {} + headers['User-Agent'] = self.USER_AGENT + resp = requests.request( method, url, + data=body, + headers=headers, verify=self.verify_cert, timeout=self.timeout, **kwargs) @@ -268,7 +282,7 @@ def get_auth_info(self): 'endpoint_url': self.endpoint_url} -class SessionClient(NeutronClientMixin): +class SessionClient(AbstractHTTPClient): def __init__(self, session, @@ -285,23 +299,19 @@ def __init__(self, self.auth_token = None self.endpoint_url = None - def request(self, url, method, **kwargs): + def _request(self, url, method, body=None, headers=None, **kwargs): kwargs.setdefault('user_agent', self.USER_AGENT) kwargs.setdefault('auth', self.auth) kwargs.setdefault('authenticated', False) - try: - kwargs['data'] = kwargs.pop('body') - except KeyError: - pass - endpoint_filter = kwargs.setdefault('endpoint_filter', {}) endpoint_filter.setdefault('interface', self.interface) endpoint_filter.setdefault('service_type', self.service_type) endpoint_filter.setdefault('region_name', self.region_name) kwargs = utils.safe_encode_dict(kwargs) - resp = self.session.request(url, method, **kwargs) + resp = self.session.request(url, method, data=body, headers=headers, + **kwargs) return resp, resp.text def do_request(self, url, method, **kwargs): diff --git a/neutronclient/tests/unit/test_http.py b/neutronclient/tests/unit/test_http.py index f2a6675a5..5595eb833 100644 --- a/neutronclient/tests/unit/test_http.py +++ b/neutronclient/tests/unit/test_http.py @@ -13,11 +13,15 @@ # License for the specific language governing permissions and limitations # under the License. +import abc + from mox3 import mox +import six import testtools -from neutronclient.client import HTTPClient +from neutronclient import client from neutronclient.common import exceptions +from neutronclient.tests.unit import test_auth from neutronclient.tests.unit.test_cli20 import MyResp @@ -25,21 +29,73 @@ END_URL = 'test_url' METHOD = 'GET' URL = 'http://test.test:1234/v2.0/test' +BODY = 'IAMFAKE' + +@six.add_metaclass(abc.ABCMeta) +class TestHTTPClientMixin(object): -class TestHTTPClient(testtools.TestCase): def setUp(self): - super(TestHTTPClient, self).setUp() + super(TestHTTPClientMixin, self).setUp() + self.clazz, self.http = self.initialize() self.mox = mox.Mox() - self.mox.StubOutWithMock(HTTPClient, 'request') self.addCleanup(self.mox.UnsetStubs) + self.mox.StubOutWithMock(self.clazz, '_request') + + @abc.abstractmethod + def initialize(self): + """Return client class, instance.""" + + def _test_headers(self, expected_headers, **kwargs): + """Test headers.""" + self.clazz._request(URL, METHOD, + body=kwargs.get('body'), + headers=expected_headers) + self.mox.ReplayAll() + self.http.request(URL, METHOD, **kwargs) + self.mox.VerifyAll() + + def test_headers_without_body(self): + self._test_headers({'Accept': 'application/json'}) + + def test_headers_with_body(self): + headers = {'Accept': 'application/json', + 'Content-Type': 'application/json'} + self._test_headers(headers, body=BODY) + + def test_headers_without_body_with_content_type(self): + headers = {'Accept': 'application/xml'} + self._test_headers(headers, content_type='application/xml') + + def test_headers_with_body_with_content_type(self): + headers = {'Accept': 'application/xml', + 'Content-Type': 'application/xml'} + self._test_headers(headers, body=BODY, content_type='application/xml') + + def test_headers_defined_in_headers(self): + headers = {'Accept': 'application/xml', + 'Content-Type': 'application/xml'} + self._test_headers(headers, body=BODY, headers=headers) + + +class TestSessionClient(TestHTTPClientMixin, testtools.TestCase): + + def initialize(self): + session, auth = test_auth.setup_keystone_v2() + return [client.SessionClient, + client.SessionClient(session=session, auth=auth)] + + +class TestHTTPClient(TestHTTPClientMixin, testtools.TestCase): - self.http = HTTPClient(token=AUTH_TOKEN, endpoint_url=END_URL) + def initialize(self): + return [client.HTTPClient, + client.HTTPClient(token=AUTH_TOKEN, endpoint_url=END_URL)] def test_request_error(self): - HTTPClient.request( - URL, METHOD, headers=mox.IgnoreArg() + self.clazz._request( + URL, METHOD, body=None, headers=mox.IgnoreArg() ).AndRaise(Exception('error msg')) self.mox.ReplayAll() @@ -53,8 +109,8 @@ def test_request_error(self): def test_request_success(self): rv_should_be = MyResp(200), 'test content' - HTTPClient.request( - URL, METHOD, headers=mox.IgnoreArg() + self.clazz._request( + URL, METHOD, body=None, headers=mox.IgnoreArg() ).AndReturn(rv_should_be) self.mox.ReplayAll() @@ -63,8 +119,8 @@ def test_request_success(self): def test_request_unauthorized(self): rv_should_be = MyResp(401), 'unauthorized message' - HTTPClient.request( - URL, METHOD, headers=mox.IgnoreArg() + self.clazz._request( + URL, METHOD, body=None, headers=mox.IgnoreArg() ).AndReturn(rv_should_be) self.mox.ReplayAll() @@ -75,8 +131,8 @@ def test_request_unauthorized(self): def test_request_forbidden_is_returned_to_caller(self): rv_should_be = MyResp(403), 'forbidden message' - HTTPClient.request( - URL, METHOD, headers=mox.IgnoreArg() + self.clazz._request( + URL, METHOD, body=None, headers=mox.IgnoreArg() ).AndReturn(rv_should_be) self.mox.ReplayAll() diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 393fd9679..eb841253d 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -1235,8 +1235,11 @@ def do_request(self, method, action, body=None, headers=None, params=None): if body: body = self.serialize(body) - self.httpclient.content_type = self.content_type() - resp, replybody = self.httpclient.do_request(action, method, body=body) + + resp, replybody = self.httpclient.do_request( + action, method, body=body, + content_type=self.content_type()) + status_code = resp.status_code if status_code in (requests.codes.ok, requests.codes.created, From 19527c450412117bd9a21b6b07c50359a123d1e5 Mon Sep 17 00:00:00 2001 From: armando-migliaccio Date: Fri, 12 Sep 2014 12:43:12 -0700 Subject: [PATCH 094/845] Narrow down except clause To avoid masking brainfarts like the ones that led to bug report below. Related-bug: #1368835 Change-Id: I028b779e580da7719925a8e127f48bbe1aec211e --- neutronclient/neutron/v2_0/agent.py | 2 +- neutronclient/neutron/v2_0/fw/firewallpolicy.py | 2 +- neutronclient/neutron/v2_0/network.py | 2 +- neutronclient/neutron/v2_0/port.py | 2 +- neutronclient/neutron/v2_0/router.py | 2 +- neutronclient/neutron/v2_0/subnet.py | 6 +++--- neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/neutronclient/neutron/v2_0/agent.py b/neutronclient/neutron/v2_0/agent.py index 5b4887384..e9e2688b3 100644 --- a/neutronclient/neutron/v2_0/agent.py +++ b/neutronclient/neutron/v2_0/agent.py @@ -20,7 +20,7 @@ def _format_timestamp(component): try: return component['heartbeat_timestamp'].split(".", 2)[0] - except Exception: + except (TypeError, KeyError): return '' diff --git a/neutronclient/neutron/v2_0/fw/firewallpolicy.py b/neutronclient/neutron/v2_0/fw/firewallpolicy.py index 0571a88e3..f278fbfda 100644 --- a/neutronclient/neutron/v2_0/fw/firewallpolicy.py +++ b/neutronclient/neutron/v2_0/fw/firewallpolicy.py @@ -30,7 +30,7 @@ def _format_firewall_rules(firewall_policy): output = '[' + ',\n '.join([rule for rule in firewall_policy['firewall_rules']]) + ']' return output - except Exception: + except (TypeError, KeyError): return '' diff --git a/neutronclient/neutron/v2_0/network.py b/neutronclient/neutron/v2_0/network.py index 05a8de7d8..aef3e4cf9 100644 --- a/neutronclient/neutron/v2_0/network.py +++ b/neutronclient/neutron/v2_0/network.py @@ -25,7 +25,7 @@ def _format_subnets(network): try: return '\n'.join([' '.join([s['id'], s.get('cidr', '')]) for s in network['subnets']]) - except Exception: + except (TypeError, KeyError): return '' diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index 2aad26b82..3b628b52c 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -26,7 +26,7 @@ def _format_fixed_ips(port): try: return '\n'.join([jsonutils.dumps(ip) for ip in port['fixed_ips']]) - except Exception: + except (TypeError, KeyError): return '' diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index 055e418bf..369703e01 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -27,7 +27,7 @@ def _format_external_gateway_info(router): try: return jsonutils.dumps(router['external_gateway_info']) - except Exception: + except (TypeError, KeyError): return '' diff --git a/neutronclient/neutron/v2_0/subnet.py b/neutronclient/neutron/v2_0/subnet.py index ece163258..0ba633f98 100644 --- a/neutronclient/neutron/v2_0/subnet.py +++ b/neutronclient/neutron/v2_0/subnet.py @@ -27,7 +27,7 @@ def _format_allocation_pools(subnet): try: return '\n'.join([jsonutils.dumps(pool) for pool in subnet['allocation_pools']]) - except Exception: + except (TypeError, KeyError): return '' @@ -35,7 +35,7 @@ def _format_dns_nameservers(subnet): try: return '\n'.join([jsonutils.dumps(server) for server in subnet['dns_nameservers']]) - except Exception: + except (TypeError, KeyError): return '' @@ -43,7 +43,7 @@ def _format_host_routes(subnet): try: return '\n'.join([jsonutils.dumps(route) for route in subnet['host_routes']]) - except Exception: + except (TypeError, KeyError): return '' diff --git a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py index 05a7dbcb6..116451158 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py +++ b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py @@ -28,7 +28,7 @@ def _format_peer_cidrs(ipsec_site_connection): try: return '\n'.join([jsonutils.dumps(cidrs) for cidrs in ipsec_site_connection['peer_cidrs']]) - except Exception: + except (TypeError, KeyError): return '' From 9bf8b71e33d52c72d0842dad0beca3df60da60ce Mon Sep 17 00:00:00 2001 From: Ilya Shakhat Date: Mon, 28 Jul 2014 14:02:57 +0400 Subject: [PATCH 095/845] Fix Py3 compatibility issues * Replace of urllib by six urlparse * Convert dict keys into list to be able to retrieve the first element * Use integer division for integers * Use int instead of long in XML serialization/deserialization under Py3 Co-author: Cyril Roelandt Change-Id: Ia79c831310775bf1c3dbec06010f8949c3a73887 --- neutronclient/common/serializer.py | 12 ++++++------ neutronclient/common/utils.py | 12 +++++++----- neutronclient/neutron/v2_0/fw/firewallpolicy.py | 3 +-- neutronclient/neutron/v2_0/network.py | 2 +- neutronclient/neutron/v2_0/securitygroup.py | 2 +- neutronclient/shell.py | 10 +++++----- neutronclient/tests/unit/test_auth.py | 7 +++++-- neutronclient/tests/unit/test_cli20.py | 2 +- neutronclient/tests/unit/test_cli20_agents.py | 4 ++-- neutronclient/tests/unit/test_cli20_network.py | 2 +- neutronclient/tests/unit/test_cli20_packetfilter.py | 2 +- neutronclient/tests/unit/test_cli20_securitygroup.py | 2 +- neutronclient/v2_0/client.py | 3 +-- 13 files changed, 33 insertions(+), 30 deletions(-) diff --git a/neutronclient/common/serializer.py b/neutronclient/common/serializer.py index 73f0bec2e..ef95e402a 100644 --- a/neutronclient/common/serializer.py +++ b/neutronclient/common/serializer.py @@ -30,6 +30,9 @@ LOG = logging.getLogger(__name__) +if six.PY3: + long = int + class ActionDispatcher(object): """Maps method name to local methods through action name.""" @@ -59,7 +62,7 @@ class JSONDictSerializer(DictSerializer): def default(self, data): def sanitizer(obj): - return unicode(obj) + return six.text_type(obj, 'utf8') return jsonutils.dumps(data, default=sanitizer) @@ -100,7 +103,7 @@ def default(self, data): links = data.pop(link_keys[0], None) has_atom = True root_key = (len(data) == 1 and - data.keys()[0] or constants.VIRTUAL_ROOT_KEY) + list(data.keys())[0] or constants.VIRTUAL_ROOT_KEY) root_value = data.get(root_key, data) doc = etree.Element("_temp_root") used_prefixes = [] @@ -195,10 +198,7 @@ def _to_xml_node(self, parent, metadata, nodename, data, used_prefixes): LOG.debug("Data %(data)s type is %(type)s", {'data': data, 'type': type(data)}) - if isinstance(data, str): - result.text = unicode(data, 'utf-8') - else: - result.text = unicode(data) + result.text = six.text_type(data) return result def _create_link_nodes(self, xml_doc, links): diff --git a/neutronclient/common/utils.py b/neutronclient/common/utils.py index aa7b688a5..b68a6089b 100644 --- a/neutronclient/common/utils.py +++ b/neutronclient/common/utils.py @@ -21,6 +21,8 @@ import os import sys +import six + from neutronclient.common import _ from neutronclient.common import exceptions from neutronclient.openstack.common import strutils @@ -134,8 +136,8 @@ def http_log_req(_logger, args, kwargs): if 'body' in kwargs and kwargs['body']: string_parts.append(" -d '%s'" % (kwargs['body'])) - string_parts = safe_encode_list(string_parts) - _logger.debug("\nREQ: %s\n", "".join(string_parts)) + req = strutils.safe_encode("".join(string_parts)) + _logger.debug("\nREQ: %s\n", req) def http_log_resp(_logger, resp, body): @@ -148,13 +150,13 @@ def http_log_resp(_logger, resp, body): def _safe_encode_without_obj(data): - if isinstance(data, basestring): + if isinstance(data, six.string_types): return strutils.safe_encode(data) return data def safe_encode_list(data): - return map(_safe_encode_without_obj, data) + return list(map(_safe_encode_without_obj, data)) def safe_encode_dict(data): @@ -166,4 +168,4 @@ def _encode_item(item): return (k, safe_encode_dict(v)) return (k, _safe_encode_without_obj(v)) - return dict(map(_encode_item, data.items())) + return dict(list(map(_encode_item, data.items()))) diff --git a/neutronclient/neutron/v2_0/fw/firewallpolicy.py b/neutronclient/neutron/v2_0/fw/firewallpolicy.py index f278fbfda..e2771c26c 100644 --- a/neutronclient/neutron/v2_0/fw/firewallpolicy.py +++ b/neutronclient/neutron/v2_0/fw/firewallpolicy.py @@ -19,7 +19,6 @@ from __future__ import print_function import argparse -import string from neutronclient.neutron import v2_0 as neutronv20 from neutronclient.openstack.common.gettextutils import _ @@ -71,7 +70,7 @@ def add_known_arguments(self, parser): help=_('Create a shared policy.'), default=argparse.SUPPRESS) parser.add_argument( - '--firewall-rules', type=string.split, + '--firewall-rules', type=str.split, help=_('Ordered list of whitespace-delimited firewall rule ' 'names or IDs; e.g., --firewall-rules \"rule1 rule2\"')) parser.add_argument( diff --git a/neutronclient/neutron/v2_0/network.py b/neutronclient/neutron/v2_0/network.py index aef3e4cf9..54d546570 100644 --- a/neutronclient/neutron/v2_0/network.py +++ b/neutronclient/neutron/v2_0/network.py @@ -68,7 +68,7 @@ def _get_subnet_list(sub_ids): subnet_count = len(subnet_ids) max_size = ((self.subnet_id_filter_len * subnet_count) - uri_len_exc.excess) - chunk_size = max_size / self.subnet_id_filter_len + chunk_size = max_size // self.subnet_id_filter_len subnets = [] for i in range(0, subnet_count, chunk_size): subnets.extend( diff --git a/neutronclient/neutron/v2_0/securitygroup.py b/neutronclient/neutron/v2_0/securitygroup.py index 14d25fa01..8c554a904 100644 --- a/neutronclient/neutron/v2_0/securitygroup.py +++ b/neutronclient/neutron/v2_0/securitygroup.py @@ -154,7 +154,7 @@ def _get_sec_group_list(sec_group_ids): sec_group_count = len(sec_group_ids) max_size = ((sec_group_id_filter_len * sec_group_count) - uri_len_exc.excess) - chunk_size = max_size / sec_group_id_filter_len + chunk_size = max_size // sec_group_id_filter_len secgroups = [] for i in range(0, sec_group_count, chunk_size): secgroups.extend( diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 6ea00b322..8dd2b3a00 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -666,10 +666,10 @@ def run(self, argv): self.initialize_app(remainder) except Exception as err: if self.options.verbose_level >= self.DEBUG_LEVEL: - self.log.exception(unicode(err)) + self.log.exception(err) raise else: - self.log.error(unicode(err)) + self.log.error(err) return 1 if self.interactive_mode: _argv = [sys.argv[0]] @@ -923,12 +923,12 @@ def _get_keystone_session(self): def main(argv=sys.argv[1:]): try: - return NeutronShell(NEUTRON_API_VERSION).run(map(strutils.safe_decode, - argv)) + return NeutronShell(NEUTRON_API_VERSION).run( + list(map(strutils.safe_decode, argv))) except exc.NeutronClientException: return 1 except Exception as e: - print(unicode(e)) + print(e) return 1 diff --git a/neutronclient/tests/unit/test_auth.py b/neutronclient/tests/unit/test_auth.py index 42f674582..5561ecc53 100644 --- a/neutronclient/tests/unit/test_auth.py +++ b/neutronclient/tests/unit/test_auth.py @@ -21,6 +21,7 @@ import httpretty from mox3 import mox import requests +import six import testtools from keystoneclient.auth.identity import v2 as ks_v2_auth @@ -468,12 +469,14 @@ def verify_credentials(body): utils.http_log_req(mox.IgnoreArg(), mox.IgnoreArg(), mox.Func( verify_no_credentials)) self.client.request( - mox.IsA(str), mox.IsA(str), body=mox.Func(verify_credentials), + mox.IsA(six.string_types), mox.IsA(six.string_types), + body=mox.Func(verify_credentials), headers=mox.IgnoreArg() ).AndReturn((res200, json.dumps(KS_TOKEN_RESULT))) utils.http_log_req(mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg()) self.client.request( - mox.IsA(str), mox.IsA(str), headers=mox.IsA(dict) + mox.IsA(six.string_types), mox.IsA(six.string_types), + headers=mox.IsA(dict) ).AndReturn((res200, '')) self.mox.ReplayAll() diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 78d15676b..38b5aa18c 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -599,7 +599,7 @@ def test_do_request_error_without_response_body(self): self.client.format = self.format self.mox.StubOutWithMock(self.client.httpclient, "request") params = {'test': 'value'} - expect_query = urllib.urlencode(params) + expect_query = six.moves.urllib.parse.urlencode(params) self.client.httpclient.auth_token = 'token' self.client.httpclient.request( diff --git a/neutronclient/tests/unit/test_cli20_agents.py b/neutronclient/tests/unit/test_cli20_agents.py index 710f4ba6c..2b0226602 100644 --- a/neutronclient/tests/unit/test_cli20_agents.py +++ b/neutronclient/tests/unit/test_cli20_agents.py @@ -52,5 +52,5 @@ def test_list_agents_field(self): self.assertEqual(1, len(returned_agents)) ag = returned_agents[0] self.assertEqual(1, len(ag)) - self.assertEqual("alive", ag.keys()[0]) - self.assertEqual(smile, ag.values()[0]) + self.assertIn("alive", ag.keys()) + self.assertIn(smile, ag.values()) diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index a3b964d4e..c5891a293 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -286,7 +286,7 @@ def test_list_nets_defined_column(self): self.assertEqual(1, len(returned_networks)) net = returned_networks[0] self.assertEqual(1, len(net)) - self.assertEqual("id", net.keys()[0]) + self.assertIn("id", net.keys()) def test_list_nets_with_default_column(self): cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) diff --git a/neutronclient/tests/unit/test_cli20_packetfilter.py b/neutronclient/tests/unit/test_cli20_packetfilter.py index 9d7546d96..dcb190e1c 100644 --- a/neutronclient/tests/unit/test_cli20_packetfilter.py +++ b/neutronclient/tests/unit/test_cli20_packetfilter.py @@ -173,7 +173,7 @@ def test_update_packetfilter_exception(self): self._test_update_resource, resource, cmd, 'myid', ['myid'], {}) self.assertEqual('Must specify new values to update packet_filter', - unicode(exc)) + str(exc)) def test_delete_packetfilter(self): """Delete packetfilter: myid.""" diff --git a/neutronclient/tests/unit/test_cli20_securitygroup.py b/neutronclient/tests/unit/test_cli20_securitygroup.py index d09f48d34..d1cc13694 100644 --- a/neutronclient/tests/unit/test_cli20_securitygroup.py +++ b/neutronclient/tests/unit/test_cli20_securitygroup.py @@ -234,7 +234,7 @@ def _build_test_data(self, data, excess=0): sec_group_count = len(sec_group_ids) max_size = ((sec_group_id_filter_len * sec_group_count) - excess) - chunk_size = max_size / sec_group_id_filter_len + chunk_size = max_size // sec_group_id_filter_len for i in range(0, sec_group_count, chunk_size): search_opts['id'] = sec_group_ids[i: i + chunk_size] diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 393fd9679..20b5626af 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -16,7 +16,6 @@ import logging import time -import urllib import requests import six.moves.urllib.parse as urlparse @@ -1228,7 +1227,7 @@ def do_request(self, method, action, body=None, headers=None, params=None): action = self.action_prefix + action if type(params) is dict and params: params = utils.safe_encode_dict(params) - action += '?' + urllib.urlencode(params, doseq=1) + action += '?' + urlparse.urlencode(params, doseq=1) # Ensure client always has correct uri - do not guesstimate anything self.httpclient.authenticate_and_fetch_endpoint_url() self._check_uri_length(action) From 9d8ab0df5622c421f2a50f4743c4f87e6ed42b89 Mon Sep 17 00:00:00 2001 From: Ilya Shakhat Date: Mon, 11 Aug 2014 17:38:55 +0400 Subject: [PATCH 096/845] Replace httpretty with requests_mock The httpretty library has poor Py3 support, currently the only working version is 0.8.0, all later minor versions are pinned. Requests-mock library provides mocking layer for requests package. It is written in such a way that it is fairly simple to convert existing httpretty test suites to requests-mock. The library is hosted at Stackforge and gated as usual OpenStack project. Change-Id: If7f4baefb53976571efc47dda17a7b543112d555 --- neutronclient/tests/unit/test_auth.py | 44 ++++++++--------- neutronclient/tests/unit/test_http.py | 6 ++- neutronclient/tests/unit/test_shell.py | 66 +++++++++++++------------- neutronclient/tests/unit/test_ssl.py | 18 +++---- test-requirements.txt | 2 +- 5 files changed, 69 insertions(+), 67 deletions(-) diff --git a/neutronclient/tests/unit/test_auth.py b/neutronclient/tests/unit/test_auth.py index 5561ecc53..34e5e93a3 100644 --- a/neutronclient/tests/unit/test_auth.py +++ b/neutronclient/tests/unit/test_auth.py @@ -18,9 +18,9 @@ import uuid import fixtures -import httpretty from mox3 import mox import requests +import requests_mock import six import testtools @@ -138,24 +138,24 @@ def get_response(status_code, headers=None): return response -def setup_keystone_v2(): +def setup_keystone_v2(mrequests): v2_token = ks_v2_fixture.Token(token_id=TOKENID) service = v2_token.add_service('network') service.add_endpoint(PUBLIC_ENDPOINT_URL, region=REGION) - httpretty.register_uri(httpretty.POST, + mrequests.register_uri('POST', '%s/tokens' % (V2_URL), - body=json.dumps(v2_token)) + text=json.dumps(v2_token)) auth_session = session.Session() auth_plugin = ks_v2_auth.Password(V2_URL, 'xx', 'xx') return auth_session, auth_plugin -def setup_keystone_v3(): - httpretty.register_uri(httpretty.GET, +def setup_keystone_v3(mrequests): + mrequests.register_uri('GET', V3_URL, - body=V3_VERSION_ENTRY) + text=V3_VERSION_ENTRY) v3_token = ks_v3_fixture.Token() service = v3_token.add_service('network') @@ -164,10 +164,10 @@ def setup_keystone_v3(): internal=INTERNAL_ENDPOINT_URL, region=REGION) - httpretty.register_uri(httpretty.POST, + mrequests.register_uri('POST', '%s/auth/tokens' % (V3_URL), - body=json.dumps(v3_token), - adding_headers={'X-Subject-Token': TOKENID}) + text=json.dumps(v3_token), + headers={'X-Subject-Token': TOKENID}) auth_session = session.Session() auth_plugin = ks_v3_auth.Password(V3_URL, @@ -247,9 +247,9 @@ def test_reused_token_get_auth_info(self): 'endpoint_url': self.client.endpoint_url} self.assertEqual(client_.get_auth_info(), expected) - @httpretty.activate - def test_get_token(self): - auth_session, auth_plugin = setup_keystone_v2() + @requests_mock.Mocker() + def test_get_token(self, mrequests): + auth_session, auth_plugin = setup_keystone_v2(mrequests) self.client = client.construct_http_client( username=USERNAME, @@ -403,9 +403,9 @@ def test_get_endpoint_url_failed(self): self.mox.ReplayAll() self.client.do_request('/resource', 'GET') - @httpretty.activate - def test_endpoint_type(self): - auth_session, auth_plugin = setup_keystone_v3() + @requests_mock.Mocker() + def test_endpoint_type(self, mrequests): + auth_session, auth_plugin = setup_keystone_v3(mrequests) # Test default behavior is to choose public. self.client = client.construct_http_client( @@ -518,9 +518,9 @@ def setUp(self): self.addCleanup(self.mox.VerifyAll) self.addCleanup(self.mox.UnsetStubs) - @httpretty.activate - def test_v2_auth(self): - auth_session, auth_plugin = setup_keystone_v2() + @requests_mock.Mocker() + def test_v2_auth(self, mrequests): + auth_session, auth_plugin = setup_keystone_v2(mrequests) res200 = get_response(200) self.client = client.construct_http_client( @@ -542,9 +542,9 @@ def test_v2_auth(self): self.mox.ReplayAll() self.client.do_request('/resource', 'GET') - @httpretty.activate - def test_v3_auth(self): - auth_session, auth_plugin = setup_keystone_v3() + @requests_mock.Mocker() + def test_v3_auth(self, mrequests): + auth_session, auth_plugin = setup_keystone_v3(mrequests) res200 = get_response(200) self.client = client.construct_http_client( diff --git a/neutronclient/tests/unit/test_http.py b/neutronclient/tests/unit/test_http.py index 5595eb833..51c6cdcb1 100644 --- a/neutronclient/tests/unit/test_http.py +++ b/neutronclient/tests/unit/test_http.py @@ -16,6 +16,7 @@ import abc from mox3 import mox +import requests_mock import six import testtools @@ -81,8 +82,9 @@ def test_headers_defined_in_headers(self): class TestSessionClient(TestHTTPClientMixin, testtools.TestCase): - def initialize(self): - session, auth = test_auth.setup_keystone_v2() + @requests_mock.Mocker() + def initialize(self, mrequests): + session, auth = test_auth.setup_keystone_v2(mrequests) return [client.SessionClient, client.SessionClient(session=session, auth=auth)] diff --git a/neutronclient/tests/unit/test_shell.py b/neutronclient/tests/unit/test_shell.py index 499e13821..e7a7f4aa7 100644 --- a/neutronclient/tests/unit/test_shell.py +++ b/neutronclient/tests/unit/test_shell.py @@ -20,8 +20,8 @@ import sys import fixtures -import httpretty from mox3 import mox +import requests_mock import six import testtools from testtools import matchers @@ -134,12 +134,12 @@ def test_unknown_auth_strategy(self): self.assertEqual('You must provide a service URL via ' 'either --os-url or env[OS_URL]', stderr.strip()) - @httpretty.activate - def test_auth(self): + @requests_mock.Mocker() + def test_auth(self, mrequests): # emulate Keystone version discovery - httpretty.register_uri(httpretty.GET, + mrequests.register_uri('GET', auth.V3_URL, - body=auth.V3_VERSION_ENTRY) + text=auth.V3_VERSION_ENTRY) neutron_shell = openstack_shell.NeutronShell('2.0') self.addCleanup(self.mox.UnsetStubs) @@ -169,12 +169,12 @@ def test_auth(self): neutron_shell.run(cmdline.split()) self.mox.VerifyAll() - @httpretty.activate - def test_auth_cert_and_key(self): + @requests_mock.Mocker() + def test_auth_cert_and_key(self, mrequests): # emulate Keystone version discovery - httpretty.register_uri(httpretty.GET, + mrequests.register_uri('GET', auth.V3_URL, - body=auth.V3_VERSION_ENTRY) + text=auth.V3_VERSION_ENTRY) neutron_shell = openstack_shell.NeutronShell('2.0') self.addCleanup(self.mox.UnsetStubs) @@ -205,12 +205,12 @@ def test_auth_cert_and_key(self): neutron_shell.run(cmdline.split()) self.mox.VerifyAll() - @httpretty.activate - def test_v2_auth(self): + @requests_mock.Mocker() + def test_v2_auth(self, mrequests): # emulate Keystone version discovery - httpretty.register_uri(httpretty.GET, + mrequests.register_uri('GET', auth.V2_URL, - body=auth.V2_VERSION_ENTRY) + text=auth.V2_VERSION_ENTRY) neutron_shell = openstack_shell.NeutronShell('2.0') self.addCleanup(self.mox.UnsetStubs) @@ -240,12 +240,12 @@ def test_v2_auth(self): neutron_shell.run(cmdline.split()) self.mox.VerifyAll() - @httpretty.activate - def test_failed_auth_version_discovery_v3_auth_url(self): + @requests_mock.Mocker() + def test_failed_auth_version_discovery_v3_auth_url(self, mrequests): # emulate Keystone version discovery - httpretty.register_uri(httpretty.GET, + mrequests.register_uri('GET', auth.V3_URL, - status=405) + status_code=405) neutron_shell = openstack_shell.NeutronShell('2.0') self.addCleanup(self.mox.UnsetStubs) @@ -276,12 +276,12 @@ def test_failed_auth_version_discovery_v3_auth_url(self): neutron_shell.run(cmdline.split()) self.mox.VerifyAll() - @httpretty.activate - def test_failed_auth_version_discovery_v2_auth_url(self): + @requests_mock.Mocker() + def test_failed_auth_version_discovery_v2_auth_url(self, mrequests): # emulate Keystone version discovery - httpretty.register_uri(httpretty.GET, + mrequests.register_uri('GET', auth.V2_URL, - status=405) + status_code=405) neutron_shell = openstack_shell.NeutronShell('2.0') self.addCleanup(self.mox.UnsetStubs) @@ -311,12 +311,12 @@ def test_failed_auth_version_discovery_v2_auth_url(self): neutron_shell.run(cmdline.split()) self.mox.VerifyAll() - @httpretty.activate - def test_auth_version_discovery_v3(self): + @requests_mock.Mocker() + def test_auth_version_discovery_v3(self, mrequests): # emulate Keystone version discovery - httpretty.register_uri(httpretty.GET, + mrequests.register_uri('GET', auth.BASE_URL, - body=auth.V3_VERSION_LIST) + text=auth.V3_VERSION_LIST) neutron_shell = openstack_shell.NeutronShell('2.0') self.addCleanup(self.mox.UnsetStubs) @@ -347,12 +347,12 @@ def test_auth_version_discovery_v3(self): neutron_shell.run(cmdline.split()) self.mox.VerifyAll() - @httpretty.activate - def test_auth_version_discovery_v2(self): + @requests_mock.Mocker() + def test_auth_version_discovery_v2(self, mrequests): # emulate Keystone version discovery - httpretty.register_uri(httpretty.GET, + mrequests.register_uri('GET', auth.BASE_URL, - body=auth.V3_VERSION_LIST) + text=auth.V3_VERSION_LIST) neutron_shell = openstack_shell.NeutronShell('2.0') self.addCleanup(self.mox.UnsetStubs) @@ -382,12 +382,12 @@ def test_auth_version_discovery_v2(self): neutron_shell.run(cmdline.split()) self.mox.VerifyAll() - @httpretty.activate - def test_insecure_auth(self): + @requests_mock.Mocker() + def test_insecure_auth(self, mrequests): # emulate Keystone version discovery - httpretty.register_uri(httpretty.GET, + mrequests.register_uri('GET', auth.V2_URL, - body=auth.V2_VERSION_ENTRY) + text=auth.V2_VERSION_ENTRY) neutron_shell = openstack_shell.NeutronShell('2.0') self.addCleanup(self.mox.UnsetStubs) diff --git a/neutronclient/tests/unit/test_ssl.py b/neutronclient/tests/unit/test_ssl.py index bc0e50078..545f1b944 100644 --- a/neutronclient/tests/unit/test_ssl.py +++ b/neutronclient/tests/unit/test_ssl.py @@ -17,8 +17,8 @@ import requests import testtools -import httpretty from mox3 import mox +import requests_mock from neutronclient.client import HTTPClient from neutronclient.common.clientmanager import ClientManager @@ -43,12 +43,12 @@ def setUp(self): self.mox = mox.Mox() self.addCleanup(self.mox.UnsetStubs) - @httpretty.activate - def test_ca_cert_passed(self): + @requests_mock.Mocker() + def test_ca_cert_passed(self, mrequests): # emulate Keystone version discovery - httpretty.register_uri(httpretty.GET, + mrequests.register_uri('GET', auth.V3_URL, - body=auth.V3_VERSION_ENTRY) + text=auth.V3_VERSION_ENTRY) self.mox.StubOutWithMock(ClientManager, '__init__') self.mox.StubOutWithMock(openstack_shell.NeutronShell, 'interact') @@ -87,13 +87,13 @@ def test_ca_cert_passed(self): openstack_shell.NeutronShell('2.0').run(cmdline.split()) self.mox.VerifyAll() - @httpretty.activate - def test_ca_cert_passed_as_env_var(self): + @requests_mock.Mocker() + def test_ca_cert_passed_as_env_var(self, mrequests): # emulate Keystone version discovery - httpretty.register_uri(httpretty.GET, + mrequests.register_uri('GET', auth.V3_URL, - body=auth.V3_VERSION_ENTRY) + text=auth.V3_VERSION_ENTRY) self.useFixture(fixtures.EnvironmentVariable('OS_CACERT', CA_CERT)) diff --git a/test-requirements.txt b/test-requirements.txt index dc32c243c..753131644 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -7,11 +7,11 @@ cliff-tablib>=1.0 coverage>=3.6 discover fixtures>=0.3.14 -httpretty>=0.8.0,!=0.8.1,!=0.8.2,!=0.8.3 mox3>=0.7.0 oslosphinx>=2.2.0.0a2 oslotest>=1.1.0.0a2 python-subunit>=0.0.18 +requests-mock>=0.4.0 # Apache-2.0 sphinx>=1.1.2,!=1.2.0,<1.3 testrepository>=0.0.18 testtools>=0.9.34 From 8115c02054cd52c99f670e04a58e9861a4a84f88 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Fri, 19 Sep 2014 08:51:32 +0000 Subject: [PATCH 097/845] Updated from global requirements Change-Id: I2e275bda23033034ed356e012147ce7a701d4695 --- requirements.txt | 2 +- test-requirements.txt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index 762894ef6..548697cd0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ # process, which may cause wedges in the gate later. pbr>=0.6,!=0.7,<1.0 argparse -cliff>=1.6.0 +cliff>=1.7.0 # Apache-2.0 iso8601>=0.1.9 netaddr>=0.7.12 requests>=1.2.1,!=2.4.0 diff --git a/test-requirements.txt b/test-requirements.txt index dc32c243c..f4e6be1d8 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -9,8 +9,8 @@ discover fixtures>=0.3.14 httpretty>=0.8.0,!=0.8.1,!=0.8.2,!=0.8.3 mox3>=0.7.0 -oslosphinx>=2.2.0.0a2 -oslotest>=1.1.0.0a2 +oslosphinx>=2.2.0 # Apache-2.0 +oslotest>=1.1.0 # Apache-2.0 python-subunit>=0.0.18 sphinx>=1.1.2,!=1.2.0,<1.3 testrepository>=0.0.18 From 1ab433575166c365e544674e79d7ad5d982ccce0 Mon Sep 17 00:00:00 2001 From: He Yongli Date: Mon, 17 Feb 2014 14:34:45 +0800 Subject: [PATCH 098/845] Use six.moves cStringIO instead of cStringIO to keep Python 3.x compatibility, use six.moves.cStringIO to replace StringIO Change-Id: Ib6dcd06ca8c9342d26f3b7b738c99d23433d7f30 Closes-Bug: #1280964 --- neutronclient/tests/unit/test_shell.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neutronclient/tests/unit/test_shell.py b/neutronclient/tests/unit/test_shell.py index 499e13821..f79a09910 100644 --- a/neutronclient/tests/unit/test_shell.py +++ b/neutronclient/tests/unit/test_shell.py @@ -71,8 +71,8 @@ def shell(self, argstr, check=False): clean_env = {} _old_env, os.environ = os.environ, clean_env.copy() try: - sys.stdout = six.StringIO() - sys.stderr = six.StringIO() + sys.stdout = six.moves.cStringIO() + sys.stderr = six.moves.cStringIO() _shell = openstack_shell.NeutronShell('2.0') _shell.run(argstr.split()) except SystemExit: From a1a8a0e2b24e06b04dd211c00dd52ae3a2056490 Mon Sep 17 00:00:00 2001 From: Rakesh H S Date: Tue, 23 Sep 2014 11:09:35 +0530 Subject: [PATCH 099/845] handles keyboard interrupt When an user intentionally provides an keyboard interrupt, neutronclient throws the entire traceback on to the terminal instead of handling it. neutronclient will now handle the keyboard interrrupt and provides an crisp message on to the terminal. Change-Id: I55a85cd4bfcbf26d81e5678b6f4aed3d1c288467 Closes-Bug: #1367283 --- neutronclient/shell.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 51804e9aa..2ccf0ca4a 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -925,6 +925,9 @@ def main(argv=sys.argv[1:]): try: return NeutronShell(NEUTRON_API_VERSION).run( list(map(strutils.safe_decode, argv))) + except KeyboardInterrupt: + print("... terminating neutron client", file=sys.stderr) + return 130 except exc.NeutronClientException: return 1 except Exception as e: From 0fedd33954f7e8f4c338d216b833d20f3f2b4548 Mon Sep 17 00:00:00 2001 From: Danny Choi Date: Thu, 25 Sep 2014 06:19:37 -0700 Subject: [PATCH 100/845] Change ipsecpolicies to 2 separate words: IPsec policies In the help text for vpn-ipsecpolicy-list, change ipsecpolicies to 2 separate words, IPsec policies. Change-Id: I5f49991945c8e638d35e7267b2de81eeaf7ea9bf Closes-Bug: #1373622 --- neutronclient/neutron/v2_0/vpn/ipsecpolicy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py index 82a527984..99eb844e7 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py +++ b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py @@ -22,7 +22,7 @@ class ListIPsecPolicy(neutronv20.ListCommand): - """List ipsecpolicies that belongs to a given tenant connection.""" + """List IPsec policies that belong to a given tenant connection.""" resource = 'ipsecpolicy' list_columns = ['id', 'name', 'auth_algorithm', From bb4a0dc8a542344957dada4d6d21748fa0580bdc Mon Sep 17 00:00:00 2001 From: Cedric Brandily Date: Tue, 30 Sep 2014 22:22:50 +0200 Subject: [PATCH 101/845] Correct 4xx/5xx response management in SessionClient Currently SessionClient raises an exception when status code >= 400 unlike HTTPClient. neutronclient.v2_0.client expects HTTPClient behavior. This change disables exception raise when status code >= 400 in SessionClient (more precisely in keystoneclient.session). Change-Id: Ic96cb9326842f0b6a180fd8f94293da9fa0f6b2f Closes-Bug: #1376171 --- neutronclient/client.py | 1 + 1 file changed, 1 insertion(+) diff --git a/neutronclient/client.py b/neutronclient/client.py index d2f3685d3..000b422ba 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -303,6 +303,7 @@ def _request(self, url, method, body=None, headers=None, **kwargs): kwargs.setdefault('user_agent', self.USER_AGENT) kwargs.setdefault('auth', self.auth) kwargs.setdefault('authenticated', False) + kwargs.setdefault('raise_exc', False) endpoint_filter = kwargs.setdefault('endpoint_filter', {}) endpoint_filter.setdefault('interface', self.interface) From e3e09159ff1d4f46db2e7831fe05f86b927fb1bf Mon Sep 17 00:00:00 2001 From: Danny Choi Date: Sat, 4 Oct 2014 15:11:19 -0700 Subject: [PATCH 102/845] Change "healthmonitor" to "health monitor" in help info Change "healthmonitor" to "health monitor" in help info for the following commands: lb-healthmonitor-create lb-healthmonitor-delete lb-healthmonitor-list lb-healthmonitor-show lb-healthmonitor-update Change-Id: Icbb219d9e4a2d88e00c5734acc30b1c7609d02fc Closes-Bug: 1377516 --- neutronclient/neutron/v2_0/lb/healthmonitor.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/neutronclient/neutron/v2_0/lb/healthmonitor.py b/neutronclient/neutron/v2_0/lb/healthmonitor.py index f434c40e4..6dfae9841 100644 --- a/neutronclient/neutron/v2_0/lb/healthmonitor.py +++ b/neutronclient/neutron/v2_0/lb/healthmonitor.py @@ -23,7 +23,7 @@ class ListHealthMonitor(neutronV20.ListCommand): - """List healthmonitors that belong to a given tenant.""" + """List health monitors that belong to a given tenant.""" resource = 'health_monitor' list_columns = ['id', 'type', 'admin_state_up'] @@ -32,13 +32,13 @@ class ListHealthMonitor(neutronV20.ListCommand): class ShowHealthMonitor(neutronV20.ShowCommand): - """Show information of a given healthmonitor.""" + """Show information of a given health monitor.""" resource = 'health_monitor' class CreateHealthMonitor(neutronV20.CreateCommand): - """Create a healthmonitor.""" + """Create a health monitor.""" resource = 'health_monitor' @@ -101,14 +101,14 @@ def args2body(self, parsed_args): class UpdateHealthMonitor(neutronV20.UpdateCommand): - """Update a given healthmonitor.""" + """Update a given health monitor.""" resource = 'health_monitor' allow_names = False class DeleteHealthMonitor(neutronV20.DeleteCommand): - """Delete a given healthmonitor.""" + """Delete a given health monitor.""" resource = 'health_monitor' From c02e78240193613595ab6daf352a69e81e08520c Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Mon, 6 Oct 2014 04:37:25 +0900 Subject: [PATCH 103/845] Bump hacking to 0.9.x series In order to sync global-requirements, this patch bumps hacking to 0.9.x series. This patch just add new and stricter hacking rules added by hacking 0.9.x to the ignore list. Fixing up the code and turning these on is out of scope of this patch and is for future patches. Change-Id: I47544f911f21c55705e7832237bb33557f288972 --- test-requirements.txt | 2 +- tox.ini | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/test-requirements.txt b/test-requirements.txt index dc32c243c..3f9a0e68a 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,7 +1,7 @@ # The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. -hacking>=0.8.0,<0.9 +hacking>=0.9.2,<0.10 cliff-tablib>=1.0 coverage>=3.6 diff --git a/tox.ini b/tox.ini index bac7c7607..9f5ac622f 100644 --- a/tox.ini +++ b/tox.ini @@ -34,6 +34,14 @@ downloadcache = ~/cache/pip [flake8] # E125 continuation line does not distinguish itself from next logical line # H302 import only modules -ignore = E125,H302 +# +# TODO Fix the following rules from hacking 0.9.x +# E113 unexpected indentation +# E128 continuation line under-indented for visual indent +# E129 visually indented line with same indent as next logical line +# E265 block comment should start with '# ' +# H405 multi line docstring summary not separated with an empty line +# H307 like imports should be grouped together +ignore = E113,E125,E128,E129,E265,H302,H307,H405 show-source = true exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,tools From 2eba58adc16136e0a0d461ce2494a81688c49302 Mon Sep 17 00:00:00 2001 From: John Schwarz Date: Tue, 30 Sep 2014 15:31:11 +0300 Subject: [PATCH 104/845] Don't get keystone session if using noauth This patch tells neutronclient not to open an authentication session against keystone if using --os-auth-strategy=noauth. The intended behaviour in bp keystone-v3-api-support was that --os-auth-strategy will be completely deprecated and that the absence of --os-auth-url will signify that noauth is needed. This patch does not intend to deprecate the strategy option, since that seems to be a much larger change in the client, so a smaller fix is proposed instead. This is needed for future Neutron integration tests which will be done in-tree, since we don't want Neutron to depend on other projects (including Keystone) during these tests. Closes-bug: #1375765 Change-Id: I9c61e304f75d3d105e1d66782a7495488eefd5e2 --- neutronclient/shell.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 51804e9aa..442eac88a 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -752,13 +752,15 @@ def authenticate_user(self): raise exc.CommandError( _("You must provide an auth url via" " either --os-auth-url or via env[OS_AUTH_URL]")) + auth_session = self._get_keystone_session() + auth = auth_session.auth else: # not keystone if not self.options.os_url: raise exc.CommandError( _("You must provide a service URL via" " either --os-url or env[OS_URL]")) - - auth_session = self._get_keystone_session() + auth_session = None + auth = None self.client_manager = clientmanager.ClientManager( token=self.options.os_token, @@ -783,7 +785,7 @@ def authenticate_user(self): retries=self.options.retries, raise_errors=False, session=auth_session, - auth=auth_session.auth, + auth=auth, log_credentials=True) return From 4d2133c072cf114a1cb10175727e2f737bce56fb Mon Sep 17 00:00:00 2001 From: Jaume Devesa Date: Mon, 6 Oct 2014 12:19:10 +0200 Subject: [PATCH 105/845] Fix E128 hacking check Remove E128 from the ignored checks and fix them. Change-Id: I5e0dd1026803af065a99615e947a145bfc0810f2 --- neutronclient/neutron/v2_0/networkprofile.py | 7 ++++--- neutronclient/neutron/v2_0/nsx/networkgateway.py | 2 +- neutronclient/neutron/v2_0/router.py | 7 ++++--- neutronclient/neutron/v2_0/subnet.py | 8 ++++---- tox.ini | 3 +-- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/neutronclient/neutron/v2_0/networkprofile.py b/neutronclient/neutron/v2_0/networkprofile.py index def744184..e89b0a0ee 100644 --- a/neutronclient/neutron/v2_0/networkprofile.py +++ b/neutronclient/neutron/v2_0/networkprofile.py @@ -54,9 +54,10 @@ def add_known_arguments(self, parser): help='Segment type.') # TODO(Abhishek): Check on sub-type choices depending on segment_type parser.add_argument('--sub_type', - help=_('Sub-type for the segment. Available sub-' - 'types for overlay segments: native, enhanced; ' - 'For trunk segments: vlan, overlay.')) + help=_('Sub-type for the segment. Available ' + 'sub-types for overlay segments: ' + 'native, enhanced; For trunk segments: ' + 'vlan, overlay.')) parser.add_argument('--segment_range', help=_('Range for the segment.')) parser.add_argument('--physical_network', diff --git a/neutronclient/neutron/v2_0/nsx/networkgateway.py b/neutronclient/neutron/v2_0/nsx/networkgateway.py index 0bf810906..d6795687e 100644 --- a/neutronclient/neutron/v2_0/nsx/networkgateway.py +++ b/neutronclient/neutron/v2_0/nsx/networkgateway.py @@ -169,7 +169,7 @@ def add_known_arguments(self, parser): '--device', metavar='id=ID,interface_name=NAME_OR_ID', action='append', help=_('Device info for this gateway. You can repeat this ' - 'option for multiple devices for HA gateways.')) + 'option for multiple devices for HA gateways.')) def args2body(self, parsed_args): body = {self.resource: { diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index 369703e01..71969f575 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -117,9 +117,10 @@ def get_parser(self, prog_name): parser.add_argument( 'interface', metavar='INTERFACE', help=_('The format is "SUBNET|subnet=SUBNET|port=PORT". ' - 'Either a subnet or port must be specified. ' - 'Both ID and name are accepted as SUBNET or PORT. ' - 'Note that "subnet=" can be omitted when specifying a subnet.')) + 'Either a subnet or port must be specified. ' + 'Both ID and name are accepted as SUBNET or PORT. ' + 'Note that "subnet=" can be omitted when specifying a ' + 'subnet.')) return parser def run(self, parsed_args): diff --git a/neutronclient/neutron/v2_0/subnet.py b/neutronclient/neutron/v2_0/subnet.py index 0ba633f98..5e90d0021 100644 --- a/neutronclient/neutron/v2_0/subnet.py +++ b/neutronclient/neutron/v2_0/subnet.py @@ -62,7 +62,7 @@ def add_updatable_arguments(parser): '--allocation-pool', metavar='start=IP_ADDR,end=IP_ADDR', action='append', dest='allocation_pools', type=utils.str2dict, help=_('Allocation pool IP addresses for this subnet ' - '(This option can be repeated).')) + '(This option can be repeated).')) parser.add_argument( '--allocation_pool', action='append', dest='allocation_pools', type=utils.str2dict, @@ -75,7 +75,7 @@ def add_updatable_arguments(parser): '--dns-nameserver', metavar='DNS_NAMESERVER', action='append', dest='dns_nameservers', help=_('DNS name server for this subnet ' - '(This option can be repeated).')) + '(This option can be repeated).')) parser.add_argument( '--disable-dhcp', action='store_true', @@ -97,8 +97,8 @@ def add_updatable_arguments(parser): def updatable_args2body(parsed_args, body): if parsed_args.gateway and parsed_args.no_gateway: raise exceptions.CommandError(_("--gateway option and " - "--no-gateway option can " - "not be used same time")) + "--no-gateway option can " + "not be used same time")) if parsed_args.disable_dhcp and parsed_args.enable_dhcp: raise exceptions.CommandError(_("--enable-dhcp and --disable-dhcp can " "not be used in the same command.")) diff --git a/tox.ini b/tox.ini index 9f5ac622f..feeb3344e 100644 --- a/tox.ini +++ b/tox.ini @@ -37,11 +37,10 @@ downloadcache = ~/cache/pip # # TODO Fix the following rules from hacking 0.9.x # E113 unexpected indentation -# E128 continuation line under-indented for visual indent # E129 visually indented line with same indent as next logical line # E265 block comment should start with '# ' # H405 multi line docstring summary not separated with an empty line # H307 like imports should be grouped together -ignore = E113,E125,E128,E129,E265,H302,H307,H405 +ignore = E113,E125,E129,E265,H302,H307,H405 show-source = true exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,tools From 27f02acc71e48cbeb3855952ab9fef613916c757 Mon Sep 17 00:00:00 2001 From: Christian Berendt Date: Wed, 8 Oct 2014 22:52:02 +0200 Subject: [PATCH 106/845] Remove extraneous vim editor configuration comments Change-Id: I792f24c28063bb57dc2fd295903a7d73c43578d9 Partial-Bug: #1229324 --- neutronclient/tests/unit/test_cli20_agents.py | 2 -- neutronclient/version.py | 1 - 2 files changed, 3 deletions(-) diff --git a/neutronclient/tests/unit/test_cli20_agents.py b/neutronclient/tests/unit/test_cli20_agents.py index 2b0226602..1425ad8bd 100644 --- a/neutronclient/tests/unit/test_cli20_agents.py +++ b/neutronclient/tests/unit/test_cli20_agents.py @@ -11,8 +11,6 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -# -# vim: tabstop=4 shiftwidth=4 softtabstop=4 import sys diff --git a/neutronclient/version.py b/neutronclient/version.py index 9315671c3..189990079 100644 --- a/neutronclient/version.py +++ b/neutronclient/version.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# vim: tabstop=4 shiftwidth=4 softtabstop=4 # @author: Carl Baldwin, Hewlett-Packard import pbr.version From 72afc0f4248a9f081669f55889723db35691e4ac Mon Sep 17 00:00:00 2001 From: Aaron Rosen Date: Wed, 3 Sep 2014 22:01:56 -0700 Subject: [PATCH 107/845] Leverage neutronclient.openstack.common.importutils import_class This patch allows us to drop our version of import_class in favor of openstack.common.importutils version's. Change-Id: Ia96f06e2dc7dfb9824377e3a7381da2732dd2c15 Closes-bug: 1365273 --- neutronclient/common/utils.py | 15 ++------------- neutronclient/tests/unit/test_utils.py | 11 ----------- 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/neutronclient/common/utils.py b/neutronclient/common/utils.py index b68a6089b..bdc4b5336 100644 --- a/neutronclient/common/utils.py +++ b/neutronclient/common/utils.py @@ -19,12 +19,12 @@ import logging import os -import sys import six from neutronclient.common import _ from neutronclient.common import exceptions +from neutronclient.openstack.common import importutils from neutronclient.openstack.common import strutils @@ -40,17 +40,6 @@ def env(*vars, **kwargs): return kwargs.get('default', '') -def import_class(import_str): - """Returns a class from a string including module and class. - - :param import_str: a string representation of the class name - :rtype: the requested class - """ - mod_str, _sep, class_str = import_str.rpartition('.') - __import__(mod_str) - return getattr(sys.modules[mod_str], class_str) - - def get_client_class(api_name, version, version_map): """Returns the client class for the requested API version. @@ -68,7 +57,7 @@ def get_client_class(api_name, version, version_map): 'map_keys': ', '.join(version_map.keys())} raise exceptions.UnsupportedVersion(msg) - return import_class(client_path) + return importutils.import_class(client_path) def get_item_properties(item, fields, mixed_case_fields=(), formatters=None): diff --git a/neutronclient/tests/unit/test_utils.py b/neutronclient/tests/unit/test_utils.py index f38783512..a0044d61a 100644 --- a/neutronclient/tests/unit/test_utils.py +++ b/neutronclient/tests/unit/test_utils.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -import sys - import testtools from neutronclient.common import exceptions @@ -106,15 +104,6 @@ def __call__(self, *args, **kwargs): class ImportClassTestCase(testtools.TestCase): - def test_import_class(self): - dt = utils.import_class('datetime.datetime') - self.assertTrue(sys.modules['datetime'].datetime is dt) - - def test_import_bad_class(self): - self.assertRaises( - ImportError, utils.import_class, - 'lol.u_mad.brah') - def test_get_client_class_invalid_version(self): self.assertRaises( exceptions.UnsupportedVersion, From 0f7741d6985ac4ff5231c2c286c305bf0b444b5e Mon Sep 17 00:00:00 2001 From: John Schwarz Date: Wed, 8 Oct 2014 11:20:46 +0300 Subject: [PATCH 108/845] Add missing parameters to Client's docstring Client's constructor receives keyword arguments (**kwargs), which are then passed to an HTTPClient. Since HTTPClient receives numerous parameters, Client's docstring describes which parameters are expected and their meanings. This patch adds 2 parameters which HTTPClient receives but weren't previously documented in Client's docstring. Change-Id: I12e520f6e26c3aa7c76b190c3e2125d77c71a19a --- neutronclient/v2_0/client.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index fcce02883..d2bb2eda0 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -110,6 +110,8 @@ class Client(object): :param string token: Token for authentication. (optional) :param string tenant_name: Tenant name. (optional) :param string tenant_id: Tenant id. (optional) + :param string auth_strategy: 'keystone' by default, 'noauth' for no + authentication against keystone. (optional) :param string auth_url: Keystone service endpoint for authorization. :param string service_type: Network service type to pull from the keystone catalog (e.g. 'network') (optional) @@ -125,6 +127,8 @@ class Client(object): :param integer timeout: Allows customization of the timeout for client http requests. (optional) :param bool insecure: SSL certificate validation. (optional) + :param bool log_credentials: Allow for logging of passwords or not. + Defaults to False. (optional) :param string ca_cert: SSL CA bundle file to use. (optional) :param integer retries: How many times idempotent (GET, PUT, DELETE) requests to Neutron server should be retried if From 092e6680adf1357ce5a41524f386d1b2073fd453 Mon Sep 17 00:00:00 2001 From: Qin Zhao Date: Tue, 16 Sep 2014 16:09:21 +0800 Subject: [PATCH 109/845] Add InvalidIpForNetworkClient exception When posting an 'attach interface' request to Nova with an invalid ip for defined network, Nova returns an HTTP 500 error. In fact, Neutron returns 'InvalidInput' error, but Neutron client is not able to translate this error to a specific exception. So that a general 'BadRequest' exception is thrown. Neutron client and Nova need a more specific Neutron error type, in order to address and translate the error in to a proper Nova exception. Change-Id: Idd964c33476d7559f642287d577f8a6122d7674c Partial-Bug: 1369871 --- neutronclient/common/exceptions.py | 4 ++++ neutronclient/tests/unit/test_cli20.py | 1 + 2 files changed, 5 insertions(+) diff --git a/neutronclient/common/exceptions.py b/neutronclient/common/exceptions.py index 2fed2ca2f..4c7c9d838 100644 --- a/neutronclient/common/exceptions.py +++ b/neutronclient/common/exceptions.py @@ -140,6 +140,10 @@ class IpAddressInUseClient(Conflict): pass +class InvalidIpForNetworkClient(BadRequest): + pass + + class OverQuotaClient(Conflict): pass diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 78d15676b..e4af7e233 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -666,6 +666,7 @@ def test_exception_handler_v20_neutron_known_error(self): ('ExternalIpAddressExhausted', exceptions.ExternalIpAddressExhaustedClient, 400), ('OverQuota', exceptions.OverQuotaClient, 409), + ('InvalidIpForNetwork', exceptions.InvalidIpForNetworkClient, 400), ] error_msg = 'dummy exception message' From d81222764fd3e5ef9ace9a8bd2c9ef613501ce08 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Wed, 15 Oct 2014 23:46:46 +0000 Subject: [PATCH 110/845] Updated from global requirements Change-Id: I4806c99ba95ba586decb99fdd5a0dce11b2d88b8 --- requirements.txt | 4 ++-- test-requirements.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index 548697cd0..aeeb180ca 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,8 +6,8 @@ argparse cliff>=1.7.0 # Apache-2.0 iso8601>=0.1.9 netaddr>=0.7.12 -requests>=1.2.1,!=2.4.0 -python-keystoneclient>=0.10.0 +requests>=2.2.0,!=2.4.0 +python-keystoneclient>=0.11.1 simplejson>=2.2.0 six>=1.7.0 Babel>=1.3 diff --git a/test-requirements.txt b/test-requirements.txt index 67af12cd1..862f36c34 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -12,6 +12,6 @@ oslosphinx>=2.2.0 # Apache-2.0 oslotest>=1.1.0 # Apache-2.0 python-subunit>=0.0.18 requests-mock>=0.4.0 # Apache-2.0 -sphinx>=1.1.2,!=1.2.0,<1.3 +sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3 testrepository>=0.0.18 testtools>=0.9.34 From 64b2d8a01b74fb9c0e45e452a694d9b67e7088df Mon Sep 17 00:00:00 2001 From: Jaume Devesa Date: Mon, 6 Oct 2014 12:25:48 +0200 Subject: [PATCH 111/845] Fix E129 hacking check Remove E129 from the ignored checks and fix them Change-Id: Ibd3a98ceb173e9a1fa106ac6c4ecdebb1a9a1e4f --- neutronclient/client.py | 2 +- neutronclient/common/serializer.py | 8 ++++---- neutronclient/common/validators.py | 2 +- neutronclient/neutron/v2_0/__init__.py | 4 ++-- neutronclient/neutron/v2_0/nec/packetfilter.py | 2 +- neutronclient/neutron/v2_0/port.py | 2 +- neutronclient/shell.py | 2 +- tox.ini | 3 +-- 8 files changed, 12 insertions(+), 13 deletions(-) diff --git a/neutronclient/client.py b/neutronclient/client.py index 000b422ba..ebf0b972a 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -267,7 +267,7 @@ def _get_endpoint_url(self): body = json.loads(body) for endpoint in body.get('endpoints', []): if (endpoint['type'] == 'network' and - endpoint.get('region') == self.region_name): + endpoint.get('region') == self.region_name): if self.endpoint_type not in endpoint: raise exceptions.EndpointTypeNotFound( type_=self.endpoint_type) diff --git a/neutronclient/common/serializer.py b/neutronclient/common/serializer.py index ef95e402a..0b5efd0cb 100644 --- a/neutronclient/common/serializer.py +++ b/neutronclient/common/serializer.py @@ -291,7 +291,7 @@ def _from_xml(self, datastring): parseError = False # Python2.7 if (hasattr(etree, 'ParseError') and - isinstance(e, getattr(etree, 'ParseError'))): + isinstance(e, getattr(etree, 'ParseError'))): parseError = True # Python2.6 elif isinstance(e, expat.ExpatError): @@ -341,9 +341,9 @@ def _from_xml_node(self, node, listnames): result = dict() for attr in node.keys(): if (attr == 'xmlns' or - attr.startswith('xmlns:') or - attr == constants.XSI_ATTR or - attr == constants.TYPE_ATTR): + attr.startswith('xmlns:') or + attr == constants.XSI_ATTR or + attr == constants.TYPE_ATTR): continue result[self._get_key(attr)] = node.get(attr) children = list(node) diff --git a/neutronclient/common/validators.py b/neutronclient/common/validators.py index 032444c82..e732dfdcb 100644 --- a/neutronclient/common/validators.py +++ b/neutronclient/common/validators.py @@ -29,7 +29,7 @@ def validate_int_range(parsed_args, attr_name, min_value=None, max_value=None): else: int_val = val if ((min_value is None or min_value <= int_val) and - (max_value is None or int_val <= max_value)): + (max_value is None or int_val <= max_value)): return except (ValueError, TypeError): pass diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index 2916ac904..cd565542b 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -283,7 +283,7 @@ def parse_args_to_dict(values_specs): # All others are value items # Make sure '--' occurs first and allow minus value if (not current_item or '=' in current_item or - _item.startswith('-') and not is_number(_item)): + _item.startswith('-') and not is_number(_item)): raise exceptions.CommandError( _("Invalid values_specs %s") % ' '.join(values_specs)) _value_number += 1 @@ -328,7 +328,7 @@ def _merge_args(qCmd, parsed_args, _extra_values, value_specs): if isinstance(arg_value, list): if value and isinstance(value, list): if (not arg_value or - type(arg_value[0]) == type(value[0])): + type(arg_value[0]) == type(value[0])): arg_value.extend(value) _extra_values.pop(key) diff --git a/neutronclient/neutron/v2_0/nec/packetfilter.py b/neutronclient/neutron/v2_0/nec/packetfilter.py index 12fe24d22..957a16ad9 100644 --- a/neutronclient/neutron/v2_0/nec/packetfilter.py +++ b/neutronclient/neutron/v2_0/nec/packetfilter.py @@ -224,7 +224,7 @@ def args2body(self, parsed_args): for attr in ['action', 'priority', 'name']: if (hasattr(parsed_args, attr) and - getattr(parsed_args, attr) is not None): + getattr(parsed_args, attr) is not None): body[attr] = getattr(parsed_args, attr) return {self.resource: body} diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index 3b628b52c..5434de28b 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -122,7 +122,7 @@ def args2body_extradhcpopt(self, parsed_args, port): if opt.split('=')[0] in ['opt_value', 'opt_name']: opt_ele.update(utils.str2dict(opt)) if (('opt_name' in opt_ele) and - ('opt_value' in opt_ele)): + ('opt_value' in opt_ele)): if opt_ele['opt_value'] == 'null': opt_ele['opt_value'] = None ops.append(opt_ele) diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 9d235d191..7ce585b31 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -724,7 +724,7 @@ def authenticate_user(self): self.options.os_project_id) if (not self.options.os_username - and not self.options.os_user_id): + and not self.options.os_user_id): raise exc.CommandError( _("You must provide a username or user ID via" " --os-username, env[OS_USERNAME] or" diff --git a/tox.ini b/tox.ini index b34d93606..10140ed5a 100644 --- a/tox.ini +++ b/tox.ini @@ -37,10 +37,9 @@ downloadcache = ~/cache/pip # # TODO Fix the following rules from hacking 0.9.x # E113 unexpected indentation -# E129 visually indented line with same indent as next logical line # E265 block comment should start with '# ' # H405 multi line docstring summary not separated with an empty line # H307 like imports should be grouped together -ignore = E113,E125,E129,E265,H302,H307,H405 +ignore = E113,E125,E265,H302,H307,H405 show-source = true exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,tools From d046a959460251c597158152b908f11353feff62 Mon Sep 17 00:00:00 2001 From: Jaume Devesa Date: Mon, 6 Oct 2014 12:11:39 +0200 Subject: [PATCH 112/845] Fix E113 hacking check Remove E113 from the ignored check and fix them. Just one of them raised. Change-Id: Ie4897264793a34c1fd5f64e7ca4f285f3b2d4504 --- neutronclient/neutron/v2_0/__init__.py | 4 ++-- tox.ini | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index cd565542b..eb118b191 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -385,8 +385,8 @@ def __init__(self, app, app_args): # NOTE(markmcclain): This is no longer supported in cliff version 1.5.2 # see https://bugs.launchpad.net/python-neutronclient/+bug/1265926 - #if hasattr(self, 'formatters'): - #self.formatters['table'] = TableFormater() + # if hasattr(self, 'formatters'): + # self.formatters['table'] = TableFormater() @property def cmd_resource(self): diff --git a/tox.ini b/tox.ini index 10140ed5a..833f7b031 100644 --- a/tox.ini +++ b/tox.ini @@ -36,10 +36,9 @@ downloadcache = ~/cache/pip # H302 import only modules # # TODO Fix the following rules from hacking 0.9.x -# E113 unexpected indentation # E265 block comment should start with '# ' # H405 multi line docstring summary not separated with an empty line # H307 like imports should be grouped together -ignore = E113,E125,E265,H302,H307,H405 +ignore = E125,E265,H302,H307,H405 show-source = true exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,tools From 9c464ba53f0c1317a397f96202b577f0729fc4eb Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Thu, 16 Oct 2014 19:23:53 +0900 Subject: [PATCH 113/845] Use graduated oslo libraries The following libraries have been graduated: - gettextutils -> oslo.i18n - jsonutils -> oslo.serialization - strutils, timeutils -> oslo.utils Add neutronclient.i18n module as oslo.i18n adapter. Remove openstack.common related files completely because neutronclient no longer use any oslo-incubator modules. Closes-Bug: #1382017 Change-Id: Ib7ebcb691a12858049baab3b122f95bf43038f18 --- neutronclient/client.py | 2 +- neutronclient/common/serializer.py | 4 +- neutronclient/common/utils.py | 8 +- neutronclient/common/validators.py | 2 +- .../{openstack/common/__init__.py => i18n.py} | 17 +- neutronclient/neutron/client.py | 2 +- neutronclient/neutron/v2_0/__init__.py | 4 +- neutronclient/neutron/v2_0/agentscheduler.py | 2 +- neutronclient/neutron/v2_0/credential.py | 2 +- neutronclient/neutron/v2_0/floatingip.py | 2 +- neutronclient/neutron/v2_0/fw/firewall.py | 2 +- .../neutron/v2_0/fw/firewallpolicy.py | 2 +- neutronclient/neutron/v2_0/fw/firewallrule.py | 2 +- .../neutron/v2_0/lb/healthmonitor.py | 2 +- neutronclient/neutron/v2_0/lb/member.py | 2 +- neutronclient/neutron/v2_0/lb/pool.py | 2 +- neutronclient/neutron/v2_0/lb/vip.py | 2 +- neutronclient/neutron/v2_0/metering.py | 2 +- .../neutron/v2_0/nec/packetfilter.py | 2 +- neutronclient/neutron/v2_0/network.py | 2 +- neutronclient/neutron/v2_0/networkprofile.py | 2 +- .../neutron/v2_0/nsx/networkgateway.py | 2 +- neutronclient/neutron/v2_0/nsx/qos_queue.py | 2 +- neutronclient/neutron/v2_0/policyprofile.py | 2 +- neutronclient/neutron/v2_0/port.py | 5 +- neutronclient/neutron/v2_0/quota.py | 4 +- neutronclient/neutron/v2_0/router.py | 5 +- neutronclient/neutron/v2_0/securitygroup.py | 2 +- neutronclient/neutron/v2_0/subnet.py | 5 +- neutronclient/neutron/v2_0/vpn/ikepolicy.py | 2 +- .../neutron/v2_0/vpn/ipsec_site_connection.py | 5 +- neutronclient/neutron/v2_0/vpn/ipsecpolicy.py | 2 +- neutronclient/neutron/v2_0/vpn/utils.py | 2 +- neutronclient/neutron/v2_0/vpn/vpnservice.py | 2 +- neutronclient/openstack/__init__.py | 0 .../openstack/common/gettextutils.py | 498 ------------------ neutronclient/openstack/common/importutils.py | 73 --- neutronclient/openstack/common/jsonutils.py | 186 ------- neutronclient/openstack/common/strutils.py | 239 --------- neutronclient/openstack/common/timeutils.py | 210 -------- neutronclient/shell.py | 6 +- neutronclient/tests/unit/test_auth.py | 2 +- neutronclient/tests/unit/test_cli20_agents.py | 3 +- .../tests/unit/test_cli20_network.py | 2 +- openstack-common.conf | 7 - requirements.txt | 3 + 46 files changed, 71 insertions(+), 1265 deletions(-) rename neutronclient/{openstack/common/__init__.py => i18n.py} (54%) delete mode 100644 neutronclient/openstack/__init__.py delete mode 100644 neutronclient/openstack/common/gettextutils.py delete mode 100644 neutronclient/openstack/common/importutils.py delete mode 100644 neutronclient/openstack/common/jsonutils.py delete mode 100644 neutronclient/openstack/common/strutils.py delete mode 100644 neutronclient/openstack/common/timeutils.py delete mode 100644 openstack-common.conf diff --git a/neutronclient/client.py b/neutronclient/client.py index ebf0b972a..1ff612706 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -29,7 +29,7 @@ from neutronclient.common import exceptions from neutronclient.common import utils -from neutronclient.openstack.common.gettextutils import _ +from neutronclient.i18n import _ _logger = logging.getLogger(__name__) diff --git a/neutronclient/common/serializer.py b/neutronclient/common/serializer.py index 0b5efd0cb..267d3edd6 100644 --- a/neutronclient/common/serializer.py +++ b/neutronclient/common/serializer.py @@ -21,12 +21,12 @@ from xml.etree import ElementTree as etree from xml.parsers import expat +from oslo.serialization import jsonutils import six from neutronclient.common import constants from neutronclient.common import exceptions as exception -from neutronclient.openstack.common.gettextutils import _ -from neutronclient.openstack.common import jsonutils +from neutronclient.i18n import _ LOG = logging.getLogger(__name__) diff --git a/neutronclient/common/utils.py b/neutronclient/common/utils.py index bdc4b5336..28137b7fa 100644 --- a/neutronclient/common/utils.py +++ b/neutronclient/common/utils.py @@ -20,12 +20,12 @@ import logging import os +from oslo.utils import encodeutils +from oslo.utils import importutils import six from neutronclient.common import _ from neutronclient.common import exceptions -from neutronclient.openstack.common import importutils -from neutronclient.openstack.common import strutils def env(*vars, **kwargs): @@ -125,7 +125,7 @@ def http_log_req(_logger, args, kwargs): if 'body' in kwargs and kwargs['body']: string_parts.append(" -d '%s'" % (kwargs['body'])) - req = strutils.safe_encode("".join(string_parts)) + req = encodeutils.safe_encode("".join(string_parts)) _logger.debug("\nREQ: %s\n", req) @@ -140,7 +140,7 @@ def http_log_resp(_logger, resp, body): def _safe_encode_without_obj(data): if isinstance(data, six.string_types): - return strutils.safe_encode(data) + return encodeutils.safe_encode(data) return data diff --git a/neutronclient/common/validators.py b/neutronclient/common/validators.py index e732dfdcb..304ba35c5 100644 --- a/neutronclient/common/validators.py +++ b/neutronclient/common/validators.py @@ -16,7 +16,7 @@ import netaddr from neutronclient.common import exceptions -from neutronclient.openstack.common.gettextutils import _ +from neutronclient.i18n import _ def validate_int_range(parsed_args, attr_name, min_value=None, max_value=None): diff --git a/neutronclient/openstack/common/__init__.py b/neutronclient/i18n.py similarity index 54% rename from neutronclient/openstack/common/__init__.py rename to neutronclient/i18n.py index d1223eaf7..f8084067f 100644 --- a/neutronclient/openstack/common/__init__.py +++ b/neutronclient/i18n.py @@ -1,4 +1,3 @@ -# # 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 @@ -11,7 +10,19 @@ # License for the specific language governing permissions and limitations # under the License. -import six +from oslo import i18n + +_translators = i18n.TranslatorFactory(domain='neutronclient') +# The primary translation function using the well-known name "_" +_ = _translators.primary -six.add_move(six.MovedModule('mox', 'mox', 'mox3.mox')) +# Translators for log levels. +# +# The abbreviated names are meant to reflect the usual use of a short +# name like '_'. The "L" is for "log" and the other letter comes from +# the level. +_LI = _translators.log_info +_LW = _translators.log_warning +_LE = _translators.log_error +_LC = _translators.log_critical diff --git a/neutronclient/neutron/client.py b/neutronclient/neutron/client.py index 4f0c5b01d..7236a8a0a 100644 --- a/neutronclient/neutron/client.py +++ b/neutronclient/neutron/client.py @@ -16,7 +16,7 @@ from neutronclient.common import exceptions from neutronclient.common import utils -from neutronclient.openstack.common.gettextutils import _ +from neutronclient.i18n import _ API_NAME = 'network' diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index eb118b191..538b48c59 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -24,13 +24,13 @@ from cliff.formatters import table from cliff import lister from cliff import show +from oslo.serialization import jsonutils import six from neutronclient.common import command from neutronclient.common import exceptions from neutronclient.common import utils -from neutronclient.openstack.common.gettextutils import _ -from neutronclient.openstack.common import jsonutils +from neutronclient.i18n import _ HEX_ELEM = '[0-9A-Fa-f]' UUID_PATTERN = '-'.join([HEX_ELEM + '{8}', HEX_ELEM + '{4}', diff --git a/neutronclient/neutron/v2_0/agentscheduler.py b/neutronclient/neutron/v2_0/agentscheduler.py index db702b5bf..e77f186db 100644 --- a/neutronclient/neutron/v2_0/agentscheduler.py +++ b/neutronclient/neutron/v2_0/agentscheduler.py @@ -16,10 +16,10 @@ from __future__ import print_function +from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 from neutronclient.neutron.v2_0 import network from neutronclient.neutron.v2_0 import router -from neutronclient.openstack.common.gettextutils import _ PERFECT_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%f" diff --git a/neutronclient/neutron/v2_0/credential.py b/neutronclient/neutron/v2_0/credential.py index 8dd38a113..ffec5ee1d 100644 --- a/neutronclient/neutron/v2_0/credential.py +++ b/neutronclient/neutron/v2_0/credential.py @@ -11,8 +11,8 @@ # under the License. # +from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 -from neutronclient.openstack.common.gettextutils import _ class ListCredential(neutronV20.ListCommand): diff --git a/neutronclient/neutron/v2_0/floatingip.py b/neutronclient/neutron/v2_0/floatingip.py index e995bcac0..f52e52a77 100644 --- a/neutronclient/neutron/v2_0/floatingip.py +++ b/neutronclient/neutron/v2_0/floatingip.py @@ -18,8 +18,8 @@ import argparse +from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 -from neutronclient.openstack.common.gettextutils import _ class ListFloatingIP(neutronV20.ListCommand): diff --git a/neutronclient/neutron/v2_0/fw/firewall.py b/neutronclient/neutron/v2_0/fw/firewall.py index 06b804037..149036a2a 100644 --- a/neutronclient/neutron/v2_0/fw/firewall.py +++ b/neutronclient/neutron/v2_0/fw/firewall.py @@ -18,8 +18,8 @@ import argparse +from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronv20 -from neutronclient.openstack.common.gettextutils import _ class ListFirewall(neutronv20.ListCommand): diff --git a/neutronclient/neutron/v2_0/fw/firewallpolicy.py b/neutronclient/neutron/v2_0/fw/firewallpolicy.py index e2771c26c..5d1a4b7f7 100644 --- a/neutronclient/neutron/v2_0/fw/firewallpolicy.py +++ b/neutronclient/neutron/v2_0/fw/firewallpolicy.py @@ -20,8 +20,8 @@ import argparse +from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronv20 -from neutronclient.openstack.common.gettextutils import _ def _format_firewall_rules(firewall_policy): diff --git a/neutronclient/neutron/v2_0/fw/firewallrule.py b/neutronclient/neutron/v2_0/fw/firewallrule.py index aa4b90c53..03f425c7b 100644 --- a/neutronclient/neutron/v2_0/fw/firewallrule.py +++ b/neutronclient/neutron/v2_0/fw/firewallrule.py @@ -18,8 +18,8 @@ import argparse +from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronv20 -from neutronclient.openstack.common.gettextutils import _ class ListFirewallRule(neutronv20.ListCommand): diff --git a/neutronclient/neutron/v2_0/lb/healthmonitor.py b/neutronclient/neutron/v2_0/lb/healthmonitor.py index 6dfae9841..83c2cf57f 100644 --- a/neutronclient/neutron/v2_0/lb/healthmonitor.py +++ b/neutronclient/neutron/v2_0/lb/healthmonitor.py @@ -18,8 +18,8 @@ from __future__ import print_function +from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 -from neutronclient.openstack.common.gettextutils import _ class ListHealthMonitor(neutronV20.ListCommand): diff --git a/neutronclient/neutron/v2_0/lb/member.py b/neutronclient/neutron/v2_0/lb/member.py index 1d589f269..7fd6f1ea0 100644 --- a/neutronclient/neutron/v2_0/lb/member.py +++ b/neutronclient/neutron/v2_0/lb/member.py @@ -16,8 +16,8 @@ # @author: Ilya Shakhat, Mirantis Inc. # +from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 -from neutronclient.openstack.common.gettextutils import _ class ListMember(neutronV20.ListCommand): diff --git a/neutronclient/neutron/v2_0/lb/pool.py b/neutronclient/neutron/v2_0/lb/pool.py index f491f74b5..c10f7a87f 100644 --- a/neutronclient/neutron/v2_0/lb/pool.py +++ b/neutronclient/neutron/v2_0/lb/pool.py @@ -19,8 +19,8 @@ import six +from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 -from neutronclient.openstack.common.gettextutils import _ def _format_provider(pool): diff --git a/neutronclient/neutron/v2_0/lb/vip.py b/neutronclient/neutron/v2_0/lb/vip.py index 8b1108c0d..3c85d3378 100644 --- a/neutronclient/neutron/v2_0/lb/vip.py +++ b/neutronclient/neutron/v2_0/lb/vip.py @@ -16,8 +16,8 @@ # @author: Ilya Shakhat, Mirantis Inc. # +from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 -from neutronclient.openstack.common.gettextutils import _ class ListVip(neutronV20.ListCommand): diff --git a/neutronclient/neutron/v2_0/metering.py b/neutronclient/neutron/v2_0/metering.py index 20ca7572f..c2649c483 100644 --- a/neutronclient/neutron/v2_0/metering.py +++ b/neutronclient/neutron/v2_0/metering.py @@ -14,8 +14,8 @@ # License for the specific language governing permissions and limitations # under the License. +from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronv20 -from neutronclient.openstack.common.gettextutils import _ class ListMeteringLabel(neutronv20.ListCommand): diff --git a/neutronclient/neutron/v2_0/nec/packetfilter.py b/neutronclient/neutron/v2_0/nec/packetfilter.py index 957a16ad9..48d0f744d 100644 --- a/neutronclient/neutron/v2_0/nec/packetfilter.py +++ b/neutronclient/neutron/v2_0/nec/packetfilter.py @@ -15,8 +15,8 @@ from neutronclient.common import exceptions from neutronclient.common import validators +from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 -from neutronclient.openstack.common.gettextutils import _ class ListPacketFilter(neutronV20.ListCommand): diff --git a/neutronclient/neutron/v2_0/network.py b/neutronclient/neutron/v2_0/network.py index 54d546570..a984926aa 100644 --- a/neutronclient/neutron/v2_0/network.py +++ b/neutronclient/neutron/v2_0/network.py @@ -17,8 +17,8 @@ import argparse from neutronclient.common import exceptions +from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 -from neutronclient.openstack.common.gettextutils import _ def _format_subnets(network): diff --git a/neutronclient/neutron/v2_0/networkprofile.py b/neutronclient/neutron/v2_0/networkprofile.py index e89b0a0ee..03fc4972a 100644 --- a/neutronclient/neutron/v2_0/networkprofile.py +++ b/neutronclient/neutron/v2_0/networkprofile.py @@ -16,9 +16,9 @@ from __future__ import print_function +from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 from neutronclient.neutron.v2_0 import parse_args_to_dict -from neutronclient.openstack.common.gettextutils import _ RESOURCE = 'network_profile' SEGMENT_TYPE_CHOICES = ['vlan', 'overlay', 'multi-segment', 'trunk'] diff --git a/neutronclient/neutron/v2_0/nsx/networkgateway.py b/neutronclient/neutron/v2_0/nsx/networkgateway.py index d6795687e..52a38256b 100644 --- a/neutronclient/neutron/v2_0/nsx/networkgateway.py +++ b/neutronclient/neutron/v2_0/nsx/networkgateway.py @@ -17,8 +17,8 @@ from __future__ import print_function from neutronclient.common import utils +from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 -from neutronclient.openstack.common.gettextutils import _ GW_RESOURCE = 'network_gateway' DEV_RESOURCE = 'gateway_device' diff --git a/neutronclient/neutron/v2_0/nsx/qos_queue.py b/neutronclient/neutron/v2_0/nsx/qos_queue.py index e8c8254fc..7f6cb68b2 100644 --- a/neutronclient/neutron/v2_0/nsx/qos_queue.py +++ b/neutronclient/neutron/v2_0/nsx/qos_queue.py @@ -13,8 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. +from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 -from neutronclient.openstack.common.gettextutils import _ class ListQoSQueue(neutronV20.ListCommand): diff --git a/neutronclient/neutron/v2_0/policyprofile.py b/neutronclient/neutron/v2_0/policyprofile.py index 580ef8ca7..03fd8af10 100644 --- a/neutronclient/neutron/v2_0/policyprofile.py +++ b/neutronclient/neutron/v2_0/policyprofile.py @@ -15,9 +15,9 @@ from __future__ import print_function +from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 from neutronclient.neutron.v2_0 import parse_args_to_dict -from neutronclient.openstack.common.gettextutils import _ RESOURCE = 'policy_profile' diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index 5434de28b..4d2d7fa30 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -16,11 +16,12 @@ import argparse +from oslo.serialization import jsonutils + from neutronclient.common import exceptions from neutronclient.common import utils +from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 -from neutronclient.openstack.common.gettextutils import _ -from neutronclient.openstack.common import jsonutils def _format_fixed_ips(port): diff --git a/neutronclient/neutron/v2_0/quota.py b/neutronclient/neutron/v2_0/quota.py index 6e5ad174f..780f510b1 100644 --- a/neutronclient/neutron/v2_0/quota.py +++ b/neutronclient/neutron/v2_0/quota.py @@ -20,13 +20,13 @@ from cliff import lister from cliff import show +from oslo.serialization import jsonutils import six from neutronclient.common import exceptions from neutronclient.common import utils +from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 -from neutronclient.openstack.common.gettextutils import _ -from neutronclient.openstack.common import jsonutils def get_tenant_id(tenant_id, client): diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index 71969f575..6973218cb 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -18,10 +18,11 @@ import argparse +from oslo.serialization import jsonutils + from neutronclient.common import exceptions +from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 -from neutronclient.openstack.common.gettextutils import _ -from neutronclient.openstack.common import jsonutils def _format_external_gateway_info(router): diff --git a/neutronclient/neutron/v2_0/securitygroup.py b/neutronclient/neutron/v2_0/securitygroup.py index 8c554a904..a1a2bf5b9 100644 --- a/neutronclient/neutron/v2_0/securitygroup.py +++ b/neutronclient/neutron/v2_0/securitygroup.py @@ -17,8 +17,8 @@ import argparse from neutronclient.common import exceptions +from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 -from neutronclient.openstack.common.gettextutils import _ class ListSecurityGroup(neutronV20.ListCommand): diff --git a/neutronclient/neutron/v2_0/subnet.py b/neutronclient/neutron/v2_0/subnet.py index 5e90d0021..ed5b478e3 100644 --- a/neutronclient/neutron/v2_0/subnet.py +++ b/neutronclient/neutron/v2_0/subnet.py @@ -16,11 +16,12 @@ import argparse +from oslo.serialization import jsonutils + from neutronclient.common import exceptions from neutronclient.common import utils +from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 -from neutronclient.openstack.common.gettextutils import _ -from neutronclient.openstack.common import jsonutils def _format_allocation_pools(subnet): diff --git a/neutronclient/neutron/v2_0/vpn/ikepolicy.py b/neutronclient/neutron/v2_0/vpn/ikepolicy.py index 71df04772..e2f5d2861 100644 --- a/neutronclient/neutron/v2_0/vpn/ikepolicy.py +++ b/neutronclient/neutron/v2_0/vpn/ikepolicy.py @@ -17,9 +17,9 @@ # from neutronclient.common import utils +from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronv20 from neutronclient.neutron.v2_0.vpn import utils as vpn_utils -from neutronclient.openstack.common.gettextutils import _ class ListIKEPolicy(neutronv20.ListCommand): diff --git a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py index 116451158..59d676e0a 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py +++ b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py @@ -16,12 +16,13 @@ # @author: Swaminathan Vasudevan, Hewlett-Packard. # +from oslo.serialization import jsonutils + from neutronclient.common import exceptions from neutronclient.common import utils +from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronv20 from neutronclient.neutron.v2_0.vpn import utils as vpn_utils -from neutronclient.openstack.common.gettextutils import _ -from neutronclient.openstack.common import jsonutils def _format_peer_cidrs(ipsec_site_connection): diff --git a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py index 99eb844e7..cade641b9 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py +++ b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py @@ -16,9 +16,9 @@ # @author: Swaminathan Vasudevan, Hewlett-Packard. from neutronclient.common import utils +from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronv20 from neutronclient.neutron.v2_0.vpn import utils as vpn_utils -from neutronclient.openstack.common.gettextutils import _ class ListIPsecPolicy(neutronv20.ListCommand): diff --git a/neutronclient/neutron/v2_0/vpn/utils.py b/neutronclient/neutron/v2_0/vpn/utils.py index 832a6a2c7..74fbeef6f 100644 --- a/neutronclient/neutron/v2_0/vpn/utils.py +++ b/neutronclient/neutron/v2_0/vpn/utils.py @@ -21,7 +21,7 @@ from neutronclient.common import exceptions -from neutronclient.openstack.common.gettextutils import _ +from neutronclient.i18n import _ dpd_supported_actions = ['hold', 'clear', 'restart', 'restart-by-peer', 'disabled'] diff --git a/neutronclient/neutron/v2_0/vpn/vpnservice.py b/neutronclient/neutron/v2_0/vpn/vpnservice.py index 4dac74375..cf892646f 100644 --- a/neutronclient/neutron/v2_0/vpn/vpnservice.py +++ b/neutronclient/neutron/v2_0/vpn/vpnservice.py @@ -16,8 +16,8 @@ # @author: Swaminathan Vasudevan, Hewlett-Packard. # +from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronv20 -from neutronclient.openstack.common.gettextutils import _ class ListVPNService(neutronv20.ListCommand): diff --git a/neutronclient/openstack/__init__.py b/neutronclient/openstack/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutronclient/openstack/common/gettextutils.py b/neutronclient/openstack/common/gettextutils.py deleted file mode 100644 index 22243e156..000000000 --- a/neutronclient/openstack/common/gettextutils.py +++ /dev/null @@ -1,498 +0,0 @@ -# Copyright 2012 Red Hat, Inc. -# Copyright 2013 IBM Corp. -# 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. - -""" -gettext for openstack-common modules. - -Usual usage in an openstack.common module: - - from neutronclient.openstack.common.gettextutils import _ -""" - -import copy -import functools -import gettext -import locale -from logging import handlers -import os - -from babel import localedata -import six - -_AVAILABLE_LANGUAGES = {} - -# FIXME(dhellmann): Remove this when moving to oslo.i18n. -USE_LAZY = False - - -class TranslatorFactory(object): - """Create translator functions - """ - - def __init__(self, domain, lazy=False, localedir=None): - """Establish a set of translation functions for the domain. - - :param domain: Name of translation domain, - specifying a message catalog. - :type domain: str - :param lazy: Delays translation until a message is emitted. - Defaults to False. - :type lazy: Boolean - :param localedir: Directory with translation catalogs. - :type localedir: str - """ - self.domain = domain - self.lazy = lazy - if localedir is None: - localedir = os.environ.get(domain.upper() + '_LOCALEDIR') - self.localedir = localedir - - def _make_translation_func(self, domain=None): - """Return a new translation function ready for use. - - Takes into account whether or not lazy translation is being - done. - - The domain can be specified to override the default from the - factory, but the localedir from the factory is always used - because we assume the log-level translation catalogs are - installed in the same directory as the main application - catalog. - - """ - if domain is None: - domain = self.domain - if self.lazy: - return functools.partial(Message, domain=domain) - t = gettext.translation( - domain, - localedir=self.localedir, - fallback=True, - ) - if six.PY3: - return t.gettext - return t.ugettext - - @property - def primary(self): - "The default translation function." - return self._make_translation_func() - - def _make_log_translation_func(self, level): - return self._make_translation_func(self.domain + '-log-' + level) - - @property - def log_info(self): - "Translate info-level log messages." - return self._make_log_translation_func('info') - - @property - def log_warning(self): - "Translate warning-level log messages." - return self._make_log_translation_func('warning') - - @property - def log_error(self): - "Translate error-level log messages." - return self._make_log_translation_func('error') - - @property - def log_critical(self): - "Translate critical-level log messages." - return self._make_log_translation_func('critical') - - -# NOTE(dhellmann): When this module moves out of the incubator into -# oslo.i18n, these global variables can be moved to an integration -# module within each application. - -# Create the global translation functions. -_translators = TranslatorFactory('neutronclient') - -# The primary translation function using the well-known name "_" -_ = _translators.primary - -# Translators for log levels. -# -# The abbreviated names are meant to reflect the usual use of a short -# name like '_'. The "L" is for "log" and the other letter comes from -# the level. -_LI = _translators.log_info -_LW = _translators.log_warning -_LE = _translators.log_error -_LC = _translators.log_critical - -# NOTE(dhellmann): End of globals that will move to the application's -# integration module. - - -def enable_lazy(): - """Convenience function for configuring _() to use lazy gettext - - Call this at the start of execution to enable the gettextutils._ - function to use lazy gettext functionality. This is useful if - your project is importing _ directly instead of using the - gettextutils.install() way of importing the _ function. - """ - # FIXME(dhellmann): This function will be removed in oslo.i18n, - # because the TranslatorFactory makes it superfluous. - global _, _LI, _LW, _LE, _LC, USE_LAZY - tf = TranslatorFactory('neutronclient', lazy=True) - _ = tf.primary - _LI = tf.log_info - _LW = tf.log_warning - _LE = tf.log_error - _LC = tf.log_critical - USE_LAZY = True - - -def install(domain, lazy=False): - """Install a _() function using the given translation domain. - - Given a translation domain, install a _() function using gettext's - install() function. - - The main difference from gettext.install() is that we allow - overriding the default localedir (e.g. /usr/share/locale) using - a translation-domain-specific environment variable (e.g. - NOVA_LOCALEDIR). - - :param domain: the translation domain - :param lazy: indicates whether or not to install the lazy _() function. - The lazy _() introduces a way to do deferred translation - of messages by installing a _ that builds Message objects, - instead of strings, which can then be lazily translated into - any available locale. - """ - if lazy: - from six import moves - tf = TranslatorFactory(domain, lazy=True) - moves.builtins.__dict__['_'] = tf.primary - else: - localedir = '%s_LOCALEDIR' % domain.upper() - if six.PY3: - gettext.install(domain, - localedir=os.environ.get(localedir)) - else: - gettext.install(domain, - localedir=os.environ.get(localedir), - unicode=True) - - -class Message(six.text_type): - """A Message object is a unicode object that can be translated. - - Translation of Message is done explicitly using the translate() method. - For all non-translation intents and purposes, a Message is simply unicode, - and can be treated as such. - """ - - def __new__(cls, msgid, msgtext=None, params=None, - domain='neutronclient', *args): - """Create a new Message object. - - In order for translation to work gettext requires a message ID, this - msgid will be used as the base unicode text. It is also possible - for the msgid and the base unicode text to be different by passing - the msgtext parameter. - """ - # If the base msgtext is not given, we use the default translation - # of the msgid (which is in English) just in case the system locale is - # not English, so that the base text will be in that locale by default. - if not msgtext: - msgtext = Message._translate_msgid(msgid, domain) - # We want to initialize the parent unicode with the actual object that - # would have been plain unicode if 'Message' was not enabled. - msg = super(Message, cls).__new__(cls, msgtext) - msg.msgid = msgid - msg.domain = domain - msg.params = params - return msg - - def translate(self, desired_locale=None): - """Translate this message to the desired locale. - - :param desired_locale: The desired locale to translate the message to, - if no locale is provided the message will be - translated to the system's default locale. - - :returns: the translated message in unicode - """ - - translated_message = Message._translate_msgid(self.msgid, - self.domain, - desired_locale) - if self.params is None: - # No need for more translation - return translated_message - - # This Message object may have been formatted with one or more - # Message objects as substitution arguments, given either as a single - # argument, part of a tuple, or as one or more values in a dictionary. - # When translating this Message we need to translate those Messages too - translated_params = _translate_args(self.params, desired_locale) - - translated_message = translated_message % translated_params - - return translated_message - - @staticmethod - def _translate_msgid(msgid, domain, desired_locale=None): - if not desired_locale: - system_locale = locale.getdefaultlocale() - # If the system locale is not available to the runtime use English - if not system_locale[0]: - desired_locale = 'en_US' - else: - desired_locale = system_locale[0] - - locale_dir = os.environ.get(domain.upper() + '_LOCALEDIR') - lang = gettext.translation(domain, - localedir=locale_dir, - languages=[desired_locale], - fallback=True) - if six.PY3: - translator = lang.gettext - else: - translator = lang.ugettext - - translated_message = translator(msgid) - return translated_message - - def __mod__(self, other): - # When we mod a Message we want the actual operation to be performed - # by the parent class (i.e. unicode()), the only thing we do here is - # save the original msgid and the parameters in case of a translation - params = self._sanitize_mod_params(other) - unicode_mod = super(Message, self).__mod__(params) - modded = Message(self.msgid, - msgtext=unicode_mod, - params=params, - domain=self.domain) - return modded - - def _sanitize_mod_params(self, other): - """Sanitize the object being modded with this Message. - - - Add support for modding 'None' so translation supports it - - Trim the modded object, which can be a large dictionary, to only - those keys that would actually be used in a translation - - Snapshot the object being modded, in case the message is - translated, it will be used as it was when the Message was created - """ - if other is None: - params = (other,) - elif isinstance(other, dict): - # Merge the dictionaries - # Copy each item in case one does not support deep copy. - params = {} - if isinstance(self.params, dict): - for key, val in self.params.items(): - params[key] = self._copy_param(val) - for key, val in other.items(): - params[key] = self._copy_param(val) - else: - params = self._copy_param(other) - return params - - def _copy_param(self, param): - try: - return copy.deepcopy(param) - except Exception: - # Fallback to casting to unicode this will handle the - # python code-like objects that can't be deep-copied - return six.text_type(param) - - def __add__(self, other): - msg = _('Message objects do not support addition.') - raise TypeError(msg) - - def __radd__(self, other): - return self.__add__(other) - - if six.PY2: - def __str__(self): - # NOTE(luisg): Logging in python 2.6 tries to str() log records, - # and it expects specifically a UnicodeError in order to proceed. - msg = _('Message objects do not support str() because they may ' - 'contain non-ascii characters. ' - 'Please use unicode() or translate() instead.') - raise UnicodeError(msg) - - -def get_available_languages(domain): - """Lists the available languages for the given translation domain. - - :param domain: the domain to get languages for - """ - if domain in _AVAILABLE_LANGUAGES: - return copy.copy(_AVAILABLE_LANGUAGES[domain]) - - localedir = '%s_LOCALEDIR' % domain.upper() - find = lambda x: gettext.find(domain, - localedir=os.environ.get(localedir), - languages=[x]) - - # NOTE(mrodden): en_US should always be available (and first in case - # order matters) since our in-line message strings are en_US - language_list = ['en_US'] - # NOTE(luisg): Babel <1.0 used a function called list(), which was - # renamed to locale_identifiers() in >=1.0, the requirements master list - # requires >=0.9.6, uncapped, so defensively work with both. We can remove - # this check when the master list updates to >=1.0, and update all projects - list_identifiers = (getattr(localedata, 'list', None) or - getattr(localedata, 'locale_identifiers')) - locale_identifiers = list_identifiers() - - for i in locale_identifiers: - if find(i) is not None: - language_list.append(i) - - # NOTE(luisg): Babel>=1.0,<1.3 has a bug where some OpenStack supported - # locales (e.g. 'zh_CN', and 'zh_TW') aren't supported even though they - # are perfectly legitimate locales: - # https://github.com/mitsuhiko/babel/issues/37 - # In Babel 1.3 they fixed the bug and they support these locales, but - # they are still not explicitly "listed" by locale_identifiers(). - # That is why we add the locales here explicitly if necessary so that - # they are listed as supported. - aliases = {'zh': 'zh_CN', - 'zh_Hant_HK': 'zh_HK', - 'zh_Hant': 'zh_TW', - 'fil': 'tl_PH'} - for (locale, alias) in six.iteritems(aliases): - if locale in language_list and alias not in language_list: - language_list.append(alias) - - _AVAILABLE_LANGUAGES[domain] = language_list - return copy.copy(language_list) - - -def translate(obj, desired_locale=None): - """Gets the translated unicode representation of the given object. - - If the object is not translatable it is returned as-is. - If the locale is None the object is translated to the system locale. - - :param obj: the object to translate - :param desired_locale: the locale to translate the message to, if None the - default system locale will be used - :returns: the translated object in unicode, or the original object if - it could not be translated - """ - message = obj - if not isinstance(message, Message): - # If the object to translate is not already translatable, - # let's first get its unicode representation - message = six.text_type(obj) - if isinstance(message, Message): - # Even after unicoding() we still need to check if we are - # running with translatable unicode before translating - return message.translate(desired_locale) - return obj - - -def _translate_args(args, desired_locale=None): - """Translates all the translatable elements of the given arguments object. - - This method is used for translating the translatable values in method - arguments which include values of tuples or dictionaries. - If the object is not a tuple or a dictionary the object itself is - translated if it is translatable. - - If the locale is None the object is translated to the system locale. - - :param args: the args to translate - :param desired_locale: the locale to translate the args to, if None the - default system locale will be used - :returns: a new args object with the translated contents of the original - """ - if isinstance(args, tuple): - return tuple(translate(v, desired_locale) for v in args) - if isinstance(args, dict): - translated_dict = {} - for (k, v) in six.iteritems(args): - translated_v = translate(v, desired_locale) - translated_dict[k] = translated_v - return translated_dict - return translate(args, desired_locale) - - -class TranslationHandler(handlers.MemoryHandler): - """Handler that translates records before logging them. - - The TranslationHandler takes a locale and a target logging.Handler object - to forward LogRecord objects to after translating them. This handler - depends on Message objects being logged, instead of regular strings. - - The handler can be configured declaratively in the logging.conf as follows: - - [handlers] - keys = translatedlog, translator - - [handler_translatedlog] - class = handlers.WatchedFileHandler - args = ('/var/log/api-localized.log',) - formatter = context - - [handler_translator] - class = openstack.common.log.TranslationHandler - target = translatedlog - args = ('zh_CN',) - - If the specified locale is not available in the system, the handler will - log in the default locale. - """ - - def __init__(self, locale=None, target=None): - """Initialize a TranslationHandler - - :param locale: locale to use for translating messages - :param target: logging.Handler object to forward - LogRecord objects to after translation - """ - # NOTE(luisg): In order to allow this handler to be a wrapper for - # other handlers, such as a FileHandler, and still be able to - # configure it using logging.conf, this handler has to extend - # MemoryHandler because only the MemoryHandlers' logging.conf - # parsing is implemented such that it accepts a target handler. - handlers.MemoryHandler.__init__(self, capacity=0, target=target) - self.locale = locale - - def setFormatter(self, fmt): - self.target.setFormatter(fmt) - - def emit(self, record): - # We save the message from the original record to restore it - # after translation, so other handlers are not affected by this - original_msg = record.msg - original_args = record.args - - try: - self._translate_and_log_record(record) - finally: - record.msg = original_msg - record.args = original_args - - def _translate_and_log_record(self, record): - record.msg = translate(record.msg, self.locale) - - # In addition to translating the message, we also need to translate - # arguments that were passed to the log method that were not part - # of the main message e.g., log.info(_('Some message %s'), this_one)) - record.args = _translate_args(record.args, self.locale) - - self.target.emit(record) diff --git a/neutronclient/openstack/common/importutils.py b/neutronclient/openstack/common/importutils.py deleted file mode 100644 index 0ab88e19d..000000000 --- a/neutronclient/openstack/common/importutils.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright 2011 OpenStack Foundation. -# 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 related utilities and helper functions. -""" - -import sys -import traceback - - -def import_class(import_str): - """Returns a class from a string including module and class.""" - mod_str, _sep, class_str = import_str.rpartition('.') - __import__(mod_str) - try: - return getattr(sys.modules[mod_str], class_str) - except AttributeError: - raise ImportError('Class %s cannot be found (%s)' % - (class_str, - traceback.format_exception(*sys.exc_info()))) - - -def import_object(import_str, *args, **kwargs): - """Import a class and return an instance of it.""" - return import_class(import_str)(*args, **kwargs) - - -def import_object_ns(name_space, import_str, *args, **kwargs): - """Tries to import object from default namespace. - - Imports a class and return an instance of it, first by trying - to find the class in a default namespace, then failing back to - a full path if not found in the default namespace. - """ - import_value = "%s.%s" % (name_space, import_str) - try: - return import_class(import_value)(*args, **kwargs) - except ImportError: - return import_class(import_str)(*args, **kwargs) - - -def import_module(import_str): - """Import a module.""" - __import__(import_str) - return sys.modules[import_str] - - -def import_versioned_module(version, submodule=None): - module = 'neutronclient.v%s' % version - if submodule: - module = '.'.join((module, submodule)) - return import_module(module) - - -def try_import(import_str, default=None): - """Try to import a module and if it fails return default.""" - try: - return import_module(import_str) - except ImportError: - return default diff --git a/neutronclient/openstack/common/jsonutils.py b/neutronclient/openstack/common/jsonutils.py deleted file mode 100644 index d53b1e99d..000000000 --- a/neutronclient/openstack/common/jsonutils.py +++ /dev/null @@ -1,186 +0,0 @@ -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# Copyright 2011 Justin Santa Barbara -# 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. - -''' -JSON related utilities. - -This module provides a few things: - - 1) A handy function for getting an object down to something that can be - JSON serialized. See to_primitive(). - - 2) Wrappers around loads() and dumps(). The dumps() wrapper will - automatically use to_primitive() for you if needed. - - 3) This sets up anyjson to use the loads() and dumps() wrappers if anyjson - is available. -''' - - -import codecs -import datetime -import functools -import inspect -import itertools -import sys - -if sys.version_info < (2, 7): - # On Python <= 2.6, json module is not C boosted, so try to use - # simplejson module if available - try: - import simplejson as json - except ImportError: - import json -else: - import json - -import six -import six.moves.xmlrpc_client as xmlrpclib - -from neutronclient.openstack.common import gettextutils -from neutronclient.openstack.common import importutils -from neutronclient.openstack.common import strutils -from neutronclient.openstack.common import timeutils - -netaddr = importutils.try_import("netaddr") - -_nasty_type_tests = [inspect.ismodule, inspect.isclass, inspect.ismethod, - inspect.isfunction, inspect.isgeneratorfunction, - inspect.isgenerator, inspect.istraceback, inspect.isframe, - inspect.iscode, inspect.isbuiltin, inspect.isroutine, - inspect.isabstract] - -_simple_types = (six.string_types + six.integer_types - + (type(None), bool, float)) - - -def to_primitive(value, convert_instances=False, convert_datetime=True, - level=0, max_depth=3): - """Convert a complex object into primitives. - - Handy for JSON serialization. We can optionally handle instances, - but since this is a recursive function, we could have cyclical - data structures. - - To handle cyclical data structures we could track the actual objects - visited in a set, but not all objects are hashable. Instead we just - track the depth of the object inspections and don't go too deep. - - Therefore, convert_instances=True is lossy ... be aware. - - """ - # handle obvious types first - order of basic types determined by running - # full tests on nova project, resulting in the following counts: - # 572754 - # 460353 - # 379632 - # 274610 - # 199918 - # 114200 - # 51817 - # 26164 - # 6491 - # 283 - # 19 - if isinstance(value, _simple_types): - return value - - if isinstance(value, datetime.datetime): - if convert_datetime: - return timeutils.strtime(value) - else: - return value - - # value of itertools.count doesn't get caught by nasty_type_tests - # and results in infinite loop when list(value) is called. - if type(value) == itertools.count: - return six.text_type(value) - - # FIXME(vish): Workaround for LP bug 852095. Without this workaround, - # tests that raise an exception in a mocked method that - # has a @wrap_exception with a notifier will fail. If - # we up the dependency to 0.5.4 (when it is released) we - # can remove this workaround. - if getattr(value, '__module__', None) == 'mox': - return 'mock' - - if level > max_depth: - return '?' - - # The try block may not be necessary after the class check above, - # but just in case ... - try: - recursive = functools.partial(to_primitive, - convert_instances=convert_instances, - convert_datetime=convert_datetime, - level=level, - max_depth=max_depth) - if isinstance(value, dict): - return dict((k, recursive(v)) for k, v in six.iteritems(value)) - elif isinstance(value, (list, tuple)): - return [recursive(lv) for lv in value] - - # It's not clear why xmlrpclib created their own DateTime type, but - # for our purposes, make it a datetime type which is explicitly - # handled - if isinstance(value, xmlrpclib.DateTime): - value = datetime.datetime(*tuple(value.timetuple())[:6]) - - if convert_datetime and isinstance(value, datetime.datetime): - return timeutils.strtime(value) - elif isinstance(value, gettextutils.Message): - return value.data - elif hasattr(value, 'iteritems'): - return recursive(dict(value.iteritems()), level=level + 1) - elif hasattr(value, '__iter__'): - return recursive(list(value)) - elif convert_instances and hasattr(value, '__dict__'): - # Likely an instance of something. Watch for cycles. - # Ignore class member vars. - return recursive(value.__dict__, level=level + 1) - elif netaddr and isinstance(value, netaddr.IPAddress): - return six.text_type(value) - else: - if any(test(value) for test in _nasty_type_tests): - return six.text_type(value) - return value - except TypeError: - # Class objects are tricky since they may define something like - # __iter__ defined but it isn't callable as list(). - return six.text_type(value) - - -def dumps(value, default=to_primitive, **kwargs): - return json.dumps(value, default=default, **kwargs) - - -def loads(s, encoding='utf-8', **kwargs): - return json.loads(strutils.safe_decode(s, encoding), **kwargs) - - -def load(fp, encoding='utf-8', **kwargs): - return json.load(codecs.getreader(encoding)(fp), **kwargs) - - -try: - import anyjson -except ImportError: - pass -else: - anyjson._modules.append((__name__, 'dumps', TypeError, - 'loads', ValueError, 'load')) - anyjson.force_implementation(__name__) diff --git a/neutronclient/openstack/common/strutils.py b/neutronclient/openstack/common/strutils.py deleted file mode 100644 index 730373665..000000000 --- a/neutronclient/openstack/common/strutils.py +++ /dev/null @@ -1,239 +0,0 @@ -# Copyright 2011 OpenStack Foundation. -# 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. - -""" -System-level utilities and helper functions. -""" - -import math -import re -import sys -import unicodedata - -import six - -from neutronclient.openstack.common.gettextutils import _ - - -UNIT_PREFIX_EXPONENT = { - 'k': 1, - 'K': 1, - 'Ki': 1, - 'M': 2, - 'Mi': 2, - 'G': 3, - 'Gi': 3, - 'T': 4, - 'Ti': 4, -} -UNIT_SYSTEM_INFO = { - 'IEC': (1024, re.compile(r'(^[-+]?\d*\.?\d+)([KMGT]i?)?(b|bit|B)$')), - 'SI': (1000, re.compile(r'(^[-+]?\d*\.?\d+)([kMGT])?(b|bit|B)$')), -} - -TRUE_STRINGS = ('1', 't', 'true', 'on', 'y', 'yes') -FALSE_STRINGS = ('0', 'f', 'false', 'off', 'n', 'no') - -SLUGIFY_STRIP_RE = re.compile(r"[^\w\s-]") -SLUGIFY_HYPHENATE_RE = re.compile(r"[-\s]+") - - -def int_from_bool_as_string(subject): - """Interpret a string as a boolean and return either 1 or 0. - - Any string value in: - - ('True', 'true', 'On', 'on', '1') - - is interpreted as a boolean True. - - Useful for JSON-decoded stuff and config file parsing - """ - return bool_from_string(subject) and 1 or 0 - - -def bool_from_string(subject, strict=False, default=False): - """Interpret a string as a boolean. - - A case-insensitive match is performed such that strings matching 't', - 'true', 'on', 'y', 'yes', or '1' are considered True and, when - `strict=False`, anything else returns the value specified by 'default'. - - Useful for JSON-decoded stuff and config file parsing. - - If `strict=True`, unrecognized values, including None, will raise a - ValueError which is useful when parsing values passed in from an API call. - Strings yielding False are 'f', 'false', 'off', 'n', 'no', or '0'. - """ - if not isinstance(subject, six.string_types): - subject = six.text_type(subject) - - lowered = subject.strip().lower() - - if lowered in TRUE_STRINGS: - return True - elif lowered in FALSE_STRINGS: - return False - elif strict: - acceptable = ', '.join( - "'%s'" % s for s in sorted(TRUE_STRINGS + FALSE_STRINGS)) - msg = _("Unrecognized value '%(val)s', acceptable values are:" - " %(acceptable)s") % {'val': subject, - 'acceptable': acceptable} - raise ValueError(msg) - else: - return default - - -def safe_decode(text, incoming=None, errors='strict'): - """Decodes incoming text/bytes string using `incoming` if they're not - already unicode. - - :param incoming: Text's current encoding - :param errors: Errors handling policy. See here for valid - values http://docs.python.org/2/library/codecs.html - :returns: text or a unicode `incoming` encoded - representation of it. - :raises TypeError: If text is not an instance of str - """ - if not isinstance(text, (six.string_types, six.binary_type)): - raise TypeError("%s can't be decoded" % type(text)) - - if isinstance(text, six.text_type): - return text - - if not incoming: - incoming = (sys.stdin.encoding or - sys.getdefaultencoding()) - - try: - return text.decode(incoming, errors) - except UnicodeDecodeError: - # Note(flaper87) If we get here, it means that - # sys.stdin.encoding / sys.getdefaultencoding - # didn't return a suitable encoding to decode - # text. This happens mostly when global LANG - # var is not set correctly and there's no - # default encoding. In this case, most likely - # python will use ASCII or ANSI encoders as - # default encodings but they won't be capable - # of decoding non-ASCII characters. - # - # Also, UTF-8 is being used since it's an ASCII - # extension. - return text.decode('utf-8', errors) - - -def safe_encode(text, incoming=None, - encoding='utf-8', errors='strict'): - """Encodes incoming text/bytes string using `encoding`. - - If incoming is not specified, text is expected to be encoded with - current python's default encoding. (`sys.getdefaultencoding`) - - :param incoming: Text's current encoding - :param encoding: Expected encoding for text (Default UTF-8) - :param errors: Errors handling policy. See here for valid - values http://docs.python.org/2/library/codecs.html - :returns: text or a bytestring `encoding` encoded - representation of it. - :raises TypeError: If text is not an instance of str - """ - if not isinstance(text, (six.string_types, six.binary_type)): - raise TypeError("%s can't be encoded" % type(text)) - - if not incoming: - incoming = (sys.stdin.encoding or - sys.getdefaultencoding()) - - if isinstance(text, six.text_type): - return text.encode(encoding, errors) - elif text and encoding != incoming: - # Decode text before encoding it with `encoding` - text = safe_decode(text, incoming, errors) - return text.encode(encoding, errors) - else: - return text - - -def string_to_bytes(text, unit_system='IEC', return_int=False): - """Converts a string into an float representation of bytes. - - The units supported for IEC :: - - Kb(it), Kib(it), Mb(it), Mib(it), Gb(it), Gib(it), Tb(it), Tib(it) - KB, KiB, MB, MiB, GB, GiB, TB, TiB - - The units supported for SI :: - - kb(it), Mb(it), Gb(it), Tb(it) - kB, MB, GB, TB - - Note that the SI unit system does not support capital letter 'K' - - :param text: String input for bytes size conversion. - :param unit_system: Unit system for byte size conversion. - :param return_int: If True, returns integer representation of text - in bytes. (default: decimal) - :returns: Numerical representation of text in bytes. - :raises ValueError: If text has an invalid value. - - """ - try: - base, reg_ex = UNIT_SYSTEM_INFO[unit_system] - except KeyError: - msg = _('Invalid unit system: "%s"') % unit_system - raise ValueError(msg) - match = reg_ex.match(text) - if match: - magnitude = float(match.group(1)) - unit_prefix = match.group(2) - if match.group(3) in ['b', 'bit']: - magnitude /= 8 - else: - msg = _('Invalid string format: %s') % text - raise ValueError(msg) - if not unit_prefix: - res = magnitude - else: - res = magnitude * pow(base, UNIT_PREFIX_EXPONENT[unit_prefix]) - if return_int: - return int(math.ceil(res)) - return res - - -def to_slug(value, incoming=None, errors="strict"): - """Normalize string. - - Convert to lowercase, remove non-word characters, and convert spaces - to hyphens. - - Inspired by Django's `slugify` filter. - - :param value: Text to slugify - :param incoming: Text's current encoding - :param errors: Errors handling policy. See here for valid - values http://docs.python.org/2/library/codecs.html - :returns: slugified unicode representation of `value` - :raises TypeError: If text is not an instance of str - """ - value = safe_decode(value, incoming, errors) - # NOTE(aababilov): no need to use safe_(encode|decode) here: - # encodings are always "ascii", error handling is always "ignore" - # and types are always known (first: unicode; second: str) - value = unicodedata.normalize("NFKD", value).encode( - "ascii", "ignore").decode("ascii") - value = SLUGIFY_STRIP_RE.sub("", value).strip().lower() - return SLUGIFY_HYPHENATE_RE.sub("-", value) diff --git a/neutronclient/openstack/common/timeutils.py b/neutronclient/openstack/common/timeutils.py deleted file mode 100644 index 386534ba3..000000000 --- a/neutronclient/openstack/common/timeutils.py +++ /dev/null @@ -1,210 +0,0 @@ -# Copyright 2011 OpenStack Foundation. -# 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. - -""" -Time related utilities and helper functions. -""" - -import calendar -import datetime -import time - -import iso8601 -import six - - -# ISO 8601 extended time format with microseconds -_ISO8601_TIME_FORMAT_SUBSECOND = '%Y-%m-%dT%H:%M:%S.%f' -_ISO8601_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S' -PERFECT_TIME_FORMAT = _ISO8601_TIME_FORMAT_SUBSECOND - - -def isotime(at=None, subsecond=False): - """Stringify time in ISO 8601 format.""" - if not at: - at = utcnow() - st = at.strftime(_ISO8601_TIME_FORMAT - if not subsecond - else _ISO8601_TIME_FORMAT_SUBSECOND) - tz = at.tzinfo.tzname(None) if at.tzinfo else 'UTC' - st += ('Z' if tz == 'UTC' else tz) - return st - - -def parse_isotime(timestr): - """Parse time from ISO 8601 format.""" - try: - return iso8601.parse_date(timestr) - except iso8601.ParseError as e: - raise ValueError(six.text_type(e)) - except TypeError as e: - raise ValueError(six.text_type(e)) - - -def strtime(at=None, fmt=PERFECT_TIME_FORMAT): - """Returns formatted utcnow.""" - if not at: - at = utcnow() - return at.strftime(fmt) - - -def parse_strtime(timestr, fmt=PERFECT_TIME_FORMAT): - """Turn a formatted time back into a datetime.""" - return datetime.datetime.strptime(timestr, fmt) - - -def normalize_time(timestamp): - """Normalize time in arbitrary timezone to UTC naive object.""" - offset = timestamp.utcoffset() - if offset is None: - return timestamp - return timestamp.replace(tzinfo=None) - offset - - -def is_older_than(before, seconds): - """Return True if before is older than seconds.""" - if isinstance(before, six.string_types): - before = parse_strtime(before).replace(tzinfo=None) - else: - before = before.replace(tzinfo=None) - - return utcnow() - before > datetime.timedelta(seconds=seconds) - - -def is_newer_than(after, seconds): - """Return True if after is newer than seconds.""" - if isinstance(after, six.string_types): - after = parse_strtime(after).replace(tzinfo=None) - else: - after = after.replace(tzinfo=None) - - return after - utcnow() > datetime.timedelta(seconds=seconds) - - -def utcnow_ts(): - """Timestamp version of our utcnow function.""" - if utcnow.override_time is None: - # NOTE(kgriffs): This is several times faster - # than going through calendar.timegm(...) - return int(time.time()) - - return calendar.timegm(utcnow().timetuple()) - - -def utcnow(): - """Overridable version of utils.utcnow.""" - if utcnow.override_time: - try: - return utcnow.override_time.pop(0) - except AttributeError: - return utcnow.override_time - return datetime.datetime.utcnow() - - -def iso8601_from_timestamp(timestamp): - """Returns an iso8601 formatted date from timestamp.""" - return isotime(datetime.datetime.utcfromtimestamp(timestamp)) - - -utcnow.override_time = None - - -def set_time_override(override_time=None): - """Overrides utils.utcnow. - - Make it return a constant time or a list thereof, one at a time. - - :param override_time: datetime instance or list thereof. If not - given, defaults to the current UTC time. - """ - utcnow.override_time = override_time or datetime.datetime.utcnow() - - -def advance_time_delta(timedelta): - """Advance overridden time using a datetime.timedelta.""" - assert(not utcnow.override_time is None) - try: - for dt in utcnow.override_time: - dt += timedelta - except TypeError: - utcnow.override_time += timedelta - - -def advance_time_seconds(seconds): - """Advance overridden time by seconds.""" - advance_time_delta(datetime.timedelta(0, seconds)) - - -def clear_time_override(): - """Remove the overridden time.""" - utcnow.override_time = None - - -def marshall_now(now=None): - """Make an rpc-safe datetime with microseconds. - - Note: tzinfo is stripped, but not required for relative times. - """ - if not now: - now = utcnow() - return dict(day=now.day, month=now.month, year=now.year, hour=now.hour, - minute=now.minute, second=now.second, - microsecond=now.microsecond) - - -def unmarshall_time(tyme): - """Unmarshall a datetime dict.""" - return datetime.datetime(day=tyme['day'], - month=tyme['month'], - year=tyme['year'], - hour=tyme['hour'], - minute=tyme['minute'], - second=tyme['second'], - microsecond=tyme['microsecond']) - - -def delta_seconds(before, after): - """Return the difference between two timing objects. - - Compute the difference in seconds between two date, time, or - datetime objects (as a float, to microsecond resolution). - """ - delta = after - before - return total_seconds(delta) - - -def total_seconds(delta): - """Return the total seconds of datetime.timedelta object. - - Compute total seconds of datetime.timedelta, datetime.timedelta - doesn't have method total_seconds in Python2.6, calculate it manually. - """ - try: - return delta.total_seconds() - except AttributeError: - return ((delta.days * 24 * 3600) + delta.seconds + - float(delta.microseconds) / (10 ** 6)) - - -def is_soon(dt, window): - """Determines if time is going to happen in the next window seconds. - - :param dt: the time - :param window: minimum seconds to remain to consider the time not soon - - :return: True if expiration is within the given duration - """ - soon = (utcnow() + datetime.timedelta(seconds=window)) - return normalize_time(dt) <= soon diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 7ce585b31..0329d5272 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -31,6 +31,7 @@ from keystoneclient import discover from keystoneclient.openstack.common.apiclient import exceptions as ks_exc from keystoneclient import session +from oslo.utils import encodeutils import six.moves.urllib.parse as urlparse from cliff import app @@ -39,6 +40,7 @@ from neutronclient.common import clientmanager from neutronclient.common import exceptions as exc from neutronclient.common import utils +from neutronclient.i18n import _ from neutronclient.neutron.v2_0 import agent from neutronclient.neutron.v2_0 import agentscheduler from neutronclient.neutron.v2_0 import credential @@ -69,8 +71,6 @@ from neutronclient.neutron.v2_0.vpn import ipsec_site_connection from neutronclient.neutron.v2_0.vpn import ipsecpolicy from neutronclient.neutron.v2_0.vpn import vpnservice -from neutronclient.openstack.common.gettextutils import _ -from neutronclient.openstack.common import strutils from neutronclient.version import __version__ @@ -938,7 +938,7 @@ def _get_keystone_session(self): def main(argv=sys.argv[1:]): try: return NeutronShell(NEUTRON_API_VERSION).run( - list(map(strutils.safe_decode, argv))) + list(map(encodeutils.safe_decode, argv))) except KeyboardInterrupt: print("... terminating neutron client", file=sys.stderr) return 130 diff --git a/neutronclient/tests/unit/test_auth.py b/neutronclient/tests/unit/test_auth.py index 34e5e93a3..a1113e13a 100644 --- a/neutronclient/tests/unit/test_auth.py +++ b/neutronclient/tests/unit/test_auth.py @@ -19,6 +19,7 @@ import fixtures from mox3 import mox +from oslo.serialization import jsonutils import requests import requests_mock import six @@ -34,7 +35,6 @@ from neutronclient import client from neutronclient.common import exceptions from neutronclient.common import utils -from neutronclient.openstack.common import jsonutils USERNAME = 'testuser' diff --git a/neutronclient/tests/unit/test_cli20_agents.py b/neutronclient/tests/unit/test_cli20_agents.py index 2b0226602..de16d610e 100644 --- a/neutronclient/tests/unit/test_cli20_agents.py +++ b/neutronclient/tests/unit/test_cli20_agents.py @@ -16,8 +16,9 @@ import sys +from oslo.serialization import jsonutils + from neutronclient.neutron.v2_0 import agent -from neutronclient.openstack.common import jsonutils from neutronclient.tests.unit import test_cli20 diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index c5891a293..a7f5aa817 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -17,10 +17,10 @@ import sys from mox3 import mox +from oslo.serialization import jsonutils from neutronclient.common import exceptions from neutronclient.neutron.v2_0 import network -from neutronclient.openstack.common import jsonutils from neutronclient import shell from neutronclient.tests.unit import test_cli20 diff --git a/openstack-common.conf b/openstack-common.conf deleted file mode 100644 index 902a82e40..000000000 --- a/openstack-common.conf +++ /dev/null @@ -1,7 +0,0 @@ -[DEFAULT] - -# The list of modules to copy from openstack-common -modules=gettextutils,jsonutils,strutils,timeutils - -# The base module to hold the copy of openstack.common -base=neutronclient diff --git a/requirements.txt b/requirements.txt index aeeb180ca..ebd9f76a0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,6 +6,9 @@ argparse cliff>=1.7.0 # Apache-2.0 iso8601>=0.1.9 netaddr>=0.7.12 +oslo.i18n>=1.0.0 # Apache-2.0 +oslo.serialization>=1.0.0 # Apache-2.0 +oslo.utils>=1.0.0 # Apache-2.0 requests>=2.2.0,!=2.4.0 python-keystoneclient>=0.11.1 simplejson>=2.2.0 From 1ac542c5a34ec7cf386ab1b528448ff726a93277 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Wed, 22 Oct 2014 19:17:13 +0000 Subject: [PATCH 114/845] Updated from global requirements Change-Id: I65acb8237b4b8a5229842fcb1e007524b36d915c --- test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index 643677510..26f376011 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -9,7 +9,7 @@ discover fixtures>=0.3.14 mox3>=0.7.0 oslosphinx>=2.2.0 # Apache-2.0 -oslotest>=1.1.0 # Apache-2.0 +oslotest>=1.2.0 # Apache-2.0 python-subunit>=0.0.18 requests-mock>=0.4.0 # Apache-2.0 sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3 From 81fe0c776cf1a3b7c8b3a4d9b3224743439b295e Mon Sep 17 00:00:00 2001 From: Robert Li Date: Wed, 24 Sep 2014 10:13:44 -0400 Subject: [PATCH 115/845] Don't allow update of ipv6-ra-mode and ipv6-address-mode These two parameters are not updatable from the nuetron server. And therefore for subnet-update command, they should not be displayed in the help message. Change-Id: I3a6abc3a4e3030d73715d55c340a77b44d41f140 Closes-Bug: #1373417 --- neutronclient/neutron/v2_0/subnet.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/neutronclient/neutron/v2_0/subnet.py b/neutronclient/neutron/v2_0/subnet.py index 0ba633f98..fc46bf11e 100644 --- a/neutronclient/neutron/v2_0/subnet.py +++ b/neutronclient/neutron/v2_0/subnet.py @@ -84,17 +84,9 @@ def add_updatable_arguments(parser): '--enable-dhcp', action='store_true', help=_('Enable DHCP for this subnet.')) - parser.add_argument( - '--ipv6-ra-mode', - choices=['dhcpv6-stateful', 'dhcpv6-stateless', 'slaac'], - help=_('IPv6 RA (Router Advertisement) mode.')) - parser.add_argument( - '--ipv6-address-mode', - choices=['dhcpv6-stateful', 'dhcpv6-stateless', 'slaac'], - help=_('IPv6 address mode.')) -def updatable_args2body(parsed_args, body): +def updatable_args2body(parsed_args, body, for_create=True): if parsed_args.gateway and parsed_args.no_gateway: raise exceptions.CommandError(_("--gateway option and " "--no-gateway option can " @@ -119,12 +111,12 @@ def updatable_args2body(parsed_args, body): body['subnet']['host_routes'] = parsed_args.host_routes if parsed_args.dns_nameservers: body['subnet']['dns_nameservers'] = parsed_args.dns_nameservers - if parsed_args.ipv6_ra_mode: + if for_create and parsed_args.ipv6_ra_mode: if parsed_args.ip_version == 4: raise exceptions.CommandError(_("--ipv6-ra-mode is invalid " "when --ip-version is 4")) body['subnet']['ipv6_ra_mode'] = parsed_args.ipv6_ra_mode - if parsed_args.ipv6_address_mode: + if for_create and parsed_args.ipv6_address_mode: if parsed_args.ip_version == 4: raise exceptions.CommandError(_("--ipv6-address-mode is " "invalid when --ip-version " @@ -173,6 +165,14 @@ def add_known_arguments(self, parser): parser.add_argument( 'cidr', metavar='CIDR', help=_('CIDR of subnet to create.')) + parser.add_argument( + '--ipv6-ra-mode', + choices=['dhcpv6-stateful', 'dhcpv6-stateless', 'slaac'], + help=_('IPv6 RA (Router Advertisement) mode.')) + parser.add_argument( + '--ipv6-address-mode', + choices=['dhcpv6-stateful', 'dhcpv6-stateless', 'slaac'], + help=_('IPv6 address mode.')) def args2body(self, parsed_args): if parsed_args.ip_version == 4 and parsed_args.cidr.endswith('/32'): @@ -209,5 +209,5 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): body = {'subnet': {}} - updatable_args2body(parsed_args, body) + updatable_args2body(parsed_args, body, for_create=False) return body From 9ed73c04aaaedcd8757d1f3ff99669276f069c72 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Tue, 28 Oct 2014 03:39:28 +0000 Subject: [PATCH 116/845] Updated from global requirements Change-Id: I111b974f1c5f51c341805c35f585c4be2348f845 --- test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index 26f376011..deacef12f 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -11,7 +11,7 @@ mox3>=0.7.0 oslosphinx>=2.2.0 # Apache-2.0 oslotest>=1.2.0 # Apache-2.0 python-subunit>=0.0.18 -requests-mock>=0.4.0 # Apache-2.0 +requests-mock>=0.5.1 # Apache-2.0 sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3 testrepository>=0.0.18 testtools>=0.9.34 From 5d2bafa56fae2d273579c5d73f4686ee9d404a26 Mon Sep 17 00:00:00 2001 From: Jyotsna Date: Fri, 13 Jun 2014 18:11:17 +0530 Subject: [PATCH 117/845] neutron port-list -f csv outputs poorly formatted JSON strings After the change, the attached subnets are displayed as a list of dictionaries in one line only for csv option. It does not modify the output for tabular format. Closes-Bug: #1263551 Change-Id: Ia34062562f0471bc926f9fc0bbed2d5dc4352dc0 --- neutronclient/neutron/v2_0/__init__.py | 8 ++++++++ neutronclient/neutron/v2_0/port.py | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index 538b48c59..2cda5d6be 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -663,6 +663,14 @@ def setup_columns(self, info, parsed_args): return (_columns, (utils.get_item_properties( s, _columns, formatters=self._formatters, ) for s in info), ) + if parsed_args.formatter == 'csv': + return (_columns, (utils.get_item_properties( + s, _columns, formatters=self._formatters_csv) + for s in info),) + else: + return (_columns, (utils.get_item_properties( + s, _columns, formatters=self._formatters) + for s in info),) def get_data(self, parsed_args): self.log.debug('get_data(%s)', parsed_args) diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index 4d2d7fa30..bee12e105 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -31,11 +31,19 @@ def _format_fixed_ips(port): return '' +def _format_fixed_ips_csv(port): + try: + return [utils.dumps(ip) for ip in port['fixed_ips']] + except Exception: + return '' + + class ListPort(neutronV20.ListCommand): """List ports that belong to a given tenant.""" resource = 'port' _formatters = {'fixed_ips': _format_fixed_ips, } + _formatters_csv = {'fixed_ips': _format_fixed_ips_csv, } list_columns = ['id', 'name', 'mac_address', 'fixed_ips'] pagination_support = True sorting_support = True From 12a87f2f0949a1289f5db5f78dead6c672c35830 Mon Sep 17 00:00:00 2001 From: Ciprian Cosma Date: Thu, 6 Nov 2014 23:29:35 +0200 Subject: [PATCH 118/845] Fixes neutronclient lb-member-show command The current lb-member-show command uses the id or name for querying. Since the lb members do not have names, the query returns false positives. The unit test for id or name was removed. Closes-Bug: #1380937 Change-Id: I804cb9d11f5208ea2d7baa58c9d878db93851517 --- neutronclient/neutron/v2_0/lb/member.py | 1 + neutronclient/tests/unit/lb/test_cli20_member.py | 8 -------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/neutronclient/neutron/v2_0/lb/member.py b/neutronclient/neutron/v2_0/lb/member.py index 7fd6f1ea0..aeed319f1 100644 --- a/neutronclient/neutron/v2_0/lb/member.py +++ b/neutronclient/neutron/v2_0/lb/member.py @@ -35,6 +35,7 @@ class ShowMember(neutronV20.ShowCommand): """Show information of a given member.""" resource = 'member' + allow_names = False class CreateMember(neutronV20.CreateCommand): diff --git a/neutronclient/tests/unit/lb/test_cli20_member.py b/neutronclient/tests/unit/lb/test_cli20_member.py index b3f4cc87b..d793d7e69 100644 --- a/neutronclient/tests/unit/lb/test_cli20_member.py +++ b/neutronclient/tests/unit/lb/test_cli20_member.py @@ -103,14 +103,6 @@ def test_show_member_id(self): args = ['--fields', 'id', self.test_id] self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - def test_show_member_id_name(self): - """lb-member-show.""" - resource = 'member' - cmd = member.ShowMember(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name']) - def test_update_member(self): """lb-member-update myid --name myname --tags a b.""" resource = 'member' From 02c108fd0010798927f144b1dfee9f4ec1947f41 Mon Sep 17 00:00:00 2001 From: Hideki Saito Date: Sat, 8 Nov 2014 21:13:45 +0900 Subject: [PATCH 119/845] Fix mixed usage of _ * all modules are using function of _ from oslo.i18n * remove the definition of _ from openstack.common.__init__.py for safety Change-Id: Ice2f8be1190fd13e8991a8c7fa55ca636341e875 Closes-Bug: #1382476 --- neutronclient/common/__init__.py | 26 -------------------------- neutronclient/common/exceptions.py | 2 +- neutronclient/common/utils.py | 2 +- neutronclient/v2_0/client.py | 2 +- 4 files changed, 3 insertions(+), 29 deletions(-) diff --git a/neutronclient/common/__init__.py b/neutronclient/common/__init__.py index fb715cf41..e69de29bb 100644 --- a/neutronclient/common/__init__.py +++ b/neutronclient/common/__init__.py @@ -1,26 +0,0 @@ -# Copyright 2011 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 gettext - -t = gettext.translation('neutronclient', fallback=True) -try: - ugettext = t.ugettext # Python 2 -except AttributeError: - ugettext = t.gettext # Python 3 - - -def _(msg): - return ugettext(msg) diff --git a/neutronclient/common/exceptions.py b/neutronclient/common/exceptions.py index 4c7c9d838..9728fbdd7 100644 --- a/neutronclient/common/exceptions.py +++ b/neutronclient/common/exceptions.py @@ -13,7 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. -from neutronclient.common import _ +from neutronclient.i18n import _ """ Neutron base exception handling. diff --git a/neutronclient/common/utils.py b/neutronclient/common/utils.py index 28137b7fa..d82e34bd8 100644 --- a/neutronclient/common/utils.py +++ b/neutronclient/common/utils.py @@ -24,8 +24,8 @@ from oslo.utils import importutils import six -from neutronclient.common import _ from neutronclient.common import exceptions +from neutronclient.i18n import _ def env(*vars, **kwargs): diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index d2bb2eda0..44035b6c6 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -21,11 +21,11 @@ import six.moves.urllib.parse as urlparse from neutronclient import client -from neutronclient.common import _ from neutronclient.common import constants from neutronclient.common import exceptions from neutronclient.common import serializer from neutronclient.common import utils +from neutronclient.i18n import _ _logger = logging.getLogger(__name__) From 3ed2a5ec493d4a64ce8ed7abec79c6c8152dbc59 Mon Sep 17 00:00:00 2001 From: Eugene Nikanorov Date: Mon, 17 Nov 2014 21:48:03 +0400 Subject: [PATCH 120/845] Disable name support for lb-healthmonitor-* commands LBaaS healthmonitor object doesn't have name attribute so name support should be removed from corresponding commands Change-Id: I8b45ad5dfdbf6de46ff0a428aaf4cfda8b9f2f95 Closes-Bug: #1387506 --- neutronclient/neutron/v2_0/lb/healthmonitor.py | 2 ++ neutronclient/tests/unit/lb/test_cli20_healthmonitor.py | 9 --------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/neutronclient/neutron/v2_0/lb/healthmonitor.py b/neutronclient/neutron/v2_0/lb/healthmonitor.py index 83c2cf57f..0956fd465 100644 --- a/neutronclient/neutron/v2_0/lb/healthmonitor.py +++ b/neutronclient/neutron/v2_0/lb/healthmonitor.py @@ -35,6 +35,7 @@ class ShowHealthMonitor(neutronV20.ShowCommand): """Show information of a given health monitor.""" resource = 'health_monitor' + allow_names = False class CreateHealthMonitor(neutronV20.CreateCommand): @@ -111,6 +112,7 @@ class DeleteHealthMonitor(neutronV20.DeleteCommand): """Delete a given health monitor.""" resource = 'health_monitor' + allow_names = False class AssociateHealthMonitor(neutronV20.NeutronCommand): diff --git a/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py b/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py index 09c5234c6..2415a176c 100644 --- a/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py +++ b/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py @@ -125,15 +125,6 @@ def test_show_healthmonitor_id(self): args = ['--fields', 'id', self.test_id] self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - def test_show_healthmonitor_id_name(self): - """lb-healthmonitor-show.""" - resource = 'health_monitor' - cmd = healthmonitor.ShowHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name']) - def test_update_health_monitor(self): """lb-healthmonitor-update myid --name myname --tags a b.""" resource = 'health_monitor' From 8903ccec9abe8d4aeab90264547c1586d7bc7fa3 Mon Sep 17 00:00:00 2001 From: Danny Choi Date: Wed, 26 Nov 2014 22:12:30 +0000 Subject: [PATCH 121/845] Change Creates to Create in help text In the help text for cisco-credential-create and cisco-network-profile-create, change the word Creates to Create to be consistent with others. Change-Id: I7b22816738e8b7a7e629ad78108b2e588282fbc0 Closes-bug: #1395352 --- neutronclient/neutron/v2_0/credential.py | 4 ++-- neutronclient/neutron/v2_0/networkprofile.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/neutronclient/neutron/v2_0/credential.py b/neutronclient/neutron/v2_0/credential.py index ffec5ee1d..c397627b7 100644 --- a/neutronclient/neutron/v2_0/credential.py +++ b/neutronclient/neutron/v2_0/credential.py @@ -32,7 +32,7 @@ class ShowCredential(neutronV20.ShowCommand): class CreateCredential(neutronV20.CreateCommand): - """Creates a credential.""" + """Create a credential.""" resource = 'credential' @@ -67,7 +67,7 @@ def args2body(self, parsed_args): class DeleteCredential(neutronV20.DeleteCommand): - """Delete a given credential.""" + """Delete a given credential.""" resource = 'credential' allow_names = False diff --git a/neutronclient/neutron/v2_0/networkprofile.py b/neutronclient/neutron/v2_0/networkprofile.py index 03fc4972a..3450b3219 100644 --- a/neutronclient/neutron/v2_0/networkprofile.py +++ b/neutronclient/neutron/v2_0/networkprofile.py @@ -42,7 +42,7 @@ class ShowNetworkProfile(neutronV20.ShowCommand): class CreateNetworkProfile(neutronV20.CreateCommand): - """Creates a network profile.""" + """Create a network profile.""" resource = RESOURCE From a65f385c5c3dd676533fabbe86dbd79d3f9dfb37 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Thu, 27 Nov 2014 21:48:31 +0000 Subject: [PATCH 122/845] Updated from global requirements Change-Id: I3daa9bd38c7d5e26960171f6913213d98d468a36 --- test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index deacef12f..436948525 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -14,4 +14,4 @@ python-subunit>=0.0.18 requests-mock>=0.5.1 # Apache-2.0 sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3 testrepository>=0.0.18 -testtools>=0.9.34 +testtools>=0.9.36,!=1.2.0 From 8a7771844daf9015644e07acbac05664d5260b64 Mon Sep 17 00:00:00 2001 From: Yalei Wang Date: Thu, 30 Oct 2014 08:55:42 +0800 Subject: [PATCH 123/845] fix the firewall rule arg split error the previous firewall rules parsing is for type 'string', but we add the capability to deal with type 'unicode' to match the real type from cmdline. Change-Id: Id673a0c5967131d9d88fe0c78f19613a6fc1c721 Closes-Bug: 1385908 --- .../neutron/v2_0/fw/firewallpolicy.py | 2 +- .../unit/fw/test_cli20_firewallpolicy.py | 32 ++++++++++--------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/neutronclient/neutron/v2_0/fw/firewallpolicy.py b/neutronclient/neutron/v2_0/fw/firewallpolicy.py index 5d1a4b7f7..f2f147337 100644 --- a/neutronclient/neutron/v2_0/fw/firewallpolicy.py +++ b/neutronclient/neutron/v2_0/fw/firewallpolicy.py @@ -70,7 +70,7 @@ def add_known_arguments(self, parser): help=_('Create a shared policy.'), default=argparse.SUPPRESS) parser.add_argument( - '--firewall-rules', type=str.split, + '--firewall-rules', type=lambda x: x.split(), help=_('Ordered list of whitespace-delimited firewall rule ' 'names or IDs; e.g., --firewall-rules \"rule1 rule2\"')) parser.add_argument( diff --git a/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py b/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py index 8912c5e22..272a98cda 100644 --- a/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py +++ b/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py @@ -47,31 +47,33 @@ def test_create_firewall_policy_with_mandatory_params(self): admin_state_up=True, tenant_id=tenant_id) def test_create_firewall_policy_with_all_params(self): - """firewall-policy-create with all params set.""" + """firewall-policy-create with rule param of misc format.""" resource = 'firewall_policy' cmd = firewallpolicy.CreateFirewallPolicy(test_cli20.MyApp(sys.stdout), None) name = 'my-name' description = 'my-desc' - firewall_rules_arg = 'rule_id1 rule_id2' firewall_rules_res = ['rule_id1', 'rule_id2'] tenant_id = 'my-tenant' my_id = 'myid' - args = ['--description', description, - '--shared', - '--firewall-rules', firewall_rules_arg, - '--audited', - '--tenant-id', tenant_id, - '--admin-state_up', - name] position_names = ['name', ] position_values = [name, ] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - description=description, shared=True, - firewall_rules=firewall_rules_res, - audited=True, admin_state_up=True, - tenant_id=tenant_id) + + #check for both str and unicode format firewall_rules_arg + for firewall_rules_arg in ['rule_id1 rule_id2', u'rule_id1 rule_id2']: + args = ['--description', description, + '--shared', + '--firewall-rules', firewall_rules_arg, + '--audited', + '--tenant-id', tenant_id, + '--admin-state_up', + name] + self._test_create_resource(resource, cmd, name, my_id, args, + position_names, position_values, + description=description, shared=True, + firewall_rules=firewall_rules_res, + audited=True, admin_state_up=True, + tenant_id=tenant_id) def test_list_firewall_policies(self): """firewall-policy-list.""" From 497bb55f66ec0621a7fc81664b8a19ce4cde766a Mon Sep 17 00:00:00 2001 From: Jamie Lennox Date: Thu, 30 Oct 2014 16:54:47 +0100 Subject: [PATCH 124/845] Cleanup copy and pasted token Correctly use fixtures instead. Change-Id: If726b1365cb7659a9db011bdcb37252563f84ef4 --- neutronclient/tests/unit/test_auth.py | 79 +++++++++++++-------------- 1 file changed, 37 insertions(+), 42 deletions(-) diff --git a/neutronclient/tests/unit/test_auth.py b/neutronclient/tests/unit/test_auth.py index a1113e13a..90919b24d 100644 --- a/neutronclient/tests/unit/test_auth.py +++ b/neutronclient/tests/unit/test_auth.py @@ -28,8 +28,7 @@ from keystoneclient.auth.identity import v2 as ks_v2_auth from keystoneclient.auth.identity import v3 as ks_v3_auth from keystoneclient import exceptions as ks_exceptions -from keystoneclient.fixture import v2 as ks_v2_fixture -from keystoneclient.fixture import v3 as ks_v3_fixture +from keystoneclient import fixture from keystoneclient import session from neutronclient import client @@ -47,28 +46,14 @@ ADMIN_ENDPOINT_URL = 'admin_%s' % ENDPOINT_URL INTERNAL_ENDPOINT_URL = 'internal_%s' % ENDPOINT_URL ENDPOINT_OVERRIDE = 'otherurl' -TOKEN = 'tokentoken' TOKENID = uuid.uuid4().hex REGION = 'RegionOne' NOAUTH = 'noauth' -KS_TOKEN_RESULT = { - 'access': { - 'token': {'id': TOKEN, - 'expires': '2012-08-11T07:49:01Z', - 'tenant': {'id': str(uuid.uuid1())}}, - 'user': {'id': str(uuid.uuid1())}, - 'serviceCatalog': [ - {'endpoints_links': [], - 'endpoints': [{'adminURL': ENDPOINT_URL, - 'internalURL': ENDPOINT_URL, - 'publicURL': ENDPOINT_URL, - 'region': REGION}], - 'type': 'network', - 'name': 'Neutron Service'} - ] - } -} +KS_TOKEN_RESULT = fixture.V2Token() +KS_TOKEN_RESULT.set_scope() +_s = KS_TOKEN_RESULT.add_service('network', 'Neutron Service') +_s.add_endpoint(ENDPOINT_URL, region=REGION) ENDPOINTS_RESULT = { 'endpoints': [{ @@ -139,7 +124,7 @@ def get_response(status_code, headers=None): def setup_keystone_v2(mrequests): - v2_token = ks_v2_fixture.Token(token_id=TOKENID) + v2_token = fixture.V2Token(token_id=TOKENID) service = v2_token.add_service('network') service.add_endpoint(PUBLIC_ENDPOINT_URL, region=REGION) @@ -157,7 +142,7 @@ def setup_keystone_v3(mrequests): V3_URL, text=V3_VERSION_ENTRY) - v3_token = ks_v3_fixture.Token() + v3_token = fixture.V3Token() service = v3_token.add_service('network') service.add_standard_endpoints(public=PUBLIC_ENDPOINT_URL, admin=ADMIN_ENDPOINT_URL, @@ -235,13 +220,14 @@ def test_reused_token_get_auth_info(self): """Test that Client.get_auth_info() works even if client was instantiated with predefined token. """ + token_id = uuid.uuid4().hex client_ = client.HTTPClient(username=USERNAME, tenant_name=TENANT_NAME, - token=TOKEN, + token=token_id, password=PASSWORD, auth_url=AUTH_URL, region_name=REGION) - expected = {'auth_token': TOKEN, + expected = {'auth_token': token_id, 'auth_tenant_id': None, 'auth_user_id': None, 'endpoint_url': self.client.endpoint_url} @@ -275,7 +261,8 @@ def test_get_token(self, mrequests): def test_refresh_token(self): self.mox.StubOutWithMock(self.client, "request") - self.client.auth_token = TOKEN + token_id = uuid.uuid4().hex + self.client.auth_token = token_id self.client.endpoint_url = ENDPOINT_URL res200 = get_response(200) @@ -284,7 +271,7 @@ def test_refresh_token(self): # If a token is expired, neutron server retruns 401 self.client.request( mox.StrContains(ENDPOINT_URL + '/resource'), 'GET', - headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN) + headers=mox.ContainsKeyValue('X-Auth-Token', token_id) ).AndReturn((res401, '')) self.client.request( AUTH_URL + '/tokens', 'POST', @@ -292,7 +279,8 @@ def test_refresh_token(self): ).AndReturn((res200, json.dumps(KS_TOKEN_RESULT))) self.client.request( mox.StrContains(ENDPOINT_URL + '/resource'), 'GET', - headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN) + headers=mox.ContainsKeyValue('X-Auth-Token', + KS_TOKEN_RESULT.token_id) ).AndReturn((res200, '')) self.mox.ReplayAll() self.client.do_request('/resource', 'GET') @@ -301,7 +289,8 @@ def test_refresh_token_no_auth_url(self): self.mox.StubOutWithMock(self.client, "request") self.client.auth_url = None - self.client.auth_token = TOKEN + token_id = uuid.uuid4().hex + self.client.auth_token = token_id self.client.endpoint_url = ENDPOINT_URL res401 = get_response(401) @@ -309,7 +298,7 @@ def test_refresh_token_no_auth_url(self): # If a token is expired, neutron server returns 401 self.client.request( mox.StrContains(ENDPOINT_URL + '/resource'), 'GET', - headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN) + headers=mox.ContainsKeyValue('X-Auth-Token', token_id) ).AndReturn((res401, '')) self.mox.ReplayAll() self.assertRaises(exceptions.NoAuthURLProvided, @@ -326,17 +315,18 @@ def test_get_endpoint_url_with_invalid_auth_url(self): def test_get_endpoint_url(self): self.mox.StubOutWithMock(self.client, "request") - self.client.auth_token = TOKEN + token_id = uuid.uuid4().hex + self.client.auth_token = token_id res200 = get_response(200) self.client.request( - mox.StrContains(AUTH_URL + '/tokens/%s/endpoints' % TOKEN), 'GET', - headers=mox.IsA(dict) + mox.StrContains(AUTH_URL + '/tokens/%s/endpoints' % token_id), + 'GET', headers=mox.IsA(dict) ).AndReturn((res200, json.dumps(ENDPOINTS_RESULT))) self.client.request( mox.StrContains(ENDPOINT_URL + '/resource'), 'GET', - headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN) + headers=mox.ContainsKeyValue('X-Auth-Token', token_id) ).AndReturn((res200, '')) self.mox.ReplayAll() self.client.do_request('/resource', 'GET') @@ -350,12 +340,14 @@ def test_use_given_endpoint_url(self): self.mox.StubOutWithMock(self.client, "request") - self.client.auth_token = TOKEN + token_id = uuid.uuid4().hex + + self.client.auth_token = token_id res200 = get_response(200) self.client.request( mox.StrContains(ENDPOINT_OVERRIDE + '/resource'), 'GET', - headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN) + headers=mox.ContainsKeyValue('X-Auth-Token', token_id) ).AndReturn((res200, '')) self.mox.ReplayAll() self.client.do_request('/resource', 'GET') @@ -367,12 +359,13 @@ def test_get_endpoint_url_other(self): auth_url=AUTH_URL, region_name=REGION, endpoint_type='otherURL') self.mox.StubOutWithMock(self.client, "request") - self.client.auth_token = TOKEN + token_id = uuid.uuid4().hex + self.client.auth_token = token_id res200 = get_response(200) self.client.request( - mox.StrContains(AUTH_URL + '/tokens/%s/endpoints' % TOKEN), 'GET', - headers=mox.IsA(dict) + mox.StrContains(AUTH_URL + '/tokens/%s/endpoints' % token_id), + 'GET', headers=mox.IsA(dict) ).AndReturn((res200, json.dumps(ENDPOINTS_RESULT))) self.mox.ReplayAll() self.assertRaises(exceptions.EndpointTypeNotFound, @@ -383,14 +376,15 @@ def test_get_endpoint_url_other(self): def test_get_endpoint_url_failed(self): self.mox.StubOutWithMock(self.client, "request") - self.client.auth_token = TOKEN + token_id = uuid.uuid4().hex + self.client.auth_token = token_id res200 = get_response(200) res401 = get_response(401) self.client.request( - mox.StrContains(AUTH_URL + '/tokens/%s/endpoints' % TOKEN), 'GET', - headers=mox.IsA(dict) + mox.StrContains(AUTH_URL + '/tokens/%s/endpoints' % token_id), + 'GET', headers=mox.IsA(dict) ).AndReturn((res401, '')) self.client.request( AUTH_URL + '/tokens', 'POST', @@ -398,7 +392,8 @@ def test_get_endpoint_url_failed(self): ).AndReturn((res200, json.dumps(KS_TOKEN_RESULT))) self.client.request( mox.StrContains(ENDPOINT_URL + '/resource'), 'GET', - headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN) + headers=mox.ContainsKeyValue('X-Auth-Token', + KS_TOKEN_RESULT.token_id) ).AndReturn((res200, '')) self.mox.ReplayAll() self.client.do_request('/resource', 'GET') From c5d8557fbee5f035e22ebb4bf4869f0503af6315 Mon Sep 17 00:00:00 2001 From: Jamie Lennox Date: Thu, 30 Oct 2014 17:07:09 +0100 Subject: [PATCH 125/845] Use discovery fixture Use the provided fixtures rather than copying code out of keystoneclient. Change-Id: I550edc355f008d94d6c0b834494d693137c29111 --- neutronclient/tests/unit/test_auth.py | 52 ++++---------------------- neutronclient/tests/unit/test_shell.py | 8 ++-- neutronclient/tests/unit/test_ssl.py | 4 +- 3 files changed, 14 insertions(+), 50 deletions(-) diff --git a/neutronclient/tests/unit/test_auth.py b/neutronclient/tests/unit/test_auth.py index 90919b24d..bd8a4928c 100644 --- a/neutronclient/tests/unit/test_auth.py +++ b/neutronclient/tests/unit/test_auth.py @@ -66,54 +66,18 @@ }] } -BASE_HOST = 'http://keystone.example.com' -BASE_URL = "%s:5000/" % BASE_HOST -UPDATED = '2013-03-06T00:00:00Z' +BASE_URL = "http://keystone.example.com:5000/" -# FIXME (bklei): A future release of keystoneclient will support -# a discovery fixture which can replace these constants and clean -# this up. V2_URL = "%sv2.0" % BASE_URL -V2_DESCRIBED_BY_HTML = {'href': 'http://docs.openstack.org/api/' - 'openstack-identity-service/2.0/content/', - 'rel': 'describedby', - 'type': 'text/html'} - -V2_DESCRIBED_BY_PDF = {'href': 'http://docs.openstack.org/api/openstack-ident' - 'ity-service/2.0/identity-dev-guide-2.0.pdf', - 'rel': 'describedby', - 'type': 'application/pdf'} - -V2_VERSION = {'id': 'v2.0', - 'links': [{'href': V2_URL, 'rel': 'self'}, - V2_DESCRIBED_BY_HTML, V2_DESCRIBED_BY_PDF], - 'status': 'stable', - 'updated': UPDATED} - V3_URL = "%sv3" % BASE_URL -V3_MEDIA_TYPES = [{'base': 'application/json', - 'type': 'application/vnd.openstack.identity-v3+json'}, - {'base': 'application/xml', - 'type': 'application/vnd.openstack.identity-v3+xml'}] - -V3_VERSION = {'id': 'v3.0', - 'links': [{'href': V3_URL, 'rel': 'self'}], - 'media-types': V3_MEDIA_TYPES, - 'status': 'stable', - 'updated': UPDATED} - - -def _create_version_entry(version): - return jsonutils.dumps({'version': version}) - -def _create_version_list(versions): - return jsonutils.dumps({'versions': {'values': versions}}) +_v2 = fixture.V2Discovery(V2_URL) +_v3 = fixture.V3Discovery(V3_URL) +V3_VERSION_LIST = jsonutils.dumps({'versions': {'values': [_v2, _v3]}}) -V3_VERSION_LIST = _create_version_list([V3_VERSION, V2_VERSION]) -V3_VERSION_ENTRY = _create_version_entry(V3_VERSION) -V2_VERSION_ENTRY = _create_version_entry(V2_VERSION) +V2_VERSION_ENTRY = {'version': _v2} +V3_VERSION_ENTRY = {'version': _v3} def get_response(status_code, headers=None): @@ -130,7 +94,7 @@ def setup_keystone_v2(mrequests): mrequests.register_uri('POST', '%s/tokens' % (V2_URL), - text=json.dumps(v2_token)) + json=v2_token) auth_session = session.Session() auth_plugin = ks_v2_auth.Password(V2_URL, 'xx', 'xx') @@ -140,7 +104,7 @@ def setup_keystone_v2(mrequests): def setup_keystone_v3(mrequests): mrequests.register_uri('GET', V3_URL, - text=V3_VERSION_ENTRY) + json=V3_VERSION_ENTRY) v3_token = fixture.V3Token() service = v3_token.add_service('network') diff --git a/neutronclient/tests/unit/test_shell.py b/neutronclient/tests/unit/test_shell.py index c743016e9..08ff840e2 100644 --- a/neutronclient/tests/unit/test_shell.py +++ b/neutronclient/tests/unit/test_shell.py @@ -139,7 +139,7 @@ def test_auth(self, mrequests): # emulate Keystone version discovery mrequests.register_uri('GET', auth.V3_URL, - text=auth.V3_VERSION_ENTRY) + json=auth.V3_VERSION_ENTRY) neutron_shell = openstack_shell.NeutronShell('2.0') self.addCleanup(self.mox.UnsetStubs) @@ -174,7 +174,7 @@ def test_auth_cert_and_key(self, mrequests): # emulate Keystone version discovery mrequests.register_uri('GET', auth.V3_URL, - text=auth.V3_VERSION_ENTRY) + json=auth.V3_VERSION_ENTRY) neutron_shell = openstack_shell.NeutronShell('2.0') self.addCleanup(self.mox.UnsetStubs) @@ -210,7 +210,7 @@ def test_v2_auth(self, mrequests): # emulate Keystone version discovery mrequests.register_uri('GET', auth.V2_URL, - text=auth.V2_VERSION_ENTRY) + json=auth.V2_VERSION_ENTRY) neutron_shell = openstack_shell.NeutronShell('2.0') self.addCleanup(self.mox.UnsetStubs) @@ -387,7 +387,7 @@ def test_insecure_auth(self, mrequests): # emulate Keystone version discovery mrequests.register_uri('GET', auth.V2_URL, - text=auth.V2_VERSION_ENTRY) + json=auth.V2_VERSION_ENTRY) neutron_shell = openstack_shell.NeutronShell('2.0') self.addCleanup(self.mox.UnsetStubs) diff --git a/neutronclient/tests/unit/test_ssl.py b/neutronclient/tests/unit/test_ssl.py index 545f1b944..e28f7c0c5 100644 --- a/neutronclient/tests/unit/test_ssl.py +++ b/neutronclient/tests/unit/test_ssl.py @@ -48,7 +48,7 @@ def test_ca_cert_passed(self, mrequests): # emulate Keystone version discovery mrequests.register_uri('GET', auth.V3_URL, - text=auth.V3_VERSION_ENTRY) + json=auth.V3_VERSION_ENTRY) self.mox.StubOutWithMock(ClientManager, '__init__') self.mox.StubOutWithMock(openstack_shell.NeutronShell, 'interact') @@ -93,7 +93,7 @@ def test_ca_cert_passed_as_env_var(self, mrequests): # emulate Keystone version discovery mrequests.register_uri('GET', auth.V3_URL, - text=auth.V3_VERSION_ENTRY) + json=auth.V3_VERSION_ENTRY) self.useFixture(fixtures.EnvironmentVariable('OS_CACERT', CA_CERT)) From 56892bb08abce93b352a773e001f5b950c4a7c85 Mon Sep 17 00:00:00 2001 From: Elena Ezhova Date: Thu, 20 Nov 2014 14:15:53 +0300 Subject: [PATCH 126/845] Make help for agent-update more verbose Amended the description of the command to reflect what it really does. Added two optional aguments: * admin-state-down - sets admin state up of the agent to false * description - provides the description for the agent Added a unit test. Change-Id: Ie9f30f30102bef6735dc9ed538eca9a1183c2780 Closes-Bug: #1254241 --- neutronclient/neutron/v2_0/agent.py | 21 ++++++++++++++++++- neutronclient/tests/unit/test_cli20_agents.py | 10 +++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/neutronclient/neutron/v2_0/agent.py b/neutronclient/neutron/v2_0/agent.py index e9e2688b3..8f7de7a7e 100644 --- a/neutronclient/neutron/v2_0/agent.py +++ b/neutronclient/neutron/v2_0/agent.py @@ -14,6 +14,7 @@ # under the License. # +from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 @@ -55,7 +56,25 @@ class DeleteAgent(neutronV20.DeleteCommand): class UpdateAgent(neutronV20.UpdateCommand): - """Update a given agent.""" + """Updates the admin status and description for a specified agent.""" resource = 'agent' allow_names = False + + def add_known_arguments(self, parser): + parser.add_argument( + '--admin-state-down', + dest='admin_state', + action='store_false', + help=_('Set admin state up of the agent to false.')) + parser.add_argument( + '--description', + help=_('Description for the agent.')) + + def args2body(self, parsed_args): + body = { + self.resource: { + 'admin_state_up': parsed_args.admin_state, }, } + neutronV20.update_dict(parsed_args, body[self.resource], + ['description']) + return body diff --git a/neutronclient/tests/unit/test_cli20_agents.py b/neutronclient/tests/unit/test_cli20_agents.py index 2c2176723..7aa0cda3b 100644 --- a/neutronclient/tests/unit/test_cli20_agents.py +++ b/neutronclient/tests/unit/test_cli20_agents.py @@ -53,3 +53,13 @@ def test_list_agents_field(self): self.assertEqual(1, len(ag)) self.assertIn("alive", ag.keys()) self.assertIn(smile, ag.values()) + + def test_update_agent(self): + """agent-update myid --admin-state-down --description mydescr.""" + resource = 'agent' + cmd = agent.UpdateAgent(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource( + resource, cmd, 'myid', + ['myid', '--admin-state-down', '--description', 'mydescr'], + {'description': 'mydescr', 'admin_state_up': False} + ) From 89271b191e1a092d999e3eb9147e40894d8c9183 Mon Sep 17 00:00:00 2001 From: Elena Ezhova Date: Tue, 2 Dec 2014 12:56:12 +0300 Subject: [PATCH 127/845] Add unit tests for agent related commands Added unit tests to cover: * agent show; * agent delete. Change-Id: I98879cfe1bb1e7c085404aa73786a1eeaae1f40e Closes-Bug: #1206110 --- neutronclient/tests/unit/test_cli20_agents.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/neutronclient/tests/unit/test_cli20_agents.py b/neutronclient/tests/unit/test_cli20_agents.py index 7aa0cda3b..f74427467 100644 --- a/neutronclient/tests/unit/test_cli20_agents.py +++ b/neutronclient/tests/unit/test_cli20_agents.py @@ -63,3 +63,19 @@ def test_update_agent(self): ['myid', '--admin-state-down', '--description', 'mydescr'], {'description': 'mydescr', 'admin_state_up': False} ) + + def test_show_agent(self): + """Show agent: --field id --field binary myid.""" + resource = 'agent' + cmd = agent.ShowAgent(test_cli20.MyApp(sys.stdout), None) + args = ['--field', 'id', '--field', 'binary', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, + args, ['id', 'binary']) + + def test_delete_agent(self): + """Delete agent: myid.""" + resource = 'agent' + cmd = agent.DeleteAgent(test_cli20.MyApp(sys.stdout), None) + myid = 'myid' + args = [myid] + self._test_delete_resource(resource, cmd, myid, args) From 62063c12b222f0cdff0383d908ab60a2ed970334 Mon Sep 17 00:00:00 2001 From: Ilya Shakhat Date: Tue, 26 Aug 2014 15:36:04 +0400 Subject: [PATCH 128/845] Fix issues with Unicode compatibility for Py3 HTTP arguments should not be encoded because: * requests library does it automatically replacing unicode characters with utf-8 sequences * on py3 encoding function replaces string with byte array, thus breaking keystone client Closes bug 1398854 Change-Id: I747778259656f1c9803bcf990cdd85f87a77fd1a --- neutronclient/client.py | 3 --- neutronclient/tests/unit/test_cli20.py | 22 +++++++++++----------- neutronclient/tests/unit/test_shell.py | 2 +- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/neutronclient/client.py b/neutronclient/client.py index 1ff612706..f3aaae231 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -112,8 +112,6 @@ def _cs_request(self, *args, **kwargs): if 'body' in kwargs: kargs['body'] = kwargs['body'] - args = utils.safe_encode_list(args) - kargs = utils.safe_encode_dict(kargs) if self.log_credentials: log_kargs = kargs @@ -310,7 +308,6 @@ def _request(self, url, method, body=None, headers=None, **kwargs): endpoint_filter.setdefault('service_type', self.service_type) endpoint_filter.setdefault('region_name', self.region_name) - kwargs = utils.safe_encode_dict(kwargs) resp = self.session.request(url, method, data=body, headers=headers, **kwargs) return resp, resp.text diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index e6d69ea47..912eb190b 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -17,10 +17,10 @@ import contextlib import itertools import sys -import urllib import fixtures from mox3 import mox +from oslo.utils import encodeutils from oslotest import base import requests import six @@ -28,6 +28,7 @@ from neutronclient.common import constants from neutronclient.common import exceptions +from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronV2_0 from neutronclient import shell from neutronclient.v2_0 import client @@ -333,12 +334,12 @@ def _test_list_resources(self, resources, cmd, detail=False, tags=(), args.append("--tag") for tag in tags: args.append(tag) - if isinstance(tag, unicode): - tag = urllib.quote(tag.encode('utf-8')) + tag_query = urlparse.urlencode( + {'tag': encodeutils.safe_encode(tag)}) if query: - query += "&tag=" + tag + query += "&" + tag_query else: - query = "tag=" + tag + query = tag_query if (not tags) and fields_2: args.append('--') if fields_2: @@ -567,17 +568,16 @@ def test_do_request_unicode(self): unicode_text = u'\u7f51\u7edc' # url with unicode action = u'/test' - expected_action = action.encode('utf-8') + expected_action = action # query string with unicode params = {'test': unicode_text} - expect_query = urllib.urlencode({'test': - unicode_text.encode('utf-8')}) + expect_query = urlparse.urlencode(utils.safe_encode_dict(params)) # request body with unicode body = params expect_body = self.client.serialize(body) - # headers with unicode - self.client.httpclient.auth_token = unicode_text - expected_auth_token = unicode_text.encode('utf-8') + self.client.httpclient.auth_token = encodeutils.safe_encode( + unicode_text) + expected_auth_token = encodeutils.safe_encode(unicode_text) self.client.httpclient.request( end_url(expected_action, query=expect_query, format=self.format), diff --git a/neutronclient/tests/unit/test_shell.py b/neutronclient/tests/unit/test_shell.py index c743016e9..f9e2cd411 100644 --- a/neutronclient/tests/unit/test_shell.py +++ b/neutronclient/tests/unit/test_shell.py @@ -427,7 +427,7 @@ def test_main_with_unicode(self): self.mox.StubOutClassWithMocks(openstack_shell, 'NeutronShell') qshell_mock = openstack_shell.NeutronShell('2.0') unicode_text = u'\u7f51\u7edc' - argv = ['net-list', unicode_text, unicode_text.encode('utf-8')] + argv = ['net-list', unicode_text, unicode_text] qshell_mock.run([u'net-list', unicode_text, unicode_text]).AndReturn(0) self.mox.ReplayAll() From 66612c9de835bd080a9f43d91aa65e5e8639fd26 Mon Sep 17 00:00:00 2001 From: Swaminathan Vasudevan Date: Fri, 12 Sep 2014 13:16:29 -0700 Subject: [PATCH 129/845] Router create distributed accepts lower case Neutron router-create optional argument distributed does not accept "true/false" anymore. It only accepts value with Camel case "True/False". This patch will allow the users to provide both True/true or False/false. Change-Id: I79577bd70a796543b9dea53b4839512eb8944d7c Closes-bug: #1368934 --- neutronclient/neutron/v2_0/router.py | 4 ++-- neutronclient/tests/unit/test_cli20_router.py | 17 +++++++++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index 6973218cb..0a6cc2fa8 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -68,8 +68,8 @@ def add_known_arguments(self, parser): help=_('Name of router to create.')) parser.add_argument( '--distributed', - dest='distributed', - choices=['True', 'False'], + dest='distributed', metavar='{True,False}', + choices=['True', 'true', 'False', 'false'], default=argparse.SUPPRESS, help=_('Create a distributed router.')) parser.add_argument( diff --git a/neutronclient/tests/unit/test_cli20_router.py b/neutronclient/tests/unit/test_cli20_router.py index 6320750bf..33e22d550 100644 --- a/neutronclient/tests/unit/test_cli20_router.py +++ b/neutronclient/tests/unit/test_cli20_router.py @@ -83,12 +83,25 @@ def _create_router_distributed_or_ha(self, distributed=None, ha=None): position_names, position_values, **expected) - def test_create_router_distributed(self): - self._create_router_distributed_or_ha(distributed=True) + def test_create_router_distributed_True(self): + """Create router: --distributed=True.""" + self._create_router_distributed_or_ha(distributed='True') def test_create_router_ha(self): self._create_router_distributed_or_ha(ha=True) + def test_create_router_distributed_False(self): + """Create router: --distributed=False.""" + self._create_router_distributed_or_ha(distributed='False') + + def test_create_router_distributed_true(self): + """Create router: --distributed=true.""" + self._create_router_distributed_or_ha(distributed='true') + + def test_create_router_distributed_false(self): + """Create router: --distributed=false.""" + self._create_router_distributed_or_ha(distributed='false') + def test_list_routers_detail(self): """list routers: -D.""" resources = "routers" From 2f23623e3d26bdad73a2aefe88208f2714bc7bf8 Mon Sep 17 00:00:00 2001 From: Jeremy Stanley Date: Fri, 5 Dec 2014 03:30:40 +0000 Subject: [PATCH 130/845] Workflow documentation is now in infra-manual Replace URLs for workflow documentation to appropriate parts of the OpenStack Project Infrastructure Manual. Change-Id: I48d86e8474ee83f538b21790eb4de46a5b785547 --- CONTRIBUTING.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 727569717..b726f1d6b 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -1,13 +1,13 @@ If you would like to contribute to the development of OpenStack, you must follow the steps documented at: - https://wiki.openstack.org/wiki/How_To_Contribute#If_you.27re_a_developer + http://docs.openstack.org/infra/manual/developers.html#development-workflow Once those steps have been completed, changes to OpenStack should be submitted for review via the Gerrit tool, following the workflow documented at: - https://wiki.openstack.org/GerritWorkflow + http://docs.openstack.org/infra/manual/developers.html#development-workflow Pull requests submitted through GitHub will be ignored. From 187c36c19b0e43740df3c46c6f34d3d0ad76a510 Mon Sep 17 00:00:00 2001 From: liu-sheng Date: Wed, 16 Jul 2014 11:06:50 +0800 Subject: [PATCH 131/845] Correct the bash completion of CLI Currently, the "neutron help" command in CLI doesn't expose the "bash-completion" command to users, but actually, the "neutron bash-completion" command is available. Additionally, there is a "complete" command in outputs of "neutron help", but this command will prints a wrong result. This patch adds the "bash-completion" command to commands list of neutron CLI and removes the "complete" command. Change-Id: I0f2ec22da7ba36f79197acb41d0621fc726cc0f3 Closes-Bug: #1340647 --- neutronclient/shell.py | 11 ++++++++++- neutronclient/tests/unit/test_shell.py | 23 +++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 0329d5272..ad73b5066 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -38,6 +38,7 @@ from cliff import commandmanager from neutronclient.common import clientmanager +from neutronclient.common import command as openstack_command from neutronclient.common import exceptions as exc from neutronclient.common import utils from neutronclient.i18n import _ @@ -116,7 +117,12 @@ def check_non_negative_int(value): return value +class BashCompletionCommand(openstack_command.OpenStackCommand): + """Prints all of the commands and options for bash-completion.""" + resource = "bash_completion" + COMMAND_V2 = { + 'bash-completion': BashCompletionCommand, 'net-list': network.ListNetwork, 'net-external-list': network.ListExternalNetwork, 'net-show': network.ShowNetwork, @@ -345,6 +351,9 @@ def __init__(self, apiversion): for k, v in self.commands[apiversion].items(): self.command_manager.add_command(k, v) + # Pop the 'complete' to correct the outputs of 'neutron help'. + self.command_manager.commands.pop('complete') + # This is instantiated in initialize_app() only when using # password flow auth self.auth_client = None @@ -644,7 +653,7 @@ def run(self, argv): help_pos = -1 help_command_pos = -1 for arg in argv: - if arg == 'bash-completion': + if arg == 'bash-completion' and help_command_pos == -1: self._bash_completion() return 0 if arg in self.commands[self.api_version]: diff --git a/neutronclient/tests/unit/test_shell.py b/neutronclient/tests/unit/test_shell.py index c743016e9..f6af099d5 100644 --- a/neutronclient/tests/unit/test_shell.py +++ b/neutronclient/tests/unit/test_shell.py @@ -127,6 +127,29 @@ def test_help_command(self): matchers.MatchesRegex(required)) self.assertFalse(stderr) + def test_bash_completion_in_outputs_of_help_command(self): + help_text, stderr = self.shell('help') + self.assertFalse(stderr) + completion_cmd = "bash-completion" + completion_help_str = ("Prints all of the commands and options " + "for bash-completion.") + self.assertIn(completion_cmd, help_text) + self.assertIn(completion_help_str, help_text) + + def test_bash_completion_command(self): + # just check we have some output + required = [ + '.*--tenant_id', + '.*--client-certificate', + '.*help', + '.*gateway-device-create', + '.*--dns-nameserver'] + help_text, stderr = self.shell('neutron bash-completion') + self.assertFalse(stderr) + for r in required: + self.assertThat(help_text, + matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE)) + def test_unknown_auth_strategy(self): self.useFixture(fixtures.FakeLogger(level=logging.DEBUG)) stdout, stderr = self.shell('--os-auth-strategy fake quota-list') From 0560f85616f9a635762b0245b8f4b196d3d75527 Mon Sep 17 00:00:00 2001 From: liu-sheng Date: Wed, 10 Dec 2014 10:18:45 +0800 Subject: [PATCH 132/845] Fix columns setup base on csv formatter In setup_columns() method of ListCommand class, if parsed_args.formatter is "csv", the method will don't handle this case for returning before the if-else statement. Change-Id: Ic689228c722aa12a7671857797c606d0960b3d7e --- neutronclient/neutron/v2_0/__init__.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index 2cda5d6be..9c9667c5b 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -660,9 +660,6 @@ def setup_columns(self, info, parsed_args): # both list_columns and returned resource. # Also Keep their order the same as in list_columns _columns = [x for x in self.list_columns if x in _columns] - return (_columns, (utils.get_item_properties( - s, _columns, formatters=self._formatters, ) - for s in info), ) if parsed_args.formatter == 'csv': return (_columns, (utils.get_item_properties( s, _columns, formatters=self._formatters_csv) From fea8706c22438af5666bc249f86aa5facf97564d Mon Sep 17 00:00:00 2001 From: Ihar Hrachyshka Date: Thu, 11 Dec 2014 12:42:39 +0100 Subject: [PATCH 133/845] subnet: allow --enable-dhcp=False/True syntax, again Till neutronclient 2.3.4, we allowed the following syntax to enable or disable DHCP: --enable-dhcp=False or --enable-dhcp=True. Since I52d05ec2284b6910a33df4a5cb7dc3888737acb6 was merged, it's not the case anymore. Client libraries should be backwards compatible. The change that introduced the regression cannot be reverted. First, because it's correct in essence and fixed a valid bug in neutron client CLI, and second (and more importantly), we don't want to break backwards compatibility for users that currently use 2.3.5+ version that already included the new style of the argument. So we need to support both new and old styles for setting the attribute. It's sad, but argparse module that we use to parse CLI arguments does not allow to distinguish between '--enable-dhcp=smth' and '--enable-dhcp smth' variants of the argument. This limitation means that an argument with nargs=argparse.OPTIONAL that would be useful to cover both styles with single entry would consume the next argument from the command line if it's present and does not seem to be another option argument defined for the parser. Meaning, it would break some cases, f.e. when --enable-dhcp is passed right before subnet name. In that specific case, the argument would consume the name as the value of the argument and break the client. That's why for this specific fix, we're forced to use --enable-dhcp=False and --enable-dhcp=True as argument *names*. It's ugly as hell, so let's hide that mess from users by suppressing help entries for new arguments. PS: And Oh Lord the Mighty, thank you for only two possible values for boolean attributes!) Closes-Bug: #1401555 Change-Id: Ib374d1cc499e4f1e5242f1b438bc65512c109e40 --- neutronclient/neutron/v2_0/subnet.py | 18 ++++- neutronclient/tests/unit/test_cli20_subnet.py | 68 ++++++++++++++++--- 2 files changed, 74 insertions(+), 12 deletions(-) diff --git a/neutronclient/neutron/v2_0/subnet.py b/neutronclient/neutron/v2_0/subnet.py index 60b899ced..86c9f182d 100644 --- a/neutronclient/neutron/v2_0/subnet.py +++ b/neutronclient/neutron/v2_0/subnet.py @@ -85,6 +85,20 @@ def add_updatable_arguments(parser): '--enable-dhcp', action='store_true', help=_('Enable DHCP for this subnet.')) + # NOTE(ihrachys): yes, that's awful, but should be left as-is for + # backwards compatibility for versions <=2.3.4 that passed the + # boolean values through to the server without any argument + # validation. + parser.add_argument( + '--enable-dhcp=True', + action='store_true', + dest='enable_dhcp', + help=argparse.SUPPRESS) + parser.add_argument( + '--enable-dhcp=False', + action='store_true', + dest='disable_dhcp', + help=argparse.SUPPRESS) def updatable_args2body(parsed_args, body, for_create=True): @@ -93,8 +107,8 @@ def updatable_args2body(parsed_args, body, for_create=True): "--no-gateway option can " "not be used same time")) if parsed_args.disable_dhcp and parsed_args.enable_dhcp: - raise exceptions.CommandError(_("--enable-dhcp and --disable-dhcp can " - "not be used in the same command.")) + raise exceptions.CommandError(_( + "You cannot enable and disable DHCP at the same time.")) if parsed_args.no_gateway: body['subnet'].update({'gateway_ip': None}) diff --git a/neutronclient/tests/unit/test_cli20_subnet.py b/neutronclient/tests/unit/test_cli20_subnet.py index fb34003b7..a98af01f1 100644 --- a/neutronclient/tests/unit/test_cli20_subnet.py +++ b/neutronclient/tests/unit/test_cli20_subnet.py @@ -75,23 +75,71 @@ def test_create_subnet_with_bad_gateway_option(self): return self.fail('No exception for bad gateway option') + def _test_create_resource_and_catch_command_error(self, tested_args, + should_fail, + *args): + _j = lambda args: ' '.join(args) + try: + self._test_create_resource(*args) + except exceptions.CommandError: + if not should_fail: + self.fail( + 'Unexpected exception raised for %s options' % + _j(tested_args)) + self.mox.UnsetStubs() + else: + if should_fail: + self.fail( + 'No exception for %s options' % _j(tested_args)) + def test_create_subnet_with_enable_and_disable_dhcp(self): - """Create sbunet: --enable-dhcp and --disable-dhcp.""" + """Create subnet: --enable-dhcp and --disable-dhcp.""" resource = 'subnet' cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) name = 'myname' myid = 'myid' netid = 'netid' cidr = 'cidrvalue' - args = ['--enable-dhcp', '--disable-dhcp', netid, cidr] - position_names = ['ip_version', 'network_id', 'cidr', 'gateway_ip'] - position_values = [4, netid, cidr, None] - try: - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - except exceptions.CommandError: - return - self.fail('No exception for --enable-dhcp --disable-dhcp option') + position_names = ['ip_version', 'network_id', 'cidr', 'enable_dhcp'] + # enable_dhcp value is appended later inside the loop + position_values = [4, netid, cidr] + for enable_dhcp_arg, should_fail in ( + ('--enable-dhcp=False', False), + ('--enable-dhcp=True', True), + ('--enable-dhcp', True) + ): + tested_args = [enable_dhcp_arg, '--disable-dhcp'] + args = tested_args + [netid, cidr] + pos_values = position_values + [should_fail] + self._test_create_resource_and_catch_command_error( + tested_args, should_fail, + resource, cmd, name, myid, args, position_names, pos_values) + + def test_create_subnet_with_multiple_enable_dhcp(self): + """Create subnet with multiple --enable-dhcp arguments passed.""" + resource = 'subnet' + cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + netid = 'netid' + cidr = 'cidrvalue' + position_names = ['ip_version', 'network_id', 'cidr', 'enable_dhcp'] + # enable_dhcp value is appended later inside the loop + position_values = [4, netid, cidr] + + _ = 'UNUSED_MARKER' + for tested_args, should_fail, pos_value in ( + (['--enable-dhcp', '--enable-dhcp=True'], False, True), + (['--enable-dhcp', '--enable-dhcp=False'], True, _), + (['--enable-dhcp=False', '--enable-dhcp'], True, _), + (['--enable-dhcp=True', '--enable-dhcp=False'], True, _), + (['--enable-dhcp=False', '--enable-dhcp=True'], True, _) + ): + args = tested_args + [netid, cidr] + pos_values = position_values + [pos_value] + self._test_create_resource_and_catch_command_error( + tested_args, should_fail, + resource, cmd, name, myid, args, position_names, pos_values) def test_create_subnet_tenant(self): """Create subnet: --tenant_id tenantid netid cidr.""" From 04a0ec8ddfa5c0aed08b638a63cc3a5da7bf148c Mon Sep 17 00:00:00 2001 From: Koteswara Rao Kelam Date: Wed, 2 Jul 2014 02:56:13 -0700 Subject: [PATCH 134/845] firewall policy update for a rule is not working If user specifies one rule to update it is not taking but more than one rule it is accepting as a list and updating the policy. Modified policy-update to behave same as policy-create command for "--firewall-rules" option Change-Id: Ia6a1c726be310d60b3b56ce677f843e818079fbe Closes-bug: 1318617 --- .../neutron/v2_0/fw/firewallpolicy.py | 51 +++++++++++-------- .../unit/fw/test_cli20_firewallpolicy.py | 12 +++++ 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/neutronclient/neutron/v2_0/fw/firewallpolicy.py b/neutronclient/neutron/v2_0/fw/firewallpolicy.py index f2f147337..32f25eba9 100644 --- a/neutronclient/neutron/v2_0/fw/firewallpolicy.py +++ b/neutronclient/neutron/v2_0/fw/firewallpolicy.py @@ -33,6 +33,29 @@ def _format_firewall_rules(firewall_policy): return '' +def common_add_known_arguments(parser): + parser.add_argument( + '--firewall-rules', type=lambda x: x.split(), + help=_('Ordered list of whitespace-delimited firewall rule ' + 'names or IDs; e.g., --firewall-rules \"rule1 rule2\"')) + + +def common_args2body(client, parsed_args): + if parsed_args.firewall_rules: + _firewall_rules = [] + for f in parsed_args.firewall_rules: + _firewall_rules.append( + neutronv20.find_resourceid_by_name_or_id( + client, 'firewall_rule', f)) + body = {'firewall_policy': {'firewall_rules': _firewall_rules}} + else: + body = {'firewall_policy': {}} + neutronv20.update_dict(parsed_args, body['firewall_policy'], + ['name', 'description', 'shared', + 'audited', 'tenant_id']) + return body + + class ListFirewallPolicy(neutronv20.ListCommand): """List firewall policies that belong to a given tenant.""" @@ -69,10 +92,7 @@ def add_known_arguments(self, parser): action='store_true', help=_('Create a shared policy.'), default=argparse.SUPPRESS) - parser.add_argument( - '--firewall-rules', type=lambda x: x.split(), - help=_('Ordered list of whitespace-delimited firewall rule ' - 'names or IDs; e.g., --firewall-rules \"rule1 rule2\"')) + common_add_known_arguments(parser) parser.add_argument( '--audited', action='store_true', @@ -80,22 +100,7 @@ def add_known_arguments(self, parser): default=argparse.SUPPRESS) def args2body(self, parsed_args): - if parsed_args.firewall_rules: - _firewall_rules = [] - for f in parsed_args.firewall_rules: - _firewall_rules.append( - neutronv20.find_resourceid_by_name_or_id( - self.get_client(), 'firewall_rule', f)) - body = {self.resource: { - 'firewall_rules': _firewall_rules, - }, - } - else: - body = {self.resource: {}} - neutronv20.update_dict(parsed_args, body[self.resource], - ['name', 'description', 'shared', - 'audited', 'tenant_id']) - return body + return common_args2body(self.get_client(), parsed_args) class UpdateFirewallPolicy(neutronv20.UpdateCommand): @@ -103,6 +108,12 @@ class UpdateFirewallPolicy(neutronv20.UpdateCommand): resource = 'firewall_policy' + def add_known_arguments(self, parser): + common_add_known_arguments(parser) + + def args2body(self, parsed_args): + return common_args2body(self.get_client(), parsed_args) + class DeleteFirewallPolicy(neutronv20.DeleteCommand): """Delete a given firewall policy.""" diff --git a/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py b/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py index 272a98cda..a8786dbbc 100644 --- a/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py +++ b/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py @@ -133,6 +133,18 @@ def test_update_firewall_policy(self): ['myid', '--name', 'newname'], {'name': 'newname', }) + def test_update_firewall_policy_with_rules(self): + """firewall-policy-update myid --firewall-rules "rule1 rule2".""" + resource = 'firewall_policy' + cmd = firewallpolicy.UpdateFirewallPolicy(test_cli20.MyApp(sys.stdout), + None) + firewall_rules_arg = u'rule_id3 rule_id4' + firewall_rules_res = ['rule_id3', 'rule_id4'] + self._test_update_resource( + resource, cmd, 'myid', + ['myid', '--firewall-rules', firewall_rules_arg], + {'firewall_rules': firewall_rules_res, }) + def test_delete_firewall_policy(self): """firewall-policy-delete my-id.""" resource = 'firewall_policy' From 4b181cd64c1c14b0eb180665086f9806a09d126b Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Thu, 18 Dec 2014 01:28:04 +0000 Subject: [PATCH 135/845] Updated from global requirements Change-Id: I1c29fbdbd0413b2c58dbf3acf979759aa45123ae --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index ebd9f76a0..0b0c1fece 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ iso8601>=0.1.9 netaddr>=0.7.12 oslo.i18n>=1.0.0 # Apache-2.0 oslo.serialization>=1.0.0 # Apache-2.0 -oslo.utils>=1.0.0 # Apache-2.0 +oslo.utils>=1.1.0 # Apache-2.0 requests>=2.2.0,!=2.4.0 python-keystoneclient>=0.11.1 simplejson>=2.2.0 From 5822d619e56ece42b711fdc378de772ebda65f38 Mon Sep 17 00:00:00 2001 From: Jamie Lennox Date: Thu, 30 Oct 2014 17:17:17 +0100 Subject: [PATCH 136/845] Use requests_mock instead of mox Kill off mox and use requests_mock. There are fixtures available for everything we want to test - there is no reason to resort to using mox. This cleans up and makes clearer a lot of tests. Change-Id: I2633bcaf36388fb4db1de4cd75d5ccd76f961509 --- neutronclient/tests/unit/test_auth.py | 282 +++++++++----------------- neutronclient/tests/unit/test_http.py | 67 +++--- 2 files changed, 121 insertions(+), 228 deletions(-) diff --git a/neutronclient/tests/unit/test_auth.py b/neutronclient/tests/unit/test_auth.py index bd8a4928c..7cb8c56b4 100644 --- a/neutronclient/tests/unit/test_auth.py +++ b/neutronclient/tests/unit/test_auth.py @@ -15,25 +15,22 @@ # import json +import logging import uuid import fixtures -from mox3 import mox from oslo.serialization import jsonutils -import requests -import requests_mock -import six +from requests_mock.contrib import fixture as mock_fixture import testtools from keystoneclient.auth.identity import v2 as ks_v2_auth from keystoneclient.auth.identity import v3 as ks_v3_auth from keystoneclient import exceptions as ks_exceptions -from keystoneclient import fixture +from keystoneclient import fixture as ks_fixture from keystoneclient import session from neutronclient import client from neutronclient.common import exceptions -from neutronclient.common import utils USERNAME = 'testuser' @@ -41,16 +38,16 @@ TENANT_NAME = 'testtenant' TENANT_ID = 'testtenant_id' PASSWORD = 'password' -ENDPOINT_URL = 'localurl' -PUBLIC_ENDPOINT_URL = 'public_%s' % ENDPOINT_URL -ADMIN_ENDPOINT_URL = 'admin_%s' % ENDPOINT_URL -INTERNAL_ENDPOINT_URL = 'internal_%s' % ENDPOINT_URL -ENDPOINT_OVERRIDE = 'otherurl' +ENDPOINT_URL = 'http://localurl' +PUBLIC_ENDPOINT_URL = '%s/public' % ENDPOINT_URL +ADMIN_ENDPOINT_URL = '%s/admin' % ENDPOINT_URL +INTERNAL_ENDPOINT_URL = '%s/internal' % ENDPOINT_URL +ENDPOINT_OVERRIDE = 'http://otherurl' TOKENID = uuid.uuid4().hex REGION = 'RegionOne' NOAUTH = 'noauth' -KS_TOKEN_RESULT = fixture.V2Token() +KS_TOKEN_RESULT = ks_fixture.V2Token() KS_TOKEN_RESULT.set_scope() _s = KS_TOKEN_RESULT.add_service('network', 'Neutron Service') _s.add_endpoint(ENDPOINT_URL, region=REGION) @@ -71,8 +68,8 @@ V2_URL = "%sv2.0" % BASE_URL V3_URL = "%sv3" % BASE_URL -_v2 = fixture.V2Discovery(V2_URL) -_v3 = fixture.V3Discovery(V3_URL) +_v2 = ks_fixture.V2Discovery(V2_URL) +_v3 = ks_fixture.V3Discovery(V3_URL) V3_VERSION_LIST = jsonutils.dumps({'versions': {'values': [_v2, _v3]}}) @@ -80,15 +77,8 @@ V3_VERSION_ENTRY = {'version': _v3} -def get_response(status_code, headers=None): - response = mox.Mox().CreateMock(requests.Response) - response.headers = headers or {} - response.status_code = status_code - return response - - def setup_keystone_v2(mrequests): - v2_token = fixture.V2Token(token_id=TOKENID) + v2_token = ks_fixture.V2Token(token_id=TOKENID) service = v2_token.add_service('network') service.add_endpoint(PUBLIC_ENDPOINT_URL, region=REGION) @@ -106,7 +96,7 @@ def setup_keystone_v3(mrequests): V3_URL, json=V3_VERSION_ENTRY) - v3_token = fixture.V3Token() + v3_token = ks_fixture.V3Token() service = v3_token.add_service('network') service.add_standard_endpoints(public=PUBLIC_ENDPOINT_URL, admin=ADMIN_ENDPOINT_URL, @@ -135,28 +125,21 @@ class CLITestAuthNoAuth(testtools.TestCase): def setUp(self): """Prepare the test environment.""" super(CLITestAuthNoAuth, self).setUp() - self.mox = mox.Mox() + + self.requests = self.useFixture(mock_fixture.Fixture()) + self.client = client.HTTPClient(username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD, endpoint_url=ENDPOINT_URL, auth_strategy=NOAUTH, region_name=REGION) - self.addCleanup(self.mox.VerifyAll) - self.addCleanup(self.mox.UnsetStubs) def test_get_noauth(self): - self.mox.StubOutWithMock(self.client, "request") - - res200 = get_response(200) - - self.client.request( - mox.StrContains(ENDPOINT_URL + '/resource'), 'GET', - headers=mox.IsA(dict), - ).AndReturn((res200, '')) - self.mox.ReplayAll() - + url = ENDPOINT_URL + '/resource' + self.requests.get(ENDPOINT_URL + '/resource') self.client.do_request('/resource', 'GET') + self.assertEqual(url, self.requests.last_request.url) self.assertEqual(self.client.endpoint_url, ENDPOINT_URL) @@ -165,11 +148,13 @@ class CLITestAuthKeystone(testtools.TestCase): def setUp(self): """Prepare the test environment.""" super(CLITestAuthKeystone, self).setUp() - self.mox = mox.Mox() for var in ('http_proxy', 'HTTP_PROXY'): self.useFixture(fixtures.EnvironmentVariableFixture(var)) + self.logger = self.useFixture(fixtures.FakeLogger(level=logging.DEBUG)) + self.requests = self.useFixture(mock_fixture.Fixture()) + self.client = client.construct_http_client( username=USERNAME, tenant_name=TENANT_NAME, @@ -177,9 +162,6 @@ def setUp(self): auth_url=AUTH_URL, region_name=REGION) - self.addCleanup(self.mox.VerifyAll) - self.addCleanup(self.mox.UnsetStubs) - def test_reused_token_get_auth_info(self): """Test that Client.get_auth_info() works even if client was instantiated with predefined token. @@ -197,9 +179,8 @@ def test_reused_token_get_auth_info(self): 'endpoint_url': self.client.endpoint_url} self.assertEqual(client_.get_auth_info(), expected) - @requests_mock.Mocker() - def test_get_token(self, mrequests): - auth_session, auth_plugin = setup_keystone_v2(mrequests) + def test_get_token(self): + auth_session, auth_plugin = setup_keystone_v2(self.requests) self.client = client.construct_http_client( username=USERNAME, @@ -210,61 +191,50 @@ def test_get_token(self, mrequests): session=auth_session, auth=auth_plugin) - self.mox.StubOutWithMock(self.client, "request") - res200 = get_response(200) - - self.client.request( - '/resource', 'GET', - authenticated=True - ).AndReturn((res200, '')) - - self.mox.ReplayAll() - + m = self.requests.get(PUBLIC_ENDPOINT_URL + '/resource', + request_headers={'X-Auth-Token': TOKENID}) self.client.do_request('/resource', 'GET') + self.assertTrue(m.called) def test_refresh_token(self): - self.mox.StubOutWithMock(self.client, "request") - token_id = uuid.uuid4().hex + text = uuid.uuid4().hex self.client.auth_token = token_id self.client.endpoint_url = ENDPOINT_URL - res200 = get_response(200) - res401 = get_response(401) - - # If a token is expired, neutron server retruns 401 - self.client.request( - mox.StrContains(ENDPOINT_URL + '/resource'), 'GET', - headers=mox.ContainsKeyValue('X-Auth-Token', token_id) - ).AndReturn((res401, '')) - self.client.request( - AUTH_URL + '/tokens', 'POST', - body=mox.IsA(str), headers=mox.IsA(dict) - ).AndReturn((res200, json.dumps(KS_TOKEN_RESULT))) - self.client.request( - mox.StrContains(ENDPOINT_URL + '/resource'), 'GET', - headers=mox.ContainsKeyValue('X-Auth-Token', - KS_TOKEN_RESULT.token_id) - ).AndReturn((res200, '')) - self.mox.ReplayAll() - self.client.do_request('/resource', 'GET') + res_url = ENDPOINT_URL + '/resource' + v2_url = AUTH_URL + '/tokens' + + # token_id gives 401, KS_TOKEN_RESULT gives 200 + self.requests.get(res_url, + request_headers={'X-Auth-Token': token_id}, + status_code=401) + + self.requests.get( + res_url, + text=text, + status_code=200, + request_headers={'X-Auth-Token': KS_TOKEN_RESULT.token_id}) + + self.requests.post(v2_url, json=KS_TOKEN_RESULT) + + resp = self.client.do_request('/resource', 'GET') + + self.assertEqual(text, resp[1]) + self.assertEqual(3, len(self.requests.request_history)) + + self.assertEqual(res_url, self.requests.request_history[0].url) + self.assertEqual(v2_url, self.requests.request_history[1].url) + self.assertEqual(res_url, self.requests.request_history[2].url) def test_refresh_token_no_auth_url(self): - self.mox.StubOutWithMock(self.client, "request") self.client.auth_url = None token_id = uuid.uuid4().hex self.client.auth_token = token_id self.client.endpoint_url = ENDPOINT_URL - res401 = get_response(401) - - # If a token is expired, neutron server returns 401 - self.client.request( - mox.StrContains(ENDPOINT_URL + '/resource'), 'GET', - headers=mox.ContainsKeyValue('X-Auth-Token', token_id) - ).AndReturn((res401, '')) - self.mox.ReplayAll() + self.requests.get(ENDPOINT_URL + '/resource', status_code=401) self.assertRaises(exceptions.NoAuthURLProvided, self.client.do_request, '/resource', @@ -277,24 +247,18 @@ def test_get_endpoint_url_with_invalid_auth_url(self): self.client._get_endpoint_url) def test_get_endpoint_url(self): - self.mox.StubOutWithMock(self.client, "request") - token_id = uuid.uuid4().hex self.client.auth_token = token_id - res200 = get_response(200) - - self.client.request( - mox.StrContains(AUTH_URL + '/tokens/%s/endpoints' % token_id), - 'GET', headers=mox.IsA(dict) - ).AndReturn((res200, json.dumps(ENDPOINTS_RESULT))) - self.client.request( - mox.StrContains(ENDPOINT_URL + '/resource'), 'GET', - headers=mox.ContainsKeyValue('X-Auth-Token', token_id) - ).AndReturn((res200, '')) - self.mox.ReplayAll() + self.requests.get(AUTH_URL + '/tokens/%s/endpoints' % token_id, + json=ENDPOINTS_RESULT) + self.requests.get(ENDPOINT_URL + '/resource') + self.client.do_request('/resource', 'GET') + self.assertEqual(token_id, + self.requests.last_request.headers['X-Auth-Token']) + def test_use_given_endpoint_url(self): self.client = client.HTTPClient( username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD, @@ -302,69 +266,49 @@ def test_use_given_endpoint_url(self): endpoint_url=ENDPOINT_OVERRIDE) self.assertEqual(self.client.endpoint_url, ENDPOINT_OVERRIDE) - self.mox.StubOutWithMock(self.client, "request") - token_id = uuid.uuid4().hex - self.client.auth_token = token_id - res200 = get_response(200) - self.client.request( - mox.StrContains(ENDPOINT_OVERRIDE + '/resource'), 'GET', - headers=mox.ContainsKeyValue('X-Auth-Token', token_id) - ).AndReturn((res200, '')) - self.mox.ReplayAll() + self.requests.get(ENDPOINT_OVERRIDE + '/resource') + self.client.do_request('/resource', 'GET') + self.assertEqual(self.client.endpoint_url, ENDPOINT_OVERRIDE) + self.assertEqual(token_id, + self.requests.last_request.headers['X-Auth-Token']) def test_get_endpoint_url_other(self): self.client = client.HTTPClient( username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD, auth_url=AUTH_URL, region_name=REGION, endpoint_type='otherURL') - self.mox.StubOutWithMock(self.client, "request") token_id = uuid.uuid4().hex self.client.auth_token = token_id - res200 = get_response(200) - self.client.request( - mox.StrContains(AUTH_URL + '/tokens/%s/endpoints' % token_id), - 'GET', headers=mox.IsA(dict) - ).AndReturn((res200, json.dumps(ENDPOINTS_RESULT))) - self.mox.ReplayAll() + self.requests.get(AUTH_URL + '/tokens/%s/endpoints' % token_id, + json=ENDPOINTS_RESULT) + self.assertRaises(exceptions.EndpointTypeNotFound, self.client.do_request, '/resource', 'GET') def test_get_endpoint_url_failed(self): - self.mox.StubOutWithMock(self.client, "request") - token_id = uuid.uuid4().hex self.client.auth_token = token_id - res200 = get_response(200) - res401 = get_response(401) - - self.client.request( - mox.StrContains(AUTH_URL + '/tokens/%s/endpoints' % token_id), - 'GET', headers=mox.IsA(dict) - ).AndReturn((res401, '')) - self.client.request( - AUTH_URL + '/tokens', 'POST', - body=mox.IsA(str), headers=mox.IsA(dict) - ).AndReturn((res200, json.dumps(KS_TOKEN_RESULT))) - self.client.request( - mox.StrContains(ENDPOINT_URL + '/resource'), 'GET', - headers=mox.ContainsKeyValue('X-Auth-Token', - KS_TOKEN_RESULT.token_id) - ).AndReturn((res200, '')) - self.mox.ReplayAll() + self.requests.get(AUTH_URL + '/tokens/%s/endpoints' % token_id, + status_code=401) + self.requests.post(AUTH_URL + '/tokens', json=KS_TOKEN_RESULT) + m = self.requests.get(ENDPOINT_URL + '/resource') + self.client.do_request('/resource', 'GET') - @requests_mock.Mocker() - def test_endpoint_type(self, mrequests): - auth_session, auth_plugin = setup_keystone_v3(mrequests) + self.assertEqual(KS_TOKEN_RESULT.token_id, + m.last_request.headers['X-Auth-Token']) + + def test_endpoint_type(self): + auth_session, auth_plugin = setup_keystone_v3(self.requests) # Test default behavior is to choose public. self.client = client.construct_http_client( @@ -413,34 +357,16 @@ def test_endpoint_type(self, mrequests): self.client.authenticate) def test_strip_credentials_from_log(self): - def verify_no_credentials(kwargs): - return ('REDACTED' in kwargs['body']) and ( - self.client.password not in kwargs['body']) - - def verify_credentials(body): - return 'REDACTED' not in body and self.client.password in body - - self.mox.StubOutWithMock(self.client, "request") - self.mox.StubOutWithMock(utils, "http_log_req") - - res200 = get_response(200) - - utils.http_log_req(mox.IgnoreArg(), mox.IgnoreArg(), mox.Func( - verify_no_credentials)) - self.client.request( - mox.IsA(six.string_types), mox.IsA(six.string_types), - body=mox.Func(verify_credentials), - headers=mox.IgnoreArg() - ).AndReturn((res200, json.dumps(KS_TOKEN_RESULT))) - utils.http_log_req(mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg()) - self.client.request( - mox.IsA(six.string_types), mox.IsA(six.string_types), - headers=mox.IsA(dict) - ).AndReturn((res200, '')) - self.mox.ReplayAll() - + m = self.requests.post(AUTH_URL + '/tokens', json=KS_TOKEN_RESULT) + self.requests.get(ENDPOINT_URL + '/resource') self.client.do_request('/resource', 'GET') + self.assertIn('REDACTED', self.logger.output) + self.assertNotIn(self.client.password, self.logger.output) + + self.assertNotIn('REDACTED', m.last_request.body) + self.assertIn(self.client.password, m.last_request.body) + class CLITestAuthKeystoneWithId(CLITestAuthKeystone): @@ -473,14 +399,10 @@ class TestKeystoneClientVersions(testtools.TestCase): def setUp(self): """Prepare the test environment.""" super(TestKeystoneClientVersions, self).setUp() - self.mox = mox.Mox() - self.addCleanup(self.mox.VerifyAll) - self.addCleanup(self.mox.UnsetStubs) + self.requests = self.useFixture(mock_fixture.Fixture()) - @requests_mock.Mocker() - def test_v2_auth(self, mrequests): - auth_session, auth_plugin = setup_keystone_v2(mrequests) - res200 = get_response(200) + def test_v2_auth(self): + auth_session, auth_plugin = setup_keystone_v2(self.requests) self.client = client.construct_http_client( username=USERNAME, @@ -491,20 +413,14 @@ def test_v2_auth(self, mrequests): session=auth_session, auth=auth_plugin) - self.mox.StubOutWithMock(self.client, "request") + m = self.requests.get(PUBLIC_ENDPOINT_URL + '/resource') - self.client.request( - '/resource', 'GET', - authenticated=True - ).AndReturn((res200, '')) - - self.mox.ReplayAll() self.client.do_request('/resource', 'GET') - @requests_mock.Mocker() - def test_v3_auth(self, mrequests): - auth_session, auth_plugin = setup_keystone_v3(mrequests) - res200 = get_response(200) + self.assertTrue(m.called) + + def test_v3_auth(self): + auth_session, auth_plugin = setup_keystone_v3(self.requests) self.client = client.construct_http_client( user_id=USER_ID, @@ -515,12 +431,8 @@ def test_v3_auth(self, mrequests): session=auth_session, auth=auth_plugin) - self.mox.StubOutWithMock(self.client, "request") + m = self.requests.get(PUBLIC_ENDPOINT_URL + '/resource') - self.client.request( - '/resource', 'GET', - authenticated=True - ).AndReturn((res200, '')) - - self.mox.ReplayAll() self.client.do_request('/resource', 'GET') + + self.assertTrue(m.called) diff --git a/neutronclient/tests/unit/test_http.py b/neutronclient/tests/unit/test_http.py index 51c6cdcb1..6e04b62df 100644 --- a/neutronclient/tests/unit/test_http.py +++ b/neutronclient/tests/unit/test_http.py @@ -15,15 +15,13 @@ import abc -from mox3 import mox -import requests_mock +from requests_mock.contrib import fixture as mock_fixture import six import testtools from neutronclient import client from neutronclient.common import exceptions from neutronclient.tests.unit import test_auth -from neutronclient.tests.unit.test_cli20 import MyResp AUTH_TOKEN = 'test_token' @@ -39,10 +37,8 @@ class TestHTTPClientMixin(object): def setUp(self): super(TestHTTPClientMixin, self).setUp() + self.requests = self.useFixture(mock_fixture.Fixture()) self.clazz, self.http = self.initialize() - self.mox = mox.Mox() - self.addCleanup(self.mox.UnsetStubs) - self.mox.StubOutWithMock(self.clazz, '_request') @abc.abstractmethod def initialize(self): @@ -50,12 +46,10 @@ def initialize(self): def _test_headers(self, expected_headers, **kwargs): """Test headers.""" - self.clazz._request(URL, METHOD, - body=kwargs.get('body'), - headers=expected_headers) - self.mox.ReplayAll() + self.requests.register_uri(METHOD, URL, + request_headers=expected_headers) self.http.request(URL, METHOD, **kwargs) - self.mox.VerifyAll() + self.assertEqual(kwargs.get('body'), self.requests.last_request.body) def test_headers_without_body(self): self._test_headers({'Accept': 'application/json'}) @@ -82,9 +76,8 @@ def test_headers_defined_in_headers(self): class TestSessionClient(TestHTTPClientMixin, testtools.TestCase): - @requests_mock.Mocker() - def initialize(self, mrequests): - session, auth = test_auth.setup_keystone_v2(mrequests) + def initialize(self): + session, auth = test_auth.setup_keystone_v2(self.requests) return [client.SessionClient, client.SessionClient(session=session, auth=auth)] @@ -96,47 +89,35 @@ def initialize(self): client.HTTPClient(token=AUTH_TOKEN, endpoint_url=END_URL)] def test_request_error(self): - self.clazz._request( - URL, METHOD, body=None, headers=mox.IgnoreArg() - ).AndRaise(Exception('error msg')) - self.mox.ReplayAll() + def cb(*args, **kwargs): + raise Exception('error msg') + self.requests.get(URL, body=cb) self.assertRaises( exceptions.ConnectionFailed, self.http._cs_request, URL, METHOD ) - self.mox.VerifyAll() def test_request_success(self): - rv_should_be = MyResp(200), 'test content' - - self.clazz._request( - URL, METHOD, body=None, headers=mox.IgnoreArg() - ).AndReturn(rv_should_be) - self.mox.ReplayAll() + text = 'test content' + self.requests.register_uri(METHOD, URL, text=text) - self.assertEqual(rv_should_be, self.http._cs_request(URL, METHOD)) - self.mox.VerifyAll() + resp, resp_text = self.http._cs_request(URL, METHOD) + self.assertEqual(200, resp.status_code) + self.assertEqual(text, resp_text) def test_request_unauthorized(self): - rv_should_be = MyResp(401), 'unauthorized message' - self.clazz._request( - URL, METHOD, body=None, headers=mox.IgnoreArg() - ).AndReturn(rv_should_be) - self.mox.ReplayAll() - + text = 'unauthorized message' + self.requests.register_uri(METHOD, URL, status_code=401, text=text) e = self.assertRaises(exceptions.Unauthorized, self.http._cs_request, URL, METHOD) - self.assertEqual('unauthorized message', e.message) - self.mox.VerifyAll() + self.assertEqual(text, e.message) def test_request_forbidden_is_returned_to_caller(self): - rv_should_be = MyResp(403), 'forbidden message' - self.clazz._request( - URL, METHOD, body=None, headers=mox.IgnoreArg() - ).AndReturn(rv_should_be) - self.mox.ReplayAll() - - self.assertEqual(rv_should_be, self.http._cs_request(URL, METHOD)) - self.mox.VerifyAll() + text = 'forbidden message' + self.requests.register_uri(METHOD, URL, status_code=403, text=text) + + resp, resp_text = self.http._cs_request(URL, METHOD) + self.assertEqual(403, resp.status_code) + self.assertEqual(text, resp_text) From 799e288f48e5d99731dedbfb94808a8cbe01c05c Mon Sep 17 00:00:00 2001 From: Jamie Lennox Date: Sun, 31 Aug 2014 13:11:46 +1000 Subject: [PATCH 137/845] Use adapter from keystoneclient The adapter in keystoneclient is there to abstract the common client parameters that may be used when sending a request. We should use that rather than keeping our own methods. Closes-Bug: #1403726 Change-Id: I727ac76babb6f6aef1a0f619c011ad67a6e2fccf --- neutronclient/client.py | 159 +++++++++--------- neutronclient/tests/unit/test_auth.py | 6 +- .../tests/unit/test_cli20_network.py | 8 +- .../tests/unit/test_cli20_securitygroup.py | 7 +- neutronclient/v2_0/client.py | 11 -- 5 files changed, 85 insertions(+), 106 deletions(-) diff --git a/neutronclient/client.py b/neutronclient/client.py index f3aaae231..0a68ff063 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -14,7 +14,6 @@ # under the License. # -import abc try: import json except ImportError: @@ -23,9 +22,8 @@ import os from keystoneclient import access -from keystoneclient.auth.identity.base import BaseIdentityPlugin +from keystoneclient import adapter import requests -import six from neutronclient.common import exceptions from neutronclient.common import utils @@ -44,35 +42,14 @@ logging.getLogger("requests").setLevel(_requests_log_level) -@six.add_metaclass(abc.ABCMeta) -class AbstractHTTPClient(object): +class HTTPClient(object): + """Handles the REST calls and responses, include authn.""" USER_AGENT = 'python-neutronclient' CONTENT_TYPE = 'application/json' - def request(self, url, method, body=None, content_type=None, headers=None, - **kwargs): - """Request without authentication.""" - - headers = headers or {} - content_type = content_type or self.CONTENT_TYPE - headers.setdefault('Accept', content_type) - if body: - headers.setdefault('Content-Type', content_type) - - return self._request(url, method, body=body, headers=headers, **kwargs) - - @abc.abstractmethod - def do_request(self, url, method, **kwargs): - """Request with authentication.""" - - @abc.abstractmethod - def _request(self, url, method, body=None, headers=None, **kwargs): - """Request without authentication nor headers population.""" - - -class HTTPClient(AbstractHTTPClient): - """Handles the REST calls and responses, include authentication.""" + # 8192 Is the default max URI len for eventlet.wsgi.server + MAX_URI_LEN = 8192 def __init__(self, username=None, user_id=None, tenant_name=None, tenant_id=None, @@ -149,8 +126,16 @@ def authenticate_and_fetch_endpoint_url(self): elif not self.endpoint_url: self.endpoint_url = self._get_endpoint_url() - def _request(self, url, method, body=None, headers=None, **kwargs): + def request(self, url, method, body=None, headers=None, **kwargs): + """Request without authentication.""" + + content_type = kwargs.pop('content_type', None) or 'application/json' headers = headers or {} + headers.setdefault('Accept', content_type) + + if body: + headers.setdefault('Content-Type', content_type) + headers['User-Agent'] = self.USER_AGENT resp = requests.request( @@ -164,8 +149,17 @@ def _request(self, url, method, body=None, headers=None, **kwargs): return resp, resp.text + def _check_uri_length(self, action): + uri_len = len(self.endpoint_url) + len(action) + if uri_len > self.MAX_URI_LEN: + raise exceptions.RequestURITooLong( + excess=uri_len - self.MAX_URI_LEN) + def do_request(self, url, method, **kwargs): + # Ensure client always has correct uri - do not guesstimate anything self.authenticate_and_fetch_endpoint_url() + self._check_uri_length(url) + # Perform the request once. If we get a 401 back then it # might be because the auth token expired, so try to # re-authenticate and try again. If it still fails, bail. @@ -280,71 +274,67 @@ def get_auth_info(self): 'endpoint_url': self.endpoint_url} -class SessionClient(AbstractHTTPClient): - - def __init__(self, - session, - auth, - interface=None, - service_type=None, - region_name=None): - - self.session = session - self.auth = auth - self.interface = interface - self.service_type = service_type - self.region_name = region_name - self.auth_token = None - self.endpoint_url = None +class SessionClient(adapter.Adapter): - def _request(self, url, method, body=None, headers=None, **kwargs): - kwargs.setdefault('user_agent', self.USER_AGENT) - kwargs.setdefault('auth', self.auth) + def request(self, *args, **kwargs): kwargs.setdefault('authenticated', False) kwargs.setdefault('raise_exc', False) - endpoint_filter = kwargs.setdefault('endpoint_filter', {}) - endpoint_filter.setdefault('interface', self.interface) - endpoint_filter.setdefault('service_type', self.service_type) - endpoint_filter.setdefault('region_name', self.region_name) + content_type = kwargs.pop('content_type', None) or 'application/json' + + headers = kwargs.setdefault('headers', {}) + headers.setdefault('Accept', content_type) + + try: + kwargs.setdefault('data', kwargs.pop('body')) + except KeyError: + pass + + if kwargs.get('data'): + headers.setdefault('Content-Type', content_type) - resp = self.session.request(url, method, data=body, headers=headers, - **kwargs) + resp = super(SessionClient, self).request(*args, **kwargs) return resp, resp.text def do_request(self, url, method, **kwargs): kwargs.setdefault('authenticated', True) return self.request(url, method, **kwargs) - def authenticate(self): - # This method is provided for backward compatibility only. - # We only care about setting the service endpoint. - self.endpoint_url = self.session.get_endpoint( - self.auth, - service_type=self.service_type, - region_name=self.region_name, - interface=self.interface) + @property + def endpoint_url(self): + # NOTE(jamielennox): This is used purely by the CLI and should be + # removed when the CLI gets smarter. + return self.get_endpoint() - def authenticate_and_fetch_endpoint_url(self): - # This method is provided for backward compatibility only. - # We only care about setting the service endpoint. - self.authenticate() + @property + def auth_token(self): + # NOTE(jamielennox): This is used purely by the CLI and should be + # removed when the CLI gets smarter. + return self.get_token() + + def authenticate(self): + # NOTE(jamielennox): This is used purely by the CLI and should be + # removed when the CLI gets smarter. + self.get_token() def get_auth_info(self): - # This method is provided for backward compatibility only. - if not isinstance(self.auth, BaseIdentityPlugin): - msg = ('Auth info not available. Auth plugin is not an identity ' - 'auth plugin.') - raise exceptions.NeutronClientException(message=msg) - access_info = self.auth.get_access(self.session) - endpoint_url = self.auth.get_endpoint(self.session, - service_type=self.service_type, - region_name=self.region_name, - interface=self.interface) - return {'auth_token': access_info.auth_token, - 'auth_tenant_id': access_info.tenant_id, - 'auth_user_id': access_info.user_id, - 'endpoint_url': endpoint_url} + auth_info = {'auth_token': self.auth_token, + 'endpoint_url': self.endpoint_url} + + # NOTE(jamielennox): This is the best we can do here. It will work + # with identity plugins which is the primary case but we should + # deprecate it's usage as much as possible. + try: + get_access = (self.auth or self.session.auth).get_access + except AttributeError: + pass + else: + auth_ref = get_access(self.session) + + auth_info['auth_tenant_id'] = auth_ref.project_id + auth_info['auth_user_id'] = auth_ref.user_id + + return auth_info # FIXME(bklei): Should refactor this to use kwargs and only @@ -366,14 +356,15 @@ def construct_http_client(username=None, ca_cert=None, service_type='network', session=None, - auth=None): + **kwargs): if session: + kwargs.setdefault('user_agent', 'python-neutronclient') + kwargs.setdefault('interface', endpoint_type) return SessionClient(session=session, - auth=auth, - interface=endpoint_type, service_type=service_type, - region_name=region_name) + region_name=region_name, + **kwargs) else: # FIXME(bklei): username and password are now optional. Need # to test that they were provided in this mode. Should also diff --git a/neutronclient/tests/unit/test_auth.py b/neutronclient/tests/unit/test_auth.py index 7cb8c56b4..2cae5115d 100644 --- a/neutronclient/tests/unit/test_auth.py +++ b/neutronclient/tests/unit/test_auth.py @@ -316,7 +316,6 @@ def test_endpoint_type(self): auth_url=AUTH_URL, region_name=REGION, session=auth_session, auth=auth_plugin) - self.client.authenticate() self.assertEqual(self.client.endpoint_url, PUBLIC_ENDPOINT_URL) # Test admin url @@ -325,7 +324,6 @@ def test_endpoint_type(self): auth_url=AUTH_URL, region_name=REGION, endpoint_type='adminURL', session=auth_session, auth=auth_plugin) - self.client.authenticate() self.assertEqual(self.client.endpoint_url, ADMIN_ENDPOINT_URL) # Test public url @@ -334,7 +332,6 @@ def test_endpoint_type(self): auth_url=AUTH_URL, region_name=REGION, endpoint_type='publicURL', session=auth_session, auth=auth_plugin) - self.client.authenticate() self.assertEqual(self.client.endpoint_url, PUBLIC_ENDPOINT_URL) # Test internal url @@ -343,7 +340,6 @@ def test_endpoint_type(self): auth_url=AUTH_URL, region_name=REGION, endpoint_type='internalURL', session=auth_session, auth=auth_plugin) - self.client.authenticate() self.assertEqual(self.client.endpoint_url, INTERNAL_ENDPOINT_URL) # Test url that isn't found in the service catalog @@ -354,7 +350,7 @@ def test_endpoint_type(self): self.assertRaises( ks_exceptions.EndpointNotFound, - self.client.authenticate) + getattr, self.client, 'endpoint_url') def test_strip_credentials_from_log(self): m = self.requests.post(AUTH_URL + '/tokens', json=KS_TOKEN_RESULT) diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index a7f5aa817..321e79816 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -519,13 +519,15 @@ def mox_calls(path, data): filters, response = self._build_test_data(data) # 1 char of extra URI len will cause a split in 2 requests - self.mox.StubOutWithMock(self.client, "_check_uri_length") - self.client._check_uri_length(mox.IgnoreArg()).AndRaise( + self.mox.StubOutWithMock(self.client.httpclient, + "_check_uri_length") + self.client.httpclient._check_uri_length(mox.IgnoreArg()).AndRaise( exceptions.RequestURITooLong(excess=1)) for data in sub_data_lists: filters, response = self._build_test_data(data) - self.client._check_uri_length(mox.IgnoreArg()).AndReturn(None) + self.client.httpclient._check_uri_length( + mox.IgnoreArg()).AndReturn(None) self.client.httpclient.request( test_cli20.MyUrlComparator( test_cli20.end_url( diff --git a/neutronclient/tests/unit/test_cli20_securitygroup.py b/neutronclient/tests/unit/test_cli20_securitygroup.py index d1cc13694..788da53d3 100644 --- a/neutronclient/tests/unit/test_cli20_securitygroup.py +++ b/neutronclient/tests/unit/test_cli20_securitygroup.py @@ -265,13 +265,14 @@ def mox_calls(path, data): def test_extend_list_exceed_max_uri_len(self): def mox_calls(path, data): # 1 char of extra URI len will cause a split in 2 requests - self.mox.StubOutWithMock(self.client, '_check_uri_length') - self.client._check_uri_length(mox.IgnoreArg()).AndRaise( + self.mox.StubOutWithMock(self.client.httpclient, + '_check_uri_length') + self.client.httpclient._check_uri_length(mox.IgnoreArg()).AndRaise( exceptions.RequestURITooLong(excess=1)) responses = self._build_test_data(data, excess=1) for item in responses: - self.client._check_uri_length( + self.client.httpclient._check_uri_length( mox.IgnoreArg()).AndReturn(None) self.client.httpclient.request( test_cli20.end_url(path, item['filter']), diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 44035b6c6..0e8e6dc56 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -256,8 +256,6 @@ class Client(object): 'net_partitions': 'net_partition', 'packet_filters': 'packet_filter', } - # 8192 Is the default max URI len for eventlet.wsgi.server - MAX_URI_LEN = 8192 def get_attr_metadata(self): if self.format == 'json': @@ -1219,12 +1217,6 @@ def _handle_fault_response(self, status_code, response_body): # Raise the appropriate exception exception_handler_v20(status_code, des_error_body) - def _check_uri_length(self, action): - uri_len = len(self.httpclient.endpoint_url) + len(action) - if uri_len > self.MAX_URI_LEN: - raise exceptions.RequestURITooLong( - excess=uri_len - self.MAX_URI_LEN) - def do_request(self, method, action, body=None, headers=None, params=None): # Add format and tenant_id action += ".%s" % self.format @@ -1232,9 +1224,6 @@ def do_request(self, method, action, body=None, headers=None, params=None): if type(params) is dict and params: params = utils.safe_encode_dict(params) action += '?' + urlparse.urlencode(params, doseq=1) - # Ensure client always has correct uri - do not guesstimate anything - self.httpclient.authenticate_and_fetch_endpoint_url() - self._check_uri_length(action) if body: body = self.serialize(body) From 4beadef8b5a254a08e03ab80ae1f32a5c07d5278 Mon Sep 17 00:00:00 2001 From: Swaminathan Vasudevan Date: Thu, 4 Dec 2014 18:11:59 -0800 Subject: [PATCH 138/845] Fix True/False to accept Camel and Lower case There are couple of inconsistency in using the Camelcase and Lower case for 'True/False' options in the python-neutronclient. With this fix it will be consistent across all CLI commands. Change-Id: Ifc08ac326e8130f69f864b05781076951a306fa0 --- neutronclient/neutron/v2_0/fw/firewallrule.py | 3 ++- neutronclient/neutron/v2_0/nec/packetfilter.py | 3 ++- neutronclient/neutron/v2_0/router.py | 4 ++-- .../tests/unit/fw/test_cli20_firewallrule.py | 6 ++++++ neutronclient/tests/unit/test_cli20_router.py | 13 +++++++++++-- 5 files changed, 23 insertions(+), 6 deletions(-) diff --git a/neutronclient/neutron/v2_0/fw/firewallrule.py b/neutronclient/neutron/v2_0/fw/firewallrule.py index 03f425c7b..87405d9dd 100644 --- a/neutronclient/neutron/v2_0/fw/firewallrule.py +++ b/neutronclient/neutron/v2_0/fw/firewallrule.py @@ -97,7 +97,8 @@ def add_known_arguments(self, parser): 'a:b).')) parser.add_argument( '--enabled', - dest='enabled', choices=['True', 'False'], + dest='enabled', metavar='{True,False}', + choices=['True', 'true', 'False', 'false'], help=_('Whether to enable or disable this rule.'), default=argparse.SUPPRESS) parser.add_argument( diff --git a/neutronclient/neutron/v2_0/nec/packetfilter.py b/neutronclient/neutron/v2_0/nec/packetfilter.py index 48d0f744d..bb12fce69 100644 --- a/neutronclient/neutron/v2_0/nec/packetfilter.py +++ b/neutronclient/neutron/v2_0/nec/packetfilter.py @@ -71,7 +71,8 @@ def add_known_arguments(self, parser): help=_('Set Admin State Up to false')) else: parser.add_argument( - '--admin-state', choices=['True', 'False'], + '--admin-state', metavar='{True,False}', + choices=['True', 'true', 'False', 'false'], help=_('Set a value of Admin State Up')) parser.add_argument( diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index 0a6cc2fa8..b63048073 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -74,8 +74,8 @@ def add_known_arguments(self, parser): help=_('Create a distributed router.')) parser.add_argument( '--ha', - dest='ha', - choices=['True', 'False'], + dest='ha', metavar='{True,False}', + choices=['True', 'true', 'false', 'False'], default=argparse.SUPPRESS, help=_('Create a highly available router.')) diff --git a/neutronclient/tests/unit/fw/test_cli20_firewallrule.py b/neutronclient/tests/unit/fw/test_cli20_firewallrule.py index 8f6ef9fa9..e302d6569 100644 --- a/neutronclient/tests/unit/fw/test_cli20_firewallrule.py +++ b/neutronclient/tests/unit/fw/test_cli20_firewallrule.py @@ -46,6 +46,12 @@ def _test_create_firewall_rule_with_mandatory_params(self, enabled): protocol=protocol, action=action, enabled=enabled, tenant_id=tenant_id) + def test_create_enabled_firewall_rule_with_mandatory_params_lcase(self): + self._test_create_firewall_rule_with_mandatory_params(enabled='true') + + def test_create_disabled_firewall_rule_with_mandatory_params_lcase(self): + self._test_create_firewall_rule_with_mandatory_params(enabled='false') + def test_create_enabled_firewall_rule_with_mandatory_params(self): self._test_create_firewall_rule_with_mandatory_params(enabled='True') diff --git a/neutronclient/tests/unit/test_cli20_router.py b/neutronclient/tests/unit/test_cli20_router.py index 33e22d550..87e878aa6 100644 --- a/neutronclient/tests/unit/test_cli20_router.py +++ b/neutronclient/tests/unit/test_cli20_router.py @@ -87,8 +87,17 @@ def test_create_router_distributed_True(self): """Create router: --distributed=True.""" self._create_router_distributed_or_ha(distributed='True') - def test_create_router_ha(self): - self._create_router_distributed_or_ha(ha=True) + def test_create_router_ha_with_True(self): + self._create_router_distributed_or_ha(ha='True') + + def test_create_router_ha_with_true(self): + self._create_router_distributed_or_ha(ha='true') + + def test_create_router_ha_with_False(self): + self._create_router_distributed_or_ha(ha='False') + + def test_create_router_ha_with_false(self): + self._create_router_distributed_or_ha(ha='false') def test_create_router_distributed_False(self): """Create router: --distributed=False.""" From 4fa57fe2708382b8f6029bfd351091f352583105 Mon Sep 17 00:00:00 2001 From: Terry Howe Date: Mon, 22 Dec 2014 20:10:43 -0700 Subject: [PATCH 139/845] Namespace of arguments is incorrectly used Namespace does not have project_domain_name, it has os_project_domain_name. Change-Id: Ic0f0265078ddf92673e67e7aa276a57f2e37c98b Closes-Bug: 1405046 --- neutronclient/shell.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 0329d5272..9d703b5bb 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -719,8 +719,8 @@ def authenticate_user(self): project_info = (self.options.os_tenant_name or self.options.os_tenant_id or (self.options.os_project_name and - (self.options.project_domain_name or - self.options.project_domain_id)) or + (self.options.os_project_domain_name or + self.options.os_project_domain_id)) or self.options.os_project_id) if (not self.options.os_username From d6e40b566e059d3a3a352c371955077e128356a8 Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Wed, 7 Jan 2015 09:47:21 +0100 Subject: [PATCH 140/845] Add Python 3 classifiers Change-Id: Ib22c48ce091916c7c874db91c8c16ebfc7846411 --- setup.cfg | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup.cfg b/setup.cfg index 50dba19d7..ee3f9af84 100644 --- a/setup.cfg +++ b/setup.cfg @@ -17,6 +17,9 @@ classifier = Programming Language :: Python :: 2 Programming Language :: Python :: 2.7 Programming Language :: Python :: 2.6 + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.3 + Programming Language :: Python :: 3.4 [files] packages = From aed3faf074dbf631dcde55479cc76cb9aa795635 Mon Sep 17 00:00:00 2001 From: shihanzhang Date: Thu, 8 Jan 2015 12:08:17 +0800 Subject: [PATCH 141/845] Fix TypeError for six.text_type Change-Id: Ib0d668a6ddadb0fec60c964dc5e1e1bd83b74be4 Closes-Bug: #1408529 --- neutronclient/common/serializer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/common/serializer.py b/neutronclient/common/serializer.py index 267d3edd6..0a016d17a 100644 --- a/neutronclient/common/serializer.py +++ b/neutronclient/common/serializer.py @@ -62,7 +62,7 @@ class JSONDictSerializer(DictSerializer): def default(self, data): def sanitizer(obj): - return six.text_type(obj, 'utf8') + return six.text_type(obj) return jsonutils.dumps(data, default=sanitizer) From 096fd1b175085fcc3c46a248b4afb67561fd29ef Mon Sep 17 00:00:00 2001 From: shihanzhang Date: Thu, 29 May 2014 15:12:49 +0800 Subject: [PATCH 142/845] Add '--router:external' option to 'net-create' Change-Id: Ibb100d54a5fd8b04ac5e1fc3a26826c695f4d951 Closes-bug: #1320793 --- neutronclient/neutron/v2_0/network.py | 8 +++++++- neutronclient/tests/unit/test_cli20_network.py | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/neutronclient/neutron/v2_0/network.py b/neutronclient/neutron/v2_0/network.py index a984926aa..9850601a7 100644 --- a/neutronclient/neutron/v2_0/network.py +++ b/neutronclient/neutron/v2_0/network.py @@ -119,6 +119,11 @@ def add_known_arguments(self, parser): action='store_true', help=_('Set the network as shared.'), default=argparse.SUPPRESS) + parser.add_argument( + '--router:external', + action='store_true', + help=_('Set network as external, it is only available for admin'), + default=argparse.SUPPRESS) parser.add_argument( 'name', metavar='NAME', help=_('Name of network to create.')) @@ -128,7 +133,8 @@ def args2body(self, parsed_args): 'name': parsed_args.name, 'admin_state_up': parsed_args.admin_state}, } neutronV20.update_dict(parsed_args, body['network'], - ['shared', 'tenant_id']) + ['shared', 'tenant_id', 'router:external']) + return body diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index a7f5aa817..bd8181f27 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -85,6 +85,20 @@ def test_create_network_tags(self): position_names, position_values, tags=['a', 'b']) + def test_create_network_external(self): + """Create net: --router:external myname.""" + resource = 'network' + cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + args = [name, '--router:external'] + position_names = ['name', ] + position_values = [name, ] + external = {'router:external': True} + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values, + **external) + def test_create_network_state(self): """Create net: --admin_state_down myname.""" resource = 'network' From 78b63107f3d6be7bf1220e3c99dc667cfbb29aeb Mon Sep 17 00:00:00 2001 From: shihanzhang Date: Thu, 6 Mar 2014 18:46:33 +0800 Subject: [PATCH 143/845] Parameter support both id and name The parameter of router operation not only supports id also supports name, so it should modify the hint. Change-Id: Ib5ddb771fb87181bcf638499703c03ebcc9cc680 Closes-bug: #1288656 --- neutronclient/neutron/v2_0/router.py | 30 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index 0a6cc2fa8..c44dd23f3 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -113,8 +113,8 @@ def success_message(self, router_id, portinfo): def get_parser(self, prog_name): parser = super(RouterInterfaceCommand, self).get_parser(prog_name) parser.add_argument( - 'router_id', metavar='router-id', - help=_('ID of the router.')) + 'router', metavar='ROUTER', + help=_('ID or name of the router.')) parser.add_argument( 'interface', metavar='INTERFACE', help=_('The format is "SUBNET|subnet=SUBNET|port=PORT". ' @@ -139,14 +139,14 @@ def run(self, parsed_args): value = parsed_args.interface _router_id = neutronV20.find_resourceid_by_name_or_id( - neutron_client, self.resource, parsed_args.router_id) + neutron_client, self.resource, parsed_args.router) _interface_id = neutronV20.find_resourceid_by_name_or_id( neutron_client, resource, value) body = {'%s_id' % resource: _interface_id} portinfo = self.call_api(neutron_client, _router_id, body) - print(self.success_message(parsed_args.router_id, portinfo), + print(self.success_message(parsed_args.router, portinfo), file=self.app.stdout) @@ -181,11 +181,11 @@ class SetGatewayRouter(neutronV20.NeutronCommand): def get_parser(self, prog_name): parser = super(SetGatewayRouter, self).get_parser(prog_name) parser.add_argument( - 'router_id', metavar='router-id', - help=_('ID of the router.')) + 'router', metavar='ROUTER', + help=_('ID or name of the router.')) parser.add_argument( - 'external_network_id', metavar='external-network-id', - help=_('ID of the external network for the gateway.')) + 'external_network', metavar='EXTERNAL-NETWORK', + help=_('ID or name of the external network for the gateway.')) parser.add_argument( '--disable-snat', action='store_true', help=_('Disable source NAT on the router gateway.')) @@ -196,14 +196,14 @@ def run(self, parsed_args): neutron_client = self.get_client() neutron_client.format = parsed_args.request_format _router_id = neutronV20.find_resourceid_by_name_or_id( - neutron_client, self.resource, parsed_args.router_id) + neutron_client, self.resource, parsed_args.router) _ext_net_id = neutronV20.find_resourceid_by_name_or_id( - neutron_client, 'network', parsed_args.external_network_id) + neutron_client, 'network', parsed_args.external_network) router_dict = {'network_id': _ext_net_id} if parsed_args.disable_snat: router_dict['enable_snat'] = False neutron_client.add_gateway_router(_router_id, router_dict) - print(_('Set gateway for router %s') % parsed_args.router_id, + print(_('Set gateway for router %s') % parsed_args.router, file=self.app.stdout) @@ -216,8 +216,8 @@ class RemoveGatewayRouter(neutronV20.NeutronCommand): def get_parser(self, prog_name): parser = super(RemoveGatewayRouter, self).get_parser(prog_name) parser.add_argument( - 'router_id', metavar='router-id', - help=_('ID of the router.')) + 'router', metavar='ROUTER', + help=_('ID or name of the router.')) return parser def run(self, parsed_args): @@ -225,7 +225,7 @@ def run(self, parsed_args): neutron_client = self.get_client() neutron_client.format = parsed_args.request_format _router_id = neutronV20.find_resourceid_by_name_or_id( - neutron_client, self.resource, parsed_args.router_id) + neutron_client, self.resource, parsed_args.router) neutron_client.remove_gateway_router(_router_id) - print(_('Removed gateway from router %s') % parsed_args.router_id, + print(_('Removed gateway from router %s') % parsed_args.router, file=self.app.stdout) From cb5d462f6eec1e48bba1cb506144e63c5a785cc6 Mon Sep 17 00:00:00 2001 From: sridhargaddam Date: Sun, 16 Nov 2014 13:56:57 +0000 Subject: [PATCH 144/845] Parse provider network attributes in net_create Currently when provider network attributes are placed before the network name in neutron net-create command, the network name is incorrectly parsed to one of the provider attribute values. This patch addresses the issue by parsing the provider network attributes, which inturn allows us to place these attributes at any location in the argument list. Closes-Bug: #1384984 Change-Id: I930ced12d1f6747df91a47af8f34585ed36b84f3 --- neutronclient/neutron/v2_0/network.py | 21 +++++++++++++++++-- .../tests/unit/test_cli20_network.py | 18 ++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/neutronclient/neutron/v2_0/network.py b/neutronclient/neutron/v2_0/network.py index 9850601a7..44adff744 100644 --- a/neutronclient/neutron/v2_0/network.py +++ b/neutronclient/neutron/v2_0/network.py @@ -124,6 +124,21 @@ def add_known_arguments(self, parser): action='store_true', help=_('Set network as external, it is only available for admin'), default=argparse.SUPPRESS) + parser.add_argument( + '--provider:network_type', + metavar='', + help=_('The physical mechanism by which the virtual network' + ' is implemented.')) + parser.add_argument( + '--provider:physical_network', + metavar='', + help=_('Name of the physical network over which the virtual' + ' network is implemented.')) + parser.add_argument( + '--provider:segmentation_id', + metavar='', + help=_('VLAN ID for VLAN networks or tunnel-id for GRE/VXLAN' + ' networks.')) parser.add_argument( 'name', metavar='NAME', help=_('Name of network to create.')) @@ -133,8 +148,10 @@ def args2body(self, parsed_args): 'name': parsed_args.name, 'admin_state_up': parsed_args.admin_state}, } neutronV20.update_dict(parsed_args, body['network'], - ['shared', 'tenant_id', 'router:external']) - + ['shared', 'tenant_id', 'router:external', + 'provider:network_type', + 'provider:physical_network', + 'provider:segmentation_id']) return body diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index d6e6c6ee9..1ac49b637 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -72,6 +72,24 @@ def test_create_network_tenant(self): position_names, position_values, tenant_id='tenantid') + def test_create_network_provider_args(self): + """Create net: with --provider arguments.""" + resource = 'network' + cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + + # Test --provider attributes before network name + args = ['--provider:network_type', 'vlan', + '--provider:physical_network', 'physnet1', + '--provider:segmentation_id', '400', name] + position_names = ['provider:network_type', + 'provider:physical_network', + 'provider:segmentation_id', 'name'] + position_values = ['vlan', 'physnet1', '400', name] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + def test_create_network_tags(self): """Create net: myname --tags a b.""" resource = 'network' From 86fede6e017a7c9c920aac91decc765a82c72a59 Mon Sep 17 00:00:00 2001 From: Ilya Shakhat Date: Tue, 13 Jan 2015 17:12:35 +0300 Subject: [PATCH 145/845] Remove unreachable code from test_cli20 class Change-Id: Ic97efb9a066e4709ee07405d9964988bfb659eff --- neutronclient/tests/unit/test_cli20.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 912eb190b..21db5021e 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -177,11 +177,6 @@ def _find_resourceid(self, client, resource, name_or_id, def _get_attr_metadata(self): return self.metadata - client.Client.EXTED_PLURALS.update(constants.PLURALS) - client.Client.EXTED_PLURALS.update({'tags': 'tag'}) - return {'plurals': client.Client.EXTED_PLURALS, - 'xmlns': constants.XML_NS_V20, - constants.EXT_NS: {'prefix': 'http://xxxx.yy.com'}} def setUp(self, plurals=None): """Prepare the test environment.""" From 30bd81c72ff73f4a6d87bf88fdaf98b287b59a72 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Thu, 15 Jan 2015 02:32:16 +0000 Subject: [PATCH 146/845] Updated from global requirements Change-Id: I8d4fba2e05bd73311eb86c948e7f1c6bdb953a6a --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 0b0c1fece..f6278f11a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,8 +7,8 @@ cliff>=1.7.0 # Apache-2.0 iso8601>=0.1.9 netaddr>=0.7.12 oslo.i18n>=1.0.0 # Apache-2.0 -oslo.serialization>=1.0.0 # Apache-2.0 -oslo.utils>=1.1.0 # Apache-2.0 +oslo.serialization>=1.2.0 # Apache-2.0 +oslo.utils>=1.2.0 # Apache-2.0 requests>=2.2.0,!=2.4.0 python-keystoneclient>=0.11.1 simplejson>=2.2.0 From 845f4618f4818d3f536eda1dde8d9b9d860f0417 Mon Sep 17 00:00:00 2001 From: sridhargaddam Date: Wed, 19 Nov 2014 10:03:02 +0000 Subject: [PATCH 147/845] Fix KeyError when filtering SG rule listing extend_list API of ListSecurityGroupRule class assumes that the data includes security_group_id and remote_group_id fields, which may not be the case when we filter on other fields. This patch addresses this issue. Closes-Bug: #1359230 Change-Id: Icb38e6e926ae441116ac4a895650f80c24d373a6 --- neutronclient/neutron/v2_0/securitygroup.py | 6 +++-- .../tests/unit/test_cli20_securitygroup.py | 23 +++++++++++-------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/neutronclient/neutron/v2_0/securitygroup.py b/neutronclient/neutron/v2_0/securitygroup.py index a1a2bf5b9..7f27eaca2 100644 --- a/neutronclient/neutron/v2_0/securitygroup.py +++ b/neutronclient/neutron/v2_0/securitygroup.py @@ -134,7 +134,8 @@ def extend_list(self, data, parsed_args): sec_group_ids = set() for rule in data: for key in self.replace_rules: - sec_group_ids.add(rule[key]) + if key in rule: + sec_group_ids.add(rule[key]) sec_group_ids = list(sec_group_ids) def _get_sec_group_list(sec_group_ids): @@ -164,7 +165,8 @@ def _get_sec_group_list(sec_group_ids): for sg in secgroups if sg['name']]) for rule in data: for key in self.replace_rules: - rule[key] = sg_dict.get(rule[key], rule[key]) + if key in rule: + rule[key] = sg_dict.get(rule[key], rule[key]) def setup_columns(self, info, parsed_args): parsed_args.columns = self.replace_columns(parsed_args.columns, diff --git a/neutronclient/tests/unit/test_cli20_securitygroup.py b/neutronclient/tests/unit/test_cli20_securitygroup.py index 788da53d3..a90662154 100644 --- a/neutronclient/tests/unit/test_cli20_securitygroup.py +++ b/neutronclient/tests/unit/test_cli20_securitygroup.py @@ -189,14 +189,9 @@ def test_list_security_group_rules(self): mox.IgnoreArg()) self._test_list_resources(resources, cmd, True) - def _test_extend_list(self, mox_calls): + def _test_extend_list(self, mox_calls, data): resources = "security_groups" - data = [{'name': 'default', - 'security_group_id': 'secgroupid%02d' % i, - 'remote_group_id': 'remgroupid%02d' % i} - for i in range(10)] - cmd = securitygroup.ListSecurityGroupRule( test_cli20.MyApp(sys.stdout), None) self.mox.StubOutWithMock(cmd, "get_client") @@ -226,8 +221,9 @@ def _build_test_data(self, data, excess=0): sec_group_ids = set() for rule in data: for key in replace_rules: - sec_group_ids.add(rule[key]) - response.append({'id': rule[key], 'name': 'default'}) + if rule.get(key): + sec_group_ids.add(rule[key]) + response.append({'id': rule[key], 'name': 'default'}) sec_group_ids = list(sec_group_ids) result = [] @@ -260,7 +256,10 @@ def mox_calls(path, data): 'X-Auth-Token', test_cli20.TOKEN)).AndReturn( responses[0]['response']) - self._test_extend_list(mox_calls) + data = [{'name': 'default', + 'remote_group_id': 'remgroupid%02d' % i} + for i in range(10)] + self._test_extend_list(mox_calls, data) def test_extend_list_exceed_max_uri_len(self): def mox_calls(path, data): @@ -282,7 +281,11 @@ def mox_calls(path, data): 'X-Auth-Token', test_cli20.TOKEN)).AndReturn( item['response']) - self._test_extend_list(mox_calls) + data = [{'name': 'default', + 'security_group_id': 'secgroupid%02d' % i, + 'remote_group_id': 'remgroupid%02d' % i} + for i in range(10)] + self._test_extend_list(mox_calls, data) def test_list_security_group_rules_pagination(self): resources = "security_group_rules" From 5b1c45a3ecedd17ffe27767067611d45c22a0bc6 Mon Sep 17 00:00:00 2001 From: Yuuichi Fujioka Date: Mon, 22 Dec 2014 15:31:21 +0900 Subject: [PATCH 148/845] Add floating-ip-address to floatingip-create a parameter is added to the API. this patch adds a parameter that is collesponding to the parameter. partial blueprint allow-specific-floating-ip-address Change-Id: I232568db11054b73a9a557cb669e570bcd362d6d --- neutronclient/neutron/v2_0/floatingip.py | 6 ++++++ neutronclient/tests/unit/test_cli20_floatingips.py | 14 ++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/neutronclient/neutron/v2_0/floatingip.py b/neutronclient/neutron/v2_0/floatingip.py index f52e52a77..d1fc9f466 100644 --- a/neutronclient/neutron/v2_0/floatingip.py +++ b/neutronclient/neutron/v2_0/floatingip.py @@ -61,6 +61,9 @@ def add_known_arguments(self, parser): parser.add_argument( '--fixed_ip_address', help=argparse.SUPPRESS) + parser.add_argument( + '--floating-ip-address', + help=_('IP address of the floating IP')) def args2body(self, parsed_args): _network_id = neutronV20.find_resourceid_by_name_or_id( @@ -73,6 +76,9 @@ def args2body(self, parsed_args): if parsed_args.fixed_ip_address: body[self.resource].update({'fixed_ip_address': parsed_args.fixed_ip_address}) + if parsed_args.floating_ip_address: + body[self.resource].update({'floating_ip_address': + parsed_args.floating_ip_address}) return body diff --git a/neutronclient/tests/unit/test_cli20_floatingips.py b/neutronclient/tests/unit/test_cli20_floatingips.py index 3a98e2cf6..92f527a4b 100644 --- a/neutronclient/tests/unit/test_cli20_floatingips.py +++ b/neutronclient/tests/unit/test_cli20_floatingips.py @@ -71,6 +71,20 @@ def test_create_floatingip_and_port_and_address(self): self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) + def test_create_floatingip_with_ip_address_of_floating_ip(self): + """Create floatingip: fip1 with a given IP address of floating IP.""" + resource = 'floatingip' + cmd = fip.CreateFloatingIP(test_cli20.MyApp(sys.stdout), None) + name = 'fip1' + myid = 'myid' + addr = '10.0.0.99' + + args = [name, '--floating-ip-address', addr] + position_values = [name, addr] + position_names = ['floating_network_id', 'floating_ip_address'] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + def test_list_floatingips(self): """list floatingips: -D.""" resources = 'floatingips' From 51d2a230e62ef10a3675d67bc36583f434e6300f Mon Sep 17 00:00:00 2001 From: Elena Ezhova Date: Thu, 4 Dec 2014 11:32:20 +0300 Subject: [PATCH 149/845] Add parser options for port-update and port-create Added parser options for port parameters that can be updated thus making the help reference for port-update more verbose. This patch also allows subnet_id and ip_address parameters of the --fixed-ip argument to be passed in one update request. Added a unit test for this. Also added a missing device_owner argument to both port-update and port-create commands. Change-Id: Id78a1400b282b34ee7b30437390be954a2c34f88 Closes-Bug: #1165171 Closes-Bug: #1208427 Closes-Bug: #1399681 --- neutronclient/neutron/v2_0/port.py | 104 +++++++++++++------- neutronclient/tests/unit/test_cli20_port.py | 33 ++++++- 2 files changed, 97 insertions(+), 40 deletions(-) diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index bee12e105..00ff586ea 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -38,6 +38,55 @@ def _format_fixed_ips_csv(port): return '' +def _add_updatable_args(parser): + parser.add_argument( + '--name', + help=_('Name of this port.')) + parser.add_argument( + '--fixed-ip', metavar='subnet_id=SUBNET,ip_address=IP_ADDR', + action='append', + help=_('Desired IP and/or subnet for this port: ' + 'subnet_id=,ip_address=. ' + 'You can repeat this option.')) + parser.add_argument( + '--fixed_ip', + action='append', + help=argparse.SUPPRESS) + parser.add_argument( + '--device-id', + help=_('Device ID of this port.')) + parser.add_argument( + '--device_id', + help=argparse.SUPPRESS) + parser.add_argument( + '--device-owner', + help=_('Device owner of this port.')) + parser.add_argument( + '--device_owner', + help=argparse.SUPPRESS) + + +def _updatable_args2body(parsed_args, body, client): + if parsed_args.device_id: + body['port'].update({'device_id': parsed_args.device_id}) + if parsed_args.device_owner: + body['port'].update({'device_owner': parsed_args.device_owner}) + if parsed_args.name: + body['port'].update({'name': parsed_args.name}) + ips = [] + if parsed_args.fixed_ip: + for ip_spec in parsed_args.fixed_ip: + ip_dict = utils.str2dict(ip_spec) + if 'subnet_id' in ip_dict: + subnet_name_id = ip_dict['subnet_id'] + _subnet_id = neutronV20.find_resourceid_by_name_or_id( + client, 'subnet', subnet_name_id) + ip_dict['subnet_id'] = _subnet_id + ips.append(ip_dict) + if ips: + body['port'].update({'fixed_ips': ips}) + + class ListPort(neutronV20.ListCommand): """List ports that belong to a given tenant.""" @@ -152,9 +201,7 @@ class CreatePort(neutronV20.CreateCommand, UpdatePortSecGroupMixin, resource = 'port' def add_known_arguments(self, parser): - parser.add_argument( - '--name', - help=_('Name of this port.')) + _add_updatable_args(parser) parser.add_argument( '--admin-state-down', dest='admin_state', action='store_false', @@ -169,23 +216,6 @@ def add_known_arguments(self, parser): parser.add_argument( '--mac_address', help=argparse.SUPPRESS) - parser.add_argument( - '--device-id', - help=_('Device ID of this port.')) - parser.add_argument( - '--device_id', - help=argparse.SUPPRESS) - parser.add_argument( - '--fixed-ip', metavar='subnet_id=SUBNET,ip_address=IP_ADDR', - action='append', - help=_('Desired IP and/or subnet for this port: ' - 'subnet_id=,ip_address=.' - 'You can repeat this option.')) - parser.add_argument( - '--fixed_ip', - action='append', - help=argparse.SUPPRESS) - self.add_arguments_secgroup(parser) self.add_arguments_extradhcpopt(parser) @@ -194,30 +224,16 @@ def add_known_arguments(self, parser): help=_('Network ID or name this port belongs to.')) def args2body(self, parsed_args): + client = self.get_client() _network_id = neutronV20.find_resourceid_by_name_or_id( - self.get_client(), 'network', parsed_args.network_id) + client, 'network', parsed_args.network_id) body = {'port': {'admin_state_up': parsed_args.admin_state, 'network_id': _network_id, }, } + _updatable_args2body(parsed_args, body, client) if parsed_args.mac_address: body['port'].update({'mac_address': parsed_args.mac_address}) - if parsed_args.device_id: - body['port'].update({'device_id': parsed_args.device_id}) if parsed_args.tenant_id: body['port'].update({'tenant_id': parsed_args.tenant_id}) - if parsed_args.name: - body['port'].update({'name': parsed_args.name}) - ips = [] - if parsed_args.fixed_ip: - for ip_spec in parsed_args.fixed_ip: - ip_dict = utils.str2dict(ip_spec) - if 'subnet_id' in ip_dict: - subnet_name_id = ip_dict['subnet_id'] - _subnet_id = neutronV20.find_resourceid_by_name_or_id( - self.get_client(), 'subnet', subnet_name_id) - ip_dict['subnet_id'] = _subnet_id - ips.append(ip_dict) - if ips: - body['port'].update({'fixed_ips': ips}) self.args2body_secgroup(parsed_args, body['port']) self.args2body_extradhcpopt(parsed_args, body['port']) @@ -238,11 +254,25 @@ class UpdatePort(neutronV20.UpdateCommand, UpdatePortSecGroupMixin, resource = 'port' def add_known_arguments(self, parser): + _add_updatable_args(parser) + parser.add_argument( + '--admin-state-up', + choices=['True', 'False'], + help=_('Set admin state up for the port.')) + parser.add_argument( + '--admin_state_up', + choices=['True', 'False'], + help=argparse.SUPPRESS) self.add_arguments_secgroup(parser) self.add_arguments_extradhcpopt(parser) def args2body(self, parsed_args): body = {'port': {}} + client = self.get_client() + _updatable_args2body(parsed_args, body, client) + if parsed_args.admin_state_up: + body['port'].update({'admin_state_up': + parsed_args.admin_state_up}) self.args2body_secgroup(parsed_args, body['port']) self.args2body_extradhcpopt(parsed_args, body['port']) return body diff --git a/neutronclient/tests/unit/test_cli20_port.py b/neutronclient/tests/unit/test_cli20_port.py index d7306d709..2b61027b7 100644 --- a/neutronclient/tests/unit/test_cli20_port.py +++ b/neutronclient/tests/unit/test_cli20_port.py @@ -320,14 +320,18 @@ def test_list_router_ports_fields(self): fields_2=['c', 'd']) def test_update_port(self): - """Update port: myid --name myname --tags a b.""" + """Update port: myid --name myname --admin-state-up False + --tags a b. + """ resource = 'port' cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', ['myid', '--name', 'myname', + '--admin-state-up', 'False', '--tags', 'a', 'b'], - {'name': 'myname', 'tags': ['a', 'b'], } - ) + {'name': 'myname', + 'admin_state_up': 'False', + 'tags': ['a', 'b'], }) def test_update_port_secgroup(self): resource = 'port' @@ -368,6 +372,29 @@ def test_update_port_extra_dhcp_opts(self): cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, myid, args, updatedfields) + def test_update_port_fixed_ip(self): + resource = 'port' + cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) + myid = 'myid' + net_id = 'net_id' + ip_addr = '123.123.123.123' + args = [myid, + '--fixed-ip', "network_id=%(net_id)s,ip_address=%(ip_addr)s" % + {'net_id': net_id, + 'ip_addr': ip_addr}] + updated_fields = {"fixed_ips": [{'network_id': net_id, + 'ip_address': ip_addr}]} + self._test_update_resource(resource, cmd, myid, args, updated_fields) + + def test_update_port_device_id_device_owner(self): + resource = 'port' + cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) + myid = 'myid' + args = ['--device-id', 'dev_id', '--device-owner', 'fake', myid] + updatefields = {'device_id': 'dev_id', + 'device_owner': 'fake'} + self._test_update_resource(resource, cmd, myid, args, updatefields) + def test_delete_extra_dhcp_opts_from_port(self): resource = 'port' myid = 'myid' From 2dce00b41dd6d6206566085161dc0958e805a9b9 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Mon, 26 Jan 2015 10:34:17 +0000 Subject: [PATCH 150/845] Updated from global requirements Change-Id: I2131f5f51c723516a4f912ce49f5f25e5308ad15 --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index f6278f11a..cefcc5779 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,11 +6,11 @@ argparse cliff>=1.7.0 # Apache-2.0 iso8601>=0.1.9 netaddr>=0.7.12 -oslo.i18n>=1.0.0 # Apache-2.0 +oslo.i18n>=1.3.0 # Apache-2.0 oslo.serialization>=1.2.0 # Apache-2.0 oslo.utils>=1.2.0 # Apache-2.0 requests>=2.2.0,!=2.4.0 -python-keystoneclient>=0.11.1 +python-keystoneclient>=1.0.0 simplejson>=2.2.0 six>=1.7.0 Babel>=1.3 From 68fc402ee99592e68afe0edce0d231f0bc332c20 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Wed, 28 Jan 2015 02:30:57 +0900 Subject: [PATCH 151/845] Split base function of v2_0.Client into a separate class v2_0.Client now contains both base features of HTTP clients and API methods which talk to Neutron server. We have so many API methods now and it is not easy to find the base features from the class. This commit just split the base features into ClientBase. Change-Id: I00b78569ad09efc59624ee73a6786d77226bda20 --- neutronclient/v2_0/client.py | 349 ++++++++++++++++++----------------- 1 file changed, 178 insertions(+), 171 deletions(-) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 0e8e6dc56..c14da0d4c 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -101,7 +101,7 @@ def with_params(*args, **kwargs): return with_params -class Client(object): +class ClientBase(object): """Client for the OpenStack Neutron v2.0 API. :param string username: Username for authentication. (optional) @@ -152,6 +152,183 @@ class Client(object): """ + # API has no way to report plurals, so we have to hard code them + # This variable should be overridden by a child class. + EXTED_PLURALS = {} + + def __init__(self, **kwargs): + """Initialize a new client for the Neutron v2.0 API.""" + super(ClientBase, self).__init__() + self.retries = kwargs.pop('retries', 0) + self.raise_errors = kwargs.pop('raise_errors', True) + self.httpclient = client.construct_http_client(**kwargs) + self.version = '2.0' + self.format = 'json' + self.action_prefix = "/v%s" % (self.version) + self.retry_interval = 1 + + def _handle_fault_response(self, status_code, response_body): + # Create exception with HTTP status code and message + _logger.debug("Error message: %s", response_body) + # Add deserialized error message to exception arguments + try: + des_error_body = self.deserialize(response_body, status_code) + except Exception: + # If unable to deserialized body it is probably not a + # Neutron error + des_error_body = {'message': response_body} + # Raise the appropriate exception + exception_handler_v20(status_code, des_error_body) + + def do_request(self, method, action, body=None, headers=None, params=None): + # Add format and tenant_id + action += ".%s" % self.format + action = self.action_prefix + action + if type(params) is dict and params: + params = utils.safe_encode_dict(params) + action += '?' + urlparse.urlencode(params, doseq=1) + + if body: + body = self.serialize(body) + + resp, replybody = self.httpclient.do_request( + action, method, body=body, + content_type=self.content_type()) + + status_code = resp.status_code + if status_code in (requests.codes.ok, + requests.codes.created, + requests.codes.accepted, + requests.codes.no_content): + return self.deserialize(replybody, status_code) + else: + if not replybody: + replybody = resp.reason + self._handle_fault_response(status_code, replybody) + + def get_auth_info(self): + return self.httpclient.get_auth_info() + + def serialize(self, data): + """Serializes a dictionary into either XML or JSON. + + A dictionary with a single key can be passed and it can contain any + structure. + """ + if data is None: + return None + elif type(data) is dict: + return serializer.Serializer( + self.get_attr_metadata()).serialize(data, self.content_type()) + else: + raise Exception(_("Unable to serialize object of type = '%s'") % + type(data)) + + def deserialize(self, data, status_code): + """Deserializes an XML or JSON string into a dictionary.""" + if status_code == 204: + return data + return serializer.Serializer(self.get_attr_metadata()).deserialize( + data, self.content_type())['body'] + + def get_attr_metadata(self): + if self.format == 'json': + return {} + old_request_format = self.format + self.format = 'json' + exts = self.list_extensions()['extensions'] + self.format = old_request_format + ns = dict([(ext['alias'], ext['namespace']) for ext in exts]) + self.EXTED_PLURALS.update(constants.PLURALS) + return {'plurals': self.EXTED_PLURALS, + 'xmlns': constants.XML_NS_V20, + constants.EXT_NS: ns} + + def content_type(self, _format=None): + """Returns the mime-type for either 'xml' or 'json'. + + Defaults to the currently set format. + """ + _format = _format or self.format + return "application/%s" % (_format) + + def retry_request(self, method, action, body=None, + headers=None, params=None): + """Call do_request with the default retry configuration. + + Only idempotent requests should retry failed connection attempts. + :raises: ConnectionFailed if the maximum # of retries is exceeded + """ + max_attempts = self.retries + 1 + for i in range(max_attempts): + try: + return self.do_request(method, action, body=body, + headers=headers, params=params) + except exceptions.ConnectionFailed: + # Exception has already been logged by do_request() + if i < self.retries: + _logger.debug('Retrying connection to Neutron service') + time.sleep(self.retry_interval) + elif self.raise_errors: + raise + + if self.retries: + msg = (_("Failed to connect to Neutron server after %d attempts") + % max_attempts) + else: + msg = _("Failed to connect Neutron server") + + raise exceptions.ConnectionFailed(reason=msg) + + def delete(self, action, body=None, headers=None, params=None): + return self.retry_request("DELETE", action, body=body, + headers=headers, params=params) + + def get(self, action, body=None, headers=None, params=None): + return self.retry_request("GET", action, body=body, + headers=headers, params=params) + + def post(self, action, body=None, headers=None, params=None): + # Do not retry POST requests to avoid the orphan objects problem. + return self.do_request("POST", action, body=body, + headers=headers, params=params) + + def put(self, action, body=None, headers=None, params=None): + return self.retry_request("PUT", action, body=body, + headers=headers, params=params) + + def list(self, collection, path, retrieve_all=True, **params): + if retrieve_all: + res = [] + for r in self._pagination(collection, path, **params): + res.extend(r[collection]) + return {collection: res} + else: + return self._pagination(collection, path, **params) + + def _pagination(self, collection, path, **params): + if params.get('page_reverse', False): + linkrel = 'previous' + else: + linkrel = 'next' + next = True + while next: + res = self.get(path, params=params) + yield res + next = False + try: + for link in res['%s_links' % collection]: + if link['rel'] == linkrel: + query_str = urlparse.urlparse(link['href']).query + params = urlparse.parse_qs(query_str) + next = True + break + except KeyError: + break + + +class Client(ClientBase): + networks_path = "/networks" network_path = "/networks/%s" ports_path = "/ports" @@ -257,19 +434,6 @@ class Client(object): 'packet_filters': 'packet_filter', } - def get_attr_metadata(self): - if self.format == 'json': - return {} - old_request_format = self.format - self.format = 'json' - exts = self.list_extensions()['extensions'] - self.format = old_request_format - ns = dict([(ext['alias'], ext['namespace']) for ext in exts]) - self.EXTED_PLURALS.update(constants.PLURALS) - return {'plurals': self.EXTED_PLURALS, - 'xmlns': constants.XML_NS_V20, - constants.EXT_NS: ns} - @APIParamsCall def get_quotas_tenant(self, **_params): """Fetch tenant info in server's context for following quota operation. @@ -1192,160 +1356,3 @@ def show_packet_filter(self, packet_filter_id, **_params): def delete_packet_filter(self, packet_filter_id): """Delete the specified packet filter.""" return self.delete(self.packet_filter_path % packet_filter_id) - - def __init__(self, **kwargs): - """Initialize a new client for the Neutron v2.0 API.""" - super(Client, self).__init__() - self.retries = kwargs.pop('retries', 0) - self.raise_errors = kwargs.pop('raise_errors', True) - self.httpclient = client.construct_http_client(**kwargs) - self.version = '2.0' - self.format = 'json' - self.action_prefix = "/v%s" % (self.version) - self.retry_interval = 1 - - def _handle_fault_response(self, status_code, response_body): - # Create exception with HTTP status code and message - _logger.debug("Error message: %s", response_body) - # Add deserialized error message to exception arguments - try: - des_error_body = self.deserialize(response_body, status_code) - except Exception: - # If unable to deserialized body it is probably not a - # Neutron error - des_error_body = {'message': response_body} - # Raise the appropriate exception - exception_handler_v20(status_code, des_error_body) - - def do_request(self, method, action, body=None, headers=None, params=None): - # Add format and tenant_id - action += ".%s" % self.format - action = self.action_prefix + action - if type(params) is dict and params: - params = utils.safe_encode_dict(params) - action += '?' + urlparse.urlencode(params, doseq=1) - - if body: - body = self.serialize(body) - - resp, replybody = self.httpclient.do_request( - action, method, body=body, - content_type=self.content_type()) - - status_code = resp.status_code - if status_code in (requests.codes.ok, - requests.codes.created, - requests.codes.accepted, - requests.codes.no_content): - return self.deserialize(replybody, status_code) - else: - if not replybody: - replybody = resp.reason - self._handle_fault_response(status_code, replybody) - - def get_auth_info(self): - return self.httpclient.get_auth_info() - - def serialize(self, data): - """Serializes a dictionary into either XML or JSON. - - A dictionary with a single key can be passed and it can contain any - structure. - """ - if data is None: - return None - elif type(data) is dict: - return serializer.Serializer( - self.get_attr_metadata()).serialize(data, self.content_type()) - else: - raise Exception(_("Unable to serialize object of type = '%s'") % - type(data)) - - def deserialize(self, data, status_code): - """Deserializes an XML or JSON string into a dictionary.""" - if status_code == 204: - return data - return serializer.Serializer(self.get_attr_metadata()).deserialize( - data, self.content_type())['body'] - - def content_type(self, _format=None): - """Returns the mime-type for either 'xml' or 'json'. - - Defaults to the currently set format. - """ - _format = _format or self.format - return "application/%s" % (_format) - - def retry_request(self, method, action, body=None, - headers=None, params=None): - """Call do_request with the default retry configuration. - - Only idempotent requests should retry failed connection attempts. - :raises: ConnectionFailed if the maximum # of retries is exceeded - """ - max_attempts = self.retries + 1 - for i in range(max_attempts): - try: - return self.do_request(method, action, body=body, - headers=headers, params=params) - except exceptions.ConnectionFailed: - # Exception has already been logged by do_request() - if i < self.retries: - _logger.debug('Retrying connection to Neutron service') - time.sleep(self.retry_interval) - elif self.raise_errors: - raise - - if self.retries: - msg = (_("Failed to connect to Neutron server after %d attempts") - % max_attempts) - else: - msg = _("Failed to connect Neutron server") - - raise exceptions.ConnectionFailed(reason=msg) - - def delete(self, action, body=None, headers=None, params=None): - return self.retry_request("DELETE", action, body=body, - headers=headers, params=params) - - def get(self, action, body=None, headers=None, params=None): - return self.retry_request("GET", action, body=body, - headers=headers, params=params) - - def post(self, action, body=None, headers=None, params=None): - # Do not retry POST requests to avoid the orphan objects problem. - return self.do_request("POST", action, body=body, - headers=headers, params=params) - - def put(self, action, body=None, headers=None, params=None): - return self.retry_request("PUT", action, body=body, - headers=headers, params=params) - - def list(self, collection, path, retrieve_all=True, **params): - if retrieve_all: - res = [] - for r in self._pagination(collection, path, **params): - res.extend(r[collection]) - return {collection: res} - else: - return self._pagination(collection, path, **params) - - def _pagination(self, collection, path, **params): - if params.get('page_reverse', False): - linkrel = 'previous' - else: - linkrel = 'next' - next = True - while next: - res = self.get(path, params=params) - yield res - next = False - try: - for link in res['%s_links' % collection]: - if link['rel'] == linkrel: - query_str = urlparse.urlparse(link['href']).query - params = urlparse.parse_qs(query_str) - next = True - break - except KeyError: - break From b0923a3e1220d13f7311379cd09d31f49274d9b3 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Wed, 28 Jan 2015 03:02:46 +0900 Subject: [PATCH 152/845] Utility method for boolean argument In the recent commit, True/False of boolean argument became case-insensitve and this code is copy-and-paste'ed in several places. Now there are several number of patches which add explicit arguments for *-update or *-list and the code of boolean argumetns like below will be used more than now. This commit add a utility method to register such boolean opt. parser.add_argument( '--enabled', dest='enabled', metavar='{True,False}', choices=['True', 'true', 'False', 'false'], help=_('Whether to enable or disable this rule.'), default=argparse.SUPPRESS) Change-Id: I9575eeef32154a8b92589c2cc7889803216bddb2 --- neutronclient/common/utils.py | 13 +++++++++++++ neutronclient/neutron/v2_0/fw/firewallrule.py | 10 ++++------ neutronclient/neutron/v2_0/nec/packetfilter.py | 8 ++++---- neutronclient/neutron/v2_0/router.py | 15 +++++---------- 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/neutronclient/common/utils.py b/neutronclient/common/utils.py index d82e34bd8..a847b0f3b 100644 --- a/neutronclient/common/utils.py +++ b/neutronclient/common/utils.py @@ -17,6 +17,7 @@ """Utilities and helper functions.""" +import argparse import logging import os @@ -158,3 +159,15 @@ def _encode_item(item): return (k, _safe_encode_without_obj(v)) return dict(list(map(_encode_item, data.items()))) + + +def add_boolean_argument(parser, name, **kwargs): + for keyword in ('metavar', 'choices'): + kwargs.pop(keyword, None) + default = kwargs.pop('default', argparse.SUPPRESS) + parser.add_argument( + name, + metavar='{True,False}', + choices=['True', 'true', 'False', 'false'], + default=default, + **kwargs) diff --git a/neutronclient/neutron/v2_0/fw/firewallrule.py b/neutronclient/neutron/v2_0/fw/firewallrule.py index 87405d9dd..ecc369bcf 100644 --- a/neutronclient/neutron/v2_0/fw/firewallrule.py +++ b/neutronclient/neutron/v2_0/fw/firewallrule.py @@ -18,6 +18,7 @@ import argparse +from neutronclient.common import utils from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronv20 @@ -95,12 +96,9 @@ def add_known_arguments(self, parser): '--destination-port', help=_('Destination port (integer in [1, 65535] or range in ' 'a:b).')) - parser.add_argument( - '--enabled', - dest='enabled', metavar='{True,False}', - choices=['True', 'true', 'False', 'false'], - help=_('Whether to enable or disable this rule.'), - default=argparse.SUPPRESS) + utils.add_boolean_argument( + parser, '--enabled', dest='enabled', + help=_('Whether to enable or disable this rule.')) parser.add_argument( '--protocol', choices=['tcp', 'udp', 'icmp', 'any'], required=True, diff --git a/neutronclient/neutron/v2_0/nec/packetfilter.py b/neutronclient/neutron/v2_0/nec/packetfilter.py index bb12fce69..dd77cf569 100644 --- a/neutronclient/neutron/v2_0/nec/packetfilter.py +++ b/neutronclient/neutron/v2_0/nec/packetfilter.py @@ -14,6 +14,7 @@ # under the License. from neutronclient.common import exceptions +from neutronclient.common import utils from neutronclient.common import validators from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 @@ -70,9 +71,8 @@ def add_known_arguments(self, parser): dest='admin_state', action='store_false', help=_('Set Admin State Up to false')) else: - parser.add_argument( - '--admin-state', metavar='{True,False}', - choices=['True', 'true', 'False', 'false'], + utils.add_boolean_argument( + parser, '--admin-state', help=_('Set a value of Admin State Up')) parser.add_argument( @@ -207,7 +207,7 @@ def args2body(self, parsed_args): self.validate_fields(parsed_args) body = {} - if parsed_args.admin_state: + if hasattr(parsed_args, 'admin_state'): body['admin_state_up'] = (parsed_args.admin_state == 'True') # fields which allows None diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index b63048073..24d24b5e5 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -21,6 +21,7 @@ from oslo.serialization import jsonutils from neutronclient.common import exceptions +from neutronclient.common import utils from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 @@ -66,17 +67,11 @@ def add_known_arguments(self, parser): parser.add_argument( 'name', metavar='NAME', help=_('Name of router to create.')) - parser.add_argument( - '--distributed', - dest='distributed', metavar='{True,False}', - choices=['True', 'true', 'False', 'false'], - default=argparse.SUPPRESS, + utils.add_boolean_argument( + parser, '--distributed', dest='distributed', help=_('Create a distributed router.')) - parser.add_argument( - '--ha', - dest='ha', metavar='{True,False}', - choices=['True', 'true', 'false', 'False'], - default=argparse.SUPPRESS, + utils.add_boolean_argument( + parser, '--ha', dest='ha', help=_('Create a highly available router.')) def args2body(self, parsed_args): From 6f7cd146726391c5fe37d2e64ea57469319f455a Mon Sep 17 00:00:00 2001 From: Terry Howe Date: Tue, 23 Dec 2014 05:09:42 -0700 Subject: [PATCH 153/845] Reverse order of tests to avoid incompatibility If py26 tests are run first, there is a incompatibility in the .testrepository that breaks several tests. To get around this, you need to run the py33 tests first. This change was made over in the cliff project. Before: (.venv)terry@brat:~/hp/ncli$ rm -rf .testrepository/ (.venv)terry@brat:~/hp/ncli$ tox ... py26: commands succeeded py27: commands succeeded ERROR: py33: commands failed ERROR: py34: commands failed ERROR: pypy: commands failed pep8: commands succeeded After: (.venv)terry@brat:~/hp/ncli$ rm -rf .testrepository/ (.venv)terry@brat:~/hp/ncli$ tox ... py33: commands succeeded py34: commands succeeded py26: commands succeeded py27: commands succeeded pypy: commands succeeded pep8: commands succeeded congratulations :) (.venv)terry@brat:~/hp/ncli$ Closes-Bug: 1415446 Change-Id: Ia0b44cc4a05dd1c668204d8d4dce5381d35373fe --- tox.ini | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 833f7b031..4187fb854 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,6 @@ [tox] -envlist = py26,py27,py33,py34,pypy,pep8 +# py3 first to avoid .testrepository incompatibility +envlist = py33,py34,py26,py27,pypy,pep8 minversion = 1.6 skipsdist = True From 59d7564202c9a5acd57f23a2708291d1d1cb6a54 Mon Sep 17 00:00:00 2001 From: "watanabe.isao" Date: Tue, 27 Jan 2015 09:37:20 +0900 Subject: [PATCH 154/845] Skip None id when getting security_group_ids When getting security_group_ids in securitygroup.py, a None id is included into the list due to the remote group id is Null when the security group rule direction is egress. And this causes the calculation of the number of chunk_size is 1 more than the number it should be. Then the request page size excess the MAX request page size Then the RequestURITooLong exception happens again, and finally the commend fails. Co-Authored-By: Furukawa, Yushiro Change-Id: Ib671cf3ddc6827793c4a86d2d3c8a34957f5dfd3 Related-Bug: 1271462 Closes-Bug: 1415692 --- neutronclient/neutron/v2_0/securitygroup.py | 2 +- neutronclient/tests/unit/test_cli20_securitygroup.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/neutronclient/neutron/v2_0/securitygroup.py b/neutronclient/neutron/v2_0/securitygroup.py index 7f27eaca2..6bfd80d65 100644 --- a/neutronclient/neutron/v2_0/securitygroup.py +++ b/neutronclient/neutron/v2_0/securitygroup.py @@ -134,7 +134,7 @@ def extend_list(self, data, parsed_args): sec_group_ids = set() for rule in data: for key in self.replace_rules: - if key in rule: + if rule.get(key): sec_group_ids.add(rule[key]) sec_group_ids = list(sec_group_ids) diff --git a/neutronclient/tests/unit/test_cli20_securitygroup.py b/neutronclient/tests/unit/test_cli20_securitygroup.py index a90662154..ec18fb8ee 100644 --- a/neutronclient/tests/unit/test_cli20_securitygroup.py +++ b/neutronclient/tests/unit/test_cli20_securitygroup.py @@ -259,6 +259,7 @@ def mox_calls(path, data): data = [{'name': 'default', 'remote_group_id': 'remgroupid%02d' % i} for i in range(10)] + data.append({'name': 'default', 'remote_group_id': None}) self._test_extend_list(mox_calls, data) def test_extend_list_exceed_max_uri_len(self): @@ -285,6 +286,9 @@ def mox_calls(path, data): 'security_group_id': 'secgroupid%02d' % i, 'remote_group_id': 'remgroupid%02d' % i} for i in range(10)] + data.append({'name': 'default', + 'security_group_id': 'secgroupid10', + 'remote_group_id': None}) self._test_extend_list(mox_calls, data) def test_list_security_group_rules_pagination(self): From e1633ed71d323360cec83b5a03be5ac03a6c5ca7 Mon Sep 17 00:00:00 2001 From: Xu Han Peng Date: Mon, 15 Dec 2014 23:24:58 +0800 Subject: [PATCH 155/845] Add ip_version to extra dhcp opts Add ip_version to port-create and port-update option --extra-dhcp-opt. Change-Id: I0976ed0ada0a174c475c725ab8c7eb89b33f61ca Partially-implements: Blueprint extra-dhcp-opts-ipv4-ipv6 --- neutronclient/neutron/v2_0/port.py | 24 +++++----- neutronclient/tests/unit/test_cli20_port.py | 52 +++++++++++++++++++++ 2 files changed, 63 insertions(+), 13 deletions(-) diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index 00ff586ea..d8d70090a 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -163,8 +163,8 @@ def add_arguments_extradhcpopt(self, parser): action='append', dest='extra_dhcp_opts', help=_('Extra dhcp options to be assigned to this port: ' - 'opt_name=,opt_value=. You can ' - 'repeat this option.')) + 'opt_name=,opt_value=,' + 'ip_version={4,6}. You can repeat this option.')) def args2body_extradhcpopt(self, parsed_args, port): ops = [] @@ -174,19 +174,17 @@ def args2body_extradhcpopt(self, parsed_args, port): # both must be thrown out. opt_ele = {} edo_err_msg = _("Invalid --extra-dhcp-opt option, can only be: " - "opt_name=,opt_value=. " + "opt_name=,opt_value=," + "ip_version={4,6}. " "You can repeat this option.") for opt in parsed_args.extra_dhcp_opts: - if opt.split('=')[0] in ['opt_value', 'opt_name']: - opt_ele.update(utils.str2dict(opt)) - if (('opt_name' in opt_ele) and - ('opt_value' in opt_ele)): - if opt_ele['opt_value'] == 'null': - opt_ele['opt_value'] = None - ops.append(opt_ele) - opt_ele = {} - else: - raise exceptions.CommandError(edo_err_msg) + opt_ele.update(utils.str2dict(opt)) + if ('opt_name' in opt_ele and + ('opt_value' in opt_ele or 'ip_version' in opt_ele)): + if opt_ele.get('opt_value') == 'null': + opt_ele['opt_value'] = None + ops.append(opt_ele) + opt_ele = {} else: raise exceptions.CommandError(edo_err_msg) diff --git a/neutronclient/tests/unit/test_cli20_port.py b/neutronclient/tests/unit/test_cli20_port.py index 2b61027b7..b0fb3b53a 100644 --- a/neutronclient/tests/unit/test_cli20_port.py +++ b/neutronclient/tests/unit/test_cli20_port.py @@ -66,6 +66,34 @@ def test_create_port_extra_dhcp_opts_args(self): self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) + def test_create_port_extra_dhcp_opts_args_ip_version(self): + """Create port: netid --extra_dhcp_opt.""" + resource = 'port' + cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + netid = 'netid' + extra_dhcp_opts = [{'opt_name': 'bootfile-name', + 'opt_value': 'pxelinux.0', + 'ip_version': "4"}, + {'opt_name': 'tftp-server', + 'opt_value': '2001:192:168::1', + 'ip_version': "6"}, + {'opt_name': 'server-ip-address', + 'opt_value': '123.123.123.45', + 'ip_version': "4"}] + args = [netid] + for dhcp_opt in extra_dhcp_opts: + args += ['--extra-dhcp-opt', + ('opt_name=%(opt_name)s,opt_value=%(opt_value)s,' + 'ip_version=%(ip_version)s' % + dhcp_opt)] + position_names = ['network_id', 'extra_dhcp_opts'] + position_values = [netid, extra_dhcp_opts] + position_values.extend([netid]) + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + def test_create_port_full(self): """Create port: --mac_address mac --device_id deviceid netid.""" resource = 'port' @@ -395,6 +423,30 @@ def test_update_port_device_id_device_owner(self): 'device_owner': 'fake'} self._test_update_resource(resource, cmd, myid, args, updatefields) + def test_update_port_extra_dhcp_opts_ip_version(self): + """Update port: myid --extra_dhcp_opt.""" + resource = 'port' + myid = 'myid' + args = [myid, + '--extra-dhcp-opt', + "opt_name=bootfile-name,opt_value=pxelinux.0,ip_version=4", + '--extra-dhcp-opt', + "opt_name=tftp-server,opt_value=2001:192:168::1,ip_version=6", + '--extra-dhcp-opt', + "opt_name=server-ip-address,opt_value=null,ip_version=4" + ] + updatedfields = {'extra_dhcp_opts': [{'opt_name': 'bootfile-name', + 'opt_value': 'pxelinux.0', + 'ip_version': '4'}, + {'opt_name': 'tftp-server', + 'opt_value': '2001:192:168::1', + 'ip_version': '6'}, + {'opt_name': 'server-ip-address', + 'opt_value': None, + 'ip_version': '4'}]} + cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, myid, args, updatedfields) + def test_delete_extra_dhcp_opts_from_port(self): resource = 'port' myid = 'myid' From 3d6769c0bfa4f7dce45864f76d8e350cac4d96a9 Mon Sep 17 00:00:00 2001 From: Elena Ezhova Date: Mon, 2 Feb 2015 16:41:21 +0300 Subject: [PATCH 156/845] Fix typo in test_cli20_agentschedulers filename Change-Id: Id7a489df5460038aca8ebf80f782b3b4f675bdba --- ...test_cli20_agenschedulers.py => test_cli20_agentschedulers.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename neutronclient/tests/unit/{test_cli20_agenschedulers.py => test_cli20_agentschedulers.py} (100%) diff --git a/neutronclient/tests/unit/test_cli20_agenschedulers.py b/neutronclient/tests/unit/test_cli20_agentschedulers.py similarity index 100% rename from neutronclient/tests/unit/test_cli20_agenschedulers.py rename to neutronclient/tests/unit/test_cli20_agentschedulers.py From 0be3b622c656a80709f015e6c250aa1917da39e9 Mon Sep 17 00:00:00 2001 From: Craig Tracey Date: Sat, 2 Aug 2014 11:26:59 -0400 Subject: [PATCH 157/845] Implement LBaaS object model v2 This change implements the LBaaS v2 object model changes necessary for python-neutronclient. It includes new objects loadbalancer and listener, as well as new implementations for pool, member, and healthmonitor. While many constructs are the same as in v1, many are not, and have therefore been namespaced under lb/v2. This client now supports both the v1 and v2 implementations simultaneously. v1 commands retain their original form: neutron lb-- and v2 commands take the form: neutron lbaas-- Co-Authored-By: Michael Johnson Change-Id: I970b1de39d0dd8872459a584dd1f5c9110796ac9 Partially-implements: blueprint lbaas-api-and-objmodel-improvement DocImpact --- neutronclient/neutron/v2_0/__init__.py | 17 +- neutronclient/neutron/v2_0/lb/member.py | 6 +- neutronclient/neutron/v2_0/lb/v2/__init__.py | 0 .../neutron/v2_0/lb/v2/healthmonitor.py | 118 +++++++++++++ neutronclient/neutron/v2_0/lb/v2/listener.py | 114 ++++++++++++ .../neutron/v2_0/lb/v2/loadbalancer.py | 93 ++++++++++ neutronclient/neutron/v2_0/lb/v2/member.py | 143 +++++++++++++++ neutronclient/neutron/v2_0/lb/v2/pool.py | 134 ++++++++++++++ neutronclient/shell.py | 30 ++++ neutronclient/tests/unit/lb/v2/__init__.py | 0 .../unit/lb/v2/test_cli20_healthmonitor.py | 154 ++++++++++++++++ .../tests/unit/lb/v2/test_cli20_listener.py | 137 ++++++++++++++ .../unit/lb/v2/test_cli20_loadbalancer.py | 137 ++++++++++++++ .../tests/unit/lb/v2/test_cli20_member.py | 159 +++++++++++++++++ .../tests/unit/lb/v2/test_cli20_pool.py | 149 ++++++++++++++++ neutronclient/tests/unit/test_cli20.py | 4 +- neutronclient/v2_0/client.py | 167 ++++++++++++++++++ 17 files changed, 1557 insertions(+), 5 deletions(-) create mode 100644 neutronclient/neutron/v2_0/lb/v2/__init__.py create mode 100644 neutronclient/neutron/v2_0/lb/v2/healthmonitor.py create mode 100644 neutronclient/neutron/v2_0/lb/v2/listener.py create mode 100644 neutronclient/neutron/v2_0/lb/v2/loadbalancer.py create mode 100644 neutronclient/neutron/v2_0/lb/v2/member.py create mode 100644 neutronclient/neutron/v2_0/lb/v2/pool.py create mode 100644 neutronclient/tests/unit/lb/v2/__init__.py create mode 100644 neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py create mode 100644 neutronclient/tests/unit/lb/v2/test_cli20_listener.py create mode 100644 neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py create mode 100644 neutronclient/tests/unit/lb/v2/test_cli20_member.py create mode 100644 neutronclient/tests/unit/lb/v2/test_cli20_pool.py diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index 2cda5d6be..8725b4a94 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -411,7 +411,11 @@ def get_parser(self, prog_name): return parser + def cleanup_output_data(self, data): + pass + def format_output_data(self, data): + self.cleanup_output_data(data) # Modify data to make it more readable if self.resource in data: for k, v in six.iteritems(data[self.resource]): @@ -429,6 +433,9 @@ def format_output_data(self, data): def add_known_arguments(self, parser): pass + def set_extra_attrs(self, parsed_args): + pass + def args2body(self, parsed_args): return {} @@ -452,6 +459,7 @@ def get_parser(self, prog_name): def get_data(self, parsed_args): self.log.debug('get_data(%s)' % parsed_args) + self.set_extra_attrs(parsed_args) neutron_client = self.get_client() neutron_client.format = parsed_args.request_format _extra_values = parse_args_to_dict(self.values_specs) @@ -492,6 +500,7 @@ def get_parser(self, prog_name): def run(self, parsed_args): self.log.debug('run(%s)', parsed_args) + self.set_extra_attrs(parsed_args) neutron_client = self.get_client() neutron_client.format = parsed_args.request_format _extra_values = parse_args_to_dict(self.values_specs) @@ -509,7 +518,7 @@ def run(self, parsed_args): if self.allow_names: _id = find_resourceid_by_name_or_id( neutron_client, self.resource, parsed_args.id, - cmd_resource=self.cmd_resource) + cmd_resource=self.cmd_resource, parent_id=self.parent_id) else: _id = find_resourceid_by_id( neutron_client, self.resource, parsed_args.id, @@ -542,10 +551,12 @@ def get_parser(self, prog_name): parser.add_argument( 'id', metavar=self.resource.upper(), help=help_str % self.resource) + self.add_known_arguments(parser) return parser def run(self, parsed_args): self.log.debug('run(%s)', parsed_args) + self.set_extra_attrs(parsed_args) neutron_client = self.get_client() neutron_client.format = parsed_args.request_format obj_deleter = getattr(neutron_client, @@ -589,6 +600,7 @@ def get_parser(self, prog_name): add_pagination_argument(parser) if self.sorting_support: add_sorting_argument(parser) + self.add_known_arguments(parser) return parser def args2search_opts(self, parsed_args): @@ -674,6 +686,7 @@ def setup_columns(self, info, parsed_args): def get_data(self, parsed_args): self.log.debug('get_data(%s)', parsed_args) + self.set_extra_attrs(parsed_args) data = self.retrieve_list(parsed_args) self.extend_list(data, parsed_args) return self.setup_columns(data, parsed_args) @@ -696,10 +709,12 @@ def get_parser(self, prog_name): parser.add_argument( 'id', metavar=self.resource.upper(), help=help_str % self.resource) + self.add_known_arguments(parser) return parser def get_data(self, parsed_args): self.log.debug('get_data(%s)', parsed_args) + self.set_extra_attrs(parsed_args) neutron_client = self.get_client() neutron_client.format = parsed_args.request_format diff --git a/neutronclient/neutron/v2_0/lb/member.py b/neutronclient/neutron/v2_0/lb/member.py index aeed319f1..6effb3013 100644 --- a/neutronclient/neutron/v2_0/lb/member.py +++ b/neutronclient/neutron/v2_0/lb/member.py @@ -44,9 +44,6 @@ class CreateMember(neutronV20.CreateCommand): resource = 'member' def add_known_arguments(self, parser): - parser.add_argument( - 'pool_id', metavar='POOL', - help=_('Pool ID or name this vip belongs to.')) parser.add_argument( '--admin-state-down', dest='admin_state', action='store_false', @@ -63,6 +60,9 @@ def add_known_arguments(self, parser): required=True, help=_('Port on which the pool member listens for requests or ' 'connections.')) + parser.add_argument( + 'pool_id', metavar='POOL', + help=_('Pool ID or name this vip belongs to.')) def args2body(self, parsed_args): _pool_id = neutronV20.find_resourceid_by_name_or_id( diff --git a/neutronclient/neutron/v2_0/lb/v2/__init__.py b/neutronclient/neutron/v2_0/lb/v2/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/neutron/v2_0/lb/v2/healthmonitor.py b/neutronclient/neutron/v2_0/lb/v2/healthmonitor.py new file mode 100644 index 000000000..23136f74f --- /dev/null +++ b/neutronclient/neutron/v2_0/lb/v2/healthmonitor.py @@ -0,0 +1,118 @@ +# Copyright 2013 Mirantis Inc. +# Copyright 2014 Blue Box Group, Inc. +# All Rights Reserved +# +# Author: Ilya Shakhat, Mirantis Inc. +# Author: Craig Tracey +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from neutronclient.i18n import _ +from neutronclient.neutron import v2_0 as neutronV20 + + +class ListHealthMonitor(neutronV20.ListCommand): + """LBaaS v2 List healthmonitors that belong to a given tenant.""" + + resource = 'healthmonitor' + shadow_resource = 'lbaas_healthmonitor' + list_columns = ['id', 'type', 'admin_state_up'] + pagination_support = True + sorting_support = True + + +class ShowHealthMonitor(neutronV20.ShowCommand): + """LBaaS v2 Show information of a given healthmonitor.""" + + resource = 'healthmonitor' + shadow_resource = 'lbaas_healthmonitor' + + +class CreateHealthMonitor(neutronV20.CreateCommand): + """LBaaS v2 Create a healthmonitor.""" + + resource = 'healthmonitor' + shadow_resource = 'lbaas_healthmonitor' + + def add_known_arguments(self, parser): + parser.add_argument( + '--admin-state-down', + dest='admin_state', action='store_false', + help=_('Set admin state up to false.')) + parser.add_argument( + '--expected-codes', + help=_('The list of HTTP status codes expected in ' + 'response from the member to declare it healthy. This ' + 'attribute can contain one value, ' + 'or a list of values separated by comma, ' + 'or a range of values (e.g. "200-299"). If this attribute ' + 'is not specified, it defaults to "200".')) + parser.add_argument( + '--http-method', + help=_('The HTTP method used for requests by the monitor of type ' + 'HTTP.')) + parser.add_argument( + '--url-path', + help=_('The HTTP path used in the HTTP request used by the monitor' + ' to test a member health. This must be a string ' + 'beginning with a / (forward slash).')) + parser.add_argument( + '--delay', + required=True, + help=_('The time in seconds between sending probes to members.')) + parser.add_argument( + '--max-retries', + required=True, + help=_('Number of permissible connection failures before changing ' + 'the member status to INACTIVE. [1..10].')) + parser.add_argument( + '--timeout', + required=True, + help=_('Maximum number of seconds for a monitor to wait for a ' + 'connection to be established before it times out. The ' + 'value must be less than the delay value.')) + parser.add_argument( + '--type', + required=True, choices=['PING', 'TCP', 'HTTP', 'HTTPS'], + help=_('One of the predefined health monitor types.')) + + def args2body(self, parsed_args): + body = { + self.resource: { + 'admin_state_up': parsed_args.admin_state, + 'delay': parsed_args.delay, + 'max_retries': parsed_args.max_retries, + 'timeout': parsed_args.timeout, + 'type': parsed_args.type, + }, + } + neutronV20.update_dict(parsed_args, body[self.resource], + ['expected_codes', 'http_method', 'url_path', + 'tenant_id']) + return body + + +class UpdateHealthMonitor(neutronV20.UpdateCommand): + """LBaaS v2 Update a given healthmonitor.""" + + resource = 'healthmonitor' + shadow_resource = 'lbaas_healthmonitor' + allow_names = False + + +class DeleteHealthMonitor(neutronV20.DeleteCommand): + """LBaaS v2 Delete a given healthmonitor.""" + + resource = 'healthmonitor' + shadow_resource = 'lbaas_healthmonitor' diff --git a/neutronclient/neutron/v2_0/lb/v2/listener.py b/neutronclient/neutron/v2_0/lb/v2/listener.py new file mode 100644 index 000000000..6b398ffaf --- /dev/null +++ b/neutronclient/neutron/v2_0/lb/v2/listener.py @@ -0,0 +1,114 @@ +# Copyright 2014 Blue Box Group, Inc. +# Copyright 2015 Hewlett-Packard Development Company, L.P. +# All Rights Reserved +# +# Author: Craig Tracey +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from neutronclient.i18n import _ +from neutronclient.neutron import v2_0 as neutronV20 + + +def _get_loadbalancer_id(client, lb_id_or_name): + return neutronV20.find_resourceid_by_name_or_id( + client, + 'loadbalancer', + lb_id_or_name, + cmd_resource='lbaas_loadbalancer') + + +class ListListener(neutronV20.ListCommand): + """LBaaS v2 List listeners that belong to a given tenant.""" + + resource = 'listener' + list_columns = ['id', 'default_pool_id', 'name', 'protocol', + 'protocol_port', 'admin_state_up', 'status'] + pagination_support = True + sorting_support = True + + +class ShowListener(neutronV20.ShowCommand): + """LBaaS v2 Show information of a given listener.""" + + resource = 'listener' + + +class CreateListener(neutronV20.CreateCommand): + """LBaaS v2 Create a listener.""" + + resource = 'listener' + + def add_known_arguments(self, parser): + parser.add_argument( + '--admin-state-down', + dest='admin_state', action='store_false', + help=_('Set admin state up to false.')) + parser.add_argument( + '--connection-limit', + help=_('The maximum number of connections per second allowed for ' + 'the vip. Positive integer or -1 for unlimited (default).')) + parser.add_argument( + '--description', + help=_('Description of the listener.')) + parser.add_argument( + '--name', + help=_('The name of the listener.')) + parser.add_argument( + '--loadbalancer', + required=True, + metavar='LOADBALANCER', + help=_('ID or name of the load balancer.')) + parser.add_argument( + '--protocol', + required=True, + choices=['TCP', 'HTTP', 'HTTPS'], + help=_('Protocol for the listener.')) + parser.add_argument( + '--protocol-port', + dest='protocol_port', required=True, + metavar='PORT', + help=_('Protocol port for the listener.')) + + def args2body(self, parsed_args): + if parsed_args.loadbalancer: + parsed_args.loadbalancer = _get_loadbalancer_id( + self.get_client(), + parsed_args.loadbalancer) + body = { + self.resource: { + 'loadbalancer_id': parsed_args.loadbalancer, + 'protocol': parsed_args.protocol, + 'protocol_port': parsed_args.protocol_port, + 'admin_state_up': parsed_args.admin_state, + }, + } + + neutronV20.update_dict(parsed_args, body[self.resource], + ['connection-limit', 'description', + 'loadbalancer_id', 'name']) + return body + + +class UpdateListener(neutronV20.UpdateCommand): + """LBaaS v2 Update a given listener.""" + + resource = 'listener' + allow_names = False + + +class DeleteListener(neutronV20.DeleteCommand): + """LBaaS v2 Delete a given listener.""" + + resource = 'listener' diff --git a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py new file mode 100644 index 000000000..576c5fa5a --- /dev/null +++ b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py @@ -0,0 +1,93 @@ +# Copyright 2014 Blue Box Group, Inc. +# Copyright 2015 Hewlett-Packard Development Company, L.P. +# All Rights Reserved +# +# Author: Craig Tracey +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from neutronclient.i18n import _ +from neutronclient.neutron import v2_0 as neutronV20 + + +class ListLoadBalancer(neutronV20.ListCommand): + """LBaaS v2 List loadbalancers that belong to a given tenant.""" + + resource = 'loadbalancer' + list_columns = ['id', 'name', 'vip_address', + 'provisioning_status', 'provider'] + pagination_support = True + sorting_support = True + + +class ShowLoadBalancer(neutronV20.ShowCommand): + """LBaaS v2 Show information of a given loadbalancer.""" + + resource = 'loadbalancer' + + +class CreateLoadBalancer(neutronV20.CreateCommand): + """LBaaS v2 Create a loadbalancer.""" + + resource = 'loadbalancer' + allow_names = True + + def add_known_arguments(self, parser): + parser.add_argument( + '--description', + help=_('Description of the load balancer.')) + parser.add_argument( + '--admin-state-down', + dest='admin_state', action='store_false', + help=_('Set admin state up to false.')) + parser.add_argument( + '--name', metavar='NAME', + help=_('Name of the load balancer.')) + parser.add_argument( + '--provider', + help=_('Provider name of load balancer service.')) + parser.add_argument( + '--vip-address', + help=_('VIP address for the load balancer.')) + parser.add_argument( + 'vip_subnet', metavar='VIP_SUBNET', + help=_('Load balancer VIP subnet.')) + + def args2body(self, parsed_args): + _subnet_id = neutronV20.find_resourceid_by_name_or_id( + self.get_client(), 'subnet', parsed_args.vip_subnet) + body = { + self.resource: { + 'name': parsed_args.name, + 'vip_subnet_id': _subnet_id, + 'admin_state_up': parsed_args.admin_state, + }, + } + neutronV20.update_dict(parsed_args, body[self.resource], + ['description', 'provider', 'vip_address']) + return body + + +class UpdateLoadBalancer(neutronV20.UpdateCommand): + """LBaaS v2 Update a given loadbalancer.""" + + resource = 'loadbalancer' + allow_names = True + + +class DeleteLoadBalancer(neutronV20.DeleteCommand): + """LBaaS v2 Delete a given loadbalancer.""" + + resource = 'loadbalancer' + allow_names = True diff --git a/neutronclient/neutron/v2_0/lb/v2/member.py b/neutronclient/neutron/v2_0/lb/v2/member.py new file mode 100644 index 000000000..4709f65ba --- /dev/null +++ b/neutronclient/neutron/v2_0/lb/v2/member.py @@ -0,0 +1,143 @@ +# Copyright 2013 Mirantis Inc. +# Copyright 2014 Blue Box Group, Inc. +# Copyright 2015 Hewlett-Packard Development Company, L.P. +# All Rights Reserved +# +# Author: Ilya Shakhat, Mirantis Inc. +# Author: Craig Tracey +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from neutronclient.i18n import _ +from neutronclient.neutron import v2_0 as neutronV20 + + +def _get_pool_id(client, pool_id_or_name): + return neutronV20.find_resourceid_by_name_or_id(client, 'pool', + pool_id_or_name, + cmd_resource='lbaas_pool') + + +class LbaasMemberMixin(object): + + def set_extra_attrs(self, parsed_args): + self.parent_id = _get_pool_id(self.get_client(), parsed_args.pool) + + def add_known_arguments(self, parser): + parser.add_argument( + 'pool', metavar='POOL', + help=_('ID or name of the pool that this member belongs to.')) + + +class ListMember(LbaasMemberMixin, neutronV20.ListCommand): + """LBaaS v2 List members that belong to a given tenant.""" + + resource = 'member' + shadow_resource = 'lbaas_member' + list_columns = [ + 'id', 'address', 'protocol_port', 'weight', + 'subnet_id', 'admin_state_up', 'status' + ] + pagination_support = True + sorting_support = True + + +class ShowMember(LbaasMemberMixin, neutronV20.ShowCommand): + """LBaaS v2 Show information of a given member.""" + + resource = 'member' + shadow_resource = 'lbaas_member' + + +class CreateMember(neutronV20.CreateCommand): + """LBaaS v2 Create a member.""" + + resource = 'member' + shadow_resource = 'lbaas_member' + + def add_known_arguments(self, parser): + parser.add_argument( + '--admin-state-down', + dest='admin_state', action='store_false', + help=_('Set admin state up to false')) + parser.add_argument( + '--weight', + help=_('Weight of member in the pool (default:1, [0..256]).')) + parser.add_argument( + '--subnet', + required=True, + help=_('Subnet ID or name for the member.')) + parser.add_argument( + '--address', + required=True, + help=_('IP address of the pool member in the pool.')) + parser.add_argument( + '--protocol-port', + required=True, + help=_('Port on which the pool member listens for requests or ' + 'connections.')) + parser.add_argument( + 'pool', metavar='POOL', + help=_('ID or name of the pool that this member belongs to.')) + + def args2body(self, parsed_args): + self.parent_id = _get_pool_id(self.get_client(), parsed_args.pool) + _subnet_id = neutronV20.find_resourceid_by_name_or_id( + self.get_client(), 'subnet', parsed_args.subnet) + body = { + self.resource: { + 'subnet_id': _subnet_id, + 'admin_state_up': parsed_args.admin_state, + 'protocol_port': parsed_args.protocol_port, + 'address': parsed_args.address, + }, + } + neutronV20.update_dict(parsed_args, body[self.resource], + ['weight', 'subnet_id']) + return body + + +class UpdateMember(neutronV20.UpdateCommand): + """LBaaS v2 Update a given member.""" + + resource = 'member' + shadow_resource = 'lbaas_member' + + def add_known_arguments(self, parser): + parser.add_argument( + '--admin-state-down', + dest='admin_state', action='store_false', + help=_('Set admin state up to false')) + parser.add_argument( + '--weight', + help=_('Weight of member in the pool (default:1, [0..256])')) + parser.add_argument( + 'pool', metavar='POOL', + help=_('ID or name of the pool that this member belongs to')) + + def args2body(self, parsed_args): + self.parent_id = _get_pool_id(self.get_client(), parsed_args.pool) + body = { + self.resource: {} + } + neutronV20.update_dict(parsed_args, body[self.resource], + ['admin_state_up', 'weight']) + return body + + +class DeleteMember(LbaasMemberMixin, neutronV20.DeleteCommand): + """LBaaS v2 Delete a given member.""" + + resource = 'member' + shadow_resource = 'lbaas_member' diff --git a/neutronclient/neutron/v2_0/lb/v2/pool.py b/neutronclient/neutron/v2_0/lb/v2/pool.py new file mode 100644 index 000000000..11644be45 --- /dev/null +++ b/neutronclient/neutron/v2_0/lb/v2/pool.py @@ -0,0 +1,134 @@ +# Copyright 2013 Mirantis Inc. +# Copyright 2014 Blue Box Group, Inc. +# Copyright 2015 Hewlett-Packard Development Company, L.P. +# All Rights Reserved +# +# Author: Ilya Shakhat, Mirantis Inc. +# Author: Craig Tracey +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from neutronclient.common import exceptions +from neutronclient.i18n import _ +from neutronclient.neutron import v2_0 as neutronV20 + + +def _parse_persistence(parsed_args): + persistence = None + if parsed_args.session_persistence: + parts = parsed_args.session_persistence.split(':') + if not len(parts) == 2: + raise exceptions.CommandError('Incorrect --session-persistence' + ' format. Format is :') + persistence = {'type': parts[0], 'cookie_name': parts[1]} + return persistence + + +class ListPool(neutronV20.ListCommand): + """LBaaS v2 List pools that belong to a given tenant.""" + + resource = 'pool' + shadow_resource = 'lbaas_pool' + list_columns = ['id', 'name', 'lb_method', 'protocol', + 'admin_state_up'] + pagination_support = True + sorting_support = True + + +class ShowPool(neutronV20.ShowCommand): + """LBaaS v2 Show information of a given pool.""" + + resource = 'pool' + shadow_resource = 'lbaas_pool' + + def cleanup_output_data(self, data): + if 'members' not in data['pool']: + return [] + member_info = [] + for member in data['pool']['members']: + member_info.append(member['id']) + data['pool']['members'] = member_info + + +class CreatePool(neutronV20.CreateCommand): + """LBaaS v2 Create a pool.""" + + resource = 'pool' + shadow_resource = 'lbaas_pool' + + def add_known_arguments(self, parser): + parser.add_argument( + '--admin-state-down', + dest='admin_state', action='store_false', + help=_('Set admin state up to false.')) + parser.add_argument( + '--description', + help=_('Description of the pool.')) + parser.add_argument( + '--healthmonitor-id', + help=_('ID of the health monitor to use.')) + parser.add_argument( + '--session-persistence', metavar='TYPE:VALUE', + help=_('The type of session persistence to use.')) + parser.add_argument( + '--lb-algorithm', + required=True, + choices=['ROUND_ROBIN', 'LEAST_CONNECTIONS', 'SOURCE_IP'], + help=_('The algorithm used to distribute load between the members ' + 'of the pool.')) + parser.add_argument( + '--listener', + required=True, + help=_('The listener to associate with the pool')) + parser.add_argument( + '--protocol', + required=True, + choices=['HTTP', 'HTTPS', 'TCP'], + help=_('Protocol for balancing.')) + parser.add_argument( + 'name', metavar='NAME', + help=_('The name of the pool.')) + + def args2body(self, parsed_args): + if parsed_args.session_persistence: + parsed_args.session_persistence = _parse_persistence(parsed_args) + _listener_id = neutronV20.find_resourceid_by_name_or_id( + self.get_client(), 'listener', parsed_args.listener) + body = { + self.resource: { + 'name': parsed_args.name, + 'admin_state_up': parsed_args.admin_state, + 'protocol': parsed_args.protocol, + 'lb_algorithm': parsed_args.lb_algorithm, + 'listener_id': _listener_id, + }, + } + neutronV20.update_dict(parsed_args, body[self.resource], + ['description', 'healthmonitor_id', + 'session_persistence']) + return body + + +class UpdatePool(neutronV20.UpdateCommand): + """LBaaS v2 Update a given pool.""" + + resource = 'pool' + shadow_resource = 'lbaas_pool' + + +class DeletePool(neutronV20.DeleteCommand): + """LBaaS v2 Delete a given pool.""" + + resource = 'pool' + shadow_resource = 'lbaas_pool' diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 76036a008..f892ba20c 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -53,6 +53,11 @@ from neutronclient.neutron.v2_0.lb import healthmonitor as lb_healthmonitor from neutronclient.neutron.v2_0.lb import member as lb_member from neutronclient.neutron.v2_0.lb import pool as lb_pool +from neutronclient.neutron.v2_0.lb.v2 import healthmonitor as lbaas_healthmon +from neutronclient.neutron.v2_0.lb.v2 import listener as lbaas_listener +from neutronclient.neutron.v2_0.lb.v2 import loadbalancer as lbaas_loadbalancer +from neutronclient.neutron.v2_0.lb.v2 import member as lbaas_member +from neutronclient.neutron.v2_0.lb.v2 import pool as lbaas_pool from neutronclient.neutron.v2_0.lb import vip as lb_vip from neutronclient.neutron.v2_0 import metering from neutronclient.neutron.v2_0.nec import packetfilter @@ -170,6 +175,31 @@ class BashCompletionCommand(openstack_command.OpenStackCommand): 'security-group-rule-show': securitygroup.ShowSecurityGroupRule, 'security-group-rule-create': securitygroup.CreateSecurityGroupRule, 'security-group-rule-delete': securitygroup.DeleteSecurityGroupRule, + 'lbaas-loadbalancer-list': lbaas_loadbalancer.ListLoadBalancer, + 'lbaas-loadbalancer-show': lbaas_loadbalancer.ShowLoadBalancer, + 'lbaas-loadbalancer-create': lbaas_loadbalancer.CreateLoadBalancer, + 'lbaas-loadbalancer-update': lbaas_loadbalancer.UpdateLoadBalancer, + 'lbaas-loadbalancer-delete': lbaas_loadbalancer.DeleteLoadBalancer, + 'lbaas-listener-list': lbaas_listener.ListListener, + 'lbaas-listener-show': lbaas_listener.ShowListener, + 'lbaas-listener-create': lbaas_listener.CreateListener, + 'lbaas-listener-update': lbaas_listener.UpdateListener, + 'lbaas-listener-delete': lbaas_listener.DeleteListener, + 'lbaas-pool-list': lbaas_pool.ListPool, + 'lbaas-pool-show': lbaas_pool.ShowPool, + 'lbaas-pool-create': lbaas_pool.CreatePool, + 'lbaas-pool-update': lbaas_pool.UpdatePool, + 'lbaas-pool-delete': lbaas_pool.DeletePool, + 'lbaas-healthmonitor-list': lbaas_healthmon.ListHealthMonitor, + 'lbaas-healthmonitor-show': lbaas_healthmon.ShowHealthMonitor, + 'lbaas-healthmonitor-create': lbaas_healthmon.CreateHealthMonitor, + 'lbaas-healthmonitor-update': lbaas_healthmon.UpdateHealthMonitor, + 'lbaas-healthmonitor-delete': lbaas_healthmon.DeleteHealthMonitor, + 'lbaas-member-list': lbaas_member.ListMember, + 'lbaas-member-show': lbaas_member.ShowMember, + 'lbaas-member-create': lbaas_member.CreateMember, + 'lbaas-member-update': lbaas_member.UpdateMember, + 'lbaas-member-delete': lbaas_member.DeleteMember, 'lb-vip-list': lb_vip.ListVip, 'lb-vip-show': lb_vip.ShowVip, 'lb-vip-create': lb_vip.CreateVip, diff --git a/neutronclient/tests/unit/lb/v2/__init__.py b/neutronclient/tests/unit/lb/v2/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py b/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py new file mode 100644 index 000000000..20af12762 --- /dev/null +++ b/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py @@ -0,0 +1,154 @@ +# Copyright 2014 Blue Box Group, 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. +# +# @author: Craig Tracey +# + +import sys + +from neutronclient.neutron.v2_0.lb.v2 import healthmonitor +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20LbHealthMonitorJSON(test_cli20.CLITestV20Base): + + def test_create_healthmonitor_with_mandatory_params(self): + """lbaas-healthmonitor-create with mandatory params only.""" + resource = 'healthmonitor' + cmd_resource = 'lbaas_healthmonitor' + cmd = healthmonitor.CreateHealthMonitor(test_cli20.MyApp(sys.stdout), + None) + my_id = 'my-id' + type = 'PING' + max_retries = '3' + delay = '10' + timeout = '60' + args = ['--type', type, '--max-retries', max_retries, + '--delay', delay, '--timeout', timeout] + position_names = ['type', 'max_retries', 'delay', 'timeout'] + position_values = [type, max_retries, delay, timeout] + self._test_create_resource(resource, cmd, '', my_id, args, + position_names, position_values, + cmd_resource=cmd_resource) + + def test_create_healthmonitor_with_all_params(self): + """lbaas-healthmonitor-create with all params set.""" + resource = 'healthmonitor' + cmd_resource = 'lbaas_healthmonitor' + cmd = healthmonitor.CreateHealthMonitor(test_cli20.MyApp(sys.stdout), + None) + my_id = 'my-id' + type = 'PING' + max_retries = '3' + delay = '10' + timeout = '60' + http_method = 'GET' + expected_codes = '201' + url_path = '/somepath' + args = ['--admin-state-down', '--http-method', http_method, + '--expected-codes', expected_codes, '--url-path', url_path, + '--type', type, '--max-retries', max_retries, + '--delay', delay, '--timeout', timeout] + position_names = ['admin_state_up', 'http_method', 'expected_codes', + 'url_path', 'type', 'max_retries', 'delay', + 'timeout'] + position_values = [False, http_method, expected_codes, url_path, + type, max_retries, delay, timeout] + self._test_create_resource(resource, cmd, '', my_id, args, + position_names, position_values, + cmd_resource=cmd_resource) + + def test_list_healthmonitors(self): + """lbaas-healthmonitor-list.""" + resources = 'healthmonitors' + cmd_resources = 'lbaas_healthmonitors' + cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources(resources, cmd, True, + cmd_resources=cmd_resources) + + def test_list_healthmonitors_pagination(self): + """lbaas-healthmonitor-list with pagination.""" + resources = 'healthmonitors' + cmd_resources = 'lbaas_healthmonitors' + cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources_with_pagination(resources, cmd, + cmd_resources=cmd_resources) + + def test_list_healthmonitors_sort(self): + """lbaas-healthmonitor-list --sort-key id --sort-key asc.""" + resources = 'healthmonitors' + cmd_resources = 'lbaas_healthmonitors' + cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources(resources, cmd, True, + cmd_resources=cmd_resources) + + def test_list_healthmonitors_limit(self): + """lbaas-healthmonitor-list -P.""" + resources = 'healthmonitors' + cmd_resources = 'lbaas_healthmonitors' + cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources(resources, cmd, page_size=1000, + cmd_resources=cmd_resources) + + def test_show_healthmonitor_id(self): + """lbaas-healthmonitor-show test_id.""" + resource = 'healthmonitor' + cmd_resource = 'lbaas_healthmonitor' + cmd = healthmonitor.ShowHealthMonitor(test_cli20.MyApp(sys.stdout), + None) + args = ['--fields', 'id', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, args, ['id'], + cmd_resource=cmd_resource) + + def test_show_healthmonitor_id_name(self): + """lbaas-healthmonitor-show.""" + resource = 'healthmonitor' + cmd_resource = 'lbaas_healthmonitor' + cmd = healthmonitor.ShowHealthMonitor(test_cli20.MyApp(sys.stdout), + None) + args = ['--fields', 'id', '--fields', 'name', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, + args, ['id', 'name'], + cmd_resource=cmd_resource) + + def test_update_healthmonitor(self): + """lbaas-healthmonitor-update myid --name newname.""" + resource = 'healthmonitor' + cmd_resource = 'lbaas_healthmonitor' + cmd = healthmonitor.UpdateHealthMonitor(test_cli20.MyApp(sys.stdout), + None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--name', 'newname'], + {'name': 'newname', }, + cmd_resource=cmd_resource) + + def test_delete_healthmonitor(self): + """lbaas-healthmonitor-delete my-id.""" + resource = 'healthmonitor' + cmd_resource = 'lbaas_healthmonitor' + cmd = healthmonitor.DeleteHealthMonitor(test_cli20.MyApp(sys.stdout), + None) + my_id = 'my-id' + args = [my_id] + self._test_delete_resource(resource, cmd, my_id, args, + cmd_resource=cmd_resource) + + +class CLITestV20LbHealthMonitorXML(CLITestV20LbHealthMonitorJSON): + format = 'xml' diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_listener.py b/neutronclient/tests/unit/lb/v2/test_cli20_listener.py new file mode 100644 index 000000000..fc0a062b6 --- /dev/null +++ b/neutronclient/tests/unit/lb/v2/test_cli20_listener.py @@ -0,0 +1,137 @@ +# Copyright 2014 Blue Box Group, 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. +# +# @author: Craig Tracey +# + +import sys + +from neutronclient.neutron.v2_0.lb.v2 import listener +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20LbListenerJSON(test_cli20.CLITestV20Base): + + def test_create_listener_with_mandatory_params(self): + """lbaas-listener-create with mandatory params only.""" + resource = 'listener' + cmd_resource = 'lbaas_listener' + cmd = listener.CreateListener(test_cli20.MyApp(sys.stdout), None) + my_id = 'my-id' + loadbalancer_id = 'loadbalancer' + protocol = 'TCP' + protocol_port = '80' + args = ['--protocol', protocol, '--protocol-port', protocol_port, + '--loadbalancer', loadbalancer_id] + position_names = ['protocol', 'protocol_port', 'loadbalancer_id'] + position_values = [protocol, protocol_port, loadbalancer_id, + True] + self._test_create_resource(resource, cmd, '', my_id, args, + position_names, position_values, + cmd_resource=cmd_resource) + + def test_create_listener_with_all_params(self): + """lbaas-listener-create with all params set.""" + resource = 'listener' + cmd_resource = 'lbaas_listener' + cmd = listener.CreateListener(test_cli20.MyApp(sys.stdout), None) + my_id = 'my-id' + loadbalancer = 'loadbalancer' + protocol = 'TCP' + protocol_port = '80' + args = ['--admin-state-down', + '--protocol', protocol, '--protocol-port', protocol_port, + '--loadbalancer', loadbalancer] + position_names = ['admin_state_up', + 'protocol', 'protocol_port', 'loadbalancer_id'] + position_values = [False, protocol, protocol_port, loadbalancer] + self._test_create_resource(resource, cmd, '', my_id, args, + position_names, position_values, + cmd_resource=cmd_resource) + + def test_list_listeners(self): + """lbaas-listener-list.""" + resources = 'listeners' + cmd_resources = 'lbaas_listeners' + cmd = listener.ListListener(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, True, + cmd_resources=cmd_resources) + + def test_list_listeners_pagination(self): + """lbaas-listener-list with pagination.""" + resources = 'listeners' + cmd_resources = 'lbaas_listeners' + cmd = listener.ListListener(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources_with_pagination(resources, cmd, + cmd_resources=cmd_resources) + + def test_list_listeners_sort(self): + """lbaas-listener-list --sort-key id --sort-key asc.""" + resources = 'listeners' + cmd_resources = 'lbaas_listeners' + cmd = listener.ListListener(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, True, + cmd_resources=cmd_resources) + + def test_list_listeners_limit(self): + """lbaas-listener-list -P.""" + resources = 'listeners' + cmd_resources = 'lbaas_listeners' + cmd = listener.ListListener(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, page_size=1000, + cmd_resources=cmd_resources) + + def test_show_listener_id(self): + """lbaas-listener-show test_id.""" + resource = 'listener' + cmd_resource = 'lbaas_listener' + cmd = listener.ShowListener(test_cli20.MyApp(sys.stdout), None) + args = ['--fields', 'id', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, args, ['id'], + cmd_resource=cmd_resource) + + def test_show_listener_id_name(self): + """lbaas-listener-show.""" + resource = 'listener' + cmd_resource = 'lbaas_listener' + cmd = listener.ShowListener(test_cli20.MyApp(sys.stdout), None) + args = ['--fields', 'id', '--fields', 'name', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, + args, ['id', 'name'], + cmd_resource=cmd_resource) + + def test_update_listener(self): + """lbaas-listener-update myid --name newname.""" + resource = 'listener' + cmd_resource = 'lbaas_listener' + cmd = listener.UpdateListener(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--name', 'newname'], + {'name': 'newname', }, + cmd_resource=cmd_resource) + + def test_delete_listener(self): + """lbaas-listener-delete my-id.""" + resource = 'listener' + cmd_resource = 'lbaas_listener' + cmd = listener.DeleteListener(test_cli20.MyApp(sys.stdout), None) + my_id = 'my-id' + args = [my_id] + self._test_delete_resource(resource, cmd, my_id, args, + cmd_resource=cmd_resource) + + +class CLITestV20LbListenerXML(CLITestV20LbListenerJSON): + format = 'xml' diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py b/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py new file mode 100644 index 000000000..a8ccd4e7e --- /dev/null +++ b/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py @@ -0,0 +1,137 @@ +# Copyright 2014 Blue Box Group, 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. +# +# @author: Craig Tracey +# + +import sys + +from neutronclient.neutron.v2_0.lb.v2 import loadbalancer as lb +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20LbLoadBalancerJSON(test_cli20.CLITestV20Base): + + def test_create_loadbalancer_with_mandatory_params(self): + """lbaas-loadbalancer-create with mandatory params only.""" + resource = 'loadbalancer' + cmd_resource = 'lbaas_loadbalancer' + cmd = lb.CreateLoadBalancer(test_cli20.MyApp(sys.stdout), None) + name = 'lbaas-loadbalancer-name' + vip_subnet_id = 'vip-subnet' + my_id = 'my-id' + args = ['--name', name, vip_subnet_id] + position_names = ['name', 'vip_subnet_id'] + position_values = [name, vip_subnet_id] + self._test_create_resource(resource, cmd, name, my_id, args, + position_names, position_values, + cmd_resource=cmd_resource) + + def test_create_loadbalancer_with_all_params(self): + """lbaas-loadbalancer-create with all params set.""" + resource = 'loadbalancer' + cmd_resource = 'lbaas_loadbalancer' + cmd = lb.CreateLoadBalancer(test_cli20.MyApp(sys.stdout), None) + name = 'lbaas-loadbalancer-name' + description = 'lbaas-loadbalancer-desc' + vip_subnet_id = 'vip-subnet' + my_id = 'my-id' + args = ['--admin-state-down', '--description', description, + '--name', name, vip_subnet_id] + position_names = ['admin_state_up', 'description', 'name', + 'vip_subnet_id'] + position_values = [False, description, name, vip_subnet_id] + self._test_create_resource(resource, cmd, name, my_id, args, + position_names, position_values, + cmd_resource=cmd_resource) + + def test_list_loadbalancers(self): + """lbaas-loadbalancer-list.""" + resources = 'loadbalancers' + cmd_resources = 'lbaas_loadbalancers' + cmd = lb.ListLoadBalancer(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, True, + cmd_resources=cmd_resources) + + def test_list_loadbalancers_pagination(self): + """lbaas-loadbalancer-list with pagination.""" + resources = 'loadbalancers' + cmd_resources = 'lbaas_loadbalancers' + cmd = lb.ListLoadBalancer(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources_with_pagination(resources, cmd, + cmd_resources=cmd_resources) + + def test_list_loadbalancers_sort(self): + """lbaas-loadbalancer-list --sort-key name --sort-key id + --sort-key asc --sort-key desc + """ + resources = 'loadbalancers' + cmd_resources = 'lbaas_loadbalancers' + cmd = lb.ListLoadBalancer(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, + sort_key=["name", "id"], + sort_dir=["asc", "desc"], + cmd_resources=cmd_resources) + + def test_list_loadbalancers_limit(self): + """lbaas-loadbalancer-list -P.""" + resources = 'loadbalancers' + cmd_resources = 'lbaas_loadbalancers' + cmd = lb.ListLoadBalancer(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, page_size=1000, + cmd_resources=cmd_resources) + + def test_show_loadbalancer_id(self): + """lbaas-loadbalancer-loadbalancer-show test_id.""" + resource = 'loadbalancer' + cmd_resource = 'lbaas_loadbalancer' + cmd = lb.ShowLoadBalancer(test_cli20.MyApp(sys.stdout), None) + args = ['--fields', 'id', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, args, ['id'], + cmd_resource=cmd_resource) + + def test_show_loadbalancer_id_name(self): + """lbaas-loadbalancer-loadbalancer-show.""" + resource = 'loadbalancer' + cmd_resource = 'lbaas_loadbalancer' + cmd = lb.ShowLoadBalancer(test_cli20.MyApp(sys.stdout), None) + args = ['--fields', 'id', '--fields', 'name', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, + args, ['id', 'name'], + cmd_resource=cmd_resource) + + def test_update_loadbalancer(self): + """lbaas-loadbalancer-loadbalancer-update myid --name newname.""" + resource = 'loadbalancer' + cmd_resource = 'lbaas_loadbalancer' + cmd = lb.UpdateLoadBalancer(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--name', 'newname'], + {'name': 'newname', }, + cmd_resource=cmd_resource) + + def test_delete_loadbalancer(self): + """lbaas-loadbalancer-loadbalancer-delete my-id.""" + resource = 'loadbalancer' + cmd_resource = 'lbaas_loadbalancer' + cmd = lb.DeleteLoadBalancer(test_cli20.MyApp(sys.stdout), None) + my_id = 'my-id' + args = [my_id] + self._test_delete_resource(resource, cmd, my_id, args, + cmd_resource=cmd_resource) + + +class CLITestV20LbLoadBalancerXML(CLITestV20LbLoadBalancerJSON): + format = 'xml' diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_member.py b/neutronclient/tests/unit/lb/v2/test_cli20_member.py new file mode 100644 index 000000000..2adb03660 --- /dev/null +++ b/neutronclient/tests/unit/lb/v2/test_cli20_member.py @@ -0,0 +1,159 @@ +# Copyright 2014 Blue Box Group, 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. +# +# @author: Craig Tracey +# + +import sys + +from neutronclient.neutron.v2_0.lb.v2 import member +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20LbMemberJSON(test_cli20.CLITestV20Base): + + def test_create_member_with_mandatory_params(self): + """lbaas-member-create with mandatory params only.""" + resource = 'member' + cmd_resource = 'lbaas_member' + cmd = member.CreateMember(test_cli20.MyApp(sys.stdout), None) + my_id = 'my-id' + address = '10.1.1.1' + protocol_port = '80' + pool_id = 'pool-id' + subnet_id = 'subnet-id' + args = ['--address', address, '--protocol-port', protocol_port, + '--subnet', subnet_id, pool_id] + position_names = ['admin_state_up', 'address', + 'protocol_port', 'subnet_id'] + position_values = [True, address, protocol_port, subnet_id] + self._test_create_resource(resource, cmd, '', my_id, args, + position_names, position_values, + cmd_resource=cmd_resource, + parent_id=pool_id) + + def test_create_member_with_all_params(self): + """lbaas-member-create with all params set.""" + resource = 'member' + cmd_resource = 'lbaas_member' + cmd = member.CreateMember(test_cli20.MyApp(sys.stdout), None) + my_id = 'my-id' + address = '10.1.1.1' + protocol_port = '80' + pool_id = 'pool-id' + subnet_id = 'subnet-id' + weight = '100' + args = ['--address', address, '--protocol-port', protocol_port, + '--subnet', subnet_id, pool_id, '--weight', weight, + '--admin-state-down'] + position_names = ['admin_state_up', 'address', 'protocol_port', + 'subnet_id', 'weight'] + position_values = [False, address, protocol_port, subnet_id, weight] + self._test_create_resource(resource, cmd, '', my_id, args, + position_names, position_values, + cmd_resource=cmd_resource, + parent_id=pool_id) + + def test_list_members(self): + """lbaas-member-list.""" + resources = 'members' + cmd_resources = 'lbaas_members' + pool_id = 'pool-id' + cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, True, base_args=[pool_id], + cmd_resources=cmd_resources, + parent_id=pool_id) + + def test_list_members_pagination(self): + """lbaas-member-list with pagination.""" + resources = 'members' + cmd_resources = 'lbaas_members' + pool_id = 'pool-id' + cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources_with_pagination(resources, cmd, + base_args=[pool_id], + cmd_resources=cmd_resources, + parent_id=pool_id) + + def test_list_members_sort(self): + """lbaas-member-list --sort-key id --sort-key asc.""" + resources = 'members' + cmd_resources = 'lbaas_members' + pool_id = 'pool-id' + cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, True, base_args=[pool_id], + cmd_resources=cmd_resources, + parent_id=pool_id) + + def test_list_members_limit(self): + """lbaas-member-list -P.""" + resources = 'members' + cmd_resources = 'lbaas_members' + pool_id = 'pool-id' + cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, page_size=1000, + base_args=[pool_id], + cmd_resources=cmd_resources, + parent_id=pool_id) + + def test_show_member_id(self): + """lbaas-member-show test_id.""" + resource = 'member' + cmd_resource = 'lbaas_member' + pool_id = 'pool-id' + cmd = member.ShowMember(test_cli20.MyApp(sys.stdout), None) + args = ['--fields', 'id', self.test_id, pool_id] + self._test_show_resource(resource, cmd, self.test_id, args, ['id'], + cmd_resource=cmd_resource, parent_id=pool_id) + + def test_show_member_id_name(self): + """lbaas-member-show.""" + resource = 'member' + cmd_resource = 'lbaas_member' + pool_id = 'pool-id' + cmd = member.ShowMember(test_cli20.MyApp(sys.stdout), None) + args = ['--fields', 'id', '--fields', 'name', self.test_id, pool_id] + self._test_show_resource(resource, cmd, self.test_id, + args, ['id', 'name'], + cmd_resource=cmd_resource, parent_id=pool_id) + + def test_update_member(self): + """lbaas-member-update myid --name newname.""" + resource = 'member' + cmd_resource = 'lbaas_member' + my_id = 'my-id' + pool_id = 'pool-id' + args = [my_id, pool_id, '--name', 'newname'] + cmd = member.UpdateMember(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, my_id, args, + {'name': 'newname', }, + cmd_resource=cmd_resource, + parent_id=pool_id) + + def test_delete_member(self): + """lbaas-member-delete my-id.""" + resource = 'member' + cmd_resource = 'lbaas_member' + cmd = member.DeleteMember(test_cli20.MyApp(sys.stdout), None) + my_id = 'my-id' + pool_id = 'pool-id' + args = [my_id, pool_id] + self._test_delete_resource(resource, cmd, my_id, args, + cmd_resource=cmd_resource, + parent_id=pool_id) + + +class CLITestV20LbMemberXML(CLITestV20LbMemberJSON): + format = 'xml' diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_pool.py b/neutronclient/tests/unit/lb/v2/test_cli20_pool.py new file mode 100644 index 000000000..daf3e5292 --- /dev/null +++ b/neutronclient/tests/unit/lb/v2/test_cli20_pool.py @@ -0,0 +1,149 @@ +# Copyright 2014 Blue Box Group, 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. +# +# @author: Craig Tracey +# + +import sys + +from neutronclient.neutron.v2_0.lb.v2 import pool +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20LbPoolJSON(test_cli20.CLITestV20Base): + + def test_create_pool_with_mandatory_params(self): + """lbaas-pool-create with mandatory params only.""" + resource = 'pool' + cmd_resource = 'lbaas_pool' + cmd = pool.CreatePool(test_cli20.MyApp(sys.stdout), None) + my_id = 'my-id' + lb_algorithm = 'ROUND_ROBIN' + listener = 'listener' + protocol = 'TCP' + name = 'my-pool' + args = ['--lb-algorithm', lb_algorithm, '--protocol', protocol, + '--listener', listener, name] + position_names = ['admin_state_up', 'lb_algorithm', 'protocol', + 'listener_id', 'name'] + position_values = [True, lb_algorithm, protocol, listener, name] + self._test_create_resource(resource, cmd, '', my_id, args, + position_names, position_values, + cmd_resource=cmd_resource) + + def test_create_pool_with_all_params(self): + """lbaas-pool-create with all params set.""" + resource = 'pool' + cmd_resource = 'lbaas_pool' + cmd = pool.CreatePool(test_cli20.MyApp(sys.stdout), None) + my_id = 'my-id' + lb_algorithm = 'ROUND_ROBIN' + listener = 'listener' + protocol = 'TCP' + description = 'description' + session_persistence_str = 'HTTP_COOKIE:1234' + session_persistence = {'type': 'HTTP_COOKIE', + 'cookie_name': '1234'} + healthmon_id = 'healthmon-id' + name = 'my-pool' + args = ['--lb-algorithm', lb_algorithm, '--protocol', protocol, + '--description', description, '--session-persistence', + session_persistence_str, '--healthmonitor-id', + healthmon_id, '--admin-state-down', name, + '--listener', listener] + position_names = ['lb_algorithm', 'protocol', 'description', + 'session_persistence', 'healthmonitor_id', + 'admin_state_up', 'listener_id', 'name'] + position_values = [lb_algorithm, protocol, description, + session_persistence, healthmon_id, + False, listener, name] + self._test_create_resource(resource, cmd, '', my_id, args, + position_names, position_values, + cmd_resource=cmd_resource) + + def test_list_pools(self): + """lbaas-pool-list.""" + resources = 'pools' + cmd_resources = 'lbaas_pools' + cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, True, + cmd_resources=cmd_resources) + + def test_list_pools_pagination(self): + """lbaas-pool-list with pagination.""" + resources = 'pools' + cmd_resources = 'lbaas_pools' + cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources_with_pagination(resources, cmd, + cmd_resources=cmd_resources) + + def test_list_pools_sort(self): + """lbaas-pool-list --sort-key id --sort-key asc.""" + resources = 'pools' + cmd_resources = 'lbaas_pools' + cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, True, + cmd_resources=cmd_resources) + + def test_list_pools_limit(self): + """lbaas-pool-list -P.""" + resources = 'pools' + cmd_resources = 'lbaas_pools' + cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, page_size=1000, + cmd_resources=cmd_resources) + + def test_show_pool_id(self): + """lbaas-pool-show test_id.""" + resource = 'pool' + cmd_resource = 'lbaas_pool' + cmd = pool.ShowPool(test_cli20.MyApp(sys.stdout), None) + args = ['--fields', 'id', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, args, ['id'], + cmd_resource=cmd_resource) + + def test_show_pool_id_name(self): + """lbaas-pool-show.""" + resource = 'pool' + cmd_resource = 'lbaas_pool' + cmd = pool.ShowPool(test_cli20.MyApp(sys.stdout), None) + args = ['--fields', 'id', '--fields', 'name', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, + args, ['id', 'name'], + cmd_resource=cmd_resource) + + def test_update_pool(self): + """lbaas-pool-update myid --name newname.""" + resource = 'pool' + cmd_resource = 'lbaas_pool' + cmd = pool.UpdatePool(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--name', 'newname'], + {'name': 'newname', }, + cmd_resource=cmd_resource) + + def test_delete_pool(self): + """lbaas-pool-delete my-id.""" + resource = 'pool' + cmd_resource = 'lbaas_pool' + cmd = pool.DeletePool(test_cli20.MyApp(sys.stdout), None) + my_id = 'my-id' + args = [my_id] + self._test_delete_resource(resource, cmd, my_id, args, + cmd_resource=cmd_resource) + + +class CLITestV20LbPoolXML(CLITestV20LbPoolJSON): + format = 'xml' diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 21db5021e..6b26e6f98 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -396,6 +396,7 @@ def _test_list_resources(self, resources, cmd, detail=False, tags=(), return _str def _test_list_resources_with_pagination(self, resources, cmd, + base_args=None, cmd_resources=None, parent_id=None): self.mox.StubOutWithMock(cmd, "get_client") @@ -430,7 +431,8 @@ def _test_list_resources_with_pagination(self, resources, cmd, 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr2)) self.mox.ReplayAll() cmd_parser = cmd.get_parser("list_" + cmd_resources) - args = ['--request-format', self.format] + args = base_args if base_args is not None else [] + args.extend(['--request-format', self.format]) shell.run_command(cmd, cmd_parser, args) self.mox.VerifyAll() self.mox.UnsetStubs() diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 0e8e6dc56..f33c7aa41 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -1,4 +1,5 @@ # Copyright 2012 OpenStack Foundation. +# Copyright 2015 Hewlett-Packard Development Company, L.P. # All Rights Reserved # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -178,6 +179,18 @@ class Client(object): ikepolicy_path = "/vpn/ikepolicies/%s" ipsec_site_connections_path = "/vpn/ipsec-site-connections" ipsec_site_connection_path = "/vpn/ipsec-site-connections/%s" + + lbaas_loadbalancers_path = "/lbaas/loadbalancers" + lbaas_loadbalancer_path = "/lbaas/loadbalancers/%s" + lbaas_listeners_path = "/lbaas/listeners" + lbaas_listener_path = "/lbaas/listeners/%s" + lbaas_pools_path = "/lbaas/pools" + lbaas_pool_path = "/lbaas/pools/%s" + lbaas_healthmonitors_path = "/lbaas/healthmonitors" + lbaas_healthmonitor_path = "/lbaas/healthmonitors/%s" + lbaas_members_path = lbaas_pool_path + "/members" + lbaas_member_path = lbaas_pool_path + "/members/%s" + vips_path = "/lb/vips" vip_path = "/lb/vips/%s" pools_path = "/lb/pools" @@ -255,6 +268,12 @@ class Client(object): 'metering_label_rules': 'metering_label_rule', 'net_partitions': 'net_partition', 'packet_filters': 'packet_filter', + 'loadbalancers': 'loadbalancer', + 'listeners': 'listener', + 'lbaas_pools': 'lbaas_pool', + 'lbaas_healthmonitors': 'lbaas_healthmonitor', + 'lbaas_members': 'lbaas_member', + 'healthmonitors': 'healthmonitor', } def get_attr_metadata(self): @@ -628,6 +647,154 @@ def delete_ipsecpolicy(self, ipsecpolicy): """Deletes the specified IPsecPolicy.""" return self.delete(self.ipsecpolicy_path % (ipsecpolicy)) + @APIParamsCall + def list_loadbalancers(self, retrieve_all=True, **_params): + """Fetches a list of all loadbalancers for a tenant.""" + return self.list('loadbalancers', self.lbaas_loadbalancers_path, + retrieve_all, **_params) + + @APIParamsCall + def show_loadbalancer(self, lbaas_loadbalancer, **_params): + """Fetches information for a load balancer.""" + return self.get(self.lbaas_loadbalancer_path % (lbaas_loadbalancer), + params=_params) + + @APIParamsCall + def create_loadbalancer(self, body=None): + """Creates a new load balancer.""" + return self.post(self.lbaas_loadbalancers_path, body=body) + + @APIParamsCall + def update_loadbalancer(self, lbaas_loadbalancer, body=None): + """Updates a load balancer.""" + return self.put(self.lbaas_loadbalancer_path % (lbaas_loadbalancer), + body=body) + + @APIParamsCall + def delete_loadbalancer(self, lbaas_loadbalancer): + """Deletes the specified load balancer.""" + return self.delete(self.lbaas_loadbalancer_path % + (lbaas_loadbalancer)) + + @APIParamsCall + def list_listeners(self, retrieve_all=True, **_params): + """Fetches a list of all lbaas_listeners for a tenant.""" + return self.list('listeners', self.lbaas_listeners_path, + retrieve_all, **_params) + + @APIParamsCall + def show_listener(self, lbaas_listener, **_params): + """Fetches information for a lbaas_listener.""" + return self.get(self.lbaas_listener_path % (lbaas_listener), + params=_params) + + @APIParamsCall + def create_listener(self, body=None): + """Creates a new lbaas_listener.""" + return self.post(self.lbaas_listeners_path, body=body) + + @APIParamsCall + def update_listener(self, lbaas_listener, body=None): + """Updates a lbaas_listener.""" + return self.put(self.lbaas_listener_path % (lbaas_listener), + body=body) + + @APIParamsCall + def delete_listener(self, lbaas_listener): + """Deletes the specified lbaas_listener.""" + return self.delete(self.lbaas_listener_path % (lbaas_listener)) + + @APIParamsCall + def list_lbaas_pools(self, retrieve_all=True, **_params): + """Fetches a list of all lbaas_pools for a tenant.""" + return self.list('pools', self.lbaas_pools_path, + retrieve_all, **_params) + + @APIParamsCall + def show_lbaas_pool(self, lbaas_pool, **_params): + """Fetches information for a lbaas_pool.""" + return self.get(self.lbaas_pool_path % (lbaas_pool), + params=_params) + + @APIParamsCall + def create_lbaas_pool(self, body=None): + """Creates a new lbaas_pool.""" + return self.post(self.lbaas_pools_path, body=body) + + @APIParamsCall + def update_lbaas_pool(self, lbaas_pool, body=None): + """Updates a lbaas_pool.""" + return self.put(self.lbaas_pool_path % (lbaas_pool), + body=body) + + @APIParamsCall + def delete_lbaas_pool(self, lbaas_pool): + """Deletes the specified lbaas_pool.""" + return self.delete(self.lbaas_pool_path % (lbaas_pool)) + + @APIParamsCall + def list_lbaas_healthmonitors(self, retrieve_all=True, **_params): + """Fetches a list of all lbaas_healthmonitors for a tenant.""" + return self.list('healthmonitors', self.lbaas_healthmonitors_path, + retrieve_all, **_params) + + @APIParamsCall + def show_lbaas_healthmonitor(self, lbaas_healthmonitor, **_params): + """Fetches information for a lbaas_healthmonitor.""" + return self.get(self.lbaas_healthmonitor_path % (lbaas_healthmonitor), + params=_params) + + @APIParamsCall + def create_lbaas_healthmonitor(self, body=None): + """Creates a new lbaas_healthmonitor.""" + return self.post(self.lbaas_healthmonitors_path, body=body) + + @APIParamsCall + def update_lbaas_healthmonitor(self, lbaas_healthmonitor, body=None): + """Updates a lbaas_healthmonitor.""" + return self.put(self.lbaas_healthmonitor_path % (lbaas_healthmonitor), + body=body) + + @APIParamsCall + def delete_lbaas_healthmonitor(self, lbaas_healthmonitor): + """Deletes the specified lbaas_healthmonitor.""" + return self.delete(self.lbaas_healthmonitor_path % + (lbaas_healthmonitor)) + + @APIParamsCall + def list_lbaas_loadbalancers(self, retrieve_all=True, **_params): + """Fetches a list of all lbaas_loadbalancers for a tenant.""" + return self.list('loadbalancers', self.lbaas_loadbalancers_path, + retrieve_all, **_params) + + @APIParamsCall + def list_lbaas_members(self, lbaas_pool, retrieve_all=True, **_params): + """Fetches a list of all lbaas_members for a tenant.""" + return self.list('members', self.lbaas_members_path % lbaas_pool, + retrieve_all, **_params) + + @APIParamsCall + def show_lbaas_member(self, lbaas_member, lbaas_pool, **_params): + """Fetches information of a certain lbaas_member.""" + return self.get(self.lbaas_member_path % (lbaas_pool, lbaas_member), + params=_params) + + @APIParamsCall + def create_lbaas_member(self, lbaas_pool, body=None): + """Creates an lbaas_member.""" + return self.post(self.lbaas_members_path % lbaas_pool, body=body) + + @APIParamsCall + def update_lbaas_member(self, lbaas_member, lbaas_pool, body=None): + """Updates a lbaas_healthmonitor.""" + return self.put(self.lbaas_member_path % (lbaas_pool, lbaas_member), + body=body) + + @APIParamsCall + def delete_lbaas_member(self, lbaas_member, lbaas_pool): + """Deletes the specified lbaas_member.""" + return self.delete(self.lbaas_member_path % (lbaas_pool, lbaas_member)) + @APIParamsCall def list_vips(self, retrieve_all=True, **_params): """Fetches a list of all load balancer vips for a tenant.""" From 57adb7f340577c0d27b377be449cb65edeb8508a Mon Sep 17 00:00:00 2001 From: Ilya Shakhat Date: Tue, 13 Jan 2015 16:44:01 +0300 Subject: [PATCH 158/845] Fix CSV formatting of fixed_ips field in port-list command With this patch fixed_ips field is printed in JSON format. The patch corrects https://review.openstack.org/#/c/99929/ Closes bug 1263551 Change-Id: I10842b96a443396abf0b268a699cce85b98c5a72 --- neutronclient/neutron/v2_0/__init__.py | 16 ++++++++-------- neutronclient/neutron/v2_0/port.py | 4 ++-- neutronclient/tests/unit/test_cli20.py | 5 ++++- neutronclient/tests/unit/test_cli20_port.py | 13 +++++++++++++ 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index cfd536a7f..1ca26e221 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -672,14 +672,14 @@ def setup_columns(self, info, parsed_args): # both list_columns and returned resource. # Also Keep their order the same as in list_columns _columns = [x for x in self.list_columns if x in _columns] - if parsed_args.formatter == 'csv': - return (_columns, (utils.get_item_properties( - s, _columns, formatters=self._formatters_csv) - for s in info),) - else: - return (_columns, (utils.get_item_properties( - s, _columns, formatters=self._formatters) - for s in info),) + + formatters = self._formatters + if hasattr(self, '_formatters_csv') and parsed_args.formatter == 'csv': + formatters = self._formatters_csv + + return (_columns, (utils.get_item_properties( + s, _columns, formatters=formatters, ) + for s in info), ) def get_data(self, parsed_args): self.log.debug('get_data(%s)', parsed_args) diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index 00ff586ea..77e09a40f 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -33,8 +33,8 @@ def _format_fixed_ips(port): def _format_fixed_ips_csv(port): try: - return [utils.dumps(ip) for ip in port['fixed_ips']] - except Exception: + return jsonutils.dumps(port['fixed_ips']) + except (TypeError, KeyError): return '' diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 6b26e6f98..5f5e06a3e 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -299,7 +299,7 @@ def _test_list_resources(self, resources, cmd, detail=False, tags=(), fields_1=(), fields_2=(), page_size=None, sort_key=(), sort_dir=(), response_contents=None, base_args=None, path=None, cmd_resources=None, - parent_id=None): + parent_id=None, output_format=None): self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") cmd.get_client().MultipleTimes().AndReturn(self.client) @@ -378,6 +378,9 @@ def _test_list_resources(self, resources, cmd, detail=False, tags=(), path = getattr(self.client, cmd_resources + "_path") if parent_id: path = path % parent_id + if output_format: + args.append('-f') + args.append(output_format) self.client.httpclient.request( MyUrlComparator(end_url(path, query, format=self.format), self.client), diff --git a/neutronclient/tests/unit/test_cli20_port.py b/neutronclient/tests/unit/test_cli20_port.py index 2b61027b7..0ea92c732 100644 --- a/neutronclient/tests/unit/test_cli20_port.py +++ b/neutronclient/tests/unit/test_cli20_port.py @@ -227,6 +227,19 @@ def test_list_ports_fields(self): self._test_list_resources(resources, cmd, fields_1=['a', 'b'], fields_2=['c', 'd']) + def test_list_ports_with_fixed_ips_in_csv(self): + """List ports: -f csv.""" + resources = "ports" + cmd = port.ListPort(test_cli20.MyApp(sys.stdout), None) + fixed_ips = [{"subnet_id": "30422057-d6df-4c90-8314-aefb5e326666", + "ip_address": "10.0.0.12"}, + {"subnet_id": "30422057-d6df-4c90-8314-aefb5e326666", + "ip_address": "10.0.0.4"}] + contents = [{'name': 'name1', 'fixed_ips': fixed_ips}] + self._test_list_resources(resources, cmd, True, + response_contents=contents, + output_format='csv') + def _test_list_router_port(self, resources, cmd, myid, detail=False, tags=(), fields_1=(), fields_2=()): From 069b14c87d27c4b4b79c89db145315cee9ddf664 Mon Sep 17 00:00:00 2001 From: PhilippeJ Date: Sat, 7 Feb 2015 22:26:40 +0100 Subject: [PATCH 159/845] Fix for incorrect parameter in user-id error message in shell.py Change-Id: I3f260377b8675675f631862e28101fa51bf2aad9 --- neutronclient/shell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/shell.py b/neutronclient/shell.py index f892ba20c..66fdca81a 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -767,7 +767,7 @@ def authenticate_user(self): raise exc.CommandError( _("You must provide a username or user ID via" " --os-username, env[OS_USERNAME] or" - " --os-user_id, env[OS_USER_ID]")) + " --os-user-id, env[OS_USER_ID]")) if not self.options.os_password: # No password, If we've got a tty, try prompting for it From a774e84ef0c3d69f269c7a7a6ffcc4bbb38cb3bb Mon Sep 17 00:00:00 2001 From: Elena Ezhova Date: Mon, 2 Feb 2015 17:23:08 +0300 Subject: [PATCH 160/845] Add unit tests for agentscheduler related commands Added unit tests for: * add network to DHCP agent; * remove network from DHCP agent; * list networks on DHCP agent; * list DHCP agents hosting network; * add router to L3 agent; * remove router from L3 agent; * list routers on L3 agent; * list L3 agents hosting router. Change-Id: I6ae82c2a65dcf86fa1510ac27d465b9cd3941a1b Closes-Bug: #1206113 --- .../tests/unit/test_cli20_agentschedulers.py | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/neutronclient/tests/unit/test_cli20_agentschedulers.py b/neutronclient/tests/unit/test_cli20_agentschedulers.py index c0a807053..8dc307a8b 100644 --- a/neutronclient/tests/unit/test_cli20_agentschedulers.py +++ b/neutronclient/tests/unit/test_cli20_agentschedulers.py @@ -18,10 +18,150 @@ import sys +from mox3 import mox + from neutronclient.neutron.v2_0 import agentscheduler +from neutronclient.neutron.v2_0 import network from neutronclient.tests.unit import test_cli20 +AGENT_ID = 'agent_id1' +NETWORK_ID = 'net_id1' +ROUTER_ID = 'router_id1' + + +class CLITestV20AgentScheduler(test_cli20.CLITestV20Base): + def _test_add_to_agent(self, resource, cmd, cmd_args, destination, + body, result): + path = ((self.client.agent_path + destination) % + cmd_args[0]) + + self.mox.StubOutWithMock(cmd, "get_client") + self.mox.StubOutWithMock(self.client.httpclient, "request") + cmd.get_client().MultipleTimes().AndReturn(self.client) + result_str = self.client.serialize(result) + return_tup = (test_cli20.MyResp(200), result_str) + + self.client.httpclient.request( + test_cli20.end_url(path), 'POST', + body=test_cli20.MyComparator(body, self.client), + headers=mox.ContainsKeyValue( + 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(return_tup) + self.mox.ReplayAll() + cmd_parser = cmd.get_parser('test_' + resource) + parsed_args = cmd_parser.parse_args(cmd_args) + cmd.run(parsed_args) + self.mox.VerifyAll() + self.mox.UnsetStubs() + + def _test_remove_from_agent(self, resource, cmd, cmd_args, destination): + path = ((self.client.agent_path + destination + '/%s') % + cmd_args) + self.mox.StubOutWithMock(cmd, "get_client") + self.mox.StubOutWithMock(self.client.httpclient, "request") + cmd.get_client().MultipleTimes().AndReturn(self.client) + + return_tup = (test_cli20.MyResp(204), None) + self.client.httpclient.request( + test_cli20.end_url(path), 'DELETE', + body=None, + headers=mox.ContainsKeyValue( + 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(return_tup) + self.mox.ReplayAll() + cmd_parser = cmd.get_parser('test_' + resource) + parsed_args = cmd_parser.parse_args(cmd_args) + cmd.run(parsed_args) + self.mox.VerifyAll() + self.mox.UnsetStubs() + + +class CLITestV20DHCPAgentScheduler(CLITestV20AgentScheduler): + + def test_add_network_to_agent(self): + resource = 'agent' + cmd = agentscheduler.AddNetworkToDhcpAgent( + test_cli20.MyApp(sys.stdout), None) + args = (AGENT_ID, NETWORK_ID) + body = {'network_id': NETWORK_ID} + result = {'network_id': 'net_id', } + self._test_add_to_agent(resource, cmd, args, self.client.DHCP_NETS, + body, result) + + def test_remove_network_from_agent(self): + resource = 'agent' + cmd = agentscheduler.RemoveNetworkFromDhcpAgent( + test_cli20.MyApp(sys.stdout), None) + args = (AGENT_ID, NETWORK_ID) + self._test_remove_from_agent(resource, cmd, args, + self.client.DHCP_NETS) + + def test_list_networks_on_agent(self): + resources = 'networks' + cmd = agentscheduler.ListNetworksOnDhcpAgent( + test_cli20.MyApp(sys.stdout), None) + agent_id = 'agent_id1' + path = ((self.client.agent_path + self.client.DHCP_NETS) % + agent_id) + self.mox.StubOutWithMock(network.ListNetwork, "extend_list") + network.ListNetwork.extend_list(mox.IsA(list), mox.IgnoreArg()) + self._test_list_resources(resources, cmd, base_args=[agent_id], + path=path) + + def test_list_agents_hosting_network(self): + resources = 'agent' + cmd = agentscheduler.ListDhcpAgentsHostingNetwork( + test_cli20.MyApp(sys.stdout), None) + agent_id = 'agent_id1' + path = ((self.client.network_path + self.client.DHCP_AGENTS) % + agent_id) + contents = {self.id_field: 'myid1', 'alive': True} + self._test_list_resources(resources, cmd, base_args=[agent_id], + path=path, response_contents=contents) + + +class CLITestV20L3AgentScheduler(CLITestV20AgentScheduler): + + def test_add_router_to_agent(self): + resource = 'agent' + cmd = agentscheduler.AddRouterToL3Agent( + test_cli20.MyApp(sys.stdout), None) + args = (AGENT_ID, ROUTER_ID) + body = {'router_id': ROUTER_ID} + result = {'network_id': 'net_id', } + self._test_add_to_agent(resource, cmd, args, self.client.L3_ROUTERS, + body, result) + + def test_remove_router_from_agent(self): + resource = 'agent' + cmd = agentscheduler.RemoveRouterFromL3Agent( + test_cli20.MyApp(sys.stdout), None) + args = (AGENT_ID, ROUTER_ID) + self._test_remove_from_agent(resource, cmd, args, + self.client.L3_ROUTERS) + + def test_list_routers_on_agent(self): + resources = 'router' + cmd = agentscheduler.ListRoutersOnL3Agent( + test_cli20.MyApp(sys.stdout), None) + agent_id = 'agent_id1' + path = ((self.client.agent_path + self.client.L3_ROUTERS) % + agent_id) + contents = {self.id_field: 'myid1', 'name': 'my_name'} + self._test_list_resources(resources, cmd, base_args=[agent_id], + path=path, response_contents=contents) + + def test_list_agents_hosting_router(self): + resources = 'agent' + cmd = agentscheduler.ListL3AgentsHostingRouter( + test_cli20.MyApp(sys.stdout), None) + agent_id = 'agent_id1' + path = ((self.client.router_path + self.client.L3_AGENTS) % + agent_id) + contents = {self.id_field: 'myid1', 'alive': True} + self._test_list_resources(resources, cmd, base_args=[agent_id], + path=path, response_contents=contents) + + class CLITestV20LBaaSAgentScheduler(test_cli20.CLITestV20Base): def test_list_pools_on_agent(self): From 3e5c6baa43fc262abd29e7fdffca81a8b2ebd0fe Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Fri, 13 Feb 2015 02:00:21 +0000 Subject: [PATCH 161/845] Updated from global requirements Change-Id: Ide4446ae84da4e47db2998c1494b54129a6650ba --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index cefcc5779..aeb18936f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ oslo.i18n>=1.3.0 # Apache-2.0 oslo.serialization>=1.2.0 # Apache-2.0 oslo.utils>=1.2.0 # Apache-2.0 requests>=2.2.0,!=2.4.0 -python-keystoneclient>=1.0.0 +python-keystoneclient>=1.1.0 simplejson>=2.2.0 six>=1.7.0 Babel>=1.3 From 62a8a5b1502353850174c0b8c5e8839695c4e4eb Mon Sep 17 00:00:00 2001 From: K Jonathan Harker Date: Thu, 19 Feb 2015 12:56:22 -0800 Subject: [PATCH 162/845] Make some auth error messages more verbose When specifying OS_URL but not OS_TOKEN the user is given an error about needing to specify OS_TOKEN with no explanation why. Expand the error messages to explain that the two values must co-exist. Change-Id: Ib3208a097997bceece4f2a881b5e4e3285852265 --- neutronclient/shell.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 66fdca81a..953eae27b 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -746,12 +746,14 @@ def authenticate_user(self): if not self.options.os_token: raise exc.CommandError( _("You must provide a token via" - " either --os-token or env[OS_TOKEN]")) + " either --os-token or env[OS_TOKEN]" + " when providing a service URL")) if not self.options.os_url: raise exc.CommandError( _("You must provide a service URL via" - " either --os-url or env[OS_URL]")) + " either --os-url or env[OS_URL]" + " when providing a token")) else: # Validate password flow auth From b9a7d52ecb346c4faf0a09184ac6bfc17509eab5 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Fri, 20 Feb 2015 13:59:35 +0000 Subject: [PATCH 163/845] Updated from global requirements Change-Id: I2d22f45e9108f261096cf9f4a584e5bcf7165167 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index aeb18936f..0c93734aa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,5 +12,5 @@ oslo.utils>=1.2.0 # Apache-2.0 requests>=2.2.0,!=2.4.0 python-keystoneclient>=1.1.0 simplejson>=2.2.0 -six>=1.7.0 +six>=1.9.0 Babel>=1.3 From 7b8c2247bc60ff291644cc9d464d2210a2ca60c6 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Mon, 23 Feb 2015 08:35:24 +0900 Subject: [PATCH 164/845] Honor allow_names in *-update command Previously the help message of *-update command says "ID or name" even if the command takes only ID. UpdateCommand get_parser should honor allow_names when generating the help message. Change-Id: I6053d80cea4e1012230d5a1e6d15608545134f20 Closes-Bug: #1424474 --- neutronclient/neutron/v2_0/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index 1ca26e221..2fca3b30a 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -492,9 +492,13 @@ class UpdateCommand(NeutronCommand): def get_parser(self, prog_name): parser = super(UpdateCommand, self).get_parser(prog_name) + if self.allow_names: + help_str = _('ID or name of %s to update.') + else: + help_str = _('ID of %s to update.') parser.add_argument( 'id', metavar=self.resource.upper(), - help=_('ID or name of %s to update.') % self.resource) + help=help_str % self.resource) self.add_known_arguments(parser) return parser From e5e815ccc1ed7a7971d38893b0d5117c2d97ddf4 Mon Sep 17 00:00:00 2001 From: Michael Johnson Date: Sat, 21 Feb 2015 00:15:25 +0000 Subject: [PATCH 165/845] Fix lbaas-loadbalancer-create with no --name This fix allows creating load balancers with no name specified on the command line per the LBaaS v2 API specification. Change-Id: Ia9134fab7799576eb4782b04e30112ea0f622e64 --- neutronclient/neutron/v2_0/lb/v2/loadbalancer.py | 4 ++-- neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py index 576c5fa5a..49fd13451 100644 --- a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py +++ b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py @@ -69,13 +69,13 @@ def args2body(self, parsed_args): self.get_client(), 'subnet', parsed_args.vip_subnet) body = { self.resource: { - 'name': parsed_args.name, 'vip_subnet_id': _subnet_id, 'admin_state_up': parsed_args.admin_state, }, } neutronV20.update_dict(parsed_args, body[self.resource], - ['description', 'provider', 'vip_address']) + ['description', 'provider', 'vip_address', + 'name']) return body diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py b/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py index a8ccd4e7e..11bed0317 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py @@ -32,9 +32,9 @@ def test_create_loadbalancer_with_mandatory_params(self): name = 'lbaas-loadbalancer-name' vip_subnet_id = 'vip-subnet' my_id = 'my-id' - args = ['--name', name, vip_subnet_id] - position_names = ['name', 'vip_subnet_id'] - position_values = [name, vip_subnet_id] + args = [vip_subnet_id] + position_names = ['vip_subnet_id'] + position_values = [vip_subnet_id] self._test_create_resource(resource, cmd, name, my_id, args, position_names, position_values, cmd_resource=cmd_resource) From 779b02e480d8bb3bb1147f3fe0e101d6f1e802d5 Mon Sep 17 00:00:00 2001 From: Justin Hammond Date: Tue, 26 Nov 2013 23:30:47 +0000 Subject: [PATCH 166/845] Client command extension support Adds extension support with emphasis on ease of extension creation. Extensions strongly conform to preexisting neutron commands (/neutron/v2_0/*). A sample extension has been included (/neutron/v2_0/contrib/_fox_sockets.py). As it is assumed that the sample extension will be packaged with the client, small changes were required to include it with the unit tests. It is also possible to install a module with a 'neutronclient.extension' entry- point defined. More information on this can be found in the stevedore docs under the section "Loading the Plugins". Extension discovery is modeled after nova's module discovery but deviates strongly beyond that. A conforming module, at a minimum: * Will have a class that subclasses NeutronClientExtension to provide the requisite version support, paths, and variable names for the client. Example: neutronclient.neutron.v2_0.contrib._fox_sockets.FoxInSocket * Will have at least one class that subclasses from the ClientExtension* classes to provide the new functionality to the client Example: neutronclient.neutron.v2_0.contrib._fox_sockets.FoxInSocketsList * ClientExtension* subclasses must have a shell_command class variable if the command is to be available to the CLI (shell.py) Example: neutronclient.neutron.v2_0.contrib._fox_sockets.FoxInSocketsList Provides client command extensions through new classes: NeutronClientExtension, and ClientExtension. The precedence of command loading are as follows: * hard coded commands are loaded first * contribued commands (those in /contrib) * external commands (installed in the environment) are loaded last Commands that have the same name will be overwritten by commands that are loaded later. To greatly change the execution of a command for your particular extension you only need to override the execute method. Currently this extension support is limited to top-level resources. Parent/ child relationships may be added if desired. Change-Id: I5b2fe530c90b5ce1243fc10341d6d434a1ecea7a Implements: blueprint extensible-neutronclient --- neutronclient/common/extension.py | 86 ++++++++++++++++++ .../neutron/v2_0/contrib/__init__.py | 0 .../neutron/v2_0/contrib/_fox_sockets.py | 85 ++++++++++++++++++ neutronclient/shell.py | 24 +++++ neutronclient/tests/unit/test_cli20.py | 3 +- .../tests/unit/test_client_extension.py | 87 ++++++++++++++++++ neutronclient/v2_0/client.py | 89 +++++++++++++++++++ test-requirements.txt | 1 + 8 files changed, 374 insertions(+), 1 deletion(-) create mode 100644 neutronclient/common/extension.py create mode 100644 neutronclient/neutron/v2_0/contrib/__init__.py create mode 100644 neutronclient/neutron/v2_0/contrib/_fox_sockets.py create mode 100644 neutronclient/tests/unit/test_client_extension.py diff --git a/neutronclient/common/extension.py b/neutronclient/common/extension.py new file mode 100644 index 000000000..2ff8d34a7 --- /dev/null +++ b/neutronclient/common/extension.py @@ -0,0 +1,86 @@ +# Copyright 2015 Rackspace Hosting 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. +# +from stevedore import extension + +from neutronclient.neutron import v2_0 as neutronV20 + + +def _discover_via_entry_points(): + emgr = extension.ExtensionManager('neutronclient.extension', + invoke_on_load=False) + return ((ext.name, ext.plugin) for ext in emgr) + + +class NeutronClientExtension(neutronV20.NeutronCommand): + pagination_support = False + _formatters = {} + sorting_support = False + + +class ClientExtensionShow(NeutronClientExtension, neutronV20.ShowCommand): + def get_data(self, parsed_args): + # NOTE(mdietz): Calls 'execute' to provide a consistent pattern + # for any implementers adding extensions with + # regard to any other extension verb. + return self.execute(parsed_args) + + def execute(self, parsed_args): + return super(ClientExtensionShow, self).get_data(parsed_args) + + +class ClientExtensionList(NeutronClientExtension, neutronV20.ListCommand): + + def get_data(self, parsed_args): + # NOTE(mdietz): Calls 'execute' to provide a consistent pattern + # for any implementers adding extensions with + # regard to any other extension verb. + return self.execute(parsed_args) + + def execute(self, parsed_args): + return super(ClientExtensionList, self).get_data(parsed_args) + + +class ClientExtensionDelete(NeutronClientExtension, neutronV20.DeleteCommand): + def run(self, parsed_args): + # NOTE(mdietz): Calls 'execute' to provide a consistent pattern + # for any implementers adding extensions with + # regard to any other extension verb. + return self.execute(parsed_args) + + def execute(self, parsed_args): + return super(ClientExtensionDelete, self).run(parsed_args) + + +class ClientExtensionCreate(NeutronClientExtension, neutronV20.CreateCommand): + def get_data(self, parsed_args): + # NOTE(mdietz): Calls 'execute' to provide a consistent pattern + # for any implementers adding extensions with + # regard to any other extension verb. + return self.execute(parsed_args) + + def execute(self, parsed_args): + return super(ClientExtensionCreate, self).get_data(parsed_args) + + +class ClientExtensionUpdate(NeutronClientExtension, neutronV20.UpdateCommand): + def run(self, parsed_args): + # NOTE(mdietz): Calls 'execute' to provide a consistent pattern + # for any implementers adding extensions with + # regard to any other extension verb. + return self.execute(parsed_args) + + def execute(self, parsed_args): + return super(ClientExtensionUpdate, self).run(parsed_args) diff --git a/neutronclient/neutron/v2_0/contrib/__init__.py b/neutronclient/neutron/v2_0/contrib/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/neutron/v2_0/contrib/_fox_sockets.py b/neutronclient/neutron/v2_0/contrib/_fox_sockets.py new file mode 100644 index 000000000..da88eb124 --- /dev/null +++ b/neutronclient/neutron/v2_0/contrib/_fox_sockets.py @@ -0,0 +1,85 @@ +# Copyright 2015 Rackspace Hosting 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. +# + +from neutronclient.common import extension +from neutronclient.i18n import _ +from neutronclient.neutron import v2_0 as neutronV20 + + +def _add_updatable_args(parser): + parser.add_argument( + 'name', + help=_('Name of this fox socket.')) + + +def _updatable_args2body(parsed_args, body, client): + if parsed_args.name: + body['fox_socket'].update({'name': parsed_args.name}) + + +class FoxInSocket(extension.NeutronClientExtension): + resource = 'fox_socket' + resource_plural = '%ss' % resource + object_path = '/%s' % resource_plural + resource_path = '/%s/%%s' % resource_plural + versions = ['2.0'] + + +class FoxInSocketsList(extension.ClientExtensionList, FoxInSocket): + shell_command = 'fox-sockets-list' + list_columns = ['id', 'name'] + pagination_support = True + sorting_support = True + + +class FoxInSocketsCreate(extension.ClientExtensionCreate, FoxInSocket): + shell_command = 'fox-sockets-create' + list_columns = ['id', 'name'] + + def add_known_arguments(self, parser): + _add_updatable_args(parser) + + def args2body(self, parsed_args): + body = {'fox_socket': {}} + client = self.get_client() + _updatable_args2body(parsed_args, body, client) + neutronV20.update_dict(parsed_args, body['fox_socket'], []) + return body + + +class FoxInSocketsUpdate(extension.ClientExtensionUpdate, FoxInSocket): + shell_command = 'fox-sockets-update' + list_columns = ['id', 'name'] + + def add_known_arguments(self, parser): + #_add_updatable_args(parser) + parser.add_argument( + '--name', + help=_('Name of this fox socket.')) + + def args2body(self, parsed_args): + body = {'fox_socket': { + 'name': parsed_args.name}, } + neutronV20.update_dict(parsed_args, body['fox_socket'], []) + return body + + +class FoxInSocketsDelete(extension.ClientExtensionDelete, FoxInSocket): + shell_command = 'fox-sockets-delete' + + +class FoxInSocketsShow(extension.ClientExtensionShow, FoxInSocket): + shell_command = 'fox-sockets-show' diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 66fdca81a..4d7562e6f 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -22,6 +22,8 @@ import argparse import getpass +import inspect +import itertools import logging import os import sys @@ -40,6 +42,7 @@ from neutronclient.common import clientmanager from neutronclient.common import command as openstack_command from neutronclient.common import exceptions as exc +from neutronclient.common import extension as client_extension from neutronclient.common import utils from neutronclient.i18n import _ from neutronclient.neutron.v2_0 import agent @@ -381,6 +384,8 @@ def __init__(self, apiversion): for k, v in self.commands[apiversion].items(): self.command_manager.add_command(k, v) + self._register_extensions(VERSION) + # Pop the 'complete' to correct the outputs of 'neutron help'. self.command_manager.commands.pop('complete') @@ -671,6 +676,25 @@ def _bash_completion(self): options.add(option) print(' '.join(commands | options)) + def _register_extensions(self, version): + for name, module in itertools.chain( + client_extension._discover_via_entry_points()): + self._extend_shell_commands(module, version) + + def _extend_shell_commands(self, module, version): + classes = inspect.getmembers(module, inspect.isclass) + for cls_name, cls in classes: + if (issubclass(cls, client_extension.NeutronClientExtension) and + hasattr(cls, 'shell_command')): + cmd = cls.shell_command + if hasattr(cls, 'versions'): + if version not in cls.versions: + continue + try: + self.command_manager.add_command(cmd, cls) + except TypeError: + pass + def run(self, argv): """Equivalent to the main program for the application. diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 5f5e06a3e..86c79b417 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -217,7 +217,8 @@ def _test_create_resource(self, resource, cmd, name, myid, args, 'credential', 'network_profile', 'policy_profile', 'ikepolicy', 'ipsecpolicy', 'metering_label', - 'metering_label_rule', 'net_partition'] + 'metering_label_rule', 'net_partition', + 'fox_socket'] if not cmd_resource: cmd_resource = resource if (resource in non_admin_status_resources): diff --git a/neutronclient/tests/unit/test_client_extension.py b/neutronclient/tests/unit/test_client_extension.py new file mode 100644 index 000000000..fe1712be7 --- /dev/null +++ b/neutronclient/tests/unit/test_client_extension.py @@ -0,0 +1,87 @@ +# Copyright 2015 Rackspace Hosting 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 sys + +import mock + +from neutronclient.neutron.v2_0.contrib import _fox_sockets as fox_sockets +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20ExtensionJSON(test_cli20.CLITestV20Base): + def setUp(self): + # need to mock before super because extensions loaded on instantiation + self._mock_extension_loading() + super(CLITestV20ExtensionJSON, self).setUp(plurals={'tags': 'tag'}) + + def _create_patch(self, name, func=None): + patcher = mock.patch(name) + thing = patcher.start() + self.addCleanup(patcher.stop) + return thing + + def _mock_extension_loading(self): + ext_pkg = 'neutronclient.common.extension' + contrib = self._create_patch(ext_pkg + '._discover_via_entry_points') + iterator = iter([("_fox_sockets", fox_sockets)]) + contrib.return_value.__iter__.return_value = iterator + return contrib + + def test_delete_fox_socket(self): + """Delete fox socket: myid.""" + resource = 'fox_socket' + cmd = fox_sockets.FoxInSocketsDelete(test_cli20.MyApp(sys.stdout), + None) + myid = 'myid' + args = [myid] + self._test_delete_resource(resource, cmd, myid, args) + + def test_update_fox_socket(self): + """Update fox_socket: myid --name myname.""" + resource = 'fox_socket' + cmd = fox_sockets.FoxInSocketsUpdate(test_cli20.MyApp(sys.stdout), + None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--name', 'myname'], + {'name': 'myname'}) + + def test_create_fox_socket(self): + """Create fox_socket: myname.""" + resource = 'fox_socket' + cmd = fox_sockets.FoxInSocketsCreate(test_cli20.MyApp(sys.stdout), + None) + name = 'myname' + myid = 'myid' + args = [name, ] + position_names = ['name', ] + position_values = [name, ] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_list_fox_sockets(self): + """List fox_sockets.""" + resources = 'fox_sockets' + cmd = fox_sockets.FoxInSocketsList(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, True) + + def test_show_fox_socket(self): + """Show fox_socket: --fields id --fields name myid.""" + resource = 'fox_socket' + cmd = fox_sockets.FoxInSocketsShow(test_cli20.MyApp(sys.stdout), None) + args = ['--fields', 'id', '--fields', 'name', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, + args, ['id', 'name']) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 96405725b..0feac68e9 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -15,6 +15,8 @@ # under the License. # +import inspect +import itertools import logging import time @@ -24,6 +26,7 @@ from neutronclient import client from neutronclient.common import constants from neutronclient.common import exceptions +from neutronclient.common import extension as client_extension from neutronclient.common import serializer from neutronclient.common import utils from neutronclient.i18n import _ @@ -453,6 +456,36 @@ class Client(ClientBase): 'healthmonitors': 'healthmonitor', } + @APIParamsCall + def list_ext(self, path, **_params): + """Client extension hook for lists. + """ + return self.get(path, params=_params) + + @APIParamsCall + def show_ext(self, path, id, **_params): + """Client extension hook for shows. + """ + return self.get(path % id, params=_params) + + @APIParamsCall + def create_ext(self, path, body=None): + """Client extension hook for creates. + """ + return self.post(path, body=body) + + @APIParamsCall + def update_ext(self, path, id, body=None): + """Client extension hook for updates. + """ + return self.put(path % id, body=body) + + @APIParamsCall + def delete_ext(self, path, id): + """Client extension hook for deletes. + """ + return self.delete(path % id) + @APIParamsCall def get_quotas_tenant(self, **_params): """Fetch tenant info in server's context for following quota operation. @@ -1523,3 +1556,59 @@ def show_packet_filter(self, packet_filter_id, **_params): def delete_packet_filter(self, packet_filter_id): """Delete the specified packet filter.""" return self.delete(self.packet_filter_path % packet_filter_id) + + def __init__(self, **kwargs): + """Initialize a new client for the Neutron v2.0 API.""" + super(Client, self).__init__(**kwargs) + self._register_extensions(self.version) + + def extend_show(self, resource_plural, path): + def _fx(obj, **_params): + return self.show_ext(path, obj, **_params) + setattr(self, "show_%s" % resource_plural, _fx) + + def extend_list(self, resource_plural, path): + def _fx(**_params): + return self.list_ext(path, **_params) + setattr(self, "list_%s" % resource_plural, _fx) + + def extend_create(self, resource_singular, path): + def _fx(body=None): + return self.create_ext(path, body) + setattr(self, "create_%s" % resource_singular, _fx) + + def extend_delete(self, resource_singular, path): + def _fx(obj): + return self.delete_ext(path, obj) + setattr(self, "delete_%s" % resource_singular, _fx) + + def extend_update(self, resource_singular, path): + def _fx(obj, body=None): + return self.update_ext(path, obj, body) + setattr(self, "update_%s" % resource_singular, _fx) + + def _extend_client_with_module(self, module, version): + classes = inspect.getmembers(module, inspect.isclass) + for cls_name, cls in classes: + if hasattr(cls, 'versions'): + if version not in cls.versions: + continue + if issubclass(cls, client_extension.ClientExtensionList): + self.extend_list(cls.resource_plural, cls.object_path) + elif issubclass(cls, client_extension.ClientExtensionCreate): + self.extend_create(cls.resource, cls.object_path) + elif issubclass(cls, client_extension.ClientExtensionUpdate): + self.extend_update(cls.resource, cls.resource_path) + elif issubclass(cls, client_extension.ClientExtensionDelete): + self.extend_delete(cls.resource, cls.resource_path) + elif issubclass(cls, client_extension.ClientExtensionShow): + self.extend_show(cls.resource, cls.resource_path) + elif issubclass(cls, client_extension.NeutronClientExtension): + setattr(self, "%s_path" % cls.resource_plural, + cls.object_path) + setattr(self, "%s_path" % cls.resource, cls.resource_path) + + def _register_extensions(self, version): + for name, module in itertools.chain( + client_extension._discover_via_entry_points()): + self._extend_client_with_module(module, version) diff --git a/test-requirements.txt b/test-requirements.txt index 436948525..72514259e 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -8,6 +8,7 @@ coverage>=3.6 discover fixtures>=0.3.14 mox3>=0.7.0 +mock>=1.0 oslosphinx>=2.2.0 # Apache-2.0 oslotest>=1.2.0 # Apache-2.0 python-subunit>=0.0.18 From 6ca9a003a6e30ad11458f3a5ca1bb63c7bedfe94 Mon Sep 17 00:00:00 2001 From: Brandon Logan Date: Wed, 25 Feb 2015 12:18:03 -0600 Subject: [PATCH 167/845] Added client calls for the lbaas v2 agent scheduler The lbaas v2 agent requires separate REST calls from the v1 agent so it is necessary to also add these commands to the client. Change-Id: I2fa88e64094f74e198925f2fec397bf6f2f941d5 Depends-On: Ifc09e922e9afbdfa31d2edf022c28d56446a9403 Partially-implements: blueprint lbaas-refactor-haproxy-namespace-driver-to-new-driver-interface --- neutronclient/neutron/v2_0/agentscheduler.py | 53 +++++++++++++++++++ neutronclient/shell.py | 4 ++ .../tests/unit/test_cli20_agentschedulers.py | 24 +++++++++ neutronclient/v2_0/client.py | 15 ++++++ 4 files changed, 96 insertions(+) diff --git a/neutronclient/neutron/v2_0/agentscheduler.py b/neutronclient/neutron/v2_0/agentscheduler.py index e77f186db..6776b9928 100644 --- a/neutronclient/neutron/v2_0/agentscheduler.py +++ b/neutronclient/neutron/v2_0/agentscheduler.py @@ -275,3 +275,56 @@ def call_server(self, neutron_client, search_opts, parsed_args): agent = neutron_client.get_lbaas_agent_hosting_pool(**search_opts) data = {'agents': [agent['agent']]} return data + + +class ListLoadBalancersOnLbaasAgent(neutronV20.ListCommand): + """List the loadbalancers on a loadbalancer v2 agent.""" + + list_columns = ['id', 'name', 'admin_state_up', 'provisioning_status'] + resource = 'loadbalancer' + unknown_parts_flag = False + + def get_parser(self, prog_name): + parser = super(ListLoadBalancersOnLbaasAgent, self).get_parser( + prog_name) + parser.add_argument( + 'lbaas_agent', + help=_('ID of the loadbalancer agent to query.')) + return parser + + def call_server(self, neutron_client, search_opts, parsed_args): + data = neutron_client.list_loadbalancers_on_lbaas_agent( + parsed_args.lbaas_agent, **search_opts) + return data + + +class GetLbaasAgentHostingLoadBalancer(neutronV20.ListCommand): + """Get lbaas v2 agent hosting a loadbalancer. + + Deriving from ListCommand though server will return only one agent + to keep common output format for all agent schedulers + """ + + resource = 'agent' + list_columns = ['id', 'host', 'admin_state_up', 'alive'] + unknown_parts_flag = False + + def get_parser(self, prog_name): + parser = super(GetLbaasAgentHostingLoadBalancer, + self).get_parser(prog_name) + parser.add_argument('loadbalancer', + help=_('LoadBalancer to query.')) + return parser + + def extend_list(self, data, parsed_args): + for agent in data: + agent['alive'] = ":-)" if agent['alive'] else 'xxx' + + def call_server(self, neutron_client, search_opts, parsed_args): + _id = neutronV20.find_resourceid_by_name_or_id( + neutron_client, 'loadbalancer', parsed_args.loadbalancer) + search_opts['loadbalancer'] = _id + agent = neutron_client.get_lbaas_agent_hosting_loadbalancer( + **search_opts) + data = {'agents': [agent['agent']]} + return data \ No newline at end of file diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 953eae27b..96f675ca9 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -255,6 +255,10 @@ class BashCompletionCommand(openstack_command.OpenStackCommand): 'l3-agent-list-hosting-router': agentscheduler.ListL3AgentsHostingRouter, 'lb-pool-list-on-agent': agentscheduler.ListPoolsOnLbaasAgent, 'lb-agent-hosting-pool': agentscheduler.GetLbaasAgentHostingPool, + 'lbaas-loadbalancer-list-on-agent': + agentscheduler.ListLoadBalancersOnLbaasAgent, + 'lbaas-agent-hosting-loadbalancer': + agentscheduler.GetLbaasAgentHostingLoadBalancer, 'service-provider-list': servicetype.ListServiceProvider, 'firewall-rule-list': firewallrule.ListFirewallRule, 'firewall-rule-show': firewallrule.ShowFirewallRule, diff --git a/neutronclient/tests/unit/test_cli20_agentschedulers.py b/neutronclient/tests/unit/test_cli20_agentschedulers.py index 8dc307a8b..4c8992ee0 100644 --- a/neutronclient/tests/unit/test_cli20_agentschedulers.py +++ b/neutronclient/tests/unit/test_cli20_agentschedulers.py @@ -186,5 +186,29 @@ def test_get_lbaas_agent_hosting_pool(self): path=path, response_contents=contents) +class CLITestV20LBaaSV2AgentScheduler(test_cli20.CLITestV20Base): + + def test_list_loadbalancers_on_agent(self): + resources = 'loadbalancers' + cmd = agentscheduler.ListLoadBalancersOnLbaasAgent( + test_cli20.MyApp(sys.stdout), None) + agent_id = 'agent_id1' + path = ((self.client.agent_path + self.client.AGENT_LOADBALANCERS) % + agent_id) + self._test_list_resources(resources, cmd, base_args=[agent_id], + path=path) + + def test_get_lbaas_agent_hosting_pool(self): + resources = 'agent' + cmd = agentscheduler.GetLbaasAgentHostingLoadBalancer( + test_cli20.MyApp(sys.stdout), None) + lb_id = 'lb_id1' + path = ((self.client.lbaas_loadbalancer_path + + self.client.LOADBALANCER_HOSTING_AGENT) % lb_id) + contents = {self.id_field: 'myid1', 'alive': True} + self._test_list_resources(resources, cmd, base_args=[lb_id], + path=path, response_contents=contents) + + class CLITestV20LBaaSAgentSchedulerXML(CLITestV20LBaaSAgentScheduler): format = 'xml' diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 96405725b..87fca7c51 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -410,6 +410,8 @@ class Client(ClientBase): L3_AGENTS = '/l3-agents' LOADBALANCER_POOLS = '/loadbalancer-pools' LOADBALANCER_AGENT = '/loadbalancer-agent' + AGENT_LOADBALANCERS = '/agent-loadbalancers' + LOADBALANCER_HOSTING_AGENT = '/loadbalancer-hosting-agent' firewall_rules_path = "/fw/firewall_rules" firewall_rule_path = "/fw/firewall_rules/%s" firewall_policies_path = "/fw/firewall_policies" @@ -1351,6 +1353,19 @@ def list_pools_on_lbaas_agent(self, lbaas_agent, **_params): return self.get((self.agent_path + self.LOADBALANCER_POOLS) % lbaas_agent, params=_params) + @APIParamsCall + def get_lbaas_agent_hosting_loadbalancer(self, loadbalancer, **_params): + """Fetches a loadbalancer agent hosting a loadbalancer.""" + return self.get((self.lbaas_loadbalancer_path + + self.LOADBALANCER_HOSTING_AGENT) % loadbalancer, + params=_params) + + @APIParamsCall + def list_loadbalancers_on_lbaas_agent(self, lbaas_agent, **_params): + """Fetches a list of loadbalancers hosted by the loadbalancer agent.""" + return self.get((self.agent_path + self.AGENT_LOADBALANCERS) % + lbaas_agent, params=_params) + @APIParamsCall def list_service_providers(self, retrieve_all=True, **_params): """Fetches service providers.""" From aa0c39fe2d2b1cd6d83987644928fe5c8811789e Mon Sep 17 00:00:00 2001 From: Brandon Logan Date: Fri, 27 Feb 2015 02:10:10 -0600 Subject: [PATCH 168/845] Fixed pool and health monitor create bugs lbaas-pool-create now accepts --name as an optional argument and no longer lists healthmonitor_id as an acceptable argument. lbaas-create-healthmonitor now accepts --pool as an argument so a healthmonitor can be added to a pool. Change-Id: I7c5ae61a5278119bf5c3999f4b295bd8a291d777 Closes-Bug: #1426246 Closes-Bug: #1426253 --- .../neutron/v2_0/lb/v2/healthmonitor.py | 8 ++++++++ neutronclient/neutron/v2_0/lb/v2/pool.py | 11 +++-------- .../unit/lb/v2/test_cli20_healthmonitor.py | 14 ++++++++------ .../tests/unit/lb/v2/test_cli20_pool.py | 18 +++++++----------- 4 files changed, 26 insertions(+), 25 deletions(-) diff --git a/neutronclient/neutron/v2_0/lb/v2/healthmonitor.py b/neutronclient/neutron/v2_0/lb/v2/healthmonitor.py index 23136f74f..ff1c11e3c 100644 --- a/neutronclient/neutron/v2_0/lb/v2/healthmonitor.py +++ b/neutronclient/neutron/v2_0/lb/v2/healthmonitor.py @@ -86,8 +86,15 @@ def add_known_arguments(self, parser): '--type', required=True, choices=['PING', 'TCP', 'HTTP', 'HTTPS'], help=_('One of the predefined health monitor types.')) + parser.add_argument( + '--pool', required=True, + help=_('ID or name of the pool that this healthmonitor will ' + 'monitor.')) def args2body(self, parsed_args): + pool_id = neutronV20.find_resourceid_by_name_or_id( + self.get_client(), 'pool', parsed_args.pool, + cmd_resource='lbaas_pool') body = { self.resource: { 'admin_state_up': parsed_args.admin_state, @@ -95,6 +102,7 @@ def args2body(self, parsed_args): 'max_retries': parsed_args.max_retries, 'timeout': parsed_args.timeout, 'type': parsed_args.type, + 'pool_id': pool_id }, } neutronV20.update_dict(parsed_args, body[self.resource], diff --git a/neutronclient/neutron/v2_0/lb/v2/pool.py b/neutronclient/neutron/v2_0/lb/v2/pool.py index 11644be45..7182171b3 100644 --- a/neutronclient/neutron/v2_0/lb/v2/pool.py +++ b/neutronclient/neutron/v2_0/lb/v2/pool.py @@ -75,12 +75,11 @@ def add_known_arguments(self, parser): parser.add_argument( '--description', help=_('Description of the pool.')) - parser.add_argument( - '--healthmonitor-id', - help=_('ID of the health monitor to use.')) parser.add_argument( '--session-persistence', metavar='TYPE:VALUE', help=_('The type of session persistence to use.')) + parser.add_argument( + '--name', help=_('The name of the pool.')) parser.add_argument( '--lb-algorithm', required=True, @@ -96,9 +95,6 @@ def add_known_arguments(self, parser): required=True, choices=['HTTP', 'HTTPS', 'TCP'], help=_('Protocol for balancing.')) - parser.add_argument( - 'name', metavar='NAME', - help=_('The name of the pool.')) def args2body(self, parsed_args): if parsed_args.session_persistence: @@ -107,7 +103,6 @@ def args2body(self, parsed_args): self.get_client(), 'listener', parsed_args.listener) body = { self.resource: { - 'name': parsed_args.name, 'admin_state_up': parsed_args.admin_state, 'protocol': parsed_args.protocol, 'lb_algorithm': parsed_args.lb_algorithm, @@ -115,7 +110,7 @@ def args2body(self, parsed_args): }, } neutronV20.update_dict(parsed_args, body[self.resource], - ['description', 'healthmonitor_id', + ['description', 'name', 'session_persistence']) return body diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py b/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py index 20af12762..96f96a68f 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py @@ -35,10 +35,11 @@ def test_create_healthmonitor_with_mandatory_params(self): max_retries = '3' delay = '10' timeout = '60' + pool = 'pool1' args = ['--type', type, '--max-retries', max_retries, - '--delay', delay, '--timeout', timeout] - position_names = ['type', 'max_retries', 'delay', 'timeout'] - position_values = [type, max_retries, delay, timeout] + '--delay', delay, '--timeout', timeout, '--pool', pool] + position_names = ['type', 'max_retries', 'delay', 'timeout', 'pool_id'] + position_values = [type, max_retries, delay, timeout, pool] self._test_create_resource(resource, cmd, '', my_id, args, position_names, position_values, cmd_resource=cmd_resource) @@ -57,15 +58,16 @@ def test_create_healthmonitor_with_all_params(self): http_method = 'GET' expected_codes = '201' url_path = '/somepath' + pool = 'pool1' args = ['--admin-state-down', '--http-method', http_method, '--expected-codes', expected_codes, '--url-path', url_path, '--type', type, '--max-retries', max_retries, - '--delay', delay, '--timeout', timeout] + '--delay', delay, '--timeout', timeout, '--pool', pool] position_names = ['admin_state_up', 'http_method', 'expected_codes', 'url_path', 'type', 'max_retries', 'delay', - 'timeout'] + 'timeout', 'pool_id'] position_values = [False, http_method, expected_codes, url_path, - type, max_retries, delay, timeout] + type, max_retries, delay, timeout, pool] self._test_create_resource(resource, cmd, '', my_id, args, position_names, position_values, cmd_resource=cmd_resource) diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_pool.py b/neutronclient/tests/unit/lb/v2/test_cli20_pool.py index daf3e5292..77b565562 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_pool.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_pool.py @@ -33,12 +33,11 @@ def test_create_pool_with_mandatory_params(self): lb_algorithm = 'ROUND_ROBIN' listener = 'listener' protocol = 'TCP' - name = 'my-pool' args = ['--lb-algorithm', lb_algorithm, '--protocol', protocol, - '--listener', listener, name] + '--listener', listener] position_names = ['admin_state_up', 'lb_algorithm', 'protocol', - 'listener_id', 'name'] - position_values = [True, lb_algorithm, protocol, listener, name] + 'listener_id'] + position_values = [True, lb_algorithm, protocol, listener] self._test_create_resource(resource, cmd, '', my_id, args, position_names, position_values, cmd_resource=cmd_resource) @@ -56,19 +55,16 @@ def test_create_pool_with_all_params(self): session_persistence_str = 'HTTP_COOKIE:1234' session_persistence = {'type': 'HTTP_COOKIE', 'cookie_name': '1234'} - healthmon_id = 'healthmon-id' name = 'my-pool' args = ['--lb-algorithm', lb_algorithm, '--protocol', protocol, '--description', description, '--session-persistence', - session_persistence_str, '--healthmonitor-id', - healthmon_id, '--admin-state-down', name, + session_persistence_str, '--admin-state-down', '--name', name, '--listener', listener] position_names = ['lb_algorithm', 'protocol', 'description', - 'session_persistence', 'healthmonitor_id', - 'admin_state_up', 'listener_id', 'name'] + 'session_persistence', 'admin_state_up', 'name', + 'listener_id'] position_values = [lb_algorithm, protocol, description, - session_persistence, healthmon_id, - False, listener, name] + session_persistence, False, name, listener] self._test_create_resource(resource, cmd, '', my_id, args, position_names, position_values, cmd_resource=cmd_resource) From 17f0ca36a93ba862d471d50de6c50b4be1a95dfe Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Wed, 4 Mar 2015 01:34:40 +0900 Subject: [PATCH 169/845] Ignore order of query parameters when compared in MyUrlComparator Previously the order of query parameters are strictly checked in MyUrlComparator, but there are cases where the order is not predictable for example field parameters are generated from 'set'. In this commit query parameters in lhs and rhs are compared ignoring its order. Change-Id: Ie720f0ec290722ff979b1fa8ea89e47aacd6225c Closes-Bug: #1422866 --- neutronclient/tests/unit/test_cli20.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 86c79b417..df92e4d15 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -91,10 +91,14 @@ def equals(self, rhs): lhsp = urlparse.urlparse(self.lhs) rhsp = urlparse.urlparse(rhs) + lhs_qs = urlparse.parse_qsl(lhsp.query) + rhs_qs = urlparse.parse_qsl(rhsp.query) + return (lhsp.scheme == rhsp.scheme and lhsp.netloc == rhsp.netloc and lhsp.path == rhsp.path and - urlparse.parse_qs(lhsp.query) == urlparse.parse_qs(rhsp.query)) + len(lhs_qs) == len(rhs_qs) and + set(lhs_qs) == set(rhs_qs)) def __str__(self): if self.client and self.client.format != FORMAT: From ada1568809168fcf9d3024b09c3d90af4eeee7ec Mon Sep 17 00:00:00 2001 From: watsalya Date: Sat, 14 Feb 2015 00:58:00 +0530 Subject: [PATCH 170/845] "neutron help router-update" help info updated Initially we were unable to see complete help info for "neutron router-update" command. Now, with this patch complete help info is displayed to the user. Apart from the existing information, the following information will also be displayed:- --name --admin-state-up --distributed Change-Id: I149855e86a2d874c10f1ff315a2627e0f0f306c7 Closes-Bug: #1401030 --- neutronclient/neutron/v2_0/router.py | 25 +++++++++++ neutronclient/tests/unit/test_cli20_router.py | 42 +++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index 611259603..907f19d90 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -92,6 +92,31 @@ class UpdateRouter(neutronV20.UpdateCommand): resource = 'router' + def add_known_arguments(self, parser): + parser.add_argument( + '--name', + help=_('Name of this router.')) + utils.add_boolean_argument( + parser, '--admin-state-up', dest='admin_state', + help=_('Specify the administrative state of the router' + ' (True meaning "Up")')) + utils.add_boolean_argument( + parser, '--admin_state_up', dest='admin_state', + help=argparse.SUPPRESS) + utils.add_boolean_argument( + parser, '--distributed', dest='distributed', + help=_('True means this router should operate in' + ' distributed mode.')) + + def args2body(self, parsed_args): + body = {self.resource: {}} + if hasattr(parsed_args, 'admin_state'): + body[self.resource].update( + {'admin_state_up': parsed_args.admin_state}) + neutronV20.update_dict(parsed_args, body[self.resource], + ['name', 'distributed']) + return body + class RouterInterfaceCommand(neutronV20.NeutronCommand): """Based class to Add/Remove router interface.""" diff --git a/neutronclient/tests/unit/test_cli20_router.py b/neutronclient/tests/unit/test_cli20_router.py index 87e878aa6..d8151cdc0 100644 --- a/neutronclient/tests/unit/test_cli20_router.py +++ b/neutronclient/tests/unit/test_cli20_router.py @@ -154,6 +154,48 @@ def test_update_router(self): {'name': 'myname'} ) + def test_update_router_admin_state(self): + """Update router: myid --admin-state-up .""" + resource = 'router' + cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--admin-state-up', 'True'], + {'admin_state_up': 'True'} + ) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--admin-state-up', 'true'], + {'admin_state_up': 'true'} + ) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--admin-state-up', 'False'], + {'admin_state_up': 'False'} + ) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--admin-state-up', 'false'], + {'admin_state_up': 'false'} + ) + + def test_update_router_distributed(self): + """Update router: myid --distributed .""" + resource = 'router' + cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--distributed', 'True'], + {'distributed': 'True'} + ) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--distributed', 'true'], + {'distributed': 'true'} + ) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--distributed', 'False'], + {'distributed': 'False'} + ) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--distributed', 'false'], + {'distributed': 'false'} + ) + def test_delete_router(self): """Delete router: myid.""" resource = 'router' From ca52c272bbb5ef6d9ed3d5c795c8aca4b17bb242 Mon Sep 17 00:00:00 2001 From: Kyle Mestery Date: Fri, 6 Mar 2015 22:19:15 +0000 Subject: [PATCH 171/845] Add OS_TEST_PATH to testr Change default test path to unit tets, and support setting $OS_TEST_PATH to specify a different path (such as functional). Change-Id: Id58351f02e79b35990719ea1698a2fd2169a9cf6 Partial-Bug: #1429289 --- .testr.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.testr.conf b/.testr.conf index d152a5aa9..01dee7036 100644 --- a/.testr.conf +++ b/.testr.conf @@ -1,4 +1,4 @@ [DEFAULT] -test_command=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 ${PYTHON:-python} -m subunit.run discover -t ./ . $LISTOPT $IDOPTION +test_command=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 ${PYTHON:-python} -m subunit.run discover -t ./ ${OS_TEST_PATH:-./neutronclient/tests/unit} $LISTOPT $IDOPTION test_id_option=--load-list $IDFILE test_list_option=--list From 9b3b253399130b98e00a823cbcbafcf5420fefa4 Mon Sep 17 00:00:00 2001 From: Kyle Mestery Date: Fri, 6 Mar 2015 22:47:22 +0000 Subject: [PATCH 172/845] First pass at tempest-lib based functional testing Begin moving neutron CLI tests out of tempest and into this repo using tempest-lib. This patch adds the framework to run the functional tests, later patches will port the existing tempest tests. Change-Id: I1d168d7f255d08e7d7d0193b1d470b9c957c3ae8 Partila-Bug: #1429289 --- neutronclient/tests/functional/__init__.py | 0 neutronclient/tests/functional/base.py | 44 +++++++++++++++++ .../tests/functional/test_readonly_neutron.py | 47 +++++++++++++++++++ test-requirements.txt | 1 + tox.ini | 4 ++ 5 files changed, 96 insertions(+) create mode 100644 neutronclient/tests/functional/__init__.py create mode 100644 neutronclient/tests/functional/base.py create mode 100644 neutronclient/tests/functional/test_readonly_neutron.py diff --git a/neutronclient/tests/functional/__init__.py b/neutronclient/tests/functional/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/tests/functional/base.py b/neutronclient/tests/functional/base.py new file mode 100644 index 000000000..48561d6c7 --- /dev/null +++ b/neutronclient/tests/functional/base.py @@ -0,0 +1,44 @@ +# 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 os + +from tempest_lib.cli import base + + +class ClientTestBase(base.ClientTestBase): + """This is a first pass at a simple read only python-neutronclient test. + This only exercises client commands that are read only. + + This should test commands: + * as a regular user + * as a admin user + * with and without optional parameters + * initially just check return codes, and later test command outputs + + """ + + def _get_clients(self): + cli_dir = os.environ.get( + 'OS_NEUTRONCLIENT_EXEC_DIR', + os.path.join(os.path.abspath('.'), '.tox/functional/bin')) + + return base.CLIClient( + username=os.environ.get('OS_USERNAME'), + password=os.environ.get('OS_PASSWORD'), + tenant_name=os.environ.get('OS_TENANT_NAME'), + uri=os.environ.get('OS_AUTH_URL'), + cli_dir=cli_dir) + + def neutron(self, *args, **kwargs): + return self.clients.neutron(*args, + **kwargs) diff --git a/neutronclient/tests/functional/test_readonly_neutron.py b/neutronclient/tests/functional/test_readonly_neutron.py new file mode 100644 index 000000000..d4bf9c048 --- /dev/null +++ b/neutronclient/tests/functional/test_readonly_neutron.py @@ -0,0 +1,47 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from tempest_lib import exceptions + +from neutronclient.tests.functional import base + + +class SimpleReadOnlyNeutronClientTest(base.ClientTestBase): + + """This is a first pass at a simple read only python-neutronclient test. + This only exercises client commands that are read only. + + This should test commands: + * as a regular user + * as a admin user + * with and without optional parameters + * initially just check return codes, and later test command outputs + + """ + + def test_admin_fake_action(self): + self.assertRaises(exceptions.CommandFailed, + self.neutron, + 'this-does-neutron-exist') + + # NOTE(mestery): Commands in order listed in 'neutron help' + + # Optional arguments: + + def test_admin_version(self): + self.neutron('', flags='--version') + + def test_admin_debug_list(self): + self.neutron('net-list', flags='--debug') + + def test_admin_timeout(self): + self.neutron('net-list', flags='--http-timeout %d' % 10) diff --git a/test-requirements.txt b/test-requirements.txt index 72514259e..08b1781d1 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -16,3 +16,4 @@ requests-mock>=0.5.1 # Apache-2.0 sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3 testrepository>=0.0.18 testtools>=0.9.36,!=1.2.0 +tempest-lib>=0.3.0 diff --git a/tox.ini b/tox.ini index 4187fb854..dbed9e67a 100644 --- a/tox.ini +++ b/tox.ini @@ -22,6 +22,10 @@ distribute = false [testenv:venv] commands = {posargs} +[testenv:functional] +setenv = + OS_TEST_PATH = ./neutronclient/tests/functional + [testenv:cover] commands = python setup.py testr --coverage --testr-args='{posargs}' From 014d4e7dd8c6d4c28342ce06f7156f206bf8b92b Mon Sep 17 00:00:00 2001 From: Kyle Mestery Date: Fri, 6 Mar 2015 23:02:56 +0000 Subject: [PATCH 173/845] Add post_test_hook for functional tests Add post_test_hook to get functional job working for python-neutronclient. Once the project config side of this lands this script can be self gating. Change-Id: I21e691155f69b83940aa859d77e16c4289d9f78c Partial-Bug: #1429289 --- .../tests/functional/hooks/post_test_hook.sh | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 neutronclient/tests/functional/hooks/post_test_hook.sh diff --git a/neutronclient/tests/functional/hooks/post_test_hook.sh b/neutronclient/tests/functional/hooks/post_test_hook.sh new file mode 100644 index 000000000..e0c966939 --- /dev/null +++ b/neutronclient/tests/functional/hooks/post_test_hook.sh @@ -0,0 +1,50 @@ +#!/bin/bash -xe + +# 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. + +# This script is executed inside post_test_hook function in devstack gate. + +function generate_testr_results { + if [ -f .testrepository/0 ]; then + sudo .tox/functional/bin/testr last --subunit > $WORKSPACE/testrepository.subunit + sudo mv $WORKSPACE/testrepository.subunit $BASE/logs/testrepository.subunit + sudo .tox/functional/bin/python /usr/local/jenkins/slave_scripts/subunit2html.py $BASE/logs/testrepository.subunit $BASE/logs/testr_results.html + sudo gzip -9 $BASE/logs/testrepository.subunit + sudo gzip -9 $BASE/logs/testr_results.html + sudo chown jenkins:jenkins $BASE/logs/testrepository.subunit.gz $BASE/logs/testr_results.html.gz + sudo chmod a+r $BASE/logs/testrepository.subunit.gz $BASE/logs/testr_results.html.gz + fi +} + +export NEUTRONCLIENT_DIR="$BASE/new/python-neutronclient" + +# Get admin credentials +cd $BASE/new/devstack +source openrc admin admin + +# Go to the neutronclient dir +cd $NEUTRONCLIENT_DIR + +sudo chown -R jenkins:stack $NEUTRONCLIENT_DIR + +# Run tests +echo "Running neutronclient functional test suite" +set +e +# Preserve env for OS_ credentials +sudo -E -H -u jenkins tox -efunctional +EXIT_CODE=$? +set -e + +# Collect and parse result +generate_testr_results +exit $EXIT_CODE From 4b6ed760d4303744907feefd81e60f38ae3750ef Mon Sep 17 00:00:00 2001 From: Mark Rawlings Date: Tue, 3 Mar 2015 18:03:58 +0000 Subject: [PATCH 174/845] Reinstate Max URI length checking to V2_0 Client A previous commit 799e288f48e5d99731dedbfb94808a8cbe01c05c has broken the ability to recognise long URIs (>8192) and raise an exception used to prevent the excessive URI being sent to the server. This impacts the commands net-list and security-group-rule-list. The previous edit removed one of the duplicate _check_uri_length functions. This edit 'swaps' the removal and updates the unit test stubbing accordingly. The capability to split excessive URIs into managable chunks remained in the code, but no longer executed because this exception was not raised. It should be noted that as a side effect of recognising the long URI, data is returned with the exception that indicates 'how excessive' the URI length was. This allows the URI to be broken into chunks that will be supported. Restore the capability to recognise that an excessive URI has been provided and to raise the expected exception with related data. Closes-Bug: #1422736 Change-Id: I1b719bed406b83c5f2deac06e127798a91f51ad7 --- neutronclient/client.py | 10 ---------- neutronclient/tests/unit/test_cli20.py | 11 +++++++++++ neutronclient/tests/unit/test_cli20_network.py | 6 +++--- neutronclient/tests/unit/test_cli20_securitygroup.py | 6 +++--- neutronclient/v2_0/client.py | 11 +++++++++++ 5 files changed, 28 insertions(+), 16 deletions(-) diff --git a/neutronclient/client.py b/neutronclient/client.py index 0a68ff063..bc645f195 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -48,9 +48,6 @@ class HTTPClient(object): USER_AGENT = 'python-neutronclient' CONTENT_TYPE = 'application/json' - # 8192 Is the default max URI len for eventlet.wsgi.server - MAX_URI_LEN = 8192 - def __init__(self, username=None, user_id=None, tenant_name=None, tenant_id=None, password=None, auth_url=None, @@ -149,16 +146,9 @@ def request(self, url, method, body=None, headers=None, **kwargs): return resp, resp.text - def _check_uri_length(self, action): - uri_len = len(self.endpoint_url) + len(action) - if uri_len > self.MAX_URI_LEN: - raise exceptions.RequestURITooLong( - excess=uri_len - self.MAX_URI_LEN) - def do_request(self, url, method, **kwargs): # Ensure client always has correct uri - do not guesstimate anything self.authenticate_and_fetch_endpoint_url() - self._check_uri_length(url) # Perform the request once. If we get a 401 back then it # might be because the auth token expired, so try to diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 86c79b417..3f360c705 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -618,6 +618,17 @@ def test_do_request_error_without_response_body(self): self.mox.VerifyAll() self.mox.UnsetStubs() + def test_do_request_with_long_uri_exception(self): + long_string = 'x' * 8200 # 8200 > MAX_URI_LEN:8192 + params = {'id': long_string} + + try: + self.client.do_request('GET', '/test', body='', params=params) + except exceptions.RequestURITooLong as cm: + self.assertNotEqual(cm.excess, 0) + else: + self.fail('Expected exception NOT raised') + class ClientV2UnicodeTestXML(ClientV2TestJson): format = 'xml' diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index 1ac49b637..31754be26 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -551,14 +551,14 @@ def mox_calls(path, data): filters, response = self._build_test_data(data) # 1 char of extra URI len will cause a split in 2 requests - self.mox.StubOutWithMock(self.client.httpclient, + self.mox.StubOutWithMock(self.client, "_check_uri_length") - self.client.httpclient._check_uri_length(mox.IgnoreArg()).AndRaise( + self.client._check_uri_length(mox.IgnoreArg()).AndRaise( exceptions.RequestURITooLong(excess=1)) for data in sub_data_lists: filters, response = self._build_test_data(data) - self.client.httpclient._check_uri_length( + self.client._check_uri_length( mox.IgnoreArg()).AndReturn(None) self.client.httpclient.request( test_cli20.MyUrlComparator( diff --git a/neutronclient/tests/unit/test_cli20_securitygroup.py b/neutronclient/tests/unit/test_cli20_securitygroup.py index ec18fb8ee..8032e0390 100644 --- a/neutronclient/tests/unit/test_cli20_securitygroup.py +++ b/neutronclient/tests/unit/test_cli20_securitygroup.py @@ -265,14 +265,14 @@ def mox_calls(path, data): def test_extend_list_exceed_max_uri_len(self): def mox_calls(path, data): # 1 char of extra URI len will cause a split in 2 requests - self.mox.StubOutWithMock(self.client.httpclient, + self.mox.StubOutWithMock(self.client, '_check_uri_length') - self.client.httpclient._check_uri_length(mox.IgnoreArg()).AndRaise( + self.client._check_uri_length(mox.IgnoreArg()).AndRaise( exceptions.RequestURITooLong(excess=1)) responses = self._build_test_data(data, excess=1) for item in responses: - self.client.httpclient._check_uri_length( + self.client._check_uri_length( mox.IgnoreArg()).AndReturn(None) self.client.httpclient.request( test_cli20.end_url(path, item['filter']), diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 0feac68e9..2e63bb2a2 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -184,6 +184,12 @@ def _handle_fault_response(self, status_code, response_body): # Raise the appropriate exception exception_handler_v20(status_code, des_error_body) + def _check_uri_length(self, action): + uri_len = len(self.httpclient.endpoint_url) + len(action) + if uri_len > self.MAX_URI_LEN: + raise exceptions.RequestURITooLong( + excess=uri_len - self.MAX_URI_LEN) + def do_request(self, method, action, body=None, headers=None, params=None): # Add format and tenant_id action += ".%s" % self.format @@ -192,6 +198,8 @@ def do_request(self, method, action, body=None, headers=None, params=None): params = utils.safe_encode_dict(params) action += '?' + urlparse.urlencode(params, doseq=1) + self._check_uri_length(action) + if body: body = self.serialize(body) @@ -456,6 +464,9 @@ class Client(ClientBase): 'healthmonitors': 'healthmonitor', } + # 8192 Is the default max URI len for eventlet.wsgi.server + MAX_URI_LEN = 8192 + @APIParamsCall def list_ext(self, path, **_params): """Client extension hook for lists. From 0e9d1e5eabfbb01599aef4f3e74abb02f112450d Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Wed, 11 Mar 2015 14:00:41 +0900 Subject: [PATCH 175/845] exec permission to port_test_hook.sh Change-Id: I516b27a7dc59a2e2fd7fb8128aa35c43a8c0b5b8 --- neutronclient/tests/functional/hooks/post_test_hook.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 neutronclient/tests/functional/hooks/post_test_hook.sh diff --git a/neutronclient/tests/functional/hooks/post_test_hook.sh b/neutronclient/tests/functional/hooks/post_test_hook.sh old mode 100644 new mode 100755 From 30b198edec988d7c969c1edd4a2754bd9c9c79f4 Mon Sep 17 00:00:00 2001 From: Doug Fish Date: Wed, 11 Mar 2015 08:15:03 -0500 Subject: [PATCH 176/845] Remove unused AlreadyAttachedClient This exception is no longer referenced in Horizon. It should be removed as noted in the TODO being removed. Change-Id: I9970ca375d19476c12891475d0a8c0c559e0507f --- neutronclient/common/exceptions.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/neutronclient/common/exceptions.py b/neutronclient/common/exceptions.py index 9728fbdd7..9a12f1995 100644 --- a/neutronclient/common/exceptions.py +++ b/neutronclient/common/exceptions.py @@ -148,12 +148,6 @@ class OverQuotaClient(Conflict): pass -# TODO(amotoki): It is unused in Neutron, but it is referred to -# in Horizon code. After Horizon code is updated, remove it. -class AlreadyAttachedClient(Conflict): - pass - - class IpAddressGenerationFailureClient(Conflict): pass From a4a50878f2b4d723415e1611d5223da15aab67c1 Mon Sep 17 00:00:00 2001 From: Kyle Mestery Date: Tue, 10 Mar 2015 02:44:23 +0000 Subject: [PATCH 177/845] Copy functional tests from tempest cli This copies in all the tests from the tempest file tempest/cli/simple_read_only/network/test_neutron.py in preparation for removing that file. Change-Id: Icaaaa094c1637f5d0e5296491d349a282fadc2cc Closes-Bug: #1429289 Depends-On: I00e56cab162941bb9d9bd84d0fffd66e9698bb4c --- .../tests/functional/test_readonly_neutron.py | 160 +++++++++++++++++- 1 file changed, 156 insertions(+), 4 deletions(-) diff --git a/neutronclient/tests/functional/test_readonly_neutron.py b/neutronclient/tests/functional/test_readonly_neutron.py index d4bf9c048..a552ef7ac 100644 --- a/neutronclient/tests/functional/test_readonly_neutron.py +++ b/neutronclient/tests/functional/test_readonly_neutron.py @@ -10,6 +10,8 @@ # License for the specific language governing permissions and limitations # under the License. +import re + from tempest_lib import exceptions from neutronclient.tests.functional import base @@ -37,11 +39,161 @@ def test_admin_fake_action(self): # Optional arguments: - def test_admin_version(self): + def test_neutron_fake_action(self): + self.assertRaises(exceptions.CommandFailed, + self.neutron, + 'this-does-not-exist') + + def test_neutron_net_list(self): + net_list = self.parser.listing(self.neutron('net-list')) + self.assertTableStruct(net_list, ['id', 'name', 'subnets']) + + def test_neutron_ext_list(self): + ext = self.parser.listing(self.neutron('ext-list')) + self.assertTableStruct(ext, ['alias', 'name']) + + def test_neutron_dhcp_agent_list_hosting_net(self): + self.neutron('dhcp-agent-list-hosting-net', + params='private') + + def test_neutron_agent_list(self): + agents = self.parser.listing(self.neutron('agent-list')) + field_names = ['id', 'agent_type', 'host', 'alive', 'admin_state_up'] + self.assertTableStruct(agents, field_names) + + def test_neutron_floatingip_list(self): + self.neutron('floatingip-list') + + def test_neutron_meter_label_list(self): + self.neutron('meter-label-list') + + def test_neutron_meter_label_rule_list(self): + self.neutron('meter-label-rule-list') + + def _test_neutron_lbaas_command(self, command): + try: + self.neutron(command) + except exceptions.CommandFailed as e: + if '404 Not Found' not in e.stderr: + self.fail('%s: Unexpected failure.' % command) + + def test_neutron_lb_healthmonitor_list(self): + self._test_neutron_lbaas_command('lb-healthmonitor-list') + + def test_neutron_lb_member_list(self): + self._test_neutron_lbaas_command('lb-member-list') + + def test_neutron_lb_pool_list(self): + self._test_neutron_lbaas_command('lb-pool-list') + + def test_neutron_lb_vip_list(self): + self._test_neutron_lbaas_command('lb-vip-list') + + def test_neutron_net_external_list(self): + net_ext_list = self.parser.listing(self.neutron('net-external-list')) + self.assertTableStruct(net_ext_list, ['id', 'name', 'subnets']) + + def test_neutron_port_list(self): + port_list = self.parser.listing(self.neutron('port-list')) + self.assertTableStruct(port_list, ['id', 'name', 'mac_address', + 'fixed_ips']) + + def test_neutron_quota_list(self): + self.neutron('quota-list') + + def test_neutron_router_list(self): + router_list = self.parser.listing(self.neutron('router-list')) + self.assertTableStruct(router_list, ['id', 'name', + 'external_gateway_info']) + + def test_neutron_security_group_list(self): + security_grp = self.parser.listing(self.neutron('security-group-list')) + self.assertTableStruct(security_grp, ['id', 'name', 'description']) + + def test_neutron_security_group_rule_list(self): + security_grp = self.parser.listing(self.neutron + ('security-group-rule-list')) + self.assertTableStruct(security_grp, ['id', 'security_group', + 'direction', 'protocol', + 'remote_ip_prefix', + 'remote_group']) + + def test_neutron_subnet_list(self): + subnet_list = self.parser.listing(self.neutron('subnet-list')) + self.assertTableStruct(subnet_list, ['id', 'name', 'cidr', + 'allocation_pools']) + + def test_neutron_vpn_ikepolicy_list(self): + ikepolicy = self.parser.listing(self.neutron('vpn-ikepolicy-list')) + self.assertTableStruct(ikepolicy, ['id', 'name', + 'auth_algorithm', + 'encryption_algorithm', + 'ike_version', 'pfs']) + + def test_neutron_vpn_ipsecpolicy_list(self): + ipsecpolicy = self.parser.listing(self.neutron('vpn-ipsecpolicy-list')) + self.assertTableStruct(ipsecpolicy, ['id', 'name', + 'auth_algorithm', + 'encryption_algorithm', + 'pfs']) + + def test_neutron_vpn_service_list(self): + vpn_list = self.parser.listing(self.neutron('vpn-service-list')) + self.assertTableStruct(vpn_list, ['id', 'name', + 'router_id', 'status']) + + def test_neutron_ipsec_site_connection_list(self): + ipsec_site = self.parser.listing(self.neutron + ('ipsec-site-connection-list')) + self.assertTableStruct(ipsec_site, ['id', 'name', + 'peer_address', + 'peer_cidrs', + 'route_mode', + 'auth_mode', 'status']) + + def test_neutron_firewall_list(self): + firewall_list = self.parser.listing(self.neutron + ('firewall-list')) + self.assertTableStruct(firewall_list, ['id', 'name', + 'firewall_policy_id']) + + def test_neutron_firewall_policy_list(self): + firewall_policy = self.parser.listing(self.neutron + ('firewall-policy-list')) + self.assertTableStruct(firewall_policy, ['id', 'name', + 'firewall_rules']) + + def test_neutron_firewall_rule_list(self): + firewall_rule = self.parser.listing(self.neutron + ('firewall-rule-list')) + self.assertTableStruct(firewall_rule, ['id', 'name', + 'firewall_policy_id', + 'summary', 'enabled']) + + def test_neutron_help(self): + help_text = self.neutron('help') + lines = help_text.split('\n') + self.assertFirstLineStartsWith(lines, 'usage: neutron') + + commands = [] + cmds_start = lines.index('Commands for API v2.0:') + command_pattern = re.compile('^ {2}([a-z0-9\-\_]+)') + for line in lines[cmds_start:]: + match = command_pattern.match(line) + if match: + commands.append(match.group(1)) + commands = set(commands) + wanted_commands = set(('net-create', 'subnet-list', 'port-delete', + 'router-show', 'agent-update', 'help')) + self.assertFalse(wanted_commands - commands) + + # Optional arguments: + + def test_neutron_version(self): self.neutron('', flags='--version') - def test_admin_debug_list(self): + def test_neutron_debug_net_list(self): self.neutron('net-list', flags='--debug') - def test_admin_timeout(self): - self.neutron('net-list', flags='--http-timeout %d' % 10) + def test_neutron_quiet_net_list(self): + self.neutron('net-list', flags='--quiet') From 6e4841359d50c31bffea00b74269e94a05e2442a Mon Sep 17 00:00:00 2001 From: ptoohill1 Date: Wed, 4 Mar 2015 14:26:29 -0600 Subject: [PATCH 178/845] Updating lbaas cli for TLS Adding additional params for TLS support in the LBaaS cli Updates tests to verify TLS params Partially-Implements: lbaas-ssl-termination Depends-On: I4c5c494370479176af6f80623ae3a0c6af30ef14 Change-Id: Ie0ae991e11b491143ef63adc5d6f0b879477dd54 --- neutronclient/neutron/v2_0/lb/v2/listener.py | 15 +++++++++++++-- .../tests/unit/lb/v2/test_cli20_listener.py | 11 ++++++++--- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/neutronclient/neutron/v2_0/lb/v2/listener.py b/neutronclient/neutron/v2_0/lb/v2/listener.py index 6b398ffaf..6ce9944b9 100644 --- a/neutronclient/neutron/v2_0/lb/v2/listener.py +++ b/neutronclient/neutron/v2_0/lb/v2/listener.py @@ -65,6 +65,15 @@ def add_known_arguments(self, parser): parser.add_argument( '--name', help=_('The name of the listener.')) + parser.add_argument( + '--default-tls-container-id', + dest='default_tls_container_id', + help=_('Default TLS container ID to retrieve TLS information.')) + parser.add_argument( + '--sni-container-ids', + dest='sni_container_ids', + nargs='+', + help=_('List of TLS container IDs for SNI.')) parser.add_argument( '--loadbalancer', required=True, @@ -73,7 +82,7 @@ def add_known_arguments(self, parser): parser.add_argument( '--protocol', required=True, - choices=['TCP', 'HTTP', 'HTTPS'], + choices=['TCP', 'HTTP', 'HTTPS', 'TERMINATED_HTTPS'], help=_('Protocol for the listener.')) parser.add_argument( '--protocol-port', @@ -97,7 +106,9 @@ def args2body(self, parsed_args): neutronV20.update_dict(parsed_args, body[self.resource], ['connection-limit', 'description', - 'loadbalancer_id', 'name']) + 'loadbalancer_id', 'name', + 'default_tls_container_id', + 'sni_container_ids']) return body diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_listener.py b/neutronclient/tests/unit/lb/v2/test_cli20_listener.py index fc0a062b6..ea421a8ec 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_listener.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_listener.py @@ -51,12 +51,17 @@ def test_create_listener_with_all_params(self): loadbalancer = 'loadbalancer' protocol = 'TCP' protocol_port = '80' + def_tls_cont_id = '11111' args = ['--admin-state-down', '--protocol', protocol, '--protocol-port', protocol_port, - '--loadbalancer', loadbalancer] + '--loadbalancer', loadbalancer, + '--default-tls-container-id', def_tls_cont_id, + '--sni-container-ids', '1111', '2222', '3333'] position_names = ['admin_state_up', - 'protocol', 'protocol_port', 'loadbalancer_id'] - position_values = [False, protocol, protocol_port, loadbalancer] + 'protocol', 'protocol_port', 'loadbalancer_id', + 'default_tls_container_id', 'sni_container_ids'] + position_values = [False, protocol, protocol_port, loadbalancer, + def_tls_cont_id, ['1111', '2222', '3333']] self._test_create_resource(resource, cmd, '', my_id, args, position_names, position_values, cmd_resource=cmd_resource) From 0eb43b81e4d070af6882601bcc42c60e8fffaba3 Mon Sep 17 00:00:00 2001 From: mathieu-rohon Date: Sat, 7 Mar 2015 21:05:08 +0100 Subject: [PATCH 179/845] Add commands from extensions to available commands Any project can extend available commands in neutronclient thanks to change : I5b2fe530c90b5ce1243fc10341d6d434a1ecea7a However, commands submitted by external modules are not correctly added to available commands in the client. Change-Id: I53783f18e7811deee9ae7e1f48fc429afcba8936 Closes-bug: #1430825 --- neutronclient/shell.py | 1 + neutronclient/tests/unit/test_client_extension.py | 13 +++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 261936348..7b20bd76e 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -692,6 +692,7 @@ def _extend_shell_commands(self, module, version): continue try: self.command_manager.add_command(cmd, cls) + self.commands[version][cmd] = cls except TypeError: pass diff --git a/neutronclient/tests/unit/test_client_extension.py b/neutronclient/tests/unit/test_client_extension.py index fe1712be7..0ac8cc823 100644 --- a/neutronclient/tests/unit/test_client_extension.py +++ b/neutronclient/tests/unit/test_client_extension.py @@ -19,6 +19,7 @@ import mock from neutronclient.neutron.v2_0.contrib import _fox_sockets as fox_sockets +from neutronclient import shell from neutronclient.tests.unit import test_cli20 @@ -37,10 +38,18 @@ def _create_patch(self, name, func=None): def _mock_extension_loading(self): ext_pkg = 'neutronclient.common.extension' contrib = self._create_patch(ext_pkg + '._discover_via_entry_points') - iterator = iter([("_fox_sockets", fox_sockets)]) - contrib.return_value.__iter__.return_value = iterator + contrib.return_value = [("_fox_sockets", fox_sockets)] return contrib + def test_ext_cmd_loaded(self): + shell.NeutronShell('2.0') + ext_cmd = {'fox-sockets-list': fox_sockets.FoxInSocketsList, + 'fox-sockets-create': fox_sockets.FoxInSocketsCreate, + 'fox-sockets-update': fox_sockets.FoxInSocketsUpdate, + 'fox-sockets-delete': fox_sockets.FoxInSocketsDelete, + 'fox-sockets-show': fox_sockets.FoxInSocketsShow} + self.assertDictContainsSubset(ext_cmd, shell.COMMANDS['2.0']) + def test_delete_fox_socket(self): """Delete fox socket: myid.""" resource = 'fox_socket' From 5a6e6089265a5749641a4a91e1c4c877b9e8b314 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Mon, 2 Mar 2015 13:22:19 +0900 Subject: [PATCH 180/845] Show rules in handy format in security-group-list Previously security group rules in security-group-list was just JSON dump of security group rules if security_group_rules field is requested to display and it leads to long lines. This commit defines a formater for sg rules for ListSecurityGroup. It changes the default columns to show "security_group_rules". I believe it is useful for users because we don't need to run neutron security-group-rule-list to know rules. Closes-Bug: #1153766 Change-Id: I5e1b93bea6ab1121f85dc19d2e75fffd065cd627 --- neutronclient/neutron/v2_0/securitygroup.py | 63 +++++++++++++- .../tests/functional/test_readonly_neutron.py | 3 +- .../tests/unit/test_cli20_securitygroup.py | 82 +++++++++++++++++++ 3 files changed, 146 insertions(+), 2 deletions(-) diff --git a/neutronclient/neutron/v2_0/securitygroup.py b/neutronclient/neutron/v2_0/securitygroup.py index 6bfd80d65..bafa3b09b 100644 --- a/neutronclient/neutron/v2_0/securitygroup.py +++ b/neutronclient/neutron/v2_0/securitygroup.py @@ -21,11 +21,72 @@ from neutronclient.neutron import v2_0 as neutronV20 +def _get_protocol_port(rule): + proto = rule['protocol'] + port_min = rule['port_range_min'] + port_max = rule['port_range_max'] + if proto in ('tcp', 'udp'): + if (port_min and port_min == port_max): + protocol_port = '%s/%s' % (port_min, proto) + elif port_min: + protocol_port = '%s-%s/%s' % (port_min, port_max, proto) + else: + protocol_port = proto + elif proto == 'icmp': + icmp_opts = [] + if port_min is not None: + icmp_opts.append('type:%s' % port_min) + if port_max is not None: + icmp_opts.append('code:%s' % port_max) + + if icmp_opts: + protocol_port = 'icmp (%s)' % ', '.join(icmp_opts) + else: + protocol_port = 'icmp' + elif proto is not None: + # port_range_min/max are not recognized for protocol + # other than TCP, UDP and ICMP. + protocol_port = proto + else: + protocol_port = None + + return protocol_port + + +def _format_sg_rule(rule): + formatted = [] + for field in ['direction', + 'ethertype', + ('protocol_port', _get_protocol_port), + 'remote_ip_prefix', + 'remote_group_id']: + if isinstance(field, tuple): + field, get_method = field + data = get_method(rule) + else: + data = rule[field] + if not data: + continue + if field in ('remote_ip_prefix', 'remote_group_id'): + data = '%s: %s' % (field, data) + formatted.append(data) + return ', '.join(formatted) + + +def _format_sg_rules(secgroup): + try: + return '\n'.join(sorted([_format_sg_rule(rule) for rule + in secgroup['security_group_rules']])) + except Exception: + return '' + + class ListSecurityGroup(neutronV20.ListCommand): """List security groups that belong to a given tenant.""" resource = 'security_group' - list_columns = ['id', 'name', 'description'] + list_columns = ['id', 'name', 'security_group_rules'] + _formatters = {'security_group_rules': _format_sg_rules} pagination_support = True sorting_support = True diff --git a/neutronclient/tests/functional/test_readonly_neutron.py b/neutronclient/tests/functional/test_readonly_neutron.py index a552ef7ac..eae8cd2d2 100644 --- a/neutronclient/tests/functional/test_readonly_neutron.py +++ b/neutronclient/tests/functional/test_readonly_neutron.py @@ -108,7 +108,8 @@ def test_neutron_router_list(self): def test_neutron_security_group_list(self): security_grp = self.parser.listing(self.neutron('security-group-list')) - self.assertTableStruct(security_grp, ['id', 'name', 'description']) + self.assertTableStruct(security_grp, ['id', 'name', + 'security_group_rules']) def test_neutron_security_group_rule_list(self): security_grp = self.parser.listing(self.neutron diff --git a/neutronclient/tests/unit/test_cli20_securitygroup.py b/neutronclient/tests/unit/test_cli20_securitygroup.py index 8032e0390..400e2b8a3 100644 --- a/neutronclient/tests/unit/test_cli20_securitygroup.py +++ b/neutronclient/tests/unit/test_cli20_securitygroup.py @@ -15,6 +15,7 @@ # under the License. import sys +import uuid from mox3 import mox import six @@ -434,6 +435,87 @@ def test_list_security_group_rules_extend_with_fields_no_id(self): self._test_list_security_group_rules_extend(args=args, query_field=True) + def _prepare_rule(self, direction=None, ethertype=None, + protocol=None, port_range_min=None, port_range_max=None, + remote_ip_prefix=None, remote_group_id=None): + return {'id': str(uuid.uuid4()), + 'tenant_id': str(uuid.uuid4()), + 'security_group_id': str(uuid.uuid4()), + 'direction': direction or 'ingress', + 'ethertype': ethertype or 'IPv4', + 'protocol': protocol, + 'port_range_min': port_range_min, + 'port_range_max': port_range_max, + 'remote_ip_prefix': remote_ip_prefix, + 'remote_group_id': remote_group_id} + + def test__get_protocol_port_all_none(self): + sg_rule = self._prepare_rule() + self.assertIsNone(securitygroup._get_protocol_port(sg_rule)) + + def test__get_protocol_port_tcp_all_port(self): + sg_rule = self._prepare_rule(protocol='tcp') + self.assertEqual('tcp', securitygroup._get_protocol_port(sg_rule)) + + def test__get_protocol_port_tcp_one_port(self): + sg_rule = self._prepare_rule(protocol='tcp', + port_range_min=22, port_range_max=22) + self.assertEqual('22/tcp', securitygroup._get_protocol_port(sg_rule)) + + def test__get_protocol_port_tcp_port_range(self): + sg_rule = self._prepare_rule(protocol='tcp', + port_range_min=5000, port_range_max=5010) + self.assertEqual('5000-5010/tcp', + securitygroup._get_protocol_port(sg_rule)) + + def test__get_protocol_port_udp_all_port(self): + sg_rule = self._prepare_rule(protocol='udp') + self.assertEqual('udp', securitygroup._get_protocol_port(sg_rule)) + + def test__get_protocol_port_udp_one_port(self): + sg_rule = self._prepare_rule(protocol='udp', + port_range_min=22, port_range_max=22) + self.assertEqual('22/udp', securitygroup._get_protocol_port(sg_rule)) + + def test__get_protocol_port_udp_port_range(self): + sg_rule = self._prepare_rule(protocol='udp', + port_range_min=5000, port_range_max=5010) + self.assertEqual('5000-5010/udp', + securitygroup._get_protocol_port(sg_rule)) + + def test__get_protocol_port_icmp_all(self): + sg_rule = self._prepare_rule(protocol='icmp') + self.assertEqual('icmp', securitygroup._get_protocol_port(sg_rule)) + + def test__get_protocol_port_udp_code_type(self): + sg_rule = self._prepare_rule(protocol='icmp', + port_range_min=1, port_range_max=8) + self.assertEqual('icmp (type:1, code:8)', + securitygroup._get_protocol_port(sg_rule)) + + def test__format_sg_rules(self): + rules = [self._prepare_rule(), + self._prepare_rule(protocol='tcp', port_range_min=80, + port_range_max=80), + self._prepare_rule(remote_ip_prefix='192.168.1.0/24'), + self._prepare_rule(remote_group_id='group1'), + self._prepare_rule(protocol='tcp', + remote_ip_prefix='10.1.1.0/24'), + self._prepare_rule(direction='egress'), + self._prepare_rule(direction='egress', ethertype='IPv6'), + ] + sg = {'security_group_rules': rules} + expected_data = ['ingress, IPv4', + 'ingress, IPv4, 80/tcp', + 'ingress, IPv4, remote_ip_prefix: 192.168.1.0/24', + 'ingress, IPv4, remote_group_id: group1', + 'ingress, IPv4, tcp, remote_ip_prefix: 10.1.1.0/24', + 'egress, IPv4', + 'egress, IPv6', + ] + expected = '\n'.join(sorted(expected_data)) + self.assertEqual(expected, securitygroup._format_sg_rules(sg)) + class CLITestV20SecurityGroupsXML(CLITestV20SecurityGroupsJSON): format = 'xml' From 4829e25b6a68df0372a3b84c3cc7c9b680ced669 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Sun, 1 Mar 2015 19:37:40 +0900 Subject: [PATCH 181/845] security-group-rule-list: show all info of rules briefly Previously security-group-rule-list does not display full information of rules by default (e.g., port ranges, ether types) and users need to use security-group-rule-show to check details. It is not convenient. This commit introduces some aggregated columns ("protocol/port" and "remote") to show infomration briefly and as a result full attributes of rules will be displayed. Closes-Bug: #1182629 Change-Id: I047bf9a1ccba5b023d66f22ef5256f7786196113 --- neutronclient/neutron/v2_0/securitygroup.py | 65 +++++- .../tests/functional/test_readonly_neutron.py | 5 +- .../tests/unit/test_cli20_securitygroup.py | 205 ++++++++++++------ 3 files changed, 200 insertions(+), 75 deletions(-) diff --git a/neutronclient/neutron/v2_0/securitygroup.py b/neutronclient/neutron/v2_0/securitygroup.py index bafa3b09b..65e05c92e 100644 --- a/neutronclient/neutron/v2_0/securitygroup.py +++ b/neutronclient/neutron/v2_0/securitygroup.py @@ -21,6 +21,16 @@ from neutronclient.neutron import v2_0 as neutronV20 +def _get_remote(rule): + if rule['remote_ip_prefix']: + remote = '%s (CIDR)' % rule['remote_ip_prefix'] + elif rule['remote_group_id']: + remote = '%s (group)' % rule['remote_group_id'] + else: + remote = None + return remote + + def _get_protocol_port(rule): proto = rule['protocol'] port_min = rule['port_range_min'] @@ -157,10 +167,19 @@ class ListSecurityGroupRule(neutronV20.ListCommand): """List security group rules that belong to a given tenant.""" resource = 'security_group_rule' - list_columns = ['id', 'security_group_id', 'direction', 'protocol', - 'remote_ip_prefix', 'remote_group_id'] + list_columns = ['id', 'security_group_id', 'direction', + 'ethertype', 'protocol/port', 'remote'] + # replace_rules: key is an attribute name in Neutron API and + # corresponding value is a display name shown by CLI. replace_rules = {'security_group_id': 'security_group', 'remote_group_id': 'remote_group'} + digest_fields = { + 'remote': { + 'method': _get_remote, + 'depends_on': ['remote_ip_prefix', 'remote_group_id']}, + 'protocol/port': { + 'method': _get_protocol_port, + 'depends_on': ['protocol', 'port_range_min', 'port_range_max']}} pagination_support = True sorting_support = True @@ -177,19 +196,28 @@ def replace_columns(cols, rules, reverse=False): rules = dict((rules[k], k) for k in rules.keys()) return [rules.get(col, col) for col in cols] + def get_required_fields(self, fields): + fields = self.replace_columns(fields, self.replace_rules, reverse=True) + for field, digest_fields in self.digest_fields.items(): + if field in fields: + fields += digest_fields['depends_on'] + fields.remove(field) + return fields + def retrieve_list(self, parsed_args): - parsed_args.fields = self.replace_columns(parsed_args.fields, - self.replace_rules, - reverse=True) + parsed_args.fields = self.get_required_fields(parsed_args.fields) return super(ListSecurityGroupRule, self).retrieve_list(parsed_args) - def extend_list(self, data, parsed_args): - if parsed_args.no_nameconv: - return + def _get_sg_name_dict(self, data, page_size, no_nameconv): + """Get names of security groups referred in the retrieved rules. + + :return: a dict from secgroup ID to secgroup name + """ + if no_nameconv: + return {} neutron_client = self.get_client() search_opts = {'fields': ['id', 'name']} if self.pagination_support: - page_size = parsed_args.page_size if page_size: search_opts.update({'limit': page_size}) sec_group_ids = set() @@ -222,14 +250,28 @@ def _get_sec_group_list(sec_group_ids): secgroups.extend( _get_sec_group_list(sec_group_ids[i: i + chunk_size])) - sg_dict = dict([(sg['id'], sg['name']) - for sg in secgroups if sg['name']]) + return dict([(sg['id'], sg['name']) + for sg in secgroups if sg['name']]) + + @staticmethod + def _has_fileds(rule, required_fileds): + return all([key in rule for key in required_fileds]) + + def extend_list(self, data, parsed_args): + sg_dict = self._get_sg_name_dict(data, parsed_args.page_size, + parsed_args.no_nameconv) for rule in data: + # Replace security group UUID with its name. for key in self.replace_rules: if key in rule: rule[key] = sg_dict.get(rule[key], rule[key]) + for field, digest_rule in self.digest_fields.items(): + if self._has_fileds(rule, digest_rule['depends_on']): + rule[field] = digest_rule['method'](rule) or 'any' def setup_columns(self, info, parsed_args): + # Translate the specified columns from the command line + # into field names used in "info". parsed_args.columns = self.replace_columns(parsed_args.columns, self.replace_rules, reverse=True) @@ -240,6 +282,7 @@ def setup_columns(self, info, parsed_args): parsed_args) cols = info[0] if not parsed_args.no_nameconv: + # Replace column names in the header line (in info[0]) cols = self.replace_columns(info[0], self.replace_rules) parsed_args.columns = cols return (cols, info[1]) diff --git a/neutronclient/tests/functional/test_readonly_neutron.py b/neutronclient/tests/functional/test_readonly_neutron.py index eae8cd2d2..b436b0ba0 100644 --- a/neutronclient/tests/functional/test_readonly_neutron.py +++ b/neutronclient/tests/functional/test_readonly_neutron.py @@ -115,9 +115,8 @@ def test_neutron_security_group_rule_list(self): security_grp = self.parser.listing(self.neutron ('security-group-rule-list')) self.assertTableStruct(security_grp, ['id', 'security_group', - 'direction', 'protocol', - 'remote_ip_prefix', - 'remote_group']) + 'direction', 'ethertype', + 'protocol/port', 'remote']) def test_neutron_subnet_list(self): subnet_list = self.parser.listing(self.neutron('subnet-list')) diff --git a/neutronclient/tests/unit/test_cli20_securitygroup.py b/neutronclient/tests/unit/test_cli20_securitygroup.py index 400e2b8a3..5fc715bce 100644 --- a/neutronclient/tests/unit/test_cli20_securitygroup.py +++ b/neutronclient/tests/unit/test_cli20_securitygroup.py @@ -332,9 +332,9 @@ def test_show_security_group_rule(self): self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - def _test_list_security_group_rules_extend(self, data=None, expected=None, + def _test_list_security_group_rules_extend(self, api_data, expected, args=(), conv=True, - query_field=False): + query_fields=None): def setup_list_stub(resources, data, query): reses = {resources: data} resstr = self.client.serialize(reses) @@ -349,38 +349,22 @@ def setup_list_stub(resources, data, query): headers=mox.ContainsKeyValue( 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(resp) - # Setup the default data - _data = {'cols': ['id', 'security_group_id', 'remote_group_id'], - 'data': [('ruleid1', 'myid1', 'myid1'), - ('ruleid2', 'myid2', 'myid3'), - ('ruleid3', 'myid2', 'myid2')]} - _expected = {'cols': ['id', 'security_group', 'remote_group'], - 'data': [('ruleid1', 'group1', 'group1'), - ('ruleid2', 'group2', 'group3'), - ('ruleid3', 'group2', 'group2')]} - if data is None: - data = _data - list_data = [dict(zip(data['cols'], d)) for d in data['data']] - if expected is None: - expected = {} - expected['cols'] = expected.get('cols', _expected['cols']) - expected['data'] = expected.get('data', _expected['data']) - cmd = securitygroup.ListSecurityGroupRule( test_cli20.MyApp(sys.stdout), None) self.mox.StubOutWithMock(cmd, 'get_client') self.mox.StubOutWithMock(self.client.httpclient, 'request') cmd.get_client().AndReturn(self.client) query = '' - if query_field: - query = '&'.join(['fields=' + f for f in data['cols']]) - setup_list_stub('security_group_rules', list_data, query) + if query_fields: + query = '&'.join(['fields=' + f for f in query_fields]) + setup_list_stub('security_group_rules', api_data, query) if conv: cmd.get_client().AndReturn(self.client) sec_ids = set() - for n in data['data']: - sec_ids.add(n[1]) - sec_ids.add(n[2]) + for n in api_data: + sec_ids.add(n['security_group_id']) + if n.get('remote_group_id'): + sec_ids.add(n['remote_group_id']) filters = '' for id in sec_ids: filters = filters + "&id=%s" % id @@ -397,50 +381,131 @@ def setup_list_stub(resources, data, query): self.mox.VerifyAll() self.mox.UnsetStubs() # Check columns - self.assertEqual(result[0], expected['cols']) + self.assertEqual(expected['cols'], result[0]) # Check data _result = [x for x in result[1]] self.assertEqual(len(_result), len(expected['data'])) for res, exp in zip(_result, expected['data']): - self.assertEqual(len(res), len(exp)) - self.assertEqual(res, exp) - - def test_list_security_group_rules_extend_source_id(self): - self._test_list_security_group_rules_extend() - - def test_list_security_group_rules_extend_no_nameconv(self): - expected = {'cols': ['id', 'security_group_id', 'remote_group_id'], - 'data': [('ruleid1', 'myid1', 'myid1'), - ('ruleid2', 'myid2', 'myid3'), - ('ruleid3', 'myid2', 'myid2')]} - args = ['--no-nameconv'] - self._test_list_security_group_rules_extend(expected=expected, - args=args, conv=False) - - def test_list_security_group_rules_extend_with_columns(self): + self.assertEqual(len(exp), len(res)) + self.assertEqual(exp, res) + + def _test_list_security_group_rules_extend_sg_name( + self, expected_mode=None, args=(), conv=True, query_field=False): + if query_field: + field_filters = ['id', 'security_group_id', + 'remote_ip_prefix', 'remote_group_id'] + else: + field_filters = None + + data = [self._prepare_rule(rule_id='ruleid1', sg_id='myid1', + remote_group_id='myid1', + filters=field_filters), + self._prepare_rule(rule_id='ruleid2', sg_id='myid2', + remote_group_id='myid3', + filters=field_filters), + self._prepare_rule(rule_id='ruleid3', sg_id='myid2', + remote_group_id='myid2', + filters=field_filters), + ] + + if expected_mode == 'noconv': + expected = {'cols': ['id', 'security_group_id', 'remote_group_id'], + 'data': [('ruleid1', 'myid1', 'myid1'), + ('ruleid2', 'myid2', 'myid3'), + ('ruleid3', 'myid2', 'myid2')]} + elif expected_mode == 'remote_group_id': + expected = {'cols': ['id', 'security_group', 'remote_group'], + 'data': [('ruleid1', 'group1', 'group1'), + ('ruleid2', 'group2', 'group3'), + ('ruleid3', 'group2', 'group2')]} + else: + expected = {'cols': ['id', 'security_group', 'remote'], + 'data': [('ruleid1', 'group1', 'group1 (group)'), + ('ruleid2', 'group2', 'group3 (group)'), + ('ruleid3', 'group2', 'group2 (group)')]} + + self._test_list_security_group_rules_extend( + data, expected, args=args, conv=conv, query_fields=field_filters) + + def test_list_security_group_rules_extend_remote_sg_name(self): + args = '-c id -c security_group -c remote'.split() + self._test_list_security_group_rules_extend_sg_name(args=args) + + def test_list_security_group_rules_extend_sg_name_noconv(self): + args = '--no-nameconv -c id -c security_group_id -c remote_group_id' + args = args.split() + self._test_list_security_group_rules_extend_sg_name( + expected_mode='noconv', args=args, conv=False) + + def test_list_security_group_rules_extend_sg_name_with_columns(self): args = '-c id -c security_group_id -c remote_group_id'.split() - self._test_list_security_group_rules_extend(args=args) + self._test_list_security_group_rules_extend_sg_name( + expected_mode='remote_group_id', args=args) - def test_list_security_group_rules_extend_with_columns_no_id(self): + def test_list_security_group_rules_extend_sg_name_with_columns_no_id(self): args = '-c id -c security_group -c remote_group'.split() - self._test_list_security_group_rules_extend(args=args) - - def test_list_security_group_rules_extend_with_fields(self): - args = '-F id -F security_group_id -F remote_group_id'.split() - self._test_list_security_group_rules_extend(args=args, - query_field=True) - - def test_list_security_group_rules_extend_with_fields_no_id(self): - args = '-F id -F security_group -F remote_group'.split() - self._test_list_security_group_rules_extend(args=args, - query_field=True) - - def _prepare_rule(self, direction=None, ethertype=None, + self._test_list_security_group_rules_extend_sg_name( + expected_mode='remote_group_id', args=args) + + def test_list_security_group_rules_extend_sg_name_with_fields(self): + # NOTE: remote_ip_prefix is required to show "remote" column + args = ('-F id -F security_group_id ' + '-F remote_ip_prefix -F remote_group_id').split() + self._test_list_security_group_rules_extend_sg_name( + args=args, query_field=True) + + def test_list_security_group_rules_extend_sg_name_with_fields_no_id(self): + # NOTE: remote_ip_prefix is required to show "remote" column + args = ('-F id -F security_group ' + '-F remote_ip_prefix -F remote_group').split() + self._test_list_security_group_rules_extend_sg_name(args=args, + query_field=True) + + def test_list_security_group_rules_extend_remote(self): + args = '-c id -c security_group -c remote'.split() + + data = [self._prepare_rule(rule_id='ruleid1', sg_id='myid1', + remote_ip_prefix='172.16.18.0/24'), + self._prepare_rule(rule_id='ruleid2', sg_id='myid2', + remote_ip_prefix='172.16.20.0/24'), + self._prepare_rule(rule_id='ruleid3', sg_id='myid2', + remote_group_id='myid3')] + expected = {'cols': ['id', 'security_group', 'remote'], + 'data': [('ruleid1', 'group1', '172.16.18.0/24 (CIDR)'), + ('ruleid2', 'group2', '172.16.20.0/24 (CIDR)'), + ('ruleid3', 'group2', 'group3 (group)')]} + self._test_list_security_group_rules_extend(data, expected, args) + + def test_list_security_group_rules_extend_proto_port(self): + data = [self._prepare_rule(rule_id='ruleid1', sg_id='myid1', + protocol='tcp', + port_range_min=22, port_range_max=22), + self._prepare_rule(rule_id='ruleid2', sg_id='myid2', + direction='egress', ethertype='IPv6', + protocol='udp', + port_range_min=80, port_range_max=81), + self._prepare_rule(rule_id='ruleid3', sg_id='myid2', + protocol='icmp', + remote_ip_prefix='10.2.0.0/16')] + expected = { + 'cols': ['id', 'security_group', 'direction', 'ethertype', + 'protocol/port', 'remote'], + 'data': [ + ('ruleid1', 'group1', 'ingress', 'IPv4', '22/tcp', 'any'), + ('ruleid2', 'group2', 'egress', 'IPv6', '80-81/udp', 'any'), + ('ruleid3', 'group2', 'ingress', 'IPv4', 'icmp', + '10.2.0.0/16 (CIDR)') + ]} + self._test_list_security_group_rules_extend(data, expected) + + def _prepare_rule(self, rule_id=None, sg_id=None, tenant_id=None, + direction=None, ethertype=None, protocol=None, port_range_min=None, port_range_max=None, - remote_ip_prefix=None, remote_group_id=None): - return {'id': str(uuid.uuid4()), - 'tenant_id': str(uuid.uuid4()), - 'security_group_id': str(uuid.uuid4()), + remote_ip_prefix=None, remote_group_id=None, + filters=None): + rule = {'id': rule_id or str(uuid.uuid4()), + 'tenant_id': tenant_id or str(uuid.uuid4()), + 'security_group_id': sg_id or str(uuid.uuid4()), 'direction': direction or 'ingress', 'ethertype': ethertype or 'IPv4', 'protocol': protocol, @@ -448,6 +513,24 @@ def _prepare_rule(self, direction=None, ethertype=None, 'port_range_max': port_range_max, 'remote_ip_prefix': remote_ip_prefix, 'remote_group_id': remote_group_id} + if filters: + return dict([(k, v) for k, v in rule.items() if k in filters]) + else: + return rule + + def test__get_remote_both_unspecified(self): + sg_rule = self._prepare_rule(remote_ip_prefix=None, + remote_group_id=None) + self.assertIsNone(securitygroup._get_remote(sg_rule)) + + def test__get_remote_remote_ip_prefix_specified(self): + sg_rule = self._prepare_rule(remote_ip_prefix='172.16.18.0/24') + self.assertEqual('172.16.18.0/24 (CIDR)', + securitygroup._get_remote(sg_rule)) + + def test__get_remote_remote_group_specified(self): + sg_rule = self._prepare_rule(remote_group_id='sg_id1') + self.assertEqual('sg_id1 (group)', securitygroup._get_remote(sg_rule)) def test__get_protocol_port_all_none(self): sg_rule = self._prepare_rule() From 4e98615d1612f011041be011f411e5115a269f8b Mon Sep 17 00:00:00 2001 From: ptoohill1 Date: Wed, 11 Mar 2015 21:36:55 -0500 Subject: [PATCH 182/845] Updates pool session persistence options Updates pool options to take kv args for type and cookie_name Updates tests Change-Id: I780948430db295af01b7297ee828f57d2a91c6d4 Closes-Bug: #1430541 --- neutronclient/neutron/v2_0/lb/v2/pool.py | 22 ++++++------------- .../tests/unit/lb/v2/test_cli20_pool.py | 4 ++-- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/neutronclient/neutron/v2_0/lb/v2/pool.py b/neutronclient/neutron/v2_0/lb/v2/pool.py index 7182171b3..188596f38 100644 --- a/neutronclient/neutron/v2_0/lb/v2/pool.py +++ b/neutronclient/neutron/v2_0/lb/v2/pool.py @@ -19,22 +19,11 @@ # under the License. # -from neutronclient.common import exceptions +from neutronclient.common import utils from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 -def _parse_persistence(parsed_args): - persistence = None - if parsed_args.session_persistence: - parts = parsed_args.session_persistence.split(':') - if not len(parts) == 2: - raise exceptions.CommandError('Incorrect --session-persistence' - ' format. Format is :') - persistence = {'type': parts[0], 'cookie_name': parts[1]} - return persistence - - class ListPool(neutronV20.ListCommand): """LBaaS v2 List pools that belong to a given tenant.""" @@ -76,8 +65,10 @@ def add_known_arguments(self, parser): '--description', help=_('Description of the pool.')) parser.add_argument( - '--session-persistence', metavar='TYPE:VALUE', - help=_('The type of session persistence to use.')) + '--session-persistence', + metavar='type=TYPE[,cookie_name=COOKIE_NAME]', + help=_('The type of session persistence to use and associated ' + 'cookie name')) parser.add_argument( '--name', help=_('The name of the pool.')) parser.add_argument( @@ -98,7 +89,8 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): if parsed_args.session_persistence: - parsed_args.session_persistence = _parse_persistence(parsed_args) + parsed_args.session_persistence = utils.str2dict( + parsed_args.session_persistence) _listener_id = neutronV20.find_resourceid_by_name_or_id( self.get_client(), 'listener', parsed_args.listener) body = { diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_pool.py b/neutronclient/tests/unit/lb/v2/test_cli20_pool.py index 77b565562..94defe8a6 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_pool.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_pool.py @@ -52,8 +52,8 @@ def test_create_pool_with_all_params(self): listener = 'listener' protocol = 'TCP' description = 'description' - session_persistence_str = 'HTTP_COOKIE:1234' - session_persistence = {'type': 'HTTP_COOKIE', + session_persistence_str = 'type=APP_COOKIE,cookie_name=1234' + session_persistence = {'type': 'APP_COOKIE', 'cookie_name': '1234'} name = 'my-pool' args = ['--lb-algorithm', lb_algorithm, '--protocol', protocol, From 5f0f2802a83646dc4cad29078e2c8e82c69dc041 Mon Sep 17 00:00:00 2001 From: Anand Shanmugam Date: Sat, 14 Mar 2015 23:47:13 +0530 Subject: [PATCH 183/845] Fix failures when calling list operations using Python binding This bug is caused because of the regression caused by I1b719bed406b83c5f2deac06e127798a91f51ad7 . The original bug was raised because check_max_uri was not working after the introduction of sessionclient. The change created a regression in which the python bindings of the neutron client was not usable and causing traces. The fix is to revert the change id and add a new check_max_uri to the sessionclient. Now uri length will be checked in sessionclient and httpclient as well. please see bug for further info. Closes-Bug: #1431449 Change-Id: Ief2352a90bb75a76e8c671d51beb0fb7a53a22f9 --- neutronclient/client.py | 15 +++++++++++++++ neutronclient/tests/unit/test_cli20_network.py | 6 +++--- .../tests/unit/test_cli20_securitygroup.py | 6 +++--- neutronclient/v2_0/client.py | 11 ----------- 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/neutronclient/client.py b/neutronclient/client.py index bc645f195..0ab732ec3 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -40,6 +40,7 @@ _requests_log_level = logging.WARNING logging.getLogger("requests").setLevel(_requests_log_level) +MAX_URI_LEN = 8192 class HTTPClient(object): @@ -146,9 +147,16 @@ def request(self, url, method, body=None, headers=None, **kwargs): return resp, resp.text + def _check_uri_length(self, action): + uri_len = len(self.endpoint_url) + len(action) + if uri_len > MAX_URI_LEN: + raise exceptions.RequestURITooLong( + excess=uri_len - MAX_URI_LEN) + def do_request(self, url, method, **kwargs): # Ensure client always has correct uri - do not guesstimate anything self.authenticate_and_fetch_endpoint_url() + self._check_uri_length(url) # Perform the request once. If we get a 401 back then it # might be because the auth token expired, so try to @@ -286,8 +294,15 @@ def request(self, *args, **kwargs): resp = super(SessionClient, self).request(*args, **kwargs) return resp, resp.text + def _check_uri_length(self, url): + uri_len = len(self.endpoint_url) + len(url) + if uri_len > MAX_URI_LEN: + raise exceptions.RequestURITooLong( + excess=uri_len - MAX_URI_LEN) + def do_request(self, url, method, **kwargs): kwargs.setdefault('authenticated', True) + self._check_uri_length(url) return self.request(url, method, **kwargs) @property diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index 31754be26..1ac49b637 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -551,14 +551,14 @@ def mox_calls(path, data): filters, response = self._build_test_data(data) # 1 char of extra URI len will cause a split in 2 requests - self.mox.StubOutWithMock(self.client, + self.mox.StubOutWithMock(self.client.httpclient, "_check_uri_length") - self.client._check_uri_length(mox.IgnoreArg()).AndRaise( + self.client.httpclient._check_uri_length(mox.IgnoreArg()).AndRaise( exceptions.RequestURITooLong(excess=1)) for data in sub_data_lists: filters, response = self._build_test_data(data) - self.client._check_uri_length( + self.client.httpclient._check_uri_length( mox.IgnoreArg()).AndReturn(None) self.client.httpclient.request( test_cli20.MyUrlComparator( diff --git a/neutronclient/tests/unit/test_cli20_securitygroup.py b/neutronclient/tests/unit/test_cli20_securitygroup.py index 8032e0390..ec18fb8ee 100644 --- a/neutronclient/tests/unit/test_cli20_securitygroup.py +++ b/neutronclient/tests/unit/test_cli20_securitygroup.py @@ -265,14 +265,14 @@ def mox_calls(path, data): def test_extend_list_exceed_max_uri_len(self): def mox_calls(path, data): # 1 char of extra URI len will cause a split in 2 requests - self.mox.StubOutWithMock(self.client, + self.mox.StubOutWithMock(self.client.httpclient, '_check_uri_length') - self.client._check_uri_length(mox.IgnoreArg()).AndRaise( + self.client.httpclient._check_uri_length(mox.IgnoreArg()).AndRaise( exceptions.RequestURITooLong(excess=1)) responses = self._build_test_data(data, excess=1) for item in responses: - self.client._check_uri_length( + self.client.httpclient._check_uri_length( mox.IgnoreArg()).AndReturn(None) self.client.httpclient.request( test_cli20.end_url(path, item['filter']), diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 2e63bb2a2..0feac68e9 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -184,12 +184,6 @@ def _handle_fault_response(self, status_code, response_body): # Raise the appropriate exception exception_handler_v20(status_code, des_error_body) - def _check_uri_length(self, action): - uri_len = len(self.httpclient.endpoint_url) + len(action) - if uri_len > self.MAX_URI_LEN: - raise exceptions.RequestURITooLong( - excess=uri_len - self.MAX_URI_LEN) - def do_request(self, method, action, body=None, headers=None, params=None): # Add format and tenant_id action += ".%s" % self.format @@ -198,8 +192,6 @@ def do_request(self, method, action, body=None, headers=None, params=None): params = utils.safe_encode_dict(params) action += '?' + urlparse.urlencode(params, doseq=1) - self._check_uri_length(action) - if body: body = self.serialize(body) @@ -464,9 +456,6 @@ class Client(ClientBase): 'healthmonitors': 'healthmonitor', } - # 8192 Is the default max URI len for eventlet.wsgi.server - MAX_URI_LEN = 8192 - @APIParamsCall def list_ext(self, path, **_params): """Client extension hook for lists. From 0c9cd0d555e219a1ecba6df72360f184dd4975a7 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Sat, 21 Mar 2015 00:17:51 +0000 Subject: [PATCH 184/845] Updated from global requirements Change-Id: I5df27bf88d70b29da62932a0c25c896849d6171d --- requirements.txt | 8 ++++---- test-requirements.txt | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/requirements.txt b/requirements.txt index 0c93734aa..12f89a07e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,12 +3,12 @@ # process, which may cause wedges in the gate later. pbr>=0.6,!=0.7,<1.0 argparse -cliff>=1.7.0 # Apache-2.0 +cliff>=1.10.0,<1.11.0 # Apache-2.0 iso8601>=0.1.9 netaddr>=0.7.12 -oslo.i18n>=1.3.0 # Apache-2.0 -oslo.serialization>=1.2.0 # Apache-2.0 -oslo.utils>=1.2.0 # Apache-2.0 +oslo.i18n>=1.5.0,<1.6.0 # Apache-2.0 +oslo.serialization>=1.4.0,<1.5.0 # Apache-2.0 +oslo.utils>=1.4.0,<1.5.0 # Apache-2.0 requests>=2.2.0,!=2.4.0 python-keystoneclient>=1.1.0 simplejson>=2.2.0 diff --git a/test-requirements.txt b/test-requirements.txt index 08b1781d1..99578886c 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -9,11 +9,11 @@ discover fixtures>=0.3.14 mox3>=0.7.0 mock>=1.0 -oslosphinx>=2.2.0 # Apache-2.0 -oslotest>=1.2.0 # Apache-2.0 +oslosphinx>=2.5.0,<2.6.0 # Apache-2.0 +oslotest>=1.5.1,<1.6.0 # Apache-2.0 python-subunit>=0.0.18 -requests-mock>=0.5.1 # Apache-2.0 +requests-mock>=0.6.0 # Apache-2.0 sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3 testrepository>=0.0.18 testtools>=0.9.36,!=1.2.0 -tempest-lib>=0.3.0 +tempest-lib>=0.4.0 From e2ca29134cb266ada2e171f3f654a22860c40aec Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Mon, 23 Mar 2015 15:15:16 +0900 Subject: [PATCH 185/845] Update hacking to 0.10 Release notes: http://git.openstack.org/cgit/openstack-dev/hacking/tag/?id=0.10.0 * Fix W292 (no newline at end of file) * Fix H238 (old style class declaration, use new style) * Skip H105 (Don't use author tags) temporarily. * Remove H307 from ignore list since it is removed from Hacking rule. * Remove H302 (import only modules) since there is no violation now. Change-Id: Ic5967652819c9c322de327d504882a0e3029d462 --- neutronclient/neutron/v2_0/agentscheduler.py | 2 +- neutronclient/tests/unit/test_cli20.py | 2 +- neutronclient/tests/unit/test_validators.py | 2 +- test-requirements.txt | 2 +- tox.ini | 5 ++--- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/neutronclient/neutron/v2_0/agentscheduler.py b/neutronclient/neutron/v2_0/agentscheduler.py index 6776b9928..ca1610052 100644 --- a/neutronclient/neutron/v2_0/agentscheduler.py +++ b/neutronclient/neutron/v2_0/agentscheduler.py @@ -327,4 +327,4 @@ def call_server(self, neutron_client, search_opts, parsed_args): agent = neutron_client.get_lbaas_agent_hosting_loadbalancer( **search_opts) data = {'agents': [agent['agent']]} - return data \ No newline at end of file + return data diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 657b15e47..ee6d7ac8c 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -50,7 +50,7 @@ def capture_std_streams(): sys.stdout, sys.stderr = stdout, stderr -class FakeStdout: +class FakeStdout(object): def __init__(self): self.content = [] diff --git a/neutronclient/tests/unit/test_validators.py b/neutronclient/tests/unit/test_validators.py index 619acb817..a92496baa 100644 --- a/neutronclient/tests/unit/test_validators.py +++ b/neutronclient/tests/unit/test_validators.py @@ -19,7 +19,7 @@ from neutronclient.common import validators -class FakeParsedArgs(): +class FakeParsedArgs(object): pass diff --git a/test-requirements.txt b/test-requirements.txt index 08b1781d1..1c9cd43fa 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,7 +1,7 @@ # The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. -hacking>=0.9.2,<0.10 +hacking>=0.10.0,<0.11 cliff-tablib>=1.0 coverage>=3.6 diff --git a/tox.ini b/tox.ini index dbed9e67a..94ab88246 100644 --- a/tox.ini +++ b/tox.ini @@ -38,12 +38,11 @@ downloadcache = ~/cache/pip [flake8] # E125 continuation line does not distinguish itself from next logical line -# H302 import only modules # # TODO Fix the following rules from hacking 0.9.x # E265 block comment should start with '# ' +# H105 Don't use author tags # H405 multi line docstring summary not separated with an empty line -# H307 like imports should be grouped together -ignore = E125,E265,H302,H307,H405 +ignore = E125,E265,H105,H405 show-source = true exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,tools From ed46ba9a45e4d0fe5741f2521e6ad3fad567e066 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Mon, 23 Mar 2015 15:40:06 +0900 Subject: [PATCH 186/845] Remove author tag We have a hacking rule H105 (Don't use author tags) now and the same policy has been applied to other neutron repos. Change-Id: I1a1a35bbfaad6bbe229b9b12cf6cfb405b6ed516 --- neutronclient/neutron/v2_0/fw/firewall.py | 2 -- neutronclient/neutron/v2_0/fw/firewallpolicy.py | 2 -- neutronclient/neutron/v2_0/fw/firewallrule.py | 2 -- neutronclient/neutron/v2_0/lb/healthmonitor.py | 2 -- neutronclient/neutron/v2_0/lb/member.py | 2 -- neutronclient/neutron/v2_0/lb/pool.py | 2 -- neutronclient/neutron/v2_0/lb/v2/healthmonitor.py | 3 --- neutronclient/neutron/v2_0/lb/v2/listener.py | 2 -- neutronclient/neutron/v2_0/lb/v2/loadbalancer.py | 2 -- neutronclient/neutron/v2_0/lb/v2/member.py | 3 --- neutronclient/neutron/v2_0/lb/v2/pool.py | 3 --- neutronclient/neutron/v2_0/lb/vip.py | 2 -- neutronclient/neutron/v2_0/metering.py | 2 -- neutronclient/neutron/v2_0/netpartition.py | 1 - neutronclient/neutron/v2_0/networkprofile.py | 3 --- neutronclient/neutron/v2_0/policyprofile.py | 2 -- neutronclient/neutron/v2_0/vpn/ikepolicy.py | 2 -- neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py | 2 -- neutronclient/neutron/v2_0/vpn/ipsecpolicy.py | 1 - neutronclient/neutron/v2_0/vpn/utils.py | 2 -- neutronclient/neutron/v2_0/vpn/vpnservice.py | 2 -- neutronclient/tests/unit/fw/test_cli20_firewall.py | 2 -- neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py | 2 -- neutronclient/tests/unit/fw/test_cli20_firewallrule.py | 2 -- neutronclient/tests/unit/lb/test_cli20_healthmonitor.py | 2 -- neutronclient/tests/unit/lb/test_cli20_member.py | 2 -- neutronclient/tests/unit/lb/test_cli20_pool.py | 2 -- neutronclient/tests/unit/lb/test_cli20_vip.py | 2 -- neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py | 2 -- neutronclient/tests/unit/lb/v2/test_cli20_listener.py | 2 -- neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py | 2 -- neutronclient/tests/unit/lb/v2/test_cli20_member.py | 2 -- neutronclient/tests/unit/lb/v2/test_cli20_pool.py | 2 -- neutronclient/tests/unit/test_cli20_agentschedulers.py | 2 -- neutronclient/tests/unit/test_cli20_credential.py | 2 -- neutronclient/tests/unit/test_cli20_metering.py | 2 -- neutronclient/tests/unit/test_cli20_networkprofile.py | 2 -- neutronclient/tests/unit/test_cli20_nuage_netpartition.py | 1 - neutronclient/tests/unit/test_cli20_policyprofile.py | 2 -- neutronclient/tests/unit/test_cli20_servicetype.py | 2 -- neutronclient/tests/unit/test_command_meta.py | 1 - neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py | 1 - .../tests/unit/vpn/test_cli20_ipsec_site_connection.py | 1 - neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py | 1 - neutronclient/tests/unit/vpn/test_cli20_vpnservice.py | 1 - neutronclient/tests/unit/vpn/test_utils.py | 1 - neutronclient/version.py | 1 - tox.ini | 3 +-- 48 files changed, 1 insertion(+), 90 deletions(-) diff --git a/neutronclient/neutron/v2_0/fw/firewall.py b/neutronclient/neutron/v2_0/fw/firewall.py index 149036a2a..2a108f5c7 100644 --- a/neutronclient/neutron/v2_0/fw/firewall.py +++ b/neutronclient/neutron/v2_0/fw/firewall.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: KC Wang, Big Switch Networks -# import argparse diff --git a/neutronclient/neutron/v2_0/fw/firewallpolicy.py b/neutronclient/neutron/v2_0/fw/firewallpolicy.py index 32f25eba9..410ec475f 100644 --- a/neutronclient/neutron/v2_0/fw/firewallpolicy.py +++ b/neutronclient/neutron/v2_0/fw/firewallpolicy.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: KC Wang, Big Switch Networks -# from __future__ import print_function diff --git a/neutronclient/neutron/v2_0/fw/firewallrule.py b/neutronclient/neutron/v2_0/fw/firewallrule.py index ecc369bcf..0df936ad8 100644 --- a/neutronclient/neutron/v2_0/fw/firewallrule.py +++ b/neutronclient/neutron/v2_0/fw/firewallrule.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: KC Wang, Big Switch Networks -# import argparse diff --git a/neutronclient/neutron/v2_0/lb/healthmonitor.py b/neutronclient/neutron/v2_0/lb/healthmonitor.py index 0956fd465..d691ebf85 100644 --- a/neutronclient/neutron/v2_0/lb/healthmonitor.py +++ b/neutronclient/neutron/v2_0/lb/healthmonitor.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Ilya Shakhat, Mirantis Inc. -# from __future__ import print_function diff --git a/neutronclient/neutron/v2_0/lb/member.py b/neutronclient/neutron/v2_0/lb/member.py index 6effb3013..472cd05ee 100644 --- a/neutronclient/neutron/v2_0/lb/member.py +++ b/neutronclient/neutron/v2_0/lb/member.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Ilya Shakhat, Mirantis Inc. -# from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 diff --git a/neutronclient/neutron/v2_0/lb/pool.py b/neutronclient/neutron/v2_0/lb/pool.py index c10f7a87f..174b91a0d 100644 --- a/neutronclient/neutron/v2_0/lb/pool.py +++ b/neutronclient/neutron/v2_0/lb/pool.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Ilya Shakhat, Mirantis Inc. -# import six diff --git a/neutronclient/neutron/v2_0/lb/v2/healthmonitor.py b/neutronclient/neutron/v2_0/lb/v2/healthmonitor.py index ff1c11e3c..ac3b2aa93 100644 --- a/neutronclient/neutron/v2_0/lb/v2/healthmonitor.py +++ b/neutronclient/neutron/v2_0/lb/v2/healthmonitor.py @@ -2,9 +2,6 @@ # Copyright 2014 Blue Box Group, Inc. # All Rights Reserved # -# Author: Ilya Shakhat, Mirantis Inc. -# Author: Craig Tracey -# # 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 diff --git a/neutronclient/neutron/v2_0/lb/v2/listener.py b/neutronclient/neutron/v2_0/lb/v2/listener.py index 6ce9944b9..e0e9fcb27 100644 --- a/neutronclient/neutron/v2_0/lb/v2/listener.py +++ b/neutronclient/neutron/v2_0/lb/v2/listener.py @@ -2,8 +2,6 @@ # Copyright 2015 Hewlett-Packard Development Company, L.P. # All Rights Reserved # -# Author: Craig Tracey -# # 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 diff --git a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py index 49fd13451..1bada17da 100644 --- a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py +++ b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py @@ -2,8 +2,6 @@ # Copyright 2015 Hewlett-Packard Development Company, L.P. # All Rights Reserved # -# Author: Craig Tracey -# # 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 diff --git a/neutronclient/neutron/v2_0/lb/v2/member.py b/neutronclient/neutron/v2_0/lb/v2/member.py index 4709f65ba..3fd71c6c2 100644 --- a/neutronclient/neutron/v2_0/lb/v2/member.py +++ b/neutronclient/neutron/v2_0/lb/v2/member.py @@ -3,9 +3,6 @@ # Copyright 2015 Hewlett-Packard Development Company, L.P. # All Rights Reserved # -# Author: Ilya Shakhat, Mirantis Inc. -# Author: Craig Tracey -# # 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 diff --git a/neutronclient/neutron/v2_0/lb/v2/pool.py b/neutronclient/neutron/v2_0/lb/v2/pool.py index 188596f38..fd5e03a5c 100644 --- a/neutronclient/neutron/v2_0/lb/v2/pool.py +++ b/neutronclient/neutron/v2_0/lb/v2/pool.py @@ -3,9 +3,6 @@ # Copyright 2015 Hewlett-Packard Development Company, L.P. # All Rights Reserved # -# Author: Ilya Shakhat, Mirantis Inc. -# Author: Craig Tracey -# # 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 diff --git a/neutronclient/neutron/v2_0/lb/vip.py b/neutronclient/neutron/v2_0/lb/vip.py index 3c85d3378..59b0c3871 100644 --- a/neutronclient/neutron/v2_0/lb/vip.py +++ b/neutronclient/neutron/v2_0/lb/vip.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Ilya Shakhat, Mirantis Inc. -# from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 diff --git a/neutronclient/neutron/v2_0/metering.py b/neutronclient/neutron/v2_0/metering.py index c2649c483..34bf2b6de 100644 --- a/neutronclient/neutron/v2_0/metering.py +++ b/neutronclient/neutron/v2_0/metering.py @@ -1,7 +1,5 @@ # Copyright (C) 2013 eNovance SAS # -# Author: Sylvain Afchain -# # 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 diff --git a/neutronclient/neutron/v2_0/netpartition.py b/neutronclient/neutron/v2_0/netpartition.py index 4fd060442..260ad0901 100644 --- a/neutronclient/neutron/v2_0/netpartition.py +++ b/neutronclient/neutron/v2_0/netpartition.py @@ -12,7 +12,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Ronak Shah, Nuage Networks, Alcatel-Lucent USA Inc. from neutronclient.neutron.v2_0 import CreateCommand from neutronclient.neutron.v2_0 import DeleteCommand diff --git a/neutronclient/neutron/v2_0/networkprofile.py b/neutronclient/neutron/v2_0/networkprofile.py index 3450b3219..ed85875f5 100644 --- a/neutronclient/neutron/v2_0/networkprofile.py +++ b/neutronclient/neutron/v2_0/networkprofile.py @@ -10,9 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. # -#@author Abhishek Raut, Cisco Systems -#@author Sergey Sudakovich, Cisco Systems -#@author Rudrajit Tapadar, Cisco Systems from __future__ import print_function diff --git a/neutronclient/neutron/v2_0/policyprofile.py b/neutronclient/neutron/v2_0/policyprofile.py index 03fd8af10..7ff00db9e 100644 --- a/neutronclient/neutron/v2_0/policyprofile.py +++ b/neutronclient/neutron/v2_0/policyprofile.py @@ -10,8 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. # -#@author Abhishek Raut, Cisco Systems -#@author Sergey Sudakovich, Cisco Systems from __future__ import print_function diff --git a/neutronclient/neutron/v2_0/vpn/ikepolicy.py b/neutronclient/neutron/v2_0/vpn/ikepolicy.py index e2f5d2861..ea93eda40 100644 --- a/neutronclient/neutron/v2_0/vpn/ikepolicy.py +++ b/neutronclient/neutron/v2_0/vpn/ikepolicy.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Swaminathan Vasudevan, Hewlett-Packard. -# from neutronclient.common import utils from neutronclient.i18n import _ diff --git a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py index 59d676e0a..6bf937b94 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py +++ b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Swaminathan Vasudevan, Hewlett-Packard. -# from oslo.serialization import jsonutils diff --git a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py index cade641b9..a1b3db909 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py +++ b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Swaminathan Vasudevan, Hewlett-Packard. from neutronclient.common import utils from neutronclient.i18n import _ diff --git a/neutronclient/neutron/v2_0/vpn/utils.py b/neutronclient/neutron/v2_0/vpn/utils.py index 74fbeef6f..0159e76b9 100644 --- a/neutronclient/neutron/v2_0/vpn/utils.py +++ b/neutronclient/neutron/v2_0/vpn/utils.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Swaminathan Vasudevan, Hewlett-Packard. -# """VPN Utilities and helper functions.""" diff --git a/neutronclient/neutron/v2_0/vpn/vpnservice.py b/neutronclient/neutron/v2_0/vpn/vpnservice.py index cf892646f..88915f187 100644 --- a/neutronclient/neutron/v2_0/vpn/vpnservice.py +++ b/neutronclient/neutron/v2_0/vpn/vpnservice.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Swaminathan Vasudevan, Hewlett-Packard. -# from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronv20 diff --git a/neutronclient/tests/unit/fw/test_cli20_firewall.py b/neutronclient/tests/unit/fw/test_cli20_firewall.py index 3b6288b63..c7872a822 100644 --- a/neutronclient/tests/unit/fw/test_cli20_firewall.py +++ b/neutronclient/tests/unit/fw/test_cli20_firewall.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: KC Wang, Big Switch Networks Inc. -# import sys diff --git a/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py b/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py index a8786dbbc..243843a00 100644 --- a/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py +++ b/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: KC Wang, Big Switch Networks Inc. -# import sys diff --git a/neutronclient/tests/unit/fw/test_cli20_firewallrule.py b/neutronclient/tests/unit/fw/test_cli20_firewallrule.py index e302d6569..72f80288e 100644 --- a/neutronclient/tests/unit/fw/test_cli20_firewallrule.py +++ b/neutronclient/tests/unit/fw/test_cli20_firewallrule.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: KC Wang, Big Switch Networks Inc. -# import sys diff --git a/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py b/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py index 2415a176c..300fa678e 100644 --- a/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py +++ b/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Ilya Shakhat, Mirantis Inc. -# import sys diff --git a/neutronclient/tests/unit/lb/test_cli20_member.py b/neutronclient/tests/unit/lb/test_cli20_member.py index d793d7e69..731459d24 100644 --- a/neutronclient/tests/unit/lb/test_cli20_member.py +++ b/neutronclient/tests/unit/lb/test_cli20_member.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Ilya Shakhat, Mirantis Inc. -# import sys diff --git a/neutronclient/tests/unit/lb/test_cli20_pool.py b/neutronclient/tests/unit/lb/test_cli20_pool.py index c293fa87f..dc9f6b5b0 100644 --- a/neutronclient/tests/unit/lb/test_cli20_pool.py +++ b/neutronclient/tests/unit/lb/test_cli20_pool.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Ilya Shakhat, Mirantis Inc. -# import sys diff --git a/neutronclient/tests/unit/lb/test_cli20_vip.py b/neutronclient/tests/unit/lb/test_cli20_vip.py index fb501415e..a3229033e 100644 --- a/neutronclient/tests/unit/lb/test_cli20_vip.py +++ b/neutronclient/tests/unit/lb/test_cli20_vip.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Ilya Shakhat, Mirantis Inc. -# import sys diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py b/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py index 96f96a68f..590700082 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Craig Tracey -# import sys diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_listener.py b/neutronclient/tests/unit/lb/v2/test_cli20_listener.py index ea421a8ec..4a70fd685 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_listener.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_listener.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Craig Tracey -# import sys diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py b/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py index 11bed0317..3f34b3cb8 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Craig Tracey -# import sys diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_member.py b/neutronclient/tests/unit/lb/v2/test_cli20_member.py index 2adb03660..4058f47d2 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_member.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_member.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Craig Tracey -# import sys diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_pool.py b/neutronclient/tests/unit/lb/v2/test_cli20_pool.py index 94defe8a6..48630f893 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_pool.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_pool.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Craig Tracey -# import sys diff --git a/neutronclient/tests/unit/test_cli20_agentschedulers.py b/neutronclient/tests/unit/test_cli20_agentschedulers.py index 4c8992ee0..8085581e1 100644 --- a/neutronclient/tests/unit/test_cli20_agentschedulers.py +++ b/neutronclient/tests/unit/test_cli20_agentschedulers.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Oleg Bondarev, Mirantis Inc. -# import sys diff --git a/neutronclient/tests/unit/test_cli20_credential.py b/neutronclient/tests/unit/test_cli20_credential.py index 53e8ecc8a..4f4a1d35d 100644 --- a/neutronclient/tests/unit/test_cli20_credential.py +++ b/neutronclient/tests/unit/test_cli20_credential.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Kyle Mestery, Cisco Systems, Inc. -# import sys diff --git a/neutronclient/tests/unit/test_cli20_metering.py b/neutronclient/tests/unit/test_cli20_metering.py index 995c29b6f..9be645f9b 100644 --- a/neutronclient/tests/unit/test_cli20_metering.py +++ b/neutronclient/tests/unit/test_cli20_metering.py @@ -1,7 +1,5 @@ # Copyright (C) 2013 eNovance SAS # -# Author: Sylvain Afchain -# # 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 diff --git a/neutronclient/tests/unit/test_cli20_networkprofile.py b/neutronclient/tests/unit/test_cli20_networkprofile.py index 8b955ed66..c5a735338 100644 --- a/neutronclient/tests/unit/test_cli20_networkprofile.py +++ b/neutronclient/tests/unit/test_cli20_networkprofile.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Kyle Mestery, Cisco Systems, Inc. -# import sys diff --git a/neutronclient/tests/unit/test_cli20_nuage_netpartition.py b/neutronclient/tests/unit/test_cli20_nuage_netpartition.py index b62d29688..18f9d3ab0 100644 --- a/neutronclient/tests/unit/test_cli20_nuage_netpartition.py +++ b/neutronclient/tests/unit/test_cli20_nuage_netpartition.py @@ -12,7 +12,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Ronak Shah, Nuage Networks, Alcatel-Lucent USA Inc. import sys diff --git a/neutronclient/tests/unit/test_cli20_policyprofile.py b/neutronclient/tests/unit/test_cli20_policyprofile.py index 791b840ba..94cbf2c57 100644 --- a/neutronclient/tests/unit/test_cli20_policyprofile.py +++ b/neutronclient/tests/unit/test_cli20_policyprofile.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Kyle Mestery, Cisco Systems, Inc. -# import sys diff --git a/neutronclient/tests/unit/test_cli20_servicetype.py b/neutronclient/tests/unit/test_cli20_servicetype.py index 0429a1a44..5ee4bd7ce 100644 --- a/neutronclient/tests/unit/test_cli20_servicetype.py +++ b/neutronclient/tests/unit/test_cli20_servicetype.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Eugene Nikanorov, Mirantis Inc. -# import sys diff --git a/neutronclient/tests/unit/test_command_meta.py b/neutronclient/tests/unit/test_command_meta.py index be50ab8ab..9b1c72134 100644 --- a/neutronclient/tests/unit/test_command_meta.py +++ b/neutronclient/tests/unit/test_command_meta.py @@ -16,7 +16,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Isaku Yamahata, Intel import logging diff --git a/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py b/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py index fc5a1a4d0..d21fb1455 100644 --- a/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py +++ b/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Swaminathan Vasudevan, Hewlett Packard. import sys diff --git a/neutronclient/tests/unit/vpn/test_cli20_ipsec_site_connection.py b/neutronclient/tests/unit/vpn/test_cli20_ipsec_site_connection.py index 077db1e16..afcd04b2a 100644 --- a/neutronclient/tests/unit/vpn/test_cli20_ipsec_site_connection.py +++ b/neutronclient/tests/unit/vpn/test_cli20_ipsec_site_connection.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Swaminathan Vasudevan, Hewlett Packard. import sys diff --git a/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py b/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py index ca413a053..7d3d0ba1e 100644 --- a/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py +++ b/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Swaminathan Vasudevan, Hewlett Packard. import sys diff --git a/neutronclient/tests/unit/vpn/test_cli20_vpnservice.py b/neutronclient/tests/unit/vpn/test_cli20_vpnservice.py index 8afd081dc..a3cab0fb5 100644 --- a/neutronclient/tests/unit/vpn/test_cli20_vpnservice.py +++ b/neutronclient/tests/unit/vpn/test_cli20_vpnservice.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Swaminathan Vasudevan, Hewlett Packard. import sys diff --git a/neutronclient/tests/unit/vpn/test_utils.py b/neutronclient/tests/unit/vpn/test_utils.py index 7b815a55a..c39f7ceb5 100644 --- a/neutronclient/tests/unit/vpn/test_utils.py +++ b/neutronclient/tests/unit/vpn/test_utils.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Swaminathan Vasudevan, Hewlett Packard. import testtools diff --git a/neutronclient/version.py b/neutronclient/version.py index 189990079..4178a0964 100644 --- a/neutronclient/version.py +++ b/neutronclient/version.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -# @author: Carl Baldwin, Hewlett-Packard import pbr.version diff --git a/tox.ini b/tox.ini index 94ab88246..e59202421 100644 --- a/tox.ini +++ b/tox.ini @@ -41,8 +41,7 @@ downloadcache = ~/cache/pip # # TODO Fix the following rules from hacking 0.9.x # E265 block comment should start with '# ' -# H105 Don't use author tags # H405 multi line docstring summary not separated with an empty line -ignore = E125,E265,H105,H405 +ignore = E125,E265,H405 show-source = true exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,tools From 631e5519cad6fa0b93780f7310ba1d771f80adae Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Mon, 23 Mar 2015 15:46:57 +0900 Subject: [PATCH 187/845] Fix E265 block comment should start with '# ' All E265 errors are now fixed and remove it from ignore list. Change-Id: I09ada4cc0f33cae5da4cbc29a4e843e3ce14e111 --- neutronclient/common/serializer.py | 10 +++------- neutronclient/neutron/v2_0/contrib/_fox_sockets.py | 2 +- .../tests/unit/fw/test_cli20_firewallpolicy.py | 2 +- tox.ini | 3 +-- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/neutronclient/common/serializer.py b/neutronclient/common/serializer.py index 0a016d17a..3293b88a4 100644 --- a/neutronclient/common/serializer.py +++ b/neutronclient/common/serializer.py @@ -12,10 +12,6 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -# -### -### Codes from neutron wsgi -### import logging from xml.etree import ElementTree as etree @@ -126,7 +122,7 @@ def to_xml_string(self, node, used_prefixes, has_atom=False): self._add_xmlns(node, used_prefixes, has_atom) return etree.tostring(node, encoding='UTF-8') - #NOTE (ameade): the has_atom should be removed after all of the + # NOTE(ameade): the has_atom should be removed after all of the # XML serializers and view builders have been updated to the current # spec that required all responses include the xmlns:atom, the has_atom # flag is to prevent current tests from breaking @@ -146,7 +142,7 @@ def _to_xml_node(self, parent, metadata, nodename, data, used_prefixes): result = etree.SubElement(parent, nodename) if ":" in nodename: used_prefixes.append(nodename.split(":", 1)[0]) - #TODO(bcwaldon): accomplish this without a type-check + # TODO(bcwaldon): accomplish this without a type-check if isinstance(data, list): if not data: result.set( @@ -162,7 +158,7 @@ def _to_xml_node(self, parent, metadata, nodename, data, used_prefixes): for item in data: self._to_xml_node(result, metadata, singular, item, used_prefixes) - #TODO(bcwaldon): accomplish this without a type-check + # TODO(bcwaldon): accomplish this without a type-check elif isinstance(data, dict): if not data: result.set( diff --git a/neutronclient/neutron/v2_0/contrib/_fox_sockets.py b/neutronclient/neutron/v2_0/contrib/_fox_sockets.py index da88eb124..63996981b 100644 --- a/neutronclient/neutron/v2_0/contrib/_fox_sockets.py +++ b/neutronclient/neutron/v2_0/contrib/_fox_sockets.py @@ -65,7 +65,7 @@ class FoxInSocketsUpdate(extension.ClientExtensionUpdate, FoxInSocket): list_columns = ['id', 'name'] def add_known_arguments(self, parser): - #_add_updatable_args(parser) + # _add_updatable_args(parser) parser.add_argument( '--name', help=_('Name of this fox socket.')) diff --git a/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py b/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py index 243843a00..703b7cd2e 100644 --- a/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py +++ b/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py @@ -57,7 +57,7 @@ def test_create_firewall_policy_with_all_params(self): position_names = ['name', ] position_values = [name, ] - #check for both str and unicode format firewall_rules_arg + # check for both str and unicode format firewall_rules_arg for firewall_rules_arg in ['rule_id1 rule_id2', u'rule_id1 rule_id2']: args = ['--description', description, '--shared', diff --git a/tox.ini b/tox.ini index e59202421..b6c33c6b5 100644 --- a/tox.ini +++ b/tox.ini @@ -40,8 +40,7 @@ downloadcache = ~/cache/pip # E125 continuation line does not distinguish itself from next logical line # # TODO Fix the following rules from hacking 0.9.x -# E265 block comment should start with '# ' # H405 multi line docstring summary not separated with an empty line -ignore = E125,E265,H405 +ignore = E125,H405 show-source = true exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,tools From 07334cbce112ebac8ad852b0b06fbd5cf99a016a Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Sun, 1 Mar 2015 22:13:46 +0900 Subject: [PATCH 188/845] Make secgroup rules more readable in security-group-show Previously security_group_rules in security-group-show is displayed in JSON dump in a format of one line per rule, but each line is too long and hard to understand rules. After this commit each rule is displayed as JSON dump with indentation and we can easily read security group rules. Partial-Bug: #1153766 Change-Id: I0a0d4afad340aee0346c73cf8881900375978858 --- neutronclient/neutron/v2_0/securitygroup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/neutronclient/neutron/v2_0/securitygroup.py b/neutronclient/neutron/v2_0/securitygroup.py index 65e05c92e..31c589cba 100644 --- a/neutronclient/neutron/v2_0/securitygroup.py +++ b/neutronclient/neutron/v2_0/securitygroup.py @@ -106,6 +106,7 @@ class ShowSecurityGroup(neutronV20.ShowCommand): resource = 'security_group' allow_names = True + json_indent = 5 class CreateSecurityGroup(neutronV20.CreateCommand): From e73f304b395efd42c6a238181903121f6a476806 Mon Sep 17 00:00:00 2001 From: Assaf Muller Date: Sun, 5 Oct 2014 14:41:50 +0300 Subject: [PATCH 189/845] Add HA router state to l3-agent-list-hosting-router Depends-On: Ie0f53b7565d53ff791b0cdcca20be2b4415b49cc Change-Id: I888fb62f9b9013f655e02a463063db549654b00e Partially-Implements: blueprint report-ha-router-master --- neutronclient/neutron/v2_0/agentscheduler.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/neutronclient/neutron/v2_0/agentscheduler.py b/neutronclient/neutron/v2_0/agentscheduler.py index db702b5bf..f7ee3e525 100644 --- a/neutronclient/neutron/v2_0/agentscheduler.py +++ b/neutronclient/neutron/v2_0/agentscheduler.py @@ -212,6 +212,10 @@ def get_parser(self, prog_name): return parser def extend_list(self, data, parsed_args): + # Show the ha_state column only if the server responds with it, + # as some plugins do not support HA routers. + if any('ha_state' in agent for agent in data): + self.list_columns.append('ha_state') for agent in data: agent['alive'] = ":-)" if agent['alive'] else 'xxx' From f3e80b8131bea119d6ebcb8e465d0510db853d19 Mon Sep 17 00:00:00 2001 From: Cedric Brandily Date: Sun, 29 Mar 2015 21:17:50 +0200 Subject: [PATCH 190/845] Prefer argparse mutual exclusion This change replaces homemade option mutual exclusion check by argparse one (ArgumentParser.add_mutually_exclusive_group). Change-Id: Ib626aba4b63db4dd635a4ef000c2dd5f19dd4446 --- neutronclient/neutron/v2_0/subnet.py | 11 ++++------- neutronclient/tests/unit/test_cli20_subnet.py | 9 +++------ 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/neutronclient/neutron/v2_0/subnet.py b/neutronclient/neutron/v2_0/subnet.py index 86c9f182d..5a051c7ff 100644 --- a/neutronclient/neutron/v2_0/subnet.py +++ b/neutronclient/neutron/v2_0/subnet.py @@ -52,10 +52,11 @@ def add_updatable_arguments(parser): parser.add_argument( '--name', help=_('Name of this subnet.')) - parser.add_argument( + gateway_sg = parser.add_mutually_exclusive_group() + gateway_sg.add_argument( '--gateway', metavar='GATEWAY_IP', help=_('Gateway IP of this subnet.')) - parser.add_argument( + gateway_sg.add_argument( '--no-gateway', action='store_true', help=_('No distribution of gateway.')) @@ -102,17 +103,13 @@ def add_updatable_arguments(parser): def updatable_args2body(parsed_args, body, for_create=True): - if parsed_args.gateway and parsed_args.no_gateway: - raise exceptions.CommandError(_("--gateway option and " - "--no-gateway option can " - "not be used same time")) if parsed_args.disable_dhcp and parsed_args.enable_dhcp: raise exceptions.CommandError(_( "You cannot enable and disable DHCP at the same time.")) if parsed_args.no_gateway: body['subnet'].update({'gateway_ip': None}) - if parsed_args.gateway: + elif parsed_args.gateway: body['subnet'].update({'gateway_ip': parsed_args.gateway}) if parsed_args.name: body['subnet'].update({'name': parsed_args.name}) diff --git a/neutronclient/tests/unit/test_cli20_subnet.py b/neutronclient/tests/unit/test_cli20_subnet.py index a98af01f1..e6b15b3d9 100644 --- a/neutronclient/tests/unit/test_cli20_subnet.py +++ b/neutronclient/tests/unit/test_cli20_subnet.py @@ -68,12 +68,9 @@ def test_create_subnet_with_bad_gateway_option(self): args = ['--gateway', gateway, '--no-gateway', netid, cidr] position_names = ['ip_version', 'network_id', 'cidr', 'gateway_ip'] position_values = [4, netid, cidr, None] - try: - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - except Exception: - return - self.fail('No exception for bad gateway option') + self.assertRaises( + SystemExit, self._test_create_resource, + resource, cmd, name, myid, args, position_names, position_values) def _test_create_resource_and_catch_command_error(self, tested_args, should_fail, From 6588c42430e6ec46b71c9ccb20215e4db80caf3b Mon Sep 17 00:00:00 2001 From: Cedric Brandily Date: Thu, 2 Apr 2015 11:32:31 +0200 Subject: [PATCH 191/845] Support fwaasrouterinsertion extension fwaasrouterinsertion extension allows to specify which routers implement a firewall (on create/update). This changes adds its support by allowing to: * set routers with --router option in firewall-create/update commands, * unset routers with --no-routers option in firewall-update command. Change-Id: I654c1ddd4140a60b8a09237f7142ad211e951d92 Closes-Bug: #1435264 --- neutronclient/neutron/v2_0/fw/firewall.py | 36 +++++++++++++++++- .../tests/unit/fw/test_cli20_firewall.py | 37 +++++++++++++++++++ 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/neutronclient/neutron/v2_0/fw/firewall.py b/neutronclient/neutron/v2_0/fw/firewall.py index 149036a2a..1fd363aca 100644 --- a/neutronclient/neutron/v2_0/fw/firewall.py +++ b/neutronclient/neutron/v2_0/fw/firewall.py @@ -63,15 +63,27 @@ def add_known_arguments(self, parser): dest='admin_state', action='store_false', help=_('Set admin state up to false.')) + parser.add_argument( + '--router', + dest='routers', + metavar='ROUTER', + action='append', + help=_('Firewall associated router names or IDs (requires FWaaS ' + 'router insertion extension, this option can be repeated)')) def args2body(self, parsed_args): + client = self.get_client() _policy_id = neutronv20.find_resourceid_by_name_or_id( - self.get_client(), 'firewall_policy', + client, 'firewall_policy', parsed_args.firewall_policy_id) body = { self.resource: { 'firewall_policy_id': _policy_id, 'admin_state_up': parsed_args.admin_state, }, } + if parsed_args.routers: + body[self.resource]['router_ids'] = [ + neutronv20.find_resourceid_by_name_or_id(client, 'router', r) + for r in parsed_args.routers] neutronv20.update_dict(parsed_args, body[self.resource], ['name', 'description', 'shared', 'tenant_id']) @@ -87,14 +99,34 @@ def add_known_arguments(self, parser): parser.add_argument( '--policy', metavar='POLICY', help=_('Firewall policy name or ID.')) + router_sg = parser.add_mutually_exclusive_group() + router_sg.add_argument( + '--router', + dest='routers', + metavar='ROUTER', + action='append', + help=_('Firewall associated router names or IDs (requires FWaaS ' + 'router insertion extension, this option can be repeated)')) + router_sg.add_argument( + '--no-routers', + action='store_true', + help=_('Associate no routers with the firewall (requires FWaaS ' + 'router insertion extension)')) def args2body(self, parsed_args): data = {} + client = self.get_client() if parsed_args.policy: _policy_id = neutronv20.find_resourceid_by_name_or_id( - self.get_client(), 'firewall_policy', + client, 'firewall_policy', parsed_args.policy) data['firewall_policy_id'] = _policy_id + if parsed_args.routers: + data['router_ids'] = [ + neutronv20.find_resourceid_by_name_or_id(client, 'router', r) + for r in parsed_args.routers] + elif parsed_args.no_routers: + data['router_ids'] = [] return {self.resource: data} diff --git a/neutronclient/tests/unit/fw/test_cli20_firewall.py b/neutronclient/tests/unit/fw/test_cli20_firewall.py index 3b6288b63..b69f6b1f6 100644 --- a/neutronclient/tests/unit/fw/test_cli20_firewall.py +++ b/neutronclient/tests/unit/fw/test_cli20_firewall.py @@ -61,6 +61,19 @@ def test_create_firewall_with_all_params(self): shared=True, admin_state_up=False, tenant_id=tenant_id) + def test_create_firewall_with_routers(self): + resource = 'firewall' + cmd = firewall.CreateFirewall(test_cli20.MyApp(sys.stdout), None) + name = 'my-name' + policy_id = 'my-policy-id' + my_id = 'my-id' + args = ['--router', 'fake-id', '--router', 'fake-name', policy_id] + router_ids = ['fake-id', 'fake-name'] + position_names = ['firewall_policy_id', 'router_ids'] + position_values = [policy_id, router_ids] + self._test_create_resource(resource, cmd, name, my_id, args, + position_names, position_values) + def test_list_firewalls(self): """firewall-list.""" resources = "firewalls" @@ -120,6 +133,30 @@ def test_update_firewall_using_policy_name(self): ['myid', '--policy', 'newpolicy'], {'firewall_policy_id': 'newpolicy'}) + def test_update_firewall_with_routers(self): + resource = 'firewall' + cmd = firewall.UpdateFirewall(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource( + resource, cmd, 'myid', + ['myid', '--router', 'fake-id', '--router', 'fake-name'], + {'router_ids': ['fake-id', 'fake-name']}) + + def test_update_firewall_with_no_routers(self): + resource = 'firewall' + cmd = firewall.UpdateFirewall(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource( + resource, cmd, 'myid', + ['myid', '--no-routers'], {'router_ids': []}) + + def test_update_firewall_with_bad_router_options(self): + resource = 'firewall' + cmd = firewall.UpdateFirewall(test_cli20.MyApp(sys.stdout), None) + self.assertRaises( + SystemExit, + self._test_update_resource, + resource, cmd, 'myid', + ['myid', '--no-routers', '--router', 'fake-id'], {}) + def test_delete_firewall(self): """firewall-delete my-id.""" resource = 'firewall' From 052b9da6fae964c3719b65f74baa89ab8b4344f4 Mon Sep 17 00:00:00 2001 From: Vikram Hosakote Date: Tue, 31 Mar 2015 10:02:27 -0700 Subject: [PATCH 192/845] 'neutron port-create' missing help info for --binding:vnic-type --binding:vnic-type can be specified for 'neutron port-create' to create SR-IOV vNIC ports, but the help info is missing for --binding:vnic-type. This patch fixes the above bug. Below is the output. $ neutron port-create --help | grep -i vnic [--vnic-type ] --vnic-type VNIC type for this port. DocImpact Change-Id: Ib31c345d84df8861a058fdd221bb7bc9fc33b6c9 Closes-bug: #1432393 --- neutronclient/neutron/v2_0/port.py | 10 ++++ neutronclient/tests/unit/test_cli20_port.py | 60 +++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index 79ac1bfcd..3a7ec7ec4 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -214,6 +214,14 @@ def add_known_arguments(self, parser): parser.add_argument( '--mac_address', help=argparse.SUPPRESS) + parser.add_argument( + '--vnic-type', metavar='', + choices=['direct', 'macvtap', 'normal'], + help=_('VNIC type for this port.')) + parser.add_argument( + '--vnic_type', + choices=['direct', 'macvtap', 'normal'], + help=argparse.SUPPRESS) self.add_arguments_secgroup(parser) self.add_arguments_extradhcpopt(parser) @@ -232,6 +240,8 @@ def args2body(self, parsed_args): body['port'].update({'mac_address': parsed_args.mac_address}) if parsed_args.tenant_id: body['port'].update({'tenant_id': parsed_args.tenant_id}) + if parsed_args.vnic_type: + body['port'].update({'binding:vnic_type': parsed_args.vnic_type}) self.args2body_secgroup(parsed_args, body['port']) self.args2body_extradhcpopt(parsed_args, body['port']) diff --git a/neutronclient/tests/unit/test_cli20_port.py b/neutronclient/tests/unit/test_cli20_port.py index cda9657e2..59d9804ee 100644 --- a/neutronclient/tests/unit/test_cli20_port.py +++ b/neutronclient/tests/unit/test_cli20_port.py @@ -113,6 +113,66 @@ def test_create_port_full(self): self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) + def test_create_port_vnic_type_normal(self): + """Create port: --vnic_type normal netid.""" + resource = 'port' + cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + netid = 'netid' + args = ['--vnic_type', 'normal', netid] + position_names = ['binding:vnic_type', 'network_id'] + position_values = ['normal', netid] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + # Test dashed options + args = ['--vnic-type', 'normal', netid] + position_names = ['binding:vnic_type', 'network_id'] + position_values = ['normal', netid] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_create_port_vnic_type_direct(self): + """Create port: --vnic_type direct netid.""" + resource = 'port' + cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + netid = 'netid' + args = ['--vnic_type', 'direct', netid] + position_names = ['binding:vnic_type', 'network_id'] + position_values = ['direct', netid] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + # Test dashed options + args = ['--vnic-type', 'direct', netid] + position_names = ['binding:vnic_type', 'network_id'] + position_values = ['direct', netid] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_create_port_vnic_type_macvtap(self): + """Create port: --vnic_type macvtap netid.""" + resource = 'port' + cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + netid = 'netid' + args = ['--vnic_type', 'macvtap', netid] + position_names = ['binding:vnic_type', 'network_id'] + position_values = ['macvtap', netid] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + # Test dashed options + args = ['--vnic-type', 'macvtap', netid] + position_names = ['binding:vnic_type', 'network_id'] + position_values = ['macvtap', netid] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + def test_create_port_tenant(self): """Create port: --tenant_id tenantid netid.""" resource = 'port' From af3fcb7420a45394e056b2caf9decbeeb24263a8 Mon Sep 17 00:00:00 2001 From: Pritesh Kothari Date: Thu, 5 Mar 2015 22:54:45 -0800 Subject: [PATCH 193/845] Adding VLAN Transparency support to neutronclient Adding VLAN Transparency support to neutronclient, it allows to create new vlan transparent networks. It adds support for displaying the network vlan transparency support via net-list. DocImpact Depends-on: Ie87087a70b83dab589419aa5c17ce7ccafd64cbd Implements: blueprint nfv-vlan-trunks Change-Id: I24c2eecd34123933f98b596654baaf76ed1453e8 --- neutronclient/neutron/v2_0/network.py | 7 +++++++ neutronclient/tests/unit/test_cli20_network.py | 14 ++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/neutronclient/neutron/v2_0/network.py b/neutronclient/neutron/v2_0/network.py index 44adff744..5338cf54b 100644 --- a/neutronclient/neutron/v2_0/network.py +++ b/neutronclient/neutron/v2_0/network.py @@ -17,6 +17,7 @@ import argparse from neutronclient.common import exceptions +from neutronclient.common import utils from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 @@ -139,6 +140,11 @@ def add_known_arguments(self, parser): metavar='', help=_('VLAN ID for VLAN networks or tunnel-id for GRE/VXLAN' ' networks.')) + utils.add_boolean_argument( + parser, + '--vlan-transparent', + default=argparse.SUPPRESS, + help=_('Create a vlan transparent network.')) parser.add_argument( 'name', metavar='NAME', help=_('Name of network to create.')) @@ -149,6 +155,7 @@ def args2body(self, parsed_args): 'admin_state_up': parsed_args.admin_state}, } neutronV20.update_dict(parsed_args, body['network'], ['shared', 'tenant_id', 'router:external', + 'vlan_transparent', 'provider:network_type', 'provider:physical_network', 'provider:segmentation_id']) diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index 1ac49b637..8605cf77d 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -136,6 +136,20 @@ def test_create_network_state(self): position_names, position_values, admin_state_up=False) + def test_create_network_vlan_transparent(self): + """Create net: myname --vlan-transparent True.""" + resource = 'network' + cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + args = ['--vlan-transparent', 'True', name] + vlantrans = {'vlan_transparent': 'True'} + position_names = ['name', ] + position_values = [name, ] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values, + **vlantrans) + def test_list_nets_empty_with_column(self): resources = "networks" cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) From 6e10447aebe8300ea2b5f85410b9ca938e891dbd Mon Sep 17 00:00:00 2001 From: zengfagao Date: Thu, 19 Mar 2015 09:37:45 -0700 Subject: [PATCH 194/845] Add Neutron subnet-create with subnetpool Add neutron subnet-create with subnetpool feature. With subnetpool, user can either have prefixlen or cidr. The cidr for subnet is optional parameter with subnetpool. Change-Id: I795b233985f9ab7cb59d7318a5d7a1e9c4431a1c Partially-Implements: blueprint subnet-allocation --- neutronclient/neutron/v2_0/subnet.py | 37 ++++++++++++++---- neutronclient/tests/unit/test_cli20_subnet.py | 38 +++++++++++++++++++ 2 files changed, 67 insertions(+), 8 deletions(-) diff --git a/neutronclient/neutron/v2_0/subnet.py b/neutronclient/neutron/v2_0/subnet.py index 86c9f182d..0772c4f4f 100644 --- a/neutronclient/neutron/v2_0/subnet.py +++ b/neutronclient/neutron/v2_0/subnet.py @@ -178,7 +178,7 @@ def add_known_arguments(self, parser): 'network_id', metavar='NETWORK', help=_('Network ID or name this subnet belongs to.')) parser.add_argument( - 'cidr', metavar='CIDR', + 'cidr', nargs='?', metavar='CIDR', help=_('CIDR of subnet to create.')) parser.add_argument( '--ipv6-ra-mode', @@ -188,19 +188,40 @@ def add_known_arguments(self, parser): '--ipv6-address-mode', choices=['dhcpv6-stateful', 'dhcpv6-stateless', 'slaac'], help=_('IPv6 address mode.')) + parser.add_argument( + '--subnetpool', metavar='SUBNETPOOL', + help=_('ID or name of subnetpool from which this subnet ' + 'will obtain a CIDR.')) + parser.add_argument( + '--prefixlen', metavar='PREFIX_LENGTH', + help=_('Prefix length for subnet allocation from subnetpool.')) def args2body(self, parsed_args): - if parsed_args.ip_version == 4 and parsed_args.cidr.endswith('/32'): - self.log.warning(_("An IPv4 subnet with a /32 CIDR will have " - "only one usable IP address so the device " - "attached to it will not have any IP " - "connectivity.")) _network_id = neutronV20.find_resourceid_by_name_or_id( self.get_client(), 'network', parsed_args.network_id) - body = {'subnet': {'cidr': parsed_args.cidr, - 'network_id': _network_id, + body = {'subnet': {'network_id': _network_id, 'ip_version': parsed_args.ip_version, }, } + if parsed_args.cidr: + # With subnetpool, cidr is now optional for creating subnet. + cidr = parsed_args.cidr + body['subnet'].update({'cidr': cidr}) + unusable_cidr = '/32' if parsed_args.ip_version == 4 else '/128' + if cidr.endswith(unusable_cidr): + self.log.warning(_("An IPv%(ip)d subnet with a %(cidr)s CIDR " + "will have only one usable IP address so " + "the device attached to it will not have " + "any IP connectivity.") + % {"ip": parsed_args.ip_version, + "cidr": unusable_cidr}) + + if parsed_args.prefixlen: + body['subnet'].update({'prefixlen': parsed_args.prefixlen}) + if parsed_args.subnetpool: + _subnetpool_id = neutronV20.find_resourceid_by_name_or_id( + self.get_client(), 'subnetpool', parsed_args.subnetpool) + body['subnet'].update({'subnetpool_id': _subnetpool_id}) + updatable_args2body(parsed_args, body) if parsed_args.tenant_id: body['subnet'].update({'tenant_id': parsed_args.tenant_id}) diff --git a/neutronclient/tests/unit/test_cli20_subnet.py b/neutronclient/tests/unit/test_cli20_subnet.py index a98af01f1..9bfadb6ca 100644 --- a/neutronclient/tests/unit/test_cli20_subnet.py +++ b/neutronclient/tests/unit/test_cli20_subnet.py @@ -477,6 +477,44 @@ def test_create_subnet_with_ipv6_address_mode_ipv4(self): resource, cmd, name, myid, args, position_names, position_values, tenant_id='tenantid') + def test_create_subnet_with_subnetpool_ipv4_with_cidr_wildcard(self): + resource = 'subnet' + cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + netid = 'netid' + cidr = 'cidrwildcard' + args = ['--tenant_id', 'tenantid', + '--ip-version', '4', + '--ipv6-address-mode', 'slaac', + '--subnetpool', 'subnetpool_id', + netid, cidr] + position_names = ['ip_version', 'ipv6_address_mode', + 'network_id', 'subnetpool_id', 'cidr'] + position_values = [4, None, netid, 'subnetpool_id', cidr] + self.assertRaises(exceptions.CommandError, self._test_create_resource, + resource, cmd, name, myid, args, position_names, + position_values, tenant_id='tenantid') + + def test_create_subnet_with_subnetpool_ipv4_with_prefixlen(self): + resource = 'subnet' + cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + netid = 'netid' + args = ['--tenant_id', 'tenantid', + '--ip-version', '4', + '--ipv6-address-mode', 'slaac', + '--subnetpool', 'subnetpool_id', + '--prefixlen', '31', + netid] + position_names = ['ip_version', 'ipv6_address_mode', + 'network_id', 'subnetpool_id'] + position_values = [4, None, netid, 'subnetpool_id'] + self.assertRaises(exceptions.CommandError, self._test_create_resource, + resource, cmd, name, myid, args, position_names, + position_values, tenant_id='tenantid') + def test_list_subnets_detail(self): """List subnets: -D.""" resources = "subnets" From c242441f008038d4b557e4cb0bfda44dbe603dd4 Mon Sep 17 00:00:00 2001 From: Carl Baldwin Date: Fri, 27 Mar 2015 21:54:50 +0000 Subject: [PATCH 195/845] Allow passing None for subnetpool Change-Id: I90be335bbe466381ba40423cfef345ca380676b5 Partially-Implements: blueprint subnet-allocation --- neutronclient/neutron/v2_0/subnet.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/neutronclient/neutron/v2_0/subnet.py b/neutronclient/neutron/v2_0/subnet.py index 0772c4f4f..1e89c3ba2 100644 --- a/neutronclient/neutron/v2_0/subnet.py +++ b/neutronclient/neutron/v2_0/subnet.py @@ -218,8 +218,11 @@ def args2body(self, parsed_args): if parsed_args.prefixlen: body['subnet'].update({'prefixlen': parsed_args.prefixlen}) if parsed_args.subnetpool: - _subnetpool_id = neutronV20.find_resourceid_by_name_or_id( - self.get_client(), 'subnetpool', parsed_args.subnetpool) + if parsed_args.subnetpool == 'None': + _subnetpool_id = None + else: + _subnetpool_id = neutronV20.find_resourceid_by_name_or_id( + self.get_client(), 'subnetpool', parsed_args.subnetpool) body['subnet'].update({'subnetpool_id': _subnetpool_id}) updatable_args2body(parsed_args, body) From d6cfd34e13426b88b40cb6ef7fdcd60d9b7dcb6b Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Thu, 9 Apr 2015 14:41:36 +0900 Subject: [PATCH 196/845] Revert "Remove unused AlreadyAttachedClient" The removal of AlreadyAtatchedClient exception from neutronclient suddenly causes the failure of neutronclient icehouse job. It is better to wait the removal of the exception until Icehouse EOL (which will come soon). This reverts commit 30b198edec988d7c969c1edd4a2754bd9c9c79f4. Closes-Bug: #1441969 Change-Id: Ifa02a4ca39bc9c0fe9b65d5b0d66c588885cb12c --- neutronclient/common/exceptions.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/neutronclient/common/exceptions.py b/neutronclient/common/exceptions.py index 9a12f1995..9728fbdd7 100644 --- a/neutronclient/common/exceptions.py +++ b/neutronclient/common/exceptions.py @@ -148,6 +148,12 @@ class OverQuotaClient(Conflict): pass +# TODO(amotoki): It is unused in Neutron, but it is referred to +# in Horizon code. After Horizon code is updated, remove it. +class AlreadyAttachedClient(Conflict): + pass + + class IpAddressGenerationFailureClient(Conflict): pass From b978f909014cbc398c21bfae917fcece89a36e38 Mon Sep 17 00:00:00 2001 From: zengfagao Date: Thu, 26 Feb 2015 13:39:00 -0800 Subject: [PATCH 197/845] Add Neutron subnetpool API Add Neutron subnetpool listing, creating, updating, showing and deleting Client API. neutron subnetpool-list neutron subnetpool-create neutron subnetpool-update neutron subnetpool-show neutron subnetpool-delete Change-Id: I19a6782d33609609f43353df8293864e60ead817 Partially-Implements: blueprint subnet-allocation --- neutronclient/common/constants.py | 1 + neutronclient/neutron/v2_0/subnetpool.py | 101 ++++++++++++++ neutronclient/shell.py | 6 + neutronclient/tests/unit/test_cli20.py | 2 +- .../tests/unit/test_cli20_subnetpool.py | 132 ++++++++++++++++++ neutronclient/v2_0/client.py | 28 ++++ 6 files changed, 269 insertions(+), 1 deletion(-) create mode 100644 neutronclient/neutron/v2_0/subnetpool.py create mode 100644 neutronclient/tests/unit/test_cli20_subnetpool.py diff --git a/neutronclient/common/constants.py b/neutronclient/common/constants.py index 305b5df05..525a04058 100644 --- a/neutronclient/common/constants.py +++ b/neutronclient/common/constants.py @@ -36,6 +36,7 @@ PLURALS = {'networks': 'network', 'ports': 'port', 'subnets': 'subnet', + 'subnetpools': 'subnetpool', 'dns_nameservers': 'dns_nameserver', 'host_routes': 'host_route', 'allocation_pools': 'allocation_pool', diff --git a/neutronclient/neutron/v2_0/subnetpool.py b/neutronclient/neutron/v2_0/subnetpool.py new file mode 100644 index 000000000..54926b0d9 --- /dev/null +++ b/neutronclient/neutron/v2_0/subnetpool.py @@ -0,0 +1,101 @@ +# Copyright 2015 OpenStack Foundation. +# 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. +# + +from neutronclient.i18n import _ +from neutronclient.neutron import v2_0 as neutronV20 + + +def add_updatable_arguments(parser): + parser.add_argument( + '--min-prefixlen', type=int, + help=_('Subnetpool minimum prefix length.')) + parser.add_argument( + '--max-prefixlen', type=int, + help=_('Subnetpool maximum prefix length.')) + parser.add_argument( + '--default-prefixlen', type=int, + help=_('Subnetpool default prefix length.')) + parser.add_argument( + '--pool-prefix', + action='append', dest='prefixes', + help=_('Subnetpool prefixes (This option can be repeated).')) + + +def updatable_args2body(parsed_args, body, for_create=True): + neutronV20.update_dict(parsed_args, body['subnetpool'], + ['name', 'prefixes', 'default_prefixlen', + 'min_prefixlen', 'max_prefixlen']) + + +class ListSubnetPool(neutronV20.ListCommand): + """List subnetpools that belong to a given tenant.""" + + resource = 'subnetpool' + list_columns = ['id', 'name', 'prefixes', + 'default_prefixlen'] + pagination_support = True + sorting_support = True + + +class ShowSubnetPool(neutronV20.ShowCommand): + """Show information of a given subnetpool.""" + + resource = 'subnetpool' + + +class CreateSubnetPool(neutronV20.CreateCommand): + """Create a subnetpool for a given tenant.""" + + resource = 'subnetpool' + + def add_known_arguments(self, parser): + add_updatable_arguments(parser) + parser.add_argument( + '--shared', + action='store_true', + help=_('Set the subnetpool as shared.')) + parser.add_argument( + 'name', + help=_('Name of subnetpool to create.')) + + def args2body(self, parsed_args): + body = {'subnetpool': {'prefixes': parsed_args.prefixes}} + updatable_args2body(parsed_args, body) + if parsed_args.shared: + body['subnetpool']['shared'] = True + return body + + +class DeleteSubnetPool(neutronV20.DeleteCommand): + """Delete a given subnetpool.""" + + resource = 'subnetpool' + + +class UpdateSubnetPool(neutronV20.UpdateCommand): + """Update subnetpool's information.""" + + resource = 'subnetpool' + + def add_known_arguments(self, parser): + add_updatable_arguments(parser) + parser.add_argument('--name', + help=_('Name of subnetpool to update.')) + + def args2body(self, parsed_args): + body = {'subnetpool': {}} + updatable_args2body(parsed_args, body, for_create=False) + return body diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 8acfc19fc..bf77b4a8c 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -76,6 +76,7 @@ from neutronclient.neutron.v2_0 import securitygroup from neutronclient.neutron.v2_0 import servicetype from neutronclient.neutron.v2_0 import subnet +from neutronclient.neutron.v2_0 import subnetpool from neutronclient.neutron.v2_0.vpn import ikepolicy from neutronclient.neutron.v2_0.vpn import ipsec_site_connection from neutronclient.neutron.v2_0.vpn import ipsecpolicy @@ -142,6 +143,11 @@ class BashCompletionCommand(openstack_command.OpenStackCommand): 'subnet-create': subnet.CreateSubnet, 'subnet-delete': subnet.DeleteSubnet, 'subnet-update': subnet.UpdateSubnet, + 'subnetpool-list': subnetpool.ListSubnetPool, + 'subnetpool-show': subnetpool.ShowSubnetPool, + 'subnetpool-create': subnetpool.CreateSubnetPool, + 'subnetpool-delete': subnetpool.DeleteSubnetPool, + 'subnetpool-update': subnetpool.UpdateSubnetPool, 'port-list': port.ListPort, 'port-show': port.ShowPort, 'port-create': port.CreatePort, diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index ee6d7ac8c..e9432534e 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -222,7 +222,7 @@ def _test_create_resource(self, resource, cmd, name, myid, args, 'policy_profile', 'ikepolicy', 'ipsecpolicy', 'metering_label', 'metering_label_rule', 'net_partition', - 'fox_socket'] + 'fox_socket', 'subnetpool'] if not cmd_resource: cmd_resource = resource if (resource in non_admin_status_resources): diff --git a/neutronclient/tests/unit/test_cli20_subnetpool.py b/neutronclient/tests/unit/test_cli20_subnetpool.py new file mode 100644 index 000000000..782d74d78 --- /dev/null +++ b/neutronclient/tests/unit/test_cli20_subnetpool.py @@ -0,0 +1,132 @@ +# Copyright 2015 OpenStack Foundation. +# 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 sys + +from mox3 import mox + +from neutronclient.common import exceptions +from neutronclient.neutron.v2_0 import subnetpool +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20SubnetPoolJSON(test_cli20.CLITestV20Base): + def setUp(self): + super(CLITestV20SubnetPoolJSON, self).setUp(plurals={'tags': 'tag'}) + + def test_create_subnetpool_shared(self): + """Create subnetpool: myname.""" + resource = 'subnetpool' + cmd = subnetpool.CreateSubnetPool(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + min_prefixlen = 30 + prefix1 = '10.11.12.0/24' + prefix2 = '12.11.13.0/24' + args = [name, '--min-prefixlen', str(min_prefixlen), + '--pool-prefix', prefix1, '--pool-prefix', prefix2, + '--shared'] + position_names = ['name', 'min_prefixlen', 'prefixes', 'shared'] + position_values = [name, min_prefixlen, [prefix1, prefix2], True] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_create_subnetpool_not_shared(self): + """Create subnetpool: myname.""" + resource = 'subnetpool' + cmd = subnetpool.CreateSubnetPool(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + min_prefixlen = 30 + prefix1 = '10.11.12.0/24' + prefix2 = '12.11.13.0/24' + args = [name, '--min-prefixlen', str(min_prefixlen), + '--pool-prefix', prefix1, '--pool-prefix', prefix2] + position_names = ['name', 'min_prefixlen', 'prefixes'] + position_values = [name, min_prefixlen, [prefix1, prefix2]] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_create_subnetpool_with_unicode(self): + """Create subnetpool: u'\u7f51\u7edc'.""" + resource = 'subnetpool' + cmd = subnetpool.CreateSubnetPool(test_cli20.MyApp(sys.stdout), None) + name = u'\u7f51\u7edc' + myid = 'myid' + min_prefixlen = 30 + prefixes = '10.11.12.0/24' + args = [name, '--min-prefixlen', str(min_prefixlen), + '--pool-prefix', prefixes] + position_names = ['name', 'min_prefixlen', 'prefixes'] + position_values = [name, min_prefixlen, [prefixes]] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_list_subnetpool_pagination(self): + cmd = subnetpool.ListSubnetPool(test_cli20.MyApp(sys.stdout), None) + self.mox.StubOutWithMock(subnetpool.ListSubnetPool, "extend_list") + subnetpool.ListSubnetPool.extend_list(mox.IsA(list), mox.IgnoreArg()) + self._test_list_resources_with_pagination("subnetpools", cmd) + self.mox.VerifyAll() + self.mox.UnsetStubs() + + def test_list_subnetpools_sort(self): + """List subnetpools: --sort-key name --sort-key id --sort-key asc + --sort-key desc + """ + resources = "subnetpools" + cmd = subnetpool.ListSubnetPool(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, + sort_key=["name", "id"], + sort_dir=["asc", "desc"]) + + def test_list_subnetpools_limit(self): + """List subnetpools: -P.""" + resources = "subnetpools" + cmd = subnetpool.ListSubnetPool(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, page_size=1000) + + def test_update_subnetpool_exception(self): + """Update subnetpool: myid.""" + resource = 'subnetpool' + cmd = subnetpool.UpdateSubnetPool(test_cli20.MyApp(sys.stdout), None) + self.assertRaises(exceptions.CommandError, self._test_update_resource, + resource, cmd, 'myid', ['myid'], {}) + + def test_update_subnetpool(self): + """Update subnetpool: myid --name myname.""" + resource = 'subnetpool' + cmd = subnetpool.UpdateSubnetPool(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--name', 'myname'], + {'name': 'myname'} + ) + + def test_show_subnetpool(self): + """Show subnetpool: --fields id --fields name myid.""" + resource = 'subnetpool' + cmd = subnetpool.ShowSubnetPool(test_cli20.MyApp(sys.stdout), None) + args = ['--fields', 'id', '--fields', 'name', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, args, + ['id', 'name']) + + def test_delete_subnetpool(self): + """Delete subnetpool: subnetpoolid.""" + resource = 'subnetpool' + cmd = subnetpool.DeleteSubnetPool(test_cli20.MyApp(sys.stdout), None) + myid = 'myid' + args = [myid] + self._test_delete_resource(resource, cmd, myid, args) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 5cf112573..52640339e 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -339,6 +339,8 @@ class Client(ClientBase): port_path = "/ports/%s" subnets_path = "/subnets" subnet_path = "/subnets/%s" + subnetpools_path = "/subnetpools" + subnetpool_path = "/subnetpools/%s" quotas_path = "/quotas" quota_path = "/quotas/%s" extensions_path = "/extensions" @@ -604,6 +606,32 @@ def delete_subnet(self, subnet): """Deletes the specified subnet.""" return self.delete(self.subnet_path % (subnet)) + @APIParamsCall + def list_subnetpools(self, retrieve_all=True, **_params): + """Fetches a list of all subnetpools for a tenant.""" + return self.list('subnetpools', self.subnetpools_path, retrieve_all, + **_params) + + @APIParamsCall + def show_subnetpool(self, subnetpool, **_params): + """Fetches information of a certain subnetpool.""" + return self.get(self.subnetpool_path % (subnetpool), params=_params) + + @APIParamsCall + def create_subnetpool(self, body=None): + """Creates a new subnetpool.""" + return self.post(self.subnetpools_path, body=body) + + @APIParamsCall + def update_subnetpool(self, subnetpool, body=None): + """Updates a subnetpool.""" + return self.put(self.subnetpool_path % (subnetpool), body=body) + + @APIParamsCall + def delete_subnetpool(self, subnetpool): + """Deletes the specified subnetpool.""" + return self.delete(self.subnetpool_path % (subnetpool)) + @APIParamsCall def list_routers(self, retrieve_all=True, **_params): """Fetches a list of all routers for a tenant.""" From ae09debf985c459941f18e89a9f281fa3d548407 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Sun, 22 Mar 2015 04:39:02 +0900 Subject: [PATCH 198/845] Add basic functional tests for client library We don't have a test coverage on neutronclient with HTTPClient. CLI tests in our unit tests mock out the HTTP client and We don't have a test combined with keystoneclient. This leads to bugs in python bindings like bug 1431449. Neutronclient has two backend HTTP client (HTTPClient and SessionClient) so it is better to have functional tests for client library itself. Change-Id: I069a95d5611371730584cf166ceaae6d8e0f6e05 --- .../tests/functional/test_clientlib.py | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 neutronclient/tests/functional/test_clientlib.py diff --git a/neutronclient/tests/functional/test_clientlib.py b/neutronclient/tests/functional/test_clientlib.py new file mode 100644 index 000000000..0ad84dbbe --- /dev/null +++ b/neutronclient/tests/functional/test_clientlib.py @@ -0,0 +1,88 @@ +# 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 os +import uuid + +from keystoneclient.auth.identity import v2 as v2_auth +from keystoneclient import discover +from keystoneclient import session +from tempest_lib import base +import testtools + +from neutronclient.common import exceptions +from neutronclient.v2_0 import client as v2_client + +# This module tests client library functionalities with +# Keystone client. Neutron client supports two types of +# HTTP clients (HTTPClient and SessionClient), +# so it is better to test both clients. + + +class LibraryTestBase(base.BaseTestCase): + + def setUp(self): + super(LibraryTestBase, self).setUp() + self.client = self._get_client() + + +class Libv2HTTPClientTestBase(LibraryTestBase): + + def _get_client(self): + return v2_client.Client(username=os.environ.get('OS_USERNAME'), + password=os.environ.get('OS_PASSWORD'), + tenant_name=os.environ.get('OS_TENANT_NAME'), + auth_url=os.environ.get('OS_AUTH_URL')) + + +class Libv2SessionClientTestBase(LibraryTestBase): + + def _get_client(self): + session_params = {} + ks_session = session.Session.construct(session_params) + ks_discover = discover.Discover(session=ks_session, + auth_url=os.environ.get('OS_AUTH_URL')) + # At the moment, we use keystone v2 API + v2_auth_url = ks_discover.url_for('2.0') + ks_session.auth = v2_auth.Password( + v2_auth_url, + username=os.environ.get('OS_USERNAME'), + password=os.environ.get('OS_PASSWORD'), + tenant_name=os.environ.get('OS_TENANT_NAME')) + return v2_client.Client(session=ks_session) + + +class LibraryTestCase(object): + + def test_list_network(self): + nets = self.client.list_networks() + self.assertIsInstance(nets['networks'], list) + + def test_post_put_delele_network(self): + name = str(uuid.uuid4()) + net = self.client.create_network({'network': {'name': name}}) + net_id = net['network']['id'] + self.assertEqual(name, net['network']['name']) + name2 = str(uuid.uuid4()) + net = self.client.update_network(net_id, {'network': {'name': name2}}) + self.assertEqual(name2, net['network']['name']) + self.client.delete_network(net_id) + with testtools.ExpectedException(exceptions.NetworkNotFoundClient): + self.client.show_network(net_id) + + +class LibraryHTTPClientTest(LibraryTestCase, Libv2HTTPClientTestBase): + pass + + +class LibrarySessionClientTest(LibraryTestCase, Libv2SessionClientTestBase): + pass From cdfcf3c153ee7860658cf70206c0da81da561bca Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Mon, 23 Mar 2015 15:57:37 +0900 Subject: [PATCH 199/845] Fix one remaining E125 error and remove it from ignore list E125 continuation line does not distinguish itself from next logical line Also update the comment of H405 since multi line docstring is used frequently in unit test documentation and it seems better to hold it in the ignore list not temporarily. Change-Id: Ic24e6cebe0aa531e5a4556d3e23c752358bf3696 --- neutronclient/tests/unit/test_cli20.py | 6 +++--- tox.ini | 6 ++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index ee6d7ac8c..556830143 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -641,9 +641,9 @@ class ClientV2UnicodeTestXML(ClientV2TestJson): class CLITestV20ExceptionHandler(CLITestV20Base): def _test_exception_handler_v20( - self, expected_exception, status_code, expected_msg, - error_type=None, error_msg=None, error_detail=None, - error_content=None): + self, expected_exception, status_code, expected_msg, + error_type=None, error_msg=None, error_detail=None, + error_content=None): if error_content is None: error_content = {'NeutronError': {'type': error_type, 'message': error_msg, diff --git a/tox.ini b/tox.ini index b6c33c6b5..7f705d18a 100644 --- a/tox.ini +++ b/tox.ini @@ -37,10 +37,8 @@ commands= downloadcache = ~/cache/pip [flake8] -# E125 continuation line does not distinguish itself from next logical line -# -# TODO Fix the following rules from hacking 0.9.x # H405 multi line docstring summary not separated with an empty line -ignore = E125,H405 +# (mutli line docstring is used in unit tests frequently) +ignore = H405 show-source = true exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,tools From 7bc65cb9a5c18bfd2a9fed1fed60aacb113a9c22 Mon Sep 17 00:00:00 2001 From: preeti-mirji Date: Wed, 15 Apr 2015 03:03:57 -0700 Subject: [PATCH 200/845] Fix invalid error message in neutron-cli Error message displayed is not correct when we provide invalid syntax for optional arguments in neutron CLI Closes-bug: 1438518 Change-Id: If5f875f657c977868c934a21d663e6d1a0a5158e --- neutronclient/neutron/v2_0/__init__.py | 4 ++++ neutronclient/tests/unit/test_casual_args.py | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index 2fca3b30a..36386c443 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -288,6 +288,10 @@ def parse_args_to_dict(values_specs): _("Invalid values_specs %s") % ' '.join(values_specs)) _value_number += 1 + if _item.startswith('---'): + raise exceptions.CommandError( + _("Invalid values_specs %s") % ' '.join(values_specs)) + _values_specs.append(_item) # Deal with last one argument diff --git a/neutronclient/tests/unit/test_casual_args.py b/neutronclient/tests/unit/test_casual_args.py index f1cf201b5..dd87a5399 100644 --- a/neutronclient/tests/unit/test_casual_args.py +++ b/neutronclient/tests/unit/test_casual_args.py @@ -78,6 +78,11 @@ def test_arg(self): self.assertEqual('value1', neutronV20.parse_args_to_dict(_specs)['arg1']) + def test_arg_invalid_syntax(self): + _specs = ['--tag=t', '---arg1', 'value1'] + self.assertRaises(exceptions.CommandError, + neutronV20.parse_args_to_dict, _specs) + def test_dict_arg(self): _specs = ['--tag=t', '--arg1', 'type=dict', 'key1=value1,key2=value2'] arg1 = neutronV20.parse_args_to_dict(_specs)['arg1'] From d75689a23812b7f75e9754fe25ab70e8c5c5c461 Mon Sep 17 00:00:00 2001 From: Russell Bryant Date: Wed, 15 Apr 2015 16:41:57 -0400 Subject: [PATCH 201/845] Add --binding-profile to port-create Add a new option that allows you to specify a custom 'binding:profile' when doing a port-create. Change-Id: I66550c3463fbd088de85eaec731a92e48096a9e2 --- neutronclient/neutron/v2_0/port.py | 9 +++++++++ neutronclient/tests/unit/test_cli20_port.py | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index 3a7ec7ec4..6eacebcf7 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -222,6 +222,12 @@ def add_known_arguments(self, parser): '--vnic_type', choices=['direct', 'macvtap', 'normal'], help=argparse.SUPPRESS) + parser.add_argument( + '--binding-profile', + help=_('Custom data to be passed as binding:profile.')) + parser.add_argument( + '--binding_profile', + help=argparse.SUPPRESS) self.add_arguments_secgroup(parser) self.add_arguments_extradhcpopt(parser) @@ -242,6 +248,9 @@ def args2body(self, parsed_args): body['port'].update({'tenant_id': parsed_args.tenant_id}) if parsed_args.vnic_type: body['port'].update({'binding:vnic_type': parsed_args.vnic_type}) + if parsed_args.binding_profile: + body['port'].update({'binding:profile': + jsonutils.loads(parsed_args.binding_profile)}) self.args2body_secgroup(parsed_args, body['port']) self.args2body_extradhcpopt(parsed_args, body['port']) diff --git a/neutronclient/tests/unit/test_cli20_port.py b/neutronclient/tests/unit/test_cli20_port.py index 59d9804ee..76eb1057c 100644 --- a/neutronclient/tests/unit/test_cli20_port.py +++ b/neutronclient/tests/unit/test_cli20_port.py @@ -173,6 +173,25 @@ def test_create_port_vnic_type_macvtap(self): self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) + def test_create_port_with_binding_profile(self): + resource = 'port' + cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + netid = 'netid' + args = ['--binding_profile', '{"foo":"bar"}', netid] + position_names = ['binding:profile', 'network_id'] + position_values = [{'foo': 'bar'}, netid] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + # Test dashed options + args = ['--binding-profile', '{"foo":"bar"}', netid] + position_names = ['binding:profile', 'network_id'] + position_values = [{'foo': 'bar'}, netid] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + def test_create_port_tenant(self): """Create port: --tenant_id tenantid netid.""" resource = 'port' From 2f0fc2cf093a3c089ec341e1fc8c7b89f22994ab Mon Sep 17 00:00:00 2001 From: Doug Hellmann Date: Thu, 16 Apr 2015 18:13:29 +0000 Subject: [PATCH 202/845] Uncap library requirements for liberty Change-Id: I175c87748ade0ea6be8c7c8b9572dbb706d49a11 Depends-On: Ib948b756b8e6ca47a4c9c44c48031e54b7386a06 --- requirements.txt | 8 ++++---- test-requirements.txt | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/requirements.txt b/requirements.txt index 12f89a07e..b7a9ee1f6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,12 +3,12 @@ # process, which may cause wedges in the gate later. pbr>=0.6,!=0.7,<1.0 argparse -cliff>=1.10.0,<1.11.0 # Apache-2.0 +cliff>=1.10.0 # Apache-2.0 iso8601>=0.1.9 netaddr>=0.7.12 -oslo.i18n>=1.5.0,<1.6.0 # Apache-2.0 -oslo.serialization>=1.4.0,<1.5.0 # Apache-2.0 -oslo.utils>=1.4.0,<1.5.0 # Apache-2.0 +oslo.i18n>=1.5.0 # Apache-2.0 +oslo.serialization>=1.4.0 # Apache-2.0 +oslo.utils>=1.4.0 # Apache-2.0 requests>=2.2.0,!=2.4.0 python-keystoneclient>=1.1.0 simplejson>=2.2.0 diff --git a/test-requirements.txt b/test-requirements.txt index b91035320..1c395de55 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -9,8 +9,8 @@ discover fixtures>=0.3.14 mox3>=0.7.0 mock>=1.0 -oslosphinx>=2.5.0,<2.6.0 # Apache-2.0 -oslotest>=1.5.1,<1.6.0 # Apache-2.0 +oslosphinx>=2.5.0 # Apache-2.0 +oslotest>=1.5.1 # Apache-2.0 python-subunit>=0.0.18 requests-mock>=0.6.0 # Apache-2.0 sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3 From d830767711531a78800610483fed49c38a16d20d Mon Sep 17 00:00:00 2001 From: Doug Hellmann Date: Tue, 21 Apr 2015 15:38:25 +0000 Subject: [PATCH 203/845] Update README to work with release tools The README file needs to have links to the project documentation and bug tracker in a parsable format in order for some of the release tools scripts to work (particularly the one that prints the release note email). Change-Id: I37e0acc5ed8e1af565359290fa622456901c735e --- README.rst | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index ea8e44c88..0fc449a4b 100644 --- a/README.rst +++ b/README.rst @@ -1 +1,14 @@ -This is the client API library for Neutron. +Python bindings to the Neutron API +================================== + +This is a client library for Neutron built on the Neutron API. It +provides a Python API (the ``neutronclient`` module) and a command-line tool +(``neutron``). + +Development takes place via the usual OpenStack processes as outlined in the +`developer guide `_. + +* License: Apache License, Version 2.0 +* Documentation: http://docs.openstack.org/developer/python-neutronclient +* Source: http://git.openstack.org/cgit/openstack/python-neutronclient +* Bugs: http://bugs.launchpad.net/python-neutronclient From 5eb292c30c26c255f97f7d9a5a25f040c324c9e1 Mon Sep 17 00:00:00 2001 From: zengfagao Date: Sat, 11 Apr 2015 07:15:14 -0700 Subject: [PATCH 204/845] Fix Python client library for Neutron To create a subnet, we used to require positional cidr value. With subnetpool, cidr is now optional value. With optional positional cidr, the cidr value cannot be parsed into known_args if it is separated from network value. To fix the problem, we need to find cidr value from value_specs, and use it if we find it. Cidr is optional, may not exist. Change-Id: Ic4bccf4e7384cc891cef71fb7ce3b52fbf997345 Closes-Bug:#1442771 --- neutronclient/common/utils.py | 9 +++++++++ neutronclient/shell.py | 15 +++++++++++++++ neutronclient/tests/unit/test_cli20_subnet.py | 18 +++++++++++++++++- neutronclient/tests/unit/test_utils.py | 5 +++++ 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/neutronclient/common/utils.py b/neutronclient/common/utils.py index a847b0f3b..2ddd5a82a 100644 --- a/neutronclient/common/utils.py +++ b/neutronclient/common/utils.py @@ -19,6 +19,7 @@ import argparse import logging +import netaddr import os from oslo.utils import encodeutils @@ -171,3 +172,11 @@ def add_boolean_argument(parser, name, **kwargs): choices=['True', 'true', 'False', 'false'], default=default, **kwargs) + + +def is_valid_cidr(cidr): + try: + netaddr.IPNetwork(cidr) + return True + except Exception: + return False diff --git a/neutronclient/shell.py b/neutronclient/shell.py index bf77b4a8c..bea76cbde 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -97,10 +97,25 @@ def run_command(cmd, cmd_parser, sub_argv): _argv = sub_argv[:index] values_specs = sub_argv[index:] known_args, _values_specs = cmd_parser.parse_known_args(_argv) + if(isinstance(cmd, subnet.CreateSubnet) and not known_args.cidr): + cidr = get_first_valid_cidr(_values_specs) + if cidr: + known_args.cidr = cidr + _values_specs.remove(cidr) cmd.values_specs = (index == -1 and _values_specs or values_specs) return cmd.run(known_args) +def get_first_valid_cidr(value_specs): + # Bug 1442771, argparse does not allow optional positional parameter + # to be separated from previous positional parameter. + # When cidr was separated from network, the value will not be able + # to be parsed into known_args, but saved to _values_specs instead. + for value in value_specs: + if utils.is_valid_cidr(value): + return value + + def env(*_vars, **kwargs): """Search for the first defined of possibly many env vars. diff --git a/neutronclient/tests/unit/test_cli20_subnet.py b/neutronclient/tests/unit/test_cli20_subnet.py index 65cace34b..e6a7ea361 100644 --- a/neutronclient/tests/unit/test_cli20_subnet.py +++ b/neutronclient/tests/unit/test_cli20_subnet.py @@ -34,7 +34,7 @@ def test_create_subnet(self): name = 'myname' myid = 'myid' netid = 'netid' - cidr = 'cidrvalue' + cidr = '10.10.10.0/24' gateway = 'gatewayvalue' args = ['--gateway', gateway, netid, cidr] position_names = ['ip_version', 'network_id', 'cidr', 'gateway_ip'] @@ -42,6 +42,22 @@ def test_create_subnet(self): self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) + def test_create_subnet_network_cidr_seperated(self): + # For positional value, network_id and cidr can be separated. + """Create subnet: --gateway gateway netid cidr.""" + resource = 'subnet' + cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + netid = 'netid' + cidr = '10.10.10.0/24' + gateway = 'gatewayvalue' + args = [netid, '--gateway', gateway, cidr] + position_names = ['ip_version', 'network_id', 'cidr', 'gateway_ip'] + position_values = [4, netid, cidr, gateway] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + def test_create_subnet_with_no_gateway(self): """Create subnet: --no-gateway netid cidr.""" resource = 'subnet' diff --git a/neutronclient/tests/unit/test_utils.py b/neutronclient/tests/unit/test_utils.py index a0044d61a..a8d3a6ead 100644 --- a/neutronclient/tests/unit/test_utils.py +++ b/neutronclient/tests/unit/test_utils.py @@ -102,6 +102,11 @@ def __call__(self, *args, **kwargs): act = utils.get_item_properties(item, fields, formatters=formatters) self.assertEqual(('test_name', 'test_id', 'test', 'pass'), act) + def test_is_cidr(self): + self.assertTrue(utils.is_valid_cidr('10.10.10.0/24')) + self.assertFalse(utils.is_valid_cidr('10.10.10..0/24')) + self.assertFalse(utils.is_valid_cidr('wrong_cidr_format')) + class ImportClassTestCase(testtools.TestCase): def test_get_client_class_invalid_version(self): From 6190a721030094d8eb9016fb85cb56e7d451b157 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Fri, 10 Apr 2015 15:41:44 -0400 Subject: [PATCH 205/845] Add functional test for subnet create This test adds a functional test to verify a subnet-create command with the arg order used in the docs. python-neutronclient introduced a regression which broke the usage of this order. This test will prevent this from happening in the future. Change-Id: If7e4211a4cbf33bc87a1304553ad3dc9c89346c4 Related-Bug: #1442771 --- .../tests/functional/test_subnet_create.py | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 neutronclient/tests/functional/test_subnet_create.py diff --git a/neutronclient/tests/functional/test_subnet_create.py b/neutronclient/tests/functional/test_subnet_create.py new file mode 100644 index 000000000..ed7f390b8 --- /dev/null +++ b/neutronclient/tests/functional/test_subnet_create.py @@ -0,0 +1,36 @@ +# Copyright 2015 Hewlett-Packard Development Company, L.P +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from neutronclient.tests.functional import base + + +class SubnetCreateNeutronClientCLITest(base.ClientTestBase): + + def test_create_subnet_net_name_first(self): + self.neutron('net-create', params='netwrk-1') + self.addCleanup(self.neutron, 'net-delete netwrk-1') + self.neutron('subnet-create netwrk-1', + params='--name fake --gateway 192.168.51.1 ' + '192.168.51.0/24') + self.addCleanup(self.neutron, 'subnet-delete fake') + subnet_list = self.parser.listing(self.neutron('subnet-list')) + self.assertTableStruct(subnet_list, ['id', 'name', 'cidr', + 'allocation_pools']) + found = False + for row in subnet_list: + if row.get('name') == 'fake': + found = True + break + if not found: + self.fail('Created subnet not found in list') From 06a6e4d5ae5c1a6b9952248f648ce5f88ae8dcb7 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Mon, 4 May 2015 20:09:08 +0000 Subject: [PATCH 206/845] Updated from global requirements Change-Id: I60902c0d8602e7e0080eafcea2c3a729c7ab79ef --- requirements.txt | 4 ++-- test-requirements.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index b7a9ee1f6..416348910 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,8 +9,8 @@ netaddr>=0.7.12 oslo.i18n>=1.5.0 # Apache-2.0 oslo.serialization>=1.4.0 # Apache-2.0 oslo.utils>=1.4.0 # Apache-2.0 -requests>=2.2.0,!=2.4.0 -python-keystoneclient>=1.1.0 +requests>=2.5.2 +python-keystoneclient>=1.3.0 simplejson>=2.2.0 six>=1.9.0 Babel>=1.3 diff --git a/test-requirements.txt b/test-requirements.txt index 1c395de55..24c84a17e 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -16,4 +16,4 @@ requests-mock>=0.6.0 # Apache-2.0 sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3 testrepository>=0.0.18 testtools>=0.9.36,!=1.2.0 -tempest-lib>=0.4.0 +tempest-lib>=0.5.0 From d9113f17ac3bdfac1cbf9214c37775d5d91e0205 Mon Sep 17 00:00:00 2001 From: Doug Hellmann Date: Wed, 6 May 2015 19:39:35 +0000 Subject: [PATCH 207/845] Drop use of 'oslo' namespace package The Oslo libraries have moved all of their code out of the 'oslo' namespace package into per-library packages. The namespace package was retained during kilo for backwards compatibility, but will be removed by the liberty-2 milestone. This change removes the use of the namespace package, replacing it with the new package names. The patches in the libraries will be put on hold until application patches have landed, or L2, whichever comes first. At that point, new versions of the libraries without namespace packages will be released as a major version update. Please merge this patch, or an equivalent, before L2 to avoid problems with those library releases. Blueprint: remove-namespace-packages https://blueprints.launchpad.net/oslo-incubator/+spec/remove-namespace-packages Change-Id: I474dcd9de166b00d02b3586858a28797e97a3231 --- neutronclient/common/serializer.py | 2 +- neutronclient/common/utils.py | 4 ++-- neutronclient/i18n.py | 2 +- neutronclient/neutron/v2_0/__init__.py | 2 +- neutronclient/neutron/v2_0/port.py | 2 +- neutronclient/neutron/v2_0/quota.py | 2 +- neutronclient/neutron/v2_0/router.py | 2 +- neutronclient/neutron/v2_0/subnet.py | 2 +- neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py | 2 +- neutronclient/shell.py | 2 +- neutronclient/tests/unit/test_auth.py | 2 +- neutronclient/tests/unit/test_cli20.py | 2 +- neutronclient/tests/unit/test_cli20_agents.py | 2 +- neutronclient/tests/unit/test_cli20_network.py | 2 +- 14 files changed, 15 insertions(+), 15 deletions(-) diff --git a/neutronclient/common/serializer.py b/neutronclient/common/serializer.py index 3293b88a4..f21e87c7e 100644 --- a/neutronclient/common/serializer.py +++ b/neutronclient/common/serializer.py @@ -17,7 +17,7 @@ from xml.etree import ElementTree as etree from xml.parsers import expat -from oslo.serialization import jsonutils +from oslo_serialization import jsonutils import six from neutronclient.common import constants diff --git a/neutronclient/common/utils.py b/neutronclient/common/utils.py index 2ddd5a82a..e0d5f546a 100644 --- a/neutronclient/common/utils.py +++ b/neutronclient/common/utils.py @@ -22,8 +22,8 @@ import netaddr import os -from oslo.utils import encodeutils -from oslo.utils import importutils +from oslo_utils import encodeutils +from oslo_utils import importutils import six from neutronclient.common import exceptions diff --git a/neutronclient/i18n.py b/neutronclient/i18n.py index f8084067f..e13a880aa 100644 --- a/neutronclient/i18n.py +++ b/neutronclient/i18n.py @@ -10,7 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. -from oslo import i18n +import oslo_i18n as i18n _translators = i18n.TranslatorFactory(domain='neutronclient') diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index 36386c443..c6d4e6fe2 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -24,7 +24,7 @@ from cliff.formatters import table from cliff import lister from cliff import show -from oslo.serialization import jsonutils +from oslo_serialization import jsonutils import six from neutronclient.common import command diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index 6eacebcf7..a983317dc 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -16,7 +16,7 @@ import argparse -from oslo.serialization import jsonutils +from oslo_serialization import jsonutils from neutronclient.common import exceptions from neutronclient.common import utils diff --git a/neutronclient/neutron/v2_0/quota.py b/neutronclient/neutron/v2_0/quota.py index 780f510b1..03ab64de6 100644 --- a/neutronclient/neutron/v2_0/quota.py +++ b/neutronclient/neutron/v2_0/quota.py @@ -20,7 +20,7 @@ from cliff import lister from cliff import show -from oslo.serialization import jsonutils +from oslo_serialization import jsonutils import six from neutronclient.common import exceptions diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index 611259603..844a960a0 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -18,7 +18,7 @@ import argparse -from oslo.serialization import jsonutils +from oslo_serialization import jsonutils from neutronclient.common import exceptions from neutronclient.common import utils diff --git a/neutronclient/neutron/v2_0/subnet.py b/neutronclient/neutron/v2_0/subnet.py index d969907f0..9959355d7 100644 --- a/neutronclient/neutron/v2_0/subnet.py +++ b/neutronclient/neutron/v2_0/subnet.py @@ -16,7 +16,7 @@ import argparse -from oslo.serialization import jsonutils +from oslo_serialization import jsonutils from neutronclient.common import exceptions from neutronclient.common import utils diff --git a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py index 6bf937b94..0d6914cd4 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py +++ b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py @@ -14,7 +14,7 @@ # under the License. # -from oslo.serialization import jsonutils +from oslo_serialization import jsonutils from neutronclient.common import exceptions from neutronclient.common import utils diff --git a/neutronclient/shell.py b/neutronclient/shell.py index bea76cbde..4953c1f85 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -33,7 +33,7 @@ from keystoneclient import discover from keystoneclient.openstack.common.apiclient import exceptions as ks_exc from keystoneclient import session -from oslo.utils import encodeutils +from oslo_utils import encodeutils import six.moves.urllib.parse as urlparse from cliff import app diff --git a/neutronclient/tests/unit/test_auth.py b/neutronclient/tests/unit/test_auth.py index 2cae5115d..285839d4c 100644 --- a/neutronclient/tests/unit/test_auth.py +++ b/neutronclient/tests/unit/test_auth.py @@ -19,7 +19,7 @@ import uuid import fixtures -from oslo.serialization import jsonutils +from oslo_serialization import jsonutils from requests_mock.contrib import fixture as mock_fixture import testtools diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 1e9fee6ef..caea74a3e 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -20,7 +20,7 @@ import fixtures from mox3 import mox -from oslo.utils import encodeutils +from oslo_utils import encodeutils from oslotest import base import requests import six diff --git a/neutronclient/tests/unit/test_cli20_agents.py b/neutronclient/tests/unit/test_cli20_agents.py index f74427467..bc545c6e1 100644 --- a/neutronclient/tests/unit/test_cli20_agents.py +++ b/neutronclient/tests/unit/test_cli20_agents.py @@ -14,7 +14,7 @@ import sys -from oslo.serialization import jsonutils +from oslo_serialization import jsonutils from neutronclient.neutron.v2_0 import agent from neutronclient.tests.unit import test_cli20 diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index 8605cf77d..e0681d250 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -17,7 +17,7 @@ import sys from mox3 import mox -from oslo.serialization import jsonutils +from oslo_serialization import jsonutils from neutronclient.common import exceptions from neutronclient.neutron.v2_0 import network From df93b2716774ec1d044e65bbbd037fdf56313bc7 Mon Sep 17 00:00:00 2001 From: Qin Zhao Date: Fri, 6 Mar 2015 17:20:06 +0800 Subject: [PATCH 208/845] Add InvalidIpForSubnetClient exception A new Neutron exception type, InvalidIpForSubnetClient, is added by https://review.openstack.org/#/c/121767/ . Need to let Neutron client to identify this new exception type. Change-Id: Ib4f0d7af3807f96463c82d1d2a1e53b56081facc Partial-Bug: 1369871 --- neutronclient/common/exceptions.py | 4 ++++ neutronclient/tests/unit/test_cli20.py | 1 + 2 files changed, 5 insertions(+) diff --git a/neutronclient/common/exceptions.py b/neutronclient/common/exceptions.py index 9728fbdd7..da5e19520 100644 --- a/neutronclient/common/exceptions.py +++ b/neutronclient/common/exceptions.py @@ -144,6 +144,10 @@ class InvalidIpForNetworkClient(BadRequest): pass +class InvalidIpForSubnetClient(BadRequest): + pass + + class OverQuotaClient(Conflict): pass diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 1e9fee6ef..7a04ec4a1 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -683,6 +683,7 @@ def test_exception_handler_v20_neutron_known_error(self): exceptions.ExternalIpAddressExhaustedClient, 400), ('OverQuota', exceptions.OverQuotaClient, 409), ('InvalidIpForNetwork', exceptions.InvalidIpForNetworkClient, 400), + ('InvalidIpForSubnet', exceptions.InvalidIpForSubnetClient, 400), ] error_msg = 'dummy exception message' From d5360202e16b635eac907fe1f30bf387b39e238d Mon Sep 17 00:00:00 2001 From: LIU Yulong Date: Mon, 11 May 2015 16:12:41 +0800 Subject: [PATCH 209/845] Add missing tenant_id to lbaas-v2 resources creation Now tenant_id is not specific when creating lbaas v2 loadbalancer, listener, pool, member. This patch add missing tenant_id to lbaas-v2 resources creation. So admin user can create resource for specific project. Change-Id: If4db4d8660354bc1f4d5259ebed73d78b4cfcd97 Closes-Bug: #1453706 --- neutronclient/neutron/v2_0/lb/v2/listener.py | 3 ++- neutronclient/neutron/v2_0/lb/v2/loadbalancer.py | 2 +- neutronclient/neutron/v2_0/lb/v2/member.py | 2 +- neutronclient/neutron/v2_0/lb/v2/pool.py | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/neutronclient/neutron/v2_0/lb/v2/listener.py b/neutronclient/neutron/v2_0/lb/v2/listener.py index e0e9fcb27..4adea3938 100644 --- a/neutronclient/neutron/v2_0/lb/v2/listener.py +++ b/neutronclient/neutron/v2_0/lb/v2/listener.py @@ -106,7 +106,8 @@ def args2body(self, parsed_args): ['connection-limit', 'description', 'loadbalancer_id', 'name', 'default_tls_container_id', - 'sni_container_ids']) + 'sni_container_ids', + 'tenant_id']) return body diff --git a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py index 1bada17da..8e381107e 100644 --- a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py +++ b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py @@ -73,7 +73,7 @@ def args2body(self, parsed_args): } neutronV20.update_dict(parsed_args, body[self.resource], ['description', 'provider', 'vip_address', - 'name']) + 'tenant_id', 'name']) return body diff --git a/neutronclient/neutron/v2_0/lb/v2/member.py b/neutronclient/neutron/v2_0/lb/v2/member.py index 3fd71c6c2..05eae44e1 100644 --- a/neutronclient/neutron/v2_0/lb/v2/member.py +++ b/neutronclient/neutron/v2_0/lb/v2/member.py @@ -101,7 +101,7 @@ def args2body(self, parsed_args): }, } neutronV20.update_dict(parsed_args, body[self.resource], - ['weight', 'subnet_id']) + ['weight', 'subnet_id', 'tenant_id']) return body diff --git a/neutronclient/neutron/v2_0/lb/v2/pool.py b/neutronclient/neutron/v2_0/lb/v2/pool.py index fd5e03a5c..f8280c7cf 100644 --- a/neutronclient/neutron/v2_0/lb/v2/pool.py +++ b/neutronclient/neutron/v2_0/lb/v2/pool.py @@ -100,7 +100,7 @@ def args2body(self, parsed_args): } neutronV20.update_dict(parsed_args, body[self.resource], ['description', 'name', - 'session_persistence']) + 'session_persistence', 'tenant_id']) return body From 9e6977de589f34ff226df042f2b631a6cd84efe1 Mon Sep 17 00:00:00 2001 From: Cedric Brandily Date: Sat, 9 May 2015 22:49:50 +0200 Subject: [PATCH 210/845] Allow setting router's external ip(s) This change allows to set external_fixed_ips in router-gateway-set command using --fixed-ip option: neutron router-gateway-set $router $extnet\ --fixed-ip ip_address=10.0.0.4 neutron router-gateway-set $router $extnet\ --fixed-ip subnet_id=extsubnet Change-Id: Ifa37aee0bbf8e2ee1eb37020924f13af73ad35cc Related: blueprint specify-router-ext-ip --- neutronclient/neutron/v2_0/router.py | 16 ++++++++++++ neutronclient/tests/unit/test_cli20_router.py | 26 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index 611259603..f60d6f5a8 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -184,6 +184,11 @@ def get_parser(self, prog_name): parser.add_argument( '--disable-snat', action='store_true', help=_('Disable source NAT on the router gateway.')) + parser.add_argument( + '--fixed-ip', action='append', + help=_('Desired IP and/or subnet on external network: ' + 'subnet_id=,ip_address=. ' + 'You can repeat this option.')) return parser def run(self, parsed_args): @@ -197,6 +202,17 @@ def run(self, parsed_args): router_dict = {'network_id': _ext_net_id} if parsed_args.disable_snat: router_dict['enable_snat'] = False + if parsed_args.fixed_ip: + ips = [] + for ip_spec in parsed_args.fixed_ip: + ip_dict = utils.str2dict(ip_spec) + subnet_name_id = ip_dict.get('subnet_id') + if subnet_name_id: + subnet_id = neutronV20.find_resourceid_by_name_or_id( + neutron_client, 'subnet', subnet_name_id) + ip_dict['subnet_id'] = subnet_id + ips.append(ip_dict) + router_dict['external_fixed_ips'] = ips neutron_client.add_gateway_router(_router_id, router_dict) print(_('Set gateway for router %s') % parsed_args.router, file=self.app.stdout) diff --git a/neutronclient/tests/unit/test_cli20_router.py b/neutronclient/tests/unit/test_cli20_router.py index 87e878aa6..5da317386 100644 --- a/neutronclient/tests/unit/test_cli20_router.py +++ b/neutronclient/tests/unit/test_cli20_router.py @@ -244,6 +244,32 @@ def test_set_gateway_disable_snat(self): "enable_snat": False}} ) + def test_set_gateway_external_ip(self): + """set external gateway for router: myid externalid --fixed-ip ...""" + resource = 'router' + cmd = router.SetGatewayRouter(test_cli20.MyApp(sys.stdout), None) + args = ['myid', 'externalid', '--fixed-ip', 'ip_address=10.0.0.2'] + self._test_update_resource(resource, cmd, 'myid', + args, + {"external_gateway_info": + {"network_id": "externalid", + "external_fixed_ips": [ + {"ip_address": "10.0.0.2"}]}} + ) + + def test_set_gateway_external_subnet(self): + """set external gateway for router: myid externalid --fixed-ip ...""" + resource = 'router' + cmd = router.SetGatewayRouter(test_cli20.MyApp(sys.stdout), None) + args = ['myid', 'externalid', '--fixed-ip', 'subnet_id=mysubnet'] + self._test_update_resource(resource, cmd, 'myid', + args, + {"external_gateway_info": + {"network_id": "externalid", + "external_fixed_ips": [ + {"subnet_id": "mysubnet"}]}} + ) + def test_remove_gateway(self): """Remove external gateway from router: externalid.""" resource = 'router' From f62492bb7f6f5eb44c65280717a13476e0cb4203 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Tue, 12 May 2015 14:35:35 +0000 Subject: [PATCH 211/845] Updated from global requirements Change-Id: I5a1d53d39fde0359dcea37deb1fc989f9f1f1d1a --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 416348910..28e96525e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ # The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. -pbr>=0.6,!=0.7,<1.0 +pbr>=0.11,<2.0 argparse cliff>=1.10.0 # Apache-2.0 iso8601>=0.1.9 From 7eb3241650ad87d2f6ca5aa45b3a2415eafe7207 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20Verg=C3=A9?= Date: Sat, 16 May 2015 17:58:27 +0200 Subject: [PATCH 212/845] Fix functional tests and tox 2.0 errors With the recent update to tox 2.0.x, environment variables such as OS_AUTH_URL are not passed by default, resulting in tests errors mentionning Keystone authentication failures. This patch reads credentials from the 'functional_creds.conf' config file, like it is done in novaclient, glanceclient, manilaclient and soon in cinderclient. Reading credentials the old way (the environment) is still possible. Change-Id: Ief0f050044ecd90a14bbaf044e2b93ade0a6173f Closes-Bug: #1455102 --- functional_creds.conf.sample | 8 ++++ neutronclient/tests/functional/base.py | 48 ++++++++++++++++--- .../tests/functional/hooks/post_test_hook.sh | 17 ++++++- .../tests/functional/test_clientlib.py | 21 ++++---- 4 files changed, 76 insertions(+), 18 deletions(-) create mode 100644 functional_creds.conf.sample diff --git a/functional_creds.conf.sample b/functional_creds.conf.sample new file mode 100644 index 000000000..081a73681 --- /dev/null +++ b/functional_creds.conf.sample @@ -0,0 +1,8 @@ +# Credentials for functional testing +[auth] +uri = http://10.42.0.50:5000/v2.0 + +[admin] +user = admin +tenant = admin +pass = secrete diff --git a/neutronclient/tests/functional/base.py b/neutronclient/tests/functional/base.py index 48561d6c7..d70fc77b0 100644 --- a/neutronclient/tests/functional/base.py +++ b/neutronclient/tests/functional/base.py @@ -12,9 +12,44 @@ import os +from six.moves import configparser from tempest_lib.cli import base +_CREDS_FILE = 'functional_creds.conf' + + +def credentials(): + """Retrieves credentials to run functional tests + + Credentials are either read from the environment or from a config file + ('functional_creds.conf'). Environment variables override those from the + config file. + + The 'functional_creds.conf' file is the clean and new way to use (by + default tox 2.0 does not pass environment variables). + """ + + username = os.environ.get('OS_USERNAME') + password = os.environ.get('OS_PASSWORD') + tenant_name = os.environ.get('OS_TENANT_NAME') + auth_url = os.environ.get('OS_AUTH_URL') + + config = configparser.RawConfigParser() + if config.read(_CREDS_FILE): + username = username or config.get('admin', 'user') + password = password or config.get('admin', 'pass') + tenant_name = tenant_name or config.get('admin', 'tenant') + auth_url = auth_url or config.get('auth', 'uri') + + return { + 'username': username, + 'password': password, + 'tenant_name': tenant_name, + 'auth_url': auth_url + } + + class ClientTestBase(base.ClientTestBase): """This is a first pass at a simple read only python-neutronclient test. This only exercises client commands that are read only. @@ -28,16 +63,15 @@ class ClientTestBase(base.ClientTestBase): """ def _get_clients(self): + creds = credentials() cli_dir = os.environ.get( 'OS_NEUTRONCLIENT_EXEC_DIR', os.path.join(os.path.abspath('.'), '.tox/functional/bin')) - - return base.CLIClient( - username=os.environ.get('OS_USERNAME'), - password=os.environ.get('OS_PASSWORD'), - tenant_name=os.environ.get('OS_TENANT_NAME'), - uri=os.environ.get('OS_AUTH_URL'), - cli_dir=cli_dir) + return base.CLIClient(username=creds['username'], + password=creds['password'], + tenant_name=creds['tenant_name'], + uri=creds['auth_url'], + cli_dir=cli_dir) def neutron(self, *args, **kwargs): return self.clients.neutron(*args, diff --git a/neutronclient/tests/functional/hooks/post_test_hook.sh b/neutronclient/tests/functional/hooks/post_test_hook.sh index e0c966939..68f906a24 100755 --- a/neutronclient/tests/functional/hooks/post_test_hook.sh +++ b/neutronclient/tests/functional/hooks/post_test_hook.sh @@ -28,15 +28,28 @@ function generate_testr_results { export NEUTRONCLIENT_DIR="$BASE/new/python-neutronclient" +sudo chown -R jenkins:stack $NEUTRONCLIENT_DIR + # Get admin credentials cd $BASE/new/devstack source openrc admin admin +# Store these credentials into the config file +CREDS_FILE=$NEUTRONCLIENT_DIR/functional_creds.conf +cat < $CREDS_FILE +# Credentials for functional testing +[auth] +uri = $OS_AUTH_URL + +[admin] +user = $OS_USERNAME +tenant = $OS_TENANT_NAME +pass = $OS_PASSWORD +EOF + # Go to the neutronclient dir cd $NEUTRONCLIENT_DIR -sudo chown -R jenkins:stack $NEUTRONCLIENT_DIR - # Run tests echo "Running neutronclient functional test suite" set +e diff --git a/neutronclient/tests/functional/test_clientlib.py b/neutronclient/tests/functional/test_clientlib.py index 0ad84dbbe..a18fbbd99 100644 --- a/neutronclient/tests/functional/test_clientlib.py +++ b/neutronclient/tests/functional/test_clientlib.py @@ -10,7 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. -import os import uuid from keystoneclient.auth.identity import v2 as v2_auth @@ -20,6 +19,7 @@ import testtools from neutronclient.common import exceptions +from neutronclient.tests.functional import base as func_base from neutronclient.v2_0 import client as v2_client # This module tests client library functionalities with @@ -38,26 +38,29 @@ def setUp(self): class Libv2HTTPClientTestBase(LibraryTestBase): def _get_client(self): - return v2_client.Client(username=os.environ.get('OS_USERNAME'), - password=os.environ.get('OS_PASSWORD'), - tenant_name=os.environ.get('OS_TENANT_NAME'), - auth_url=os.environ.get('OS_AUTH_URL')) + creds = func_base.credentials() + return v2_client.Client(username=creds['username'], + password=creds['password'], + tenant_name=creds['tenant_name'], + auth_url=creds['auth_url']) class Libv2SessionClientTestBase(LibraryTestBase): def _get_client(self): + creds = func_base.credentials() + session_params = {} ks_session = session.Session.construct(session_params) ks_discover = discover.Discover(session=ks_session, - auth_url=os.environ.get('OS_AUTH_URL')) + auth_url=creds['auth_url']) # At the moment, we use keystone v2 API v2_auth_url = ks_discover.url_for('2.0') ks_session.auth = v2_auth.Password( v2_auth_url, - username=os.environ.get('OS_USERNAME'), - password=os.environ.get('OS_PASSWORD'), - tenant_name=os.environ.get('OS_TENANT_NAME')) + username=creds['username'], + password=creds['password'], + tenant_name=creds['tenant_name']) return v2_client.Client(session=ks_session) From e3f61c97be1ebaf3b93b6bbd65259b73916be0f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20Verg=C3=A9?= Date: Fri, 15 May 2015 17:03:05 +0200 Subject: [PATCH 213/845] LBaaS v2: Fix listing pool members The command `neutron lbaas-member-list POOLID` should list members that belong to a given pool. Instead, it lists all members for all pools. This patch corrects this behavior. Change-Id: Ida80e17831865924a0e114417371faa437cce2ba Closes-Bug: #1454986 --- neutronclient/neutron/v2_0/lb/v2/member.py | 7 ++++++- neutronclient/tests/unit/lb/v2/test_cli20_member.py | 12 ++++++++---- neutronclient/tests/unit/test_cli20.py | 7 +++---- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/neutronclient/neutron/v2_0/lb/v2/member.py b/neutronclient/neutron/v2_0/lb/v2/member.py index 3fd71c6c2..3dd3e87b1 100644 --- a/neutronclient/neutron/v2_0/lb/v2/member.py +++ b/neutronclient/neutron/v2_0/lb/v2/member.py @@ -38,7 +38,7 @@ def add_known_arguments(self, parser): class ListMember(LbaasMemberMixin, neutronV20.ListCommand): - """LBaaS v2 List members that belong to a given tenant.""" + """LBaaS v2 List members that belong to a given pool.""" resource = 'member' shadow_resource = 'lbaas_member' @@ -49,6 +49,11 @@ class ListMember(LbaasMemberMixin, neutronV20.ListCommand): pagination_support = True sorting_support = True + def get_data(self, parsed_args): + self.parent_id = _get_pool_id(self.get_client(), parsed_args.pool) + self.values_specs.append('--pool_id=%s' % self.parent_id) + return super(ListMember, self).get_data(parsed_args) + class ShowMember(LbaasMemberMixin, neutronV20.ShowCommand): """LBaaS v2 Show information of a given member.""" diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_member.py b/neutronclient/tests/unit/lb/v2/test_cli20_member.py index 4058f47d2..73b329f6d 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_member.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_member.py @@ -72,7 +72,8 @@ def test_list_members(self): cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, True, base_args=[pool_id], cmd_resources=cmd_resources, - parent_id=pool_id) + parent_id=pool_id, + query="pool_id=%s" % pool_id) def test_list_members_pagination(self): """lbaas-member-list with pagination.""" @@ -83,7 +84,8 @@ def test_list_members_pagination(self): self._test_list_resources_with_pagination(resources, cmd, base_args=[pool_id], cmd_resources=cmd_resources, - parent_id=pool_id) + parent_id=pool_id, + query="pool_id=%s" % pool_id) def test_list_members_sort(self): """lbaas-member-list --sort-key id --sort-key asc.""" @@ -93,7 +95,8 @@ def test_list_members_sort(self): cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, True, base_args=[pool_id], cmd_resources=cmd_resources, - parent_id=pool_id) + parent_id=pool_id, + query="pool_id=%s" % pool_id) def test_list_members_limit(self): """lbaas-member-list -P.""" @@ -104,7 +107,8 @@ def test_list_members_limit(self): self._test_list_resources(resources, cmd, page_size=1000, base_args=[pool_id], cmd_resources=cmd_resources, - parent_id=pool_id) + parent_id=pool_id, + query="pool_id=%s" % pool_id) def test_show_member_id(self): """lbaas-member-show test_id.""" diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index caea74a3e..4e0553b99 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -304,7 +304,7 @@ def _test_list_resources(self, resources, cmd, detail=False, tags=(), fields_1=(), fields_2=(), page_size=None, sort_key=(), sort_dir=(), response_contents=None, base_args=None, path=None, cmd_resources=None, - parent_id=None, output_format=None): + parent_id=None, output_format=None, query=""): self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") cmd.get_client().MultipleTimes().AndReturn(self.client) @@ -319,7 +319,6 @@ def _test_list_resources(self, resources, cmd, detail=False, tags=(), self.client.format = self.format resstr = self.client.serialize(reses) # url method body - query = "" args = base_args if base_args is not None else [] if detail: args.append('-D') @@ -406,7 +405,7 @@ def _test_list_resources(self, resources, cmd, detail=False, tags=(), def _test_list_resources_with_pagination(self, resources, cmd, base_args=None, cmd_resources=None, - parent_id=None): + parent_id=None, query=""): self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") cmd.get_client().MultipleTimes().AndReturn(self.client) @@ -427,7 +426,7 @@ def _test_list_resources_with_pagination(self, resources, cmd, resstr1 = self.client.serialize(reses1) resstr2 = self.client.serialize(reses2) self.client.httpclient.request( - end_url(path, "", format=self.format), 'GET', + end_url(path, query, format=self.format), 'GET', body=None, headers=mox.ContainsKeyValue( 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr1)) From a2ae8ebdc30220212ba95680cfadeade848c1a7d Mon Sep 17 00:00:00 2001 From: Cedric Brandily Date: Sun, 31 May 2015 19:42:46 +0200 Subject: [PATCH 214/845] Raise user-friendly exceptions in str2dict Currently str2dict raises non user-friendly exceptions when input is not valid: neutron subnet-create mynetwork 10/23 --allocation-pools=10/24 # implies str2dict('10/24') # raises: dictionary update sequence element #0 has length 1; 2 is required This change updates str2dict implementation in order to raise user-friendly exceptions. Closes-Bug: #1460336 Change-Id: Id381865e6e5d9bebcb2c984c0338126566a97c2b --- neutronclient/common/utils.py | 12 +++++++++--- neutronclient/tests/unit/test_utils.py | 7 +++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/neutronclient/common/utils.py b/neutronclient/common/utils.py index e0d5f546a..788b024ae 100644 --- a/neutronclient/common/utils.py +++ b/neutronclient/common/utils.py @@ -105,9 +105,15 @@ def str2dict(strdict): :param strdict: key1=value1,key2=value2 """ - if not strdict: - return {} - return dict([kv.split('=', 1) for kv in strdict.split(',')]) + result = {} + if strdict: + for kv in strdict.split(','): + key, sep, value = kv.partition('=') + if not sep: + msg = _("invalid key-value '%s', expected format: key=value") + raise argparse.ArgumentTypeError(msg % kv) + result[key] = value + return result def http_log_req(_logger, args, kwargs): diff --git a/neutronclient/tests/unit/test_utils.py b/neutronclient/tests/unit/test_utils.py index a8d3a6ead..27ebc0f17 100644 --- a/neutronclient/tests/unit/test_utils.py +++ b/neutronclient/tests/unit/test_utils.py @@ -13,6 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. +import argparse + import testtools from neutronclient.common import exceptions @@ -42,6 +44,11 @@ def test_none_string_to_dictionary(self): expected = {} self.assertEqual(expected, utils.str2dict(input_str)) + def test_invalid_string_to_dictionary(self): + input_str = 'invalid' + self.assertRaises(argparse.ArgumentTypeError, + utils.str2dict, input_str) + def test_get_dict_item_properties(self): item = {'name': 'test_name', 'id': 'test_id'} fields = ('name', 'id') From a788a3e45230ebe182590be0fd6b4af71ec3f73c Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Mon, 8 Jun 2015 21:20:10 +0000 Subject: [PATCH 215/845] Updated from global requirements Change-Id: Iad5044af6bea48fdb9ce7e97ef9fc6e0e6e2f013 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 28e96525e..e4e748b2e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ oslo.i18n>=1.5.0 # Apache-2.0 oslo.serialization>=1.4.0 # Apache-2.0 oslo.utils>=1.4.0 # Apache-2.0 requests>=2.5.2 -python-keystoneclient>=1.3.0 +python-keystoneclient>=1.6.0 simplejson>=2.2.0 six>=1.9.0 Babel>=1.3 From 58a5ec66cfa224c5446ce83d627cb93b2327e356 Mon Sep 17 00:00:00 2001 From: "Lucas H. Xu" Date: Wed, 10 Jun 2015 12:42:31 -0400 Subject: [PATCH 216/845] Add alternative login description in neutronclient docs A way to test it is to follow the description and create a neutron client using the alternative way Make changes based on Akihiro comments Add extra colon in line 19 Change-Id: I2f7a5a3e8f7386d24d258303b3056615da8612ad --- doc/source/index.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/doc/source/index.rst b/doc/source/index.rst index 612a41ee1..9e032df51 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -15,6 +15,18 @@ In order to use the python neutron client directly, you must first obtain an aut >>> network_id = networks['networks'][0]['id'] >>> neutron.delete_network(network_id) +Alternatively, if you have a username and password, authentication is done +against the public endpoint. You must also specify a tenant that is associated +with the user:: + + >>> from neutronclient.v2_0 import client + >>> username='adminUser' + >>> password='secretword' + >>> tenant_name='openstackDemo' + >>> auth_url='http://192.168.206.130:5000/v2.0' + >>> neutron = client.Client(username=username, password=password, + ... tenant_name=tenant_name, auth_url=auth_url) + >>>nets = neutron.list_networks() Command-line Tool ================= From c809e068a042fd876fa5b5bf7074b8c28ede3967 Mon Sep 17 00:00:00 2001 From: Ming Yang Date: Fri, 29 May 2015 11:21:54 -0700 Subject: [PATCH 217/845] Allow bash completion script to work with BSD sed Shorthand \s isn't POSIX compliant; it works with GNU sed, but not BSD sed. Changed \s to the literal whitespace character to fix this issue. Change-Id: I936b372e4022a4717a64cea9a8928b6dc00c6711 Closes-Bug: #1459470 Closes-Bug: #1277492 --- tools/neutron.bash_completion | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/neutron.bash_completion b/tools/neutron.bash_completion index 3e542e0a9..aa8e4e4e3 100644 --- a/tools/neutron.bash_completion +++ b/tools/neutron.bash_completion @@ -10,9 +10,9 @@ _neutron() if [ "x$_neutron_opts" == "x" ] ; then nbc="`neutron bash-completion`" - _neutron_opts="`echo "$nbc" | sed -e "s/--[a-z0-9_-]*//g" -e "s/\s\s*/ /g"`" - _neutron_flags="`echo " $nbc" | sed -e "s/ [^-][^-][a-z0-9_-]*//g" -e "s/\s\s*/ /g"`" - _neutron_opts_exp="`echo "$_neutron_opts" | sed -e "s/\s/|/g"`" + _neutron_opts="`echo "$nbc" | sed -e "s/--[a-z0-9_-]*//g" -e "s/[ ][ ]*/ /g"`" + _neutron_flags="`echo " $nbc" | sed -e "s/ [^-][^-][a-z0-9_-]*//g" -e "s/[ ][ ]*/ /g"`" + _neutron_opts_exp="`echo "$_neutron_opts" | sed -e "s/[ ]/|/g"`" fi if [[ " ${COMP_WORDS[@]} " =~ " "($_neutron_opts_exp)" " && "$prev" != "help" ]] ; then From f13161b1accdb9f009d30fe86088854d03b411cd Mon Sep 17 00:00:00 2001 From: Ming Yang Date: Fri, 12 Jun 2015 12:34:38 -0700 Subject: [PATCH 218/845] Fixes indentation for bash completion script Change-Id: Ib8bda088da0af001c8d910ecfae5f9362abe8507 --- tools/neutron.bash_completion | 36 +++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/tools/neutron.bash_completion b/tools/neutron.bash_completion index aa8e4e4e3..15ed41776 100644 --- a/tools/neutron.bash_completion +++ b/tools/neutron.bash_completion @@ -3,25 +3,25 @@ _neutron_flags="" # lazy init _neutron_opts_exp="" # lazy init _neutron() { - local cur prev nbc cflags - COMPREPLY=() - cur="${COMP_WORDS[COMP_CWORD]}" - prev="${COMP_WORDS[COMP_CWORD-1]}" + local cur prev nbc cflags + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" - if [ "x$_neutron_opts" == "x" ] ; then - nbc="`neutron bash-completion`" - _neutron_opts="`echo "$nbc" | sed -e "s/--[a-z0-9_-]*//g" -e "s/[ ][ ]*/ /g"`" - _neutron_flags="`echo " $nbc" | sed -e "s/ [^-][^-][a-z0-9_-]*//g" -e "s/[ ][ ]*/ /g"`" - _neutron_opts_exp="`echo "$_neutron_opts" | sed -e "s/[ ]/|/g"`" - fi + if [ "x$_neutron_opts" == "x" ] ; then + nbc="`neutron bash-completion`" + _neutron_opts="`echo "$nbc" | sed -e "s/--[a-z0-9_-]*//g" -e "s/[ ][ ]*/ /g"`" + _neutron_flags="`echo " $nbc" | sed -e "s/ [^-][^-][a-z0-9_-]*//g" -e "s/[ ][ ]*/ /g"`" + _neutron_opts_exp="`echo "$_neutron_opts" | sed -e "s/[ ]/|/g"`" + fi - if [[ " ${COMP_WORDS[@]} " =~ " "($_neutron_opts_exp)" " && "$prev" != "help" ]] ; then - COMPLETION_CACHE=~/.neutronclient/*/*-cache - cflags="$_neutron_flags "$(cat $COMPLETION_CACHE 2> /dev/null | tr '\n' ' ') - COMPREPLY=($(compgen -W "${cflags}" -- ${cur})) - else - COMPREPLY=($(compgen -W "${_neutron_opts}" -- ${cur})) - fi - return 0 + if [[ " ${COMP_WORDS[@]} " =~ " "($_neutron_opts_exp)" " && "$prev" != "help" ]] ; then + COMPLETION_CACHE=~/.neutronclient/*/*-cache + cflags="$_neutron_flags "$(cat $COMPLETION_CACHE 2> /dev/null | tr '\n' ' ') + COMPREPLY=($(compgen -W "${cflags}" -- ${cur})) + else + COMPREPLY=($(compgen -W "${_neutron_opts}" -- ${cur})) + fi + return 0 } complete -F _neutron neutron From dcb7401a04c96aeb7c00a456b1ba0c77134a0a25 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Tue, 16 Jun 2015 19:23:09 +0000 Subject: [PATCH 219/845] Updated from global requirements Change-Id: I5a9286ae3f5daaba7d3445d27c64b00aecdccb23 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e4e748b2e..a7d5f8dda 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ iso8601>=0.1.9 netaddr>=0.7.12 oslo.i18n>=1.5.0 # Apache-2.0 oslo.serialization>=1.4.0 # Apache-2.0 -oslo.utils>=1.4.0 # Apache-2.0 +oslo.utils>=1.6.0 # Apache-2.0 requests>=2.5.2 python-keystoneclient>=1.6.0 simplejson>=2.2.0 From 8557cd9e1b432e671c3e546fa8d8484b4c934972 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Tue, 23 Jun 2015 21:50:00 +0000 Subject: [PATCH 220/845] Updated from global requirements Change-Id: I81052418cf701d5fcda47491542d486f419cc19a --- requirements.txt | 10 +++++----- setup.py | 1 - test-requirements.txt | 12 ++++++------ 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/requirements.txt b/requirements.txt index a7d5f8dda..6f3dafdd4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,14 +1,14 @@ # The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. -pbr>=0.11,<2.0 +pbr<2.0,>=0.11 argparse -cliff>=1.10.0 # Apache-2.0 +cliff>=1.13.0 # Apache-2.0 iso8601>=0.1.9 netaddr>=0.7.12 -oslo.i18n>=1.5.0 # Apache-2.0 -oslo.serialization>=1.4.0 # Apache-2.0 -oslo.utils>=1.6.0 # Apache-2.0 +oslo.i18n>=1.5.0 # Apache-2.0 +oslo.serialization>=1.4.0 # Apache-2.0 +oslo.utils>=1.6.0 # Apache-2.0 requests>=2.5.2 python-keystoneclient>=1.6.0 simplejson>=2.2.0 diff --git a/setup.py b/setup.py index 736375744..056c16c2b 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/test-requirements.txt b/test-requirements.txt index 24c84a17e..dc5d2e715 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,7 +1,7 @@ # The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. -hacking>=0.10.0,<0.11 +hacking<0.11,>=0.10.0 cliff-tablib>=1.0 coverage>=3.6 @@ -9,11 +9,11 @@ discover fixtures>=0.3.14 mox3>=0.7.0 mock>=1.0 -oslosphinx>=2.5.0 # Apache-2.0 -oslotest>=1.5.1 # Apache-2.0 +oslosphinx>=2.5.0 # Apache-2.0 +oslotest>=1.5.1 # Apache-2.0 python-subunit>=0.0.18 -requests-mock>=0.6.0 # Apache-2.0 -sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3 +requests-mock>=0.6.0 # Apache-2.0 +sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 testrepository>=0.0.18 -testtools>=0.9.36,!=1.2.0 +testtools>=1.4.0 tempest-lib>=0.5.0 From da3a4156a23295077962b22d79ff85355ea1771d Mon Sep 17 00:00:00 2001 From: nmagnezi Date: Tue, 30 Jun 2015 12:44:15 +0300 Subject: [PATCH 221/845] Revert "Add '--router:external' option to 'net-create'" This reverts commit 096fd1b175085fcc3c46a248b4afb67561fd29ef. In previous neutronclient versions, we allowed the following syntax to set network as external: --router:external=False or --router:external=True. Since Ibb100d54a5fd8b04ac5e1fc3a26826c695f4d951 was merged, this syntax no longer works. Conflicts: neutronclient/neutron/v2_0/network.py Change-Id: Idbae5461dcaeff3da48d773e323977afcdc6f85e Closes-Bug: #1451391 --- neutronclient/neutron/v2_0/network.py | 7 +------ neutronclient/tests/unit/test_cli20_network.py | 14 -------------- 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/neutronclient/neutron/v2_0/network.py b/neutronclient/neutron/v2_0/network.py index 5338cf54b..8884c7967 100644 --- a/neutronclient/neutron/v2_0/network.py +++ b/neutronclient/neutron/v2_0/network.py @@ -120,11 +120,6 @@ def add_known_arguments(self, parser): action='store_true', help=_('Set the network as shared.'), default=argparse.SUPPRESS) - parser.add_argument( - '--router:external', - action='store_true', - help=_('Set network as external, it is only available for admin'), - default=argparse.SUPPRESS) parser.add_argument( '--provider:network_type', metavar='', @@ -154,7 +149,7 @@ def args2body(self, parsed_args): 'name': parsed_args.name, 'admin_state_up': parsed_args.admin_state}, } neutronV20.update_dict(parsed_args, body['network'], - ['shared', 'tenant_id', 'router:external', + ['shared', 'tenant_id', 'vlan_transparent', 'provider:network_type', 'provider:physical_network', diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index e0681d250..2d6edc563 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -103,20 +103,6 @@ def test_create_network_tags(self): position_names, position_values, tags=['a', 'b']) - def test_create_network_external(self): - """Create net: --router:external myname.""" - resource = 'network' - cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - args = [name, '--router:external'] - position_names = ['name', ] - position_values = [name, ] - external = {'router:external': True} - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - **external) - def test_create_network_state(self): """Create net: --admin_state_down myname.""" resource = 'network' From f446ab5afaba57681792eae7f249d7939b2cba30 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Tue, 30 Jun 2015 22:45:38 +0000 Subject: [PATCH 222/845] Updated from global requirements Change-Id: I26aa57478f72713db419ef98ef9395ba73092b3b --- test-requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test-requirements.txt b/test-requirements.txt index dc5d2e715..8cbfe2153 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -6,7 +6,7 @@ hacking<0.11,>=0.10.0 cliff-tablib>=1.0 coverage>=3.6 discover -fixtures>=0.3.14 +fixtures>=1.3.1 mox3>=0.7.0 mock>=1.0 oslosphinx>=2.5.0 # Apache-2.0 @@ -16,4 +16,4 @@ requests-mock>=0.6.0 # Apache-2.0 sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 testrepository>=0.0.18 testtools>=1.4.0 -tempest-lib>=0.5.0 +tempest-lib>=0.6.1 From f4ddc6e01af9b1a9462baac9584e3343cf574439 Mon Sep 17 00:00:00 2001 From: Amir Sadoughi Date: Fri, 15 May 2015 22:39:53 +0000 Subject: [PATCH 223/845] Support resource plurals not ending in 's' Change-Id: I192f43b7ac44a223d50e86c785b07a0eecd6519a Closes-Bug: 1474401 --- .../tests/unit/test_client_extension.py | 44 +++++++++++++++++++ neutronclient/v2_0/client.py | 1 + 2 files changed, 45 insertions(+) diff --git a/neutronclient/tests/unit/test_client_extension.py b/neutronclient/tests/unit/test_client_extension.py index 0ac8cc823..0e4b3a8c0 100644 --- a/neutronclient/tests/unit/test_client_extension.py +++ b/neutronclient/tests/unit/test_client_extension.py @@ -18,6 +18,7 @@ import mock +from neutronclient.common import extension from neutronclient.neutron.v2_0.contrib import _fox_sockets as fox_sockets from neutronclient import shell from neutronclient.tests.unit import test_cli20 @@ -94,3 +95,46 @@ def test_show_fox_socket(self): args = ['--fields', 'id', '--fields', 'name', self.test_id] self._test_show_resource(resource, cmd, self.test_id, args, ['id', 'name']) + + +class CLITestV20ExtensionJSONAlternatePlurals(test_cli20.CLITestV20Base): + class IPAddress(extension.NeutronClientExtension): + resource = 'ip_address' + resource_plural = '%ses' % resource + object_path = '/%s' % resource_plural + resource_path = '/%s/%%s' % resource_plural + versions = ['2.0'] + + class IPAddressesList(extension.ClientExtensionList, IPAddress): + shell_command = 'ip-address-list' + + def setUp(self): + # need to mock before super because extensions loaded on instantiation + self._mock_extension_loading() + super(CLITestV20ExtensionJSONAlternatePlurals, self).setUp() + + def _create_patch(self, name, func=None): + patcher = mock.patch(name) + thing = patcher.start() + self.addCleanup(patcher.stop) + return thing + + def _mock_extension_loading(self): + ext_pkg = 'neutronclient.common.extension' + contrib = self._create_patch(ext_pkg + '._discover_via_entry_points') + ip_address = mock.MagicMock() + ip_address.IPAddress = self.IPAddress + ip_address.IPAddressesList = self.IPAddressesList + contrib.return_value = [("ip_address", ip_address)] + return contrib + + def test_ext_cmd_loaded(self): + shell.NeutronShell('2.0') + ext_cmd = {'ip-address-list': self.IPAddressesList} + self.assertDictContainsSubset(ext_cmd, shell.COMMANDS['2.0']) + + def test_list_ip_addresses(self): + """List ip_addresses.""" + resources = 'ip_addresses' + cmd = self.IPAddressesList(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, True) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 52640339e..52dddc24e 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -1650,6 +1650,7 @@ def _extend_client_with_module(self, module, version): setattr(self, "%s_path" % cls.resource_plural, cls.object_path) setattr(self, "%s_path" % cls.resource, cls.resource_path) + self.EXTED_PLURALS.update({cls.resource_plural: cls.resource}) def _register_extensions(self, version): for name, module in itertools.chain( From 7066c31fe30f462603e26778bc157cc4a0211926 Mon Sep 17 00:00:00 2001 From: Amir Sadoughi Date: Wed, 3 Jun 2015 21:04:52 +0000 Subject: [PATCH 224/845] Support for child resource extensions Child resource extension classes will have to add a new class member, parent_resource, with new formatting for object_path and resource_path to take into account parent_id. An example class is shown in the unit test. Change-Id: Ie8f875edd341cac2f7af8fd286ae38d2d93e2a04 Closes-Bug: 1446428 --- .../tests/unit/test_client_extension.py | 72 +++++++++++++++++++ neutronclient/v2_0/client.py | 56 +++++++++++---- 2 files changed, 113 insertions(+), 15 deletions(-) diff --git a/neutronclient/tests/unit/test_client_extension.py b/neutronclient/tests/unit/test_client_extension.py index 0e4b3a8c0..6af8a60d9 100644 --- a/neutronclient/tests/unit/test_client_extension.py +++ b/neutronclient/tests/unit/test_client_extension.py @@ -14,6 +14,7 @@ # under the License. # +import inspect import sys import mock @@ -138,3 +139,74 @@ def test_list_ip_addresses(self): resources = 'ip_addresses' cmd = self.IPAddressesList(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, True) + + +class CLITestV20ExtensionJSONChildResource(test_cli20.CLITestV20Base): + class Child(extension.NeutronClientExtension): + parent_resource = 'parents' + child_resource = 'child' + resource = '%s_%s' % (parent_resource, child_resource) + resource_plural = '%sren' % resource + child_resource_plural = '%ren' % child_resource + object_path = '/%s/%%s/%s' % (parent_resource, child_resource_plural) + resource_path = '/%s/%%s/%s/%%s' % (parent_resource, + child_resource_plural) + versions = ['2.0'] + + class ChildrenList(extension.ClientExtensionList, Child): + shell_command = 'parent-child-list' + + class ChildShow(extension.ClientExtensionShow, Child): + shell_command = 'parent-child-show' + + class ChildUpdate(extension.ClientExtensionUpdate, Child): + shell_command = 'parent-child-update' + + class ChildDelete(extension.ClientExtensionDelete, Child): + shell_command = 'parent-child-delete' + + class ChildCreate(extension.ClientExtensionCreate, Child): + shell_command = 'parent-child-create' + + def setUp(self): + # need to mock before super because extensions loaded on instantiation + self._mock_extension_loading() + super(CLITestV20ExtensionJSONChildResource, self).setUp() + + def _create_patch(self, name, func=None): + patcher = mock.patch(name) + thing = patcher.start() + self.addCleanup(patcher.stop) + return thing + + def _mock_extension_loading(self): + ext_pkg = 'neutronclient.common.extension' + contrib = self._create_patch(ext_pkg + '._discover_via_entry_points') + child = mock.MagicMock() + child.Child = self.Child + child.ChildrenList = self.ChildrenList + child.ChildShow = self.ChildShow + child.ChildUpdate = self.ChildUpdate + child.ChildDelete = self.ChildDelete + child.ChildCreate = self.ChildCreate + contrib.return_value = [("child", child)] + return contrib + + def test_ext_cmd_loaded(self): + shell.NeutronShell('2.0') + ext_cmd = {'parent-child-list': self.ChildrenList, + 'parent-child-show': self.ChildShow, + 'parent-child-update': self.ChildUpdate, + 'parent-child-delete': self.ChildDelete, + 'parent-child-create': self.ChildCreate} + self.assertDictContainsSubset(ext_cmd, shell.COMMANDS['2.0']) + + def test_client_methods_have_parent_id_arg(self): + methods = (self.client.list_parents_children, + self.client.show_parents_child, + self.client.update_parents_child, + self.client.delete_parents_child, + self.client.create_parents_child) + for method in methods: + argspec = inspect.getargspec(method) + self.assertIn("parent_id", argspec.args) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 52dddc24e..e0314d23e 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -1605,30 +1605,50 @@ def __init__(self, **kwargs): super(Client, self).__init__(**kwargs) self._register_extensions(self.version) - def extend_show(self, resource_plural, path): + def extend_show(self, resource_plural, path, parent_resource): def _fx(obj, **_params): return self.show_ext(path, obj, **_params) - setattr(self, "show_%s" % resource_plural, _fx) - def extend_list(self, resource_plural, path): + def _parent_fx(parent_id, obj, **_params): + return self.show_ext(path % parent_id, obj, **_params) + fn = _fx if not parent_resource else _parent_fx + setattr(self, "show_%s" % resource_plural, fn) + + def extend_list(self, resource_plural, path, parent_resource): def _fx(**_params): return self.list_ext(path, **_params) - setattr(self, "list_%s" % resource_plural, _fx) - def extend_create(self, resource_singular, path): + def _parent_fx(parent_id, **_params): + return self.list_ext(path % parent_id, **_params) + fn = _fx if not parent_resource else _parent_fx + setattr(self, "list_%s" % resource_plural, fn) + + def extend_create(self, resource_singular, path, parent_resource): def _fx(body=None): return self.create_ext(path, body) - setattr(self, "create_%s" % resource_singular, _fx) - def extend_delete(self, resource_singular, path): + def _parent_fx(parent_id, body=None): + return self.create_ext(path % parent_id, body) + fn = _fx if not parent_resource else _parent_fx + setattr(self, "create_%s" % resource_singular, fn) + + def extend_delete(self, resource_singular, path, parent_resource): def _fx(obj): return self.delete_ext(path, obj) - setattr(self, "delete_%s" % resource_singular, _fx) - def extend_update(self, resource_singular, path): + def _parent_fx(parent_id, obj): + return self.delete_ext(path % parent_id, obj) + fn = _fx if not parent_resource else _parent_fx + setattr(self, "delete_%s" % resource_singular, fn) + + def extend_update(self, resource_singular, path, parent_resource): def _fx(obj, body=None): return self.update_ext(path, obj, body) - setattr(self, "update_%s" % resource_singular, _fx) + + def _parent_fx(parent_id, obj, body=None): + return self.update_ext(path % parent_id, obj, body) + fn = _fx if not parent_resource else _parent_fx + setattr(self, "update_%s" % resource_singular, fn) def _extend_client_with_module(self, module, version): classes = inspect.getmembers(module, inspect.isclass) @@ -1636,16 +1656,22 @@ def _extend_client_with_module(self, module, version): if hasattr(cls, 'versions'): if version not in cls.versions: continue + parent_resource = getattr(cls, 'parent_resource', None) if issubclass(cls, client_extension.ClientExtensionList): - self.extend_list(cls.resource_plural, cls.object_path) + self.extend_list(cls.resource_plural, cls.object_path, + parent_resource) elif issubclass(cls, client_extension.ClientExtensionCreate): - self.extend_create(cls.resource, cls.object_path) + self.extend_create(cls.resource, cls.object_path, + parent_resource) elif issubclass(cls, client_extension.ClientExtensionUpdate): - self.extend_update(cls.resource, cls.resource_path) + self.extend_update(cls.resource, cls.resource_path, + parent_resource) elif issubclass(cls, client_extension.ClientExtensionDelete): - self.extend_delete(cls.resource, cls.resource_path) + self.extend_delete(cls.resource, cls.resource_path, + parent_resource) elif issubclass(cls, client_extension.ClientExtensionShow): - self.extend_show(cls.resource, cls.resource_path) + self.extend_show(cls.resource, cls.resource_path, + parent_resource) elif issubclass(cls, client_extension.NeutronClientExtension): setattr(self, "%s_path" % cls.resource_plural, cls.object_path) From 25a947b203260039437eddcad5771ff089f0ec94 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Sun, 12 Jul 2015 15:22:20 +0000 Subject: [PATCH 225/845] Updated from global requirements Change-Id: Ia71cf497d380d179c62213574cf12b244966995d --- test-requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index 8cbfe2153..96cfa3c1c 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -8,7 +8,8 @@ coverage>=3.6 discover fixtures>=1.3.1 mox3>=0.7.0 -mock>=1.0 +mock>=1.1;python_version!='2.6' +mock==1.0.1;python_version=='2.6' oslosphinx>=2.5.0 # Apache-2.0 oslotest>=1.5.1 # Apache-2.0 python-subunit>=0.0.18 From 52721a8e93ba730e4799913e2b706e9081daaf43 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Sat, 4 Jul 2015 01:12:16 +0900 Subject: [PATCH 226/845] Call UnsetStub/VerifyAll properly for tests with exceptions Some subnet tests expects CommandError before calling stubbed API call. As a result, self.mox.VerifyAll and self.mox.UnsetStubs in _test_create_resource are not called. It is because _test_create_resource does not handle an exception from shell.run_command. This commit changes _test_create_resource to deal with an expected exception. The current unit tests works, but it would be a problem for future feature enhancement. This problem is found during debugging unit test failure in the review of I13da0f204e8ce335a2082c7d829bee28ac567c3f Change-Id: Icb85156f6a2d8e9c0a45cea6e64672e8bf56f15c --- neutronclient/tests/unit/test_cli20.py | 29 +++++++---- neutronclient/tests/unit/test_cli20_subnet.py | 52 +++++++++---------- 2 files changed, 42 insertions(+), 39 deletions(-) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 42330c8aa..b430740a5 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -211,7 +211,9 @@ def _test_create_resource(self, resource, cmd, name, myid, args, position_names, position_values, tenant_id=None, tags=None, admin_state_up=True, extra_body=None, cmd_resource=None, - parent_id=None, **kwargs): + parent_id=None, no_api_call=False, + expected_exception=None, + **kwargs): self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") cmd.get_client().MultipleTimes().AndReturn(self.client) @@ -257,21 +259,26 @@ def _test_create_resource(self, resource, cmd, name, myid, args, mox_body = MyComparator(body, self.client) else: mox_body = self.client.serialize(body) - self.client.httpclient.request( - end_url(path, format=self.format), 'POST', - body=mox_body, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr)) + if not no_api_call: + self.client.httpclient.request( + end_url(path, format=self.format), 'POST', + body=mox_body, + headers=mox.ContainsKeyValue( + 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr)) args.extend(['--request-format', self.format]) self.mox.ReplayAll() cmd_parser = cmd.get_parser('create_' + resource) - shell.run_command(cmd, cmd_parser, args) + if expected_exception: + self.assertRaises(expected_exception, + shell.run_command, cmd, cmd_parser, args) + else: + shell.run_command(cmd, cmd_parser, args) + _str = self.fake_stdout.make_string() + self.assertIn(myid, _str) + if name: + self.assertIn(name, _str) self.mox.VerifyAll() self.mox.UnsetStubs() - _str = self.fake_stdout.make_string() - self.assertIn(myid, _str) - if name: - self.assertIn(name, _str) def _test_list_columns(self, cmd, resources, resources_out, args=('-f', 'json'), diff --git a/neutronclient/tests/unit/test_cli20_subnet.py b/neutronclient/tests/unit/test_cli20_subnet.py index e6a7ea361..414a04d92 100644 --- a/neutronclient/tests/unit/test_cli20_subnet.py +++ b/neutronclient/tests/unit/test_cli20_subnet.py @@ -88,22 +88,14 @@ def test_create_subnet_with_bad_gateway_option(self): SystemExit, self._test_create_resource, resource, cmd, name, myid, args, position_names, position_values) - def _test_create_resource_and_catch_command_error(self, tested_args, - should_fail, + def _test_create_resource_and_catch_command_error(self, should_fail, *args): - _j = lambda args: ' '.join(args) - try: - self._test_create_resource(*args) - except exceptions.CommandError: - if not should_fail: - self.fail( - 'Unexpected exception raised for %s options' % - _j(tested_args)) - self.mox.UnsetStubs() + if should_fail: + params = {'no_api_call': True, + 'expected_exception': exceptions.CommandError} else: - if should_fail: - self.fail( - 'No exception for %s options' % _j(tested_args)) + params = {} + self._test_create_resource(*args, **params) def test_create_subnet_with_enable_and_disable_dhcp(self): """Create subnet: --enable-dhcp and --disable-dhcp.""" @@ -125,7 +117,7 @@ def test_create_subnet_with_enable_and_disable_dhcp(self): args = tested_args + [netid, cidr] pos_values = position_values + [should_fail] self._test_create_resource_and_catch_command_error( - tested_args, should_fail, + should_fail, resource, cmd, name, myid, args, position_names, pos_values) def test_create_subnet_with_multiple_enable_dhcp(self): @@ -151,7 +143,7 @@ def test_create_subnet_with_multiple_enable_dhcp(self): args = tested_args + [netid, cidr] pos_values = position_values + [pos_value] self._test_create_resource_and_catch_command_error( - tested_args, should_fail, + should_fail, resource, cmd, name, myid, args, position_names, pos_values) def test_create_subnet_tenant(self): @@ -468,9 +460,10 @@ def test_create_subnet_with_ipv6_ra_mode_ipv4(self): position_names = ['ip_version', 'ipv6_ra_mode', 'network_id', 'cidr'] position_values = [4, None, netid, cidr] - self.assertRaises(exceptions.CommandError, self._test_create_resource, - resource, cmd, name, myid, args, position_names, - position_values, tenant_id='tenantid') + self._test_create_resource( + resource, cmd, name, myid, args, position_names, + position_values, tenant_id='tenantid', + no_api_call=True, expected_exception=exceptions.CommandError) def test_create_subnet_with_ipv6_address_mode_ipv4(self): resource = 'subnet' @@ -486,9 +479,10 @@ def test_create_subnet_with_ipv6_address_mode_ipv4(self): position_names = ['ip_version', 'ipv6_address_mode', 'network_id', 'cidr'] position_values = [4, None, netid, cidr] - self.assertRaises(exceptions.CommandError, self._test_create_resource, - resource, cmd, name, myid, args, position_names, - position_values, tenant_id='tenantid') + self._test_create_resource( + resource, cmd, name, myid, args, position_names, + position_values, tenant_id='tenantid', + no_api_call=True, expected_exception=exceptions.CommandError) def test_create_subnet_with_subnetpool_ipv4_with_cidr_wildcard(self): resource = 'subnet' @@ -505,9 +499,10 @@ def test_create_subnet_with_subnetpool_ipv4_with_cidr_wildcard(self): position_names = ['ip_version', 'ipv6_address_mode', 'network_id', 'subnetpool_id', 'cidr'] position_values = [4, None, netid, 'subnetpool_id', cidr] - self.assertRaises(exceptions.CommandError, self._test_create_resource, - resource, cmd, name, myid, args, position_names, - position_values, tenant_id='tenantid') + self._test_create_resource( + resource, cmd, name, myid, args, position_names, + position_values, tenant_id='tenantid', + no_api_call=True, expected_exception=exceptions.CommandError) def test_create_subnet_with_subnetpool_ipv4_with_prefixlen(self): resource = 'subnet' @@ -524,9 +519,10 @@ def test_create_subnet_with_subnetpool_ipv4_with_prefixlen(self): position_names = ['ip_version', 'ipv6_address_mode', 'network_id', 'subnetpool_id'] position_values = [4, None, netid, 'subnetpool_id'] - self.assertRaises(exceptions.CommandError, self._test_create_resource, - resource, cmd, name, myid, args, position_names, - position_values, tenant_id='tenantid') + self._test_create_resource( + resource, cmd, name, myid, args, position_names, + position_values, tenant_id='tenantid', + no_api_call=True, expected_exception=exceptions.CommandError) def test_list_subnets_detail(self): """List subnets: -D.""" From 043656c8f8945b5c236bbd30b460f94e05a5a59d Mon Sep 17 00:00:00 2001 From: Sudipta Biswas Date: Sat, 4 Jul 2015 15:56:59 +0900 Subject: [PATCH 227/845] Determine ip version during subnet create. Currently, the default IP version for a subnet-create is IPv4. This behavior is wrong when we use a IPv6 subnetpool and do not specify the ip version explicitly during the subnet create operation. This fix proposes to make the neutron client code for subnet creation become aware of the subnetpool version. This ensures that the proper IP version is sent down to the neutron server if the subnet create is requested with a subnetpool. Co-Authored-By: Akihiro Motoki Change-Id: I13da0f204e8ce335a2082c7d829bee28ac567c3f Closes-bug: 1444146 --- neutronclient/neutron/v2_0/__init__.py | 51 +++++++++++++------ neutronclient/neutron/v2_0/subnet.py | 36 ++++++++----- neutronclient/tests/unit/test_cli20_subnet.py | 35 +++++++++++++ 3 files changed, 94 insertions(+), 28 deletions(-) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index c6d4e6fe2..ad2f6e746 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -46,8 +46,8 @@ def _get_resource_plural(resource, client): return resource + 's' -def find_resourceid_by_id(client, resource, resource_id, cmd_resource=None, - parent_id=None): +def find_resource_by_id(client, resource, resource_id, cmd_resource=None, + parent_id=None, fields=None): if not cmd_resource: cmd_resource = resource cmd_resource_plural = _get_resource_plural(cmd_resource, client) @@ -57,12 +57,15 @@ def find_resourceid_by_id(client, resource, resource_id, cmd_resource=None, match = re.match(UUID_PATTERN, resource_id) collection = resource_plural if match: + params = {'id': resource_id} + if fields: + params['fields'] = fields if parent_id: - data = obj_lister(parent_id, id=resource_id, fields='id') + data = obj_lister(parent_id, **params) else: - data = obj_lister(id=resource_id, fields='id') + data = obj_lister(**params) if data and data[collection]: - return data[collection][0]['id'] + return data[collection][0] not_found_message = (_("Unable to find %(resource)s with id " "'%(id)s'") % {'resource': resource, 'id': resource_id}) @@ -71,14 +74,23 @@ def find_resourceid_by_id(client, resource, resource_id, cmd_resource=None, message=not_found_message, status_code=404) -def _find_resourceid_by_name(client, resource, name, project_id=None, - cmd_resource=None, parent_id=None): +def find_resourceid_by_id(client, resource, resource_id, cmd_resource=None, + parent_id=None): + info = find_resource_by_id(client, resource, resource_id, cmd_resource, + parent_id, fields='id') + return info['id'] + + +def _find_resource_by_name(client, resource, name, project_id=None, + cmd_resource=None, parent_id=None, fields=None): if not cmd_resource: cmd_resource = resource cmd_resource_plural = _get_resource_plural(cmd_resource, client) resource_plural = _get_resource_plural(resource, client) obj_lister = getattr(client, "list_%s" % cmd_resource_plural) - params = {'name': name, 'fields': 'id'} + params = {'name': name} + if fields: + params['fields'] = fields if project_id: params['tenant_id'] = project_id if parent_id: @@ -98,18 +110,27 @@ def _find_resourceid_by_name(client, resource, name, project_id=None, raise exceptions.NeutronClientException( message=not_found_message, status_code=404) else: - return info[0]['id'] + return info[0] + + +def find_resource_by_name_or_id(client, resource, name_or_id, + project_id=None, cmd_resource=None, + parent_id=None, fields=None): + try: + return find_resource_by_id(client, resource, name_or_id, + cmd_resource, parent_id, fields) + except exceptions.NeutronClientException: + return _find_resource_by_name(client, resource, name_or_id, + project_id, cmd_resource, parent_id, + fields) def find_resourceid_by_name_or_id(client, resource, name_or_id, project_id=None, cmd_resource=None, parent_id=None): - try: - return find_resourceid_by_id(client, resource, name_or_id, - cmd_resource, parent_id) - except exceptions.NeutronClientException: - return _find_resourceid_by_name(client, resource, name_or_id, - project_id, cmd_resource, parent_id) + return find_resource_by_name_or_id(client, resource, name_or_id, + project_id, cmd_resource, + parent_id, fields='id')['id'] def add_show_list_common_argument(parser): diff --git a/neutronclient/neutron/v2_0/subnet.py b/neutronclient/neutron/v2_0/subnet.py index 9959355d7..2f61c4bd9 100644 --- a/neutronclient/neutron/v2_0/subnet.py +++ b/neutronclient/neutron/v2_0/subnet.py @@ -165,7 +165,10 @@ def add_known_arguments(self, parser): '--ip-version', type=int, default=4, choices=[4, 6], - help=_('IP version to use, default is 4.')) + help=_('IP version to use, default is 4. ' + 'Note that when subnetpool is specified, ' + 'IP version is determined from the subnetpool ' + 'and this option is ignored.')) parser.add_argument( '--ip_version', type=int, @@ -196,8 +199,25 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): _network_id = neutronV20.find_resourceid_by_name_or_id( self.get_client(), 'network', parsed_args.network_id) - body = {'subnet': {'network_id': _network_id, - 'ip_version': parsed_args.ip_version, }, } + body = {'subnet': {'network_id': _network_id}} + + if parsed_args.prefixlen: + body['subnet'].update({'prefixlen': parsed_args.prefixlen}) + if parsed_args.subnetpool: + if parsed_args.subnetpool == 'None': + _subnetpool_id = None + else: + _subnetpool = neutronV20.find_resource_by_name_or_id( + self.get_client(), 'subnetpool', parsed_args.subnetpool) + _subnetpool_id = _subnetpool['id'] + # Now that we have the pool_id - let's just have a check on the + # ip version used in the pool + parsed_args.ip_version = _subnetpool['ip_version'] + body['subnet'].update({'subnetpool_id': _subnetpool_id}) + + # IP version needs to be set as IP version can be + # determined by subnetpool. + body['subnet']['ip_version'] = parsed_args.ip_version if parsed_args.cidr: # With subnetpool, cidr is now optional for creating subnet. @@ -212,16 +232,6 @@ def args2body(self, parsed_args): % {"ip": parsed_args.ip_version, "cidr": unusable_cidr}) - if parsed_args.prefixlen: - body['subnet'].update({'prefixlen': parsed_args.prefixlen}) - if parsed_args.subnetpool: - if parsed_args.subnetpool == 'None': - _subnetpool_id = None - else: - _subnetpool_id = neutronV20.find_resourceid_by_name_or_id( - self.get_client(), 'subnetpool', parsed_args.subnetpool) - body['subnet'].update({'subnetpool_id': _subnetpool_id}) - updatable_args2body(parsed_args, body) if parsed_args.tenant_id: body['subnet'].update({'tenant_id': parsed_args.tenant_id}) diff --git a/neutronclient/tests/unit/test_cli20_subnet.py b/neutronclient/tests/unit/test_cli20_subnet.py index 414a04d92..e166cf91a 100644 --- a/neutronclient/tests/unit/test_cli20_subnet.py +++ b/neutronclient/tests/unit/test_cli20_subnet.py @@ -19,6 +19,7 @@ from mox3 import mox from neutronclient.common import exceptions +from neutronclient.neutron import v2_0 as neutronV20 from neutronclient.neutron.v2_0 import subnet from neutronclient.tests.unit import test_cli20 @@ -484,6 +485,28 @@ def test_create_subnet_with_ipv6_address_mode_ipv4(self): position_values, tenant_id='tenantid', no_api_call=True, expected_exception=exceptions.CommandError) + def test_create_subnet_with_subnetpool_ipv6_and_ip_ver_ignored(self): + resource = 'subnet' + cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + netid = 'netid' + args = ['--tenant_id', 'tenantid', + '--ip-version', '4', + '--subnetpool', 'subnetpool_id', + netid] + position_names = ['ip_version', 'network_id', 'subnetpool_id'] + position_values = [6, netid, 'subnetpool_id'] + self.mox.StubOutWithMock(neutronV20, 'find_resource_by_name_or_id') + neutronV20.find_resource_by_name_or_id( + self.client, + 'subnetpool', + 'subnetpool_id').AndReturn({'id': 'subnetpool_id', + 'ip_version': 6}) + self._test_create_resource( + resource, cmd, name, myid, args, position_names, + position_values, tenant_id='tenantid') + def test_create_subnet_with_subnetpool_ipv4_with_cidr_wildcard(self): resource = 'subnet' cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) @@ -499,6 +522,12 @@ def test_create_subnet_with_subnetpool_ipv4_with_cidr_wildcard(self): position_names = ['ip_version', 'ipv6_address_mode', 'network_id', 'subnetpool_id', 'cidr'] position_values = [4, None, netid, 'subnetpool_id', cidr] + self.mox.StubOutWithMock(neutronV20, 'find_resource_by_name_or_id') + neutronV20.find_resource_by_name_or_id( + self.client, + 'subnetpool', + 'subnetpool_id').AndReturn({'id': 'subnetpool_id', + 'ip_version': 4}) self._test_create_resource( resource, cmd, name, myid, args, position_names, position_values, tenant_id='tenantid', @@ -519,6 +548,12 @@ def test_create_subnet_with_subnetpool_ipv4_with_prefixlen(self): position_names = ['ip_version', 'ipv6_address_mode', 'network_id', 'subnetpool_id'] position_values = [4, None, netid, 'subnetpool_id'] + self.mox.StubOutWithMock(neutronV20, 'find_resource_by_name_or_id') + neutronV20.find_resource_by_name_or_id( + self.client, + 'subnetpool', + 'subnetpool_id').AndReturn({'id': 'subnetpool_id', + 'ip_version': 4}) self._test_create_resource( resource, cmd, name, myid, args, position_names, position_values, tenant_id='tenantid', From 31f8f23fb5d52a2d674b9810f413c9d2afb5eacf Mon Sep 17 00:00:00 2001 From: Carl Baldwin Date: Mon, 6 Jul 2015 20:05:38 +0000 Subject: [PATCH 228/845] Avoid overwriting parsed_args I just notice this in the context of this review [1]. It seemed like an odd pattern to me but I wasn't sure if it should be folded in to that patch because of the reach of the change. So, I thought I'd post it as a clean-up. [1] https://review.openstack.org/#/c/194078/13/neutronclient/neutron/v2_0/subnet.py Change-Id: Iaf53f595e6d50fc18a33258439b6ae5e476dfca9 --- neutronclient/neutron/v2_0/subnet.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/neutronclient/neutron/v2_0/subnet.py b/neutronclient/neutron/v2_0/subnet.py index 2f61c4bd9..402be1347 100644 --- a/neutronclient/neutron/v2_0/subnet.py +++ b/neutronclient/neutron/v2_0/subnet.py @@ -102,7 +102,7 @@ def add_updatable_arguments(parser): help=argparse.SUPPRESS) -def updatable_args2body(parsed_args, body, for_create=True): +def updatable_args2body(parsed_args, body, for_create=True, ip_version=None): if parsed_args.disable_dhcp and parsed_args.enable_dhcp: raise exceptions.CommandError(_( "You cannot enable and disable DHCP at the same time.")) @@ -124,12 +124,12 @@ def updatable_args2body(parsed_args, body, for_create=True): if parsed_args.dns_nameservers: body['subnet']['dns_nameservers'] = parsed_args.dns_nameservers if for_create and parsed_args.ipv6_ra_mode: - if parsed_args.ip_version == 4: + if ip_version == 4: raise exceptions.CommandError(_("--ipv6-ra-mode is invalid " "when --ip-version is 4")) body['subnet']['ipv6_ra_mode'] = parsed_args.ipv6_ra_mode if for_create and parsed_args.ipv6_address_mode: - if parsed_args.ip_version == 4: + if ip_version == 4: raise exceptions.CommandError(_("--ipv6-address-mode is " "invalid when --ip-version " "is 4")) @@ -203,6 +203,7 @@ def args2body(self, parsed_args): if parsed_args.prefixlen: body['subnet'].update({'prefixlen': parsed_args.prefixlen}) + ip_version = parsed_args.ip_version if parsed_args.subnetpool: if parsed_args.subnetpool == 'None': _subnetpool_id = None @@ -212,27 +213,27 @@ def args2body(self, parsed_args): _subnetpool_id = _subnetpool['id'] # Now that we have the pool_id - let's just have a check on the # ip version used in the pool - parsed_args.ip_version = _subnetpool['ip_version'] + ip_version = _subnetpool['ip_version'] body['subnet'].update({'subnetpool_id': _subnetpool_id}) # IP version needs to be set as IP version can be # determined by subnetpool. - body['subnet']['ip_version'] = parsed_args.ip_version + body['subnet']['ip_version'] = ip_version if parsed_args.cidr: # With subnetpool, cidr is now optional for creating subnet. cidr = parsed_args.cidr body['subnet'].update({'cidr': cidr}) - unusable_cidr = '/32' if parsed_args.ip_version == 4 else '/128' + unusable_cidr = '/32' if ip_version == 4 else '/128' if cidr.endswith(unusable_cidr): self.log.warning(_("An IPv%(ip)d subnet with a %(cidr)s CIDR " "will have only one usable IP address so " "the device attached to it will not have " "any IP connectivity.") - % {"ip": parsed_args.ip_version, + % {"ip": ip_version, "cidr": unusable_cidr}) - updatable_args2body(parsed_args, body) + updatable_args2body(parsed_args, body, ip_version=ip_version) if parsed_args.tenant_id: body['subnet'].update({'tenant_id': parsed_args.tenant_id}) From f9364936e09c085b9cb2d66a9c3ac3291284c132 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Mon, 13 Jul 2015 09:47:37 +0900 Subject: [PATCH 229/845] Remove unused AlreadyAttachedClient This is a revert of revert of the original "Remove unused AlreadyAttachedClient" (30b198edec988d7c969c1edd4a2754bd9c9c79f4). The removed exception was referenced in Horizon Icehouse code and Icehouse is now EOL, so we can remove it safely now. This reverts commit d6cfd34e13426b88b40cb6ef7fdcd60d9b7dcb6b. Related-Bug: #1441969 Change-Id: Ic3ef81af3dab0e0aa3d0012c6e642b060488dd62 --- neutronclient/common/exceptions.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/neutronclient/common/exceptions.py b/neutronclient/common/exceptions.py index da5e19520..ee088c426 100644 --- a/neutronclient/common/exceptions.py +++ b/neutronclient/common/exceptions.py @@ -152,12 +152,6 @@ class OverQuotaClient(Conflict): pass -# TODO(amotoki): It is unused in Neutron, but it is referred to -# in Horizon code. After Horizon code is updated, remove it. -class AlreadyAttachedClient(Conflict): - pass - - class IpAddressGenerationFailureClient(Conflict): pass From 0094e51003b858522a8ec4b2922ed9e725aadbc3 Mon Sep 17 00:00:00 2001 From: "vikram.choudhary" Date: Tue, 14 Jul 2015 15:54:25 +0530 Subject: [PATCH 230/845] Support CLI changes for associating subnetpools and address-scopes. This patch adds the command line support for associating subnetpools and address scopes. DocImpact APIImpact Change-Id: I3b604cae34734a8381e619af4e8e9fad164871e5 Co-Authored-By: Ryan Tidwell Co-Authored-By: Numan Siddique Partially-implements: blueprint address-scopes --- neutronclient/neutron/v2_0/subnetpool.py | 35 ++++++++++++++++- .../tests/unit/test_cli20_subnetpool.py | 38 +++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/neutronclient/neutron/v2_0/subnetpool.py b/neutronclient/neutron/v2_0/subnetpool.py index 54926b0d9..0631a1986 100644 --- a/neutronclient/neutron/v2_0/subnetpool.py +++ b/neutronclient/neutron/v2_0/subnetpool.py @@ -45,7 +45,7 @@ class ListSubnetPool(neutronV20.ListCommand): resource = 'subnetpool' list_columns = ['id', 'name', 'prefixes', - 'default_prefixlen'] + 'default_prefixlen', 'address-scope'] pagination_support = True sorting_support = True @@ -70,12 +70,25 @@ def add_known_arguments(self, parser): parser.add_argument( 'name', help=_('Name of subnetpool to create.')) + parser.add_argument( + '--address-scope', + metavar='ADDRSCOPE', + help=_('ID or name of the address scope with which the subnetpool ' + 'is associated. Prefixes must be unique across address ' + 'scopes')) def args2body(self, parsed_args): body = {'subnetpool': {'prefixes': parsed_args.prefixes}} updatable_args2body(parsed_args, body) if parsed_args.shared: body['subnetpool']['shared'] = True + + # Parse and update for "address-scope" option + if parsed_args.address_scope: + _addrscope_id = neutronV20.find_resourceid_by_name_or_id( + self.get_client(), 'address-scope', + parsed_args.address_scope) + body['subnetpool']['address_scope_id'] = _addrscope_id return body @@ -94,8 +107,28 @@ def add_known_arguments(self, parser): add_updatable_arguments(parser) parser.add_argument('--name', help=_('Name of subnetpool to update.')) + addrscope_args = parser.add_mutually_exclusive_group() + addrscope_args.add_argument('--address-scope', + metavar='ADDRSCOPE', + help=_('ID or name of the address scope ' + 'with which the subnetpool is ' + 'associated. Prefixes must be ' + 'unique across address scopes')) + addrscope_args.add_argument('--no-address-scope', + action='store_true', + help=_('Detach subnetpool from the ' + 'address scope')) def args2body(self, parsed_args): body = {'subnetpool': {}} updatable_args2body(parsed_args, body, for_create=False) + + # Parse and update for "address-scope" option/s + if parsed_args.no_address_scope: + body['subnetpool']['address_scope_id'] = None + elif parsed_args.address_scope: + _addrscope_id = neutronV20.find_resourceid_by_name_or_id( + self.get_client(), 'address-scope', + parsed_args.address_scope) + body['subnetpool']['address_scope_id'] = _addrscope_id return body diff --git a/neutronclient/tests/unit/test_cli20_subnetpool.py b/neutronclient/tests/unit/test_cli20_subnetpool.py index 782d74d78..9873e848c 100644 --- a/neutronclient/tests/unit/test_cli20_subnetpool.py +++ b/neutronclient/tests/unit/test_cli20_subnetpool.py @@ -75,6 +75,26 @@ def test_create_subnetpool_with_unicode(self): self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) + def test_create_subnetpool_with_addrscope(self): + """Create subnetpool: myname in addrscope: foo-address-scope""" + resource = 'subnetpool' + cmd = subnetpool.CreateSubnetPool(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + min_prefixlen = 30 + prefix1 = '11.11.11.0/24' + prefix2 = '12.12.12.0/24' + address_scope = 'foo-address-scope' + args = [name, '--min-prefixlen', str(min_prefixlen), + '--pool-prefix', prefix1, '--pool-prefix', prefix2, + '--address-scope', address_scope] + position_names = ['name', 'min_prefixlen', 'prefixes', + 'address_scope_id'] + position_values = [name, min_prefixlen, [prefix1, prefix2], + address_scope] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + def test_list_subnetpool_pagination(self): cmd = subnetpool.ListSubnetPool(test_cli20.MyApp(sys.stdout), None) self.mox.StubOutWithMock(subnetpool.ListSubnetPool, "extend_list") @@ -115,6 +135,24 @@ def test_update_subnetpool(self): {'name': 'myname'} ) + def test_update_subnetpool_with_address_scope(self): + """Update subnetpool: myid --address-scope newscope.""" + resource = 'subnetpool' + cmd = subnetpool.UpdateSubnetPool(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--address-scope', 'newscope'], + {'address_scope_id': 'newscope'} + ) + + def test_update_subnetpool_with_no_address_scope(self): + """Update subnetpool: myid --no-address-scope.""" + resource = 'subnetpool' + cmd = subnetpool.UpdateSubnetPool(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--no-address-scope'], + {'address_scope_id': None} + ) + def test_show_subnetpool(self): """Show subnetpool: --fields id --fields name myid.""" resource = 'subnetpool' From ab7d9e805a86b6178ec8e462ad71391de0ce4417 Mon Sep 17 00:00:00 2001 From: Gal Sagie Date: Tue, 7 Jul 2015 09:42:37 +0300 Subject: [PATCH 231/845] Devref documentation for client command extension support Change-Id: If71c9e2bbf888b1a68d3b7dc351f7df02f1a380f Closes-Bug: #1470622 --- .../devref/client_command_extensions.rst | 40 +++++++++++++++++++ doc/source/devref/index.rst | 29 ++++++++++++++ doc/source/index.rst | 12 +++++- 3 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 doc/source/devref/client_command_extensions.rst create mode 100644 doc/source/devref/index.rst diff --git a/doc/source/devref/client_command_extensions.rst b/doc/source/devref/client_command_extensions.rst new file mode 100644 index 000000000..8be6245b5 --- /dev/null +++ b/doc/source/devref/client_command_extensions.rst @@ -0,0 +1,40 @@ +================================= +Client command extension support +================================= + +The client command extension adds support for extending the neutron client while +considering ease of creation. +Extensions strongly conform to preexisting neutron commands (/neutron/v2_0/). + +A sample extension can be seen at: +neutronclient/neutron/v2_0/contrib/_fox_sockets.py + +Minimum requirements from an extension +-------------------------------------- + +* Will have a class that subclasses NeutronClientExtension to provide the + requisite version support, paths, and variable names for the client. + Example: neutronclient.neutron.v2_0.contrib._fox_sockets.FoxInSocket + +* Will have at least one class that subclasses from the ClientExtension + classes to provide the new functionality to the client + Example: neutronclient.neutron.v2_0.contrib._fox_sockets.FoxInSocketsList + +* ClientExtension subclasses must have a shell_command class variable if the + command is to be available to the CLI (shell.py) + Example: neutronclient.neutron.v2_0.contrib._fox_sockets.FoxInSocketsList + + +Precedence of command loading +------------------------------ + +* hard coded commands are loaded first +* contributed commands (those in /contrib) +* external commands (installed in the environment) are loaded last + +Commands that have the same name will be overwritten by commands that are +loaded later. To change the execution of a command for your particular +extension you only need to override the execute method. + +Currently this extension support is limited to top-level resources. +Parent/child relationships may be added if desired. \ No newline at end of file diff --git a/doc/source/devref/index.rst b/doc/source/devref/index.rst new file mode 100644 index 000000000..c23f0a6e3 --- /dev/null +++ b/doc/source/devref/index.rst @@ -0,0 +1,29 @@ +.. + Copyright 2015 OpenStack Foundation + 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. + +Developer Guide +=============== + +In the Developer Guide, you will find information on Neutron's Client lower level +programming APIs. + + +Programming HowTos and Tutorials +-------------------------------- +.. toctree:: + :maxdepth: 3 + + client_command_extensions diff --git a/doc/source/index.rst b/doc/source/index.rst index 9e032df51..d02a5c8f1 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -48,6 +48,16 @@ If neutron server does not require authentication, besides these two arguments o Once you've configured your authentication parameters, you can run ``neutron -h`` to see a complete listing of available commands. + +Developer Docs +============== + +.. toctree:: + :maxdepth: 1 + + devref/index + + Release Notes ============= @@ -72,4 +82,4 @@ Release Notes * add --endpoint-type and OS_ENDPOINT_TYPE to shell client * made the publicURL the default endpoint instead of adminURL * add ability to update security group name (requires 2013.2-Havana or later) -* add flake8 and pbr support for testing and building +* add flake8 and pbr support for testing and building \ No newline at end of file From d61a5b5cd97c3ce325544a1c55419b1424d9ddbe Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Wed, 15 Jul 2015 01:37:35 +0000 Subject: [PATCH 232/845] Updated from global requirements Change-Id: I3bb2f925788ba35174ff08271e7e0780a69c0049 --- requirements.txt | 4 ++-- setup.py | 2 +- test-requirements.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index 6f3dafdd4..5bc7d2d35 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,14 +1,14 @@ # The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. -pbr<2.0,>=0.11 +pbr<2.0,>=1.3 argparse cliff>=1.13.0 # Apache-2.0 iso8601>=0.1.9 netaddr>=0.7.12 oslo.i18n>=1.5.0 # Apache-2.0 oslo.serialization>=1.4.0 # Apache-2.0 -oslo.utils>=1.6.0 # Apache-2.0 +oslo.utils>=1.9.0 # Apache-2.0 requests>=2.5.2 python-keystoneclient>=1.6.0 simplejson>=2.2.0 diff --git a/setup.py b/setup.py index 056c16c2b..d8080d05c 100644 --- a/setup.py +++ b/setup.py @@ -25,5 +25,5 @@ pass setuptools.setup( - setup_requires=['pbr'], + setup_requires=['pbr>=1.3'], pbr=True) diff --git a/test-requirements.txt b/test-requirements.txt index 96cfa3c1c..73ede4fee 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -11,7 +11,7 @@ mox3>=0.7.0 mock>=1.1;python_version!='2.6' mock==1.0.1;python_version=='2.6' oslosphinx>=2.5.0 # Apache-2.0 -oslotest>=1.5.1 # Apache-2.0 +oslotest>=1.7.0 # Apache-2.0 python-subunit>=0.0.18 requests-mock>=0.6.0 # Apache-2.0 sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 From 11897cb42c2ba67c8afc1c6791c6c40184689784 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Wed, 15 Jul 2015 05:42:42 -0700 Subject: [PATCH 233/845] NSX gateway extension: allow more transport type values This patch adds ipsec_gre and ipsec_stt to the list of allowed value for the trasnport type option when creating gateway devices. Change-Id: Ie48a74a05505db76a7305368793a77789d15f53e Partial-Bug: #1474834 --- neutronclient/neutron/v2_0/nsx/networkgateway.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/neutronclient/neutron/v2_0/nsx/networkgateway.py b/neutronclient/neutron/v2_0/nsx/networkgateway.py index 52a38256b..251fbc658 100644 --- a/neutronclient/neutron/v2_0/nsx/networkgateway.py +++ b/neutronclient/neutron/v2_0/nsx/networkgateway.py @@ -23,8 +23,9 @@ GW_RESOURCE = 'network_gateway' DEV_RESOURCE = 'gateway_device' CONNECTOR_TYPE_HELP = _("Type of the transport zone connector to use for this " - "device. Valid values are gre, stt, ipsecgre, " - "ipsecstt, and bridge. Defaults to stt.") + "device. Valid values are gre, stt, ipsec_gre, " + "ipsec_stt, and bridge. Defaults to stt. ipsecgre and " + "ipsecstt are also accepted as alternative names") CONNECTOR_IP_HELP = _("IP address for this device's transport connector. " "It must correspond to the IP address of the interface " "used for tenant traffic on the NSX gateway node.") @@ -84,7 +85,8 @@ def add_known_arguments(self, parser): parser.add_argument( '--connector-type', default='stt', - choices=['stt', 'gre', 'ipsecgre', 'ipsecstt', 'bridge'], + choices=['stt', 'gre', 'ipsecgre', 'ipsecstt', 'bridge', + 'ipsec_gre', 'ipsec_stt'], help=CONNECTOR_TYPE_HELP) parser.add_argument( '--connector-ip', @@ -117,7 +119,8 @@ def add_known_arguments(self, parser): parser.add_argument( '--connector-type', required=False, - choices=['stt', 'gre', 'ipsecgre', 'ipsecstt', 'bridge'], + choices=['stt', 'gre', 'ipsecgre', 'ipsecstt', 'bridge', + 'ipsec_gre', 'ipsec_stt'], help=CONNECTOR_TYPE_HELP) parser.add_argument( '--connector-ip', From ccf6fb868a39d83d6ea51f5245a31cef6ff19476 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Fri, 17 Jul 2015 16:18:04 +0000 Subject: [PATCH 234/845] Updated from global requirements Change-Id: Ibb4f2797db5dd4f3fe6bedabdfbe1c15287c25a2 --- test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index 73ede4fee..8392ca18f 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -8,7 +8,7 @@ coverage>=3.6 discover fixtures>=1.3.1 mox3>=0.7.0 -mock>=1.1;python_version!='2.6' +mock!=1.1.4,>=1.1;python_version!='2.6' mock==1.0.1;python_version=='2.6' oslosphinx>=2.5.0 # Apache-2.0 oslotest>=1.7.0 # Apache-2.0 From 8da3dc8f0cfff7b99ea87a742943cf480e65c75d Mon Sep 17 00:00:00 2001 From: Ilya Shakhat Date: Mon, 10 Aug 2015 12:59:58 +0300 Subject: [PATCH 235/845] Remove newlines from request and response log Do not surround request and response messages with newline characters to simplify reading by robots. Change-Id: I2efec1154cd9a327ed25b342e10d9205a22fda1c --- neutronclient/common/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neutronclient/common/utils.py b/neutronclient/common/utils.py index 788b024ae..dc5f7d6b1 100644 --- a/neutronclient/common/utils.py +++ b/neutronclient/common/utils.py @@ -134,13 +134,13 @@ def http_log_req(_logger, args, kwargs): if 'body' in kwargs and kwargs['body']: string_parts.append(" -d '%s'" % (kwargs['body'])) req = encodeutils.safe_encode("".join(string_parts)) - _logger.debug("\nREQ: %s\n", req) + _logger.debug("REQ: %s", req) def http_log_resp(_logger, resp, body): if not _logger.isEnabledFor(logging.DEBUG): return - _logger.debug("RESP:%(code)s %(headers)s %(body)s\n", + _logger.debug("RESP: %(code)s %(headers)s %(body)s", {'code': resp.status_code, 'headers': resp.headers, 'body': body}) From 22c84927d133158c9de429ba1b9dbcd1d077d773 Mon Sep 17 00:00:00 2001 From: Ramanjaneya Date: Fri, 24 Jul 2015 15:51:53 +0530 Subject: [PATCH 236/845] Support RBAC neutron-client changes. This patch adds the command line support for RBAC work. DocImpact APIImpact Partially-Implements: blueprint rbac-networks Co-Authored-By: dongfeng Change-Id: I00c6b84b3f7d810f137ce05c0cd936dc194d9708 --- neutronclient/neutron/v2_0/rbac.py | 102 +++++++++++++++++ neutronclient/shell.py | 6 + neutronclient/tests/unit/test_cli20.py | 3 +- neutronclient/tests/unit/test_cli20_rbac.py | 117 ++++++++++++++++++++ neutronclient/v2_0/client.py | 30 +++++ 5 files changed, 257 insertions(+), 1 deletion(-) create mode 100644 neutronclient/neutron/v2_0/rbac.py create mode 100644 neutronclient/tests/unit/test_cli20_rbac.py diff --git a/neutronclient/neutron/v2_0/rbac.py b/neutronclient/neutron/v2_0/rbac.py new file mode 100644 index 000000000..59a2c80bb --- /dev/null +++ b/neutronclient/neutron/v2_0/rbac.py @@ -0,0 +1,102 @@ +# Copyright 2015 Huawei Technologies India Pvt Ltd. +# 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. + +from neutronclient.i18n import _ +from neutronclient.neutron import v2_0 as neutronV20 + + +def get_rbac_object_id(client, obj_type, obj_id_or_name): + if obj_type == 'network': + obj_id = neutronV20.find_resourceid_by_name_or_id(client, + 'network', + obj_id_or_name) + return obj_id + + +class ListRBACPolicy(neutronV20.ListCommand): + """List RBAC policies that belong to a given tenant.""" + + resource = 'rbac_policy' + list_columns = ['id', 'object_id'] + pagination_support = True + sorting_support = True + + +class ShowRBACPolicy(neutronV20.ShowCommand): + """Show information of a given RBAC policy.""" + + resource = 'rbac_policy' + + +class CreateRBACPolicy(neutronV20.CreateCommand): + """Create a RBAC policy for a given tenant.""" + + resource = 'rbac_policy' + + def add_known_arguments(self, parser): + parser.add_argument( + 'name', + metavar='RBAC_OBJECT', + help=_('ID or name of the RBAC object.')) + parser.add_argument( + '--type', choices=['network'], + required=True, + help=_('Type of the object that RBAC policy affects.')) + parser.add_argument( + '--target-tenant', + help=_('ID of the tenant to which the RBAC ' + 'policy will be enforced.')) + parser.add_argument( + '--action', choices=['access_as_external', 'access_as_shared'], + required=True, + help=_('Action for the RBAC policy.')) + + def args2body(self, parsed_args): + neutron_client = self.get_client() + neutron_client.format = parsed_args.request_format + _object_id = get_rbac_object_id(neutron_client, parsed_args.type, + parsed_args.name) + body = {self.resource: { + 'object_id': _object_id, + 'object_type': parsed_args.type, + 'target_tenant': parsed_args.target_tenant, + 'action': parsed_args.action, + }, } + return body + + +class UpdateRBACPolicy(neutronV20.UpdateCommand): + """Update RBAC policy for given tenant.""" + + resource = 'rbac_policy' + + def add_known_arguments(self, parser): + parser.add_argument( + '--target-tenant', + help=_('ID of the tenant to which the RBAC ' + 'policy will be enforced.')) + + def args2body(self, parsed_args): + + body = {self.resource: { + 'target_tenant': parsed_args.target_tenant, + }, } + return body + + +class DeleteRBACPolicy(neutronV20.DeleteCommand): + """Delete a RBAC policy.""" + + resource = 'rbac_policy' diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 4953c1f85..2c7cc7af3 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -72,6 +72,7 @@ from neutronclient.neutron.v2_0 import policyprofile from neutronclient.neutron.v2_0 import port from neutronclient.neutron.v2_0 import quota +from neutronclient.neutron.v2_0 import rbac from neutronclient.neutron.v2_0 import router from neutronclient.neutron.v2_0 import securitygroup from neutronclient.neutron.v2_0 import servicetype @@ -360,6 +361,11 @@ class BashCompletionCommand(openstack_command.OpenStackCommand): 'nec-packet-filter-create': packetfilter.CreatePacketFilter, 'nec-packet-filter-update': packetfilter.UpdatePacketFilter, 'nec-packet-filter-delete': packetfilter.DeletePacketFilter, + 'rbac-create': rbac.CreateRBACPolicy, + 'rbac-update': rbac.UpdateRBACPolicy, + 'rbac-list': rbac.ListRBACPolicy, + 'rbac-show': rbac.ShowRBACPolicy, + 'rbac-delete': rbac.DeleteRBACPolicy, } COMMANDS = {'2.0': COMMAND_V2} diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index b430740a5..68a235cc4 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -224,7 +224,8 @@ def _test_create_resource(self, resource, cmd, name, myid, args, 'policy_profile', 'ikepolicy', 'ipsecpolicy', 'metering_label', 'metering_label_rule', 'net_partition', - 'fox_socket', 'subnetpool'] + 'fox_socket', 'subnetpool', + 'rbac_policy'] if not cmd_resource: cmd_resource = resource if (resource in non_admin_status_resources): diff --git a/neutronclient/tests/unit/test_cli20_rbac.py b/neutronclient/tests/unit/test_cli20_rbac.py new file mode 100644 index 000000000..fd51ffb7d --- /dev/null +++ b/neutronclient/tests/unit/test_cli20_rbac.py @@ -0,0 +1,117 @@ +# Copyright 2015 Huawei Technologies India Pvt Ltd. +# 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 sys + +from neutronclient.neutron.v2_0 import rbac +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20RBACJSON(test_cli20.CLITestV20Base): + def test_create_rbac_policy_with_mandatory_params(self): + """Create rbac: rbac_object --type network --action access_as_shared""" + resource = 'rbac_policy' + cmd = rbac.CreateRBACPolicy(test_cli20.MyApp(sys.stdout), None) + name = 'rbac_object' + myid = 'myid' + args = [name, '--type', 'network', + '--action', 'access_as_shared'] + position_names = ['object_id', 'object_type', + 'target_tenant', 'action'] + position_values = [name, 'network', None, 'access_as_shared'] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_create_rbac_policy_with_all_params(self): + """Create rbac: rbac_object --type network """ + """--target-tenant tenant_id --action access_as_external""" + resource = 'rbac_policy' + cmd = rbac.CreateRBACPolicy(test_cli20.MyApp(sys.stdout), None) + name = 'rbac_object' + myid = 'myid' + args = [name, '--type', 'network', + '--target-tenant', 'tenant_id', + '--action', 'access_as_external'] + position_names = ['object_id', 'object_type', + 'target_tenant', 'action'] + position_values = [name, 'network', 'tenant_id', 'access_as_external'] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_create_rbac_policy_with_unicode(self): + """Create rbac policy u'\u7f51\u7edc'.""" + resource = 'rbac_policy' + cmd = rbac.CreateRBACPolicy(test_cli20.MyApp(sys.stdout), None) + name = u'\u7f51\u7edc' + myid = 'myid' + args = [name, '--type', 'network', + '--target-tenant', 'tenant_id', + '--action', 'access_as_external'] + position_names = ['object_id', 'object_type', + 'target_tenant', 'action'] + position_values = [name, 'network', 'tenant_id', 'access_as_external'] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_update_rbac_policy(self): + """rbac-update --target-tenant .""" + resource = 'rbac_policy' + cmd = rbac.UpdateRBACPolicy(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--target-tenant', 'tenant_id'], + {'target_tenant': 'tenant_id', }) + + def test_delete_rbac_policy(self): + """rbac-delete my-id.""" + resource = 'rbac_policy' + cmd = rbac.DeleteRBACPolicy(test_cli20.MyApp(sys.stdout), None) + my_id = 'myid1' + args = [my_id] + self._test_delete_resource(resource, cmd, my_id, args) + + def test_list_rbac_policies(self): + """rbac-list.""" + resources = "rbac_policies" + cmd = rbac.ListRBACPolicy(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, True) + + def test_list_rbac_policies_pagination(self): + """rbac-list with pagination.""" + resources = "rbac_policies" + cmd = rbac.ListRBACPolicy(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources_with_pagination(resources, cmd) + + def test_list_rbac_policies_sort(self): + """sorted list: rbac-list --sort-key name --sort-key id + --sort-key asc --sort-key desc + """ + resources = "rbac_policies" + cmd = rbac.ListRBACPolicy(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, + sort_key=["name", "id"], + sort_dir=["asc", "desc"]) + + def test_list_rbac_policies_limit(self): + """size (1000) limited list: rbac-list -P.""" + resources = "rbac_policies" + cmd = rbac.ListRBACPolicy(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, page_size=1000) + + def test_show_rbac_policy(self): + """rbac-show test_id.""" + resource = 'rbac_policy' + cmd = rbac.ShowRBACPolicy(test_cli20.MyApp(sys.stdout), None) + args = ['--fields', 'id', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, args, ['id']) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 52dddc24e..a38096b51 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -427,6 +427,8 @@ class Client(ClientBase): firewall_path = "/fw/firewalls/%s" net_partitions_path = "/net-partitions" net_partition_path = "/net-partitions/%s" + rbac_policies_path = "/rbac-policies" + rbac_policy_path = "/rbac-policies/%s" # API has no way to report plurals, so we have to hard code them EXTED_PLURALS = {'routers': 'router', @@ -458,6 +460,7 @@ class Client(ClientBase): 'lbaas_healthmonitors': 'lbaas_healthmonitor', 'lbaas_members': 'lbaas_member', 'healthmonitors': 'healthmonitor', + 'rbac_policies': 'rbac_policy', } @APIParamsCall @@ -1600,6 +1603,33 @@ def delete_packet_filter(self, packet_filter_id): """Delete the specified packet filter.""" return self.delete(self.packet_filter_path % packet_filter_id) + @APIParamsCall + def create_rbac_policy(self, body=None): + """Create a new RBAC policy.""" + return self.post(self.rbac_policies_path, body=body) + + @APIParamsCall + def update_rbac_policy(self, rbac_policy_id, body=None): + """Update a RBAC policy.""" + return self.put(self.rbac_policy_path % rbac_policy_id, body=body) + + @APIParamsCall + def list_rbac_policies(self, retrieve_all=True, **_params): + """Fetch a list of all RBAC policies for a tenant.""" + return self.list('rbac_policies', self.rbac_policies_path, + retrieve_all, **_params) + + @APIParamsCall + def show_rbac_policy(self, rbac_policy_id, **_params): + """Fetch information of a certain RBAC policy.""" + return self.get(self.rbac_policy_path % rbac_policy_id, + params=_params) + + @APIParamsCall + def delete_rbac_policy(self, rbac_policy_id): + """Delete the specified RBAC policy.""" + return self.delete(self.rbac_policy_path % rbac_policy_id) + def __init__(self, **kwargs): """Initialize a new client for the Neutron v2.0 API.""" super(Client, self).__init__(**kwargs) From 16e02dd83adabd58029e57a95f8ce844d972a469 Mon Sep 17 00:00:00 2001 From: John Schwarz Date: Wed, 12 Aug 2015 17:39:55 +0300 Subject: [PATCH 237/845] Disable failing vpn tests Until one of the solutions proposed in [1] are complete, and until the vpn plugin is added to this repo, failing tests should be commented so as to not block ongoing development. [1]: https://etherpad.openstack.org/p/vpn-test-changes Related-bug: #1484148 Change-Id: I9a41bd74a1a55c0b0137c7b4453eaf7517c43483 --- neutronclient/tests/functional/test_readonly_neutron.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/neutronclient/tests/functional/test_readonly_neutron.py b/neutronclient/tests/functional/test_readonly_neutron.py index b436b0ba0..c088c5f92 100644 --- a/neutronclient/tests/functional/test_readonly_neutron.py +++ b/neutronclient/tests/functional/test_readonly_neutron.py @@ -11,6 +11,7 @@ # under the License. import re +import unittest2 from tempest_lib import exceptions @@ -123,6 +124,7 @@ def test_neutron_subnet_list(self): self.assertTableStruct(subnet_list, ['id', 'name', 'cidr', 'allocation_pools']) + @unittest2.skip("Skipping until 1484148 is resolved") def test_neutron_vpn_ikepolicy_list(self): ikepolicy = self.parser.listing(self.neutron('vpn-ikepolicy-list')) self.assertTableStruct(ikepolicy, ['id', 'name', @@ -130,6 +132,7 @@ def test_neutron_vpn_ikepolicy_list(self): 'encryption_algorithm', 'ike_version', 'pfs']) + @unittest2.skip("Skipping until 1484148 is resolved") def test_neutron_vpn_ipsecpolicy_list(self): ipsecpolicy = self.parser.listing(self.neutron('vpn-ipsecpolicy-list')) self.assertTableStruct(ipsecpolicy, ['id', 'name', @@ -137,11 +140,13 @@ def test_neutron_vpn_ipsecpolicy_list(self): 'encryption_algorithm', 'pfs']) + @unittest2.skip("Skipping until 1484148 is resolved") def test_neutron_vpn_service_list(self): vpn_list = self.parser.listing(self.neutron('vpn-service-list')) self.assertTableStruct(vpn_list, ['id', 'name', 'router_id', 'status']) + @unittest2.skip("Skipping until 1484148 is resolved") def test_neutron_ipsec_site_connection_list(self): ipsec_site = self.parser.listing(self.neutron ('ipsec-site-connection-list')) From 52718908bf0ef189c25e2db74905913df8034a79 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Tue, 23 Jun 2015 21:17:35 +0900 Subject: [PATCH 238/845] Remove --shared option from firewall-create Neutron FWaaS firewall object does not support 'shared' attribute at now and 'shared' attribute has no meaning. It is confusing to have --shared option in CLI side. Change-Id: Ia737b80d7b8a397f06fdf6d6ea8a5cd64d9036a4 Closes-Bug: #1465440 --- neutronclient/neutron/v2_0/fw/firewall.py | 10 +--------- neutronclient/tests/unit/fw/test_cli20_firewall.py | 3 +-- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/neutronclient/neutron/v2_0/fw/firewall.py b/neutronclient/neutron/v2_0/fw/firewall.py index e177669ba..3a88a6baa 100644 --- a/neutronclient/neutron/v2_0/fw/firewall.py +++ b/neutronclient/neutron/v2_0/fw/firewall.py @@ -14,8 +14,6 @@ # under the License. # -import argparse - from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronv20 @@ -51,11 +49,6 @@ def add_known_arguments(self, parser): parser.add_argument( '--description', help=_('Description for the firewall rule.')) - parser.add_argument( - '--shared', - action='store_true', - help=_('Set shared to True (default is False).'), - default=argparse.SUPPRESS) parser.add_argument( '--admin-state-down', dest='admin_state', @@ -83,8 +76,7 @@ def args2body(self, parsed_args): neutronv20.find_resourceid_by_name_or_id(client, 'router', r) for r in parsed_args.routers] neutronv20.update_dict(parsed_args, body[self.resource], - ['name', 'description', 'shared', - 'tenant_id']) + ['name', 'description', 'tenant_id']) return body diff --git a/neutronclient/tests/unit/fw/test_cli20_firewall.py b/neutronclient/tests/unit/fw/test_cli20_firewall.py index 1d7fecbba..fdcbda6b2 100644 --- a/neutronclient/tests/unit/fw/test_cli20_firewall.py +++ b/neutronclient/tests/unit/fw/test_cli20_firewall.py @@ -47,7 +47,6 @@ def test_create_firewall_with_all_params(self): tenant_id = 'my-tenant' my_id = 'my-id' args = ['--description', description, - '--shared', '--admin-state-down', '--tenant-id', tenant_id, policy_id] @@ -56,7 +55,7 @@ def test_create_firewall_with_all_params(self): self._test_create_resource(resource, cmd, name, my_id, args, position_names, position_values, description=description, - shared=True, admin_state_up=False, + admin_state_up=False, tenant_id=tenant_id) def test_create_firewall_with_routers(self): From d749973859988e30347e94029703b508e126c79e Mon Sep 17 00:00:00 2001 From: "vikram.choudhary" Date: Wed, 19 Aug 2015 13:50:58 +0530 Subject: [PATCH 239/845] Support Command line changes for Address Scope This patch adds the command line support for address scope functionality. Co-Authored-By: Ryan Tidwell Co-Authored-By: Numan Siddique Partially-implements: blueprint address-scopes Change-Id: I2a0e0e797b1864a104b3851e7dcdc7691ff9c9a1 --- neutronclient/neutron/v2_0/address_scope.py | 76 ++++++++++ neutronclient/shell.py | 6 + neutronclient/tests/unit/test_cli20.py | 2 +- .../tests/unit/test_cli20_address_scope.py | 141 ++++++++++++++++++ neutronclient/v2_0/client.py | 30 ++++ 5 files changed, 254 insertions(+), 1 deletion(-) create mode 100755 neutronclient/neutron/v2_0/address_scope.py create mode 100755 neutronclient/tests/unit/test_cli20_address_scope.py diff --git a/neutronclient/neutron/v2_0/address_scope.py b/neutronclient/neutron/v2_0/address_scope.py new file mode 100755 index 000000000..c8517289b --- /dev/null +++ b/neutronclient/neutron/v2_0/address_scope.py @@ -0,0 +1,76 @@ +# Copyright 2015 Huawei Technologies India Pvt. Ltd.. +# 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. +# + +from neutronclient.i18n import _ +from neutronclient.neutron import v2_0 as neutronV20 + + +class ListAddressScope(neutronV20.ListCommand): + """List address scopes that belong to a given tenant.""" + + resource = 'address_scope' + list_columns = ['id', 'name'] + pagination_support = True + sorting_support = True + + +class ShowAddressScope(neutronV20.ShowCommand): + """Show information about an address scope.""" + + resource = 'address_scope' + + +class CreateAddressScope(neutronV20.CreateCommand): + """Create an address scope for a given tenant.""" + + resource = 'address_scope' + + def add_known_arguments(self, parser): + parser.add_argument( + '--shared', + action='store_true', + help=_('Set the address scope as shared.')) + parser.add_argument( + 'name', + help=_('Specify the name of the address scope.')) + + def args2body(self, parsed_args): + body = {'name': parsed_args.name} + if parsed_args.shared: + body['shared'] = True + neutronV20.update_dict(parsed_args, body, ['tenant_id']) + return {self.resource: body} + + +class DeleteAddressScope(neutronV20.DeleteCommand): + """Delete an address scope.""" + + resource = 'address_scope' + + +class UpdateAddressScope(neutronV20.UpdateCommand): + """Update an address scope.""" + + resource = 'address_scope' + + def add_known_arguments(self, parser): + parser.add_argument('--name', + help=_('Name of the address scope to update.')) + + def args2body(self, parsed_args): + body = {} + neutronV20.update_dict(parsed_args, body, ['name']) + return {self.resource: body} diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 2c7cc7af3..d40c77a86 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -45,6 +45,7 @@ from neutronclient.common import extension as client_extension from neutronclient.common import utils from neutronclient.i18n import _ +from neutronclient.neutron.v2_0 import address_scope from neutronclient.neutron.v2_0 import agent from neutronclient.neutron.v2_0 import agentscheduler from neutronclient.neutron.v2_0 import credential @@ -366,6 +367,11 @@ class BashCompletionCommand(openstack_command.OpenStackCommand): 'rbac-list': rbac.ListRBACPolicy, 'rbac-show': rbac.ShowRBACPolicy, 'rbac-delete': rbac.DeleteRBACPolicy, + 'address-scope-list': address_scope.ListAddressScope, + 'address-scope-show': address_scope.ShowAddressScope, + 'address-scope-create': address_scope.CreateAddressScope, + 'address-scope-delete': address_scope.DeleteAddressScope, + 'address-scope-update': address_scope.UpdateAddressScope, } COMMANDS = {'2.0': COMMAND_V2} diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 68a235cc4..efee1ae27 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -225,7 +225,7 @@ def _test_create_resource(self, resource, cmd, name, myid, args, 'ipsecpolicy', 'metering_label', 'metering_label_rule', 'net_partition', 'fox_socket', 'subnetpool', - 'rbac_policy'] + 'rbac_policy', 'address_scope'] if not cmd_resource: cmd_resource = resource if (resource in non_admin_status_resources): diff --git a/neutronclient/tests/unit/test_cli20_address_scope.py b/neutronclient/tests/unit/test_cli20_address_scope.py new file mode 100755 index 000000000..e7b8a33b2 --- /dev/null +++ b/neutronclient/tests/unit/test_cli20_address_scope.py @@ -0,0 +1,141 @@ +# Copyright 2015 Huawei Technologies India Pvt. Ltd. +# 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 sys + +from mox3 import mox + +from neutronclient.common import exceptions +from neutronclient.neutron.v2_0 import address_scope +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20AddressScopeJSON(test_cli20.CLITestV20Base): + def setUp(self): + super(CLITestV20AddressScopeJSON, self).setUp(plurals={'tags': 'tag'}) + + def test_create_address_scope_with_minimum_option(self): + """Create address_scope: foo-address-scope with minimum option.""" + resource = 'address_scope' + cmd = address_scope.CreateAddressScope( + test_cli20.MyApp(sys.stdout), None) + name = 'foo-address-scope' + myid = 'myid' + args = [name] + position_names = ['name'] + position_values = [name] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_create_address_scope_with_all_option(self): + """Create address_scope: foo-address-scope with all options.""" + resource = 'address_scope' + cmd = address_scope.CreateAddressScope( + test_cli20.MyApp(sys.stdout), None) + name = 'foo-address-scope' + myid = 'myid' + args = [name, '--shared'] + position_names = ['name', 'shared'] + position_values = [name, True] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_create_address_scope_with_unicode(self): + """Create address_scope: u'\u7f51\u7edc'.""" + resource = 'address_scope' + cmd = address_scope.CreateAddressScope( + test_cli20.MyApp(sys.stdout), None) + name = u'\u7f51\u7edc' + myid = 'myid' + args = [name] + position_names = ['name'] + position_values = [name] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_update_address_scope_exception(self): + """Update address_scope (Negative) : myid.""" + resource = 'address_scope' + cmd = address_scope.UpdateAddressScope( + test_cli20.MyApp(sys.stdout), None) + self.assertRaises(exceptions.CommandError, self._test_update_resource, + resource, cmd, 'myid', ['myid'], {}) + + def test_update_address_scope(self): + """Update address_scope: myid --name newname-address-scope.""" + resource = 'address_scope' + cmd = address_scope.UpdateAddressScope( + test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--name', 'newname-address-scope'], + {'name': 'newname-address-scope'} + ) + + def test_list_address_scope(self): + """address_scope-list.""" + resources = "address_scopes" + cmd = address_scope.ListAddressScope(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources(resources, cmd, True) + + def test_list_address_scope_pagination(self): + """address_scope-list.""" + cmd = address_scope.ListAddressScope(test_cli20.MyApp(sys.stdout), + None) + self.mox.StubOutWithMock(address_scope.ListAddressScope, + "extend_list") + address_scope.ListAddressScope.extend_list(mox.IsA(list), + mox.IgnoreArg()) + self._test_list_resources_with_pagination("address_scopes", + cmd) + self.mox.VerifyAll() + self.mox.UnsetStubs() + + def test_list_address_scope_sort(self): + """sorted list: address_scope-list --sort-key name --sort-key id + --sort-key asc --sort-key desc + """ + resources = "address_scopes" + cmd = address_scope.ListAddressScope(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources(resources, cmd, + sort_key=["name", "id"], + sort_dir=["asc", "desc"]) + + def test_list_address_scope_limit(self): + """size (1000) limited list: address_scope-list -P.""" + resources = "address_scopes" + cmd = address_scope.ListAddressScope(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources(resources, cmd, page_size=1000) + + def test_show_address_scope(self): + """Show address_scope: --fields id --fields name myid.""" + resource = 'address_scope' + cmd = address_scope.ShowAddressScope( + test_cli20.MyApp(sys.stdout), None) + args = ['--fields', 'id', '--fields', 'name', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, args, + ['id', 'name']) + + def test_delete_address_scope(self): + """Delete address_scope: address_scope_id.""" + resource = 'address_scope' + cmd = address_scope.DeleteAddressScope( + test_cli20.MyApp(sys.stdout), None) + myid = 'myid' + args = [myid] + self._test_delete_resource(resource, cmd, myid, args) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index a38096b51..c77b99891 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -341,6 +341,8 @@ class Client(ClientBase): subnet_path = "/subnets/%s" subnetpools_path = "/subnetpools" subnetpool_path = "/subnetpools/%s" + address_scopes_path = "/address-scopes" + address_scope_path = "/address-scopes/%s" quotas_path = "/quotas" quota_path = "/quotas/%s" extensions_path = "/extensions" @@ -461,6 +463,7 @@ class Client(ClientBase): 'lbaas_members': 'lbaas_member', 'healthmonitors': 'healthmonitor', 'rbac_policies': 'rbac_policy', + 'address_scopes': 'address_scope', } @APIParamsCall @@ -662,6 +665,33 @@ def delete_router(self, router): """Deletes the specified router.""" return self.delete(self.router_path % (router)) + @APIParamsCall + def list_address_scopes(self, retrieve_all=True, **_params): + """Fetches a list of all address scopes for a tenant.""" + return self.list('address_scopes', self.address_scopes_path, + retrieve_all, **_params) + + @APIParamsCall + def show_address_scope(self, address_scope, **_params): + """Fetches information of a certain address scope.""" + return self.get(self.address_scope_path % (address_scope), + params=_params) + + @APIParamsCall + def create_address_scope(self, body=None): + """Creates a new address scope.""" + return self.post(self.address_scopes_path, body=body) + + @APIParamsCall + def update_address_scope(self, address_scope, body=None): + """Updates a address scope.""" + return self.put(self.address_scope_path % (address_scope), body=body) + + @APIParamsCall + def delete_address_scope(self, address_scope): + """Deletes the specified address scope.""" + return self.delete(self.address_scope_path % (address_scope)) + @APIParamsCall def add_interface_router(self, router, body=None): """Adds an internal network interface to the specified router.""" From de5d3bb5466e1137f10360214ed743c7622b2aeb Mon Sep 17 00:00:00 2001 From: Paul Michali Date: Tue, 18 Aug 2015 19:45:21 +0000 Subject: [PATCH 240/845] Create hooks for running functional test Create hook so that we can introduce a new job to test the VPN test cases using the VPN devstack plugin, and run other test cases w/o the plugin enabled. I2db939bf99288c0cdec06cdd49fec3bdc72e5253 adds the ability to use a gate hook and creates experimental jobs for both core and VPN tests, named gate-neutronclient-test-dsvm-functional and gate-neutronclient-dsvm-functional-vpn, respectively. A follow-up commit will move the VPN test cases, and modify tox.ini to support two jobs, one for VPN tests and one for other tests. Change-Id: Ieeaca1375d68705509f4e05f10cb35c0fa0b9582 Partial-Bug: 1484148 --- .../tests/functional/hooks/gate_hook.sh | 16 ++++++++++++++++ .../tests/functional/hooks/post_test_hook.sh | 3 ++- 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 neutronclient/tests/functional/hooks/gate_hook.sh diff --git a/neutronclient/tests/functional/hooks/gate_hook.sh b/neutronclient/tests/functional/hooks/gate_hook.sh new file mode 100644 index 000000000..4594c2249 --- /dev/null +++ b/neutronclient/tests/functional/hooks/gate_hook.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + + +set -ex + + +VENV=${1:-"functional"} + + +if [ "$VENV" == "functional-vpn" ] +then + export DEVSTACK_LOCAL_CONFIG="enable_plugin neutron-vpnaas git://git.openstack.org/openstack/neutron-vpnaas" +fi + +remaining_time +timeout -s 9 ${REMAINING_TIME}m $BASE/new/devstack-gate/devstack-vm-gate.sh diff --git a/neutronclient/tests/functional/hooks/post_test_hook.sh b/neutronclient/tests/functional/hooks/post_test_hook.sh index 68f906a24..0f96ec886 100755 --- a/neutronclient/tests/functional/hooks/post_test_hook.sh +++ b/neutronclient/tests/functional/hooks/post_test_hook.sh @@ -51,10 +51,11 @@ EOF cd $NEUTRONCLIENT_DIR # Run tests +VENV=${1:-"functional"} echo "Running neutronclient functional test suite" set +e # Preserve env for OS_ credentials -sudo -E -H -u jenkins tox -efunctional +sudo -E -H -u jenkins tox -e $VENV EXIT_CODE=$? set -e From 54e7b94012c54c9c33c5ed0e4043d0e5ee4f3005 Mon Sep 17 00:00:00 2001 From: gong yong sheng Date: Thu, 20 Aug 2015 11:35:36 +0800 Subject: [PATCH 241/845] Add document for entry point in setup.cfg In addition, to remove the contrib/ way to load commands since it is not implemented at all. Change-Id: Id15eb10e7cb086d55d2bcad4f719f9120c6f7d1a Closes-Bug:#1486824 --- doc/source/devref/client_command_extensions.rst | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/doc/source/devref/client_command_extensions.rst b/doc/source/devref/client_command_extensions.rst index 8be6245b5..10d39d815 100644 --- a/doc/source/devref/client_command_extensions.rst +++ b/doc/source/devref/client_command_extensions.rst @@ -29,12 +29,19 @@ Precedence of command loading ------------------------------ * hard coded commands are loaded first -* contributed commands (those in /contrib) -* external commands (installed in the environment) are loaded last +* external commands (installed in the environment) are loaded then Commands that have the same name will be overwritten by commands that are loaded later. To change the execution of a command for your particular extension you only need to override the execute method. Currently this extension support is limited to top-level resources. -Parent/child relationships may be added if desired. \ No newline at end of file +Parent/child relationships may be added if desired. + +neutronclient.extension entry_point +----------------------------------- +To activate the commands in a specific extension module, add an entry in +setup.cfg under neutronclient.extension. For example: +[entry_points] +neutronclient.extension = + fox_sockets = neutronclient.neutron.v2_0.contrib._fox_sockets \ No newline at end of file From f6ca3a1fd382c6e0c03bd226bf411abffb977b6c Mon Sep 17 00:00:00 2001 From: mohankumar_n Date: Wed, 19 Aug 2015 18:49:54 +0530 Subject: [PATCH 242/845] Adding registration interface for non_admin_status_resources By default, admin_state_up is set to True in _test_create_resource. If a resource doesn't need to package admin_state_up then it's resource name should be mentioned in "non_admin_status_resources" explicitly. The current logic doesn't work for the extension project/s which are developed outside the tree but depends upon the existing test framework for running their unit test-cases. To address this issue, the patch adds a new registration interface for non_admin_status_resources using which one can add their resource to non_admin_status_resources and avoid admin_state_up packaging. Change-Id: I2abf3bd038f7e444818bfebfed37713ba536f1b5 --- neutronclient/tests/unit/test_cli20.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index efee1ae27..448a48ab0 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -15,6 +15,7 @@ # import contextlib +import copy import itertools import sys @@ -38,6 +39,16 @@ TOKEN = 'testtoken' ENDURL = 'localurl' +non_admin_status_resources = ['subnet', 'floatingip', 'security_group', + 'security_group_rule', 'qos_queue', + 'network_gateway', 'gateway_device', + 'credential', 'network_profile', + 'policy_profile', 'ikepolicy', + 'ipsecpolicy', 'metering_label', + 'metering_label_rule', 'net_partition', + 'fox_socket', 'subnetpool', + 'rbac_policy', 'address_scope'] + @contextlib.contextmanager def capture_std_streams(): @@ -186,6 +197,7 @@ def setUp(self, plurals=None): """Prepare the test environment.""" super(CLITestV20Base, self).setUp() client.Client.EXTED_PLURALS.update(constants.PLURALS) + self.non_admin_status_resources = copy.copy(non_admin_status_resources) if plurals is not None: client.Client.EXTED_PLURALS.update(plurals) self.metadata = {'plurals': client.Client.EXTED_PLURALS, @@ -207,6 +219,9 @@ def setUp(self, plurals=None): self._get_attr_metadata)) self.client = client.Client(token=TOKEN, endpoint_url=self.endurl) + def register_non_admin_status_resource(self, resource_name): + self.non_admin_status_resources.append(resource_name) + def _test_create_resource(self, resource, cmd, name, myid, args, position_names, position_values, tenant_id=None, tags=None, admin_state_up=True, @@ -217,18 +232,9 @@ def _test_create_resource(self, resource, cmd, name, myid, args, self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") cmd.get_client().MultipleTimes().AndReturn(self.client) - non_admin_status_resources = ['subnet', 'floatingip', 'security_group', - 'security_group_rule', 'qos_queue', - 'network_gateway', 'gateway_device', - 'credential', 'network_profile', - 'policy_profile', 'ikepolicy', - 'ipsecpolicy', 'metering_label', - 'metering_label_rule', 'net_partition', - 'fox_socket', 'subnetpool', - 'rbac_policy', 'address_scope'] if not cmd_resource: cmd_resource = resource - if (resource in non_admin_status_resources): + if (resource in self.non_admin_status_resources): body = {resource: {}, } else: body = {resource: {'admin_state_up': admin_state_up, }, } From 45ed3ecd30a455578662408f14270922cda38b76 Mon Sep 17 00:00:00 2001 From: gong yong sheng Date: Thu, 20 Aug 2015 13:52:50 +0800 Subject: [PATCH 243/845] Add extension name to extension's command help text line Currently, the cliff does not support to group commands. A good way to differentiate the commands from extension modules is to prefix the commands with extension name. Change-Id: Ib16404c6098340ca90e9548134d4115f5abbf5f6 Closes-Bug:#1486829 --- neutronclient/neutron/v2_0/contrib/_fox_sockets.py | 12 ++++++++++++ neutronclient/shell.py | 7 +++++-- neutronclient/tests/unit/test_client_extension.py | 12 ++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/neutronclient/neutron/v2_0/contrib/_fox_sockets.py b/neutronclient/neutron/v2_0/contrib/_fox_sockets.py index 63996981b..740043537 100644 --- a/neutronclient/neutron/v2_0/contrib/_fox_sockets.py +++ b/neutronclient/neutron/v2_0/contrib/_fox_sockets.py @@ -31,6 +31,8 @@ def _updatable_args2body(parsed_args, body, client): class FoxInSocket(extension.NeutronClientExtension): + """Define required variables for resource operations.""" + resource = 'fox_socket' resource_plural = '%ss' % resource object_path = '/%s' % resource_plural @@ -39,6 +41,8 @@ class FoxInSocket(extension.NeutronClientExtension): class FoxInSocketsList(extension.ClientExtensionList, FoxInSocket): + """List fox sockets.""" + shell_command = 'fox-sockets-list' list_columns = ['id', 'name'] pagination_support = True @@ -46,6 +50,8 @@ class FoxInSocketsList(extension.ClientExtensionList, FoxInSocket): class FoxInSocketsCreate(extension.ClientExtensionCreate, FoxInSocket): + """Create a fox socket.""" + shell_command = 'fox-sockets-create' list_columns = ['id', 'name'] @@ -61,6 +67,8 @@ def args2body(self, parsed_args): class FoxInSocketsUpdate(extension.ClientExtensionUpdate, FoxInSocket): + """Update a fox socket.""" + shell_command = 'fox-sockets-update' list_columns = ['id', 'name'] @@ -78,8 +86,12 @@ def args2body(self, parsed_args): class FoxInSocketsDelete(extension.ClientExtensionDelete, FoxInSocket): + """Delete a fox socket.""" + shell_command = 'fox-sockets-delete' class FoxInSocketsShow(extension.ClientExtensionShow, FoxInSocket): + """Show a fox socket.""" + shell_command = 'fox-sockets-show' diff --git a/neutronclient/shell.py b/neutronclient/shell.py index d40c77a86..7f80404d2 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -716,9 +716,9 @@ def _bash_completion(self): def _register_extensions(self, version): for name, module in itertools.chain( client_extension._discover_via_entry_points()): - self._extend_shell_commands(module, version) + self._extend_shell_commands(name, module, version) - def _extend_shell_commands(self, module, version): + def _extend_shell_commands(self, name, module, version): classes = inspect.getmembers(module, inspect.isclass) for cls_name, cls in classes: if (issubclass(cls, client_extension.NeutronClientExtension) and @@ -728,6 +728,9 @@ def _extend_shell_commands(self, module, version): if version not in cls.versions: continue try: + name_prefix = "[%s]" % name + cls.__doc__ = ("%s %s" % (name_prefix, cls.__doc__) if + cls.__doc__ else name_prefix) self.command_manager.add_command(cmd, cls) self.commands[version][cmd] = cls except TypeError: diff --git a/neutronclient/tests/unit/test_client_extension.py b/neutronclient/tests/unit/test_client_extension.py index 0e4b3a8c0..4b64d4bd7 100644 --- a/neutronclient/tests/unit/test_client_extension.py +++ b/neutronclient/tests/unit/test_client_extension.py @@ -51,6 +51,18 @@ def test_ext_cmd_loaded(self): 'fox-sockets-show': fox_sockets.FoxInSocketsShow} self.assertDictContainsSubset(ext_cmd, shell.COMMANDS['2.0']) + def test_ext_cmd_help_doc_with_extension_name(self): + shell.NeutronShell('2.0') + ext_cmd = {'fox-sockets-list': fox_sockets.FoxInSocketsList, + 'fox-sockets-create': fox_sockets.FoxInSocketsCreate, + 'fox-sockets-update': fox_sockets.FoxInSocketsUpdate, + 'fox-sockets-delete': fox_sockets.FoxInSocketsDelete, + 'fox-sockets-show': fox_sockets.FoxInSocketsShow} + self.assertDictContainsSubset(ext_cmd, shell.COMMANDS['2.0']) + for item in ext_cmd: + cmdcls = shell.COMMANDS['2.0'].get(item) + self.assertTrue(cmdcls.__doc__.startswith("[_fox_sockets]")) + def test_delete_fox_socket(self): """Delete fox socket: myid.""" resource = 'fox_socket' From abc2b65fdad5095dc9c5aa9ec19ff26c61fe1020 Mon Sep 17 00:00:00 2001 From: Carl Baldwin Date: Wed, 22 Jul 2015 19:36:32 +0000 Subject: [PATCH 244/845] Fix find_resourceid_by_name call for address scopes This slipped through here [1]. [1] https://review.openstack.org/#/c/196472/ Change-Id: I13ea894c1494ec2e73af1630bbabd1c65428c58c Partially-implements: blueprint address-scopes --- neutronclient/neutron/v2_0/subnetpool.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neutronclient/neutron/v2_0/subnetpool.py b/neutronclient/neutron/v2_0/subnetpool.py index 0631a1986..e51874ed2 100644 --- a/neutronclient/neutron/v2_0/subnetpool.py +++ b/neutronclient/neutron/v2_0/subnetpool.py @@ -86,7 +86,7 @@ def args2body(self, parsed_args): # Parse and update for "address-scope" option if parsed_args.address_scope: _addrscope_id = neutronV20.find_resourceid_by_name_or_id( - self.get_client(), 'address-scope', + self.get_client(), 'address_scope', parsed_args.address_scope) body['subnetpool']['address_scope_id'] = _addrscope_id return body @@ -128,7 +128,7 @@ def args2body(self, parsed_args): body['subnetpool']['address_scope_id'] = None elif parsed_args.address_scope: _addrscope_id = neutronV20.find_resourceid_by_name_or_id( - self.get_client(), 'address-scope', + self.get_client(), 'address_scope', parsed_args.address_scope) body['subnetpool']['address_scope_id'] = _addrscope_id return body From c44b57f3f5eed2e22ec556268347cb39b799c50b Mon Sep 17 00:00:00 2001 From: Carl Baldwin Date: Wed, 22 Jul 2015 19:36:32 +0000 Subject: [PATCH 245/845] Make subnetpool-list show correct address scope column Change-Id: If07f445443c3b693f90546b93a1bdf032c11ad25 Partially-implements: blueprint address-scopes --- neutronclient/neutron/v2_0/subnetpool.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/neutron/v2_0/subnetpool.py b/neutronclient/neutron/v2_0/subnetpool.py index e51874ed2..bc0bdf0d4 100644 --- a/neutronclient/neutron/v2_0/subnetpool.py +++ b/neutronclient/neutron/v2_0/subnetpool.py @@ -45,7 +45,7 @@ class ListSubnetPool(neutronV20.ListCommand): resource = 'subnetpool' list_columns = ['id', 'name', 'prefixes', - 'default_prefixlen', 'address-scope'] + 'default_prefixlen', 'address_scope_id'] pagination_support = True sorting_support = True From bb7124e8cacab54fc193475fbae1a6854120e092 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Sun, 23 Aug 2015 05:28:30 +0000 Subject: [PATCH 246/845] Updated from global requirements Change-Id: Idfe08c62bff1ba159f3c97d305f4322b1f0c205f --- requirements.txt | 6 +++--- test-requirements.txt | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/requirements.txt b/requirements.txt index 5bc7d2d35..2dcc12fb0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,14 +1,14 @@ # The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. -pbr<2.0,>=1.3 +pbr<2.0,>=1.4 argparse -cliff>=1.13.0 # Apache-2.0 +cliff>=1.14.0 # Apache-2.0 iso8601>=0.1.9 netaddr>=0.7.12 oslo.i18n>=1.5.0 # Apache-2.0 oslo.serialization>=1.4.0 # Apache-2.0 -oslo.utils>=1.9.0 # Apache-2.0 +oslo.utils>=2.0.0 # Apache-2.0 requests>=2.5.2 python-keystoneclient>=1.6.0 simplejson>=2.2.0 diff --git a/test-requirements.txt b/test-requirements.txt index 8392ca18f..174934641 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -8,10 +8,9 @@ coverage>=3.6 discover fixtures>=1.3.1 mox3>=0.7.0 -mock!=1.1.4,>=1.1;python_version!='2.6' -mock==1.0.1;python_version=='2.6' +mock>=1.2 oslosphinx>=2.5.0 # Apache-2.0 -oslotest>=1.7.0 # Apache-2.0 +oslotest>=1.10.0 # Apache-2.0 python-subunit>=0.0.18 requests-mock>=0.6.0 # Apache-2.0 sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 From a174215491ffe26fdceca3675e1a0adb91e629f1 Mon Sep 17 00:00:00 2001 From: gong yong sheng Date: Thu, 20 Aug 2015 14:55:39 +0800 Subject: [PATCH 247/845] Clear the extension requirement Change-Id: If0725e009dfb98fe949b9e00349108c88cf4424a Closes-bug: #1486845 --- .../devref/client_command_extensions.rst | 50 +++++++++++++++---- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/doc/source/devref/client_command_extensions.rst b/doc/source/devref/client_command_extensions.rst index 10d39d815..b89078eb3 100644 --- a/doc/source/devref/client_command_extensions.rst +++ b/doc/source/devref/client_command_extensions.rst @@ -12,18 +12,45 @@ neutronclient/neutron/v2_0/contrib/_fox_sockets.py Minimum requirements from an extension -------------------------------------- -* Will have a class that subclasses NeutronClientExtension to provide the - requisite version support, paths, and variable names for the client. - Example: neutronclient.neutron.v2_0.contrib._fox_sockets.FoxInSocket +* NeutronClientExtension subclasses must have a shell_command class variable + if the command is to be available to the CLI (shell.py) -* Will have at least one class that subclasses from the ClientExtension - classes to provide the new functionality to the client Example: neutronclient.neutron.v2_0.contrib._fox_sockets.FoxInSocketsList -* ClientExtension subclasses must have a shell_command class variable if the - command is to be available to the CLI (shell.py) +Minimum requirements to use canonical neutron CRUD commands framework +---------------------------------------------------------------------- + +Neutron commands are cliff commands, commands in extension can use their +own way to finish their tasks. But if they want to make use of the canonical +neutron CRUD commands framework, the extension should: + +* have a class that subclasses NeutronClientExtension to provide the + requisite resource name, version support, and resource collection and + object paths for a resource the commands will process. + + Example: neutronclient.neutron.v2_0.contrib._fox_sockets.FoxInSocket + +* have a class that subclasses from the ClientExtensionList to provide + resource object list function. This is because most commands + need the list function to get object ID via + neutronclient.neutron.v2_0.__init__.find_resource_by_id. + Example: neutronclient.neutron.v2_0.contrib._fox_sockets.FoxInSocketsList +* if needed, subclass ClientExtensionUpdate to implement update of the resource + object. + + Example: neutronclient.neutron.v2_0.contrib._fox_sockets.FoxInSocketsUpdate + +* if needed, subclass ClientExtensionDelete to implement deletion of the resource + object. + + Example: neutronclient.neutron.v2_0.contrib._fox_sockets.FoxInSocketsDelete + +* if needed, subclass ClientExtensionShow to get the detail of the resource + object. + + Example: neutronclient.neutron.v2_0.contrib._fox_sockets.FoxInSocketsShow Precedence of command loading ------------------------------ @@ -40,8 +67,9 @@ Parent/child relationships may be added if desired. neutronclient.extension entry_point ----------------------------------- + To activate the commands in a specific extension module, add an entry in -setup.cfg under neutronclient.extension. For example: -[entry_points] -neutronclient.extension = - fox_sockets = neutronclient.neutron.v2_0.contrib._fox_sockets \ No newline at end of file +setup.cfg under neutronclient.extension. For example:: + [entry_points] + neutronclient.extension = + fox_sockets = neutronclient.neutron.v2_0.contrib._fox_sockets From 002a0c73759d86d3b93d9d53ce5f832a1bb434b3 Mon Sep 17 00:00:00 2001 From: Ramanjaneya Date: Tue, 7 Jul 2015 18:40:49 +0530 Subject: [PATCH 248/845] Support QoS neutron-client (1/2). This patch adds the command line support for 1. QoS policy 2. QoS policy association with the neutron network 3. QoS policy association with the neutron port Note: Neutronclient support for QoS rule-types will be addressed via a separate patch (https://review.openstack.org/#/c/198277/) DocImpact APIImpact Partially-Implements: blueprint quantum-qos-api Co-Authored-By: Ramanjaneya Reddy Palleti Co-Authored-By: John Schwarz Co-Authored-By: vikram.choudhary Change-Id: If7460516a3b3e3ccb91f2e7becfe7335ff809bda --- neutronclient/neutron/v2_0/network.py | 18 +- neutronclient/neutron/v2_0/port.py | 11 +- neutronclient/neutron/v2_0/qos/__init__.py | 0 neutronclient/neutron/v2_0/qos/policy.py | 140 +++++++++++++++ neutronclient/shell.py | 6 + neutronclient/tests/unit/qos/__init__.py | 0 .../tests/unit/qos/test_cli20_policy.py | 160 ++++++++++++++++++ neutronclient/tests/unit/test_cli20.py | 3 +- .../tests/unit/test_cli20_network.py | 29 ++++ neutronclient/tests/unit/test_cli20_port.py | 30 ++++ neutronclient/v2_0/client.py | 33 ++++ 11 files changed, 425 insertions(+), 5 deletions(-) create mode 100755 neutronclient/neutron/v2_0/qos/__init__.py create mode 100755 neutronclient/neutron/v2_0/qos/policy.py create mode 100755 neutronclient/tests/unit/qos/__init__.py create mode 100755 neutronclient/tests/unit/qos/test_cli20_policy.py diff --git a/neutronclient/neutron/v2_0/network.py b/neutronclient/neutron/v2_0/network.py index 8884c7967..8dc963cd4 100644 --- a/neutronclient/neutron/v2_0/network.py +++ b/neutronclient/neutron/v2_0/network.py @@ -20,6 +20,7 @@ from neutronclient.common import utils from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 +from neutronclient.neutron.v2_0.qos import policy as qos_policy def _format_subnets(network): @@ -101,7 +102,7 @@ class ShowNetwork(neutronV20.ShowCommand): resource = 'network' -class CreateNetwork(neutronV20.CreateCommand): +class CreateNetwork(neutronV20.CreateCommand, qos_policy.CreateQosPolicyMixin): """Create a network for a given tenant.""" resource = 'network' @@ -144,6 +145,8 @@ def add_known_arguments(self, parser): 'name', metavar='NAME', help=_('Name of network to create.')) + self.add_arguments_qos_policy(parser) + def args2body(self, parsed_args): body = {'network': { 'name': parsed_args.name, @@ -154,6 +157,9 @@ def args2body(self, parsed_args): 'provider:network_type', 'provider:physical_network', 'provider:segmentation_id']) + + self.args2body_qos_policy(parsed_args, body['network']) + return body @@ -163,7 +169,15 @@ class DeleteNetwork(neutronV20.DeleteCommand): resource = 'network' -class UpdateNetwork(neutronV20.UpdateCommand): +class UpdateNetwork(neutronV20.UpdateCommand, qos_policy.UpdateQosPolicyMixin): """Update network's information.""" resource = 'network' + + def add_known_arguments(self, parser): + self.add_arguments_qos_policy(parser) + + def args2body(self, parsed_args): + body = {'network': {}} + self.args2body_qos_policy(parsed_args, body['network']) + return body diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index a983317dc..42d671473 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -22,6 +22,7 @@ from neutronclient.common import utils from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 +from neutronclient.neutron.v2_0.qos import policy as qos_policy def _format_fixed_ips(port): @@ -193,7 +194,7 @@ def args2body_extradhcpopt(self, parsed_args, port): class CreatePort(neutronV20.CreateCommand, UpdatePortSecGroupMixin, - UpdateExtraDhcpOptMixin): + UpdateExtraDhcpOptMixin, qos_policy.CreateQosPolicyMixin): """Create a port for a given tenant.""" resource = 'port' @@ -230,6 +231,7 @@ def add_known_arguments(self, parser): help=argparse.SUPPRESS) self.add_arguments_secgroup(parser) self.add_arguments_extradhcpopt(parser) + self.add_arguments_qos_policy(parser) parser.add_argument( 'network_id', metavar='NETWORK', @@ -254,6 +256,7 @@ def args2body(self, parsed_args): self.args2body_secgroup(parsed_args, body['port']) self.args2body_extradhcpopt(parsed_args, body['port']) + self.args2body_qos_policy(parsed_args, body['port']) return body @@ -265,7 +268,7 @@ class DeletePort(neutronV20.DeleteCommand): class UpdatePort(neutronV20.UpdateCommand, UpdatePortSecGroupMixin, - UpdateExtraDhcpOptMixin): + UpdateExtraDhcpOptMixin, qos_policy.UpdateQosPolicyMixin): """Update port's information.""" resource = 'port' @@ -282,6 +285,7 @@ def add_known_arguments(self, parser): help=argparse.SUPPRESS) self.add_arguments_secgroup(parser) self.add_arguments_extradhcpopt(parser) + self.add_arguments_qos_policy(parser) def args2body(self, parsed_args): body = {'port': {}} @@ -290,6 +294,9 @@ def args2body(self, parsed_args): if parsed_args.admin_state_up: body['port'].update({'admin_state_up': parsed_args.admin_state_up}) + self.args2body_secgroup(parsed_args, body['port']) self.args2body_extradhcpopt(parsed_args, body['port']) + self.args2body_qos_policy(parsed_args, body['port']) + return body diff --git a/neutronclient/neutron/v2_0/qos/__init__.py b/neutronclient/neutron/v2_0/qos/__init__.py new file mode 100755 index 000000000..e69de29bb diff --git a/neutronclient/neutron/v2_0/qos/policy.py b/neutronclient/neutron/v2_0/qos/policy.py new file mode 100755 index 000000000..fd6ffb143 --- /dev/null +++ b/neutronclient/neutron/v2_0/qos/policy.py @@ -0,0 +1,140 @@ +# Copyright 2015 Huawei Technologies India Pvt Ltd, 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. +# + +from neutronclient.i18n import _ +from neutronclient.neutron import v2_0 as neutronv20 + + +def get_qos_policy_id(client, policy_id_or_name): + _policy_id = neutronv20.find_resourceid_by_name_or_id( + client, 'policy', policy_id_or_name, cmd_resource='qos_policy') + return _policy_id + + +class CreateQosPolicyMixin(object): + def add_arguments_qos_policy(self, parser): + qos_policy_args = parser.add_mutually_exclusive_group() + qos_policy_args.add_argument( + '--qos-policy', + help=_('Attach QoS policy ID or name to the resource.')) + return qos_policy_args + + def args2body_qos_policy(self, parsed_args, resource): + if parsed_args.qos_policy: + _policy_id = get_qos_policy_id(self.get_client(), + parsed_args.qos_policy) + resource['qos_policy_id'] = _policy_id + + +class UpdateQosPolicyMixin(CreateQosPolicyMixin): + def add_arguments_qos_policy(self, parser): + qos_policy_args = (super(UpdateQosPolicyMixin, self). + add_arguments_qos_policy(parser)) + qos_policy_args.add_argument( + '--no-qos-policy', + action='store_true', + help=_('Detach QoS policy from the resource.')) + return qos_policy_args + + def args2body_qos_policy(self, parsed_args, resource): + super(UpdateQosPolicyMixin, self).args2body_qos_policy(parsed_args, + resource) + if parsed_args.no_qos_policy: + resource['qos_policy_id'] = None + + +class ListQoSPolicy(neutronv20.ListCommand): + """List QoS policies that belong to a given tenant connection.""" + + resource = 'policy' + shadow_resource = 'qos_policy' + list_columns = ['id', 'name'] + pagination_support = True + sorting_support = True + + +class ShowQoSPolicy(neutronv20.ShowCommand): + """Show information of a given qos policy.""" + + resource = 'policy' + shadow_resource = 'qos_policy' + + +class CreateQoSPolicy(neutronv20.CreateCommand): + """Create a qos policy.""" + + resource = 'policy' + shadow_resource = 'qos_policy' + + def add_known_arguments(self, parser): + parser.add_argument( + 'name', metavar='NAME', + help=_('Name of QoS policy to create.')) + parser.add_argument( + '--description', + help=_('Description of the QoS policy.')) + parser.add_argument( + '--shared', + action='store_true', + help=_('Accessible by other tenants. ' + 'Set shared to True (default is False).')) + + def args2body(self, parsed_args): + body = {self.resource: {'name': parsed_args.name}, } + if parsed_args.description: + body[self.resource]['description'] = parsed_args.description + if parsed_args.shared: + body[self.resource]['shared'] = parsed_args.shared + if parsed_args.tenant_id: + body[self.resource]['tenant_id'] = parsed_args.tenant_id + return body + + +class UpdateQoSPolicy(neutronv20.UpdateCommand): + """Update a given qos policy.""" + + resource = 'policy' + shadow_resource = 'qos_policy' + + def add_known_arguments(self, parser): + parser.add_argument( + '--name', + help=_('Name of QoS policy.')) + parser.add_argument( + '--description', + help=_('Description of the QoS policy.')) + parser.add_argument( + '--shared', + action='store_true', + help=_('Accessible by other tenants. ' + 'Set shared to True (default is False).')) + + def args2body(self, parsed_args): + body = {self.resource: {}, } + if parsed_args.name: + body[self.resource]['name'] = parsed_args.name + if parsed_args.description: + body[self.resource]['description'] = parsed_args.description + if parsed_args.shared: + body[self.resource]['shared'] = parsed_args.shared + return body + + +class DeleteQoSPolicy(neutronv20.DeleteCommand): + """Delete a given qos policy.""" + + resource = 'policy' + shadow_resource = 'qos_policy' diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 7f80404d2..8554b2805 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -72,6 +72,7 @@ from neutronclient.neutron.v2_0.nsx import qos_queue from neutronclient.neutron.v2_0 import policyprofile from neutronclient.neutron.v2_0 import port +from neutronclient.neutron.v2_0.qos import policy as qos_policy from neutronclient.neutron.v2_0 import quota from neutronclient.neutron.v2_0 import rbac from neutronclient.neutron.v2_0 import router @@ -372,6 +373,11 @@ class BashCompletionCommand(openstack_command.OpenStackCommand): 'address-scope-create': address_scope.CreateAddressScope, 'address-scope-delete': address_scope.DeleteAddressScope, 'address-scope-update': address_scope.UpdateAddressScope, + 'qos-policy-list': qos_policy.ListQoSPolicy, + 'qos-policy-show': qos_policy.ShowQoSPolicy, + 'qos-policy-create': qos_policy.CreateQoSPolicy, + 'qos-policy-update': qos_policy.UpdateQoSPolicy, + 'qos-policy-delete': qos_policy.DeleteQoSPolicy, } COMMANDS = {'2.0': COMMAND_V2} diff --git a/neutronclient/tests/unit/qos/__init__.py b/neutronclient/tests/unit/qos/__init__.py new file mode 100755 index 000000000..e69de29bb diff --git a/neutronclient/tests/unit/qos/test_cli20_policy.py b/neutronclient/tests/unit/qos/test_cli20_policy.py new file mode 100755 index 000000000..9e4bf6d0e --- /dev/null +++ b/neutronclient/tests/unit/qos/test_cli20_policy.py @@ -0,0 +1,160 @@ +# Copyright 2015 Huawei Technologies India Pvt Ltd. +# 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 sys + +from neutronclient.neutron.v2_0.qos import policy as policy +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20QoSPolicyJSON(test_cli20.CLITestV20Base): + def setUp(self): + super(CLITestV20QoSPolicyJSON, self).setUp() + self.res = 'policy' + self.cmd_res = 'qos_policy' + self.ress = "policies" + self.cmd_ress = 'qos_policies' + + def test_create_policy_with_only_keyattributes(self): + """Create qos policy abc.""" + cmd = policy.CreateQoSPolicy(test_cli20.MyApp(sys.stdout), + None) + myid = 'myid' + name = 'abc' + args = [name] + position_names = ['name'] + position_values = [name] + self._test_create_resource(self.res, cmd, name, myid, args, + position_names, position_values, + cmd_resource=self.cmd_res) + + def test_create_policy_with_description(self): + """Create qos policy xyz --description abc.""" + cmd = policy.CreateQoSPolicy(test_cli20.MyApp(sys.stdout), + None) + myid = 'myid' + name = 'abc' + description = 'policy_abc' + args = [name, '--description', description] + position_names = ['name', 'description'] + position_values = [name, description] + self._test_create_resource(self.res, cmd, name, myid, args, + position_names, position_values, + cmd_resource=self.cmd_res) + + def test_create_policy_with_shared(self): + """Create qos policy abc shared across tenants""" + cmd = policy.CreateQoSPolicy(test_cli20.MyApp(sys.stdout), + None) + myid = 'myid' + name = 'abc' + description = 'policy_abc' + args = [name, '--description', description, '--shared'] + position_names = ['name', 'description', 'shared'] + position_values = [name, description, True] + self._test_create_resource(self.res, cmd, name, myid, args, + position_names, position_values, + cmd_resource=self.cmd_res) + + def test_create_policy_with_unicode(self): + """Create qos policy u'\u7f51\u7edc'.""" + cmd = policy.CreateQoSPolicy(test_cli20.MyApp(sys.stdout), + None) + myid = 'myid' + name = u'\u7f51\u7edc' + description = u'\u7f51\u7edc' + args = [name, '--description', description] + position_names = ['name', 'description'] + position_values = [name, description] + self._test_create_resource(self.res, cmd, name, myid, args, + position_names, position_values, + cmd_resource=self.cmd_res) + + def test_update_policy(self): + """policy-update myid --name newname.""" + cmd = policy.UpdateQoSPolicy(test_cli20.MyApp(sys.stdout), + None) + self._test_update_resource(self.res, cmd, 'myid', + ['myid', '--name', 'newname'], + {'name': 'newname', }, + cmd_resource=self.cmd_res) + + def test_update_policy_description(self): + """policy-update myid --name newname --description newdesc""" + cmd = policy.UpdateQoSPolicy(test_cli20.MyApp(sys.stdout), + None) + self._test_update_resource(self.res, cmd, 'myid', + ['myid', '--description', 'newdesc'], + {'description': 'newdesc', }, + cmd_resource=self.cmd_res) + + def test_list_policies(self): + """qos-policy-list.""" + cmd = policy.ListQoSPolicy(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources(self.ress, cmd, True, + cmd_resources=self.cmd_ress) + + def test_list_policies_pagination(self): + """qos-policy-list for pagination.""" + cmd = policy.ListQoSPolicy(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources_with_pagination(self.ress, cmd, + cmd_resources=self.cmd_ress) + + def test_list_policies_sort(self): + """sorted list: qos-policy-list --sort-key name --sort-key id + --sort-key asc --sort-key desc + """ + cmd = policy.ListQoSPolicy(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources(self.ress, cmd, + sort_key=["name", "id"], + sort_dir=["asc", "desc"], + cmd_resources=self.cmd_ress) + + def test_list_policies_limit(self): + """size (1000) limited list: qos-policy-list -P.""" + cmd = policy.ListQoSPolicy(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources(self.ress, cmd, page_size=1000, + cmd_resources=self.cmd_ress) + + def test_show_policy_id(self): + """qos-policy-show test_id.""" + cmd = policy.ShowQoSPolicy(test_cli20.MyApp(sys.stdout), + None) + args = ['--fields', 'id', self.test_id] + self._test_show_resource(self.res, cmd, self.test_id, args, + ['id'], cmd_resource=self.cmd_res) + + def test_show_policy_name(self): + """qos-policy-show.""" + cmd = policy.ShowQoSPolicy(test_cli20.MyApp(sys.stdout), + None) + args = ['--fields', 'id', '--fields', 'name', self.test_id] + self._test_show_resource(self.res, cmd, self.test_id, + args, ['id', 'name'], + cmd_resource=self.cmd_res) + + def test_delete_policy(self): + """qos-policy-delete my-id.""" + cmd = policy.DeleteQoSPolicy(test_cli20.MyApp(sys.stdout), + None) + my_id = 'myid1' + args = [my_id] + self._test_delete_resource(self.res, cmd, my_id, args, + cmd_resource=self.cmd_res) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 448a48ab0..9e09d7a0b 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -47,7 +47,8 @@ 'ipsecpolicy', 'metering_label', 'metering_label_rule', 'net_partition', 'fox_socket', 'subnetpool', - 'rbac_policy', 'address_scope'] + 'rbac_policy', 'address_scope', + 'policy'] @contextlib.contextmanager diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index 2d6edc563..2f10b801a 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -136,6 +136,19 @@ def test_create_network_vlan_transparent(self): position_names, position_values, **vlantrans) + def test_create_network_with_qos_policy(self): + """Create net: --qos-policy mypolicy.""" + resource = 'network' + cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + qos_policy_name = 'mypolicy' + args = [name, '--qos-policy', qos_policy_name] + position_names = ['name', 'qos_policy_id'] + position_values = [name, qos_policy_name] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + def test_list_nets_empty_with_column(self): resources = "networks" cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) @@ -486,6 +499,22 @@ def test_update_network_with_unicode(self): 'tags': ['a', 'b'], } ) + def test_update_network_with_qos_policy(self): + """Update net: myid --qos-policy mypolicy.""" + resource = 'network' + cmd = network.UpdateNetwork(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--qos-policy', 'mypolicy'], + {'qos_policy_id': 'mypolicy', }) + + def test_update_network_with_no_qos_policy(self): + """Update net: myid --no-qos-policy.""" + resource = 'network' + cmd = network.UpdateNetwork(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--no-qos-policy'], + {'qos_policy_id': None, }) + def test_show_network(self): """Show net: --fields id --fields name myid.""" resource = 'network' diff --git a/neutronclient/tests/unit/test_cli20_port.py b/neutronclient/tests/unit/test_cli20_port.py index 76eb1057c..7717753eb 100644 --- a/neutronclient/tests/unit/test_cli20_port.py +++ b/neutronclient/tests/unit/test_cli20_port.py @@ -288,6 +288,20 @@ def test_create_port_secgroups_list(self): self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) + def test_create_port_with_qos_policy(self): + """Create port: --qos-policy mypolicy.""" + resource = 'port' + cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + netid = 'netid' + qos_policy_name = 'mypolicy' + args = [netid, '--qos-policy', qos_policy_name] + position_names = ['network_id', 'qos_policy_id'] + position_values = [netid, qos_policy_name] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + def test_list_ports(self): """List ports: -D.""" resources = "ports" @@ -539,6 +553,22 @@ def test_update_port_extra_dhcp_opts_ip_version(self): cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, myid, args, updatedfields) + def test_update_port_with_qos_policy(self): + """Update port: myid --qos-policy mypolicy.""" + resource = 'port' + cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--qos-policy', 'mypolicy'], + {'qos_policy_id': 'mypolicy', }) + + def test_update_port_with_no_qos_policy(self): + """Update port: myid --no-qos-policy.""" + resource = 'port' + cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--no-qos-policy'], + {'qos_policy_id': None, }) + def test_delete_extra_dhcp_opts_from_port(self): resource = 'port' myid = 'myid' diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index c77b99891..e521420b5 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -431,6 +431,8 @@ class Client(ClientBase): net_partition_path = "/net-partitions/%s" rbac_policies_path = "/rbac-policies" rbac_policy_path = "/rbac-policies/%s" + qos_policies_path = "/qos/policies" + qos_policy_path = "/qos/policies/%s" # API has no way to report plurals, so we have to hard code them EXTED_PLURALS = {'routers': 'router', @@ -464,6 +466,8 @@ class Client(ClientBase): 'healthmonitors': 'healthmonitor', 'rbac_policies': 'rbac_policy', 'address_scopes': 'address_scope', + 'qos_policies': 'qos_policy', + 'policies': 'policy', } @APIParamsCall @@ -1660,6 +1664,35 @@ def delete_rbac_policy(self, rbac_policy_id): """Delete the specified RBAC policy.""" return self.delete(self.rbac_policy_path % rbac_policy_id) + @APIParamsCall + def list_qos_policies(self, retrieve_all=True, **_params): + """Fetches a list of all qos policies for a tenant.""" + # Pass filters in "params" argument to do_request + return self.list('policies', self.qos_policies_path, + retrieve_all, **_params) + + @APIParamsCall + def show_qos_policy(self, qos_policy, **_params): + """Fetches information of a certain qos policy.""" + return self.get(self.qos_policy_path % qos_policy, + params=_params) + + @APIParamsCall + def create_qos_policy(self, body=None): + """Creates a new qos policy.""" + return self.post(self.qos_policies_path, body=body) + + @APIParamsCall + def update_qos_policy(self, qos_policy, body=None): + """Updates a qos policy.""" + return self.put(self.qos_policy_path % qos_policy, + body=body) + + @APIParamsCall + def delete_qos_policy(self, qos_policy): + """Deletes the specified qos policy.""" + return self.delete(self.qos_policy_path % qos_policy) + def __init__(self, **kwargs): """Initialize a new client for the Neutron v2.0 API.""" super(Client, self).__init__(**kwargs) From 31df9de28fe089af0c492b6becca5eb3d8f2676d Mon Sep 17 00:00:00 2001 From: Ramanjaneya Date: Tue, 7 Jul 2015 18:59:28 +0530 Subject: [PATCH 249/845] Support CLI changes for QoS (2/2). This patch adds the command line support for QoS rules. DocImpact APIImpact Partially-Implements: blueprint quantum-qos-api Change-Id: Iea4dfa742f8005057f6caee8ab734649c0262d75 Co-Authored-By: vikram.choudhary Co-Authored-By: John Schwarz --- .../neutron/v2_0/qos/bandwidth_limit_rule.py | 107 +++++++++++++ neutronclient/neutron/v2_0/qos/policy.py | 10 ++ neutronclient/neutron/v2_0/qos/rule.py | 58 +++++++ neutronclient/shell.py | 18 +++ .../tests/unit/qos/test_cli20_rule.py | 146 ++++++++++++++++++ neutronclient/tests/unit/test_cli20.py | 2 +- neutronclient/v2_0/client.py | 44 ++++++ 7 files changed, 384 insertions(+), 1 deletion(-) create mode 100644 neutronclient/neutron/v2_0/qos/bandwidth_limit_rule.py create mode 100644 neutronclient/neutron/v2_0/qos/rule.py create mode 100644 neutronclient/tests/unit/qos/test_cli20_rule.py diff --git a/neutronclient/neutron/v2_0/qos/bandwidth_limit_rule.py b/neutronclient/neutron/v2_0/qos/bandwidth_limit_rule.py new file mode 100644 index 000000000..b0e1569ac --- /dev/null +++ b/neutronclient/neutron/v2_0/qos/bandwidth_limit_rule.py @@ -0,0 +1,107 @@ +# Copyright 2015 Huawei Technologies India Pvt Ltd, 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. +# + + +from neutronclient.common import exceptions +from neutronclient.i18n import _ +from neutronclient.neutron import v2_0 as neutronv20 +from neutronclient.neutron.v2_0.qos import rule as qos_rule + + +BANDWIDTH_LIMIT_RULE_RESOURCE = 'bandwidth_limit_rule' + + +def add_bandwidth_limit_arguments(parser): + parser.add_argument( + '--max-kbps', + help=_('max bandwidth in kbps.')) + parser.add_argument( + '--max-burst-kbps', + help=_('max burst bandwidth in kbps.')) + + +def update_bandwidth_limit_args2body(parsed_args, body): + max_kbps = parsed_args.max_kbps + max_burst_kbps = parsed_args.max_burst_kbps + if not (max_kbps or max_burst_kbps): + raise exceptions.CommandError(_("Must provide max_kbps" + " or max_burst_kbps option.")) + neutronv20.update_dict(parsed_args, body, + ['max_kbps', 'max_burst_kbps', 'tenant_id']) + + +class CreateQoSBandwidthLimitRule(qos_rule.QosRuleMixin, + neutronv20.CreateCommand): + """Create a qos bandwidth limit rule.""" + + resource = BANDWIDTH_LIMIT_RULE_RESOURCE + + def add_known_arguments(self, parser): + super(CreateQoSBandwidthLimitRule, self).add_known_arguments(parser) + add_bandwidth_limit_arguments(parser) + + def args2body(self, parsed_args): + body = {} + update_bandwidth_limit_args2body(parsed_args, body) + return {'bandwidth_limit_rule': body} + + +class ListQoSBandwidthLimitRules(qos_rule.QosRuleMixin, + neutronv20.ListCommand): + """List all qos bandwidth limit rules belonging to the specified policy.""" + + resource = BANDWIDTH_LIMIT_RULE_RESOURCE + + _formatters = {} + pagination_support = True + sorting_support = True + + def args2body(self, parsed_args): + body = {} + qos_rule.update_policy_args2body(parsed_args, body) + return {'qos_rule': body} + + +class ShowQoSBandwidthLimitRule(qos_rule.QosRuleMixin, neutronv20.ShowCommand): + """Show information about the given qos bandwidth limit rule.""" + + resource = BANDWIDTH_LIMIT_RULE_RESOURCE + allow_names = False + + +class UpdateQoSBandwidthLimitRule(qos_rule.QosRuleMixin, + neutronv20.UpdateCommand): + """Update the given qos bandwidth limit rule.""" + + resource = BANDWIDTH_LIMIT_RULE_RESOURCE + allow_names = False + + def add_known_arguments(self, parser): + super(UpdateQoSBandwidthLimitRule, self).add_known_arguments(parser) + add_bandwidth_limit_arguments(parser) + + def args2body(self, parsed_args): + body = {} + update_bandwidth_limit_args2body(parsed_args, body) + return {'bandwidth_limit_rule': body} + + +class DeleteQoSBandwidthLimitRule(qos_rule.QosRuleMixin, + neutronv20.DeleteCommand): + """Delete a given qos bandwidth limit rule.""" + + resource = BANDWIDTH_LIMIT_RULE_RESOURCE + allow_names = False diff --git a/neutronclient/neutron/v2_0/qos/policy.py b/neutronclient/neutron/v2_0/qos/policy.py index fd6ffb143..6c680d22c 100755 --- a/neutronclient/neutron/v2_0/qos/policy.py +++ b/neutronclient/neutron/v2_0/qos/policy.py @@ -14,6 +14,8 @@ # under the License. # +import os + from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronv20 @@ -72,6 +74,14 @@ class ShowQoSPolicy(neutronv20.ShowCommand): resource = 'policy' shadow_resource = 'qos_policy' + def format_output_data(self, data): + rules = [] + for rule in data['policy'].get('rules', []): + rules.append("%s (type: %s)" % (rule['id'], rule['type'])) + data['policy']['rules'] = os.linesep.join(rules) + + super(ShowQoSPolicy, self).format_output_data(data) + class CreateQoSPolicy(neutronv20.CreateCommand): """Create a qos policy.""" diff --git a/neutronclient/neutron/v2_0/qos/rule.py b/neutronclient/neutron/v2_0/qos/rule.py new file mode 100644 index 000000000..3b6510d17 --- /dev/null +++ b/neutronclient/neutron/v2_0/qos/rule.py @@ -0,0 +1,58 @@ +# Copyright 2015 Huawei Technologies India Pvt Ltd, 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. +# + + +from neutronclient.i18n import _ +from neutronclient.neutron import v2_0 as neutronv20 +from neutronclient.neutron.v2_0.qos import policy as qos_policy + + +def add_policy_argument(parser): + parser.add_argument( + 'policy', metavar='QOS_POLICY', + help=_('ID or name of the QoS policy.')) + + +def add_rule_argument(parser): + parser.add_argument( + 'rule', metavar='QOS_RULE', + help=_('ID of the QoS rule.')) + + +def update_policy_args2body(parsed_args, body): + neutronv20.update_dict(parsed_args, body, ['policy']) + + +def update_rule_args2body(parsed_args, body): + neutronv20.update_dict(parsed_args, body, ['rule']) + + +class QosRuleMixin(object): + def add_known_arguments(self, parser): + add_policy_argument(parser) + + def set_extra_attrs(self, parsed_args): + self.parent_id = qos_policy.get_qos_policy_id(self.get_client(), + parsed_args.policy) + + +class ListQoSRuleTypes(neutronv20.ListCommand): + """List available qos rule types.""" + + resource = 'rule_type' + shadow_resource = 'qos_rule_type' + pagination_support = True + sorting_support = True diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 8554b2805..cc50a95e5 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -72,7 +72,9 @@ from neutronclient.neutron.v2_0.nsx import qos_queue from neutronclient.neutron.v2_0 import policyprofile from neutronclient.neutron.v2_0 import port +from neutronclient.neutron.v2_0.qos import bandwidth_limit_rule from neutronclient.neutron.v2_0.qos import policy as qos_policy +from neutronclient.neutron.v2_0.qos import rule as qos_rule from neutronclient.neutron.v2_0 import quota from neutronclient.neutron.v2_0 import rbac from neutronclient.neutron.v2_0 import router @@ -378,6 +380,22 @@ class BashCompletionCommand(openstack_command.OpenStackCommand): 'qos-policy-create': qos_policy.CreateQoSPolicy, 'qos-policy-update': qos_policy.UpdateQoSPolicy, 'qos-policy-delete': qos_policy.DeleteQoSPolicy, + 'qos-bandwidth-limit-rule-create': ( + bandwidth_limit_rule.CreateQoSBandwidthLimitRule + ), + 'qos-bandwidth-limit-rule-show': ( + bandwidth_limit_rule.ShowQoSBandwidthLimitRule + ), + 'qos-bandwidth-limit-rule-list': ( + bandwidth_limit_rule.ListQoSBandwidthLimitRules + ), + 'qos-bandwidth-limit-rule-update': ( + bandwidth_limit_rule.UpdateQoSBandwidthLimitRule + ), + 'qos-bandwidth-limit-rule-delete': ( + bandwidth_limit_rule.DeleteQoSBandwidthLimitRule + ), + 'qos-available-rule-types': qos_rule.ListQoSRuleTypes, } COMMANDS = {'2.0': COMMAND_V2} diff --git a/neutronclient/tests/unit/qos/test_cli20_rule.py b/neutronclient/tests/unit/qos/test_cli20_rule.py new file mode 100644 index 000000000..c2fe92130 --- /dev/null +++ b/neutronclient/tests/unit/qos/test_cli20_rule.py @@ -0,0 +1,146 @@ +# Copyright 2015 Huawei Technologies India Pvt Ltd. +# 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 sys + +from neutronclient.neutron.v2_0.qos import bandwidth_limit_rule as bw_rule +from neutronclient.neutron.v2_0.qos import rule as qos_rule +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20QoSRuleJSON(test_cli20.CLITestV20Base): + def setUp(self): + super(CLITestV20QoSRuleJSON, self).setUp() + self.res = 'bandwidth_limit_rule' + self.cmd_res = 'qos_bandwidth_limit_rule' + self.ress = 'bandwidth_limit_rules' + self.cmd_ress = 'qos_bandwidth_limit_rules' + + def test_create_bandwidth_limit_rule_with_max_kbps(self): + cmd = bw_rule.CreateQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), + None) + my_id = 'my-id' + max_kbps = '1337' + policy_id = 'policy_id' + args = ['--max-kbps', max_kbps, policy_id] + position_names = ['max_kbps'] + position_values = [max_kbps] + self._test_create_resource(self.res, cmd, '', my_id, args, + position_names, position_values, + cmd_resource=self.cmd_res, + parent_id=policy_id) + + def test_create_bandwidth_limit_rule_with_max_burst_kbps(self): + cmd = bw_rule.CreateQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), + None) + my_id = 'my-id' + max_burst_kbps = '1337' + policy_id = 'policy_id' + args = ['--max-burst-kbps', max_burst_kbps, policy_id] + position_names = ['max_burst_kbps'] + position_values = [max_burst_kbps] + self._test_create_resource(self.res, cmd, '', my_id, args, + position_names, position_values, + cmd_resource=self.cmd_res, + parent_id=policy_id) + + def test_create_bandwidth_limit_rule_with_all_params(self): + cmd = bw_rule.CreateQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), + None) + my_id = 'my-id' + max_kbps = '1337' + max_burst_kbps = '1337' + policy_id = 'policy_id' + args = ['--max-kbps', max_kbps, + '--max-burst-kbps', max_burst_kbps, + policy_id] + position_names = ['max_kbps', 'max_burst_kbps'] + position_values = [max_kbps, max_burst_kbps] + self._test_create_resource(self.res, cmd, '', my_id, args, + position_names, position_values, + cmd_resource=self.cmd_res, + parent_id=policy_id) + + def test_update_bandwidth_limit_rule_with_max_kbps(self): + cmd = bw_rule.UpdateQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), + None) + my_id = 'my-id' + max_kbps = '1337' + policy_id = 'policy_id' + args = ['--max-kbps', max_kbps, my_id, policy_id] + self._test_update_resource(self.res, cmd, my_id, args, + {'max_kbps': max_kbps, }, + cmd_resource=self.cmd_res, + parent_id=policy_id) + + def test_update_bandwidth_limit_rule_with_max_burst_kbps(self): + cmd = bw_rule.UpdateQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), + None) + my_id = 'my-id' + max_burst_kbps = '1337' + policy_id = 'policy_id' + args = ['--max-burst-kbps', max_burst_kbps, + my_id, policy_id] + self._test_update_resource(self.res, cmd, my_id, args, + {'max_burst_kbps': max_burst_kbps}, + cmd_resource=self.cmd_res, + parent_id=policy_id) + + def test_update_bandwidth_limit_rule_with_all_params(self): + cmd = bw_rule.UpdateQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), + None) + my_id = 'my-id' + max_kbps = '1337' + max_burst_kbps = '1337' + policy_id = 'policy_id' + args = ['--max-kbps', max_kbps, + '--max-burst-kbps', max_burst_kbps, + my_id, policy_id] + self._test_update_resource(self.res, cmd, my_id, args, + {'max_kbps': max_kbps, + 'max_burst_kbps': max_burst_kbps}, + cmd_resource=self.cmd_res, + parent_id=policy_id) + + def test_delete_bandwidth_limit_rule(self): + cmd = bw_rule.DeleteQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), + None) + my_id = 'my-id' + policy_id = 'policy_id' + args = [my_id, policy_id] + self._test_delete_resource(self.res, cmd, my_id, args, + cmd_resource=self.cmd_res, + parent_id=policy_id) + + def test_show_bandwidth_limit_rule(self): + cmd = bw_rule.ShowQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), + None) + policy_id = 'policy_id' + args = [self.test_id, policy_id] + self._test_show_resource(self.res, cmd, self.test_id, args, + [], cmd_resource=self.cmd_res, + parent_id=policy_id) + + def test_list_qos_rule_types(self): + """qos_rule_types.""" + resources = 'rule_types' + cmd_resources = 'qos_rule_types' + response_contents = [{'type': 'bandwidth_limit'}] + cmd = qos_rule.ListQoSRuleTypes(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources(resources, cmd, True, + response_contents=response_contents, + cmd_resources=cmd_resources) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 9e09d7a0b..94d979cf0 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -48,7 +48,7 @@ 'metering_label_rule', 'net_partition', 'fox_socket', 'subnetpool', 'rbac_policy', 'address_scope', - 'policy'] + 'policy', 'bandwidth_limit_rule'] @contextlib.contextmanager diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index e521420b5..facc6d167 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -433,6 +433,10 @@ class Client(ClientBase): rbac_policy_path = "/rbac-policies/%s" qos_policies_path = "/qos/policies" qos_policy_path = "/qos/policies/%s" + qos_bandwidth_limit_rules_path = "/qos/policies/%s/bandwidth_limit_rules" + qos_bandwidth_limit_rule_path = "/qos/policies/%s/bandwidth_limit_rules/%s" + qos_rule_types_path = "/qos/rule-types" + qos_rule_type_path = "/qos/rule-types/%s" # API has no way to report plurals, so we have to hard code them EXTED_PLURALS = {'routers': 'router', @@ -468,6 +472,8 @@ class Client(ClientBase): 'address_scopes': 'address_scope', 'qos_policies': 'qos_policy', 'policies': 'policy', + 'bandwidth_limit_rules': 'bandwidth_limit_rule', + 'rule_types': 'rule_type', } @APIParamsCall @@ -1693,6 +1699,44 @@ def delete_qos_policy(self, qos_policy): """Deletes the specified qos policy.""" return self.delete(self.qos_policy_path % qos_policy) + @APIParamsCall + def list_qos_rule_types(self, retrieve_all=True, **_params): + """List available qos rule types.""" + return self.list('rule_types', self.qos_rule_types_path, + retrieve_all, **_params) + + @APIParamsCall + def list_bandwidth_limit_rules(self, policy_id, + retrieve_all=True, **_params): + """Fetches a list of all qos rules for the given policy.""" + return self.list('bandwidth_limit_rules', + self.qos_bandwidth_limit_rules_path % policy_id, + retrieve_all, **_params) + + @APIParamsCall + def show_bandwidth_limit_rule(self, rule, policy, body=None): + """Creates a new bandwidth limit rule.""" + return self.get(self.qos_bandwidth_limit_rule_path % + (policy, rule), body=body) + + @APIParamsCall + def create_bandwidth_limit_rule(self, policy, body=None): + """Creates a new bandwidth limit rule.""" + return self.post(self.qos_bandwidth_limit_rules_path % policy, + body=body) + + @APIParamsCall + def update_bandwidth_limit_rule(self, rule, policy, body=None): + """Updates a bandwidth limit rule.""" + return self.put(self.qos_bandwidth_limit_rule_path % + (policy, rule), body=body) + + @APIParamsCall + def delete_bandwidth_limit_rule(self, rule, policy): + """Deletes a bandwidth limit rule.""" + return self.delete(self.qos_bandwidth_limit_rule_path % + (policy, rule)) + def __init__(self, **kwargs): """Initialize a new client for the Neutron v2.0 API.""" super(Client, self).__init__(**kwargs) From 9a51f2de3a59bd57b35f6622071c9dcef6c8839c Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Thu, 27 Aug 2015 04:18:58 +0000 Subject: [PATCH 250/845] Updated from global requirements Change-Id: Idb2e1dd2637e8e5ddd684ebf47ca1135d8a758a9 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 2dcc12fb0..9715f48b6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ # The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. -pbr<2.0,>=1.4 +pbr<2.0,>=1.6 argparse cliff>=1.14.0 # Apache-2.0 iso8601>=0.1.9 From a4f64f69b629045e5712957be7faea36fc0b52df Mon Sep 17 00:00:00 2001 From: Stephen Balukoff Date: Thu, 27 Aug 2015 09:34:45 -0700 Subject: [PATCH 251/845] Update tls_container_id to tls_container_ref Since the Listener TLS container references are not UUIDs, the API was changes slightly to refer them as 'refs'. This commit updates the CLI to reflect this subtle but important change as well. Change-Id: Ifd4639de79469d531f683f4a02455dbdb4b12f7f --- neutronclient/neutron/v2_0/lb/v2/listener.py | 17 +++++++++-------- .../tests/unit/lb/v2/test_cli20_listener.py | 10 +++++----- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/neutronclient/neutron/v2_0/lb/v2/listener.py b/neutronclient/neutron/v2_0/lb/v2/listener.py index 4adea3938..46afc5088 100644 --- a/neutronclient/neutron/v2_0/lb/v2/listener.py +++ b/neutronclient/neutron/v2_0/lb/v2/listener.py @@ -64,14 +64,15 @@ def add_known_arguments(self, parser): '--name', help=_('The name of the listener.')) parser.add_argument( - '--default-tls-container-id', - dest='default_tls_container_id', - help=_('Default TLS container ID to retrieve TLS information.')) + '--default-tls-container-ref', + dest='default_tls_container_ref', + help=_('Default TLS container reference' + ' to retrieve TLS information.')) parser.add_argument( - '--sni-container-ids', - dest='sni_container_ids', + '--sni-container-refs', + dest='sni_container_refs', nargs='+', - help=_('List of TLS container IDs for SNI.')) + help=_('List of TLS container references for SNI.')) parser.add_argument( '--loadbalancer', required=True, @@ -105,8 +106,8 @@ def args2body(self, parsed_args): neutronV20.update_dict(parsed_args, body[self.resource], ['connection-limit', 'description', 'loadbalancer_id', 'name', - 'default_tls_container_id', - 'sni_container_ids', + 'default_tls_container_ref', + 'sni_container_refs', 'tenant_id']) return body diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_listener.py b/neutronclient/tests/unit/lb/v2/test_cli20_listener.py index 4a70fd685..6ecac35b5 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_listener.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_listener.py @@ -49,17 +49,17 @@ def test_create_listener_with_all_params(self): loadbalancer = 'loadbalancer' protocol = 'TCP' protocol_port = '80' - def_tls_cont_id = '11111' + def_tls_cont_ref = '11111' args = ['--admin-state-down', '--protocol', protocol, '--protocol-port', protocol_port, '--loadbalancer', loadbalancer, - '--default-tls-container-id', def_tls_cont_id, - '--sni-container-ids', '1111', '2222', '3333'] + '--default-tls-container-ref', def_tls_cont_ref, + '--sni-container-refs', '1111', '2222', '3333'] position_names = ['admin_state_up', 'protocol', 'protocol_port', 'loadbalancer_id', - 'default_tls_container_id', 'sni_container_ids'] + 'default_tls_container_ref', 'sni_container_refs'] position_values = [False, protocol, protocol_port, loadbalancer, - def_tls_cont_id, ['1111', '2222', '3333']] + def_tls_cont_ref, ['1111', '2222', '3333']] self._test_create_resource(resource, cmd, '', my_id, args, position_names, position_values, cmd_resource=cmd_resource) From 0558b49e902ec7af6b78fe1660e756dd2f33ddae Mon Sep 17 00:00:00 2001 From: Toshiaki Higuchi Date: Tue, 20 Jan 2015 13:54:48 +0900 Subject: [PATCH 252/845] Add REJECT rule on FWaaS Client This patch adds REJECT rule. Python-neutronclient changes for CLI. Blueprint: fwaas-reject-rule Change-Id: I267201ad206f50a09ed9c300ac689b6d4fd678b9 --- neutronclient/neutron/v2_0/fw/firewallrule.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/neutron/v2_0/fw/firewallrule.py b/neutronclient/neutron/v2_0/fw/firewallrule.py index 0df936ad8..e2c49d2f0 100644 --- a/neutronclient/neutron/v2_0/fw/firewallrule.py +++ b/neutronclient/neutron/v2_0/fw/firewallrule.py @@ -104,7 +104,7 @@ def add_known_arguments(self, parser): parser.add_argument( '--action', required=True, - choices=['allow', 'deny'], + choices=['allow', 'deny', 'reject'], help=_('Action for the firewall rule.')) def args2body(self, parsed_args): From 627f68e2c6a382b51a1b4ee3250ae431431e86b6 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Wed, 2 Sep 2015 08:19:20 +0000 Subject: [PATCH 253/845] Updated from global requirements Change-Id: Ie96197042615fac287948e8814a01bf7bec14e9b --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 9715f48b6..e8184e487 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ pbr<2.0,>=1.6 argparse cliff>=1.14.0 # Apache-2.0 iso8601>=0.1.9 -netaddr>=0.7.12 +netaddr!=0.7.16,>=0.7.12 oslo.i18n>=1.5.0 # Apache-2.0 oslo.serialization>=1.4.0 # Apache-2.0 oslo.utils>=2.0.0 # Apache-2.0 From d75f79f67c176a38d41fcc5f8794d5956ac65ace Mon Sep 17 00:00:00 2001 From: Takashi NATSUME Date: Fri, 4 Sep 2015 11:04:04 +0900 Subject: [PATCH 254/845] Update path to subunit2html in post_test_hook Per: http://lists.openstack.org/pipermail/openstack-dev/2015-August/072982.html The location of subunit2html changed on the images in the gate so update the path used in the post_test_hook. Long-term we should just use what's in devstack-gate. Change-Id: If37743547f739e494501bbc5dc3bb214293919fa Closes-Bug: #1491646 --- neutronclient/tests/functional/hooks/post_test_hook.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/tests/functional/hooks/post_test_hook.sh b/neutronclient/tests/functional/hooks/post_test_hook.sh index 0f96ec886..87cc29a65 100755 --- a/neutronclient/tests/functional/hooks/post_test_hook.sh +++ b/neutronclient/tests/functional/hooks/post_test_hook.sh @@ -18,7 +18,7 @@ function generate_testr_results { if [ -f .testrepository/0 ]; then sudo .tox/functional/bin/testr last --subunit > $WORKSPACE/testrepository.subunit sudo mv $WORKSPACE/testrepository.subunit $BASE/logs/testrepository.subunit - sudo .tox/functional/bin/python /usr/local/jenkins/slave_scripts/subunit2html.py $BASE/logs/testrepository.subunit $BASE/logs/testr_results.html + sudo /usr/os-testr-env/bin/subunit2html $BASE/logs/testrepository.subunit $BASE/logs/testr_results.html sudo gzip -9 $BASE/logs/testrepository.subunit sudo gzip -9 $BASE/logs/testr_results.html sudo chown jenkins:jenkins $BASE/logs/testrepository.subunit.gz $BASE/logs/testr_results.html.gz From 4903c16c9acd30e5a9512bd2bfcedf1774b2a5a2 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Fri, 4 Sep 2015 15:46:33 +0900 Subject: [PATCH 255/845] Remove NEC plugin specific commands Related blueprint core-vendor-decomposition Related-Bug: #1487929 Change-Id: I05107361106f73212274c7e1506dfde2e26032a4 --- neutronclient/neutron/v2_0/nec/__init__.py | 0 .../neutron/v2_0/nec/packetfilter.py | 237 -------------- neutronclient/shell.py | 6 - .../tests/unit/test_cli20_packetfilter.py | 298 ------------------ neutronclient/v2_0/client.py | 30 -- 5 files changed, 571 deletions(-) delete mode 100644 neutronclient/neutron/v2_0/nec/__init__.py delete mode 100644 neutronclient/neutron/v2_0/nec/packetfilter.py delete mode 100644 neutronclient/tests/unit/test_cli20_packetfilter.py diff --git a/neutronclient/neutron/v2_0/nec/__init__.py b/neutronclient/neutron/v2_0/nec/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutronclient/neutron/v2_0/nec/packetfilter.py b/neutronclient/neutron/v2_0/nec/packetfilter.py deleted file mode 100644 index dd77cf569..000000000 --- a/neutronclient/neutron/v2_0/nec/packetfilter.py +++ /dev/null @@ -1,237 +0,0 @@ -# Copyright 2014 NEC Corporation -# 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. - -from neutronclient.common import exceptions -from neutronclient.common import utils -from neutronclient.common import validators -from neutronclient.i18n import _ -from neutronclient.neutron import v2_0 as neutronV20 - - -class ListPacketFilter(neutronV20.ListCommand): - """List packet filters that belong to a given tenant.""" - - resource = 'packet_filter' - list_columns = ['id', 'name', 'action', 'priority', 'summary'] - pagination_support = True - sorting_support = True - - def extend_list(self, data, parsed_args): - for d in data: - val = [] - proto_eth_type = [] - if d.get('protocol'): - proto_eth_type.append('protocol: %s' % d['protocol'].upper()) - if d.get('eth_type'): - proto_eth_type.append('eth_type: %s' % d['eth_type']) - if proto_eth_type: - val.append(', '.join(proto_eth_type)) - val.append('network: ' + d['network_id']) - if d.get('in_port'): - val.append('in_port: ' + d['in_port']) - source = [str(d.get(field)) for field - in ['src_mac', 'src_cidr', 'src_port'] if d.get(field)] - if source: - val.append('source: ' + ' '.join(source)) - dest = [str(d.get(field)) for field - in ['dst_mac', 'dst_cidr', 'dst_port'] if d.get(field)] - if dest: - val.append('destination: ' + ' '.join(dest)) - d['summary'] = '\n'.join(val) - - -class ShowPacketFilter(neutronV20.ShowCommand): - """Show information of a given packet filter.""" - - resource = 'packet_filter' - - -class PacketFilterOptionMixin(object): - def add_known_arguments(self, parser): - mode = self._get_mode() - if not mode: - return - mode_create = mode == 'create' - - if mode_create: - parser.add_argument( - '--admin-state-down', - dest='admin_state', action='store_false', - help=_('Set Admin State Up to false')) - else: - utils.add_boolean_argument( - parser, '--admin-state', - help=_('Set a value of Admin State Up')) - - parser.add_argument( - '--name', - help=_('Name of this packet filter')) - - if mode_create: - parser.add_argument( - '--in-port', metavar='PORT', - help=_('Name or ID of the input port')) - - parser.add_argument( - '--src-mac', - help=_('Source MAC address')) - parser.add_argument( - '--dst-mac', - help=_('Destination MAC address')) - parser.add_argument( - '--eth-type', - help=_('Ether Type. Integer [0:65535] (hex or decimal).' - ' E.g., 0x0800 (IPv4), 0x0806 (ARP), 0x86DD (IPv6)')) - parser.add_argument( - '--protocol', - help=_('IP Protocol.' - ' Protocol name or integer.' - ' Recognized names are icmp, tcp, udp, arp' - ' (case insensitive).' - ' Integer should be [0:255] (decimal or hex).')) - parser.add_argument( - '--src-cidr', - help=_('Source IP address CIDR')) - parser.add_argument( - '--dst-cidr', - help=_('Destination IP address CIDR')) - parser.add_argument( - '--src-port', - help=_('Source port address')) - parser.add_argument( - '--dst-port', - help=_('Destination port address')) - - default_priority = '30000' if mode_create else None - parser.add_argument( - '--priority', metavar='PRIORITY', - default=default_priority, - help=(_('Priority of the filter. Integer of [0:65535].%s') - % (' Default: 30000.' if mode_create else ''))) - - default_action = 'allow' if mode_create else None - parser.add_argument( - '--action', - choices=['allow', 'drop'], - default=default_action, - help=(_('Action of the filter.%s') - % (' Default: allow' if mode_create else ''))) - - if mode_create: - parser.add_argument( - 'network', metavar='NETWORK', - help=_('network to which this packet filter is applied')) - - def _get_mode(self): - klass = self.__class__.__name__.lower() - if klass.startswith('create'): - mode = 'create' - elif klass.startswith('update'): - mode = 'update' - else: - mode = None - return mode - - def validate_fields(self, parsed_args): - self._validate_protocol(parsed_args.protocol) - validators.validate_int_range(parsed_args, 'priority', 0, 0xffff) - validators.validate_int_range(parsed_args, 'src_port', 0, 0xffff) - validators.validate_int_range(parsed_args, 'dst_port', 0, 0xffff) - validators.validate_ip_subnet(parsed_args, 'src_cidr') - validators.validate_ip_subnet(parsed_args, 'dst_cidr') - - def _validate_protocol(self, protocol): - if not protocol or protocol == 'action=clear': - return - try: - protocol = int(protocol, 0) - if 0 <= protocol <= 255: - return - except ValueError: - # Use string as a protocol name - # Exact check will be done in the server side. - return - msg = (_('protocol %s should be either of name ' - '(tcp, udp, icmp, arp; ' - 'case insensitive) or integer [0:255] (decimal or hex).') % - protocol) - raise exceptions.CommandError(msg) - - -class CreatePacketFilter(PacketFilterOptionMixin, - neutronV20.CreateCommand): - """Create a packet filter for a given tenant.""" - - resource = 'packet_filter' - - def args2body(self, parsed_args): - self.validate_fields(parsed_args) - - _network_id = neutronV20.find_resourceid_by_name_or_id( - self.get_client(), 'network', parsed_args.network) - body = {'network_id': _network_id, - 'admin_state_up': parsed_args.admin_state} - if parsed_args.in_port: - _port_id = neutronV20.find_resourceid_by_name_or_id( - self.get_client(), 'port', parsed_args.in_port) - body['in_port'] = _port_id - - neutronV20.update_dict( - parsed_args, body, - ['action', 'priority', 'name', - 'eth_type', 'protocol', 'src_mac', 'dst_mac', - 'src_cidr', 'dst_cidr', 'src_port', 'dst_port']) - - return {self.resource: body} - - -class UpdatePacketFilter(PacketFilterOptionMixin, - neutronV20.UpdateCommand): - """Update packet filter's information.""" - - resource = 'packet_filter' - - def args2body(self, parsed_args): - self.validate_fields(parsed_args) - - body = {} - if hasattr(parsed_args, 'admin_state'): - body['admin_state_up'] = (parsed_args.admin_state == 'True') - - # fields which allows None - for attr in ['eth_type', 'protocol', 'src_mac', 'dst_mac', - 'src_cidr', 'dst_cidr', 'src_port', 'dst_port']: - if not hasattr(parsed_args, attr): - continue - val = getattr(parsed_args, attr) - if val is None: - continue - if val == '' or val == 'action=clear': - body[attr] = None - else: - body[attr] = val - - for attr in ['action', 'priority', 'name']: - if (hasattr(parsed_args, attr) and - getattr(parsed_args, attr) is not None): - body[attr] = getattr(parsed_args, attr) - - return {self.resource: body} - - -class DeletePacketFilter(neutronV20.DeleteCommand): - """Delete a given packet filter.""" - - resource = 'packet_filter' diff --git a/neutronclient/shell.py b/neutronclient/shell.py index cc50a95e5..9dbc69dd6 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -64,7 +64,6 @@ from neutronclient.neutron.v2_0.lb.v2 import pool as lbaas_pool from neutronclient.neutron.v2_0.lb import vip as lb_vip from neutronclient.neutron.v2_0 import metering -from neutronclient.neutron.v2_0.nec import packetfilter from neutronclient.neutron.v2_0 import netpartition from neutronclient.neutron.v2_0 import network from neutronclient.neutron.v2_0 import networkprofile @@ -360,11 +359,6 @@ class BashCompletionCommand(openstack_command.OpenStackCommand): 'nuage-netpartition-show': netpartition.ShowNetPartition, 'nuage-netpartition-create': netpartition.CreateNetPartition, 'nuage-netpartition-delete': netpartition.DeleteNetPartition, - 'nec-packet-filter-list': packetfilter.ListPacketFilter, - 'nec-packet-filter-show': packetfilter.ShowPacketFilter, - 'nec-packet-filter-create': packetfilter.CreatePacketFilter, - 'nec-packet-filter-update': packetfilter.UpdatePacketFilter, - 'nec-packet-filter-delete': packetfilter.DeletePacketFilter, 'rbac-create': rbac.CreateRBACPolicy, 'rbac-update': rbac.UpdateRBACPolicy, 'rbac-list': rbac.ListRBACPolicy, diff --git a/neutronclient/tests/unit/test_cli20_packetfilter.py b/neutronclient/tests/unit/test_cli20_packetfilter.py deleted file mode 100644 index dcb190e1c..000000000 --- a/neutronclient/tests/unit/test_cli20_packetfilter.py +++ /dev/null @@ -1,298 +0,0 @@ -# Copyright 2014 NEC Corporation. -# 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 sys - -from mox3 import mox - -from neutronclient.common import exceptions -from neutronclient.neutron.v2_0.nec import packetfilter as pf -from neutronclient import shell -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20PacketFilterJSON(test_cli20.CLITestV20Base): - def test_create_packetfilter_with_mandatory_params(self): - """Create packetfilter: packetfilter1.""" - resource = 'packet_filter' - cmd = pf.CreatePacketFilter(test_cli20.MyApp(sys.stdout), None) - name = 'packetfilter1' - myid = 'myid' - args = ['--priority', '30000', '--action', 'allow', 'net1'] - position_names = ['network_id', 'action', 'priority'] - position_values = ['net1', 'allow', '30000'] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_packetfilter_with_all_params(self): - """Create packetfilter: packetfilter1.""" - resource = 'packet_filter' - cmd = pf.CreatePacketFilter(test_cli20.MyApp(sys.stdout), None) - name = 'packetfilter1' - myid = 'myid' - args = ['--name', name, - '--admin-state-down', - '--in-port', 'port1', - '--src-mac', '00:11:22:33:44:55', - '--dst-mac', 'aa:bb:cc:dd:ee:ff', - '--eth-type', '0x0800', - '--protocol', 'tcp', - '--src-cidr', '10.1.1.0/24', - '--dst-cidr', '10.2.2.0/24', - '--src-port', '40001', - '--dst-port', '4000', - '--priority', '30000', - '--action', 'drop', 'net1'] - params = {'network_id': 'net1', - 'action': 'drop', - 'priority': '30000', - 'name': name, - 'admin_state_up': False, - 'in_port': 'port1', - 'src_mac': '00:11:22:33:44:55', - 'dst_mac': 'aa:bb:cc:dd:ee:ff', - 'eth_type': '0x0800', - 'protocol': 'tcp', - 'src_cidr': '10.1.1.0/24', - 'dst_cidr': '10.2.2.0/24', - 'src_port': '40001', - 'dst_port': '4000', - } - position_names = sorted(params) - position_values = [params[k] for k in sorted(params)] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_list_packetfilters_detail(self): - """list packetfilters: -D.""" - resources = "packet_filters" - cmd = pf.ListPacketFilter(test_cli20.MyApp(sys.stdout), None) - response_contents = [{'id': 'myid1', 'network_id': 'net1'}, - {'id': 'myid2', 'network_id': 'net2'}] - self._test_list_resources(resources, cmd, True, - response_contents=response_contents) - - def _stubout_extend_list(self): - self.mox.StubOutWithMock(pf.ListPacketFilter, "extend_list") - pf.ListPacketFilter.extend_list(mox.IsA(list), mox.IgnoreArg()) - - def test_list_packetfilters_pagination(self): - resources = "packet_filters" - cmd = pf.ListPacketFilter(test_cli20.MyApp(sys.stdout), None) - self._stubout_extend_list() - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_packetfilters_sort(self): - """list packetfilters: --sort-key name --sort-key id --sort-key asc - --sort-key desc - """ - resources = "packet_filters" - cmd = pf.ListPacketFilter(test_cli20.MyApp(sys.stdout), None) - self._stubout_extend_list() - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_packetfilters_limit(self): - """list packetfilters: -P.""" - resources = "packet_filters" - cmd = pf.ListPacketFilter(test_cli20.MyApp(sys.stdout), None) - self._stubout_extend_list() - self._test_list_resources(resources, cmd, page_size=1000) - - def test_update_packetfilter(self): - """Update packetfilter: myid --name myname --tags a b.""" - resource = 'packet_filter' - cmd = pf.UpdatePacketFilter(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'myname'], - {'name': 'myname'} - ) - - def test_update_packetfilter_with_all_params(self): - resource = 'packet_filter' - cmd = pf.UpdatePacketFilter(test_cli20.MyApp(sys.stdout), None) - name = 'packetfilter1' - args = ['--name', name, - '--admin-state', 'True', - '--src-mac', '00:11:22:33:44:55', - '--dst-mac', 'aa:bb:cc:dd:ee:ff', - '--eth-type', '0x0800', - '--protocol', 'tcp', - '--src-cidr', '10.1.1.0/24', - '--dst-cidr', '10.2.2.0/24', - '--src-port', '40001', - '--dst-port', '4000', - '--priority', '30000', - '--action', 'drop', - 'myid' - ] - params = {'action': 'drop', - 'priority': '30000', - 'name': name, - 'admin_state_up': True, - 'src_mac': '00:11:22:33:44:55', - 'dst_mac': 'aa:bb:cc:dd:ee:ff', - 'eth_type': '0x0800', - 'protocol': 'tcp', - 'src_cidr': '10.1.1.0/24', - 'dst_cidr': '10.2.2.0/24', - 'src_port': '40001', - 'dst_port': '4000', - } - # position_names = sorted(params) - # position_values = [params[k] for k in sorted(params)] - self._test_update_resource(resource, cmd, 'myid', - args, params) - - def test_update_packetfilter_admin_state_false(self): - resource = 'packet_filter' - cmd = pf.UpdatePacketFilter(test_cli20.MyApp(sys.stdout), None) - args = ['--admin-state', 'False', 'myid'] - params = {'admin_state_up': False} - self._test_update_resource(resource, cmd, 'myid', - args, params) - - def test_update_packetfilter_exception(self): - """Update packetfilter: myid.""" - resource = 'packet_filter' - cmd = pf.UpdatePacketFilter(test_cli20.MyApp(sys.stdout), None) - exc = self.assertRaises(exceptions.CommandError, - self._test_update_resource, - resource, cmd, 'myid', ['myid'], {}) - self.assertEqual('Must specify new values to update packet_filter', - str(exc)) - - def test_delete_packetfilter(self): - """Delete packetfilter: myid.""" - resource = 'packet_filter' - cmd = pf.DeletePacketFilter(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) - - def test_show_packetfilter(self): - """Show packetfilter: myid.""" - resource = 'packet_filter' - cmd = pf.ShowPacketFilter(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, - ['id', 'name']) - - -class CLITestV20PacketFilterXML(CLITestV20PacketFilterJSON): - format = 'xml' - - -class CLITestV20PacketFilterValidateParam(test_cli20.CLITestV20Base): - def _test_create_packetfilter_pass_validation(self, cmdline=None, - params=None, base_args=None): - resource = 'packet_filter' - cmd = pf.CreatePacketFilter(test_cli20.MyApp(sys.stdout), None) - name = 'packetfilter1' - myid = 'myid' - if base_args is None: - args = '--priority 30000 --action allow net1'.split() - else: - args = base_args.split() - if cmdline: - args += cmdline.split() - _params = {'network_id': 'net1', - 'action': 'allow', - 'priority': '30000'} - if params: - _params.update(params) - position_names = sorted(_params) - position_values = [_params[k] for k in sorted(_params)] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def _test_create_packetfilter_negative_validation(self, cmdline): - resource = 'packet_filter' - cmd = pf.CreatePacketFilter(test_cli20.MyApp(sys.stdout), None) - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) - cmd_parser = cmd.get_parser('create_' + resource) - args = cmdline.split() - self.assertRaises(exceptions.CommandError, - shell.run_command, - cmd, cmd_parser, args) - - def test_create_pf_hex_priority(self): - self._test_create_packetfilter_pass_validation( - base_args='--priority 0xffff --action allow net1', - params={'priority': '0xffff'}) - - def test_create_pf_hex_src_port(self): - self._test_create_packetfilter_pass_validation( - cmdline='--src-port 0xffff', params={'src_port': '0xffff'}) - - def test_create_pf_hex_dst_port(self): - self._test_create_packetfilter_pass_validation( - cmdline='--dst-port 0xffff', params={'dst_port': '0xffff'}) - - def test_create_pf_ip_proto_zero(self): - self._test_create_packetfilter_pass_validation( - cmdline='--protocol 0', params={'protocol': '0'}) - - def test_create_pf_ip_proto_max_hex(self): - self._test_create_packetfilter_pass_validation( - cmdline='--protocol 0xff', params={'protocol': '0xff'}) - - def test_create_pf_ip_proto_with_names(self): - for proto in ['tcp', 'xxxx']: - self._test_create_packetfilter_pass_validation( - cmdline='--protocol ' + proto, params={'protocol': proto}) - - def test_create_pf_negative_priority(self): - self._test_create_packetfilter_negative_validation( - '--priority -1 --action allow net1') - - def test_create_pf_too_big_priority(self): - self._test_create_packetfilter_negative_validation( - '--priority 65536 --action allow net1') - - def test_create_pf_negative_src_port(self): - self._test_create_packetfilter_negative_validation( - '--src-port -1 --priority 20000 --action allow net1') - - def test_create_pf_too_big_src_port(self): - self._test_create_packetfilter_negative_validation( - '--src-port 65536 --priority 20000 --action allow net1') - - def test_create_pf_negative_dst_port(self): - self._test_create_packetfilter_negative_validation( - '--dst-port -1 --priority 20000 --action allow net1') - - def test_create_pf_too_big_dst_port(self): - self._test_create_packetfilter_negative_validation( - '--dst-port 65536 --priority 20000 --action allow net1') - - def test_create_pf_negative_protocol(self): - self._test_create_packetfilter_negative_validation( - '--protocol -1 --priority 20000 --action allow net1') - - def test_create_pf_too_big_hex_protocol(self): - self._test_create_packetfilter_negative_validation( - '--protocol 0x100 --priority 20000 --action allow net1') - - def test_create_pf_invalid_src_cidr(self): - self._test_create_packetfilter_negative_validation( - '--src-cidr invalid --priority 20000 --action allow net1') - - def test_create_pf_invalid_dst_cidr(self): - self._test_create_packetfilter_negative_validation( - '--dst-cidr invalid --priority 20000 --action allow net1') diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index facc6d167..f69c4b96c 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -408,8 +408,6 @@ class Client(ClientBase): metering_label_path = "/metering/metering-labels/%s" metering_label_rules_path = "/metering/metering-label-rules" metering_label_rule_path = "/metering/metering-label-rules/%s" - packet_filters_path = "/packet_filters" - packet_filter_path = "/packet_filters/%s" DHCP_NETS = '/dhcp-networks' DHCP_AGENTS = '/dhcp-agents' @@ -461,7 +459,6 @@ class Client(ClientBase): 'metering_labels': 'metering_label', 'metering_label_rules': 'metering_label_rule', 'net_partitions': 'net_partition', - 'packet_filters': 'packet_filter', 'loadbalancers': 'loadbalancer', 'listeners': 'listener', 'lbaas_pools': 'lbaas_pool', @@ -1616,33 +1613,6 @@ def delete_net_partition(self, netpartition): """Delete the network partition.""" return self.delete(self.net_partition_path % netpartition) - @APIParamsCall - def create_packet_filter(self, body=None): - """Create a new packet filter.""" - return self.post(self.packet_filters_path, body=body) - - @APIParamsCall - def update_packet_filter(self, packet_filter_id, body=None): - """Update a packet filter.""" - return self.put(self.packet_filter_path % packet_filter_id, body=body) - - @APIParamsCall - def list_packet_filters(self, retrieve_all=True, **_params): - """Fetch a list of all packet filters for a tenant.""" - return self.list('packet_filters', self.packet_filters_path, - retrieve_all, **_params) - - @APIParamsCall - def show_packet_filter(self, packet_filter_id, **_params): - """Fetch information of a certain packet filter.""" - return self.get(self.packet_filter_path % packet_filter_id, - params=_params) - - @APIParamsCall - def delete_packet_filter(self, packet_filter_id): - """Delete the specified packet filter.""" - return self.delete(self.packet_filter_path % packet_filter_id) - @APIParamsCall def create_rbac_policy(self, body=None): """Create a new RBAC policy.""" From 8292cb85f8830e65284736ea1f9bae2f344c084a Mon Sep 17 00:00:00 2001 From: Sourabh Patwardhan Date: Tue, 1 Sep 2015 16:02:14 -0700 Subject: [PATCH 256/845] Remove Cisco-specific neutron client commands Removed Cisco-specific neutron client commands and ported code to networking-cisco as part of phase 2 of core vendor decompostion. Corresponding vendor repo change: Ia93134bfc7379781e56b0494087e3eca3fd9b232 Partial-Bug: #1490768 Partial-implements: blueprint core-vendor-decomposition Change-Id: I97d1370e2a3333fbcf87107f4624590d43d2940d --- neutronclient/neutron/v2_0/credential.py | 73 --------- neutronclient/neutron/v2_0/networkprofile.py | 148 ------------------ neutronclient/neutron/v2_0/policyprofile.py | 72 --------- neutronclient/shell.py | 15 -- neutronclient/tests/unit/test_cli20.py | 3 +- .../tests/unit/test_cli20_credential.py | 77 --------- .../tests/unit/test_cli20_networkprofile.py | 133 ---------------- .../tests/unit/test_cli20_policyprofile.py | 71 --------- neutronclient/v2_0/client.py | 81 ---------- 9 files changed, 1 insertion(+), 672 deletions(-) delete mode 100644 neutronclient/neutron/v2_0/credential.py delete mode 100644 neutronclient/neutron/v2_0/networkprofile.py delete mode 100644 neutronclient/neutron/v2_0/policyprofile.py delete mode 100644 neutronclient/tests/unit/test_cli20_credential.py delete mode 100644 neutronclient/tests/unit/test_cli20_networkprofile.py delete mode 100644 neutronclient/tests/unit/test_cli20_policyprofile.py diff --git a/neutronclient/neutron/v2_0/credential.py b/neutronclient/neutron/v2_0/credential.py deleted file mode 100644 index c397627b7..000000000 --- a/neutronclient/neutron/v2_0/credential.py +++ /dev/null @@ -1,73 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -from neutronclient.i18n import _ -from neutronclient.neutron import v2_0 as neutronV20 - - -class ListCredential(neutronV20.ListCommand): - """List credentials that belong to a given tenant.""" - - resource = 'credential' - _formatters = {} - list_columns = ['credential_id', 'credential_name', 'user_name', - 'password', 'type'] - - -class ShowCredential(neutronV20.ShowCommand): - """Show information of a given credential.""" - - resource = 'credential' - allow_names = False - - -class CreateCredential(neutronV20.CreateCommand): - """Create a credential.""" - - resource = 'credential' - - def add_known_arguments(self, parser): - parser.add_argument( - 'credential_name', - help=_('Name/IP address for credential.')) - parser.add_argument( - 'credential_type', - help=_('Type of the credential.')) - parser.add_argument( - '--username', - help=_('Username for the credential.')) - parser.add_argument( - '--password', - help=_('Password for the credential.')) - - def args2body(self, parsed_args): - body = {'credential': { - 'credential_name': parsed_args.credential_name}} - - if parsed_args.credential_type: - body['credential'].update({'type': - parsed_args.credential_type}) - if parsed_args.username: - body['credential'].update({'user_name': - parsed_args.username}) - if parsed_args.password: - body['credential'].update({'password': - parsed_args.password}) - return body - - -class DeleteCredential(neutronV20.DeleteCommand): - """Delete a given credential.""" - - resource = 'credential' - allow_names = False diff --git a/neutronclient/neutron/v2_0/networkprofile.py b/neutronclient/neutron/v2_0/networkprofile.py deleted file mode 100644 index ed85875f5..000000000 --- a/neutronclient/neutron/v2_0/networkprofile.py +++ /dev/null @@ -1,148 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -from __future__ import print_function - -from neutronclient.i18n import _ -from neutronclient.neutron import v2_0 as neutronV20 -from neutronclient.neutron.v2_0 import parse_args_to_dict - -RESOURCE = 'network_profile' -SEGMENT_TYPE_CHOICES = ['vlan', 'overlay', 'multi-segment', 'trunk'] - - -class ListNetworkProfile(neutronV20.ListCommand): - """List network profiles that belong to a given tenant.""" - - resource = RESOURCE - _formatters = {} - list_columns = ['id', 'name', 'segment_type', 'sub_type', 'segment_range', - 'physical_network', 'multicast_ip_index', - 'multicast_ip_range'] - - -class ShowNetworkProfile(neutronV20.ShowCommand): - """Show information of a given network profile.""" - - resource = RESOURCE - allow_names = True - - -class CreateNetworkProfile(neutronV20.CreateCommand): - """Create a network profile.""" - - resource = RESOURCE - - def add_known_arguments(self, parser): - parser.add_argument('name', - help=_('Name for network profile.')) - parser.add_argument('segment_type', - choices=SEGMENT_TYPE_CHOICES, - help='Segment type.') - # TODO(Abhishek): Check on sub-type choices depending on segment_type - parser.add_argument('--sub_type', - help=_('Sub-type for the segment. Available ' - 'sub-types for overlay segments: ' - 'native, enhanced; For trunk segments: ' - 'vlan, overlay.')) - parser.add_argument('--segment_range', - help=_('Range for the segment.')) - parser.add_argument('--physical_network', - help=_('Name for the physical network.')) - parser.add_argument('--multicast_ip_range', - help=_('Multicast IPv4 range.')) - parser.add_argument("--add-tenant", - action='append', dest='add_tenants', - help=_("Add tenant to the network profile. " - "You can repeat this option.")) - - def args2body(self, parsed_args): - body = {'network_profile': {'name': parsed_args.name}} - if parsed_args.segment_type: - body['network_profile'].update({'segment_type': - parsed_args.segment_type}) - if parsed_args.sub_type: - body['network_profile'].update({'sub_type': - parsed_args.sub_type}) - if parsed_args.segment_range: - body['network_profile'].update({'segment_range': - parsed_args.segment_range}) - if parsed_args.physical_network: - body['network_profile'].update({'physical_network': - parsed_args.physical_network}) - if parsed_args.multicast_ip_range: - body['network_profile'].update({'multicast_ip_range': - parsed_args.multicast_ip_range}) - if parsed_args.add_tenants: - body['network_profile'].update({'add_tenants': - parsed_args.add_tenants}) - return body - - -class DeleteNetworkProfile(neutronV20.DeleteCommand): - """Delete a given network profile.""" - - resource = RESOURCE - allow_names = True - - -class UpdateNetworkProfile(neutronV20.UpdateCommand): - """Update network profile's information.""" - - resource = RESOURCE - - def add_known_arguments(self, parser): - parser.add_argument("--remove-tenant", - action='append', dest='remove_tenants', - help=_("Remove tenant from the network profile. " - "You can repeat this option.")) - parser.add_argument("--add-tenant", - action='append', dest='add_tenants', - help=_("Add tenant to the network profile. " - "You can repeat this option.")) - - def args2body(self, parsed_args): - body = {'network_profile': {}} - if parsed_args.remove_tenants: - body['network_profile']['remove_tenants'] = (parsed_args. - remove_tenants) - if parsed_args.add_tenants: - body['network_profile']['add_tenants'] = parsed_args.add_tenants - return body - - -# Aaron: This function is deprecated -class UpdateNetworkProfileV2(neutronV20.NeutronCommand): - - api = 'network' - resource = RESOURCE - - def get_parser(self, prog_name): - parser = super(UpdateNetworkProfileV2, self).get_parser(prog_name) - parser.add_argument("--remove-tenant", - help="Remove tenant from the network profile.") - return parser - - def run(self, parsed_args): - self.log.debug('run(%s)' % parsed_args) - neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format - data = {self.resource: parse_args_to_dict(parsed_args)} - if parsed_args.remove_tenant: - data[self.resource]['remove_tenant'] = parsed_args.remove_tenant - neutron_client.update_network_profile(parsed_args.id, - {self.resource: data}) - print((_('Updated %(resource)s: %(id)s') % - {'id': parsed_args.id, 'resource': self.resource}), - file=self.app.stdout) - return diff --git a/neutronclient/neutron/v2_0/policyprofile.py b/neutronclient/neutron/v2_0/policyprofile.py deleted file mode 100644 index 7ff00db9e..000000000 --- a/neutronclient/neutron/v2_0/policyprofile.py +++ /dev/null @@ -1,72 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -from __future__ import print_function - -from neutronclient.i18n import _ -from neutronclient.neutron import v2_0 as neutronV20 -from neutronclient.neutron.v2_0 import parse_args_to_dict - -RESOURCE = 'policy_profile' - - -class ListPolicyProfile(neutronV20.ListCommand): - """List policy profiles that belong to a given tenant.""" - - resource = RESOURCE - _formatters = {} - list_columns = ['id', 'name'] - - -class ShowPolicyProfile(neutronV20.ShowCommand): - """Show information of a given policy profile.""" - - resource = RESOURCE - allow_names = True - - -class UpdatePolicyProfile(neutronV20.UpdateCommand): - """Update policy profile's information.""" - - resource = RESOURCE - - -class UpdatePolicyProfileV2(neutronV20.UpdateCommand): - """Update policy profile's information.""" - - api = 'network' - resource = RESOURCE - - def get_parser(self, prog_name): - parser = super(UpdatePolicyProfileV2, self).get_parser(prog_name) - parser.add_argument("--add-tenant", - help=_("Add tenant to the policy profile.")) - parser.add_argument("--remove-tenant", - help=_("Remove tenant from the policy profile.")) - return parser - - def run(self, parsed_args): - self.log.debug('run(%s)' % parsed_args) - neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format - data = {self.resource: parse_args_to_dict(parsed_args)} - if parsed_args.add_tenant: - data[self.resource]['add_tenant'] = parsed_args.add_tenant - if parsed_args.remove_tenant: - data[self.resource]['remove_tenant'] = parsed_args.remove_tenant - neutron_client.update_policy_profile(parsed_args.id, - {self.resource: data}) - print((_('Updated %(resource)s: %(id)s') % - {'id': parsed_args.id, 'resource': self.resource}), - file=self.app.stdout) - return diff --git a/neutronclient/shell.py b/neutronclient/shell.py index cc50a95e5..fae2a219d 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -48,7 +48,6 @@ from neutronclient.neutron.v2_0 import address_scope from neutronclient.neutron.v2_0 import agent from neutronclient.neutron.v2_0 import agentscheduler -from neutronclient.neutron.v2_0 import credential from neutronclient.neutron.v2_0 import extension from neutronclient.neutron.v2_0 import floatingip from neutronclient.neutron.v2_0.fw import firewall @@ -67,10 +66,8 @@ from neutronclient.neutron.v2_0.nec import packetfilter from neutronclient.neutron.v2_0 import netpartition from neutronclient.neutron.v2_0 import network -from neutronclient.neutron.v2_0 import networkprofile from neutronclient.neutron.v2_0.nsx import networkgateway from neutronclient.neutron.v2_0.nsx import qos_queue -from neutronclient.neutron.v2_0 import policyprofile from neutronclient.neutron.v2_0 import port from neutronclient.neutron.v2_0.qos import bandwidth_limit_rule from neutronclient.neutron.v2_0.qos import policy as qos_policy @@ -306,18 +303,6 @@ class BashCompletionCommand(openstack_command.OpenStackCommand): 'firewall-create': firewall.CreateFirewall, 'firewall-update': firewall.UpdateFirewall, 'firewall-delete': firewall.DeleteFirewall, - 'cisco-credential-list': credential.ListCredential, - 'cisco-credential-show': credential.ShowCredential, - 'cisco-credential-create': credential.CreateCredential, - 'cisco-credential-delete': credential.DeleteCredential, - 'cisco-network-profile-list': networkprofile.ListNetworkProfile, - 'cisco-network-profile-show': networkprofile.ShowNetworkProfile, - 'cisco-network-profile-create': networkprofile.CreateNetworkProfile, - 'cisco-network-profile-delete': networkprofile.DeleteNetworkProfile, - 'cisco-network-profile-update': networkprofile.UpdateNetworkProfile, - 'cisco-policy-profile-list': policyprofile.ListPolicyProfile, - 'cisco-policy-profile-show': policyprofile.ShowPolicyProfile, - 'cisco-policy-profile-update': policyprofile.UpdatePolicyProfile, 'ipsec-site-connection-list': ( ipsec_site_connection.ListIPsecSiteConnection ), diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 94d979cf0..779022363 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -42,8 +42,7 @@ non_admin_status_resources = ['subnet', 'floatingip', 'security_group', 'security_group_rule', 'qos_queue', 'network_gateway', 'gateway_device', - 'credential', 'network_profile', - 'policy_profile', 'ikepolicy', + 'ikepolicy', 'ipsecpolicy', 'metering_label', 'metering_label_rule', 'net_partition', 'fox_socket', 'subnetpool', diff --git a/neutronclient/tests/unit/test_cli20_credential.py b/neutronclient/tests/unit/test_cli20_credential.py deleted file mode 100644 index 4f4a1d35d..000000000 --- a/neutronclient/tests/unit/test_cli20_credential.py +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright 2013 Cisco Systems 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 sys - -from neutronclient.neutron.v2_0 import credential -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20Credential(test_cli20.CLITestV20Base): - - def test_create_credential(self): - """Create credential: myid.""" - resource = 'credential' - cmd = credential.CreateCredential(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - type = 'mytype' - args = [name, type] - position_names = ['credential_name', 'type'] - position_values = [name, type] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_list_credentials_detail(self): - """List credentials: -D.""" - resources = 'credentials' - cmd = credential.ListCredential(test_cli20.MyApp(sys.stdout), None) - contents = [{'credential_name': 'myname', 'type': 'mytype'}] - self._test_list_resources(resources, cmd, True, - response_contents=contents) - - def test_list_credential_known_option_after_unknown(self): - """List credential: -- --tags a b --request-format xml.""" - resources = 'credentials' - cmd = credential.ListCredential(test_cli20.MyApp(sys.stdout), None) - contents = [{'credential_name': 'myname', 'type': 'mytype'}] - self._test_list_resources(resources, cmd, tags=['a', 'b'], - response_contents=contents) - - def test_list_credential_fields(self): - """List credential: --fields a --fields b -- --fields c d.""" - resources = 'credentials' - cmd = credential.ListCredential(test_cli20.MyApp(sys.stdout), None) - contents = [{'credential_name': 'myname', 'type': 'mytype'}] - self._test_list_resources(resources, cmd, - fields_1=['a', 'b'], fields_2=['c', 'd'], - response_contents=contents) - - def test_show_credential(self): - """Show credential: --fields id --fields name myid.""" - resource = 'credential' - cmd = credential.ShowCredential(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, - ['id', 'name']) - - def test_delete_credential(self): - """Delete credential: myid.""" - resource = 'credential' - cmd = credential.DeleteCredential(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) diff --git a/neutronclient/tests/unit/test_cli20_networkprofile.py b/neutronclient/tests/unit/test_cli20_networkprofile.py deleted file mode 100644 index c5a735338..000000000 --- a/neutronclient/tests/unit/test_cli20_networkprofile.py +++ /dev/null @@ -1,133 +0,0 @@ -# Copyright 2013 Cisco Systems 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 sys - -from neutronclient.neutron.v2_0 import networkprofile -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20NetworkProfile(test_cli20.CLITestV20Base): - - def test_create_networkprofile(self): - """Create networkprofile: myid.""" - resource = 'network_profile' - cmd = networkprofile.CreateNetworkProfile(test_cli20. - MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - segment_type = 'vlan' - args = [name, segment_type] - position_names = ['name', 'segment_type'] - position_values = [name, segment_type] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_list_networkprofile_detail(self): - """List networkprofile: -D.""" - resources = 'network_profiles' - cmd = networkprofile.ListNetworkProfile(test_cli20.MyApp(sys.stdout), - None) - contents = [{'name': 'myname', 'segment_type': 'vlan'}] - self._test_list_resources(resources, cmd, True, - response_contents=contents) - - def test_list_networkprofile_known_option_after_unknown(self): - """List networkprofile: -- --tags a b --request-format xml.""" - resources = 'network_profiles' - cmd = networkprofile.ListNetworkProfile(test_cli20.MyApp(sys.stdout), - None) - contents = [{'name': 'myname', 'segment_type': 'vlan'}] - self._test_list_resources(resources, cmd, tags=['a', 'b'], - response_contents=contents) - - def test_list_networkprofile_fields(self): - """List networkprofile: --fields a --fields b -- --fields c d.""" - resources = 'network_profiles' - cmd = networkprofile.ListNetworkProfile(test_cli20.MyApp(sys.stdout), - None) - contents = [{'name': 'myname', 'segment_type': 'vlan'}] - self._test_list_resources(resources, cmd, - fields_1=['a', 'b'], fields_2=['c', 'd'], - response_contents=contents) - - def test_show_networkprofile(self): - """Show networkprofile: --fields id --fields name myid.""" - resource = 'network_profile' - cmd = networkprofile.ShowNetworkProfile(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, - ['id', 'name']) - - def test_delete_networkprofile(self): - """Delete networkprofile: myid.""" - resource = 'network_profile' - cmd = networkprofile.DeleteNetworkProfile(test_cli20. - MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) - - def test_create_networkprofile_trunk(self): - """Create networkprofile: myid.""" - resource = 'network_profile' - cmd = networkprofile.CreateNetworkProfile(test_cli20. - MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - segment_type = 'trunk' - args = [name, segment_type, '--sub_type', 'vlan'] - position_names = ['name', 'segment_type', ] - position_values = [name, segment_type, ] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - sub_type='vlan') - - def test_list_networkprofile_trunk_detail(self): - """List networkprofile: -D.""" - resources = 'network_profiles' - cmd = networkprofile.ListNetworkProfile(test_cli20.MyApp(sys.stdout), - None) - contents = [{'name': 'myname', 'segment_type': 'trunk', - '--sub_type': 'vlan'}] - self._test_list_resources(resources, cmd, True, - response_contents=contents) - - def test_create_networkprofile_multi_tenants(self): - """Create networkprofile with mulitple tenants: myid.""" - resource = 'network_profile' - cmd = networkprofile.CreateNetworkProfile(test_cli20. - MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - segment_type = 'vlan' - args = [name, segment_type, '--add-tenant', 'demo', - '--add-tenant', 'admin'] - position_names = ['name', 'segment_type', 'add_tenants'] - position_values = [name, segment_type, ['demo', 'admin']] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_update_networkprofile_multi_tenants(self): - resource = 'network_profile' - cmd = networkprofile.UpdateNetworkProfile(test_cli20. - MyApp(sys.stdout), None) - args = ['myid', '--add-tenant', 'service', '--add-tenant', 'demo', - '--remove-tenant', 'demo'] - extrafields = {'add_tenants': ['service', 'demo'], - 'remove_tenants': ['demo']} - self._test_update_resource(resource, cmd, 'myid', args, extrafields) diff --git a/neutronclient/tests/unit/test_cli20_policyprofile.py b/neutronclient/tests/unit/test_cli20_policyprofile.py deleted file mode 100644 index 94cbf2c57..000000000 --- a/neutronclient/tests/unit/test_cli20_policyprofile.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright 2013 Cisco Systems 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 sys - -from neutronclient.neutron.v2_0 import policyprofile -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20PolicyProfile(test_cli20.CLITestV20Base): - - def test_list_policyprofile_detail(self): - """List policyprofile: -D.""" - resources = 'policy_profiles' - cmd = policyprofile.ListPolicyProfile(test_cli20.MyApp(sys.stdout), - None) - contents = [{'name': 'myname', 'segment_type': 'vlan'}] - self._test_list_resources(resources, cmd, True, - response_contents=contents) - - def test_list_policyprofile_known_option_after_unknown(self): - """List policyprofile: -- --tags a b --request-format xml.""" - resources = 'policy_profiles' - cmd = policyprofile.ListPolicyProfile(test_cli20.MyApp(sys.stdout), - None) - contents = [{'name': 'myname', 'segment_type': 'vlan'}] - self._test_list_resources(resources, cmd, tags=['a', 'b'], - response_contents=contents) - - def test_list_policyprofile_fields(self): - """List policyprofile: --fields a --fields b -- --fields c d.""" - resources = 'policy_profiles' - cmd = policyprofile.ListPolicyProfile(test_cli20.MyApp(sys.stdout), - None) - contents = [{'name': 'myname', 'segment_type': 'vlan'}] - self._test_list_resources(resources, cmd, - fields_1=['a', 'b'], fields_2=['c', 'd'], - response_contents=contents) - - def test_show_policyprofile(self): - """Show policyprofile: --fields id --fields name myid.""" - resource = 'policy_profile' - cmd = policyprofile.ShowPolicyProfile(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, - ['id', 'name']) - - def test_update_policyprofile(self): - """Update policyprofile: myid --name myname --tags a b.""" - resource = 'policy_profile' - cmd = policyprofile.UpdatePolicyProfile(test_cli20.MyApp(sys.stdout), - None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'myname', - '--tags', 'a', 'b'], - {'name': 'myname', 'tags': ['a', 'b'], } - ) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index facc6d167..c53f00de9 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -396,14 +396,6 @@ class Client(ClientBase): gateway_devices_path = "/gateway-devices" gateway_device_path = "/gateway-devices/%s" service_providers_path = "/service-providers" - credentials_path = "/credentials" - credential_path = "/credentials/%s" - network_profiles_path = "/network_profiles" - network_profile_path = "/network_profiles/%s" - network_profile_bindings_path = "/network_profile_bindings" - policy_profiles_path = "/policy_profiles" - policy_profile_path = "/policy_profiles/%s" - policy_profile_bindings_path = "/policy_profile_bindings" metering_labels_path = "/metering/metering-labels" metering_label_path = "/metering/metering-labels/%s" metering_label_rules_path = "/metering/metering-label-rules" @@ -1477,79 +1469,6 @@ def list_service_providers(self, retrieve_all=True, **_params): return self.list('service_providers', self.service_providers_path, retrieve_all, **_params) - def list_credentials(self, **_params): - """Fetch a list of all credentials for a tenant.""" - return self.get(self.credentials_path, params=_params) - - @APIParamsCall - def show_credential(self, credential, **_params): - """Fetch a credential.""" - return self.get(self.credential_path % (credential), params=_params) - - @APIParamsCall - def create_credential(self, body=None): - """Create a new credential.""" - return self.post(self.credentials_path, body=body) - - @APIParamsCall - def update_credential(self, credential, body=None): - """Update a credential.""" - return self.put(self.credential_path % (credential), body=body) - - @APIParamsCall - def delete_credential(self, credential): - """Delete the specified credential.""" - return self.delete(self.credential_path % (credential)) - - def list_network_profile_bindings(self, **params): - """Fetch a list of all tenants associated for a network profile.""" - return self.get(self.network_profile_bindings_path, params=params) - - @APIParamsCall - def list_network_profiles(self, **params): - """Fetch a list of all network profiles for a tenant.""" - return self.get(self.network_profiles_path, params=params) - - @APIParamsCall - def show_network_profile(self, profile, **params): - """Fetch a network profile.""" - return self.get(self.network_profile_path % (profile), params=params) - - @APIParamsCall - def create_network_profile(self, body=None): - """Create a network profile.""" - return self.post(self.network_profiles_path, body=body) - - @APIParamsCall - def update_network_profile(self, profile, body=None): - """Update a network profile.""" - return self.put(self.network_profile_path % (profile), body=body) - - @APIParamsCall - def delete_network_profile(self, profile): - """Delete the network profile.""" - return self.delete(self.network_profile_path % profile) - - @APIParamsCall - def list_policy_profile_bindings(self, **params): - """Fetch a list of all tenants associated for a policy profile.""" - return self.get(self.policy_profile_bindings_path, params=params) - - @APIParamsCall - def list_policy_profiles(self, **params): - """Fetch a list of all network profiles for a tenant.""" - return self.get(self.policy_profiles_path, params=params) - - @APIParamsCall - def show_policy_profile(self, profile, **params): - """Fetch a network profile.""" - return self.get(self.policy_profile_path % (profile), params=params) - - @APIParamsCall - def update_policy_profile(self, profile, body=None): - """Update a policy profile.""" - return self.put(self.policy_profile_path % (profile), body=body) - @APIParamsCall def create_metering_label(self, body=None): """Creates a metering label.""" From 7afe02b7f823d6876592d6371c25c469ec67dd25 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Thu, 17 Sep 2015 12:16:54 +0000 Subject: [PATCH 257/845] Updated from global requirements Change-Id: I1a0f06306f14d3ee897912edf197c48380dbef70 --- requirements.txt | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index e8184e487..6a677ab02 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ # The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. -pbr<2.0,>=1.6 +pbr>=1.6 argparse cliff>=1.14.0 # Apache-2.0 iso8601>=0.1.9 diff --git a/setup.py b/setup.py index d8080d05c..782bb21f0 100644 --- a/setup.py +++ b/setup.py @@ -25,5 +25,5 @@ pass setuptools.setup( - setup_requires=['pbr>=1.3'], + setup_requires=['pbr>=1.8'], pbr=True) From 716eb017d35829c18745c4c0279b895784c297f1 Mon Sep 17 00:00:00 2001 From: Henry Gessau Date: Mon, 21 Sep 2015 14:44:04 +0000 Subject: [PATCH 258/845] Revert "Remove Cisco-specific neutron client commands" This reverts commit 8292cb85f8830e65284736ea1f9bae2f344c084a. This cannot go in 3.x since it requires a version bump. Will repropose for Mitaka (4.x client). Change-Id: Ia89d3e3c9c4a27ffbe3089354b4eaeca527de8ae --- neutronclient/neutron/v2_0/credential.py | 73 +++++++++ neutronclient/neutron/v2_0/networkprofile.py | 148 ++++++++++++++++++ neutronclient/neutron/v2_0/policyprofile.py | 72 +++++++++ neutronclient/shell.py | 15 ++ neutronclient/tests/unit/test_cli20.py | 3 +- .../tests/unit/test_cli20_credential.py | 77 +++++++++ .../tests/unit/test_cli20_networkprofile.py | 133 ++++++++++++++++ .../tests/unit/test_cli20_policyprofile.py | 71 +++++++++ neutronclient/v2_0/client.py | 81 ++++++++++ 9 files changed, 672 insertions(+), 1 deletion(-) create mode 100644 neutronclient/neutron/v2_0/credential.py create mode 100644 neutronclient/neutron/v2_0/networkprofile.py create mode 100644 neutronclient/neutron/v2_0/policyprofile.py create mode 100644 neutronclient/tests/unit/test_cli20_credential.py create mode 100644 neutronclient/tests/unit/test_cli20_networkprofile.py create mode 100644 neutronclient/tests/unit/test_cli20_policyprofile.py diff --git a/neutronclient/neutron/v2_0/credential.py b/neutronclient/neutron/v2_0/credential.py new file mode 100644 index 000000000..c397627b7 --- /dev/null +++ b/neutronclient/neutron/v2_0/credential.py @@ -0,0 +1,73 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from neutronclient.i18n import _ +from neutronclient.neutron import v2_0 as neutronV20 + + +class ListCredential(neutronV20.ListCommand): + """List credentials that belong to a given tenant.""" + + resource = 'credential' + _formatters = {} + list_columns = ['credential_id', 'credential_name', 'user_name', + 'password', 'type'] + + +class ShowCredential(neutronV20.ShowCommand): + """Show information of a given credential.""" + + resource = 'credential' + allow_names = False + + +class CreateCredential(neutronV20.CreateCommand): + """Create a credential.""" + + resource = 'credential' + + def add_known_arguments(self, parser): + parser.add_argument( + 'credential_name', + help=_('Name/IP address for credential.')) + parser.add_argument( + 'credential_type', + help=_('Type of the credential.')) + parser.add_argument( + '--username', + help=_('Username for the credential.')) + parser.add_argument( + '--password', + help=_('Password for the credential.')) + + def args2body(self, parsed_args): + body = {'credential': { + 'credential_name': parsed_args.credential_name}} + + if parsed_args.credential_type: + body['credential'].update({'type': + parsed_args.credential_type}) + if parsed_args.username: + body['credential'].update({'user_name': + parsed_args.username}) + if parsed_args.password: + body['credential'].update({'password': + parsed_args.password}) + return body + + +class DeleteCredential(neutronV20.DeleteCommand): + """Delete a given credential.""" + + resource = 'credential' + allow_names = False diff --git a/neutronclient/neutron/v2_0/networkprofile.py b/neutronclient/neutron/v2_0/networkprofile.py new file mode 100644 index 000000000..ed85875f5 --- /dev/null +++ b/neutronclient/neutron/v2_0/networkprofile.py @@ -0,0 +1,148 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from __future__ import print_function + +from neutronclient.i18n import _ +from neutronclient.neutron import v2_0 as neutronV20 +from neutronclient.neutron.v2_0 import parse_args_to_dict + +RESOURCE = 'network_profile' +SEGMENT_TYPE_CHOICES = ['vlan', 'overlay', 'multi-segment', 'trunk'] + + +class ListNetworkProfile(neutronV20.ListCommand): + """List network profiles that belong to a given tenant.""" + + resource = RESOURCE + _formatters = {} + list_columns = ['id', 'name', 'segment_type', 'sub_type', 'segment_range', + 'physical_network', 'multicast_ip_index', + 'multicast_ip_range'] + + +class ShowNetworkProfile(neutronV20.ShowCommand): + """Show information of a given network profile.""" + + resource = RESOURCE + allow_names = True + + +class CreateNetworkProfile(neutronV20.CreateCommand): + """Create a network profile.""" + + resource = RESOURCE + + def add_known_arguments(self, parser): + parser.add_argument('name', + help=_('Name for network profile.')) + parser.add_argument('segment_type', + choices=SEGMENT_TYPE_CHOICES, + help='Segment type.') + # TODO(Abhishek): Check on sub-type choices depending on segment_type + parser.add_argument('--sub_type', + help=_('Sub-type for the segment. Available ' + 'sub-types for overlay segments: ' + 'native, enhanced; For trunk segments: ' + 'vlan, overlay.')) + parser.add_argument('--segment_range', + help=_('Range for the segment.')) + parser.add_argument('--physical_network', + help=_('Name for the physical network.')) + parser.add_argument('--multicast_ip_range', + help=_('Multicast IPv4 range.')) + parser.add_argument("--add-tenant", + action='append', dest='add_tenants', + help=_("Add tenant to the network profile. " + "You can repeat this option.")) + + def args2body(self, parsed_args): + body = {'network_profile': {'name': parsed_args.name}} + if parsed_args.segment_type: + body['network_profile'].update({'segment_type': + parsed_args.segment_type}) + if parsed_args.sub_type: + body['network_profile'].update({'sub_type': + parsed_args.sub_type}) + if parsed_args.segment_range: + body['network_profile'].update({'segment_range': + parsed_args.segment_range}) + if parsed_args.physical_network: + body['network_profile'].update({'physical_network': + parsed_args.physical_network}) + if parsed_args.multicast_ip_range: + body['network_profile'].update({'multicast_ip_range': + parsed_args.multicast_ip_range}) + if parsed_args.add_tenants: + body['network_profile'].update({'add_tenants': + parsed_args.add_tenants}) + return body + + +class DeleteNetworkProfile(neutronV20.DeleteCommand): + """Delete a given network profile.""" + + resource = RESOURCE + allow_names = True + + +class UpdateNetworkProfile(neutronV20.UpdateCommand): + """Update network profile's information.""" + + resource = RESOURCE + + def add_known_arguments(self, parser): + parser.add_argument("--remove-tenant", + action='append', dest='remove_tenants', + help=_("Remove tenant from the network profile. " + "You can repeat this option.")) + parser.add_argument("--add-tenant", + action='append', dest='add_tenants', + help=_("Add tenant to the network profile. " + "You can repeat this option.")) + + def args2body(self, parsed_args): + body = {'network_profile': {}} + if parsed_args.remove_tenants: + body['network_profile']['remove_tenants'] = (parsed_args. + remove_tenants) + if parsed_args.add_tenants: + body['network_profile']['add_tenants'] = parsed_args.add_tenants + return body + + +# Aaron: This function is deprecated +class UpdateNetworkProfileV2(neutronV20.NeutronCommand): + + api = 'network' + resource = RESOURCE + + def get_parser(self, prog_name): + parser = super(UpdateNetworkProfileV2, self).get_parser(prog_name) + parser.add_argument("--remove-tenant", + help="Remove tenant from the network profile.") + return parser + + def run(self, parsed_args): + self.log.debug('run(%s)' % parsed_args) + neutron_client = self.get_client() + neutron_client.format = parsed_args.request_format + data = {self.resource: parse_args_to_dict(parsed_args)} + if parsed_args.remove_tenant: + data[self.resource]['remove_tenant'] = parsed_args.remove_tenant + neutron_client.update_network_profile(parsed_args.id, + {self.resource: data}) + print((_('Updated %(resource)s: %(id)s') % + {'id': parsed_args.id, 'resource': self.resource}), + file=self.app.stdout) + return diff --git a/neutronclient/neutron/v2_0/policyprofile.py b/neutronclient/neutron/v2_0/policyprofile.py new file mode 100644 index 000000000..7ff00db9e --- /dev/null +++ b/neutronclient/neutron/v2_0/policyprofile.py @@ -0,0 +1,72 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from __future__ import print_function + +from neutronclient.i18n import _ +from neutronclient.neutron import v2_0 as neutronV20 +from neutronclient.neutron.v2_0 import parse_args_to_dict + +RESOURCE = 'policy_profile' + + +class ListPolicyProfile(neutronV20.ListCommand): + """List policy profiles that belong to a given tenant.""" + + resource = RESOURCE + _formatters = {} + list_columns = ['id', 'name'] + + +class ShowPolicyProfile(neutronV20.ShowCommand): + """Show information of a given policy profile.""" + + resource = RESOURCE + allow_names = True + + +class UpdatePolicyProfile(neutronV20.UpdateCommand): + """Update policy profile's information.""" + + resource = RESOURCE + + +class UpdatePolicyProfileV2(neutronV20.UpdateCommand): + """Update policy profile's information.""" + + api = 'network' + resource = RESOURCE + + def get_parser(self, prog_name): + parser = super(UpdatePolicyProfileV2, self).get_parser(prog_name) + parser.add_argument("--add-tenant", + help=_("Add tenant to the policy profile.")) + parser.add_argument("--remove-tenant", + help=_("Remove tenant from the policy profile.")) + return parser + + def run(self, parsed_args): + self.log.debug('run(%s)' % parsed_args) + neutron_client = self.get_client() + neutron_client.format = parsed_args.request_format + data = {self.resource: parse_args_to_dict(parsed_args)} + if parsed_args.add_tenant: + data[self.resource]['add_tenant'] = parsed_args.add_tenant + if parsed_args.remove_tenant: + data[self.resource]['remove_tenant'] = parsed_args.remove_tenant + neutron_client.update_policy_profile(parsed_args.id, + {self.resource: data}) + print((_('Updated %(resource)s: %(id)s') % + {'id': parsed_args.id, 'resource': self.resource}), + file=self.app.stdout) + return diff --git a/neutronclient/shell.py b/neutronclient/shell.py index fae2a219d..cc50a95e5 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -48,6 +48,7 @@ from neutronclient.neutron.v2_0 import address_scope from neutronclient.neutron.v2_0 import agent from neutronclient.neutron.v2_0 import agentscheduler +from neutronclient.neutron.v2_0 import credential from neutronclient.neutron.v2_0 import extension from neutronclient.neutron.v2_0 import floatingip from neutronclient.neutron.v2_0.fw import firewall @@ -66,8 +67,10 @@ from neutronclient.neutron.v2_0.nec import packetfilter from neutronclient.neutron.v2_0 import netpartition from neutronclient.neutron.v2_0 import network +from neutronclient.neutron.v2_0 import networkprofile from neutronclient.neutron.v2_0.nsx import networkgateway from neutronclient.neutron.v2_0.nsx import qos_queue +from neutronclient.neutron.v2_0 import policyprofile from neutronclient.neutron.v2_0 import port from neutronclient.neutron.v2_0.qos import bandwidth_limit_rule from neutronclient.neutron.v2_0.qos import policy as qos_policy @@ -303,6 +306,18 @@ class BashCompletionCommand(openstack_command.OpenStackCommand): 'firewall-create': firewall.CreateFirewall, 'firewall-update': firewall.UpdateFirewall, 'firewall-delete': firewall.DeleteFirewall, + 'cisco-credential-list': credential.ListCredential, + 'cisco-credential-show': credential.ShowCredential, + 'cisco-credential-create': credential.CreateCredential, + 'cisco-credential-delete': credential.DeleteCredential, + 'cisco-network-profile-list': networkprofile.ListNetworkProfile, + 'cisco-network-profile-show': networkprofile.ShowNetworkProfile, + 'cisco-network-profile-create': networkprofile.CreateNetworkProfile, + 'cisco-network-profile-delete': networkprofile.DeleteNetworkProfile, + 'cisco-network-profile-update': networkprofile.UpdateNetworkProfile, + 'cisco-policy-profile-list': policyprofile.ListPolicyProfile, + 'cisco-policy-profile-show': policyprofile.ShowPolicyProfile, + 'cisco-policy-profile-update': policyprofile.UpdatePolicyProfile, 'ipsec-site-connection-list': ( ipsec_site_connection.ListIPsecSiteConnection ), diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 779022363..94d979cf0 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -42,7 +42,8 @@ non_admin_status_resources = ['subnet', 'floatingip', 'security_group', 'security_group_rule', 'qos_queue', 'network_gateway', 'gateway_device', - 'ikepolicy', + 'credential', 'network_profile', + 'policy_profile', 'ikepolicy', 'ipsecpolicy', 'metering_label', 'metering_label_rule', 'net_partition', 'fox_socket', 'subnetpool', diff --git a/neutronclient/tests/unit/test_cli20_credential.py b/neutronclient/tests/unit/test_cli20_credential.py new file mode 100644 index 000000000..4f4a1d35d --- /dev/null +++ b/neutronclient/tests/unit/test_cli20_credential.py @@ -0,0 +1,77 @@ +# Copyright 2013 Cisco Systems 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 sys + +from neutronclient.neutron.v2_0 import credential +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20Credential(test_cli20.CLITestV20Base): + + def test_create_credential(self): + """Create credential: myid.""" + resource = 'credential' + cmd = credential.CreateCredential(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + type = 'mytype' + args = [name, type] + position_names = ['credential_name', 'type'] + position_values = [name, type] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_list_credentials_detail(self): + """List credentials: -D.""" + resources = 'credentials' + cmd = credential.ListCredential(test_cli20.MyApp(sys.stdout), None) + contents = [{'credential_name': 'myname', 'type': 'mytype'}] + self._test_list_resources(resources, cmd, True, + response_contents=contents) + + def test_list_credential_known_option_after_unknown(self): + """List credential: -- --tags a b --request-format xml.""" + resources = 'credentials' + cmd = credential.ListCredential(test_cli20.MyApp(sys.stdout), None) + contents = [{'credential_name': 'myname', 'type': 'mytype'}] + self._test_list_resources(resources, cmd, tags=['a', 'b'], + response_contents=contents) + + def test_list_credential_fields(self): + """List credential: --fields a --fields b -- --fields c d.""" + resources = 'credentials' + cmd = credential.ListCredential(test_cli20.MyApp(sys.stdout), None) + contents = [{'credential_name': 'myname', 'type': 'mytype'}] + self._test_list_resources(resources, cmd, + fields_1=['a', 'b'], fields_2=['c', 'd'], + response_contents=contents) + + def test_show_credential(self): + """Show credential: --fields id --fields name myid.""" + resource = 'credential' + cmd = credential.ShowCredential(test_cli20.MyApp(sys.stdout), None) + args = ['--fields', 'id', '--fields', 'name', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, args, + ['id', 'name']) + + def test_delete_credential(self): + """Delete credential: myid.""" + resource = 'credential' + cmd = credential.DeleteCredential(test_cli20.MyApp(sys.stdout), None) + myid = 'myid' + args = [myid] + self._test_delete_resource(resource, cmd, myid, args) diff --git a/neutronclient/tests/unit/test_cli20_networkprofile.py b/neutronclient/tests/unit/test_cli20_networkprofile.py new file mode 100644 index 000000000..c5a735338 --- /dev/null +++ b/neutronclient/tests/unit/test_cli20_networkprofile.py @@ -0,0 +1,133 @@ +# Copyright 2013 Cisco Systems 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 sys + +from neutronclient.neutron.v2_0 import networkprofile +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20NetworkProfile(test_cli20.CLITestV20Base): + + def test_create_networkprofile(self): + """Create networkprofile: myid.""" + resource = 'network_profile' + cmd = networkprofile.CreateNetworkProfile(test_cli20. + MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + segment_type = 'vlan' + args = [name, segment_type] + position_names = ['name', 'segment_type'] + position_values = [name, segment_type] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_list_networkprofile_detail(self): + """List networkprofile: -D.""" + resources = 'network_profiles' + cmd = networkprofile.ListNetworkProfile(test_cli20.MyApp(sys.stdout), + None) + contents = [{'name': 'myname', 'segment_type': 'vlan'}] + self._test_list_resources(resources, cmd, True, + response_contents=contents) + + def test_list_networkprofile_known_option_after_unknown(self): + """List networkprofile: -- --tags a b --request-format xml.""" + resources = 'network_profiles' + cmd = networkprofile.ListNetworkProfile(test_cli20.MyApp(sys.stdout), + None) + contents = [{'name': 'myname', 'segment_type': 'vlan'}] + self._test_list_resources(resources, cmd, tags=['a', 'b'], + response_contents=contents) + + def test_list_networkprofile_fields(self): + """List networkprofile: --fields a --fields b -- --fields c d.""" + resources = 'network_profiles' + cmd = networkprofile.ListNetworkProfile(test_cli20.MyApp(sys.stdout), + None) + contents = [{'name': 'myname', 'segment_type': 'vlan'}] + self._test_list_resources(resources, cmd, + fields_1=['a', 'b'], fields_2=['c', 'd'], + response_contents=contents) + + def test_show_networkprofile(self): + """Show networkprofile: --fields id --fields name myid.""" + resource = 'network_profile' + cmd = networkprofile.ShowNetworkProfile(test_cli20.MyApp(sys.stdout), + None) + args = ['--fields', 'id', '--fields', 'name', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, args, + ['id', 'name']) + + def test_delete_networkprofile(self): + """Delete networkprofile: myid.""" + resource = 'network_profile' + cmd = networkprofile.DeleteNetworkProfile(test_cli20. + MyApp(sys.stdout), None) + myid = 'myid' + args = [myid] + self._test_delete_resource(resource, cmd, myid, args) + + def test_create_networkprofile_trunk(self): + """Create networkprofile: myid.""" + resource = 'network_profile' + cmd = networkprofile.CreateNetworkProfile(test_cli20. + MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + segment_type = 'trunk' + args = [name, segment_type, '--sub_type', 'vlan'] + position_names = ['name', 'segment_type', ] + position_values = [name, segment_type, ] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values, + sub_type='vlan') + + def test_list_networkprofile_trunk_detail(self): + """List networkprofile: -D.""" + resources = 'network_profiles' + cmd = networkprofile.ListNetworkProfile(test_cli20.MyApp(sys.stdout), + None) + contents = [{'name': 'myname', 'segment_type': 'trunk', + '--sub_type': 'vlan'}] + self._test_list_resources(resources, cmd, True, + response_contents=contents) + + def test_create_networkprofile_multi_tenants(self): + """Create networkprofile with mulitple tenants: myid.""" + resource = 'network_profile' + cmd = networkprofile.CreateNetworkProfile(test_cli20. + MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + segment_type = 'vlan' + args = [name, segment_type, '--add-tenant', 'demo', + '--add-tenant', 'admin'] + position_names = ['name', 'segment_type', 'add_tenants'] + position_values = [name, segment_type, ['demo', 'admin']] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_update_networkprofile_multi_tenants(self): + resource = 'network_profile' + cmd = networkprofile.UpdateNetworkProfile(test_cli20. + MyApp(sys.stdout), None) + args = ['myid', '--add-tenant', 'service', '--add-tenant', 'demo', + '--remove-tenant', 'demo'] + extrafields = {'add_tenants': ['service', 'demo'], + 'remove_tenants': ['demo']} + self._test_update_resource(resource, cmd, 'myid', args, extrafields) diff --git a/neutronclient/tests/unit/test_cli20_policyprofile.py b/neutronclient/tests/unit/test_cli20_policyprofile.py new file mode 100644 index 000000000..94cbf2c57 --- /dev/null +++ b/neutronclient/tests/unit/test_cli20_policyprofile.py @@ -0,0 +1,71 @@ +# Copyright 2013 Cisco Systems 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 sys + +from neutronclient.neutron.v2_0 import policyprofile +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20PolicyProfile(test_cli20.CLITestV20Base): + + def test_list_policyprofile_detail(self): + """List policyprofile: -D.""" + resources = 'policy_profiles' + cmd = policyprofile.ListPolicyProfile(test_cli20.MyApp(sys.stdout), + None) + contents = [{'name': 'myname', 'segment_type': 'vlan'}] + self._test_list_resources(resources, cmd, True, + response_contents=contents) + + def test_list_policyprofile_known_option_after_unknown(self): + """List policyprofile: -- --tags a b --request-format xml.""" + resources = 'policy_profiles' + cmd = policyprofile.ListPolicyProfile(test_cli20.MyApp(sys.stdout), + None) + contents = [{'name': 'myname', 'segment_type': 'vlan'}] + self._test_list_resources(resources, cmd, tags=['a', 'b'], + response_contents=contents) + + def test_list_policyprofile_fields(self): + """List policyprofile: --fields a --fields b -- --fields c d.""" + resources = 'policy_profiles' + cmd = policyprofile.ListPolicyProfile(test_cli20.MyApp(sys.stdout), + None) + contents = [{'name': 'myname', 'segment_type': 'vlan'}] + self._test_list_resources(resources, cmd, + fields_1=['a', 'b'], fields_2=['c', 'd'], + response_contents=contents) + + def test_show_policyprofile(self): + """Show policyprofile: --fields id --fields name myid.""" + resource = 'policy_profile' + cmd = policyprofile.ShowPolicyProfile(test_cli20.MyApp(sys.stdout), + None) + args = ['--fields', 'id', '--fields', 'name', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, args, + ['id', 'name']) + + def test_update_policyprofile(self): + """Update policyprofile: myid --name myname --tags a b.""" + resource = 'policy_profile' + cmd = policyprofile.UpdatePolicyProfile(test_cli20.MyApp(sys.stdout), + None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--name', 'myname', + '--tags', 'a', 'b'], + {'name': 'myname', 'tags': ['a', 'b'], } + ) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index c53f00de9..facc6d167 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -396,6 +396,14 @@ class Client(ClientBase): gateway_devices_path = "/gateway-devices" gateway_device_path = "/gateway-devices/%s" service_providers_path = "/service-providers" + credentials_path = "/credentials" + credential_path = "/credentials/%s" + network_profiles_path = "/network_profiles" + network_profile_path = "/network_profiles/%s" + network_profile_bindings_path = "/network_profile_bindings" + policy_profiles_path = "/policy_profiles" + policy_profile_path = "/policy_profiles/%s" + policy_profile_bindings_path = "/policy_profile_bindings" metering_labels_path = "/metering/metering-labels" metering_label_path = "/metering/metering-labels/%s" metering_label_rules_path = "/metering/metering-label-rules" @@ -1469,6 +1477,79 @@ def list_service_providers(self, retrieve_all=True, **_params): return self.list('service_providers', self.service_providers_path, retrieve_all, **_params) + def list_credentials(self, **_params): + """Fetch a list of all credentials for a tenant.""" + return self.get(self.credentials_path, params=_params) + + @APIParamsCall + def show_credential(self, credential, **_params): + """Fetch a credential.""" + return self.get(self.credential_path % (credential), params=_params) + + @APIParamsCall + def create_credential(self, body=None): + """Create a new credential.""" + return self.post(self.credentials_path, body=body) + + @APIParamsCall + def update_credential(self, credential, body=None): + """Update a credential.""" + return self.put(self.credential_path % (credential), body=body) + + @APIParamsCall + def delete_credential(self, credential): + """Delete the specified credential.""" + return self.delete(self.credential_path % (credential)) + + def list_network_profile_bindings(self, **params): + """Fetch a list of all tenants associated for a network profile.""" + return self.get(self.network_profile_bindings_path, params=params) + + @APIParamsCall + def list_network_profiles(self, **params): + """Fetch a list of all network profiles for a tenant.""" + return self.get(self.network_profiles_path, params=params) + + @APIParamsCall + def show_network_profile(self, profile, **params): + """Fetch a network profile.""" + return self.get(self.network_profile_path % (profile), params=params) + + @APIParamsCall + def create_network_profile(self, body=None): + """Create a network profile.""" + return self.post(self.network_profiles_path, body=body) + + @APIParamsCall + def update_network_profile(self, profile, body=None): + """Update a network profile.""" + return self.put(self.network_profile_path % (profile), body=body) + + @APIParamsCall + def delete_network_profile(self, profile): + """Delete the network profile.""" + return self.delete(self.network_profile_path % profile) + + @APIParamsCall + def list_policy_profile_bindings(self, **params): + """Fetch a list of all tenants associated for a policy profile.""" + return self.get(self.policy_profile_bindings_path, params=params) + + @APIParamsCall + def list_policy_profiles(self, **params): + """Fetch a list of all network profiles for a tenant.""" + return self.get(self.policy_profiles_path, params=params) + + @APIParamsCall + def show_policy_profile(self, profile, **params): + """Fetch a network profile.""" + return self.get(self.policy_profile_path % (profile), params=params) + + @APIParamsCall + def update_policy_profile(self, profile, body=None): + """Update a policy profile.""" + return self.put(self.policy_profile_path % (profile), body=body) + @APIParamsCall def create_metering_label(self, body=None): """Creates a metering label.""" From 4f82e99a4b2a2074938d146d5c2193b6054a591f Mon Sep 17 00:00:00 2001 From: Monty Taylor Date: Mon, 21 Sep 2015 14:53:55 +0000 Subject: [PATCH 259/845] Change ignore-errors to ignore_errors Needed for coverage 4.0 Change-Id: I7654eb768efbd440e4e812cee6bf85daf0c96cb5 --- .coveragerc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.coveragerc b/.coveragerc index 83f3102c1..183b7a648 100644 --- a/.coveragerc +++ b/.coveragerc @@ -4,4 +4,4 @@ source = neutronclient omit = neutronclient/openstack/*,neutronclient/tests/* [report] -ignore-errors = True +ignore_errors = True From 3e115991c1cf9ec7cf4252440fa8d6015a5f53ce Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Mon, 21 Sep 2015 18:59:29 +0000 Subject: [PATCH 260/845] Updated from global requirements Change-Id: I3e494d66dcaea3b5425d7766ae766f3722ff4705 --- test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index 174934641..160b24689 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -16,4 +16,4 @@ requests-mock>=0.6.0 # Apache-2.0 sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 testrepository>=0.0.18 testtools>=1.4.0 -tempest-lib>=0.6.1 +tempest-lib>=0.8.0 From b915201dbfd81b7dbb62c51a30576d596c3a3fa7 Mon Sep 17 00:00:00 2001 From: Paul Michali Date: Wed, 19 Aug 2015 12:16:05 +0000 Subject: [PATCH 261/845] Enable VPN test cases With the gate hook changes from Ieeaca1375d68705509f4e05f10cb35c0fa0b9582 and new jobs from I2db939bf99288c0cdec06cdd49fec3bdc72e5253, this commit does several things: * Moves test modules to tests/functional/core/ * Moves VPN test cases to new module & places in tests/functional/adv-svcs * Modifies tox.ini to support 'functional' and 'functional-adv-svcs' test This commit will be used to test the experimental jobs, one that runs the same tests as before, one that runs VPN tests using the VPN DevStack plugin via the gate-hook.sh. Modified post test hook so that test results are properly created (was not seeing results from testr, prior to this). Goal is to place all advanced services into tests/functional/adv-svcs/ and use plugins, as needed in the gate hook. Change-Id: I1e3d19e51a1cbd1bc947bbf9927260cd4d73841a Depends-On: I2db939bf99288c0cdec06cdd49fec3bdc72e5253 Partial-Bug: 1484148 --- .../tests/functional/adv-svcs/__init__.py | 0 .../adv-svcs/test_readonly_neutron_vpn.py | 55 +++++++++++++++++++ .../tests/functional/core/__init__.py | 0 .../functional/{ => core}/test_clientlib.py | 0 .../{ => core}/test_readonly_neutron.py | 33 ----------- .../{ => core}/test_subnet_create.py | 0 .../tests/functional/hooks/gate_hook.sh | 4 +- .../tests/functional/hooks/post_test_hook.sh | 42 ++++++++++---- tox.ini | 8 ++- 9 files changed, 97 insertions(+), 45 deletions(-) create mode 100644 neutronclient/tests/functional/adv-svcs/__init__.py create mode 100644 neutronclient/tests/functional/adv-svcs/test_readonly_neutron_vpn.py create mode 100644 neutronclient/tests/functional/core/__init__.py rename neutronclient/tests/functional/{ => core}/test_clientlib.py (100%) rename neutronclient/tests/functional/{ => core}/test_readonly_neutron.py (79%) rename neutronclient/tests/functional/{ => core}/test_subnet_create.py (100%) diff --git a/neutronclient/tests/functional/adv-svcs/__init__.py b/neutronclient/tests/functional/adv-svcs/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/tests/functional/adv-svcs/test_readonly_neutron_vpn.py b/neutronclient/tests/functional/adv-svcs/test_readonly_neutron_vpn.py new file mode 100644 index 000000000..ab3aba018 --- /dev/null +++ b/neutronclient/tests/functional/adv-svcs/test_readonly_neutron_vpn.py @@ -0,0 +1,55 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from neutronclient.tests.functional import base + + +class SimpleReadOnlyNeutronVpnClientTest(base.ClientTestBase): + + """This is a first pass at a simple read only python-neutronclient test. + This only exercises vpn based client commands that are read only. + + This should test commands: + * as a regular user + * as a admin user + * with and without optional parameters + * initially just check return codes, and later test command outputs + + """ + + def test_neutron_vpn_ikepolicy_list(self): + ikepolicy = self.parser.listing(self.neutron('vpn-ikepolicy-list')) + self.assertTableStruct(ikepolicy, ['id', 'name', + 'auth_algorithm', + 'encryption_algorithm', + 'ike_version', 'pfs']) + + def test_neutron_vpn_ipsecpolicy_list(self): + ipsecpolicy = self.parser.listing(self.neutron('vpn-ipsecpolicy-list')) + self.assertTableStruct(ipsecpolicy, ['id', 'name', + 'auth_algorithm', + 'encryption_algorithm', + 'pfs']) + + def test_neutron_vpn_service_list(self): + vpn_list = self.parser.listing(self.neutron('vpn-service-list')) + self.assertTableStruct(vpn_list, ['id', 'name', + 'router_id', 'status']) + + def test_neutron_ipsec_site_connection_list(self): + ipsec_site = self.parser.listing(self.neutron + ('ipsec-site-connection-list')) + self.assertTableStruct(ipsec_site, ['id', 'name', + 'peer_address', + 'peer_cidrs', + 'route_mode', + 'auth_mode', 'status']) diff --git a/neutronclient/tests/functional/core/__init__.py b/neutronclient/tests/functional/core/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/tests/functional/test_clientlib.py b/neutronclient/tests/functional/core/test_clientlib.py similarity index 100% rename from neutronclient/tests/functional/test_clientlib.py rename to neutronclient/tests/functional/core/test_clientlib.py diff --git a/neutronclient/tests/functional/test_readonly_neutron.py b/neutronclient/tests/functional/core/test_readonly_neutron.py similarity index 79% rename from neutronclient/tests/functional/test_readonly_neutron.py rename to neutronclient/tests/functional/core/test_readonly_neutron.py index c088c5f92..d6ed1b8f7 100644 --- a/neutronclient/tests/functional/test_readonly_neutron.py +++ b/neutronclient/tests/functional/core/test_readonly_neutron.py @@ -11,7 +11,6 @@ # under the License. import re -import unittest2 from tempest_lib import exceptions @@ -124,38 +123,6 @@ def test_neutron_subnet_list(self): self.assertTableStruct(subnet_list, ['id', 'name', 'cidr', 'allocation_pools']) - @unittest2.skip("Skipping until 1484148 is resolved") - def test_neutron_vpn_ikepolicy_list(self): - ikepolicy = self.parser.listing(self.neutron('vpn-ikepolicy-list')) - self.assertTableStruct(ikepolicy, ['id', 'name', - 'auth_algorithm', - 'encryption_algorithm', - 'ike_version', 'pfs']) - - @unittest2.skip("Skipping until 1484148 is resolved") - def test_neutron_vpn_ipsecpolicy_list(self): - ipsecpolicy = self.parser.listing(self.neutron('vpn-ipsecpolicy-list')) - self.assertTableStruct(ipsecpolicy, ['id', 'name', - 'auth_algorithm', - 'encryption_algorithm', - 'pfs']) - - @unittest2.skip("Skipping until 1484148 is resolved") - def test_neutron_vpn_service_list(self): - vpn_list = self.parser.listing(self.neutron('vpn-service-list')) - self.assertTableStruct(vpn_list, ['id', 'name', - 'router_id', 'status']) - - @unittest2.skip("Skipping until 1484148 is resolved") - def test_neutron_ipsec_site_connection_list(self): - ipsec_site = self.parser.listing(self.neutron - ('ipsec-site-connection-list')) - self.assertTableStruct(ipsec_site, ['id', 'name', - 'peer_address', - 'peer_cidrs', - 'route_mode', - 'auth_mode', 'status']) - def test_neutron_firewall_list(self): firewall_list = self.parser.listing(self.neutron ('firewall-list')) diff --git a/neutronclient/tests/functional/test_subnet_create.py b/neutronclient/tests/functional/core/test_subnet_create.py similarity index 100% rename from neutronclient/tests/functional/test_subnet_create.py rename to neutronclient/tests/functional/core/test_subnet_create.py diff --git a/neutronclient/tests/functional/hooks/gate_hook.sh b/neutronclient/tests/functional/hooks/gate_hook.sh index 4594c2249..9f1d8eb5b 100644 --- a/neutronclient/tests/functional/hooks/gate_hook.sh +++ b/neutronclient/tests/functional/hooks/gate_hook.sh @@ -3,11 +3,13 @@ set -ex +source $BASE/new/devstack-gate/functions.sh +start_timer VENV=${1:-"functional"} -if [ "$VENV" == "functional-vpn" ] +if [ "$VENV" == "functional-adv-svcs" ] then export DEVSTACK_LOCAL_CONFIG="enable_plugin neutron-vpnaas git://git.openstack.org/openstack/neutron-vpnaas" fi diff --git a/neutronclient/tests/functional/hooks/post_test_hook.sh b/neutronclient/tests/functional/hooks/post_test_hook.sh index 87cc29a65..b23fb4320 100755 --- a/neutronclient/tests/functional/hooks/post_test_hook.sh +++ b/neutronclient/tests/functional/hooks/post_test_hook.sh @@ -14,21 +14,43 @@ # This script is executed inside post_test_hook function in devstack gate. +SCRIPTS_DIR="/usr/os-testr-env/bin/" + +function generate_test_logs { + local path="$1" + # Compress all $path/*.txt files and move the directories holding those + # files to /opt/stack/logs. Files with .log suffix have their + # suffix changed to .txt (so browsers will know to open the compressed + # files and not download them). + if [ -d "$path" ] + then + sudo find $path -iname "*.log" -type f -exec mv {} {}.txt \; -exec gzip -9 {}.txt \; + sudo mv $path/* /opt/stack/logs/ + fi +} + function generate_testr_results { - if [ -f .testrepository/0 ]; then - sudo .tox/functional/bin/testr last --subunit > $WORKSPACE/testrepository.subunit - sudo mv $WORKSPACE/testrepository.subunit $BASE/logs/testrepository.subunit - sudo /usr/os-testr-env/bin/subunit2html $BASE/logs/testrepository.subunit $BASE/logs/testr_results.html - sudo gzip -9 $BASE/logs/testrepository.subunit - sudo gzip -9 $BASE/logs/testr_results.html - sudo chown jenkins:jenkins $BASE/logs/testrepository.subunit.gz $BASE/logs/testr_results.html.gz - sudo chmod a+r $BASE/logs/testrepository.subunit.gz $BASE/logs/testr_results.html.gz + # Give job user rights to access tox logs + sudo -H -u $owner chmod o+rw . + sudo -H -u $owner chmod o+rw -R .testrepository + if [ -f ".testrepository/0" ] ; then + .tox/$VENV/bin/subunit-1to2 < .testrepository/0 > ./testrepository.subunit + $SCRIPTS_DIR/subunit2html ./testrepository.subunit testr_results.html + gzip -9 ./testrepository.subunit + gzip -9 ./testr_results.html + sudo mv ./*.gz /opt/stack/logs/ + fi + + if [ "$venv" == "functional" ] || [ "$venv" == "functional-adv-svcs" ] + then + generate_test_logs "/tmp/${venv}-logs" fi } export NEUTRONCLIENT_DIR="$BASE/new/python-neutronclient" +owner=jenkins -sudo chown -R jenkins:stack $NEUTRONCLIENT_DIR +sudo chown -R $owner:stack $NEUTRONCLIENT_DIR # Get admin credentials cd $BASE/new/devstack @@ -55,7 +77,7 @@ VENV=${1:-"functional"} echo "Running neutronclient functional test suite" set +e # Preserve env for OS_ credentials -sudo -E -H -u jenkins tox -e $VENV +sudo -E -H -u $owner tox -e $VENV EXIT_CODE=$? set -e diff --git a/tox.ini b/tox.ini index 7f705d18a..f9e00d6d8 100644 --- a/tox.ini +++ b/tox.ini @@ -24,7 +24,13 @@ commands = {posargs} [testenv:functional] setenv = - OS_TEST_PATH = ./neutronclient/tests/functional + OS_TEST_PATH = ./neutronclient/tests/functional/core + OS_NEUTRONCLIENT_EXEC_DIR = {envdir}/bin + +[testenv:functional-adv-svcs] +setenv = + OS_TEST_PATH = ./neutronclient/tests/functional/adv-svcs + OS_NEUTRONCLIENT_EXEC_DIR = {envdir}/bin [testenv:cover] commands = python setup.py testr --coverage --testr-args='{posargs}' From fcf289797c063088f9003359dfd1c7d4f41ed5ef Mon Sep 17 00:00:00 2001 From: Henry Gessau Date: Wed, 23 Sep 2015 13:31:50 -0400 Subject: [PATCH 262/845] Py3k compliance: check for bytes when making a string When faking stdout in tests we must account for the case when "strings" might be bytes, which python3 cares about. Change-Id: I0e9349c86371055eed479799d39ab6adbcad6f95 Closes-Bug: #1499004 --- neutronclient/tests/unit/test_cli20.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 94d979cf0..240d8c911 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -73,6 +73,12 @@ def write(self, text): def make_string(self): result = '' for line in self.content: + if six.PY3: + if isinstance(line, bytes): + try: + line = line.decode(encoding='utf-8') + except UnicodeError: + pass result = result + line return result From a11a5453949f62b8abe6360680538afc755012f5 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Fri, 25 Sep 2015 09:02:41 +0900 Subject: [PATCH 263/845] Fix extend_show parameter name show operation is an operation to a single object. The parameter name 'resource_plural' is confusing. Change-Id: I11e49b3e245e439fc8376ad2052bd59a1fb715d7 --- neutronclient/v2_0/client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index ce6a8b1e4..e341e90af 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -1712,14 +1712,14 @@ def __init__(self, **kwargs): super(Client, self).__init__(**kwargs) self._register_extensions(self.version) - def extend_show(self, resource_plural, path, parent_resource): + def extend_show(self, resource_singular, path, parent_resource): def _fx(obj, **_params): return self.show_ext(path, obj, **_params) def _parent_fx(parent_id, obj, **_params): return self.show_ext(path % parent_id, obj, **_params) fn = _fx if not parent_resource else _parent_fx - setattr(self, "show_%s" % resource_plural, fn) + setattr(self, "show_%s" % resource_singular, fn) def extend_list(self, resource_plural, path, parent_resource): def _fx(**_params): From 95915b49b357708814e528ce89178f096f9cc3bb Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Mon, 28 Sep 2015 02:32:43 +0900 Subject: [PATCH 264/845] Define non_admin_status_resources in each test Previously non_admin_status_resources for each resource is defined in the common test module test_cli20.py. This means when we add a new resoruce without admin_status_up we need to change the common test module. This commit moves non_admin_status_resources into each test. Change-Id: Ieefa87eedcbabf083c127df893c11ad265f590b1 --- .../tests/unit/qos/test_cli20_policy.py | 3 +++ .../tests/unit/qos/test_cli20_rule.py | 3 +++ neutronclient/tests/unit/test_cli20.py | 27 ++++++++++--------- .../tests/unit/test_cli20_address_scope.py | 3 +++ .../tests/unit/test_cli20_credential.py | 2 ++ .../tests/unit/test_cli20_floatingips.py | 3 +++ .../tests/unit/test_cli20_metering.py | 3 +++ .../tests/unit/test_cli20_networkprofile.py | 2 ++ .../unit/test_cli20_nsx_networkgateway.py | 2 ++ .../tests/unit/test_cli20_nsx_queue.py | 3 +++ .../unit/test_cli20_nuage_netpartition.py | 1 + .../tests/unit/test_cli20_policyprofile.py | 2 ++ neutronclient/tests/unit/test_cli20_rbac.py | 3 +++ .../tests/unit/test_cli20_securitygroup.py | 3 +++ neutronclient/tests/unit/test_cli20_subnet.py | 3 +++ .../tests/unit/test_cli20_subnetpool.py | 3 +++ .../tests/unit/test_client_extension.py | 3 +++ .../tests/unit/vpn/test_cli20_ikepolicy.py | 2 ++ .../tests/unit/vpn/test_cli20_ipsecpolicy.py | 2 ++ 19 files changed, 60 insertions(+), 13 deletions(-) diff --git a/neutronclient/tests/unit/qos/test_cli20_policy.py b/neutronclient/tests/unit/qos/test_cli20_policy.py index 9e4bf6d0e..6eae329c1 100755 --- a/neutronclient/tests/unit/qos/test_cli20_policy.py +++ b/neutronclient/tests/unit/qos/test_cli20_policy.py @@ -21,6 +21,9 @@ class CLITestV20QoSPolicyJSON(test_cli20.CLITestV20Base): + + non_admin_status_resources = ['policy'] + def setUp(self): super(CLITestV20QoSPolicyJSON, self).setUp() self.res = 'policy' diff --git a/neutronclient/tests/unit/qos/test_cli20_rule.py b/neutronclient/tests/unit/qos/test_cli20_rule.py index c2fe92130..776fafd37 100644 --- a/neutronclient/tests/unit/qos/test_cli20_rule.py +++ b/neutronclient/tests/unit/qos/test_cli20_rule.py @@ -22,6 +22,9 @@ class CLITestV20QoSRuleJSON(test_cli20.CLITestV20Base): + + non_admin_status_resources = ['bandwidth_limit_rule'] + def setUp(self): super(CLITestV20QoSRuleJSON, self).setUp() self.res = 'bandwidth_limit_rule' diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 94d979cf0..7c1cdc5c7 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -15,7 +15,6 @@ # import contextlib -import copy import itertools import sys @@ -39,17 +38,6 @@ TOKEN = 'testtoken' ENDURL = 'localurl' -non_admin_status_resources = ['subnet', 'floatingip', 'security_group', - 'security_group_rule', 'qos_queue', - 'network_gateway', 'gateway_device', - 'credential', 'network_profile', - 'policy_profile', 'ikepolicy', - 'ipsecpolicy', 'metering_label', - 'metering_label_rule', 'net_partition', - 'fox_socket', 'subnetpool', - 'rbac_policy', 'address_scope', - 'policy', 'bandwidth_limit_rule'] - @contextlib.contextmanager def capture_std_streams(): @@ -187,6 +175,8 @@ class CLITestV20Base(base.BaseTestCase): test_id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' id_field = 'id' + non_admin_status_resources = [] + def _find_resourceid(self, client, resource, name_or_id, cmd_resource=None, parent_id=None): return name_or_id @@ -198,7 +188,6 @@ def setUp(self, plurals=None): """Prepare the test environment.""" super(CLITestV20Base, self).setUp() client.Client.EXTED_PLURALS.update(constants.PLURALS) - self.non_admin_status_resources = copy.copy(non_admin_status_resources) if plurals is not None: client.Client.EXTED_PLURALS.update(plurals) self.metadata = {'plurals': client.Client.EXTED_PLURALS, @@ -221,6 +210,18 @@ def setUp(self, plurals=None): self.client = client.Client(token=TOKEN, endpoint_url=self.endurl) def register_non_admin_status_resource(self, resource_name): + # TODO(amotoki): + # It is recommended to define + # "non_admin_status_resources in each test class rather than + # using register_non_admin_status_resource method. + + # If we change self.non_admin_status_resources like this, + # we need to ensure this should be an instance variable + # to avoid changing the class variable. + if (id(self.non_admin_status_resources) == + id(self.__class__.non_admin_status_resources)): + self.non_admin_status_resources = (self.__class__. + non_admin_status_resources[:]) self.non_admin_status_resources.append(resource_name) def _test_create_resource(self, resource, cmd, name, myid, args, diff --git a/neutronclient/tests/unit/test_cli20_address_scope.py b/neutronclient/tests/unit/test_cli20_address_scope.py index e7b8a33b2..7b8ea7b78 100755 --- a/neutronclient/tests/unit/test_cli20_address_scope.py +++ b/neutronclient/tests/unit/test_cli20_address_scope.py @@ -24,6 +24,9 @@ class CLITestV20AddressScopeJSON(test_cli20.CLITestV20Base): + + non_admin_status_resources = ['address_scope'] + def setUp(self): super(CLITestV20AddressScopeJSON, self).setUp(plurals={'tags': 'tag'}) diff --git a/neutronclient/tests/unit/test_cli20_credential.py b/neutronclient/tests/unit/test_cli20_credential.py index 4f4a1d35d..58c413da6 100644 --- a/neutronclient/tests/unit/test_cli20_credential.py +++ b/neutronclient/tests/unit/test_cli20_credential.py @@ -22,6 +22,8 @@ class CLITestV20Credential(test_cli20.CLITestV20Base): + non_admin_status_resources = ['credential'] + def test_create_credential(self): """Create credential: myid.""" resource = 'credential' diff --git a/neutronclient/tests/unit/test_cli20_floatingips.py b/neutronclient/tests/unit/test_cli20_floatingips.py index 92f527a4b..66a12ace5 100644 --- a/neutronclient/tests/unit/test_cli20_floatingips.py +++ b/neutronclient/tests/unit/test_cli20_floatingips.py @@ -21,6 +21,9 @@ class CLITestV20FloatingIpsJSON(test_cli20.CLITestV20Base): + + non_admin_status_resources = ['floatingip'] + def test_create_floatingip(self): """Create floatingip: fip1.""" resource = 'floatingip' diff --git a/neutronclient/tests/unit/test_cli20_metering.py b/neutronclient/tests/unit/test_cli20_metering.py index 9be645f9b..ad9e99b6c 100644 --- a/neutronclient/tests/unit/test_cli20_metering.py +++ b/neutronclient/tests/unit/test_cli20_metering.py @@ -19,6 +19,9 @@ class CLITestV20MeteringJSON(test_cli20.CLITestV20Base): + + non_admin_status_resources = ['metering_label', 'metering_label_rule'] + def test_create_metering_label(self): """Create a metering label.""" resource = 'metering_label' diff --git a/neutronclient/tests/unit/test_cli20_networkprofile.py b/neutronclient/tests/unit/test_cli20_networkprofile.py index c5a735338..673aeca4c 100644 --- a/neutronclient/tests/unit/test_cli20_networkprofile.py +++ b/neutronclient/tests/unit/test_cli20_networkprofile.py @@ -22,6 +22,8 @@ class CLITestV20NetworkProfile(test_cli20.CLITestV20Base): + non_admin_status_resources = ['network_profile'] + def test_create_networkprofile(self): """Create networkprofile: myid.""" resource = 'network_profile' diff --git a/neutronclient/tests/unit/test_cli20_nsx_networkgateway.py b/neutronclient/tests/unit/test_cli20_nsx_networkgateway.py index 0d77d4ddf..0ce2c8121 100644 --- a/neutronclient/tests/unit/test_cli20_nsx_networkgateway.py +++ b/neutronclient/tests/unit/test_cli20_nsx_networkgateway.py @@ -28,6 +28,8 @@ class CLITestV20NetworkGatewayJSON(test_cli20.CLITestV20Base): gw_resource = "network_gateway" dev_resource = "gateway_device" + non_admin_status_resources = ['network_gateway', 'gateway_device'] + def setUp(self): super(CLITestV20NetworkGatewayJSON, self).setUp( plurals={'devices': 'device', diff --git a/neutronclient/tests/unit/test_cli20_nsx_queue.py b/neutronclient/tests/unit/test_cli20_nsx_queue.py index f65087347..1cbe0c747 100644 --- a/neutronclient/tests/unit/test_cli20_nsx_queue.py +++ b/neutronclient/tests/unit/test_cli20_nsx_queue.py @@ -21,6 +21,9 @@ class CLITestV20QosQueueJSON(test_cli20.CLITestV20Base): + + non_admin_status_resources = ['qos_queue'] + def setUp(self): super(CLITestV20QosQueueJSON, self).setUp( plurals={'qos_queues': 'qos_queue'}) diff --git a/neutronclient/tests/unit/test_cli20_nuage_netpartition.py b/neutronclient/tests/unit/test_cli20_nuage_netpartition.py index 18f9d3ab0..a9839f523 100644 --- a/neutronclient/tests/unit/test_cli20_nuage_netpartition.py +++ b/neutronclient/tests/unit/test_cli20_nuage_netpartition.py @@ -21,6 +21,7 @@ class CLITestV20NetPartitionJSON(test_cli20.CLITestV20Base): resource = 'net_partition' + non_admin_status_resources = ['net_partition'] def test_create_netpartition(self): cmd = netpartition.CreateNetPartition(test_cli20.MyApp(sys.stdout), diff --git a/neutronclient/tests/unit/test_cli20_policyprofile.py b/neutronclient/tests/unit/test_cli20_policyprofile.py index 94cbf2c57..6a7fa6d51 100644 --- a/neutronclient/tests/unit/test_cli20_policyprofile.py +++ b/neutronclient/tests/unit/test_cli20_policyprofile.py @@ -22,6 +22,8 @@ class CLITestV20PolicyProfile(test_cli20.CLITestV20Base): + non_admin_status_resources = ['policy_profile'] + def test_list_policyprofile_detail(self): """List policyprofile: -D.""" resources = 'policy_profiles' diff --git a/neutronclient/tests/unit/test_cli20_rbac.py b/neutronclient/tests/unit/test_cli20_rbac.py index fd51ffb7d..4027d617e 100644 --- a/neutronclient/tests/unit/test_cli20_rbac.py +++ b/neutronclient/tests/unit/test_cli20_rbac.py @@ -20,6 +20,9 @@ class CLITestV20RBACJSON(test_cli20.CLITestV20Base): + + non_admin_status_resources = ['rbac_policy'] + def test_create_rbac_policy_with_mandatory_params(self): """Create rbac: rbac_object --type network --action access_as_shared""" resource = 'rbac_policy' diff --git a/neutronclient/tests/unit/test_cli20_securitygroup.py b/neutronclient/tests/unit/test_cli20_securitygroup.py index 36d2ad0a7..ffc60fb23 100644 --- a/neutronclient/tests/unit/test_cli20_securitygroup.py +++ b/neutronclient/tests/unit/test_cli20_securitygroup.py @@ -27,6 +27,9 @@ class CLITestV20SecurityGroupsJSON(test_cli20.CLITestV20Base): + + non_admin_status_resources = ['security_group', 'security_group_rule'] + def test_create_security_group(self): """Create security group: webservers.""" resource = 'security_group' diff --git a/neutronclient/tests/unit/test_cli20_subnet.py b/neutronclient/tests/unit/test_cli20_subnet.py index e166cf91a..a5b1b62eb 100644 --- a/neutronclient/tests/unit/test_cli20_subnet.py +++ b/neutronclient/tests/unit/test_cli20_subnet.py @@ -25,6 +25,9 @@ class CLITestV20SubnetJSON(test_cli20.CLITestV20Base): + + non_admin_status_resources = ['subnet'] + def setUp(self): super(CLITestV20SubnetJSON, self).setUp(plurals={'tags': 'tag'}) diff --git a/neutronclient/tests/unit/test_cli20_subnetpool.py b/neutronclient/tests/unit/test_cli20_subnetpool.py index 9873e848c..e5555f793 100644 --- a/neutronclient/tests/unit/test_cli20_subnetpool.py +++ b/neutronclient/tests/unit/test_cli20_subnetpool.py @@ -24,6 +24,9 @@ class CLITestV20SubnetPoolJSON(test_cli20.CLITestV20Base): + + non_admin_status_resources = ['subnetpool'] + def setUp(self): super(CLITestV20SubnetPoolJSON, self).setUp(plurals={'tags': 'tag'}) diff --git a/neutronclient/tests/unit/test_client_extension.py b/neutronclient/tests/unit/test_client_extension.py index 8684c0891..1cd4dcb96 100644 --- a/neutronclient/tests/unit/test_client_extension.py +++ b/neutronclient/tests/unit/test_client_extension.py @@ -26,6 +26,9 @@ class CLITestV20ExtensionJSON(test_cli20.CLITestV20Base): + + non_admin_status_resources = ['fox_socket'] + def setUp(self): # need to mock before super because extensions loaded on instantiation self._mock_extension_loading() diff --git a/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py b/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py index d21fb1455..604e2cc07 100644 --- a/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py +++ b/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py @@ -22,6 +22,8 @@ class CLITestV20VpnIkePolicyJSON(test_cli20.CLITestV20Base): + non_admin_status_resources = ['ikepolicy'] + def test_create_ikepolicy_all_params(self): """vpn-ikepolicy-create all params.""" resource = 'ikepolicy' diff --git a/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py b/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py index 7d3d0ba1e..d133e5ec3 100644 --- a/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py +++ b/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py @@ -22,6 +22,8 @@ class CLITestV20VpnIpsecPolicyJSON(test_cli20.CLITestV20Base): + non_admin_status_resources = ['ipsecpolicy'] + def test_create_ipsecpolicy_all_params(self): """vpn-ipsecpolicy-create all params with dashes.""" resource = 'ipsecpolicy' From 895680a825c9dacfccffa3c99c8690f7184288e4 Mon Sep 17 00:00:00 2001 From: Paul Michali Date: Tue, 1 Sep 2015 17:04:58 -0400 Subject: [PATCH 265/845] CRUD for VPN endpoint group API This contains the create, delete, update, show, and list client APIs for the new VPN endpoint group API. The API syntax is: vpn-endpoint-group-create [--name name] [--description desc] \ --type {subnet|cidr|vlan} --value [--value ...] vpn-endpoint-group-delete vpn-endpoint-group-show vpn-endpoint-group-update [--name name] [--description desc] vpn-endpoint-group-list Notes: Using prefix "vpn-" on commands, so that there is no conflict with other neutron commands. Note: Using --type and --value, instead of --endpoint_type and --endpoint, which are already used (and must be globally unique). For example, if latter re used, see error "Must specify new values to update endpoint_group". The --value may be repeated multiple times, as a list of endpoints can be added to a group. There must be at least one. For --type=subnet, the user may enter the name or UUID of subnet, which must already exist. Client will translate names to UUIDs. As shown, the endpoints cannot be modified. A new endpoint group must be formed, and then can be used by VPN (once support is in place). When updating, either name or description must be provided (otherwise there is nothing to update). Added unit tests for the new API. DocImpact Change-Id: Ic696674d97474f8bbf3f895aa21b1585a04dc2fe Partial-Bug: 1459423 Depends-On: Ia729bd0c6967fa2b8c698495aa360f340b42d98a Depends-On: I6e10590a988312eafca076a14be38b19e2d44a87 --- .../neutron/v2_0/vpn/endpoint_group.py | 100 ++++++++++++ neutronclient/shell.py | 6 + .../unit/vpn/test_cli20_endpoint_group.py | 148 ++++++++++++++++++ neutronclient/v2_0/client.py | 30 ++++ 4 files changed, 284 insertions(+) create mode 100644 neutronclient/neutron/v2_0/vpn/endpoint_group.py create mode 100644 neutronclient/tests/unit/vpn/test_cli20_endpoint_group.py diff --git a/neutronclient/neutron/v2_0/vpn/endpoint_group.py b/neutronclient/neutron/v2_0/vpn/endpoint_group.py new file mode 100644 index 000000000..e28e167fa --- /dev/null +++ b/neutronclient/neutron/v2_0/vpn/endpoint_group.py @@ -0,0 +1,100 @@ +# (c) Copyright 2015 Cisco Systems, 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. +# + +from neutronclient.i18n import _ +from neutronclient.neutron import v2_0 as neutronv20 + + +def add_known_endpoint_group_arguments(parser, is_create=True): + parser.add_argument( + '--name', + help=_('Set a name for the endpoint group.')) + parser.add_argument( + '--description', + help=_('Set a description for the endpoint group.')) + if is_create: + parser.add_argument( + '--type', + required=is_create, + help=_('Type of endpoints in group (e.g. subnet, cidr, vlan).')) + parser.add_argument( + '--value', + action='append', dest='endpoints', + required=is_create, + help=_('Endpoint(s) for the group. Must all be of the same type.')) + + +class ListEndpointGroup(neutronv20.ListCommand): + """List VPN endpoint groups that belong to a given tenant.""" + + resource = 'endpoint_group' + list_columns = ['id', 'name', 'type', 'endpoints'] + _formatters = {} + pagination_support = True + sorting_support = True + + +class ShowEndpointGroup(neutronv20.ShowCommand): + """Show a specific VPN endpoint group.""" + + resource = 'endpoint_group' + + +class CreateEndpointGroup(neutronv20.CreateCommand): + """Create a VPN endpoint group.""" + resource = 'endpoint_group' + + def add_known_arguments(self, parser): + add_known_endpoint_group_arguments(parser) + + def subnet_name2id(self, endpoint): + return neutronv20.find_resourceid_by_name_or_id(self.get_client(), + 'subnet', endpoint) + + def args2body(self, parsed_args): + if parsed_args.type == 'subnet': + endpoints = [self.subnet_name2id(ep) + for ep in parsed_args.endpoints] + else: + endpoints = parsed_args.endpoints + + body = {'endpoints': endpoints} + + neutronv20.update_dict(parsed_args, body, + ['name', 'description', + 'tenant_id', 'type']) + return {self.resource: body} + + +class UpdateEndpointGroup(neutronv20.UpdateCommand): + """Update a given VPN endpoint group.""" + + resource = 'endpoint_group' + + def add_known_arguments(self, parser): + add_known_endpoint_group_arguments(parser, is_create=False) + + def args2body(self, parsed_args): + body = {} + neutronv20.update_dict(parsed_args, body, + ['name', 'description']) + return {self.resource: body} + + +class DeleteEndpointGroup(neutronv20.DeleteCommand): + """Delete a given VPN endpoint group.""" + + resource = 'endpoint_group' diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 0bc4094d4..a1c789b5d 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -78,6 +78,7 @@ from neutronclient.neutron.v2_0 import servicetype from neutronclient.neutron.v2_0 import subnet from neutronclient.neutron.v2_0 import subnetpool +from neutronclient.neutron.v2_0.vpn import endpoint_group from neutronclient.neutron.v2_0.vpn import ikepolicy from neutronclient.neutron.v2_0.vpn import ipsec_site_connection from neutronclient.neutron.v2_0.vpn import ipsecpolicy @@ -317,6 +318,11 @@ class BashCompletionCommand(openstack_command.OpenStackCommand): 'ipsec-site-connection-delete': ( ipsec_site_connection.DeleteIPsecSiteConnection ), + 'vpn-endpoint-group-list': endpoint_group.ListEndpointGroup, + 'vpn-endpoint-group-show': endpoint_group.ShowEndpointGroup, + 'vpn-endpoint-group-create': endpoint_group.CreateEndpointGroup, + 'vpn-endpoint-group-update': endpoint_group.UpdateEndpointGroup, + 'vpn-endpoint-group-delete': endpoint_group.DeleteEndpointGroup, 'vpn-service-list': vpnservice.ListVPNService, 'vpn-service-show': vpnservice.ShowVPNService, 'vpn-service-create': vpnservice.CreateVPNService, diff --git a/neutronclient/tests/unit/vpn/test_cli20_endpoint_group.py b/neutronclient/tests/unit/vpn/test_cli20_endpoint_group.py new file mode 100644 index 000000000..948c14406 --- /dev/null +++ b/neutronclient/tests/unit/vpn/test_cli20_endpoint_group.py @@ -0,0 +1,148 @@ +# (c) Copyright 2015 Cisco Systems, 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 sys + +from neutronclient.neutron.v2_0.vpn import endpoint_group +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20VpnEndpointGroupJSON(test_cli20.CLITestV20Base): + + def setUp(self): + super(CLITestV20VpnEndpointGroupJSON, self).setUp() + self.register_non_admin_status_resource('endpoint_group') + + def test_create_endpoint_group_with_cidrs(self): + """vpn-endpoint-group-create with CIDR endpoints.""" + resource = 'endpoint_group' + cmd = endpoint_group.CreateEndpointGroup(test_cli20.MyApp(sys.stdout), + None) + tenant_id = 'mytenant-id' + my_id = 'my-id' + name = 'my-endpoint-group' + description = 'my endpoint group' + endpoint_type = 'cidr' + endpoints = ['10.0.0.0/24', '20.0.0.0/24'] + + args = ['--name', name, + '--description', description, + '--tenant-id', tenant_id, + '--type', endpoint_type, + '--value', '10.0.0.0/24', + '--value', '20.0.0.0/24'] + + position_names = ['name', 'description', 'tenant_id', + 'type', 'endpoints'] + + position_values = [name, description, tenant_id, + endpoint_type, endpoints] + + self._test_create_resource(resource, cmd, name, my_id, args, + position_names, position_values) + + def test_create_endpoint_group_with_subnets(self): + """vpn-endpoint-group-create with subnet endpoints.""" + resource = 'endpoint_group' + cmd = endpoint_group.CreateEndpointGroup(test_cli20.MyApp(sys.stdout), + None) + tenant_id = 'mytenant-id' + my_id = 'my-id' + endpoint_type = 'subnet' + subnet = 'subnet-id' + endpoints = [subnet] + + args = ['--type', endpoint_type, + '--value', subnet, + '--tenant-id', tenant_id] + + position_names = ['type', 'endpoints', 'tenant_id'] + + position_values = [endpoint_type, endpoints, tenant_id] + + self._test_create_resource(resource, cmd, None, my_id, args, + position_names, position_values) + + def test_list_endpoint_group(self): + """vpn-endpoint-group-list.""" + resources = "endpoint_groups" + cmd = endpoint_group.ListEndpointGroup(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources(resources, cmd, True) + + def test_list_endpoint_group_pagination(self): + """vpn-endpoint-group-list.""" + resources = "endpoint_groups" + cmd = endpoint_group.ListEndpointGroup(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources_with_pagination(resources, cmd) + + def test_list_endpoint_group_sort(self): + """vpn-endpoint-group-list --sort-key name --sort-key id + --sort-key asc --sort-key desc + """ + resources = "endpoint_groups" + cmd = endpoint_group.ListEndpointGroup(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources(resources, cmd, + sort_key=["name", "id"], + sort_dir=["asc", "desc"]) + + def test_list_endpoint_group_limit(self): + """vpn-endpoint-group-list -P.""" + resources = "endpoint_groups" + cmd = endpoint_group.ListEndpointGroup(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources(resources, cmd, page_size=1000) + + def test_show_endpoint_group_id(self): + """vpn-endpoint-group-show test_id.""" + resource = 'endpoint_group' + cmd = endpoint_group.ShowEndpointGroup(test_cli20.MyApp(sys.stdout), + None) + args = ['--fields', 'id', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, args, ['id']) + + def test_show_endpoint_group_id_name(self): + """vpn-endpoint-group-show.""" + resource = 'endpoint_group' + cmd = endpoint_group.ShowEndpointGroup(test_cli20.MyApp(sys.stdout), + None) + args = ['--fields', 'id', '--fields', 'name', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, + args, ['id', 'name']) + + def test_update_endpoint_group(self): + """vpn-endpoint-group-update myid --name newname + --description newdesc. + """ + resource = 'endpoint_group' + cmd = endpoint_group.UpdateEndpointGroup(test_cli20.MyApp(sys.stdout), + None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--name', 'newname', + '--description', 'newdesc'], + {'name': 'newname', + 'description': 'newdesc'}) + + def test_delete_endpoint_group(self): + """vpn-endpoint-group-delete my-id.""" + resource = 'endpoint_group' + cmd = endpoint_group.DeleteEndpointGroup(test_cli20.MyApp(sys.stdout), + None) + my_id = 'my-id' + args = [my_id] + self._test_delete_resource(resource, cmd, my_id, args) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 4849a0f86..2a472bdad 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -355,6 +355,8 @@ class Client(ClientBase): security_group_path = "/security-groups/%s" security_group_rules_path = "/security-group-rules" security_group_rule_path = "/security-group-rules/%s" + endpoint_groups_path = "/vpn/endpoint-groups" + endpoint_group_path = "/vpn/endpoint-groups/%s" vpnservices_path = "/vpn/vpnservices" vpnservice_path = "/vpn/vpnservices/%s" ipsecpolicies_path = "/vpn/ipsecpolicies" @@ -439,6 +441,7 @@ class Client(ClientBase): 'ikepolicies': 'ikepolicy', 'ipsec_site_connections': 'ipsec_site_connection', 'vpnservices': 'vpnservice', + 'endpoint_groups': 'endpoint_group', 'vips': 'vip', 'pools': 'pool', 'members': 'member', @@ -794,6 +797,33 @@ def show_security_group_rule(self, security_group_rule, **_params): return self.get(self.security_group_rule_path % (security_group_rule), params=_params) + @APIParamsCall + def list_endpoint_groups(self, retrieve_all=True, **_params): + """Fetches a list of all VPN endpoint groups for a tenant.""" + return self.list('endpoint_groups', self.endpoint_groups_path, + retrieve_all, **_params) + + @APIParamsCall + def show_endpoint_group(self, endpointgroup, **_params): + """Fetches information for a specific VPN endpoint group.""" + return self.get(self.endpoint_group_path % endpointgroup, + params=_params) + + @APIParamsCall + def create_endpoint_group(self, body=None): + """Creates a new VPN endpoint group.""" + return self.post(self.endpoint_groups_path, body=body) + + @APIParamsCall + def update_endpoint_group(self, endpoint_group, body=None): + """Updates a VPN endpoint group.""" + return self.put(self.endpoint_group_path % endpoint_group, body=body) + + @APIParamsCall + def delete_endpoint_group(self, endpoint_group): + """Deletes the specified VPN endpoint group.""" + return self.delete(self.endpoint_group_path % endpoint_group) + @APIParamsCall def list_vpnservices(self, retrieve_all=True, **_params): """Fetches a list of all configured VPN services for a tenant.""" From 647b0847b23130ab29fcf8f2661b26969204cf6c Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Thu, 24 Sep 2015 06:45:19 +0900 Subject: [PATCH 266/845] neutron v2 command module cleanup #1 Purge "body[resource].update({key: value})" pattern and use "body[key] = value" pattern. The purged pattern is a bad convention in neutronclient and I commented not to use it many times but I got tired of it. Change-Id: I2fe0be30d648f59fa45c5951ccc5060c35527aff --- neutronclient/neutron/v2_0/agent.py | 8 ++-- .../neutron/v2_0/contrib/_fox_sockets.py | 14 ++---- neutronclient/neutron/v2_0/credential.py | 15 ++---- neutronclient/neutron/v2_0/floatingip.py | 14 +++--- neutronclient/neutron/v2_0/fw/firewall.py | 12 ++--- .../neutron/v2_0/fw/firewallpolicy.py | 8 ++-- neutronclient/neutron/v2_0/fw/firewallrule.py | 18 +++---- .../neutron/v2_0/lb/healthmonitor.py | 12 ++--- neutronclient/neutron/v2_0/lb/member.py | 12 ++--- neutronclient/neutron/v2_0/lb/pool.py | 12 ++--- .../neutron/v2_0/lb/v2/healthmonitor.py | 12 ++--- neutronclient/neutron/v2_0/lb/v2/listener.py | 12 ++--- .../neutron/v2_0/lb/v2/loadbalancer.py | 12 ++--- neutronclient/neutron/v2_0/lb/v2/member.py | 20 +++----- neutronclient/neutron/v2_0/lb/v2/pool.py | 12 ++--- neutronclient/neutron/v2_0/lb/vip.py | 12 ++--- neutronclient/neutron/v2_0/metering.py | 28 ++++------- neutronclient/neutron/v2_0/netpartition.py | 4 +- neutronclient/neutron/v2_0/network.py | 17 ++++--- neutronclient/neutron/v2_0/networkprofile.py | 31 +++++------- .../neutron/v2_0/nsx/networkgateway.py | 9 ++-- neutronclient/neutron/v2_0/port.py | 45 +++++++++--------- neutronclient/neutron/v2_0/qos/policy.py | 20 ++++---- neutronclient/neutron/v2_0/rbac.py | 13 ++--- neutronclient/neutron/v2_0/router.py | 15 +++--- neutronclient/neutron/v2_0/securitygroup.py | 47 +++++++------------ neutronclient/neutron/v2_0/subnet.py | 38 +++++++-------- neutronclient/neutron/v2_0/subnetpool.py | 18 +++---- neutronclient/neutron/v2_0/vpn/ikepolicy.py | 21 ++++----- .../neutron/v2_0/vpn/ipsec_site_connection.py | 31 +++++------- neutronclient/neutron/v2_0/vpn/ipsecpolicy.py | 23 ++++----- neutronclient/neutron/v2_0/vpn/vpnservice.py | 10 ++-- 32 files changed, 236 insertions(+), 339 deletions(-) diff --git a/neutronclient/neutron/v2_0/agent.py b/neutronclient/neutron/v2_0/agent.py index 8f7de7a7e..123924c4a 100644 --- a/neutronclient/neutron/v2_0/agent.py +++ b/neutronclient/neutron/v2_0/agent.py @@ -72,9 +72,7 @@ def add_known_arguments(self, parser): help=_('Description for the agent.')) def args2body(self, parsed_args): - body = { - self.resource: { - 'admin_state_up': parsed_args.admin_state, }, } - neutronV20.update_dict(parsed_args, body[self.resource], + body = {'admin_state_up': parsed_args.admin_state} + neutronV20.update_dict(parsed_args, body, ['description']) - return body + return {self.resource: body} diff --git a/neutronclient/neutron/v2_0/contrib/_fox_sockets.py b/neutronclient/neutron/v2_0/contrib/_fox_sockets.py index 740043537..c2e9b198d 100644 --- a/neutronclient/neutron/v2_0/contrib/_fox_sockets.py +++ b/neutronclient/neutron/v2_0/contrib/_fox_sockets.py @@ -16,7 +16,6 @@ from neutronclient.common import extension from neutronclient.i18n import _ -from neutronclient.neutron import v2_0 as neutronV20 def _add_updatable_args(parser): @@ -27,7 +26,7 @@ def _add_updatable_args(parser): def _updatable_args2body(parsed_args, body, client): if parsed_args.name: - body['fox_socket'].update({'name': parsed_args.name}) + body['name'] = parsed_args.name class FoxInSocket(extension.NeutronClientExtension): @@ -59,11 +58,10 @@ def add_known_arguments(self, parser): _add_updatable_args(parser) def args2body(self, parsed_args): - body = {'fox_socket': {}} + body = {} client = self.get_client() _updatable_args2body(parsed_args, body, client) - neutronV20.update_dict(parsed_args, body['fox_socket'], []) - return body + return {'fox_socket': body} class FoxInSocketsUpdate(extension.ClientExtensionUpdate, FoxInSocket): @@ -79,10 +77,8 @@ def add_known_arguments(self, parser): help=_('Name of this fox socket.')) def args2body(self, parsed_args): - body = {'fox_socket': { - 'name': parsed_args.name}, } - neutronV20.update_dict(parsed_args, body['fox_socket'], []) - return body + body = {'name': parsed_args.name} + return {'fox_socket': body} class FoxInSocketsDelete(extension.ClientExtensionDelete, FoxInSocket): diff --git a/neutronclient/neutron/v2_0/credential.py b/neutronclient/neutron/v2_0/credential.py index c397627b7..67d532a1f 100644 --- a/neutronclient/neutron/v2_0/credential.py +++ b/neutronclient/neutron/v2_0/credential.py @@ -51,19 +51,14 @@ def add_known_arguments(self, parser): help=_('Password for the credential.')) def args2body(self, parsed_args): - body = {'credential': { - 'credential_name': parsed_args.credential_name}} - + body = {'credential_name': parsed_args.credential_name} if parsed_args.credential_type: - body['credential'].update({'type': - parsed_args.credential_type}) + body['type'] = parsed_args.credential_type if parsed_args.username: - body['credential'].update({'user_name': - parsed_args.username}) + body['user_name'] = parsed_args.username if parsed_args.password: - body['credential'].update({'password': - parsed_args.password}) - return body + body['password'] = parsed_args.password + return {'credential': body} class DeleteCredential(neutronV20.DeleteCommand): diff --git a/neutronclient/neutron/v2_0/floatingip.py b/neutronclient/neutron/v2_0/floatingip.py index d1fc9f466..3022508bb 100644 --- a/neutronclient/neutron/v2_0/floatingip.py +++ b/neutronclient/neutron/v2_0/floatingip.py @@ -68,18 +68,16 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): _network_id = neutronV20.find_resourceid_by_name_or_id( self.get_client(), 'network', parsed_args.floating_network_id) - body = {self.resource: {'floating_network_id': _network_id}} + body = {'floating_network_id': _network_id} if parsed_args.port_id: - body[self.resource].update({'port_id': parsed_args.port_id}) + body['port_id'] = parsed_args.port_id if parsed_args.tenant_id: - body[self.resource].update({'tenant_id': parsed_args.tenant_id}) + body['tenant_id'] = parsed_args.tenant_id if parsed_args.fixed_ip_address: - body[self.resource].update({'fixed_ip_address': - parsed_args.fixed_ip_address}) + body['fixed_ip_address'] = parsed_args.fixed_ip_address if parsed_args.floating_ip_address: - body[self.resource].update({'floating_ip_address': - parsed_args.floating_ip_address}) - return body + body['floating_ip_address'] = parsed_args.floating_ip_address + return {self.resource: body} class DeleteFloatingIP(neutronV20.DeleteCommand): diff --git a/neutronclient/neutron/v2_0/fw/firewall.py b/neutronclient/neutron/v2_0/fw/firewall.py index 3a88a6baa..a4dd2ee2b 100644 --- a/neutronclient/neutron/v2_0/fw/firewall.py +++ b/neutronclient/neutron/v2_0/fw/firewall.py @@ -67,17 +67,15 @@ def args2body(self, parsed_args): _policy_id = neutronv20.find_resourceid_by_name_or_id( client, 'firewall_policy', parsed_args.firewall_policy_id) - body = { - self.resource: { - 'firewall_policy_id': _policy_id, - 'admin_state_up': parsed_args.admin_state, }, } + body = {'firewall_policy_id': _policy_id, + 'admin_state_up': parsed_args.admin_state, } if parsed_args.routers: - body[self.resource]['router_ids'] = [ + body['router_ids'] = [ neutronv20.find_resourceid_by_name_or_id(client, 'router', r) for r in parsed_args.routers] - neutronv20.update_dict(parsed_args, body[self.resource], + neutronv20.update_dict(parsed_args, body, ['name', 'description', 'tenant_id']) - return body + return {self.resource: body} class UpdateFirewall(neutronv20.UpdateCommand): diff --git a/neutronclient/neutron/v2_0/fw/firewallpolicy.py b/neutronclient/neutron/v2_0/fw/firewallpolicy.py index 410ec475f..e385d314c 100644 --- a/neutronclient/neutron/v2_0/fw/firewallpolicy.py +++ b/neutronclient/neutron/v2_0/fw/firewallpolicy.py @@ -45,13 +45,13 @@ def common_args2body(client, parsed_args): _firewall_rules.append( neutronv20.find_resourceid_by_name_or_id( client, 'firewall_rule', f)) - body = {'firewall_policy': {'firewall_rules': _firewall_rules}} + body = {'firewall_rules': _firewall_rules} else: - body = {'firewall_policy': {}} - neutronv20.update_dict(parsed_args, body['firewall_policy'], + body = {} + neutronv20.update_dict(parsed_args, body, ['name', 'description', 'shared', 'audited', 'tenant_id']) - return body + return {'firewall_policy': body} class ListFirewallPolicy(neutronv20.ListCommand): diff --git a/neutronclient/neutron/v2_0/fw/firewallrule.py b/neutronclient/neutron/v2_0/fw/firewallrule.py index e2c49d2f0..1f0536387 100644 --- a/neutronclient/neutron/v2_0/fw/firewallrule.py +++ b/neutronclient/neutron/v2_0/fw/firewallrule.py @@ -108,10 +108,8 @@ def add_known_arguments(self, parser): help=_('Action for the firewall rule.')) def args2body(self, parsed_args): - body = { - self.resource: {}, - } - neutronv20.update_dict(parsed_args, body[self.resource], + body = {} + neutronv20.update_dict(parsed_args, body, ['name', 'description', 'shared', 'protocol', 'source_ip_address', 'destination_ip_address', 'source_port', 'destination_port', @@ -119,8 +117,8 @@ def args2body(self, parsed_args): protocol = parsed_args.protocol if protocol == 'any': protocol = None - body[self.resource]['protocol'] = protocol - return body + body['protocol'] = protocol + return {self.resource: body} class UpdateFirewallRule(neutronv20.UpdateCommand): @@ -135,15 +133,13 @@ def add_known_arguments(self, parser): help=_('Protocol for the firewall rule.')) def args2body(self, parsed_args): - body = { - self.resource: {}, - } + body = {} protocol = parsed_args.protocol if protocol: if protocol == 'any': protocol = None - body[self.resource]['protocol'] = protocol - return body + body['protocol'] = protocol + return {self.resource: body} class DeleteFirewallRule(neutronv20.DeleteCommand): diff --git a/neutronclient/neutron/v2_0/lb/healthmonitor.py b/neutronclient/neutron/v2_0/lb/healthmonitor.py index d691ebf85..1da7eb01c 100644 --- a/neutronclient/neutron/v2_0/lb/healthmonitor.py +++ b/neutronclient/neutron/v2_0/lb/healthmonitor.py @@ -84,19 +84,15 @@ def add_known_arguments(self, parser): help=_('One of the predefined health monitor types.')) def args2body(self, parsed_args): - body = { - self.resource: { - 'admin_state_up': parsed_args.admin_state, + body = {'admin_state_up': parsed_args.admin_state, 'delay': parsed_args.delay, 'max_retries': parsed_args.max_retries, 'timeout': parsed_args.timeout, - 'type': parsed_args.type, - }, - } - neutronV20.update_dict(parsed_args, body[self.resource], + 'type': parsed_args.type} + neutronV20.update_dict(parsed_args, body, ['expected_codes', 'http_method', 'url_path', 'tenant_id']) - return body + return {self.resource: body} class UpdateHealthMonitor(neutronV20.UpdateCommand): diff --git a/neutronclient/neutron/v2_0/lb/member.py b/neutronclient/neutron/v2_0/lb/member.py index 472cd05ee..2a5d4b1b6 100644 --- a/neutronclient/neutron/v2_0/lb/member.py +++ b/neutronclient/neutron/v2_0/lb/member.py @@ -65,18 +65,14 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): _pool_id = neutronV20.find_resourceid_by_name_or_id( self.get_client(), 'pool', parsed_args.pool_id) - body = { - self.resource: { - 'pool_id': _pool_id, - 'admin_state_up': parsed_args.admin_state, - }, - } + body = {'pool_id': _pool_id, + 'admin_state_up': parsed_args.admin_state} neutronV20.update_dict( parsed_args, - body[self.resource], + body, ['address', 'protocol_port', 'weight', 'tenant_id'] ) - return body + return {self.resource: body} class UpdateMember(neutronV20.UpdateCommand): diff --git a/neutronclient/neutron/v2_0/lb/pool.py b/neutronclient/neutron/v2_0/lb/pool.py index 174b91a0d..849e1c4bf 100644 --- a/neutronclient/neutron/v2_0/lb/pool.py +++ b/neutronclient/neutron/v2_0/lb/pool.py @@ -82,16 +82,12 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): _subnet_id = neutronV20.find_resourceid_by_name_or_id( self.get_client(), 'subnet', parsed_args.subnet_id) - body = { - self.resource: { - 'admin_state_up': parsed_args.admin_state, - 'subnet_id': _subnet_id, - }, - } - neutronV20.update_dict(parsed_args, body[self.resource], + body = {'admin_state_up': parsed_args.admin_state, + 'subnet_id': _subnet_id} + neutronV20.update_dict(parsed_args, body, ['description', 'lb_method', 'name', 'protocol', 'tenant_id', 'provider']) - return body + return {self.resource: body} class UpdatePool(neutronV20.UpdateCommand): diff --git a/neutronclient/neutron/v2_0/lb/v2/healthmonitor.py b/neutronclient/neutron/v2_0/lb/v2/healthmonitor.py index ac3b2aa93..4b621cdff 100644 --- a/neutronclient/neutron/v2_0/lb/v2/healthmonitor.py +++ b/neutronclient/neutron/v2_0/lb/v2/healthmonitor.py @@ -92,20 +92,16 @@ def args2body(self, parsed_args): pool_id = neutronV20.find_resourceid_by_name_or_id( self.get_client(), 'pool', parsed_args.pool, cmd_resource='lbaas_pool') - body = { - self.resource: { - 'admin_state_up': parsed_args.admin_state, + body = {'admin_state_up': parsed_args.admin_state, 'delay': parsed_args.delay, 'max_retries': parsed_args.max_retries, 'timeout': parsed_args.timeout, 'type': parsed_args.type, - 'pool_id': pool_id - }, - } - neutronV20.update_dict(parsed_args, body[self.resource], + 'pool_id': pool_id} + neutronV20.update_dict(parsed_args, body, ['expected_codes', 'http_method', 'url_path', 'tenant_id']) - return body + return {self.resource: body} class UpdateHealthMonitor(neutronV20.UpdateCommand): diff --git a/neutronclient/neutron/v2_0/lb/v2/listener.py b/neutronclient/neutron/v2_0/lb/v2/listener.py index 46afc5088..07509fb69 100644 --- a/neutronclient/neutron/v2_0/lb/v2/listener.py +++ b/neutronclient/neutron/v2_0/lb/v2/listener.py @@ -94,22 +94,18 @@ def args2body(self, parsed_args): parsed_args.loadbalancer = _get_loadbalancer_id( self.get_client(), parsed_args.loadbalancer) - body = { - self.resource: { - 'loadbalancer_id': parsed_args.loadbalancer, + body = {'loadbalancer_id': parsed_args.loadbalancer, 'protocol': parsed_args.protocol, 'protocol_port': parsed_args.protocol_port, - 'admin_state_up': parsed_args.admin_state, - }, - } + 'admin_state_up': parsed_args.admin_state} - neutronV20.update_dict(parsed_args, body[self.resource], + neutronV20.update_dict(parsed_args, body, ['connection-limit', 'description', 'loadbalancer_id', 'name', 'default_tls_container_ref', 'sni_container_refs', 'tenant_id']) - return body + return {self.resource: body} class UpdateListener(neutronV20.UpdateCommand): diff --git a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py index 8e381107e..be70c4eca 100644 --- a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py +++ b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py @@ -65,16 +65,12 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): _subnet_id = neutronV20.find_resourceid_by_name_or_id( self.get_client(), 'subnet', parsed_args.vip_subnet) - body = { - self.resource: { - 'vip_subnet_id': _subnet_id, - 'admin_state_up': parsed_args.admin_state, - }, - } - neutronV20.update_dict(parsed_args, body[self.resource], + body = {'vip_subnet_id': _subnet_id, + 'admin_state_up': parsed_args.admin_state} + neutronV20.update_dict(parsed_args, body, ['description', 'provider', 'vip_address', 'tenant_id', 'name']) - return body + return {self.resource: body} class UpdateLoadBalancer(neutronV20.UpdateCommand): diff --git a/neutronclient/neutron/v2_0/lb/v2/member.py b/neutronclient/neutron/v2_0/lb/v2/member.py index e94ff9933..24fd27122 100644 --- a/neutronclient/neutron/v2_0/lb/v2/member.py +++ b/neutronclient/neutron/v2_0/lb/v2/member.py @@ -97,17 +97,13 @@ def args2body(self, parsed_args): self.parent_id = _get_pool_id(self.get_client(), parsed_args.pool) _subnet_id = neutronV20.find_resourceid_by_name_or_id( self.get_client(), 'subnet', parsed_args.subnet) - body = { - self.resource: { - 'subnet_id': _subnet_id, + body = {'subnet_id': _subnet_id, 'admin_state_up': parsed_args.admin_state, 'protocol_port': parsed_args.protocol_port, - 'address': parsed_args.address, - }, - } - neutronV20.update_dict(parsed_args, body[self.resource], + 'address': parsed_args.address} + neutronV20.update_dict(parsed_args, body, ['weight', 'subnet_id', 'tenant_id']) - return body + return {self.resource: body} class UpdateMember(neutronV20.UpdateCommand): @@ -130,12 +126,10 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): self.parent_id = _get_pool_id(self.get_client(), parsed_args.pool) - body = { - self.resource: {} - } - neutronV20.update_dict(parsed_args, body[self.resource], + body = {} + neutronV20.update_dict(parsed_args, body, ['admin_state_up', 'weight']) - return body + return {self.resource: body} class DeleteMember(LbaasMemberMixin, neutronV20.DeleteCommand): diff --git a/neutronclient/neutron/v2_0/lb/v2/pool.py b/neutronclient/neutron/v2_0/lb/v2/pool.py index f8280c7cf..c3c218525 100644 --- a/neutronclient/neutron/v2_0/lb/v2/pool.py +++ b/neutronclient/neutron/v2_0/lb/v2/pool.py @@ -90,18 +90,14 @@ def args2body(self, parsed_args): parsed_args.session_persistence) _listener_id = neutronV20.find_resourceid_by_name_or_id( self.get_client(), 'listener', parsed_args.listener) - body = { - self.resource: { - 'admin_state_up': parsed_args.admin_state, + body = {'admin_state_up': parsed_args.admin_state, 'protocol': parsed_args.protocol, 'lb_algorithm': parsed_args.lb_algorithm, - 'listener_id': _listener_id, - }, - } - neutronV20.update_dict(parsed_args, body[self.resource], + 'listener_id': _listener_id} + neutronV20.update_dict(parsed_args, body, ['description', 'name', 'session_persistence', 'tenant_id']) - return body + return {self.resource: body} class UpdatePool(neutronV20.UpdateCommand): diff --git a/neutronclient/neutron/v2_0/lb/vip.py b/neutronclient/neutron/v2_0/lb/vip.py index 59b0c3871..4e5d0c82b 100644 --- a/neutronclient/neutron/v2_0/lb/vip.py +++ b/neutronclient/neutron/v2_0/lb/vip.py @@ -81,18 +81,14 @@ def args2body(self, parsed_args): _subnet_id = neutronV20.find_resourceid_by_name_or_id( self.get_client(), 'subnet', parsed_args.subnet_id) - body = { - self.resource: { - 'pool_id': _pool_id, + body = {'pool_id': _pool_id, 'admin_state_up': parsed_args.admin_state, - 'subnet_id': _subnet_id, - }, - } - neutronV20.update_dict(parsed_args, body[self.resource], + 'subnet_id': _subnet_id} + neutronV20.update_dict(parsed_args, body, ['address', 'connection_limit', 'description', 'name', 'protocol_port', 'protocol', 'tenant_id']) - return body + return {self.resource: body} class UpdateVip(neutronV20.UpdateCommand): diff --git a/neutronclient/neutron/v2_0/metering.py b/neutronclient/neutron/v2_0/metering.py index 34bf2b6de..095689616 100644 --- a/neutronclient/neutron/v2_0/metering.py +++ b/neutronclient/neutron/v2_0/metering.py @@ -50,18 +50,14 @@ def add_known_arguments(self, parser): help=_('Set the label as shared.')) def args2body(self, parsed_args): - body = {'metering_label': { - 'name': parsed_args.name}, } - + body = {'name': parsed_args.name} if parsed_args.tenant_id: - body['metering_label'].update({'tenant_id': parsed_args.tenant_id}) + body['tenant_id'] = parsed_args.tenant_id if parsed_args.description: - body['metering_label'].update( - {'description': parsed_args.description}) + body['description'] = parsed_args.description if parsed_args.shared: - body['metering_label'].update( - {'shared': True}) - return body + body['shared'] = True + return {'metering_label': body} class DeleteMeteringLabel(neutronv20.DeleteCommand): @@ -113,18 +109,14 @@ def args2body(self, parsed_args): label_id = neutronv20.find_resourceid_by_name_or_id( neutron_client, 'metering_label', parsed_args.label_id) - body = {'metering_label_rule': { - 'metering_label_id': label_id, - 'remote_ip_prefix': parsed_args.remote_ip_prefix - }} + body = {'metering_label_id': label_id, + 'remote_ip_prefix': parsed_args.remote_ip_prefix} if parsed_args.direction: - body['metering_label_rule'].update( - {'direction': parsed_args.direction}) + body['direction'] = parsed_args.direction if parsed_args.excluded: - body['metering_label_rule'].update( - {'excluded': True}) - return body + body['excluded'] = True + return {'metering_label_rule': body} class DeleteMeteringLabelRule(neutronv20.DeleteCommand): diff --git a/neutronclient/neutron/v2_0/netpartition.py b/neutronclient/neutron/v2_0/netpartition.py index 260ad0901..dda278218 100644 --- a/neutronclient/neutron/v2_0/netpartition.py +++ b/neutronclient/neutron/v2_0/netpartition.py @@ -42,8 +42,8 @@ def add_known_arguments(self, parser): help='Name of netpartition to create.') def args2body(self, parsed_args): - body = {'net_partition': {'name': parsed_args.name}, } - return body + body = {'name': parsed_args.name} + return {'net_partition': body} class DeleteNetPartition(DeleteCommand): diff --git a/neutronclient/neutron/v2_0/network.py b/neutronclient/neutron/v2_0/network.py index 8dc963cd4..4b5674437 100644 --- a/neutronclient/neutron/v2_0/network.py +++ b/neutronclient/neutron/v2_0/network.py @@ -148,19 +148,18 @@ def add_known_arguments(self, parser): self.add_arguments_qos_policy(parser) def args2body(self, parsed_args): - body = {'network': { - 'name': parsed_args.name, - 'admin_state_up': parsed_args.admin_state}, } - neutronV20.update_dict(parsed_args, body['network'], + body = {'name': parsed_args.name, + 'admin_state_up': parsed_args.admin_state} + neutronV20.update_dict(parsed_args, body, ['shared', 'tenant_id', 'vlan_transparent', 'provider:network_type', 'provider:physical_network', 'provider:segmentation_id']) - self.args2body_qos_policy(parsed_args, body['network']) + self.args2body_qos_policy(parsed_args, body) - return body + return {'network': body} class DeleteNetwork(neutronV20.DeleteCommand): @@ -178,6 +177,6 @@ def add_known_arguments(self, parser): self.add_arguments_qos_policy(parser) def args2body(self, parsed_args): - body = {'network': {}} - self.args2body_qos_policy(parsed_args, body['network']) - return body + body = {} + self.args2body_qos_policy(parsed_args, body) + return {'network': body} diff --git a/neutronclient/neutron/v2_0/networkprofile.py b/neutronclient/neutron/v2_0/networkprofile.py index ed85875f5..d27ec7c32 100644 --- a/neutronclient/neutron/v2_0/networkprofile.py +++ b/neutronclient/neutron/v2_0/networkprofile.py @@ -67,26 +67,20 @@ def add_known_arguments(self, parser): "You can repeat this option.")) def args2body(self, parsed_args): - body = {'network_profile': {'name': parsed_args.name}} + body = {'name': parsed_args.name} if parsed_args.segment_type: - body['network_profile'].update({'segment_type': - parsed_args.segment_type}) + body['segment_type'] = parsed_args.segment_type if parsed_args.sub_type: - body['network_profile'].update({'sub_type': - parsed_args.sub_type}) + body['sub_type'] = parsed_args.sub_type if parsed_args.segment_range: - body['network_profile'].update({'segment_range': - parsed_args.segment_range}) + body['segment_range'] = parsed_args.segment_range if parsed_args.physical_network: - body['network_profile'].update({'physical_network': - parsed_args.physical_network}) + body['physical_network'] = parsed_args.physical_network if parsed_args.multicast_ip_range: - body['network_profile'].update({'multicast_ip_range': - parsed_args.multicast_ip_range}) + body['multicast_ip_range'] = parsed_args.multicast_ip_range if parsed_args.add_tenants: - body['network_profile'].update({'add_tenants': - parsed_args.add_tenants}) - return body + body['add_tenants'] = parsed_args.add_tenants + return {'network_profile': body} class DeleteNetworkProfile(neutronV20.DeleteCommand): @@ -112,13 +106,12 @@ def add_known_arguments(self, parser): "You can repeat this option.")) def args2body(self, parsed_args): - body = {'network_profile': {}} + body = {} if parsed_args.remove_tenants: - body['network_profile']['remove_tenants'] = (parsed_args. - remove_tenants) + body['remove_tenants'] = parsed_args.remove_tenants if parsed_args.add_tenants: - body['network_profile']['add_tenants'] = parsed_args.add_tenants - return body + body['add_tenants'] = parsed_args.add_tenants + return {'network_profile': body} # Aaron: This function is deprecated diff --git a/neutronclient/neutron/v2_0/nsx/networkgateway.py b/neutronclient/neutron/v2_0/nsx/networkgateway.py index 251fbc658..124d7bbad 100644 --- a/neutronclient/neutron/v2_0/nsx/networkgateway.py +++ b/neutronclient/neutron/v2_0/nsx/networkgateway.py @@ -175,17 +175,16 @@ def add_known_arguments(self, parser): 'option for multiple devices for HA gateways.')) def args2body(self, parsed_args): - body = {self.resource: { - 'name': parsed_args.name}} + body = {'name': parsed_args.name} devices = [] if parsed_args.device: for device in parsed_args.device: devices.append(utils.str2dict(device)) if devices: - body[self.resource].update({'devices': devices}) + body['devices'] = devices if parsed_args.tenant_id: - body[self.resource].update({'tenant_id': parsed_args.tenant_id}) - return body + body['tenant_id'] = parsed_args.tenant_id + return {self.resource: body} class DeleteNetworkGateway(neutronV20.DeleteCommand): diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index 42d671473..f2483e1a8 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -69,11 +69,11 @@ def _add_updatable_args(parser): def _updatable_args2body(parsed_args, body, client): if parsed_args.device_id: - body['port'].update({'device_id': parsed_args.device_id}) + body['device_id'] = parsed_args.device_id if parsed_args.device_owner: - body['port'].update({'device_owner': parsed_args.device_owner}) + body['device_owner'] = parsed_args.device_owner if parsed_args.name: - body['port'].update({'name': parsed_args.name}) + body['name'] = parsed_args.name ips = [] if parsed_args.fixed_ip: for ip_spec in parsed_args.fixed_ip: @@ -85,7 +85,7 @@ def _updatable_args2body(parsed_args, body, client): ip_dict['subnet_id'] = _subnet_id ips.append(ip_dict) if ips: - body['port'].update({'fixed_ips': ips}) + body['fixed_ips'] = ips class ListPort(neutronV20.ListCommand): @@ -190,7 +190,7 @@ def args2body_extradhcpopt(self, parsed_args, port): raise exceptions.CommandError(edo_err_msg) if ops: - port.update({'extra_dhcp_opts': ops}) + port['extra_dhcp_opts'] = ops class CreatePort(neutronV20.CreateCommand, UpdatePortSecGroupMixin, @@ -241,24 +241,24 @@ def args2body(self, parsed_args): client = self.get_client() _network_id = neutronV20.find_resourceid_by_name_or_id( client, 'network', parsed_args.network_id) - body = {'port': {'admin_state_up': parsed_args.admin_state, - 'network_id': _network_id, }, } + body = {'admin_state_up': parsed_args.admin_state, + 'network_id': _network_id, } _updatable_args2body(parsed_args, body, client) if parsed_args.mac_address: - body['port'].update({'mac_address': parsed_args.mac_address}) + body['mac_address'] = parsed_args.mac_address if parsed_args.tenant_id: - body['port'].update({'tenant_id': parsed_args.tenant_id}) + body['tenant_id'] = parsed_args.tenant_id if parsed_args.vnic_type: - body['port'].update({'binding:vnic_type': parsed_args.vnic_type}) + body['binding:vnic_type'] = parsed_args.vnic_type if parsed_args.binding_profile: - body['port'].update({'binding:profile': - jsonutils.loads(parsed_args.binding_profile)}) + body['binding:profile'] = jsonutils.loads( + parsed_args.binding_profile) - self.args2body_secgroup(parsed_args, body['port']) - self.args2body_extradhcpopt(parsed_args, body['port']) - self.args2body_qos_policy(parsed_args, body['port']) + self.args2body_secgroup(parsed_args, body) + self.args2body_extradhcpopt(parsed_args, body) + self.args2body_qos_policy(parsed_args, body) - return body + return {'port': body} class DeletePort(neutronV20.DeleteCommand): @@ -288,15 +288,14 @@ def add_known_arguments(self, parser): self.add_arguments_qos_policy(parser) def args2body(self, parsed_args): - body = {'port': {}} + body = {} client = self.get_client() _updatable_args2body(parsed_args, body, client) if parsed_args.admin_state_up: - body['port'].update({'admin_state_up': - parsed_args.admin_state_up}) + body['admin_state_up'] = parsed_args.admin_state_up - self.args2body_secgroup(parsed_args, body['port']) - self.args2body_extradhcpopt(parsed_args, body['port']) - self.args2body_qos_policy(parsed_args, body['port']) + self.args2body_secgroup(parsed_args, body) + self.args2body_extradhcpopt(parsed_args, body) + self.args2body_qos_policy(parsed_args, body) - return body + return {'port': body} diff --git a/neutronclient/neutron/v2_0/qos/policy.py b/neutronclient/neutron/v2_0/qos/policy.py index 6c680d22c..8d6035627 100755 --- a/neutronclient/neutron/v2_0/qos/policy.py +++ b/neutronclient/neutron/v2_0/qos/policy.py @@ -103,14 +103,14 @@ def add_known_arguments(self, parser): 'Set shared to True (default is False).')) def args2body(self, parsed_args): - body = {self.resource: {'name': parsed_args.name}, } + body = {'name': parsed_args.name} if parsed_args.description: - body[self.resource]['description'] = parsed_args.description + body['description'] = parsed_args.description if parsed_args.shared: - body[self.resource]['shared'] = parsed_args.shared + body['shared'] = parsed_args.shared if parsed_args.tenant_id: - body[self.resource]['tenant_id'] = parsed_args.tenant_id - return body + body['tenant_id'] = parsed_args.tenant_id + return {self.resource: body} class UpdateQoSPolicy(neutronv20.UpdateCommand): @@ -133,14 +133,14 @@ def add_known_arguments(self, parser): 'Set shared to True (default is False).')) def args2body(self, parsed_args): - body = {self.resource: {}, } + body = {} if parsed_args.name: - body[self.resource]['name'] = parsed_args.name + body['name'] = parsed_args.name if parsed_args.description: - body[self.resource]['description'] = parsed_args.description + body['description'] = parsed_args.description if parsed_args.shared: - body[self.resource]['shared'] = parsed_args.shared - return body + body['shared'] = parsed_args.shared + return {self.resource: body} class DeleteQoSPolicy(neutronv20.DeleteCommand): diff --git a/neutronclient/neutron/v2_0/rbac.py b/neutronclient/neutron/v2_0/rbac.py index 59a2c80bb..5dc72c0b3 100644 --- a/neutronclient/neutron/v2_0/rbac.py +++ b/neutronclient/neutron/v2_0/rbac.py @@ -68,13 +68,13 @@ def args2body(self, parsed_args): neutron_client.format = parsed_args.request_format _object_id = get_rbac_object_id(neutron_client, parsed_args.type, parsed_args.name) - body = {self.resource: { + body = { 'object_id': _object_id, 'object_type': parsed_args.type, 'target_tenant': parsed_args.target_tenant, 'action': parsed_args.action, - }, } - return body + } + return {self.resource: body} class UpdateRBACPolicy(neutronV20.UpdateCommand): @@ -89,11 +89,8 @@ def add_known_arguments(self, parser): 'policy will be enforced.')) def args2body(self, parsed_args): - - body = {self.resource: { - 'target_tenant': parsed_args.target_tenant, - }, } - return body + body = {'target_tenant': parsed_args.target_tenant} + return {self.resource: body} class DeleteRBACPolicy(neutronV20.DeleteCommand): diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index 414759d68..31a2bf7ba 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -75,10 +75,10 @@ def add_known_arguments(self, parser): help=_('Create a highly available router.')) def args2body(self, parsed_args): - body = {self.resource: {'admin_state_up': parsed_args.admin_state}} - neutronV20.update_dict(parsed_args, body[self.resource], + body = {'admin_state_up': parsed_args.admin_state} + neutronV20.update_dict(parsed_args, body, ['name', 'tenant_id', 'distributed', 'ha']) - return body + return {self.resource: body} class DeleteRouter(neutronV20.DeleteCommand): @@ -109,13 +109,12 @@ def add_known_arguments(self, parser): ' distributed mode.')) def args2body(self, parsed_args): - body = {self.resource: {}} + body = {} if hasattr(parsed_args, 'admin_state'): - body[self.resource].update( - {'admin_state_up': parsed_args.admin_state}) - neutronV20.update_dict(parsed_args, body[self.resource], + body['admin_state_up'] = parsed_args.admin_state + neutronV20.update_dict(parsed_args, body, ['name', 'distributed']) - return body + return {self.resource: body} class RouterInterfaceCommand(neutronV20.NeutronCommand): diff --git a/neutronclient/neutron/v2_0/securitygroup.py b/neutronclient/neutron/v2_0/securitygroup.py index 31c589cba..9e8d0dab5 100644 --- a/neutronclient/neutron/v2_0/securitygroup.py +++ b/neutronclient/neutron/v2_0/securitygroup.py @@ -123,14 +123,12 @@ def add_known_arguments(self, parser): help=_('Description of security group.')) def args2body(self, parsed_args): - body = {'security_group': { - 'name': parsed_args.name}} + body = {'name': parsed_args.name} if parsed_args.description: - body['security_group'].update( - {'description': parsed_args.description}) + body['description'] = parsed_args.description if parsed_args.tenant_id: - body['security_group'].update({'tenant_id': parsed_args.tenant_id}) - return body + body['tenant_id'] = parsed_args.tenant_id + return {'security_group': body} class DeleteSecurityGroup(neutronV20.DeleteCommand): @@ -154,14 +152,12 @@ def add_known_arguments(self, parser): help=_('Description of security group.')) def args2body(self, parsed_args): - body = {'security_group': {}} + body = {} if parsed_args.name: - body['security_group'].update( - {'name': parsed_args.name}) + body['name'] = parsed_args.name if parsed_args.description: - body['security_group'].update( - {'description': parsed_args.description}) - return body + body['description'] = parsed_args.description + return {'security_group': body} class ListSecurityGroupRule(neutronV20.ListCommand): @@ -344,32 +340,25 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): _security_group_id = neutronV20.find_resourceid_by_name_or_id( self.get_client(), 'security_group', parsed_args.security_group_id) - body = {'security_group_rule': { - 'security_group_id': _security_group_id, - 'direction': parsed_args.direction, - 'ethertype': parsed_args.ethertype}} + body = {'security_group_id': _security_group_id, + 'direction': parsed_args.direction, + 'ethertype': parsed_args.ethertype} if parsed_args.protocol: - body['security_group_rule'].update( - {'protocol': parsed_args.protocol}) + body['protocol'] = parsed_args.protocol if parsed_args.port_range_min: - body['security_group_rule'].update( - {'port_range_min': parsed_args.port_range_min}) + body['port_range_min'] = parsed_args.port_range_min if parsed_args.port_range_max: - body['security_group_rule'].update( - {'port_range_max': parsed_args.port_range_max}) + body['port_range_max'] = parsed_args.port_range_max if parsed_args.remote_ip_prefix: - body['security_group_rule'].update( - {'remote_ip_prefix': parsed_args.remote_ip_prefix}) + body['remote_ip_prefix'] = parsed_args.remote_ip_prefix if parsed_args.remote_group_id: _remote_group_id = neutronV20.find_resourceid_by_name_or_id( self.get_client(), 'security_group', parsed_args.remote_group_id) - body['security_group_rule'].update( - {'remote_group_id': _remote_group_id}) + body['remote_group_id'] = _remote_group_id if parsed_args.tenant_id: - body['security_group_rule'].update( - {'tenant_id': parsed_args.tenant_id}) - return body + body['tenant_id'] = parsed_args.tenant_id + return {'security_group_rule': body} class DeleteSecurityGroupRule(neutronV20.DeleteCommand): diff --git a/neutronclient/neutron/v2_0/subnet.py b/neutronclient/neutron/v2_0/subnet.py index 402be1347..a0dd9947e 100644 --- a/neutronclient/neutron/v2_0/subnet.py +++ b/neutronclient/neutron/v2_0/subnet.py @@ -108,32 +108,32 @@ def updatable_args2body(parsed_args, body, for_create=True, ip_version=None): "You cannot enable and disable DHCP at the same time.")) if parsed_args.no_gateway: - body['subnet'].update({'gateway_ip': None}) + body['gateway_ip'] = None elif parsed_args.gateway: - body['subnet'].update({'gateway_ip': parsed_args.gateway}) + body['gateway_ip'] = parsed_args.gateway if parsed_args.name: - body['subnet'].update({'name': parsed_args.name}) + body['name'] = parsed_args.name if parsed_args.disable_dhcp: - body['subnet'].update({'enable_dhcp': False}) + body['enable_dhcp'] = False if parsed_args.enable_dhcp: - body['subnet'].update({'enable_dhcp': True}) + body['enable_dhcp'] = True if parsed_args.allocation_pools: - body['subnet']['allocation_pools'] = parsed_args.allocation_pools + body['allocation_pools'] = parsed_args.allocation_pools if parsed_args.host_routes: - body['subnet']['host_routes'] = parsed_args.host_routes + body['host_routes'] = parsed_args.host_routes if parsed_args.dns_nameservers: - body['subnet']['dns_nameservers'] = parsed_args.dns_nameservers + body['dns_nameservers'] = parsed_args.dns_nameservers if for_create and parsed_args.ipv6_ra_mode: if ip_version == 4: raise exceptions.CommandError(_("--ipv6-ra-mode is invalid " "when --ip-version is 4")) - body['subnet']['ipv6_ra_mode'] = parsed_args.ipv6_ra_mode + body['ipv6_ra_mode'] = parsed_args.ipv6_ra_mode if for_create and parsed_args.ipv6_address_mode: if ip_version == 4: raise exceptions.CommandError(_("--ipv6-address-mode is " "invalid when --ip-version " "is 4")) - body['subnet']['ipv6_address_mode'] = parsed_args.ipv6_address_mode + body['ipv6_address_mode'] = parsed_args.ipv6_address_mode class ListSubnet(neutronV20.ListCommand): @@ -199,10 +199,10 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): _network_id = neutronV20.find_resourceid_by_name_or_id( self.get_client(), 'network', parsed_args.network_id) - body = {'subnet': {'network_id': _network_id}} + body = {'network_id': _network_id} if parsed_args.prefixlen: - body['subnet'].update({'prefixlen': parsed_args.prefixlen}) + body['prefixlen'] = parsed_args.prefixlen ip_version = parsed_args.ip_version if parsed_args.subnetpool: if parsed_args.subnetpool == 'None': @@ -214,16 +214,16 @@ def args2body(self, parsed_args): # Now that we have the pool_id - let's just have a check on the # ip version used in the pool ip_version = _subnetpool['ip_version'] - body['subnet'].update({'subnetpool_id': _subnetpool_id}) + body['subnetpool_id'] = _subnetpool_id # IP version needs to be set as IP version can be # determined by subnetpool. - body['subnet']['ip_version'] = ip_version + body['ip_version'] = ip_version if parsed_args.cidr: # With subnetpool, cidr is now optional for creating subnet. cidr = parsed_args.cidr - body['subnet'].update({'cidr': cidr}) + body['cidr'] = cidr unusable_cidr = '/32' if ip_version == 4 else '/128' if cidr.endswith(unusable_cidr): self.log.warning(_("An IPv%(ip)d subnet with a %(cidr)s CIDR " @@ -235,9 +235,9 @@ def args2body(self, parsed_args): updatable_args2body(parsed_args, body, ip_version=ip_version) if parsed_args.tenant_id: - body['subnet'].update({'tenant_id': parsed_args.tenant_id}) + body['tenant_id'] = parsed_args.tenant_id - return body + return {'subnet': body} class DeleteSubnet(neutronV20.DeleteCommand): @@ -255,6 +255,6 @@ def add_known_arguments(self, parser): add_updatable_arguments(parser) def args2body(self, parsed_args): - body = {'subnet': {}} + body = {} updatable_args2body(parsed_args, body, for_create=False) - return body + return {'subnet': body} diff --git a/neutronclient/neutron/v2_0/subnetpool.py b/neutronclient/neutron/v2_0/subnetpool.py index bc0bdf0d4..2357750bc 100644 --- a/neutronclient/neutron/v2_0/subnetpool.py +++ b/neutronclient/neutron/v2_0/subnetpool.py @@ -35,7 +35,7 @@ def add_updatable_arguments(parser): def updatable_args2body(parsed_args, body, for_create=True): - neutronV20.update_dict(parsed_args, body['subnetpool'], + neutronV20.update_dict(parsed_args, body, ['name', 'prefixes', 'default_prefixlen', 'min_prefixlen', 'max_prefixlen']) @@ -78,18 +78,18 @@ def add_known_arguments(self, parser): 'scopes')) def args2body(self, parsed_args): - body = {'subnetpool': {'prefixes': parsed_args.prefixes}} + body = {'prefixes': parsed_args.prefixes} updatable_args2body(parsed_args, body) if parsed_args.shared: - body['subnetpool']['shared'] = True + body['shared'] = True # Parse and update for "address-scope" option if parsed_args.address_scope: _addrscope_id = neutronV20.find_resourceid_by_name_or_id( self.get_client(), 'address_scope', parsed_args.address_scope) - body['subnetpool']['address_scope_id'] = _addrscope_id - return body + body['address_scope_id'] = _addrscope_id + return {'subnetpool': body} class DeleteSubnetPool(neutronV20.DeleteCommand): @@ -120,15 +120,15 @@ def add_known_arguments(self, parser): 'address scope')) def args2body(self, parsed_args): - body = {'subnetpool': {}} + body = {} updatable_args2body(parsed_args, body, for_create=False) # Parse and update for "address-scope" option/s if parsed_args.no_address_scope: - body['subnetpool']['address_scope_id'] = None + body['address_scope_id'] = None elif parsed_args.address_scope: _addrscope_id = neutronV20.find_resourceid_by_name_or_id( self.get_client(), 'address_scope', parsed_args.address_scope) - body['subnetpool']['address_scope_id'] = _addrscope_id - return body + body['address_scope_id'] = _addrscope_id + return {'subnetpool': body} diff --git a/neutronclient/neutron/v2_0/vpn/ikepolicy.py b/neutronclient/neutron/v2_0/vpn/ikepolicy.py index ea93eda40..e34c350ec 100644 --- a/neutronclient/neutron/v2_0/vpn/ikepolicy.py +++ b/neutronclient/neutron/v2_0/vpn/ikepolicy.py @@ -78,23 +78,23 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): - body = {'ikepolicy': { + body = { 'auth_algorithm': parsed_args.auth_algorithm, 'encryption_algorithm': parsed_args.encryption_algorithm, 'phase1_negotiation_mode': parsed_args.phase1_negotiation_mode, 'ike_version': parsed_args.ike_version, 'pfs': parsed_args.pfs, - }, } + } if parsed_args.name: - body['ikepolicy'].update({'name': parsed_args.name}) + body['name'] = parsed_args.name if parsed_args.description: - body['ikepolicy'].update({'description': parsed_args.description}) + body['description'] = parsed_args.description if parsed_args.tenant_id: - body['ikepolicy'].update({'tenant_id': parsed_args.tenant_id}) + body['tenant_id'] = parsed_args.tenant_id if parsed_args.lifetime: vpn_utils.validate_lifetime_dict(parsed_args.lifetime) - body['ikepolicy'].update({'lifetime': parsed_args.lifetime}) - return body + body['lifetime'] = parsed_args.lifetime + return {'ikepolicy': body} class UpdateIKEPolicy(neutronv20.UpdateCommand): @@ -111,12 +111,11 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): - body = {'ikepolicy': { - }, } + body = {} if parsed_args.lifetime: vpn_utils.validate_lifetime_dict(parsed_args.lifetime) - body['ikepolicy'].update({'lifetime': parsed_args.lifetime}) - return body + body['lifetime'] = parsed_args.lifetime + return {'ikepolicy': body} class DeleteIKEPolicy(neutronv20.DeleteCommand): diff --git a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py index 0d6914cd4..57ddd8a1d 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py +++ b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py @@ -123,7 +123,7 @@ def args2body(self, parsed_args): message = _("Invalid MTU value: MTU must be " "greater than or equal to 68") raise exceptions.CommandError(message) - body = {'ipsec_site_connection': { + body = { 'vpnservice_id': _vpnservice_id, 'ikepolicy_id': _ikepolicy_id, 'ipsecpolicy_id': _ipsecpolicy_id, @@ -133,27 +133,20 @@ def args2body(self, parsed_args): 'initiator': parsed_args.initiator, 'psk': parsed_args.psk, 'admin_state_up': parsed_args.admin_state_down, - }, } + } if parsed_args.name: - body['ipsec_site_connection'].update( - {'name': parsed_args.name} - ) + body['name'] = parsed_args.name if parsed_args.description: - body['ipsec_site_connection'].update( - {'description': parsed_args.description} - ) + body['description'] = parsed_args.description if parsed_args.tenant_id: - body['ipsec_site_connection'].update( - {'tenant_id': parsed_args.tenant_id} - ) + body['tenant_id'] = parsed_args.tenant_id if parsed_args.dpd: vpn_utils.validate_dpd_dict(parsed_args.dpd) - body['ipsec_site_connection'].update({'dpd': parsed_args.dpd}) + body['dpd'] = parsed_args.dpd if parsed_args.peer_cidrs: - body['ipsec_site_connection'][ - 'peer_cidrs'] = parsed_args.peer_cidrs + body['peer_cidrs'] = parsed_args.peer_cidrs - return body + return {'ipsec_site_connection': body} class UpdateIPsecSiteConnection(neutronv20.UpdateCommand): @@ -170,13 +163,11 @@ def add_known_arguments(self, parser): help=vpn_utils.dpd_help("IPsec connection.")) def args2body(self, parsed_args): - body = {'ipsec_site_connection': { - }, } - + body = {} if parsed_args.dpd: vpn_utils.validate_dpd_dict(parsed_args.dpd) - body['ipsec_site_connection'].update({'dpd': parsed_args.dpd}) - return body + body['dpd'] = parsed_args.dpd + return {'ipsec_site_connection': body} class DeleteIPsecSiteConnection(neutronv20.DeleteCommand): diff --git a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py index a1b3db909..bd3bf616e 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py +++ b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py @@ -77,25 +77,23 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): - body = {'ipsecpolicy': { + body = { 'auth_algorithm': parsed_args.auth_algorithm, 'encryption_algorithm': parsed_args.encryption_algorithm, 'encapsulation_mode': parsed_args.encapsulation_mode, 'transform_protocol': parsed_args.transform_protocol, 'pfs': parsed_args.pfs, - }, } + } if parsed_args.name: - body['ipsecpolicy'].update({'name': parsed_args.name}) + body['name'] = parsed_args.name if parsed_args.description: - body['ipsecpolicy'].update( - {'description': parsed_args.description} - ) + body['description'] = parsed_args.description if parsed_args.tenant_id: - body['ipsecpolicy'].update({'tenant_id': parsed_args.tenant_id}) + body['tenant_id'] = parsed_args.tenant_id if parsed_args.lifetime: vpn_utils.validate_lifetime_dict(parsed_args.lifetime) - body['ipsecpolicy'].update({'lifetime': parsed_args.lifetime}) - return body + body['lifetime'] = parsed_args.lifetime + return {'ipsecpolicy': body} class UpdateIPsecPolicy(neutronv20.UpdateCommand): @@ -112,12 +110,11 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): - body = {'ipsecpolicy': { - }, } + body = {} if parsed_args.lifetime: vpn_utils.validate_lifetime_dict(parsed_args.lifetime) - body['ipsecpolicy'].update({'lifetime': parsed_args.lifetime}) - return body + body['lifetime'] = parsed_args.lifetime + return {'ipsecpolicy': body} class DeleteIPsecPolicy(neutronv20.DeleteCommand): diff --git a/neutronclient/neutron/v2_0/vpn/vpnservice.py b/neutronclient/neutron/v2_0/vpn/vpnservice.py index 88915f187..3022f2d5f 100644 --- a/neutronclient/neutron/v2_0/vpn/vpnservice.py +++ b/neutronclient/neutron/v2_0/vpn/vpnservice.py @@ -66,14 +66,14 @@ def args2body(self, parsed_args): self.get_client(), 'router', parsed_args.router) - body = {self.resource: {'subnet_id': _subnet_id, - 'router_id': _router_id, - 'admin_state_up': parsed_args.admin_state}, } - neutronv20.update_dict(parsed_args, body[self.resource], + body = {'subnet_id': _subnet_id, + 'router_id': _router_id, + 'admin_state_up': parsed_args.admin_state} + neutronv20.update_dict(parsed_args, body, ['name', 'description', 'tenant_id']) - return body + return {self.resource: body} class UpdateVPNService(neutronv20.UpdateCommand): From dc29068838c50844883ed7833195a6dd2181eaa6 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Thu, 24 Sep 2015 07:40:00 +0900 Subject: [PATCH 267/845] neutron v2 command module cleanup (2) Use neutronV20.update_dict(parsed_args, body, [attr list]) if possible rather than many if-clause. update_dict() checks attribute existence and non-None and most if-clause does the same. Change-Id: I22f8407f0b8f7db6ffda1c1f9b4991cd9f3f9409 --- neutronclient/neutron/v2_0/floatingip.py | 18 +++++-------- .../neutron/v2_0/fw/firewallpolicy.py | 2 -- neutronclient/neutron/v2_0/metering.py | 15 +++-------- neutronclient/neutron/v2_0/networkprofile.py | 22 +++++----------- neutronclient/neutron/v2_0/port.py | 14 +++-------- neutronclient/neutron/v2_0/securitygroup.py | 25 ++++++------------- neutronclient/neutron/v2_0/subnet.py | 11 +++----- neutronclient/neutron/v2_0/vpn/ikepolicy.py | 18 ++++--------- neutronclient/neutron/v2_0/vpn/ipsecpolicy.py | 18 ++++--------- 9 files changed, 40 insertions(+), 103 deletions(-) diff --git a/neutronclient/neutron/v2_0/floatingip.py b/neutronclient/neutron/v2_0/floatingip.py index 3022508bb..15aa69a85 100644 --- a/neutronclient/neutron/v2_0/floatingip.py +++ b/neutronclient/neutron/v2_0/floatingip.py @@ -69,14 +69,10 @@ def args2body(self, parsed_args): _network_id = neutronV20.find_resourceid_by_name_or_id( self.get_client(), 'network', parsed_args.floating_network_id) body = {'floating_network_id': _network_id} - if parsed_args.port_id: - body['port_id'] = parsed_args.port_id - if parsed_args.tenant_id: - body['tenant_id'] = parsed_args.tenant_id - if parsed_args.fixed_ip_address: - body['fixed_ip_address'] = parsed_args.fixed_ip_address - if parsed_args.floating_ip_address: - body['floating_ip_address'] = parsed_args.floating_ip_address + neutronV20.update_dict(parsed_args, body, + ['port_id', 'tenant_id', + 'fixed_ip_address', + 'floating_ip_address']) return {self.resource: body} @@ -116,10 +112,8 @@ def run(self, parsed_args): neutron_client = self.get_client() neutron_client.format = parsed_args.request_format update_dict = {} - if parsed_args.port_id: - update_dict['port_id'] = parsed_args.port_id - if parsed_args.fixed_ip_address: - update_dict['fixed_ip_address'] = parsed_args.fixed_ip_address + neutronV20.update_dict(parsed_args, update_dict, + ['port_id', 'fixed_ip_address']) neutron_client.update_floatingip(parsed_args.floatingip_id, {'floatingip': update_dict}) print(_('Associated floating IP %s') % parsed_args.floatingip_id, diff --git a/neutronclient/neutron/v2_0/fw/firewallpolicy.py b/neutronclient/neutron/v2_0/fw/firewallpolicy.py index e385d314c..1a6d062fc 100644 --- a/neutronclient/neutron/v2_0/fw/firewallpolicy.py +++ b/neutronclient/neutron/v2_0/fw/firewallpolicy.py @@ -149,7 +149,6 @@ def args2body(self, parsed_args): body = {'firewall_rule_id': _rule, 'insert_before': _insert_before, 'insert_after': _insert_after} - neutronv20.update_dict(parsed_args, body, []) return body def get_parser(self, prog_name): @@ -197,7 +196,6 @@ def args2body(self, parsed_args): self.get_client(), 'firewall_rule', parsed_args.firewall_rule_id) body = {'firewall_rule_id': _rule} - neutronv20.update_dict(parsed_args, body, []) return body def get_parser(self, prog_name): diff --git a/neutronclient/neutron/v2_0/metering.py b/neutronclient/neutron/v2_0/metering.py index 095689616..ea793c3bd 100644 --- a/neutronclient/neutron/v2_0/metering.py +++ b/neutronclient/neutron/v2_0/metering.py @@ -51,12 +51,8 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): body = {'name': parsed_args.name} - if parsed_args.tenant_id: - body['tenant_id'] = parsed_args.tenant_id - if parsed_args.description: - body['description'] = parsed_args.description - if parsed_args.shared: - body['shared'] = True + neutronv20.update_dict(parsed_args, body, + ['tenant_id', 'description', 'shared']) return {'metering_label': body} @@ -111,11 +107,8 @@ def args2body(self, parsed_args): body = {'metering_label_id': label_id, 'remote_ip_prefix': parsed_args.remote_ip_prefix} - - if parsed_args.direction: - body['direction'] = parsed_args.direction - if parsed_args.excluded: - body['excluded'] = True + neutronv20.update_dict(parsed_args, body, + ['direction', 'excluded']) return {'metering_label_rule': body} diff --git a/neutronclient/neutron/v2_0/networkprofile.py b/neutronclient/neutron/v2_0/networkprofile.py index d27ec7c32..6e2a5c913 100644 --- a/neutronclient/neutron/v2_0/networkprofile.py +++ b/neutronclient/neutron/v2_0/networkprofile.py @@ -68,18 +68,10 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): body = {'name': parsed_args.name} - if parsed_args.segment_type: - body['segment_type'] = parsed_args.segment_type - if parsed_args.sub_type: - body['sub_type'] = parsed_args.sub_type - if parsed_args.segment_range: - body['segment_range'] = parsed_args.segment_range - if parsed_args.physical_network: - body['physical_network'] = parsed_args.physical_network - if parsed_args.multicast_ip_range: - body['multicast_ip_range'] = parsed_args.multicast_ip_range - if parsed_args.add_tenants: - body['add_tenants'] = parsed_args.add_tenants + neutronV20.update_dict(parsed_args, body, + ['segment_type', 'sub_type', 'segment_range', + 'physical_network', 'multicast_ip_range', + 'add_tenants']) return {'network_profile': body} @@ -107,10 +99,8 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): body = {} - if parsed_args.remove_tenants: - body['remove_tenants'] = parsed_args.remove_tenants - if parsed_args.add_tenants: - body['add_tenants'] = parsed_args.add_tenants + neutronV20.update_dict(parsed_args, body, + ['remove_tenants', 'add_tenants']) return {'network_profile': body} diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index f2483e1a8..698268703 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -68,12 +68,8 @@ def _add_updatable_args(parser): def _updatable_args2body(parsed_args, body, client): - if parsed_args.device_id: - body['device_id'] = parsed_args.device_id - if parsed_args.device_owner: - body['device_owner'] = parsed_args.device_owner - if parsed_args.name: - body['name'] = parsed_args.name + neutronV20.update_dict(parsed_args, body, + ['device_id', 'device_owner', 'name']) ips = [] if parsed_args.fixed_ip: for ip_spec in parsed_args.fixed_ip: @@ -244,10 +240,8 @@ def args2body(self, parsed_args): body = {'admin_state_up': parsed_args.admin_state, 'network_id': _network_id, } _updatable_args2body(parsed_args, body, client) - if parsed_args.mac_address: - body['mac_address'] = parsed_args.mac_address - if parsed_args.tenant_id: - body['tenant_id'] = parsed_args.tenant_id + neutronV20.update_dict(parsed_args, body, + ['mac_address', 'tenant_id']) if parsed_args.vnic_type: body['binding:vnic_type'] = parsed_args.vnic_type if parsed_args.binding_profile: diff --git a/neutronclient/neutron/v2_0/securitygroup.py b/neutronclient/neutron/v2_0/securitygroup.py index 9e8d0dab5..962b3f874 100644 --- a/neutronclient/neutron/v2_0/securitygroup.py +++ b/neutronclient/neutron/v2_0/securitygroup.py @@ -124,10 +124,8 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): body = {'name': parsed_args.name} - if parsed_args.description: - body['description'] = parsed_args.description - if parsed_args.tenant_id: - body['tenant_id'] = parsed_args.tenant_id + neutronV20.update_dict(parsed_args, body, + ['description', 'tenant_id']) return {'security_group': body} @@ -153,10 +151,8 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): body = {} - if parsed_args.name: - body['name'] = parsed_args.name - if parsed_args.description: - body['description'] = parsed_args.description + neutronV20.update_dict(parsed_args, body, + ['name', 'description']) return {'security_group': body} @@ -343,21 +339,14 @@ def args2body(self, parsed_args): body = {'security_group_id': _security_group_id, 'direction': parsed_args.direction, 'ethertype': parsed_args.ethertype} - if parsed_args.protocol: - body['protocol'] = parsed_args.protocol - if parsed_args.port_range_min: - body['port_range_min'] = parsed_args.port_range_min - if parsed_args.port_range_max: - body['port_range_max'] = parsed_args.port_range_max - if parsed_args.remote_ip_prefix: - body['remote_ip_prefix'] = parsed_args.remote_ip_prefix + neutronV20.update_dict(parsed_args, body, + ['protocol', 'port_range_min', 'port_range_max', + 'remote_ip_prefix', 'tenant_id']) if parsed_args.remote_group_id: _remote_group_id = neutronV20.find_resourceid_by_name_or_id( self.get_client(), 'security_group', parsed_args.remote_group_id) body['remote_group_id'] = _remote_group_id - if parsed_args.tenant_id: - body['tenant_id'] = parsed_args.tenant_id return {'security_group_rule': body} diff --git a/neutronclient/neutron/v2_0/subnet.py b/neutronclient/neutron/v2_0/subnet.py index a0dd9947e..07da46b9a 100644 --- a/neutronclient/neutron/v2_0/subnet.py +++ b/neutronclient/neutron/v2_0/subnet.py @@ -107,22 +107,17 @@ def updatable_args2body(parsed_args, body, for_create=True, ip_version=None): raise exceptions.CommandError(_( "You cannot enable and disable DHCP at the same time.")) + neutronV20.update_dict(parsed_args, body, + ['name', 'allocation_pools', + 'host_routes', 'dns_nameservers']) if parsed_args.no_gateway: body['gateway_ip'] = None elif parsed_args.gateway: body['gateway_ip'] = parsed_args.gateway - if parsed_args.name: - body['name'] = parsed_args.name if parsed_args.disable_dhcp: body['enable_dhcp'] = False if parsed_args.enable_dhcp: body['enable_dhcp'] = True - if parsed_args.allocation_pools: - body['allocation_pools'] = parsed_args.allocation_pools - if parsed_args.host_routes: - body['host_routes'] = parsed_args.host_routes - if parsed_args.dns_nameservers: - body['dns_nameservers'] = parsed_args.dns_nameservers if for_create and parsed_args.ipv6_ra_mode: if ip_version == 4: raise exceptions.CommandError(_("--ipv6-ra-mode is invalid " diff --git a/neutronclient/neutron/v2_0/vpn/ikepolicy.py b/neutronclient/neutron/v2_0/vpn/ikepolicy.py index e34c350ec..dcfad483c 100644 --- a/neutronclient/neutron/v2_0/vpn/ikepolicy.py +++ b/neutronclient/neutron/v2_0/vpn/ikepolicy.py @@ -78,19 +78,11 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): - body = { - 'auth_algorithm': parsed_args.auth_algorithm, - 'encryption_algorithm': parsed_args.encryption_algorithm, - 'phase1_negotiation_mode': parsed_args.phase1_negotiation_mode, - 'ike_version': parsed_args.ike_version, - 'pfs': parsed_args.pfs, - } - if parsed_args.name: - body['name'] = parsed_args.name - if parsed_args.description: - body['description'] = parsed_args.description - if parsed_args.tenant_id: - body['tenant_id'] = parsed_args.tenant_id + body = {} + neutronv20.update_dict(parsed_args, body, + ['auth_algorithm', 'encryption_algorithm', + 'phase1_negotiation_mode', 'ike_version', + 'pfs', 'name', 'description', 'tenant_id']) if parsed_args.lifetime: vpn_utils.validate_lifetime_dict(parsed_args.lifetime) body['lifetime'] = parsed_args.lifetime diff --git a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py index bd3bf616e..138c941d3 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py +++ b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py @@ -77,19 +77,11 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): - body = { - 'auth_algorithm': parsed_args.auth_algorithm, - 'encryption_algorithm': parsed_args.encryption_algorithm, - 'encapsulation_mode': parsed_args.encapsulation_mode, - 'transform_protocol': parsed_args.transform_protocol, - 'pfs': parsed_args.pfs, - } - if parsed_args.name: - body['name'] = parsed_args.name - if parsed_args.description: - body['description'] = parsed_args.description - if parsed_args.tenant_id: - body['tenant_id'] = parsed_args.tenant_id + body = {} + neutronv20.update_dict(parsed_args, body, + ['auth_algorithm', 'encryption_algorithm', + 'encapsulation_mode', 'transform_protocol', + 'pfs', 'name', 'description', 'tenant_id']) if parsed_args.lifetime: vpn_utils.validate_lifetime_dict(parsed_args.lifetime) body['lifetime'] = parsed_args.lifetime From 3b1c1c2395236fcec9e3e6cc94a56f21df4a9861 Mon Sep 17 00:00:00 2001 From: Hideki Saito Date: Sat, 29 Aug 2015 15:39:07 +0900 Subject: [PATCH 268/845] Add allowed-address-pairs to port-update --allowed-address-pair and --no-allowed-address-pairs have been newly defined to port-create and port-update. If you would like to disable allowed-address-pairs, you can use --no-address-pairs option. Change-Id: I4811bd1f8d48c34b6d3f20347e4d45625586ac86 Closes-Bug: #1485182 --- neutron_test.sh | 25 +++++- neutronclient/neutron/v2_0/port.py | 36 +++++++- neutronclient/tests/unit/test_cli20_port.py | 97 +++++++++++++++++++++ 3 files changed, 154 insertions(+), 4 deletions(-) diff --git a/neutron_test.sh b/neutron_test.sh index f2dae36a6..ee0af2abc 100755 --- a/neutron_test.sh +++ b/neutron_test.sh @@ -28,7 +28,7 @@ fi echo "NOTE: User should be admin in order to perform all operations." sleep 3 -FORMAT=" --request-format xml" +FORMAT=" --request-format json" # test the CRUD of network network=$net_name @@ -78,9 +78,30 @@ port_id=`neutron port-list $FORMAT -- --name $port --fields id | tail -n 2 | hea echo "ID of port with name $port is $port_id" neutron port-show $FORMAT $port || die "fail to show port $port" neutron port-show $FORMAT $port_id || die "fail to show port $port_id" - neutron port-update $FORMAT $port --device_id deviceid1 || die "fail to update port $port" neutron port-update $FORMAT $port_id --device_id deviceid2 || die "fail to update port $port_id" +neutron port-update $FORMAT $port_id --allowed-address-pair ip_address=1.1.1.11,mac_address=10:00:00:00:00:00 --allowed-address-pair ip_address=1.1.1.12,mac_address=10:00:00:00:00:01 || die "fail to update port $port_id --allowed-address-pair" +neutron port-show $FORMAT $port || die "fail to show port $port" +neutron port-show $FORMAT $port_id || die "fail to show port $port_id" +neutron port-update $FORMAT $port_id --no-allowed-address-pairs || die "fail to update port $port_id --no-allowed-address-pairs" +neutron port-show $FORMAT $port || die "fail to show port $port" +neutron port-show $FORMAT $port_id || die "fail to show port $port_id" +neutron port-delete $port_id + +# test the create port with allowed-address-pairs +port=$port_name +neutron port-create $FORMAT $NOAUTH $network --name $port -- --allowed-address-pairs type=dict list=true ip_address=1.1.1.11,mac_address=10:00:00:00:00:00 ip_address=1.1.1.12,mac_address=10:00:00:00:00:01 || die "fail to create port $port" +tempport=`neutron port-list $FORMAT -- --name $port --fields id | wc -l` +echo $tempport +if [ $tempport -ne 5 ]; then + die "ports with name $port is not unique or found" +fi +port_id=`neutron port-list $FORMAT -- --name $port --fields id | tail -n 2 | head -n 1 | cut -d' ' -f 2` +echo "ID of port with name $port is $port_id" +neutron port-show $FORMAT $port || die "fail to show port $port" +neutron port-show $FORMAT $port_id || die "fail to show port $port_id" +neutron port-update $FORMAT $port_id --no-allowed-address-pairs || die "fail to update port $port_id --no-allowed-address-pairs" +neutron port-show $port_id # test quota commands RUD DEFAULT_NETWORKS=10 diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index f2483e1a8..93a0a5750 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -193,8 +193,35 @@ def args2body_extradhcpopt(self, parsed_args, port): port['extra_dhcp_opts'] = ops +class UpdatePortAllowedAddressPair(object): + """Update Port for allowed_address_pairs""" + + def add_arguments_allowedaddresspairs(self, parser): + group_aap = parser.add_mutually_exclusive_group() + group_aap.add_argument( + '--allowed-address-pair', + metavar='ip_address=IP_ADDR[,mac_address=MAC_ADDR]', + default=[], + action='append', + dest='allowed_address_pairs', + type=utils.str2dict, + help=_('Allowed address pair associated with the port.' + 'You can repeat this option.')) + group_aap.add_argument( + '--no-allowed-address-pairs', + action='store_true', + help=_('Associate no allowed address pairs with the port.')) + + def args2body_allowedaddresspairs(self, parsed_args, port): + if parsed_args.allowed_address_pairs: + port['allowed_address_pairs'] = parsed_args.allowed_address_pairs + elif parsed_args.no_allowed_address_pairs: + port['allowed_address_pairs'] = [] + + class CreatePort(neutronV20.CreateCommand, UpdatePortSecGroupMixin, - UpdateExtraDhcpOptMixin, qos_policy.CreateQosPolicyMixin): + UpdateExtraDhcpOptMixin, qos_policy.CreateQosPolicyMixin, + UpdatePortAllowedAddressPair): """Create a port for a given tenant.""" resource = 'port' @@ -232,6 +259,7 @@ def add_known_arguments(self, parser): self.add_arguments_secgroup(parser) self.add_arguments_extradhcpopt(parser) self.add_arguments_qos_policy(parser) + self.add_arguments_allowedaddresspairs(parser) parser.add_argument( 'network_id', metavar='NETWORK', @@ -257,6 +285,7 @@ def args2body(self, parsed_args): self.args2body_secgroup(parsed_args, body) self.args2body_extradhcpopt(parsed_args, body) self.args2body_qos_policy(parsed_args, body) + self.args2body_allowedaddresspairs(parsed_args, body) return {'port': body} @@ -268,7 +297,8 @@ class DeletePort(neutronV20.DeleteCommand): class UpdatePort(neutronV20.UpdateCommand, UpdatePortSecGroupMixin, - UpdateExtraDhcpOptMixin, qos_policy.UpdateQosPolicyMixin): + UpdateExtraDhcpOptMixin, qos_policy.UpdateQosPolicyMixin, + UpdatePortAllowedAddressPair): """Update port's information.""" resource = 'port' @@ -286,6 +316,7 @@ def add_known_arguments(self, parser): self.add_arguments_secgroup(parser) self.add_arguments_extradhcpopt(parser) self.add_arguments_qos_policy(parser) + self.add_arguments_allowedaddresspairs(parser) def args2body(self, parsed_args): body = {} @@ -297,5 +328,6 @@ def args2body(self, parsed_args): self.args2body_secgroup(parsed_args, body) self.args2body_extradhcpopt(parsed_args, body) self.args2body_qos_policy(parsed_args, body) + self.args2body_allowedaddresspairs(parsed_args, body) return {'port': body} diff --git a/neutronclient/tests/unit/test_cli20_port.py b/neutronclient/tests/unit/test_cli20_port.py index 7717753eb..db873c1e5 100644 --- a/neutronclient/tests/unit/test_cli20_port.py +++ b/neutronclient/tests/unit/test_cli20_port.py @@ -302,6 +302,54 @@ def test_create_port_with_qos_policy(self): self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) + def test_create_port_with_allowed_address_pair_ipaddr(self): + """Create port: + --allowed-address-pair ip_address=addr0 + --allowed-address-pair ip_address=addr1 + """ + resource = 'port' + cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + netid = 'netid' + pairs = [{'ip_address': '123.123.123.123'}, + {'ip_address': '123.123.123.45'}] + args = [netid, + '--allowed-address-pair', + 'ip_address=123.123.123.123', + '--allowed-address-pair', + 'ip_address=123.123.123.45'] + position_names = ['network_id', 'allowed_address_pairs'] + position_values = [netid, pairs] + position_values.extend([netid]) + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_create_port_with_allowed_address_pair(self): + """Create port: + --allowed-address-pair ip_address=addr0,mac_address=mac0 + --allowed-address-pair ip_address=addr1,mac_address=mac1 + """ + resource = 'port' + cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + netid = 'netid' + pairs = [{'ip_address': '123.123.123.123', + 'mac_address': '10:00:00:00:00:00'}, + {'ip_address': '123.123.123.45', + 'mac_address': '10:00:00:00:00:01'}] + args = [netid, + '--allowed-address-pair', + 'ip_address=123.123.123.123,mac_address=10:00:00:00:00:00', + '--allowed-address-pair', + 'ip_address=123.123.123.45,mac_address=10:00:00:00:00:01'] + position_names = ['network_id', 'allowed_address_pairs'] + position_values = [netid, pairs] + position_values.extend([netid]) + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + def test_list_ports(self): """List ports: -D.""" resources = "ports" @@ -599,6 +647,55 @@ def test_update_port_security_group_off(self): ['--no-security-groups', 'myid'], {'security_groups': []}) + def test_update_port_allowed_address_pair_ipaddr(self): + """Update port(ip_address only): + --allowed-address-pairs ip_address=addr0 + --allowed-address-pairs ip_address=addr1 + """ + import sys + resource = 'port' + cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) + myid = 'myid' + pairs = [{'ip_address': '123.123.123.123'}, + {'ip_address': '123.123.123.45'}] + args = [myid, + '--allowed-address-pair', + 'ip_address=123.123.123.123', + '--allowed-address-pair', + 'ip_address=123.123.123.45'] + updatefields = {'allowed_address_pairs': pairs} + self._test_update_resource(resource, cmd, myid, args, updatefields) + + def test_update_port_allowed_address_pair(self): + """Update port: + --allowed-address-pair + ip_address=addr0,mac_address=mac0 + --allowed-address-pair + ip_address_addr1,mac_address=mac1 + """ + resource = 'port' + cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) + myid = 'myid' + pairs = [{'ip_address': '123.123.123.123', + 'mac_address': '10:00:00:00:00:00'}, + {'ip_address': '123.123.123.45', + 'mac_address': '10:00:00:00:00:01'}] + args = [myid, + '--allowed-address-pair', + 'ip_address=123.123.123.123,mac_address=10:00:00:00:00:00', + '--allowed-address-pair', + 'ip_address=123.123.123.45,mac_address=10:00:00:00:00:01'] + updatefields = {'allowed_address_pairs': pairs} + self._test_update_resource(resource, cmd, myid, args, updatefields) + + def test_update_port_allowed_address_pairs_off(self): + """Update port: --no-allowed-address-pairs.""" + resource = 'port' + cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, 'myid', + ['--no-allowed-address-pairs', 'myid'], + {'allowed_address_pairs': []}) + def test_show_port(self): """Show port: --fields id --fields name myid.""" resource = 'port' From 4e94366e638a61d5ff34a5acaee1648d0b5cee08 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Fri, 2 Oct 2015 17:20:39 +0000 Subject: [PATCH 269/845] Updated from global requirements Change-Id: I7fa501bb53c3454c2b0566504df4e51ff82ae479 --- test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index 160b24689..1b5ab554d 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -16,4 +16,4 @@ requests-mock>=0.6.0 # Apache-2.0 sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 testrepository>=0.0.18 testtools>=1.4.0 -tempest-lib>=0.8.0 +tempest-lib>=0.9.0 From 88eb5845b77fa06a12c096706093329f82dba54c Mon Sep 17 00:00:00 2001 From: Cedric Brandily Date: Tue, 6 Oct 2015 22:22:09 +0200 Subject: [PATCH 270/845] Ensure to decode bytes or fail The commit fcf289797c063088f9003359dfd1c7d4f41ed5ef introduces the pattern: if isinstance(line, bytes): try: line = line.decode(encoding='utf-8') except UnicodeError: pass # concat line with a string which is not working in PY3K if an UnicodeError is raised because line is not decoded and concatened to a string. This change delegates decoding to safe_decode[1] which returns a text object or raises an error. [1] oslo_utils.encodeutils Closes-Bug: #1503415 Related-Bug: #1499004 Change-Id: I16b8013f33aa3efad65be8040d3210120e047bbd --- neutronclient/tests/unit/test_cli20.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 240d8c911..0ceca4a16 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -73,13 +73,7 @@ def write(self, text): def make_string(self): result = '' for line in self.content: - if six.PY3: - if isinstance(line, bytes): - try: - line = line.decode(encoding='utf-8') - except UnicodeError: - pass - result = result + line + result += encodeutils.safe_decode(line, 'utf-8') return result From 21abe4ba34faee99551dbf27134e0482d67876a3 Mon Sep 17 00:00:00 2001 From: Clark Boylan Date: Tue, 6 Oct 2015 14:48:37 -0700 Subject: [PATCH 271/845] Let devstack-gate handle the gate hook timeout Devstack gate now handles all of the hook timeouts itself so that individual jobs do not need to reinvent this wheel. Remove the timeout support from the functional test gate hook here as a result. Change-Id: I3819978b04faea5712c995cbe277ae42870acba9 --- neutronclient/tests/functional/hooks/gate_hook.sh | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/neutronclient/tests/functional/hooks/gate_hook.sh b/neutronclient/tests/functional/hooks/gate_hook.sh index 9f1d8eb5b..b44c237f1 100644 --- a/neutronclient/tests/functional/hooks/gate_hook.sh +++ b/neutronclient/tests/functional/hooks/gate_hook.sh @@ -1,18 +1,12 @@ #!/usr/bin/env bash - set -ex -source $BASE/new/devstack-gate/functions.sh -start_timer - VENV=${1:-"functional"} - if [ "$VENV" == "functional-adv-svcs" ] then export DEVSTACK_LOCAL_CONFIG="enable_plugin neutron-vpnaas git://git.openstack.org/openstack/neutron-vpnaas" fi -remaining_time -timeout -s 9 ${REMAINING_TIME}m $BASE/new/devstack-gate/devstack-vm-gate.sh +$BASE/new/devstack-gate/devstack-vm-gate.sh From 18ce7d9f2e95a0479841b4b48c41765644fce7aa Mon Sep 17 00:00:00 2001 From: Reedip Banerjee Date: Wed, 7 Oct 2015 13:35:44 +0530 Subject: [PATCH 272/845] Fix arg order of assertEqual and use assertTrue assertEqual expects that the arguments provided to it should be (expected, observed).If a particluar order is kept as a convention, then it helps to provide a cleaner message to the developer if Unit Tests fail. There are several Unit Test files where the arguments for assertEqual have been swapped. This commit fixes the issue in python-neutronclient. Additionally instead of using assertEqual(True,xxx), assertTrue(xxx) should be used. The same has been fixed in this patch. Partial-Bug: #1259292 Related-Bug: #1259292 Change-Id: I83ec45af75f61f8005a60efc8176db0df5cfbe9a --- neutronclient/tests/unit/test_auth.py | 16 ++++++++-------- neutronclient/tests/unit/test_cli20.py | 8 ++++---- neutronclient/tests/unit/test_cli20_network.py | 8 ++++---- .../tests/unit/test_cli20_securitygroup.py | 2 +- neutronclient/tests/unit/test_command_meta.py | 2 +- neutronclient/tests/unit/test_shell.py | 6 +++--- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/neutronclient/tests/unit/test_auth.py b/neutronclient/tests/unit/test_auth.py index 285839d4c..b2f55400c 100644 --- a/neutronclient/tests/unit/test_auth.py +++ b/neutronclient/tests/unit/test_auth.py @@ -140,7 +140,7 @@ def test_get_noauth(self): self.requests.get(ENDPOINT_URL + '/resource') self.client.do_request('/resource', 'GET') self.assertEqual(url, self.requests.last_request.url) - self.assertEqual(self.client.endpoint_url, ENDPOINT_URL) + self.assertEqual(ENDPOINT_URL, self.client.endpoint_url) class CLITestAuthKeystone(testtools.TestCase): @@ -177,7 +177,7 @@ def test_reused_token_get_auth_info(self): 'auth_tenant_id': None, 'auth_user_id': None, 'endpoint_url': self.client.endpoint_url} - self.assertEqual(client_.get_auth_info(), expected) + self.assertEqual(expected, client_.get_auth_info()) def test_get_token(self): auth_session, auth_plugin = setup_keystone_v2(self.requests) @@ -264,7 +264,7 @@ def test_use_given_endpoint_url(self): username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD, auth_url=AUTH_URL, region_name=REGION, endpoint_url=ENDPOINT_OVERRIDE) - self.assertEqual(self.client.endpoint_url, ENDPOINT_OVERRIDE) + self.assertEqual(ENDPOINT_OVERRIDE, self.client.endpoint_url) token_id = uuid.uuid4().hex self.client.auth_token = token_id @@ -273,7 +273,7 @@ def test_use_given_endpoint_url(self): self.client.do_request('/resource', 'GET') - self.assertEqual(self.client.endpoint_url, ENDPOINT_OVERRIDE) + self.assertEqual(ENDPOINT_OVERRIDE, self.client.endpoint_url) self.assertEqual(token_id, self.requests.last_request.headers['X-Auth-Token']) @@ -316,7 +316,7 @@ def test_endpoint_type(self): auth_url=AUTH_URL, region_name=REGION, session=auth_session, auth=auth_plugin) - self.assertEqual(self.client.endpoint_url, PUBLIC_ENDPOINT_URL) + self.assertEqual(PUBLIC_ENDPOINT_URL, self.client.endpoint_url) # Test admin url self.client = client.construct_http_client( @@ -324,7 +324,7 @@ def test_endpoint_type(self): auth_url=AUTH_URL, region_name=REGION, endpoint_type='adminURL', session=auth_session, auth=auth_plugin) - self.assertEqual(self.client.endpoint_url, ADMIN_ENDPOINT_URL) + self.assertEqual(ADMIN_ENDPOINT_URL, self.client.endpoint_url) # Test public url self.client = client.construct_http_client( @@ -332,7 +332,7 @@ def test_endpoint_type(self): auth_url=AUTH_URL, region_name=REGION, endpoint_type='publicURL', session=auth_session, auth=auth_plugin) - self.assertEqual(self.client.endpoint_url, PUBLIC_ENDPOINT_URL) + self.assertEqual(PUBLIC_ENDPOINT_URL, self.client.endpoint_url) # Test internal url self.client = client.construct_http_client( @@ -340,7 +340,7 @@ def test_endpoint_type(self): auth_url=AUTH_URL, region_name=REGION, endpoint_type='internalURL', session=auth_session, auth=auth_plugin) - self.assertEqual(self.client.endpoint_url, INTERNAL_ENDPOINT_URL) + self.assertEqual(INTERNAL_ENDPOINT_URL, self.client.endpoint_url) # Test url that isn't found in the service catalog self.client = client.construct_http_client( diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 0ceca4a16..2ce2b2bcf 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -612,7 +612,7 @@ def test_do_request_unicode(self): self.mox.UnsetStubs() # test response with unicode - self.assertEqual(res_body, body) + self.assertEqual(body, res_body) def test_do_request_error_without_response_body(self): self.client.format = self.format @@ -765,10 +765,10 @@ def test_exception_handler_v20_default_fallback(self): def test_exception_status(self): e = exceptions.BadRequest() - self.assertEqual(e.status_code, 400) + self.assertEqual(400, e.status_code) e = exceptions.BadRequest(status_code=499) - self.assertEqual(e.status_code, 499) + self.assertEqual(499, e.status_code) # SslCertificateValidationError has no explicit status_code, # but should have a 'safe' defined fallback. @@ -776,7 +776,7 @@ def test_exception_status(self): self.assertIsNotNone(e.status_code) e = exceptions.SslCertificateValidationError(status_code=599) - self.assertEqual(e.status_code, 599) + self.assertEqual(599, e.status_code) def test_connection_failed(self): self.mox.StubOutWithMock(self.client.httpclient, 'request') diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index 2f10b801a..dc5ba1e8b 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -281,11 +281,11 @@ def setup_list_stub(resources, data, query): self.mox.VerifyAll() self.mox.UnsetStubs() _result = [x for x in result[1]] - self.assertEqual(len(_result), len(expected)) + self.assertEqual(len(expected), len(_result)) for res, exp in zip(_result, expected): - self.assertEqual(len(res), len(exp)) - for a, b in zip(res, exp): - self.assertEqual(a, b) + self.assertEqual(len(exp), len(res)) + for obsrvd, expctd in zip(res, exp): + self.assertEqual(expctd, obsrvd) def test_list_nets_extend_subnets(self): data = [{'id': 'netid1', 'name': 'net1', 'subnets': ['mysubid1']}, diff --git a/neutronclient/tests/unit/test_cli20_securitygroup.py b/neutronclient/tests/unit/test_cli20_securitygroup.py index 36d2ad0a7..758162d48 100644 --- a/neutronclient/tests/unit/test_cli20_securitygroup.py +++ b/neutronclient/tests/unit/test_cli20_securitygroup.py @@ -384,7 +384,7 @@ def setup_list_stub(resources, data, query): self.assertEqual(expected['cols'], result[0]) # Check data _result = [x for x in result[1]] - self.assertEqual(len(_result), len(expected['data'])) + self.assertEqual(len(expected['data']), len(_result)) for res, exp in zip(_result, expected['data']): self.assertEqual(len(exp), len(res)) self.assertEqual(exp, res) diff --git a/neutronclient/tests/unit/test_command_meta.py b/neutronclient/tests/unit/test_command_meta.py index 9b1c72134..184f36680 100644 --- a/neutronclient/tests/unit/test_command_meta.py +++ b/neutronclient/tests/unit/test_command_meta.py @@ -32,7 +32,7 @@ class FakeCommand(neutronV20.NeutronCommand): self.assertTrue(helpers.safe_hasattr(FakeCommand, 'log')) self.assertIsInstance(FakeCommand.log, logging.getLoggerClass()) - self.assertEqual(FakeCommand.log.name, __name__ + ".FakeCommand") + self.assertEqual(__name__ + ".FakeCommand", FakeCommand.log.name) def test_neutron_command_log_defined_explicitly(self): class FakeCommand(neutronV20.NeutronCommand): diff --git a/neutronclient/tests/unit/test_shell.py b/neutronclient/tests/unit/test_shell.py index 091c9ecf1..d82b29215 100644 --- a/neutronclient/tests/unit/test_shell.py +++ b/neutronclient/tests/unit/test_shell.py @@ -77,7 +77,7 @@ def shell(self, argstr, check=False): _shell.run(argstr.split()) except SystemExit: exc_type, exc_value, exc_traceback = sys.exc_info() - self.assertEqual(exc_value.code, 0) + self.assertEqual(0, exc_value.code) finally: stdout = sys.stdout.getvalue() stderr = sys.stderr.getvalue() @@ -444,7 +444,7 @@ def test_insecure_auth(self, mrequests): def test_build_option_parser(self): neutron_shell = openstack_shell.NeutronShell('2.0') result = neutron_shell.build_option_parser('descr', '2.0') - self.assertEqual(True, isinstance(result, argparse.ArgumentParser)) + self.assertIsInstance(result, argparse.ArgumentParser) def test_main_with_unicode(self): self.mox.StubOutClassWithMocks(openstack_shell, 'NeutronShell') @@ -457,7 +457,7 @@ def test_main_with_unicode(self): ret = openstack_shell.main(argv=argv) self.mox.VerifyAll() self.mox.UnsetStubs() - self.assertEqual(ret, 0) + self.assertEqual(0, ret) def test_endpoint_option(self): shell = openstack_shell.NeutronShell('2.0') From 77d96dbfb9bdecd630d264d553f3a3991187ae12 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Fri, 9 Oct 2015 05:04:35 +0000 Subject: [PATCH 273/845] Updated from global requirements Change-Id: Ia535c70b42862a6758a8b3d3fb037e442bcd2877 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 6a677ab02..4d5f61d5a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ netaddr!=0.7.16,>=0.7.12 oslo.i18n>=1.5.0 # Apache-2.0 oslo.serialization>=1.4.0 # Apache-2.0 oslo.utils>=2.0.0 # Apache-2.0 -requests>=2.5.2 +requests!=2.8.0,>=2.5.2 python-keystoneclient>=1.6.0 simplejson>=2.2.0 six>=1.9.0 From 1ced38c1a72271faabb7ef6d3b37d3f7d329ac4f Mon Sep 17 00:00:00 2001 From: Thomas Morin Date: Mon, 12 Oct 2015 12:18:55 +0200 Subject: [PATCH 274/845] Revert parent_id and obj_id parameter order This change reverts the parameter order between parent_id and object id in the definition of obj_updater, obj_deleter, and obj_shower in neutronclient/v2_0/client.py, to match the call order in neutronclient/neutron/v2_0/__init__.py . Fixing the caller code would be the alternative, but would require changing the sig of many non-dynmically created methods in neutronclient/v2_0/client.py . Change-Id: Ia5d499e5a3cf3ff1b357c954f7e82a9066c94982 Closes-Bug: 1505170 --- neutronclient/v2_0/client.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index e1f4faac7..952e6d0fa 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -1746,7 +1746,7 @@ def extend_show(self, resource_plural, path, parent_resource): def _fx(obj, **_params): return self.show_ext(path, obj, **_params) - def _parent_fx(parent_id, obj, **_params): + def _parent_fx(obj, parent_id, **_params): return self.show_ext(path % parent_id, obj, **_params) fn = _fx if not parent_resource else _parent_fx setattr(self, "show_%s" % resource_plural, fn) @@ -1773,7 +1773,7 @@ def extend_delete(self, resource_singular, path, parent_resource): def _fx(obj): return self.delete_ext(path, obj) - def _parent_fx(parent_id, obj): + def _parent_fx(obj, parent_id): return self.delete_ext(path % parent_id, obj) fn = _fx if not parent_resource else _parent_fx setattr(self, "delete_%s" % resource_singular, fn) @@ -1782,7 +1782,7 @@ def extend_update(self, resource_singular, path, parent_resource): def _fx(obj, body=None): return self.update_ext(path, obj, body) - def _parent_fx(parent_id, obj, body=None): + def _parent_fx(obj, parent_id, body=None): return self.update_ext(path % parent_id, obj, body) fn = _fx if not parent_resource else _parent_fx setattr(self, "update_%s" % resource_singular, fn) From d908e9798404ed0fba6423bb1748f6c473f33c08 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Tue, 13 Oct 2015 11:04:47 +0000 Subject: [PATCH 275/845] Updated from global requirements Change-Id: Iaf7cf7eb5ba5aafa226ad154eb009de5583d4491 --- test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index 1b5ab554d..966a54316 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -16,4 +16,4 @@ requests-mock>=0.6.0 # Apache-2.0 sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 testrepository>=0.0.18 testtools>=1.4.0 -tempest-lib>=0.9.0 +tempest-lib>=0.10.0 From c1310f32fbb6dfa958bb31152ee5b492b177c6cb Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Fri, 16 Oct 2015 01:00:55 +0000 Subject: [PATCH 276/845] Updated from global requirements Change-Id: I2999319e622ab5e0bf1fb78e562b3ca80d2c2d8e --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 4d5f61d5a..cd31a7ca9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,9 +8,9 @@ iso8601>=0.1.9 netaddr!=0.7.16,>=0.7.12 oslo.i18n>=1.5.0 # Apache-2.0 oslo.serialization>=1.4.0 # Apache-2.0 -oslo.utils>=2.0.0 # Apache-2.0 +oslo.utils>=2.4.0 # Apache-2.0 requests!=2.8.0,>=2.5.2 -python-keystoneclient>=1.6.0 +python-keystoneclient!=1.8.0,>=1.6.0 simplejson>=2.2.0 six>=1.9.0 Babel>=1.3 From 72a2d1f62229771749f34ceedc75d7e1418c2715 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Mon, 19 Oct 2015 23:32:52 +0000 Subject: [PATCH 277/845] Updated from global requirements Change-Id: I0434c512527abb2af68954bef32bc7ea194dd83f --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index cd31a7ca9..cf5f83c87 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ iso8601>=0.1.9 netaddr!=0.7.16,>=0.7.12 oslo.i18n>=1.5.0 # Apache-2.0 oslo.serialization>=1.4.0 # Apache-2.0 -oslo.utils>=2.4.0 # Apache-2.0 +oslo.utils!=2.6.0,>=2.4.0 # Apache-2.0 requests!=2.8.0,>=2.5.2 python-keystoneclient!=1.8.0,>=1.6.0 simplejson>=2.2.0 From 51b2e71add3d63dccff98b22a803339e9dfae267 Mon Sep 17 00:00:00 2001 From: Monty Taylor Date: Sun, 18 Oct 2015 14:47:56 -0400 Subject: [PATCH 278/845] Use clouds.yaml from devstack for functional tests devstack produces a file called clouds.yaml already with credentials in it. Rather than producing our own config file to run functional tests, just consume the clouds.yaml file that's already there. Closes-Bug: #1507386 Change-Id: Ie15b5da2e61f5b35e3bdeb75f0ba21c58ddfca76 --- functional_creds.conf.sample | 8 --- neutronclient/tests/functional/base.py | 57 +++++++------------ .../tests/functional/core/test_clientlib.py | 13 +++-- .../tests/functional/hooks/post_test_hook.sh | 17 ------ test-requirements.txt | 1 + 5 files changed, 32 insertions(+), 64 deletions(-) delete mode 100644 functional_creds.conf.sample diff --git a/functional_creds.conf.sample b/functional_creds.conf.sample deleted file mode 100644 index 081a73681..000000000 --- a/functional_creds.conf.sample +++ /dev/null @@ -1,8 +0,0 @@ -# Credentials for functional testing -[auth] -uri = http://10.42.0.50:5000/v2.0 - -[admin] -user = admin -tenant = admin -pass = secrete diff --git a/neutronclient/tests/functional/base.py b/neutronclient/tests/functional/base.py index d70fc77b0..0432a5b3d 100644 --- a/neutronclient/tests/functional/base.py +++ b/neutronclient/tests/functional/base.py @@ -12,66 +12,53 @@ import os -from six.moves import configparser +import os_client_config from tempest_lib.cli import base -_CREDS_FILE = 'functional_creds.conf' - - -def credentials(): +def credentials(cloud='devstack-admin'): """Retrieves credentials to run functional tests - Credentials are either read from the environment or from a config file - ('functional_creds.conf'). Environment variables override those from the - config file. + Credentials are either read via os-client-config from the environment + or from a config file ('clouds.yaml'). Environment variables override + those from the config file. - The 'functional_creds.conf' file is the clean and new way to use (by - default tox 2.0 does not pass environment variables). + devstack produces a clouds.yaml with two named clouds - one named + 'devstack' which has user privs and one named 'devstack-admin' which + has admin privs. This function will default to getting the devstack-admin + cloud as that is the current expected behavior. """ + return get_cloud_config(cloud=cloud).get_auth_args() - username = os.environ.get('OS_USERNAME') - password = os.environ.get('OS_PASSWORD') - tenant_name = os.environ.get('OS_TENANT_NAME') - auth_url = os.environ.get('OS_AUTH_URL') - - config = configparser.RawConfigParser() - if config.read(_CREDS_FILE): - username = username or config.get('admin', 'user') - password = password or config.get('admin', 'pass') - tenant_name = tenant_name or config.get('admin', 'tenant') - auth_url = auth_url or config.get('auth', 'uri') - return { - 'username': username, - 'password': password, - 'tenant_name': tenant_name, - 'auth_url': auth_url - } +def get_cloud_config(cloud='devstack-admin'): + return os_client_config.OpenStackConfig().get_one_cloud(cloud=cloud) class ClientTestBase(base.ClientTestBase): """This is a first pass at a simple read only python-neutronclient test. - This only exercises client commands that are read only. + This only exercises client commands that are read only. This should test commands: * as a regular user - * as a admin user + * as an admin user * with and without optional parameters * initially just check return codes, and later test command outputs """ def _get_clients(self): - creds = credentials() + self.creds = credentials() cli_dir = os.environ.get( 'OS_NEUTRONCLIENT_EXEC_DIR', os.path.join(os.path.abspath('.'), '.tox/functional/bin')) - return base.CLIClient(username=creds['username'], - password=creds['password'], - tenant_name=creds['tenant_name'], - uri=creds['auth_url'], - cli_dir=cli_dir) + + return base.CLIClient( + username=self.creds['username'], + password=self.creds['password'], + tenant_name=self.creds['project_name'], + uri=self.creds['auth_url'], + cli_dir=cli_dir) def neutron(self, *args, **kwargs): return self.clients.neutron(*args, diff --git a/neutronclient/tests/functional/core/test_clientlib.py b/neutronclient/tests/functional/core/test_clientlib.py index a18fbbd99..40d6382c1 100644 --- a/neutronclient/tests/functional/core/test_clientlib.py +++ b/neutronclient/tests/functional/core/test_clientlib.py @@ -39,17 +39,22 @@ class Libv2HTTPClientTestBase(LibraryTestBase): def _get_client(self): creds = func_base.credentials() + session_params = {} + ks_session = session.Session.construct(session_params) + ks_discover = discover.Discover(session=ks_session, + auth_url=creds['auth_url']) + # At the moment, we use keystone v2 API + v2_auth_url = ks_discover.url_for('2.0') return v2_client.Client(username=creds['username'], password=creds['password'], - tenant_name=creds['tenant_name'], - auth_url=creds['auth_url']) + tenant_name=creds['project_name'], + auth_url=v2_auth_url) class Libv2SessionClientTestBase(LibraryTestBase): def _get_client(self): creds = func_base.credentials() - session_params = {} ks_session = session.Session.construct(session_params) ks_discover = discover.Discover(session=ks_session, @@ -60,7 +65,7 @@ def _get_client(self): v2_auth_url, username=creds['username'], password=creds['password'], - tenant_name=creds['tenant_name']) + tenant_name=creds['project_name']) return v2_client.Client(session=ks_session) diff --git a/neutronclient/tests/functional/hooks/post_test_hook.sh b/neutronclient/tests/functional/hooks/post_test_hook.sh index b23fb4320..5fb66a109 100755 --- a/neutronclient/tests/functional/hooks/post_test_hook.sh +++ b/neutronclient/tests/functional/hooks/post_test_hook.sh @@ -52,23 +52,6 @@ owner=jenkins sudo chown -R $owner:stack $NEUTRONCLIENT_DIR -# Get admin credentials -cd $BASE/new/devstack -source openrc admin admin - -# Store these credentials into the config file -CREDS_FILE=$NEUTRONCLIENT_DIR/functional_creds.conf -cat < $CREDS_FILE -# Credentials for functional testing -[auth] -uri = $OS_AUTH_URL - -[admin] -user = $OS_USERNAME -tenant = $OS_TENANT_NAME -pass = $OS_PASSWORD -EOF - # Go to the neutronclient dir cd $NEUTRONCLIENT_DIR diff --git a/test-requirements.txt b/test-requirements.txt index 966a54316..ce4ccb1e5 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -9,6 +9,7 @@ discover fixtures>=1.3.1 mox3>=0.7.0 mock>=1.2 +os-client-config>=1.4.0,!=1.6.2 oslosphinx>=2.5.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 python-subunit>=0.0.18 From 8e741981b11a41dfa6f53f96ff1bbe37e30e4151 Mon Sep 17 00:00:00 2001 From: Manjeet Singh Bhatia Date: Thu, 15 Oct 2015 04:31:08 +0000 Subject: [PATCH 279/845] Adding a generate_default_ethertype_function Neutron Client is always setting ethertype value to IPv4 which should not be done if protocol is icmpv6. This patch will add a function that will generate appropriate default value of ethertype according to protocol. Change-Id: Ia1a5342a1d568cb1a015e1b7acecf38b8d1f46e1 Related-Bug: #1505832 --- neutronclient/neutron/v2_0/securitygroup.py | 10 ++++++++-- neutronclient/tests/unit/test_cli20_securitygroup.py | 8 ++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/neutronclient/neutron/v2_0/securitygroup.py b/neutronclient/neutron/v2_0/securitygroup.py index 962b3f874..2fb0bb95d 100644 --- a/neutronclient/neutron/v2_0/securitygroup.py +++ b/neutronclient/neutron/v2_0/securitygroup.py @@ -91,6 +91,12 @@ def _format_sg_rules(secgroup): return '' +def generate_default_ethertype(protocol): + if protocol == 'icmpv6': + return 'IPv6' + return 'IPv4' + + class ListSecurityGroup(neutronV20.ListCommand): """List security groups that belong to a given tenant.""" @@ -303,7 +309,6 @@ def add_known_arguments(self, parser): help=_('Direction of traffic: ingress/egress.')) parser.add_argument( '--ethertype', - default='IPv4', help=_('IPv4/IPv6')) parser.add_argument( '--protocol', @@ -338,7 +343,8 @@ def args2body(self, parsed_args): self.get_client(), 'security_group', parsed_args.security_group_id) body = {'security_group_id': _security_group_id, 'direction': parsed_args.direction, - 'ethertype': parsed_args.ethertype} + 'ethertype': parsed_args.ethertype or + generate_default_ethertype(parsed_args.protocol)} neutronV20.update_dict(parsed_args, body, ['protocol', 'port_range_min', 'port_range_max', 'remote_ip_prefix', 'tenant_id']) diff --git a/neutronclient/tests/unit/test_cli20_securitygroup.py b/neutronclient/tests/unit/test_cli20_securitygroup.py index 758162d48..61b86fd9d 100644 --- a/neutronclient/tests/unit/test_cli20_securitygroup.py +++ b/neutronclient/tests/unit/test_cli20_securitygroup.py @@ -570,6 +570,14 @@ def test__get_protocol_port_icmp_all(self): sg_rule = self._prepare_rule(protocol='icmp') self.assertEqual('icmp', securitygroup._get_protocol_port(sg_rule)) + def test_get_ethertype_for_protocol_icmpv6(self): + self.assertEqual('IPv6', + securitygroup.generate_default_ethertype('icmpv6')) + + def test_get_ethertype_for_protocol_icmp(self): + self.assertEqual('IPv4', + securitygroup.generate_default_ethertype('icmp')) + def test__get_protocol_port_udp_code_type(self): sg_rule = self._prepare_rule(protocol='icmp', port_range_min=1, port_range_max=8) From 3d736107f97c27a35cff2d7ed6c041521be5ab03 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Sat, 24 Oct 2015 00:27:35 +0000 Subject: [PATCH 280/845] Updated from global requirements Change-Id: I9ff8197e956aa3994ecee38203de4654ee272581 --- requirements.txt | 2 +- test-requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index cf5f83c87..9168054f9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,7 +7,7 @@ cliff>=1.14.0 # Apache-2.0 iso8601>=0.1.9 netaddr!=0.7.16,>=0.7.12 oslo.i18n>=1.5.0 # Apache-2.0 -oslo.serialization>=1.4.0 # Apache-2.0 +oslo.serialization>=1.10.0 # Apache-2.0 oslo.utils!=2.6.0,>=2.4.0 # Apache-2.0 requests!=2.8.0,>=2.5.2 python-keystoneclient!=1.8.0,>=1.6.0 diff --git a/test-requirements.txt b/test-requirements.txt index ce4ccb1e5..1fffcfc9c 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -9,7 +9,7 @@ discover fixtures>=1.3.1 mox3>=0.7.0 mock>=1.2 -os-client-config>=1.4.0,!=1.6.2 +os-client-config!=1.6.2,>=1.4.0 oslosphinx>=2.5.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 python-subunit>=0.0.18 From 2eed8ea24a703825d51d1270a2e1502cd7bec551 Mon Sep 17 00:00:00 2001 From: Monty Taylor Date: Sat, 17 Oct 2015 17:57:32 -0400 Subject: [PATCH 281/845] Use os-client-config and keystoneauth1 in shell keystoneauth1 is the new auth-only library for getting keystone Sessions, which is lighter-weight and does not require the entire keystoneclient library. It also handles all of the keystone version discover and plugin selection so that code doesn't have to live in neutronclient. Additionally, use os-client-config to process options and get the Session from keystoneauth1. This adds support for reading clouds.yaml files and supporting the OS_CLOUD env var for selecting named clouds from a list of them. This is a step towards bug#1503428 but is not the whole picture. Remove the auth tests - since they are covered inside of ksa. Closes-Bug: #1507384 Change-Id: Ic4f9fd8f231c33513fd74da58ab1b4a3fb00d9f4 --- neutronclient/client.py | 12 +- neutronclient/common/clientmanager.py | 3 + neutronclient/shell.py | 221 ++------- .../tests/functional/core/test_clientlib.py | 46 +- neutronclient/tests/unit/test_auth.py | 434 ------------------ neutronclient/tests/unit/test_http.py | 9 - neutronclient/tests/unit/test_shell.py | 300 +----------- neutronclient/tests/unit/test_ssl.py | 179 -------- requirements.txt | 3 +- test-requirements.txt | 1 - 10 files changed, 68 insertions(+), 1140 deletions(-) delete mode 100644 neutronclient/tests/unit/test_auth.py delete mode 100644 neutronclient/tests/unit/test_ssl.py diff --git a/neutronclient/client.py b/neutronclient/client.py index 0ab732ec3..77076304e 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -21,8 +21,8 @@ import logging import os -from keystoneclient import access -from keystoneclient import adapter +from keystoneauth1 import access +from keystoneauth1 import adapter import requests from neutronclient.common import exceptions @@ -179,7 +179,7 @@ def do_request(self, url, method, **kwargs): def _extract_service_catalog(self, body): """Set the client's service catalog from the response data.""" - self.auth_ref = access.AccessInfo.factory(body=body) + self.auth_ref = access.create(body=body) self.service_catalog = self.auth_ref.service_catalog self.auth_token = self.auth_ref.auth_token self.auth_tenant_id = self.auth_ref.tenant_id @@ -187,9 +187,9 @@ def _extract_service_catalog(self, body): if not self.endpoint_url: self.endpoint_url = self.service_catalog.url_for( - attr='region', filter_value=self.region_name, + region_name=self.region_name, service_type=self.service_type, - endpoint_type=self.endpoint_type) + interface=self.endpoint_type) def _authenticate_keystone(self): if self.user_id: @@ -355,7 +355,7 @@ def construct_http_client(username=None, timeout=None, endpoint_url=None, insecure=False, - endpoint_type='publicURL', + endpoint_type='public', log_credentials=None, auth_strategy='keystone', ca_cert=None, diff --git a/neutronclient/common/clientmanager.py b/neutronclient/common/clientmanager.py index e9cc88574..7948cf101 100644 --- a/neutronclient/common/clientmanager.py +++ b/neutronclient/common/clientmanager.py @@ -62,6 +62,7 @@ def __init__(self, token=None, url=None, ca_cert=None, log_credentials=False, service_type=None, + service_name=None, timeout=None, retries=0, raise_errors=True, @@ -72,6 +73,7 @@ def __init__(self, token=None, url=None, self._url = url self._auth_url = auth_url self._service_type = service_type + self._service_name = service_name self._endpoint_type = endpoint_type self._tenant_name = tenant_name self._tenant_id = tenant_id @@ -103,6 +105,7 @@ def initialize(self): region_name=self._region_name, auth_url=self._auth_url, service_type=self._service_type, + service_name=self._service_name, endpoint_type=self._endpoint_type, insecure=self._insecure, ca_cert=self._ca_cert, diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 2c25c8365..6cbe7a342 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -21,20 +21,15 @@ from __future__ import print_function import argparse -import getpass import inspect import itertools import logging import os import sys -from keystoneclient.auth.identity import v2 as v2_auth -from keystoneclient.auth.identity import v3 as v3_auth -from keystoneclient import discover -from keystoneclient.openstack.common.apiclient import exceptions as ks_exc -from keystoneclient import session +from keystoneauth1 import session +import os_client_config from oslo_utils import encodeutils -import six.moves.urllib.parse as urlparse from cliff import app from cliff import commandmanager @@ -499,7 +494,7 @@ def build_option_parser(self, description, version): default=0, help=_("How many times the request to the Neutron server should " "be retried if it fails.")) - # FIXME(bklei): this method should come from python-keystoneclient + # FIXME(bklei): this method should come from keystoneauth1 self._append_global_identity_args(parser) return parser @@ -507,9 +502,9 @@ def build_option_parser(self, description, version): def _append_global_identity_args(self, parser): # FIXME(bklei): these are global identity (Keystone) arguments which # should be consistent and shared by all service clients. Therefore, - # they should be provided by python-keystoneclient. We will need to + # they should be provided by keystoneauth1. We will need to # refactor this code once this functionality is available in - # python-keystoneclient. + # keystoneauth1. # # Note: At that time we'll need to decide if we can just abandon # the deprecated args (--service-type and --endpoint-type). @@ -521,8 +516,8 @@ def _append_global_identity_args(self, parser): parser.add_argument( '--os-endpoint-type', metavar='', - default=env('OS_ENDPOINT_TYPE', default='publicURL'), - help=_('Defaults to env[OS_ENDPOINT_TYPE] or publicURL.')) + default=env('OS_ENDPOINT_TYPE', default='public'), + help=_('Defaults to env[OS_ENDPOINT_TYPE] or public.')) # FIXME(bklei): --service-type is deprecated but kept in for # backward compatibility. @@ -535,7 +530,7 @@ def _append_global_identity_args(self, parser): # backward compatibility. parser.add_argument( '--endpoint-type', metavar='', - default=env('OS_ENDPOINT_TYPE', default='publicURL'), + default=env('OS_ENDPOINT_TYPE', default='public'), help=_('DEPRECATED! Use --os-endpoint-type.')) parser.add_argument( @@ -547,6 +542,11 @@ def _append_global_identity_args(self, parser): '--os_auth_strategy', help=argparse.SUPPRESS) + parser.add_argument( + '--os-cloud', metavar='', + default=env('OS_CLOUD', default=None), + help=_('Defaults to env[OS_CLOUD].')) + parser.add_argument( '--os-auth-url', metavar='', default=env('OS_AUTH_URL'), @@ -829,104 +829,28 @@ def authenticate_user(self): """Make sure the user has provided all of the authentication info we need. """ - if self.options.os_auth_strategy == 'keystone': - if self.options.os_token or self.options.os_url: - # Token flow auth takes priority - if not self.options.os_token: - raise exc.CommandError( - _("You must provide a token via" - " either --os-token or env[OS_TOKEN]" - " when providing a service URL")) - - if not self.options.os_url: - raise exc.CommandError( - _("You must provide a service URL via" - " either --os-url or env[OS_URL]" - " when providing a token")) - - else: - # Validate password flow auth - project_info = (self.options.os_tenant_name or - self.options.os_tenant_id or - (self.options.os_project_name and - (self.options.os_project_domain_name or - self.options.os_project_domain_id)) or - self.options.os_project_id) - - if (not self.options.os_username - and not self.options.os_user_id): - raise exc.CommandError( - _("You must provide a username or user ID via" - " --os-username, env[OS_USERNAME] or" - " --os-user-id, env[OS_USER_ID]")) - - if not self.options.os_password: - # No password, If we've got a tty, try prompting for it - if hasattr(sys.stdin, 'isatty') and sys.stdin.isatty(): - # Check for Ctl-D - try: - self.options.os_password = getpass.getpass( - 'OS Password: ') - except EOFError: - pass - # No password because we didn't have a tty or the - # user Ctl-D when prompted. - if not self.options.os_password: - raise exc.CommandError( - _("You must provide a password via" - " either --os-password or env[OS_PASSWORD]")) - - if (not project_info): - # tenent is deprecated in Keystone v3. Use the latest - # terminology instead. - raise exc.CommandError( - _("You must provide a project_id or project_name (" - "with project_domain_name or project_domain_id) " - "via " - " --os-project-id (env[OS_PROJECT_ID])" - " --os-project-name (env[OS_PROJECT_NAME])," - " --os-project-domain-id " - "(env[OS_PROJECT_DOMAIN_ID])" - " --os-project-domain-name " - "(env[OS_PROJECT_DOMAIN_NAME])")) - - if not self.options.os_auth_url: - raise exc.CommandError( - _("You must provide an auth url via" - " either --os-auth-url or via env[OS_AUTH_URL]")) - auth_session = self._get_keystone_session() - auth = auth_session.auth - else: # not keystone - if not self.options.os_url: - raise exc.CommandError( - _("You must provide a service URL via" - " either --os-url or env[OS_URL]")) - auth_session = None - auth = None - + cloud_config = os_client_config.OpenStackConfig().get_one_cloud( + cloud=self.options.os_cloud, argparse=self.options, + network_api_version=self.api_version) + verify, cert = cloud_config.get_requests_verify_args() + auth = cloud_config.get_auth() + + auth_session = session.Session( + auth=auth, verify=verify, cert=cert, + timeout=self.options.http_timeout) + + interface = self.options.os_endpoint_type or self.endpoint_type + if interface.endswith('URL'): + interface = interface[:-3] self.client_manager = clientmanager.ClientManager( - token=self.options.os_token, - url=self.options.os_url, - auth_url=self.options.os_auth_url, - tenant_name=self.options.os_tenant_name, - tenant_id=self.options.os_tenant_id, - username=self.options.os_username, - user_id=self.options.os_user_id, - password=self.options.os_password, - region_name=self.options.os_region_name, - api_version=self.api_version, - auth_strategy=self.options.os_auth_strategy, - # FIXME (bklei) honor deprecated service_type and - # endpoint type until they are removed - service_type=self.options.os_service_type or - self.options.service_type, - endpoint_type=self.options.os_endpoint_type or self.endpoint_type, - insecure=self.options.insecure, - ca_cert=self.options.os_cacert, - timeout=self.options.http_timeout, retries=self.options.retries, raise_errors=False, session=auth_session, + region_name=cloud_config.get_region_name(), + api_version=cloud_config.get_api_version('network'), + service_type=cloud_config.get_service_type('network'), + service_name=cloud_config.get_service_name('network'), + endpoint_type=interface, auth=auth, log_credentials=True) return @@ -981,89 +905,6 @@ def configure_logging(self): root_logger.addHandler(console) return - def get_v2_auth(self, v2_auth_url): - return v2_auth.Password( - v2_auth_url, - username=self.options.os_username, - password=self.options.os_password, - tenant_id=self.options.os_tenant_id, - tenant_name=self.options.os_tenant_name) - - def get_v3_auth(self, v3_auth_url): - project_id = self.options.os_project_id or self.options.os_tenant_id - project_name = (self.options.os_project_name or - self.options.os_tenant_name) - - return v3_auth.Password( - v3_auth_url, - username=self.options.os_username, - password=self.options.os_password, - user_id=self.options.os_user_id, - user_domain_name=self.options.os_user_domain_name, - user_domain_id=self.options.os_user_domain_id, - project_id=project_id, - project_name=project_name, - project_domain_name=self.options.os_project_domain_name, - project_domain_id=self.options.os_project_domain_id - ) - - def _discover_auth_versions(self, session, auth_url): - # discover the API versions the server is supporting base on the - # given URL - try: - ks_discover = discover.Discover(session=session, auth_url=auth_url) - return (ks_discover.url_for('2.0'), ks_discover.url_for('3.0')) - except ks_exc.ClientException: - # Identity service may not support discover API version. - # Lets try to figure out the API version from the original URL. - url_parts = urlparse.urlparse(auth_url) - (scheme, netloc, path, params, query, fragment) = url_parts - path = path.lower() - if path.startswith('/v3'): - return (None, auth_url) - elif path.startswith('/v2'): - return (auth_url, None) - else: - # not enough information to determine the auth version - msg = _('Unable to determine the Keystone version ' - 'to authenticate with using the given ' - 'auth_url. Identity service may not support API ' - 'version discovery. Please provide a versioned ' - 'auth_url instead.') - raise exc.CommandError(msg) - - def _get_keystone_session(self): - # first create a Keystone session - cacert = self.options.os_cacert or None - cert = self.options.os_cert or None - key = self.options.os_key or None - insecure = self.options.insecure or False - ks_session = session.Session.construct(dict(cacert=cacert, - cert=cert, - key=key, - insecure=insecure)) - # discover the supported keystone versions using the given url - (v2_auth_url, v3_auth_url) = self._discover_auth_versions( - session=ks_session, - auth_url=self.options.os_auth_url) - - # Determine which authentication plugin to use. First inspect the - # auth_url to see the supported version. If both v3 and v2 are - # supported, then use the highest version if possible. - user_domain_name = self.options.os_user_domain_name or None - user_domain_id = self.options.os_user_domain_id or None - project_domain_name = self.options.os_project_domain_name or None - project_domain_id = self.options.os_project_domain_id or None - domain_info = (user_domain_name or user_domain_id or - project_domain_name or project_domain_id) - - if (v2_auth_url and not domain_info) or not v3_auth_url: - ks_session.auth = self.get_v2_auth(v2_auth_url) - else: - ks_session.auth = self.get_v3_auth(v3_auth_url) - - return ks_session - def main(argv=sys.argv[1:]): try: diff --git a/neutronclient/tests/functional/core/test_clientlib.py b/neutronclient/tests/functional/core/test_clientlib.py index 40d6382c1..91fb6be1c 100644 --- a/neutronclient/tests/functional/core/test_clientlib.py +++ b/neutronclient/tests/functional/core/test_clientlib.py @@ -12,9 +12,8 @@ import uuid -from keystoneclient.auth.identity import v2 as v2_auth -from keystoneclient import discover -from keystoneclient import session +from keystoneauth1 import plugin as ksa_plugin +from keystoneauth1 import session from tempest_lib import base import testtools @@ -38,13 +37,22 @@ def setUp(self): class Libv2HTTPClientTestBase(LibraryTestBase): def _get_client(self): + creds = func_base.credentials() - session_params = {} - ks_session = session.Session.construct(session_params) - ks_discover = discover.Discover(session=ks_session, - auth_url=creds['auth_url']) - # At the moment, we use keystone v2 API - v2_auth_url = ks_discover.url_for('2.0') + cloud_config = func_base.get_cloud_config() + + # We're getting a session so we can find the v2 url via KSA + keystone_auth = cloud_config.get_auth() + (verify, cert) = cloud_config.get_requests_verify_args() + + ks_session = session.Session( + auth=keystone_auth, verify=verify, cert=cert) + + # for the old HTTPClient, we use keystone v2 API, regardless of + # whether v3 also exists or is configured + v2_auth_url = keystone_auth.get_endpoint( + ks_session, interface=ksa_plugin.AUTH_INTERFACE, version=(2, 0)) + return v2_client.Client(username=creds['username'], password=creds['password'], tenant_name=creds['project_name'], @@ -54,18 +62,14 @@ def _get_client(self): class Libv2SessionClientTestBase(LibraryTestBase): def _get_client(self): - creds = func_base.credentials() - session_params = {} - ks_session = session.Session.construct(session_params) - ks_discover = discover.Discover(session=ks_session, - auth_url=creds['auth_url']) - # At the moment, we use keystone v2 API - v2_auth_url = ks_discover.url_for('2.0') - ks_session.auth = v2_auth.Password( - v2_auth_url, - username=creds['username'], - password=creds['password'], - tenant_name=creds['project_name']) + cloud_config = func_base.get_cloud_config() + keystone_auth = cloud_config.get_auth() + (verify, cert) = cloud_config.get_requests_verify_args() + + ks_session = session.Session( + auth=keystone_auth, + verify=verify, + cert=cert) return v2_client.Client(session=ks_session) diff --git a/neutronclient/tests/unit/test_auth.py b/neutronclient/tests/unit/test_auth.py deleted file mode 100644 index b2f55400c..000000000 --- a/neutronclient/tests/unit/test_auth.py +++ /dev/null @@ -1,434 +0,0 @@ -# Copyright 2012 NEC Corporation -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import json -import logging -import uuid - -import fixtures -from oslo_serialization import jsonutils -from requests_mock.contrib import fixture as mock_fixture -import testtools - -from keystoneclient.auth.identity import v2 as ks_v2_auth -from keystoneclient.auth.identity import v3 as ks_v3_auth -from keystoneclient import exceptions as ks_exceptions -from keystoneclient import fixture as ks_fixture -from keystoneclient import session - -from neutronclient import client -from neutronclient.common import exceptions - - -USERNAME = 'testuser' -USER_ID = 'testuser_id' -TENANT_NAME = 'testtenant' -TENANT_ID = 'testtenant_id' -PASSWORD = 'password' -ENDPOINT_URL = 'http://localurl' -PUBLIC_ENDPOINT_URL = '%s/public' % ENDPOINT_URL -ADMIN_ENDPOINT_URL = '%s/admin' % ENDPOINT_URL -INTERNAL_ENDPOINT_URL = '%s/internal' % ENDPOINT_URL -ENDPOINT_OVERRIDE = 'http://otherurl' -TOKENID = uuid.uuid4().hex -REGION = 'RegionOne' -NOAUTH = 'noauth' - -KS_TOKEN_RESULT = ks_fixture.V2Token() -KS_TOKEN_RESULT.set_scope() -_s = KS_TOKEN_RESULT.add_service('network', 'Neutron Service') -_s.add_endpoint(ENDPOINT_URL, region=REGION) - -ENDPOINTS_RESULT = { - 'endpoints': [{ - 'type': 'network', - 'name': 'Neutron Service', - 'region': REGION, - 'adminURL': ENDPOINT_URL, - 'internalURL': ENDPOINT_URL, - 'publicURL': ENDPOINT_URL - }] -} - -BASE_URL = "http://keystone.example.com:5000/" - -V2_URL = "%sv2.0" % BASE_URL -V3_URL = "%sv3" % BASE_URL - -_v2 = ks_fixture.V2Discovery(V2_URL) -_v3 = ks_fixture.V3Discovery(V3_URL) - -V3_VERSION_LIST = jsonutils.dumps({'versions': {'values': [_v2, _v3]}}) - -V2_VERSION_ENTRY = {'version': _v2} -V3_VERSION_ENTRY = {'version': _v3} - - -def setup_keystone_v2(mrequests): - v2_token = ks_fixture.V2Token(token_id=TOKENID) - service = v2_token.add_service('network') - service.add_endpoint(PUBLIC_ENDPOINT_URL, region=REGION) - - mrequests.register_uri('POST', - '%s/tokens' % (V2_URL), - json=v2_token) - - auth_session = session.Session() - auth_plugin = ks_v2_auth.Password(V2_URL, 'xx', 'xx') - return auth_session, auth_plugin - - -def setup_keystone_v3(mrequests): - mrequests.register_uri('GET', - V3_URL, - json=V3_VERSION_ENTRY) - - v3_token = ks_fixture.V3Token() - service = v3_token.add_service('network') - service.add_standard_endpoints(public=PUBLIC_ENDPOINT_URL, - admin=ADMIN_ENDPOINT_URL, - internal=INTERNAL_ENDPOINT_URL, - region=REGION) - - mrequests.register_uri('POST', - '%s/auth/tokens' % (V3_URL), - text=json.dumps(v3_token), - headers={'X-Subject-Token': TOKENID}) - - auth_session = session.Session() - auth_plugin = ks_v3_auth.Password(V3_URL, - username='xx', - user_id='xx', - user_domain_name='xx', - user_domain_id='xx') - return auth_session, auth_plugin - - -AUTH_URL = V2_URL - - -class CLITestAuthNoAuth(testtools.TestCase): - - def setUp(self): - """Prepare the test environment.""" - super(CLITestAuthNoAuth, self).setUp() - - self.requests = self.useFixture(mock_fixture.Fixture()) - - self.client = client.HTTPClient(username=USERNAME, - tenant_name=TENANT_NAME, - password=PASSWORD, - endpoint_url=ENDPOINT_URL, - auth_strategy=NOAUTH, - region_name=REGION) - - def test_get_noauth(self): - url = ENDPOINT_URL + '/resource' - self.requests.get(ENDPOINT_URL + '/resource') - self.client.do_request('/resource', 'GET') - self.assertEqual(url, self.requests.last_request.url) - self.assertEqual(ENDPOINT_URL, self.client.endpoint_url) - - -class CLITestAuthKeystone(testtools.TestCase): - - def setUp(self): - """Prepare the test environment.""" - super(CLITestAuthKeystone, self).setUp() - - for var in ('http_proxy', 'HTTP_PROXY'): - self.useFixture(fixtures.EnvironmentVariableFixture(var)) - - self.logger = self.useFixture(fixtures.FakeLogger(level=logging.DEBUG)) - self.requests = self.useFixture(mock_fixture.Fixture()) - - self.client = client.construct_http_client( - username=USERNAME, - tenant_name=TENANT_NAME, - password=PASSWORD, - auth_url=AUTH_URL, - region_name=REGION) - - def test_reused_token_get_auth_info(self): - """Test that Client.get_auth_info() works even if client was - instantiated with predefined token. - """ - token_id = uuid.uuid4().hex - client_ = client.HTTPClient(username=USERNAME, - tenant_name=TENANT_NAME, - token=token_id, - password=PASSWORD, - auth_url=AUTH_URL, - region_name=REGION) - expected = {'auth_token': token_id, - 'auth_tenant_id': None, - 'auth_user_id': None, - 'endpoint_url': self.client.endpoint_url} - self.assertEqual(expected, client_.get_auth_info()) - - def test_get_token(self): - auth_session, auth_plugin = setup_keystone_v2(self.requests) - - self.client = client.construct_http_client( - username=USERNAME, - tenant_name=TENANT_NAME, - password=PASSWORD, - auth_url=AUTH_URL, - region_name=REGION, - session=auth_session, - auth=auth_plugin) - - m = self.requests.get(PUBLIC_ENDPOINT_URL + '/resource', - request_headers={'X-Auth-Token': TOKENID}) - self.client.do_request('/resource', 'GET') - self.assertTrue(m.called) - - def test_refresh_token(self): - token_id = uuid.uuid4().hex - text = uuid.uuid4().hex - self.client.auth_token = token_id - self.client.endpoint_url = ENDPOINT_URL - - res_url = ENDPOINT_URL + '/resource' - v2_url = AUTH_URL + '/tokens' - - # token_id gives 401, KS_TOKEN_RESULT gives 200 - self.requests.get(res_url, - request_headers={'X-Auth-Token': token_id}, - status_code=401) - - self.requests.get( - res_url, - text=text, - status_code=200, - request_headers={'X-Auth-Token': KS_TOKEN_RESULT.token_id}) - - self.requests.post(v2_url, json=KS_TOKEN_RESULT) - - resp = self.client.do_request('/resource', 'GET') - - self.assertEqual(text, resp[1]) - self.assertEqual(3, len(self.requests.request_history)) - - self.assertEqual(res_url, self.requests.request_history[0].url) - self.assertEqual(v2_url, self.requests.request_history[1].url) - self.assertEqual(res_url, self.requests.request_history[2].url) - - def test_refresh_token_no_auth_url(self): - self.client.auth_url = None - - token_id = uuid.uuid4().hex - self.client.auth_token = token_id - self.client.endpoint_url = ENDPOINT_URL - - self.requests.get(ENDPOINT_URL + '/resource', status_code=401) - self.assertRaises(exceptions.NoAuthURLProvided, - self.client.do_request, - '/resource', - 'GET') - - def test_get_endpoint_url_with_invalid_auth_url(self): - # Handle the case when auth_url is not provided - self.client.auth_url = None - self.assertRaises(exceptions.NoAuthURLProvided, - self.client._get_endpoint_url) - - def test_get_endpoint_url(self): - token_id = uuid.uuid4().hex - self.client.auth_token = token_id - - self.requests.get(AUTH_URL + '/tokens/%s/endpoints' % token_id, - json=ENDPOINTS_RESULT) - self.requests.get(ENDPOINT_URL + '/resource') - - self.client.do_request('/resource', 'GET') - - self.assertEqual(token_id, - self.requests.last_request.headers['X-Auth-Token']) - - def test_use_given_endpoint_url(self): - self.client = client.HTTPClient( - username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD, - auth_url=AUTH_URL, region_name=REGION, - endpoint_url=ENDPOINT_OVERRIDE) - self.assertEqual(ENDPOINT_OVERRIDE, self.client.endpoint_url) - - token_id = uuid.uuid4().hex - self.client.auth_token = token_id - - self.requests.get(ENDPOINT_OVERRIDE + '/resource') - - self.client.do_request('/resource', 'GET') - - self.assertEqual(ENDPOINT_OVERRIDE, self.client.endpoint_url) - self.assertEqual(token_id, - self.requests.last_request.headers['X-Auth-Token']) - - def test_get_endpoint_url_other(self): - self.client = client.HTTPClient( - username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD, - auth_url=AUTH_URL, region_name=REGION, endpoint_type='otherURL') - - token_id = uuid.uuid4().hex - self.client.auth_token = token_id - - self.requests.get(AUTH_URL + '/tokens/%s/endpoints' % token_id, - json=ENDPOINTS_RESULT) - - self.assertRaises(exceptions.EndpointTypeNotFound, - self.client.do_request, - '/resource', - 'GET') - - def test_get_endpoint_url_failed(self): - token_id = uuid.uuid4().hex - self.client.auth_token = token_id - - self.requests.get(AUTH_URL + '/tokens/%s/endpoints' % token_id, - status_code=401) - self.requests.post(AUTH_URL + '/tokens', json=KS_TOKEN_RESULT) - m = self.requests.get(ENDPOINT_URL + '/resource') - - self.client.do_request('/resource', 'GET') - - self.assertEqual(KS_TOKEN_RESULT.token_id, - m.last_request.headers['X-Auth-Token']) - - def test_endpoint_type(self): - auth_session, auth_plugin = setup_keystone_v3(self.requests) - - # Test default behavior is to choose public. - self.client = client.construct_http_client( - username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD, - auth_url=AUTH_URL, region_name=REGION, - session=auth_session, auth=auth_plugin) - - self.assertEqual(PUBLIC_ENDPOINT_URL, self.client.endpoint_url) - - # Test admin url - self.client = client.construct_http_client( - username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD, - auth_url=AUTH_URL, region_name=REGION, endpoint_type='adminURL', - session=auth_session, auth=auth_plugin) - - self.assertEqual(ADMIN_ENDPOINT_URL, self.client.endpoint_url) - - # Test public url - self.client = client.construct_http_client( - username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD, - auth_url=AUTH_URL, region_name=REGION, endpoint_type='publicURL', - session=auth_session, auth=auth_plugin) - - self.assertEqual(PUBLIC_ENDPOINT_URL, self.client.endpoint_url) - - # Test internal url - self.client = client.construct_http_client( - username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD, - auth_url=AUTH_URL, region_name=REGION, endpoint_type='internalURL', - session=auth_session, auth=auth_plugin) - - self.assertEqual(INTERNAL_ENDPOINT_URL, self.client.endpoint_url) - - # Test url that isn't found in the service catalog - self.client = client.construct_http_client( - username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD, - auth_url=AUTH_URL, region_name=REGION, endpoint_type='privateURL', - session=auth_session, auth=auth_plugin) - - self.assertRaises( - ks_exceptions.EndpointNotFound, - getattr, self.client, 'endpoint_url') - - def test_strip_credentials_from_log(self): - m = self.requests.post(AUTH_URL + '/tokens', json=KS_TOKEN_RESULT) - self.requests.get(ENDPOINT_URL + '/resource') - self.client.do_request('/resource', 'GET') - - self.assertIn('REDACTED', self.logger.output) - self.assertNotIn(self.client.password, self.logger.output) - - self.assertNotIn('REDACTED', m.last_request.body) - self.assertIn(self.client.password, m.last_request.body) - - -class CLITestAuthKeystoneWithId(CLITestAuthKeystone): - - def setUp(self): - """Prepare the test environment.""" - super(CLITestAuthKeystoneWithId, self).setUp() - self.client = client.HTTPClient(user_id=USER_ID, - tenant_id=TENANT_ID, - password=PASSWORD, - auth_url=AUTH_URL, - region_name=REGION) - - -class CLITestAuthKeystoneWithIdandName(CLITestAuthKeystone): - - def setUp(self): - """Prepare the test environment.""" - super(CLITestAuthKeystoneWithIdandName, self).setUp() - self.client = client.HTTPClient(username=USERNAME, - user_id=USER_ID, - tenant_id=TENANT_ID, - tenant_name=TENANT_NAME, - password=PASSWORD, - auth_url=AUTH_URL, - region_name=REGION) - - -class TestKeystoneClientVersions(testtools.TestCase): - - def setUp(self): - """Prepare the test environment.""" - super(TestKeystoneClientVersions, self).setUp() - self.requests = self.useFixture(mock_fixture.Fixture()) - - def test_v2_auth(self): - auth_session, auth_plugin = setup_keystone_v2(self.requests) - - self.client = client.construct_http_client( - username=USERNAME, - tenant_name=TENANT_NAME, - password=PASSWORD, - auth_url=AUTH_URL, - region_name=REGION, - session=auth_session, - auth=auth_plugin) - - m = self.requests.get(PUBLIC_ENDPOINT_URL + '/resource') - - self.client.do_request('/resource', 'GET') - - self.assertTrue(m.called) - - def test_v3_auth(self): - auth_session, auth_plugin = setup_keystone_v3(self.requests) - - self.client = client.construct_http_client( - user_id=USER_ID, - tenant_id=TENANT_ID, - password=PASSWORD, - auth_url=V3_URL, - region_name=REGION, - session=auth_session, - auth=auth_plugin) - - m = self.requests.get(PUBLIC_ENDPOINT_URL + '/resource') - - self.client.do_request('/resource', 'GET') - - self.assertTrue(m.called) diff --git a/neutronclient/tests/unit/test_http.py b/neutronclient/tests/unit/test_http.py index 6e04b62df..761cc6fa8 100644 --- a/neutronclient/tests/unit/test_http.py +++ b/neutronclient/tests/unit/test_http.py @@ -21,7 +21,6 @@ from neutronclient import client from neutronclient.common import exceptions -from neutronclient.tests.unit import test_auth AUTH_TOKEN = 'test_token' @@ -74,14 +73,6 @@ def test_headers_defined_in_headers(self): self._test_headers(headers, body=BODY, headers=headers) -class TestSessionClient(TestHTTPClientMixin, testtools.TestCase): - - def initialize(self): - session, auth = test_auth.setup_keystone_v2(self.requests) - return [client.SessionClient, - client.SessionClient(session=session, auth=auth)] - - class TestHTTPClient(TestHTTPClientMixin, testtools.TestCase): def initialize(self): diff --git a/neutronclient/tests/unit/test_shell.py b/neutronclient/tests/unit/test_shell.py index d82b29215..0fee6515d 100644 --- a/neutronclient/tests/unit/test_shell.py +++ b/neutronclient/tests/unit/test_shell.py @@ -21,18 +21,11 @@ import fixtures from mox3 import mox -import requests_mock import six import testtools from testtools import matchers -from keystoneclient.auth.identity import v2 as v2_auth -from keystoneclient.auth.identity import v3 as v3_auth -from keystoneclient import session - -from neutronclient.common import clientmanager from neutronclient import shell as openstack_shell -from neutronclient.tests.unit import test_auth as auth DEFAULT_USERNAME = 'username' @@ -150,297 +143,6 @@ def test_bash_completion_command(self): self.assertThat(help_text, matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE)) - def test_unknown_auth_strategy(self): - self.useFixture(fixtures.FakeLogger(level=logging.DEBUG)) - stdout, stderr = self.shell('--os-auth-strategy fake quota-list') - self.assertFalse(stdout) - self.assertEqual('You must provide a service URL via ' - 'either --os-url or env[OS_URL]', stderr.strip()) - - @requests_mock.Mocker() - def test_auth(self, mrequests): - # emulate Keystone version discovery - mrequests.register_uri('GET', - auth.V3_URL, - json=auth.V3_VERSION_ENTRY) - - neutron_shell = openstack_shell.NeutronShell('2.0') - self.addCleanup(self.mox.UnsetStubs) - self.mox.StubOutWithMock(clientmanager.ClientManager, '__init__') - self.mox.StubOutWithMock(neutron_shell, 'run_subcommand') - clientmanager.ClientManager.__init__( - token='', url='', auth_url=auth.V3_URL, - tenant_name='test', tenant_id='tenant_id', - username='test', user_id='', - password='test', region_name='', api_version={'network': '2.0'}, - auth_strategy='keystone', service_type='network', - endpoint_type='publicURL', insecure=False, ca_cert=None, - timeout=None, - raise_errors=False, - retries=0, - auth=mox.IsA(v3_auth.Password), - session=mox.IsA(session.Session), - log_credentials=True) - neutron_shell.run_subcommand(['quota-list']) - self.mox.ReplayAll() - cmdline = ('--os-username test ' - '--os-password test ' - '--os-tenant-name test ' - '--os-auth-url %s ' - '--os-auth-strategy keystone quota-list' - % auth.V3_URL) - neutron_shell.run(cmdline.split()) - self.mox.VerifyAll() - - @requests_mock.Mocker() - def test_auth_cert_and_key(self, mrequests): - # emulate Keystone version discovery - mrequests.register_uri('GET', - auth.V3_URL, - json=auth.V3_VERSION_ENTRY) - - neutron_shell = openstack_shell.NeutronShell('2.0') - self.addCleanup(self.mox.UnsetStubs) - self.mox.StubOutWithMock(clientmanager.ClientManager, '__init__') - self.mox.StubOutWithMock(neutron_shell, 'run_subcommand') - clientmanager.ClientManager.__init__( - token='', url='', auth_url=auth.V3_URL, - tenant_name='test', tenant_id='tenant_id', - username='test', user_id='', - password='test', region_name='', api_version={'network': '2.0'}, - auth_strategy='keystone', service_type='network', - raise_errors=False, - endpoint_type='publicURL', insecure=False, ca_cert=None, retries=0, - timeout=None, - auth=mox.IsA(v3_auth.Password), - session=mox.IsA(session.Session), - log_credentials=True) - neutron_shell.run_subcommand(['quota-list']) - self.mox.ReplayAll() - cmdline = ('--os-username test ' - '--os-password test ' - '--os-tenant-name test ' - '--os-cert test ' - '--os-key test ' - '--os-auth-url %s ' - '--os-auth-strategy keystone quota-list' - % auth.V3_URL) - neutron_shell.run(cmdline.split()) - self.mox.VerifyAll() - - @requests_mock.Mocker() - def test_v2_auth(self, mrequests): - # emulate Keystone version discovery - mrequests.register_uri('GET', - auth.V2_URL, - json=auth.V2_VERSION_ENTRY) - - neutron_shell = openstack_shell.NeutronShell('2.0') - self.addCleanup(self.mox.UnsetStubs) - self.mox.StubOutWithMock(clientmanager.ClientManager, '__init__') - self.mox.StubOutWithMock(neutron_shell, 'run_subcommand') - clientmanager.ClientManager.__init__( - token='', url='', auth_url=auth.V2_URL, - tenant_name='test', tenant_id='tenant_id', - username='test', user_id='', - password='test', region_name='', api_version={'network': '2.0'}, - auth_strategy='keystone', service_type='network', - endpoint_type='publicURL', insecure=False, ca_cert=None, - timeout=None, - raise_errors=False, - retries=0, - auth=mox.IsA(v2_auth.Password), - session=mox.IsA(session.Session), - log_credentials=True) - neutron_shell.run_subcommand(['quota-list']) - self.mox.ReplayAll() - cmdline = ('--os-username test ' - '--os-password test ' - '--os-tenant-name test ' - '--os-auth-url %s ' - '--os-auth-strategy keystone quota-list' - % auth.V2_URL) - neutron_shell.run(cmdline.split()) - self.mox.VerifyAll() - - @requests_mock.Mocker() - def test_failed_auth_version_discovery_v3_auth_url(self, mrequests): - # emulate Keystone version discovery - mrequests.register_uri('GET', - auth.V3_URL, - status_code=405) - - neutron_shell = openstack_shell.NeutronShell('2.0') - self.addCleanup(self.mox.UnsetStubs) - self.mox.StubOutWithMock(clientmanager.ClientManager, '__init__') - self.mox.StubOutWithMock(neutron_shell, 'run_subcommand') - clientmanager.ClientManager.__init__( - token='', url='', auth_url=auth.V3_URL, - tenant_name='test', tenant_id='tenant_id', - username='test', user_id='', - password='test', region_name='', api_version={'network': '2.0'}, - auth_strategy='keystone', service_type='network', - endpoint_type='publicURL', insecure=False, ca_cert=None, - timeout=None, - raise_errors=False, - retries=0, - auth=mox.IsA(v3_auth.Password), - session=mox.IsA(session.Session), - log_credentials=True) - neutron_shell.run_subcommand(['quota-list']) - self.mox.ReplayAll() - cmdline = ('--os-username test ' - '--os-password test ' - '--os-user-domain-name test ' - '--os-tenant-name test ' - '--os-auth-url %s ' - '--os-auth-strategy keystone quota-list' - % auth.V3_URL) - neutron_shell.run(cmdline.split()) - self.mox.VerifyAll() - - @requests_mock.Mocker() - def test_failed_auth_version_discovery_v2_auth_url(self, mrequests): - # emulate Keystone version discovery - mrequests.register_uri('GET', - auth.V2_URL, - status_code=405) - - neutron_shell = openstack_shell.NeutronShell('2.0') - self.addCleanup(self.mox.UnsetStubs) - self.mox.StubOutWithMock(clientmanager.ClientManager, '__init__') - self.mox.StubOutWithMock(neutron_shell, 'run_subcommand') - clientmanager.ClientManager.__init__( - token='', url='', auth_url=auth.V2_URL, - tenant_name='test', tenant_id='tenant_id', - username='test', user_id='', - password='test', region_name='', api_version={'network': '2.0'}, - auth_strategy='keystone', service_type='network', - endpoint_type='publicURL', insecure=False, ca_cert=None, - timeout=None, - raise_errors=False, - retries=0, - auth=mox.IsA(v2_auth.Password), - session=mox.IsA(session.Session), - log_credentials=True) - neutron_shell.run_subcommand(['quota-list']) - self.mox.ReplayAll() - cmdline = ('--os-username test ' - '--os-password test ' - '--os-tenant-name test ' - '--os-auth-url %s ' - '--os-auth-strategy keystone quota-list' - % auth.V2_URL) - neutron_shell.run(cmdline.split()) - self.mox.VerifyAll() - - @requests_mock.Mocker() - def test_auth_version_discovery_v3(self, mrequests): - # emulate Keystone version discovery - mrequests.register_uri('GET', - auth.BASE_URL, - text=auth.V3_VERSION_LIST) - - neutron_shell = openstack_shell.NeutronShell('2.0') - self.addCleanup(self.mox.UnsetStubs) - self.mox.StubOutWithMock(clientmanager.ClientManager, '__init__') - self.mox.StubOutWithMock(neutron_shell, 'run_subcommand') - clientmanager.ClientManager.__init__( - token='', url='', auth_url=auth.BASE_URL, - tenant_name='test', tenant_id='tenant_id', - username='test', user_id='', - password='test', region_name='', api_version={'network': '2.0'}, - auth_strategy='keystone', service_type='network', - endpoint_type='publicURL', insecure=False, ca_cert=None, - timeout=None, - raise_errors=False, - retries=0, - auth=mox.IsA(v3_auth.Password), - session=mox.IsA(session.Session), - log_credentials=True) - neutron_shell.run_subcommand(['quota-list']) - self.mox.ReplayAll() - cmdline = ('--os-username test ' - '--os-password test ' - '--os-user-domain-name test ' - '--os-tenant-name test ' - '--os-auth-url %s ' - '--os-auth-strategy keystone quota-list' - % auth.BASE_URL) - neutron_shell.run(cmdline.split()) - self.mox.VerifyAll() - - @requests_mock.Mocker() - def test_auth_version_discovery_v2(self, mrequests): - # emulate Keystone version discovery - mrequests.register_uri('GET', - auth.BASE_URL, - text=auth.V3_VERSION_LIST) - - neutron_shell = openstack_shell.NeutronShell('2.0') - self.addCleanup(self.mox.UnsetStubs) - self.mox.StubOutWithMock(clientmanager.ClientManager, '__init__') - self.mox.StubOutWithMock(neutron_shell, 'run_subcommand') - clientmanager.ClientManager.__init__( - token='', url='', auth_url=auth.BASE_URL, - tenant_name='test', tenant_id='tenant_id', - username='test', user_id='', - password='test', region_name='', api_version={'network': '2.0'}, - auth_strategy='keystone', service_type='network', - endpoint_type='publicURL', insecure=False, ca_cert=None, - timeout=None, - raise_errors=False, - retries=0, - auth=mox.IsA(v2_auth.Password), - session=mox.IsA(session.Session), - log_credentials=True) - neutron_shell.run_subcommand(['quota-list']) - self.mox.ReplayAll() - cmdline = ('--os-username test ' - '--os-password test ' - '--os-tenant-name test ' - '--os-auth-url %s ' - '--os-auth-strategy keystone quota-list' - % auth.BASE_URL) - neutron_shell.run(cmdline.split()) - self.mox.VerifyAll() - - @requests_mock.Mocker() - def test_insecure_auth(self, mrequests): - # emulate Keystone version discovery - mrequests.register_uri('GET', - auth.V2_URL, - json=auth.V2_VERSION_ENTRY) - - neutron_shell = openstack_shell.NeutronShell('2.0') - self.addCleanup(self.mox.UnsetStubs) - self.mox.StubOutWithMock(clientmanager.ClientManager, '__init__') - self.mox.StubOutWithMock(neutron_shell, 'run_subcommand') - clientmanager.ClientManager.__init__( - token='', url='', auth_url=auth.V2_URL, - tenant_name='test', tenant_id='tenant_id', - username='test', user_id='', - password='test', region_name='', api_version={'network': '2.0'}, - auth_strategy='keystone', service_type='network', - endpoint_type='publicURL', insecure=True, ca_cert=None, - timeout=None, - raise_errors=False, - retries=0, - auth=mox.IgnoreArg(), - session=mox.IgnoreArg(), - log_credentials=True) - neutron_shell.run_subcommand(['quota-list']) - self.mox.ReplayAll() - cmdline = ('--os-username test ' - '--os-password test ' - '--os-tenant-name test ' - '--insecure ' - '--os-auth-url %s ' - '--os-auth-strategy keystone quota-list' - % auth.V2_URL) - neutron_shell.run(cmdline.split()) - self.mox.VerifyAll() - def test_build_option_parser(self): neutron_shell = openstack_shell.NeutronShell('2.0') result = neutron_shell.build_option_parser('descr', '2.0') @@ -465,7 +167,7 @@ def test_endpoint_option(self): # Neither $OS_ENDPOINT_TYPE nor --os-endpoint-type namespace = parser.parse_args([]) - self.assertEqual('publicURL', namespace.os_endpoint_type) + self.assertEqual('public', namespace.os_endpoint_type) # --endpoint-type but not $OS_ENDPOINT_TYPE namespace = parser.parse_args(['--os-endpoint-type=admin']) diff --git a/neutronclient/tests/unit/test_ssl.py b/neutronclient/tests/unit/test_ssl.py deleted file mode 100644 index e28f7c0c5..000000000 --- a/neutronclient/tests/unit/test_ssl.py +++ /dev/null @@ -1,179 +0,0 @@ -# Copyright (C) 2013 OpenStack Foundation. -# 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 fixtures -import requests -import testtools - -from mox3 import mox -import requests_mock - -from neutronclient.client import HTTPClient -from neutronclient.common.clientmanager import ClientManager -from neutronclient.common import exceptions -from neutronclient import shell as openstack_shell -from neutronclient.tests.unit import test_auth as auth - -AUTH_TOKEN = 'test_token' -END_URL = 'test_url' -METHOD = 'GET' -URL = 'http://test.test:1234/v2.0/' -CA_CERT = '/tmp/test/path' - - -class TestSSL(testtools.TestCase): - def setUp(self): - super(TestSSL, self).setUp() - - self.useFixture(fixtures.EnvironmentVariable('OS_TOKEN', AUTH_TOKEN)) - self.useFixture(fixtures.EnvironmentVariable('OS_URL', END_URL)) - - self.mox = mox.Mox() - self.addCleanup(self.mox.UnsetStubs) - - @requests_mock.Mocker() - def test_ca_cert_passed(self, mrequests): - # emulate Keystone version discovery - mrequests.register_uri('GET', - auth.V3_URL, - json=auth.V3_VERSION_ENTRY) - - self.mox.StubOutWithMock(ClientManager, '__init__') - self.mox.StubOutWithMock(openstack_shell.NeutronShell, 'interact') - - ClientManager.__init__( - ca_cert=CA_CERT, - # we are not really interested in other args - api_version=mox.IgnoreArg(), - auth_strategy=mox.IgnoreArg(), - auth_url=mox.IgnoreArg(), - service_type=mox.IgnoreArg(), - endpoint_type=mox.IgnoreArg(), - insecure=mox.IgnoreArg(), - password=mox.IgnoreArg(), - region_name=mox.IgnoreArg(), - tenant_id=mox.IgnoreArg(), - tenant_name=mox.IgnoreArg(), - token=mox.IgnoreArg(), - url=mox.IgnoreArg(), - username=mox.IgnoreArg(), - user_id=mox.IgnoreArg(), - retries=mox.IgnoreArg(), - raise_errors=mox.IgnoreArg(), - log_credentials=mox.IgnoreArg(), - timeout=mox.IgnoreArg(), - auth=mox.IgnoreArg(), - session=mox.IgnoreArg() - ) - openstack_shell.NeutronShell.interact().AndReturn(0) - self.mox.ReplayAll() - - cmdline = ( - '--os-cacert %s --os-auth-url %s' % - (CA_CERT, auth.V3_URL)) - - openstack_shell.NeutronShell('2.0').run(cmdline.split()) - self.mox.VerifyAll() - - @requests_mock.Mocker() - def test_ca_cert_passed_as_env_var(self, mrequests): - - # emulate Keystone version discovery - mrequests.register_uri('GET', - auth.V3_URL, - json=auth.V3_VERSION_ENTRY) - - self.useFixture(fixtures.EnvironmentVariable('OS_CACERT', CA_CERT)) - - self.mox.StubOutWithMock(ClientManager, '__init__') - self.mox.StubOutWithMock(openstack_shell.NeutronShell, 'interact') - - ClientManager.__init__( - ca_cert=CA_CERT, - # we are not really interested in other args - api_version=mox.IgnoreArg(), - auth_strategy=mox.IgnoreArg(), - auth_url=mox.IgnoreArg(), - service_type=mox.IgnoreArg(), - endpoint_type=mox.IgnoreArg(), - insecure=mox.IgnoreArg(), - password=mox.IgnoreArg(), - region_name=mox.IgnoreArg(), - tenant_id=mox.IgnoreArg(), - tenant_name=mox.IgnoreArg(), - token=mox.IgnoreArg(), - url=mox.IgnoreArg(), - username=mox.IgnoreArg(), - user_id=mox.IgnoreArg(), - retries=mox.IgnoreArg(), - raise_errors=mox.IgnoreArg(), - log_credentials=mox.IgnoreArg(), - timeout=mox.IgnoreArg(), - auth=mox.IgnoreArg(), - session=mox.IgnoreArg() - ) - openstack_shell.NeutronShell.interact().AndReturn(0) - self.mox.ReplayAll() - - cmdline = ('--os-auth-url %s' % auth.V3_URL) - openstack_shell.NeutronShell('2.0').run(cmdline.split()) - - self.mox.VerifyAll() - - def test_client_manager_properly_creates_httpclient_instance(self): - self.mox.StubOutWithMock(HTTPClient, '__init__') - HTTPClient.__init__( - ca_cert=CA_CERT, - # we are not really interested in other args - auth_strategy=mox.IgnoreArg(), - auth_url=mox.IgnoreArg(), - endpoint_url=mox.IgnoreArg(), - insecure=mox.IgnoreArg(), - password=mox.IgnoreArg(), - region_name=mox.IgnoreArg(), - tenant_name=mox.IgnoreArg(), - token=mox.IgnoreArg(), - username=mox.IgnoreArg(), - user_id=mox.IgnoreArg(), - tenant_id=mox.IgnoreArg(), - timeout=mox.IgnoreArg(), - log_credentials=mox.IgnoreArg(), - service_type=mox.IgnoreArg(), - endpoint_type=mox.IgnoreArg() - ) - self.mox.ReplayAll() - - version = {'network': '2.0'} - ClientManager(ca_cert=CA_CERT, - api_version=version, - url=END_URL, - token=AUTH_TOKEN).neutron - self.mox.VerifyAll() - - def test_proper_exception_is_raised_when_cert_validation_fails(self): - http = HTTPClient(token=AUTH_TOKEN, endpoint_url=END_URL) - - self.mox.StubOutWithMock(HTTPClient, 'request') - HTTPClient.request( - URL, METHOD, headers=mox.IgnoreArg() - ).AndRaise(requests.exceptions.SSLError) - self.mox.ReplayAll() - - self.assertRaises( - exceptions.SslCertificateValidationError, - http._cs_request, - URL, METHOD - ) - self.mox.VerifyAll() diff --git a/requirements.txt b/requirements.txt index 9168054f9..e83e793da 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,8 +9,9 @@ netaddr!=0.7.16,>=0.7.12 oslo.i18n>=1.5.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 oslo.utils!=2.6.0,>=2.4.0 # Apache-2.0 +os-client-config!=1.6.2,>=1.4.0 +keystoneauth1>=1.0.0 requests!=2.8.0,>=2.5.2 -python-keystoneclient!=1.8.0,>=1.6.0 simplejson>=2.2.0 six>=1.9.0 Babel>=1.3 diff --git a/test-requirements.txt b/test-requirements.txt index 1fffcfc9c..966a54316 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -9,7 +9,6 @@ discover fixtures>=1.3.1 mox3>=0.7.0 mock>=1.2 -os-client-config!=1.6.2,>=1.4.0 oslosphinx>=2.5.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 python-subunit>=0.0.18 From 2119b68610278dcfee299941b6e4b4cfdedd16fe Mon Sep 17 00:00:00 2001 From: karthik s Date: Thu, 5 Nov 2015 16:10:41 +0530 Subject: [PATCH 282/845] Documentation error in show_bandwidth_limit_rule Docstring for the function show_bandwidth_limit_rule() is wrong. Change-Id: Ic98e789f17fabe02dc59b3bea8ca96af0ee3bd71 Closes-Bug: #1513418 --- neutronclient/v2_0/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 05b27b3a6..3cb8ffcf4 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -1715,7 +1715,7 @@ def list_bandwidth_limit_rules(self, policy_id, @APIParamsCall def show_bandwidth_limit_rule(self, rule, policy, body=None): - """Creates a new bandwidth limit rule.""" + """Fetches information of a certain bandwidth limit rule.""" return self.get(self.qos_bandwidth_limit_rule_path % (policy, rule), body=body) From a77d3148b3ed087830ca78b94cf25271fca2de35 Mon Sep 17 00:00:00 2001 From: Nandini Tata Date: Wed, 28 Oct 2015 21:45:11 +0000 Subject: [PATCH 283/845] Create floating IP on a specific subnet ID Implemented optional parameter (with dashed arguments only) for python-neutronclient to create floating IP on a subnet ID. Created corresponding unit test cases. Closes-Bug: #1482496 Change-Id: Iea9175bd534fdfd1bbfa6819f8ef6f45fcbcfa5e --- neutronclient/neutron/v2_0/floatingip.py | 6 +++- .../tests/unit/test_cli20_floatingips.py | 29 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/neutronclient/neutron/v2_0/floatingip.py b/neutronclient/neutron/v2_0/floatingip.py index 15aa69a85..c6b3345f5 100644 --- a/neutronclient/neutron/v2_0/floatingip.py +++ b/neutronclient/neutron/v2_0/floatingip.py @@ -64,6 +64,10 @@ def add_known_arguments(self, parser): parser.add_argument( '--floating-ip-address', help=_('IP address of the floating IP')) + parser.add_argument( + '--subnet', + dest='subnet_id', + help=_('Subnet ID on which you want to create the floating IP.')) def args2body(self, parsed_args): _network_id = neutronV20.find_resourceid_by_name_or_id( @@ -72,7 +76,7 @@ def args2body(self, parsed_args): neutronV20.update_dict(parsed_args, body, ['port_id', 'tenant_id', 'fixed_ip_address', - 'floating_ip_address']) + 'floating_ip_address', 'subnet_id']) return {self.resource: body} diff --git a/neutronclient/tests/unit/test_cli20_floatingips.py b/neutronclient/tests/unit/test_cli20_floatingips.py index 92f527a4b..4f09856b1 100644 --- a/neutronclient/tests/unit/test_cli20_floatingips.py +++ b/neutronclient/tests/unit/test_cli20_floatingips.py @@ -85,6 +85,35 @@ def test_create_floatingip_with_ip_address_of_floating_ip(self): self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) + def test_create_floatingip_with_subnet_id(self): + """Create floatingip: fip1 on a given subnet id.""" + resource = 'floatingip' + cmd = fip.CreateFloatingIP(test_cli20.MyApp(sys.stdout), None) + name = 'fip1' + myid = 'myid' + subnet_id = 'mysubnetid' + + args = [name, '--subnet', subnet_id] + position_values = [name, subnet_id] + position_names = ['floating_network_id', 'subnet_id'] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_create_floatingip_with_subnet_id_and_port(self): + """Create floatingip: fip1 on a given subnet id and port.""" + resource = 'floatingip' + cmd = fip.CreateFloatingIP(test_cli20.MyApp(sys.stdout), None) + name = 'fip1' + myid = 'myid' + pid = 'mypid' + subnet_id = 'mysubnetid' + + args = [name, '--subnet', subnet_id, '--port-id', pid] + position_values = [name, subnet_id, pid] + position_names = ['floating_network_id', 'subnet_id', 'port_id'] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + def test_list_floatingips(self): """list floatingips: -D.""" resources = 'floatingips' From a150ce000da6f713276086280aaaa7848924aed2 Mon Sep 17 00:00:00 2001 From: James Arendt Date: Wed, 19 Aug 2015 12:10:36 -0700 Subject: [PATCH 284/845] Add Neutron flavor framework CLI Provide command line control of Neutron flavor framework resources, adding the following: flavor-create flavor-delete flavor-list flavor-show flavor-update flavor-profile-create flavor-profile-delete flavor-profile-list flavor-profile-show flavor-profile-update as well as ability to manipulate flavor to profile associations: flavor-associate flavor-disassociate Change to the '--enabled {True,False}' syntax. Bump up unit test coverage. ApiImpact DocImpact Implements: blueprint neutron-flavor-framework Change-Id: I0d73c8de223659071eb305d8bd1c699aefaeeb89 --- neutronclient/neutron/v2_0/flavor/__init__.py | 0 neutronclient/neutron/v2_0/flavor/flavor.py | 167 ++++++++++++++++++ .../neutron/v2_0/flavor/flavor_profile.py | 99 +++++++++++ neutronclient/shell.py | 14 ++ neutronclient/tests/unit/flavor/__init__.py | 0 .../tests/unit/flavor/test_cli20_flavor.py | 154 ++++++++++++++++ .../unit/flavor/test_cli20_flavor_profile.py | 122 +++++++++++++ neutronclient/v2_0/client.py | 73 ++++++++ 8 files changed, 629 insertions(+) create mode 100644 neutronclient/neutron/v2_0/flavor/__init__.py create mode 100644 neutronclient/neutron/v2_0/flavor/flavor.py create mode 100644 neutronclient/neutron/v2_0/flavor/flavor_profile.py create mode 100644 neutronclient/tests/unit/flavor/__init__.py create mode 100644 neutronclient/tests/unit/flavor/test_cli20_flavor.py create mode 100644 neutronclient/tests/unit/flavor/test_cli20_flavor_profile.py diff --git a/neutronclient/neutron/v2_0/flavor/__init__.py b/neutronclient/neutron/v2_0/flavor/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/neutron/v2_0/flavor/flavor.py b/neutronclient/neutron/v2_0/flavor/flavor.py new file mode 100644 index 000000000..a8a1055fa --- /dev/null +++ b/neutronclient/neutron/v2_0/flavor/flavor.py @@ -0,0 +1,167 @@ +# Copyright 2015 Hewlett-Packard Development Company, L.P. +# 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. + +from __future__ import print_function + +import argparse + +from neutronclient.common import utils +from neutronclient.i18n import _ +from neutronclient.neutron import v2_0 as neutronV20 + + +class ListFlavor(neutronV20.ListCommand): + """List Neutron service flavors.""" + + resource = 'flavor' + list_columns = ['id', 'name', 'service_type', 'enabled'] + pagination_support = True + sorting_support = True + + +class ShowFlavor(neutronV20.ShowCommand): + """Show information about a given Neutron service flavor.""" + + resource = 'flavor' + + +class CreateFlavor(neutronV20.CreateCommand): + """Create a Neutron service flavor.""" + + resource = 'flavor' + + def add_known_arguments(self, parser): + parser.add_argument( + 'name', + metavar='NAME', + help=_('Name for the flavor.')) + parser.add_argument( + 'service_type', + metavar='SERVICE_TYPE', + help=_('Service type to which the flavor applies to: e.g. VPN. ' + '(See service-provider-list for loaded examples.)')) + parser.add_argument( + '--description', + help=_('Description for the flavor.')) + utils.add_boolean_argument( + parser, + '--enabled', + default=argparse.SUPPRESS, + help=_('Sets enabled flag.')) + + def args2body(self, parsed_args): + body = {} + neutronV20.update_dict(parsed_args, body, + ['name', 'description', 'service_type', + 'enabled']) + return {self.resource: body} + + +class DeleteFlavor(neutronV20.DeleteCommand): + """Delete a given Neutron service flavor.""" + + resource = 'flavor' + + +class UpdateFlavor(neutronV20.UpdateCommand): + """Update a Neutron service flavor.""" + + resource = 'flavor' + + def add_known_arguments(self, parser): + parser.add_argument( + '--name', + help=_('Name for the flavor.')) + parser.add_argument( + '--description', + help=_('Description for the flavor.')) + utils.add_boolean_argument( + parser, + '--enabled', + default=argparse.SUPPRESS, + help=_('Sets enabled flag.')) + + def args2body(self, parsed_args): + body = {} + neutronV20.update_dict(parsed_args, body, + ['name', 'description', 'enabled']) + return {self.resource: body} + + +class AssociateFlavor(neutronV20.NeutronCommand): + """Associate a Neutron service flavor with a flavor profile.""" + + resource = 'flavor' + + def get_parser(self, prog_name): + parser = super(AssociateFlavor, self).get_parser(prog_name) + parser.add_argument( + 'flavor', + metavar='FLAVOR', + help=_('Name or ID of the flavor to associate.')) + parser.add_argument( + 'flavor_profile', + metavar='FLAVOR_PROFILE', + help=_('ID of the flavor profile to be associated with the ' + 'flavor.')) + return parser + + def run(self, parsed_args): + neutron_client = self.get_client() + neutron_client.format = parsed_args.request_format + flavor_id = neutronV20.find_resourceid_by_name_or_id( + neutron_client, 'flavor', parsed_args.flavor) + service_profile_id = neutronV20.find_resourceid_by_id( + neutron_client, 'service_profile', parsed_args.flavor_profile) + body = {'service_profile': {'id': service_profile_id}} + neutron_client.associate_flavor(flavor_id, body) + print((_('Associated flavor %(flavor)s with ' + 'flavor_profile %(profile)s') % + {'flavor': parsed_args.flavor, + 'profile': parsed_args.flavor_profile}), + file=self.app.stdout) + + +class DisassociateFlavor(neutronV20.NeutronCommand): + """Disassociate a Neutron service flavor from a flavor profile.""" + + resource = 'flavor' + + def get_parser(self, prog_name): + parser = super(DisassociateFlavor, self).get_parser(prog_name) + parser.add_argument( + 'flavor', + metavar='FLAVOR', + help=_('Name or ID of the flavor.')) + parser.add_argument( + 'flavor_profile', + metavar='FLAVOR_PROFILE', + help=_('ID of the flavor profile to be disassociated from the ' + 'flavor.')) + return parser + + def run(self, parsed_args): + neutron_client = self.get_client() + neutron_client.format = parsed_args.request_format + flavor_id = neutronV20.find_resourceid_by_name_or_id( + neutron_client, 'flavor', parsed_args.flavor) + service_profile_id = neutronV20.find_resourceid_by_id( + neutron_client, 'service_profile', parsed_args.flavor_profile) + neutron_client.disassociate_flavor(flavor_id, service_profile_id) + print((_('Disassociated flavor %(flavor)s from ' + 'flavor_profile %(profile)s') % + {'flavor': parsed_args.flavor, + 'profile': parsed_args.flavor_profile}), + file=self.app.stdout) diff --git a/neutronclient/neutron/v2_0/flavor/flavor_profile.py b/neutronclient/neutron/v2_0/flavor/flavor_profile.py new file mode 100644 index 000000000..792978782 --- /dev/null +++ b/neutronclient/neutron/v2_0/flavor/flavor_profile.py @@ -0,0 +1,99 @@ +# Copyright 2015 Hewlett-Packard Development Company, L.P. +# 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 argparse + +from neutronclient.common import utils +from neutronclient.i18n import _ +from neutronclient.neutron import v2_0 as neutronV20 + + +class ListFlavorProfile(neutronV20.ListCommand): + """List Neutron service flavor profiles.""" + + resource = 'service_profile' + list_columns = ['id', 'description', 'enabled', 'metainfo'] + pagination_support = True + sorting_support = True + + +class ShowFlavorProfile(neutronV20.ShowCommand): + """Show information about a given Neutron service flavor profile.""" + + resource = 'service_profile' + + +class CreateFlavorProfile(neutronV20.CreateCommand): + """Create a Neutron service flavor profile.""" + + resource = 'service_profile' + + def add_known_arguments(self, parser): + parser.add_argument( + '--description', + help=_('Description for the flavor profile.')) + parser.add_argument( + '--driver', + help=_('Python module path to driver.')) + parser.add_argument( + '--metainfo', + help=_('Metainfo for the flavor profile.')) + utils.add_boolean_argument( + parser, + '--enabled', + default=argparse.SUPPRESS, + help=_('Sets enabled flag.')) + + def args2body(self, parsed_args): + body = {} + neutronV20.update_dict(parsed_args, body, + ['description', 'driver', 'enabled', + 'metainfo']) + return {self.resource: body} + + +class DeleteFlavorProfile(neutronV20.DeleteCommand): + """Delete a given Neutron service flavor profile.""" + + resource = 'service_profile' + + +class UpdateFlavorProfile(neutronV20.UpdateCommand): + """Update a given Neutron service flavor profile.""" + + resource = 'service_profile' + + def add_known_arguments(self, parser): + parser.add_argument( + '--description', + help=_('Description for the flavor profile.')) + parser.add_argument( + '--driver', + help=_('Python module path to driver.')) + parser.add_argument( + '--metainfo', + help=_('Metainfo for the flavor profile.')) + utils.add_boolean_argument( + parser, + '--enabled', + default=argparse.SUPPRESS, + help=_('Sets enabled flag.')) + + def args2body(self, parsed_args): + body = {} + neutronV20.update_dict(parsed_args, body, + ['description', 'driver', 'enabled', + 'metainfo']) + return {self.resource: body} diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 2c25c8365..176664c8b 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -50,6 +50,8 @@ from neutronclient.neutron.v2_0 import agentscheduler from neutronclient.neutron.v2_0 import credential from neutronclient.neutron.v2_0 import extension +from neutronclient.neutron.v2_0.flavor import flavor +from neutronclient.neutron.v2_0.flavor import flavor_profile from neutronclient.neutron.v2_0 import floatingip from neutronclient.neutron.v2_0.fw import firewall from neutronclient.neutron.v2_0.fw import firewallpolicy @@ -396,6 +398,18 @@ class BashCompletionCommand(openstack_command.OpenStackCommand): bandwidth_limit_rule.DeleteQoSBandwidthLimitRule ), 'qos-available-rule-types': qos_rule.ListQoSRuleTypes, + 'flavor-list': flavor.ListFlavor, + 'flavor-show': flavor.ShowFlavor, + 'flavor-create': flavor.CreateFlavor, + 'flavor-delete': flavor.DeleteFlavor, + 'flavor-update': flavor.UpdateFlavor, + 'flavor-associate': flavor.AssociateFlavor, + 'flavor-disassociate': flavor.DisassociateFlavor, + 'flavor-profile-list': flavor_profile.ListFlavorProfile, + 'flavor-profile-show': flavor_profile.ShowFlavorProfile, + 'flavor-profile-create': flavor_profile.CreateFlavorProfile, + 'flavor-profile-delete': flavor_profile.DeleteFlavorProfile, + 'flavor-profile-update': flavor_profile.UpdateFlavorProfile, } COMMANDS = {'2.0': COMMAND_V2} diff --git a/neutronclient/tests/unit/flavor/__init__.py b/neutronclient/tests/unit/flavor/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/tests/unit/flavor/test_cli20_flavor.py b/neutronclient/tests/unit/flavor/test_cli20_flavor.py new file mode 100644 index 000000000..22bd8bb9b --- /dev/null +++ b/neutronclient/tests/unit/flavor/test_cli20_flavor.py @@ -0,0 +1,154 @@ +# Copyright 2015 Hewlett-Packard Development Company, L.P. +# 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 sys + +from neutronclient.neutron.v2_0.flavor import flavor +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20FlavorJSON(test_cli20.CLITestV20Base): + + def setUp(self): + """Prepare test environment.""" + super(CLITestV20FlavorJSON, self).setUp(plurals={'flavors': 'flavor'}) + self.register_non_admin_status_resource('flavor') + self.register_non_admin_status_resource('service_profile') + + def test_create_flavor_with_missing_params(self): + """Create test flavor with missing parameters.""" + resource = 'flavor' + cmd = flavor.CreateFlavor( + test_cli20.MyApp(sys.stdout), None) + name = 'Test flavor' + myid = 'myid' + position_names = [] + position_values = [] + args = [] + self.assertRaises( + SystemExit, self._test_create_resource, + resource, cmd, name, myid, args, position_names, position_values) + + def test_create_flavor_with_mandatory_params(self): + """Create test flavor with minimal parameters.""" + resource = 'flavor' + cmd = flavor.CreateFlavor( + test_cli20.MyApp(sys.stdout), None) + name = 'Test flavor' + myid = 'myid' + service_type = 'DUMMY' + # Defaults are returned in body + position_names = ['name', 'service_type'] + position_values = [name, service_type] + args = [name, service_type] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_create_flavor_with_optional_params(self): + """Create test flavor including optional parameters.""" + resource = 'flavor' + cmd = flavor.CreateFlavor( + test_cli20.MyApp(sys.stdout), None) + name = 'Test flavor' + myid = 'myid' + service_type = 'DUMMY' + description = 'Test description' + position_names = ['name', 'service_type', 'description', 'enabled'] + position_values = [name, service_type, description, 'False'] + args = [name, service_type, + '--description', description, + '--enabled=False'] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_delete_flavor(self): + """Delete flavor.""" + resource = 'flavor' + cmd = flavor.DeleteFlavor(test_cli20.MyApp(sys.stdout), None) + my_id = 'my-id' + args = [my_id] + self._test_delete_resource(resource, cmd, my_id, args) + + def test_list_flavors(self): + """List flavors test.""" + resources = 'flavors' + cmd = flavor.ListFlavor( + test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, True) + + def test_list_flavors_with_pagination(self): + """List flavors test with pagination.""" + resources = 'flavors' + cmd = flavor.ListFlavor( + test_cli20.MyApp(sys.stdout), None) + self._test_list_resources_with_pagination(resources, cmd) + + def test_list_flavors_with_sort(self): + """List flavors test with sorting by name and id.""" + resources = 'flavors' + cmd = flavor.ListFlavor( + test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, + sort_key=["name", "id"], + sort_dir=["asc", "desc"]) + + def test_show_flavor(self): + """Show flavor test.""" + resource = 'flavor' + cmd = flavor.ShowFlavor( + test_cli20.MyApp(sys.stdout), None) + args = ['--fields', 'id', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, args, ['id']) + + def test_update_flavor_with_name(self): + """Update flavor test.""" + resource = 'flavor' + cmd = flavor.UpdateFlavor( + test_cli20.MyApp(sys.stdout), None) + newname = 'Test New Name' + newdescription = 'New Description' + args = ['--name', newname, + '--description', newdescription, + '--enabled', 'False', self.test_id] + self._test_update_resource(resource, cmd, self.test_id, args, + {'name': newname, + 'description': newdescription, + 'enabled': 'False'}) + + def test_associate_flavor(self): + """Associate flavor test.""" + resource = 'service_profile' + cmd = flavor.AssociateFlavor(test_cli20.MyApp(sys.stdout), None) + flavor_id = 'flavor-id' + profile_id = 'profile-id' + name = '' + args = [flavor_id, profile_id] + position_names = ['id'] + position_values = [profile_id] + self._test_create_resource(resource, cmd, name, profile_id, args, + position_names, position_values, + cmd_resource='flavor_profile_binding', + parent_id=flavor_id) + + def test_disassociate_flavor(self): + """Disassociate flavor test.""" + resource = 'flavor_profile_binding' + cmd = flavor.DisassociateFlavor(test_cli20.MyApp(sys.stdout), None) + flavor_id = 'flavor-id' + profile_id = 'profile-id' + args = [flavor_id, profile_id] + self._test_delete_resource(resource, cmd, profile_id, args, + parent_id=flavor_id) diff --git a/neutronclient/tests/unit/flavor/test_cli20_flavor_profile.py b/neutronclient/tests/unit/flavor/test_cli20_flavor_profile.py new file mode 100644 index 000000000..d13090297 --- /dev/null +++ b/neutronclient/tests/unit/flavor/test_cli20_flavor_profile.py @@ -0,0 +1,122 @@ +# Copyright 2015 Hewlett-Packard Development Company, L.P. +# 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 sys + +from neutronclient.neutron.v2_0.flavor import flavor_profile +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20FlavorProfileJSON(test_cli20.CLITestV20Base): + + def setUp(self): + """Prepare test environment.""" + super(CLITestV20FlavorProfileJSON, self).setUp( + plurals={'service_profiles': 'service_profile'}) + self.register_non_admin_status_resource('service_profile') + + def test_create_flavor_profile_with_mandatory_params(self): + """Create test flavor profile test.""" + resource = 'service_profile' + cmd = flavor_profile.CreateFlavorProfile( + test_cli20.MyApp(sys.stdout), None) + name = '' + description = 'Test flavor profile' + myid = 'myid' + metainfo = "{'a':'b'}" + # Defaults are returned in body + position_names = ['description', 'metainfo'] + position_values = [description, metainfo] + args = ['--description', description, '--metainfo', metainfo] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_create_flavor_profile_with_optional_params(self): + """Create test flavor profile disabled test.""" + resource = 'service_profile' + cmd = flavor_profile.CreateFlavorProfile( + test_cli20.MyApp(sys.stdout), None) + name = '' + description = 'Test flavor profile - disabled' + myid = 'myid' + driver = 'mydriver' + metainfo = "{'a':'b'}" + position_names = ['description', 'driver', 'metainfo', 'enabled'] + position_values = [description, driver, metainfo, 'False'] + args = ['--description', description, '--driver', driver, + '--metainfo', metainfo, '--enabled=False'] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_list_flavor_profiles(self): + """List flavor profiles test.""" + resources = 'service_profiles' + cmd = flavor_profile.ListFlavorProfile( + test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, True) + + def test_list_flavor_profiles_with_pagination(self): + """List flavor profiles test with pagination.""" + resources = 'service_profiles' + cmd = flavor_profile.ListFlavorProfile( + test_cli20.MyApp(sys.stdout), None) + self._test_list_resources_with_pagination(resources, cmd) + + def test_list_flavor_profiles_with_sort(self): + """List flavor profiles test with sort by description.""" + resources = 'service_profiles' + cmd = flavor_profile.ListFlavorProfile( + test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, + sort_key=["description"], + sort_dir=["asc"]) + + def test_show_flavor_profile(self): + """Show flavor profile test.""" + resource = 'service_profile' + cmd = flavor_profile.ShowFlavorProfile( + test_cli20.MyApp(sys.stdout), None) + args = ['--fields', 'id', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, args, ['id']) + + def test_update_flavor_profile(self): + """Update flavor profile test.""" + resource = 'service_profile' + cmd = flavor_profile.UpdateFlavorProfile( + test_cli20.MyApp(sys.stdout), None) + newdescription = 'Test new description' + newdriver = 'NewDriver' + newmetainfo = "{'c':'d'}" + newenabled = "False" + args = ['--description', newdescription, + '--driver', newdriver, + '--metainfo', newmetainfo, + '--enabled', newenabled, + self.test_id] + self._test_update_resource(resource, cmd, self.test_id, args, + {'description': newdescription, + 'driver': newdriver, + 'metainfo': newmetainfo, + 'enabled': newenabled}) + + def test_delete_flavor_profile(self): + """Delete flavor profile.""" + resource = 'service_profile' + cmd = flavor_profile.DeleteFlavorProfile(test_cli20.MyApp(sys.stdout), + None) + my_id = 'my-id' + args = [my_id] + self._test_delete_resource(resource, cmd, my_id, args) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 3cb8ffcf4..9ff91e5b9 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -437,6 +437,12 @@ class Client(ClientBase): qos_bandwidth_limit_rule_path = "/qos/policies/%s/bandwidth_limit_rules/%s" qos_rule_types_path = "/qos/rule-types" qos_rule_type_path = "/qos/rule-types/%s" + flavors_path = "/flavors" + flavor_path = "/flavors/%s" + service_profiles_path = "/service_profiles" + service_profile_path = "/service_profiles/%s" + flavor_profile_bindings_path = flavor_path + service_profiles_path + flavor_profile_binding_path = flavor_path + service_profile_path # API has no way to report plurals, so we have to hard code them EXTED_PLURALS = {'routers': 'router', @@ -474,6 +480,7 @@ class Client(ClientBase): 'policies': 'policy', 'bandwidth_limit_rules': 'bandwidth_limit_rule', 'rule_types': 'rule_type', + 'flavors': 'flavor', } @APIParamsCall @@ -1737,6 +1744,72 @@ def delete_bandwidth_limit_rule(self, rule, policy): return self.delete(self.qos_bandwidth_limit_rule_path % (policy, rule)) + @APIParamsCall + def create_flavor(self, body=None): + """Creates a new Neutron service flavor.""" + return self.post(self.flavors_path, body=body) + + @APIParamsCall + def delete_flavor(self, flavor): + """Deletes the specified Neutron service flavor.""" + return self.delete(self.flavor_path % (flavor)) + + @APIParamsCall + def list_flavors(self, retrieve_all=True, **_params): + """Fetches a list of all Neutron service flavors for a tenant.""" + return self.list('flavors', self.flavors_path, retrieve_all, + **_params) + + @APIParamsCall + def show_flavor(self, flavor, **_params): + """Fetches information for a certain Neutron service flavor.""" + return self.get(self.flavor_path % (flavor), params=_params) + + @APIParamsCall + def update_flavor(self, flavor, body): + """Update a Neutron service flavor.""" + return self.put(self.flavor_path % (flavor), body=body) + + @APIParamsCall + def associate_flavor(self, flavor, body): + """Associate a Neutron service flavor with a profile.""" + return self.post(self.flavor_profile_bindings_path % + (flavor), body=body) + + @APIParamsCall + def disassociate_flavor(self, flavor, flavor_profile): + """Disassociate a Neutron service flavor with a profile.""" + return self.delete(self.flavor_profile_binding_path % + (flavor, flavor_profile)) + + @APIParamsCall + def create_service_profile(self, body=None): + """Creates a new Neutron service flavor profile.""" + return self.post(self.service_profiles_path, body=body) + + @APIParamsCall + def delete_service_profile(self, flavor_profile): + """Deletes the specified Neutron service flavor profile.""" + return self.delete(self.service_profile_path % (flavor_profile)) + + @APIParamsCall + def list_service_profiles(self, retrieve_all=True, **_params): + """Fetches a list of all Neutron service flavor profiles.""" + return self.list('service_profiles', self.service_profiles_path, + retrieve_all, **_params) + + @APIParamsCall + def show_service_profile(self, flavor_profile, **_params): + """Fetches information for a certain Neutron service flavor profile.""" + return self.get(self.service_profile_path % (flavor_profile), + params=_params) + + @APIParamsCall + def update_service_profile(self, service_profile, body): + """Update a Neutron service profile.""" + return self.put(self.service_profile_path % (service_profile), + body=body) + def __init__(self, **kwargs): """Initialize a new client for the Neutron v2.0 API.""" super(Client, self).__init__(**kwargs) From 01bd100846398ce44c30c482eac4de3f856e4dd4 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Wed, 14 Oct 2015 01:41:12 +0900 Subject: [PATCH 285/845] Drop cliff-tablib from test-requirements.txt tablib has no support on Py3 and it is a blocker of Py3 support. In my understanding, cliff-tablib is not required for testing, so I think we can drop cliff-tablib safely. Change-Id: I6734a01028545d8bd2cd0950b66cc1714b3ae291 --- test-requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index 1fffcfc9c..f52718e8e 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -3,7 +3,6 @@ # process, which may cause wedges in the gate later. hacking<0.11,>=0.10.0 -cliff-tablib>=1.0 coverage>=3.6 discover fixtures>=1.3.1 From a46c126b3842dd0a4f34d11506b53e6787056074 Mon Sep 17 00:00:00 2001 From: James Arendt Date: Mon, 14 Sep 2015 13:18:29 -0700 Subject: [PATCH 286/845] Add flavor argument to loadbalancer v2 create Builds on cli changes for Neutron flavor framework, which have now been merged. The '--flavor' argument takes a flavor name or id, looking up the id from name if needed. The loadbalancer v2 service can be configured to use the flavor to find the operator-selected provider and populate the provider field, enabling dynamic operator control of which provider is being used on creation. Change-Id: I02ed05d56fab4d0f60a8616978970336b4415f2c Implements: blueprint neutron-flavor-framework --- neutronclient/neutron/v2_0/lb/v2/loadbalancer.py | 8 ++++++++ neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py | 7 ++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py index be70c4eca..dc5be0061 100644 --- a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py +++ b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py @@ -55,6 +55,9 @@ def add_known_arguments(self, parser): parser.add_argument( '--provider', help=_('Provider name of load balancer service.')) + parser.add_argument( + '--flavor', + help=_('ID or name of flavor.')) parser.add_argument( '--vip-address', help=_('VIP address for the load balancer.')) @@ -67,6 +70,11 @@ def args2body(self, parsed_args): self.get_client(), 'subnet', parsed_args.vip_subnet) body = {'vip_subnet_id': _subnet_id, 'admin_state_up': parsed_args.admin_state} + if parsed_args.flavor: + _flavor_id = neutronV20.find_resourceid_by_name_or_id( + self.get_client(), 'flavor', parsed_args.flavor) + body['flavor_id'] = _flavor_id + neutronV20.update_dict(parsed_args, body, ['description', 'provider', 'vip_address', 'tenant_id', 'name']) diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py b/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py index 3f34b3cb8..e96df15cf 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py @@ -44,13 +44,14 @@ def test_create_loadbalancer_with_all_params(self): cmd = lb.CreateLoadBalancer(test_cli20.MyApp(sys.stdout), None) name = 'lbaas-loadbalancer-name' description = 'lbaas-loadbalancer-desc' + flavor_id = 'lbaas-loadbalancer-flavor' vip_subnet_id = 'vip-subnet' my_id = 'my-id' args = ['--admin-state-down', '--description', description, - '--name', name, vip_subnet_id] + '--name', name, '--flavor', flavor_id, vip_subnet_id] position_names = ['admin_state_up', 'description', 'name', - 'vip_subnet_id'] - position_values = [False, description, name, vip_subnet_id] + 'flavor_id', 'vip_subnet_id'] + position_values = [False, description, name, flavor_id, vip_subnet_id] self._test_create_resource(resource, cmd, name, my_id, args, position_names, position_values, cmd_resource=cmd_resource) From 03e5e5f981e3d574aa21381d7cfbc5784ef5b175 Mon Sep 17 00:00:00 2001 From: Paul Michali Date: Mon, 5 Oct 2015 13:28:14 +0000 Subject: [PATCH 287/845] CLI support for VPNaaS multiple local subnets This provides CLI support for the new multiple local subnet feature, where the user can specify endpoint groups (subnets for local, CIDRs for peer) and then specify them in the IPSec site connection create. Three CLIs are changed. The VPN service create CLI will now allow the subnet argument to be optional. This will not be specified when mulitple local subnets with endpoint groups are used. The IPSec site to site connection create/update CLIs will now accept local and peer endpoint group IDs/names, and the peer-cidr argument (deprecated) will be optional. Checks are done on create, to ensure that when endpoint groups are used (the normal case), both local and peer group IDs/names are specified, and that peer CIDR and endpoint groups are mutually exclusive. The server will check whether peer CIDR or endpoint groups are used (via subnet specification on VPN service). For syntax of the endpoint groups in the IPSec connection command, there are several choices, including the following (showing 'local' only, but 'peer' would be similar): --local-epg --local-ep-group --local-endpoint-group For now, --local-ep-group and --peer-ep-group was chosen, hopefully as a balance between being descriptive and terse naming. For the list CLI removed the peer CIDR field, which will normally be empty now, and removed the route mode, which is hard-coded to "static" (and pretty much useless info). This, along with the server changes, provides the basic feature for multiple local subnets. Depends-On: I7a011e3170d7db463a6561e550b2ead3e3311125 Closes-Bug: 1459423 DocImpact Change-Id: I2338d3478ad57da112fe34e83e131d94faf8bbcd --- .../neutron/v2_0/vpn/ipsec_site_connection.py | 100 ++++++---- neutronclient/neutron/v2_0/vpn/vpnservice.py | 13 +- .../adv-svcs/test_readonly_neutron_vpn.py | 2 - .../vpn/test_cli20_ipsec_site_connection.py | 186 ++++++++++-------- .../tests/unit/vpn/test_cli20_vpnservice.py | 25 ++- 5 files changed, 198 insertions(+), 128 deletions(-) diff --git a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py index 57ddd8a1d..8dfa4f38e 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py +++ b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py @@ -37,8 +37,7 @@ class ListIPsecSiteConnection(neutronv20.ListCommand): resource = 'ipsec_site_connection' _formatters = {'peer_cidrs': _format_peer_cidrs} list_columns = [ - 'id', 'name', 'peer_address', 'peer_cidrs', 'route_mode', - 'auth_mode', 'status'] + 'id', 'name', 'peer_address', 'auth_mode', 'status'] pagination_support = True sorting_support = True @@ -49,7 +48,46 @@ class ShowIPsecSiteConnection(neutronv20.ShowCommand): resource = 'ipsec_site_connection' -class CreateIPsecSiteConnection(neutronv20.CreateCommand): +class IPsecSiteConnectionMixin(object): + + def add_known_arguments(self, parser): + parser.add_argument( + '--dpd', + metavar="action=ACTION,interval=INTERVAL,timeout=TIMEOUT", + type=utils.str2dict, + help=vpn_utils.dpd_help("IPsec connection.")) + parser.add_argument( + '--local-ep-group', + help=_('Local endpoint group ID/name with subnet(s) for ' + 'IPSec connection.')) + parser.add_argument( + '--peer-ep-group', + help=_('Peer endpoint group ID/name with CIDR(s) for ' + 'IPsec connection.')) + + def args2body(self, parsed_args, body=None): + """Add in conditional args and then return all conn info.""" + + if body is None: + body = {} + if parsed_args.dpd: + vpn_utils.validate_dpd_dict(parsed_args.dpd) + body['dpd'] = parsed_args.dpd + if parsed_args.local_ep_group: + _local_epg = neutronv20.find_resourceid_by_name_or_id( + self.get_client(), 'endpoint_group', + parsed_args.local_ep_group) + body['local_ep_group_id'] = _local_epg + if parsed_args.peer_ep_group: + _peer_epg = neutronv20.find_resourceid_by_name_or_id( + self.get_client(), 'endpoint_group', + parsed_args.peer_ep_group) + body['peer_ep_group_id'] = _peer_epg + return {self.resource: body} + + +class CreateIPsecSiteConnection(IPsecSiteConnectionMixin, + neutronv20.CreateCommand): """Create an IPsec site connection.""" resource = 'ipsec_site_connection' @@ -73,11 +111,6 @@ def add_known_arguments(self, parser): default='bi-directional', choices=['bi-directional', 'response-only'], help=_('Initiator state in lowercase, default:bi-directional')) - parser.add_argument( - '--dpd', - metavar="action=ACTION,interval=INTERVAL,timeout=TIMEOUT", - type=utils.str2dict, - help=vpn_utils.dpd_help("IPsec connection.")) parser.add_argument( '--vpnservice-id', metavar='VPNSERVICE', required=True, @@ -102,12 +135,14 @@ def add_known_arguments(self, parser): parser.add_argument( '--peer-cidr', action='append', dest='peer_cidrs', - required=True, - help=_('Remote subnet(s) in CIDR format.')) + help=_('[DEPRECATED in Mitaka] Remote subnet(s) in CIDR format. ' + 'Cannot be specified when using endpoint groups. Only ' + 'applicable, if subnet provided for VPN service.')) parser.add_argument( '--psk', required=True, help=_('Pre-shared key string.')) + super(CreateIPsecSiteConnection, self).add_known_arguments(parser) def args2body(self, parsed_args): _vpnservice_id = neutronv20.find_resourceid_by_name_or_id( @@ -123,52 +158,45 @@ def args2body(self, parsed_args): message = _("Invalid MTU value: MTU must be " "greater than or equal to 68") raise exceptions.CommandError(message) + if (bool(parsed_args.local_ep_group) != + bool(parsed_args.peer_ep_group)): + message = _("You must specify both local and peer endpoint " + "groups.") + raise exceptions.CommandError(message) + if parsed_args.peer_cidrs and parsed_args.local_ep_group: + message = _("You cannot specify both endpoint groups and peer " + "CIDR(s).") + raise exceptions.CommandError(message) + if not parsed_args.peer_cidrs and not parsed_args.local_ep_group: + message = _("You must specify endpoint groups or peer CIDR(s).") + raise exceptions.CommandError(message) body = { 'vpnservice_id': _vpnservice_id, 'ikepolicy_id': _ikepolicy_id, 'ipsecpolicy_id': _ipsecpolicy_id, - 'peer_address': parsed_args.peer_address, - 'peer_id': parsed_args.peer_id, - 'mtu': parsed_args.mtu, - 'initiator': parsed_args.initiator, - 'psk': parsed_args.psk, 'admin_state_up': parsed_args.admin_state_down, } + neutronv20.update_dict(parsed_args, body, + ['peer_id', 'mtu', 'initiator', 'psk', + 'peer_address']) if parsed_args.name: body['name'] = parsed_args.name if parsed_args.description: body['description'] = parsed_args.description if parsed_args.tenant_id: body['tenant_id'] = parsed_args.tenant_id - if parsed_args.dpd: - vpn_utils.validate_dpd_dict(parsed_args.dpd) - body['dpd'] = parsed_args.dpd if parsed_args.peer_cidrs: body['peer_cidrs'] = parsed_args.peer_cidrs - - return {'ipsec_site_connection': body} + return super(CreateIPsecSiteConnection, self).args2body(parsed_args, + body) -class UpdateIPsecSiteConnection(neutronv20.UpdateCommand): +class UpdateIPsecSiteConnection(IPsecSiteConnectionMixin, + neutronv20.UpdateCommand): """Update a given IPsec site connection.""" resource = 'ipsec_site_connection' - def add_known_arguments(self, parser): - - parser.add_argument( - '--dpd', - metavar="action=ACTION,interval=INTERVAL,timeout=TIMEOUT", - type=utils.str2dict, - help=vpn_utils.dpd_help("IPsec connection.")) - - def args2body(self, parsed_args): - body = {} - if parsed_args.dpd: - vpn_utils.validate_dpd_dict(parsed_args.dpd) - body['dpd'] = parsed_args.dpd - return {'ipsec_site_connection': body} - class DeleteIPsecSiteConnection(neutronv20.DeleteCommand): """Delete a given IPsec site connection.""" diff --git a/neutronclient/neutron/v2_0/vpn/vpnservice.py b/neutronclient/neutron/v2_0/vpn/vpnservice.py index 3022f2d5f..c18f4e7e6 100644 --- a/neutronclient/neutron/v2_0/vpn/vpnservice.py +++ b/neutronclient/neutron/v2_0/vpn/vpnservice.py @@ -55,13 +55,16 @@ def add_known_arguments(self, parser): 'router', metavar='ROUTER', help=_('Router unique identifier for the VPN service.')) parser.add_argument( - 'subnet', metavar='SUBNET', - help=_('Subnet unique identifier for the VPN service deployment.')) + 'subnet', nargs='?', metavar='SUBNET', + help=_('[DEPRECATED in Mitaka] Unique identifier for the local ' + 'private subnet.')) def args2body(self, parsed_args): - _subnet_id = neutronv20.find_resourceid_by_name_or_id( - self.get_client(), 'subnet', - parsed_args.subnet) + if parsed_args.subnet: + _subnet_id = neutronv20.find_resourceid_by_name_or_id( + self.get_client(), 'subnet', parsed_args.subnet) + else: + _subnet_id = None _router_id = neutronv20.find_resourceid_by_name_or_id( self.get_client(), 'router', parsed_args.router) diff --git a/neutronclient/tests/functional/adv-svcs/test_readonly_neutron_vpn.py b/neutronclient/tests/functional/adv-svcs/test_readonly_neutron_vpn.py index ab3aba018..172fef017 100644 --- a/neutronclient/tests/functional/adv-svcs/test_readonly_neutron_vpn.py +++ b/neutronclient/tests/functional/adv-svcs/test_readonly_neutron_vpn.py @@ -50,6 +50,4 @@ def test_neutron_ipsec_site_connection_list(self): ('ipsec-site-connection-list')) self.assertTableStruct(ipsec_site, ['id', 'name', 'peer_address', - 'peer_cidrs', - 'route_mode', 'auth_mode', 'status']) diff --git a/neutronclient/tests/unit/vpn/test_cli20_ipsec_site_connection.py b/neutronclient/tests/unit/vpn/test_cli20_ipsec_site_connection.py index afcd04b2a..106866309 100644 --- a/neutronclient/tests/unit/vpn/test_cli20_ipsec_site_connection.py +++ b/neutronclient/tests/unit/vpn/test_cli20_ipsec_site_connection.py @@ -23,8 +23,9 @@ class CLITestV20IPsecSiteConnectionJSON(test_cli20.CLITestV20Base): - def test_create_ipsec_site_connection_all_params(self): - """ipsecsite-connection-create all params.""" + # TODO(pcm): Remove, once peer-cidr is deprecated completely + def test_create_ipsec_site_connection_all_params_using_peer_cidrs(self): + """ipsecsite-connection-create all params using peer CIDRs.""" resource = 'ipsec_site_connection' cmd = ipsec_site_connection.CreateIPsecSiteConnection( test_cli20.MyApp(sys.stdout), None @@ -78,13 +79,14 @@ def test_create_ipsec_site_connection_all_params(self): position_names, position_values, extra_body=extra_body) - def test_create_ipsec_site_connection_with_limited_params(self): - """ipsecsite-connection-create with limited params.""" + def test_create_ipsec_site_conn_all_params(self): + """ipsecsite-connection-create all params using endpoint groups.""" resource = 'ipsec_site_connection' cmd = ipsec_site_connection.CreateIPsecSiteConnection( test_cli20.MyApp(sys.stdout), None ) tenant_id = 'mytenant_id' + name = 'connection1' my_id = 'my_id' peer_address = '192.168.2.10' peer_id = '192.168.2.10' @@ -94,41 +96,53 @@ def test_create_ipsec_site_connection_with_limited_params(self): vpnservice_id = 'vpnservice_id' ikepolicy_id = 'ikepolicy_id' ipsecpolicy_id = 'ipsecpolicy_id' - peer_cidrs = ['192.168.3.0/24', '192.168.2.0/24'] + local_ep_group = 'local-epg' + peer_ep_group = 'peer-epg' admin_state = True + description = 'my-vpn-connection' + dpd = 'action=restart,interval=30,timeout=120' args = ['--tenant-id', tenant_id, - '--peer-address', peer_address, - '--peer-id', peer_id, - '--psk', psk, + '--peer-address', peer_address, '--peer-id', peer_id, + '--psk', psk, '--initiator', initiator, '--vpnservice-id', vpnservice_id, - '--ikepolicy-id', ikepolicy_id, - '--ipsecpolicy-id', ipsecpolicy_id, - '--peer-cidr', '192.168.3.0/24', - '--peer-cidr', '192.168.2.0/24'] + '--ikepolicy-id', ikepolicy_id, '--name', name, + '--ipsecpolicy-id', ipsecpolicy_id, '--mtu', mtu, + '--description', description, + '--local-ep-group', local_ep_group, + '--peer-ep-group', peer_ep_group, + '--dpd', dpd] - position_names = ['tenant_id', 'admin_state_up', - 'peer_address', 'peer_id', 'peer_cidrs', - 'psk', 'mtu', 'initiator', + position_names = ['name', 'tenant_id', 'admin_state_up', + 'peer_address', 'peer_id', 'psk', 'mtu', + 'local_ep_group_id', 'peer_ep_group_id', + 'initiator', 'description', 'vpnservice_id', 'ikepolicy_id', 'ipsecpolicy_id'] - position_values = [tenant_id, admin_state, peer_address, - peer_id, peer_cidrs, psk, mtu, - initiator, + position_values = [name, tenant_id, admin_state, peer_address, + peer_id, psk, mtu, local_ep_group, + peer_ep_group, initiator, description, vpnservice_id, ikepolicy_id, ipsecpolicy_id] + extra_body = { + 'dpd': { + 'action': 'restart', + 'interval': 30, + 'timeout': 120, + }, + } - self._test_create_resource(resource, cmd, None, my_id, args, - position_names, position_values) + self._test_create_resource(resource, cmd, name, my_id, args, + position_names, position_values, + extra_body=extra_body) - def _test_dpd_values(self, dpd): - """ipsecsite-connection-create with invalid dpd values.""" + def test_create_ipsec_site_connection_with_limited_params(self): + """ipsecsite-connection-create with limited params.""" resource = 'ipsec_site_connection' cmd = ipsec_site_connection.CreateIPsecSiteConnection( test_cli20.MyApp(sys.stdout), None ) tenant_id = 'mytenant_id' - name = 'connection1' my_id = 'my_id' peer_address = '192.168.2.10' peer_id = '192.168.2.10' @@ -138,91 +152,105 @@ def _test_dpd_values(self, dpd): vpnservice_id = 'vpnservice_id' ikepolicy_id = 'ikepolicy_id' ipsecpolicy_id = 'ipsecpolicy_id' - peer_cidrs = ['192.168.3.0/24', '192.168.2.0/24'] + local_ep_group = 'local-epg' + peer_ep_group = 'peer-epg' admin_state = True - description = 'my-vpn-connection' args = ['--tenant-id', tenant_id, - '--peer-address', peer_address, '--peer-id', peer_id, - '--psk', psk, '--initiator', initiator, + '--peer-address', peer_address, + '--peer-id', peer_id, + '--psk', psk, '--vpnservice-id', vpnservice_id, - '--ikepolicy-id', ikepolicy_id, '--name', name, - '--ipsecpolicy-id', ipsecpolicy_id, '--mtu', mtu, - '--description', description, - '--peer-cidr', '192.168.3.0/24', - '--peer-cidr', '192.168.2.0/24', - '--dpd', dpd] + '--ikepolicy-id', ikepolicy_id, + '--ipsecpolicy-id', ipsecpolicy_id, + '--local-ep-group', local_ep_group, + '--peer-ep-group', peer_ep_group] - position_names = ['name', 'tenant_id', 'admin_state_up', - 'peer_address', 'peer_id', 'peer_cidrs', - 'psk', 'mtu', 'initiator', 'description', + position_names = ['tenant_id', 'admin_state_up', + 'peer_address', 'peer_id', + 'local_ep_group_id', 'peer_ep_group_id', + 'psk', 'mtu', 'initiator', 'vpnservice_id', 'ikepolicy_id', 'ipsecpolicy_id'] - position_values = [name, tenant_id, admin_state, peer_address, - peer_id, peer_cidrs, psk, mtu, - initiator, description, + position_values = [tenant_id, admin_state, peer_address, peer_id, + local_ep_group, peer_ep_group, psk, mtu, initiator, vpnservice_id, ikepolicy_id, ipsecpolicy_id] - self.assertRaises( - exceptions.CommandError, - self._test_create_resource, - resource, cmd, name, my_id, args, - position_names, position_values) - def test_invalid_mtu(self): - """ipsecsite-connection-create with invalid dpd values.""" + self._test_create_resource(resource, cmd, None, my_id, args, + position_names, position_values) + + def _test_create_failure(self, additional_args=None): + """Helper to test failure of IPSec site-to-site creation failure.""" resource = 'ipsec_site_connection' cmd = ipsec_site_connection.CreateIPsecSiteConnection( test_cli20.MyApp(sys.stdout), None ) tenant_id = 'mytenant_id' - name = 'connection1' my_id = 'my_id' peer_address = '192.168.2.10' peer_id = '192.168.2.10' psk = 'abcd' - mtu = '67' + mtu = '1500' initiator = 'bi-directional' vpnservice_id = 'vpnservice_id' ikepolicy_id = 'ikepolicy_id' ipsecpolicy_id = 'ipsecpolicy_id' - peer_cidrs = ['192.168.3.0/24', '192.168.2.0/24'] admin_state = True - description = 'my-vpn-connection' args = ['--tenant-id', tenant_id, - '--peer-address', peer_address, '--peer-id', peer_id, - '--psk', psk, '--initiator', initiator, + '--peer-address', peer_address, + '--peer-id', peer_id, + '--psk', psk, '--vpnservice-id', vpnservice_id, - '--ikepolicy-id', ikepolicy_id, '--name', name, - '--ipsecpolicy-id', ipsecpolicy_id, '--mtu', mtu, - '--description', description, - '--peer-cidr', '192.168.3.0/24', - '--peer-cidr', '192.168.2.0/24'] + '--ikepolicy-id', ikepolicy_id, + '--ipsecpolicy-id', ipsecpolicy_id] + if additional_args is not None: + args += additional_args + position_names = ['tenant_id', 'admin_state_up', 'peer_address', + 'peer_id', 'psk', 'mtu', 'initiator', + 'local_ep_group_id', 'peer_ep_group_id', + 'vpnservice_id', 'ikepolicy_id', 'ipsecpolicy_id'] + + position_values = [tenant_id, admin_state, peer_address, peer_id, psk, + mtu, initiator, None, None, vpnservice_id, + ikepolicy_id, ipsecpolicy_id] + self.assertRaises(exceptions.CommandError, + self._test_create_resource, + resource, cmd, None, my_id, args, + position_names, position_values) + + def test_fail_create_with_invalid_mtu(self): + """ipsecsite-connection-create with invalid dpd values.""" + bad_mtu = ['--mtu', '67'] + self._test_create_failure(bad_mtu) - position_names = ['name', 'tenant_id', 'admin_state_up', - 'peer_address', 'peer_id', 'peer_cidrs', - 'psk', 'mtu', 'initiator', 'description', - 'vpnservice_id', 'ikepolicy_id', - 'ipsecpolicy_id'] + def test_fail_create_with_invalid_dpd_keys(self): + bad_dpd_key = ['--dpd', 'act=restart,interval=30,time=120'] + self._test_create_failure(bad_dpd_key) - position_values = [name, tenant_id, admin_state, peer_address, - peer_id, peer_cidrs, psk, mtu, - initiator, description, - vpnservice_id, ikepolicy_id, ipsecpolicy_id] - self.assertRaises( - exceptions.CommandError, - self._test_create_resource, - resource, cmd, name, my_id, args, - position_names, position_values) + def test_fail_create_with_invalid_dpd_values(self): + bad_dpd_values = ['--dpd', 'action=hold,interval=30,timeout=-1'] + self._test_create_failure(bad_dpd_values) + + def test_fail_create_missing_endpoint_groups_or_cidr(self): + """Must provide either endpoint groups or peer cidrs.""" + self._test_create_failure() + + def test_fail_create_missing_peer_endpoint_group(self): + """Fails if dont have both endpoint groups - missing peer.""" + self._test_create_failure(['--local-ep-group', 'local-epg']) - def test_create_ipsec_site_connection_with_invalid_dpd_keys(self): - dpd = 'act=restart,interval=30,time=120' - self._test_dpd_values(dpd) + def test_fail_create_missing_local_endpoint_group(self): + """Fails if dont have both endpoint groups - missing local.""" + self._test_create_failure(['--peer-ep-group', 'peer-epg']) - def test_create_ipsec_site_connection_with_invalid_dpd_values(self): - dpd = 'action=hold,interval=30,timeout=-1' - self._test_dpd_values(dpd) + def test_fail_create_when_both_endpoints_and_peer_cidr(self): + """Cannot intermix endpoint groups and peer CIDRs for create.""" + additional_args = ['--local-ep-group', 'local-epg', + '--peer-ep-group', 'peer-epg', + '--peer-cidr', '10.2.0.0/24'] + self._test_create_failure(additional_args) def test_list_ipsec_site_connection(self): """ipsecsite-connection-list.""" @@ -300,7 +328,3 @@ def test_show_ipsec_site_connection_id_name(self): args = ['--fields', 'id', '--fields', 'name', self.test_id] self._test_show_resource(resource, cmd, self.test_id, args, ['id', 'name']) - - -class CLITestV20IPsecSiteConnectionXML(CLITestV20IPsecSiteConnectionJSON): - format = 'xml' diff --git a/neutronclient/tests/unit/vpn/test_cli20_vpnservice.py b/neutronclient/tests/unit/vpn/test_cli20_vpnservice.py index a3cab0fb5..2713fa62d 100644 --- a/neutronclient/tests/unit/vpn/test_cli20_vpnservice.py +++ b/neutronclient/tests/unit/vpn/test_cli20_vpnservice.py @@ -73,6 +73,27 @@ def test_create_vpnservice_with_limited_params(self): self._test_create_resource(resource, cmd, None, my_id, args, position_names, position_values) + def test_create_vpnservice_without_subnet(self): + """vpn-service-create with no subnet provided.""" + resource = 'vpnservice' + cmd = vpnservice.CreateVPNService(test_cli20.MyApp(sys.stdout), None) + router = 'myrouter-id' + tenant_id = 'mytenant-id' + my_id = 'my-id' + admin_state = True + + args = [router, + '--tenant-id', tenant_id] + + position_names = ['admin_state_up', + 'subnet_id', 'router_id', + 'tenant_id'] + + position_values = [admin_state, None, router, tenant_id] + + self._test_create_resource(resource, cmd, None, my_id, args, + position_names, position_values) + def test_list_vpnservice(self): """vpn-service-list.""" resources = "vpnservices" @@ -131,7 +152,3 @@ def test_delete_vpnservice(self): my_id = 'my-id' args = [my_id] self._test_delete_resource(resource, cmd, my_id, args) - - -class CLITestV20VpnServiceXML(CLITestV20VpnServiceJSON): - format = 'xml' From 82c9ab771b47b9c1718e0e9ad933d68d8a0e7de5 Mon Sep 17 00:00:00 2001 From: Reedip Banerjee Date: Thu, 17 Sep 2015 12:11:59 +0530 Subject: [PATCH 288/845] Improve neutron-client error message output In case a user provides incorrect input, the NOVA CLIs provide information regarding usage of the NOVA help CLI. This is useful for new users. Similar information has been updated in the neutron CLIs as well. Change-Id: I1be2ba5a2c937b58900221a9ec7625c461ac17d9 Closes-Bug: #1495742 --- neutronclient/shell.py | 4 ++++ neutronclient/tests/unit/test_shell.py | 18 ++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 6cbe7a342..dc15c71b7 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -818,6 +818,10 @@ def run_subcommand(self, argv): ) cmd_parser = cmd.get_parser(full_name) return run_command(cmd, cmd_parser, sub_argv) + except SystemExit: + print(_("Try 'neutron help %s' for more information.") % + cmd_name, file=sys.stderr) + raise except Exception as e: if self.options.verbose_level >= self.DEBUG_LEVEL: self.log.exception("%s", e) diff --git a/neutronclient/tests/unit/test_shell.py b/neutronclient/tests/unit/test_shell.py index 0fee6515d..284ca837e 100644 --- a/neutronclient/tests/unit/test_shell.py +++ b/neutronclient/tests/unit/test_shell.py @@ -59,7 +59,9 @@ def setUp(self): fixtures.EnvironmentVariable( var, self.FAKE_ENV[var])) - def shell(self, argstr, check=False): + def shell(self, argstr, check=False, expected_val=0): + # expected_val is the expected return value after executing + # the command in NeutronShell orig = (sys.stdout, sys.stderr) clean_env = {} _old_env, os.environ = os.environ, clean_env.copy() @@ -70,7 +72,7 @@ def shell(self, argstr, check=False): _shell.run(argstr.split()) except SystemExit: exc_type, exc_value, exc_traceback = sys.exc_info() - self.assertEqual(0, exc_value.code) + self.assertEqual(expected_val, exc_value.code) finally: stdout = sys.stdout.getvalue() stderr = sys.stderr.getvalue() @@ -211,3 +213,15 @@ def test_timeout_environment_variable(self): namespace = parser.parse_args([]) self.assertEqual(50, namespace.http_timeout) + + def test_run_incomplete_command(self): + self.useFixture(fixtures.FakeLogger(level=logging.DEBUG)) + cmd = ( + '--os-username test --os-password test --os-project-id test ' + '--os-auth-strategy keystone --os-auth-url ' + '%s port-create' % + DEFAULT_AUTH_URL) + stdout, stderr = self.shell(cmd, check=True, expected_val=2) + search_str = "Try 'neutron help port-create' for more information" + self.assertTrue(any(search_str in string for string + in stderr.split('\n'))) From cf492e99b2ca26aad6c9120104555cd497e45b21 Mon Sep 17 00:00:00 2001 From: Saksham Varma Date: Wed, 11 Nov 2015 14:15:08 -0800 Subject: [PATCH 289/845] Revert "Revert "Remove Cisco-specific neutron client commands"" This reverts commit 716eb017d35829c18745c4c0279b895784c297f1. The original commit 8292cb85f8830e65284736ea1f9bae2f344c084a had to be reverted since it needed a client version bump to 4.x. This commit reverts that revert. Change-Id: Ib936b9ee4ac16089153209e60c05ed477731fd77 --- neutronclient/neutron/v2_0/credential.py | 68 --------- neutronclient/neutron/v2_0/networkprofile.py | 131 ----------------- neutronclient/neutron/v2_0/policyprofile.py | 72 ---------- neutronclient/shell.py | 15 -- .../tests/unit/test_cli20_credential.py | 79 ---------- .../tests/unit/test_cli20_networkprofile.py | 135 ------------------ .../tests/unit/test_cli20_policyprofile.py | 73 ---------- neutronclient/v2_0/client.py | 81 ----------- 8 files changed, 654 deletions(-) delete mode 100644 neutronclient/neutron/v2_0/credential.py delete mode 100644 neutronclient/neutron/v2_0/networkprofile.py delete mode 100644 neutronclient/neutron/v2_0/policyprofile.py delete mode 100644 neutronclient/tests/unit/test_cli20_credential.py delete mode 100644 neutronclient/tests/unit/test_cli20_networkprofile.py delete mode 100644 neutronclient/tests/unit/test_cli20_policyprofile.py diff --git a/neutronclient/neutron/v2_0/credential.py b/neutronclient/neutron/v2_0/credential.py deleted file mode 100644 index 67d532a1f..000000000 --- a/neutronclient/neutron/v2_0/credential.py +++ /dev/null @@ -1,68 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -from neutronclient.i18n import _ -from neutronclient.neutron import v2_0 as neutronV20 - - -class ListCredential(neutronV20.ListCommand): - """List credentials that belong to a given tenant.""" - - resource = 'credential' - _formatters = {} - list_columns = ['credential_id', 'credential_name', 'user_name', - 'password', 'type'] - - -class ShowCredential(neutronV20.ShowCommand): - """Show information of a given credential.""" - - resource = 'credential' - allow_names = False - - -class CreateCredential(neutronV20.CreateCommand): - """Create a credential.""" - - resource = 'credential' - - def add_known_arguments(self, parser): - parser.add_argument( - 'credential_name', - help=_('Name/IP address for credential.')) - parser.add_argument( - 'credential_type', - help=_('Type of the credential.')) - parser.add_argument( - '--username', - help=_('Username for the credential.')) - parser.add_argument( - '--password', - help=_('Password for the credential.')) - - def args2body(self, parsed_args): - body = {'credential_name': parsed_args.credential_name} - if parsed_args.credential_type: - body['type'] = parsed_args.credential_type - if parsed_args.username: - body['user_name'] = parsed_args.username - if parsed_args.password: - body['password'] = parsed_args.password - return {'credential': body} - - -class DeleteCredential(neutronV20.DeleteCommand): - """Delete a given credential.""" - - resource = 'credential' - allow_names = False diff --git a/neutronclient/neutron/v2_0/networkprofile.py b/neutronclient/neutron/v2_0/networkprofile.py deleted file mode 100644 index 6e2a5c913..000000000 --- a/neutronclient/neutron/v2_0/networkprofile.py +++ /dev/null @@ -1,131 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -from __future__ import print_function - -from neutronclient.i18n import _ -from neutronclient.neutron import v2_0 as neutronV20 -from neutronclient.neutron.v2_0 import parse_args_to_dict - -RESOURCE = 'network_profile' -SEGMENT_TYPE_CHOICES = ['vlan', 'overlay', 'multi-segment', 'trunk'] - - -class ListNetworkProfile(neutronV20.ListCommand): - """List network profiles that belong to a given tenant.""" - - resource = RESOURCE - _formatters = {} - list_columns = ['id', 'name', 'segment_type', 'sub_type', 'segment_range', - 'physical_network', 'multicast_ip_index', - 'multicast_ip_range'] - - -class ShowNetworkProfile(neutronV20.ShowCommand): - """Show information of a given network profile.""" - - resource = RESOURCE - allow_names = True - - -class CreateNetworkProfile(neutronV20.CreateCommand): - """Create a network profile.""" - - resource = RESOURCE - - def add_known_arguments(self, parser): - parser.add_argument('name', - help=_('Name for network profile.')) - parser.add_argument('segment_type', - choices=SEGMENT_TYPE_CHOICES, - help='Segment type.') - # TODO(Abhishek): Check on sub-type choices depending on segment_type - parser.add_argument('--sub_type', - help=_('Sub-type for the segment. Available ' - 'sub-types for overlay segments: ' - 'native, enhanced; For trunk segments: ' - 'vlan, overlay.')) - parser.add_argument('--segment_range', - help=_('Range for the segment.')) - parser.add_argument('--physical_network', - help=_('Name for the physical network.')) - parser.add_argument('--multicast_ip_range', - help=_('Multicast IPv4 range.')) - parser.add_argument("--add-tenant", - action='append', dest='add_tenants', - help=_("Add tenant to the network profile. " - "You can repeat this option.")) - - def args2body(self, parsed_args): - body = {'name': parsed_args.name} - neutronV20.update_dict(parsed_args, body, - ['segment_type', 'sub_type', 'segment_range', - 'physical_network', 'multicast_ip_range', - 'add_tenants']) - return {'network_profile': body} - - -class DeleteNetworkProfile(neutronV20.DeleteCommand): - """Delete a given network profile.""" - - resource = RESOURCE - allow_names = True - - -class UpdateNetworkProfile(neutronV20.UpdateCommand): - """Update network profile's information.""" - - resource = RESOURCE - - def add_known_arguments(self, parser): - parser.add_argument("--remove-tenant", - action='append', dest='remove_tenants', - help=_("Remove tenant from the network profile. " - "You can repeat this option.")) - parser.add_argument("--add-tenant", - action='append', dest='add_tenants', - help=_("Add tenant to the network profile. " - "You can repeat this option.")) - - def args2body(self, parsed_args): - body = {} - neutronV20.update_dict(parsed_args, body, - ['remove_tenants', 'add_tenants']) - return {'network_profile': body} - - -# Aaron: This function is deprecated -class UpdateNetworkProfileV2(neutronV20.NeutronCommand): - - api = 'network' - resource = RESOURCE - - def get_parser(self, prog_name): - parser = super(UpdateNetworkProfileV2, self).get_parser(prog_name) - parser.add_argument("--remove-tenant", - help="Remove tenant from the network profile.") - return parser - - def run(self, parsed_args): - self.log.debug('run(%s)' % parsed_args) - neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format - data = {self.resource: parse_args_to_dict(parsed_args)} - if parsed_args.remove_tenant: - data[self.resource]['remove_tenant'] = parsed_args.remove_tenant - neutron_client.update_network_profile(parsed_args.id, - {self.resource: data}) - print((_('Updated %(resource)s: %(id)s') % - {'id': parsed_args.id, 'resource': self.resource}), - file=self.app.stdout) - return diff --git a/neutronclient/neutron/v2_0/policyprofile.py b/neutronclient/neutron/v2_0/policyprofile.py deleted file mode 100644 index 7ff00db9e..000000000 --- a/neutronclient/neutron/v2_0/policyprofile.py +++ /dev/null @@ -1,72 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -from __future__ import print_function - -from neutronclient.i18n import _ -from neutronclient.neutron import v2_0 as neutronV20 -from neutronclient.neutron.v2_0 import parse_args_to_dict - -RESOURCE = 'policy_profile' - - -class ListPolicyProfile(neutronV20.ListCommand): - """List policy profiles that belong to a given tenant.""" - - resource = RESOURCE - _formatters = {} - list_columns = ['id', 'name'] - - -class ShowPolicyProfile(neutronV20.ShowCommand): - """Show information of a given policy profile.""" - - resource = RESOURCE - allow_names = True - - -class UpdatePolicyProfile(neutronV20.UpdateCommand): - """Update policy profile's information.""" - - resource = RESOURCE - - -class UpdatePolicyProfileV2(neutronV20.UpdateCommand): - """Update policy profile's information.""" - - api = 'network' - resource = RESOURCE - - def get_parser(self, prog_name): - parser = super(UpdatePolicyProfileV2, self).get_parser(prog_name) - parser.add_argument("--add-tenant", - help=_("Add tenant to the policy profile.")) - parser.add_argument("--remove-tenant", - help=_("Remove tenant from the policy profile.")) - return parser - - def run(self, parsed_args): - self.log.debug('run(%s)' % parsed_args) - neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format - data = {self.resource: parse_args_to_dict(parsed_args)} - if parsed_args.add_tenant: - data[self.resource]['add_tenant'] = parsed_args.add_tenant - if parsed_args.remove_tenant: - data[self.resource]['remove_tenant'] = parsed_args.remove_tenant - neutron_client.update_policy_profile(parsed_args.id, - {self.resource: data}) - print((_('Updated %(resource)s: %(id)s') % - {'id': parsed_args.id, 'resource': self.resource}), - file=self.app.stdout) - return diff --git a/neutronclient/shell.py b/neutronclient/shell.py index dc15c71b7..3c512ac2a 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -43,7 +43,6 @@ from neutronclient.neutron.v2_0 import address_scope from neutronclient.neutron.v2_0 import agent from neutronclient.neutron.v2_0 import agentscheduler -from neutronclient.neutron.v2_0 import credential from neutronclient.neutron.v2_0 import extension from neutronclient.neutron.v2_0 import floatingip from neutronclient.neutron.v2_0.fw import firewall @@ -61,10 +60,8 @@ from neutronclient.neutron.v2_0 import metering from neutronclient.neutron.v2_0 import netpartition from neutronclient.neutron.v2_0 import network -from neutronclient.neutron.v2_0 import networkprofile from neutronclient.neutron.v2_0.nsx import networkgateway from neutronclient.neutron.v2_0.nsx import qos_queue -from neutronclient.neutron.v2_0 import policyprofile from neutronclient.neutron.v2_0 import port from neutronclient.neutron.v2_0.qos import bandwidth_limit_rule from neutronclient.neutron.v2_0.qos import policy as qos_policy @@ -301,18 +298,6 @@ class BashCompletionCommand(openstack_command.OpenStackCommand): 'firewall-create': firewall.CreateFirewall, 'firewall-update': firewall.UpdateFirewall, 'firewall-delete': firewall.DeleteFirewall, - 'cisco-credential-list': credential.ListCredential, - 'cisco-credential-show': credential.ShowCredential, - 'cisco-credential-create': credential.CreateCredential, - 'cisco-credential-delete': credential.DeleteCredential, - 'cisco-network-profile-list': networkprofile.ListNetworkProfile, - 'cisco-network-profile-show': networkprofile.ShowNetworkProfile, - 'cisco-network-profile-create': networkprofile.CreateNetworkProfile, - 'cisco-network-profile-delete': networkprofile.DeleteNetworkProfile, - 'cisco-network-profile-update': networkprofile.UpdateNetworkProfile, - 'cisco-policy-profile-list': policyprofile.ListPolicyProfile, - 'cisco-policy-profile-show': policyprofile.ShowPolicyProfile, - 'cisco-policy-profile-update': policyprofile.UpdatePolicyProfile, 'ipsec-site-connection-list': ( ipsec_site_connection.ListIPsecSiteConnection ), diff --git a/neutronclient/tests/unit/test_cli20_credential.py b/neutronclient/tests/unit/test_cli20_credential.py deleted file mode 100644 index 58c413da6..000000000 --- a/neutronclient/tests/unit/test_cli20_credential.py +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright 2013 Cisco Systems 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 sys - -from neutronclient.neutron.v2_0 import credential -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20Credential(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['credential'] - - def test_create_credential(self): - """Create credential: myid.""" - resource = 'credential' - cmd = credential.CreateCredential(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - type = 'mytype' - args = [name, type] - position_names = ['credential_name', 'type'] - position_values = [name, type] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_list_credentials_detail(self): - """List credentials: -D.""" - resources = 'credentials' - cmd = credential.ListCredential(test_cli20.MyApp(sys.stdout), None) - contents = [{'credential_name': 'myname', 'type': 'mytype'}] - self._test_list_resources(resources, cmd, True, - response_contents=contents) - - def test_list_credential_known_option_after_unknown(self): - """List credential: -- --tags a b --request-format xml.""" - resources = 'credentials' - cmd = credential.ListCredential(test_cli20.MyApp(sys.stdout), None) - contents = [{'credential_name': 'myname', 'type': 'mytype'}] - self._test_list_resources(resources, cmd, tags=['a', 'b'], - response_contents=contents) - - def test_list_credential_fields(self): - """List credential: --fields a --fields b -- --fields c d.""" - resources = 'credentials' - cmd = credential.ListCredential(test_cli20.MyApp(sys.stdout), None) - contents = [{'credential_name': 'myname', 'type': 'mytype'}] - self._test_list_resources(resources, cmd, - fields_1=['a', 'b'], fields_2=['c', 'd'], - response_contents=contents) - - def test_show_credential(self): - """Show credential: --fields id --fields name myid.""" - resource = 'credential' - cmd = credential.ShowCredential(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, - ['id', 'name']) - - def test_delete_credential(self): - """Delete credential: myid.""" - resource = 'credential' - cmd = credential.DeleteCredential(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) diff --git a/neutronclient/tests/unit/test_cli20_networkprofile.py b/neutronclient/tests/unit/test_cli20_networkprofile.py deleted file mode 100644 index 673aeca4c..000000000 --- a/neutronclient/tests/unit/test_cli20_networkprofile.py +++ /dev/null @@ -1,135 +0,0 @@ -# Copyright 2013 Cisco Systems 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 sys - -from neutronclient.neutron.v2_0 import networkprofile -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20NetworkProfile(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['network_profile'] - - def test_create_networkprofile(self): - """Create networkprofile: myid.""" - resource = 'network_profile' - cmd = networkprofile.CreateNetworkProfile(test_cli20. - MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - segment_type = 'vlan' - args = [name, segment_type] - position_names = ['name', 'segment_type'] - position_values = [name, segment_type] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_list_networkprofile_detail(self): - """List networkprofile: -D.""" - resources = 'network_profiles' - cmd = networkprofile.ListNetworkProfile(test_cli20.MyApp(sys.stdout), - None) - contents = [{'name': 'myname', 'segment_type': 'vlan'}] - self._test_list_resources(resources, cmd, True, - response_contents=contents) - - def test_list_networkprofile_known_option_after_unknown(self): - """List networkprofile: -- --tags a b --request-format xml.""" - resources = 'network_profiles' - cmd = networkprofile.ListNetworkProfile(test_cli20.MyApp(sys.stdout), - None) - contents = [{'name': 'myname', 'segment_type': 'vlan'}] - self._test_list_resources(resources, cmd, tags=['a', 'b'], - response_contents=contents) - - def test_list_networkprofile_fields(self): - """List networkprofile: --fields a --fields b -- --fields c d.""" - resources = 'network_profiles' - cmd = networkprofile.ListNetworkProfile(test_cli20.MyApp(sys.stdout), - None) - contents = [{'name': 'myname', 'segment_type': 'vlan'}] - self._test_list_resources(resources, cmd, - fields_1=['a', 'b'], fields_2=['c', 'd'], - response_contents=contents) - - def test_show_networkprofile(self): - """Show networkprofile: --fields id --fields name myid.""" - resource = 'network_profile' - cmd = networkprofile.ShowNetworkProfile(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, - ['id', 'name']) - - def test_delete_networkprofile(self): - """Delete networkprofile: myid.""" - resource = 'network_profile' - cmd = networkprofile.DeleteNetworkProfile(test_cli20. - MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) - - def test_create_networkprofile_trunk(self): - """Create networkprofile: myid.""" - resource = 'network_profile' - cmd = networkprofile.CreateNetworkProfile(test_cli20. - MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - segment_type = 'trunk' - args = [name, segment_type, '--sub_type', 'vlan'] - position_names = ['name', 'segment_type', ] - position_values = [name, segment_type, ] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - sub_type='vlan') - - def test_list_networkprofile_trunk_detail(self): - """List networkprofile: -D.""" - resources = 'network_profiles' - cmd = networkprofile.ListNetworkProfile(test_cli20.MyApp(sys.stdout), - None) - contents = [{'name': 'myname', 'segment_type': 'trunk', - '--sub_type': 'vlan'}] - self._test_list_resources(resources, cmd, True, - response_contents=contents) - - def test_create_networkprofile_multi_tenants(self): - """Create networkprofile with mulitple tenants: myid.""" - resource = 'network_profile' - cmd = networkprofile.CreateNetworkProfile(test_cli20. - MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - segment_type = 'vlan' - args = [name, segment_type, '--add-tenant', 'demo', - '--add-tenant', 'admin'] - position_names = ['name', 'segment_type', 'add_tenants'] - position_values = [name, segment_type, ['demo', 'admin']] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_update_networkprofile_multi_tenants(self): - resource = 'network_profile' - cmd = networkprofile.UpdateNetworkProfile(test_cli20. - MyApp(sys.stdout), None) - args = ['myid', '--add-tenant', 'service', '--add-tenant', 'demo', - '--remove-tenant', 'demo'] - extrafields = {'add_tenants': ['service', 'demo'], - 'remove_tenants': ['demo']} - self._test_update_resource(resource, cmd, 'myid', args, extrafields) diff --git a/neutronclient/tests/unit/test_cli20_policyprofile.py b/neutronclient/tests/unit/test_cli20_policyprofile.py deleted file mode 100644 index 6a7fa6d51..000000000 --- a/neutronclient/tests/unit/test_cli20_policyprofile.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright 2013 Cisco Systems 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 sys - -from neutronclient.neutron.v2_0 import policyprofile -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20PolicyProfile(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['policy_profile'] - - def test_list_policyprofile_detail(self): - """List policyprofile: -D.""" - resources = 'policy_profiles' - cmd = policyprofile.ListPolicyProfile(test_cli20.MyApp(sys.stdout), - None) - contents = [{'name': 'myname', 'segment_type': 'vlan'}] - self._test_list_resources(resources, cmd, True, - response_contents=contents) - - def test_list_policyprofile_known_option_after_unknown(self): - """List policyprofile: -- --tags a b --request-format xml.""" - resources = 'policy_profiles' - cmd = policyprofile.ListPolicyProfile(test_cli20.MyApp(sys.stdout), - None) - contents = [{'name': 'myname', 'segment_type': 'vlan'}] - self._test_list_resources(resources, cmd, tags=['a', 'b'], - response_contents=contents) - - def test_list_policyprofile_fields(self): - """List policyprofile: --fields a --fields b -- --fields c d.""" - resources = 'policy_profiles' - cmd = policyprofile.ListPolicyProfile(test_cli20.MyApp(sys.stdout), - None) - contents = [{'name': 'myname', 'segment_type': 'vlan'}] - self._test_list_resources(resources, cmd, - fields_1=['a', 'b'], fields_2=['c', 'd'], - response_contents=contents) - - def test_show_policyprofile(self): - """Show policyprofile: --fields id --fields name myid.""" - resource = 'policy_profile' - cmd = policyprofile.ShowPolicyProfile(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, - ['id', 'name']) - - def test_update_policyprofile(self): - """Update policyprofile: myid --name myname --tags a b.""" - resource = 'policy_profile' - cmd = policyprofile.UpdatePolicyProfile(test_cli20.MyApp(sys.stdout), - None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'myname', - '--tags', 'a', 'b'], - {'name': 'myname', 'tags': ['a', 'b'], } - ) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 3cb8ffcf4..03470f440 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -398,14 +398,6 @@ class Client(ClientBase): gateway_devices_path = "/gateway-devices" gateway_device_path = "/gateway-devices/%s" service_providers_path = "/service-providers" - credentials_path = "/credentials" - credential_path = "/credentials/%s" - network_profiles_path = "/network_profiles" - network_profile_path = "/network_profiles/%s" - network_profile_bindings_path = "/network_profile_bindings" - policy_profiles_path = "/policy_profiles" - policy_profile_path = "/policy_profiles/%s" - policy_profile_bindings_path = "/policy_profile_bindings" metering_labels_path = "/metering/metering-labels" metering_label_path = "/metering/metering-labels/%s" metering_label_rules_path = "/metering/metering-label-rules" @@ -1504,79 +1496,6 @@ def list_service_providers(self, retrieve_all=True, **_params): return self.list('service_providers', self.service_providers_path, retrieve_all, **_params) - def list_credentials(self, **_params): - """Fetch a list of all credentials for a tenant.""" - return self.get(self.credentials_path, params=_params) - - @APIParamsCall - def show_credential(self, credential, **_params): - """Fetch a credential.""" - return self.get(self.credential_path % (credential), params=_params) - - @APIParamsCall - def create_credential(self, body=None): - """Create a new credential.""" - return self.post(self.credentials_path, body=body) - - @APIParamsCall - def update_credential(self, credential, body=None): - """Update a credential.""" - return self.put(self.credential_path % (credential), body=body) - - @APIParamsCall - def delete_credential(self, credential): - """Delete the specified credential.""" - return self.delete(self.credential_path % (credential)) - - def list_network_profile_bindings(self, **params): - """Fetch a list of all tenants associated for a network profile.""" - return self.get(self.network_profile_bindings_path, params=params) - - @APIParamsCall - def list_network_profiles(self, **params): - """Fetch a list of all network profiles for a tenant.""" - return self.get(self.network_profiles_path, params=params) - - @APIParamsCall - def show_network_profile(self, profile, **params): - """Fetch a network profile.""" - return self.get(self.network_profile_path % (profile), params=params) - - @APIParamsCall - def create_network_profile(self, body=None): - """Create a network profile.""" - return self.post(self.network_profiles_path, body=body) - - @APIParamsCall - def update_network_profile(self, profile, body=None): - """Update a network profile.""" - return self.put(self.network_profile_path % (profile), body=body) - - @APIParamsCall - def delete_network_profile(self, profile): - """Delete the network profile.""" - return self.delete(self.network_profile_path % profile) - - @APIParamsCall - def list_policy_profile_bindings(self, **params): - """Fetch a list of all tenants associated for a policy profile.""" - return self.get(self.policy_profile_bindings_path, params=params) - - @APIParamsCall - def list_policy_profiles(self, **params): - """Fetch a list of all network profiles for a tenant.""" - return self.get(self.policy_profiles_path, params=params) - - @APIParamsCall - def show_policy_profile(self, profile, **params): - """Fetch a network profile.""" - return self.get(self.policy_profile_path % (profile), params=params) - - @APIParamsCall - def update_policy_profile(self, profile, body=None): - """Update a policy profile.""" - return self.put(self.policy_profile_path % (profile), body=body) - @APIParamsCall def create_metering_label(self, body=None): """Creates a metering label.""" From 2941b0c74a7e2a69e9cf88cea97d6e402476e6a9 Mon Sep 17 00:00:00 2001 From: Richard Theis Date: Mon, 16 Nov 2015 15:35:17 -0600 Subject: [PATCH 290/845] Add route options to neutron router-update Add the --route and --no-routes options to neutron router-update. This will make it easier for CLI users to add or remove routes associated with a router. Change-Id: If0e3125420afe3609633cd6c133edaa5a172357d Closes-Bug: #1452229 --- neutronclient/neutron/v2_0/router.py | 14 +++++ neutronclient/tests/unit/test_cli20_router.py | 62 +++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index 31a2bf7ba..4c9a83c26 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -107,6 +107,16 @@ def add_known_arguments(self, parser): parser, '--distributed', dest='distributed', help=_('True means this router should operate in' ' distributed mode.')) + routes_group = parser.add_mutually_exclusive_group() + routes_group.add_argument( + '--route', metavar='destination=CIDR,nexthop=IP_ADDR', + action='append', dest='routes', type=utils.str2dict, + help=_('Route to associate with the router.' + ' You can repeat this option.')) + routes_group.add_argument( + '--no-routes', + action='store_true', + help=_('Remove routes associated with the router.')) def args2body(self, parsed_args): body = {} @@ -114,6 +124,10 @@ def args2body(self, parsed_args): body['admin_state_up'] = parsed_args.admin_state neutronV20.update_dict(parsed_args, body, ['name', 'distributed']) + if parsed_args.no_routes: + body['routes'] = None + elif parsed_args.routes: + body['routes'] = parsed_args.routes return {self.resource: body} diff --git a/neutronclient/tests/unit/test_cli20_router.py b/neutronclient/tests/unit/test_cli20_router.py index d83cb6906..60ee733bd 100644 --- a/neutronclient/tests/unit/test_cli20_router.py +++ b/neutronclient/tests/unit/test_cli20_router.py @@ -196,6 +196,68 @@ def test_update_router_distributed(self): {'distributed': 'false'} ) + def test_update_router_no_routes(self): + """Update router: myid --no-routes""" + resource = 'router' + cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--no-routes'], + {'routes': None}) + + def test_update_router_add_route(self): + """Update router: myid + --route destination=10.0.3.0/24,nexthop=10.0.0.10 + """ + resource = 'router' + cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None) + myid = 'myid' + args = [myid, + '--route', + 'destination=10.0.3.0/24,nexthop=10.0.0.10'] + routes = [{'destination': '10.0.3.0/24', + 'nexthop': '10.0.0.10'}] + updatefields = {'routes': routes} + self._test_update_resource(resource, cmd, myid, args, updatefields) + + def test_update_router_add_routes(self): + """Update router: myid + --route destination=10.0.3.0/24,nexthop=10.0.0.10 + --route destination=fd7a:1d63:2063::/64, + nexthop=fd7a:1d63:2063:0:f816:3eff:fe0e:a697 + """ + resource = 'router' + cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None) + myid = 'myid' + args = [myid, + '--route', + 'destination=10.0.3.0/24,nexthop=10.0.0.10', + '--route', + 'destination=fd7a:1d63:2063::/64,' + 'nexthop=fd7a:1d63:2063:0:f816:3eff:fe0e:a697'] + routes = [{'destination': '10.0.3.0/24', + 'nexthop': '10.0.0.10'}, + {'destination': 'fd7a:1d63:2063::/64', + 'nexthop': 'fd7a:1d63:2063:0:f816:3eff:fe0e:a697'}] + updatefields = {'routes': routes} + self._test_update_resource(resource, cmd, myid, args, updatefields) + + def test_update_router_no_routes_with_add_route(self): + """Update router: --no-routes with --route""" + resource = 'router' + cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None) + myid = 'myid' + args = [myid, + '--no-routes', + '--route', + 'destination=10.0.3.0/24,nexthop=10.0.0.10'] + actual_error_code = 0 + try: + self._test_update_resource(resource, cmd, myid, args, None) + except SystemExit: + exc_type, exc_value, exc_traceback = sys.exc_info() + actual_error_code = exc_value.code + self.assertEqual(2, actual_error_code) + def test_delete_router(self): """Delete router: myid.""" resource = 'router' From 529d3f5a3c8d71df23d795275f01ec4a4aca994b Mon Sep 17 00:00:00 2001 From: Kevin Benton Date: Thu, 19 Nov 2015 21:27:05 -0800 Subject: [PATCH 291/845] Do not allow name lookups on RBAC policies RBAC policies have no name field so the name query to the server was always returning all entries since the name filter was ignored. This corrects the behavior by disabling the name lookup for RBAC policies. Change-Id: I6c5afa05cefb1709e9667a1aaf20105c707dc95c Closes-Bug: #1517818 --- neutronclient/neutron/v2_0/rbac.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/neutronclient/neutron/v2_0/rbac.py b/neutronclient/neutron/v2_0/rbac.py index 5dc72c0b3..ace6613f1 100644 --- a/neutronclient/neutron/v2_0/rbac.py +++ b/neutronclient/neutron/v2_0/rbac.py @@ -32,12 +32,14 @@ class ListRBACPolicy(neutronV20.ListCommand): list_columns = ['id', 'object_id'] pagination_support = True sorting_support = True + allow_names = False class ShowRBACPolicy(neutronV20.ShowCommand): """Show information of a given RBAC policy.""" resource = 'rbac_policy' + allow_names = False class CreateRBACPolicy(neutronV20.CreateCommand): @@ -81,6 +83,7 @@ class UpdateRBACPolicy(neutronV20.UpdateCommand): """Update RBAC policy for given tenant.""" resource = 'rbac_policy' + allow_names = False def add_known_arguments(self, parser): parser.add_argument( @@ -97,3 +100,4 @@ class DeleteRBACPolicy(neutronV20.DeleteCommand): """Delete a RBAC policy.""" resource = 'rbac_policy' + allow_names = False From 895b9b9d211e0e318fbd459d3ef2f9f4abf8c669 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Tue, 24 Nov 2015 06:58:48 +0900 Subject: [PATCH 292/845] Move the old release notes to a separate file Preparation for reno adoption in a following patch Change-Id: Ice4f45147ae9699a5be2c60c0ed247b893881d62 --- doc/source/history.rst | 28 ++++++++++++++++++++++++++++ doc/source/index.rst | 28 +--------------------------- 2 files changed, 29 insertions(+), 27 deletions(-) create mode 100644 doc/source/history.rst diff --git a/doc/source/history.rst b/doc/source/history.rst new file mode 100644 index 000000000..57b6fad7f --- /dev/null +++ b/doc/source/history.rst @@ -0,0 +1,28 @@ +Release Notes +============= + +2.2.2 +----- + +* improved support for listing a large number of filtered subnets +* add --endpoint-type and OS_ENDPOINT_TYPE to shell client +* made the publicURL the default endpoint instead of adminURL +* add ability to update security group name (requires 2013.2-Havana or later) +* add flake8 and pbr support for testing and building + +2.2.0 +----- + +* add security group commands +* add Lbaas commands +* allow options put after positional arguments +* add NVP queue and net gateway commands +* add commands for agent management extensions +* add commands for DHCP and L3 agents scheduling +* support XML request format +* support pagination options + +2.0 +----- + +* support Neutron API 2.0 diff --git a/doc/source/index.rst b/doc/source/index.rst index d02a5c8f1..8b5793bc6 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -56,30 +56,4 @@ Developer Docs :maxdepth: 1 devref/index - - -Release Notes -============= - -2.0 ------ -* support Neutron API 2.0 - -2.2.0 ------ -* add security group commands -* add Lbaas commands -* allow options put after positional arguments -* add NVP queue and net gateway commands -* add commands for agent management extensions -* add commands for DHCP and L3 agents scheduling -* support XML request format -* support pagination options - -2.2.2 ------ -* improved support for listing a large number of filtered subnets -* add --endpoint-type and OS_ENDPOINT_TYPE to shell client -* made the publicURL the default endpoint instead of adminURL -* add ability to update security group name (requires 2013.2-Havana or later) -* add flake8 and pbr support for testing and building \ No newline at end of file + history From 66b03a8167eb40c14c1c441ee56b313c348e7a88 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Tue, 24 Nov 2015 07:13:42 +0900 Subject: [PATCH 293/845] Add reno for release notes management In addition to the convention of releasenotes/source directory which used in most OpenStack project, we have the release notes in our developer documents. Change-Id: Ied07af5ee5c8929e639e17a34b559cebfe419dc4 --- .gitignore | 1 + doc/source/conf.py | 5 +- doc/source/history.rst | 3 + neutronclient/version.py | 3 +- releasenotes/notes/.placeholder | 0 .../start-using-reno-9081b3e4c1951fdb.yaml | 3 + releasenotes/source/_static/.placeholder | 0 releasenotes/source/_templates/.placeholder | 0 releasenotes/source/conf.py | 276 ++++++++++++++++++ releasenotes/source/index.rst | 5 + test-requirements.txt | 1 + tox.ini | 3 + 12 files changed, 298 insertions(+), 2 deletions(-) create mode 100644 releasenotes/notes/.placeholder create mode 100644 releasenotes/notes/start-using-reno-9081b3e4c1951fdb.yaml create mode 100644 releasenotes/source/_static/.placeholder create mode 100644 releasenotes/source/_templates/.placeholder create mode 100644 releasenotes/source/conf.py create mode 100644 releasenotes/source/index.rst diff --git a/.gitignore b/.gitignore index 42921e017..3e20723ad 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ build-stamp cover/* doc/build/ doc/source/api/ +releasenotes/build/ python_neutronclient.egg-info/* neutron/vcsversion.py neutronclient/versioninfo diff --git a/doc/source/conf.py b/doc/source/conf.py index 9648d80c7..bc6dad770 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -7,7 +7,10 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', 'oslosphinx'] +extensions = ['sphinx.ext.autodoc', + 'oslosphinx', + 'reno.sphinxext', +] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] diff --git a/doc/source/history.rst b/doc/source/history.rst index 57b6fad7f..97b00c09d 100644 --- a/doc/source/history.rst +++ b/doc/source/history.rst @@ -1,6 +1,9 @@ +============= Release Notes ============= +.. release-notes:: + 2.2.2 ----- diff --git a/neutronclient/version.py b/neutronclient/version.py index 4178a0964..d597b5c91 100644 --- a/neutronclient/version.py +++ b/neutronclient/version.py @@ -17,4 +17,5 @@ import pbr.version -__version__ = pbr.version.VersionInfo('python-neutronclient').version_string() +version_info = pbr.version.VersionInfo('python-neutronclient') +__version__ = version_info.version_string() diff --git a/releasenotes/notes/.placeholder b/releasenotes/notes/.placeholder new file mode 100644 index 000000000..e69de29bb diff --git a/releasenotes/notes/start-using-reno-9081b3e4c1951fdb.yaml b/releasenotes/notes/start-using-reno-9081b3e4c1951fdb.yaml new file mode 100644 index 000000000..873a30fe6 --- /dev/null +++ b/releasenotes/notes/start-using-reno-9081b3e4c1951fdb.yaml @@ -0,0 +1,3 @@ +--- +other: + - Start using reno to manage release notes. diff --git a/releasenotes/source/_static/.placeholder b/releasenotes/source/_static/.placeholder new file mode 100644 index 000000000..e69de29bb diff --git a/releasenotes/source/_templates/.placeholder b/releasenotes/source/_templates/.placeholder new file mode 100644 index 000000000..e69de29bb diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py new file mode 100644 index 000000000..e1e2a5ae5 --- /dev/null +++ b/releasenotes/source/conf.py @@ -0,0 +1,276 @@ +# -*- coding: utf-8 -*- +# 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. + +# Neutron Client Release Notes documentation build configuration file, +# created by sphinx-quickstart on Tue Nov 3 17:40:50 2015. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'oslosphinx', + 'reno.sphinxext', +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +# source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Neutron Client Release Notes' +copyright = u'2015, Neutron Developers' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +from neutronclient.version import version_info +# The full version, including alpha/beta/rc tags. +release = version_info.version_string_with_vcs() +# The short X.Y version. +version = version_info.canonical_version_string() + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +# today = '' +# Else, today_fmt is used as the format for a strftime call. +# today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = [] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +# default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +# add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +# add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +# show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +# modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +# keep_warnings = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +# html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +# html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +# html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +# html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +# html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +# html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +# html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +# html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +# html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +# html_additional_pages = {} + +# If false, no module index is generated. +# html_domain_indices = True + +# If false, no index is generated. +# html_use_index = True + +# If true, the index is split into individual pages for each letter. +# html_split_index = False + +# If true, links to the reST sources are added to the pages. +# html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +# html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +# html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +# html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +# html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'NeutronReleaseNotesdoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # 'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + ('index', 'NeutronClientReleaseNotes.tex', + u'Neutron Client Release Notes Documentation', + u'Neutron Developers', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +# latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +# latex_use_parts = False + +# If true, show page references after internal links. +# latex_show_pagerefs = False + +# If true, show URL addresses after external links. +# latex_show_urls = False + +# Documents to append as an appendix to all manuals. +# latex_appendices = [] + +# If false, no module index is generated. +# latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'neutronclientreleasenotes', + u'Neutron Client Release Notes Documentation', + [u'Neutron Developers'], 1) +] + +# If true, show URL addresses after external links. +# man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'NeutronClientReleaseNotes', + u'Neutron Client Release Notes Documentation', + u'Neutron Developers', 'NeutronClientReleaseNotes', + 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +# texinfo_appendices = [] + +# If false, no module index is generated. +# texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +# texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +# texinfo_no_detailmenu = False diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst new file mode 100644 index 000000000..2a4bceb4e --- /dev/null +++ b/releasenotes/source/index.rst @@ -0,0 +1,5 @@ +============= +Release Notes +============= + +.. release-notes:: diff --git a/test-requirements.txt b/test-requirements.txt index f154c0298..de778110f 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -11,6 +11,7 @@ mock>=1.2 oslosphinx>=2.5.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 python-subunit>=0.0.18 +reno>=0.1.1 # Apache2 requests-mock>=0.6.0 # Apache-2.0 sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 testrepository>=0.0.18 diff --git a/tox.ini b/tox.ini index f9e00d6d8..c7291ef04 100644 --- a/tox.ini +++ b/tox.ini @@ -39,6 +39,9 @@ commands = python setup.py testr --coverage --testr-args='{posargs}' commands= python setup.py build_sphinx +[testenv:releasenotes] +commands = sphinx-build -a -E -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html + [tox:jenkins] downloadcache = ~/cache/pip From 0042a39d9245b9262b95b7b124a2bb15ba2682e5 Mon Sep 17 00:00:00 2001 From: Kevin Benton Date: Mon, 23 Nov 2015 17:08:42 -0800 Subject: [PATCH 294/845] Add 'baremetal' as an allowed vnic type For wiring up ports for baremetal servers, a new vnic type of 'baremetal' will be used to allow mechanism drivers to easily distinguish which ports should contain switch port information. This patch adds 'baremetal' as an allowed option on the client. Change-Id: If25a1af55a3b849227c9145341ae5e3577a5d7fb Partial-Implements: blueprint neutron-ironic-integration --- neutronclient/neutron/v2_0/port.py | 6 +++--- neutronclient/tests/unit/test_cli20_port.py | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index 74c713c8d..e98e0f00d 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -239,12 +239,12 @@ def add_known_arguments(self, parser): '--mac_address', help=argparse.SUPPRESS) parser.add_argument( - '--vnic-type', metavar='', - choices=['direct', 'macvtap', 'normal'], + '--vnic-type', metavar='', + choices=['direct', 'macvtap', 'normal', 'baremetal'], help=_('VNIC type for this port.')) parser.add_argument( '--vnic_type', - choices=['direct', 'macvtap', 'normal'], + choices=['direct', 'macvtap', 'normal', 'baremetal'], help=argparse.SUPPRESS) parser.add_argument( '--binding-profile', diff --git a/neutronclient/tests/unit/test_cli20_port.py b/neutronclient/tests/unit/test_cli20_port.py index db873c1e5..ad9b2b89d 100644 --- a/neutronclient/tests/unit/test_cli20_port.py +++ b/neutronclient/tests/unit/test_cli20_port.py @@ -173,6 +173,26 @@ def test_create_port_vnic_type_macvtap(self): self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) + def test_create_port_vnic_type_baremetal(self): + """Create port: --vnic_type baremetal netid.""" + resource = 'port' + cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + netid = 'netid' + args = ['--vnic_type', 'baremetal', netid] + position_names = ['binding:vnic_type', 'network_id'] + position_values = ['baremetal', netid] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + # Test dashed options + args = ['--vnic-type', 'baremetal', netid] + position_names = ['binding:vnic_type', 'network_id'] + position_values = ['baremetal', netid] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + def test_create_port_with_binding_profile(self): resource = 'port' cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) From f8d5bbccc2639472b5b713c216afaca7ecc9f612 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Wed, 25 Nov 2015 13:00:18 +0000 Subject: [PATCH 295/845] Updated from global requirements Change-Id: Ifda19b47c1e515c0d40d083571c0f4d9898c19e0 --- requirements.txt | 2 +- test-requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index e83e793da..7936e1da6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ iso8601>=0.1.9 netaddr!=0.7.16,>=0.7.12 oslo.i18n>=1.5.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 -oslo.utils!=2.6.0,>=2.4.0 # Apache-2.0 +oslo.utils>=2.8.0 # Apache-2.0 os-client-config!=1.6.2,>=1.4.0 keystoneauth1>=1.0.0 requests!=2.8.0,>=2.5.2 diff --git a/test-requirements.txt b/test-requirements.txt index de778110f..e9377058d 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -8,7 +8,7 @@ discover fixtures>=1.3.1 mox3>=0.7.0 mock>=1.2 -oslosphinx>=2.5.0 # Apache-2.0 +oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 python-subunit>=0.0.18 reno>=0.1.1 # Apache2 From a14a5f9a27a03ff9ae2cd393f4c0a219c19b0322 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Sat, 28 Nov 2015 04:58:33 +0900 Subject: [PATCH 296/845] Use sphinx-build -W in [docs] target Even though [pbr] warnerrors = true is specified, somehow sphinx warnings are not treated as errors. If we use sphinx-build, -W option detects document warnings and it leads to better documentation quality. Fix a warning in the existing document too. Change-Id: Icbb39686c7a24f0040d998fb14e14c63613da190 --- doc/source/devref/client_command_extensions.rst | 7 ++++--- tox.ini | 3 +-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/source/devref/client_command_extensions.rst b/doc/source/devref/client_command_extensions.rst index b89078eb3..79025be39 100644 --- a/doc/source/devref/client_command_extensions.rst +++ b/doc/source/devref/client_command_extensions.rst @@ -70,6 +70,7 @@ neutronclient.extension entry_point To activate the commands in a specific extension module, add an entry in setup.cfg under neutronclient.extension. For example:: - [entry_points] - neutronclient.extension = - fox_sockets = neutronclient.neutron.v2_0.contrib._fox_sockets + + [entry_points] + neutronclient.extension = + fox_sockets = neutronclient.neutron.v2_0.contrib._fox_sockets diff --git a/tox.ini b/tox.ini index c7291ef04..452b751f0 100644 --- a/tox.ini +++ b/tox.ini @@ -36,8 +36,7 @@ setenv = commands = python setup.py testr --coverage --testr-args='{posargs}' [testenv:docs] -commands= - python setup.py build_sphinx +commands = sphinx-build -W -b html doc/source doc/build/html [testenv:releasenotes] commands = sphinx-build -a -E -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html From f1de15721c50e630716957ee8a343428a0db73c5 Mon Sep 17 00:00:00 2001 From: Tang Chen Date: Mon, 30 Nov 2015 02:56:11 +0800 Subject: [PATCH 297/845] Trivial: Fix a typo in class ListCommand Change-Id: I13d5d82e34b09cb5f313a937bef5dba3ed08fa58 --- neutronclient/neutron/v2_0/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index ad2f6e746..4bd6b1da8 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -683,9 +683,9 @@ def retrieve_list(self, parsed_args): def extend_list(self, data, parsed_args): """Update a retrieved list. - This method provides a way to modify a original list returned from + This method provides a way to modify an original list returned from the neutron server. For example, you can add subnet cidr information - to a list network. + to a network list. """ pass From cc434c2241d26bbd571d7467875307634931fd3b Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Tue, 1 Dec 2015 06:10:03 +0000 Subject: [PATCH 298/845] Updated from global requirements Change-Id: I09d0d469aebc9f565ba8c53deaa2a0aabd1f134a --- requirements.txt | 4 ++-- test-requirements.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index 7936e1da6..e902f01b6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ # process, which may cause wedges in the gate later. pbr>=1.6 argparse -cliff>=1.14.0 # Apache-2.0 +cliff>=1.15.0 # Apache-2.0 iso8601>=0.1.9 netaddr!=0.7.16,>=0.7.12 oslo.i18n>=1.5.0 # Apache-2.0 @@ -11,7 +11,7 @@ oslo.serialization>=1.10.0 # Apache-2.0 oslo.utils>=2.8.0 # Apache-2.0 os-client-config!=1.6.2,>=1.4.0 keystoneauth1>=1.0.0 -requests!=2.8.0,>=2.5.2 +requests>=2.8.1 simplejson>=2.2.0 six>=1.9.0 Babel>=1.3 diff --git a/test-requirements.txt b/test-requirements.txt index e9377058d..62eb0e8c6 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -12,7 +12,7 @@ oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 python-subunit>=0.0.18 reno>=0.1.1 # Apache2 -requests-mock>=0.6.0 # Apache-2.0 +requests-mock>=0.7.0 # Apache-2.0 sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 testrepository>=0.0.18 testtools>=1.4.0 From dd8f157aa77d3303acf83c170ea51dc895bfb4a6 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Sat, 28 Nov 2015 04:52:04 +0900 Subject: [PATCH 299/845] Reorganize documentation structure This reorganization is to make it easier to add more contents. It move the usage of CLI and python binding to separate pages. Change-Id: I39e0d1d18010dfd1c687c573619a09331f12241a --- doc/source/devref/index.rst | 29 ---------------- doc/source/index.rst | 67 ++++++++++++------------------------ doc/source/usage/cli.rst | 55 +++++++++++++++++++++++++++++ doc/source/usage/library.rst | 38 ++++++++++++++++++++ 4 files changed, 115 insertions(+), 74 deletions(-) delete mode 100644 doc/source/devref/index.rst create mode 100644 doc/source/usage/cli.rst create mode 100644 doc/source/usage/library.rst diff --git a/doc/source/devref/index.rst b/doc/source/devref/index.rst deleted file mode 100644 index c23f0a6e3..000000000 --- a/doc/source/devref/index.rst +++ /dev/null @@ -1,29 +0,0 @@ -.. - Copyright 2015 OpenStack Foundation - 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. - -Developer Guide -=============== - -In the Developer Guide, you will find information on Neutron's Client lower level -programming APIs. - - -Programming HowTos and Tutorials --------------------------------- -.. toctree:: - :maxdepth: 3 - - client_command_extensions diff --git a/doc/source/index.rst b/doc/source/index.rst index 8b5793bc6..fa240033a 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -1,59 +1,36 @@ -Python bindings to the OpenStack Network API -============================================ +=============================================== +Python bindings to the OpenStack Networking API +=============================================== -In order to use the python neutron client directly, you must first obtain an auth token and identify which endpoint you wish to speak to. Once you have done so, you can use the API like so:: +This is a client for OpenStack Networking API. There is a :doc:`Python API +` (the neutronclient module), and a :doc:`command-line script +` (installed as **neutron**). Each implements the entire OpenStack +Networking API. - >>> import logging - >>> from neutronclient.neutron import client - >>> logging.basicConfig(level=logging.DEBUG) - >>> neutron = client.Client('2.0', endpoint_url=OS_URL, token=OS_TOKEN) - >>> neutron.format = 'json' - >>> network = {'name': 'mynetwork', 'admin_state_up': True} - >>> neutron.create_network({'network':network}) - >>> networks = neutron.list_networks(name='mynetwork') - >>> print networks - >>> network_id = networks['networks'][0]['id'] - >>> neutron.delete_network(network_id) +Using neturonclient +------------------- -Alternatively, if you have a username and password, authentication is done -against the public endpoint. You must also specify a tenant that is associated -with the user:: - - >>> from neutronclient.v2_0 import client - >>> username='adminUser' - >>> password='secretword' - >>> tenant_name='openstackDemo' - >>> auth_url='http://192.168.206.130:5000/v2.0' - >>> neutron = client.Client(username=username, password=password, - ... tenant_name=tenant_name, auth_url=auth_url) - >>>nets = neutron.list_networks() - -Command-line Tool -================= -In order to use the CLI, you must provide your OpenStack username, password, tenant, and auth endpoint. Use the corresponding configuration options (``--os-username``, ``--os-password``, ``--os-tenant-name``, and ``--os-auth-url``) or set them in environment variables:: - - export OS_USERNAME=user - export OS_PASSWORD=pass - export OS_TENANT_NAME=tenant - export OS_AUTH_URL=http://auth.example.com:5000/v2.0 - -The command line tool will attempt to reauthenticate using your provided credentials for every request. You can override this behavior by manually supplying an auth token using ``--os-url`` and ``--os-auth-token``. You can alternatively set these environment variables:: +.. toctree:: + :maxdepth: 2 - export OS_URL=http://neutron.example.org:9696/ - export OS_TOKEN=3bcc3d3a03f44e3d8377f9247b0ad155 + usage/cli + usage/library -If neutron server does not require authentication, besides these two arguments or environment variables (We can use any value as token.), we need manually supply ``--os-auth-strategy`` or set the environment variable:: +Developer Guide +--------------- - export OS_AUTH_STRATEGY=noauth +In the Developer Guide, you will find information on Neutron’s client +lower level programming details or APIs. -Once you've configured your authentication parameters, you can run ``neutron -h`` to see a complete listing of available commands. +.. toctree:: + :maxdepth: 2 + devref/client_command_extensions -Developer Docs -============== +History +------- .. toctree:: :maxdepth: 1 - devref/index history diff --git a/doc/source/usage/cli.rst b/doc/source/usage/cli.rst new file mode 100644 index 000000000..ac6b66b56 --- /dev/null +++ b/doc/source/usage/cli.rst @@ -0,0 +1,55 @@ +====================== +Command-line Interface +====================== + +The **neutron** shell utility interacts with OpenStack Networking API from the +command-line. It supports the entire features of OpenStack Networking API. + +Basic Usage +----------- + +In order to use the CLI, you must provide your OpenStack username, password, +tenant, and auth endpoint. Use the corresponding configuration options +(``--os-username``, ``--os-password``, ``--os-tenant-name``, and +``--os-auth-url``), but it is easier to set them in environment variables. + +.. code-block:: shell + + export OS_USERNAME=user + export OS_PASSWORD=pass + export OS_TENANT_NAME=tenant + export OS_AUTH_URL=http://auth.example.com:5000/v2.0 + +Once you've configured your authentication parameters, you can run **neutron** +commands. All commands take the form of: + +.. code-block:: none + + neutron [arguments...] + +Run **neutron help** to get a full list of all possible commands, and run +**neutron help ** to get detailed help for that command. + +Using with keystone token +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The command-line tool will attempt to re-authenticate using your provided +credentials for every request. You can override this behavior by manually +supplying an auth token using ``--os-url`` and ``--os-auth-token``. You can +alternatively set these environment variables. + +.. code-block:: shell + + export OS_URL=http://neutron.example.org:9696/ + export OS_TOKEN=3bcc3d3a03f44e3d8377f9247b0ad155 + +Using noauth mode +~~~~~~~~~~~~~~~~~ + +If neutron server does not require authentication, besides these two arguments +or environment variables (We can use any value as token.), we need manually +supply ``--os-auth-strategy`` or set the environment variable. + +.. code-block:: shell + + export OS_AUTH_STRATEGY=noauth diff --git a/doc/source/usage/library.rst b/doc/source/usage/library.rst new file mode 100644 index 000000000..8e9a61c00 --- /dev/null +++ b/doc/source/usage/library.rst @@ -0,0 +1,38 @@ +======================== +neutronclient Python API +======================== + +Basic Usage +----------- + +First create a client instance. + +.. code-block:: python + + >>> from neutronclient.v2_0 import client + >>> username='adminUser' + >>> password='secretword' + >>> tenant_name='openstackDemo' + >>> auth_url='http://192.168.206.130:5000/v2.0' + >>> neutron = client.Client(username=username, + ... password=password, + ... tenant_name=tenant_name, + ... auth_url=auth_url) + +Now you can call various methods on the client instance. + +.. code-block:: python + + >>> network = {'name': 'mynetwork', 'admin_state_up': True} + >>> neutron.create_network({'network':network}) + >>> networks = neutron.list_networks(name='mynetwork') + >>> print networks + >>> network_id = networks['networks'][0]['id'] + >>> neutron.delete_network(network_id) + +Alternatively, you can create a client instance using an auth token +and a service endpoint URL directly. + + >>> from neutronclient.v2_0 import client + >>> neutron = client.Client(endpoint_url='http://192.168.206.130:9696/', + token='d3f9226f27774f338019aa2611112ef6') From 7ff7e76d21e441efd611abf8ca6de96c15d9bb35 Mon Sep 17 00:00:00 2001 From: shu-mutou Date: Wed, 2 Dec 2015 18:08:23 +0900 Subject: [PATCH 300/845] Remove py26 support as of mitaka, the infra team won't have the resources available to reasonably test py26, also the oslo team is dropping py26 support from their libraries. sine we rely on oslo for a lot of our work, and depend on infra for our CI, we should drop py26 support too. Change-Id: I62db55ee1a9e7564a10a7284bd0e401b2cdbc580 Closes-Bug: 1519510 Depends-On: I4a2f860e8138bddb032418ee555e07a6fa0a3f3e --- setup.cfg | 1 - tox.ini | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index ee3f9af84..0962bd079 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,6 @@ classifier = Programming Language :: Python Programming Language :: Python :: 2 Programming Language :: Python :: 2.7 - Programming Language :: Python :: 2.6 Programming Language :: Python :: 3 Programming Language :: Python :: 3.3 Programming Language :: Python :: 3.4 diff --git a/tox.ini b/tox.ini index 452b751f0..8dce7dcd3 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] # py3 first to avoid .testrepository incompatibility -envlist = py33,py34,py26,py27,pypy,pep8 +envlist = py33,py34,py27,pypy,pep8 minversion = 1.6 skipsdist = True From 50311ab5376e7e6661bb8044732dd6ec03f26d74 Mon Sep 17 00:00:00 2001 From: takanorimiyagishi Date: Wed, 2 Dec 2015 14:31:13 +0900 Subject: [PATCH 301/845] Fix a column name "protocol/port" to "port/protocol" Fix neutron security-group-rule-list's result. The column name "protocol/port" had been reversed from value, and value is correct. Change-Id: I5aac35bfd717af4adf97c48f19b90b12357df71c Closes-bug: #1521819 --- neutronclient/neutron/v2_0/securitygroup.py | 7 ++++++- .../tests/functional/core/test_readonly_neutron.py | 2 +- neutronclient/tests/unit/test_cli20_securitygroup.py | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/neutronclient/neutron/v2_0/securitygroup.py b/neutronclient/neutron/v2_0/securitygroup.py index 2fb0bb95d..4891a1576 100644 --- a/neutronclient/neutron/v2_0/securitygroup.py +++ b/neutronclient/neutron/v2_0/securitygroup.py @@ -167,15 +167,20 @@ class ListSecurityGroupRule(neutronV20.ListCommand): resource = 'security_group_rule' list_columns = ['id', 'security_group_id', 'direction', - 'ethertype', 'protocol/port', 'remote'] + 'ethertype', 'port/protocol', 'remote'] # replace_rules: key is an attribute name in Neutron API and # corresponding value is a display name shown by CLI. replace_rules = {'security_group_id': 'security_group', 'remote_group_id': 'remote_group'} digest_fields = { + # The entry 'protocol/port' is leaving deliberetely for keep + # compatibility, 'remote': { 'method': _get_remote, 'depends_on': ['remote_ip_prefix', 'remote_group_id']}, + 'port/protocol': { + 'method': _get_protocol_port, + 'depends_on': ['protocol', 'port_range_min', 'port_range_max']}, 'protocol/port': { 'method': _get_protocol_port, 'depends_on': ['protocol', 'port_range_min', 'port_range_max']}} diff --git a/neutronclient/tests/functional/core/test_readonly_neutron.py b/neutronclient/tests/functional/core/test_readonly_neutron.py index d6ed1b8f7..aaecdc1f0 100644 --- a/neutronclient/tests/functional/core/test_readonly_neutron.py +++ b/neutronclient/tests/functional/core/test_readonly_neutron.py @@ -116,7 +116,7 @@ def test_neutron_security_group_rule_list(self): ('security-group-rule-list')) self.assertTableStruct(security_grp, ['id', 'security_group', 'direction', 'ethertype', - 'protocol/port', 'remote']) + 'port/protocol', 'remote']) def test_neutron_subnet_list(self): subnet_list = self.parser.listing(self.neutron('subnet-list')) diff --git a/neutronclient/tests/unit/test_cli20_securitygroup.py b/neutronclient/tests/unit/test_cli20_securitygroup.py index 8a8b1ada8..f077c1fcc 100644 --- a/neutronclient/tests/unit/test_cli20_securitygroup.py +++ b/neutronclient/tests/unit/test_cli20_securitygroup.py @@ -492,7 +492,7 @@ def test_list_security_group_rules_extend_proto_port(self): remote_ip_prefix='10.2.0.0/16')] expected = { 'cols': ['id', 'security_group', 'direction', 'ethertype', - 'protocol/port', 'remote'], + 'port/protocol', 'remote'], 'data': [ ('ruleid1', 'group1', 'ingress', 'IPv4', '22/tcp', 'any'), ('ruleid2', 'group2', 'egress', 'IPv6', '80-81/udp', 'any'), From d82050c58e40906f05a3cefd49c8782f9d10a584 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Sat, 28 Nov 2015 10:17:45 +0900 Subject: [PATCH 302/845] Add release notes for changes since 3.0.0 release We decided to use reno for coming releases, but we don't have entries for changes made after the previous release was shipped. This commit adds notes for them. Also fixes the wrong place of "Release Notes" section in the document index. It was my mistake in the past commit. Change-Id: Ie0eedf8cb82a57db070a65f73d965f1a3ce378ab --- .../relnotes-from-3.0.0-d7306f5af5e3868d.yaml | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 releasenotes/notes/relnotes-from-3.0.0-d7306f5af5e3868d.yaml diff --git a/releasenotes/notes/relnotes-from-3.0.0-d7306f5af5e3868d.yaml b/releasenotes/notes/relnotes-from-3.0.0-d7306f5af5e3868d.yaml new file mode 100644 index 000000000..15e569f3f --- /dev/null +++ b/releasenotes/notes/relnotes-from-3.0.0-d7306f5af5e3868d.yaml @@ -0,0 +1,25 @@ +--- +features: + - Support os-client-config. OS_CLOUD environment variable is used for + selecting named cloud configuration. + - Support keystoneauth1 library which brings us better kyestone v3 support. + - Client command extension now supports a child resource. + - New CLI for VPNaaS multiple local subnets. + - New CLI for VPNaaS endpoint group API. + - New CLI for flavor argument to loadbalancer v2 create. + - New CLI for Neutron flavor framework. + - Support creating floating IP on a specific subnet ID. + - NSX gateway extension adds new transport type values (ipsec_gre and ipsec_stt). + - New router-update option to update static routes (--route and --no-routes). + - New allowed-address-pairs option to port-update +upgrade: + - Cisco-specific neutron client commands have been removed. + These commands are ported to networking-cisco. + - py26 support has been dropped. +fixes: + - Name is no longer looked up on RBAC policies, + RBAC policies have no name field so the name query to + the server was always returning all entries since the + name filter was ignored. (bug 1517818) +other: + - cliff-tablib has been removed from test dependencies. From 800f35d8a5a99a67f27b8adf6b0c5046e748d49a Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Fri, 4 Dec 2015 02:25:31 +0900 Subject: [PATCH 303/845] Do not include reno releasenotes in normal documentation Per the recent release note guidelines for libraries [1], reno based release notes will be moved out from the normal docs. The previous (partial) release notes are also moved to releasenotes directory. The developer documentation now has a link to the releasenote web site. In addition, having reno directive in normal sphinx document prevents users from generating our docs from release tarballs. [1] http://lists.openstack.org/pipermail/openstack-dev/2015-November/080694.html Change-Id: I817f1a7331b8664486544c82e1a9a917864601bf Closes-Bug: #1520096 --- doc/source/index.rst | 6 ++---- releasenotes/source/index.rst | 8 ++++++++ .../history.rst => releasenotes/source/old_relnotes.rst | 8 +++----- 3 files changed, 13 insertions(+), 9 deletions(-) rename doc/source/history.rst => releasenotes/source/old_relnotes.rst (91%) diff --git a/doc/source/index.rst b/doc/source/index.rst index fa240033a..f42195b7c 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -30,7 +30,5 @@ lower level programming details or APIs. History ------- -.. toctree:: - :maxdepth: 1 - - history +Release notes is available at +http://docs.openstack.org/releasenotes/python-neutronclient/. diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index 2a4bceb4e..ed6bd6728 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -3,3 +3,11 @@ Release Notes ============= .. release-notes:: + +Past release notes +------------------ + +.. toctree:: + :maxdepth: 1 + + old_relnotes diff --git a/doc/source/history.rst b/releasenotes/source/old_relnotes.rst similarity index 91% rename from doc/source/history.rst rename to releasenotes/source/old_relnotes.rst index 97b00c09d..6a571a7b4 100644 --- a/doc/source/history.rst +++ b/releasenotes/source/old_relnotes.rst @@ -1,8 +1,6 @@ -============= -Release Notes -============= - -.. release-notes:: +================= +Old Release Notes +================= 2.2.2 ----- From aa2dad3d8335b3055c04be38f179cec7f2923904 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Fri, 4 Dec 2015 01:48:01 +0900 Subject: [PATCH 304/845] Setup for translation client translation effort starts and several client projects including novaclient already setup translations. To start translation, we need to initially import the translation file - and place it at the proper place so that the usual CI scripts can handle it. The proper place is for all python projects $PROJECT/locale/$PROJECT.pot - see setup.cfg. Further imports will be done by the OpenStack Proposal bot. Closes-Bug: #1099603 Change-Id: I5b13a9428e6272e83a45b82c06f524be772d3d7a --- babel.cfg | 2 + .../locale/python-neutronclient.pot | 1768 +++++++++++++++++ setup.cfg | 14 + 3 files changed, 1784 insertions(+) create mode 100644 babel.cfg create mode 100644 python-neutronclient/locale/python-neutronclient.pot diff --git a/babel.cfg b/babel.cfg new file mode 100644 index 000000000..15cd6cb76 --- /dev/null +++ b/babel.cfg @@ -0,0 +1,2 @@ +[python: **.py] + diff --git a/python-neutronclient/locale/python-neutronclient.pot b/python-neutronclient/locale/python-neutronclient.pot new file mode 100644 index 000000000..94216dd25 --- /dev/null +++ b/python-neutronclient/locale/python-neutronclient.pot @@ -0,0 +1,1768 @@ +# Translations template for python-neutronclient. +# Copyright (C) 2015 ORGANIZATION +# This file is distributed under the same license as the +# python-neutronclient project. +# FIRST AUTHOR , 2015. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: python-neutronclient 3.1.1.dev64\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2015-12-03 21:54+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.1.1\n" + +#: neutronclient/client.py:230 +msgid "" +"For \"noauth\" authentication strategy, the endpoint must be specified " +"either in the constructor or using --os-url" +msgstr "" + +#: neutronclient/client.py:241 +#, python-format +msgid "Unknown auth strategy: %s" +msgstr "" + +#: neutronclient/shell.py:136 +#, python-format +msgid "invalid int value: %r" +msgstr "" + +#: neutronclient/shell.py:138 +#, python-format +msgid "input value %d is negative" +msgstr "" + +#: neutronclient/shell.py:410 +#, python-format +msgid "" +"\n" +"Commands for API v%s:\n" +msgstr "" + +#: neutronclient/shell.py:475 +msgid "" +"Increase verbosity of output and show tracebacks on errors. You can " +"repeat this option." +msgstr "" + +#: neutronclient/shell.py:482 +msgid "Suppress output except warnings and errors." +msgstr "" + +#: neutronclient/shell.py:488 +msgid "Show this help message and exit." +msgstr "" + +#: neutronclient/shell.py:494 +msgid "" +"How many times the request to the Neutron server should be retried if it " +"fails." +msgstr "" + +#: neutronclient/shell.py:514 +msgid "Defaults to env[OS_NETWORK_SERVICE_TYPE] or network." +msgstr "" + +#: neutronclient/shell.py:519 +msgid "Defaults to env[OS_ENDPOINT_TYPE] or public." +msgstr "" + +#: neutronclient/shell.py:526 +msgid "DEPRECATED! Use --os-service-type." +msgstr "" + +#: neutronclient/shell.py:533 +msgid "DEPRECATED! Use --os-endpoint-type." +msgstr "" + +#: neutronclient/shell.py:538 +msgid "DEPRECATED! Only keystone is supported." +msgstr "" + +#: neutronclient/shell.py:547 +msgid "Defaults to env[OS_CLOUD]." +msgstr "" + +#: neutronclient/shell.py:552 +msgid "Authentication URL, defaults to env[OS_AUTH_URL]." +msgstr "" + +#: neutronclient/shell.py:561 +msgid "Authentication tenant name, defaults to env[OS_TENANT_NAME]." +msgstr "" + +#: neutronclient/shell.py:580 +msgid "Authentication tenant ID, defaults to env[OS_TENANT_ID]." +msgstr "" + +#: neutronclient/shell.py:594 +msgid "Authentication username, defaults to env[OS_USERNAME]." +msgstr "" + +#: neutronclient/shell.py:602 +msgid "Authentication user ID (Env: OS_USER_ID)" +msgstr "" + +#: neutronclient/shell.py:654 +msgid "" +"Path of certificate file to use in SSL connection. This file can " +"optionally be prepended with the private key. Defaults to env[OS_CERT]." +msgstr "" + +#: neutronclient/shell.py:663 +msgid "" +"Specify a CA bundle file to use in verifying a TLS (https) server " +"certificate. Defaults to env[OS_CACERT]." +msgstr "" + +#: neutronclient/shell.py:671 +msgid "" +"Path of client key to use in SSL connection. This option is not necessary" +" if your key is prepended to your certificate file. Defaults to " +"env[OS_KEY]." +msgstr "" + +#: neutronclient/shell.py:679 +msgid "Authentication password, defaults to env[OS_PASSWORD]." +msgstr "" + +#: neutronclient/shell.py:687 +msgid "Authentication region name, defaults to env[OS_REGION_NAME]." +msgstr "" + +#: neutronclient/shell.py:696 +msgid "Authentication token, defaults to env[OS_TOKEN]." +msgstr "" + +#: neutronclient/shell.py:704 +msgid "" +"Timeout in seconds to wait for an HTTP response. Defaults to " +"env[OS_NETWORK_TIMEOUT] or None if not specified." +msgstr "" + +#: neutronclient/shell.py:710 +msgid "Defaults to env[OS_URL]." +msgstr "" + +#: neutronclient/shell.py:719 +msgid "" +"Explicitly allow neutronclient to perform \"insecure\" SSL (https) " +"requests. The server's certificate will not be verified against any " +"certificate authorities. This option should be used with caution." +msgstr "" + +#: neutronclient/shell.py:821 +#, python-format +msgid "Try 'neutron help %s' for more information." +msgstr "" + +#: neutronclient/common/exceptions.py:39 +msgid "An unknown exception occurred." +msgstr "" + +#: neutronclient/common/exceptions.py:78 +msgid "Unauthorized: bad credentials." +msgstr "" + +#: neutronclient/common/exceptions.py:83 +msgid "Forbidden: your credentials don't give you access to this resource." +msgstr "" + +#: neutronclient/common/exceptions.py:170 +msgid "auth_url was not provided to the Neutron client" +msgstr "" + +#: neutronclient/common/exceptions.py:174 +msgid "Could not find Service or Region in Service Catalog." +msgstr "" + +#: neutronclient/common/exceptions.py:178 +#, python-format +msgid "Could not find endpoint type %(type_)s in Service Catalog." +msgstr "" + +#: neutronclient/common/exceptions.py:182 +msgid "" +"Found more than one matching endpoint in Service Catalog: " +"%(matching_endpoints)" +msgstr "" + +#: neutronclient/common/exceptions.py:195 +#, python-format +msgid "Connection to neutron failed: %(reason)s" +msgstr "" + +#: neutronclient/common/exceptions.py:199 +#, python-format +msgid "SSL certificate validation has failed: %(reason)s" +msgstr "" + +#: neutronclient/common/exceptions.py:203 +#, python-format +msgid "Malformed response body: %(reason)s" +msgstr "" + +#: neutronclient/common/exceptions.py:207 +#, python-format +msgid "Invalid content type %(content_type)s." +msgstr "" + +#: neutronclient/common/exceptions.py:229 +#, python-format +msgid "" +"Multiple %(resource)s matches found for name '%(name)s', use an ID to be " +"more specific." +msgstr "" + +#: neutronclient/common/serializer.py:223 +msgid "Cannot understand JSON" +msgstr "" + +#: neutronclient/common/serializer.py:296 +msgid "Cannot understand XML" +msgstr "" + +#: neutronclient/common/utils.py:56 +#, python-format +msgid "" +"Invalid %(api_name)s client version '%(version)s'. must be one of: " +"%(map_keys)s" +msgstr "" + +#: neutronclient/common/utils.py:113 +#, python-format +msgid "invalid key-value '%s', expected format: key=value" +msgstr "" + +#: neutronclient/common/validators.py:38 +#, python-format +msgid "%(attr_name)s \"%(val)s\" should be an integer [%(min)i:%(max)i]." +msgstr "" + +#: neutronclient/common/validators.py:43 +#, python-format +msgid "" +"%(attr_name)s \"%(val)s\" should be an integer greater than or equal to " +"%(min)i." +msgstr "" + +#: neutronclient/common/validators.py:48 +#, python-format +msgid "" +"%(attr_name)s \"%(val)s\" should be an integer smaller than or equal to " +"%(max)i." +msgstr "" + +#: neutronclient/common/validators.py:53 +#, python-format +msgid "%(attr_name)s \"%(val)s\" should be an integer." +msgstr "" + +#: neutronclient/common/validators.py:68 +#, python-format +msgid "%(attr_name)s \"%(val)s\" is not a valid CIDR." +msgstr "" + +#: neutronclient/neutron/client.py:56 +#, python-format +msgid "API version %s is not supported" +msgstr "" + +#: neutronclient/neutron/v2_0/__init__.py:69 +#, python-format +msgid "Unable to find %(resource)s with id '%(id)s'" +msgstr "" + +#: neutronclient/neutron/v2_0/__init__.py:106 +#, python-format +msgid "Unable to find %(resource)s with name '%(name)s'" +msgstr "" + +#: neutronclient/neutron/v2_0/__init__.py:139 +msgid "Show detailed information." +msgstr "" + +#: neutronclient/neutron/v2_0/__init__.py:154 +msgid "Specify the field(s) to be returned by server. You can repeat this option." +msgstr "" + +#: neutronclient/neutron/v2_0/__init__.py:164 +msgid "" +"Specify retrieve unit of each request, then split one request to several " +"requests." +msgstr "" + +#: neutronclient/neutron/v2_0/__init__.py:174 +msgid "" +"Sorts the list by the specified fields in the specified directions. You " +"can repeat this option, but you must specify an equal number of sort_dir " +"and sort_key values. Extra sort_dir options are ignored. Missing sort_dir" +" options use the default asc value." +msgstr "" + +#: neutronclient/neutron/v2_0/__init__.py:183 +msgid "Sorts the list in the specified direction. You can repeat this option." +msgstr "" + +#: neutronclient/neutron/v2_0/__init__.py:209 +#: neutronclient/neutron/v2_0/__init__.py:287 +#: neutronclient/neutron/v2_0/__init__.py:309 +#: neutronclient/neutron/v2_0/__init__.py:314 +#, python-format +msgid "Invalid values_specs %s" +msgstr "" + +#: neutronclient/neutron/v2_0/__init__.py:279 +#, python-format +msgid "Duplicated options %s" +msgstr "" + +#: neutronclient/neutron/v2_0/__init__.py:429 +msgid "The XML or JSON request format." +msgstr "" + +#: neutronclient/neutron/v2_0/__init__.py:478 +#: neutronclient/neutron/v2_0/quota.py:47 +#: neutronclient/neutron/v2_0/quota.py:108 +#: neutronclient/neutron/v2_0/quota.py:152 +msgid "The owner tenant ID." +msgstr "" + +#: neutronclient/neutron/v2_0/__init__.py:504 +#, python-format +msgid "Created a new %s:" +msgstr "" + +#: neutronclient/neutron/v2_0/__init__.py:521 +#, python-format +msgid "ID or name of %s to update." +msgstr "" + +#: neutronclient/neutron/v2_0/__init__.py:523 +#, python-format +msgid "ID of %s to update." +msgstr "" + +#: neutronclient/neutron/v2_0/__init__.py:545 +#, python-format +msgid "Must specify new values to update %s" +msgstr "" + +#: neutronclient/neutron/v2_0/__init__.py:561 +#, python-format +msgid "Updated %(resource)s: %(id)s" +msgstr "" + +#: neutronclient/neutron/v2_0/__init__.py:577 +#, python-format +msgid "ID or name of %s to delete." +msgstr "" + +#: neutronclient/neutron/v2_0/__init__.py:579 +#, python-format +msgid "ID of %s to delete." +msgstr "" + +#: neutronclient/neutron/v2_0/__init__.py:607 +#, python-format +msgid "Deleted %(resource)s: %(id)s" +msgstr "" + +#: neutronclient/neutron/v2_0/__init__.py:732 +#, python-format +msgid "ID or name of %s to look up." +msgstr "" + +#: neutronclient/neutron/v2_0/__init__.py:734 +#, python-format +msgid "ID of %s to look up." +msgstr "" + +#: neutronclient/neutron/v2_0/address_scope.py:45 +msgid "Set the address scope as shared." +msgstr "" + +#: neutronclient/neutron/v2_0/address_scope.py:48 +msgid "Specify the name of the address scope." +msgstr "" + +#: neutronclient/neutron/v2_0/address_scope.py:71 +msgid "Name of the address scope to update." +msgstr "" + +#: neutronclient/neutron/v2_0/agent.py:69 +msgid "Set admin state up of the agent to false." +msgstr "" + +#: neutronclient/neutron/v2_0/agent.py:72 +msgid "Description for the agent." +msgstr "" + +#: neutronclient/neutron/v2_0/agentscheduler.py:35 +#: neutronclient/neutron/v2_0/agentscheduler.py:60 +#: neutronclient/neutron/v2_0/agentscheduler.py:88 +msgid "ID of the DHCP agent." +msgstr "" + +#: neutronclient/neutron/v2_0/agentscheduler.py:38 +msgid "Network to add." +msgstr "" + +#: neutronclient/neutron/v2_0/agentscheduler.py:49 +#, python-format +msgid "Added network %s to DHCP agent" +msgstr "" + +#: neutronclient/neutron/v2_0/agentscheduler.py:63 +msgid "Network to remove." +msgstr "" + +#: neutronclient/neutron/v2_0/agentscheduler.py:74 +#, python-format +msgid "Removed network %s from DHCP agent" +msgstr "" + +#: neutronclient/neutron/v2_0/agentscheduler.py:110 +msgid "Network to query." +msgstr "" + +#: neutronclient/neutron/v2_0/agentscheduler.py:133 +#: neutronclient/neutron/v2_0/agentscheduler.py:158 +msgid "ID of the L3 agent." +msgstr "" + +#: neutronclient/neutron/v2_0/agentscheduler.py:136 +msgid "Router to add." +msgstr "" + +#: neutronclient/neutron/v2_0/agentscheduler.py:147 +#, python-format +msgid "Added router %s to L3 agent" +msgstr "" + +#: neutronclient/neutron/v2_0/agentscheduler.py:161 +msgid "Router to remove." +msgstr "" + +#: neutronclient/neutron/v2_0/agentscheduler.py:172 +#, python-format +msgid "Removed router %s from L3 agent" +msgstr "" + +#: neutronclient/neutron/v2_0/agentscheduler.py:190 +msgid "ID of the L3 agent to query." +msgstr "" + +#: neutronclient/neutron/v2_0/agentscheduler.py:211 +msgid "Router to query." +msgstr "" + +#: neutronclient/neutron/v2_0/agentscheduler.py:243 +#: neutronclient/neutron/v2_0/agentscheduler.py:296 +msgid "ID of the loadbalancer agent to query." +msgstr "" + +#: neutronclient/neutron/v2_0/agentscheduler.py:267 +msgid "Pool to query." +msgstr "" + +#: neutronclient/neutron/v2_0/agentscheduler.py:320 +msgid "LoadBalancer to query." +msgstr "" + +#: neutronclient/neutron/v2_0/floatingip.py:50 +msgid "Network name or ID to allocate floating IP from." +msgstr "" + +#: neutronclient/neutron/v2_0/floatingip.py:53 +msgid "ID of the port to be associated with the floating IP." +msgstr "" + +#: neutronclient/neutron/v2_0/floatingip.py:59 +#: neutronclient/neutron/v2_0/floatingip.py:107 +msgid "IP address on the port (only required if port has multiple IPs)." +msgstr "" + +#: neutronclient/neutron/v2_0/floatingip.py:66 +msgid "IP address of the floating IP" +msgstr "" + +#: neutronclient/neutron/v2_0/floatingip.py:70 +msgid "Subnet ID on which you want to create the floating IP." +msgstr "" + +#: neutronclient/neutron/v2_0/floatingip.py:100 +msgid "ID of the floating IP to associate." +msgstr "" + +#: neutronclient/neutron/v2_0/floatingip.py:103 +msgid "ID or name of the port to be associated with the floating IP." +msgstr "" + +#: neutronclient/neutron/v2_0/floatingip.py:123 +#, python-format +msgid "Associated floating IP %s" +msgstr "" + +#: neutronclient/neutron/v2_0/floatingip.py:138 +msgid "ID of the floating IP to disassociate." +msgstr "" + +#: neutronclient/neutron/v2_0/floatingip.py:147 +#, python-format +msgid "Disassociated floating IP %s" +msgstr "" + +#: neutronclient/neutron/v2_0/metering.py:43 +msgid "Name of metering label to create." +msgstr "" + +#: neutronclient/neutron/v2_0/metering.py:46 +msgid "Description of metering label to create." +msgstr "" + +#: neutronclient/neutron/v2_0/metering.py:50 +msgid "Set the label as shared." +msgstr "" + +#: neutronclient/neutron/v2_0/metering.py:89 +msgid "Id or Name of the label." +msgstr "" + +#: neutronclient/neutron/v2_0/metering.py:92 +#: neutronclient/neutron/v2_0/securitygroup.py:330 +msgid "CIDR to match on." +msgstr "" + +#: neutronclient/neutron/v2_0/metering.py:96 +msgid "Direction of traffic, default: ingress." +msgstr "" + +#: neutronclient/neutron/v2_0/metering.py:100 +msgid "Exclude this CIDR from the label, default: not excluded." +msgstr "" + +#: neutronclient/neutron/v2_0/network.py:114 +#: neutronclient/neutron/v2_0/port.py:230 +#: neutronclient/neutron/v2_0/router.py:62 +#: neutronclient/neutron/v2_0/fw/firewall.py:56 +#: neutronclient/neutron/v2_0/lb/healthmonitor.py:48 +#: neutronclient/neutron/v2_0/lb/member.py:48 +#: neutronclient/neutron/v2_0/lb/pool.py:54 +#: neutronclient/neutron/v2_0/lb/vip.py:52 +#: neutronclient/neutron/v2_0/lb/v2/healthmonitor.py:49 +#: neutronclient/neutron/v2_0/lb/v2/listener.py:55 +#: neutronclient/neutron/v2_0/lb/v2/loadbalancer.py:51 +#: neutronclient/neutron/v2_0/lb/v2/pool.py:60 +#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:98 +#: neutronclient/neutron/v2_0/vpn/vpnservice.py:47 +msgid "Set admin state up to false." +msgstr "" + +#: neutronclient/neutron/v2_0/network.py:122 +msgid "Set the network as shared." +msgstr "" + +#: neutronclient/neutron/v2_0/network.py:127 +msgid "The physical mechanism by which the virtual network is implemented." +msgstr "" + +#: neutronclient/neutron/v2_0/network.py:132 +msgid "" +"Name of the physical network over which the virtual network is " +"implemented." +msgstr "" + +#: neutronclient/neutron/v2_0/network.py:137 +msgid "VLAN ID for VLAN networks or tunnel-id for GRE/VXLAN networks." +msgstr "" + +#: neutronclient/neutron/v2_0/network.py:143 +msgid "Create a vlan transparent network." +msgstr "" + +#: neutronclient/neutron/v2_0/network.py:146 +msgid "Name of network to create." +msgstr "" + +#: neutronclient/neutron/v2_0/port.py:45 +msgid "Name of this port." +msgstr "" + +#: neutronclient/neutron/v2_0/port.py:49 +msgid "" +"Desired IP and/or subnet for this port: " +"subnet_id=,ip_address=. You can repeat this option." +msgstr "" + +#: neutronclient/neutron/v2_0/port.py:58 +msgid "Device ID of this port." +msgstr "" + +#: neutronclient/neutron/v2_0/port.py:64 +msgid "Device owner of this port." +msgstr "" + +#: neutronclient/neutron/v2_0/port.py:111 +msgid "ID or name of router to look up." +msgstr "" + +#: neutronclient/neutron/v2_0/port.py:135 +msgid "Security group associated with the port. You can repeat this option." +msgstr "" + +#: neutronclient/neutron/v2_0/port.py:140 +msgid "Associate no security groups with the port." +msgstr "" + +#: neutronclient/neutron/v2_0/port.py:162 +msgid "" +"Extra dhcp options to be assigned to this port: " +"opt_name=,opt_value=,ip_version={4,6}. You can " +"repeat this option." +msgstr "" + +#: neutronclient/neutron/v2_0/port.py:173 +msgid "" +"Invalid --extra-dhcp-opt option, can only be: " +"opt_name=,opt_value=,ip_version={4,6}. You can " +"repeat this option." +msgstr "" + +#: neutronclient/neutron/v2_0/port.py:204 +msgid "Allowed address pair associated with the port.You can repeat this option." +msgstr "" + +#: neutronclient/neutron/v2_0/port.py:209 +msgid "Associate no allowed address pairs with the port." +msgstr "" + +#: neutronclient/neutron/v2_0/port.py:237 +msgid "MAC address of this port." +msgstr "" + +#: neutronclient/neutron/v2_0/port.py:244 +msgid "VNIC type for this port." +msgstr "" + +#: neutronclient/neutron/v2_0/port.py:251 +msgid "Custom data to be passed as binding:profile." +msgstr "" + +#: neutronclient/neutron/v2_0/port.py:262 +msgid "Network ID or name this port belongs to." +msgstr "" + +#: neutronclient/neutron/v2_0/port.py:305 +msgid "Set admin state up for the port." +msgstr "" + +#: neutronclient/neutron/v2_0/quota.py:62 +#, python-format +msgid "Deleted %(resource)s: %(tenant_id)s" +msgstr "" + +#: neutronclient/neutron/v2_0/quota.py:158 +msgid "The limit of networks." +msgstr "" + +#: neutronclient/neutron/v2_0/quota.py:161 +msgid "The limit of subnets." +msgstr "" + +#: neutronclient/neutron/v2_0/quota.py:164 +msgid "The limit of ports." +msgstr "" + +#: neutronclient/neutron/v2_0/quota.py:167 +msgid "The limit of routers." +msgstr "" + +#: neutronclient/neutron/v2_0/quota.py:170 +msgid "The limit of floating IPs." +msgstr "" + +#: neutronclient/neutron/v2_0/quota.py:173 +msgid "The limit of security groups." +msgstr "" + +#: neutronclient/neutron/v2_0/quota.py:176 +msgid "The limit of security groups rules." +msgstr "" + +#: neutronclient/neutron/v2_0/quota.py:179 +msgid "The limit of vips." +msgstr "" + +#: neutronclient/neutron/v2_0/quota.py:182 +msgid "The limit of pools." +msgstr "" + +#: neutronclient/neutron/v2_0/quota.py:185 +msgid "The limit of pool members." +msgstr "" + +#: neutronclient/neutron/v2_0/quota.py:188 +msgid "The limit of health monitors." +msgstr "" + +#: neutronclient/neutron/v2_0/quota.py:196 +#, python-format +msgid "Quota limit for %(name)s must be an integer" +msgstr "" + +#: neutronclient/neutron/v2_0/rbac.py:54 +msgid "ID or name of the RBAC object." +msgstr "" + +#: neutronclient/neutron/v2_0/rbac.py:58 +msgid "Type of the object that RBAC policy affects." +msgstr "" + +#: neutronclient/neutron/v2_0/rbac.py:61 neutronclient/neutron/v2_0/rbac.py:91 +msgid "ID of the tenant to which the RBAC policy will be enforced." +msgstr "" + +#: neutronclient/neutron/v2_0/rbac.py:66 +msgid "Action for the RBAC policy." +msgstr "" + +#: neutronclient/neutron/v2_0/router.py:69 +msgid "Name of router to create." +msgstr "" + +#: neutronclient/neutron/v2_0/router.py:72 +msgid "Create a distributed router." +msgstr "" + +#: neutronclient/neutron/v2_0/router.py:75 +msgid "Create a highly available router." +msgstr "" + +#: neutronclient/neutron/v2_0/router.py:98 +msgid "Name of this router." +msgstr "" + +#: neutronclient/neutron/v2_0/router.py:101 +msgid "Specify the administrative state of the router (True meaning \"Up\")" +msgstr "" + +#: neutronclient/neutron/v2_0/router.py:108 +msgid "True means this router should operate in distributed mode." +msgstr "" + +#: neutronclient/neutron/v2_0/router.py:114 +msgid "Route to associate with the router. You can repeat this option." +msgstr "" + +#: neutronclient/neutron/v2_0/router.py:119 +msgid "Remove routes associated with the router." +msgstr "" + +#: neutronclient/neutron/v2_0/router.py:150 +#: neutronclient/neutron/v2_0/router.py:218 +#: neutronclient/neutron/v2_0/router.py:269 +msgid "ID or name of the router." +msgstr "" + +#: neutronclient/neutron/v2_0/router.py:153 +msgid "" +"The format is \"SUBNET|subnet=SUBNET|port=PORT\". Either a subnet or port" +" must be specified. Both ID and name are accepted as SUBNET or PORT. Note" +" that \"subnet=\" can be omitted when specifying a subnet." +msgstr "" + +#: neutronclient/neutron/v2_0/router.py:168 +msgid "You must specify either subnet or port for INTERFACE parameter." +msgstr "" + +#: neutronclient/neutron/v2_0/router.py:193 +#, python-format +msgid "Added interface %(port)s to router %(router)s." +msgstr "" + +#: neutronclient/neutron/v2_0/router.py:205 +#, python-format +msgid "Removed interface from router %s." +msgstr "" + +#: neutronclient/neutron/v2_0/router.py:221 +msgid "ID or name of the external network for the gateway." +msgstr "" + +#: neutronclient/neutron/v2_0/router.py:224 +msgid "Disable source NAT on the router gateway." +msgstr "" + +#: neutronclient/neutron/v2_0/router.py:227 +msgid "" +"Desired IP and/or subnet on external network: " +"subnet_id=,ip_address=. You can repeat this option." +msgstr "" + +#: neutronclient/neutron/v2_0/router.py:255 +#, python-format +msgid "Set gateway for router %s" +msgstr "" + +#: neutronclient/neutron/v2_0/router.py:279 +#, python-format +msgid "Removed gateway from router %s" +msgstr "" + +#: neutronclient/neutron/v2_0/securitygroup.py:126 +#: neutronclient/neutron/v2_0/securitygroup.py:153 +msgid "Name of security group." +msgstr "" + +#: neutronclient/neutron/v2_0/securitygroup.py:129 +#: neutronclient/neutron/v2_0/securitygroup.py:156 +msgid "Description of security group." +msgstr "" + +#: neutronclient/neutron/v2_0/securitygroup.py:189 +msgid "Do not convert security group ID to its name." +msgstr "" + +#: neutronclient/neutron/v2_0/securitygroup.py:305 +msgid "Security group name or ID to add rule." +msgstr "" + +#: neutronclient/neutron/v2_0/securitygroup.py:309 +msgid "Direction of traffic: ingress/egress." +msgstr "" + +#: neutronclient/neutron/v2_0/securitygroup.py:312 +msgid "IPv4/IPv6" +msgstr "" + +#: neutronclient/neutron/v2_0/securitygroup.py:315 +msgid "Protocol of packet." +msgstr "" + +#: neutronclient/neutron/v2_0/securitygroup.py:318 +msgid "Starting port range." +msgstr "" + +#: neutronclient/neutron/v2_0/securitygroup.py:324 +msgid "Ending port range." +msgstr "" + +#: neutronclient/neutron/v2_0/securitygroup.py:336 +msgid "Remote security group name or ID to apply rule." +msgstr "" + +#: neutronclient/neutron/v2_0/subnet.py:54 +msgid "Name of this subnet." +msgstr "" + +#: neutronclient/neutron/v2_0/subnet.py:58 +msgid "Gateway IP of this subnet." +msgstr "" + +#: neutronclient/neutron/v2_0/subnet.py:62 +msgid "No distribution of gateway." +msgstr "" + +#: neutronclient/neutron/v2_0/subnet.py:66 +msgid "" +"Allocation pool IP addresses for this subnet (This option can be " +"repeated)." +msgstr "" + +#: neutronclient/neutron/v2_0/subnet.py:75 +msgid "Additional route (This option can be repeated)." +msgstr "" + +#: neutronclient/neutron/v2_0/subnet.py:79 +msgid "DNS name server for this subnet (This option can be repeated)." +msgstr "" + +#: neutronclient/neutron/v2_0/subnet.py:84 +msgid "Disable DHCP for this subnet." +msgstr "" + +#: neutronclient/neutron/v2_0/subnet.py:88 +msgid "Enable DHCP for this subnet." +msgstr "" + +#: neutronclient/neutron/v2_0/subnet.py:107 +msgid "You cannot enable and disable DHCP at the same time." +msgstr "" + +#: neutronclient/neutron/v2_0/subnet.py:123 +msgid "--ipv6-ra-mode is invalid when --ip-version is 4" +msgstr "" + +#: neutronclient/neutron/v2_0/subnet.py:128 +msgid "--ipv6-address-mode is invalid when --ip-version is 4" +msgstr "" + +#: neutronclient/neutron/v2_0/subnet.py:163 +msgid "" +"IP version to use, default is 4. Note that when subnetpool is specified, " +"IP version is determined from the subnetpool and this option is ignored." +msgstr "" + +#: neutronclient/neutron/v2_0/subnet.py:174 +msgid "Network ID or name this subnet belongs to." +msgstr "" + +#: neutronclient/neutron/v2_0/subnet.py:177 +msgid "CIDR of subnet to create." +msgstr "" + +#: neutronclient/neutron/v2_0/subnet.py:181 +msgid "IPv6 RA (Router Advertisement) mode." +msgstr "" + +#: neutronclient/neutron/v2_0/subnet.py:185 +msgid "IPv6 address mode." +msgstr "" + +#: neutronclient/neutron/v2_0/subnet.py:188 +msgid "ID or name of subnetpool from which this subnet will obtain a CIDR." +msgstr "" + +#: neutronclient/neutron/v2_0/subnet.py:192 +msgid "Prefix length for subnet allocation from subnetpool." +msgstr "" + +#: neutronclient/neutron/v2_0/subnet.py:224 +#, python-format +msgid "" +"An IPv%(ip)d subnet with a %(cidr)s CIDR will have only one usable IP " +"address so the device attached to it will not have any IP connectivity." +msgstr "" + +#: neutronclient/neutron/v2_0/subnetpool.py:24 +msgid "Subnetpool minimum prefix length." +msgstr "" + +#: neutronclient/neutron/v2_0/subnetpool.py:27 +msgid "Subnetpool maximum prefix length." +msgstr "" + +#: neutronclient/neutron/v2_0/subnetpool.py:30 +msgid "Subnetpool default prefix length." +msgstr "" + +#: neutronclient/neutron/v2_0/subnetpool.py:34 +msgid "Subnetpool prefixes (This option can be repeated)." +msgstr "" + +#: neutronclient/neutron/v2_0/subnetpool.py:69 +msgid "Set the subnetpool as shared." +msgstr "" + +#: neutronclient/neutron/v2_0/subnetpool.py:72 +msgid "Name of subnetpool to create." +msgstr "" + +#: neutronclient/neutron/v2_0/subnetpool.py:76 +#: neutronclient/neutron/v2_0/subnetpool.py:113 +msgid "" +"ID or name of the address scope with which the subnetpool is associated. " +"Prefixes must be unique across address scopes" +msgstr "" + +#: neutronclient/neutron/v2_0/subnetpool.py:109 +msgid "Name of subnetpool to update." +msgstr "" + +#: neutronclient/neutron/v2_0/subnetpool.py:119 +msgid "Detach subnetpool from the address scope" +msgstr "" + +#: neutronclient/neutron/v2_0/contrib/_fox_sockets.py:24 +#: neutronclient/neutron/v2_0/contrib/_fox_sockets.py:77 +msgid "Name of this fox socket." +msgstr "" + +#: neutronclient/neutron/v2_0/flavor/flavor.py:49 +#: neutronclient/neutron/v2_0/flavor/flavor.py:86 +msgid "Name for the flavor." +msgstr "" + +#: neutronclient/neutron/v2_0/flavor/flavor.py:53 +msgid "" +"Service type to which the flavor applies to: e.g. VPN. (See service-" +"provider-list for loaded examples.)" +msgstr "" + +#: neutronclient/neutron/v2_0/flavor/flavor.py:57 +#: neutronclient/neutron/v2_0/flavor/flavor.py:89 +msgid "Description for the flavor." +msgstr "" + +#: neutronclient/neutron/v2_0/flavor/flavor.py:62 +#: neutronclient/neutron/v2_0/flavor/flavor.py:94 +#: neutronclient/neutron/v2_0/flavor/flavor_profile.py:57 +#: neutronclient/neutron/v2_0/flavor/flavor_profile.py:92 +msgid "Sets enabled flag." +msgstr "" + +#: neutronclient/neutron/v2_0/flavor/flavor.py:113 +msgid "Name or ID of the flavor to associate." +msgstr "" + +#: neutronclient/neutron/v2_0/flavor/flavor.py:117 +msgid "ID of the flavor profile to be associated with the flavor." +msgstr "" + +#: neutronclient/neutron/v2_0/flavor/flavor.py:130 +#, python-format +msgid "Associated flavor %(flavor)s with flavor_profile %(profile)s" +msgstr "" + +#: neutronclient/neutron/v2_0/flavor/flavor.py:147 +msgid "Name or ID of the flavor." +msgstr "" + +#: neutronclient/neutron/v2_0/flavor/flavor.py:151 +msgid "ID of the flavor profile to be disassociated from the flavor." +msgstr "" + +#: neutronclient/neutron/v2_0/flavor/flavor.py:163 +#, python-format +msgid "Disassociated flavor %(flavor)s from flavor_profile %(profile)s" +msgstr "" + +#: neutronclient/neutron/v2_0/flavor/flavor_profile.py:46 +#: neutronclient/neutron/v2_0/flavor/flavor_profile.py:81 +msgid "Description for the flavor profile." +msgstr "" + +#: neutronclient/neutron/v2_0/flavor/flavor_profile.py:49 +#: neutronclient/neutron/v2_0/flavor/flavor_profile.py:84 +msgid "Python module path to driver." +msgstr "" + +#: neutronclient/neutron/v2_0/flavor/flavor_profile.py:52 +#: neutronclient/neutron/v2_0/flavor/flavor_profile.py:87 +msgid "Metainfo for the flavor profile." +msgstr "" + +#: neutronclient/neutron/v2_0/fw/firewall.py:45 +#: neutronclient/neutron/v2_0/fw/firewall.py:89 +msgid "Firewall policy name or ID." +msgstr "" + +#: neutronclient/neutron/v2_0/fw/firewall.py:48 +msgid "Name for the firewall." +msgstr "" + +#: neutronclient/neutron/v2_0/fw/firewall.py:51 +#: neutronclient/neutron/v2_0/fw/firewallrule.py:77 +msgid "Description for the firewall rule." +msgstr "" + +#: neutronclient/neutron/v2_0/fw/firewall.py:62 +#: neutronclient/neutron/v2_0/fw/firewall.py:96 +msgid "" +"Firewall associated router names or IDs (requires FWaaS router insertion " +"extension, this option can be repeated)" +msgstr "" + +#: neutronclient/neutron/v2_0/fw/firewall.py:101 +msgid "" +"Associate no routers with the firewall (requires FWaaS router insertion " +"extension)" +msgstr "" + +#: neutronclient/neutron/v2_0/fw/firewallpolicy.py:37 +msgid "" +"Ordered list of whitespace-delimited firewall rule names or IDs; e.g., " +"--firewall-rules \"rule1 rule2\"" +msgstr "" + +#: neutronclient/neutron/v2_0/fw/firewallpolicy.py:83 +msgid "Name for the firewall policy." +msgstr "" + +#: neutronclient/neutron/v2_0/fw/firewallpolicy.py:86 +msgid "Description for the firewall policy." +msgstr "" + +#: neutronclient/neutron/v2_0/fw/firewallpolicy.py:91 +msgid "Create a shared policy." +msgstr "" + +#: neutronclient/neutron/v2_0/fw/firewallpolicy.py:97 +msgid "Sets audited to True." +msgstr "" + +#: neutronclient/neutron/v2_0/fw/firewallpolicy.py:159 +msgid "Insert before this rule." +msgstr "" + +#: neutronclient/neutron/v2_0/fw/firewallpolicy.py:163 +msgid "Insert after this rule." +msgstr "" + +#: neutronclient/neutron/v2_0/fw/firewallpolicy.py:167 +msgid "New rule to insert." +msgstr "" + +#: neutronclient/neutron/v2_0/fw/firewallpolicy.py:179 +#, python-format +msgid "Inserted firewall rule in firewall policy %(id)s" +msgstr "" + +#: neutronclient/neutron/v2_0/fw/firewallpolicy.py:206 +msgid "Firewall rule to remove from policy." +msgstr "" + +#: neutronclient/neutron/v2_0/fw/firewallpolicy.py:218 +#, python-format +msgid "Removed firewall rule from firewall policy %(id)s" +msgstr "" + +#: neutronclient/neutron/v2_0/fw/firewallrule.py:74 +msgid "Name for the firewall rule." +msgstr "" + +#: neutronclient/neutron/v2_0/fw/firewallrule.py:82 +msgid "Set shared to True (default is False)." +msgstr "" + +#: neutronclient/neutron/v2_0/fw/firewallrule.py:86 +msgid "Source IP address or subnet." +msgstr "" + +#: neutronclient/neutron/v2_0/fw/firewallrule.py:89 +msgid "Destination IP address or subnet." +msgstr "" + +#: neutronclient/neutron/v2_0/fw/firewallrule.py:92 +msgid "Source port (integer in [1, 65535] or range in a:b)." +msgstr "" + +#: neutronclient/neutron/v2_0/fw/firewallrule.py:95 +msgid "Destination port (integer in [1, 65535] or range in a:b)." +msgstr "" + +#: neutronclient/neutron/v2_0/fw/firewallrule.py:99 +msgid "Whether to enable or disable this rule." +msgstr "" + +#: neutronclient/neutron/v2_0/fw/firewallrule.py:103 +#: neutronclient/neutron/v2_0/fw/firewallrule.py:133 +msgid "Protocol for the firewall rule." +msgstr "" + +#: neutronclient/neutron/v2_0/fw/firewallrule.py:108 +msgid "Action for the firewall rule." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/healthmonitor.py:51 +#: neutronclient/neutron/v2_0/lb/v2/healthmonitor.py:52 +msgid "" +"The list of HTTP status codes expected in response from the member to " +"declare it healthy. This attribute can contain one value, or a list of " +"values separated by comma, or a range of values (e.g. \"200-299\"). If " +"this attribute is not specified, it defaults to \"200\"." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/healthmonitor.py:59 +#: neutronclient/neutron/v2_0/lb/v2/healthmonitor.py:60 +msgid "The HTTP method used for requests by the monitor of type HTTP." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/healthmonitor.py:63 +#: neutronclient/neutron/v2_0/lb/v2/healthmonitor.py:64 +msgid "" +"The HTTP path used in the HTTP request used by the monitor to test a " +"member health. This must be a string beginning with a / (forward slash)." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/healthmonitor.py:69 +#: neutronclient/neutron/v2_0/lb/v2/healthmonitor.py:70 +msgid "The time in seconds between sending probes to members." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/healthmonitor.py:73 +msgid "" +"Number of permissible connection failures before changing the member " +"status to INACTIVE. [1..10]" +msgstr "" + +#: neutronclient/neutron/v2_0/lb/healthmonitor.py:78 +#: neutronclient/neutron/v2_0/lb/v2/healthmonitor.py:79 +msgid "" +"Maximum number of seconds for a monitor to wait for a connection to be " +"established before it times out. The value must be less than the delay " +"value." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/healthmonitor.py:84 +#: neutronclient/neutron/v2_0/lb/v2/healthmonitor.py:85 +msgid "One of the predefined health monitor types." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/healthmonitor.py:121 +#: neutronclient/neutron/v2_0/lb/healthmonitor.py:148 +msgid "Health monitor to associate." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/healthmonitor.py:124 +#: neutronclient/neutron/v2_0/lb/healthmonitor.py:151 +msgid "ID of the pool to be associated with the health monitor." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/healthmonitor.py:134 +#, python-format +msgid "Associated health monitor %s" +msgstr "" + +#: neutronclient/neutron/v2_0/lb/healthmonitor.py:162 +#, python-format +msgid "Disassociated health monitor %s" +msgstr "" + +#: neutronclient/neutron/v2_0/lb/member.py:51 +msgid "Weight of pool member in the pool (default:1, [0..256])." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/member.py:55 +msgid "IP address of the pool member on the pool network." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/member.py:59 +#: neutronclient/neutron/v2_0/lb/v2/member.py:90 +msgid "Port on which the pool member listens for requests or connections." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/member.py:63 +#: neutronclient/neutron/v2_0/lb/vip.py:45 +msgid "Pool ID or name this vip belongs to." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/pool.py:57 +#: neutronclient/neutron/v2_0/lb/v2/pool.py:63 +msgid "Description of the pool." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/pool.py:62 +#: neutronclient/neutron/v2_0/lb/v2/pool.py:75 +msgid "The algorithm used to distribute load between the members of the pool." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/pool.py:67 +#: neutronclient/neutron/v2_0/lb/v2/pool.py:70 +msgid "The name of the pool." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/pool.py:72 +#: neutronclient/neutron/v2_0/lb/vip.py:72 +#: neutronclient/neutron/v2_0/lb/v2/pool.py:85 +msgid "Protocol for balancing." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/pool.py:76 +msgid "The subnet on which the members of the pool will be located." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/pool.py:80 +msgid "Provider name of loadbalancer service." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/vip.py:48 +msgid "IP address of the vip." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/vip.py:55 +#: neutronclient/neutron/v2_0/lb/v2/listener.py:58 +msgid "" +"The maximum number of connections per second allowed for the vip. " +"Positive integer or -1 for unlimited (default)." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/vip.py:59 +msgid "Description of the vip." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/vip.py:63 +msgid "Name of the vip." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/vip.py:67 +msgid "" +"TCP port on which to listen for client traffic that is associated with " +"the vip address." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/vip.py:76 +msgid "The subnet on which to allocate the vip address." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/v2/healthmonitor.py:74 +msgid "" +"Number of permissible connection failures before changing the member " +"status to INACTIVE. [1..10]." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/v2/healthmonitor.py:88 +msgid "ID or name of the pool that this healthmonitor will monitor." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/v2/listener.py:62 +msgid "Description of the listener." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/v2/listener.py:65 +msgid "The name of the listener." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/v2/listener.py:69 +msgid "Default TLS container reference to retrieve TLS information." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/v2/listener.py:75 +msgid "List of TLS container references for SNI." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/v2/listener.py:80 +msgid "ID or name of the load balancer." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/v2/listener.py:85 +msgid "Protocol for the listener." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/v2/listener.py:90 +msgid "Protocol port for the listener." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/v2/loadbalancer.py:47 +msgid "Description of the load balancer." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/v2/loadbalancer.py:54 +msgid "Name of the load balancer." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/v2/loadbalancer.py:57 +msgid "Provider name of load balancer service." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/v2/loadbalancer.py:60 +msgid "ID or name of flavor." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/v2/loadbalancer.py:63 +msgid "VIP address for the load balancer." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/v2/loadbalancer.py:66 +msgid "Load balancer VIP subnet." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/v2/member.py:37 +#: neutronclient/neutron/v2_0/lb/v2/member.py:94 +msgid "ID or name of the pool that this member belongs to." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/v2/member.py:75 +#: neutronclient/neutron/v2_0/lb/v2/member.py:119 +msgid "Set admin state up to false" +msgstr "" + +#: neutronclient/neutron/v2_0/lb/v2/member.py:78 +msgid "Weight of member in the pool (default:1, [0..256])." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/v2/member.py:82 +msgid "Subnet ID or name for the member." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/v2/member.py:86 +msgid "IP address of the pool member in the pool." +msgstr "" + +#: neutronclient/neutron/v2_0/lb/v2/member.py:122 +msgid "Weight of member in the pool (default:1, [0..256])" +msgstr "" + +#: neutronclient/neutron/v2_0/lb/v2/member.py:125 +msgid "ID or name of the pool that this member belongs to" +msgstr "" + +#: neutronclient/neutron/v2_0/lb/v2/pool.py:67 +msgid "The type of session persistence to use and associated cookie name" +msgstr "" + +#: neutronclient/neutron/v2_0/lb/v2/pool.py:80 +msgid "The listener to associate with the pool" +msgstr "" + +#: neutronclient/neutron/v2_0/nsx/networkgateway.py:25 +msgid "" +"Type of the transport zone connector to use for this device. Valid values" +" are gre, stt, ipsec_gre, ipsec_stt, and bridge. Defaults to stt. " +"ipsecgre and ipsecstt are also accepted as alternative names" +msgstr "" + +#: neutronclient/neutron/v2_0/nsx/networkgateway.py:29 +msgid "" +"IP address for this device's transport connector. It must correspond to " +"the IP address of the interface used for tenant traffic on the NSX " +"gateway node." +msgstr "" + +#: neutronclient/neutron/v2_0/nsx/networkgateway.py:32 +msgid "" +"PEM certificate used by the NSX gateway transport node to authenticate " +"with the NSX controller." +msgstr "" + +#: neutronclient/neutron/v2_0/nsx/networkgateway.py:34 +msgid "" +"File containing the PEM certificate used by the NSX gateway transport " +"node to authenticate with the NSX controller." +msgstr "" + +#: neutronclient/neutron/v2_0/nsx/networkgateway.py:170 +msgid "Name of network gateway to create." +msgstr "" + +#: neutronclient/neutron/v2_0/nsx/networkgateway.py:174 +msgid "" +"Device info for this gateway. You can repeat this option for multiple " +"devices for HA gateways." +msgstr "" + +#: neutronclient/neutron/v2_0/nsx/networkgateway.py:212 +msgid "ID of the network gateway." +msgstr "" + +#: neutronclient/neutron/v2_0/nsx/networkgateway.py:215 +msgid "ID of the internal network to connect on the gateway." +msgstr "" + +#: neutronclient/neutron/v2_0/nsx/networkgateway.py:218 +msgid "" +"L2 segmentation strategy on the external side of the gateway (e.g.: VLAN," +" FLAT)." +msgstr "" + +#: neutronclient/neutron/v2_0/nsx/networkgateway.py:222 +msgid "Identifier for the L2 segment on the external side of the gateway." +msgstr "" + +#: neutronclient/neutron/v2_0/nsx/networkgateway.py:249 +#, python-format +msgid "Connected network to gateway %s" +msgstr "" + +#: neutronclient/neutron/v2_0/nsx/networkgateway.py:268 +#, python-format +msgid "Disconnected network from gateway %s" +msgstr "" + +#: neutronclient/neutron/v2_0/nsx/qos_queue.py:43 +msgid "Name of queue." +msgstr "" + +#: neutronclient/neutron/v2_0/nsx/qos_queue.py:46 +msgid "Minimum rate." +msgstr "" + +#: neutronclient/neutron/v2_0/nsx/qos_queue.py:49 +msgid "Maximum rate." +msgstr "" + +#: neutronclient/neutron/v2_0/nsx/qos_queue.py:52 +msgid "QOS marking as untrusted or trusted." +msgstr "" + +#: neutronclient/neutron/v2_0/nsx/qos_queue.py:56 +msgid "" +"If true all created ports will be the size of this queue, if queue is not" +" specified" +msgstr "" + +#: neutronclient/neutron/v2_0/nsx/qos_queue.py:60 +msgid "Differentiated Services Code Point." +msgstr "" + +#: neutronclient/neutron/v2_0/qos/bandwidth_limit_rule.py:30 +msgid "max bandwidth in kbps." +msgstr "" + +#: neutronclient/neutron/v2_0/qos/bandwidth_limit_rule.py:33 +msgid "max burst bandwidth in kbps." +msgstr "" + +#: neutronclient/neutron/v2_0/qos/bandwidth_limit_rule.py:40 +msgid "Must provide max_kbps or max_burst_kbps option." +msgstr "" + +#: neutronclient/neutron/v2_0/qos/policy.py:34 +msgid "Attach QoS policy ID or name to the resource." +msgstr "" + +#: neutronclient/neutron/v2_0/qos/policy.py:51 +msgid "Detach QoS policy from the resource." +msgstr "" + +#: neutronclient/neutron/v2_0/qos/policy.py:95 +msgid "Name of QoS policy to create." +msgstr "" + +#: neutronclient/neutron/v2_0/qos/policy.py:98 +#: neutronclient/neutron/v2_0/qos/policy.py:128 +msgid "Description of the QoS policy." +msgstr "" + +#: neutronclient/neutron/v2_0/qos/policy.py:102 +#: neutronclient/neutron/v2_0/qos/policy.py:132 +msgid "Accessible by other tenants. Set shared to True (default is False)." +msgstr "" + +#: neutronclient/neutron/v2_0/qos/policy.py:125 +msgid "Name of QoS policy." +msgstr "" + +#: neutronclient/neutron/v2_0/qos/rule.py:26 +msgid "ID or name of the QoS policy." +msgstr "" + +#: neutronclient/neutron/v2_0/qos/rule.py:32 +msgid "ID of the QoS rule." +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/endpoint_group.py:24 +msgid "Set a name for the endpoint group." +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/endpoint_group.py:27 +msgid "Set a description for the endpoint group." +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/endpoint_group.py:32 +msgid "Type of endpoints in group (e.g. subnet, cidr, vlan)." +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/endpoint_group.py:37 +msgid "Endpoint(s) for the group. Must all be of the same type." +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/ikepolicy.py:48 +msgid "Description of the IKE policy" +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/ikepolicy.py:52 +msgid "Authentication algorithm in lowercase. Default:sha1" +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/ikepolicy.py:57 +#: neutronclient/neutron/v2_0/vpn/ipsecpolicy.py:60 +msgid "Encryption algorithm in lowercase, default:aes-128" +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/ikepolicy.py:61 +msgid "IKE Phase1 negotiation mode in lowercase, default:main" +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/ikepolicy.py:65 +msgid "IKE version in lowercase, default:v1" +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/ikepolicy.py:69 +#: neutronclient/neutron/v2_0/vpn/ipsecpolicy.py:68 +msgid "Perfect Forward Secrecy in lowercase, default:group5" +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/ikepolicy.py:77 +msgid "Name of the IKE policy." +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:61 +msgid "Local endpoint group ID/name with subnet(s) for IPSec connection." +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:65 +msgid "Peer endpoint group ID/name with CIDR(s) for IPsec connection." +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:101 +msgid "Set friendly name for the connection." +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:104 +msgid "Set a description for the connection." +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:108 +msgid "MTU size for the connection, default:1500" +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:113 +msgid "Initiator state in lowercase, default:bi-directional" +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:117 +msgid "VPN service instance ID associated with this connection." +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:121 +msgid "IKE policy ID associated with this connection." +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:125 +msgid "IPsec policy ID associated with this connection." +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:129 +msgid "Peer gateway public IPv4/IPv6 address or FQDN." +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:133 +msgid "" +"Peer router identity for authentication. Can be IPv4/IPv6 address, e-mail" +" address, key id, or FQDN." +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:138 +msgid "" +"[DEPRECATED in Mitaka] Remote subnet(s) in CIDR format. Cannot be " +"specified when using endpoint groups. Only applicable, if subnet provided" +" for VPN service." +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:144 +msgid "Pre-shared key string." +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:158 +msgid "Invalid MTU value: MTU must be greater than or equal to 68" +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:163 +msgid "You must specify both local and peer endpoint groups." +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:167 +msgid "You cannot specify both endpoint groups and peer CIDR(s)." +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:171 +msgid "You must specify endpoint groups or peer CIDR(s)." +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/ipsecpolicy.py:48 +msgid "Description of the IPsec policy." +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/ipsecpolicy.py:52 +msgid "Transform protocol in lowercase, default:esp" +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/ipsecpolicy.py:56 +msgid "Authentication algorithm in lowercase, default:sha1" +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/ipsecpolicy.py:64 +msgid "Encapsulation mode in lowercase, default:tunnel" +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/ipsecpolicy.py:76 +msgid "Name of the IPsec policy." +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/utils.py:35 +#, python-format +msgid "" +"DPD Dictionary KeyError: Reason-Invalid DPD key : '%(key)s' not in " +"%(supported_key)s " +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/utils.py:42 +#, python-format +msgid "" +"DPD Dictionary ValueError: Reason-Invalid DPD action : '%(key_value)s' " +"not in %(supported_action)s " +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/utils.py:54 +#, python-format +msgid "" +"DPD Dictionary ValueError: Reason-Invalid positive integer value: " +"'%(key)s' = %(value)s " +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/utils.py:69 +#, python-format +msgid "" +"Lifetime Dictionary KeyError: Reason-Invalid unit key : '%(key)s' not in " +"%(supported_key)s " +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/utils.py:76 +#, python-format +msgid "" +"Lifetime Dictionary ValueError: Reason-Invalid units : '%(key_value)s' " +"not in %(supported_units)s " +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/utils.py:87 +#, python-format +msgid "" +"Lifetime Dictionary ValueError: Reason-Invalid value should be at least " +"60:'%(key_value)s' = %(value)s " +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/utils.py:99 +#, python-format +msgid "" +"%s lifetime attributes. 'units'-seconds, default:seconds. 'value'-non " +"negative integer, default:3600." +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/utils.py:106 +#, python-format +msgid "" +" %s Dead Peer Detection attributes. 'action'-hold,clear,disabled,restart" +",restart-by-peer. 'interval' and 'timeout' are non negative integers. " +"'interval' should be less than 'timeout' value. 'action', default:hold " +"'interval', default:30, 'timeout', default:120." +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/vpnservice.py:50 +msgid "Set a name for the VPN service." +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/vpnservice.py:53 +msgid "Set a description for the VPN service." +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/vpnservice.py:56 +msgid "Router unique identifier for the VPN service." +msgstr "" + +#: neutronclient/neutron/v2_0/vpn/vpnservice.py:59 +msgid "[DEPRECATED in Mitaka] Unique identifier for the local private subnet." +msgstr "" + +#: neutronclient/v2_0/client.py:228 +#, python-format +msgid "Unable to serialize object of type = '%s'" +msgstr "" + +#: neutronclient/v2_0/client.py:280 +#, python-format +msgid "Failed to connect to Neutron server after %d attempts" +msgstr "" + +#: neutronclient/v2_0/client.py:283 +msgid "Failed to connect Neutron server" +msgstr "" + diff --git a/setup.cfg b/setup.cfg index 0962bd079..7f11923dc 100644 --- a/setup.cfg +++ b/setup.cfg @@ -39,3 +39,17 @@ source-dir = doc/source [wheel] universal = 1 + +[extract_messages] +keywords = _ gettext ngettext l_ lazy_gettext +mapping_file = babel.cfg +output_file = python-neutronclient/locale/python-neutronclient.pot + +[compile_catalog] +directory = python-neutronclient/locale +domain = python-neutronclient + +[update_catalog] +domain = python-neutronclient +output_dir = python-neutronclient/locale +input_file = python-neutronclient/locale/python-neutronclient.pot From f5b4a27fb7931c7f1ff827bb3413effba5da6327 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Sat, 28 Nov 2015 06:08:21 +0900 Subject: [PATCH 305/845] Add more contents about CLI usage * Filtering in list operation * Changing displayed columns in list operation * Debug: display API level communication Change-Id: Ifba20559c76bf0c1b29f9ef3ac9def3882099956 --- doc/source/usage/cli.rst | 136 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) diff --git a/doc/source/usage/cli.rst b/doc/source/usage/cli.rst index ac6b66b56..a87e9d138 100644 --- a/doc/source/usage/cli.rst +++ b/doc/source/usage/cli.rst @@ -53,3 +53,139 @@ supply ``--os-auth-strategy`` or set the environment variable. .. code-block:: shell export OS_AUTH_STRATEGY=noauth + +Display options +--------------- + +Filtering +~~~~~~~~~ + +Neutron API supports filtering in the listing operation. +**neutron** CLI supports this feature too. + +To specify a filter in ``*-list`` command, you need to pass a pair of an +attribute name and an expected value with the format of ``-- ``. +The example below retrieves ports owned by compute instances. + +.. code-block:: console + + $ neutron port-list --device_owner network:dhcp + +--------------------------------------+------+-------------------+-------------------------------------------------------------------------------------------------------------+ + | id | name | mac_address | fixed_ips | + +--------------------------------------+------+-------------------+-------------------------------------------------------------------------------------------------------------+ + | 8953d683-29ad-4be3-b73f-060727c7849b | | fa:16:3e:4b:9e:0a | {"subnet_id": "6b832dfe-f271-443c-abad-629961414a73", "ip_address": "10.0.0.2"} | + | | | | {"subnet_id": "cdcc616b-0cff-482f-96f5-06fc63d21247", "ip_address": "fd12:877c:1d66:0:f816:3eff:fe4b:9e0a"} | + +--------------------------------------+------+-------------------+-------------------------------------------------------------------------------------------------------------+ + +You can also specify multiple filters. +The example below retrieves security group rules applied to IPv4 traffic +which belongs to a security group bfa493f9-2b03-46d2-8399-b9b038a53bc1. + +.. code-block:: console + + $ neutron security-group-rule-list --security-group-id bfa493f9-2b03-46d2-8399-b9b038a53bc1 --ethertype IPv4 + +--------------------------------------+----------------+-----------+-----------+---------------+-----------------+ + | id | security_group | direction | ethertype | protocol/port | remote | + +--------------------------------------+----------------+-----------+-----------+---------------+-----------------+ + | 65489805-0400-4bce-9bd9-16a81952263c | default | egress | IPv4 | any | any | + | 9429f336-4947-4643-bbd9-24528cc65648 | default | ingress | IPv4 | any | default (group) | + +--------------------------------------+----------------+-----------+-----------+---------------+-----------------+ + +.. note:: + + Looking up UUID from name is not supported when specifying a filter. + You need to use UUID to specify a specific resource. + +.. note:: + + Filtering for dictionary or list attributes is not supported. + +Changing displayed columns +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you want displayed columns in a list operation, ``-c`` option can be used. +``-c`` can be specified multiple times and the column order will be same as +the order of ``-c`` options. + +.. code-block:: console + + $ neutron port-list -c id -c device_owner -c fixed_ips + +--------------------------------------+--------------------------+-------------------------------------------------------------------------------------------------------------+ + | id | device_owner | fixed_ips | + +--------------------------------------+--------------------------+-------------------------------------------------------------------------------------------------------------+ + | 41ca1b9b-4bbd-4aa8-bcaa-31d3d5704205 | network:router_interface | {"subnet_id": "6b832dfe-f271-443c-abad-629961414a73", "ip_address": "10.0.0.1"} | + | 8953d683-29ad-4be3-b73f-060727c7849b | network:dhcp | {"subnet_id": "6b832dfe-f271-443c-abad-629961414a73", "ip_address": "10.0.0.2"} | + | | | {"subnet_id": "cdcc616b-0cff-482f-96f5-06fc63d21247", "ip_address": "fd12:877c:1d66:0:f816:3eff:fe4b:9e0a"} | + | a9da29f8-4504-4526-a5ce-cd3624fbd173 | neutron:LOADBALANCER | {"subnet_id": "6b832dfe-f271-443c-abad-629961414a73", "ip_address": "10.0.0.3"} | + | | | {"subnet_id": "cdcc616b-0cff-482f-96f5-06fc63d21247", "ip_address": "fd12:877c:1d66:0:f816:3eff:feb1:ab71"} | + | d6a1ff96-0a99-416f-a4d6-65d9614cf64e | compute:nova | {"subnet_id": "6b832dfe-f271-443c-abad-629961414a73", "ip_address": "10.0.0.4"} | + | | | {"subnet_id": "cdcc616b-0cff-482f-96f5-06fc63d21247", "ip_address": "fd12:877c:1d66:0:f816:3eff:fe2c:348e"} | + | f4789225-26d0-409f-8047-82d2c7a87a95 | network:router_interface | {"subnet_id": "cdcc616b-0cff-482f-96f5-06fc63d21247", "ip_address": "fd12:877c:1d66::1"} | + +--------------------------------------+--------------------------+-------------------------------------------------------------------------------------------------------------+ + +Extra argument mechanism +------------------------ + +[TODO: Write the extra argument mechanism. It is the most tricky area around +neutron CLI usage.] + +Debugging +--------- + +Display API-level communication +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``-v`` (or ``--verbose``, ``--debug``) option displays a detail interaction +with your neutron server. It is useful to debug what happens in the API level. + +Here is an sample output of ``net-show`` command. + +The first line show what parameters are recognized by neutronclient. +It is sometimes useful to check if command-line parameters you specify are recognized properly. + +.. code-block:: console + + $ neutron -v net-show mynetwork + DEBUG: neutronclient.neutron.v2_0.network.ShowNetwork get_data(Namespace(columns=[], fields=[], formatter='table', id=u'mynetwork', max_width=0, noindent=False, prefix='', request_format='json', show_details=False, variables=[])) + +Next, neutronclient sends an authentication request to keystone to get a token +which is used in further operations. + +.. code-block:: console + + DEBUG: keystoneauth.session REQ: curl -g -i -X GET http://172.16.18.47:5000 -H "Accept: application/json" -H "User-Agent: keystoneauth1" + DEBUG: keystoneauth.session RESP: [300] Content-Length: 593 Vary: X-Auth-Token Keep-Alive: timeout=5, max=100 Server: Apache/2.4.7 (Ubuntu) Connection: Keep-Alive Date: Fri, 27 Nov 2015 20:10:54 GMT Content-Type: application/json + RESP BODY: {"versions": {"values": [{"status": "stable", "updated": "2015-03-30T00:00:00Z", "media-types": [{"base": "application/json", "type": "application/vnd.openstack.identity-v3+json"}], "id": "v3.4", "links": [{"href": "http://172.16.18.47:5000/v3/", "rel": "self"}]}, {"status": "stable", "updated": "2014-04-17T00:00:00Z", "media-types": [{"base": "application/json", "type": "application/vnd.openstack.identity-v2.0+json"}], "id": "v2.0", "links": [{"href": "http://172.16.18.47:5000/v2.0/", "rel": "self"}, {"href": "http://docs.openstack.org/", "type": "text/html", "rel": "describedby"}]}]}} + + DEBUG: keystoneauth.identity.v3.base Making authentication request to http://172.16.18.47:5000/v3/auth/tokens + +Neutronclient looks up a network ID corresponding to a given network name. + +.. code-block:: console + + DEBUG: keystoneauth.session REQ: curl -g -i -X GET http://172.16.18.47:9696/v2.0/networks.json?fields=id&name=mynetwork -H "User-Agent: python-neutronclient" -H "Accept: application/json" -H "X-Auth-Token: {SHA1}39300e7398d53a02afd183f13cb6afaef95ec4e5" + DEBUG: keystoneauth.session RESP: [200] Date: Fri, 27 Nov 2015 20:10:55 GMT Connection: keep-alive Content-Type: application/json; charset=UTF-8 Content-Length: 62 X-Openstack-Request-Id: req-ccebf6e4-4f52-4874-a1ab-5499abcba378 + RESP BODY: {"networks": [{"id": "3698d3c7-d581-443e-bf86-53c4e3a738f7"}]} + +Finally, neutronclient retrieves a detail of a given network using the resolved ID. + +.. code-block:: console + + DEBUG: keystoneauth.session REQ: curl -g -i -X GET http://172.16.18.47:9696/v2.0/networks/3698d3c7-d581-443e-bf86-53c4e3a738f7.json -H "User-Agent: python-neutronclient" -H "Accept: application/json" -H "X-Auth-Token: {SHA1}39300e7398d53a02afd183f13cb6afaef95ec4e5" + DEBUG: keystoneauth.session RESP: [200] Date: Fri, 27 Nov 2015 20:10:55 GMT Connection: keep-alive Content-Type: application/json; charset=UTF-8 Content-Length: 272 X-Openstack-Request-Id: req-261add00-d6d3-4ea7-becc-105b60ac7369 + RESP BODY: {"network": {"status": "ACTIVE", "subnets": [], "name": "mynetwork", "admin_state_up": true, "tenant_id": "8f0ebf767043483a987736c8c684178d", "mtu": 0, "router:external": false, "shared": false, "port_security_enabled": true, "id": "3698d3c7-d581-443e-bf86-53c4e3a738f7"}} + + +-----------------------+--------------------------------------+ + | Field | Value | + +-----------------------+--------------------------------------+ + | admin_state_up | True | + | id | 3698d3c7-d581-443e-bf86-53c4e3a738f7 | + | mtu | 0 | + | name | mynetwork | + | port_security_enabled | True | + | router:external | False | + | shared | False | + | status | ACTIVE | + | subnets | | + | tenant_id | 8f0ebf767043483a987736c8c684178d | + +-----------------------+--------------------------------------+ From a2f2908c1648d332ad3d8cc2a94eb592a60a88e7 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Sat, 28 Nov 2015 06:18:50 +0900 Subject: [PATCH 306/845] Add os-client-config to CLI usage Change-Id: I5d4a3d7206c8f90f479047e8ee810496c06022d9 --- doc/source/usage/cli.rst | 44 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/doc/source/usage/cli.rst b/doc/source/usage/cli.rst index a87e9d138..801ebae2a 100644 --- a/doc/source/usage/cli.rst +++ b/doc/source/usage/cli.rst @@ -30,6 +30,50 @@ commands. All commands take the form of: Run **neutron help** to get a full list of all possible commands, and run **neutron help ** to get detailed help for that command. +Using with os-client-config +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`os-client-config `_ +provides more convenient way to manage a collection of client configurations +and you can easily switch multiple OpenStack-based configurations. + +To use os-client-config, you first need to prepare +``~/.config/openstack/clouds.yaml`` like the following. + +.. code-block:: yaml + + clouds: + devstack: + auth: + auth_url: http://auth.example.com:5000 + password: your-secret + project_domain_id: default + project_name: demo + user_domain_id: default + username: demo + identity_api_version: '3' + region_name: RegionOne + devstack-admin: + auth: + auth_url: http://auth.example.com:35357 + password: another-secret + project_domain_id: default + project_name: admin + user_domain_id: default + username: admin + identity_api_version: '3' + region_name: RegionOne + +Then, you need to specify a configuration name defined in the above clouds.yaml. + +.. code-block:: shell + + export OS_CLOUD=devstack + +For more detail information, see the +`os-client-config `_ +documentation. + Using with keystone token ~~~~~~~~~~~~~~~~~~~~~~~~~ From 1b97f4bcc3ddc616360c540c6e3c068d096f150e Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Sat, 28 Nov 2015 22:28:55 +0900 Subject: [PATCH 307/845] Add description of extra args in CLI Change-Id: Ia732d7ba82e3e5a5698a68be8e86eed87916f7da --- doc/source/usage/cli.rst | 142 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 138 insertions(+), 4 deletions(-) diff --git a/doc/source/usage/cli.rst b/doc/source/usage/cli.rst index 801ebae2a..6977321ca 100644 --- a/doc/source/usage/cli.rst +++ b/doc/source/usage/cli.rst @@ -167,11 +167,145 @@ the order of ``-c`` options. | f4789225-26d0-409f-8047-82d2c7a87a95 | network:router_interface | {"subnet_id": "cdcc616b-0cff-482f-96f5-06fc63d21247", "ip_address": "fd12:877c:1d66::1"} | +--------------------------------------+--------------------------+-------------------------------------------------------------------------------------------------------------+ -Extra argument mechanism ------------------------- +.. _cli_extra_arguments: -[TODO: Write the extra argument mechanism. It is the most tricky area around -neutron CLI usage.] +Extra arguments for create/update operation +------------------------------------------- + +**neutron** CLI has a mechanism called the *extra arguments* for ``*-create`` +and ``*-update`` commands. It allows users to specify a set of *unknown +options* which are not defined as options and not shown in the help text. +**Unknown options MUST be placed at the end of the command line.** +*unknown options* will be directly passed to the API layer. By this mechanism, +you can pass an attribute which is not defined in the upstream **neutron** +CLI. For example, when you are developing a new feature which add a new +attribute to an existing resource, it is useful because we can test your +feature without changing the existing neutron CLI. + +For example, if you run the following command:: + + neutron resource-update --key1 value1 --key2 value2 + +where ``resource`` is some resource name and ``--key1`` and ``--key2`` are +unknown options, then the following JSON will be sent to the neutron API:: + + PUT /v2.0/resources/ + + { + "resource": { + "key2": "value2", + "key1": "value1" + } + } + +Key interpretation +~~~~~~~~~~~~~~~~~~ + +This means an option name (``--key1`` in this case) must be one of valid +resources of a corresponding resource. An option name ``--foo_bar`` is +recognized as an attribute name ``foo_bar``. ``--foo-bar`` is also interpreted +as an attribute name ``foo_bar``. + +Value interpretation +~~~~~~~~~~~~~~~~~~~~ + +By default, if the number of values is 1, the option value is interpreted as a +string and is passed to the API layer as specified in a command-line. + +If the number of values is greater than 1, the option value is interpreted as a +list and the result in the API layer will be same as when specifying a list as +described below. + + neutron resource-update --key1 val1 val2 val3 --key2 val4 + +In the above example, a value of ``key1`` is interpreted as +``["val1", "val2", "val3"]`` and a value of ``key2`` is interpreted +as ``val4``. + +The extra argument mechanism supports more complex value like a list or a dict. + +Specify a list value +++++++++++++++++++++ + +A command-line:: + + neutron resource-update --key list=true val1 val2 val3 + +will send the following in the API layer:: + + { + "key": [ + "val1", + "val2", + "val3" + ] + } + +.. note:: + + If you want to specify a list value, it is recommended to specify + ``list=true``. When ``list=true`` is specified, specified values are + interpreted as a list even regardless of the number of values. + + If ``list=true`` is not specified, specified values are interpreted + depends on the number of values how. If the number of values is more than 2, + the specified values are interpreted as a list. If 1, the value + is interpreted as a string. + +Specify a dict value +++++++++++++++++++++ + +A command-line:: + + neutron resource-update --key type=dict key1=val1,key2=val2,key3=val3 + +will send the following in the API layer:: + + { + "key": { + "key1": "val1" + "key2": "val2", + "key3": "val3", + } + } + +.. note:: + + ``type=bool True/False`` and ``type=int 10`` are also supported. + +Specify a list of dicts ++++++++++++++++++++++++ + +A command-line:: + + neutron resource-update --key type=dict list=true key1=val1 key2=val2 key3=val3 + +will send the following in the API layer:: + + { + "key": [ + {"key1": "val1"}, + {"key2": "val2"}, + {"key3": "val3"} + ] + } + +Passing None as a value +~~~~~~~~~~~~~~~~~~~~~~~ + +There is a case where we would like to pass ``None`` (``null`` in JSON) +in the API layer. To do this:: + + neutron resource-update --key action=clear + +The following body will be in the API layer:: + + {"key": null} + +.. note:: + + If ``action=clear`` is specified, ``list=true`` or ``type=dict`` is ignored. + It means when ``action=clear`` is specified ``None`` is always sent. Debugging --------- From b05d94377c2ee04523960fde5422a755bcea9bc5 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Sun, 29 Nov 2015 06:18:33 +0900 Subject: [PATCH 308/845] Add CLI option guideline A lot of changes have been proposed to add options to *-create/update operations. This guideline tries to clarify the conventions used in neutornclient. Change-Id: I2c66c3dcba2569fdac2e54afb49406084cbf7037 --- doc/source/devref/cli_option_guideline.rst | 246 +++++++++++++++++++++ doc/source/index.rst | 1 + 2 files changed, 247 insertions(+) create mode 100644 doc/source/devref/cli_option_guideline.rst diff --git a/doc/source/devref/cli_option_guideline.rst b/doc/source/devref/cli_option_guideline.rst new file mode 100644 index 000000000..0d51ef68a --- /dev/null +++ b/doc/source/devref/cli_option_guideline.rst @@ -0,0 +1,246 @@ +==================== +CLI Option Guideline +==================== + +This document describes the conventions of neutron CLI options. + +General conventions +------------------- + +#. Option names should be delimited by a hyphen instead of a underscore. + This is the common guidelines across all OpenStack CLIs. + + * Good: ``--ip-version`` + * Not Good: ``--ip_version`` + +#. Use at least one required option for ``*-create`` command. If all options + are optional, we typically use ``name`` field as a required option. + +#. When you need to specify an ID of a resource, it is better to provide + another way to specify the resource like ``name`` or other reasonable field. + +#. If an attribute name in API is ``foo_id``, the corresponding option + should be ``--foo`` instead of ``--foo-id``. + + * It is because we usually support ID and ``name`` to specify a resource. + +#. Do not use ``nargs='?'`` without a special reason. + + * The behavior of ``nargs='?'`` of python argparse is a bit tricky + and it sometimes leads to unexpected option parsing which is + different from the help message. The detail is described + in :ref:`the Background section ` below. + +#. (option) Avoid using positional options as much as possible. + + * Positional arguments should be limited to attributes which will + be required in the long future. + +#. We honor existing options and should keep compatibilities when adding or + changing options. + +Options for boolean value +------------------------- + +Use the form of ``--option-name {True|False}``. + +* For a new option, it is recommended. +* It is suggested to use ``common.utils.add_boolean_argument`` in an + implementation. It allows ``true``/``false`` in addition to ``True``/``False``. +* For existing options, migration to the recommended form is not necessarily + required. All backward-compatibility should be kept without reasonable + reasons. + +Options for dict value +---------------------- + +Some API attributes take a dictionary. + +``--foo key1=val1,key2=val2`` is usually used. + +This means ``{"key1": "val1", "key2": "val2"}`` is passed in the API layer. + +Examples: + +* ``--host-route destination=CIDR,nexthop=IP_ADDR`` for a subnet +* ``--fixed-ip subnet_id=SUBNET,ip_address=IP_ADDR`` for a port. + +Options for list value +---------------------- + +Some attributes take a list. + +In this case, we usually use: + +* Define an option per element (Use a singular form as an option name) +* Allow to specify the option multiple times + +For Example, **port-create** has ``--security-group`` option. +``--security-group SG1 --security-group SG2`` generates +``{"security_groups: ["SG1", "SG2"]}`` in the API layer. + +This convention applies to a case of a list of dict. +``--allocation-pool`` and ``--host-route`` for a subnet are examples. + +Compatibility with extra arguments +---------------------------------- + +*extra arguments* supports various types of option specifications. +At least the following patterns needs to be considered when defining +a new option. For more detail, see :ref:`cli_extra_arguments`. + +* Normal options with value +* Boolean options : ``--foo True``, ``--bar=False`` +* List options : ``--bars list=true val1 val2``, ``--bars val1 val2`` +* Dict options : ``--foo type=dict key1=va1,key2=val2`` +* List of Dict options : ``--bars list=true type=dict key1=val1,key2=val2 key3=val3,key4=val4`` +* ``action=clear`` + +For normal options with value, there are four patterns to specify an option +as extra arguments. + +* ``--admin-state-up True`` (a space between option name and value) +* ``--admin-state-up=True`` (= between option name and value) +* ``--admin_state_up True`` (underscore is used as delimiter) +* ``--admin_state_up=True`` (underscore is used as delimiter) + +.. _background: + +Background +---------- + +There are a lot of opinions on which form of options are better or not. +This section tries to capture the reason of the current choice. + +Use at least one required option +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As a convention, **neutron** CLI requires one required argument. + +If all options are optional in the API level and we have ``name`` field, +we usually use ``name`` as a required parameter. +Requiring at least one argument has the following benefits: + +* If we run ``neutron *-create`` without a required argument, we will have a + brief help message without detail option help. It is convenient. +* We can avoid miss operation by just hitting ``neutron *-create``. + Requiring at least one parameter is a good balance. + +Even though we can change this convention to allow to create a resource +without ``name`` field, it will bring confusions to existing users. + +There may be opinion that it is inconsistent with API level requirement +or Horizon behavior, but even if neutron CLI requires ``name`` field +there is no bad impact on regular users. Considering possible confusion +if we change it, it looks better to keep it as-is. + +Options for Boolean value +~~~~~~~~~~~~~~~~~~~~~~~~~ + +* ``--enable-foo``/``--disable-foo`` or similar patterns (including + ``--admin-state-down``) is not suggested because we need two exclusive + options for one attribute in REST API. It is meaningless. + +* It is not recommended to have an option only to specify non-default value. + For example, we have ``--shared`` or ``--admin-state-down`` options for + net-create. This form only works for ``*-create`` and does not work for + ``*-update``. It leads to having different options for ``*-create`` and + ``*-update``. + +* A flag option like ``--enable-dhcp`` (without value) also has a problem when + considering the compatibility with *extra argument*. We can specify + ``-enable-dhcp True/False`` or ``--enable-dhcp=True/False`` in the *extra + argument* mechanism. If we introduce ``--enable-dhcp`` (without value), + the form of ``-enable-dhcp True/False`` cannot be used now. + This is another reason we don't use a flag style option for a boolean parameter. + +.. _background-nargs: + +Avoid using positional options +------------------------------ + +The behavior of ``nargs='?'`` of python argparse is a bit tricky. +When we use ``nargs='?'``, if the order of command-line options is +swapped a bit, a command-line parser fails to parse the options easily. + +There are two examples of such failures. + +Example 1 shows that an actual behavior is different from +a way shown in a help message. The help message at ``[5]`` +shows we can use ``--bb CC``, but as you see at ``[7]`` +the arguent parsing fails. + +Example 1: + +.. code-block:: console + + In [1]: import argparse + In [2]: parser = argparse.ArgumentParser() + In [3]: parser.add_argument('--bb', nargs='?') + In [4]: parser.add_argument('cc') + + In [5]: parser.print_help() + usage: ipython [-h] [--bb [BB]] cc + + positional arguments: + cc + + optional arguments: + -h, --help show this help message and exit + --bb [BB] + + In [6]: parser.parse_args('--bb 1 X'.split()) + Out[6]: Namespace(bb='1', cc='X') + + In [7]: parser.parse_args('--bb X'.split()) + usage: ipython [-h] [--bb [BB]] cc + ipython: error: too few arguments + An exception has occurred, use %tb to see the full traceback. + + SystemExit: 2 + + +The second example shows how fragile ``nargs='?'`` is when a user +specifies options in a way different from the help message. +Most CLI usesr do not care the the order of command-line options, +so this fragile behavior should be avoided. + +Example 2: + +.. code-block:: console + + In [1]: import argparse + In [2]: parser = argparse.ArgumentParser() + In [3]: parser.add_argument('--a', help='option a') + In [4]: parser.add_argument('--b', help='option b') + In [5]: parser.add_argument('x', help='positional arg X') + In [6]: parser.add_argument('y', nargs='?', help='positional arg Y') + In [7]: parser.print_help() + usage: ipython [-h] [--a A] [--b B] x [y] + + positional arguments: + x positional arg X + y positional arg Y + + optional arguments: + -h, --help show this help message and exit + --a A option a + --b B option b + + In [8]: parser.parse_args('--a 1 --b 2 X Y'.split()) + Out[8]: Namespace(a='1', b='2', x='X', y='Y') + + In [9]: parser.parse_args('X Y --a 1 --b 2'.split()) + Out[9]: Namespace(a='1', b='2', x='X', y='Y') + + In [10]: parser.parse_args('X --a 1 --b 2 Y'.split()) + usage: ipython [-h] [--a A] [--b B] x [y] + ipython: error: unrecognized arguments: Y + An exception has occurred, use %tb to see the full traceback. + + SystemExit: 2 + + To exit: use 'exit', 'quit', or Ctrl-D. + To exit: use 'exit', 'quit', or Ctrl-D. + + diff --git a/doc/source/index.rst b/doc/source/index.rst index fa240033a..1d604310f 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -26,6 +26,7 @@ lower level programming details or APIs. :maxdepth: 2 devref/client_command_extensions + devref/cli_option_guideline History ------- From a704812b4d2114ea457ba3bec64683fbe129b2a2 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Sat, 5 Dec 2015 01:26:16 +0000 Subject: [PATCH 309/845] Updated from global requirements Change-Id: I1b8e4c8fb28c8417cab276dbba911c68cfbd45a8 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e902f01b6..de1c6e102 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ oslo.i18n>=1.5.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 oslo.utils>=2.8.0 # Apache-2.0 os-client-config!=1.6.2,>=1.4.0 -keystoneauth1>=1.0.0 +keystoneauth1>=2.0.0 requests>=2.8.1 simplejson>=2.2.0 six>=1.9.0 From f2cff4fdbd09c6211772a4d7f25e77a70d0a4c29 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Sun, 6 Dec 2015 17:07:27 +0900 Subject: [PATCH 310/845] Remove extra space from VPN validator exception messages Change-Id: I544bd7ba33bc187fee03b0ae6cd12f9b6c522dca --- neutronclient/neutron/v2_0/vpn/utils.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/neutronclient/neutron/v2_0/vpn/utils.py b/neutronclient/neutron/v2_0/vpn/utils.py index 0159e76b9..ab6760793 100644 --- a/neutronclient/neutron/v2_0/vpn/utils.py +++ b/neutronclient/neutron/v2_0/vpn/utils.py @@ -35,14 +35,14 @@ def validate_dpd_dict(dpd_dict): message = _( "DPD Dictionary KeyError: " "Reason-Invalid DPD key : " - "'%(key)s' not in %(supported_key)s ") % { + "'%(key)s' not in %(supported_key)s") % { 'key': key, 'supported_key': dpd_supported_keys} raise exceptions.CommandError(message) if key == 'action' and value not in dpd_supported_actions: message = _( "DPD Dictionary ValueError: " "Reason-Invalid DPD action : " - "'%(key_value)s' not in %(supported_action)s ") % { + "'%(key_value)s' not in %(supported_action)s") % { 'key_value': value, 'supported_action': dpd_supported_actions} raise exceptions.CommandError(message) @@ -54,7 +54,7 @@ def validate_dpd_dict(dpd_dict): message = _( "DPD Dictionary ValueError: " "Reason-Invalid positive integer value: " - "'%(key)s' = %(value)s ") % { + "'%(key)s' = %(value)s") % { 'key': key, 'value': value} raise exceptions.CommandError(message) else: @@ -69,14 +69,14 @@ def validate_lifetime_dict(lifetime_dict): message = _( "Lifetime Dictionary KeyError: " "Reason-Invalid unit key : " - "'%(key)s' not in %(supported_key)s ") % { + "'%(key)s' not in %(supported_key)s") % { 'key': key, 'supported_key': lifetime_keys} raise exceptions.CommandError(message) if key == 'units' and value not in lifetime_units: message = _( "Lifetime Dictionary ValueError: " "Reason-Invalid units : " - "'%(key_value)s' not in %(supported_units)s ") % { + "'%(key_value)s' not in %(supported_units)s") % { 'key_value': key, 'supported_units': lifetime_units} raise exceptions.CommandError(message) if key == 'value': @@ -87,7 +87,7 @@ def validate_lifetime_dict(lifetime_dict): message = _( "Lifetime Dictionary ValueError: " "Reason-Invalid value should be at least 60:" - "'%(key_value)s' = %(value)s ") % { + "'%(key_value)s' = %(value)s") % { 'key_value': key, 'value': value} raise exceptions.CommandError(message) else: From 88010add39f755ea4c2b4aa54ac6e66f48b1af51 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Sun, 6 Dec 2015 20:47:32 +0000 Subject: [PATCH 311/845] Updated from global requirements Change-Id: Ib6d9529b0ed5cfa844d848fc0e81255d21328f15 --- requirements.txt | 2 +- test-requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index de1c6e102..4575da343 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ oslo.i18n>=1.5.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 oslo.utils>=2.8.0 # Apache-2.0 os-client-config!=1.6.2,>=1.4.0 -keystoneauth1>=2.0.0 +keystoneauth1>=2.1.0 requests>=2.8.1 simplejson>=2.2.0 six>=1.9.0 diff --git a/test-requirements.txt b/test-requirements.txt index 62eb0e8c6..ab0266d8a 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -16,4 +16,4 @@ requests-mock>=0.7.0 # Apache-2.0 sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 testrepository>=0.0.18 testtools>=1.4.0 -tempest-lib>=0.10.0 +tempest-lib>=0.11.0 From 709928b5503f8899ddb7bc772f23432212284082 Mon Sep 17 00:00:00 2001 From: Hirofumi Ichihara Date: Tue, 8 Dec 2015 22:36:58 +0900 Subject: [PATCH 312/845] Add availability_zone CLI This patch adds the availability_zone support for CLI. Change-Id: I13faf6d90e84f401f07376c517c643ba9eda2295 Co-Authored-By: IWAMOTO Toshihiro Partially-implements: blueprint add-availability-zone --- .../neutron/v2_0/availability_zone.py | 38 +++++++++++++++++++ neutronclient/neutron/v2_0/network.py | 3 ++ neutronclient/neutron/v2_0/router.py | 4 ++ neutronclient/shell.py | 2 + neutronclient/tests/unit/test_cli20_az.py | 31 +++++++++++++++ .../tests/unit/test_cli20_network.py | 15 ++++++++ neutronclient/tests/unit/test_cli20_router.py | 15 ++++++++ neutronclient/v2_0/client.py | 7 ++++ 8 files changed, 115 insertions(+) create mode 100644 neutronclient/neutron/v2_0/availability_zone.py create mode 100644 neutronclient/tests/unit/test_cli20_az.py diff --git a/neutronclient/neutron/v2_0/availability_zone.py b/neutronclient/neutron/v2_0/availability_zone.py new file mode 100644 index 000000000..761a50b19 --- /dev/null +++ b/neutronclient/neutron/v2_0/availability_zone.py @@ -0,0 +1,38 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from neutronclient.i18n import _ +from neutronclient.neutron import v2_0 as neutronv20 + + +def add_az_hint_argument(parser, resource): + parser.add_argument( + '--availability-zone-hint', metavar='AVAILABILITY_ZONE', + action='append', dest='availability_zone_hints', + help=_('Availability Zone for the %s ' + '(requires availability zone extension, ' + 'this option can be repeated).') % resource) + + +def args2body_az_hint(parsed_args, resource): + if parsed_args.availability_zone_hints: + resource['availability_zone_hints'] = ( + parsed_args.availability_zone_hints) + + +class ListAvailabilityZone(neutronv20.ListCommand): + """List availability zones.""" + + resource = 'availability_zone' + list_columns = ['name', 'resource', 'state'] + pagination_support = True + sorting_support = True diff --git a/neutronclient/neutron/v2_0/network.py b/neutronclient/neutron/v2_0/network.py index 4b5674437..04aa9a707 100644 --- a/neutronclient/neutron/v2_0/network.py +++ b/neutronclient/neutron/v2_0/network.py @@ -20,6 +20,7 @@ from neutronclient.common import utils from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 +from neutronclient.neutron.v2_0 import availability_zone from neutronclient.neutron.v2_0.qos import policy as qos_policy @@ -146,6 +147,7 @@ def add_known_arguments(self, parser): help=_('Name of network to create.')) self.add_arguments_qos_policy(parser) + availability_zone.add_az_hint_argument(parser, self.resource) def args2body(self, parsed_args): body = {'name': parsed_args.name, @@ -158,6 +160,7 @@ def args2body(self, parsed_args): 'provider:segmentation_id']) self.args2body_qos_policy(parsed_args, body) + availability_zone.args2body_az_hint(parsed_args, body) return {'network': body} diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index 4c9a83c26..ddae9bbef 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -24,6 +24,7 @@ from neutronclient.common import utils from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 +from neutronclient.neutron.v2_0 import availability_zone def _format_external_gateway_info(router): @@ -74,10 +75,13 @@ def add_known_arguments(self, parser): parser, '--ha', dest='ha', help=_('Create a highly available router.')) + availability_zone.add_az_hint_argument(parser, self.resource) + def args2body(self, parsed_args): body = {'admin_state_up': parsed_args.admin_state} neutronV20.update_dict(parsed_args, body, ['name', 'tenant_id', 'distributed', 'ha']) + availability_zone.args2body_az_hint(parsed_args, body) return {self.resource: body} diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 09b7c4c9e..fe8bd97c1 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -43,6 +43,7 @@ from neutronclient.neutron.v2_0 import address_scope from neutronclient.neutron.v2_0 import agent from neutronclient.neutron.v2_0 import agentscheduler +from neutronclient.neutron.v2_0 import availability_zone from neutronclient.neutron.v2_0 import extension from neutronclient.neutron.v2_0.flavor import flavor from neutronclient.neutron.v2_0.flavor import flavor_profile @@ -390,6 +391,7 @@ class BashCompletionCommand(openstack_command.OpenStackCommand): 'flavor-profile-create': flavor_profile.CreateFlavorProfile, 'flavor-profile-delete': flavor_profile.DeleteFlavorProfile, 'flavor-profile-update': flavor_profile.UpdateFlavorProfile, + 'availability-zone-list': availability_zone.ListAvailabilityZone, } COMMANDS = {'2.0': COMMAND_V2} diff --git a/neutronclient/tests/unit/test_cli20_az.py b/neutronclient/tests/unit/test_cli20_az.py new file mode 100644 index 000000000..cb127c529 --- /dev/null +++ b/neutronclient/tests/unit/test_cli20_az.py @@ -0,0 +1,31 @@ +# 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 sys + +from neutronclient.neutron.v2_0 import availability_zone as az +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20Agent(test_cli20.CLITestV20Base): + def test_list_agents(self): + contents = {'availability_zones': [{'name': 'zone1', + 'resource': 'network', + 'state': 'available'}, + {'name': 'zone2', + 'resource': 'router', + 'state': 'unavailable'}]} + args = ['-f', 'json'] + resources = "availability_zones" + + cmd = az.ListAvailabilityZone(test_cli20.MyApp(sys.stdout), None) + self._test_list_columns(cmd, resources, contents, args) diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index dc5ba1e8b..1e51a8844 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -149,6 +149,21 @@ def test_create_network_with_qos_policy(self): self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) + def test_create_network_with_az_hint(self): + """Create net: --availability-zone-hint zone1 + --availability-zone-hint zone2. + """ + resource = 'network' + cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + args = ['--availability-zone-hint', 'zone1', + '--availability-zone-hint', 'zone2', name] + position_names = ['availability_zone_hints', 'name'] + position_values = [['zone1', 'zone2'], name] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + def test_list_nets_empty_with_column(self): resources = "networks" cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) diff --git a/neutronclient/tests/unit/test_cli20_router.py b/neutronclient/tests/unit/test_cli20_router.py index 60ee733bd..35862ec5a 100644 --- a/neutronclient/tests/unit/test_cli20_router.py +++ b/neutronclient/tests/unit/test_cli20_router.py @@ -111,6 +111,21 @@ def test_create_router_distributed_false(self): """Create router: --distributed=false.""" self._create_router_distributed_or_ha(distributed='false') + def test_create_router_with_az_hint(self): + """Create router: --availability-zone-hint zone1 + --availability-zone-hint zone2. + """ + resource = 'router' + cmd = router.CreateRouter(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + args = ['--availability-zone-hint', 'zone1', + '--availability-zone-hint', 'zone2', name] + position_names = ['availability_zone_hints', 'name'] + position_values = [['zone1', 'zone2'], name] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + def test_list_routers_detail(self): """list routers: -D.""" resources = "routers" diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index da8a9a48d..3d71a7f6b 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -435,6 +435,7 @@ class Client(ClientBase): service_profile_path = "/service_profiles/%s" flavor_profile_bindings_path = flavor_path + service_profiles_path flavor_profile_binding_path = flavor_path + service_profile_path + availability_zones_path = "/availability_zones" # API has no way to report plurals, so we have to hard code them EXTED_PLURALS = {'routers': 'router', @@ -1729,6 +1730,12 @@ def update_service_profile(self, service_profile, body): return self.put(self.service_profile_path % (service_profile), body=body) + @APIParamsCall + def list_availability_zones(self, retrieve_all=True, **_params): + """Fetches a list of all availability zones.""" + return self.list('availability_zones', self.availability_zones_path, + retrieve_all, **_params) + def __init__(self, **kwargs): """Initialize a new client for the Neutron v2.0 API.""" super(Client, self).__init__(**kwargs) From 54a4aea969fe06422f64005652ce23ac7b616d98 Mon Sep 17 00:00:00 2001 From: Elena Ezhova Date: Fri, 4 Dec 2015 16:45:47 +0300 Subject: [PATCH 313/845] Remove XML support As XML support has been removed from neutron, there is no need to keep it in client. Dropped XML serializer and deserializer and deprecated "request-format" CLI option, making JSON the default and the only supported request format. DocImpact Change-Id: I88b0fdd65a649694252d5ff43a174e75026df5b1 Closes-Bug: #1380787 --- neutron_test.sh | 100 +++--- neutronclient/common/constants.py | 12 - neutronclient/common/serializer.py | 287 +----------------- neutronclient/neutron/v2_0/__init__.py | 11 +- neutronclient/neutron/v2_0/agentscheduler.py | 4 - neutronclient/neutron/v2_0/flavor/flavor.py | 2 - neutronclient/neutron/v2_0/floatingip.py | 2 - .../neutron/v2_0/fw/firewallpolicy.py | 2 - .../neutron/v2_0/lb/healthmonitor.py | 2 - neutronclient/neutron/v2_0/lb/pool.py | 1 - neutronclient/neutron/v2_0/metering.py | 1 - .../neutron/v2_0/nsx/networkgateway.py | 2 - neutronclient/neutron/v2_0/port.py | 1 - neutronclient/neutron/v2_0/quota.py | 4 - neutronclient/neutron/v2_0/rbac.py | 1 - neutronclient/neutron/v2_0/router.py | 3 - .../tests/unit/fw/test_cli20_firewall.py | 4 - .../unit/fw/test_cli20_firewallpolicy.py | 6 - .../tests/unit/fw/test_cli20_firewallrule.py | 4 - .../tests/unit/lb/test_cli20_healthmonitor.py | 4 - .../tests/unit/lb/test_cli20_member.py | 4 - .../tests/unit/lb/test_cli20_pool.py | 4 - neutronclient/tests/unit/lb/test_cli20_vip.py | 4 - .../unit/lb/v2/test_cli20_healthmonitor.py | 4 - .../tests/unit/lb/v2/test_cli20_listener.py | 4 - .../unit/lb/v2/test_cli20_loadbalancer.py | 4 - .../tests/unit/lb/v2/test_cli20_member.py | 4 - .../tests/unit/lb/v2/test_cli20_pool.py | 4 - neutronclient/tests/unit/test_cli20.py | 42 +-- .../tests/unit/test_cli20_agentschedulers.py | 4 - .../tests/unit/test_cli20_floatingips.py | 4 - .../tests/unit/test_cli20_metering.py | 4 - .../tests/unit/test_cli20_network.py | 4 - .../unit/test_cli20_nsx_networkgateway.py | 4 - .../tests/unit/test_cli20_nsx_queue.py | 4 - .../unit/test_cli20_nuage_netpartition.py | 4 - neutronclient/tests/unit/test_cli20_port.py | 4 - neutronclient/tests/unit/test_cli20_router.py | 4 - .../tests/unit/test_cli20_securitygroup.py | 4 - .../tests/unit/test_cli20_servicetype.py | 4 - neutronclient/tests/unit/test_cli20_subnet.py | 35 +-- neutronclient/tests/unit/test_http.py | 14 +- .../tests/unit/vpn/test_cli20_ikepolicy.py | 4 - .../tests/unit/vpn/test_cli20_ipsecpolicy.py | 4 - neutronclient/v2_0/client.py | 37 +-- .../drop-xml-support-41babecb1784d996.yaml | 5 + 46 files changed, 82 insertions(+), 588 deletions(-) create mode 100644 releasenotes/notes/drop-xml-support-41babecb1784d996.yaml diff --git a/neutron_test.sh b/neutron_test.sh index ee0af2abc..d6dc75409 100755 --- a/neutron_test.sh +++ b/neutron_test.sh @@ -28,12 +28,10 @@ fi echo "NOTE: User should be admin in order to perform all operations." sleep 3 -FORMAT=" --request-format json" - # test the CRUD of network network=$net_name -neutron net-create $FORMAT $NOAUTH $network || die "fail to create network $network" -temp=`neutron net-list $FORMAT -- --name $network --fields id | wc -l` +neutron net-create $NOAUTH $network || die "fail to create network $network" +temp=`neutron net-list -- --name $network --fields id | wc -l` echo $temp if [ $temp -ne 5 ]; then die "networks with name $network is not unique or found" @@ -41,66 +39,66 @@ fi network_id=`neutron net-list -- --name $network --fields id | tail -n 2 | head -n 1 | cut -d' ' -f 2` echo "ID of network with name $network is $network_id" -neutron net-show $FORMAT $network || die "fail to show network $network" -neutron net-show $FORMAT $network_id || die "fail to show network $network_id" +neutron net-show $network || die "fail to show network $network" +neutron net-show $network_id || die "fail to show network $network_id" -neutron net-update $FORMAT $network --admin_state_up False || die "fail to update network $network" -neutron net-update $FORMAT $network_id --admin_state_up True || die "fail to update network $network_id" +neutron net-update $network --admin_state_up False || die "fail to update network $network" +neutron net-update $network_id --admin_state_up True || die "fail to update network $network_id" -neutron net-list $FORMAT -c id -- --id fakeid || die "fail to list networks with column selection on empty list" +neutron net-list -c id -- --id fakeid || die "fail to list networks with column selection on empty list" # test the CRUD of subnet subnet=$subnet_name cidr=10.0.1.0/24 -neutron subnet-create $FORMAT $NOAUTH $network $cidr --name $subnet || die "fail to create subnet $subnet" -tempsubnet=`neutron subnet-list $FORMAT -- --name $subnet --fields id | wc -l` +neutron subnet-create $NOAUTH $network $cidr --name $subnet || die "fail to create subnet $subnet" +tempsubnet=`neutron subnet-list -- --name $subnet --fields id | wc -l` echo $tempsubnet if [ $tempsubnet -ne 5 ]; then die "subnets with name $subnet is not unique or found" fi -subnet_id=`neutron subnet-list $FORMAT -- --name $subnet --fields id | tail -n 2 | head -n 1 | cut -d' ' -f 2` +subnet_id=`neutron subnet-list -- --name $subnet --fields id | tail -n 2 | head -n 1 | cut -d' ' -f 2` echo "ID of subnet with name $subnet is $subnet_id" -neutron subnet-show $FORMAT $subnet || die "fail to show subnet $subnet" -neutron subnet-show $FORMAT $subnet_id || die "fail to show subnet $subnet_id" +neutron subnet-show $subnet || die "fail to show subnet $subnet" +neutron subnet-show $subnet_id || die "fail to show subnet $subnet_id" -neutron subnet-update $FORMAT $subnet --dns_nameservers list=true 1.1.1.11 1.1.1.12 || die "fail to update subnet $subnet" -neutron subnet-update $FORMAT $subnet_id --dns_nameservers list=true 2.2.2.21 2.2.2.22 || die "fail to update subnet $subnet_id" +neutron subnet-update $subnet --dns_nameservers list=true 1.1.1.11 1.1.1.12 || die "fail to update subnet $subnet" +neutron subnet-update $subnet_id --dns_nameservers list=true 2.2.2.21 2.2.2.22 || die "fail to update subnet $subnet_id" # test the crud of ports port=$port_name -neutron port-create $FORMAT $NOAUTH $network --name $port || die "fail to create port $port" -tempport=`neutron port-list $FORMAT -- --name $port --fields id | wc -l` +neutron port-create $NOAUTH $network --name $port || die "fail to create port $port" +tempport=`neutron port-list -- --name $port --fields id | wc -l` echo $tempport if [ $tempport -ne 5 ]; then die "ports with name $port is not unique or found" fi -port_id=`neutron port-list $FORMAT -- --name $port --fields id | tail -n 2 | head -n 1 | cut -d' ' -f 2` +port_id=`neutron port-list -- --name $port --fields id | tail -n 2 | head -n 1 | cut -d' ' -f 2` echo "ID of port with name $port is $port_id" -neutron port-show $FORMAT $port || die "fail to show port $port" -neutron port-show $FORMAT $port_id || die "fail to show port $port_id" -neutron port-update $FORMAT $port --device_id deviceid1 || die "fail to update port $port" -neutron port-update $FORMAT $port_id --device_id deviceid2 || die "fail to update port $port_id" -neutron port-update $FORMAT $port_id --allowed-address-pair ip_address=1.1.1.11,mac_address=10:00:00:00:00:00 --allowed-address-pair ip_address=1.1.1.12,mac_address=10:00:00:00:00:01 || die "fail to update port $port_id --allowed-address-pair" -neutron port-show $FORMAT $port || die "fail to show port $port" -neutron port-show $FORMAT $port_id || die "fail to show port $port_id" -neutron port-update $FORMAT $port_id --no-allowed-address-pairs || die "fail to update port $port_id --no-allowed-address-pairs" -neutron port-show $FORMAT $port || die "fail to show port $port" -neutron port-show $FORMAT $port_id || die "fail to show port $port_id" +neutron port-show $port || die "fail to show port $port" +neutron port-show $port_id || die "fail to show port $port_id" +neutron port-update $port --device_id deviceid1 || die "fail to update port $port" +neutron port-update $port_id --device_id deviceid2 || die "fail to update port $port_id" +neutron port-update $port_id --allowed-address-pair ip_address=1.1.1.11,mac_address=10:00:00:00:00:00 --allowed-address-pair ip_address=1.1.1.12,mac_address=10:00:00:00:00:01 || die "fail to update port $port_id --allowed-address-pair" +neutron port-show $port || die "fail to show port $port" +neutron port-show $port_id || die "fail to show port $port_id" +neutron port-update $port_id --no-allowed-address-pairs || die "fail to update port $port_id --no-allowed-address-pairs" +neutron port-show $port || die "fail to show port $port" +neutron port-show $port_id || die "fail to show port $port_id" neutron port-delete $port_id # test the create port with allowed-address-pairs port=$port_name -neutron port-create $FORMAT $NOAUTH $network --name $port -- --allowed-address-pairs type=dict list=true ip_address=1.1.1.11,mac_address=10:00:00:00:00:00 ip_address=1.1.1.12,mac_address=10:00:00:00:00:01 || die "fail to create port $port" -tempport=`neutron port-list $FORMAT -- --name $port --fields id | wc -l` +neutron port-create $NOAUTH $network --name $port -- --allowed-address-pairs type=dict list=true ip_address=1.1.1.11,mac_address=10:00:00:00:00:00 ip_address=1.1.1.12,mac_address=10:00:00:00:00:01 || die "fail to create port $port" +tempport=`neutron port-list -- --name $port --fields id | wc -l` echo $tempport if [ $tempport -ne 5 ]; then die "ports with name $port is not unique or found" fi -port_id=`neutron port-list $FORMAT -- --name $port --fields id | tail -n 2 | head -n 1 | cut -d' ' -f 2` +port_id=`neutron port-list -- --name $port --fields id | tail -n 2 | head -n 1 | cut -d' ' -f 2` echo "ID of port with name $port is $port_id" -neutron port-show $FORMAT $port || die "fail to show port $port" -neutron port-show $FORMAT $port_id || die "fail to show port $port_id" -neutron port-update $FORMAT $port_id --no-allowed-address-pairs || die "fail to update port $port_id --no-allowed-address-pairs" +neutron port-show $port || die "fail to show port $port" +neutron port-show $port_id || die "fail to show port $port_id" +neutron port-update $port_id --no-allowed-address-pairs || die "fail to update port $port_id --no-allowed-address-pairs" neutron port-show $port_id # test quota commands RUD @@ -108,58 +106,58 @@ DEFAULT_NETWORKS=10 DEFAULT_PORTS=50 tenant_id=tenant_a tenant_id_b=tenant_b -neutron quota-update $FORMAT --tenant_id $tenant_id --network 30 || die "fail to update quota for tenant $tenant_id" -neutron quota-update $FORMAT --tenant_id $tenant_id_b --network 20 || die "fail to update quota for tenant $tenant_id" -networks=`neutron quota-list $FORMAT -c network -c tenant_id | grep $tenant_id | awk '{print $2}'` +neutron quota-update --tenant_id $tenant_id --network 30 || die "fail to update quota for tenant $tenant_id" +neutron quota-update --tenant_id $tenant_id_b --network 20 || die "fail to update quota for tenant $tenant_id" +networks=`neutron quota-list -c network -c tenant_id | grep $tenant_id | awk '{print $2}'` if [ $networks -ne 30 ]; then die "networks quota should be 30" fi -networks=`neutron quota-list $FORMAT -c network -c tenant_id | grep $tenant_id_b | awk '{print $2}'` +networks=`neutron quota-list -c network -c tenant_id | grep $tenant_id_b | awk '{print $2}'` if [ $networks -ne 20 ]; then die "networks quota should be 20" fi -networks=`neutron quota-show $FORMAT --tenant_id $tenant_id | grep network | awk -F'|' '{print $3}'` +networks=`neutron quota-show --tenant_id $tenant_id | grep network | awk -F'|' '{print $3}'` if [ $networks -ne 30 ]; then die "networks quota should be 30" fi -neutron quota-delete $FORMAT --tenant_id $tenant_id || die "fail to delete quota for tenant $tenant_id" -networks=`neutron quota-show $FORMAT --tenant_id $tenant_id | grep network | awk -F'|' '{print $3}'` +neutron quota-delete --tenant_id $tenant_id || die "fail to delete quota for tenant $tenant_id" +networks=`neutron quota-show --tenant_id $tenant_id | grep network | awk -F'|' '{print $3}'` if [ $networks -ne $DEFAULT_NETWORKS ]; then die "networks quota should be $DEFAULT_NETWORKS" fi # update self if [ "t$NOAUTH" = "t" ]; then # with auth - neutron quota-update $FORMAT --port 99 || die "fail to update quota for self" - ports=`neutron quota-show $FORMAT | grep port | awk -F'|' '{print $3}'` + neutron quota-update --port 99 || die "fail to update quota for self" + ports=`neutron quota-show | grep port | awk -F'|' '{print $3}'` if [ $ports -ne 99 ]; then die "ports quota should be 99" fi - ports=`neutron quota-list $FORMAT -c port | grep 99 | awk '{print $2}'` + ports=`neutron quota-list -c port | grep 99 | awk '{print $2}'` if [ $ports -ne 99 ]; then die "ports quota should be 99" fi - neutron quota-delete $FORMAT || die "fail to delete quota for tenant self" - ports=`neutron quota-show $FORMAT | grep port | awk -F'|' '{print $3}'` + neutron quota-delete || die "fail to delete quota for tenant self" + ports=`neutron quota-show | grep port | awk -F'|' '{print $3}'` if [ $ports -ne $DEFAULT_PORTS ]; then die "ports quota should be $DEFAULT_PORTS" fi else # without auth - neutron quota-update $FORMAT --port 100 + neutron quota-update --port 100 if [ $? -eq 0 ]; then die "without valid context on server, quota update command should fail." fi - neutron quota-show $FORMAT + neutron quota-show if [ $? -eq 0 ]; then die "without valid context on server, quota show command should fail." fi - neutron quota-delete $FORMAT + neutron quota-delete if [ $? -eq 0 ]; then die "without valid context on server, quota delete command should fail." fi - neutron quota-list $FORMAT || die "fail to update quota for self" + neutron quota-list || die "fail to update quota for self" fi cleanup diff --git a/neutronclient/common/constants.py b/neutronclient/common/constants.py index 525a04058..ccb22cd80 100644 --- a/neutronclient/common/constants.py +++ b/neutronclient/common/constants.py @@ -14,18 +14,6 @@ # limitations under the License. -EXT_NS = '_extension_ns' -XML_NS_V20 = 'http://openstack.org/quantum/api/v2.0' -XSI_NAMESPACE = "http://www.w3.org/2001/XMLSchema-instance" -XSI_ATTR = "xsi:nil" -XSI_NIL_ATTR = "xmlns:xsi" -TYPE_XMLNS = "xmlns:quantum" -TYPE_ATTR = "quantum:type" -VIRTUAL_ROOT_KEY = "_v_root" -ATOM_NAMESPACE = "http://www.w3.org/2005/Atom" -ATOM_XMLNS = "xmlns:atom" -ATOM_LINK_NOTATION = "{%s}link" % ATOM_NAMESPACE - TYPE_BOOL = "bool" TYPE_INT = "int" TYPE_LONG = "long" diff --git a/neutronclient/common/serializer.py b/neutronclient/common/serializer.py index f21e87c7e..e094c2687 100644 --- a/neutronclient/common/serializer.py +++ b/neutronclient/common/serializer.py @@ -14,13 +14,10 @@ # under the License. import logging -from xml.etree import ElementTree as etree -from xml.parsers import expat from oslo_serialization import jsonutils import six -from neutronclient.common import constants from neutronclient.common import exceptions as exception from neutronclient.i18n import _ @@ -62,148 +59,6 @@ def sanitizer(obj): return jsonutils.dumps(data, default=sanitizer) -class XMLDictSerializer(DictSerializer): - - def __init__(self, metadata=None, xmlns=None): - """XMLDictSerializer constructor. - - :param metadata: information needed to deserialize XML into - a dictionary. - :param xmlns: XML namespace to include with serialized XML - """ - super(XMLDictSerializer, self).__init__() - self.metadata = metadata or {} - if not xmlns: - xmlns = self.metadata.get('xmlns') - if not xmlns: - xmlns = constants.XML_NS_V20 - self.xmlns = xmlns - - def default(self, data): - """Default serializer of XMLDictSerializer. - - :param data: expect data to contain a single key as XML root, or - contain another '*_links' key as atom links. Other - case will use 'VIRTUAL_ROOT_KEY' as XML root. - """ - try: - links = None - has_atom = False - if data is None: - root_key = constants.VIRTUAL_ROOT_KEY - root_value = None - else: - link_keys = [k for k in six.iterkeys(data) or [] - if k.endswith('_links')] - if link_keys: - links = data.pop(link_keys[0], None) - has_atom = True - root_key = (len(data) == 1 and - list(data.keys())[0] or constants.VIRTUAL_ROOT_KEY) - root_value = data.get(root_key, data) - doc = etree.Element("_temp_root") - used_prefixes = [] - self._to_xml_node(doc, self.metadata, root_key, - root_value, used_prefixes) - if links: - self._create_link_nodes(list(doc)[0], links) - return self.to_xml_string(list(doc)[0], used_prefixes, has_atom) - except AttributeError as e: - LOG.exception(str(e)) - return '' - - def __call__(self, data): - # Provides a migration path to a cleaner WSGI layer, this - # "default" stuff and extreme extensibility isn't being used - # like originally intended - return self.default(data) - - def to_xml_string(self, node, used_prefixes, has_atom=False): - self._add_xmlns(node, used_prefixes, has_atom) - return etree.tostring(node, encoding='UTF-8') - - # NOTE(ameade): the has_atom should be removed after all of the - # XML serializers and view builders have been updated to the current - # spec that required all responses include the xmlns:atom, the has_atom - # flag is to prevent current tests from breaking - def _add_xmlns(self, node, used_prefixes, has_atom=False): - node.set('xmlns', self.xmlns) - node.set(constants.TYPE_XMLNS, self.xmlns) - if has_atom: - node.set(constants.ATOM_XMLNS, constants.ATOM_NAMESPACE) - node.set(constants.XSI_NIL_ATTR, constants.XSI_NAMESPACE) - ext_ns = self.metadata.get(constants.EXT_NS, {}) - for prefix in used_prefixes: - if prefix in ext_ns: - node.set('xmlns:' + prefix, ext_ns[prefix]) - - def _to_xml_node(self, parent, metadata, nodename, data, used_prefixes): - """Recursive method to convert data members to XML nodes.""" - result = etree.SubElement(parent, nodename) - if ":" in nodename: - used_prefixes.append(nodename.split(":", 1)[0]) - # TODO(bcwaldon): accomplish this without a type-check - if isinstance(data, list): - if not data: - result.set( - constants.TYPE_ATTR, - constants.TYPE_LIST) - return result - singular = metadata.get('plurals', {}).get(nodename, None) - if singular is None: - if nodename.endswith('s'): - singular = nodename[:-1] - else: - singular = 'item' - for item in data: - self._to_xml_node(result, metadata, singular, item, - used_prefixes) - # TODO(bcwaldon): accomplish this without a type-check - elif isinstance(data, dict): - if not data: - result.set( - constants.TYPE_ATTR, - constants.TYPE_DICT) - return result - attrs = metadata.get('attributes', {}).get(nodename, {}) - for k, v in sorted(data.items()): - if k in attrs: - result.set(k, str(v)) - else: - self._to_xml_node(result, metadata, k, v, - used_prefixes) - elif data is None: - result.set(constants.XSI_ATTR, 'true') - else: - if isinstance(data, bool): - result.set( - constants.TYPE_ATTR, - constants.TYPE_BOOL) - elif isinstance(data, int): - result.set( - constants.TYPE_ATTR, - constants.TYPE_INT) - elif isinstance(data, long): - result.set( - constants.TYPE_ATTR, - constants.TYPE_LONG) - elif isinstance(data, float): - result.set( - constants.TYPE_ATTR, - constants.TYPE_FLOAT) - LOG.debug("Data %(data)s type is %(type)s", - {'data': data, - 'type': type(data)}) - result.text = six.text_type(data) - return result - - def _create_link_nodes(self, xml_doc, links): - for link in links: - link_node = etree.SubElement(xml_doc, 'atom:link') - link_node.set('rel', link['rel']) - link_node.set('href', link['href']) - - class TextDeserializer(ActionDispatcher): """Default request body deserialization.""" @@ -227,140 +82,11 @@ def default(self, datastring): return {'body': self._from_json(datastring)} -class XMLDeserializer(TextDeserializer): - - def __init__(self, metadata=None): - """XMLDeserializer constructor. - - :param metadata: information needed to deserialize XML into - a dictionary. - """ - super(XMLDeserializer, self).__init__() - self.metadata = metadata or {} - xmlns = self.metadata.get('xmlns') - if not xmlns: - xmlns = constants.XML_NS_V20 - self.xmlns = xmlns - - def _get_key(self, tag): - tags = tag.split("}", 1) - if len(tags) == 2: - ns = tags[0][1:] - bare_tag = tags[1] - ext_ns = self.metadata.get(constants.EXT_NS, {}) - if ns == self.xmlns: - return bare_tag - for prefix, _ns in ext_ns.items(): - if ns == _ns: - return prefix + ":" + bare_tag - else: - return tag - - def _get_links(self, root_tag, node): - link_nodes = node.findall(constants.ATOM_LINK_NOTATION) - root_tag = self._get_key(node.tag) - link_key = "%s_links" % root_tag - link_list = [] - for link in link_nodes: - link_list.append({'rel': link.get('rel'), - 'href': link.get('href')}) - # Remove link node in order to avoid link node being - # processed as an item in _from_xml_node - node.remove(link) - return link_list and {link_key: link_list} or {} - - def _from_xml(self, datastring): - if datastring is None: - return None - plurals = set(self.metadata.get('plurals', {})) - try: - node = etree.fromstring(datastring) - root_tag = self._get_key(node.tag) - links = self._get_links(root_tag, node) - result = self._from_xml_node(node, plurals) - # There is no case where root_tag = constants.VIRTUAL_ROOT_KEY - # and links is not None because of the way data are serialized - if root_tag == constants.VIRTUAL_ROOT_KEY: - return result - return dict({root_tag: result}, **links) - except Exception as e: - parseError = False - # Python2.7 - if (hasattr(etree, 'ParseError') and - isinstance(e, getattr(etree, 'ParseError'))): - parseError = True - # Python2.6 - elif isinstance(e, expat.ExpatError): - parseError = True - if parseError: - msg = _("Cannot understand XML") - raise exception.MalformedResponseBody(reason=msg) - else: - raise - - def _from_xml_node(self, node, listnames): - """Convert a minidom node to a simple Python type. - - :param node: minidom node name - :param listnames: list of XML node names whose subnodes should - be considered list items. - - """ - attrNil = node.get(str(etree.QName(constants.XSI_NAMESPACE, "nil"))) - attrType = node.get(str(etree.QName( - self.metadata.get('xmlns'), "type"))) - if (attrNil and attrNil.lower() == 'true'): - return None - elif not len(node) and not node.text: - if (attrType and attrType == constants.TYPE_DICT): - return {} - elif (attrType and attrType == constants.TYPE_LIST): - return [] - else: - return '' - elif (len(node) == 0 and node.text): - converters = {constants.TYPE_BOOL: - lambda x: x.lower() == 'true', - constants.TYPE_INT: - lambda x: int(x), - constants.TYPE_LONG: - lambda x: long(x), - constants.TYPE_FLOAT: - lambda x: float(x)} - if attrType and attrType in converters: - return converters[attrType](node.text) - else: - return node.text - elif self._get_key(node.tag) in listnames: - return [self._from_xml_node(n, listnames) for n in node] - else: - result = dict() - for attr in node.keys(): - if (attr == 'xmlns' or - attr.startswith('xmlns:') or - attr == constants.XSI_ATTR or - attr == constants.TYPE_ATTR): - continue - result[self._get_key(attr)] = node.get(attr) - children = list(node) - for child in children: - result[self._get_key(child.tag)] = self._from_xml_node( - child, listnames) - return result - - def default(self, datastring): - return {'body': self._from_xml(datastring)} - - def __call__(self, datastring): - # Adding a migration path to allow us to remove unncessary classes - return self.default(datastring) - - # NOTE(maru): this class is duplicated from neutron.wsgi class Serializer(object): """Serializes and deserializes dictionaries to certain MIME types.""" - def __init__(self, metadata=None, default_xmlns=None): + def __init__(self, metadata=None): """Create a serializer based on the given WSGI environment. 'metadata' is an optional dict mapping MIME types to information @@ -368,12 +94,10 @@ def __init__(self, metadata=None, default_xmlns=None): """ self.metadata = metadata or {} - self.default_xmlns = default_xmlns def _get_serialize_handler(self, content_type): handlers = { 'application/json': JSONDictSerializer(), - 'application/xml': XMLDictSerializer(self.metadata), } try: @@ -381,22 +105,21 @@ def _get_serialize_handler(self, content_type): except Exception: raise exception.InvalidContentType(content_type=content_type) - def serialize(self, data, content_type): + def serialize(self, data): """Serialize a dictionary into the specified content type.""" - return self._get_serialize_handler(content_type).serialize(data) + return self._get_serialize_handler("application/json").serialize(data) - def deserialize(self, datastring, content_type): + def deserialize(self, datastring): """Deserialize a string to a dictionary. The string must be in the format of a supported MIME type. """ - return self.get_deserialize_handler(content_type).deserialize( + return self.get_deserialize_handler("application/json").deserialize( datastring) def get_deserialize_handler(self, content_type): handlers = { 'application/json': JSONDeserializer(), - 'application/xml': XMLDeserializer(self.metadata), } try: diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index 4bd6b1da8..b34be995d 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -426,12 +426,12 @@ def get_parser(self, prog_name): parser = super(NeutronCommand, self).get_parser(prog_name) parser.add_argument( '--request-format', - help=_('The XML or JSON request format.'), + help=_('DEPRECATED! Only JSON request format is supported.'), default='json', - choices=['json', 'xml', ], ) + choices=['json', ], ) parser.add_argument( '--request_format', - choices=['json', 'xml', ], + choices=['json', ], help=argparse.SUPPRESS) return parser @@ -486,7 +486,6 @@ def get_data(self, parsed_args): self.log.debug('get_data(%s)' % parsed_args) self.set_extra_attrs(parsed_args) neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format _extra_values = parse_args_to_dict(self.values_specs) _merge_args(self, parsed_args, _extra_values, self.values_specs) @@ -531,7 +530,6 @@ def run(self, parsed_args): self.log.debug('run(%s)', parsed_args) self.set_extra_attrs(parsed_args) neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format _extra_values = parse_args_to_dict(self.values_specs) _merge_args(self, parsed_args, _extra_values, self.values_specs) @@ -587,7 +585,6 @@ def run(self, parsed_args): self.log.debug('run(%s)', parsed_args) self.set_extra_attrs(parsed_args) neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format obj_deleter = getattr(neutron_client, "delete_%s" % self.cmd_resource) if self.allow_names: @@ -654,7 +651,6 @@ def call_server(self, neutron_client, search_opts, parsed_args): def retrieve_list(self, parsed_args): """Retrieve a list of resources from Neutron server.""" neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format _extra_values = parse_args_to_dict(self.values_specs) _merge_args(self, parsed_args, _extra_values, self.values_specs) @@ -742,7 +738,6 @@ def get_data(self, parsed_args): self.log.debug('get_data(%s)', parsed_args) self.set_extra_attrs(parsed_args) neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format params = {} if parsed_args.show_details: diff --git a/neutronclient/neutron/v2_0/agentscheduler.py b/neutronclient/neutron/v2_0/agentscheduler.py index b1345637c..59a01fbee 100644 --- a/neutronclient/neutron/v2_0/agentscheduler.py +++ b/neutronclient/neutron/v2_0/agentscheduler.py @@ -41,7 +41,6 @@ def get_parser(self, prog_name): def run(self, parsed_args): self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format _net_id = neutronV20.find_resourceid_by_name_or_id( neutron_client, 'network', parsed_args.network) neutron_client.add_network_to_dhcp_agent(parsed_args.dhcp_agent, @@ -66,7 +65,6 @@ def get_parser(self, prog_name): def run(self, parsed_args): self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format _net_id = neutronV20.find_resourceid_by_name_or_id( neutron_client, 'network', parsed_args.network) neutron_client.remove_network_from_dhcp_agent( @@ -139,7 +137,6 @@ def get_parser(self, prog_name): def run(self, parsed_args): self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format _id = neutronV20.find_resourceid_by_name_or_id( neutron_client, 'router', parsed_args.router) neutron_client.add_router_to_l3_agent(parsed_args.l3_agent, @@ -164,7 +161,6 @@ def get_parser(self, prog_name): def run(self, parsed_args): self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format _id = neutronV20.find_resourceid_by_name_or_id( neutron_client, 'router', parsed_args.router) neutron_client.remove_router_from_l3_agent( diff --git a/neutronclient/neutron/v2_0/flavor/flavor.py b/neutronclient/neutron/v2_0/flavor/flavor.py index a8a1055fa..c53db3e27 100644 --- a/neutronclient/neutron/v2_0/flavor/flavor.py +++ b/neutronclient/neutron/v2_0/flavor/flavor.py @@ -120,7 +120,6 @@ def get_parser(self, prog_name): def run(self, parsed_args): neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format flavor_id = neutronV20.find_resourceid_by_name_or_id( neutron_client, 'flavor', parsed_args.flavor) service_profile_id = neutronV20.find_resourceid_by_id( @@ -154,7 +153,6 @@ def get_parser(self, prog_name): def run(self, parsed_args): neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format flavor_id = neutronV20.find_resourceid_by_name_or_id( neutron_client, 'flavor', parsed_args.flavor) service_profile_id = neutronV20.find_resourceid_by_id( diff --git a/neutronclient/neutron/v2_0/floatingip.py b/neutronclient/neutron/v2_0/floatingip.py index c6b3345f5..c603d2f28 100644 --- a/neutronclient/neutron/v2_0/floatingip.py +++ b/neutronclient/neutron/v2_0/floatingip.py @@ -114,7 +114,6 @@ def get_parser(self, prog_name): def run(self, parsed_args): self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format update_dict = {} neutronV20.update_dict(parsed_args, update_dict, ['port_id', 'fixed_ip_address']) @@ -141,7 +140,6 @@ def get_parser(self, prog_name): def run(self, parsed_args): self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format neutron_client.update_floatingip(parsed_args.floatingip_id, {'floatingip': {'port_id': None}}) print(_('Disassociated floating IP %s') % parsed_args.floatingip_id, diff --git a/neutronclient/neutron/v2_0/fw/firewallpolicy.py b/neutronclient/neutron/v2_0/fw/firewallpolicy.py index 1a6d062fc..c0720eab5 100644 --- a/neutronclient/neutron/v2_0/fw/firewallpolicy.py +++ b/neutronclient/neutron/v2_0/fw/firewallpolicy.py @@ -170,7 +170,6 @@ def get_parser(self, prog_name): def run(self, parsed_args): neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format body = self.args2body(parsed_args) _id = neutronv20.find_resourceid_by_name_or_id(neutron_client, self.resource, @@ -209,7 +208,6 @@ def get_parser(self, prog_name): def run(self, parsed_args): neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format body = self.args2body(parsed_args) _id = neutronv20.find_resourceid_by_name_or_id(neutron_client, self.resource, diff --git a/neutronclient/neutron/v2_0/lb/healthmonitor.py b/neutronclient/neutron/v2_0/lb/healthmonitor.py index 1da7eb01c..8b1a1a110 100644 --- a/neutronclient/neutron/v2_0/lb/healthmonitor.py +++ b/neutronclient/neutron/v2_0/lb/healthmonitor.py @@ -126,7 +126,6 @@ def get_parser(self, prog_name): def run(self, parsed_args): neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format body = {'health_monitor': {'id': parsed_args.health_monitor_id}} pool_id = neutronV20.find_resourceid_by_name_or_id( neutron_client, 'pool', parsed_args.pool_id) @@ -153,7 +152,6 @@ def get_parser(self, prog_name): def run(self, parsed_args): neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format pool_id = neutronV20.find_resourceid_by_name_or_id( neutron_client, 'pool', parsed_args.pool_id) neutron_client.disassociate_health_monitor(pool_id, diff --git a/neutronclient/neutron/v2_0/lb/pool.py b/neutronclient/neutron/v2_0/lb/pool.py index 849e1c4bf..b0674fcb8 100644 --- a/neutronclient/neutron/v2_0/lb/pool.py +++ b/neutronclient/neutron/v2_0/lb/pool.py @@ -110,7 +110,6 @@ class RetrievePoolStats(neutronV20.ShowCommand): def get_data(self, parsed_args): self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format pool_id = neutronV20.find_resourceid_by_name_or_id( self.get_client(), 'pool', parsed_args.id) params = {} diff --git a/neutronclient/neutron/v2_0/metering.py b/neutronclient/neutron/v2_0/metering.py index ea793c3bd..aa79ce1ef 100644 --- a/neutronclient/neutron/v2_0/metering.py +++ b/neutronclient/neutron/v2_0/metering.py @@ -101,7 +101,6 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format label_id = neutronv20.find_resourceid_by_name_or_id( neutron_client, 'metering_label', parsed_args.label_id) diff --git a/neutronclient/neutron/v2_0/nsx/networkgateway.py b/neutronclient/neutron/v2_0/nsx/networkgateway.py index 124d7bbad..515b87d6b 100644 --- a/neutronclient/neutron/v2_0/nsx/networkgateway.py +++ b/neutronclient/neutron/v2_0/nsx/networkgateway.py @@ -237,7 +237,6 @@ class ConnectNetworkGateway(NetworkGatewayInterfaceCommand): def run(self, parsed_args): self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format (gateway_id, network_id) = self.retrieve_ids(neutron_client, parsed_args) neutron_client.connect_network_gateway( @@ -256,7 +255,6 @@ class DisconnectNetworkGateway(NetworkGatewayInterfaceCommand): def run(self, parsed_args): self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format (gateway_id, network_id) = self.retrieve_ids(neutron_client, parsed_args) neutron_client.disconnect_network_gateway( diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index e98e0f00d..49a2610c2 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -113,7 +113,6 @@ def get_parser(self, prog_name): def get_data(self, parsed_args): neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format _id = neutronV20.find_resourceid_by_name_or_id( neutron_client, 'router', parsed_args.id) self.values_specs.append('--device_id=%s' % _id) diff --git a/neutronclient/neutron/v2_0/quota.py b/neutronclient/neutron/v2_0/quota.py index 03ab64de6..eb750744a 100644 --- a/neutronclient/neutron/v2_0/quota.py +++ b/neutronclient/neutron/v2_0/quota.py @@ -53,7 +53,6 @@ def get_parser(self, prog_name): def run(self, parsed_args): self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format tenant_id = get_tenant_id(parsed_args.tenant_id, neutron_client) obj_deleter = getattr(neutron_client, @@ -81,7 +80,6 @@ def get_data(self, parsed_args): neutron_client = self.get_client() search_opts = {} self.log.debug('search options: %s', search_opts) - neutron_client.format = parsed_args.request_format obj_lister = getattr(neutron_client, "list_%ss" % self.resource) data = obj_lister(**search_opts) @@ -114,7 +112,6 @@ def get_parser(self, prog_name): def get_data(self, parsed_args): self.log.debug('get_data(%s)', parsed_args) neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format tenant_id = get_tenant_id(parsed_args.tenant_id, neutron_client) params = {} @@ -212,7 +209,6 @@ def args2body(self, parsed_args): def get_data(self, parsed_args): self.log.debug('run(%s)', parsed_args) neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format _extra_values = neutronV20.parse_args_to_dict(self.values_specs) neutronV20._merge_args(self, parsed_args, _extra_values, self.values_specs) diff --git a/neutronclient/neutron/v2_0/rbac.py b/neutronclient/neutron/v2_0/rbac.py index ace6613f1..79c991d12 100644 --- a/neutronclient/neutron/v2_0/rbac.py +++ b/neutronclient/neutron/v2_0/rbac.py @@ -67,7 +67,6 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format _object_id = get_rbac_object_id(neutron_client, parsed_args.type, parsed_args.name) body = { diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index 4c9a83c26..c2d159b24 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -160,7 +160,6 @@ def get_parser(self, prog_name): def run(self, parsed_args): self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format if '=' in parsed_args.interface: resource, value = parsed_args.interface.split('=', 1) @@ -232,7 +231,6 @@ def get_parser(self, prog_name): def run(self, parsed_args): self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format _router_id = neutronV20.find_resourceid_by_name_or_id( neutron_client, self.resource, parsed_args.router) _ext_net_id = neutronV20.find_resourceid_by_name_or_id( @@ -272,7 +270,6 @@ def get_parser(self, prog_name): def run(self, parsed_args): self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format _router_id = neutronV20.find_resourceid_by_name_or_id( neutron_client, self.resource, parsed_args.router) neutron_client.remove_gateway_router(_router_id) diff --git a/neutronclient/tests/unit/fw/test_cli20_firewall.py b/neutronclient/tests/unit/fw/test_cli20_firewall.py index fdcbda6b2..c764139f7 100644 --- a/neutronclient/tests/unit/fw/test_cli20_firewall.py +++ b/neutronclient/tests/unit/fw/test_cli20_firewall.py @@ -161,7 +161,3 @@ def test_delete_firewall(self): my_id = 'my-id' args = [my_id] self._test_delete_resource(resource, cmd, my_id, args) - - -class CLITestV20FirewallXML(CLITestV20FirewallJSON): - format = 'xml' diff --git a/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py b/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py index 703b7cd2e..b1e20c675 100644 --- a/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py +++ b/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py @@ -182,7 +182,6 @@ def test_insert_firewall_rule(self): headers=mox.ContainsKeyValue( 'X-Auth-Token', test_cli20.TOKEN)).AndReturn((test_cli20.MyResp(204), None)) - args.extend(['--request-format', self.format]) self.mox.ReplayAll() cmd_parser = cmd.get_parser(resource + "_insert_rule") shell.run_command(cmd, cmd_parser, args) @@ -213,13 +212,8 @@ def test_remove_firewall_rule(self): headers=mox.ContainsKeyValue( 'X-Auth-Token', test_cli20.TOKEN)).AndReturn((test_cli20.MyResp(204), None)) - args.extend(['--request-format', self.format]) self.mox.ReplayAll() cmd_parser = cmd.get_parser(resource + "_remove_rule") shell.run_command(cmd, cmd_parser, args) self.mox.VerifyAll() self.mox.UnsetStubs() - - -class CLITestV20FirewallPolicyXML(CLITestV20FirewallPolicyJSON): - format = 'xml' diff --git a/neutronclient/tests/unit/fw/test_cli20_firewallrule.py b/neutronclient/tests/unit/fw/test_cli20_firewallrule.py index 72f80288e..905c81c94 100644 --- a/neutronclient/tests/unit/fw/test_cli20_firewallrule.py +++ b/neutronclient/tests/unit/fw/test_cli20_firewallrule.py @@ -178,7 +178,3 @@ def test_delete_firewall_rule(self): my_id = 'myid1' args = [my_id] self._test_delete_resource(resource, cmd, my_id, args) - - -class CLITestV20FirewallRuleXML(CLITestV20FirewallRuleJSON): - format = 'xml' diff --git a/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py b/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py index 300fa678e..6895b5ee5 100644 --- a/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py +++ b/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py @@ -201,7 +201,3 @@ def test_disassociate_healthmonitor(self): cmd.run(parsed_args) self.mox.VerifyAll() self.mox.UnsetStubs() - - -class CLITestV20LbHealthmonitorXML(CLITestV20LbHealthmonitorJSON): - format = 'xml' diff --git a/neutronclient/tests/unit/lb/test_cli20_member.py b/neutronclient/tests/unit/lb/test_cli20_member.py index 731459d24..7942432a4 100644 --- a/neutronclient/tests/unit/lb/test_cli20_member.py +++ b/neutronclient/tests/unit/lb/test_cli20_member.py @@ -117,7 +117,3 @@ def test_delete_member(self): my_id = 'my-id' args = [my_id] self._test_delete_resource(resource, cmd, my_id, args) - - -class CLITestV20LbMemberXML(CLITestV20LbMemberJSON): - format = 'xml' diff --git a/neutronclient/tests/unit/lb/test_cli20_pool.py b/neutronclient/tests/unit/lb/test_cli20_pool.py index dc9f6b5b0..82fa42d2d 100644 --- a/neutronclient/tests/unit/lb/test_cli20_pool.py +++ b/neutronclient/tests/unit/lb/test_cli20_pool.py @@ -164,7 +164,3 @@ def test_retrieve_pool_stats(self): _str = self.fake_stdout.make_string() self.assertIn('bytes_in', _str) self.assertIn('bytes_out', _str) - - -class CLITestV20LbPoolXML(CLITestV20LbPoolJSON): - format = 'xml' diff --git a/neutronclient/tests/unit/lb/test_cli20_vip.py b/neutronclient/tests/unit/lb/test_cli20_vip.py index a3229033e..3f2f34594 100644 --- a/neutronclient/tests/unit/lb/test_cli20_vip.py +++ b/neutronclient/tests/unit/lb/test_cli20_vip.py @@ -205,7 +205,3 @@ def test_delete_vip(self): my_id = 'my-id' args = [my_id] self._test_delete_resource(resource, cmd, my_id, args) - - -class CLITestV20LbVipXML(CLITestV20LbVipJSON): - format = 'xml' diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py b/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py index 590700082..415e29f97 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py @@ -148,7 +148,3 @@ def test_delete_healthmonitor(self): args = [my_id] self._test_delete_resource(resource, cmd, my_id, args, cmd_resource=cmd_resource) - - -class CLITestV20LbHealthMonitorXML(CLITestV20LbHealthMonitorJSON): - format = 'xml' diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_listener.py b/neutronclient/tests/unit/lb/v2/test_cli20_listener.py index 6ecac35b5..e290ac823 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_listener.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_listener.py @@ -134,7 +134,3 @@ def test_delete_listener(self): args = [my_id] self._test_delete_resource(resource, cmd, my_id, args, cmd_resource=cmd_resource) - - -class CLITestV20LbListenerXML(CLITestV20LbListenerJSON): - format = 'xml' diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py b/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py index e96df15cf..f0c7457b1 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py @@ -130,7 +130,3 @@ def test_delete_loadbalancer(self): args = [my_id] self._test_delete_resource(resource, cmd, my_id, args, cmd_resource=cmd_resource) - - -class CLITestV20LbLoadBalancerXML(CLITestV20LbLoadBalancerJSON): - format = 'xml' diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_member.py b/neutronclient/tests/unit/lb/v2/test_cli20_member.py index 73b329f6d..15ee9ce5f 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_member.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_member.py @@ -155,7 +155,3 @@ def test_delete_member(self): self._test_delete_resource(resource, cmd, my_id, args, cmd_resource=cmd_resource, parent_id=pool_id) - - -class CLITestV20LbMemberXML(CLITestV20LbMemberJSON): - format = 'xml' diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_pool.py b/neutronclient/tests/unit/lb/v2/test_cli20_pool.py index 48630f893..2ec40f437 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_pool.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_pool.py @@ -137,7 +137,3 @@ def test_delete_pool(self): args = [my_id] self._test_delete_resource(resource, cmd, my_id, args, cmd_resource=cmd_resource) - - -class CLITestV20LbPoolXML(CLITestV20LbPoolJSON): - format = 'xml' diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 2e609a0bd..febc5863a 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -190,10 +190,7 @@ def setUp(self, plurals=None): client.Client.EXTED_PLURALS.update(constants.PLURALS) if plurals is not None: client.Client.EXTED_PLURALS.update(plurals) - self.metadata = {'plurals': client.Client.EXTED_PLURALS, - 'xmlns': constants.XML_NS_V20, - constants.EXT_NS: {'prefix': - 'http://xxxx.yy.com'}} + self.metadata = {'plurals': client.Client.EXTED_PLURALS} self.mox = mox.Mox() self.endurl = ENDURL self.fake_stdout = FakeStdout() @@ -208,6 +205,7 @@ def setUp(self, plurals=None): 'neutronclient.v2_0.client.Client.get_attr_metadata', self._get_attr_metadata)) self.client = client.Client(token=TOKEN, endpoint_url=self.endurl) + self.client.format = self.format def register_non_admin_status_resource(self, resource_name): # TODO(amotoki): @@ -254,7 +252,6 @@ def _test_create_resource(self, resource, cmd, name, myid, args, {self.id_field: myid}, } if name: ress[resource].update({'name': name}) - self.client.format = self.format resstr = self.client.serialize(ress) # url method body resource_plural = neutronV2_0._get_resource_plural(cmd_resource, @@ -262,19 +259,14 @@ def _test_create_resource(self, resource, cmd, name, myid, args, path = getattr(self.client, resource_plural + "_path") if parent_id: path = path % parent_id - # Work around for LP #1217791. XML deserializer called from - # MyComparator does not decodes XML string correctly. - if self.format == 'json': - mox_body = MyComparator(body, self.client) - else: - mox_body = self.client.serialize(body) + mox_body = MyComparator(body, self.client) + if not no_api_call: self.client.httpclient.request( end_url(path, format=self.format), 'POST', body=mox_body, headers=mox.ContainsKeyValue( 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr)) - args.extend(['--request-format', self.format]) self.mox.ReplayAll() cmd_parser = cmd.get_parser('create_' + resource) if expected_exception: @@ -295,7 +287,6 @@ def _test_list_columns(self, cmd, resources, self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") cmd.get_client().MultipleTimes().AndReturn(self.client) - self.client.format = self.format if not cmd_resources: cmd_resources = resources @@ -309,7 +300,6 @@ def _test_list_columns(self, cmd, resources, body=None, headers=mox.ContainsKeyValue( 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr)) - args = tuple(args) + ('--request-format', self.format) self.mox.ReplayAll() cmd_parser = cmd.get_parser("list_" + cmd_resources) shell.run_command(cmd, cmd_parser, args) @@ -332,13 +322,11 @@ def _test_list_resources(self, resources, cmd, detail=False, tags=(), else: contents = response_contents reses = {resources: contents} - self.client.format = self.format resstr = self.client.serialize(reses) # url method body args = base_args if base_args is not None else [] if detail: args.append('-D') - args.extend(['--request-format', self.format]) if fields_1: for field in fields_1: args.append('--fields') @@ -438,7 +426,6 @@ def _test_list_resources_with_pagination(self, resources, cmd, 'rel': 'next'}]} reses2 = {resources: [{'id': 'myid3', }, {'id': 'myid4', }]} - self.client.format = self.format resstr1 = self.client.serialize(reses1) resstr2 = self.client.serialize(reses2) self.client.httpclient.request( @@ -455,7 +442,6 @@ def _test_list_resources_with_pagination(self, resources, cmd, self.mox.ReplayAll() cmd_parser = cmd.get_parser("list_" + cmd_resources) args = base_args if base_args is not None else [] - args.extend(['--request-format', self.format]) shell.run_command(cmd, cmd_parser, args) self.mox.VerifyAll() self.mox.UnsetStubs() @@ -474,13 +460,8 @@ def _test_update_resource(self, resource, cmd, myid, args, extrafields, path = path % (parent_id, myid) else: path = path % myid - self.client.format = self.format - # Work around for LP #1217791. XML deserializer called from - # MyComparator does not decodes XML string correctly. - if self.format == 'json': - mox_body = MyComparator(body, self.client) - else: - mox_body = self.client.serialize(body) + mox_body = MyComparator(body, self.client) + self.client.httpclient.request( MyUrlComparator(end_url(path, format=self.format), self.client), @@ -488,7 +469,6 @@ def _test_update_resource(self, resource, cmd, myid, args, extrafields, body=mox_body, headers=mox.ContainsKeyValue( 'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), None)) - args.extend(['--request-format', self.format]) self.mox.ReplayAll() cmd_parser = cmd.get_parser("update_" + cmd_resource) shell.run_command(cmd, cmd_parser, args) @@ -509,7 +489,6 @@ def _test_show_resource(self, resource, cmd, myid, args, fields=(), expected_res = {resource: {self.id_field: myid, 'name': 'myname', }, } - self.client.format = self.format resstr = self.client.serialize(expected_res) path = getattr(self.client, cmd_resource + "_path") if parent_id: @@ -521,7 +500,6 @@ def _test_show_resource(self, resource, cmd, myid, args, fields=(), body=None, headers=mox.ContainsKeyValue( 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr)) - args.extend(['--request-format', self.format]) self.mox.ReplayAll() cmd_parser = cmd.get_parser("show_" + cmd_resource) shell.run_command(cmd, cmd_parser, args) @@ -548,7 +526,6 @@ def _test_delete_resource(self, resource, cmd, myid, args, body=None, headers=mox.ContainsKeyValue( 'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), None)) - args.extend(['--request-format', self.format]) self.mox.ReplayAll() cmd_parser = cmd.get_parser("delete_" + cmd_resource) shell.run_command(cmd, cmd_parser, args) @@ -571,7 +548,6 @@ def _test_update_resource_action(self, resource, cmd, myid, action, args, body=MyComparator(body, self.client), headers=mox.ContainsKeyValue( 'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), retval)) - args.extend(['--request-format', self.format]) self.mox.ReplayAll() cmd_parser = cmd.get_parser("delete_" + cmd_resource) shell.run_command(cmd, cmd_parser, args) @@ -583,7 +559,6 @@ def _test_update_resource_action(self, resource, cmd, myid, action, args, class ClientV2TestJson(CLITestV20Base): def test_do_request_unicode(self): - self.client.format = self.format self.mox.StubOutWithMock(self.client.httpclient, "request") unicode_text = u'\u7f51\u7edc' # url with unicode @@ -616,7 +591,6 @@ def test_do_request_unicode(self): self.assertEqual(body, res_body) def test_do_request_error_without_response_body(self): - self.client.format = self.format self.mox.StubOutWithMock(self.client.httpclient, "request") params = {'test': 'value'} expect_query = six.moves.urllib.parse.urlencode(params) @@ -649,10 +623,6 @@ def test_do_request_with_long_uri_exception(self): self.fail('Expected exception NOT raised') -class ClientV2UnicodeTestXML(ClientV2TestJson): - format = 'xml' - - class CLITestV20ExceptionHandler(CLITestV20Base): def _test_exception_handler_v20( diff --git a/neutronclient/tests/unit/test_cli20_agentschedulers.py b/neutronclient/tests/unit/test_cli20_agentschedulers.py index 8085581e1..d9c1f0033 100644 --- a/neutronclient/tests/unit/test_cli20_agentschedulers.py +++ b/neutronclient/tests/unit/test_cli20_agentschedulers.py @@ -206,7 +206,3 @@ def test_get_lbaas_agent_hosting_pool(self): contents = {self.id_field: 'myid1', 'alive': True} self._test_list_resources(resources, cmd, base_args=[lb_id], path=path, response_contents=contents) - - -class CLITestV20LBaaSAgentSchedulerXML(CLITestV20LBaaSAgentScheduler): - format = 'xml' diff --git a/neutronclient/tests/unit/test_cli20_floatingips.py b/neutronclient/tests/unit/test_cli20_floatingips.py index f42f145ba..42522e7b2 100644 --- a/neutronclient/tests/unit/test_cli20_floatingips.py +++ b/neutronclient/tests/unit/test_cli20_floatingips.py @@ -177,7 +177,3 @@ def test_associate_ip(self): self._test_update_resource(resource, cmd, 'myid', args, {"port_id": "portid"} ) - - -class CLITestV20FloatingIpsXML(CLITestV20FloatingIpsJSON): - format = 'xml' diff --git a/neutronclient/tests/unit/test_cli20_metering.py b/neutronclient/tests/unit/test_cli20_metering.py index ad9e99b6c..c51af3770 100644 --- a/neutronclient/tests/unit/test_cli20_metering.py +++ b/neutronclient/tests/unit/test_cli20_metering.py @@ -97,7 +97,3 @@ def test_show_metering_label_rule(self): args = ['--fields', 'id', self.test_id] self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - - -class CLITestV20MeteringXML(CLITestV20MeteringJSON): - format = 'xml' diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index dc5ba1e8b..85b281b60 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -600,7 +600,3 @@ def mox_calls(path, data): 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(response) self._test_extend_list(mox_calls) - - -class CLITestV20NetworkXML(CLITestV20NetworkJSON): - format = 'xml' diff --git a/neutronclient/tests/unit/test_cli20_nsx_networkgateway.py b/neutronclient/tests/unit/test_cli20_nsx_networkgateway.py index 0ce2c8121..640578dbe 100644 --- a/neutronclient/tests/unit/test_cli20_nsx_networkgateway.py +++ b/neutronclient/tests/unit/test_cli20_nsx_networkgateway.py @@ -261,7 +261,3 @@ def test_show_gateway_device(self): args = ['--fields', 'id', '--fields', 'name', self.test_id] self._test_show_resource(self.dev_resource, cmd, self.test_id, args, ['id', 'name']) - - -class CLITestV20NetworkGatewayXML(CLITestV20NetworkGatewayJSON): - format = 'xml' diff --git a/neutronclient/tests/unit/test_cli20_nsx_queue.py b/neutronclient/tests/unit/test_cli20_nsx_queue.py index 1cbe0c747..901422149 100644 --- a/neutronclient/tests/unit/test_cli20_nsx_queue.py +++ b/neutronclient/tests/unit/test_cli20_nsx_queue.py @@ -83,7 +83,3 @@ def test_delete_qos_queue(self): myid = 'myid' args = [myid] self._test_delete_resource(resource, cmd, myid, args) - - -class CLITestV20QosQueueXML(CLITestV20QosQueueJSON): - format = 'xml' diff --git a/neutronclient/tests/unit/test_cli20_nuage_netpartition.py b/neutronclient/tests/unit/test_cli20_nuage_netpartition.py index a9839f523..fc26979cf 100644 --- a/neutronclient/tests/unit/test_cli20_nuage_netpartition.py +++ b/neutronclient/tests/unit/test_cli20_nuage_netpartition.py @@ -53,7 +53,3 @@ def test_delete_netpartition(self): myid = 'myid' args = [myid] self._test_delete_resource(self.resource, cmd, myid, args) - - -class CLITestV20NetPartitionXML(CLITestV20NetPartitionJSON): - format = 'xml' diff --git a/neutronclient/tests/unit/test_cli20_port.py b/neutronclient/tests/unit/test_cli20_port.py index ad9b2b89d..7cc3bbd18 100644 --- a/neutronclient/tests/unit/test_cli20_port.py +++ b/neutronclient/tests/unit/test_cli20_port.py @@ -731,7 +731,3 @@ def test_delete_port(self): myid = 'myid' args = [myid] self._test_delete_resource(resource, cmd, myid, args) - - -class CLITestV20PortXML(CLITestV20PortJSON): - format = 'xml' diff --git a/neutronclient/tests/unit/test_cli20_router.py b/neutronclient/tests/unit/test_cli20_router.py index 60ee733bd..20534c748 100644 --- a/neutronclient/tests/unit/test_cli20_router.py +++ b/neutronclient/tests/unit/test_cli20_router.py @@ -382,7 +382,3 @@ def test_remove_gateway(self): self._test_update_resource(resource, cmd, 'externalid', args, {"external_gateway_info": {}} ) - - -class CLITestV20RouterXML(CLITestV20RouterJSON): - format = 'xml' diff --git a/neutronclient/tests/unit/test_cli20_securitygroup.py b/neutronclient/tests/unit/test_cli20_securitygroup.py index f077c1fcc..19e3c688e 100644 --- a/neutronclient/tests/unit/test_cli20_securitygroup.py +++ b/neutronclient/tests/unit/test_cli20_securitygroup.py @@ -609,7 +609,3 @@ def test__format_sg_rules(self): ] expected = '\n'.join(sorted(expected_data)) self.assertEqual(expected, securitygroup._format_sg_rules(sg)) - - -class CLITestV20SecurityGroupsXML(CLITestV20SecurityGroupsJSON): - format = 'xml' diff --git a/neutronclient/tests/unit/test_cli20_servicetype.py b/neutronclient/tests/unit/test_cli20_servicetype.py index 5ee4bd7ce..ec9a663fa 100644 --- a/neutronclient/tests/unit/test_cli20_servicetype.py +++ b/neutronclient/tests/unit/test_cli20_servicetype.py @@ -53,7 +53,3 @@ def test_list_service_providers_limit(self): cmd = servicetype.ListServiceProvider(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, page_size=1000) - - -class CLITestV20ServiceProvidersXML(CLITestV20ServiceProvidersJSON): - format = 'xml' diff --git a/neutronclient/tests/unit/test_cli20_subnet.py b/neutronclient/tests/unit/test_cli20_subnet.py index a5b1b62eb..84d59e5e7 100644 --- a/neutronclient/tests/unit/test_cli20_subnet.py +++ b/neutronclient/tests/unit/test_cli20_subnet.py @@ -574,12 +574,6 @@ def test_list_subnets_tags(self): cmd = subnet.ListSubnet(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, tags=['a', 'b']) - def test_list_subnets_known_option_after_unknown(self): - """List subnets: -- --tags a b --request-format xml.""" - resources = "subnets" - cmd = subnet.ListSubnet(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, tags=['a', 'b']) - def test_list_subnets_detail_tags(self): """List subnets: -D -- --tags a b.""" resources = "subnets" @@ -624,36 +618,13 @@ def test_update_subnet(self): {'name': 'myname', 'tags': ['a', 'b'], } ) - def test_update_subnet_known_option_before_id(self): - """Update subnet: --request-format json myid --name myname.""" - # --request-format xml is known option - resource = 'subnet' - cmd = subnet.UpdateSubnet(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['--request-format', 'json', - 'myid', '--name', 'myname'], - {'name': 'myname', } - ) - - def test_update_subnet_known_option_after_id(self): - """Update subnet: myid --name myname --request-format json.""" - # --request-format xml is known option - resource = 'subnet' - cmd = subnet.UpdateSubnet(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'myname', - '--request-format', 'json'], - {'name': 'myname', } - ) - def test_update_subnet_allocation_pools(self): """Update subnet: myid --name myname --tags a b.""" resource = 'subnet' cmd = subnet.UpdateSubnet(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', ['myid', '--allocation-pool', - 'start=1.2.0.2,end=1.2.0.127', - '--request-format', 'json'], + 'start=1.2.0.2,end=1.2.0.127'], {'allocation_pools': [{'start': '1.2.0.2', 'end': '1.2.0.127'}]} ) @@ -687,7 +658,3 @@ def test_delete_subnet(self): myid = 'myid' args = [myid] self._test_delete_resource(resource, cmd, myid, args) - - -class CLITestV20SubnetXML(CLITestV20SubnetJSON): - format = 'xml' diff --git a/neutronclient/tests/unit/test_http.py b/neutronclient/tests/unit/test_http.py index 761cc6fa8..d16f87b32 100644 --- a/neutronclient/tests/unit/test_http.py +++ b/neutronclient/tests/unit/test_http.py @@ -59,17 +59,17 @@ def test_headers_with_body(self): self._test_headers(headers, body=BODY) def test_headers_without_body_with_content_type(self): - headers = {'Accept': 'application/xml'} - self._test_headers(headers, content_type='application/xml') + headers = {'Accept': 'application/json'} + self._test_headers(headers, content_type='application/json') def test_headers_with_body_with_content_type(self): - headers = {'Accept': 'application/xml', - 'Content-Type': 'application/xml'} - self._test_headers(headers, body=BODY, content_type='application/xml') + headers = {'Accept': 'application/json', + 'Content-Type': 'application/json'} + self._test_headers(headers, body=BODY, content_type='application/json') def test_headers_defined_in_headers(self): - headers = {'Accept': 'application/xml', - 'Content-Type': 'application/xml'} + headers = {'Accept': 'application/json', + 'Content-Type': 'application/json'} self._test_headers(headers, body=BODY, headers=headers) diff --git a/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py b/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py index 604e2cc07..4295fc6ec 100644 --- a/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py +++ b/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py @@ -207,7 +207,3 @@ def test_delete_ikepolicy(self): my_id = 'my-id' args = [my_id] self._test_delete_resource(resource, cmd, my_id, args) - - -class CLITestV20VpnIkePolicyXML(CLITestV20VpnIkePolicyJSON): - format = 'xml' diff --git a/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py b/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py index d133e5ec3..9d42c6abd 100644 --- a/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py +++ b/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py @@ -204,7 +204,3 @@ def test_delete_ipsecpolicy(self): my_id = 'my-id' args = [my_id] self._test_delete_resource(resource, cmd, my_id, args) - - -class CLITestV20VpnIpsecPolicyXML(CLITestV20VpnIpsecPolicyJSON): - format = 'xml' diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index da8a9a48d..469c36e44 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -24,7 +24,6 @@ import six.moves.urllib.parse as urlparse from neutronclient import client -from neutronclient.common import constants from neutronclient.common import exceptions from neutronclient.common import extension as client_extension from neutronclient.common import serializer @@ -195,9 +194,7 @@ def do_request(self, method, action, body=None, headers=None, params=None): if body: body = self.serialize(body) - resp, replybody = self.httpclient.do_request( - action, method, body=body, - content_type=self.content_type()) + resp, replybody = self.httpclient.do_request(action, method, body=body) status_code = resp.status_code if status_code in (requests.codes.ok, @@ -214,7 +211,7 @@ def get_auth_info(self): return self.httpclient.get_auth_info() def serialize(self, data): - """Serializes a dictionary into either XML or JSON. + """Serializes a dictionary into JSON. A dictionary with a single key can be passed and it can contain any structure. @@ -222,39 +219,17 @@ def serialize(self, data): if data is None: return None elif type(data) is dict: - return serializer.Serializer( - self.get_attr_metadata()).serialize(data, self.content_type()) + return serializer.Serializer().serialize(data) else: raise Exception(_("Unable to serialize object of type = '%s'") % type(data)) def deserialize(self, data, status_code): - """Deserializes an XML or JSON string into a dictionary.""" + """Deserializes a JSON string into a dictionary.""" if status_code == 204: return data - return serializer.Serializer(self.get_attr_metadata()).deserialize( - data, self.content_type())['body'] - - def get_attr_metadata(self): - if self.format == 'json': - return {} - old_request_format = self.format - self.format = 'json' - exts = self.list_extensions()['extensions'] - self.format = old_request_format - ns = dict([(ext['alias'], ext['namespace']) for ext in exts]) - self.EXTED_PLURALS.update(constants.PLURALS) - return {'plurals': self.EXTED_PLURALS, - 'xmlns': constants.XML_NS_V20, - constants.EXT_NS: ns} - - def content_type(self, _format=None): - """Returns the mime-type for either 'xml' or 'json'. - - Defaults to the currently set format. - """ - _format = _format or self.format - return "application/%s" % (_format) + return serializer.Serializer().deserialize( + data)['body'] def retry_request(self, method, action, body=None, headers=None, params=None): diff --git a/releasenotes/notes/drop-xml-support-41babecb1784d996.yaml b/releasenotes/notes/drop-xml-support-41babecb1784d996.yaml new file mode 100644 index 000000000..23c95e688 --- /dev/null +++ b/releasenotes/notes/drop-xml-support-41babecb1784d996.yaml @@ -0,0 +1,5 @@ +--- +upgrade: + - XML request format support has been removed. +deprecations: + - request-format option is deprecated. From 074fb3d5363ab7df81e67eea3198b086263f788a Mon Sep 17 00:00:00 2001 From: Monty Taylor Date: Mon, 9 Nov 2015 09:52:05 -0500 Subject: [PATCH 314/845] Allow passing version as '2' as well as '2.0' Currently python-neutronclient and python-openstackclient have different views of what OS_NETWORK_API_VERSION should be. Rather than fighting a land war, how about we make both things accept both things, since it's generally understandable by humans that '2' and '2.0' are the same thing. Change-Id: Ibf3c14e15b19c1aca696ce5c693588565a73a524 --- neutronclient/neutron/client.py | 40 ++++++++++++++------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/neutronclient/neutron/client.py b/neutronclient/neutron/client.py index 7236a8a0a..5725d1266 100644 --- a/neutronclient/neutron/client.py +++ b/neutronclient/neutron/client.py @@ -14,14 +14,13 @@ # under the License. # -from neutronclient.common import exceptions from neutronclient.common import utils -from neutronclient.i18n import _ API_NAME = 'network' API_VERSIONS = { '2.0': 'neutronclient.v2_0.client.Client', + '2': 'neutronclient.v2_0.client.Client', } @@ -35,27 +34,22 @@ def make_client(instance): instance.initialize() url = instance._url url = url.rstrip("/") - if '2.0' == instance._api_version[API_NAME]: - client = neutron_client(username=instance._username, - tenant_name=instance._tenant_name, - password=instance._password, - region_name=instance._region_name, - auth_url=instance._auth_url, - endpoint_url=url, - endpoint_type=instance._endpoint_type, - token=instance._token, - auth_strategy=instance._auth_strategy, - insecure=instance._insecure, - ca_cert=instance._ca_cert, - retries=instance._retries, - raise_errors=instance._raise_errors, - session=instance._session, - auth=instance._auth) - return client - else: - raise exceptions.UnsupportedVersion(_("API version %s is not " - "supported") % - instance._api_version[API_NAME]) + client = neutron_client(username=instance._username, + tenant_name=instance._tenant_name, + password=instance._password, + region_name=instance._region_name, + auth_url=instance._auth_url, + endpoint_url=url, + endpoint_type=instance._endpoint_type, + token=instance._token, + auth_strategy=instance._auth_strategy, + insecure=instance._insecure, + ca_cert=instance._ca_cert, + retries=instance._retries, + raise_errors=instance._raise_errors, + session=instance._session, + auth=instance._auth) + return client def Client(api_version, *args, **kwargs): From 1774ac3586fdde08416e242cc17e4957813f9d49 Mon Sep 17 00:00:00 2001 From: Manjeet Singh Bhatia Date: Thu, 22 Oct 2015 04:06:25 +0000 Subject: [PATCH 315/845] Allow lower case protocol values currently in neutron-lbaasv2 only upper case protocol values are allowed for listener and pool. This patch will make protocol values case insensitive for pool and listener. Change-Id: I41fb7b538c81eba848b78c071edaf806663063a1 Related-Bug: #1508687 --- neutronclient/common/utils.py | 4 ++++ neutronclient/neutron/v2_0/lb/v2/listener.py | 2 ++ neutronclient/neutron/v2_0/lb/v2/pool.py | 1 + 3 files changed, 7 insertions(+) diff --git a/neutronclient/common/utils.py b/neutronclient/common/utils.py index dc5f7d6b1..388d5e674 100644 --- a/neutronclient/common/utils.py +++ b/neutronclient/common/utils.py @@ -42,6 +42,10 @@ def env(*vars, **kwargs): return kwargs.get('default', '') +def convert_to_uppercase(string): + return string.upper() + + def get_client_class(api_name, version, version_map): """Returns the client class for the requested API version. diff --git a/neutronclient/neutron/v2_0/lb/v2/listener.py b/neutronclient/neutron/v2_0/lb/v2/listener.py index 07509fb69..39e06e4ec 100644 --- a/neutronclient/neutron/v2_0/lb/v2/listener.py +++ b/neutronclient/neutron/v2_0/lb/v2/listener.py @@ -15,6 +15,7 @@ # under the License. # +from neutronclient.common import utils from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 @@ -82,6 +83,7 @@ def add_known_arguments(self, parser): '--protocol', required=True, choices=['TCP', 'HTTP', 'HTTPS', 'TERMINATED_HTTPS'], + type=utils.convert_to_uppercase, help=_('Protocol for the listener.')) parser.add_argument( '--protocol-port', diff --git a/neutronclient/neutron/v2_0/lb/v2/pool.py b/neutronclient/neutron/v2_0/lb/v2/pool.py index c3c218525..ca2b82bc8 100644 --- a/neutronclient/neutron/v2_0/lb/v2/pool.py +++ b/neutronclient/neutron/v2_0/lb/v2/pool.py @@ -82,6 +82,7 @@ def add_known_arguments(self, parser): '--protocol', required=True, choices=['HTTP', 'HTTPS', 'TCP'], + type=utils.convert_to_uppercase, help=_('Protocol for balancing.')) def args2body(self, parsed_args): From 4c96d4a1d5d2eaebcd4fb20d1fd8671f60971543 Mon Sep 17 00:00:00 2001 From: "vikram.choudhary" Date: Fri, 11 Dec 2015 12:30:40 +0530 Subject: [PATCH 316/845] Adding missing headers to the devref documents Change-Id: Ie7f8b8d5751ff378e999287c29c1f3e4714ea64a --- doc/source/devref/cli_option_guideline.rst | 23 +++++++++++++++++- .../devref/client_command_extensions.rst | 23 +++++++++++++++++- doc/source/index.rst | 24 +++++++++++++++++-- doc/source/usage/cli.rst | 23 +++++++++++++++++- doc/source/usage/library.rst | 23 +++++++++++++++++- 5 files changed, 110 insertions(+), 6 deletions(-) diff --git a/doc/source/devref/cli_option_guideline.rst b/doc/source/devref/cli_option_guideline.rst index 0d51ef68a..bd2625d91 100644 --- a/doc/source/devref/cli_option_guideline.rst +++ b/doc/source/devref/cli_option_guideline.rst @@ -1,4 +1,25 @@ -==================== +.. + 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. + + + Convention for heading levels in Neutron devref: + ======= Heading 0 (reserved for the title in a document) + ------- Heading 1 + ~~~~~~~ Heading 2 + +++++++ Heading 3 + ''''''' Heading 4 + (Avoid deeper levels because they do not render well.) + CLI Option Guideline ==================== diff --git a/doc/source/devref/client_command_extensions.rst b/doc/source/devref/client_command_extensions.rst index 79025be39..4b74cdf77 100644 --- a/doc/source/devref/client_command_extensions.rst +++ b/doc/source/devref/client_command_extensions.rst @@ -1,4 +1,25 @@ -================================= +.. + 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. + + + Convention for heading levels in Neutron devref: + ======= Heading 0 (reserved for the title in a document) + ------- Heading 1 + ~~~~~~~ Heading 2 + +++++++ Heading 3 + ''''''' Heading 4 + (Avoid deeper levels because they do not render well.) + Client command extension support ================================= diff --git a/doc/source/index.rst b/doc/source/index.rst index 4ed204949..fb34a0a01 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -1,4 +1,24 @@ -=============================================== +.. + 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. + + Convention for heading levels in Neutron devref: + ======= Heading 0 (reserved for the title in a document) + ------- Heading 1 + ~~~~~~~ Heading 2 + +++++++ Heading 3 + ''''''' Heading 4 + (Avoid deeper levels because they do not render well.) + Python bindings to the OpenStack Networking API =============================================== @@ -7,7 +27,7 @@ This is a client for OpenStack Networking API. There is a :doc:`Python API ` (installed as **neutron**). Each implements the entire OpenStack Networking API. -Using neturonclient +Using neutronclient ------------------- .. toctree:: diff --git a/doc/source/usage/cli.rst b/doc/source/usage/cli.rst index 6977321ca..e898386a2 100644 --- a/doc/source/usage/cli.rst +++ b/doc/source/usage/cli.rst @@ -1,4 +1,25 @@ -====================== +.. + 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. + + + Convention for heading levels in Neutron devref: + ======= Heading 0 (reserved for the title in a document) + ------- Heading 1 + ~~~~~~~ Heading 2 + +++++++ Heading 3 + ''''''' Heading 4 + (Avoid deeper levels because they do not render well.) + Command-line Interface ====================== diff --git a/doc/source/usage/library.rst b/doc/source/usage/library.rst index 8e9a61c00..44ff5a641 100644 --- a/doc/source/usage/library.rst +++ b/doc/source/usage/library.rst @@ -1,4 +1,25 @@ -======================== +.. + 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. + + + Convention for heading levels in Neutron devref: + ======= Heading 0 (reserved for the title in a document) + ------- Heading 1 + ~~~~~~~ Heading 2 + +++++++ Heading 3 + ''''''' Heading 4 + (Avoid deeper levels because they do not render well.) + neutronclient Python API ======================== From a0e20366cf30596a519ce49f2837b3dd575ee8d5 Mon Sep 17 00:00:00 2001 From: Manjeet Singh Bhatia Date: Wed, 9 Dec 2015 19:51:23 +0000 Subject: [PATCH 317/845] Remove nuage plugin from client This remove the nuage code from client Change-Id: I7a3bdbedc429015258e76506da950226cacb07e8 Closes-Bug: #1518643 --- neutronclient/neutron/v2_0/netpartition.py | 52 ------------------ neutronclient/shell.py | 5 -- .../unit/test_cli20_nuage_netpartition.py | 55 ------------------- neutronclient/v2_0/client.py | 24 -------- 4 files changed, 136 deletions(-) delete mode 100644 neutronclient/neutron/v2_0/netpartition.py delete mode 100644 neutronclient/tests/unit/test_cli20_nuage_netpartition.py diff --git a/neutronclient/neutron/v2_0/netpartition.py b/neutronclient/neutron/v2_0/netpartition.py deleted file mode 100644 index dda278218..000000000 --- a/neutronclient/neutron/v2_0/netpartition.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright 2014 Alcatel-Lucent USA Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -from neutronclient.neutron.v2_0 import CreateCommand -from neutronclient.neutron.v2_0 import DeleteCommand -from neutronclient.neutron.v2_0 import ListCommand -from neutronclient.neutron.v2_0 import ShowCommand - - -class ListNetPartition(ListCommand): - """List netpartitions that belong to a given tenant.""" - resource = 'net_partition' - list_columns = ['id', 'name'] - - -class ShowNetPartition(ShowCommand): - """Show information of a given netpartition.""" - - resource = 'net_partition' - - -class CreateNetPartition(CreateCommand): - """Create a netpartition for a given tenant.""" - - resource = 'net_partition' - - def add_known_arguments(self, parser): - parser.add_argument( - 'name', metavar='name', - help='Name of netpartition to create.') - - def args2body(self, parsed_args): - body = {'name': parsed_args.name} - return {'net_partition': body} - - -class DeleteNetPartition(DeleteCommand): - """Delete a given netpartition.""" - - resource = 'net_partition' diff --git a/neutronclient/shell.py b/neutronclient/shell.py index fe8bd97c1..5a34d4613 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -61,7 +61,6 @@ from neutronclient.neutron.v2_0.lb.v2 import pool as lbaas_pool from neutronclient.neutron.v2_0.lb import vip as lb_vip from neutronclient.neutron.v2_0 import metering -from neutronclient.neutron.v2_0 import netpartition from neutronclient.neutron.v2_0 import network from neutronclient.neutron.v2_0.nsx import networkgateway from neutronclient.neutron.v2_0.nsx import qos_queue @@ -344,10 +343,6 @@ class BashCompletionCommand(openstack_command.OpenStackCommand): 'meter-label-rule-list': metering.ListMeteringLabelRule, 'meter-label-rule-show': metering.ShowMeteringLabelRule, 'meter-label-rule-delete': metering.DeleteMeteringLabelRule, - 'nuage-netpartition-list': netpartition.ListNetPartition, - 'nuage-netpartition-show': netpartition.ShowNetPartition, - 'nuage-netpartition-create': netpartition.CreateNetPartition, - 'nuage-netpartition-delete': netpartition.DeleteNetPartition, 'rbac-create': rbac.CreateRBACPolicy, 'rbac-update': rbac.UpdateRBACPolicy, 'rbac-list': rbac.ListRBACPolicy, diff --git a/neutronclient/tests/unit/test_cli20_nuage_netpartition.py b/neutronclient/tests/unit/test_cli20_nuage_netpartition.py deleted file mode 100644 index fc26979cf..000000000 --- a/neutronclient/tests/unit/test_cli20_nuage_netpartition.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright 2014 Alcatel-Lucent USA Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from neutronclient.neutron.v2_0 import netpartition -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20NetPartitionJSON(test_cli20.CLITestV20Base): - resource = 'net_partition' - non_admin_status_resources = ['net_partition'] - - def test_create_netpartition(self): - cmd = netpartition.CreateNetPartition(test_cli20.MyApp(sys.stdout), - None) - name = 'myname' - myid = 'myid' - args = [name, ] - position_names = ['name', ] - position_values = [name, ] - self._test_create_resource(self.resource, cmd, name, myid, args, - position_names, position_values) - - def test_list_netpartitions(self): - resources = '%ss' % self.resource - cmd = netpartition.ListNetPartition(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, True) - - def test_show_netpartition(self): - cmd = netpartition.ShowNetPartition(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(self.resource, cmd, self.test_id, args, - ['id', 'name']) - - def test_delete_netpartition(self): - cmd = netpartition.DeleteNetPartition(test_cli20.MyApp(sys.stdout), - None) - myid = 'myid' - args = [myid] - self._test_delete_resource(self.resource, cmd, myid, args) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 3c3c84af4..12bf2afe7 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -394,8 +394,6 @@ class Client(ClientBase): firewall_policy_remove_path = "/fw/firewall_policies/%s/remove_rule" firewalls_path = "/fw/firewalls" firewall_path = "/fw/firewalls/%s" - net_partitions_path = "/net-partitions" - net_partition_path = "/net-partitions/%s" rbac_policies_path = "/rbac-policies" rbac_policy_path = "/rbac-policies/%s" qos_policies_path = "/qos/policies" @@ -435,7 +433,6 @@ class Client(ClientBase): 'firewalls': 'firewall', 'metering_labels': 'metering_label', 'metering_label_rules': 'metering_label_rule', - 'net_partitions': 'net_partition', 'loadbalancers': 'loadbalancer', 'listeners': 'listener', 'lbaas_pools': 'lbaas_pool', @@ -1524,27 +1521,6 @@ def show_metering_label_rule(self, metering_label_rule, **_params): return self.get(self.metering_label_rule_path % (metering_label_rule), params=_params) - @APIParamsCall - def list_net_partitions(self, **params): - """Fetch a list of all network partitions for a tenant.""" - return self.get(self.net_partitions_path, params=params) - - @APIParamsCall - def show_net_partition(self, netpartition, **params): - """Fetch a network partition.""" - return self.get(self.net_partition_path % (netpartition), - params=params) - - @APIParamsCall - def create_net_partition(self, body=None): - """Create a network partition.""" - return self.post(self.net_partitions_path, body=body) - - @APIParamsCall - def delete_net_partition(self, netpartition): - """Delete the network partition.""" - return self.delete(self.net_partition_path % netpartition) - @APIParamsCall def create_rbac_policy(self, body=None): """Create a new RBAC policy.""" From 35e70932439bc204a52a6ee31bbe542d1da22247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Nov=C3=BD?= Date: Fri, 11 Dec 2015 23:11:57 +0100 Subject: [PATCH 318/845] Deprecated tox -downloadcache option removed Caching is enabled by default from pip version 6.0 More info: https://testrun.org/tox/latest/config.html#confval-downloadcache=path https://pip.pypa.io/en/stable/reference/pip_install/#caching Change-Id: I25b5b1d463060e821bdc15be03100347cc5f4889 --- tox.ini | 3 --- 1 file changed, 3 deletions(-) diff --git a/tox.ini b/tox.ini index 8dce7dcd3..d0a53a443 100644 --- a/tox.ini +++ b/tox.ini @@ -41,9 +41,6 @@ commands = sphinx-build -W -b html doc/source doc/build/html [testenv:releasenotes] commands = sphinx-build -a -E -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html -[tox:jenkins] -downloadcache = ~/cache/pip - [flake8] # H405 multi line docstring summary not separated with an empty line # (mutli line docstring is used in unit tests frequently) From ef5bd93d73e06a722bf79f75dc4e7c79060cee23 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Fri, 11 Dec 2015 22:53:38 +0000 Subject: [PATCH 319/845] Updated from global requirements Change-Id: If16ac00d9e2c727c918438917c170ae88d08aa7f --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4575da343..67d0df441 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ iso8601>=0.1.9 netaddr!=0.7.16,>=0.7.12 oslo.i18n>=1.5.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 -oslo.utils>=2.8.0 # Apache-2.0 +oslo.utils!=3.1.0,>=2.8.0 # Apache-2.0 os-client-config!=1.6.2,>=1.4.0 keystoneauth1>=2.1.0 requests>=2.8.1 From ec4e4827317f5d9e2b5e4bc7f0774664c098da70 Mon Sep 17 00:00:00 2001 From: shu-mutou Date: Thu, 3 Dec 2015 15:40:14 +0900 Subject: [PATCH 320/845] Fix H405 violations There are a lot of H405 violation codes. We need to fix the code for avoiding more violations. Change-Id: Iaa83626b81a3d8369b16e2b4f394e2bc4628998a Closes-Bug: #1521899 --- neutronclient/common/exceptions.py | 4 +- neutronclient/neutron/v2_0/floatingip.py | 3 +- neutronclient/shell.py | 8 +- .../adv-svcs/test_readonly_neutron_vpn.py | 6 +- .../functional/core/test_readonly_neutron.py | 3 +- .../tests/unit/fw/test_cli20_firewall.py | 25 ++-- .../unit/fw/test_cli20_firewallpolicy.py | 34 +++--- .../tests/unit/fw/test_cli20_firewallrule.py | 25 ++-- .../tests/unit/lb/test_cli20_healthmonitor.py | 21 ++-- .../tests/unit/lb/test_cli20_member.py | 21 ++-- .../tests/unit/lb/test_cli20_pool.py | 25 ++-- neutronclient/tests/unit/lb/test_cli20_vip.py | 25 ++-- .../unit/lb/v2/test_cli20_healthmonitor.py | 20 ++-- .../tests/unit/lb/v2/test_cli20_listener.py | 20 ++-- .../unit/lb/v2/test_cli20_loadbalancer.py | 23 ++-- .../tests/unit/lb/v2/test_cli20_member.py | 20 ++-- .../tests/unit/lb/v2/test_cli20_pool.py | 20 ++-- .../tests/unit/qos/test_cli20_policy.py | 29 +++-- .../tests/unit/qos/test_cli20_rule.py | 2 +- .../tests/unit/test_cli20_address_scope.py | 26 ++-- neutronclient/tests/unit/test_cli20_agents.py | 6 +- .../tests/unit/test_cli20_floatingips.py | 29 +++-- .../tests/unit/test_cli20_metering.py | 4 +- .../tests/unit/test_cli20_network.py | 66 +++++----- .../tests/unit/test_cli20_nsx_queue.py | 4 +- neutronclient/tests/unit/test_cli20_port.py | 113 ++++++++---------- neutronclient/tests/unit/test_cli20_rbac.py | 26 ++-- neutronclient/tests/unit/test_cli20_router.py | 80 ++++++------- .../tests/unit/test_cli20_securitygroup.py | 14 +-- neutronclient/tests/unit/test_cli20_subnet.py | 80 ++++++------- .../tests/unit/test_cli20_subnetpool.py | 27 ++--- .../tests/unit/test_client_extension.py | 12 +- neutronclient/tests/unit/test_http.py | 2 +- .../unit/vpn/test_cli20_endpoint_group.py | 25 ++-- .../tests/unit/vpn/test_cli20_ikepolicy.py | 23 ++-- .../vpn/test_cli20_ipsec_site_connection.py | 37 +++--- .../tests/unit/vpn/test_cli20_ipsecpolicy.py | 23 ++-- .../tests/unit/vpn/test_cli20_vpnservice.py | 25 ++-- neutronclient/v2_0/client.py | 22 ++-- tox.ini | 3 - 40 files changed, 462 insertions(+), 519 deletions(-) diff --git a/neutronclient/common/exceptions.py b/neutronclient/common/exceptions.py index ee088c426..e051d5fba 100644 --- a/neutronclient/common/exceptions.py +++ b/neutronclient/common/exceptions.py @@ -219,7 +219,9 @@ class CommandError(NeutronCLIError): class UnsupportedVersion(NeutronCLIError): - """Indicates that the user is trying to use an unsupported version of + """Indicates usage of an unsupported API version + + Indicates that the user is trying to use an unsupported version of the API. """ pass diff --git a/neutronclient/neutron/v2_0/floatingip.py b/neutronclient/neutron/v2_0/floatingip.py index c603d2f28..2d953554b 100644 --- a/neutronclient/neutron/v2_0/floatingip.py +++ b/neutronclient/neutron/v2_0/floatingip.py @@ -124,8 +124,7 @@ def run(self, parsed_args): class DisassociateFloatingIP(neutronV20.NeutronCommand): - """Remove a mapping from a floating IP to a fixed IP. - """ + """Remove a mapping from a floating IP to a fixed IP.""" api = 'network' resource = 'floatingip' diff --git a/neutronclient/shell.py b/neutronclient/shell.py index fe8bd97c1..d496e0758 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -398,7 +398,9 @@ class BashCompletionCommand(openstack_command.OpenStackCommand): class HelpAction(argparse.Action): - """Provide a custom action so the -h and --help options + """Print help message including sub-commands + + Provide a custom action so the -h and --help options to the main app will print a list of the commands. The commands are determined by checking the CommandManager @@ -831,7 +833,9 @@ def run_subcommand(self, argv): return 1 def authenticate_user(self): - """Make sure the user has provided all of the authentication + """Confirm user authentication + + Make sure the user has provided all of the authentication info we need. """ cloud_config = os_client_config.OpenStackConfig().get_one_cloud( diff --git a/neutronclient/tests/functional/adv-svcs/test_readonly_neutron_vpn.py b/neutronclient/tests/functional/adv-svcs/test_readonly_neutron_vpn.py index 172fef017..291e59ad7 100644 --- a/neutronclient/tests/functional/adv-svcs/test_readonly_neutron_vpn.py +++ b/neutronclient/tests/functional/adv-svcs/test_readonly_neutron_vpn.py @@ -15,15 +15,15 @@ class SimpleReadOnlyNeutronVpnClientTest(base.ClientTestBase): - """This is a first pass at a simple read only python-neutronclient test. - This only exercises vpn based client commands that are read only. + """Tests for vpn based client commands that are read only + This is a first pass at a simple read only python-neutronclient test. + This only exercises vpn based client commands that are read only. This should test commands: * as a regular user * as a admin user * with and without optional parameters * initially just check return codes, and later test command outputs - """ def test_neutron_vpn_ikepolicy_list(self): diff --git a/neutronclient/tests/functional/core/test_readonly_neutron.py b/neutronclient/tests/functional/core/test_readonly_neutron.py index aaecdc1f0..353e5637c 100644 --- a/neutronclient/tests/functional/core/test_readonly_neutron.py +++ b/neutronclient/tests/functional/core/test_readonly_neutron.py @@ -20,14 +20,13 @@ class SimpleReadOnlyNeutronClientTest(base.ClientTestBase): """This is a first pass at a simple read only python-neutronclient test. - This only exercises client commands that are read only. + This only exercises client commands that are read only. This should test commands: * as a regular user * as a admin user * with and without optional parameters * initially just check return codes, and later test command outputs - """ def test_admin_fake_action(self): diff --git a/neutronclient/tests/unit/fw/test_cli20_firewall.py b/neutronclient/tests/unit/fw/test_cli20_firewall.py index c764139f7..ece9ac84d 100644 --- a/neutronclient/tests/unit/fw/test_cli20_firewall.py +++ b/neutronclient/tests/unit/fw/test_cli20_firewall.py @@ -23,7 +23,7 @@ class CLITestV20FirewallJSON(test_cli20.CLITestV20Base): def test_create_firewall_with_mandatory_params(self): - """firewall-create with mandatory (none) params.""" + # firewall-create with mandatory (none) params. resource = 'firewall' cmd = firewall.CreateFirewall(test_cli20.MyApp(sys.stdout), None) name = '' @@ -38,7 +38,7 @@ def test_create_firewall_with_mandatory_params(self): admin_state_up=True, tenant_id=tenant_id) def test_create_firewall_with_all_params(self): - """firewall-create with all params set.""" + # firewall-create with all params set. resource = 'firewall' cmd = firewall.CreateFirewall(test_cli20.MyApp(sys.stdout), None) name = 'my-name' @@ -72,21 +72,20 @@ def test_create_firewall_with_routers(self): position_names, position_values) def test_list_firewalls(self): - """firewall-list.""" + # firewall-list. resources = "firewalls" cmd = firewall.ListFirewall(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, True) def test_list_firewalls_pagination(self): - """firewall-list with pagination.""" + # firewall-list with pagination. resources = "firewalls" cmd = firewall.ListFirewall(test_cli20.MyApp(sys.stdout), None) self._test_list_resources_with_pagination(resources, cmd) def test_list_firewalls_sort(self): - """sorted list: firewall-list --sort-key name --sort-key id - --sort-key asc --sort-key desc - """ + # sorted list: firewall-list --sort-key name --sort-key id + # --sort-key asc --sort-key desc resources = "firewalls" cmd = firewall.ListFirewall(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, @@ -94,20 +93,20 @@ def test_list_firewalls_sort(self): sort_dir=["asc", "desc"]) def test_list_firewalls_limit(self): - """size (1000) limited list: firewall-list -P.""" + # size (1000) limited list: firewall-list -P. resources = "firewalls" cmd = firewall.ListFirewall(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, page_size=1000) def test_show_firewall_id(self): - """firewall-show test_id.""" + # firewall-show test_id. resource = 'firewall' cmd = firewall.ShowFirewall(test_cli20.MyApp(sys.stdout), None) args = ['--fields', 'id', self.test_id] self._test_show_resource(resource, cmd, self.test_id, args, ['id']) def test_show_firewall_id_name(self): - """firewall-show.""" + # firewall-show. resource = 'firewall' cmd = firewall.ShowFirewall(test_cli20.MyApp(sys.stdout), None) args = ['--fields', 'id', '--fields', 'name', self.test_id] @@ -115,7 +114,7 @@ def test_show_firewall_id_name(self): args, ['id', 'name']) def test_update_firewall(self): - """firewall-update myid --name newname --tags a b.""" + # firewall-update myid --name newname --tags a b. resource = 'firewall' cmd = firewall.UpdateFirewall(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', @@ -123,7 +122,7 @@ def test_update_firewall(self): {'name': 'newname', }) def test_update_firewall_using_policy_name(self): - """firewall-update myid --policy newpolicy.""" + # firewall-update myid --policy newpolicy. resource = 'firewall' cmd = firewall.UpdateFirewall(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', @@ -155,7 +154,7 @@ def test_update_firewall_with_bad_router_options(self): ['myid', '--no-routers', '--router', 'fake-id'], {}) def test_delete_firewall(self): - """firewall-delete my-id.""" + # firewall-delete my-id. resource = 'firewall' cmd = firewall.DeleteFirewall(test_cli20.MyApp(sys.stdout), None) my_id = 'my-id' diff --git a/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py b/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py index b1e20c675..3d3241e1b 100644 --- a/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py +++ b/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py @@ -28,7 +28,7 @@ def setUp(self): super(CLITestV20FirewallPolicyJSON, self).setUp() def test_create_firewall_policy_with_mandatory_params(self): - """firewall-policy-create with mandatory (none) params only.""" + # firewall-policy-create with mandatory (none) params only. resource = 'firewall_policy' cmd = firewallpolicy.CreateFirewallPolicy(test_cli20.MyApp(sys.stdout), None) @@ -45,7 +45,7 @@ def test_create_firewall_policy_with_mandatory_params(self): admin_state_up=True, tenant_id=tenant_id) def test_create_firewall_policy_with_all_params(self): - """firewall-policy-create with rule param of misc format.""" + # firewall-policy-create with rule param of misc format. resource = 'firewall_policy' cmd = firewallpolicy.CreateFirewallPolicy(test_cli20.MyApp(sys.stdout), None) @@ -74,23 +74,22 @@ def test_create_firewall_policy_with_all_params(self): tenant_id=tenant_id) def test_list_firewall_policies(self): - """firewall-policy-list.""" + # firewall-policy-list. resources = "firewall_policies" cmd = firewallpolicy.ListFirewallPolicy(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, True) def test_list_firewall_policies_pagination(self): - """firewall-policy-list.""" + # firewall-policy-list.""" resources = "firewall_policies" cmd = firewallpolicy.ListFirewallPolicy(test_cli20.MyApp(sys.stdout), None) self._test_list_resources_with_pagination(resources, cmd) def test_list_firewall_policies_sort(self): - """sorted list: firewall-policy-list --sort-key name --sort-key id - --sort-key asc --sort-key desc - """ + # sorted list: firewall-policy-list --sort-key name --sort-key id + # --sort-key asc --sort-key desc resources = "firewall_policies" cmd = firewallpolicy.ListFirewallPolicy(test_cli20.MyApp(sys.stdout), None) @@ -99,14 +98,14 @@ def test_list_firewall_policies_sort(self): sort_dir=["asc", "desc"]) def test_list_firewall_policies_limit(self): - """size (1000) limited list: firewall-policy-list -P.""" + # size (1000) limited list: firewall-policy-list -P. resources = "firewall_policies" cmd = firewallpolicy.ListFirewallPolicy(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, page_size=1000) def test_show_firewall_policy_id(self): - """firewall-policy-show test_id.""" + # firewall-policy-show test_id. resource = 'firewall_policy' cmd = firewallpolicy.ShowFirewallPolicy(test_cli20.MyApp(sys.stdout), None) @@ -114,7 +113,7 @@ def test_show_firewall_policy_id(self): self._test_show_resource(resource, cmd, self.test_id, args, ['id']) def test_show_firewall_policy_id_name(self): - """firewall-policy-show.""" + # firewall-policy-show. resource = 'firewall_policy' cmd = firewallpolicy.ShowFirewallPolicy(test_cli20.MyApp(sys.stdout), None) @@ -123,7 +122,7 @@ def test_show_firewall_policy_id_name(self): args, ['id', 'name']) def test_update_firewall_policy(self): - """firewall-policy-update myid --name newname.""" + # firewall-policy-update myid --name newname. resource = 'firewall_policy' cmd = firewallpolicy.UpdateFirewallPolicy(test_cli20.MyApp(sys.stdout), None) @@ -132,7 +131,7 @@ def test_update_firewall_policy(self): {'name': 'newname', }) def test_update_firewall_policy_with_rules(self): - """firewall-policy-update myid --firewall-rules "rule1 rule2".""" + # firewall-policy-update myid --firewall-rules "rule1 rule2". resource = 'firewall_policy' cmd = firewallpolicy.UpdateFirewallPolicy(test_cli20.MyApp(sys.stdout), None) @@ -144,7 +143,7 @@ def test_update_firewall_policy_with_rules(self): {'firewall_rules': firewall_rules_res, }) def test_delete_firewall_policy(self): - """firewall-policy-delete my-id.""" + # firewall-policy-delete my-id. resource = 'firewall_policy' cmd = firewallpolicy.DeleteFirewallPolicy(test_cli20.MyApp(sys.stdout), None) @@ -153,10 +152,8 @@ def test_delete_firewall_policy(self): self._test_delete_resource(resource, cmd, my_id, args) def test_insert_firewall_rule(self): - """firewall-policy-insert-rule myid newruleid - --insert-before ruleAid - --insert-after ruleBid - """ + # firewall-policy-insert-rule myid newruleid --insert-before ruleAid + # --insert-after ruleBid resource = 'firewall_policy' cmd = firewallpolicy.FirewallPolicyInsertRule( test_cli20.MyApp(sys.stdout), @@ -189,8 +186,7 @@ def test_insert_firewall_rule(self): self.mox.UnsetStubs() def test_remove_firewall_rule(self): - """firewall-policy-remove-rule myid ruleid - """ + # firewall-policy-remove-rule myid ruleid resource = 'firewall_policy' cmd = firewallpolicy.FirewallPolicyRemoveRule( test_cli20.MyApp(sys.stdout), diff --git a/neutronclient/tests/unit/fw/test_cli20_firewallrule.py b/neutronclient/tests/unit/fw/test_cli20_firewallrule.py index 905c81c94..10290b631 100644 --- a/neutronclient/tests/unit/fw/test_cli20_firewallrule.py +++ b/neutronclient/tests/unit/fw/test_cli20_firewallrule.py @@ -23,7 +23,7 @@ class CLITestV20FirewallRuleJSON(test_cli20.CLITestV20Base): def _test_create_firewall_rule_with_mandatory_params(self, enabled): - """firewall-rule-create with mandatory (none) params only.""" + # firewall-rule-create with mandatory (none) params only. resource = 'firewall_rule' cmd = firewallrule.CreateFirewallRule(test_cli20.MyApp(sys.stdout), None) @@ -57,7 +57,7 @@ def test_create_disabled_firewall_rule_with_mandatory_params(self): self._test_create_firewall_rule_with_mandatory_params(enabled='False') def _setup_create_firewall_rule_with_all_params(self, protocol='tcp'): - """firewall-rule-create with all params set.""" + # firewall-rule-create with all params set. resource = 'firewall_rule' cmd = firewallrule.CreateFirewallRule(test_cli20.MyApp(sys.stdout), None) @@ -104,23 +104,22 @@ def test_create_firewall_rule_with_proto_any(self): self._setup_create_firewall_rule_with_all_params(protocol='any') def test_list_firewall_rules(self): - """firewall-rule-list.""" + # firewall-rule-list. resources = "firewall_rules" cmd = firewallrule.ListFirewallRule(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, True) def test_list_firewall_rules_pagination(self): - """firewall-rule-list.""" + # firewall-rule-list. resources = "firewall_rules" cmd = firewallrule.ListFirewallRule(test_cli20.MyApp(sys.stdout), None) self._test_list_resources_with_pagination(resources, cmd) def test_list_firewall_rules_sort(self): - """firewall-rule-list --sort-key name --sort-key id --sort-key asc - --sort-key desc - """ + # firewall-rule-list --sort-key name --sort-key id --sort-key asc + # --sort-key desc resources = "firewall_rules" cmd = firewallrule.ListFirewallRule(test_cli20.MyApp(sys.stdout), None) @@ -129,14 +128,14 @@ def test_list_firewall_rules_sort(self): sort_dir=["asc", "desc"]) def test_list_firewall_rules_limit(self): - """firewall-rule-list -P.""" + # firewall-rule-list -P.""" resources = "firewall_rules" cmd = firewallrule.ListFirewallRule(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, page_size=1000) def test_show_firewall_rule_id(self): - """firewall-rule-show test_id.""" + # firewall-rule-show test_id. resource = 'firewall_rule' cmd = firewallrule.ShowFirewallRule(test_cli20.MyApp(sys.stdout), None) @@ -144,7 +143,7 @@ def test_show_firewall_rule_id(self): self._test_show_resource(resource, cmd, self.test_id, args, ['id']) def test_show_firewall_rule_id_name(self): - """firewall-rule-show.""" + # firewall-rule-show. resource = 'firewall_rule' cmd = firewallrule.ShowFirewallRule(test_cli20.MyApp(sys.stdout), None) @@ -153,7 +152,7 @@ def test_show_firewall_rule_id_name(self): args, ['id', 'name']) def test_update_firewall_rule(self): - """firewall-rule-update myid --name newname.""" + # firewall-rule-update myid --name newname. resource = 'firewall_rule' cmd = firewallrule.UpdateFirewallRule(test_cli20.MyApp(sys.stdout), None) @@ -162,7 +161,7 @@ def test_update_firewall_rule(self): {'name': 'newname', }) def test_update_firewall_rule_protocol(self): - """firewall-rule-update myid --protocol any.""" + # firewall-rule-update myid --protocol any. resource = 'firewall_rule' cmd = firewallrule.UpdateFirewallRule(test_cli20.MyApp(sys.stdout), None) @@ -171,7 +170,7 @@ def test_update_firewall_rule_protocol(self): {'protocol': None, }) def test_delete_firewall_rule(self): - """firewall-rule-delete my-id.""" + # firewall-rule-delete my-id. resource = 'firewall_rule' cmd = firewallrule.DeleteFirewallRule(test_cli20.MyApp(sys.stdout), None) diff --git a/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py b/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py index 6895b5ee5..999116e2a 100644 --- a/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py +++ b/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py @@ -24,7 +24,7 @@ class CLITestV20LbHealthmonitorJSON(test_cli20.CLITestV20Base): def test_create_healthmonitor_with_mandatory_params(self): - """lb-healthmonitor-create with mandatory params only.""" + # lb-healthmonitor-create with mandatory params only. resource = 'health_monitor' cmd = healthmonitor.CreateHealthMonitor(test_cli20.MyApp(sys.stdout), None) @@ -49,7 +49,7 @@ def test_create_healthmonitor_with_mandatory_params(self): position_names, position_values) def test_create_healthmonitor_with_all_params(self): - """lb-healthmonitor-create with all params set.""" + # lb-healthmonitor-create with all params set. resource = 'health_monitor' cmd = healthmonitor.CreateHealthMonitor(test_cli20.MyApp(sys.stdout), None) @@ -84,23 +84,22 @@ def test_create_healthmonitor_with_all_params(self): position_names, position_values) def test_list_healthmonitors(self): - """lb-healthmonitor-list.""" + # lb-healthmonitor-list. resources = "health_monitors" cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, True) def test_list_healthmonitors_pagination(self): - """lb-healthmonitor-list.""" + # lb-healthmonitor-list. resources = "health_monitors" cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout), None) self._test_list_resources_with_pagination(resources, cmd) def test_list_healthmonitors_sort(self): - """lb-healthmonitor-list --sort-key name --sort-key id --sort-key asc - --sort-key desc - """ + # lb-healthmonitor-list --sort-key name --sort-key id --sort-key asc + # --sort-key desc resources = "health_monitors" cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout), None) @@ -109,14 +108,14 @@ def test_list_healthmonitors_sort(self): sort_dir=["asc", "desc"]) def test_list_healthmonitors_limit(self): - """lb-healthmonitor-list -P.""" + # lb-healthmonitor-list -P. resources = "health_monitors" cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, page_size=1000) def test_show_healthmonitor_id(self): - """lb-healthmonitor-show test_id.""" + # lb-healthmonitor-show test_id. resource = 'health_monitor' cmd = healthmonitor.ShowHealthMonitor(test_cli20.MyApp(sys.stdout), None) @@ -124,7 +123,7 @@ def test_show_healthmonitor_id(self): self._test_show_resource(resource, cmd, self.test_id, args, ['id']) def test_update_health_monitor(self): - """lb-healthmonitor-update myid --name myname --tags a b.""" + # lb-healthmonitor-update myid --name myname --tags a b. resource = 'health_monitor' cmd = healthmonitor.UpdateHealthMonitor(test_cli20.MyApp(sys.stdout), None) @@ -133,7 +132,7 @@ def test_update_health_monitor(self): {'timeout': '5', }) def test_delete_healthmonitor(self): - """lb-healthmonitor-delete my-id.""" + # lb-healthmonitor-delete my-id.""" resource = 'health_monitor' cmd = healthmonitor.DeleteHealthMonitor(test_cli20.MyApp(sys.stdout), None) diff --git a/neutronclient/tests/unit/lb/test_cli20_member.py b/neutronclient/tests/unit/lb/test_cli20_member.py index 7942432a4..8ce2a0f09 100644 --- a/neutronclient/tests/unit/lb/test_cli20_member.py +++ b/neutronclient/tests/unit/lb/test_cli20_member.py @@ -25,7 +25,7 @@ def setUp(self): super(CLITestV20LbMemberJSON, self).setUp(plurals={'tags': 'tag'}) def test_create_member(self): - """lb-member-create with mandatory params only.""" + # lb-member-create with mandatory params only. resource = 'member' cmd = member.CreateMember(test_cli20.MyApp(sys.stdout), None) address = '10.0.0.1' @@ -43,7 +43,7 @@ def test_create_member(self): admin_state_up=None) def test_create_member_all_params(self): - """lb-member-create with all available params.""" + # lb-member-create with all available params. resource = 'member' cmd = member.CreateMember(test_cli20.MyApp(sys.stdout), None) address = '10.0.0.1' @@ -67,21 +67,20 @@ def test_create_member_all_params(self): admin_state_up=None) def test_list_members(self): - """lb-member-list.""" + # lb-member-list. resources = "members" cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, True) def test_list_members_pagination(self): - """lb-member-list.""" + # lb-member-list. resources = "members" cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None) self._test_list_resources_with_pagination(resources, cmd) def test_list_members_sort(self): - """lb-member-list --sort-key name --sort-key id --sort-key asc - --sort-key desc - """ + # lb-member-list --sort-key name --sort-key id --sort-key asc + # --sort-key desc resources = "members" cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, @@ -89,20 +88,20 @@ def test_list_members_sort(self): sort_dir=["asc", "desc"]) def test_list_members_limit(self): - """lb-member-list -P.""" + # lb-member-list -P. resources = "members" cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, page_size=1000) def test_show_member_id(self): - """lb-member-show test_id.""" + # lb-member-show test_id. resource = 'member' cmd = member.ShowMember(test_cli20.MyApp(sys.stdout), None) args = ['--fields', 'id', self.test_id] self._test_show_resource(resource, cmd, self.test_id, args, ['id']) def test_update_member(self): - """lb-member-update myid --name myname --tags a b.""" + # lb-member-update myid --name myname --tags a b. resource = 'member' cmd = member.UpdateMember(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', @@ -111,7 +110,7 @@ def test_update_member(self): {'name': 'myname', 'tags': ['a', 'b'], }) def test_delete_member(self): - """lb-member-delete my-id.""" + # lb-member-delete my-id. resource = 'member' cmd = member.DeleteMember(test_cli20.MyApp(sys.stdout), None) my_id = 'my-id' diff --git a/neutronclient/tests/unit/lb/test_cli20_pool.py b/neutronclient/tests/unit/lb/test_cli20_pool.py index 82fa42d2d..9a08f66fe 100644 --- a/neutronclient/tests/unit/lb/test_cli20_pool.py +++ b/neutronclient/tests/unit/lb/test_cli20_pool.py @@ -25,7 +25,7 @@ class CLITestV20LbPoolJSON(test_cli20.CLITestV20Base): def test_create_pool_with_mandatory_params(self): - """lb-pool-create with mandatory params only.""" + # lb-pool-create with mandatory params only. resource = 'pool' cmd = pool.CreatePool(test_cli20.MyApp(sys.stdout), None) name = 'my-name' @@ -47,7 +47,7 @@ def test_create_pool_with_mandatory_params(self): position_names, position_values) def test_create_pool_with_all_params(self): - """lb-pool-create with all params set.""" + # lb-pool-create with all params set. resource = 'pool' cmd = pool.CreatePool(test_cli20.MyApp(sys.stdout), None) name = 'my-name' @@ -74,21 +74,20 @@ def test_create_pool_with_all_params(self): position_names, position_values) def test_list_pools(self): - """lb-pool-list.""" + # lb-pool-list. resources = "pools" cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, True) def test_list_pools_pagination(self): - """lb-pool-list.""" + # lb-pool-list. resources = "pools" cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None) self._test_list_resources_with_pagination(resources, cmd) def test_list_pools_sort(self): - """lb-pool-list --sort-key name --sort-key id --sort-key asc - --sort-key desc - """ + # lb-pool-list --sort-key name --sort-key id --sort-key asc + # --sort-key desc resources = "pools" cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, @@ -96,20 +95,20 @@ def test_list_pools_sort(self): sort_dir=["asc", "desc"]) def test_list_pools_limit(self): - """lb-pool-list -P.""" + # lb-pool-list -P. resources = "pools" cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, page_size=1000) def test_show_pool_id(self): - """lb-pool-show test_id.""" + # lb-pool-show test_id. resource = 'pool' cmd = pool.ShowPool(test_cli20.MyApp(sys.stdout), None) args = ['--fields', 'id', self.test_id] self._test_show_resource(resource, cmd, self.test_id, args, ['id']) def test_show_pool_id_name(self): - """lb-pool-show.""" + # lb-pool-show. resource = 'pool' cmd = pool.ShowPool(test_cli20.MyApp(sys.stdout), None) args = ['--fields', 'id', '--fields', 'name', self.test_id] @@ -117,7 +116,7 @@ def test_show_pool_id_name(self): args, ['id', 'name']) def test_update_pool(self): - """lb-pool-update myid --name newname --tags a b.""" + # lb-pool-update myid --name newname --tags a b. resource = 'pool' cmd = pool.UpdatePool(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', @@ -125,7 +124,7 @@ def test_update_pool(self): {'name': 'newname', }) def test_delete_pool(self): - """lb-pool-delete my-id.""" + # lb-pool-delete my-id. resource = 'pool' cmd = pool.DeletePool(test_cli20.MyApp(sys.stdout), None) my_id = 'my-id' @@ -133,7 +132,7 @@ def test_delete_pool(self): self._test_delete_resource(resource, cmd, my_id, args) def test_retrieve_pool_stats(self): - """lb-pool-stats test_id.""" + # lb-pool-stats test_id. resource = 'pool' cmd = pool.RetrievePoolStats(test_cli20.MyApp(sys.stdout), None) my_id = self.test_id diff --git a/neutronclient/tests/unit/lb/test_cli20_vip.py b/neutronclient/tests/unit/lb/test_cli20_vip.py index 3f2f34594..b08de447e 100644 --- a/neutronclient/tests/unit/lb/test_cli20_vip.py +++ b/neutronclient/tests/unit/lb/test_cli20_vip.py @@ -25,7 +25,7 @@ def setUp(self): super(CLITestV20LbVipJSON, self).setUp(plurals={'tags': 'tag'}) def test_create_vip_with_mandatory_params(self): - """lb-vip-create with all mandatory params.""" + # lb-vip-create with all mandatory params. resource = 'vip' cmd = vip.CreateVip(test_cli20.MyApp(sys.stdout), None) pool_id = 'my-pool-id' @@ -50,7 +50,7 @@ def test_create_vip_with_mandatory_params(self): admin_state_up=True) def test_create_vip_with_all_params(self): - """lb-vip-create with all params.""" + # lb-vip-create with all params. resource = 'vip' cmd = vip.CreateVip(test_cli20.MyApp(sys.stdout), None) pool_id = 'my-pool-id' @@ -86,7 +86,7 @@ def test_create_vip_with_all_params(self): position_names, position_values) def test_create_vip_with_session_persistence_params(self): - """lb-vip-create with mandatory and session-persistence params.""" + # lb-vip-create with mandatory and session-persistence params. resource = 'vip' cmd = vip.CreateVip(test_cli20.MyApp(sys.stdout), None) pool_id = 'my-pool-id' @@ -120,21 +120,20 @@ def test_create_vip_with_session_persistence_params(self): admin_state_up=True, extra_body=extra_body) def test_list_vips(self): - """lb-vip-list.""" + # lb-vip-list. resources = "vips" cmd = vip.ListVip(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, True) def test_list_vips_pagination(self): - """lb-vip-list.""" + # lb-vip-list. resources = "vips" cmd = vip.ListVip(test_cli20.MyApp(sys.stdout), None) self._test_list_resources_with_pagination(resources, cmd) def test_list_vips_sort(self): - """lb-vip-list --sort-key name --sort-key id --sort-key asc - --sort-key desc - """ + # lb-vip-list --sort-key name --sort-key id --sort-key asc + # --sort-key desc resources = "vips" cmd = vip.ListVip(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, @@ -142,20 +141,20 @@ def test_list_vips_sort(self): sort_dir=["asc", "desc"]) def test_list_vips_limit(self): - """lb-vip-list -P.""" + # lb-vip-list -P. resources = "vips" cmd = vip.ListVip(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, page_size=1000) def test_show_vip_id(self): - """lb-vip-show test_id.""" + # lb-vip-show test_id. resource = 'vip' cmd = vip.ShowVip(test_cli20.MyApp(sys.stdout), None) args = ['--fields', 'id', self.test_id] self._test_show_resource(resource, cmd, self.test_id, args, ['id']) def test_show_vip_id_name(self): - """lb-vip-show.""" + # lb-vip-show. resource = 'vip' cmd = vip.ShowVip(test_cli20.MyApp(sys.stdout), None) args = ['--fields', 'id', '--fields', 'name', self.test_id] @@ -163,7 +162,7 @@ def test_show_vip_id_name(self): args, ['id', 'name']) def test_update_vip(self): - """lb-vip-update myid --name myname --tags a b.""" + # lb-vip-update myid --name myname --tags a b. resource = 'vip' cmd = vip.UpdateVip(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', @@ -199,7 +198,7 @@ def test_update_vip_with_session_persistence_and_name(self): self._test_update_resource(resource, cmd, 'myid', args, body) def test_delete_vip(self): - """lb-vip-delete my-id.""" + # lb-vip-delete my-id. resource = 'vip' cmd = vip.DeleteVip(test_cli20.MyApp(sys.stdout), None) my_id = 'my-id' diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py b/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py index 415e29f97..ec07088a0 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py @@ -23,7 +23,7 @@ class CLITestV20LbHealthMonitorJSON(test_cli20.CLITestV20Base): def test_create_healthmonitor_with_mandatory_params(self): - """lbaas-healthmonitor-create with mandatory params only.""" + # lbaas-healthmonitor-create with mandatory params only. resource = 'healthmonitor' cmd_resource = 'lbaas_healthmonitor' cmd = healthmonitor.CreateHealthMonitor(test_cli20.MyApp(sys.stdout), @@ -43,7 +43,7 @@ def test_create_healthmonitor_with_mandatory_params(self): cmd_resource=cmd_resource) def test_create_healthmonitor_with_all_params(self): - """lbaas-healthmonitor-create with all params set.""" + # lbaas-healthmonitor-create with all params set. resource = 'healthmonitor' cmd_resource = 'lbaas_healthmonitor' cmd = healthmonitor.CreateHealthMonitor(test_cli20.MyApp(sys.stdout), @@ -71,7 +71,7 @@ def test_create_healthmonitor_with_all_params(self): cmd_resource=cmd_resource) def test_list_healthmonitors(self): - """lbaas-healthmonitor-list.""" + # lbaas-healthmonitor-list. resources = 'healthmonitors' cmd_resources = 'lbaas_healthmonitors' cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout), @@ -80,7 +80,7 @@ def test_list_healthmonitors(self): cmd_resources=cmd_resources) def test_list_healthmonitors_pagination(self): - """lbaas-healthmonitor-list with pagination.""" + # lbaas-healthmonitor-list with pagination. resources = 'healthmonitors' cmd_resources = 'lbaas_healthmonitors' cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout), @@ -89,7 +89,7 @@ def test_list_healthmonitors_pagination(self): cmd_resources=cmd_resources) def test_list_healthmonitors_sort(self): - """lbaas-healthmonitor-list --sort-key id --sort-key asc.""" + # lbaas-healthmonitor-list --sort-key id --sort-key asc. resources = 'healthmonitors' cmd_resources = 'lbaas_healthmonitors' cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout), @@ -98,7 +98,7 @@ def test_list_healthmonitors_sort(self): cmd_resources=cmd_resources) def test_list_healthmonitors_limit(self): - """lbaas-healthmonitor-list -P.""" + # lbaas-healthmonitor-list -P. resources = 'healthmonitors' cmd_resources = 'lbaas_healthmonitors' cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout), @@ -107,7 +107,7 @@ def test_list_healthmonitors_limit(self): cmd_resources=cmd_resources) def test_show_healthmonitor_id(self): - """lbaas-healthmonitor-show test_id.""" + # lbaas-healthmonitor-show test_id. resource = 'healthmonitor' cmd_resource = 'lbaas_healthmonitor' cmd = healthmonitor.ShowHealthMonitor(test_cli20.MyApp(sys.stdout), @@ -117,7 +117,7 @@ def test_show_healthmonitor_id(self): cmd_resource=cmd_resource) def test_show_healthmonitor_id_name(self): - """lbaas-healthmonitor-show.""" + # lbaas-healthmonitor-show. resource = 'healthmonitor' cmd_resource = 'lbaas_healthmonitor' cmd = healthmonitor.ShowHealthMonitor(test_cli20.MyApp(sys.stdout), @@ -128,7 +128,7 @@ def test_show_healthmonitor_id_name(self): cmd_resource=cmd_resource) def test_update_healthmonitor(self): - """lbaas-healthmonitor-update myid --name newname.""" + # lbaas-healthmonitor-update myid --name newname. resource = 'healthmonitor' cmd_resource = 'lbaas_healthmonitor' cmd = healthmonitor.UpdateHealthMonitor(test_cli20.MyApp(sys.stdout), @@ -139,7 +139,7 @@ def test_update_healthmonitor(self): cmd_resource=cmd_resource) def test_delete_healthmonitor(self): - """lbaas-healthmonitor-delete my-id.""" + # lbaas-healthmonitor-delete my-id. resource = 'healthmonitor' cmd_resource = 'lbaas_healthmonitor' cmd = healthmonitor.DeleteHealthMonitor(test_cli20.MyApp(sys.stdout), diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_listener.py b/neutronclient/tests/unit/lb/v2/test_cli20_listener.py index e290ac823..dae1f64bb 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_listener.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_listener.py @@ -23,7 +23,7 @@ class CLITestV20LbListenerJSON(test_cli20.CLITestV20Base): def test_create_listener_with_mandatory_params(self): - """lbaas-listener-create with mandatory params only.""" + # lbaas-listener-create with mandatory params only. resource = 'listener' cmd_resource = 'lbaas_listener' cmd = listener.CreateListener(test_cli20.MyApp(sys.stdout), None) @@ -41,7 +41,7 @@ def test_create_listener_with_mandatory_params(self): cmd_resource=cmd_resource) def test_create_listener_with_all_params(self): - """lbaas-listener-create with all params set.""" + # lbaas-listener-create with all params set. resource = 'listener' cmd_resource = 'lbaas_listener' cmd = listener.CreateListener(test_cli20.MyApp(sys.stdout), None) @@ -65,7 +65,7 @@ def test_create_listener_with_all_params(self): cmd_resource=cmd_resource) def test_list_listeners(self): - """lbaas-listener-list.""" + # lbaas-listener-list. resources = 'listeners' cmd_resources = 'lbaas_listeners' cmd = listener.ListListener(test_cli20.MyApp(sys.stdout), None) @@ -73,7 +73,7 @@ def test_list_listeners(self): cmd_resources=cmd_resources) def test_list_listeners_pagination(self): - """lbaas-listener-list with pagination.""" + # lbaas-listener-list with pagination. resources = 'listeners' cmd_resources = 'lbaas_listeners' cmd = listener.ListListener(test_cli20.MyApp(sys.stdout), None) @@ -81,7 +81,7 @@ def test_list_listeners_pagination(self): cmd_resources=cmd_resources) def test_list_listeners_sort(self): - """lbaas-listener-list --sort-key id --sort-key asc.""" + # lbaas-listener-list --sort-key id --sort-key asc. resources = 'listeners' cmd_resources = 'lbaas_listeners' cmd = listener.ListListener(test_cli20.MyApp(sys.stdout), None) @@ -89,7 +89,7 @@ def test_list_listeners_sort(self): cmd_resources=cmd_resources) def test_list_listeners_limit(self): - """lbaas-listener-list -P.""" + # lbaas-listener-list -P. resources = 'listeners' cmd_resources = 'lbaas_listeners' cmd = listener.ListListener(test_cli20.MyApp(sys.stdout), None) @@ -97,7 +97,7 @@ def test_list_listeners_limit(self): cmd_resources=cmd_resources) def test_show_listener_id(self): - """lbaas-listener-show test_id.""" + # lbaas-listener-show test_id. resource = 'listener' cmd_resource = 'lbaas_listener' cmd = listener.ShowListener(test_cli20.MyApp(sys.stdout), None) @@ -106,7 +106,7 @@ def test_show_listener_id(self): cmd_resource=cmd_resource) def test_show_listener_id_name(self): - """lbaas-listener-show.""" + # lbaas-listener-show. resource = 'listener' cmd_resource = 'lbaas_listener' cmd = listener.ShowListener(test_cli20.MyApp(sys.stdout), None) @@ -116,7 +116,7 @@ def test_show_listener_id_name(self): cmd_resource=cmd_resource) def test_update_listener(self): - """lbaas-listener-update myid --name newname.""" + # lbaas-listener-update myid --name newname. resource = 'listener' cmd_resource = 'lbaas_listener' cmd = listener.UpdateListener(test_cli20.MyApp(sys.stdout), None) @@ -126,7 +126,7 @@ def test_update_listener(self): cmd_resource=cmd_resource) def test_delete_listener(self): - """lbaas-listener-delete my-id.""" + # lbaas-listener-delete my-id. resource = 'listener' cmd_resource = 'lbaas_listener' cmd = listener.DeleteListener(test_cli20.MyApp(sys.stdout), None) diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py b/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py index f0c7457b1..dea5ba25f 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py @@ -23,7 +23,7 @@ class CLITestV20LbLoadBalancerJSON(test_cli20.CLITestV20Base): def test_create_loadbalancer_with_mandatory_params(self): - """lbaas-loadbalancer-create with mandatory params only.""" + # lbaas-loadbalancer-create with mandatory params only. resource = 'loadbalancer' cmd_resource = 'lbaas_loadbalancer' cmd = lb.CreateLoadBalancer(test_cli20.MyApp(sys.stdout), None) @@ -38,7 +38,7 @@ def test_create_loadbalancer_with_mandatory_params(self): cmd_resource=cmd_resource) def test_create_loadbalancer_with_all_params(self): - """lbaas-loadbalancer-create with all params set.""" + # lbaas-loadbalancer-create with all params set. resource = 'loadbalancer' cmd_resource = 'lbaas_loadbalancer' cmd = lb.CreateLoadBalancer(test_cli20.MyApp(sys.stdout), None) @@ -57,7 +57,7 @@ def test_create_loadbalancer_with_all_params(self): cmd_resource=cmd_resource) def test_list_loadbalancers(self): - """lbaas-loadbalancer-list.""" + # lbaas-loadbalancer-list. resources = 'loadbalancers' cmd_resources = 'lbaas_loadbalancers' cmd = lb.ListLoadBalancer(test_cli20.MyApp(sys.stdout), None) @@ -65,7 +65,7 @@ def test_list_loadbalancers(self): cmd_resources=cmd_resources) def test_list_loadbalancers_pagination(self): - """lbaas-loadbalancer-list with pagination.""" + # lbaas-loadbalancer-list with pagination. resources = 'loadbalancers' cmd_resources = 'lbaas_loadbalancers' cmd = lb.ListLoadBalancer(test_cli20.MyApp(sys.stdout), None) @@ -73,9 +73,8 @@ def test_list_loadbalancers_pagination(self): cmd_resources=cmd_resources) def test_list_loadbalancers_sort(self): - """lbaas-loadbalancer-list --sort-key name --sort-key id - --sort-key asc --sort-key desc - """ + # lbaas-loadbalancer-list --sort-key name --sort-key id --sort-key asc + # --sort-key desc resources = 'loadbalancers' cmd_resources = 'lbaas_loadbalancers' cmd = lb.ListLoadBalancer(test_cli20.MyApp(sys.stdout), None) @@ -85,7 +84,7 @@ def test_list_loadbalancers_sort(self): cmd_resources=cmd_resources) def test_list_loadbalancers_limit(self): - """lbaas-loadbalancer-list -P.""" + # lbaas-loadbalancer-list -P. resources = 'loadbalancers' cmd_resources = 'lbaas_loadbalancers' cmd = lb.ListLoadBalancer(test_cli20.MyApp(sys.stdout), None) @@ -93,7 +92,7 @@ def test_list_loadbalancers_limit(self): cmd_resources=cmd_resources) def test_show_loadbalancer_id(self): - """lbaas-loadbalancer-loadbalancer-show test_id.""" + # lbaas-loadbalancer-loadbalancer-show test_id. resource = 'loadbalancer' cmd_resource = 'lbaas_loadbalancer' cmd = lb.ShowLoadBalancer(test_cli20.MyApp(sys.stdout), None) @@ -102,7 +101,7 @@ def test_show_loadbalancer_id(self): cmd_resource=cmd_resource) def test_show_loadbalancer_id_name(self): - """lbaas-loadbalancer-loadbalancer-show.""" + # lbaas-loadbalancer-loadbalancer-show. resource = 'loadbalancer' cmd_resource = 'lbaas_loadbalancer' cmd = lb.ShowLoadBalancer(test_cli20.MyApp(sys.stdout), None) @@ -112,7 +111,7 @@ def test_show_loadbalancer_id_name(self): cmd_resource=cmd_resource) def test_update_loadbalancer(self): - """lbaas-loadbalancer-loadbalancer-update myid --name newname.""" + # lbaas-loadbalancer-loadbalancer-update myid --name newname. resource = 'loadbalancer' cmd_resource = 'lbaas_loadbalancer' cmd = lb.UpdateLoadBalancer(test_cli20.MyApp(sys.stdout), None) @@ -122,7 +121,7 @@ def test_update_loadbalancer(self): cmd_resource=cmd_resource) def test_delete_loadbalancer(self): - """lbaas-loadbalancer-loadbalancer-delete my-id.""" + # lbaas-loadbalancer-loadbalancer-delete my-id. resource = 'loadbalancer' cmd_resource = 'lbaas_loadbalancer' cmd = lb.DeleteLoadBalancer(test_cli20.MyApp(sys.stdout), None) diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_member.py b/neutronclient/tests/unit/lb/v2/test_cli20_member.py index 15ee9ce5f..3f9bced20 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_member.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_member.py @@ -23,7 +23,7 @@ class CLITestV20LbMemberJSON(test_cli20.CLITestV20Base): def test_create_member_with_mandatory_params(self): - """lbaas-member-create with mandatory params only.""" + # lbaas-member-create with mandatory params only. resource = 'member' cmd_resource = 'lbaas_member' cmd = member.CreateMember(test_cli20.MyApp(sys.stdout), None) @@ -43,7 +43,7 @@ def test_create_member_with_mandatory_params(self): parent_id=pool_id) def test_create_member_with_all_params(self): - """lbaas-member-create with all params set.""" + # lbaas-member-create with all params set. resource = 'member' cmd_resource = 'lbaas_member' cmd = member.CreateMember(test_cli20.MyApp(sys.stdout), None) @@ -65,7 +65,7 @@ def test_create_member_with_all_params(self): parent_id=pool_id) def test_list_members(self): - """lbaas-member-list.""" + # lbaas-member-list. resources = 'members' cmd_resources = 'lbaas_members' pool_id = 'pool-id' @@ -76,7 +76,7 @@ def test_list_members(self): query="pool_id=%s" % pool_id) def test_list_members_pagination(self): - """lbaas-member-list with pagination.""" + # lbaas-member-list with pagination. resources = 'members' cmd_resources = 'lbaas_members' pool_id = 'pool-id' @@ -88,7 +88,7 @@ def test_list_members_pagination(self): query="pool_id=%s" % pool_id) def test_list_members_sort(self): - """lbaas-member-list --sort-key id --sort-key asc.""" + # lbaas-member-list --sort-key id --sort-key asc. resources = 'members' cmd_resources = 'lbaas_members' pool_id = 'pool-id' @@ -99,7 +99,7 @@ def test_list_members_sort(self): query="pool_id=%s" % pool_id) def test_list_members_limit(self): - """lbaas-member-list -P.""" + # lbaas-member-list -P. resources = 'members' cmd_resources = 'lbaas_members' pool_id = 'pool-id' @@ -111,7 +111,7 @@ def test_list_members_limit(self): query="pool_id=%s" % pool_id) def test_show_member_id(self): - """lbaas-member-show test_id.""" + # lbaas-member-show test_id. resource = 'member' cmd_resource = 'lbaas_member' pool_id = 'pool-id' @@ -121,7 +121,7 @@ def test_show_member_id(self): cmd_resource=cmd_resource, parent_id=pool_id) def test_show_member_id_name(self): - """lbaas-member-show.""" + # lbaas-member-show. resource = 'member' cmd_resource = 'lbaas_member' pool_id = 'pool-id' @@ -132,7 +132,7 @@ def test_show_member_id_name(self): cmd_resource=cmd_resource, parent_id=pool_id) def test_update_member(self): - """lbaas-member-update myid --name newname.""" + # lbaas-member-update myid --name newname. resource = 'member' cmd_resource = 'lbaas_member' my_id = 'my-id' @@ -145,7 +145,7 @@ def test_update_member(self): parent_id=pool_id) def test_delete_member(self): - """lbaas-member-delete my-id.""" + # lbaas-member-delete my-id. resource = 'member' cmd_resource = 'lbaas_member' cmd = member.DeleteMember(test_cli20.MyApp(sys.stdout), None) diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_pool.py b/neutronclient/tests/unit/lb/v2/test_cli20_pool.py index 2ec40f437..efaa06565 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_pool.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_pool.py @@ -23,7 +23,7 @@ class CLITestV20LbPoolJSON(test_cli20.CLITestV20Base): def test_create_pool_with_mandatory_params(self): - """lbaas-pool-create with mandatory params only.""" + # lbaas-pool-create with mandatory params only. resource = 'pool' cmd_resource = 'lbaas_pool' cmd = pool.CreatePool(test_cli20.MyApp(sys.stdout), None) @@ -41,7 +41,7 @@ def test_create_pool_with_mandatory_params(self): cmd_resource=cmd_resource) def test_create_pool_with_all_params(self): - """lbaas-pool-create with all params set.""" + # lbaas-pool-create with all params set. resource = 'pool' cmd_resource = 'lbaas_pool' cmd = pool.CreatePool(test_cli20.MyApp(sys.stdout), None) @@ -68,7 +68,7 @@ def test_create_pool_with_all_params(self): cmd_resource=cmd_resource) def test_list_pools(self): - """lbaas-pool-list.""" + # lbaas-pool-list. resources = 'pools' cmd_resources = 'lbaas_pools' cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None) @@ -76,7 +76,7 @@ def test_list_pools(self): cmd_resources=cmd_resources) def test_list_pools_pagination(self): - """lbaas-pool-list with pagination.""" + # lbaas-pool-list with pagination. resources = 'pools' cmd_resources = 'lbaas_pools' cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None) @@ -84,7 +84,7 @@ def test_list_pools_pagination(self): cmd_resources=cmd_resources) def test_list_pools_sort(self): - """lbaas-pool-list --sort-key id --sort-key asc.""" + # lbaas-pool-list --sort-key id --sort-key asc. resources = 'pools' cmd_resources = 'lbaas_pools' cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None) @@ -92,7 +92,7 @@ def test_list_pools_sort(self): cmd_resources=cmd_resources) def test_list_pools_limit(self): - """lbaas-pool-list -P.""" + # lbaas-pool-list -P. resources = 'pools' cmd_resources = 'lbaas_pools' cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None) @@ -100,7 +100,7 @@ def test_list_pools_limit(self): cmd_resources=cmd_resources) def test_show_pool_id(self): - """lbaas-pool-show test_id.""" + # lbaas-pool-show test_id. resource = 'pool' cmd_resource = 'lbaas_pool' cmd = pool.ShowPool(test_cli20.MyApp(sys.stdout), None) @@ -109,7 +109,7 @@ def test_show_pool_id(self): cmd_resource=cmd_resource) def test_show_pool_id_name(self): - """lbaas-pool-show.""" + # lbaas-pool-show. resource = 'pool' cmd_resource = 'lbaas_pool' cmd = pool.ShowPool(test_cli20.MyApp(sys.stdout), None) @@ -119,7 +119,7 @@ def test_show_pool_id_name(self): cmd_resource=cmd_resource) def test_update_pool(self): - """lbaas-pool-update myid --name newname.""" + # lbaas-pool-update myid --name newname. resource = 'pool' cmd_resource = 'lbaas_pool' cmd = pool.UpdatePool(test_cli20.MyApp(sys.stdout), None) @@ -129,7 +129,7 @@ def test_update_pool(self): cmd_resource=cmd_resource) def test_delete_pool(self): - """lbaas-pool-delete my-id.""" + # lbaas-pool-delete my-id. resource = 'pool' cmd_resource = 'lbaas_pool' cmd = pool.DeletePool(test_cli20.MyApp(sys.stdout), None) diff --git a/neutronclient/tests/unit/qos/test_cli20_policy.py b/neutronclient/tests/unit/qos/test_cli20_policy.py index 6eae329c1..ab6cb8fbe 100755 --- a/neutronclient/tests/unit/qos/test_cli20_policy.py +++ b/neutronclient/tests/unit/qos/test_cli20_policy.py @@ -32,7 +32,7 @@ def setUp(self): self.cmd_ress = 'qos_policies' def test_create_policy_with_only_keyattributes(self): - """Create qos policy abc.""" + # Create qos policy abc. cmd = policy.CreateQoSPolicy(test_cli20.MyApp(sys.stdout), None) myid = 'myid' @@ -45,7 +45,7 @@ def test_create_policy_with_only_keyattributes(self): cmd_resource=self.cmd_res) def test_create_policy_with_description(self): - """Create qos policy xyz --description abc.""" + # Create qos policy xyz --description abc. cmd = policy.CreateQoSPolicy(test_cli20.MyApp(sys.stdout), None) myid = 'myid' @@ -59,7 +59,7 @@ def test_create_policy_with_description(self): cmd_resource=self.cmd_res) def test_create_policy_with_shared(self): - """Create qos policy abc shared across tenants""" + # Create qos policy abc shared across tenants cmd = policy.CreateQoSPolicy(test_cli20.MyApp(sys.stdout), None) myid = 'myid' @@ -73,7 +73,7 @@ def test_create_policy_with_shared(self): cmd_resource=self.cmd_res) def test_create_policy_with_unicode(self): - """Create qos policy u'\u7f51\u7edc'.""" + # Create qos policy u'\u7f51\u7edc'. cmd = policy.CreateQoSPolicy(test_cli20.MyApp(sys.stdout), None) myid = 'myid' @@ -87,7 +87,7 @@ def test_create_policy_with_unicode(self): cmd_resource=self.cmd_res) def test_update_policy(self): - """policy-update myid --name newname.""" + # policy-update myid --name newname. cmd = policy.UpdateQoSPolicy(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(self.res, cmd, 'myid', @@ -96,7 +96,7 @@ def test_update_policy(self): cmd_resource=self.cmd_res) def test_update_policy_description(self): - """policy-update myid --name newname --description newdesc""" + # policy-update myid --name newname --description newdesc cmd = policy.UpdateQoSPolicy(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(self.res, cmd, 'myid', @@ -105,23 +105,22 @@ def test_update_policy_description(self): cmd_resource=self.cmd_res) def test_list_policies(self): - """qos-policy-list.""" + # qos-policy-list. cmd = policy.ListQoSPolicy(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(self.ress, cmd, True, cmd_resources=self.cmd_ress) def test_list_policies_pagination(self): - """qos-policy-list for pagination.""" + # qos-policy-list for pagination. cmd = policy.ListQoSPolicy(test_cli20.MyApp(sys.stdout), None) self._test_list_resources_with_pagination(self.ress, cmd, cmd_resources=self.cmd_ress) def test_list_policies_sort(self): - """sorted list: qos-policy-list --sort-key name --sort-key id - --sort-key asc --sort-key desc - """ + # sorted list: qos-policy-list --sort-key name --sort-key id + # --sort-key asc --sort-key desc cmd = policy.ListQoSPolicy(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(self.ress, cmd, @@ -130,14 +129,14 @@ def test_list_policies_sort(self): cmd_resources=self.cmd_ress) def test_list_policies_limit(self): - """size (1000) limited list: qos-policy-list -P.""" + # size (1000) limited list: qos-policy-list -P. cmd = policy.ListQoSPolicy(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(self.ress, cmd, page_size=1000, cmd_resources=self.cmd_ress) def test_show_policy_id(self): - """qos-policy-show test_id.""" + # qos-policy-show test_id. cmd = policy.ShowQoSPolicy(test_cli20.MyApp(sys.stdout), None) args = ['--fields', 'id', self.test_id] @@ -145,7 +144,7 @@ def test_show_policy_id(self): ['id'], cmd_resource=self.cmd_res) def test_show_policy_name(self): - """qos-policy-show.""" + # qos-policy-show. cmd = policy.ShowQoSPolicy(test_cli20.MyApp(sys.stdout), None) args = ['--fields', 'id', '--fields', 'name', self.test_id] @@ -154,7 +153,7 @@ def test_show_policy_name(self): cmd_resource=self.cmd_res) def test_delete_policy(self): - """qos-policy-delete my-id.""" + # qos-policy-delete my-id. cmd = policy.DeleteQoSPolicy(test_cli20.MyApp(sys.stdout), None) my_id = 'myid1' diff --git a/neutronclient/tests/unit/qos/test_cli20_rule.py b/neutronclient/tests/unit/qos/test_cli20_rule.py index 776fafd37..fcc29b6e0 100644 --- a/neutronclient/tests/unit/qos/test_cli20_rule.py +++ b/neutronclient/tests/unit/qos/test_cli20_rule.py @@ -138,7 +138,7 @@ def test_show_bandwidth_limit_rule(self): parent_id=policy_id) def test_list_qos_rule_types(self): - """qos_rule_types.""" + # qos_rule_types. resources = 'rule_types' cmd_resources = 'qos_rule_types' response_contents = [{'type': 'bandwidth_limit'}] diff --git a/neutronclient/tests/unit/test_cli20_address_scope.py b/neutronclient/tests/unit/test_cli20_address_scope.py index 7b8ea7b78..9b98631d8 100755 --- a/neutronclient/tests/unit/test_cli20_address_scope.py +++ b/neutronclient/tests/unit/test_cli20_address_scope.py @@ -31,7 +31,7 @@ def setUp(self): super(CLITestV20AddressScopeJSON, self).setUp(plurals={'tags': 'tag'}) def test_create_address_scope_with_minimum_option(self): - """Create address_scope: foo-address-scope with minimum option.""" + # Create address_scope: foo-address-scope with minimum option. resource = 'address_scope' cmd = address_scope.CreateAddressScope( test_cli20.MyApp(sys.stdout), None) @@ -44,7 +44,7 @@ def test_create_address_scope_with_minimum_option(self): position_names, position_values) def test_create_address_scope_with_all_option(self): - """Create address_scope: foo-address-scope with all options.""" + # Create address_scope: foo-address-scope with all options. resource = 'address_scope' cmd = address_scope.CreateAddressScope( test_cli20.MyApp(sys.stdout), None) @@ -57,7 +57,7 @@ def test_create_address_scope_with_all_option(self): position_names, position_values) def test_create_address_scope_with_unicode(self): - """Create address_scope: u'\u7f51\u7edc'.""" + # Create address_scope: u'\u7f51\u7edc'. resource = 'address_scope' cmd = address_scope.CreateAddressScope( test_cli20.MyApp(sys.stdout), None) @@ -70,7 +70,7 @@ def test_create_address_scope_with_unicode(self): position_names, position_values) def test_update_address_scope_exception(self): - """Update address_scope (Negative) : myid.""" + # Update address_scope (Negative) : myid. resource = 'address_scope' cmd = address_scope.UpdateAddressScope( test_cli20.MyApp(sys.stdout), None) @@ -78,7 +78,7 @@ def test_update_address_scope_exception(self): resource, cmd, 'myid', ['myid'], {}) def test_update_address_scope(self): - """Update address_scope: myid --name newname-address-scope.""" + # Update address_scope: myid --name newname-address-scope. resource = 'address_scope' cmd = address_scope.UpdateAddressScope( test_cli20.MyApp(sys.stdout), None) @@ -88,14 +88,14 @@ def test_update_address_scope(self): ) def test_list_address_scope(self): - """address_scope-list.""" + # address_scope-list. resources = "address_scopes" cmd = address_scope.ListAddressScope(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, True) def test_list_address_scope_pagination(self): - """address_scope-list.""" + # address_scope-list. cmd = address_scope.ListAddressScope(test_cli20.MyApp(sys.stdout), None) self.mox.StubOutWithMock(address_scope.ListAddressScope, @@ -108,9 +108,9 @@ def test_list_address_scope_pagination(self): self.mox.UnsetStubs() def test_list_address_scope_sort(self): - """sorted list: address_scope-list --sort-key name --sort-key id - --sort-key asc --sort-key desc - """ + # sorted list: + # address_scope-list --sort-key name --sort-key id --sort-key asc + # --sort-key desc resources = "address_scopes" cmd = address_scope.ListAddressScope(test_cli20.MyApp(sys.stdout), None) @@ -119,14 +119,14 @@ def test_list_address_scope_sort(self): sort_dir=["asc", "desc"]) def test_list_address_scope_limit(self): - """size (1000) limited list: address_scope-list -P.""" + # size (1000) limited list: address_scope-list -P. resources = "address_scopes" cmd = address_scope.ListAddressScope(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, page_size=1000) def test_show_address_scope(self): - """Show address_scope: --fields id --fields name myid.""" + # Show address_scope: --fields id --fields name myid. resource = 'address_scope' cmd = address_scope.ShowAddressScope( test_cli20.MyApp(sys.stdout), None) @@ -135,7 +135,7 @@ def test_show_address_scope(self): ['id', 'name']) def test_delete_address_scope(self): - """Delete address_scope: address_scope_id.""" + # Delete address_scope: address_scope_id. resource = 'address_scope' cmd = address_scope.DeleteAddressScope( test_cli20.MyApp(sys.stdout), None) diff --git a/neutronclient/tests/unit/test_cli20_agents.py b/neutronclient/tests/unit/test_cli20_agents.py index bc545c6e1..d28e869ff 100644 --- a/neutronclient/tests/unit/test_cli20_agents.py +++ b/neutronclient/tests/unit/test_cli20_agents.py @@ -55,7 +55,7 @@ def test_list_agents_field(self): self.assertIn(smile, ag.values()) def test_update_agent(self): - """agent-update myid --admin-state-down --description mydescr.""" + # agent-update myid --admin-state-down --description mydescr. resource = 'agent' cmd = agent.UpdateAgent(test_cli20.MyApp(sys.stdout), None) self._test_update_resource( @@ -65,7 +65,7 @@ def test_update_agent(self): ) def test_show_agent(self): - """Show agent: --field id --field binary myid.""" + # Show agent: --field id --field binary myid. resource = 'agent' cmd = agent.ShowAgent(test_cli20.MyApp(sys.stdout), None) args = ['--field', 'id', '--field', 'binary', self.test_id] @@ -73,7 +73,7 @@ def test_show_agent(self): args, ['id', 'binary']) def test_delete_agent(self): - """Delete agent: myid.""" + # Delete agent: myid. resource = 'agent' cmd = agent.DeleteAgent(test_cli20.MyApp(sys.stdout), None) myid = 'myid' diff --git a/neutronclient/tests/unit/test_cli20_floatingips.py b/neutronclient/tests/unit/test_cli20_floatingips.py index 42522e7b2..d500af811 100644 --- a/neutronclient/tests/unit/test_cli20_floatingips.py +++ b/neutronclient/tests/unit/test_cli20_floatingips.py @@ -25,7 +25,7 @@ class CLITestV20FloatingIpsJSON(test_cli20.CLITestV20Base): non_admin_status_resources = ['floatingip'] def test_create_floatingip(self): - """Create floatingip: fip1.""" + # Create floatingip: fip1. resource = 'floatingip' cmd = fip.CreateFloatingIP(test_cli20.MyApp(sys.stdout), None) name = 'fip1' @@ -37,7 +37,7 @@ def test_create_floatingip(self): position_names, position_values) def test_create_floatingip_and_port(self): - """Create floatingip: fip1.""" + # Create floatingip: fip1. resource = 'floatingip' cmd = fip.CreateFloatingIP(test_cli20.MyApp(sys.stdout), None) name = 'fip1' @@ -56,7 +56,7 @@ def test_create_floatingip_and_port(self): position_names, position_values) def test_create_floatingip_and_port_and_address(self): - """Create floatingip: fip1 with a given port and address.""" + # Create floatingip: fip1 with a given port and address. resource = 'floatingip' cmd = fip.CreateFloatingIP(test_cli20.MyApp(sys.stdout), None) name = 'fip1' @@ -75,7 +75,7 @@ def test_create_floatingip_and_port_and_address(self): position_names, position_values) def test_create_floatingip_with_ip_address_of_floating_ip(self): - """Create floatingip: fip1 with a given IP address of floating IP.""" + # Create floatingip: fip1 with a given IP address of floating IP. resource = 'floatingip' cmd = fip.CreateFloatingIP(test_cli20.MyApp(sys.stdout), None) name = 'fip1' @@ -89,7 +89,7 @@ def test_create_floatingip_with_ip_address_of_floating_ip(self): position_names, position_values) def test_create_floatingip_with_subnet_id(self): - """Create floatingip: fip1 on a given subnet id.""" + # Create floatingip: fip1 on a given subnet id. resource = 'floatingip' cmd = fip.CreateFloatingIP(test_cli20.MyApp(sys.stdout), None) name = 'fip1' @@ -103,7 +103,7 @@ def test_create_floatingip_with_subnet_id(self): position_names, position_values) def test_create_floatingip_with_subnet_id_and_port(self): - """Create floatingip: fip1 on a given subnet id and port.""" + # Create floatingip: fip1 on a given subnet id and port. resource = 'floatingip' cmd = fip.CreateFloatingIP(test_cli20.MyApp(sys.stdout), None) name = 'fip1' @@ -118,7 +118,7 @@ def test_create_floatingip_with_subnet_id_and_port(self): position_names, position_values) def test_list_floatingips(self): - """list floatingips: -D.""" + # list floatingips: -D. resources = 'floatingips' cmd = fip.ListFloatingIP(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, True) @@ -129,9 +129,8 @@ def test_list_floatingips_pagination(self): self._test_list_resources_with_pagination(resources, cmd) def test_list_floatingips_sort(self): - """list floatingips: --sort-key name --sort-key id --sort-key asc - --sort-key desc - """ + # list floatingips: + # --sort-key name --sort-key id --sort-key asc --sort-key desc resources = 'floatingips' cmd = fip.ListFloatingIP(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, @@ -139,13 +138,13 @@ def test_list_floatingips_sort(self): sort_dir=["asc", "desc"]) def test_list_floatingips_limit(self): - """list floatingips: -P.""" + # list floatingips: -P. resources = 'floatingips' cmd = fip.ListFloatingIP(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, page_size=1000) def test_delete_floatingip(self): - """Delete floatingip: fip1.""" + # Delete floatingip: fip1. resource = 'floatingip' cmd = fip.DeleteFloatingIP(test_cli20.MyApp(sys.stdout), None) myid = 'myid' @@ -153,7 +152,7 @@ def test_delete_floatingip(self): self._test_delete_resource(resource, cmd, myid, args) def test_show_floatingip(self): - """Show floatingip: --fields id.""" + # Show floatingip: --fields id. resource = 'floatingip' cmd = fip.ShowFloatingIP(test_cli20.MyApp(sys.stdout), None) args = ['--fields', 'id', self.test_id] @@ -161,7 +160,7 @@ def test_show_floatingip(self): args, ['id']) def test_disassociate_ip(self): - """Disassociate floating IP: myid.""" + # Disassociate floating IP: myid. resource = 'floatingip' cmd = fip.DisassociateFloatingIP(test_cli20.MyApp(sys.stdout), None) args = ['myid'] @@ -170,7 +169,7 @@ def test_disassociate_ip(self): ) def test_associate_ip(self): - """Associate floating IP: myid portid.""" + # Associate floating IP: myid portid. resource = 'floatingip' cmd = fip.AssociateFloatingIP(test_cli20.MyApp(sys.stdout), None) args = ['myid', 'portid'] diff --git a/neutronclient/tests/unit/test_cli20_metering.py b/neutronclient/tests/unit/test_cli20_metering.py index c51af3770..9ecf04734 100644 --- a/neutronclient/tests/unit/test_cli20_metering.py +++ b/neutronclient/tests/unit/test_cli20_metering.py @@ -23,7 +23,7 @@ class CLITestV20MeteringJSON(test_cli20.CLITestV20Base): non_admin_status_resources = ['metering_label', 'metering_label_rule'] def test_create_metering_label(self): - """Create a metering label.""" + # Create a metering label. resource = 'metering_label' cmd = metering.CreateMeteringLabel( test_cli20.MyApp(sys.stdout), None) @@ -43,7 +43,7 @@ def test_list_metering_labels(self): self._test_list_resources(resources, cmd) def test_delete_metering_label(self): - """Delete a metering label.""" + # Delete a metering label. resource = 'metering_label' cmd = metering.DeleteMeteringLabel( test_cli20.MyApp(sys.stdout), None) diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index 0fcbb683f..4909565c9 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -30,7 +30,7 @@ def setUp(self): super(CLITestV20NetworkJSON, self).setUp(plurals={'tags': 'tag'}) def test_create_network(self): - """Create net: myname.""" + # Create net: myname. resource = 'network' cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -42,7 +42,7 @@ def test_create_network(self): position_names, position_values) def test_create_network_with_unicode(self): - """Create net: u'\u7f51\u7edc'.""" + # Create net: u'\u7f51\u7edc'. resource = 'network' cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) name = u'\u7f51\u7edc' @@ -54,7 +54,7 @@ def test_create_network_with_unicode(self): position_names, position_values) def test_create_network_tenant(self): - """Create net: --tenant_id tenantid myname.""" + # Create net: --tenant_id tenantid myname. resource = 'network' cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -73,7 +73,7 @@ def test_create_network_tenant(self): tenant_id='tenantid') def test_create_network_provider_args(self): - """Create net: with --provider arguments.""" + # Create net: with --provider arguments. resource = 'network' cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -91,7 +91,7 @@ def test_create_network_provider_args(self): position_names, position_values) def test_create_network_tags(self): - """Create net: myname --tags a b.""" + # Create net: myname --tags a b. resource = 'network' cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -104,7 +104,7 @@ def test_create_network_tags(self): tags=['a', 'b']) def test_create_network_state(self): - """Create net: --admin_state_down myname.""" + # Create net: --admin_state_down myname. resource = 'network' cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -123,7 +123,7 @@ def test_create_network_state(self): admin_state_up=False) def test_create_network_vlan_transparent(self): - """Create net: myname --vlan-transparent True.""" + # Create net: myname --vlan-transparent True. resource = 'network' cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -137,7 +137,7 @@ def test_create_network_vlan_transparent(self): **vlantrans) def test_create_network_with_qos_policy(self): - """Create net: --qos-policy mypolicy.""" + # Create net: --qos-policy mypolicy. resource = 'network' cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -150,9 +150,8 @@ def test_create_network_with_qos_policy(self): position_names, position_values) def test_create_network_with_az_hint(self): - """Create net: --availability-zone-hint zone1 - --availability-zone-hint zone2. - """ + # Create net: --availability-zone-hint zone1 + # --availability-zone-hint zone2. resource = 'network' cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -212,49 +211,46 @@ def test_list_nets_pagination(self): self._test_list_resources_with_pagination("networks", cmd) def test_list_nets_sort(self): - """list nets: --sort-key name --sort-key id --sort-dir asc - --sort-dir desc - """ + # list nets: + # --sort-key name --sort-key id --sort-dir asc --sort-dir desc cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) self._test_list_networks(cmd, sort_key=['name', 'id'], sort_dir=['asc', 'desc']) def test_list_nets_sort_with_keys_more_than_dirs(self): - """list nets: --sort-key name --sort-key id --sort-dir desc - """ + # list nets: --sort-key name --sort-key id --sort-dir desc cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) self._test_list_networks(cmd, sort_key=['name', 'id'], sort_dir=['desc']) def test_list_nets_sort_with_dirs_more_than_keys(self): - """list nets: --sort-key name --sort-dir desc --sort-dir asc - """ + # list nets: --sort-key name --sort-dir desc --sort-dir asc cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) self._test_list_networks(cmd, sort_key=['name'], sort_dir=['desc', 'asc']) def test_list_nets_limit(self): - """list nets: -P.""" + # list nets: -P. cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) self._test_list_networks(cmd, page_size=1000) def test_list_nets_detail(self): - """list nets: -D.""" + # list nets: -D. cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) self._test_list_networks(cmd, True) def test_list_nets_tags(self): - """List nets: -- --tags a b.""" + # List nets: -- --tags a b. cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) self._test_list_networks(cmd, tags=['a', 'b']) def test_list_nets_tags_with_unicode(self): - """List nets: -- --tags u'\u7f51\u7edc'.""" + # List nets: -- --tags u'\u7f51\u7edc'. cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) self._test_list_networks(cmd, tags=[u'\u7f51\u7edc']) def test_list_nets_detail_tags(self): - """List nets: -D -- --tags a b.""" + # List nets: -D -- --tags a b. cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) self._test_list_networks(cmd, detail=True, tags=['a', 'b']) @@ -321,7 +317,7 @@ def test_list_nets_extend_subnets_no_subnet(self): self._test_list_nets_extend_subnets(data, expected) def test_list_nets_fields(self): - """List nets: --fields a --fields b -- --fields c d.""" + # List nets: --fields a --fields b -- --fields c d. cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) self._test_list_networks(cmd, fields_1=['a', 'b'], fields_2=['c', 'd']) @@ -459,27 +455,27 @@ def _test_list_external_nets(self, resources, cmd, self.assertIn('myid1', _str) def test_list_external_nets_detail(self): - """list external nets: -D.""" + # list external nets: -D. resources = "networks" cmd = network.ListExternalNetwork(test_cli20.MyApp(sys.stdout), None) self._test_list_external_nets(resources, cmd, True) def test_list_external_nets_tags(self): - """List external nets: -- --tags a b.""" + # List external nets: -- --tags a b. resources = "networks" cmd = network.ListExternalNetwork(test_cli20.MyApp(sys.stdout), None) self._test_list_external_nets(resources, cmd, tags=['a', 'b']) def test_list_external_nets_detail_tags(self): - """List external nets: -D -- --tags a b.""" + # List external nets: -D -- --tags a b. resources = "networks" cmd = network.ListExternalNetwork(test_cli20.MyApp(sys.stdout), None) self._test_list_external_nets(resources, cmd, detail=True, tags=['a', 'b']) def test_list_externel_nets_fields(self): - """List external nets: --fields a --fields b -- --fields c d.""" + # List external nets: --fields a --fields b -- --fields c d. resources = "networks" cmd = network.ListExternalNetwork(test_cli20.MyApp(sys.stdout), None) self._test_list_external_nets(resources, cmd, @@ -487,14 +483,14 @@ def test_list_externel_nets_fields(self): fields_2=['c', 'd']) def test_update_network_exception(self): - """Update net: myid.""" + # Update net: myid. resource = 'network' cmd = network.UpdateNetwork(test_cli20.MyApp(sys.stdout), None) self.assertRaises(exceptions.CommandError, self._test_update_resource, resource, cmd, 'myid', ['myid'], {}) def test_update_network(self): - """Update net: myid --name myname --tags a b.""" + # Update net: myid --name myname --tags a b. resource = 'network' cmd = network.UpdateNetwork(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', @@ -504,7 +500,7 @@ def test_update_network(self): ) def test_update_network_with_unicode(self): - """Update net: myid --name u'\u7f51\u7edc' --tags a b.""" + # Update net: myid --name u'\u7f51\u7edc' --tags a b. resource = 'network' cmd = network.UpdateNetwork(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', @@ -515,7 +511,7 @@ def test_update_network_with_unicode(self): ) def test_update_network_with_qos_policy(self): - """Update net: myid --qos-policy mypolicy.""" + # Update net: myid --qos-policy mypolicy. resource = 'network' cmd = network.UpdateNetwork(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', @@ -523,7 +519,7 @@ def test_update_network_with_qos_policy(self): {'qos_policy_id': 'mypolicy', }) def test_update_network_with_no_qos_policy(self): - """Update net: myid --no-qos-policy.""" + # Update net: myid --no-qos-policy. resource = 'network' cmd = network.UpdateNetwork(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', @@ -531,7 +527,7 @@ def test_update_network_with_no_qos_policy(self): {'qos_policy_id': None, }) def test_show_network(self): - """Show net: --fields id --fields name myid.""" + # Show net: --fields id --fields name myid. resource = 'network' cmd = network.ShowNetwork(test_cli20.MyApp(sys.stdout), None) args = ['--fields', 'id', '--fields', 'name', self.test_id] @@ -539,7 +535,7 @@ def test_show_network(self): ['id', 'name']) def test_delete_network(self): - """Delete net: myid.""" + # Delete net: myid. resource = 'network' cmd = network.DeleteNetwork(test_cli20.MyApp(sys.stdout), None) myid = 'myid' diff --git a/neutronclient/tests/unit/test_cli20_nsx_queue.py b/neutronclient/tests/unit/test_cli20_nsx_queue.py index 901422149..503f1c177 100644 --- a/neutronclient/tests/unit/test_cli20_nsx_queue.py +++ b/neutronclient/tests/unit/test_cli20_nsx_queue.py @@ -29,7 +29,7 @@ def setUp(self): plurals={'qos_queues': 'qos_queue'}) def test_create_qos_queue(self): - """Create a qos queue.""" + # Create a qos queue. resource = 'qos_queue' cmd = qos.CreateQoSQueue( test_cli20.MyApp(sys.stdout), None) @@ -43,7 +43,7 @@ def test_create_qos_queue(self): position_names, position_values) def test_create_qos_queue_all_values(self): - """Create a qos queue.""" + # Create a qos queue. resource = 'qos_queue' cmd = qos.CreateQoSQueue( test_cli20.MyApp(sys.stdout), None) diff --git a/neutronclient/tests/unit/test_cli20_port.py b/neutronclient/tests/unit/test_cli20_port.py index 7cc3bbd18..0fe898532 100644 --- a/neutronclient/tests/unit/test_cli20_port.py +++ b/neutronclient/tests/unit/test_cli20_port.py @@ -29,7 +29,7 @@ def setUp(self): super(CLITestV20PortJSON, self).setUp(plurals={'tags': 'tag'}) def test_create_port(self): - """Create port: netid.""" + # Create port: netid. resource = 'port' cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -43,7 +43,7 @@ def test_create_port(self): position_names, position_values) def test_create_port_extra_dhcp_opts_args(self): - """Create port: netid --extra_dhcp_opt.""" + # Create port: netid --extra_dhcp_opt. resource = 'port' cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -67,7 +67,7 @@ def test_create_port_extra_dhcp_opts_args(self): position_names, position_values) def test_create_port_extra_dhcp_opts_args_ip_version(self): - """Create port: netid --extra_dhcp_opt.""" + # Create port: netid --extra_dhcp_opt. resource = 'port' cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -95,7 +95,7 @@ def test_create_port_extra_dhcp_opts_args_ip_version(self): position_names, position_values) def test_create_port_full(self): - """Create port: --mac_address mac --device_id deviceid netid.""" + # Create port: --mac_address mac --device_id deviceid netid. resource = 'port' cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -114,7 +114,7 @@ def test_create_port_full(self): position_names, position_values) def test_create_port_vnic_type_normal(self): - """Create port: --vnic_type normal netid.""" + # Create port: --vnic_type normal netid. resource = 'port' cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -134,7 +134,7 @@ def test_create_port_vnic_type_normal(self): position_names, position_values) def test_create_port_vnic_type_direct(self): - """Create port: --vnic_type direct netid.""" + # Create port: --vnic_type direct netid. resource = 'port' cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -154,7 +154,7 @@ def test_create_port_vnic_type_direct(self): position_names, position_values) def test_create_port_vnic_type_macvtap(self): - """Create port: --vnic_type macvtap netid.""" + # Create port: --vnic_type macvtap netid. resource = 'port' cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -174,7 +174,7 @@ def test_create_port_vnic_type_macvtap(self): position_names, position_values) def test_create_port_vnic_type_baremetal(self): - """Create port: --vnic_type baremetal netid.""" + # Create port: --vnic_type baremetal netid. resource = 'port' cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -213,7 +213,7 @@ def test_create_port_with_binding_profile(self): position_names, position_values) def test_create_port_tenant(self): - """Create port: --tenant_id tenantid netid.""" + # Create port: --tenant_id tenantid netid. resource = 'port' cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -234,7 +234,7 @@ def test_create_port_tenant(self): tenant_id='tenantid') def test_create_port_tags(self): - """Create port: netid mac_address device_id --tags a b.""" + # Create port: netid mac_address device_id --tags a b. resource = 'port' cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -249,7 +249,7 @@ def test_create_port_tags(self): tags=['a', 'b']) def test_create_port_secgroup(self): - """Create port: --security-group sg1_id netid.""" + # Create port: --security-group sg1_id netid. resource = 'port' cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -262,11 +262,9 @@ def test_create_port_secgroup(self): position_names, position_values) def test_create_port_secgroups(self): - """Create port: netid - - The are - --security-group sg1_id --security-group sg2_id - """ + # Create port: netid + # The are --security-group sg1_id + # --security-group sg2_id resource = 'port' cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -293,10 +291,8 @@ def test_create_port_secgroup_off(self): position_names, position_values) def test_create_port_secgroups_list(self): - """Create port: netid - The are - --security-groups list=true sg_id1 sg_id2 - """ + # Create port: netid + # The are --security-groups list=true sg_id1 sg_id2 resource = 'port' cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -309,7 +305,7 @@ def test_create_port_secgroups_list(self): position_names, position_values) def test_create_port_with_qos_policy(self): - """Create port: --qos-policy mypolicy.""" + # Create port: --qos-policy mypolicy. resource = 'port' cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -323,10 +319,9 @@ def test_create_port_with_qos_policy(self): position_names, position_values) def test_create_port_with_allowed_address_pair_ipaddr(self): - """Create port: - --allowed-address-pair ip_address=addr0 - --allowed-address-pair ip_address=addr1 - """ + # Create port: + # --allowed-address-pair ip_address=addr0 + # --allowed-address-pair ip_address=addr1 resource = 'port' cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -346,10 +341,9 @@ def test_create_port_with_allowed_address_pair_ipaddr(self): position_names, position_values) def test_create_port_with_allowed_address_pair(self): - """Create port: - --allowed-address-pair ip_address=addr0,mac_address=mac0 - --allowed-address-pair ip_address=addr1,mac_address=mac1 - """ + # Create port: + # --allowed-address-pair ip_address=addr0,mac_address=mac0 + # --allowed-address-pair ip_address=addr1,mac_address=mac1 resource = 'port' cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -371,7 +365,7 @@ def test_create_port_with_allowed_address_pair(self): position_names, position_values) def test_list_ports(self): - """List ports: -D.""" + # List ports: -D. resources = "ports" cmd = port.ListPort(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, True) @@ -382,9 +376,8 @@ def test_list_ports_pagination(self): self._test_list_resources_with_pagination(resources, cmd) def test_list_ports_sort(self): - """list ports: --sort-key name --sort-key id --sort-key asc - --sort-key desc - """ + # list ports: + # --sort-key name --sort-key id --sort-key asc --sort-key desc resources = "ports" cmd = port.ListPort(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, @@ -392,32 +385,32 @@ def test_list_ports_sort(self): sort_dir=["asc", "desc"]) def test_list_ports_limit(self): - """list ports: -P.""" + # list ports: -P. resources = "ports" cmd = port.ListPort(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, page_size=1000) def test_list_ports_tags(self): - """List ports: -- --tags a b.""" + # List ports: -- --tags a b. resources = "ports" cmd = port.ListPort(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, tags=['a', 'b']) def test_list_ports_detail_tags(self): - """List ports: -D -- --tags a b.""" + # List ports: -D -- --tags a b. resources = "ports" cmd = port.ListPort(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, detail=True, tags=['a', 'b']) def test_list_ports_fields(self): - """List ports: --fields a --fields b -- --fields c d.""" + # List ports: --fields a --fields b -- --fields c d. resources = "ports" cmd = port.ListPort(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, fields_1=['a', 'b'], fields_2=['c', 'd']) def test_list_ports_with_fixed_ips_in_csv(self): - """List ports: -f csv.""" + # List ports: -f csv. resources = "ports" cmd = port.ListPort(test_cli20.MyApp(sys.stdout), None) fixed_ips = [{"subnet_id": "30422057-d6df-4c90-8314-aefb5e326666", @@ -493,28 +486,28 @@ def _test_list_router_port(self, resources, cmd, self.assertIn('myid1', _str) def test_list_router_ports(self): - """List router ports: -D.""" + # List router ports: -D. resources = "ports" cmd = port.ListRouterPort(test_cli20.MyApp(sys.stdout), None) self._test_list_router_port(resources, cmd, self.test_id, True) def test_list_router_ports_tags(self): - """List router ports: -- --tags a b.""" + # List router ports: -- --tags a b. resources = "ports" cmd = port.ListRouterPort(test_cli20.MyApp(sys.stdout), None) self._test_list_router_port(resources, cmd, self.test_id, tags=['a', 'b']) def test_list_router_ports_detail_tags(self): - """List router ports: -D -- --tags a b.""" + # List router ports: -D -- --tags a b. resources = "ports" cmd = port.ListRouterPort(test_cli20.MyApp(sys.stdout), None) self._test_list_router_port(resources, cmd, self.test_id, detail=True, tags=['a', 'b']) def test_list_router_ports_fields(self): - """List ports: --fields a --fields b -- --fields c d.""" + # List ports: --fields a --fields b -- --fields c d. resources = "ports" cmd = port.ListRouterPort(test_cli20.MyApp(sys.stdout), None) self._test_list_router_port(resources, cmd, self.test_id, @@ -522,9 +515,7 @@ def test_list_router_ports_fields(self): fields_2=['c', 'd']) def test_update_port(self): - """Update port: myid --name myname --admin-state-up False - --tags a b. - """ + # Update port: myid --name myname --admin-state-up False --tags a b. resource = 'port' cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', @@ -554,7 +545,7 @@ def test_update_port_secgroups(self): self._test_update_resource(resource, cmd, myid, args, updatefields) def test_update_port_extra_dhcp_opts(self): - """Update port: myid --extra_dhcp_opt.""" + # Update port: myid --extra_dhcp_opt. resource = 'port' myid = 'myid' args = [myid, @@ -598,7 +589,7 @@ def test_update_port_device_id_device_owner(self): self._test_update_resource(resource, cmd, myid, args, updatefields) def test_update_port_extra_dhcp_opts_ip_version(self): - """Update port: myid --extra_dhcp_opt.""" + # Update port: myid --extra_dhcp_opt. resource = 'port' myid = 'myid' args = [myid, @@ -622,7 +613,7 @@ def test_update_port_extra_dhcp_opts_ip_version(self): self._test_update_resource(resource, cmd, myid, args, updatedfields) def test_update_port_with_qos_policy(self): - """Update port: myid --qos-policy mypolicy.""" + # Update port: myid --qos-policy mypolicy. resource = 'port' cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', @@ -630,7 +621,7 @@ def test_update_port_with_qos_policy(self): {'qos_policy_id': 'mypolicy', }) def test_update_port_with_no_qos_policy(self): - """Update port: myid --no-qos-policy.""" + # Update port: myid --no-qos-policy. resource = 'port' cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', @@ -660,7 +651,7 @@ def test_delete_extra_dhcp_opts_from_port(self): self._test_update_resource(resource, cmd, myid, args, updatedfields) def test_update_port_security_group_off(self): - """Update port: --no-security-groups myid.""" + # Update port: --no-security-groups myid. resource = 'port' cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', @@ -668,10 +659,9 @@ def test_update_port_security_group_off(self): {'security_groups': []}) def test_update_port_allowed_address_pair_ipaddr(self): - """Update port(ip_address only): - --allowed-address-pairs ip_address=addr0 - --allowed-address-pairs ip_address=addr1 - """ + # Update port(ip_address only): + # --allowed-address-pairs ip_address=addr0 + # --allowed-address-pairs ip_address=addr1 import sys resource = 'port' cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) @@ -687,12 +677,9 @@ def test_update_port_allowed_address_pair_ipaddr(self): self._test_update_resource(resource, cmd, myid, args, updatefields) def test_update_port_allowed_address_pair(self): - """Update port: - --allowed-address-pair - ip_address=addr0,mac_address=mac0 - --allowed-address-pair - ip_address_addr1,mac_address=mac1 - """ + # Update port: + # --allowed-address-pair ip_address=addr0,mac_address=mac0 + # --allowed-address-pair ip_address_addr1,mac_address=mac1 resource = 'port' cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) myid = 'myid' @@ -709,7 +696,7 @@ def test_update_port_allowed_address_pair(self): self._test_update_resource(resource, cmd, myid, args, updatefields) def test_update_port_allowed_address_pairs_off(self): - """Update port: --no-allowed-address-pairs.""" + # Update port: --no-allowed-address-pairs. resource = 'port' cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', @@ -717,7 +704,7 @@ def test_update_port_allowed_address_pairs_off(self): {'allowed_address_pairs': []}) def test_show_port(self): - """Show port: --fields id --fields name myid.""" + # Show port: --fields id --fields name myid. resource = 'port' cmd = port.ShowPort(test_cli20.MyApp(sys.stdout), None) args = ['--fields', 'id', '--fields', 'name', self.test_id] @@ -725,7 +712,7 @@ def test_show_port(self): args, ['id', 'name']) def test_delete_port(self): - """Delete port: myid.""" + # Delete port: myid. resource = 'port' cmd = port.DeletePort(test_cli20.MyApp(sys.stdout), None) myid = 'myid' diff --git a/neutronclient/tests/unit/test_cli20_rbac.py b/neutronclient/tests/unit/test_cli20_rbac.py index 4027d617e..1d287bad6 100644 --- a/neutronclient/tests/unit/test_cli20_rbac.py +++ b/neutronclient/tests/unit/test_cli20_rbac.py @@ -24,7 +24,7 @@ class CLITestV20RBACJSON(test_cli20.CLITestV20Base): non_admin_status_resources = ['rbac_policy'] def test_create_rbac_policy_with_mandatory_params(self): - """Create rbac: rbac_object --type network --action access_as_shared""" + # Create rbac: rbac_object --type network --action access_as_shared resource = 'rbac_policy' cmd = rbac.CreateRBACPolicy(test_cli20.MyApp(sys.stdout), None) name = 'rbac_object' @@ -38,8 +38,8 @@ def test_create_rbac_policy_with_mandatory_params(self): position_names, position_values) def test_create_rbac_policy_with_all_params(self): - """Create rbac: rbac_object --type network """ - """--target-tenant tenant_id --action access_as_external""" + # Create rbac: rbac_object --type network --target-tenant tenant_id + # --action access_as_external resource = 'rbac_policy' cmd = rbac.CreateRBACPolicy(test_cli20.MyApp(sys.stdout), None) name = 'rbac_object' @@ -54,7 +54,7 @@ def test_create_rbac_policy_with_all_params(self): position_names, position_values) def test_create_rbac_policy_with_unicode(self): - """Create rbac policy u'\u7f51\u7edc'.""" + # Create rbac policy u'\u7f51\u7edc'. resource = 'rbac_policy' cmd = rbac.CreateRBACPolicy(test_cli20.MyApp(sys.stdout), None) name = u'\u7f51\u7edc' @@ -69,7 +69,7 @@ def test_create_rbac_policy_with_unicode(self): position_names, position_values) def test_update_rbac_policy(self): - """rbac-update --target-tenant .""" + # rbac-update --target-tenant . resource = 'rbac_policy' cmd = rbac.UpdateRBACPolicy(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', @@ -77,7 +77,7 @@ def test_update_rbac_policy(self): {'target_tenant': 'tenant_id', }) def test_delete_rbac_policy(self): - """rbac-delete my-id.""" + # rbac-delete my-id. resource = 'rbac_policy' cmd = rbac.DeleteRBACPolicy(test_cli20.MyApp(sys.stdout), None) my_id = 'myid1' @@ -85,21 +85,21 @@ def test_delete_rbac_policy(self): self._test_delete_resource(resource, cmd, my_id, args) def test_list_rbac_policies(self): - """rbac-list.""" + # rbac-list. resources = "rbac_policies" cmd = rbac.ListRBACPolicy(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, True) def test_list_rbac_policies_pagination(self): - """rbac-list with pagination.""" + # rbac-list with pagination. resources = "rbac_policies" cmd = rbac.ListRBACPolicy(test_cli20.MyApp(sys.stdout), None) self._test_list_resources_with_pagination(resources, cmd) def test_list_rbac_policies_sort(self): - """sorted list: rbac-list --sort-key name --sort-key id - --sort-key asc --sort-key desc - """ + # sorted list: + # rbac-list --sort-key name --sort-key id --sort-key asc + # --sort-key desc resources = "rbac_policies" cmd = rbac.ListRBACPolicy(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, @@ -107,13 +107,13 @@ def test_list_rbac_policies_sort(self): sort_dir=["asc", "desc"]) def test_list_rbac_policies_limit(self): - """size (1000) limited list: rbac-list -P.""" + # size (1000) limited list: rbac-list -P. resources = "rbac_policies" cmd = rbac.ListRBACPolicy(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, page_size=1000) def test_show_rbac_policy(self): - """rbac-show test_id.""" + # rbac-show test_id. resource = 'rbac_policy' cmd = rbac.ShowRBACPolicy(test_cli20.MyApp(sys.stdout), None) args = ['--fields', 'id', self.test_id] diff --git a/neutronclient/tests/unit/test_cli20_router.py b/neutronclient/tests/unit/test_cli20_router.py index 4d4c2c88a..d69552c8d 100644 --- a/neutronclient/tests/unit/test_cli20_router.py +++ b/neutronclient/tests/unit/test_cli20_router.py @@ -23,7 +23,7 @@ class CLITestV20RouterJSON(test_cli20.CLITestV20Base): def test_create_router(self): - """Create router: router1.""" + # Create router: router1. resource = 'router' cmd = router.CreateRouter(test_cli20.MyApp(sys.stdout), None) name = 'router1' @@ -35,7 +35,7 @@ def test_create_router(self): position_names, position_values) def test_create_router_tenant(self): - """Create router: --tenant_id tenantid myname.""" + # Create router: --tenant_id tenantid myname. resource = 'router' cmd = router.CreateRouter(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -48,7 +48,7 @@ def test_create_router_tenant(self): tenant_id='tenantid') def test_create_router_admin_state(self): - """Create router: --admin_state_down myname.""" + # Create router: --admin_state_down myname. resource = 'router' cmd = router.CreateRouter(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -61,7 +61,7 @@ def test_create_router_admin_state(self): admin_state_up=False) def _create_router_distributed_or_ha(self, distributed=None, ha=None): - """Create router: --distributed distributed --ha ha myname.""" + # Create router: --distributed distributed --ha ha myname. resource = 'router' cmd = router.CreateRouter(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -84,7 +84,7 @@ def _create_router_distributed_or_ha(self, distributed=None, ha=None): **expected) def test_create_router_distributed_True(self): - """Create router: --distributed=True.""" + # Create router: --distributed=True. self._create_router_distributed_or_ha(distributed='True') def test_create_router_ha_with_True(self): @@ -100,21 +100,20 @@ def test_create_router_ha_with_false(self): self._create_router_distributed_or_ha(ha='false') def test_create_router_distributed_False(self): - """Create router: --distributed=False.""" + # Create router: --distributed=False. self._create_router_distributed_or_ha(distributed='False') def test_create_router_distributed_true(self): - """Create router: --distributed=true.""" + # Create router: --distributed=true. self._create_router_distributed_or_ha(distributed='true') def test_create_router_distributed_false(self): - """Create router: --distributed=false.""" + # Create router: --distributed=false. self._create_router_distributed_or_ha(distributed='false') def test_create_router_with_az_hint(self): - """Create router: --availability-zone-hint zone1 - --availability-zone-hint zone2. - """ + # Create router: --availability-zone-hint zone1 + # --availability-zone-hint zone2. resource = 'router' cmd = router.CreateRouter(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -127,7 +126,7 @@ def test_create_router_with_az_hint(self): position_names, position_values) def test_list_routers_detail(self): - """list routers: -D.""" + # list routers: -D. resources = "routers" cmd = router.ListRouter(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, True) @@ -138,9 +137,8 @@ def test_list_routers_pagination(self): self._test_list_resources_with_pagination(resources, cmd) def test_list_routers_sort(self): - """list routers: --sort-key name --sort-key id --sort-key asc - --sort-key desc - """ + # list routers: + # --sort-key name --sort-key id --sort-key asc --sort-key desc resources = "routers" cmd = router.ListRouter(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, @@ -148,20 +146,20 @@ def test_list_routers_sort(self): sort_dir=["asc", "desc"]) def test_list_routers_limit(self): - """list routers: -P.""" + # list routers: -P. resources = "routers" cmd = router.ListRouter(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, page_size=1000) def test_update_router_exception(self): - """Update router: myid.""" + # Update router: myid. resource = 'router' cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None) self.assertRaises(exceptions.CommandError, self._test_update_resource, resource, cmd, 'myid', ['myid'], {}) def test_update_router(self): - """Update router: myid --name myname --tags a b.""" + # Update router: myid --name myname --tags a b. resource = 'router' cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', @@ -170,7 +168,7 @@ def test_update_router(self): ) def test_update_router_admin_state(self): - """Update router: myid --admin-state-up .""" + # Update router: myid --admin-state-up . resource = 'router' cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', @@ -191,7 +189,7 @@ def test_update_router_admin_state(self): ) def test_update_router_distributed(self): - """Update router: myid --distributed .""" + # Update router: myid --distributed . resource = 'router' cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', @@ -212,7 +210,7 @@ def test_update_router_distributed(self): ) def test_update_router_no_routes(self): - """Update router: myid --no-routes""" + # Update router: myid --no-routes resource = 'router' cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', @@ -220,9 +218,7 @@ def test_update_router_no_routes(self): {'routes': None}) def test_update_router_add_route(self): - """Update router: myid - --route destination=10.0.3.0/24,nexthop=10.0.0.10 - """ + # Update router: myid --route destination=10.0.3.0/24,nexthop=10.0.0.10 resource = 'router' cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None) myid = 'myid' @@ -235,11 +231,9 @@ def test_update_router_add_route(self): self._test_update_resource(resource, cmd, myid, args, updatefields) def test_update_router_add_routes(self): - """Update router: myid - --route destination=10.0.3.0/24,nexthop=10.0.0.10 - --route destination=fd7a:1d63:2063::/64, - nexthop=fd7a:1d63:2063:0:f816:3eff:fe0e:a697 - """ + # Update router: myid --route destination=10.0.3.0/24,nexthop=10.0.0.10 + # --route destination=fd7a:1d63:2063::/64, + # nexthop=fd7a:1d63:2063:0:f816:3eff:fe0e:a697 resource = 'router' cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None) myid = 'myid' @@ -257,7 +251,7 @@ def test_update_router_add_routes(self): self._test_update_resource(resource, cmd, myid, args, updatefields) def test_update_router_no_routes_with_add_route(self): - """Update router: --no-routes with --route""" + # Update router: --no-routes with --route resource = 'router' cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None) myid = 'myid' @@ -274,7 +268,7 @@ def test_update_router_no_routes_with_add_route(self): self.assertEqual(2, actual_error_code) def test_delete_router(self): - """Delete router: myid.""" + # Delete router: myid. resource = 'router' cmd = router.DeleteRouter(test_cli20.MyApp(sys.stdout), None) myid = 'myid' @@ -282,7 +276,7 @@ def test_delete_router(self): self._test_delete_resource(resource, cmd, myid, args) def test_show_router(self): - """Show router: myid.""" + # Show router: myid. resource = 'router' cmd = router.ShowRouter(test_cli20.MyApp(sys.stdout), None) args = ['--fields', 'id', '--fields', 'name', self.test_id] @@ -305,43 +299,43 @@ def _test_add_remove_interface(self, action, mode, cmd, args): body, retval) def test_add_interface_compat(self): - """Add interface to router: myid subnetid.""" + # Add interface to router: myid subnetid. cmd = router.AddInterfaceRouter(test_cli20.MyApp(sys.stdout), None) args = ['myid', 'subnetid'] self._test_add_remove_interface('add', 'subnet', cmd, args) def test_add_interface_by_subnet(self): - """Add interface to router: myid subnet=subnetid.""" + # Add interface to router: myid subnet=subnetid. cmd = router.AddInterfaceRouter(test_cli20.MyApp(sys.stdout), None) args = ['myid', 'subnet=subnetid'] self._test_add_remove_interface('add', 'subnet', cmd, args) def test_add_interface_by_port(self): - """Add interface to router: myid port=portid.""" + # Add interface to router: myid port=portid. cmd = router.AddInterfaceRouter(test_cli20.MyApp(sys.stdout), None) args = ['myid', 'port=portid'] self._test_add_remove_interface('add', 'port', cmd, args) def test_del_interface_compat(self): - """Delete interface from router: myid subnetid.""" + # Delete interface from router: myid subnetid. cmd = router.RemoveInterfaceRouter(test_cli20.MyApp(sys.stdout), None) args = ['myid', 'subnetid'] self._test_add_remove_interface('remove', 'subnet', cmd, args) def test_del_interface_by_subnet(self): - """Delete interface from router: myid subnet=subnetid.""" + # Delete interface from router: myid subnet=subnetid. cmd = router.RemoveInterfaceRouter(test_cli20.MyApp(sys.stdout), None) args = ['myid', 'subnet=subnetid'] self._test_add_remove_interface('remove', 'subnet', cmd, args) def test_del_interface_by_port(self): - """Delete interface from router: myid port=portid.""" + # Delete interface from router: myid port=portid. cmd = router.RemoveInterfaceRouter(test_cli20.MyApp(sys.stdout), None) args = ['myid', 'port=portid'] self._test_add_remove_interface('remove', 'port', cmd, args) def test_set_gateway(self): - """Set external gateway for router: myid externalid.""" + # Set external gateway for router: myid externalid. resource = 'router' cmd = router.SetGatewayRouter(test_cli20.MyApp(sys.stdout), None) args = ['myid', 'externalid'] @@ -352,7 +346,7 @@ def test_set_gateway(self): ) def test_set_gateway_disable_snat(self): - """set external gateway for router: myid externalid.""" + # set external gateway for router: myid externalid. resource = 'router' cmd = router.SetGatewayRouter(test_cli20.MyApp(sys.stdout), None) args = ['myid', 'externalid', '--disable-snat'] @@ -364,7 +358,7 @@ def test_set_gateway_disable_snat(self): ) def test_set_gateway_external_ip(self): - """set external gateway for router: myid externalid --fixed-ip ...""" + # set external gateway for router: myid externalid --fixed-ip ... resource = 'router' cmd = router.SetGatewayRouter(test_cli20.MyApp(sys.stdout), None) args = ['myid', 'externalid', '--fixed-ip', 'ip_address=10.0.0.2'] @@ -377,7 +371,7 @@ def test_set_gateway_external_ip(self): ) def test_set_gateway_external_subnet(self): - """set external gateway for router: myid externalid --fixed-ip ...""" + # set external gateway for router: myid externalid --fixed-ip ... resource = 'router' cmd = router.SetGatewayRouter(test_cli20.MyApp(sys.stdout), None) args = ['myid', 'externalid', '--fixed-ip', 'subnet_id=mysubnet'] @@ -390,7 +384,7 @@ def test_set_gateway_external_subnet(self): ) def test_remove_gateway(self): - """Remove external gateway from router: externalid.""" + # Remove external gateway from router: externalid. resource = 'router' cmd = router.RemoveGatewayRouter(test_cli20.MyApp(sys.stdout), None) args = ['externalid'] diff --git a/neutronclient/tests/unit/test_cli20_securitygroup.py b/neutronclient/tests/unit/test_cli20_securitygroup.py index 19e3c688e..9886587cc 100644 --- a/neutronclient/tests/unit/test_cli20_securitygroup.py +++ b/neutronclient/tests/unit/test_cli20_securitygroup.py @@ -31,7 +31,7 @@ class CLITestV20SecurityGroupsJSON(test_cli20.CLITestV20Base): non_admin_status_resources = ['security_group', 'security_group_rule'] def test_create_security_group(self): - """Create security group: webservers.""" + # Create security group: webservers. resource = 'security_group' cmd = securitygroup.CreateSecurityGroup( test_cli20.MyApp(sys.stdout), None) @@ -44,7 +44,7 @@ def test_create_security_group(self): position_names, position_values) def test_create_security_group_tenant(self): - """Create security group: webservers.""" + # Create security group: webservers. resource = 'security_group' cmd = securitygroup.CreateSecurityGroup( test_cli20.MyApp(sys.stdout), None) @@ -59,7 +59,7 @@ def test_create_security_group_tenant(self): tenant_id='tenant_id') def test_create_security_group_with_description(self): - """Create security group: webservers.""" + # Create security group: webservers. resource = 'security_group' cmd = securitygroup.CreateSecurityGroup( test_cli20.MyApp(sys.stdout), None) @@ -115,7 +115,7 @@ def test_show_security_group_id_name(self): args, ['id', 'name']) def test_delete_security_group(self): - """Delete security group: myid.""" + # Delete security group: myid. resource = 'security_group' cmd = securitygroup.DeleteSecurityGroup( test_cli20.MyApp(sys.stdout), None) @@ -124,7 +124,7 @@ def test_delete_security_group(self): self._test_delete_resource(resource, cmd, myid, args) def test_update_security_group(self): - """Update security group: myid --name myname --description desc.""" + # Update security group: myid --name myname --description desc. resource = 'security_group' cmd = securitygroup.UpdateSecurityGroup( test_cli20.MyApp(sys.stdout), None) @@ -147,7 +147,7 @@ def test_update_security_group_with_unicode(self): ) def test_create_security_group_rule_full(self): - """Create security group rule.""" + # Create security group rule. resource = 'security_group_rule' cmd = securitygroup.CreateSecurityGroupRule( test_cli20.MyApp(sys.stdout), None) @@ -175,7 +175,7 @@ def test_create_security_group_rule_full(self): position_names, position_values) def test_delete_security_group_rule(self): - """Delete security group rule: myid.""" + # Delete security group rule: myid. resource = 'security_group_rule' cmd = securitygroup.DeleteSecurityGroupRule( test_cli20.MyApp(sys.stdout), None) diff --git a/neutronclient/tests/unit/test_cli20_subnet.py b/neutronclient/tests/unit/test_cli20_subnet.py index 84d59e5e7..4a28198f5 100644 --- a/neutronclient/tests/unit/test_cli20_subnet.py +++ b/neutronclient/tests/unit/test_cli20_subnet.py @@ -32,7 +32,7 @@ def setUp(self): super(CLITestV20SubnetJSON, self).setUp(plurals={'tags': 'tag'}) def test_create_subnet(self): - """Create subnet: --gateway gateway netid cidr.""" + # Create subnet: --gateway gateway netid cidr. resource = 'subnet' cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -48,7 +48,7 @@ def test_create_subnet(self): def test_create_subnet_network_cidr_seperated(self): # For positional value, network_id and cidr can be separated. - """Create subnet: --gateway gateway netid cidr.""" + # Create subnet: --gateway gateway netid cidr. resource = 'subnet' cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -63,7 +63,7 @@ def test_create_subnet_network_cidr_seperated(self): position_names, position_values) def test_create_subnet_with_no_gateway(self): - """Create subnet: --no-gateway netid cidr.""" + # Create subnet: --no-gateway netid cidr. resource = 'subnet' cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -77,7 +77,7 @@ def test_create_subnet_with_no_gateway(self): position_names, position_values) def test_create_subnet_with_bad_gateway_option(self): - """Create sbunet: --no-gateway netid cidr.""" + # Create sbunet: --no-gateway netid cidr. resource = 'subnet' cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -102,7 +102,7 @@ def _test_create_resource_and_catch_command_error(self, should_fail, self._test_create_resource(*args, **params) def test_create_subnet_with_enable_and_disable_dhcp(self): - """Create subnet: --enable-dhcp and --disable-dhcp.""" + # Create subnet: --enable-dhcp and --disable-dhcp. resource = 'subnet' cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -125,7 +125,7 @@ def test_create_subnet_with_enable_and_disable_dhcp(self): resource, cmd, name, myid, args, position_names, pos_values) def test_create_subnet_with_multiple_enable_dhcp(self): - """Create subnet with multiple --enable-dhcp arguments passed.""" + # Create subnet with multiple --enable-dhcp arguments passed. resource = 'subnet' cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -151,7 +151,7 @@ def test_create_subnet_with_multiple_enable_dhcp(self): resource, cmd, name, myid, args, position_names, pos_values) def test_create_subnet_tenant(self): - """Create subnet: --tenant_id tenantid netid cidr.""" + # Create subnet: --tenant_id tenantid netid cidr. resource = 'subnet' cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -166,7 +166,7 @@ def test_create_subnet_tenant(self): tenant_id='tenantid') def test_create_subnet_tags(self): - """Create subnet: netid cidr --tags a b.""" + # Create subnet: netid cidr --tags a b. resource = 'subnet' cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -181,9 +181,8 @@ def test_create_subnet_tags(self): tags=['a', 'b']) def test_create_subnet_allocation_pool(self): - """Create subnet: --tenant_id tenantid netid cidr. - The is --allocation_pool start=1.1.1.10,end=1.1.1.20 - """ + # Create subnet: --tenant_id tenantid netid cidr. + # The is --allocation_pool start=1.1.1.10,end=1.1.1.20 resource = 'subnet' cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -202,10 +201,9 @@ def test_create_subnet_allocation_pool(self): tenant_id='tenantid') def test_create_subnet_allocation_pools(self): - """Create subnet: --tenant-id tenantid netid cidr. - The are --allocation_pool start=1.1.1.10,end=1.1.1.20 and - --allocation_pool start=1.1.1.30,end=1.1.1.40 - """ + # Create subnet: --tenant-id tenantid netid cidr. + # The are --allocation_pool start=1.1.1.10,end=1.1.1.20 and + # --allocation_pool start=1.1.1.30,end=1.1.1.40 resource = 'subnet' cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -226,10 +224,9 @@ def test_create_subnet_allocation_pools(self): tenant_id='tenantid') def test_create_subnet_host_route(self): - """Create subnet: --tenant_id tenantid netid cidr. - The is - --host-route destination=172.16.1.0/24,nexthop=1.1.1.20 - """ + # Create subnet: --tenant_id tenantid netid cidr. + # The is + # --host-route destination=172.16.1.0/24,nexthop=1.1.1.20 resource = 'subnet' cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -248,11 +245,10 @@ def test_create_subnet_host_route(self): tenant_id='tenantid') def test_create_subnet_host_routes(self): - """Create subnet: --tenant-id tenantid netid cidr. - The are - --host-route destination=172.16.1.0/24,nexthop=1.1.1.20 and - --host-route destination=172.17.7.0/24,nexthop=1.1.1.40 - """ + # Create subnet: --tenant-id tenantid netid cidr. + # The are + # --host-route destination=172.16.1.0/24,nexthop=1.1.1.20 and + # --host-route destination=172.17.7.0/24,nexthop=1.1.1.40 resource = 'subnet' cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -273,10 +269,9 @@ def test_create_subnet_host_routes(self): tenant_id='tenantid') def test_create_subnet_dns_nameservers(self): - """Create subnet: --tenant-id tenantid netid cidr. - The are - --dns-nameserver 1.1.1.20 and --dns-nameserver 1.1.1.40 - """ + # Create subnet: --tenant-id tenantid netid cidr. + # The are + # --dns-nameserver 1.1.1.20 and --dns-nameserver 1.1.1.40 resource = 'subnet' cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -296,7 +291,7 @@ def test_create_subnet_dns_nameservers(self): tenant_id='tenantid') def test_create_subnet_with_disable_dhcp(self): - """Create subnet: --tenant-id tenantid --disable-dhcp netid cidr.""" + # Create subnet: --tenant-id tenantid --disable-dhcp netid cidr. resource = 'subnet' cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -375,7 +370,7 @@ def test_create_subnet_merge_single_single(self): tenant_id='tenantid') def test_create_subnet_max_v4_cidr(self): - """Create subnet: --gateway gateway netid cidr.""" + # Create subnet: --gateway gateway netid cidr. resource = 'subnet' cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -563,25 +558,25 @@ def test_create_subnet_with_subnetpool_ipv4_with_prefixlen(self): no_api_call=True, expected_exception=exceptions.CommandError) def test_list_subnets_detail(self): - """List subnets: -D.""" + # List subnets: -D. resources = "subnets" cmd = subnet.ListSubnet(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, True) def test_list_subnets_tags(self): - """List subnets: -- --tags a b.""" + # List subnets: -- --tags a b. resources = "subnets" cmd = subnet.ListSubnet(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, tags=['a', 'b']) def test_list_subnets_detail_tags(self): - """List subnets: -D -- --tags a b.""" + # List subnets: -D -- --tags a b. resources = "subnets" cmd = subnet.ListSubnet(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, detail=True, tags=['a', 'b']) def test_list_subnets_fields(self): - """List subnets: --fields a --fields b -- --fields c d.""" + # List subnets: --fields a --fields b -- --fields c d. resources = "subnets" cmd = subnet.ListSubnet(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, @@ -593,9 +588,8 @@ def test_list_subnets_pagination(self): self._test_list_resources_with_pagination(resources, cmd) def test_list_subnets_sort(self): - """List subnets: --sort-key name --sort-key id --sort-key asc - --sort-key desc - """ + # List subnets: --sort-key name --sort-key id --sort-key asc + # --sort-key desc resources = "subnets" cmd = subnet.ListSubnet(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, @@ -603,13 +597,13 @@ def test_list_subnets_sort(self): sort_dir=["asc", "desc"]) def test_list_subnets_limit(self): - """List subnets: -P.""" + # List subnets: -P. resources = "subnets" cmd = subnet.ListSubnet(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, page_size=1000) def test_update_subnet(self): - """Update subnet: myid --name myname --tags a b.""" + # Update subnet: myid --name myname --tags a b. resource = 'subnet' cmd = subnet.UpdateSubnet(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', @@ -619,7 +613,7 @@ def test_update_subnet(self): ) def test_update_subnet_allocation_pools(self): - """Update subnet: myid --name myname --tags a b.""" + # Update subnet: myid --name myname --tags a b. resource = 'subnet' cmd = subnet.UpdateSubnet(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', @@ -630,7 +624,7 @@ def test_update_subnet_allocation_pools(self): ) def test_update_subnet_enable_disable_dhcp(self): - """Update sbunet: --enable-dhcp and --disable-dhcp.""" + # Update sbunet: --enable-dhcp and --disable-dhcp. resource = 'subnet' cmd = subnet.UpdateSubnet(test_cli20.MyApp(sys.stdout), None) try: @@ -644,7 +638,7 @@ def test_update_subnet_enable_disable_dhcp(self): self.fail('No exception for --enable-dhcp --disable-dhcp option') def test_show_subnet(self): - """Show subnet: --fields id --fields name myid.""" + # Show subnet: --fields id --fields name myid. resource = 'subnet' cmd = subnet.ShowSubnet(test_cli20.MyApp(sys.stdout), None) args = ['--fields', 'id', '--fields', 'name', self.test_id] @@ -652,7 +646,7 @@ def test_show_subnet(self): args, ['id', 'name']) def test_delete_subnet(self): - """Delete subnet: subnetid.""" + # Delete subnet: subnetid. resource = 'subnet' cmd = subnet.DeleteSubnet(test_cli20.MyApp(sys.stdout), None) myid = 'myid' diff --git a/neutronclient/tests/unit/test_cli20_subnetpool.py b/neutronclient/tests/unit/test_cli20_subnetpool.py index e5555f793..dd743aa98 100644 --- a/neutronclient/tests/unit/test_cli20_subnetpool.py +++ b/neutronclient/tests/unit/test_cli20_subnetpool.py @@ -31,7 +31,7 @@ def setUp(self): super(CLITestV20SubnetPoolJSON, self).setUp(plurals={'tags': 'tag'}) def test_create_subnetpool_shared(self): - """Create subnetpool: myname.""" + # Create subnetpool: myname. resource = 'subnetpool' cmd = subnetpool.CreateSubnetPool(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -48,7 +48,7 @@ def test_create_subnetpool_shared(self): position_names, position_values) def test_create_subnetpool_not_shared(self): - """Create subnetpool: myname.""" + # Create subnetpool: myname. resource = 'subnetpool' cmd = subnetpool.CreateSubnetPool(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -64,7 +64,7 @@ def test_create_subnetpool_not_shared(self): position_names, position_values) def test_create_subnetpool_with_unicode(self): - """Create subnetpool: u'\u7f51\u7edc'.""" + # Create subnetpool: u'\u7f51\u7edc'. resource = 'subnetpool' cmd = subnetpool.CreateSubnetPool(test_cli20.MyApp(sys.stdout), None) name = u'\u7f51\u7edc' @@ -79,7 +79,7 @@ def test_create_subnetpool_with_unicode(self): position_names, position_values) def test_create_subnetpool_with_addrscope(self): - """Create subnetpool: myname in addrscope: foo-address-scope""" + # Create subnetpool: myname in addrscope: foo-address-scope resource = 'subnetpool' cmd = subnetpool.CreateSubnetPool(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -107,9 +107,8 @@ def test_list_subnetpool_pagination(self): self.mox.UnsetStubs() def test_list_subnetpools_sort(self): - """List subnetpools: --sort-key name --sort-key id --sort-key asc - --sort-key desc - """ + # List subnetpools: + # --sort-key name --sort-key id --sort-key asc --sort-key desc resources = "subnetpools" cmd = subnetpool.ListSubnetPool(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, @@ -117,20 +116,20 @@ def test_list_subnetpools_sort(self): sort_dir=["asc", "desc"]) def test_list_subnetpools_limit(self): - """List subnetpools: -P.""" + # List subnetpools: -P. resources = "subnetpools" cmd = subnetpool.ListSubnetPool(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, page_size=1000) def test_update_subnetpool_exception(self): - """Update subnetpool: myid.""" + # Update subnetpool: myid. resource = 'subnetpool' cmd = subnetpool.UpdateSubnetPool(test_cli20.MyApp(sys.stdout), None) self.assertRaises(exceptions.CommandError, self._test_update_resource, resource, cmd, 'myid', ['myid'], {}) def test_update_subnetpool(self): - """Update subnetpool: myid --name myname.""" + # Update subnetpool: myid --name myname. resource = 'subnetpool' cmd = subnetpool.UpdateSubnetPool(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', @@ -139,7 +138,7 @@ def test_update_subnetpool(self): ) def test_update_subnetpool_with_address_scope(self): - """Update subnetpool: myid --address-scope newscope.""" + # Update subnetpool: myid --address-scope newscope. resource = 'subnetpool' cmd = subnetpool.UpdateSubnetPool(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', @@ -148,7 +147,7 @@ def test_update_subnetpool_with_address_scope(self): ) def test_update_subnetpool_with_no_address_scope(self): - """Update subnetpool: myid --no-address-scope.""" + # Update subnetpool: myid --no-address-scope. resource = 'subnetpool' cmd = subnetpool.UpdateSubnetPool(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', @@ -157,7 +156,7 @@ def test_update_subnetpool_with_no_address_scope(self): ) def test_show_subnetpool(self): - """Show subnetpool: --fields id --fields name myid.""" + # Show subnetpool: --fields id --fields name myid. resource = 'subnetpool' cmd = subnetpool.ShowSubnetPool(test_cli20.MyApp(sys.stdout), None) args = ['--fields', 'id', '--fields', 'name', self.test_id] @@ -165,7 +164,7 @@ def test_show_subnetpool(self): ['id', 'name']) def test_delete_subnetpool(self): - """Delete subnetpool: subnetpoolid.""" + # Delete subnetpool: subnetpoolid. resource = 'subnetpool' cmd = subnetpool.DeleteSubnetPool(test_cli20.MyApp(sys.stdout), None) myid = 'myid' diff --git a/neutronclient/tests/unit/test_client_extension.py b/neutronclient/tests/unit/test_client_extension.py index 1cd4dcb96..d0609b3e8 100644 --- a/neutronclient/tests/unit/test_client_extension.py +++ b/neutronclient/tests/unit/test_client_extension.py @@ -68,7 +68,7 @@ def test_ext_cmd_help_doc_with_extension_name(self): self.assertTrue(cmdcls.__doc__.startswith("[_fox_sockets]")) def test_delete_fox_socket(self): - """Delete fox socket: myid.""" + # Delete fox socket: myid. resource = 'fox_socket' cmd = fox_sockets.FoxInSocketsDelete(test_cli20.MyApp(sys.stdout), None) @@ -77,7 +77,7 @@ def test_delete_fox_socket(self): self._test_delete_resource(resource, cmd, myid, args) def test_update_fox_socket(self): - """Update fox_socket: myid --name myname.""" + # Update fox_socket: myid --name myname. resource = 'fox_socket' cmd = fox_sockets.FoxInSocketsUpdate(test_cli20.MyApp(sys.stdout), None) @@ -86,7 +86,7 @@ def test_update_fox_socket(self): {'name': 'myname'}) def test_create_fox_socket(self): - """Create fox_socket: myname.""" + # Create fox_socket: myname. resource = 'fox_socket' cmd = fox_sockets.FoxInSocketsCreate(test_cli20.MyApp(sys.stdout), None) @@ -99,13 +99,13 @@ def test_create_fox_socket(self): position_names, position_values) def test_list_fox_sockets(self): - """List fox_sockets.""" + # List fox_sockets. resources = 'fox_sockets' cmd = fox_sockets.FoxInSocketsList(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, True) def test_show_fox_socket(self): - """Show fox_socket: --fields id --fields name myid.""" + # Show fox_socket: --fields id --fields name myid. resource = 'fox_socket' cmd = fox_sockets.FoxInSocketsShow(test_cli20.MyApp(sys.stdout), None) args = ['--fields', 'id', '--fields', 'name', self.test_id] @@ -150,7 +150,7 @@ def test_ext_cmd_loaded(self): self.assertDictContainsSubset(ext_cmd, shell.COMMANDS['2.0']) def test_list_ip_addresses(self): - """List ip_addresses.""" + # List ip_addresses. resources = 'ip_addresses' cmd = self.IPAddressesList(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, True) diff --git a/neutronclient/tests/unit/test_http.py b/neutronclient/tests/unit/test_http.py index d16f87b32..2f65b8ed0 100644 --- a/neutronclient/tests/unit/test_http.py +++ b/neutronclient/tests/unit/test_http.py @@ -44,7 +44,7 @@ def initialize(self): """Return client class, instance.""" def _test_headers(self, expected_headers, **kwargs): - """Test headers.""" + # Test headers. self.requests.register_uri(METHOD, URL, request_headers=expected_headers) self.http.request(URL, METHOD, **kwargs) diff --git a/neutronclient/tests/unit/vpn/test_cli20_endpoint_group.py b/neutronclient/tests/unit/vpn/test_cli20_endpoint_group.py index 948c14406..f43da906b 100644 --- a/neutronclient/tests/unit/vpn/test_cli20_endpoint_group.py +++ b/neutronclient/tests/unit/vpn/test_cli20_endpoint_group.py @@ -27,7 +27,7 @@ def setUp(self): self.register_non_admin_status_resource('endpoint_group') def test_create_endpoint_group_with_cidrs(self): - """vpn-endpoint-group-create with CIDR endpoints.""" + # vpn-endpoint-group-create with CIDR endpoints.""" resource = 'endpoint_group' cmd = endpoint_group.CreateEndpointGroup(test_cli20.MyApp(sys.stdout), None) @@ -55,7 +55,7 @@ def test_create_endpoint_group_with_cidrs(self): position_names, position_values) def test_create_endpoint_group_with_subnets(self): - """vpn-endpoint-group-create with subnet endpoints.""" + # vpn-endpoint-group-create with subnet endpoints.""" resource = 'endpoint_group' cmd = endpoint_group.CreateEndpointGroup(test_cli20.MyApp(sys.stdout), None) @@ -77,23 +77,22 @@ def test_create_endpoint_group_with_subnets(self): position_names, position_values) def test_list_endpoint_group(self): - """vpn-endpoint-group-list.""" + # vpn-endpoint-group-list. resources = "endpoint_groups" cmd = endpoint_group.ListEndpointGroup(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, True) def test_list_endpoint_group_pagination(self): - """vpn-endpoint-group-list.""" + # vpn-endpoint-group-list. resources = "endpoint_groups" cmd = endpoint_group.ListEndpointGroup(test_cli20.MyApp(sys.stdout), None) self._test_list_resources_with_pagination(resources, cmd) def test_list_endpoint_group_sort(self): - """vpn-endpoint-group-list --sort-key name --sort-key id - --sort-key asc --sort-key desc - """ + # vpn-endpoint-group-list --sort-key name --sort-key id + # --sort-key asc --sort-key desc resources = "endpoint_groups" cmd = endpoint_group.ListEndpointGroup(test_cli20.MyApp(sys.stdout), None) @@ -102,14 +101,14 @@ def test_list_endpoint_group_sort(self): sort_dir=["asc", "desc"]) def test_list_endpoint_group_limit(self): - """vpn-endpoint-group-list -P.""" + # vpn-endpoint-group-list -P. resources = "endpoint_groups" cmd = endpoint_group.ListEndpointGroup(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, page_size=1000) def test_show_endpoint_group_id(self): - """vpn-endpoint-group-show test_id.""" + # vpn-endpoint-group-show test_id. resource = 'endpoint_group' cmd = endpoint_group.ShowEndpointGroup(test_cli20.MyApp(sys.stdout), None) @@ -117,7 +116,7 @@ def test_show_endpoint_group_id(self): self._test_show_resource(resource, cmd, self.test_id, args, ['id']) def test_show_endpoint_group_id_name(self): - """vpn-endpoint-group-show.""" + # vpn-endpoint-group-show. resource = 'endpoint_group' cmd = endpoint_group.ShowEndpointGroup(test_cli20.MyApp(sys.stdout), None) @@ -126,9 +125,7 @@ def test_show_endpoint_group_id_name(self): args, ['id', 'name']) def test_update_endpoint_group(self): - """vpn-endpoint-group-update myid --name newname - --description newdesc. - """ + # vpn-endpoint-group-update myid --name newname --description newdesc. resource = 'endpoint_group' cmd = endpoint_group.UpdateEndpointGroup(test_cli20.MyApp(sys.stdout), None) @@ -139,7 +136,7 @@ def test_update_endpoint_group(self): 'description': 'newdesc'}) def test_delete_endpoint_group(self): - """vpn-endpoint-group-delete my-id.""" + # vpn-endpoint-group-delete my-id. resource = 'endpoint_group' cmd = endpoint_group.DeleteEndpointGroup(test_cli20.MyApp(sys.stdout), None) diff --git a/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py b/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py index 4295fc6ec..d7d1bbfe9 100644 --- a/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py +++ b/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py @@ -25,7 +25,7 @@ class CLITestV20VpnIkePolicyJSON(test_cli20.CLITestV20Base): non_admin_status_resources = ['ikepolicy'] def test_create_ikepolicy_all_params(self): - """vpn-ikepolicy-create all params.""" + # vpn-ikepolicy-create all params. resource = 'ikepolicy' cmd = ikepolicy.CreateIKEPolicy(test_cli20.MyApp(sys.stdout), None) name = 'ikepolicy1' @@ -71,7 +71,7 @@ def test_create_ikepolicy_all_params(self): extra_body=extra_body) def test_create_ikepolicy_with_limited_params(self): - """vpn-ikepolicy-create with limited params.""" + # vpn-ikepolicy-create with limited params. resource = 'ikepolicy' cmd = ikepolicy.CreateIKEPolicy(test_cli20.MyApp(sys.stdout), None) name = 'ikepolicy1' @@ -150,21 +150,20 @@ def test_create_ikepolicy_with_invalid_lifetime_value(self): self._test_lifetime_values(lifetime) def test_list_ikepolicy(self): - """vpn-ikepolicy-list.""" + # vpn-ikepolicy-list. resources = "ikepolicies" cmd = ikepolicy.ListIKEPolicy(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, True) def test_list_ikepolicy_pagination(self): - """vpn-ikepolicy-list.""" + # vpn-ikepolicy-list. resources = "ikepolicies" cmd = ikepolicy.ListIKEPolicy(test_cli20.MyApp(sys.stdout), None) self._test_list_resources_with_pagination(resources, cmd) def test_list_ikepolicy_sort(self): - """vpn-ikepolicy-list --sort-key name --sort-key id --sort-key asc - --sort-key desc - """ + # vpn-ikepolicy-list --sort-key name --sort-key id --sort-key asc + # --sort-key desc resources = "ikepolicies" cmd = ikepolicy.ListIKEPolicy(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, @@ -172,20 +171,20 @@ def test_list_ikepolicy_sort(self): sort_dir=["asc", "desc"]) def test_list_ikepolicy_limit(self): - """vpn-ikepolicy-list -P.""" + # vpn-ikepolicy-list -P. resources = "ikepolicies" cmd = ikepolicy.ListIKEPolicy(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, page_size=1000) def test_show_ikepolicy_id(self): - """vpn-ikepolicy-show ikepolicy_id.""" + # vpn-ikepolicy-show ikepolicy_id. resource = 'ikepolicy' cmd = ikepolicy.ShowIKEPolicy(test_cli20.MyApp(sys.stdout), None) args = ['--fields', 'id', self.test_id] self._test_show_resource(resource, cmd, self.test_id, args, ['id']) def test_show_ikepolicy_id_name(self): - """vpn-ikepolicy-show.""" + # vpn-ikepolicy-show. resource = 'ikepolicy' cmd = ikepolicy.ShowIKEPolicy(test_cli20.MyApp(sys.stdout), None) args = ['--fields', 'id', '--fields', 'name', self.test_id] @@ -193,7 +192,7 @@ def test_show_ikepolicy_id_name(self): args, ['id', 'name']) def test_update_ikepolicy(self): - """vpn-ikepolicy-update myid --name newname --tags a b.""" + # vpn-ikepolicy-update myid --name newname --tags a b. resource = 'ikepolicy' cmd = ikepolicy.UpdateIKEPolicy(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', @@ -201,7 +200,7 @@ def test_update_ikepolicy(self): {'name': 'newname', }) def test_delete_ikepolicy(self): - """vpn-ikepolicy-delete my-id.""" + # vpn-ikepolicy-delete my-id. resource = 'ikepolicy' cmd = ikepolicy.DeleteIKEPolicy(test_cli20.MyApp(sys.stdout), None) my_id = 'my-id' diff --git a/neutronclient/tests/unit/vpn/test_cli20_ipsec_site_connection.py b/neutronclient/tests/unit/vpn/test_cli20_ipsec_site_connection.py index 106866309..85a28b60d 100644 --- a/neutronclient/tests/unit/vpn/test_cli20_ipsec_site_connection.py +++ b/neutronclient/tests/unit/vpn/test_cli20_ipsec_site_connection.py @@ -25,7 +25,7 @@ class CLITestV20IPsecSiteConnectionJSON(test_cli20.CLITestV20Base): # TODO(pcm): Remove, once peer-cidr is deprecated completely def test_create_ipsec_site_connection_all_params_using_peer_cidrs(self): - """ipsecsite-connection-create all params using peer CIDRs.""" + # ipsecsite-connection-create all params using peer CIDRs. resource = 'ipsec_site_connection' cmd = ipsec_site_connection.CreateIPsecSiteConnection( test_cli20.MyApp(sys.stdout), None @@ -80,7 +80,7 @@ def test_create_ipsec_site_connection_all_params_using_peer_cidrs(self): extra_body=extra_body) def test_create_ipsec_site_conn_all_params(self): - """ipsecsite-connection-create all params using endpoint groups.""" + # ipsecsite-connection-create all params using endpoint groups. resource = 'ipsec_site_connection' cmd = ipsec_site_connection.CreateIPsecSiteConnection( test_cli20.MyApp(sys.stdout), None @@ -137,7 +137,7 @@ def test_create_ipsec_site_conn_all_params(self): extra_body=extra_body) def test_create_ipsec_site_connection_with_limited_params(self): - """ipsecsite-connection-create with limited params.""" + # ipsecsite-connection-create with limited params. resource = 'ipsec_site_connection' cmd = ipsec_site_connection.CreateIPsecSiteConnection( test_cli20.MyApp(sys.stdout), None @@ -181,7 +181,7 @@ def test_create_ipsec_site_connection_with_limited_params(self): position_names, position_values) def _test_create_failure(self, additional_args=None): - """Helper to test failure of IPSec site-to-site creation failure.""" + # Helper to test failure of IPSec site-to-site creation failure. resource = 'ipsec_site_connection' cmd = ipsec_site_connection.CreateIPsecSiteConnection( test_cli20.MyApp(sys.stdout), None @@ -221,7 +221,7 @@ def _test_create_failure(self, additional_args=None): position_names, position_values) def test_fail_create_with_invalid_mtu(self): - """ipsecsite-connection-create with invalid dpd values.""" + # ipsecsite-connection-create with invalid dpd values. bad_mtu = ['--mtu', '67'] self._test_create_failure(bad_mtu) @@ -234,26 +234,26 @@ def test_fail_create_with_invalid_dpd_values(self): self._test_create_failure(bad_dpd_values) def test_fail_create_missing_endpoint_groups_or_cidr(self): - """Must provide either endpoint groups or peer cidrs.""" + # Must provide either endpoint groups or peer cidrs. self._test_create_failure() def test_fail_create_missing_peer_endpoint_group(self): - """Fails if dont have both endpoint groups - missing peer.""" + # Fails if dont have both endpoint groups - missing peer. self._test_create_failure(['--local-ep-group', 'local-epg']) def test_fail_create_missing_local_endpoint_group(self): - """Fails if dont have both endpoint groups - missing local.""" + # Fails if dont have both endpoint groups - missing local. self._test_create_failure(['--peer-ep-group', 'peer-epg']) def test_fail_create_when_both_endpoints_and_peer_cidr(self): - """Cannot intermix endpoint groups and peer CIDRs for create.""" + # Cannot intermix endpoint groups and peer CIDRs for create. additional_args = ['--local-ep-group', 'local-epg', '--peer-ep-group', 'peer-epg', '--peer-cidr', '10.2.0.0/24'] self._test_create_failure(additional_args) def test_list_ipsec_site_connection(self): - """ipsecsite-connection-list.""" + # ipsecsite-connection-list. resources = "ipsec_site_connections" cmd = ipsec_site_connection.ListIPsecSiteConnection( test_cli20.MyApp(sys.stdout), None @@ -261,7 +261,7 @@ def test_list_ipsec_site_connection(self): self._test_list_resources(resources, cmd, True) def test_list_ipsec_site_connection_pagination(self): - """ipsecsite-connection-list.""" + # ipsecsite-connection-list. resources = "ipsec_site_connections" cmd = ipsec_site_connection.ListIPsecSiteConnection( test_cli20.MyApp(sys.stdout), None @@ -269,9 +269,8 @@ def test_list_ipsec_site_connection_pagination(self): self._test_list_resources_with_pagination(resources, cmd) def test_list_ipsec_site_connection_sort(self): - """ipsecsite-connection-list. - --sort-key name --sort-key id --sort-key asc --sort-key desc - """ + # ipsecsite-connection-list. + # --sort-key name --sort-key id --sort-key asc --sort-key desc resources = "ipsec_site_connections" cmd = ipsec_site_connection.ListIPsecSiteConnection( test_cli20.MyApp(sys.stdout), None @@ -281,7 +280,7 @@ def test_list_ipsec_site_connection_sort(self): sort_dir=["asc", "desc"]) def test_list_ipsec_site_connection_limit(self): - """ipsecsite-connection-list -P.""" + # ipsecsite-connection-list -P. resources = "ipsec_site_connections" cmd = ipsec_site_connection.ListIPsecSiteConnection( test_cli20.MyApp(sys.stdout), None @@ -289,7 +288,7 @@ def test_list_ipsec_site_connection_limit(self): self._test_list_resources(resources, cmd, page_size=1000) def test_delete_ipsec_site_connection(self): - """ipsecsite-connection-delete my-id.""" + # ipsecsite-connection-delete my-id. resource = 'ipsec_site_connection' cmd = ipsec_site_connection.DeleteIPsecSiteConnection( test_cli20.MyApp(sys.stdout), None @@ -299,7 +298,7 @@ def test_delete_ipsec_site_connection(self): self._test_delete_resource(resource, cmd, my_id, args) def test_update_ipsec_site_connection(self): - """ipsecsite-connection-update myid --name myname --tags a b.""" + # ipsecsite-connection-update myid --name myname --tags a b.""" resource = 'ipsec_site_connection' cmd = ipsec_site_connection.UpdateIPsecSiteConnection( test_cli20.MyApp(sys.stdout), None @@ -311,7 +310,7 @@ def test_update_ipsec_site_connection(self): 'tags': ['a', 'b'], }) def test_show_ipsec_site_connection_id(self): - """ipsecsite-connection-show test_id.""" + # ipsecsite-connection-show test_id.""" resource = 'ipsec_site_connection' cmd = ipsec_site_connection.ShowIPsecSiteConnection( test_cli20.MyApp(sys.stdout), None @@ -320,7 +319,7 @@ def test_show_ipsec_site_connection_id(self): self._test_show_resource(resource, cmd, self.test_id, args, ['id']) def test_show_ipsec_site_connection_id_name(self): - """ipsecsite-connection-show.""" + # ipsecsite-connection-show.""" resource = 'ipsec_site_connection' cmd = ipsec_site_connection.ShowIPsecSiteConnection( test_cli20.MyApp(sys.stdout), None diff --git a/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py b/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py index 9d42c6abd..5df90ba4e 100644 --- a/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py +++ b/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py @@ -25,7 +25,7 @@ class CLITestV20VpnIpsecPolicyJSON(test_cli20.CLITestV20Base): non_admin_status_resources = ['ipsecpolicy'] def test_create_ipsecpolicy_all_params(self): - """vpn-ipsecpolicy-create all params with dashes.""" + # vpn-ipsecpolicy-create all params with dashes. resource = 'ipsecpolicy' cmd = ipsecpolicy.CreateIPsecPolicy(test_cli20.MyApp(sys.stdout), None) name = 'ipsecpolicy1' @@ -70,7 +70,7 @@ def test_create_ipsecpolicy_all_params(self): extra_body=extra_body) def test_create_ipsecpolicy_with_limited_params(self): - """vpn-ipsecpolicy-create with limited params.""" + # vpn-ipsecpolicy-create with limited params. resource = 'ipsecpolicy' cmd = ipsecpolicy.CreateIPsecPolicy(test_cli20.MyApp(sys.stdout), None) name = 'ipsecpolicy1' @@ -147,21 +147,20 @@ def test_create_ipsecpolicy_with_invalide_lifetime_values(self): self._test_lifetime_values(lifetime) def test_list_ipsecpolicy(self): - """vpn-ipsecpolicy-list.""" + # vpn-ipsecpolicy-list. resources = "ipsecpolicies" cmd = ipsecpolicy.ListIPsecPolicy(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, True) def test_list_ipsecpolicy_pagination(self): - """vpn-ipsecpolicy-list.""" + # vpn-ipsecpolicy-list. resources = "ipsecpolicies" cmd = ipsecpolicy.ListIPsecPolicy(test_cli20.MyApp(sys.stdout), None) self._test_list_resources_with_pagination(resources, cmd) def test_list_ipsecpolicy_sort(self): - """vpn-ipsecpolicy-list --sort-key name --sort-key id --sort-key asc - --sort-key desc - """ + # vpn-ipsecpolicy-list --sort-key name --sort-key id --sort-key asc + # --sort-key desc resources = "ipsecpolicies" cmd = ipsecpolicy.ListIPsecPolicy(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, @@ -169,20 +168,20 @@ def test_list_ipsecpolicy_sort(self): sort_dir=["asc", "desc"]) def test_list_ipsecpolicy_limit(self): - """vpn-ipsecpolicy-list -P.""" + # vpn-ipsecpolicy-list -P. resources = "ipsecpolicies" cmd = ipsecpolicy.ListIPsecPolicy(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, page_size=1000) def test_show_ipsecpolicy_id(self): - """vpn-ipsecpolicy-show ipsecpolicy_id.""" + # vpn-ipsecpolicy-show ipsecpolicy_id. resource = 'ipsecpolicy' cmd = ipsecpolicy.ShowIPsecPolicy(test_cli20.MyApp(sys.stdout), None) args = ['--fields', 'id', self.test_id] self._test_show_resource(resource, cmd, self.test_id, args, ['id']) def test_show_ipsecpolicy_id_name(self): - """vpn-ipsecpolicy-show.""" + # vpn-ipsecpolicy-show. resource = 'ipsecpolicy' cmd = ipsecpolicy.ShowIPsecPolicy(test_cli20.MyApp(sys.stdout), None) args = ['--fields', 'id', '--fields', 'name', self.test_id] @@ -190,7 +189,7 @@ def test_show_ipsecpolicy_id_name(self): args, ['id', 'name']) def test_update_ipsecpolicy(self): - """vpn-ipsecpolicy-update myid --name newname --tags a b.""" + # vpn-ipsecpolicy-update myid --name newname --tags a b. resource = 'ipsecpolicy' cmd = ipsecpolicy.UpdateIPsecPolicy(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', @@ -198,7 +197,7 @@ def test_update_ipsecpolicy(self): {'name': 'newname', }) def test_delete_ipsecpolicy(self): - """vpn-ipsecpolicy-delete my-id.""" + # vpn-ipsecpolicy-delete my-id. resource = 'ipsecpolicy' cmd = ipsecpolicy.DeleteIPsecPolicy(test_cli20.MyApp(sys.stdout), None) my_id = 'my-id' diff --git a/neutronclient/tests/unit/vpn/test_cli20_vpnservice.py b/neutronclient/tests/unit/vpn/test_cli20_vpnservice.py index 2713fa62d..9b28c7364 100644 --- a/neutronclient/tests/unit/vpn/test_cli20_vpnservice.py +++ b/neutronclient/tests/unit/vpn/test_cli20_vpnservice.py @@ -23,7 +23,7 @@ class CLITestV20VpnServiceJSON(test_cli20.CLITestV20Base): def test_create_vpnservice_all_params(self): - """vpn-service-create all params.""" + # vpn-service-create all params. resource = 'vpnservice' cmd = vpnservice.CreateVPNService(test_cli20.MyApp(sys.stdout), None) subnet = 'mysubnet-id' @@ -51,7 +51,7 @@ def test_create_vpnservice_all_params(self): position_names, position_values) def test_create_vpnservice_with_limited_params(self): - """vpn-service-create with limited params.""" + # vpn-service-create with limited params. resource = 'vpnservice' cmd = vpnservice.CreateVPNService(test_cli20.MyApp(sys.stdout), None) subnet = 'mysubnet-id' @@ -74,7 +74,7 @@ def test_create_vpnservice_with_limited_params(self): position_names, position_values) def test_create_vpnservice_without_subnet(self): - """vpn-service-create with no subnet provided.""" + # vpn-service-create with no subnet provided. resource = 'vpnservice' cmd = vpnservice.CreateVPNService(test_cli20.MyApp(sys.stdout), None) router = 'myrouter-id' @@ -95,21 +95,20 @@ def test_create_vpnservice_without_subnet(self): position_names, position_values) def test_list_vpnservice(self): - """vpn-service-list.""" + # vpn-service-list. resources = "vpnservices" cmd = vpnservice.ListVPNService(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, True) def test_list_vpnservice_pagination(self): - """vpn-service-list.""" + # vpn-service-list. resources = "vpnservices" cmd = vpnservice.ListVPNService(test_cli20.MyApp(sys.stdout), None) self._test_list_resources_with_pagination(resources, cmd) def test_list_vpnservice_sort(self): - """vpn-service-list --sort-key name --sort-key id --sort-key asc - --sort-key desc - """ + # vpn-service-list --sort-key name --sort-key id --sort-key asc + # --sort-key desc resources = "vpnservices" cmd = vpnservice.ListVPNService(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, @@ -117,20 +116,20 @@ def test_list_vpnservice_sort(self): sort_dir=["asc", "desc"]) def test_list_vpnservice_limit(self): - """vpn-service-list -P.""" + # vpn-service-list -P. resources = "vpnservices" cmd = vpnservice.ListVPNService(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, page_size=1000) def test_show_vpnservice_id(self): - """vpn-service-show test_id.""" + # vpn-service-show test_id. resource = 'vpnservice' cmd = vpnservice.ShowVPNService(test_cli20.MyApp(sys.stdout), None) args = ['--fields', 'id', self.test_id] self._test_show_resource(resource, cmd, self.test_id, args, ['id']) def test_show_vpnservice_id_name(self): - """vpn-service-show.""" + # vpn-service-show.""" resource = 'vpnservice' cmd = vpnservice.ShowVPNService(test_cli20.MyApp(sys.stdout), None) args = ['--fields', 'id', '--fields', 'name', self.test_id] @@ -138,7 +137,7 @@ def test_show_vpnservice_id_name(self): args, ['id', 'name']) def test_update_vpnservice(self): - """vpn-service-update myid --name newname --tags a b.""" + # vpn-service-update myid --name newname --tags a b. resource = 'vpnservice' cmd = vpnservice.UpdateVPNService(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', @@ -146,7 +145,7 @@ def test_update_vpnservice(self): {'name': 'newname', }) def test_delete_vpnservice(self): - """vpn-service-delete my-id.""" + # vpn-service-delete my-id. resource = 'vpnservice' cmd = vpnservice.DeleteVPNService(test_cli20.MyApp(sys.stdout), None) my_id = 'my-id' diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 3c3c84af4..830ada569 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -88,8 +88,7 @@ def exception_handler_v20(status_code, error_content): class APIParamsCall(object): - """A Decorator to add support for format and tenant overriding and filters. - """ + """A Decorator to support formating and tenant overriding and filters.""" def __init__(self, function): self.function = function @@ -152,7 +151,6 @@ class ClientBase(object): nets = neutron.list_networks() ... - """ # API has no way to report plurals, so we have to hard code them @@ -453,38 +451,32 @@ class Client(ClientBase): @APIParamsCall def list_ext(self, path, **_params): - """Client extension hook for lists. - """ + """Client extension hook for lists.""" return self.get(path, params=_params) @APIParamsCall def show_ext(self, path, id, **_params): - """Client extension hook for shows. - """ + """Client extension hook for shows.""" return self.get(path % id, params=_params) @APIParamsCall def create_ext(self, path, body=None): - """Client extension hook for creates. - """ + """Client extension hook for creates.""" return self.post(path, body=body) @APIParamsCall def update_ext(self, path, id, body=None): - """Client extension hook for updates. - """ + """Client extension hook for updates.""" return self.put(path % id, body=body) @APIParamsCall def delete_ext(self, path, id): - """Client extension hook for deletes. - """ + """Client extension hook for deletes.""" return self.delete(path % id) @APIParamsCall def get_quotas_tenant(self, **_params): - """Fetch tenant info in server's context for following quota operation. - """ + """Fetch tenant info for following quota operation.""" return self.get(self.quota_path % 'tenant', params=_params) @APIParamsCall diff --git a/tox.ini b/tox.ini index 8dce7dcd3..37e6dbfec 100644 --- a/tox.ini +++ b/tox.ini @@ -45,8 +45,5 @@ commands = sphinx-build -a -E -d releasenotes/build/doctrees -b html releasenote downloadcache = ~/cache/pip [flake8] -# H405 multi line docstring summary not separated with an empty line -# (mutli line docstring is used in unit tests frequently) -ignore = H405 show-source = true exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,tools From 815934c9e1c0abb57cbe87bc0b6d0bd869591c01 Mon Sep 17 00:00:00 2001 From: Kevin Benton Date: Wed, 9 Dec 2015 14:55:27 -0800 Subject: [PATCH 321/845] Allow tenant_id positional in quota syntax This patch allows tenant_id as a positional argument to the neutron 'quota-show', 'quota-update', and 'quota-delete' command. This is helpful because the positional argument is thrown away by default and the calling tenant ID is used instead so a user can unexpectedly update or view the wrong quota. This syntax is still hidden from the help output to encourage the more explicit --tenant-id syntax. Closes-Bug: #1524532 Change-Id: I83d4e0763cc8513a5d608a3754f7d46eaa3f1f6b --- neutronclient/neutron/v2_0/quota.py | 25 +++++++++++++++++-------- neutronclient/tests/unit/test_quota.py | 17 +++++++++++++++++ 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/neutronclient/neutron/v2_0/quota.py b/neutronclient/neutron/v2_0/quota.py index eb750744a..7346f54b8 100644 --- a/neutronclient/neutron/v2_0/quota.py +++ b/neutronclient/neutron/v2_0/quota.py @@ -29,8 +29,8 @@ from neutronclient.neutron import v2_0 as neutronV20 -def get_tenant_id(tenant_id, client): - return (tenant_id if tenant_id else +def get_tenant_id(args, client): + return (args.pos_tenant_id or args.tenant_id or client.get_quotas_tenant()['tenant']['tenant_id']) @@ -48,13 +48,15 @@ def get_parser(self, prog_name): parser.add_argument( '--tenant_id', help=argparse.SUPPRESS) + parser.add_argument( + 'pos_tenant_id', + help=argparse.SUPPRESS, nargs='?') return parser def run(self, parsed_args): self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() - tenant_id = get_tenant_id(parsed_args.tenant_id, - neutron_client) + tenant_id = get_tenant_id(parsed_args, neutron_client) obj_deleter = getattr(neutron_client, "delete_%s" % self.resource) obj_deleter(tenant_id) @@ -107,13 +109,18 @@ def get_parser(self, prog_name): parser.add_argument( '--tenant_id', help=argparse.SUPPRESS) + # allow people to do neutron quota-show . + # we use a different name for this because the default will + # override whatever is in the named arg otherwise. + parser.add_argument( + 'pos_tenant_id', + help=argparse.SUPPRESS, nargs='?') return parser def get_data(self, parsed_args): self.log.debug('get_data(%s)', parsed_args) neutron_client = self.get_client() - tenant_id = get_tenant_id(parsed_args.tenant_id, - neutron_client) + tenant_id = get_tenant_id(parsed_args, neutron_client) params = {} obj_shower = getattr(neutron_client, "show_%s" % self.resource) @@ -183,6 +190,9 @@ def get_parser(self, prog_name): parser.add_argument( '--health-monitor', metavar='health_monitors', help=_('The limit of health monitors.')) + parser.add_argument( + 'pos_tenant_id', + help=argparse.SUPPRESS, nargs='?') return parser @@ -219,8 +229,7 @@ def get_data(self, parsed_args): body[self.resource] = _extra_values obj_updator = getattr(neutron_client, "update_%s" % self.resource) - tenant_id = get_tenant_id(parsed_args.tenant_id, - neutron_client) + tenant_id = get_tenant_id(parsed_args, neutron_client) data = obj_updator(tenant_id, body) if self.resource in data: for k, v in six.iteritems(data[self.resource]): diff --git a/neutronclient/tests/unit/test_quota.py b/neutronclient/tests/unit/test_quota.py index 5364a412e..5be7ba3b7 100644 --- a/neutronclient/tests/unit/test_quota.py +++ b/neutronclient/tests/unit/test_quota.py @@ -42,3 +42,20 @@ def test_update_quota(self): def test_delete_quota_get_parser(self): cmd = test_cli20.MyApp(sys.stdout) test_quota.DeleteQuota(cmd, None).get_parser(cmd) + + def test_show_quota_positional(self): + resource = 'quota' + cmd = test_quota.ShowQuota( + test_cli20.MyApp(sys.stdout), None) + args = [self.test_id] + self._test_show_resource(resource, cmd, self.test_id, args) + + def test_update_quota_positional(self): + resource = 'quota' + cmd = test_quota.UpdateQuota( + test_cli20.MyApp(sys.stdout), None) + args = [self.test_id, '--network', 'test'] + self.assertRaises( + exceptions.NeutronClientException, self._test_update_resource, + resource, cmd, self.test_id, args=args, + extrafields={'network': 'new'}) From 96eff78ce9785956b95f22cf5c01b30e3de32814 Mon Sep 17 00:00:00 2001 From: Kenji Yasui Date: Tue, 15 Sep 2015 00:13:17 +0000 Subject: [PATCH 322/845] Add help information of 'firewall-rule-create' This patch adds information about ip-version to firewall-rule-create's help output. Change-Id: I799cdb98e12c90fb83965ca32e8fa21fc925856b Closes-Bug: #1495427 --- neutronclient/neutron/v2_0/fw/firewallrule.py | 7 ++- .../tests/unit/fw/test_cli20_firewallrule.py | 47 ++++++++++++++----- 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/neutronclient/neutron/v2_0/fw/firewallrule.py b/neutronclient/neutron/v2_0/fw/firewallrule.py index 1f0536387..97df1df17 100644 --- a/neutronclient/neutron/v2_0/fw/firewallrule.py +++ b/neutronclient/neutron/v2_0/fw/firewallrule.py @@ -81,6 +81,10 @@ def add_known_arguments(self, parser): action='store_true', help=_('Set shared to True (default is False).'), default=argparse.SUPPRESS) + parser.add_argument( + '--ip-version', + type=int, choices=[4, 6], default=4, + help=_('IP version for the firewall rule (default is 4).')) parser.add_argument( '--source-ip-address', help=_('Source IP address or subnet.')) @@ -113,7 +117,8 @@ def args2body(self, parsed_args): ['name', 'description', 'shared', 'protocol', 'source_ip_address', 'destination_ip_address', 'source_port', 'destination_port', - 'action', 'enabled', 'tenant_id']) + 'action', 'enabled', 'tenant_id', + 'ip_version']) protocol = parsed_args.protocol if protocol == 'any': protocol = None diff --git a/neutronclient/tests/unit/fw/test_cli20_firewallrule.py b/neutronclient/tests/unit/fw/test_cli20_firewallrule.py index 10290b631..9c2203501 100644 --- a/neutronclient/tests/unit/fw/test_cli20_firewallrule.py +++ b/neutronclient/tests/unit/fw/test_cli20_firewallrule.py @@ -32,6 +32,7 @@ def _test_create_firewall_rule_with_mandatory_params(self, enabled): my_id = 'myid' protocol = 'tcp' action = 'allow' + ip_version = 4 args = ['--tenant-id', tenant_id, '--admin-state-up', '--protocol', protocol, @@ -42,7 +43,8 @@ def _test_create_firewall_rule_with_mandatory_params(self, enabled): self._test_create_resource(resource, cmd, name, my_id, args, position_names, position_values, protocol=protocol, action=action, - enabled=enabled, tenant_id=tenant_id) + enabled=enabled, tenant_id=tenant_id, + ip_version=ip_version) def test_create_enabled_firewall_rule_with_mandatory_params_lcase(self): self._test_create_firewall_rule_with_mandatory_params(enabled='true') @@ -56,7 +58,8 @@ def test_create_enabled_firewall_rule_with_mandatory_params(self): def test_create_disabled_firewall_rule_with_mandatory_params(self): self._test_create_firewall_rule_with_mandatory_params(enabled='False') - def _setup_create_firewall_rule_with_all_params(self, protocol='tcp'): + def _setup_create_firewall_rule_with_all_params(self, protocol='tcp', + ip_version='4'): # firewall-rule-create with all params set. resource = 'firewall_rule' cmd = firewallrule.CreateFirewallRule(test_cli20.MyApp(sys.stdout), @@ -74,6 +77,7 @@ def _setup_create_firewall_rule_with_all_params(self, protocol='tcp'): args = ['--description', description, '--shared', '--protocol', protocol, + '--ip-version', ip_version, '--source-ip-address', source_ip, '--destination-ip-address', destination_ip, '--source-port', source_port, @@ -86,16 +90,29 @@ def _setup_create_firewall_rule_with_all_params(self, protocol='tcp'): position_values = [] if protocol == 'any': protocol = None - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - description=description, shared=True, - protocol=protocol, - source_ip_address=source_ip, - destination_ip_address=destination_ip, - source_port=source_port, - destination_port=destination_port, - action=action, enabled='True', - tenant_id=tenant_id) + if ip_version == '4' or ip_version == '6': + self._test_create_resource(resource, cmd, name, my_id, args, + position_names, position_values, + description=description, shared=True, + protocol=protocol, + ip_version=int(ip_version), + source_ip_address=source_ip, + destination_ip_address=destination_ip, + source_port=source_port, + destination_port=destination_port, + action=action, enabled='True', + tenant_id=tenant_id) + else: + self.assertRaises(SystemExit, self._test_create_resource, + resource, cmd, name, my_id, args, + position_names, position_values, + ip_version=int(ip_version), + source_ip_address=source_ip, + destination_ip_address=destination_ip, + source_port=source_port, + destination_port=destination_port, + action=action, enabled='True', + tenant_id=tenant_id) def test_create_firewall_rule_with_all_params(self): self._setup_create_firewall_rule_with_all_params() @@ -103,6 +120,12 @@ def test_create_firewall_rule_with_all_params(self): def test_create_firewall_rule_with_proto_any(self): self._setup_create_firewall_rule_with_all_params(protocol='any') + def test_create_firewall_rule_with_IP_version_6(self): + self._setup_create_firewall_rule_with_all_params(ip_version='6') + + def test_create_firewall_rule_with_invalid_IP_version(self): + self._setup_create_firewall_rule_with_all_params(ip_version='5') + def test_list_firewall_rules(self): # firewall-rule-list. resources = "firewall_rules" From 9baa62c3c58f4d6187ab22fc3c04843efd283b55 Mon Sep 17 00:00:00 2001 From: Reedip Banerjee Date: Wed, 11 Nov 2015 12:19:02 +0530 Subject: [PATCH 323/845] Fixed connection_limit and added UT Due to a typo, the REST API attribute of connection_limit was not accessed when creating a new lbaas-V2 listener. This patch fixes the typo, so that the connection_limit can be initialized when listener is created. Also, the previous code did not have UT for connection_limit when the listener was created. The same has been added here. Closes-Bug: #1515112 Change-Id: I71eef7b92de91edc1f3ce48f40c645bca704427d --- neutronclient/neutron/v2_0/lb/v2/listener.py | 5 +++-- neutronclient/tests/unit/lb/v2/test_cli20_listener.py | 10 +++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/neutronclient/neutron/v2_0/lb/v2/listener.py b/neutronclient/neutron/v2_0/lb/v2/listener.py index 39e06e4ec..92750ac04 100644 --- a/neutronclient/neutron/v2_0/lb/v2/listener.py +++ b/neutronclient/neutron/v2_0/lb/v2/listener.py @@ -57,7 +57,8 @@ def add_known_arguments(self, parser): parser.add_argument( '--connection-limit', help=_('The maximum number of connections per second allowed for ' - 'the vip. Positive integer or -1 for unlimited (default).')) + 'the vip. Positive integer or -1 for unlimited (default).'), + type=int) parser.add_argument( '--description', help=_('Description of the listener.')) @@ -102,7 +103,7 @@ def args2body(self, parsed_args): 'admin_state_up': parsed_args.admin_state} neutronV20.update_dict(parsed_args, body, - ['connection-limit', 'description', + ['connection_limit', 'description', 'loadbalancer_id', 'name', 'default_tls_container_ref', 'sni_container_refs', diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_listener.py b/neutronclient/tests/unit/lb/v2/test_cli20_listener.py index dae1f64bb..77d62e0b0 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_listener.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_listener.py @@ -49,17 +49,21 @@ def test_create_listener_with_all_params(self): loadbalancer = 'loadbalancer' protocol = 'TCP' protocol_port = '80' + connection_limit = 10 def_tls_cont_ref = '11111' args = ['--admin-state-down', '--protocol', protocol, '--protocol-port', protocol_port, '--loadbalancer', loadbalancer, '--default-tls-container-ref', def_tls_cont_ref, - '--sni-container-refs', '1111', '2222', '3333'] + '--sni-container-refs', '1111', '2222', '3333', + '--connection-limit', '10'] position_names = ['admin_state_up', 'protocol', 'protocol_port', 'loadbalancer_id', - 'default_tls_container_ref', 'sni_container_refs'] + 'default_tls_container_ref', 'sni_container_refs', + 'connection_limit'] position_values = [False, protocol, protocol_port, loadbalancer, - def_tls_cont_ref, ['1111', '2222', '3333']] + def_tls_cont_ref, ['1111', '2222', '3333'], + connection_limit] self._test_create_resource(resource, cmd, '', my_id, args, position_names, position_values, cmd_resource=cmd_resource) From 8d1990a6947ceb2c8750a4e32fe158aee98e6824 Mon Sep 17 00:00:00 2001 From: shu-mutou Date: Tue, 15 Dec 2015 16:28:20 +0900 Subject: [PATCH 324/845] Drop py33 support "Python 3.3 support is being dropped since OpenStack Liberty." written in following URL. https://wiki.openstack.org/wiki/Python3 And already the infra team and the oslo team are dropping py33 support from their projects. Since we rely on oslo for a lot of our work, and depend on infra for our CI, we should drop py33 support too. Change-Id: Ic3bfd32822a5e55d6d056af163075d9b3bdaabd4 Closes-Bug: #1526170 --- setup.cfg | 1 - tox.ini | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 0962bd079..454945da5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -17,7 +17,6 @@ classifier = Programming Language :: Python :: 2 Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 - Programming Language :: Python :: 3.3 Programming Language :: Python :: 3.4 [files] diff --git a/tox.ini b/tox.ini index b6c0bd8cb..0d075ec87 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] # py3 first to avoid .testrepository incompatibility -envlist = py33,py34,py27,pypy,pep8 +envlist = py34,py27,pypy,pep8 minversion = 1.6 skipsdist = True From b7e3a214d7c34f0ac48f77fbb5f0bbac0e6490b8 Mon Sep 17 00:00:00 2001 From: "vikram.choudhary" Date: Thu, 10 Dec 2015 23:56:37 +0530 Subject: [PATCH 325/845] Reworded nargs='?' explanation for better clarity Change-Id: Iae2c71695ed18504690cc953b4128aeaf3196275 --- doc/source/devref/cli_option_guideline.rst | 38 ++++++++++------------ 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/doc/source/devref/cli_option_guideline.rst b/doc/source/devref/cli_option_guideline.rst index 0d51ef68a..1368a5c67 100644 --- a/doc/source/devref/cli_option_guideline.rst +++ b/doc/source/devref/cli_option_guideline.rst @@ -26,10 +26,10 @@ General conventions #. Do not use ``nargs='?'`` without a special reason. - * The behavior of ``nargs='?'`` of python argparse is a bit tricky - and it sometimes leads to unexpected option parsing which is - different from the help message. The detail is described - in :ref:`the Background section ` below. + * The behavior of ``nargs='?'`` option for python argparse is + bit tricky and may lead to unexpected option parsing different + from the help message. The detail is described in the + :ref:`Background section ` below. #. (option) Avoid using positional options as much as possible. @@ -156,21 +156,18 @@ Options for Boolean value .. _background-nargs: -Avoid using positional options ------------------------------- +Avoid using nargs in positional or optional arguments +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The behavior of ``nargs='?'`` of python argparse is a bit tricky. -When we use ``nargs='?'``, if the order of command-line options is -swapped a bit, a command-line parser fails to parse the options easily. - -There are two examples of such failures. - -Example 1 shows that an actual behavior is different from -a way shown in a help message. The help message at ``[5]`` -shows we can use ``--bb CC``, but as you see at ``[7]`` -the arguent parsing fails. +The behavior of ``nargs='?'`` option for python argparse is bit tricky. +When we use ``nargs='?'`` and if the order of command-line options is +changed then the command-line parser may fail to parse the arguments +correctly. Two examples of such failures are provided below. Example 1: +This example shows how the actual behavior can differ from the provided +help message. In the below block, help message at ``[5]`` says ``--bb CC`` +is a valid format but the argument parsing for the same format fails at ``[7]``. .. code-block:: console @@ -200,12 +197,9 @@ Example 1: SystemExit: 2 -The second example shows how fragile ``nargs='?'`` is when a user -specifies options in a way different from the help message. -Most CLI usesr do not care the the order of command-line options, -so this fragile behavior should be avoided. - Example 2: +This example shows how fragile ``nargs='?'`` can be when user specifies +options in different order from the help message. .. code-block:: console @@ -243,4 +237,6 @@ Example 2: To exit: use 'exit', 'quit', or Ctrl-D. To exit: use 'exit', 'quit', or Ctrl-D. +Note: Most CLI users don't care about the order of the command-line +options. Hence, such fragile behavior should be avoided. From 0ca94a55a93ab7ac9b59b66142ab4ecb347820ff Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Tue, 15 Dec 2015 19:00:03 +0000 Subject: [PATCH 326/845] Updated from global requirements Change-Id: Ib3b1dc1387faaaa0b21233cfd3d02e1852f4fdc8 --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 67d0df441..e46d19284 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,10 +8,10 @@ iso8601>=0.1.9 netaddr!=0.7.16,>=0.7.12 oslo.i18n>=1.5.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 -oslo.utils!=3.1.0,>=2.8.0 # Apache-2.0 +oslo.utils>=3.2.0 # Apache-2.0 os-client-config!=1.6.2,>=1.4.0 keystoneauth1>=2.1.0 -requests>=2.8.1 +requests!=2.9.0,>=2.8.1 simplejson>=2.2.0 six>=1.9.0 Babel>=1.3 From 83d61566cee8b8e786b46103cf3e72fbe8077c1a Mon Sep 17 00:00:00 2001 From: shu-mutou Date: Wed, 2 Dec 2015 12:19:19 +0900 Subject: [PATCH 327/845] Delete python bytecode before every test run Because python creates pyc|pyo files and __pycache__ directories during tox runs, certain changes in the tree, like deletes of files, or switching branches, can create spurious errors. The target bytecodes for deletion are in normal directories, but not in dot started directory. Change-Id: Iee36c67d2e5e4d9e78b1b1b493030761e0f46e3c Closes-Bug: #1368661 --- tox.ini | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index b6c0bd8cb..ded914cc6 100644 --- a/tox.ini +++ b/tox.ini @@ -13,7 +13,14 @@ usedevelop = True install_command = pip install -U {opts} {packages} deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt -commands = python setup.py testr --testr-args='{posargs}' +# Delete bytecodes from normal directories before running tests. +# Note that bytecodes in dot directories will not be deleted +# to keep bytecodes of python modules installed into virtualenvs. +commands = sh -c "find . -type d -name '.?*' -prune -o \ + \( -type d -name '__pycache__' -o -type f -name '*.py[co]' \) \ + -print0 | xargs -0 rm -rf" + python setup.py testr --testr-args='{posargs}' +whitelist_externals = sh [testenv:pep8] commands = flake8 From 7d838f19330c20a12605f63e8f32735793308622 Mon Sep 17 00:00:00 2001 From: yuyangbj Date: Thu, 17 Dec 2015 11:46:28 +0800 Subject: [PATCH 328/845] Show availability_zone info in CLI neutron agent-list Like nova service-list, we should show availability zone information in agent-list result. It will leave empty if the agent is not belong to any zone. Change-Id: I210b3668c247f06b0c14debe22409ce99836e024 Partially-implements: blueprint add-availability-zone --- neutronclient/neutron/v2_0/agent.py | 4 ++-- neutronclient/tests/unit/test_cli20_agents.py | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/neutronclient/neutron/v2_0/agent.py b/neutronclient/neutron/v2_0/agent.py index 123924c4a..110afc61c 100644 --- a/neutronclient/neutron/v2_0/agent.py +++ b/neutronclient/neutron/v2_0/agent.py @@ -29,8 +29,8 @@ class ListAgent(neutronV20.ListCommand): """List agents.""" resource = 'agent' - list_columns = ['id', 'agent_type', 'host', 'alive', 'admin_state_up', - 'binary'] + list_columns = ['id', 'agent_type', 'host', 'availability_zone', 'alive', + 'admin_state_up', 'binary'] _formatters = {'heartbeat_timestamp': _format_timestamp} sorting_support = True diff --git a/neutronclient/tests/unit/test_cli20_agents.py b/neutronclient/tests/unit/test_cli20_agents.py index d28e869ff..7641d2741 100644 --- a/neutronclient/tests/unit/test_cli20_agents.py +++ b/neutronclient/tests/unit/test_cli20_agents.py @@ -54,6 +54,22 @@ def test_list_agents_field(self): self.assertIn("alive", ag.keys()) self.assertIn(smile, ag.values()) + def test_list_agents_zone_field(self): + contents = {'agents': [{'availability_zone': 'myzone'}]} + args = ['-f', 'json'] + resources = "agents" + + cmd = agent.ListAgent(test_cli20.MyApp(sys.stdout), None) + self._test_list_columns(cmd, resources, contents, args) + _str = self.fake_stdout.make_string() + + returned_agents = jsonutils.loads(_str) + self.assertEqual(1, len(returned_agents)) + ag = returned_agents[0] + self.assertEqual(1, len(ag)) + self.assertIn("availability_zone", ag.keys()) + self.assertIn('myzone', ag.values()) + def test_update_agent(self): # agent-update myid --admin-state-down --description mydescr. resource = 'agent' From b77985562983e41a2260f21169d8a6275a8c7dc5 Mon Sep 17 00:00:00 2001 From: Reedip Banerjee Date: Tue, 17 Nov 2015 07:11:22 +0530 Subject: [PATCH 329/845] Support for Name field in Members and HMs This patch adds support to enable naming LBaasV2 Members and Health Monitors(HMs) in python-neutronclient. Closes-Bug: #1515506 Change-Id: I27ac48953bb09841234fce6d852f062e2030284e --- .../neutron/v2_0/lb/v2/healthmonitor.py | 18 +++++++++++++++--- neutronclient/neutron/v2_0/lb/v2/member.py | 12 +++++++++--- .../unit/lb/v2/test_cli20_healthmonitor.py | 8 +++++--- .../tests/unit/lb/v2/test_cli20_member.py | 8 +++++--- 4 files changed, 34 insertions(+), 12 deletions(-) diff --git a/neutronclient/neutron/v2_0/lb/v2/healthmonitor.py b/neutronclient/neutron/v2_0/lb/v2/healthmonitor.py index 4b621cdff..3bf5d7693 100644 --- a/neutronclient/neutron/v2_0/lb/v2/healthmonitor.py +++ b/neutronclient/neutron/v2_0/lb/v2/healthmonitor.py @@ -24,7 +24,7 @@ class ListHealthMonitor(neutronV20.ListCommand): resource = 'healthmonitor' shadow_resource = 'lbaas_healthmonitor' - list_columns = ['id', 'type', 'admin_state_up'] + list_columns = ['id', 'name', 'type', 'admin_state_up'] pagination_support = True sorting_support = True @@ -43,6 +43,9 @@ class CreateHealthMonitor(neutronV20.CreateCommand): shadow_resource = 'lbaas_healthmonitor' def add_known_arguments(self, parser): + parser.add_argument( + '--name', + help=_('Name of the health monitor to be created.')) parser.add_argument( '--admin-state-down', dest='admin_state', action='store_false', @@ -100,7 +103,7 @@ def args2body(self, parsed_args): 'pool_id': pool_id} neutronV20.update_dict(parsed_args, body, ['expected_codes', 'http_method', 'url_path', - 'tenant_id']) + 'tenant_id', 'name']) return {self.resource: body} @@ -109,7 +112,16 @@ class UpdateHealthMonitor(neutronV20.UpdateCommand): resource = 'healthmonitor' shadow_resource = 'lbaas_healthmonitor' - allow_names = False + + def add_known_arguments(self, parser): + parser.add_argument( + '--name', + help=_('Updated name of the health monitor.')) + + def args2body(self, parsed_args): + body = {} + neutronV20.update_dict(parsed_args, body, ['name']) + return {self.resource: body} class DeleteHealthMonitor(neutronV20.DeleteCommand): diff --git a/neutronclient/neutron/v2_0/lb/v2/member.py b/neutronclient/neutron/v2_0/lb/v2/member.py index 24fd27122..2e4f09aca 100644 --- a/neutronclient/neutron/v2_0/lb/v2/member.py +++ b/neutronclient/neutron/v2_0/lb/v2/member.py @@ -43,7 +43,7 @@ class ListMember(LbaasMemberMixin, neutronV20.ListCommand): resource = 'member' shadow_resource = 'lbaas_member' list_columns = [ - 'id', 'address', 'protocol_port', 'weight', + 'id', 'name', 'address', 'protocol_port', 'weight', 'subnet_id', 'admin_state_up', 'status' ] pagination_support = True @@ -76,6 +76,9 @@ def add_known_arguments(self, parser): parser.add_argument( '--weight', help=_('Weight of member in the pool (default:1, [0..256]).')) + parser.add_argument( + '--name', + help=_('Name of the member to be created.')) parser.add_argument( '--subnet', required=True, @@ -102,7 +105,7 @@ def args2body(self, parsed_args): 'protocol_port': parsed_args.protocol_port, 'address': parsed_args.address} neutronV20.update_dict(parsed_args, body, - ['weight', 'subnet_id', 'tenant_id']) + ['weight', 'subnet_id', 'tenant_id', 'name']) return {self.resource: body} @@ -123,12 +126,15 @@ def add_known_arguments(self, parser): parser.add_argument( 'pool', metavar='POOL', help=_('ID or name of the pool that this member belongs to')) + parser.add_argument( + '--name', + help=_('Updated name of the member.')) def args2body(self, parsed_args): self.parent_id = _get_pool_id(self.get_client(), parsed_args.pool) body = {} neutronV20.update_dict(parsed_args, body, - ['admin_state_up', 'weight']) + ['admin_state_up', 'weight', 'name']) return {self.resource: body} diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py b/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py index ec07088a0..b585ec3a2 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py @@ -57,15 +57,17 @@ def test_create_healthmonitor_with_all_params(self): expected_codes = '201' url_path = '/somepath' pool = 'pool1' + name = 'healthmonitor1' args = ['--admin-state-down', '--http-method', http_method, '--expected-codes', expected_codes, '--url-path', url_path, '--type', type, '--max-retries', max_retries, - '--delay', delay, '--timeout', timeout, '--pool', pool] + '--delay', delay, '--timeout', timeout, '--pool', pool, + '--name', name] position_names = ['admin_state_up', 'http_method', 'expected_codes', 'url_path', 'type', 'max_retries', 'delay', - 'timeout', 'pool_id'] + 'timeout', 'pool_id', 'name'] position_values = [False, http_method, expected_codes, url_path, - type, max_retries, delay, timeout, pool] + type, max_retries, delay, timeout, pool, name] self._test_create_resource(resource, cmd, '', my_id, args, position_names, position_values, cmd_resource=cmd_resource) diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_member.py b/neutronclient/tests/unit/lb/v2/test_cli20_member.py index 3f9bced20..acc663ac5 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_member.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_member.py @@ -53,12 +53,14 @@ def test_create_member_with_all_params(self): pool_id = 'pool-id' subnet_id = 'subnet-id' weight = '100' + name = 'member1' args = ['--address', address, '--protocol-port', protocol_port, '--subnet', subnet_id, pool_id, '--weight', weight, - '--admin-state-down'] + '--admin-state-down', '--name', name] position_names = ['admin_state_up', 'address', 'protocol_port', - 'subnet_id', 'weight'] - position_values = [False, address, protocol_port, subnet_id, weight] + 'subnet_id', 'weight', 'name'] + position_values = [False, address, protocol_port, + subnet_id, weight, name] self._test_create_resource(resource, cmd, '', my_id, args, position_names, position_values, cmd_resource=cmd_resource, From 022a8959c9c05156b4b09520cf061650e3ecdf21 Mon Sep 17 00:00:00 2001 From: LIU Yulong Date: Thu, 30 Apr 2015 10:22:32 +0800 Subject: [PATCH 330/845] Add method to retrieve loadbalancer stats In order to let user can retrieve some usage information of neutron loadbalancer v2, this patch add lbaas v2 statistics method to python-neutronclient. Command: neutron lbaas-loadbalancer-stats lbaas_id Change-Id: I7ef1e93debae9e3332a20cb60d5995d39e8b4719 Implements: blueprint lbaas-v2-loadbalancer-stats --- .../neutron/v2_0/lb/v2/loadbalancer.py | 35 ++++++++++++++++++ neutronclient/shell.py | 1 + .../unit/lb/v2/test_cli20_loadbalancer.py | 37 +++++++++++++++++++ neutronclient/v2_0/client.py | 7 ++++ 4 files changed, 80 insertions(+) diff --git a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py index dc5be0061..e3e89623f 100644 --- a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py +++ b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py @@ -93,3 +93,38 @@ class DeleteLoadBalancer(neutronV20.DeleteCommand): resource = 'loadbalancer' allow_names = True + + +class RetrieveLoadBalancerStats(neutronV20.ShowCommand): + """Retrieve stats for a given loadbalancer.""" + + resource = 'loadbalancer' + + def get_data(self, parsed_args): + self.log.debug('run(%s)' % parsed_args) + neutron_client = self.get_client() + neutron_client.format = parsed_args.request_format + loadbalancer_id = neutronV20.find_resourceid_by_name_or_id( + self.get_client(), 'loadbalancer', parsed_args.id) + params = {} + if parsed_args.fields: + params = {'fields': parsed_args.fields} + + data = neutron_client.retrieve_loadbalancer_stats(loadbalancer_id, + **params) + self.format_output_data(data) + stats = data['stats'] + if 'stats' in data: + # To render the output table like: + # +--------------------+-------+ + # | Field | Value | + # +--------------------+-------+ + # | field1 | value1| + # | field2 | value2| + # | field3 | value3| + # | ... | ... | + # +--------------------+-------+ + # it has two columns and the Filed column is alphabetical, + # here covert the data dict to the 1-1 vector format below: + # [(field1, field2, field3, ...), (value1, value2, value3, ...)] + return list(zip(*sorted(stats.items()))) diff --git a/neutronclient/shell.py b/neutronclient/shell.py index b4ddfbf7a..9d817b8fd 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -203,6 +203,7 @@ class BashCompletionCommand(openstack_command.OpenStackCommand): 'lbaas-loadbalancer-create': lbaas_loadbalancer.CreateLoadBalancer, 'lbaas-loadbalancer-update': lbaas_loadbalancer.UpdateLoadBalancer, 'lbaas-loadbalancer-delete': lbaas_loadbalancer.DeleteLoadBalancer, + 'lbaas-loadbalancer-stats': lbaas_loadbalancer.RetrieveLoadBalancerStats, 'lbaas-listener-list': lbaas_listener.ListListener, 'lbaas-listener-show': lbaas_listener.ShowListener, 'lbaas-listener-create': lbaas_listener.CreateListener, diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py b/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py index dea5ba25f..cb58c6d39 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py @@ -16,6 +16,8 @@ import sys +from mox3 import mox + from neutronclient.neutron.v2_0.lb.v2 import loadbalancer as lb from neutronclient.tests.unit import test_cli20 @@ -129,3 +131,38 @@ def test_delete_loadbalancer(self): args = [my_id] self._test_delete_resource(resource, cmd, my_id, args, cmd_resource=cmd_resource) + + def test_retrieve_loadbalancer_stats(self): + # lbaas-loadbalancer-stats test_id. + resource = 'loadbalancer' + cmd = lb.RetrieveLoadBalancerStats(test_cli20.MyApp(sys.stdout), None) + my_id = self.test_id + fields = ['bytes_in', 'bytes_out'] + args = ['--fields', 'bytes_in', '--fields', 'bytes_out', my_id] + + self.mox.StubOutWithMock(cmd, "get_client") + self.mox.StubOutWithMock(self.client.httpclient, "request") + cmd.get_client().MultipleTimes().AndReturn(self.client) + query = "&".join(["fields=%s" % field for field in fields]) + expected_res = {'stats': {'bytes_in': '1234', 'bytes_out': '4321'}} + resstr = self.client.serialize(expected_res) + path = getattr(self.client, "lbaas_loadbalancer_path_stats") + return_tup = (test_cli20.MyResp(200), resstr) + self.client.httpclient.request( + test_cli20.end_url(path % my_id, query), 'GET', + body=None, + headers=mox.ContainsKeyValue( + 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(return_tup) + self.mox.ReplayAll() + + cmd_parser = cmd.get_parser("test_" + resource) + parsed_args = cmd_parser.parse_args(args) + cmd.run(parsed_args) + + self.mox.VerifyAll() + self.mox.UnsetStubs() + _str = self.fake_stdout.make_string() + self.assertIn('bytes_in', _str) + self.assertIn('1234', _str) + self.assertIn('bytes_out', _str) + self.assertIn('4321', _str) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 324fd1219..c97efd824 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -341,6 +341,7 @@ class Client(ClientBase): lbaas_loadbalancers_path = "/lbaas/loadbalancers" lbaas_loadbalancer_path = "/lbaas/loadbalancers/%s" + lbaas_loadbalancer_path_stats = "/lbaas/loadbalancers/%s/stats" lbaas_listeners_path = "/lbaas/listeners" lbaas_listener_path = "/lbaas/listeners/%s" lbaas_pools_path = "/lbaas/pools" @@ -937,6 +938,12 @@ def delete_loadbalancer(self, lbaas_loadbalancer): return self.delete(self.lbaas_loadbalancer_path % (lbaas_loadbalancer)) + @APIParamsCall + def retrieve_loadbalancer_stats(self, loadbalancer, **_params): + """Retrieves stats for a certain load balancer.""" + return self.get(self.lbaas_loadbalancer_path_stats % (loadbalancer), + params=_params) + @APIParamsCall def list_listeners(self, retrieve_all=True, **_params): """Fetches a list of all lbaas_listeners for a tenant.""" From 787ba9250b3dbdd9c0c1b8ee2a4211ba124ee479 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Sat, 28 Nov 2015 09:16:42 +0900 Subject: [PATCH 331/845] Use _i18n instead of i18n It is suggested to use _i18n.py per oslo.i18n document. http://docs.openstack.org/developer/oslo.i18n/usage.html neutronclient.i18n is now a wrapper module which emits the derecation warning. It is because might be used in implementation of the client extensions in other repositories. Closes-Bug: #1519493 Change-Id: I44969daeedc9a66dd9ad5bf80617516faf245ecc --- neutronclient/_i18n.py | 41 +++++++++++++++++++ neutronclient/client.py | 2 +- neutronclient/common/exceptions.py | 2 +- neutronclient/common/serializer.py | 2 +- neutronclient/common/utils.py | 2 +- neutronclient/common/validators.py | 2 +- neutronclient/i18n.py | 25 ++++++----- neutronclient/neutron/v2_0/__init__.py | 2 +- neutronclient/neutron/v2_0/address_scope.py | 2 +- neutronclient/neutron/v2_0/agent.py | 2 +- neutronclient/neutron/v2_0/agentscheduler.py | 2 +- .../neutron/v2_0/contrib/_fox_sockets.py | 2 +- neutronclient/neutron/v2_0/flavor/flavor.py | 2 +- .../neutron/v2_0/flavor/flavor_profile.py | 2 +- neutronclient/neutron/v2_0/floatingip.py | 2 +- neutronclient/neutron/v2_0/fw/firewall.py | 2 +- .../neutron/v2_0/fw/firewallpolicy.py | 2 +- neutronclient/neutron/v2_0/fw/firewallrule.py | 2 +- .../neutron/v2_0/lb/healthmonitor.py | 2 +- neutronclient/neutron/v2_0/lb/member.py | 2 +- neutronclient/neutron/v2_0/lb/pool.py | 2 +- .../neutron/v2_0/lb/v2/healthmonitor.py | 2 +- neutronclient/neutron/v2_0/lb/v2/listener.py | 2 +- .../neutron/v2_0/lb/v2/loadbalancer.py | 2 +- neutronclient/neutron/v2_0/lb/v2/member.py | 2 +- neutronclient/neutron/v2_0/lb/v2/pool.py | 2 +- neutronclient/neutron/v2_0/lb/vip.py | 2 +- neutronclient/neutron/v2_0/metering.py | 2 +- neutronclient/neutron/v2_0/network.py | 2 +- .../neutron/v2_0/nsx/networkgateway.py | 2 +- neutronclient/neutron/v2_0/nsx/qos_queue.py | 2 +- neutronclient/neutron/v2_0/port.py | 2 +- .../neutron/v2_0/qos/bandwidth_limit_rule.py | 2 +- neutronclient/neutron/v2_0/qos/policy.py | 2 +- neutronclient/neutron/v2_0/qos/rule.py | 2 +- neutronclient/neutron/v2_0/quota.py | 2 +- neutronclient/neutron/v2_0/rbac.py | 2 +- neutronclient/neutron/v2_0/router.py | 2 +- neutronclient/neutron/v2_0/securitygroup.py | 2 +- neutronclient/neutron/v2_0/subnet.py | 2 +- neutronclient/neutron/v2_0/subnetpool.py | 2 +- .../neutron/v2_0/vpn/endpoint_group.py | 2 +- neutronclient/neutron/v2_0/vpn/ikepolicy.py | 2 +- .../neutron/v2_0/vpn/ipsec_site_connection.py | 2 +- neutronclient/neutron/v2_0/vpn/ipsecpolicy.py | 2 +- neutronclient/neutron/v2_0/vpn/utils.py | 2 +- neutronclient/neutron/v2_0/vpn/vpnservice.py | 2 +- neutronclient/shell.py | 2 +- neutronclient/v2_0/client.py | 2 +- requirements.txt | 1 + 50 files changed, 101 insertions(+), 60 deletions(-) create mode 100644 neutronclient/_i18n.py diff --git a/neutronclient/_i18n.py b/neutronclient/_i18n.py new file mode 100644 index 000000000..f5aa76bcf --- /dev/null +++ b/neutronclient/_i18n.py @@ -0,0 +1,41 @@ +# 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 oslo_i18n + + +DOMAIN = 'neutronclient' + +_translators = oslo_i18n.TranslatorFactory(domain=DOMAIN) + +# The primary translation function using the well-known name "_" +_ = _translators.primary + +# The contextual translation function using the name "_C" +_C = _translators.contextual_form + +# The plural translation function using the name "_P" +_P = _translators.plural_form + +# Translators for log levels. +# +# The abbreviated names are meant to reflect the usual use of a short +# name like '_'. The "L" is for "log" and the other letter comes from +# the level. +_LI = _translators.log_info +_LW = _translators.log_warning +_LE = _translators.log_error +_LC = _translators.log_critical + + +def get_available_languages(): + return oslo_i18n.get_available_languages(DOMAIN) diff --git a/neutronclient/client.py b/neutronclient/client.py index 77076304e..445d3afb0 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -25,9 +25,9 @@ from keystoneauth1 import adapter import requests +from neutronclient._i18n import _ from neutronclient.common import exceptions from neutronclient.common import utils -from neutronclient.i18n import _ _logger = logging.getLogger(__name__) diff --git a/neutronclient/common/exceptions.py b/neutronclient/common/exceptions.py index e051d5fba..95f54f722 100644 --- a/neutronclient/common/exceptions.py +++ b/neutronclient/common/exceptions.py @@ -13,7 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. -from neutronclient.i18n import _ +from neutronclient._i18n import _ """ Neutron base exception handling. diff --git a/neutronclient/common/serializer.py b/neutronclient/common/serializer.py index e094c2687..828e17f3a 100644 --- a/neutronclient/common/serializer.py +++ b/neutronclient/common/serializer.py @@ -18,8 +18,8 @@ from oslo_serialization import jsonutils import six +from neutronclient._i18n import _ from neutronclient.common import exceptions as exception -from neutronclient.i18n import _ LOG = logging.getLogger(__name__) diff --git a/neutronclient/common/utils.py b/neutronclient/common/utils.py index 388d5e674..9c056752b 100644 --- a/neutronclient/common/utils.py +++ b/neutronclient/common/utils.py @@ -26,8 +26,8 @@ from oslo_utils import importutils import six +from neutronclient._i18n import _ from neutronclient.common import exceptions -from neutronclient.i18n import _ def env(*vars, **kwargs): diff --git a/neutronclient/common/validators.py b/neutronclient/common/validators.py index 304ba35c5..831d68e8f 100644 --- a/neutronclient/common/validators.py +++ b/neutronclient/common/validators.py @@ -15,8 +15,8 @@ import netaddr +from neutronclient._i18n import _ from neutronclient.common import exceptions -from neutronclient.i18n import _ def validate_int_range(parsed_args, attr_name, min_value=None, max_value=None): diff --git a/neutronclient/i18n.py b/neutronclient/i18n.py index e13a880aa..776628fc7 100644 --- a/neutronclient/i18n.py +++ b/neutronclient/i18n.py @@ -10,19 +10,18 @@ # License for the specific language governing permissions and limitations # under the License. -import oslo_i18n as i18n +# TODO(amotoki): Remove this file at the beginning of Nxx cycle. -_translators = i18n.TranslatorFactory(domain='neutronclient') +from debtcollector import moves -# The primary translation function using the well-known name "_" -_ = _translators.primary +from neutronclient import _i18n -# Translators for log levels. -# -# The abbreviated names are meant to reflect the usual use of a short -# name like '_'. The "L" is for "log" and the other letter comes from -# the level. -_LI = _translators.log_info -_LW = _translators.log_warning -_LE = _translators.log_error -_LC = _translators.log_critical +message = ("moved to neutronclient._i18n; please migrate to local " + "oslo_i18n usage, as defined at " + "http://docs.openstack.org/developer/oslo.i18n/usage.html") + +_ = moves.moved_function(_i18n._, '_', __name__, message=message) +_LC = moves.moved_function(_i18n._LC, '_LC', __name__, message=message) +_LE = moves.moved_function(_i18n._LE, '_LE', __name__, message=message) +_LW = moves.moved_function(_i18n._LW, '_LW', __name__, message=message) +_LI = moves.moved_function(_i18n._LI, '_LI', __name__, message=message) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index b34be995d..d5be4a107 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -27,10 +27,10 @@ from oslo_serialization import jsonutils import six +from neutronclient._i18n import _ from neutronclient.common import command from neutronclient.common import exceptions from neutronclient.common import utils -from neutronclient.i18n import _ HEX_ELEM = '[0-9A-Fa-f]' UUID_PATTERN = '-'.join([HEX_ELEM + '{8}', HEX_ELEM + '{4}', diff --git a/neutronclient/neutron/v2_0/address_scope.py b/neutronclient/neutron/v2_0/address_scope.py index c8517289b..a79590b1b 100755 --- a/neutronclient/neutron/v2_0/address_scope.py +++ b/neutronclient/neutron/v2_0/address_scope.py @@ -14,7 +14,7 @@ # under the License. # -from neutronclient.i18n import _ +from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronV20 diff --git a/neutronclient/neutron/v2_0/agent.py b/neutronclient/neutron/v2_0/agent.py index 123924c4a..7fcbdb17f 100644 --- a/neutronclient/neutron/v2_0/agent.py +++ b/neutronclient/neutron/v2_0/agent.py @@ -14,7 +14,7 @@ # under the License. # -from neutronclient.i18n import _ +from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronV20 diff --git a/neutronclient/neutron/v2_0/agentscheduler.py b/neutronclient/neutron/v2_0/agentscheduler.py index 59a01fbee..3348e6412 100644 --- a/neutronclient/neutron/v2_0/agentscheduler.py +++ b/neutronclient/neutron/v2_0/agentscheduler.py @@ -16,7 +16,7 @@ from __future__ import print_function -from neutronclient.i18n import _ +from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronV20 from neutronclient.neutron.v2_0 import network from neutronclient.neutron.v2_0 import router diff --git a/neutronclient/neutron/v2_0/contrib/_fox_sockets.py b/neutronclient/neutron/v2_0/contrib/_fox_sockets.py index c2e9b198d..1ff5ffd2b 100644 --- a/neutronclient/neutron/v2_0/contrib/_fox_sockets.py +++ b/neutronclient/neutron/v2_0/contrib/_fox_sockets.py @@ -14,8 +14,8 @@ # under the License. # +from neutronclient._i18n import _ from neutronclient.common import extension -from neutronclient.i18n import _ def _add_updatable_args(parser): diff --git a/neutronclient/neutron/v2_0/flavor/flavor.py b/neutronclient/neutron/v2_0/flavor/flavor.py index c53db3e27..30e3ae414 100644 --- a/neutronclient/neutron/v2_0/flavor/flavor.py +++ b/neutronclient/neutron/v2_0/flavor/flavor.py @@ -17,8 +17,8 @@ import argparse +from neutronclient._i18n import _ from neutronclient.common import utils -from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 diff --git a/neutronclient/neutron/v2_0/flavor/flavor_profile.py b/neutronclient/neutron/v2_0/flavor/flavor_profile.py index 792978782..894a8a67c 100644 --- a/neutronclient/neutron/v2_0/flavor/flavor_profile.py +++ b/neutronclient/neutron/v2_0/flavor/flavor_profile.py @@ -15,8 +15,8 @@ import argparse +from neutronclient._i18n import _ from neutronclient.common import utils -from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 diff --git a/neutronclient/neutron/v2_0/floatingip.py b/neutronclient/neutron/v2_0/floatingip.py index 2d953554b..44b402ad0 100644 --- a/neutronclient/neutron/v2_0/floatingip.py +++ b/neutronclient/neutron/v2_0/floatingip.py @@ -18,7 +18,7 @@ import argparse -from neutronclient.i18n import _ +from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronV20 diff --git a/neutronclient/neutron/v2_0/fw/firewall.py b/neutronclient/neutron/v2_0/fw/firewall.py index a4dd2ee2b..dacca9fb3 100644 --- a/neutronclient/neutron/v2_0/fw/firewall.py +++ b/neutronclient/neutron/v2_0/fw/firewall.py @@ -14,7 +14,7 @@ # under the License. # -from neutronclient.i18n import _ +from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronv20 diff --git a/neutronclient/neutron/v2_0/fw/firewallpolicy.py b/neutronclient/neutron/v2_0/fw/firewallpolicy.py index c0720eab5..07786879a 100644 --- a/neutronclient/neutron/v2_0/fw/firewallpolicy.py +++ b/neutronclient/neutron/v2_0/fw/firewallpolicy.py @@ -18,7 +18,7 @@ import argparse -from neutronclient.i18n import _ +from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronv20 diff --git a/neutronclient/neutron/v2_0/fw/firewallrule.py b/neutronclient/neutron/v2_0/fw/firewallrule.py index 1f0536387..0f02c09f6 100644 --- a/neutronclient/neutron/v2_0/fw/firewallrule.py +++ b/neutronclient/neutron/v2_0/fw/firewallrule.py @@ -16,8 +16,8 @@ import argparse +from neutronclient._i18n import _ from neutronclient.common import utils -from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronv20 diff --git a/neutronclient/neutron/v2_0/lb/healthmonitor.py b/neutronclient/neutron/v2_0/lb/healthmonitor.py index 8b1a1a110..ee5d70bb5 100644 --- a/neutronclient/neutron/v2_0/lb/healthmonitor.py +++ b/neutronclient/neutron/v2_0/lb/healthmonitor.py @@ -16,7 +16,7 @@ from __future__ import print_function -from neutronclient.i18n import _ +from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronV20 diff --git a/neutronclient/neutron/v2_0/lb/member.py b/neutronclient/neutron/v2_0/lb/member.py index 2a5d4b1b6..d81bd69ae 100644 --- a/neutronclient/neutron/v2_0/lb/member.py +++ b/neutronclient/neutron/v2_0/lb/member.py @@ -14,7 +14,7 @@ # under the License. # -from neutronclient.i18n import _ +from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronV20 diff --git a/neutronclient/neutron/v2_0/lb/pool.py b/neutronclient/neutron/v2_0/lb/pool.py index b0674fcb8..aff529e02 100644 --- a/neutronclient/neutron/v2_0/lb/pool.py +++ b/neutronclient/neutron/v2_0/lb/pool.py @@ -17,7 +17,7 @@ import six -from neutronclient.i18n import _ +from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronV20 diff --git a/neutronclient/neutron/v2_0/lb/v2/healthmonitor.py b/neutronclient/neutron/v2_0/lb/v2/healthmonitor.py index 4b621cdff..14f9b9ac9 100644 --- a/neutronclient/neutron/v2_0/lb/v2/healthmonitor.py +++ b/neutronclient/neutron/v2_0/lb/v2/healthmonitor.py @@ -15,7 +15,7 @@ # under the License. # -from neutronclient.i18n import _ +from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronV20 diff --git a/neutronclient/neutron/v2_0/lb/v2/listener.py b/neutronclient/neutron/v2_0/lb/v2/listener.py index 92750ac04..4a4171908 100644 --- a/neutronclient/neutron/v2_0/lb/v2/listener.py +++ b/neutronclient/neutron/v2_0/lb/v2/listener.py @@ -15,8 +15,8 @@ # under the License. # +from neutronclient._i18n import _ from neutronclient.common import utils -from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 diff --git a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py index dc5be0061..15688c21c 100644 --- a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py +++ b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py @@ -15,7 +15,7 @@ # under the License. # -from neutronclient.i18n import _ +from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronV20 diff --git a/neutronclient/neutron/v2_0/lb/v2/member.py b/neutronclient/neutron/v2_0/lb/v2/member.py index 24fd27122..c1fb670f5 100644 --- a/neutronclient/neutron/v2_0/lb/v2/member.py +++ b/neutronclient/neutron/v2_0/lb/v2/member.py @@ -16,7 +16,7 @@ # under the License. # -from neutronclient.i18n import _ +from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronV20 diff --git a/neutronclient/neutron/v2_0/lb/v2/pool.py b/neutronclient/neutron/v2_0/lb/v2/pool.py index ca2b82bc8..3c1ea99b8 100644 --- a/neutronclient/neutron/v2_0/lb/v2/pool.py +++ b/neutronclient/neutron/v2_0/lb/v2/pool.py @@ -16,8 +16,8 @@ # under the License. # +from neutronclient._i18n import _ from neutronclient.common import utils -from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 diff --git a/neutronclient/neutron/v2_0/lb/vip.py b/neutronclient/neutron/v2_0/lb/vip.py index 4e5d0c82b..4f51357cb 100644 --- a/neutronclient/neutron/v2_0/lb/vip.py +++ b/neutronclient/neutron/v2_0/lb/vip.py @@ -14,7 +14,7 @@ # under the License. # -from neutronclient.i18n import _ +from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronV20 diff --git a/neutronclient/neutron/v2_0/metering.py b/neutronclient/neutron/v2_0/metering.py index aa79ce1ef..a6ecef8e0 100644 --- a/neutronclient/neutron/v2_0/metering.py +++ b/neutronclient/neutron/v2_0/metering.py @@ -12,7 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. -from neutronclient.i18n import _ +from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronv20 diff --git a/neutronclient/neutron/v2_0/network.py b/neutronclient/neutron/v2_0/network.py index 04aa9a707..8d342702d 100644 --- a/neutronclient/neutron/v2_0/network.py +++ b/neutronclient/neutron/v2_0/network.py @@ -16,9 +16,9 @@ import argparse +from neutronclient._i18n import _ from neutronclient.common import exceptions from neutronclient.common import utils -from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 from neutronclient.neutron.v2_0 import availability_zone from neutronclient.neutron.v2_0.qos import policy as qos_policy diff --git a/neutronclient/neutron/v2_0/nsx/networkgateway.py b/neutronclient/neutron/v2_0/nsx/networkgateway.py index 515b87d6b..46d83e9a5 100644 --- a/neutronclient/neutron/v2_0/nsx/networkgateway.py +++ b/neutronclient/neutron/v2_0/nsx/networkgateway.py @@ -16,8 +16,8 @@ from __future__ import print_function +from neutronclient._i18n import _ from neutronclient.common import utils -from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 GW_RESOURCE = 'network_gateway' diff --git a/neutronclient/neutron/v2_0/nsx/qos_queue.py b/neutronclient/neutron/v2_0/nsx/qos_queue.py index 7f6cb68b2..8ea5f1303 100644 --- a/neutronclient/neutron/v2_0/nsx/qos_queue.py +++ b/neutronclient/neutron/v2_0/nsx/qos_queue.py @@ -13,7 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. -from neutronclient.i18n import _ +from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronV20 diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index 49a2610c2..c611d26cf 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -18,9 +18,9 @@ from oslo_serialization import jsonutils +from neutronclient._i18n import _ from neutronclient.common import exceptions from neutronclient.common import utils -from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 from neutronclient.neutron.v2_0.qos import policy as qos_policy diff --git a/neutronclient/neutron/v2_0/qos/bandwidth_limit_rule.py b/neutronclient/neutron/v2_0/qos/bandwidth_limit_rule.py index b0e1569ac..9db102f5b 100644 --- a/neutronclient/neutron/v2_0/qos/bandwidth_limit_rule.py +++ b/neutronclient/neutron/v2_0/qos/bandwidth_limit_rule.py @@ -15,8 +15,8 @@ # +from neutronclient._i18n import _ from neutronclient.common import exceptions -from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronv20 from neutronclient.neutron.v2_0.qos import rule as qos_rule diff --git a/neutronclient/neutron/v2_0/qos/policy.py b/neutronclient/neutron/v2_0/qos/policy.py index 8d6035627..047888aa8 100755 --- a/neutronclient/neutron/v2_0/qos/policy.py +++ b/neutronclient/neutron/v2_0/qos/policy.py @@ -16,7 +16,7 @@ import os -from neutronclient.i18n import _ +from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronv20 diff --git a/neutronclient/neutron/v2_0/qos/rule.py b/neutronclient/neutron/v2_0/qos/rule.py index 3b6510d17..e4c8d9260 100644 --- a/neutronclient/neutron/v2_0/qos/rule.py +++ b/neutronclient/neutron/v2_0/qos/rule.py @@ -15,7 +15,7 @@ # -from neutronclient.i18n import _ +from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronv20 from neutronclient.neutron.v2_0.qos import policy as qos_policy diff --git a/neutronclient/neutron/v2_0/quota.py b/neutronclient/neutron/v2_0/quota.py index 7346f54b8..4b6e86e0b 100644 --- a/neutronclient/neutron/v2_0/quota.py +++ b/neutronclient/neutron/v2_0/quota.py @@ -23,9 +23,9 @@ from oslo_serialization import jsonutils import six +from neutronclient._i18n import _ from neutronclient.common import exceptions from neutronclient.common import utils -from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 diff --git a/neutronclient/neutron/v2_0/rbac.py b/neutronclient/neutron/v2_0/rbac.py index 79c991d12..d840af5b1 100644 --- a/neutronclient/neutron/v2_0/rbac.py +++ b/neutronclient/neutron/v2_0/rbac.py @@ -13,7 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. -from neutronclient.i18n import _ +from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronV20 diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index bcb4ae3c7..955c7b802 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -20,9 +20,9 @@ from oslo_serialization import jsonutils +from neutronclient._i18n import _ from neutronclient.common import exceptions from neutronclient.common import utils -from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 from neutronclient.neutron.v2_0 import availability_zone diff --git a/neutronclient/neutron/v2_0/securitygroup.py b/neutronclient/neutron/v2_0/securitygroup.py index 4891a1576..22ff9fc8e 100644 --- a/neutronclient/neutron/v2_0/securitygroup.py +++ b/neutronclient/neutron/v2_0/securitygroup.py @@ -16,8 +16,8 @@ import argparse +from neutronclient._i18n import _ from neutronclient.common import exceptions -from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 diff --git a/neutronclient/neutron/v2_0/subnet.py b/neutronclient/neutron/v2_0/subnet.py index 07da46b9a..d8b594663 100644 --- a/neutronclient/neutron/v2_0/subnet.py +++ b/neutronclient/neutron/v2_0/subnet.py @@ -18,9 +18,9 @@ from oslo_serialization import jsonutils +from neutronclient._i18n import _ from neutronclient.common import exceptions from neutronclient.common import utils -from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 diff --git a/neutronclient/neutron/v2_0/subnetpool.py b/neutronclient/neutron/v2_0/subnetpool.py index 2357750bc..8677cd5c5 100644 --- a/neutronclient/neutron/v2_0/subnetpool.py +++ b/neutronclient/neutron/v2_0/subnetpool.py @@ -14,7 +14,7 @@ # under the License. # -from neutronclient.i18n import _ +from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronV20 diff --git a/neutronclient/neutron/v2_0/vpn/endpoint_group.py b/neutronclient/neutron/v2_0/vpn/endpoint_group.py index e28e167fa..a112b315e 100644 --- a/neutronclient/neutron/v2_0/vpn/endpoint_group.py +++ b/neutronclient/neutron/v2_0/vpn/endpoint_group.py @@ -14,7 +14,7 @@ # under the License. # -from neutronclient.i18n import _ +from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronv20 diff --git a/neutronclient/neutron/v2_0/vpn/ikepolicy.py b/neutronclient/neutron/v2_0/vpn/ikepolicy.py index dcfad483c..e771aadaf 100644 --- a/neutronclient/neutron/v2_0/vpn/ikepolicy.py +++ b/neutronclient/neutron/v2_0/vpn/ikepolicy.py @@ -14,8 +14,8 @@ # under the License. # +from neutronclient._i18n import _ from neutronclient.common import utils -from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronv20 from neutronclient.neutron.v2_0.vpn import utils as vpn_utils diff --git a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py index 8dfa4f38e..8726c28ac 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py +++ b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py @@ -16,9 +16,9 @@ from oslo_serialization import jsonutils +from neutronclient._i18n import _ from neutronclient.common import exceptions from neutronclient.common import utils -from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronv20 from neutronclient.neutron.v2_0.vpn import utils as vpn_utils diff --git a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py index 138c941d3..80a334da6 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py +++ b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py @@ -14,8 +14,8 @@ # under the License. # +from neutronclient._i18n import _ from neutronclient.common import utils -from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronv20 from neutronclient.neutron.v2_0.vpn import utils as vpn_utils diff --git a/neutronclient/neutron/v2_0/vpn/utils.py b/neutronclient/neutron/v2_0/vpn/utils.py index ab6760793..65d9c7c4b 100644 --- a/neutronclient/neutron/v2_0/vpn/utils.py +++ b/neutronclient/neutron/v2_0/vpn/utils.py @@ -18,8 +18,8 @@ """VPN Utilities and helper functions.""" +from neutronclient._i18n import _ from neutronclient.common import exceptions -from neutronclient.i18n import _ dpd_supported_actions = ['hold', 'clear', 'restart', 'restart-by-peer', 'disabled'] diff --git a/neutronclient/neutron/v2_0/vpn/vpnservice.py b/neutronclient/neutron/v2_0/vpn/vpnservice.py index c18f4e7e6..8bd2198c9 100644 --- a/neutronclient/neutron/v2_0/vpn/vpnservice.py +++ b/neutronclient/neutron/v2_0/vpn/vpnservice.py @@ -14,7 +14,7 @@ # under the License. # -from neutronclient.i18n import _ +from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronv20 diff --git a/neutronclient/shell.py b/neutronclient/shell.py index b4ddfbf7a..8c92c4b7f 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -34,12 +34,12 @@ from cliff import app from cliff import commandmanager +from neutronclient._i18n import _ from neutronclient.common import clientmanager from neutronclient.common import command as openstack_command from neutronclient.common import exceptions as exc from neutronclient.common import extension as client_extension from neutronclient.common import utils -from neutronclient.i18n import _ from neutronclient.neutron.v2_0 import address_scope from neutronclient.neutron.v2_0 import agent from neutronclient.neutron.v2_0 import agentscheduler diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 324fd1219..d69bcca32 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -23,12 +23,12 @@ import requests import six.moves.urllib.parse as urlparse +from neutronclient._i18n import _ from neutronclient import client from neutronclient.common import exceptions from neutronclient.common import extension as client_extension from neutronclient.common import serializer from neutronclient.common import utils -from neutronclient.i18n import _ _logger = logging.getLogger(__name__) diff --git a/requirements.txt b/requirements.txt index e46d19284..be8fb03d0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,6 +4,7 @@ pbr>=1.6 argparse cliff>=1.15.0 # Apache-2.0 +debtcollector>=0.3.0 # Apache-2.0 iso8601>=0.1.9 netaddr!=0.7.16,>=0.7.12 oslo.i18n>=1.5.0 # Apache-2.0 From 177d8a581f1ba3bc8440b66a8ecb7ca6daea6311 Mon Sep 17 00:00:00 2001 From: Manjeet Singh Bhatia Date: Fri, 18 Dec 2015 19:25:37 +0000 Subject: [PATCH 332/845] improve tox -e cover add coverage report to see test coverage after running tox -e cover. Change-Id: I9d440f8c2f38893a98a1665009b0bb93ce079edb --- .coveragerc | 4 ++-- tox.ini | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.coveragerc b/.coveragerc index 83f3102c1..94e40afbb 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,7 +1,7 @@ [run] branch = True source = neutronclient -omit = neutronclient/openstack/*,neutronclient/tests/* +omit = neutronclient/tests/* [report] -ignore-errors = True +ignore_errors = True diff --git a/tox.ini b/tox.ini index f9e00d6d8..6f990a9c8 100644 --- a/tox.ini +++ b/tox.ini @@ -33,7 +33,9 @@ setenv = OS_NEUTRONCLIENT_EXEC_DIR = {envdir}/bin [testenv:cover] -commands = python setup.py testr --coverage --testr-args='{posargs}' +commands = + python setup.py test --coverage --coverage-package-name=neutronclient --testr-args='{posargs}' + coverage report [testenv:docs] commands= From 95881a080fc4c6e19344fd0d83cdc76a7acf4353 Mon Sep 17 00:00:00 2001 From: Manjeet Singh Bhatia Date: Fri, 11 Dec 2015 22:41:44 +0000 Subject: [PATCH 333/845] Add protocol value options to sg-rule-create This patch will show choices of allowed protocols for security group rule create and also indicate type and code in help when protocol is ICMP. DocImpact '[0-255]' values are allowed for protocol but not listed anywhere in description for creating security group rule. Change-Id: Ib79be78c253332e27991795e8add370ae351e9a6 Closes-Bug: #1523063 --- neutronclient/neutron/v2_0/securitygroup.py | 8 +++--- .../tests/unit/test_cli20_securitygroup.py | 27 +++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/neutronclient/neutron/v2_0/securitygroup.py b/neutronclient/neutron/v2_0/securitygroup.py index 4891a1576..c4ed5434d 100644 --- a/neutronclient/neutron/v2_0/securitygroup.py +++ b/neutronclient/neutron/v2_0/securitygroup.py @@ -317,16 +317,18 @@ def add_known_arguments(self, parser): help=_('IPv4/IPv6')) parser.add_argument( '--protocol', - help=_('Protocol of packet.')) + help=_('Protocol of packet. Allowed values are ' + '[icmp, icmpv6, tcp, udp] and ' + 'integer representations [0-255]')) parser.add_argument( '--port-range-min', - help=_('Starting port range.')) + help=_('Starting port range. For ICMP it is type.')) parser.add_argument( '--port_range_min', help=argparse.SUPPRESS) parser.add_argument( '--port-range-max', - help=_('Ending port range.')) + help=_('Ending port range. For ICMP it is code.')) parser.add_argument( '--port_range_max', help=argparse.SUPPRESS) diff --git a/neutronclient/tests/unit/test_cli20_securitygroup.py b/neutronclient/tests/unit/test_cli20_securitygroup.py index 19e3c688e..4229a244a 100644 --- a/neutronclient/tests/unit/test_cli20_securitygroup.py +++ b/neutronclient/tests/unit/test_cli20_securitygroup.py @@ -174,6 +174,33 @@ def test_create_security_group_rule_full(self): self._test_create_resource(resource, cmd, None, myid, args, position_names, position_values) + def test_create_security_group_rule_with_integer_protocol_value(self): + resource = 'security_group_rule' + cmd = securitygroup.CreateSecurityGroupRule( + test_cli20.MyApp(sys.stdout), None) + myid = 'myid' + direction = 'ingress' + ethertype = 'IPv4' + protocol = '2' + port_range_min = '22' + port_range_max = '22' + remote_ip_prefix = '10.0.0.0/24' + security_group_id = '1' + remote_group_id = '1' + args = ['--remote_ip_prefix', remote_ip_prefix, '--direction', + direction, '--ethertype', ethertype, '--protocol', protocol, + '--port_range_min', port_range_min, '--port_range_max', + port_range_max, '--remote_group_id', remote_group_id, + security_group_id] + position_names = ['remote_ip_prefix', 'direction', 'ethertype', + 'protocol', 'port_range_min', 'port_range_max', + 'remote_group_id', 'security_group_id'] + position_values = [remote_ip_prefix, direction, ethertype, protocol, + port_range_min, port_range_max, remote_group_id, + security_group_id] + self._test_create_resource(resource, cmd, None, myid, args, + position_names, position_values) + def test_delete_security_group_rule(self): """Delete security group rule: myid.""" resource = 'security_group_rule' From 597800f5033d8e78c5bec102e1585339348f9ae4 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Thu, 31 Dec 2015 06:57:50 +0900 Subject: [PATCH 334/845] Support pagination listing in client extension Change-Id: Ibe4b803f3bcac2d13a9fe5992b2f7b665801ef3f Closes-Bug: #1530211 --- neutronclient/tests/unit/test_client_extension.py | 5 +++++ neutronclient/v2_0/client.py | 14 ++++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/neutronclient/tests/unit/test_client_extension.py b/neutronclient/tests/unit/test_client_extension.py index d0609b3e8..0fccd5e84 100644 --- a/neutronclient/tests/unit/test_client_extension.py +++ b/neutronclient/tests/unit/test_client_extension.py @@ -104,6 +104,11 @@ def test_list_fox_sockets(self): cmd = fox_sockets.FoxInSocketsList(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, True) + def test_list_fox_pagination(self): + resources = 'fox_sockets' + cmd = fox_sockets.FoxInSocketsList(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources_with_pagination(resources, cmd) + def test_show_fox_socket(self): # Show fox_socket: --fields id --fields name myid. resource = 'fox_socket' diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 324fd1219..10ff18686 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -447,9 +447,9 @@ class Client(ClientBase): } @APIParamsCall - def list_ext(self, path, **_params): + def list_ext(self, collection, path, retrieve_all, **_params): """Client extension hook for lists.""" - return self.get(path, params=_params) + return self.list(collection, path, retrieve_all, **_params) @APIParamsCall def show_ext(self, path, id, **_params): @@ -1694,11 +1694,13 @@ def _parent_fx(obj, parent_id, **_params): setattr(self, "show_%s" % resource_singular, fn) def extend_list(self, resource_plural, path, parent_resource): - def _fx(**_params): - return self.list_ext(path, **_params) + def _fx(retrieve_all=True, **_params): + return self.list_ext(resource_plural, path, + retrieve_all, **_params) - def _parent_fx(parent_id, **_params): - return self.list_ext(path % parent_id, **_params) + def _parent_fx(parent_id, retrieve_all=True, **_params): + return self.list_ext(resource_plural, path % parent_id, + retrieve_all, **_params) fn = _fx if not parent_resource else _parent_fx setattr(self, "list_%s" % resource_plural, fn) From c7586cd59dc0c6d0b1bde06f9cc19725408b9d77 Mon Sep 17 00:00:00 2001 From: Julien Danjou Date: Fri, 1 Jan 2016 14:09:54 +0100 Subject: [PATCH 335/845] test: fix option in port test This fixes a test that uses the option --no-security-group, which actually does not exist. `argparse` fuzzy-matching system makes the test work by guessing that it's an abbrev for --no-security-groups, but that's just luck at this point. Change-Id: I903ce9564a83d3ee69f4efeb726d3c2d3ff69bbb --- neutronclient/tests/unit/test_cli20_port.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/tests/unit/test_cli20_port.py b/neutronclient/tests/unit/test_cli20_port.py index 0fe898532..de2197620 100644 --- a/neutronclient/tests/unit/test_cli20_port.py +++ b/neutronclient/tests/unit/test_cli20_port.py @@ -284,7 +284,7 @@ def test_create_port_secgroup_off(self): name = 'myname' myid = 'myid' netid = 'netid' - args = ['--no-security-group', netid] + args = ['--no-security-groups', netid] position_names = ['network_id', 'security_groups'] position_values = [netid, []] self._test_create_resource(resource, cmd, name, myid, args, From 9371c45864a248ff0e38e3d9abba7037bc758da6 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Fri, 1 Jan 2016 20:32:55 +0000 Subject: [PATCH 336/845] Updated from global requirements Change-Id: I4b93f1a0d0572a29f45999a20b2ffeed87c4fb89 --- test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index ab0266d8a..1ef23babf 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -16,4 +16,4 @@ requests-mock>=0.7.0 # Apache-2.0 sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 testrepository>=0.0.18 testtools>=1.4.0 -tempest-lib>=0.11.0 +tempest-lib>=0.12.0 From 918a21a0e086d7bbdd29b1b88763e029a4f3626d Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Sat, 2 Jan 2016 22:17:59 +0900 Subject: [PATCH 337/845] Drop unused TableFormater code TableFormater was introduced as a workaround and it was commented out in a later fix. It is no longer needed now. TrivialFix Change-Id: Ia960ee1abc5febcc95536f63d2d1dada357a73c6 --- neutronclient/neutron/v2_0/__init__.py | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index b34be995d..1835d8dd7 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -21,7 +21,6 @@ import logging import re -from cliff.formatters import table from cliff import lister from cliff import show from oslo_serialization import jsonutils @@ -370,19 +369,6 @@ def update_dict(obj, dict, attributes): dict[attribute] = getattr(obj, attribute) -class TableFormater(table.TableFormatter): - """This class is used to keep consistency with prettytable 0.6. - - https://bugs.launchpad.net/python-neutronclient/+bug/1165962 - """ - def emit_list(self, column_names, data, stdout, parsed_args): - if column_names: - super(TableFormater, self).emit_list(column_names, data, stdout, - parsed_args) - else: - stdout.write('\n') - - # command.OpenStackCommand is abstract class so that metaclass of # subclass must be subclass of metaclass of all its base. # otherwise metaclass conflict exception is raised. @@ -405,14 +391,6 @@ class NeutronCommand(command.OpenStackCommand): shadow_resource = None parent_id = None - def __init__(self, app, app_args): - super(NeutronCommand, self).__init__(app, app_args) - # NOTE(markmcclain): This is no longer supported in cliff version 1.5.2 - # see https://bugs.launchpad.net/python-neutronclient/+bug/1265926 - - # if hasattr(self, 'formatters'): - # self.formatters['table'] = TableFormater() - @property def cmd_resource(self): if self.shadow_resource: From 199cb1e4f857d7ed65142af05eb0de2876eaaea4 Mon Sep 17 00:00:00 2001 From: Nikola Dipanov Date: Wed, 30 Dec 2015 10:15:46 +0000 Subject: [PATCH 338/845] port: Add 'direct-physical' as a valid vnic-type When creating a port, it is now valid to pass direct-physical as a vnic-type, as it's been implemented in the following change: If1ab969c2002c649a3d51635ca2765c262e2d37f This patch enables this in the neutron CLI. DocImpact Related-bug: 1500993 Change-Id: If3426a6a5b07f6159fd636ae65d0ad3c296db990 --- neutronclient/neutron/v2_0/port.py | 10 +++++++--- neutronclient/tests/unit/test_cli20_port.py | 20 +++++++++++++++++++ ...cal-vnic-port-create-736d8b2600faf22b.yaml | 6 ++++++ 3 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 releasenotes/notes/direct-physical-vnic-port-create-736d8b2600faf22b.yaml diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index 49a2610c2..f4a3e2229 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -238,12 +238,16 @@ def add_known_arguments(self, parser): '--mac_address', help=argparse.SUPPRESS) parser.add_argument( - '--vnic-type', metavar='', - choices=['direct', 'macvtap', 'normal', 'baremetal'], + '--vnic-type', + metavar='', + choices=['direct', 'direct-physical', 'macvtap', + 'normal', 'baremetal'], help=_('VNIC type for this port.')) parser.add_argument( '--vnic_type', - choices=['direct', 'macvtap', 'normal', 'baremetal'], + choices=['direct', 'direct-physical', 'macvtap', + 'normal', 'baremetal'], help=argparse.SUPPRESS) parser.add_argument( '--binding-profile', diff --git a/neutronclient/tests/unit/test_cli20_port.py b/neutronclient/tests/unit/test_cli20_port.py index 0fe898532..3c2e4c585 100644 --- a/neutronclient/tests/unit/test_cli20_port.py +++ b/neutronclient/tests/unit/test_cli20_port.py @@ -153,6 +153,26 @@ def test_create_port_vnic_type_direct(self): self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) + def test_create_port_vnic_type_direct_physical(self): + # Create port: --vnic_type direct-physical netid. + resource = 'port' + cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + netid = 'netid' + args = ['--vnic_type', 'direct-physical', netid] + position_names = ['binding:vnic_type', 'network_id'] + position_values = ['direct-physical', netid] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + # Test dashed options + args = ['--vnic-type', 'direct-physical', netid] + position_names = ['binding:vnic_type', 'network_id'] + position_values = ['direct-physical', netid] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + def test_create_port_vnic_type_macvtap(self): # Create port: --vnic_type macvtap netid. resource = 'port' diff --git a/releasenotes/notes/direct-physical-vnic-port-create-736d8b2600faf22b.yaml b/releasenotes/notes/direct-physical-vnic-port-create-736d8b2600faf22b.yaml new file mode 100644 index 000000000..911377496 --- /dev/null +++ b/releasenotes/notes/direct-physical-vnic-port-create-736d8b2600faf22b.yaml @@ -0,0 +1,6 @@ +--- +features: + - Added new 'direct-physical' vnic-type option for port-create CLI command. + Passing this particular value allows for a port to be create with the + vnic-type used for assigning SR-IOV physical functions to + instances. From d0e50b8b90fa29db04e0c19ea699f6bc7ae16652 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Wed, 9 Dec 2015 06:07:50 +0900 Subject: [PATCH 339/845] Use six.python_2_unicode_compatible for NeutronException.__str__ The definition of __str__ is different between py2 and py3. __str__ should return a byte stream to make print(exc) work in python2, but __str__ should return string type in python3. six.python_2_unicode_compatible sets up __str__ and __unicode__ approriately. Change-Id: I6c85ca8c4e6f86450ee390db81e3aa101293b846 Related-Bug: #1235228 --- neutronclient/common/exceptions.py | 3 ++ neutronclient/tests/unit/test_exceptions.py | 36 +++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 neutronclient/tests/unit/test_exceptions.py diff --git a/neutronclient/common/exceptions.py b/neutronclient/common/exceptions.py index 95f54f722..400289b32 100644 --- a/neutronclient/common/exceptions.py +++ b/neutronclient/common/exceptions.py @@ -13,6 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. +import six + from neutronclient._i18n import _ """ @@ -29,6 +31,7 @@ """ +@six.python_2_unicode_compatible class NeutronException(Exception): """Base Neutron Exception. diff --git a/neutronclient/tests/unit/test_exceptions.py b/neutronclient/tests/unit/test_exceptions.py new file mode 100644 index 000000000..b87be9527 --- /dev/null +++ b/neutronclient/tests/unit/test_exceptions.py @@ -0,0 +1,36 @@ +# 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 fixtures +import testtools + +from neutronclient.common import exceptions +from neutronclient.i18n import _ + + +class TestExceptions(testtools.TestCase): + + def test_exception_print_with_unicode(self): + class TestException(exceptions.NeutronException): + message = _('Exception with %(reason)s') + + multibyte_unicode_string = u'\uff21\uff22\uff23' + e = TestException(reason=multibyte_unicode_string) + + fixture = fixtures.StringStream('stdout') + self.useFixture(fixture) + with fixtures.MonkeyPatch('sys.stdout', fixture.stream): + print(e) + self.assertEqual('Exception with %s' % multibyte_unicode_string, + fixture.getDetails().get('stdout').as_text()) From be30c221c16aa84f0b2bfd49a476cdaf7373c14a Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Tue, 5 Jan 2016 02:45:04 +0000 Subject: [PATCH 340/845] Updated from global requirements Change-Id: Ia383e3cabe1b996ae0dd919bab1458ab49719411 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index be8fb03d0..033545cd4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ pbr>=1.6 argparse cliff>=1.15.0 # Apache-2.0 -debtcollector>=0.3.0 # Apache-2.0 +debtcollector>=0.3.0 # Apache-2.0 iso8601>=0.1.9 netaddr!=0.7.16,>=0.7.12 oslo.i18n>=1.5.0 # Apache-2.0 From 722ce10d3cdb209b92fcb9832fede545c4b4c104 Mon Sep 17 00:00:00 2001 From: Richard Theis Date: Tue, 8 Dec 2015 16:45:50 -0600 Subject: [PATCH 341/845] Devref: Transition to OpenStack Client Add developer reference for the transitition of the neutron CLI support to the openstack CLI. Change-Id: I5f9885eab7b6d1b6f4ee0d9cf84a07a0cb9d8c9a Related-Bug: #1521291 --- doc/source/devref/transition_to_osc.rst | 182 ++++++++++++++++++++++++ doc/source/index.rst | 4 +- 2 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 doc/source/devref/transition_to_osc.rst diff --git a/doc/source/devref/transition_to_osc.rst b/doc/source/devref/transition_to_osc.rst new file mode 100644 index 000000000..9d4c87a2a --- /dev/null +++ b/doc/source/devref/transition_to_osc.rst @@ -0,0 +1,182 @@ +.. + 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. + + + Convention for heading levels in Neutron devref: + ======= Heading 0 (reserved for the title in a document) + ------- Heading 1 + ~~~~~~~ Heading 2 + +++++++ Heading 3 + ''''''' Heading 4 + (Avoid deeper levels because they do not render well.) + +Transition to OpenStack Client +============================== + +This document details the transition roadmap for moving the neutron client's +OpenStack Networking API support, both the Python library and the ``neutron`` +command-line interface (CLI), to the +`OpenStack client (OSC) `_ +and the `OpenStack Python SDK `_. +This transition is being guided by the +`Deprecate individual CLIs in favour of OSC `_ +OpenStack spec. See the `Neutron RFE `_ and +`OSC neutron-client blueprint `_ +for the overall progress of this transition. + +Overview +-------- + +This transition will result in the neutron client's ``neutron`` CLI being +deprecated and then eventually removed. The ``neutron`` CLI will be replaced +by OSC's networking support available via the ``openstack`` CLI. This is +similar to the deprecation and removal process for the +`keystone client's `_ +``keystone`` CLI. + +The neutron client's Python library won't be deprecated. It will be available +along side the networking support provided by the OpenStack Python SDK. However, +the OpenStack Python SDK will be used to implement OSC's networking support. + +Users of the neutron client's command extensions will need to transition to the +`OSC plugin system `_ +before the ``neutron`` CLI is removed. Such users will maintain their OSC plugin +within their own project and will be responsible for deprecating and removing +their ``neutron`` CLI extension. + +Transition Steps +---------------- + +1. **Done:** OSC adds OpenStack Python SDK as a dependency. See the following + patch set: https://review.openstack.org/#/c/138745/ + +2. **Done:** OSC switches its networking support for the + `network `_ + command object to use the OpenStack Python SDK instead of the neutron + client's Python library. See the following patch set: + https://review.openstack.org/#/c/253348/ + +3. **Done:** OSC removes its python-neutronclient dependency. + See the following patch set: https://review.openstack.org/#/c/255545/ + +4. **In Progress:** OpenStack Python SDK releases version 1.0 to guarantee + backwards compatibility of its networking support and OSC updates + its dependencies to include OpenStack Python SDK version 1.0 or later. + +5. **In Progress:** OSC switches its networking support for the + `ip floating `_, + `ip floating pool `_, + `ip fixed `_, + `security group `_, and + `security group rule `_ + command objects to use the OpenStack Python SDK instead of the nova + client's Python library when neutron is enabled. When nova network + is enabled, then the nova client's Python library will continue to + be used. See the following OSC bugs: + + * `Floating IP CRUD `_ + + * `Port CRUD `_ + + * `Security Group CRUD `_ + + * `Security Group Rule CRUD `_ + +6. **In Progress:** OSC enhances its networking support under the + `neutron-client `_ + OSC spec. At this point and when applicable, enhancements to the ``neutron`` + CLI must also be made to the ``openstack`` CLI and the OpenStack Python SDK. + Enhancements to the networking support in the OpenStack Python SDK will be + handled via bugs. Neutron stadium users of the neutron client's command + extensions should start their transition to the OSC plugin system. + See the developer guide section below for more information on this step. + +7. **Not Started:** Deprecate the ``neutron`` CLI once the criteria below have + been meet. Running the CLI after it has been deprecated will issue a warning + messages such as the following: + ``DeprecationWarning: The neutron CLI is deprecated in favor of python-openstackclient.`` + In addition, only security fixes will be made to the CLI after it has been + deprecated. + + * The networking support provide by the ``openstack`` CLI is functionally + equivalent to the ``neutron`` CLI and it contains sufficient functional + and unit test coverage. + + * Neutron core and advanced services projects, Neutron documentation and + `DevStack `_ use ``openstack`` + CLI instead of ``neutron`` CLI. + + * Most neutron stadium users of the neutron client's command extensions have + transitioned to the OSC plugin system and use the ``openstack`` CLI instead + of the ``neutron`` CLI. + +8. **Not Started:** Remove the ``neutron`` CLI after two deprecation cycles. + +Developer Guide +--------------- +The ``neutron`` CLI version 3.1.1, without extensions, supports over 200 +commands while the ``openstack`` CLI version 2.0.1 supports about 20 +networking commands. Of the 20 commands, most do not have all of the options +or arguments of their ``neutron`` CLI equivalent. With this large functional +gap, one critical question for developers during this transition is "Which +CLI do I change?" The answer depends on the state of a command and the +state of the overall transition. Details are outlined in the table +below. Early stages of the transition will require dual maintenance. +Eventually, dual maintenance will be reduced to critical bug fixes only +with feature requests only being made to the ``openstack`` CLI. + ++----------------------+------------------------+----------------------------------------------+ +| neutron Command | openstack Command | CLI to Change | ++======================+========================+==============================================+ +| Exists | Doesn't Exist | neutron | ++----------------------+------------------------+----------------------------------------------+ +| Exists | In Progress | neutron and update related OSC bug | ++----------------------+------------------------+----------------------------------------------+ +| Exists | Exists | neutron and openstack | ++----------------------+------------------------+----------------------------------------------+ +| Doesn't Exist | Doesn't Exist | neutron and openstack | ++----------------------+------------------------+----------------------------------------------+ +| Doesn't Exist | Exists | openstack | ++----------------------+------------------------+----------------------------------------------+ + +When adding or updating an ``openstack`` networking command, changes may +first be required to the OpenStack Python SDK to support the underlying +networking resource object, properties and/or actions. Once the OpenStack +Python SDK changes are merged, the related OSC changes can be merged. +The OSC changes may require an update to the OSC openstacksdk version in the +`requirements.txt `_ +file. + +Neutron stadium users of the neutron client's command extensions must adopt the +`OSC plugin system `_ +for this transition. Such users will maintain their OSC plugin within their +own project and should follow the guidance in the table above to determine +which CLI to change. + +Developer References +-------------------- + +* See `OSC neutron-client blueprint `_ + to determine if an ``openstack`` command is in progress. See the ``Related bugs`` list. +* See `OSC command list `_ + to determine if an ``openstack`` command exists. +* See `OSC plugin command list `_ + to determine if an ``openstack`` plugin command exists. +* See `OSC command structure `_ + to determine the current ``openstack`` command objects, plugin objects and actions. +* See `OSC human interface guide `_ + for guidance on creating new OSC command interfaces. +* See `OSC plugin `_ + for information on the OSC plugin system to be used for ``neutron`` CLI extensions. +* Report an OSC bug: https://bugs.launchpad.net/python-openstackclient/+filebug +* Report an OpenStack Python SDK bug: https://bugs.launchpad.net/python-openstacksdk/+filebug diff --git a/doc/source/index.rst b/doc/source/index.rst index 4ed204949..02a61a108 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -20,13 +20,15 @@ Developer Guide --------------- In the Developer Guide, you will find information on Neutron’s client -lower level programming details or APIs. +lower level programming details or APIs as well as the transition to +OpenStack client. .. toctree:: :maxdepth: 2 devref/client_command_extensions devref/cli_option_guideline + devref/transition_to_osc History ------- From 6f0307b90ca6f71d5f40df5e68005d2c4b6085f9 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Tue, 5 Jan 2016 06:35:34 +0900 Subject: [PATCH 342/845] Add some items to the release notes Change-Id: I75d56e5d987c0564e75f9721ee4aae69069ed330 --- .../availability-zone-support-8e66f55e46b7ef9a.yaml | 10 ++++++++++ .../notes/docs-improvements-17e31babe38e2962.yaml | 5 +++++ .../notes/drop-nuage-commands-df10aab6ccd77ed2.yaml | 3 +++ 3 files changed, 18 insertions(+) create mode 100644 releasenotes/notes/availability-zone-support-8e66f55e46b7ef9a.yaml create mode 100644 releasenotes/notes/docs-improvements-17e31babe38e2962.yaml create mode 100644 releasenotes/notes/drop-nuage-commands-df10aab6ccd77ed2.yaml diff --git a/releasenotes/notes/availability-zone-support-8e66f55e46b7ef9a.yaml b/releasenotes/notes/availability-zone-support-8e66f55e46b7ef9a.yaml new file mode 100644 index 000000000..5551084f1 --- /dev/null +++ b/releasenotes/notes/availability-zone-support-8e66f55e46b7ef9a.yaml @@ -0,0 +1,10 @@ +--- +features: + - | + CLI support for availability zones. + + * The ``availability-zone-list`` command provides a list of + availability zones. + * The ``net-create`` and ``router-create`` commands include a + ``--availability-zone-hint`` option. + * The ``agent-list`` command output includes availability zones. diff --git a/releasenotes/notes/docs-improvements-17e31babe38e2962.yaml b/releasenotes/notes/docs-improvements-17e31babe38e2962.yaml new file mode 100644 index 000000000..f00dc742d --- /dev/null +++ b/releasenotes/notes/docs-improvements-17e31babe38e2962.yaml @@ -0,0 +1,5 @@ +--- +other: + - | + Addition of CLI user documentation including output filters, extra + options, and operation using os-client-config. diff --git a/releasenotes/notes/drop-nuage-commands-df10aab6ccd77ed2.yaml b/releasenotes/notes/drop-nuage-commands-df10aab6ccd77ed2.yaml new file mode 100644 index 000000000..b76f40f05 --- /dev/null +++ b/releasenotes/notes/drop-nuage-commands-df10aab6ccd77ed2.yaml @@ -0,0 +1,3 @@ +--- +upgrade: + - Remove Nuage-specific commands. From 2f075a8c167fbd08c773da2e9004876d805fc993 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Thu, 7 Jan 2016 16:48:46 +0000 Subject: [PATCH 343/845] Updated from global requirements Change-Id: I3d16c7e2a42ec3a3d969b782200276ef682bb749 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 033545cd4..58b4593ff 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ netaddr!=0.7.16,>=0.7.12 oslo.i18n>=1.5.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 oslo.utils>=3.2.0 # Apache-2.0 -os-client-config!=1.6.2,>=1.4.0 +os-client-config>=1.13.1 keystoneauth1>=2.1.0 requests!=2.9.0,>=2.8.1 simplejson>=2.2.0 From 5286df8d9f6a57efc9493f783106fdc30c159f13 Mon Sep 17 00:00:00 2001 From: reedip Date: Tue, 5 Jan 2016 11:45:51 +0900 Subject: [PATCH 344/845] Adding a lowercase converter in utils.py Certain NeutronClient CLIs require input in lower cases. In order to allow user to provide input in a case-insensitive format, similar to the uppercase function already existing in utils.py, this patch adds a lowercase function. Reference: https://review.openstack.org/#/c/140628/16..18/neutronclient/common/utils.py Change-Id: I3ad44a24a3a341209d3d3a033691b09d98b72e55 --- neutronclient/common/utils.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/neutronclient/common/utils.py b/neutronclient/common/utils.py index 9c056752b..4f19dac40 100644 --- a/neutronclient/common/utils.py +++ b/neutronclient/common/utils.py @@ -46,6 +46,10 @@ def convert_to_uppercase(string): return string.upper() +def convert_to_lowercase(string): + return string.lower() + + def get_client_class(api_name, version, version_map): """Returns the client class for the requested API version. From 3a4d49d9dfb08a6e2f2702c7e6cc71d51b3146a4 Mon Sep 17 00:00:00 2001 From: Ukesh Kumar Vasudevan Date: Fri, 18 Dec 2015 08:42:48 +0000 Subject: [PATCH 345/845] Enhance the help info of "neutron router-gateway-set" The help info of "neutron router-gateway-set" is not much complete. Below is the help message of "neutron router-gateway-set": usage: neutron router-gateway-set [-h] [--request-format {json,xml}] [--disable-snat] [--fixed-ip FIXED_IP] ROUTER EXTERNAL-NETWORK neutron router-gateway-set: error: too few arguments Modified the help message and added unit test. Usage should be as below, usage: neutron router-gateway-set [-h] [--request-format {json,xml}] [--disable-snat] [--fixed-ip subnet_id=SUBNET,ip_address=IP_ADDR] ROUTER EXTERNAL-NETWORK neutron router-gateway-set: error: too few arguments Change-Id: I2a0bbd147803108e4001ffe86594e1201b9c388b Closes-Bug: 1527129 --- neutronclient/neutron/v2_0/router.py | 5 ++++- neutronclient/tests/unit/test_cli20_router.py | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index 955c7b802..4e4066b4b 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -226,9 +226,12 @@ def get_parser(self, prog_name): '--disable-snat', action='store_true', help=_('Disable source NAT on the router gateway.')) parser.add_argument( - '--fixed-ip', action='append', + '--fixed-ip', metavar='subnet_id=SUBNET,ip_address=IP_ADDR', + action='append', help=_('Desired IP and/or subnet on external network: ' 'subnet_id=,ip_address=. ' + 'You can specify both of subnet_id and ip_address or ' + 'specify one of them as well. ' 'You can repeat this option.')) return parser diff --git a/neutronclient/tests/unit/test_cli20_router.py b/neutronclient/tests/unit/test_cli20_router.py index d69552c8d..66372f861 100644 --- a/neutronclient/tests/unit/test_cli20_router.py +++ b/neutronclient/tests/unit/test_cli20_router.py @@ -383,6 +383,21 @@ def test_set_gateway_external_subnet(self): {"subnet_id": "mysubnet"}]}} ) + def test_set_gateway_external_ip_and_subnet(self): + # set external gateway for router: myid externalid --fixed-ip ... + resource = 'router' + cmd = router.SetGatewayRouter(test_cli20.MyApp(sys.stdout), None) + args = ['myid', 'externalid', '--fixed-ip', + 'ip_address=10.0.0.2,subnet_id=mysubnet'] + self._test_update_resource(resource, cmd, 'myid', + args, + {"external_gateway_info": + {"network_id": "externalid", + "external_fixed_ips": [ + {"subnet_id": "mysubnet", + "ip_address": "10.0.0.2"}]}} + ) + def test_remove_gateway(self): # Remove external gateway from router: externalid. resource = 'router' From 538c23a9e0fb8e3c19b28bd1ceaa225f09f7a6e9 Mon Sep 17 00:00:00 2001 From: Ryan Tidwell Date: Mon, 12 Oct 2015 13:42:33 -0700 Subject: [PATCH 346/845] Add support for ip_version on AddressScope create Address scopes must now be created with an explicit ip_version. This patchenables ip_version to be passed on the neutron CLI. Change-Id: Iee902e1539d2bc0cb03c24f483ab7bab8b6a657e Depends-On: Ibc6de08e0ef58a5da954d13f274f6003012a76cd Partially-Implements: blueprint address-scopes --- neutronclient/neutron/v2_0/address_scope.py | 11 +++- .../tests/unit/test_cli20_address_scope.py | 57 ++++++++++++++----- 2 files changed, 52 insertions(+), 16 deletions(-) diff --git a/neutronclient/neutron/v2_0/address_scope.py b/neutronclient/neutron/v2_0/address_scope.py index a79590b1b..8a89282fd 100755 --- a/neutronclient/neutron/v2_0/address_scope.py +++ b/neutronclient/neutron/v2_0/address_scope.py @@ -22,7 +22,7 @@ class ListAddressScope(neutronV20.ListCommand): """List address scopes that belong to a given tenant.""" resource = 'address_scope' - list_columns = ['id', 'name'] + list_columns = ['id', 'name', 'ip_version'] pagination_support = True sorting_support = True @@ -46,9 +46,16 @@ def add_known_arguments(self, parser): parser.add_argument( 'name', help=_('Specify the name of the address scope.')) + parser.add_argument( + 'ip_version', + metavar='IP_VERSION', + type=int, + choices=[4, 6], + help=_('Specify the address family of the address scope.')) def args2body(self, parsed_args): - body = {'name': parsed_args.name} + body = {'name': parsed_args.name, + 'ip_version': parsed_args.ip_version} if parsed_args.shared: body['shared'] = True neutronV20.update_dict(parsed_args, body, ['tenant_id']) diff --git a/neutronclient/tests/unit/test_cli20_address_scope.py b/neutronclient/tests/unit/test_cli20_address_scope.py index 9b98631d8..481f23128 100755 --- a/neutronclient/tests/unit/test_cli20_address_scope.py +++ b/neutronclient/tests/unit/test_cli20_address_scope.py @@ -30,19 +30,46 @@ class CLITestV20AddressScopeJSON(test_cli20.CLITestV20Base): def setUp(self): super(CLITestV20AddressScopeJSON, self).setUp(plurals={'tags': 'tag'}) - def test_create_address_scope_with_minimum_option(self): - # Create address_scope: foo-address-scope with minimum option. + def test_create_address_scope_with_minimum_option_ipv4(self): + """Create address_scope: foo-address-scope with minimum option.""" resource = 'address_scope' cmd = address_scope.CreateAddressScope( test_cli20.MyApp(sys.stdout), None) name = 'foo-address-scope' myid = 'myid' - args = [name] - position_names = ['name'] - position_values = [name] + args = [name, '4'] + position_names = ['name', 'ip_version'] + position_values = [name, 4] self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) + def test_create_address_scope_with_minimum_option_ipv6(self): + """Create address_scope: foo-address-scope with minimum option.""" + resource = 'address_scope' + cmd = address_scope.CreateAddressScope( + test_cli20.MyApp(sys.stdout), None) + name = 'foo-address-scope' + myid = 'myid' + args = [name, '6'] + position_names = ['name', 'ip_version'] + position_values = [name, 6] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_create_address_scope_with_minimum_option_bad_ip_version(self): + """Create address_scope: foo-address-scope with minimum option.""" + resource = 'address_scope' + cmd = address_scope.CreateAddressScope( + test_cli20.MyApp(sys.stdout), None) + name = 'foo-address-scope' + myid = 'myid' + args = [name, '5'] + position_names = ['name', 'ip_version'] + position_values = [name, 5] + self.assertRaises(SystemExit, self._test_create_resource, + resource, cmd, name, myid, args, position_names, + position_values) + def test_create_address_scope_with_all_option(self): # Create address_scope: foo-address-scope with all options. resource = 'address_scope' @@ -50,9 +77,9 @@ def test_create_address_scope_with_all_option(self): test_cli20.MyApp(sys.stdout), None) name = 'foo-address-scope' myid = 'myid' - args = [name, '--shared'] - position_names = ['name', 'shared'] - position_values = [name, True] + args = [name, '4', '--shared'] + position_names = ['name', 'ip_version', 'shared'] + position_values = [name, 4, True] self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) @@ -62,10 +89,11 @@ def test_create_address_scope_with_unicode(self): cmd = address_scope.CreateAddressScope( test_cli20.MyApp(sys.stdout), None) name = u'\u7f51\u7edc' + ip_version = u'4' myid = 'myid' - args = [name] - position_names = ['name'] - position_values = [name] + args = [name, ip_version] + position_names = ['name', 'ip_version'] + position_values = [name, 4] self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) @@ -115,7 +143,7 @@ def test_list_address_scope_sort(self): cmd = address_scope.ListAddressScope(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, - sort_key=["name", "id"], + sort_key=["name", "id", "ip_version"], sort_dir=["asc", "desc"]) def test_list_address_scope_limit(self): @@ -130,9 +158,10 @@ def test_show_address_scope(self): resource = 'address_scope' cmd = address_scope.ShowAddressScope( test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] + args = ['--fields', 'id', '--fields', 'name', self.test_id, + '--fields', 'ip_version', '6'] self._test_show_resource(resource, cmd, self.test_id, args, - ['id', 'name']) + ['id', 'name', 'ip_version']) def test_delete_address_scope(self): # Delete address_scope: address_scope_id. From 09030171037027d54445a6cfa36481b302373cc4 Mon Sep 17 00:00:00 2001 From: linwwu Date: Fri, 8 Jan 2016 22:51:53 +0800 Subject: [PATCH 347/845] Remove 'u' displayed before subnetpool-list's prefixes There is useless 'u' in the showing of "neutron subnetpool-list", Please check bug/1531418 for details. Add formatter as subnet did in the code. Change-Id: I4e1502c379032da240ad51cce8be8c1ca558ef15 Closes-Bug: #1531418 --- neutronclient/neutron/v2_0/subnetpool.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/neutronclient/neutron/v2_0/subnetpool.py b/neutronclient/neutron/v2_0/subnetpool.py index 8677cd5c5..a8b9461df 100644 --- a/neutronclient/neutron/v2_0/subnetpool.py +++ b/neutronclient/neutron/v2_0/subnetpool.py @@ -18,6 +18,13 @@ from neutronclient.neutron import v2_0 as neutronV20 +def _format_prefixes(subnetpool): + try: + return '\n'.join(pool for pool in subnetpool['prefixes']) + except (TypeError, KeyError): + return subnetpool['prefixes'] + + def add_updatable_arguments(parser): parser.add_argument( '--min-prefixlen', type=int, @@ -43,6 +50,7 @@ def updatable_args2body(parsed_args, body, for_create=True): class ListSubnetPool(neutronV20.ListCommand): """List subnetpools that belong to a given tenant.""" + _formatters = {'prefixes': _format_prefixes, } resource = 'subnetpool' list_columns = ['id', 'name', 'prefixes', 'default_prefixlen', 'address_scope_id'] From aac9356e00d99941f500a8866b48f55dde5a6c25 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Wed, 13 Jan 2016 08:19:08 +0000 Subject: [PATCH 348/845] Updated from global requirements Change-Id: I1d16b2a589d05b1b46e7655ea45460edeb6ac6a3 --- test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index 1ef23babf..9bad1ccc9 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -16,4 +16,4 @@ requests-mock>=0.7.0 # Apache-2.0 sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 testrepository>=0.0.18 testtools>=1.4.0 -tempest-lib>=0.12.0 +tempest-lib>=0.13.0 From 745148817bacacc26ff06a8470cf52c815f6565a Mon Sep 17 00:00:00 2001 From: IWAMOTO Toshihiro Date: Wed, 13 Jan 2016 18:06:57 +0900 Subject: [PATCH 349/845] Convert remaining use of neutronclient.i18n to _i18n Change-Id: I77f168af92ae51ce16bed4988bbcaf7c18557727 Related-Bug: 1519493 --- neutronclient/neutron/v2_0/availability_zone.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/neutron/v2_0/availability_zone.py b/neutronclient/neutron/v2_0/availability_zone.py index 761a50b19..2081d0990 100644 --- a/neutronclient/neutron/v2_0/availability_zone.py +++ b/neutronclient/neutron/v2_0/availability_zone.py @@ -10,7 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. -from neutronclient.i18n import _ +from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronv20 From f903ad9eea143d03eb6f09a53bc6c3de7bc815da Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Fri, 8 Jan 2016 22:56:22 +0900 Subject: [PATCH 350/845] refactor: Remove usage of useless command.command.OpenStackCommand It seems OpenStackCommand was introduced to make neutronclient comamnds usable in OpenStack client, but at now OpenStack client directly implements cliff Command and does not require this kind of wrapper. To simplify the code, this commit drops the useless class. Partial-Bug: #1532258 Change-Id: Iaa5ce9f4b98d5c85ee6ba0303067e65a829fc78e --- neutronclient/common/command.py | 35 -------------------------- neutronclient/neutron/v2_0/__init__.py | 16 +++++++++--- neutronclient/shell.py | 15 ++++++----- 3 files changed, 22 insertions(+), 44 deletions(-) delete mode 100644 neutronclient/common/command.py diff --git a/neutronclient/common/command.py b/neutronclient/common/command.py deleted file mode 100644 index 3d0540744..000000000 --- a/neutronclient/common/command.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright 2012 OpenStack Foundation. -# 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. -# - -from cliff import command - - -class OpenStackCommand(command.Command): - """Base class for OpenStack commands.""" - - api = None - - def run(self, parsed_args): - if not self.api: - return - else: - return super(OpenStackCommand, self).run(parsed_args) - - def get_data(self, parsed_args): - pass - - def take_action(self, parsed_args): - return self.get_data(parsed_args) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index 2bffb5f01..3aeb26a44 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -21,13 +21,13 @@ import logging import re +from cliff import command from cliff import lister from cliff import show from oslo_serialization import jsonutils import six from neutronclient._i18n import _ -from neutronclient.common import command from neutronclient.common import exceptions from neutronclient.common import utils @@ -369,7 +369,7 @@ def update_dict(obj, dict, attributes): dict[attribute] = getattr(obj, attribute) -# command.OpenStackCommand is abstract class so that metaclass of +# cliff.command.Command is abstract class so that metaclass of # subclass must be subclass of metaclass of all its base. # otherwise metaclass conflict exception is raised. class NeutronCommandMeta(abc.ABCMeta): @@ -382,8 +382,10 @@ def __new__(cls, name, bases, cls_dict): @six.add_metaclass(NeutronCommandMeta) -class NeutronCommand(command.OpenStackCommand): +class NeutronCommand(command.Command): + # TODO(amotoki): Get rid of 'api' attribute. There is no such convention + # in OpenStack client. It may be an ancient convention though. api = 'network' values_specs = [] json_indent = None @@ -391,6 +393,14 @@ class NeutronCommand(command.OpenStackCommand): shadow_resource = None parent_id = None + # TODO(amotoki): Remove the usage of get_data and use take_action directly. + # Overriding take_action() is recommended when implementing cliff command. + def get_data(self, parsed_args): + pass + + def take_action(self, parsed_args): + return self.get_data(parsed_args) + @property def cmd_resource(self): if self.shadow_resource: diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 508cdcea2..01771d53f 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -32,11 +32,11 @@ from oslo_utils import encodeutils from cliff import app +from cliff import command from cliff import commandmanager from neutronclient._i18n import _ from neutronclient.common import clientmanager -from neutronclient.common import command as openstack_command from neutronclient.common import exceptions as exc from neutronclient.common import extension as client_extension from neutronclient.common import utils @@ -140,9 +140,12 @@ def check_non_negative_int(value): return value -class BashCompletionCommand(openstack_command.OpenStackCommand): +class BashCompletionCommand(command.Command): """Prints all of the commands and options for bash-completion.""" - resource = "bash_completion" + + def take_action(self, parsed_args): + pass + COMMAND_V2 = { 'bash-completion': BashCompletionCommand, @@ -727,9 +730,9 @@ def _bash_completion(self): options = set() for option, _action in self.parser._option_string_actions.items(): options.add(option) - for command_name, command in self.command_manager: - commands.add(command_name) - cmd_factory = command.load() + for _name, _command in self.command_manager: + commands.add(_name) + cmd_factory = _command.load() cmd = cmd_factory(self, None) cmd_parser = cmd.get_parser('') for option, _action in cmd_parser._option_string_actions.items(): From 2eb5b71c074805de2ca2a23c04be653ca8675410 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Fri, 8 Jan 2016 23:01:36 +0900 Subject: [PATCH 351/845] refactor: Drop meaningless 'api' attribute from NeutronCommand class 'api' attribute in NeutronCommand class was required by OpenStackCommand class. It is now meaningless. Partial-Bug: #1532258 Change-Id: I7fa24cc1011e5ee0ce1c8956edae785cfb4b9408 --- neutronclient/neutron/v2_0/__init__.py | 8 -------- neutronclient/neutron/v2_0/floatingip.py | 2 -- neutronclient/neutron/v2_0/quota.py | 3 --- neutronclient/neutron/v2_0/router.py | 3 --- 4 files changed, 16 deletions(-) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index 3aeb26a44..80c0131ce 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -384,9 +384,6 @@ def __new__(cls, name, bases, cls_dict): @six.add_metaclass(NeutronCommandMeta) class NeutronCommand(command.Command): - # TODO(amotoki): Get rid of 'api' attribute. There is no such convention - # in OpenStack client. It may be an ancient convention though. - api = 'network' values_specs = [] json_indent = None resource = None @@ -456,7 +453,6 @@ def args2body(self, parsed_args): class CreateCommand(NeutronCommand, show.ShowOne): """Create a resource for a given tenant.""" - api = 'network' log = None def get_parser(self, prog_name): @@ -498,7 +494,6 @@ def get_data(self, parsed_args): class UpdateCommand(NeutronCommand): """Update resource's information.""" - api = 'network' log = None allow_names = True @@ -553,7 +548,6 @@ def run(self, parsed_args): class DeleteCommand(NeutronCommand): """Delete a given resource.""" - api = 'network' log = None allow_names = True @@ -599,7 +593,6 @@ def run(self, parsed_args): class ListCommand(NeutronCommand, lister.Lister): """List resources that belong to a given tenant.""" - api = 'network' log = None _formatters = {} list_columns = [] @@ -705,7 +698,6 @@ def get_data(self, parsed_args): class ShowCommand(NeutronCommand, show.ShowOne): """Show information of a given resource.""" - api = 'network' log = None allow_names = True diff --git a/neutronclient/neutron/v2_0/floatingip.py b/neutronclient/neutron/v2_0/floatingip.py index 44b402ad0..7e634e9da 100644 --- a/neutronclient/neutron/v2_0/floatingip.py +++ b/neutronclient/neutron/v2_0/floatingip.py @@ -90,7 +90,6 @@ class DeleteFloatingIP(neutronV20.DeleteCommand): class AssociateFloatingIP(neutronV20.NeutronCommand): """Create a mapping between a floating IP and a fixed IP.""" - api = 'network' resource = 'floatingip' def get_parser(self, prog_name): @@ -126,7 +125,6 @@ def run(self, parsed_args): class DisassociateFloatingIP(neutronV20.NeutronCommand): """Remove a mapping from a floating IP to a fixed IP.""" - api = 'network' resource = 'floatingip' def get_parser(self, prog_name): diff --git a/neutronclient/neutron/v2_0/quota.py b/neutronclient/neutron/v2_0/quota.py index 4b6e86e0b..2ccc54546 100644 --- a/neutronclient/neutron/v2_0/quota.py +++ b/neutronclient/neutron/v2_0/quota.py @@ -37,7 +37,6 @@ def get_tenant_id(args, client): class DeleteQuota(neutronV20.NeutronCommand): """Delete defined quotas of a given tenant.""" - api = 'network' resource = 'quota' def get_parser(self, prog_name): @@ -70,7 +69,6 @@ def run(self, parsed_args): class ListQuota(neutronV20.NeutronCommand, lister.Lister): """List quotas of all tenants who have non-default quota values.""" - api = 'network' resource = 'quota' def get_parser(self, prog_name): @@ -98,7 +96,6 @@ class ShowQuota(neutronV20.NeutronCommand, show.ShowOne): """Show quotas of a given tenant. """ - api = 'network' resource = "quota" def get_parser(self, prog_name): diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index 4e4066b4b..88da856b3 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -138,7 +138,6 @@ def args2body(self, parsed_args): class RouterInterfaceCommand(neutronV20.NeutronCommand): """Based class to Add/Remove router interface.""" - api = 'network' resource = 'router' def call_api(self, neutron_client, router_id, body): @@ -211,7 +210,6 @@ def success_message(self, router_id, portinfo): class SetGatewayRouter(neutronV20.NeutronCommand): """Set the external network gateway for a router.""" - api = 'network' resource = 'router' def get_parser(self, prog_name): @@ -264,7 +262,6 @@ def run(self, parsed_args): class RemoveGatewayRouter(neutronV20.NeutronCommand): """Remove an external network gateway from a router.""" - api = 'network' resource = 'router' def get_parser(self, prog_name): From 131160ed5605430c3c538133e28ed3bcbe4bc231 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Fri, 8 Jan 2016 23:11:07 +0900 Subject: [PATCH 352/845] refactor: Get rid of usage of get_data in favor of take_action Previously the dropped OpenStackCommand uses get_data() as an interface which subclasses should implement. On the other hand, cliff Command recommends to implement take_action() in subclasses and there is no reason not to follow this convention. Partial-Bug: #1532258 Change-Id: I164d6f31152df621611985a84eef76e7dfa54d44 --- neutronclient/common/extension.py | 12 +++++------ neutronclient/neutron/v2_0/__init__.py | 20 +++++++++---------- neutronclient/neutron/v2_0/lb/pool.py | 2 +- .../neutron/v2_0/lb/v2/loadbalancer.py | 2 +- neutronclient/neutron/v2_0/lb/v2/member.py | 4 ++-- neutronclient/neutron/v2_0/port.py | 4 ++-- neutronclient/neutron/v2_0/quota.py | 10 +++++----- .../tests/unit/test_cli20_network.py | 2 +- .../tests/unit/test_cli20_securitygroup.py | 2 +- 9 files changed, 28 insertions(+), 30 deletions(-) diff --git a/neutronclient/common/extension.py b/neutronclient/common/extension.py index 2ff8d34a7..f7e361bed 100644 --- a/neutronclient/common/extension.py +++ b/neutronclient/common/extension.py @@ -31,26 +31,26 @@ class NeutronClientExtension(neutronV20.NeutronCommand): class ClientExtensionShow(NeutronClientExtension, neutronV20.ShowCommand): - def get_data(self, parsed_args): + def take_action(self, parsed_args): # NOTE(mdietz): Calls 'execute' to provide a consistent pattern # for any implementers adding extensions with # regard to any other extension verb. return self.execute(parsed_args) def execute(self, parsed_args): - return super(ClientExtensionShow, self).get_data(parsed_args) + return super(ClientExtensionShow, self).take_action(parsed_args) class ClientExtensionList(NeutronClientExtension, neutronV20.ListCommand): - def get_data(self, parsed_args): + def take_action(self, parsed_args): # NOTE(mdietz): Calls 'execute' to provide a consistent pattern # for any implementers adding extensions with # regard to any other extension verb. return self.execute(parsed_args) def execute(self, parsed_args): - return super(ClientExtensionList, self).get_data(parsed_args) + return super(ClientExtensionList, self).take_action(parsed_args) class ClientExtensionDelete(NeutronClientExtension, neutronV20.DeleteCommand): @@ -65,14 +65,14 @@ def execute(self, parsed_args): class ClientExtensionCreate(NeutronClientExtension, neutronV20.CreateCommand): - def get_data(self, parsed_args): + def take_action(self, parsed_args): # NOTE(mdietz): Calls 'execute' to provide a consistent pattern # for any implementers adding extensions with # regard to any other extension verb. return self.execute(parsed_args) def execute(self, parsed_args): - return super(ClientExtensionCreate, self).get_data(parsed_args) + return super(ClientExtensionCreate, self).take_action(parsed_args) class ClientExtensionUpdate(NeutronClientExtension, neutronV20.UpdateCommand): diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index 80c0131ce..300e98e51 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -390,11 +390,9 @@ class NeutronCommand(command.Command): shadow_resource = None parent_id = None - # TODO(amotoki): Remove the usage of get_data and use take_action directly. - # Overriding take_action() is recommended when implementing cliff command. - def get_data(self, parsed_args): - pass - + # TODO(amotoki): Remove take_action here. It should be an abstract method + # as cliff.command.Command does. To do this, we need to avoid overriding + # run() directly. def take_action(self, parsed_args): return self.get_data(parsed_args) @@ -466,8 +464,8 @@ def get_parser(self, prog_name): self.add_known_arguments(parser) return parser - def get_data(self, parsed_args): - self.log.debug('get_data(%s)' % parsed_args) + def take_action(self, parsed_args): + self.log.debug('run(%s)' % parsed_args) self.set_extra_attrs(parsed_args) neutron_client = self.get_client() _extra_values = parse_args_to_dict(self.values_specs) @@ -687,8 +685,8 @@ def setup_columns(self, info, parsed_args): s, _columns, formatters=formatters, ) for s in info), ) - def get_data(self, parsed_args): - self.log.debug('get_data(%s)', parsed_args) + def take_action(self, parsed_args): + self.log.debug('run(%s)', parsed_args) self.set_extra_attrs(parsed_args) data = self.retrieve_list(parsed_args) self.extend_list(data, parsed_args) @@ -714,8 +712,8 @@ def get_parser(self, prog_name): self.add_known_arguments(parser) return parser - def get_data(self, parsed_args): - self.log.debug('get_data(%s)', parsed_args) + def take_action(self, parsed_args): + self.log.debug('run(%s)', parsed_args) self.set_extra_attrs(parsed_args) neutron_client = self.get_client() diff --git a/neutronclient/neutron/v2_0/lb/pool.py b/neutronclient/neutron/v2_0/lb/pool.py index aff529e02..86497a7d9 100644 --- a/neutronclient/neutron/v2_0/lb/pool.py +++ b/neutronclient/neutron/v2_0/lb/pool.py @@ -107,7 +107,7 @@ class RetrievePoolStats(neutronV20.ShowCommand): resource = 'pool' - def get_data(self, parsed_args): + def take_action(self, parsed_args): self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() pool_id = neutronV20.find_resourceid_by_name_or_id( diff --git a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py index 065ac54cd..bfa4f9c31 100644 --- a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py +++ b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py @@ -100,7 +100,7 @@ class RetrieveLoadBalancerStats(neutronV20.ShowCommand): resource = 'loadbalancer' - def get_data(self, parsed_args): + def take_action(self, parsed_args): self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() neutron_client.format = parsed_args.request_format diff --git a/neutronclient/neutron/v2_0/lb/v2/member.py b/neutronclient/neutron/v2_0/lb/v2/member.py index a5a775173..28109c12b 100644 --- a/neutronclient/neutron/v2_0/lb/v2/member.py +++ b/neutronclient/neutron/v2_0/lb/v2/member.py @@ -49,10 +49,10 @@ class ListMember(LbaasMemberMixin, neutronV20.ListCommand): pagination_support = True sorting_support = True - def get_data(self, parsed_args): + def take_action(self, parsed_args): self.parent_id = _get_pool_id(self.get_client(), parsed_args.pool) self.values_specs.append('--pool_id=%s' % self.parent_id) - return super(ListMember, self).get_data(parsed_args) + return super(ListMember, self).take_action(parsed_args) class ShowMember(LbaasMemberMixin, neutronV20.ShowCommand): diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index 719d4addd..6c997f433 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -111,12 +111,12 @@ def get_parser(self, prog_name): help=_('ID or name of router to look up.')) return parser - def get_data(self, parsed_args): + def take_action(self, parsed_args): neutron_client = self.get_client() _id = neutronV20.find_resourceid_by_name_or_id( neutron_client, 'router', parsed_args.id) self.values_specs.append('--device_id=%s' % _id) - return super(ListRouterPort, self).get_data(parsed_args) + return super(ListRouterPort, self).take_action(parsed_args) class ShowPort(neutronV20.ShowCommand): diff --git a/neutronclient/neutron/v2_0/quota.py b/neutronclient/neutron/v2_0/quota.py index 2ccc54546..a88f0c628 100644 --- a/neutronclient/neutron/v2_0/quota.py +++ b/neutronclient/neutron/v2_0/quota.py @@ -75,8 +75,8 @@ def get_parser(self, prog_name): parser = super(ListQuota, self).get_parser(prog_name) return parser - def get_data(self, parsed_args): - self.log.debug('get_data(%s)', parsed_args) + def take_action(self, parsed_args): + self.log.debug('run(%s)', parsed_args) neutron_client = self.get_client() search_opts = {} self.log.debug('search options: %s', search_opts) @@ -114,8 +114,8 @@ def get_parser(self, prog_name): help=argparse.SUPPRESS, nargs='?') return parser - def get_data(self, parsed_args): - self.log.debug('get_data(%s)', parsed_args) + def take_action(self, parsed_args): + self.log.debug('run(%s)', parsed_args) neutron_client = self.get_client() tenant_id = get_tenant_id(parsed_args, neutron_client) params = {} @@ -213,7 +213,7 @@ def args2body(self, parsed_args): getattr(parsed_args, resource)) return {self.resource: quota} - def get_data(self, parsed_args): + def take_action(self, parsed_args): self.log.debug('run(%s)', parsed_args) neutron_client = self.get_client() _extra_values = neutronV20.parse_args_to_dict(self.values_specs) diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index 4909565c9..8d264c4d1 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -288,7 +288,7 @@ def setup_list_stub(resources, data, query): args = [] cmd_parser = cmd.get_parser('list_networks') parsed_args = cmd_parser.parse_args(args) - result = cmd.get_data(parsed_args) + result = cmd.take_action(parsed_args) self.mox.VerifyAll() self.mox.UnsetStubs() _result = [x for x in result[1]] diff --git a/neutronclient/tests/unit/test_cli20_securitygroup.py b/neutronclient/tests/unit/test_cli20_securitygroup.py index 721d08c06..07c6a3604 100644 --- a/neutronclient/tests/unit/test_cli20_securitygroup.py +++ b/neutronclient/tests/unit/test_cli20_securitygroup.py @@ -407,7 +407,7 @@ def setup_list_stub(resources, data, query): cmd_parser = cmd.get_parser('list_security_group_rules') parsed_args = cmd_parser.parse_args(args) - result = cmd.get_data(parsed_args) + result = cmd.take_action(parsed_args) self.mox.VerifyAll() self.mox.UnsetStubs() # Check columns From 2f08273be94db6f77f0b6d79fcff79746935d0d5 Mon Sep 17 00:00:00 2001 From: Irina Date: Mon, 18 Jan 2016 18:54:08 +0800 Subject: [PATCH 353/845] Fix typo in docstrings Change 'formating' to 'formatting'. Change-Id: If48e47a674dbb454f41ee429b310d26e8404f8a2 --- neutronclient/v2_0/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index fed5b1395..7c9bd3184 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -88,7 +88,7 @@ def exception_handler_v20(status_code, error_content): class APIParamsCall(object): - """A Decorator to support formating and tenant overriding and filters.""" + """A Decorator to support formatting and tenant overriding and filters.""" def __init__(self, function): self.function = function From b12d19df92311f43d1d21e8b7232a7b41f9a6801 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Tue, 19 Jan 2016 12:08:12 +0000 Subject: [PATCH 354/845] Updated from global requirements Change-Id: Ieb89e1291194e947c444a5a3bfce5423b5797c1d --- requirements.txt | 22 +++++++++++----------- test-requirements.txt | 20 ++++++++++---------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/requirements.txt b/requirements.txt index 58b4593ff..4dd2f604c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,18 +1,18 @@ # The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. -pbr>=1.6 -argparse +pbr>=1.6 # Apache-2.0 +argparse # PSF cliff>=1.15.0 # Apache-2.0 debtcollector>=0.3.0 # Apache-2.0 -iso8601>=0.1.9 -netaddr!=0.7.16,>=0.7.12 +iso8601>=0.1.9 # MIT +netaddr!=0.7.16,>=0.7.12 # BSD oslo.i18n>=1.5.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 -oslo.utils>=3.2.0 # Apache-2.0 -os-client-config>=1.13.1 -keystoneauth1>=2.1.0 -requests!=2.9.0,>=2.8.1 -simplejson>=2.2.0 -six>=1.9.0 -Babel>=1.3 +oslo.utils>=3.4.0 # Apache-2.0 +os-client-config>=1.13.1 # Apache-2.0 +keystoneauth1>=2.1.0 # Apache-2.0 +requests!=2.9.0,>=2.8.1 # Apache-2.0 +simplejson>=2.2.0 # MIT +six>=1.9.0 # MIT +Babel>=1.3 # BSD diff --git a/test-requirements.txt b/test-requirements.txt index 9bad1ccc9..35c9b6b6e 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -3,17 +3,17 @@ # process, which may cause wedges in the gate later. hacking<0.11,>=0.10.0 -coverage>=3.6 -discover -fixtures>=1.3.1 -mox3>=0.7.0 -mock>=1.2 +coverage>=3.6 # Apache-2.0 +discover # BSD +fixtures>=1.3.1 # Apache-2.0/BSD +mox3>=0.7.0 # Apache-2.0 +mock>=1.2 # BSD oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 -python-subunit>=0.0.18 +python-subunit>=0.0.18 # Apache-2.0/BSD reno>=0.1.1 # Apache2 requests-mock>=0.7.0 # Apache-2.0 -sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 -testrepository>=0.0.18 -testtools>=1.4.0 -tempest-lib>=0.13.0 +sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 # BSD +testrepository>=0.0.18 # Apache-2.0/BSD +testtools>=1.4.0 # MIT +tempest-lib>=0.13.0 # Apache-2.0 From 047bbd90778535ba2a47eef19cb92faf7ce37bce Mon Sep 17 00:00:00 2001 From: John Davidge Date: Tue, 20 Oct 2015 12:56:56 +0100 Subject: [PATCH 355/845] Add support for default subnetpools API Adds support for creating, viewing, and updating subnetpools with the new 'is_default' property. This feature will be used by OpenStack admins to manage the default subnetpools used by their tenants. This feature replaces the old configuration options for default subnetpools so that the neutron service no longer needs to be restarted after changes. Creating a new default subnetpool is achieved by adding the flag: '--is-default True' to the subnetpool-create call. For example: neutron subnetpool-create --pool-prefix 10.0.0.0/24 --is_default True The above will result in the creation of a new default subnetpool, only if a default does not already exist for the given IP version. Only one default may exist for each IP version. Similarly, 'is-default True' and '--is-default False' can be used when calling subnetpool-update. This feature can only be used by the cloud admin. All users can see the is_default value in subnetpool-list and subnetpool-show. Includes release notes. DocImpact Change-Id: I80ad2a1407266eff5c66c75974d3cc467701a75e Closes-Bug: 1508012 --- neutronclient/neutron/v2_0/subnetpool.py | 9 +++++++-- .../tests/unit/test_cli20_subnetpool.py | 20 +++++++++++++++++++ ...t-subnetpool-support-c0d34870e9d3e814.yaml | 9 +++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 releasenotes/notes/default-subnetpool-support-c0d34870e9d3e814.yaml diff --git a/neutronclient/neutron/v2_0/subnetpool.py b/neutronclient/neutron/v2_0/subnetpool.py index 8677cd5c5..f94c28b6b 100644 --- a/neutronclient/neutron/v2_0/subnetpool.py +++ b/neutronclient/neutron/v2_0/subnetpool.py @@ -15,6 +15,7 @@ # from neutronclient._i18n import _ +from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronV20 @@ -32,12 +33,16 @@ def add_updatable_arguments(parser): '--pool-prefix', action='append', dest='prefixes', help=_('Subnetpool prefixes (This option can be repeated).')) + utils.add_boolean_argument( + parser, '--is-default', + help=_('Specify whether this should be the default subnetpool ' + '(True meaning default).')) def updatable_args2body(parsed_args, body, for_create=True): neutronV20.update_dict(parsed_args, body, ['name', 'prefixes', 'default_prefixlen', - 'min_prefixlen', 'max_prefixlen']) + 'min_prefixlen', 'max_prefixlen', 'is_default']) class ListSubnetPool(neutronV20.ListCommand): @@ -45,7 +50,7 @@ class ListSubnetPool(neutronV20.ListCommand): resource = 'subnetpool' list_columns = ['id', 'name', 'prefixes', - 'default_prefixlen', 'address_scope_id'] + 'default_prefixlen', 'address_scope_id', 'is_default'] pagination_support = True sorting_support = True diff --git a/neutronclient/tests/unit/test_cli20_subnetpool.py b/neutronclient/tests/unit/test_cli20_subnetpool.py index dd743aa98..60d25de6f 100644 --- a/neutronclient/tests/unit/test_cli20_subnetpool.py +++ b/neutronclient/tests/unit/test_cli20_subnetpool.py @@ -63,6 +63,26 @@ def test_create_subnetpool_not_shared(self): self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) + def test_create_subnetpool(self, default='false'): + # Create subnetpool: myname. + resource = 'subnetpool' + cmd = subnetpool.CreateSubnetPool(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + min_prefixlen = 30 + prefix1 = '10.11.12.0/24' + prefix2 = '12.11.13.0/24' + args = [name, '--min-prefixlen', str(min_prefixlen), + '--pool-prefix', prefix1, '--pool-prefix', prefix2, + '--is-default', default] + position_names = ['name', 'min_prefixlen', 'prefixes', 'is_default'] + position_values = [name, min_prefixlen, [prefix1, prefix2], default] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_create_subnetpool_default(self): + self.test_create_subnetpool(default='true') + def test_create_subnetpool_with_unicode(self): # Create subnetpool: u'\u7f51\u7edc'. resource = 'subnetpool' diff --git a/releasenotes/notes/default-subnetpool-support-c0d34870e9d3e814.yaml b/releasenotes/notes/default-subnetpool-support-c0d34870e9d3e814.yaml new file mode 100644 index 000000000..2a571d6d8 --- /dev/null +++ b/releasenotes/notes/default-subnetpool-support-c0d34870e9d3e814.yaml @@ -0,0 +1,9 @@ +--- +features: + - | + CLI support for default subnetpools. + + * The ``subnetpool-list`` and ``subnetpool-show`` command output includes + the ``is_default`` field. + * The ``subnetpool-create`` and ``subnetpool-update`` commands include a + ``--is-default`` option. From a97f28f18729a55f3cdc0c7c21d6a183d6b01a1c Mon Sep 17 00:00:00 2001 From: minwang Date: Tue, 17 Nov 2015 16:53:05 -0800 Subject: [PATCH 356/845] Add code for load balancer status tree So far the feature of retrieving a specific Load Balancer's Status Tree is not implemented in the neutronclient code, we need to add feature code and related tests. DocImpact Add loadbalancer-status-tree feature in CLI Change-Id: Ia7804ab6baac674830c6834f67cfd411ebf4d14f --- .../neutron/v2_0/lb/v2/loadbalancer.py | 29 +++++++++++++++ neutronclient/shell.py | 1 + .../unit/lb/v2/test_cli20_loadbalancer.py | 37 +++++++++++++++++++ neutronclient/v2_0/client.py | 7 ++++ .../add-lb-status-tree-723f23c09617de3b.yaml | 7 ++++ releasenotes/source/old_relnotes.rst | 1 + 6 files changed, 82 insertions(+) create mode 100644 releasenotes/notes/add-lb-status-tree-723f23c09617de3b.yaml diff --git a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py index 065ac54cd..caef541aa 100644 --- a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py +++ b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py @@ -14,6 +14,7 @@ # License for the specific language governing permissions and limitations # under the License. # +from oslo_serialization import jsonutils from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronV20 @@ -128,3 +129,31 @@ def get_data(self, parsed_args): # here covert the data dict to the 1-1 vector format below: # [(field1, field2, field3, ...), (value1, value2, value3, ...)] return list(zip(*sorted(stats.items()))) + + +class RetrieveLoadBalancerStatus(neutronV20.NeutronCommand): + """Retrieve status for a given loadbalancer. + + The only output is a formatted JSON tree, and the table format + does not support this type of data. + """ + resource = 'loadbalancer' + + def get_parser(self, prog_name): + parser = super(RetrieveLoadBalancerStatus, self).get_parser(prog_name) + parser.add_argument( + self.resource, metavar=self.resource.upper(), + help=_('ID or name of %s to show.') % self.resource) + + return parser + + def take_action(self, parsed_args): + self.log.debug('run(%s)' % parsed_args) + neutron_client = self.get_client() + lb_id = neutronV20.find_resourceid_by_name_or_id( + neutron_client, self.resource, parsed_args.loadbalancer) + params = {} + data = neutron_client.retrieve_loadbalancer_status(lb_id, **params) + res = data['statuses'] + if 'statuses' in data: + print(jsonutils.dumps(res, indent=4)) diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 508cdcea2..d7840b8a5 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -204,6 +204,7 @@ class BashCompletionCommand(openstack_command.OpenStackCommand): 'lbaas-loadbalancer-update': lbaas_loadbalancer.UpdateLoadBalancer, 'lbaas-loadbalancer-delete': lbaas_loadbalancer.DeleteLoadBalancer, 'lbaas-loadbalancer-stats': lbaas_loadbalancer.RetrieveLoadBalancerStats, + 'lbaas-loadbalancer-status': lbaas_loadbalancer.RetrieveLoadBalancerStatus, 'lbaas-listener-list': lbaas_listener.ListListener, 'lbaas-listener-show': lbaas_listener.ShowListener, 'lbaas-listener-create': lbaas_listener.CreateListener, diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py b/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py index cb58c6d39..205e6816b 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py @@ -166,3 +166,40 @@ def test_retrieve_loadbalancer_stats(self): self.assertIn('1234', _str) self.assertIn('bytes_out', _str) self.assertIn('4321', _str) + + def test_get_loadbalancer_statuses(self): + # lbaas-loadbalancer-status test_id. + resource = 'loadbalancer' + cmd = lb.RetrieveLoadBalancerStatus(test_cli20.MyApp(sys.stdout), None) + my_id = self.test_id + args = [my_id] + + self.mox.StubOutWithMock(cmd, "get_client") + self.mox.StubOutWithMock(self.client.httpclient, "request") + cmd.get_client().MultipleTimes().AndReturn(self.client) + + expected_res = {'statuses': {'operating_status': 'ONLINE', + 'provisioning_status': 'ACTIVE'}} + + resstr = self.client.serialize(expected_res) + + path = getattr(self.client, "lbaas_loadbalancer_path_status") + return_tup = (test_cli20.MyResp(200), resstr) + self.client.httpclient.request( + test_cli20.end_url(path % my_id), 'GET', + body=None, + headers=mox.ContainsKeyValue( + 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(return_tup) + self.mox.ReplayAll() + + cmd_parser = cmd.get_parser("test_" + resource) + parsed_args = cmd_parser.parse_args(args) + cmd.run(parsed_args) + + self.mox.VerifyAll() + self.mox.UnsetStubs() + _str = self.fake_stdout.make_string() + self.assertIn('operating_status', _str) + self.assertIn('ONLINE', _str) + self.assertIn('provisioning_status', _str) + self.assertIn('ACTIVE', _str) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 4dc738276..a58e9b5c3 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -342,6 +342,7 @@ class Client(ClientBase): lbaas_loadbalancers_path = "/lbaas/loadbalancers" lbaas_loadbalancer_path = "/lbaas/loadbalancers/%s" lbaas_loadbalancer_path_stats = "/lbaas/loadbalancers/%s/stats" + lbaas_loadbalancer_path_status = "/lbaas/loadbalancers/%s/statuses" lbaas_listeners_path = "/lbaas/listeners" lbaas_listener_path = "/lbaas/listeners/%s" lbaas_pools_path = "/lbaas/pools" @@ -944,6 +945,12 @@ def retrieve_loadbalancer_stats(self, loadbalancer, **_params): return self.get(self.lbaas_loadbalancer_path_stats % (loadbalancer), params=_params) + @APIParamsCall + def retrieve_loadbalancer_status(self, loadbalancer, **_params): + """Retrieves status for a certain load balancer.""" + return self.get(self.lbaas_loadbalancer_path_status % (loadbalancer), + params=_params) + @APIParamsCall def list_listeners(self, retrieve_all=True, **_params): """Fetches a list of all lbaas_listeners for a tenant.""" diff --git a/releasenotes/notes/add-lb-status-tree-723f23c09617de3b.yaml b/releasenotes/notes/add-lb-status-tree-723f23c09617de3b.yaml new file mode 100644 index 000000000..8f48d2863 --- /dev/null +++ b/releasenotes/notes/add-lb-status-tree-723f23c09617de3b.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + CLI support for load balancer status tree. + + * The ``lbaas-loadbalancer-status`` command provides the status + tree of a specific load balancer. \ No newline at end of file diff --git a/releasenotes/source/old_relnotes.rst b/releasenotes/source/old_relnotes.rst index 6a571a7b4..0936e32b1 100644 --- a/releasenotes/source/old_relnotes.rst +++ b/releasenotes/source/old_relnotes.rst @@ -10,6 +10,7 @@ Old Release Notes * made the publicURL the default endpoint instead of adminURL * add ability to update security group name (requires 2013.2-Havana or later) * add flake8 and pbr support for testing and building +* add ability to retrieve a specific load balancer's status tree 2.2.0 ----- From 0259e03feaaaf6ed6742bd6512b00eb9f7fc16a4 Mon Sep 17 00:00:00 2001 From: Andreas Jaeger Date: Wed, 20 Jan 2016 19:20:55 +0100 Subject: [PATCH 357/845] Remove argparse from requirements argparse was external in python 2.6 but not anymore, remove it from requirements. This should help with pip 8.0 that gets confused in this situation. Installation of the external argparse is not needed. Change-Id: Ib7e74912b36c1b5ccb514e31fac35efeff57378d --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4dd2f604c..e649fc1ae 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,6 @@ # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. pbr>=1.6 # Apache-2.0 -argparse # PSF cliff>=1.15.0 # Apache-2.0 debtcollector>=0.3.0 # Apache-2.0 iso8601>=0.1.9 # MIT From c2545f869a65564d5ff231ff453272c6a3310d6c Mon Sep 17 00:00:00 2001 From: linwwu Date: Wed, 13 Jan 2016 08:22:05 +0800 Subject: [PATCH 358/845] Remove inconsistency from vpn help text Improve help text for IKEPOLICY VPNSERVICE IPSECPOLICY IPSECSITECONNECTION in python-neutronclient Fixes bug #1335160 Change-Id: I5da7efe8f8c8e6494884571b3d70237782cd314f Closes-bug: #1335160 --- neutronclient/neutron/v2_0/__init__.py | 15 ++++++++++++--- neutronclient/neutron/v2_0/vpn/ikepolicy.py | 3 +++ .../neutron/v2_0/vpn/ipsec_site_connection.py | 3 +++ neutronclient/neutron/v2_0/vpn/ipsecpolicy.py | 3 +++ neutronclient/neutron/v2_0/vpn/vpnservice.py | 3 +++ 5 files changed, 24 insertions(+), 3 deletions(-) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index d5be4a107..f44822a56 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -513,6 +513,7 @@ class UpdateCommand(NeutronCommand): api = 'network' log = None allow_names = True + help_resource = None def get_parser(self, prog_name): parser = super(UpdateCommand, self).get_parser(prog_name) @@ -520,9 +521,11 @@ def get_parser(self, prog_name): help_str = _('ID or name of %s to update.') else: help_str = _('ID of %s to update.') + if not self.help_resource: + self.help_resource = self.resource parser.add_argument( 'id', metavar=self.resource.upper(), - help=help_str % self.resource) + help=help_str % self.help_resource) self.add_known_arguments(parser) return parser @@ -568,6 +571,7 @@ class DeleteCommand(NeutronCommand): api = 'network' log = None allow_names = True + help_resource = None def get_parser(self, prog_name): parser = super(DeleteCommand, self).get_parser(prog_name) @@ -575,9 +579,11 @@ def get_parser(self, prog_name): help_str = _('ID or name of %s to delete.') else: help_str = _('ID of %s to delete.') + if not self.help_resource: + self.help_resource = self.resource parser.add_argument( 'id', metavar=self.resource.upper(), - help=help_str % self.resource) + help=help_str % self.help_resource) self.add_known_arguments(parser) return parser @@ -720,6 +726,7 @@ class ShowCommand(NeutronCommand, show.ShowOne): api = 'network' log = None allow_names = True + help_resource = None def get_parser(self, prog_name): parser = super(ShowCommand, self).get_parser(prog_name) @@ -728,9 +735,11 @@ def get_parser(self, prog_name): help_str = _('ID or name of %s to look up.') else: help_str = _('ID of %s to look up.') + if not self.help_resource: + self.help_resource = self.resource parser.add_argument( 'id', metavar=self.resource.upper(), - help=help_str % self.resource) + help=help_str % self.help_resource) self.add_known_arguments(parser) return parser diff --git a/neutronclient/neutron/v2_0/vpn/ikepolicy.py b/neutronclient/neutron/v2_0/vpn/ikepolicy.py index e771aadaf..509083163 100644 --- a/neutronclient/neutron/v2_0/vpn/ikepolicy.py +++ b/neutronclient/neutron/v2_0/vpn/ikepolicy.py @@ -35,6 +35,7 @@ class ShowIKEPolicy(neutronv20.ShowCommand): """Show information of a given IKE policy.""" resource = 'ikepolicy' + help_resource = 'IKE policy' class CreateIKEPolicy(neutronv20.CreateCommand): @@ -93,6 +94,7 @@ class UpdateIKEPolicy(neutronv20.UpdateCommand): """Update a given IKE policy.""" resource = 'ikepolicy' + help_resource = 'IKE policy' def add_known_arguments(self, parser): parser.add_argument( @@ -114,3 +116,4 @@ class DeleteIKEPolicy(neutronv20.DeleteCommand): """Delete a given IKE policy.""" resource = 'ikepolicy' + help_resource = 'IKE policy' diff --git a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py index 8726c28ac..bace4d2e0 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py +++ b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py @@ -46,6 +46,7 @@ class ShowIPsecSiteConnection(neutronv20.ShowCommand): """Show information of a given IPsec site connection.""" resource = 'ipsec_site_connection' + help_resource = 'IPsec site connection' class IPsecSiteConnectionMixin(object): @@ -196,9 +197,11 @@ class UpdateIPsecSiteConnection(IPsecSiteConnectionMixin, """Update a given IPsec site connection.""" resource = 'ipsec_site_connection' + help_resource = 'IPsec site connection' class DeleteIPsecSiteConnection(neutronv20.DeleteCommand): """Delete a given IPsec site connection.""" resource = 'ipsec_site_connection' + help_resource = 'IPsec site connection' diff --git a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py index 80a334da6..76f09142e 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py +++ b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py @@ -35,6 +35,7 @@ class ShowIPsecPolicy(neutronv20.ShowCommand): """Show information of a given IPsec policy.""" resource = 'ipsecpolicy' + help_resource = 'IPsec policy' class CreateIPsecPolicy(neutronv20.CreateCommand): @@ -92,6 +93,7 @@ class UpdateIPsecPolicy(neutronv20.UpdateCommand): """Update a given IPsec policy.""" resource = 'ipsecpolicy' + help_resource = 'IPsec policy' def add_known_arguments(self, parser): parser.add_argument( @@ -113,3 +115,4 @@ class DeleteIPsecPolicy(neutronv20.DeleteCommand): """Delete a given IPsec policy.""" resource = 'ipsecpolicy' + help_resource = 'IPsec policy' diff --git a/neutronclient/neutron/v2_0/vpn/vpnservice.py b/neutronclient/neutron/v2_0/vpn/vpnservice.py index 8bd2198c9..9e9ae1221 100644 --- a/neutronclient/neutron/v2_0/vpn/vpnservice.py +++ b/neutronclient/neutron/v2_0/vpn/vpnservice.py @@ -34,6 +34,7 @@ class ShowVPNService(neutronv20.ShowCommand): """Show information of a given VPN service.""" resource = 'vpnservice' + help_resource = 'VPN service' class CreateVPNService(neutronv20.CreateCommand): @@ -83,9 +84,11 @@ class UpdateVPNService(neutronv20.UpdateCommand): """Update a given VPN service.""" resource = 'vpnservice' + help_resource = 'VPN service' class DeleteVPNService(neutronv20.DeleteCommand): """Delete a given VPN service.""" resource = 'vpnservice' + help_resource = 'VPN service' From b6a3c4aa9abaccb4136db18673a533e11ea0326d Mon Sep 17 00:00:00 2001 From: reedip Date: Fri, 22 Jan 2016 12:10:05 +0900 Subject: [PATCH 359/845] Trivial Update on ReleaseNotes Release notes have been updated, post the merge of [1], stating the fact that now python-neutronclient does not support py33 anymore. [1]: https://review.openstack.org/#/c/257704/ TrivialFix Change-Id: Ic9e0f6083526deb469fc6ed6a3463fbb3f368296 --- releasenotes/notes/relnotes-from-3.0.0-d7306f5af5e3868d.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/releasenotes/notes/relnotes-from-3.0.0-d7306f5af5e3868d.yaml b/releasenotes/notes/relnotes-from-3.0.0-d7306f5af5e3868d.yaml index 15e569f3f..012bc7fc2 100644 --- a/releasenotes/notes/relnotes-from-3.0.0-d7306f5af5e3868d.yaml +++ b/releasenotes/notes/relnotes-from-3.0.0-d7306f5af5e3868d.yaml @@ -16,6 +16,7 @@ upgrade: - Cisco-specific neutron client commands have been removed. These commands are ported to networking-cisco. - py26 support has been dropped. + - py33 support has been dropped. fixes: - Name is no longer looked up on RBAC policies, RBAC policies have no name field so the name query to From 11d9cc87e0031ee95f39c2949eb5065c6145458f Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Sat, 23 Jan 2016 10:53:38 +0000 Subject: [PATCH 360/845] Updated from global requirements Change-Id: Ib7748eeafe276035ff986f410dff94e4e49ac2cf --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index e649fc1ae..87043807d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,10 +3,10 @@ # process, which may cause wedges in the gate later. pbr>=1.6 # Apache-2.0 cliff>=1.15.0 # Apache-2.0 -debtcollector>=0.3.0 # Apache-2.0 +debtcollector>=1.2.0 # Apache-2.0 iso8601>=0.1.9 # MIT netaddr!=0.7.16,>=0.7.12 # BSD -oslo.i18n>=1.5.0 # Apache-2.0 +oslo.i18n>=2.1.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 oslo.utils>=3.4.0 # Apache-2.0 os-client-config>=1.13.1 # Apache-2.0 From 9dbdced729f6d7750307092d10a9737867a5b01c Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Sat, 9 Jan 2016 13:18:36 +0900 Subject: [PATCH 361/845] Show tenant_id in *-list by admin If API call is done by admin, neutron API returns all resources from all tenants. We received several times a request that tenant_id is useful in such case. This patch displays tenant_id column when *-list is invoked by a user with 'admin' role by default. If columns to be displayed are specified by '-c' option, the specified columns are honored and tenant_id columns is not added. Add functional tests to cover this change. This feature depends on keystoneauth1 and unit tests cannot cover this change. Closes-Bug: #1526238 Change-Id: Id58a0b795f76b4fe24aaf89f1f6486c23f19a553 --- neutronclient/client.py | 6 ++++ neutronclient/neutron/v2_0/__init__.py | 29 +++++++++++++++- neutronclient/tests/functional/base.py | 21 ++++++++---- .../tests/functional/core/test_clientlib.py | 7 ++++ .../tests/functional/core/test_common.py | 33 +++++++++++++++++++ .../tests/unit/test_cli20_network.py | 3 +- .../tests/unit/test_cli20_securitygroup.py | 3 +- ...ant-id-admin-listing-dc13ee7eb889d418.yaml | 6 ++++ 8 files changed, 97 insertions(+), 11 deletions(-) create mode 100644 neutronclient/tests/functional/core/test_common.py create mode 100644 releasenotes/notes/show-tenant-id-admin-listing-dc13ee7eb889d418.yaml diff --git a/neutronclient/client.py b/neutronclient/client.py index 445d3afb0..a48126fc2 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -271,6 +271,9 @@ def get_auth_info(self): 'auth_user_id': self.auth_user_id, 'endpoint_url': self.endpoint_url} + def get_auth_ref(self): + return getattr(self, 'auth_ref', None) + class SessionClient(adapter.Adapter): @@ -341,6 +344,9 @@ def get_auth_info(self): return auth_info + def get_auth_ref(self): + return self.session.auth.get_auth_ref(self.session) + # FIXME(bklei): Should refactor this to use kwargs and only # explicitly list arguments that are not None. diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index 34c5592af..a91d1f8bc 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -681,7 +681,8 @@ def setup_columns(self, info, parsed_args): # if no -c(s) by user and list_columns, we use columns in # both list_columns and returned resource. # Also Keep their order the same as in list_columns - _columns = [x for x in self.list_columns if x in _columns] + _columns = self._setup_columns_with_tenant_id(self.list_columns, + _columns) formatters = self._formatters if hasattr(self, '_formatters_csv') and parsed_args.formatter == 'csv': @@ -691,6 +692,32 @@ def setup_columns(self, info, parsed_args): s, _columns, formatters=formatters, ) for s in info), ) + def _setup_columns_with_tenant_id(self, display_columns, avail_columns): + _columns = [x for x in display_columns if x in avail_columns] + if 'tenant_id' in display_columns: + return _columns + if 'tenant_id' not in avail_columns: + return _columns + if not self.is_admin_role(): + return _columns + try: + pos_id = _columns.index('id') + except ValueError: + pos_id = 0 + try: + pos_name = _columns.index('name') + except ValueError: + pos_name = 0 + _columns.insert(max(pos_id, pos_name) + 1, 'tenant_id') + return _columns + + def is_admin_role(self): + client = self.get_client() + auth_ref = client.httpclient.get_auth_ref() + if not auth_ref: + return False + return 'admin' in auth_ref.role_names + def take_action(self, parsed_args): self.log.debug('run(%s)', parsed_args) self.set_extra_attrs(parsed_args) diff --git a/neutronclient/tests/functional/base.py b/neutronclient/tests/functional/base.py index 0432a5b3d..06eeb055d 100644 --- a/neutronclient/tests/functional/base.py +++ b/neutronclient/tests/functional/base.py @@ -47,19 +47,28 @@ class ClientTestBase(base.ClientTestBase): """ - def _get_clients(self): - self.creds = credentials() + def _get_clients_from_os_cloud_config(self, cloud='devstack-admin'): + creds = credentials(cloud) cli_dir = os.environ.get( 'OS_NEUTRONCLIENT_EXEC_DIR', os.path.join(os.path.abspath('.'), '.tox/functional/bin')) return base.CLIClient( - username=self.creds['username'], - password=self.creds['password'], - tenant_name=self.creds['project_name'], - uri=self.creds['auth_url'], + username=creds['username'], + password=creds['password'], + tenant_name=creds['project_name'], + uri=creds['auth_url'], cli_dir=cli_dir) + def _get_clients(self): + return self._get_clients_from_os_cloud_config() + def neutron(self, *args, **kwargs): return self.clients.neutron(*args, **kwargs) + + def neutron_non_admin(self, *args, **kwargs): + if not hasattr(self, '_non_admin_clients'): + self._non_admin_clients = self._get_clients_from_os_cloud_config( + cloud='devstack') + return self._non_admin_clients.neutron(*args, **kwargs) diff --git a/neutronclient/tests/functional/core/test_clientlib.py b/neutronclient/tests/functional/core/test_clientlib.py index 91fb6be1c..ab10f968a 100644 --- a/neutronclient/tests/functional/core/test_clientlib.py +++ b/neutronclient/tests/functional/core/test_clientlib.py @@ -91,6 +91,13 @@ def test_post_put_delele_network(self): with testtools.ExpectedException(exceptions.NetworkNotFoundClient): self.client.show_network(net_id) + def test_get_auth_ref(self): + # Call some API call to ensure the client is authenticated. + self.client.list_networks() + auth_ref = self.client.httpclient.get_auth_ref() + self.assertIsNotNone(auth_ref) + self.assertIsNotNone(auth_ref.role_names) + class LibraryHTTPClientTest(LibraryTestCase, Libv2HTTPClientTestBase): pass diff --git a/neutronclient/tests/functional/core/test_common.py b/neutronclient/tests/functional/core/test_common.py new file mode 100644 index 000000000..6bdadf96f --- /dev/null +++ b/neutronclient/tests/functional/core/test_common.py @@ -0,0 +1,33 @@ +# Copyright 2016 NEC Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from neutronclient.tests.functional import base + + +class CLICommonFeatureTest(base.ClientTestBase): + + def test_tenant_id_shown_in_list_by_admin(self): + nets = self.parser.table(self.neutron('net-list')) + self.assertIn('tenant_id', nets['headers']) + + def test_tenant_id_not_shown_in_list_with_columns(self): + nets = self.parser.table(self.neutron('net-list -c id -c name')) + self.assertNotIn('tenant_id', nets['headers']) + self.assertListEqual(['id', 'name'], nets['headers']) + + def test_tenant_id_not_shown_in_list_by_non_admin(self): + output = self.neutron_non_admin('net-list') + self.assertNotIn('tenant_id', self.parser.table(output)['headers']) + self.assertTableStruct(self.parser.listing(output), + ['id', 'name']) diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index 8d264c4d1..9c3cb019d 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -271,9 +271,8 @@ def setup_list_stub(resources, data, query): cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) self.mox.StubOutWithMock(cmd, 'get_client') self.mox.StubOutWithMock(self.client.httpclient, 'request') - cmd.get_client().AndReturn(self.client) + cmd.get_client().MultipleTimes().AndReturn(self.client) setup_list_stub('networks', data, '') - cmd.get_client().AndReturn(self.client) filters = '' for n in data: for s in n['subnets']: diff --git a/neutronclient/tests/unit/test_cli20_securitygroup.py b/neutronclient/tests/unit/test_cli20_securitygroup.py index 07c6a3604..8b8ed4785 100644 --- a/neutronclient/tests/unit/test_cli20_securitygroup.py +++ b/neutronclient/tests/unit/test_cli20_securitygroup.py @@ -383,13 +383,12 @@ def setup_list_stub(resources, data, query): test_cli20.MyApp(sys.stdout), None) self.mox.StubOutWithMock(cmd, 'get_client') self.mox.StubOutWithMock(self.client.httpclient, 'request') - cmd.get_client().AndReturn(self.client) + cmd.get_client().MultipleTimes().AndReturn(self.client) query = '' if query_fields: query = '&'.join(['fields=' + f for f in query_fields]) setup_list_stub('security_group_rules', api_data, query) if conv: - cmd.get_client().AndReturn(self.client) sec_ids = set() for n in api_data: sec_ids.add(n['security_group_id']) diff --git a/releasenotes/notes/show-tenant-id-admin-listing-dc13ee7eb889d418.yaml b/releasenotes/notes/show-tenant-id-admin-listing-dc13ee7eb889d418.yaml new file mode 100644 index 000000000..6b84e1843 --- /dev/null +++ b/releasenotes/notes/show-tenant-id-admin-listing-dc13ee7eb889d418.yaml @@ -0,0 +1,6 @@ +--- +features: + - Show tenant_id when ``*-list`` command is run by admin. In neutron + the list operations by admin retrieve all resources from all tenants. + It is not easy to distinguish resources without tenant_id. + This feature is useful for admin operations. From 65812849419bd13b2f72bcfc8a40ae6c5078427b Mon Sep 17 00:00:00 2001 From: Hirofumi Ichihara Date: Mon, 25 Jan 2016 19:29:57 +0900 Subject: [PATCH 362/845] Fix code-block for python code in doc Change-Id: Ia5cb007b0a0093e478416161763dcaa23b9cfbe4 --- doc/source/usage/library.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/source/usage/library.rst b/doc/source/usage/library.rst index 44ff5a641..dff06961f 100644 --- a/doc/source/usage/library.rst +++ b/doc/source/usage/library.rst @@ -54,6 +54,8 @@ Now you can call various methods on the client instance. Alternatively, you can create a client instance using an auth token and a service endpoint URL directly. +.. code-block:: python + >>> from neutronclient.v2_0 import client >>> neutron = client.Client(endpoint_url='http://192.168.206.130:9696/', - token='d3f9226f27774f338019aa2611112ef6') + ... token='d3f9226f27774f338019aa2611112ef6') From 92870401217facba33ec5f0d22d1db3b24da6847 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Tue, 26 Jan 2016 09:56:02 +0900 Subject: [PATCH 363/845] Remove unnecessary entry from old relnotes commit a97f28f18729a55f3cdc0c7c21d6a183d6b01a1c accidentally added a new entry to the old release note. This commit drops it. Change-Id: I686760d69828e4fdb7b47b30fef09394222c5c56 --- releasenotes/source/old_relnotes.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/releasenotes/source/old_relnotes.rst b/releasenotes/source/old_relnotes.rst index 0936e32b1..6a571a7b4 100644 --- a/releasenotes/source/old_relnotes.rst +++ b/releasenotes/source/old_relnotes.rst @@ -10,7 +10,6 @@ Old Release Notes * made the publicURL the default endpoint instead of adminURL * add ability to update security group name (requires 2013.2-Havana or later) * add flake8 and pbr support for testing and building -* add ability to retrieve a specific load balancer's status tree 2.2.0 ----- From 9f792d0b9c6ed86cea0f7efaf65817ff32e5d72d Mon Sep 17 00:00:00 2001 From: Andreas Jaeger Date: Thu, 28 Jan 2016 08:57:09 +0100 Subject: [PATCH 364/845] Update translation setup Follow new infra setup for translations, see spec http://specs.openstack.org/openstack-infra/infra-specs/specs/translation_setup.html for full details. This basically renames python-neutronclient/locale/python-neutronclient.pot to neutronclient/locale/neutronclient. For this we need to update setup.cfg. The domain name is already correct in neutronclient/_i18n.py. There's no need to keep the pot file. The updated scripts work without them. So, we can just delete the file and once there are translations, an updated pot file together with translations can be imported. Change-Id: I888808f8af291223531df799382ad0c70bf8c567 --- .../locale/python-neutronclient.pot | 1768 ----------------- setup.cfg | 12 +- 2 files changed, 6 insertions(+), 1774 deletions(-) delete mode 100644 python-neutronclient/locale/python-neutronclient.pot diff --git a/python-neutronclient/locale/python-neutronclient.pot b/python-neutronclient/locale/python-neutronclient.pot deleted file mode 100644 index 94216dd25..000000000 --- a/python-neutronclient/locale/python-neutronclient.pot +++ /dev/null @@ -1,1768 +0,0 @@ -# Translations template for python-neutronclient. -# Copyright (C) 2015 ORGANIZATION -# This file is distributed under the same license as the -# python-neutronclient project. -# FIRST AUTHOR , 2015. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: python-neutronclient 3.1.1.dev64\n" -"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2015-12-03 21:54+0900\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.1.1\n" - -#: neutronclient/client.py:230 -msgid "" -"For \"noauth\" authentication strategy, the endpoint must be specified " -"either in the constructor or using --os-url" -msgstr "" - -#: neutronclient/client.py:241 -#, python-format -msgid "Unknown auth strategy: %s" -msgstr "" - -#: neutronclient/shell.py:136 -#, python-format -msgid "invalid int value: %r" -msgstr "" - -#: neutronclient/shell.py:138 -#, python-format -msgid "input value %d is negative" -msgstr "" - -#: neutronclient/shell.py:410 -#, python-format -msgid "" -"\n" -"Commands for API v%s:\n" -msgstr "" - -#: neutronclient/shell.py:475 -msgid "" -"Increase verbosity of output and show tracebacks on errors. You can " -"repeat this option." -msgstr "" - -#: neutronclient/shell.py:482 -msgid "Suppress output except warnings and errors." -msgstr "" - -#: neutronclient/shell.py:488 -msgid "Show this help message and exit." -msgstr "" - -#: neutronclient/shell.py:494 -msgid "" -"How many times the request to the Neutron server should be retried if it " -"fails." -msgstr "" - -#: neutronclient/shell.py:514 -msgid "Defaults to env[OS_NETWORK_SERVICE_TYPE] or network." -msgstr "" - -#: neutronclient/shell.py:519 -msgid "Defaults to env[OS_ENDPOINT_TYPE] or public." -msgstr "" - -#: neutronclient/shell.py:526 -msgid "DEPRECATED! Use --os-service-type." -msgstr "" - -#: neutronclient/shell.py:533 -msgid "DEPRECATED! Use --os-endpoint-type." -msgstr "" - -#: neutronclient/shell.py:538 -msgid "DEPRECATED! Only keystone is supported." -msgstr "" - -#: neutronclient/shell.py:547 -msgid "Defaults to env[OS_CLOUD]." -msgstr "" - -#: neutronclient/shell.py:552 -msgid "Authentication URL, defaults to env[OS_AUTH_URL]." -msgstr "" - -#: neutronclient/shell.py:561 -msgid "Authentication tenant name, defaults to env[OS_TENANT_NAME]." -msgstr "" - -#: neutronclient/shell.py:580 -msgid "Authentication tenant ID, defaults to env[OS_TENANT_ID]." -msgstr "" - -#: neutronclient/shell.py:594 -msgid "Authentication username, defaults to env[OS_USERNAME]." -msgstr "" - -#: neutronclient/shell.py:602 -msgid "Authentication user ID (Env: OS_USER_ID)" -msgstr "" - -#: neutronclient/shell.py:654 -msgid "" -"Path of certificate file to use in SSL connection. This file can " -"optionally be prepended with the private key. Defaults to env[OS_CERT]." -msgstr "" - -#: neutronclient/shell.py:663 -msgid "" -"Specify a CA bundle file to use in verifying a TLS (https) server " -"certificate. Defaults to env[OS_CACERT]." -msgstr "" - -#: neutronclient/shell.py:671 -msgid "" -"Path of client key to use in SSL connection. This option is not necessary" -" if your key is prepended to your certificate file. Defaults to " -"env[OS_KEY]." -msgstr "" - -#: neutronclient/shell.py:679 -msgid "Authentication password, defaults to env[OS_PASSWORD]." -msgstr "" - -#: neutronclient/shell.py:687 -msgid "Authentication region name, defaults to env[OS_REGION_NAME]." -msgstr "" - -#: neutronclient/shell.py:696 -msgid "Authentication token, defaults to env[OS_TOKEN]." -msgstr "" - -#: neutronclient/shell.py:704 -msgid "" -"Timeout in seconds to wait for an HTTP response. Defaults to " -"env[OS_NETWORK_TIMEOUT] or None if not specified." -msgstr "" - -#: neutronclient/shell.py:710 -msgid "Defaults to env[OS_URL]." -msgstr "" - -#: neutronclient/shell.py:719 -msgid "" -"Explicitly allow neutronclient to perform \"insecure\" SSL (https) " -"requests. The server's certificate will not be verified against any " -"certificate authorities. This option should be used with caution." -msgstr "" - -#: neutronclient/shell.py:821 -#, python-format -msgid "Try 'neutron help %s' for more information." -msgstr "" - -#: neutronclient/common/exceptions.py:39 -msgid "An unknown exception occurred." -msgstr "" - -#: neutronclient/common/exceptions.py:78 -msgid "Unauthorized: bad credentials." -msgstr "" - -#: neutronclient/common/exceptions.py:83 -msgid "Forbidden: your credentials don't give you access to this resource." -msgstr "" - -#: neutronclient/common/exceptions.py:170 -msgid "auth_url was not provided to the Neutron client" -msgstr "" - -#: neutronclient/common/exceptions.py:174 -msgid "Could not find Service or Region in Service Catalog." -msgstr "" - -#: neutronclient/common/exceptions.py:178 -#, python-format -msgid "Could not find endpoint type %(type_)s in Service Catalog." -msgstr "" - -#: neutronclient/common/exceptions.py:182 -msgid "" -"Found more than one matching endpoint in Service Catalog: " -"%(matching_endpoints)" -msgstr "" - -#: neutronclient/common/exceptions.py:195 -#, python-format -msgid "Connection to neutron failed: %(reason)s" -msgstr "" - -#: neutronclient/common/exceptions.py:199 -#, python-format -msgid "SSL certificate validation has failed: %(reason)s" -msgstr "" - -#: neutronclient/common/exceptions.py:203 -#, python-format -msgid "Malformed response body: %(reason)s" -msgstr "" - -#: neutronclient/common/exceptions.py:207 -#, python-format -msgid "Invalid content type %(content_type)s." -msgstr "" - -#: neutronclient/common/exceptions.py:229 -#, python-format -msgid "" -"Multiple %(resource)s matches found for name '%(name)s', use an ID to be " -"more specific." -msgstr "" - -#: neutronclient/common/serializer.py:223 -msgid "Cannot understand JSON" -msgstr "" - -#: neutronclient/common/serializer.py:296 -msgid "Cannot understand XML" -msgstr "" - -#: neutronclient/common/utils.py:56 -#, python-format -msgid "" -"Invalid %(api_name)s client version '%(version)s'. must be one of: " -"%(map_keys)s" -msgstr "" - -#: neutronclient/common/utils.py:113 -#, python-format -msgid "invalid key-value '%s', expected format: key=value" -msgstr "" - -#: neutronclient/common/validators.py:38 -#, python-format -msgid "%(attr_name)s \"%(val)s\" should be an integer [%(min)i:%(max)i]." -msgstr "" - -#: neutronclient/common/validators.py:43 -#, python-format -msgid "" -"%(attr_name)s \"%(val)s\" should be an integer greater than or equal to " -"%(min)i." -msgstr "" - -#: neutronclient/common/validators.py:48 -#, python-format -msgid "" -"%(attr_name)s \"%(val)s\" should be an integer smaller than or equal to " -"%(max)i." -msgstr "" - -#: neutronclient/common/validators.py:53 -#, python-format -msgid "%(attr_name)s \"%(val)s\" should be an integer." -msgstr "" - -#: neutronclient/common/validators.py:68 -#, python-format -msgid "%(attr_name)s \"%(val)s\" is not a valid CIDR." -msgstr "" - -#: neutronclient/neutron/client.py:56 -#, python-format -msgid "API version %s is not supported" -msgstr "" - -#: neutronclient/neutron/v2_0/__init__.py:69 -#, python-format -msgid "Unable to find %(resource)s with id '%(id)s'" -msgstr "" - -#: neutronclient/neutron/v2_0/__init__.py:106 -#, python-format -msgid "Unable to find %(resource)s with name '%(name)s'" -msgstr "" - -#: neutronclient/neutron/v2_0/__init__.py:139 -msgid "Show detailed information." -msgstr "" - -#: neutronclient/neutron/v2_0/__init__.py:154 -msgid "Specify the field(s) to be returned by server. You can repeat this option." -msgstr "" - -#: neutronclient/neutron/v2_0/__init__.py:164 -msgid "" -"Specify retrieve unit of each request, then split one request to several " -"requests." -msgstr "" - -#: neutronclient/neutron/v2_0/__init__.py:174 -msgid "" -"Sorts the list by the specified fields in the specified directions. You " -"can repeat this option, but you must specify an equal number of sort_dir " -"and sort_key values. Extra sort_dir options are ignored. Missing sort_dir" -" options use the default asc value." -msgstr "" - -#: neutronclient/neutron/v2_0/__init__.py:183 -msgid "Sorts the list in the specified direction. You can repeat this option." -msgstr "" - -#: neutronclient/neutron/v2_0/__init__.py:209 -#: neutronclient/neutron/v2_0/__init__.py:287 -#: neutronclient/neutron/v2_0/__init__.py:309 -#: neutronclient/neutron/v2_0/__init__.py:314 -#, python-format -msgid "Invalid values_specs %s" -msgstr "" - -#: neutronclient/neutron/v2_0/__init__.py:279 -#, python-format -msgid "Duplicated options %s" -msgstr "" - -#: neutronclient/neutron/v2_0/__init__.py:429 -msgid "The XML or JSON request format." -msgstr "" - -#: neutronclient/neutron/v2_0/__init__.py:478 -#: neutronclient/neutron/v2_0/quota.py:47 -#: neutronclient/neutron/v2_0/quota.py:108 -#: neutronclient/neutron/v2_0/quota.py:152 -msgid "The owner tenant ID." -msgstr "" - -#: neutronclient/neutron/v2_0/__init__.py:504 -#, python-format -msgid "Created a new %s:" -msgstr "" - -#: neutronclient/neutron/v2_0/__init__.py:521 -#, python-format -msgid "ID or name of %s to update." -msgstr "" - -#: neutronclient/neutron/v2_0/__init__.py:523 -#, python-format -msgid "ID of %s to update." -msgstr "" - -#: neutronclient/neutron/v2_0/__init__.py:545 -#, python-format -msgid "Must specify new values to update %s" -msgstr "" - -#: neutronclient/neutron/v2_0/__init__.py:561 -#, python-format -msgid "Updated %(resource)s: %(id)s" -msgstr "" - -#: neutronclient/neutron/v2_0/__init__.py:577 -#, python-format -msgid "ID or name of %s to delete." -msgstr "" - -#: neutronclient/neutron/v2_0/__init__.py:579 -#, python-format -msgid "ID of %s to delete." -msgstr "" - -#: neutronclient/neutron/v2_0/__init__.py:607 -#, python-format -msgid "Deleted %(resource)s: %(id)s" -msgstr "" - -#: neutronclient/neutron/v2_0/__init__.py:732 -#, python-format -msgid "ID or name of %s to look up." -msgstr "" - -#: neutronclient/neutron/v2_0/__init__.py:734 -#, python-format -msgid "ID of %s to look up." -msgstr "" - -#: neutronclient/neutron/v2_0/address_scope.py:45 -msgid "Set the address scope as shared." -msgstr "" - -#: neutronclient/neutron/v2_0/address_scope.py:48 -msgid "Specify the name of the address scope." -msgstr "" - -#: neutronclient/neutron/v2_0/address_scope.py:71 -msgid "Name of the address scope to update." -msgstr "" - -#: neutronclient/neutron/v2_0/agent.py:69 -msgid "Set admin state up of the agent to false." -msgstr "" - -#: neutronclient/neutron/v2_0/agent.py:72 -msgid "Description for the agent." -msgstr "" - -#: neutronclient/neutron/v2_0/agentscheduler.py:35 -#: neutronclient/neutron/v2_0/agentscheduler.py:60 -#: neutronclient/neutron/v2_0/agentscheduler.py:88 -msgid "ID of the DHCP agent." -msgstr "" - -#: neutronclient/neutron/v2_0/agentscheduler.py:38 -msgid "Network to add." -msgstr "" - -#: neutronclient/neutron/v2_0/agentscheduler.py:49 -#, python-format -msgid "Added network %s to DHCP agent" -msgstr "" - -#: neutronclient/neutron/v2_0/agentscheduler.py:63 -msgid "Network to remove." -msgstr "" - -#: neutronclient/neutron/v2_0/agentscheduler.py:74 -#, python-format -msgid "Removed network %s from DHCP agent" -msgstr "" - -#: neutronclient/neutron/v2_0/agentscheduler.py:110 -msgid "Network to query." -msgstr "" - -#: neutronclient/neutron/v2_0/agentscheduler.py:133 -#: neutronclient/neutron/v2_0/agentscheduler.py:158 -msgid "ID of the L3 agent." -msgstr "" - -#: neutronclient/neutron/v2_0/agentscheduler.py:136 -msgid "Router to add." -msgstr "" - -#: neutronclient/neutron/v2_0/agentscheduler.py:147 -#, python-format -msgid "Added router %s to L3 agent" -msgstr "" - -#: neutronclient/neutron/v2_0/agentscheduler.py:161 -msgid "Router to remove." -msgstr "" - -#: neutronclient/neutron/v2_0/agentscheduler.py:172 -#, python-format -msgid "Removed router %s from L3 agent" -msgstr "" - -#: neutronclient/neutron/v2_0/agentscheduler.py:190 -msgid "ID of the L3 agent to query." -msgstr "" - -#: neutronclient/neutron/v2_0/agentscheduler.py:211 -msgid "Router to query." -msgstr "" - -#: neutronclient/neutron/v2_0/agentscheduler.py:243 -#: neutronclient/neutron/v2_0/agentscheduler.py:296 -msgid "ID of the loadbalancer agent to query." -msgstr "" - -#: neutronclient/neutron/v2_0/agentscheduler.py:267 -msgid "Pool to query." -msgstr "" - -#: neutronclient/neutron/v2_0/agentscheduler.py:320 -msgid "LoadBalancer to query." -msgstr "" - -#: neutronclient/neutron/v2_0/floatingip.py:50 -msgid "Network name or ID to allocate floating IP from." -msgstr "" - -#: neutronclient/neutron/v2_0/floatingip.py:53 -msgid "ID of the port to be associated with the floating IP." -msgstr "" - -#: neutronclient/neutron/v2_0/floatingip.py:59 -#: neutronclient/neutron/v2_0/floatingip.py:107 -msgid "IP address on the port (only required if port has multiple IPs)." -msgstr "" - -#: neutronclient/neutron/v2_0/floatingip.py:66 -msgid "IP address of the floating IP" -msgstr "" - -#: neutronclient/neutron/v2_0/floatingip.py:70 -msgid "Subnet ID on which you want to create the floating IP." -msgstr "" - -#: neutronclient/neutron/v2_0/floatingip.py:100 -msgid "ID of the floating IP to associate." -msgstr "" - -#: neutronclient/neutron/v2_0/floatingip.py:103 -msgid "ID or name of the port to be associated with the floating IP." -msgstr "" - -#: neutronclient/neutron/v2_0/floatingip.py:123 -#, python-format -msgid "Associated floating IP %s" -msgstr "" - -#: neutronclient/neutron/v2_0/floatingip.py:138 -msgid "ID of the floating IP to disassociate." -msgstr "" - -#: neutronclient/neutron/v2_0/floatingip.py:147 -#, python-format -msgid "Disassociated floating IP %s" -msgstr "" - -#: neutronclient/neutron/v2_0/metering.py:43 -msgid "Name of metering label to create." -msgstr "" - -#: neutronclient/neutron/v2_0/metering.py:46 -msgid "Description of metering label to create." -msgstr "" - -#: neutronclient/neutron/v2_0/metering.py:50 -msgid "Set the label as shared." -msgstr "" - -#: neutronclient/neutron/v2_0/metering.py:89 -msgid "Id or Name of the label." -msgstr "" - -#: neutronclient/neutron/v2_0/metering.py:92 -#: neutronclient/neutron/v2_0/securitygroup.py:330 -msgid "CIDR to match on." -msgstr "" - -#: neutronclient/neutron/v2_0/metering.py:96 -msgid "Direction of traffic, default: ingress." -msgstr "" - -#: neutronclient/neutron/v2_0/metering.py:100 -msgid "Exclude this CIDR from the label, default: not excluded." -msgstr "" - -#: neutronclient/neutron/v2_0/network.py:114 -#: neutronclient/neutron/v2_0/port.py:230 -#: neutronclient/neutron/v2_0/router.py:62 -#: neutronclient/neutron/v2_0/fw/firewall.py:56 -#: neutronclient/neutron/v2_0/lb/healthmonitor.py:48 -#: neutronclient/neutron/v2_0/lb/member.py:48 -#: neutronclient/neutron/v2_0/lb/pool.py:54 -#: neutronclient/neutron/v2_0/lb/vip.py:52 -#: neutronclient/neutron/v2_0/lb/v2/healthmonitor.py:49 -#: neutronclient/neutron/v2_0/lb/v2/listener.py:55 -#: neutronclient/neutron/v2_0/lb/v2/loadbalancer.py:51 -#: neutronclient/neutron/v2_0/lb/v2/pool.py:60 -#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:98 -#: neutronclient/neutron/v2_0/vpn/vpnservice.py:47 -msgid "Set admin state up to false." -msgstr "" - -#: neutronclient/neutron/v2_0/network.py:122 -msgid "Set the network as shared." -msgstr "" - -#: neutronclient/neutron/v2_0/network.py:127 -msgid "The physical mechanism by which the virtual network is implemented." -msgstr "" - -#: neutronclient/neutron/v2_0/network.py:132 -msgid "" -"Name of the physical network over which the virtual network is " -"implemented." -msgstr "" - -#: neutronclient/neutron/v2_0/network.py:137 -msgid "VLAN ID for VLAN networks or tunnel-id for GRE/VXLAN networks." -msgstr "" - -#: neutronclient/neutron/v2_0/network.py:143 -msgid "Create a vlan transparent network." -msgstr "" - -#: neutronclient/neutron/v2_0/network.py:146 -msgid "Name of network to create." -msgstr "" - -#: neutronclient/neutron/v2_0/port.py:45 -msgid "Name of this port." -msgstr "" - -#: neutronclient/neutron/v2_0/port.py:49 -msgid "" -"Desired IP and/or subnet for this port: " -"subnet_id=,ip_address=. You can repeat this option." -msgstr "" - -#: neutronclient/neutron/v2_0/port.py:58 -msgid "Device ID of this port." -msgstr "" - -#: neutronclient/neutron/v2_0/port.py:64 -msgid "Device owner of this port." -msgstr "" - -#: neutronclient/neutron/v2_0/port.py:111 -msgid "ID or name of router to look up." -msgstr "" - -#: neutronclient/neutron/v2_0/port.py:135 -msgid "Security group associated with the port. You can repeat this option." -msgstr "" - -#: neutronclient/neutron/v2_0/port.py:140 -msgid "Associate no security groups with the port." -msgstr "" - -#: neutronclient/neutron/v2_0/port.py:162 -msgid "" -"Extra dhcp options to be assigned to this port: " -"opt_name=,opt_value=,ip_version={4,6}. You can " -"repeat this option." -msgstr "" - -#: neutronclient/neutron/v2_0/port.py:173 -msgid "" -"Invalid --extra-dhcp-opt option, can only be: " -"opt_name=,opt_value=,ip_version={4,6}. You can " -"repeat this option." -msgstr "" - -#: neutronclient/neutron/v2_0/port.py:204 -msgid "Allowed address pair associated with the port.You can repeat this option." -msgstr "" - -#: neutronclient/neutron/v2_0/port.py:209 -msgid "Associate no allowed address pairs with the port." -msgstr "" - -#: neutronclient/neutron/v2_0/port.py:237 -msgid "MAC address of this port." -msgstr "" - -#: neutronclient/neutron/v2_0/port.py:244 -msgid "VNIC type for this port." -msgstr "" - -#: neutronclient/neutron/v2_0/port.py:251 -msgid "Custom data to be passed as binding:profile." -msgstr "" - -#: neutronclient/neutron/v2_0/port.py:262 -msgid "Network ID or name this port belongs to." -msgstr "" - -#: neutronclient/neutron/v2_0/port.py:305 -msgid "Set admin state up for the port." -msgstr "" - -#: neutronclient/neutron/v2_0/quota.py:62 -#, python-format -msgid "Deleted %(resource)s: %(tenant_id)s" -msgstr "" - -#: neutronclient/neutron/v2_0/quota.py:158 -msgid "The limit of networks." -msgstr "" - -#: neutronclient/neutron/v2_0/quota.py:161 -msgid "The limit of subnets." -msgstr "" - -#: neutronclient/neutron/v2_0/quota.py:164 -msgid "The limit of ports." -msgstr "" - -#: neutronclient/neutron/v2_0/quota.py:167 -msgid "The limit of routers." -msgstr "" - -#: neutronclient/neutron/v2_0/quota.py:170 -msgid "The limit of floating IPs." -msgstr "" - -#: neutronclient/neutron/v2_0/quota.py:173 -msgid "The limit of security groups." -msgstr "" - -#: neutronclient/neutron/v2_0/quota.py:176 -msgid "The limit of security groups rules." -msgstr "" - -#: neutronclient/neutron/v2_0/quota.py:179 -msgid "The limit of vips." -msgstr "" - -#: neutronclient/neutron/v2_0/quota.py:182 -msgid "The limit of pools." -msgstr "" - -#: neutronclient/neutron/v2_0/quota.py:185 -msgid "The limit of pool members." -msgstr "" - -#: neutronclient/neutron/v2_0/quota.py:188 -msgid "The limit of health monitors." -msgstr "" - -#: neutronclient/neutron/v2_0/quota.py:196 -#, python-format -msgid "Quota limit for %(name)s must be an integer" -msgstr "" - -#: neutronclient/neutron/v2_0/rbac.py:54 -msgid "ID or name of the RBAC object." -msgstr "" - -#: neutronclient/neutron/v2_0/rbac.py:58 -msgid "Type of the object that RBAC policy affects." -msgstr "" - -#: neutronclient/neutron/v2_0/rbac.py:61 neutronclient/neutron/v2_0/rbac.py:91 -msgid "ID of the tenant to which the RBAC policy will be enforced." -msgstr "" - -#: neutronclient/neutron/v2_0/rbac.py:66 -msgid "Action for the RBAC policy." -msgstr "" - -#: neutronclient/neutron/v2_0/router.py:69 -msgid "Name of router to create." -msgstr "" - -#: neutronclient/neutron/v2_0/router.py:72 -msgid "Create a distributed router." -msgstr "" - -#: neutronclient/neutron/v2_0/router.py:75 -msgid "Create a highly available router." -msgstr "" - -#: neutronclient/neutron/v2_0/router.py:98 -msgid "Name of this router." -msgstr "" - -#: neutronclient/neutron/v2_0/router.py:101 -msgid "Specify the administrative state of the router (True meaning \"Up\")" -msgstr "" - -#: neutronclient/neutron/v2_0/router.py:108 -msgid "True means this router should operate in distributed mode." -msgstr "" - -#: neutronclient/neutron/v2_0/router.py:114 -msgid "Route to associate with the router. You can repeat this option." -msgstr "" - -#: neutronclient/neutron/v2_0/router.py:119 -msgid "Remove routes associated with the router." -msgstr "" - -#: neutronclient/neutron/v2_0/router.py:150 -#: neutronclient/neutron/v2_0/router.py:218 -#: neutronclient/neutron/v2_0/router.py:269 -msgid "ID or name of the router." -msgstr "" - -#: neutronclient/neutron/v2_0/router.py:153 -msgid "" -"The format is \"SUBNET|subnet=SUBNET|port=PORT\". Either a subnet or port" -" must be specified. Both ID and name are accepted as SUBNET or PORT. Note" -" that \"subnet=\" can be omitted when specifying a subnet." -msgstr "" - -#: neutronclient/neutron/v2_0/router.py:168 -msgid "You must specify either subnet or port for INTERFACE parameter." -msgstr "" - -#: neutronclient/neutron/v2_0/router.py:193 -#, python-format -msgid "Added interface %(port)s to router %(router)s." -msgstr "" - -#: neutronclient/neutron/v2_0/router.py:205 -#, python-format -msgid "Removed interface from router %s." -msgstr "" - -#: neutronclient/neutron/v2_0/router.py:221 -msgid "ID or name of the external network for the gateway." -msgstr "" - -#: neutronclient/neutron/v2_0/router.py:224 -msgid "Disable source NAT on the router gateway." -msgstr "" - -#: neutronclient/neutron/v2_0/router.py:227 -msgid "" -"Desired IP and/or subnet on external network: " -"subnet_id=,ip_address=. You can repeat this option." -msgstr "" - -#: neutronclient/neutron/v2_0/router.py:255 -#, python-format -msgid "Set gateway for router %s" -msgstr "" - -#: neutronclient/neutron/v2_0/router.py:279 -#, python-format -msgid "Removed gateway from router %s" -msgstr "" - -#: neutronclient/neutron/v2_0/securitygroup.py:126 -#: neutronclient/neutron/v2_0/securitygroup.py:153 -msgid "Name of security group." -msgstr "" - -#: neutronclient/neutron/v2_0/securitygroup.py:129 -#: neutronclient/neutron/v2_0/securitygroup.py:156 -msgid "Description of security group." -msgstr "" - -#: neutronclient/neutron/v2_0/securitygroup.py:189 -msgid "Do not convert security group ID to its name." -msgstr "" - -#: neutronclient/neutron/v2_0/securitygroup.py:305 -msgid "Security group name or ID to add rule." -msgstr "" - -#: neutronclient/neutron/v2_0/securitygroup.py:309 -msgid "Direction of traffic: ingress/egress." -msgstr "" - -#: neutronclient/neutron/v2_0/securitygroup.py:312 -msgid "IPv4/IPv6" -msgstr "" - -#: neutronclient/neutron/v2_0/securitygroup.py:315 -msgid "Protocol of packet." -msgstr "" - -#: neutronclient/neutron/v2_0/securitygroup.py:318 -msgid "Starting port range." -msgstr "" - -#: neutronclient/neutron/v2_0/securitygroup.py:324 -msgid "Ending port range." -msgstr "" - -#: neutronclient/neutron/v2_0/securitygroup.py:336 -msgid "Remote security group name or ID to apply rule." -msgstr "" - -#: neutronclient/neutron/v2_0/subnet.py:54 -msgid "Name of this subnet." -msgstr "" - -#: neutronclient/neutron/v2_0/subnet.py:58 -msgid "Gateway IP of this subnet." -msgstr "" - -#: neutronclient/neutron/v2_0/subnet.py:62 -msgid "No distribution of gateway." -msgstr "" - -#: neutronclient/neutron/v2_0/subnet.py:66 -msgid "" -"Allocation pool IP addresses for this subnet (This option can be " -"repeated)." -msgstr "" - -#: neutronclient/neutron/v2_0/subnet.py:75 -msgid "Additional route (This option can be repeated)." -msgstr "" - -#: neutronclient/neutron/v2_0/subnet.py:79 -msgid "DNS name server for this subnet (This option can be repeated)." -msgstr "" - -#: neutronclient/neutron/v2_0/subnet.py:84 -msgid "Disable DHCP for this subnet." -msgstr "" - -#: neutronclient/neutron/v2_0/subnet.py:88 -msgid "Enable DHCP for this subnet." -msgstr "" - -#: neutronclient/neutron/v2_0/subnet.py:107 -msgid "You cannot enable and disable DHCP at the same time." -msgstr "" - -#: neutronclient/neutron/v2_0/subnet.py:123 -msgid "--ipv6-ra-mode is invalid when --ip-version is 4" -msgstr "" - -#: neutronclient/neutron/v2_0/subnet.py:128 -msgid "--ipv6-address-mode is invalid when --ip-version is 4" -msgstr "" - -#: neutronclient/neutron/v2_0/subnet.py:163 -msgid "" -"IP version to use, default is 4. Note that when subnetpool is specified, " -"IP version is determined from the subnetpool and this option is ignored." -msgstr "" - -#: neutronclient/neutron/v2_0/subnet.py:174 -msgid "Network ID or name this subnet belongs to." -msgstr "" - -#: neutronclient/neutron/v2_0/subnet.py:177 -msgid "CIDR of subnet to create." -msgstr "" - -#: neutronclient/neutron/v2_0/subnet.py:181 -msgid "IPv6 RA (Router Advertisement) mode." -msgstr "" - -#: neutronclient/neutron/v2_0/subnet.py:185 -msgid "IPv6 address mode." -msgstr "" - -#: neutronclient/neutron/v2_0/subnet.py:188 -msgid "ID or name of subnetpool from which this subnet will obtain a CIDR." -msgstr "" - -#: neutronclient/neutron/v2_0/subnet.py:192 -msgid "Prefix length for subnet allocation from subnetpool." -msgstr "" - -#: neutronclient/neutron/v2_0/subnet.py:224 -#, python-format -msgid "" -"An IPv%(ip)d subnet with a %(cidr)s CIDR will have only one usable IP " -"address so the device attached to it will not have any IP connectivity." -msgstr "" - -#: neutronclient/neutron/v2_0/subnetpool.py:24 -msgid "Subnetpool minimum prefix length." -msgstr "" - -#: neutronclient/neutron/v2_0/subnetpool.py:27 -msgid "Subnetpool maximum prefix length." -msgstr "" - -#: neutronclient/neutron/v2_0/subnetpool.py:30 -msgid "Subnetpool default prefix length." -msgstr "" - -#: neutronclient/neutron/v2_0/subnetpool.py:34 -msgid "Subnetpool prefixes (This option can be repeated)." -msgstr "" - -#: neutronclient/neutron/v2_0/subnetpool.py:69 -msgid "Set the subnetpool as shared." -msgstr "" - -#: neutronclient/neutron/v2_0/subnetpool.py:72 -msgid "Name of subnetpool to create." -msgstr "" - -#: neutronclient/neutron/v2_0/subnetpool.py:76 -#: neutronclient/neutron/v2_0/subnetpool.py:113 -msgid "" -"ID or name of the address scope with which the subnetpool is associated. " -"Prefixes must be unique across address scopes" -msgstr "" - -#: neutronclient/neutron/v2_0/subnetpool.py:109 -msgid "Name of subnetpool to update." -msgstr "" - -#: neutronclient/neutron/v2_0/subnetpool.py:119 -msgid "Detach subnetpool from the address scope" -msgstr "" - -#: neutronclient/neutron/v2_0/contrib/_fox_sockets.py:24 -#: neutronclient/neutron/v2_0/contrib/_fox_sockets.py:77 -msgid "Name of this fox socket." -msgstr "" - -#: neutronclient/neutron/v2_0/flavor/flavor.py:49 -#: neutronclient/neutron/v2_0/flavor/flavor.py:86 -msgid "Name for the flavor." -msgstr "" - -#: neutronclient/neutron/v2_0/flavor/flavor.py:53 -msgid "" -"Service type to which the flavor applies to: e.g. VPN. (See service-" -"provider-list for loaded examples.)" -msgstr "" - -#: neutronclient/neutron/v2_0/flavor/flavor.py:57 -#: neutronclient/neutron/v2_0/flavor/flavor.py:89 -msgid "Description for the flavor." -msgstr "" - -#: neutronclient/neutron/v2_0/flavor/flavor.py:62 -#: neutronclient/neutron/v2_0/flavor/flavor.py:94 -#: neutronclient/neutron/v2_0/flavor/flavor_profile.py:57 -#: neutronclient/neutron/v2_0/flavor/flavor_profile.py:92 -msgid "Sets enabled flag." -msgstr "" - -#: neutronclient/neutron/v2_0/flavor/flavor.py:113 -msgid "Name or ID of the flavor to associate." -msgstr "" - -#: neutronclient/neutron/v2_0/flavor/flavor.py:117 -msgid "ID of the flavor profile to be associated with the flavor." -msgstr "" - -#: neutronclient/neutron/v2_0/flavor/flavor.py:130 -#, python-format -msgid "Associated flavor %(flavor)s with flavor_profile %(profile)s" -msgstr "" - -#: neutronclient/neutron/v2_0/flavor/flavor.py:147 -msgid "Name or ID of the flavor." -msgstr "" - -#: neutronclient/neutron/v2_0/flavor/flavor.py:151 -msgid "ID of the flavor profile to be disassociated from the flavor." -msgstr "" - -#: neutronclient/neutron/v2_0/flavor/flavor.py:163 -#, python-format -msgid "Disassociated flavor %(flavor)s from flavor_profile %(profile)s" -msgstr "" - -#: neutronclient/neutron/v2_0/flavor/flavor_profile.py:46 -#: neutronclient/neutron/v2_0/flavor/flavor_profile.py:81 -msgid "Description for the flavor profile." -msgstr "" - -#: neutronclient/neutron/v2_0/flavor/flavor_profile.py:49 -#: neutronclient/neutron/v2_0/flavor/flavor_profile.py:84 -msgid "Python module path to driver." -msgstr "" - -#: neutronclient/neutron/v2_0/flavor/flavor_profile.py:52 -#: neutronclient/neutron/v2_0/flavor/flavor_profile.py:87 -msgid "Metainfo for the flavor profile." -msgstr "" - -#: neutronclient/neutron/v2_0/fw/firewall.py:45 -#: neutronclient/neutron/v2_0/fw/firewall.py:89 -msgid "Firewall policy name or ID." -msgstr "" - -#: neutronclient/neutron/v2_0/fw/firewall.py:48 -msgid "Name for the firewall." -msgstr "" - -#: neutronclient/neutron/v2_0/fw/firewall.py:51 -#: neutronclient/neutron/v2_0/fw/firewallrule.py:77 -msgid "Description for the firewall rule." -msgstr "" - -#: neutronclient/neutron/v2_0/fw/firewall.py:62 -#: neutronclient/neutron/v2_0/fw/firewall.py:96 -msgid "" -"Firewall associated router names or IDs (requires FWaaS router insertion " -"extension, this option can be repeated)" -msgstr "" - -#: neutronclient/neutron/v2_0/fw/firewall.py:101 -msgid "" -"Associate no routers with the firewall (requires FWaaS router insertion " -"extension)" -msgstr "" - -#: neutronclient/neutron/v2_0/fw/firewallpolicy.py:37 -msgid "" -"Ordered list of whitespace-delimited firewall rule names or IDs; e.g., " -"--firewall-rules \"rule1 rule2\"" -msgstr "" - -#: neutronclient/neutron/v2_0/fw/firewallpolicy.py:83 -msgid "Name for the firewall policy." -msgstr "" - -#: neutronclient/neutron/v2_0/fw/firewallpolicy.py:86 -msgid "Description for the firewall policy." -msgstr "" - -#: neutronclient/neutron/v2_0/fw/firewallpolicy.py:91 -msgid "Create a shared policy." -msgstr "" - -#: neutronclient/neutron/v2_0/fw/firewallpolicy.py:97 -msgid "Sets audited to True." -msgstr "" - -#: neutronclient/neutron/v2_0/fw/firewallpolicy.py:159 -msgid "Insert before this rule." -msgstr "" - -#: neutronclient/neutron/v2_0/fw/firewallpolicy.py:163 -msgid "Insert after this rule." -msgstr "" - -#: neutronclient/neutron/v2_0/fw/firewallpolicy.py:167 -msgid "New rule to insert." -msgstr "" - -#: neutronclient/neutron/v2_0/fw/firewallpolicy.py:179 -#, python-format -msgid "Inserted firewall rule in firewall policy %(id)s" -msgstr "" - -#: neutronclient/neutron/v2_0/fw/firewallpolicy.py:206 -msgid "Firewall rule to remove from policy." -msgstr "" - -#: neutronclient/neutron/v2_0/fw/firewallpolicy.py:218 -#, python-format -msgid "Removed firewall rule from firewall policy %(id)s" -msgstr "" - -#: neutronclient/neutron/v2_0/fw/firewallrule.py:74 -msgid "Name for the firewall rule." -msgstr "" - -#: neutronclient/neutron/v2_0/fw/firewallrule.py:82 -msgid "Set shared to True (default is False)." -msgstr "" - -#: neutronclient/neutron/v2_0/fw/firewallrule.py:86 -msgid "Source IP address or subnet." -msgstr "" - -#: neutronclient/neutron/v2_0/fw/firewallrule.py:89 -msgid "Destination IP address or subnet." -msgstr "" - -#: neutronclient/neutron/v2_0/fw/firewallrule.py:92 -msgid "Source port (integer in [1, 65535] or range in a:b)." -msgstr "" - -#: neutronclient/neutron/v2_0/fw/firewallrule.py:95 -msgid "Destination port (integer in [1, 65535] or range in a:b)." -msgstr "" - -#: neutronclient/neutron/v2_0/fw/firewallrule.py:99 -msgid "Whether to enable or disable this rule." -msgstr "" - -#: neutronclient/neutron/v2_0/fw/firewallrule.py:103 -#: neutronclient/neutron/v2_0/fw/firewallrule.py:133 -msgid "Protocol for the firewall rule." -msgstr "" - -#: neutronclient/neutron/v2_0/fw/firewallrule.py:108 -msgid "Action for the firewall rule." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/healthmonitor.py:51 -#: neutronclient/neutron/v2_0/lb/v2/healthmonitor.py:52 -msgid "" -"The list of HTTP status codes expected in response from the member to " -"declare it healthy. This attribute can contain one value, or a list of " -"values separated by comma, or a range of values (e.g. \"200-299\"). If " -"this attribute is not specified, it defaults to \"200\"." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/healthmonitor.py:59 -#: neutronclient/neutron/v2_0/lb/v2/healthmonitor.py:60 -msgid "The HTTP method used for requests by the monitor of type HTTP." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/healthmonitor.py:63 -#: neutronclient/neutron/v2_0/lb/v2/healthmonitor.py:64 -msgid "" -"The HTTP path used in the HTTP request used by the monitor to test a " -"member health. This must be a string beginning with a / (forward slash)." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/healthmonitor.py:69 -#: neutronclient/neutron/v2_0/lb/v2/healthmonitor.py:70 -msgid "The time in seconds between sending probes to members." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/healthmonitor.py:73 -msgid "" -"Number of permissible connection failures before changing the member " -"status to INACTIVE. [1..10]" -msgstr "" - -#: neutronclient/neutron/v2_0/lb/healthmonitor.py:78 -#: neutronclient/neutron/v2_0/lb/v2/healthmonitor.py:79 -msgid "" -"Maximum number of seconds for a monitor to wait for a connection to be " -"established before it times out. The value must be less than the delay " -"value." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/healthmonitor.py:84 -#: neutronclient/neutron/v2_0/lb/v2/healthmonitor.py:85 -msgid "One of the predefined health monitor types." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/healthmonitor.py:121 -#: neutronclient/neutron/v2_0/lb/healthmonitor.py:148 -msgid "Health monitor to associate." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/healthmonitor.py:124 -#: neutronclient/neutron/v2_0/lb/healthmonitor.py:151 -msgid "ID of the pool to be associated with the health monitor." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/healthmonitor.py:134 -#, python-format -msgid "Associated health monitor %s" -msgstr "" - -#: neutronclient/neutron/v2_0/lb/healthmonitor.py:162 -#, python-format -msgid "Disassociated health monitor %s" -msgstr "" - -#: neutronclient/neutron/v2_0/lb/member.py:51 -msgid "Weight of pool member in the pool (default:1, [0..256])." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/member.py:55 -msgid "IP address of the pool member on the pool network." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/member.py:59 -#: neutronclient/neutron/v2_0/lb/v2/member.py:90 -msgid "Port on which the pool member listens for requests or connections." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/member.py:63 -#: neutronclient/neutron/v2_0/lb/vip.py:45 -msgid "Pool ID or name this vip belongs to." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/pool.py:57 -#: neutronclient/neutron/v2_0/lb/v2/pool.py:63 -msgid "Description of the pool." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/pool.py:62 -#: neutronclient/neutron/v2_0/lb/v2/pool.py:75 -msgid "The algorithm used to distribute load between the members of the pool." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/pool.py:67 -#: neutronclient/neutron/v2_0/lb/v2/pool.py:70 -msgid "The name of the pool." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/pool.py:72 -#: neutronclient/neutron/v2_0/lb/vip.py:72 -#: neutronclient/neutron/v2_0/lb/v2/pool.py:85 -msgid "Protocol for balancing." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/pool.py:76 -msgid "The subnet on which the members of the pool will be located." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/pool.py:80 -msgid "Provider name of loadbalancer service." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/vip.py:48 -msgid "IP address of the vip." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/vip.py:55 -#: neutronclient/neutron/v2_0/lb/v2/listener.py:58 -msgid "" -"The maximum number of connections per second allowed for the vip. " -"Positive integer or -1 for unlimited (default)." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/vip.py:59 -msgid "Description of the vip." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/vip.py:63 -msgid "Name of the vip." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/vip.py:67 -msgid "" -"TCP port on which to listen for client traffic that is associated with " -"the vip address." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/vip.py:76 -msgid "The subnet on which to allocate the vip address." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/v2/healthmonitor.py:74 -msgid "" -"Number of permissible connection failures before changing the member " -"status to INACTIVE. [1..10]." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/v2/healthmonitor.py:88 -msgid "ID or name of the pool that this healthmonitor will monitor." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/v2/listener.py:62 -msgid "Description of the listener." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/v2/listener.py:65 -msgid "The name of the listener." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/v2/listener.py:69 -msgid "Default TLS container reference to retrieve TLS information." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/v2/listener.py:75 -msgid "List of TLS container references for SNI." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/v2/listener.py:80 -msgid "ID or name of the load balancer." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/v2/listener.py:85 -msgid "Protocol for the listener." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/v2/listener.py:90 -msgid "Protocol port for the listener." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/v2/loadbalancer.py:47 -msgid "Description of the load balancer." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/v2/loadbalancer.py:54 -msgid "Name of the load balancer." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/v2/loadbalancer.py:57 -msgid "Provider name of load balancer service." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/v2/loadbalancer.py:60 -msgid "ID or name of flavor." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/v2/loadbalancer.py:63 -msgid "VIP address for the load balancer." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/v2/loadbalancer.py:66 -msgid "Load balancer VIP subnet." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/v2/member.py:37 -#: neutronclient/neutron/v2_0/lb/v2/member.py:94 -msgid "ID or name of the pool that this member belongs to." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/v2/member.py:75 -#: neutronclient/neutron/v2_0/lb/v2/member.py:119 -msgid "Set admin state up to false" -msgstr "" - -#: neutronclient/neutron/v2_0/lb/v2/member.py:78 -msgid "Weight of member in the pool (default:1, [0..256])." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/v2/member.py:82 -msgid "Subnet ID or name for the member." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/v2/member.py:86 -msgid "IP address of the pool member in the pool." -msgstr "" - -#: neutronclient/neutron/v2_0/lb/v2/member.py:122 -msgid "Weight of member in the pool (default:1, [0..256])" -msgstr "" - -#: neutronclient/neutron/v2_0/lb/v2/member.py:125 -msgid "ID or name of the pool that this member belongs to" -msgstr "" - -#: neutronclient/neutron/v2_0/lb/v2/pool.py:67 -msgid "The type of session persistence to use and associated cookie name" -msgstr "" - -#: neutronclient/neutron/v2_0/lb/v2/pool.py:80 -msgid "The listener to associate with the pool" -msgstr "" - -#: neutronclient/neutron/v2_0/nsx/networkgateway.py:25 -msgid "" -"Type of the transport zone connector to use for this device. Valid values" -" are gre, stt, ipsec_gre, ipsec_stt, and bridge. Defaults to stt. " -"ipsecgre and ipsecstt are also accepted as alternative names" -msgstr "" - -#: neutronclient/neutron/v2_0/nsx/networkgateway.py:29 -msgid "" -"IP address for this device's transport connector. It must correspond to " -"the IP address of the interface used for tenant traffic on the NSX " -"gateway node." -msgstr "" - -#: neutronclient/neutron/v2_0/nsx/networkgateway.py:32 -msgid "" -"PEM certificate used by the NSX gateway transport node to authenticate " -"with the NSX controller." -msgstr "" - -#: neutronclient/neutron/v2_0/nsx/networkgateway.py:34 -msgid "" -"File containing the PEM certificate used by the NSX gateway transport " -"node to authenticate with the NSX controller." -msgstr "" - -#: neutronclient/neutron/v2_0/nsx/networkgateway.py:170 -msgid "Name of network gateway to create." -msgstr "" - -#: neutronclient/neutron/v2_0/nsx/networkgateway.py:174 -msgid "" -"Device info for this gateway. You can repeat this option for multiple " -"devices for HA gateways." -msgstr "" - -#: neutronclient/neutron/v2_0/nsx/networkgateway.py:212 -msgid "ID of the network gateway." -msgstr "" - -#: neutronclient/neutron/v2_0/nsx/networkgateway.py:215 -msgid "ID of the internal network to connect on the gateway." -msgstr "" - -#: neutronclient/neutron/v2_0/nsx/networkgateway.py:218 -msgid "" -"L2 segmentation strategy on the external side of the gateway (e.g.: VLAN," -" FLAT)." -msgstr "" - -#: neutronclient/neutron/v2_0/nsx/networkgateway.py:222 -msgid "Identifier for the L2 segment on the external side of the gateway." -msgstr "" - -#: neutronclient/neutron/v2_0/nsx/networkgateway.py:249 -#, python-format -msgid "Connected network to gateway %s" -msgstr "" - -#: neutronclient/neutron/v2_0/nsx/networkgateway.py:268 -#, python-format -msgid "Disconnected network from gateway %s" -msgstr "" - -#: neutronclient/neutron/v2_0/nsx/qos_queue.py:43 -msgid "Name of queue." -msgstr "" - -#: neutronclient/neutron/v2_0/nsx/qos_queue.py:46 -msgid "Minimum rate." -msgstr "" - -#: neutronclient/neutron/v2_0/nsx/qos_queue.py:49 -msgid "Maximum rate." -msgstr "" - -#: neutronclient/neutron/v2_0/nsx/qos_queue.py:52 -msgid "QOS marking as untrusted or trusted." -msgstr "" - -#: neutronclient/neutron/v2_0/nsx/qos_queue.py:56 -msgid "" -"If true all created ports will be the size of this queue, if queue is not" -" specified" -msgstr "" - -#: neutronclient/neutron/v2_0/nsx/qos_queue.py:60 -msgid "Differentiated Services Code Point." -msgstr "" - -#: neutronclient/neutron/v2_0/qos/bandwidth_limit_rule.py:30 -msgid "max bandwidth in kbps." -msgstr "" - -#: neutronclient/neutron/v2_0/qos/bandwidth_limit_rule.py:33 -msgid "max burst bandwidth in kbps." -msgstr "" - -#: neutronclient/neutron/v2_0/qos/bandwidth_limit_rule.py:40 -msgid "Must provide max_kbps or max_burst_kbps option." -msgstr "" - -#: neutronclient/neutron/v2_0/qos/policy.py:34 -msgid "Attach QoS policy ID or name to the resource." -msgstr "" - -#: neutronclient/neutron/v2_0/qos/policy.py:51 -msgid "Detach QoS policy from the resource." -msgstr "" - -#: neutronclient/neutron/v2_0/qos/policy.py:95 -msgid "Name of QoS policy to create." -msgstr "" - -#: neutronclient/neutron/v2_0/qos/policy.py:98 -#: neutronclient/neutron/v2_0/qos/policy.py:128 -msgid "Description of the QoS policy." -msgstr "" - -#: neutronclient/neutron/v2_0/qos/policy.py:102 -#: neutronclient/neutron/v2_0/qos/policy.py:132 -msgid "Accessible by other tenants. Set shared to True (default is False)." -msgstr "" - -#: neutronclient/neutron/v2_0/qos/policy.py:125 -msgid "Name of QoS policy." -msgstr "" - -#: neutronclient/neutron/v2_0/qos/rule.py:26 -msgid "ID or name of the QoS policy." -msgstr "" - -#: neutronclient/neutron/v2_0/qos/rule.py:32 -msgid "ID of the QoS rule." -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/endpoint_group.py:24 -msgid "Set a name for the endpoint group." -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/endpoint_group.py:27 -msgid "Set a description for the endpoint group." -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/endpoint_group.py:32 -msgid "Type of endpoints in group (e.g. subnet, cidr, vlan)." -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/endpoint_group.py:37 -msgid "Endpoint(s) for the group. Must all be of the same type." -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/ikepolicy.py:48 -msgid "Description of the IKE policy" -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/ikepolicy.py:52 -msgid "Authentication algorithm in lowercase. Default:sha1" -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/ikepolicy.py:57 -#: neutronclient/neutron/v2_0/vpn/ipsecpolicy.py:60 -msgid "Encryption algorithm in lowercase, default:aes-128" -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/ikepolicy.py:61 -msgid "IKE Phase1 negotiation mode in lowercase, default:main" -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/ikepolicy.py:65 -msgid "IKE version in lowercase, default:v1" -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/ikepolicy.py:69 -#: neutronclient/neutron/v2_0/vpn/ipsecpolicy.py:68 -msgid "Perfect Forward Secrecy in lowercase, default:group5" -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/ikepolicy.py:77 -msgid "Name of the IKE policy." -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:61 -msgid "Local endpoint group ID/name with subnet(s) for IPSec connection." -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:65 -msgid "Peer endpoint group ID/name with CIDR(s) for IPsec connection." -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:101 -msgid "Set friendly name for the connection." -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:104 -msgid "Set a description for the connection." -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:108 -msgid "MTU size for the connection, default:1500" -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:113 -msgid "Initiator state in lowercase, default:bi-directional" -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:117 -msgid "VPN service instance ID associated with this connection." -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:121 -msgid "IKE policy ID associated with this connection." -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:125 -msgid "IPsec policy ID associated with this connection." -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:129 -msgid "Peer gateway public IPv4/IPv6 address or FQDN." -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:133 -msgid "" -"Peer router identity for authentication. Can be IPv4/IPv6 address, e-mail" -" address, key id, or FQDN." -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:138 -msgid "" -"[DEPRECATED in Mitaka] Remote subnet(s) in CIDR format. Cannot be " -"specified when using endpoint groups. Only applicable, if subnet provided" -" for VPN service." -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:144 -msgid "Pre-shared key string." -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:158 -msgid "Invalid MTU value: MTU must be greater than or equal to 68" -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:163 -msgid "You must specify both local and peer endpoint groups." -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:167 -msgid "You cannot specify both endpoint groups and peer CIDR(s)." -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py:171 -msgid "You must specify endpoint groups or peer CIDR(s)." -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/ipsecpolicy.py:48 -msgid "Description of the IPsec policy." -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/ipsecpolicy.py:52 -msgid "Transform protocol in lowercase, default:esp" -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/ipsecpolicy.py:56 -msgid "Authentication algorithm in lowercase, default:sha1" -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/ipsecpolicy.py:64 -msgid "Encapsulation mode in lowercase, default:tunnel" -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/ipsecpolicy.py:76 -msgid "Name of the IPsec policy." -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/utils.py:35 -#, python-format -msgid "" -"DPD Dictionary KeyError: Reason-Invalid DPD key : '%(key)s' not in " -"%(supported_key)s " -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/utils.py:42 -#, python-format -msgid "" -"DPD Dictionary ValueError: Reason-Invalid DPD action : '%(key_value)s' " -"not in %(supported_action)s " -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/utils.py:54 -#, python-format -msgid "" -"DPD Dictionary ValueError: Reason-Invalid positive integer value: " -"'%(key)s' = %(value)s " -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/utils.py:69 -#, python-format -msgid "" -"Lifetime Dictionary KeyError: Reason-Invalid unit key : '%(key)s' not in " -"%(supported_key)s " -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/utils.py:76 -#, python-format -msgid "" -"Lifetime Dictionary ValueError: Reason-Invalid units : '%(key_value)s' " -"not in %(supported_units)s " -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/utils.py:87 -#, python-format -msgid "" -"Lifetime Dictionary ValueError: Reason-Invalid value should be at least " -"60:'%(key_value)s' = %(value)s " -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/utils.py:99 -#, python-format -msgid "" -"%s lifetime attributes. 'units'-seconds, default:seconds. 'value'-non " -"negative integer, default:3600." -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/utils.py:106 -#, python-format -msgid "" -" %s Dead Peer Detection attributes. 'action'-hold,clear,disabled,restart" -",restart-by-peer. 'interval' and 'timeout' are non negative integers. " -"'interval' should be less than 'timeout' value. 'action', default:hold " -"'interval', default:30, 'timeout', default:120." -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/vpnservice.py:50 -msgid "Set a name for the VPN service." -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/vpnservice.py:53 -msgid "Set a description for the VPN service." -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/vpnservice.py:56 -msgid "Router unique identifier for the VPN service." -msgstr "" - -#: neutronclient/neutron/v2_0/vpn/vpnservice.py:59 -msgid "[DEPRECATED in Mitaka] Unique identifier for the local private subnet." -msgstr "" - -#: neutronclient/v2_0/client.py:228 -#, python-format -msgid "Unable to serialize object of type = '%s'" -msgstr "" - -#: neutronclient/v2_0/client.py:280 -#, python-format -msgid "Failed to connect to Neutron server after %d attempts" -msgstr "" - -#: neutronclient/v2_0/client.py:283 -msgid "Failed to connect Neutron server" -msgstr "" - diff --git a/setup.cfg b/setup.cfg index b5724fd73..9dec54c9b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -42,13 +42,13 @@ universal = 1 [extract_messages] keywords = _ gettext ngettext l_ lazy_gettext mapping_file = babel.cfg -output_file = python-neutronclient/locale/python-neutronclient.pot +output_file = neutronclient/locale/neutronclient.pot [compile_catalog] -directory = python-neutronclient/locale -domain = python-neutronclient +directory = neutronclient/locale +domain = neutronclient [update_catalog] -domain = python-neutronclient -output_dir = python-neutronclient/locale -input_file = python-neutronclient/locale/python-neutronclient.pot +domain = neutronclient +output_dir = neutronclient/locale +input_file = neutronclient/locale/neutronclient.pot From 809af6c080a87f668a0d1855f0edb3d528e33a30 Mon Sep 17 00:00:00 2001 From: James Arendt Date: Sat, 30 Jan 2016 21:08:58 -0800 Subject: [PATCH 365/845] Make metavar usage consistent Neutron CLI generally uses all caps for positional arguments via metavars. There are a few exceptions in the current code that do not match the other interfaces. Change-Id: I905baebb3428e994cb724031cbb92120a0953d71 Closes-Bug: #1541625 --- neutronclient/neutron/v2_0/address_scope.py | 1 + neutronclient/neutron/v2_0/agentscheduler.py | 16 ++++++++++++++++ neutronclient/neutron/v2_0/port.py | 2 +- neutronclient/neutron/v2_0/subnetpool.py | 1 + 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/neutronclient/neutron/v2_0/address_scope.py b/neutronclient/neutron/v2_0/address_scope.py index 8a89282fd..662209b1f 100755 --- a/neutronclient/neutron/v2_0/address_scope.py +++ b/neutronclient/neutron/v2_0/address_scope.py @@ -45,6 +45,7 @@ def add_known_arguments(self, parser): help=_('Set the address scope as shared.')) parser.add_argument( 'name', + metavar='NAME', help=_('Specify the name of the address scope.')) parser.add_argument( 'ip_version', diff --git a/neutronclient/neutron/v2_0/agentscheduler.py b/neutronclient/neutron/v2_0/agentscheduler.py index 3348e6412..24287b710 100644 --- a/neutronclient/neutron/v2_0/agentscheduler.py +++ b/neutronclient/neutron/v2_0/agentscheduler.py @@ -32,9 +32,11 @@ def get_parser(self, prog_name): parser = super(AddNetworkToDhcpAgent, self).get_parser(prog_name) parser.add_argument( 'dhcp_agent', + metavar='DHCP_AGENT', help=_('ID of the DHCP agent.')) parser.add_argument( 'network', + metavar='NETWORK', help=_('Network to add.')) return parser @@ -56,9 +58,11 @@ def get_parser(self, prog_name): parser = super(RemoveNetworkFromDhcpAgent, self).get_parser(prog_name) parser.add_argument( 'dhcp_agent', + metavar='DHCP_AGENT', help=_('ID of the DHCP agent.')) parser.add_argument( 'network', + metavar='NETWORK', help=_('Network to remove.')) return parser @@ -83,6 +87,7 @@ def get_parser(self, prog_name): self).get_parser(prog_name) parser.add_argument( 'dhcp_agent', + metavar='DHCP_AGENT', help=_('ID of the DHCP agent.')) return parser @@ -105,6 +110,7 @@ def get_parser(self, prog_name): self).get_parser(prog_name) parser.add_argument( 'network', + metavar='NETWORK', help=_('Network to query.')) return parser @@ -128,9 +134,11 @@ def get_parser(self, prog_name): parser = super(AddRouterToL3Agent, self).get_parser(prog_name) parser.add_argument( 'l3_agent', + metavar='L3_AGENT', help=_('ID of the L3 agent.')) parser.add_argument( 'router', + metavar='ROUTER', help=_('Router to add.')) return parser @@ -152,9 +160,11 @@ def get_parser(self, prog_name): parser = super(RemoveRouterFromL3Agent, self).get_parser(prog_name) parser.add_argument( 'l3_agent', + metavar='L3_AGENT', help=_('ID of the L3 agent.')) parser.add_argument( 'router', + metavar='ROUTER', help=_('Router to remove.')) return parser @@ -183,6 +193,7 @@ def get_parser(self, prog_name): self).get_parser(prog_name) parser.add_argument( 'l3_agent', + metavar='L3_AGENT', help=_('ID of the L3 agent to query.')) return parser @@ -204,6 +215,7 @@ def get_parser(self, prog_name): parser = super(ListL3AgentsHostingRouter, self).get_parser(prog_name) parser.add_argument('router', + metavar='ROUTER', help=_('Router to query.')) return parser @@ -236,6 +248,7 @@ def get_parser(self, prog_name): parser = super(ListPoolsOnLbaasAgent, self).get_parser(prog_name) parser.add_argument( 'lbaas_agent', + metavar='LBAAS_AGENT', help=_('ID of the loadbalancer agent to query.')) return parser @@ -260,6 +273,7 @@ def get_parser(self, prog_name): parser = super(GetLbaasAgentHostingPool, self).get_parser(prog_name) parser.add_argument('pool', + metavar='POOL', help=_('Pool to query.')) return parser @@ -289,6 +303,7 @@ def get_parser(self, prog_name): prog_name) parser.add_argument( 'lbaas_agent', + metavar='LBAAS_AGENT', help=_('ID of the loadbalancer agent to query.')) return parser @@ -313,6 +328,7 @@ def get_parser(self, prog_name): parser = super(GetLbaasAgentHostingLoadBalancer, self).get_parser(prog_name) parser.add_argument('loadbalancer', + metavar='LOADBALANCER', help=_('LoadBalancer to query.')) return parser diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index 6c997f433..0e27cd227 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -107,7 +107,7 @@ class ListRouterPort(neutronV20.ListCommand): def get_parser(self, prog_name): parser = super(ListRouterPort, self).get_parser(prog_name) parser.add_argument( - 'id', metavar='router', + 'id', metavar='ROUTER', help=_('ID or name of router to look up.')) return parser diff --git a/neutronclient/neutron/v2_0/subnetpool.py b/neutronclient/neutron/v2_0/subnetpool.py index 3b36bfe13..a4a224753 100644 --- a/neutronclient/neutron/v2_0/subnetpool.py +++ b/neutronclient/neutron/v2_0/subnetpool.py @@ -82,6 +82,7 @@ def add_known_arguments(self, parser): help=_('Set the subnetpool as shared.')) parser.add_argument( 'name', + metavar='NAME', help=_('Name of subnetpool to create.')) parser.add_argument( '--address-scope', From d35643974c28f900b4f976f728b9bd43f95eeba7 Mon Sep 17 00:00:00 2001 From: reedip Date: Mon, 14 Dec 2015 09:57:52 +0900 Subject: [PATCH 366/845] Allow UPPER case in protocol/action for FW Rule Currently firewall rule create/update allows only lower case values for its protocol and action arguments. Limiting the protocol/action attribute to lower case is not very user friendly. This patch allows the user to provide protocol and action fields in UPPER/lower case. Change-Id: Ib8b278fc89f81d89d30f4e8dde9797e9149d3919 Co-Authored-By:Akihiro Motoki Closes-Bug: #1508753 --- neutronclient/neutron/v2_0/fw/firewallrule.py | 7 ++++++- .../tests/unit/fw/test_cli20_firewallrule.py | 20 ++++++++++++++----- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/neutronclient/neutron/v2_0/fw/firewallrule.py b/neutronclient/neutron/v2_0/fw/firewallrule.py index e77e96fac..70ef8e817 100644 --- a/neutronclient/neutron/v2_0/fw/firewallrule.py +++ b/neutronclient/neutron/v2_0/fw/firewallrule.py @@ -103,18 +103,20 @@ def add_known_arguments(self, parser): help=_('Whether to enable or disable this rule.')) parser.add_argument( '--protocol', choices=['tcp', 'udp', 'icmp', 'any'], + type=utils.convert_to_lowercase, required=True, help=_('Protocol for the firewall rule.')) parser.add_argument( '--action', required=True, + type=utils.convert_to_lowercase, choices=['allow', 'deny', 'reject'], help=_('Action for the firewall rule.')) def args2body(self, parsed_args): body = {} neutronv20.update_dict(parsed_args, body, - ['name', 'description', 'shared', 'protocol', + ['name', 'description', 'shared', 'source_ip_address', 'destination_ip_address', 'source_port', 'destination_port', 'action', 'enabled', 'tenant_id', @@ -135,7 +137,10 @@ def add_known_arguments(self, parser): parser.add_argument( '--protocol', choices=['tcp', 'udp', 'icmp', 'any'], required=False, + type=utils.convert_to_lowercase, help=_('Protocol for the firewall rule.')) + # TODO(reedip) : Need to add the option for action once + # action also comes into Update Firewall Rule def args2body(self, parsed_args): body = {} diff --git a/neutronclient/tests/unit/fw/test_cli20_firewallrule.py b/neutronclient/tests/unit/fw/test_cli20_firewallrule.py index 9c2203501..d84f9ed95 100644 --- a/neutronclient/tests/unit/fw/test_cli20_firewallrule.py +++ b/neutronclient/tests/unit/fw/test_cli20_firewallrule.py @@ -58,8 +58,9 @@ def test_create_enabled_firewall_rule_with_mandatory_params(self): def test_create_disabled_firewall_rule_with_mandatory_params(self): self._test_create_firewall_rule_with_mandatory_params(enabled='False') - def _setup_create_firewall_rule_with_all_params(self, protocol='tcp', - ip_version='4'): + def _setup_create_firewall_rule_with_all_params( + self, protocol='tcp', protocol_cli=None, + action='allow', action_cli=None, ip_version='4'): # firewall-rule-create with all params set. resource = 'firewall_rule' cmd = firewallrule.CreateFirewallRule(test_cli20.MyApp(sys.stdout), @@ -70,19 +71,18 @@ def _setup_create_firewall_rule_with_all_params(self, protocol='tcp', destination_ip = '192.168.2.0/24' source_port = '0:65535' destination_port = '0:65535' - action = 'allow' tenant_id = 'my-tenant' my_id = 'myid' enabled = 'True' args = ['--description', description, '--shared', - '--protocol', protocol, + '--protocol', protocol_cli or protocol, '--ip-version', ip_version, '--source-ip-address', source_ip, '--destination-ip-address', destination_ip, '--source-port', source_port, '--destination-port', destination_port, - '--action', action, + '--action', action_cli or action, '--enabled', enabled, '--admin-state-up', '--tenant-id', tenant_id] @@ -126,6 +126,16 @@ def test_create_firewall_rule_with_IP_version_6(self): def test_create_firewall_rule_with_invalid_IP_version(self): self._setup_create_firewall_rule_with_all_params(ip_version='5') + def test_create_firewall_rule_with_proto_action_upper_capitalized(self): + for protocol in ('TCP', 'Tcp', 'ANY', 'AnY'): + self._setup_create_firewall_rule_with_all_params( + protocol=protocol.lower(), + protocol_cli=protocol) + for action in ('Allow', 'DENY', 'reject'): + self._setup_create_firewall_rule_with_all_params( + action=action.lower(), + action_cli=action) + def test_list_firewall_rules(self): # firewall-rule-list. resources = "firewall_rules" From eafefadeefa3e5ea1d0f41a8af85017505a0d1bd Mon Sep 17 00:00:00 2001 From: Reedip Banerjee Date: Thu, 10 Dec 2015 10:22:19 +0530 Subject: [PATCH 367/845] Show all updatable options in (fw/fw-policy)-update CLI Currently firewall-policy-update and firewall-update CLIs do not show all the options which can be updated in its help section. This patch adds the following updatable options # Firewall-Policy: - shared - audited - description - name # Firewall: - admin-state - description - name This patch adds the information to the above FW CLIs. Change-Id: I66a80ff68fc369298528cd59923c294ed39f4e80 Closes-Bug: #1504411 --- neutronclient/neutron/v2_0/fw/firewall.py | 109 +++++++++--------- .../neutron/v2_0/fw/firewallpolicy.py | 31 +++-- .../tests/unit/fw/test_cli20_firewall.py | 8 ++ .../unit/fw/test_cli20_firewallpolicy.py | 11 ++ 4 files changed, 95 insertions(+), 64 deletions(-) diff --git a/neutronclient/neutron/v2_0/fw/firewall.py b/neutronclient/neutron/v2_0/fw/firewall.py index dacca9fb3..b7d3ea88a 100644 --- a/neutronclient/neutron/v2_0/fw/firewall.py +++ b/neutronclient/neutron/v2_0/fw/firewall.py @@ -13,11 +13,52 @@ # License for the specific language governing permissions and limitations # under the License. # - from neutronclient._i18n import _ +from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronv20 +def add_common_args(parser): + parser.add_argument( + '--name', + help=_('Name for the firewall.')) + parser.add_argument( + '--description', + help=_('Description for the firewall.')) + router = parser.add_mutually_exclusive_group() + router.add_argument( + '--router', + dest='routers', + metavar='ROUTER', + action='append', + help=_('Firewall associated router name or ID (requires FWaaS ' + 'router insertion extension, this option can be repeated)')) + router.add_argument( + '--no-routers', + action='store_true', + help=_('Associate no routers with the firewall (requires FWaaS ' + 'router insertion extension)')) + + +def parse_common_args(client, parsed_args): + body = {} + if parsed_args.policy: + body['firewall_policy_id'] = neutronv20.find_resourceid_by_name_or_id( + client, 'firewall_policy', + parsed_args.policy) + + if parsed_args.routers: + body['router_ids'] = [ + neutronv20.find_resourceid_by_name_or_id(client, 'router', r) + for r in parsed_args.routers] + elif parsed_args.no_routers: + body['router_ids'] = [] + + neutronv20.update_dict(parsed_args, body, + ['name', 'description']) + return body + + class ListFirewall(neutronv20.ListCommand): """List firewalls that belong to a given tenant.""" @@ -40,41 +81,20 @@ class CreateFirewall(neutronv20.CreateCommand): resource = 'firewall' def add_known_arguments(self, parser): + add_common_args(parser) parser.add_argument( - 'firewall_policy_id', metavar='POLICY', + 'policy', metavar='POLICY', help=_('Firewall policy name or ID.')) - parser.add_argument( - '--name', - help=_('Name for the firewall.')) - parser.add_argument( - '--description', - help=_('Description for the firewall rule.')) parser.add_argument( '--admin-state-down', dest='admin_state', action='store_false', help=_('Set admin state up to false.')) - parser.add_argument( - '--router', - dest='routers', - metavar='ROUTER', - action='append', - help=_('Firewall associated router names or IDs (requires FWaaS ' - 'router insertion extension, this option can be repeated)')) def args2body(self, parsed_args): - client = self.get_client() - _policy_id = neutronv20.find_resourceid_by_name_or_id( - client, 'firewall_policy', - parsed_args.firewall_policy_id) - body = {'firewall_policy_id': _policy_id, - 'admin_state_up': parsed_args.admin_state, } - if parsed_args.routers: - body['router_ids'] = [ - neutronv20.find_resourceid_by_name_or_id(client, 'router', r) - for r in parsed_args.routers] - neutronv20.update_dict(parsed_args, body, - ['name', 'description', 'tenant_id']) + body = parse_common_args(self.get_client(), parsed_args) + neutronv20.update_dict(parsed_args, body, ['tenant_id']) + body['admin_state_up'] = parsed_args.admin_state return {self.resource: body} @@ -84,38 +104,19 @@ class UpdateFirewall(neutronv20.UpdateCommand): resource = 'firewall' def add_known_arguments(self, parser): + add_common_args(parser) parser.add_argument( '--policy', metavar='POLICY', help=_('Firewall policy name or ID.')) - router_sg = parser.add_mutually_exclusive_group() - router_sg.add_argument( - '--router', - dest='routers', - metavar='ROUTER', - action='append', - help=_('Firewall associated router names or IDs (requires FWaaS ' - 'router insertion extension, this option can be repeated)')) - router_sg.add_argument( - '--no-routers', - action='store_true', - help=_('Associate no routers with the firewall (requires FWaaS ' - 'router insertion extension)')) + utils.add_boolean_argument( + parser, '--admin-state-up', dest='admin_state_up', + help=_('Update the admin state for the firewall' + '(True means UP)')) def args2body(self, parsed_args): - data = {} - client = self.get_client() - if parsed_args.policy: - _policy_id = neutronv20.find_resourceid_by_name_or_id( - client, 'firewall_policy', - parsed_args.policy) - data['firewall_policy_id'] = _policy_id - if parsed_args.routers: - data['router_ids'] = [ - neutronv20.find_resourceid_by_name_or_id(client, 'router', r) - for r in parsed_args.routers] - elif parsed_args.no_routers: - data['router_ids'] = [] - return {self.resource: data} + body = parse_common_args(self.get_client(), parsed_args) + neutronv20.update_dict(parsed_args, body, ['admin_state_up']) + return {self.resource: body} class DeleteFirewall(neutronv20.DeleteCommand): diff --git a/neutronclient/neutron/v2_0/fw/firewallpolicy.py b/neutronclient/neutron/v2_0/fw/firewallpolicy.py index 07786879a..08d623ab9 100644 --- a/neutronclient/neutron/v2_0/fw/firewallpolicy.py +++ b/neutronclient/neutron/v2_0/fw/firewallpolicy.py @@ -19,6 +19,7 @@ import argparse from neutronclient._i18n import _ +from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronv20 @@ -31,14 +32,17 @@ def _format_firewall_rules(firewall_policy): return '' -def common_add_known_arguments(parser): +def add_common_args(parser): + parser.add_argument( + '--description', + help=_('Description for the firewall policy.')) parser.add_argument( '--firewall-rules', type=lambda x: x.split(), help=_('Ordered list of whitespace-delimited firewall rule ' 'names or IDs; e.g., --firewall-rules \"rule1 rule2\"')) -def common_args2body(client, parsed_args): +def parse_common_args(client, parsed_args): if parsed_args.firewall_rules: _firewall_rules = [] for f in parsed_args.firewall_rules: @@ -81,24 +85,20 @@ def add_known_arguments(self, parser): 'name', metavar='NAME', help=_('Name for the firewall policy.')) - parser.add_argument( - '--description', - help=_('Description for the firewall policy.')) parser.add_argument( '--shared', - dest='shared', action='store_true', help=_('Create a shared policy.'), default=argparse.SUPPRESS) - common_add_known_arguments(parser) parser.add_argument( '--audited', action='store_true', help=_('Sets audited to True.'), default=argparse.SUPPRESS) + add_common_args(parser) def args2body(self, parsed_args): - return common_args2body(self.get_client(), parsed_args) + return parse_common_args(self.get_client(), parsed_args) class UpdateFirewallPolicy(neutronv20.UpdateCommand): @@ -107,10 +107,21 @@ class UpdateFirewallPolicy(neutronv20.UpdateCommand): resource = 'firewall_policy' def add_known_arguments(self, parser): - common_add_known_arguments(parser) + add_common_args(parser) + parser.add_argument( + '--name', + help=_('Name for the firewall policy.')) + utils.add_boolean_argument( + parser, '--shared', + help=_('Update the sharing status of the policy. ' + '(True means shared)')) + utils.add_boolean_argument( + parser, '--audited', + help=_('Update the audit status of the policy. ' + '(True means auditing is enabled)')) def args2body(self, parsed_args): - return common_args2body(self.get_client(), parsed_args) + return parse_common_args(self.get_client(), parsed_args) class DeleteFirewallPolicy(neutronv20.DeleteCommand): diff --git a/neutronclient/tests/unit/fw/test_cli20_firewall.py b/neutronclient/tests/unit/fw/test_cli20_firewall.py index ece9ac84d..f9a2deebf 100644 --- a/neutronclient/tests/unit/fw/test_cli20_firewall.py +++ b/neutronclient/tests/unit/fw/test_cli20_firewall.py @@ -160,3 +160,11 @@ def test_delete_firewall(self): my_id = 'my-id' args = [my_id] self._test_delete_resource(resource, cmd, my_id, args) + + def test_update_firewall_admin_state(self): + # firewall-update myid --admin-state-up True. + resource = 'firewall' + cmd = firewall.UpdateFirewall(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--admin-state-up', 'True'], + {'admin_state_up': 'True'}) diff --git a/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py b/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py index 3d3241e1b..8ec611c86 100644 --- a/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py +++ b/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py @@ -213,3 +213,14 @@ def test_remove_firewall_rule(self): shell.run_command(cmd, cmd_parser, args) self.mox.VerifyAll() self.mox.UnsetStubs() + + def test_update_firewall_policy_name_shared_audited(self): + # firewall-policy-update myid --name newname2 --shared --audited + resource = 'firewall_policy' + cmd = firewallpolicy.UpdateFirewallPolicy(test_cli20.MyApp(sys.stdout), + None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--name', 'newname2', + '--shared', 'True', '--audited', 'True'], + {'name': 'newname2', + 'shared': 'True', 'audited': 'True'}) From 4ac0d8068c60f7568579f87518ccfc58064e4973 Mon Sep 17 00:00:00 2001 From: reedip Date: Tue, 15 Dec 2015 14:57:27 +0900 Subject: [PATCH 368/845] "neutron help firewall-rule-update" info updated Currently, neutron firewall-rule-update does not show all the options which it supports. With this patch, additional options which were earlier missing are added to the help option of firewall-rule-update. The additional options added are: --name --description --shared --source-ip-address --destination-ip-address --source-port --destination-port --enabled --action --ip-version Change-Id: I000dacfe9acbd220a61b2d3c6cea86c7a42b398f Depends-On: Ib8b278fc89f81d89d30f4e8dde9797e9149d3919 Closes-Bug: #1503985 --- neutronclient/neutron/v2_0/fw/firewallrule.py | 130 ++++++++++-------- .../tests/unit/fw/test_cli20_firewallrule.py | 47 ++++++- 2 files changed, 113 insertions(+), 64 deletions(-) diff --git a/neutronclient/neutron/v2_0/fw/firewallrule.py b/neutronclient/neutron/v2_0/fw/firewallrule.py index 70ef8e817..df5db95d6 100644 --- a/neutronclient/neutron/v2_0/fw/firewallrule.py +++ b/neutronclient/neutron/v2_0/fw/firewallrule.py @@ -21,6 +21,62 @@ from neutronclient.neutron import v2_0 as neutronv20 +def _add_common_args(parser, is_create=True): + """If is_create is True, protocol and action become mandatory arguments. + + CreateCommand = is_create : True + UpdateCommand = is_create : False + """ + parser.add_argument( + '--name', + help=_('Name for the firewall rule.')) + parser.add_argument( + '--description', + help=_('Description for the firewall rule.')) + parser.add_argument( + '--source-ip-address', + help=_('Source IP address or subnet.')) + parser.add_argument( + '--destination-ip-address', + help=_('Destination IP address or subnet.')) + parser.add_argument( + '--source-port', + help=_('Source port (integer in [1, 65535] or range in a:b).')) + parser.add_argument( + '--destination-port', + help=_('Destination port (integer in [1, 65535] or range in ' + 'a:b).')) + utils.add_boolean_argument( + parser, '--enabled', dest='enabled', + help=_('Whether to enable or disable this rule.')) + parser.add_argument( + '--protocol', choices=['tcp', 'udp', 'icmp', 'any'], + required=is_create, + type=utils.convert_to_lowercase, + help=_('Protocol for the firewall rule.')) + parser.add_argument( + '--action', + required=is_create, + type=utils.convert_to_lowercase, + choices=['allow', 'deny', 'reject'], + help=_('Action for the firewall rule.')) + + +def common_args2body(parsed_args): + body = {} + neutronv20.update_dict(parsed_args, body, + ['name', 'description', 'shared', 'tenant_id', + 'source_ip_address', 'destination_ip_address', + 'source_port', 'destination_port', 'action', + 'enabled', 'ip_version']) + protocol = parsed_args.protocol + if protocol: + if protocol == 'any': + protocol = None + body['protocol'] = protocol + return body + + class ListFirewallRule(neutronv20.ListCommand): """List firewall rules that belong to a given tenant.""" @@ -69,63 +125,19 @@ class CreateFirewallRule(neutronv20.CreateCommand): resource = 'firewall_rule' def add_known_arguments(self, parser): - parser.add_argument( - '--name', - help=_('Name for the firewall rule.')) - parser.add_argument( - '--description', - help=_('Description for the firewall rule.')) parser.add_argument( '--shared', - dest='shared', action='store_true', - help=_('Set shared to True (default is False).'), + help=_('Set shared flag for the firewall rule.'), default=argparse.SUPPRESS) + _add_common_args(parser) parser.add_argument( '--ip-version', type=int, choices=[4, 6], default=4, help=_('IP version for the firewall rule (default is 4).')) - parser.add_argument( - '--source-ip-address', - help=_('Source IP address or subnet.')) - parser.add_argument( - '--destination-ip-address', - help=_('Destination IP address or subnet.')) - parser.add_argument( - '--source-port', - help=_('Source port (integer in [1, 65535] or range in a:b).')) - parser.add_argument( - '--destination-port', - help=_('Destination port (integer in [1, 65535] or range in ' - 'a:b).')) - utils.add_boolean_argument( - parser, '--enabled', dest='enabled', - help=_('Whether to enable or disable this rule.')) - parser.add_argument( - '--protocol', choices=['tcp', 'udp', 'icmp', 'any'], - type=utils.convert_to_lowercase, - required=True, - help=_('Protocol for the firewall rule.')) - parser.add_argument( - '--action', - required=True, - type=utils.convert_to_lowercase, - choices=['allow', 'deny', 'reject'], - help=_('Action for the firewall rule.')) def args2body(self, parsed_args): - body = {} - neutronv20.update_dict(parsed_args, body, - ['name', 'description', 'shared', - 'source_ip_address', 'destination_ip_address', - 'source_port', 'destination_port', - 'action', 'enabled', 'tenant_id', - 'ip_version']) - protocol = parsed_args.protocol - if protocol == 'any': - protocol = None - body['protocol'] = protocol - return {self.resource: body} + return {self.resource: common_args2body(parsed_args)} class UpdateFirewallRule(neutronv20.UpdateCommand): @@ -134,22 +146,20 @@ class UpdateFirewallRule(neutronv20.UpdateCommand): resource = 'firewall_rule' def add_known_arguments(self, parser): + utils.add_boolean_argument( + parser, + '--shared', + dest='shared', + help=_('Update the shared flag for the firewall rule.'), + default=argparse.SUPPRESS) parser.add_argument( - '--protocol', choices=['tcp', 'udp', 'icmp', 'any'], - required=False, - type=utils.convert_to_lowercase, - help=_('Protocol for the firewall rule.')) - # TODO(reedip) : Need to add the option for action once - # action also comes into Update Firewall Rule + '--ip-version', + type=int, choices=[4, 6], + help=_('Update IP version for the firewall rule.')) + _add_common_args(parser, is_create=False) def args2body(self, parsed_args): - body = {} - protocol = parsed_args.protocol - if protocol: - if protocol == 'any': - protocol = None - body['protocol'] = protocol - return {self.resource: body} + return {self.resource: common_args2body(parsed_args)} class DeleteFirewallRule(neutronv20.DeleteCommand): diff --git a/neutronclient/tests/unit/fw/test_cli20_firewallrule.py b/neutronclient/tests/unit/fw/test_cli20_firewallrule.py index d84f9ed95..50fabca8e 100644 --- a/neutronclient/tests/unit/fw/test_cli20_firewallrule.py +++ b/neutronclient/tests/unit/fw/test_cli20_firewallrule.py @@ -193,15 +193,54 @@ def test_update_firewall_rule(self): ['myid', '--name', 'newname'], {'name': 'newname', }) - def test_update_firewall_rule_protocol(self): # firewall-rule-update myid --protocol any. - resource = 'firewall_rule' - cmd = firewallrule.UpdateFirewallRule(test_cli20.MyApp(sys.stdout), - None) self._test_update_resource(resource, cmd, 'myid', ['myid', '--protocol', 'any'], {'protocol': None, }) + # firewall-rule-update myid --description any + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--description', 'any'], + {'description': 'any', }) + + # firewall-rule-update myid --source_ip_address 192.192.192.192 + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--source_ip_address', + '192.192.192.192'], + {'source_ip_address': '192.192.192.192', }) + + # firewall-rule-update myid --source_port 32767 + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--source_port', '32767'], + {'source_port': '32767', }) + + # firewall-rule-update myid --destination_ip_address 0.1.0.1 + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--destination_ip_address', + '0.1.0.1'], + {'destination_ip_address': '0.1.0.1', }) + + # firewall-rule-update myid --destination_port 65432 + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--destination_port', + '65432'], + {'destination_port': '65432', }) + + # firewall-rule-update myid --enabled False + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--enabled', 'False'], + {'enabled': 'False', }) + + # firewall-rule-update myid --action reject + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--action', 'reject'], + {'action': 'reject', }) + + # firewall-rule-update myid --shared false + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--shared', 'false'], + {'shared': 'false', }) + def test_delete_firewall_rule(self): # firewall-rule-delete my-id. resource = 'firewall_rule' From da57c86a53e1842e622594e269d18bb96b7cfba6 Mon Sep 17 00:00:00 2001 From: Henry Gessau Date: Tue, 26 Jan 2016 18:56:08 -0500 Subject: [PATCH 369/845] Client bindings for Get-me-a-network Add client bindings for auto-allocated-topology. Add the 'auto-allocated-topology-show' CLI. Partially-implements: blueprint get-me-a-network Depends-On: Ia35e8a946bf0ac0bb085cde46b675d17b0bb2f51 Change-Id: I67a30ce552fd0310e051ac39dda0a763ee29ef6e --- .../neutron/v2_0/auto_allocated_topology.py | 65 +++++++++++++++++++ neutronclient/shell.py | 3 + .../unit/test_auto_allocated_topology.py | 41 ++++++++++++ neutronclient/v2_0/client.py | 8 +++ ...add-get-me-a-network-5ab2d60bf6f257b1.yaml | 9 +++ 5 files changed, 126 insertions(+) create mode 100755 neutronclient/neutron/v2_0/auto_allocated_topology.py create mode 100755 neutronclient/tests/unit/test_auto_allocated_topology.py create mode 100644 releasenotes/notes/add-get-me-a-network-5ab2d60bf6f257b1.yaml diff --git a/neutronclient/neutron/v2_0/auto_allocated_topology.py b/neutronclient/neutron/v2_0/auto_allocated_topology.py new file mode 100755 index 000000000..ad96fd988 --- /dev/null +++ b/neutronclient/neutron/v2_0/auto_allocated_topology.py @@ -0,0 +1,65 @@ +# Copyright 2016 IBM +# 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 argparse +from cliff import show +from oslo_serialization import jsonutils + +from neutronclient._i18n import _ +from neutronclient.neutron import v2_0 + + +class ShowAutoAllocatedTopology(v2_0.NeutronCommand, show.ShowOne): + """Show the auto-allocated topology of a given tenant.""" + + resource = 'auto_allocated_topology' + + def get_parser(self, prog_name): + parser = super(ShowAutoAllocatedTopology, self).get_parser(prog_name) + parser.add_argument( + '--tenant-id', metavar='tenant-id', + help=_('The owner tenant ID.')) + # Allow people to do + # neutron auto-allocated-topology-show + # (Only useful to users who can look at other tenants' topologies.) + # We use a different name for this arg because the default will + # override whatever is in the named arg otherwise. + parser.add_argument( + 'pos_tenant_id', + help=argparse.SUPPRESS, nargs='?') + return parser + + def take_action(self, parsed_args): + client = self.get_client() + tenant_id = parsed_args.tenant_id or parsed_args.pos_tenant_id + data = client.show_auto_allocated_topology(tenant_id) + if self.resource in data: + for k, v in data[self.resource].items(): + if isinstance(v, list): + value = "" + for _item in v: + if value: + value += "\n" + if isinstance(_item, dict): + value += jsonutils.dumps(_item) + else: + value += str(_item) + data[self.resource][k] = value + elif v is None: + data[self.resource][k] = '' + return zip(*sorted(data[self.resource].items())) + else: + return None diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 4011133ca..c1f6b0aaf 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -43,6 +43,7 @@ from neutronclient.neutron.v2_0 import address_scope from neutronclient.neutron.v2_0 import agent from neutronclient.neutron.v2_0 import agentscheduler +from neutronclient.neutron.v2_0 import auto_allocated_topology from neutronclient.neutron.v2_0 import availability_zone from neutronclient.neutron.v2_0 import extension from neutronclient.neutron.v2_0.flavor import flavor @@ -392,6 +393,8 @@ def take_action(self, parsed_args): 'flavor-profile-delete': flavor_profile.DeleteFlavorProfile, 'flavor-profile-update': flavor_profile.UpdateFlavorProfile, 'availability-zone-list': availability_zone.ListAvailabilityZone, + 'auto-allocated-topology-show': ( + auto_allocated_topology.ShowAutoAllocatedTopology), } COMMANDS = {'2.0': COMMAND_V2} diff --git a/neutronclient/tests/unit/test_auto_allocated_topology.py b/neutronclient/tests/unit/test_auto_allocated_topology.py new file mode 100755 index 000000000..b0f8cf0e7 --- /dev/null +++ b/neutronclient/tests/unit/test_auto_allocated_topology.py @@ -0,0 +1,41 @@ +# Copyright 2016 IBM +# 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 sys + +from neutronclient.neutron.v2_0 import auto_allocated_topology as aat +from neutronclient.tests.unit import test_cli20 + + +class TestAutoAllocatedTopologyJSON(test_cli20.CLITestV20Base): + + def test_show_auto_allocated_topology_arg(self): + resource = 'auto_allocated_topology' + cmd = aat.ShowAutoAllocatedTopology(test_cli20.MyApp(sys.stdout), None) + args = ['--tenant-id', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, args) + + def test_show_auto_allocated_topology_posarg(self): + resource = 'auto_allocated_topology' + cmd = aat.ShowAutoAllocatedTopology(test_cli20.MyApp(sys.stdout), None) + args = ['some-tenant'] + self._test_show_resource(resource, cmd, "some-tenant", args) + + def test_show_auto_allocated_topology_no_arg(self): + resource = 'auto_allocated_topology' + cmd = aat.ShowAutoAllocatedTopology(test_cli20.MyApp(sys.stdout), None) + args = [] + self._test_show_resource(resource, cmd, "None", args) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 2d1fd6d9f..df0c46c16 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -409,6 +409,7 @@ class Client(ClientBase): flavor_profile_bindings_path = flavor_path + service_profiles_path flavor_profile_binding_path = flavor_path + service_profile_path availability_zones_path = "/availability_zones" + auto_allocated_topology_path = "/auto-allocated-topology/%s" # API has no way to report plurals, so we have to hard code them EXTED_PLURALS = {'routers': 'router', @@ -1693,6 +1694,13 @@ def list_availability_zones(self, retrieve_all=True, **_params): return self.list('availability_zones', self.availability_zones_path, retrieve_all, **_params) + @APIParamsCall + def show_auto_allocated_topology(self, tenant_id, **_params): + """Fetch information about a tenant's auto-allocated topology.""" + return self.get( + self.auto_allocated_topology_path % tenant_id, + params=_params) + def __init__(self, **kwargs): """Initialize a new client for the Neutron v2.0 API.""" super(Client, self).__init__(**kwargs) diff --git a/releasenotes/notes/add-get-me-a-network-5ab2d60bf6f257b1.yaml b/releasenotes/notes/add-get-me-a-network-5ab2d60bf6f257b1.yaml new file mode 100644 index 000000000..51dd37f41 --- /dev/null +++ b/releasenotes/notes/add-get-me-a-network-5ab2d60bf6f257b1.yaml @@ -0,0 +1,9 @@ +--- +features: + - | + CLI support for the "get-me-a-network" feature, which simplifies + the process for launching an instance with basic network + connectivity. + + * The ``auto-allocated-topology-show`` command provides the + network of the automatically allocated topology for a tenant. From 92b5fe9eb1aa0dca75129fd0f0f7da25f312623b Mon Sep 17 00:00:00 2001 From: Reedip Banerjee Date: Thu, 10 Dec 2015 09:01:27 +0530 Subject: [PATCH 370/845] Provide argument filtering in neutron *-list This implements a common framework for attribute filtering arguments and arguments for net-list as an initial support. Co-Authored-By: Reedip Banerjee Co-Authored-By: Akihiro Motoki Change-Id: Ie1b896d6f293b2881a7067ed9232a5957a5180cb Closes-Bug: #1320798 Partial-Bug: #1488912 --- neutronclient/neutron/v2_0/__init__.py | 66 ++++++++++++++++ neutronclient/neutron/v2_0/network.py | 17 +++++ neutronclient/tests/unit/test_cli20.py | 75 +++++++++++++++++++ .../tests/unit/test_cli20_network.py | 12 ++- 4 files changed, 168 insertions(+), 2 deletions(-) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index 34c5592af..f37fcb1fe 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -18,6 +18,7 @@ import abc import argparse +import functools import logging import re @@ -603,6 +604,37 @@ class ListCommand(NeutronCommand, lister.Lister): unknown_parts_flag = True pagination_support = False sorting_support = False + resource_plural = None + + # A list to define arguments for filtering by attribute value + # CLI arguments are shown in the order of this list. + # Each element must be either of a string of an attribute name + # or a dict of a full attribute definitions whose format is: + # {'name': attribute name, (mandatory) + # 'help': help message for CLI (mandatory) + # 'boolean': boolean parameter or not. (Default: False) (optional) + # 'argparse_kwargs': a dict of parameters passed to + # argparse add_argument() + # (Default: {}) (optional) + # } + # For more details, see ListNetworks.filter_attrs. + filter_attrs = [] + + default_attr_defs = { + 'name': { + 'help': _("Filter %s according to their name."), + 'boolean': False, + }, + 'tenant_id': { + 'help': _('Filter %s belonging to the given tenant.'), + 'boolean': False, + }, + 'admin_state_up': { + 'help': _('Filter and list the %s whose adminstrative ' + 'state is active'), + 'boolean': True, + }, + } def get_parser(self, prog_name): parser = super(ListCommand, self).get_parser(prog_name) @@ -612,8 +644,36 @@ def get_parser(self, prog_name): if self.sorting_support: add_sorting_argument(parser) self.add_known_arguments(parser) + self.add_filtering_arguments(parser) return parser + def add_filtering_arguments(self, parser): + if not self.filter_attrs: + return + + group_parser = parser.add_argument_group('filtering arguments') + collection = self.resource_plural or '%ss' % self.resource + for attr in self.filter_attrs: + if isinstance(attr, str): + # Use detail defined in default_attr_defs + attr_name = attr + attr_defs = self.default_attr_defs[attr] + else: + attr_name = attr['name'] + attr_defs = attr + option_name = '--%s' % attr_name.replace('_', '-') + params = attr_defs.get('argparse_kwargs', {}) + try: + help_msg = attr_defs['help'] % collection + except TypeError: + help_msg = attr_defs['help'] + if attr_defs.get('boolean', False): + add_arg_func = functools.partial(utils.add_boolean_argument, + group_parser) + else: + add_arg_func = group_parser.add_argument + add_arg_func(option_name, help=help_msg, **params) + def args2search_opts(self, parsed_args): search_opts = {} fields = parsed_args.fields @@ -621,6 +681,12 @@ def args2search_opts(self, parsed_args): search_opts.update({'fields': fields}) if parsed_args.show_details: search_opts.update({'verbose': 'True'}) + filter_attrs = [field if isinstance(field, str) else field['name'] + for field in self.filter_attrs] + for attr in filter_attrs: + val = getattr(parsed_args, attr, None) + if val: + search_opts[attr] = val return search_opts def call_server(self, neutron_client, search_opts, parsed_args): diff --git a/neutronclient/neutron/v2_0/network.py b/neutronclient/neutron/v2_0/network.py index 8d342702d..e5ac3bd7b 100644 --- a/neutronclient/neutron/v2_0/network.py +++ b/neutronclient/neutron/v2_0/network.py @@ -44,6 +44,23 @@ class ListNetwork(neutronV20.ListCommand): pagination_support = True sorting_support = True + filter_attrs = [ + 'tenant_id', + 'name', + 'admin_state_up', + {'name': 'status', + 'help': _("Filter %s according to their operation status." + "(For example: ACTIVE, ERROR etc)"), + 'boolean': False, + 'argparse_kwargs': {'type': utils.convert_to_uppercase}}, + {'name': 'shared', + 'help': _('Filter and list the networks which are shared.'), + 'boolean': True}, + {'name': 'router:external', + 'help': _('Filter and list the networks which are external.'), + 'boolean': True}, + ] + def extend_list(self, data, parsed_args): """Add subnet information to a network list.""" neutron_client = self.get_client() diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index febc5863a..8aa0aabe3 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -557,6 +557,81 @@ def _test_update_resource_action(self, resource, cmd, myid, action, args, self.assertIn(myid, _str) +class TestListCommand(neutronV2_0.ListCommand): + resource = 'test_resource' + filter_attrs = [ + 'name', + 'admin_state_up', + {'name': 'foo', 'help': 'non-boolean attribute foo'}, + {'name': 'bar', 'help': 'boolean attribute bar', + 'boolean': True}, + {'name': 'baz', 'help': 'integer attribute baz', + 'argparse_kwargs': {'choices': ['baz1', 'baz2']}}, + ] + + +class ListCommandTestCase(CLITestV20Base): + + def setUp(self): + super(ListCommandTestCase, self).setUp() + self.client.extend_list('test_resources', '/test_resources', None) + setattr(self.client, 'test_resources_path', '/test_resources') + + def _test_list_resources_filter_params(self, base_args='', query=''): + resources = 'test_resources' + cmd = TestListCommand(MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, + base_args=base_args.split(), + query=query) + + def _test_list_resources_with_arg_error(self, base_args=''): + self.addCleanup(self.mox.UnsetStubs) + resources = 'test_resources' + cmd = TestListCommand(MyApp(sys.stdout), None) + # argparse parse error leads to SystemExit + self.assertRaises(SystemExit, + self._test_list_resources, + resources, cmd, + base_args=base_args.split()) + + def test_list_resources_without_filter(self): + self._test_list_resources_filter_params() + + def test_list_resources_use_default_filter(self): + self._test_list_resources_filter_params( + base_args='--name val1 --admin-state-up False', + query='name=val1&admin_state_up=False') + + def test_list_resources_use_custom_filter(self): + self._test_list_resources_filter_params( + base_args='--foo FOO --bar True', + query='foo=FOO&bar=True') + + def test_list_resources_boolean_check_default_filter(self): + self._test_list_resources_filter_params( + base_args='--admin-state-up True', query='admin_state_up=True') + self._test_list_resources_filter_params( + base_args='--admin-state-up False', query='admin_state_up=False') + self._test_list_resources_with_arg_error( + base_args='--admin-state-up non-true-false') + + def test_list_resources_boolean_check_custom_filter(self): + self._test_list_resources_filter_params( + base_args='--bar True', query='bar=True') + self._test_list_resources_filter_params( + base_args='--bar False', query='bar=False') + self._test_list_resources_with_arg_error( + base_args='--bar non-true-false') + + def test_list_resources_argparse_kwargs(self): + self._test_list_resources_filter_params( + base_args='--baz baz1', query='baz=baz1') + self._test_list_resources_filter_params( + base_args='--baz baz2', query='baz=baz2') + self._test_list_resources_with_arg_error( + base_args='--bar non-choice') + + class ClientV2TestJson(CLITestV20Base): def test_do_request_unicode(self): self.mox.StubOutWithMock(self.client.httpclient, "request") diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index 8d264c4d1..0465b88dd 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -196,13 +196,15 @@ def test_list_nets_empty_with_column(self): def _test_list_networks(self, cmd, detail=False, tags=(), fields_1=(), fields_2=(), page_size=None, - sort_key=(), sort_dir=()): + sort_key=(), sort_dir=(), base_args=None, + query=''): resources = "networks" self.mox.StubOutWithMock(network.ListNetwork, "extend_list") network.ListNetwork.extend_list(mox.IsA(list), mox.IgnoreArg()) self._test_list_resources(resources, cmd, detail, tags, fields_1, fields_2, page_size=page_size, - sort_key=sort_key, sort_dir=sort_dir) + sort_key=sort_key, sort_dir=sort_dir, + base_args=base_args, query=query) def test_list_nets_pagination(self): cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) @@ -611,3 +613,9 @@ def mox_calls(path, data): 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(response) self._test_extend_list(mox_calls) + + def test_list_shared_networks(self): + # list nets : --shared False + cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) + self._test_list_networks(cmd, base_args='--shared False'.split(), + query='shared=False') From 6810d96ea103b46b42de9073dd6c913b75626fb3 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Thu, 11 Feb 2016 07:44:36 +0000 Subject: [PATCH 371/845] Updated from global requirements Change-Id: I4fe34e07dcb5ffd16f849e95483d5b4aaaf5102c --- requirements.txt | 2 +- test-requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 87043807d..c5280b98e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. pbr>=1.6 # Apache-2.0 -cliff>=1.15.0 # Apache-2.0 +cliff!=1.16.0,>=1.15.0 # Apache-2.0 debtcollector>=1.2.0 # Apache-2.0 iso8601>=0.1.9 # MIT netaddr!=0.7.16,>=0.7.12 # BSD diff --git a/test-requirements.txt b/test-requirements.txt index 35c9b6b6e..992d131af 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -16,4 +16,4 @@ requests-mock>=0.7.0 # Apache-2.0 sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 # BSD testrepository>=0.0.18 # Apache-2.0/BSD testtools>=1.4.0 # MIT -tempest-lib>=0.13.0 # Apache-2.0 +tempest-lib>=0.14.0 # Apache-2.0 From 53195f701883b8e0309d3926cb5e1d2c26a4b3f5 Mon Sep 17 00:00:00 2001 From: cshahani Date: Wed, 10 Feb 2016 22:53:38 -0800 Subject: [PATCH 372/845] Fix typos in the docstrings Fixed some typos in the docstrings for methods and corrected the docstring for a method. Change-Id: I33860de72988e2160ec65d96c9e118f7c9861788 --- neutronclient/v2_0/client.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index df0c46c16..cc323c957 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -451,27 +451,27 @@ class Client(ClientBase): @APIParamsCall def list_ext(self, collection, path, retrieve_all, **_params): - """Client extension hook for lists.""" + """Client extension hook for list.""" return self.list(collection, path, retrieve_all, **_params) @APIParamsCall def show_ext(self, path, id, **_params): - """Client extension hook for shows.""" + """Client extension hook for show.""" return self.get(path % id, params=_params) @APIParamsCall def create_ext(self, path, body=None): - """Client extension hook for creates.""" + """Client extension hook for create.""" return self.post(path, body=body) @APIParamsCall def update_ext(self, path, id, body=None): - """Client extension hook for updates.""" + """Client extension hook for update.""" return self.put(path % id, body=body) @APIParamsCall def delete_ext(self, path, id): - """Client extension hook for deletes.""" + """Client extension hook for delete.""" return self.delete(path % id) @APIParamsCall @@ -501,24 +501,24 @@ def delete_quota(self, tenant_id): @APIParamsCall def list_extensions(self, **_params): - """Fetch a list of all exts on server side.""" + """Fetch a list of all extensions on server side.""" return self.get(self.extensions_path, params=_params) @APIParamsCall def show_extension(self, ext_alias, **_params): - """Fetch a list of all exts on server side.""" + """Fetches information of a certain extension.""" return self.get(self.extension_path % ext_alias, params=_params) @APIParamsCall def list_ports(self, retrieve_all=True, **_params): - """Fetches a list of all networks for a tenant.""" + """Fetches a list of all ports for a tenant.""" # Pass filters in "params" argument to do_request return self.list('ports', self.ports_path, retrieve_all, **_params) @APIParamsCall def show_port(self, port, **_params): - """Fetches information of a certain network.""" + """Fetches information of a certain port.""" return self.get(self.port_path % (port), params=_params) @APIParamsCall @@ -565,7 +565,7 @@ def delete_network(self, network): @APIParamsCall def list_subnets(self, retrieve_all=True, **_params): - """Fetches a list of all networks for a tenant.""" + """Fetches a list of all subnets for a tenant.""" return self.list('subnets', self.subnets_path, retrieve_all, **_params) @@ -1419,7 +1419,7 @@ def firewall_policy_remove_rule(self, firewall_policy, body=None): @APIParamsCall def list_firewalls(self, retrieve_all=True, **_params): - """Fetches a list of all firewals for a tenant.""" + """Fetches a list of all firewalls for a tenant.""" # Pass filters in "params" argument to do_request return self.list('firewalls', self.firewalls_path, retrieve_all, From 9c3ad54ba45569f201bef793bba9f7cf805c1124 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Fri, 19 Feb 2016 07:41:04 +0900 Subject: [PATCH 373/845] Fix typo in the help of net-list TrivialFix Change-Id: If20a2dcbacd5230b9f569478ab97c9114c15301b --- neutronclient/neutron/v2_0/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index f37fcb1fe..82848e95c 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -630,7 +630,7 @@ class ListCommand(NeutronCommand, lister.Lister): 'boolean': False, }, 'admin_state_up': { - 'help': _('Filter and list the %s whose adminstrative ' + 'help': _('Filter and list the %s whose administrative ' 'state is active'), 'boolean': True, }, From 3e3baba4f232de7fd04e05bb6e9afdc723668594 Mon Sep 17 00:00:00 2001 From: reedip Date: Wed, 27 Jan 2016 17:00:25 +0900 Subject: [PATCH 374/845] Fix the exception when ID/Name not found Currently NeutronClient raises a broad exception (NeutronClientException) from [1]-[2], which is not appropriate. The following patch proposes using the NotFound exception which does the same work. [1]:http://git.openstack.org/cgit/openstack/python-neutronclient/tree/neutronclient/neutron/v2_0/__init__.py#n72 [2]:http://git.openstack.org/cgit/openstack/python-neutronclient/tree/neutronclient/neutron/v2_0/__init__.py#n109 Change-Id: Ib23d1e53947b5dffcff8db0dde77cae0a0b31243 --- neutronclient/neutron/v2_0/__init__.py | 12 +++++------- neutronclient/tests/unit/test_name_or_id.py | 4 ++-- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index 34c5592af..702a50ce7 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -68,9 +68,8 @@ def find_resource_by_id(client, resource, resource_id, cmd_resource=None, not_found_message = (_("Unable to find %(resource)s with id " "'%(id)s'") % {'resource': resource, 'id': resource_id}) - # 404 is used to simulate server side behavior - raise exceptions.NeutronClientException( - message=not_found_message, status_code=404) + # 404 is raised by exceptions.NotFound to simulate serverside behavior + raise exceptions.NotFound(message=not_found_message) def find_resourceid_by_id(client, resource, resource_id, cmd_resource=None, @@ -105,9 +104,8 @@ def _find_resource_by_name(client, resource, name, project_id=None, not_found_message = (_("Unable to find %(resource)s with name " "'%(name)s'") % {'resource': resource, 'name': name}) - # 404 is used to simulate server side behavior - raise exceptions.NeutronClientException( - message=not_found_message, status_code=404) + # 404 is raised by exceptions.NotFound to simulate serverside behavior + raise exceptions.NotFound(message=not_found_message) else: return info[0] @@ -118,7 +116,7 @@ def find_resource_by_name_or_id(client, resource, name_or_id, try: return find_resource_by_id(client, resource, name_or_id, cmd_resource, parent_id, fields) - except exceptions.NeutronClientException: + except exceptions.NotFound: return _find_resource_by_name(client, resource, name_or_id, project_id, cmd_resource, parent_id, fields) diff --git a/neutronclient/tests/unit/test_name_or_id.py b/neutronclient/tests/unit/test_name_or_id.py index 412c45515..47b6b9b21 100644 --- a/neutronclient/tests/unit/test_name_or_id.py +++ b/neutronclient/tests/unit/test_name_or_id.py @@ -144,7 +144,7 @@ def test_get_id_from_name_notfound(self): try: neutronV20.find_resourceid_by_name_or_id( self.client, 'network', name) - except exceptions.NeutronClientException as ex: + except exceptions.NotFound as ex: self.assertIn('Unable to find', ex.message) self.assertEqual(404, ex.status_code) @@ -193,6 +193,6 @@ def test_get_id_from_name_multiple_with_project_not_found(self): try: neutronV20.find_resourceid_by_name_or_id( self.client, 'security_group', name, project) - except exceptions.NeutronClientException as ex: + except exceptions.NotFound as ex: self.assertIn('Unable to find', ex.message) self.assertEqual(404, ex.status_code) From 44da935264ab339d52bcbece1746c16301f28679 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Sat, 20 Feb 2016 22:00:30 +0000 Subject: [PATCH 375/845] Updated from global requirements Change-Id: Ie017f8c7acd17384b98743b79888c4e821d7497f --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c5280b98e..c1142eb00 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ iso8601>=0.1.9 # MIT netaddr!=0.7.16,>=0.7.12 # BSD oslo.i18n>=2.1.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 -oslo.utils>=3.4.0 # Apache-2.0 +oslo.utils>=3.5.0 # Apache-2.0 os-client-config>=1.13.1 # Apache-2.0 keystoneauth1>=2.1.0 # Apache-2.0 requests!=2.9.0,>=2.8.1 # Apache-2.0 From ed17ae60bd5fbcfffb94e91f270191f0b4d92970 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Sun, 6 Dec 2015 16:55:24 +0900 Subject: [PATCH 376/845] Improve str2dict key validation to avoid wrong keys This commit adds valid_keys and required_keys to str2dict and define a new function which can be used as argparse type validator. By this function, we can declare what fields are valid and what fields are required for dictionary option in option definition. Change-Id: Ib7b233e082c15d0bd6e16a754b8acad52e413986 Closes-Bug: #1102897 --- neutronclient/common/utils.py | 36 +++++++++++++++++-- neutronclient/neutron/v2_0/lb/v2/pool.py | 5 ++- neutronclient/neutron/v2_0/port.py | 19 ++++++---- neutronclient/neutron/v2_0/router.py | 12 ++++--- neutronclient/neutron/v2_0/subnet.py | 9 +++-- neutronclient/neutron/v2_0/vpn/ikepolicy.py | 4 +-- .../neutron/v2_0/vpn/ipsec_site_connection.py | 3 +- neutronclient/neutron/v2_0/vpn/ipsecpolicy.py | 4 +-- neutronclient/tests/unit/test_cli20_port.py | 9 ++--- neutronclient/tests/unit/test_utils.py | 30 ++++++++++++++++ .../tests/unit/vpn/test_cli20_ikepolicy.py | 18 +++++----- .../vpn/test_cli20_ipsec_site_connection.py | 8 +++-- .../tests/unit/vpn/test_cli20_ipsecpolicy.py | 24 ++++++++----- 13 files changed, 132 insertions(+), 49 deletions(-) diff --git a/neutronclient/common/utils.py b/neutronclient/common/utils.py index 4f19dac40..47610e60a 100644 --- a/neutronclient/common/utils.py +++ b/neutronclient/common/utils.py @@ -18,6 +18,7 @@ """Utilities and helper functions.""" import argparse +import functools import logging import netaddr import os @@ -108,10 +109,21 @@ def str2bool(strbool): return strbool.lower() == 'true' -def str2dict(strdict): +def str2dict(strdict, required_keys=None, optional_keys=None): """Convert key1=value1,key2=value2,... string into dictionary. - :param strdict: key1=value1,key2=value2 + :param strdict: string in the form of key1=value1,key2=value2 + :param required_keys: list of required keys. All keys in this list must be + specified. Otherwise ArgumentTypeError will be raised. + If this parameter is unspecified, no required key check + will be done. + :param optional_keys: list of optional keys. + This parameter is used for valid key check. + When at least one of required_keys and optional_keys, + a key must be a member of either of required_keys or + optional_keys. Otherwise, ArgumentTypeError will be + raised. When both required_keys and optional_keys are + unspecified, no valid key check will be done. """ result = {} if strdict: @@ -121,9 +133,29 @@ def str2dict(strdict): msg = _("invalid key-value '%s', expected format: key=value") raise argparse.ArgumentTypeError(msg % kv) result[key] = value + valid_keys = set(required_keys or []) | set(optional_keys or []) + if valid_keys: + invalid_keys = [k for k in result if k not in valid_keys] + if invalid_keys: + msg = _("Invalid key(s) '%(invalid_keys)s' specified. " + "Valid key(s): '%(valid_keys)s'.") + raise argparse.ArgumentTypeError( + msg % {'invalid_keys': ', '.join(sorted(invalid_keys)), + 'valid_keys': ', '.join(sorted(valid_keys))}) + if required_keys: + not_found_keys = [k for k in required_keys if k not in result] + if not_found_keys: + msg = _("Required key(s) '%s' not specified.") + raise argparse.ArgumentTypeError(msg % ', '.join(not_found_keys)) return result +def str2dict_type(optional_keys=None, required_keys=None): + return functools.partial(str2dict, + optional_keys=optional_keys, + required_keys=required_keys) + + def http_log_req(_logger, args, kwargs): if not _logger.isEnabledFor(logging.DEBUG): return diff --git a/neutronclient/neutron/v2_0/lb/v2/pool.py b/neutronclient/neutron/v2_0/lb/v2/pool.py index 3c1ea99b8..2745091e4 100644 --- a/neutronclient/neutron/v2_0/lb/v2/pool.py +++ b/neutronclient/neutron/v2_0/lb/v2/pool.py @@ -64,6 +64,8 @@ def add_known_arguments(self, parser): parser.add_argument( '--session-persistence', metavar='type=TYPE[,cookie_name=COOKIE_NAME]', + type=utils.str2dict_type(required_keys=['type'], + optional_keys=['cookie_name']), help=_('The type of session persistence to use and associated ' 'cookie name')) parser.add_argument( @@ -86,9 +88,6 @@ def add_known_arguments(self, parser): help=_('Protocol for balancing.')) def args2body(self, parsed_args): - if parsed_args.session_persistence: - parsed_args.session_persistence = utils.str2dict( - parsed_args.session_persistence) _listener_id = neutronV20.find_resourceid_by_name_or_id( self.get_client(), 'listener', parsed_args.listener) body = {'admin_state_up': parsed_args.admin_state, diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index 0e27cd227..9d71e1a4e 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -46,6 +46,7 @@ def _add_updatable_args(parser): parser.add_argument( '--fixed-ip', metavar='subnet_id=SUBNET,ip_address=IP_ADDR', action='append', + type=utils.str2dict_type(optional_keys=['subnet_id', 'ip_address']), help=_('Desired IP and/or subnet for this port: ' 'subnet_id=,ip_address=. ' 'You can repeat this option.')) @@ -73,13 +74,12 @@ def _updatable_args2body(parsed_args, body, client): ips = [] if parsed_args.fixed_ip: for ip_spec in parsed_args.fixed_ip: - ip_dict = utils.str2dict(ip_spec) - if 'subnet_id' in ip_dict: - subnet_name_id = ip_dict['subnet_id'] + if 'subnet_id' in ip_spec: + subnet_name_id = ip_spec['subnet_id'] _subnet_id = neutronV20.find_resourceid_by_name_or_id( client, 'subnet', subnet_name_id) - ip_dict['subnet_id'] = _subnet_id - ips.append(ip_dict) + ip_spec['subnet_id'] = _subnet_id + ips.append(ip_spec) if ips: body['fixed_ips'] = ips @@ -158,6 +158,9 @@ def add_arguments_extradhcpopt(self, parser): default=[], action='append', dest='extra_dhcp_opts', + type=utils.str2dict_type( + required_keys=['opt_name'], + optional_keys=['opt_value', 'ip_version']), help=_('Extra dhcp options to be assigned to this port: ' 'opt_name=,opt_value=,' 'ip_version={4,6}. You can repeat this option.')) @@ -174,7 +177,7 @@ def args2body_extradhcpopt(self, parsed_args, port): "ip_version={4,6}. " "You can repeat this option.") for opt in parsed_args.extra_dhcp_opts: - opt_ele.update(utils.str2dict(opt)) + opt_ele.update(opt) if ('opt_name' in opt_ele and ('opt_value' in opt_ele or 'ip_version' in opt_ele)): if opt_ele.get('opt_value') == 'null': @@ -199,7 +202,9 @@ def add_arguments_allowedaddresspairs(self, parser): default=[], action='append', dest='allowed_address_pairs', - type=utils.str2dict, + type=utils.str2dict_type( + required_keys=['ip_address'], + optional_keys=['mac_address']), help=_('Allowed address pair associated with the port.' 'You can repeat this option.')) group_aap.add_argument( diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index 88da856b3..c372f98ae 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -114,7 +114,8 @@ def add_known_arguments(self, parser): routes_group = parser.add_mutually_exclusive_group() routes_group.add_argument( '--route', metavar='destination=CIDR,nexthop=IP_ADDR', - action='append', dest='routes', type=utils.str2dict, + action='append', dest='routes', + type=utils.str2dict_type(required_keys=['destination', 'nexthop']), help=_('Route to associate with the router.' ' You can repeat this option.')) routes_group.add_argument( @@ -226,6 +227,8 @@ def get_parser(self, prog_name): parser.add_argument( '--fixed-ip', metavar='subnet_id=SUBNET,ip_address=IP_ADDR', action='append', + type=utils.str2dict_type(optional_keys=['subnet_id', + 'ip_address']), help=_('Desired IP and/or subnet on external network: ' 'subnet_id=,ip_address=. ' 'You can specify both of subnet_id and ip_address or ' @@ -246,13 +249,12 @@ def run(self, parsed_args): if parsed_args.fixed_ip: ips = [] for ip_spec in parsed_args.fixed_ip: - ip_dict = utils.str2dict(ip_spec) - subnet_name_id = ip_dict.get('subnet_id') + subnet_name_id = ip_spec.get('subnet_id') if subnet_name_id: subnet_id = neutronV20.find_resourceid_by_name_or_id( neutron_client, 'subnet', subnet_name_id) - ip_dict['subnet_id'] = subnet_id - ips.append(ip_dict) + ip_spec['subnet_id'] = subnet_id + ips.append(ip_spec) router_dict['external_fixed_ips'] = ips neutron_client.add_gateway_router(_router_id, router_dict) print(_('Set gateway for router %s') % parsed_args.router, diff --git a/neutronclient/neutron/v2_0/subnet.py b/neutronclient/neutron/v2_0/subnet.py index d8b594663..3abe313f2 100644 --- a/neutronclient/neutron/v2_0/subnet.py +++ b/neutronclient/neutron/v2_0/subnet.py @@ -62,16 +62,19 @@ def add_updatable_arguments(parser): help=_('No distribution of gateway.')) parser.add_argument( '--allocation-pool', metavar='start=IP_ADDR,end=IP_ADDR', - action='append', dest='allocation_pools', type=utils.str2dict, + action='append', dest='allocation_pools', + type=utils.str2dict_type(required_keys=['start', 'end']), help=_('Allocation pool IP addresses for this subnet ' '(This option can be repeated).')) parser.add_argument( '--allocation_pool', - action='append', dest='allocation_pools', type=utils.str2dict, + action='append', dest='allocation_pools', + type=utils.str2dict_type(required_keys=['start', 'end']), help=argparse.SUPPRESS) parser.add_argument( '--host-route', metavar='destination=CIDR,nexthop=IP_ADDR', - action='append', dest='host_routes', type=utils.str2dict, + action='append', dest='host_routes', + type=utils.str2dict_type(required_keys=['destination', 'nexthop']), help=_('Additional route (This option can be repeated).')) parser.add_argument( '--dns-nameserver', metavar='DNS_NAMESERVER', diff --git a/neutronclient/neutron/v2_0/vpn/ikepolicy.py b/neutronclient/neutron/v2_0/vpn/ikepolicy.py index 509083163..fe41655ee 100644 --- a/neutronclient/neutron/v2_0/vpn/ikepolicy.py +++ b/neutronclient/neutron/v2_0/vpn/ikepolicy.py @@ -71,7 +71,7 @@ def add_known_arguments(self, parser): parser.add_argument( '--lifetime', metavar="units=UNITS,value=VALUE", - type=utils.str2dict, + type=utils.str2dict_type(optional_keys=['units', 'value']), help=vpn_utils.lifetime_help("IKE")) parser.add_argument( 'name', metavar='NAME', @@ -100,7 +100,7 @@ def add_known_arguments(self, parser): parser.add_argument( '--lifetime', metavar="units=UNITS,value=VALUE", - type=utils.str2dict, + type=utils.str2dict_type(optional_keys=['units', 'value']), help=vpn_utils.lifetime_help("IKE")) def args2body(self, parsed_args): diff --git a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py index bace4d2e0..af596c3f2 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py +++ b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py @@ -55,7 +55,8 @@ def add_known_arguments(self, parser): parser.add_argument( '--dpd', metavar="action=ACTION,interval=INTERVAL,timeout=TIMEOUT", - type=utils.str2dict, + type=utils.str2dict_type( + optional_keys=['action', 'interval', 'timeout']), help=vpn_utils.dpd_help("IPsec connection.")) parser.add_argument( '--local-ep-group', diff --git a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py index 76f09142e..8f6296485 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py +++ b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py @@ -70,7 +70,7 @@ def add_known_arguments(self, parser): parser.add_argument( '--lifetime', metavar="units=UNITS,value=VALUE", - type=utils.str2dict, + type=utils.str2dict_type(optional_keys=['units', 'value']), help=vpn_utils.lifetime_help("IPsec")) parser.add_argument( 'name', metavar='NAME', @@ -99,7 +99,7 @@ def add_known_arguments(self, parser): parser.add_argument( '--lifetime', metavar="units=UNITS,value=VALUE", - type=utils.str2dict, + type=utils.str2dict_type(optional_keys=['units', 'value']), help=vpn_utils.lifetime_help("IPsec")) def args2body(self, parsed_args): diff --git a/neutronclient/tests/unit/test_cli20_port.py b/neutronclient/tests/unit/test_cli20_port.py index 4b6d617f7..6ed31342a 100644 --- a/neutronclient/tests/unit/test_cli20_port.py +++ b/neutronclient/tests/unit/test_cli20_port.py @@ -589,13 +589,14 @@ def test_update_port_fixed_ip(self): resource = 'port' cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) myid = 'myid' - net_id = 'net_id' + subnet_id = 'subnet_id' ip_addr = '123.123.123.123' args = [myid, - '--fixed-ip', "network_id=%(net_id)s,ip_address=%(ip_addr)s" % - {'net_id': net_id, + '--fixed-ip', + "subnet_id=%(subnet_id)s,ip_address=%(ip_addr)s" % + {'subnet_id': subnet_id, 'ip_addr': ip_addr}] - updated_fields = {"fixed_ips": [{'network_id': net_id, + updated_fields = {"fixed_ips": [{'subnet_id': subnet_id, 'ip_address': ip_addr}]} self._test_update_resource(resource, cmd, myid, args, updated_fields) diff --git a/neutronclient/tests/unit/test_utils.py b/neutronclient/tests/unit/test_utils.py index 27ebc0f17..44152418d 100644 --- a/neutronclient/tests/unit/test_utils.py +++ b/neutronclient/tests/unit/test_utils.py @@ -49,6 +49,36 @@ def test_invalid_string_to_dictionary(self): self.assertRaises(argparse.ArgumentTypeError, utils.str2dict, input_str) + def test_str2dict_optional_keys(self): + self.assertDictEqual({'key1': 'value1'}, + utils.str2dict('key1=value1', + optional_keys=['key1', 'key2'])) + self.assertDictEqual({'key1': 'value1', 'key2': 'value2'}, + utils.str2dict('key1=value1,key2=value2', + optional_keys=['key1', 'key2'])) + e = self.assertRaises(argparse.ArgumentTypeError, + utils.str2dict, + 'key1=value1,key2=value2,key3=value3', + optional_keys=['key1', 'key2']) + self.assertEqual("Invalid key(s) 'key3' specified. " + "Valid key(s): 'key1, key2'.", + str(e)) + + def test_str2dict_required_keys(self): + self.assertDictEqual( + {'key1': 'value1', 'key2': 'value2', 'key3': 'value3'}, + utils.str2dict('key1=value1,key2=value2,key3=value3', + required_keys=['key1', 'key2'], + optional_keys=['key3'])) + self.assertDictEqual( + {'key1': 'value1', 'key2': 'value2'}, + utils.str2dict('key1=value1,key2=value2', + required_keys=['key1', 'key2'])) + e = self.assertRaises(argparse.ArgumentTypeError, + utils.str2dict, 'key1=value1', + required_keys=['key1', 'key2']) + self.assertEqual("Required key(s) 'key2' not specified.", str(e)) + def test_get_dict_item_properties(self): item = {'name': 'test_name', 'id': 'test_id'} fields = ('name', 'id') diff --git a/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py b/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py index d7d1bbfe9..c6ac8fb78 100644 --- a/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py +++ b/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py @@ -16,6 +16,7 @@ import sys +from neutronclient.common import exceptions from neutronclient.neutron.v2_0.vpn import ikepolicy from neutronclient.tests.unit import test_cli20 @@ -101,7 +102,7 @@ def test_create_ikepolicy_with_limited_params(self): self._test_create_resource(resource, cmd, name, my_id, args, position_names, position_values) - def _test_lifetime_values(self, lifetime): + def _test_lifetime_values(self, lifetime, expected_exc=None): resource = 'ikepolicy' cmd = ikepolicy.CreateIKEPolicy(test_cli20.MyApp(sys.stdout), None) name = 'ikepolicy1' @@ -134,16 +135,17 @@ def _test_lifetime_values(self, lifetime): auth_algorithm, encryption_algorithm, phase1_negotiation_mode, ike_version, pfs, tenant_id] - try: - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values) - except Exception: - return - self.fail("IKEPolicy Lifetime Error") + if not expected_exc: + expected_exc = exceptions.CommandError + self.assertRaises( + expected_exc, + self._test_create_resource, + resource, cmd, name, my_id, args, + position_names, position_values) def test_create_ikepolicy_with_invalid_lifetime_keys(self): lifetime = 'uts=seconds,val=20000' - self._test_lifetime_values(lifetime) + self._test_lifetime_values(lifetime, expected_exc=SystemExit) def test_create_ikepolicy_with_invalid_lifetime_value(self): lifetime = 'units=seconds,value=-1' diff --git a/neutronclient/tests/unit/vpn/test_cli20_ipsec_site_connection.py b/neutronclient/tests/unit/vpn/test_cli20_ipsec_site_connection.py index 85a28b60d..8ead88f2a 100644 --- a/neutronclient/tests/unit/vpn/test_cli20_ipsec_site_connection.py +++ b/neutronclient/tests/unit/vpn/test_cli20_ipsec_site_connection.py @@ -180,7 +180,7 @@ def test_create_ipsec_site_connection_with_limited_params(self): self._test_create_resource(resource, cmd, None, my_id, args, position_names, position_values) - def _test_create_failure(self, additional_args=None): + def _test_create_failure(self, additional_args=None, expected_exc=None): # Helper to test failure of IPSec site-to-site creation failure. resource = 'ipsec_site_connection' cmd = ipsec_site_connection.CreateIPsecSiteConnection( @@ -215,7 +215,9 @@ def _test_create_failure(self, additional_args=None): position_values = [tenant_id, admin_state, peer_address, peer_id, psk, mtu, initiator, None, None, vpnservice_id, ikepolicy_id, ipsecpolicy_id] - self.assertRaises(exceptions.CommandError, + if not expected_exc: + expected_exc = exceptions.CommandError + self.assertRaises(expected_exc, self._test_create_resource, resource, cmd, None, my_id, args, position_names, position_values) @@ -227,7 +229,7 @@ def test_fail_create_with_invalid_mtu(self): def test_fail_create_with_invalid_dpd_keys(self): bad_dpd_key = ['--dpd', 'act=restart,interval=30,time=120'] - self._test_create_failure(bad_dpd_key) + self._test_create_failure(bad_dpd_key, SystemExit) def test_fail_create_with_invalid_dpd_values(self): bad_dpd_values = ['--dpd', 'action=hold,interval=30,timeout=-1'] diff --git a/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py b/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py index 5df90ba4e..c6b23e19c 100644 --- a/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py +++ b/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py @@ -16,6 +16,7 @@ import sys +from neutronclient.common import exceptions from neutronclient.neutron.v2_0.vpn import ipsecpolicy from neutronclient.tests.unit import test_cli20 @@ -98,7 +99,7 @@ def test_create_ipsecpolicy_with_limited_params(self): self._test_create_resource(resource, cmd, name, my_id, args, position_names, position_values) - def _test_lifetime_values(self, lifetime): + def _test_lifetime_values(self, lifetime, expected_exc=None): resource = 'ipsecpolicy' cmd = ipsecpolicy.CreateIPsecPolicy(test_cli20.MyApp(sys.stdout), None) name = 'ipsecpolicy1' @@ -131,19 +132,24 @@ def _test_lifetime_values(self, lifetime): auth_algorithm, encryption_algorithm, phase1_negotiation_mode, ike_version, pfs, tenant_id] - try: - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values) - except Exception: - return - self.fail("IPsecPolicy Lifetime Error") + if not expected_exc: + expected_exc = exceptions.CommandError + self.assertRaises( + expected_exc, + self._test_create_resource, + resource, cmd, name, my_id, args, + position_names, position_values) def test_create_ipsecpolicy_with_invalid_lifetime_keys(self): lifetime = 'uts=seconds,val=20000' + self._test_lifetime_values(lifetime, SystemExit) + + def test_create_ipsecpolicy_with_invalid_lifetime_units(self): + lifetime = 'units=minutes,value=600' self._test_lifetime_values(lifetime) - def test_create_ipsecpolicy_with_invalide_lifetime_values(self): - lifetime = 'units=minutes,value=0' + def test_create_ipsecpolicy_with_invalid_lifetime_value(self): + lifetime = 'units=seconds,value=0' self._test_lifetime_values(lifetime) def test_list_ipsecpolicy(self): From 8d4c0e4ac2e52af0e1ec4952f31aed851d1815e9 Mon Sep 17 00:00:00 2001 From: Miguel Lavalle Date: Sat, 20 Feb 2016 01:04:36 +0000 Subject: [PATCH 377/845] Add DNS integration support to the client DNS integration has been added to Neutron. This commit complements that by adding --dns-name and --dns-domain arguments to create and update commands of networks, ports and floating IPs. Change-Id: I7a6ec05b6d7483fceb35f586ac476e8713904b59 Closes-Bug: #1547736 --- neutronclient/neutron/v2_0/dns.py | 67 +++++++++++++++++++ neutronclient/neutron/v2_0/floatingip.py | 5 ++ neutronclient/neutron/v2_0/network.py | 5 ++ neutronclient/neutron/v2_0/port.py | 5 ++ .../tests/unit/test_cli20_floatingips.py | 15 +++++ .../tests/unit/test_cli20_network.py | 29 ++++++++ neutronclient/tests/unit/test_cli20_port.py | 30 +++++++++ 7 files changed, 156 insertions(+) create mode 100644 neutronclient/neutron/v2_0/dns.py diff --git a/neutronclient/neutron/v2_0/dns.py b/neutronclient/neutron/v2_0/dns.py new file mode 100644 index 000000000..0cf7ec0b4 --- /dev/null +++ b/neutronclient/neutron/v2_0/dns.py @@ -0,0 +1,67 @@ +# Copyright (c) 2016 IBM +# 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. + +from neutronclient._i18n import _ + + +def add_dns_argument_create(parser, resource, attribute): + # Add dns_name and dns_domain support to network, port and floatingip + # create + argument = '--dns-%s' % attribute + parser.add_argument( + argument, + help=_('Assign DNS %(attribute)s to the %(resource)s ' + '(requires DNS integration ' + 'extension)') % {'attribute': attribute, 'resource': resource}) + + +def args2body_dns_create(parsed_args, resource, attribute): + # Add dns_name and dns_domain support to network, port and floatingip + # create + destination = 'dns_%s' % attribute + argument = getattr(parsed_args, destination) + if argument: + resource[destination] = argument + + +def add_dns_argument_update(parser, resource, attribute): + # Add dns_name and dns_domain support to network, port and floatingip + # update + argument = '--dns-%s' % attribute + no_argument = '--no-dns-%s' % attribute + dns_args = parser.add_mutually_exclusive_group() + dns_args.add_argument( + argument, + help=_('Assign DNS %(attribute)s to the %(resource)s ' + '(requires DNS integration ' + 'extension.)') % {'attribute': attribute, 'resource': resource}) + dns_args.add_argument( + no_argument, action='store_true', + help=_('Unassign DNS %(attribute)s from the %(resource)s ' + '(requires DNS integration ' + 'extension.)') % {'attribute': attribute, 'resource': resource}) + + +def args2body_dns_update(parsed_args, resource, attribute): + # Add dns_name and dns_domain support to network, port and floatingip + # update + destination = 'dns_%s' % attribute + no_destination = 'no_dns_%s' % attribute + argument = getattr(parsed_args, destination) + no_argument = getattr(parsed_args, no_destination) + if argument: + resource[destination] = argument + if no_argument: + resource[destination] = "" diff --git a/neutronclient/neutron/v2_0/floatingip.py b/neutronclient/neutron/v2_0/floatingip.py index 7e634e9da..e361a293a 100644 --- a/neutronclient/neutron/v2_0/floatingip.py +++ b/neutronclient/neutron/v2_0/floatingip.py @@ -20,6 +20,7 @@ from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronV20 +from neutronclient.neutron.v2_0 import dns class ListFloatingIP(neutronV20.ListCommand): @@ -68,6 +69,8 @@ def add_known_arguments(self, parser): '--subnet', dest='subnet_id', help=_('Subnet ID on which you want to create the floating IP.')) + dns.add_dns_argument_create(parser, self.resource, 'domain') + dns.add_dns_argument_create(parser, self.resource, 'name') def args2body(self, parsed_args): _network_id = neutronV20.find_resourceid_by_name_or_id( @@ -77,6 +80,8 @@ def args2body(self, parsed_args): ['port_id', 'tenant_id', 'fixed_ip_address', 'floating_ip_address', 'subnet_id']) + dns.args2body_dns_create(parsed_args, body, 'domain') + dns.args2body_dns_create(parsed_args, body, 'name') return {self.resource: body} diff --git a/neutronclient/neutron/v2_0/network.py b/neutronclient/neutron/v2_0/network.py index e5ac3bd7b..2fb26d327 100644 --- a/neutronclient/neutron/v2_0/network.py +++ b/neutronclient/neutron/v2_0/network.py @@ -21,6 +21,7 @@ from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronV20 from neutronclient.neutron.v2_0 import availability_zone +from neutronclient.neutron.v2_0 import dns from neutronclient.neutron.v2_0.qos import policy as qos_policy @@ -165,6 +166,7 @@ def add_known_arguments(self, parser): self.add_arguments_qos_policy(parser) availability_zone.add_az_hint_argument(parser, self.resource) + dns.add_dns_argument_create(parser, self.resource, 'domain') def args2body(self, parsed_args): body = {'name': parsed_args.name, @@ -178,6 +180,7 @@ def args2body(self, parsed_args): self.args2body_qos_policy(parsed_args, body) availability_zone.args2body_az_hint(parsed_args, body) + dns.args2body_dns_create(parsed_args, body, 'domain') return {'network': body} @@ -195,8 +198,10 @@ class UpdateNetwork(neutronV20.UpdateCommand, qos_policy.UpdateQosPolicyMixin): def add_known_arguments(self, parser): self.add_arguments_qos_policy(parser) + dns.add_dns_argument_update(parser, self.resource, 'domain') def args2body(self, parsed_args): body = {} self.args2body_qos_policy(parsed_args, body) + dns.args2body_dns_update(parsed_args, body, 'domain') return {'network': body} diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index 0e27cd227..29426ee58 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -22,6 +22,7 @@ from neutronclient.common import exceptions from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronV20 +from neutronclient.neutron.v2_0 import dns from neutronclient.neutron.v2_0.qos import policy as qos_policy @@ -263,6 +264,7 @@ def add_known_arguments(self, parser): parser.add_argument( 'network_id', metavar='NETWORK', help=_('Network ID or name this port belongs to.')) + dns.add_dns_argument_create(parser, self.resource, 'name') def args2body(self, parsed_args): client = self.get_client() @@ -283,6 +285,7 @@ def args2body(self, parsed_args): self.args2body_extradhcpopt(parsed_args, body) self.args2body_qos_policy(parsed_args, body) self.args2body_allowedaddresspairs(parsed_args, body) + dns.args2body_dns_create(parsed_args, body, 'name') return {'port': body} @@ -314,6 +317,7 @@ def add_known_arguments(self, parser): self.add_arguments_extradhcpopt(parser) self.add_arguments_qos_policy(parser) self.add_arguments_allowedaddresspairs(parser) + dns.add_dns_argument_update(parser, self.resource, 'name') def args2body(self, parsed_args): body = {} @@ -326,5 +330,6 @@ def args2body(self, parsed_args): self.args2body_extradhcpopt(parsed_args, body) self.args2body_qos_policy(parsed_args, body) self.args2body_allowedaddresspairs(parsed_args, body) + dns.args2body_dns_update(parsed_args, body, 'name') return {'port': body} diff --git a/neutronclient/tests/unit/test_cli20_floatingips.py b/neutronclient/tests/unit/test_cli20_floatingips.py index d500af811..cca3706ce 100644 --- a/neutronclient/tests/unit/test_cli20_floatingips.py +++ b/neutronclient/tests/unit/test_cli20_floatingips.py @@ -117,6 +117,21 @@ def test_create_floatingip_with_subnet_id_and_port(self): self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) + def test_create_floatingip_with_dns_name_and_dns_domain(self): + # Create floatingip: fip1 with dns name and dns domain. + resource = 'floatingip' + cmd = fip.CreateFloatingIP(test_cli20.MyApp(sys.stdout), None) + name = 'fip1' + myid = 'myid' + dns_name_name = 'my-floatingip' + dns_domain_name = 'my-domain.org.' + args = [name, '--dns-name', dns_name_name, '--dns-domain', + dns_domain_name] + position_names = ['floating_network_id', 'dns_name', 'dns_domain'] + position_values = [name, dns_name_name, dns_domain_name] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + def test_list_floatingips(self): # list floatingips: -D. resources = 'floatingips' diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index 0465b88dd..dc8fd72ae 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -163,6 +163,19 @@ def test_create_network_with_az_hint(self): self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) + def test_create_network_with_dns_domain(self): + # Create net: --dns-domain my-domain.org. + resource = 'network' + cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + dns_domain_name = 'my-domain.org.' + args = [name, '--dns-domain', dns_domain_name] + position_names = ['name', 'dns_domain'] + position_values = [name, dns_domain_name] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + def test_list_nets_empty_with_column(self): resources = "networks" cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) @@ -528,6 +541,22 @@ def test_update_network_with_no_qos_policy(self): ['myid', '--no-qos-policy'], {'qos_policy_id': None, }) + def test_update_network_with_dns_domain(self): + # Update net: myid --dns-domain my-domain.org. + resource = 'network' + cmd = network.UpdateNetwork(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--dns-domain', 'my-domain.org.'], + {'dns_domain': 'my-domain.org.', }) + + def test_update_network_with_no_dns_domain(self): + # Update net: myid --no-dns-domain + resource = 'network' + cmd = network.UpdateNetwork(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--no-dns-domain'], + {'dns_domain': "", }) + def test_show_network(self): # Show net: --fields id --fields name myid. resource = 'network' diff --git a/neutronclient/tests/unit/test_cli20_port.py b/neutronclient/tests/unit/test_cli20_port.py index 4b6d617f7..8f502ebae 100644 --- a/neutronclient/tests/unit/test_cli20_port.py +++ b/neutronclient/tests/unit/test_cli20_port.py @@ -338,6 +338,20 @@ def test_create_port_with_qos_policy(self): self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) + def test_create_port_with_dns_name(self): + # Create port: --dns-name my-port. + resource = 'port' + cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + netid = 'netid' + dns_name_name = 'my-port' + args = [netid, '--dns-name', dns_name_name] + position_names = ['network_id', 'dns_name'] + position_values = [netid, dns_name_name] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + def test_create_port_with_allowed_address_pair_ipaddr(self): # Create port: # --allowed-address-pair ip_address=addr0 @@ -648,6 +662,22 @@ def test_update_port_with_no_qos_policy(self): ['myid', '--no-qos-policy'], {'qos_policy_id': None, }) + def test_update_port_with_dns_name(self): + # Update port: myid --dns-name my-port. + resource = 'port' + cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--dns-name', 'my-port'], + {'dns_name': 'my-port', }) + + def test_update_port_with_no_dns_name(self): + # Update port: myid --no-dns-name + resource = 'port' + cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--no-dns-name'], + {'dns_name': "", }) + def test_delete_extra_dhcp_opts_from_port(self): resource = 'port' myid = 'myid' From 0dd54a08c63195046eede744b2d7051eaafae5fc Mon Sep 17 00:00:00 2001 From: Brandon Palm Date: Tue, 23 Feb 2016 14:06:12 -0600 Subject: [PATCH 378/845] Use instanceof instead of type Adjusted conditional statements to use instanceof when comparing variables. Instanceof supports inheritance type checking better than type. Change-Id: I873ef7d5e283ee70f1548f040f1c1d9a675c7159 Closes-Bug: 1548974 --- neutronclient/neutron/v2_0/__init__.py | 2 +- neutronclient/v2_0/client.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index 216677537..f3f466957 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -351,7 +351,7 @@ def _merge_args(qCmd, parsed_args, _extra_values, value_specs): if isinstance(arg_value, list): if value and isinstance(value, list): if (not arg_value or - type(arg_value[0]) == type(value[0])): + isinstance(arg_value[0], type(value[0]))): arg_value.extend(value) _extra_values.pop(key) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index cc323c957..69cc64f5c 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -185,7 +185,7 @@ def do_request(self, method, action, body=None, headers=None, params=None): # Add format and tenant_id action += ".%s" % self.format action = self.action_prefix + action - if type(params) is dict and params: + if isinstance(params, dict) and params: params = utils.safe_encode_dict(params) action += '?' + urlparse.urlencode(params, doseq=1) @@ -216,7 +216,7 @@ def serialize(self, data): """ if data is None: return None - elif type(data) is dict: + elif isinstance(data, dict): return serializer.Serializer().serialize(data) else: raise Exception(_("Unable to serialize object of type = '%s'") % From af1a55bfd2e47b0e3cd8349f0a9b1277474fee18 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Fri, 4 Dec 2015 01:53:58 +0900 Subject: [PATCH 379/845] Ensure to use exception per status code for all cases Previously, only when an exception has a content with {'NeutronError': {'type': xxxx, 'message': xxxx}}, exception per status code is raised from neutronclient library. There are cases where this kind of message is not contained in exception messages, for example, some extension is loaded. Library users expect an exception is raised based on response status code and it should not depend on an exception message. This commit applies a fallback logic to map generic per-status exception to all exception types from the neutron server. Closes-Bug: #1513879 Change-Id: Ib3d0a8359aed444b12217b3404d40443d61fc2c0 --- neutronclient/tests/unit/test_cli20.py | 32 ++++++++++-------- neutronclient/v2_0/client.py | 46 +++++++++++--------------- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 8aa0aabe3..e3a1014eb 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -713,6 +713,8 @@ def _test_exception_handler_v20( client.exception_handler_v20, status_code, error_content) self.assertEqual(status_code, e.status_code) + self.assertEqual(expected_exception.__name__, + e.__class__.__name__) if expected_msg is None: if error_detail: @@ -780,32 +782,36 @@ def test_exception_handler_v20_neutron_unknown_status_code(self): 'UnknownError', error_msg, error_detail) def test_exception_handler_v20_bad_neutron_error(self): - error_content = {'NeutronError': {'unknown_key': 'UNKNOWN'}} - self._test_exception_handler_v20( - exceptions.NeutronClientException, 500, - expected_msg={'unknown_key': 'UNKNOWN'}, - error_content=error_content) + for status_code, client_exc in exceptions.HTTP_EXCEPTION_MAP.items(): + error_content = {'NeutronError': {'unknown_key': 'UNKNOWN'}} + self._test_exception_handler_v20( + client_exc, status_code, + expected_msg="{'unknown_key': 'UNKNOWN'}", + error_content=error_content) def test_exception_handler_v20_error_dict_contains_message(self): error_content = {'message': 'This is an error message'} - self._test_exception_handler_v20( - exceptions.NeutronClientException, 500, - expected_msg='This is an error message', - error_content=error_content) + for status_code, client_exc in exceptions.HTTP_EXCEPTION_MAP.items(): + self._test_exception_handler_v20( + client_exc, status_code, + expected_msg='This is an error message', + error_content=error_content) def test_exception_handler_v20_error_dict_not_contain_message(self): + # 599 is not contained in HTTP_EXCEPTION_MAP. error_content = {'error': 'This is an error message'} - expected_msg = '%s-%s' % (500, error_content) + expected_msg = '%s-%s' % (599, error_content) self._test_exception_handler_v20( - exceptions.NeutronClientException, 500, + exceptions.NeutronClientException, 599, expected_msg=expected_msg, error_content=error_content) def test_exception_handler_v20_default_fallback(self): + # 599 is not contained in HTTP_EXCEPTION_MAP. error_content = 'This is an error message' - expected_msg = '%s-%s' % (500, error_content) + expected_msg = '%s-%s' % (599, error_content) self._test_exception_handler_v20( - exceptions.NeutronClientException, 500, + exceptions.NeutronClientException, 599, expected_msg=expected_msg, error_content=error_content) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index cc323c957..08a850314 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -47,7 +47,7 @@ def exception_handler_v20(status_code, error_content): if isinstance(error_content, dict): error_dict = error_content.get('NeutronError') # Find real error type - bad_neutron_error_flag = False + client_exc = None if error_dict: # If Neutron key is found, it will definitely contain # a 'message' and 'type' keys? @@ -56,35 +56,29 @@ def exception_handler_v20(status_code, error_content): error_message = error_dict['message'] if error_dict['detail']: error_message += "\n" + error_dict['detail'] - except Exception: - bad_neutron_error_flag = True - if not bad_neutron_error_flag: # If corresponding exception is defined, use it. client_exc = getattr(exceptions, '%sClient' % error_type, None) - # Otherwise look up per status-code client exception - if not client_exc: - client_exc = exceptions.HTTP_EXCEPTION_MAP.get(status_code) - if client_exc: - raise client_exc(message=error_message, - status_code=status_code) - else: - raise exceptions.NeutronClientException( - status_code=status_code, message=error_message) - else: - raise exceptions.NeutronClientException(status_code=status_code, - message=error_dict) + except Exception: + error_message = "%s" % error_dict else: - message = None + error_message = None if isinstance(error_content, dict): - message = error_content.get('message') - if message: - raise exceptions.NeutronClientException(status_code=status_code, - message=message) - - # If we end up here the exception was not a neutron error - msg = "%s-%s" % (status_code, error_content) - raise exceptions.NeutronClientException(status_code=status_code, - message=msg) + error_message = error_content.get('message') + if not error_message: + # If we end up here the exception was not a neutron error + error_message = "%s-%s" % (status_code, error_content) + + # If an exception corresponding to the error type is not found, + # look up per status-code client exception. + if not client_exc: + client_exc = exceptions.HTTP_EXCEPTION_MAP.get(status_code) + # If there is no exception per status-code, + # Use NeutronClientException as fallback. + if not client_exc: + client_exc = exceptions.NeutronClientException + + raise client_exc(message=error_message, + status_code=status_code) class APIParamsCall(object): From 150cc4ce56fdf9f80a11fd5a7ca9cab4e7bc2c62 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Wed, 24 Feb 2016 20:47:32 +0900 Subject: [PATCH 380/845] Do not print 'Created' message when using non-table formatter When using non-table formatter like JSON or YAML formatter, users want to pass output result to some other program. Printing 'Created' message breaks the data format and non-table formatter makes useless. Functional tests for non-table formatters is added to avoid the same thing again. Change-Id: Ieb38bd26d26d134d4d274ad2d9f4d1e79fa4e977 Closes-Bug: #1548897 --- neutronclient/neutron/v2_0/__init__.py | 5 +- .../functional/core/test_cli_formatter.py | 59 +++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 neutronclient/tests/functional/core/test_cli_formatter.py diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index 216677537..d915a9567 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -481,8 +481,9 @@ def take_action(self, parsed_args): self.format_output_data(data) info = self.resource in data and data[self.resource] or None if info: - print(_('Created a new %s:') % self.resource, - file=self.app.stdout) + if parsed_args.formatter == 'table': + print(_('Created a new %s:') % self.resource, + file=self.app.stdout) else: info = {'': ''} return zip(*sorted(six.iteritems(info))) diff --git a/neutronclient/tests/functional/core/test_cli_formatter.py b/neutronclient/tests/functional/core/test_cli_formatter.py new file mode 100644 index 000000000..cd133dee1 --- /dev/null +++ b/neutronclient/tests/functional/core/test_cli_formatter.py @@ -0,0 +1,59 @@ +# Copyright 2016 NEC Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from oslo_serialization import jsonutils +from oslo_utils import uuidutils +import yaml + +from neutronclient.tests.functional import base + + +class TestCLIFormatter(base.ClientTestBase): + + def setUp(self): + super(TestCLIFormatter, self).setUp() + self.net_name = 'net-%s' % uuidutils.generate_uuid() + self.addCleanup(self.neutron, 'net-delete %s' % self.net_name) + + def _create_net(self, fmt, col_attrs): + params = ['-c %s' % attr for attr in col_attrs] + params.append('-f %s' % fmt) + params.append(self.net_name) + param_string = ' '.join(params) + return self.neutron('net-create', params=param_string) + + def test_net_create_with_json_formatter(self): + result = self._create_net('json', ['name', 'admin_state_up']) + self.assertDictEqual({'name': self.net_name, + 'admin_state_up': True}, + jsonutils.loads(result)) + + def test_net_create_with_yaml_formatter(self): + result = self._create_net('yaml', ['name', 'admin_state_up']) + self.assertDictEqual({'name': self.net_name, + 'admin_state_up': True}, + yaml.load(result)) + + def test_net_create_with_value_formatter(self): + # NOTE(amotoki): In 'value' formatter, there is no guarantee + # in the order of attribute, so we use one attribute in this test. + result = self._create_net('value', ['name']) + self.assertEqual(self.net_name, result.strip()) + + def test_net_create_with_shell_formatter(self): + result = self._create_net('shell', ['name', 'admin_state_up']) + result_lines = set(result.strip().split('\n')) + self.assertSetEqual(set(['name="%s"' % self.net_name, + 'admin_state_up="True"']), + result_lines) From e0667e96cfb9dedcdcf0eb5bbafe6efb4cf5c437 Mon Sep 17 00:00:00 2001 From: Carl Baldwin Date: Fri, 19 Feb 2016 23:28:49 +0000 Subject: [PATCH 381/845] Add use_default_subnetpool to subnet create requests Change-Id: I7dcbc8477b2ffa776a9bb272b896a5adfca860ae Depends-On: Ifff57c0485e4727f352b2cc2bd1bdaabd0f1606b Closes-Bug: #1547705 --- neutronclient/neutron/v2_0/subnet.py | 6 ++++++ neutronclient/tests/unit/test_cli20_subnet.py | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/neutronclient/neutron/v2_0/subnet.py b/neutronclient/neutron/v2_0/subnet.py index d8b594663..3ba4f170a 100644 --- a/neutronclient/neutron/v2_0/subnet.py +++ b/neutronclient/neutron/v2_0/subnet.py @@ -187,6 +187,10 @@ def add_known_arguments(self, parser): '--subnetpool', metavar='SUBNETPOOL', help=_('ID or name of subnetpool from which this subnet ' 'will obtain a CIDR.')) + parser.add_argument( + '--use-default-subnetpool', + action='store_true', + help=_('Use default subnetpool for ip_version, if it exists.')) parser.add_argument( '--prefixlen', metavar='PREFIX_LENGTH', help=_('Prefix length for subnet allocation from subnetpool.')) @@ -199,6 +203,8 @@ def args2body(self, parsed_args): if parsed_args.prefixlen: body['prefixlen'] = parsed_args.prefixlen ip_version = parsed_args.ip_version + if parsed_args.use_default_subnetpool: + body['use_default_subnetpool'] = True if parsed_args.subnetpool: if parsed_args.subnetpool == 'None': _subnetpool_id = None diff --git a/neutronclient/tests/unit/test_cli20_subnet.py b/neutronclient/tests/unit/test_cli20_subnet.py index 4a28198f5..a1ebf8952 100644 --- a/neutronclient/tests/unit/test_cli20_subnet.py +++ b/neutronclient/tests/unit/test_cli20_subnet.py @@ -290,6 +290,25 @@ def test_create_subnet_dns_nameservers(self): position_names, position_values, tenant_id='tenantid') + def test_create_subnet_with_use_default_subnetpool(self): + # Create subnet: --tenant-id tenantid --use-default-subnetpool \ + # netid cidr. + resource = 'subnet' + cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + netid = 'netid' + cidr = 'prefixvalue' + args = ['--tenant_id', 'tenantid', + '--use-default-subnetpool', + netid, cidr] + position_names = ['ip_version', 'use_default_subnetpool', 'network_id', + 'cidr'] + position_values = [4, True, netid, cidr] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values, + tenant_id='tenantid') + def test_create_subnet_with_disable_dhcp(self): # Create subnet: --tenant-id tenantid --disable-dhcp netid cidr. resource = 'subnet' From 3c26455a03e1144d582007f24c61be0100bb25a6 Mon Sep 17 00:00:00 2001 From: "vikram.choudhary" Date: Thu, 11 Feb 2016 15:10:11 +0530 Subject: [PATCH 382/845] BGP Dynamic Routing: neutronclient changes This patch adds neutronclient support for BGP routing functionality. Partially-Implements: blueprint bgp-dynamic-routing Co-Authored-By: Ryan Tidwell Co-Authored-By: Numan Siddique Co-Authored-By: Jaume Devesa Change-Id: I5b20bcbf6c837495d81c395f600498d2c8f3495c --- neutronclient/neutron/v2_0/bgp/__init__.py | 0 .../neutron/v2_0/bgp/dragentscheduler.py | 117 ++++++++ neutronclient/neutron/v2_0/bgp/peer.py | 127 ++++++++ neutronclient/neutron/v2_0/bgp/speaker.py | 277 ++++++++++++++++++ neutronclient/shell.py | 32 ++ neutronclient/tests/unit/bgp/__init__.py | 0 .../unit/bgp/test_cli20_dragentscheduler.py | 66 +++++ .../tests/unit/bgp/test_cli20_peer.py | 223 ++++++++++++++ .../tests/unit/bgp/test_cli20_speaker.py | 267 +++++++++++++++++ neutronclient/v2_0/client.py | 117 ++++++++ .../bgp-dynamic-routing-b97a1c81d3007049.yaml | 5 + 11 files changed, 1231 insertions(+) create mode 100644 neutronclient/neutron/v2_0/bgp/__init__.py create mode 100644 neutronclient/neutron/v2_0/bgp/dragentscheduler.py create mode 100644 neutronclient/neutron/v2_0/bgp/peer.py create mode 100755 neutronclient/neutron/v2_0/bgp/speaker.py create mode 100644 neutronclient/tests/unit/bgp/__init__.py create mode 100644 neutronclient/tests/unit/bgp/test_cli20_dragentscheduler.py create mode 100644 neutronclient/tests/unit/bgp/test_cli20_peer.py create mode 100644 neutronclient/tests/unit/bgp/test_cli20_speaker.py create mode 100644 releasenotes/notes/bgp-dynamic-routing-b97a1c81d3007049.yaml diff --git a/neutronclient/neutron/v2_0/bgp/__init__.py b/neutronclient/neutron/v2_0/bgp/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/neutron/v2_0/bgp/dragentscheduler.py b/neutronclient/neutron/v2_0/bgp/dragentscheduler.py new file mode 100644 index 000000000..81ce8f1ec --- /dev/null +++ b/neutronclient/neutron/v2_0/bgp/dragentscheduler.py @@ -0,0 +1,117 @@ +# Copyright 2016 Huawei Technologies India Pvt. Ltd. +# 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. +# + +from __future__ import print_function + +from neutronclient._i18n import _ +from neutronclient.neutron import v2_0 as neutronV20 +from neutronclient.neutron.v2_0.bgp import speaker as bgp_speaker + + +def add_common_args(parser): + parser.add_argument('dragent_id', + metavar='BGP_DRAGENT_ID', + help=_('ID of the Dynamic Routing agent.')) + parser.add_argument('bgp_speaker', + metavar='BGP_SPEAKER', + help=_('ID or name of the BGP speaker.')) + + +class AddBGPSpeakerToDRAgent(neutronV20.NeutronCommand): + """Add a BGP speaker to a Dynamic Routing agent.""" + + def get_parser(self, prog_name): + parser = super(AddBGPSpeakerToDRAgent, self).get_parser(prog_name) + add_common_args(parser) + return parser + + def take_action(self, parsed_args): + neutron_client = self.get_client() + _speaker_id = bgp_speaker.get_bgp_speaker_id(neutron_client, + parsed_args.bgp_speaker) + neutron_client.add_bgp_speaker_to_dragent( + parsed_args.dragent_id, {'bgp_speaker_id': _speaker_id}) + print(_('Associated BGP speaker %s to the Dynamic Routing agent.') + % parsed_args.bgp_speaker, file=self.app.stdout) + + +class RemoveBGPSpeakerFromDRAgent(neutronV20.NeutronCommand): + """Removes a BGP speaker from a Dynamic Routing agent.""" + + def get_parser(self, prog_name): + parser = super(RemoveBGPSpeakerFromDRAgent, self).get_parser( + prog_name) + add_common_args(parser) + return parser + + def take_action(self, parsed_args): + neutron_client = self.get_client() + _speaker_id = bgp_speaker.get_bgp_speaker_id(neutron_client, + parsed_args.bgp_speaker) + neutron_client.remove_bgp_speaker_from_dragent(parsed_args.dragent_id, + _speaker_id) + print(_('Disassociated BGP speaker %s from the ' + 'Dynamic Routing agent.') + % parsed_args.bgp_speaker, file=self.app.stdout) + + +class ListBGPSpeakersOnDRAgent(neutronV20.ListCommand): + """List BGP speakers hosted by a Dynamic Routing agent.""" + + list_columns = ['id', 'name', 'local_as', 'ip_version'] + resource = 'bgp_speaker' + + def get_parser(self, prog_name): + parser = super(ListBGPSpeakersOnDRAgent, + self).get_parser(prog_name) + parser.add_argument( + 'dragent_id', + metavar='BGP_DRAGENT_ID', + help=_('ID of the Dynamic Routing agent.')) + return parser + + def call_server(self, neutron_client, search_opts, parsed_args): + data = neutron_client.list_bgp_speaker_on_dragent( + parsed_args.dragent_id, **search_opts) + return data + + +class ListDRAgentsHostingBGPSpeaker(neutronV20.ListCommand): + """List Dynamic Routing agents hosting a BGP speaker.""" + + resource = 'agent' + _formatters = {} + list_columns = ['id', 'host', 'admin_state_up', 'alive'] + unknown_parts_flag = False + + def get_parser(self, prog_name): + parser = super(ListDRAgentsHostingBGPSpeaker, + self).get_parser(prog_name) + parser.add_argument('bgp_speaker', + metavar='BGP_SPEAKER', + help=_('ID or name of the BGP speaker.')) + return parser + + def extend_list(self, data, parsed_args): + for agent in data: + agent['alive'] = ":-)" if agent['alive'] else 'xxx' + + def call_server(self, neutron_client, search_opts, parsed_args): + _speaker_id = bgp_speaker.get_bgp_speaker_id(neutron_client, + parsed_args.bgp_speaker) + search_opts['bgp_speaker'] = _speaker_id + data = neutron_client.list_dragents_hosting_bgp_speaker(**search_opts) + return data diff --git a/neutronclient/neutron/v2_0/bgp/peer.py b/neutronclient/neutron/v2_0/bgp/peer.py new file mode 100644 index 000000000..8fefb660a --- /dev/null +++ b/neutronclient/neutron/v2_0/bgp/peer.py @@ -0,0 +1,127 @@ +# Copyright 2016 Huawei Technologies India Pvt. Ltd. +# 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. +# + +from neutronclient._i18n import _ +from neutronclient.common import exceptions +from neutronclient.common import utils +from neutronclient.common import validators +from neutronclient.neutron import v2_0 as neutronv20 + + +def get_bgp_peer_id(client, id_or_name): + return neutronv20.find_resourceid_by_name_or_id(client, + 'bgp_peer', + id_or_name) + + +def validate_peer_attributes(parsed_args): + # Validate AS number + validators.validate_int_range(parsed_args, 'remote_as', + neutronv20.bgp.speaker.MIN_AS_NUM, + neutronv20.bgp.speaker.MAX_AS_NUM) + # Validate password + if parsed_args.auth_type != 'none' and parsed_args.password is None: + raise exceptions.CommandError(_('Must provide password if auth-type ' + 'is specified.')) + if parsed_args.auth_type == 'none' and parsed_args.password: + raise exceptions.CommandError(_('Must provide auth-type if password ' + 'is specified.')) + + +class ListPeers(neutronv20.ListCommand): + """List BGP peers.""" + + resource = 'bgp_peer' + list_columns = ['id', 'name', 'peer_ip', 'remote_as'] + pagination_support = True + sorting_support = True + + +class ShowPeer(neutronv20.ShowCommand): + """Show information of a given BGP peer.""" + + resource = 'bgp_peer' + + +class CreatePeer(neutronv20.CreateCommand): + """Create a BGP Peer.""" + + resource = 'bgp_peer' + + def add_known_arguments(self, parser): + parser.add_argument( + 'name', + metavar='NAME', + help=_('Name of the BGP peer to create.')) + parser.add_argument( + '--peer-ip', + metavar='PEER_IP_ADDRESS', + required=True, + help=_('Peer IP address.')) + parser.add_argument( + '--remote-as', + required=True, + metavar='PEER_REMOTE_AS', + help=_('Peer AS number. (Integer in [%(min_val)s, %(max_val)s] ' + 'is allowed.)') % + {'min_val': neutronv20.bgp.speaker.MIN_AS_NUM, + 'max_val': neutronv20.bgp.speaker.MAX_AS_NUM}) + parser.add_argument( + '--auth-type', + metavar='PEER_AUTH_TYPE', + choices=['none', 'md5'], + default='none', + type=utils.convert_to_lowercase, + help=_('Authentication algorithm. Supported algorithms: ' + 'none(default), md5')) + parser.add_argument( + '--password', + metavar='AUTH_PASSWORD', + help=_('Authentication password.')) + + def args2body(self, parsed_args): + body = {} + validate_peer_attributes(parsed_args) + neutronv20.update_dict(parsed_args, body, + ['name', 'peer_ip', + 'remote_as', 'auth_type', 'password']) + return {self.resource: body} + + +class UpdatePeer(neutronv20.UpdateCommand): + """Update BGP Peer's information.""" + + resource = 'bgp_peer' + + def add_known_arguments(self, parser): + parser.add_argument( + '--name', + help=_('Updated name of the BGP peer.')) + parser.add_argument( + '--password', + metavar='AUTH_PASSWORD', + help=_('Updated authentication password.')) + + def args2body(self, parsed_args): + body = {} + neutronv20.update_dict(parsed_args, body, ['name', 'password']) + return {self.resource: body} + + +class DeletePeer(neutronv20.DeleteCommand): + """Delete a BGP peer.""" + + resource = 'bgp_peer' diff --git a/neutronclient/neutron/v2_0/bgp/speaker.py b/neutronclient/neutron/v2_0/bgp/speaker.py new file mode 100755 index 000000000..f2a3df7db --- /dev/null +++ b/neutronclient/neutron/v2_0/bgp/speaker.py @@ -0,0 +1,277 @@ +# Copyright 2016 Huawei Technologies India Pvt. Ltd. +# 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. +# + +from __future__ import print_function + +from neutronclient._i18n import _ +from neutronclient.common import utils +from neutronclient.common import validators +from neutronclient.neutron import v2_0 as neutronv20 +from neutronclient.neutron.v2_0.bgp import peer as bgp_peer + +# Allowed BGP Autonomous number range +MIN_AS_NUM = 1 +MAX_AS_NUM = 65535 + + +def get_network_id(client, id_or_name): + return neutronv20.find_resourceid_by_name_or_id(client, + 'network', + id_or_name) + + +def get_bgp_speaker_id(client, id_or_name): + return neutronv20.find_resourceid_by_name_or_id(client, + 'bgp_speaker', + id_or_name) + + +def validate_speaker_attributes(parsed_args): + # Validate AS number + validators.validate_int_range(parsed_args, 'local_as', + MIN_AS_NUM, MAX_AS_NUM) + + +def add_common_arguments(parser): + utils.add_boolean_argument( + parser, '--advertise-floating-ip-host-routes', + help=_('Whether to enable or disable the advertisement ' + 'of floating-ip host routes by the BGP speaker. ' + 'By default floating ip host routes will be ' + 'advertised by the BGP speaker.')) + utils.add_boolean_argument( + parser, '--advertise-tenant-networks', + help=_('Whether to enable or disable the advertisement ' + 'of tenant network routes by the BGP speaker. ' + 'By default tenant network routes will be ' + 'advertised by the BGP speaker.')) + + +def args2body_common_arguments(body, parsed_args): + neutronv20.update_dict(parsed_args, body, + ['name', + 'advertise_floating_ip_host_routes', + 'advertise_tenant_networks']) + + +class ListSpeakers(neutronv20.ListCommand): + """List BGP speakers.""" + + resource = 'bgp_speaker' + list_columns = ['id', 'name', 'local_as', 'ip_version'] + pagination_support = True + sorting_support = True + + +class ShowSpeaker(neutronv20.ShowCommand): + """Show information of a given BGP speaker.""" + + resource = 'bgp_speaker' + + +class CreateSpeaker(neutronv20.CreateCommand): + """Create a BGP Speaker.""" + + resource = 'bgp_speaker' + + def add_known_arguments(self, parser): + parser.add_argument( + 'name', + metavar='NAME', + help=_('Name of the BGP speaker to create.')) + parser.add_argument( + '--local-as', + metavar='LOCAL_AS', + required=True, + help=_('Local AS number. (Integer in [%(min_val)s, %(max_val)s] ' + 'is allowed.)') % {'min_val': MIN_AS_NUM, + 'max_val': MAX_AS_NUM}) + parser.add_argument( + '--ip-version', + type=int, choices=[4, 6], + default=4, + help=_('IP version for the BGP speaker (default is 4).')) + add_common_arguments(parser) + + def args2body(self, parsed_args): + body = {} + validate_speaker_attributes(parsed_args) + body['local_as'] = parsed_args.local_as + body['ip_version'] = parsed_args.ip_version + args2body_common_arguments(body, parsed_args) + return {self.resource: body} + + +class UpdateSpeaker(neutronv20.UpdateCommand): + """Update BGP Speaker's information.""" + + resource = 'bgp_speaker' + + def add_known_arguments(self, parser): + parser.add_argument( + '--name', + help=_('Name of the BGP speaker to update.')) + add_common_arguments(parser) + + def args2body(self, parsed_args): + body = {} + args2body_common_arguments(body, parsed_args) + return {self.resource: body} + + +class DeleteSpeaker(neutronv20.DeleteCommand): + """Delete a BGP speaker.""" + + resource = 'bgp_speaker' + + +class AddPeerToSpeaker(neutronv20.NeutronCommand): + """Add a peer to the BGP speaker.""" + + def get_parser(self, prog_name): + parser = super(AddPeerToSpeaker, self).get_parser(prog_name) + parser.add_argument( + 'bgp_speaker', + metavar='BGP_SPEAKER', + help=_('ID or name of the BGP speaker.')) + parser.add_argument( + 'bgp_peer', + metavar='BGP_PEER', + help=_('ID or name of the BGP peer to add.')) + return parser + + def run(self, parsed_args): + neutron_client = self.get_client() + _speaker_id = get_bgp_speaker_id(neutron_client, + parsed_args.bgp_speaker) + _peer_id = bgp_peer.get_bgp_peer_id(neutron_client, + parsed_args.bgp_peer) + neutron_client.add_peer_to_bgp_speaker(_speaker_id, + {'bgp_peer_id': _peer_id}) + print(_('Added BGP peer %(peer)s to BGP speaker %(speaker)s.') % + {'peer': parsed_args.bgp_peer, + 'speaker': parsed_args.bgp_speaker}, + file=self.app.stdout) + + +class RemovePeerFromSpeaker(neutronv20.NeutronCommand): + """Remove a peer from the BGP speaker.""" + + def get_parser(self, prog_name): + parser = super(RemovePeerFromSpeaker, self).get_parser(prog_name) + parser.add_argument( + 'bgp_speaker', + metavar='BGP_SPEAKER', + help=_('ID or name of the BGP speaker.')) + parser.add_argument( + 'bgp_peer', + metavar='BGP_PEER', + help=_('ID or name of the BGP peer to remove.')) + return parser + + def run(self, parsed_args): + neutron_client = self.get_client() + _speaker_id = get_bgp_speaker_id(neutron_client, + parsed_args.bgp_speaker) + _peer_id = bgp_peer.get_bgp_peer_id(neutron_client, + parsed_args.bgp_peer) + neutron_client.remove_peer_from_bgp_speaker(_speaker_id, + {'bgp_peer_id': _peer_id}) + print(_('Removed BGP peer %(peer)s from BGP speaker %(speaker)s.') % + {'peer': parsed_args.bgp_peer, + 'speaker': parsed_args.bgp_speaker}, + file=self.app.stdout) + + +class AddNetworkToSpeaker(neutronv20.NeutronCommand): + """Add a network to the BGP speaker.""" + + def get_parser(self, prog_name): + parser = super(AddNetworkToSpeaker, self).get_parser(prog_name) + parser.add_argument( + 'bgp_speaker', + metavar='BGP_SPEAKER', + help=_('ID or name of the BGP speaker.')) + parser.add_argument( + 'network', + metavar='NETWORK', + help=_('ID or name of the network to add.')) + return parser + + def run(self, parsed_args): + neutron_client = self.get_client() + _speaker_id = get_bgp_speaker_id(neutron_client, + parsed_args.bgp_speaker) + _net_id = get_network_id(neutron_client, + parsed_args.network) + neutron_client.add_network_to_bgp_speaker(_speaker_id, + {'network_id': _net_id}) + print(_('Added network %(net)s to BGP speaker %(speaker)s.') % + {'net': parsed_args.network, 'speaker': parsed_args.bgp_speaker}, + file=self.app.stdout) + + +class RemoveNetworkFromSpeaker(neutronv20.NeutronCommand): + """Remove a network from the BGP speaker.""" + + def get_parser(self, prog_name): + parser = super(RemoveNetworkFromSpeaker, self).get_parser(prog_name) + parser.add_argument( + 'bgp_speaker', + metavar='BGP_SPEAKER', + help=_('ID or name of the BGP speaker.')) + parser.add_argument( + 'network', + metavar='NETWORK', + help=_('ID or name of the network to remove.')) + return parser + + def run(self, parsed_args): + neutron_client = self.get_client() + _speaker_id = get_bgp_speaker_id(neutron_client, + parsed_args.bgp_speaker) + _net_id = get_network_id(neutron_client, + parsed_args.network) + neutron_client.remove_network_from_bgp_speaker(_speaker_id, + {'network_id': _net_id}) + print(_('Removed network %(net)s from BGP speaker %(speaker)s.') % + {'net': parsed_args.network, 'speaker': parsed_args.bgp_speaker}, + file=self.app.stdout) + + +class ListRoutesAdvertisedBySpeaker(neutronv20.ListCommand): + """List routes advertised by a given BGP speaker.""" + + list_columns = ['id', 'destination', 'next_hop'] + resource = 'advertised_route' + pagination_support = True + sorting_support = True + + def get_parser(self, prog_name): + parser = super(ListRoutesAdvertisedBySpeaker, + self).get_parser(prog_name) + parser.add_argument( + 'bgp_speaker', + metavar='BGP_SPEAKER', + help=_('ID or name of the BGP speaker.')) + return parser + + def call_server(self, neutron_client, search_opts, parsed_args): + _speaker_id = get_bgp_speaker_id(neutron_client, + parsed_args.bgp_speaker) + data = neutron_client.list_route_advertised_from_bgp_speaker( + _speaker_id, **search_opts) + return data diff --git a/neutronclient/shell.py b/neutronclient/shell.py index c1f6b0aaf..795571ad4 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -45,6 +45,9 @@ from neutronclient.neutron.v2_0 import agentscheduler from neutronclient.neutron.v2_0 import auto_allocated_topology from neutronclient.neutron.v2_0 import availability_zone +from neutronclient.neutron.v2_0.bgp import dragentscheduler as bgp_drsched +from neutronclient.neutron.v2_0.bgp import peer as bgp_peer +from neutronclient.neutron.v2_0.bgp import speaker as bgp_speaker from neutronclient.neutron.v2_0 import extension from neutronclient.neutron.v2_0.flavor import flavor from neutronclient.neutron.v2_0.flavor import flavor_profile @@ -395,6 +398,35 @@ def take_action(self, parsed_args): 'availability-zone-list': availability_zone.ListAvailabilityZone, 'auto-allocated-topology-show': ( auto_allocated_topology.ShowAutoAllocatedTopology), + 'bgp-dragent-speaker-add': ( + bgp_drsched.AddBGPSpeakerToDRAgent + ), + 'bgp-dragent-speaker-remove': ( + bgp_drsched.RemoveBGPSpeakerFromDRAgent + ), + 'bgp-speaker-list-on-dragent': ( + bgp_drsched.ListBGPSpeakersOnDRAgent + ), + 'bgp-dragent-list-hosting-speaker': ( + bgp_drsched.ListDRAgentsHostingBGPSpeaker + ), + 'bgp-speaker-list': bgp_speaker.ListSpeakers, + 'bgp-speaker-advertiseroute-list': ( + bgp_speaker.ListRoutesAdvertisedBySpeaker + ), + 'bgp-speaker-show': bgp_speaker.ShowSpeaker, + 'bgp-speaker-create': bgp_speaker.CreateSpeaker, + 'bgp-speaker-update': bgp_speaker.UpdateSpeaker, + 'bgp-speaker-delete': bgp_speaker.DeleteSpeaker, + 'bgp-speaker-peer-add': bgp_speaker.AddPeerToSpeaker, + 'bgp-speaker-peer-remove': bgp_speaker.RemovePeerFromSpeaker, + 'bgp-speaker-network-add': bgp_speaker.AddNetworkToSpeaker, + 'bgp-speaker-network-remove': bgp_speaker.RemoveNetworkFromSpeaker, + 'bgp-peer-list': bgp_peer.ListPeers, + 'bgp-peer-show': bgp_peer.ShowPeer, + 'bgp-peer-create': bgp_peer.CreatePeer, + 'bgp-peer-update': bgp_peer.UpdatePeer, + 'bgp-peer-delete': bgp_peer.DeletePeer, } COMMANDS = {'2.0': COMMAND_V2} diff --git a/neutronclient/tests/unit/bgp/__init__.py b/neutronclient/tests/unit/bgp/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/tests/unit/bgp/test_cli20_dragentscheduler.py b/neutronclient/tests/unit/bgp/test_cli20_dragentscheduler.py new file mode 100644 index 000000000..cbe85d92f --- /dev/null +++ b/neutronclient/tests/unit/bgp/test_cli20_dragentscheduler.py @@ -0,0 +1,66 @@ +# Copyright 2016 Huawei Technologies India Pvt. Ltd. +# 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 sys + +from neutronclient.neutron.v2_0.bgp import dragentscheduler as bgp_drsched +from neutronclient.tests.unit import test_cli20 +from neutronclient.tests.unit import test_cli20_agentschedulers as test_as + + +BGP_DRAGENT_ID = 'bgp_dragent_id1' +BGP_SPEAKER = 'bgp_speaker_id1' + + +class CLITestV20DRAgentScheduler(test_as.CLITestV20AgentScheduler): + + def test_add_bgp_speaker_to_dragent(self): + resource = 'agent' + cmd = bgp_drsched.AddBGPSpeakerToDRAgent( + test_cli20.MyApp(sys.stdout), None) + args = (BGP_DRAGENT_ID, BGP_SPEAKER) + body = {'bgp_speaker_id': BGP_SPEAKER} + result = {'bgp_speaker_id': 'bgp_speaker_id', } + self._test_add_to_agent(resource, cmd, args, + self.client.BGP_DRINSTANCES, + body, result) + + def test_remove_bgp_speaker_from_dragent(self): + resource = 'agent' + cmd = bgp_drsched.RemoveBGPSpeakerFromDRAgent( + test_cli20.MyApp(sys.stdout), None) + args = (BGP_DRAGENT_ID, BGP_SPEAKER) + self._test_remove_from_agent(resource, cmd, args, + self.client.BGP_DRINSTANCES) + + def test_list_bgp_speakers_on_dragent(self): + resources = 'bgp_speakers' + cmd = bgp_drsched.ListBGPSpeakersOnDRAgent( + test_cli20.MyApp(sys.stdout), None) + path = ((self.client.agent_path + self.client.BGP_DRINSTANCES) % + BGP_DRAGENT_ID) + self._test_list_resources(resources, cmd, base_args=[BGP_DRAGENT_ID], + path=path) + + def test_list_dragents_hosting_bgp_speaker(self): + resources = 'agent' + cmd = bgp_drsched.ListDRAgentsHostingBGPSpeaker( + test_cli20.MyApp(sys.stdout), None) + path = ((self.client.bgp_speaker_path + self.client.BGP_DRAGENTS) % + BGP_DRAGENT_ID) + contents = {self.id_field: 'myid1', 'alive': True} + self._test_list_resources(resources, cmd, base_args=[BGP_DRAGENT_ID], + path=path, response_contents=contents) diff --git a/neutronclient/tests/unit/bgp/test_cli20_peer.py b/neutronclient/tests/unit/bgp/test_cli20_peer.py new file mode 100644 index 000000000..b16532704 --- /dev/null +++ b/neutronclient/tests/unit/bgp/test_cli20_peer.py @@ -0,0 +1,223 @@ +# Copyright 2016 Huawei Technologies India Pvt. Ltd. +# 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 sys + +from neutronclient.common import exceptions +from neutronclient.neutron.v2_0.bgp import peer as bgp_peer +from neutronclient.neutron.v2_0.bgp import speaker as bgp_speaker +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20BGPPeerJSON(test_cli20.CLITestV20Base): + + non_admin_status_resources = ['bgp_peer'] + + def test_create_bgp_peer_with_mandatory_params(self): + # Create BGP peer with mandatory params. + resource = 'bgp_peer' + cmd = bgp_peer.CreatePeer(test_cli20.MyApp(sys.stdout), + None) + name = 'my-name' + my_id = 'my-id' + peerip = '1.1.1.1' + remote_asnum = '1' + args = [name, + '--peer-ip', peerip, + '--remote-as', remote_asnum, ] + position_names = ['name', 'peer_ip', 'remote_as', + 'auth_type'] + position_values = [name, peerip, remote_asnum, 'none'] + self._test_create_resource(resource, cmd, name, my_id, args, + position_names, position_values) + + def test_create_bgp_peer_with_all_params(self): + # Create BGP peer with all params. + resource = 'bgp_peer' + cmd = bgp_peer.CreatePeer(test_cli20.MyApp(sys.stdout), + None) + name = 'my-name' + my_id = 'my-id' + peerip = '1.1.1.1' + remote_asnum = '65535' + authType = 'md5' + password = 'abc' + args = [name, + '--peer-ip', peerip, + '--remote-as', remote_asnum, + '--auth-type', authType, + '--password', password] + position_names = ['name', 'peer_ip', 'remote_as', + 'auth_type', 'password'] + position_values = [name, peerip, remote_asnum, authType, password] + self._test_create_resource(resource, cmd, name, my_id, args, + position_names, position_values) + + def test_create_bgp_peer_with_invalid_min_remote_asnum(self): + # Create BGP peer with invalid minimum remote-asnum. + resource = 'bgp_peer' + cmd = bgp_peer.CreatePeer(test_cli20.MyApp(sys.stdout), + None) + name = 'my-name' + my_id = 'my-id' + peerip = '1.1.1.1' + remote_asnum = '0' + args = [name, + '--peer-ip', peerip, + '--remote-as', remote_asnum, ] + position_names = ['name', 'peer_ip', 'remote_as', ] + position_values = [name, peerip, remote_asnum, ] + exc = self.assertRaises(exceptions.CommandError, + self._test_create_resource, + resource, cmd, name, my_id, args, + position_names, position_values) + self.assertEqual('remote-as "0" should be an integer [%s:%s].' % + (bgp_speaker.MIN_AS_NUM, bgp_speaker.MAX_AS_NUM), + str(exc)) + + def test_create_bgp_peer_with_invalid_max_remote_asnum(self): + # Create BGP peer with invalid maximum remote-asnum. + resource = 'bgp_peer' + cmd = bgp_peer.CreatePeer(test_cli20.MyApp(sys.stdout), + None) + name = 'my-name' + my_id = 'my-id' + peerip = '1.1.1.1' + remote_asnum = '65536' + args = [name, + '--peer-ip', peerip, + '--remote-as', remote_asnum, ] + position_names = ['name', 'peer_ip', 'remote_as', + 'auth_type', 'password'] + position_values = [name, peerip, remote_asnum, 'none', ''] + exc = self.assertRaises(exceptions.CommandError, + self._test_create_resource, + resource, cmd, name, my_id, args, + position_names, position_values) + self.assertEqual('remote-as "65536" should be an integer [%s:%s].' % + (bgp_speaker.MIN_AS_NUM, bgp_speaker.MAX_AS_NUM), + str(exc)) + + def test_create_authenticated_bgp_peer_without_authtype(self): + # Create authenticated BGP peer without auth-type. + resource = 'bgp_peer' + cmd = bgp_peer.CreatePeer(test_cli20.MyApp(sys.stdout), + None) + name = 'my-name' + my_id = 'my-id' + peerip = '1.1.1.1' + remote_asnum = '2048' + password = 'abc' + args = [name, + '--peer-ip', peerip, + '--remote-as', remote_asnum, + '--password', password] + position_names = ['name', 'peer_ip', 'remote_as', 'password'] + position_values = [name, peerip, remote_asnum, password] + exc = self.assertRaises(exceptions.CommandError, + self._test_create_resource, + resource, cmd, name, my_id, args, + position_names, position_values) + self.assertEqual('Must provide auth-type if password is specified.', + str(exc)) + + def test_create_authenticated_bgp_peer_without_password(self): + # Create authenticated BGP peer without password. + resource = 'bgp_peer' + cmd = bgp_peer.CreatePeer(test_cli20.MyApp(sys.stdout), + None) + name = 'my-name' + my_id = 'my-id' + peerip = '1.1.1.1' + remote_asnum = '2048' + authType = 'md5' + args = [name, + '--peer-ip', peerip, + '--remote-as', remote_asnum, + '--auth-type', authType] + position_names = ['name', 'peer_ip', 'remote_as', 'auth-type'] + position_values = [name, peerip, remote_asnum, authType] + exc = self.assertRaises(exceptions.CommandError, + self._test_create_resource, + resource, cmd, name, my_id, args, + position_names, position_values) + self.assertEqual('Must provide password if auth-type is specified.', + str(exc)) + + def test_update_bgp_peer(self): + # Update BGP peer: + # myid --advertise-tenant-networks True + # --advertise-floating-ip-host-routes False + resource = 'bgp_peer' + cmd = bgp_peer.UpdatePeer(test_cli20.MyApp(sys.stdout), + None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--name', 'new-name', + '--password', 'abc'], + {'name': 'new-name', 'password': 'abc'}) + + def test_update_bgp_peer_exception(self): + # Update BGP peer: myid. + resource = 'bgp_peer' + cmd = bgp_peer.UpdatePeer(test_cli20.MyApp(sys.stdout), + None) + self.assertRaises(exceptions.CommandError, + self._test_update_resource, + resource, cmd, 'myid', ['myid'], {}) + + def test_list_bgp_peer(self): + # List all BGP peers. + resources = "bgp_peers" + cmd = bgp_peer.ListPeers(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources(resources, cmd, True) + + # TODO(Vikram): Add test_list_bgp_peer_pagination + + def test_list_bgp_peer_sort(self): + # sorted list: bgp-peer-list --sort-key name --sort-key id + # --sort-key asc --sort-key desc + resources = "bgp_peers" + cmd = bgp_peer.ListPeers(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources(resources, cmd, + sort_key=["name", "id"], + sort_dir=["asc", "desc"]) + + def test_list_bgp_peer_limit(self): + # size (1000) limited list: bgp-peer-list -P. + resources = "bgp_peers" + cmd = bgp_peer.ListPeers(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources(resources, cmd, page_size=1000) + + def test_show_bgp_peer(self): + # Show BGP peer: --fields id --fields name myid. + resource = 'bgp_peer' + cmd = bgp_peer.ShowPeer(test_cli20.MyApp(sys.stdout), + None) + args = ['--fields', 'id', '--fields', 'name', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, args, + ['id', 'name']) + + def test_delete_bgp_peer(self): + # Delete BGP peer: bgp_peer_id. + resource = 'bgp_peer' + cmd = bgp_peer.DeletePeer(test_cli20.MyApp(sys.stdout), + None) + myid = 'myid' + args = [myid] + self._test_delete_resource(resource, cmd, myid, args) diff --git a/neutronclient/tests/unit/bgp/test_cli20_speaker.py b/neutronclient/tests/unit/bgp/test_cli20_speaker.py new file mode 100644 index 000000000..63ce5fc82 --- /dev/null +++ b/neutronclient/tests/unit/bgp/test_cli20_speaker.py @@ -0,0 +1,267 @@ +# Copyright 2016 Huawei Technologies India Pvt. Ltd. +# 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 sys + +from mox3 import mox + +from neutronclient.common import exceptions +from neutronclient.neutron.v2_0.bgp import speaker as bgp_speaker +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20BGPSpeakerJSON(test_cli20.CLITestV20Base): + + non_admin_status_resources = ['bgp_speaker'] + + def test_create_bgp_speaker_with_minimal_options(self): + # Create BGP Speaker with mandatory params. + resource = 'bgp_speaker' + cmd = bgp_speaker.CreateSpeaker(test_cli20.MyApp(sys.stdout), + None) + name = 'my-name' + my_id = 'my-id' + local_asnum = '1' + args = [name, '--local-as', local_asnum, ] + position_names = ['name', 'local_as', 'ip_version'] + position_values = [name, local_asnum, 4] + self._test_create_resource(resource, cmd, name, my_id, args, + position_names, position_values) + + def test_create_ipv4_bgp_speaker_with_all_params(self): + # Create BGP Speaker with all params. + resource = 'bgp_speaker' + cmd = bgp_speaker.CreateSpeaker(test_cli20.MyApp(sys.stdout), + None) + name = 'my-name' + my_id = 'my-id' + local_asnum = '1' + args = [name, + '--local-as', local_asnum, + '--ip-version', '4', + '--advertise-floating-ip-host-routes', 'True', + '--advertise-tenant-networks', 'True'] + position_names = ['name', 'local_as', 'ip_version', + 'advertise_floating_ip_host_routes', + 'advertise_tenant_networks'] + position_values = [name, local_asnum, 4, 'True', 'True'] + self._test_create_resource(resource, cmd, name, my_id, args, + position_names, position_values) + + def test_create_ipv6_bgp_speaker_with_all_params(self): + # Create BGP Speaker with all params. + resource = 'bgp_speaker' + cmd = bgp_speaker.CreateSpeaker(test_cli20.MyApp(sys.stdout), + None) + name = 'my-name' + my_id = 'my-id' + local_asnum = '65535' + args = [name, + '--local-as', local_asnum, + '--ip-version', '6', + '--advertise-floating-ip-host-routes', 'True', + '--advertise-tenant-networks', 'True'] + position_names = ['name', 'local_as', 'ip_version', + 'advertise_floating_ip_host_routes', + 'advertise_tenant_networks'] + position_values = [name, local_asnum, 6, 'True', 'True'] + self._test_create_resource(resource, cmd, name, my_id, args, + position_names, position_values) + + def test_create_bgp_speaker_with_invalid_min_local_asnum(self): + # Create BGP Speaker with invalid minimum local-asnum. + resource = 'bgp_speaker' + cmd = bgp_speaker.CreateSpeaker(test_cli20.MyApp(sys.stdout), + None) + name = 'my-name' + my_id = 'my-id' + local_asnum = '0' + args = [name, + '--local-as', local_asnum] + position_names = ['name', 'local_as'] + position_values = [name, local_asnum] + exc = self.assertRaises(exceptions.CommandError, + self._test_create_resource, + resource, cmd, name, my_id, args, + position_names, position_values) + self.assertEqual('local-as "0" should be an integer [%s:%s].' % + (bgp_speaker.MIN_AS_NUM, bgp_speaker.MAX_AS_NUM), + str(exc)) + + def test_create_bgp_speaker_with_invalid_max_local_asnum(self): + # Create BGP Speaker with invalid maximum local-asnum. + resource = 'bgp_speaker' + cmd = bgp_speaker.CreateSpeaker(test_cli20.MyApp(sys.stdout), + None) + name = 'my-name' + my_id = 'my-id' + local_asnum = '65536' + args = [name, + '--local-as', local_asnum] + position_names = ['name', 'local_as', ] + position_values = [name, local_asnum, ] + exc = self.assertRaises(exceptions.CommandError, + self._test_create_resource, + resource, cmd, name, my_id, args, + position_names, position_values) + self.assertEqual('local-as "65536" should be an integer [%s:%s].' % + (bgp_speaker.MIN_AS_NUM, bgp_speaker.MAX_AS_NUM), + str(exc)) + + def test_update_bgp_speaker(self): + # Update BGP Speaker: + # myid --advertise-tenant-networks True + # --advertise-floating-ip-host-routes False + resource = 'bgp_speaker' + cmd = bgp_speaker.UpdateSpeaker(test_cli20.MyApp(sys.stdout), + None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', + '--name', 'new-name', + '--advertise-tenant-networks', 'True', + '--advertise-floating-ip-host-routes', + 'False'], + {'name': 'new-name', + 'advertise_tenant_networks': 'True', + 'advertise_floating_ip_host_routes': + 'False'}) + + def test_update_bgp_speaker_exception(self): + # Update BGP Speaker: myid. + resource = 'bgp_speaker' + cmd = bgp_speaker.UpdateSpeaker(test_cli20.MyApp(sys.stdout), + None) + self.assertRaises(exceptions.CommandError, + self._test_update_resource, + resource, cmd, 'myid', ['myid'], {}) + + def test_list_bgp_speaker(self): + # List all BGP Speakers. + resources = "bgp_speakers" + cmd = bgp_speaker.ListSpeakers(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources(resources, cmd, True) + + def test_list_bgp_speaker_pagination(self): + # List all BGP Speakers with pagination support. + cmd = bgp_speaker.ListSpeakers(test_cli20.MyApp(sys.stdout), + None) + self.mox.StubOutWithMock(bgp_speaker.ListSpeakers, + "extend_list") + bgp_speaker.ListSpeakers.extend_list(mox.IsA(list), + mox.IgnoreArg()) + self._test_list_resources_with_pagination("bgp_speakers", + cmd) + self.mox.VerifyAll() + self.mox.UnsetStubs() + + def test_list_bgp_speaker_sort(self): + # sorted list: bgp-speaker-list --sort-key name --sort-key id + # --sort-key asc --sort-key desc + resources = "bgp_speakers" + cmd = bgp_speaker.ListSpeakers(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources(resources, cmd, + sort_key=["name", "id"], + sort_dir=["asc", "desc"]) + + def test_list_bgp_speaker_limit(self): + # size (1000) limited list: bgp-speaker-list -P. + resources = "bgp_speakers" + cmd = bgp_speaker.ListSpeakers(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources(resources, cmd, page_size=1000) + + def test_show_bgp_speaker(self): + # Show BGP Speaker: --fields id --fields name myid. + resource = 'bgp_speaker' + cmd = bgp_speaker.ShowSpeaker(test_cli20.MyApp(sys.stdout), + None) + args = ['--fields', 'id', '--fields', 'name', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, args, + ['id', 'name']) + + def test_delete_bgp_speaker(self): + # Delete BGP Speaker: bgp_speaker_id. + resource = 'bgp_speaker' + cmd = bgp_speaker.DeleteSpeaker(test_cli20.MyApp(sys.stdout), + None) + myid = 'myid' + args = [myid] + self._test_delete_resource(resource, cmd, myid, args) + + def _test_add_remove_peer(self, action, cmd, args): + """Add or Remove BGP Peer to/from a BGP Speaker.""" + resource = 'bgp_speaker' + subcmd = '%s_bgp_peer' % action + body = {'bgp_peer_id': 'peerid'} + if action == 'add': + retval = {'bgp_peer': 'peerid'} + else: + retval = None + self._test_update_resource_action(resource, cmd, 'myid', + subcmd, args, body, retval) + + def test_add_peer_to_bgp_speaker(self): + # Add peer to BGP speaker: myid peer_id=peerid + cmd = bgp_speaker.AddPeerToSpeaker(test_cli20.MyApp(sys.stdout), + None) + args = ['myid', 'peerid'] + self._test_add_remove_peer('add', cmd, args) + + def test_remove_peer_from_bgp_speaker(self): + # Remove peer from BGP speaker: myid peer_id=peerid + cmd = bgp_speaker.RemovePeerFromSpeaker(test_cli20.MyApp(sys.stdout), + None) + args = ['myid', 'peerid'] + self._test_add_remove_peer('remove', cmd, args) + + def _test_add_remove_network(self, action, cmd, args): + # Add or Remove network to/from a BGP Speaker. + resource = 'bgp_speaker' + subcmd = '%s_gateway_network' % action + body = {'network_id': 'netid'} + if action == 'add': + retval = {'network': 'netid'} + else: + retval = None + self._test_update_resource_action(resource, cmd, 'myid', + subcmd, args, body, retval) + + def test_add_network_to_bgp_speaker(self): + # Add peer to BGP speaker: myid network_id=netid + cmd = bgp_speaker.AddNetworkToSpeaker(test_cli20.MyApp(sys.stdout), + None) + args = ['myid', 'netid'] + self._test_add_remove_network('add', cmd, args) + + def test_remove_network_from_bgp_speaker(self): + # Remove network from BGP speaker: myid network_id=netid + cmd = bgp_speaker.RemoveNetworkFromSpeaker( + test_cli20.MyApp(sys.stdout), None) + args = ['myid', 'netid'] + self._test_add_remove_network('remove', cmd, args) + + def test_list_routes_advertised_by_a_bgp_speaker(self): + # Retrieve advertised route list + resources = 'advertised_routes' + cmd = bgp_speaker.ListRoutesAdvertisedBySpeaker( + test_cli20.MyApp(sys.stdout), None) + bs_id = 'bgp_speaker_id1' + path = ((self.client.bgp_speaker_path + '/get_advertised_routes') % + bs_id) + self._test_list_resources(resources, cmd, base_args=[bs_id], + path=path) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index df0c46c16..cfd6264b7 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -410,6 +410,14 @@ class Client(ClientBase): flavor_profile_binding_path = flavor_path + service_profile_path availability_zones_path = "/availability_zones" auto_allocated_topology_path = "/auto-allocated-topology/%s" + BGP_DRINSTANCES = "/bgp-drinstances" + BGP_DRINSTANCE = "/bgp-drinstance/%s" + BGP_DRAGENTS = "/bgp-dragents" + BGP_DRAGENT = "/bgp-dragents/%s" + bgp_speakers_path = "/bgp-speakers" + bgp_speaker_path = "/bgp-speakers/%s" + bgp_peers_path = "/bgp-peers" + bgp_peer_path = "/bgp-peers/%s" # API has no way to report plurals, so we have to hard code them EXTED_PLURALS = {'routers': 'router', @@ -447,6 +455,8 @@ class Client(ClientBase): 'bandwidth_limit_rules': 'bandwidth_limit_rule', 'rule_types': 'rule_type', 'flavors': 'flavor', + 'bgp_speakers': 'bgp_speaker', + 'bgp_peers': 'bgp_peer', } @APIParamsCall @@ -1346,6 +1356,30 @@ def add_router_to_l3_agent(self, l3_agent, body): return self.post((self.agent_path + self.L3_ROUTERS) % l3_agent, body=body) + @APIParamsCall + def list_dragents_hosting_bgp_speaker(self, bgp_speaker, **_params): + """Fetches a list of Dynamic Routing agents hosting a BGP speaker.""" + return self.get((self.bgp_speaker_path + self.BGP_DRAGENTS) + % bgp_speaker, params=_params) + + @APIParamsCall + def add_bgp_speaker_to_dragent(self, bgp_dragent, body): + """Adds a BGP speaker to Dynamic Routing agent.""" + return self.post((self.agent_path + self.BGP_DRINSTANCES) + % bgp_dragent, body=body) + + @APIParamsCall + def remove_bgp_speaker_from_dragent(self, bgp_dragent, bgpspeaker_id): + """Removes a BGP speaker from Dynamic Routing agent.""" + return self.delete((self.agent_path + self.BGP_DRINSTANCES + "/%s") + % (bgp_dragent, bgpspeaker_id)) + + @APIParamsCall + def list_bgp_speaker_on_dragent(self, bgp_dragent, **_params): + """Fetches a list of BGP speakers hosted by Dynamic Routing agent.""" + return self.get((self.agent_path + self.BGP_DRINSTANCES) + % bgp_dragent, params=_params) + @APIParamsCall def list_firewall_rules(self, retrieve_all=True, **_params): """Fetches a list of all firewall rules for a tenant.""" @@ -1701,6 +1735,89 @@ def show_auto_allocated_topology(self, tenant_id, **_params): self.auto_allocated_topology_path % tenant_id, params=_params) + @APIParamsCall + def list_bgp_speakers(self, retrieve_all=True, **_params): + """Fetches a list of all BGP speakers for a tenant.""" + return self.list('bgp_speakers', self.bgp_speakers_path, retrieve_all, + **_params) + + @APIParamsCall + def show_bgp_speaker(self, bgp_speaker_id, **_params): + """Fetches information of a certain BGP speaker.""" + return self.get(self.bgp_speaker_path % (bgp_speaker_id), + params=_params) + + @APIParamsCall + def create_bgp_speaker(self, body=None): + """Creates a new BGP speaker.""" + return self.post(self.bgp_speakers_path, body=body) + + @APIParamsCall + def update_bgp_speaker(self, bgp_speaker_id, body=None): + """Update a BGP speaker.""" + return self.put(self.bgp_speaker_path % bgp_speaker_id, body=body) + + @APIParamsCall + def delete_bgp_speaker(self, speaker_id): + """Deletes the specified BGP speaker.""" + return self.delete(self.bgp_speaker_path % (speaker_id)) + + @APIParamsCall + def add_peer_to_bgp_speaker(self, speaker_id, body=None): + """Adds a peer to BGP speaker.""" + return self.put((self.bgp_speaker_path % speaker_id) + + "/add_bgp_peer", body=body) + + @APIParamsCall + def remove_peer_from_bgp_speaker(self, speaker_id, body=None): + """Removes a peer from BGP speaker.""" + return self.put((self.bgp_speaker_path % speaker_id) + + "/remove_bgp_peer", body=body) + + @APIParamsCall + def add_network_to_bgp_speaker(self, speaker_id, body=None): + """Adds a network to BGP speaker.""" + return self.put((self.bgp_speaker_path % speaker_id) + + "/add_gateway_network", body=body) + + @APIParamsCall + def remove_network_from_bgp_speaker(self, speaker_id, body=None): + """Removes a network from BGP speaker.""" + return self.put((self.bgp_speaker_path % speaker_id) + + "/remove_gateway_network", body=body) + + @APIParamsCall + def list_route_advertised_from_bgp_speaker(self, speaker_id, **_params): + """Fetches a list of all routes advertised by BGP speaker.""" + return self.get((self.bgp_speaker_path % speaker_id) + + "/get_advertised_routes", params=_params) + + @APIParamsCall + def list_bgp_peers(self, **_params): + """Fetches a list of all BGP peers.""" + return self.get(self.bgp_peers_path, params=_params) + + @APIParamsCall + def show_bgp_peer(self, peer_id, **_params): + """Fetches information of a certain BGP peer.""" + return self.get(self.bgp_peer_path % peer_id, + params=_params) + + @APIParamsCall + def create_bgp_peer(self, body=None): + """Create a new BGP peer.""" + return self.post(self.bgp_peers_path, body=body) + + @APIParamsCall + def update_bgp_peer(self, bgp_peer_id, body=None): + """Update a BGP peer.""" + return self.put(self.bgp_peer_path % bgp_peer_id, body=body) + + @APIParamsCall + def delete_bgp_peer(self, peer_id): + """Deletes the specified BGP peer.""" + return self.delete(self.bgp_peer_path % peer_id) + def __init__(self, **kwargs): """Initialize a new client for the Neutron v2.0 API.""" super(Client, self).__init__(**kwargs) diff --git a/releasenotes/notes/bgp-dynamic-routing-b97a1c81d3007049.yaml b/releasenotes/notes/bgp-dynamic-routing-b97a1c81d3007049.yaml new file mode 100644 index 000000000..2fa2e8e6e --- /dev/null +++ b/releasenotes/notes/bgp-dynamic-routing-b97a1c81d3007049.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + CLI support for the BGP dynamic routing functionality will help + advertising neutron fixed-ips and dvr host routes via BGP. \ No newline at end of file From 65118c09eb08e760590939e57d3b1a485c567f91 Mon Sep 17 00:00:00 2001 From: Hirofumi Ichihara Date: Wed, 24 Feb 2016 19:28:28 +0900 Subject: [PATCH 383/845] Add wrapper classes for return-request-id-to-caller Added wrapper classes which are inherited from base data types tuple, dict and str. Each of these wrapper classes contain a 'request_ids' attribute which is populated with a 'x-openstack-request-id' received in a header from a response body. This change is required to return 'request_id' from client to log request_id mappings of cross projects[1]. [1]: http://specs.openstack.org/openstack/openstack-specs/specs/return-request-id.html Change-Id: I55fcba61c4efb308f575e95e154aba23e5dd5245 Implements: blueprint return-request-id-to-caller --- doc/source/usage/library.rst | 10 + neutronclient/common/exceptions.py | 9 + neutronclient/tests/unit/test_cli20.py | 204 ++++++++++++++++-- neutronclient/v2_0/client.py | 124 ++++++++++- ...request-id-to-caller-15b1d23a4ddc27a3.yaml | 3 + 5 files changed, 327 insertions(+), 23 deletions(-) create mode 100644 releasenotes/notes/return-request-id-to-caller-15b1d23a4ddc27a3.yaml diff --git a/doc/source/usage/library.rst b/doc/source/usage/library.rst index dff06961f..8b301e385 100644 --- a/doc/source/usage/library.rst +++ b/doc/source/usage/library.rst @@ -59,3 +59,13 @@ and a service endpoint URL directly. >>> from neutronclient.v2_0 import client >>> neutron = client.Client(endpoint_url='http://192.168.206.130:9696/', ... token='d3f9226f27774f338019aa2611112ef6') + +You can get ``X-Openstack-Request-Id`` as ``request_ids`` from the result. + +.. code-block:: python + + >>> network = {'name': 'mynetwork', 'admin_state_up': True} + >>> neutron.create_network({'network':network}) + >>> networks = neutron.list_networks(name='mynetwork') + >>> print networks.request_ids + ['req-978a0160-7ab0-44f0-8a93-08e9a4e785fa'] diff --git a/neutronclient/common/exceptions.py b/neutronclient/common/exceptions.py index 95f54f722..6aff6d644 100644 --- a/neutronclient/common/exceptions.py +++ b/neutronclient/common/exceptions.py @@ -60,10 +60,19 @@ class NeutronClientException(NeutronException): """ status_code = 0 + req_ids_msg = _("Neutron server returns request_ids: %s") + request_ids = [] def __init__(self, message=None, **kwargs): + self.request_ids = kwargs.get('request_ids') if 'status_code' in kwargs: self.status_code = kwargs['status_code'] + if self.request_ids: + req_ids_msg = self.req_ids_msg % self.request_ids + if message: + message += '\n' + req_ids_msg + else: + message = req_ids_msg super(NeutronClientException, self).__init__(message, **kwargs) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index e3a1014eb..5407e7bed 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -37,6 +37,7 @@ FORMAT = 'json' TOKEN = 'testtoken' ENDURL = 'localurl' +REQUEST_ID = 'test_request_id' @contextlib.contextmanager @@ -65,7 +66,7 @@ def make_string(self): return result -class MyResp(object): +class MyResp(requests.Response): def __init__(self, status_code, headers=None, reason=None): self.status_code = status_code self.headers = headers or {} @@ -648,41 +649,46 @@ def test_do_request_unicode(self): self.client.httpclient.auth_token = encodeutils.safe_encode( unicode_text) expected_auth_token = encodeutils.safe_encode(unicode_text) + resp_headers = {'x-openstack-request-id': REQUEST_ID} self.client.httpclient.request( end_url(expected_action, query=expect_query, format=self.format), 'PUT', body=expect_body, headers=mox.ContainsKeyValue( 'X-Auth-Token', - expected_auth_token)).AndReturn((MyResp(200), expect_body)) + expected_auth_token)).AndReturn((MyResp(200, resp_headers), + expect_body)) self.mox.ReplayAll() - res_body = self.client.do_request('PUT', action, body=body, - params=params) + result = self.client.do_request('PUT', action, body=body, + params=params) self.mox.VerifyAll() self.mox.UnsetStubs() # test response with unicode - self.assertEqual(body, res_body) + self.assertEqual(body, result) def test_do_request_error_without_response_body(self): self.mox.StubOutWithMock(self.client.httpclient, "request") params = {'test': 'value'} expect_query = six.moves.urllib.parse.urlencode(params) self.client.httpclient.auth_token = 'token' + resp_headers = {'x-openstack-request-id': REQUEST_ID} self.client.httpclient.request( MyUrlComparator(end_url( '/test', query=expect_query, format=self.format), self.client), 'PUT', body='', headers=mox.ContainsKeyValue('X-Auth-Token', 'token') - ).AndReturn((MyResp(400, reason='An error'), '')) + ).AndReturn((MyResp(400, headers=resp_headers, reason='An error'), '')) self.mox.ReplayAll() error = self.assertRaises(exceptions.NeutronClientException, self.client.do_request, 'PUT', '/test', body='', params=params) - self.assertEqual("An error", str(error)) + expected_error = "An error\nNeutron server returns " \ + "request_ids: %s" % [REQUEST_ID] + self.assertEqual(expected_error, str(error)) self.mox.VerifyAll() self.mox.UnsetStubs() @@ -697,21 +703,126 @@ def test_do_request_with_long_uri_exception(self): else: self.fail('Expected exception NOT raised') + def test_do_request_request_ids(self): + self.mox.StubOutWithMock(self.client.httpclient, "request") + params = {'test': 'value'} + expect_query = six.moves.urllib.parse.urlencode(params) + self.client.httpclient.auth_token = 'token' + body = params + expect_body = self.client.serialize(body) + resp_headers = {'x-openstack-request-id': REQUEST_ID} + self.client.httpclient.request( + MyUrlComparator(end_url( + '/test', query=expect_query, + format=self.format), self.client), + 'PUT', body=expect_body, + headers=mox.ContainsKeyValue('X-Auth-Token', 'token') + ).AndReturn((MyResp(200, resp_headers), expect_body)) + + self.mox.ReplayAll() + result = self.client.do_request('PUT', '/test', body=body, + params=params) + self.mox.VerifyAll() + self.mox.UnsetStubs() + + self.assertEqual(body, result) + self.assertEqual([REQUEST_ID], result.request_ids) + + def test_list_request_ids_with_retrieve_all_true(self): + self.mox.StubOutWithMock(self.client.httpclient, "request") + + path = '/test' + resources = 'tests' + fake_query = "marker=myid2&limit=2" + reses1 = {resources: [{'id': 'myid1', }, + {'id': 'myid2', }], + '%s_links' % resources: [{'href': end_url(path, fake_query), + 'rel': 'next'}]} + reses2 = {resources: [{'id': 'myid3', }, + {'id': 'myid4', }]} + resstr1 = self.client.serialize(reses1) + resstr2 = self.client.serialize(reses2) + resp_headers = {'x-openstack-request-id': REQUEST_ID} + self.client.httpclient.request( + end_url(path, "", format=self.format), 'GET', + body=None, + headers=mox.ContainsKeyValue( + 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200, resp_headers), + resstr1)) + self.client.httpclient.request( + MyUrlComparator(end_url(path, fake_query, format=self.format), + self.client), 'GET', + body=None, + headers=mox.ContainsKeyValue( + 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200, resp_headers), + resstr2)) + self.mox.ReplayAll() + result = self.client.list(resources, path) + + self.mox.VerifyAll() + self.mox.UnsetStubs() + + self.assertEqual([REQUEST_ID, REQUEST_ID], result.request_ids) + + def test_list_request_ids_with_retrieve_all_false(self): + self.mox.StubOutWithMock(self.client.httpclient, "request") + + path = '/test' + resources = 'tests' + fake_query = "marker=myid2&limit=2" + reses1 = {resources: [{'id': 'myid1', }, + {'id': 'myid2', }], + '%s_links' % resources: [{'href': end_url(path, fake_query), + 'rel': 'next'}]} + reses2 = {resources: [{'id': 'myid3', }, + {'id': 'myid4', }]} + resstr1 = self.client.serialize(reses1) + resstr2 = self.client.serialize(reses2) + resp_headers = {'x-openstack-request-id': REQUEST_ID} + self.client.httpclient.request( + end_url(path, "", format=self.format), 'GET', + body=None, + headers=mox.ContainsKeyValue( + 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200, resp_headers), + resstr1)) + self.client.httpclient.request( + MyUrlComparator(end_url(path, fake_query, format=self.format), + self.client), 'GET', + body=None, + headers=mox.ContainsKeyValue( + 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200, resp_headers), + resstr2)) + self.mox.ReplayAll() + result = self.client.list(resources, path, retrieve_all=False) + next(result) + self.assertEqual([REQUEST_ID], result.request_ids) + next(result) + self.assertEqual([REQUEST_ID, REQUEST_ID], result.request_ids) + self.mox.VerifyAll() + self.mox.UnsetStubs() + class CLITestV20ExceptionHandler(CLITestV20Base): def _test_exception_handler_v20( self, expected_exception, status_code, expected_msg, error_type=None, error_msg=None, error_detail=None, - error_content=None): + request_id=None, error_content=None): + + resp = MyResp(status_code, {'x-openstack-request-id': request_id}) + if request_id is not None: + expected_msg += "\nNeutron server returns " \ + "request_ids: %s" % [request_id] if error_content is None: error_content = {'NeutronError': {'type': error_type, 'message': error_msg, 'detail': error_detail}} + expected_content = self.client._convert_into_with_meta(error_content, + resp) e = self.assertRaises(expected_exception, client.exception_handler_v20, - status_code, error_content) + status_code, expected_content) self.assertEqual(status_code, e.status_code) self.assertEqual(expected_exception.__name__, e.__class__.__name__) @@ -728,7 +839,7 @@ def test_exception_handler_v20_ip_address_in_use(self): 'fake-network-uuid. The IP address fake-ip is in use.') self._test_exception_handler_v20( exceptions.IpAddressInUseClient, 409, err_msg, - 'IpAddressInUse', err_msg, '') + 'IpAddressInUse', err_msg, '', REQUEST_ID) def test_exception_handler_v20_neutron_known_error(self): known_error_map = [ @@ -754,7 +865,7 @@ def test_exception_handler_v20_neutron_known_error(self): self._test_exception_handler_v20( client_exc, status_code, error_msg + '\n' + error_detail, - server_exc, error_msg, error_detail) + server_exc, error_msg, error_detail, REQUEST_ID) def test_exception_handler_v20_neutron_known_error_without_detail(self): error_msg = 'Network not found' @@ -762,7 +873,7 @@ def test_exception_handler_v20_neutron_known_error_without_detail(self): self._test_exception_handler_v20( exceptions.NetworkNotFoundClient, 404, error_msg, - 'NetworkNotFound', error_msg, error_detail) + 'NetworkNotFound', error_msg, error_detail, REQUEST_ID) def test_exception_handler_v20_unknown_error_to_per_code_exception(self): for status_code, client_exc in exceptions.HTTP_EXCEPTION_MAP.items(): @@ -771,7 +882,7 @@ def test_exception_handler_v20_unknown_error_to_per_code_exception(self): self._test_exception_handler_v20( client_exc, status_code, error_msg + '\n' + error_detail, - 'UnknownError', error_msg, error_detail) + 'UnknownError', error_msg, error_detail, [REQUEST_ID]) def test_exception_handler_v20_neutron_unknown_status_code(self): error_msg = 'Unknown error' @@ -779,7 +890,7 @@ def test_exception_handler_v20_neutron_unknown_status_code(self): self._test_exception_handler_v20( exceptions.NeutronClientException, 501, error_msg + '\n' + error_detail, - 'UnknownError', error_msg, error_detail) + 'UnknownError', error_msg, error_detail, REQUEST_ID) def test_exception_handler_v20_bad_neutron_error(self): for status_code, client_exc in exceptions.HTTP_EXCEPTION_MAP.items(): @@ -787,7 +898,8 @@ def test_exception_handler_v20_bad_neutron_error(self): self._test_exception_handler_v20( client_exc, status_code, expected_msg="{'unknown_key': 'UNKNOWN'}", - error_content=error_content) + error_content=error_content, + request_id=REQUEST_ID) def test_exception_handler_v20_error_dict_contains_message(self): error_content = {'message': 'This is an error message'} @@ -795,7 +907,8 @@ def test_exception_handler_v20_error_dict_contains_message(self): self._test_exception_handler_v20( client_exc, status_code, expected_msg='This is an error message', - error_content=error_content) + error_content=error_content, + request_id=REQUEST_ID) def test_exception_handler_v20_error_dict_not_contain_message(self): # 599 is not contained in HTTP_EXCEPTION_MAP. @@ -804,6 +917,7 @@ def test_exception_handler_v20_error_dict_not_contain_message(self): self._test_exception_handler_v20( exceptions.NeutronClientException, 599, expected_msg=expected_msg, + request_id=None, error_content=error_content) def test_exception_handler_v20_default_fallback(self): @@ -813,6 +927,7 @@ def test_exception_handler_v20_default_fallback(self): self._test_exception_handler_v20( exceptions.NeutronClientException, 599, expected_msg=expected_msg, + request_id=None, error_content=error_content) def test_exception_status(self): @@ -848,3 +963,60 @@ def test_connection_failed(self): self.assertIsNotNone(error.status_code) self.mox.VerifyAll() self.mox.UnsetStubs() + + +class DictWithMetaTest(base.BaseTestCase): + + def test_dict_with_meta(self): + body = {'test': 'value'} + resp = MyResp(200, {'x-openstack-request-id': REQUEST_ID}) + obj = client._DictWithMeta(body, resp) + self.assertEqual(body, obj) + + # Check request_ids attribute is added to obj + self.assertTrue(hasattr(obj, 'request_ids')) + self.assertEqual([REQUEST_ID], obj.request_ids) + + +class TupleWithMetaTest(base.BaseTestCase): + + def test_tuple_with_meta(self): + body = ('test', 'value') + resp = MyResp(200, {'x-openstack-request-id': REQUEST_ID}) + obj = client._TupleWithMeta(body, resp) + self.assertEqual(body, obj) + + # Check request_ids attribute is added to obj + self.assertTrue(hasattr(obj, 'request_ids')) + self.assertEqual([REQUEST_ID], obj.request_ids) + + +class StrWithMetaTest(base.BaseTestCase): + + def test_str_with_meta(self): + body = "test_string" + resp = MyResp(200, {'x-openstack-request-id': REQUEST_ID}) + obj = client._StrWithMeta(body, resp) + self.assertEqual(body, obj) + + # Check request_ids attribute is added to obj + self.assertTrue(hasattr(obj, 'request_ids')) + self.assertEqual([REQUEST_ID], obj.request_ids) + + +class GeneratorWithMetaTest(base.BaseTestCase): + + body = {'test': 'value'} + resp = MyResp(200, {'x-openstack-request-id': REQUEST_ID}) + + def _pagination(self, collection, path, **params): + obj = client._DictWithMeta(self.body, self.resp) + yield obj + + def test_generator(self): + obj = client._GeneratorWithMeta(self._pagination, 'test_collection', + 'test_path', test_args='test_args') + self.assertEqual(self.body, next(obj)) + + self.assertTrue(hasattr(obj, 'request_ids')) + self.assertEqual([REQUEST_ID], obj.request_ids) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 08a850314..9fd2c6a47 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -22,6 +22,7 @@ import requests import six.moves.urllib.parse as urlparse +from six import string_types from neutronclient._i18n import _ from neutronclient import client @@ -44,6 +45,7 @@ def exception_handler_v20(status_code, error_content): :param error_content: deserialized body of error response """ error_dict = None + request_ids = error_content.request_ids if isinstance(error_content, dict): error_dict = error_content.get('NeutronError') # Find real error type @@ -78,7 +80,8 @@ def exception_handler_v20(status_code, error_content): client_exc = exceptions.NeutronClientException raise client_exc(message=error_message, - status_code=status_code) + status_code=status_code, + request_ids=request_ids) class APIParamsCall(object): @@ -97,6 +100,99 @@ def with_params(*args, **kwargs): return with_params +class _RequestIdMixin(object): + """Wrapper class to expose x-openstack-request-id to the caller.""" + def _request_ids_setup(self): + self._request_ids = [] + + @property + def request_ids(self): + return self._request_ids + + def _append_request_ids(self, resp): + """Add request_ids as an attribute to the object + + :param resp: Response object or list of Response objects + """ + if isinstance(resp, list): + # Add list of request_ids if response is of type list. + for resp_obj in resp: + self._append_request_id(resp_obj) + elif resp is not None: + # Add request_ids if response contains single object. + self._append_request_id(resp) + + def _append_request_id(self, resp): + if isinstance(resp, requests.Response): + # Extract 'x-openstack-request-id' from headers if + # response is a Response object. + request_id = resp.headers.get('x-openstack-request-id') + else: + # If resp is of type string. + request_id = resp + if request_id: + self._request_ids.append(request_id) + + +class _DictWithMeta(dict, _RequestIdMixin): + def __init__(self, values, resp): + super(_DictWithMeta, self).__init__(values) + self._request_ids_setup() + self._append_request_ids(resp) + + +class _TupleWithMeta(tuple, _RequestIdMixin): + def __new__(cls, values, resp): + return super(_TupleWithMeta, cls).__new__(cls, values) + + def __init__(self, values, resp): + self._request_ids_setup() + self._append_request_ids(resp) + + +class _StrWithMeta(str, _RequestIdMixin): + def __new__(cls, value, resp): + return super(_StrWithMeta, cls).__new__(cls, value) + + def __init__(self, values, resp): + self._request_ids_setup() + self._append_request_ids(resp) + + +class _GeneratorWithMeta(_RequestIdMixin): + def __init__(self, paginate_func, collection, path, **params): + self.paginate_func = paginate_func + self.collection = collection + self.path = path + self.params = params + self.generator = None + self._request_ids_setup() + + def _paginate(self): + for r in self.paginate_func( + self.collection, self.path, **self.params): + yield r, r.request_ids + + def __iter__(self): + return self + + # Python 3 compatibility + def __next__(self): + return self.next() + + def next(self): + if not self.generator: + self.generator = self._paginate() + + try: + obj, req_id = next(self.generator) + self._append_request_ids(req_id) + except StopIteration: + raise StopIteration() + + return obj + + class ClientBase(object): """Client for the OpenStack Neutron v2.0 API. @@ -162,7 +258,7 @@ def __init__(self, **kwargs): self.action_prefix = "/v%s" % (self.version) self.retry_interval = 1 - def _handle_fault_response(self, status_code, response_body): + def _handle_fault_response(self, status_code, response_body, resp): # Create exception with HTTP status code and message _logger.debug("Error message: %s", response_body) # Add deserialized error message to exception arguments @@ -172,8 +268,9 @@ def _handle_fault_response(self, status_code, response_body): # If unable to deserialized body it is probably not a # Neutron error des_error_body = {'message': response_body} + error_body = self._convert_into_with_meta(des_error_body, resp) # Raise the appropriate exception - exception_handler_v20(status_code, des_error_body) + exception_handler_v20(status_code, error_body) def do_request(self, method, action, body=None, headers=None, params=None): # Add format and tenant_id @@ -193,11 +290,12 @@ def do_request(self, method, action, body=None, headers=None, params=None): requests.codes.created, requests.codes.accepted, requests.codes.no_content): - return self.deserialize(replybody, status_code) + data = self.deserialize(replybody, status_code) + return self._convert_into_with_meta(data, resp) else: if not replybody: replybody = resp.reason - self._handle_fault_response(status_code, replybody) + self._handle_fault_response(status_code, replybody, resp) def get_auth_info(self): return self.httpclient.get_auth_info() @@ -271,11 +369,14 @@ def put(self, action, body=None, headers=None, params=None): def list(self, collection, path, retrieve_all=True, **params): if retrieve_all: res = [] + request_ids = [] for r in self._pagination(collection, path, **params): res.extend(r[collection]) - return {collection: res} + request_ids.extend(r.request_ids) + return _DictWithMeta({collection: res}, request_ids) else: - return self._pagination(collection, path, **params) + return _GeneratorWithMeta(self._pagination, collection, + path, **params) def _pagination(self, collection, path, **params): if params.get('page_reverse', False): @@ -297,6 +398,15 @@ def _pagination(self, collection, path, **params): except KeyError: break + def _convert_into_with_meta(self, item, resp): + if item: + if isinstance(item, dict): + return _DictWithMeta(item, resp) + elif isinstance(item, string_types): + return _StrWithMeta(item, resp) + else: + return _TupleWithMeta((), resp) + class Client(ClientBase): diff --git a/releasenotes/notes/return-request-id-to-caller-15b1d23a4ddc27a3.yaml b/releasenotes/notes/return-request-id-to-caller-15b1d23a4ddc27a3.yaml new file mode 100644 index 000000000..da7c5f817 --- /dev/null +++ b/releasenotes/notes/return-request-id-to-caller-15b1d23a4ddc27a3.yaml @@ -0,0 +1,3 @@ +--- +features: + - Neutron client returns 'x-openstack-request-id'. From 585a4ffbfa85cf27293431086056e67706fedc48 Mon Sep 17 00:00:00 2001 From: Richard Theis Date: Thu, 4 Feb 2016 16:02:18 -0600 Subject: [PATCH 384/845] Devref Update: Transition to OpenStack Client Update the "Transition to OpenStack Client" devref with information about the new OSC command specs process. This process can be used to propose new networking commands while deferring their implementation. This process may help speed the overall transition to OSC. This update also includes information about the OSC neutron support etherpad which provides detailed status for the transition. And finally, updates were made to the overall transition plan based on discussions at the midcycle. Change-Id: I46bc066d2169a1e20af13baac7131ffa2eedd7c8 Related-Bug: #1521291 --- doc/source/devref/transition_to_osc.rst | 162 ++++++++++++++++-------- 1 file changed, 106 insertions(+), 56 deletions(-) diff --git a/doc/source/devref/transition_to_osc.rst b/doc/source/devref/transition_to_osc.rst index 9d4c87a2a..0ac2c901f 100644 --- a/doc/source/devref/transition_to_osc.rst +++ b/doc/source/devref/transition_to_osc.rst @@ -26,13 +26,13 @@ Transition to OpenStack Client This document details the transition roadmap for moving the neutron client's OpenStack Networking API support, both the Python library and the ``neutron`` command-line interface (CLI), to the -`OpenStack client (OSC) `_ +`OpenStack Client (OSC) `_ and the `OpenStack Python SDK `_. This transition is being guided by the `Deprecate individual CLIs in favour of OSC `_ -OpenStack spec. See the `Neutron RFE `_ and -`OSC neutron-client blueprint `_ -for the overall progress of this transition. +OpenStack spec. See the `Neutron RFE `_, +`OSC neutron support etherpad `_ and +details below for the overall progress of this transition. Overview -------- @@ -42,17 +42,15 @@ deprecated and then eventually removed. The ``neutron`` CLI will be replaced by OSC's networking support available via the ``openstack`` CLI. This is similar to the deprecation and removal process for the `keystone client's `_ -``keystone`` CLI. - -The neutron client's Python library won't be deprecated. It will be available -along side the networking support provided by the OpenStack Python SDK. However, -the OpenStack Python SDK will be used to implement OSC's networking support. +``keystone`` CLI. The neutron client's Python library won't be deprecated. +It will be available along side the networking support provided by the +OpenStack Python SDK. Users of the neutron client's command extensions will need to transition to the `OSC plugin system `_ before the ``neutron`` CLI is removed. Such users will maintain their OSC plugin -within their own project and will be responsible for deprecating and removing -their ``neutron`` CLI extension. +commands within their own project and will be responsible for deprecating and +removing their ``neutron`` CLI extension. Transition Steps ---------------- @@ -92,13 +90,12 @@ Transition Steps * `Security Group Rule CRUD `_ -6. **In Progress:** OSC enhances its networking support under the - `neutron-client `_ - OSC spec. At this point and when applicable, enhancements to the ``neutron`` +6. **In Progress:** OSC continues enhancing its networking support. + At this point and when applicable, enhancements to the ``neutron`` CLI must also be made to the ``openstack`` CLI and the OpenStack Python SDK. Enhancements to the networking support in the OpenStack Python SDK will be - handled via bugs. Neutron stadium users of the neutron client's command - extensions should start their transition to the OSC plugin system. + handled via bugs. Users of the neutron client's command extensions should + start their transition to the OSC plugin system. See the developer guide section below for more information on this step. 7. **Not Started:** Deprecate the ``neutron`` CLI once the criteria below have @@ -112,64 +109,116 @@ Transition Steps equivalent to the ``neutron`` CLI and it contains sufficient functional and unit test coverage. - * Neutron core and advanced services projects, Neutron documentation and - `DevStack `_ use ``openstack`` - CLI instead of ``neutron`` CLI. + * `Neutron Stadium `_ + projects, Neutron documentation and `DevStack `_ + use ``openstack`` CLI instead of ``neutron`` CLI. - * Most neutron stadium users of the neutron client's command extensions have - transitioned to the OSC plugin system and use the ``openstack`` CLI instead - of the ``neutron`` CLI. + * Most users of the neutron client's command extensions have transitioned + to the OSC plugin system and use the ``openstack`` CLI instead of the + ``neutron`` CLI. 8. **Not Started:** Remove the ``neutron`` CLI after two deprecation cycles. Developer Guide --------------- -The ``neutron`` CLI version 3.1.1, without extensions, supports over 200 -commands while the ``openstack`` CLI version 2.0.1 supports about 20 -networking commands. Of the 20 commands, most do not have all of the options +The ``neutron`` CLI version 4.x, without extensions, supports over 200 +commands while the ``openstack`` CLI version 2.1.0 supports about 30 +networking commands. Of the 30 commands, many do not have all of the options or arguments of their ``neutron`` CLI equivalent. With this large functional -gap, one critical question for developers during this transition is "Which -CLI do I change?" The answer depends on the state of a command and the -state of the overall transition. Details are outlined in the table -below. Early stages of the transition will require dual maintenance. -Eventually, dual maintenance will be reduced to critical bug fixes only -with feature requests only being made to the ``openstack`` CLI. - -+----------------------+------------------------+----------------------------------------------+ -| neutron Command | openstack Command | CLI to Change | -+======================+========================+==============================================+ -| Exists | Doesn't Exist | neutron | -+----------------------+------------------------+----------------------------------------------+ -| Exists | In Progress | neutron and update related OSC bug | -+----------------------+------------------------+----------------------------------------------+ -| Exists | Exists | neutron and openstack | -+----------------------+------------------------+----------------------------------------------+ -| Doesn't Exist | Doesn't Exist | neutron and openstack | -+----------------------+------------------------+----------------------------------------------+ -| Doesn't Exist | Exists | openstack | -+----------------------+------------------------+----------------------------------------------+ - -When adding or updating an ``openstack`` networking command, changes may -first be required to the OpenStack Python SDK to support the underlying -networking resource object, properties and/or actions. Once the OpenStack -Python SDK changes are merged, the related OSC changes can be merged. -The OSC changes may require an update to the OSC openstacksdk version in the +gap, a couple critical questions for developers during this transition are "Which +CLI do I change?" and "Where does my CLI belong?" The answer depends on the +state of a command and the state of the overall transition. Details are +outlined in the tables below. Early stages of the transition will require dual +maintenance. Eventually, dual maintenance will be reduced to critical bug fixes +only with feature requests only being made to the ``openstack`` CLI. + +**Which CLI do I change?** + ++----------------------+------------------------+-------------------------------------------------+ +| ``neutron`` Command | ``openstack`` Command | CLI to Change | ++======================+========================+=================================================+ +| Exists | Doesn't Exist | ``neutron`` | ++----------------------+------------------------+-------------------------------------------------+ +| Exists | In Progress | ``neutron`` and ``openstack`` | +| | | (update related blueprint or bug) | ++----------------------+------------------------+-------------------------------------------------+ +| Exists | Exists | ``openstack`` | +| | | (assumes command parity resulting in | +| | | ``neutron`` being deprecated) | ++----------------------+------------------------+-------------------------------------------------+ +| Doesn't Exist | Doesn't Exist | ``openstack`` | ++----------------------+------------------------+-------------------------------------------------+ + +**Where does my CLI belong?** + ++---------------------------+-------------------+-------------------------------------------------+ +| Networking Commands | OSC Plugin | OpenStack Project for ``openstack`` Commands | ++===========================+===================+=================================================+ +| Core (Stable) | No | python-openstackclient | ++---------------------------+-------------------+-------------------------------------------------+ +| Core (New/Experimental) | Yes | python-neutronclient | +| | | (with possible move to python-openstackclient) | ++---------------------------+-------------------+-------------------------------------------------+ +| LBaaS v2 | Yes | neutron-lbaas | ++---------------------------+-------------------+-------------------------------------------------+ +| VPNaaS v2 | Yes | neutron-vpnaas | ++---------------------------+-------------------+-------------------------------------------------+ +| FWaaS v2 | Yes | neutron-fwaas | ++---------------------------+-------------------+-------------------------------------------------+ +| LBaaS v1 | N/A | None (deprecated) | ++---------------------------+-------------------+-------------------------------------------------+ +| FWaaS v1 | N/A | None (deprecated) | ++---------------------------+-------------------+-------------------------------------------------+ +| Other | Yes | Applicable project owning networking resource | ++---------------------------+-------------------+-------------------------------------------------+ + + +The following network resources are part of the "Core (Stable)" group: + +- availability zone +- extension +- floating ip +- network +- port +- quota +- rbac +- router +- security group +- security group rule +- subnet +- subnet pool + + +When adding or updating an ``openstack`` networking command to +python-openstackclient, changes may first be required to the +OpenStack Python SDK to support the underlying networking resource object, +properties and/or actions. Once the OpenStack Python SDK changes are merged, +the related OSC changes can be merged. The OSC changes may require an update +to the OSC openstacksdk version in the `requirements.txt `_ -file. +file. ``openstack`` networking commands outside python-openstackclient +are encouraged but not required to use the OpenStack Python SDK. + +When adding an ``openstack`` networking command to python-openstackclient, +you can optionally propose an +`OSC command spec `_ +which documents the new command interface before proceeding with the implementation. -Neutron stadium users of the neutron client's command extensions must adopt the +Users of the neutron client's command extensions must adopt the `OSC plugin system `_ for this transition. Such users will maintain their OSC plugin within their own project and should follow the guidance in the table above to determine -which CLI to change. +which command to change. Developer References -------------------- -* See `OSC neutron-client blueprint `_ - to determine if an ``openstack`` command is in progress. See the ``Related bugs`` list. +* See `OSC neutron support etherpad `_ + to determine if an ``openstack`` command is in progress. * See `OSC command list `_ to determine if an ``openstack`` command exists. +* See `OSC command spec list `_ + to determine if an ``openstack`` command spec exists. * See `OSC plugin command list `_ to determine if an ``openstack`` plugin command exists. * See `OSC command structure `_ @@ -178,5 +227,6 @@ Developer References for guidance on creating new OSC command interfaces. * See `OSC plugin `_ for information on the OSC plugin system to be used for ``neutron`` CLI extensions. +* Create an OSC blueprint: https://blueprints.launchpad.net/python-openstackclient/ * Report an OSC bug: https://bugs.launchpad.net/python-openstackclient/+filebug * Report an OpenStack Python SDK bug: https://bugs.launchpad.net/python-openstacksdk/+filebug From b667b32af4a8b635cbe3f5a3fa8a231b6114a138 Mon Sep 17 00:00:00 2001 From: Haim Daniel Date: Sun, 8 Nov 2015 13:20:45 +0200 Subject: [PATCH 385/845] add rbac support for qos-policies APIImpact Partial-implements: rfe role-based access control for qos policies Related Blueprint: rbac-qos Related-bug: #1512587 Depends-On: I1c59073daa181005a3e878bc2fe033a0709fbf31 Change-Id: If11a10da617e279b19c17d995b7ab89be8cd9427 --- neutronclient/neutron/v2_0/rbac.py | 35 +++++++++++++------ neutronclient/tests/unit/test_cli20_rbac.py | 34 ++++++++++++------ ...dd-rbac-qos-type-support-c42e31fadd7b.yaml | 8 +++++ test-requirements.txt | 1 + 4 files changed, 57 insertions(+), 21 deletions(-) create mode 100644 releasenotes/notes/add-rbac-qos-type-support-c42e31fadd7b.yaml diff --git a/neutronclient/neutron/v2_0/rbac.py b/neutronclient/neutron/v2_0/rbac.py index d840af5b1..46f254af8 100644 --- a/neutronclient/neutron/v2_0/rbac.py +++ b/neutronclient/neutron/v2_0/rbac.py @@ -16,20 +16,31 @@ from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronV20 +# key=object_type: value={key=resource, value=cmd_resource} +RBAC_OBJECTS = {'network': {'network': 'network'}, + 'qos-policy': {'policy': 'qos_policy'}} -def get_rbac_object_id(client, obj_type, obj_id_or_name): - if obj_type == 'network': - obj_id = neutronV20.find_resourceid_by_name_or_id(client, - 'network', - obj_id_or_name) - return obj_id + +def _get_cmd_resource(obj_type): + resource = list(RBAC_OBJECTS[obj_type])[0] + cmd_resource = RBAC_OBJECTS[obj_type][resource] + return resource, cmd_resource + + +def get_rbac_obj_params(client, obj_type, obj_id_or_name): + resource, cmd_resource = _get_cmd_resource(obj_type) + obj_id = neutronV20.find_resourceid_by_name_or_id( + client=client, resource=resource, name_or_id=obj_id_or_name, + cmd_resource=cmd_resource) + + return obj_id, cmd_resource class ListRBACPolicy(neutronV20.ListCommand): """List RBAC policies that belong to a given tenant.""" resource = 'rbac_policy' - list_columns = ['id', 'object_id'] + list_columns = ['id', 'object_type', 'object_id'] pagination_support = True sorting_support = True allow_names = False @@ -53,7 +64,7 @@ def add_known_arguments(self, parser): metavar='RBAC_OBJECT', help=_('ID or name of the RBAC object.')) parser.add_argument( - '--type', choices=['network'], + '--type', choices=RBAC_OBJECTS.keys(), required=True, help=_('Type of the object that RBAC policy affects.')) parser.add_argument( @@ -67,11 +78,13 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): neutron_client = self.get_client() - _object_id = get_rbac_object_id(neutron_client, parsed_args.type, - parsed_args.name) + neutron_client.format = parsed_args.request_format + _object_id, _object_type = get_rbac_obj_params(neutron_client, + parsed_args.type, + parsed_args.name) body = { 'object_id': _object_id, - 'object_type': parsed_args.type, + 'object_type': _object_type, 'target_tenant': parsed_args.target_tenant, 'action': parsed_args.action, } diff --git a/neutronclient/tests/unit/test_cli20_rbac.py b/neutronclient/tests/unit/test_cli20_rbac.py index 1d287bad6..ca7ea46d2 100644 --- a/neutronclient/tests/unit/test_cli20_rbac.py +++ b/neutronclient/tests/unit/test_cli20_rbac.py @@ -15,41 +15,54 @@ import sys +import testscenarios + from neutronclient.neutron.v2_0 import rbac from neutronclient.tests.unit import test_cli20 +load_tests = testscenarios.load_tests_apply_scenarios -class CLITestV20RBACJSON(test_cli20.CLITestV20Base): +class CLITestV20RBACBaseJSON(test_cli20.CLITestV20Base): non_admin_status_resources = ['rbac_policy'] + scenarios = [ + ('network rbac objects', + {'object_type_name': 'network', 'object_type_val': 'network'}), + ('qos policy rbac objects', + {'object_type_name': 'qos-policy', 'object_type_val': 'qos_policy'}), + ] + def test_create_rbac_policy_with_mandatory_params(self): - # Create rbac: rbac_object --type network --action access_as_shared + # Create rbac: rbac_object --type --action + # access_as_shared resource = 'rbac_policy' cmd = rbac.CreateRBACPolicy(test_cli20.MyApp(sys.stdout), None) name = 'rbac_object' myid = 'myid' - args = [name, '--type', 'network', + args = [name, '--type', self.object_type_name, '--action', 'access_as_shared'] position_names = ['object_id', 'object_type', 'target_tenant', 'action'] - position_values = [name, 'network', None, 'access_as_shared'] + position_values = [name, self.object_type_val, None, + 'access_as_shared'] self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) def test_create_rbac_policy_with_all_params(self): - # Create rbac: rbac_object --type network --target-tenant tenant_id - # --action access_as_external + # Create rbac: rbac_object --type + # --target-tenant tenant_id --action access_as_external resource = 'rbac_policy' cmd = rbac.CreateRBACPolicy(test_cli20.MyApp(sys.stdout), None) name = 'rbac_object' myid = 'myid' - args = [name, '--type', 'network', + args = [name, '--type', self.object_type_name, '--target-tenant', 'tenant_id', '--action', 'access_as_external'] position_names = ['object_id', 'object_type', 'target_tenant', 'action'] - position_values = [name, 'network', 'tenant_id', 'access_as_external'] + position_values = [name, self.object_type_val, 'tenant_id', + 'access_as_external'] self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) @@ -59,12 +72,13 @@ def test_create_rbac_policy_with_unicode(self): cmd = rbac.CreateRBACPolicy(test_cli20.MyApp(sys.stdout), None) name = u'\u7f51\u7edc' myid = 'myid' - args = [name, '--type', 'network', + args = [name, '--type', self.object_type_name, '--target-tenant', 'tenant_id', '--action', 'access_as_external'] position_names = ['object_id', 'object_type', 'target_tenant', 'action'] - position_values = [name, 'network', 'tenant_id', 'access_as_external'] + position_values = [name, self.object_type_val, 'tenant_id', + 'access_as_external'] self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) diff --git a/releasenotes/notes/add-rbac-qos-type-support-c42e31fadd7b.yaml b/releasenotes/notes/add-rbac-qos-type-support-c42e31fadd7b.yaml new file mode 100644 index 000000000..9dff77c75 --- /dev/null +++ b/releasenotes/notes/add-rbac-qos-type-support-c42e31fadd7b.yaml @@ -0,0 +1,8 @@ +--- +features: + - | + CLI support for QoS policy RBAC. + + * The ``rbac-create`` command include a --type qos-policy + option. + * The ``rbac-list`` command output includes a new 'type' column. diff --git a/test-requirements.txt b/test-requirements.txt index 992d131af..7bece79a1 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -16,4 +16,5 @@ requests-mock>=0.7.0 # Apache-2.0 sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 # BSD testrepository>=0.0.18 # Apache-2.0/BSD testtools>=1.4.0 # MIT +testscenarios>=0.4 # Apache-2.0/BSD tempest-lib>=0.14.0 # Apache-2.0 From a147631309cfc57f47f74fc9721f07db85d3fe65 Mon Sep 17 00:00:00 2001 From: Nikolas Hermanns Date: Fri, 12 Feb 2016 11:21:25 +0100 Subject: [PATCH 386/845] Misleading output when network is not found Output is: Unable to find network with name '7ea795c9-0b72-4585-bd91-9bf83c0d1c80' Output should be: Unable to find network with name or id Someone could think that the net id is not checked. Closes-Bug: #1544907 Change-Id: I6999ab4e07a58751a9860764447c00fe7bf8ee81 --- neutronclient/neutron/v2_0/__init__.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index f3f466957..d740aac3e 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -118,9 +118,17 @@ def find_resource_by_name_or_id(client, resource, name_or_id, return find_resource_by_id(client, resource, name_or_id, cmd_resource, parent_id, fields) except exceptions.NotFound: - return _find_resource_by_name(client, resource, name_or_id, - project_id, cmd_resource, parent_id, - fields) + try: + return _find_resource_by_name(client, resource, name_or_id, + project_id, cmd_resource, parent_id, + fields) + except exceptions.NotFound: + not_found_message = (_("Unable to find %(resource)s with name " + "or id '%(name_or_id)s'") % + {'resource': resource, + 'name_or_id': name_or_id}) + raise exceptions.NotFound( + message=not_found_message) def find_resourceid_by_name_or_id(client, resource, name_or_id, From 1b765bda395855f34ae454dcec7607cdc314211a Mon Sep 17 00:00:00 2001 From: Brandon Palm Date: Tue, 23 Feb 2016 14:22:58 -0600 Subject: [PATCH 387/845] Fixed a bunch of spacing Nothing too crazy here. Just changed some spacing problems that I encounted in the neutronclient. Change-Id: I236a4af738851508681f595cb9233c2cfbc52899 --- neutronclient/common/extension.py | 1 - neutronclient/neutron/v2_0/__init__.py | 6 +++--- neutronclient/neutron/v2_0/quota.py | 2 +- neutronclient/neutron/v2_0/router.py | 2 +- neutronclient/tests/unit/test_cli20_router.py | 6 +++--- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/neutronclient/common/extension.py b/neutronclient/common/extension.py index f7e361bed..ce06dee35 100644 --- a/neutronclient/common/extension.py +++ b/neutronclient/common/extension.py @@ -42,7 +42,6 @@ def execute(self, parsed_args): class ClientExtensionList(NeutronClientExtension, neutronV20.ListCommand): - def take_action(self, parsed_args): # NOTE(mdietz): Calls 'execute' to provide a consistent pattern # for any implementers adding extensions with diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index f3f466957..0b1a85563 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -202,9 +202,9 @@ def _process_previous_argument(current_arg, _value_number, current_type_str, values_specs): if current_arg is not None: if _value_number == 0 and (current_type_str or _list_flag): - # This kind of argument should have value - raise exceptions.CommandError( - _("Invalid values_specs %s") % ' '.join(values_specs)) + # This kind of argument should have value + raise exceptions.CommandError( + _("Invalid values_specs %s") % ' '.join(values_specs)) if _value_number > 1 or _list_flag or current_type_str == 'list': current_arg.update({'nargs': '+'}) elif _value_number == 0: diff --git a/neutronclient/neutron/v2_0/quota.py b/neutronclient/neutron/v2_0/quota.py index a88f0c628..e9d476d0d 100644 --- a/neutronclient/neutron/v2_0/quota.py +++ b/neutronclient/neutron/v2_0/quota.py @@ -89,7 +89,7 @@ def take_action(self, parsed_args): info = data[collection] _columns = len(info) > 0 and sorted(info[0].keys()) or [] return (_columns, (utils.get_item_properties(s, _columns) - for s in info)) + for s in info)) class ShowQuota(neutronV20.NeutronCommand, show.ShowOne): diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index 88da856b3..934512bc4 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -168,7 +168,7 @@ def run(self, parsed_args): resource, value = parsed_args.interface.split('=', 1) if resource not in ['subnet', 'port']: exceptions.CommandError(_('You must specify either subnet or ' - 'port for INTERFACE parameter.')) + 'port for INTERFACE parameter.')) else: resource = 'subnet' value = parsed_args.interface diff --git a/neutronclient/tests/unit/test_cli20_router.py b/neutronclient/tests/unit/test_cli20_router.py index 66372f861..6555b4ada 100644 --- a/neutronclient/tests/unit/test_cli20_router.py +++ b/neutronclient/tests/unit/test_cli20_router.py @@ -226,7 +226,7 @@ def test_update_router_add_route(self): '--route', 'destination=10.0.3.0/24,nexthop=10.0.0.10'] routes = [{'destination': '10.0.3.0/24', - 'nexthop': '10.0.0.10'}] + 'nexthop': '10.0.0.10'}] updatefields = {'routes': routes} self._test_update_resource(resource, cmd, myid, args, updatefields) @@ -244,9 +244,9 @@ def test_update_router_add_routes(self): 'destination=fd7a:1d63:2063::/64,' 'nexthop=fd7a:1d63:2063:0:f816:3eff:fe0e:a697'] routes = [{'destination': '10.0.3.0/24', - 'nexthop': '10.0.0.10'}, + 'nexthop': '10.0.0.10'}, {'destination': 'fd7a:1d63:2063::/64', - 'nexthop': 'fd7a:1d63:2063:0:f816:3eff:fe0e:a697'}] + 'nexthop': 'fd7a:1d63:2063:0:f816:3eff:fe0e:a697'}] updatefields = {'routes': routes} self._test_update_resource(resource, cmd, myid, args, updatefields) From c422c2610ed219f84e4f9cfaa8ffdf5689999e7e Mon Sep 17 00:00:00 2001 From: Stephen Balukoff Date: Sat, 29 Aug 2015 02:52:41 -0700 Subject: [PATCH 388/845] LBaaS updates to reflect shared pools feature These updates allow the CLI user to fully take advantage of the shared pools functionality being added to Neutron LBaaS in preparation for L7 switching functionality. The Neutron LBaaS patch is here: https://review.openstack.org/#/c/218560/ Partially-Implements: blueprint lbaas-l7-rules Change-Id: Ib72d743ff0d6ca7f0fde034a8c73029308ca01a1 --- neutronclient/neutron/v2_0/lb/v2/listener.py | 57 +++++++++++------ neutronclient/neutron/v2_0/lb/v2/pool.py | 61 +++++++++++++++---- .../tests/unit/lb/v2/test_cli20_listener.py | 44 ++++++++++++- .../tests/unit/lb/v2/test_cli20_pool.py | 48 +++++++++++++-- ...shared-pools-support-6f79b565afad3e47.yaml | 8 +++ 5 files changed, 180 insertions(+), 38 deletions(-) create mode 100644 releasenotes/notes/add-shared-pools-support-6f79b565afad3e47.yaml diff --git a/neutronclient/neutron/v2_0/lb/v2/listener.py b/neutronclient/neutron/v2_0/lb/v2/listener.py index 4a4171908..b63e2c28f 100644 --- a/neutronclient/neutron/v2_0/lb/v2/listener.py +++ b/neutronclient/neutron/v2_0/lb/v2/listener.py @@ -16,18 +16,27 @@ # from neutronclient._i18n import _ +from neutronclient.common import exceptions from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronV20 def _get_loadbalancer_id(client, lb_id_or_name): return neutronV20.find_resourceid_by_name_or_id( - client, - 'loadbalancer', - lb_id_or_name, + client, 'loadbalancer', lb_id_or_name, cmd_resource='lbaas_loadbalancer') +def _get_pool(client, pool_id_or_name): + return neutronV20.find_resource_by_name_or_id( + client, 'pool', pool_id_or_name, cmd_resource='lbaas_pool') + + +def _get_pool_id(client, pool_id_or_name): + return neutronV20.find_resourceid_by_name_or_id( + client, 'pool', pool_id_or_name, cmd_resource='lbaas_pool') + + class ListListener(neutronV20.ListCommand): """LBaaS v2 List listeners that belong to a given tenant.""" @@ -64,7 +73,8 @@ def add_known_arguments(self, parser): help=_('Description of the listener.')) parser.add_argument( '--name', - help=_('The name of the listener.')) + help=_('The name of the listener. At least one of --default-pool ' + 'or --loadbalancer must be specified.')) parser.add_argument( '--default-tls-container-ref', dest='default_tls_container_ref', @@ -75,9 +85,11 @@ def add_known_arguments(self, parser): dest='sni_container_refs', nargs='+', help=_('List of TLS container references for SNI.')) + parser.add_argument( + '--default-pool', + help=_('Default pool for the listener.')) parser.add_argument( '--loadbalancer', - required=True, metavar='LOADBALANCER', help=_('ID or name of the load balancer.')) parser.add_argument( @@ -93,22 +105,29 @@ def add_known_arguments(self, parser): help=_('Protocol port for the listener.')) def args2body(self, parsed_args): + resource = { + 'protocol': parsed_args.protocol, + 'protocol_port': parsed_args.protocol_port, + 'admin_state_up': parsed_args.admin_state + } + if not parsed_args.loadbalancer and not parsed_args.default_pool: + message = _('Either --default-pool or --loadbalancer must be ' + 'specified.') + raise exceptions.CommandError(message) if parsed_args.loadbalancer: - parsed_args.loadbalancer = _get_loadbalancer_id( - self.get_client(), - parsed_args.loadbalancer) - body = {'loadbalancer_id': parsed_args.loadbalancer, - 'protocol': parsed_args.protocol, - 'protocol_port': parsed_args.protocol_port, - 'admin_state_up': parsed_args.admin_state} - - neutronV20.update_dict(parsed_args, body, + loadbalancer_id = _get_loadbalancer_id( + self.get_client(), parsed_args.loadbalancer) + resource['loadbalancer_id'] = loadbalancer_id + if parsed_args.default_pool: + default_pool_id = _get_pool_id( + self.get_client(), parsed_args.default_pool) + resource['default_pool_id'] = default_pool_id + + neutronV20.update_dict(parsed_args, resource, ['connection_limit', 'description', - 'loadbalancer_id', 'name', - 'default_tls_container_ref', - 'sni_container_refs', - 'tenant_id']) - return {self.resource: body} + 'name', 'default_tls_container_ref', + 'sni_container_refs', 'tenant_id']) + return {self.resource: resource} class UpdateListener(neutronV20.UpdateCommand): diff --git a/neutronclient/neutron/v2_0/lb/v2/pool.py b/neutronclient/neutron/v2_0/lb/v2/pool.py index 2745091e4..6cd0b8be5 100644 --- a/neutronclient/neutron/v2_0/lb/v2/pool.py +++ b/neutronclient/neutron/v2_0/lb/v2/pool.py @@ -1,6 +1,7 @@ # Copyright 2013 Mirantis Inc. # Copyright 2014 Blue Box Group, Inc. # Copyright 2015 Hewlett-Packard Development Company, L.P. +# Copyright 2015 Blue Box, an IBM Company # All Rights Reserved # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -17,10 +18,27 @@ # from neutronclient._i18n import _ +from neutronclient.common import exceptions from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronV20 +def _get_loadbalancer_id(client, lb_id_or_name): + return neutronV20.find_resourceid_by_name_or_id( + client, 'loadbalancer', lb_id_or_name, + cmd_resource='lbaas_loadbalancer') + + +def _get_listener(client, listener_id_or_name): + return neutronV20.find_resource_by_name_or_id( + client, 'listener', listener_id_or_name) + + +def _get_listener_id(client, listener_id_or_name): + return neutronV20.find_resourceid_by_name_or_id( + client, 'listener', listener_id_or_name) + + class ListPool(neutronV20.ListCommand): """LBaaS v2 List pools that belong to a given tenant.""" @@ -70,16 +88,22 @@ def add_known_arguments(self, parser): 'cookie name')) parser.add_argument( '--name', help=_('The name of the pool.')) + parser.add_argument( + '--listener', + help=_('Listener whose default-pool should be set to this pool. ' + 'At least one of --listener or --loadbalancer must be ' + 'specified.')) + parser.add_argument( + '--loadbalancer', + help=_('Loadbalancer with which this pool should be associated. ' + 'At least one of --listener or --loadbalancer must be ' + 'specified.')) parser.add_argument( '--lb-algorithm', required=True, choices=['ROUND_ROBIN', 'LEAST_CONNECTIONS', 'SOURCE_IP'], help=_('The algorithm used to distribute load between the members ' 'of the pool.')) - parser.add_argument( - '--listener', - required=True, - help=_('The listener to associate with the pool')) parser.add_argument( '--protocol', required=True, @@ -88,16 +112,29 @@ def add_known_arguments(self, parser): help=_('Protocol for balancing.')) def args2body(self, parsed_args): - _listener_id = neutronV20.find_resourceid_by_name_or_id( - self.get_client(), 'listener', parsed_args.listener) - body = {'admin_state_up': parsed_args.admin_state, - 'protocol': parsed_args.protocol, - 'lb_algorithm': parsed_args.lb_algorithm, - 'listener_id': _listener_id} - neutronV20.update_dict(parsed_args, body, + resource = { + 'admin_state_up': parsed_args.admin_state, + 'protocol': parsed_args.protocol, + 'lb_algorithm': parsed_args.lb_algorithm + } + if not parsed_args.listener and not parsed_args.loadbalancer: + message = _('At least one of --listener or --loadbalancer must be ' + 'specified.') + raise exceptions.CommandError(message) + if parsed_args.listener: + listener_id = _get_listener_id( + self.get_client(), + parsed_args.listener) + resource['listener_id'] = listener_id + if parsed_args.loadbalancer: + loadbalancer_id = _get_loadbalancer_id( + self.get_client(), + parsed_args.loadbalancer) + resource['loadbalancer_id'] = loadbalancer_id + neutronV20.update_dict(parsed_args, resource, ['description', 'name', 'session_persistence', 'tenant_id']) - return {self.resource: body} + return {self.resource: resource} class UpdatePool(neutronV20.UpdateCommand): diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_listener.py b/neutronclient/tests/unit/lb/v2/test_cli20_listener.py index 77d62e0b0..581e511ea 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_listener.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_listener.py @@ -16,14 +16,15 @@ import sys +from neutronclient.common import exceptions from neutronclient.neutron.v2_0.lb.v2 import listener from neutronclient.tests.unit import test_cli20 class CLITestV20LbListenerJSON(test_cli20.CLITestV20Base): - def test_create_listener_with_mandatory_params(self): - # lbaas-listener-create with mandatory params only. + def test_create_listener_with_loadbalancer(self): + # lbaas-listener-create with --loadbalancer resource = 'listener' cmd_resource = 'lbaas_listener' cmd = listener.CreateListener(test_cli20.MyApp(sys.stdout), None) @@ -40,6 +41,41 @@ def test_create_listener_with_mandatory_params(self): position_names, position_values, cmd_resource=cmd_resource) + def test_create_listener_with_default_pool(self): + # lbaas-listener-create with --default-pool and no --loadbalancer. + resource = 'listener' + cmd_resource = 'lbaas_listener' + cmd = listener.CreateListener(test_cli20.MyApp(sys.stdout), None) + my_id = 'my-id' + default_pool_id = 'default-pool' + protocol = 'TCP' + protocol_port = '80' + args = ['--protocol', protocol, '--protocol-port', protocol_port, + '--default-pool', default_pool_id] + position_names = ['protocol', 'protocol_port', 'default_pool_id'] + position_values = [protocol, protocol_port, default_pool_id, + True] + self._test_create_resource(resource, cmd, '', my_id, args, + position_names, position_values, + cmd_resource=cmd_resource) + + def test_create_listener_with_no_loadbalancer_or_default_pool(self): + # lbaas-listener-create without --default-pool or --loadbalancer. + resource = 'listener' + cmd_resource = 'lbaas_listener' + cmd = listener.CreateListener(test_cli20.MyApp(sys.stdout), None) + my_id = 'my-id' + protocol = 'TCP' + protocol_port = '80' + args = ['--protocol', protocol, '--protocol-port', protocol_port] + position_names = ['protocol', 'protocol_port'] + position_values = [protocol, protocol_port, True] + self._test_create_resource(resource, cmd, '', my_id, args, + position_names, position_values, + cmd_resource=cmd_resource, + no_api_call=True, + expected_exception=exceptions.CommandError) + def test_create_listener_with_all_params(self): # lbaas-listener-create with all params set. resource = 'listener' @@ -47,6 +83,7 @@ def test_create_listener_with_all_params(self): cmd = listener.CreateListener(test_cli20.MyApp(sys.stdout), None) my_id = 'my-id' loadbalancer = 'loadbalancer' + default_pool_id = 'default-pool' protocol = 'TCP' protocol_port = '80' connection_limit = 10 @@ -54,14 +91,17 @@ def test_create_listener_with_all_params(self): args = ['--admin-state-down', '--protocol', protocol, '--protocol-port', protocol_port, '--loadbalancer', loadbalancer, + '--default-pool', default_pool_id, '--default-tls-container-ref', def_tls_cont_ref, '--sni-container-refs', '1111', '2222', '3333', '--connection-limit', '10'] position_names = ['admin_state_up', 'protocol', 'protocol_port', 'loadbalancer_id', + 'default_pool_id', 'default_tls_container_ref', 'sni_container_refs', 'connection_limit'] position_values = [False, protocol, protocol_port, loadbalancer, + default_pool_id, def_tls_cont_ref, ['1111', '2222', '3333'], connection_limit] self._test_create_resource(resource, cmd, '', my_id, args, diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_pool.py b/neutronclient/tests/unit/lb/v2/test_cli20_pool.py index efaa06565..2e5787b0e 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_pool.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_pool.py @@ -16,14 +16,15 @@ import sys +from neutronclient.common import exceptions from neutronclient.neutron.v2_0.lb.v2 import pool from neutronclient.tests.unit import test_cli20 class CLITestV20LbPoolJSON(test_cli20.CLITestV20Base): - def test_create_pool_with_mandatory_params(self): - # lbaas-pool-create with mandatory params only. + def test_create_pool_with_listener(self): + # lbaas-pool-create with listener resource = 'pool' cmd_resource = 'lbaas_pool' cmd = pool.CreatePool(test_cli20.MyApp(sys.stdout), None) @@ -40,6 +41,41 @@ def test_create_pool_with_mandatory_params(self): position_names, position_values, cmd_resource=cmd_resource) + def test_create_pool_with_loadbalancer_no_listener(self): + """lbaas-pool-create with loadbalancer, no listener.""" + resource = 'pool' + cmd_resource = 'lbaas_pool' + cmd = pool.CreatePool(test_cli20.MyApp(sys.stdout), None) + my_id = 'my-id' + lb_algorithm = 'ROUND_ROBIN' + loadbalancer = 'loadbalancer' + protocol = 'TCP' + args = ['--lb-algorithm', lb_algorithm, '--protocol', protocol, + '--loadbalancer', loadbalancer] + position_names = ['admin_state_up', 'lb_algorithm', 'protocol', + 'loadbalancer_id'] + position_values = [True, lb_algorithm, protocol, loadbalancer] + self._test_create_resource(resource, cmd, '', my_id, args, + position_names, position_values, + cmd_resource=cmd_resource) + + def test_create_pool_with_no_listener_or_loadbalancer(self): + """lbaas-pool-create with no listener or loadbalancer.""" + resource = 'pool' + cmd_resource = 'lbaas_pool' + cmd = pool.CreatePool(test_cli20.MyApp(sys.stdout), None) + my_id = 'my-id' + lb_algorithm = 'ROUND_ROBIN' + protocol = 'TCP' + args = ['--lb-algorithm', lb_algorithm, '--protocol', protocol] + position_names = ['admin_state_up', 'lb_algorithm', 'protocol'] + position_values = [True, lb_algorithm, protocol] + self._test_create_resource(resource, cmd, '', my_id, args, + position_names, position_values, + cmd_resource=cmd_resource, + no_api_call=True, + expected_exception=exceptions.CommandError) + def test_create_pool_with_all_params(self): # lbaas-pool-create with all params set. resource = 'pool' @@ -48,6 +84,7 @@ def test_create_pool_with_all_params(self): my_id = 'my-id' lb_algorithm = 'ROUND_ROBIN' listener = 'listener' + loadbalancer = 'loadbalancer' protocol = 'TCP' description = 'description' session_persistence_str = 'type=APP_COOKIE,cookie_name=1234' @@ -57,12 +94,13 @@ def test_create_pool_with_all_params(self): args = ['--lb-algorithm', lb_algorithm, '--protocol', protocol, '--description', description, '--session-persistence', session_persistence_str, '--admin-state-down', '--name', name, - '--listener', listener] + '--listener', listener, '--loadbalancer', loadbalancer] position_names = ['lb_algorithm', 'protocol', 'description', 'session_persistence', 'admin_state_up', 'name', - 'listener_id'] + 'listener_id', 'loadbalancer_id'] position_values = [lb_algorithm, protocol, description, - session_persistence, False, name, listener] + session_persistence, False, name, listener, + loadbalancer] self._test_create_resource(resource, cmd, '', my_id, args, position_names, position_values, cmd_resource=cmd_resource) diff --git a/releasenotes/notes/add-shared-pools-support-6f79b565afad3e47.yaml b/releasenotes/notes/add-shared-pools-support-6f79b565afad3e47.yaml new file mode 100644 index 000000000..1be2e0ed1 --- /dev/null +++ b/releasenotes/notes/add-shared-pools-support-6f79b565afad3e47.yaml @@ -0,0 +1,8 @@ +--- +features: + - | + CLI support for Neutron-LBaaS v2 shared pools added. + + * Pools can be created independently from listeners. + * Listeners can share the same default_pool. + * Makes Layer 7 switching support much more useful. From a64aad2c0ffe84e15d59b5df03b0685e37094a45 Mon Sep 17 00:00:00 2001 From: Evgeny Fedoruk Date: Wed, 26 Aug 2015 08:35:29 -0700 Subject: [PATCH 389/845] Reflecting L7 content rules capability in LBaaS Adds CLI commands for L7 policies and rules Change-Id: I3617c7ecd2a3ac0cae555893235e34d6c2135b81 Implements: blueprint lbaas-l7-rules Co-Authored-By: Evgeny Fedoruk Co-Authored-By: Stephen Balukoff --- neutronclient/neutron/v2_0/lb/v2/l7policy.py | 155 +++++++++++ neutronclient/neutron/v2_0/lb/v2/l7rule.py | 148 ++++++++++ neutronclient/shell.py | 12 + .../tests/unit/lb/v2/test_cli20_l7policy.py | 260 ++++++++++++++++++ .../tests/unit/lb/v2/test_cli20_l7rule.py | 210 ++++++++++++++ neutronclient/v2_0/client.py | 64 +++++ ...-policies-capability-0f17cd06f044c83c.yaml | 8 + 7 files changed, 857 insertions(+) create mode 100644 neutronclient/neutron/v2_0/lb/v2/l7policy.py create mode 100644 neutronclient/neutron/v2_0/lb/v2/l7rule.py create mode 100644 neutronclient/tests/unit/lb/v2/test_cli20_l7policy.py create mode 100644 neutronclient/tests/unit/lb/v2/test_cli20_l7rule.py create mode 100644 releasenotes/notes/add-l7-content-policies-capability-0f17cd06f044c83c.yaml diff --git a/neutronclient/neutron/v2_0/lb/v2/l7policy.py b/neutronclient/neutron/v2_0/lb/v2/l7policy.py new file mode 100644 index 000000000..26c331554 --- /dev/null +++ b/neutronclient/neutron/v2_0/lb/v2/l7policy.py @@ -0,0 +1,155 @@ +# Copyright 2016 Radware LTD. +# 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. +# + +from neutronclient._i18n import _ +from neutronclient.common import exceptions +from neutronclient.common import utils +from neutronclient.neutron import v2_0 as neutronV20 + + +def _get_listener_id(client, listener_id_or_name): + return neutronV20.find_resourceid_by_name_or_id( + client, 'listener', listener_id_or_name) + + +def _get_pool_id(client, pool_id_or_name): + return neutronV20.find_resourceid_by_name_or_id( + client, 'pool', pool_id_or_name, cmd_resource='lbaas_pool') + + +def _add_common_args(parser, is_create=True): + parser.add_argument( + '--name', + help=_('Name of the policy.')) + parser.add_argument( + '--description', + help=_('Description of the policy.')) + parser.add_argument( + '--action', + required=is_create, + metavar='ACTION', + type=utils.convert_to_uppercase, + choices=['REJECT', 'REDIRECT_TO_POOL', 'REDIRECT_TO_URL'], + help=_('Action type of the policy.')) + parser.add_argument( + '--redirect-pool', + help=_('ID or name of the pool for REDIRECT_TO_POOL action type.')) + parser.add_argument( + '--redirect-url', + help=_('URL for REDIRECT_TO_URL action type. ' + 'This should be a valid URL string.')) + parser.add_argument( + '--position', + type=int, + help=_('L7 policy position in ordered policies list. ' + 'This must be an integer starting from 1. ' + 'Not specifying the position will place the policy ' + 'at the tail of existing policies list.')) + + +def _common_args2body(client, parsed_args, is_create=True): + if parsed_args.redirect_url: + if parsed_args.action != 'REDIRECT_TO_URL': + raise exceptions.CommandError(_('Action must be REDIRECT_TO_URL')) + if parsed_args.redirect_pool: + if parsed_args.action != 'REDIRECT_TO_POOL': + raise exceptions.CommandError(_('Action must be REDIRECT_TO_POOL')) + parsed_args.redirect_pool_id = _get_pool_id( + client, parsed_args.redirect_pool) + if (parsed_args.action == 'REDIRECT_TO_URL' and + not parsed_args.redirect_url): + raise exceptions.CommandError(_('Redirect URL must be specified')) + if (parsed_args.action == 'REDIRECT_TO_POOL' and + not parsed_args.redirect_pool): + raise exceptions.CommandError(_('Redirect pool must be specified')) + + attributes = ['name', 'description', + 'action', 'redirect_pool_id', 'redirect_url', + 'position', 'admin_state_up'] + if is_create: + parsed_args.listener_id = _get_listener_id( + client, parsed_args.listener) + attributes.extend(['listener_id', 'tenant_id']) + body = {} + neutronV20.update_dict(parsed_args, body, attributes) + return {'l7policy': body} + + +class ListL7Policy(neutronV20.ListCommand): + """LBaaS v2 List L7 policies that belong to a given listener.""" + + resource = 'l7policy' + shadow_resource = 'lbaas_l7policy' + pagination_support = True + sorting_support = True + list_columns = [ + 'id', 'name', 'action', 'redirect_pool_id', 'redirect_url', + 'position', 'admin_state_up', 'status' + ] + + +class ShowL7Policy(neutronV20.ShowCommand): + """LBaaS v2 Show information of a given L7 policy.""" + + resource = 'l7policy' + shadow_resource = 'lbaas_l7policy' + + +class CreateL7Policy(neutronV20.CreateCommand): + """LBaaS v2 Create L7 policy.""" + + resource = 'l7policy' + shadow_resource = 'lbaas_l7policy' + + def add_known_arguments(self, parser): + _add_common_args(parser) + parser.add_argument( + '--admin-state-down', + dest='admin_state_up', + action='store_false', + help=_('Set admin state up to false.')) + parser.add_argument( + '--listener', + required=True, + metavar='LISTENER', + help=_('ID or name of the listener this policy belongs to.')) + + def args2body(self, parsed_args): + return _common_args2body(self.get_client(), parsed_args) + + +class UpdateL7Policy(neutronV20.UpdateCommand): + """LBaaS v2 Update a given L7 policy.""" + + resource = 'l7policy' + shadow_resource = 'lbaas_l7policy' + + def add_known_arguments(self, parser): + _add_common_args(parser, is_create=False) + utils.add_boolean_argument( + parser, '--admin-state-up', + help=_('Specify the administrative state of the policy' + ' (True meaning "Up").')) + + def args2body(self, parsed_args): + return _common_args2body(self.get_client(), parsed_args, False) + + +class DeleteL7Policy(neutronV20.DeleteCommand): + """LBaaS v2 Delete a given L7 policy.""" + + resource = 'l7policy' + shadow_resource = 'lbaas_l7policy' diff --git a/neutronclient/neutron/v2_0/lb/v2/l7rule.py b/neutronclient/neutron/v2_0/lb/v2/l7rule.py new file mode 100644 index 000000000..1286559f2 --- /dev/null +++ b/neutronclient/neutron/v2_0/lb/v2/l7rule.py @@ -0,0 +1,148 @@ +# Copyright 2016 Radware LTD. +# 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. +# + + +from neutronclient._i18n import _ +from neutronclient.common import utils +from neutronclient.neutron import v2_0 as neutronV20 + + +def _get_policy_id(client, policy_id_or_name): + return neutronV20.find_resourceid_by_name_or_id( + client, 'l7policy', policy_id_or_name, + cmd_resource='lbaas_l7policy') + + +class LbaasL7RuleMixin(object): + + def set_extra_attrs(self, parsed_args): + self.parent_id = _get_policy_id(self.get_client(), + parsed_args.l7policy) + + def add_known_arguments(self, parser): + parser.add_argument( + 'l7policy', metavar='L7POLICY', + help=_('ID or name of L7 policy this rule belongs to.')) + + +def _add_common_args(parser, is_create=True): + parser.add_argument( + '--type', + required=is_create, + type=utils.convert_to_uppercase, + choices=['HOST_NAME', 'PATH', 'FILE_TYPE', 'HEADER', 'COOKIE'], + help=_('Rule type.')) + parser.add_argument( + '--compare-type', + required=is_create, + type=utils.convert_to_uppercase, + choices=['REGEX', 'STARTS_WITH', 'ENDS_WITH', + 'CONTAINS', 'EQUAL_TO'], + help=_('Rule compare type.')) + parser.add_argument( + '--invert-compare', + dest='invert', + action='store_true', + help=_('Invert the compare type.')) + parser.add_argument( + '--key', + help=_('Key to compare.' + ' Relevant for HEADER and COOKIE types only.')) + parser.add_argument( + '--value', + required=is_create, + help=_('Value to compare.')) + + +def _common_args2body(client, parsed_args, is_create=True): + attributes = ['type', 'compare_type', + 'invert', 'key', 'value', 'admin_state_up'] + if is_create: + attributes.append('tenant_id') + body = {} + neutronV20.update_dict(parsed_args, body, attributes) + return {'rule': body} + + +class ListL7Rule(LbaasL7RuleMixin, neutronV20.ListCommand): + """LBaaS v2 List L7 rules that belong to a given L7 policy.""" + + resource = 'rule' + shadow_resource = 'lbaas_l7rule' + pagination_support = True + sorting_support = True + + list_columns = [ + 'id', 'type', 'compare_type', 'invert', 'key', 'value', + 'admin_state_up', 'status' + ] + + def take_action(self, parsed_args): + self.parent_id = _get_policy_id(self.get_client(), + parsed_args.l7policy) + self.values_specs.append('--l7policy_id=%s' % self.parent_id) + return super(ListL7Rule, self).take_action(parsed_args) + + +class ShowL7Rule(LbaasL7RuleMixin, neutronV20.ShowCommand): + """LBaaS v2 Show information of a given rule.""" + + resource = 'rule' + shadow_resource = 'lbaas_l7rule' + + +class CreateL7Rule(LbaasL7RuleMixin, neutronV20.CreateCommand): + """LBaaS v2 Create L7 rule.""" + + resource = 'rule' + shadow_resource = 'lbaas_l7rule' + + def add_known_arguments(self, parser): + super(CreateL7Rule, self).add_known_arguments(parser) + _add_common_args(parser) + parser.add_argument( + '--admin-state-down', + dest='admin_state_up', + action='store_false', + help=_('Set admin state up to false')) + + def args2body(self, parsed_args): + return _common_args2body(self.get_client(), parsed_args) + + +class UpdateL7Rule(LbaasL7RuleMixin, neutronV20.UpdateCommand): + """LBaaS v2 Update a given L7 rule.""" + + resource = 'rule' + shadow_resource = 'lbaas_l7rule' + + def add_known_arguments(self, parser): + super(UpdateL7Rule, self).add_known_arguments(parser) + _add_common_args(parser, False) + utils.add_boolean_argument( + parser, '--admin-state-up', + help=_('Specify the administrative state of the rule' + ' (True meaning "Up").')) + + def args2body(self, parsed_args): + return _common_args2body(self.get_client(), parsed_args, False) + + +class DeleteL7Rule(LbaasL7RuleMixin, neutronV20.DeleteCommand): + """LBaaS v2 Delete a given L7 rule.""" + + resource = 'rule' + shadow_resource = 'lbaas_l7rule' diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 795571ad4..fef1134d1 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -59,6 +59,8 @@ from neutronclient.neutron.v2_0.lb import member as lb_member from neutronclient.neutron.v2_0.lb import pool as lb_pool from neutronclient.neutron.v2_0.lb.v2 import healthmonitor as lbaas_healthmon +from neutronclient.neutron.v2_0.lb.v2 import l7policy as lbaas_l7policy +from neutronclient.neutron.v2_0.lb.v2 import l7rule as lbaas_l7rule from neutronclient.neutron.v2_0.lb.v2 import listener as lbaas_listener from neutronclient.neutron.v2_0.lb.v2 import loadbalancer as lbaas_loadbalancer from neutronclient.neutron.v2_0.lb.v2 import member as lbaas_member @@ -217,6 +219,16 @@ def take_action(self, parsed_args): 'lbaas-listener-create': lbaas_listener.CreateListener, 'lbaas-listener-update': lbaas_listener.UpdateListener, 'lbaas-listener-delete': lbaas_listener.DeleteListener, + 'lbaas-l7policy-list': lbaas_l7policy.ListL7Policy, + 'lbaas-l7policy-show': lbaas_l7policy.ShowL7Policy, + 'lbaas-l7policy-create': lbaas_l7policy.CreateL7Policy, + 'lbaas-l7policy-update': lbaas_l7policy.UpdateL7Policy, + 'lbaas-l7policy-delete': lbaas_l7policy.DeleteL7Policy, + 'lbaas-l7rule-list': lbaas_l7rule.ListL7Rule, + 'lbaas-l7rule-show': lbaas_l7rule.ShowL7Rule, + 'lbaas-l7rule-create': lbaas_l7rule.CreateL7Rule, + 'lbaas-l7rule-update': lbaas_l7rule.UpdateL7Rule, + 'lbaas-l7rule-delete': lbaas_l7rule.DeleteL7Rule, 'lbaas-pool-list': lbaas_pool.ListPool, 'lbaas-pool-show': lbaas_pool.ShowPool, 'lbaas-pool-create': lbaas_pool.CreatePool, diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_l7policy.py b/neutronclient/tests/unit/lb/v2/test_cli20_l7policy.py new file mode 100644 index 000000000..71869315b --- /dev/null +++ b/neutronclient/tests/unit/lb/v2/test_cli20_l7policy.py @@ -0,0 +1,260 @@ +# Copyright 2016 Radware LTD. +# 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 sys + +from neutronclient.common import exceptions +from neutronclient.neutron.v2_0.lb.v2 import l7policy +from neutronclient.tests.unit import test_cli20 + +"""Structure for mapping cli and api arguments + +The structure maps cli arguments and a list of its +api argument name, default cli value and default api value. +It helps to make tests more general for different argument types. +""" +args_conf = { + 'name': ['name', 'test_policy', 'test_policy'], + 'description': ['description', 'test policy', 'test policy'], + 'listener': ['listener_id', 'test_listener', 'test_listener'], + 'admin-state-up': ['admin_state_up', True, True], + 'admin-state-down': ['admin_state_up', None, False], + 'action': ['action', 'REJECT', 'REJECT'], + 'redirect-url': ['redirect_url', 'http://url', 'http://url'], + 'redirect-pool': ['redirect_pool_id', 'test_pool', 'test_pool'], + 'position': ['position', '1', 1]} + + +class CLITestV20LbL7PolicyJSON(test_cli20.CLITestV20Base): + + def _get_test_args(self, *args, **kwargs): + """Function for generically building testing arguments""" + cli_args = [] + api_args = {} + for arg in args: + cli_args.append('--' + arg) + if not args_conf[arg][1]: + pass + elif arg in kwargs: + cli_args.append(str(kwargs[arg])) + else: + cli_args.append(args_conf[arg][1]) + + if arg in kwargs: + api_args[args_conf[arg][0]] = kwargs[arg] + else: + api_args[args_conf[arg][0]] = args_conf[arg][2] + + return cli_args, api_args + + def _test_create_policy(self, *args, **kwargs): + resource = 'l7policy' + cmd_resource = 'lbaas_l7policy' + cmd = l7policy.CreateL7Policy(test_cli20.MyApp(sys.stdout), None) + cli_args, api_args = self._get_test_args(*args, **kwargs) + position_names = list(api_args.keys()) + position_values = list(api_args.values()) + self._test_create_resource(resource, cmd, None, 'test_id', + cli_args, position_names, position_values, + cmd_resource=cmd_resource) + + def _test_update_policy(self, *args, **kwargs): + resource = 'l7policy' + cmd_resource = 'lbaas_l7policy' + cmd = l7policy.UpdateL7Policy(test_cli20.MyApp(sys.stdout), None) + cli_args, api_args = self._get_test_args(*args, **kwargs) + cli_args.append('test_id') + self._test_update_resource(resource, cmd, 'test_id', + cli_args, api_args, + cmd_resource=cmd_resource) + + def test_create_policy_with_mandatory_params(self): + # lbaas-l7policy-create with mandatory params only. + self._test_create_policy('action', 'listener') + + def test_create_policy_with_all_params(self): + # lbaas-l7policy-create REJECT policy. + self._test_create_policy('name', 'description', + 'action', 'listener', + 'position') + + def test_create_disabled_policy(self): + # lbaas-l7policy-create disabled REJECT policy. + self._test_create_policy('action', 'listener', 'admin-state-down') + + def test_create_url_redirect_policy(self): + # lbaas-l7policy-create REDIRECT_TO_URL policy. + self._test_create_policy('name', 'description', + 'action', 'listener', + 'redirect-url', + action='REDIRECT_TO_URL') + + def test_create_url_redirect_policy_no_url(self): + # lbaas-l7policy-create REDIRECT_TO_URL policy without url argument. + self.assertRaises(exceptions.CommandError, + self._test_create_policy, + 'name', 'description', + 'action', 'listener', + action='REDIRECT_TO_URL') + + def test_create_pool_redirect_policy(self): + # lbaas-l7policy-create REDIRECT_TO_POOL policy. + self._test_create_policy('name', 'description', + 'action', 'listener', + 'redirect-pool', + action='REDIRECT_TO_POOL') + + def test_create_pool_redirect_policy_no_pool(self): + # lbaas-l7policy-create REDIRECT_TO_POOL policy without pool argument. + self.assertRaises(exceptions.CommandError, + self._test_create_policy, + 'name', 'description', + 'action', 'listener', + action='REDIRECT_TO_POOL') + + def test_create_reject_policy_with_url(self): + # lbaas-l7policy-create REJECT policy while specifying url argument. + self.assertRaises(exceptions.CommandError, + self._test_create_policy, + 'action', 'listener', + 'redirect-url') + + def test_create_reject_policy_with_pool(self): + # lbaas-l7policy-create REJECT policy while specifying pool argument. + self.assertRaises(exceptions.CommandError, + self._test_create_policy, + 'action', 'listener', + 'redirect-pool') + + def test_create_pool_redirect_policy_with_url(self): + # lbaas-l7policy-create REDIRECT_TO_POOL policy with url argument. + self.assertRaises(exceptions.CommandError, + self._test_create_policy, + 'action', 'listener', + 'redirect-pool', 'redirect-url', + action='REDIRECT_TO_POOL') + + def test_create_url_redirect_policy_with_pool(self): + # lbaas-l7policy-create REDIRECT_TO_URL policy with pool argument. + self.assertRaises(exceptions.CommandError, + self._test_create_policy, + 'action', 'listener', + 'redirect-pool', 'redirect-url', + action='REDIRECT_TO_URL') + + def test_list_policies(self): + # lbaas-l7policy-list. + + resources = 'l7policies' + cmd_resources = 'lbaas_l7policies' + cmd = l7policy.ListL7Policy(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, True, + cmd_resources=cmd_resources) + + def test_list_policies_pagination(self): + # lbaas-l7policy-list with pagination. + + resources = 'l7policies' + cmd_resources = 'lbaas_l7policies' + cmd = l7policy.ListL7Policy(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources_with_pagination( + resources, cmd, cmd_resources=cmd_resources) + + def test_list_policies_sort(self): + # lbaas-l7policy-list --sort-key id --sort-key asc. + + resources = 'l7policies' + cmd_resources = 'lbaas_l7policies' + cmd = l7policy.ListL7Policy(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources( + resources, cmd, True, cmd_resources=cmd_resources) + + def test_list_policies_limit(self): + # lbaas-l7policy-list -P. + + resources = 'l7policies' + cmd_resources = 'lbaas_l7policies' + cmd = l7policy.ListL7Policy(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources( + resources, cmd, page_size=1000, cmd_resources=cmd_resources) + + def test_show_policy_id(self): + # lbaas-l7policy-show test_id. + + resource = 'l7policy' + cmd_resource = 'lbaas_l7policy' + cmd = l7policy.ShowL7Policy(test_cli20.MyApp(sys.stdout), None) + args = ['--fields', 'test_id', self.test_id] + self._test_show_resource( + resource, cmd, self.test_id, args, + ['test_id'], cmd_resource=cmd_resource) + + def test_show_policy_id_name(self): + # lbaas-l7policy-show. + + resource = 'l7policy' + cmd_resource = 'lbaas_l7policy' + cmd = l7policy.ShowL7Policy(test_cli20.MyApp(sys.stdout), None) + args = ['--fields', 'test_id', '--fields', 'name', self.test_id] + self._test_show_resource( + resource, cmd, self.test_id, args, + ['test_id', 'name'], cmd_resource=cmd_resource) + + def test_disable_policy(self): + # lbaas-l7policy-update test_id --admin-state-up False. + + self._test_update_policy('admin-state-up', + **{'admin-state-up': 'False'}) + + def test_update_policy_name_and_description(self): + # lbaas-l7policy-update test_id --name other --description other_desc. + + self._test_update_policy('name', 'description', + name='name', + description='other desc') + + def test_update_pool_redirect_policy(self): + # lbaas-l7policy-update test_id --action REDIRECT_TO_POOL + # --redirect-pool id. + + self._test_update_policy('action', 'redirect-pool', + **{'action': 'REDIRECT_TO_POOL', + 'redirect-pool': 'id'}) + + def test_update_url_redirect_policy(self): + # lbaas-l7policy-update test_id --action REDIRECT_TO_URL + # --redirect-url http://other_url. + + self._test_update_policy('action', 'redirect-url', + **{'action': 'REDIRECT_TO_URL', + 'redirect-url': 'http://other_url'}) + + def test_update_policy_position(self): + # lbaas-l7policy-update test_id --position 2. + + self._test_update_policy('position', + position=2) + + def test_delete_policy(self): + # lbaas-l7policy-delete test_id. + + resource = 'l7policy' + cmd_resource = 'lbaas_l7policy' + cmd = l7policy.DeleteL7Policy(test_cli20.MyApp(sys.stdout), None) + test_id = 'test_id' + args = [test_id] + self._test_delete_resource(resource, cmd, test_id, args, + cmd_resource=cmd_resource) diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_l7rule.py b/neutronclient/tests/unit/lb/v2/test_cli20_l7rule.py new file mode 100644 index 000000000..54830542d --- /dev/null +++ b/neutronclient/tests/unit/lb/v2/test_cli20_l7rule.py @@ -0,0 +1,210 @@ +# Copyright 2016 Radware LTD. +# 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 sys + +from neutronclient.neutron.v2_0.lb.v2 import l7rule +from neutronclient.tests.unit import test_cli20 + + +"""Structure for mapping cli and api arguments + +The structure maps cli arguments and a list of its +api argument name, default cli value and default api value. +It helps to make tests more general for different argument types. +""" +args_conf = { + 'admin-state-up': ['admin_state_up', True, True], + 'admin-state-down': ['admin_state_up', None, False], + 'type': ['type', 'HOST_NAME', 'HOST_NAME'], + 'compare-type': ['compare_type', 'EQUAL_TO', 'EQUAL_TO'], + 'invert-compare': ['invert', None, True], + 'key': ['key', 'key', 'key'], + 'value': ['value', 'value', 'value']} + + +class CLITestV20LbL7RuleJSON(test_cli20.CLITestV20Base): + + def _get_test_args(self, *args, **kwargs): + """Function for generically building testing arguments""" + cli_args = [] + api_args = {} + for arg in args: + cli_args.append('--' + arg) + if not args_conf[arg][1]: + pass + elif arg in kwargs: + cli_args.append(str(kwargs[arg])) + else: + cli_args.append(args_conf[arg][1]) + + if arg in kwargs: + api_args[args_conf[arg][0]] = kwargs[arg] + else: + api_args[args_conf[arg][0]] = args_conf[arg][2] + + if 'invert' not in api_args: + api_args['invert'] = False + return cli_args, api_args + + def _test_create_rule(self, *args, **kwargs): + resource = 'rule' + cmd_resource = 'lbaas_l7rule' + cmd = l7rule.CreateL7Rule(test_cli20.MyApp(sys.stdout), None) + cli_args, api_args = self._get_test_args(*args, **kwargs) + position_names = list(api_args.keys()) + position_values = list(api_args.values()) + cli_args.append('test_policy') + self._test_create_resource(resource, cmd, None, 'test_id', + cli_args, position_names, position_values, + cmd_resource=cmd_resource, + parent_id='test_policy') + + def _test_update_rule(self, *args, **kwargs): + resource = 'rule' + cmd_resource = 'lbaas_l7rule' + cmd = l7rule.UpdateL7Rule(test_cli20.MyApp(sys.stdout), None) + cli_args, api_args = self._get_test_args(*args, **kwargs) + cli_args.append('test_id') + cli_args.append('test_policy') + self._test_update_resource(resource, cmd, 'test_id', + cli_args, api_args, + cmd_resource=cmd_resource, + parent_id='test_policy') + + def test_create_rule_with_mandatory_params(self): + # lbaas-l7rule-create with mandatory params only. + + self._test_create_rule('type', 'compare-type', + 'value') + + def test_create_disabled_rule(self): + # lbaas-l7rule-create disabled rule. + + self._test_create_rule('type', 'compare-type', + 'value', 'admin-state-down') + + def test_create_rule_with_all_params(self): + # lbaas-l7rule-create with all params set. + + self._test_create_rule('type', 'compare-type', + 'invert-compare', 'key', 'value', + type='HEADER', compare_type='CONTAINS', + key='other_key', value='other_value') + + def test_create_rule_with_inverted_compare(self): + # lbaas-l7rule-create with invertted compare type. + + self._test_create_rule('type', 'compare-type', + 'invert-compare', 'value') + + def test_list_rules(self): + # lbaas-l7rule-list. + + resources = 'rules' + cmd_resources = 'lbaas_l7rules' + cmd = l7rule.ListL7Rule(test_cli20.MyApp(sys.stdout), None) + + policy_id = 'policy_id' + self._test_list_resources(resources, cmd, True, + base_args=[policy_id], + cmd_resources=cmd_resources, + parent_id=policy_id, + query="l7policy_id=%s" % policy_id) + + def test_list_rules_pagination(self): + # lbaas-l7rule-list with pagination. + + resources = 'rules' + cmd_resources = 'lbaas_l7rules' + cmd = l7rule.ListL7Rule(test_cli20.MyApp(sys.stdout), None) + policy_id = 'policy_id' + self._test_list_resources_with_pagination( + resources, cmd, base_args=[policy_id], + cmd_resources=cmd_resources, parent_id=policy_id, + query="l7policy_id=%s" % policy_id) + + def test_list_rules_sort(self): + # lbaas-l7rule-list --sort-key id --sort-key asc. + + resources = 'rules' + cmd_resources = 'lbaas_l7rules' + cmd = l7rule.ListL7Rule(test_cli20.MyApp(sys.stdout), None) + policy_id = 'policy_id' + self._test_list_resources( + resources, cmd, True, base_args=[policy_id], + cmd_resources=cmd_resources, parent_id=policy_id, + query="l7policy_id=%s" % policy_id) + + def test_list_rules_limit(self): + # lbaas-l7rule-list -P. + + resources = 'rules' + cmd_resources = 'lbaas_l7rules' + cmd = l7rule.ListL7Rule(test_cli20.MyApp(sys.stdout), None) + policy_id = 'policy_id' + self._test_list_resources(resources, cmd, page_size=1000, + base_args=[policy_id], + cmd_resources=cmd_resources, + parent_id=policy_id, + query="l7policy_id=%s" % policy_id) + + def test_show_rule_id(self): + # lbaas-l7rule-show test_id. + + resource = 'rule' + cmd_resource = 'lbaas_l7rule' + policy_id = 'policy_id' + cmd = l7rule.ShowL7Rule(test_cli20.MyApp(sys.stdout), None) + args = ['--fields', 'id', self.test_id, policy_id] + self._test_show_resource(resource, cmd, self.test_id, args, ['id'], + cmd_resource=cmd_resource, + parent_id=policy_id) + + def test_update_rule_type(self): + # lbaas-l7rule-update test_id --type HEADER test_policy + + self._test_update_rule('type', type='HEADER') + + def test_update_rule_compare_type(self): + # lbaas-l7rule-update test_id --compare-type CONTAINS test_policy. + + self._test_update_rule('compare-type', + **{'compare-type': 'CONTAINS'}) + + def test_update_rule_inverted_compare_type(self): + # lbaas-l7rule-update test_id --invert-compare test_policy. + + self._test_update_rule('invert-compare') + + def test_update_rule_key_value(self): + # lbaas-l7rule-update test_id --key other --value other test_policy. + + self._test_update_rule('key', 'value', + key='other', value='other') + + def test_delete_rule(self): + # lbaas-l7rule-delete test_id policy_id. + + resource = 'rule' + cmd_resource = 'lbaas_l7rule' + policy_id = 'policy_id' + test_id = 'test_id' + cmd = l7rule.DeleteL7Rule(test_cli20.MyApp(sys.stdout), None) + args = [test_id, policy_id] + self._test_delete_resource(resource, cmd, test_id, args, + cmd_resource=cmd_resource, + parent_id=policy_id) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 2c4f10520..bbbb3e7c3 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -339,6 +339,10 @@ class Client(ClientBase): lbaas_loadbalancer_path_status = "/lbaas/loadbalancers/%s/statuses" lbaas_listeners_path = "/lbaas/listeners" lbaas_listener_path = "/lbaas/listeners/%s" + lbaas_l7policies_path = "/lbaas/l7policies" + lbaas_l7policy_path = lbaas_l7policies_path + "/%s" + lbaas_l7rules_path = lbaas_l7policy_path + "/rules" + lbaas_l7rule_path = lbaas_l7rules_path + "/%s" lbaas_pools_path = "/lbaas/pools" lbaas_pool_path = "/lbaas/pools/%s" lbaas_healthmonitors_path = "/lbaas/healthmonitors" @@ -438,6 +442,9 @@ class Client(ClientBase): 'metering_label_rules': 'metering_label_rule', 'loadbalancers': 'loadbalancer', 'listeners': 'listener', + 'l7rules': 'l7rule', + 'l7policies': 'l7policy', + 'lbaas_l7policies': 'lbaas_l7policy', 'lbaas_pools': 'lbaas_pool', 'lbaas_healthmonitors': 'lbaas_healthmonitor', 'lbaas_members': 'lbaas_member', @@ -447,6 +454,7 @@ class Client(ClientBase): 'qos_policies': 'qos_policy', 'policies': 'policy', 'bandwidth_limit_rules': 'bandwidth_limit_rule', + 'rules': 'rule', 'rule_types': 'rule_type', 'flavors': 'flavor', 'bgp_speakers': 'bgp_speaker', @@ -984,6 +992,62 @@ def delete_listener(self, lbaas_listener): """Deletes the specified lbaas_listener.""" return self.delete(self.lbaas_listener_path % (lbaas_listener)) + @APIParamsCall + def list_lbaas_l7policies(self, retrieve_all=True, **_params): + """Fetches a list of all L7 policies for a listener.""" + return self.list('l7policies', self.lbaas_l7policies_path, + retrieve_all, **_params) + + @APIParamsCall + def show_lbaas_l7policy(self, l7policy, **_params): + """Fetches information of a certain listener's L7 policy.""" + return self.get(self.lbaas_l7policy_path % l7policy, + params=_params) + + @APIParamsCall + def create_lbaas_l7policy(self, body=None): + """Creates L7 policy for a certain listener.""" + return self.post(self.lbaas_l7policies_path, body=body) + + @APIParamsCall + def update_lbaas_l7policy(self, l7policy, body=None): + """Updates L7 policy.""" + return self.put(self.lbaas_l7policy_path % l7policy, + body=body) + + @APIParamsCall + def delete_lbaas_l7policy(self, l7policy): + """Deletes the specified L7 policy.""" + return self.delete(self.lbaas_l7policy_path % l7policy) + + @APIParamsCall + def list_lbaas_l7rules(self, l7policy, retrieve_all=True, **_params): + """Fetches a list of all rules for L7 policy.""" + return self.list('rules', self.lbaas_l7rules_path % l7policy, + retrieve_all, **_params) + + @APIParamsCall + def show_lbaas_l7rule(self, l7rule, l7policy, **_params): + """Fetches information of a certain L7 policy's rule.""" + return self.get(self.lbaas_l7rule_path % (l7policy, l7rule), + params=_params) + + @APIParamsCall + def create_lbaas_l7rule(self, l7policy, body=None): + """Creates rule for a certain L7 policy.""" + return self.post(self.lbaas_l7rules_path % l7policy, body=body) + + @APIParamsCall + def update_lbaas_l7rule(self, l7rule, l7policy, body=None): + """Updates L7 rule.""" + return self.put(self.lbaas_l7rule_path % (l7policy, l7rule), + body=body) + + @APIParamsCall + def delete_lbaas_l7rule(self, l7rule, l7policy): + """Deletes the specified L7 rule.""" + return self.delete(self.lbaas_l7rule_path % (l7policy, l7rule)) + @APIParamsCall def list_lbaas_pools(self, retrieve_all=True, **_params): """Fetches a list of all lbaas_pools for a tenant.""" diff --git a/releasenotes/notes/add-l7-content-policies-capability-0f17cd06f044c83c.yaml b/releasenotes/notes/add-l7-content-policies-capability-0f17cd06f044c83c.yaml new file mode 100644 index 000000000..e413df7ec --- /dev/null +++ b/releasenotes/notes/add-l7-content-policies-capability-0f17cd06f044c83c.yaml @@ -0,0 +1,8 @@ +--- +features: + - | + CLI support for Layer 7 content policies and rules. + + * L7 policies can be defined for listeners along + with the ability to set L7 policy order. + * Multiple rules can be created for an L7 policy. From 88fbd3870cf3872fb0c9b4269503c62458664b3b Mon Sep 17 00:00:00 2001 From: John Davidge Date: Mon, 15 Feb 2016 10:02:54 -0800 Subject: [PATCH 390/845] Support cleanup of tenant resources with a single API call The addition of the 'neutron purge' command allows cloud admins to conveniently delete multiple neutron resources associated with a given tenant. The command will delete all supported resources provided that they can be deleted (not in use, etc) and feedback the amount of each resource deleted to the user. A completion percentage is also given to keep the user informed of progress. Currently supports deletion of: Networks Subnets (implicitly) Routers Ports (including router interfaces) Floating IPs Security Groups This feature can be easily extended to support more resource types in the future. DocImpact: Update API documentation to describe neutron-purge usage Change-Id: I5a366d3537191045eb53f9cccd8cd0f7ce54a63b Closes-Bug: 1511574 Partially-Implements: blueprint tenant-delete --- neutronclient/neutron/v2_0/purge.py | 147 +++++++++++++++ neutronclient/shell.py | 2 + .../tests/functional/core/test_purge.py | 172 ++++++++++++++++++ neutronclient/tests/unit/test_cli20_purge.py | 100 ++++++++++ .../add-neutron-purge-a89e3d1179dce4b1.yaml | 16 ++ 5 files changed, 437 insertions(+) create mode 100644 neutronclient/neutron/v2_0/purge.py create mode 100644 neutronclient/tests/functional/core/test_purge.py create mode 100644 neutronclient/tests/unit/test_cli20_purge.py create mode 100644 releasenotes/notes/add-neutron-purge-a89e3d1179dce4b1.yaml diff --git a/neutronclient/neutron/v2_0/purge.py b/neutronclient/neutron/v2_0/purge.py new file mode 100644 index 000000000..6d8e9d9b9 --- /dev/null +++ b/neutronclient/neutron/v2_0/purge.py @@ -0,0 +1,147 @@ +# Copyright 2016 Cisco Systems +# 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 sys + +from neutronclient._i18n import _ +from neutronclient.neutron import v2_0 as neutronV20 + + +class Purge(neutronV20.NeutronCommand): + + def _pluralize(self, string): + return string + 's' + + def _get_resources(self, neutron_client, resource_types, tenant_id): + resources = [] + for resource_type in resource_types: + resources.append([]) + resource_type_plural = self._pluralize(resource_type) + opts = {'fields': ['id', 'tenant_id']} + if resource_type_plural == 'ports': + opts['fields'].append('device_id') + opts['fields'].append('device_owner') + function = getattr(neutron_client, 'list_%s' % + resource_type_plural) + if callable(function): + returned_resources = function(**opts).get(resource_type_plural, + []) + for resource in returned_resources: + if resource['tenant_id'] == tenant_id: + index = resource_types.index(resource_type) + resources[index].append(resource) + self.total_resources += 1 + return resources + + def _delete_resource(self, neutron_client, resource_type, resource): + resource_id = resource['id'] + if resource_type == 'port': + if resource.get('device_owner', '') == 'network:router_interface': + body = {'port_id': resource_id} + neutron_client.remove_interface_router(resource['device_id'], + body) + return + function = getattr(neutron_client, 'delete_%s' % resource_type) + if callable(function): + function(resource_id) + + def _purge_resources(self, neutron_client, resource_types, + tenant_resources): + deleted = {} + failed = {} + failures = False + for resources in tenant_resources: + index = tenant_resources.index(resources) + resource_type = resource_types[index] + failed[resource_type] = 0 + deleted[resource_type] = 0 + for resource in resources: + try: + self._delete_resource(neutron_client, resource_type, + resource) + deleted[resource_type] += 1 + self.deleted_resources += 1 + except Exception: + failures = True + failed[resource_type] += 1 + self.total_resources -= 1 + percent_complete = 100 + if self.total_resources > 0: + percent_complete = (self.deleted_resources / + float(self.total_resources)) * 100 + sys.stdout.write("\rPurging resources: %d%% complete." % + percent_complete) + sys.stdout.flush() + return (deleted, failed, failures) + + def _build_message(self, deleted, failed, failures): + msg = '' + deleted_msg = [] + for resource, value in deleted.items(): + if value: + if not msg: + msg = 'Deleted' + if not value == 1: + resource = self._pluralize(resource) + deleted_msg.append(" %d %s" % (value, resource)) + if deleted_msg: + msg += ','.join(deleted_msg) + + failed_msg = [] + if failures: + if msg: + msg += '. ' + msg += 'The following resources could not be deleted:' + for resource, value in failed.items(): + if value: + if not value == 1: + resource = self._pluralize(resource) + failed_msg.append(" %d %s" % (value, resource)) + msg += ','.join(failed_msg) + + if msg: + msg += '.' + else: + msg = _('Tenant has no supported resources.') + + return msg + + def get_parser(self, prog_name): + parser = super(Purge, self).get_parser(prog_name) + parser.add_argument( + 'tenant', metavar='TENANT', + help=_('ID of Tenant owning the resources to be deleted.')) + return parser + + def run(self, parsed_args): + neutron_client = self.get_client() + + self.any_failures = False + + # A list of the types of resources supported in the order in which + # they should be deleted. + resource_types = ['floatingip', 'port', 'router', + 'network', 'security_group'] + + deleted = {} + failed = {} + self.total_resources = 0 + self.deleted_resources = 0 + resources = self._get_resources(neutron_client, resource_types, + parsed_args.tenant) + deleted, failed, failures = self._purge_resources(neutron_client, + resource_types, + resources) + print('\n%s' % self._build_message(deleted, failed, failures)) diff --git a/neutronclient/shell.py b/neutronclient/shell.py index c1f6b0aaf..037e2be80 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -66,6 +66,7 @@ from neutronclient.neutron.v2_0.nsx import networkgateway from neutronclient.neutron.v2_0.nsx import qos_queue from neutronclient.neutron.v2_0 import port +from neutronclient.neutron.v2_0 import purge from neutronclient.neutron.v2_0.qos import bandwidth_limit_rule from neutronclient.neutron.v2_0.qos import policy as qos_policy from neutronclient.neutron.v2_0.qos import rule as qos_rule @@ -171,6 +172,7 @@ def take_action(self, parsed_args): 'port-create': port.CreatePort, 'port-delete': port.DeletePort, 'port-update': port.UpdatePort, + 'purge': purge.Purge, 'quota-list': quota.ListQuota, 'quota-show': quota.ShowQuota, 'quota-delete': quota.DeleteQuota, diff --git a/neutronclient/tests/functional/core/test_purge.py b/neutronclient/tests/functional/core/test_purge.py new file mode 100644 index 000000000..91d92a9ef --- /dev/null +++ b/neutronclient/tests/functional/core/test_purge.py @@ -0,0 +1,172 @@ +# Copyright 2016 Cisco Systems +# 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. + +from neutronclient.tests.functional import base + +from tempest_lib import exceptions + + +class PurgeNeutronClientCLITest(base.ClientTestBase): + + def _safe_cleanup(self, delete_command): + try: + self.neutron(delete_command) + except exceptions.CommandFailed: + # This resource was already purged successfully + pass + + def _create_subnet(self, name, tenant_id, cidr): + params = ('%(name)s --name %(name)s --tenant-id %(tenant)s ' + '%(cidr)s' % {'name': name, + 'tenant': tenant_id, + 'cidr': cidr}) + subnet = self.parser.listing(self.neutron('subnet-create', + params=params)) + for row in subnet: + if row['Field'] == 'id': + return row['Value'] + + def _create_router(self, name, tenant_id): + params = ('%(name)s --tenant_id %(tenant)s' % {'name': name, + 'tenant': tenant_id}) + router = self.parser.listing(self.neutron('router-create', + params=params)) + for row in router: + if row['Field'] == 'id': + return row['Value'] + + def _create_floatingip(self, network, tenant_id): + params = ('%(network)s --tenant-id %(tenant)s' % + {'network': network, 'tenant': tenant_id}) + floatingip = self.parser.listing(self.neutron('floatingip-create', + params=params)) + for row in floatingip: + if row['Field'] == 'id': + return row['Value'] + + def _create_resources(self, name, tenant_id, shared_tenant_id=None): + # If no shared_tenant_id is provided, create the resources for the + # current tenant to test that they will be deleted when not in use. + if not shared_tenant_id: + shared_tenant_id = tenant_id + + self.neutron('net-create', + params=('%(name)s --router:external True ' + '--tenant-id %(tenant)s' % {'name': name, + 'tenant': tenant_id})) + self.addCleanup(self._safe_cleanup, 'net-delete %s' % name) + + self.neutron('net-create', + params=('%(name)s-shared --shared ' + '--tenant-id %(tenant)s' % + {'name': name, 'tenant': shared_tenant_id})) + self.addCleanup(self._safe_cleanup, + 'net-delete %s-shared' % name) + + subnet = self._create_subnet(name, tenant_id, '192.168.71.0/24') + self.addCleanup(self._safe_cleanup, 'subnet-delete %s' % name) + + subnet = self._create_subnet('%s-shared' % name, tenant_id, + '192.168.81.0/24') + self.addCleanup(self._safe_cleanup, 'subnet-delete %s-shared' % name) + + router = self._create_router(name, tenant_id) + self.addCleanup(self._safe_cleanup, 'router-delete %s' % name) + + self.neutron('router-interface-add', + params=('%(router)s %(subnet)s ' + '--tenant-id %(tenant)s' % {'router': router, + 'subnet': subnet, + 'tenant': tenant_id})) + + self.neutron('port-create', + params=('%(name)s --name %(name)s ' + '--tenant-id %(tenant)s' % {'name': name, + 'tenant': tenant_id})) + self.addCleanup(self._safe_cleanup, 'port-delete %s' % name) + + self.neutron('port-create', + params=('%(name)s-shared --name %(name)s-shared ' + '--tenant-id %(tenant)s' % {'name': name, + 'tenant': tenant_id})) + self.addCleanup(self._safe_cleanup, 'port-delete %s-shared' % name) + + self.neutron('security-group-create', + params=('%(name)s --tenant-id %(tenant)s' % + {'name': name, 'tenant': tenant_id})) + self.addCleanup(self._safe_cleanup, 'security-group-delete %s' % name) + + floatingip = self._create_floatingip(name, tenant_id) + self.addCleanup(self._safe_cleanup, ('floatingip-delete ' + '%s' % floatingip)) + return floatingip + + def _verify_deletion(self, resources, resource_type): + purged = True + no_purge_purged = True + for row in resources: + if resource_type == 'port' and row.get('id', None): + port = self.parser.listing(self.neutron('port-show', + params=row['id'])) + port_dict = {} + for row in port: + port_dict[row['Field']] = row['Value'] + if port_dict['device_owner'] == 'network:router_interface': + if port_dict['tenant_id'] == 'purge-tenant': + purged = False + elif port_dict['tenant_id'] == 'no-purge-tenant': + no_purge_purged = False + if not purged or not no_purge_purged: + self.addCleanup(self.neutron, + ('router-interface-delete %(router)s ' + 'port=%(port)s' % + {'router': port_dict['device_id'], + 'port': port_dict['id']})) + if (row.get('name') == 'purge-me' or + row.get('id') == self.purge_floatingip): + purged = False + elif ('no-purge' in row.get('name', '') or + row.get('id') == self.no_purge_floatingip): + no_purge_purged = False + + if not purged: + self.fail('%s not deleted by neutron purge' % resource_type) + + if no_purge_purged: + self.fail('%s owned by another tenant incorrectly deleted ' + 'by neutron purge' % resource_type) + + def test_purge(self): + self.purge_floatingip = self._create_resources('purge-me', + 'purge-tenant') + self.no_purge_floatingip = self._create_resources('no-purge', + 'no-purge-tenant', + 'purge-tenant') + + purge_output = self.neutron('purge', params='purge-tenant').strip() + if not purge_output: + self.fail('Purge command did not return feedback') + + networks = self.parser.listing(self.neutron('net-list')) + subnets = self.parser.listing(self.neutron('subnet-list')) + routers = self.parser.listing(self.neutron('router-list')) + ports = self.parser.listing(self.neutron('port-list')) + floatingips = self.parser.listing(self.neutron('floatingip-list')) + + self._verify_deletion(networks, 'network') + self._verify_deletion(subnets, 'subnet') + self._verify_deletion(ports, 'port') + self._verify_deletion(routers, 'router') + self._verify_deletion(floatingips, 'floatingip') diff --git a/neutronclient/tests/unit/test_cli20_purge.py b/neutronclient/tests/unit/test_cli20_purge.py new file mode 100644 index 000000000..9bfd91c53 --- /dev/null +++ b/neutronclient/tests/unit/test_cli20_purge.py @@ -0,0 +1,100 @@ +# Copyright 2016 Cisco Systems +# 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 sys + +from neutronclient.neutron.v2_0 import purge +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20Purge(test_cli20.CLITestV20Base): + + def setUp(self): + super(CLITestV20Purge, self).setUp() + self.resource_types = ['floatingip', 'port', 'router', + 'network', 'security_group'] + + def _generate_resources_dict(self, value=0): + resources_dict = {} + resources_dict['true'] = value + for resource_type in self.resource_types: + resources_dict[resource_type] = value + return resources_dict + + def _verify_suffix(self, resources, message): + for resource, value in resources.items(): + if value > 0: + suffix = list('%(value)d %(resource)s' % + {'value': value, 'resource': resource}) + if value != 1: + suffix.append('s') + suffix = ''.join(suffix) + self.assertIn(suffix, message) + else: + self.assertNotIn(resource, message) + + def _verify_message(self, message, deleted, failed): + message = message.split('.') + success_prefix = "Deleted " + failure_prefix = "The following resources could not be deleted: " + if not deleted['true']: + for msg in message: + self.assertNotIn(success_prefix, msg) + message = message[0] + if not failed['true']: + expected = 'Tenant has no supported resources' + self.assertEqual(expected, message) + else: + self.assertIn(failure_prefix, message) + self._verify_suffix(failed, message) + else: + resources_deleted = message[0] + self.assertIn(success_prefix, resources_deleted) + self._verify_suffix(deleted, resources_deleted) + if failed['true']: + resources_failed = message[1] + self.assertIn(failure_prefix, resources_failed) + self._verify_suffix(failed, resources_failed) + else: + for msg in message: + self.assertNotIn(failure_prefix, msg) + + def _verify_result(self, my_purge, deleted, failed): + message = my_purge._build_message(deleted, failed, failed['true']) + self._verify_message(message, deleted, failed) + + def test_build_message(self): + my_purge = purge.Purge(test_cli20.MyApp(sys.stdout), None) + + # Verify message when tenant has no supported resources + deleted = self._generate_resources_dict() + failed = self._generate_resources_dict() + self._verify_result(my_purge, deleted, failed) + + # Verify message when tenant has supported resources, + # and they are all deleteable + deleted = self._generate_resources_dict(1) + self._verify_result(my_purge, deleted, failed) + + # Verify message when tenant has supported resources, + # and some are not deleteable + failed = self._generate_resources_dict(1) + self._verify_result(my_purge, deleted, failed) + + # Verify message when tenant has supported resources, + # and all are not deleteable + deleted = self._generate_resources_dict() + self._verify_result(my_purge, deleted, failed) diff --git a/releasenotes/notes/add-neutron-purge-a89e3d1179dce4b1.yaml b/releasenotes/notes/add-neutron-purge-a89e3d1179dce4b1.yaml new file mode 100644 index 000000000..a689b89ed --- /dev/null +++ b/releasenotes/notes/add-neutron-purge-a89e3d1179dce4b1.yaml @@ -0,0 +1,16 @@ +--- +features: + - | + New command 'neutron purge ' will delete all + supported resources owned by the given tenant, provided + that the user has sufficient authorization and the + resources in question are not shared, in use, or + otherwise undeletable. + + Supported resources are: + * Networks + * Subnets + * Routers + * Ports + * Floating IPs + * Security Groups From 8910471df3ca643eac4429ae6d5c82e633a98b1a Mon Sep 17 00:00:00 2001 From: Manjeet Singh Bhatia Date: Tue, 19 Jan 2016 23:26:41 +0000 Subject: [PATCH 391/845] Add commands for Network IP Availability This patch adds commandline for getting details about networks IP availability. Change-Id: Ie02c01ed8c4e291f91ed4cfd8781f4ec6a612cc7 Co-Authored-By: Brandon Logan Co-Authored-By: Akihiro Motoki --- .../neutron/v2_0/network_ip_availability.py | 73 +++++++++++++++++++ neutronclient/shell.py | 3 + .../test_cli20_network_ip_availability.py | 54 ++++++++++++++ neutronclient/v2_0/client.py | 16 ++++ ...work-ip-availability-ac9a462f42fe9db4.yaml | 9 +++ 5 files changed, 155 insertions(+) create mode 100644 neutronclient/neutron/v2_0/network_ip_availability.py create mode 100644 neutronclient/tests/unit/test_cli20_network_ip_availability.py create mode 100644 releasenotes/notes/network-ip-availability-ac9a462f42fe9db4.yaml diff --git a/neutronclient/neutron/v2_0/network_ip_availability.py b/neutronclient/neutron/v2_0/network_ip_availability.py new file mode 100644 index 000000000..d1332a21c --- /dev/null +++ b/neutronclient/neutron/v2_0/network_ip_availability.py @@ -0,0 +1,73 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from cliff import show +import six + +from neutronclient._i18n import _ +from neutronclient.neutron import v2_0 as neutronV20 + + +class ListIpAvailability(neutronV20.ListCommand): + """List IP usage of networks""" + + resource = 'network_ip_availability' + resource_plural = 'network_ip_availabilities' + list_columns = ['network_id', 'network_name', 'total_ips', 'used_ips'] + paginations_support = True + sorting_support = True + + filter_attrs = [ + {'name': 'ip_version', + 'help': _('Returns IP availability for the network subnets ' + 'with a given IP version. Default: 4'), + 'argparse_kwargs': {'type': int, + 'choices': [4, 6], + 'default': 4} + }, + {'name': 'network_id', + 'help': _('Returns IP availability for the network ' + 'matching a given network ID.')}, + {'name': 'network_name', + 'help': _('Returns IP availability for the network ' + 'matching a given name.')}, + {'name': 'tenant_id', + 'help': _('Returns IP availability for the networks ' + 'with a given tenant ID.')}, + ] + + +class ShowIpAvailability(neutronV20.NeutronCommand, show.ShowOne): + """Show IP usage of specific network""" + + resource = 'network_ip_availability' + + def get_parser(self, prog_name): + parser = super(ShowIpAvailability, self).get_parser(prog_name) + parser.add_argument( + 'network_id', metavar='NETWORK', + help=_('ID or name of network to look up.')) + return parser + + def take_action(self, parsed_args): + self.log.debug('run(%s)', parsed_args) + neutron_client = self.get_client() + _id = neutronV20.find_resourceid_by_name_or_id( + neutron_client, 'network', parsed_args.network_id) + data = neutron_client.show_network_ip_availability(_id) + self.format_output_data(data) + resource = data[self.resource] + if self.resource in data: + return zip(*sorted(six.iteritems(resource))) + else: + return None diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 795571ad4..be4259808 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -66,6 +66,7 @@ from neutronclient.neutron.v2_0.lb import vip as lb_vip from neutronclient.neutron.v2_0 import metering from neutronclient.neutron.v2_0 import network +from neutronclient.neutron.v2_0 import network_ip_availability from neutronclient.neutron.v2_0.nsx import networkgateway from neutronclient.neutron.v2_0.nsx import qos_queue from neutronclient.neutron.v2_0 import port @@ -427,6 +428,8 @@ def take_action(self, parsed_args): 'bgp-peer-create': bgp_peer.CreatePeer, 'bgp-peer-update': bgp_peer.UpdatePeer, 'bgp-peer-delete': bgp_peer.DeletePeer, + 'net-ip-availability-list': network_ip_availability.ListIpAvailability, + 'net-ip-availability-show': network_ip_availability.ShowIpAvailability, } COMMANDS = {'2.0': COMMAND_V2} diff --git a/neutronclient/tests/unit/test_cli20_network_ip_availability.py b/neutronclient/tests/unit/test_cli20_network_ip_availability.py new file mode 100644 index 000000000..eb325a8f6 --- /dev/null +++ b/neutronclient/tests/unit/test_cli20_network_ip_availability.py @@ -0,0 +1,54 @@ +# 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 sys + +from neutronclient.neutron.v2_0 import network_ip_availability +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20NetworkIPAvailability(test_cli20.CLITestV20Base): + + id_field = 'network_id' + + def _test_list_network_ip_availability(self, args, query): + resources = "network_ip_availabilities" + cmd = network_ip_availability.ListIpAvailability(test_cli20.MyApp + (sys.stdout), None) + self._test_list_resources(resources, cmd, + base_args=args, + query=query) + + def test_list_network_ip_availability(self): + self._test_list_network_ip_availability(args=None, + query='ip_version=4') + + def test_list_network_ip_availability_ipv6(self): + self._test_list_network_ip_availability( + args=['--ip-version', '6'], query='ip_version=6') + + def test_list_network_ip_availability_net_id_and_ipv4(self): + self._test_list_network_ip_availability( + args=['--ip-version', '4', '--network-id', 'myid'], + query='ip_version=4&network_id=myid') + + def test_list_network_ip_availability_net_name_and_tenant_id(self): + self._test_list_network_ip_availability( + args=['--network-name', 'foo', '--tenant-id', 'mytenant'], + query='network_name=foo&tenant_id=mytenant&ip_version=4') + + def test_show_network_ip_availability(self): + resource = "network_ip_availability" + cmd = network_ip_availability.ShowIpAvailability( + test_cli20.MyApp(sys.stdout), None) + self._test_show_resource(resource, cmd, self.test_id, + args=[self.test_id]) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 2c4f10520..c06f850b6 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -412,6 +412,8 @@ class Client(ClientBase): bgp_speaker_path = "/bgp-speakers/%s" bgp_peers_path = "/bgp-peers" bgp_peer_path = "/bgp-peers/%s" + network_ip_availabilities_path = '/network-ip-availabilities' + network_ip_availability_path = '/network-ip-availabilities/%s' # API has no way to report plurals, so we have to hard code them EXTED_PLURALS = {'routers': 'router', @@ -451,6 +453,7 @@ class Client(ClientBase): 'flavors': 'flavor', 'bgp_speakers': 'bgp_speaker', 'bgp_peers': 'bgp_peer', + 'network_ip_availabilities': 'network_ip_availability', } @APIParamsCall @@ -1812,6 +1815,19 @@ def delete_bgp_peer(self, peer_id): """Deletes the specified BGP peer.""" return self.delete(self.bgp_peer_path % peer_id) + @APIParamsCall + def list_network_ip_availabilities(self, retrieve_all=True, **_params): + """Fetches IP availibility information for all networks""" + return self.list('network_ip_availabilities', + self.network_ip_availabilities_path, + retrieve_all, **_params) + + @APIParamsCall + def show_network_ip_availability(self, network, **_params): + """Fetches IP availability information for a specified network""" + return self.get(self.network_ip_availability_path % (network), + params=_params) + def __init__(self, **kwargs): """Initialize a new client for the Neutron v2.0 API.""" super(Client, self).__init__(**kwargs) diff --git a/releasenotes/notes/network-ip-availability-ac9a462f42fe9db4.yaml b/releasenotes/notes/network-ip-availability-ac9a462f42fe9db4.yaml new file mode 100644 index 000000000..7a120fd7e --- /dev/null +++ b/releasenotes/notes/network-ip-availability-ac9a462f42fe9db4.yaml @@ -0,0 +1,9 @@ +--- +features: + - | + CLI support for network IP availability + + * The ``net-ip-availability-list`` command provides a list of IP + usage statistics for all networks. + * The ``net-ip-availability-show`` command provides IP usage stats + for a specific network. From 7621f6ee70275f76fcd6603d6879e5f291d07984 Mon Sep 17 00:00:00 2001 From: Yi Zhao Date: Mon, 29 Feb 2016 14:40:14 +0800 Subject: [PATCH 392/845] Fixed typos in securitygroup.py Change-Id: I8e09cc2e324256138a4b0bc48e6eb2caae8cffc5 --- neutronclient/neutron/v2_0/securitygroup.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/neutronclient/neutron/v2_0/securitygroup.py b/neutronclient/neutron/v2_0/securitygroup.py index 0e1f6d283..0e6728e5e 100644 --- a/neutronclient/neutron/v2_0/securitygroup.py +++ b/neutronclient/neutron/v2_0/securitygroup.py @@ -173,8 +173,8 @@ class ListSecurityGroupRule(neutronV20.ListCommand): replace_rules = {'security_group_id': 'security_group', 'remote_group_id': 'remote_group'} digest_fields = { - # The entry 'protocol/port' is leaving deliberetely for keep - # compatibility, + # The entry 'protocol/port' is left deliberately for backwards + # compatibility. 'remote': { 'method': _get_remote, 'depends_on': ['remote_ip_prefix', 'remote_group_id']}, @@ -258,8 +258,8 @@ def _get_sec_group_list(sec_group_ids): for sg in secgroups if sg['name']]) @staticmethod - def _has_fileds(rule, required_fileds): - return all([key in rule for key in required_fileds]) + def _has_fields(rule, required_fields): + return all([key in rule for key in required_fields]) def extend_list(self, data, parsed_args): sg_dict = self._get_sg_name_dict(data, parsed_args.page_size, @@ -270,7 +270,7 @@ def extend_list(self, data, parsed_args): if key in rule: rule[key] = sg_dict.get(rule[key], rule[key]) for field, digest_rule in self.digest_fields.items(): - if self._has_fileds(rule, digest_rule['depends_on']): + if self._has_fields(rule, digest_rule['depends_on']): rule[field] = digest_rule['method'](rule) or 'any' def setup_columns(self, info, parsed_args): From f67f4af8bd733f5bd186a7c02211ed0667e08a96 Mon Sep 17 00:00:00 2001 From: Hirofumi Ichihara Date: Tue, 1 Mar 2016 12:37:15 +0900 Subject: [PATCH 393/845] Add tags support This patch adds the tag support for CLI. Change-Id: Ia84b873e3916e7b9668181fe14c1448ef608bf1d Partial-Implements: blueprint add-tags-to-core-resources Related-Bug: #1489291 --- neutronclient/neutron/v2_0/__init__.py | 3 + neutronclient/neutron/v2_0/network.py | 21 +++ neutronclient/neutron/v2_0/tag.py | 105 ++++++++++++++ neutronclient/shell.py | 4 + neutronclient/tests/unit/test_cli20_tag.py | 128 ++++++++++++++++++ neutronclient/v2_0/client.py | 22 +++ .../add-tag-support-bad62d60ecc7075c.yaml | 16 +++ 7 files changed, 299 insertions(+) create mode 100644 neutronclient/neutron/v2_0/tag.py create mode 100644 neutronclient/tests/unit/test_cli20_tag.py create mode 100644 releasenotes/notes/add-tag-support-bad62d60ecc7075c.yaml diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index d740aac3e..9da48286a 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -36,6 +36,7 @@ UUID_PATTERN = '-'.join([HEX_ELEM + '{8}', HEX_ELEM + '{4}', HEX_ELEM + '{4}', HEX_ELEM + '{4}', HEX_ELEM + '{12}']) +HYPHEN_OPTS = ['tags_any', 'not_tags', 'not_tags_any'] def _get_resource_plural(resource, client): @@ -691,6 +692,8 @@ def args2search_opts(self, parsed_args): for field in self.filter_attrs] for attr in filter_attrs: val = getattr(parsed_args, attr, None) + if attr in HYPHEN_OPTS: + attr = attr.replace('_', '-') if val: search_opts[attr] = val return search_opts diff --git a/neutronclient/neutron/v2_0/network.py b/neutronclient/neutron/v2_0/network.py index 2fb26d327..4ec7ace8e 100644 --- a/neutronclient/neutron/v2_0/network.py +++ b/neutronclient/neutron/v2_0/network.py @@ -60,6 +60,27 @@ class ListNetwork(neutronV20.ListCommand): {'name': 'router:external', 'help': _('Filter and list the networks which are external.'), 'boolean': True}, + {'name': 'tags', + 'help': _("Filter and list %s which has all given tags. " + "Multiple tags can be set like --tags "), + 'boolean': False, + 'argparse_kwargs': {'metavar': 'TAG'}}, + {'name': 'tags_any', + 'help': _("Filter and list %s which has any given tags. " + "Multiple tags can be set like --tags-any "), + 'boolean': False, + 'argparse_kwargs': {'metavar': 'TAG'}}, + {'name': 'not_tags', + 'help': _("Filter and list %s which does not have all given tags. " + "Multiple tags can be set like --not-tags "), + 'boolean': False, + 'argparse_kwargs': {'metavar': 'TAG'}}, + {'name': 'not_tags_any', + 'help': _("Filter and list %s which does not have any given tags. " + "Multiple tags can be set like --not-tags-any " + ""), + 'boolean': False, + 'argparse_kwargs': {'metavar': 'TAG'}}, ] def extend_list(self, data, parsed_args): diff --git a/neutronclient/neutron/v2_0/tag.py b/neutronclient/neutron/v2_0/tag.py new file mode 100644 index 000000000..2507d417e --- /dev/null +++ b/neutronclient/neutron/v2_0/tag.py @@ -0,0 +1,105 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from neutronclient._i18n import _ +from neutronclient.common import exceptions +from neutronclient.neutron import v2_0 as neutronv20 + + +# List of resources can be set tag +TAG_RESOURCES = ['network'] + + +def _convert_resource_args(client, parsed_args): + resource_type = neutronv20._get_resource_plural( + parsed_args.resource_type, client) + resource_id = neutronv20.find_resourceid_by_name_or_id( + client, parsed_args.resource_type, parsed_args.resource) + return resource_type, resource_id + + +def _add_common_arguments(parser): + parser.add_argument('--resource-type', + choices=TAG_RESOURCES, + dest='resource_type', + required=True, + help=_('Resource Type.')) + parser.add_argument('--resource', + required=True, + help=_('Resource name or ID.')) + + +class AddTag(neutronv20.NeutronCommand): + """Add a tag into the resource.""" + + def get_parser(self, prog_name): + parser = super(AddTag, self).get_parser(prog_name) + _add_common_arguments(parser) + parser.add_argument('--tag', + required=True, + help=_('Tag to be added.')) + return parser + + def take_action(self, parsed_args): + client = self.get_client() + resource_type, resource_id = _convert_resource_args(client, + parsed_args) + client.add_tag(resource_type, resource_id, parsed_args.tag) + + +class ReplaceTag(neutronv20.NeutronCommand): + """Replace all tags on the resource.""" + + def get_parser(self, prog_name): + parser = super(ReplaceTag, self).get_parser(prog_name) + _add_common_arguments(parser) + parser.add_argument('--tag', + metavar='TAG', + action='append', + dest='tags', + required=True, + help=_('Tag (This option can be repeated).')) + return parser + + def take_action(self, parsed_args): + client = self.get_client() + resource_type, resource_id = _convert_resource_args(client, + parsed_args) + body = {'tags': parsed_args.tags} + client.replace_tag(resource_type, resource_id, body) + + +class RemoveTag(neutronv20.NeutronCommand): + """Remove a tag on the resource.""" + + def get_parser(self, prog_name): + parser = super(RemoveTag, self).get_parser(prog_name) + _add_common_arguments(parser) + tag_opt = parser.add_mutually_exclusive_group() + tag_opt.add_argument('--all', + action='store_true', + help=_('Remove all tags on the resource.')) + tag_opt.add_argument('--tag', + help=_('Tag to be removed.')) + return parser + + def take_action(self, parsed_args): + if not parsed_args.all and not parsed_args.tag: + raise exceptions.CommandError( + _("--all or --tag must be specified")) + client = self.get_client() + resource_type, resource_id = _convert_resource_args(client, + parsed_args) + if parsed_args.all: + client.remove_tag_all(resource_type, resource_id) + else: + client.remove_tag(resource_type, resource_id, parsed_args.tag) diff --git a/neutronclient/shell.py b/neutronclient/shell.py index bb923c2e8..5261a349e 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -83,6 +83,7 @@ from neutronclient.neutron.v2_0 import servicetype from neutronclient.neutron.v2_0 import subnet from neutronclient.neutron.v2_0 import subnetpool +from neutronclient.neutron.v2_0 import tag from neutronclient.neutron.v2_0.vpn import endpoint_group from neutronclient.neutron.v2_0.vpn import ikepolicy from neutronclient.neutron.v2_0.vpn import ipsec_site_connection @@ -444,6 +445,9 @@ def take_action(self, parsed_args): 'bgp-peer-delete': bgp_peer.DeletePeer, 'net-ip-availability-list': network_ip_availability.ListIpAvailability, 'net-ip-availability-show': network_ip_availability.ShowIpAvailability, + 'tag-add': tag.AddTag, + 'tag-replace': tag.ReplaceTag, + 'tag-remove': tag.RemoveTag, } COMMANDS = {'2.0': COMMAND_V2} diff --git a/neutronclient/tests/unit/test_cli20_tag.py b/neutronclient/tests/unit/test_cli20_tag.py new file mode 100644 index 000000000..24f07b73a --- /dev/null +++ b/neutronclient/tests/unit/test_cli20_tag.py @@ -0,0 +1,128 @@ +# 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 sys + +from mox3 import mox + +from neutronclient.common import exceptions +from neutronclient.neutron import v2_0 as neutronV2_0 +from neutronclient.neutron.v2_0 import network +from neutronclient.neutron.v2_0 import tag +from neutronclient import shell +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20Tag(test_cli20.CLITestV20Base): + def _test_tag_operation(self, cmd, path, method, args, prog_name, + body=None): + self.mox.StubOutWithMock(cmd, "get_client") + self.mox.StubOutWithMock(self.client.httpclient, "request") + cmd.get_client().MultipleTimes().AndReturn(self.client) + if body: + body = test_cli20.MyComparator(body, self.client) + self.client.httpclient.request( + test_cli20.MyUrlComparator( + test_cli20.end_url(path, format=self.format), self.client), + method, body=body, + headers=mox.ContainsKeyValue( + 'X-Auth-Token', test_cli20.TOKEN)).AndReturn( + (test_cli20.MyResp(204), None)) + self.mox.ReplayAll() + cmd_parser = cmd.get_parser(prog_name) + shell.run_command(cmd, cmd_parser, args) + self.mox.VerifyAll() + self.mox.UnsetStubs() + + def _test_tags_query(self, cmd, resources, args, query): + self.mox.StubOutWithMock(cmd, "get_client") + self.mox.StubOutWithMock(self.client.httpclient, "request") + cmd.get_client().MultipleTimes().AndReturn(self.client) + path = getattr(self.client, resources + "_path") + res = {resources: [{'id': 'myid'}]} + resstr = self.client.serialize(res) + self.client.httpclient.request( + test_cli20.MyUrlComparator( + test_cli20.end_url(path, query, format=self.format), + self.client), + 'GET', body=None, + headers=mox.ContainsKeyValue( + 'X-Auth-Token', test_cli20.TOKEN)).AndReturn( + (test_cli20.MyResp(200), resstr)) + self.mox.ReplayAll() + cmd_parser = cmd.get_parser("list_networks") + shell.run_command(cmd, cmd_parser, args) + self.mox.VerifyAll() + self.mox.UnsetStubs() + _str = self.fake_stdout.make_string() + self.assertIn('myid', _str) + + def _make_tag_path(self, resource, resource_id, tag): + path = getattr(self.client, "tag_path") + resource_plural = neutronV2_0._get_resource_plural(resource, + self.client) + return path % (resource_plural, resource_id, tag) + + def _make_tags_path(self, resource, resource_id): + path = getattr(self.client, "tags_path") + resource_plural = neutronV2_0._get_resource_plural(resource, + self.client) + return path % (resource_plural, resource_id) + + def test_add_tag(self): + cmd = tag.AddTag(test_cli20.MyApp(sys.stdout), None) + path = self._make_tag_path('network', 'myid', 'red') + args = ['--resource-type', 'network', '--resource', 'myid', + '--tag', 'red'] + self._test_tag_operation(cmd, path, 'PUT', args, "tag-add") + + def test_replace_tag(self): + cmd = tag.ReplaceTag(test_cli20.MyApp(sys.stdout), None) + path = self._make_tags_path('network', 'myid') + args = ['--resource-type', 'network', '--resource', 'myid', + '--tag', 'red', '--tag', 'blue'] + body = {'tags': ['red', 'blue']} + self._test_tag_operation(cmd, path, 'PUT', args, "tag-replace", + body=body) + + def test_remove_tag(self): + cmd = tag.RemoveTag(test_cli20.MyApp(sys.stdout), None) + path = self._make_tag_path('network', 'myid', 'red') + args = ['--resource-type', 'network', '--resource', 'myid', + '--tag', 'red'] + self._test_tag_operation(cmd, path, 'DELETE', args, "tag-remove") + + def test_remove_tag_all(self): + cmd = tag.RemoveTag(test_cli20.MyApp(sys.stdout), None) + path = self._make_tags_path('network', 'myid') + args = ['--resource-type', 'network', '--resource', 'myid', + '--all'] + self._test_tag_operation(cmd, path, 'DELETE', args, "tag-remove") + + def test_no_tag_nor_all(self): + cmd = tag.RemoveTag(test_cli20.MyApp(sys.stdout), None) + path = self._make_tags_path('network', 'myid') + args = ['--resource-type', 'network', '--resource', 'myid'] + self.assertRaises(exceptions.CommandError, self._test_tag_operation, + cmd, path, 'DELETE', args, "tag-remove") + + def test_tags_query(self): + # This test examines that '-' in the tag related filters + # is not converted to '_'. + resources = 'networks' + cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) + self.mox.StubOutWithMock(network.ListNetwork, "extend_list") + network.ListNetwork.extend_list(mox.IsA(list), mox.IgnoreArg()) + args = ['--not-tags', 'red,blue', '--tags-any', 'green', + '--not-tags-any', 'black'] + query = "not-tags=red,blue&tags-any=green¬-tags-any=black" + self._test_tags_query(cmd, resources, args, query) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index dbe3af5ef..f25b007e5 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -528,6 +528,8 @@ class Client(ClientBase): bgp_peer_path = "/bgp-peers/%s" network_ip_availabilities_path = '/network-ip-availabilities' network_ip_availability_path = '/network-ip-availabilities/%s' + tags_path = "/%s/%s/tags" + tag_path = "/%s/%s/tags/%s" # API has no way to report plurals, so we have to hard code them EXTED_PLURALS = {'routers': 'router', @@ -2002,6 +2004,26 @@ def show_network_ip_availability(self, network, **_params): return self.get(self.network_ip_availability_path % (network), params=_params) + @APIParamsCall + def add_tag(self, resource_type, resource_id, tag, **_params): + """Add a tag on the resource.""" + return self.put(self.tag_path % (resource_type, resource_id, tag)) + + @APIParamsCall + def replace_tag(self, resource_type, resource_id, body, **_params): + """Replace tags on the resource.""" + return self.put(self.tags_path % (resource_type, resource_id), body) + + @APIParamsCall + def remove_tag(self, resource_type, resource_id, tag, **_params): + """Remove a tag on the resource.""" + return self.delete(self.tag_path % (resource_type, resource_id, tag)) + + @APIParamsCall + def remove_tag_all(self, resource_type, resource_id, **_params): + """Remove all tags on the resource.""" + return self.delete(self.tags_path % (resource_type, resource_id)) + def __init__(self, **kwargs): """Initialize a new client for the Neutron v2.0 API.""" super(Client, self).__init__(**kwargs) diff --git a/releasenotes/notes/add-tag-support-bad62d60ecc7075c.yaml b/releasenotes/notes/add-tag-support-bad62d60ecc7075c.yaml new file mode 100644 index 000000000..cc5b6eec2 --- /dev/null +++ b/releasenotes/notes/add-tag-support-bad62d60ecc7075c.yaml @@ -0,0 +1,16 @@ +--- +features: + - | + CLI support for tag. + + * The ``tag-add`` command sets a tag on the network resource. It also + includes ``--resource-type``, ``--resource`` and ``--tag`` options. + * The ``tag-replace`` command replaces tags on the network resource. It + also includes ``--resource-type``, ``--resource`` and ``--tag`` + options. More than one ``--tag`` options can be set. + * The ``tag-remove`` command removes tags on the network resource. It also + includes ``--resource-type``, ``--resource``, ``--tag`` and ``--all`` + options. The ``--all`` option allow to remove all tags on the network + resource. + * The ``net-list`` command includes ``--tags``, ``--tags-any``, + ``--not-tags`` and ``--not-tags-any`` options. \ No newline at end of file From 6f2963d75204acd515089b70496f2ca3d0397f60 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Fri, 8 Jan 2016 23:16:43 +0900 Subject: [PATCH 394/845] refactor: Avoid overriding run() in cliff command cliff Command subclasses are suggested to override take_action. run() method is reserved for extending base classes. Thus, this commit changes to implement take_action() instead of run(). Closes-Bug: #1532258 Change-Id: Id845a80dbad2f436df5a55c9be502068e6f7b291 --- neutronclient/common/extension.py | 8 ++++---- neutronclient/neutron/v2_0/__init__.py | 10 ++-------- neutronclient/neutron/v2_0/agentscheduler.py | 8 ++++---- neutronclient/neutron/v2_0/bgp/speaker.py | 8 ++++---- neutronclient/neutron/v2_0/flavor/flavor.py | 4 ++-- neutronclient/neutron/v2_0/floatingip.py | 4 ++-- neutronclient/neutron/v2_0/fw/firewallpolicy.py | 4 ++-- neutronclient/neutron/v2_0/lb/healthmonitor.py | 4 ++-- neutronclient/neutron/v2_0/nsx/networkgateway.py | 4 ++-- neutronclient/neutron/v2_0/purge.py | 2 +- neutronclient/neutron/v2_0/quota.py | 2 +- neutronclient/neutron/v2_0/router.py | 6 +++--- 12 files changed, 29 insertions(+), 35 deletions(-) diff --git a/neutronclient/common/extension.py b/neutronclient/common/extension.py index f7e361bed..90f44a79b 100644 --- a/neutronclient/common/extension.py +++ b/neutronclient/common/extension.py @@ -54,14 +54,14 @@ def execute(self, parsed_args): class ClientExtensionDelete(NeutronClientExtension, neutronV20.DeleteCommand): - def run(self, parsed_args): + def take_action(self, parsed_args): # NOTE(mdietz): Calls 'execute' to provide a consistent pattern # for any implementers adding extensions with # regard to any other extension verb. return self.execute(parsed_args) def execute(self, parsed_args): - return super(ClientExtensionDelete, self).run(parsed_args) + return super(ClientExtensionDelete, self).take_action(parsed_args) class ClientExtensionCreate(NeutronClientExtension, neutronV20.CreateCommand): @@ -76,11 +76,11 @@ def execute(self, parsed_args): class ClientExtensionUpdate(NeutronClientExtension, neutronV20.UpdateCommand): - def run(self, parsed_args): + def take_action(self, parsed_args): # NOTE(mdietz): Calls 'execute' to provide a consistent pattern # for any implementers adding extensions with # regard to any other extension verb. return self.execute(parsed_args) def execute(self, parsed_args): - return super(ClientExtensionUpdate, self).run(parsed_args) + return super(ClientExtensionUpdate, self).take_action(parsed_args) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index d740aac3e..14a354865 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -397,12 +397,6 @@ class NeutronCommand(command.Command): shadow_resource = None parent_id = None - # TODO(amotoki): Remove take_action here. It should be an abstract method - # as cliff.command.Command does. To do this, we need to avoid overriding - # run() directly. - def take_action(self, parsed_args): - return self.get_data(parsed_args) - @property def cmd_resource(self): if self.shadow_resource: @@ -517,7 +511,7 @@ def get_parser(self, prog_name): self.add_known_arguments(parser) return parser - def run(self, parsed_args): + def take_action(self, parsed_args): self.log.debug('run(%s)', parsed_args) self.set_extra_attrs(parsed_args) neutron_client = self.get_client() @@ -574,7 +568,7 @@ def get_parser(self, prog_name): self.add_known_arguments(parser) return parser - def run(self, parsed_args): + def take_action(self, parsed_args): self.log.debug('run(%s)', parsed_args) self.set_extra_attrs(parsed_args) neutron_client = self.get_client() diff --git a/neutronclient/neutron/v2_0/agentscheduler.py b/neutronclient/neutron/v2_0/agentscheduler.py index 24287b710..dacfab863 100644 --- a/neutronclient/neutron/v2_0/agentscheduler.py +++ b/neutronclient/neutron/v2_0/agentscheduler.py @@ -40,7 +40,7 @@ def get_parser(self, prog_name): help=_('Network to add.')) return parser - def run(self, parsed_args): + def take_action(self, parsed_args): self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() _net_id = neutronV20.find_resourceid_by_name_or_id( @@ -66,7 +66,7 @@ def get_parser(self, prog_name): help=_('Network to remove.')) return parser - def run(self, parsed_args): + def take_action(self, parsed_args): self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() _net_id = neutronV20.find_resourceid_by_name_or_id( @@ -142,7 +142,7 @@ def get_parser(self, prog_name): help=_('Router to add.')) return parser - def run(self, parsed_args): + def take_action(self, parsed_args): self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() _id = neutronV20.find_resourceid_by_name_or_id( @@ -168,7 +168,7 @@ def get_parser(self, prog_name): help=_('Router to remove.')) return parser - def run(self, parsed_args): + def take_action(self, parsed_args): self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() _id = neutronV20.find_resourceid_by_name_or_id( diff --git a/neutronclient/neutron/v2_0/bgp/speaker.py b/neutronclient/neutron/v2_0/bgp/speaker.py index f2a3df7db..1a70734e5 100755 --- a/neutronclient/neutron/v2_0/bgp/speaker.py +++ b/neutronclient/neutron/v2_0/bgp/speaker.py @@ -153,7 +153,7 @@ def get_parser(self, prog_name): help=_('ID or name of the BGP peer to add.')) return parser - def run(self, parsed_args): + def take_action(self, parsed_args): neutron_client = self.get_client() _speaker_id = get_bgp_speaker_id(neutron_client, parsed_args.bgp_speaker) @@ -182,7 +182,7 @@ def get_parser(self, prog_name): help=_('ID or name of the BGP peer to remove.')) return parser - def run(self, parsed_args): + def take_action(self, parsed_args): neutron_client = self.get_client() _speaker_id = get_bgp_speaker_id(neutron_client, parsed_args.bgp_speaker) @@ -211,7 +211,7 @@ def get_parser(self, prog_name): help=_('ID or name of the network to add.')) return parser - def run(self, parsed_args): + def take_action(self, parsed_args): neutron_client = self.get_client() _speaker_id = get_bgp_speaker_id(neutron_client, parsed_args.bgp_speaker) @@ -239,7 +239,7 @@ def get_parser(self, prog_name): help=_('ID or name of the network to remove.')) return parser - def run(self, parsed_args): + def take_action(self, parsed_args): neutron_client = self.get_client() _speaker_id = get_bgp_speaker_id(neutron_client, parsed_args.bgp_speaker) diff --git a/neutronclient/neutron/v2_0/flavor/flavor.py b/neutronclient/neutron/v2_0/flavor/flavor.py index 30e3ae414..57ec8fa3b 100644 --- a/neutronclient/neutron/v2_0/flavor/flavor.py +++ b/neutronclient/neutron/v2_0/flavor/flavor.py @@ -118,7 +118,7 @@ def get_parser(self, prog_name): 'flavor.')) return parser - def run(self, parsed_args): + def take_action(self, parsed_args): neutron_client = self.get_client() flavor_id = neutronV20.find_resourceid_by_name_or_id( neutron_client, 'flavor', parsed_args.flavor) @@ -151,7 +151,7 @@ def get_parser(self, prog_name): 'flavor.')) return parser - def run(self, parsed_args): + def take_action(self, parsed_args): neutron_client = self.get_client() flavor_id = neutronV20.find_resourceid_by_name_or_id( neutron_client, 'flavor', parsed_args.flavor) diff --git a/neutronclient/neutron/v2_0/floatingip.py b/neutronclient/neutron/v2_0/floatingip.py index e361a293a..f20889688 100644 --- a/neutronclient/neutron/v2_0/floatingip.py +++ b/neutronclient/neutron/v2_0/floatingip.py @@ -115,7 +115,7 @@ def get_parser(self, prog_name): help=argparse.SUPPRESS) return parser - def run(self, parsed_args): + def take_action(self, parsed_args): self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() update_dict = {} @@ -139,7 +139,7 @@ def get_parser(self, prog_name): help=_('ID of the floating IP to disassociate.')) return parser - def run(self, parsed_args): + def take_action(self, parsed_args): self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() neutron_client.update_floatingip(parsed_args.floatingip_id, diff --git a/neutronclient/neutron/v2_0/fw/firewallpolicy.py b/neutronclient/neutron/v2_0/fw/firewallpolicy.py index 08d623ab9..a01fc726f 100644 --- a/neutronclient/neutron/v2_0/fw/firewallpolicy.py +++ b/neutronclient/neutron/v2_0/fw/firewallpolicy.py @@ -179,7 +179,7 @@ def get_parser(self, prog_name): self.add_known_arguments(parser) return parser - def run(self, parsed_args): + def take_action(self, parsed_args): neutron_client = self.get_client() body = self.args2body(parsed_args) _id = neutronv20.find_resourceid_by_name_or_id(neutron_client, @@ -217,7 +217,7 @@ def get_parser(self, prog_name): self.add_known_arguments(parser) return parser - def run(self, parsed_args): + def take_action(self, parsed_args): neutron_client = self.get_client() body = self.args2body(parsed_args) _id = neutronv20.find_resourceid_by_name_or_id(neutron_client, diff --git a/neutronclient/neutron/v2_0/lb/healthmonitor.py b/neutronclient/neutron/v2_0/lb/healthmonitor.py index ee5d70bb5..6fea0a12a 100644 --- a/neutronclient/neutron/v2_0/lb/healthmonitor.py +++ b/neutronclient/neutron/v2_0/lb/healthmonitor.py @@ -124,7 +124,7 @@ def get_parser(self, prog_name): help=_('ID of the pool to be associated with the health monitor.')) return parser - def run(self, parsed_args): + def take_action(self, parsed_args): neutron_client = self.get_client() body = {'health_monitor': {'id': parsed_args.health_monitor_id}} pool_id = neutronV20.find_resourceid_by_name_or_id( @@ -150,7 +150,7 @@ def get_parser(self, prog_name): help=_('ID of the pool to be associated with the health monitor.')) return parser - def run(self, parsed_args): + def take_action(self, parsed_args): neutron_client = self.get_client() pool_id = neutronV20.find_resourceid_by_name_or_id( neutron_client, 'pool', parsed_args.pool_id) diff --git a/neutronclient/neutron/v2_0/nsx/networkgateway.py b/neutronclient/neutron/v2_0/nsx/networkgateway.py index 46d83e9a5..c7df5130c 100644 --- a/neutronclient/neutron/v2_0/nsx/networkgateway.py +++ b/neutronclient/neutron/v2_0/nsx/networkgateway.py @@ -234,7 +234,7 @@ def retrieve_ids(self, client, args): class ConnectNetworkGateway(NetworkGatewayInterfaceCommand): """Add an internal network interface to a router.""" - def run(self, parsed_args): + def take_action(self, parsed_args): self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() (gateway_id, network_id) = self.retrieve_ids(neutron_client, @@ -252,7 +252,7 @@ def run(self, parsed_args): class DisconnectNetworkGateway(NetworkGatewayInterfaceCommand): """Remove a network from a network gateway.""" - def run(self, parsed_args): + def take_action(self, parsed_args): self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() (gateway_id, network_id) = self.retrieve_ids(neutron_client, diff --git a/neutronclient/neutron/v2_0/purge.py b/neutronclient/neutron/v2_0/purge.py index 6d8e9d9b9..61762963f 100644 --- a/neutronclient/neutron/v2_0/purge.py +++ b/neutronclient/neutron/v2_0/purge.py @@ -125,7 +125,7 @@ def get_parser(self, prog_name): help=_('ID of Tenant owning the resources to be deleted.')) return parser - def run(self, parsed_args): + def take_action(self, parsed_args): neutron_client = self.get_client() self.any_failures = False diff --git a/neutronclient/neutron/v2_0/quota.py b/neutronclient/neutron/v2_0/quota.py index a88f0c628..985e61921 100644 --- a/neutronclient/neutron/v2_0/quota.py +++ b/neutronclient/neutron/v2_0/quota.py @@ -52,7 +52,7 @@ def get_parser(self, prog_name): help=argparse.SUPPRESS, nargs='?') return parser - def run(self, parsed_args): + def take_action(self, parsed_args): self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() tenant_id = get_tenant_id(parsed_args, neutron_client) diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index c372f98ae..76022e77d 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -161,7 +161,7 @@ def get_parser(self, prog_name): 'subnet.')) return parser - def run(self, parsed_args): + def take_action(self, parsed_args): self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() @@ -236,7 +236,7 @@ def get_parser(self, prog_name): 'You can repeat this option.')) return parser - def run(self, parsed_args): + def take_action(self, parsed_args): self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() _router_id = neutronV20.find_resourceid_by_name_or_id( @@ -273,7 +273,7 @@ def get_parser(self, prog_name): help=_('ID or name of the router.')) return parser - def run(self, parsed_args): + def take_action(self, parsed_args): self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() _router_id = neutronV20.find_resourceid_by_name_or_id( From 724d4b5c1145a39d2faf765f608d7a6a06bfbe4f Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Fri, 8 Jan 2016 23:27:55 +0900 Subject: [PATCH 395/845] refactor: Merge all debug logging at the beginning of take_action After refactoring made in this series of patches, all neutronclient commands implements take_action(). Debug logging at the beginning of take_action can be implemented in a signle place. Change-Id: Ic766c0bd0f203b2408dc459247804c7d9290b0d5 --- neutronclient/neutron/v2_0/__init__.py | 9 ++++----- neutronclient/neutron/v2_0/agentscheduler.py | 4 ---- neutronclient/neutron/v2_0/floatingip.py | 2 -- neutronclient/neutron/v2_0/lb/pool.py | 1 - neutronclient/neutron/v2_0/lb/v2/loadbalancer.py | 1 - neutronclient/neutron/v2_0/nsx/networkgateway.py | 2 -- neutronclient/neutron/v2_0/quota.py | 4 ---- neutronclient/neutron/v2_0/router.py | 3 --- 8 files changed, 4 insertions(+), 22 deletions(-) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index 14a354865..528f3af12 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -397,6 +397,10 @@ class NeutronCommand(command.Command): shadow_resource = None parent_id = None + def run(self, parsed_args): + self.log.debug('run(%s)', parsed_args) + return super(NeutronCommand, self).run(parsed_args) + @property def cmd_resource(self): if self.shadow_resource: @@ -466,7 +470,6 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - self.log.debug('run(%s)' % parsed_args) self.set_extra_attrs(parsed_args) neutron_client = self.get_client() _extra_values = parse_args_to_dict(self.values_specs) @@ -512,7 +515,6 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - self.log.debug('run(%s)', parsed_args) self.set_extra_attrs(parsed_args) neutron_client = self.get_client() _extra_values = parse_args_to_dict(self.values_specs) @@ -569,7 +571,6 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - self.log.debug('run(%s)', parsed_args) self.set_extra_attrs(parsed_args) neutron_client = self.get_client() obj_deleter = getattr(neutron_client, @@ -758,7 +759,6 @@ def setup_columns(self, info, parsed_args): for s in info), ) def take_action(self, parsed_args): - self.log.debug('run(%s)', parsed_args) self.set_extra_attrs(parsed_args) data = self.retrieve_list(parsed_args) self.extend_list(data, parsed_args) @@ -788,7 +788,6 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - self.log.debug('run(%s)', parsed_args) self.set_extra_attrs(parsed_args) neutron_client = self.get_client() diff --git a/neutronclient/neutron/v2_0/agentscheduler.py b/neutronclient/neutron/v2_0/agentscheduler.py index dacfab863..2d2b1f7d5 100644 --- a/neutronclient/neutron/v2_0/agentscheduler.py +++ b/neutronclient/neutron/v2_0/agentscheduler.py @@ -41,7 +41,6 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() _net_id = neutronV20.find_resourceid_by_name_or_id( neutron_client, 'network', parsed_args.network) @@ -67,7 +66,6 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() _net_id = neutronV20.find_resourceid_by_name_or_id( neutron_client, 'network', parsed_args.network) @@ -143,7 +141,6 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() _id = neutronV20.find_resourceid_by_name_or_id( neutron_client, 'router', parsed_args.router) @@ -169,7 +166,6 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() _id = neutronV20.find_resourceid_by_name_or_id( neutron_client, 'router', parsed_args.router) diff --git a/neutronclient/neutron/v2_0/floatingip.py b/neutronclient/neutron/v2_0/floatingip.py index f20889688..78c0dd3f8 100644 --- a/neutronclient/neutron/v2_0/floatingip.py +++ b/neutronclient/neutron/v2_0/floatingip.py @@ -116,7 +116,6 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() update_dict = {} neutronV20.update_dict(parsed_args, update_dict, @@ -140,7 +139,6 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() neutron_client.update_floatingip(parsed_args.floatingip_id, {'floatingip': {'port_id': None}}) diff --git a/neutronclient/neutron/v2_0/lb/pool.py b/neutronclient/neutron/v2_0/lb/pool.py index 86497a7d9..5d3a9db7f 100644 --- a/neutronclient/neutron/v2_0/lb/pool.py +++ b/neutronclient/neutron/v2_0/lb/pool.py @@ -108,7 +108,6 @@ class RetrievePoolStats(neutronV20.ShowCommand): resource = 'pool' def take_action(self, parsed_args): - self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() pool_id = neutronV20.find_resourceid_by_name_or_id( self.get_client(), 'pool', parsed_args.id) diff --git a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py index 5a60eb49a..ceba4990d 100644 --- a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py +++ b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py @@ -102,7 +102,6 @@ class RetrieveLoadBalancerStats(neutronV20.ShowCommand): resource = 'loadbalancer' def take_action(self, parsed_args): - self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() neutron_client.format = parsed_args.request_format loadbalancer_id = neutronV20.find_resourceid_by_name_or_id( diff --git a/neutronclient/neutron/v2_0/nsx/networkgateway.py b/neutronclient/neutron/v2_0/nsx/networkgateway.py index c7df5130c..153c855ae 100644 --- a/neutronclient/neutron/v2_0/nsx/networkgateway.py +++ b/neutronclient/neutron/v2_0/nsx/networkgateway.py @@ -235,7 +235,6 @@ class ConnectNetworkGateway(NetworkGatewayInterfaceCommand): """Add an internal network interface to a router.""" def take_action(self, parsed_args): - self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() (gateway_id, network_id) = self.retrieve_ids(neutron_client, parsed_args) @@ -253,7 +252,6 @@ class DisconnectNetworkGateway(NetworkGatewayInterfaceCommand): """Remove a network from a network gateway.""" def take_action(self, parsed_args): - self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() (gateway_id, network_id) = self.retrieve_ids(neutron_client, parsed_args) diff --git a/neutronclient/neutron/v2_0/quota.py b/neutronclient/neutron/v2_0/quota.py index 985e61921..e7e74b106 100644 --- a/neutronclient/neutron/v2_0/quota.py +++ b/neutronclient/neutron/v2_0/quota.py @@ -53,7 +53,6 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() tenant_id = get_tenant_id(parsed_args, neutron_client) obj_deleter = getattr(neutron_client, @@ -76,7 +75,6 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - self.log.debug('run(%s)', parsed_args) neutron_client = self.get_client() search_opts = {} self.log.debug('search options: %s', search_opts) @@ -115,7 +113,6 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - self.log.debug('run(%s)', parsed_args) neutron_client = self.get_client() tenant_id = get_tenant_id(parsed_args, neutron_client) params = {} @@ -214,7 +211,6 @@ def args2body(self, parsed_args): return {self.resource: quota} def take_action(self, parsed_args): - self.log.debug('run(%s)', parsed_args) neutron_client = self.get_client() _extra_values = neutronV20.parse_args_to_dict(self.values_specs) neutronV20._merge_args(self, parsed_args, _extra_values, diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index 76022e77d..5eba1f20d 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -162,7 +162,6 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() if '=' in parsed_args.interface: @@ -237,7 +236,6 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() _router_id = neutronV20.find_resourceid_by_name_or_id( neutron_client, self.resource, parsed_args.router) @@ -274,7 +272,6 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - self.log.debug('run(%s)' % parsed_args) neutron_client = self.get_client() _router_id = neutronV20.find_resourceid_by_name_or_id( neutron_client, self.resource, parsed_args.router) From 0740766467a3c8de0bf9d03689158a86591bd506 Mon Sep 17 00:00:00 2001 From: Jaspinder Date: Mon, 4 Jan 2016 05:08:41 -0600 Subject: [PATCH 396/845] fix: can't get authentication with os-token and os-url Currently, there is no way to authenticate a user through Neutron CLI by just using endpoint and token authentication. This simple fix will at least allow for that to be permitted. Change-Id: Ia7d285af224ef225aa20f83d7d4c87b81aac58ed Closes-Bug: 1450414 --- neutronclient/shell.py | 21 +++++++++++++++---- ...ndpoint-auth-support-26bf7ee12e4ec833.yaml | 7 +++++++ 2 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 releasenotes/notes/add-token-endpoint-auth-support-26bf7ee12e4ec833.yaml diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 5261a349e..64de864af 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -898,11 +898,22 @@ def authenticate_user(self): cloud=self.options.os_cloud, argparse=self.options, network_api_version=self.api_version) verify, cert = cloud_config.get_requests_verify_args() - auth = cloud_config.get_auth() - auth_session = session.Session( - auth=auth, verify=verify, cert=cert, - timeout=self.options.http_timeout) + # TODO(singhj): Remove dependancy on HTTPClient + # for the case of token-endpoint authentication + + # When using token-endpoint authentication legacy + # HTTPClient will be used, otherwise SessionClient + # will be used. + if self.options.os_token and self.options.os_url: + auth = None + auth_session = None + else: + auth = cloud_config.get_auth() + + auth_session = session.Session( + auth=auth, verify=verify, cert=cert, + timeout=self.options.http_timeout) interface = self.options.os_endpoint_type or self.endpoint_type if interface.endswith('URL'): @@ -911,6 +922,8 @@ def authenticate_user(self): retries=self.options.retries, raise_errors=False, session=auth_session, + url=self.options.os_url, + token=self.options.os_token, region_name=cloud_config.get_region_name(), api_version=cloud_config.get_api_version('network'), service_type=cloud_config.get_service_type('network'), diff --git a/releasenotes/notes/add-token-endpoint-auth-support-26bf7ee12e4ec833.yaml b/releasenotes/notes/add-token-endpoint-auth-support-26bf7ee12e4ec833.yaml new file mode 100644 index 000000000..d3445cde0 --- /dev/null +++ b/releasenotes/notes/add-token-endpoint-auth-support-26bf7ee12e4ec833.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + CLI support for token-endpoint authentication. + + * Allows for authentication via ``--os-token`` and ``--os-url`` options + or the ``OS_TOKEN`` and ``OS_URL`` environment variables, respectively From d3f13f4c20eba725fa8228a25cdfd77d47b7569d Mon Sep 17 00:00:00 2001 From: Henry Gessau Date: Wed, 24 Feb 2016 12:46:58 -0500 Subject: [PATCH 397/845] Support dry-run option for auto-allocated-topology Add the ability to pass fields with the auto-allocated-topology-show command to support the dry-run validation option of the API. With dry-run the CLI result is "Pass" or the error message from the response. Rename the client binding from show_ to get_. Also provide a client binding for validating the auto-allocation requirements. Partially-Implements: blueprint get-me-a-network DocImpact: Add info about dry-run to the auto-allocate section in the networking guide. Change-Id: Ieba6f3cde23a8a93067b8239b096d5103f6a3128 --- .../neutron/v2_0/auto_allocated_topology.py | 18 +++++++++++++++++- .../tests/unit/test_auto_allocated_topology.py | 14 ++++++++++++++ neutronclient/v2_0/client.py | 6 +++++- 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/neutronclient/neutron/v2_0/auto_allocated_topology.py b/neutronclient/neutron/v2_0/auto_allocated_topology.py index ad96fd988..3f0f58baf 100755 --- a/neutronclient/neutron/v2_0/auto_allocated_topology.py +++ b/neutronclient/neutron/v2_0/auto_allocated_topology.py @@ -19,6 +19,7 @@ from oslo_serialization import jsonutils from neutronclient._i18n import _ +from neutronclient.common import exceptions from neutronclient.neutron import v2_0 @@ -29,6 +30,11 @@ class ShowAutoAllocatedTopology(v2_0.NeutronCommand, show.ShowOne): def get_parser(self, prog_name): parser = super(ShowAutoAllocatedTopology, self).get_parser(prog_name) + parser.add_argument( + '--dry-run', + help=_('Validate the requirements for auto-allocated-topology. ' + '(Does not return a topology.)'), + action='store_true') parser.add_argument( '--tenant-id', metavar='tenant-id', help=_('The owner tenant ID.')) @@ -44,8 +50,16 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.get_client() + extra_values = v2_0.parse_args_to_dict(self.values_specs) + if extra_values: + raise exceptions.CommandError( + _("Invalid argument(s): --%s") % ', --'.join(extra_values)) tenant_id = parsed_args.tenant_id or parsed_args.pos_tenant_id - data = client.show_auto_allocated_topology(tenant_id) + if parsed_args.dry_run: + data = client.validate_auto_allocated_topology_requirements( + tenant_id) + else: + data = client.get_auto_allocated_topology(tenant_id) if self.resource in data: for k, v in data[self.resource].items(): if isinstance(v, list): @@ -58,6 +72,8 @@ def take_action(self, parsed_args): else: value += str(_item) data[self.resource][k] = value + elif v == "dry-run=pass": + return ("dry-run",), ("pass",) elif v is None: data[self.resource][k] = '' return zip(*sorted(data[self.resource].items())) diff --git a/neutronclient/tests/unit/test_auto_allocated_topology.py b/neutronclient/tests/unit/test_auto_allocated_topology.py index b0f8cf0e7..af2742b0e 100755 --- a/neutronclient/tests/unit/test_auto_allocated_topology.py +++ b/neutronclient/tests/unit/test_auto_allocated_topology.py @@ -39,3 +39,17 @@ def test_show_auto_allocated_topology_no_arg(self): cmd = aat.ShowAutoAllocatedTopology(test_cli20.MyApp(sys.stdout), None) args = [] self._test_show_resource(resource, cmd, "None", args) + + def test_show_auto_allocated_topology_dry_run_as_tenant(self): + resource = 'auto_allocated_topology' + cmd = aat.ShowAutoAllocatedTopology(test_cli20.MyApp(sys.stdout), None) + args = ['--dry-run'] + self._test_show_resource(resource, cmd, "None", args, + fields=('dry-run',)) + + def test_show_auto_allocated_topology_dry_run_as_admin(self): + resource = 'auto_allocated_topology' + cmd = aat.ShowAutoAllocatedTopology(test_cli20.MyApp(sys.stdout), None) + args = ['--dry-run', 'some-tenant'] + self._test_show_resource(resource, cmd, "some-tenant", args, + fields=('dry-run',)) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index f25b007e5..71bd50317 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -1902,12 +1902,16 @@ def list_availability_zones(self, retrieve_all=True, **_params): retrieve_all, **_params) @APIParamsCall - def show_auto_allocated_topology(self, tenant_id, **_params): + def get_auto_allocated_topology(self, tenant_id, **_params): """Fetch information about a tenant's auto-allocated topology.""" return self.get( self.auto_allocated_topology_path % tenant_id, params=_params) + def validate_auto_allocated_topology_requirements(self, tenant_id): + """Validate requirements for getting an auto-allocated topology.""" + return self.get_auto_allocated_topology(tenant_id, fields=['dry-run']) + @APIParamsCall def list_bgp_speakers(self, retrieve_all=True, **_params): """Fetches a list of all BGP speakers for a tenant.""" From 32088264e5e65a56ca320d855ee4ee1624b2079f Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Thu, 3 Mar 2016 08:44:02 +0900 Subject: [PATCH 398/845] Update relnote on fix of bug 1450414 https://review.openstack.org/#/c/263337/ is actually a bug fix rather than a new feature. Let's update the release note. Change-Id: I8b912abd95bb9c24edfc95dec26e2cfa7cf547e0 --- .../add-token-endpoint-auth-support-26bf7ee12e4ec833.yaml | 7 ------- .../fix-token-endpoint-auth-support-26bf7ee12e4ec833.yaml | 6 ++++++ 2 files changed, 6 insertions(+), 7 deletions(-) delete mode 100644 releasenotes/notes/add-token-endpoint-auth-support-26bf7ee12e4ec833.yaml create mode 100644 releasenotes/notes/fix-token-endpoint-auth-support-26bf7ee12e4ec833.yaml diff --git a/releasenotes/notes/add-token-endpoint-auth-support-26bf7ee12e4ec833.yaml b/releasenotes/notes/add-token-endpoint-auth-support-26bf7ee12e4ec833.yaml deleted file mode 100644 index d3445cde0..000000000 --- a/releasenotes/notes/add-token-endpoint-auth-support-26bf7ee12e4ec833.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -features: - - | - CLI support for token-endpoint authentication. - - * Allows for authentication via ``--os-token`` and ``--os-url`` options - or the ``OS_TOKEN`` and ``OS_URL`` environment variables, respectively diff --git a/releasenotes/notes/fix-token-endpoint-auth-support-26bf7ee12e4ec833.yaml b/releasenotes/notes/fix-token-endpoint-auth-support-26bf7ee12e4ec833.yaml new file mode 100644 index 000000000..9f8b29040 --- /dev/null +++ b/releasenotes/notes/fix-token-endpoint-auth-support-26bf7ee12e4ec833.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - Fix `bug 1450414 `_ + that authentication with via ``--os-token`` and ``--os-url`` options + (or corresponding environment variables) does not work + after keystone v3 API support. From 8ff7d5cabaa755af13a08c144230b245daa3e39b Mon Sep 17 00:00:00 2001 From: Vic Howard Date: Mon, 7 Dec 2015 11:03:25 -0500 Subject: [PATCH 399/845] Adding DSCP marking changes to neutronclient The following patch implements the DSCP QoS support in neutronclient. This patch also removes some hardcoded values from bandwidth limit rule. Supporting CLI/Network guide docs are located here: I881b8f5bc9024c20275bc56062de72a1c70c8321 Co-Authored-By: Margaret Frances Change-Id: I25ad60c1b9a66e568276a772b8c496987d9f8299 Depends-On: Ic3baefe176df05f049a2e06529c58fd65fe6b419 Partial-Bug: #1468353 --- .../neutron/v2_0/qos/bandwidth_limit_rule.py | 10 +- .../neutron/v2_0/qos/dscp_marking_rule.py | 112 ++++++++++++++ neutronclient/neutron/v2_0/qos/rule.py | 5 + neutronclient/shell.py | 16 ++ .../qos/test_cli20_bandwidth_limit_rule.py | 137 ++++++++++++++++++ .../unit/qos/test_cli20_dscp_marking_rule.py | 91 ++++++++++++ .../tests/unit/qos/test_cli20_rule.py | 116 +-------------- neutronclient/v2_0/client.py | 37 ++++- .../notes/dscp_qos-4a26d3c0363624b0.yaml | 6 + 9 files changed, 409 insertions(+), 121 deletions(-) create mode 100644 neutronclient/neutron/v2_0/qos/dscp_marking_rule.py create mode 100644 neutronclient/tests/unit/qos/test_cli20_bandwidth_limit_rule.py create mode 100644 neutronclient/tests/unit/qos/test_cli20_dscp_marking_rule.py create mode 100644 releasenotes/notes/dscp_qos-4a26d3c0363624b0.yaml diff --git a/neutronclient/neutron/v2_0/qos/bandwidth_limit_rule.py b/neutronclient/neutron/v2_0/qos/bandwidth_limit_rule.py index 9db102f5b..1cb36cd8e 100644 --- a/neutronclient/neutron/v2_0/qos/bandwidth_limit_rule.py +++ b/neutronclient/neutron/v2_0/qos/bandwidth_limit_rule.py @@ -56,7 +56,7 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): body = {} update_bandwidth_limit_args2body(parsed_args, body) - return {'bandwidth_limit_rule': body} + return {self.resource: body} class ListQoSBandwidthLimitRules(qos_rule.QosRuleMixin, @@ -64,16 +64,10 @@ class ListQoSBandwidthLimitRules(qos_rule.QosRuleMixin, """List all qos bandwidth limit rules belonging to the specified policy.""" resource = BANDWIDTH_LIMIT_RULE_RESOURCE - _formatters = {} pagination_support = True sorting_support = True - def args2body(self, parsed_args): - body = {} - qos_rule.update_policy_args2body(parsed_args, body) - return {'qos_rule': body} - class ShowQoSBandwidthLimitRule(qos_rule.QosRuleMixin, neutronv20.ShowCommand): """Show information about the given qos bandwidth limit rule.""" @@ -96,7 +90,7 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): body = {} update_bandwidth_limit_args2body(parsed_args, body) - return {'bandwidth_limit_rule': body} + return {self.resource: body} class DeleteQoSBandwidthLimitRule(qos_rule.QosRuleMixin, diff --git a/neutronclient/neutron/v2_0/qos/dscp_marking_rule.py b/neutronclient/neutron/v2_0/qos/dscp_marking_rule.py new file mode 100644 index 000000000..963c481a7 --- /dev/null +++ b/neutronclient/neutron/v2_0/qos/dscp_marking_rule.py @@ -0,0 +1,112 @@ +# Copyright 2016 Comcast, 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. +# + +from neutronclient._i18n import _ +from neutronclient.common import exceptions +from neutronclient.neutron import v2_0 as neutronv20 +from neutronclient.neutron.v2_0.qos import rule as qos_rule + + +DSCP_MARKING_RESOURCE = 'dscp_marking_rule' +# DSCP DETAILS +# 0 - none | 8 - cs1 | 10 - af11 | 12 - af12 | 14 - af13 | +# 16 - cs2 | 18 - af21 | 20 - af22 | 22 - af23 | 24 - cs3 | +# 26 - af31 | 28 - af32 | 30 - af33 | 32 - cs4 | 34 - af41 | +# 36 - af42 | 38 - af43 | 40 - cs5 | 46 - ef | 48 - cs6 | +# 56 - cs7 + +DSCP_VALID_MARKS = [0, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, + 34, 36, 38, 40, 46, 48, 56] + + +def add_dscp_marking_arguments(parser): + parser.add_argument( + '--dscp-mark', + required=True, + type=str, + help=_('DSCP mark: value can be 0, even numbers from 8-56, \ + excluding 42, 44, 50, 52, and 54.')) + + +def update_dscp_args2body(parsed_args, body): + dscp_mark = parsed_args.dscp_mark + if int(dscp_mark) not in DSCP_VALID_MARKS: + raise exceptions.CommandError(_("DSCP mark: %s not supported. " + "Please note value can either be 0 " + "or any even number from 8-56 " + "excluding 42, 44, 50, 52 and " + "54.") % dscp_mark) + neutronv20.update_dict(parsed_args, body, + ['dscp_mark']) + + +class CreateQoSDscpMarkingRule(qos_rule.QosRuleMixin, + neutronv20.CreateCommand): + """Create a QoS DSCP marking rule.""" + + resource = DSCP_MARKING_RESOURCE + + def add_known_arguments(self, parser): + super(CreateQoSDscpMarkingRule, self).add_known_arguments(parser) + add_dscp_marking_arguments(parser) + + def args2body(self, parsed_args): + body = {} + update_dscp_args2body(parsed_args, body) + return {self.resource: body} + + +class ListQoSDscpMarkingRules(qos_rule.QosRuleMixin, + neutronv20.ListCommand): + """List all QoS DSCP marking rules belonging to the specified policy.""" + + _formatters = {} + pagination_support = True + sorting_support = True + resource = DSCP_MARKING_RESOURCE + + +class ShowQoSDscpMarkingRule(qos_rule.QosRuleMixin, + neutronv20.ShowCommand): + """Show information about the given qos dscp marking rule.""" + + resource = DSCP_MARKING_RESOURCE + allow_names = False + + +class UpdateQoSDscpMarkingRule(qos_rule.QosRuleMixin, + neutronv20.UpdateCommand): + """Update the given QoS DSCP marking rule.""" + + allow_names = False + resource = DSCP_MARKING_RESOURCE + + def add_known_arguments(self, parser): + super(UpdateQoSDscpMarkingRule, self).add_known_arguments(parser) + add_dscp_marking_arguments(parser) + + def args2body(self, parsed_args): + body = {} + update_dscp_args2body(parsed_args, body) + return {self.resource: body} + + +class DeleteQoSDscpMarkingRule(qos_rule.QosRuleMixin, + neutronv20.DeleteCommand): + """Delete a given qos dscp marking rule.""" + + allow_names = False + resource = DSCP_MARKING_RESOURCE diff --git a/neutronclient/neutron/v2_0/qos/rule.py b/neutronclient/neutron/v2_0/qos/rule.py index e4c8d9260..a2fc9a472 100644 --- a/neutronclient/neutron/v2_0/qos/rule.py +++ b/neutronclient/neutron/v2_0/qos/rule.py @@ -48,6 +48,11 @@ def set_extra_attrs(self, parsed_args): self.parent_id = qos_policy.get_qos_policy_id(self.get_client(), parsed_args.policy) + def args2body(self, parsed_args): + body = {} + update_policy_args2body(parsed_args, body) + return {'qos_rule': body} + class ListQoSRuleTypes(neutronv20.ListCommand): """List available qos rule types.""" diff --git a/neutronclient/shell.py b/neutronclient/shell.py index fef1134d1..f6462af0d 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -72,6 +72,7 @@ from neutronclient.neutron.v2_0.nsx import qos_queue from neutronclient.neutron.v2_0 import port from neutronclient.neutron.v2_0.qos import bandwidth_limit_rule +from neutronclient.neutron.v2_0.qos import dscp_marking_rule from neutronclient.neutron.v2_0.qos import policy as qos_policy from neutronclient.neutron.v2_0.qos import rule as qos_rule from neutronclient.neutron.v2_0 import quota @@ -394,6 +395,21 @@ def take_action(self, parsed_args): 'qos-bandwidth-limit-rule-delete': ( bandwidth_limit_rule.DeleteQoSBandwidthLimitRule ), + 'qos-dscp-marking-rule-create': ( + dscp_marking_rule.CreateQoSDscpMarkingRule + ), + 'qos-dscp-marking-rule-show': ( + dscp_marking_rule.ShowQoSDscpMarkingRule + ), + 'qos-dscp-marking-rule-list': ( + dscp_marking_rule.ListQoSDscpMarkingRules + ), + 'qos-dscp-marking-rule-update': ( + dscp_marking_rule.UpdateQoSDscpMarkingRule + ), + 'qos-dscp-marking-rule-delete': ( + dscp_marking_rule.DeleteQoSDscpMarkingRule + ), 'qos-available-rule-types': qos_rule.ListQoSRuleTypes, 'flavor-list': flavor.ListFlavor, 'flavor-show': flavor.ShowFlavor, diff --git a/neutronclient/tests/unit/qos/test_cli20_bandwidth_limit_rule.py b/neutronclient/tests/unit/qos/test_cli20_bandwidth_limit_rule.py new file mode 100644 index 000000000..d1b39c76d --- /dev/null +++ b/neutronclient/tests/unit/qos/test_cli20_bandwidth_limit_rule.py @@ -0,0 +1,137 @@ +# Copyright 2015 Huawei Technologies India Pvt Ltd. +# 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 sys + +from neutronclient.neutron.v2_0.qos import bandwidth_limit_rule as bw_rule +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20QoSBandwidthLimitRuleJSON(test_cli20.CLITestV20Base): + + non_admin_status_resources = ['bandwidth_limit_rule'] + + def setUp(self): + super(CLITestV20QoSBandwidthLimitRuleJSON, self).setUp() + self.res = 'bandwidth_limit_rule' + self.cmd_res = 'qos_bandwidth_limit_rule' + self.ress = self.res + 's' + self.cmd_ress = self.cmd_res + 's' + + def test_create_bandwidth_limit_rule_with_max_kbps(self): + cmd = bw_rule.CreateQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), + None) + my_id = 'my-id' + max_kbps = '1337' + policy_id = 'policy_id' + args = ['--max-kbps', max_kbps, policy_id] + position_names = ['max_kbps'] + position_values = [max_kbps] + self._test_create_resource(self.res, cmd, '', my_id, args, + position_names, position_values, + cmd_resource=self.cmd_res, + parent_id=policy_id) + + def test_create_bandwidth_limit_rule_with_max_burst_kbps(self): + cmd = bw_rule.CreateQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), + None) + my_id = 'my-id' + max_burst_kbps = '1337' + policy_id = 'policy_id' + args = ['--max-burst-kbps', max_burst_kbps, policy_id] + position_names = ['max_burst_kbps'] + position_values = [max_burst_kbps] + self._test_create_resource(self.res, cmd, '', my_id, args, + position_names, position_values, + cmd_resource=self.cmd_res, + parent_id=policy_id) + + def test_create_bandwidth_limit_rule_with_all_params(self): + cmd = bw_rule.CreateQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), + None) + my_id = 'my-id' + max_kbps = '1337' + max_burst_kbps = '1337' + policy_id = 'policy_id' + args = ['--max-kbps', max_kbps, + '--max-burst-kbps', max_burst_kbps, + policy_id] + position_names = ['max_kbps', 'max_burst_kbps'] + position_values = [max_kbps, max_burst_kbps] + self._test_create_resource(self.res, cmd, '', my_id, args, + position_names, position_values, + cmd_resource=self.cmd_res, + parent_id=policy_id) + + def test_update_bandwidth_limit_rule_with_max_kbps(self): + cmd = bw_rule.UpdateQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), + None) + my_id = 'my-id' + max_kbps = '1337' + policy_id = 'policy_id' + args = ['--max-kbps', max_kbps, my_id, policy_id] + self._test_update_resource(self.res, cmd, my_id, args, + {'max_kbps': max_kbps, }, + cmd_resource=self.cmd_res, + parent_id=policy_id) + + def test_update_bandwidth_limit_rule_with_max_burst_kbps(self): + cmd = bw_rule.UpdateQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), + None) + my_id = 'my-id' + max_burst_kbps = '1337' + policy_id = 'policy_id' + args = ['--max-burst-kbps', max_burst_kbps, + my_id, policy_id] + self._test_update_resource(self.res, cmd, my_id, args, + {'max_burst_kbps': max_burst_kbps}, + cmd_resource=self.cmd_res, + parent_id=policy_id) + + def test_update_bandwidth_limit_rule_with_all_params(self): + cmd = bw_rule.UpdateQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), + None) + my_id = 'my-id' + max_kbps = '1337' + max_burst_kbps = '1337' + policy_id = 'policy_id' + args = ['--max-kbps', max_kbps, + '--max-burst-kbps', max_burst_kbps, + my_id, policy_id] + self._test_update_resource(self.res, cmd, my_id, args, + {'max_kbps': max_kbps, + 'max_burst_kbps': max_burst_kbps}, + cmd_resource=self.cmd_res, + parent_id=policy_id) + + def test_delete_bandwidth_limit_rule(self): + cmd = bw_rule.DeleteQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), + None) + my_id = 'my-id' + policy_id = 'policy_id' + args = [my_id, policy_id] + self._test_delete_resource(self.res, cmd, my_id, args, + cmd_resource=self.cmd_res, + parent_id=policy_id) + + def test_show_bandwidth_limit_rule(self): + cmd = bw_rule.ShowQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), + None) + policy_id = 'policy_id' + args = [self.test_id, policy_id] + self._test_show_resource(self.res, cmd, self.test_id, args, + [], cmd_resource=self.cmd_res, + parent_id=policy_id) diff --git a/neutronclient/tests/unit/qos/test_cli20_dscp_marking_rule.py b/neutronclient/tests/unit/qos/test_cli20_dscp_marking_rule.py new file mode 100644 index 000000000..74c941618 --- /dev/null +++ b/neutronclient/tests/unit/qos/test_cli20_dscp_marking_rule.py @@ -0,0 +1,91 @@ +# Copyright 2016 Comcast 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 sys + +from neutronclient.common import exceptions +from neutronclient.neutron.v2_0.qos import dscp_marking_rule as dscp_rule +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20QoSDscpMarkingRuleJSON(test_cli20.CLITestV20Base): + + non_admin_status_resources = ['dscp_marking_rule'] + + def setUp(self): + super(CLITestV20QoSDscpMarkingRuleJSON, self).setUp() + self.dscp_res = 'dscp_marking_rule' + self.dscp_cmd_res = 'qos_dscp_marking_rule' + self.dscp_ress = self.dscp_res + 's' + self.dscp_cmd_ress = self.dscp_cmd_res + 's' + + def test_create_dscp_marking_rule_with_dscp_mark(self): + cmd = dscp_rule.CreateQoSDscpMarkingRule(test_cli20.MyApp(sys.stdout), + None) + my_id = 'my-id' + policy_id = 'policy_id' + position_names = ['dscp_mark'] + valid_dscp_marks = ['0', '56'] + invalid_dscp_marks = ['-1', '19', '42', '44', '57', '58'] + for dscp_mark in valid_dscp_marks: + args = ['--dscp-mark', dscp_mark, policy_id] + position_values = [dscp_mark] + self._test_create_resource(self.dscp_res, cmd, '', my_id, args, + position_names, position_values, + cmd_resource=self.dscp_cmd_res, + parent_id=policy_id) + for dscp_mark in invalid_dscp_marks: + args = ['--dscp-mark', dscp_mark, policy_id] + position_values = [dscp_mark] + self._test_create_resource( + self.dscp_res, cmd, '', my_id, args, + position_names, position_values, + cmd_resource=self.dscp_cmd_res, + parent_id=policy_id, + no_api_call=True, + expected_exception=exceptions.CommandError) + + def test_update_dscp_marking_rule_with_dscp_mark(self): + cmd = dscp_rule.UpdateQoSDscpMarkingRule(test_cli20.MyApp(sys.stdout), + None) + my_id = 'my-id' + dscp_mark = '56' + policy_id = 'policy_id' + args = ['--dscp-mark', dscp_mark, + my_id, policy_id] + self._test_update_resource(self.dscp_res, cmd, my_id, args, + {'dscp_mark': dscp_mark}, + cmd_resource=self.dscp_cmd_res, + parent_id=policy_id) + + def test_delete_dscp_marking_rule(self): + cmd = dscp_rule.DeleteQoSDscpMarkingRule(test_cli20.MyApp(sys.stdout), + None) + my_id = 'my-id' + policy_id = 'policy_id' + args = [my_id, policy_id] + self._test_delete_resource(self.dscp_res, cmd, my_id, args, + cmd_resource=self.dscp_cmd_res, + parent_id=policy_id) + + def test_show_dscp_marking_rule(self): + cmd = dscp_rule.ShowQoSDscpMarkingRule(test_cli20.MyApp(sys.stdout), + None) + policy_id = 'policy_id' + args = [self.test_id, policy_id] + self._test_show_resource(self.dscp_res, cmd, self.test_id, args, + [], cmd_resource=self.dscp_cmd_res, + parent_id=policy_id) diff --git a/neutronclient/tests/unit/qos/test_cli20_rule.py b/neutronclient/tests/unit/qos/test_cli20_rule.py index fcc29b6e0..bdbf52df9 100644 --- a/neutronclient/tests/unit/qos/test_cli20_rule.py +++ b/neutronclient/tests/unit/qos/test_cli20_rule.py @@ -16,132 +16,24 @@ import sys -from neutronclient.neutron.v2_0.qos import bandwidth_limit_rule as bw_rule from neutronclient.neutron.v2_0.qos import rule as qos_rule from neutronclient.tests.unit import test_cli20 class CLITestV20QoSRuleJSON(test_cli20.CLITestV20Base): - non_admin_status_resources = ['bandwidth_limit_rule'] + non_admin_status_resources = ['bandwidth_limit_rule', 'dscp_marking_rule'] def setUp(self): super(CLITestV20QoSRuleJSON, self).setUp() - self.res = 'bandwidth_limit_rule' - self.cmd_res = 'qos_bandwidth_limit_rule' - self.ress = 'bandwidth_limit_rules' - self.cmd_ress = 'qos_bandwidth_limit_rules' - - def test_create_bandwidth_limit_rule_with_max_kbps(self): - cmd = bw_rule.CreateQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - max_kbps = '1337' - policy_id = 'policy_id' - args = ['--max-kbps', max_kbps, policy_id] - position_names = ['max_kbps'] - position_values = [max_kbps] - self._test_create_resource(self.res, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=self.cmd_res, - parent_id=policy_id) - - def test_create_bandwidth_limit_rule_with_max_burst_kbps(self): - cmd = bw_rule.CreateQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - max_burst_kbps = '1337' - policy_id = 'policy_id' - args = ['--max-burst-kbps', max_burst_kbps, policy_id] - position_names = ['max_burst_kbps'] - position_values = [max_burst_kbps] - self._test_create_resource(self.res, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=self.cmd_res, - parent_id=policy_id) - - def test_create_bandwidth_limit_rule_with_all_params(self): - cmd = bw_rule.CreateQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - max_kbps = '1337' - max_burst_kbps = '1337' - policy_id = 'policy_id' - args = ['--max-kbps', max_kbps, - '--max-burst-kbps', max_burst_kbps, - policy_id] - position_names = ['max_kbps', 'max_burst_kbps'] - position_values = [max_kbps, max_burst_kbps] - self._test_create_resource(self.res, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=self.cmd_res, - parent_id=policy_id) - - def test_update_bandwidth_limit_rule_with_max_kbps(self): - cmd = bw_rule.UpdateQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - max_kbps = '1337' - policy_id = 'policy_id' - args = ['--max-kbps', max_kbps, my_id, policy_id] - self._test_update_resource(self.res, cmd, my_id, args, - {'max_kbps': max_kbps, }, - cmd_resource=self.cmd_res, - parent_id=policy_id) - - def test_update_bandwidth_limit_rule_with_max_burst_kbps(self): - cmd = bw_rule.UpdateQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - max_burst_kbps = '1337' - policy_id = 'policy_id' - args = ['--max-burst-kbps', max_burst_kbps, - my_id, policy_id] - self._test_update_resource(self.res, cmd, my_id, args, - {'max_burst_kbps': max_burst_kbps}, - cmd_resource=self.cmd_res, - parent_id=policy_id) - - def test_update_bandwidth_limit_rule_with_all_params(self): - cmd = bw_rule.UpdateQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - max_kbps = '1337' - max_burst_kbps = '1337' - policy_id = 'policy_id' - args = ['--max-kbps', max_kbps, - '--max-burst-kbps', max_burst_kbps, - my_id, policy_id] - self._test_update_resource(self.res, cmd, my_id, args, - {'max_kbps': max_kbps, - 'max_burst_kbps': max_burst_kbps}, - cmd_resource=self.cmd_res, - parent_id=policy_id) - - def test_delete_bandwidth_limit_rule(self): - cmd = bw_rule.DeleteQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - policy_id = 'policy_id' - args = [my_id, policy_id] - self._test_delete_resource(self.res, cmd, my_id, args, - cmd_resource=self.cmd_res, - parent_id=policy_id) - - def test_show_bandwidth_limit_rule(self): - cmd = bw_rule.ShowQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), - None) - policy_id = 'policy_id' - args = [self.test_id, policy_id] - self._test_show_resource(self.res, cmd, self.test_id, args, - [], cmd_resource=self.cmd_res, - parent_id=policy_id) def test_list_qos_rule_types(self): # qos_rule_types. resources = 'rule_types' cmd_resources = 'qos_rule_types' - response_contents = [{'type': 'bandwidth_limit'}] + response_contents = [{'type': 'bandwidth_limit', + 'type': 'dscp_marking'}] + cmd = qos_rule.ListQoSRuleTypes(test_cli20.MyApp(sys.stdout), None) self._test_list_resources(resources, cmd, True, diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 6db75370f..f37004402 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -508,6 +508,8 @@ class Client(ClientBase): qos_policy_path = "/qos/policies/%s" qos_bandwidth_limit_rules_path = "/qos/policies/%s/bandwidth_limit_rules" qos_bandwidth_limit_rule_path = "/qos/policies/%s/bandwidth_limit_rules/%s" + qos_dscp_marking_rules_path = "/qos/policies/%s/dscp_marking_rules" + qos_dscp_marking_rule_path = "/qos/policies/%s/dscp_marking_rules/%s" qos_rule_types_path = "/qos/rule-types" qos_rule_type_path = "/qos/rule-types/%s" flavors_path = "/flavors" @@ -565,6 +567,7 @@ class Client(ClientBase): 'policies': 'policy', 'bandwidth_limit_rules': 'bandwidth_limit_rule', 'rules': 'rule', + 'dscp_marking_rules': 'dscp_marking_rule', 'rule_types': 'rule_type', 'flavors': 'flavor', 'bgp_speakers': 'bgp_speaker', @@ -1795,7 +1798,7 @@ def list_qos_rule_types(self, retrieve_all=True, **_params): @APIParamsCall def list_bandwidth_limit_rules(self, policy_id, retrieve_all=True, **_params): - """Fetches a list of all qos rules for the given policy.""" + """Fetches a list of all bandwidth limit rules for the given policy.""" return self.list('bandwidth_limit_rules', self.qos_bandwidth_limit_rules_path % policy_id, retrieve_all, **_params) @@ -1824,6 +1827,38 @@ def delete_bandwidth_limit_rule(self, rule, policy): return self.delete(self.qos_bandwidth_limit_rule_path % (policy, rule)) + @APIParamsCall + def list_dscp_marking_rules(self, policy_id, + retrieve_all=True, **_params): + """Fetches a list of all DSCP marking rules for the given policy.""" + return self.list('dscp_marking_rules', + self.qos_dscp_marking_rules_path % policy_id, + retrieve_all, **_params) + + @APIParamsCall + def show_dscp_marking_rule(self, rule, policy, body=None): + """Shows information of a certain DSCP marking rule.""" + return self.get(self.qos_dscp_marking_rule_path % + (policy, rule), body=body) + + @APIParamsCall + def create_dscp_marking_rule(self, policy, body=None): + """Creates a new DSCP marking rule.""" + return self.post(self.qos_dscp_marking_rules_path % policy, + body=body) + + @APIParamsCall + def update_dscp_marking_rule(self, rule, policy, body=None): + """Updates a DSCP marking rule.""" + return self.put(self.qos_dscp_marking_rule_path % + (policy, rule), body=body) + + @APIParamsCall + def delete_dscp_marking_rule(self, rule, policy): + """Deletes a DSCP marking rule.""" + return self.delete(self.qos_dscp_marking_rule_path % + (policy, rule)) + @APIParamsCall def create_flavor(self, body=None): """Creates a new Neutron service flavor.""" diff --git a/releasenotes/notes/dscp_qos-4a26d3c0363624b0.yaml b/releasenotes/notes/dscp_qos-4a26d3c0363624b0.yaml new file mode 100644 index 000000000..37770cd51 --- /dev/null +++ b/releasenotes/notes/dscp_qos-4a26d3c0363624b0.yaml @@ -0,0 +1,6 @@ +--- +prelude: > + Adding new QoS DSCP marking rule commands. +features: + - New create, update, list, show, and delete commands are added for QoS + DSCP marking rule functionality. From 3d1ea8bb16b243871a739b1004bb1663855fe3c3 Mon Sep 17 00:00:00 2001 From: Rabi Mishra Date: Fri, 4 Mar 2016 13:48:15 +0530 Subject: [PATCH 400/845] Fix TypeError with error message neutronclient throws type error when dealing with i18n messages. This patch fixes it. Change-Id: I08190eb7f6c54f0d44c671b1806423bba22ac0bf Closes-Bug: #1552760 --- neutronclient/common/exceptions.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/neutronclient/common/exceptions.py b/neutronclient/common/exceptions.py index 6aff6d644..2e4d679e6 100644 --- a/neutronclient/common/exceptions.py +++ b/neutronclient/common/exceptions.py @@ -70,7 +70,8 @@ def __init__(self, message=None, **kwargs): if self.request_ids: req_ids_msg = self.req_ids_msg % self.request_ids if message: - message += '\n' + req_ids_msg + message = _('%(msg)s\n%(id)s') % {'msg': message, + 'id': req_ids_msg} else: message = req_ids_msg super(NeutronClientException, self).__init__(message, **kwargs) From 8fe5386a006992bdf606872d5e8ad57e60786c50 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Fri, 4 Mar 2016 19:39:53 +0900 Subject: [PATCH 401/845] Add release note of critial TypeError fix This is a follow-up patch of https://review.openstack.org/#/c/288301 Related-Bug: #1552760 Change-Id: Ie5baf6df82db6b5d25bd4125332d8c8edfec9797 --- .../fix-exception-typeerror-4.1.0-b37d738146575ed5.yaml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 releasenotes/notes/fix-exception-typeerror-4.1.0-b37d738146575ed5.yaml diff --git a/releasenotes/notes/fix-exception-typeerror-4.1.0-b37d738146575ed5.yaml b/releasenotes/notes/fix-exception-typeerror-4.1.0-b37d738146575ed5.yaml new file mode 100644 index 000000000..a21eebbeb --- /dev/null +++ b/releasenotes/notes/fix-exception-typeerror-4.1.0-b37d738146575ed5.yaml @@ -0,0 +1,5 @@ +--- +critical: + - Fix a critical bug that when lazy translation is enabled + NeutronClientException raises a TypeError + (`bug 1552760 `_). From 5d28651f9b482de1ab3081b3b828eb3781f2fd3a Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Fri, 4 Mar 2016 14:22:30 +0000 Subject: [PATCH 402/845] Updated from global requirements Change-Id: If21c3be96b39240ce2d61be75633f6f84d7d35f5 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c1142eb00..cc6fd9dc8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. pbr>=1.6 # Apache-2.0 -cliff!=1.16.0,>=1.15.0 # Apache-2.0 +cliff!=1.16.0,!=1.17.0,>=1.15.0 # Apache-2.0 debtcollector>=1.2.0 # Apache-2.0 iso8601>=0.1.9 # MIT netaddr!=0.7.16,>=0.7.12 # BSD From 7bf8f229bba9762b188ae3517b9ef810269c671e Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Thu, 10 Dec 2015 04:57:11 +0900 Subject: [PATCH 403/845] Use raw values when non-table formatter is used neutron CLI supports various output formats such as json, yaml (e.g. neutron net-list -f json), but the current output formattings is optimized to the table formatter to get human-friendly output in normal CLI use (For example, new lines are inserted). However, this kind of formatting is unnecessary when other output formatters are used and prevents users from getting proper data. This commit changes to skip format_output_data() method when output formatters other than 'table' are used. Now we have proper outputs in JSON, YAML or other formats. In CreateCommand and ShowCommand, cleanup_output_data() is moved to each command class because it should be called regardless of a formatter type. In ListCommand, value formatter for CSV is still supported for backward compatiblity, but the value formatter for fixed_ips of the port resource is removed because it brings no much value in CSV output. Closes-Bug: #1524624 Change-Id: I2668fa90402d7119db69f09a20fb7c7270c9616e --- neutronclient/neutron/v2_0/__init__.py | 18 ++++-- neutronclient/neutron/v2_0/port.py | 8 --- neutronclient/tests/unit/test_cli20.py | 86 ++++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 13 deletions(-) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index 05402f1f7..604f876f3 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -429,7 +429,6 @@ def cleanup_output_data(self, data): pass def format_output_data(self, data): - self.cleanup_output_data(data) # Modify data to make it more readable if self.resource in data: for k, v in six.iteritems(data[self.resource]): @@ -484,7 +483,9 @@ def take_action(self, parsed_args): data = obj_creator(self.parent_id, body) else: data = obj_creator(body) - self.format_output_data(data) + self.cleanup_output_data(data) + if parsed_args.formatter == 'table': + self.format_output_data(data) info = self.resource in data and data[self.resource] or None if info: if parsed_args.formatter == 'table': @@ -754,9 +755,14 @@ def setup_columns(self, info, parsed_args): # Also Keep their order the same as in list_columns _columns = [x for x in self.list_columns if x in _columns] - formatters = self._formatters - if hasattr(self, '_formatters_csv') and parsed_args.formatter == 'csv': + if parsed_args.formatter == 'table': + formatters = self._formatters + elif (parsed_args.formatter == 'csv' + and hasattr(self, '_formatters_csv')): formatters = self._formatters_csv + else: + # For other formatters, we use raw value returned from neutron + formatters = {} return (_columns, (utils.get_item_properties( s, _columns, formatters=formatters, ) @@ -814,7 +820,9 @@ def take_action(self, parsed_args): data = obj_shower(_id, self.parent_id, **params) else: data = obj_shower(_id, **params) - self.format_output_data(data) + self.cleanup_output_data(data) + if parsed_args.formatter == 'table': + self.format_output_data(data) resource = data[self.resource] if self.resource in data: return zip(*sorted(six.iteritems(resource))) diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index f4917292d..050ec6512 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -33,13 +33,6 @@ def _format_fixed_ips(port): return '' -def _format_fixed_ips_csv(port): - try: - return jsonutils.dumps(port['fixed_ips']) - except (TypeError, KeyError): - return '' - - def _add_updatable_args(parser): parser.add_argument( '--name', @@ -90,7 +83,6 @@ class ListPort(neutronV20.ListCommand): resource = 'port' _formatters = {'fixed_ips': _format_fixed_ips, } - _formatters_csv = {'fixed_ips': _format_fixed_ips_csv, } list_columns = ['id', 'name', 'mac_address', 'fixed_ips'] pagination_support = True sorting_support = True diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 5407e7bed..77dde8b62 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -16,6 +16,7 @@ import contextlib import itertools +import json import sys import fixtures @@ -25,11 +26,13 @@ import requests import six import six.moves.urllib.parse as urlparse +import yaml from neutronclient.common import constants from neutronclient.common import exceptions from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronV2_0 +from neutronclient.neutron.v2_0 import network from neutronclient import shell from neutronclient.v2_0 import client @@ -1020,3 +1023,86 @@ def test_generator(self): self.assertTrue(hasattr(obj, 'request_ids')) self.assertEqual([REQUEST_ID], obj.request_ids) + + +class CLITestV20OutputFormatter(CLITestV20Base): + + def _test_create_resource_with_formatter(self, fmt): + resource = 'network' + cmd = network.CreateNetwork(MyApp(sys.stdout), None) + args = ['-f', fmt, 'myname'] + position_names = ['name'] + position_values = ['myname'] + self._test_create_resource(resource, cmd, 'myname', 'myid', args, + position_names, position_values) + + def test_create_resource_table(self): + self._test_create_resource_with_formatter('table') + print(self.fake_stdout.content) + # table data is contains in the third element. + data = self.fake_stdout.content[2].split('\n') + self.assertTrue(any(' id ' in d for d in data)) + self.assertTrue(any(' name ' in d for d in data)) + + def test_create_resource_json(self): + self._test_create_resource_with_formatter('json') + data = json.loads(self.fake_stdout.make_string()) + self.assertEqual('myname', data['name']) + self.assertEqual('myid', data['id']) + + def test_create_resource_yaml(self): + self._test_create_resource_with_formatter('yaml') + data = yaml.load(self.fake_stdout.make_string()) + self.assertEqual('myname', data['name']) + self.assertEqual('myid', data['id']) + + def _test_show_resource_with_formatter(self, fmt): + resource = 'network' + cmd = network.ShowNetwork(MyApp(sys.stdout), None) + args = ['-f', fmt, '-F', 'id', '-F', 'name', 'myid'] + self._test_show_resource(resource, cmd, 'myid', + args, ['id', 'name']) + + def test_show_resource_table(self): + self._test_show_resource_with_formatter('table') + data = self.fake_stdout.content[0].split('\n') + self.assertTrue(any(' id ' in d for d in data)) + self.assertTrue(any(' name ' in d for d in data)) + + def test_show_resource_json(self): + self._test_show_resource_with_formatter('json') + data = json.loads(''.join(self.fake_stdout.content)) + self.assertEqual('myname', data['name']) + self.assertEqual('myid', data['id']) + + def test_show_resource_yaml(self): + self._test_show_resource_with_formatter('yaml') + data = yaml.load(''.join(self.fake_stdout.content)) + self.assertEqual('myname', data['name']) + self.assertEqual('myid', data['id']) + + def _test_list_resources_with_formatter(self, fmt): + resources = 'networks' + cmd = network.ListNetwork(MyApp(sys.stdout), None) + # ListNetwork has its own extend_list, so we need to stub out it + # to avoid an extra API call. + self.mox.StubOutWithMock(network.ListNetwork, "extend_list") + network.ListNetwork.extend_list(mox.IsA(list), mox.IgnoreArg()) + self._test_list_resources(resources, cmd, output_format=fmt) + + def test_list_resources_table(self): + self._test_list_resources_with_formatter('table') + data = self.fake_stdout.content[0].split('\n') + self.assertTrue(any(' id ' in d for d in data)) + self.assertTrue(any(' myid1 ' in d for d in data)) + self.assertTrue(any(' myid2 ' in d for d in data)) + + def test_list_resources_json(self): + self._test_list_resources_with_formatter('json') + data = json.loads(''.join(self.fake_stdout.content)) + self.assertEqual(['myid1', 'myid2'], [d['id'] for d in data]) + + def test_list_resources_yaml(self): + self._test_list_resources_with_formatter('yaml') + data = yaml.load(''.join(self.fake_stdout.content)) + self.assertEqual(['myid1', 'myid2'], [d['id'] for d in data]) From 2db432fbf5be5c307b82117200649379859450e5 Mon Sep 17 00:00:00 2001 From: Kevin Benton Date: Wed, 2 Mar 2016 17:29:18 -0800 Subject: [PATCH 404/845] Add parser options for description on resources This adds the description field to the parsers for all of the resources that gained description fields in the dependent patch. Change-Id: I939b517c5320ea9dd3f387c12adee1ed8876adec Related-Bug: #1483480 Depends-On: I6e1ef53d7aae7d04a5485810cc1db0a8eb125953 --- neutronclient/neutron/v2_0/floatingip.py | 5 ++++- neutronclient/neutron/v2_0/network.py | 6 +++++- neutronclient/neutron/v2_0/port.py | 6 +++++- neutronclient/neutron/v2_0/router.py | 11 ++++++++-- neutronclient/neutron/v2_0/securitygroup.py | 6 +++++- neutronclient/neutron/v2_0/subnet.py | 6 +++++- neutronclient/neutron/v2_0/subnetpool.py | 6 +++++- .../tests/unit/test_cli20_floatingips.py | 5 +++-- .../tests/unit/test_cli20_network.py | 21 ++++++++++++++++--- neutronclient/tests/unit/test_cli20_port.py | 7 +++++-- neutronclient/tests/unit/test_cli20_router.py | 11 +++++----- .../tests/unit/test_cli20_securitygroup.py | 5 +++-- neutronclient/tests/unit/test_cli20_subnet.py | 10 +++++---- .../tests/unit/test_cli20_subnetpool.py | 11 +++++----- 14 files changed, 85 insertions(+), 31 deletions(-) diff --git a/neutronclient/neutron/v2_0/floatingip.py b/neutronclient/neutron/v2_0/floatingip.py index 78c0dd3f8..b6a0ebaa4 100644 --- a/neutronclient/neutron/v2_0/floatingip.py +++ b/neutronclient/neutron/v2_0/floatingip.py @@ -49,6 +49,9 @@ def add_known_arguments(self, parser): parser.add_argument( 'floating_network_id', metavar='FLOATING_NETWORK', help=_('Network name or ID to allocate floating IP from.')) + parser.add_argument( + '--description', + help=_('Description of the floating IP.')) parser.add_argument( '--port-id', help=_('ID of the port to be associated with the floating IP.')) @@ -78,7 +81,7 @@ def args2body(self, parsed_args): body = {'floating_network_id': _network_id} neutronV20.update_dict(parsed_args, body, ['port_id', 'tenant_id', - 'fixed_ip_address', + 'fixed_ip_address', 'description', 'floating_ip_address', 'subnet_id']) dns.args2body_dns_create(parsed_args, body, 'domain') dns.args2body_dns_create(parsed_args, body, 'name') diff --git a/neutronclient/neutron/v2_0/network.py b/neutronclient/neutron/v2_0/network.py index 4ec7ace8e..23362e12e 100644 --- a/neutronclient/neutron/v2_0/network.py +++ b/neutronclient/neutron/v2_0/network.py @@ -184,6 +184,9 @@ def add_known_arguments(self, parser): parser.add_argument( 'name', metavar='NAME', help=_('Name of network to create.')) + parser.add_argument( + '--description', + help=_('Description of network.')) self.add_arguments_qos_policy(parser) availability_zone.add_az_hint_argument(parser, self.resource) @@ -197,7 +200,8 @@ def args2body(self, parsed_args): 'vlan_transparent', 'provider:network_type', 'provider:physical_network', - 'provider:segmentation_id']) + 'provider:segmentation_id', + 'description']) self.args2body_qos_policy(parsed_args, body) availability_zone.args2body_az_hint(parsed_args, body) diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index f4917292d..b68bfbe78 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -44,6 +44,9 @@ def _add_updatable_args(parser): parser.add_argument( '--name', help=_('Name of this port.')) + parser.add_argument( + '--description', + help=_('Description of this port.')) parser.add_argument( '--fixed-ip', metavar='subnet_id=SUBNET,ip_address=IP_ADDR', action='append', @@ -71,7 +74,8 @@ def _add_updatable_args(parser): def _updatable_args2body(parsed_args, body, client): neutronV20.update_dict(parsed_args, body, - ['device_id', 'device_owner', 'name']) + ['device_id', 'device_owner', 'name', + 'description']) ips = [] if parsed_args.fixed_ip: for ip_spec in parsed_args.fixed_ip: diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index 5eba1f20d..605efc8a5 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -68,6 +68,9 @@ def add_known_arguments(self, parser): parser.add_argument( 'name', metavar='NAME', help=_('Name of router to create.')) + parser.add_argument( + '--description', + help=_('Description of router.')) utils.add_boolean_argument( parser, '--distributed', dest='distributed', help=_('Create a distributed router.')) @@ -80,7 +83,8 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): body = {'admin_state_up': parsed_args.admin_state} neutronV20.update_dict(parsed_args, body, - ['name', 'tenant_id', 'distributed', 'ha']) + ['name', 'tenant_id', 'distributed', 'ha', + 'description']) availability_zone.args2body_az_hint(parsed_args, body) return {self.resource: body} @@ -100,6 +104,9 @@ def add_known_arguments(self, parser): parser.add_argument( '--name', help=_('Name of this router.')) + parser.add_argument( + '--description', + help=_('Description of router.')) utils.add_boolean_argument( parser, '--admin-state-up', dest='admin_state', help=_('Specify the administrative state of the router' @@ -128,7 +135,7 @@ def args2body(self, parsed_args): if hasattr(parsed_args, 'admin_state'): body['admin_state_up'] = parsed_args.admin_state neutronV20.update_dict(parsed_args, body, - ['name', 'distributed']) + ['name', 'distributed', 'description']) if parsed_args.no_routes: body['routes'] = None elif parsed_args.routes: diff --git a/neutronclient/neutron/v2_0/securitygroup.py b/neutronclient/neutron/v2_0/securitygroup.py index 0e1f6d283..b558504bf 100644 --- a/neutronclient/neutron/v2_0/securitygroup.py +++ b/neutronclient/neutron/v2_0/securitygroup.py @@ -305,6 +305,9 @@ class CreateSecurityGroupRule(neutronV20.CreateCommand): resource = 'security_group_rule' def add_known_arguments(self, parser): + parser.add_argument( + '--description', + help=_('Description of security group rule.')) parser.add_argument( 'security_group_id', metavar='SECURITY_GROUP', help=_('Security group name or ID to add rule.')) @@ -354,7 +357,8 @@ def args2body(self, parsed_args): generate_default_ethertype(parsed_args.protocol)} neutronV20.update_dict(parsed_args, body, ['protocol', 'port_range_min', 'port_range_max', - 'remote_ip_prefix', 'tenant_id']) + 'remote_ip_prefix', 'tenant_id', + 'description']) if parsed_args.remote_group_id: _remote_group_id = neutronV20.find_resourceid_by_name_or_id( self.get_client(), 'security_group', diff --git a/neutronclient/neutron/v2_0/subnet.py b/neutronclient/neutron/v2_0/subnet.py index 2149ebf43..3f27cb77b 100644 --- a/neutronclient/neutron/v2_0/subnet.py +++ b/neutronclient/neutron/v2_0/subnet.py @@ -52,6 +52,9 @@ def add_updatable_arguments(parser): parser.add_argument( '--name', help=_('Name of this subnet.')) + parser.add_argument( + '--description', + help=_('Description of this subnet.')) gateway_sg = parser.add_mutually_exclusive_group() gateway_sg.add_argument( '--gateway', metavar='GATEWAY_IP', @@ -112,7 +115,8 @@ def updatable_args2body(parsed_args, body, for_create=True, ip_version=None): neutronV20.update_dict(parsed_args, body, ['name', 'allocation_pools', - 'host_routes', 'dns_nameservers']) + 'host_routes', 'dns_nameservers', + 'description']) if parsed_args.no_gateway: body['gateway_ip'] = None elif parsed_args.gateway: diff --git a/neutronclient/neutron/v2_0/subnetpool.py b/neutronclient/neutron/v2_0/subnetpool.py index a4a224753..138c5215a 100644 --- a/neutronclient/neutron/v2_0/subnetpool.py +++ b/neutronclient/neutron/v2_0/subnetpool.py @@ -27,6 +27,9 @@ def _format_prefixes(subnetpool): def add_updatable_arguments(parser): + parser.add_argument( + '--description', + help=_('Description of subnetpool.')) parser.add_argument( '--min-prefixlen', type=int, help=_('Subnetpool minimum prefix length.')) @@ -49,7 +52,8 @@ def add_updatable_arguments(parser): def updatable_args2body(parsed_args, body, for_create=True): neutronV20.update_dict(parsed_args, body, ['name', 'prefixes', 'default_prefixlen', - 'min_prefixlen', 'max_prefixlen', 'is_default']) + 'min_prefixlen', 'max_prefixlen', 'is_default', + 'description']) class ListSubnetPool(neutronV20.ListCommand): diff --git a/neutronclient/tests/unit/test_cli20_floatingips.py b/neutronclient/tests/unit/test_cli20_floatingips.py index cca3706ce..9a1c39863 100644 --- a/neutronclient/tests/unit/test_cli20_floatingips.py +++ b/neutronclient/tests/unit/test_cli20_floatingips.py @@ -30,11 +30,12 @@ def test_create_floatingip(self): cmd = fip.CreateFloatingIP(test_cli20.MyApp(sys.stdout), None) name = 'fip1' myid = 'myid' - args = [name] + args = [name, '--description', 'floats like a butterfly'] position_names = ['floating_network_id'] position_values = [name] self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) + position_names, position_values, + description='floats like a butterfly') def test_create_floatingip_and_port(self): # Create floatingip: fip1. diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index dc8fd72ae..f900d7548 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -53,6 +53,19 @@ def test_create_network_with_unicode(self): self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) + def test_create_network_description(self): + # Create net: --tenant_id tenantid myname. + resource = 'network' + cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + args = ['--description', 'Nice network', name] + position_names = ['name', ] + position_values = [name, ] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values, + description='Nice network') + def test_create_network_tenant(self): # Create net: --tenant_id tenantid myname. resource = 'network' @@ -510,9 +523,11 @@ def test_update_network(self): cmd = network.UpdateNetwork(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', ['myid', '--name', 'myname', - '--tags', 'a', 'b'], - {'name': 'myname', 'tags': ['a', 'b'], } - ) + '--tags', 'a', 'b', '--description', + 'This network takes the scenic route'], + {'name': 'myname', 'tags': ['a', 'b'], + 'description': 'This network takes the ' + 'scenic route'}) def test_update_network_with_unicode(self): # Update net: myid --name u'\u7f51\u7edc' --tags a b. diff --git a/neutronclient/tests/unit/test_cli20_port.py b/neutronclient/tests/unit/test_cli20_port.py index dd2ce6b63..50eaffbdb 100644 --- a/neutronclient/tests/unit/test_cli20_port.py +++ b/neutronclient/tests/unit/test_cli20_port.py @@ -35,12 +35,13 @@ def test_create_port(self): name = 'myname' myid = 'myid' netid = 'netid' - args = [netid] + args = [netid, '--description', 'DESC'] position_names = ['network_id'] position_values = [] position_values.extend([netid]) self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) + position_names, position_values, + description='DESC') def test_create_port_extra_dhcp_opts_args(self): # Create port: netid --extra_dhcp_opt. @@ -555,9 +556,11 @@ def test_update_port(self): self._test_update_resource(resource, cmd, 'myid', ['myid', '--name', 'myname', '--admin-state-up', 'False', + '--description', 'garbage', '--tags', 'a', 'b'], {'name': 'myname', 'admin_state_up': 'False', + 'description': 'garbage', 'tags': ['a', 'b'], }) def test_update_port_secgroup(self): diff --git a/neutronclient/tests/unit/test_cli20_router.py b/neutronclient/tests/unit/test_cli20_router.py index 66372f861..d93c46420 100644 --- a/neutronclient/tests/unit/test_cli20_router.py +++ b/neutronclient/tests/unit/test_cli20_router.py @@ -28,11 +28,12 @@ def test_create_router(self): cmd = router.CreateRouter(test_cli20.MyApp(sys.stdout), None) name = 'router1' myid = 'myid' - args = [name, ] + args = [name, '--description', 'rooter'] position_names = ['name', ] position_values = [name, ] self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) + position_names, position_values, + description='rooter') def test_create_router_tenant(self): # Create router: --tenant_id tenantid myname. @@ -163,9 +164,9 @@ def test_update_router(self): resource = 'router' cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'myname'], - {'name': 'myname'} - ) + ['myid', '--name', 'myname', + '--description', ':D'], + {'name': 'myname', 'description': ':D'}) def test_update_router_admin_state(self): # Update router: myid --admin-state-up . diff --git a/neutronclient/tests/unit/test_cli20_securitygroup.py b/neutronclient/tests/unit/test_cli20_securitygroup.py index 07c6a3604..3bd62edb3 100644 --- a/neutronclient/tests/unit/test_cli20_securitygroup.py +++ b/neutronclient/tests/unit/test_cli20_securitygroup.py @@ -164,7 +164,7 @@ def test_create_security_group_rule_full(self): direction, '--ethertype', ethertype, '--protocol', protocol, '--port_range_min', port_range_min, '--port_range_max', port_range_max, '--remote_group_id', remote_group_id, - security_group_id] + security_group_id, '--description', 'PCI policy 1421912'] position_names = ['remote_ip_prefix', 'direction', 'ethertype', 'protocol', 'port_range_min', 'port_range_max', 'remote_group_id', 'security_group_id'] @@ -172,7 +172,8 @@ def test_create_security_group_rule_full(self): port_range_min, port_range_max, remote_group_id, security_group_id] self._test_create_resource(resource, cmd, None, myid, args, - position_names, position_values) + position_names, position_values, + description='PCI policy 1421912') def test_create_security_group_rule_with_integer_protocol_value(self): resource = 'security_group_rule' diff --git a/neutronclient/tests/unit/test_cli20_subnet.py b/neutronclient/tests/unit/test_cli20_subnet.py index a1ebf8952..25a771e10 100644 --- a/neutronclient/tests/unit/test_cli20_subnet.py +++ b/neutronclient/tests/unit/test_cli20_subnet.py @@ -40,11 +40,12 @@ def test_create_subnet(self): netid = 'netid' cidr = '10.10.10.0/24' gateway = 'gatewayvalue' - args = ['--gateway', gateway, netid, cidr] + args = ['--gateway', gateway, netid, cidr, '--description', 'cave'] position_names = ['ip_version', 'network_id', 'cidr', 'gateway_ip'] position_values = [4, netid, cidr, gateway] self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) + position_names, position_values, + description='cave') def test_create_subnet_network_cidr_seperated(self): # For positional value, network_id and cidr can be separated. @@ -627,9 +628,10 @@ def test_update_subnet(self): cmd = subnet.UpdateSubnet(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', ['myid', '--name', 'myname', + '--description', 'cavern', '--tags', 'a', 'b'], - {'name': 'myname', 'tags': ['a', 'b'], } - ) + {'name': 'myname', 'tags': ['a', 'b'], + 'description': 'cavern'}) def test_update_subnet_allocation_pools(self): # Update subnet: myid --name myname --tags a b. diff --git a/neutronclient/tests/unit/test_cli20_subnetpool.py b/neutronclient/tests/unit/test_cli20_subnetpool.py index 60d25de6f..e4e460ab1 100644 --- a/neutronclient/tests/unit/test_cli20_subnetpool.py +++ b/neutronclient/tests/unit/test_cli20_subnetpool.py @@ -41,11 +41,12 @@ def test_create_subnetpool_shared(self): prefix2 = '12.11.13.0/24' args = [name, '--min-prefixlen', str(min_prefixlen), '--pool-prefix', prefix1, '--pool-prefix', prefix2, - '--shared'] + '--shared', '--description', 'public pool'] position_names = ['name', 'min_prefixlen', 'prefixes', 'shared'] position_values = [name, min_prefixlen, [prefix1, prefix2], True] self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) + position_names, position_values, + description='public pool') def test_create_subnetpool_not_shared(self): # Create subnetpool: myname. @@ -153,9 +154,9 @@ def test_update_subnetpool(self): resource = 'subnetpool' cmd = subnetpool.UpdateSubnetPool(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'myname'], - {'name': 'myname'} - ) + ['myid', '--name', 'myname', + '--description', ':)'], + {'name': 'myname', 'description': ':)'}) def test_update_subnetpool_with_address_scope(self): # Update subnetpool: myid --address-scope newscope. From 3052b61b8e0f9461e8c6a6c0f311314d53e813a5 Mon Sep 17 00:00:00 2001 From: Doug Hellmann Date: Wed, 9 Mar 2016 13:26:54 -0500 Subject: [PATCH 405/845] Update reno for stable/mitaka Change-Id: I1af0553365fe20569ea2497cbea0ef7fc4649171 --- releasenotes/source/index.rst | 1 + releasenotes/source/mitaka.rst | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 releasenotes/source/mitaka.rst diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index ed6bd6728..26c832e20 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -11,3 +11,4 @@ Past release notes :maxdepth: 1 old_relnotes + mitaka diff --git a/releasenotes/source/mitaka.rst b/releasenotes/source/mitaka.rst new file mode 100644 index 000000000..e54560965 --- /dev/null +++ b/releasenotes/source/mitaka.rst @@ -0,0 +1,6 @@ +=================================== + Mitaka Series Release Notes +=================================== + +.. release-notes:: + :branch: origin/stable/mitaka From 2f571acb1074089027cd24cfbdada3a0680441d2 Mon Sep 17 00:00:00 2001 From: Doug Hellmann Date: Fri, 11 Mar 2016 14:19:50 -0500 Subject: [PATCH 406/845] organize the release notes consistently Organize the release notes pages consistently with the other projects. This patch also fixes the indentation of the toctree to fix the link to the mitaka page. Change-Id: I2e5a61e205f2812cd4cf8683bbaeb809d9b3500b Signed-off-by: Doug Hellmann --- releasenotes/source/index.rst | 14 +++++--------- releasenotes/source/unreleased.rst | 5 +++++ 2 files changed, 10 insertions(+), 9 deletions(-) create mode 100644 releasenotes/source/unreleased.rst diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index 26c832e20..40b342ddd 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -1,14 +1,10 @@ -============= -Release Notes -============= - -.. release-notes:: - -Past release notes ------------------- +============================== + Neutron Client Release Notes +============================== .. toctree:: :maxdepth: 1 + unreleased + mitaka old_relnotes - mitaka diff --git a/releasenotes/source/unreleased.rst b/releasenotes/source/unreleased.rst new file mode 100644 index 000000000..cd22aabcc --- /dev/null +++ b/releasenotes/source/unreleased.rst @@ -0,0 +1,5 @@ +============================== + Current Series Release Notes +============================== + +.. release-notes:: From 71804444d44712ff36087f9ce7337f2af7f67db9 Mon Sep 17 00:00:00 2001 From: reedip Date: Mon, 22 Feb 2016 11:16:37 +0900 Subject: [PATCH 407/845] Fix assertNotEqual parameters One parameter shuffle for assertNotEqual was missed being converted to ( expected, obeserved ). This patch fixes the parameter order. TrivialFix Change-Id: I1a24d97b2b0fdc1c456520c917484333cf92618f --- neutronclient/tests/unit/test_cli20.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 5407e7bed..725e80f2b 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -699,7 +699,7 @@ def test_do_request_with_long_uri_exception(self): try: self.client.do_request('GET', '/test', body='', params=params) except exceptions.RequestURITooLong as cm: - self.assertNotEqual(cm.excess, 0) + self.assertNotEqual(0, cm.excess) else: self.fail('Expected exception NOT raised') From 4012d8b73babdad0fcce009943479260a11a88bb Mon Sep 17 00:00:00 2001 From: Thomas Herve Date: Tue, 8 Mar 2016 13:28:06 +0100 Subject: [PATCH 408/845] Remove APIParamsCall decorator Removes the APIParamsCall decorator around client method, as requesting a different format is not supported anymore. Change-Id: I758693898dc87d0e3dd26487b5625004ba3d152a Closes-Bug: #1554496 --- neutronclient/v2_0/client.py | 280 ----------------------------------- 1 file changed, 280 deletions(-) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 43e8d4025..098614a8c 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -84,22 +84,6 @@ def exception_handler_v20(status_code, error_content): request_ids=request_ids) -class APIParamsCall(object): - """A Decorator to support formatting and tenant overriding and filters.""" - def __init__(self, function): - self.function = function - - def __get__(self, instance, owner): - def with_params(*args, **kwargs): - _format = instance.format - if 'format' in kwargs: - instance.format = kwargs['format'] - ret = self.function(instance, *args, **kwargs) - instance.format = _format - return ret - return with_params - - class _RequestIdMixin(object): """Wrapper class to expose x-openstack-request-id to the caller.""" def _request_ids_setup(self): @@ -579,383 +563,312 @@ class Client(ClientBase): 'network_ip_availabilities': 'network_ip_availability', } - @APIParamsCall def list_ext(self, collection, path, retrieve_all, **_params): """Client extension hook for list.""" return self.list(collection, path, retrieve_all, **_params) - @APIParamsCall def show_ext(self, path, id, **_params): """Client extension hook for show.""" return self.get(path % id, params=_params) - @APIParamsCall def create_ext(self, path, body=None): """Client extension hook for create.""" return self.post(path, body=body) - @APIParamsCall def update_ext(self, path, id, body=None): """Client extension hook for update.""" return self.put(path % id, body=body) - @APIParamsCall def delete_ext(self, path, id): """Client extension hook for delete.""" return self.delete(path % id) - @APIParamsCall def get_quotas_tenant(self, **_params): """Fetch tenant info for following quota operation.""" return self.get(self.quota_path % 'tenant', params=_params) - @APIParamsCall def list_quotas(self, **_params): """Fetch all tenants' quotas.""" return self.get(self.quotas_path, params=_params) - @APIParamsCall def show_quota(self, tenant_id, **_params): """Fetch information of a certain tenant's quotas.""" return self.get(self.quota_path % (tenant_id), params=_params) - @APIParamsCall def update_quota(self, tenant_id, body=None): """Update a tenant's quotas.""" return self.put(self.quota_path % (tenant_id), body=body) - @APIParamsCall def delete_quota(self, tenant_id): """Delete the specified tenant's quota values.""" return self.delete(self.quota_path % (tenant_id)) - @APIParamsCall def list_extensions(self, **_params): """Fetch a list of all extensions on server side.""" return self.get(self.extensions_path, params=_params) - @APIParamsCall def show_extension(self, ext_alias, **_params): """Fetches information of a certain extension.""" return self.get(self.extension_path % ext_alias, params=_params) - @APIParamsCall def list_ports(self, retrieve_all=True, **_params): """Fetches a list of all ports for a tenant.""" # Pass filters in "params" argument to do_request return self.list('ports', self.ports_path, retrieve_all, **_params) - @APIParamsCall def show_port(self, port, **_params): """Fetches information of a certain port.""" return self.get(self.port_path % (port), params=_params) - @APIParamsCall def create_port(self, body=None): """Creates a new port.""" return self.post(self.ports_path, body=body) - @APIParamsCall def update_port(self, port, body=None): """Updates a port.""" return self.put(self.port_path % (port), body=body) - @APIParamsCall def delete_port(self, port): """Deletes the specified port.""" return self.delete(self.port_path % (port)) - @APIParamsCall def list_networks(self, retrieve_all=True, **_params): """Fetches a list of all networks for a tenant.""" # Pass filters in "params" argument to do_request return self.list('networks', self.networks_path, retrieve_all, **_params) - @APIParamsCall def show_network(self, network, **_params): """Fetches information of a certain network.""" return self.get(self.network_path % (network), params=_params) - @APIParamsCall def create_network(self, body=None): """Creates a new network.""" return self.post(self.networks_path, body=body) - @APIParamsCall def update_network(self, network, body=None): """Updates a network.""" return self.put(self.network_path % (network), body=body) - @APIParamsCall def delete_network(self, network): """Deletes the specified network.""" return self.delete(self.network_path % (network)) - @APIParamsCall def list_subnets(self, retrieve_all=True, **_params): """Fetches a list of all subnets for a tenant.""" return self.list('subnets', self.subnets_path, retrieve_all, **_params) - @APIParamsCall def show_subnet(self, subnet, **_params): """Fetches information of a certain subnet.""" return self.get(self.subnet_path % (subnet), params=_params) - @APIParamsCall def create_subnet(self, body=None): """Creates a new subnet.""" return self.post(self.subnets_path, body=body) - @APIParamsCall def update_subnet(self, subnet, body=None): """Updates a subnet.""" return self.put(self.subnet_path % (subnet), body=body) - @APIParamsCall def delete_subnet(self, subnet): """Deletes the specified subnet.""" return self.delete(self.subnet_path % (subnet)) - @APIParamsCall def list_subnetpools(self, retrieve_all=True, **_params): """Fetches a list of all subnetpools for a tenant.""" return self.list('subnetpools', self.subnetpools_path, retrieve_all, **_params) - @APIParamsCall def show_subnetpool(self, subnetpool, **_params): """Fetches information of a certain subnetpool.""" return self.get(self.subnetpool_path % (subnetpool), params=_params) - @APIParamsCall def create_subnetpool(self, body=None): """Creates a new subnetpool.""" return self.post(self.subnetpools_path, body=body) - @APIParamsCall def update_subnetpool(self, subnetpool, body=None): """Updates a subnetpool.""" return self.put(self.subnetpool_path % (subnetpool), body=body) - @APIParamsCall def delete_subnetpool(self, subnetpool): """Deletes the specified subnetpool.""" return self.delete(self.subnetpool_path % (subnetpool)) - @APIParamsCall def list_routers(self, retrieve_all=True, **_params): """Fetches a list of all routers for a tenant.""" # Pass filters in "params" argument to do_request return self.list('routers', self.routers_path, retrieve_all, **_params) - @APIParamsCall def show_router(self, router, **_params): """Fetches information of a certain router.""" return self.get(self.router_path % (router), params=_params) - @APIParamsCall def create_router(self, body=None): """Creates a new router.""" return self.post(self.routers_path, body=body) - @APIParamsCall def update_router(self, router, body=None): """Updates a router.""" return self.put(self.router_path % (router), body=body) - @APIParamsCall def delete_router(self, router): """Deletes the specified router.""" return self.delete(self.router_path % (router)) - @APIParamsCall def list_address_scopes(self, retrieve_all=True, **_params): """Fetches a list of all address scopes for a tenant.""" return self.list('address_scopes', self.address_scopes_path, retrieve_all, **_params) - @APIParamsCall def show_address_scope(self, address_scope, **_params): """Fetches information of a certain address scope.""" return self.get(self.address_scope_path % (address_scope), params=_params) - @APIParamsCall def create_address_scope(self, body=None): """Creates a new address scope.""" return self.post(self.address_scopes_path, body=body) - @APIParamsCall def update_address_scope(self, address_scope, body=None): """Updates a address scope.""" return self.put(self.address_scope_path % (address_scope), body=body) - @APIParamsCall def delete_address_scope(self, address_scope): """Deletes the specified address scope.""" return self.delete(self.address_scope_path % (address_scope)) - @APIParamsCall def add_interface_router(self, router, body=None): """Adds an internal network interface to the specified router.""" return self.put((self.router_path % router) + "/add_router_interface", body=body) - @APIParamsCall def remove_interface_router(self, router, body=None): """Removes an internal network interface from the specified router.""" return self.put((self.router_path % router) + "/remove_router_interface", body=body) - @APIParamsCall def add_gateway_router(self, router, body=None): """Adds an external network gateway to the specified router.""" return self.put((self.router_path % router), body={'router': {'external_gateway_info': body}}) - @APIParamsCall def remove_gateway_router(self, router): """Removes an external network gateway from the specified router.""" return self.put((self.router_path % router), body={'router': {'external_gateway_info': {}}}) - @APIParamsCall def list_floatingips(self, retrieve_all=True, **_params): """Fetches a list of all floatingips for a tenant.""" # Pass filters in "params" argument to do_request return self.list('floatingips', self.floatingips_path, retrieve_all, **_params) - @APIParamsCall def show_floatingip(self, floatingip, **_params): """Fetches information of a certain floatingip.""" return self.get(self.floatingip_path % (floatingip), params=_params) - @APIParamsCall def create_floatingip(self, body=None): """Creates a new floatingip.""" return self.post(self.floatingips_path, body=body) - @APIParamsCall def update_floatingip(self, floatingip, body=None): """Updates a floatingip.""" return self.put(self.floatingip_path % (floatingip), body=body) - @APIParamsCall def delete_floatingip(self, floatingip): """Deletes the specified floatingip.""" return self.delete(self.floatingip_path % (floatingip)) - @APIParamsCall def create_security_group(self, body=None): """Creates a new security group.""" return self.post(self.security_groups_path, body=body) - @APIParamsCall def update_security_group(self, security_group, body=None): """Updates a security group.""" return self.put(self.security_group_path % security_group, body=body) - @APIParamsCall def list_security_groups(self, retrieve_all=True, **_params): """Fetches a list of all security groups for a tenant.""" return self.list('security_groups', self.security_groups_path, retrieve_all, **_params) - @APIParamsCall def show_security_group(self, security_group, **_params): """Fetches information of a certain security group.""" return self.get(self.security_group_path % (security_group), params=_params) - @APIParamsCall def delete_security_group(self, security_group): """Deletes the specified security group.""" return self.delete(self.security_group_path % (security_group)) - @APIParamsCall def create_security_group_rule(self, body=None): """Creates a new security group rule.""" return self.post(self.security_group_rules_path, body=body) - @APIParamsCall def delete_security_group_rule(self, security_group_rule): """Deletes the specified security group rule.""" return self.delete(self.security_group_rule_path % (security_group_rule)) - @APIParamsCall def list_security_group_rules(self, retrieve_all=True, **_params): """Fetches a list of all security group rules for a tenant.""" return self.list('security_group_rules', self.security_group_rules_path, retrieve_all, **_params) - @APIParamsCall def show_security_group_rule(self, security_group_rule, **_params): """Fetches information of a certain security group rule.""" return self.get(self.security_group_rule_path % (security_group_rule), params=_params) - @APIParamsCall def list_endpoint_groups(self, retrieve_all=True, **_params): """Fetches a list of all VPN endpoint groups for a tenant.""" return self.list('endpoint_groups', self.endpoint_groups_path, retrieve_all, **_params) - @APIParamsCall def show_endpoint_group(self, endpointgroup, **_params): """Fetches information for a specific VPN endpoint group.""" return self.get(self.endpoint_group_path % endpointgroup, params=_params) - @APIParamsCall def create_endpoint_group(self, body=None): """Creates a new VPN endpoint group.""" return self.post(self.endpoint_groups_path, body=body) - @APIParamsCall def update_endpoint_group(self, endpoint_group, body=None): """Updates a VPN endpoint group.""" return self.put(self.endpoint_group_path % endpoint_group, body=body) - @APIParamsCall def delete_endpoint_group(self, endpoint_group): """Deletes the specified VPN endpoint group.""" return self.delete(self.endpoint_group_path % endpoint_group) - @APIParamsCall def list_vpnservices(self, retrieve_all=True, **_params): """Fetches a list of all configured VPN services for a tenant.""" return self.list('vpnservices', self.vpnservices_path, retrieve_all, **_params) - @APIParamsCall def show_vpnservice(self, vpnservice, **_params): """Fetches information of a specific VPN service.""" return self.get(self.vpnservice_path % (vpnservice), params=_params) - @APIParamsCall def create_vpnservice(self, body=None): """Creates a new VPN service.""" return self.post(self.vpnservices_path, body=body) - @APIParamsCall def update_vpnservice(self, vpnservice, body=None): """Updates a VPN service.""" return self.put(self.vpnservice_path % (vpnservice), body=body) - @APIParamsCall def delete_vpnservice(self, vpnservice): """Deletes the specified VPN service.""" return self.delete(self.vpnservice_path % (vpnservice)) - @APIParamsCall def list_ipsec_site_connections(self, retrieve_all=True, **_params): """Fetches all configured IPsecSiteConnections for a tenant.""" return self.list('ipsec_site_connections', @@ -963,57 +876,47 @@ def list_ipsec_site_connections(self, retrieve_all=True, **_params): retrieve_all, **_params) - @APIParamsCall def show_ipsec_site_connection(self, ipsecsite_conn, **_params): """Fetches information of a specific IPsecSiteConnection.""" return self.get( self.ipsec_site_connection_path % (ipsecsite_conn), params=_params ) - @APIParamsCall def create_ipsec_site_connection(self, body=None): """Creates a new IPsecSiteConnection.""" return self.post(self.ipsec_site_connections_path, body=body) - @APIParamsCall def update_ipsec_site_connection(self, ipsecsite_conn, body=None): """Updates an IPsecSiteConnection.""" return self.put( self.ipsec_site_connection_path % (ipsecsite_conn), body=body ) - @APIParamsCall def delete_ipsec_site_connection(self, ipsecsite_conn): """Deletes the specified IPsecSiteConnection.""" return self.delete(self.ipsec_site_connection_path % (ipsecsite_conn)) - @APIParamsCall def list_ikepolicies(self, retrieve_all=True, **_params): """Fetches a list of all configured IKEPolicies for a tenant.""" return self.list('ikepolicies', self.ikepolicies_path, retrieve_all, **_params) - @APIParamsCall def show_ikepolicy(self, ikepolicy, **_params): """Fetches information of a specific IKEPolicy.""" return self.get(self.ikepolicy_path % (ikepolicy), params=_params) - @APIParamsCall def create_ikepolicy(self, body=None): """Creates a new IKEPolicy.""" return self.post(self.ikepolicies_path, body=body) - @APIParamsCall def update_ikepolicy(self, ikepolicy, body=None): """Updates an IKEPolicy.""" return self.put(self.ikepolicy_path % (ikepolicy), body=body) - @APIParamsCall def delete_ikepolicy(self, ikepolicy): """Deletes the specified IKEPolicy.""" return self.delete(self.ikepolicy_path % (ikepolicy)) - @APIParamsCall def list_ipsecpolicies(self, retrieve_all=True, **_params): """Fetches a list of all configured IPsecPolicies for a tenant.""" return self.list('ipsecpolicies', @@ -1021,542 +924,445 @@ def list_ipsecpolicies(self, retrieve_all=True, **_params): retrieve_all, **_params) - @APIParamsCall def show_ipsecpolicy(self, ipsecpolicy, **_params): """Fetches information of a specific IPsecPolicy.""" return self.get(self.ipsecpolicy_path % (ipsecpolicy), params=_params) - @APIParamsCall def create_ipsecpolicy(self, body=None): """Creates a new IPsecPolicy.""" return self.post(self.ipsecpolicies_path, body=body) - @APIParamsCall def update_ipsecpolicy(self, ipsecpolicy, body=None): """Updates an IPsecPolicy.""" return self.put(self.ipsecpolicy_path % (ipsecpolicy), body=body) - @APIParamsCall def delete_ipsecpolicy(self, ipsecpolicy): """Deletes the specified IPsecPolicy.""" return self.delete(self.ipsecpolicy_path % (ipsecpolicy)) - @APIParamsCall def list_loadbalancers(self, retrieve_all=True, **_params): """Fetches a list of all loadbalancers for a tenant.""" return self.list('loadbalancers', self.lbaas_loadbalancers_path, retrieve_all, **_params) - @APIParamsCall def show_loadbalancer(self, lbaas_loadbalancer, **_params): """Fetches information for a load balancer.""" return self.get(self.lbaas_loadbalancer_path % (lbaas_loadbalancer), params=_params) - @APIParamsCall def create_loadbalancer(self, body=None): """Creates a new load balancer.""" return self.post(self.lbaas_loadbalancers_path, body=body) - @APIParamsCall def update_loadbalancer(self, lbaas_loadbalancer, body=None): """Updates a load balancer.""" return self.put(self.lbaas_loadbalancer_path % (lbaas_loadbalancer), body=body) - @APIParamsCall def delete_loadbalancer(self, lbaas_loadbalancer): """Deletes the specified load balancer.""" return self.delete(self.lbaas_loadbalancer_path % (lbaas_loadbalancer)) - @APIParamsCall def retrieve_loadbalancer_stats(self, loadbalancer, **_params): """Retrieves stats for a certain load balancer.""" return self.get(self.lbaas_loadbalancer_path_stats % (loadbalancer), params=_params) - @APIParamsCall def retrieve_loadbalancer_status(self, loadbalancer, **_params): """Retrieves status for a certain load balancer.""" return self.get(self.lbaas_loadbalancer_path_status % (loadbalancer), params=_params) - @APIParamsCall def list_listeners(self, retrieve_all=True, **_params): """Fetches a list of all lbaas_listeners for a tenant.""" return self.list('listeners', self.lbaas_listeners_path, retrieve_all, **_params) - @APIParamsCall def show_listener(self, lbaas_listener, **_params): """Fetches information for a lbaas_listener.""" return self.get(self.lbaas_listener_path % (lbaas_listener), params=_params) - @APIParamsCall def create_listener(self, body=None): """Creates a new lbaas_listener.""" return self.post(self.lbaas_listeners_path, body=body) - @APIParamsCall def update_listener(self, lbaas_listener, body=None): """Updates a lbaas_listener.""" return self.put(self.lbaas_listener_path % (lbaas_listener), body=body) - @APIParamsCall def delete_listener(self, lbaas_listener): """Deletes the specified lbaas_listener.""" return self.delete(self.lbaas_listener_path % (lbaas_listener)) - @APIParamsCall def list_lbaas_l7policies(self, retrieve_all=True, **_params): """Fetches a list of all L7 policies for a listener.""" return self.list('l7policies', self.lbaas_l7policies_path, retrieve_all, **_params) - @APIParamsCall def show_lbaas_l7policy(self, l7policy, **_params): """Fetches information of a certain listener's L7 policy.""" return self.get(self.lbaas_l7policy_path % l7policy, params=_params) - @APIParamsCall def create_lbaas_l7policy(self, body=None): """Creates L7 policy for a certain listener.""" return self.post(self.lbaas_l7policies_path, body=body) - @APIParamsCall def update_lbaas_l7policy(self, l7policy, body=None): """Updates L7 policy.""" return self.put(self.lbaas_l7policy_path % l7policy, body=body) - @APIParamsCall def delete_lbaas_l7policy(self, l7policy): """Deletes the specified L7 policy.""" return self.delete(self.lbaas_l7policy_path % l7policy) - @APIParamsCall def list_lbaas_l7rules(self, l7policy, retrieve_all=True, **_params): """Fetches a list of all rules for L7 policy.""" return self.list('rules', self.lbaas_l7rules_path % l7policy, retrieve_all, **_params) - @APIParamsCall def show_lbaas_l7rule(self, l7rule, l7policy, **_params): """Fetches information of a certain L7 policy's rule.""" return self.get(self.lbaas_l7rule_path % (l7policy, l7rule), params=_params) - @APIParamsCall def create_lbaas_l7rule(self, l7policy, body=None): """Creates rule for a certain L7 policy.""" return self.post(self.lbaas_l7rules_path % l7policy, body=body) - @APIParamsCall def update_lbaas_l7rule(self, l7rule, l7policy, body=None): """Updates L7 rule.""" return self.put(self.lbaas_l7rule_path % (l7policy, l7rule), body=body) - @APIParamsCall def delete_lbaas_l7rule(self, l7rule, l7policy): """Deletes the specified L7 rule.""" return self.delete(self.lbaas_l7rule_path % (l7policy, l7rule)) - @APIParamsCall def list_lbaas_pools(self, retrieve_all=True, **_params): """Fetches a list of all lbaas_pools for a tenant.""" return self.list('pools', self.lbaas_pools_path, retrieve_all, **_params) - @APIParamsCall def show_lbaas_pool(self, lbaas_pool, **_params): """Fetches information for a lbaas_pool.""" return self.get(self.lbaas_pool_path % (lbaas_pool), params=_params) - @APIParamsCall def create_lbaas_pool(self, body=None): """Creates a new lbaas_pool.""" return self.post(self.lbaas_pools_path, body=body) - @APIParamsCall def update_lbaas_pool(self, lbaas_pool, body=None): """Updates a lbaas_pool.""" return self.put(self.lbaas_pool_path % (lbaas_pool), body=body) - @APIParamsCall def delete_lbaas_pool(self, lbaas_pool): """Deletes the specified lbaas_pool.""" return self.delete(self.lbaas_pool_path % (lbaas_pool)) - @APIParamsCall def list_lbaas_healthmonitors(self, retrieve_all=True, **_params): """Fetches a list of all lbaas_healthmonitors for a tenant.""" return self.list('healthmonitors', self.lbaas_healthmonitors_path, retrieve_all, **_params) - @APIParamsCall def show_lbaas_healthmonitor(self, lbaas_healthmonitor, **_params): """Fetches information for a lbaas_healthmonitor.""" return self.get(self.lbaas_healthmonitor_path % (lbaas_healthmonitor), params=_params) - @APIParamsCall def create_lbaas_healthmonitor(self, body=None): """Creates a new lbaas_healthmonitor.""" return self.post(self.lbaas_healthmonitors_path, body=body) - @APIParamsCall def update_lbaas_healthmonitor(self, lbaas_healthmonitor, body=None): """Updates a lbaas_healthmonitor.""" return self.put(self.lbaas_healthmonitor_path % (lbaas_healthmonitor), body=body) - @APIParamsCall def delete_lbaas_healthmonitor(self, lbaas_healthmonitor): """Deletes the specified lbaas_healthmonitor.""" return self.delete(self.lbaas_healthmonitor_path % (lbaas_healthmonitor)) - @APIParamsCall def list_lbaas_loadbalancers(self, retrieve_all=True, **_params): """Fetches a list of all lbaas_loadbalancers for a tenant.""" return self.list('loadbalancers', self.lbaas_loadbalancers_path, retrieve_all, **_params) - @APIParamsCall def list_lbaas_members(self, lbaas_pool, retrieve_all=True, **_params): """Fetches a list of all lbaas_members for a tenant.""" return self.list('members', self.lbaas_members_path % lbaas_pool, retrieve_all, **_params) - @APIParamsCall def show_lbaas_member(self, lbaas_member, lbaas_pool, **_params): """Fetches information of a certain lbaas_member.""" return self.get(self.lbaas_member_path % (lbaas_pool, lbaas_member), params=_params) - @APIParamsCall def create_lbaas_member(self, lbaas_pool, body=None): """Creates an lbaas_member.""" return self.post(self.lbaas_members_path % lbaas_pool, body=body) - @APIParamsCall def update_lbaas_member(self, lbaas_member, lbaas_pool, body=None): """Updates a lbaas_healthmonitor.""" return self.put(self.lbaas_member_path % (lbaas_pool, lbaas_member), body=body) - @APIParamsCall def delete_lbaas_member(self, lbaas_member, lbaas_pool): """Deletes the specified lbaas_member.""" return self.delete(self.lbaas_member_path % (lbaas_pool, lbaas_member)) - @APIParamsCall def list_vips(self, retrieve_all=True, **_params): """Fetches a list of all load balancer vips for a tenant.""" # Pass filters in "params" argument to do_request return self.list('vips', self.vips_path, retrieve_all, **_params) - @APIParamsCall def show_vip(self, vip, **_params): """Fetches information of a certain load balancer vip.""" return self.get(self.vip_path % (vip), params=_params) - @APIParamsCall def create_vip(self, body=None): """Creates a new load balancer vip.""" return self.post(self.vips_path, body=body) - @APIParamsCall def update_vip(self, vip, body=None): """Updates a load balancer vip.""" return self.put(self.vip_path % (vip), body=body) - @APIParamsCall def delete_vip(self, vip): """Deletes the specified load balancer vip.""" return self.delete(self.vip_path % (vip)) - @APIParamsCall def list_pools(self, retrieve_all=True, **_params): """Fetches a list of all load balancer pools for a tenant.""" # Pass filters in "params" argument to do_request return self.list('pools', self.pools_path, retrieve_all, **_params) - @APIParamsCall def show_pool(self, pool, **_params): """Fetches information of a certain load balancer pool.""" return self.get(self.pool_path % (pool), params=_params) - @APIParamsCall def create_pool(self, body=None): """Creates a new load balancer pool.""" return self.post(self.pools_path, body=body) - @APIParamsCall def update_pool(self, pool, body=None): """Updates a load balancer pool.""" return self.put(self.pool_path % (pool), body=body) - @APIParamsCall def delete_pool(self, pool): """Deletes the specified load balancer pool.""" return self.delete(self.pool_path % (pool)) - @APIParamsCall def retrieve_pool_stats(self, pool, **_params): """Retrieves stats for a certain load balancer pool.""" return self.get(self.pool_path_stats % (pool), params=_params) - @APIParamsCall def list_members(self, retrieve_all=True, **_params): """Fetches a list of all load balancer members for a tenant.""" # Pass filters in "params" argument to do_request return self.list('members', self.members_path, retrieve_all, **_params) - @APIParamsCall def show_member(self, member, **_params): """Fetches information of a certain load balancer member.""" return self.get(self.member_path % (member), params=_params) - @APIParamsCall def create_member(self, body=None): """Creates a new load balancer member.""" return self.post(self.members_path, body=body) - @APIParamsCall def update_member(self, member, body=None): """Updates a load balancer member.""" return self.put(self.member_path % (member), body=body) - @APIParamsCall def delete_member(self, member): """Deletes the specified load balancer member.""" return self.delete(self.member_path % (member)) - @APIParamsCall def list_health_monitors(self, retrieve_all=True, **_params): """Fetches a list of all load balancer health monitors for a tenant.""" # Pass filters in "params" argument to do_request return self.list('health_monitors', self.health_monitors_path, retrieve_all, **_params) - @APIParamsCall def show_health_monitor(self, health_monitor, **_params): """Fetches information of a certain load balancer health monitor.""" return self.get(self.health_monitor_path % (health_monitor), params=_params) - @APIParamsCall def create_health_monitor(self, body=None): """Creates a new load balancer health monitor.""" return self.post(self.health_monitors_path, body=body) - @APIParamsCall def update_health_monitor(self, health_monitor, body=None): """Updates a load balancer health monitor.""" return self.put(self.health_monitor_path % (health_monitor), body=body) - @APIParamsCall def delete_health_monitor(self, health_monitor): """Deletes the specified load balancer health monitor.""" return self.delete(self.health_monitor_path % (health_monitor)) - @APIParamsCall def associate_health_monitor(self, pool, body): """Associate specified load balancer health monitor and pool.""" return self.post(self.associate_pool_health_monitors_path % (pool), body=body) - @APIParamsCall def disassociate_health_monitor(self, pool, health_monitor): """Disassociate specified load balancer health monitor and pool.""" path = (self.disassociate_pool_health_monitors_path % {'pool': pool, 'health_monitor': health_monitor}) return self.delete(path) - @APIParamsCall def create_qos_queue(self, body=None): """Creates a new queue.""" return self.post(self.qos_queues_path, body=body) - @APIParamsCall def list_qos_queues(self, **_params): """Fetches a list of all queues for a tenant.""" return self.get(self.qos_queues_path, params=_params) - @APIParamsCall def show_qos_queue(self, queue, **_params): """Fetches information of a certain queue.""" return self.get(self.qos_queue_path % (queue), params=_params) - @APIParamsCall def delete_qos_queue(self, queue): """Deletes the specified queue.""" return self.delete(self.qos_queue_path % (queue)) - @APIParamsCall def list_agents(self, **_params): """Fetches agents.""" # Pass filters in "params" argument to do_request return self.get(self.agents_path, params=_params) - @APIParamsCall def show_agent(self, agent, **_params): """Fetches information of a certain agent.""" return self.get(self.agent_path % (agent), params=_params) - @APIParamsCall def update_agent(self, agent, body=None): """Updates an agent.""" return self.put(self.agent_path % (agent), body=body) - @APIParamsCall def delete_agent(self, agent): """Deletes the specified agent.""" return self.delete(self.agent_path % (agent)) - @APIParamsCall def list_network_gateways(self, **_params): """Retrieve network gateways.""" return self.get(self.network_gateways_path, params=_params) - @APIParamsCall def show_network_gateway(self, gateway_id, **_params): """Fetch a network gateway.""" return self.get(self.network_gateway_path % gateway_id, params=_params) - @APIParamsCall def create_network_gateway(self, body=None): """Create a new network gateway.""" return self.post(self.network_gateways_path, body=body) - @APIParamsCall def update_network_gateway(self, gateway_id, body=None): """Update a network gateway.""" return self.put(self.network_gateway_path % gateway_id, body=body) - @APIParamsCall def delete_network_gateway(self, gateway_id): """Delete the specified network gateway.""" return self.delete(self.network_gateway_path % gateway_id) - @APIParamsCall def connect_network_gateway(self, gateway_id, body=None): """Connect a network gateway to the specified network.""" base_uri = self.network_gateway_path % gateway_id return self.put("%s/connect_network" % base_uri, body=body) - @APIParamsCall def disconnect_network_gateway(self, gateway_id, body=None): """Disconnect a network from the specified gateway.""" base_uri = self.network_gateway_path % gateway_id return self.put("%s/disconnect_network" % base_uri, body=body) - @APIParamsCall def list_gateway_devices(self, **_params): """Retrieve gateway devices.""" return self.get(self.gateway_devices_path, params=_params) - @APIParamsCall def show_gateway_device(self, gateway_device_id, **_params): """Fetch a gateway device.""" return self.get(self.gateway_device_path % gateway_device_id, params=_params) - @APIParamsCall def create_gateway_device(self, body=None): """Create a new gateway device.""" return self.post(self.gateway_devices_path, body=body) - @APIParamsCall def update_gateway_device(self, gateway_device_id, body=None): """Updates a new gateway device.""" return self.put(self.gateway_device_path % gateway_device_id, body=body) - @APIParamsCall def delete_gateway_device(self, gateway_device_id): """Delete the specified gateway device.""" return self.delete(self.gateway_device_path % gateway_device_id) - @APIParamsCall def list_dhcp_agent_hosting_networks(self, network, **_params): """Fetches a list of dhcp agents hosting a network.""" return self.get((self.network_path + self.DHCP_AGENTS) % network, params=_params) - @APIParamsCall def list_networks_on_dhcp_agent(self, dhcp_agent, **_params): """Fetches a list of dhcp agents hosting a network.""" return self.get((self.agent_path + self.DHCP_NETS) % dhcp_agent, params=_params) - @APIParamsCall def add_network_to_dhcp_agent(self, dhcp_agent, body=None): """Adds a network to dhcp agent.""" return self.post((self.agent_path + self.DHCP_NETS) % dhcp_agent, body=body) - @APIParamsCall def remove_network_from_dhcp_agent(self, dhcp_agent, network_id): """Remove a network from dhcp agent.""" return self.delete((self.agent_path + self.DHCP_NETS + "/%s") % ( dhcp_agent, network_id)) - @APIParamsCall def list_l3_agent_hosting_routers(self, router, **_params): """Fetches a list of L3 agents hosting a router.""" return self.get((self.router_path + self.L3_AGENTS) % router, params=_params) - @APIParamsCall def list_routers_on_l3_agent(self, l3_agent, **_params): """Fetches a list of L3 agents hosting a router.""" return self.get((self.agent_path + self.L3_ROUTERS) % l3_agent, params=_params) - @APIParamsCall def add_router_to_l3_agent(self, l3_agent, body): """Adds a router to L3 agent.""" return self.post((self.agent_path + self.L3_ROUTERS) % l3_agent, body=body) - @APIParamsCall def list_dragents_hosting_bgp_speaker(self, bgp_speaker, **_params): """Fetches a list of Dynamic Routing agents hosting a BGP speaker.""" return self.get((self.bgp_speaker_path + self.BGP_DRAGENTS) % bgp_speaker, params=_params) - @APIParamsCall def add_bgp_speaker_to_dragent(self, bgp_dragent, body): """Adds a BGP speaker to Dynamic Routing agent.""" return self.post((self.agent_path + self.BGP_DRINSTANCES) % bgp_dragent, body=body) - @APIParamsCall def remove_bgp_speaker_from_dragent(self, bgp_dragent, bgpspeaker_id): """Removes a BGP speaker from Dynamic Routing agent.""" return self.delete((self.agent_path + self.BGP_DRINSTANCES + "/%s") % (bgp_dragent, bgpspeaker_id)) - @APIParamsCall def list_bgp_speaker_on_dragent(self, bgp_dragent, **_params): """Fetches a list of BGP speakers hosted by Dynamic Routing agent.""" return self.get((self.agent_path + self.BGP_DRINSTANCES) % bgp_dragent, params=_params) - @APIParamsCall def list_firewall_rules(self, retrieve_all=True, **_params): """Fetches a list of all firewall rules for a tenant.""" # Pass filters in "params" argument to do_request @@ -1564,28 +1370,23 @@ def list_firewall_rules(self, retrieve_all=True, **_params): return self.list('firewall_rules', self.firewall_rules_path, retrieve_all, **_params) - @APIParamsCall def show_firewall_rule(self, firewall_rule, **_params): """Fetches information of a certain firewall rule.""" return self.get(self.firewall_rule_path % (firewall_rule), params=_params) - @APIParamsCall def create_firewall_rule(self, body=None): """Creates a new firewall rule.""" return self.post(self.firewall_rules_path, body=body) - @APIParamsCall def update_firewall_rule(self, firewall_rule, body=None): """Updates a firewall rule.""" return self.put(self.firewall_rule_path % (firewall_rule), body=body) - @APIParamsCall def delete_firewall_rule(self, firewall_rule): """Deletes the specified firewall rule.""" return self.delete(self.firewall_rule_path % (firewall_rule)) - @APIParamsCall def list_firewall_policies(self, retrieve_all=True, **_params): """Fetches a list of all firewall policies for a tenant.""" # Pass filters in "params" argument to do_request @@ -1593,41 +1394,34 @@ def list_firewall_policies(self, retrieve_all=True, **_params): return self.list('firewall_policies', self.firewall_policies_path, retrieve_all, **_params) - @APIParamsCall def show_firewall_policy(self, firewall_policy, **_params): """Fetches information of a certain firewall policy.""" return self.get(self.firewall_policy_path % (firewall_policy), params=_params) - @APIParamsCall def create_firewall_policy(self, body=None): """Creates a new firewall policy.""" return self.post(self.firewall_policies_path, body=body) - @APIParamsCall def update_firewall_policy(self, firewall_policy, body=None): """Updates a firewall policy.""" return self.put(self.firewall_policy_path % (firewall_policy), body=body) - @APIParamsCall def delete_firewall_policy(self, firewall_policy): """Deletes the specified firewall policy.""" return self.delete(self.firewall_policy_path % (firewall_policy)) - @APIParamsCall def firewall_policy_insert_rule(self, firewall_policy, body=None): """Inserts specified rule into firewall policy.""" return self.put(self.firewall_policy_insert_path % (firewall_policy), body=body) - @APIParamsCall def firewall_policy_remove_rule(self, firewall_policy, body=None): """Removes specified rule from firewall policy.""" return self.put(self.firewall_policy_remove_path % (firewall_policy), body=body) - @APIParamsCall def list_firewalls(self, retrieve_all=True, **_params): """Fetches a list of all firewalls for a tenant.""" # Pass filters in "params" argument to do_request @@ -1635,172 +1429,142 @@ def list_firewalls(self, retrieve_all=True, **_params): return self.list('firewalls', self.firewalls_path, retrieve_all, **_params) - @APIParamsCall def show_firewall(self, firewall, **_params): """Fetches information of a certain firewall.""" return self.get(self.firewall_path % (firewall), params=_params) - @APIParamsCall def create_firewall(self, body=None): """Creates a new firewall.""" return self.post(self.firewalls_path, body=body) - @APIParamsCall def update_firewall(self, firewall, body=None): """Updates a firewall.""" return self.put(self.firewall_path % (firewall), body=body) - @APIParamsCall def delete_firewall(self, firewall): """Deletes the specified firewall.""" return self.delete(self.firewall_path % (firewall)) - @APIParamsCall def remove_router_from_l3_agent(self, l3_agent, router_id): """Remove a router from l3 agent.""" return self.delete((self.agent_path + self.L3_ROUTERS + "/%s") % ( l3_agent, router_id)) - @APIParamsCall def get_lbaas_agent_hosting_pool(self, pool, **_params): """Fetches a loadbalancer agent hosting a pool.""" return self.get((self.pool_path + self.LOADBALANCER_AGENT) % pool, params=_params) - @APIParamsCall def list_pools_on_lbaas_agent(self, lbaas_agent, **_params): """Fetches a list of pools hosted by the loadbalancer agent.""" return self.get((self.agent_path + self.LOADBALANCER_POOLS) % lbaas_agent, params=_params) - @APIParamsCall def get_lbaas_agent_hosting_loadbalancer(self, loadbalancer, **_params): """Fetches a loadbalancer agent hosting a loadbalancer.""" return self.get((self.lbaas_loadbalancer_path + self.LOADBALANCER_HOSTING_AGENT) % loadbalancer, params=_params) - @APIParamsCall def list_loadbalancers_on_lbaas_agent(self, lbaas_agent, **_params): """Fetches a list of loadbalancers hosted by the loadbalancer agent.""" return self.get((self.agent_path + self.AGENT_LOADBALANCERS) % lbaas_agent, params=_params) - @APIParamsCall def list_service_providers(self, retrieve_all=True, **_params): """Fetches service providers.""" # Pass filters in "params" argument to do_request return self.list('service_providers', self.service_providers_path, retrieve_all, **_params) - @APIParamsCall def create_metering_label(self, body=None): """Creates a metering label.""" return self.post(self.metering_labels_path, body=body) - @APIParamsCall def delete_metering_label(self, label): """Deletes the specified metering label.""" return self.delete(self.metering_label_path % (label)) - @APIParamsCall def list_metering_labels(self, retrieve_all=True, **_params): """Fetches a list of all metering labels for a tenant.""" return self.list('metering_labels', self.metering_labels_path, retrieve_all, **_params) - @APIParamsCall def show_metering_label(self, metering_label, **_params): """Fetches information of a certain metering label.""" return self.get(self.metering_label_path % (metering_label), params=_params) - @APIParamsCall def create_metering_label_rule(self, body=None): """Creates a metering label rule.""" return self.post(self.metering_label_rules_path, body=body) - @APIParamsCall def delete_metering_label_rule(self, rule): """Deletes the specified metering label rule.""" return self.delete(self.metering_label_rule_path % (rule)) - @APIParamsCall def list_metering_label_rules(self, retrieve_all=True, **_params): """Fetches a list of all metering label rules for a label.""" return self.list('metering_label_rules', self.metering_label_rules_path, retrieve_all, **_params) - @APIParamsCall def show_metering_label_rule(self, metering_label_rule, **_params): """Fetches information of a certain metering label rule.""" return self.get(self.metering_label_rule_path % (metering_label_rule), params=_params) - @APIParamsCall def create_rbac_policy(self, body=None): """Create a new RBAC policy.""" return self.post(self.rbac_policies_path, body=body) - @APIParamsCall def update_rbac_policy(self, rbac_policy_id, body=None): """Update a RBAC policy.""" return self.put(self.rbac_policy_path % rbac_policy_id, body=body) - @APIParamsCall def list_rbac_policies(self, retrieve_all=True, **_params): """Fetch a list of all RBAC policies for a tenant.""" return self.list('rbac_policies', self.rbac_policies_path, retrieve_all, **_params) - @APIParamsCall def show_rbac_policy(self, rbac_policy_id, **_params): """Fetch information of a certain RBAC policy.""" return self.get(self.rbac_policy_path % rbac_policy_id, params=_params) - @APIParamsCall def delete_rbac_policy(self, rbac_policy_id): """Delete the specified RBAC policy.""" return self.delete(self.rbac_policy_path % rbac_policy_id) - @APIParamsCall def list_qos_policies(self, retrieve_all=True, **_params): """Fetches a list of all qos policies for a tenant.""" # Pass filters in "params" argument to do_request return self.list('policies', self.qos_policies_path, retrieve_all, **_params) - @APIParamsCall def show_qos_policy(self, qos_policy, **_params): """Fetches information of a certain qos policy.""" return self.get(self.qos_policy_path % qos_policy, params=_params) - @APIParamsCall def create_qos_policy(self, body=None): """Creates a new qos policy.""" return self.post(self.qos_policies_path, body=body) - @APIParamsCall def update_qos_policy(self, qos_policy, body=None): """Updates a qos policy.""" return self.put(self.qos_policy_path % qos_policy, body=body) - @APIParamsCall def delete_qos_policy(self, qos_policy): """Deletes the specified qos policy.""" return self.delete(self.qos_policy_path % qos_policy) - @APIParamsCall def list_qos_rule_types(self, retrieve_all=True, **_params): """List available qos rule types.""" return self.list('rule_types', self.qos_rule_types_path, retrieve_all, **_params) - @APIParamsCall def list_bandwidth_limit_rules(self, policy_id, retrieve_all=True, **_params): """Fetches a list of all bandwidth limit rules for the given policy.""" @@ -1808,31 +1572,26 @@ def list_bandwidth_limit_rules(self, policy_id, self.qos_bandwidth_limit_rules_path % policy_id, retrieve_all, **_params) - @APIParamsCall def show_bandwidth_limit_rule(self, rule, policy, body=None): """Fetches information of a certain bandwidth limit rule.""" return self.get(self.qos_bandwidth_limit_rule_path % (policy, rule), body=body) - @APIParamsCall def create_bandwidth_limit_rule(self, policy, body=None): """Creates a new bandwidth limit rule.""" return self.post(self.qos_bandwidth_limit_rules_path % policy, body=body) - @APIParamsCall def update_bandwidth_limit_rule(self, rule, policy, body=None): """Updates a bandwidth limit rule.""" return self.put(self.qos_bandwidth_limit_rule_path % (policy, rule), body=body) - @APIParamsCall def delete_bandwidth_limit_rule(self, rule, policy): """Deletes a bandwidth limit rule.""" return self.delete(self.qos_bandwidth_limit_rule_path % (policy, rule)) - @APIParamsCall def list_dscp_marking_rules(self, policy_id, retrieve_all=True, **_params): """Fetches a list of all DSCP marking rules for the given policy.""" @@ -1840,103 +1599,85 @@ def list_dscp_marking_rules(self, policy_id, self.qos_dscp_marking_rules_path % policy_id, retrieve_all, **_params) - @APIParamsCall def show_dscp_marking_rule(self, rule, policy, body=None): """Shows information of a certain DSCP marking rule.""" return self.get(self.qos_dscp_marking_rule_path % (policy, rule), body=body) - @APIParamsCall def create_dscp_marking_rule(self, policy, body=None): """Creates a new DSCP marking rule.""" return self.post(self.qos_dscp_marking_rules_path % policy, body=body) - @APIParamsCall def update_dscp_marking_rule(self, rule, policy, body=None): """Updates a DSCP marking rule.""" return self.put(self.qos_dscp_marking_rule_path % (policy, rule), body=body) - @APIParamsCall def delete_dscp_marking_rule(self, rule, policy): """Deletes a DSCP marking rule.""" return self.delete(self.qos_dscp_marking_rule_path % (policy, rule)) - @APIParamsCall def create_flavor(self, body=None): """Creates a new Neutron service flavor.""" return self.post(self.flavors_path, body=body) - @APIParamsCall def delete_flavor(self, flavor): """Deletes the specified Neutron service flavor.""" return self.delete(self.flavor_path % (flavor)) - @APIParamsCall def list_flavors(self, retrieve_all=True, **_params): """Fetches a list of all Neutron service flavors for a tenant.""" return self.list('flavors', self.flavors_path, retrieve_all, **_params) - @APIParamsCall def show_flavor(self, flavor, **_params): """Fetches information for a certain Neutron service flavor.""" return self.get(self.flavor_path % (flavor), params=_params) - @APIParamsCall def update_flavor(self, flavor, body): """Update a Neutron service flavor.""" return self.put(self.flavor_path % (flavor), body=body) - @APIParamsCall def associate_flavor(self, flavor, body): """Associate a Neutron service flavor with a profile.""" return self.post(self.flavor_profile_bindings_path % (flavor), body=body) - @APIParamsCall def disassociate_flavor(self, flavor, flavor_profile): """Disassociate a Neutron service flavor with a profile.""" return self.delete(self.flavor_profile_binding_path % (flavor, flavor_profile)) - @APIParamsCall def create_service_profile(self, body=None): """Creates a new Neutron service flavor profile.""" return self.post(self.service_profiles_path, body=body) - @APIParamsCall def delete_service_profile(self, flavor_profile): """Deletes the specified Neutron service flavor profile.""" return self.delete(self.service_profile_path % (flavor_profile)) - @APIParamsCall def list_service_profiles(self, retrieve_all=True, **_params): """Fetches a list of all Neutron service flavor profiles.""" return self.list('service_profiles', self.service_profiles_path, retrieve_all, **_params) - @APIParamsCall def show_service_profile(self, flavor_profile, **_params): """Fetches information for a certain Neutron service flavor profile.""" return self.get(self.service_profile_path % (flavor_profile), params=_params) - @APIParamsCall def update_service_profile(self, service_profile, body): """Update a Neutron service profile.""" return self.put(self.service_profile_path % (service_profile), body=body) - @APIParamsCall def list_availability_zones(self, retrieve_all=True, **_params): """Fetches a list of all availability zones.""" return self.list('availability_zones', self.availability_zones_path, retrieve_all, **_params) - @APIParamsCall def get_auto_allocated_topology(self, tenant_id, **_params): """Fetch information about a tenant's auto-allocated topology.""" return self.get( @@ -1947,118 +1688,97 @@ def validate_auto_allocated_topology_requirements(self, tenant_id): """Validate requirements for getting an auto-allocated topology.""" return self.get_auto_allocated_topology(tenant_id, fields=['dry-run']) - @APIParamsCall def list_bgp_speakers(self, retrieve_all=True, **_params): """Fetches a list of all BGP speakers for a tenant.""" return self.list('bgp_speakers', self.bgp_speakers_path, retrieve_all, **_params) - @APIParamsCall def show_bgp_speaker(self, bgp_speaker_id, **_params): """Fetches information of a certain BGP speaker.""" return self.get(self.bgp_speaker_path % (bgp_speaker_id), params=_params) - @APIParamsCall def create_bgp_speaker(self, body=None): """Creates a new BGP speaker.""" return self.post(self.bgp_speakers_path, body=body) - @APIParamsCall def update_bgp_speaker(self, bgp_speaker_id, body=None): """Update a BGP speaker.""" return self.put(self.bgp_speaker_path % bgp_speaker_id, body=body) - @APIParamsCall def delete_bgp_speaker(self, speaker_id): """Deletes the specified BGP speaker.""" return self.delete(self.bgp_speaker_path % (speaker_id)) - @APIParamsCall def add_peer_to_bgp_speaker(self, speaker_id, body=None): """Adds a peer to BGP speaker.""" return self.put((self.bgp_speaker_path % speaker_id) + "/add_bgp_peer", body=body) - @APIParamsCall def remove_peer_from_bgp_speaker(self, speaker_id, body=None): """Removes a peer from BGP speaker.""" return self.put((self.bgp_speaker_path % speaker_id) + "/remove_bgp_peer", body=body) - @APIParamsCall def add_network_to_bgp_speaker(self, speaker_id, body=None): """Adds a network to BGP speaker.""" return self.put((self.bgp_speaker_path % speaker_id) + "/add_gateway_network", body=body) - @APIParamsCall def remove_network_from_bgp_speaker(self, speaker_id, body=None): """Removes a network from BGP speaker.""" return self.put((self.bgp_speaker_path % speaker_id) + "/remove_gateway_network", body=body) - @APIParamsCall def list_route_advertised_from_bgp_speaker(self, speaker_id, **_params): """Fetches a list of all routes advertised by BGP speaker.""" return self.get((self.bgp_speaker_path % speaker_id) + "/get_advertised_routes", params=_params) - @APIParamsCall def list_bgp_peers(self, **_params): """Fetches a list of all BGP peers.""" return self.get(self.bgp_peers_path, params=_params) - @APIParamsCall def show_bgp_peer(self, peer_id, **_params): """Fetches information of a certain BGP peer.""" return self.get(self.bgp_peer_path % peer_id, params=_params) - @APIParamsCall def create_bgp_peer(self, body=None): """Create a new BGP peer.""" return self.post(self.bgp_peers_path, body=body) - @APIParamsCall def update_bgp_peer(self, bgp_peer_id, body=None): """Update a BGP peer.""" return self.put(self.bgp_peer_path % bgp_peer_id, body=body) - @APIParamsCall def delete_bgp_peer(self, peer_id): """Deletes the specified BGP peer.""" return self.delete(self.bgp_peer_path % peer_id) - @APIParamsCall def list_network_ip_availabilities(self, retrieve_all=True, **_params): """Fetches IP availibility information for all networks""" return self.list('network_ip_availabilities', self.network_ip_availabilities_path, retrieve_all, **_params) - @APIParamsCall def show_network_ip_availability(self, network, **_params): """Fetches IP availability information for a specified network""" return self.get(self.network_ip_availability_path % (network), params=_params) - @APIParamsCall def add_tag(self, resource_type, resource_id, tag, **_params): """Add a tag on the resource.""" return self.put(self.tag_path % (resource_type, resource_id, tag)) - @APIParamsCall def replace_tag(self, resource_type, resource_id, body, **_params): """Replace tags on the resource.""" return self.put(self.tags_path % (resource_type, resource_id), body) - @APIParamsCall def remove_tag(self, resource_type, resource_id, tag, **_params): """Remove a tag on the resource.""" return self.delete(self.tag_path % (resource_type, resource_id, tag)) - @APIParamsCall def remove_tag_all(self, resource_type, resource_id, **_params): """Remove all tags on the resource.""" return self.delete(self.tags_path % (resource_type, resource_id)) From 84cd3c402884654ebc81cec69c7986d45d95e054 Mon Sep 17 00:00:00 2001 From: Sridhar Gaddam Date: Mon, 28 Mar 2016 09:40:47 +0530 Subject: [PATCH 409/845] Fix duplicate entries in list_columns while extending the list Closes-Bug: #1561378 Change-Id: I88cfb8f191570a4a34cac54ae872c46b53a3b793 --- neutronclient/neutron/v2_0/agentscheduler.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/neutronclient/neutron/v2_0/agentscheduler.py b/neutronclient/neutron/v2_0/agentscheduler.py index 2d2b1f7d5..4476b1dd3 100644 --- a/neutronclient/neutron/v2_0/agentscheduler.py +++ b/neutronclient/neutron/v2_0/agentscheduler.py @@ -219,7 +219,8 @@ def extend_list(self, data, parsed_args): # Show the ha_state column only if the server responds with it, # as some plugins do not support HA routers. if any('ha_state' in agent for agent in data): - self.list_columns.append('ha_state') + if 'ha_state' not in self.list_columns: + self.list_columns.append('ha_state') for agent in data: agent['alive'] = ":-)" if agent['alive'] else 'xxx' From 6c82731fe733a6f85430cae0ddc541faa7f54dc5 Mon Sep 17 00:00:00 2001 From: Richard Theis Date: Tue, 29 Mar 2016 13:10:47 -0500 Subject: [PATCH 410/845] Devref Update: Transition to OpenStack Client Update the "Transition to OpenStack Client" devref with the following: - Status update per start of Newton release - Change developer references to use github for latest information Change-Id: I17111b833a83395f379ed8e93091cd1956b80dbd Related-Bug: #1521291 --- doc/source/devref/transition_to_osc.rst | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/doc/source/devref/transition_to_osc.rst b/doc/source/devref/transition_to_osc.rst index 0ac2c901f..511c41a77 100644 --- a/doc/source/devref/transition_to_osc.rst +++ b/doc/source/devref/transition_to_osc.rst @@ -82,13 +82,13 @@ Transition Steps is enabled, then the nova client's Python library will continue to be used. See the following OSC bugs: - * `Floating IP CRUD `_ + * **In Progress** `Floating IP CRUD `_ - * `Port CRUD `_ + * **In Progress** `Port CRUD `_ - * `Security Group CRUD `_ + * **Done** `Security Group CRUD `_ - * `Security Group Rule CRUD `_ + * **In Progress** `Security Group Rule CRUD `_ 6. **In Progress:** OSC continues enhancing its networking support. At this point and when applicable, enhancements to the ``neutron`` @@ -122,8 +122,8 @@ Transition Steps Developer Guide --------------- The ``neutron`` CLI version 4.x, without extensions, supports over 200 -commands while the ``openstack`` CLI version 2.1.0 supports about 30 -networking commands. Of the 30 commands, many do not have all of the options +commands while the ``openstack`` CLI version 2.2.0 supports about 35 +networking commands. Of the 35 commands, many do not have all of the options or arguments of their ``neutron`` CLI equivalent. With this large functional gap, a couple critical questions for developers during this transition are "Which CLI do I change?" and "Where does my CLI belong?" The answer depends on the @@ -205,8 +205,8 @@ you can optionally propose an which documents the new command interface before proceeding with the implementation. Users of the neutron client's command extensions must adopt the -`OSC plugin system `_ -for this transition. Such users will maintain their OSC plugin within their +`OSC plugin `_ +system for this transition. Such users will maintain their OSC plugin within their own project and should follow the guidance in the table above to determine which command to change. @@ -215,17 +215,17 @@ Developer References * See `OSC neutron support etherpad `_ to determine if an ``openstack`` command is in progress. -* See `OSC command list `_ +* See `OSC command list `_ to determine if an ``openstack`` command exists. * See `OSC command spec list `_ to determine if an ``openstack`` command spec exists. * See `OSC plugin command list `_ to determine if an ``openstack`` plugin command exists. -* See `OSC command structure `_ +* See `OSC command structure `_ to determine the current ``openstack`` command objects, plugin objects and actions. -* See `OSC human interface guide `_ +* See `OSC human interface guide `_ for guidance on creating new OSC command interfaces. -* See `OSC plugin `_ +* See `OSC plugin `_ for information on the OSC plugin system to be used for ``neutron`` CLI extensions. * Create an OSC blueprint: https://blueprints.launchpad.net/python-openstackclient/ * Report an OSC bug: https://bugs.launchpad.net/python-openstackclient/+filebug From d8fa792ee720e7877fa371e69633546b752e73da Mon Sep 17 00:00:00 2001 From: Lokesh S Date: Tue, 29 Mar 2016 10:11:35 +0000 Subject: [PATCH 411/845] Log SHA1 hash of X-Auth-Token value Remove logging of sensitive information like the token value from X-Auth-Token. Instead log the sha1 hash of the token value, prefixed with '{SHA1}'. Closes-Bug: #1367339 Change-Id: I72d2ff5ca569c942aa6896aeadab489ff0097255 --- neutronclient/common/utils.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/neutronclient/common/utils.py b/neutronclient/common/utils.py index 47610e60a..882eb2c5c 100644 --- a/neutronclient/common/utils.py +++ b/neutronclient/common/utils.py @@ -19,6 +19,7 @@ import argparse import functools +import hashlib import logging import netaddr import os @@ -30,6 +31,8 @@ from neutronclient._i18n import _ from neutronclient.common import exceptions +SENSITIVE_HEADERS = ('X-Auth-Token',) + def env(*vars, **kwargs): """Returns the first environment variable set. @@ -167,8 +170,13 @@ def http_log_req(_logger, args, kwargs): else: string_parts.append(' %s' % element) - for element in kwargs['headers']: - header = ' -H "%s: %s"' % (element, kwargs['headers'][element]) + for (key, value) in six.iteritems(kwargs['headers']): + if key in SENSITIVE_HEADERS: + v = value.encode('utf-8') + h = hashlib.sha1(v) + d = h.hexdigest() + value = "{SHA1}%s" % d + header = ' -H "%s: %s"' % (key, value) string_parts.append(header) if 'body' in kwargs and kwargs['body']: From 1b8af3e97f4884a11bcea80e63f4bb52f2f124e5 Mon Sep 17 00:00:00 2001 From: Brian Haley Date: Tue, 5 Apr 2016 12:38:22 -0400 Subject: [PATCH 412/845] Change --no-gateway help text The Admin guide uses the wording "Creates a subnet that has no gateway IP address." when describing the --no-gateway option, make the help text more in line with that. Trivialfix Change-Id: I078f74dea5a7b403416f1c27704a4638aa1a43a0 --- neutronclient/neutron/v2_0/subnet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/neutron/v2_0/subnet.py b/neutronclient/neutron/v2_0/subnet.py index 3f27cb77b..952b65245 100644 --- a/neutronclient/neutron/v2_0/subnet.py +++ b/neutronclient/neutron/v2_0/subnet.py @@ -62,7 +62,7 @@ def add_updatable_arguments(parser): gateway_sg.add_argument( '--no-gateway', action='store_true', - help=_('No distribution of gateway.')) + help=_('Do not configure a gateway for this subnet.')) parser.add_argument( '--allocation-pool', metavar='start=IP_ADDR,end=IP_ADDR', action='append', dest='allocation_pools', From 3faf02f6ca119ddbbecb2e4917384764ff229448 Mon Sep 17 00:00:00 2001 From: Richard Theis Date: Wed, 30 Mar 2016 08:31:12 -0500 Subject: [PATCH 413/845] Devref: Newton updates for transition to OSC Update the "Transition to OpenStack Client" devref with the plan for handling network resources in Newton. In particular, this update clarifies the OpenStack project where the commands will reside. This update attempts to capture some of the information in [1] and to address packaging concerns. [1] https://etherpad.openstack.org/p/osc-neutron-support Change-Id: I95e579ab23377a6cf36fc7a50356ab406d350053 Related-Bug: #1521291 --- doc/source/devref/transition_to_osc.rst | 35 ++++++++++--------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/doc/source/devref/transition_to_osc.rst b/doc/source/devref/transition_to_osc.rst index 0ac2c901f..5dcf67ef0 100644 --- a/doc/source/devref/transition_to_osc.rst +++ b/doc/source/devref/transition_to_osc.rst @@ -154,16 +154,16 @@ only with feature requests only being made to the ``openstack`` CLI. +---------------------------+-------------------+-------------------------------------------------+ | Networking Commands | OSC Plugin | OpenStack Project for ``openstack`` Commands | +===========================+===================+=================================================+ -| Core (Stable) | No | python-openstackclient | +| Core | No | python-openstackclient | +---------------------------+-------------------+-------------------------------------------------+ -| Core (New/Experimental) | Yes | python-neutronclient | -| | | (with possible move to python-openstackclient) | +| LBaaS v2 | Yes | python-neutronclient | +| | | (under ``neutronclient/osc/v2/lbaas``) | +---------------------------+-------------------+-------------------------------------------------+ -| LBaaS v2 | Yes | neutron-lbaas | +| VPNaaS v2 | Yes | python-neutronclient | +| | | (under ``neutronclient/osc/v2/vpnaas``) | +---------------------------+-------------------+-------------------------------------------------+ -| VPNaaS v2 | Yes | neutron-vpnaas | -+---------------------------+-------------------+-------------------------------------------------+ -| FWaaS v2 | Yes | neutron-fwaas | +| FWaaS v2 | Yes | python-neutronclient | +| | | (under ``neutronclient/osc/v2/fwaas``) | +---------------------------+-------------------+-------------------------------------------------+ | LBaaS v1 | N/A | None (deprecated) | +---------------------------+-------------------+-------------------------------------------------+ @@ -173,21 +173,14 @@ only with feature requests only being made to the ``openstack`` CLI. +---------------------------+-------------------+-------------------------------------------------+ -The following network resources are part of the "Core (Stable)" group: - -- availability zone -- extension -- floating ip -- network -- port -- quota -- rbac -- router -- security group -- security group rule -- subnet -- subnet pool +**Important:** The actual name of the command object and/or action in OSC may differ +from those used by neutron in order to follow the OSC command structure and to avoid +name conflicts. Developers should get new command objects and actions approved by +the OSC team before proceeding with the implementation. +The "Core" group includes network resources that provide ``neutron`` project features +(i.e. not advanced service or other features). Examples in the "Core" group include: +network, subnet, port, etc. When adding or updating an ``openstack`` networking command to python-openstackclient, changes may first be required to the From 726293ddbe0842e8ef7a2849038fabca4298c25e Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Thu, 7 Apr 2016 15:28:23 +0000 Subject: [PATCH 414/845] Updated from global requirements Change-Id: I602360343feb37f17961f555a288fd6a92c90f37 --- test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index 7bece79a1..a989350f5 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -11,7 +11,7 @@ mock>=1.2 # BSD oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 python-subunit>=0.0.18 # Apache-2.0/BSD -reno>=0.1.1 # Apache2 +reno>=1.6.2 # Apache2 requests-mock>=0.7.0 # Apache-2.0 sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 # BSD testrepository>=0.0.18 # Apache-2.0/BSD From 270da35b7bf0f0a36a8b0daed57e894c5d328a07 Mon Sep 17 00:00:00 2001 From: reedip Date: Tue, 5 Jan 2016 11:10:45 +0900 Subject: [PATCH 415/845] Update help information for lbaasv2 CLIs Help information for lbaasV2 Update CLIs lack several options ( Refer bug#1515111 ). This patch adds all the options which are missing in the "neutron lbaas-*-update " CLIs Also updated is the --http-method for HealthMonitor, making it case in-sensitive. This also adds update of --default-pool for L7 support. Change-Id: If859dc899efa92173c7a654e3f1481b35790faaa Closes-Bug: #1515111 Closes-Bug: #1523968 --- .../neutron/v2_0/lb/v2/healthmonitor.py | 102 ++++++++++-------- neutronclient/neutron/v2_0/lb/v2/listener.py | 79 +++++++++----- .../neutron/v2_0/lb/v2/loadbalancer.py | 43 ++++++-- neutronclient/neutron/v2_0/lb/v2/member.py | 51 +++++---- neutronclient/neutron/v2_0/lb/v2/pool.py | 89 +++++++++------ .../unit/lb/v2/test_cli20_healthmonitor.py | 33 +++++- .../tests/unit/lb/v2/test_cli20_listener.py | 24 ++++- .../unit/lb/v2/test_cli20_loadbalancer.py | 20 +++- .../tests/unit/lb/v2/test_cli20_member.py | 14 ++- .../tests/unit/lb/v2/test_cli20_pool.py | 22 +++- 10 files changed, 326 insertions(+), 151 deletions(-) diff --git a/neutronclient/neutron/v2_0/lb/v2/healthmonitor.py b/neutronclient/neutron/v2_0/lb/v2/healthmonitor.py index a11b57806..34e72c842 100644 --- a/neutronclient/neutron/v2_0/lb/v2/healthmonitor.py +++ b/neutronclient/neutron/v2_0/lb/v2/healthmonitor.py @@ -14,11 +14,56 @@ # License for the specific language governing permissions and limitations # under the License. # - from neutronclient._i18n import _ +from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronV20 +def _add_common_args(parser, is_create=True): + parser.add_argument( + '--delay', + required=is_create, + help=_('The time in seconds between sending probes to members.')) + parser.add_argument( + '--name', + help=_('Name of the health monitor.')) + parser.add_argument( + '--timeout', + required=is_create, + help=_('Maximum number of seconds for a monitor to wait for a ' + 'connection to be established before it times out. The ' + 'value must be less than the delay value.')) + parser.add_argument( + '--http-method', + type=utils.convert_to_uppercase, + help=_('The HTTP method used for requests by the monitor of type ' + 'HTTP.')) + parser.add_argument( + '--url-path', + help=_('The HTTP path used in the HTTP request used by the monitor ' + 'to test a member health. This must be a string ' + 'beginning with a / (forward slash).')) + parser.add_argument( + '--max-retries', + required=is_create, + help=_('Number of permissible connection failures before changing ' + 'the member status to INACTIVE. [1..10].')) + parser.add_argument( + '--expected-codes', + help=_('The list of HTTP status codes expected in ' + 'response from the member to declare it healthy. This ' + 'attribute can contain one value, ' + 'or a list of values separated by comma, ' + 'or a range of values (e.g. "200-299"). If this attribute ' + 'is not specified, it defaults to "200".')) + + +def _parse_common_args(body, parsed_args): + neutronV20.update_dict(parsed_args, body, + ['expected_codes', 'http_method', 'url_path', + 'timeout', 'name', 'delay', 'max_retries']) + + class ListHealthMonitor(neutronV20.ListCommand): """LBaaS v2 List healthmonitors that belong to a given tenant.""" @@ -43,45 +88,11 @@ class CreateHealthMonitor(neutronV20.CreateCommand): shadow_resource = 'lbaas_healthmonitor' def add_known_arguments(self, parser): - parser.add_argument( - '--name', - help=_('Name of the health monitor to be created.')) + _add_common_args(parser) parser.add_argument( '--admin-state-down', dest='admin_state', action='store_false', help=_('Set admin state up to false.')) - parser.add_argument( - '--expected-codes', - help=_('The list of HTTP status codes expected in ' - 'response from the member to declare it healthy. This ' - 'attribute can contain one value, ' - 'or a list of values separated by comma, ' - 'or a range of values (e.g. "200-299"). If this attribute ' - 'is not specified, it defaults to "200".')) - parser.add_argument( - '--http-method', - help=_('The HTTP method used for requests by the monitor of type ' - 'HTTP.')) - parser.add_argument( - '--url-path', - help=_('The HTTP path used in the HTTP request used by the monitor' - ' to test a member health. This must be a string ' - 'beginning with a / (forward slash).')) - parser.add_argument( - '--delay', - required=True, - help=_('The time in seconds between sending probes to members.')) - parser.add_argument( - '--max-retries', - required=True, - help=_('Number of permissible connection failures before changing ' - 'the member status to INACTIVE. [1..10].')) - parser.add_argument( - '--timeout', - required=True, - help=_('Maximum number of seconds for a monitor to wait for a ' - 'connection to be established before it times out. The ' - 'value must be less than the delay value.')) parser.add_argument( '--type', required=True, choices=['PING', 'TCP', 'HTTP', 'HTTPS'], @@ -96,14 +107,11 @@ def args2body(self, parsed_args): self.get_client(), 'pool', parsed_args.pool, cmd_resource='lbaas_pool') body = {'admin_state_up': parsed_args.admin_state, - 'delay': parsed_args.delay, - 'max_retries': parsed_args.max_retries, - 'timeout': parsed_args.timeout, 'type': parsed_args.type, 'pool_id': pool_id} neutronV20.update_dict(parsed_args, body, - ['expected_codes', 'http_method', 'url_path', - 'tenant_id', 'name']) + ['tenant_id']) + _parse_common_args(body, parsed_args) return {self.resource: body} @@ -114,13 +122,17 @@ class UpdateHealthMonitor(neutronV20.UpdateCommand): shadow_resource = 'lbaas_healthmonitor' def add_known_arguments(self, parser): - parser.add_argument( - '--name', - help=_('Updated name of the health monitor.')) + _add_common_args(parser, is_create=False) + utils.add_boolean_argument( + parser, '--admin-state-up', + help=_('Update the administrative state of ' + 'the health monitor (True meaning "Up").')) def args2body(self, parsed_args): body = {} - neutronV20.update_dict(parsed_args, body, ['name']) + _parse_common_args(body, parsed_args) + neutronV20.update_dict(parsed_args, body, + ['admin_state_up']) return {self.resource: body} diff --git a/neutronclient/neutron/v2_0/lb/v2/listener.py b/neutronclient/neutron/v2_0/lb/v2/listener.py index b63e2c28f..1adac9789 100644 --- a/neutronclient/neutron/v2_0/lb/v2/listener.py +++ b/neutronclient/neutron/v2_0/lb/v2/listener.py @@ -37,6 +37,29 @@ def _get_pool_id(client, pool_id_or_name): client, 'pool', pool_id_or_name, cmd_resource='lbaas_pool') +def _add_common_args(parser): + parser.add_argument( + '--description', + help=_('Description of the listener.')) + parser.add_argument( + '--connection-limit', + type=int, + help=_('The maximum number of connections per second allowed for ' + 'the vip. Positive integer or -1 for unlimited (default).')) + parser.add_argument( + '--default-pool', + help=_('Default pool for the listener.')) + + +def _parse_common_args(body, parsed_args, client): + neutronV20.update_dict(parsed_args, body, + ['name', 'description', 'connection_limit']) + if parsed_args.default_pool: + default_pool_id = _get_pool_id( + client, parsed_args.default_pool) + body['default_pool_id'] = default_pool_id + + class ListListener(neutronV20.ListCommand): """LBaaS v2 List listeners that belong to a given tenant.""" @@ -59,18 +82,11 @@ class CreateListener(neutronV20.CreateCommand): resource = 'listener' def add_known_arguments(self, parser): + _add_common_args(parser) parser.add_argument( '--admin-state-down', dest='admin_state', action='store_false', help=_('Set admin state up to false.')) - parser.add_argument( - '--connection-limit', - help=_('The maximum number of connections per second allowed for ' - 'the vip. Positive integer or -1 for unlimited (default).'), - type=int) - parser.add_argument( - '--description', - help=_('Description of the listener.')) parser.add_argument( '--name', help=_('The name of the listener. At least one of --default-pool ' @@ -85,9 +101,6 @@ def add_known_arguments(self, parser): dest='sni_container_refs', nargs='+', help=_('List of TLS container references for SNI.')) - parser.add_argument( - '--default-pool', - help=_('Default pool for the listener.')) parser.add_argument( '--loadbalancer', metavar='LOADBALANCER', @@ -105,36 +118,48 @@ def add_known_arguments(self, parser): help=_('Protocol port for the listener.')) def args2body(self, parsed_args): - resource = { - 'protocol': parsed_args.protocol, - 'protocol_port': parsed_args.protocol_port, - 'admin_state_up': parsed_args.admin_state - } if not parsed_args.loadbalancer and not parsed_args.default_pool: message = _('Either --default-pool or --loadbalancer must be ' 'specified.') raise exceptions.CommandError(message) + body = { + 'protocol': parsed_args.protocol, + 'protocol_port': parsed_args.protocol_port, + 'admin_state_up': parsed_args.admin_state + } if parsed_args.loadbalancer: loadbalancer_id = _get_loadbalancer_id( self.get_client(), parsed_args.loadbalancer) - resource['loadbalancer_id'] = loadbalancer_id - if parsed_args.default_pool: - default_pool_id = _get_pool_id( - self.get_client(), parsed_args.default_pool) - resource['default_pool_id'] = default_pool_id - - neutronV20.update_dict(parsed_args, resource, - ['connection_limit', 'description', - 'name', 'default_tls_container_ref', + body['loadbalancer_id'] = loadbalancer_id + + neutronV20.update_dict(parsed_args, body, + ['default_tls_container_ref', 'sni_container_refs', 'tenant_id']) - return {self.resource: resource} + _parse_common_args(body, parsed_args, self.get_client()) + return {self.resource: body} class UpdateListener(neutronV20.UpdateCommand): """LBaaS v2 Update a given listener.""" resource = 'listener' - allow_names = False + + def add_known_arguments(self, parser): + _add_common_args(parser) + parser.add_argument( + '--name', + help=_('Name of the listener.')) + utils.add_boolean_argument( + parser, '--admin-state-up', dest='admin_state_up', + help=_('Specify the administrative state of the listener. ' + '(True meaning "Up")')) + + def args2body(self, parsed_args): + body = {} + neutronV20.update_dict(parsed_args, body, + ['admin_state_up']) + _parse_common_args(body, parsed_args, self.get_client()) + return {self.resource: body} class DeleteListener(neutronV20.DeleteCommand): diff --git a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py index ceba4990d..76f37fc50 100644 --- a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py +++ b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py @@ -17,9 +17,24 @@ from oslo_serialization import jsonutils from neutronclient._i18n import _ +from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronV20 +def _add_common_args(parser): + parser.add_argument( + '--description', + help=_('Description of the load balancer.')) + parser.add_argument( + '--name', metavar='NAME', + help=_('Name of the load balancer.')) + + +def _parse_common_args(body, parsed_args): + neutronV20.update_dict(parsed_args, body, + ['name', 'description']) + + class ListLoadBalancer(neutronV20.ListCommand): """LBaaS v2 List loadbalancers that belong to a given tenant.""" @@ -40,19 +55,13 @@ class CreateLoadBalancer(neutronV20.CreateCommand): """LBaaS v2 Create a loadbalancer.""" resource = 'loadbalancer' - allow_names = True def add_known_arguments(self, parser): - parser.add_argument( - '--description', - help=_('Description of the load balancer.')) + _add_common_args(parser) parser.add_argument( '--admin-state-down', dest='admin_state', action='store_false', help=_('Set admin state up to false.')) - parser.add_argument( - '--name', metavar='NAME', - help=_('Name of the load balancer.')) parser.add_argument( '--provider', help=_('Provider name of load balancer service.')) @@ -77,8 +86,8 @@ def args2body(self, parsed_args): body['flavor_id'] = _flavor_id neutronV20.update_dict(parsed_args, body, - ['description', 'provider', 'vip_address', - 'tenant_id', 'name']) + ['provider', 'vip_address', 'tenant_id']) + _parse_common_args(body, parsed_args) return {self.resource: body} @@ -86,14 +95,26 @@ class UpdateLoadBalancer(neutronV20.UpdateCommand): """LBaaS v2 Update a given loadbalancer.""" resource = 'loadbalancer' - allow_names = True + + def add_known_arguments(self, parser): + utils.add_boolean_argument( + parser, '--admin-state-up', + help=_('Update the administrative state of ' + 'the load balancer (True meaning "Up").')) + _add_common_args(parser) + + def args2body(self, parsed_args): + body = {} + _parse_common_args(body, parsed_args) + neutronV20.update_dict(parsed_args, body, + ['admin_state_up']) + return {self.resource: body} class DeleteLoadBalancer(neutronV20.DeleteCommand): """LBaaS v2 Delete a given loadbalancer.""" resource = 'loadbalancer' - allow_names = True class RetrieveLoadBalancerStats(neutronV20.ShowCommand): diff --git a/neutronclient/neutron/v2_0/lb/v2/member.py b/neutronclient/neutron/v2_0/lb/v2/member.py index 28109c12b..4a94a7a8d 100644 --- a/neutronclient/neutron/v2_0/lb/v2/member.py +++ b/neutronclient/neutron/v2_0/lb/v2/member.py @@ -15,8 +15,10 @@ # License for the specific language governing permissions and limitations # under the License. # +import argparse from neutronclient._i18n import _ +from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronV20 @@ -37,6 +39,20 @@ def add_known_arguments(self, parser): help=_('ID or name of the pool that this member belongs to.')) +def _add_common_args(parser): + parser.add_argument( + '--name', + help=_('Name of the member.')) + parser.add_argument( + '--weight', + help=_('Weight of member in the pool (default:1, [0..256]).')) + + +def _parse_common_args(body, parsed_args): + neutronV20.update_dict(parsed_args, body, + ['weight', 'name']) + + class ListMember(LbaasMemberMixin, neutronV20.ListCommand): """LBaaS v2 List members that belong to a given pool.""" @@ -69,16 +85,11 @@ class CreateMember(neutronV20.CreateCommand): shadow_resource = 'lbaas_member' def add_known_arguments(self, parser): + _add_common_args(parser) parser.add_argument( '--admin-state-down', dest='admin_state', action='store_false', - help=_('Set admin state up to false')) - parser.add_argument( - '--weight', - help=_('Weight of member in the pool (default:1, [0..256]).')) - parser.add_argument( - '--name', - help=_('Name of the member to be created.')) + help=_('Set admin state up to false.')) parser.add_argument( '--subnet', required=True, @@ -105,7 +116,8 @@ def args2body(self, parsed_args): 'protocol_port': parsed_args.protocol_port, 'address': parsed_args.address} neutronV20.update_dict(parsed_args, body, - ['weight', 'subnet_id', 'tenant_id', 'name']) + ['subnet_id', 'tenant_id']) + _parse_common_args(body, parsed_args) return {self.resource: body} @@ -119,22 +131,25 @@ def add_known_arguments(self, parser): parser.add_argument( '--admin-state-down', dest='admin_state', action='store_false', - help=_('Set admin state up to false')) - parser.add_argument( - '--weight', - help=_('Weight of member in the pool (default:1, [0..256])')) + default=argparse.SUPPRESS, + help=_('[DEPRECATED in Mitaka] Set admin state up to false.')) parser.add_argument( 'pool', metavar='POOL', - help=_('ID or name of the pool that this member belongs to')) - parser.add_argument( - '--name', - help=_('Updated name of the member.')) + help=_('ID or name of the pool that this member belongs to.')) + utils.add_boolean_argument( + parser, '--admin-state-up', + dest='admin_state', + help=_('Update the administrative state of ' + 'the member (True meaning "Up").')) + # ToDo(reedip): After Mitaka, remove admin-state-down + _add_common_args(parser) def args2body(self, parsed_args): self.parent_id = _get_pool_id(self.get_client(), parsed_args.pool) body = {} - neutronV20.update_dict(parsed_args, body, - ['admin_state_up', 'weight', 'name']) + if hasattr(parsed_args, 'admin_state'): + body['admin_state_up'] = parsed_args.admin_state + _parse_common_args(body, parsed_args) return {self.resource: body} diff --git a/neutronclient/neutron/v2_0/lb/v2/pool.py b/neutronclient/neutron/v2_0/lb/v2/pool.py index 6cd0b8be5..b2bd326a9 100644 --- a/neutronclient/neutron/v2_0/lb/v2/pool.py +++ b/neutronclient/neutron/v2_0/lb/v2/pool.py @@ -39,6 +39,29 @@ def _get_listener_id(client, listener_id_or_name): client, 'listener', listener_id_or_name) +def _add_common_args(parser): + parser.add_argument( + '--description', + help=_('Description of the pool.')) + parser.add_argument( + '--name', help=_('The name of the pool.')) + parser.add_argument( + '--lb-algorithm', + required=True, + type=utils.convert_to_uppercase, + choices=['ROUND_ROBIN', 'LEAST_CONNECTIONS', 'SOURCE_IP'], + help=_('The algorithm used to distribute load between the members ' + 'of the pool.')) + + +def _parse_common_args(parsed_args): + body = {} + neutronV20.update_dict(parsed_args, + body, ['description', 'lb_algorithm', 'name', + 'session_persistence']) + return body + + class ListPool(neutronV20.ListCommand): """LBaaS v2 List pools that belong to a given tenant.""" @@ -72,22 +95,11 @@ class CreatePool(neutronV20.CreateCommand): shadow_resource = 'lbaas_pool' def add_known_arguments(self, parser): + _add_common_args(parser) parser.add_argument( '--admin-state-down', dest='admin_state', action='store_false', help=_('Set admin state up to false.')) - parser.add_argument( - '--description', - help=_('Description of the pool.')) - parser.add_argument( - '--session-persistence', - metavar='type=TYPE[,cookie_name=COOKIE_NAME]', - type=utils.str2dict_type(required_keys=['type'], - optional_keys=['cookie_name']), - help=_('The type of session persistence to use and associated ' - 'cookie name')) - parser.add_argument( - '--name', help=_('The name of the pool.')) parser.add_argument( '--listener', help=_('Listener whose default-pool should be set to this pool. ' @@ -98,43 +110,40 @@ def add_known_arguments(self, parser): help=_('Loadbalancer with which this pool should be associated. ' 'At least one of --listener or --loadbalancer must be ' 'specified.')) - parser.add_argument( - '--lb-algorithm', - required=True, - choices=['ROUND_ROBIN', 'LEAST_CONNECTIONS', 'SOURCE_IP'], - help=_('The algorithm used to distribute load between the members ' - 'of the pool.')) parser.add_argument( '--protocol', + type=utils.convert_to_uppercase, required=True, choices=['HTTP', 'HTTPS', 'TCP'], - type=utils.convert_to_uppercase, help=_('Protocol for balancing.')) + parser.add_argument( + '--session-persistence', + metavar='type=TYPE[,cookie_name=COOKIE_NAME]', + type=utils.str2dict_type(required_keys=['type'], + optional_keys=['cookie_name']), + help=_('The type of session persistence to use and associated ' + 'cookie name.')) def args2body(self, parsed_args): - resource = { - 'admin_state_up': parsed_args.admin_state, - 'protocol': parsed_args.protocol, - 'lb_algorithm': parsed_args.lb_algorithm - } if not parsed_args.listener and not parsed_args.loadbalancer: message = _('At least one of --listener or --loadbalancer must be ' 'specified.') raise exceptions.CommandError(message) + body = _parse_common_args(parsed_args) if parsed_args.listener: listener_id = _get_listener_id( self.get_client(), parsed_args.listener) - resource['listener_id'] = listener_id + body['listener_id'] = listener_id if parsed_args.loadbalancer: loadbalancer_id = _get_loadbalancer_id( self.get_client(), parsed_args.loadbalancer) - resource['loadbalancer_id'] = loadbalancer_id - neutronV20.update_dict(parsed_args, resource, - ['description', 'name', - 'session_persistence', 'tenant_id']) - return {self.resource: resource} + body['loadbalancer_id'] = loadbalancer_id + body['admin_state_up'] = parsed_args.admin_state + neutronV20.update_dict(parsed_args, body, + ['tenant_id', 'protocol']) + return {self.resource: body} class UpdatePool(neutronV20.UpdateCommand): @@ -143,6 +152,26 @@ class UpdatePool(neutronV20.UpdateCommand): resource = 'pool' shadow_resource = 'lbaas_pool' + def add_known_arguments(self, parser): + utils.add_boolean_argument( + parser, '--admin-state-up', + help=_('Update the administrative state of ' + 'the pool (True meaning "Up").')) + parser.add_argument( + '--session-persistence', + metavar='type=TYPE[,cookie_name=COOKIE_NAME]', + type=utils.str2dict_type(required_keys=['type'], + optional_keys=['cookie_name']), + help=_('The type of session persistence to use and associated ' + 'cookie name.')) + _add_common_args(parser) + + def args2body(self, parsed_args): + body = _parse_common_args(parsed_args) + neutronV20.update_dict(parsed_args, body, + ['admin_state_up']) + return {self.resource: body} + class DeletePool(neutronV20.DeleteCommand): """LBaaS v2 Delete a given pool.""" diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py b/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py index b585ec3a2..945ac18e3 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py @@ -129,17 +129,40 @@ def test_show_healthmonitor_id_name(self): args, ['id', 'name'], cmd_resource=cmd_resource) - def test_update_healthmonitor(self): - # lbaas-healthmonitor-update myid --name newname. + def _test_update_hm(self, args, expected_values): resource = 'healthmonitor' cmd_resource = 'lbaas_healthmonitor' + my_id = 'myid' cmd = healthmonitor.UpdateHealthMonitor(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'newname'], - {'name': 'newname', }, + args.insert(0, my_id) + self._test_update_resource(resource, cmd, my_id, + args, + expected_values, cmd_resource=cmd_resource) + def test_update_healthmonitor(self): + # lbaas-healthmonitor-update myid --name newname. + self._test_update_hm(['--name', 'newname'], {'name': 'newname', }) + # lbaas-healthmonitor-update myid --delay 10. + self._test_update_hm(['--delay', '10'], {'delay': '10'}) + # lbaas-healthmonitor-update myid --timeout 5. + self._test_update_hm(['--timeout', '5'], {'timeout': '5', }) + # lbaas-healthmonitor-update myid --delay 10. + self._test_update_hm(['--http-method', 'OPTIONS'], + {'http_method': 'OPTIONS'}) + # lbaas-healthmonitor-update myid --url-path /test/string . + self._test_update_hm(['--url-path', '/test/string'], + {'url_path': '/test/string', }) + # lbaas-healthmonitor-update myid --max-retries 5 + self._test_update_hm(['--max-retries', '5'], {'max_retries': '5'}) + # lbaas-healthmonitor-update myid --expected-codes 201 + self._test_update_hm(['--expected-codes', '201'], + {'expected_codes': '201'}) + # lbaas-healthmonitor-update myid --admin-state-up False + self._test_update_hm(['--admin-state-up', 'False'], + {'admin_state_up': 'False'}) + def test_delete_healthmonitor(self): # lbaas-healthmonitor-delete my-id. resource = 'healthmonitor' diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_listener.py b/neutronclient/tests/unit/lb/v2/test_cli20_listener.py index 581e511ea..3993ea7f7 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_listener.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_listener.py @@ -159,16 +159,30 @@ def test_show_listener_id_name(self): args, ['id', 'name'], cmd_resource=cmd_resource) - def test_update_listener(self): - # lbaas-listener-update myid --name newname. + def _test_update_listener(self, args, expected_values): resource = 'listener' cmd_resource = 'lbaas_listener' + my_id = 'myid' + args.insert(0, my_id) cmd = listener.UpdateListener(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'newname'], - {'name': 'newname', }, + self._test_update_resource(resource, cmd, my_id, + args, expected_values, cmd_resource=cmd_resource) + def test_update_listener(self): + # lbaas-listener-update myid --name newname. + self._test_update_listener(['--name', 'newname'], + {'name': 'newname', }) + # lbaas-listener-update myid --description check. + self._test_update_listener(['--description', 'check'], + {'description': 'check', }) + # lbaas-listener-update myid --connection-limit -1 + self._test_update_listener(['--connection-limit', '-1'], + {'connection_limit': -1, }) + # lbaas-listener-update myid --admin-state-up False. + self._test_update_listener(['--admin-state-up', 'False'], + {'admin_state_up': 'False', }) + def test_delete_listener(self): # lbaas-listener-delete my-id. resource = 'listener' diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py b/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py index 205e6816b..b62ce3435 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py @@ -112,16 +112,26 @@ def test_show_loadbalancer_id_name(self): args, ['id', 'name'], cmd_resource=cmd_resource) - def test_update_loadbalancer(self): - # lbaas-loadbalancer-loadbalancer-update myid --name newname. + def _test_update_lb(self, args, expected_values): resource = 'loadbalancer' cmd_resource = 'lbaas_loadbalancer' + my_id = 'myid' + args.insert(0, my_id) cmd = lb.UpdateLoadBalancer(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'newname'], - {'name': 'newname', }, + self._test_update_resource(resource, cmd, my_id, + args, expected_values, cmd_resource=cmd_resource) + def test_update_loadbalancer(self): + # lbaas-loadbalancer-update myid --name newname. + self._test_update_lb(['--name', 'newname'], {'name': 'newname', }) + # lbaas-loadbalancer-update myid --description check. + self._test_update_lb(['--description', 'check'], + {'description': 'check', }) + # lbaas-loadbalancer-update myid --admin-state-up False. + self._test_update_lb(['--admin-state-up', 'False'], + {'admin_state_up': 'False', }) + def test_delete_loadbalancer(self): # lbaas-loadbalancer-loadbalancer-delete my-id. resource = 'loadbalancer' diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_member.py b/neutronclient/tests/unit/lb/v2/test_cli20_member.py index acc663ac5..057da4254 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_member.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_member.py @@ -61,7 +61,7 @@ def test_create_member_with_all_params(self): 'subnet_id', 'weight', 'name'] position_values = [False, address, protocol_port, subnet_id, weight, name] - self._test_create_resource(resource, cmd, '', my_id, args, + self._test_create_resource(resource, cmd, name, my_id, args, position_names, position_values, cmd_resource=cmd_resource, parent_id=pool_id) @@ -145,6 +145,18 @@ def test_update_member(self): {'name': 'newname', }, cmd_resource=cmd_resource, parent_id=pool_id) + # lbaas-member-update myid --weight 100. + args = [my_id, pool_id, '--weight', '100'] + self._test_update_resource(resource, cmd, my_id, args, + {'weight': '100', }, + cmd_resource=cmd_resource, + parent_id=pool_id) + # lbaas-member-update myid --admin-state-up False + args = [my_id, pool_id, '--admin-state-up', 'False'] + self._test_update_resource(resource, cmd, my_id, args, + {'admin_state_up': 'False', }, + cmd_resource=cmd_resource, + parent_id=pool_id) def test_delete_member(self): # lbaas-member-delete my-id. diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_pool.py b/neutronclient/tests/unit/lb/v2/test_cli20_pool.py index 2e5787b0e..7a4f7e7cb 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_pool.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_pool.py @@ -157,13 +157,27 @@ def test_show_pool_id_name(self): cmd_resource=cmd_resource) def test_update_pool(self): - # lbaas-pool-update myid --name newname. + # lbaas-pool-update myid --name newname --description SuperPool + # --lb-algorithm SOURCE_IP --admin-state-up + # --session-persistence type=dict,type=HTTP_COOKIE,cookie_name=pie + resource = 'pool' cmd_resource = 'lbaas_pool' cmd = pool.UpdatePool(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'newname'], - {'name': 'newname', }, + args = ['myid', '--name', 'newname', + '--description', 'SuperPool', '--lb-algorithm', "SOURCE_IP", + '--admin-state-up', 'True', + '--session-persistence', 'type=dict,' + 'type=HTTP_COOKIE,cookie_name=pie'] + body = {'name': 'newname', + "description": "SuperPool", + "lb_algorithm": "SOURCE_IP", + "admin_state_up": 'True', + 'session_persistence': { + 'type': 'HTTP_COOKIE', + 'cookie_name': 'pie', + }, } + self._test_update_resource(resource, cmd, 'myid', args, body, cmd_resource=cmd_resource) def test_delete_pool(self): From fc2950c2bb6ababef650e20ebefa2c3f1b46fa1a Mon Sep 17 00:00:00 2001 From: reedip Date: Fri, 19 Feb 2016 12:56:21 +0900 Subject: [PATCH 416/845] Change try..except to assertRaises in UT Following patch changes the try..except condition for testing to assertRaises. Change-Id: I2253b4e75f778ad077bd905487ede075a430a1fa --- neutronclient/tests/unit/test_cli20.py | 11 ++---- neutronclient/tests/unit/test_cli20_router.py | 11 ++---- neutronclient/tests/unit/test_cli20_subnet.py | 14 +++---- neutronclient/tests/unit/test_name_or_id.py | 38 ++++++++----------- 4 files changed, 29 insertions(+), 45 deletions(-) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 725e80f2b..c5b570983 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -695,13 +695,10 @@ def test_do_request_error_without_response_body(self): def test_do_request_with_long_uri_exception(self): long_string = 'x' * 8200 # 8200 > MAX_URI_LEN:8192 params = {'id': long_string} - - try: - self.client.do_request('GET', '/test', body='', params=params) - except exceptions.RequestURITooLong as cm: - self.assertNotEqual(0, cm.excess) - else: - self.fail('Expected exception NOT raised') + exception = self.assertRaises(exceptions.RequestURITooLong, + self.client.do_request, + 'GET', '/test', body='', params=params) + self.assertNotEqual(0, exception.excess) def test_do_request_request_ids(self): self.mox.StubOutWithMock(self.client.httpclient, "request") diff --git a/neutronclient/tests/unit/test_cli20_router.py b/neutronclient/tests/unit/test_cli20_router.py index d93c46420..cd7ed0310 100644 --- a/neutronclient/tests/unit/test_cli20_router.py +++ b/neutronclient/tests/unit/test_cli20_router.py @@ -260,13 +260,10 @@ def test_update_router_no_routes_with_add_route(self): '--no-routes', '--route', 'destination=10.0.3.0/24,nexthop=10.0.0.10'] - actual_error_code = 0 - try: - self._test_update_resource(resource, cmd, myid, args, None) - except SystemExit: - exc_type, exc_value, exc_traceback = sys.exc_info() - actual_error_code = exc_value.code - self.assertEqual(2, actual_error_code) + exception = self.assertRaises(SystemExit, + self._test_update_resource, + resource, cmd, myid, args, None) + self.assertEqual(2, exception.code) def test_delete_router(self): # Delete router: myid. diff --git a/neutronclient/tests/unit/test_cli20_subnet.py b/neutronclient/tests/unit/test_cli20_subnet.py index 25a771e10..1a9abcd0d 100644 --- a/neutronclient/tests/unit/test_cli20_subnet.py +++ b/neutronclient/tests/unit/test_cli20_subnet.py @@ -648,15 +648,11 @@ def test_update_subnet_enable_disable_dhcp(self): # Update sbunet: --enable-dhcp and --disable-dhcp. resource = 'subnet' cmd = subnet.UpdateSubnet(test_cli20.MyApp(sys.stdout), None) - try: - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'myname', - '--enable-dhcp', '--disable-dhcp'], - {'name': 'myname', } - ) - except exceptions.CommandError: - return - self.fail('No exception for --enable-dhcp --disable-dhcp option') + self.assertRaises(exceptions.CommandError, + self._test_update_resource, + resource, cmd, 'myid', + ['myid', '--name', 'myname', '--enable-dhcp', + '--disable-dhcp'], {'name': 'myname', }) def test_show_subnet(self): # Show subnet: --fields id --fields name myid. diff --git a/neutronclient/tests/unit/test_name_or_id.py b/neutronclient/tests/unit/test_name_or_id.py index 47b6b9b21..7ae3ff9c8 100644 --- a/neutronclient/tests/unit/test_name_or_id.py +++ b/neutronclient/tests/unit/test_name_or_id.py @@ -120,11 +120,10 @@ def test_get_id_from_name_multiple(self): headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) ).AndReturn((test_cli20.MyResp(200), resstr)) self.mox.ReplayAll() - try: - neutronV20.find_resourceid_by_name_or_id( - self.client, 'network', name) - except exceptions.NeutronClientNoUniqueMatch as ex: - self.assertIn('Multiple', ex.message) + exception = self.assertRaises(exceptions.NeutronClientNoUniqueMatch, + neutronV20.find_resourceid_by_name_or_id, + self.client, 'network', name) + self.assertIn('Multiple', exception.message) def test_get_id_from_name_notfound(self): name = 'myname' @@ -141,12 +140,11 @@ def test_get_id_from_name_notfound(self): headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) ).AndReturn((test_cli20.MyResp(200), resstr)) self.mox.ReplayAll() - try: - neutronV20.find_resourceid_by_name_or_id( - self.client, 'network', name) - except exceptions.NotFound as ex: - self.assertIn('Unable to find', ex.message) - self.assertEqual(404, ex.status_code) + exception = self.assertRaises(exceptions.NotFound, + neutronV20.find_resourceid_by_name_or_id, + self.client, 'network', name) + self.assertIn('Unable to find', exception.message) + self.assertEqual(404, exception.status_code) def test_get_id_from_name_multiple_with_project(self): name = 'web_server' @@ -175,9 +173,7 @@ def test_get_id_from_name_multiple_with_project(self): def test_get_id_from_name_multiple_with_project_not_found(self): name = 'web_server' project = str(uuid.uuid4()) - reses = {'security_groups': - [{'id': str(uuid.uuid4()), 'tenant_id': str(uuid.uuid4())}]} - resstr = self.client.serialize(reses) + resstr_notfound = self.client.serialize({'security_groups': []}) self.mox.StubOutWithMock(self.client.httpclient, "request") path = getattr(self.client, "security_groups_path") self.client.httpclient.request( @@ -187,12 +183,10 @@ def test_get_id_from_name_multiple_with_project_not_found(self): 'GET', body=None, headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) - ).AndReturn((test_cli20.MyResp(200), resstr)) + ).AndReturn((test_cli20.MyResp(200), resstr_notfound)) self.mox.ReplayAll() - - try: - neutronV20.find_resourceid_by_name_or_id( - self.client, 'security_group', name, project) - except exceptions.NotFound as ex: - self.assertIn('Unable to find', ex.message) - self.assertEqual(404, ex.status_code) + exc = self.assertRaises(exceptions.NotFound, + neutronV20.find_resourceid_by_name_or_id, + self.client, 'security_group', name, project) + self.assertIn('Unable to find', exc.message) + self.assertEqual(404, exc.status_code) From d4538462a72e20047221ca510ddfee4b0c3d79a9 Mon Sep 17 00:00:00 2001 From: Henry Gessau Date: Thu, 25 Feb 2016 12:22:13 -0500 Subject: [PATCH 417/845] Remove the last remaining vendor code Partial-Bug: #1520062 Change-Id: Ibbb03d52d9b2f6af7151aaf7bfdbb4734cea9d16 --- neutronclient/neutron/v2_0/nsx/__init__.py | 0 .../neutron/v2_0/nsx/networkgateway.py | 265 ------------------ neutronclient/neutron/v2_0/nsx/qos_queue.py | 82 ------ neutronclient/shell.py | 18 -- .../unit/test_cli20_nsx_networkgateway.py | 263 ----------------- .../tests/unit/test_cli20_nsx_queue.py | 85 ------ neutronclient/tests/unit/test_shell.py | 2 - 7 files changed, 715 deletions(-) delete mode 100644 neutronclient/neutron/v2_0/nsx/__init__.py delete mode 100644 neutronclient/neutron/v2_0/nsx/networkgateway.py delete mode 100644 neutronclient/neutron/v2_0/nsx/qos_queue.py delete mode 100644 neutronclient/tests/unit/test_cli20_nsx_networkgateway.py delete mode 100644 neutronclient/tests/unit/test_cli20_nsx_queue.py diff --git a/neutronclient/neutron/v2_0/nsx/__init__.py b/neutronclient/neutron/v2_0/nsx/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutronclient/neutron/v2_0/nsx/networkgateway.py b/neutronclient/neutron/v2_0/nsx/networkgateway.py deleted file mode 100644 index 153c855ae..000000000 --- a/neutronclient/neutron/v2_0/nsx/networkgateway.py +++ /dev/null @@ -1,265 +0,0 @@ -# Copyright 2013 OpenStack Foundation. -# 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. -# - -from __future__ import print_function - -from neutronclient._i18n import _ -from neutronclient.common import utils -from neutronclient.neutron import v2_0 as neutronV20 - -GW_RESOURCE = 'network_gateway' -DEV_RESOURCE = 'gateway_device' -CONNECTOR_TYPE_HELP = _("Type of the transport zone connector to use for this " - "device. Valid values are gre, stt, ipsec_gre, " - "ipsec_stt, and bridge. Defaults to stt. ipsecgre and " - "ipsecstt are also accepted as alternative names") -CONNECTOR_IP_HELP = _("IP address for this device's transport connector. " - "It must correspond to the IP address of the interface " - "used for tenant traffic on the NSX gateway node.") -CLIENT_CERT_HELP = _("PEM certificate used by the NSX gateway transport node " - "to authenticate with the NSX controller.") -CLIENT_CERT_FILE_HELP = _("File containing the PEM certificate used by the " - "NSX gateway transport node to authenticate with " - "the NSX controller.") - - -class ListGatewayDevice(neutronV20.ListCommand): - """List network gateway devices for a given tenant.""" - - resource = DEV_RESOURCE - list_columns = ['id', 'name'] - - -class ShowGatewayDevice(neutronV20.ShowCommand): - """Show information for a given network gateway device.""" - - resource = DEV_RESOURCE - - -def read_cert_file(cert_file): - return open(cert_file, 'rb').read() - - -def gateway_device_args2body(parsed_args): - body = {} - if parsed_args.name: - body['name'] = parsed_args.name - if parsed_args.connector_type: - body['connector_type'] = parsed_args.connector_type - if parsed_args.connector_ip: - body['connector_ip'] = parsed_args.connector_ip - cert_data = None - if parsed_args.cert_file: - cert_data = read_cert_file(parsed_args.cert_file) - elif parsed_args.cert_data: - cert_data = parsed_args.cert_data - if cert_data: - body['client_certificate'] = cert_data - if getattr(parsed_args, 'tenant_id', None): - body['tenant_id'] = parsed_args.tenant_id - return {DEV_RESOURCE: body} - - -class CreateGatewayDevice(neutronV20.CreateCommand): - """Create a network gateway device.""" - - resource = DEV_RESOURCE - - def add_known_arguments(self, parser): - parser.add_argument( - 'name', metavar='NAME', - help='Name of network gateway device to create.') - parser.add_argument( - '--connector-type', - default='stt', - choices=['stt', 'gre', 'ipsecgre', 'ipsecstt', 'bridge', - 'ipsec_gre', 'ipsec_stt'], - help=CONNECTOR_TYPE_HELP) - parser.add_argument( - '--connector-ip', - required=True, - help=CONNECTOR_IP_HELP) - client_cert_group = parser.add_mutually_exclusive_group( - required=True) - client_cert_group.add_argument( - '--client-certificate', - dest='cert_data', - help=CLIENT_CERT_HELP) - client_cert_group.add_argument( - '--client-certificate-file', - dest='cert_file', - help=CLIENT_CERT_FILE_HELP) - - def args2body(self, parsed_args): - return gateway_device_args2body(parsed_args) - - -class UpdateGatewayDevice(neutronV20.UpdateCommand): - """Update a network gateway device.""" - - resource = DEV_RESOURCE - - def add_known_arguments(self, parser): - parser.add_argument( - '--name', metavar='NAME', - help='New name for network gateway device.') - parser.add_argument( - '--connector-type', - required=False, - choices=['stt', 'gre', 'ipsecgre', 'ipsecstt', 'bridge', - 'ipsec_gre', 'ipsec_stt'], - help=CONNECTOR_TYPE_HELP) - parser.add_argument( - '--connector-ip', - required=False, - help=CONNECTOR_IP_HELP) - client_cert_group = parser.add_mutually_exclusive_group() - client_cert_group.add_argument( - '--client-certificate', - dest='cert_data', - help=CLIENT_CERT_HELP) - client_cert_group.add_argument( - '--client-certificate-file', - dest='cert_file', - help=CLIENT_CERT_FILE_HELP) - - def args2body(self, parsed_args): - return gateway_device_args2body(parsed_args) - - -class DeleteGatewayDevice(neutronV20.DeleteCommand): - """Delete a given network gateway device.""" - - resource = DEV_RESOURCE - - -class ListNetworkGateway(neutronV20.ListCommand): - """List network gateways for a given tenant.""" - - resource = GW_RESOURCE - list_columns = ['id', 'name'] - - -class ShowNetworkGateway(neutronV20.ShowCommand): - """Show information of a given network gateway.""" - - resource = GW_RESOURCE - - -class CreateNetworkGateway(neutronV20.CreateCommand): - """Create a network gateway.""" - - resource = GW_RESOURCE - - def add_known_arguments(self, parser): - parser.add_argument( - 'name', metavar='NAME', - help=_('Name of network gateway to create.')) - parser.add_argument( - '--device', metavar='id=ID,interface_name=NAME_OR_ID', - action='append', - help=_('Device info for this gateway. You can repeat this ' - 'option for multiple devices for HA gateways.')) - - def args2body(self, parsed_args): - body = {'name': parsed_args.name} - devices = [] - if parsed_args.device: - for device in parsed_args.device: - devices.append(utils.str2dict(device)) - if devices: - body['devices'] = devices - if parsed_args.tenant_id: - body['tenant_id'] = parsed_args.tenant_id - return {self.resource: body} - - -class DeleteNetworkGateway(neutronV20.DeleteCommand): - """Delete a given network gateway.""" - - resource = GW_RESOURCE - - -class UpdateNetworkGateway(neutronV20.UpdateCommand): - """Update the name for a network gateway.""" - - resource = GW_RESOURCE - - -class NetworkGatewayInterfaceCommand(neutronV20.NeutronCommand): - """Base class for connecting/disconnecting networks to/from a gateway.""" - - resource = GW_RESOURCE - - def get_parser(self, prog_name): - parser = super(NetworkGatewayInterfaceCommand, - self).get_parser(prog_name) - parser.add_argument( - 'net_gateway_id', metavar='NET-GATEWAY-ID', - help=_('ID of the network gateway.')) - parser.add_argument( - 'network_id', metavar='NETWORK-ID', - help=_('ID of the internal network to connect on the gateway.')) - parser.add_argument( - '--segmentation-type', - help=_('L2 segmentation strategy on the external side of ' - 'the gateway (e.g.: VLAN, FLAT).')) - parser.add_argument( - '--segmentation-id', - help=_('Identifier for the L2 segment on the external side ' - 'of the gateway.')) - return parser - - def retrieve_ids(self, client, args): - gateway_id = neutronV20.find_resourceid_by_name_or_id( - client, self.resource, args.net_gateway_id) - network_id = neutronV20.find_resourceid_by_name_or_id( - client, 'network', args.network_id) - return (gateway_id, network_id) - - -class ConnectNetworkGateway(NetworkGatewayInterfaceCommand): - """Add an internal network interface to a router.""" - - def take_action(self, parsed_args): - neutron_client = self.get_client() - (gateway_id, network_id) = self.retrieve_ids(neutron_client, - parsed_args) - neutron_client.connect_network_gateway( - gateway_id, {'network_id': network_id, - 'segmentation_type': parsed_args.segmentation_type, - 'segmentation_id': parsed_args.segmentation_id}) - # TODO(Salvatore-Orlando): Do output formatting as - # any other command - print(_('Connected network to gateway %s') % gateway_id, - file=self.app.stdout) - - -class DisconnectNetworkGateway(NetworkGatewayInterfaceCommand): - """Remove a network from a network gateway.""" - - def take_action(self, parsed_args): - neutron_client = self.get_client() - (gateway_id, network_id) = self.retrieve_ids(neutron_client, - parsed_args) - neutron_client.disconnect_network_gateway( - gateway_id, {'network_id': network_id, - 'segmentation_type': parsed_args.segmentation_type, - 'segmentation_id': parsed_args.segmentation_id}) - # TODO(Salvatore-Orlando): Do output formatting as - # any other command - print(_('Disconnected network from gateway %s') % gateway_id, - file=self.app.stdout) diff --git a/neutronclient/neutron/v2_0/nsx/qos_queue.py b/neutronclient/neutron/v2_0/nsx/qos_queue.py deleted file mode 100644 index 8ea5f1303..000000000 --- a/neutronclient/neutron/v2_0/nsx/qos_queue.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright 2013 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. - -from neutronclient._i18n import _ -from neutronclient.neutron import v2_0 as neutronV20 - - -class ListQoSQueue(neutronV20.ListCommand): - """List queues that belong to a given tenant.""" - - resource = 'qos_queue' - list_columns = ['id', 'name', 'min', 'max', - 'qos_marking', 'dscp', 'default'] - - -class ShowQoSQueue(neutronV20.ShowCommand): - """Show information of a given queue.""" - - resource = 'qos_queue' - allow_names = True - - -class CreateQoSQueue(neutronV20.CreateCommand): - """Create a queue.""" - - resource = 'qos_queue' - - def add_known_arguments(self, parser): - parser.add_argument( - 'name', metavar='NAME', - help=_('Name of queue.')) - parser.add_argument( - '--min', - help=_('Minimum rate.')), - parser.add_argument( - '--max', - help=_('Maximum rate.')), - parser.add_argument( - '--qos-marking', - help=_('QOS marking as untrusted or trusted.')), - parser.add_argument( - '--default', - default=False, - help=_('If true all created ports will be the size of this queue, ' - 'if queue is not specified')), - parser.add_argument( - '--dscp', - help=_('Differentiated Services Code Point.')), - - def args2body(self, parsed_args): - params = {'name': parsed_args.name, - 'default': parsed_args.default} - if parsed_args.min: - params['min'] = parsed_args.min - if parsed_args.max: - params['max'] = parsed_args.max - if parsed_args.qos_marking: - params['qos_marking'] = parsed_args.qos_marking - if parsed_args.dscp: - params['dscp'] = parsed_args.dscp - if parsed_args.tenant_id: - params['tenant_id'] = parsed_args.tenant_id - return {'qos_queue': params} - - -class DeleteQoSQueue(neutronV20.DeleteCommand): - """Delete a given queue.""" - - resource = 'qos_queue' - allow_names = True diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 7fcf8fc0f..a0089ddd1 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -69,8 +69,6 @@ from neutronclient.neutron.v2_0 import metering from neutronclient.neutron.v2_0 import network from neutronclient.neutron.v2_0 import network_ip_availability -from neutronclient.neutron.v2_0.nsx import networkgateway -from neutronclient.neutron.v2_0.nsx import qos_queue from neutronclient.neutron.v2_0 import port from neutronclient.neutron.v2_0 import purge from neutronclient.neutron.v2_0.qos import bandwidth_limit_rule @@ -274,26 +272,10 @@ def take_action(self, parsed_args): 'lb-healthmonitor-disassociate': ( lb_healthmonitor.DisassociateHealthMonitor ), - 'queue-create': qos_queue.CreateQoSQueue, - 'queue-delete': qos_queue.DeleteQoSQueue, - 'queue-show': qos_queue.ShowQoSQueue, - 'queue-list': qos_queue.ListQoSQueue, 'agent-list': agent.ListAgent, 'agent-show': agent.ShowAgent, 'agent-delete': agent.DeleteAgent, 'agent-update': agent.UpdateAgent, - 'net-gateway-create': networkgateway.CreateNetworkGateway, - 'net-gateway-update': networkgateway.UpdateNetworkGateway, - 'net-gateway-delete': networkgateway.DeleteNetworkGateway, - 'net-gateway-show': networkgateway.ShowNetworkGateway, - 'net-gateway-list': networkgateway.ListNetworkGateway, - 'net-gateway-connect': networkgateway.ConnectNetworkGateway, - 'net-gateway-disconnect': networkgateway.DisconnectNetworkGateway, - 'gateway-device-create': networkgateway.CreateGatewayDevice, - 'gateway-device-update': networkgateway.UpdateGatewayDevice, - 'gateway-device-delete': networkgateway.DeleteGatewayDevice, - 'gateway-device-show': networkgateway.ShowGatewayDevice, - 'gateway-device-list': networkgateway.ListGatewayDevice, 'dhcp-agent-network-add': agentscheduler.AddNetworkToDhcpAgent, 'dhcp-agent-network-remove': agentscheduler.RemoveNetworkFromDhcpAgent, 'net-list-on-dhcp-agent': agentscheduler.ListNetworksOnDhcpAgent, diff --git a/neutronclient/tests/unit/test_cli20_nsx_networkgateway.py b/neutronclient/tests/unit/test_cli20_nsx_networkgateway.py deleted file mode 100644 index 640578dbe..000000000 --- a/neutronclient/tests/unit/test_cli20_nsx_networkgateway.py +++ /dev/null @@ -1,263 +0,0 @@ -# Copyright 2012 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 sys - -from mox3 import mox -import six - -from neutronclient.neutron.v2_0.nsx import networkgateway as nwgw -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20NetworkGatewayJSON(test_cli20.CLITestV20Base): - - gw_resource = "network_gateway" - dev_resource = "gateway_device" - - non_admin_status_resources = ['network_gateway', 'gateway_device'] - - def setUp(self): - super(CLITestV20NetworkGatewayJSON, self).setUp( - plurals={'devices': 'device', - 'network_gateways': 'network_gateway'}) - - def test_create_gateway(self): - cmd = nwgw.CreateNetworkGateway(test_cli20.MyApp(sys.stdout), None) - name = 'gw-test' - myid = 'myid' - args = [name, ] - position_names = ['name', ] - position_values = [name, ] - self._test_create_resource(self.gw_resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_gateway_with_tenant(self): - cmd = nwgw.CreateNetworkGateway(test_cli20.MyApp(sys.stdout), None) - name = 'gw-test' - myid = 'myid' - args = ['--tenant_id', 'tenantid', name] - position_names = ['name', ] - position_values = [name, ] - self._test_create_resource(self.gw_resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_gateway_with_device(self): - cmd = nwgw.CreateNetworkGateway(test_cli20.MyApp(sys.stdout), None) - name = 'gw-test' - myid = 'myid' - args = ['--device', 'device_id=test', name, ] - position_names = ['name', ] - position_values = [name, ] - self._test_create_resource(self.gw_resource, cmd, name, myid, args, - position_names, position_values, - devices=[{'device_id': 'test'}]) - - def test_list_gateways(self): - resources = '%ss' % self.gw_resource - cmd = nwgw.ListNetworkGateway(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_update_gateway(self): - cmd = nwgw.UpdateNetworkGateway(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(self.gw_resource, cmd, 'myid', - ['myid', '--name', 'higuain'], - {'name': 'higuain'}) - - def test_delete_gateway(self): - cmd = nwgw.DeleteNetworkGateway(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(self.gw_resource, cmd, myid, args) - - def test_show_gateway(self): - cmd = nwgw.ShowNetworkGateway(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(self.gw_resource, cmd, self.test_id, args, - ['id', 'name']) - - def test_connect_network_to_gateway(self): - cmd = nwgw.ConnectNetworkGateway(test_cli20.MyApp(sys.stdout), None) - args = ['gw_id', 'net_id', - '--segmentation-type', 'edi', - '--segmentation-id', '7'] - self._test_update_resource_action(self.gw_resource, cmd, 'gw_id', - 'connect_network', - args, - {'network_id': 'net_id', - 'segmentation_type': 'edi', - 'segmentation_id': '7'}) - - def test_disconnect_network_from_gateway(self): - cmd = nwgw.DisconnectNetworkGateway(test_cli20.MyApp(sys.stdout), None) - args = ['gw_id', 'net_id', - '--segmentation-type', 'edi', - '--segmentation-id', '7'] - self._test_update_resource_action(self.gw_resource, cmd, 'gw_id', - 'disconnect_network', - args, - {'network_id': 'net_id', - 'segmentation_type': 'edi', - 'segmentation_id': '7'}) - - def _test_create_gateway_device(self, - name, - connector_type, - connector_ip, - client_certificate=None, - client_certificate_file=None, - must_raise=False): - cmd = nwgw.CreateGatewayDevice(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - extra_body = {'connector_type': connector_type, - 'connector_ip': connector_ip, - 'client_certificate': client_certificate} - self.mox.StubOutWithMock(nwgw, 'read_cert_file') - if client_certificate_file: - nwgw.read_cert_file(mox.IgnoreArg()).AndReturn('xyz') - extra_body['client_certificate'] = 'xyz' - self.mox.ReplayAll() - position_names = ['name', ] - position_values = [name, ] - args = [] - for (k, v) in six.iteritems(extra_body): - if (k == 'client_certificate' and client_certificate_file): - v = client_certificate_file - k = 'client_certificate_file' - # Append argument only if value for it was specified - if v: - args.extend(['--%s' % k.replace('_', '-'), v]) - # The following is just for verifying the call fails as expected when - # both certificate and certificate file are specified. The extra - # argument added is client-certificate since the loop above added - # client-certificate-file - if client_certificate_file and client_certificate: - args.extend(['--client-certificate', client_certificate_file]) - args.append(name) - if must_raise: - with test_cli20.capture_std_streams(): - self.assertRaises( - SystemExit, self._test_create_resource, - self.dev_resource, cmd, name, myid, args, - position_names, position_values, extra_body=extra_body) - else: - self._test_create_resource( - self.dev_resource, cmd, name, myid, args, - position_names, position_values, extra_body=extra_body) - self.mox.UnsetStubs() - - def test_create_gateway_device(self): - self._test_create_gateway_device('dev_test', 'stt', '1.1.1.1', 'xyz') - - def test_create_gateway_device_with_certfile(self): - self._test_create_gateway_device('dev_test', 'stt', '1.1.1.1', - client_certificate_file='some_file') - - def test_create_gateway_device_invalid_connector_type_fails(self): - self._test_create_gateway_device('dev_test', 'ciccio', - '1.1.1.1', client_certificate='xyz', - must_raise=True) - - def test_create_gateway_device_missing_connector_ip_fails(self): - self._test_create_gateway_device('dev_test', 'stt', - None, client_certificate='xyz', - must_raise=True) - - def test_create_gateway_device_missing_certificates_fails(self): - self._test_create_gateway_device('dev_test', 'stt', '1.1.1.1', - must_raise=True) - - def test_create_gateway_device_with_cert_and_cert_file_fails(self): - self._test_create_gateway_device('dev_test', 'stt', '1.1.1.1', - client_certificate='xyz', - client_certificate_file='some_file', - must_raise=True) - - def _test_update_gateway_device(self, - name=None, - connector_type=None, - connector_ip=None, - client_certificate=None, - client_certificate_file=None, - must_raise=False): - cmd = nwgw.UpdateGatewayDevice(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - extra_body = {} - self.mox.StubOutWithMock(nwgw, 'read_cert_file') - if client_certificate_file: - nwgw.read_cert_file(mox.IgnoreArg()).AndReturn('xyz') - self.mox.ReplayAll() - args = [myid] - - def process_arg(argname, arg): - if arg: - extra_body[argname] = arg - args.extend(['--%s' % argname.replace('_', '-'), arg]) - - process_arg('name', name) - process_arg('connector_type', connector_type) - process_arg('connector_ip', connector_ip) - process_arg('client_certificate', client_certificate) - if client_certificate_file: - extra_body['client_certificate'] = 'xyz' - args.extend(['--client-certificate-file', - client_certificate_file]) - if must_raise: - with test_cli20.capture_std_streams(): - self.assertRaises( - SystemExit, self._test_update_resource, - self.dev_resource, cmd, myid, args, - extrafields=extra_body) - else: - self._test_update_resource( - self.dev_resource, cmd, myid, args, - extrafields=extra_body) - self.mox.UnsetStubs() - - def test_update_gateway_device(self): - self._test_update_gateway_device('dev_test', 'stt', '1.1.1.1', 'xyz') - - def test_update_gateway_device_partial_body(self): - self._test_update_gateway_device(name='dev_test', - connector_type='stt') - - def test_update_gateway_device_with_certfile(self): - self._test_update_gateway_device('dev_test', 'stt', '1.1.1.1', - client_certificate_file='some_file') - - def test_update_gateway_device_invalid_connector_type_fails(self): - self._test_update_gateway_device('dev_test', 'ciccio', - '1.1.1.1', client_certificate='xyz', - must_raise=True) - - def test_update_gateway_device_with_cert_and_cert_file_fails(self): - self._test_update_gateway_device('dev_test', 'stt', '1.1.1.1', - client_certificate='xyz', - client_certificate_file='some_file', - must_raise=True) - - def test_delete_gateway_device(self): - cmd = nwgw.DeleteGatewayDevice(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(self.dev_resource, cmd, myid, args) - - def test_show_gateway_device(self): - cmd = nwgw.ShowGatewayDevice(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(self.dev_resource, cmd, self.test_id, args, - ['id', 'name']) diff --git a/neutronclient/tests/unit/test_cli20_nsx_queue.py b/neutronclient/tests/unit/test_cli20_nsx_queue.py deleted file mode 100644 index 503f1c177..000000000 --- a/neutronclient/tests/unit/test_cli20_nsx_queue.py +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env python -# Copyright 2013 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 sys - -from neutronclient.neutron.v2_0.nsx import qos_queue as qos -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20QosQueueJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['qos_queue'] - - def setUp(self): - super(CLITestV20QosQueueJSON, self).setUp( - plurals={'qos_queues': 'qos_queue'}) - - def test_create_qos_queue(self): - # Create a qos queue. - resource = 'qos_queue' - cmd = qos.CreateQoSQueue( - test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - name = 'my_queue' - default = False - args = ['--default', default, name] - position_names = ['name', 'default'] - position_values = [name, default] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_qos_queue_all_values(self): - # Create a qos queue. - resource = 'qos_queue' - cmd = qos.CreateQoSQueue( - test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - name = 'my_queue' - default = False - min = '10' - max = '40' - qos_marking = 'untrusted' - dscp = '0' - args = ['--default', default, '--min', min, '--max', max, - '--qos-marking', qos_marking, '--dscp', dscp, name] - position_names = ['name', 'default', 'min', 'max', 'qos_marking', - 'dscp'] - position_values = [name, default, min, max, qos_marking, dscp] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_list_qos_queue(self): - resources = "qos_queues" - cmd = qos.ListQoSQueue( - test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_show_qos_queue_id(self): - resource = 'qos_queue' - cmd = qos.ShowQoSQueue( - test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id']) - - def test_delete_qos_queue(self): - resource = 'qos_queue' - cmd = qos.DeleteQoSQueue( - test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) diff --git a/neutronclient/tests/unit/test_shell.py b/neutronclient/tests/unit/test_shell.py index 284ca837e..8e2af7f5c 100644 --- a/neutronclient/tests/unit/test_shell.py +++ b/neutronclient/tests/unit/test_shell.py @@ -135,9 +135,7 @@ def test_bash_completion_command(self): # just check we have some output required = [ '.*--tenant_id', - '.*--client-certificate', '.*help', - '.*gateway-device-create', '.*--dns-nameserver'] help_text, stderr = self.shell('neutron bash-completion') self.assertFalse(stderr) From 84aebc2973adb40e8d945a9c7016f8b2771c19b9 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Fri, 15 Apr 2016 12:41:49 +0900 Subject: [PATCH 418/845] Fix random failure of security group unit test CLITestV20SecurityGroupsJSON.test_extend_list_exceed_max_uri_len fails randomly because this test uses query parameters and the order of query parameters in a construct URL is not deterministic. As a result the test failed randomly. The test should use MyUrlComparator to compare URLs. MyUrlComparator ignores the order parameters when comparing URLs. Change-Id: I8a8116f45583102fa1351d17a1cc5174bdce3cc9 Closes-Bug: #1570689 --- neutronclient/tests/unit/test_cli20_securitygroup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/neutronclient/tests/unit/test_cli20_securitygroup.py b/neutronclient/tests/unit/test_cli20_securitygroup.py index 3bd62edb3..1af88ff79 100644 --- a/neutronclient/tests/unit/test_cli20_securitygroup.py +++ b/neutronclient/tests/unit/test_cli20_securitygroup.py @@ -307,7 +307,8 @@ def mox_calls(path, data): self.client.httpclient._check_uri_length( mox.IgnoreArg()).AndReturn(None) self.client.httpclient.request( - test_cli20.end_url(path, item['filter']), + test_cli20.MyUrlComparator( + test_cli20.end_url(path, item['filter']), self.client), 'GET', body=None, headers=mox.ContainsKeyValue( From 0927632a66acff8aedee9459d22aad277e5cc234 Mon Sep 17 00:00:00 2001 From: Tin Lam Date: Fri, 15 Apr 2016 19:03:38 -0500 Subject: [PATCH 419/845] Added missing help text for 'purge' command Added help text for 'purge' when running 'neutron --help'. Change-Id: I0176eb9c1774b6553dbceadf93c68718a914c404 Closes-Bug: #1570976 --- neutronclient/neutron/v2_0/purge.py | 1 + 1 file changed, 1 insertion(+) diff --git a/neutronclient/neutron/v2_0/purge.py b/neutronclient/neutron/v2_0/purge.py index 61762963f..63f7d2ebe 100644 --- a/neutronclient/neutron/v2_0/purge.py +++ b/neutronclient/neutron/v2_0/purge.py @@ -20,6 +20,7 @@ class Purge(neutronV20.NeutronCommand): + """Delete all resources that belong to a given tenant.""" def _pluralize(self, string): return string + 's' From feba9bb2c972a0f6ff4f6118deb67b3c5a4c8608 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Wed, 20 Apr 2016 05:19:35 +0000 Subject: [PATCH 420/845] Updated from global requirements Change-Id: I00f8c716e6ea6399066a87d149c6eed298859c1f --- requirements.txt | 2 +- test-requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index cc6fd9dc8..7bee71b8d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,4 +14,4 @@ keystoneauth1>=2.1.0 # Apache-2.0 requests!=2.9.0,>=2.8.1 # Apache-2.0 simplejson>=2.2.0 # MIT six>=1.9.0 # MIT -Babel>=1.3 # BSD +Babel!=2.3.0,!=2.3.1,!=2.3.2,!=2.3.3,>=1.3 # BSD diff --git a/test-requirements.txt b/test-requirements.txt index a989350f5..76d549411 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -5,7 +5,7 @@ hacking<0.11,>=0.10.0 coverage>=3.6 # Apache-2.0 discover # BSD -fixtures>=1.3.1 # Apache-2.0/BSD +fixtures<2.0,>=1.3.1 # Apache-2.0/BSD mox3>=0.7.0 # Apache-2.0 mock>=1.2 # BSD oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0 From 37ec942417c77b3104ff69a8b33a5549fb3d0c98 Mon Sep 17 00:00:00 2001 From: Inessa Vasilevskaya Date: Mon, 11 Apr 2016 17:22:48 +0300 Subject: [PATCH 421/845] Fixes unclear error when no --pool-prefix given --pool-prefix is required on subnetpool creation. refactor: removed unused 'for_create' argument in add_updatable_arguments in subnetpool module. Closes-bug: #1536479 Change-Id: I79573369cb95c0b090e9ebfea9af40485d968e2f --- neutronclient/neutron/v2_0/subnetpool.py | 9 +++++---- neutronclient/tests/unit/test_cli20_subnetpool.py | 13 +++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/neutronclient/neutron/v2_0/subnetpool.py b/neutronclient/neutron/v2_0/subnetpool.py index 138c5215a..ee6dedc57 100644 --- a/neutronclient/neutron/v2_0/subnetpool.py +++ b/neutronclient/neutron/v2_0/subnetpool.py @@ -26,7 +26,7 @@ def _format_prefixes(subnetpool): return subnetpool['prefixes'] -def add_updatable_arguments(parser): +def add_updatable_arguments(parser, for_create=False): parser.add_argument( '--description', help=_('Description of subnetpool.')) @@ -42,6 +42,7 @@ def add_updatable_arguments(parser): parser.add_argument( '--pool-prefix', action='append', dest='prefixes', + required=for_create, help=_('Subnetpool prefixes (This option can be repeated).')) utils.add_boolean_argument( parser, '--is-default', @@ -49,7 +50,7 @@ def add_updatable_arguments(parser): '(True meaning default).')) -def updatable_args2body(parsed_args, body, for_create=True): +def updatable_args2body(parsed_args, body): neutronV20.update_dict(parsed_args, body, ['name', 'prefixes', 'default_prefixlen', 'min_prefixlen', 'max_prefixlen', 'is_default', @@ -79,7 +80,7 @@ class CreateSubnetPool(neutronV20.CreateCommand): resource = 'subnetpool' def add_known_arguments(self, parser): - add_updatable_arguments(parser) + add_updatable_arguments(parser, for_create=True) parser.add_argument( '--shared', action='store_true', @@ -139,7 +140,7 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): body = {} - updatable_args2body(parsed_args, body, for_create=False) + updatable_args2body(parsed_args, body) # Parse and update for "address-scope" option/s if parsed_args.no_address_scope: diff --git a/neutronclient/tests/unit/test_cli20_subnetpool.py b/neutronclient/tests/unit/test_cli20_subnetpool.py index e4e460ab1..63d04f411 100644 --- a/neutronclient/tests/unit/test_cli20_subnetpool.py +++ b/neutronclient/tests/unit/test_cli20_subnetpool.py @@ -119,6 +119,19 @@ def test_create_subnetpool_with_addrscope(self): self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) + def test_create_subnetpool_no_poolprefix(self): + # Should raise an error because --pool-prefix is required + resource = 'subnetpool' + cmd = subnetpool.CreateSubnetPool(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + args = [name] + position_names = ['name'] + position_values = [name] + self.assertRaises(SystemExit, self._test_create_resource, resource, + cmd, name, myid, args, position_names, + position_values) + def test_list_subnetpool_pagination(self): cmd = subnetpool.ListSubnetPool(test_cli20.MyApp(sys.stdout), None) self.mox.StubOutWithMock(subnetpool.ListSubnetPool, "extend_list") From b16bc6cffa31fd97330f462429f31d90093886c6 Mon Sep 17 00:00:00 2001 From: "nick.zhuyj" Date: Fri, 15 Apr 2016 23:18:31 -0500 Subject: [PATCH 422/845] Support sha256 for vpn-ikepolicy and vpn-ipsecpolicy Currently only sha1 is supported for vpn ikepolicy and ipsecpolicy config. One patch is already merged to support sha256 for these 2 configs. Sync neutron command to support sha256. Change-Id: I8c1189bd0eba3de12cd1ef78b795e46efe65c29e Depends-On: I02c80ec3494eb0edef2fdaa5d21ca0c3bbcacac2 Closes-Bug: #1567846 --- neutronclient/neutron/v2_0/vpn/ikepolicy.py | 2 +- neutronclient/neutron/v2_0/vpn/ipsecpolicy.py | 2 +- .../tests/unit/vpn/test_cli20_ikepolicy.py | 29 +++++++++++++++---- .../tests/unit/vpn/test_cli20_ipsecpolicy.py | 29 +++++++++++++++---- 4 files changed, 50 insertions(+), 12 deletions(-) diff --git a/neutronclient/neutron/v2_0/vpn/ikepolicy.py b/neutronclient/neutron/v2_0/vpn/ikepolicy.py index fe41655ee..2a5d91b3b 100644 --- a/neutronclient/neutron/v2_0/vpn/ikepolicy.py +++ b/neutronclient/neutron/v2_0/vpn/ikepolicy.py @@ -49,7 +49,7 @@ def add_known_arguments(self, parser): help=_('Description of the IKE policy')) parser.add_argument( '--auth-algorithm', - default='sha1', choices=['sha1'], + default='sha1', choices=['sha1', 'sha256'], help=_('Authentication algorithm in lowercase. ' 'Default:sha1')) parser.add_argument( diff --git a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py index 8f6296485..5da45158e 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py +++ b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py @@ -53,7 +53,7 @@ def add_known_arguments(self, parser): help=_('Transform protocol in lowercase, default:esp')) parser.add_argument( '--auth-algorithm', - default='sha1', choices=['sha1'], + default='sha1', choices=['sha1', 'sha256'], help=_('Authentication algorithm in lowercase, default:sha1')) parser.add_argument( '--encryption-algorithm', diff --git a/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py b/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py index c6ac8fb78..1dbf5885c 100644 --- a/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py +++ b/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py @@ -25,13 +25,14 @@ class CLITestV20VpnIkePolicyJSON(test_cli20.CLITestV20Base): non_admin_status_resources = ['ikepolicy'] - def test_create_ikepolicy_all_params(self): + def _test_create_ikepolicy_all_params(self, auth='sha1', + expected_exc=None): # vpn-ikepolicy-create all params. resource = 'ikepolicy' cmd = ikepolicy.CreateIKEPolicy(test_cli20.MyApp(sys.stdout), None) name = 'ikepolicy1' description = 'my-ike-policy' - auth_algorithm = 'sha1' + auth_algorithm = auth encryption_algorithm = 'aes-256' ike_version = 'v1' phase1_negotiation_mode = 'main' @@ -67,9 +68,27 @@ def test_create_ikepolicy_all_params(self): }, } - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - extra_body=extra_body) + if not expected_exc: + self._test_create_resource(resource, cmd, name, my_id, args, + position_names, position_values, + extra_body=extra_body) + else: + self.assertRaises( + expected_exc, + self._test_create_resource, + resource, cmd, name, my_id, args, + position_names, position_values, + extra_body=extra_body) + + def test_create_ikepolicy_all_params(self): + self._test_create_ikepolicy_all_params() + + def test_create_ikepolicy_auth_sha256(self): + self._test_create_ikepolicy_all_params(auth='sha256') + + def test_create_ikepolicy_invalid_auth(self): + self._test_create_ikepolicy_all_params(auth='xyz', + expected_exc=SystemExit) def test_create_ikepolicy_with_limited_params(self): # vpn-ikepolicy-create with limited params. diff --git a/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py b/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py index c6b23e19c..c8f70f6b4 100644 --- a/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py +++ b/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py @@ -25,13 +25,14 @@ class CLITestV20VpnIpsecPolicyJSON(test_cli20.CLITestV20Base): non_admin_status_resources = ['ipsecpolicy'] - def test_create_ipsecpolicy_all_params(self): + def _test_create_ipsecpolicy_all_params(self, auth='sha1', + expected_exc=None): # vpn-ipsecpolicy-create all params with dashes. resource = 'ipsecpolicy' cmd = ipsecpolicy.CreateIPsecPolicy(test_cli20.MyApp(sys.stdout), None) name = 'ipsecpolicy1' description = 'first-ipsecpolicy1' - auth_algorithm = 'sha1' + auth_algorithm = auth encryption_algorithm = 'aes-256' encapsulation_mode = 'tunnel' pfs = 'group5' @@ -66,9 +67,27 @@ def test_create_ipsecpolicy_all_params(self): }, } - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - extra_body=extra_body) + if not expected_exc: + self._test_create_resource(resource, cmd, name, my_id, args, + position_names, position_values, + extra_body=extra_body) + else: + self.assertRaises( + expected_exc, + self._test_create_resource, + resource, cmd, name, my_id, args, + position_names, position_values, + extra_body=extra_body) + + def test_create_ipsecpolicy_all_params(self): + self._test_create_ipsecpolicy_all_params() + + def test_create_ipsecpolicy_auth_sha256(self): + self._test_create_ipsecpolicy_all_params(auth='sha256') + + def test_create_ipsecpolicy_invalid_auth(self): + self._test_create_ipsecpolicy_all_params(auth='xyz', + expected_exc=SystemExit) def test_create_ipsecpolicy_with_limited_params(self): # vpn-ipsecpolicy-create with limited params. From 98fc6c5757a18c46eae12159a5b540dec8abc6d6 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Sat, 30 Apr 2016 18:08:33 +0000 Subject: [PATCH 423/845] Updated from global requirements Change-Id: I60b9361df2039a49694a4c967f4d5ca7204fab9c --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 7bee71b8d..5d15463c5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ pbr>=1.6 # Apache-2.0 cliff!=1.16.0,!=1.17.0,>=1.15.0 # Apache-2.0 debtcollector>=1.2.0 # Apache-2.0 -iso8601>=0.1.9 # MIT +iso8601>=0.1.11 # MIT netaddr!=0.7.16,>=0.7.12 # BSD oslo.i18n>=2.1.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 From 04cf26dd4271ae84762cd7ca6672c8c74d66ecf4 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Fri, 6 May 2016 22:22:32 +0000 Subject: [PATCH 424/845] Updated from global requirements Change-Id: I0d11f690a337abac8be439f9c0acc3d7134d767b --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 5d15463c5..8ee707299 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,4 +14,4 @@ keystoneauth1>=2.1.0 # Apache-2.0 requests!=2.9.0,>=2.8.1 # Apache-2.0 simplejson>=2.2.0 # MIT six>=1.9.0 # MIT -Babel!=2.3.0,!=2.3.1,!=2.3.2,!=2.3.3,>=1.3 # BSD +Babel>=2.3.4 # BSD From 2e048fdc94840b1853326829c94ae0917d5b37f5 Mon Sep 17 00:00:00 2001 From: Richard Theis Date: Sat, 7 May 2016 06:37:53 -0500 Subject: [PATCH 425/845] Devref: Add dynamic routing to OSC transition Add dynamic routing commands to OSC transition devref. Change-Id: Ifa8c2ece88e185f932cb812d1393750ce93585e9 Related-Bug: #1521291 --- doc/source/devref/transition_to_osc.rst | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/doc/source/devref/transition_to_osc.rst b/doc/source/devref/transition_to_osc.rst index b12ad8b2c..98aa4377a 100644 --- a/doc/source/devref/transition_to_osc.rst +++ b/doc/source/devref/transition_to_osc.rst @@ -156,21 +156,24 @@ only with feature requests only being made to the ``openstack`` CLI. +===========================+===================+=================================================+ | Core | No | python-openstackclient | +---------------------------+-------------------+-------------------------------------------------+ -| LBaaS v2 | Yes | python-neutronclient | -| | | (under ``neutronclient/osc/v2/lbaas``) | +| Dynamic Routing | Yes | python-neutronclient | +| | | (``neutronclient/osc/v2/dynamic_routing``) | +---------------------------+-------------------+-------------------------------------------------+ -| VPNaaS v2 | Yes | python-neutronclient | -| | | (under ``neutronclient/osc/v2/vpnaas``) | +| FWaaS v1 | N/A | None (deprecated) | +---------------------------+-------------------+-------------------------------------------------+ | FWaaS v2 | Yes | python-neutronclient | -| | | (under ``neutronclient/osc/v2/fwaas``) | +| | | (``neutronclient/osc/v2/fwaas``) | +---------------------------+-------------------+-------------------------------------------------+ | LBaaS v1 | N/A | None (deprecated) | +---------------------------+-------------------+-------------------------------------------------+ -| FWaaS v1 | N/A | None (deprecated) | +| LBaaS v2 | Yes | python-neutronclient | +| | | (``neutronclient/osc/v2/lbaas``) | +---------------------------+-------------------+-------------------------------------------------+ | Other | Yes | Applicable project owning networking resource | +---------------------------+-------------------+-------------------------------------------------+ +| VPNaaS | Yes | python-neutronclient | +| | | (``neutronclient/osc/v2/vpnaas``) | ++---------------------------+-------------------+-------------------------------------------------+ **Important:** The actual name of the command object and/or action in OSC may differ From a065d20ea3213e2cd81272f8bc7f944820dabd17 Mon Sep 17 00:00:00 2001 From: Gary Kotton Date: Mon, 16 May 2016 04:45:29 -0700 Subject: [PATCH 426/845] Address pairs help missing space Add in a missing space at the end of the line for the help message for the address pairs TrivialFix Change-Id: Ib205aafaea7df109f3de920888a0fb0883cdaf3e --- neutronclient/neutron/v2_0/port.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index b68bfbe78..d79551d87 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -210,7 +210,7 @@ def add_arguments_allowedaddresspairs(self, parser): type=utils.str2dict_type( required_keys=['ip_address'], optional_keys=['mac_address']), - help=_('Allowed address pair associated with the port.' + help=_('Allowed address pair associated with the port. ' 'You can repeat this option.')) group_aap.add_argument( '--no-allowed-address-pairs', From 51f07b894f261077257c90d61fc63ce633863c4c Mon Sep 17 00:00:00 2001 From: venkatamahesh Date: Tue, 17 May 2016 10:16:07 +0530 Subject: [PATCH 427/845] Update the home-page with developer documentation Change-Id: Ia289848b66caf07ce3684906b29021711d4d286c --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 9dec54c9b..2e4a3a6e8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -5,7 +5,7 @@ description-file = README.rst author = OpenStack Networking Project author-email = openstack-dev@lists.openstack.org -home-page = http://www.openstack.org/ +home-page = http://docs.openstack.org/developer/python-neutronclient classifier = Environment :: OpenStack Intended Audience :: Developers From 9e4f826166609392ca2a4c64069eb28632ae215d Mon Sep 17 00:00:00 2001 From: Ihar Hrachyshka Date: Wed, 18 May 2016 10:09:51 +0200 Subject: [PATCH 428/845] tests: removed mocking for Client.get_attr_metadata The method is not present since XML support removal: I88b0fdd65a649694252d5ff43a174e75026df5b1 Change-Id: Iaa920a1e9bd37b97ed6f4f5a4d1da05aac20d366 Related-Bug: #1583029 --- neutronclient/tests/unit/test_cli20.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index c5b570983..7a0747b38 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -182,9 +182,6 @@ def _find_resourceid(self, client, resource, name_or_id, cmd_resource=None, parent_id=None): return name_or_id - def _get_attr_metadata(self): - return self.metadata - def setUp(self, plurals=None): """Prepare the test environment.""" super(CLITestV20Base, self).setUp() @@ -202,9 +199,6 @@ def setUp(self, plurals=None): self.useFixture(fixtures.MonkeyPatch( 'neutronclient.neutron.v2_0.find_resourceid_by_id', self._find_resourceid)) - self.useFixture(fixtures.MonkeyPatch( - 'neutronclient.v2_0.client.Client.get_attr_metadata', - self._get_attr_metadata)) self.client = client.Client(token=TOKEN, endpoint_url=self.endurl) self.client.format = self.format From 35ce1a512a92147b0402c5eed9b2aeb1f0f13d87 Mon Sep 17 00:00:00 2001 From: Ihar Hrachyshka Date: Wed, 18 May 2016 09:59:50 +0200 Subject: [PATCH 429/845] Switched from fixtures.MonkeyPatch to mock.patch fixtures 2.0.0 released a new version of monkey patching fixture that triggered issues with patching out class methods in stable/liberty. We already stopped relying on fixtures for monkey patching in neutron: I58d7a750e263e4af54589ace07ac00bec34b553a and switched to mock there. fixtures library is still used for other fixtures, so the dependency stays. Mock library is already in the dependency list, so test-requirements.txt is not touched. Change-Id: If43f3e08ee2235f96b0e5069b0df798a1acb48ea Closes-Bug: #1583029 --- neutronclient/tests/unit/test_cli20.py | 17 ++++++------- .../tests/unit/test_client_extension.py | 24 +++---------------- 2 files changed, 12 insertions(+), 29 deletions(-) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 7a0747b38..19bd29708 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -18,7 +18,7 @@ import itertools import sys -import fixtures +import mock from mox3 import mox from oslo_utils import encodeutils from oslotest import base @@ -192,13 +192,14 @@ def setUp(self, plurals=None): self.mox = mox.Mox() self.endurl = ENDURL self.fake_stdout = FakeStdout() - self.useFixture(fixtures.MonkeyPatch('sys.stdout', self.fake_stdout)) - self.useFixture(fixtures.MonkeyPatch( - 'neutronclient.neutron.v2_0.find_resourceid_by_name_or_id', - self._find_resourceid)) - self.useFixture(fixtures.MonkeyPatch( - 'neutronclient.neutron.v2_0.find_resourceid_by_id', - self._find_resourceid)) + + self.addCleanup(mock.patch.stopall) + mock.patch('sys.stdout', new=self.fake_stdout).start() + mock.patch('neutronclient.neutron.v2_0.find_resourceid_by_name_or_id', + new=self._find_resourceid).start() + mock.patch('neutronclient.neutron.v2_0.find_resourceid_by_id', + new=self._find_resourceid).start() + self.client = client.Client(token=TOKEN, endpoint_url=self.endurl) self.client.format = self.format diff --git a/neutronclient/tests/unit/test_client_extension.py b/neutronclient/tests/unit/test_client_extension.py index 0fccd5e84..973f80068 100644 --- a/neutronclient/tests/unit/test_client_extension.py +++ b/neutronclient/tests/unit/test_client_extension.py @@ -34,15 +34,9 @@ def setUp(self): self._mock_extension_loading() super(CLITestV20ExtensionJSON, self).setUp(plurals={'tags': 'tag'}) - def _create_patch(self, name, func=None): - patcher = mock.patch(name) - thing = patcher.start() - self.addCleanup(patcher.stop) - return thing - def _mock_extension_loading(self): ext_pkg = 'neutronclient.common.extension' - contrib = self._create_patch(ext_pkg + '._discover_via_entry_points') + contrib = mock.patch(ext_pkg + '._discover_via_entry_points').start() contrib.return_value = [("_fox_sockets", fox_sockets)] return contrib @@ -134,15 +128,9 @@ def setUp(self): self._mock_extension_loading() super(CLITestV20ExtensionJSONAlternatePlurals, self).setUp() - def _create_patch(self, name, func=None): - patcher = mock.patch(name) - thing = patcher.start() - self.addCleanup(patcher.stop) - return thing - def _mock_extension_loading(self): ext_pkg = 'neutronclient.common.extension' - contrib = self._create_patch(ext_pkg + '._discover_via_entry_points') + contrib = mock.patch(ext_pkg + '._discover_via_entry_points').start() ip_address = mock.MagicMock() ip_address.IPAddress = self.IPAddress ip_address.IPAddressesList = self.IPAddressesList @@ -193,15 +181,9 @@ def setUp(self): self._mock_extension_loading() super(CLITestV20ExtensionJSONChildResource, self).setUp() - def _create_patch(self, name, func=None): - patcher = mock.patch(name) - thing = patcher.start() - self.addCleanup(patcher.stop) - return thing - def _mock_extension_loading(self): ext_pkg = 'neutronclient.common.extension' - contrib = self._create_patch(ext_pkg + '._discover_via_entry_points') + contrib = mock.patch(ext_pkg + '._discover_via_entry_points').start() child = mock.MagicMock() child.Child = self.Child child.ChildrenList = self.ChildrenList From ea0dfb12a89862db9dd3a9767a936eb1bbaa88d8 Mon Sep 17 00:00:00 2001 From: changzhi Date: Fri, 13 May 2016 12:24:54 +0800 Subject: [PATCH 430/845] Make purge supports dvr router's interface In DVR mode, port's 'device_owner' is 'network:router_interface_distributed' if one router attachs some subnets. This patch makes 'purge' command support clean router's dvr interface. Closes-bug: #1580432 Change-Id: I2d45e297ac11305ae7b065fd5450580f0c2aefed --- neutronclient/neutron/v2_0/purge.py | 4 +++- neutronclient/tests/functional/core/test_purge.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/neutronclient/neutron/v2_0/purge.py b/neutronclient/neutron/v2_0/purge.py index 63f7d2ebe..332a20b32 100644 --- a/neutronclient/neutron/v2_0/purge.py +++ b/neutronclient/neutron/v2_0/purge.py @@ -49,7 +49,9 @@ def _get_resources(self, neutron_client, resource_types, tenant_id): def _delete_resource(self, neutron_client, resource_type, resource): resource_id = resource['id'] if resource_type == 'port': - if resource.get('device_owner', '') == 'network:router_interface': + router_interface_owners = ['network:router_interface', + 'network:router_interface_distributed'] + if resource.get('device_owner', '') in router_interface_owners: body = {'port_id': resource_id} neutron_client.remove_interface_router(resource['device_id'], body) diff --git a/neutronclient/tests/functional/core/test_purge.py b/neutronclient/tests/functional/core/test_purge.py index 91d92a9ef..6aed6e40b 100644 --- a/neutronclient/tests/functional/core/test_purge.py +++ b/neutronclient/tests/functional/core/test_purge.py @@ -116,6 +116,8 @@ def _create_resources(self, name, tenant_id, shared_tenant_id=None): def _verify_deletion(self, resources, resource_type): purged = True no_purge_purged = True + router_interface_owners = ['network:router_interface', + 'network:router_interface_distributed'] for row in resources: if resource_type == 'port' and row.get('id', None): port = self.parser.listing(self.neutron('port-show', @@ -123,7 +125,7 @@ def _verify_deletion(self, resources, resource_type): port_dict = {} for row in port: port_dict[row['Field']] = row['Value'] - if port_dict['device_owner'] == 'network:router_interface': + if port_dict['device_owner'] in router_interface_owners: if port_dict['tenant_id'] == 'purge-tenant': purged = False elif port_dict['tenant_id'] == 'no-purge-tenant': From 78d778cdeccb6a24b7f6fa7e3c2eb0891b633701 Mon Sep 17 00:00:00 2001 From: Ihar Hrachyshka Date: Wed, 18 May 2016 10:44:16 +0200 Subject: [PATCH 431/845] Constraint tox targets with upper-constraints.txt Client version pin is maintained in upper-constraints.txt file, so we should replace it in the file with source based specifier before applying it to pip install, otherwise pip fails with: Could not satisfy constraints for 'python-neutronclient': installation from path or url cannot be constrained to a version Change-Id: I33302cdcab980a42c39dc9ec1e430add459615ff Closes-Bug: #1583050 --- tools/tox_install.sh | 55 ++++++++++++++++++++++++++++++++++++++++++++ tox.ini | 9 +++++++- 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100755 tools/tox_install.sh diff --git a/tools/tox_install.sh b/tools/tox_install.sh new file mode 100755 index 000000000..c800a4141 --- /dev/null +++ b/tools/tox_install.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash + +# Client constraint file contains this client version pin that is in conflict +# with installing the client from source. We should replace the version pin in +# the constraints file before applying it for from-source installation. + +ZUUL_CLONER=/usr/zuul-env/bin/zuul-cloner +BRANCH_NAME=master +CLIENT_NAME=python-neutronclient +requirements_installed=$(echo "import openstack_requirements" | python 2>/dev/null ; echo $?) + +set -e + +CONSTRAINTS_FILE=$1 +shift + +install_cmd="pip install" +if [ $CONSTRAINTS_FILE != "unconstrained" ]; then + + mydir=$(mktemp -dt "$CLIENT_NAME-tox_install-XXXXXXX") + localfile=$mydir/upper-constraints.txt + if [[ $CONSTRAINTS_FILE != http* ]]; then + CONSTRAINTS_FILE=file://$CONSTRAINTS_FILE + fi + curl $CONSTRAINTS_FILE -k -o $localfile + install_cmd="$install_cmd -c$localfile" + + if [ $requirements_installed -eq 0 ]; then + echo "ALREADY INSTALLED" > /tmp/tox_install.txt + echo "Requirements already installed; using existing package" + elif [ -x "$ZUUL_CLONER" ]; then + export ZUUL_BRANCH=${ZUUL_BRANCH-$BRANCH} + echo "ZUUL CLONER" > /tmp/tox_install.txt + pushd $mydir + $ZUUL_CLONER --cache-dir \ + /opt/git \ + --branch $BRANCH_NAME \ + git://git.openstack.org \ + openstack/requirements + cd openstack/requirements + $install_cmd -e . + popd + else + echo "PIP HARDCODE" > /tmp/tox_install.txt + if [ -z "$REQUIREMENTS_PIP_LOCATION" ]; then + REQUIREMENTS_PIP_LOCATION="git+https://git.openstack.org/openstack/requirements@$BRANCH_NAME#egg=requirements" + fi + $install_cmd -U -e ${REQUIREMENTS_PIP_LOCATION} + fi + + edit-constraints $localfile -- $CLIENT_NAME "-e file://$PWD#egg=$CLIENT_NAME" +fi + +$install_cmd -U $* +exit $? diff --git a/tox.ini b/tox.ini index 028453f36..92ded104c 100644 --- a/tox.ini +++ b/tox.ini @@ -10,7 +10,8 @@ setenv = VIRTUAL_ENV={envdir} LANGUAGE=en_US:en LC_ALL=C usedevelop = True -install_command = pip install -U {opts} {packages} +install_command = + {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages} deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt # Delete bytecodes from normal directories before running tests. @@ -27,6 +28,8 @@ commands = flake8 distribute = false [testenv:venv] +# TODO(ihrachys): remove once infra supports constraints for this target +install_command = pip install -U {opts} {packages} commands = {posargs} [testenv:functional] @@ -40,6 +43,8 @@ setenv = OS_NEUTRONCLIENT_EXEC_DIR = {envdir}/bin [testenv:cover] +# TODO(ihrachys): remove once infra supports constraints for this target +install_command = pip install -U {opts} {packages} commands = python setup.py test --coverage --coverage-package-name=neutronclient --testr-args='{posargs}' coverage report @@ -48,6 +53,8 @@ commands = commands = sphinx-build -W -b html doc/source doc/build/html [testenv:releasenotes] +# TODO(ihrachys): remove once infra supports constraints for this target +install_command = pip install -U {opts} {packages} commands = sphinx-build -a -E -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html [flake8] From 954375b22725aa17d867194d601e0caf1dc03a4d Mon Sep 17 00:00:00 2001 From: ZhaoBo Date: Thu, 19 May 2016 19:21:14 +0800 Subject: [PATCH 432/845] Update tempest_lib to tempest.lib http://specs.openstack.org/openstack/qa-specs/specs/tempest/reintegrate-tempest-lib.html The tempest-lib project is going to live in the tempest repo. Now tempest.lib had been created, so this patch update them in neutronclient. Change-Id: I25c2309f84a2f06ba0882682d63d2a8be347ae07 Closes-bug: #1583562 --- neutronclient/tests/functional/base.py | 2 +- neutronclient/tests/functional/core/test_clientlib.py | 2 +- neutronclient/tests/functional/core/test_purge.py | 2 +- neutronclient/tests/functional/core/test_readonly_neutron.py | 2 +- test-requirements.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/neutronclient/tests/functional/base.py b/neutronclient/tests/functional/base.py index 0432a5b3d..16bbe2b07 100644 --- a/neutronclient/tests/functional/base.py +++ b/neutronclient/tests/functional/base.py @@ -13,7 +13,7 @@ import os import os_client_config -from tempest_lib.cli import base +from tempest.lib.cli import base def credentials(cloud='devstack-admin'): diff --git a/neutronclient/tests/functional/core/test_clientlib.py b/neutronclient/tests/functional/core/test_clientlib.py index 91fb6be1c..1f5d43f77 100644 --- a/neutronclient/tests/functional/core/test_clientlib.py +++ b/neutronclient/tests/functional/core/test_clientlib.py @@ -14,7 +14,7 @@ from keystoneauth1 import plugin as ksa_plugin from keystoneauth1 import session -from tempest_lib import base +from tempest.lib import base import testtools from neutronclient.common import exceptions diff --git a/neutronclient/tests/functional/core/test_purge.py b/neutronclient/tests/functional/core/test_purge.py index 6aed6e40b..33b8af44c 100644 --- a/neutronclient/tests/functional/core/test_purge.py +++ b/neutronclient/tests/functional/core/test_purge.py @@ -15,7 +15,7 @@ from neutronclient.tests.functional import base -from tempest_lib import exceptions +from tempest.lib import exceptions class PurgeNeutronClientCLITest(base.ClientTestBase): diff --git a/neutronclient/tests/functional/core/test_readonly_neutron.py b/neutronclient/tests/functional/core/test_readonly_neutron.py index 353e5637c..a9de5bd0e 100644 --- a/neutronclient/tests/functional/core/test_readonly_neutron.py +++ b/neutronclient/tests/functional/core/test_readonly_neutron.py @@ -12,7 +12,7 @@ import re -from tempest_lib import exceptions +from tempest.lib import exceptions from neutronclient.tests.functional import base diff --git a/test-requirements.txt b/test-requirements.txt index 76d549411..ba523072b 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -17,4 +17,4 @@ sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 # BSD testrepository>=0.0.18 # Apache-2.0/BSD testtools>=1.4.0 # MIT testscenarios>=0.4 # Apache-2.0/BSD -tempest-lib>=0.14.0 # Apache-2.0 +tempest>=11.0.0 # Apache-2.0 From 53a59e5aa2e838612852328f0cf3e4eb4f65ed43 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Sat, 21 May 2016 15:52:16 +0000 Subject: [PATCH 433/845] Updated from global requirements Change-Id: If7ed53fa20d1f3da2555d771f80c71cf6b3fed88 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8ee707299..965087048 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ oslo.serialization>=1.10.0 # Apache-2.0 oslo.utils>=3.5.0 # Apache-2.0 os-client-config>=1.13.1 # Apache-2.0 keystoneauth1>=2.1.0 # Apache-2.0 -requests!=2.9.0,>=2.8.1 # Apache-2.0 +requests>=2.10.0 # Apache-2.0 simplejson>=2.2.0 # MIT six>=1.9.0 # MIT Babel>=2.3.4 # BSD From 925d44ad6635e1b33e89871b965a155dc2804426 Mon Sep 17 00:00:00 2001 From: Bertrand Lallau Date: Thu, 26 May 2016 09:59:02 +0200 Subject: [PATCH 434/845] Remove unnecessary executable permissions This removes executable permissions on modules not requiring it. Change-Id: I0c6ea48770c36997dc9278f6314e4cbe97db7fa4 --- neutronclient/neutron/v2_0/address_scope.py | 0 neutronclient/neutron/v2_0/auto_allocated_topology.py | 0 neutronclient/neutron/v2_0/bgp/speaker.py | 0 neutronclient/neutron/v2_0/qos/__init__.py | 0 neutronclient/neutron/v2_0/qos/policy.py | 0 neutronclient/tests/unit/qos/__init__.py | 0 neutronclient/tests/unit/qos/test_cli20_policy.py | 0 neutronclient/tests/unit/test_auto_allocated_topology.py | 0 neutronclient/tests/unit/test_cli20_address_scope.py | 0 9 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 neutronclient/neutron/v2_0/address_scope.py mode change 100755 => 100644 neutronclient/neutron/v2_0/auto_allocated_topology.py mode change 100755 => 100644 neutronclient/neutron/v2_0/bgp/speaker.py mode change 100755 => 100644 neutronclient/neutron/v2_0/qos/__init__.py mode change 100755 => 100644 neutronclient/neutron/v2_0/qos/policy.py mode change 100755 => 100644 neutronclient/tests/unit/qos/__init__.py mode change 100755 => 100644 neutronclient/tests/unit/qos/test_cli20_policy.py mode change 100755 => 100644 neutronclient/tests/unit/test_auto_allocated_topology.py mode change 100755 => 100644 neutronclient/tests/unit/test_cli20_address_scope.py diff --git a/neutronclient/neutron/v2_0/address_scope.py b/neutronclient/neutron/v2_0/address_scope.py old mode 100755 new mode 100644 diff --git a/neutronclient/neutron/v2_0/auto_allocated_topology.py b/neutronclient/neutron/v2_0/auto_allocated_topology.py old mode 100755 new mode 100644 diff --git a/neutronclient/neutron/v2_0/bgp/speaker.py b/neutronclient/neutron/v2_0/bgp/speaker.py old mode 100755 new mode 100644 diff --git a/neutronclient/neutron/v2_0/qos/__init__.py b/neutronclient/neutron/v2_0/qos/__init__.py old mode 100755 new mode 100644 diff --git a/neutronclient/neutron/v2_0/qos/policy.py b/neutronclient/neutron/v2_0/qos/policy.py old mode 100755 new mode 100644 diff --git a/neutronclient/tests/unit/qos/__init__.py b/neutronclient/tests/unit/qos/__init__.py old mode 100755 new mode 100644 diff --git a/neutronclient/tests/unit/qos/test_cli20_policy.py b/neutronclient/tests/unit/qos/test_cli20_policy.py old mode 100755 new mode 100644 diff --git a/neutronclient/tests/unit/test_auto_allocated_topology.py b/neutronclient/tests/unit/test_auto_allocated_topology.py old mode 100755 new mode 100644 diff --git a/neutronclient/tests/unit/test_cli20_address_scope.py b/neutronclient/tests/unit/test_cli20_address_scope.py old mode 100755 new mode 100644 From 343e4b186f992c0ec142d4a892f80ed4a208732d Mon Sep 17 00:00:00 2001 From: Dariusz Smigiel Date: Mon, 23 May 2016 12:58:29 -0500 Subject: [PATCH 435/845] Update for API bindings All occurrences of tenant replaced with project (where applicable). Partially Implements blueprint: keystone-v3 Change-Id: I4919745aa59863f99c7740e730d8cbfd91c2f646 --- doc/source/usage/library.rst | 4 +- neutronclient/client.py | 28 ++-- neutronclient/common/clientmanager.py | 18 ++- neutronclient/neutron/client.py | 2 +- .../tests/functional/core/test_clientlib.py | 27 +++- neutronclient/v2_0/client.py | 123 ++++++++++-------- .../notes/keystonev3-7f9ede9c21b30841.yaml | 7 + 7 files changed, 134 insertions(+), 75 deletions(-) create mode 100644 releasenotes/notes/keystonev3-7f9ede9c21b30841.yaml diff --git a/doc/source/usage/library.rst b/doc/source/usage/library.rst index 8b301e385..481a08b45 100644 --- a/doc/source/usage/library.rst +++ b/doc/source/usage/library.rst @@ -33,11 +33,11 @@ First create a client instance. >>> from neutronclient.v2_0 import client >>> username='adminUser' >>> password='secretword' - >>> tenant_name='openstackDemo' + >>> project_name='openstackDemo' >>> auth_url='http://192.168.206.130:5000/v2.0' >>> neutron = client.Client(username=username, ... password=password, - ... tenant_name=tenant_name, + ... project_name=project_name, ... auth_url=auth_url) Now you can call various methods on the client instance. diff --git a/neutronclient/client.py b/neutronclient/client.py index 445d3afb0..d41590c3e 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -21,6 +21,7 @@ import logging import os +import debtcollector.renames from keystoneauth1 import access from keystoneauth1 import adapter import requests @@ -49,8 +50,12 @@ class HTTPClient(object): USER_AGENT = 'python-neutronclient' CONTENT_TYPE = 'application/json' + @debtcollector.renames.renamed_kwarg( + 'tenant_id', 'project_id', replace=True) + @debtcollector.renames.renamed_kwarg( + 'tenant_name', 'project_name', replace=True) def __init__(self, username=None, user_id=None, - tenant_name=None, tenant_id=None, + project_name=None, project_id=None, password=None, auth_url=None, token=None, region_name=None, timeout=None, endpoint_url=None, insecure=False, @@ -61,8 +66,8 @@ def __init__(self, username=None, user_id=None, self.username = username self.user_id = user_id - self.tenant_name = tenant_name - self.tenant_id = tenant_id + self.project_name = project_name + self.project_id = project_id self.password = password self.auth_url = auth_url.rstrip('/') if auth_url else None self.service_type = service_type @@ -199,12 +204,12 @@ def _authenticate_keystone(self): creds = {'username': self.username, 'password': self.password} - if self.tenant_id: + if self.project_id: body = {'auth': {'passwordCredentials': creds, - 'tenantId': self.tenant_id, }, } + 'tenantId': self.project_id, }, } else: body = {'auth': {'passwordCredentials': creds, - 'tenantName': self.tenant_name, }, } + 'tenantName': self.project_name, }, } if self.auth_url is None: raise exceptions.NoAuthURLProvided() @@ -344,10 +349,13 @@ def get_auth_info(self): # FIXME(bklei): Should refactor this to use kwargs and only # explicitly list arguments that are not None. +@debtcollector.renames.renamed_kwarg('tenant_id', 'project_id', replace=True) +@debtcollector.renames.renamed_kwarg( + 'tenant_name', 'project_name', replace=True) def construct_http_client(username=None, user_id=None, - tenant_name=None, - tenant_id=None, + project_name=None, + project_id=None, password=None, auth_url=None, token=None, @@ -376,8 +384,8 @@ def construct_http_client(username=None, # refactor to use kwargs. return HTTPClient(username=username, password=password, - tenant_id=tenant_id, - tenant_name=tenant_name, + project_id=project_id, + project_name=project_name, user_id=user_id, auth_url=auth_url, token=token, diff --git a/neutronclient/common/clientmanager.py b/neutronclient/common/clientmanager.py index 7948cf101..b79ee5b31 100644 --- a/neutronclient/common/clientmanager.py +++ b/neutronclient/common/clientmanager.py @@ -19,6 +19,8 @@ import logging +import debtcollector.renames + from neutronclient import client from neutronclient.neutron import client as neutron_client @@ -47,11 +49,15 @@ class ClientManager(object): # in stable versions) quantum = neutron + @debtcollector.renames.renamed_kwarg( + 'tenant_id', 'project_id', replace=True) + @debtcollector.renames.renamed_kwarg( + 'tenant_name', 'project_name', replace=True) def __init__(self, token=None, url=None, auth_url=None, endpoint_type=None, - tenant_name=None, - tenant_id=None, + project_name=None, + project_id=None, username=None, user_id=None, password=None, @@ -75,8 +81,8 @@ def __init__(self, token=None, url=None, self._service_type = service_type self._service_name = service_name self._endpoint_type = endpoint_type - self._tenant_name = tenant_name - self._tenant_id = tenant_id + self._project_name = project_name + self._project_id = project_id self._username = username self._user_id = user_id self._password = password @@ -99,8 +105,8 @@ def initialize(self): httpclient = client.construct_http_client( username=self._username, user_id=self._user_id, - tenant_name=self._tenant_name, - tenant_id=self._tenant_id, + project_name=self._project_name, + project_id=self._project_id, password=self._password, region_name=self._region_name, auth_url=self._auth_url, diff --git a/neutronclient/neutron/client.py b/neutronclient/neutron/client.py index 5725d1266..7961a4ca2 100644 --- a/neutronclient/neutron/client.py +++ b/neutronclient/neutron/client.py @@ -35,7 +35,7 @@ def make_client(instance): url = instance._url url = url.rstrip("/") client = neutron_client(username=instance._username, - tenant_name=instance._tenant_name, + project_name=instance._project_name, password=instance._password, region_name=instance._region_name, auth_url=instance._auth_url, diff --git a/neutronclient/tests/functional/core/test_clientlib.py b/neutronclient/tests/functional/core/test_clientlib.py index 91fb6be1c..d39cd17ff 100644 --- a/neutronclient/tests/functional/core/test_clientlib.py +++ b/neutronclient/tests/functional/core/test_clientlib.py @@ -36,8 +36,7 @@ def setUp(self): class Libv2HTTPClientTestBase(LibraryTestBase): - def _get_client(self): - + def _setup_creds(self): creds = func_base.credentials() cloud_config = func_base.get_cloud_config() @@ -52,13 +51,29 @@ def _get_client(self): # whether v3 also exists or is configured v2_auth_url = keystone_auth.get_endpoint( ks_session, interface=ksa_plugin.AUTH_INTERFACE, version=(2, 0)) + return v2_auth_url, creds + + +class Libv2HTTPClientTenantTestBase(Libv2HTTPClientTestBase): + def _get_client(self): + v2_auth_url, creds = self._setup_creds() return v2_client.Client(username=creds['username'], password=creds['password'], tenant_name=creds['project_name'], auth_url=v2_auth_url) +class Libv2HTTPClientProjectTestBase(Libv2HTTPClientTestBase): + + def _get_client(self): + v2_auth_url, creds = self._setup_creds() + return v2_client.Client(username=creds['username'], + password=creds['password'], + project_name=creds['project_name'], + auth_url=v2_auth_url) + + class Libv2SessionClientTestBase(LibraryTestBase): def _get_client(self): @@ -92,7 +107,13 @@ def test_post_put_delele_network(self): self.client.show_network(net_id) -class LibraryHTTPClientTest(LibraryTestCase, Libv2HTTPClientTestBase): +class LibraryHTTPClientTenantTest(LibraryTestCase, + Libv2HTTPClientTenantTestBase): + pass + + +class LibraryHTTPClientProjectTest(LibraryTestCase, + Libv2HTTPClientProjectTestBase): pass diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 098614a8c..c157502fd 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -20,6 +20,7 @@ import logging import time +import debtcollector.renames import requests import six.moves.urllib.parse as urlparse from six import string_types @@ -184,8 +185,10 @@ class ClientBase(object): :param string user_id: User ID for authentication. (optional) :param string password: Password for authentication. (optional) :param string token: Token for authentication. (optional) - :param string tenant_name: Tenant name. (optional) - :param string tenant_id: Tenant id. (optional) + :param string tenant_name: DEPRECATED! Use project_name instead. + :param string project_name: Project name. (optional) + :param string tenant_id: DEPRECATED! Use project_id instead. + :param string project_id: Project id. (optional) :param string auth_strategy: 'keystone' by default, 'noauth' for no authentication against keystone. (optional) :param string auth_url: Keystone service endpoint for authorization. @@ -220,7 +223,7 @@ class ClientBase(object): from neutronclient.v2_0 import client neutron = client.Client(username=USER, password=PASS, - tenant_name=TENANT_NAME, + project_name=PROJECT_NAME, auth_url=KEYSTONE_URL) nets = neutron.list_networks() @@ -231,6 +234,8 @@ class ClientBase(object): # This variable should be overridden by a child class. EXTED_PLURALS = {} + @debtcollector.renames.renamed_kwarg( + 'tenant_id', 'project_id', replace=True) def __init__(self, **kwargs): """Initialize a new client for the Neutron v2.0 API.""" super(ClientBase, self).__init__() @@ -257,7 +262,7 @@ def _handle_fault_response(self, status_code, response_body, resp): exception_handler_v20(status_code, error_body) def do_request(self, method, action, body=None, headers=None, params=None): - # Add format and tenant_id + # Add format and project_id action += ".%s" % self.format action = self.action_prefix + action if isinstance(params, dict) and params: @@ -584,24 +589,30 @@ def delete_ext(self, path, id): return self.delete(path % id) def get_quotas_tenant(self, **_params): - """Fetch tenant info for following quota operation.""" + """Fetch project info for following quota operation.""" return self.get(self.quota_path % 'tenant', params=_params) def list_quotas(self, **_params): - """Fetch all tenants' quotas.""" + """Fetch all projects' quotas.""" return self.get(self.quotas_path, params=_params) - def show_quota(self, tenant_id, **_params): - """Fetch information of a certain tenant's quotas.""" - return self.get(self.quota_path % (tenant_id), params=_params) + @debtcollector.renames.renamed_kwarg( + 'tenant_id', 'project_id', replace=True) + def show_quota(self, project_id, **_params): + """Fetch information of a certain project's quotas.""" + return self.get(self.quota_path % (project_id), params=_params) - def update_quota(self, tenant_id, body=None): - """Update a tenant's quotas.""" - return self.put(self.quota_path % (tenant_id), body=body) + @debtcollector.renames.renamed_kwarg( + 'tenant_id', 'project_id', replace=True) + def update_quota(self, project_id, body=None): + """Update a project's quotas.""" + return self.put(self.quota_path % (project_id), body=body) - def delete_quota(self, tenant_id): - """Delete the specified tenant's quota values.""" - return self.delete(self.quota_path % (tenant_id)) + @debtcollector.renames.renamed_kwarg( + 'tenant_id', 'project_id', replace=True) + def delete_quota(self, project_id): + """Delete the specified project's quota values.""" + return self.delete(self.quota_path % (project_id)) def list_extensions(self, **_params): """Fetch a list of all extensions on server side.""" @@ -612,7 +623,7 @@ def show_extension(self, ext_alias, **_params): return self.get(self.extension_path % ext_alias, params=_params) def list_ports(self, retrieve_all=True, **_params): - """Fetches a list of all ports for a tenant.""" + """Fetches a list of all ports for a project.""" # Pass filters in "params" argument to do_request return self.list('ports', self.ports_path, retrieve_all, **_params) @@ -634,7 +645,7 @@ def delete_port(self, port): return self.delete(self.port_path % (port)) def list_networks(self, retrieve_all=True, **_params): - """Fetches a list of all networks for a tenant.""" + """Fetches a list of all networks for a project.""" # Pass filters in "params" argument to do_request return self.list('networks', self.networks_path, retrieve_all, **_params) @@ -656,7 +667,7 @@ def delete_network(self, network): return self.delete(self.network_path % (network)) def list_subnets(self, retrieve_all=True, **_params): - """Fetches a list of all subnets for a tenant.""" + """Fetches a list of all subnets for a project.""" return self.list('subnets', self.subnets_path, retrieve_all, **_params) @@ -677,7 +688,7 @@ def delete_subnet(self, subnet): return self.delete(self.subnet_path % (subnet)) def list_subnetpools(self, retrieve_all=True, **_params): - """Fetches a list of all subnetpools for a tenant.""" + """Fetches a list of all subnetpools for a project.""" return self.list('subnetpools', self.subnetpools_path, retrieve_all, **_params) @@ -698,7 +709,7 @@ def delete_subnetpool(self, subnetpool): return self.delete(self.subnetpool_path % (subnetpool)) def list_routers(self, retrieve_all=True, **_params): - """Fetches a list of all routers for a tenant.""" + """Fetches a list of all routers for a project.""" # Pass filters in "params" argument to do_request return self.list('routers', self.routers_path, retrieve_all, **_params) @@ -720,7 +731,7 @@ def delete_router(self, router): return self.delete(self.router_path % (router)) def list_address_scopes(self, retrieve_all=True, **_params): - """Fetches a list of all address scopes for a tenant.""" + """Fetches a list of all address scopes for a project.""" return self.list('address_scopes', self.address_scopes_path, retrieve_all, **_params) @@ -762,7 +773,7 @@ def remove_gateway_router(self, router): body={'router': {'external_gateway_info': {}}}) def list_floatingips(self, retrieve_all=True, **_params): - """Fetches a list of all floatingips for a tenant.""" + """Fetches a list of all floatingips for a project.""" # Pass filters in "params" argument to do_request return self.list('floatingips', self.floatingips_path, retrieve_all, **_params) @@ -793,7 +804,7 @@ def update_security_group(self, security_group, body=None): security_group, body=body) def list_security_groups(self, retrieve_all=True, **_params): - """Fetches a list of all security groups for a tenant.""" + """Fetches a list of all security groups for a project.""" return self.list('security_groups', self.security_groups_path, retrieve_all, **_params) @@ -816,7 +827,7 @@ def delete_security_group_rule(self, security_group_rule): (security_group_rule)) def list_security_group_rules(self, retrieve_all=True, **_params): - """Fetches a list of all security group rules for a tenant.""" + """Fetches a list of all security group rules for a project.""" return self.list('security_group_rules', self.security_group_rules_path, retrieve_all, **_params) @@ -827,7 +838,7 @@ def show_security_group_rule(self, security_group_rule, **_params): params=_params) def list_endpoint_groups(self, retrieve_all=True, **_params): - """Fetches a list of all VPN endpoint groups for a tenant.""" + """Fetches a list of all VPN endpoint groups for a project.""" return self.list('endpoint_groups', self.endpoint_groups_path, retrieve_all, **_params) @@ -849,7 +860,7 @@ def delete_endpoint_group(self, endpoint_group): return self.delete(self.endpoint_group_path % endpoint_group) def list_vpnservices(self, retrieve_all=True, **_params): - """Fetches a list of all configured VPN services for a tenant.""" + """Fetches a list of all configured VPN services for a project.""" return self.list('vpnservices', self.vpnservices_path, retrieve_all, **_params) @@ -870,7 +881,7 @@ def delete_vpnservice(self, vpnservice): return self.delete(self.vpnservice_path % (vpnservice)) def list_ipsec_site_connections(self, retrieve_all=True, **_params): - """Fetches all configured IPsecSiteConnections for a tenant.""" + """Fetches all configured IPsecSiteConnections for a project.""" return self.list('ipsec_site_connections', self.ipsec_site_connections_path, retrieve_all, @@ -897,7 +908,7 @@ def delete_ipsec_site_connection(self, ipsecsite_conn): return self.delete(self.ipsec_site_connection_path % (ipsecsite_conn)) def list_ikepolicies(self, retrieve_all=True, **_params): - """Fetches a list of all configured IKEPolicies for a tenant.""" + """Fetches a list of all configured IKEPolicies for a project.""" return self.list('ikepolicies', self.ikepolicies_path, retrieve_all, **_params) @@ -918,7 +929,7 @@ def delete_ikepolicy(self, ikepolicy): return self.delete(self.ikepolicy_path % (ikepolicy)) def list_ipsecpolicies(self, retrieve_all=True, **_params): - """Fetches a list of all configured IPsecPolicies for a tenant.""" + """Fetches a list of all configured IPsecPolicies for a project.""" return self.list('ipsecpolicies', self.ipsecpolicies_path, retrieve_all, @@ -941,7 +952,7 @@ def delete_ipsecpolicy(self, ipsecpolicy): return self.delete(self.ipsecpolicy_path % (ipsecpolicy)) def list_loadbalancers(self, retrieve_all=True, **_params): - """Fetches a list of all loadbalancers for a tenant.""" + """Fetches a list of all loadbalancers for a project.""" return self.list('loadbalancers', self.lbaas_loadbalancers_path, retrieve_all, **_params) @@ -975,7 +986,7 @@ def retrieve_loadbalancer_status(self, loadbalancer, **_params): params=_params) def list_listeners(self, retrieve_all=True, **_params): - """Fetches a list of all lbaas_listeners for a tenant.""" + """Fetches a list of all lbaas_listeners for a project.""" return self.list('listeners', self.lbaas_listeners_path, retrieve_all, **_params) @@ -1044,7 +1055,7 @@ def delete_lbaas_l7rule(self, l7rule, l7policy): return self.delete(self.lbaas_l7rule_path % (l7policy, l7rule)) def list_lbaas_pools(self, retrieve_all=True, **_params): - """Fetches a list of all lbaas_pools for a tenant.""" + """Fetches a list of all lbaas_pools for a project.""" return self.list('pools', self.lbaas_pools_path, retrieve_all, **_params) @@ -1067,7 +1078,7 @@ def delete_lbaas_pool(self, lbaas_pool): return self.delete(self.lbaas_pool_path % (lbaas_pool)) def list_lbaas_healthmonitors(self, retrieve_all=True, **_params): - """Fetches a list of all lbaas_healthmonitors for a tenant.""" + """Fetches a list of all lbaas_healthmonitors for a project.""" return self.list('healthmonitors', self.lbaas_healthmonitors_path, retrieve_all, **_params) @@ -1091,12 +1102,12 @@ def delete_lbaas_healthmonitor(self, lbaas_healthmonitor): (lbaas_healthmonitor)) def list_lbaas_loadbalancers(self, retrieve_all=True, **_params): - """Fetches a list of all lbaas_loadbalancers for a tenant.""" + """Fetches a list of all lbaas_loadbalancers for a project.""" return self.list('loadbalancers', self.lbaas_loadbalancers_path, retrieve_all, **_params) def list_lbaas_members(self, lbaas_pool, retrieve_all=True, **_params): - """Fetches a list of all lbaas_members for a tenant.""" + """Fetches a list of all lbaas_members for a project.""" return self.list('members', self.lbaas_members_path % lbaas_pool, retrieve_all, **_params) @@ -1119,7 +1130,7 @@ def delete_lbaas_member(self, lbaas_member, lbaas_pool): return self.delete(self.lbaas_member_path % (lbaas_pool, lbaas_member)) def list_vips(self, retrieve_all=True, **_params): - """Fetches a list of all load balancer vips for a tenant.""" + """Fetches a list of all load balancer vips for a project.""" # Pass filters in "params" argument to do_request return self.list('vips', self.vips_path, retrieve_all, **_params) @@ -1141,7 +1152,7 @@ def delete_vip(self, vip): return self.delete(self.vip_path % (vip)) def list_pools(self, retrieve_all=True, **_params): - """Fetches a list of all load balancer pools for a tenant.""" + """Fetches a list of all load balancer pools for a project.""" # Pass filters in "params" argument to do_request return self.list('pools', self.pools_path, retrieve_all, **_params) @@ -1167,7 +1178,7 @@ def retrieve_pool_stats(self, pool, **_params): return self.get(self.pool_path_stats % (pool), params=_params) def list_members(self, retrieve_all=True, **_params): - """Fetches a list of all load balancer members for a tenant.""" + """Fetches a list of all load balancer members for a project.""" # Pass filters in "params" argument to do_request return self.list('members', self.members_path, retrieve_all, **_params) @@ -1189,7 +1200,9 @@ def delete_member(self, member): return self.delete(self.member_path % (member)) def list_health_monitors(self, retrieve_all=True, **_params): - """Fetches a list of all load balancer health monitors for a tenant.""" + """Fetches a list of all load balancer health monitors for a project. + + """ # Pass filters in "params" argument to do_request return self.list('health_monitors', self.health_monitors_path, retrieve_all, **_params) @@ -1227,7 +1240,7 @@ def create_qos_queue(self, body=None): return self.post(self.qos_queues_path, body=body) def list_qos_queues(self, **_params): - """Fetches a list of all queues for a tenant.""" + """Fetches a list of all queues for a project.""" return self.get(self.qos_queues_path, params=_params) def show_qos_queue(self, queue, **_params): @@ -1364,7 +1377,7 @@ def list_bgp_speaker_on_dragent(self, bgp_dragent, **_params): % bgp_dragent, params=_params) def list_firewall_rules(self, retrieve_all=True, **_params): - """Fetches a list of all firewall rules for a tenant.""" + """Fetches a list of all firewall rules for a project.""" # Pass filters in "params" argument to do_request return self.list('firewall_rules', self.firewall_rules_path, @@ -1388,7 +1401,7 @@ def delete_firewall_rule(self, firewall_rule): return self.delete(self.firewall_rule_path % (firewall_rule)) def list_firewall_policies(self, retrieve_all=True, **_params): - """Fetches a list of all firewall policies for a tenant.""" + """Fetches a list of all firewall policies for a project.""" # Pass filters in "params" argument to do_request return self.list('firewall_policies', self.firewall_policies_path, @@ -1423,7 +1436,7 @@ def firewall_policy_remove_rule(self, firewall_policy, body=None): body=body) def list_firewalls(self, retrieve_all=True, **_params): - """Fetches a list of all firewalls for a tenant.""" + """Fetches a list of all firewalls for a project.""" # Pass filters in "params" argument to do_request return self.list('firewalls', self.firewalls_path, retrieve_all, @@ -1486,7 +1499,7 @@ def delete_metering_label(self, label): return self.delete(self.metering_label_path % (label)) def list_metering_labels(self, retrieve_all=True, **_params): - """Fetches a list of all metering labels for a tenant.""" + """Fetches a list of all metering labels for a project.""" return self.list('metering_labels', self.metering_labels_path, retrieve_all, **_params) @@ -1523,7 +1536,7 @@ def update_rbac_policy(self, rbac_policy_id, body=None): return self.put(self.rbac_policy_path % rbac_policy_id, body=body) def list_rbac_policies(self, retrieve_all=True, **_params): - """Fetch a list of all RBAC policies for a tenant.""" + """Fetch a list of all RBAC policies for a project.""" return self.list('rbac_policies', self.rbac_policies_path, retrieve_all, **_params) @@ -1537,7 +1550,7 @@ def delete_rbac_policy(self, rbac_policy_id): return self.delete(self.rbac_policy_path % rbac_policy_id) def list_qos_policies(self, retrieve_all=True, **_params): - """Fetches a list of all qos policies for a tenant.""" + """Fetches a list of all qos policies for a project.""" # Pass filters in "params" argument to do_request return self.list('policies', self.qos_policies_path, retrieve_all, **_params) @@ -1628,7 +1641,7 @@ def delete_flavor(self, flavor): return self.delete(self.flavor_path % (flavor)) def list_flavors(self, retrieve_all=True, **_params): - """Fetches a list of all Neutron service flavors for a tenant.""" + """Fetches a list of all Neutron service flavors for a project.""" return self.list('flavors', self.flavors_path, retrieve_all, **_params) @@ -1678,18 +1691,22 @@ def list_availability_zones(self, retrieve_all=True, **_params): return self.list('availability_zones', self.availability_zones_path, retrieve_all, **_params) - def get_auto_allocated_topology(self, tenant_id, **_params): - """Fetch information about a tenant's auto-allocated topology.""" + @debtcollector.renames.renamed_kwarg( + 'tenant_id', 'project_id', replace=True) + def get_auto_allocated_topology(self, project_id, **_params): + """Fetch information about a project's auto-allocated topology.""" return self.get( - self.auto_allocated_topology_path % tenant_id, + self.auto_allocated_topology_path % project_id, params=_params) - def validate_auto_allocated_topology_requirements(self, tenant_id): + @debtcollector.renames.renamed_kwarg( + 'tenant_id', 'project_id', replace=True) + def validate_auto_allocated_topology_requirements(self, project_id): """Validate requirements for getting an auto-allocated topology.""" - return self.get_auto_allocated_topology(tenant_id, fields=['dry-run']) + return self.get_auto_allocated_topology(project_id, fields=['dry-run']) def list_bgp_speakers(self, retrieve_all=True, **_params): - """Fetches a list of all BGP speakers for a tenant.""" + """Fetches a list of all BGP speakers for a project.""" return self.list('bgp_speakers', self.bgp_speakers_path, retrieve_all, **_params) diff --git a/releasenotes/notes/keystonev3-7f9ede9c21b30841.yaml b/releasenotes/notes/keystonev3-7f9ede9c21b30841.yaml new file mode 100644 index 000000000..507259663 --- /dev/null +++ b/releasenotes/notes/keystonev3-7f9ede9c21b30841.yaml @@ -0,0 +1,7 @@ +--- +deprecations: + - | + Keystone v3 support for CLI + + * Using 'tenant_id' and 'tenant_name' arguments in API bindings is + deprecated. Use 'project_id' and 'project_name' arguments instead. From bbb7a88df44bd7a49c04adb13458d5e20f1a51ac Mon Sep 17 00:00:00 2001 From: xiaosheng Date: Sat, 28 May 2016 16:29:39 +0800 Subject: [PATCH 436/845] Trivial: ignore openstack/common in flake8 exclude list The directory openstack/common doesn't exist any more. So remove it from flake8 exclude list. Change-Id: I04da2e6b9ed81a82bcee5e0e2510fd451dd2cb07 --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 92ded104c..a20951fec 100644 --- a/tox.ini +++ b/tox.ini @@ -59,4 +59,4 @@ commands = sphinx-build -a -E -d releasenotes/build/doctrees -b html releasenote [flake8] show-source = true -exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,tools +exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,tools From 81a3d1fc1b782231e4226e42e833bbfe21d28884 Mon Sep 17 00:00:00 2001 From: Gary Kotton Date: Sun, 22 May 2016 05:39:18 -0700 Subject: [PATCH 437/845] Add in missing translations A translation was missing for a neutron output when a exception occurred with the client. In addition to this there were a number of help translations missing in the same file. Change-Id: I9d7f099564dd158f2554c066cc592fb0a3a30429 --- neutronclient/shell.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/neutronclient/shell.py b/neutronclient/shell.py index a0089ddd1..d880c657c 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -622,10 +622,10 @@ def _append_global_identity_args(self, parser): '--os-project-name', metavar='', default=utils.env('OS_PROJECT_NAME'), - help='Another way to specify tenant name. ' - 'This option is mutually exclusive with ' - ' --os-tenant-name. ' - 'Defaults to env[OS_PROJECT_NAME].') + help=_('Another way to specify tenant name. ' + 'This option is mutually exclusive with ' + ' --os-tenant-name. ' + 'Defaults to env[OS_PROJECT_NAME].')) parser.add_argument( '--os_tenant_name', @@ -641,10 +641,10 @@ def _append_global_identity_args(self, parser): '--os-project-id', metavar='', default=utils.env('OS_PROJECT_ID'), - help='Another way to specify tenant ID. ' - 'This option is mutually exclusive with ' - ' --os-tenant-id. ' - 'Defaults to env[OS_PROJECT_ID].') + help=_('Another way to specify tenant ID. ' + 'This option is mutually exclusive with ' + ' --os-tenant-id. ' + 'Defaults to env[OS_PROJECT_ID].')) parser.add_argument( '--os-username', metavar='', @@ -667,8 +667,8 @@ def _append_global_identity_args(self, parser): '--os-user-domain-id', metavar='', default=utils.env('OS_USER_DOMAIN_ID'), - help='OpenStack user domain ID. ' - 'Defaults to env[OS_USER_DOMAIN_ID].') + help=_('OpenStack user domain ID. ' + 'Defaults to env[OS_USER_DOMAIN_ID].')) parser.add_argument( '--os_user_domain_id', @@ -678,8 +678,8 @@ def _append_global_identity_args(self, parser): '--os-user-domain-name', metavar='', default=utils.env('OS_USER_DOMAIN_NAME'), - help='OpenStack user domain name. ' - 'Defaults to env[OS_USER_DOMAIN_NAME].') + help=_('OpenStack user domain name. ' + 'Defaults to env[OS_USER_DOMAIN_NAME].')) parser.add_argument( '--os_user_domain_name', @@ -697,13 +697,13 @@ def _append_global_identity_args(self, parser): '--os-project-domain-id', metavar='', default=utils.env('OS_PROJECT_DOMAIN_ID'), - help='Defaults to env[OS_PROJECT_DOMAIN_ID].') + help=_('Defaults to env[OS_PROJECT_DOMAIN_ID].')) parser.add_argument( '--os-project-domain-name', metavar='', default=utils.env('OS_PROJECT_DOMAIN_NAME'), - help='Defaults to env[OS_PROJECT_DOMAIN_NAME].') + help=_('Defaults to env[OS_PROJECT_DOMAIN_NAME].')) parser.add_argument( '--os-cert', @@ -987,7 +987,7 @@ def main(argv=sys.argv[1:]): return NeutronShell(NEUTRON_API_VERSION).run( list(map(encodeutils.safe_decode, argv))) except KeyboardInterrupt: - print("... terminating neutron client", file=sys.stderr) + print(_("... terminating neutron client"), file=sys.stderr) return 130 except exc.NeutronClientException: return 1 From 6d5356a375e2a8bea2c1ed28de3afce7d429653c Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Thu, 2 Jun 2016 21:11:39 +0000 Subject: [PATCH 438/845] Updated from global requirements Change-Id: I94641c2e8629df1225e37e53c22c3d80295c9f60 --- requirements.txt | 2 +- test-requirements.txt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index 965087048..b71396c56 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ iso8601>=0.1.11 # MIT netaddr!=0.7.16,>=0.7.12 # BSD oslo.i18n>=2.1.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 -oslo.utils>=3.5.0 # Apache-2.0 +oslo.utils>=3.11.0 # Apache-2.0 os-client-config>=1.13.1 # Apache-2.0 keystoneauth1>=2.1.0 # Apache-2.0 requests>=2.10.0 # Apache-2.0 diff --git a/test-requirements.txt b/test-requirements.txt index 76d549411..d0da89060 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -5,9 +5,9 @@ hacking<0.11,>=0.10.0 coverage>=3.6 # Apache-2.0 discover # BSD -fixtures<2.0,>=1.3.1 # Apache-2.0/BSD +fixtures>=3.0.0 # Apache-2.0/BSD mox3>=0.7.0 # Apache-2.0 -mock>=1.2 # BSD +mock>=2.0 # BSD oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 python-subunit>=0.0.18 # Apache-2.0/BSD From 521ff7cee562df5105c6404fadb261092af7e342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=82awek=20Kap=C5=82o=C5=84ski?= Date: Thu, 9 Jun 2016 20:39:03 +0000 Subject: [PATCH 439/845] Add no-shared option to qos-policy-update command QoS policy can now be updated to be not shared with other tenants if it was shared before. To set QoS policy as not shared option "--no-shared" should be passed to qos-policy-update command. Option '--shared' works still like it was before so this change is backward compatible. Change-Id: I18d32813b3c59cbadfe1f4a1b9ba06725a1d7bb8 Closes-Bug: #1590942 --- neutronclient/neutron/v2_0/qos/policy.py | 13 ++++++-- .../tests/unit/qos/test_cli20_policy.py | 30 +++++++++++++++++++ ...to-qos-policy-update-56ac41fb3af7e309.yaml | 6 ++++ 3 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 releasenotes/notes/add-no-shared-option-to-qos-policy-update-56ac41fb3af7e309.yaml diff --git a/neutronclient/neutron/v2_0/qos/policy.py b/neutronclient/neutron/v2_0/qos/policy.py index 047888aa8..53805284b 100644 --- a/neutronclient/neutron/v2_0/qos/policy.py +++ b/neutronclient/neutron/v2_0/qos/policy.py @@ -126,11 +126,17 @@ def add_known_arguments(self, parser): parser.add_argument( '--description', help=_('Description of the QoS policy.')) - parser.add_argument( + shared_group = parser.add_mutually_exclusive_group() + shared_group.add_argument( '--shared', action='store_true', help=_('Accessible by other tenants. ' 'Set shared to True (default is False).')) + shared_group.add_argument( + '--no-shared', + action='store_true', + help=_('Not accessible by other tenants. ' + 'Set shared to False.')) def args2body(self, parsed_args): body = {} @@ -139,7 +145,10 @@ def args2body(self, parsed_args): if parsed_args.description: body['description'] = parsed_args.description if parsed_args.shared: - body['shared'] = parsed_args.shared + body['shared'] = True + if parsed_args.no_shared: + body['shared'] = False + return {self.resource: body} diff --git a/neutronclient/tests/unit/qos/test_cli20_policy.py b/neutronclient/tests/unit/qos/test_cli20_policy.py index ab6cb8fbe..b1db866e4 100644 --- a/neutronclient/tests/unit/qos/test_cli20_policy.py +++ b/neutronclient/tests/unit/qos/test_cli20_policy.py @@ -104,6 +104,36 @@ def test_update_policy_description(self): {'description': 'newdesc', }, cmd_resource=self.cmd_res) + def test_update_policy_to_shared(self): + # policy-update myid --shared + cmd = policy.UpdateQoSPolicy(test_cli20.MyApp(sys.stdout), + None) + self._test_update_resource(self.res, cmd, 'myid', + ['myid', '--shared'], + {'shared': True, }, + cmd_resource=self.cmd_res) + + def test_update_policy_to_no_shared(self): + # policy-update myid --no-shared + cmd = policy.UpdateQoSPolicy(test_cli20.MyApp(sys.stdout), + None) + self._test_update_resource(self.res, cmd, 'myid', + ['myid', '--no-shared'], + {'shared': False, }, + cmd_resource=self.cmd_res) + + def test_update_policy_to_shared_no_shared_together(self): + # policy-update myid --shared --no-shared + cmd = policy.UpdateQoSPolicy(test_cli20.MyApp(sys.stdout), + None) + self.assertRaises( + SystemExit, + self._test_update_resource, + self.res, cmd, 'myid', + ['myid', '--shared', '--no-shared'], {}, + cmd_resource=self.cmd_res + ) + def test_list_policies(self): # qos-policy-list. cmd = policy.ListQoSPolicy(test_cli20.MyApp(sys.stdout), diff --git a/releasenotes/notes/add-no-shared-option-to-qos-policy-update-56ac41fb3af7e309.yaml b/releasenotes/notes/add-no-shared-option-to-qos-policy-update-56ac41fb3af7e309.yaml new file mode 100644 index 000000000..0c34f5eeb --- /dev/null +++ b/releasenotes/notes/add-no-shared-option-to-qos-policy-update-56ac41fb3af7e309.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + CLI support to set QoS policy as not shared if it was shared before. + The ``qos-policy-update`` command include a ``--no-shared`` option. + Closes `bug 1590942 `_. From 5f079feeaf0a7b3e39b8f83b2da3a22daa99cec6 Mon Sep 17 00:00:00 2001 From: ricolin Date: Thu, 15 Oct 2015 17:49:39 +0800 Subject: [PATCH 440/845] improve readme contents Add more information in README.rst Change-Id: Iae60a780828015c6a5919405cdcd31010b386d26 --- README.rst | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/README.rst b/README.rst index 0fc449a4b..cceea78f5 100644 --- a/README.rst +++ b/README.rst @@ -1,14 +1,31 @@ Python bindings to the Neutron API ================================== +.. image:: https://img.shields.io/pypi/v/python-neutronclient.svg + :target: https://pypi.python.org/pypi/python-neutronclient/ + :alt: Latest Version + +.. image:: https://img.shields.io/pypi/dm/python-neutronclient.svg + :target: https://pypi.python.org/pypi/python-neutronclient/ + :alt: Downloads + This is a client library for Neutron built on the Neutron API. It provides a Python API (the ``neutronclient`` module) and a command-line tool (``neutron``). -Development takes place via the usual OpenStack processes as outlined in the -`developer guide `_. - * License: Apache License, Version 2.0 -* Documentation: http://docs.openstack.org/developer/python-neutronclient -* Source: http://git.openstack.org/cgit/openstack/python-neutronclient -* Bugs: http://bugs.launchpad.net/python-neutronclient +* `PyPi`_ - package installation +* `Online Documentation`_ +* `Launchpad project`_ - release management +* `Blueprints`_ - feature specifications +* `Bugs`_ - issue tracking +* `Source`_ +* `Developer's Guide`_ + +.. _PyPi: https://pypi.python.org/pypi/python-neutronclient +.. _Online Documentation: http://docs.openstack.org/developer/python-neutronclient +.. _Launchpad project: https://launchpad.net/python-neutronclient +.. _Blueprints: https://blueprints.launchpad.net/python-neutronclient +.. _Bugs: https://bugs.launchpad.net/python-neutronclient +.. _Source: https://git.openstack.org/cgit/openstack/python-neutronclient +.. _Developer's Guide: http://docs.openstack.org/infra/manual/developers.html From 8fe56dfa4c5e7dfb018675232b378b0f23564fe3 Mon Sep 17 00:00:00 2001 From: Hong Hui Xiao Date: Thu, 12 May 2016 13:30:14 +0000 Subject: [PATCH 441/845] Add segment as an attribute to subnet in client The api change for https://review.openstack.org/#/c/288774/20 Change-Id: I352bf7660f16ca6be6b694d490e5dbb1c6828e12 Partially-Implements: blueprint routed-networks --- neutronclient/neutron/v2_0/subnet.py | 6 ++++++ neutronclient/tests/unit/test_cli20_subnet.py | 17 +++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/neutronclient/neutron/v2_0/subnet.py b/neutronclient/neutron/v2_0/subnet.py index 952b65245..acec7e06a 100644 --- a/neutronclient/neutron/v2_0/subnet.py +++ b/neutronclient/neutron/v2_0/subnet.py @@ -201,6 +201,9 @@ def add_known_arguments(self, parser): parser.add_argument( '--prefixlen', metavar='PREFIX_LENGTH', help=_('Prefix length for subnet allocation from subnetpool.')) + parser.add_argument( + '--segment', metavar='SEGMENT', + help=_('ID of segment with which this subnet will be associated.')) def args2body(self, parsed_args): _network_id = neutronV20.find_resourceid_by_name_or_id( @@ -212,6 +215,9 @@ def args2body(self, parsed_args): ip_version = parsed_args.ip_version if parsed_args.use_default_subnetpool: body['use_default_subnetpool'] = True + if parsed_args.segment: + body['segment_id'] = neutronV20.find_resourceid_by_name_or_id( + self.get_client(), 'segment', parsed_args.segment) if parsed_args.subnetpool: if parsed_args.subnetpool == 'None': _subnetpool_id = None diff --git a/neutronclient/tests/unit/test_cli20_subnet.py b/neutronclient/tests/unit/test_cli20_subnet.py index 1a9abcd0d..d74aa9d5c 100644 --- a/neutronclient/tests/unit/test_cli20_subnet.py +++ b/neutronclient/tests/unit/test_cli20_subnet.py @@ -77,6 +77,23 @@ def test_create_subnet_with_no_gateway(self): self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) + def test_create_subnet_with_segment(self): + # Create subnet: --segment segment netid cidr. + resource = 'subnet' + cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + netid = 'netid' + cidr = '10.10.10.0/24' + segment = 'segment' + args = ['--segment', segment, netid, cidr, + '--description', 'cave'] + position_names = ['ip_version', 'network_id', 'cidr', 'segment_id'] + position_values = [4, netid, cidr, segment] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values, + description='cave') + def test_create_subnet_with_bad_gateway_option(self): # Create sbunet: --no-gateway netid cidr. resource = 'subnet' From 8585c140de05af603b6b20c17a0d02c9b0efb063 Mon Sep 17 00:00:00 2001 From: zhurong Date: Wed, 8 Jun 2016 04:37:41 -0400 Subject: [PATCH 442/845] Trivial Fix: Fix typo Change-Id: I05f5abf619016a48babedf1f108430d470d8870e --- neutronclient/v2_0/client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index c157502fd..5504fd1dd 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -1117,11 +1117,11 @@ def show_lbaas_member(self, lbaas_member, lbaas_pool, **_params): params=_params) def create_lbaas_member(self, lbaas_pool, body=None): - """Creates an lbaas_member.""" + """Creates a lbaas_member.""" return self.post(self.lbaas_members_path % lbaas_pool, body=body) def update_lbaas_member(self, lbaas_member, lbaas_pool, body=None): - """Updates a lbaas_healthmonitor.""" + """Updates a lbaas_member.""" return self.put(self.lbaas_member_path % (lbaas_pool, lbaas_member), body=body) From c5c705120a3e9d45e9d868f728531316029284a7 Mon Sep 17 00:00:00 2001 From: dongwenshuai Date: Wed, 15 Jun 2016 17:46:56 +0800 Subject: [PATCH 443/845] Fix the problem of "qos-dscp-marking-rule-show" Add an arg "**_params" in show_dscp_marking_rule() to use the "-F" option. Change-Id: I7634d205430ce01de28ac1ecf571696d84d865d3 Partial-Bug: #1587291 --- neutronclient/tests/unit/qos/test_cli20_dscp_marking_rule.py | 4 ++-- neutronclient/v2_0/client.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/neutronclient/tests/unit/qos/test_cli20_dscp_marking_rule.py b/neutronclient/tests/unit/qos/test_cli20_dscp_marking_rule.py index 74c941618..881b2f373 100644 --- a/neutronclient/tests/unit/qos/test_cli20_dscp_marking_rule.py +++ b/neutronclient/tests/unit/qos/test_cli20_dscp_marking_rule.py @@ -85,7 +85,7 @@ def test_show_dscp_marking_rule(self): cmd = dscp_rule.ShowQoSDscpMarkingRule(test_cli20.MyApp(sys.stdout), None) policy_id = 'policy_id' - args = [self.test_id, policy_id] + args = ['--fields', 'id', self.test_id, policy_id] self._test_show_resource(self.dscp_res, cmd, self.test_id, args, - [], cmd_resource=self.dscp_cmd_res, + ['id'], cmd_resource=self.dscp_cmd_res, parent_id=policy_id) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index c157502fd..de0226078 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -1612,10 +1612,10 @@ def list_dscp_marking_rules(self, policy_id, self.qos_dscp_marking_rules_path % policy_id, retrieve_all, **_params) - def show_dscp_marking_rule(self, rule, policy, body=None): + def show_dscp_marking_rule(self, rule, policy, **_params): """Shows information of a certain DSCP marking rule.""" return self.get(self.qos_dscp_marking_rule_path % - (policy, rule), body=body) + (policy, rule), params=_params) def create_dscp_marking_rule(self, policy, body=None): """Creates a new DSCP marking rule.""" From f3bea7e83aef53cf2a2794a9049b543d8c7b7d90 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Fri, 24 Jun 2016 03:17:32 +0000 Subject: [PATCH 444/845] Updated from global requirements Change-Id: Id5266aa1ab577adbccde9be2725cafb1e7f48ae7 --- requirements.txt | 2 +- test-requirements.txt | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index b71396c56..c8c4271a2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ oslo.i18n>=2.1.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 oslo.utils>=3.11.0 # Apache-2.0 os-client-config>=1.13.1 # Apache-2.0 -keystoneauth1>=2.1.0 # Apache-2.0 +keystoneauth1>=2.7.0 # Apache-2.0 requests>=2.10.0 # Apache-2.0 simplejson>=2.2.0 # MIT six>=1.9.0 # MIT diff --git a/test-requirements.txt b/test-requirements.txt index 6503d7cb3..b96d8f7e4 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -11,9 +11,9 @@ mock>=2.0 # BSD oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 python-subunit>=0.0.18 # Apache-2.0/BSD -reno>=1.6.2 # Apache2 -requests-mock>=0.7.0 # Apache-2.0 -sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 # BSD +reno>=1.8.0 # Apache2 +requests-mock>=1.0 # Apache-2.0 +sphinx!=1.3b1,<1.3,>=1.2.1 # BSD testrepository>=0.0.18 # Apache-2.0/BSD testtools>=1.4.0 # MIT testscenarios>=0.4 # Apache-2.0/BSD From e917f21bca2b29c762083a152318c2ace0961802 Mon Sep 17 00:00:00 2001 From: SongmingYan Date: Fri, 17 Jun 2016 03:46:15 -0400 Subject: [PATCH 445/845] Fix the problem of mox in test_shell.py Use mock instead of mox. Change-Id: Ic3938687a5a35e7788e7c2fe0bc562b0d32d96fc Partial-Bug: #1593126 --- neutronclient/tests/unit/test_shell.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/neutronclient/tests/unit/test_shell.py b/neutronclient/tests/unit/test_shell.py index 8e2af7f5c..d343cdc26 100644 --- a/neutronclient/tests/unit/test_shell.py +++ b/neutronclient/tests/unit/test_shell.py @@ -20,7 +20,7 @@ import sys import fixtures -from mox3 import mox +import mock import six import testtools from testtools import matchers @@ -53,7 +53,6 @@ class ShellTest(testtools.TestCase): # Patch os.environ to avoid required auth info. def setUp(self): super(ShellTest, self).setUp() - self.mox = mox.Mox() for var in self.FAKE_ENV: self.useFixture( fixtures.EnvironmentVariable( @@ -148,17 +147,14 @@ def test_build_option_parser(self): result = neutron_shell.build_option_parser('descr', '2.0') self.assertIsInstance(result, argparse.ArgumentParser) - def test_main_with_unicode(self): - self.mox.StubOutClassWithMocks(openstack_shell, 'NeutronShell') - qshell_mock = openstack_shell.NeutronShell('2.0') + @mock.patch.object(openstack_shell.NeutronShell, 'run') + def test_main_with_unicode(self, fake_shell): unicode_text = u'\u7f51\u7edc' argv = ['net-list', unicode_text, unicode_text] - qshell_mock.run([u'net-list', unicode_text, - unicode_text]).AndReturn(0) - self.mox.ReplayAll() + fake_shell.return_value = 0 ret = openstack_shell.main(argv=argv) - self.mox.VerifyAll() - self.mox.UnsetStubs() + fake_shell.assert_called_once_with([u'net-list', unicode_text, + unicode_text]) self.assertEqual(0, ret) def test_endpoint_option(self): From 1828552b9c6fada3f51a9fd9896737c1cd2ed6e7 Mon Sep 17 00:00:00 2001 From: Zhongcheng Lao Date: Thu, 28 Jan 2016 17:01:37 +0800 Subject: [PATCH 446/845] Fixed --insecure not taking effect when specified --insecure did not take effect currently which would prevent neutron client from establishing connections to keystone as by default the server certificate will be validated. This patch will fix the issue to take the --insecure option into consideration during constructing auth session. Co-Authored-By: Akihiro Motoki Change-Id: Id622fe097b2f12ab1a047f17005022c335fc6a4b Closes-Bug: #1538959 --- neutronclient/shell.py | 4 +- neutronclient/tests/unit/test_shell.py | 134 +++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 1 deletion(-) diff --git a/neutronclient/shell.py b/neutronclient/shell.py index d880c657c..0870599c3 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -894,7 +894,8 @@ def authenticate_user(self): """ cloud_config = os_client_config.OpenStackConfig().get_one_cloud( cloud=self.options.os_cloud, argparse=self.options, - network_api_version=self.api_version) + network_api_version=self.api_version, + verify=not self.options.insecure) verify, cert = cloud_config.get_requests_verify_args() # TODO(singhj): Remove dependancy on HTTPClient @@ -928,6 +929,7 @@ def authenticate_user(self): service_name=cloud_config.get_service_name('network'), endpoint_type=interface, auth=auth, + insecure=not verify, log_credentials=True) return diff --git a/neutronclient/tests/unit/test_shell.py b/neutronclient/tests/unit/test_shell.py index d343cdc26..a3326a17d 100644 --- a/neutronclient/tests/unit/test_shell.py +++ b/neutronclient/tests/unit/test_shell.py @@ -20,11 +20,13 @@ import sys import fixtures +from keystoneauth1 import session import mock import six import testtools from testtools import matchers +from neutronclient.common import clientmanager from neutronclient import shell as openstack_shell @@ -35,6 +37,13 @@ DEFAULT_AUTH_URL = 'http://127.0.0.1:5000/v2.0/' DEFAULT_TOKEN = '3bcc3d3a03f44e3d8377f9247b0ad155' DEFAULT_URL = 'http://quantum.example.org:9696/' +DEFAULT_REGION = 'regionOne' +DEFAULT_ENDPOINT_TYPE = 'public' +DEFAULT_API_VERSION = '2.0' +DEFAULT_SERVICE_TYPE = 'network' +DEFAULT_SERVICE_NAME = 'neutron' +DEFAULT_RETRIES = 3 +DEFAULT_TIMEOUT = 3.0 class ShellTest(testtools.TestCase): @@ -219,3 +228,128 @@ def test_run_incomplete_command(self): search_str = "Try 'neutron help port-create' for more information" self.assertTrue(any(search_str in string for string in stderr.split('\n'))) + + def _test_authenticate_user(self, expect_verify, expect_insecure, + **options): + base_options = {'os_cloud': None, + 'http_timeout': DEFAULT_TIMEOUT, + 'region_name': DEFAULT_REGION, + 'network_service_name': DEFAULT_SERVICE_NAME, + 'neutron_service_type': DEFAULT_SERVICE_TYPE} + + options.update(base_options) + if options.get('os_token'): + options.update({'os_token': 'token', 'os_url': 'url'}) + else: + options.update({'os_token': None, 'os_url': None}) + + with mock.patch.object(openstack_shell.NeutronShell, + 'run_subcommand'), \ + mock.patch.object(session, 'Session') as session_mock, \ + mock.patch.object(clientmanager, 'ClientManager') as cmgr_mock: + + shell = openstack_shell.NeutronShell(DEFAULT_API_VERSION) + shell.options = mock.Mock(spec=options.keys()) + for k, v in options.items(): + setattr(shell.options, k, v) + shell.options.os_endpoint_type = DEFAULT_ENDPOINT_TYPE + shell.options.retries = DEFAULT_RETRIES + + if not (options.get('os_token') and options.get('os_url')): + auth = mock.ANY + auth_session = mock.sentinel.session + session_mock.return_value = auth_session + else: + auth = None + auth_session = None + + shell.authenticate_user() + + if not (options.get('os_token') and options.get('os_url')): + session_mock.assert_called_once_with( + auth=mock.ANY, verify=expect_verify, + cert=options.get('cert'), + timeout=DEFAULT_TIMEOUT) + else: + self.assertFalse(session_mock.called) + + cmgr_mock.assert_called_once_with( + retries=DEFAULT_RETRIES, + raise_errors=False, + session=auth_session, + url=options.get('os_url'), + token=options.get('os_token'), + region_name=DEFAULT_REGION, + api_version=DEFAULT_API_VERSION, + service_type=DEFAULT_SERVICE_TYPE, + service_name=DEFAULT_SERVICE_NAME, + endpoint_type=DEFAULT_ENDPOINT_TYPE, + auth=auth, + insecure=expect_insecure, + log_credentials=True) + + def test_authenticate_secure_with_cacert_with_cert(self): + self._test_authenticate_user( + insecure=False, cacert='cacert', cert='cert', + expect_verify='cacert', expect_insecure=False) + + def test_authenticate_secure_with_cacert_with_cert_with_token(self): + self._test_authenticate_user( + os_token='token', + insecure=False, cacert='cacert', cert='cert', + expect_verify='cacert', expect_insecure=False) + + def test_authenticate_insecure_with_cacert_with_cert(self): + self._test_authenticate_user( + insecure=True, cacert='cacert', cert='cert', + expect_verify=False, expect_insecure=True) + + def test_authenticate_insecure_with_cacert_with_cert_with_token(self): + self._test_authenticate_user( + os_token='token', + insecure=True, cacert='cacert', cert='cert', + expect_verify=False, expect_insecure=True) + + def test_authenticate_secure_without_cacert_with_cert(self): + self._test_authenticate_user( + insecure=False, cert='cert', + expect_verify=True, expect_insecure=False) + + def test_authenticate_secure_without_cacert_with_cert_with_token(self): + self._test_authenticate_user( + os_token='token', + insecure=False, cert='cert', + expect_verify=True, expect_insecure=False) + + def test_authenticate_insecure_without_cacert_with_cert(self): + self._test_authenticate_user( + insecure=True, cert='cert', + expect_verify=False, expect_insecure=True) + + def test_authenticate_insecure_without_cacert_with_cert_with_token(self): + self._test_authenticate_user( + os_token='token', + insecure=True, cert='cert', + expect_verify=False, expect_insecure=True) + + def test_authenticate_secure_with_cacert_without_cert(self): + self._test_authenticate_user( + insecure=False, cacert='cacert', + expect_verify='cacert', expect_insecure=False) + + def test_authenticate_secure_with_cacert_without_cert_with_token(self): + self._test_authenticate_user( + os_token='token', + insecure=False, cacert='cacert', + expect_verify='cacert', expect_insecure=False) + + def test_authenticate_insecure_with_cacert_without_cert(self): + self._test_authenticate_user( + insecure=True, cacert='cacert', + expect_verify=False, expect_insecure=True) + + def test_authenticate_insecure_with_cacert_without_cert_with_token(self): + self._test_authenticate_user( + os_token='token', + insecure=True, cacert='cacert', + expect_verify=False, expect_insecure=True) From 3832d5342f9b61d5ddce22d00863026064b257f4 Mon Sep 17 00:00:00 2001 From: Clenimar Filemon Date: Fri, 1 Jul 2016 19:19:55 -0300 Subject: [PATCH 447/845] Trivial: missing comma in the docs Change-Id: Ib4719a651d78a875c70769f9b8611f1f6c103673 --- doc/source/usage/cli.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/usage/cli.rst b/doc/source/usage/cli.rst index e898386a2..93a5e6675 100644 --- a/doc/source/usage/cli.rst +++ b/doc/source/usage/cli.rst @@ -284,9 +284,9 @@ will send the following in the API layer:: { "key": { - "key1": "val1" + "key1": "val1", "key2": "val2", - "key3": "val3", + "key3": "val3" } } From cdb0a8549091acae220a46200d1c858b2356fd52 Mon Sep 17 00:00:00 2001 From: liujingzte Date: Thu, 7 Jul 2016 15:33:44 +0800 Subject: [PATCH 448/845] Modify the help of connection-limit In lbaasv2,connection-limit belongs to listener instead of vip, and it limits the service with certain protocol,ip and port. So the older help is not suitable. help=_('The maximum number of connections per second allowed for ' 'the vip. Change-Id: Id48be6823c2c3466f59c79b5683366c254c89564 Closes-Bug: 1579312 --- neutronclient/neutron/v2_0/lb/v2/listener.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/neutronclient/neutron/v2_0/lb/v2/listener.py b/neutronclient/neutron/v2_0/lb/v2/listener.py index 1adac9789..481943e6b 100644 --- a/neutronclient/neutron/v2_0/lb/v2/listener.py +++ b/neutronclient/neutron/v2_0/lb/v2/listener.py @@ -45,7 +45,8 @@ def _add_common_args(parser): '--connection-limit', type=int, help=_('The maximum number of connections per second allowed for ' - 'the vip. Positive integer or -1 for unlimited (default).')) + 'the listener. Positive integer or -1 ' + 'for unlimited (default).')) parser.add_argument( '--default-pool', help=_('Default pool for the listener.')) From 0cbd30b375dc6c75374e8f1f562e90c6c0ce3d54 Mon Sep 17 00:00:00 2001 From: Clenimar Filemon Date: Thu, 9 Jun 2016 19:45:14 -0300 Subject: [PATCH 449/845] Make USER_AGENT variable global USER_AGENT variable currently is an attribute of HTTPClient, even though SessionClient needs it as well. Make it global. Change-Id: Iac5ae30cb60627ec870330a5c9a56c9fb08bc236 --- neutronclient/client.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/neutronclient/client.py b/neutronclient/client.py index d41590c3e..10a189ba0 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -42,12 +42,12 @@ logging.getLogger("requests").setLevel(_requests_log_level) MAX_URI_LEN = 8192 +USER_AGENT = 'python-neutronclient' class HTTPClient(object): """Handles the REST calls and responses, include authn.""" - USER_AGENT = 'python-neutronclient' CONTENT_TYPE = 'application/json' @debtcollector.renames.renamed_kwarg( @@ -88,7 +88,7 @@ def __init__(self, username=None, user_id=None, def _cs_request(self, *args, **kwargs): kargs = {} kargs.setdefault('headers', kwargs.get('headers', {})) - kargs['headers']['User-Agent'] = self.USER_AGENT + kargs['headers']['User-Agent'] = USER_AGENT if 'body' in kwargs: kargs['body'] = kwargs['body'] @@ -139,7 +139,7 @@ def request(self, url, method, body=None, headers=None, **kwargs): if body: headers.setdefault('Content-Type', content_type) - headers['User-Agent'] = self.USER_AGENT + headers['User-Agent'] = USER_AGENT resp = requests.request( method, @@ -372,7 +372,7 @@ def construct_http_client(username=None, **kwargs): if session: - kwargs.setdefault('user_agent', 'python-neutronclient') + kwargs.setdefault('user_agent', USER_AGENT) kwargs.setdefault('interface', endpoint_type) return SessionClient(session=session, service_type=service_type, From 1d7c9926b85269484d8cad85fdb34c7e84f69ea1 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Fri, 8 Jul 2016 17:34:16 +0000 Subject: [PATCH 450/845] Updated from global requirements Change-Id: Ia7b03a36b1ad078b4d48d32121ee9225f8cc3fde --- requirements.txt | 2 +- test-requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index c8c4271a2..84e3db43e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ iso8601>=0.1.11 # MIT netaddr!=0.7.16,>=0.7.12 # BSD oslo.i18n>=2.1.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 -oslo.utils>=3.11.0 # Apache-2.0 +oslo.utils>=3.14.0 # Apache-2.0 os-client-config>=1.13.1 # Apache-2.0 keystoneauth1>=2.7.0 # Apache-2.0 requests>=2.10.0 # Apache-2.0 diff --git a/test-requirements.txt b/test-requirements.txt index b96d8f7e4..6b319581d 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -17,4 +17,4 @@ sphinx!=1.3b1,<1.3,>=1.2.1 # BSD testrepository>=0.0.18 # Apache-2.0/BSD testtools>=1.4.0 # MIT testscenarios>=0.4 # Apache-2.0/BSD -tempest>=11.0.0 # Apache-2.0 +tempest>=12.1.0 # Apache-2.0 From d63a92a55fafac608abecb6096c917167412207a Mon Sep 17 00:00:00 2001 From: Richard Theis Date: Fri, 22 Apr 2016 15:34:00 -0500 Subject: [PATCH 451/845] Base OSC plugin support Enable neutronclient OSC plugin support. This patch set does the following: - Establishes OSC plugin structure for commands and unit tests - Creates the OSC plugin - Adds OSC to test-requirements.txt Co-Authored-By: Akihiro Motoki Change-Id: I9a20bc7a3d8aa7b631fb0fd534fc5705c23326ce Related-Bug: #1521291 --- neutronclient/osc/__init__.py | 0 neutronclient/osc/plugin.py | 61 +++++++++++++++++++ neutronclient/osc/v2/__init__.py | 0 .../osc/v2/dynamic_routing/__init__.py | 0 neutronclient/osc/v2/fwaas/__init__.py | 0 neutronclient/osc/v2/lbaas/__init__.py | 0 neutronclient/osc/v2/vpnaas/__init__.py | 0 neutronclient/tests/unit/osc/__init__.py | 0 neutronclient/tests/unit/osc/v2/__init__.py | 0 .../unit/osc/v2/dynamic_routing/__init__.py | 0 neutronclient/tests/unit/osc/v2/fakes.py | 27 ++++++++ .../tests/unit/osc/v2/fwaas/__init__.py | 0 .../tests/unit/osc/v2/lbaas/__init__.py | 0 .../tests/unit/osc/v2/vpnaas/__init__.py | 0 requirements.txt | 1 + setup.cfg | 5 ++ test-requirements.txt | 1 + 17 files changed, 95 insertions(+) create mode 100644 neutronclient/osc/__init__.py create mode 100644 neutronclient/osc/plugin.py create mode 100644 neutronclient/osc/v2/__init__.py create mode 100644 neutronclient/osc/v2/dynamic_routing/__init__.py create mode 100644 neutronclient/osc/v2/fwaas/__init__.py create mode 100644 neutronclient/osc/v2/lbaas/__init__.py create mode 100644 neutronclient/osc/v2/vpnaas/__init__.py create mode 100644 neutronclient/tests/unit/osc/__init__.py create mode 100644 neutronclient/tests/unit/osc/v2/__init__.py create mode 100644 neutronclient/tests/unit/osc/v2/dynamic_routing/__init__.py create mode 100644 neutronclient/tests/unit/osc/v2/fakes.py create mode 100644 neutronclient/tests/unit/osc/v2/fwaas/__init__.py create mode 100644 neutronclient/tests/unit/osc/v2/lbaas/__init__.py create mode 100644 neutronclient/tests/unit/osc/v2/vpnaas/__init__.py diff --git a/neutronclient/osc/__init__.py b/neutronclient/osc/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/osc/plugin.py b/neutronclient/osc/plugin.py new file mode 100644 index 000000000..1efb6c5f7 --- /dev/null +++ b/neutronclient/osc/plugin.py @@ -0,0 +1,61 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +import logging + +# TODO(rtheis/amotoki): Add functional test infrastructure for OSC +# plugin commands. +# TODO(amotoki): Add and update document on OSC pluign. + +from osc_lib import utils + +LOG = logging.getLogger(__name__) + +DEFAULT_API_VERSION = '2.0' +API_VERSION_OPTION = 'os_network_api_version' +# NOTE(rtheis): API_NAME must NOT be set to 'network' since +# 'network' is owned by OSC! The OSC 'network' client uses +# the OpenStack SDK. +API_NAME = 'neutronclient' +API_VERSIONS = { + '2.0': 'neutronclient.v2_0.client.Client', + '2': 'neutronclient.v2_0.client.Client', +} + + +def make_client(instance): + """Returns an neutron client.""" + neutron_client = utils.get_client_class( + API_NAME, + instance._api_version[API_NAME], + API_VERSIONS) + LOG.debug('Instantiating neutron client: %s', neutron_client) + + # TODO(amotoki): Check the following arguments need to be passed + # to neutronclient class. Check keystoneauth code. + # - endpoint_type (do we need to specify it explicitly?) + # - auth (session object contains auth. Is it required?) + client = neutron_client(session=instance.session, + region_name=instance._region_name, + endpoint_type=instance._interface, + insecure=instance._insecure, + ca_cert=instance._cacert) + return client + + +def build_option_parser(parser): + """Hook to add global options""" + + # NOTE(amotoki): At now we register no option. + # OSC itself has an option for Network API version # and we refer to it. + return parser diff --git a/neutronclient/osc/v2/__init__.py b/neutronclient/osc/v2/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/osc/v2/dynamic_routing/__init__.py b/neutronclient/osc/v2/dynamic_routing/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/osc/v2/fwaas/__init__.py b/neutronclient/osc/v2/fwaas/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/osc/v2/lbaas/__init__.py b/neutronclient/osc/v2/lbaas/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/osc/v2/vpnaas/__init__.py b/neutronclient/osc/v2/vpnaas/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/tests/unit/osc/__init__.py b/neutronclient/tests/unit/osc/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/tests/unit/osc/v2/__init__.py b/neutronclient/tests/unit/osc/v2/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/tests/unit/osc/v2/dynamic_routing/__init__.py b/neutronclient/tests/unit/osc/v2/dynamic_routing/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/tests/unit/osc/v2/fakes.py b/neutronclient/tests/unit/osc/v2/fakes.py new file mode 100644 index 000000000..45f2b253b --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/fakes.py @@ -0,0 +1,27 @@ +# 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 argparse +import mock + +from openstackclient.tests import utils + + +class TestNeutronClientOSCV2(utils.TestCommand): + + def setUp(self): + super(TestNeutronClientOSCV2, self).setUp() + self.namespace = argparse.Namespace() + self.app.client_manager.session = mock.Mock() + self.app.client_manager.neutronclient = mock.Mock() + self.neutronclient = self.app.client_manager.neutronclient diff --git a/neutronclient/tests/unit/osc/v2/fwaas/__init__.py b/neutronclient/tests/unit/osc/v2/fwaas/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/tests/unit/osc/v2/lbaas/__init__.py b/neutronclient/tests/unit/osc/v2/lbaas/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/tests/unit/osc/v2/vpnaas/__init__.py b/neutronclient/tests/unit/osc/v2/vpnaas/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/requirements.txt b/requirements.txt index 8ee707299..917fa22d7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,6 +6,7 @@ cliff!=1.16.0,!=1.17.0,>=1.15.0 # Apache-2.0 debtcollector>=1.2.0 # Apache-2.0 iso8601>=0.1.11 # MIT netaddr!=0.7.16,>=0.7.12 # BSD +osc-lib>=0.1.0 # Apache-2.0 oslo.i18n>=2.1.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 oslo.utils>=3.5.0 # Apache-2.0 diff --git a/setup.cfg b/setup.cfg index 9dec54c9b..08923e02f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -31,6 +31,11 @@ setup-hooks = console_scripts = neutron = neutronclient.shell:main +openstack.cli.extension = + neutronclient = neutronclient.osc.plugin + +openstack.neutronclient.v2 = + [build_sphinx] all_files = 1 build-dir = doc/build diff --git a/test-requirements.txt b/test-requirements.txt index 76d549411..5a05cead9 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -10,6 +10,7 @@ mox3>=0.7.0 # Apache-2.0 mock>=1.2 # BSD oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 +python-openstackclient>=2.1.0 # Apache-2.0 python-subunit>=0.0.18 # Apache-2.0/BSD reno>=1.6.2 # Apache2 requests-mock>=0.7.0 # Apache-2.0 From 6ba4f31fbf446a98af3d0cbfadf18e5cafc2236b Mon Sep 17 00:00:00 2001 From: Jesse Proudman Date: Fri, 8 Jul 2016 09:19:03 -0700 Subject: [PATCH 452/845] HAProxy uses milliseconds for its timeout values. Match the cli docs to the reality of the implementation. Closes-Bug: 1600326 Change-Id: I1aa1d5a9f66f4b7d0866ddb98265a6e46ac25a4c --- neutronclient/neutron/v2_0/lb/healthmonitor.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/neutronclient/neutron/v2_0/lb/healthmonitor.py b/neutronclient/neutron/v2_0/lb/healthmonitor.py index 6fea0a12a..c2bd70cc9 100644 --- a/neutronclient/neutron/v2_0/lb/healthmonitor.py +++ b/neutronclient/neutron/v2_0/lb/healthmonitor.py @@ -66,7 +66,8 @@ def add_known_arguments(self, parser): parser.add_argument( '--delay', required=True, - help=_('The time in seconds between sending probes to members.')) + help=_('The time in milliseconds between sending probes to ' + 'members.')) parser.add_argument( '--max-retries', required=True, @@ -75,8 +76,8 @@ def add_known_arguments(self, parser): parser.add_argument( '--timeout', required=True, - help=_('Maximum number of seconds for a monitor to wait for a ' - 'connection to be established before it times out. The ' + help=_('Maximum number of milliseconds for a monitor to wait for ' + 'a connection to be established before it times out. The ' 'value must be less than the delay value.')) parser.add_argument( '--type', From b831f1899aa699289bb4b790202e20bd49986665 Mon Sep 17 00:00:00 2001 From: ji-xuepeng Date: Sun, 10 Jul 2016 00:05:09 +0800 Subject: [PATCH 453/845] remove unused LOG This is to remove unused LOG to keep code clean. Change-Id: I60706c20fff4c82761be21cf2487c03a69160c41 --- neutronclient/common/clientmanager.py | 5 ----- neutronclient/common/serializer.py | 3 --- 2 files changed, 8 deletions(-) diff --git a/neutronclient/common/clientmanager.py b/neutronclient/common/clientmanager.py index b79ee5b31..56d3d3c12 100644 --- a/neutronclient/common/clientmanager.py +++ b/neutronclient/common/clientmanager.py @@ -17,17 +17,12 @@ """Manage access to the clients, including authenticating when needed. """ -import logging - import debtcollector.renames from neutronclient import client from neutronclient.neutron import client as neutron_client -LOG = logging.getLogger(__name__) - - class ClientCache(object): """Descriptor class for caching created client handles.""" diff --git a/neutronclient/common/serializer.py b/neutronclient/common/serializer.py index 828e17f3a..fdf76774b 100644 --- a/neutronclient/common/serializer.py +++ b/neutronclient/common/serializer.py @@ -13,15 +13,12 @@ # License for the specific language governing permissions and limitations # under the License. -import logging - from oslo_serialization import jsonutils import six from neutronclient._i18n import _ from neutronclient.common import exceptions as exception -LOG = logging.getLogger(__name__) if six.PY3: long = int From d5d53d735948871eab81c002588cb6bf09e54765 Mon Sep 17 00:00:00 2001 From: reedip Date: Mon, 14 Dec 2015 11:33:42 +0900 Subject: [PATCH 454/845] Improve help messages for NeutronClient This patch improves some grammatical issues in the help messages of neutronclient. TrivialFix Change-Id: I9f303350041c4deff884a5622eeef4687d3b9940 --- neutronclient/neutron/v2_0/address_scope.py | 2 +- neutronclient/neutron/v2_0/flavor/flavor.py | 4 ++-- neutronclient/neutron/v2_0/floatingip.py | 3 ++- neutronclient/neutron/v2_0/fw/firewall.py | 17 ++++++++++------- neutronclient/neutron/v2_0/fw/firewallpolicy.py | 7 ++++--- neutronclient/neutron/v2_0/lb/member.py | 2 +- neutronclient/neutron/v2_0/lb/pool.py | 2 +- .../neutron/v2_0/lb/v2/loadbalancer.py | 6 +++--- neutronclient/neutron/v2_0/lb/v2/member.py | 2 +- neutronclient/neutron/v2_0/lb/vip.py | 9 +++++---- neutronclient/neutron/v2_0/metering.py | 6 +++--- neutronclient/neutron/v2_0/network.py | 12 ++++++------ neutronclient/neutron/v2_0/port.py | 4 ++-- neutronclient/neutron/v2_0/qos/policy.py | 9 +++++---- neutronclient/neutron/v2_0/router.py | 12 ++++++------ neutronclient/neutron/v2_0/securitygroup.py | 16 +++++++++------- neutronclient/neutron/v2_0/subnetpool.py | 10 +++++----- .../neutron/v2_0/vpn/ipsec_site_connection.py | 6 +++--- 18 files changed, 69 insertions(+), 60 deletions(-) diff --git a/neutronclient/neutron/v2_0/address_scope.py b/neutronclient/neutron/v2_0/address_scope.py index 662209b1f..b17087c35 100644 --- a/neutronclient/neutron/v2_0/address_scope.py +++ b/neutronclient/neutron/v2_0/address_scope.py @@ -76,7 +76,7 @@ class UpdateAddressScope(neutronV20.UpdateCommand): def add_known_arguments(self, parser): parser.add_argument('--name', - help=_('Name of the address scope to update.')) + help=_('Updated name of the address scope.')) def args2body(self, parsed_args): body = {} diff --git a/neutronclient/neutron/v2_0/flavor/flavor.py b/neutronclient/neutron/v2_0/flavor/flavor.py index 57ec8fa3b..3058da656 100644 --- a/neutronclient/neutron/v2_0/flavor/flavor.py +++ b/neutronclient/neutron/v2_0/flavor/flavor.py @@ -110,7 +110,7 @@ def get_parser(self, prog_name): parser.add_argument( 'flavor', metavar='FLAVOR', - help=_('Name or ID of the flavor to associate.')) + help=_('ID or name of the flavor to associate.')) parser.add_argument( 'flavor_profile', metavar='FLAVOR_PROFILE', @@ -143,7 +143,7 @@ def get_parser(self, prog_name): parser.add_argument( 'flavor', metavar='FLAVOR', - help=_('Name or ID of the flavor.')) + help=_('ID or name of the flavor to be disassociated.')) parser.add_argument( 'flavor_profile', metavar='FLAVOR_PROFILE', diff --git a/neutronclient/neutron/v2_0/floatingip.py b/neutronclient/neutron/v2_0/floatingip.py index b6a0ebaa4..9f0ce089b 100644 --- a/neutronclient/neutron/v2_0/floatingip.py +++ b/neutronclient/neutron/v2_0/floatingip.py @@ -48,7 +48,8 @@ class CreateFloatingIP(neutronV20.CreateCommand): def add_known_arguments(self, parser): parser.add_argument( 'floating_network_id', metavar='FLOATING_NETWORK', - help=_('Network name or ID to allocate floating IP from.')) + help=_('ID or name of the network from which ' + 'the floating IP is allocated.')) parser.add_argument( '--description', help=_('Description of the floating IP.')) diff --git a/neutronclient/neutron/v2_0/fw/firewall.py b/neutronclient/neutron/v2_0/fw/firewall.py index b7d3ea88a..c04660a3b 100644 --- a/neutronclient/neutron/v2_0/fw/firewall.py +++ b/neutronclient/neutron/v2_0/fw/firewall.py @@ -31,13 +31,14 @@ def add_common_args(parser): dest='routers', metavar='ROUTER', action='append', - help=_('Firewall associated router name or ID (requires FWaaS ' - 'router insertion extension, this option can be repeated)')) + help=_('ID or name of the router associated with the firewall ' + '(requires FWaaS router insertion extension to be enabled). ' + 'This option can be repeated.')) router.add_argument( '--no-routers', action='store_true', help=_('Associate no routers with the firewall (requires FWaaS ' - 'router insertion extension)')) + 'router insertion extension).')) def parse_common_args(client, parsed_args): @@ -84,7 +85,8 @@ def add_known_arguments(self, parser): add_common_args(parser) parser.add_argument( 'policy', metavar='POLICY', - help=_('Firewall policy name or ID.')) + help=_('ID or name of the firewall policy ' + 'associated to this firewall.')) parser.add_argument( '--admin-state-down', dest='admin_state', @@ -107,11 +109,12 @@ def add_known_arguments(self, parser): add_common_args(parser) parser.add_argument( '--policy', metavar='POLICY', - help=_('Firewall policy name or ID.')) + help=_('ID or name of the firewall policy ' + 'associated to this firewall.')) utils.add_boolean_argument( parser, '--admin-state-up', dest='admin_state_up', - help=_('Update the admin state for the firewall' - '(True means UP)')) + help=_('Update the admin state for the firewall ' + '(True means UP).')) def args2body(self, parsed_args): body = parse_common_args(self.get_client(), parsed_args) diff --git a/neutronclient/neutron/v2_0/fw/firewallpolicy.py b/neutronclient/neutron/v2_0/fw/firewallpolicy.py index a01fc726f..0c99948eb 100644 --- a/neutronclient/neutron/v2_0/fw/firewallpolicy.py +++ b/neutronclient/neutron/v2_0/fw/firewallpolicy.py @@ -114,11 +114,11 @@ def add_known_arguments(self, parser): utils.add_boolean_argument( parser, '--shared', help=_('Update the sharing status of the policy. ' - '(True means shared)')) + '(True means shared).')) utils.add_boolean_argument( parser, '--audited', help=_('Update the audit status of the policy. ' - '(True means auditing is enabled)')) + '(True means auditing is enabled).')) def args2body(self, parsed_args): return parse_common_args(self.get_client(), parsed_args) @@ -213,7 +213,8 @@ def get_parser(self, prog_name): parser.add_argument( 'firewall_rule_id', metavar='FIREWALL_RULE', - help=_('Firewall rule to remove from policy.')) + help=_('ID or name of the firewall rule to be removed ' + 'from the policy.')) self.add_known_arguments(parser) return parser diff --git a/neutronclient/neutron/v2_0/lb/member.py b/neutronclient/neutron/v2_0/lb/member.py index d81bd69ae..c90840cd1 100644 --- a/neutronclient/neutron/v2_0/lb/member.py +++ b/neutronclient/neutron/v2_0/lb/member.py @@ -60,7 +60,7 @@ def add_known_arguments(self, parser): 'connections.')) parser.add_argument( 'pool_id', metavar='POOL', - help=_('Pool ID or name this vip belongs to.')) + help=_('ID or name of the pool this vip belongs to.')) def args2body(self, parsed_args): _pool_id = neutronV20.find_resourceid_by_name_or_id( diff --git a/neutronclient/neutron/v2_0/lb/pool.py b/neutronclient/neutron/v2_0/lb/pool.py index 5d3a9db7f..afc0cc13e 100644 --- a/neutronclient/neutron/v2_0/lb/pool.py +++ b/neutronclient/neutron/v2_0/lb/pool.py @@ -77,7 +77,7 @@ def add_known_arguments(self, parser): 'located.')) parser.add_argument( '--provider', - help=_('Provider name of loadbalancer service.')) + help=_('Provider name of the loadbalancer service.')) def args2body(self, parsed_args): _subnet_id = neutronV20.find_resourceid_by_name_or_id( diff --git a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py index 76f37fc50..e68518602 100644 --- a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py +++ b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py @@ -64,10 +64,10 @@ def add_known_arguments(self, parser): help=_('Set admin state up to false.')) parser.add_argument( '--provider', - help=_('Provider name of load balancer service.')) + help=_('Provider name of the load balancer service.')) parser.add_argument( '--flavor', - help=_('ID or name of flavor.')) + help=_('ID or name of the flavor.')) parser.add_argument( '--vip-address', help=_('VIP address for the load balancer.')) @@ -146,7 +146,7 @@ def take_action(self, parsed_args): # | ... | ... | # +--------------------+-------+ # it has two columns and the Filed column is alphabetical, - # here covert the data dict to the 1-1 vector format below: + # here convert the data dict to the 1-1 vector format below: # [(field1, field2, field3, ...), (value1, value2, value3, ...)] return list(zip(*sorted(stats.items()))) diff --git a/neutronclient/neutron/v2_0/lb/v2/member.py b/neutronclient/neutron/v2_0/lb/v2/member.py index 4a94a7a8d..aeae1cb28 100644 --- a/neutronclient/neutron/v2_0/lb/v2/member.py +++ b/neutronclient/neutron/v2_0/lb/v2/member.py @@ -45,7 +45,7 @@ def _add_common_args(parser): help=_('Name of the member.')) parser.add_argument( '--weight', - help=_('Weight of member in the pool (default:1, [0..256]).')) + help=_('Weight of the member in the pool (default:1, [0..256]).')) def _parse_common_args(body, parsed_args): diff --git a/neutronclient/neutron/v2_0/lb/vip.py b/neutronclient/neutron/v2_0/lb/vip.py index 4f51357cb..e3ac8ee9d 100644 --- a/neutronclient/neutron/v2_0/lb/vip.py +++ b/neutronclient/neutron/v2_0/lb/vip.py @@ -42,7 +42,7 @@ class CreateVip(neutronV20.CreateCommand): def add_known_arguments(self, parser): parser.add_argument( 'pool_id', metavar='POOL', - help=_('Pool ID or name this vip belongs to.')) + help=_('ID or name of the pool to which this vip belongs.')) parser.add_argument( '--address', help=_('IP address of the vip.')) @@ -53,14 +53,15 @@ def add_known_arguments(self, parser): parser.add_argument( '--connection-limit', help=_('The maximum number of connections per second allowed for ' - 'the vip. Positive integer or -1 for unlimited (default).')) + 'the vip. Valid values: a positive integer or -1 ' + 'for unlimited (default).')) parser.add_argument( '--description', - help=_('Description of the vip.')) + help=_('Description of the vip to be created.')) parser.add_argument( '--name', required=True, - help=_('Name of the vip.')) + help=_('Name of the vip to be created.')) parser.add_argument( '--protocol-port', required=True, diff --git a/neutronclient/neutron/v2_0/metering.py b/neutronclient/neutron/v2_0/metering.py index a6ecef8e0..df81724fc 100644 --- a/neutronclient/neutron/v2_0/metering.py +++ b/neutronclient/neutron/v2_0/metering.py @@ -40,10 +40,10 @@ class CreateMeteringLabel(neutronv20.CreateCommand): def add_known_arguments(self, parser): parser.add_argument( 'name', metavar='NAME', - help=_('Name of metering label to create.')) + help=_('Name of the metering label to be created.')) parser.add_argument( '--description', - help=_('Description of metering label to create.')) + help=_('Description of the metering label to be created.')) parser.add_argument( '--shared', action='store_true', @@ -86,7 +86,7 @@ class CreateMeteringLabelRule(neutronv20.CreateCommand): def add_known_arguments(self, parser): parser.add_argument( 'label_id', metavar='LABEL', - help=_('Id or Name of the label.')) + help=_('ID or name of the label.')) parser.add_argument( 'remote_ip_prefix', metavar='REMOTE_IP_PREFIX', help=_('CIDR to match on.')) diff --git a/neutronclient/neutron/v2_0/network.py b/neutronclient/neutron/v2_0/network.py index 23362e12e..7a89f5a23 100644 --- a/neutronclient/neutron/v2_0/network.py +++ b/neutronclient/neutron/v2_0/network.py @@ -169,21 +169,21 @@ def add_known_arguments(self, parser): parser.add_argument( '--provider:physical_network', metavar='', - help=_('Name of the physical network over which the virtual' - ' network is implemented.')) + help=_('Name of the physical network over which the virtual ' + 'network is implemented.')) parser.add_argument( '--provider:segmentation_id', metavar='', - help=_('VLAN ID for VLAN networks or tunnel-id for GRE/VXLAN' - ' networks.')) + help=_('VLAN ID for VLAN networks or tunnel-id for GRE/VXLAN ' + 'networks.')) utils.add_boolean_argument( parser, '--vlan-transparent', default=argparse.SUPPRESS, - help=_('Create a vlan transparent network.')) + help=_('Create a VLAN transparent network.')) parser.add_argument( 'name', metavar='NAME', - help=_('Name of network to create.')) + help=_('Name of the network to be created.')) parser.add_argument( '--description', help=_('Description of network.')) diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index d79551d87..b279cf601 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -113,7 +113,7 @@ def get_parser(self, prog_name): parser = super(ListRouterPort, self).get_parser(prog_name) parser.add_argument( 'id', metavar='ROUTER', - help=_('ID or name of router to look up.')) + help=_('ID or name of the router to look up.')) return parser def take_action(self, parsed_args): @@ -272,7 +272,7 @@ def add_known_arguments(self, parser): parser.add_argument( 'network_id', metavar='NETWORK', - help=_('Network ID or name this port belongs to.')) + help=_('ID or name of the network this port belongs to.')) dns.add_dns_argument_create(parser, self.resource, 'name') def args2body(self, parsed_args): diff --git a/neutronclient/neutron/v2_0/qos/policy.py b/neutronclient/neutron/v2_0/qos/policy.py index 53805284b..3dc982f31 100644 --- a/neutronclient/neutron/v2_0/qos/policy.py +++ b/neutronclient/neutron/v2_0/qos/policy.py @@ -31,7 +31,8 @@ def add_arguments_qos_policy(self, parser): qos_policy_args = parser.add_mutually_exclusive_group() qos_policy_args.add_argument( '--qos-policy', - help=_('Attach QoS policy ID or name to the resource.')) + help=_('ID or name of the QoS policy that should' + 'be attached to the resource.')) return qos_policy_args def args2body_qos_policy(self, parsed_args, resource): @@ -92,10 +93,10 @@ class CreateQoSPolicy(neutronv20.CreateCommand): def add_known_arguments(self, parser): parser.add_argument( 'name', metavar='NAME', - help=_('Name of QoS policy to create.')) + help=_('Name of the QoS policy to be created.')) parser.add_argument( '--description', - help=_('Description of the QoS policy.')) + help=_('Description of the QoS policy to be created.')) parser.add_argument( '--shared', action='store_true', @@ -122,7 +123,7 @@ class UpdateQoSPolicy(neutronv20.UpdateCommand): def add_known_arguments(self, parser): parser.add_argument( '--name', - help=_('Name of QoS policy.')) + help=_('Name of the QoS policy.')) parser.add_argument( '--description', help=_('Description of the QoS policy.')) diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index 605efc8a5..200feee18 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -67,7 +67,7 @@ def add_known_arguments(self, parser): help=argparse.SUPPRESS) parser.add_argument( 'name', metavar='NAME', - help=_('Name of router to create.')) + help=_('Name of the router to be created.')) parser.add_argument( '--description', help=_('Description of router.')) @@ -103,21 +103,21 @@ class UpdateRouter(neutronV20.UpdateCommand): def add_known_arguments(self, parser): parser.add_argument( '--name', - help=_('Name of this router.')) + help=_('Updated name of the router.')) parser.add_argument( '--description', help=_('Description of router.')) utils.add_boolean_argument( parser, '--admin-state-up', dest='admin_state', - help=_('Specify the administrative state of the router' - ' (True meaning "Up")')) + help=_('Specify the administrative state of the router ' + '(True means "Up").')) utils.add_boolean_argument( parser, '--admin_state_up', dest='admin_state', help=argparse.SUPPRESS) utils.add_boolean_argument( parser, '--distributed', dest='distributed', - help=_('True means this router should operate in' - ' distributed mode.')) + help=_('True means this router should operate in ' + 'distributed mode.')) routes_group = parser.add_mutually_exclusive_group() routes_group.add_argument( '--route', metavar='destination=CIDR,nexthop=IP_ADDR', diff --git a/neutronclient/neutron/v2_0/securitygroup.py b/neutronclient/neutron/v2_0/securitygroup.py index d79d897c6..8cb89ac1c 100644 --- a/neutronclient/neutron/v2_0/securitygroup.py +++ b/neutronclient/neutron/v2_0/securitygroup.py @@ -123,10 +123,10 @@ class CreateSecurityGroup(neutronV20.CreateCommand): def add_known_arguments(self, parser): parser.add_argument( 'name', metavar='NAME', - help=_('Name of security group.')) + help=_('Name of the security group to be created.')) parser.add_argument( '--description', - help=_('Description of security group.')) + help=_('Description of the security group to be created.')) def args2body(self, parsed_args): body = {'name': parsed_args.name} @@ -150,10 +150,10 @@ class UpdateSecurityGroup(neutronV20.UpdateCommand): def add_known_arguments(self, parser): parser.add_argument( '--name', - help=_('Name of security group.')) + help=_('Updated name of the security group.')) parser.add_argument( '--description', - help=_('Description of security group.')) + help=_('Updated description of the security group.')) def args2body(self, parsed_args): body = {} @@ -310,7 +310,8 @@ def add_known_arguments(self, parser): help=_('Description of security group rule.')) parser.add_argument( 'security_group_id', metavar='SECURITY_GROUP', - help=_('Security group name or ID to add rule.')) + help=_('ID or name of the security group to ' + 'which the rule is added.')) parser.add_argument( '--direction', default='ingress', choices=['ingress', 'egress'], @@ -322,7 +323,7 @@ def add_known_arguments(self, parser): '--protocol', help=_('Protocol of packet. Allowed values are ' '[icmp, icmpv6, tcp, udp] and ' - 'integer representations [0-255]')) + 'integer representations [0-255].')) parser.add_argument( '--port-range-min', help=_('Starting port range. For ICMP it is type.')) @@ -343,7 +344,8 @@ def add_known_arguments(self, parser): help=argparse.SUPPRESS) parser.add_argument( '--remote-group-id', metavar='REMOTE_GROUP', - help=_('Remote security group name or ID to apply rule.')) + help=_('ID or name of the remote security group ' + 'to which the rule is applied.')) parser.add_argument( '--remote_group_id', help=argparse.SUPPRESS) diff --git a/neutronclient/neutron/v2_0/subnetpool.py b/neutronclient/neutron/v2_0/subnetpool.py index ee6dedc57..7d83c1188 100644 --- a/neutronclient/neutron/v2_0/subnetpool.py +++ b/neutronclient/neutron/v2_0/subnetpool.py @@ -88,13 +88,13 @@ def add_known_arguments(self, parser): parser.add_argument( 'name', metavar='NAME', - help=_('Name of subnetpool to create.')) + help=_('Name of the subnetpool to be created.')) parser.add_argument( '--address-scope', metavar='ADDRSCOPE', help=_('ID or name of the address scope with which the subnetpool ' 'is associated. Prefixes must be unique across address ' - 'scopes')) + 'scopes.')) def args2body(self, parsed_args): body = {'prefixes': parsed_args.prefixes} @@ -125,18 +125,18 @@ class UpdateSubnetPool(neutronV20.UpdateCommand): def add_known_arguments(self, parser): add_updatable_arguments(parser) parser.add_argument('--name', - help=_('Name of subnetpool to update.')) + help=_('Updated name of the subnetpool.')) addrscope_args = parser.add_mutually_exclusive_group() addrscope_args.add_argument('--address-scope', metavar='ADDRSCOPE', help=_('ID or name of the address scope ' 'with which the subnetpool is ' 'associated. Prefixes must be ' - 'unique across address scopes')) + 'unique across address scopes.')) addrscope_args.add_argument('--no-address-scope', action='store_true', help=_('Detach subnetpool from the ' - 'address scope')) + 'address scope.')) def args2body(self, parsed_args): body = {} diff --git a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py index af596c3f2..503d90163 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py +++ b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py @@ -61,11 +61,11 @@ def add_known_arguments(self, parser): parser.add_argument( '--local-ep-group', help=_('Local endpoint group ID/name with subnet(s) for ' - 'IPSec connection.')) + 'the IPsec connection.')) parser.add_argument( '--peer-ep-group', help=_('Peer endpoint group ID/name with CIDR(s) for ' - 'IPsec connection.')) + 'the IPsec connection.')) def args2body(self, parsed_args, body=None): """Add in conditional args and then return all conn info.""" @@ -100,7 +100,7 @@ def add_known_arguments(self, parser): help=_('Set admin state up to false.')) parser.add_argument( '--name', - help=_('Set friendly name for the connection.')) + help=_('Set a name for the connection.')) parser.add_argument( '--description', help=_('Set a description for the connection.')) From d45442b3d46b26d928a5634874530168f961bb2b Mon Sep 17 00:00:00 2001 From: Abhishek Raut Date: Tue, 3 May 2016 05:17:12 -0700 Subject: [PATCH 455/845] Add support to expose default quotas for tenants Neutron client should be able to show the default quotas set for tenants. This patch adds support for the same and introduces a new CLI to retrieve default quotas. Sample usage: neutron quota-default-show +-----------------------+-------+ | Field | Value | +-----------------------+-------+ | floatingip | 50 | | l2-gateway-connection | -1 | | network | 10 | | port | 50 | | rbac_policy | 10 | | router | 10 | | security_group | 10 | | security_group_rule | 100 | | subnet | 10 | | subnetpool | -1 | +-----------------------+-------+ Change-Id: Ie92c22862d2b8ace32a2cf4cb642a6d0bac7932d Closes-Bug: #1204956 --- neutronclient/neutron/v2_0/quota.py | 49 ++++++++++--------- neutronclient/shell.py | 1 + neutronclient/tests/unit/test_quota.py | 33 +++++++++++++ neutronclient/v2_0/client.py | 7 +++ ...d-quota-default-show-c2ab35b791dcdcbc.yaml | 7 +++ 5 files changed, 73 insertions(+), 24 deletions(-) create mode 100644 releasenotes/notes/add-quota-default-show-c2ab35b791dcdcbc.yaml diff --git a/neutronclient/neutron/v2_0/quota.py b/neutronclient/neutron/v2_0/quota.py index e7e74b106..061f87371 100644 --- a/neutronclient/neutron/v2_0/quota.py +++ b/neutronclient/neutron/v2_0/quota.py @@ -16,6 +16,7 @@ from __future__ import print_function +import abc import argparse from cliff import lister @@ -90,14 +91,17 @@ def take_action(self, parsed_args): for s in info)) -class ShowQuota(neutronV20.NeutronCommand, show.ShowOne): - """Show quotas of a given tenant. +class ShowQuotaBase(neutronV20.NeutronCommand, show.ShowOne): + """Base class to show quotas of a given tenant.""" - """ resource = "quota" + @abc.abstractmethod + def retrieve_data(self, tenant_id, neutron_client): + """Retrieve data using neutron client for the given tenant.""" + def get_parser(self, prog_name): - parser = super(ShowQuota, self).get_parser(prog_name) + parser = super(ShowQuotaBase, self).get_parser(prog_name) parser.add_argument( '--tenant-id', metavar='tenant-id', help=_('The owner tenant ID.')) @@ -115,27 +119,24 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): neutron_client = self.get_client() tenant_id = get_tenant_id(parsed_args, neutron_client) - params = {} - obj_shower = getattr(neutron_client, - "show_%s" % self.resource) - data = obj_shower(tenant_id, **params) + data = self.retrieve_data(tenant_id, neutron_client) if self.resource in data: - for k, v in six.iteritems(data[self.resource]): - if isinstance(v, list): - value = "" - for _item in v: - if value: - value += "\n" - if isinstance(_item, dict): - value += jsonutils.dumps(_item) - else: - value += str(_item) - data[self.resource][k] = value - elif v is None: - data[self.resource][k] = '' return zip(*sorted(six.iteritems(data[self.resource]))) - else: - return None + return + + +class ShowQuota(ShowQuotaBase): + """Show quotas for a given tenant.""" + + def retrieve_data(self, tenant_id, neutron_client): + return neutron_client.show_quota(tenant_id) + + +class ShowQuotaDefault(ShowQuotaBase): + """Show default quotas for a given tenant.""" + + def retrieve_data(self, tenant_id, neutron_client): + return neutron_client.show_quota_default(tenant_id) class UpdateQuota(neutronV20.NeutronCommand, show.ShowOne): @@ -240,4 +241,4 @@ def take_action(self, parsed_args): data[self.resource][k] = '' return zip(*sorted(six.iteritems(data[self.resource]))) else: - return None + return diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 0870599c3..5ddae52b2 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -181,6 +181,7 @@ def take_action(self, parsed_args): 'purge': purge.Purge, 'quota-list': quota.ListQuota, 'quota-show': quota.ShowQuota, + 'quota-default-show': quota.ShowQuotaDefault, 'quota-delete': quota.DeleteQuota, 'quota-update': quota.UpdateQuota, 'ext-list': extension.ListExt, diff --git a/neutronclient/tests/unit/test_quota.py b/neutronclient/tests/unit/test_quota.py index 5be7ba3b7..b9d7b4448 100644 --- a/neutronclient/tests/unit/test_quota.py +++ b/neutronclient/tests/unit/test_quota.py @@ -16,6 +16,8 @@ import sys +from mox3 import mox + from neutronclient.common import exceptions from neutronclient.neutron.v2_0 import quota as test_quota from neutronclient.tests.unit import test_cli20 @@ -59,3 +61,34 @@ def test_update_quota_positional(self): exceptions.NeutronClientException, self._test_update_resource, resource, cmd, self.test_id, args=args, extrafields={'network': 'new'}) + + def test_show_quota_default(self): + resource = 'quota' + cmd = test_quota.ShowQuotaDefault( + test_cli20.MyApp(sys.stdout), None) + args = ['--tenant-id', self.test_id] + self.mox.StubOutWithMock(cmd, "get_client") + self.mox.StubOutWithMock(self.client.httpclient, "request") + cmd.get_client().MultipleTimes().AndReturn(self.client) + expected_res = {'quota': {'port': 50, 'network': 10, 'subnet': 10}} + resstr = self.client.serialize(expected_res) + path = getattr(self.client, "quota_default_path") + return_tup = (test_cli20.MyResp(200), resstr) + self.client.httpclient.request( + test_cli20.end_url(path % self.test_id), 'GET', + body=None, + headers=mox.ContainsKeyValue( + 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(return_tup) + self.mox.ReplayAll() + + cmd_parser = cmd.get_parser("test_" + resource) + parsed_args = cmd_parser.parse_args(args) + cmd.run(parsed_args) + + self.mox.VerifyAll() + self.mox.UnsetStubs() + _str = self.fake_stdout.make_string() + self.assertIn('network', _str) + self.assertIn('subnet', _str) + self.assertIn('port', _str) + self.assertNotIn('subnetpool', _str) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 5504fd1dd..ccea20ba0 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -411,6 +411,7 @@ class Client(ClientBase): address_scope_path = "/address-scopes/%s" quotas_path = "/quotas" quota_path = "/quotas/%s" + quota_default_path = "/quotas/%s/default" extensions_path = "/extensions" extension_path = "/extensions/%s" routers_path = "/routers" @@ -602,6 +603,12 @@ def show_quota(self, project_id, **_params): """Fetch information of a certain project's quotas.""" return self.get(self.quota_path % (project_id), params=_params) + @debtcollector.renames.renamed_kwarg( + 'tenant_id', 'project_id', replace=True) + def show_quota_default(self, project_id, **_params): + """Fetch information of a certain project's default quotas.""" + return self.get(self.quota_default_path % (project_id), params=_params) + @debtcollector.renames.renamed_kwarg( 'tenant_id', 'project_id', replace=True) def update_quota(self, project_id, body=None): diff --git a/releasenotes/notes/add-quota-default-show-c2ab35b791dcdcbc.yaml b/releasenotes/notes/add-quota-default-show-c2ab35b791dcdcbc.yaml new file mode 100644 index 000000000..3aececa35 --- /dev/null +++ b/releasenotes/notes/add-quota-default-show-c2ab35b791dcdcbc.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + CLI support to display the default quota reserved for a tenant. + + * The ``quota-default-show`` command outputs the default quota + of resources for a given tenant. From 6bc4685f6b50fb8883c67b7f80b12ced6228d27b Mon Sep 17 00:00:00 2001 From: Hirofumi Ichihara Date: Wed, 20 Jul 2016 03:09:51 +0900 Subject: [PATCH 456/845] Add functional test hook for fwaas command This patch adds a hook into functional test so that fwaas command test runs. Change-Id: Ib4ba644b22db0df91a9b0c7011b46ece337198af Closes-Bug: #1604510 --- neutronclient/tests/functional/hooks/fwaas | 2 ++ .../tests/functional/hooks/gate_hook.sh | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 neutronclient/tests/functional/hooks/fwaas mode change 100644 => 100755 neutronclient/tests/functional/hooks/gate_hook.sh diff --git a/neutronclient/tests/functional/hooks/fwaas b/neutronclient/tests/functional/hooks/fwaas new file mode 100644 index 000000000..0f04168ad --- /dev/null +++ b/neutronclient/tests/functional/hooks/fwaas @@ -0,0 +1,2 @@ +enable_plugin neutron-fwaas git://git.openstack.org/openstack/neutron-fwaas +enable_service q-fwaas \ No newline at end of file diff --git a/neutronclient/tests/functional/hooks/gate_hook.sh b/neutronclient/tests/functional/hooks/gate_hook.sh old mode 100644 new mode 100755 index b44c237f1..9ad05aabf --- a/neutronclient/tests/functional/hooks/gate_hook.sh +++ b/neutronclient/tests/functional/hooks/gate_hook.sh @@ -4,6 +4,26 @@ set -ex VENV=${1:-"functional"} +GATE_DEST=$BASE/new +NEUTRONCLIENT_PATH=$GATE_DEST/python-neutronclient +GATE_HOOKS=$NEUTRONCLIENT_PATH/neutronclient/tests/functional/hooks +DEVSTACK_PATH=$GATE_DEST/devstack + +# Inject config from hook into localrc +function load_rc_hook { + local hook="$1" + config=$(cat $GATE_HOOKS/$hook) + export DEVSTACK_LOCAL_CONFIG+=" +# generated from hook '$hook' +${config} +" +} + +if [ "$VENV" == "functional" ] +then + load_rc_hook fwaas +fi + if [ "$VENV" == "functional-adv-svcs" ] then export DEVSTACK_LOCAL_CONFIG="enable_plugin neutron-vpnaas git://git.openstack.org/openstack/neutron-vpnaas" From 53e2ad1ba964bade5ebc2cb2b74113b46adc3f19 Mon Sep 17 00:00:00 2001 From: Hirofumi Ichihara Date: Tue, 19 Jul 2016 18:18:49 +0900 Subject: [PATCH 457/845] Log request-id for each api call This patch adds support to log 'X-Openstack-Request-Id' for each api call. Change-Id: Ia17d6c4cc021246316a7317011596e0e2efa37be Closes-Bug: #1604306 --- neutronclient/tests/unit/test_cli20.py | 10 +++++++++- neutronclient/v2_0/client.py | 7 +++++++ .../notes/log-request-id-64bef955b8292c18.yaml | 3 +++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/log-request-id-64bef955b8292c18.yaml diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 19bd29708..abbe8cf6d 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -66,11 +66,19 @@ def make_string(self): return result +class MyRequest(requests.Request): + def __init__(self, method=None): + self.method = method + + class MyResp(requests.Response): - def __init__(self, status_code, headers=None, reason=None): + def __init__(self, status_code, headers=None, reason=None, + request=None, url=None): self.status_code = status_code self.headers = headers or {} self.reason = reason + self.request = request or MyRequest() + self.url = url class MyApp(object): diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 5504fd1dd..f192c061b 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -112,6 +112,13 @@ def _append_request_id(self, resp): # Extract 'x-openstack-request-id' from headers if # response is a Response object. request_id = resp.headers.get('x-openstack-request-id') + # log request-id for each api call + _logger.debug('%(method)s call to neutron for ' + '%(url)s used request id ' + '%(response_request_id)s', + {'method': resp.request.method, + 'url': resp.url, + 'response_request_id': request_id}) else: # If resp is of type string. request_id = resp diff --git a/releasenotes/notes/log-request-id-64bef955b8292c18.yaml b/releasenotes/notes/log-request-id-64bef955b8292c18.yaml new file mode 100644 index 000000000..62c73fa2e --- /dev/null +++ b/releasenotes/notes/log-request-id-64bef955b8292c18.yaml @@ -0,0 +1,3 @@ +--- +features: + - Added support to log 'x-openstack-request-id' for each api call. \ No newline at end of file From 3b1c5384223b9896d206cdab430d945516124054 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Thu, 21 Jul 2016 04:10:18 +0000 Subject: [PATCH 458/845] Updated from global requirements Change-Id: I530dfab31a02b9acbb781f221ecf042f1d260290 --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index af9e0ddbd..8d91b3ce7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,10 +6,10 @@ cliff!=1.16.0,!=1.17.0,>=1.15.0 # Apache-2.0 debtcollector>=1.2.0 # Apache-2.0 iso8601>=0.1.11 # MIT netaddr!=0.7.16,>=0.7.12 # BSD -osc-lib>=0.1.0 # Apache-2.0 +osc-lib>=0.4.0 # Apache-2.0 oslo.i18n>=2.1.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 -oslo.utils>=3.14.0 # Apache-2.0 +oslo.utils>=3.15.0 # Apache-2.0 os-client-config>=1.13.1 # Apache-2.0 keystoneauth1>=2.7.0 # Apache-2.0 requests>=2.10.0 # Apache-2.0 From 45d18eb380077e710121205f717ae5e4355f7e41 Mon Sep 17 00:00:00 2001 From: "Swapnil Kulkarni (coolsvap)" Date: Fri, 22 Jul 2016 04:07:19 +0000 Subject: [PATCH 459/845] Remove discover from test-requirements It's only needed for python < 2.7 which is not supported Change-Id: I2d0386cd181afce62bbba810f420f4fdb83d16fa --- test-requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index fa8057a94..0fa7f50cf 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -4,7 +4,6 @@ hacking<0.11,>=0.10.0 coverage>=3.6 # Apache-2.0 -discover # BSD fixtures>=3.0.0 # Apache-2.0/BSD mox3>=0.7.0 # Apache-2.0 mock>=2.0 # BSD From 8876ab264aaec34154e6a2fd56ae22debd7ba649 Mon Sep 17 00:00:00 2001 From: Carl Baldwin Date: Fri, 22 Jul 2016 17:27:32 -0600 Subject: [PATCH 460/845] Add client exception for HostNotCompatibleWithFixedIps Change-Id: I636ef272202ec7ea8fc0adbfd86ca3eb07e316ba Partially-Implements: blueprint routed-networks Depends-On: I8dc8890907d1e241dd12448fa184cea1b0620663 --- neutronclient/common/exceptions.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/neutronclient/common/exceptions.py b/neutronclient/common/exceptions.py index 2e4d679e6..d165a3bc0 100644 --- a/neutronclient/common/exceptions.py +++ b/neutronclient/common/exceptions.py @@ -170,6 +170,10 @@ class MacAddressInUseClient(Conflict): pass +class HostNotCompatibleWithFixedIpsClient(Conflict): + pass + + class ExternalIpAddressExhaustedClient(BadRequest): pass From ec20f7f85c3a8ecd788536401eeeb0fef4ef18c2 Mon Sep 17 00:00:00 2001 From: Takashi NATSUME Date: Tue, 26 Jul 2016 15:48:11 +0900 Subject: [PATCH 461/845] Fix string interpolation at logging call Skip creating the formatted log message if the message is not going to be emitted because of the log level. Change-Id: I19d985addb2bdc1b5e17ecd5ac90223e5347d7b2 Closes-Bug: #1596829 --- neutronclient/neutron/v2_0/lb/v2/loadbalancer.py | 2 +- neutronclient/neutron/v2_0/subnet.py | 6 +++--- neutronclient/tests/unit/test_cli20_subnet.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py index 76f37fc50..32aac8748 100644 --- a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py +++ b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py @@ -168,7 +168,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - self.log.debug('run(%s)' % parsed_args) + self.log.debug('run(%s)', parsed_args) neutron_client = self.get_client() lb_id = neutronV20.find_resourceid_by_name_or_id( neutron_client, self.resource, parsed_args.loadbalancer) diff --git a/neutronclient/neutron/v2_0/subnet.py b/neutronclient/neutron/v2_0/subnet.py index 952b65245..fed0997bc 100644 --- a/neutronclient/neutron/v2_0/subnet.py +++ b/neutronclient/neutron/v2_0/subnet.py @@ -237,9 +237,9 @@ def args2body(self, parsed_args): self.log.warning(_("An IPv%(ip)d subnet with a %(cidr)s CIDR " "will have only one usable IP address so " "the device attached to it will not have " - "any IP connectivity.") - % {"ip": ip_version, - "cidr": unusable_cidr}) + "any IP connectivity."), + {"ip": ip_version, + "cidr": unusable_cidr}) updatable_args2body(parsed_args, body, ip_version=ip_version) if parsed_args.tenant_id: diff --git a/neutronclient/tests/unit/test_cli20_subnet.py b/neutronclient/tests/unit/test_cli20_subnet.py index 1a9abcd0d..afaf3f60b 100644 --- a/neutronclient/tests/unit/test_cli20_subnet.py +++ b/neutronclient/tests/unit/test_cli20_subnet.py @@ -402,7 +402,7 @@ def test_create_subnet_max_v4_cidr(self): position_names = ['ip_version', 'network_id', 'cidr', 'gateway_ip'] position_values = [4, netid, cidr, gateway] self.mox.StubOutWithMock(cmd.log, 'warning') - cmd.log.warning(mox.IgnoreArg()) + cmd.log.warning(mox.IgnoreArg(), {'ip': 4, 'cidr': '/32'}) self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) self.mox.VerifyAll() From 310a6ece52a94cf8fb31d10be73219b417f3938f Mon Sep 17 00:00:00 2001 From: reedip Date: Tue, 22 Mar 2016 17:57:49 +0900 Subject: [PATCH 462/845] Add quota support for LB and Listeners This patch adds support for quota update of Loadbalancers and Listeners. Change-Id: I94c0e0898dff18fbadf1c24b5555dd497e9bf640 Closes-Bug: #1559027 --- neutronclient/neutron/v2_0/quota.py | 9 ++++++++- .../notes/quota-update-for-LB-b21e7bc9e4a10f3e.yaml | 4 ++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/quota-update-for-LB-b21e7bc9e4a10f3e.yaml diff --git a/neutronclient/neutron/v2_0/quota.py b/neutronclient/neutron/v2_0/quota.py index e7e74b106..3e8a9a819 100644 --- a/neutronclient/neutron/v2_0/quota.py +++ b/neutronclient/neutron/v2_0/quota.py @@ -184,6 +184,12 @@ def get_parser(self, prog_name): parser.add_argument( '--health-monitor', metavar='health_monitors', help=_('The limit of health monitors.')) + parser.add_argument( + '--loadbalancer', metavar='loadbalancers', + help=_('The limit of load balancers.')) + parser.add_argument( + '--listener', metavar='listeners', + help=_('The limit of listeners.')) parser.add_argument( 'pos_tenant_id', help=argparse.SUPPRESS, nargs='?') @@ -203,7 +209,8 @@ def args2body(self, parsed_args): quota = {} for resource in ('network', 'subnet', 'port', 'router', 'floatingip', 'security_group', 'security_group_rule', - 'vip', 'pool', 'member', 'health_monitor'): + 'vip', 'pool', 'member', 'health_monitor', + 'loadbalancer', 'listener'): if getattr(parsed_args, resource): quota[resource] = self._validate_int( resource, diff --git a/releasenotes/notes/quota-update-for-LB-b21e7bc9e4a10f3e.yaml b/releasenotes/notes/quota-update-for-LB-b21e7bc9e4a10f3e.yaml new file mode 100644 index 000000000..9d6321db3 --- /dev/null +++ b/releasenotes/notes/quota-update-for-LB-b21e7bc9e4a10f3e.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Quota of Loadbalancers and listeners can now be updated. From 84c42160e9eac654e734a19032237fdf1a8cb058 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Wed, 27 Jul 2016 22:37:13 +0000 Subject: [PATCH 463/845] Move find_resource family to API binding layer find_resource variants are also used by OSC plugin. It looks reasonable to move them to the API binding layer. This commit does not touch the related unit tests to ensure backward compatibility. Related-Bug: #1521291 Change-Id: Iec3e9b379255111f5390325778a1d07bf73b29d6 --- neutronclient/neutron/v2_0/__init__.py | 96 ++-------------------- neutronclient/neutron/v2_0/tag.py | 3 +- neutronclient/tests/unit/test_cli20.py | 3 +- neutronclient/tests/unit/test_cli20_tag.py | 7 +- neutronclient/v2_0/client.py | 88 ++++++++++++++++++++ 5 files changed, 100 insertions(+), 97 deletions(-) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index 05402f1f7..51c4b7daf 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -20,7 +20,6 @@ import argparse import functools import logging -import re from cliff import command from cliff import lister @@ -32,104 +31,26 @@ from neutronclient.common import exceptions from neutronclient.common import utils -HEX_ELEM = '[0-9A-Fa-f]' -UUID_PATTERN = '-'.join([HEX_ELEM + '{8}', HEX_ELEM + '{4}', - HEX_ELEM + '{4}', HEX_ELEM + '{4}', - HEX_ELEM + '{12}']) HYPHEN_OPTS = ['tags_any', 'not_tags', 'not_tags_any'] -def _get_resource_plural(resource, client): - plurals = getattr(client, 'EXTED_PLURALS', []) - for k in plurals: - if plurals[k] == resource: - return k - return resource + 's' - - def find_resource_by_id(client, resource, resource_id, cmd_resource=None, parent_id=None, fields=None): - if not cmd_resource: - cmd_resource = resource - cmd_resource_plural = _get_resource_plural(cmd_resource, client) - resource_plural = _get_resource_plural(resource, client) - obj_lister = getattr(client, "list_%s" % cmd_resource_plural) - # perform search by id only if we are passing a valid UUID - match = re.match(UUID_PATTERN, resource_id) - collection = resource_plural - if match: - params = {'id': resource_id} - if fields: - params['fields'] = fields - if parent_id: - data = obj_lister(parent_id, **params) - else: - data = obj_lister(**params) - if data and data[collection]: - return data[collection][0] - not_found_message = (_("Unable to find %(resource)s with id " - "'%(id)s'") % - {'resource': resource, 'id': resource_id}) - # 404 is raised by exceptions.NotFound to simulate serverside behavior - raise exceptions.NotFound(message=not_found_message) + return client.find_resource_by_id(resource, resource_id, cmd_resource, + parent_id, fields) def find_resourceid_by_id(client, resource, resource_id, cmd_resource=None, parent_id=None): - info = find_resource_by_id(client, resource, resource_id, cmd_resource, - parent_id, fields='id') - return info['id'] - - -def _find_resource_by_name(client, resource, name, project_id=None, - cmd_resource=None, parent_id=None, fields=None): - if not cmd_resource: - cmd_resource = resource - cmd_resource_plural = _get_resource_plural(cmd_resource, client) - resource_plural = _get_resource_plural(resource, client) - obj_lister = getattr(client, "list_%s" % cmd_resource_plural) - params = {'name': name} - if fields: - params['fields'] = fields - if project_id: - params['tenant_id'] = project_id - if parent_id: - data = obj_lister(parent_id, **params) - else: - data = obj_lister(**params) - collection = resource_plural - info = data[collection] - if len(info) > 1: - raise exceptions.NeutronClientNoUniqueMatch(resource=resource, - name=name) - elif len(info) == 0: - not_found_message = (_("Unable to find %(resource)s with name " - "'%(name)s'") % - {'resource': resource, 'name': name}) - # 404 is raised by exceptions.NotFound to simulate serverside behavior - raise exceptions.NotFound(message=not_found_message) - else: - return info[0] + return find_resource_by_id(client, resource, resource_id, cmd_resource, + parent_id, fields='id')['id'] def find_resource_by_name_or_id(client, resource, name_or_id, project_id=None, cmd_resource=None, parent_id=None, fields=None): - try: - return find_resource_by_id(client, resource, name_or_id, - cmd_resource, parent_id, fields) - except exceptions.NotFound: - try: - return _find_resource_by_name(client, resource, name_or_id, - project_id, cmd_resource, parent_id, - fields) - except exceptions.NotFound: - not_found_message = (_("Unable to find %(resource)s with name " - "or id '%(name_or_id)s'") % - {'resource': resource, - 'name_or_id': name_or_id}) - raise exceptions.NotFound( - message=not_found_message) + return client.find_resource(resource, name_or_id, project_id, + cmd_resource, parent_id, fields) def find_resourceid_by_name_or_id(client, resource, name_or_id, @@ -695,8 +616,7 @@ def args2search_opts(self, parsed_args): return search_opts def call_server(self, neutron_client, search_opts, parsed_args): - resource_plural = _get_resource_plural(self.cmd_resource, - neutron_client) + resource_plural = neutron_client.get_resource_plural(self.cmd_resource) obj_lister = getattr(neutron_client, "list_%s" % resource_plural) if self.parent_id: data = obj_lister(self.parent_id, **search_opts) @@ -729,7 +649,7 @@ def retrieve_list(self, parsed_args): if dirs: search_opts.update({'sort_dir': dirs}) data = self.call_server(neutron_client, search_opts, parsed_args) - collection = _get_resource_plural(self.resource, neutron_client) + collection = neutron_client.get_resource_plural(self.resource) return data.get(collection, []) def extend_list(self, data, parsed_args): diff --git a/neutronclient/neutron/v2_0/tag.py b/neutronclient/neutron/v2_0/tag.py index 2507d417e..774849cd8 100644 --- a/neutronclient/neutron/v2_0/tag.py +++ b/neutronclient/neutron/v2_0/tag.py @@ -20,8 +20,7 @@ def _convert_resource_args(client, parsed_args): - resource_type = neutronv20._get_resource_plural( - parsed_args.resource_type, client) + resource_type = client.get_resource_plural(parsed_args.resource_type) resource_id = neutronv20.find_resourceid_by_name_or_id( client, parsed_args.resource_type, parsed_args.resource) return resource_type, resource_id diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 19bd29708..324782f0b 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -250,8 +250,7 @@ def _test_create_resource(self, resource, cmd, name, myid, args, ress[resource].update({'name': name}) resstr = self.client.serialize(ress) # url method body - resource_plural = neutronV2_0._get_resource_plural(cmd_resource, - self.client) + resource_plural = self.client.get_resource_plural(cmd_resource) path = getattr(self.client, resource_plural + "_path") if parent_id: path = path % parent_id diff --git a/neutronclient/tests/unit/test_cli20_tag.py b/neutronclient/tests/unit/test_cli20_tag.py index 24f07b73a..09b99ab0a 100644 --- a/neutronclient/tests/unit/test_cli20_tag.py +++ b/neutronclient/tests/unit/test_cli20_tag.py @@ -15,7 +15,6 @@ from mox3 import mox from neutronclient.common import exceptions -from neutronclient.neutron import v2_0 as neutronV2_0 from neutronclient.neutron.v2_0 import network from neutronclient.neutron.v2_0 import tag from neutronclient import shell @@ -68,14 +67,12 @@ def _test_tags_query(self, cmd, resources, args, query): def _make_tag_path(self, resource, resource_id, tag): path = getattr(self.client, "tag_path") - resource_plural = neutronV2_0._get_resource_plural(resource, - self.client) + resource_plural = self.client.get_resource_plural(resource) return path % (resource_plural, resource_id, tag) def _make_tags_path(self, resource, resource_id): path = getattr(self.client, "tags_path") - resource_plural = neutronV2_0._get_resource_plural(resource, - self.client) + resource_plural = self.client.get_resource_plural(resource) return path % (resource_plural, resource_id) def test_add_tag(self): diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 5504fd1dd..0b5ca6b07 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -18,6 +18,7 @@ import inspect import itertools import logging +import re import time import debtcollector.renames @@ -35,6 +36,11 @@ _logger = logging.getLogger(__name__) +HEX_ELEM = '[0-9A-Fa-f]' +UUID_PATTERN = '-'.join([HEX_ELEM + '{8}', HEX_ELEM + '{4}', + HEX_ELEM + '{4}', HEX_ELEM + '{4}', + HEX_ELEM + '{12}']) + def exception_handler_v20(status_code, error_content): """Exception handler for API v2.0 client. @@ -396,6 +402,88 @@ def _convert_into_with_meta(self, item, resp): else: return _TupleWithMeta((), resp) + def get_resource_plural(self, resource): + for k in self.EXTED_PLURALS: + if self.EXTED_PLURALS[k] == resource: + return k + return resource + 's' + + def _find_resource_by_id(self, resource, resource_id, cmd_resource=None, + parent_id=None, fields=None): + if not cmd_resource: + cmd_resource = resource + cmd_resource_plural = self.get_resource_plural(cmd_resource) + resource_plural = self.get_resource_plural(resource) + # TODO(amotoki): Use show_%s instead of list_%s + obj_lister = getattr(self, "list_%s" % cmd_resource_plural) + # perform search by id only if we are passing a valid UUID + match = re.match(UUID_PATTERN, resource_id) + collection = resource_plural + if match: + params = {'id': resource_id} + if fields: + params['fields'] = fields + if parent_id: + data = obj_lister(parent_id, **params) + else: + data = obj_lister(**params) + if data and data[collection]: + return data[collection][0] + not_found_message = (_("Unable to find %(resource)s with id " + "'%(id)s'") % + {'resource': resource, 'id': resource_id}) + # 404 is raised by exceptions.NotFound to simulate serverside behavior + raise exceptions.NotFound(message=not_found_message) + + def _find_resource_by_name(self, resource, name, project_id=None, + cmd_resource=None, parent_id=None, fields=None): + if not cmd_resource: + cmd_resource = resource + cmd_resource_plural = self.get_resource_plural(cmd_resource) + resource_plural = self.get_resource_plural(resource) + obj_lister = getattr(self, "list_%s" % cmd_resource_plural) + params = {'name': name} + if fields: + params['fields'] = fields + if project_id: + params['tenant_id'] = project_id + if parent_id: + data = obj_lister(parent_id, **params) + else: + data = obj_lister(**params) + collection = resource_plural + info = data[collection] + if len(info) > 1: + raise exceptions.NeutronClientNoUniqueMatch(resource=resource, + name=name) + elif len(info) == 0: + not_found_message = (_("Unable to find %(resource)s with name " + "'%(name)s'") % + {'resource': resource, 'name': name}) + # 404 is raised by exceptions.NotFound + # to simulate serverside behavior + raise exceptions.NotFound(message=not_found_message) + else: + return info[0] + + def find_resource(self, resource, name_or_id, project_id=None, + cmd_resource=None, parent_id=None, fields=None): + try: + return self._find_resource_by_id(resource, name_or_id, + cmd_resource, parent_id, fields) + except exceptions.NotFound: + try: + return self._find_resource_by_name( + resource, name_or_id, project_id, + cmd_resource, parent_id, fields) + except exceptions.NotFound: + not_found_message = (_("Unable to find %(resource)s with name " + "or id '%(name_or_id)s'") % + {'resource': resource, + 'name_or_id': name_or_id}) + raise exceptions.NotFound( + message=not_found_message) + class Client(ClientBase): From 449a1f7d13b0983ea39aa21c3d5423a984f8ad12 Mon Sep 17 00:00:00 2001 From: Armando Migliaccio Date: Mon, 11 Jul 2016 17:23:12 -0700 Subject: [PATCH 464/845] Add trunk commands to openstackclient This patch introduces the client bindings for the trunk extension. It uses the openstackclient framework, and thus requires the openstack client to work. New commands introduced are as follows: "openstack network trunk create" to create a trunk. "openstack network trunk set" to update/add subports to a trunk. "openstack network trunk unset" to remove subports from trunk. "openstack network trunk list" to list all trunks. "openstack network trunk delete" to delete trunks. "openstack subport list --trunk " to list all subports belonging to a trunk. DocImpact: Openstackclient now supports CLIs to configure trunk resource via OSC plugin for neutronclient Change-Id: I6fe1dbd81813fae234801a61c0e3d89f9e7c791e Co-authored-by: SongmingYan Co-authored-by: Abhishek Raut Partial-implements: blueprint vlan-aware-vms --- neutronclient/osc/v2/trunk/__init__.py | 0 neutronclient/osc/v2/trunk/network_trunk.py | 347 +++++++++++ .../tests/unit/osc/v2/trunk/__init__.py | 0 .../tests/unit/osc/v2/trunk/fakes.py | 83 +++ .../unit/osc/v2/trunk/test_network_trunk.py | 570 ++++++++++++++++++ neutronclient/v2_0/client.py | 39 ++ ...d-osc-trunk-commands-7e77283a369729c5.yaml | 8 + setup.cfg | 7 + 8 files changed, 1054 insertions(+) create mode 100644 neutronclient/osc/v2/trunk/__init__.py create mode 100644 neutronclient/osc/v2/trunk/network_trunk.py create mode 100644 neutronclient/tests/unit/osc/v2/trunk/__init__.py create mode 100644 neutronclient/tests/unit/osc/v2/trunk/fakes.py create mode 100644 neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py create mode 100644 releasenotes/notes/add-osc-trunk-commands-7e77283a369729c5.yaml diff --git a/neutronclient/osc/v2/trunk/__init__.py b/neutronclient/osc/v2/trunk/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/osc/v2/trunk/network_trunk.py b/neutronclient/osc/v2/trunk/network_trunk.py new file mode 100644 index 000000000..fa8fb3f7a --- /dev/null +++ b/neutronclient/osc/v2/trunk/network_trunk.py @@ -0,0 +1,347 @@ +# Copyright 2016 ZTE Corporation. +# 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. +# + +"""Network trunk and subports action implementations""" +import logging + +from osc_lib.cli import parseractions +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils + +# TODO(abhiraut): Switch to neutronclients identity utils +from openstackclient.identity import common as identity_common + +from neutronclient._i18n import _ +# TODO(abhiraut): Switch to client methods +from neutronclient.neutron import v2_0 as neutronV20 + +LOG = logging.getLogger(__name__) + +TRUNK = 'trunk' +TRUNKS = 'trunks' +SUB_PORTS = 'sub_ports' + + +class CreateNetworkTrunk(command.ShowOne): + """Create a network trunk for a given project""" + + def get_parser(self, prog_name): + parser = super(CreateNetworkTrunk, self).get_parser(prog_name) + parser.add_argument( + 'name', + metavar='', + help=_("Name of the trunk to create") + ) + parser.add_argument( + '--parent-port', + metavar='', + required=True, + help=_("Parent port belonging to this trunk (name or ID)") + ) + parser.add_argument( + '--subport', + metavar='', + action=parseractions.MultiKeyValueAction, dest='add_subports', + help=_("Subport to add. Subport is of form " + "\'port=,segmentation-type=,segmentation-ID=\' " + "(--subport) option can be repeated)") + ) + admin_group = parser.add_mutually_exclusive_group() + admin_group.add_argument( + '--enable', + action='store_true', + default=True, + help=_("Enable trunk (default)") + ) + admin_group.add_argument( + '--disable', + action='store_true', + help=_("Disable trunk") + ) + parser.add_argument( + '--project', + metavar='', + help=_("Owner's project (name or ID)") + ) + identity_common.add_project_domain_option_to_parser(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + attrs = _get_attrs_for_trunk(self.app.client_manager, + parsed_args) + body = {TRUNK: attrs} + obj = client.create_trunk(body) + columns = _get_columns(obj[TRUNK]) + data = utils.get_dict_properties(obj[TRUNK], columns, + formatters=_formatters) + return columns, data + + +class DeleteNetworkTrunk(command.Command): + """Delete a given network trunk""" + + def get_parser(self, prog_name): + parser = super(DeleteNetworkTrunk, self).get_parser(prog_name) + parser.add_argument( + 'trunk', + metavar="", + nargs="+", + help=_("Trunk(s) to delete (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + result = 0 + for trunk in parsed_args.trunk: + try: + trunk_id = _get_id(client, trunk, TRUNK) + client.delete_trunk(trunk_id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete trunk with name " + "or ID '%(trunk)s': %(e)s") + % {'trunk': trunk, 'e': e}) + if result > 0: + total = len(parsed_args.trunk) + msg = (_("%(result)s of %(total)s trunks failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) + + +class ListNetworkTrunk(command.Lister): + """List all network trunks""" + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + data = client.list_trunks() + # TODO(abhiraut): List more columns using --long + headers = ('ID', 'Name', 'Parent Port') + columns = ('id', 'name', 'port_id') + return (headers, + (utils.get_dict_properties( + s, columns, + formatters=_formatters, + ) for s in data[TRUNKS])) + + +class SetNetworkTrunk(command.Command): + """Set network trunk properties""" + + def get_parser(self, prog_name): + parser = super(SetNetworkTrunk, self).get_parser(prog_name) + parser.add_argument( + 'trunk', + metavar="", + help=_("Trunk to modify (name or ID)") + ) + parser.add_argument( + '--name', + metavar="", + help=_("Set trunk name") + ) + parser.add_argument( + '--subport', + metavar='', + action=parseractions.MultiKeyValueAction, dest='set_subports', + help=_("Subport to add. Subport is of form " + "\'port=,segmentation-type=,segmentation-ID=\'" + "(--subport) option can be repeated)") + ) + admin_group = parser.add_mutually_exclusive_group() + admin_group.add_argument( + '--enable', + action='store_true', + help=_("Enable trunk") + ) + admin_group.add_argument( + '--disable', + action='store_true', + help=_("Disable trunk") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + trunk_id = _get_id(client, parsed_args.trunk, TRUNK) + attrs = _get_attrs_for_trunk(self.app.client_manager, parsed_args) + body = {TRUNK: attrs} + client.update_trunk(trunk_id, body) + if parsed_args.set_subports: + subport_attrs = _get_attrs_for_subports(self.app.client_manager, + parsed_args) + client.trunk_add_subports(trunk_id, subport_attrs) + + +class ShowNetworkTrunk(command.ShowOne): + """Show information of a given network trunk""" + def get_parser(self, prog_name): + parser = super(ShowNetworkTrunk, self).get_parser(prog_name) + parser.add_argument( + 'trunk', + metavar="", + help=_("Trunk to display (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + trunk_id = _get_id(client, parsed_args.trunk, TRUNK) + obj = client.show_trunk(trunk_id) + columns = _get_columns(obj[TRUNK]) + data = utils.get_dict_properties(obj[TRUNK], columns, + formatters=_formatters) + return columns, data + + +class ListNetworkSubport(command.Lister): + """List all subports for a given network trunk""" + + def get_parser(self, prog_name): + parser = super(ListNetworkSubport, self).get_parser(prog_name) + parser.add_argument( + '--trunk', + required=True, + metavar="", + help=_("List subports belonging to this trunk (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + trunk_id = _get_id(client, parsed_args.trunk, TRUNK) + data = client.trunk_get_subports(trunk_id) + headers = ('Port', 'Segmentation Type', 'Segmentation ID') + columns = ('port_id', 'segmentation_type', 'segmentation_id') + return (headers, + (utils.get_dict_properties( + s, columns, + ) for s in data[SUB_PORTS])) + + +class UnsetNetworkTrunk(command.Command): + """Unset subports from a given network trunk""" + + def get_parser(self, prog_name): + parser = super(UnsetNetworkTrunk, self).get_parser(prog_name) + parser.add_argument( + 'trunk', + metavar="", + help=_("Unset subports from this trunk (name or ID)") + ) + parser.add_argument( + '--subport', + metavar="", + required=True, + action='append', dest='unset_subports', + help=_("Subport to delete (name or ID of the port) " + "(--subport) option can be repeated") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + attrs = _get_attrs_for_subports(self.app.client_manager, parsed_args) + trunk_id = _get_id(client, parsed_args.trunk, TRUNK) + client.trunk_remove_subports(trunk_id, attrs) + + +def _format_admin_state(item): + return 'UP' if item else 'DOWN' + + +_formatters = { + 'admin_state_up': _format_admin_state, + 'sub_ports': utils.format_list_of_dicts, +} + + +def _get_columns(item): + return tuple(sorted(list(item.keys()))) + + +def _get_attrs_for_trunk(client_manager, parsed_args): + attrs = {} + if parsed_args.name is not None: + attrs['name'] = str(parsed_args.name) + if parsed_args.enable: + attrs['admin_state_up'] = True + if parsed_args.disable: + attrs['admin_state_up'] = False + if 'parent_port' in parsed_args and parsed_args.parent_port is not None: + port_id = _get_id(client_manager.neutronclient, + parsed_args.parent_port, 'port') + attrs['port_id'] = port_id + if 'add_subports' in parsed_args and parsed_args.add_subports is not None: + attrs[SUB_PORTS] = _format_subports(client_manager, + parsed_args.add_subports) + + # "trunk set" command doesn't support setting project. + if 'project' in parsed_args and parsed_args.project is not None: + identity_client = client_manager.identity + project_id = identity_common.find_project( + identity_client, + parsed_args.project, + parsed_args.project_domain, + ).id + attrs['tenant_id'] = project_id + + return attrs + + +def _format_subports(client_manager, subports): + attrs = [] + for subport in subports: + subport_attrs = {} + if subport.get('port'): + port_id = _get_id(client_manager.neutronclient, + subport['port'], 'port') + subport_attrs['port_id'] = port_id + if subport.get('segmentation-id'): + try: + subport_attrs['segmentation_id'] = int( + subport['segmentation-id']) + except ValueError: + msg = (_("Segmentation-id '%s' is not an integer") % + subport['segmentation-id']) + raise exceptions.CommandError(msg) + if subport.get('segmentation-type'): + subport_attrs['segmentation_type'] = subport['segmentation-type'] + attrs.append(subport_attrs) + return attrs + + +def _get_attrs_for_subports(client_manager, parsed_args): + attrs = {} + if 'set_subports' in parsed_args and parsed_args.set_subports is not None: + attrs[SUB_PORTS] = _format_subports(client_manager, + parsed_args.set_subports) + if ('unset_subports' in parsed_args and + parsed_args.unset_subports is not None): + subports_list = [] + for subport in parsed_args.unset_subports: + port_id = _get_id(client_manager.neutronclient, + subport, 'port') + subports_list.append({'port_id': port_id}) + attrs[SUB_PORTS] = subports_list + return attrs + + +def _get_id(client, id_or_name, resource): + return neutronV20.find_resourceid_by_name_or_id( + client, resource, str(id_or_name)) diff --git a/neutronclient/tests/unit/osc/v2/trunk/__init__.py b/neutronclient/tests/unit/osc/v2/trunk/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/tests/unit/osc/v2/trunk/fakes.py b/neutronclient/tests/unit/osc/v2/trunk/fakes.py new file mode 100644 index 000000000..0eb6f9786 --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/trunk/fakes.py @@ -0,0 +1,83 @@ +# 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 copy +import mock +import uuid + + +class FakeTrunk(object): + """Fake one or more trunks.""" + @staticmethod + def create_one_trunk(attrs=None): + """Create a fake trunk. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A Dictionary with id, name, admin_state_up, + port_id, sub_ports, status and project_id + """ + attrs = attrs or {} + + # Set default attributes. + trunk_attrs = { + 'id': 'trunk-id-' + uuid.uuid4().hex, + 'name': 'trunk-name-' + uuid.uuid4().hex, + 'port_id': 'port-' + uuid.uuid4().hex, + 'admin_state_up': True, + 'project_id': 'project-id-' + uuid.uuid4().hex, + 'status': 'ACTIVE', + 'sub_ports': [{'port_id': 'subport-' + uuid.uuid4().hex, + 'segmentation_type': 'vlan', + 'segmentation_id': 100}], + } + + # Overwrite default attributes. + trunk_attrs.update(attrs) + return copy.deepcopy(trunk_attrs) + + @staticmethod + def create_trunks(attrs=None, count=2): + """Create multiple fake trunks. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of routers to fake + :return: + A list of dictionaries faking the trunks + """ + trunks = [] + for i in range(0, count): + trunks.append(FakeTrunk.create_one_trunk(attrs)) + + return trunks + + @staticmethod + def get_trunks(trunks=None, count=2): + """Get an iterable MagicMock object with a list of faked trunks. + + If trunks list is provided, then initialize the Mock object with the + list. Otherwise create one. + + :param List trunks: + A list of FakeResource objects faking trunks + :param int count: + The number of trunks to fake + :return: + An iterable Mock object with side_effect set to a list of faked + trunks + """ + if trunks is None: + trunks = FakeTrunk.create_trunks(count) + return mock.MagicMock(side_effect=trunks) diff --git a/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py b/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py new file mode 100644 index 000000000..ff9baa073 --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py @@ -0,0 +1,570 @@ +# Copyright 2016 ZTE Corporation. +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import mock +from mock import call + +from osc_lib import exceptions +from osc_lib import utils + +# TODO(abhiraut): Switch to osc-lib test utils +from openstackclient.tests import utils as tests_utils + +from neutronclient.osc.v2.trunk import network_trunk as trunk +from neutronclient.tests.unit.osc.v2 import fakes as test_fakes +from neutronclient.tests.unit.osc.v2.trunk import fakes + + +def _get_id(client, id_or_name, resource): + return id_or_name + + +class TestCreateNetworkTrunk(test_fakes.TestNeutronClientOSCV2): + # The new trunk created + _trunk = fakes.FakeTrunk.create_one_trunk() + + columns = ( + 'admin_state_up', + 'id', + 'name', + 'port_id', + 'project_id', + 'status', + 'sub_ports', + ) + data = ( + trunk._format_admin_state(_trunk['admin_state_up']), + _trunk['id'], + _trunk['name'], + _trunk['port_id'], + _trunk['project_id'], + _trunk['status'], + utils.format_list_of_dicts(_trunk['sub_ports']), + ) + + def setUp(self): + super(TestCreateNetworkTrunk, self).setUp() + mock.patch('neutronclient.osc.v2.trunk.network_trunk._get_id', + new=_get_id).start() + self.neutronclient.create_trunk = mock.Mock( + return_value={trunk.TRUNK: self._trunk}) + + # Get the command object to test + self.cmd = trunk.CreateNetworkTrunk(self.app, self.namespace) + + def test_create_no_options(self): + arglist = [] + verifylist = [] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_create_default_options(self): + arglist = [ + "--parent-port", self._trunk['port_id'], + self._trunk['name'], + ] + verifylist = [ + ('parent_port', self._trunk['port_id']), + ('name', self._trunk['name']), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = (self.cmd.take_action(parsed_args)) + + self.neutronclient.create_trunk.assert_called_once_with({ + trunk.TRUNK: {'name': self._trunk['name'], + 'admin_state_up': self._trunk['admin_state_up'], + 'port_id': self._trunk['port_id']} + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_create_full_options(self): + subport = self._trunk['sub_ports'][0] + arglist = [ + "--disable", + "--parent-port", self._trunk['port_id'], + "--subport", 'port=%(port)s,segmentation-type=%(seg_type)s,' + 'segmentation-id=%(seg_id)s' % { + 'seg_id': subport['segmentation_id'], + 'seg_type': subport['segmentation_type'], + 'port': subport['port_id']}, + self._trunk['name'], + ] + verifylist = [ + ('name', self._trunk['name']), + ('parent_port', self._trunk['port_id']), + ('add_subports', [{ + 'port': subport['port_id'], + 'segmentation-id': str(subport['segmentation_id']), + 'segmentation-type': subport['segmentation_type']}]), + ('disable', True), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = (self.cmd.take_action(parsed_args)) + + self.neutronclient.create_trunk.assert_called_once_with({ + trunk.TRUNK: {'name': self._trunk['name'], + 'admin_state_up': False, + 'sub_ports': [subport], + 'port_id': self._trunk['port_id']} + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_create_trunk_with_subport_invalid_segmentation_id_fail(self): + subport = self._trunk['sub_ports'][0] + arglist = [ + "--parent-port", self._trunk['port_id'], + "--subport", "port=%(port)s,segmentation-type=%(seg_type)s," + "segmentation-id=boom" % { + 'seg_type': subport['segmentation_type'], + 'port': subport['port_id']}, + self._trunk['name'], + ] + verifylist = [ + ('name', self._trunk['name']), + ('parent_port', self._trunk['port_id']), + ('add_subports', [{ + 'port': subport['port_id'], + 'segmentation-id': 'boom', + 'segmentation-type': subport['segmentation_type']}]), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual("Segmentation-id 'boom' is not an integer", + str(e)) + + +class TestDeleteNetworkTrunk(test_fakes.TestNeutronClientOSCV2): + # The trunk to be deleted. + _trunks = fakes.FakeTrunk.create_trunks(count=2) + + def setUp(self): + super(TestDeleteNetworkTrunk, self).setUp() + + mock.patch('neutronclient.osc.v2.trunk.network_trunk._get_id', + new=_get_id).start() + self.neutronclient.delete_trunk = mock.Mock(return_value=None) + + # Get the command object to test + self.cmd = trunk.DeleteNetworkTrunk(self.app, self.namespace) + + def test_delete_trunk(self): + arglist = [ + self._trunks[0]['name'], + ] + verifylist = [ + ('trunk', [self._trunks[0]['name']]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.neutronclient.delete_trunk.assert_called_once_with( + self._trunks[0]['name']) + self.assertIsNone(result) + + def test_delete_trunk_multiple(self): + arglist = [] + verifylist = [] + + for t in self._trunks: + arglist.append(t['name']) + verifylist = [ + ('trunk', arglist), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + calls = [] + for t in self._trunks: + calls.append(call(t['name'])) + self.neutronclient.delete_trunk.assert_has_calls(calls) + self.assertIsNone(result) + + def test_delete_trunk_multiple_with_exception(self): + arglist = [ + self._trunks[0]['name'], + 'unexist_trunk', + ] + verifylist = [ + ('trunk', + [self._trunks[0]['name'], 'unexist_trunk']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + get_mock_result = [self._trunks[0], exceptions.CommandError] + trunk._get_id = ( + mock.MagicMock(side_effect=get_mock_result) + ) + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 trunks failed to delete.', str(e)) + self.neutronclient.delete_trunk.assert_called_once_with( + self._trunks[0] + ) + + +class TestShowNetworkTrunk(test_fakes.TestNeutronClientOSCV2): + + # The trunk to set. + _trunk = fakes.FakeTrunk.create_one_trunk() + + columns = ( + 'admin_state_up', + 'id', + 'name', + 'port_id', + 'project_id', + 'status', + 'sub_ports', + ) + data = ( + trunk._format_admin_state(_trunk['admin_state_up']), + _trunk['id'], + _trunk['name'], + _trunk['port_id'], + _trunk['project_id'], + _trunk['status'], + utils.format_list_of_dicts(_trunk['sub_ports']), + ) + + def setUp(self): + super(TestShowNetworkTrunk, self).setUp() + + mock.patch('neutronclient.osc.v2.trunk.network_trunk._get_id', + new=_get_id).start() + self.neutronclient.show_trunk = mock.Mock( + return_value={trunk.TRUNK: self._trunk}) + + # Get the command object to test + self.cmd = trunk.ShowNetworkTrunk(self.app, self.namespace) + + def test_show_no_options(self): + arglist = [] + verifylist = [] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_show_all_options(self): + arglist = [ + self._trunk['id'], + ] + verifylist = [ + ('trunk', self._trunk['id']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.neutronclient.show_trunk.assert_called_once_with( + self._trunk['id']) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestListNetworkTrunk(test_fakes.TestNeutronClientOSCV2): + # Create trunks to be listed. + _trunks = fakes.FakeTrunk.create_trunks(count=3) + + columns = ( + 'ID', + 'Name', + 'Parent Port', + ) + data = [] + for t in _trunks: + data.append(( + t['id'], + t['name'], + t['port_id'], + )) + + def setUp(self): + super(TestListNetworkTrunk, self).setUp() + mock.patch('neutronclient.osc.v2.trunk.network_trunk._get_id', + new=_get_id).start() + self.neutronclient.list_trunks = mock.Mock( + return_value={trunk.TRUNKS: self._trunks}) + + # Get the command object to test + self.cmd = trunk.ListNetworkTrunk(self.app, self.namespace) + + def test_trunk_list_no_option(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.neutronclient.list_trunks.assert_called_once_with() + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + +class TestSetNetworkTrunk(test_fakes.TestNeutronClientOSCV2): + # Create trunks to be listed. + _trunk = fakes.FakeTrunk.create_one_trunk() + + columns = ( + 'admin_state_up', + 'id', + 'name', + 'port_id', + 'project_id', + 'status', + 'sub_ports', + ) + data = ( + trunk._format_admin_state(_trunk['admin_state_up']), + _trunk['id'], + _trunk['name'], + _trunk['port_id'], + _trunk['project_id'], + _trunk['status'], + utils.format_list_of_dicts(_trunk['sub_ports']), + ) + + def setUp(self): + super(TestSetNetworkTrunk, self).setUp() + mock.patch('neutronclient.osc.v2.trunk.network_trunk._get_id', + new=_get_id).start() + self.neutronclient.update_trunk = mock.Mock( + return_value={trunk.TRUNK: self._trunk}) + self.neutronclient.trunk_add_subports = mock.Mock( + return_value=self._trunk) + + # Get the command object to test + self.cmd = trunk.SetNetworkTrunk(self.app, self.namespace) + + def test_set_network_trunk_name(self): + arglist = [ + '--name', 'trunky', + self._trunk['name'], + ] + verifylist = [ + ('name', 'trunky'), + ('trunk', self._trunk['name']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + attrs = { + 'name': 'trunky', + } + self.neutronclient.update_trunk.assert_called_once_with( + self._trunk['name'], {trunk.TRUNK: attrs}) + self.assertIsNone(result) + + def test_set_network_trunk_admin_state_up_disable(self): + arglist = [ + '--disable', + self._trunk['name'], + ] + verifylist = [ + ('disable', True), + ('trunk', self._trunk['name']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + attrs = { + 'admin_state_up': False, + } + self.neutronclient.update_trunk.assert_called_once_with( + self._trunk['name'], {trunk.TRUNK: attrs}) + self.assertIsNone(result) + + def test_set_network_trunk_admin_state_up_enable(self): + arglist = [ + '--enable', + self._trunk['name'], + ] + verifylist = [ + ('enable', True), + ('trunk', self._trunk['name']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + attrs = { + 'admin_state_up': True, + } + self.neutronclient.update_trunk.assert_called_once_with( + self._trunk['name'], {trunk.TRUNK: attrs}) + self.assertIsNone(result) + + def test_set_network_trunk_nothing(self): + arglist = [self._trunk['name'], ] + verifylist = [('trunk', self._trunk['name']), ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = {} + self.neutronclient.update_trunk.assert_called_once_with( + self._trunk['name'], {trunk.TRUNK: attrs}) + self.assertIsNone(result) + + def test_set_network_trunk_subports(self): + subport = self._trunk['sub_ports'][0] + arglist = [ + "--subport", 'port=%(port)s,segmentation-type=%(seg_type)s,' + 'segmentation-id=%(seg_id)s' % { + 'seg_id': subport['segmentation_id'], + 'seg_type': subport['segmentation_type'], + 'port': subport['port_id']}, + self._trunk['name'], + ] + verifylist = [ + ('trunk', self._trunk['name']), + ('set_subports', [{ + 'port': subport['port_id'], + 'segmentation-id': str(subport['segmentation_id']), + 'segmentation-type': subport['segmentation_type']}]), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.neutronclient.trunk_add_subports.assert_called_once_with( + self._trunk['name'], {'sub_ports': [subport]} + ) + self.assertIsNone(result) + + +class TestListNetworkSubport(test_fakes.TestNeutronClientOSCV2): + + _trunk = fakes.FakeTrunk.create_one_trunk() + _subports = _trunk['sub_ports'] + + columns = ( + 'Port', + 'Segmentation Type', + 'Segmentation ID', + ) + data = [] + for s in _subports: + data.append(( + s['port_id'], + s['segmentation_type'], + s['segmentation_id'], + )) + + def setUp(self): + super(TestListNetworkSubport, self).setUp() + mock.patch('neutronclient.osc.v2.trunk.network_trunk._get_id', + new=_get_id).start() + self.neutronclient.trunk_get_subports = mock.Mock( + return_value={trunk.SUB_PORTS: self._subports}) + + # Get the command object to test + self.cmd = trunk.ListNetworkSubport(self.app, self.namespace) + + def test_subport_list(self): + arglist = [ + '--trunk', self._trunk['name'], + ] + verifylist = [ + ('trunk', self._trunk['name']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.neutronclient.trunk_get_subports.assert_called_once_with( + self._trunk['name']) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + +class TestUnsetNetworkTrunk(test_fakes.TestNeutronClientOSCV2): + + _trunk = fakes.FakeTrunk.create_one_trunk() + + columns = ( + 'admin_state_up', + 'id', + 'name', + 'port_id', + 'project_id', + 'status', + 'sub_ports', + ) + data = ( + trunk._format_admin_state(_trunk['admin_state_up']), + _trunk['id'], + _trunk['name'], + _trunk['port_id'], + _trunk['project_id'], + _trunk['status'], + utils.format_list_of_dicts(_trunk['sub_ports']), + ) + + def setUp(self): + super(TestUnsetNetworkTrunk, self).setUp() + + mock.patch('neutronclient.osc.v2.trunk.network_trunk._get_id', + new=_get_id).start() + self.neutronclient.trunk_remove_subports = mock.Mock( + return_value=None) + + # Get the command object to test + self.cmd = trunk.UnsetNetworkTrunk(self.app, self.namespace) + + def test_unset_network_trunk_subport(self): + subport = self._trunk['sub_ports'][0] + arglist = [ + "--subport", subport['port_id'], + self._trunk['name'], + ] + verifylist = [ + ('trunk', self._trunk['name']), + ('unset_subports', [subport['port_id']]), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.neutronclient.trunk_remove_subports.assert_called_once_with( + self._trunk['name'], + {trunk.SUB_PORTS: [{'port_id': subport['port_id']}]} + ) + self.assertIsNone(result) + + def test_unset_subport_no_arguments_fail(self): + arglist = [ + self._trunk['name'], + ] + verifylist = [ + ('trunk', self._trunk['name']), + ] + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 5504fd1dd..4dc55e3ef 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -521,6 +521,11 @@ class Client(ClientBase): network_ip_availability_path = '/network-ip-availabilities/%s' tags_path = "/%s/%s/tags" tag_path = "/%s/%s/tags/%s" + trunks_path = "/trunks" + trunk_path = "/trunks/%s" + subports_path = "/trunks/%s/get_subports" + subports_add_path = "/trunks/%s/add_subports" + subports_remove_path = "/trunks/%s/remove_subports" # API has no way to report plurals, so we have to hard code them EXTED_PLURALS = {'routers': 'router', @@ -566,6 +571,7 @@ class Client(ClientBase): 'bgp_speakers': 'bgp_speaker', 'bgp_peers': 'bgp_peer', 'network_ip_availabilities': 'network_ip_availability', + 'trunks': 'trunk', } def list_ext(self, collection, path, retrieve_all, **_params): @@ -1800,6 +1806,39 @@ def remove_tag_all(self, resource_type, resource_id, **_params): """Remove all tags on the resource.""" return self.delete(self.tags_path % (resource_type, resource_id)) + def create_trunk(self, body=None): + """Create a trunk port.""" + return self.post(self.trunks_path, body=body) + + def update_trunk(self, trunk, body=None): + """Update a trunk port.""" + return self.put(self.trunk_path % trunk, body=body) + + def delete_trunk(self, trunk): + """Delete a trunk port.""" + return self.delete(self.trunk_path % (trunk)) + + def list_trunks(self, retrieve_all=True, **_params): + """Fetch a list of all trunk ports.""" + return self.list('trunks', self.trunks_path, retrieve_all, + **_params) + + def show_trunk(self, trunk, **_params): + """Fetch information for a certain trunk port.""" + return self.get(self.trunk_path % (trunk), params=_params) + + def trunk_add_subports(self, trunk, body=None): + """Add specified subports to the trunk.""" + return self.put(self.subports_add_path % (trunk), body=body) + + def trunk_remove_subports(self, trunk, body=None): + """Removes specified subports from the trunk.""" + return self.put(self.subports_remove_path % (trunk), body=body) + + def trunk_get_subports(self, trunk, **_params): + """Fetch a list of all subports attached to given trunk.""" + return self.get(self.subports_path % (trunk), params=_params) + def __init__(self, **kwargs): """Initialize a new client for the Neutron v2.0 API.""" super(Client, self).__init__(**kwargs) diff --git a/releasenotes/notes/add-osc-trunk-commands-7e77283a369729c5.yaml b/releasenotes/notes/add-osc-trunk-commands-7e77283a369729c5.yaml new file mode 100644 index 000000000..684ae3102 --- /dev/null +++ b/releasenotes/notes/add-osc-trunk-commands-7e77283a369729c5.yaml @@ -0,0 +1,8 @@ +--- +features: + - | + Add ``network trunk create``, ``network trunk list``, + ``network trunk set``, ``network trunk unset``, ``network trunk delete`` + and ``network subport list`` OSC commands for trunk resource along with + client bindings. + [Blueprint `vlan-aware-vms `_] diff --git a/setup.cfg b/setup.cfg index 89d31ec54..bb780713e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -35,6 +35,13 @@ openstack.cli.extension = neutronclient = neutronclient.osc.plugin openstack.neutronclient.v2 = + network_subport_list = neutronclient.osc.v2.trunk.network_trunk:ListNetworkSubport + network_trunk_create = neutronclient.osc.v2.trunk.network_trunk:CreateNetworkTrunk + network_trunk_delete = neutronclient.osc.v2.trunk.network_trunk:DeleteNetworkTrunk + network_trunk_list = neutronclient.osc.v2.trunk.network_trunk:ListNetworkTrunk + network_trunk_set = neutronclient.osc.v2.trunk.network_trunk:SetNetworkTrunk + network_trunk_show = neutronclient.osc.v2.trunk.network_trunk:ShowNetworkTrunk + network_trunk_unset = neutronclient.osc.v2.trunk.network_trunk:UnsetNetworkTrunk [build_sphinx] all_files = 1 From 43403b7b07bdd071388b3ea798c43d1d16a1f5bf Mon Sep 17 00:00:00 2001 From: Abhishek Raut Date: Thu, 28 Jul 2016 16:40:39 -0700 Subject: [PATCH 465/845] Add long option to network trunk list command This patch adds a '--long' option to trunk list OSC CLI. Using '--long' option will now allow user to see additional fields while listing all trunks. This patch is also a follow up patch for change I6fe1dbd81813fae234801a61c0e3d89f9e7c791e to address multiple TODOs like importing modules from osc-lib instead direct imports and adding exception handling for NetworkTrunkSet command with appropriate unit tests. Change-Id: Iae1801b0b4f6bc2ca434290041ab5277285e400b --- neutronclient/osc/v2/trunk/network_trunk.py | 55 ++++++++--- neutronclient/tests/unit/osc/v2/fakes.py | 2 +- .../unit/osc/v2/trunk/test_network_trunk.py | 93 +++++++++++++++++-- 3 files changed, 128 insertions(+), 22 deletions(-) diff --git a/neutronclient/osc/v2/trunk/network_trunk.py b/neutronclient/osc/v2/trunk/network_trunk.py index fa8fb3f7a..e3eed827a 100644 --- a/neutronclient/osc/v2/trunk/network_trunk.py +++ b/neutronclient/osc/v2/trunk/network_trunk.py @@ -26,8 +26,6 @@ from openstackclient.identity import common as identity_common from neutronclient._i18n import _ -# TODO(abhiraut): Switch to client methods -from neutronclient.neutron import v2_0 as neutronV20 LOG = logging.getLogger(__name__) @@ -58,7 +56,7 @@ def get_parser(self, prog_name): action=parseractions.MultiKeyValueAction, dest='add_subports', help=_("Subport to add. Subport is of form " "\'port=,segmentation-type=,segmentation-ID=\' " - "(--subport) option can be repeated)") + "(--subport) option can be repeated") ) admin_group = parser.add_mutually_exclusive_group() admin_group.add_argument( @@ -127,12 +125,38 @@ def take_action(self, parsed_args): class ListNetworkTrunk(command.Lister): """List all network trunks""" + def get_parser(self, prog_name): + parser = super(ListNetworkTrunk, self).get_parser(prog_name) + parser.add_argument( + '--long', + action='store_true', + default=False, + help=_("List additional fields in output") + ) + return parser + def take_action(self, parsed_args): client = self.app.client_manager.neutronclient data = client.list_trunks() - # TODO(abhiraut): List more columns using --long - headers = ('ID', 'Name', 'Parent Port') - columns = ('id', 'name', 'port_id') + headers = ( + 'ID', + 'Name', + 'Parent Port' + ) + columns = ( + 'id', + 'name', + 'port_id' + ) + if parsed_args.long: + headers += ( + 'Status', + 'State', + ) + columns += ( + 'status', + 'admin_state_up', + ) return (headers, (utils.get_dict_properties( s, columns, @@ -161,7 +185,7 @@ def get_parser(self, prog_name): action=parseractions.MultiKeyValueAction, dest='set_subports', help=_("Subport to add. Subport is of form " "\'port=,segmentation-type=,segmentation-ID=\'" - "(--subport) option can be repeated)") + "(--subport) option can be repeated") ) admin_group = parser.add_mutually_exclusive_group() admin_group.add_argument( @@ -181,11 +205,21 @@ def take_action(self, parsed_args): trunk_id = _get_id(client, parsed_args.trunk, TRUNK) attrs = _get_attrs_for_trunk(self.app.client_manager, parsed_args) body = {TRUNK: attrs} - client.update_trunk(trunk_id, body) + try: + client.update_trunk(trunk_id, body) + except Exception as e: + msg = (_("Failed to set trunk '%(t)s': %(e)s") + % {'t': parsed_args.trunk, 'e': e}) + raise exceptions.CommandError(msg) if parsed_args.set_subports: subport_attrs = _get_attrs_for_subports(self.app.client_manager, parsed_args) - client.trunk_add_subports(trunk_id, subport_attrs) + try: + client.trunk_add_subports(trunk_id, subport_attrs) + except Exception as e: + msg = (_("Failed to add subports to trunk '%(t)s': %(e)s") + % {'t': parsed_args.trunk, 'e': e}) + raise exceptions.CommandError(msg) class ShowNetworkTrunk(command.ShowOne): @@ -343,5 +377,4 @@ def _get_attrs_for_subports(client_manager, parsed_args): def _get_id(client, id_or_name, resource): - return neutronV20.find_resourceid_by_name_or_id( - client, resource, str(id_or_name)) + return client.find_resource(resource, str(id_or_name))['id'] diff --git a/neutronclient/tests/unit/osc/v2/fakes.py b/neutronclient/tests/unit/osc/v2/fakes.py index 45f2b253b..75277a952 100644 --- a/neutronclient/tests/unit/osc/v2/fakes.py +++ b/neutronclient/tests/unit/osc/v2/fakes.py @@ -14,7 +14,7 @@ import argparse import mock -from openstackclient.tests import utils +from osc_lib.tests import utils class TestNeutronClientOSCV2(utils.TestCommand): diff --git a/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py b/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py index ff9baa073..5bb5ea214 100644 --- a/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py +++ b/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py @@ -15,13 +15,12 @@ import mock from mock import call +import testtools from osc_lib import exceptions +from osc_lib.tests import utils as tests_utils from osc_lib import utils -# TODO(abhiraut): Switch to osc-lib test utils -from openstackclient.tests import utils as tests_utils - from neutronclient.osc.v2.trunk import network_trunk as trunk from neutronclient.tests.unit.osc.v2 import fakes as test_fakes from neutronclient.tests.unit.osc.v2.trunk import fakes @@ -147,10 +146,8 @@ def test_create_trunk_with_subport_invalid_segmentation_id_fail(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - try: + with testtools.ExpectedException(exceptions.CommandError) as e: self.cmd.take_action(parsed_args) - self.fail('CommandError should be raised.') - except exceptions.CommandError as e: self.assertEqual("Segmentation-id 'boom' is not an integer", str(e)) @@ -217,10 +214,8 @@ def test_delete_trunk_multiple_with_exception(self): trunk._get_id = ( mock.MagicMock(side_effect=get_mock_result) ) - try: + with testtools.ExpectedException(exceptions.CommandError) as e: self.cmd.take_action(parsed_args) - self.fail('CommandError should be raised.') - except exceptions.CommandError as e: self.assertEqual('1 of 2 trunks failed to delete.', str(e)) self.neutronclient.delete_trunk.assert_called_once_with( self._trunks[0] @@ -295,6 +290,10 @@ class TestListNetworkTrunk(test_fakes.TestNeutronClientOSCV2): 'Name', 'Parent Port', ) + columns_long = columns + ( + 'Status', + 'State', + ) data = [] for t in _trunks: data.append(( @@ -302,6 +301,15 @@ class TestListNetworkTrunk(test_fakes.TestNeutronClientOSCV2): t['name'], t['port_id'], )) + data_long = [] + for t in _trunks: + data_long.append(( + t['id'], + t['name'], + t['port_id'], + t['status'], + trunk._format_admin_state(t['admin_state_up']), + )) def setUp(self): super(TestListNetworkTrunk, self).setUp() @@ -324,6 +332,21 @@ def test_trunk_list_no_option(self): self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) + def test_trunk_list_long(self): + arglist = [ + '--long', + ] + verifylist = [ + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.neutronclient.list_trunks.assert_called_once_with() + self.assertEqual(self.columns_long, columns) + self.assertEqual(self.data_long, list(data)) + class TestSetNetworkTrunk(test_fakes.TestNeutronClientOSCV2): # Create trunks to be listed. @@ -435,7 +458,7 @@ def test_set_network_trunk_nothing(self): def test_set_network_trunk_subports(self): subport = self._trunk['sub_ports'][0] arglist = [ - "--subport", 'port=%(port)s,segmentation-type=%(seg_type)s,' + '--subport', 'port=%(port)s,segmentation-type=%(seg_type)s,' 'segmentation-id=%(seg_id)s' % { 'seg_id': subport['segmentation_id'], 'seg_type': subport['segmentation_type'], @@ -458,6 +481,56 @@ def test_set_network_trunk_subports(self): ) self.assertIsNone(result) + def test_set_trunk_attrs_with_exception(self): + arglist = [ + '--name', 'reallylongname', + self._trunk['name'], + ] + verifylist = [ + ('trunk', self._trunk['name']), + ('name', 'reallylongname'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.neutronclient.update_trunk = ( + mock.MagicMock(side_effect=exceptions.CommandError) + ) + with testtools.ExpectedException(exceptions.CommandError) as e: + self.cmd.take_action(parsed_args) + self.assertEqual( + "Failed to set trunk '%s': " % self._trunk['name'], + str(e)) + attrs = {'name': 'reallylongname'} + self.neutronclient.update_trunk.assert_called_once_with( + self._trunk['name'], {trunk.TRUNK: attrs}) + self.neutronclient.trunk_add_subports.assert_not_called() + + def test_set_trunk_add_subport_with_exception(self): + arglist = [ + '--subport', 'port=invalid_subport', + self._trunk['name'], + ] + verifylist = [ + ('trunk', self._trunk['name']), + ('set_subports', [{'port': 'invalid_subport'}]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.neutronclient.trunk_add_subports = ( + mock.MagicMock(side_effect=exceptions.CommandError) + ) + with testtools.ExpectedException(exceptions.CommandError) as e: + self.cmd.take_action(parsed_args) + self.assertEqual( + "Failed to add subports to trunk '%s': " % self._trunk['name'], + str(e)) + self.neutronclient.update_trunk.assert_called_once_with( + self._trunk['name'], {trunk.TRUNK: {}}) + self.neutronclient.trunk_add_subports.assert_called_once_with( + self._trunk['name'], + {'sub_ports': [{'port_id': 'invalid_subport'}]} + ) + class TestListNetworkSubport(test_fakes.TestNeutronClientOSCV2): From 151608146b2c9da040390e3028d60ccb6c15f85e Mon Sep 17 00:00:00 2001 From: Abhishek Raut Date: Fri, 29 Jul 2016 23:21:29 -0700 Subject: [PATCH 466/845] Add documentation for OSC plugin commands This patch adds a new path under neutronclient docs to document the newly added OSC commands. All new CLI commands added via the openstack client plugin must now provide a user guide to these commands under doc/source/usage/osc/v2 Change-Id: Id16d2fb0712fe6dd4a45a765bf7b65410302f5b8 --- doc/source/index.rst | 1 + doc/source/usage/osc/v2/network-trunk.rst | 171 ++++++++++++++++++++++ doc/source/usage/osc_cli_plugins.rst | 33 +++++ 3 files changed, 205 insertions(+) create mode 100644 doc/source/usage/osc/v2/network-trunk.rst create mode 100644 doc/source/usage/osc_cli_plugins.rst diff --git a/doc/source/index.rst b/doc/source/index.rst index d7efbd272..be4c77057 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -35,6 +35,7 @@ Using neutronclient usage/cli usage/library + usage/osc_cli_plugins Developer Guide --------------- diff --git a/doc/source/usage/osc/v2/network-trunk.rst b/doc/source/usage/osc/v2/network-trunk.rst new file mode 100644 index 000000000..5a79310a4 --- /dev/null +++ b/doc/source/usage/osc/v2/network-trunk.rst @@ -0,0 +1,171 @@ +============= +network trunk +============= + +A **network trunk** is a container to group logical ports from different +networks and provide a single trunked vNIC for servers. It consists of +one parent port which is a regular VIF and multiple subports which allow +the server to connect to more networks. + +Network v2 + +network subport list +-------------------- + +List all subports for a given network trunk + +.. program:: network subport list +.. code:: bash + + os network subport list + --trunk + +.. option:: --trunk + + List subports belonging to this trunk (name or ID) (required) + +network trunk create +-------------------- + +Create a network trunk for a given project + +.. program:: network trunk create +.. code:: bash + + os network trunk create + --parent-port + [--subport ] + [--enable | --disable] + [--project [--project-domain ]] + + +.. option:: --parent-port + + Parent port belonging to this trunk (name or ID) (required) + +.. option:: --subport + + Subport to add. Subport is of form 'port=,segmentation-type=,segmentation-ID=' + (--subport) option can be repeated + +.. option:: --enable + + Enable trunk (default) + +.. option:: --disable + + Disable trunk + +.. option:: --project + + Owner's project (name or ID) + +.. option:: --project-domain + + Domain the project belongs to (name or ID). + This can be used in case collisions between project names exist. + +network trunk delete +-------------------- + +Delete a given network trunk + +.. program:: network trunk delete +.. code:: bash + + os network trunk delete + [ ...] + +.. _network_trunk_delete-trunk: +.. describe:: + + Trunk(s) to delete (name or ID) + +network trunk list +------------------ + +List all network trunks + +.. program:: network trunk list +.. code:: bash + + os network trunk list + [--long] + +.. option:: --long + + List additional fields in output + +network trunk set +----------------- + +Set network trunk properties + +.. program:: network trunk set +.. code:: bash + + os network trunk set + [--name ] + [--subport ] + [--enable | --disable] + + +.. option:: --name + + Set trunk name + +.. option:: --subport + + Subport to add. Subport is of form 'port=,segmentation-type=,segmentation-ID=' + (--subport) option can be repeated + +.. option:: --enable + + Enable trunk + +.. option:: --disable + + Disable trunk + +.. _network_trunk_set-trunk: +.. describe:: + + Trunk to modify (name or ID) + +network trunk show +------------------ + +Show information of a given network trunk + +.. program:: network trunk show +.. code:: bash + + os network trunk show + + +.. _network_trunk_show-trunk: +.. describe:: + + Trunk to display (name or ID) + +network trunk unset +------------------- + +Unset subports from a given network trunk + +.. program:: network trunk unset +.. code:: bash + + os network trunk unset + --subport + + +.. option:: --subport + + Subport to delete (name or ID of the port) (required) + (--subport) option can be repeated + +.. _network_trunk_unset-trunk: +.. describe:: + + Unset subports from this trunk (name or ID) diff --git a/doc/source/usage/osc_cli_plugins.rst b/doc/source/usage/osc_cli_plugins.rst new file mode 100644 index 000000000..343c3a9d3 --- /dev/null +++ b/doc/source/usage/osc_cli_plugins.rst @@ -0,0 +1,33 @@ +.. + 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. + + + Convention for heading levels in Neutron devref: + ======= Heading 0 (reserved for the title in a document) + ------- Heading 1 + ~~~~~~~ Heading 2 + +++++++ Heading 3 + ''''''' Heading 4 + (Avoid deeper levels because they do not render well.) + +Using Network CLI extensions to OpenStack Client +================================================ + +List of released CLI commands available in openstack client. These commands +can be referenced by doing ``openstack help network``. + +.. toctree:: + :glob: + :maxdepth: 2 + + osc/v2/* From 0dbbb9c516406740a577296d55b002905f3776fd Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Mon, 1 Aug 2016 18:47:27 +0000 Subject: [PATCH 467/845] Updated from global requirements Change-Id: Ic374936346fe630322f94fe0ef82bb5c29c4d211 --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 8d91b3ce7..3ce9a2314 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,9 +9,9 @@ netaddr!=0.7.16,>=0.7.12 # BSD osc-lib>=0.4.0 # Apache-2.0 oslo.i18n>=2.1.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 -oslo.utils>=3.15.0 # Apache-2.0 +oslo.utils>=3.16.0 # Apache-2.0 os-client-config>=1.13.1 # Apache-2.0 -keystoneauth1>=2.7.0 # Apache-2.0 +keystoneauth1>=2.10.0 # Apache-2.0 requests>=2.10.0 # Apache-2.0 simplejson>=2.2.0 # MIT six>=1.9.0 # MIT From 092c3e5e91a89fd63040df0477202a4607409abc Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Mon, 1 Aug 2016 09:33:20 +0000 Subject: [PATCH 468/845] Make find_resourceid_by_id public in python binding class This is a regression of Iec3e9b379255111f5390325778a1d07bf73b29d. The unit test coverage was missing. Change-Id: I0fc5d6dedf469ea70854a53b4e79f3b8bf9206a6 Closes-Bug: #1608403 --- neutronclient/tests/unit/test_name_or_id.py | 35 +++++++++++++++++++++ neutronclient/v2_0/client.py | 8 ++--- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/neutronclient/tests/unit/test_name_or_id.py b/neutronclient/tests/unit/test_name_or_id.py index 7ae3ff9c8..dcb9a23e6 100644 --- a/neutronclient/tests/unit/test_name_or_id.py +++ b/neutronclient/tests/unit/test_name_or_id.py @@ -190,3 +190,38 @@ def test_get_id_from_name_multiple_with_project_not_found(self): self.client, 'security_group', name, project) self.assertIn('Unable to find', exc.message) self.assertEqual(404, exc.status_code) + + def _test_get_resource_by_id(self, id_only=False): + _id = str(uuid.uuid4()) + net = {'id': _id, 'name': 'test'} + reses = {'networks': [net], } + resstr = self.client.serialize(reses) + self.mox.StubOutWithMock(self.client.httpclient, "request") + path = getattr(self.client, "networks_path") + if id_only: + query_params = "fields=id&id=%s" % _id + else: + query_params = "id=%s" % _id + self.client.httpclient.request( + test_cli20.MyUrlComparator( + test_cli20.end_url(path, query_params), + self.client), + 'GET', + body=None, + headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) + ).AndReturn((test_cli20.MyResp(200), resstr)) + self.mox.ReplayAll() + if id_only: + returned_id = neutronV20.find_resourceid_by_id( + self.client, 'network', _id) + self.assertEqual(_id, returned_id) + else: + result = neutronV20.find_resource_by_id( + self.client, 'network', _id) + self.assertEqual(net, result) + + def test_get_resource_by_id(self): + self._test_get_resource_by_id(id_only=False) + + def test_get_resourceid_by_id(self): + self._test_get_resource_by_id(id_only=True) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 0b5ca6b07..eaa084650 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -408,8 +408,8 @@ def get_resource_plural(self, resource): return k return resource + 's' - def _find_resource_by_id(self, resource, resource_id, cmd_resource=None, - parent_id=None, fields=None): + def find_resource_by_id(self, resource, resource_id, cmd_resource=None, + parent_id=None, fields=None): if not cmd_resource: cmd_resource = resource cmd_resource_plural = self.get_resource_plural(cmd_resource) @@ -469,8 +469,8 @@ def _find_resource_by_name(self, resource, name, project_id=None, def find_resource(self, resource, name_or_id, project_id=None, cmd_resource=None, parent_id=None, fields=None): try: - return self._find_resource_by_id(resource, name_or_id, - cmd_resource, parent_id, fields) + return self.find_resource_by_id(resource, name_or_id, + cmd_resource, parent_id, fields) except exceptions.NotFound: try: return self._find_resource_by_name( From 9f78b6320d66cad993d8f1783932816dfd588428 Mon Sep 17 00:00:00 2001 From: zheng yin Date: Tue, 12 Jul 2016 05:22:03 +0800 Subject: [PATCH 469/845] Add Python 3.5 classifier and venv There is a passing gate job, we can claim support for Python 3.5 in the classifier. This patch also adds the convenience py35 venv. Change-Id: I595320fb8b8e97951b8d445a14eb4f5a311a151f --- setup.cfg | 1 + tox.ini | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 89d31ec54..888b985e4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -18,6 +18,7 @@ classifier = Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 Programming Language :: Python :: 3.4 + Programming Language :: Python :: 3.5 [files] packages = diff --git a/tox.ini b/tox.ini index a20951fec..062ca6724 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] # py3 first to avoid .testrepository incompatibility -envlist = py34,py27,pypy,pep8 +envlist = py35,py34,py27,pypy,pep8 minversion = 1.6 skipsdist = True From ccab3e5e9b04b7e17404771ff806a82da7dadddf Mon Sep 17 00:00:00 2001 From: "hobo.kengo" Date: Sun, 7 Aug 2016 23:53:59 +0000 Subject: [PATCH 470/845] Disallow specifying name for meter-label-rule Change-Id: Ib002d5dd66403c7d82f3ad7bbb6f037bd5365093 Closes-Bug: #1609717 --- neutronclient/neutron/v2_0/metering.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/neutronclient/neutron/v2_0/metering.py b/neutronclient/neutron/v2_0/metering.py index a6ecef8e0..24982c19d 100644 --- a/neutronclient/neutron/v2_0/metering.py +++ b/neutronclient/neutron/v2_0/metering.py @@ -76,6 +76,7 @@ class ShowMeteringLabelRule(neutronv20.ShowCommand): """Show information of a given metering label rule.""" resource = 'metering_label_rule' + allow_names = False class CreateMeteringLabelRule(neutronv20.CreateCommand): @@ -115,3 +116,4 @@ class DeleteMeteringLabelRule(neutronv20.DeleteCommand): """Delete a given metering label.""" resource = 'metering_label_rule' + allow_names = False From 850101e3562ad636f075ad57ecc2efbe844e3a72 Mon Sep 17 00:00:00 2001 From: Monty Taylor Date: Mon, 8 Aug 2016 14:42:39 -0500 Subject: [PATCH 471/845] Remove environment defaults neutronclient uses os-client-config behind the scenes, which provides default values from the environment as well for all of these values. However, occ does it in a way that does allows incorporating config file based options as well. Setting env var defaults to argparse here winds up with two different systems fighting for variable precedence. Change-Id: I4dc95a72873391530d224add8095d7bbe8c445b8 --- neutronclient/shell.py | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 0870599c3..486177ab0 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -601,12 +601,10 @@ def _append_global_identity_args(self, parser): parser.add_argument( '--os-cloud', metavar='', - default=env('OS_CLOUD', default=None), help=_('Defaults to env[OS_CLOUD].')) parser.add_argument( '--os-auth-url', metavar='', - default=env('OS_AUTH_URL'), help=_('Authentication URL, defaults to env[OS_AUTH_URL].')) parser.add_argument( '--os_auth_url', @@ -615,13 +613,11 @@ def _append_global_identity_args(self, parser): project_name_group = parser.add_mutually_exclusive_group() project_name_group.add_argument( '--os-tenant-name', metavar='', - default=env('OS_TENANT_NAME'), help=_('Authentication tenant name, defaults to ' 'env[OS_TENANT_NAME].')) project_name_group.add_argument( '--os-project-name', metavar='', - default=utils.env('OS_PROJECT_NAME'), help=_('Another way to specify tenant name. ' 'This option is mutually exclusive with ' ' --os-tenant-name. ' @@ -634,13 +630,11 @@ def _append_global_identity_args(self, parser): project_id_group = parser.add_mutually_exclusive_group() project_id_group.add_argument( '--os-tenant-id', metavar='', - default=env('OS_TENANT_ID'), help=_('Authentication tenant ID, defaults to ' 'env[OS_TENANT_ID].')) project_id_group.add_argument( '--os-project-id', metavar='', - default=utils.env('OS_PROJECT_ID'), help=_('Another way to specify tenant ID. ' 'This option is mutually exclusive with ' ' --os-tenant-id. ' @@ -648,7 +642,6 @@ def _append_global_identity_args(self, parser): parser.add_argument( '--os-username', metavar='', - default=utils.env('OS_USERNAME'), help=_('Authentication username, defaults to env[OS_USERNAME].')) parser.add_argument( '--os_username', @@ -656,7 +649,6 @@ def _append_global_identity_args(self, parser): parser.add_argument( '--os-user-id', metavar='', - default=env('OS_USER_ID'), help=_('Authentication user ID (Env: OS_USER_ID)')) parser.add_argument( @@ -666,7 +658,6 @@ def _append_global_identity_args(self, parser): parser.add_argument( '--os-user-domain-id', metavar='', - default=utils.env('OS_USER_DOMAIN_ID'), help=_('OpenStack user domain ID. ' 'Defaults to env[OS_USER_DOMAIN_ID].')) @@ -677,7 +668,6 @@ def _append_global_identity_args(self, parser): parser.add_argument( '--os-user-domain-name', metavar='', - default=utils.env('OS_USER_DOMAIN_NAME'), help=_('OpenStack user domain name. ' 'Defaults to env[OS_USER_DOMAIN_NAME].')) @@ -696,19 +686,16 @@ def _append_global_identity_args(self, parser): parser.add_argument( '--os-project-domain-id', metavar='', - default=utils.env('OS_PROJECT_DOMAIN_ID'), help=_('Defaults to env[OS_PROJECT_DOMAIN_ID].')) parser.add_argument( '--os-project-domain-name', metavar='', - default=utils.env('OS_PROJECT_DOMAIN_NAME'), help=_('Defaults to env[OS_PROJECT_DOMAIN_NAME].')) parser.add_argument( '--os-cert', metavar='', - default=utils.env('OS_CERT'), help=_("Path of certificate file to use in SSL " "connection. This file can optionally be " "prepended with the private key. Defaults " @@ -717,7 +704,6 @@ def _append_global_identity_args(self, parser): parser.add_argument( '--os-cacert', metavar='', - default=env('OS_CACERT', default=None), help=_("Specify a CA bundle file to use in " "verifying a TLS (https) server certificate. " "Defaults to env[OS_CACERT].")) @@ -725,7 +711,6 @@ def _append_global_identity_args(self, parser): parser.add_argument( '--os-key', metavar='', - default=utils.env('OS_KEY'), help=_("Path of client key to use in SSL " "connection. This option is not necessary " "if your key is prepended to your certificate " @@ -733,7 +718,6 @@ def _append_global_identity_args(self, parser): parser.add_argument( '--os-password', metavar='', - default=utils.env('OS_PASSWORD'), help=_('Authentication password, defaults to env[OS_PASSWORD].')) parser.add_argument( '--os_password', @@ -741,7 +725,6 @@ def _append_global_identity_args(self, parser): parser.add_argument( '--os-region-name', metavar='', - default=env('OS_REGION_NAME'), help=_('Authentication region name, defaults to ' 'env[OS_REGION_NAME].')) parser.add_argument( @@ -750,7 +733,6 @@ def _append_global_identity_args(self, parser): parser.add_argument( '--os-token', metavar='', - default=env('OS_TOKEN'), help=_('Authentication token, defaults to env[OS_TOKEN].')) parser.add_argument( '--os_token', @@ -764,7 +746,6 @@ def _append_global_identity_args(self, parser): parser.add_argument( '--os-url', metavar='', - default=env('OS_URL'), help=_('Defaults to env[OS_URL].')) parser.add_argument( '--os_url', From c91f9f2ccd7d61012673d5f3adb201e0767b939d Mon Sep 17 00:00:00 2001 From: Hirofumi Ichihara Date: Thu, 21 Jul 2016 16:27:36 +0900 Subject: [PATCH 472/845] Don't decode empty response body Some neutron POST and PUT APIs don't return response body, for instance, dhcp-agent-network-add and l3-agent-router-add. This patch add a check for response body. Change-Id: Ia772927a28cb83c4c88680b5add925b12978dc5d Related-Bug: #1555921 --- neutronclient/tests/unit/test_cli20.py | 5 +++++ neutronclient/v2_0/client.py | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 19bd29708..80362858e 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -793,6 +793,11 @@ def test_list_request_ids_with_retrieve_all_false(self): self.mox.VerifyAll() self.mox.UnsetStubs() + def test_deserialize_without_data(self): + data = u'' + result = self.client.deserialize(data, 200) + self.assertEqual(data, result) + class CLITestV20ExceptionHandler(CLITestV20Base): diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 5504fd1dd..9a01b38b1 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -305,7 +305,8 @@ def serialize(self, data): def deserialize(self, data, status_code): """Deserializes a JSON string into a dictionary.""" - if status_code == 204: + # TODO(hichihara): Remove checking 204 in bug 1611167 + if status_code == 204 or not data: return data return serializer.Serializer().deserialize( data)['body'] From b16c9a8d3d70fe2ec69ab44b740011dc2b2bc097 Mon Sep 17 00:00:00 2001 From: reedip Date: Tue, 5 Jan 2016 16:32:36 +0900 Subject: [PATCH 473/845] Add support for Bulk Delete in NeutronClient The following patch adds support for BulkDelete in NeutronClient. Currently, the core existing Neutron CLIs are going to support Bulk Deletion in NeutronClient. However, if any extension does not require Bulk Delete, it can be disabled by updating the class attribute 'bulk_delete'. DocImpact Depends-On: Ib23d1e53947b5dffcff8db0dde77cae0a0b31243 Change-Id: I3b8a05698625baad3906784e3ecffb0f0242d660 Closes-Bug: #1495440 --- neutronclient/neutron/v2_0/__init__.py | 60 +++++++++++++++---- neutronclient/tests/unit/test_cli20.py | 25 +++++--- .../tests/unit/test_cli20_network.py | 9 +++ .../bulk-delete-support-94a353db08efec8d.yaml | 9 +++ 4 files changed, 85 insertions(+), 18 deletions(-) create mode 100644 releasenotes/notes/bulk-delete-support-94a353db08efec8d.yaml diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index 51c4b7daf..4468bfb3a 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -478,17 +478,19 @@ class DeleteCommand(NeutronCommand): log = None allow_names = True help_resource = None + bulk_delete = True def get_parser(self, prog_name): parser = super(DeleteCommand, self).get_parser(prog_name) - if self.allow_names: - help_str = _('ID or name of %s to delete.') - else: - help_str = _('ID of %s to delete.') if not self.help_resource: self.help_resource = self.resource + if self.allow_names: + help_str = _('ID(s) or name(s) of %s to delete.') + else: + help_str = _('ID(s) of %s to delete.') parser.add_argument( 'id', metavar=self.resource.upper(), + nargs='+' if self.bulk_delete else 1, help=help_str % self.help_resource) self.add_known_arguments(parser) return parser @@ -498,24 +500,62 @@ def take_action(self, parsed_args): neutron_client = self.get_client() obj_deleter = getattr(neutron_client, "delete_%s" % self.cmd_resource) + + if self.bulk_delete: + self._bulk_delete(obj_deleter, neutron_client, parsed_args.id) + else: + self.delete_item(obj_deleter, neutron_client, parsed_args.id) + print((_('Deleted %(resource)s: %(id)s') + % {'id': parsed_args.id, + 'resource': self.resource}), + file=self.app.stdout) + return + + def _bulk_delete(self, obj_deleter, neutron_client, parsed_args_ids): + successful_delete = [] + non_existent = [] + multiple_ids = [] + for item_id in parsed_args_ids: + try: + self.delete_item(obj_deleter, neutron_client, item_id) + successful_delete.append(item_id) + except exceptions.NotFound: + non_existent.append(item_id) + except exceptions.NeutronClientNoUniqueMatch: + multiple_ids.append(item_id) + if successful_delete: + print((_('Deleted %(resource)s(s): %(id)s')) + % {'id': ", ".join(successful_delete), + 'resource': self.cmd_resource}, + file=self.app.stdout) + if non_existent: + print((_("Unable to find %(resource)s(s) with id(s) " + "'%(id)s'") % + {'resource': self.cmd_resource, + 'id': ", ".join(non_existent)}), + file=self.app.stdout) + if multiple_ids: + print((_("Multiple %(resource)s(s) matches found for name(s)" + " '%(id)s'. Please use an ID to be more specific.")) % + {'resource': self.cmd_resource, + 'id': ", ".join(multiple_ids)}, + file=self.app.stdout) + + def delete_item(self, obj_deleter, neutron_client, item_id): if self.allow_names: params = {'cmd_resource': self.cmd_resource, 'parent_id': self.parent_id} _id = find_resourceid_by_name_or_id(neutron_client, self.resource, - parsed_args.id, + item_id, **params) else: - _id = parsed_args.id + _id = item_id if self.parent_id: obj_deleter(_id, self.parent_id) else: obj_deleter(_id) - print((_('Deleted %(resource)s: %(id)s') - % {'id': parsed_args.id, - 'resource': self.resource}), - file=self.app.stdout) return diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 324782f0b..11077ad10 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -504,14 +504,7 @@ def _test_show_resource(self, resource, cmd, myid, args, fields=(), self.assertIn(myid, _str) self.assertIn('myname', _str) - def _test_delete_resource(self, resource, cmd, myid, args, - cmd_resource=None, parent_id=None): - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) - if not cmd_resource: - cmd_resource = resource - path = getattr(self.client, cmd_resource + "_path") + def _test_set_path_and_delete(self, path, parent_id, myid): if parent_id: path = path % (parent_id, myid) else: @@ -521,6 +514,20 @@ def _test_delete_resource(self, resource, cmd, myid, args, body=None, headers=mox.ContainsKeyValue( 'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), None)) + + def _test_delete_resource(self, resource, cmd, myid, args, + cmd_resource=None, parent_id=None, + extra_ids=None): + self.mox.StubOutWithMock(cmd, "get_client") + self.mox.StubOutWithMock(self.client.httpclient, "request") + cmd.get_client().MultipleTimes().AndReturn(self.client) + if not cmd_resource: + cmd_resource = resource + path = getattr(self.client, cmd_resource + "_path") + self._test_set_path_and_delete(path, parent_id, myid) + # extra_ids is used to test for bulk_delete + if extra_ids: + self._test_set_path_and_delete(path, parent_id, extra_ids) self.mox.ReplayAll() cmd_parser = cmd.get_parser("delete_" + cmd_resource) shell.run_command(cmd, cmd_parser, args) @@ -528,6 +535,8 @@ def _test_delete_resource(self, resource, cmd, myid, args, self.mox.UnsetStubs() _str = self.fake_stdout.make_string() self.assertIn(myid, _str) + if extra_ids: + self.assertIn(extra_ids, _str) def _test_update_resource_action(self, resource, cmd, myid, action, args, body, retval=None, cmd_resource=None): diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index f900d7548..f28b8a448 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -588,6 +588,15 @@ def test_delete_network(self): args = [myid] self._test_delete_resource(resource, cmd, myid, args) + def test_bulk_delete_network(self): + # Delete net: myid1 myid2. + resource = 'network' + cmd = network.DeleteNetwork(test_cli20.MyApp(sys.stdout), None) + myid1 = 'myid1' + myid2 = 'myid2' + args = [myid1, myid2] + self._test_delete_resource(resource, cmd, myid1, args, extra_ids=myid2) + def _test_extend_list(self, mox_calls): data = [{'id': 'netid%d' % i, 'name': 'net%d' % i, 'subnets': ['mysubid%d' % i]} diff --git a/releasenotes/notes/bulk-delete-support-94a353db08efec8d.yaml b/releasenotes/notes/bulk-delete-support-94a353db08efec8d.yaml new file mode 100644 index 000000000..4d2575d9d --- /dev/null +++ b/releasenotes/notes/bulk-delete-support-94a353db08efec8d.yaml @@ -0,0 +1,9 @@ +--- +features: + - | + CLI support for bulk delete. + + * By using this feature, multiple resource + can be deleted using a single command. + * Example: ``neutron router-delete router_a router_b`` + deletes both router_a and router_b. From 9fc1f4656d42534db30f5dabe30d4d4c052ab551 Mon Sep 17 00:00:00 2001 From: Clenimar Filemon Date: Thu, 16 Jun 2016 14:33:58 -0300 Subject: [PATCH 474/845] Update docs to use Identity v3 The docs currently show deprecated forms of client creation. Update them to use keystoneauth Session and Identity v3. Change-Id: I3ec285e9b02db870276bae6a9b7e1278e5d306f6 Closes-Bug: #1566448 --- doc/source/usage/cli.rst | 17 +++++++++++++-- doc/source/usage/library.rst | 42 ++++++++++++++++++++++++++++-------- 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/doc/source/usage/cli.rst b/doc/source/usage/cli.rst index 93a5e6675..4471ac35d 100644 --- a/doc/source/usage/cli.rst +++ b/doc/source/usage/cli.rst @@ -30,10 +30,23 @@ Basic Usage ----------- In order to use the CLI, you must provide your OpenStack username, password, -tenant, and auth endpoint. Use the corresponding configuration options -(``--os-username``, ``--os-password``, ``--os-tenant-name``, and +project, domain information for both user and project, and auth endpoint. Use +the corresponding configuration options (``--os-username``, ``--os-password``, +``--os-project-name``, ``--os-user-domain-id``, ``os-project-domain-id``, and ``--os-auth-url``), but it is easier to set them in environment variables. +.. code-block:: shell + + export OS_USERNAME=user + export OS_PASSWORD=pass + export OS_PROJECT_NAME=project + export OS_USER_DOMAIN_ID=default + export OS_PROJECT_DOMAIN_ID=default + export OS_AUTH_URL=http://auth.example.com:5000/v3 + +If you are using Identity v2.0 API (DEPRECATED), you don't need to pass domain +information. + .. code-block:: shell export OS_USERNAME=user diff --git a/doc/source/usage/library.rst b/doc/source/usage/library.rst index 481a08b45..8ab601cfd 100644 --- a/doc/source/usage/library.rst +++ b/doc/source/usage/library.rst @@ -26,19 +26,43 @@ neutronclient Python API Basic Usage ----------- -First create a client instance. +First create a client instance using a keystoneauth Session. For more +information on this keystoneauth API, see `Using Sessions`_. + +.. _Using Sessions: http://docs.openstack.org/developer/keystoneauth/using-sessions.html .. code-block:: python + >>> from keystoneauth1 import identity + >>> from keystoneauth1 import session >>> from neutronclient.v2_0 import client - >>> username='adminUser' - >>> password='secretword' - >>> project_name='openstackDemo' - >>> auth_url='http://192.168.206.130:5000/v2.0' - >>> neutron = client.Client(username=username, - ... password=password, - ... project_name=project_name, - ... auth_url=auth_url) + >>> username='username' + >>> password='password' + >>> project_name='demo' + >>> project_domain_id='default' + >>> user_domain_id='default' + >>> auth_url='http://auth.example.com:5000/v3' + >>> auth = identity.Password(auth_url=auth_url, + ... username=username, + ... password=password, + ... project_name=project_name, + ... project_domain_id=project_domain_id, + ... user_domain_id=user_domain_id) + >>> sess = session.Session(auth=auth) + >>> neutron = client.Client(session=sess) + +If you are using Identity v2.0 API (DEPRECATED), create an auth plugin using +the appropriate parameters and `keystoneauth1.identity` will handle Identity +API version discovery. Then you can create a Session and a Neutronclient just +like the previous example. + +.. code-block:: python + + >>> auth = identity.Password(auth_url=auth_url, + ... username=username, + ... password=password, + ... project_name=project_name) + >>> # create a Session and a Neutronclient Now you can call various methods on the client instance. From 95345b802a403dfb7a85638ac302b60f2e4247f8 Mon Sep 17 00:00:00 2001 From: Richard Theis Date: Wed, 10 Aug 2016 13:43:11 -0500 Subject: [PATCH 475/845] Update OSC transition status Update the OSC transition devref with the latest status. Change-Id: I6f9956975e70cc38544eb97191fa626772605ff1 --- doc/source/devref/transition_to_osc.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/source/devref/transition_to_osc.rst b/doc/source/devref/transition_to_osc.rst index 98aa4377a..92fa92600 100644 --- a/doc/source/devref/transition_to_osc.rst +++ b/doc/source/devref/transition_to_osc.rst @@ -71,7 +71,7 @@ Transition Steps backwards compatibility of its networking support and OSC updates its dependencies to include OpenStack Python SDK version 1.0 or later. -5. **In Progress:** OSC switches its networking support for the +5. **Done:** OSC switches its networking support for the `ip floating `_, `ip floating pool `_, `ip fixed `_, @@ -82,13 +82,13 @@ Transition Steps is enabled, then the nova client's Python library will continue to be used. See the following OSC bugs: - * **In Progress** `Floating IP CRUD `_ + * **Done** `Floating IP CRUD `_ - * **In Progress** `Port CRUD `_ + * **Done** `Port CRUD `_ * **Done** `Security Group CRUD `_ - * **In Progress** `Security Group Rule CRUD `_ + * **Done** `Security Group Rule CRUD `_ 6. **In Progress:** OSC continues enhancing its networking support. At this point and when applicable, enhancements to the ``neutron`` @@ -122,8 +122,8 @@ Transition Steps Developer Guide --------------- The ``neutron`` CLI version 4.x, without extensions, supports over 200 -commands while the ``openstack`` CLI version 2.2.0 supports about 35 -networking commands. Of the 35 commands, many do not have all of the options +commands while the ``openstack`` CLI version 2.6.0 supports over 50 +networking commands. Of the 50 commands, some do not have all of the options or arguments of their ``neutron`` CLI equivalent. With this large functional gap, a couple critical questions for developers during this transition are "Which CLI do I change?" and "Where does my CLI belong?" The answer depends on the From f9ceed4f4326609f2fbf8263d336dfcae82a5bb6 Mon Sep 17 00:00:00 2001 From: reedip Date: Tue, 26 Jan 2016 11:59:31 +0900 Subject: [PATCH 476/845] Add 'shared' option to neutron address-scope-update With reference to [1], neutron address-scope-update has 'shared' option missing. The following patch fixes this issue. [1]:https://github.com/openstack/neutron/blob/9b830dde9b1f445b0c96d038af66e43e6d4bf0d7/neutron/extensions/address_scope.py TrivialFix Change-Id: I520ea471f5ab2681d0890370349433440734ab69 --- neutronclient/neutron/v2_0/address_scope.py | 7 ++++++- neutronclient/tests/unit/test_cli20_address_scope.py | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/neutronclient/neutron/v2_0/address_scope.py b/neutronclient/neutron/v2_0/address_scope.py index b17087c35..73eba80f5 100644 --- a/neutronclient/neutron/v2_0/address_scope.py +++ b/neutronclient/neutron/v2_0/address_scope.py @@ -15,6 +15,7 @@ # from neutronclient._i18n import _ +from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronV20 @@ -77,8 +78,12 @@ class UpdateAddressScope(neutronV20.UpdateCommand): def add_known_arguments(self, parser): parser.add_argument('--name', help=_('Updated name of the address scope.')) + utils.add_boolean_argument( + parser, '--shared', + help=_('Set sharing of address scope. ' + '(True means shared)')) def args2body(self, parsed_args): body = {} - neutronV20.update_dict(parsed_args, body, ['name']) + neutronV20.update_dict(parsed_args, body, ['name', 'shared']) return {self.resource: body} diff --git a/neutronclient/tests/unit/test_cli20_address_scope.py b/neutronclient/tests/unit/test_cli20_address_scope.py index 481f23128..e770de3b7 100644 --- a/neutronclient/tests/unit/test_cli20_address_scope.py +++ b/neutronclient/tests/unit/test_cli20_address_scope.py @@ -114,6 +114,11 @@ def test_update_address_scope(self): ['myid', '--name', 'newname-address-scope'], {'name': 'newname-address-scope'} ) + # Update address_scope: myid --shared + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--shared', 'True'], + {'shared': "True"} + ) def test_list_address_scope(self): # address_scope-list. From 5799e5daaf23eef4572ac724a986c3c2a08b458b Mon Sep 17 00:00:00 2001 From: Reedip Date: Fri, 12 Aug 2016 11:19:01 +0530 Subject: [PATCH 477/845] Trivial correction in variable name As mentioned by Akihiro Motoki in I3b8a05698625baad3906784e3ecffb0f0242d660 the variable name "extra_ids" needed a cleanup, which is done in this patch. TrivialFix Change-Id: I9636582a3d76332916f9502c9906dc6570709c12 --- neutronclient/tests/unit/test_cli20.py | 12 ++++++------ neutronclient/tests/unit/test_cli20_network.py | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index c8e99a158..c8fec064c 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -525,7 +525,7 @@ def _test_set_path_and_delete(self, path, parent_id, myid): def _test_delete_resource(self, resource, cmd, myid, args, cmd_resource=None, parent_id=None, - extra_ids=None): + extra_id=None): self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") cmd.get_client().MultipleTimes().AndReturn(self.client) @@ -533,9 +533,9 @@ def _test_delete_resource(self, resource, cmd, myid, args, cmd_resource = resource path = getattr(self.client, cmd_resource + "_path") self._test_set_path_and_delete(path, parent_id, myid) - # extra_ids is used to test for bulk_delete - if extra_ids: - self._test_set_path_and_delete(path, parent_id, extra_ids) + # extra_id is used to test for bulk_delete + if extra_id: + self._test_set_path_and_delete(path, parent_id, extra_id) self.mox.ReplayAll() cmd_parser = cmd.get_parser("delete_" + cmd_resource) shell.run_command(cmd, cmd_parser, args) @@ -543,8 +543,8 @@ def _test_delete_resource(self, resource, cmd, myid, args, self.mox.UnsetStubs() _str = self.fake_stdout.make_string() self.assertIn(myid, _str) - if extra_ids: - self.assertIn(extra_ids, _str) + if extra_id: + self.assertIn(extra_id, _str) def _test_update_resource_action(self, resource, cmd, myid, action, args, body, retval=None, cmd_resource=None): diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index f28b8a448..5c728b05a 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -595,7 +595,7 @@ def test_bulk_delete_network(self): myid1 = 'myid1' myid2 = 'myid2' args = [myid1, myid2] - self._test_delete_resource(resource, cmd, myid1, args, extra_ids=myid2) + self._test_delete_resource(resource, cmd, myid1, args, extra_id=myid2) def _test_extend_list(self, mox_calls): data = [{'id': 'netid%d' % i, 'name': 'net%d' % i, From 0741d34bfe2da28877157e199ed4feb3f1cb47bc Mon Sep 17 00:00:00 2001 From: wengjiangang Date: Tue, 28 Jun 2016 03:49:34 -0400 Subject: [PATCH 478/845] Fix problem of RBAC command arguments Add default value '*' to argument 'target_tenant' Change-Id: I61c35ee4def53bc141a9ce562c01fe7422663a0e Closes-Bug: #1596750 --- neutronclient/neutron/v2_0/rbac.py | 1 + neutronclient/tests/unit/test_cli20_rbac.py | 2 +- .../notes/fix-rbac-create-command-dd40a474f0f092db.yaml | 7 +++++++ 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/fix-rbac-create-command-dd40a474f0f092db.yaml diff --git a/neutronclient/neutron/v2_0/rbac.py b/neutronclient/neutron/v2_0/rbac.py index 46f254af8..64645e573 100644 --- a/neutronclient/neutron/v2_0/rbac.py +++ b/neutronclient/neutron/v2_0/rbac.py @@ -69,6 +69,7 @@ def add_known_arguments(self, parser): help=_('Type of the object that RBAC policy affects.')) parser.add_argument( '--target-tenant', + default='*', help=_('ID of the tenant to which the RBAC ' 'policy will be enforced.')) parser.add_argument( diff --git a/neutronclient/tests/unit/test_cli20_rbac.py b/neutronclient/tests/unit/test_cli20_rbac.py index ca7ea46d2..ca89df6f9 100644 --- a/neutronclient/tests/unit/test_cli20_rbac.py +++ b/neutronclient/tests/unit/test_cli20_rbac.py @@ -44,7 +44,7 @@ def test_create_rbac_policy_with_mandatory_params(self): '--action', 'access_as_shared'] position_names = ['object_id', 'object_type', 'target_tenant', 'action'] - position_values = [name, self.object_type_val, None, + position_values = [name, self.object_type_val, '*', 'access_as_shared'] self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) diff --git a/releasenotes/notes/fix-rbac-create-command-dd40a474f0f092db.yaml b/releasenotes/notes/fix-rbac-create-command-dd40a474f0f092db.yaml new file mode 100644 index 000000000..7ae438abf --- /dev/null +++ b/releasenotes/notes/fix-rbac-create-command-dd40a474f0f092db.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - Fix 'bug 1596750 ' + that using 'rbac-create' without specifying 'target-tenant' will + return 'Request Failed internal server error while processing your + request'. + Update the default value of the argument '--target-tenant' to '*' From a16135029a404eff7db4833d3b511afdd7a78ec9 Mon Sep 17 00:00:00 2001 From: Henry Gessau Date: Wed, 17 Aug 2016 04:44:32 -0400 Subject: [PATCH 479/845] Do not check stderr output from shell The output from shell commands may contain warning messages which are not errors. There are cases where there is nothing neutronclient can do to avoid, for example, depecration messages. The ShellTest.shell() already checks for SystemExit result codes, so asserting that stderr is empty is not needed. Change-Id: Ibeabf474194b2d38273d76117a3a2f3bee5f7286 --- neutronclient/tests/unit/test_shell.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/neutronclient/tests/unit/test_shell.py b/neutronclient/tests/unit/test_shell.py index a3326a17d..4e62d78d0 100644 --- a/neutronclient/tests/unit/test_shell.py +++ b/neutronclient/tests/unit/test_shell.py @@ -102,7 +102,6 @@ def test_help(self): self.assertThat( help_text, matchers.MatchesRegex(required)) - self.assertFalse(stderr) def test_bash_completion(self): required = '.*os_user_domain_id.*' @@ -110,7 +109,6 @@ def test_bash_completion(self): self.assertThat( bash_completion, matchers.MatchesRegex(required)) - self.assertFalse(stderr) def test_help_on_subcommand(self): required = [ @@ -120,7 +118,6 @@ def test_help_on_subcommand(self): self.assertThat( stdout, matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE)) - self.assertFalse(stderr) def test_help_command(self): required = 'usage:' @@ -128,11 +125,9 @@ def test_help_command(self): self.assertThat( help_text, matchers.MatchesRegex(required)) - self.assertFalse(stderr) def test_bash_completion_in_outputs_of_help_command(self): help_text, stderr = self.shell('help') - self.assertFalse(stderr) completion_cmd = "bash-completion" completion_help_str = ("Prints all of the commands and options " "for bash-completion.") @@ -146,7 +141,6 @@ def test_bash_completion_command(self): '.*help', '.*--dns-nameserver'] help_text, stderr = self.shell('neutron bash-completion') - self.assertFalse(stderr) for r in required: self.assertThat(help_text, matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE)) From bca532b67492cdfca674aa48f4ae58fa1da37612 Mon Sep 17 00:00:00 2001 From: Henry Gessau Date: Tue, 9 Aug 2016 19:03:33 -0400 Subject: [PATCH 480/845] Enable DeprecationWarning in test environments Many deprecations are triggered early (on imports, for example) before the warnings are enabled by the WarningsFixture in the base test class. To make sure all DeprecationWarning messages are emitted we enable them via the PYTHONWARNINGS environment variable. Depends-On: Ibeabf474194b2d38273d76117a3a2f3bee5f7286 Change-Id: Iab8171ac0943c774be0d2117ac442f2e153b3c7f --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index 062ca6724..24f0ce3c9 100644 --- a/tox.ini +++ b/tox.ini @@ -9,6 +9,7 @@ setenv = VIRTUAL_ENV={envdir} LANG=en_US.UTF-8 LANGUAGE=en_US:en LC_ALL=C + PYTHONWARNINGS=default::DeprecationWarning usedevelop = True install_command = {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages} From 9750c10f42aa3e058432f7c7d7352961fe7fe789 Mon Sep 17 00:00:00 2001 From: reedip Date: Wed, 2 Mar 2016 18:33:08 +0900 Subject: [PATCH 481/845] Simplify clearing session-persistence Currently Session Persistance for a pool can be cleared by "neutron lbaas-pool-update pool --session-persistence type=dict type=NONE" But this option seems to be a bit long and complicated, so therefore this patch tries to simplify it by adding a new option --no-session-persistence, which does the same job. Change-Id: Iacafe765a2f0f9537a4e4d01b9b8086a9e313b92 Depends-On: I654b172927e1d96677a7da9e0846231b0ac48aa9 --- neutronclient/neutron/v2_0/lb/v2/pool.py | 23 +++++++++++++------ .../tests/unit/lb/v2/test_cli20_pool.py | 11 +++++++++ ...session-clear-option-3c0b78ebc133a10c.yaml | 6 +++++ 3 files changed, 33 insertions(+), 7 deletions(-) create mode 100644 releasenotes/notes/add-new-session-clear-option-3c0b78ebc133a10c.yaml diff --git a/neutronclient/neutron/v2_0/lb/v2/pool.py b/neutronclient/neutron/v2_0/lb/v2/pool.py index b2bd326a9..5e76af228 100644 --- a/neutronclient/neutron/v2_0/lb/v2/pool.py +++ b/neutronclient/neutron/v2_0/lb/v2/pool.py @@ -39,7 +39,7 @@ def _get_listener_id(client, listener_id_or_name): client, 'listener', listener_id_or_name) -def _add_common_args(parser): +def _add_common_args(parser, is_create=True): parser.add_argument( '--description', help=_('Description of the pool.')) @@ -47,7 +47,7 @@ def _add_common_args(parser): '--name', help=_('The name of the pool.')) parser.add_argument( '--lb-algorithm', - required=True, + required=is_create, type=utils.convert_to_uppercase, choices=['ROUND_ROBIN', 'LEAST_CONNECTIONS', 'SOURCE_IP'], help=_('The algorithm used to distribute load between the members ' @@ -57,8 +57,7 @@ def _add_common_args(parser): def _parse_common_args(parsed_args): body = {} neutronV20.update_dict(parsed_args, - body, ['description', 'lb_algorithm', 'name', - 'session_persistence']) + body, ['description', 'lb_algorithm', 'name']) return body @@ -142,7 +141,8 @@ def args2body(self, parsed_args): body['loadbalancer_id'] = loadbalancer_id body['admin_state_up'] = parsed_args.admin_state neutronV20.update_dict(parsed_args, body, - ['tenant_id', 'protocol']) + ['tenant_id', 'protocol', + 'session_persistence']) return {self.resource: body} @@ -157,17 +157,26 @@ def add_known_arguments(self, parser): parser, '--admin-state-up', help=_('Update the administrative state of ' 'the pool (True meaning "Up").')) - parser.add_argument( + session_group = parser.add_mutually_exclusive_group() + session_group.add_argument( '--session-persistence', metavar='type=TYPE[,cookie_name=COOKIE_NAME]', type=utils.str2dict_type(required_keys=['type'], optional_keys=['cookie_name']), help=_('The type of session persistence to use and associated ' 'cookie name.')) - _add_common_args(parser) + session_group.add_argument( + '--no-session-persistence', + action='store_true', + help=_('Clear session persistence for the pool.')) + _add_common_args(parser, False) def args2body(self, parsed_args): body = _parse_common_args(parsed_args) + if parsed_args.no_session_persistence: + body['session_persistence'] = None + elif parsed_args.session_persistence: + body['session_persistence'] = parsed_args.session_persistence neutronV20.update_dict(parsed_args, body, ['admin_state_up']) return {self.resource: body} diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_pool.py b/neutronclient/tests/unit/lb/v2/test_cli20_pool.py index 7a4f7e7cb..8c1f428a3 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_pool.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_pool.py @@ -179,6 +179,17 @@ def test_update_pool(self): }, } self._test_update_resource(resource, cmd, 'myid', args, body, cmd_resource=cmd_resource) + # lbaas-pool-update myid --name Name + # --no-session-persistence + + resource = 'pool' + cmd_resource = 'lbaas_pool' + cmd = pool.UpdatePool(test_cli20.MyApp(sys.stdout), None) + args = ['myid', '--name', 'Name', '--no-session-persistence'] + body = {'name': "Name", + "session_persistence": None, } + self._test_update_resource(resource, cmd, 'myid', args, body, + cmd_resource=cmd_resource) def test_delete_pool(self): # lbaas-pool-delete my-id. diff --git a/releasenotes/notes/add-new-session-clear-option-3c0b78ebc133a10c.yaml b/releasenotes/notes/add-new-session-clear-option-3c0b78ebc133a10c.yaml new file mode 100644 index 000000000..2782efe4f --- /dev/null +++ b/releasenotes/notes/add-new-session-clear-option-3c0b78ebc133a10c.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + A new option ``--no-session-persistence`` has been added to + the ``neutron lbaas-pool-update`` CLI to clear the session persistence + with which the current pool is associated. From b3f22cc18934c5db421fc902b07cc9dad4782291 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Fri, 19 Aug 2016 19:52:53 +0000 Subject: [PATCH 482/845] Updated from global requirements Change-Id: I302ea10c2705aacdbf917ae339c7f8547d73bc1e --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 3ce9a2314..327cffd20 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ osc-lib>=0.4.0 # Apache-2.0 oslo.i18n>=2.1.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 oslo.utils>=3.16.0 # Apache-2.0 -os-client-config>=1.13.1 # Apache-2.0 +os-client-config!=1.19.0,!=1.19.1,!=1.20.0,>=1.13.1 # Apache-2.0 keystoneauth1>=2.10.0 # Apache-2.0 requests>=2.10.0 # Apache-2.0 simplejson>=2.2.0 # MIT From 629393090b311ad7878ed3d9c21ed1fc938a71f1 Mon Sep 17 00:00:00 2001 From: reedip Date: Fri, 22 Jan 2016 17:55:35 +0900 Subject: [PATCH 483/845] Improve Help message of VPN Update CLI The vpn-* and ipsec-connection update CLIs do not show all possible updatable options. With respect to the information in [1], this patch introduces the updatable options in the following CLIs: neutron ipsec-site-connection-update neutron vpn-ipsecpolicy-update neutron vpn-service-update neutron vpn-ikepolicy-update [1]:https://git.openstack.org/cgit/openstack/neutron-vpnaas/tree/neutron_vpnaas/extensions/vpnaas.py Change-Id: I76b1846c62747fe7e9c6b0bd1ef40728269ed553 --- neutronclient/neutron/v2_0/vpn/ikepolicy.py | 102 ++++++------- .../neutron/v2_0/vpn/ipsec_site_connection.py | 136 ++++++++++-------- neutronclient/neutron/v2_0/vpn/ipsecpolicy.py | 100 ++++++------- neutronclient/neutron/v2_0/vpn/vpnservice.py | 41 ++++-- .../tests/unit/vpn/test_cli20_ikepolicy.py | 10 +- .../vpn/test_cli20_ipsec_site_connection.py | 13 +- .../tests/unit/vpn/test_cli20_ipsecpolicy.py | 19 ++- .../tests/unit/vpn/test_cli20_vpnservice.py | 4 + 8 files changed, 247 insertions(+), 178 deletions(-) diff --git a/neutronclient/neutron/v2_0/vpn/ikepolicy.py b/neutronclient/neutron/v2_0/vpn/ikepolicy.py index 2a5d91b3b..3a6c1b678 100644 --- a/neutronclient/neutron/v2_0/vpn/ikepolicy.py +++ b/neutronclient/neutron/v2_0/vpn/ikepolicy.py @@ -14,12 +14,59 @@ # under the License. # +import argparse + from neutronclient._i18n import _ from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronv20 from neutronclient.neutron.v2_0.vpn import utils as vpn_utils +def add_common_args(parser, is_create=True): + parser.add_argument( + '--description', + help=_('Description of the IKE policy.')) + parser.add_argument( + '--auth-algorithm', + default='sha1' if is_create else argparse.SUPPRESS, + help=_('Authentication algorithm in lowercase. ' + 'Default:sha1')) + parser.add_argument( + '--encryption-algorithm', + default='aes-128' if is_create else argparse.SUPPRESS, + help=_('Encryption algorithm in lowercase, default:aes-128')) + parser.add_argument( + '--phase1-negotiation-mode', + default='main' if is_create else argparse.SUPPRESS, + choices=['main'], + help=_('IKE Phase1 negotiation mode in lowercase, default:main')) + parser.add_argument( + '--ike-version', + default='v1' if is_create else argparse.SUPPRESS, + choices=['v1', 'v2'], + help=_('IKE version in lowercase, default:v1')) + parser.add_argument( + '--pfs', + default='group5' if is_create else argparse.SUPPRESS, + help=_('Perfect Forward Secrecy in lowercase, default:group5')) + parser.add_argument( + '--lifetime', + metavar="units=UNITS,value=VALUE", + type=utils.str2dict_type(optional_keys=['units', 'value']), + help=vpn_utils.lifetime_help("IKE")) + + +def parse_common_args2body(parsed_args, body): + neutronv20.update_dict(parsed_args, body, + ['auth_algorithm', 'encryption_algorithm', + 'phase1_negotiation_mode', 'ike_version', + 'pfs', 'name', 'description', 'tenant_id']) + if parsed_args.lifetime: + vpn_utils.validate_lifetime_dict(parsed_args.lifetime) + body['lifetime'] = parsed_args.lifetime + return body + + class ListIKEPolicy(neutronv20.ListCommand): """List IKE policies that belong to a tenant.""" @@ -44,50 +91,13 @@ class CreateIKEPolicy(neutronv20.CreateCommand): resource = 'ikepolicy' def add_known_arguments(self, parser): - parser.add_argument( - '--description', - help=_('Description of the IKE policy')) - parser.add_argument( - '--auth-algorithm', - default='sha1', choices=['sha1', 'sha256'], - help=_('Authentication algorithm in lowercase. ' - 'Default:sha1')) - parser.add_argument( - '--encryption-algorithm', - default='aes-128', - help=_('Encryption algorithm in lowercase, default:aes-128')) - parser.add_argument( - '--phase1-negotiation-mode', - default='main', choices=['main'], - help=_('IKE Phase1 negotiation mode in lowercase, default:main')) - parser.add_argument( - '--ike-version', - default='v1', choices=['v1', 'v2'], - help=_('IKE version in lowercase, default:v1')) - parser.add_argument( - '--pfs', - default='group5', choices=['group2', 'group5', 'group14'], - help=_('Perfect Forward Secrecy in lowercase, default:group5')) - parser.add_argument( - '--lifetime', - metavar="units=UNITS,value=VALUE", - type=utils.str2dict_type(optional_keys=['units', 'value']), - help=vpn_utils.lifetime_help("IKE")) parser.add_argument( 'name', metavar='NAME', help=_('Name of the IKE policy.')) + add_common_args(parser) def args2body(self, parsed_args): - - body = {} - neutronv20.update_dict(parsed_args, body, - ['auth_algorithm', 'encryption_algorithm', - 'phase1_negotiation_mode', 'ike_version', - 'pfs', 'name', 'description', 'tenant_id']) - if parsed_args.lifetime: - vpn_utils.validate_lifetime_dict(parsed_args.lifetime) - body['lifetime'] = parsed_args.lifetime - return {'ikepolicy': body} + return {'ikepolicy': parse_common_args2body(parsed_args, body={})} class UpdateIKEPolicy(neutronv20.UpdateCommand): @@ -98,18 +108,12 @@ class UpdateIKEPolicy(neutronv20.UpdateCommand): def add_known_arguments(self, parser): parser.add_argument( - '--lifetime', - metavar="units=UNITS,value=VALUE", - type=utils.str2dict_type(optional_keys=['units', 'value']), - help=vpn_utils.lifetime_help("IKE")) + '--name', + help=_('Updated Name of the IKE policy.')) + add_common_args(parser, is_create=False) def args2body(self, parsed_args): - - body = {} - if parsed_args.lifetime: - vpn_utils.validate_lifetime_dict(parsed_args.lifetime) - body['lifetime'] = parsed_args.lifetime - return {'ikepolicy': body} + return {'ikepolicy': parse_common_args2body(parsed_args, body={})} class DeleteIKEPolicy(neutronv20.DeleteCommand): diff --git a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py index 503d90163..505f4c5d9 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py +++ b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py @@ -14,6 +14,8 @@ # under the License. # +import argparse + from oslo_serialization import jsonutils from neutronclient._i18n import _ @@ -51,7 +53,13 @@ class ShowIPsecSiteConnection(neutronv20.ShowCommand): class IPsecSiteConnectionMixin(object): - def add_known_arguments(self, parser): + def add_known_arguments(self, parser, is_create=True): + parser.add_argument( + '--name', + help=_('Set friendly name for the connection.')) + parser.add_argument( + '--description', + help=_('Set a description for the connection.')) parser.add_argument( '--dpd', metavar="action=ACTION,interval=INTERVAL,timeout=TIMEOUT", @@ -61,11 +69,39 @@ def add_known_arguments(self, parser): parser.add_argument( '--local-ep-group', help=_('Local endpoint group ID/name with subnet(s) for ' - 'the IPsec connection.')) + 'IPSec connection.')) parser.add_argument( '--peer-ep-group', help=_('Peer endpoint group ID/name with CIDR(s) for ' - 'the IPsec connection.')) + 'IPSec connection.')) + parser.add_argument( + '--peer-cidr', + action='append', dest='peer_cidrs', + help=_('[DEPRECATED in Mitaka] Remote subnet(s) in CIDR format. ' + 'Cannot be specified when using endpoint groups. Only ' + 'applicable, if subnet provided for VPN service.')) + parser.add_argument( + '--peer-id', + required=is_create, + help=_('Peer router identity for authentication. Can be ' + 'IPv4/IPv6 address, e-mail address, key id, or FQDN.')) + parser.add_argument( + '--peer-address', + required=is_create, + help=_('Peer gateway public IPv4/IPv6 address or FQDN.')) + parser.add_argument( + '--psk', + required=is_create, + help=_('Pre-shared key string.')) + parser.add_argument( + '--mtu', + default='1500' if is_create else argparse.SUPPRESS, + help=_('MTU size for the connection, default:1500')) + parser.add_argument( + '--initiator', + default='bi-directional' if is_create else argparse.SUPPRESS, + choices=['bi-directional', 'response-only'], + help=_('Initiator state in lowercase, default:bi-directional')) def args2body(self, parsed_args, body=None): """Add in conditional args and then return all conn info.""" @@ -85,6 +121,19 @@ def args2body(self, parsed_args, body=None): self.get_client(), 'endpoint_group', parsed_args.peer_ep_group) body['peer_ep_group_id'] = _peer_epg + if hasattr(parsed_args, 'mtu') and int(parsed_args.mtu) < 68: + message = _("Invalid MTU value: MTU must be " + "greater than or equal to 68.") + raise exceptions.CommandError(message) + # ToDo (Reedip) : Remove below check when peer-cidr is removed + if parsed_args.peer_cidrs and parsed_args.local_ep_group: + message = _("You cannot specify both endpoint groups and peer " + "CIDR(s).") + raise exceptions.CommandError(message) + neutronv20.update_dict(parsed_args, body, + ['peer_id', 'mtu', 'initiator', 'psk', + 'peer_address', 'name', 'description', + 'peer_cidrs']) return {self.resource: body} @@ -98,21 +147,6 @@ def add_known_arguments(self, parser): '--admin-state-down', default=True, action='store_false', help=_('Set admin state up to false.')) - parser.add_argument( - '--name', - help=_('Set a name for the connection.')) - parser.add_argument( - '--description', - help=_('Set a description for the connection.')) - parser.add_argument( - '--mtu', - default='1500', - help=_('MTU size for the connection, default:1500')) - parser.add_argument( - '--initiator', - default='bi-directional', choices=['bi-directional', - 'response-only'], - help=_('Initiator state in lowercase, default:bi-directional')) parser.add_argument( '--vpnservice-id', metavar='VPNSERVICE', required=True, @@ -125,25 +159,6 @@ def add_known_arguments(self, parser): '--ipsecpolicy-id', metavar='IPSECPOLICY', required=True, help=_('IPsec policy ID associated with this connection.')) - parser.add_argument( - '--peer-address', - required=True, - help=_('Peer gateway public IPv4/IPv6 address or FQDN.')) - parser.add_argument( - '--peer-id', - required=True, - help=_('Peer router identity for authentication. Can be ' - 'IPv4/IPv6 address, e-mail address, key id, or FQDN.')) - parser.add_argument( - '--peer-cidr', - action='append', dest='peer_cidrs', - help=_('[DEPRECATED in Mitaka] Remote subnet(s) in CIDR format. ' - 'Cannot be specified when using endpoint groups. Only ' - 'applicable, if subnet provided for VPN service.')) - parser.add_argument( - '--psk', - required=True, - help=_('Pre-shared key string.')) super(CreateIPsecSiteConnection, self).add_known_arguments(parser) def args2body(self, parsed_args): @@ -156,39 +171,23 @@ def args2body(self, parsed_args): _ipsecpolicy_id = neutronv20.find_resourceid_by_name_or_id( self.get_client(), 'ipsecpolicy', parsed_args.ipsecpolicy_id) - if int(parsed_args.mtu) < 68: - message = _("Invalid MTU value: MTU must be " - "greater than or equal to 68") - raise exceptions.CommandError(message) - if (bool(parsed_args.local_ep_group) != - bool(parsed_args.peer_ep_group)): - message = _("You must specify both local and peer endpoint " - "groups.") - raise exceptions.CommandError(message) - if parsed_args.peer_cidrs and parsed_args.local_ep_group: - message = _("You cannot specify both endpoint groups and peer " - "CIDR(s).") - raise exceptions.CommandError(message) - if not parsed_args.peer_cidrs and not parsed_args.local_ep_group: - message = _("You must specify endpoint groups or peer CIDR(s).") - raise exceptions.CommandError(message) body = { 'vpnservice_id': _vpnservice_id, 'ikepolicy_id': _ikepolicy_id, 'ipsecpolicy_id': _ipsecpolicy_id, 'admin_state_up': parsed_args.admin_state_down, } - neutronv20.update_dict(parsed_args, body, - ['peer_id', 'mtu', 'initiator', 'psk', - 'peer_address']) - if parsed_args.name: - body['name'] = parsed_args.name - if parsed_args.description: - body['description'] = parsed_args.description if parsed_args.tenant_id: body['tenant_id'] = parsed_args.tenant_id - if parsed_args.peer_cidrs: - body['peer_cidrs'] = parsed_args.peer_cidrs + + if (bool(parsed_args.local_ep_group) != + bool(parsed_args.peer_ep_group)): + message = _("You must specify both local and peer endpoint " + "groups.") + raise exceptions.CommandError(message) + if not parsed_args.peer_cidrs and not parsed_args.local_ep_group: + message = _("You must specify endpoint groups or peer CIDR(s).") + raise exceptions.CommandError(message) return super(CreateIPsecSiteConnection, self).args2body(parsed_args, body) @@ -200,6 +199,19 @@ class UpdateIPsecSiteConnection(IPsecSiteConnectionMixin, resource = 'ipsec_site_connection' help_resource = 'IPsec site connection' + def add_known_arguments(self, parser): + utils.add_boolean_argument( + parser, '--admin-state-up', + help=_('Update the administrative state. (True meaning "Up")')) + super(UpdateIPsecSiteConnection, self).add_known_arguments(parser, + False) + + def args2body(self, parsed_args): + body = {} + neutronv20.update_dict(parsed_args, body, ['admin_state_up']) + return super(UpdateIPsecSiteConnection, self).args2body(parsed_args, + body) + class DeleteIPsecSiteConnection(neutronv20.DeleteCommand): """Delete a given IPsec site connection.""" diff --git a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py index 5da45158e..e9ad2f5cc 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py +++ b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py @@ -14,12 +14,58 @@ # under the License. # +import argparse + from neutronclient._i18n import _ from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronv20 from neutronclient.neutron.v2_0.vpn import utils as vpn_utils +def add_common_args(parser, is_create=True): + parser.add_argument( + '--auth-algorithm', + default='sha1' if is_create else argparse.SUPPRESS, + help=_('Authentication algorithm in lowercase, default:sha1')) + parser.add_argument( + '--description', + help=_('Description of the IPsec policy.')) + parser.add_argument( + '--encryption-algorithm', + default='aes-128' if is_create else argparse.SUPPRESS, + help=_('Encryption algorithm in lowercase, default:aes-128')) + parser.add_argument( + '--encapsulation-mode', + default='tunnel' if is_create else argparse.SUPPRESS, + choices=['tunnel', 'transport'], + help=_('Encapsulation mode in lowercase, default:tunnel')) + parser.add_argument( + '--lifetime', + metavar="units=UNITS,value=VALUE", + type=utils.str2dict_type(optional_keys=['units', 'value']), + help=vpn_utils.lifetime_help("IPsec")) + parser.add_argument( + '--pfs', + default='group5' if is_create else argparse.SUPPRESS, + help=_('Perfect Forward Secrecy in lowercase, default:group5')) + parser.add_argument( + '--transform-protocol', + default='esp' if is_create else argparse.SUPPRESS, + choices=['esp', 'ah', 'ah-esp'], + help=_('Transform protocol in lowercase, default:esp')) + + +def parse_common_args2body(parsed_args, body): + neutronv20.update_dict(parsed_args, body, + ['auth_algorithm', 'encryption_algorithm', + 'encapsulation_mode', 'transform_protocol', + 'pfs', 'name', 'description', 'tenant_id']) + if parsed_args.lifetime: + vpn_utils.validate_lifetime_dict(parsed_args.lifetime) + body['lifetime'] = parsed_args.lifetime + return body + + class ListIPsecPolicy(neutronv20.ListCommand): """List IPsec policies that belong to a given tenant connection.""" @@ -44,49 +90,13 @@ class CreateIPsecPolicy(neutronv20.CreateCommand): resource = 'ipsecpolicy' def add_known_arguments(self, parser): - parser.add_argument( - '--description', - help=_('Description of the IPsec policy.')) - parser.add_argument( - '--transform-protocol', - default='esp', choices=['esp', 'ah', 'ah-esp'], - help=_('Transform protocol in lowercase, default:esp')) - parser.add_argument( - '--auth-algorithm', - default='sha1', choices=['sha1', 'sha256'], - help=_('Authentication algorithm in lowercase, default:sha1')) - parser.add_argument( - '--encryption-algorithm', - default='aes-128', - help=_('Encryption algorithm in lowercase, default:aes-128')) - parser.add_argument( - '--encapsulation-mode', - default='tunnel', choices=['tunnel', 'transport'], - help=_('Encapsulation mode in lowercase, default:tunnel')) - parser.add_argument( - '--pfs', - default='group5', choices=['group2', 'group5', 'group14'], - help=_('Perfect Forward Secrecy in lowercase, default:group5')) - parser.add_argument( - '--lifetime', - metavar="units=UNITS,value=VALUE", - type=utils.str2dict_type(optional_keys=['units', 'value']), - help=vpn_utils.lifetime_help("IPsec")) parser.add_argument( 'name', metavar='NAME', help=_('Name of the IPsec policy.')) + add_common_args(parser) def args2body(self, parsed_args): - - body = {} - neutronv20.update_dict(parsed_args, body, - ['auth_algorithm', 'encryption_algorithm', - 'encapsulation_mode', 'transform_protocol', - 'pfs', 'name', 'description', 'tenant_id']) - if parsed_args.lifetime: - vpn_utils.validate_lifetime_dict(parsed_args.lifetime) - body['lifetime'] = parsed_args.lifetime - return {'ipsecpolicy': body} + return {'ipsecpolicy': parse_common_args2body(parsed_args, body={})} class UpdateIPsecPolicy(neutronv20.UpdateCommand): @@ -97,18 +107,12 @@ class UpdateIPsecPolicy(neutronv20.UpdateCommand): def add_known_arguments(self, parser): parser.add_argument( - '--lifetime', - metavar="units=UNITS,value=VALUE", - type=utils.str2dict_type(optional_keys=['units', 'value']), - help=vpn_utils.lifetime_help("IPsec")) + '--name', + help=_('Updated name of the IPsec policy.')) + add_common_args(parser, is_create=False) def args2body(self, parsed_args): - - body = {} - if parsed_args.lifetime: - vpn_utils.validate_lifetime_dict(parsed_args.lifetime) - body['lifetime'] = parsed_args.lifetime - return {'ipsecpolicy': body} + return {'ipsecpolicy': parse_common_args2body(parsed_args, body={})} class DeleteIPsecPolicy(neutronv20.DeleteCommand): diff --git a/neutronclient/neutron/v2_0/vpn/vpnservice.py b/neutronclient/neutron/v2_0/vpn/vpnservice.py index 9e9ae1221..dae531408 100644 --- a/neutronclient/neutron/v2_0/vpn/vpnservice.py +++ b/neutronclient/neutron/v2_0/vpn/vpnservice.py @@ -15,9 +15,24 @@ # from neutronclient._i18n import _ +from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronv20 +def add_common_args(parser): + parser.add_argument( + '--name', + help=_('Name for the VPN service.')) + parser.add_argument( + '--description', + help=_('Description for the VPN service.')) + + +def common_args2body(parsed_args, body): + neutronv20.update_dict(parsed_args, body, + ['name', 'description']) + + class ListVPNService(neutronv20.ListCommand): """List VPN service configurations that belong to a given tenant.""" @@ -46,12 +61,6 @@ def add_known_arguments(self, parser): '--admin-state-down', dest='admin_state', action='store_false', help=_('Set admin state up to false.')) - parser.add_argument( - '--name', - help=_('Set a name for the VPN service.')) - parser.add_argument( - '--description', - help=_('Set a description for the VPN service.')) parser.add_argument( 'router', metavar='ROUTER', help=_('Router unique identifier for the VPN service.')) @@ -59,6 +68,7 @@ def add_known_arguments(self, parser): 'subnet', nargs='?', metavar='SUBNET', help=_('[DEPRECATED in Mitaka] Unique identifier for the local ' 'private subnet.')) + add_common_args(parser) def args2body(self, parsed_args): if parsed_args.subnet: @@ -74,9 +84,8 @@ def args2body(self, parsed_args): 'router_id': _router_id, 'admin_state_up': parsed_args.admin_state} neutronv20.update_dict(parsed_args, body, - ['name', 'description', - 'tenant_id']) - + ['tenant_id']) + common_args2body(parsed_args, body) return {self.resource: body} @@ -86,6 +95,20 @@ class UpdateVPNService(neutronv20.UpdateCommand): resource = 'vpnservice' help_resource = 'VPN service' + def add_known_arguments(self, parser): + add_common_args(parser) + utils.add_boolean_argument( + parser, '--admin-state-up', + help=_('Update the admin state for the VPN Service.' + '(True means UP)')) + + def args2body(self, parsed_args): + body = {} + common_args2body(parsed_args, body) + neutronv20.update_dict(parsed_args, body, + ['admin_state_up']) + return {self.resource: body} + class DeleteVPNService(neutronv20.DeleteCommand): """Delete a given VPN service.""" diff --git a/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py b/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py index 1dbf5885c..bfdd429e0 100644 --- a/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py +++ b/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py @@ -86,10 +86,6 @@ def test_create_ikepolicy_all_params(self): def test_create_ikepolicy_auth_sha256(self): self._test_create_ikepolicy_all_params(auth='sha256') - def test_create_ikepolicy_invalid_auth(self): - self._test_create_ikepolicy_all_params(auth='xyz', - expected_exc=SystemExit) - def test_create_ikepolicy_with_limited_params(self): # vpn-ikepolicy-create with limited params. resource = 'ikepolicy' @@ -219,6 +215,12 @@ def test_update_ikepolicy(self): self._test_update_resource(resource, cmd, 'myid', ['myid', '--name', 'newname'], {'name': 'newname', }) + # vpn-ikepolicy-update myid --pfs group2 --ike-version v2. + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--pfs', 'group2', + '--ike-version', 'v2'], + {'pfs': 'group2', + 'ike_version': 'v2'}) def test_delete_ikepolicy(self): # vpn-ikepolicy-delete my-id. diff --git a/neutronclient/tests/unit/vpn/test_cli20_ipsec_site_connection.py b/neutronclient/tests/unit/vpn/test_cli20_ipsec_site_connection.py index 8ead88f2a..cbab15cb8 100644 --- a/neutronclient/tests/unit/vpn/test_cli20_ipsec_site_connection.py +++ b/neutronclient/tests/unit/vpn/test_cli20_ipsec_site_connection.py @@ -300,7 +300,7 @@ def test_delete_ipsec_site_connection(self): self._test_delete_resource(resource, cmd, my_id, args) def test_update_ipsec_site_connection(self): - # ipsecsite-connection-update myid --name myname --tags a b.""" + # ipsecsite-connection-update myid --name Branch-new --tags a b. resource = 'ipsec_site_connection' cmd = ipsec_site_connection.UpdateIPsecSiteConnection( test_cli20.MyApp(sys.stdout), None @@ -310,6 +310,17 @@ def test_update_ipsec_site_connection(self): '--tags', 'a', 'b'], {'name': 'Branch-new', 'tags': ['a', 'b'], }) + # ipsecsite-connection-update myid --mtu 69 --initiator response-only + # --peer-id '192.168.2.11' --peer-ep-group 'update-grp' + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--mtu', '69', + '--initiator', 'response-only', + '--peer-id', '192.168.2.11', + '--peer-ep-group', 'update-grp'], + {'mtu': '69', + 'initiator': 'response-only', + 'peer_id': '192.168.2.11', + 'peer_ep_group_id': 'update-grp', },) def test_show_ipsec_site_connection_id(self): # ipsecsite-connection-show test_id.""" diff --git a/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py b/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py index c8f70f6b4..a24f7e076 100644 --- a/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py +++ b/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py @@ -85,10 +85,6 @@ def test_create_ipsecpolicy_all_params(self): def test_create_ipsecpolicy_auth_sha256(self): self._test_create_ipsecpolicy_all_params(auth='sha256') - def test_create_ipsecpolicy_invalid_auth(self): - self._test_create_ipsecpolicy_all_params(auth='xyz', - expected_exc=SystemExit) - def test_create_ipsecpolicy_with_limited_params(self): # vpn-ipsecpolicy-create with limited params. resource = 'ipsecpolicy' @@ -213,7 +209,7 @@ def test_show_ipsecpolicy_id_name(self): self._test_show_resource(resource, cmd, self.test_id, args, ['id', 'name']) - def test_update_ipsecpolicy(self): + def test_update_ipsecpolicy_name(self): # vpn-ipsecpolicy-update myid --name newname --tags a b. resource = 'ipsecpolicy' cmd = ipsecpolicy.UpdateIPsecPolicy(test_cli20.MyApp(sys.stdout), None) @@ -221,6 +217,19 @@ def test_update_ipsecpolicy(self): ['myid', '--name', 'newname'], {'name': 'newname', }) + def test_update_ipsecpolicy_other_params(self): + # vpn-ipsecpolicy-update myid --transform-protocol esp + # --pfs group14 --encapsulation-mode transport + resource = 'ipsecpolicy' + cmd = ipsecpolicy.UpdateIPsecPolicy(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--transform-protocol', 'esp', + '--pfs', 'group14', + '--encapsulation-mode', 'transport'], + {'transform_protocol': 'esp', + 'pfs': 'group14', + 'encapsulation_mode': 'transport', }) + def test_delete_ipsecpolicy(self): # vpn-ipsecpolicy-delete my-id. resource = 'ipsecpolicy' diff --git a/neutronclient/tests/unit/vpn/test_cli20_vpnservice.py b/neutronclient/tests/unit/vpn/test_cli20_vpnservice.py index 9b28c7364..3c4def84d 100644 --- a/neutronclient/tests/unit/vpn/test_cli20_vpnservice.py +++ b/neutronclient/tests/unit/vpn/test_cli20_vpnservice.py @@ -143,6 +143,10 @@ def test_update_vpnservice(self): self._test_update_resource(resource, cmd, 'myid', ['myid', '--name', 'newname'], {'name': 'newname', }) + # vpn-service-update myid --admin-state-up False + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--admin-state-up', 'False'], + {'admin_state_up': 'False', }) def test_delete_vpnservice(self): # vpn-service-delete my-id. From 41701862fd831c333a620c11a46877fb5241b9c8 Mon Sep 17 00:00:00 2001 From: AvnishPal Date: Mon, 22 Aug 2016 15:14:54 +0530 Subject: [PATCH 484/845] Use upper constraints for all jobs in tox.ini Openstack infra now supports upper constraints for all jobs. Updated tox.ini to use upper constraints for all jobs. Change-Id: I94f57e294a7de91d45c26b1f90caad67b0974718 Closes-Bug: #1614361 --- tox.ini | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tox.ini b/tox.ini index 062ca6724..18cead5a4 100644 --- a/tox.ini +++ b/tox.ini @@ -28,8 +28,6 @@ commands = flake8 distribute = false [testenv:venv] -# TODO(ihrachys): remove once infra supports constraints for this target -install_command = pip install -U {opts} {packages} commands = {posargs} [testenv:functional] @@ -43,8 +41,6 @@ setenv = OS_NEUTRONCLIENT_EXEC_DIR = {envdir}/bin [testenv:cover] -# TODO(ihrachys): remove once infra supports constraints for this target -install_command = pip install -U {opts} {packages} commands = python setup.py test --coverage --coverage-package-name=neutronclient --testr-args='{posargs}' coverage report @@ -53,8 +49,6 @@ commands = commands = sphinx-build -W -b html doc/source doc/build/html [testenv:releasenotes] -# TODO(ihrachys): remove once infra supports constraints for this target -install_command = pip install -U {opts} {packages} commands = sphinx-build -a -E -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html [flake8] From 976cfe9434bbf28ac66e97919324e0e4f054af59 Mon Sep 17 00:00:00 2001 From: reedip Date: Mon, 18 Apr 2016 13:39:53 +0900 Subject: [PATCH 485/845] Remove admin-state-down from lbaas-member-update lbaas-member-update used --admin-state-down, which was deprecated in Mitaka. It has been removed in this version using this patch. Change-Id: I5c4492e6d121b0ae73e21aa3ba0594446fa46d3f --- neutronclient/neutron/v2_0/lb/v2/member.py | 13 ++----------- .../remove-deprecated-option-b53f5d7e6a16ce95.yaml | 4 ++++ 2 files changed, 6 insertions(+), 11 deletions(-) create mode 100644 releasenotes/notes/remove-deprecated-option-b53f5d7e6a16ce95.yaml diff --git a/neutronclient/neutron/v2_0/lb/v2/member.py b/neutronclient/neutron/v2_0/lb/v2/member.py index aeae1cb28..3fec7760d 100644 --- a/neutronclient/neutron/v2_0/lb/v2/member.py +++ b/neutronclient/neutron/v2_0/lb/v2/member.py @@ -15,8 +15,6 @@ # License for the specific language governing permissions and limitations # under the License. # -import argparse - from neutronclient._i18n import _ from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronV20 @@ -128,27 +126,20 @@ class UpdateMember(neutronV20.UpdateCommand): shadow_resource = 'lbaas_member' def add_known_arguments(self, parser): - parser.add_argument( - '--admin-state-down', - dest='admin_state', action='store_false', - default=argparse.SUPPRESS, - help=_('[DEPRECATED in Mitaka] Set admin state up to false.')) parser.add_argument( 'pool', metavar='POOL', help=_('ID or name of the pool that this member belongs to.')) utils.add_boolean_argument( parser, '--admin-state-up', - dest='admin_state', help=_('Update the administrative state of ' 'the member (True meaning "Up").')) - # ToDo(reedip): After Mitaka, remove admin-state-down _add_common_args(parser) def args2body(self, parsed_args): self.parent_id = _get_pool_id(self.get_client(), parsed_args.pool) body = {} - if hasattr(parsed_args, 'admin_state'): - body['admin_state_up'] = parsed_args.admin_state + if hasattr(parsed_args, "admin_state_up"): + body['admin_state_up'] = parsed_args.admin_state_up _parse_common_args(body, parsed_args) return {self.resource: body} diff --git a/releasenotes/notes/remove-deprecated-option-b53f5d7e6a16ce95.yaml b/releasenotes/notes/remove-deprecated-option-b53f5d7e6a16ce95.yaml new file mode 100644 index 000000000..fb304fbda --- /dev/null +++ b/releasenotes/notes/remove-deprecated-option-b53f5d7e6a16ce95.yaml @@ -0,0 +1,4 @@ +--- +deprecations: + - | + "admin-state-down" option was deprecated in Mitaka and has been removed in Newton. From b687aadfda82f19f8c7fc1fdfe3b9dd0cc7dcb06 Mon Sep 17 00:00:00 2001 From: Reedip Banerjee Date: Wed, 9 Dec 2015 10:08:05 +0530 Subject: [PATCH 486/845] Add possible updatable options to net-update CLI net-update CLI now displays all the possible updatable options. net-update CLI shows the following information: --name --description Change-Id: I69aa284ee4a217f6627bb7120a0fc63b856bd6cc Closes-Bug: #1320796 --- neutronclient/neutron/v2_0/network.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/neutronclient/neutron/v2_0/network.py b/neutronclient/neutron/v2_0/network.py index 7a89f5a23..3ec3ac6d5 100644 --- a/neutronclient/neutron/v2_0/network.py +++ b/neutronclient/neutron/v2_0/network.py @@ -33,6 +33,11 @@ def _format_subnets(network): return '' +def args2body_common(body, parsed_args): + neutronV20.update_dict(parsed_args, body, + ['name', 'description']) + + class ListNetwork(neutronV20.ListCommand): """List networks that belong to a given tenant.""" @@ -193,8 +198,8 @@ def add_known_arguments(self, parser): dns.add_dns_argument_create(parser, self.resource, 'domain') def args2body(self, parsed_args): - body = {'name': parsed_args.name, - 'admin_state_up': parsed_args.admin_state} + body = {'admin_state_up': parsed_args.admin_state} + args2body_common(body, parsed_args) neutronV20.update_dict(parsed_args, body, ['shared', 'tenant_id', 'vlan_transparent', @@ -202,11 +207,9 @@ def args2body(self, parsed_args): 'provider:physical_network', 'provider:segmentation_id', 'description']) - self.args2body_qos_policy(parsed_args, body) availability_zone.args2body_az_hint(parsed_args, body) dns.args2body_dns_create(parsed_args, body, 'domain') - return {'network': body} @@ -222,11 +225,18 @@ class UpdateNetwork(neutronV20.UpdateCommand, qos_policy.UpdateQosPolicyMixin): resource = 'network' def add_known_arguments(self, parser): + parser.add_argument( + '--name', + help=_('Name of the network.')) + parser.add_argument( + '--description', + help=_('Description of this network.')) self.add_arguments_qos_policy(parser) dns.add_dns_argument_update(parser, self.resource, 'domain') def args2body(self, parsed_args): body = {} + args2body_common(body, parsed_args) self.args2body_qos_policy(parsed_args, body) dns.args2body_dns_update(parsed_args, body, 'domain') return {'network': body} From 79034dda29fd821b37526400f760bcbac3bfeb97 Mon Sep 17 00:00:00 2001 From: xiewj Date: Mon, 4 Jul 2016 00:49:37 -0400 Subject: [PATCH 487/845] Fix prompt message for qos-bandwidth-limit-rule-create The option names in the error message should match the actual option names (--max-kbps and --max-burst-kbps). Change-Id: I97f57fb56030d032274270d73bdb66fddf0f58f9 Closes-Bug: #1597635 --- neutronclient/neutron/v2_0/qos/bandwidth_limit_rule.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/neutronclient/neutron/v2_0/qos/bandwidth_limit_rule.py b/neutronclient/neutron/v2_0/qos/bandwidth_limit_rule.py index 1cb36cd8e..27e7b626a 100644 --- a/neutronclient/neutron/v2_0/qos/bandwidth_limit_rule.py +++ b/neutronclient/neutron/v2_0/qos/bandwidth_limit_rule.py @@ -27,18 +27,18 @@ def add_bandwidth_limit_arguments(parser): parser.add_argument( '--max-kbps', - help=_('max bandwidth in kbps.')) + help=_('Maximum bandwidth in kbps.')) parser.add_argument( '--max-burst-kbps', - help=_('max burst bandwidth in kbps.')) + help=_('Maximum burst bandwidth in kbps.')) def update_bandwidth_limit_args2body(parsed_args, body): max_kbps = parsed_args.max_kbps max_burst_kbps = parsed_args.max_burst_kbps if not (max_kbps or max_burst_kbps): - raise exceptions.CommandError(_("Must provide max_kbps" - " or max_burst_kbps option.")) + raise exceptions.CommandError(_("Must provide max-kbps" + " or max-burst-kbps option.")) neutronv20.update_dict(parsed_args, body, ['max_kbps', 'max_burst_kbps', 'tenant_id']) From f1f810bc3543fb7c0ff94aa0b52dfd1d5b6076c8 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Wed, 24 Aug 2016 13:35:49 +0000 Subject: [PATCH 488/845] Updated from global requirements Change-Id: I387652cf712063b99d467d482a6ef368e55b3fb8 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 327cffd20..e866ae75b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ osc-lib>=0.4.0 # Apache-2.0 oslo.i18n>=2.1.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 oslo.utils>=3.16.0 # Apache-2.0 -os-client-config!=1.19.0,!=1.19.1,!=1.20.0,>=1.13.1 # Apache-2.0 +os-client-config!=1.19.0,!=1.19.1,!=1.20.0,!=1.20.1,>=1.13.1 # Apache-2.0 keystoneauth1>=2.10.0 # Apache-2.0 requests>=2.10.0 # Apache-2.0 simplejson>=2.2.0 # MIT From 494b283eed095346a62ee7024d6099d134aa8144 Mon Sep 17 00:00:00 2001 From: Shih-Hao Li Date: Wed, 24 Aug 2016 14:56:49 -0700 Subject: [PATCH 489/845] Unable to add classless-static-route in extra_dhcp_opt extension When adding classless-static-route in extra_dhcp_opt for a port, neutron client complains syntax error. The reason is neutron client interprets the "," inside the opt_value as a delimiter of key-value pairs for --extra-dhcp-opt. This patch modifies neutron client to allow commas inside opt_value. This is one of two proposed solutions described in #1605421. Change-Id: I8ade427885d11f86d594cecaed96fcb82e38798f Closes-Bug: #1605421 --- neutronclient/common/utils.py | 11 +++++++++++ neutronclient/tests/unit/test_utils.py | 7 +++++++ 2 files changed, 18 insertions(+) diff --git a/neutronclient/common/utils.py b/neutronclient/common/utils.py index 882eb2c5c..d4b53d075 100644 --- a/neutronclient/common/utils.py +++ b/neutronclient/common/utils.py @@ -130,7 +130,18 @@ def str2dict(strdict, required_keys=None, optional_keys=None): """ result = {} if strdict: + i = 0 + kvlist = [] for kv in strdict.split(','): + if '=' in kv: + kvlist.append(kv) + i += 1 + elif i == 0: + msg = _("missing value for key '%s'") + raise argparse.ArgumentTypeError(msg % kv) + else: + kvlist[i-1] = "%s,%s" % (kvlist[i-1], kv) + for kv in kvlist: key, sep, value = kv.partition('=') if not sep: msg = _("invalid key-value '%s', expected format: key=value") diff --git a/neutronclient/tests/unit/test_utils.py b/neutronclient/tests/unit/test_utils.py index 44152418d..249670604 100644 --- a/neutronclient/tests/unit/test_utils.py +++ b/neutronclient/tests/unit/test_utils.py @@ -49,6 +49,13 @@ def test_invalid_string_to_dictionary(self): self.assertRaises(argparse.ArgumentTypeError, utils.str2dict, input_str) + def test_string_with_comma_value_to_dictionary(self): + input_str = ('opt_name=classless-static-route,' + 'opt_value=169.254.169.254/32,10.0.0.1') + expected = {'opt_name': 'classless-static-route', + 'opt_value': '169.254.169.254/32,10.0.0.1'} + self.assertEqual(expected, utils.str2dict(input_str)) + def test_str2dict_optional_keys(self): self.assertDictEqual({'key1': 'value1'}, utils.str2dict('key1=value1', From c33b041c0c20b8078dad0f4cdca7c9b2177a122f Mon Sep 17 00:00:00 2001 From: reedip Date: Tue, 1 Mar 2016 14:29:24 +0900 Subject: [PATCH 490/845] Remove case dependancy for user inputs With the introduction of Lowercase/Uppercase conversion functions in the utils.py, the following patch removes the dependancy of the current code on character casing. utils.convert_to_lowercase is called where the code expects the input to be in lower case while utils.convert_to_uppercase will be called where the code expects the input to be in CAPITAL casing only. Change-Id: I1c5c3c87f343fc2731469b9a0c38d278f6018a11 --- neutronclient/neutron/v2_0/metering.py | 2 ++ neutronclient/neutron/v2_0/port.py | 2 ++ neutronclient/neutron/v2_0/rbac.py | 3 +++ neutronclient/neutron/v2_0/securitygroup.py | 3 +++ neutronclient/neutron/v2_0/subnet.py | 2 ++ neutronclient/neutron/v2_0/vpn/ikepolicy.py | 18 +++++++++------ .../neutron/v2_0/vpn/ipsec_site_connection.py | 2 +- neutronclient/neutron/v2_0/vpn/ipsecpolicy.py | 22 ++++++++++++------- ...move-case-dependency-773ccb3237c38e81.yaml | 7 ++++++ 9 files changed, 45 insertions(+), 16 deletions(-) create mode 100644 releasenotes/notes/remove-case-dependency-773ccb3237c38e81.yaml diff --git a/neutronclient/neutron/v2_0/metering.py b/neutronclient/neutron/v2_0/metering.py index df81724fc..67bbec908 100644 --- a/neutronclient/neutron/v2_0/metering.py +++ b/neutronclient/neutron/v2_0/metering.py @@ -13,6 +13,7 @@ # under the License. from neutronclient._i18n import _ +from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronv20 @@ -93,6 +94,7 @@ def add_known_arguments(self, parser): parser.add_argument( '--direction', default='ingress', choices=['ingress', 'egress'], + type=utils.convert_to_lowercase, help=_('Direction of traffic, default: ingress.')) parser.add_argument( '--excluded', diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index b279cf601..e3dfa6989 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -253,11 +253,13 @@ def add_known_arguments(self, parser): '| normal | baremetal>', choices=['direct', 'direct-physical', 'macvtap', 'normal', 'baremetal'], + type=utils.convert_to_lowercase, help=_('VNIC type for this port.')) parser.add_argument( '--vnic_type', choices=['direct', 'direct-physical', 'macvtap', 'normal', 'baremetal'], + type=utils.convert_to_lowercase, help=argparse.SUPPRESS) parser.add_argument( '--binding-profile', diff --git a/neutronclient/neutron/v2_0/rbac.py b/neutronclient/neutron/v2_0/rbac.py index 64645e573..e1153b055 100644 --- a/neutronclient/neutron/v2_0/rbac.py +++ b/neutronclient/neutron/v2_0/rbac.py @@ -14,6 +14,7 @@ # under the License. from neutronclient._i18n import _ +from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronV20 # key=object_type: value={key=resource, value=cmd_resource} @@ -66,6 +67,7 @@ def add_known_arguments(self, parser): parser.add_argument( '--type', choices=RBAC_OBJECTS.keys(), required=True, + type=utils.convert_to_lowercase, help=_('Type of the object that RBAC policy affects.')) parser.add_argument( '--target-tenant', @@ -74,6 +76,7 @@ def add_known_arguments(self, parser): 'policy will be enforced.')) parser.add_argument( '--action', choices=['access_as_external', 'access_as_shared'], + type=utils.convert_to_lowercase, required=True, help=_('Action for the RBAC policy.')) diff --git a/neutronclient/neutron/v2_0/securitygroup.py b/neutronclient/neutron/v2_0/securitygroup.py index 8cb89ac1c..3c0bafa0e 100644 --- a/neutronclient/neutron/v2_0/securitygroup.py +++ b/neutronclient/neutron/v2_0/securitygroup.py @@ -18,6 +18,7 @@ from neutronclient._i18n import _ from neutronclient.common import exceptions +from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronV20 @@ -314,6 +315,7 @@ def add_known_arguments(self, parser): 'which the rule is added.')) parser.add_argument( '--direction', + type=utils.convert_to_lowercase, default='ingress', choices=['ingress', 'egress'], help=_('Direction of traffic: ingress/egress.')) parser.add_argument( @@ -321,6 +323,7 @@ def add_known_arguments(self, parser): help=_('IPv4/IPv6')) parser.add_argument( '--protocol', + type=utils.convert_to_lowercase, help=_('Protocol of packet. Allowed values are ' '[icmp, icmpv6, tcp, udp] and ' 'integer representations [0-255].')) diff --git a/neutronclient/neutron/v2_0/subnet.py b/neutronclient/neutron/v2_0/subnet.py index 107b52543..633826422 100644 --- a/neutronclient/neutron/v2_0/subnet.py +++ b/neutronclient/neutron/v2_0/subnet.py @@ -184,10 +184,12 @@ def add_known_arguments(self, parser): help=_('CIDR of subnet to create.')) parser.add_argument( '--ipv6-ra-mode', + type=utils.convert_to_lowercase, choices=['dhcpv6-stateful', 'dhcpv6-stateless', 'slaac'], help=_('IPv6 RA (Router Advertisement) mode.')) parser.add_argument( '--ipv6-address-mode', + type=utils.convert_to_lowercase, choices=['dhcpv6-stateful', 'dhcpv6-stateless', 'slaac'], help=_('IPv6 address mode.')) parser.add_argument( diff --git a/neutronclient/neutron/v2_0/vpn/ikepolicy.py b/neutronclient/neutron/v2_0/vpn/ikepolicy.py index 3a6c1b678..1aa8a5af3 100644 --- a/neutronclient/neutron/v2_0/vpn/ikepolicy.py +++ b/neutronclient/neutron/v2_0/vpn/ikepolicy.py @@ -28,27 +28,31 @@ def add_common_args(parser, is_create=True): help=_('Description of the IKE policy.')) parser.add_argument( '--auth-algorithm', + type=utils.convert_to_lowercase, default='sha1' if is_create else argparse.SUPPRESS, - help=_('Authentication algorithm in lowercase. ' - 'Default:sha1')) + help=_('Authentication algorithm, default:sha1.')) parser.add_argument( '--encryption-algorithm', default='aes-128' if is_create else argparse.SUPPRESS, - help=_('Encryption algorithm in lowercase, default:aes-128')) + type=utils.convert_to_lowercase, + help=_('Encryption algorithm, default:aes-128.')) parser.add_argument( '--phase1-negotiation-mode', default='main' if is_create else argparse.SUPPRESS, choices=['main'], - help=_('IKE Phase1 negotiation mode in lowercase, default:main')) + type=utils.convert_to_lowercase, + help=_('IKE Phase1 negotiation mode, default:main.')) parser.add_argument( '--ike-version', default='v1' if is_create else argparse.SUPPRESS, choices=['v1', 'v2'], - help=_('IKE version in lowercase, default:v1')) + type=utils.convert_to_lowercase, + help=_('IKE version for the policy, default:v1.')) parser.add_argument( '--pfs', default='group5' if is_create else argparse.SUPPRESS, - help=_('Perfect Forward Secrecy in lowercase, default:group5')) + type=utils.convert_to_lowercase, + help=_('Perfect Forward Secrecy, default:group5.')) parser.add_argument( '--lifetime', metavar="units=UNITS,value=VALUE", @@ -109,7 +113,7 @@ class UpdateIKEPolicy(neutronv20.UpdateCommand): def add_known_arguments(self, parser): parser.add_argument( '--name', - help=_('Updated Name of the IKE policy.')) + help=_('Updated name of the IKE policy.')) add_common_args(parser, is_create=False) def args2body(self, parsed_args): diff --git a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py index 505f4c5d9..03a635f6e 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py +++ b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py @@ -96,7 +96,7 @@ def add_known_arguments(self, parser, is_create=True): parser.add_argument( '--mtu', default='1500' if is_create else argparse.SUPPRESS, - help=_('MTU size for the connection, default:1500')) + help=_('MTU size for the connection, default:1500.')) parser.add_argument( '--initiator', default='bi-directional' if is_create else argparse.SUPPRESS, diff --git a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py index e9ad2f5cc..016ebbd19 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py +++ b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py @@ -26,19 +26,22 @@ def add_common_args(parser, is_create=True): parser.add_argument( '--auth-algorithm', default='sha1' if is_create else argparse.SUPPRESS, - help=_('Authentication algorithm in lowercase, default:sha1')) + type=utils.convert_to_lowercase, + help=_('Authentication algorithm for IPsec policy, default:sha1.')) parser.add_argument( '--description', help=_('Description of the IPsec policy.')) - parser.add_argument( - '--encryption-algorithm', - default='aes-128' if is_create else argparse.SUPPRESS, - help=_('Encryption algorithm in lowercase, default:aes-128')) parser.add_argument( '--encapsulation-mode', default='tunnel' if is_create else argparse.SUPPRESS, choices=['tunnel', 'transport'], - help=_('Encapsulation mode in lowercase, default:tunnel')) + type=utils.convert_to_lowercase, + help=_('Encapsulation mode for IPsec policy, default:tunnel.')) + parser.add_argument( + '--encryption-algorithm', + default='aes-128' if is_create else argparse.SUPPRESS, + type=utils.convert_to_lowercase, + help=_('Encryption algorithm for IPsec policy, default:aes-128.')) parser.add_argument( '--lifetime', metavar="units=UNITS,value=VALUE", @@ -47,12 +50,14 @@ def add_common_args(parser, is_create=True): parser.add_argument( '--pfs', default='group5' if is_create else argparse.SUPPRESS, - help=_('Perfect Forward Secrecy in lowercase, default:group5')) + type=utils.convert_to_lowercase, + help=_('Perfect Forward Secrecy for IPsec policy, default:group5.')) parser.add_argument( '--transform-protocol', default='esp' if is_create else argparse.SUPPRESS, + type=utils.convert_to_lowercase, choices=['esp', 'ah', 'ah-esp'], - help=_('Transform protocol in lowercase, default:esp')) + help=_('Transform protocol for IPsec policy, default:esp.')) def parse_common_args2body(parsed_args, body): @@ -88,6 +93,7 @@ class CreateIPsecPolicy(neutronv20.CreateCommand): """Create an IPsec policy.""" resource = 'ipsecpolicy' + help_resource = 'IPsec policy' def add_known_arguments(self, parser): parser.add_argument( diff --git a/releasenotes/notes/remove-case-dependency-773ccb3237c38e81.yaml b/releasenotes/notes/remove-case-dependency-773ccb3237c38e81.yaml new file mode 100644 index 000000000..d32f96b4f --- /dev/null +++ b/releasenotes/notes/remove-case-dependency-773ccb3237c38e81.yaml @@ -0,0 +1,7 @@ +--- +other: + - | + This patch provides user the support to use + any form of casing, thus removing the specific + UPPER/lower case inputs required by different + neutron CLIs. From 368af6c72f656f4bdcc9594020cc14145ffe6234 Mon Sep 17 00:00:00 2001 From: Cao Xuan Hoang Date: Thu, 25 Aug 2016 14:43:48 +0700 Subject: [PATCH 491/845] Clean imports in code In some part in the code we import objects. In the Openstack style guidelines they recommend to import only modules. http://docs.openstack.org/developer/hacking/#imports Change-Id: I22cb3fed9dc442eb90d54b876f7443522709e846 --- neutronclient/tests/unit/test_cli20_extensions.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/neutronclient/tests/unit/test_cli20_extensions.py b/neutronclient/tests/unit/test_cli20_extensions.py index 9e7d30a34..cc2913747 100644 --- a/neutronclient/tests/unit/test_cli20_extensions.py +++ b/neutronclient/tests/unit/test_cli20_extensions.py @@ -15,18 +15,16 @@ import sys -from neutronclient.neutron.v2_0.extension import ListExt -from neutronclient.neutron.v2_0.extension import ShowExt -from neutronclient.tests.unit.test_cli20 import CLITestV20Base -from neutronclient.tests.unit.test_cli20 import MyApp +from neutronclient.neutron.v2_0 import extension +from neutronclient.tests.unit import test_cli20 -class CLITestV20Extension(CLITestV20Base): +class CLITestV20Extension(test_cli20.CLITestV20Base): id_field = 'alias' def test_list_extensions(self): resources = 'extensions' - cmd = ListExt(MyApp(sys.stdout), None) + cmd = extension.ListExt(test_cli20.MyApp(sys.stdout), None) contents = [{'alias': 'ext1', 'name': 'name1', 'other': 'other1'}, {'alias': 'ext2', 'name': 'name2', 'other': 'other2'}] ret = self._test_list_resources(resources, cmd, @@ -41,7 +39,7 @@ def test_show_extension(self): # -F option does not work for ext-show at the moment, so -F option # is not passed in the commandline args as other tests do. resource = 'extension' - cmd = ShowExt(MyApp(sys.stdout), None) + cmd = extension.ShowExt(test_cli20.MyApp(sys.stdout), None) args = [self.test_id] ext_alias = self.test_id self._test_show_resource(resource, cmd, ext_alias, args, fields=[]) From 3a76631e485d7abe686396072745396baf8371a5 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Thu, 25 Aug 2016 17:33:34 +0900 Subject: [PATCH 492/845] Drop LBaaS v1 functional tests LBaaS v1 plugin was dropped recently in neutron-lbaas code base. In Newton, there is no longer a way to test LBaaS v1 commands. Change-Id: Iad03044d9e739ed4291179e77d4af2fb5799dfa7 Closes-Bug: #1616749 --- .../functional/core/test_readonly_neutron.py | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/neutronclient/tests/functional/core/test_readonly_neutron.py b/neutronclient/tests/functional/core/test_readonly_neutron.py index a9de5bd0e..ee3d6e099 100644 --- a/neutronclient/tests/functional/core/test_readonly_neutron.py +++ b/neutronclient/tests/functional/core/test_readonly_neutron.py @@ -69,25 +69,6 @@ def test_neutron_meter_label_list(self): def test_neutron_meter_label_rule_list(self): self.neutron('meter-label-rule-list') - def _test_neutron_lbaas_command(self, command): - try: - self.neutron(command) - except exceptions.CommandFailed as e: - if '404 Not Found' not in e.stderr: - self.fail('%s: Unexpected failure.' % command) - - def test_neutron_lb_healthmonitor_list(self): - self._test_neutron_lbaas_command('lb-healthmonitor-list') - - def test_neutron_lb_member_list(self): - self._test_neutron_lbaas_command('lb-member-list') - - def test_neutron_lb_pool_list(self): - self._test_neutron_lbaas_command('lb-pool-list') - - def test_neutron_lb_vip_list(self): - self._test_neutron_lbaas_command('lb-vip-list') - def test_neutron_net_external_list(self): net_ext_list = self.parser.listing(self.neutron('net-external-list')) self.assertTableStruct(net_ext_list, ['id', 'name', 'subnets']) From a20f9c5555dd54be1440ec6cc6cb8527345168ea Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Thu, 25 Aug 2016 15:54:22 +0900 Subject: [PATCH 493/845] Move advanced service functional tests to functional-adv-svcs Previously FWaaSv1 readonly functional tests are run as part of not functional-adv-svcs but functional tests for base neutron. Change-Id: I19ae76a05e9c261591c5a77bfaac55c4865a400d --- .../adv-svcs/test_readonly_neutron_fwaas.py | 37 +++++++++++++++++++ .../functional/core/test_readonly_neutron.py | 19 ---------- neutronclient/tests/functional/hooks/fwaas | 2 +- .../tests/functional/hooks/gate_hook.sh | 8 +--- neutronclient/tests/functional/hooks/vpnaas | 1 + 5 files changed, 41 insertions(+), 26 deletions(-) create mode 100644 neutronclient/tests/functional/adv-svcs/test_readonly_neutron_fwaas.py create mode 100644 neutronclient/tests/functional/hooks/vpnaas diff --git a/neutronclient/tests/functional/adv-svcs/test_readonly_neutron_fwaas.py b/neutronclient/tests/functional/adv-svcs/test_readonly_neutron_fwaas.py new file mode 100644 index 000000000..a3e2573f4 --- /dev/null +++ b/neutronclient/tests/functional/adv-svcs/test_readonly_neutron_fwaas.py @@ -0,0 +1,37 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from neutronclient.tests.functional import base + + +class SimpleReadOnlyNeutronClientTest(base.ClientTestBase): + + """Tests for FWaaS v1 based client commands that are read only""" + + def test_neutron_firewall_list(self): + firewall_list = self.parser.listing(self.neutron + ('firewall-list')) + self.assertTableStruct(firewall_list, ['id', 'name', + 'firewall_policy_id']) + + def test_neutron_firewall_policy_list(self): + firewall_policy = self.parser.listing(self.neutron + ('firewall-policy-list')) + self.assertTableStruct(firewall_policy, ['id', 'name', + 'firewall_rules']) + + def test_neutron_firewall_rule_list(self): + firewall_rule = self.parser.listing(self.neutron + ('firewall-rule-list')) + self.assertTableStruct(firewall_rule, ['id', 'name', + 'firewall_policy_id', + 'summary', 'enabled']) diff --git a/neutronclient/tests/functional/core/test_readonly_neutron.py b/neutronclient/tests/functional/core/test_readonly_neutron.py index ee3d6e099..9429c9a4c 100644 --- a/neutronclient/tests/functional/core/test_readonly_neutron.py +++ b/neutronclient/tests/functional/core/test_readonly_neutron.py @@ -103,25 +103,6 @@ def test_neutron_subnet_list(self): self.assertTableStruct(subnet_list, ['id', 'name', 'cidr', 'allocation_pools']) - def test_neutron_firewall_list(self): - firewall_list = self.parser.listing(self.neutron - ('firewall-list')) - self.assertTableStruct(firewall_list, ['id', 'name', - 'firewall_policy_id']) - - def test_neutron_firewall_policy_list(self): - firewall_policy = self.parser.listing(self.neutron - ('firewall-policy-list')) - self.assertTableStruct(firewall_policy, ['id', 'name', - 'firewall_rules']) - - def test_neutron_firewall_rule_list(self): - firewall_rule = self.parser.listing(self.neutron - ('firewall-rule-list')) - self.assertTableStruct(firewall_rule, ['id', 'name', - 'firewall_policy_id', - 'summary', 'enabled']) - def test_neutron_help(self): help_text = self.neutron('help') lines = help_text.split('\n') diff --git a/neutronclient/tests/functional/hooks/fwaas b/neutronclient/tests/functional/hooks/fwaas index 0f04168ad..8f53b16a0 100644 --- a/neutronclient/tests/functional/hooks/fwaas +++ b/neutronclient/tests/functional/hooks/fwaas @@ -1,2 +1,2 @@ enable_plugin neutron-fwaas git://git.openstack.org/openstack/neutron-fwaas -enable_service q-fwaas \ No newline at end of file +enable_service q-fwaas diff --git a/neutronclient/tests/functional/hooks/gate_hook.sh b/neutronclient/tests/functional/hooks/gate_hook.sh index 9ad05aabf..e0dd841dd 100755 --- a/neutronclient/tests/functional/hooks/gate_hook.sh +++ b/neutronclient/tests/functional/hooks/gate_hook.sh @@ -19,14 +19,10 @@ ${config} " } -if [ "$VENV" == "functional" ] -then - load_rc_hook fwaas -fi - if [ "$VENV" == "functional-adv-svcs" ] then - export DEVSTACK_LOCAL_CONFIG="enable_plugin neutron-vpnaas git://git.openstack.org/openstack/neutron-vpnaas" + load_rc_hook fwaas + load_rc_hook vpnaas fi $BASE/new/devstack-gate/devstack-vm-gate.sh diff --git a/neutronclient/tests/functional/hooks/vpnaas b/neutronclient/tests/functional/hooks/vpnaas new file mode 100644 index 000000000..11cecb91f --- /dev/null +++ b/neutronclient/tests/functional/hooks/vpnaas @@ -0,0 +1 @@ +enable_plugin neutron-vpnaas git://git.openstack.org/openstack/neutron-vpnaas From 56754d0d16356e2a46d98b453153fd7639e042fb Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Thu, 25 Aug 2016 01:11:27 +0000 Subject: [PATCH 494/845] Updated from global requirements Change-Id: I834d81cf3ee29263adc5a880de170a960329d5d9 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e866ae75b..1f8e59efa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ cliff!=1.16.0,!=1.17.0,>=1.15.0 # Apache-2.0 debtcollector>=1.2.0 # Apache-2.0 iso8601>=0.1.11 # MIT netaddr!=0.7.16,>=0.7.12 # BSD -osc-lib>=0.4.0 # Apache-2.0 +osc-lib>=1.0.2 # Apache-2.0 oslo.i18n>=2.1.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 oslo.utils>=3.16.0 # Apache-2.0 From ee6b6eafb7799f7066d437f826deb39bd3172c87 Mon Sep 17 00:00:00 2001 From: Armando Migliaccio Date: Tue, 23 Aug 2016 17:13:28 -0700 Subject: [PATCH 495/845] Provide client bindings for DELETE method of auto-allocated-topology extension DocImpact: Add documentation for auto-allocate-topology-delete CLI Partial-bug: #1614872 Depends-on: I2fba51bdf8c781fcc0449e1e9947de976c96eec4 Change-Id: I07ef85e4a0c43613351820bd56e429d0155c9fa5 --- .../neutron/v2_0/auto_allocated_topology.py | 32 +++++++++++++++++++ neutronclient/shell.py | 2 ++ .../unit/test_auto_allocated_topology.py | 21 ++++++++++++ neutronclient/v2_0/client.py | 8 +++++ ...ated-topology-delete-aaccd60bd0f2e7b2.yaml | 4 +++ 5 files changed, 67 insertions(+) create mode 100644 releasenotes/notes/add-auto-allocated-topology-delete-aaccd60bd0f2e7b2.yaml diff --git a/neutronclient/neutron/v2_0/auto_allocated_topology.py b/neutronclient/neutron/v2_0/auto_allocated_topology.py index 3f0f58baf..12ca4dbd1 100644 --- a/neutronclient/neutron/v2_0/auto_allocated_topology.py +++ b/neutronclient/neutron/v2_0/auto_allocated_topology.py @@ -14,6 +14,8 @@ # under the License. # +from __future__ import print_function + import argparse from cliff import show from oslo_serialization import jsonutils @@ -79,3 +81,33 @@ def take_action(self, parsed_args): return zip(*sorted(data[self.resource].items())) else: return None + + +class DeleteAutoAllocatedTopology(v2_0.NeutronCommand): + """Delete the auto-allocated topology of a given tenant.""" + + resource = 'auto_allocated_topology' + + def get_parser(self, prog_name): + parser = super(DeleteAutoAllocatedTopology, self).get_parser(prog_name) + parser.add_argument( + '--tenant-id', metavar='tenant-id', + help=_('The owner tenant ID.')) + # Allow people to do + # neutron auto-allocated-topology-delete + # (Only useful to users who can look at other tenants' topologies.) + # We use a different name for this arg because the default will + # override whatever is in the named arg otherwise. + parser.add_argument( + 'pos_tenant_id', + help=argparse.SUPPRESS, nargs='?') + return parser + + def take_action(self, parsed_args): + client = self.get_client() + tenant_id = parsed_args.tenant_id or parsed_args.pos_tenant_id + client.delete_auto_allocated_topology(tenant_id) + # It tenant is None, let's be clear on what it means. + tenant_id = tenant_id or 'None (i.e. yours)' + print(_('Deleted topology for tenant %s.') % tenant_id, + file=self.app.stdout) diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 831996c1d..dad8d48ed 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -413,6 +413,8 @@ def take_action(self, parsed_args): 'availability-zone-list': availability_zone.ListAvailabilityZone, 'auto-allocated-topology-show': ( auto_allocated_topology.ShowAutoAllocatedTopology), + 'auto-allocated-topology-delete': ( + auto_allocated_topology.DeleteAutoAllocatedTopology), 'bgp-dragent-speaker-add': ( bgp_drsched.AddBGPSpeakerToDRAgent ), diff --git a/neutronclient/tests/unit/test_auto_allocated_topology.py b/neutronclient/tests/unit/test_auto_allocated_topology.py index af2742b0e..e2f7e5b13 100644 --- a/neutronclient/tests/unit/test_auto_allocated_topology.py +++ b/neutronclient/tests/unit/test_auto_allocated_topology.py @@ -53,3 +53,24 @@ def test_show_auto_allocated_topology_dry_run_as_admin(self): args = ['--dry-run', 'some-tenant'] self._test_show_resource(resource, cmd, "some-tenant", args, fields=('dry-run',)) + + def test_delete_auto_allocated_topology_arg(self): + resource = 'auto_allocated_topology' + cmd = aat.DeleteAutoAllocatedTopology(test_cli20.MyApp(sys.stdout), + None) + args = ['--tenant-id', self.test_id] + self._test_delete_resource(resource, cmd, self.test_id, args) + + def test_delete_auto_allocated_topology_posarg(self): + resource = 'auto_allocated_topology' + cmd = aat.DeleteAutoAllocatedTopology(test_cli20.MyApp(sys.stdout), + None) + args = ['some-tenant'] + self._test_delete_resource(resource, cmd, "some-tenant", args) + + def test_delete_auto_allocated_topology_no_arg(self): + resource = 'auto_allocated_topology' + cmd = aat.DeleteAutoAllocatedTopology(test_cli20.MyApp(sys.stdout), + None) + args = [] + self._test_delete_resource(resource, cmd, "None", args) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 9c48fe009..3b6a5900e 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -1807,6 +1807,14 @@ def get_auto_allocated_topology(self, project_id, **_params): self.auto_allocated_topology_path % project_id, params=_params) + @debtcollector.renames.renamed_kwarg( + 'tenant_id', 'project_id', replace=True) + def delete_auto_allocated_topology(self, project_id, **_params): + """Delete a project's auto-allocated topology.""" + return self.delete( + self.auto_allocated_topology_path % project_id, + params=_params) + @debtcollector.renames.renamed_kwarg( 'tenant_id', 'project_id', replace=True) def validate_auto_allocated_topology_requirements(self, project_id): diff --git a/releasenotes/notes/add-auto-allocated-topology-delete-aaccd60bd0f2e7b2.yaml b/releasenotes/notes/add-auto-allocated-topology-delete-aaccd60bd0f2e7b2.yaml new file mode 100644 index 000000000..84c395236 --- /dev/null +++ b/releasenotes/notes/add-auto-allocated-topology-delete-aaccd60bd0f2e7b2.yaml @@ -0,0 +1,4 @@ +--- +features: + - The ``auto-allocated-topology-delete`` command allows users to + delete the auto allocated topology. From 496969e05ddcbcfdfeb4ff5941e37209c56846cb Mon Sep 17 00:00:00 2001 From: Armando Migliaccio Date: Mon, 29 Aug 2016 23:09:47 -0700 Subject: [PATCH 496/845] Make trunk commands handle timestamps for trunk resources Partial-implements: blueprint vlan-aware-vms Change-Id: I853ae5d01401cf9df429b2e3f4565c2aef8a34dd --- neutronclient/osc/v2/trunk/network_trunk.py | 4 ++++ .../tests/unit/osc/v2/trunk/test_network_trunk.py | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/neutronclient/osc/v2/trunk/network_trunk.py b/neutronclient/osc/v2/trunk/network_trunk.py index 5ba9b9a03..70f791fd8 100644 --- a/neutronclient/osc/v2/trunk/network_trunk.py +++ b/neutronclient/osc/v2/trunk/network_trunk.py @@ -159,10 +159,14 @@ def take_action(self, parsed_args): headers += ( 'Status', 'State', + 'Created At', + 'Updated At', ) columns += ( 'status', 'admin_state_up', + 'created_at', + 'updated_at' ) return (headers, (utils.get_dict_properties( diff --git a/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py b/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py index ee16fd028..d9658156b 100644 --- a/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py +++ b/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py @@ -295,7 +295,9 @@ def test_show_all_options(self): class TestListNetworkTrunk(test_fakes.TestNeutronClientOSCV2): # Create trunks to be listed. - _trunks = fakes.FakeTrunk.create_trunks(count=3) + _trunks = fakes.FakeTrunk.create_trunks( + {'created_at': '2001-01-01 00:00:00', + 'updated_at': '2001-01-01 00:00:00'}, count=3) columns = ( 'ID', @@ -306,6 +308,8 @@ class TestListNetworkTrunk(test_fakes.TestNeutronClientOSCV2): columns_long = columns + ( 'Status', 'State', + 'Created At', + 'Updated At' ) data = [] for t in _trunks: @@ -324,6 +328,8 @@ class TestListNetworkTrunk(test_fakes.TestNeutronClientOSCV2): t['description'], t['status'], trunk._format_admin_state(t['admin_state_up']), + '2001-01-01 00:00:00', + '2001-01-01 00:00:00', )) def setUp(self): From a6cdd1daced1080c42caabfcd9cb0ee509d8a269 Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez Date: Thu, 2 Jun 2016 09:39:10 +0100 Subject: [PATCH 497/845] Add QoS egress minimum bandwidth rule to neutronclient The following patch implements the QoS egress minimum bandwidth assurance in neutronclient. Change-Id: I025522f73a9a8e3a9f69f097cedaeba330b9914a Depends-On: I6b619a96a2bfde164646c71409b671352bc6ce7d Depends-On: I13c54be22f35ac7eb5835d8424a919d0b61a8e95 Partial-Bug: #1560963 --- .../v2_0/qos/minimum_bandwidth_rule.py | 111 ++++++++++++++ neutronclient/shell.py | 16 ++ .../qos/test_cli20_minimum_bandwidth_rule.py | 142 ++++++++++++++++++ .../tests/unit/qos/test_cli20_rule.py | 7 +- neutronclient/v2_0/client.py | 34 +++++ ...os_minimum_bandwidth-dc4adb23c51de30b.yaml | 4 + 6 files changed, 312 insertions(+), 2 deletions(-) create mode 100644 neutronclient/neutron/v2_0/qos/minimum_bandwidth_rule.py create mode 100644 neutronclient/tests/unit/qos/test_cli20_minimum_bandwidth_rule.py create mode 100644 releasenotes/notes/qos_minimum_bandwidth-dc4adb23c51de30b.yaml diff --git a/neutronclient/neutron/v2_0/qos/minimum_bandwidth_rule.py b/neutronclient/neutron/v2_0/qos/minimum_bandwidth_rule.py new file mode 100644 index 000000000..cc64549af --- /dev/null +++ b/neutronclient/neutron/v2_0/qos/minimum_bandwidth_rule.py @@ -0,0 +1,111 @@ +# Copyright (c) 2016 Intel Corporation. +# 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. +# + +from neutronclient._i18n import _ +from neutronclient.common import utils +from neutronclient.neutron import v2_0 as neutronv20 +from neutronclient.neutron.v2_0.qos import rule as qos_rule + + +MINIMUM_BANDWIDTH_RULE_RESOURCE = 'minimum_bandwidth_rule' + + +def add_minimum_bandwidth_arguments(parser): + parser.add_argument( + '--min-kbps', + required=True, + type=str, + help=_('QoS minimum bandwidth assurance, expressed in kilobits ' + 'per second.')) + # NOTE(ralonsoh): the only direction implemented is "egress". Please, + # refer to the spec (https://review.openstack.org/#/c/316082/). + parser.add_argument( + '--direction', + # NOTE(ihrachys): though server picks the default for us (egress), it's + # better to require the argument to make the UI more explicit and the + # intentions more clear in the future when we add other values for the + # attribute on server side. + required=True, + type=utils.convert_to_lowercase, + choices=['egress'], + help=_('Traffic direction.')) + + +def update_minimum_bandwidth_args2body(parsed_args, body): + neutronv20.update_dict(parsed_args, body, ['min_kbps', 'direction']) + + +class CreateQoSMinimumBandwidthRule(qos_rule.QosRuleMixin, + neutronv20.CreateCommand): + """Create a qos minimum bandwidth rule.""" + + resource = MINIMUM_BANDWIDTH_RULE_RESOURCE + + def add_known_arguments(self, parser): + super(CreateQoSMinimumBandwidthRule, self).add_known_arguments( + parser) + add_minimum_bandwidth_arguments(parser) + + def args2body(self, parsed_args): + body = {} + update_minimum_bandwidth_args2body(parsed_args, body) + return {self.resource: body} + + +class ListQoSMinimumBandwidthRules(qos_rule.QosRuleMixin, + neutronv20.ListCommand): + """List all qos minimum bandwidth rules belonging to the specified policy. + + """ + + resource = MINIMUM_BANDWIDTH_RULE_RESOURCE + _formatters = {} + pagination_support = True + sorting_support = True + + +class ShowQoSMinimumBandwidthRule(qos_rule.QosRuleMixin, + neutronv20.ShowCommand): + """Show information about the given qos minimum bandwidth rule.""" + + resource = MINIMUM_BANDWIDTH_RULE_RESOURCE + allow_names = False + + +class UpdateQoSMinimumBandwidthRule(qos_rule.QosRuleMixin, + neutronv20.UpdateCommand): + """Update the given qos minimum bandwidth rule.""" + + resource = MINIMUM_BANDWIDTH_RULE_RESOURCE + allow_names = False + + def add_known_arguments(self, parser): + super(UpdateQoSMinimumBandwidthRule, self).add_known_arguments( + parser) + add_minimum_bandwidth_arguments(parser) + + def args2body(self, parsed_args): + body = {} + update_minimum_bandwidth_args2body(parsed_args, body) + return {self.resource: body} + + +class DeleteQoSMinimumBandwidthRule(qos_rule.QosRuleMixin, + neutronv20.DeleteCommand): + """Delete a given qos minimum bandwidth rule.""" + + resource = MINIMUM_BANDWIDTH_RULE_RESOURCE + allow_names = False diff --git a/neutronclient/shell.py b/neutronclient/shell.py index dad8d48ed..39ab0aa58 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -73,6 +73,7 @@ from neutronclient.neutron.v2_0 import purge from neutronclient.neutron.v2_0.qos import bandwidth_limit_rule from neutronclient.neutron.v2_0.qos import dscp_marking_rule +from neutronclient.neutron.v2_0.qos import minimum_bandwidth_rule from neutronclient.neutron.v2_0.qos import policy as qos_policy from neutronclient.neutron.v2_0.qos import rule as qos_rule from neutronclient.neutron.v2_0 import quota @@ -397,6 +398,21 @@ def take_action(self, parsed_args): 'qos-dscp-marking-rule-delete': ( dscp_marking_rule.DeleteQoSDscpMarkingRule ), + 'qos-minimum-bandwidth-rule-create': ( + minimum_bandwidth_rule.CreateQoSMinimumBandwidthRule + ), + 'qos-minimum-bandwidth-rule-show': ( + minimum_bandwidth_rule.ShowQoSMinimumBandwidthRule + ), + 'qos-minimum-bandwidth-rule-list': ( + minimum_bandwidth_rule.ListQoSMinimumBandwidthRules + ), + 'qos-minimum-bandwidth-rule-update': ( + minimum_bandwidth_rule.UpdateQoSMinimumBandwidthRule + ), + 'qos-minimum-bandwidth-rule-delete': ( + minimum_bandwidth_rule.DeleteQoSMinimumBandwidthRule + ), 'qos-available-rule-types': qos_rule.ListQoSRuleTypes, 'flavor-list': flavor.ListFlavor, 'flavor-show': flavor.ShowFlavor, diff --git a/neutronclient/tests/unit/qos/test_cli20_minimum_bandwidth_rule.py b/neutronclient/tests/unit/qos/test_cli20_minimum_bandwidth_rule.py new file mode 100644 index 000000000..0c44f5cd7 --- /dev/null +++ b/neutronclient/tests/unit/qos/test_cli20_minimum_bandwidth_rule.py @@ -0,0 +1,142 @@ +# Copyright (c) 2016 Intel Corporation. +# 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 sys + +from neutronclient.neutron.v2_0.qos import minimum_bandwidth_rule as bw_rule +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20QoSMinimumBandwidthRuleJSON(test_cli20.CLITestV20Base): + + non_admin_status_resources = ['minimum_bandwidth_rule'] + + def setUp(self): + super(CLITestV20QoSMinimumBandwidthRuleJSON, self).setUp() + self.res = 'minimum_bandwidth_rule' + self.cmd_res = 'qos_minimum_bandwidth_rule' + self.ress = self.res + 's' + self.cmd_ress = self.cmd_res + 's' + + def test_create_minimum_bandwidth_rule_min_kbps_only(self): + cmd = bw_rule.CreateQoSMinimumBandwidthRule( + test_cli20.MyApp(sys.stdout), None) + my_id = 'my-id' + min_kbps = '1500' + policy_id = 'policy_id' + args = ['--min-kbps', min_kbps, + policy_id] + position_names = ['min_kbps'] + position_values = [min_kbps] + self.assertRaises(SystemExit, self._test_create_resource, + self.res, cmd, '', my_id, args, + position_names, position_values, + cmd_resource=self.cmd_res, + parent_id=policy_id, + no_api_call=True) + + def test_create_minimum_bandwidth_rule_direction_only(self): + cmd = bw_rule.CreateQoSMinimumBandwidthRule( + test_cli20.MyApp(sys.stdout), None) + my_id = 'my-id' + direction = 'egress' + policy_id = 'policy_id' + args = ['--direction', direction, + policy_id] + position_names = ['direction'] + position_values = [direction] + self.assertRaises(SystemExit, self._test_create_resource, + self.res, cmd, '', my_id, args, + position_names, position_values, + cmd_resource=self.cmd_res, + parent_id=policy_id, + no_api_call=True) + + def test_create_minimum_bandwidth_rule_none(self): + cmd = bw_rule.CreateQoSMinimumBandwidthRule( + test_cli20.MyApp(sys.stdout), None) + my_id = 'my-id' + policy_id = 'policy_id' + args = [policy_id] + position_names = [] + position_values = [] + self.assertRaises(SystemExit, self._test_create_resource, + self.res, cmd, '', my_id, args, + position_names, position_values, + cmd_resource=self.cmd_res, + parent_id=policy_id, + no_api_call=True) + + def test_create_minimum_bandwidth_rule_all(self): + cmd = bw_rule.CreateQoSMinimumBandwidthRule( + test_cli20.MyApp(sys.stdout), None) + my_id = 'my-id' + min_kbps = '1500' + direction = 'egress' + policy_id = 'policy_id' + args = ['--min-kbps', min_kbps, + '--direction', direction, + policy_id] + position_names = ['direction', 'min_kbps'] + position_values = [direction, min_kbps] + self._test_create_resource(self.res, cmd, '', my_id, args, + position_names, position_values, + cmd_resource=self.cmd_res, + parent_id=policy_id) + + def test_update_minimum_bandwidth_rule(self): + cmd = bw_rule.UpdateQoSMinimumBandwidthRule( + test_cli20.MyApp(sys.stdout), None) + my_id = 'my-id' + min_kbps = '1200' + direction = 'egress' + policy_id = 'policy_id' + args = ['--min-kbps', min_kbps, + '--direction', direction, + my_id, policy_id] + self._test_update_resource(self.res, cmd, my_id, args, + {'min_kbps': min_kbps, + 'direction': direction}, + cmd_resource=self.cmd_res, + parent_id=policy_id) + + def test_delete_minimum_bandwidth_rule(self): + cmd = bw_rule.DeleteQoSMinimumBandwidthRule( + test_cli20.MyApp(sys.stdout), None) + my_id = 'my-id' + policy_id = 'policy_id' + args = [my_id, policy_id] + self._test_delete_resource(self.res, cmd, my_id, args, + cmd_resource=self.cmd_res, + parent_id=policy_id) + + def test_show_minimum_bandwidth_rule(self): + cmd = bw_rule.ShowQoSMinimumBandwidthRule( + test_cli20.MyApp(sys.stdout), None) + policy_id = 'policy_id' + args = [self.test_id, policy_id] + self._test_show_resource(self.res, cmd, self.test_id, args, + [], cmd_resource=self.cmd_res, + parent_id=policy_id) + + def test_list_minimum_bandwidth_rule(self): + cmd = bw_rule.ListQoSMinimumBandwidthRules( + test_cli20.MyApp(sys.stdout), None) + policy_id = 'policy_id' + args = [policy_id] + contents = [{'name': 'rule1', 'min-kbps': 1000, 'direction': 'egress'}] + self._test_list_resources(self.cmd_ress, cmd, parent_id=policy_id, + base_args=args, response_contents=contents) diff --git a/neutronclient/tests/unit/qos/test_cli20_rule.py b/neutronclient/tests/unit/qos/test_cli20_rule.py index bdbf52df9..c1da5afef 100644 --- a/neutronclient/tests/unit/qos/test_cli20_rule.py +++ b/neutronclient/tests/unit/qos/test_cli20_rule.py @@ -22,7 +22,9 @@ class CLITestV20QoSRuleJSON(test_cli20.CLITestV20Base): - non_admin_status_resources = ['bandwidth_limit_rule', 'dscp_marking_rule'] + non_admin_status_resources = ['bandwidth_limit_rule', + 'dscp_marking_rule', + 'minimum_bandwidth_rule'] def setUp(self): super(CLITestV20QoSRuleJSON, self).setUp() @@ -32,7 +34,8 @@ def test_list_qos_rule_types(self): resources = 'rule_types' cmd_resources = 'qos_rule_types' response_contents = [{'type': 'bandwidth_limit', - 'type': 'dscp_marking'}] + 'type': 'dscp_marking', + 'type': 'minimum_bandwidth'}] cmd = qos_rule.ListQoSRuleTypes(test_cli20.MyApp(sys.stdout), None) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index a01cc91e9..95324a704 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -595,6 +595,10 @@ class Client(ClientBase): qos_bandwidth_limit_rule_path = "/qos/policies/%s/bandwidth_limit_rules/%s" qos_dscp_marking_rules_path = "/qos/policies/%s/dscp_marking_rules" qos_dscp_marking_rule_path = "/qos/policies/%s/dscp_marking_rules/%s" + qos_minimum_bandwidth_rules_path = \ + "/qos/policies/%s/minimum_bandwidth_rules" + qos_minimum_bandwidth_rule_path = \ + "/qos/policies/%s/minimum_bandwidth_rules/%s" qos_rule_types_path = "/qos/rule-types" qos_rule_type_path = "/qos/rule-types/%s" flavors_path = "/flavors" @@ -660,6 +664,7 @@ class Client(ClientBase): 'qos_policies': 'qos_policy', 'policies': 'policy', 'bandwidth_limit_rules': 'bandwidth_limit_rule', + 'minimum_bandwidth_rules': 'minimum_bandwidth_rule', 'rules': 'rule', 'dscp_marking_rules': 'dscp_marking_rule', 'rule_types': 'rule_type', @@ -1740,6 +1745,35 @@ def delete_dscp_marking_rule(self, rule, policy): return self.delete(self.qos_dscp_marking_rule_path % (policy, rule)) + def list_minimum_bandwidth_rules(self, policy_id, retrieve_all=True, + **_params): + """Fetches a list of all minimum bandwidth rules for the given policy. + + """ + return self.list('qos_minimum_bandwidth_rules', + self.qos_minimum_bandwidth_rules_path % + policy_id, retrieve_all, **_params) + + def show_minimum_bandwidth_rule(self, rule, policy, body=None): + """Fetches information of a certain minimum bandwidth rule.""" + return self.get(self.qos_minimum_bandwidth_rule_path % + (policy, rule), body=body) + + def create_minimum_bandwidth_rule(self, policy, body=None): + """Creates a new minimum bandwidth rule.""" + return self.post(self.qos_minimum_bandwidth_rules_path % policy, + body=body) + + def update_minimum_bandwidth_rule(self, rule, policy, body=None): + """Updates a minimum bandwidth rule.""" + return self.put(self.qos_minimum_bandwidth_rule_path % + (policy, rule), body=body) + + def delete_minimum_bandwidth_rule(self, rule, policy): + """Deletes a minimum bandwidth rule.""" + return self.delete(self.qos_minimum_bandwidth_rule_path % + (policy, rule)) + def create_flavor(self, body=None): """Creates a new Neutron service flavor.""" return self.post(self.flavors_path, body=body) diff --git a/releasenotes/notes/qos_minimum_bandwidth-dc4adb23c51de30b.yaml b/releasenotes/notes/qos_minimum_bandwidth-dc4adb23c51de30b.yaml new file mode 100644 index 000000000..2d1002f07 --- /dev/null +++ b/releasenotes/notes/qos_minimum_bandwidth-dc4adb23c51de30b.yaml @@ -0,0 +1,4 @@ +--- +features: + - New create, update, list, show, and delete commands are added for the QoS + minimum bandwidth rule. From afe3c8389df7a413c8290ea1c574c077f956cc1b Mon Sep 17 00:00:00 2001 From: Kevin Benton Date: Sun, 28 Aug 2016 10:14:12 -0700 Subject: [PATCH 498/845] Add flavor argument to router This allows the router to take a flavor argument that can be either the ID or the name of a flavor to use when creating a router. Partially-Implements: blueprint multi-l3-backends Change-Id: I100fc75de6d41900f6452f356f0b7b741cd177ce --- neutronclient/neutron/v2_0/router.py | 7 +++++++ neutronclient/tests/unit/test_cli20_router.py | 13 +++++++++++++ 2 files changed, 20 insertions(+) diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index 200feee18..b431daaea 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -71,6 +71,9 @@ def add_known_arguments(self, parser): parser.add_argument( '--description', help=_('Description of router.')) + parser.add_argument( + '--flavor', + help=_('ID or name of flavor.')) utils.add_boolean_argument( parser, '--distributed', dest='distributed', help=_('Create a distributed router.')) @@ -82,6 +85,10 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): body = {'admin_state_up': parsed_args.admin_state} + if parsed_args.flavor: + _flavor_id = neutronV20.find_resourceid_by_name_or_id( + self.get_client(), 'flavor', parsed_args.flavor) + body['flavor_id'] = _flavor_id neutronV20.update_dict(parsed_args, body, ['name', 'tenant_id', 'distributed', 'ha', 'description']) diff --git a/neutronclient/tests/unit/test_cli20_router.py b/neutronclient/tests/unit/test_cli20_router.py index cd7ed0310..1ca818de0 100644 --- a/neutronclient/tests/unit/test_cli20_router.py +++ b/neutronclient/tests/unit/test_cli20_router.py @@ -35,6 +35,19 @@ def test_create_router(self): position_names, position_values, description='rooter') + def test_create_router_flavor(self): + resource = 'router' + cmd = router.CreateRouter(test_cli20.MyApp(sys.stdout), None) + name = 'router1' + myid = 'myid' + flavor = 'router-flavor' + args = [name, '--flavor', flavor] + position_names = ['name', ] + position_values = [name, flavor] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values, + flavor_id='router-flavor') + def test_create_router_tenant(self): # Create router: --tenant_id tenantid myname. resource = 'router' From 01afaa7b0bc22592cd91e31ec3c1c2500cfb4680 Mon Sep 17 00:00:00 2001 From: gecong1973 Date: Tue, 12 Jul 2016 09:29:08 +0800 Subject: [PATCH 499/845] fix one spelling mistake availibility should be availability Change-Id: I691e8b39eea2dc7a865d91bd17bcb8f401387458 --- neutronclient/v2_0/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 5504fd1dd..391d57b1c 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -1774,7 +1774,7 @@ def delete_bgp_peer(self, peer_id): return self.delete(self.bgp_peer_path % peer_id) def list_network_ip_availabilities(self, retrieve_all=True, **_params): - """Fetches IP availibility information for all networks""" + """Fetches IP availability information for all networks""" return self.list('network_ip_availabilities', self.network_ip_availabilities_path, retrieve_all, **_params) From d875cb34b94c9efe9767cc5ccd89e462bc0e7614 Mon Sep 17 00:00:00 2001 From: SongmingYan Date: Wed, 8 Jun 2016 04:19:39 -0400 Subject: [PATCH 500/845] Fix the problem of "qos-bandwidth-limit-rule-show" Add a arg "**_params" in show_bandwidth_limit_rule() to use the "-F" option. Change-Id: I1bee877a2e83801527bcccffe404053b6aafe012 Partial-Bug: #1587291 --- .../tests/unit/qos/test_cli20_bandwidth_limit_rule.py | 4 ++-- neutronclient/v2_0/client.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/neutronclient/tests/unit/qos/test_cli20_bandwidth_limit_rule.py b/neutronclient/tests/unit/qos/test_cli20_bandwidth_limit_rule.py index d1b39c76d..8d4380a81 100644 --- a/neutronclient/tests/unit/qos/test_cli20_bandwidth_limit_rule.py +++ b/neutronclient/tests/unit/qos/test_cli20_bandwidth_limit_rule.py @@ -131,7 +131,7 @@ def test_show_bandwidth_limit_rule(self): cmd = bw_rule.ShowQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), None) policy_id = 'policy_id' - args = [self.test_id, policy_id] + args = ['--fields', 'id', self.test_id, policy_id] self._test_show_resource(self.res, cmd, self.test_id, args, - [], cmd_resource=self.cmd_res, + ['id'], cmd_resource=self.cmd_res, parent_id=policy_id) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 3b6a5900e..2b3e59810 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -1693,10 +1693,10 @@ def list_bandwidth_limit_rules(self, policy_id, self.qos_bandwidth_limit_rules_path % policy_id, retrieve_all, **_params) - def show_bandwidth_limit_rule(self, rule, policy, body=None): + def show_bandwidth_limit_rule(self, rule, policy, **_params): """Fetches information of a certain bandwidth limit rule.""" return self.get(self.qos_bandwidth_limit_rule_path % - (policy, rule), body=body) + (policy, rule), params=_params) def create_bandwidth_limit_rule(self, policy, body=None): """Creates a new bandwidth limit rule.""" From 98110501e024547dfdc3c3c441ced3f91fe8462c Mon Sep 17 00:00:00 2001 From: Anindita Das Date: Thu, 7 Jul 2016 16:03:19 +0000 Subject: [PATCH 501/845] quota-update to return an error msg for 0 args If no arguments are provided while executing neutron quota-update instead of printing the current quota values it will throw an error message "Must specify new values to update quota" Added unit test to verify that the exception is raised when no arguments are provided to quota update Includes release notes. DocImpact Closes-Bug: #1597552 Change-Id: I476991c39eafa16148f0cc3252ae5c26b9c8cbfc --- neutronclient/neutron/v2_0/quota.py | 4 ++++ neutronclient/tests/unit/test_quota.py | 8 ++++++++ .../fix-quota-update-zero-args-d596c4169c2d2e30.yaml | 9 +++++++++ 3 files changed, 21 insertions(+) create mode 100644 releasenotes/notes/fix-quota-update-zero-args-d596c4169c2d2e30.yaml diff --git a/neutronclient/neutron/v2_0/quota.py b/neutronclient/neutron/v2_0/quota.py index 061f87371..626af1275 100644 --- a/neutronclient/neutron/v2_0/quota.py +++ b/neutronclient/neutron/v2_0/quota.py @@ -209,6 +209,10 @@ def args2body(self, parsed_args): quota[resource] = self._validate_int( resource, getattr(parsed_args, resource)) + if not quota: + raise exceptions.CommandError( + message=_('Must specify a valid resource with new quota ' + 'value')) return {self.resource: quota} def take_action(self, parsed_args): diff --git a/neutronclient/tests/unit/test_quota.py b/neutronclient/tests/unit/test_quota.py index b9d7b4448..78f4daefe 100644 --- a/neutronclient/tests/unit/test_quota.py +++ b/neutronclient/tests/unit/test_quota.py @@ -92,3 +92,11 @@ def test_show_quota_default(self): self.assertIn('subnet', _str) self.assertIn('port', _str) self.assertNotIn('subnetpool', _str) + + def test_update_quota_noargs(self): + resource = 'quota' + cmd = test_quota.UpdateQuota(test_cli20.MyApp(sys.stdout), None) + args = [self.test_id] + self.assertRaises(exceptions.CommandError, self._test_update_resource, + resource, cmd, self.test_id, args=args, + extrafields=None) diff --git a/releasenotes/notes/fix-quota-update-zero-args-d596c4169c2d2e30.yaml b/releasenotes/notes/fix-quota-update-zero-args-d596c4169c2d2e30.yaml new file mode 100644 index 000000000..eb70752a3 --- /dev/null +++ b/releasenotes/notes/fix-quota-update-zero-args-d596c4169c2d2e30.yaml @@ -0,0 +1,9 @@ +--- +fixes: + - | + Fix CLI quota-update to return an error message for no args + + * ``quota-update`` CLI will return an error message + ``Must specify a valid resource with new quota value`` if no + argument is provided while executing it. If arguments are + provided with CLI, no existing behavior is changed. From 89d4a9a2eafc256b8a88234e93aba296fd0bf586 Mon Sep 17 00:00:00 2001 From: Armando Migliaccio Date: Mon, 29 Aug 2016 22:01:49 -0700 Subject: [PATCH 502/845] Make trunk commands handle description for trunk resources Depends-on: Ie3149e206fe8a83631dd9d42d344fea3f03dc0db Partially-implements: blueprint vlan-aware-vms Change-Id: Ie721beb78a4a3b6da9e0f68167b91be043b7034e --- doc/source/usage/osc/v2/network-trunk.rst | 10 ++++ neutronclient/osc/v2/trunk/network_trunk.py | 18 +++++- .../tests/unit/osc/v2/trunk/fakes.py | 5 +- .../unit/osc/v2/trunk/test_network_trunk.py | 55 +++++++++++++------ 4 files changed, 68 insertions(+), 20 deletions(-) diff --git a/doc/source/usage/osc/v2/network-trunk.rst b/doc/source/usage/osc/v2/network-trunk.rst index 5a79310a4..a39e3bd0e 100644 --- a/doc/source/usage/osc/v2/network-trunk.rst +++ b/doc/source/usage/osc/v2/network-trunk.rst @@ -37,6 +37,7 @@ Create a network trunk for a given project [--subport ] [--enable | --disable] [--project [--project-domain ]] + [--description ] .. option:: --parent-port @@ -65,6 +66,10 @@ Create a network trunk for a given project Domain the project belongs to (name or ID). This can be used in case collisions between project names exist. +.. option:: --description + + A description of the trunk. + network trunk delete -------------------- @@ -106,6 +111,7 @@ Set network trunk properties os network trunk set [--name ] + [--description ] [--subport ] [--enable | --disable] @@ -114,6 +120,10 @@ Set network trunk properties Set trunk name +.. option:: --description + + A description of the trunk. + .. option:: --subport Subport to add. Subport is of form 'port=,segmentation-type=,segmentation-ID=' diff --git a/neutronclient/osc/v2/trunk/network_trunk.py b/neutronclient/osc/v2/trunk/network_trunk.py index e3eed827a..5ba9b9a03 100644 --- a/neutronclient/osc/v2/trunk/network_trunk.py +++ b/neutronclient/osc/v2/trunk/network_trunk.py @@ -44,6 +44,11 @@ def get_parser(self, prog_name): metavar='', help=_("Name of the trunk to create") ) + parser.add_argument( + '--description', + metavar='', + help=_("A description of the trunk") + ) parser.add_argument( '--parent-port', metavar='', @@ -141,12 +146,14 @@ def take_action(self, parsed_args): headers = ( 'ID', 'Name', - 'Parent Port' + 'Parent Port', + 'Description' ) columns = ( 'id', 'name', - 'port_id' + 'port_id', + 'description' ) if parsed_args.long: headers += ( @@ -179,6 +186,11 @@ def get_parser(self, prog_name): metavar="", help=_("Set trunk name") ) + parser.add_argument( + '--description', + metavar='', + help=_("A description of the trunk") + ) parser.add_argument( '--subport', metavar='', @@ -313,6 +325,8 @@ def _get_attrs_for_trunk(client_manager, parsed_args): attrs = {} if parsed_args.name is not None: attrs['name'] = str(parsed_args.name) + if parsed_args.description is not None: + attrs['description'] = str(parsed_args.description) if parsed_args.enable: attrs['admin_state_up'] = True if parsed_args.disable: diff --git a/neutronclient/tests/unit/osc/v2/trunk/fakes.py b/neutronclient/tests/unit/osc/v2/trunk/fakes.py index 0eb6f9786..1acbcc3da 100644 --- a/neutronclient/tests/unit/osc/v2/trunk/fakes.py +++ b/neutronclient/tests/unit/osc/v2/trunk/fakes.py @@ -24,8 +24,8 @@ def create_one_trunk(attrs=None): :param Dictionary attrs: A dictionary with all attributes :return: - A Dictionary with id, name, admin_state_up, - port_id, sub_ports, status and project_id + A Dictionary with id, name, description, admin_state_up, port_id, + sub_ports, status and project_id """ attrs = attrs or {} @@ -33,6 +33,7 @@ def create_one_trunk(attrs=None): trunk_attrs = { 'id': 'trunk-id-' + uuid.uuid4().hex, 'name': 'trunk-name-' + uuid.uuid4().hex, + 'description': '', 'port_id': 'port-' + uuid.uuid4().hex, 'admin_state_up': True, 'project_id': 'project-id-' + uuid.uuid4().hex, diff --git a/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py b/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py index 5bb5ea214..ee16fd028 100644 --- a/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py +++ b/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py @@ -36,6 +36,7 @@ class TestCreateNetworkTrunk(test_fakes.TestNeutronClientOSCV2): columns = ( 'admin_state_up', + 'description', 'id', 'name', 'port_id', @@ -43,15 +44,18 @@ class TestCreateNetworkTrunk(test_fakes.TestNeutronClientOSCV2): 'status', 'sub_ports', ) - data = ( - trunk._format_admin_state(_trunk['admin_state_up']), - _trunk['id'], - _trunk['name'], - _trunk['port_id'], - _trunk['project_id'], - _trunk['status'], - utils.format_list_of_dicts(_trunk['sub_ports']), - ) + + def get_data(self): + return ( + trunk._format_admin_state(self._trunk['admin_state_up']), + self._trunk['description'], + self._trunk['id'], + self._trunk['name'], + self._trunk['port_id'], + self._trunk['project_id'], + self._trunk['status'], + utils.format_list_of_dicts(self._trunk['sub_ports']), + ) def setUp(self): super(TestCreateNetworkTrunk, self).setUp() @@ -59,6 +63,7 @@ def setUp(self): new=_get_id).start() self.neutronclient.create_trunk = mock.Mock( return_value={trunk.TRUNK: self._trunk}) + self.data = self.get_data() # Get the command object to test self.cmd = trunk.CreateNetworkTrunk(self.app, self.namespace) @@ -92,9 +97,12 @@ def test_create_default_options(self): self.assertEqual(self.data, data) def test_create_full_options(self): + self._trunk['description'] = 'foo description' + self.data = self.get_data() subport = self._trunk['sub_ports'][0] arglist = [ "--disable", + "--description", self._trunk['description'], "--parent-port", self._trunk['port_id'], "--subport", 'port=%(port)s,segmentation-type=%(seg_type)s,' 'segmentation-id=%(seg_id)s' % { @@ -105,6 +113,7 @@ def test_create_full_options(self): ] verifylist = [ ('name', self._trunk['name']), + ('description', self._trunk['description']), ('parent_port', self._trunk['port_id']), ('add_subports', [{ 'port': subport['port_id'], @@ -119,6 +128,7 @@ def test_create_full_options(self): self.neutronclient.create_trunk.assert_called_once_with({ trunk.TRUNK: {'name': self._trunk['name'], + 'description': self._trunk['description'], 'admin_state_up': False, 'sub_ports': [subport], 'port_id': self._trunk['port_id']} @@ -229,6 +239,7 @@ class TestShowNetworkTrunk(test_fakes.TestNeutronClientOSCV2): columns = ( 'admin_state_up', + 'description', 'id', 'name', 'port_id', @@ -238,6 +249,7 @@ class TestShowNetworkTrunk(test_fakes.TestNeutronClientOSCV2): ) data = ( trunk._format_admin_state(_trunk['admin_state_up']), + _trunk['description'], _trunk['id'], _trunk['name'], _trunk['port_id'], @@ -289,6 +301,7 @@ class TestListNetworkTrunk(test_fakes.TestNeutronClientOSCV2): 'ID', 'Name', 'Parent Port', + 'Description' ) columns_long = columns + ( 'Status', @@ -300,6 +313,7 @@ class TestListNetworkTrunk(test_fakes.TestNeutronClientOSCV2): t['id'], t['name'], t['port_id'], + t['description'] )) data_long = [] for t in _trunks: @@ -307,6 +321,7 @@ class TestListNetworkTrunk(test_fakes.TestNeutronClientOSCV2): t['id'], t['name'], t['port_id'], + t['description'], t['status'], trunk._format_admin_state(t['admin_state_up']), )) @@ -356,6 +371,7 @@ class TestSetNetworkTrunk(test_fakes.TestNeutronClientOSCV2): 'admin_state_up', 'id', 'name', + 'description', 'port_id', 'project_id', 'status', @@ -365,6 +381,7 @@ class TestSetNetworkTrunk(test_fakes.TestNeutronClientOSCV2): trunk._format_admin_state(_trunk['admin_state_up']), _trunk['id'], _trunk['name'], + _trunk['description'], _trunk['port_id'], _trunk['project_id'], _trunk['status'], @@ -383,26 +400,32 @@ def setUp(self): # Get the command object to test self.cmd = trunk.SetNetworkTrunk(self.app, self.namespace) - def test_set_network_trunk_name(self): + def _test_set_network_trunk_attr(self, attr, value): arglist = [ - '--name', 'trunky', - self._trunk['name'], + '--%s' % attr, value, + self._trunk[attr], ] verifylist = [ - ('name', 'trunky'), - ('trunk', self._trunk['name']), + (attr, value), + ('trunk', self._trunk[attr]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) attrs = { - 'name': 'trunky', + attr: value, } self.neutronclient.update_trunk.assert_called_once_with( - self._trunk['name'], {trunk.TRUNK: attrs}) + self._trunk[attr], {trunk.TRUNK: attrs}) self.assertIsNone(result) + def test_set_network_trunk_name(self): + self._test_set_network_trunk_attr('name', 'trunky') + + def test_test_set_network_trunk_description(self): + self._test_set_network_trunk_attr('description', 'description') + def test_set_network_trunk_admin_state_up_disable(self): arglist = [ '--disable', From 4baa4a06ec76a17eb4d7dc996bd8baa2f8c1f577 Mon Sep 17 00:00:00 2001 From: Richard Theis Date: Fri, 19 Aug 2016 12:28:28 +0100 Subject: [PATCH 503/845] Devref: Newton mid-cycle updates for transition to OSC Update the "Transition to OpenStack Client" devref based on Newton mid-cycle [1]. [1] https://etherpad.openstack.org/p/newton-neutron-midcycle-workitems Change-Id: Ie1b4eec30cbee5d41971450b676f7c898a667f0b Related-Bug: #1521291 --- doc/source/devref/transition_to_osc.rst | 59 ++++++++++++++++--------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/doc/source/devref/transition_to_osc.rst b/doc/source/devref/transition_to_osc.rst index 92fa92600..29c0ee187 100644 --- a/doc/source/devref/transition_to_osc.rst +++ b/doc/source/devref/transition_to_osc.rst @@ -92,11 +92,10 @@ Transition Steps 6. **In Progress:** OSC continues enhancing its networking support. At this point and when applicable, enhancements to the ``neutron`` - CLI must also be made to the ``openstack`` CLI and the OpenStack Python SDK. - Enhancements to the networking support in the OpenStack Python SDK will be - handled via bugs. Users of the neutron client's command extensions should - start their transition to the OSC plugin system. - See the developer guide section below for more information on this step. + CLI must also be made to the ``openstack`` CLI and possibly the + OpenStack Python SDK. Users of the neutron client's command extensions + should start their transition to the OSC plugin system. See the + developer guide section below for more information on this step. 7. **Not Started:** Deprecate the ``neutron`` CLI once the criteria below have been meet. Running the CLI after it has been deprecated will issue a warning @@ -125,12 +124,11 @@ The ``neutron`` CLI version 4.x, without extensions, supports over 200 commands while the ``openstack`` CLI version 2.6.0 supports over 50 networking commands. Of the 50 commands, some do not have all of the options or arguments of their ``neutron`` CLI equivalent. With this large functional -gap, a couple critical questions for developers during this transition are "Which -CLI do I change?" and "Where does my CLI belong?" The answer depends on the -state of a command and the state of the overall transition. Details are -outlined in the tables below. Early stages of the transition will require dual -maintenance. Eventually, dual maintenance will be reduced to critical bug fixes -only with feature requests only being made to the ``openstack`` CLI. +gap, a few critical questions for developers during this transition are "Which +CLI do I change?", "Where does my CLI belong?", and "Which Python library do I change?" +The answer depends on the state of a command and the state of the overall transition. +Details are outlined in the tables below. Early stages of the transition will require +dual maintenance. **Which CLI do I change?** @@ -156,6 +154,9 @@ only with feature requests only being made to the ``openstack`` CLI. +===========================+===================+=================================================+ | Core | No | python-openstackclient | +---------------------------+-------------------+-------------------------------------------------+ +| Advanced Feature | Yes | python-neutronclient | +| (neutron repository) | | (``neutronclient/osc/v2/``) | ++---------------------------+-------------------+-------------------------------------------------+ | Dynamic Routing | Yes | python-neutronclient | | | | (``neutronclient/osc/v2/dynamic_routing``) | +---------------------------+-------------------+-------------------------------------------------+ @@ -169,21 +170,38 @@ only with feature requests only being made to the ``openstack`` CLI. | LBaaS v2 | Yes | python-neutronclient | | | | (``neutronclient/osc/v2/lbaas``) | +---------------------------+-------------------+-------------------------------------------------+ -| Other | Yes | Applicable project owning networking resource | -+---------------------------+-------------------+-------------------------------------------------+ | VPNaaS | Yes | python-neutronclient | | | | (``neutronclient/osc/v2/vpnaas``) | +---------------------------+-------------------+-------------------------------------------------+ +| Other | Yes | Applicable project owning networking resource | ++---------------------------+-------------------+-------------------------------------------------+ + +**Which Python library do I change?** + ++-------------------------------------------------+-----------------------------------------------+ +| OpenStack Project for ``openstack`` Commands | Python Library to Change | ++=================================================+===============================================+ +| python-openstackclient | python-openstacksdk | ++-------------------------------------------------+-----------------------------------------------+ +| python-neutronclient | python-neutronclient | ++-------------------------------------------------+-----------------------------------------------+ +| Other | Applicable project owning network resource | ++-------------------------------------------------+-----------------------------------------------+ **Important:** The actual name of the command object and/or action in OSC may differ from those used by neutron in order to follow the OSC command structure and to avoid -name conflicts. Developers should get new command objects and actions approved by -the OSC team before proceeding with the implementation. - -The "Core" group includes network resources that provide ``neutron`` project features -(i.e. not advanced service or other features). Examples in the "Core" group include: -network, subnet, port, etc. +name conflicts. The `network` prefix must be used to avoid name conflicts if the +command object name is highly likely to have an ambiguous meaning. Developers should +get new command objects and actions approved by the OSC team before proceeding with the +implementation. + +The "Core" group includes network resources that provide core ``neutron`` project +features (e.g. network, subnet, port, etc.) and not advanced features in the +``neutron`` project (e.g. trunk, etc.) or advanced services in separate projects +(FWaaS, LBaaS, VPNaaS, dynamic routing, etc.). +The "Other" group applies projects other than the core ``neutron`` project. +Contact the neutron PTL or core team with questions on network resource classification. When adding or updating an ``openstack`` networking command to python-openstackclient, changes may first be required to the @@ -192,8 +210,7 @@ properties and/or actions. Once the OpenStack Python SDK changes are merged, the related OSC changes can be merged. The OSC changes may require an update to the OSC openstacksdk version in the `requirements.txt `_ -file. ``openstack`` networking commands outside python-openstackclient -are encouraged but not required to use the OpenStack Python SDK. +file. When adding an ``openstack`` networking command to python-openstackclient, you can optionally propose an From 00940cf7c51b3f502adc07277dede90c817237d6 Mon Sep 17 00:00:00 2001 From: Hirofumi Ichihara Date: Wed, 31 Aug 2016 00:10:12 +0900 Subject: [PATCH 504/845] Use correct cmd_parser In _test_update_resource_action(), although the method is for update, "delete_" prefix is passed into get_parser(). This patch uses "update_". Change-Id: Iebdf5e748cc3cae6c9d66d63a8f1ccf93d2ae60a --- neutronclient/tests/unit/test_cli20.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index c8fec064c..d071d85a8 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -561,7 +561,7 @@ def _test_update_resource_action(self, resource, cmd, myid, action, args, headers=mox.ContainsKeyValue( 'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), retval)) self.mox.ReplayAll() - cmd_parser = cmd.get_parser("delete_" + cmd_resource) + cmd_parser = cmd.get_parser("update_" + cmd_resource) shell.run_command(cmd, cmd_parser, args) self.mox.VerifyAll() self.mox.UnsetStubs() From 0e183511db90de760e0702de4f75f0d35eab237f Mon Sep 17 00:00:00 2001 From: Andreas Jaeger Date: Tue, 30 Aug 2016 19:55:59 +0200 Subject: [PATCH 505/845] Sync tools/tox_install.sh Sync tools/tox_install.sh with python-openstackclient. This brings in the following improvements: * Do not leave temporary directory around, instead delete temporary directory directly after usage (change I939eae82dba3287fd4e4086128ebf4609a0e0770). * Do not set ZUUL_BRANCH explicitely and remove unused if condition (change I0077c986a17d6bb92791474e03d1e77776e9382f). Change-Id: I58e62c7b3f9340ca6df6f9f17a9ed7801b32dbf5 --- tools/tox_install.sh | 66 ++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/tools/tox_install.sh b/tools/tox_install.sh index c800a4141..e3fb45960 100755 --- a/tools/tox_install.sh +++ b/tools/tox_install.sh @@ -15,41 +15,41 @@ CONSTRAINTS_FILE=$1 shift install_cmd="pip install" -if [ $CONSTRAINTS_FILE != "unconstrained" ]; then - - mydir=$(mktemp -dt "$CLIENT_NAME-tox_install-XXXXXXX") - localfile=$mydir/upper-constraints.txt - if [[ $CONSTRAINTS_FILE != http* ]]; then - CONSTRAINTS_FILE=file://$CONSTRAINTS_FILE - fi - curl $CONSTRAINTS_FILE -k -o $localfile - install_cmd="$install_cmd -c$localfile" - - if [ $requirements_installed -eq 0 ]; then - echo "ALREADY INSTALLED" > /tmp/tox_install.txt - echo "Requirements already installed; using existing package" - elif [ -x "$ZUUL_CLONER" ]; then - export ZUUL_BRANCH=${ZUUL_BRANCH-$BRANCH} - echo "ZUUL CLONER" > /tmp/tox_install.txt - pushd $mydir - $ZUUL_CLONER --cache-dir \ - /opt/git \ - --branch $BRANCH_NAME \ - git://git.openstack.org \ - openstack/requirements - cd openstack/requirements - $install_cmd -e . - popd - else - echo "PIP HARDCODE" > /tmp/tox_install.txt - if [ -z "$REQUIREMENTS_PIP_LOCATION" ]; then - REQUIREMENTS_PIP_LOCATION="git+https://git.openstack.org/openstack/requirements@$BRANCH_NAME#egg=requirements" - fi - $install_cmd -U -e ${REQUIREMENTS_PIP_LOCATION} +mydir=$(mktemp -dt "$CLIENT_NAME-tox_install-XXXXXXX") +trap "rm -rf $mydir" EXIT +localfile=$mydir/upper-constraints.txt +if [[ $CONSTRAINTS_FILE != http* ]]; then + CONSTRAINTS_FILE=file://$CONSTRAINTS_FILE +fi +curl $CONSTRAINTS_FILE -k -o $localfile +install_cmd="$install_cmd -c$localfile" + +if [ $requirements_installed -eq 0 ]; then + echo "ALREADY INSTALLED" > /tmp/tox_install.txt + echo "Requirements already installed; using existing package" +elif [ -x "$ZUUL_CLONER" ]; then + echo "ZUUL CLONER" > /tmp/tox_install.txt + pushd $mydir + $ZUUL_CLONER --cache-dir \ + /opt/git \ + --branch $BRANCH_NAME \ + git://git.openstack.org \ + openstack/requirements + cd openstack/requirements + $install_cmd -e . + popd +else + echo "PIP HARDCODE" > /tmp/tox_install.txt + if [ -z "$REQUIREMENTS_PIP_LOCATION" ]; then + REQUIREMENTS_PIP_LOCATION="git+https://git.openstack.org/openstack/requirements@$BRANCH_NAME#egg=requirements" fi - - edit-constraints $localfile -- $CLIENT_NAME "-e file://$PWD#egg=$CLIENT_NAME" + $install_cmd -U -e ${REQUIREMENTS_PIP_LOCATION} fi +# This is the main purpose of the script: Allow local installation of +# the current repo. It is listed in constraints file and thus any +# install will be constrained and we need to unconstrain it. +edit-constraints $localfile -- $CLIENT_NAME "-e file://$PWD#egg=$CLIENT_NAME" + $install_cmd -U $* exit $? From 2a15b11019cadd6c190383df8d1ba8ddccf6eb2a Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Thu, 1 Sep 2016 09:21:45 +0000 Subject: [PATCH 506/845] Updated from global requirements Change-Id: I429eeaf2e39441e26a16c9caf56ba8d3f3107bd2 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 1f8e59efa..709fa2e15 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ osc-lib>=1.0.2 # Apache-2.0 oslo.i18n>=2.1.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 oslo.utils>=3.16.0 # Apache-2.0 -os-client-config!=1.19.0,!=1.19.1,!=1.20.0,!=1.20.1,>=1.13.1 # Apache-2.0 +os-client-config!=1.19.0,!=1.19.1,!=1.20.0,!=1.20.1,!=1.21.0,>=1.13.1 # Apache-2.0 keystoneauth1>=2.10.0 # Apache-2.0 requests>=2.10.0 # Apache-2.0 simplejson>=2.2.0 # MIT From 75468cc02fd7509bcc6dd874a82db23e3c4f31ec Mon Sep 17 00:00:00 2001 From: guiyanxing Date: Mon, 15 Aug 2016 15:26:41 +0800 Subject: [PATCH 507/845] Correct DisassociatingHealthmonitor help messages Change-Id: Ie6f0419046f25c078f49c5343fbb4e10232a0b88 --- neutronclient/neutron/v2_0/lb/healthmonitor.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/neutronclient/neutron/v2_0/lb/healthmonitor.py b/neutronclient/neutron/v2_0/lb/healthmonitor.py index c2bd70cc9..600a7f2a9 100644 --- a/neutronclient/neutron/v2_0/lb/healthmonitor.py +++ b/neutronclient/neutron/v2_0/lb/healthmonitor.py @@ -145,10 +145,11 @@ def get_parser(self, prog_name): parser = super(DisassociateHealthMonitor, self).get_parser(prog_name) parser.add_argument( 'health_monitor_id', metavar='HEALTH_MONITOR_ID', - help=_('Health monitor to associate.')) + help=_('Health monitor to disassociate.')) parser.add_argument( 'pool_id', metavar='POOL', - help=_('ID of the pool to be associated with the health monitor.')) + help=_('ID of the pool to be disassociated with the health ' + 'monitor.')) return parser def take_action(self, parsed_args): From 47ae850c0e3e75dbc2b6586978273c00fa3e6203 Mon Sep 17 00:00:00 2001 From: Doug Hellmann Date: Fri, 2 Sep 2016 09:43:32 -0400 Subject: [PATCH 508/845] Update reno for stable/newton Change-Id: I1d54586f380bf827f4689ec226437603da749942 --- releasenotes/source/index.rst | 1 + releasenotes/source/newton.rst | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 releasenotes/source/newton.rst diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index 40b342ddd..5b5bd02bb 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -6,5 +6,6 @@ :maxdepth: 1 unreleased + newton mitaka old_relnotes diff --git a/releasenotes/source/newton.rst b/releasenotes/source/newton.rst new file mode 100644 index 000000000..97036ed25 --- /dev/null +++ b/releasenotes/source/newton.rst @@ -0,0 +1,6 @@ +=================================== + Newton Series Release Notes +=================================== + +.. release-notes:: + :branch: origin/stable/newton From 6bdac38263fbee0fe91972ae3bde770429c5d724 Mon Sep 17 00:00:00 2001 From: Reedip Date: Tue, 6 Sep 2016 19:42:39 +0530 Subject: [PATCH 509/845] Replace lb_method with lb_algorithm LBaaS v2 Pools consist of lb_algorithm, but ListPool still tries to show lb_method (which was not a part of LBaaS v2 but LBaaS v1). Change-Id: I0630fafc1c555c9110bee11def873dce8ecf4770 Closes-Bug: #1620639 --- neutronclient/neutron/v2_0/lb/v2/pool.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/neutron/v2_0/lb/v2/pool.py b/neutronclient/neutron/v2_0/lb/v2/pool.py index b2bd326a9..ca0931d08 100644 --- a/neutronclient/neutron/v2_0/lb/v2/pool.py +++ b/neutronclient/neutron/v2_0/lb/v2/pool.py @@ -67,7 +67,7 @@ class ListPool(neutronV20.ListCommand): resource = 'pool' shadow_resource = 'lbaas_pool' - list_columns = ['id', 'name', 'lb_method', 'protocol', + list_columns = ['id', 'name', 'lb_algorithm', 'protocol', 'admin_state_up'] pagination_support = True sorting_support = True From f801289d0ddb5f30f8f7c1853a288068f7e999bc Mon Sep 17 00:00:00 2001 From: Doug Wiegley Date: Mon, 12 Sep 2016 23:36:18 +0000 Subject: [PATCH 510/845] Revert "HAProxy uses milliseconds for its timeout values." This reverts commit 6ba4f31fbf446a98af3d0cbfadf18e5cafc2236b. Change-Id: I0fd80b126847ea9b2cebcb2578ea1bd3110b30d4 --- neutronclient/neutron/v2_0/lb/healthmonitor.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/neutronclient/neutron/v2_0/lb/healthmonitor.py b/neutronclient/neutron/v2_0/lb/healthmonitor.py index c2bd70cc9..6fea0a12a 100644 --- a/neutronclient/neutron/v2_0/lb/healthmonitor.py +++ b/neutronclient/neutron/v2_0/lb/healthmonitor.py @@ -66,8 +66,7 @@ def add_known_arguments(self, parser): parser.add_argument( '--delay', required=True, - help=_('The time in milliseconds between sending probes to ' - 'members.')) + help=_('The time in seconds between sending probes to members.')) parser.add_argument( '--max-retries', required=True, @@ -76,8 +75,8 @@ def add_known_arguments(self, parser): parser.add_argument( '--timeout', required=True, - help=_('Maximum number of milliseconds for a monitor to wait for ' - 'a connection to be established before it times out. The ' + help=_('Maximum number of seconds for a monitor to wait for a ' + 'connection to be established before it times out. The ' 'value must be less than the delay value.')) parser.add_argument( '--type', From b8a05333ddc4e248e18080750e3cfa9cbedbca53 Mon Sep 17 00:00:00 2001 From: Monty Taylor Date: Tue, 20 Sep 2016 12:31:37 +0200 Subject: [PATCH 511/845] Handle keystoneauth exceptions in retry logic The retry_logic in do_request catches neutronclient errors, but if a SessionClient is being used, nothing throws neutronclient errors - instead keystoneauth Session throws keystoneauth exceptions. Add the keystoneauth.exceptions.ConnectionError to the things that handle the error and retry. Change-Id: If16e50ee53279652fbdfc2d13d5509f46a54124a --- neutronclient/v2_0/client.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 4da97e668..374f87391 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -22,6 +22,7 @@ import time import debtcollector.renames +from keystoneauth1 import exceptions as ksa_exc import requests import six.moves.urllib.parse as urlparse from six import string_types @@ -335,7 +336,7 @@ def retry_request(self, method, action, body=None, try: return self.do_request(method, action, body=body, headers=headers, params=params) - except exceptions.ConnectionFailed: + except (exceptions.ConnectionFailed, ksa_exc.ConnectionError): # Exception has already been logged by do_request() if i < self.retries: _logger.debug('Retrying connection to Neutron service') From 291b31e0e5eed01cbbe917a644e38c2cedafae3e Mon Sep 17 00:00:00 2001 From: lilintan Date: Thu, 22 Sep 2016 18:48:57 +0800 Subject: [PATCH 512/845] Replace 'MagicMock' with 'Mock' Change-Id: I3f14a7932de5ce1586a9900d5d9801d1fe54c71c Closes-Bug: #1475722 --- neutronclient/tests/unit/osc/v2/trunk/fakes.py | 4 ++-- neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py | 6 +++--- neutronclient/tests/unit/test_client_extension.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/neutronclient/tests/unit/osc/v2/trunk/fakes.py b/neutronclient/tests/unit/osc/v2/trunk/fakes.py index 1acbcc3da..4ae1f287f 100644 --- a/neutronclient/tests/unit/osc/v2/trunk/fakes.py +++ b/neutronclient/tests/unit/osc/v2/trunk/fakes.py @@ -66,7 +66,7 @@ def create_trunks(attrs=None, count=2): @staticmethod def get_trunks(trunks=None, count=2): - """Get an iterable MagicMock object with a list of faked trunks. + """Get an iterable Mock object with a list of faked trunks. If trunks list is provided, then initialize the Mock object with the list. Otherwise create one. @@ -81,4 +81,4 @@ def get_trunks(trunks=None, count=2): """ if trunks is None: trunks = FakeTrunk.create_trunks(count) - return mock.MagicMock(side_effect=trunks) + return mock.Mock(side_effect=trunks) diff --git a/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py b/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py index d9658156b..c8ed19ac7 100644 --- a/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py +++ b/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py @@ -222,7 +222,7 @@ def test_delete_trunk_multiple_with_exception(self): get_mock_result = [self._trunks[0], exceptions.CommandError] trunk._get_id = ( - mock.MagicMock(side_effect=get_mock_result) + mock.Mock(side_effect=get_mock_result) ) with testtools.ExpectedException(exceptions.CommandError) as e: self.cmd.take_action(parsed_args) @@ -522,7 +522,7 @@ def test_set_trunk_attrs_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.neutronclient.update_trunk = ( - mock.MagicMock(side_effect=exceptions.CommandError) + mock.Mock(side_effect=exceptions.CommandError) ) with testtools.ExpectedException(exceptions.CommandError) as e: self.cmd.take_action(parsed_args) @@ -546,7 +546,7 @@ def test_set_trunk_add_subport_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.neutronclient.trunk_add_subports = ( - mock.MagicMock(side_effect=exceptions.CommandError) + mock.Mock(side_effect=exceptions.CommandError) ) with testtools.ExpectedException(exceptions.CommandError) as e: self.cmd.take_action(parsed_args) diff --git a/neutronclient/tests/unit/test_client_extension.py b/neutronclient/tests/unit/test_client_extension.py index 973f80068..60a208588 100644 --- a/neutronclient/tests/unit/test_client_extension.py +++ b/neutronclient/tests/unit/test_client_extension.py @@ -131,7 +131,7 @@ def setUp(self): def _mock_extension_loading(self): ext_pkg = 'neutronclient.common.extension' contrib = mock.patch(ext_pkg + '._discover_via_entry_points').start() - ip_address = mock.MagicMock() + ip_address = mock.Mock() ip_address.IPAddress = self.IPAddress ip_address.IPAddressesList = self.IPAddressesList contrib.return_value = [("ip_address", ip_address)] @@ -184,7 +184,7 @@ def setUp(self): def _mock_extension_loading(self): ext_pkg = 'neutronclient.common.extension' contrib = mock.patch(ext_pkg + '._discover_via_entry_points').start() - child = mock.MagicMock() + child = mock.Mock() child.Child = self.Child child.ChildrenList = self.ChildrenList child.ChildShow = self.ChildShow From 9ecea3bbf1d4664f2a70f9a6caac88120d7c119b Mon Sep 17 00:00:00 2001 From: Reedip Date: Sat, 24 Sep 2016 11:29:38 +0530 Subject: [PATCH 513/845] Return proper error code for CLI failure Due to [1] , the return code which was expected from NeutronClient changed if a particular CLI failed to find an object belonging to the resource to be deleted. The following patch fixes it. [1]: https://review.openstack.org/#/c/263609/ Change-Id: I19b4328361157fbca3e557e02797ed0c895e924b Closes-Bug:#1623169 --- neutronclient/neutron/v2_0/__init__.py | 26 ++++++++++--------- neutronclient/tests/unit/test_cli20.py | 12 ++++++--- .../tests/unit/test_cli20_network.py | 12 +++++++++ 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index 4468bfb3a..f169ad52e 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -528,18 +528,20 @@ def _bulk_delete(self, obj_deleter, neutron_client, parsed_args_ids): % {'id': ", ".join(successful_delete), 'resource': self.cmd_resource}, file=self.app.stdout) - if non_existent: - print((_("Unable to find %(resource)s(s) with id(s) " - "'%(id)s'") % - {'resource': self.cmd_resource, - 'id': ", ".join(non_existent)}), - file=self.app.stdout) - if multiple_ids: - print((_("Multiple %(resource)s(s) matches found for name(s)" - " '%(id)s'. Please use an ID to be more specific.")) % - {'resource': self.cmd_resource, - 'id': ", ".join(multiple_ids)}, - file=self.app.stdout) + if non_existent or multiple_ids: + err_msgs = [] + if non_existent: + err_msgs.append((_("Unable to find %(resource)s(s) with id(s) " + "'%(id)s'.") % + {'resource': self.cmd_resource, + 'id': ", ".join(non_existent)})) + if multiple_ids: + err_msgs.append((_("Multiple %(resource)s(s) matches found " + "for name(s) '%(id)s'. Please use an ID " + "to be more specific.") % + {'resource': self.cmd_resource, + 'id': ", ".join(multiple_ids)})) + raise exceptions.NeutronCLIError(message='\n'.join(err_msgs)) def delete_item(self, obj_deleter, neutron_client, item_id): if self.allow_names: diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index d071d85a8..be643e786 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -512,7 +512,9 @@ def _test_show_resource(self, resource, cmd, myid, args, fields=(), self.assertIn(myid, _str) self.assertIn('myname', _str) - def _test_set_path_and_delete(self, path, parent_id, myid): + def _test_set_path_and_delete(self, path, parent_id, myid, + delete_fail=False): + return_val = 404 if delete_fail else 204 if parent_id: path = path % (parent_id, myid) else: @@ -521,11 +523,12 @@ def _test_set_path_and_delete(self, path, parent_id, myid): end_url(path, format=self.format), 'DELETE', body=None, headers=mox.ContainsKeyValue( - 'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), None)) + 'X-Auth-Token', TOKEN)).AndReturn((MyResp( + return_val), None)) def _test_delete_resource(self, resource, cmd, myid, args, cmd_resource=None, parent_id=None, - extra_id=None): + extra_id=None, delete_fail=False): self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") cmd.get_client().MultipleTimes().AndReturn(self.client) @@ -535,7 +538,8 @@ def _test_delete_resource(self, resource, cmd, myid, args, self._test_set_path_and_delete(path, parent_id, myid) # extra_id is used to test for bulk_delete if extra_id: - self._test_set_path_and_delete(path, parent_id, extra_id) + self._test_set_path_and_delete(path, parent_id, extra_id, + delete_fail) self.mox.ReplayAll() cmd_parser = cmd.get_parser("delete_" + cmd_resource) shell.run_command(cmd, cmd_parser, args) diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index 5c728b05a..cbd41bd1c 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -597,6 +597,18 @@ def test_bulk_delete_network(self): args = [myid1, myid2] self._test_delete_resource(resource, cmd, myid1, args, extra_id=myid2) + def test_bulk_delete_network_fail(self): + # Delete net: myid1 myid2. + resource = 'network' + cmd = network.DeleteNetwork(test_cli20.MyApp(sys.stdout), None) + myid1 = 'myid1' + myid2 = 'myid2' + args = [myid1, myid2] + self.assertRaises(exceptions.NeutronCLIError, + self._test_delete_resource, + resource, cmd, myid1, args, extra_id=myid2, + delete_fail=True) + def _test_extend_list(self, mox_calls): data = [{'id': 'netid%d' % i, 'name': 'net%d' % i, 'subnets': ['mysubid%d' % i]} From 0391cf7e481a07b058ea5c5ff1878ea222d64294 Mon Sep 17 00:00:00 2001 From: Andreas Jaeger Date: Thu, 6 Oct 2016 20:51:16 +0200 Subject: [PATCH 514/845] Enable release notes translation Releasenote translation publishing is being prepared. 'locale_dirs' needs to be defined in conf.py to generate translated version of the release notes. Note that this repository might not get translated release notes - or no translations at all - but we add the entry here nevertheless to prepare for it. Change-Id: I5b1b979ada5989eef490de13bf11faa49dee2d1d --- releasenotes/source/conf.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py index e1e2a5ae5..4e72a7b68 100644 --- a/releasenotes/source/conf.py +++ b/releasenotes/source/conf.py @@ -274,3 +274,6 @@ # If true, do not generate a @detailmenu in the "Top" node's menu. # texinfo_no_detailmenu = False + +# -- Options for Internationalization output ------------------------------ +locale_dirs = ['locale/'] From d8df6002d74edb64a64a5bfa2e62e2fb4aa301c3 Mon Sep 17 00:00:00 2001 From: Reedip Date: Fri, 16 Sep 2016 12:37:57 +0530 Subject: [PATCH 515/845] Fix Quota Support for HMs Health Monitors could not be updated using the `neutron quota-update` CLI. This patch fixes the same. Change-Id: Ie9eefcd9042f3d09b3ba88a9bd195463302dd5c0 Closes-Bug: #1624225 --- neutronclient/neutron/v2_0/quota.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/neutronclient/neutron/v2_0/quota.py b/neutronclient/neutron/v2_0/quota.py index 160b6e444..519c6e292 100644 --- a/neutronclient/neutron/v2_0/quota.py +++ b/neutronclient/neutron/v2_0/quota.py @@ -184,6 +184,7 @@ def get_parser(self, prog_name): help=_('The limit of pool members.')) parser.add_argument( '--health-monitor', metavar='health_monitors', + dest='healthmonitor', help=_('The limit of health monitors.')) parser.add_argument( '--loadbalancer', metavar='loadbalancers', @@ -210,7 +211,7 @@ def args2body(self, parsed_args): quota = {} for resource in ('network', 'subnet', 'port', 'router', 'floatingip', 'security_group', 'security_group_rule', - 'vip', 'pool', 'member', 'health_monitor', + 'vip', 'pool', 'member', 'healthmonitor', 'loadbalancer', 'listener'): if getattr(parsed_args, resource): quota[resource] = self._validate_int( From c14022e66d471a76fc2e9875f17e9ae107aaf09a Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Mon, 10 Oct 2016 12:38:58 +0000 Subject: [PATCH 516/845] Updated from global requirements Change-Id: Idda966e85eb7dedf950c4d398b3247ddee10d1ac --- requirements.txt | 4 ++-- test-requirements.txt | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/requirements.txt b/requirements.txt index 709fa2e15..b1a27061f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,10 +2,10 @@ # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. pbr>=1.6 # Apache-2.0 -cliff!=1.16.0,!=1.17.0,>=1.15.0 # Apache-2.0 +cliff>=2.2.0 # Apache-2.0 debtcollector>=1.2.0 # Apache-2.0 iso8601>=0.1.11 # MIT -netaddr!=0.7.16,>=0.7.12 # BSD +netaddr!=0.7.16,>=0.7.13 # BSD osc-lib>=1.0.2 # Apache-2.0 oslo.i18n>=2.1.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 diff --git a/test-requirements.txt b/test-requirements.txt index 0fa7f50cf..6625aac15 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -7,13 +7,13 @@ coverage>=3.6 # Apache-2.0 fixtures>=3.0.0 # Apache-2.0/BSD mox3>=0.7.0 # Apache-2.0 mock>=2.0 # BSD -oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0 +oslosphinx>=4.7.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 python-openstackclient>=2.1.0 # Apache-2.0 python-subunit>=0.0.18 # Apache-2.0/BSD reno>=1.8.0 # Apache2 -requests-mock>=1.0 # Apache-2.0 -sphinx!=1.3b1,<1.3,>=1.2.1 # BSD +requests-mock>=1.1 # Apache-2.0 +sphinx!=1.3b1,<1.4,>=1.2.1 # BSD testrepository>=0.0.18 # Apache-2.0/BSD testtools>=1.4.0 # MIT testscenarios>=0.4 # Apache-2.0/BSD From 86914044a596b0d42e21070db229d88e9e013e57 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Tue, 11 Oct 2016 16:57:34 +0900 Subject: [PATCH 517/845] OSC plugin: catch up with osc-lib 1.0 interface change The detail is documented at http://docs.openstack.org/developer/osc-lib/transition.html Change-Id: I012a63290380ab41b2b8207cff660d5a0adac40e --- neutronclient/osc/plugin.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/neutronclient/osc/plugin.py b/neutronclient/osc/plugin.py index 1efb6c5f7..cc27316ad 100644 --- a/neutronclient/osc/plugin.py +++ b/neutronclient/osc/plugin.py @@ -46,10 +46,10 @@ def make_client(instance): # - endpoint_type (do we need to specify it explicitly?) # - auth (session object contains auth. Is it required?) client = neutron_client(session=instance.session, - region_name=instance._region_name, - endpoint_type=instance._interface, - insecure=instance._insecure, - ca_cert=instance._cacert) + region_name=instance.region_name, + endpoint_type=instance.interface, + insecure=not instance.verify, + ca_cert=instance.cacert) return client From b9e584bd07f5a8cd344743d2ac5124503435c920 Mon Sep 17 00:00:00 2001 From: lilintan Date: Mon, 26 Sep 2016 14:37:05 +0800 Subject: [PATCH 518/845] Use method is_valid_cidr from oslo.utils Oslo.utils provides same method is_valid_cidr, just use it. Change-Id: If3c619a118c4c192951d8603a4b3c2b34546a2cf --- neutronclient/common/utils.py | 9 --------- neutronclient/shell.py | 4 ++-- neutronclient/tests/unit/test_utils.py | 8 +++++--- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/neutronclient/common/utils.py b/neutronclient/common/utils.py index 882eb2c5c..ec5658968 100644 --- a/neutronclient/common/utils.py +++ b/neutronclient/common/utils.py @@ -21,7 +21,6 @@ import functools import hashlib import logging -import netaddr import os from oslo_utils import encodeutils @@ -226,11 +225,3 @@ def add_boolean_argument(parser, name, **kwargs): choices=['True', 'true', 'False', 'false'], default=default, **kwargs) - - -def is_valid_cidr(cidr): - try: - netaddr.IPNetwork(cidr) - return True - except Exception: - return False diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 39ab0aa58..00a76ec07 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -30,6 +30,7 @@ from keystoneauth1 import session import os_client_config from oslo_utils import encodeutils +from oslo_utils import netutils from cliff import app from cliff import command @@ -39,7 +40,6 @@ from neutronclient.common import clientmanager from neutronclient.common import exceptions as exc from neutronclient.common import extension as client_extension -from neutronclient.common import utils from neutronclient.neutron.v2_0 import address_scope from neutronclient.neutron.v2_0 import agent from neutronclient.neutron.v2_0 import agentscheduler @@ -120,7 +120,7 @@ def get_first_valid_cidr(value_specs): # When cidr was separated from network, the value will not be able # to be parsed into known_args, but saved to _values_specs instead. for value in value_specs: - if utils.is_valid_cidr(value): + if netutils.is_valid_cidr(value): return value diff --git a/neutronclient/tests/unit/test_utils.py b/neutronclient/tests/unit/test_utils.py index 44152418d..769f5911e 100644 --- a/neutronclient/tests/unit/test_utils.py +++ b/neutronclient/tests/unit/test_utils.py @@ -15,6 +15,8 @@ import argparse +from oslo_utils import netutils + import testtools from neutronclient.common import exceptions @@ -140,9 +142,9 @@ def __call__(self, *args, **kwargs): self.assertEqual(('test_name', 'test_id', 'test', 'pass'), act) def test_is_cidr(self): - self.assertTrue(utils.is_valid_cidr('10.10.10.0/24')) - self.assertFalse(utils.is_valid_cidr('10.10.10..0/24')) - self.assertFalse(utils.is_valid_cidr('wrong_cidr_format')) + self.assertTrue(netutils.is_valid_cidr('10.10.10.0/24')) + self.assertFalse(netutils.is_valid_cidr('10.10.10..0/24')) + self.assertFalse(netutils.is_valid_cidr('wrong_cidr_format')) class ImportClassTestCase(testtools.TestCase): From 450c8670d9478083d767eb966379d56829dee92a Mon Sep 17 00:00:00 2001 From: Kevin Benton Date: Thu, 6 Oct 2016 20:36:47 -0700 Subject: [PATCH 519/845] Add rbac_policy to quota resources Allow updating the quota for rbac policies. Change-Id: Ia14efb844fa6dd4127840408a03cb54f16d78b35 Closes-Bug: #1631231 --- neutronclient/neutron/v2_0/quota.py | 5 ++++- .../notes/quota-update-for-rbac-192a8e65bf481941.yaml | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/quota-update-for-rbac-192a8e65bf481941.yaml diff --git a/neutronclient/neutron/v2_0/quota.py b/neutronclient/neutron/v2_0/quota.py index 519c6e292..db3f5290a 100644 --- a/neutronclient/neutron/v2_0/quota.py +++ b/neutronclient/neutron/v2_0/quota.py @@ -192,6 +192,9 @@ def get_parser(self, prog_name): parser.add_argument( '--listener', metavar='listeners', help=_('The limit of listeners.')) + parser.add_argument( + '--rbac-policy', metavar='rbac_policies', + help=_('The limit of RBAC policies.')) parser.add_argument( 'pos_tenant_id', help=argparse.SUPPRESS, nargs='?') @@ -212,7 +215,7 @@ def args2body(self, parsed_args): for resource in ('network', 'subnet', 'port', 'router', 'floatingip', 'security_group', 'security_group_rule', 'vip', 'pool', 'member', 'healthmonitor', - 'loadbalancer', 'listener'): + 'loadbalancer', 'listener', 'rbac_policy'): if getattr(parsed_args, resource): quota[resource] = self._validate_int( resource, diff --git a/releasenotes/notes/quota-update-for-rbac-192a8e65bf481941.yaml b/releasenotes/notes/quota-update-for-rbac-192a8e65bf481941.yaml new file mode 100644 index 000000000..572ce35c2 --- /dev/null +++ b/releasenotes/notes/quota-update-for-rbac-192a8e65bf481941.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Quota for RBAC policies can now be set. From dfe6714357ea3f77ee14f3ba11b01290f863b4ae Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Wed, 19 Oct 2016 17:45:52 +0000 Subject: [PATCH 520/845] Updated from global requirements Change-Id: I7c7a77b9e7935d89782ea23d3bbe54dc30afc0ac --- requirements.txt | 6 +++--- test-requirements.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index b1a27061f..8fbe8961f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,12 +6,12 @@ cliff>=2.2.0 # Apache-2.0 debtcollector>=1.2.0 # Apache-2.0 iso8601>=0.1.11 # MIT netaddr!=0.7.16,>=0.7.13 # BSD -osc-lib>=1.0.2 # Apache-2.0 +osc-lib>=1.2.0 # Apache-2.0 oslo.i18n>=2.1.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 oslo.utils>=3.16.0 # Apache-2.0 -os-client-config!=1.19.0,!=1.19.1,!=1.20.0,!=1.20.1,!=1.21.0,>=1.13.1 # Apache-2.0 -keystoneauth1>=2.10.0 # Apache-2.0 +os-client-config>=1.22.0 # Apache-2.0 +keystoneauth1>=2.14.0 # Apache-2.0 requests>=2.10.0 # Apache-2.0 simplejson>=2.2.0 # MIT six>=1.9.0 # MIT diff --git a/test-requirements.txt b/test-requirements.txt index 6625aac15..576709e8c 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -9,7 +9,7 @@ mox3>=0.7.0 # Apache-2.0 mock>=2.0 # BSD oslosphinx>=4.7.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 -python-openstackclient>=2.1.0 # Apache-2.0 +python-openstackclient>=3.3.0 # Apache-2.0 python-subunit>=0.0.18 # Apache-2.0/BSD reno>=1.8.0 # Apache2 requests-mock>=1.1 # Apache-2.0 From 7cb7950a3667bf10e665e7122c73a9fd67bfb4d7 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Fri, 21 Oct 2016 00:50:27 +0000 Subject: [PATCH 521/845] Updated from global requirements Change-Id: I6d217c553c1f4885ee7ffbb9b5623cb97910802a --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8fbe8961f..d2a806c96 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ netaddr!=0.7.16,>=0.7.13 # BSD osc-lib>=1.2.0 # Apache-2.0 oslo.i18n>=2.1.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 -oslo.utils>=3.16.0 # Apache-2.0 +oslo.utils>=3.17.0 # Apache-2.0 os-client-config>=1.22.0 # Apache-2.0 keystoneauth1>=2.14.0 # Apache-2.0 requests>=2.10.0 # Apache-2.0 From e91c82e4dcf7d888077e283b14a8bb1e3f5f7ce0 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Sat, 22 Oct 2016 01:27:17 +0000 Subject: [PATCH 522/845] Updated from global requirements Change-Id: I2ea110e8810899c46b4e768deb521df36d404692 --- test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index 576709e8c..feccad817 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -3,7 +3,7 @@ # process, which may cause wedges in the gate later. hacking<0.11,>=0.10.0 -coverage>=3.6 # Apache-2.0 +coverage>=4.0 # Apache-2.0 fixtures>=3.0.0 # Apache-2.0/BSD mox3>=0.7.0 # Apache-2.0 mock>=2.0 # BSD From 341d7805c5a959f48165ecb3e00305523c78f11c Mon Sep 17 00:00:00 2001 From: lilintan Date: Wed, 28 Sep 2016 19:19:36 +0800 Subject: [PATCH 523/845] Remove unused object I didn't find where to use it. Change-Id: I10cbac0b1893650dbb00ed7a87e6f7b6361fe051 --- neutronclient/tests/unit/test_http.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/neutronclient/tests/unit/test_http.py b/neutronclient/tests/unit/test_http.py index 2f65b8ed0..ab4126de5 100644 --- a/neutronclient/tests/unit/test_http.py +++ b/neutronclient/tests/unit/test_http.py @@ -37,7 +37,7 @@ def setUp(self): super(TestHTTPClientMixin, self).setUp() self.requests = self.useFixture(mock_fixture.Fixture()) - self.clazz, self.http = self.initialize() + self.http = self.initialize() @abc.abstractmethod def initialize(self): @@ -76,8 +76,7 @@ def test_headers_defined_in_headers(self): class TestHTTPClient(TestHTTPClientMixin, testtools.TestCase): def initialize(self): - return [client.HTTPClient, - client.HTTPClient(token=AUTH_TOKEN, endpoint_url=END_URL)] + return client.HTTPClient(token=AUTH_TOKEN, endpoint_url=END_URL) def test_request_error(self): def cb(*args, **kwargs): From 3a64a7a166be25d40436fd40c8351a79267bd3c4 Mon Sep 17 00:00:00 2001 From: Armando Migliaccio Date: Fri, 4 Nov 2016 11:47:00 -0700 Subject: [PATCH 524/845] Deprecate neutron CLI It is time to signal that we're fully committed to delivering a pure OSC experience. Change-Id: I2d1e73af2391893a7f136a53cbfcaecec4a18efa --- neutronclient/shell.py | 2 ++ releasenotes/notes/deprecate-cli-7be1123817969439.yaml | 8 ++++++++ 2 files changed, 10 insertions(+) create mode 100644 releasenotes/notes/deprecate-cli-7be1123817969439.yaml diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 00a76ec07..63d1cd8a7 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -986,6 +986,8 @@ def configure_logging(self): def main(argv=sys.argv[1:]): try: + print(_("neutron CLI is deprecated and will be removed " + "in the future. Use openstack CLI instead."), file=sys.stderr) return NeutronShell(NEUTRON_API_VERSION).run( list(map(encodeutils.safe_decode, argv))) except KeyboardInterrupt: diff --git a/releasenotes/notes/deprecate-cli-7be1123817969439.yaml b/releasenotes/notes/deprecate-cli-7be1123817969439.yaml new file mode 100644 index 000000000..4833ddda6 --- /dev/null +++ b/releasenotes/notes/deprecate-cli-7be1123817969439.yaml @@ -0,0 +1,8 @@ +--- +deprecations: + - The neutron CLI is now deprecated. This is the signal that it is + time to start using the openstack CLI. No new features will be + added to the neutron CLI, though fixes to the CLI will be assessed + on a case by case basis. Fixes to the API bindings, as well as + development of new API bindings as well as OSC plugins are exempt + from this deprecation. From 1206e512bbbba1820cdda834bcd8bc85d5b30f57 Mon Sep 17 00:00:00 2001 From: Richard Theis Date: Tue, 18 Oct 2016 13:38:26 -0500 Subject: [PATCH 525/845] Devref: Transition to OSC update Devref update for the transition to OSC. Change-Id: Iff65be64d7225565ba33aebc8f9949c6d4ea509e Partial-Bug: 1521291 --- doc/source/devref/transition_to_osc.rst | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/doc/source/devref/transition_to_osc.rst b/doc/source/devref/transition_to_osc.rst index 29c0ee187..d41092150 100644 --- a/doc/source/devref/transition_to_osc.rst +++ b/doc/source/devref/transition_to_osc.rst @@ -70,6 +70,7 @@ Transition Steps 4. **In Progress:** OpenStack Python SDK releases version 1.0 to guarantee backwards compatibility of its networking support and OSC updates its dependencies to include OpenStack Python SDK version 1.0 or later. + See the following blueprint: https://blueprints.launchpad.net/python-openstackclient/+spec/network-command-sdk-support 5. **Done:** OSC switches its networking support for the `ip floating `_, @@ -97,12 +98,15 @@ Transition Steps should start their transition to the OSC plugin system. See the developer guide section below for more information on this step. -7. **Not Started:** Deprecate the ``neutron`` CLI once the criteria below have - been meet. Running the CLI after it has been deprecated will issue a warning - messages such as the following: - ``DeprecationWarning: The neutron CLI is deprecated in favor of python-openstackclient.`` - In addition, only security fixes will be made to the CLI after it has been - deprecated. +7. **In Progress:** Deprecate the ``neutron`` CLI. Running the CLI after + it has been `deprecated `_ + will issue a warning message: + ``neutron CLI is deprecated and will be removed in the future. Use openstack CLI instead.`` + In addition, no new features will be added to the CLI, though fixes to + the CLI will be assessed on a case by case basis. + +8. **Not Started:** Remove the ``neutron`` CLI after two deprecation cycles + once the criteria below have been met. * The networking support provide by the ``openstack`` CLI is functionally equivalent to the ``neutron`` CLI and it contains sufficient functional @@ -116,13 +120,11 @@ Transition Steps to the OSC plugin system and use the ``openstack`` CLI instead of the ``neutron`` CLI. -8. **Not Started:** Remove the ``neutron`` CLI after two deprecation cycles. - Developer Guide --------------- -The ``neutron`` CLI version 4.x, without extensions, supports over 200 -commands while the ``openstack`` CLI version 2.6.0 supports over 50 -networking commands. Of the 50 commands, some do not have all of the options +The ``neutron`` CLI version 6.x, without extensions, supports over 200 +commands while the ``openstack`` CLI version 3.3.0 supports over 70 +networking commands. Of the 70 commands, some do not have all of the options or arguments of their ``neutron`` CLI equivalent. With this large functional gap, a few critical questions for developers during this transition are "Which CLI do I change?", "Where does my CLI belong?", and "Which Python library do I change?" From 42d995bf9d23c90300e88292fb9eca1dd4aa5dc0 Mon Sep 17 00:00:00 2001 From: Dongcan Ye Date: Tue, 8 Nov 2016 10:04:18 +0800 Subject: [PATCH 526/845] [VPNaaS] Add support for sha2-384 and sha2-512 Commit I87b257ee6500c424fc273955a6d89d972a2823e9 had supported sha2-384 and sha2-512 in VPNaaS side, this patch add those support in CLI side. Change-Id: Ie1842acd830e9b8c624cf099491ed5ff46304b8d Closes-Bug: #1639990 --- neutronclient/neutron/v2_0/vpn/ikepolicy.py | 1 + neutronclient/neutron/v2_0/vpn/ipsecpolicy.py | 1 + neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py | 10 ++++++++++ neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py | 10 ++++++++++ 4 files changed, 22 insertions(+) diff --git a/neutronclient/neutron/v2_0/vpn/ikepolicy.py b/neutronclient/neutron/v2_0/vpn/ikepolicy.py index 1aa8a5af3..20a96aaea 100644 --- a/neutronclient/neutron/v2_0/vpn/ikepolicy.py +++ b/neutronclient/neutron/v2_0/vpn/ikepolicy.py @@ -30,6 +30,7 @@ def add_common_args(parser, is_create=True): '--auth-algorithm', type=utils.convert_to_lowercase, default='sha1' if is_create else argparse.SUPPRESS, + choices=['sha1', 'sha256', 'sha384', 'sha512'], help=_('Authentication algorithm, default:sha1.')) parser.add_argument( '--encryption-algorithm', diff --git a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py index 016ebbd19..ea70155b7 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py +++ b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py @@ -27,6 +27,7 @@ def add_common_args(parser, is_create=True): '--auth-algorithm', default='sha1' if is_create else argparse.SUPPRESS, type=utils.convert_to_lowercase, + choices=['sha1', 'sha256', 'sha384', 'sha512'], help=_('Authentication algorithm for IPsec policy, default:sha1.')) parser.add_argument( '--description', diff --git a/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py b/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py index bfdd429e0..33e326cab 100644 --- a/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py +++ b/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py @@ -86,6 +86,16 @@ def test_create_ikepolicy_all_params(self): def test_create_ikepolicy_auth_sha256(self): self._test_create_ikepolicy_all_params(auth='sha256') + def test_create_ikepolicy_auth_sha384(self): + self._test_create_ikepolicy_all_params(auth='sha384') + + def test_create_ikepolicy_auth_sha512(self): + self._test_create_ikepolicy_all_params(auth='sha512') + + def test_create_ikepolicy_invalid_auth(self): + self._test_create_ikepolicy_all_params(auth='invalid', + expected_exc=SystemExit) + def test_create_ikepolicy_with_limited_params(self): # vpn-ikepolicy-create with limited params. resource = 'ikepolicy' diff --git a/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py b/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py index a24f7e076..8c7df4a6f 100644 --- a/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py +++ b/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py @@ -85,6 +85,16 @@ def test_create_ipsecpolicy_all_params(self): def test_create_ipsecpolicy_auth_sha256(self): self._test_create_ipsecpolicy_all_params(auth='sha256') + def test_create_ipsecpolicy_auth_sha384(self): + self._test_create_ipsecpolicy_all_params(auth='sha384') + + def test_create_ipsecpolicy_auth_sha512(self): + self._test_create_ipsecpolicy_all_params(auth='sha512') + + def test_create_ipsecpolicy_invalid_auth(self): + self._test_create_ipsecpolicy_all_params(auth='invalid', + expected_exc=SystemExit) + def test_create_ipsecpolicy_with_limited_params(self): # vpn-ipsecpolicy-create with limited params. resource = 'ipsecpolicy' From cc1d3fdd3582b0bbc2e24b65b9c690b5f0318148 Mon Sep 17 00:00:00 2001 From: Rodion Tikunov Date: Tue, 8 Nov 2016 17:34:22 +0300 Subject: [PATCH 527/845] Added --enable-snat option for router-gateway-set If enable_snat_by_default option set to false and disable snat via cli it becomes unavailable to enable snat again. This commit allows to enable snat after disabling it. Change-Id: I01009d5cd5edd5be3eead615c37d6aa2e3224442 Closes-Bug: #1598171 --- neutronclient/neutron/v2_0/router.py | 5 +++++ neutronclient/tests/unit/test_cli20_router.py | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index b431daaea..40584f20f 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -234,6 +234,9 @@ def get_parser(self, prog_name): parser.add_argument( 'external_network', metavar='EXTERNAL-NETWORK', help=_('ID or name of the external network for the gateway.')) + parser.add_argument( + '--enable-snat', action='store_true', + help=_('Enable source NAT on the router gateway.')) parser.add_argument( '--disable-snat', action='store_true', help=_('Disable source NAT on the router gateway.')) @@ -256,6 +259,8 @@ def take_action(self, parsed_args): _ext_net_id = neutronV20.find_resourceid_by_name_or_id( neutron_client, 'network', parsed_args.external_network) router_dict = {'network_id': _ext_net_id} + if parsed_args.enable_snat: + router_dict['enable_snat'] = True if parsed_args.disable_snat: router_dict['enable_snat'] = False if parsed_args.fixed_ip: diff --git a/neutronclient/tests/unit/test_cli20_router.py b/neutronclient/tests/unit/test_cli20_router.py index 1ca818de0..02a762ca3 100644 --- a/neutronclient/tests/unit/test_cli20_router.py +++ b/neutronclient/tests/unit/test_cli20_router.py @@ -356,6 +356,18 @@ def test_set_gateway(self): {"network_id": "externalid"}} ) + def test_set_gateway_enable_snat(self): + # enable external gateway for router: myid externalid. + resource = 'router' + cmd = router.SetGatewayRouter(test_cli20.MyApp(sys.stdout), None) + args = ['myid', 'externalid', '--enable-snat'] + self._test_update_resource(resource, cmd, 'myid', + args, + {"external_gateway_info": + {"network_id": "externalid", + "enable_snat": True}} + ) + def test_set_gateway_disable_snat(self): # set external gateway for router: myid externalid. resource = 'router' From 6a3a7c681659a96240be740f377078595828835f Mon Sep 17 00:00:00 2001 From: "Darek Smigiel (dasm)" Date: Mon, 28 Mar 2016 21:58:48 +0000 Subject: [PATCH 528/845] Refactor of Network tests Simplified and removed common parts of tests. Change-Id: I249dac1c6ae2719979e631de026b12fac7dbf1a2 --- .../tests/unit/test_cli20_network.py | 171 ++++++++---------- 1 file changed, 73 insertions(+), 98 deletions(-) diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index f4464c33d..8333b753c 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -25,74 +25,57 @@ from neutronclient.tests.unit import test_cli20 -class CLITestV20NetworkJSON(test_cli20.CLITestV20Base): +class CLITestV20CreateNetworkJSON(test_cli20.CLITestV20Base): def setUp(self): - super(CLITestV20NetworkJSON, self).setUp(plurals={'tags': 'tag'}) + super(CLITestV20CreateNetworkJSON, self).setUp(plurals={'tags': 'tag'}) - def test_create_network(self): - # Create net: myname. - resource = 'network' + def _test_create_network(self, **kwargs): cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - args = [name, ] - position_names = ['name', ] - position_values = [name, ] + resource = kwargs.pop('resource', 'network') + + name = kwargs.pop('name', 'myname') + myid = kwargs.pop('myid', 'myid') + args = kwargs.pop('args', [name, ]) + position_names = kwargs.pop('position_names', ['name', ]) + position_values = kwargs.pop('position_values', [name, ]) + self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) + position_names, position_values, + **kwargs) + + def test_create_network(self): + # Create net: myname. + self._test_create_network() def test_create_network_with_unicode(self): # Create net: u'\u7f51\u7edc'. - resource = 'network' - cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) - name = u'\u7f51\u7edc' - myid = 'myid' - args = [name, ] - position_names = ['name', ] - position_values = [name, ] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) + self._test_create_network(name=u'\u7f51\u7edc') def test_create_network_description(self): # Create net: --tenant_id tenantid myname. - resource = 'network' - cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) name = 'myname' - myid = 'myid' args = ['--description', 'Nice network', name] - position_names = ['name', ] - position_values = [name, ] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - description='Nice network') + self._test_create_network(name=name, + args=args, + description='Nice network') - def test_create_network_tenant(self): + def test_create_network_tenant_underscore(self): # Create net: --tenant_id tenantid myname. - resource = 'network' - cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) name = 'myname' - myid = 'myid' args = ['--tenant_id', 'tenantid', name] - position_names = ['name', ] - position_values = [name, ] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') + self._test_create_network(name=name, args=args, tenant_id="tenantid") + def test_create_network_tenant_dash(self): # Test dashed options + # Create net: --tenant_id tenantid myname. + name = 'myname' args = ['--tenant-id', 'tenantid', name] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') + self._test_create_network(name=name, args=args, tenant_id="tenantid") def test_create_network_provider_args(self): # Create net: with --provider arguments. - resource = 'network' - cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - # Test --provider attributes before network name + name = 'myname' args = ['--provider:network_type', 'vlan', '--provider:physical_network', 'physnet1', '--provider:segmentation_id', '400', name] @@ -100,94 +83,78 @@ def test_create_network_provider_args(self): 'provider:physical_network', 'provider:segmentation_id', 'name'] position_values = ['vlan', 'physnet1', '400', name] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) + self._test_create_network(name=name, + args=args, + position_names=position_names, + position_values=position_values) def test_create_network_tags(self): # Create net: myname --tags a b. - resource = 'network' - cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) name = 'myname' - myid = 'myid' args = [name, '--tags', 'a', 'b'] - position_names = ['name', ] - position_values = [name, ] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tags=['a', 'b']) + self._test_create_network(name=name, args=args, tags=['a', 'b']) - def test_create_network_state(self): + def test_create_network_state_underscore(self): # Create net: --admin_state_down myname. - resource = 'network' - cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) name = 'myname' - myid = 'myid' args = ['--admin_state_down', name, ] - position_names = ['name', ] - position_values = [name, ] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - admin_state_up=False) + self._test_create_network(name=name, args=args, admin_state_up=False) + def test_create_network_state_dash(self): # Test dashed options + name = 'myname' args = ['--admin-state-down', name, ] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - admin_state_up=False) + self._test_create_network(name=name, args=args, admin_state_up=False) def test_create_network_vlan_transparent(self): # Create net: myname --vlan-transparent True. - resource = 'network' - cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) name = 'myname' - myid = 'myid' args = ['--vlan-transparent', 'True', name] - vlantrans = {'vlan_transparent': 'True'} - position_names = ['name', ] - position_values = [name, ] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - **vlantrans) + self._test_create_network(name=name, + args=args, + vlan_transparent='True') def test_create_network_with_qos_policy(self): # Create net: --qos-policy mypolicy. - resource = 'network' - cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) name = 'myname' - myid = 'myid' qos_policy_name = 'mypolicy' args = [name, '--qos-policy', qos_policy_name] position_names = ['name', 'qos_policy_id'] position_values = [name, qos_policy_name] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) + self._test_create_network(name=name, + args=args, + position_names=position_names, + position_values=position_values) def test_create_network_with_az_hint(self): # Create net: --availability-zone-hint zone1 # --availability-zone-hint zone2. - resource = 'network' - cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) name = 'myname' - myid = 'myid' args = ['--availability-zone-hint', 'zone1', '--availability-zone-hint', 'zone2', name] position_names = ['availability_zone_hints', 'name'] position_values = [['zone1', 'zone2'], name] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) + self._test_create_network(name=name, + args=args, + position_names=position_names, + position_values=position_values) def test_create_network_with_dns_domain(self): # Create net: --dns-domain my-domain.org. - resource = 'network' - cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) name = 'myname' - myid = 'myid' dns_domain_name = 'my-domain.org.' args = [name, '--dns-domain', dns_domain_name] position_names = ['name', 'dns_domain'] position_values = [name, dns_domain_name] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) + self._test_create_network(name=name, + args=args, + position_names=position_names, + position_values=position_values) + + +class CLITestV20ListNetworkJSON(test_cli20.CLITestV20Base): + def setUp(self): + super(CLITestV20ListNetworkJSON, self).setUp(plurals={'tags': 'tag'}) def test_list_nets_empty_with_column(self): resources = "networks" @@ -501,7 +468,7 @@ def test_list_external_nets_detail_tags(self): self._test_list_external_nets(resources, cmd, detail=True, tags=['a', 'b']) - def test_list_externel_nets_fields(self): + def test_list_external_nets_fields(self): # List external nets: --fields a --fields b -- --fields c d. resources = "networks" cmd = network.ListExternalNetwork(test_cli20.MyApp(sys.stdout), None) @@ -509,6 +476,14 @@ def test_list_externel_nets_fields(self): fields_1=['a', 'b'], fields_2=['c', 'd']) + def test_list_shared_networks(self): + # list nets : --shared False + cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) + self._test_list_networks(cmd, base_args='--shared False'.split(), + query='shared=False') + + +class CLITestV20UpdateNetworkJSON(test_cli20.CLITestV20Base): def test_update_network_exception(self): # Update net: myid. resource = 'network' @@ -571,6 +546,8 @@ def test_update_network_with_no_dns_domain(self): ['myid', '--no-dns-domain'], {'dns_domain': "", }) + +class CLITestV20ShowNetworkJSON(test_cli20.CLITestV20Base): def test_show_network(self): # Show net: --fields id --fields name myid. resource = 'network' @@ -579,6 +556,8 @@ def test_show_network(self): self._test_show_resource(resource, cmd, self.test_id, args, ['id', 'name']) + +class CLITestV20DeleteNetworkJSON(test_cli20.CLITestV20Base): def test_delete_network(self): # Delete net: myid. resource = 'network' @@ -608,6 +587,8 @@ def test_bulk_delete_network_fail(self): resource, cmd, myid1, args, extra_id=myid2, delete_fail=True) + +class CLITestV20ExtendListNetworkJSON(test_cli20.CLITestV20Base): def _test_extend_list(self, mox_calls): data = [{'id': 'netid%d' % i, 'name': 'net%d' % i, 'subnets': ['mysubid%d' % i]} @@ -677,9 +658,3 @@ def mox_calls(path, data): 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(response) self._test_extend_list(mox_calls) - - def test_list_shared_networks(self): - # list nets : --shared False - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_list_networks(cmd, base_args='--shared False'.split(), - query='shared=False') From e9e31d9766f14a453f261dbb9aaded979f6d2911 Mon Sep 17 00:00:00 2001 From: gecong1973 Date: Mon, 21 Nov 2016 11:49:33 +0800 Subject: [PATCH 529/845] Fix a typo TrivialFix Change-Id: Ia95e55b712a06648ea19dce30b01a568d7b3edde --- neutronclient/osc/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/osc/plugin.py b/neutronclient/osc/plugin.py index 1efb6c5f7..2939fe1ac 100644 --- a/neutronclient/osc/plugin.py +++ b/neutronclient/osc/plugin.py @@ -15,7 +15,7 @@ # TODO(rtheis/amotoki): Add functional test infrastructure for OSC # plugin commands. -# TODO(amotoki): Add and update document on OSC pluign. +# TODO(amotoki): Add and update document on OSC plugin. from osc_lib import utils From d5516641c18407b281a706f036223769cd8dd01e Mon Sep 17 00:00:00 2001 From: Flavio Percoco Date: Thu, 24 Nov 2016 23:53:09 +0100 Subject: [PATCH 530/845] Show team and repo badges on README This patch adds the team's and repository's badges to the README file. The motivation behind this is to communicate the project status and features at first glance. For more information about this effort, please read this email thread: http://lists.openstack.org/pipermail/openstack-dev/2016-October/105562.html To see an example of how this would look like check: https://gist.github.com/9c58a860190b7421066893b12675b496 Change-Id: Ie6bf3f8a7addf0b070a6447d500b5b61144c3664 --- README.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.rst b/README.rst index cceea78f5..0433add84 100644 --- a/README.rst +++ b/README.rst @@ -1,3 +1,12 @@ +======================== +Team and repository tags +======================== + +.. image:: http://governance.openstack.org/badges/python-neutronclient.svg + :target: http://governance.openstack.org/reference/tags/index.html + +.. Change things from this point on + Python bindings to the Neutron API ================================== From a0f15a84d7f6bc1d21fa14c0a1cdbbc5ce683ae6 Mon Sep 17 00:00:00 2001 From: Armando Migliaccio Date: Tue, 29 Nov 2016 11:24:08 -0800 Subject: [PATCH 531/845] Disable VPNaaS functional tests in the neutronclient gate This addresses failures for neutronclient's functional job of advanced services. VPNaaS is no longer maintained and thus it makes sense to stop gating against it. Hook and tests are kept for local development and in case things will resurrect back to life. This patch also makes extension functional tests conditional to the presence of the extension under test. Closes-bug: #1645819 Change-Id: I17ab8accaa943b9ac04893b5e131be0afe0f5429 --- .../functional/adv-svcs/test_readonly_neutron_fwaas.py | 7 ++++++- .../tests/functional/adv-svcs/test_readonly_neutron_vpn.py | 4 ++++ neutronclient/tests/functional/base.py | 7 +++++++ neutronclient/tests/functional/hooks/gate_hook.sh | 1 - 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/neutronclient/tests/functional/adv-svcs/test_readonly_neutron_fwaas.py b/neutronclient/tests/functional/adv-svcs/test_readonly_neutron_fwaas.py index a3e2573f4..9cec62571 100644 --- a/neutronclient/tests/functional/adv-svcs/test_readonly_neutron_fwaas.py +++ b/neutronclient/tests/functional/adv-svcs/test_readonly_neutron_fwaas.py @@ -13,10 +13,15 @@ from neutronclient.tests.functional import base -class SimpleReadOnlyNeutronClientTest(base.ClientTestBase): +class SimpleReadOnlyNeutronFwv1ClientTest(base.ClientTestBase): """Tests for FWaaS v1 based client commands that are read only""" + def setUp(self): + super(SimpleReadOnlyNeutronFwv1ClientTest, self).setUp() + if not self.is_extension_enabled('fwaas'): + self.skipTest('FWaaS is not enabled') + def test_neutron_firewall_list(self): firewall_list = self.parser.listing(self.neutron ('firewall-list')) diff --git a/neutronclient/tests/functional/adv-svcs/test_readonly_neutron_vpn.py b/neutronclient/tests/functional/adv-svcs/test_readonly_neutron_vpn.py index 291e59ad7..6e3b6cdcb 100644 --- a/neutronclient/tests/functional/adv-svcs/test_readonly_neutron_vpn.py +++ b/neutronclient/tests/functional/adv-svcs/test_readonly_neutron_vpn.py @@ -25,6 +25,10 @@ class SimpleReadOnlyNeutronVpnClientTest(base.ClientTestBase): * with and without optional parameters * initially just check return codes, and later test command outputs """ + def setUp(self): + super(SimpleReadOnlyNeutronVpnClientTest, self).setUp() + if not self.is_extension_enabled('vpnaas'): + self.skipTest('VPNaaS is not enabled') def test_neutron_vpn_ikepolicy_list(self): ikepolicy = self.parser.listing(self.neutron('vpn-ikepolicy-list')) diff --git a/neutronclient/tests/functional/base.py b/neutronclient/tests/functional/base.py index b0c672f89..8233b1fde 100644 --- a/neutronclient/tests/functional/base.py +++ b/neutronclient/tests/functional/base.py @@ -72,3 +72,10 @@ def neutron_non_admin(self, *args, **kwargs): self._non_admin_clients = self._get_clients_from_os_cloud_config( cloud='devstack') return self._non_admin_clients.neutron(*args, **kwargs) + + def is_extension_enabled(self, extension_alias): + extensions = self.parser.listing(self.neutron('ext-list')) + for extension in extensions: + if extension_alias in extension['alias']: + return True + return False diff --git a/neutronclient/tests/functional/hooks/gate_hook.sh b/neutronclient/tests/functional/hooks/gate_hook.sh index e0dd841dd..e34a6c802 100755 --- a/neutronclient/tests/functional/hooks/gate_hook.sh +++ b/neutronclient/tests/functional/hooks/gate_hook.sh @@ -22,7 +22,6 @@ ${config} if [ "$VENV" == "functional-adv-svcs" ] then load_rc_hook fwaas - load_rc_hook vpnaas fi $BASE/new/devstack-gate/devstack-vm-gate.sh From a686743940215e2c84b98a2323ca97743647a7cc Mon Sep 17 00:00:00 2001 From: Armando Migliaccio Date: Mon, 5 Dec 2016 17:36:51 -0800 Subject: [PATCH 532/845] Clarify how to do client-side development for repos in neutron governance Change-Id: I90085b47b2e960fbd75e936fb3ee5c0553b0a8e2 --- doc/source/devref/transition_to_osc.rst | 37 +++++++++++++------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/doc/source/devref/transition_to_osc.rst b/doc/source/devref/transition_to_osc.rst index d41092150..2f0e52476 100644 --- a/doc/source/devref/transition_to_osc.rst +++ b/doc/source/devref/transition_to_osc.rst @@ -151,33 +151,34 @@ dual maintenance. **Where does my CLI belong?** +If you are developing an API in any of the `neutron repos `_ +the client-side support must be generally located in either the openstackclient or neutronclient +repos. Whether the actual code goes into one or the other repo it depends on the nature of the +feature, its maturity level, and/or the depth of feedback required during the development. + +The table below provides an idea of what goes where. Generally speaking, we consider Core anything +that is L2 and L3 related or that it has been located in the neutron repo for quite sometime, e.g. +QoS or Metering, or that it is available in each neutron deployment irrespective of its configuration +(e.g. auto-allocated-topology). Any client feature that falls into this categorization will need to +be contributed in OSC. Any other that does not, will need to go into neutronclient, assuming that +its server-side is located in a neutron controlled repo. This is a general guideline, when in doubt, +please reach out to a member of the neutron core team for clarifications. + +---------------------------+-------------------+-------------------------------------------------+ | Networking Commands | OSC Plugin | OpenStack Project for ``openstack`` Commands | +===========================+===================+=================================================+ | Core | No | python-openstackclient | +---------------------------+-------------------+-------------------------------------------------+ -| Advanced Feature | Yes | python-neutronclient | -| (neutron repository) | | (``neutronclient/osc/v2/``) | -+---------------------------+-------------------+-------------------------------------------------+ -| Dynamic Routing | Yes | python-neutronclient | -| | | (``neutronclient/osc/v2/dynamic_routing``) | -+---------------------------+-------------------+-------------------------------------------------+ -| FWaaS v1 | N/A | None (deprecated) | -+---------------------------+-------------------+-------------------------------------------------+ -| FWaaS v2 | Yes | python-neutronclient | -| | | (``neutronclient/osc/v2/fwaas``) | -+---------------------------+-------------------+-------------------------------------------------+ -| LBaaS v1 | N/A | None (deprecated) | -+---------------------------+-------------------+-------------------------------------------------+ -| LBaaS v2 | Yes | python-neutronclient | -| | | (``neutronclient/osc/v2/lbaas``) | -+---------------------------+-------------------+-------------------------------------------------+ -| VPNaaS | Yes | python-neutronclient | -| | | (``neutronclient/osc/v2/vpnaas``) | +| Extension | Yes | python-neutronclient | +| (i.e. neutron stadium) | | (``neutronclient/osc/v2/``) | +---------------------------+-------------------+-------------------------------------------------+ | Other | Yes | Applicable project owning networking resource | +---------------------------+-------------------+-------------------------------------------------+ +When a repo stops being under neutron governance, its client-side counterpart will have to go through +deprecation. Bear in mind that for grandfathered extensions like FWaaS v1, VPNaaS, and LBaaS v1, this +is not required as the neutronclient is already deprecated on its own. + **Which Python library do I change?** +-------------------------------------------------+-----------------------------------------------+ From 0b7ee51ae85a71364a2677cbf6aaf66e4e90cea9 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Thu, 28 Jul 2016 00:35:47 +0000 Subject: [PATCH 533/845] Add common utilities for OSC plugin implementation This patch introduces two utility modules. * osc/utils.py for OSC plugin general stuff which are planned to move to osc-lib potentially * osc/v2/utils.py for Networking v2.0 API related methods Co-Authored-By: Yushiro FURUKAWA Change-Id: Ia36687843c56ebde0d3705ef11435ec7f5cff9d8 --- neutronclient/osc/utils.py | 195 ++++++++++++++++++ neutronclient/osc/v2/trunk/network_trunk.py | 36 ++-- neutronclient/osc/v2/utils.py | 21 ++ neutronclient/tests/unit/osc/test_utils.py | 60 ++++++ .../unit/osc/v2/trunk/test_network_trunk.py | 11 +- requirements.txt | 3 + 6 files changed, 298 insertions(+), 28 deletions(-) create mode 100644 neutronclient/osc/utils.py create mode 100644 neutronclient/osc/v2/utils.py create mode 100644 neutronclient/tests/unit/osc/test_utils.py diff --git a/neutronclient/osc/utils.py b/neutronclient/osc/utils.py new file mode 100644 index 000000000..c9aecc1e3 --- /dev/null +++ b/neutronclient/osc/utils.py @@ -0,0 +1,195 @@ +# Copyright 2016 NEC Corporation +# +# 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. + +"""This module should contain OSC plugin generic methods. + +Methods in this module are candidates adopted to osc-lib. + +Stuffs specific to neutronclient OSC plugin should not be added +to this module. They should go to neutronclient.osc.v2.utils. +""" + +import operator + +from keystoneclient import exceptions as identity_exc +from keystoneclient.v3 import domains +from keystoneclient.v3 import projects +from osc_lib import utils + +from neutronclient._i18n import _ + + +LIST_BOTH = 'both' +LIST_SHORT_ONLY = 'short_only' +LIST_LONG_ONLY = 'long_only' + + +def get_column_definitions(attr_map, long_listing): + """Return table headers and column names for a listing table. + + :param attr_map: a list of table entry definitions. + Each entry should be a tuple consisting of + (API attribute name, header name, listing mode). For example: + (('id', 'ID', LIST_BOTH), + ('name', 'Name', LIST_BOTH), + ('tenant_id', 'Project', LIST_LONG_ONLY)) + The third field of each tuple must be one of LIST_BOTH, + LIST_LONG_ONLY (a corresponding column is shown only in a long mode), or + LIST_SHORT_ONLY (a corresponding column is shown only in a short mode). + :param long_listing: A boolean value which indicates a long listing + or not. In most cases, parsed_args.long is passed to this argument. + :return: A tuple of a list of table headers and a list of column names. + """ + + if long_listing: + headers = [hdr for col, hdr, listing_mode in attr_map + if listing_mode in (LIST_BOTH, LIST_LONG_ONLY)] + columns = [col for col, hdr, listing_mode in attr_map + if listing_mode in (LIST_BOTH, LIST_LONG_ONLY)] + else: + headers = [hdr for col, hdr, listing_mode in attr_map if listing_mode + if listing_mode in (LIST_BOTH, LIST_SHORT_ONLY)] + columns = [col for col, hdr, listing_mode in attr_map if listing_mode + if listing_mode in (LIST_BOTH, LIST_SHORT_ONLY)] + + return headers, columns + + +def get_columns(item, attr_map=None): + """Return pair of resource attributes and corresponding display names. + + Assume the following item and attr_map are passed. + item: {'id': 'myid', 'name': 'myname', + 'foo': 'bar', 'tenant_id': 'mytenan'} + attr_map: + (('id', 'ID', LIST_BOTH), + ('name', 'Name', LIST_BOTH), + ('tenant_id', 'Project', LIST_LONG_ONLY)) + + This method returns: + + (('id', 'name', 'tenant_id', 'foo'), # attributes + ('ID', 'Name', 'Project', 'foo') # display names + + Both tuples of attributes and display names are sorted by display names + in the alphabetical order. + Attributes not found in a given attr_map are kept as-is. + + :param item: a dictionary which represents a resource. + Keys of the dictionary are expected to be attributes of the resource. + Values are not referred to by this method. + :param attr_map: a list of mapping from attribute to display name. + The same format is used as for get_column_definitions attr_map. + :return: A pair of tuple of attributes and tuple of display names. + """ + attr_map = attr_map or tuple([]) + _attr_map_dict = dict((col, hdr) for col, hdr, listing_mode in attr_map) + + columns = [(column, _attr_map_dict.get(column, column)) + for column in item.keys()] + columns = sorted(columns, key=operator.itemgetter(1)) + return (tuple(col[0] for col in columns), + tuple(col[1] for col in columns)) + + +# TODO(amotoki): Use osc-lib version once osc-lib provides this. +def add_project_owner_option_to_parser(parser): + """Register project and project domain options. + + :param parser: argparse.Argument parser object. + + """ + parser.add_argument( + '--project', + metavar='', + help=_("Owner's project (name or ID)") + ) + # Borrowed from openstackclient.identity.common + # as it is not exposed officially. + parser.add_argument( + '--project-domain', + metavar='', + help=_('Domain the project belongs to (name or ID). ' + 'This can be used in case collisions between project names ' + 'exist.'), + ) + + +# The following methods are borrowed from openstackclient.identity.common +# as it is not exposed officially. +# TODO(amotoki): Use osc-lib version once osc-lib provides this. + + +def find_domain(identity_client, name_or_id): + return _find_identity_resource(identity_client.domains, name_or_id, + domains.Domain) + + +def find_project(identity_client, name_or_id, domain_name_or_id=None): + domain_id = _get_domain_id_if_requested(identity_client, domain_name_or_id) + if not domain_id: + return _find_identity_resource(identity_client.projects, name_or_id, + projects.Project) + else: + return _find_identity_resource(identity_client.projects, name_or_id, + projects.Project, domain_id=domain_id) + + +def _get_domain_id_if_requested(identity_client, domain_name_or_id): + if not domain_name_or_id: + return None + domain = find_domain(identity_client, domain_name_or_id) + return domain.id + + +def _find_identity_resource(identity_client_manager, name_or_id, + resource_type, **kwargs): + """Find a specific identity resource. + + Using keystoneclient's manager, attempt to find a specific resource by its + name or ID. If Forbidden to find the resource (a common case if the user + does not have permission), then return the resource by creating a local + instance of keystoneclient's Resource. + + The parameter identity_client_manager is a keystoneclient manager, + for example: keystoneclient.v3.users or keystoneclient.v3.projects. + + The parameter resource_type is a keystoneclient resource, for example: + keystoneclient.v3.users.User or keystoneclient.v3.projects.Project. + + :param identity_client_manager: the manager that contains the resource + :type identity_client_manager: `keystoneclient.base.CrudManager` + :param name_or_id: the resources's name or ID + :type name_or_id: string + :param resource_type: class that represents the resource type + :type resource_type: `keystoneclient.base.Resource` + + :returns: the resource in question + :rtype: `keystoneclient.base.Resource` + + """ + + try: + identity_resource = utils.find_resource(identity_client_manager, + name_or_id, **kwargs) + if identity_resource is not None: + return identity_resource + except identity_exc.Forbidden: + pass + + return resource_type(None, {'id': name_or_id, 'name': name_or_id}) + + +# The above are borrowed from openstackclient.identity.common. +# DO NOT ADD original methods in neutronclient repo to the above area. diff --git a/neutronclient/osc/v2/trunk/network_trunk.py b/neutronclient/osc/v2/trunk/network_trunk.py index 70f791fd8..acd6208db 100644 --- a/neutronclient/osc/v2/trunk/network_trunk.py +++ b/neutronclient/osc/v2/trunk/network_trunk.py @@ -20,12 +20,11 @@ from osc_lib.cli import parseractions from osc_lib.command import command from osc_lib import exceptions -from osc_lib import utils - -# TODO(abhiraut): Switch to neutronclients identity utils -from openstackclient.identity import common as identity_common +from osc_lib import utils as osc_utils from neutronclient._i18n import _ +from neutronclient.osc import utils as nc_osc_utils +from neutronclient.osc.v2 import utils as v2_utils LOG = logging.getLogger(__name__) @@ -75,12 +74,7 @@ def get_parser(self, prog_name): action='store_true', help=_("Disable trunk") ) - parser.add_argument( - '--project', - metavar='', - help=_("Owner's project (name or ID)") - ) - identity_common.add_project_domain_option_to_parser(parser) + nc_osc_utils.add_project_owner_option_to_parser(parser) return parser def take_action(self, parsed_args): @@ -90,8 +84,8 @@ def take_action(self, parsed_args): body = {TRUNK: attrs} obj = client.create_trunk(body) columns = _get_columns(obj[TRUNK]) - data = utils.get_dict_properties(obj[TRUNK], columns, - formatters=_formatters) + data = osc_utils.get_dict_properties(obj[TRUNK], columns, + formatters=_formatters) return columns, data @@ -169,7 +163,7 @@ def take_action(self, parsed_args): 'updated_at' ) return (headers, - (utils.get_dict_properties( + (osc_utils.get_dict_properties( s, columns, formatters=_formatters, ) for s in data[TRUNKS])) @@ -254,8 +248,8 @@ def take_action(self, parsed_args): trunk_id = _get_id(client, parsed_args.trunk, TRUNK) obj = client.show_trunk(trunk_id) columns = _get_columns(obj[TRUNK]) - data = utils.get_dict_properties(obj[TRUNK], columns, - formatters=_formatters) + data = osc_utils.get_dict_properties(obj[TRUNK], columns, + formatters=_formatters) return columns, data @@ -279,7 +273,7 @@ def take_action(self, parsed_args): headers = ('Port', 'Segmentation Type', 'Segmentation ID') columns = ('port_id', 'segmentation_type', 'segmentation_id') return (headers, - (utils.get_dict_properties( + (osc_utils.get_dict_properties( s, columns, ) for s in data[SUB_PORTS])) @@ -311,13 +305,9 @@ def take_action(self, parsed_args): client.trunk_remove_subports(trunk_id, attrs) -def _format_admin_state(item): - return 'UP' if item else 'DOWN' - - _formatters = { - 'admin_state_up': _format_admin_state, - 'sub_ports': utils.format_list_of_dicts, + 'admin_state_up': v2_utils.format_admin_state, + 'sub_ports': osc_utils.format_list_of_dicts, } @@ -346,7 +336,7 @@ def _get_attrs_for_trunk(client_manager, parsed_args): # "trunk set" command doesn't support setting project. if 'project' in parsed_args and parsed_args.project is not None: identity_client = client_manager.identity - project_id = identity_common.find_project( + project_id = nc_osc_utils.find_project( identity_client, parsed_args.project, parsed_args.project_domain, diff --git a/neutronclient/osc/v2/utils.py b/neutronclient/osc/v2/utils.py new file mode 100644 index 000000000..9e9040179 --- /dev/null +++ b/neutronclient/osc/v2/utils.py @@ -0,0 +1,21 @@ +# Copyright 2016 NEC Corporation +# +# 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. + +"""This module is intended to contain methods specific +to Networking v2 API and its extensions. +""" + + +def format_admin_state(state): + return 'UP' if state else 'DOWN' diff --git a/neutronclient/tests/unit/osc/test_utils.py b/neutronclient/tests/unit/osc/test_utils.py new file mode 100644 index 000000000..c9857beda --- /dev/null +++ b/neutronclient/tests/unit/osc/test_utils.py @@ -0,0 +1,60 @@ +# Copyright 2016 NEC Corporation +# +# 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 testtools + +from neutronclient.osc import utils + + +class TestUtils(testtools.TestCase): + + def test_get_column_definitions(self): + attr_map = ( + ('id', 'ID', utils.LIST_BOTH), + ('tenant_id', 'Project', utils.LIST_LONG_ONLY), + ('name', 'Name', utils.LIST_BOTH), + ('summary', 'Summary', utils.LIST_SHORT_ONLY), + ) + headers, columns = utils.get_column_definitions(attr_map, + long_listing=False) + self.assertEqual(['id', 'name', 'summary'], columns) + self.assertEqual(['ID', 'Name', 'Summary'], headers) + + def test_get_column_definitions_long(self): + attr_map = ( + ('id', 'ID', utils.LIST_BOTH), + ('tenant_id', 'Project', utils.LIST_LONG_ONLY), + ('name', 'Name', utils.LIST_BOTH), + ('summary', 'Summary', utils.LIST_SHORT_ONLY), + ) + headers, columns = utils.get_column_definitions(attr_map, + long_listing=True) + self.assertEqual(['id', 'tenant_id', 'name'], columns) + self.assertEqual(['ID', 'Project', 'Name'], headers) + + def test_get_columns(self): + item = { + 'id': 'test-id', + 'tenant_id': 'test-tenant_id', + # 'name' is not included + 'foo': 'bar', # unknown attribute + } + attr_map = ( + ('id', 'ID', utils.LIST_BOTH), + ('tenant_id', 'Project', utils.LIST_LONG_ONLY), + ('name', 'Name', utils.LIST_BOTH), + ) + columns, display_names = utils.get_columns(item, attr_map) + self.assertEqual(tuple(['id', 'tenant_id', 'foo']), columns) + self.assertEqual(tuple(['ID', 'Project', 'foo']), display_names) diff --git a/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py b/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py index c8ed19ac7..31c446ce1 100644 --- a/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py +++ b/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py @@ -22,6 +22,7 @@ from osc_lib import utils from neutronclient.osc.v2.trunk import network_trunk as trunk +from neutronclient.osc.v2 import utils as v2_utils from neutronclient.tests.unit.osc.v2 import fakes as test_fakes from neutronclient.tests.unit.osc.v2.trunk import fakes @@ -47,7 +48,7 @@ class TestCreateNetworkTrunk(test_fakes.TestNeutronClientOSCV2): def get_data(self): return ( - trunk._format_admin_state(self._trunk['admin_state_up']), + v2_utils.format_admin_state(self._trunk['admin_state_up']), self._trunk['description'], self._trunk['id'], self._trunk['name'], @@ -248,7 +249,7 @@ class TestShowNetworkTrunk(test_fakes.TestNeutronClientOSCV2): 'sub_ports', ) data = ( - trunk._format_admin_state(_trunk['admin_state_up']), + v2_utils.format_admin_state(_trunk['admin_state_up']), _trunk['description'], _trunk['id'], _trunk['name'], @@ -327,7 +328,7 @@ class TestListNetworkTrunk(test_fakes.TestNeutronClientOSCV2): t['port_id'], t['description'], t['status'], - trunk._format_admin_state(t['admin_state_up']), + v2_utils.format_admin_state(t['admin_state_up']), '2001-01-01 00:00:00', '2001-01-01 00:00:00', )) @@ -384,7 +385,7 @@ class TestSetNetworkTrunk(test_fakes.TestNeutronClientOSCV2): 'sub_ports', ) data = ( - trunk._format_admin_state(_trunk['admin_state_up']), + v2_utils.format_admin_state(_trunk['admin_state_up']), _trunk['id'], _trunk['name'], _trunk['description'], @@ -620,7 +621,7 @@ class TestUnsetNetworkTrunk(test_fakes.TestNeutronClientOSCV2): 'sub_ports', ) data = ( - trunk._format_admin_state(_trunk['admin_state_up']), + v2_utils.format_admin_state(_trunk['admin_state_up']), _trunk['id'], _trunk['name'], _trunk['port_id'], diff --git a/requirements.txt b/requirements.txt index d2a806c96..9fdcda393 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,6 +12,9 @@ oslo.serialization>=1.10.0 # Apache-2.0 oslo.utils>=3.17.0 # Apache-2.0 os-client-config>=1.22.0 # Apache-2.0 keystoneauth1>=2.14.0 # Apache-2.0 +# keystoneclient is used only by neutronclient.osc.utils +# TODO(amotoki): Drop this after osc.utils has no dependency on keystoneclient +python-keystoneclient>=3.8.0 # Apache-2.0 requests>=2.10.0 # Apache-2.0 simplejson>=2.2.0 # MIT six>=1.9.0 # MIT From 266e6c3c3c3e9a8b856d99238dc5323edeb66955 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Wed, 9 Dec 2015 06:22:03 +0900 Subject: [PATCH 534/845] Decode exception parameters to expand exception message properly If a multibyte string without unicode prefix 'u' is passed to NeutronException in python 2, string subsitution will fail. In python 2, multibyte string without 'u' prefix is encoded into bytes. As a result a string substitution in NeutronException.__init__ will fail. Change-Id: I1aa08b69fe119087a7a49fda768a24f85f0e7c76 Closes-Bug: #1235228 --- neutronclient/common/exceptions.py | 11 ++++++++++- neutronclient/tests/unit/test_exceptions.py | 12 ++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/neutronclient/common/exceptions.py b/neutronclient/common/exceptions.py index 3de16e7ee..4298d423a 100644 --- a/neutronclient/common/exceptions.py +++ b/neutronclient/common/exceptions.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo_utils import encodeutils import six from neutronclient._i18n import _ @@ -31,6 +32,14 @@ """ +# NOTE: This method is defined here to avoid +# an import loop between common.utils and this module. +def _safe_decode_dict(kwargs): + for k, v in kwargs.items(): + kwargs[k] = encodeutils.safe_decode(v) + return kwargs + + @six.python_2_unicode_compatible class NeutronException(Exception): """Base Neutron Exception. @@ -45,7 +54,7 @@ def __init__(self, message=None, **kwargs): if message: self.message = message try: - self._error_string = self.message % kwargs + self._error_string = self.message % _safe_decode_dict(kwargs) except Exception: # at least get the core message out if something happened self._error_string = self.message diff --git a/neutronclient/tests/unit/test_exceptions.py b/neutronclient/tests/unit/test_exceptions.py index b87be9527..6ef89c057 100644 --- a/neutronclient/tests/unit/test_exceptions.py +++ b/neutronclient/tests/unit/test_exceptions.py @@ -13,6 +13,8 @@ # under the License. import fixtures +from oslo_utils import encodeutils +import six import testtools from neutronclient.common import exceptions @@ -34,3 +36,13 @@ class TestException(exceptions.NeutronException): print(e) self.assertEqual('Exception with %s' % multibyte_unicode_string, fixture.getDetails().get('stdout').as_text()) + + def test_exception_message_with_encoded_unicode(self): + class TestException(exceptions.NeutronException): + message = _('Exception with %(reason)s') + + multibyte_string = u'\uff21\uff22\uff23' + multibyte_binary = encodeutils.safe_encode(multibyte_string) + e = TestException(reason=multibyte_binary) + self.assertEqual('Exception with %s' % multibyte_string, + six.text_type(e)) From 19546d0c6d323ef50666f263e760c40fddf3b1ad Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Fri, 23 Dec 2016 22:18:02 +0000 Subject: [PATCH 535/845] Updated from global requirements Change-Id: I47bedcde68a0aaebf973f2207f9855d29922bc13 --- requirements.txt | 12 ++++++------ test-requirements.txt | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/requirements.txt b/requirements.txt index 9fdcda393..b769319c8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,21 +1,21 @@ # The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. -pbr>=1.6 # Apache-2.0 -cliff>=2.2.0 # Apache-2.0 +pbr>=1.8 # Apache-2.0 +cliff>=2.3.0 # Apache-2.0 debtcollector>=1.2.0 # Apache-2.0 iso8601>=0.1.11 # MIT netaddr!=0.7.16,>=0.7.13 # BSD osc-lib>=1.2.0 # Apache-2.0 oslo.i18n>=2.1.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 -oslo.utils>=3.17.0 # Apache-2.0 +oslo.utils>=3.18.0 # Apache-2.0 os-client-config>=1.22.0 # Apache-2.0 -keystoneauth1>=2.14.0 # Apache-2.0 +keystoneauth1>=2.16.0 # Apache-2.0 # keystoneclient is used only by neutronclient.osc.utils # TODO(amotoki): Drop this after osc.utils has no dependency on keystoneclient -python-keystoneclient>=3.8.0 # Apache-2.0 -requests>=2.10.0 # Apache-2.0 +python-keystoneclient>=3.8.0 # Apache-2.0 +requests!=2.12.2,>=2.10.0 # Apache-2.0 simplejson>=2.2.0 # MIT six>=1.9.0 # MIT Babel>=2.3.4 # BSD diff --git a/test-requirements.txt b/test-requirements.txt index feccad817..2c2652bc2 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -11,7 +11,7 @@ oslosphinx>=4.7.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 python-openstackclient>=3.3.0 # Apache-2.0 python-subunit>=0.0.18 # Apache-2.0/BSD -reno>=1.8.0 # Apache2 +reno>=1.8.0 # Apache-2.0 requests-mock>=1.1 # Apache-2.0 sphinx!=1.3b1,<1.4,>=1.2.1 # BSD testrepository>=0.0.18 # Apache-2.0/BSD From 0a721cf576479d21d35893ed82c29bca6adb92cb Mon Sep 17 00:00:00 2001 From: Yushiro FURUKAWA Date: Wed, 7 Sep 2016 09:48:04 +0900 Subject: [PATCH 536/845] Add FWaaS V2 commands for OSC plugin This commit supports FWaaS v2 CLI as OSC plugin[1]. [1] http://specs.openstack.org/openstack/neutron-specs/specs/newton/fwaas-api-2.0.html Partial-Implements: blueprint fwaas-api-2.0 Depends-On: I7b4108772e8370e8f51971caf40ecd23e9f977e9 Change-Id: I57504f97ac39b5b7d301fd5cc88228a121f0677e Closes-Bug: #1609686 --- neutronclient/osc/v2/fwaas/constants.py | 26 + neutronclient/osc/v2/fwaas/firewallgroup.py | 377 +++++++++ neutronclient/osc/v2/fwaas/firewallpolicy.py | 423 ++++++++++ neutronclient/osc/v2/fwaas/firewallrule.py | 399 ++++++++++ .../tests/unit/osc/v2/fwaas/common.py | 270 +++++++ .../tests/unit/osc/v2/fwaas/fakes.py | 117 +++ .../unit/osc/v2/fwaas/test_firewallgroup.py | 613 +++++++++++++++ .../unit/osc/v2/fwaas/test_firewallpolicy.py | 599 ++++++++++++++ .../unit/osc/v2/fwaas/test_firewallrule.py | 736 ++++++++++++++++++ neutronclient/v2_0/client.py | 94 +++ .../support-fwaasv2-cli-7f21676c551f8ae0.yaml | 4 + setup.cfg | 21 + 12 files changed, 3679 insertions(+) create mode 100644 neutronclient/osc/v2/fwaas/constants.py create mode 100644 neutronclient/osc/v2/fwaas/firewallgroup.py create mode 100644 neutronclient/osc/v2/fwaas/firewallpolicy.py create mode 100644 neutronclient/osc/v2/fwaas/firewallrule.py create mode 100644 neutronclient/tests/unit/osc/v2/fwaas/common.py create mode 100644 neutronclient/tests/unit/osc/v2/fwaas/fakes.py create mode 100644 neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py create mode 100644 neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py create mode 100644 neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py create mode 100644 releasenotes/notes/support-fwaasv2-cli-7f21676c551f8ae0.yaml diff --git a/neutronclient/osc/v2/fwaas/constants.py b/neutronclient/osc/v2/fwaas/constants.py new file mode 100644 index 000000000..29f3ce09f --- /dev/null +++ b/neutronclient/osc/v2/fwaas/constants.py @@ -0,0 +1,26 @@ +# Copyright 2016 FUJITSU LIMITED +# 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. +# + +FWG = 'firewall_group' +FWGS = 'firewall_groups' +FWP = 'firewall_policy' +FWPS = 'firewall_policies' +FWR = 'firewall_rule' +FWRS = 'firewall_rules' + +CMD_RESOURCE_MAP = dict( + (res, 'fwaas_' + res) for res in [FWG, FWGS, FWP, FWPS, FWR, FWRS] +) diff --git a/neutronclient/osc/v2/fwaas/firewallgroup.py b/neutronclient/osc/v2/fwaas/firewallgroup.py new file mode 100644 index 000000000..3aa569f8f --- /dev/null +++ b/neutronclient/osc/v2/fwaas/firewallgroup.py @@ -0,0 +1,377 @@ +# Copyright 2016 FUJITSU LIMITED +# 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. +# + +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils +from oslo_log import log as logging + +from neutronclient._i18n import _ +from neutronclient.osc import utils as osc_utils +from neutronclient.osc.v2.fwaas import constants as const +from neutronclient.osc.v2 import utils as v2_utils + + +LOG = logging.getLogger(__name__) + +_formatters = { + 'admin_state_up': v2_utils.format_admin_state, +} + +_attr_map = ( + ('id', 'ID', osc_utils.LIST_BOTH), + ('name', 'Name', osc_utils.LIST_BOTH), + ('ingress_firewall_policy_id', 'Ingress Policy ID', osc_utils.LIST_BOTH), + ('egress_firewall_policy_id', 'Egress Policy ID', osc_utils.LIST_BOTH), + ('description', 'Description', osc_utils.LIST_LONG_ONLY), + ('status', 'Status', osc_utils.LIST_LONG_ONLY), + ('ports', 'Ports', osc_utils.LIST_LONG_ONLY), + ('admin_state_up', 'State', osc_utils.LIST_LONG_ONLY), + ('public', 'Public', osc_utils.LIST_LONG_ONLY), + ('tenant_id', 'Project', osc_utils.LIST_LONG_ONLY), +) + + +def _get_common_parser(parser): + parser.add_argument( + '--name', + help=_('Name for the firewall group')) + parser.add_argument( + '--description', + metavar='', + help=_('Description of the firewall group')) + ingress_group = parser.add_mutually_exclusive_group() + ingress_group.add_argument( + '--ingress-firewall-policy', + metavar='', + dest='ingress_firewall_policy', + help=_('Ingress firewall policy (name or ID)')) + ingress_group.add_argument( + '--no-ingress-firewall-policy', + dest='no_ingress_firewall_policy', + action='store_true', + help=_('Detach ingress firewall policy from the firewall group')) + egress_group = parser.add_mutually_exclusive_group() + egress_group.add_argument( + '--egress-firewall-policy', + metavar='', + dest='egress_firewall_policy', + help=_('Egress firewall policy (name or ID)')) + egress_group.add_argument( + '--no-egress-firewall-policy', + dest='no_egress_firewall_policy', + action='store_true', + help=_('Detach egress firewall policy from the firewall group')) + public_group = parser.add_mutually_exclusive_group() + public_group.add_argument( + '--public', + action='store_true', + help=_('Make the firewall group public, which allows it to be ' + 'used in all projects (as opposed to the default, ' + 'which is to restrict its use to the current project)')) + public_group.add_argument( + '--private', + action='store_true', + help=_('Restrict use of the firewall group to the ' + 'current project')) + admin_group = parser.add_mutually_exclusive_group() + admin_group.add_argument( + '--enable', + action='store_true', + help=_('Enable firewall group')) + admin_group.add_argument( + '--disable', + action='store_true', + help=_('Disable firewall group')) + return parser + + +def _get_common_attrs(client_manager, parsed_args, is_create=True): + attrs = {} + client = client_manager.neutronclient + + if is_create: + if 'project' in parsed_args and parsed_args.project is not None: + attrs['tenant_id'] = osc_utils.find_project( + client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + if (parsed_args.ingress_firewall_policy and + parsed_args.no_ingress_firewall_policy): + attrs['ingress_firewall_policy_id'] = client.find_resource( + const.FWP, parsed_args.ingress_firewall_policy, + cmd_resource='fwaas_' + const.FWP)['id'] + elif parsed_args.ingress_firewall_policy: + attrs['ingress_firewall_policy_id'] = client.find_resource( + const.FWP, parsed_args.ingress_firewall_policy, + cmd_resource='fwaas_' + const.FWP)['id'] + elif parsed_args.no_ingress_firewall_policy: + attrs['ingress_firewall_policy_id'] = None + if (parsed_args.egress_firewall_policy and + parsed_args.no_egress_firewall_policy): + attrs['egress_firewall_policy_id'] = client.find_resource( + const.FWP, parsed_args.egress_firewall_policy, + cmd_resource='fwaas_' + const.FWP)['id'] + elif parsed_args.egress_firewall_policy: + attrs['egress_firewall_policy_id'] = client.find_resource( + const.FWP, parsed_args.egress_firewall_policy, + cmd_resource='fwaas_' + const.FWP)['id'] + elif parsed_args.no_egress_firewall_policy: + attrs['egress_firewall_policy_id'] = None + if parsed_args.public: + attrs['public'] = True + if parsed_args.private: + attrs['public'] = False + if parsed_args.enable: + attrs['admin_state_up'] = True + if parsed_args.disable: + attrs['admin_state_up'] = False + if parsed_args.name: + attrs['name'] = str(parsed_args.name) + if parsed_args.description: + attrs['description'] = str(parsed_args.description) + if parsed_args.port and parsed_args.no_port: + attrs['ports'] = sorted([client.find_resource( + 'port', p)['id'] for p in set(parsed_args.port)]) + elif parsed_args.port: + ports = [] + for p in set(parsed_args.port): + ports.append(client.find_resource('port', p)['id']) + if not is_create: + ports += client.find_resource( + const.FWG, parsed_args.firewall_group)['ports'] + attrs['ports'] = sorted(set(ports)) + elif parsed_args.no_port: + attrs['ports'] = [] + return attrs + + +class CreateFirewallGroup(command.ShowOne): + _description = _("Create a new firewall group") + + def get_parser(self, prog_name): + parser = super(CreateFirewallGroup, self).get_parser(prog_name) + _get_common_parser(parser) + osc_utils.add_project_owner_option_to_parser(parser) + port_group = parser.add_mutually_exclusive_group() + port_group.add_argument( + '--port', + metavar='', + action='append', + help=_('Port(s) (name or ID) to apply firewall group. This ' + 'option can be repeated')) + port_group.add_argument( + '--no-port', + dest='no_port', + action='store_true', + help=_('Detach all port from the firewall group')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + attrs = _get_common_attrs(self.app.client_manager, parsed_args) + obj = client.create_fwaas_firewall_group( + {const.FWG: attrs})[const.FWG] + columns, display_columns = osc_utils.get_columns(obj, _attr_map) + data = utils.get_dict_properties(obj, columns, formatters=_formatters) + return (display_columns, data) + + +class DeleteFirewallGroup(command.Command): + _description = _("Delete firewall group(s)") + + def get_parser(self, prog_name): + parser = super(DeleteFirewallGroup, self).get_parser(prog_name) + parser.add_argument( + const.FWG, + metavar='', + nargs='+', + help=_('Firewall group(s) to delete (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + result = 0 + for fwg in parsed_args.firewall_group: + try: + fwg_id = client.find_resource( + const.FWG, fwg, cmd_resource='fwaas_' + const.FWG)['id'] + client.delete_fwaas_firewall_group(fwg_id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete firewall group with " + "name or ID '%(firewall_group)s': %(e)s") % { + const.FWG: fwg, 'e': e}) + + if result > 0: + total = len(parsed_args.firewall_groups) + msg = (_("%(result)s of %(total)s firewall group(s) " + "failed to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) + + +class ListFirewallGroup(command.Lister): + _description = _("List firewall groups") + + def get_parser(self, prog_name): + parser = super(ListFirewallGroup, self).get_parser(prog_name) + parser.add_argument( + '--long', + action='store_true', + help=_("List additional fields in output") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + obj = client.list_fwaas_firewall_groups()[const.FWGS] + headers, columns = osc_utils.get_column_definitions( + _attr_map, long_listing=parsed_args.long) + return (headers, (utils.get_dict_properties( + s, columns, formatters=_formatters) for s in obj)) + + +class SetFirewallGroup(command.Command): + _description = _("Set firewall group properties") + + def get_parser(self, prog_name): + parser = super(SetFirewallGroup, self).get_parser(prog_name) + _get_common_parser(parser) + parser.add_argument( + const.FWG, + metavar='', + help=_('Firewall group to update (name or ID)')) + parser.add_argument( + '--port', + metavar='', + action='append', + help=_('Port(s) (name or ID) to apply firewall group. This ' + 'option can be repeated')) + parser.add_argument( + '--no-port', + dest='no_port', + action='store_true', + help=_('Detach all port from the firewall group')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + fwg_id = client.find_resource(const.FWG, parsed_args.firewall_group, + cmd_resource='fwaas_' + const.FWG)['id'] + attrs = _get_common_attrs(self.app.client_manager, parsed_args, + is_create=False) + try: + client.update_fwaas_firewall_group(fwg_id, {const.FWG: attrs}) + except Exception as e: + msg = (_("Failed to set firewall group '%(group)s': %(e)s") + % {'group': parsed_args.firewall_group, 'e': e}) + raise exceptions.CommandError(msg) + + +class ShowFirewallGroup(command.ShowOne): + _description = _("Display firewall group details") + + def get_parser(self, prog_name): + parser = super(ShowFirewallGroup, self).get_parser(prog_name) + parser.add_argument( + const.FWG, + metavar='', + help=_('Firewall group to show (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + fwg_id = client.find_resource(const.FWG, parsed_args.firewall_group, + cmd_resource='fwaas_' + const.FWG)['id'] + obj = client.show_fwaas_firewall_group(fwg_id)[const.FWG] + columns, display_columns = osc_utils.get_columns(obj, _attr_map) + data = utils.get_dict_properties(obj, columns, formatters=_formatters) + return (display_columns, data) + + +class UnsetFirewallGroup(command.Command): + _description = _("Unset firewall group properties") + + def get_parser(self, prog_name): + parser = super(UnsetFirewallGroup, self).get_parser(prog_name) + parser.add_argument( + const.FWG, + metavar='', + help=_('Firewall group to unset (name or ID)')) + port_group = parser.add_mutually_exclusive_group() + port_group.add_argument( + '--port', + metavar='', + action='append', + help=_('Port(s) (name or ID) to apply firewall group. This ' + 'option can be repeated')) + port_group.add_argument( + '--all-port', + action='store_true', + help=_('Remove all ports for this firewall group')) + parser.add_argument( + '--ingress-firewall-policy', + action='store_true', + help=_('Ingress firewall policy (name or ID) to delete')) + parser.add_argument( + '--egress-firewall-policy', + action='store_true', + dest='egress_firewall_policy', + help=_('Egress firewall policy (name or ID) to delete')) + parser.add_argument( + '--public', + action='store_true', + help=_('Restrict use of the firewall group to the ' + 'current project')) + parser.add_argument( + '--enable', + action='store_true', + help=_('Disable firewall group')) + return parser + + def _get_attrs(self, client_manager, parsed_args): + attrs = {} + client = client_manager.neutronclient + if parsed_args.ingress_firewall_policy: + attrs['ingress_firewall_policy_id'] = None + if parsed_args.egress_firewall_policy: + attrs['egress_firewall_policy_id'] = None + if parsed_args.public: + attrs['public'] = False + if parsed_args.enable: + attrs['admin_state_up'] = False + if parsed_args.port: + old = client.find_resource( + const.FWG, parsed_args.firewall_group)['ports'] + new = [client.find_resource( + 'port', r)['id'] for r in parsed_args.port] + attrs['ports'] = sorted(list(set(old) - set(new))) + if parsed_args.all_port: + attrs['ports'] = [] + return attrs + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + fwg_id = client.find_resource(const.FWG, parsed_args.firewall_group, + cmd_resource='fwaas_' + const.FWG)['id'] + attrs = self._get_attrs(self.app.client_manager, parsed_args) + try: + client.update_fwaas_firewall_group(fwg_id, {const.FWG: attrs}) + except Exception as e: + msg = (_("Failed to unset firewall group '%(group)s': %(e)s") + % {'group': parsed_args.firewall_group, 'e': e}) + raise exceptions.CommandError(msg) diff --git a/neutronclient/osc/v2/fwaas/firewallpolicy.py b/neutronclient/osc/v2/fwaas/firewallpolicy.py new file mode 100644 index 000000000..9a5a0d716 --- /dev/null +++ b/neutronclient/osc/v2/fwaas/firewallpolicy.py @@ -0,0 +1,423 @@ +# Copyright 2016 FUJITSU LIMITED +# 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. +# + +from __future__ import print_function + +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils +from oslo_log import log as logging + +from neutronclient._i18n import _ +from neutronclient.osc import utils as osc_utils +from neutronclient.osc.v2.fwaas import constants as const + + +LOG = logging.getLogger(__name__) + +_formatters = {} + +_attr_map = ( + ('id', 'ID', osc_utils.LIST_BOTH), + ('name', 'Name', osc_utils.LIST_BOTH), + ('firewall_rules', 'Firewall Rules', osc_utils.LIST_BOTH), + ('description', 'Description', osc_utils.LIST_LONG_ONLY), + ('audited', 'Audited', osc_utils.LIST_LONG_ONLY), + ('public', 'Public', osc_utils.LIST_LONG_ONLY), + ('tenant_id', 'Project', osc_utils.LIST_LONG_ONLY), +) + + +def _get_common_attrs(client_manager, parsed_args, is_create=True): + attrs = {} + client = client_manager.neutronclient + + if is_create: + if 'project' in parsed_args and parsed_args.project is not None: + attrs['tenant_id'] = osc_utils.find_project( + client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + if parsed_args.firewall_rule and parsed_args.no_firewall_rule: + _firewall_rules = [] + for f in set(parsed_args.firewall_rule): + _firewall_rules.append(client.find_resource( + const.FWR, f, cmd_resource='fwaas_' + const.FWR)['id']) + attrs[const.FWRS] = sorted(_firewall_rules) + elif parsed_args.firewall_rule: + rules = [] + for f in set(parsed_args.firewall_rule): + rules.append(client.find_resource( + const.FWR, f, cmd_resource='fwaas_' + const.FWR)['id']) + if not is_create: + rules += client.find_resource( + const.FWP, parsed_args.firewall_policy)[const.FWRS] + attrs[const.FWRS] = sorted(set(rules)) + elif parsed_args.no_firewall_rule: + attrs[const.FWRS] = [] + if parsed_args.audited: + attrs['audited'] = True + if parsed_args.no_audited: + attrs['audited'] = False + if parsed_args.name: + attrs['name'] = str(parsed_args.name) + if parsed_args.description: + attrs['description'] = str(parsed_args.description) + if parsed_args.public: + attrs['public'] = True + if parsed_args.private: + attrs['public'] = False + return attrs + + +def _get_common_parser(parser): + parser.add_argument( + '--description', + help=_('Description of the firewall policy')) + audited_group = parser.add_mutually_exclusive_group() + audited_group.add_argument( + '--audited', + action='store_true', + help=_('Enable auditing for the policy')) + audited_group.add_argument( + '--no-audited', + action='store_true', + help=_('Disable auditing for the policy')) + public_group = parser.add_mutually_exclusive_group() + public_group.add_argument( + '--public', + action='store_true', + help=_('Make the firewall policy public, which allows it to be ' + 'used in all projects (as opposed to the default, ' + 'which is to restrict its use to the current project)')) + public_group.add_argument( + '--private', + action='store_true', + help=_('Restrict use of the firewall policy to the ' + 'current project')) + return parser + + +class CreateFirewallPolicy(command.ShowOne): + _description = _("Create a new firewall policy") + + def get_parser(self, prog_name): + parser = super(CreateFirewallPolicy, self).get_parser(prog_name) + _get_common_parser(parser) + osc_utils.add_project_owner_option_to_parser(parser) + parser.add_argument( + 'name', + metavar='', + help=_('Name for the firewall policy')) + fwr_group = parser.add_mutually_exclusive_group() + fwr_group.add_argument( + '--firewall-rule', + action='append', + metavar='', + help=_('Firewall rule(s) to apply (name or ID)')) + fwr_group.add_argument( + '--no-firewall-rule', + action='store_true', + help=_('Unset all firewall rules from firewall policy')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + attrs = _get_common_attrs(self.app.client_manager, parsed_args) + obj = client.create_fwaas_firewall_policy( + {const.FWP: attrs})[const.FWP] + columns, display_columns = osc_utils.get_columns(obj, _attr_map) + data = utils.get_dict_properties(obj, columns, formatters=_formatters) + return (display_columns, data) + + +class DeleteFirewallPolicy(command.Command): + _description = _("Delete firewall policy(s)") + + def get_parser(self, prog_name): + parser = super(DeleteFirewallPolicy, self).get_parser(prog_name) + parser.add_argument( + const.FWP, + metavar='', + nargs='+', + help=_('Firewall policy(s) to delete (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + result = 0 + for fwp in parsed_args.firewall_policy: + try: + fwp_id = client.find_resource( + const.FWP, fwp, cmd_resource='fwaas_' + const.FWP)['id'] + client.delete_fwaas_firewall_policy(fwp_id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete Firewall policy with " + "name or ID '%(firewall_policy)s': %(e)s") % { + const.FWP: fwp, 'e': e}) + + if result > 0: + total = len(parsed_args.firewall_policy) + msg = (_("%(result)s of %(total)s Firewall policy(s) " + "failed to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) + + +class FirewallPolicyInsertRule(command.Command): + _description = _("Insert a rule into a given firewall policy") + + def get_parser(self, prog_name): + parser = super(FirewallPolicyInsertRule, self).get_parser(prog_name) + parser.add_argument( + const.FWP, + metavar='', + help=_('Firewall policy to insert rule (name or ID)')) + parser.add_argument( + '--insert-before', + metavar='', + help=_('Insert the new rule before this existing rule ' + '(name or ID)')) + parser.add_argument( + '--insert-after', + metavar='', + help=_('Insert the new rule after this existing rule ' + '(name or ID)')) + parser.add_argument( + const.FWR, + metavar='', + help=_('Firewall rule to be inserted (name or ID)')) + return parser + + def args2body(self, parsed_args): + client = self.app.client_manager.neutronclient + _rule_id = _get_required_firewall_rule(client, parsed_args) + _insert_before = '' + if 'insert_before' in parsed_args: + if parsed_args.insert_before: + _insert_before = client.find_resource( + const.FWR, parsed_args.insert_before, + cmd_resource='fwaas_' + const.FWR)['id'] + _insert_after = '' + if 'insert_after' in parsed_args: + if parsed_args.insert_after: + _insert_after = client.find_resource( + const.FWR, parsed_args.insert_after, + cmd_resource='fwaas_' + const.FWR)['id'] + return {'firewall_rule_id': _rule_id, + 'insert_before': _insert_before, + 'insert_after': _insert_after} + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + body = self.args2body(parsed_args) + policy_id = client.find_resource( + const.FWP, parsed_args.firewall_policy, + cmd_resource='fwaas_' + const.FWP)['id'] + client.insert_rule_fwaas_firewall_policy(policy_id, body) + rule_id = body['firewall_rule_id'] + policy = parsed_args.firewall_policy + print((_('Inserted firewall rule %(rule)s in firewall policy ' + '%(policy)s') % {'rule': rule_id, 'policy': policy}), + file=self.app.stdout) + + +class FirewallPolicyRemoveRule(command.Command): + _description = _("Remove a rule from a given firewall policy") + + def get_parser(self, prog_name): + parser = super(FirewallPolicyRemoveRule, self).get_parser(prog_name) + parser.add_argument( + const.FWP, + metavar='', + help=_('Firewall policy to remove rule (name or ID)')) + parser.add_argument( + const.FWR, + metavar='', + help=_('Firewall rule to remove from policy (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + fwr_id = _get_required_firewall_rule(client, parsed_args) + body = {'firewall_rule_id': fwr_id} + policy_id = client.find_resource( + const.FWP, parsed_args.firewall_policy, + cmd_resource='fwaas_' + const.FWP)['id'] + client.remove_rule_fwaas_firewall_policy(policy_id, body) + rule_id = body['firewall_rule_id'] + policy = parsed_args.firewall_policy + print((_('Removed firewall rule %(rule)s from firewall policy ' + '%(policy)s') % {'rule': rule_id, 'policy': policy}), + file=self.app.stdout) + + +class ListFirewallPolicy(command.Lister): + _description = _("List firewall policies") + + def get_parser(self, prog_name): + parser = super(ListFirewallPolicy, self).get_parser(prog_name) + parser.add_argument( + '--long', + action='store_true', + default=False, + help=_("List additional fields in output") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + obj = client.list_fwaas_firewall_policies()[const.FWPS] + headers, columns = osc_utils.get_column_definitions( + _attr_map, long_listing=parsed_args.long) + return (headers, (utils.get_dict_properties( + s, columns, formatters=_formatters) for s in obj)) + + +class SetFirewallPolicy(command.Command): + _description = _("Set firewall policy properties") + + def get_parser(self, prog_name): + parser = super(SetFirewallPolicy, self).get_parser(prog_name) + _get_common_parser(parser) + parser.add_argument( + const.FWP, + metavar='', + help=_('Firewall policy to update (name or ID)')) + parser.add_argument( + '--name', + metavar='', + help=_('Name for the firewall policy')) + parser.add_argument( + '--firewall-rule', + action='append', + metavar='', + help=_('Firewall rule(s) to apply (name or ID)')) + parser.add_argument( + '--no-firewall-rule', + action='store_true', + help=_('Remove all firewall rules from firewall policy')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + fwp_id = client.find_resource( + const.FWP, parsed_args.firewall_policy, + cmd_resource='fwaas_' + const.FWP)['id'] + attrs = _get_common_attrs(self.app.client_manager, + parsed_args, is_create=False) + try: + client.update_fwaas_firewall_policy(fwp_id, {const.FWP: attrs}) + except Exception as e: + msg = (_("Failed to set firewall policy '%(policy)s': %(e)s") + % {'policy': parsed_args.firewall_policy, 'e': e}) + raise exceptions.CommandError(msg) + + +class ShowFirewallPolicy(command.ShowOne): + _description = _("Display firewall policy details") + + def get_parser(self, prog_name): + parser = super(ShowFirewallPolicy, self).get_parser(prog_name) + parser.add_argument( + const.FWP, + metavar='', + help=_('Firewall policy to show (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + fwp_id = client.find_resource(const.FWP, + parsed_args.firewall_policy, + cmd_resource='fwaas_' + const.FWP)['id'] + obj = client.show_fwaas_firewall_policy(fwp_id)[const.FWP] + columns, display_columns = osc_utils.get_columns(obj, _attr_map) + data = utils.get_dict_properties(obj, columns, formatters=_formatters) + return (display_columns, data) + + +def _get_required_firewall_rule(client, parsed_args): + if not parsed_args.firewall_rule: + msg = (_("Firewall rule (name or ID) is required.")) + raise exceptions.CommandError(msg) + return client.find_resource(const.FWR, parsed_args.firewall_rule, + cmd_resource='fwaas_' + const.FWR)['id'] + + +class UnsetFirewallPolicy(command.Command): + _description = _("Unset firewall policy properties") + + def get_parser(self, prog_name): + parser = super(UnsetFirewallPolicy, self).get_parser(prog_name) + parser.add_argument( + const.FWP, + metavar='', + help=_('Firewall policy to unset (name or ID)')) + firewall_rule_group = parser.add_mutually_exclusive_group() + firewall_rule_group.add_argument( + '--firewall-rule', + action='append', + metavar='', + help=_('Remove firewall rule(s) from the firewall policy ' + '(name or ID)')) + firewall_rule_group.add_argument( + '--all-firewall-rule', + action='store_true', + help=_('Remove all firewall rules from the firewall policy')) + parser.add_argument( + '--audited', + action='store_true', + help=_('Disable auditing for the policy')) + parser.add_argument( + '--public', + action='store_true', + help=_('Restrict use of the firewall policy to the ' + 'current project')) + return parser + + def _get_attrs(self, client_manager, parsed_args): + attrs = {} + client = client_manager.neutronclient + + if parsed_args.firewall_rule: + old = client.find_resource( + const.FWP, parsed_args.firewall_policy)[const.FWRS] + new = [] + for f in set(parsed_args.firewall_rule): + new.append(client.find_resource( + const.FWR, f, cmd_resource='fwaas_' + const.FWR)['id']) + attrs[const.FWRS] = sorted(list(set(old) - set(new))) + if parsed_args.all_firewall_rule: + attrs[const.FWRS] = [] + if parsed_args.audited: + attrs['audited'] = False + if parsed_args.public: + attrs['public'] = False + return attrs + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + fwp_id = client.find_resource( + const.FWP, parsed_args.firewall_policy, + cmd_resource='fwaas_' + const.FWP)['id'] + attrs = self._get_attrs(self.app.client_manager, parsed_args) + try: + client.update_fwaas_firewall_policy(fwp_id, {const.FWP: attrs}) + except Exception as e: + msg = (_("Failed to unset firewall policy '%(policy)s': %(e)s") + % {'policy': parsed_args.firewall_policy, 'e': e}) + raise exceptions.CommandError(msg) diff --git a/neutronclient/osc/v2/fwaas/firewallrule.py b/neutronclient/osc/v2/fwaas/firewallrule.py new file mode 100644 index 000000000..a7d9051f3 --- /dev/null +++ b/neutronclient/osc/v2/fwaas/firewallrule.py @@ -0,0 +1,399 @@ +# Copyright 2016 FUJITSU LIMITED +# 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 copy + +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils +from oslo_log import log as logging + +from neutronclient._i18n import _ +from neutronclient.common import utils as nc_utils +from neutronclient.osc import utils as osc_utils +from neutronclient.osc.v2.fwaas import constants as const + + +LOG = logging.getLogger(__name__) + + +_attr_map = ( + ('id', 'ID', osc_utils.LIST_BOTH), + ('name', 'Name', osc_utils.LIST_BOTH), + ('enabled', 'Enabled', osc_utils.LIST_BOTH), + ('summary', 'Summary', osc_utils.LIST_SHORT_ONLY), + ('description', 'Description', osc_utils.LIST_LONG_ONLY), + ('ip_version', 'IP Version', osc_utils.LIST_LONG_ONLY), + ('action', 'Action', osc_utils.LIST_LONG_ONLY), + ('protocol', 'Protocol', osc_utils.LIST_LONG_ONLY), + ('source_ip_address', 'Source IP Address', osc_utils.LIST_LONG_ONLY), + ('source_port', 'Source Port', osc_utils.LIST_LONG_ONLY), + ('destination_ip_address', 'Destination IP Address', + osc_utils.LIST_LONG_ONLY), + ('destination_port', 'Destination Port', osc_utils.LIST_LONG_ONLY), + ('public', 'Public', osc_utils.LIST_LONG_ONLY), + ('tenant_id', 'Project', osc_utils.LIST_LONG_ONLY), +) + + +def _get_common_parser(parser): + parser.add_argument( + '--name', + metavar='', + help=_('Name of the firewall rule')) + parser.add_argument( + '--description', + metavar='', + help=_('Description of the firewall rule')) + parser.add_argument( + '--protocol', + choices=['tcp', 'udp', 'icmp', 'any'], + type=nc_utils.convert_to_lowercase, + help=_('Protocol for the firewall rule')) + parser.add_argument( + '--action', + choices=['allow', 'deny', 'reject'], + type=nc_utils.convert_to_lowercase, + help=_('Action for the firewall rule')) + parser.add_argument( + '--ip-version', + metavar='', + choices=['4', '6'], + help=_('Set IP version 4 or 6 (default is 4)')) + src_ip_group = parser.add_mutually_exclusive_group() + src_ip_group.add_argument( + '--source-ip-address', + metavar='', + help=_('Source IP address or subnet')) + src_ip_group.add_argument( + '--no-source-ip-address', + action='store_true', + help=_('Detach source IP address')) + dst_ip_group = parser.add_mutually_exclusive_group() + dst_ip_group.add_argument( + '--destination-ip-address', + metavar='', + help=_('Destination IP address or subnet')) + dst_ip_group.add_argument( + '--no-destination-ip-address', + action='store_true', + help=_('Detach destination IP address')) + src_port_group = parser.add_mutually_exclusive_group() + src_port_group.add_argument( + '--source-port', + metavar='', + help=_('Source port number or range' + '(integer in [1, 65535] or range like 123:456)')) + src_port_group.add_argument( + '--no-source-port', + action='store_true', + help=_('Detach source port number or range')) + dst_port_group = parser.add_mutually_exclusive_group() + dst_port_group.add_argument( + '--destination-port', + metavar='', + help=_('Destination port number or range' + '(integer in [1, 65535] or range like 123:456)')) + dst_port_group.add_argument( + '--no-destination-port', + action='store_true', + help=_('Detach destination port number or range')) + public_group = parser.add_mutually_exclusive_group() + public_group.add_argument( + '--public', + action='store_true', + help=_('Make the firewall rule public, which allows it to be ' + 'used in all projects (as opposed to the default, ' + 'which is to restrict its use to the current project)')) + public_group.add_argument( + '--private', + action='store_true', + help=_('Restrict use of the firewall rule to the current project')) + enable_group = parser.add_mutually_exclusive_group() + enable_group.add_argument( + '--enable-rule', + action='store_true', + help=_('Enable this rule (default is enabled)')) + enable_group.add_argument( + '--disable-rule', + action='store_true', + help=_('Disable this rule')) + return parser + + +def _get_common_attrs(client_manager, parsed_args, is_create=True): + attrs = {} + if is_create: + if 'project' in parsed_args and parsed_args.project is not None: + attrs['tenant_id'] = osc_utils.find_project( + client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + if parsed_args.name: + attrs['name'] = str(parsed_args.name) + if parsed_args.description: + attrs['description'] = str(parsed_args.description) + if parsed_args.protocol: + attrs['protocol'] = parsed_args.protocol + if parsed_args.action: + attrs['action'] = parsed_args.action + if parsed_args.ip_version: + attrs['ip_version'] = str(parsed_args.ip_version) + if parsed_args.source_port: + attrs['source_port'] = parsed_args.source_port + if parsed_args.no_source_port: + attrs['source_port'] = None + if parsed_args.source_ip_address: + attrs['source_ip_address'] = parsed_args.source_ip_address + if parsed_args.no_source_ip_address: + attrs['source_ip_address'] = None + if parsed_args.destination_port: + attrs['destination_port'] = str(parsed_args.destination_port) + if parsed_args.no_destination_port: + attrs['destination_port'] = None + if parsed_args.destination_ip_address: + attrs['destination_ip_address'] = str( + parsed_args.destination_ip_address) + if parsed_args.no_destination_ip_address: + attrs['destination_ip_address'] = None + if parsed_args.enable_rule: + attrs['enabled'] = True + if parsed_args.disable_rule: + attrs['enabled'] = False + if parsed_args.public: + attrs['public'] = True + if parsed_args.private: + attrs['public'] = False + return attrs + + +def format_protocol(protocol): + return protocol if protocol else 'any' + +_formatters = {'protocol': format_protocol} + + +class CreateFirewallRule(command.ShowOne): + _description = _("Create a new firewall rule") + + def get_parser(self, prog_name): + parser = super(CreateFirewallRule, self).get_parser(prog_name) + _get_common_parser(parser) + osc_utils.add_project_owner_option_to_parser(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + attrs = _get_common_attrs(self.app.client_manager, parsed_args) + obj = client.create_fwaas_firewall_rule( + {const.FWR: attrs})[const.FWR] + columns, display_columns = osc_utils.get_columns(obj, _attr_map) + data = utils.get_dict_properties(obj, columns, formatters=_formatters) + return display_columns, data + + +class DeleteFirewallRule(command.Command): + _description = _("Delete firewall rule(s)") + + def get_parser(self, prog_name): + parser = super(DeleteFirewallRule, self).get_parser(prog_name) + parser.add_argument( + const.FWR, + metavar='', + nargs='+', + help=_('Firewall rule(s) to delete (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + result = 0 + for fwr in parsed_args.firewall_rule: + try: + fwr_id = client.find_resource( + const.FWR, fwr, cmd_resource='fwaas_' + const.FWR)['id'] + client.delete_fwaas_firewall_rule(fwr_id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete Firewall rule with " + "name or ID '%(firewall_rule)s': %(e)s") % { + const.FWR: fwr, 'e': e}) + + if result > 0: + total = len(parsed_args.firewall_rule) + msg = (_("%(result)s of %(total)s Firewall rule(s) failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) + + +class ListFirewallRule(command.Lister): + _description = _("List firewall rules that belong to a given tenant") + + def get_parser(self, prog_name): + parser = super(ListFirewallRule, self).get_parser(prog_name) + parser.add_argument( + '--long', + action='store_true', + default=False, + help=_("List additional fields in output") + ) + return parser + + def extend_list(self, data, parsed_args): + ext_data = copy.deepcopy(data) + for d in ext_data: + protocol = d['protocol'].upper() if d['protocol'] else 'ANY' + src_ip = 'none specified' + dst_ip = 'none specified' + src_port = '(none specified)' + dst_port = '(none specified)' + if 'source_ip_address' in d and d['source_ip_address']: + src_ip = str(d['source_ip_address']).lower() + if 'source_port' in d and d['source_port']: + src_port = '(' + str(d['source_port']).lower() + ')' + if 'destination_ip_address' in d and d['destination_ip_address']: + dst_ip = str(d['destination_ip_address']).lower() + if 'destination_port' in d and d['destination_port']: + dst_port = '(' + str(d['destination_port']).lower() + ')' + action = d['action'] if d.get('action') else 'no-action' + src = 'source(port): ' + src_ip + src_port + dst = 'dest(port): ' + dst_ip + dst_port + d['summary'] = ',\n '.join([protocol, src, dst, action]) + return ext_data + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + obj = client.list_fwaas_firewall_rules()[const.FWRS] + obj_extend = self.extend_list(obj, parsed_args) + headers, columns = osc_utils.get_column_definitions( + _attr_map, long_listing=parsed_args.long) + return (headers, (utils.get_dict_properties( + s, columns, formatters=_formatters) for s in obj_extend)) + + +class SetFirewallRule(command.Command): + _description = _("Set firewall rule properties") + + def get_parser(self, prog_name): + parser = super(SetFirewallRule, self).get_parser(prog_name) + _get_common_parser(parser) + parser.add_argument( + const.FWR, + metavar='', + help=_('Firewall rule to set (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + attrs = _get_common_attrs(self.app.client_manager, + parsed_args, is_create=False) + fwr_id = client.find_resource( + const.FWR, parsed_args.firewall_rule, + cmd_resource='fwaas_' + const.FWR)['id'] + try: + client.update_fwaas_firewall_rule(fwr_id, {const.FWR: attrs}) + except Exception as e: + msg = (_("Failed to set firewall rule '%(rule)s': %(e)s") + % {'rule': parsed_args.firewall_rule, 'e': e}) + raise exceptions.CommandError(msg) + + +class ShowFirewallRule(command.ShowOne): + _description = _("Display firewall rule details") + + def get_parser(self, prog_name): + parser = super(ShowFirewallRule, self).get_parser(prog_name) + parser.add_argument( + const.FWR, + metavar='', + help=_('Firewall rule to display (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + fwr_id = client.find_resource( + const.FWR, parsed_args.firewall_rule, + cmd_resource='fwaas_' + const.FWR)['id'] + obj = client.show_fwaas_firewall_rule(fwr_id)[const.FWR] + columns, display_columns = osc_utils.get_columns(obj, _attr_map) + data = utils.get_dict_properties(obj, columns, formatters=_formatters) + return (display_columns, data) + + +class UnsetFirewallRule(command.Command): + _description = _("Unset firewall rule properties") + + def get_parser(self, prog_name): + parser = super(UnsetFirewallRule, self).get_parser(prog_name) + parser.add_argument( + const.FWR, + metavar='', + help=_('Firewall rule to unset (name or ID)')) + parser.add_argument( + '--source-ip-address', + action='store_true', + help=_('Source IP address or subnet')) + parser.add_argument( + '--destination-ip-address', + action='store_true', + help=_('Destination IP address or subnet')) + parser.add_argument( + '--source-port', + action='store_true', + help=_('Source port number or range' + '(integer in [1, 65535] or range like 123:456)')) + parser.add_argument( + '--destination-port', + action='store_true', + help=_('Destination port number or range' + '(integer in [1, 65535] or range like 123:456)')) + parser.add_argument( + '--public', + action='store_true', + help=_('Restrict use of the firewall rule to the current project')) + parser.add_argument( + '--enable-rule', + action='store_true', + help=_('Disable this rule')) + return parser + + def _get_attrs(self, client_manager, parsed_args): + attrs = {} + if parsed_args.source_ip_address: + attrs['source_ip_address'] = None + if parsed_args.source_port: + attrs['source_port'] = None + if parsed_args.destination_ip_address: + attrs['destination_ip_address'] = None + if parsed_args.destination_port: + attrs['destination_port'] = None + if parsed_args.public: + attrs['public'] = False + if parsed_args.enable_rule: + attrs['enabled'] = False + return attrs + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + attrs = self._get_attrs(self.app.client_manager, parsed_args) + fwr_id = client.find_resource( + const.FWR, parsed_args.firewall_rule, + cmd_resource='fwaas_' + const.FWR)['id'] + try: + client.update_fwaas_firewall_rule(fwr_id, {const.FWR: attrs}) + except Exception as e: + msg = (_("Failed to unset firewall rule '%(rule)s': %(e)s") + % {'rule': parsed_args.firewall_rule, 'e': e}) + raise exceptions.CommandError(msg) diff --git a/neutronclient/tests/unit/osc/v2/fwaas/common.py b/neutronclient/tests/unit/osc/v2/fwaas/common.py new file mode 100644 index 000000000..915988488 --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/fwaas/common.py @@ -0,0 +1,270 @@ +# Copyright 2016 FUJITSU LIMITED +# 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. +# + +from osc_lib.tests import utils + +from neutronclient.tests.unit.osc.v2 import fakes as test_fakes + + +class TestListFWaaS(test_fakes.TestNeutronClientOSCV2): + + def test_list_with_no_option(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.list_headers), headers) + self.assertEqual([self.list_data], list(data)) + + def test_list_with_long_option(self): + arglist = ['--long'] + verifylist = [('long', True)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.headers), headers) + self.assertEqual([self.data], list(data)) + + +class TestShowFWaaS(test_fakes.TestNeutronClientOSCV2): + + def test_show_filtered_by_id_or_name(self): + target = self.resource['id'] + arglist = [target] + verifylist = [(self.res, target)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with(target) + self.assertEqual(self.ordered_headers, headers) + self.assertEqual(self.ordered_data, data) + + +class TestCreateFWaaS(test_fakes.TestNeutronClientOSCV2): + pass + + +class TestSetFWaaS(test_fakes.TestNeutronClientOSCV2): + + def test_set_name(self): + target = self.resource['id'] + update = 'change' + arglist = [target, '--name', update] + verifylist = [ + (self.res, target), + ('name', update), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'name': update}}) + self.assertIsNone(result) + + def test_set_description(self): + target = self.resource['id'] + update = 'change-desc' + arglist = [target, '--description', update] + verifylist = [ + (self.res, target), + ('description', update), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'description': update}}) + self.assertIsNone(result) + + def test_set_public(self): + target = self.resource['id'] + arglist = [target, '--public'] + verifylist = [ + (self.res, target), + ('public', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'public': True}}) + self.assertIsNone(result) + + def test_set_duplicate_public(self): + target = self.resource['id'] + arglist = [target, '--public', '--public'] + verifylist = [ + (self.res, target), + ('public', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'public': True}}) + self.assertIsNone(result) + + def test_set_private(self): + target = self.resource['id'] + arglist = [target, '--private'] + verifylist = [ + (self.res, target), + ('public', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'public': False}}) + self.assertIsNone(result) + + def test_set_duplicate_private(self): + target = self.resource['id'] + arglist = [target, '--private', '--private'] + verifylist = [ + (self.res, target), + ('public', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'public': False}}) + self.assertIsNone(result) + + def test_set_private_and_public(self): + target = self.resource['id'] + arglist = [target, '--private', '--public'] + verifylist = [ + (self.res, target), + ('private', True), + ('public', True), + ] + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_set_public_and_priavte(self): + target = self.resource['id'] + arglist = [target, '--public', '--private'] + verifylist = [ + (self.res, target), + ('public', True), + ('private', True), + ] + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_set_project(self): + target = self.resource['id'] + project_id = 'b14ce3b699594d13819a859480286489' + arglist = [target, '--project', project_id] + verifylist = [ + (self.res, target), + ('tenant_id', project_id), + ] + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_set_project_domain(self): + target = self.resource['id'] + project_domain = 'mydomain.com' + arglist = [target, '--project-domain', project_domain] + verifylist = [ + (self.res, target), + ('project_domain', project_domain), + ] + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + +class TestDeleteFWaaS(test_fakes.TestNeutronClientOSCV2): + + def test_delete_with_one_resource(self): + target = self.resource['id'] + arglist = [target] + verifylist = [(self.res, [target])] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with(target) + self.assertIsNone(result) + + def test_delete_with_multiple_resources(self): + target1 = 'target1' + target2 = 'target2' + arglist = [target1, target2] + verifylist = [(self.res, [target1, target2])] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) + + self.assertEqual(2, self.mocked.call_count) + for idx, reference in enumerate([target1, target2]): + actual = ''.join(self.mocked.call_args_list[idx][0]) + self.assertEqual(reference, actual) + + +class TestUnsetFWaaS(test_fakes.TestNeutronClientOSCV2): + + def test_unset_public(self): + target = self.resource['id'] + arglist = [ + target, + '--public', + ] + verifylist = [ + (self.res, target), + ('public', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.mocked.assert_called_once_with( + target, {self.res: {'public': False}}) + self.assertIsNone(result) + + def test_set_public_and_priavte(self): + target = self.resource['id'] + arglist = [target, '--public', '--private'] + verifylist = [ + (self.res, target), + ('public', True), + ('private', True), + ] + # check_parser: error: unrecognized arguments: --private + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_set_duplicate_public(self): + target = self.resource['id'] + arglist = [target, '--public', '--public'] + verifylist = [ + (self.res, target), + ('public', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'public': False}}) + self.assertIsNone(result) diff --git a/neutronclient/tests/unit/osc/v2/fwaas/fakes.py b/neutronclient/tests/unit/osc/v2/fwaas/fakes.py new file mode 100644 index 000000000..0eb3fe529 --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/fwaas/fakes.py @@ -0,0 +1,117 @@ +# Copyright 2016 FUJITSU LIMITED +# 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 collections +import copy +import mock +import uuid + + +class FakeFWaaS(object): + + def create(self, attrs={}): + """Create a fake fwaas resources + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A OrderedDict faking the fwaas resource + """ + self.ordered.update(attrs) + return copy.deepcopy(self.ordered) + + def bulk_create(self, attrs=None, count=2): + """Create multiple fake fwaas resources + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of fwaas resources to fake + :return: + A list of dictionaries faking the fwaas resources + """ + return [self.create(attrs=attrs) for i in range(0, count)] + + def get(self, attrs=None, count=2): + """Create multiple fake fwaas resources + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of fwaas resources to fake + :return: + A list of dictionaries faking the fwaas resource + """ + if attrs is None: + self.attrs = self.bulk_create(count=count) + return mock.Mock(side_effect=attrs) + + +class FirewallGroup(FakeFWaaS): + """Fake one or more firewall group""" + + def __init__(self): + super(FirewallGroup, self).__init__() + self.ordered = collections.OrderedDict(( + ('id', 'firewall-group-id-' + uuid.uuid4().hex), + ('name', 'my-group-' + uuid.uuid4().hex), + ('ingress_firewall_policy_id', None), + ('egress_firewall_policy_id', None), + ('description', 'my-desc-' + uuid.uuid4().hex), + ('status', 'INACTIVE'), + ('ports', []), + ('admin_state_up', True), + ('public', False), + ('tenant_id', 'tenant-id-' + uuid.uuid4().hex), + )) + + +class FirewallPolicy(FakeFWaaS): + """Fake one or more firewall policy""" + + def __init__(self): + super(FirewallPolicy, self).__init__() + self.ordered = collections.OrderedDict(( + ('id', 'firewall-policy-' + uuid.uuid4().hex), + ('name', 'my-policy-' + uuid.uuid4().hex), + ('firewall_rules', []), + ('description', 'my-desc-' + uuid.uuid4().hex), + ('audited', True), + ('public', False), + ('tenant_id', 'tenant-id-' + uuid.uuid4().hex), + )) + + +class FirewallRule(FakeFWaaS): + """Fake one or more firewall rule""" + + def __init__(self): + super(FirewallRule, self).__init__() + self.ordered = collections.OrderedDict(( + ('id', 'firewall-rule-id-' + uuid.uuid4().hex), + ('name', 'my-rule-' + uuid.uuid4().hex), + ('enabled', False), + ('description', 'my-desc-' + uuid.uuid4().hex), + ('ip_version', 4), + ('action', 'deny'), + ('protocol', 'any'), + ('source_ip_address', '192.168.1.0/24'), + ('source_port', '1:11111'), + ('destination_ip_address', '192.168.2.2'), + ('destination_port', '2:22222'), + ('public', False), + ('tenant_id', 'tenant-id-' + uuid.uuid4().hex), + )) diff --git a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py new file mode 100644 index 000000000..44bcc708f --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py @@ -0,0 +1,613 @@ +# Copyright 2016 FUJITSU LIMITED +# 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 copy +import mock +import re + +from osc_lib import exceptions +from osc_lib.tests import utils + +from neutronclient.osc import utils as osc_utils +from neutronclient.osc.v2.fwaas import firewallgroup +from neutronclient.tests.unit.osc.v2 import fakes as test_fakes +from neutronclient.tests.unit.osc.v2.fwaas import common +from neutronclient.tests.unit.osc.v2.fwaas import fakes + + +_fwg = fakes.FirewallGroup().create() +CONVERT_MAP = { + 'ingress_firewall_policy': 'ingress_firewall_policy_id', + 'egress_firewall_policy': 'egress_firewall_policy_id', + 'no_ingress_firewall_policy': 'ingress_firewall_policy_id', + 'no_egress_firewall_policy': 'egress_firewall_policy_id', + 'public': 'public', + 'private': 'public', + 'project': 'tenant_id', + 'enable': 'admin_state_up', + 'disable': 'admin_state_up', + 'port': 'ports', +} + + +def _generate_response(ordered_dict=None, data=None): + source = ordered_dict if ordered_dict else _fwg + up = {'admin_state_up': 'UP' if source['admin_state_up'] else 'DOWN'} + if data: + up.append(data) + source.update(up) + return tuple(source[key] for key in source) + + +def _generate_req_and_res(verifylist): + request = dict(verifylist) + response = copy.deepcopy(_fwg) + for key, val in verifylist: + del request[key] + if re.match('^no_', key) and val is True: + new_value = None + elif key == 'enable' and val: + new_value = True + elif key == 'disable' and val: + new_value = False + else: + new_value = val + converted = CONVERT_MAP.get(key, key) + request[converted] = new_value + response[converted] = new_value + return request, response + + +class TestFirewallGroup(test_fakes.TestNeutronClientOSCV2): + + def check_results(self, headers, data, exp_req, is_list=False): + if is_list: + req_body = {self.res_plural: [exp_req]} + else: + req_body = {self.res: exp_req} + self.mocked.assert_called_once_with(req_body) + self.assertEqual(self.ordered_headers, headers) + self.assertEqual(self.ordered_data, data) + + def setUp(self): + super(TestFirewallGroup, self).setUp() + + def side_effect_for_find_resource(*args, **kwargs): + port_id = args[1] + ports = _fwg['ports'] + if self.res in args[0]: + ports = _fwg['ports'] + return {'id': port_id, 'ports': ports} + + def side_effect_for_list_ports(*args): + port_name = 'id_for_port' + return {'ports': [{'id': port_name}]} + + self.neutronclient.find_resource.side_effect = mock.Mock( + side_effect=side_effect_for_find_resource) + self.neutronclient.list_ports = mock.Mock( + side_effect=side_effect_for_list_ports) + osc_utils.find_project = mock.Mock() + osc_utils.find_project.id = _fwg['tenant_id'] + self.res = 'firewall_group' + self.res_plural = 'firewall_groups' + self.resource = _fwg + self.list_headers = ( + 'ID', + 'Name', + 'Ingress Policy ID', + 'Egress Policy ID', + ) + self.list_data = ( + _fwg['id'], + _fwg['name'], + _fwg['ingress_firewall_policy_id'], + _fwg['egress_firewall_policy_id'], + ) + self.headers = tuple(self.list_headers + ( + 'Description', + 'Status', + 'Ports', + 'State', + 'Public', + 'Project', + )) + self.data = _generate_response() + self.ordered_headers = copy.deepcopy(tuple(sorted(self.headers))) + self.ordered_data = ( + _fwg['description'], + _fwg['egress_firewall_policy_id'], + _fwg['id'], + _fwg['ingress_firewall_policy_id'], + _fwg['name'], + _fwg['ports'], + _fwg['tenant_id'], + _fwg['public'], + 'UP' if _fwg['admin_state_up'] else 'DOWN', + _fwg['status'], + ) + self.ordered_columns = ( + 'description', + 'egress_firewall_policy_id', + 'id', + 'ingress_firewall_policy_id', + 'name', + 'ports', + 'tenant_id', + 'public', + 'admin_state_up', + 'status', + ) + + +class TestCreateFirewallGroup(TestFirewallGroup, common.TestCreateFWaaS): + + def setUp(self): + # Mock objects + super(TestCreateFirewallGroup, self).setUp() + self.neutronclient.create_fwaas_firewall_group = mock.Mock( + return_value={self.res: _fwg}) + self.mocked = self.neutronclient.create_fwaas_firewall_group + self.cmd = firewallgroup.CreateFirewallGroup(self.app, self.namespace) + + def _update_expect_response(self, request, response): + """Set expected request and response + + :param request + A dictionary of request body(dict of verifylist) + :param response + A OrderedDict of request body + """ + # Update response body + self.neutronclient.create_fwaas_firewall_group.return_value = \ + {self.res: dict(response)} + osc_utils.find_project.return_value.id = response['tenant_id'] + # Update response(finally returns 'data') + self.data = _generate_response(ordered_dict=response) + self.ordered_data = tuple( + response[column] for column in self.ordered_columns + ) + + def test_create_with_no_option(self): + # firewall_group-create with mandatory (none) params. + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.ordered_headers, headers) + self.assertEqual(self.ordered_data, data) + + def test_create_with_port(self): + # firewall_group-create with 'port' + port_id = 'id_for_port' + arglist = ['--port', port_id] + verifylist = [('port', [port_id])] + request, response = _generate_req_and_res(verifylist) + self._update_expect_response(request, response) + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.check_results(headers, data, request) + + def test_create_with_all_params(self): + name = 'my-name' + description = 'my-desc' + ingress_policy = 'my-ingress-policy' + egress_policy = 'my-egress-policy' + port = 'port' + tenant_id = 'my-tenant' + arglist = [ + '--name', name, + '--description', description, + '--ingress-firewall-policy', ingress_policy, + '--egress-firewall-policy', egress_policy, + '--port', port, + '--project', tenant_id, + '--public', + '--disable', + ] + verifylist = [ + ('name', name), + ('description', description), + ('ingress_firewall_policy', ingress_policy), + ('egress_firewall_policy', egress_policy), + ('port', [port]), + ('public', True), + ('project', tenant_id), + ('disable', True), + ] + request, response = _generate_req_and_res(verifylist) + self._update_expect_response(request, response) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.check_results(headers, data, request) + + def test_create_with_public_and_private(self): + arglist = [ + '--public', + '--private', + ] + verifylist = [ + ('public', True), + ('private', True), + ] + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_create_with_ports_and_no(self): + port = 'my-port' + arglist = [ + '--port', port, + '--no-port', + ] + verifylist = [ + ('port', [port]), + ('no_port', True), + ] + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_create_with_ingress_policy_and_no(self): + policy = 'my-policy' + arglist = [ + '--ingress-firewall-policy', policy, + '--no-ingress-firewall-policy', + ] + verifylist = [ + ('ingress_firewall_policy', policy), + ('no_ingress_firewall_policy', True), + ] + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_create_with_egress_policy_and_no(self): + policy = 'my-policy' + arglist = [ + '--egress-firewall-policy', policy, + '--no-egress-firewall-policy', + ] + verifylist = [ + ('egress_firewall_policy', policy), + ('no_egress_firewall_policy', True), + ] + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + +class TestListFirewallGroup(TestFirewallGroup, common.TestListFWaaS): + + def setUp(self): + super(TestListFirewallGroup, self).setUp() + # Mock objects + self.neutronclient.list_fwaas_firewall_groups = mock.Mock( + return_value={self.res_plural: [_fwg]}) + self.mocked = self.neutronclient.list_fwaas_firewall_groups + self.cmd = firewallgroup.ListFirewallGroup(self.app, self.namespace) + + +class TestShowFirewallGroup(TestFirewallGroup, common.TestShowFWaaS): + + def setUp(self): + super(TestShowFirewallGroup, self).setUp() + # Mock objects + self.neutronclient.show_fwaas_firewall_group = mock.Mock( + return_value={self.res: _fwg}) + self.mocked = self.neutronclient.show_fwaas_firewall_group + self.cmd = firewallgroup.ShowFirewallGroup(self.app, self.namespace) + + +class TestSetFirewallGroup(TestFirewallGroup, common.TestSetFWaaS): + + def setUp(self): + super(TestSetFirewallGroup, self).setUp() + # Mock objects + _fwg['ports'] = ['old_port'] + self.neutronclient.update_fwaas_firewall_group = mock.Mock( + return_value={self.res: _fwg}) + self.mocked = self.neutronclient.update_fwaas_firewall_group + self.cmd = firewallgroup.SetFirewallGroup(self.app, self.namespace) + + def _update_expect_response(self, request, response): + """Set expected request and response + + :param request + A dictionary of request body(dict of verifylist) + :param response + A OrderedDict of request body + """ + osc_utils.find_project.return_value.id = response['tenant_id'] + # Update response(finally returns 'data') + self.data = _generate_response(ordered_dict=response) + self.ordered_data = tuple( + response[column] for column in self.ordered_columns + ) + + def test_set_ingress_policy(self): + # firewall_group-update myid --policy newpolicy. + target = self.resource['id'] + policy = 'ingress_policy' + arglist = [target, '--ingress-firewall-policy', policy] + verifylist = [ + (self.res, target), + ('ingress_firewall_policy', policy), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'ingress_firewall_policy_id': policy}}) + self.assertIsNone(result) + + def test_set_port(self): + target = self.resource['id'] + port1 = 'additional_port1' + port2 = 'additional_port2' + arglist = [ + target, + '--port', port1, + '--port', port2, + ] + verifylist = [ + (self.res, target), + ('port', [port1, port2]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + expect = {'ports': sorted(_fwg['ports'] + [port1, port2])} + self.mocked.assert_called_once_with(target, {self.res: expect}) + self.assertIsNone(result) + + def test_set_no_port(self): + # firewall_group-update myid --policy newpolicy. + target = self.resource['id'] + arglist = [target, '--no-port'] + verifylist = [ + (self.res, target), + ('no_port', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'ports': []}}) + self.assertIsNone(result) + + def test_set_admin_state(self): + target = self.resource['id'] + arglist = [target, '--enable'] + verifylist = [ + (self.res, target), + ('enable', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'admin_state_up': True}}) + self.assertIsNone(result) + + def test_set_egress_policy(self): + target = self.resource['id'] + policy = 'egress_policy' + arglist = [target, '--egress-firewall-policy', policy] + verifylist = [ + (self.res, target), + ('egress_firewall_policy', policy), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'egress_firewall_policy_id': policy}}) + self.assertIsNone(result) + + def test_set_no_ingress_policies(self): + target = self.resource['id'] + arglist = [target, '--no-ingress-firewall-policy'] + verifylist = [ + (self.res, target), + ('no_ingress_firewall_policy', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'ingress_firewall_policy_id': None}}) + self.assertIsNone(result) + + def test_set_no_egress_policies(self): + target = self.resource['id'] + arglist = [target, '--no-egress-firewall-policy'] + verifylist = [ + (self.res, target), + ('no_egress_firewall_policy', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'egress_firewall_policy_id': None}}) + self.assertIsNone(result) + + def test_set_port_and_no_port(self): + target = self.resource['id'] + port = 'my-port' + arglist = [ + target, + '--port', port, + '--no-port', + ] + verifylist = [ + (self.res, target), + ('port', [port]), + ('no_port', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.mocked.assert_called_once_with( + target, {self.res: {'ports': [port]}}) + self.assertIsNone(result) + + def test_set_ingress_policy_and_no_ingress_policy(self): + target = self.resource['id'] + arglist = [ + target, + '--ingress-firewall-policy', 'my-ingress', + '--no-ingress-firewall-policy', + ] + verifylist = [ + (self.res, target), + ('ingress_firewall_policy', 'my-ingress'), + ('no_ingress_firewall_policy', True), + ] + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_set_egress_policy_and_no_egress_policy(self): + target = self.resource['id'] + arglist = [ + target, + '--egress-firewall-policy', 'my-egress', + '--no-egress-firewall-policy', + ] + verifylist = [ + (self.res, target), + ('egress_firewall_policy', 'my-egress'), + ('no_egress_firewall_policy', True), + ] + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_set_and_raises(self): + self.neutronclient.update_fwaas_firewall_group = mock.Mock( + side_effect=Exception) + target = self.resource['id'] + arglist = [target, '--name', 'my-name'] + verifylist = [(self.res, target), ('name', 'my-name')] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args) + + +class TestDeleteFirewallGroup(TestFirewallGroup, common.TestDeleteFWaaS): + + def setUp(self): + super(TestDeleteFirewallGroup, self).setUp() + # Mock objects + self.neutronclient.delete_fwaas_firewall_group = mock.Mock() + self.mocked = self.neutronclient.delete_fwaas_firewall_group + self.cmd = firewallgroup.DeleteFirewallGroup(self.app, self.namespace) + + +class TestUnsetFirewallGroup(TestFirewallGroup, common.TestUnsetFWaaS): + + def setUp(self): + super(TestUnsetFirewallGroup, self).setUp() + _fwg['ports'] = ['old_port'] + # Mock objects + self.neutronclient.update_fwaas_firewall_group = mock.Mock() + self.mocked = self.neutronclient.update_fwaas_firewall_group + self.cmd = firewallgroup.UnsetFirewallGroup(self.app, self.namespace) + + def test_unset_ingress_policy(self): + target = self.resource['id'] + arglist = [ + target, + '--ingress-firewall-policy', + ] + verifylist = [ + (self.res, target), + ('ingress_firewall_policy', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.mocked.assert_called_once_with( + target, {self.res: {'ingress_firewall_policy_id': None}}) + self.assertIsNone(result) + + def test_unset_egress_policy(self): + target = self.resource['id'] + arglist = [ + target, + '--egress-firewall-policy', + ] + verifylist = [ + (self.res, target), + ('egress_firewall_policy', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.mocked.assert_called_once_with( + target, {self.res: {'egress_firewall_policy_id': None}}) + self.assertIsNone(result) + + def test_unset_enable(self): + target = self.resource['id'] + arglist = [ + target, + '--enable', + ] + verifylist = [ + (self.res, target), + ('enable', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.mocked.assert_called_once_with( + target, {self.res: {'admin_state_up': False}}) + self.assertIsNone(result) + + def test_unset_port(self): + target = self.resource['id'] + port = 'old_port' + arglist = [ + target, + '--port', port, + ] + verifylist = [ + (self.res, target), + ('port', [port]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.mocked.assert_called_once_with(target, {self.res: {'ports': []}}) + self.assertIsNone(result) + + def test_unset_all_port(self): + target = self.resource['id'] + arglist = [ + target, + '--all-port', + ] + verifylist = [ + (self.res, target), + ('all_port', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.mocked.assert_called_once_with(target, {self.res: {'ports': []}}) + self.assertIsNone(result) diff --git a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py new file mode 100644 index 000000000..fff4e912f --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py @@ -0,0 +1,599 @@ +# Copyright 2016 FUJITSU LIMITED +# 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 copy +import mock +import re + +from osc_lib import exceptions +from osc_lib.tests import utils + +from neutronclient.osc import utils as osc_utils +from neutronclient.osc.v2.fwaas import firewallpolicy +from neutronclient.tests.unit.osc.v2 import fakes as test_fakes +from neutronclient.tests.unit.osc.v2.fwaas import common +from neutronclient.tests.unit.osc.v2.fwaas import fakes + + +_fwp = fakes.FirewallPolicy().create() +CONVERT_MAP = { + 'public': 'public', + 'private': 'public', + 'project': 'tenant_id', + 'port': 'ports', + 'name': 'name', + 'id': 'id', + 'firewall_rule': 'firewall_rules', + 'description': 'description' +} + + +def _generate_data(ordered_dict=None, data=None): + source = ordered_dict if ordered_dict else _fwp + if data: + source.update(data) + return tuple(source[key] for key in source) + + +def _generate_req_and_res(verifylist): + request = dict(verifylist) + response = copy.deepcopy(_fwp) + for key, val in verifylist: + converted = CONVERT_MAP.get(key, key) + del request[key] + if re.match('^no_', key) and val is True: + new_value = None + elif key == 'enable' and val: + new_value = True + elif key == 'disable' and val: + new_value = False + else: + new_value = val + request[converted] = new_value + response[converted] = new_value + return request, response + + +class TestFirewallPolicy(test_fakes.TestNeutronClientOSCV2): + + def check_results(self, headers, data, exp_req, is_list=False): + if is_list: + req_body = {self.res_plural: [exp_req]} + else: + req_body = {self.res: exp_req} + self.mocked.assert_called_once_with(req_body) + self.assertEqual(self.ordered_headers, headers) + self.assertEqual(self.ordered_data, data) + + def setUp(self): + super(TestFirewallPolicy, self).setUp() + + def _find_resource(*args, **kwargs): + rule_id = args[1] + rules = [] + if self.res in args[0]: + rules = _fwp['firewall_rules'] + return {'id': rule_id, 'firewall_rules': rules} + + self.neutronclient.find_resource = mock.Mock( + side_effect=_find_resource) + # fw_common.get_id.side_effect = lambda x, y, z: z + osc_utils.find_project = mock.Mock() + osc_utils.find_project.id = _fwp['tenant_id'] + self.res = 'firewall_policy' + self.res_plural = 'firewall_policies' + self.resource = _fwp + self.list_headers = ( + 'ID', + 'Name', + 'Firewall Rules', + ) + self.list_data = ( + _fwp['id'], + _fwp['name'], + _fwp['firewall_rules'], + ) + self.headers = tuple(self.list_headers + ( + 'Description', + 'Audited', + 'Public', + 'Project') + ) + self.data = _generate_data() + self.ordered_headers = ( + 'Audited', + 'Description', + 'Firewall Rules', + 'ID', + 'Name', + 'Project', + 'Public', + ) + self.ordered_data = ( + _fwp['audited'], + _fwp['description'], + _fwp['firewall_rules'], + _fwp['id'], + _fwp['name'], + _fwp['tenant_id'], + _fwp['public'], + ) + self.ordered_columns = ( + 'audited', + 'description', + 'firewall_rules', + 'id', + 'name', + 'tenant_id', + 'public', + ) + + +class TestCreateFirewallPolicy(TestFirewallPolicy, common.TestCreateFWaaS): + + def setUp(self): + super(TestCreateFirewallPolicy, self).setUp() + self.neutronclient.create_fwaas_firewall_policy = mock.Mock( + return_value={self.res: _fwp}) + self.mocked = self.neutronclient.create_fwaas_firewall_policy + self.cmd = firewallpolicy.CreateFirewallPolicy(self.app, + self.namespace) + + def _update_expect_response(self, request, response): + """Set expected request and response + + :param request + A dictionary of request body(dict of verifylist) + :param response + A OrderedDict of request body + """ + # Update response body + self.neutronclient.create_fwaas_firewall_policy.return_value = \ + {self.res: dict(response)} + osc_utils.find_project.return_value.id = response['tenant_id'] + # Update response(finally returns 'data') + self.data = _generate_data(data=response) + self.ordered_data = tuple( + response[column] for column in self.ordered_columns + ) + + def test_create_with_no_options(self): + arglist = [] + verifylist = [] + + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_create_with_mandatory_param(self): + name = 'my-fwg' + arglist = [ + name, + ] + verifylist = [ + ('name', name), + ] + request, response = _generate_req_and_res(verifylist) + self._update_expect_response(request, response) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.check_results(headers, data, request) + + def test_create_with_all_params(self): + name = 'my-fwp' + desc = 'my-desc' + rule1 = 'rule1' + rule2 = 'rule2' + project = 'my-tenant' + arglist = [ + name, + '--description', desc, + '--firewall-rule', rule1, + '--firewall-rule', rule2, + '--project', project, + '--public', + '--audited', + ] + verifylist = [ + ('name', name), + ('description', desc), + ('firewall_rule', [rule1, rule2]), + ('project', project), + ('public', True), + ('audited', True), + ] + request, response = _generate_req_and_res(verifylist) + self._update_expect_response(request, response) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.check_results(headers, data, request) + + def test_create_with_firewall_rule_and_no(self): + name = 'my-fwp' + rule1 = 'rule1' + rule2 = 'rule2' + arglist = [ + name, + '--firewall-rule', rule1, + '--firewall-rule', rule2, + '--no-firewall-rule', + ] + verifylist = [ + ('name', name), + ('firewall_rule', [rule1, rule2]), + ('no_firewall_rule', True), + ] + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_create_with_public_and_private(self): + name = 'my-fwp' + arglist = [ + name, + '--public', + '--private', + ] + verifylist = [ + ('name', name), + ('public', True), + ('private', True), + ] + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_create_with_audited_and_no(self): + name = 'my-fwp' + arglist = [ + name, + '--audited', + '--no-audited', + ] + verifylist = [ + ('name', name), + ('audited', True), + ('no_audited', True), + ] + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + +class TestListFirewallPolicy(TestFirewallPolicy, common.TestListFWaaS): + + def setUp(self): + super(TestListFirewallPolicy, self).setUp() + self.neutronclient.list_fwaas_firewall_policies = mock.Mock( + return_value={'firewall_policies': [_fwp]}) + self.mocked = self.neutronclient.list_fwaas_firewall_policies + self.cmd = firewallpolicy.ListFirewallPolicy(self.app, self.namespace) + + +class TestShowFirewallPolicy(TestFirewallPolicy, common.TestShowFWaaS): + + def setUp(self): + super(TestShowFirewallPolicy, self).setUp() + self.neutronclient.show_fwaas_firewall_policy = mock.Mock( + return_value={self.res: _fwp}) + self.mocked = self.neutronclient.show_fwaas_firewall_policy + self.cmd = firewallpolicy.ShowFirewallPolicy(self.app, self.namespace) + + +class TestSetFirewallPolicy(TestFirewallPolicy, common.TestSetFWaaS): + + def setUp(self): + super(TestSetFirewallPolicy, self).setUp() + self.neutronclient.update_fwaas_firewall_policy = mock.Mock( + return_value={self.res: _fwp}) + self.mocked = self.neutronclient.update_fwaas_firewall_policy + self.cmd = firewallpolicy.SetFirewallPolicy(self.app, self.namespace) + + def test_set_rules(self): + target = self.resource['id'] + rule1 = 'new_rule1' + rule2 = 'new_rule2' + arglist = [ + target, + '--firewall-rule', rule1, + '--firewall-rule', rule2, + ] + verifylist = [ + (self.res, target), + ('firewall_rule', [rule1, rule2]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + expect = sorted(set(_fwp['firewall_rules'] + [rule1, rule2])) + body = {self.res: {'firewall_rules': expect}} + self.mocked.assert_called_once_with(target, body) + self.assertIsNone(result) + + def test_set_no_rules(self): + target = self.resource['id'] + arglist = [target, '--no-firewall-rule'] + verifylist = [ + (self.res, target), + ('no_firewall_rule', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + body = {self.res: {'firewall_rules': []}} + self.mocked.assert_called_once_with(target, body) + self.assertIsNone(result) + + def test_set_rules_and_no_rules(self): + target = self.resource['id'] + rule1 = 'rule1' + arglist = [ + target, + '--firewall-rule', rule1, + '--no-firewall-rule', + ] + verifylist = [ + (self.res, target), + ('firewall_rule', [rule1]), + ('no_firewall_rule', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + body = {self.res: {'firewall_rules': [rule1]}} + self.mocked.assert_called_once_with(target, body) + self.assertIsNone(result) + + def test_set_audited(self): + target = self.resource['id'] + arglist = [target, '--audited'] + verifylist = [ + (self.res, target), + ('audited', True), + ] + body = {'audited': True} + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with(target, {self.res: body}) + self.assertIsNone(result) + + def test_set_no_audited(self): + target = self.resource['id'] + arglist = [target, '--no-audited'] + verifylist = [ + (self.res, target), + ('no_audited', True), + ] + body = {'audited': False} + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with(target, {self.res: body}) + self.assertIsNone(result) + + def test_set_audited_and_no_audited(self): + target = self.resource['id'] + arglist = [ + target, + '--audited', + '--no-audited', + ] + verifylist = [ + (self.res, target), + ('audited', True), + ('no_audited', True), + ] + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_set_and_raises(self): + self.neutronclient.update_fwaas_firewall_policy = mock.Mock( + side_effect=Exception) + target = self.resource['id'] + arglist = [target, '--name', 'my-name'] + verifylist = [(self.res, target), ('name', 'my-name')] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args) + + +class TestDeleteFirewallPolicy(TestFirewallPolicy, common.TestDeleteFWaaS): + + def setUp(self): + super(TestDeleteFirewallPolicy, self).setUp() + self.neutronclient.delete_fwaas_firewall_policy = mock.Mock( + return_value={self.res: _fwp}) + self.mocked = self.neutronclient.delete_fwaas_firewall_policy + self.cmd = firewallpolicy.DeleteFirewallPolicy( + self.app, self.namespace) + + +class TestFirewallPolicyInsertRule(TestFirewallPolicy): + + def setUp(self): + super(TestFirewallPolicyInsertRule, self).setUp() + self.neutronclient.insert_rule_fwaas_firewall_policy = mock.Mock( + return_value={self.res: _fwp}) + self.mocked = self.neutronclient.insert_rule_fwaas_firewall_policy + self.cmd = firewallpolicy.FirewallPolicyInsertRule(self.app, + self.namespace) + + def test_insert_firewall_rule(self): + target = self.resource['id'] + rule = 'new-rule' + before = 'before' + after = 'after' + arglist = [ + target, + rule, + '--insert-before', before, + '--insert-after', after, + ] + verifylist = [ + (self.res, target), + ('firewall_rule', rule), + ('insert_before', before), + ('insert_after', after), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, { + 'firewall_rule_id': rule, + 'insert_before': before, + 'insert_after': after + }) + self.assertIsNone(result) + + def test_insert_with_no_firewall_rule(self): + target = self.resource['id'] + arglist = [ + target, + ] + verifylist = [ + (self.res, target), + ] + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + +class TestFirewallPolicyRemoveRule(TestFirewallPolicy): + + def setUp(self): + super(TestFirewallPolicyRemoveRule, self).setUp() + self.neutronclient.remove_rule_fwaas_firewall_policy = mock.Mock( + return_value={self.res: _fwp}) + self.mocked = self.neutronclient.remove_rule_fwaas_firewall_policy + self.cmd = firewallpolicy.FirewallPolicyRemoveRule(self.app, + self.namespace) + + def test_remove_firewall_rule(self): + target = self.resource['id'] + rule = 'remove-rule' + arglist = [ + target, + rule, + ] + verifylist = [ + (self.res, target), + ('firewall_rule', rule), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.mocked.assert_called_once_with( + target, {'firewall_rule_id': rule}) + self.assertIsNone(result) + + def test_remove_with_no_firewall_rule(self): + target = self.resource['id'] + arglist = [ + target, + ] + verifylist = [ + (self.res, target), + ] + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + +class TestUnsetFirewallPolicy(TestFirewallPolicy, common.TestUnsetFWaaS): + + def setUp(self): + super(TestUnsetFirewallPolicy, self).setUp() + self.neutronclient.update_fwaas_firewall_policy = mock.Mock( + return_value={self.res: _fwp}) + self.mocked = self.neutronclient.update_fwaas_firewall_policy + self.cmd = firewallpolicy.UnsetFirewallPolicy(self.app, self.namespace) + + def test_unset_audited(self): + target = self.resource['id'] + arglist = [ + target, + '--audited', + ] + verifylist = [ + (self.res, target), + ('audited', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + body = {self.res: {'audited': False}} + self.mocked.assert_called_once_with(target, body) + self.assertIsNone(result) + + def test_unset_firewall_rule_not_matched(self): + _fwp['firewall_rules'] = ['old_rule'] + target = self.resource['id'] + rule = 'new_rule' + arglist = [ + target, + '--firewall-rule', rule, + ] + verifylist = [ + (self.res, target), + ('firewall_rule', [rule]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + body = {self.res: {'firewall_rules': _fwp['firewall_rules']}} + self.mocked.assert_called_once_with(target, body) + self.assertIsNone(result) + + def test_unset_firewall_rule_matched(self): + _fwp['firewall_rules'] = ['rule1', 'rule2'] + target = self.resource['id'] + rule = 'rule1' + arglist = [ + target, + '--firewall-rule', rule, + ] + verifylist = [ + (self.res, target), + ('firewall_rule', [rule]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + body = {self.res: {'firewall_rules': ['rule2']}} + self.mocked.assert_called_once_with(target, body) + self.assertIsNone(result) + + def test_unset_all_firewall_rule(self): + target = self.resource['id'] + arglist = [ + target, + '--all-firewall-rule', + ] + verifylist = [ + (self.res, target), + ('all_firewall_rule', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + body = {self.res: {'firewall_rules': []}} + self.mocked.assert_called_once_with(target, body) + self.assertIsNone(result) diff --git a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py new file mode 100644 index 000000000..dd730ee49 --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py @@ -0,0 +1,736 @@ +# Copyright 2016 FUJITSU LIMITED +# 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 copy +import mock +import re +import testtools + +from osc_lib import exceptions +from osc_lib.tests import utils + +from neutronclient.osc import utils as osc_utils +from neutronclient.osc.v2.fwaas import firewallrule +from neutronclient.tests.unit.osc.v2 import fakes as test_fakes +from neutronclient.tests.unit.osc.v2.fwaas import common +from neutronclient.tests.unit.osc.v2.fwaas import fakes + + +_fwr = fakes.FirewallRule().create() +CONVERT_MAP = { + 'project': 'tenant_id', + 'enable_rule': 'enabled', + 'disable_rule': 'enabled', +} + + +def _generate_data(ordered_dict=None, data=None): + source = ordered_dict if ordered_dict else _fwr + if data: + source.update(data) + return tuple(source[key] for key in source) + + +def _generate_req_and_res(verifylist): + request = dict(verifylist) + response = copy.deepcopy(_fwr) + for key, val in verifylist: + converted = CONVERT_MAP.get(key, key) + del request[key] + if re.match('^no_', key) and val is True: + new_value = None + elif (key == 'enable' or key == 'enable_rule') and val: + new_value = True + elif (key == 'disable' or key == 'disable_rule') and val: + new_value = False + else: + new_value = val + request[converted] = new_value + response[converted] = new_value + return request, response + + +class TestFirewallRule(test_fakes.TestNeutronClientOSCV2): + + def check_results(self, headers, data, exp_req, is_list=False): + if is_list: + req_body = {self.res_plural: [exp_req]} + else: + req_body = {self.res: exp_req} + self.mocked.assert_called_once_with(req_body) + self.assertEqual(self.ordered_headers, headers) + self.assertEqual(self.ordered_data, data) + + def setUp(self): + super(TestFirewallRule, self).setUp() + self.neutronclient.find_resource = mock.Mock() + self.neutronclient.find_resource.side_effect = \ + lambda x, y, **k: {'id': y} + # fw_common.get_id.side_effect = lambda x, y, z: z + osc_utils.find_project = mock.Mock() + osc_utils.find_project.id = _fwr['tenant_id'] + self.res = 'firewall_rule' + self.res_plural = 'firewall_rules' + self.resource = _fwr + self.headers = ( + 'ID', + 'Name', + 'Enabled', + 'Description', + 'IP Version', + 'Action', + 'Protocol', + 'Source IP Address', + 'Source Port', + 'Destination IP Address', + 'Destination Port', + 'Public', + 'Project', + ) + self.data = _generate_data() + self.ordered_headers = ( + 'Action', + 'Description', + 'Destination IP Address', + 'Destination Port', + 'Enabled', + 'ID', + 'IP Version', + 'Name', + 'Project', + 'Protocol', + 'Public', + 'Source IP Address', + 'Source Port', + ) + self.ordered_data = ( + _fwr['action'], + _fwr['description'], + _fwr['destination_ip_address'], + _fwr['destination_port'], + _fwr['enabled'], + _fwr['id'], + _fwr['ip_version'], + _fwr['name'], + _fwr['tenant_id'], + _fwr['protocol'], + _fwr['public'], + _fwr['source_ip_address'], + _fwr['source_port'], + ) + self.ordered_columns = ( + 'action', + 'description', + 'destination_ip_address', + 'destination_port', + 'enabled', + 'id', + 'ip_version', + 'name', + 'tenant_id', + 'protocol', + 'public', + 'source_ip_address', + 'source_port', + ) + + +class TestCreateFirewallRule(TestFirewallRule, common.TestCreateFWaaS): + + def setUp(self): + super(TestCreateFirewallRule, self).setUp() + self.neutronclient.create_fwaas_firewall_rule = mock.Mock( + return_value={self.res: _fwr}) + self.mocked = self.neutronclient.create_fwaas_firewall_rule + self.cmd = firewallrule.CreateFirewallRule(self.app, self.namespace) + + def _update_expect_response(self, request, response): + """Set expected request and response + + :param request + A dictionary of request body(dict of verifylist) + :param response + A OrderedDict of request body + """ + # Update response body + self.neutronclient.create_fwaas_firewall_rule.return_value = \ + {self.res: dict(response)} + osc_utils.find_project.return_value.id = response['tenant_id'] + # Update response(finally returns 'data') + self.data = _generate_data(ordered_dict=response) + self.ordered_data = tuple( + response[column] for column in self.ordered_columns + ) + + def _set_all_params(self, args={}): + name = args.get('name') or 'my-name' + description = args.get('description') or 'my-desc' + source_ip = args.get('source_ip_address') or '192.168.1.0/24' + destination_ip = args.get('destination_ip_address') or '192.168.2.0/24' + source_port = args.get('source_port') or '0:65535' + protocol = args.get('protocol') or 'udp' + action = args.get('action') or 'deny' + ip_version = args.get('ip_version') or '4' + destination_port = args.get('destination_port') or '0:65535' + tenant_id = args.get('tenant_id') or 'my-tenant' + arglist = [ + '--description', description, + '--name', name, + '--protocol', protocol, + '--ip-version', ip_version, + '--source-ip-address', source_ip, + '--destination-ip-address', destination_ip, + '--source-port', source_port, + '--destination-port', destination_port, + '--action', action, + '--project', tenant_id, + '--disable-rule', + '--public', + ] + verifylist = [ + ('name', name), + ('description', description), + ('public', True), + ('protocol', protocol), + ('ip_version', ip_version), + ('source_ip_address', source_ip), + ('destination_ip_address', destination_ip), + ('source_port', source_port), + ('destination_port', destination_port), + ('action', action), + ('disable_rule', True), + ('project', tenant_id), + ] + return arglist, verifylist + + def _test_create_with_all_params(self, args={}): + arglist, verifylist = self._set_all_params(args) + request, response = _generate_req_and_res(verifylist) + self._update_expect_response(request, response) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.check_results(headers, data, request) + + def test_create_with_no_options(self): + arglist = [] + verifylist = [] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + self.check_results(headers, data, {}) + + def test_create_with_all_params(self): + self._test_create_with_all_params() + + def test_create_with_all_params_protocol_any(self): + self._test_create_with_all_params({'protocol': 'any'}) + + def test_create_with_all_params_ip_version_6(self): + self._test_create_with_all_params({'ip_version': '6'}) + + def test_create_with_all_params_invalid_ip_version(self): + arglist, verifylist = self._set_all_params({'ip_version': '128'}) + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_create_with_all_params_action_upper_capitalized(self): + for action in ('Allow', 'DENY', 'Reject'): + arglist, verifylist = self._set_all_params({'action': action}) + self.assertRaises( + testtools.matchers._impl.MismatchError, + self.check_parser, self.cmd, arglist, verifylist) + + def test_create_with_all_params_protocol_upper_capitalized(self): + for protocol in ('TCP', 'Tcp', 'ANY', 'AnY'): + arglist, verifylist = self._set_all_params({'protocol': protocol}) + self.assertRaises( + testtools.matchers._impl.MismatchError, + self.check_parser, self.cmd, arglist, verifylist) + + +class TestListFirewallRule(TestFirewallRule): + + def _setup_summary(self, expect=None): + protocol = _fwr['protocol'].upper() + src = 'source(port): 192.168.1.0/24(1:11111)' + dst = 'dest(port): 192.168.2.2(2:22222)' + action = 'deny' + if expect: + if expect.get('protocol'): + protocol = expect['protocol'] + if expect.get('source'): + src = expect['source'] + if expect.get('dest'): + dst = expect['dest'] + if expect.get('action'): + action = expect['action'] + summary = ',\n '.join([protocol, src, dst, action]) + self.short_data = ( + _fwr['id'], + _fwr['name'], + _fwr['enabled'], + summary + ) + + def setUp(self): + super(TestListFirewallRule, self).setUp() + self.cmd = firewallrule.ListFirewallRule(self.app, self.namespace) + + self.short_header = ( + 'ID', + 'Name', + 'Enabled', + 'Summary', + ) + self._setup_summary() + self.neutronclient.list_fwaas_firewall_rules = mock.Mock( + return_value={self.res_plural: [_fwr]}) + self.mocked = self.neutronclient.list_fwaas_firewall_rules + + def test_list_with_long_option(self): + arglist = ['--long'] + verifylist = [('long', True)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.headers), headers) + self.assertEqual([self.data], list(data)) + + def test_list_with_no_option(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.short_header), headers) + self.assertEqual([self.short_data], list(data)) + + +class TestShowFirewallRule(TestFirewallRule, common.TestShowFWaaS): + + def setUp(self): + super(TestShowFirewallRule, self).setUp() + self.neutronclient.show_fwaas_firewall_rule = mock.Mock( + return_value={self.res: _fwr}) + self.mocked = self.neutronclient.show_fwaas_firewall_rule + self.cmd = firewallrule.ShowFirewallRule(self.app, self.namespace) + + +class TestSetFirewallRule(TestFirewallRule, common.TestSetFWaaS): + + def setUp(self): + super(TestSetFirewallRule, self).setUp() + self.neutronclient.update_fwaas_firewall_rule = mock.Mock( + return_value={self.res: _fwr}) + self.mocked = self.neutronclient.update_fwaas_firewall_rule + self.cmd = firewallrule.SetFirewallRule(self.app, self.namespace) + + def test_set_protocol_with_any(self): + target = self.resource['id'] + protocol = 'any' + arglist = [target, '--protocol', protocol] + verifylist = [ + (self.res, target), + ('protocol', protocol), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'protocol': protocol}}) + self.assertIsNone(result) + + def test_set_protocol_with_udp(self): + target = self.resource['id'] + protocol = 'udp' + arglist = [target, '--protocol', protocol] + verifylist = [ + (self.res, target), + ('protocol', protocol), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'protocol': protocol}}) + self.assertIsNone(result) + + def test_set_source_ip_address(self): + target = self.resource['id'] + src_ip = '192.192.192.192' + arglist = [target, '--source-ip-address', src_ip] + verifylist = [ + (self.res, target), + ('source_ip_address', src_ip), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'source_ip_address': src_ip}}) + self.assertIsNone(result) + + def test_set_source_port(self): + target = self.resource['id'] + src_port = '32678' + arglist = [target, '--source-port', src_port] + verifylist = [ + (self.res, target), + ('source_port', src_port), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'source_port': src_port}}) + self.assertIsNone(result) + + def test_set_destination_ip_address(self): + target = self.resource['id'] + dst_ip = '0.1.0.1' + arglist = [target, '--destination-ip-address', dst_ip] + verifylist = [ + (self.res, target), + ('destination_ip_address', dst_ip), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'destination_ip_address': dst_ip}}) + self.assertIsNone(result) + + def test_set_destination_port(self): + target = self.resource['id'] + dst_port = '65432' + arglist = [target, '--destination-port', dst_port] + verifylist = [ + (self.res, target), + ('destination_port', dst_port), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'destination_port': dst_port}}) + self.assertIsNone(result) + + def test_set_enable_rule(self): + target = self.resource['id'] + arglist = [target, '--enable-rule'] + verifylist = [ + (self.res, target), + ('enable_rule', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'enabled': True}}) + self.assertIsNone(result) + + def test_set_disable_rule(self): + target = self.resource['id'] + arglist = [target, '--disable-rule'] + verifylist = [ + (self.res, target), + ('disable_rule', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'enabled': False}}) + self.assertIsNone(result) + + def test_set_action(self): + target = self.resource['id'] + action = 'reject' + arglist = [target, '--action', action] + verifylist = [ + (self.res, target), + ('action', action), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'action': action}}) + self.assertIsNone(result) + + def test_set_enable_rule_and_disable_rule(self): + target = self.resource['id'] + arglist = [target, '--enable-rule', '--disable-rule'] + verifylist = [ + (self.res, target), + ('enable_rule', True), + ('disable_rule', True), + ] + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_set_no_source_ip_address(self): + target = self.resource['id'] + arglist = [ + target, + '--no-source-ip-address', + ] + verifylist = [ + (self.res, target), + ('no_source_ip_address', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'source_ip_address': None}}) + self.assertIsNone(result) + + def test_set_no_source_port(self): + target = self.resource['id'] + arglist = [ + target, + '--no-source-port', + ] + verifylist = [ + (self.res, target), + ('no_source_port', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'source_port': None}}) + self.assertIsNone(result) + + def test_set_no_destination_ip_address(self): + target = self.resource['id'] + arglist = [ + target, + '--no-destination-ip-address', + ] + verifylist = [ + (self.res, target), + ('no_destination_ip_address', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'destination_ip_address': None}}) + self.assertIsNone(result) + + def test_set_no_destination_port(self): + target = self.resource['id'] + arglist = [ + target, + '--no-destination-port', + ] + verifylist = [ + (self.res, target), + ('no_destination_port', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'destination_port': None}}) + self.assertIsNone(result) + + def test_set_source_ip_address_and_no(self): + target = self.resource['id'] + arglist = [ + target, + '--source-ip-address', '192.168.1.0/24', + '--no-source-ip-address', + ] + verifylist = [ + (self.res, target), + ('source_ip_address', '192.168.1.0/24'), + ('no_source_ip_address', True), + ] + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_set_destination_ip_address_and_no(self): + target = self.resource['id'] + arglist = [ + target, + '--destination-ip-address', '192.168.2.0/24', + '--no-destination-ip-address', + ] + verifylist = [ + (self.res, target), + ('destination_ip_address', '192.168.2.0/24'), + ('no_destination_ip_address', True), + ] + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_set_source_port_and_no(self): + target = self.resource['id'] + arglist = [ + target, + '--source-port', '1:12345', + '--no-source-port', + ] + verifylist = [ + (self.res, target), + ('source_port', '1:12345'), + ('no_source_port', True), + ] + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_set_destination_port_and_no(self): + target = self.resource['id'] + arglist = [ + target, + '--destination-port', '1:54321', + '--no-destination-port', + ] + verifylist = [ + (self.res, target), + ('destination_port', '1:54321'), + ('no_destination_port', True), + ] + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_set_and_raises(self): + self.neutronclient.update_fwaas_firewall_rule = mock.Mock( + side_effect=Exception) + target = self.resource['id'] + arglist = [target, '--name', 'my-name'] + verifylist = [(self.res, target), ('name', 'my-name')] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args) + + +class TestUnsetFirewallRule(TestFirewallRule, common.TestUnsetFWaaS): + + def setUp(self): + super(TestUnsetFirewallRule, self).setUp() + self.neutronclient.update_fwaas_firewall_rule = mock.Mock( + return_value={self.res: _fwr}) + self.mocked = self.neutronclient.update_fwaas_firewall_rule + self.cmd = firewallrule.UnsetFirewallRule(self.app, self.namespace) + + def test_unset_source_port(self): + target = self.resource['id'] + arglist = [ + target, + '--source-port', + ] + verifylist = [ + (self.res, target), + ('source_port', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'source_port': None}}) + self.assertIsNone(result) + + def test_unset_destination_port(self): + target = self.resource['id'] + arglist = [ + target, + '--destination-port', + ] + verifylist = [ + (self.res, target), + ('destination_port', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'destination_port': None}}) + self.assertIsNone(result) + + def test_unset_source_ip_address(self): + target = self.resource['id'] + arglist = [ + target, + '--source-ip-address', + ] + verifylist = [ + (self.res, target), + ('source_ip_address', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'source_ip_address': None}}) + self.assertIsNone(result) + + def test_unset_destination_ip_address(self): + target = self.resource['id'] + arglist = [ + target, + '--destination-ip-address', + ] + verifylist = [ + (self.res, target), + ('destination_ip_address', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'destination_ip_address': None}}) + self.assertIsNone(result) + + def test_unset_enable_rule(self): + target = self.resource['id'] + arglist = [ + target, + '--enable-rule', + ] + verifylist = [ + (self.res, target), + ('enable_rule', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'enabled': False}}) + self.assertIsNone(result) + + +class TestDeleteFirewallRule(TestFirewallRule, common.TestDeleteFWaaS): + + def setUp(self): + super(TestDeleteFirewallRule, self).setUp() + self.neutronclient.delete_fwaas_firewall_rule = mock.Mock( + return_value={self.res: _fwr}) + self.mocked = self.neutronclient.delete_fwaas_firewall_rule + self.cmd = firewallrule.DeleteFirewallRule(self.app, self.namespace) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 374f87391..e20e94a31 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -588,6 +588,16 @@ class Client(ClientBase): firewall_policy_remove_path = "/fw/firewall_policies/%s/remove_rule" firewalls_path = "/fw/firewalls" firewall_path = "/fw/firewalls/%s" + fwaas_firewall_groups_path = "/fwaas/firewall_groups" + fwaas_firewall_group_path = "/fwaas/firewall_groups/%s" + fwaas_firewall_rules_path = "/fwaas/firewall_rules" + fwaas_firewall_rule_path = "/fwaas/firewall_rules/%s" + fwaas_firewall_policies_path = "/fwaas/firewall_policies" + fwaas_firewall_policy_path = "/fwaas/firewall_policies/%s" + fwaas_firewall_policy_insert_path = \ + "/fwaas/firewall_policies/%s/insert_rule" + fwaas_firewall_policy_remove_path = \ + "/fwaas/firewall_policies/%s/remove_rule" rbac_policies_path = "/rbac-policies" rbac_policy_path = "/rbac-policies/%s" qos_policies_path = "/qos/policies" @@ -649,6 +659,9 @@ class Client(ClientBase): 'firewall_rules': 'firewall_rule', 'firewall_policies': 'firewall_policy', 'firewalls': 'firewall', + 'fwaas_firewall_rules': 'fwaas_firewall_rule', + 'fwaas_firewall_policies': 'fwaas_firewall_policy', + 'fwaas_firewall_groups': 'fwaas_firewall_group', 'metering_labels': 'metering_label', 'metering_label_rules': 'metering_label_rule', 'loadbalancers': 'loadbalancer', @@ -1572,6 +1585,87 @@ def delete_firewall(self, firewall): """Deletes the specified firewall.""" return self.delete(self.firewall_path % (firewall)) + def list_fwaas_firewall_groups(self, retrieve_all=True, **_params): + """Fetches a list of all firewall groups for a project""" + return self.list('firewall_groups', self.fwaas_firewall_groups_path, + retrieve_all, **_params) + + def show_fwaas_firewall_group(self, fwg, **_params): + """Fetches information of a certain firewall group""" + return self.get(self.fwaas_firewall_group_path % (fwg), params=_params) + + def create_fwaas_firewall_group(self, body=None): + """Creates a new firewall group""" + return self.post(self.fwaas_firewall_groups_path, body=body) + + def update_fwaas_firewall_group(self, fwg, body=None): + """Updates a firewall group""" + return self.put(self.fwaas_firewall_group_path % (fwg), body=body) + + def delete_fwaas_firewall_group(self, fwg): + """Deletes the specified firewall group""" + return self.delete(self.fwaas_firewall_group_path % (fwg)) + + def list_fwaas_firewall_rules(self, retrieve_all=True, **_params): + """Fetches a list of all firewall rules for a project""" + # Pass filters in "params" argument to do_request + return self.list('firewall_rules', self.fwaas_firewall_rules_path, + retrieve_all, **_params) + + def show_fwaas_firewall_rule(self, firewall_rule, **_params): + """Fetches information of a certain firewall rule""" + return self.get(self.fwaas_firewall_rule_path % (firewall_rule), + params=_params) + + def create_fwaas_firewall_rule(self, body=None): + """Creates a new firewall rule""" + return self.post(self.fwaas_firewall_rules_path, body=body) + + def update_fwaas_firewall_rule(self, firewall_rule, body=None): + """Updates a firewall rule""" + return self.put(self.fwaas_firewall_rule_path % (firewall_rule), + body=body) + + def delete_fwaas_firewall_rule(self, firewall_rule): + """Deletes the specified firewall rule""" + return self.delete(self.fwaas_firewall_rule_path % (firewall_rule)) + + def list_fwaas_firewall_policies(self, retrieve_all=True, **_params): + """Fetches a list of all firewall policies for a project""" + # Pass filters in "params" argument to do_request + + return self.list('firewall_policies', + self.fwaas_firewall_policies_path, + retrieve_all, **_params) + + def show_fwaas_firewall_policy(self, firewall_policy, **_params): + """Fetches information of a certain firewall policy""" + return self.get(self.fwaas_firewall_policy_path % (firewall_policy), + params=_params) + + def create_fwaas_firewall_policy(self, body=None): + """Creates a new firewall policy""" + return self.post(self.fwaas_firewall_policies_path, body=body) + + def update_fwaas_firewall_policy(self, firewall_policy, body=None): + """Updates a firewall policy""" + return self.put(self.fwaas_firewall_policy_path % (firewall_policy), + body=body) + + def delete_fwaas_firewall_policy(self, firewall_policy): + """Deletes the specified firewall policy""" + return self.delete(self.fwaas_firewall_policy_path % (firewall_policy)) + + def insert_rule_fwaas_firewall_policy(self, firewall_policy, body=None): + """Inserts specified rule into firewall policy""" + return self.put((self.fwaas_firewall_policy_insert_path % + (firewall_policy)), body=body) + + def remove_rule_fwaas_firewall_policy(self, firewall_policy, body=None): + """Removes specified rule from firewall policy""" + return self.put((self.fwaas_firewall_policy_remove_path % + (firewall_policy)), body=body) + def remove_router_from_l3_agent(self, l3_agent, router_id): """Remove a router from l3 agent.""" return self.delete((self.agent_path + self.L3_ROUTERS + "/%s") % ( diff --git a/releasenotes/notes/support-fwaasv2-cli-7f21676c551f8ae0.yaml b/releasenotes/notes/support-fwaasv2-cli-7f21676c551f8ae0.yaml new file mode 100644 index 000000000..2032bfa63 --- /dev/null +++ b/releasenotes/notes/support-fwaasv2-cli-7f21676c551f8ae0.yaml @@ -0,0 +1,4 @@ +--- +features: + - CLI support for the "Firewall as a Service v2" feature, which is enhanced + FWaaS functionality, as OSC plugin commands. diff --git a/setup.cfg b/setup.cfg index 73c1e65ee..041ff1ac0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -44,6 +44,27 @@ openstack.neutronclient.v2 = network_trunk_show = neutronclient.osc.v2.trunk.network_trunk:ShowNetworkTrunk network_trunk_unset = neutronclient.osc.v2.trunk.network_trunk:UnsetNetworkTrunk + firewall_group_create = neutronclient.osc.v2.fwaas.firewallgroup:CreateFirewallGroup + firewall_group_delete = neutronclient.osc.v2.fwaas.firewallgroup:DeleteFirewallGroup + firewall_group_list = neutronclient.osc.v2.fwaas.firewallgroup:ListFirewallGroup + firewall_group_set = neutronclient.osc.v2.fwaas.firewallgroup:SetFirewallGroup + firewall_group_show = neutronclient.osc.v2.fwaas.firewallgroup:ShowFirewallGroup + firewall_group_unset = neutronclient.osc.v2.fwaas.firewallgroup:UnsetFirewallGroup + firewall_group_policy_create = neutronclient.osc.v2.fwaas.firewallpolicy:CreateFirewallPolicy + firewall_group_policy_delete = neutronclient.osc.v2.fwaas.firewallpolicy:DeleteFirewallPolicy + firewall_group_policy_add_rule = neutronclient.osc.v2.fwaas.firewallpolicy:FirewallPolicyInsertRule + firewall_group_policy_list = neutronclient.osc.v2.fwaas.firewallpolicy:ListFirewallPolicy + firewall_group_policy_remove_rule = neutronclient.osc.v2.fwaas.firewallpolicy:FirewallPolicyRemoveRule + firewall_group_policy_set = neutronclient.osc.v2.fwaas.firewallpolicy:SetFirewallPolicy + firewall_group_policy_show = neutronclient.osc.v2.fwaas.firewallpolicy:ShowFirewallPolicy + firewall_group_policy_unset = neutronclient.osc.v2.fwaas.firewallpolicy:UnsetFirewallPolicy + firewall_group_rule_create = neutronclient.osc.v2.fwaas.firewallrule:CreateFirewallRule + firewall_group_rule_delete = neutronclient.osc.v2.fwaas.firewallrule:DeleteFirewallRule + firewall_group_rule_list = neutronclient.osc.v2.fwaas.firewallrule:ListFirewallRule + firewall_group_rule_set = neutronclient.osc.v2.fwaas.firewallrule:SetFirewallRule + firewall_group_rule_show = neutronclient.osc.v2.fwaas.firewallrule:ShowFirewallRule + firewall_group_rule_unset = neutronclient.osc.v2.fwaas.firewallrule:UnsetFirewallRule + [build_sphinx] all_files = 1 build-dir = doc/build From 0fa3242d2c8a7e51054fb23521acd109706cc604 Mon Sep 17 00:00:00 2001 From: shenjibiao Date: Fri, 6 Jan 2017 17:42:57 +0800 Subject: [PATCH 537/845] The testcase's name may be inapposite. It is better to use 'delete', instead of 'delele'. Change-Id: Id7974a2866641aa8a5e3cc20a35e09e0f86f9283 --- neutronclient/tests/functional/core/test_clientlib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/tests/functional/core/test_clientlib.py b/neutronclient/tests/functional/core/test_clientlib.py index f95f519b8..23ba2d425 100644 --- a/neutronclient/tests/functional/core/test_clientlib.py +++ b/neutronclient/tests/functional/core/test_clientlib.py @@ -94,7 +94,7 @@ def test_list_network(self): nets = self.client.list_networks() self.assertIsInstance(nets['networks'], list) - def test_post_put_delele_network(self): + def test_post_put_delete_network(self): name = str(uuid.uuid4()) net = self.client.create_network({'network': {'name': name}}) net_id = net['network']['id'] From 6fb4a728ca98bcb09ed1902dd1a1fcf908ed9a60 Mon Sep 17 00:00:00 2001 From: Yushiro FURUKAWA Date: Fri, 30 Sep 2016 19:35:14 +0900 Subject: [PATCH 538/845] Add documentation for FWaaS v2 OSC plugin commands This commit adds a Firewall-as-a-Service v2[1] CLI usage. [1]http://specs.openstack.org/openstack/neutron-specs/specs/newton/fwaas-api-2.0.html Change-Id: Ib271acad29229d78beebc2f1c2bd285c630768df Partial-Implements: blueprint fwaas-api-2.0 Related-Bug: #1609686 --- doc/source/usage/osc/v2/firewall-group.rst | 230 +++++++++++++++ doc/source/usage/osc/v2/firewall-policy.rst | 274 ++++++++++++++++++ doc/source/usage/osc/v2/firewall-rule.rst | 304 ++++++++++++++++++++ 3 files changed, 808 insertions(+) create mode 100644 doc/source/usage/osc/v2/firewall-group.rst create mode 100644 doc/source/usage/osc/v2/firewall-policy.rst create mode 100644 doc/source/usage/osc/v2/firewall-rule.rst diff --git a/doc/source/usage/osc/v2/firewall-group.rst b/doc/source/usage/osc/v2/firewall-group.rst new file mode 100644 index 000000000..b0c24a884 --- /dev/null +++ b/doc/source/usage/osc/v2/firewall-group.rst @@ -0,0 +1,230 @@ +============== +firewall group +============== + +A **firewall group** is a perimeter firewall management to Networking. +Firewall group uses iptables to apply firewall policy to all VM ports and +router ports within a project. + +Network v2 + +firewall group create +--------------------- + +Create a firewall group for a given project. + +.. program:: firewall group create +.. code:: bash + + openstack firewall group create + +.. _firewallgroup_create-firewallgroup: +.. option:: --name + + Name for the firewall group. + +.. option:: --enable + + Enable firewall group (default). + +.. option:: --disable + + Disable firewall group. + +.. option:: --public + + Make the firewall group public, which allows it to be used in all projects + (as opposed to the default, which is to restrict its use to the current + project). + +.. option:: --private + + Restrict use of the firewall group to the current project. + +.. option:: --project + + Owner's project (name or ID). + +.. option:: --project-domain + + Domain the project belongs to (name or ID). + This can be used in case collisions between project names exist. + +.. option:: --description + + A description of the firewall group. + +.. option:: --ingress-firewall-policy + + Ingress firewall policy (name or ID). + +.. option:: --no-ingress-firewall-policy + + Detach ingress firewall policy from the firewall group. + +.. option:: --egress-firewall-policy + + Egress firewall policy (name or ID). + +.. option:: --no-egress-firewall-policy + + Detach egress firewall policy from the firewall group. + +.. option:: --port + + Port(s) to apply firewall group (name or ID). + +.. option:: --no-port + + Detach all port from the firewall group. + +firewall group delete +--------------------- + +Delete firewall group(s) + +.. program:: firewall group delete +.. code:: bash + + openstack firewall group delete + [ ...] + +.. _firewallgroup_delete-firewallgroup: +.. describe:: + + Firewall group(s) to delete (name or ID). + +firewall group list +------------------- + +List all firewall groups + +.. program:: firewall group list +.. code:: bash + + openstack firewall group list + [--long] + +.. option:: --long + + List additional fields in output. + +firewall group set +------------------ + +Set firewall group properties + +.. program:: firewall group set +.. code:: bash + + openstack firewall group set + +.. _firewallgroup_set-firewallgroup: +.. describe:: + + Firewall group to set (name or ID). + +.. option:: --name + + Set firewall group name. + +.. option:: --enable + + Enable firewall group (default). + +.. option:: --disable + + Disable firewall group. + +.. option:: --public + + Make the firewall group public, which allows it to be used in all projects + (as opposed to the default, which is to restrict its use to the current + project). + +.. option:: --private + + Restrict use of the firewall group to the current project. + +.. option:: --description + + A description of the firewall group. + +.. option:: --ingress-firewall-policy + + Ingress firewall policy (name or ID). + +.. option:: --no-ingress-firewall-policy + + Detach ingress firewall policy from the firewall group. + +.. option:: --egress-firewall-policy + + Egress firewall policy (name or ID). + +.. option:: --no-egress-firewall-policy + + Detach egress firewall policy from the firewall group. + +.. option:: --port + + Port(s) to apply firewall group. + +.. option:: --no-port + + Detach all port from the firewall group. + +firewall group show +------------------- + +Show information of a given firewall group + +.. program:: firewall group show +.. code:: bash + + openstack firewall group show + + +.. _firewallgroup_show-firewallgroup: +.. describe:: + + Firewall group to display (name or ID). + +firewall group unset +-------------------- + +Unset firewall group properties + +.. program:: firewall group unset +.. code:: bash + + openstack firewall group unset + +.. _firewallgroup_unset-firewallgroup: +.. describe:: + + Firewall group to unset (name or ID). + +.. option:: --enable + + Disable firewall group. + +.. option:: --public + + Restrict use of the firewall group to the current project. + +.. option:: --ingress-firewall-policy + + Detach ingress firewall policy from the firewall group. + +.. option:: --egress-firewall-policy + + Detach egress firewall policy from the firewall group. + +.. option:: --port + + Remove port(s) from the firewall group. + +.. option:: --all-port + + Remove all ports from the firewall group. diff --git a/doc/source/usage/osc/v2/firewall-policy.rst b/doc/source/usage/osc/v2/firewall-policy.rst new file mode 100644 index 000000000..c4a78dd07 --- /dev/null +++ b/doc/source/usage/osc/v2/firewall-policy.rst @@ -0,0 +1,274 @@ +===================== +firewall group policy +===================== + +A **firewall group policy** is an ordered collection of firewall rules. +A firewall policy can be shared across projects. Thus it can also be made part +of an audit workflow wherein the firewall_policy can be audited by the +relevant entity that is authorized (and can be different from the projects +which create or use the firewall group policy). + +Network v2 + +firewall group policy create +---------------------------- + +Create a firewall policy for a given project + +.. program:: firewall group policy create +.. code:: bash + + openstack firewall group policy create + +.. _firewallpolicy_create-firewallpolicy: +.. describe:: + + Name for the firewall policy. + +.. option:: --enable + + Enable firewall policy (default). + +.. option:: --disable + + Disable firewall policy. + +.. option:: --public + + Make the firewall policy public, which allows it to be used in all projects + (as opposed to the default, which is to restrict its use to the current + project). + +.. option:: --private + + Restrict use of the firewall policy to the current project. + +.. option:: --project + + Owner's project (name or ID). + +.. option:: --project-domain + + Domain the project belongs to (name or ID). + This can be used in case collisions between project names exist. + +.. option:: --description + + A description of the firewall policy. + +.. option:: --firewall-rule + + Firewall rule(s) to apply (name or ID). + +.. option:: --no-firewall-rule + + Remove all firewall rules from the firewall policy. + +.. option:: --audited + + Enable auditing for the policy. + +.. option:: --no-audited + + Disable auditing for the policy. + + +firewall group policy delete +---------------------------- + +Delete a given firewall policy + +.. program:: firewall group policy delete +.. code:: bash + + openstack firewall group policy delete + [ ...] + +.. _firewallpolicy_delete-firewallpolicy: +.. describe:: + + Firewall policy(s) to delete (name or ID). + +firewall group policy list +-------------------------- + +List all firewall policies + +.. program:: firewall group policy list +.. code:: bash + + openstack firewall group policy list + [--long] + +.. option:: --long + + List additional fields in output. + +firewall group policy set +------------------------- + +Set firewall policy properties + +.. program:: firewall group policy set +.. code:: bash + + openstack firewall group policy set + +.. _firewallpolicy_set-firewallpolicy: +.. describe:: + + Firewall policy to set (name or ID). + +.. option:: --name + + Set firewall policy name. + +.. option:: --enable + + Enable firewall policy (default). + +.. option:: --disable + + Disable firewall policy. + +.. option:: --public + + Make the firewall policy public, which allows it to be used in all projects + (as opposed to the default, which is to restrict its use to the current + project). + +.. option:: --private + + Restrict use of the firewall policy to the current project. + +.. option:: --project + + Owner's project (name or ID). + +.. option:: --project-domain + + Domain the project belongs to (name or ID). + This can be used in case collisions between project names exist. + +.. option:: --description + + A description of the firewall policy. + +.. option:: --firewall-rule + + Firewall rule(s) to apply (name or ID). + +.. option:: --no-firewall-rule + + Unset all firewall rules from firewall policy. + +.. option:: --audited + + Enable auditing for the policy. + +.. option:: --no-audited + + Disable auditing for the policy. + + +firewall group policy show +-------------------------- + +Show information of a given firewall policy + +.. program:: firewall group policy show +.. code:: bash + + openstack firewall group policy show + + +.. _firewallpolicy_show-firewallpolicy: +.. describe:: + + Firewall policy to display (name or ID). + +firewall group policy unset +--------------------------- + +Unset firewall policy properties + +.. program:: firewall group policy unset +.. code:: bash + + openstack firewall group policy unset + +.. _firewallpolicy_unset-firewallpolicy: +.. describe:: + + Firewall policy to unset (name or ID). + +.. option:: --enable + + Disable firewall policy. + +.. option:: --public + + Restrict use of the firewall policy to the current project. + +.. option:: --firewall-rule + + Firewall rule(s) to unset (name or ID). + +.. option:: --all-firewall-rule + + Remove all firewall rules from the firewall policy. + +.. option:: --audited + + Disable auditing for the policy. + +firewall group policy add rule +------------------------------ + +Adds a firewall rule in a firewall policy relative to the position of other +rules. + +.. program:: firewall group policy add rule +.. code:: bash + + openstack firewall group policy add rule + + + +.. _firewallpolicy_add_rule-firewallpolicy: +.. describe:: + + Firewall policy to add rule (name or ID). + +.. describe:: + + Firewall rule to be inserted (name or ID). + +.. option:: --insert-after + + Insert the new rule after this existing rule (name or ID). + +.. option:: --insert-before + + Insert the new rule before this existing rule (name or ID). + +firewall group policy remove rule +--------------------------------- + +Removes a firewall rule from a firewall policy. + +.. program:: firewall group policy remove rule +.. code:: bash + + openstack firewall group policy remove rule + + + +.. _firewallpolicy_remove_rule-firewallpolicy: +.. describe:: + + Firewall policy to remove rule (name or ID). + +.. describe:: + + Firewall rule to remove from policy (name or ID). diff --git a/doc/source/usage/osc/v2/firewall-rule.rst b/doc/source/usage/osc/v2/firewall-rule.rst new file mode 100644 index 000000000..fdb520684 --- /dev/null +++ b/doc/source/usage/osc/v2/firewall-rule.rst @@ -0,0 +1,304 @@ +=================== +firewall group rule +=================== + +A **firewall group rule** represents a collection of attributes like ports, IP +addresses which define match criteria and action (allow, or deny) that needs to +be taken on the matched data traffic. + +Network v2 + +firewall group rule create +-------------------------- + +Create a firewall rule for a given project + +.. program:: firewall group rule create +.. code:: bash + + openstack firewall group rule create + +.. option:: --name + + Set firewall rule name. + +.. option:: --enable + + Enable firewall rule (default). + +.. option:: --disable + + Disable firewall rule. + +.. option:: --public + + Make the firewall rule public, which allows it to be used in all projects + (as opposed to the default, which is to restrict its use to the current + project). + +.. option:: --private + + Restrict use of the firewall rule to the current project. + +.. option:: --project + + Owner's project (name or ID) + +.. option:: --project-domain + + Domain the project belongs to (name or ID). + This can be used in case collisions between project names exist. + +.. option:: --description + + A description of the firewall rule. + +.. option:: --protocol + + Protocol for the firewall rule ('tcp', 'udp', 'icmp', 'any'). + Default is 'any'. + +.. option:: --action + + Action for the firewall rule ('allow', 'deny', 'reject'). + Default is 'deny'. + +.. option:: --ip-version + + Set IP version 4 or 6 (default is 4). + +.. option:: --source-port + + Source port number or range + (integer in [1, 65535] or range like 123:456). + +.. option:: --no-source-port + + Detach source port number or range. + +.. option:: --destination-port + + Destination port number or range + (integer in [1, 65535] or range like 123:456). + +.. option:: --no-destination-port + + Detach destination port number or range. + +.. option:: --source-ip-address + + Source IP address or subnet. + +.. option:: --no-source-ip-address + + Detach source IP address. + +.. option:: --destination-ip-address + + Destination IP address or subnet. + +.. option:: --no-destination-ip-address + + Detach destination IP address. + +.. option:: --enable-rule + + Enable this rule (default is enabled). + +.. option:: --disable-rule + + Disable this rule. + +firewall group rule delete +-------------------------- + +Delete a given firewall rule + +.. program:: firewall group rule delete +.. code:: bash + + openstack firewall group rule delete + [ ...] + +.. _firewallrule_delete-firewallrule: +.. describe:: + + Firewall rule(s) to delete (name or ID). + +firewall group rule list +------------------------ + +List all firewall rules + +.. program:: firewall group rule list +.. code:: bash + + openstack firewall group rule list + [--long] + +.. option:: --long + + List additional fields in output. + +firewall group rule set +----------------------- + +Set firewall rule properties + +.. program:: firewall group rule set +.. code:: bash + + openstack firewall group rule set + +.. _firewallrule_set-firewallrule: +.. describe:: + + Firewall rule to set (name or ID). + +.. option:: --name + + Set firewall rule name. + +.. option:: --enable + + Enable firewall rule (default). + +.. option:: --disable + + Disable firewall rule. + +.. option:: --public + + Make the firewall rule public, which allows it to be used in all projects + (as opposed to the default, which is to restrict its use to the current + project). + +.. option:: --private + + Restrict use of the firewall rule to the current project. + +.. option:: --project + + Owner's project (name or ID). + +.. option:: --project-domain + + Domain the project belongs to (name or ID). + This can be used in case collisions between project names exist. + +.. option:: --description + + A description of the firewall rule. + +.. option:: --protocol + + Protocol for the firewall rule ('tcp', 'udp', 'icmp', 'any'). + +.. option:: --action + + Action for the firewall rule ('allow', 'deny', 'reject'). + +.. option:: --ip-version + + Set IP version 4 or 6 (default is 4). + +.. option:: --source-port + + Source port number or range + (integer in [1, 65535] or range like 123:456). + +.. option:: --no-source-port + + Detach source port number or range. + +.. option:: --destination-port + + Destination port number or range + (integer in [1, 65535] or range like 123:456). + +.. option:: --no-destination-port + + Detach destination port number or range. + +.. option:: --source-ip-address + + Source IP address or subnet. + +.. option:: --no-source-ip-address + + Detach source IP address. + +.. option:: --destination-ip-address + + Destination IP address or subnet. + +.. option:: --no-destination-ip-address + + Detach destination IP address. + +.. option:: --enable-rule + + Enable this rule (default is enabled). + +.. option:: --disable-rule + + Disable this rule. + +firewall group rule show +------------------------ + +Show information of a given firewall rule + +.. program:: firewall group rule show +.. code:: bash + + openstack firewall group rule show + + +.. _firewallrule_show-firewallrule: +.. describe:: + + Firewall rule to display (name or ID). + +firewall group rule unset +------------------------- + +Unset firewall rule properties + +.. program:: firewall group rule unset +.. code:: bash + + openstack firewall group rule unset + +.. _firewallrule_unset-firewallrule: +.. describe:: + + Firewall rule to unset (name or ID). + +.. option:: --enable + + Disable firewall rule. + +.. option:: --public + + Restrict use of the firewall rule to the current project. + +.. option:: --source-port + + Detach source port number or range. + +.. option:: --destination-port + + Detach destination port number or range. + +.. option:: --source-ip-address + + Detach source IP address. + +.. option:: --destination-ip-address + + Detach destination IP address. + +.. option:: --enable-rule + + Disable this rule. From 93d1c1f1d111ea8ce4a2aa67b2db142fbf060843 Mon Sep 17 00:00:00 2001 From: Yushiro FURUKAWA Date: Sat, 7 Jan 2017 15:28:39 +0900 Subject: [PATCH 539/845] Replace 'os' to 'openstack' for all command example Trivial-fix Change-Id: I670dd1240328859d5f10bb4d4ef878f1c8a0ed69 --- doc/source/usage/osc/v2/network-trunk.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/source/usage/osc/v2/network-trunk.rst b/doc/source/usage/osc/v2/network-trunk.rst index a39e3bd0e..06a63d209 100644 --- a/doc/source/usage/osc/v2/network-trunk.rst +++ b/doc/source/usage/osc/v2/network-trunk.rst @@ -17,7 +17,7 @@ List all subports for a given network trunk .. program:: network subport list .. code:: bash - os network subport list + openstack network subport list --trunk .. option:: --trunk @@ -32,7 +32,7 @@ Create a network trunk for a given project .. program:: network trunk create .. code:: bash - os network trunk create + openstack network trunk create --parent-port [--subport ] [--enable | --disable] @@ -78,7 +78,7 @@ Delete a given network trunk .. program:: network trunk delete .. code:: bash - os network trunk delete + openstack network trunk delete [ ...] .. _network_trunk_delete-trunk: @@ -94,7 +94,7 @@ List all network trunks .. program:: network trunk list .. code:: bash - os network trunk list + openstack network trunk list [--long] .. option:: --long @@ -109,7 +109,7 @@ Set network trunk properties .. program:: network trunk set .. code:: bash - os network trunk set + openstack network trunk set [--name ] [--description ] [--subport ] @@ -150,7 +150,7 @@ Show information of a given network trunk .. program:: network trunk show .. code:: bash - os network trunk show + openstack network trunk show .. _network_trunk_show-trunk: @@ -166,7 +166,7 @@ Unset subports from a given network trunk .. program:: network trunk unset .. code:: bash - os network trunk unset + openstack network trunk unset --subport From 9b3658d4748f5eb40b7e59dd2479461d654470bb Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Mon, 16 Jan 2017 17:27:58 +0000 Subject: [PATCH 540/845] Updated from global requirements Change-Id: Ibb55a7bbd27bedd9c3cbb5329d4081057ec9c729 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b769319c8..3d38f40df 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ oslo.i18n>=2.1.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 oslo.utils>=3.18.0 # Apache-2.0 os-client-config>=1.22.0 # Apache-2.0 -keystoneauth1>=2.16.0 # Apache-2.0 +keystoneauth1>=2.17.0 # Apache-2.0 # keystoneclient is used only by neutronclient.osc.utils # TODO(amotoki): Drop this after osc.utils has no dependency on keystoneclient python-keystoneclient>=3.8.0 # Apache-2.0 From 5031501283704da0b0a3abb4b03d722c14be48f1 Mon Sep 17 00:00:00 2001 From: QunyingRan Date: Thu, 24 Nov 2016 09:42:23 +0800 Subject: [PATCH 541/845] Modify key for 'qos-minimum-bandwidth-rule-list' reponse The result of 'qos-minimum-bandwidth-rule-list' response is wrong, because the key in response information is 'minimum_bandwidth_rules' and not 'qos_minimum_bandwidth_rules' Change-Id: Ic87fffd60f004a9b989f43e93aaeee2dc5cb300c Closes-Bug:#1643849 --- .../tests/unit/qos/test_cli20_minimum_bandwidth_rule.py | 3 ++- neutronclient/v2_0/client.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/neutronclient/tests/unit/qos/test_cli20_minimum_bandwidth_rule.py b/neutronclient/tests/unit/qos/test_cli20_minimum_bandwidth_rule.py index 0c44f5cd7..39dccd614 100644 --- a/neutronclient/tests/unit/qos/test_cli20_minimum_bandwidth_rule.py +++ b/neutronclient/tests/unit/qos/test_cli20_minimum_bandwidth_rule.py @@ -138,5 +138,6 @@ def test_list_minimum_bandwidth_rule(self): policy_id = 'policy_id' args = [policy_id] contents = [{'name': 'rule1', 'min-kbps': 1000, 'direction': 'egress'}] - self._test_list_resources(self.cmd_ress, cmd, parent_id=policy_id, + self._test_list_resources(self.ress, cmd, parent_id=policy_id, + cmd_resources=self.cmd_ress, base_args=args, response_contents=contents) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index e20e94a31..2c1d24776 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -1845,7 +1845,7 @@ def list_minimum_bandwidth_rules(self, policy_id, retrieve_all=True, """Fetches a list of all minimum bandwidth rules for the given policy. """ - return self.list('qos_minimum_bandwidth_rules', + return self.list('minimum_bandwidth_rules', self.qos_minimum_bandwidth_rules_path % policy_id, retrieve_all, **_params) From d32a26957580bb305725d4beafe9b6796135c64a Mon Sep 17 00:00:00 2001 From: Abhishek Raut Date: Sun, 22 Jan 2017 02:53:41 -0800 Subject: [PATCH 542/845] Set project_id column header to Project In order to remain consistent with other OSC commands and headers, the column header for project_id must be set to Project instead of Project ID. TrivialFix Change-Id: I7d34a769d34bbe00c262f639fee4bde35adb0a9c --- neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py | 2 +- neutronclient/osc/v2/networking_bgpvpn/network_association.py | 2 +- neutronclient/osc/v2/networking_bgpvpn/router_association.py | 2 +- neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py b/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py index 1e5dbb0ea..d0ba3d180 100644 --- a/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py +++ b/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py @@ -31,7 +31,7 @@ _attr_map = ( ('id', 'ID', nc_osc_utils.LIST_BOTH), - ('tenant_id', 'Project ID', nc_osc_utils.LIST_LONG_ONLY), + ('tenant_id', 'Project', nc_osc_utils.LIST_LONG_ONLY), ('name', 'Name', nc_osc_utils.LIST_BOTH), ('type', 'Type', nc_osc_utils.LIST_BOTH), ('route_targets', 'Route Targets', nc_osc_utils.LIST_LONG_ONLY), diff --git a/neutronclient/osc/v2/networking_bgpvpn/network_association.py b/neutronclient/osc/v2/networking_bgpvpn/network_association.py index 4799aa59a..33e199651 100644 --- a/neutronclient/osc/v2/networking_bgpvpn/network_association.py +++ b/neutronclient/osc/v2/networking_bgpvpn/network_association.py @@ -35,7 +35,7 @@ class BgpvpnNetAssoc(object): _attr_map = ( ('id', 'ID', nc_osc_utils.LIST_BOTH), - ('tenant_id', 'Project ID', nc_osc_utils.LIST_LONG_ONLY), + ('tenant_id', 'Project', nc_osc_utils.LIST_LONG_ONLY), ('%s_id' % _assoc_res_name, '%s ID' % _assoc_res_name.capitalize(), nc_osc_utils.LIST_BOTH), ) diff --git a/neutronclient/osc/v2/networking_bgpvpn/router_association.py b/neutronclient/osc/v2/networking_bgpvpn/router_association.py index dd3ce2ae4..ee788d33c 100644 --- a/neutronclient/osc/v2/networking_bgpvpn/router_association.py +++ b/neutronclient/osc/v2/networking_bgpvpn/router_association.py @@ -35,7 +35,7 @@ class BgpvpnRouterAssoc(object): _attr_map = ( ('id', 'ID', nc_osc_utils.LIST_BOTH), - ('tenant_id', 'Project ID', nc_osc_utils.LIST_LONG_ONLY), + ('tenant_id', 'Project', nc_osc_utils.LIST_LONG_ONLY), ('%s_id' % _assoc_res_name, '%s ID' % _assoc_res_name.capitalize(), nc_osc_utils.LIST_BOTH), ) diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py index 2c3bcfa8f..6138feaf3 100644 --- a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py +++ b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py @@ -95,7 +95,7 @@ class BgpvpnFakeAssoc(object): _attr_map = ( ('id', 'ID', nc_osc_utils.LIST_BOTH), - ('tenant_id', 'Project ID', nc_osc_utils.LIST_LONG_ONLY), + ('tenant_id', 'Project', nc_osc_utils.LIST_LONG_ONLY), ('%s_id' % _assoc_res_name, '%s ID' % _assoc_res_name.capitalize(), nc_osc_utils.LIST_BOTH), ) From 1e9ab9efb5d3a698492da44e06789ba5942242ae Mon Sep 17 00:00:00 2001 From: Abhishek Kekane Date: Wed, 18 Jan 2017 14:45:49 +0530 Subject: [PATCH 543/845] x-openstack-request-id logged twice in logs In the recent release of keystoneauth1 2.18.0 provision is made to log x-openstack-request-id for session client. Once this new library is synced in openstack projects, the x-openstack-request-id will be logged twice on the console if session client is used. For example, $ neutron --debug port-list DEBUG: keystoneauth.session GET call to network for http://10.232.48.204:9696/v2.0/ports.json used request id req-da75468a-2855-4e59-b308-d2a91776b927 DEBUG: neutronclient.v2_0.client GET call to neutron for http://10.232.48.204:9696/v2.0/ports.json used request id req-da75468a-2855-4e59-b308-d2a91776b927 Above log will be logged twice on the console. Removed logging of x-openstack-request-id in case of SessionClient as it is already logged in keystoneauth1. x-openstack-request-id will only be logged once on console if HTTPClient is used. Depends-On: I63fb5e5486670bc369abc5339a2da8e65ba2ec6f Closes-Bug: #1657351 Change-Id: I8efefd69a1ba4a214b1a30b20b9c1e5bd96b19d4 --- neutronclient/client.py | 11 +++++++++++ neutronclient/v2_0/client.py | 7 ------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/neutronclient/client.py b/neutronclient/client.py index c96381a89..dd37285f4 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -110,6 +110,17 @@ def _cs_request(self, *args, **kwargs): _logger.debug("throwing ConnectionFailed : %s", e) raise exceptions.ConnectionFailed(reason=e) utils.http_log_resp(_logger, resp, body) + + # log request-id for each api call + request_id = resp.headers.get('x-openstack-request-id') + if request_id: + _logger.debug('%(method)s call to neutron for ' + '%(url)s used request id ' + '%(response_request_id)s', + {'method': resp.request.method, + 'url': resp.url, + 'response_request_id': request_id}) + if resp.status_code == 401: raise exceptions.Unauthorized(message=body) return resp, body diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 2c1d24776..728339e70 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -119,13 +119,6 @@ def _append_request_id(self, resp): # Extract 'x-openstack-request-id' from headers if # response is a Response object. request_id = resp.headers.get('x-openstack-request-id') - # log request-id for each api call - _logger.debug('%(method)s call to neutron for ' - '%(url)s used request id ' - '%(response_request_id)s', - {'method': resp.request.method, - 'url': resp.url, - 'response_request_id': request_id}) else: # If resp is of type string. request_id = resp From a12f823ded93820c976688e1925c4d6a061ccbdb Mon Sep 17 00:00:00 2001 From: KATO Tomoyuki Date: Fri, 14 Oct 2016 13:57:30 +0900 Subject: [PATCH 544/845] Add plug-in summary for osc doc Stevedore Sphinx extension handles this comment. http://docs.openstack.org/developer/python-openstackclient/ plugin-commands.html Change-Id: I7e7f4f8714c0a261d587d1382103f4f367b64248 --- neutronclient/osc/plugin.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/neutronclient/osc/plugin.py b/neutronclient/osc/plugin.py index 1efb6c5f7..bb6f72dfc 100644 --- a/neutronclient/osc/plugin.py +++ b/neutronclient/osc/plugin.py @@ -11,6 +11,8 @@ # under the License. # +"""OpenStackClient plugin for advanced Networking service.""" + import logging # TODO(rtheis/amotoki): Add functional test infrastructure for OSC From bc2f975d6ccdff0c4ddbf57242bab905a572bacf Mon Sep 17 00:00:00 2001 From: Yushiro FURUKAWA Date: Wed, 18 Jan 2017 01:01:28 +0900 Subject: [PATCH 545/845] FWaaS - Adds an argument into find_resource This commit adds an additional argument 'cmd_resource' for find_resource in order to call client method correctly and fixes typo. Change-Id: Ib079f9fda9d7c190ef9a2f152b5a5af1853810a6 Closes-Bug: #1657180 --- neutronclient/osc/v2/fwaas/constants.py | 9 +- neutronclient/osc/v2/fwaas/firewallgroup.py | 26 ++-- neutronclient/osc/v2/fwaas/firewallpolicy.py | 40 ++--- neutronclient/osc/v2/fwaas/firewallrule.py | 12 +- .../tests/unit/osc/v2/fwaas/common.py | 41 ++++- .../unit/osc/v2/fwaas/test_firewallgroup.py | 143 +++++++++++++++--- .../unit/osc/v2/fwaas/test_firewallpolicy.py | 115 +++++++++++++- .../unit/osc/v2/fwaas/test_firewallrule.py | 13 +- 8 files changed, 331 insertions(+), 68 deletions(-) diff --git a/neutronclient/osc/v2/fwaas/constants.py b/neutronclient/osc/v2/fwaas/constants.py index 29f3ce09f..4c8ee6a2d 100644 --- a/neutronclient/osc/v2/fwaas/constants.py +++ b/neutronclient/osc/v2/fwaas/constants.py @@ -1,4 +1,4 @@ -# Copyright 2016 FUJITSU LIMITED +# Copyright 2016-2017 FUJITSU LIMITED # All Rights Reserved # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -20,7 +20,6 @@ FWPS = 'firewall_policies' FWR = 'firewall_rule' FWRS = 'firewall_rules' - -CMD_RESOURCE_MAP = dict( - (res, 'fwaas_' + res) for res in [FWG, FWGS, FWP, FWPS, FWR, FWRS] -) +CMD_FWG = 'fwaas_' + FWG +CMD_FWP = 'fwaas_' + FWP +CMD_FWR = 'fwaas_' + FWR diff --git a/neutronclient/osc/v2/fwaas/firewallgroup.py b/neutronclient/osc/v2/fwaas/firewallgroup.py index 3aa569f8f..b5b388879 100644 --- a/neutronclient/osc/v2/fwaas/firewallgroup.py +++ b/neutronclient/osc/v2/fwaas/firewallgroup.py @@ -1,4 +1,4 @@ -# Copyright 2016 FUJITSU LIMITED +# Copyright 2016-2017 FUJITSU LIMITED # All Rights Reserved # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -114,22 +114,22 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): parsed_args.no_ingress_firewall_policy): attrs['ingress_firewall_policy_id'] = client.find_resource( const.FWP, parsed_args.ingress_firewall_policy, - cmd_resource='fwaas_' + const.FWP)['id'] + cmd_resource=const.CMD_FWP)['id'] elif parsed_args.ingress_firewall_policy: attrs['ingress_firewall_policy_id'] = client.find_resource( const.FWP, parsed_args.ingress_firewall_policy, - cmd_resource='fwaas_' + const.FWP)['id'] + cmd_resource=const.CMD_FWP)['id'] elif parsed_args.no_ingress_firewall_policy: attrs['ingress_firewall_policy_id'] = None if (parsed_args.egress_firewall_policy and parsed_args.no_egress_firewall_policy): attrs['egress_firewall_policy_id'] = client.find_resource( const.FWP, parsed_args.egress_firewall_policy, - cmd_resource='fwaas_' + const.FWP)['id'] + cmd_resource=const.CMD_FWP)['id'] elif parsed_args.egress_firewall_policy: attrs['egress_firewall_policy_id'] = client.find_resource( const.FWP, parsed_args.egress_firewall_policy, - cmd_resource='fwaas_' + const.FWP)['id'] + cmd_resource=const.CMD_FWP)['id'] elif parsed_args.no_egress_firewall_policy: attrs['egress_firewall_policy_id'] = None if parsed_args.public: @@ -153,7 +153,8 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): ports.append(client.find_resource('port', p)['id']) if not is_create: ports += client.find_resource( - const.FWG, parsed_args.firewall_group)['ports'] + const.FWG, parsed_args.firewall_group, + cmd_resource=const.CMD_FWG)['ports'] attrs['ports'] = sorted(set(ports)) elif parsed_args.no_port: attrs['ports'] = [] @@ -209,7 +210,7 @@ def take_action(self, parsed_args): for fwg in parsed_args.firewall_group: try: fwg_id = client.find_resource( - const.FWG, fwg, cmd_resource='fwaas_' + const.FWG)['id'] + const.FWG, fwg, cmd_resource=const.CMD_FWG)['id'] client.delete_fwaas_firewall_group(fwg_id) except Exception as e: result += 1 @@ -218,7 +219,7 @@ def take_action(self, parsed_args): const.FWG: fwg, 'e': e}) if result > 0: - total = len(parsed_args.firewall_groups) + total = len(parsed_args.firewall_group) msg = (_("%(result)s of %(total)s firewall group(s) " "failed to delete.") % {'result': result, 'total': total}) raise exceptions.CommandError(msg) @@ -271,7 +272,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.neutronclient fwg_id = client.find_resource(const.FWG, parsed_args.firewall_group, - cmd_resource='fwaas_' + const.FWG)['id'] + cmd_resource=const.CMD_FWG)['id'] attrs = _get_common_attrs(self.app.client_manager, parsed_args, is_create=False) try: @@ -296,7 +297,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.neutronclient fwg_id = client.find_resource(const.FWG, parsed_args.firewall_group, - cmd_resource='fwaas_' + const.FWG)['id'] + cmd_resource=const.CMD_FWG)['id'] obj = client.show_fwaas_firewall_group(fwg_id)[const.FWG] columns, display_columns = osc_utils.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns, formatters=_formatters) @@ -356,7 +357,8 @@ def _get_attrs(self, client_manager, parsed_args): attrs['admin_state_up'] = False if parsed_args.port: old = client.find_resource( - const.FWG, parsed_args.firewall_group)['ports'] + const.FWG, parsed_args.firewall_group, + cmd_resource=const.CMD_FWG)['ports'] new = [client.find_resource( 'port', r)['id'] for r in parsed_args.port] attrs['ports'] = sorted(list(set(old) - set(new))) @@ -367,7 +369,7 @@ def _get_attrs(self, client_manager, parsed_args): def take_action(self, parsed_args): client = self.app.client_manager.neutronclient fwg_id = client.find_resource(const.FWG, parsed_args.firewall_group, - cmd_resource='fwaas_' + const.FWG)['id'] + cmd_resource=const.CMD_FWG)['id'] attrs = self._get_attrs(self.app.client_manager, parsed_args) try: client.update_fwaas_firewall_group(fwg_id, {const.FWG: attrs}) diff --git a/neutronclient/osc/v2/fwaas/firewallpolicy.py b/neutronclient/osc/v2/fwaas/firewallpolicy.py index 9a5a0d716..5ac097145 100644 --- a/neutronclient/osc/v2/fwaas/firewallpolicy.py +++ b/neutronclient/osc/v2/fwaas/firewallpolicy.py @@ -1,4 +1,4 @@ -# Copyright 2016 FUJITSU LIMITED +# Copyright 2016-2017 FUJITSU LIMITED # All Rights Reserved # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -56,16 +56,17 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): _firewall_rules = [] for f in set(parsed_args.firewall_rule): _firewall_rules.append(client.find_resource( - const.FWR, f, cmd_resource='fwaas_' + const.FWR)['id']) + const.FWR, f, cmd_resource=const.CMD_FWR)['id']) attrs[const.FWRS] = sorted(_firewall_rules) elif parsed_args.firewall_rule: rules = [] for f in set(parsed_args.firewall_rule): rules.append(client.find_resource( - const.FWR, f, cmd_resource='fwaas_' + const.FWR)['id']) + const.FWR, f, cmd_resource=const.CMD_FWR)['id']) if not is_create: rules += client.find_resource( - const.FWP, parsed_args.firewall_policy)[const.FWRS] + const.FWP, parsed_args.firewall_policy, + cmd_resource=const.CMD_FWP)[const.FWRS] attrs[const.FWRS] = sorted(set(rules)) elif parsed_args.no_firewall_rule: attrs[const.FWRS] = [] @@ -173,7 +174,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.firewall_policy) - msg = (_("%(result)s of %(total)s Firewall policy(s) " + msg = (_("%(result)s of %(total)s firewall policy(s) " "failed to delete.") % {'result': result, 'total': total}) raise exceptions.CommandError(msg) @@ -211,23 +212,23 @@ def args2body(self, parsed_args): if parsed_args.insert_before: _insert_before = client.find_resource( const.FWR, parsed_args.insert_before, - cmd_resource='fwaas_' + const.FWR)['id'] + cmd_resource=const.CMD_FWR)['id'] _insert_after = '' if 'insert_after' in parsed_args: if parsed_args.insert_after: _insert_after = client.find_resource( const.FWR, parsed_args.insert_after, - cmd_resource='fwaas_' + const.FWR)['id'] + cmd_resource=const.CMD_FWR)['id'] return {'firewall_rule_id': _rule_id, 'insert_before': _insert_before, 'insert_after': _insert_after} def take_action(self, parsed_args): client = self.app.client_manager.neutronclient - body = self.args2body(parsed_args) policy_id = client.find_resource( const.FWP, parsed_args.firewall_policy, - cmd_resource='fwaas_' + const.FWP)['id'] + cmd_resource=const.CMD_FWP)['id'] + body = self.args2body(parsed_args) client.insert_rule_fwaas_firewall_policy(policy_id, body) rule_id = body['firewall_rule_id'] policy = parsed_args.firewall_policy @@ -253,11 +254,11 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.neutronclient - fwr_id = _get_required_firewall_rule(client, parsed_args) - body = {'firewall_rule_id': fwr_id} policy_id = client.find_resource( const.FWP, parsed_args.firewall_policy, - cmd_resource='fwaas_' + const.FWP)['id'] + cmd_resource=const.CMD_FWP)['id'] + fwr_id = _get_required_firewall_rule(client, parsed_args) + body = {'firewall_rule_id': fwr_id} client.remove_rule_fwaas_firewall_policy(policy_id, body) rule_id = body['firewall_rule_id'] policy = parsed_args.firewall_policy @@ -317,7 +318,7 @@ def take_action(self, parsed_args): client = self.app.client_manager.neutronclient fwp_id = client.find_resource( const.FWP, parsed_args.firewall_policy, - cmd_resource='fwaas_' + const.FWP)['id'] + cmd_resource=const.CMD_FWP)['id'] attrs = _get_common_attrs(self.app.client_manager, parsed_args, is_create=False) try: @@ -343,7 +344,7 @@ def take_action(self, parsed_args): client = self.app.client_manager.neutronclient fwp_id = client.find_resource(const.FWP, parsed_args.firewall_policy, - cmd_resource='fwaas_' + const.FWP)['id'] + cmd_resource=const.CMD_FWP)['id'] obj = client.show_fwaas_firewall_policy(fwp_id)[const.FWP] columns, display_columns = osc_utils.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns, formatters=_formatters) @@ -354,8 +355,8 @@ def _get_required_firewall_rule(client, parsed_args): if not parsed_args.firewall_rule: msg = (_("Firewall rule (name or ID) is required.")) raise exceptions.CommandError(msg) - return client.find_resource(const.FWR, parsed_args.firewall_rule, - cmd_resource='fwaas_' + const.FWR)['id'] + return client.find_resource( + const.FWR, parsed_args.firewall_rule, cmd_resource=const.CMD_FWR)['id'] class UnsetFirewallPolicy(command.Command): @@ -395,11 +396,12 @@ def _get_attrs(self, client_manager, parsed_args): if parsed_args.firewall_rule: old = client.find_resource( - const.FWP, parsed_args.firewall_policy)[const.FWRS] + const.FWP, parsed_args.firewall_policy, + cmd_resource=const.CMD_FWP)[const.FWRS] new = [] for f in set(parsed_args.firewall_rule): new.append(client.find_resource( - const.FWR, f, cmd_resource='fwaas_' + const.FWR)['id']) + const.FWR, f, cmd_resource=const.CMD_FWR)['id']) attrs[const.FWRS] = sorted(list(set(old) - set(new))) if parsed_args.all_firewall_rule: attrs[const.FWRS] = [] @@ -413,7 +415,7 @@ def take_action(self, parsed_args): client = self.app.client_manager.neutronclient fwp_id = client.find_resource( const.FWP, parsed_args.firewall_policy, - cmd_resource='fwaas_' + const.FWP)['id'] + cmd_resource=const.CMD_FWP)['id'] attrs = self._get_attrs(self.app.client_manager, parsed_args) try: client.update_fwaas_firewall_policy(fwp_id, {const.FWP: attrs}) diff --git a/neutronclient/osc/v2/fwaas/firewallrule.py b/neutronclient/osc/v2/fwaas/firewallrule.py index a7d9051f3..4b10e3bc5 100644 --- a/neutronclient/osc/v2/fwaas/firewallrule.py +++ b/neutronclient/osc/v2/fwaas/firewallrule.py @@ -1,4 +1,4 @@ -# Copyright 2016 FUJITSU LIMITED +# Copyright 2016-2017 FUJITSU LIMITED # All Rights Reserved # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -223,7 +223,7 @@ def take_action(self, parsed_args): for fwr in parsed_args.firewall_rule: try: fwr_id = client.find_resource( - const.FWR, fwr, cmd_resource='fwaas_' + const.FWR)['id'] + const.FWR, fwr, cmd_resource=const.CMD_FWR)['id'] client.delete_fwaas_firewall_rule(fwr_id) except Exception as e: result += 1 @@ -233,7 +233,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.firewall_rule) - msg = (_("%(result)s of %(total)s Firewall rule(s) failed " + msg = (_("%(result)s of %(total)s firewall rule(s) failed " "to delete.") % {'result': result, 'total': total}) raise exceptions.CommandError(msg) @@ -301,7 +301,7 @@ def take_action(self, parsed_args): parsed_args, is_create=False) fwr_id = client.find_resource( const.FWR, parsed_args.firewall_rule, - cmd_resource='fwaas_' + const.FWR)['id'] + cmd_resource=const.CMD_FWR)['id'] try: client.update_fwaas_firewall_rule(fwr_id, {const.FWR: attrs}) except Exception as e: @@ -325,7 +325,7 @@ def take_action(self, parsed_args): client = self.app.client_manager.neutronclient fwr_id = client.find_resource( const.FWR, parsed_args.firewall_rule, - cmd_resource='fwaas_' + const.FWR)['id'] + cmd_resource=const.CMD_FWR)['id'] obj = client.show_fwaas_firewall_rule(fwr_id)[const.FWR] columns, display_columns = osc_utils.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns, formatters=_formatters) @@ -390,7 +390,7 @@ def take_action(self, parsed_args): attrs = self._get_attrs(self.app.client_manager, parsed_args) fwr_id = client.find_resource( const.FWR, parsed_args.firewall_rule, - cmd_resource='fwaas_' + const.FWR)['id'] + cmd_resource=const.CMD_FWR)['id'] try: client.update_fwaas_firewall_rule(fwr_id, {const.FWR: attrs}) except Exception as e: diff --git a/neutronclient/tests/unit/osc/v2/fwaas/common.py b/neutronclient/tests/unit/osc/v2/fwaas/common.py index 915988488..b9ea7841b 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/common.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/common.py @@ -1,4 +1,4 @@ -# Copyright 2016 FUJITSU LIMITED +# Copyright 2016-2017 FUJITSU LIMITED # All Rights Reserved # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -14,6 +14,9 @@ # under the License. # +import testtools + +from osc_lib import exceptions from osc_lib.tests import utils from neutronclient.tests.unit.osc.v2 import fakes as test_fakes @@ -46,6 +49,17 @@ class TestShowFWaaS(test_fakes.TestNeutronClientOSCV2): def test_show_filtered_by_id_or_name(self): target = self.resource['id'] + + def _mock_fwaas(*args, **kwargs): + # Find specified ingress_firewall_policy + if self.neutronclient.find_resource.call_count == 1: + self.assertEqual(self.res, args[0]) + self.assertEqual(self.resource['id'], args[1]) + self.assertEqual({'cmd_resource': 'fwaas_' + self.res}, kwargs) + return {'id': args[1]} + + self.neutronclient.find_resource.side_effect = _mock_fwaas + arglist = [target] verifylist = [(self.res, target)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -210,10 +224,20 @@ def test_delete_with_one_resource(self): self.assertIsNone(result) def test_delete_with_multiple_resources(self): + + def _mock_fwaas(*args, **kwargs): + self.assertEqual(self.res, args[0]) + self.assertIsNotNone(args[1]) + self.assertEqual({'cmd_resource': 'fwaas_' + self.res}, kwargs) + return {'id': args[1]} + + self.neutronclient.find_resource.side_effect = _mock_fwaas + target1 = 'target1' target2 = 'target2' arglist = [target1, target2] verifylist = [(self.res, [target1, target2])] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.assertIsNone(result) @@ -223,6 +247,21 @@ def test_delete_with_multiple_resources(self): actual = ''.join(self.mocked.call_args_list[idx][0]) self.assertEqual(reference, actual) + def test_delete_multiple_with_exception(self): + target1 = 'target' + arglist = [target1] + verifylist = [(self.res, [target1])] + + self.neutronclient.find_resource.side_effect = [ + target1, exceptions.CommandError + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + resource_name = self.res.replace('_', ' ') + msg = "1 of 2 %s(s) failed to delete." % resource_name + with testtools.ExpectedException(exceptions.CommandError) as e: + self.cmd.take_action(parsed_args) + self.assertEqual(msg, str(e)) + class TestUnsetFWaaS(test_fakes.TestNeutronClientOSCV2): diff --git a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py index 44bcc708f..cc074ff2f 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py @@ -22,6 +22,7 @@ from osc_lib.tests import utils from neutronclient.osc import utils as osc_utils +from neutronclient.osc.v2.fwaas import constants as const from neutronclient.osc.v2.fwaas import firewallgroup from neutronclient.tests.unit.osc.v2 import fakes as test_fakes from neutronclient.tests.unit.osc.v2.fwaas import common @@ -85,21 +86,11 @@ def check_results(self, headers, data, exp_req, is_list=False): def setUp(self): super(TestFirewallGroup, self).setUp() - def side_effect_for_find_resource(*args, **kwargs): - port_id = args[1] - ports = _fwg['ports'] - if self.res in args[0]: - ports = _fwg['ports'] - return {'id': port_id, 'ports': ports} + def _find_resource(*args, **kwargs): + return {'id': args[1], 'ports': _fwg['ports']} - def side_effect_for_list_ports(*args): - port_name = 'id_for_port' - return {'ports': [{'id': port_name}]} - - self.neutronclient.find_resource.side_effect = mock.Mock( - side_effect=side_effect_for_find_resource) - self.neutronclient.list_ports = mock.Mock( - side_effect=side_effect_for_list_ports) + self.neutronclient.find_resource = mock.Mock( + side_effect=_find_resource) osc_utils.find_project = mock.Mock() osc_utils.find_project.id = _fwg['tenant_id'] self.res = 'firewall_group' @@ -204,6 +195,45 @@ def test_create_with_port(self): self.check_results(headers, data, request) + def test_create_with_ingress_policy(self): + ingress_policy = 'my-ingress-policy' + + def _mock_port_fwg(*args, **kwargs): + return {'id': args[1]} + + self.neutronclient.find_resource.side_effect = _mock_port_fwg + + arglist = ['--ingress-firewall-policy', ingress_policy] + verifylist = [('ingress_firewall_policy', ingress_policy)] + request, response = _generate_req_and_res(verifylist) + self._update_expect_response(request, response) + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + self.neutronclient.find_resource.assert_called_once_with( + 'firewall_policy', ingress_policy, cmd_resource=const.CMD_FWP) + + self.check_results(headers, data, request) + + def test_create_with_egress_policy(self): + egress_policy = 'my-egress-policy' + + def _mock_port_fwg(*args, **kwargs): + return {'id': args[1]} + + self.neutronclient.find_resource.side_effect = _mock_port_fwg + + arglist = ['--egress-firewall-policy', egress_policy] + verifylist = [('egress_firewall_policy', egress_policy)] + request, response = _generate_req_and_res(verifylist) + self._update_expect_response(request, response) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.neutronclient.find_resource.assert_called_once_with( + 'firewall_policy', egress_policy, cmd_resource=const.CMD_FWP) + self.check_results(headers, data, request) + def test_create_with_all_params(self): name = 'my-name' description = 'my-desc' @@ -342,26 +372,77 @@ def _update_expect_response(self, request, response): response[column] for column in self.ordered_columns ) - def test_set_ingress_policy(self): - # firewall_group-update myid --policy newpolicy. + def test_set_ingress_policy_and_egress_policy(self): target = self.resource['id'] - policy = 'ingress_policy' - arglist = [target, '--ingress-firewall-policy', policy] + ingress_policy = 'ingress_policy' + egress_policy = 'egress_policy' + + def _mock_fwg_policy(*args, **kwargs): + # 1. Find specified firewall_group + if self.neutronclient.find_resource.call_count == 1: + self.neutronclient.find_resource.assert_called_with( + self.res, target, cmd_resource=const.CMD_FWG) + # 2. Find specified 'ingress_firewall_policy' + if self.neutronclient.find_resource.call_count == 2: + self.neutronclient.find_resource.assert_called_with( + 'firewall_policy', ingress_policy, + cmd_resource=const.CMD_FWP) + # 3. Find specified 'ingress_firewall_policy' + if self.neutronclient.find_resource.call_count == 3: + self.neutronclient.find_resource.assert_called_with( + 'firewall_policy', egress_policy, + cmd_resource=const.CMD_FWP) + return {'id': args[1]} + + self.neutronclient.find_resource.side_effect = _mock_fwg_policy + + arglist = [ + target, + '--ingress-firewall-policy', ingress_policy, + '--egress-firewall-policy', egress_policy, + ] verifylist = [ (self.res, target), - ('ingress_firewall_policy', policy), + ('ingress_firewall_policy', ingress_policy), + ('egress_firewall_policy', egress_policy), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'ingress_firewall_policy_id': policy}}) + target, {self.res: {'ingress_firewall_policy_id': ingress_policy, + 'egress_firewall_policy_id': egress_policy}}) self.assertIsNone(result) def test_set_port(self): target = self.resource['id'] port1 = 'additional_port1' port2 = 'additional_port2' + + def _mock_port_fwg(*args, **kwargs): + # 1. Find specified firewall_group + if self.neutronclient.find_resource.call_count == 1: + self.neutronclient.find_resource.assert_called_with( + self.res, target, cmd_resource=const.CMD_FWG) + return {'id': args[1]} + # 2. Find specified 'port' #1 + if self.neutronclient.find_resource.call_count == 2: + self.neutronclient.find_resource.assert_called_with( + 'port', args[1]) + return {'id': args[1]} + # 3. Find specified 'port' #2 + if self.neutronclient.find_resource.call_count == 3: + self.neutronclient.find_resource.assert_called_with( + 'port', args[1]) + return {'id': args[1]} + # 4. Find specified firewall_group and refer 'ports' attribute + if self.neutronclient.find_resource.call_count == 4: + self.neutronclient.find_resource.assert_called_with( + self.res, target, cmd_resource=const.CMD_FWG) + return {'ports': _fwg['ports']} + + self.neutronclient.find_resource.side_effect = _mock_port_fwg + arglist = [ target, '--port', port1, @@ -376,6 +457,7 @@ def test_set_port(self): expect = {'ports': sorted(_fwg['ports'] + [port1, port2])} self.mocked.assert_called_once_with(target, {self.res: expect}) + self.assertEqual(4, self.neutronclient.find_resource.call_count) self.assertIsNone(result) def test_set_no_port(self): @@ -584,6 +666,27 @@ def test_unset_enable(self): def test_unset_port(self): target = self.resource['id'] port = 'old_port' + + def _mock_port_fwg(*args, **kwargs): + # 1. Find specified firewall_group + if self.neutronclient.find_resource.call_count == 1: + self.neutronclient.find_resource.assert_called_with( + self.res, target, cmd_resource=const.CMD_FWG) + return {'id': args[1]} + # 2. Find specified firewall_group and refer 'ports' attribute + if self.neutronclient.find_resource.call_count == 2: + self.neutronclient.find_resource.assert_called_with( + self.res, target, cmd_resource=const.CMD_FWG) + return {'ports': _fwg['ports']} + # 3. Find specified 'port' + if self.neutronclient.find_resource.call_count == 3: + self.neutronclient.find_resource.assert_called_with( + 'port', port) + return {'id': args[1]} + + self.neutronclient.find_resource.side_effect = mock.Mock( + side_effect=_mock_port_fwg) + arglist = [ target, '--port', port, diff --git a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py index fff4e912f..49e43212b 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py @@ -22,6 +22,7 @@ from osc_lib.tests import utils from neutronclient.osc import utils as osc_utils +from neutronclient.osc.v2.fwaas import constants as const from neutronclient.osc.v2.fwaas import firewallpolicy from neutronclient.tests.unit.osc.v2 import fakes as test_fakes from neutronclient.tests.unit.osc.v2.fwaas import common @@ -90,7 +91,6 @@ def _find_resource(*args, **kwargs): self.neutronclient.find_resource = mock.Mock( side_effect=_find_resource) - # fw_common.get_id.side_effect = lambda x, y, z: z osc_utils.find_project = mock.Mock() osc_utils.find_project.id = _fwp['tenant_id'] self.res = 'firewall_policy' @@ -193,6 +193,35 @@ def test_create_with_mandatory_param(self): self.check_results(headers, data, request) + def test_create_with_rules(self): + name = 'my-fwg' + rule1 = 'rule1' + rule2 = 'rule2' + + def _mock_policy(*args, **kwargs): + self.neutronclient.find_resource.assert_called_with( + 'firewall_rule', args[1], cmd_resource=const.CMD_FWR) + return {'id': args[1]} + + self.neutronclient.find_resource.side_effect = _mock_policy + + arglist = [ + name, + '--firewall-rule', rule1, + '--firewall-rule', rule2, + ] + verifylist = [ + ('name', name), + ('firewall_rule', [rule1, rule2]), + ] + request, response = _generate_req_and_res(verifylist) + self._update_expect_response(request, response) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + self.assertEqual(2, self.neutronclient.find_resource.call_count) + + self.check_results(headers, data, request) + def test_create_with_all_params(self): name = 'my-fwp' desc = 'my-desc' @@ -308,6 +337,29 @@ def test_set_rules(self): target = self.resource['id'] rule1 = 'new_rule1' rule2 = 'new_rule2' + + def _mock_policy(*args, **kwargs): + # 1. Find specified firewall_policy + if self.neutronclient.find_resource.call_count == 1: + self.neutronclient.find_resource.assert_called_with( + self.res, target, cmd_resource=const.CMD_FWP) + # 2. Find specified firewall_rule + if self.neutronclient.find_resource.call_count == 2: + self.neutronclient.find_resource.assert_called_with( + 'firewall_rule', args[1], cmd_resource=const.CMD_FWR) + # 3. Find specified firewall_rule + if self.neutronclient.find_resource.call_count == 3: + self.neutronclient.find_resource.assert_called_with( + 'firewall_rule', args[1], cmd_resource=const.CMD_FWR) + # 4. Find specified firewall_policy's 'firewall_rules' attribute + if self.neutronclient.find_resource.call_count == 4: + self.neutronclient.find_resource.assert_called_with( + self.res, target, cmd_resource=const.CMD_FWP) + return {'firewall_rules': _fwp['firewall_rules']} + return {'id': args[1]} + + self.neutronclient.find_resource.side_effect = _mock_policy + arglist = [ target, '--firewall-rule', rule1, @@ -323,6 +375,7 @@ def test_set_rules(self): expect = sorted(set(_fwp['firewall_rules'] + [rule1, rule2])) body = {self.res: {'firewall_rules': expect}} self.mocked.assert_called_once_with(target, body) + self.assertEqual(4, self.neutronclient.find_resource.call_count) self.assertIsNone(result) def test_set_no_rules(self): @@ -357,6 +410,7 @@ def test_set_rules_and_no_rules(self): body = {self.res: {'firewall_rules': [rule1]}} self.mocked.assert_called_once_with(target, body) + self.assertEqual(2, self.neutronclient.find_resource.call_count) self.assertIsNone(result) def test_set_audited(self): @@ -441,6 +495,28 @@ def test_insert_firewall_rule(self): rule = 'new-rule' before = 'before' after = 'after' + + def _mock_policy(*args, **kwargs): + # 1. Find specified firewall_policy + if self.neutronclient.find_resource.call_count == 1: + self.neutronclient.find_resource.assert_called_with( + self.res, target, cmd_resource=const.CMD_FWP) + # 2. Find specified firewall_rule + if self.neutronclient.find_resource.call_count == 2: + self.neutronclient.find_resource.assert_called_with( + 'firewall_rule', args[1], cmd_resource=const.CMD_FWR) + # 3. Find specified firewall_rule as 'before' + if self.neutronclient.find_resource.call_count == 3: + self.neutronclient.find_resource.assert_called_with( + 'firewall_rule', args[1], cmd_resource=const.CMD_FWR) + # 4. Find specified firewall_rule as 'after' + if self.neutronclient.find_resource.call_count == 4: + self.neutronclient.find_resource.assert_called_with( + 'firewall_rule', args[1], cmd_resource=const.CMD_FWR) + return {'id': args[1]} + + self.neutronclient.find_resource.side_effect = _mock_policy + arglist = [ target, rule, @@ -463,6 +539,7 @@ def test_insert_firewall_rule(self): 'insert_after': after }) self.assertIsNone(result) + self.assertEqual(4, self.neutronclient.find_resource.call_count) def test_insert_with_no_firewall_rule(self): target = self.resource['id'] @@ -490,6 +567,21 @@ def setUp(self): def test_remove_firewall_rule(self): target = self.resource['id'] rule = 'remove-rule' + + def _mock_policy(*args, **kwargs): + # 1. Find specified firewall_policy + if self.neutronclient.find_resource.call_count == 1: + self.neutronclient.find_resource.assert_called_with( + self.res, target, cmd_resource=const.CMD_FWP) + # 2. Find specified firewall_rule + if self.neutronclient.find_resource.call_count == 2: + self.neutronclient.find_resource.assert_called_with( + 'firewall_rule', rule, cmd_resource=const.CMD_FWR) + return {'id': args[1]} + + self.neutronclient.find_resource.side_effect = mock.Mock( + side_effect=_mock_policy) + arglist = [ target, rule, @@ -503,6 +595,7 @@ def test_remove_firewall_rule(self): self.mocked.assert_called_once_with( target, {'firewall_rule_id': rule}) self.assertIsNone(result) + self.assertEqual(2, self.neutronclient.find_resource.call_count) def test_remove_with_no_firewall_rule(self): target = self.resource['id'] @@ -566,6 +659,25 @@ def test_unset_firewall_rule_matched(self): _fwp['firewall_rules'] = ['rule1', 'rule2'] target = self.resource['id'] rule = 'rule1' + + def _mock_policy(*args, **kwargs): + # 1. Find specified firewall_policy + if self.neutronclient.find_resource.call_count == 1: + self.neutronclient.find_resource.assert_called_with( + self.res, target, cmd_resource=const.CMD_FWP) + # 2. Find 'firewall_rules' attribute from specified firewall_policy + if self.neutronclient.find_resource.call_count == 2: + self.neutronclient.find_resource.assert_called_with( + self.res, target, cmd_resource=const.CMD_FWP) + return {'firewall_rules': _fwp['firewall_rules']} + # 3. Find specified 'firewall_rule' + if self.neutronclient.find_resource.call_count == 3: + self.neutronclient.find_resource.assert_called_with( + 'firewall_rule', rule, cmd_resource=const.CMD_FWR) + return {'id': args[1]} + + self.neutronclient.find_resource.side_effect = _mock_policy + arglist = [ target, '--firewall-rule', rule, @@ -580,6 +692,7 @@ def test_unset_firewall_rule_matched(self): body = {self.res: {'firewall_rules': ['rule2']}} self.mocked.assert_called_once_with(target, body) self.assertIsNone(result) + self.assertEqual(3, self.neutronclient.find_resource.call_count) def test_unset_all_firewall_rule(self): target = self.resource['id'] diff --git a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py index dd730ee49..be1b3b171 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py @@ -23,6 +23,7 @@ from osc_lib.tests import utils from neutronclient.osc import utils as osc_utils +from neutronclient.osc.v2.fwaas import constants as const from neutronclient.osc.v2.fwaas import firewallrule from neutronclient.tests.unit.osc.v2 import fakes as test_fakes from neutronclient.tests.unit.osc.v2.fwaas import common @@ -76,10 +77,14 @@ def check_results(self, headers, data, exp_req, is_list=False): def setUp(self): super(TestFirewallRule, self).setUp() - self.neutronclient.find_resource = mock.Mock() - self.neutronclient.find_resource.side_effect = \ - lambda x, y, **k: {'id': y} - # fw_common.get_id.side_effect = lambda x, y, z: z + + def _mock_fwr(*args, **kwargs): + self.neutronclient.find_resource.assert_called_once_with( + self.res, self.resource['id'], cmd_resource=const.CMD_FWR) + return {'id': args[1]} + + self.neutronclient.find_resource.side_effect = mock.Mock( + side_effect=_mock_fwr) osc_utils.find_project = mock.Mock() osc_utils.find_project.id = _fwr['tenant_id'] self.res = 'firewall_rule' From 9fa08d1bd0314ac63ad7923cae1c3129c64174e3 Mon Sep 17 00:00:00 2001 From: Yushiro FURUKAWA Date: Mon, 23 Jan 2017 20:22:30 +0900 Subject: [PATCH 546/845] FWaaSv2 - Enable to specify 'any' for protocol This commits fixes to specify 'any' for protocol in firewall_rule. Change-Id: I5d0012afdc319b67877c5ccaea4e6b41efbf6a9e Closes-Bug: #1658598 --- neutronclient/osc/v2/fwaas/firewallrule.py | 3 ++- .../unit/osc/v2/fwaas/test_firewallrule.py | 20 +++++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/neutronclient/osc/v2/fwaas/firewallrule.py b/neutronclient/osc/v2/fwaas/firewallrule.py index a7d9051f3..e63f0cdbd 100644 --- a/neutronclient/osc/v2/fwaas/firewallrule.py +++ b/neutronclient/osc/v2/fwaas/firewallrule.py @@ -147,7 +147,8 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): if parsed_args.description: attrs['description'] = str(parsed_args.description) if parsed_args.protocol: - attrs['protocol'] = parsed_args.protocol + protocol = parsed_args.protocol + attrs['protocol'] = None if protocol == 'any' else protocol if parsed_args.action: attrs['action'] = parsed_args.action if parsed_args.ip_version: diff --git a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py index dd730ee49..1aa73b304 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py @@ -56,6 +56,8 @@ def _generate_req_and_res(verifylist): new_value = True elif (key == 'disable' or key == 'disable_rule') and val: new_value = False + elif (key == 'protocol' and val and val.lower() == 'any'): + new_value = None else: new_value = val request[converted] = new_value @@ -256,7 +258,7 @@ def test_create_with_all_params_action_upper_capitalized(self): self.check_parser, self.cmd, arglist, verifylist) def test_create_with_all_params_protocol_upper_capitalized(self): - for protocol in ('TCP', 'Tcp', 'ANY', 'AnY'): + for protocol in ('TCP', 'Tcp', 'ANY', 'AnY', 'iCMp'): arglist, verifylist = self._set_all_params({'protocol': protocol}) self.assertRaises( testtools.matchers._impl.MismatchError, @@ -354,7 +356,7 @@ def test_set_protocol_with_any(self): result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'protocol': protocol}}) + target, {self.res: {'protocol': None}}) self.assertIsNone(result) def test_set_protocol_with_udp(self): @@ -640,6 +642,20 @@ def setUp(self): self.mocked = self.neutronclient.update_fwaas_firewall_rule self.cmd = firewallrule.UnsetFirewallRule(self.app, self.namespace) + def test_unset_protocol_and_raise(self): + self.neutronclient.update_fwaas_firewall_rule.side_effect = Exception + target = self.resource['id'] + arglist = [ + target, + '--protocol', + ] + verifylist = [ + (self.res, target), + ('protocol', False) + ] + self.assertRaises(utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + def test_unset_source_port(self): target = self.resource['id'] arglist = [ From 42bcdc120250c050ff7ab1df526e36c75e257203 Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Tue, 24 Jan 2017 17:33:59 +0000 Subject: [PATCH 547/845] Update reno for stable/ocata Change-Id: I82db5a7d317f85c58168500e570c76cad7ec07f3 --- releasenotes/source/index.rst | 1 + releasenotes/source/ocata.rst | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 releasenotes/source/ocata.rst diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index 5b5bd02bb..d62abd9dd 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -6,6 +6,7 @@ :maxdepth: 1 unreleased + ocata newton mitaka old_relnotes diff --git a/releasenotes/source/ocata.rst b/releasenotes/source/ocata.rst new file mode 100644 index 000000000..ebe62f42e --- /dev/null +++ b/releasenotes/source/ocata.rst @@ -0,0 +1,6 @@ +=================================== + Ocata Series Release Notes +=================================== + +.. release-notes:: + :branch: origin/stable/ocata From 9a4033105997e88862dee9bdb8de03265d6fbd3a Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Tue, 24 Jan 2017 22:33:31 +0000 Subject: [PATCH 548/845] Updated from global requirements Change-Id: I63fb5e5486670bc369abc5339a2da8e65ba2ec6f --- requirements.txt | 2 +- test-requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 3d38f40df..8b8b8c1f2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ oslo.i18n>=2.1.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 oslo.utils>=3.18.0 # Apache-2.0 os-client-config>=1.22.0 # Apache-2.0 -keystoneauth1>=2.17.0 # Apache-2.0 +keystoneauth1>=2.18.0 # Apache-2.0 # keystoneclient is used only by neutronclient.osc.utils # TODO(amotoki): Drop this after osc.utils has no dependency on keystoneclient python-keystoneclient>=3.8.0 # Apache-2.0 diff --git a/test-requirements.txt b/test-requirements.txt index 2c2652bc2..ed47631bd 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -5,7 +5,7 @@ hacking<0.11,>=0.10.0 coverage>=4.0 # Apache-2.0 fixtures>=3.0.0 # Apache-2.0/BSD -mox3>=0.7.0 # Apache-2.0 +mox3!=0.19.0,>=0.7.0 # Apache-2.0 mock>=2.0 # BSD oslosphinx>=4.7.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 From f2ace0415dd78d74b1f81c81134f7874d787fe5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89douard=20Thuleau?= Date: Fri, 23 Dec 2016 19:30:20 +0100 Subject: [PATCH 549/845] Add BGP VPN OSC commands Change-Id: Ib1ba356e994a98712e00a11ff045df67fbe4c7ea Closes-Bug: #1650204 --- doc/source/usage/osc/v2/networking-bgpvpn.rst | 383 +++++++++++++ .../osc/v2/networking_bgpvpn/__init__.py | 0 .../osc/v2/networking_bgpvpn/bgpvpn.py | 385 +++++++++++++ .../osc/v2/networking_bgpvpn/constants.py | 26 + .../networking_bgpvpn/network_association.py | 63 +++ .../networking_bgpvpn/resource_association.py | 190 +++++++ .../networking_bgpvpn/router_association.py | 63 +++ .../unit/osc/v2/networking_bgpvpn/__init__.py | 0 .../unit/osc/v2/networking_bgpvpn/fakes.py | 183 +++++++ .../osc/v2/networking_bgpvpn/test_bgpvpn.py | 515 ++++++++++++++++++ .../test_resource_association.py | 272 +++++++++ neutronclient/v2_0/client.py | 88 +++ ...etworking-bgpvpn-cli-fdd0cc3a5b14983d.yaml | 5 + setup.cfg | 15 + 14 files changed, 2188 insertions(+) create mode 100644 doc/source/usage/osc/v2/networking-bgpvpn.rst create mode 100644 neutronclient/osc/v2/networking_bgpvpn/__init__.py create mode 100644 neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py create mode 100644 neutronclient/osc/v2/networking_bgpvpn/constants.py create mode 100644 neutronclient/osc/v2/networking_bgpvpn/network_association.py create mode 100644 neutronclient/osc/v2/networking_bgpvpn/resource_association.py create mode 100644 neutronclient/osc/v2/networking_bgpvpn/router_association.py create mode 100644 neutronclient/tests/unit/osc/v2/networking_bgpvpn/__init__.py create mode 100644 neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py create mode 100644 neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py create mode 100644 neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py create mode 100644 releasenotes/notes/support-networking-bgpvpn-cli-fdd0cc3a5b14983d.yaml diff --git a/doc/source/usage/osc/v2/networking-bgpvpn.rst b/doc/source/usage/osc/v2/networking-bgpvpn.rst new file mode 100644 index 000000000..e21563e24 --- /dev/null +++ b/doc/source/usage/osc/v2/networking-bgpvpn.rst @@ -0,0 +1,383 @@ +====== +bgpvpn +====== + +A **bgpvpn** resource contains a set of parameters to define a BGP-based VPN. +BGP-based IP VPNs networks are widely used in the industry especially for +enterprises. The networking BGP VPN project aims at supporting inter-connection +between L3VPNs and Neutron resources, i.e. Networks, Routers and Ports. + +Network v2 + +bgpvpn create +------------- + +Create a BGP VPN resource for a given project + +.. program:: bgpvpn create +.. code:: bash + + openstack bgpvpn create + +.. _bgpvpn_create-bgpvpn: +.. option:: --project + + Owner's project (name or ID) + +.. option:: --project-domain + + Domain the project belongs to (name or ID). This can be used in case + collisions between project names exist + +.. option:: --name + + Name for the BGP VPN. + +.. option:: --route-target + + Add Route Target to import list (repeat option for multiple Route Targets) + +.. option:: --import-target + + Add Route Target to import list (repeat option for multiple Route Targets) + +.. option:: --export-target + + Add Route Target to export list (repeat option for multiple RouteTargets) + +.. option:: --route-distinguisher + + Add Route Distinguisher to the list of Route Distinguishers from which a + Route Distinguishers will be picked from to advertise a VPN route (repeat + option for multiple Route Distinguishers) + +.. option:: --type {l2,l3} + + BGP VPN type selection between IP VPN (l3) and Ethernet VPN (l2) + (default: l3) + +bgpvpn set +---------- + +Set BGP VPN properties + +.. program:: bgpvpn set +.. code:: bash + + openstack bgpvpn set + +.. _bgpvpn_set-bgpvpn: +.. describe:: + + BGP VPN to update (name or ID) + +.. option:: --name + + Name for the BGP VPN + +.. option:: --route-target + + Add Route Target to import list (repeat option for multiple Route Targets) + +.. option:: --no-route-target + + Empty route target list. + +.. option:: --import-target + + Add Route Target to import list (repeat option for multiple Route Targets) + +.. option:: --no-import-target + + Empty import route target list + +.. option:: --export-target + + Add Route Target to export list (repeat option for multiple Route Targets) + +.. option:: --no-export-target + + Empty export route target list + +.. option:: --route-distinguisher + + Add Route Distinguisher to the list of Route Distinguishers from which a + Route Distinguishers will be picked from to advertise a VPN route (repeat + option for multiple Route Distinguishers) + +.. option:: --no-route-distinguisher + + Empty route distinguisher list + +bgpvpn unset +---------- + +Unset BGP VPN properties + +.. program:: bgpvpn unset +.. code:: bash + + openstack bgpvpn unset + +.. _bgpvpn_unset-bgpvpn: +.. describe:: + + BGP VPN to update (name or ID) + +.. option:: --route-target + + Remove Route Target from import/export list (repeat option for multiple + Route Targets) + +.. option:: --all-route-target + + Empty route target list + +.. option:: --import-target + + Remove Route Target from import list (repeat option for multiple Route + Targets) + +.. option:: --all-import-target + + Empty import route target list + +.. option:: --export-target + + Remove Route Target from export list (repeat option for multiple Route + Targets) + +.. option:: --all-export-target + + Empty export route target list + +.. option:: --route-distinguisher + + Remove Route Distinguisher from the list of Route Distinguishers from which + a Route Distinguishers will be picked from to advertise a VPN route + (repeat option for multiple Route Distinguishers) + +.. option:: --all-route-distinguisher + + Empty route distinguisher list + +bgpvpn delete +------------- + +Delete BGP VPN resource(s) + +.. program:: bgpvpn delete +.. code:: bash + + openstack bgpvpn delete + [ ...] + +.. _bgpvpn_delete-bgpvpn: +.. describe:: + BGP VPN(s) to delete (name or ID) + +bgpvpn list +----------- + +List BGP VPN resources + +.. program:: bgpvpn list +.. code:: bash + + openstack bgpvpn list + +.. _bgpvpn_list-bgpvpn: +.. option:: --project + + Owner's project (name or ID) + +.. option:: --project-domain + + Domain the project belongs to (name or ID). This can be used in case + collisions between project names exist. + +.. option:: --long + + List additional fields in output + +.. option:: --property + + Filter property to apply on returned BGP VPNs (repeat to filter on multiple + properties) + +bgpvpn show +----------- + +Show information of a given BGP VPN + +.. program:: bgpvpn show +.. code:: bash + + openstack bgpvpn show + +.. _bgpvpn_show-bgpvpn: +.. describe:: + + BGP VPN to display (name or ID) + +bgpvpn network association create +--------------------------------- + +Create a BGP VPN network association + +.. program:: bgpvpn network association create +.. code:: bash + + openstack bgpvpn network association create + +.. _bgpvpn_net-assoc_create-bgpvpn: +.. describe:: + + ID or name of the BGP VPN + +.. describe:: + + ID or name of the network + +.. option:: --project + + Owner's project (name or ID) + +.. option:: --project-domain + + Domain the project belongs to (name or ID). This can be used in case + collisions between project names exist. + +bgpvpn network association delete +--------------------------------- + +Remove a BGP VPN network association(s) for a given BGP VPN + +.. program:: bgpvpn network association delete +.. code:: bash + + openstack bgpvpn network association delete + [ ...] + +.. _bgpvpn_net-assoc_delete-bgpvpn: +.. describe:: + ID(s) of the network association(s) to remove + +.. describe:: + ID or name of the BGP VPN + +bgpvpn network association list +------------------------------- + +List BGP VPN network associations for a given BGP VPN + +.. program:: bgpvpn network association list +.. code:: bash + + openstack bgpvpn network association list + +.. _bgpvpn_net-assoc_list-bgpvpn: +.. describe:: + ID or name of the BGP VPN + +.. option:: --long + + List additional fields in output + +bgpvpn network association show +------------------------------- + +Show information of a given BGP VPN network association + +.. program:: bgpvpn network association show +.. code:: bash + + openstack bgpvpn network association show + +.. _bgpvpn_net-assoc_show-bgpvpn: +.. describe:: + ID of the network association to look up + +.. describe:: + BGP VPN the association belongs to (name or ID) + +bgpvpn router association create +-------------------------------- + +Create a BGP VPN router association + +.. program:: bgpvpn router association create +.. code:: bash + + openstack bgpvpn router association create + +.. _bgpvpn_router-assoc_create-bgpvpn: +.. describe:: + + ID or name of the BGP VPN + +.. describe:: + + ID or name of the router. + +.. option:: --project + + Owner's project (name or ID) + +.. option:: --project-domain + + Domain the project belongs to (name or ID). This can be used in case + collisions between project names exist. + +bgpvpn router association delete +-------------------------------- + +Delete a BGP VPN router association(s) for a given BGP VPN + +.. program:: bgpvpn router association delete +.. code:: bash + + openstack bgpvpn router association delete + [ ...] + +.. _bgpvpn_router-assoc_delete-bgpvpn: +.. describe:: + ID(s) of the router association(s) to delete. + +.. describe:: + ID or name of the BGP VPN + +bgpvpn router association list +------------------------------ + +List BGP VPN router associations for a given BGP VPN + +.. program:: bgpvpn router association list +.. code:: bash + + openstack bgpvpn router association list + +.. _bgpvpn_router-assoc_list-bgpvpn: +.. describe:: + ID or name of the BGP VPN + +.. option:: --long + + List additional fields in output + +bgpvpn router association show +------------------------------ + +Show information of a given BGP VPN router association + +.. program:: bgpvpn router association show +.. code:: bash + + openstack bgpvpn router association show + +.. _bgpvpn_router-assoc_show-bgpvpn: +.. describe:: + ID of the router association to look up + +.. describe:: + BGP VPN the association belongs to (name or ID) \ No newline at end of file diff --git a/neutronclient/osc/v2/networking_bgpvpn/__init__.py b/neutronclient/osc/v2/networking_bgpvpn/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py b/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py new file mode 100644 index 000000000..1e5dbb0ea --- /dev/null +++ b/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py @@ -0,0 +1,385 @@ +# Copyright (c) 2016 Juniper Networks Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +import logging + +from osc_lib.cli.parseractions import KeyValueAction +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils as osc_utils + +from neutronclient._i18n import _ +from neutronclient._i18n import _LE +from neutronclient._i18n import _LW +from neutronclient.osc import utils as nc_osc_utils +from neutronclient.osc.v2.networking_bgpvpn import constants + +LOG = logging.getLogger(__name__) + +_attr_map = ( + ('id', 'ID', nc_osc_utils.LIST_BOTH), + ('tenant_id', 'Project ID', nc_osc_utils.LIST_LONG_ONLY), + ('name', 'Name', nc_osc_utils.LIST_BOTH), + ('type', 'Type', nc_osc_utils.LIST_BOTH), + ('route_targets', 'Route Targets', nc_osc_utils.LIST_LONG_ONLY), + ('import_targets', 'Import Targets', nc_osc_utils.LIST_LONG_ONLY), + ('export_targets', 'Export Targets', nc_osc_utils.LIST_LONG_ONLY), + ('route_distinguishers', 'Route Distinguishers', + nc_osc_utils.LIST_LONG_ONLY), + ('networks', 'Associated Networks', nc_osc_utils.LIST_LONG_ONLY), + ('routers', 'Associated Routers', nc_osc_utils.LIST_LONG_ONLY), +) +_formatters = { + 'route_targets': osc_utils.format_list, + 'import_targets': osc_utils.format_list, + 'export_targets': osc_utils.format_list, + 'route_distinguishers': osc_utils.format_list, + 'networks': osc_utils.format_list, + 'routers': osc_utils.format_list, +} + + +def _get_common_parser(parser, update=None): + """Adds to parser arguments common to create, set and unset commands. + + :params ArgumentParser parser: argparse object contains all command's + arguments + :params string update: Determines if it is a create command (value: None), + it is a set command (value: 'set') or if it is an + unset command (value: 'unset') + """ + ADD_RT = _("Add Route Target to import/export list") + REMOVE_RT = _("Remove Route Target from import/export list") + ADD_IMPORT_RT = _("Add Route Target to import list") + REMOVE_IMPORT_RT = _("Remove Route Target from import list") + ADD_EXPORT_RT = _("Add Route Target to export list") + REMOVE_EXPORT_RT = _("Remove Route Target from export list") + ADD_RD = _("Add Route Distinguisher to the list of Route Distinguishers " + "from which a Route Distinguishers will be picked from to " + "advertise a VPN route") + REMOVE_RD = _("Remove Route Distinguisher from the list of Route " + "Distinguishers from which a Route Distinguishers will be " + "picked from to advertise a VPN route") + REPEAT_RT = _("repeat option for multiple Route Targets") + REPEAT_RD = _("repeat option for multiple Route Distinguishers") + + def is_appended(): + return update is None or update == 'set' + + if update is None or update == 'set': + parser.add_argument( + '--name', + metavar="", + help=_("Name of the BGP VPN"), + ) + parser.add_argument( + '--route-target', + dest='route_targets', + action='append', + metavar="", + help="%s (%s)" % ((ADD_RT if is_appended() else REMOVE_RT), REPEAT_RT), + ) + if update: + parser.add_argument( + '--no-route-target' if update == 'set' else '--all-route-target', + dest='purge_route_target', + action='store_true', + help=_('Empty route target list'), + ) + parser.add_argument( + '--import-target', + dest='import_targets', + action='append', + metavar="", + help="%s (%s)" % ((ADD_IMPORT_RT if is_appended() else + REMOVE_IMPORT_RT), REPEAT_RT), + ) + if update: + parser.add_argument( + '--no-import-target' if update == 'set' else '--all-import-target', + dest='purge_import_target', + action='store_true', + help=_('Empty import route target list'), + ) + parser.add_argument( + '--export-target', + dest='export_targets', + action='append', + metavar="", + help="%s (%s)" % ((ADD_EXPORT_RT if is_appended() else + REMOVE_EXPORT_RT), REPEAT_RT), + ) + if update: + parser.add_argument( + '--no-export-target' if update == 'set' else + '--all-export-target', + dest='purge_export_target', + action='store_true', + help=_('Empty export route target list'), + ) + parser.add_argument( + '--route-distinguisher', + dest='route_distinguishers', + action='append', + metavar="", + help="%s (%s)" % ((ADD_RD if is_appended() else REMOVE_RD), REPEAT_RD), + ) + if update: + parser.add_argument( + '--no-route-distinguisher' if update == 'set' else + '--all-route-distinguisher', + dest='purge_route_distinguisher', + action='store_true', + help=_('Empty route distinguisher list'), + ) + + +def _args2body(client_manager, id, action, args): + + if (not (args.purge_route_target and args.purge_import_target and + args.purge_export_target and args.purge_route_distinguisher) and + (args.route_targets or args.import_targets or + args.export_targets or args.route_distinguishers)): + bgpvpn = client_manager.neutronclient.show_bgpvpn(id)['bgpvpn'] + + attrs = {} + + if 'name' in args and args.name is not None: + attrs['name'] = str(args.name) + + if args.purge_route_target: + attrs['route_targets'] = [] + elif args.route_targets: + if action == 'set': + attrs['route_targets'] = list(set(bgpvpn['route_targets']) | + set(args.route_targets)) + elif action == 'unset': + attrs['route_targets'] = list(set(bgpvpn['route_targets']) - + set(args.route_targets)) + + if args.purge_import_target: + attrs['import_targets'] = [] + elif args.import_targets: + if action == 'set': + attrs['import_targets'] = list(set(bgpvpn['import_targets']) | + set(args.import_targets)) + elif action == 'unset': + attrs['import_targets'] = list(set(bgpvpn['import_targets']) - + set(args.import_targets)) + + if args.purge_export_target: + attrs['export_targets'] = [] + elif args.export_targets: + if action == 'set': + attrs['export_targets'] = list(set(bgpvpn['export_targets']) | + set(args.export_targets)) + elif action == 'unset': + attrs['export_targets'] = list(set(bgpvpn['export_targets']) - + set(args.export_targets)) + + if args.purge_route_distinguisher: + attrs['route_distinguishers'] = [] + elif args.route_distinguishers: + if action == 'set': + attrs['route_distinguishers'] = list( + set(bgpvpn['route_distinguishers']) | + set(args.route_distinguishers)) + elif action == 'unset': + attrs['route_distinguishers'] = list( + set(bgpvpn['route_distinguishers']) - + set(args.route_distinguishers)) + + return {constants.BGPVPN: attrs} + + +class CreateBgpvpn(command.ShowOne): + _description = _("Create BGP VPN resource") + + def get_parser(self, prog_name): + parser = super(CreateBgpvpn, self).get_parser(prog_name) + nc_osc_utils.add_project_owner_option_to_parser(parser) + _get_common_parser(parser) + parser.add_argument( + '--type', + default='l3', + choices=['l2', 'l3'], + help=_("BGP VPN type selection between IP VPN (l3) and Ethernet " + "VPN (l2) (default: %(default)s)"), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + attrs = {} + if parsed_args.name is not None: + attrs['name'] = str(parsed_args.name) + if parsed_args.type is not None: + attrs['type'] = parsed_args.type + if parsed_args.route_targets is not None: + attrs['route_targets'] = parsed_args.route_targets + if parsed_args.import_targets is not None: + attrs['import_targets'] = parsed_args.import_targets + if parsed_args.export_targets is not None: + attrs['export_targets'] = parsed_args.export_targets + if parsed_args.route_distinguishers is not None: + attrs['route_distinguishers'] = parsed_args.route_distinguishers + if 'project' in parsed_args and parsed_args.project is not None: + project_id = nc_osc_utils.find_project( + self.app.client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + attrs['tenant_id'] = project_id + body = {constants.BGPVPN: attrs} + obj = client.create_bgpvpn(body)[constants.BGPVPN] + columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) + data = osc_utils.get_dict_properties(obj, columns, + formatters=_formatters) + return display_columns, data + + +class SetBgpvpn(command.Command): + _description = _("Set BGP VPN properties") + + def get_parser(self, prog_name): + parser = super(SetBgpvpn, self).get_parser(prog_name) + parser.add_argument( + 'bgpvpn', + metavar="", + help=_("BGP VPN to update (name or ID)"), + ) + _get_common_parser(parser, update='set') + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + id = client.find_resource(constants.BGPVPN, parsed_args.bgpvpn)['id'] + body = _args2body(self.app.client_manager, id, 'set', parsed_args) + client.update_bgpvpn(id, body) + + +class UnsetBgpvpn(command.Command): + _description = _("Unset BGP VPN properties") + + def get_parser(self, prog_name): + parser = super(UnsetBgpvpn, self).get_parser(prog_name) + parser.add_argument( + 'bgpvpn', + metavar="", + help=_("BGP VPN to update (name or ID)"), + ) + _get_common_parser(parser, update='unset') + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + id = client.find_resource(constants.BGPVPN, parsed_args.bgpvpn)['id'] + body = _args2body(self.app.client_manager, id, 'unset', parsed_args) + client.update_bgpvpn(id, body) + + +class DeleteBgpvpn(command.Command): + _description = _("Delete BGP VPN resource(s)") + + def get_parser(self, prog_name): + parser = super(DeleteBgpvpn, self).get_parser(prog_name) + parser.add_argument( + 'bgpvpns', + metavar="", + nargs="+", + help=_("BGP VPN(s) to delete (name or ID)"), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + fails = 0 + for id_or_name in parsed_args.bgpvpns: + try: + id = client.find_resource(constants.BGPVPN, id_or_name)['id'] + client.delete_bgpvpn(id) + LOG.warning(_LW("BGP VPN %(id)s deleted"), {'id': id}) + except Exception as e: + fails += 1 + LOG.error(_LE("Failed to delete BGP VPN with name or ID " + "'%(id_or_name)s': %(e)s"), + {'id_or_name': id_or_name, 'e': e}) + if fails > 0: + msg = (_("Failed to delete %(fails)s of %(total)s BGP VPN.") % + {'fails': fails, 'total': len(parsed_args.bgpvpns)}) + raise exceptions.CommandError(msg) + + +class ListBgpvpn(command.Lister): + _description = _("List BGP VPN resources") + + def get_parser(self, prog_name): + parser = super(ListBgpvpn, self).get_parser(prog_name) + nc_osc_utils.add_project_owner_option_to_parser(parser) + parser.add_argument( + '--long', + action='store_true', + help=_("List additional fields in output"), + ) + parser.add_argument( + '--property', + metavar="", + default=dict(), + help=_("Filter property to apply on returned BGP VPNs (repeat to " + "filter on multiple properties)"), + action=KeyValueAction, + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + params = {} + if parsed_args.project is not None: + project_id = nc_osc_utils.find_project( + self.app.client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + params['tenant_id'] = project_id + if parsed_args.property: + params.update(parsed_args.property) + objs = client.list_bgpvpns(**params)[constants.BGPVPNS] + headers, columns = nc_osc_utils.get_column_definitions( + _attr_map, long_listing=parsed_args.long) + return (headers, (osc_utils.get_dict_properties( + s, columns, formatters=_formatters) for s in objs)) + + +class ShowBgpvpn(command.ShowOne): + _description = _("Show information of a given BGP VPN") + + def get_parser(self, prog_name): + parser = super(ShowBgpvpn, self).get_parser(prog_name) + parser.add_argument( + 'bgpvpn', + metavar="", + help=_("BGP VPN to display (name or ID)"), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + id = client.find_resource(constants.BGPVPN, parsed_args.bgpvpn)['id'] + obj = client.show_bgpvpn(id)[constants.BGPVPN] + columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) + data = osc_utils.get_dict_properties(obj, columns, + formatters=_formatters) + return display_columns, data diff --git a/neutronclient/osc/v2/networking_bgpvpn/constants.py b/neutronclient/osc/v2/networking_bgpvpn/constants.py new file mode 100644 index 000000000..775721e9c --- /dev/null +++ b/neutronclient/osc/v2/networking_bgpvpn/constants.py @@ -0,0 +1,26 @@ +# Copyright (c) 2016 Juniper Networks 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. +# + +BGPVPN = 'bgpvpn' +BGPVPNS = '%ss' % BGPVPN + +NETWORK_RESOURCE_NAME = 'network' +NETWORK_ASSOC = '%s_association' % NETWORK_RESOURCE_NAME +NETWORK_ASSOCS = '%ss' % NETWORK_ASSOC + +ROUTER_RESOURCE_NAME = 'router' +ROUTER_ASSOC = '%s_association' % ROUTER_RESOURCE_NAME +ROUTER_ASSOCS = '%ss' % ROUTER_ASSOC diff --git a/neutronclient/osc/v2/networking_bgpvpn/network_association.py b/neutronclient/osc/v2/networking_bgpvpn/network_association.py new file mode 100644 index 000000000..4799aa59a --- /dev/null +++ b/neutronclient/osc/v2/networking_bgpvpn/network_association.py @@ -0,0 +1,63 @@ +# Copyright (c) 2016 Juniper Networks 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. +# + + +from neutronclient._i18n import _ +from neutronclient.osc import utils as nc_osc_utils +from neutronclient.osc.v2.networking_bgpvpn import constants +from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ + CreateBgpvpnResAssoc +from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ + DeleteBgpvpnResAssoc +from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ + ListBgpvpnResAssoc +from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ + ShowBgpvpnResAssoc + + +class BgpvpnNetAssoc(object): + _assoc_res_name = constants.NETWORK_RESOURCE_NAME + _resource = constants.NETWORK_ASSOC + _resource_plural = constants.NETWORK_ASSOCS + + _attr_map = ( + ('id', 'ID', nc_osc_utils.LIST_BOTH), + ('tenant_id', 'Project ID', nc_osc_utils.LIST_LONG_ONLY), + ('%s_id' % _assoc_res_name, '%s ID' % _assoc_res_name.capitalize(), + nc_osc_utils.LIST_BOTH), + ) + _formatters = {} + + +class CreateBgpvpnNetAssoc(BgpvpnNetAssoc, CreateBgpvpnResAssoc): + _description = _("Create a BGP VPN network association") + pass + + +class DeleteBgpvpnNetAssoc(BgpvpnNetAssoc, DeleteBgpvpnResAssoc): + _description = _("Delete a BGP VPN network association(s) for a given BGP " + "VPN") + pass + + +class ListBgpvpnNetAssoc(BgpvpnNetAssoc, ListBgpvpnResAssoc): + _description = _("List BGP VPN network associations for a given BGP VPN") + pass + + +class ShowBgpvpnNetAssoc(BgpvpnNetAssoc, ShowBgpvpnResAssoc): + _description = _("Show information of a given BGP VPN network association") + pass diff --git a/neutronclient/osc/v2/networking_bgpvpn/resource_association.py b/neutronclient/osc/v2/networking_bgpvpn/resource_association.py new file mode 100644 index 000000000..98f54cdbe --- /dev/null +++ b/neutronclient/osc/v2/networking_bgpvpn/resource_association.py @@ -0,0 +1,190 @@ +# Copyright (c) 2016 Juniper Networks Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +import logging + +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils as osc_utils + +from neutronclient._i18n import _ +from neutronclient._i18n import _LE +from neutronclient._i18n import _LW +from neutronclient.osc import utils as nc_osc_utils +from neutronclient.osc.v2.networking_bgpvpn import constants + +LOG = logging.getLogger(__name__) + + +class CreateBgpvpnResAssoc(command.ShowOne): + """Create a BGP VPN resource association""" + + def get_parser(self, prog_name): + parser = super(CreateBgpvpnResAssoc, self).get_parser(prog_name) + nc_osc_utils.add_project_owner_option_to_parser(parser) + parser.add_argument( + 'bgpvpn', + metavar="", + help=(_("BGP VPN to apply the %s association (name or ID)") % + self._assoc_res_name), + ) + parser.add_argument( + 'resource', + metavar="<%s>" % self._assoc_res_name, + help=(_("%s to associate the BGP VPN (name or ID)") % + self._assoc_res_name.capitalize()), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + create_method = getattr( + client, 'create_bgpvpn_%s_assoc' % self._assoc_res_name) + bgpvpn = client.find_resource(constants.BGPVPN, parsed_args.bgpvpn) + assoc_res = client.find_resource(self._assoc_res_name, + parsed_args.resource) + body = { + self._resource: { + '%s_id' % self._assoc_res_name: assoc_res['id'], + }, + } + if 'project' in parsed_args and parsed_args.project is not None: + project_id = nc_osc_utils.find_project( + self.app.client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + body[self._resource]['tenant_id'] = project_id + obj = create_method(bgpvpn['id'], body)[self._resource] + columns, display_columns = nc_osc_utils.get_columns(obj, + self._attr_map) + data = osc_utils.get_dict_properties(obj, columns, + formatters=self._formatters) + return display_columns, data + + +class DeleteBgpvpnResAssoc(command.Command): + """Remove a BGP VPN resource association(s) for a given BGP VPN""" + + def get_parser(self, prog_name): + parser = super(DeleteBgpvpnResAssoc, self).get_parser(prog_name) + parser.add_argument( + 'resource_association_ids', + metavar="<%s association ID>" % self._assoc_res_name, + nargs="+", + help=(_("%s association ID(s) to remove") % + self._assoc_res_name.capitalize()), + ) + parser.add_argument( + 'bgpvpn', + metavar="", + help=_("BGP VPN the association belongs to (name or ID)"), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + delete_method = getattr( + client, 'delete_bgpvpn_%s_assoc' % self._assoc_res_name) + bgpvpn = client.find_resource(constants.BGPVPN, parsed_args.bgpvpn) + fails = 0 + for id in parsed_args.resource_association_ids: + try: + delete_method(bgpvpn['id'], id) + LOG.warning( + _LW("%(assoc_res_name)s association %(id)s deleted"), + {'assoc_res_name': self._assoc_res_name.capitalize(), + 'id': id}) + except Exception as e: + fails += 1 + LOG.error(_LE("Failed to delete %(assoc_res_name)s " + "association with ID '%(id)s': %(e)s"), + {'assoc_res_name': self._assoc_res_name, + 'id': id, + 'e': e}) + if fails > 0: + msg = (_("Failed to delete %(fails)s of %(total)s " + "%(assoc_res_name)s BGP VPN association(s).") % + {'fails': fails, + 'total': len(parsed_args.resource_association_ids), + 'assoc_res_name': self._assoc_res_name}) + raise exceptions.CommandError(msg) + + +class ListBgpvpnResAssoc(command.Lister): + """List BGP VPN resource associations for a given BGP VPN""" + + def get_parser(self, prog_name): + parser = super(ListBgpvpnResAssoc, self).get_parser(prog_name) + parser.add_argument( + 'bgpvpn', + metavar="", + help=_("BGP VPN listed associations belong to (name or ID)"), + ) + parser.add_argument( + '--long', + action='store_true', + help=_("List additional fields in output"), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + list_method = getattr(client, + 'list_bgpvpn_%s_assocs' % self._assoc_res_name) + bgpvpn = client.find_resource(constants.BGPVPN, parsed_args.bgpvpn) + objs = list_method(bgpvpn['id'], + retrieve_all=True)[self._resource_plural] + headers, columns = nc_osc_utils.get_column_definitions( + self._attr_map, long_listing=parsed_args.long) + return (headers, (osc_utils.get_dict_properties( + s, columns, formatters=self._formatters) for s in objs)) + + +class ShowBgpvpnResAssoc(command.ShowOne): + """Show information of a given BGP VPN resource association""" + + def get_parser(self, prog_name): + parser = super(ShowBgpvpnResAssoc, self).get_parser(prog_name) + parser.add_argument( + 'resource_association_id', + metavar="<%s association ID>" % self._assoc_res_name, + help=(_("%s association ID to look up") % + self._assoc_res_name.capitalize()), + ) + parser.add_argument( + 'bgpvpn', + metavar="", + help=_("BGP VPN the association belongs to (name or ID)"), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + show_method = getattr(client, + 'show_bgpvpn_%s_assoc' % self._assoc_res_name) + bgpvpn = client.find_resource(constants.BGPVPN, parsed_args.bgpvpn) + assoc = client.find_resource_by_id( + self._resource, + parsed_args.resource_association_id, + cmd_resource='bgpvpn_%s_assoc' % self._assoc_res_name, + parent_id=bgpvpn['id']) + obj = show_method(bgpvpn['id'], assoc['id'])[self._resource] + columns, display_columns = nc_osc_utils.get_columns(obj, + self._attr_map) + data = osc_utils.get_dict_properties(obj, columns, + formatters=self._formatters) + return display_columns, data diff --git a/neutronclient/osc/v2/networking_bgpvpn/router_association.py b/neutronclient/osc/v2/networking_bgpvpn/router_association.py new file mode 100644 index 000000000..dd3ce2ae4 --- /dev/null +++ b/neutronclient/osc/v2/networking_bgpvpn/router_association.py @@ -0,0 +1,63 @@ +# Copyright (c) 2016 Juniper Routerworks 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. +# + + +from neutronclient._i18n import _ +from neutronclient.osc import utils as nc_osc_utils +from neutronclient.osc.v2.networking_bgpvpn import constants +from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ + CreateBgpvpnResAssoc +from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ + DeleteBgpvpnResAssoc +from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ + ListBgpvpnResAssoc +from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ + ShowBgpvpnResAssoc + + +class BgpvpnRouterAssoc(object): + _assoc_res_name = constants.ROUTER_RESOURCE_NAME + _resource = constants.ROUTER_ASSOC + _resource_plural = constants.ROUTER_ASSOCS + + _attr_map = ( + ('id', 'ID', nc_osc_utils.LIST_BOTH), + ('tenant_id', 'Project ID', nc_osc_utils.LIST_LONG_ONLY), + ('%s_id' % _assoc_res_name, '%s ID' % _assoc_res_name.capitalize(), + nc_osc_utils.LIST_BOTH), + ) + _formatters = {} + + +class CreateBgpvpnRouterAssoc(BgpvpnRouterAssoc, CreateBgpvpnResAssoc): + _description = _("Create a BGP VPN router association") + pass + + +class DeleteBgpvpnRouterAssoc(BgpvpnRouterAssoc, DeleteBgpvpnResAssoc): + _description = _("Delete a BGP VPN router association(s) for a given BGP " + "VPN") + pass + + +class ListBgpvpnRouterAssoc(BgpvpnRouterAssoc, ListBgpvpnResAssoc): + _description = _("List BGP VPN router associations for a given BGP VPN") + pass + + +class ShowBgpvpnRouterAssoc(BgpvpnRouterAssoc, ShowBgpvpnResAssoc): + _description = _("Show information of a given BGP VPN router association") + pass diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/__init__.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py new file mode 100644 index 000000000..2c3bcfa8f --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py @@ -0,0 +1,183 @@ +# Copyright (c) 2016 Juniper Networks 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 copy +import mock + +from neutronclient.osc import utils as nc_osc_utils +from neutronclient.osc.v2.networking_bgpvpn import constants +from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ + CreateBgpvpnResAssoc +from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ + DeleteBgpvpnResAssoc +from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ + ListBgpvpnResAssoc +from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ + ShowBgpvpnResAssoc +from neutronclient.tests.unit.osc.v2 import fakes as test_fakes + + +class TestNeutronClientBgpvpn(test_fakes.TestNeutronClientOSCV2): + + def setUp(self): + super(TestNeutronClientBgpvpn, self).setUp() + self.neutronclient.find_resource = mock.Mock( + side_effect=lambda resource, name_or_id, project_id=None, + cmd_resource=None, parent_id=None, fields=None: + {'id': name_or_id}) + self.neutronclient.find_resource_by_id = mock.Mock( + side_effect=lambda resource, resource_id, cmd_resource=None, + parent_id=None, fields=None: + {'id': resource_id}) + nc_osc_utils.find_project = mock.Mock( + side_effect=lambda _, name_or_id, __: mock.Mock(id=name_or_id)) + + +class FakeBgpvpn(object): + """Fake BGP VPN with attributes.""" + + @staticmethod + def create_one_bgpvpn(attrs=None): + """Create a fake BGP VPN.""" + + attrs = attrs or {} + + # Set default attributes. + bgpvpn_attrs = { + 'id': 'fake_bgpvpn_id', + 'tenant_id': 'fake_project_id', + 'name': '', + 'type': 'l3', + 'route_targets': [], + 'import_targets': [], + 'export_targets': [], + 'route_distinguishers': [], + 'networks': [], + 'routers': [], + } + + # Overwrite default attributes. + bgpvpn_attrs.update(attrs) + return copy.deepcopy(bgpvpn_attrs) + + @staticmethod + def create_bgpvpns(attrs=None, count=1): + """Create multiple fake BGP VPN.""" + + bgpvpns = [] + for i in range(0, count): + if attrs is None: + attrs = {'id': 'fake_id%d' % i} + elif getattr(attrs, 'id', None) is None: + attrs['id'] = 'fake_id%d' % i + bgpvpns.append(FakeBgpvpn.create_one_bgpvpn(attrs)) + + return {constants.BGPVPNS: bgpvpns} + + +class BgpvpnFakeAssoc(object): + _assoc_res_name = 'fake_resource' + _resource = '%s_association' % _assoc_res_name + _resource_plural = '%ss' % _resource + + _attr_map = ( + ('id', 'ID', nc_osc_utils.LIST_BOTH), + ('tenant_id', 'Project ID', nc_osc_utils.LIST_LONG_ONLY), + ('%s_id' % _assoc_res_name, '%s ID' % _assoc_res_name.capitalize(), + nc_osc_utils.LIST_BOTH), + ) + _formatters = {} + + +class CreateBgpvpnFakeResAssoc(BgpvpnFakeAssoc, CreateBgpvpnResAssoc): + pass + + +class DeleteBgpvpnFakeResAssoc(BgpvpnFakeAssoc, DeleteBgpvpnResAssoc): + pass + + +class ListBgpvpnFakeResAssoc(BgpvpnFakeAssoc, ListBgpvpnResAssoc): + pass + + +class ShowBgpvpnFakeResAssoc(BgpvpnFakeAssoc, ShowBgpvpnResAssoc): + pass + + +class FakeResource(object): + """Fake resource with minimal attributes.""" + + @staticmethod + def create_one_resource(attrs=None): + """Create a fake resource.""" + + attrs = attrs or {} + + # Set default attributes. + res_attrs = { + 'id': 'fake_resource_id', + 'tenant_id': 'fake_project_id', + } + + # Overwrite default attributes. + res_attrs.update(attrs) + return copy.deepcopy(res_attrs) + + @staticmethod + def create_resources(attrs=None, count=1): + """Create multiple fake resources.""" + + resources = [] + for i in range(0, count): + if attrs is None: + attrs = {'id': 'fake_id%d' % i} + elif getattr(attrs, 'id', None) is None: + attrs['id'] = 'fake_id%d' % i + resources.append(FakeResource.create_one_resource(attrs)) + + return {'%ss' % BgpvpnFakeAssoc._assoc_res_name: resources} + + +class FakeResAssoc(object): + """Fake resource association with minimal attributes.""" + + @staticmethod + def create_one_resource_association(resource): + """Create a fake resource association.""" + + res_assoc_attrs = { + 'id': 'fake_association_id', + 'tenant_id': resource['tenant_id'], + 'fake_resource_id': resource['id'], + } + return copy.deepcopy(res_assoc_attrs) + + @staticmethod + def create_resource_associations(resources): + """Create multiple fake resource associations.""" + + res_assocs = [] + for idx, resource in enumerate( + resources['%ss' % BgpvpnFakeAssoc._assoc_res_name]): + res_assoc_attrs = { + 'id': 'fake_association_id%d' % idx, + 'tenant_id': resource['tenant_id'], + 'fake_resource_id': resource['id'], + } + res_assocs.append(copy.deepcopy(res_assoc_attrs)) + + return {BgpvpnFakeAssoc._resource_plural: res_assocs} diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py new file mode 100644 index 000000000..3d8348824 --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py @@ -0,0 +1,515 @@ +# Copyright (c) 2016 Juniper Networks 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 copy +import mock +import operator + +from osc_lib import exceptions +from osc_lib import utils as osc_utils + +from neutronclient.osc import utils as nc_osc_utils +from neutronclient.osc.v2.networking_bgpvpn import bgpvpn +from neutronclient.osc.v2.networking_bgpvpn import constants +from neutronclient.tests.unit.osc.v2.networking_bgpvpn import fakes + + +columns_short = tuple(col for col, _, listing_mode in bgpvpn._attr_map + if listing_mode in (nc_osc_utils.LIST_BOTH, + nc_osc_utils.LIST_SHORT_ONLY)) +columns_long = tuple(col for col, _, listing_mode in bgpvpn._attr_map + if listing_mode in (nc_osc_utils.LIST_BOTH, + nc_osc_utils.LIST_LONG_ONLY)) +headers_short = tuple(head for _, head, listing_mode in bgpvpn._attr_map + if listing_mode in (nc_osc_utils.LIST_BOTH, + nc_osc_utils.LIST_SHORT_ONLY)) +headers_long = tuple(head for _, head, listing_mode in bgpvpn._attr_map + if listing_mode in (nc_osc_utils.LIST_BOTH, + nc_osc_utils.LIST_LONG_ONLY)) +sorted_attr_map = sorted(bgpvpn._attr_map, key=operator.itemgetter(1)) +sorted_columns = tuple(col for col, _, _ in sorted_attr_map) +sorted_headers = tuple(head for _, head, _ in sorted_attr_map) + + +def _get_data(attrs, columns=sorted_columns): + return osc_utils.get_dict_properties(attrs, columns, + formatters=bgpvpn._formatters) + + +class TestCreateBgpvpn(fakes.TestNeutronClientBgpvpn): + def setUp(self): + super(TestCreateBgpvpn, self).setUp() + self.cmd = bgpvpn.CreateBgpvpn(self.app, self.namespace) + + def test_create_bgpvpn_with_no_args(self): + fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() + self.neutronclient.create_bgpvpn = mock.Mock( + return_value={constants.BGPVPN: fake_bgpvpn}) + arglist = [] + verifylist = [ + ('project', None), + ('name', None), + ('type', 'l3'), + ('route_targets', None), + ('import_targets', None), + ('export_targets', None), + ('route_distinguishers', None), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + cols, data = self.cmd.take_action(parsed_args) + + self.neutronclient.create_bgpvpn.assert_called_once_with( + {constants.BGPVPN: {'type': 'l3'}}) + self.assertEqual(sorted_headers, cols) + self.assertEqual(_get_data(fake_bgpvpn), data) + + def test_create_bgpvpn_with_all_args(self): + attrs = { + 'tenant_id': 'new_fake_project_id', + 'name': 'fake_name', + 'type': 'l2', + 'route_targets': ['fake_rt1', 'fake_rt2', 'fake_rt3'], + 'import_targets': ['fake_irt1', 'fake_irt2', 'fake_irt3'], + 'export_targets': ['fake_ert1', 'fake_ert2', 'fake_ert3'], + 'route_distinguishers': ['fake_rd1', 'fake_rd2', 'fake_rd3'], + } + fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn(attrs) + self.neutronclient.create_bgpvpn = mock.Mock( + return_value={constants.BGPVPN: fake_bgpvpn}) + arglist = [ + '--project', fake_bgpvpn['tenant_id'], + '--name', fake_bgpvpn['name'], + '--type', fake_bgpvpn['type'], + ] + for rt in fake_bgpvpn['route_targets']: + arglist.extend(['--route-target', rt]) + for rt in fake_bgpvpn['import_targets']: + arglist.extend(['--import-target', rt]) + for rt in fake_bgpvpn['export_targets']: + arglist.extend(['--export-target', rt]) + for rd in fake_bgpvpn['route_distinguishers']: + arglist.extend(['--route-distinguisher', rd]) + verifylist = [ + ('project', fake_bgpvpn['tenant_id']), + ('name', fake_bgpvpn['name']), + ('type', fake_bgpvpn['type']), + ('route_targets', fake_bgpvpn['route_targets']), + ('import_targets', fake_bgpvpn['import_targets']), + ('export_targets', fake_bgpvpn['export_targets']), + ('route_distinguishers', fake_bgpvpn['route_distinguishers']), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + cols, data = self.cmd.take_action(parsed_args) + + fake_bgpvpn_call = copy.deepcopy(fake_bgpvpn) + fake_bgpvpn_call.pop('id') + fake_bgpvpn_call.pop('networks') + fake_bgpvpn_call.pop('routers') + self.neutronclient.create_bgpvpn.assert_called_once_with( + {constants.BGPVPN: fake_bgpvpn_call}) + self.assertEqual(sorted_headers, cols) + self.assertEqual(_get_data(fake_bgpvpn), data) + + +class TestSetBgpvpn(fakes.TestNeutronClientBgpvpn): + def setUp(self): + super(TestSetBgpvpn, self).setUp() + self.cmd = bgpvpn.SetBgpvpn(self.app, self.namespace) + + def test_set_bgpvpn(self): + attrs = { + 'route_targets': ['set_rt1', 'set_rt2', 'set_rt3'], + 'import_targets': ['set_irt1', 'set_irt2', 'set_irt3'], + 'export_targets': ['set_ert1', 'set_ert2', 'set_ert3'], + 'route_distinguishers': ['set_rd1', 'set_rd2', 'set_rd3'], + } + fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn(attrs) + self.neutronclient.show_bgpvpn = mock.Mock( + return_value={constants.BGPVPN: fake_bgpvpn}) + self.neutronclient.update_bgpvpn = mock.Mock() + arglist = [ + fake_bgpvpn['id'], + '--name', 'set_name', + '--route-target', 'set_rt1', + '--import-target', 'set_irt1', + '--export-target', 'set_ert1', + '--route-distinguisher', 'set_rd1', + ] + verifylist = [ + ('bgpvpn', fake_bgpvpn['id']), + ('name', 'set_name'), + ('route_targets', ['set_rt1']), + ('purge_route_target', False), + ('import_targets', ['set_irt1']), + ('purge_import_target', False), + ('export_targets', ['set_ert1']), + ('purge_export_target', False), + ('route_distinguishers', ['set_rd1']), + ('purge_route_distinguisher', False), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + attrs = { + 'name': 'set_name', + 'route_targets': list(set(fake_bgpvpn['route_targets']) | + set(['set_rt1'])), + 'import_targets': list(set(fake_bgpvpn['import_targets']) | + set(['set_irt1'])), + 'export_targets': list(set(fake_bgpvpn['export_targets']) | + set(['set_ert1'])), + 'route_distinguishers': list( + set(fake_bgpvpn['route_distinguishers']) | set(['set_rd1'])), + } + self.neutronclient.update_bgpvpn.assert_called_once_with( + fake_bgpvpn['id'], {constants.BGPVPN: attrs}) + self.assertIsNone(result) + + def test_set_bgpvpn_with_purge_list(self): + fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() + self.neutronclient.show_bgpvpn = mock.Mock( + return_value={constants.BGPVPN: fake_bgpvpn}) + self.neutronclient.update_bgpvpn = mock.Mock() + arglist = [ + fake_bgpvpn['id'], + '--route-target', 'set_rt1', + '--no-route-target', + '--import-target', 'set_irt1', + '--no-import-target', + '--export-target', 'set_ert1', + '--no-export-target', + '--route-distinguisher', 'set_rd1', + '--no-route-distinguisher', + ] + verifylist = [ + ('bgpvpn', fake_bgpvpn['id']), + ('route_targets', ['set_rt1']), + ('purge_route_target', True), + ('import_targets', ['set_irt1']), + ('purge_import_target', True), + ('export_targets', ['set_ert1']), + ('purge_export_target', True), + ('route_distinguishers', ['set_rd1']), + ('purge_route_distinguisher', True), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + attrs = { + 'route_targets': [], + 'import_targets': [], + 'export_targets': [], + 'route_distinguishers': [], + } + self.neutronclient.update_bgpvpn.assert_called_once_with( + fake_bgpvpn['id'], {constants.BGPVPN: attrs}) + self.assertIsNone(result) + + +class TestUnsetBgpvpn(fakes.TestNeutronClientBgpvpn): + def setUp(self): + super(TestUnsetBgpvpn, self).setUp() + self.cmd = bgpvpn.UnsetBgpvpn(self.app, self.namespace) + + def test_unset_bgpvpn(self): + attrs = { + 'route_targets': ['unset_rt1', 'unset_rt2', 'unset_rt3'], + 'import_targets': ['unset_irt1', 'unset_irt2', 'unset_irt3'], + 'export_targets': ['unset_ert1', 'unset_ert2', 'unset_ert3'], + 'route_distinguishers': ['unset_rd1', 'unset_rd2', 'unset_rd3'], + } + fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn(attrs) + self.neutronclient.show_bgpvpn = mock.Mock( + return_value={constants.BGPVPN: fake_bgpvpn}) + self.neutronclient.update_bgpvpn = mock.Mock() + arglist = [ + fake_bgpvpn['id'], + '--route-target', 'unset_rt1', + '--import-target', 'unset_irt1', + '--export-target', 'unset_ert1', + '--route-distinguisher', 'unset_rd1', + ] + verifylist = [ + ('bgpvpn', fake_bgpvpn['id']), + ('route_targets', ['unset_rt1']), + ('purge_route_target', False), + ('import_targets', ['unset_irt1']), + ('purge_import_target', False), + ('export_targets', ['unset_ert1']), + ('purge_export_target', False), + ('route_distinguishers', ['unset_rd1']), + ('purge_route_distinguisher', False), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + attrs = { + 'route_targets': list(set(fake_bgpvpn['route_targets']) - + set(['unset_rt1'])), + 'import_targets': list(set(fake_bgpvpn['import_targets']) - + set(['unset_irt1'])), + 'export_targets': list(set(fake_bgpvpn['export_targets']) - + set(['unset_ert1'])), + 'route_distinguishers': list( + set(fake_bgpvpn['route_distinguishers']) - set(['unset_rd1'])), + } + self.neutronclient.update_bgpvpn.assert_called_once_with( + fake_bgpvpn['id'], {constants.BGPVPN: attrs}) + self.assertIsNone(result) + + def test_unset_bgpvpn_with_purge_list(self): + fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() + self.neutronclient.show_bgpvpn = mock.Mock( + return_value={constants.BGPVPN: fake_bgpvpn}) + self.neutronclient.update_bgpvpn = mock.Mock() + arglist = [ + fake_bgpvpn['id'], + '--route-target', 'unset_rt1', + '--all-route-target', + '--import-target', 'unset_irt1', + '--all-import-target', + '--export-target', 'unset_ert1', + '--all-export-target', + '--route-distinguisher', 'unset_rd1', + '--all-route-distinguisher', + ] + verifylist = [ + ('bgpvpn', fake_bgpvpn['id']), + ('route_targets', ['unset_rt1']), + ('purge_route_target', True), + ('import_targets', ['unset_irt1']), + ('purge_import_target', True), + ('export_targets', ['unset_ert1']), + ('purge_export_target', True), + ('route_distinguishers', ['unset_rd1']), + ('purge_route_distinguisher', True), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + attrs = { + 'route_targets': [], + 'import_targets': [], + 'export_targets': [], + 'route_distinguishers': [], + } + self.neutronclient.update_bgpvpn.assert_called_once_with( + fake_bgpvpn['id'], {constants.BGPVPN: attrs}) + self.assertIsNone(result) + + +class TestDeleteBgpvpn(fakes.TestNeutronClientBgpvpn): + def setUp(self): + super(TestDeleteBgpvpn, self).setUp() + self.neutronclient.find_resource = mock.Mock( + side_effect=lambda _, name_or_id: {'id': name_or_id}) + self.cmd = bgpvpn.DeleteBgpvpn(self.app, self.namespace) + + def test_delete_one_bgpvpn(self): + fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() + self.neutronclient.delete_bgpvpn = mock.Mock() + arglist = [ + fake_bgpvpn['id'], + ] + verifylist = [ + ('bgpvpns', [fake_bgpvpn['id']]), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.neutronclient.delete_bgpvpn.assert_called_once_with( + fake_bgpvpn['id']) + self.assertIsNone(result) + + def test_delete_multi_bpgvpn(self): + fake_bgpvpns = fakes.FakeBgpvpn.create_bgpvpns(count=3) + fake_bgpvpn_ids = [fake_bgpvpn['id'] for fake_bgpvpn in + fake_bgpvpns[constants.BGPVPNS]] + self.neutronclient.delete_bgpvpn = mock.Mock() + arglist = fake_bgpvpn_ids + verifylist = [ + ('bgpvpns', fake_bgpvpn_ids), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.neutronclient.delete_bgpvpn.assert_has_calls( + [mock.call(id) for id in fake_bgpvpn_ids]) + self.assertIsNone(result) + + def test_delete_multi_bpgvpn_with_unknown(self): + count = 3 + fake_bgpvpns = fakes.FakeBgpvpn.create_bgpvpns(count=count) + fake_bgpvpn_ids = [fake_bgpvpn['id'] for fake_bgpvpn in + fake_bgpvpns[constants.BGPVPNS]] + + def raise_unknonw_resource(resource_path, name_or_id): + if str(count - 2) in name_or_id: + raise Exception() + self.neutronclient.delete_bgpvpn = mock.Mock( + side_effect=raise_unknonw_resource) + arglist = fake_bgpvpn_ids + verifylist = [ + ('bgpvpns', fake_bgpvpn_ids), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises(exceptions.CommandError, self.cmd.take_action, + parsed_args) + + self.neutronclient.delete_bgpvpn.assert_has_calls( + [mock.call(id) for id in fake_bgpvpn_ids]) + + +class TestListBgpvpn(fakes.TestNeutronClientBgpvpn): + def setUp(self): + super(TestListBgpvpn, self).setUp() + self.cmd = bgpvpn.ListBgpvpn(self.app, self.namespace) + + def test_list_all_bgpvpn(self): + count = 3 + fake_bgpvpns = fakes.FakeBgpvpn.create_bgpvpns(count=count) + self.neutronclient.list_bgpvpns = mock.Mock(return_value=fake_bgpvpns) + arglist = [] + verifylist = [] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + headers, data = self.cmd.take_action(parsed_args) + + self.neutronclient.list_bgpvpns.assert_called_once() + self.assertEqual(headers, list(headers_short)) + self.assertEqual(list(data), + [_get_data(fake_bgpvpn, columns_short) for fake_bgpvpn + in fake_bgpvpns[constants.BGPVPNS]]) + + def test_list_all_bgpvpn_long_mode(self): + count = 3 + fake_bgpvpns = fakes.FakeBgpvpn.create_bgpvpns(count=count) + self.neutronclient.list_bgpvpns = mock.Mock(return_value=fake_bgpvpns) + arglist = [ + '--long', + ] + verifylist = [ + ('long', True), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + headers, data = self.cmd.take_action(parsed_args) + + self.neutronclient.list_bgpvpns.assert_called_once() + self.assertEqual(headers, list(headers_long)) + self.assertEqual(list(data), + [_get_data(fake_bgpvpn, columns_long) for fake_bgpvpn + in fake_bgpvpns[constants.BGPVPNS]]) + + def test_list_project_bgpvpn(self): + count = 3 + project_id = 'list_fake_project_id' + attrs = {'tenant_id': project_id} + fake_bgpvpns = fakes.FakeBgpvpn.create_bgpvpns(count=count, + attrs=attrs) + self.neutronclient.list_bgpvpns = mock.Mock(return_value=fake_bgpvpns) + arglist = [ + '--project', project_id, + ] + verifylist = [ + ('project', project_id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + headers, data = self.cmd.take_action(parsed_args) + + self.neutronclient.list_bgpvpns.assert_called_once_with( + tenant_id=project_id) + self.assertEqual(headers, list(headers_short)) + self.assertEqual(list(data), + [_get_data(fake_bgpvpn, columns_short) for fake_bgpvpn + in fake_bgpvpns[constants.BGPVPNS]]) + + def test_list_bgpvpn_with_filters(self): + count = 3 + name = 'fake_id0' + layer_type = 'l2' + attrs = {'type': layer_type} + fake_bgpvpns = fakes.FakeBgpvpn.create_bgpvpns(count=count, + attrs=attrs) + returned_bgpvpn = fake_bgpvpns[constants.BGPVPNS][0] + self.neutronclient.list_bgpvpns = mock.Mock( + return_value={constants.BGPVPNS: [returned_bgpvpn]}) + arglist = [ + '--property', 'name=%s' % name, + '--property', 'type=%s' % layer_type, + ] + verifylist = [ + ('property', {'name': name, 'type': layer_type}), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + headers, data = self.cmd.take_action(parsed_args) + + self.neutronclient.list_bgpvpns.assert_called_once_with( + name=name, + type=layer_type) + self.assertEqual(headers, list(headers_short)) + self.assertEqual(list(data), + [_get_data(returned_bgpvpn, columns_short)]) + + +class TestShowBgpvpn(fakes.TestNeutronClientBgpvpn): + def setUp(self): + super(TestShowBgpvpn, self).setUp() + self.cmd = bgpvpn.ShowBgpvpn(self.app, self.namespace) + + def test_show_bgpvpn(self): + fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() + self.neutronclient.show_bgpvpn = mock.Mock( + return_value={constants.BGPVPN: fake_bgpvpn}) + arglist = [ + fake_bgpvpn['id'], + ] + verifylist = [ + ('bgpvpn', fake_bgpvpn['id']), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + headers, data = self.cmd.take_action(parsed_args) + + self.neutronclient.show_bgpvpn.assert_called_once_with( + fake_bgpvpn['id']) + self.assertEqual(sorted_headers, headers) + self.assertEqual(_get_data(fake_bgpvpn), data) diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py new file mode 100644 index 000000000..e7bc8ce9c --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py @@ -0,0 +1,272 @@ +# Copyright (c) 2016 Juniper Networks 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 copy +import mock +import operator + +from osc_lib import exceptions +from osc_lib import utils as osc_utils + +from neutronclient.osc import utils as nc_osc_utils +from neutronclient.tests.unit.osc.v2.networking_bgpvpn import fakes + + +columns_short = tuple(col for col, _, listing_mode + in fakes.BgpvpnFakeAssoc._attr_map + if listing_mode in (nc_osc_utils.LIST_BOTH, + nc_osc_utils.LIST_SHORT_ONLY)) +columns_long = tuple(col for col, _, listing_mode + in fakes.BgpvpnFakeAssoc._attr_map + if listing_mode in (nc_osc_utils.LIST_BOTH, + nc_osc_utils.LIST_LONG_ONLY)) +headers_short = tuple(head for _, head, listing_mode + in fakes.BgpvpnFakeAssoc._attr_map + if listing_mode in (nc_osc_utils.LIST_BOTH, + nc_osc_utils.LIST_SHORT_ONLY)) +headers_long = tuple(head for _, head, listing_mode + in fakes.BgpvpnFakeAssoc._attr_map + if listing_mode in (nc_osc_utils.LIST_BOTH, + nc_osc_utils.LIST_LONG_ONLY)) +sorted_attr_map = sorted(fakes.BgpvpnFakeAssoc._attr_map, + key=operator.itemgetter(1)) +sorted_columns = tuple(col for col, _, _ in sorted_attr_map) +sorted_headers = tuple(head for _, head, _ in sorted_attr_map) + + +def _get_data(attrs, columns=sorted_columns): + return osc_utils.get_dict_properties( + attrs, columns, formatters=fakes.BgpvpnFakeAssoc._formatters) + + +class TestCreateResAssoc(fakes.TestNeutronClientBgpvpn): + def setUp(self): + super(TestCreateResAssoc, self).setUp() + self.cmd = fakes.CreateBgpvpnFakeResAssoc(self.app, self.namespace) + + def test_create_resource_association(self): + fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() + fake_res = fakes.FakeResource.create_one_resource() + fake_res_assoc = fakes.FakeResAssoc.create_one_resource_association( + fake_res) + self.neutronclient.create_bgpvpn_fake_resource_assoc = mock.Mock( + return_value={fakes.BgpvpnFakeAssoc._resource: fake_res_assoc}) + arglist = [ + fake_bgpvpn['id'], + fake_res['id'], + '--project', fake_bgpvpn['tenant_id'], + ] + verifylist = [ + ('bgpvpn', fake_bgpvpn['id']), + ('resource', fake_res['id']), + ('project', fake_bgpvpn['tenant_id']) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + cols, data = self.cmd.take_action(parsed_args) + + fake_res_assoc_call = copy.deepcopy(fake_res_assoc) + fake_res_assoc_call.pop('id') + self.neutronclient.create_bgpvpn_fake_resource_assoc.\ + assert_called_once_with( + fake_bgpvpn['id'], + {fakes.BgpvpnFakeAssoc._resource: fake_res_assoc_call}) + self.assertEqual(sorted_headers, cols) + self.assertEqual(_get_data(fake_res_assoc), data) + + +class TestDeleteResAssoc(fakes.TestNeutronClientBgpvpn): + def setUp(self): + super(TestDeleteResAssoc, self).setUp() + self.cmd = fakes.DeleteBgpvpnFakeResAssoc(self.app, self.namespace) + + def test_delete_one_association(self): + fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() + fake_res = fakes.FakeResource.create_one_resource() + fake_res_assoc = fakes.FakeResAssoc.create_one_resource_association( + fake_res) + self.neutronclient.delete_bgpvpn_fake_resource_assoc = mock.Mock() + arglist = [ + fake_res_assoc['id'], + fake_bgpvpn['id'], + ] + verifylist = [ + ('resource_association_ids', [fake_res_assoc['id']]), + ('bgpvpn', fake_bgpvpn['id']), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.neutronclient.delete_bgpvpn_fake_resource_assoc.\ + assert_called_once_with(fake_bgpvpn['id'], fake_res_assoc['id']) + self.assertIsNone(result) + + def test_delete_multi_bpgvpn(self): + count = 3 + fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() + fake_res = fakes.FakeResource.create_resources(count=count) + fake_res_assocs = fakes.FakeResAssoc.create_resource_associations( + fake_res) + fake_res_assoc_ids = [ + fake_res_assoc['id'] for fake_res_assoc in + fake_res_assocs[fakes.BgpvpnFakeAssoc._resource_plural] + ] + self.neutronclient.delete_bgpvpn_fake_resource_assoc = mock.Mock() + arglist = \ + fake_res_assoc_ids + [ + fake_bgpvpn['id'] + ] + verifylist = [ + ('resource_association_ids', fake_res_assoc_ids), + ('bgpvpn', fake_bgpvpn['id']), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.neutronclient.delete_bgpvpn_fake_resource_assoc.assert_has_calls( + [mock.call(fake_bgpvpn['id'], id) for id in fake_res_assoc_ids]) + self.assertIsNone(result) + + def test_delete_multi_bpgvpn_with_unknown(self): + count = 3 + fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() + fake_res = fakes.FakeResource.create_resources(count=count) + fake_res_assocs = fakes.FakeResAssoc.create_resource_associations( + fake_res) + fake_res_assoc_ids = [ + fake_res_assoc['id'] for fake_res_assoc in + fake_res_assocs[fakes.BgpvpnFakeAssoc._resource_plural] + ] + + def raise_unknonw_resource(resource_path, name_or_id): + if str(count - 2) in name_or_id: + raise Exception() + self.neutronclient.delete_bgpvpn_fake_resource_assoc = mock.Mock( + side_effect=raise_unknonw_resource) + arglist = \ + fake_res_assoc_ids + [ + fake_bgpvpn['id'] + ] + verifylist = [ + ('resource_association_ids', fake_res_assoc_ids), + ('bgpvpn', fake_bgpvpn['id']), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises(exceptions.CommandError, self.cmd.take_action, + parsed_args) + + self.neutronclient.delete_bgpvpn_fake_resource_assoc.assert_has_calls( + [mock.call(fake_bgpvpn['id'], id) for id in fake_res_assoc_ids]) + + +class TestListResAssoc(fakes.TestNeutronClientBgpvpn): + def setUp(self): + super(TestListResAssoc, self).setUp() + self.cmd = fakes.ListBgpvpnFakeResAssoc(self.app, self.namespace) + + def test_list_bgpvpn_associations(self): + count = 3 + fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() + fake_res = fakes.FakeResource.create_resources(count=count) + fake_res_assocs = fakes.FakeResAssoc.create_resource_associations( + fake_res) + self.neutronclient.list_bgpvpn_fake_resource_assocs = mock.Mock( + return_value=fake_res_assocs) + arglist = [ + fake_bgpvpn['id'], + ] + verifylist = [ + ('bgpvpn', fake_bgpvpn['id']), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + headers, data = self.cmd.take_action(parsed_args) + + self.neutronclient.list_bgpvpn_fake_resource_assocs.\ + assert_called_once_with(fake_bgpvpn['id'], retrieve_all=True) + self.assertEqual(headers, list(headers_short)) + self.assertEqual( + list(data), + [_get_data(fake_res_assoc, columns_short) for fake_res_assoc + in fake_res_assocs[fakes.BgpvpnFakeAssoc._resource_plural]]) + + def test_list_bgpvpn_associations_long_mode(self): + count = 3 + fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() + fake_res = fakes.FakeResource.create_resources(count=count) + fake_res_assocs = fakes.FakeResAssoc.create_resource_associations( + fake_res) + self.neutronclient.list_bgpvpn_fake_resource_assocs = mock.Mock( + return_value=fake_res_assocs) + arglist = [ + '--long', + fake_bgpvpn['id'], + ] + verifylist = [ + ('long', True), + ('bgpvpn', fake_bgpvpn['id']), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + headers, data = self.cmd.take_action(parsed_args) + + self.neutronclient.list_bgpvpn_fake_resource_assocs.\ + assert_called_once_with(fake_bgpvpn['id'], retrieve_all=True) + self.assertEqual(headers, list(headers_long)) + self.assertEqual( + list(data), + [_get_data(fake_res_assoc, columns_long) for fake_res_assoc + in fake_res_assocs[fakes.BgpvpnFakeAssoc._resource_plural]]) + + +class TestShowResAssoc(fakes.TestNeutronClientBgpvpn): + def setUp(self): + super(TestShowResAssoc, self).setUp() + self.cmd = fakes.ShowBgpvpnFakeResAssoc(self.app, self.namespace) + + def test_show_resource_association(self): + fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() + fake_res = fakes.FakeResource.create_one_resource() + fake_res_assoc = fakes.FakeResAssoc.create_one_resource_association( + fake_res) + self.neutronclient.show_bgpvpn_fake_resource_assoc = mock.Mock( + return_value={fakes.BgpvpnFakeAssoc._resource: fake_res_assoc}) + arglist = [ + fake_res_assoc['id'], + fake_bgpvpn['id'], + ] + verifylist = [ + ('resource_association_id', fake_res_assoc['id']), + ('bgpvpn', fake_bgpvpn['id']), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + headers, data = self.cmd.take_action(parsed_args) + + self.neutronclient.show_bgpvpn_fake_resource_assoc.\ + assert_called_once_with(fake_bgpvpn['id'], fake_res_assoc['id']) + self.assertEqual(sorted_headers, headers) + self.assertEqual(data, _get_data(fake_res_assoc)) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 728339e70..759f7765e 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -630,6 +630,15 @@ class Client(ClientBase): subports_path = "/trunks/%s/get_subports" subports_add_path = "/trunks/%s/add_subports" subports_remove_path = "/trunks/%s/remove_subports" + bgpvpns_path = "/bgpvpn/bgpvpns" + bgpvpn_path = "/bgpvpn/bgpvpns/%s" + bgpvpn_network_associations_path =\ + "/bgpvpn/bgpvpns/%s/network_associations" + bgpvpn_network_association_path =\ + "/bgpvpn/bgpvpns/%s/network_associations/%s" + bgpvpn_router_associations_path = "/bgpvpn/bgpvpns/%s/router_associations" + bgpvpn_router_association_path =\ + "/bgpvpn/bgpvpns/%s/router_associations/%s" # API has no way to report plurals, so we have to hard code them EXTED_PLURALS = {'routers': 'router', @@ -680,6 +689,9 @@ class Client(ClientBase): 'bgp_peers': 'bgp_peer', 'network_ip_availabilities': 'network_ip_availability', 'trunks': 'trunk', + 'bgpvpns': 'bgpvpn', + 'network_associations': 'network_association', + 'router_associations': 'router_association', } def list_ext(self, collection, path, retrieve_all, **_params): @@ -2071,6 +2083,82 @@ def trunk_get_subports(self, trunk, **_params): """Fetch a list of all subports attached to given trunk.""" return self.get(self.subports_path % (trunk), params=_params) + def list_bgpvpns(self, retrieve_all=True, **_params): + """Fetches a list of all BGP VPNs for a project""" + return self.list('bgpvpns', self.bgpvpns_path, retrieve_all, **_params) + + def show_bgpvpn(self, bgpvpn, **_params): + """Fetches information of a certain BGP VPN""" + return self.get(self.bgpvpn_path % bgpvpn, params=_params) + + def create_bgpvpn(self, body=None): + """Creates a new BGP VPN""" + return self.post(self.bgpvpns_path, body=body) + + def update_bgpvpn(self, bgpvpn, body=None): + """Updates a BGP VPN""" + return self.put(self.bgpvpn_path % bgpvpn, body=body) + + def delete_bgpvpn(self, bgpvpn): + """Deletes the specified BGP VPN""" + return self.delete(self.bgpvpn_path % bgpvpn) + + def list_bgpvpn_network_assocs(self, bgpvpn, retrieve_all=True, **_params): + """Fetches a list of network associations for a given BGP VPN.""" + return self.list('network_associations', + self.bgpvpn_network_associations_path % bgpvpn, + retrieve_all, **_params) + + def show_bgpvpn_network_assoc(self, bgpvpn, net_assoc, **_params): + """Fetches information of a certain BGP VPN's network association""" + return self.get( + self.bgpvpn_network_association_path % (bgpvpn, net_assoc), + params=_params) + + def create_bgpvpn_network_assoc(self, bgpvpn, body=None): + """Creates a new BGP VPN network association""" + return self.post(self.bgpvpn_network_associations_path % bgpvpn, + body=body) + + def update_bgpvpn_network_assoc(self, bgpvpn, net_assoc, body=None): + """Updates a BGP VPN network association""" + return self.put( + self.bgpvpn_network_association_path % (bgpvpn, net_assoc), + body=body) + + def delete_bgpvpn_network_assoc(self, bgpvpn, net_assoc): + """Deletes the specified BGP VPN network association""" + return self.delete( + self.bgpvpn_network_association_path % (bgpvpn, net_assoc)) + + def list_bgpvpn_router_assocs(self, bgpvpn, retrieve_all=True, **_params): + """Fetches a list of router associations for a given BGP VPN.""" + return self.list('router_associations', + self.bgpvpn_router_associations_path % bgpvpn, + retrieve_all, **_params) + + def show_bgpvpn_router_assoc(self, bgpvpn, router_assoc, **_params): + """Fetches information of a certain BGP VPN's router association""" + return self.get( + self.bgpvpn_router_association_path % (bgpvpn, router_assoc), + params=_params) + + def create_bgpvpn_router_assoc(self, bgpvpn, body=None): + """Creates a new BGP VPN router association""" + return self.post(self.bgpvpn_router_associations_path % bgpvpn, + body=body) + + def update_bgpvpn_router_assoc(self, bgpvpn, router_assoc, body=None): + """Updates a BGP VPN router association""" + return self.put( + self.bgpvpn_router_association_path % (bgpvpn, router_assoc), + body=body) + + def delete_bgpvpn_router_assoc(self, bgpvpn, router_assoc): + """Deletes the specified BGP VPN router association""" + return self.delete( + self.bgpvpn_router_association_path % (bgpvpn, router_assoc)) + def __init__(self, **kwargs): """Initialize a new client for the Neutron v2.0 API.""" super(Client, self).__init__(**kwargs) diff --git a/releasenotes/notes/support-networking-bgpvpn-cli-fdd0cc3a5b14983d.yaml b/releasenotes/notes/support-networking-bgpvpn-cli-fdd0cc3a5b14983d.yaml new file mode 100644 index 000000000..287e0427c --- /dev/null +++ b/releasenotes/notes/support-networking-bgpvpn-cli-fdd0cc3a5b14983d.yaml @@ -0,0 +1,5 @@ +--- +features: + - CLI support for the "Neutron BGP VPN Interconnection" feature, + which is an API extension to support inter-connection between + L3VPNs/E-VPNs and Neutron resources, as OSC plugin commands. diff --git a/setup.cfg b/setup.cfg index 041ff1ac0..ac7da5cae 100644 --- a/setup.cfg +++ b/setup.cfg @@ -65,6 +65,21 @@ openstack.neutronclient.v2 = firewall_group_rule_show = neutronclient.osc.v2.fwaas.firewallrule:ShowFirewallRule firewall_group_rule_unset = neutronclient.osc.v2.fwaas.firewallrule:UnsetFirewallRule + bgpvpn_create = neutronclient.osc.v2.networking_bgpvpn.bgpvpn:CreateBgpvpn + bgpvpn_delete = neutronclient.osc.v2.networking_bgpvpn.bgpvpn:DeleteBgpvpn + bgpvpn_list = neutronclient.osc.v2.networking_bgpvpn.bgpvpn:ListBgpvpn + bgpvpn_set = neutronclient.osc.v2.networking_bgpvpn.bgpvpn:SetBgpvpn + bgpvpn_show = neutronclient.osc.v2.networking_bgpvpn.bgpvpn:ShowBgpvpn + bgpvpn_unset = neutronclient.osc.v2.networking_bgpvpn.bgpvpn:UnsetBgpvpn + bgpvpn_network_association_create = neutronclient.osc.v2.networking_bgpvpn.network_association:CreateBgpvpnNetAssoc + bgpvpn_network_association_delete = neutronclient.osc.v2.networking_bgpvpn.network_association:DeleteBgpvpnNetAssoc + bgpvpn_network_association_list = neutronclient.osc.v2.networking_bgpvpn.network_association:ListBgpvpnNetAssoc + bgpvpn_network_association_show = neutronclient.osc.v2.networking_bgpvpn.network_association:ShowBgpvpnNetAssoc + bgpvpn_router_association_create = neutronclient.osc.v2.networking_bgpvpn.router_association:CreateBgpvpnRouterAssoc + bgpvpn_router_association_delete = neutronclient.osc.v2.networking_bgpvpn.router_association:DeleteBgpvpnRouterAssoc + bgpvpn_router_association_list = neutronclient.osc.v2.networking_bgpvpn.router_association:ListBgpvpnRouterAssoc + bgpvpn_router_association_show = neutronclient.osc.v2.networking_bgpvpn.router_association:ShowBgpvpnRouterAssoc + [build_sphinx] all_files = 1 build-dir = doc/build From 1ede4306cfb800a493b940e0acd1b3f903d419cf Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Fri, 10 Feb 2017 05:59:12 +0000 Subject: [PATCH 550/845] Updated from global requirements Change-Id: I17e20f6dc80036a9a00ff0d9111abffa7a20bc8f --- test-requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test-requirements.txt b/test-requirements.txt index ed47631bd..cacc59731 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -13,8 +13,8 @@ python-openstackclient>=3.3.0 # Apache-2.0 python-subunit>=0.0.18 # Apache-2.0/BSD reno>=1.8.0 # Apache-2.0 requests-mock>=1.1 # Apache-2.0 -sphinx!=1.3b1,<1.4,>=1.2.1 # BSD +sphinx>=1.5.1 # BSD testrepository>=0.0.18 # Apache-2.0/BSD testtools>=1.4.0 # MIT testscenarios>=0.4 # Apache-2.0/BSD -tempest>=12.1.0 # Apache-2.0 +tempest>=14.0.0 # Apache-2.0 From ff6a685dbf33df31b55f0d4402cba30007f7bc7e Mon Sep 17 00:00:00 2001 From: da52700 Date: Thu, 9 Feb 2017 18:40:36 +0800 Subject: [PATCH 551/845] Trivial Fix:fix typo in .pylintrc file Change-Id: I7a9aaa6891d114cabee7c84230d36fe65e8db0a7 --- .pylintrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pylintrc b/.pylintrc index bd677fd6a..7e4a0716c 100644 --- a/.pylintrc +++ b/.pylintrc @@ -20,7 +20,7 @@ variable-rgx=[a-z_][a-z0-9_]{0,30}$ argument-rgx=[a-z_][a-z0-9_]{1,30}$ # Method names should be at least 3 characters long -# and be lowecased with underscores +# and be lowercased with underscores method-rgx=([a-z_][a-z0-9_]{2,50}|setUp|tearDown)$ # Module names matching quantum-* are ok (files in bin/) From 0ee41095f56971bf8577afc803cdcb9240caea94 Mon Sep 17 00:00:00 2001 From: Jeremy Liu Date: Thu, 19 Jan 2017 16:55:59 +0800 Subject: [PATCH 552/845] Drop MANIFEST.in - it's not needed by pbr This patch removes `MANIFEST.in` file as pbr generates a sensible manifest from git files and some standard files and it removes the need for an explicit `MANIFEST.in` file. Change-Id: I0914c0553ae3a28e9d2a883e91e926733d8e91df --- MANIFEST.in | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 MANIFEST.in diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index c0f014e74..000000000 --- a/MANIFEST.in +++ /dev/null @@ -1,6 +0,0 @@ -include tox.ini -include LICENSE README.rst HACKING.rst -include AUTHORS -include ChangeLog -include tools/* -recursive-include tests * From badfc96046f8863ddaaa1d911a6448249785db29 Mon Sep 17 00:00:00 2001 From: Dina Belova Date: Wed, 17 Feb 2016 12:15:21 -0800 Subject: [PATCH 553/845] Add profiling support to neutronclient To be able to create profiling traces for Neutron, client should be able to send special HTTP header that contains trace info. This patch is also important to be able to make cross project traces. (Typical case nova calls neutron via python client, if profiler is initialized in nova, neutron client will add extra header, that will be parsed by special osprofiler middleware in neutron api.) Closes-Bug: #1335640 Co-Authored-By: Roman Podoliaka Change-Id: Ic11796889075b2a0e589b70398fc4d4ed6f3ef7c --- neutronclient/client.py | 11 +++++++++++ neutronclient/tests/unit/test_http.py | 10 ++++++++++ .../notes/osprofiler-support-9ba539761ae437e9.yaml | 6 ++++++ test-requirements.txt | 1 + 4 files changed, 28 insertions(+) create mode 100644 releasenotes/notes/osprofiler-support-9ba539761ae437e9.yaml diff --git a/neutronclient/client.py b/neutronclient/client.py index dd37285f4..2995bcea5 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -24,12 +24,15 @@ import debtcollector.renames from keystoneauth1 import access from keystoneauth1 import adapter +from oslo_utils import importutils import requests from neutronclient._i18n import _ from neutronclient.common import exceptions from neutronclient.common import utils +osprofiler_web = importutils.try_import("osprofiler.web") + _logger = logging.getLogger(__name__) if os.environ.get('NEUTRONCLIENT_DEBUG'): @@ -151,6 +154,10 @@ def request(self, url, method, body=None, headers=None, **kwargs): headers.setdefault('Content-Type', content_type) headers['User-Agent'] = USER_AGENT + # NOTE(dbelova): osprofiler_web.get_trace_id_headers does not add any + # headers in case if osprofiler is not initialized. + if osprofiler_web: + headers.update(osprofiler_web.get_trace_id_headers()) resp = requests.request( method, @@ -301,6 +308,10 @@ def request(self, *args, **kwargs): headers = kwargs.setdefault('headers', {}) headers.setdefault('Accept', content_type) + # NOTE(dbelova): osprofiler_web.get_trace_id_headers does not add any + # headers in case if osprofiler is not initialized. + if osprofiler_web: + headers.update(osprofiler_web.get_trace_id_headers()) try: kwargs.setdefault('data', kwargs.pop('body')) diff --git a/neutronclient/tests/unit/test_http.py b/neutronclient/tests/unit/test_http.py index 2f65b8ed0..2f122fac8 100644 --- a/neutronclient/tests/unit/test_http.py +++ b/neutronclient/tests/unit/test_http.py @@ -15,6 +15,8 @@ import abc +import osprofiler.profiler +import osprofiler.web from requests_mock.contrib import fixture as mock_fixture import six import testtools @@ -72,6 +74,14 @@ def test_headers_defined_in_headers(self): 'Content-Type': 'application/json'} self._test_headers(headers, body=BODY, headers=headers) + def test_osprofiler_headers_are_injected(self): + osprofiler.profiler.init('SWORDFISH') + self.addCleanup(osprofiler.profiler._clean) + + headers = {'Accept': 'application/json'} + headers.update(osprofiler.web.get_trace_id_headers()) + self._test_headers(headers) + class TestHTTPClient(TestHTTPClientMixin, testtools.TestCase): diff --git a/releasenotes/notes/osprofiler-support-9ba539761ae437e9.yaml b/releasenotes/notes/osprofiler-support-9ba539761ae437e9.yaml new file mode 100644 index 000000000..98e2bb168 --- /dev/null +++ b/releasenotes/notes/osprofiler-support-9ba539761ae437e9.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Add osprofiler support to the neutronclient python binding. + If osprofiler is initiated, neutronclient sends a special HTTP + header that contains trace info. diff --git a/test-requirements.txt b/test-requirements.txt index ed47631bd..044b1b64a 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -9,6 +9,7 @@ mox3!=0.19.0,>=0.7.0 # Apache-2.0 mock>=2.0 # BSD oslosphinx>=4.7.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 +osprofiler>=1.4.0 # Apache-2.0 python-openstackclient>=3.3.0 # Apache-2.0 python-subunit>=0.0.18 # Apache-2.0/BSD reno>=1.8.0 # Apache-2.0 From 6af9f8978539befe9ac2a43a7e55644cfa102f06 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Fri, 24 Feb 2017 11:15:05 -0500 Subject: [PATCH 554/845] doc: Fix warning tox -e docs fails with sphinx warning. This fixes a warning "WARNING: Title underline too short" Change-Id: I7259e5f0df42aa8434443cf742932b0843f019e2 --- doc/source/usage/osc/v2/networking-bgpvpn.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/usage/osc/v2/networking-bgpvpn.rst b/doc/source/usage/osc/v2/networking-bgpvpn.rst index e21563e24..69b3150db 100644 --- a/doc/source/usage/osc/v2/networking-bgpvpn.rst +++ b/doc/source/usage/osc/v2/networking-bgpvpn.rst @@ -110,7 +110,7 @@ Set BGP VPN properties Empty route distinguisher list bgpvpn unset ----------- +------------ Unset BGP VPN properties @@ -380,4 +380,4 @@ Show information of a given BGP VPN router association ID of the router association to look up .. describe:: - BGP VPN the association belongs to (name or ID) \ No newline at end of file + BGP VPN the association belongs to (name or ID) From 25e8ff67124f64518930fa3a3e84a749c45fbfb2 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Fri, 24 Feb 2017 11:02:01 -0500 Subject: [PATCH 555/845] doc: Patch acceptance policy after neutron CLI deprecation Neutron CLI itself is now deprecated. It means a kind of feature freeze and we don't accept any changes on adding/changing/dropping the existing commands. The only exception is bug fixes on the deprecated neutron CLI. It is better to document our policy on neutron CLI chnages. Change-Id: I5f063c7eddbc9a91f587d5d3610092d07150f726 --- doc/source/index.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/source/index.rst b/doc/source/index.rst index be4c77057..8da1b7659 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -44,6 +44,13 @@ In the Developer Guide, you will find information on Neutron’s client lower level programming details or APIs as well as the transition to OpenStack client. +.. note: + + neutron CLI has been deprecated from Ocata release. + We do not add, change and drop any existing commands any more. + We only accept changes on OSC plugin, neutronclient python bindings + and bug fixes on the deprecated CLI (``neutron`` command). + .. toctree:: :maxdepth: 2 From 3bf9d4c6099b8bfe053a843c9081744e07964e34 Mon Sep 17 00:00:00 2001 From: Ankur Gupta Date: Wed, 29 Jun 2016 19:04:51 -0500 Subject: [PATCH 556/845] Change documentation about UpdateQuota class The documentation about UpdateQuota is misleading. This makes it seem like you can only update a quota when it is currently the default quota values. Trivial Fix Change-Id: I5dae7b38d388f4abae5d927998e809839e8ef879 --- neutronclient/neutron/v2_0/quota.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/neutron/v2_0/quota.py b/neutronclient/neutron/v2_0/quota.py index db3f5290a..aaf359102 100644 --- a/neutronclient/neutron/v2_0/quota.py +++ b/neutronclient/neutron/v2_0/quota.py @@ -140,7 +140,7 @@ def retrieve_data(self, tenant_id, neutron_client): class UpdateQuota(neutronV20.NeutronCommand, show.ShowOne): - """Define tenant's quotas not to use defaults.""" + """Update a given tenant's quotas.""" resource = 'quota' From a5fe2b17f4aa4b8a8623ff222c803f2a77ed1ba7 Mon Sep 17 00:00:00 2001 From: ricolin Date: Thu, 2 Mar 2017 17:32:42 +0800 Subject: [PATCH 557/845] [Fix gate]Update test requirement Since pbr already landed and the old version of hacking seems not work very well with pbr>=2, we should update it to match global requirement. Partial-Bug: #1668848 Change-Id: Ifc984fa1c425999fabc3a62b6f6f3f29fa1100af --- test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index 25fea6374..66cd0c18a 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,7 +1,7 @@ # The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. -hacking<0.11,>=0.10.0 +hacking>=0.12.0,!=0.13.0,<0.14 # Apache-2.0 coverage>=4.0 # Apache-2.0 fixtures>=3.0.0 # Apache-2.0/BSD From 6df0b27ee4fd9d6b23c503427c18f20441bd7c73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Antal?= Date: Tue, 7 Feb 2017 14:22:32 +0100 Subject: [PATCH 558/845] Handle log message interpolation by the logger According to OpenStack Guideline[1], logged string message should be interpolated by the logger. [1]: http://docs.openstack.org/developer/oslo.i18n/guidelines.html#adding-variables-to-log-messages Change-Id: I5146c62cfd7e05270c5bf8ebc3ebab3908f1f1ca Closes-Bug: #1661262 Co-Authored-By: Akihiro Motoki --- neutronclient/osc/v2/fwaas/firewallgroup.py | 4 ++-- neutronclient/osc/v2/fwaas/firewallpolicy.py | 4 ++-- neutronclient/osc/v2/fwaas/firewallrule.py | 4 ++-- neutronclient/osc/v2/trunk/network_trunk.py | 4 ++-- tox.ini | 2 ++ 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/neutronclient/osc/v2/fwaas/firewallgroup.py b/neutronclient/osc/v2/fwaas/firewallgroup.py index b5b388879..4eceddf0c 100644 --- a/neutronclient/osc/v2/fwaas/firewallgroup.py +++ b/neutronclient/osc/v2/fwaas/firewallgroup.py @@ -215,8 +215,8 @@ def take_action(self, parsed_args): except Exception as e: result += 1 LOG.error(_("Failed to delete firewall group with " - "name or ID '%(firewall_group)s': %(e)s") % { - const.FWG: fwg, 'e': e}) + "name or ID '%(firewall_group)s': %(e)s"), + {const.FWG: fwg, 'e': e}) if result > 0: total = len(parsed_args.firewall_group) diff --git a/neutronclient/osc/v2/fwaas/firewallpolicy.py b/neutronclient/osc/v2/fwaas/firewallpolicy.py index 5ac097145..6fad694b8 100644 --- a/neutronclient/osc/v2/fwaas/firewallpolicy.py +++ b/neutronclient/osc/v2/fwaas/firewallpolicy.py @@ -169,8 +169,8 @@ def take_action(self, parsed_args): except Exception as e: result += 1 LOG.error(_("Failed to delete Firewall policy with " - "name or ID '%(firewall_policy)s': %(e)s") % { - const.FWP: fwp, 'e': e}) + "name or ID '%(firewall_policy)s': %(e)s"), + {const.FWP: fwp, 'e': e}) if result > 0: total = len(parsed_args.firewall_policy) diff --git a/neutronclient/osc/v2/fwaas/firewallrule.py b/neutronclient/osc/v2/fwaas/firewallrule.py index ec8735165..e91786771 100644 --- a/neutronclient/osc/v2/fwaas/firewallrule.py +++ b/neutronclient/osc/v2/fwaas/firewallrule.py @@ -229,8 +229,8 @@ def take_action(self, parsed_args): except Exception as e: result += 1 LOG.error(_("Failed to delete Firewall rule with " - "name or ID '%(firewall_rule)s': %(e)s") % { - const.FWR: fwr, 'e': e}) + "name or ID '%(firewall_rule)s': %(e)s"), + {const.FWR: fwr, 'e': e}) if result > 0: total = len(parsed_args.firewall_rule) diff --git a/neutronclient/osc/v2/trunk/network_trunk.py b/neutronclient/osc/v2/trunk/network_trunk.py index acd6208db..0cee32e5d 100644 --- a/neutronclient/osc/v2/trunk/network_trunk.py +++ b/neutronclient/osc/v2/trunk/network_trunk.py @@ -112,8 +112,8 @@ def take_action(self, parsed_args): except Exception as e: result += 1 LOG.error(_("Failed to delete trunk with name " - "or ID '%(trunk)s': %(e)s") - % {'trunk': trunk, 'e': e}) + "or ID '%(trunk)s': %(e)s"), + {'trunk': trunk, 'e': e}) if result > 0: total = len(parsed_args.trunk) msg = (_("%(result)s of %(total)s trunks failed " diff --git a/tox.ini b/tox.ini index f9364c38b..aeb09c7a5 100644 --- a/tox.ini +++ b/tox.ini @@ -55,3 +55,5 @@ commands = sphinx-build -a -E -d releasenotes/build/doctrees -b html releasenote [flake8] show-source = true exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,tools +# H904: Delay string interpolations at logging calls +enable-extensions=H904 From 4c9d42f60ffd9965cbdf2a243d062409123367c9 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Mon, 6 Mar 2017 01:18:40 +0000 Subject: [PATCH 559/845] Updated from global requirements Change-Id: If404f874168f825fe70a2d253d26c3400183536b --- requirements.txt | 6 +++--- setup.py | 2 +- test-requirements.txt | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/requirements.txt b/requirements.txt index 8b8b8c1f2..db647de59 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ # The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. -pbr>=1.8 # Apache-2.0 +pbr>=2.0.0 # Apache-2.0 cliff>=2.3.0 # Apache-2.0 debtcollector>=1.2.0 # Apache-2.0 iso8601>=0.1.11 # MIT @@ -9,13 +9,13 @@ netaddr!=0.7.16,>=0.7.13 # BSD osc-lib>=1.2.0 # Apache-2.0 oslo.i18n>=2.1.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 -oslo.utils>=3.18.0 # Apache-2.0 +oslo.utils>=3.20.0 # Apache-2.0 os-client-config>=1.22.0 # Apache-2.0 keystoneauth1>=2.18.0 # Apache-2.0 # keystoneclient is used only by neutronclient.osc.utils # TODO(amotoki): Drop this after osc.utils has no dependency on keystoneclient python-keystoneclient>=3.8.0 # Apache-2.0 -requests!=2.12.2,>=2.10.0 # Apache-2.0 +requests!=2.12.2,!=2.13.0,>=2.10.0 # Apache-2.0 simplejson>=2.2.0 # MIT six>=1.9.0 # MIT Babel>=2.3.4 # BSD diff --git a/setup.py b/setup.py index 782bb21f0..566d84432 100644 --- a/setup.py +++ b/setup.py @@ -25,5 +25,5 @@ pass setuptools.setup( - setup_requires=['pbr>=1.8'], + setup_requires=['pbr>=2.0.0'], pbr=True) diff --git a/test-requirements.txt b/test-requirements.txt index 66cd0c18a..b4efe102b 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,7 +1,7 @@ # The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. -hacking>=0.12.0,!=0.13.0,<0.14 # Apache-2.0 +hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0 coverage>=4.0 # Apache-2.0 fixtures>=3.0.0 # Apache-2.0/BSD From 49710e71f78f9f1163f3ae1b889e4874beb90379 Mon Sep 17 00:00:00 2001 From: kavithahr Date: Wed, 8 Mar 2017 17:23:28 +0530 Subject: [PATCH 560/845] Python 3.4 support is removed In setup.cfg and tox.ini the python 3.4 is removed beacuse python 3.5 is available. Change-Id: I176d7fce4778aa7a1d4d93924d4c8785233fa7ef --- setup.cfg | 1 - tox.ini | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index ac7da5cae..cec20f9d8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -17,7 +17,6 @@ classifier = Programming Language :: Python :: 2 Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 - Programming Language :: Python :: 3.4 Programming Language :: Python :: 3.5 [files] diff --git a/tox.ini b/tox.ini index f9364c38b..d0fc45ec8 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] # py3 first to avoid .testrepository incompatibility -envlist = py35,py34,py27,pypy,pep8 +envlist = py35,py27,pypy,pep8 minversion = 1.6 skipsdist = True From 3bbcdf8ff21724eac6e4668143eb709f05808d85 Mon Sep 17 00:00:00 2001 From: ZhaoBo Date: Wed, 8 Mar 2017 15:14:59 +0800 Subject: [PATCH 561/845] Don't sort the fw_rule order in OSC Currently, the FWaaS accept the "fw_rule_list" argrument in fw_policy update, and it will update all the related fw_rules association. If the order is different, the behavior is not expected, so we should take care of the input value order, and maintain the original one. This patch remove the "sorted" function when construct the firewall policy request body, and follow the input order. Closes-Bug: #1671338 Change-Id: Iaa45446eee199582f5f11a4705e82fb0ab644bcc --- neutronclient/osc/v2/fwaas/firewallpolicy.py | 20 +++++++++---------- .../unit/osc/v2/fwaas/test_firewallpolicy.py | 12 +++++------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/neutronclient/osc/v2/fwaas/firewallpolicy.py b/neutronclient/osc/v2/fwaas/firewallpolicy.py index 5ac097145..288bac098 100644 --- a/neutronclient/osc/v2/fwaas/firewallpolicy.py +++ b/neutronclient/osc/v2/fwaas/firewallpolicy.py @@ -54,20 +54,20 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): ).id if parsed_args.firewall_rule and parsed_args.no_firewall_rule: _firewall_rules = [] - for f in set(parsed_args.firewall_rule): + for f in parsed_args.firewall_rule: _firewall_rules.append(client.find_resource( const.FWR, f, cmd_resource=const.CMD_FWR)['id']) - attrs[const.FWRS] = sorted(_firewall_rules) + attrs[const.FWRS] = _firewall_rules elif parsed_args.firewall_rule: rules = [] - for f in set(parsed_args.firewall_rule): - rules.append(client.find_resource( - const.FWR, f, cmd_resource=const.CMD_FWR)['id']) if not is_create: rules += client.find_resource( const.FWP, parsed_args.firewall_policy, cmd_resource=const.CMD_FWP)[const.FWRS] - attrs[const.FWRS] = sorted(set(rules)) + for f in parsed_args.firewall_rule: + rules.append(client.find_resource( + const.FWR, f, cmd_resource=const.CMD_FWR)['id']) + attrs[const.FWRS] = rules elif parsed_args.no_firewall_rule: attrs[const.FWRS] = [] if parsed_args.audited: @@ -395,14 +395,14 @@ def _get_attrs(self, client_manager, parsed_args): client = client_manager.neutronclient if parsed_args.firewall_rule: - old = client.find_resource( + current = client.find_resource( const.FWP, parsed_args.firewall_policy, cmd_resource=const.CMD_FWP)[const.FWRS] - new = [] + removed = [] for f in set(parsed_args.firewall_rule): - new.append(client.find_resource( + removed.append(client.find_resource( const.FWR, f, cmd_resource=const.CMD_FWR)['id']) - attrs[const.FWRS] = sorted(list(set(old) - set(new))) + attrs[const.FWRS] = [r for r in current if r not in removed] if parsed_args.all_firewall_rule: attrs[const.FWRS] = [] if parsed_args.audited: diff --git a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py index 49e43212b..6d9780768 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py @@ -343,19 +343,19 @@ def _mock_policy(*args, **kwargs): if self.neutronclient.find_resource.call_count == 1: self.neutronclient.find_resource.assert_called_with( self.res, target, cmd_resource=const.CMD_FWP) - # 2. Find specified firewall_rule + # 2. Find specified firewall_policy's 'firewall_rules' attribute if self.neutronclient.find_resource.call_count == 2: self.neutronclient.find_resource.assert_called_with( - 'firewall_rule', args[1], cmd_resource=const.CMD_FWR) + self.res, args[1], cmd_resource=const.CMD_FWP) + return {'firewall_rules': _fwp['firewall_rules']} # 3. Find specified firewall_rule if self.neutronclient.find_resource.call_count == 3: self.neutronclient.find_resource.assert_called_with( 'firewall_rule', args[1], cmd_resource=const.CMD_FWR) - # 4. Find specified firewall_policy's 'firewall_rules' attribute + # 4. Find specified firewall_rule if self.neutronclient.find_resource.call_count == 4: self.neutronclient.find_resource.assert_called_with( - self.res, target, cmd_resource=const.CMD_FWP) - return {'firewall_rules': _fwp['firewall_rules']} + 'firewall_rule', args[1], cmd_resource=const.CMD_FWR) return {'id': args[1]} self.neutronclient.find_resource.side_effect = _mock_policy @@ -372,7 +372,7 @@ def _mock_policy(*args, **kwargs): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - expect = sorted(set(_fwp['firewall_rules'] + [rule1, rule2])) + expect = _fwp['firewall_rules'] + [rule1, rule2] body = {self.res: {'firewall_rules': expect}} self.mocked.assert_called_once_with(target, body) self.assertEqual(4, self.neutronclient.find_resource.call_count) From e516b8230c14d2bf95de86d52b4f2d5b9f83402d Mon Sep 17 00:00:00 2001 From: Armando Migliaccio Date: Tue, 7 Mar 2017 16:26:36 -0800 Subject: [PATCH 562/845] Convert gate_hook to devstack-tools Change-Id: I2387bc9778a77b1673751bc4c1ff50f8d4826a5d Co-Authored-By: Ihar Hrachyshka Co-Authored-By: Sean Dague Original-Commit: Ibd0f67f9131e7f67f3a4a62cb6ad28bf80e11bbf --- .../tests/functional/hooks/gate_hook.sh | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/neutronclient/tests/functional/hooks/gate_hook.sh b/neutronclient/tests/functional/hooks/gate_hook.sh index e34a6c802..36db5690f 100755 --- a/neutronclient/tests/functional/hooks/gate_hook.sh +++ b/neutronclient/tests/functional/hooks/gate_hook.sh @@ -8,20 +8,34 @@ GATE_DEST=$BASE/new NEUTRONCLIENT_PATH=$GATE_DEST/python-neutronclient GATE_HOOKS=$NEUTRONCLIENT_PATH/neutronclient/tests/functional/hooks DEVSTACK_PATH=$GATE_DEST/devstack +LOCAL_CONF=$DEVSTACK_PATH/late-local.conf +DSCONF=/tmp/devstack-tools/bin/dsconf + +# Install devstack-tools used to produce local.conf; we can't rely on +# test-requirements.txt because the gate hook is triggered before neutronclient +# is installed +sudo -H pip install virtualenv +virtualenv /tmp/devstack-tools +/tmp/devstack-tools/bin/pip install -U devstack-tools==0.4.0 # Inject config from hook into localrc function load_rc_hook { local hook="$1" + local tmpfile + local config + tmpfile=$(tempfile) config=$(cat $GATE_HOOKS/$hook) - export DEVSTACK_LOCAL_CONFIG+=" -# generated from hook '$hook' -${config} -" + echo "[[local|localrc]]" > $tmpfile + $DSCONF setlc_raw $tmpfile "$config" + $DSCONF merge_lc $LOCAL_CONF $tmpfile + rm -f $tmpfile } + if [ "$VENV" == "functional-adv-svcs" ] then load_rc_hook fwaas fi +export DEVSTACK_LOCALCONF=$(cat $LOCAL_CONF) $BASE/new/devstack-gate/devstack-vm-gate.sh From 4bf3df40eeb0551bcd80de3af98c3f64398a0c03 Mon Sep 17 00:00:00 2001 From: Duan Jiong Date: Tue, 21 Mar 2017 12:52:23 +0800 Subject: [PATCH 563/845] Remove log translations Log messages are no longer being translated. This removes all use of the _LE, _LI, and _LW translation markers to simplify logging and to avoid confusion with new contributions. See: http://lists.openstack.org/pipermail/openstack-i18n/2016-November/002574.html http://lists.openstack.org/pipermail/openstack-dev/2017-March/113365.html Change-Id: Icbfc8958fe430fdd42a569819a8221a32ab681a5 --- neutronclient/_i18n.py | 12 +----------- neutronclient/i18n.py | 4 ---- neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py | 8 +++----- .../osc/v2/networking_bgpvpn/resource_association.py | 8 +++----- 4 files changed, 7 insertions(+), 25 deletions(-) diff --git a/neutronclient/_i18n.py b/neutronclient/_i18n.py index f5aa76bcf..50ac3c5bf 100644 --- a/neutronclient/_i18n.py +++ b/neutronclient/_i18n.py @@ -17,7 +17,7 @@ _translators = oslo_i18n.TranslatorFactory(domain=DOMAIN) -# The primary translation function using the well-known name "_" +# The translation function using the well-known name "_" _ = _translators.primary # The contextual translation function using the name "_C" @@ -26,16 +26,6 @@ # The plural translation function using the name "_P" _P = _translators.plural_form -# Translators for log levels. -# -# The abbreviated names are meant to reflect the usual use of a short -# name like '_'. The "L" is for "log" and the other letter comes from -# the level. -_LI = _translators.log_info -_LW = _translators.log_warning -_LE = _translators.log_error -_LC = _translators.log_critical - def get_available_languages(): return oslo_i18n.get_available_languages(DOMAIN) diff --git a/neutronclient/i18n.py b/neutronclient/i18n.py index 776628fc7..8e9722c14 100644 --- a/neutronclient/i18n.py +++ b/neutronclient/i18n.py @@ -21,7 +21,3 @@ "http://docs.openstack.org/developer/oslo.i18n/usage.html") _ = moves.moved_function(_i18n._, '_', __name__, message=message) -_LC = moves.moved_function(_i18n._LC, '_LC', __name__, message=message) -_LE = moves.moved_function(_i18n._LE, '_LE', __name__, message=message) -_LW = moves.moved_function(_i18n._LW, '_LW', __name__, message=message) -_LI = moves.moved_function(_i18n._LI, '_LI', __name__, message=message) diff --git a/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py b/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py index 1e5dbb0ea..e935650b9 100644 --- a/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py +++ b/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py @@ -22,8 +22,6 @@ from osc_lib import utils as osc_utils from neutronclient._i18n import _ -from neutronclient._i18n import _LE -from neutronclient._i18n import _LW from neutronclient.osc import utils as nc_osc_utils from neutronclient.osc.v2.networking_bgpvpn import constants @@ -311,11 +309,11 @@ def take_action(self, parsed_args): try: id = client.find_resource(constants.BGPVPN, id_or_name)['id'] client.delete_bgpvpn(id) - LOG.warning(_LW("BGP VPN %(id)s deleted"), {'id': id}) + LOG.warning("BGP VPN %(id)s deleted", {'id': id}) except Exception as e: fails += 1 - LOG.error(_LE("Failed to delete BGP VPN with name or ID " - "'%(id_or_name)s': %(e)s"), + LOG.error("Failed to delete BGP VPN with name or ID " + "'%(id_or_name)s': %(e)s", {'id_or_name': id_or_name, 'e': e}) if fails > 0: msg = (_("Failed to delete %(fails)s of %(total)s BGP VPN.") % diff --git a/neutronclient/osc/v2/networking_bgpvpn/resource_association.py b/neutronclient/osc/v2/networking_bgpvpn/resource_association.py index 98f54cdbe..485803c47 100644 --- a/neutronclient/osc/v2/networking_bgpvpn/resource_association.py +++ b/neutronclient/osc/v2/networking_bgpvpn/resource_association.py @@ -21,8 +21,6 @@ from osc_lib import utils as osc_utils from neutronclient._i18n import _ -from neutronclient._i18n import _LE -from neutronclient._i18n import _LW from neutronclient.osc import utils as nc_osc_utils from neutronclient.osc.v2.networking_bgpvpn import constants @@ -105,13 +103,13 @@ def take_action(self, parsed_args): try: delete_method(bgpvpn['id'], id) LOG.warning( - _LW("%(assoc_res_name)s association %(id)s deleted"), + "%(assoc_res_name)s association %(id)s deleted", {'assoc_res_name': self._assoc_res_name.capitalize(), 'id': id}) except Exception as e: fails += 1 - LOG.error(_LE("Failed to delete %(assoc_res_name)s " - "association with ID '%(id)s': %(e)s"), + LOG.error("Failed to delete %(assoc_res_name)s " + "association with ID '%(id)s': %(e)s", {'assoc_res_name': self._assoc_res_name, 'id': id, 'e': e}) From 5c7812f17883174ba91e67210bb0b17566f7d5f1 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Sat, 25 Mar 2017 01:24:59 +0900 Subject: [PATCH 564/845] Fix label of allowed-address-pair ip_address CLI reference is still generated from neutron CLI. It looks better to fix the output of CLI. Change-Id: Icf676730b53044fa0a14c4101eb52a792ea4cc4f Closes-Bug: #1669974 --- neutronclient/neutron/v2_0/port.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index f95089b61..0982b049a 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -195,7 +195,7 @@ def add_arguments_allowedaddresspairs(self, parser): group_aap = parser.add_mutually_exclusive_group() group_aap.add_argument( '--allowed-address-pair', - metavar='ip_address=IP_ADDR[,mac_address=MAC_ADDR]', + metavar='ip_address=IP_ADDR|CIDR[,mac_address=MAC_ADDR]', default=[], action='append', dest='allowed_address_pairs', @@ -203,6 +203,9 @@ def add_arguments_allowedaddresspairs(self, parser): required_keys=['ip_address'], optional_keys=['mac_address']), help=_('Allowed address pair associated with the port. ' + '"ip_address" parameter is required. IP address or ' + 'CIDR can be specified for "ip_address". ' + '"mac_address" parameter is optional. ' 'You can repeat this option.')) group_aap.add_argument( '--no-allowed-address-pairs', From 9baf4b892eeb3864ec4f3b93d81866de66e99b49 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Sat, 25 Mar 2017 04:27:09 +0900 Subject: [PATCH 565/845] Allow to specify tenant_id in subnetpool-create Previously tenant_id option was ignored in subnetpool-create. --tenant-id option is supported in all create commands in neutron CLI and it is important to keep this consistency for good user experience. Change-Id: Ifab51d623f8f3519523b284fc04e5b28301b5928 Closes-Bug: #1667279 --- neutronclient/neutron/v2_0/subnetpool.py | 1 + .../tests/unit/test_cli20_subnetpool.py | 17 ++++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/neutronclient/neutron/v2_0/subnetpool.py b/neutronclient/neutron/v2_0/subnetpool.py index 7d83c1188..fdefc8a47 100644 --- a/neutronclient/neutron/v2_0/subnetpool.py +++ b/neutronclient/neutron/v2_0/subnetpool.py @@ -99,6 +99,7 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): body = {'prefixes': parsed_args.prefixes} updatable_args2body(parsed_args, body) + neutronV20.update_dict(parsed_args, body, ['tenant_id']) if parsed_args.shared: body['shared'] = True diff --git a/neutronclient/tests/unit/test_cli20_subnetpool.py b/neutronclient/tests/unit/test_cli20_subnetpool.py index 63d04f411..343393e5b 100644 --- a/neutronclient/tests/unit/test_cli20_subnetpool.py +++ b/neutronclient/tests/unit/test_cli20_subnetpool.py @@ -30,7 +30,7 @@ class CLITestV20SubnetPoolJSON(test_cli20.CLITestV20Base): def setUp(self): super(CLITestV20SubnetPoolJSON, self).setUp(plurals={'tags': 'tag'}) - def test_create_subnetpool_shared(self): + def test_create_subnetpool_with_options(self): # Create subnetpool: myname. resource = 'subnetpool' cmd = subnetpool.CreateSubnetPool(test_cli20.MyApp(sys.stdout), None) @@ -41,14 +41,17 @@ def test_create_subnetpool_shared(self): prefix2 = '12.11.13.0/24' args = [name, '--min-prefixlen', str(min_prefixlen), '--pool-prefix', prefix1, '--pool-prefix', prefix2, - '--shared', '--description', 'public pool'] - position_names = ['name', 'min_prefixlen', 'prefixes', 'shared'] - position_values = [name, min_prefixlen, [prefix1, prefix2], True] + '--shared', '--description', 'public pool', + '--tenant-id', 'tenantid'] + position_names = ['name', 'min_prefixlen', 'prefixes', 'shared', + 'tenant_id'] + position_values = [name, min_prefixlen, [prefix1, prefix2], True, + 'tenantid'] self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values, description='public pool') - def test_create_subnetpool_not_shared(self): + def test_create_subnetpool_only_with_required_options(self): # Create subnetpool: myname. resource = 'subnetpool' cmd = subnetpool.CreateSubnetPool(test_cli20.MyApp(sys.stdout), None) @@ -64,7 +67,7 @@ def test_create_subnetpool_not_shared(self): self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) - def test_create_subnetpool(self, default='false'): + def test_create_subnetpool_with_is_default(self, default='false'): # Create subnetpool: myname. resource = 'subnetpool' cmd = subnetpool.CreateSubnetPool(test_cli20.MyApp(sys.stdout), None) @@ -82,7 +85,7 @@ def test_create_subnetpool(self, default='false'): position_names, position_values) def test_create_subnetpool_default(self): - self.test_create_subnetpool(default='true') + self.test_create_subnetpool_with_is_default(default='true') def test_create_subnetpool_with_unicode(self): # Create subnetpool: u'\u7f51\u7edc'. From a7fdab5ccf5031403dfbe43419158916200ae688 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Mon, 27 Mar 2017 08:01:05 +0000 Subject: [PATCH 566/845] FWaaS OSC plugin: Use python logging instead of oslo.log CLI implementation including OSC and neutorn CLI does not consume oslo.log and use python logging directly. At now oslo.log is not included in requirements.txt of both OSC and neutronclient and CLI does not use features provided by oslo.log. Let's use python logging. Change-Id: I81e943fb87567f0e01635888bebf875ea42a4d02 Closes-Bug: #1676295 --- neutronclient/osc/v2/fwaas/firewallgroup.py | 4 ++-- neutronclient/osc/v2/fwaas/firewallpolicy.py | 3 ++- neutronclient/osc/v2/fwaas/firewallrule.py | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/neutronclient/osc/v2/fwaas/firewallgroup.py b/neutronclient/osc/v2/fwaas/firewallgroup.py index b5b388879..68f0fa49c 100644 --- a/neutronclient/osc/v2/fwaas/firewallgroup.py +++ b/neutronclient/osc/v2/fwaas/firewallgroup.py @@ -12,12 +12,12 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -# + +import logging from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils -from oslo_log import log as logging from neutronclient._i18n import _ from neutronclient.osc import utils as osc_utils diff --git a/neutronclient/osc/v2/fwaas/firewallpolicy.py b/neutronclient/osc/v2/fwaas/firewallpolicy.py index 288bac098..b0f0a4243 100644 --- a/neutronclient/osc/v2/fwaas/firewallpolicy.py +++ b/neutronclient/osc/v2/fwaas/firewallpolicy.py @@ -16,10 +16,11 @@ from __future__ import print_function +import logging + from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils -from oslo_log import log as logging from neutronclient._i18n import _ from neutronclient.osc import utils as osc_utils diff --git a/neutronclient/osc/v2/fwaas/firewallrule.py b/neutronclient/osc/v2/fwaas/firewallrule.py index ec8735165..377ede740 100644 --- a/neutronclient/osc/v2/fwaas/firewallrule.py +++ b/neutronclient/osc/v2/fwaas/firewallrule.py @@ -14,11 +14,11 @@ # under the License. # import copy +import logging from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils -from oslo_log import log as logging from neutronclient._i18n import _ from neutronclient.common import utils as nc_utils From a905f755b995f358116a11f77de71893b0601937 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Sun, 2 Apr 2017 00:49:40 +0900 Subject: [PATCH 567/845] Do not append .json to sending URL Both SessionClient and HTTPClient set content-type as application/json, so there is no need to append ".json" as a format specifier. This commit also cleans up 'format' attribute in various client class and unit tests. Also hide --request-format opiton from the command line help as it is just ignored. We need to keep the option to keep backward-compatibility. Closes-Bug: #1678488 Change-Id: I5fc69b9dfed9b7b6350a4aad06417a914a80d090 --- neutronclient/neutron/v2_0/__init__.py | 2 +- .../neutron/v2_0/lb/v2/loadbalancer.py | 1 - neutronclient/neutron/v2_0/rbac.py | 1 - .../unit/fw/test_cli20_firewallpolicy.py | 4 +- neutronclient/tests/unit/test_cli20.py | 54 +++++++------------ neutronclient/tests/unit/test_cli20_tag.py | 5 +- neutronclient/v2_0/client.py | 2 - 7 files changed, 23 insertions(+), 46 deletions(-) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index a980fe1f0..b1b675aa7 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -336,7 +336,7 @@ def get_parser(self, prog_name): parser = super(NeutronCommand, self).get_parser(prog_name) parser.add_argument( '--request-format', - help=_('DEPRECATED! Only JSON request format is supported.'), + help=argparse.SUPPRESS, default='json', choices=['json', ], ) parser.add_argument( diff --git a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py index 8cc6dffb3..c40809fad 100644 --- a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py +++ b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py @@ -124,7 +124,6 @@ class RetrieveLoadBalancerStats(neutronV20.ShowCommand): def take_action(self, parsed_args): neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format loadbalancer_id = neutronV20.find_resourceid_by_name_or_id( self.get_client(), 'loadbalancer', parsed_args.id) params = {} diff --git a/neutronclient/neutron/v2_0/rbac.py b/neutronclient/neutron/v2_0/rbac.py index e1153b055..8b356d3ec 100644 --- a/neutronclient/neutron/v2_0/rbac.py +++ b/neutronclient/neutron/v2_0/rbac.py @@ -82,7 +82,6 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format _object_id, _object_type = get_rbac_obj_params(neutron_client, parsed_args.type, parsed_args.name) diff --git a/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py b/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py index 8ec611c86..1b425e6d1 100644 --- a/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py +++ b/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py @@ -173,7 +173,7 @@ def test_insert_firewall_rule(self): path = getattr(self.client, resource + "_insert_path") self.client.httpclient.request( test_cli20.MyUrlComparator( - test_cli20.end_url(path % myid, format=self.format), + test_cli20.end_url(path % myid), self.client), 'PUT', body=test_cli20.MyComparator(body, self.client), headers=mox.ContainsKeyValue( @@ -202,7 +202,7 @@ def test_remove_firewall_rule(self): path = getattr(self.client, resource + "_remove_path") self.client.httpclient.request( test_cli20.MyUrlComparator( - test_cli20.end_url(path % myid, format=self.format), + test_cli20.end_url(path % myid), self.client), 'PUT', body=test_cli20.MyComparator(body, self.client), headers=mox.ContainsKeyValue( diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 4ff306abc..3437a8f41 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -37,7 +37,6 @@ from neutronclient.v2_0 import client API_VERSION = "2.0" -FORMAT = 'json' TOKEN = 'testtoken' ENDURL = 'localurl' REQUEST_ID = 'test_request_id' @@ -89,8 +88,8 @@ def __init__(self, _stdout): self.stdout = _stdout -def end_url(path, query=None, format=FORMAT): - _url_str = ENDURL + "/v" + API_VERSION + path + "." + format +def end_url(path, query=None): + _url_str = ENDURL + "/v" + API_VERSION + path return query and _url_str + "?" + query or _url_str @@ -113,16 +112,6 @@ def equals(self, rhs): set(lhs_qs) == set(rhs_qs)) def __str__(self): - if self.client and self.client.format != FORMAT: - lhs_parts = self.lhs.split("?", 1) - if len(lhs_parts) == 2: - lhs = ("%s.%s?%s" % (lhs_parts[0][:-4], - self.client.format, - lhs_parts[1])) - else: - lhs = ("%s.%s" % (lhs_parts[0][:-4], - self.client.format)) - return lhs return self.lhs def __repr__(self): @@ -183,7 +172,6 @@ def __repr__(self): class CLITestV20Base(base.BaseTestCase): - format = 'json' test_id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' id_field = 'id' @@ -212,7 +200,6 @@ def setUp(self, plurals=None): new=self._find_resourceid).start() self.client = client.Client(token=TOKEN, endpoint_url=self.endurl) - self.client.format = self.format def register_non_admin_status_resource(self, resource_name): # TODO(amotoki): @@ -269,7 +256,7 @@ def _test_create_resource(self, resource, cmd, name, myid, args, if not no_api_call: self.client.httpclient.request( - end_url(path, format=self.format), 'POST', + end_url(path), 'POST', body=mox_body, headers=mox.ContainsKeyValue( 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr)) @@ -302,7 +289,7 @@ def _test_list_columns(self, cmd, resources, if parent_id: path = path % parent_id self.client.httpclient.request( - end_url(path, format=self.format), 'GET', + end_url(path), 'GET', body=None, headers=mox.ContainsKeyValue( 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr)) @@ -396,7 +383,7 @@ def _test_list_resources(self, resources, cmd, detail=False, tags=(), args.append('-f') args.append(output_format) self.client.httpclient.request( - MyUrlComparator(end_url(path, query, format=self.format), + MyUrlComparator(end_url(path, query), self.client), 'GET', body=None, @@ -435,12 +422,12 @@ def _test_list_resources_with_pagination(self, resources, cmd, resstr1 = self.client.serialize(reses1) resstr2 = self.client.serialize(reses2) self.client.httpclient.request( - end_url(path, query, format=self.format), 'GET', + end_url(path, query), 'GET', body=None, headers=mox.ContainsKeyValue( 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr1)) self.client.httpclient.request( - MyUrlComparator(end_url(path, fake_query, format=self.format), + MyUrlComparator(end_url(path, fake_query), self.client), 'GET', body=None, headers=mox.ContainsKeyValue( @@ -469,8 +456,7 @@ def _test_update_resource(self, resource, cmd, myid, args, extrafields, mox_body = MyComparator(body, self.client) self.client.httpclient.request( - MyUrlComparator(end_url(path, format=self.format), - self.client), + MyUrlComparator(end_url(path), self.client), 'PUT', body=mox_body, headers=mox.ContainsKeyValue( @@ -502,7 +488,7 @@ def _test_show_resource(self, resource, cmd, myid, args, fields=(), else: path = path % myid self.client.httpclient.request( - end_url(path, query, format=self.format), 'GET', + end_url(path, query), 'GET', body=None, headers=mox.ContainsKeyValue( 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr)) @@ -523,7 +509,7 @@ def _test_set_path_and_delete(self, path, parent_id, myid, else: path = path % (myid) self.client.httpclient.request( - end_url(path, format=self.format), 'DELETE', + end_url(path), 'DELETE', body=None, headers=mox.ContainsKeyValue( 'X-Auth-Token', TOKEN)).AndReturn((MyResp( @@ -563,7 +549,7 @@ def _test_update_resource_action(self, resource, cmd, myid, action, args, path = getattr(self.client, cmd_resource + "_path") path_action = '%s/%s' % (myid, action) self.client.httpclient.request( - end_url(path % path_action, format=self.format), 'PUT', + end_url(path % path_action), 'PUT', body=MyComparator(body, self.client), headers=mox.ContainsKeyValue( 'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), retval)) @@ -670,7 +656,7 @@ def test_do_request_unicode(self): resp_headers = {'x-openstack-request-id': REQUEST_ID} self.client.httpclient.request( - end_url(expected_action, query=expect_query, format=self.format), + end_url(expected_action, query=expect_query), 'PUT', body=expect_body, headers=mox.ContainsKeyValue( 'X-Auth-Token', @@ -694,8 +680,7 @@ def test_do_request_error_without_response_body(self): resp_headers = {'x-openstack-request-id': REQUEST_ID} self.client.httpclient.request( - MyUrlComparator(end_url( - '/test', query=expect_query, format=self.format), self.client), + MyUrlComparator(end_url('/test', query=expect_query), self.client), 'PUT', body='', headers=mox.ContainsKeyValue('X-Auth-Token', 'token') ).AndReturn((MyResp(400, headers=resp_headers, reason='An error'), '')) @@ -727,9 +712,7 @@ def test_do_request_request_ids(self): expect_body = self.client.serialize(body) resp_headers = {'x-openstack-request-id': REQUEST_ID} self.client.httpclient.request( - MyUrlComparator(end_url( - '/test', query=expect_query, - format=self.format), self.client), + MyUrlComparator(end_url('/test', query=expect_query), self.client), 'PUT', body=expect_body, headers=mox.ContainsKeyValue('X-Auth-Token', 'token') ).AndReturn((MyResp(200, resp_headers), expect_body)) @@ -759,13 +742,13 @@ def test_list_request_ids_with_retrieve_all_true(self): resstr2 = self.client.serialize(reses2) resp_headers = {'x-openstack-request-id': REQUEST_ID} self.client.httpclient.request( - end_url(path, "", format=self.format), 'GET', + end_url(path, ""), 'GET', body=None, headers=mox.ContainsKeyValue( 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200, resp_headers), resstr1)) self.client.httpclient.request( - MyUrlComparator(end_url(path, fake_query, format=self.format), + MyUrlComparator(end_url(path, fake_query), self.client), 'GET', body=None, headers=mox.ContainsKeyValue( @@ -795,14 +778,13 @@ def test_list_request_ids_with_retrieve_all_false(self): resstr2 = self.client.serialize(reses2) resp_headers = {'x-openstack-request-id': REQUEST_ID} self.client.httpclient.request( - end_url(path, "", format=self.format), 'GET', + end_url(path, ""), 'GET', body=None, headers=mox.ContainsKeyValue( 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200, resp_headers), resstr1)) self.client.httpclient.request( - MyUrlComparator(end_url(path, fake_query, format=self.format), - self.client), 'GET', + MyUrlComparator(end_url(path, fake_query), self.client), 'GET', body=None, headers=mox.ContainsKeyValue( 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200, resp_headers), diff --git a/neutronclient/tests/unit/test_cli20_tag.py b/neutronclient/tests/unit/test_cli20_tag.py index 09b99ab0a..edd70a7e9 100644 --- a/neutronclient/tests/unit/test_cli20_tag.py +++ b/neutronclient/tests/unit/test_cli20_tag.py @@ -30,8 +30,7 @@ def _test_tag_operation(self, cmd, path, method, args, prog_name, if body: body = test_cli20.MyComparator(body, self.client) self.client.httpclient.request( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, format=self.format), self.client), + test_cli20.MyUrlComparator(test_cli20.end_url(path), self.client), method, body=body, headers=mox.ContainsKeyValue( 'X-Auth-Token', test_cli20.TOKEN)).AndReturn( @@ -51,7 +50,7 @@ def _test_tags_query(self, cmd, resources, args, query): resstr = self.client.serialize(res) self.client.httpclient.request( test_cli20.MyUrlComparator( - test_cli20.end_url(path, query, format=self.format), + test_cli20.end_url(path, query), self.client), 'GET', body=None, headers=mox.ContainsKeyValue( diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 759f7765e..d2ace8044 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -250,7 +250,6 @@ def __init__(self, **kwargs): self.raise_errors = kwargs.pop('raise_errors', True) self.httpclient = client.construct_http_client(**kwargs) self.version = '2.0' - self.format = 'json' self.action_prefix = "/v%s" % (self.version) self.retry_interval = 1 @@ -270,7 +269,6 @@ def _handle_fault_response(self, status_code, response_body, resp): def do_request(self, method, action, body=None, headers=None, params=None): # Add format and project_id - action += ".%s" % self.format action = self.action_prefix + action if isinstance(params, dict) and params: params = utils.safe_encode_dict(params) From db377519eb0f271b7078ab5572924c8f19b8792e Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Wed, 12 Apr 2017 04:21:40 +0000 Subject: [PATCH 568/845] Updated from global requirements Change-Id: I2fe3503185ab8261daffc32d1345b6826c224179 --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index db647de59..aa43e7466 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ # The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. -pbr>=2.0.0 # Apache-2.0 +pbr!=2.1.0,>=2.0.0 # Apache-2.0 cliff>=2.3.0 # Apache-2.0 debtcollector>=1.2.0 # Apache-2.0 iso8601>=0.1.11 # MIT @@ -18,4 +18,4 @@ python-keystoneclient>=3.8.0 # Apache-2.0 requests!=2.12.2,!=2.13.0,>=2.10.0 # Apache-2.0 simplejson>=2.2.0 # MIT six>=1.9.0 # MIT -Babel>=2.3.4 # BSD +Babel!=2.4.0,>=2.3.4 # BSD From 4d4e4decc39fe68f3624f92903ed29edd605375e Mon Sep 17 00:00:00 2001 From: Hirofumi Ichihara Date: Tue, 9 Aug 2016 11:00:26 +0900 Subject: [PATCH 569/845] Fix using wrong status code in some tests Some client tests uses status code 204 although it expects the API returns a body with status code 200, for instance, test_add_interface_by_subnet, test_add_peer_to_bgp_speaker, and so on. This patch makes the tests use correct status code and also changes default status code into 200 because it's an usual code of PUT operation. Change-Id: I5dbcad8769a32e8834347fb57695db72b313b0a5 Closes-bug: #1611167 --- neutronclient/tests/unit/bgp/test_cli20_speaker.py | 12 ++++++++++-- neutronclient/tests/unit/test_cli20.py | 6 ++++-- neutronclient/tests/unit/test_cli20_router.py | 5 ++++- neutronclient/v2_0/client.py | 3 +-- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/neutronclient/tests/unit/bgp/test_cli20_speaker.py b/neutronclient/tests/unit/bgp/test_cli20_speaker.py index 63ce5fc82..2f6e65388 100644 --- a/neutronclient/tests/unit/bgp/test_cli20_speaker.py +++ b/neutronclient/tests/unit/bgp/test_cli20_speaker.py @@ -210,10 +210,14 @@ def _test_add_remove_peer(self, action, cmd, args): body = {'bgp_peer_id': 'peerid'} if action == 'add': retval = {'bgp_peer': 'peerid'} + retval = self.client.serialize(retval) + expected_code = 200 else: retval = None + expected_code = 204 self._test_update_resource_action(resource, cmd, 'myid', - subcmd, args, body, retval) + subcmd, args, body, expected_code, + retval) def test_add_peer_to_bgp_speaker(self): # Add peer to BGP speaker: myid peer_id=peerid @@ -236,10 +240,14 @@ def _test_add_remove_network(self, action, cmd, args): body = {'network_id': 'netid'} if action == 'add': retval = {'network': 'netid'} + retval = self.client.serialize(retval) + expected_code = 200 else: retval = None + expected_code = 204 self._test_update_resource_action(resource, cmd, 'myid', - subcmd, args, body, retval) + subcmd, args, body, expected_code, + retval) def test_add_network_to_bgp_speaker(self): # Add peer to BGP speaker: myid network_id=netid diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 80362858e..e8b5818bc 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -531,7 +531,8 @@ def _test_delete_resource(self, resource, cmd, myid, args, self.assertIn(myid, _str) def _test_update_resource_action(self, resource, cmd, myid, action, args, - body, retval=None, cmd_resource=None): + body, expected_code=200, retval=None, + cmd_resource=None): self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") cmd.get_client().MultipleTimes().AndReturn(self.client) @@ -543,7 +544,8 @@ def _test_update_resource_action(self, resource, cmd, myid, action, args, end_url(path % path_action, format=self.format), 'PUT', body=MyComparator(body, self.client), headers=mox.ContainsKeyValue( - 'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), retval)) + 'X-Auth-Token', TOKEN)).AndReturn((MyResp(expected_code), + retval)) self.mox.ReplayAll() cmd_parser = cmd.get_parser("delete_" + cmd_resource) shell.run_command(cmd, cmd_parser, args) diff --git a/neutronclient/tests/unit/test_cli20_router.py b/neutronclient/tests/unit/test_cli20_router.py index cd7ed0310..0325d9e66 100644 --- a/neutronclient/tests/unit/test_cli20_router.py +++ b/neutronclient/tests/unit/test_cli20_router.py @@ -290,11 +290,14 @@ def _test_add_remove_interface(self, action, mode, cmd, args): body = {'subnet_id': 'subnetid'} if action == 'add': retval = {'subnet_id': 'subnetid', 'port_id': 'portid'} + retval = self.client.serialize(retval) + expected_code = 200 else: retval = None + expected_code = 204 self._test_update_resource_action(resource, cmd, 'myid', subcmd, args, - body, retval) + body, expected_code, retval) def test_add_interface_compat(self): # Add interface to router: myid subnetid. diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 9a01b38b1..53a402447 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -305,8 +305,7 @@ def serialize(self, data): def deserialize(self, data, status_code): """Deserializes a JSON string into a dictionary.""" - # TODO(hichihara): Remove checking 204 in bug 1611167 - if status_code == 204 or not data: + if not data: return data return serializer.Serializer().deserialize( data)['body'] From ef69c7260d7c76e1f3a3f4a083607b3457c02fe8 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Thu, 4 May 2017 13:30:53 +0000 Subject: [PATCH 570/845] Updated from global requirements Change-Id: I46cbb16bda94a9501a65231a3a16f6885c5c8453 --- test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index b4efe102b..47b5379fa 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -10,7 +10,7 @@ mock>=2.0 # BSD oslosphinx>=4.7.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 osprofiler>=1.4.0 # Apache-2.0 -python-openstackclient>=3.3.0 # Apache-2.0 +python-openstackclient!=3.10.0,>=3.3.0 # Apache-2.0 python-subunit>=0.0.18 # Apache-2.0/BSD reno>=1.8.0 # Apache-2.0 requests-mock>=1.1 # Apache-2.0 From 4295dfb8de677d53cf82d6a1364c8e70af7d6de5 Mon Sep 17 00:00:00 2001 From: IWAMOTO Toshihiro Date: Wed, 21 Sep 2016 16:48:04 +0900 Subject: [PATCH 571/845] Add optional_keys and required_keys to --subport argument Change-Id: I2f247076226f06340a49989319065f61f8dd1d3e Closes-bug: #1625957 --- neutronclient/osc/v2/trunk/network_trunk.py | 4 + .../unit/osc/v2/trunk/test_network_trunk.py | 96 +++++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/neutronclient/osc/v2/trunk/network_trunk.py b/neutronclient/osc/v2/trunk/network_trunk.py index 0cee32e5d..a6aadbb85 100644 --- a/neutronclient/osc/v2/trunk/network_trunk.py +++ b/neutronclient/osc/v2/trunk/network_trunk.py @@ -58,6 +58,8 @@ def get_parser(self, prog_name): '--subport', metavar='', action=parseractions.MultiKeyValueAction, dest='add_subports', + optional_keys=['segmentation-id', 'segmentation-type'], + required_keys=['port'], help=_("Subport to add. Subport is of form " "\'port=,segmentation-type=,segmentation-ID=\' " "(--subport) option can be repeated") @@ -193,6 +195,8 @@ def get_parser(self, prog_name): '--subport', metavar='', action=parseractions.MultiKeyValueAction, dest='set_subports', + optional_keys=['segmentation-id', 'segmentation-type'], + required_keys=['port'], help=_("Subport to add. Subport is of form " "\'port=,segmentation-type=,segmentation-ID=\'" "(--subport) option can be repeated") diff --git a/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py b/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py index 31c446ce1..8408f7edf 100644 --- a/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py +++ b/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py @@ -13,6 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. +import argparse +import copy import mock from mock import call import testtools @@ -162,6 +164,56 @@ def test_create_trunk_with_subport_invalid_segmentation_id_fail(self): self.assertEqual("Segmentation-id 'boom' is not an integer", str(e)) + def test_create_network_trunk_subports_without_optional_keys(self): + subport = copy.copy(self._trunk['sub_ports'][0]) + # Pop out the segmentation-id and segmentation-type + subport.pop('segmentation_type') + subport.pop('segmentation_id') + arglist = [ + '--parent-port', self._trunk['port_id'], + '--subport', 'port=%(port)s' % {'port': subport['port_id']}, + self._trunk['name'], + ] + verifylist = [ + ('name', self._trunk['name']), + ('parent_port', self._trunk['port_id']), + ('add_subports', [{ + 'port': subport['port_id']}]), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = (self.cmd.take_action(parsed_args)) + + self.neutronclient.create_trunk.assert_called_once_with({ + trunk.TRUNK: {'name': self._trunk['name'], + 'admin_state_up': True, + 'sub_ports': [subport], + 'port_id': self._trunk['port_id']} + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_create_network_trunk_subports_without_required_key_fail(self): + subport = self._trunk['sub_ports'][0] + arglist = [ + '--parent-port', self._trunk['port_id'], + '--subport', 'segmentation-type=%(seg_type)s,' + 'segmentation-id=%(seg_id)s' % { + 'seg_id': subport['segmentation_id'], + 'seg_type': subport['segmentation_type']}, + self._trunk['name'], + ] + verifylist = [ + ('name', self._trunk['name']), + ('parent_port', self._trunk['port_id']), + ('add_subports', [{ + 'segmentation-id': str(subport['segmentation_id']), + 'segmentation-type': subport['segmentation_type']}]), + ] + + with testtools.ExpectedException(argparse.ArgumentTypeError): + self.check_parser(self.cmd, arglist, verifylist) + class TestDeleteNetworkTrunk(test_fakes.TestNeutronClientOSCV2): # The trunk to be deleted. @@ -511,6 +563,50 @@ def test_set_network_trunk_subports(self): ) self.assertIsNone(result) + def test_set_network_trunk_subports_without_optional_keys(self): + subport = copy.copy(self._trunk['sub_ports'][0]) + # Pop out the segmentation-id and segmentation-type + subport.pop('segmentation_type') + subport.pop('segmentation_id') + arglist = [ + '--subport', 'port=%(port)s' % {'port': subport['port_id']}, + self._trunk['name'], + ] + verifylist = [ + ('trunk', self._trunk['name']), + ('set_subports', [{ + 'port': subport['port_id']}]), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.neutronclient.trunk_add_subports.assert_called_once_with( + self._trunk['name'], {'sub_ports': [subport]} + ) + self.assertIsNone(result) + + def test_set_network_trunk_subports_without_required_key_fail(self): + subport = self._trunk['sub_ports'][0] + arglist = [ + '--subport', 'segmentation-type=%(seg_type)s,' + 'segmentation-id=%(seg_id)s' % { + 'seg_id': subport['segmentation_id'], + 'seg_type': subport['segmentation_type']}, + self._trunk['name'], + ] + verifylist = [ + ('trunk', self._trunk['name']), + ('set_subports', [{ + 'segmentation-id': str(subport['segmentation_id']), + 'segmentation-type': subport['segmentation_type']}]), + ] + + with testtools.ExpectedException(argparse.ArgumentTypeError): + self.check_parser(self.cmd, arglist, verifylist) + + self.neutronclient.trunk_add_subports.assert_not_called() + def test_set_trunk_attrs_with_exception(self): arglist = [ '--name', 'reallylongname', From fbef092b6fbdd2012f27d7d94e7b3f9145373b3e Mon Sep 17 00:00:00 2001 From: huangtianhua Date: Tue, 28 Mar 2017 10:48:33 +0800 Subject: [PATCH 572/845] Supports adding tags for port, router, subnet, subnetpool Neutron supports adding tags for several resources: network, port, subnet, subnetpool, router, but neutronclient only support to add tags for network, support for others . Change-Id: Iae8da2fa0e18e14ba27ce5232db8da5fe6eb0b86 Closes-Bug: #1676697 --- neutronclient/neutron/v2_0/tag.py | 2 +- ...upport-subnet-port-subnetpool-router-6250ec4714ee8690.yaml | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/tag-support-subnet-port-subnetpool-router-6250ec4714ee8690.yaml diff --git a/neutronclient/neutron/v2_0/tag.py b/neutronclient/neutron/v2_0/tag.py index 774849cd8..45827161e 100644 --- a/neutronclient/neutron/v2_0/tag.py +++ b/neutronclient/neutron/v2_0/tag.py @@ -16,7 +16,7 @@ # List of resources can be set tag -TAG_RESOURCES = ['network'] +TAG_RESOURCES = ['network', 'subnet', 'port', 'router', 'subnetpool'] def _convert_resource_args(client, parsed_args): diff --git a/releasenotes/notes/tag-support-subnet-port-subnetpool-router-6250ec4714ee8690.yaml b/releasenotes/notes/tag-support-subnet-port-subnetpool-router-6250ec4714ee8690.yaml new file mode 100644 index 000000000..1bc27aeb8 --- /dev/null +++ b/releasenotes/notes/tag-support-subnet-port-subnetpool-router-6250ec4714ee8690.yaml @@ -0,0 +1,4 @@ +--- +features: + - Tag operation for subnet, port, subnetpool and router resources + are now supported. From 2e60acdff20a6170065d8ad073ef987d8dc643ef Mon Sep 17 00:00:00 2001 From: Armando Migliaccio Date: Mon, 1 May 2017 12:50:51 -0700 Subject: [PATCH 573/845] Be explicit about the identity plugin required in unit tests This change fixes UT failures introduced by os-client-config===1.27.0 (and [1] in particular) that was masking the fact that these tests were not specifying the required 'token' identity plugin. [1] https://github.com/openstack/os-client-config/commit/e6755872ada4978f585bdf15edf623dbcf72c4ee Change-Id: I9521a7e1bff39c1d25b998ca12c450890517fd2f --- neutronclient/tests/unit/test_shell.py | 1 + 1 file changed, 1 insertion(+) diff --git a/neutronclient/tests/unit/test_shell.py b/neutronclient/tests/unit/test_shell.py index 4e62d78d0..2ae8bdc6f 100644 --- a/neutronclient/tests/unit/test_shell.py +++ b/neutronclient/tests/unit/test_shell.py @@ -233,6 +233,7 @@ def _test_authenticate_user(self, expect_verify, expect_insecure, options.update(base_options) if options.get('os_token'): + options.update({'auth_type': 'token'}) options.update({'os_token': 'token', 'os_url': 'url'}) else: options.update({'os_token': None, 'os_url': None}) From 235c4e60aa6a7e0072b3f9fee8ba0c66dafcb19b Mon Sep 17 00:00:00 2001 From: M V P Nitesh Date: Mon, 24 Apr 2017 17:25:45 +0530 Subject: [PATCH 574/845] Now empty tags are not added to Networks Added a check if the tag values is empty or not before adding that tag to the network. Change-Id: I92659da97fe829a7715d0bef5570a4d9c5db95da Closes-Bug: #1603292 --- neutronclient/neutron/v2_0/tag.py | 3 +++ neutronclient/tests/unit/test_cli20_tag.py | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/neutronclient/neutron/v2_0/tag.py b/neutronclient/neutron/v2_0/tag.py index 45827161e..ff59b74ec 100644 --- a/neutronclient/neutron/v2_0/tag.py +++ b/neutronclient/neutron/v2_0/tag.py @@ -50,6 +50,9 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.get_client() + if not parsed_args.tag: + raise exceptions.CommandError( + _('Cannot add an empty value as tag')) resource_type, resource_id = _convert_resource_args(client, parsed_args) client.add_tag(resource_type, resource_id, parsed_args.tag) diff --git a/neutronclient/tests/unit/test_cli20_tag.py b/neutronclient/tests/unit/test_cli20_tag.py index edd70a7e9..1b04be63b 100644 --- a/neutronclient/tests/unit/test_cli20_tag.py +++ b/neutronclient/tests/unit/test_cli20_tag.py @@ -81,6 +81,14 @@ def test_add_tag(self): '--tag', 'red'] self._test_tag_operation(cmd, path, 'PUT', args, "tag-add") + def test_add_tag_empty_tag(self): + cmd = tag.AddTag(test_cli20.MyApp(sys.stdout), None) + path = self._make_tag_path('network', 'myid', '') + args = ['--resource-type', 'network', '--resource', 'myid', + '--tag', ''] + self.assertRaises(exceptions.CommandError, self._test_tag_operation, + cmd, path, 'PUT', args, "tag-add") + def test_replace_tag(self): cmd = tag.ReplaceTag(test_cli20.MyApp(sys.stdout), None) path = self._make_tags_path('network', 'myid') From 5a7e65db3d5b58cf6c85ffe091cd78e77e77d1e3 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Wed, 3 May 2017 12:23:01 +0000 Subject: [PATCH 575/845] Updated from global requirements Change-Id: I773de629c1ada3116b9a15f8e4a8a9ee9f24d0ea --- requirements.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index aa43e7466..1e6bdaf1c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,16 +2,16 @@ # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. pbr!=2.1.0,>=2.0.0 # Apache-2.0 -cliff>=2.3.0 # Apache-2.0 +cliff>=2.6.0 # Apache-2.0 debtcollector>=1.2.0 # Apache-2.0 iso8601>=0.1.11 # MIT netaddr!=0.7.16,>=0.7.13 # BSD -osc-lib>=1.2.0 # Apache-2.0 +osc-lib>=1.5.1 # Apache-2.0 oslo.i18n>=2.1.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 oslo.utils>=3.20.0 # Apache-2.0 -os-client-config>=1.22.0 # Apache-2.0 -keystoneauth1>=2.18.0 # Apache-2.0 +os-client-config>=1.27.0 # Apache-2.0 +keystoneauth1>=2.20.0 # Apache-2.0 # keystoneclient is used only by neutronclient.osc.utils # TODO(amotoki): Drop this after osc.utils has no dependency on keystoneclient python-keystoneclient>=3.8.0 # Apache-2.0 From c66f9726c0d26bb457617fcab58e54db2c17e847 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Sun, 14 May 2017 20:06:50 +0000 Subject: [PATCH 576/845] Drop deprecated neutronclient.i18n wrapper In favor of the private i18n wrapper (_i18n), the public 'i18n' wrapper has been deprecated for several releases. Each neutron CLI plugin (not OSC plugin) should use its own i18n wrapper and the usage of neutronclient.i18n wrapper was deprecated. It is time to drop this. Also fixes test_exception.py which still consumed neutronclient.i18n. Change-Id: I27a8ace2f31bea5e1d6bf8a5ebbbfa52a3228e73 --- neutronclient/i18n.py | 23 --------------------- neutronclient/tests/unit/test_exceptions.py | 2 +- 2 files changed, 1 insertion(+), 24 deletions(-) delete mode 100644 neutronclient/i18n.py diff --git a/neutronclient/i18n.py b/neutronclient/i18n.py deleted file mode 100644 index 8e9722c14..000000000 --- a/neutronclient/i18n.py +++ /dev/null @@ -1,23 +0,0 @@ -# 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. - -# TODO(amotoki): Remove this file at the beginning of Nxx cycle. - -from debtcollector import moves - -from neutronclient import _i18n - -message = ("moved to neutronclient._i18n; please migrate to local " - "oslo_i18n usage, as defined at " - "http://docs.openstack.org/developer/oslo.i18n/usage.html") - -_ = moves.moved_function(_i18n._, '_', __name__, message=message) diff --git a/neutronclient/tests/unit/test_exceptions.py b/neutronclient/tests/unit/test_exceptions.py index 6ef89c057..91742872c 100644 --- a/neutronclient/tests/unit/test_exceptions.py +++ b/neutronclient/tests/unit/test_exceptions.py @@ -17,8 +17,8 @@ import six import testtools +from neutronclient._i18n import _ from neutronclient.common import exceptions -from neutronclient.i18n import _ class TestExceptions(testtools.TestCase): From 632671e4d96c49354378598ede00a495175f82da Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Sun, 14 May 2017 19:24:07 +0000 Subject: [PATCH 577/845] FWaaS UT: Fake rule should return value returned from API In FWaaS UT, fwaas.fakes.FirewallRule is expected to return the same values returned from the API, but it returns 'any' for 'protocol'. In FWaaS API definition, JSON null (None in python) means 'any', so it should be None rather than 'any'. This commit fixes it and related codes in test_firewall.py. _generate_data() generates expected formatted data from API data. The same logic is also needed in _update_expect_response(). This commit introduces a common function _replace_display_columns to do this. Change-Id: If4fa6b26f72003b6c94020a762a5c19d17b338cd --- .../tests/unit/osc/v2/fwaas/fakes.py | 2 +- .../unit/osc/v2/fwaas/test_firewallrule.py | 20 +++++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/neutronclient/tests/unit/osc/v2/fwaas/fakes.py b/neutronclient/tests/unit/osc/v2/fwaas/fakes.py index 0eb3fe529..7a22ab1ab 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/fakes.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/fakes.py @@ -107,7 +107,7 @@ def __init__(self): ('description', 'my-desc-' + uuid.uuid4().hex), ('ip_version', 4), ('action', 'deny'), - ('protocol', 'any'), + ('protocol', None), ('source_ip_address', '192.168.1.0/24'), ('source_port', '1:11111'), ('destination_ip_address', '192.168.2.2'), diff --git a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py index e1ad201d5..c77d84920 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py @@ -42,7 +42,18 @@ def _generate_data(ordered_dict=None, data=None): source = ordered_dict if ordered_dict else _fwr if data: source.update(data) - return tuple(source[key] for key in source) + return tuple(_replace_display_columns(key, source[key]) for key in source) + + +def _replace_display_columns(key, val): + # TODO(amotoki): This is required because of the logic of + # osc_lib.utils.get_dict_properties(). + # It needs to be fixed in osc-lib first. + if val is None: + return val + if key == 'protocol': + return firewallrule.format_protocol(val) + return val def _generate_req_and_res(verifylist): @@ -133,7 +144,7 @@ def _mock_fwr(*args, **kwargs): _fwr['ip_version'], _fwr['name'], _fwr['tenant_id'], - _fwr['protocol'], + _replace_display_columns('protocol', _fwr['protocol']), _fwr['public'], _fwr['source_ip_address'], _fwr['source_port'], @@ -179,7 +190,8 @@ def _update_expect_response(self, request, response): # Update response(finally returns 'data') self.data = _generate_data(ordered_dict=response) self.ordered_data = tuple( - response[column] for column in self.ordered_columns + _replace_display_columns(column, response[column]) + for column in self.ordered_columns ) def _set_all_params(self, args={}): @@ -273,7 +285,7 @@ def test_create_with_all_params_protocol_upper_capitalized(self): class TestListFirewallRule(TestFirewallRule): def _setup_summary(self, expect=None): - protocol = _fwr['protocol'].upper() + protocol = (_fwr['protocol'] or 'any').upper() src = 'source(port): 192.168.1.0/24(1:11111)' dst = 'dest(port): 192.168.2.2(2:22222)' action = 'deny' From e40669237b1972e90d448001933644ae1d0c835c Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Sun, 14 May 2017 19:33:03 +0000 Subject: [PATCH 578/845] Machine-readable output for JSON/YAML format Currently even if machine-readable format like JSON or YAML is specified, formatted output is displayed. When machine-readable format is used, they should not be converted by formatters. Change-Id: I5f1e90ff60f30380106e1aa730f96a1f72c7a166 Closes-Bug: #1687955 --- neutronclient/osc/v2/fwaas/firewallgroup.py | 2 +- neutronclient/osc/v2/fwaas/firewallrule.py | 9 ++++-- .../osc/v2/networking_bgpvpn/bgpvpn.py | 13 ++++---- neutronclient/osc/v2/trunk/network_trunk.py | 5 +-- neutronclient/osc/v2/utils.py | 7 ++-- neutronclient/tests/unit/osc/v2/fakes.py | 18 +++++++++++ .../tests/unit/osc/v2/fwaas/common.py | 4 +-- .../unit/osc/v2/fwaas/test_firewallgroup.py | 10 +++--- .../unit/osc/v2/fwaas/test_firewallrule.py | 8 ++--- .../osc/v2/networking_bgpvpn/test_bgpvpn.py | 31 ++++++++++-------- .../unit/osc/v2/trunk/test_network_trunk.py | 32 +++++++++---------- 11 files changed, 85 insertions(+), 54 deletions(-) diff --git a/neutronclient/osc/v2/fwaas/firewallgroup.py b/neutronclient/osc/v2/fwaas/firewallgroup.py index b84442f13..ce7aac3c0 100644 --- a/neutronclient/osc/v2/fwaas/firewallgroup.py +++ b/neutronclient/osc/v2/fwaas/firewallgroup.py @@ -28,7 +28,7 @@ LOG = logging.getLogger(__name__) _formatters = { - 'admin_state_up': v2_utils.format_admin_state, + 'admin_state_up': v2_utils.AdminStateColumn, } _attr_map = ( diff --git a/neutronclient/osc/v2/fwaas/firewallrule.py b/neutronclient/osc/v2/fwaas/firewallrule.py index 00b3b216e..f2eeb1945 100644 --- a/neutronclient/osc/v2/fwaas/firewallrule.py +++ b/neutronclient/osc/v2/fwaas/firewallrule.py @@ -16,6 +16,7 @@ import copy import logging +from cliff import columns as cliff_columns from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils @@ -181,10 +182,12 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): return attrs -def format_protocol(protocol): - return protocol if protocol else 'any' +class ProtocolColumn(cliff_columns.FormattableColumn): + def human_readable(self): + return self._value if self._value else 'any' -_formatters = {'protocol': format_protocol} + +_formatters = {'protocol': ProtocolColumn} class CreateFirewallRule(command.ShowOne): diff --git a/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py b/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py index af7414ede..96d72ddfe 100644 --- a/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py +++ b/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py @@ -16,6 +16,7 @@ import logging +from osc_lib.cli import format_columns from osc_lib.cli.parseractions import KeyValueAction from osc_lib.command import command from osc_lib import exceptions @@ -41,12 +42,12 @@ ('routers', 'Associated Routers', nc_osc_utils.LIST_LONG_ONLY), ) _formatters = { - 'route_targets': osc_utils.format_list, - 'import_targets': osc_utils.format_list, - 'export_targets': osc_utils.format_list, - 'route_distinguishers': osc_utils.format_list, - 'networks': osc_utils.format_list, - 'routers': osc_utils.format_list, + 'route_targets': format_columns.ListColumn, + 'import_targets': format_columns.ListColumn, + 'export_targets': format_columns.ListColumn, + 'route_distinguishers': format_columns.ListColumn, + 'networks': format_columns.ListColumn, + 'routers': format_columns.ListColumn, } diff --git a/neutronclient/osc/v2/trunk/network_trunk.py b/neutronclient/osc/v2/trunk/network_trunk.py index a6aadbb85..26cb52be9 100644 --- a/neutronclient/osc/v2/trunk/network_trunk.py +++ b/neutronclient/osc/v2/trunk/network_trunk.py @@ -17,6 +17,7 @@ """Network trunk and subports action implementations""" import logging +from osc_lib.cli import format_columns from osc_lib.cli import parseractions from osc_lib.command import command from osc_lib import exceptions @@ -310,8 +311,8 @@ def take_action(self, parsed_args): _formatters = { - 'admin_state_up': v2_utils.format_admin_state, - 'sub_ports': osc_utils.format_list_of_dicts, + 'admin_state_up': v2_utils.AdminStateColumn, + 'sub_ports': format_columns.ListDictColumn, } diff --git a/neutronclient/osc/v2/utils.py b/neutronclient/osc/v2/utils.py index 9e9040179..b03e29c3d 100644 --- a/neutronclient/osc/v2/utils.py +++ b/neutronclient/osc/v2/utils.py @@ -16,6 +16,9 @@ to Networking v2 API and its extensions. """ +from cliff import columns as cliff_columns -def format_admin_state(state): - return 'UP' if state else 'DOWN' + +class AdminStateColumn(cliff_columns.FormattableColumn): + def human_readable(self): + return 'UP' if self._value else 'DOWN' diff --git a/neutronclient/tests/unit/osc/v2/fakes.py b/neutronclient/tests/unit/osc/v2/fakes.py index 75277a952..db962d2e5 100644 --- a/neutronclient/tests/unit/osc/v2/fakes.py +++ b/neutronclient/tests/unit/osc/v2/fakes.py @@ -14,6 +14,7 @@ import argparse import mock +from cliff import columns as cliff_columns from osc_lib.tests import utils @@ -25,3 +26,20 @@ def setUp(self): self.app.client_manager.session = mock.Mock() self.app.client_manager.neutronclient = mock.Mock() self.neutronclient = self.app.client_manager.neutronclient + + # TODO(amotoki): Move this to osc_lib + def assertListItemEqual(self, expected, actual): + self.assertEqual(len(expected), len(actual)) + for item_expected, item_actual in zip(expected, actual): + self.assertItemEqual(item_expected, item_actual) + + # TODO(amotoki): Move this to osc_lib + def assertItemEqual(self, expected, actual): + self.assertEqual(len(expected), len(actual)) + for col_expected, col_actual in zip(expected, actual): + if isinstance(col_expected, cliff_columns.FormattableColumn): + self.assertIsInstance(col_actual, col_expected.__class__) + self.assertEqual(col_expected.human_readable(), + col_actual.human_readable()) + else: + self.assertEqual(col_expected, col_actual) diff --git a/neutronclient/tests/unit/osc/v2/fwaas/common.py b/neutronclient/tests/unit/osc/v2/fwaas/common.py index b9ea7841b..cfd4df9c9 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/common.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/common.py @@ -42,7 +42,7 @@ def test_list_with_long_option(self): self.mocked.assert_called_once_with() self.assertEqual(list(self.headers), headers) - self.assertEqual([self.data], list(data)) + self.assertListItemEqual([self.data], list(data)) class TestShowFWaaS(test_fakes.TestNeutronClientOSCV2): @@ -67,7 +67,7 @@ def _mock_fwaas(*args, **kwargs): self.mocked.assert_called_once_with(target) self.assertEqual(self.ordered_headers, headers) - self.assertEqual(self.ordered_data, data) + self.assertItemEqual(self.ordered_data, data) class TestCreateFWaaS(test_fakes.TestNeutronClientOSCV2): diff --git a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py index cc074ff2f..0a15f2b6d 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py @@ -24,6 +24,7 @@ from neutronclient.osc import utils as osc_utils from neutronclient.osc.v2.fwaas import constants as const from neutronclient.osc.v2.fwaas import firewallgroup +from neutronclient.osc.v2 import utils as v2_utils from neutronclient.tests.unit.osc.v2 import fakes as test_fakes from neutronclient.tests.unit.osc.v2.fwaas import common from neutronclient.tests.unit.osc.v2.fwaas import fakes @@ -46,7 +47,8 @@ def _generate_response(ordered_dict=None, data=None): source = ordered_dict if ordered_dict else _fwg - up = {'admin_state_up': 'UP' if source['admin_state_up'] else 'DOWN'} + up = {'admin_state_up': + v2_utils.AdminStateColumn(source['admin_state_up'])} if data: up.append(data) source.update(up) @@ -81,7 +83,7 @@ def check_results(self, headers, data, exp_req, is_list=False): req_body = {self.res: exp_req} self.mocked.assert_called_once_with(req_body) self.assertEqual(self.ordered_headers, headers) - self.assertEqual(self.ordered_data, data) + self.assertItemEqual(self.ordered_data, data) def setUp(self): super(TestFirewallGroup, self).setUp() @@ -127,7 +129,7 @@ def _find_resource(*args, **kwargs): _fwg['ports'], _fwg['tenant_id'], _fwg['public'], - 'UP' if _fwg['admin_state_up'] else 'DOWN', + v2_utils.AdminStateColumn(_fwg['admin_state_up']), _fwg['status'], ) self.ordered_columns = ( @@ -180,7 +182,7 @@ def test_create_with_no_option(self): headers, data = self.cmd.take_action(parsed_args) self.assertEqual(self.ordered_headers, headers) - self.assertEqual(self.ordered_data, data) + self.assertItemEqual(self.ordered_data, data) def test_create_with_port(self): # firewall_group-create with 'port' diff --git a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py index c77d84920..d09bbbe98 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py @@ -52,7 +52,7 @@ def _replace_display_columns(key, val): if val is None: return val if key == 'protocol': - return firewallrule.format_protocol(val) + return firewallrule.ProtocolColumn(val) return val @@ -86,7 +86,7 @@ def check_results(self, headers, data, exp_req, is_list=False): req_body = {self.res: exp_req} self.mocked.assert_called_once_with(req_body) self.assertEqual(self.ordered_headers, headers) - self.assertEqual(self.ordered_data, data) + self.assertItemEqual(self.ordered_data, data) def setUp(self): super(TestFirewallRule, self).setUp() @@ -329,7 +329,7 @@ def test_list_with_long_option(self): self.mocked.assert_called_once_with() self.assertEqual(list(self.headers), headers) - self.assertEqual([self.data], list(data)) + self.assertListItemEqual([self.data], list(data)) def test_list_with_no_option(self): arglist = [] @@ -339,7 +339,7 @@ def test_list_with_no_option(self): self.mocked.assert_called_once_with() self.assertEqual(list(self.short_header), headers) - self.assertEqual([self.short_data], list(data)) + self.assertListItemEqual([self.short_data], list(data)) class TestShowFirewallRule(TestFirewallRule, common.TestShowFWaaS): diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py index 3d8348824..aa688277d 100644 --- a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py +++ b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py @@ -76,7 +76,7 @@ def test_create_bgpvpn_with_no_args(self): self.neutronclient.create_bgpvpn.assert_called_once_with( {constants.BGPVPN: {'type': 'l3'}}) self.assertEqual(sorted_headers, cols) - self.assertEqual(_get_data(fake_bgpvpn), data) + self.assertItemEqual(_get_data(fake_bgpvpn), data) def test_create_bgpvpn_with_all_args(self): attrs = { @@ -125,7 +125,7 @@ def test_create_bgpvpn_with_all_args(self): self.neutronclient.create_bgpvpn.assert_called_once_with( {constants.BGPVPN: fake_bgpvpn_call}) self.assertEqual(sorted_headers, cols) - self.assertEqual(_get_data(fake_bgpvpn), data) + self.assertItemEqual(_get_data(fake_bgpvpn), data) class TestSetBgpvpn(fakes.TestNeutronClientBgpvpn): @@ -409,9 +409,10 @@ def test_list_all_bgpvpn(self): self.neutronclient.list_bgpvpns.assert_called_once() self.assertEqual(headers, list(headers_short)) - self.assertEqual(list(data), - [_get_data(fake_bgpvpn, columns_short) for fake_bgpvpn - in fake_bgpvpns[constants.BGPVPNS]]) + self.assertListItemEqual( + list(data), + [_get_data(fake_bgpvpn, columns_short) for fake_bgpvpn + in fake_bgpvpns[constants.BGPVPNS]]) def test_list_all_bgpvpn_long_mode(self): count = 3 @@ -430,9 +431,10 @@ def test_list_all_bgpvpn_long_mode(self): self.neutronclient.list_bgpvpns.assert_called_once() self.assertEqual(headers, list(headers_long)) - self.assertEqual(list(data), - [_get_data(fake_bgpvpn, columns_long) for fake_bgpvpn - in fake_bgpvpns[constants.BGPVPNS]]) + self.assertListItemEqual( + list(data), + [_get_data(fake_bgpvpn, columns_long) for fake_bgpvpn + in fake_bgpvpns[constants.BGPVPNS]]) def test_list_project_bgpvpn(self): count = 3 @@ -455,9 +457,10 @@ def test_list_project_bgpvpn(self): self.neutronclient.list_bgpvpns.assert_called_once_with( tenant_id=project_id) self.assertEqual(headers, list(headers_short)) - self.assertEqual(list(data), - [_get_data(fake_bgpvpn, columns_short) for fake_bgpvpn - in fake_bgpvpns[constants.BGPVPNS]]) + self.assertListItemEqual( + list(data), + [_get_data(fake_bgpvpn, columns_short) for fake_bgpvpn + in fake_bgpvpns[constants.BGPVPNS]]) def test_list_bgpvpn_with_filters(self): count = 3 @@ -485,8 +488,8 @@ def test_list_bgpvpn_with_filters(self): name=name, type=layer_type) self.assertEqual(headers, list(headers_short)) - self.assertEqual(list(data), - [_get_data(returned_bgpvpn, columns_short)]) + self.assertListItemEqual(list(data), + [_get_data(returned_bgpvpn, columns_short)]) class TestShowBgpvpn(fakes.TestNeutronClientBgpvpn): @@ -512,4 +515,4 @@ def test_show_bgpvpn(self): self.neutronclient.show_bgpvpn.assert_called_once_with( fake_bgpvpn['id']) self.assertEqual(sorted_headers, headers) - self.assertEqual(_get_data(fake_bgpvpn), data) + self.assertItemEqual(_get_data(fake_bgpvpn), data) diff --git a/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py b/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py index 8408f7edf..8b7aaa0d5 100644 --- a/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py +++ b/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py @@ -19,9 +19,9 @@ from mock import call import testtools +from osc_lib.cli import format_columns from osc_lib import exceptions from osc_lib.tests import utils as tests_utils -from osc_lib import utils from neutronclient.osc.v2.trunk import network_trunk as trunk from neutronclient.osc.v2 import utils as v2_utils @@ -50,14 +50,14 @@ class TestCreateNetworkTrunk(test_fakes.TestNeutronClientOSCV2): def get_data(self): return ( - v2_utils.format_admin_state(self._trunk['admin_state_up']), + v2_utils.AdminStateColumn(self._trunk['admin_state_up']), self._trunk['description'], self._trunk['id'], self._trunk['name'], self._trunk['port_id'], self._trunk['project_id'], self._trunk['status'], - utils.format_list_of_dicts(self._trunk['sub_ports']), + format_columns.ListDictColumn(self._trunk['sub_ports']), ) def setUp(self): @@ -97,7 +97,7 @@ def test_create_default_options(self): 'port_id': self._trunk['port_id']} }) self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) + self.assertItemEqual(self.data, data) def test_create_full_options(self): self._trunk['description'] = 'foo description' @@ -137,7 +137,7 @@ def test_create_full_options(self): 'port_id': self._trunk['port_id']} }) self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) + self.assertItemEqual(self.data, data) def test_create_trunk_with_subport_invalid_segmentation_id_fail(self): subport = self._trunk['sub_ports'][0] @@ -191,7 +191,7 @@ def test_create_network_trunk_subports_without_optional_keys(self): 'port_id': self._trunk['port_id']} }) self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) + self.assertItemEqual(self.data, data) def test_create_network_trunk_subports_without_required_key_fail(self): subport = self._trunk['sub_ports'][0] @@ -301,14 +301,14 @@ class TestShowNetworkTrunk(test_fakes.TestNeutronClientOSCV2): 'sub_ports', ) data = ( - v2_utils.format_admin_state(_trunk['admin_state_up']), + v2_utils.AdminStateColumn(_trunk['admin_state_up']), _trunk['description'], _trunk['id'], _trunk['name'], _trunk['port_id'], _trunk['project_id'], _trunk['status'], - utils.format_list_of_dicts(_trunk['sub_ports']), + format_columns.ListDictColumn(_trunk['sub_ports']), ) def setUp(self): @@ -343,7 +343,7 @@ def test_show_all_options(self): self.neutronclient.show_trunk.assert_called_once_with( self._trunk['id']) self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) + self.assertItemEqual(self.data, data) class TestListNetworkTrunk(test_fakes.TestNeutronClientOSCV2): @@ -380,7 +380,7 @@ class TestListNetworkTrunk(test_fakes.TestNeutronClientOSCV2): t['port_id'], t['description'], t['status'], - v2_utils.format_admin_state(t['admin_state_up']), + v2_utils.AdminStateColumn(t['admin_state_up']), '2001-01-01 00:00:00', '2001-01-01 00:00:00', )) @@ -404,7 +404,7 @@ def test_trunk_list_no_option(self): self.neutronclient.list_trunks.assert_called_once_with() self.assertEqual(self.columns, columns) - self.assertEqual(self.data, list(data)) + self.assertListItemEqual(self.data, list(data)) def test_trunk_list_long(self): arglist = [ @@ -419,7 +419,7 @@ def test_trunk_list_long(self): self.neutronclient.list_trunks.assert_called_once_with() self.assertEqual(self.columns_long, columns) - self.assertEqual(self.data_long, list(data)) + self.assertListItemEqual(self.data_long, list(data)) class TestSetNetworkTrunk(test_fakes.TestNeutronClientOSCV2): @@ -437,14 +437,14 @@ class TestSetNetworkTrunk(test_fakes.TestNeutronClientOSCV2): 'sub_ports', ) data = ( - v2_utils.format_admin_state(_trunk['admin_state_up']), + v2_utils.AdminStateColumn(_trunk['admin_state_up']), _trunk['id'], _trunk['name'], _trunk['description'], _trunk['port_id'], _trunk['project_id'], _trunk['status'], - utils.format_list_of_dicts(_trunk['sub_ports']), + format_columns.ListDictColumn(_trunk['sub_ports']), ) def setUp(self): @@ -717,13 +717,13 @@ class TestUnsetNetworkTrunk(test_fakes.TestNeutronClientOSCV2): 'sub_ports', ) data = ( - v2_utils.format_admin_state(_trunk['admin_state_up']), + v2_utils.AdminStateColumn(_trunk['admin_state_up']), _trunk['id'], _trunk['name'], _trunk['port_id'], _trunk['project_id'], _trunk['status'], - utils.format_list_of_dicts(_trunk['sub_ports']), + format_columns.ListDictColumn(_trunk['sub_ports']), ) def setUp(self): From 030b8ad4a78d7d1151ac10cce68297f9f1f017cf Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Tue, 16 May 2017 00:20:25 +0000 Subject: [PATCH 579/845] Updated from global requirements Change-Id: I37500788354e3d7a46002177012c592ff0e7e8da --- test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index 47b5379fa..1c80b47ee 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -3,7 +3,7 @@ # process, which may cause wedges in the gate later. hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0 -coverage>=4.0 # Apache-2.0 +coverage!=4.4,>=4.0 # Apache-2.0 fixtures>=3.0.0 # Apache-2.0/BSD mox3!=0.19.0,>=0.7.0 # Apache-2.0 mock>=2.0 # BSD From d6327431ad25a3f13ce4e19b8250af291b17bb41 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Thu, 18 May 2017 12:54:54 +0000 Subject: [PATCH 580/845] Updated from global requirements Change-Id: I42d4fb6f4fc127a327ab5d543d03c354bbed33e5 --- test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index 1c80b47ee..f8a0c38be 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -14,7 +14,7 @@ python-openstackclient!=3.10.0,>=3.3.0 # Apache-2.0 python-subunit>=0.0.18 # Apache-2.0/BSD reno>=1.8.0 # Apache-2.0 requests-mock>=1.1 # Apache-2.0 -sphinx>=1.5.1 # BSD +sphinx!=1.6.1,>=1.5.1 # BSD testrepository>=0.0.18 # Apache-2.0/BSD testtools>=1.4.0 # MIT testscenarios>=0.4 # Apache-2.0/BSD From af869a4ca6751d750ae969ac0015d4354d7da2c6 Mon Sep 17 00:00:00 2001 From: Sean Dague Date: Tue, 23 May 2017 09:51:58 -0400 Subject: [PATCH 581/845] Allow global_request_id in Client constructor This provides the facility to take global_request_id as a construction parameter for a neutron client, and pass it through the system. This will be used by Nova and others to set global_request_id so requests can be tracked across services. oslo spec I65de8261746b25d45e105394f4eeb95b9cb3bd42 Change-Id: I7ed48ad247676c71a3a7b12585572c398dda06e0 --- neutronclient/client.py | 18 ++++++++++++++++-- neutronclient/tests/unit/test_http.py | 18 ++++++++++++++++++ .../global_request_id-56856a93b982a6b3.yaml | 6 ++++++ 3 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 releasenotes/notes/global_request_id-56856a93b982a6b3.yaml diff --git a/neutronclient/client.py b/neutronclient/client.py index 2995bcea5..7c8000da4 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -46,6 +46,7 @@ logging.getLogger("requests").setLevel(_requests_log_level) MAX_URI_LEN = 8192 USER_AGENT = 'python-neutronclient' +REQ_ID_HEADER = 'X-OpenStack-Request-ID' class HTTPClient(object): @@ -64,7 +65,7 @@ def __init__(self, username=None, user_id=None, endpoint_url=None, insecure=False, endpoint_type='publicURL', auth_strategy='keystone', ca_cert=None, log_credentials=False, - service_type='network', + service_type='network', global_request_id=None, **kwargs): self.username = username @@ -83,6 +84,7 @@ def __init__(self, username=None, user_id=None, self.endpoint_url = endpoint_url self.auth_strategy = auth_strategy self.log_credentials = log_credentials + self.global_request_id = global_request_id if insecure: self.verify_cert = False else: @@ -153,6 +155,9 @@ def request(self, url, method, body=None, headers=None, **kwargs): if body: headers.setdefault('Content-Type', content_type) + if self.global_request_id: + headers.setdefault(REQ_ID_HEADER, self.global_request_id) + headers['User-Agent'] = USER_AGENT # NOTE(dbelova): osprofiler_web.get_trace_id_headers does not add any # headers in case if osprofiler is not initialized. @@ -299,6 +304,9 @@ def get_auth_ref(self): class SessionClient(adapter.Adapter): + def __init__(self, *args, **kwargs): + self.global_request_id = kwargs.pop("global_request_id", None) + super(SessionClient, self).__init__(*args, **kwargs) def request(self, *args, **kwargs): kwargs.setdefault('authenticated', False) @@ -308,6 +316,9 @@ def request(self, *args, **kwargs): headers = kwargs.setdefault('headers', {}) headers.setdefault('Accept', content_type) + + if self.global_request_id: + headers.setdefault(REQ_ID_HEADER, self.global_request_id) # NOTE(dbelova): osprofiler_web.get_trace_id_headers does not add any # headers in case if osprofiler is not initialized. if osprofiler_web: @@ -397,6 +408,7 @@ def construct_http_client(username=None, ca_cert=None, service_type='network', session=None, + global_request_id=None, **kwargs): if session: @@ -405,6 +417,7 @@ def construct_http_client(username=None, return SessionClient(session=session, service_type=service_type, region_name=region_name, + global_request_id=global_request_id, **kwargs) else: # FIXME(bklei): username and password are now optional. Need @@ -425,4 +438,5 @@ def construct_http_client(username=None, service_type=service_type, ca_cert=ca_cert, log_credentials=log_credentials, - auth_strategy=auth_strategy) + auth_strategy=auth_strategy, + global_request_id=global_request_id) diff --git a/neutronclient/tests/unit/test_http.py b/neutronclient/tests/unit/test_http.py index 09e1e7a58..a03df0756 100644 --- a/neutronclient/tests/unit/test_http.py +++ b/neutronclient/tests/unit/test_http.py @@ -14,6 +14,7 @@ # under the License. import abc +import uuid import osprofiler.profiler import osprofiler.web @@ -121,3 +122,20 @@ def test_request_forbidden_is_returned_to_caller(self): resp, resp_text = self.http._cs_request(URL, METHOD) self.assertEqual(403, resp.status_code) self.assertEqual(text, resp_text) + + +class TestHTTPClientWithReqId(TestHTTPClientMixin, testtools.TestCase): + """Tests for when global_request_id is set.""" + + def initialize(self): + self.req_id = "req-%s" % uuid.uuid4() + return client.HTTPClient(token=AUTH_TOKEN, endpoint_url=END_URL, + global_request_id=self.req_id) + + def test_request_success(self): + headers = { + 'Accept': 'application/json', + 'X-OpenStack-Request-ID': self.req_id + } + self.requests.register_uri(METHOD, URL, request_headers=headers) + self.http.request(URL, METHOD) diff --git a/releasenotes/notes/global_request_id-56856a93b982a6b3.yaml b/releasenotes/notes/global_request_id-56856a93b982a6b3.yaml new file mode 100644 index 000000000..168987613 --- /dev/null +++ b/releasenotes/notes/global_request_id-56856a93b982a6b3.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Adds a new ``global_request_id`` parameter to the Client + constructors, which will pass that id on all requests as the + ``X-OpenStack-Request-ID`` header. From feb60a9cda210c47c4a685a14eb5c336ee774cf2 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Tue, 30 May 2017 13:43:15 +0000 Subject: [PATCH 582/845] Updated from global requirements Change-Id: I71956aeabab94c6d938846e794dfe34f27f754f6 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 1e6bdaf1c..9eb8195c4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,7 +7,7 @@ debtcollector>=1.2.0 # Apache-2.0 iso8601>=0.1.11 # MIT netaddr!=0.7.16,>=0.7.13 # BSD osc-lib>=1.5.1 # Apache-2.0 -oslo.i18n>=2.1.0 # Apache-2.0 +oslo.i18n!=3.15.2,>=2.1.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 oslo.utils>=3.20.0 # Apache-2.0 os-client-config>=1.27.0 # Apache-2.0 From 41961a25fdc9e110ec51d8e78c6feb2736bebd6f Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Fri, 2 Jun 2017 22:07:10 +0000 Subject: [PATCH 583/845] Updated from global requirements Change-Id: I7eeae5666364dd24282036c0f1b981198e645d8b --- test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index f8a0c38be..67e6c2d56 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -12,7 +12,7 @@ oslotest>=1.10.0 # Apache-2.0 osprofiler>=1.4.0 # Apache-2.0 python-openstackclient!=3.10.0,>=3.3.0 # Apache-2.0 python-subunit>=0.0.18 # Apache-2.0/BSD -reno>=1.8.0 # Apache-2.0 +reno!=2.3.1,>=1.8.0 # Apache-2.0 requests-mock>=1.1 # Apache-2.0 sphinx!=1.6.1,>=1.5.1 # BSD testrepository>=0.0.18 # Apache-2.0/BSD From c579701497b7c94531ad1b439727be2fc36aa050 Mon Sep 17 00:00:00 2001 From: zhaojingjing0067370 Date: Thu, 8 Jun 2017 16:45:16 +0800 Subject: [PATCH 584/845] BGP unit test 'auth-type' errors 'auth-type' in position_names is wrong, and it should be modified to 'auth_type' for bgp peer. Change-Id: I54a7ab54500c8d3655212e81046becee241a8c18 --- neutronclient/tests/unit/bgp/test_cli20_peer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/tests/unit/bgp/test_cli20_peer.py b/neutronclient/tests/unit/bgp/test_cli20_peer.py index b16532704..c89c21b6c 100644 --- a/neutronclient/tests/unit/bgp/test_cli20_peer.py +++ b/neutronclient/tests/unit/bgp/test_cli20_peer.py @@ -148,7 +148,7 @@ def test_create_authenticated_bgp_peer_without_password(self): '--peer-ip', peerip, '--remote-as', remote_asnum, '--auth-type', authType] - position_names = ['name', 'peer_ip', 'remote_as', 'auth-type'] + position_names = ['name', 'peer_ip', 'remote_as', 'auth_type'] position_values = [name, peerip, remote_asnum, authType] exc = self.assertRaises(exceptions.CommandError, self._test_create_resource, From f4376220d380ef9e4eb7cf6d7872d519d3b71c78 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Thu, 15 Jun 2017 09:02:23 +0900 Subject: [PATCH 585/845] Call mock.patch.stopall in OSC unit tests OSC unit tests inherits osc_lib.test.utils and mock.patch.stopall (or stop) is not called anywhere. Change-Id: I1249678be7a7c823ea3824bf08e603864de023fd --- neutronclient/tests/unit/osc/v2/fakes.py | 1 + 1 file changed, 1 insertion(+) diff --git a/neutronclient/tests/unit/osc/v2/fakes.py b/neutronclient/tests/unit/osc/v2/fakes.py index db962d2e5..cc30842ab 100644 --- a/neutronclient/tests/unit/osc/v2/fakes.py +++ b/neutronclient/tests/unit/osc/v2/fakes.py @@ -26,6 +26,7 @@ def setUp(self): self.app.client_manager.session = mock.Mock() self.app.client_manager.neutronclient = mock.Mock() self.neutronclient = self.app.client_manager.neutronclient + self.addCleanup(mock.patch.stopall) # TODO(amotoki): Move this to osc_lib def assertListItemEqual(self, expected, actual): From d6d42af64ad9c124a065006296573d9be817ff80 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Thu, 15 Jun 2017 08:54:49 +0900 Subject: [PATCH 586/845] CLI implementation should raise CommandError NeutronClientException is an exception raised from API bindings and it should not be used in CLI layer. TrivialFix Change-Id: I3df4c7352c96f10388df65bd16016b3ceb221248 --- neutronclient/neutron/v2_0/quota.py | 2 +- neutronclient/tests/unit/test_quota.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/neutronclient/neutron/v2_0/quota.py b/neutronclient/neutron/v2_0/quota.py index a1144c385..6e7058d8c 100644 --- a/neutronclient/neutron/v2_0/quota.py +++ b/neutronclient/neutron/v2_0/quota.py @@ -207,7 +207,7 @@ def _validate_int(self, name, value): except Exception: message = (_('Quota limit for %(name)s must be an integer') % {'name': name}) - raise exceptions.NeutronClientException(message=message) + raise exceptions.CommandError(message=message) return return_value def args2body(self, parsed_args): diff --git a/neutronclient/tests/unit/test_quota.py b/neutronclient/tests/unit/test_quota.py index 78f4daefe..d4bc4aac9 100644 --- a/neutronclient/tests/unit/test_quota.py +++ b/neutronclient/tests/unit/test_quota.py @@ -37,7 +37,7 @@ def test_update_quota(self): test_cli20.MyApp(sys.stdout), None) args = ['--tenant-id', self.test_id, '--network', 'test'] self.assertRaises( - exceptions.NeutronClientException, self._test_update_resource, + exceptions.CommandError, self._test_update_resource, resource, cmd, self.test_id, args=args, extrafields={'network': 'new'}) @@ -58,7 +58,7 @@ def test_update_quota_positional(self): test_cli20.MyApp(sys.stdout), None) args = [self.test_id, '--network', 'test'] self.assertRaises( - exceptions.NeutronClientException, self._test_update_resource, + exceptions.CommandError, self._test_update_resource, resource, cmd, self.test_id, args=args, extrafields={'network': 'new'}) From 13ac0901d227f7259a0cef5d225ec19f50c2d8ca Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Thu, 15 Jun 2017 15:33:41 +0000 Subject: [PATCH 587/845] Updated from global requirements Change-Id: Ib4525d1804cb1d05066d5ddf074ab3d59a951d6b --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 9eb8195c4..617c23524 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,11 +11,11 @@ oslo.i18n!=3.15.2,>=2.1.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 oslo.utils>=3.20.0 # Apache-2.0 os-client-config>=1.27.0 # Apache-2.0 -keystoneauth1>=2.20.0 # Apache-2.0 +keystoneauth1>=2.21.0 # Apache-2.0 # keystoneclient is used only by neutronclient.osc.utils # TODO(amotoki): Drop this after osc.utils has no dependency on keystoneclient python-keystoneclient>=3.8.0 # Apache-2.0 -requests!=2.12.2,!=2.13.0,>=2.10.0 # Apache-2.0 +requests>=2.14.2 # Apache-2.0 simplejson>=2.2.0 # MIT six>=1.9.0 # MIT Babel!=2.4.0,>=2.3.4 # BSD From 06ba42ce5307b9969196e24be90172b82831b105 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Sun, 18 Jun 2017 03:11:30 +0900 Subject: [PATCH 588/845] switch to openstackdocstheme Change-Id: Ic8f9b6821da7958a895dad6053d2cd1b6761a405 --- doc/source/conf.py | 22 ++++++++++++++++++++-- releasenotes/source/conf.py | 20 +++++++++++++++++--- test-requirements.txt | 2 +- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index bc6dad770..0e4f7253a 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -1,6 +1,11 @@ # -*- coding: utf-8 -*- # +import os + +import openstackdocstheme + + project = 'python-neutronclient' # -- General configuration --------------------------------------------- @@ -8,7 +13,6 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', - 'oslosphinx', 'reno.sphinxext', ] @@ -38,7 +42,21 @@ # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. -# html_theme = 'nature' +html_theme = 'openstackdocs' + +html_theme_path = [openstackdocstheme.get_html_theme_path()] + +gitsha = os.popen("/usr/bin/git rev-parse HEAD").read() +giturl = ('https://git.openstack.org/cgit/openstack/%s/tree/doc/source' + % 'python-neutronclient') +html_context = { + 'gitsha': gitsha, + 'giturl': giturl, + 'bug_project': 'python-neutronclient', + 'bug_tag': 'doc', +} +html_last_updated_fmt = os.popen("git log --pretty=format:'%ad' " + "--date=format:'%Y-%m-%d %H:%M' -n1").read() # Output file base name for HTML help builder. htmlhelp_basename = '%sdoc' % project diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py index 4e72a7b68..f635d2e05 100644 --- a/releasenotes/source/conf.py +++ b/releasenotes/source/conf.py @@ -29,6 +29,10 @@ # documentation root, use os.path.abspath to make it absolute, like shown here. # sys.path.insert(0, os.path.abspath('.')) +import os + +import openstackdocstheme + # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. @@ -38,7 +42,6 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'oslosphinx', 'reno.sphinxext', ] @@ -112,7 +115,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'default' +html_theme = 'openstackdocs' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -120,7 +123,7 @@ # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -# html_theme_path = [] +html_theme_path = [openstackdocstheme.get_html_theme_path()] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". @@ -192,6 +195,17 @@ # Output file base name for HTML help builder. htmlhelp_basename = 'NeutronReleaseNotesdoc' +gitsha = os.popen("/usr/bin/git rev-parse HEAD").read() +giturl = ('https://git.openstack.org/cgit/openstack/%s/tree/doc/source' + % 'python-neutronclient') +html_context = { + 'gitsha': gitsha, + 'giturl': giturl, + 'bug_project': 'python-neutronclient', + 'bug_tag': 'doc', +} +html_last_updated_fmt = os.popen("git log --pretty=format:'%ad' " + "--date=format:'%Y-%m-%d %H:%M' -n1").read() # -- Options for LaTeX output --------------------------------------------- diff --git a/test-requirements.txt b/test-requirements.txt index 67e6c2d56..680578617 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -7,7 +7,7 @@ coverage!=4.4,>=4.0 # Apache-2.0 fixtures>=3.0.0 # Apache-2.0/BSD mox3!=0.19.0,>=0.7.0 # Apache-2.0 mock>=2.0 # BSD -oslosphinx>=4.7.0 # Apache-2.0 +openstackdocstheme>=1.5.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 osprofiler>=1.4.0 # Apache-2.0 python-openstackclient!=3.10.0,>=3.3.0 # Apache-2.0 From 8cbc51cda465d7b0f1d860cce174aa3ed5340f2b Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Sun, 18 Jun 2017 03:23:46 +0900 Subject: [PATCH 589/845] move existing content into the new standard structure This patch rearranges and reformats existing content. Change-Id: Ibcad865d2ae45696628f77b5bd5f0e6b1f6842cf --- doc/source/cli/index.rst | 45 +++++++++++++++ doc/source/{usage/cli.rst => cli/neutron.rst} | 4 +- .../{usage => cli}/osc/v2/firewall-group.rst | 0 .../{usage => cli}/osc/v2/firewall-policy.rst | 0 .../{usage => cli}/osc/v2/firewall-rule.rst | 0 .../{usage => cli}/osc/v2/network-trunk.rst | 0 .../osc/v2/networking-bgpvpn.rst | 0 .../osc_plugins.rst} | 0 .../cli_option_guideline.rst | 0 .../client_command_extensions.rst | 0 doc/source/contributor/index.rst | 43 ++++++++++++++ .../transition_to_osc.rst | 0 doc/source/index.rst | 57 +++++++++++-------- .../library.rst => reference/index.rst} | 0 14 files changed, 122 insertions(+), 27 deletions(-) create mode 100644 doc/source/cli/index.rst rename doc/source/{usage/cli.rst => cli/neutron.rst} (99%) rename doc/source/{usage => cli}/osc/v2/firewall-group.rst (100%) rename doc/source/{usage => cli}/osc/v2/firewall-policy.rst (100%) rename doc/source/{usage => cli}/osc/v2/firewall-rule.rst (100%) rename doc/source/{usage => cli}/osc/v2/network-trunk.rst (100%) rename doc/source/{usage => cli}/osc/v2/networking-bgpvpn.rst (100%) rename doc/source/{usage/osc_cli_plugins.rst => cli/osc_plugins.rst} (100%) rename doc/source/{devref => contributor}/cli_option_guideline.rst (100%) rename doc/source/{devref => contributor}/client_command_extensions.rst (100%) create mode 100644 doc/source/contributor/index.rst rename doc/source/{devref => contributor}/transition_to_osc.rst (100%) rename doc/source/{usage/library.rst => reference/index.rst} (100%) diff --git a/doc/source/cli/index.rst b/doc/source/cli/index.rst new file mode 100644 index 000000000..c6efb1f4d --- /dev/null +++ b/doc/source/cli/index.rst @@ -0,0 +1,45 @@ +:orphan: + +.. This page is to provide the top page for the CLI reference. + On the other hand, it looks better that the top level document has + direct links to individual pages for better navigation. + From that reason, :orphan: is needed to silence sphinx warning. + +.. + 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. + + + Convention for heading levels in Neutron devref: + ======= Heading 0 (reserved for the title in a document) + ------- Heading 1 + ~~~~~~~ Heading 2 + +++++++ Heading 3 + ''''''' Heading 4 + (Avoid deeper levels because they do not render well.) + +============= +CLI reference +============= + +There are two CLIs which support the Networking API: +:doc:`neutron CLI ` and +`OpenStack Client (OSC) `__. +OpenStack Client provides the basic network commands and +python-neutronclient provides :doc:`extensions ` (aka OSC plugins) +for advanced networking services. + +.. toctree:: + :maxdepth: 2 + + neutron CLI + Network extensions to OpenStack Client diff --git a/doc/source/usage/cli.rst b/doc/source/cli/neutron.rst similarity index 99% rename from doc/source/usage/cli.rst rename to doc/source/cli/neutron.rst index 4471ac35d..23ceadd23 100644 --- a/doc/source/usage/cli.rst +++ b/doc/source/cli/neutron.rst @@ -20,8 +20,8 @@ ''''''' Heading 4 (Avoid deeper levels because they do not render well.) -Command-line Interface -====================== +Using neutron CLI +================= The **neutron** shell utility interacts with OpenStack Networking API from the command-line. It supports the entire features of OpenStack Networking API. diff --git a/doc/source/usage/osc/v2/firewall-group.rst b/doc/source/cli/osc/v2/firewall-group.rst similarity index 100% rename from doc/source/usage/osc/v2/firewall-group.rst rename to doc/source/cli/osc/v2/firewall-group.rst diff --git a/doc/source/usage/osc/v2/firewall-policy.rst b/doc/source/cli/osc/v2/firewall-policy.rst similarity index 100% rename from doc/source/usage/osc/v2/firewall-policy.rst rename to doc/source/cli/osc/v2/firewall-policy.rst diff --git a/doc/source/usage/osc/v2/firewall-rule.rst b/doc/source/cli/osc/v2/firewall-rule.rst similarity index 100% rename from doc/source/usage/osc/v2/firewall-rule.rst rename to doc/source/cli/osc/v2/firewall-rule.rst diff --git a/doc/source/usage/osc/v2/network-trunk.rst b/doc/source/cli/osc/v2/network-trunk.rst similarity index 100% rename from doc/source/usage/osc/v2/network-trunk.rst rename to doc/source/cli/osc/v2/network-trunk.rst diff --git a/doc/source/usage/osc/v2/networking-bgpvpn.rst b/doc/source/cli/osc/v2/networking-bgpvpn.rst similarity index 100% rename from doc/source/usage/osc/v2/networking-bgpvpn.rst rename to doc/source/cli/osc/v2/networking-bgpvpn.rst diff --git a/doc/source/usage/osc_cli_plugins.rst b/doc/source/cli/osc_plugins.rst similarity index 100% rename from doc/source/usage/osc_cli_plugins.rst rename to doc/source/cli/osc_plugins.rst diff --git a/doc/source/devref/cli_option_guideline.rst b/doc/source/contributor/cli_option_guideline.rst similarity index 100% rename from doc/source/devref/cli_option_guideline.rst rename to doc/source/contributor/cli_option_guideline.rst diff --git a/doc/source/devref/client_command_extensions.rst b/doc/source/contributor/client_command_extensions.rst similarity index 100% rename from doc/source/devref/client_command_extensions.rst rename to doc/source/contributor/client_command_extensions.rst diff --git a/doc/source/contributor/index.rst b/doc/source/contributor/index.rst new file mode 100644 index 000000000..ddbd7e8a4 --- /dev/null +++ b/doc/source/contributor/index.rst @@ -0,0 +1,43 @@ +:orphan: + +.. This page is to provide the top page for the contributor guide. + On the other hand, it looks better that the top level document has + direct links to individual pages for better navigation. + From that reason, :orphan: is needed to silence sphinx warning. + +.. + 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. + + + Convention for heading levels in Neutron devref: + ======= Heading 0 (reserved for the title in a document) + ------- Heading 1 + ~~~~~~~ Heading 2 + +++++++ Heading 3 + ''''''' Heading 4 + (Avoid deeper levels because they do not render well.) + +================= +Contributor Guide +================= + +In the Contributor Guide, you will find information on neutronclient's +lower level programming details or APIs as well as the transition to +OpenStack client. + +.. toctree:: + :maxdepth: 2 + + client_command_extensions + cli_option_guideline + transition_to_osc diff --git a/doc/source/devref/transition_to_osc.rst b/doc/source/contributor/transition_to_osc.rst similarity index 100% rename from doc/source/devref/transition_to_osc.rst rename to doc/source/contributor/transition_to_osc.rst diff --git a/doc/source/index.rst b/doc/source/index.rst index 8da1b7659..bde3f3c43 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -19,45 +19,52 @@ ''''''' Heading 4 (Avoid deeper levels because they do not render well.) -Python bindings to the OpenStack Networking API -=============================================== +================================== +python-neutronclient documentation +================================== -This is a client for OpenStack Networking API. There is a :doc:`Python API -` (the neutronclient module), and a :doc:`command-line script -` (installed as **neutron**). Each implements the entire OpenStack -Networking API. +This is a client for OpenStack Networking API. It provides +:doc:`Python API bindings ` (the neutronclient module) and +:doc:`command-line interface (CLI) `. -Using neutronclient -------------------- +There are two CLIs which support the Networking API: +:doc:`neutron CLI ` and +`OpenStack Client (OSC) `__. +OpenStack Client provides the basic network commands and +python-neutronclient provides extensions (aka OSC plugins) +for advanced networking services. + +User Documentation +------------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 + + reference/index + cli/neutron + cli/osc_plugins - usage/cli - usage/library - usage/osc_cli_plugins +Contributor Guide +----------------- -Developer Guide ---------------- +In the :doc:`Contributor Guide `, you will find +information on neutronclient's lower level programming details or APIs +as well as the transition to OpenStack client. + +.. toctree:: + :maxdepth: 1 -In the Developer Guide, you will find information on Neutron’s client -lower level programming details or APIs as well as the transition to -OpenStack client. + contributor/client_command_extensions + contributor/cli_option_guideline + contributor/transition_to_osc -.. note: +.. note:: neutron CLI has been deprecated from Ocata release. We do not add, change and drop any existing commands any more. We only accept changes on OSC plugin, neutronclient python bindings and bug fixes on the deprecated CLI (``neutron`` command). -.. toctree:: - :maxdepth: 2 - - devref/client_command_extensions - devref/cli_option_guideline - devref/transition_to_osc - History ------- diff --git a/doc/source/usage/library.rst b/doc/source/reference/index.rst similarity index 100% rename from doc/source/usage/library.rst rename to doc/source/reference/index.rst From 24f508fba75cdf1165a2d7323968c1ff74e117e3 Mon Sep 17 00:00:00 2001 From: Sean Dague Date: Tue, 13 Jun 2017 12:17:45 -0400 Subject: [PATCH 590/845] remove explicit global_request_id handling from session client The neutron client that inherits from keystonauth1 can now use the built in 2.21.0 global_request_id handling. This just cleans up some code in the process. Change-Id: I6df5c98ebf447d3e3783ef23b4ef4d8e8af94c9b --- neutronclient/client.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/neutronclient/client.py b/neutronclient/client.py index 7c8000da4..395f7dbe5 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -304,9 +304,6 @@ def get_auth_ref(self): class SessionClient(adapter.Adapter): - def __init__(self, *args, **kwargs): - self.global_request_id = kwargs.pop("global_request_id", None) - super(SessionClient, self).__init__(*args, **kwargs) def request(self, *args, **kwargs): kwargs.setdefault('authenticated', False) @@ -317,8 +314,6 @@ def request(self, *args, **kwargs): headers = kwargs.setdefault('headers', {}) headers.setdefault('Accept', content_type) - if self.global_request_id: - headers.setdefault(REQ_ID_HEADER, self.global_request_id) # NOTE(dbelova): osprofiler_web.get_trace_id_headers does not add any # headers in case if osprofiler is not initialized. if osprofiler_web: From cee7b2d4fbc9aef645157a6a5efdb44cb6bcd2a7 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Tue, 27 Jun 2017 12:22:04 +0000 Subject: [PATCH 591/845] Updated from global requirements Change-Id: Ib08574e2413af218f70af0e56b08286256ccb79b --- test-requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test-requirements.txt b/test-requirements.txt index 680578617..85ccf056b 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -7,14 +7,14 @@ coverage!=4.4,>=4.0 # Apache-2.0 fixtures>=3.0.0 # Apache-2.0/BSD mox3!=0.19.0,>=0.7.0 # Apache-2.0 mock>=2.0 # BSD -openstackdocstheme>=1.5.0 # Apache-2.0 +openstackdocstheme>=1.11.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 osprofiler>=1.4.0 # Apache-2.0 python-openstackclient!=3.10.0,>=3.3.0 # Apache-2.0 python-subunit>=0.0.18 # Apache-2.0/BSD reno!=2.3.1,>=1.8.0 # Apache-2.0 requests-mock>=1.1 # Apache-2.0 -sphinx!=1.6.1,>=1.5.1 # BSD +sphinx>=1.6.2 # BSD testrepository>=0.0.18 # Apache-2.0/BSD testtools>=1.4.0 # MIT testscenarios>=0.4 # Apache-2.0/BSD From 7790167f6f9f128a973059e55a5552b7b172cd02 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Fri, 30 Jun 2017 15:40:19 +0900 Subject: [PATCH 592/845] doc: use new config options of openstackdocstheme The new simple configuration way was introduced in openstackdocstheme 1.11.0. This commit introduces the new way. Also drops unnecessary sphinx configurations like latex, texinfo and manpages. Change-Id: I587eb8cbd3e64c65dfc7247a01eca90af6adad4e --- doc/source/conf.py | 41 +++----------- releasenotes/source/conf.py | 107 +++--------------------------------- 2 files changed, 16 insertions(+), 132 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index 0e4f7253a..4043a8266 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -1,21 +1,21 @@ # -*- coding: utf-8 -*- # -import os - -import openstackdocstheme - - -project = 'python-neutronclient' - # -- General configuration --------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'reno.sphinxext', + 'openstackdocstheme', ] +# openstackdocstheme options +repository_name = 'openstack/python-neutronclient' +bug_project = 'python-neutronclient' +bug_tag = 'doc' +html_last_updated_fmt = '%Y-%m-%d %H:%M' + # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -44,30 +44,5 @@ # Sphinx are currently 'default' and 'sphinxdoc'. html_theme = 'openstackdocs' -html_theme_path = [openstackdocstheme.get_html_theme_path()] - -gitsha = os.popen("/usr/bin/git rev-parse HEAD").read() -giturl = ('https://git.openstack.org/cgit/openstack/%s/tree/doc/source' - % 'python-neutronclient') -html_context = { - 'gitsha': gitsha, - 'giturl': giturl, - 'bug_project': 'python-neutronclient', - 'bug_tag': 'doc', -} -html_last_updated_fmt = os.popen("git log --pretty=format:'%ad' " - "--date=format:'%Y-%m-%d %H:%M' -n1").read() - # Output file base name for HTML help builder. -htmlhelp_basename = '%sdoc' % project - - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, -# documentclass [howto/manual]). -latex_documents = [ - ('index', - '%s.tex' % project, - u'%s Documentation' % project, - u'OpenStack Foundation', 'manual'), -] +htmlhelp_basename = 'neutronclientdoc' diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py index f635d2e05..b955f990e 100644 --- a/releasenotes/source/conf.py +++ b/releasenotes/source/conf.py @@ -29,10 +29,6 @@ # documentation root, use os.path.abspath to make it absolute, like shown here. # sys.path.insert(0, os.path.abspath('.')) -import os - -import openstackdocstheme - # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. @@ -43,8 +39,15 @@ # ones. extensions = [ 'reno.sphinxext', + 'openstackdocstheme', ] +# openstackdocstheme options +repository_name = 'openstack/python-neutronclient' +bug_project = 'python-neutronclient' +bug_tag = 'doc' +html_last_updated_fmt = '%Y-%m-%d %H:%M' + # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -123,7 +126,7 @@ # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -html_theme_path = [openstackdocstheme.get_html_theme_path()] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". @@ -195,99 +198,5 @@ # Output file base name for HTML help builder. htmlhelp_basename = 'NeutronReleaseNotesdoc' -gitsha = os.popen("/usr/bin/git rev-parse HEAD").read() -giturl = ('https://git.openstack.org/cgit/openstack/%s/tree/doc/source' - % 'python-neutronclient') -html_context = { - 'gitsha': gitsha, - 'giturl': giturl, - 'bug_project': 'python-neutronclient', - 'bug_tag': 'doc', -} -html_last_updated_fmt = os.popen("git log --pretty=format:'%ad' " - "--date=format:'%Y-%m-%d %H:%M' -n1").read() - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # 'preamble': '', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - ('index', 'NeutronClientReleaseNotes.tex', - u'Neutron Client Release Notes Documentation', - u'Neutron Developers', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -# latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -# latex_use_parts = False - -# If true, show page references after internal links. -# latex_show_pagerefs = False - -# If true, show URL addresses after external links. -# latex_show_urls = False - -# Documents to append as an appendix to all manuals. -# latex_appendices = [] - -# If false, no module index is generated. -# latex_domain_indices = True - - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'neutronclientreleasenotes', - u'Neutron Client Release Notes Documentation', - [u'Neutron Developers'], 1) -] - -# If true, show URL addresses after external links. -# man_show_urls = False - - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ('index', 'NeutronClientReleaseNotes', - u'Neutron Client Release Notes Documentation', - u'Neutron Developers', 'NeutronClientReleaseNotes', - 'One line description of project.', - 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -# texinfo_appendices = [] - -# If false, no module index is generated. -# texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -# texinfo_show_urls = 'footnote' - -# If true, do not generate a @detailmenu in the "Top" node's menu. -# texinfo_no_detailmenu = False - # -- Options for Internationalization output ------------------------------ locale_dirs = ['locale/'] From 6295f85ab2a4a114f08743390c213bd6055b03f5 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Thu, 1 Jun 2017 02:54:40 +0000 Subject: [PATCH 593/845] Use entry points to define neutron CLI commands It allows us to generate CLI references automatically using cliff.sphinxext (which will be available in the next release of cliff). This allows us to avoid maintaining the in-tree CLI refernece manually. Change-Id: I3950ed8cf50508210c8b3270724e283aea8e1673 --- neutronclient/shell.py | 381 +----------------- .../tests/unit/test_client_extension.py | 29 +- setup.cfg | 285 +++++++++++++ 3 files changed, 313 insertions(+), 382 deletions(-) diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 63d1cd8a7..017d642c1 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -40,61 +40,15 @@ from neutronclient.common import clientmanager from neutronclient.common import exceptions as exc from neutronclient.common import extension as client_extension -from neutronclient.neutron.v2_0 import address_scope -from neutronclient.neutron.v2_0 import agent -from neutronclient.neutron.v2_0 import agentscheduler -from neutronclient.neutron.v2_0 import auto_allocated_topology -from neutronclient.neutron.v2_0 import availability_zone -from neutronclient.neutron.v2_0.bgp import dragentscheduler as bgp_drsched -from neutronclient.neutron.v2_0.bgp import peer as bgp_peer -from neutronclient.neutron.v2_0.bgp import speaker as bgp_speaker -from neutronclient.neutron.v2_0 import extension -from neutronclient.neutron.v2_0.flavor import flavor -from neutronclient.neutron.v2_0.flavor import flavor_profile -from neutronclient.neutron.v2_0 import floatingip -from neutronclient.neutron.v2_0.fw import firewall -from neutronclient.neutron.v2_0.fw import firewallpolicy -from neutronclient.neutron.v2_0.fw import firewallrule -from neutronclient.neutron.v2_0.lb import healthmonitor as lb_healthmonitor -from neutronclient.neutron.v2_0.lb import member as lb_member -from neutronclient.neutron.v2_0.lb import pool as lb_pool -from neutronclient.neutron.v2_0.lb.v2 import healthmonitor as lbaas_healthmon -from neutronclient.neutron.v2_0.lb.v2 import l7policy as lbaas_l7policy -from neutronclient.neutron.v2_0.lb.v2 import l7rule as lbaas_l7rule -from neutronclient.neutron.v2_0.lb.v2 import listener as lbaas_listener -from neutronclient.neutron.v2_0.lb.v2 import loadbalancer as lbaas_loadbalancer -from neutronclient.neutron.v2_0.lb.v2 import member as lbaas_member -from neutronclient.neutron.v2_0.lb.v2 import pool as lbaas_pool -from neutronclient.neutron.v2_0.lb import vip as lb_vip -from neutronclient.neutron.v2_0 import metering -from neutronclient.neutron.v2_0 import network -from neutronclient.neutron.v2_0 import network_ip_availability -from neutronclient.neutron.v2_0 import port -from neutronclient.neutron.v2_0 import purge -from neutronclient.neutron.v2_0.qos import bandwidth_limit_rule -from neutronclient.neutron.v2_0.qos import dscp_marking_rule -from neutronclient.neutron.v2_0.qos import minimum_bandwidth_rule -from neutronclient.neutron.v2_0.qos import policy as qos_policy -from neutronclient.neutron.v2_0.qos import rule as qos_rule -from neutronclient.neutron.v2_0 import quota -from neutronclient.neutron.v2_0 import rbac -from neutronclient.neutron.v2_0 import router -from neutronclient.neutron.v2_0 import securitygroup -from neutronclient.neutron.v2_0 import servicetype from neutronclient.neutron.v2_0 import subnet -from neutronclient.neutron.v2_0 import subnetpool -from neutronclient.neutron.v2_0 import tag -from neutronclient.neutron.v2_0.vpn import endpoint_group -from neutronclient.neutron.v2_0.vpn import ikepolicy -from neutronclient.neutron.v2_0.vpn import ipsec_site_connection -from neutronclient.neutron.v2_0.vpn import ipsecpolicy -from neutronclient.neutron.v2_0.vpn import vpnservice from neutronclient.version import __version__ VERSION = '2.0' NEUTRON_API_VERSION = '2.0' +NAMESPACE_MAP = {NEUTRON_API_VERSION: 'neutron.cli.v2'} + def run_command(cmd, cmd_parser, sub_argv): _argv = sub_argv @@ -156,320 +110,6 @@ def take_action(self, parsed_args): pass -COMMAND_V2 = { - 'bash-completion': BashCompletionCommand, - 'net-list': network.ListNetwork, - 'net-external-list': network.ListExternalNetwork, - 'net-show': network.ShowNetwork, - 'net-create': network.CreateNetwork, - 'net-delete': network.DeleteNetwork, - 'net-update': network.UpdateNetwork, - 'subnet-list': subnet.ListSubnet, - 'subnet-show': subnet.ShowSubnet, - 'subnet-create': subnet.CreateSubnet, - 'subnet-delete': subnet.DeleteSubnet, - 'subnet-update': subnet.UpdateSubnet, - 'subnetpool-list': subnetpool.ListSubnetPool, - 'subnetpool-show': subnetpool.ShowSubnetPool, - 'subnetpool-create': subnetpool.CreateSubnetPool, - 'subnetpool-delete': subnetpool.DeleteSubnetPool, - 'subnetpool-update': subnetpool.UpdateSubnetPool, - 'port-list': port.ListPort, - 'port-show': port.ShowPort, - 'port-create': port.CreatePort, - 'port-delete': port.DeletePort, - 'port-update': port.UpdatePort, - 'purge': purge.Purge, - 'quota-list': quota.ListQuota, - 'quota-show': quota.ShowQuota, - 'quota-default-show': quota.ShowQuotaDefault, - 'quota-delete': quota.DeleteQuota, - 'quota-update': quota.UpdateQuota, - 'ext-list': extension.ListExt, - 'ext-show': extension.ShowExt, - 'router-list': router.ListRouter, - 'router-port-list': port.ListRouterPort, - 'router-show': router.ShowRouter, - 'router-create': router.CreateRouter, - 'router-delete': router.DeleteRouter, - 'router-update': router.UpdateRouter, - 'router-interface-add': router.AddInterfaceRouter, - 'router-interface-delete': router.RemoveInterfaceRouter, - 'router-gateway-set': router.SetGatewayRouter, - 'router-gateway-clear': router.RemoveGatewayRouter, - 'floatingip-list': floatingip.ListFloatingIP, - 'floatingip-show': floatingip.ShowFloatingIP, - 'floatingip-create': floatingip.CreateFloatingIP, - 'floatingip-delete': floatingip.DeleteFloatingIP, - 'floatingip-associate': floatingip.AssociateFloatingIP, - 'floatingip-disassociate': floatingip.DisassociateFloatingIP, - 'security-group-list': securitygroup.ListSecurityGroup, - 'security-group-show': securitygroup.ShowSecurityGroup, - 'security-group-create': securitygroup.CreateSecurityGroup, - 'security-group-delete': securitygroup.DeleteSecurityGroup, - 'security-group-update': securitygroup.UpdateSecurityGroup, - 'security-group-rule-list': securitygroup.ListSecurityGroupRule, - 'security-group-rule-show': securitygroup.ShowSecurityGroupRule, - 'security-group-rule-create': securitygroup.CreateSecurityGroupRule, - 'security-group-rule-delete': securitygroup.DeleteSecurityGroupRule, - 'lbaas-loadbalancer-list': lbaas_loadbalancer.ListLoadBalancer, - 'lbaas-loadbalancer-show': lbaas_loadbalancer.ShowLoadBalancer, - 'lbaas-loadbalancer-create': lbaas_loadbalancer.CreateLoadBalancer, - 'lbaas-loadbalancer-update': lbaas_loadbalancer.UpdateLoadBalancer, - 'lbaas-loadbalancer-delete': lbaas_loadbalancer.DeleteLoadBalancer, - 'lbaas-loadbalancer-stats': lbaas_loadbalancer.RetrieveLoadBalancerStats, - 'lbaas-loadbalancer-status': lbaas_loadbalancer.RetrieveLoadBalancerStatus, - 'lbaas-listener-list': lbaas_listener.ListListener, - 'lbaas-listener-show': lbaas_listener.ShowListener, - 'lbaas-listener-create': lbaas_listener.CreateListener, - 'lbaas-listener-update': lbaas_listener.UpdateListener, - 'lbaas-listener-delete': lbaas_listener.DeleteListener, - 'lbaas-l7policy-list': lbaas_l7policy.ListL7Policy, - 'lbaas-l7policy-show': lbaas_l7policy.ShowL7Policy, - 'lbaas-l7policy-create': lbaas_l7policy.CreateL7Policy, - 'lbaas-l7policy-update': lbaas_l7policy.UpdateL7Policy, - 'lbaas-l7policy-delete': lbaas_l7policy.DeleteL7Policy, - 'lbaas-l7rule-list': lbaas_l7rule.ListL7Rule, - 'lbaas-l7rule-show': lbaas_l7rule.ShowL7Rule, - 'lbaas-l7rule-create': lbaas_l7rule.CreateL7Rule, - 'lbaas-l7rule-update': lbaas_l7rule.UpdateL7Rule, - 'lbaas-l7rule-delete': lbaas_l7rule.DeleteL7Rule, - 'lbaas-pool-list': lbaas_pool.ListPool, - 'lbaas-pool-show': lbaas_pool.ShowPool, - 'lbaas-pool-create': lbaas_pool.CreatePool, - 'lbaas-pool-update': lbaas_pool.UpdatePool, - 'lbaas-pool-delete': lbaas_pool.DeletePool, - 'lbaas-healthmonitor-list': lbaas_healthmon.ListHealthMonitor, - 'lbaas-healthmonitor-show': lbaas_healthmon.ShowHealthMonitor, - 'lbaas-healthmonitor-create': lbaas_healthmon.CreateHealthMonitor, - 'lbaas-healthmonitor-update': lbaas_healthmon.UpdateHealthMonitor, - 'lbaas-healthmonitor-delete': lbaas_healthmon.DeleteHealthMonitor, - 'lbaas-member-list': lbaas_member.ListMember, - 'lbaas-member-show': lbaas_member.ShowMember, - 'lbaas-member-create': lbaas_member.CreateMember, - 'lbaas-member-update': lbaas_member.UpdateMember, - 'lbaas-member-delete': lbaas_member.DeleteMember, - 'lb-vip-list': lb_vip.ListVip, - 'lb-vip-show': lb_vip.ShowVip, - 'lb-vip-create': lb_vip.CreateVip, - 'lb-vip-update': lb_vip.UpdateVip, - 'lb-vip-delete': lb_vip.DeleteVip, - 'lb-pool-list': lb_pool.ListPool, - 'lb-pool-show': lb_pool.ShowPool, - 'lb-pool-create': lb_pool.CreatePool, - 'lb-pool-update': lb_pool.UpdatePool, - 'lb-pool-delete': lb_pool.DeletePool, - 'lb-pool-stats': lb_pool.RetrievePoolStats, - 'lb-member-list': lb_member.ListMember, - 'lb-member-show': lb_member.ShowMember, - 'lb-member-create': lb_member.CreateMember, - 'lb-member-update': lb_member.UpdateMember, - 'lb-member-delete': lb_member.DeleteMember, - 'lb-healthmonitor-list': lb_healthmonitor.ListHealthMonitor, - 'lb-healthmonitor-show': lb_healthmonitor.ShowHealthMonitor, - 'lb-healthmonitor-create': lb_healthmonitor.CreateHealthMonitor, - 'lb-healthmonitor-update': lb_healthmonitor.UpdateHealthMonitor, - 'lb-healthmonitor-delete': lb_healthmonitor.DeleteHealthMonitor, - 'lb-healthmonitor-associate': lb_healthmonitor.AssociateHealthMonitor, - 'lb-healthmonitor-disassociate': ( - lb_healthmonitor.DisassociateHealthMonitor - ), - 'agent-list': agent.ListAgent, - 'agent-show': agent.ShowAgent, - 'agent-delete': agent.DeleteAgent, - 'agent-update': agent.UpdateAgent, - 'dhcp-agent-network-add': agentscheduler.AddNetworkToDhcpAgent, - 'dhcp-agent-network-remove': agentscheduler.RemoveNetworkFromDhcpAgent, - 'net-list-on-dhcp-agent': agentscheduler.ListNetworksOnDhcpAgent, - 'dhcp-agent-list-hosting-net': agentscheduler.ListDhcpAgentsHostingNetwork, - 'l3-agent-router-add': agentscheduler.AddRouterToL3Agent, - 'l3-agent-router-remove': agentscheduler.RemoveRouterFromL3Agent, - 'router-list-on-l3-agent': agentscheduler.ListRoutersOnL3Agent, - 'l3-agent-list-hosting-router': agentscheduler.ListL3AgentsHostingRouter, - 'lb-pool-list-on-agent': agentscheduler.ListPoolsOnLbaasAgent, - 'lb-agent-hosting-pool': agentscheduler.GetLbaasAgentHostingPool, - 'lbaas-loadbalancer-list-on-agent': - agentscheduler.ListLoadBalancersOnLbaasAgent, - 'lbaas-agent-hosting-loadbalancer': - agentscheduler.GetLbaasAgentHostingLoadBalancer, - 'service-provider-list': servicetype.ListServiceProvider, - 'firewall-rule-list': firewallrule.ListFirewallRule, - 'firewall-rule-show': firewallrule.ShowFirewallRule, - 'firewall-rule-create': firewallrule.CreateFirewallRule, - 'firewall-rule-update': firewallrule.UpdateFirewallRule, - 'firewall-rule-delete': firewallrule.DeleteFirewallRule, - 'firewall-policy-list': firewallpolicy.ListFirewallPolicy, - 'firewall-policy-show': firewallpolicy.ShowFirewallPolicy, - 'firewall-policy-create': firewallpolicy.CreateFirewallPolicy, - 'firewall-policy-update': firewallpolicy.UpdateFirewallPolicy, - 'firewall-policy-delete': firewallpolicy.DeleteFirewallPolicy, - 'firewall-policy-insert-rule': firewallpolicy.FirewallPolicyInsertRule, - 'firewall-policy-remove-rule': firewallpolicy.FirewallPolicyRemoveRule, - 'firewall-list': firewall.ListFirewall, - 'firewall-show': firewall.ShowFirewall, - 'firewall-create': firewall.CreateFirewall, - 'firewall-update': firewall.UpdateFirewall, - 'firewall-delete': firewall.DeleteFirewall, - 'ipsec-site-connection-list': ( - ipsec_site_connection.ListIPsecSiteConnection - ), - 'ipsec-site-connection-show': ( - ipsec_site_connection.ShowIPsecSiteConnection - ), - 'ipsec-site-connection-create': ( - ipsec_site_connection.CreateIPsecSiteConnection - ), - 'ipsec-site-connection-update': ( - ipsec_site_connection.UpdateIPsecSiteConnection - ), - 'ipsec-site-connection-delete': ( - ipsec_site_connection.DeleteIPsecSiteConnection - ), - 'vpn-endpoint-group-list': endpoint_group.ListEndpointGroup, - 'vpn-endpoint-group-show': endpoint_group.ShowEndpointGroup, - 'vpn-endpoint-group-create': endpoint_group.CreateEndpointGroup, - 'vpn-endpoint-group-update': endpoint_group.UpdateEndpointGroup, - 'vpn-endpoint-group-delete': endpoint_group.DeleteEndpointGroup, - 'vpn-service-list': vpnservice.ListVPNService, - 'vpn-service-show': vpnservice.ShowVPNService, - 'vpn-service-create': vpnservice.CreateVPNService, - 'vpn-service-update': vpnservice.UpdateVPNService, - 'vpn-service-delete': vpnservice.DeleteVPNService, - 'vpn-ipsecpolicy-list': ipsecpolicy.ListIPsecPolicy, - 'vpn-ipsecpolicy-show': ipsecpolicy.ShowIPsecPolicy, - 'vpn-ipsecpolicy-create': ipsecpolicy.CreateIPsecPolicy, - 'vpn-ipsecpolicy-update': ipsecpolicy.UpdateIPsecPolicy, - 'vpn-ipsecpolicy-delete': ipsecpolicy.DeleteIPsecPolicy, - 'vpn-ikepolicy-list': ikepolicy.ListIKEPolicy, - 'vpn-ikepolicy-show': ikepolicy.ShowIKEPolicy, - 'vpn-ikepolicy-create': ikepolicy.CreateIKEPolicy, - 'vpn-ikepolicy-update': ikepolicy.UpdateIKEPolicy, - 'vpn-ikepolicy-delete': ikepolicy.DeleteIKEPolicy, - 'meter-label-create': metering.CreateMeteringLabel, - 'meter-label-list': metering.ListMeteringLabel, - 'meter-label-show': metering.ShowMeteringLabel, - 'meter-label-delete': metering.DeleteMeteringLabel, - 'meter-label-rule-create': metering.CreateMeteringLabelRule, - 'meter-label-rule-list': metering.ListMeteringLabelRule, - 'meter-label-rule-show': metering.ShowMeteringLabelRule, - 'meter-label-rule-delete': metering.DeleteMeteringLabelRule, - 'rbac-create': rbac.CreateRBACPolicy, - 'rbac-update': rbac.UpdateRBACPolicy, - 'rbac-list': rbac.ListRBACPolicy, - 'rbac-show': rbac.ShowRBACPolicy, - 'rbac-delete': rbac.DeleteRBACPolicy, - 'address-scope-list': address_scope.ListAddressScope, - 'address-scope-show': address_scope.ShowAddressScope, - 'address-scope-create': address_scope.CreateAddressScope, - 'address-scope-delete': address_scope.DeleteAddressScope, - 'address-scope-update': address_scope.UpdateAddressScope, - 'qos-policy-list': qos_policy.ListQoSPolicy, - 'qos-policy-show': qos_policy.ShowQoSPolicy, - 'qos-policy-create': qos_policy.CreateQoSPolicy, - 'qos-policy-update': qos_policy.UpdateQoSPolicy, - 'qos-policy-delete': qos_policy.DeleteQoSPolicy, - 'qos-bandwidth-limit-rule-create': ( - bandwidth_limit_rule.CreateQoSBandwidthLimitRule - ), - 'qos-bandwidth-limit-rule-show': ( - bandwidth_limit_rule.ShowQoSBandwidthLimitRule - ), - 'qos-bandwidth-limit-rule-list': ( - bandwidth_limit_rule.ListQoSBandwidthLimitRules - ), - 'qos-bandwidth-limit-rule-update': ( - bandwidth_limit_rule.UpdateQoSBandwidthLimitRule - ), - 'qos-bandwidth-limit-rule-delete': ( - bandwidth_limit_rule.DeleteQoSBandwidthLimitRule - ), - 'qos-dscp-marking-rule-create': ( - dscp_marking_rule.CreateQoSDscpMarkingRule - ), - 'qos-dscp-marking-rule-show': ( - dscp_marking_rule.ShowQoSDscpMarkingRule - ), - 'qos-dscp-marking-rule-list': ( - dscp_marking_rule.ListQoSDscpMarkingRules - ), - 'qos-dscp-marking-rule-update': ( - dscp_marking_rule.UpdateQoSDscpMarkingRule - ), - 'qos-dscp-marking-rule-delete': ( - dscp_marking_rule.DeleteQoSDscpMarkingRule - ), - 'qos-minimum-bandwidth-rule-create': ( - minimum_bandwidth_rule.CreateQoSMinimumBandwidthRule - ), - 'qos-minimum-bandwidth-rule-show': ( - minimum_bandwidth_rule.ShowQoSMinimumBandwidthRule - ), - 'qos-minimum-bandwidth-rule-list': ( - minimum_bandwidth_rule.ListQoSMinimumBandwidthRules - ), - 'qos-minimum-bandwidth-rule-update': ( - minimum_bandwidth_rule.UpdateQoSMinimumBandwidthRule - ), - 'qos-minimum-bandwidth-rule-delete': ( - minimum_bandwidth_rule.DeleteQoSMinimumBandwidthRule - ), - 'qos-available-rule-types': qos_rule.ListQoSRuleTypes, - 'flavor-list': flavor.ListFlavor, - 'flavor-show': flavor.ShowFlavor, - 'flavor-create': flavor.CreateFlavor, - 'flavor-delete': flavor.DeleteFlavor, - 'flavor-update': flavor.UpdateFlavor, - 'flavor-associate': flavor.AssociateFlavor, - 'flavor-disassociate': flavor.DisassociateFlavor, - 'flavor-profile-list': flavor_profile.ListFlavorProfile, - 'flavor-profile-show': flavor_profile.ShowFlavorProfile, - 'flavor-profile-create': flavor_profile.CreateFlavorProfile, - 'flavor-profile-delete': flavor_profile.DeleteFlavorProfile, - 'flavor-profile-update': flavor_profile.UpdateFlavorProfile, - 'availability-zone-list': availability_zone.ListAvailabilityZone, - 'auto-allocated-topology-show': ( - auto_allocated_topology.ShowAutoAllocatedTopology), - 'auto-allocated-topology-delete': ( - auto_allocated_topology.DeleteAutoAllocatedTopology), - 'bgp-dragent-speaker-add': ( - bgp_drsched.AddBGPSpeakerToDRAgent - ), - 'bgp-dragent-speaker-remove': ( - bgp_drsched.RemoveBGPSpeakerFromDRAgent - ), - 'bgp-speaker-list-on-dragent': ( - bgp_drsched.ListBGPSpeakersOnDRAgent - ), - 'bgp-dragent-list-hosting-speaker': ( - bgp_drsched.ListDRAgentsHostingBGPSpeaker - ), - 'bgp-speaker-list': bgp_speaker.ListSpeakers, - 'bgp-speaker-advertiseroute-list': ( - bgp_speaker.ListRoutesAdvertisedBySpeaker - ), - 'bgp-speaker-show': bgp_speaker.ShowSpeaker, - 'bgp-speaker-create': bgp_speaker.CreateSpeaker, - 'bgp-speaker-update': bgp_speaker.UpdateSpeaker, - 'bgp-speaker-delete': bgp_speaker.DeleteSpeaker, - 'bgp-speaker-peer-add': bgp_speaker.AddPeerToSpeaker, - 'bgp-speaker-peer-remove': bgp_speaker.RemovePeerFromSpeaker, - 'bgp-speaker-network-add': bgp_speaker.AddNetworkToSpeaker, - 'bgp-speaker-network-remove': bgp_speaker.RemoveNetworkFromSpeaker, - 'bgp-peer-list': bgp_peer.ListPeers, - 'bgp-peer-show': bgp_peer.ShowPeer, - 'bgp-peer-create': bgp_peer.CreatePeer, - 'bgp-peer-update': bgp_peer.UpdatePeer, - 'bgp-peer-delete': bgp_peer.DeletePeer, - 'net-ip-availability-list': network_ip_availability.ListIpAvailability, - 'net-ip-availability-show': network_ip_availability.ShowIpAvailability, - 'tag-add': tag.AddTag, - 'tag-replace': tag.ReplaceTag, - 'tag-remove': tag.RemoveTag, -} - -COMMANDS = {'2.0': COMMAND_V2} - - class HelpAction(argparse.Action): """Print help message including sub-commands @@ -508,13 +148,11 @@ class NeutronShell(app.App): log = logging.getLogger(__name__) def __init__(self, apiversion): + namespace = NAMESPACE_MAP[apiversion] super(NeutronShell, self).__init__( description=__doc__.strip(), version=VERSION, - command_manager=commandmanager.CommandManager('neutron.cli'), ) - self.commands = COMMANDS - for k, v in self.commands[apiversion].items(): - self.command_manager.add_command(k, v) + command_manager=commandmanager.CommandManager(namespace), ) self._register_extensions(VERSION) @@ -813,7 +451,6 @@ def _extend_shell_commands(self, name, module, version): cls.__doc__ = ("%s %s" % (name_prefix, cls.__doc__) if cls.__doc__ else name_prefix) self.command_manager.add_command(cmd, cls) - self.commands[version][cmd] = cls except TypeError: pass @@ -832,15 +469,17 @@ def run(self, argv): if arg == 'bash-completion' and help_command_pos == -1: self._bash_completion() return 0 - if arg in self.commands[self.api_version]: - if command_pos == -1: - command_pos = index - elif arg in ('-h', '--help'): + if arg in ('-h', '--help'): if help_pos == -1: help_pos = index + # self.command_manager.commands contains 'help', + # so we need to check this first. elif arg == 'help': if help_command_pos == -1: help_command_pos = index + elif arg in self.command_manager.commands: + if command_pos == -1: + command_pos = index index = index + 1 if command_pos > -1 and help_pos > command_pos: argv = ['help', argv[command_pos]] diff --git a/neutronclient/tests/unit/test_client_extension.py b/neutronclient/tests/unit/test_client_extension.py index 60a208588..7e443a01e 100644 --- a/neutronclient/tests/unit/test_client_extension.py +++ b/neutronclient/tests/unit/test_client_extension.py @@ -41,25 +41,28 @@ def _mock_extension_loading(self): return contrib def test_ext_cmd_loaded(self): - shell.NeutronShell('2.0') + neutron_shell = shell.NeutronShell('2.0') ext_cmd = {'fox-sockets-list': fox_sockets.FoxInSocketsList, 'fox-sockets-create': fox_sockets.FoxInSocketsCreate, 'fox-sockets-update': fox_sockets.FoxInSocketsUpdate, 'fox-sockets-delete': fox_sockets.FoxInSocketsDelete, 'fox-sockets-show': fox_sockets.FoxInSocketsShow} - self.assertDictContainsSubset(ext_cmd, shell.COMMANDS['2.0']) + for cmd_name, cmd_class in ext_cmd.items(): + found = neutron_shell.command_manager.find_command([cmd_name]) + self.assertEqual(cmd_class, found[0]) def test_ext_cmd_help_doc_with_extension_name(self): - shell.NeutronShell('2.0') + neutron_shell = shell.NeutronShell('2.0') ext_cmd = {'fox-sockets-list': fox_sockets.FoxInSocketsList, 'fox-sockets-create': fox_sockets.FoxInSocketsCreate, 'fox-sockets-update': fox_sockets.FoxInSocketsUpdate, 'fox-sockets-delete': fox_sockets.FoxInSocketsDelete, 'fox-sockets-show': fox_sockets.FoxInSocketsShow} - self.assertDictContainsSubset(ext_cmd, shell.COMMANDS['2.0']) - for item in ext_cmd: - cmdcls = shell.COMMANDS['2.0'].get(item) - self.assertTrue(cmdcls.__doc__.startswith("[_fox_sockets]")) + for cmd_name, cmd_class in ext_cmd.items(): + found = neutron_shell.command_manager.find_command([cmd_name]) + found_factory = found[0] + self.assertEqual(cmd_class, found_factory) + self.assertTrue(found_factory.__doc__.startswith("[_fox_sockets]")) def test_delete_fox_socket(self): # Delete fox socket: myid. @@ -138,9 +141,11 @@ def _mock_extension_loading(self): return contrib def test_ext_cmd_loaded(self): - shell.NeutronShell('2.0') + neutron_shell = shell.NeutronShell('2.0') ext_cmd = {'ip-address-list': self.IPAddressesList} - self.assertDictContainsSubset(ext_cmd, shell.COMMANDS['2.0']) + for cmd_name, cmd_class in ext_cmd.items(): + found = neutron_shell.command_manager.find_command([cmd_name]) + self.assertEqual(cmd_class, found[0]) def test_list_ip_addresses(self): # List ip_addresses. @@ -195,13 +200,15 @@ def _mock_extension_loading(self): return contrib def test_ext_cmd_loaded(self): - shell.NeutronShell('2.0') + neutron_shell = shell.NeutronShell('2.0') ext_cmd = {'parent-child-list': self.ChildrenList, 'parent-child-show': self.ChildShow, 'parent-child-update': self.ChildUpdate, 'parent-child-delete': self.ChildDelete, 'parent-child-create': self.ChildCreate} - self.assertDictContainsSubset(ext_cmd, shell.COMMANDS['2.0']) + for cmd_name, cmd_class in ext_cmd.items(): + found = neutron_shell.command_manager.find_command([cmd_name]) + self.assertEqual(cmd_class, found[0]) def test_client_methods_have_parent_id_arg(self): methods = (self.client.list_parents_children, diff --git a/setup.cfg b/setup.cfg index cec20f9d8..a14c06aeb 100644 --- a/setup.cfg +++ b/setup.cfg @@ -79,6 +79,291 @@ openstack.neutronclient.v2 = bgpvpn_router_association_list = neutronclient.osc.v2.networking_bgpvpn.router_association:ListBgpvpnRouterAssoc bgpvpn_router_association_show = neutronclient.osc.v2.networking_bgpvpn.router_association:ShowBgpvpnRouterAssoc +neutron.cli.v2 = + bash-completion = neutronclient.shell:BashCompletionCommand + + net-list = neutronclient.neutron.v2_0.network:ListNetwork + net-external-list = neutronclient.neutron.v2_0.network:ListExternalNetwork + net-show = neutronclient.neutron.v2_0.network:ShowNetwork + net-create = neutronclient.neutron.v2_0.network:CreateNetwork + net-delete = neutronclient.neutron.v2_0.network:DeleteNetwork + net-update = neutronclient.neutron.v2_0.network:UpdateNetwork + + subnet-list = neutronclient.neutron.v2_0.subnet:ListSubnet + subnet-show = neutronclient.neutron.v2_0.subnet:ShowSubnet + subnet-create = neutronclient.neutron.v2_0.subnet:CreateSubnet + subnet-delete = neutronclient.neutron.v2_0.subnet:DeleteSubnet + subnet-update = neutronclient.neutron.v2_0.subnet:UpdateSubnet + + subnetpool-list = neutronclient.neutron.v2_0.subnetpool:ListSubnetPool + subnetpool-show = neutronclient.neutron.v2_0.subnetpool:ShowSubnetPool + subnetpool-create = neutronclient.neutron.v2_0.subnetpool:CreateSubnetPool + subnetpool-delete = neutronclient.neutron.v2_0.subnetpool:DeleteSubnetPool + subnetpool-update = neutronclient.neutron.v2_0.subnetpool:UpdateSubnetPool + + port-list = neutronclient.neutron.v2_0.port:ListPort + port-show = neutronclient.neutron.v2_0.port:ShowPort + port-create = neutronclient.neutron.v2_0.port:CreatePort + port-delete = neutronclient.neutron.v2_0.port:DeletePort + port-update = neutronclient.neutron.v2_0.port:UpdatePort + + purge = neutronclient.neutron.v2_0.purge:Purge + + quota-list = neutronclient.neutron.v2_0.quota:ListQuota + quota-show = neutronclient.neutron.v2_0.quota:ShowQuota + quota-default-show = neutronclient.neutron.v2_0.quota:ShowQuotaDefault + quota-delete = neutronclient.neutron.v2_0.quota:DeleteQuota + quota-update = neutronclient.neutron.v2_0.quota:UpdateQuota + + ext-list = neutronclient.neutron.v2_0.extension:ListExt + ext-show = neutronclient.neutron.v2_0.extension:ShowExt + + router-list = neutronclient.neutron.v2_0.router:ListRouter + router-port-list = neutronclient.neutron.v2_0.port:ListRouterPort + router-show = neutronclient.neutron.v2_0.router:ShowRouter + router-create = neutronclient.neutron.v2_0.router:CreateRouter + router-delete = neutronclient.neutron.v2_0.router:DeleteRouter + router-update = neutronclient.neutron.v2_0.router:UpdateRouter + router-interface-add = neutronclient.neutron.v2_0.router:AddInterfaceRouter + router-interface-delete = neutronclient.neutron.v2_0.router:RemoveInterfaceRouter + router-gateway-set = neutronclient.neutron.v2_0.router:SetGatewayRouter + router-gateway-clear = neutronclient.neutron.v2_0.router:RemoveGatewayRouter + + floatingip-list = neutronclient.neutron.v2_0.floatingip:ListFloatingIP + floatingip-show = neutronclient.neutron.v2_0.floatingip:ShowFloatingIP + floatingip-create = neutronclient.neutron.v2_0.floatingip:CreateFloatingIP + floatingip-delete = neutronclient.neutron.v2_0.floatingip:DeleteFloatingIP + floatingip-associate = neutronclient.neutron.v2_0.floatingip:AssociateFloatingIP + floatingip-disassociate = neutronclient.neutron.v2_0.floatingip:DisassociateFloatingIP + + security-group-list = neutronclient.neutron.v2_0.securitygroup:ListSecurityGroup + security-group-show = neutronclient.neutron.v2_0.securitygroup:ShowSecurityGroup + security-group-create = neutronclient.neutron.v2_0.securitygroup:CreateSecurityGroup + security-group-delete = neutronclient.neutron.v2_0.securitygroup:DeleteSecurityGroup + security-group-update = neutronclient.neutron.v2_0.securitygroup:UpdateSecurityGroup + security-group-rule-list = neutronclient.neutron.v2_0.securitygroup:ListSecurityGroupRule + security-group-rule-show = neutronclient.neutron.v2_0.securitygroup:ShowSecurityGroupRule + security-group-rule-create = neutronclient.neutron.v2_0.securitygroup:CreateSecurityGroupRule + security-group-rule-delete = neutronclient.neutron.v2_0.securitygroup:DeleteSecurityGroupRule + + agent-list = neutronclient.neutron.v2_0.agent:ListAgent + agent-show = neutronclient.neutron.v2_0.agent:ShowAgent + agent-delete = neutronclient.neutron.v2_0.agent:DeleteAgent + agent-update = neutronclient.neutron.v2_0.agent:UpdateAgent + + dhcp-agent-network-add = neutronclient.neutron.v2_0.agentscheduler:AddNetworkToDhcpAgent + dhcp-agent-network-remove = neutronclient.neutron.v2_0.agentscheduler:RemoveNetworkFromDhcpAgent + net-list-on-dhcp-agent = neutronclient.neutron.v2_0.agentscheduler:ListNetworksOnDhcpAgent + dhcp-agent-list-hosting-net = neutronclient.neutron.v2_0.agentscheduler:ListDhcpAgentsHostingNetwork + + l3-agent-router-add = neutronclient.neutron.v2_0.agentscheduler:AddRouterToL3Agent + l3-agent-router-remove = neutronclient.neutron.v2_0.agentscheduler:RemoveRouterFromL3Agent + router-list-on-l3-agent = neutronclient.neutron.v2_0.agentscheduler:ListRoutersOnL3Agent + l3-agent-list-hosting-router = neutronclient.neutron.v2_0.agentscheduler:ListL3AgentsHostingRouter + + lb-pool-list-on-agent = neutronclient.neutron.v2_0.agentscheduler:ListPoolsOnLbaasAgent + lb-agent-hosting-pool = neutronclient.neutron.v2_0.agentscheduler:GetLbaasAgentHostingPool + + lbaas-loadbalancer-list-on-agent = neutronclient.neutron.v2_0.agentscheduler:ListLoadBalancersOnLbaasAgent + lbaas-agent-hosting-loadbalancer = neutronclient.neutron.v2_0.agentscheduler:GetLbaasAgentHostingLoadBalancer + + service-provider-list = neutronclient.neutron.v2_0.servicetype:ListServiceProvider + + rbac-create = neutronclient.neutron.v2_0.rbac:CreateRBACPolicy + rbac-update = neutronclient.neutron.v2_0.rbac:UpdateRBACPolicy + rbac-list = neutronclient.neutron.v2_0.rbac:ListRBACPolicy + rbac-show = neutronclient.neutron.v2_0.rbac:ShowRBACPolicy + rbac-delete = neutronclient.neutron.v2_0.rbac:DeleteRBACPolicy + + address-scope-list = neutronclient.neutron.v2_0.address_scope:ListAddressScope + address-scope-show = neutronclient.neutron.v2_0.address_scope:ShowAddressScope + address-scope-create = neutronclient.neutron.v2_0.address_scope:CreateAddressScope + address-scope-delete = neutronclient.neutron.v2_0.address_scope:DeleteAddressScope + address-scope-update = neutronclient.neutron.v2_0.address_scope:UpdateAddressScope + + availability-zone-list = neutronclient.neutron.v2_0.availability_zone:ListAvailabilityZone + + auto-allocated-topology-show = neutronclient.neutron.v2_0.auto_allocated_topology:ShowAutoAllocatedTopology + auto-allocated-topology-delete = neutronclient.neutron.v2_0.auto_allocated_topology:DeleteAutoAllocatedTopology + + net-ip-availability-list = neutronclient.neutron.v2_0.network_ip_availability:ListIpAvailability + net-ip-availability-show = neutronclient.neutron.v2_0.network_ip_availability:ShowIpAvailability + + tag-add = neutronclient.neutron.v2_0.tag:AddTag + tag-replace = neutronclient.neutron.v2_0.tag:ReplaceTag + tag-remove = neutronclient.neutron.v2_0.tag:RemoveTag + + qos-policy-list = neutronclient.neutron.v2_0.qos.policy:ListQoSPolicy + qos-policy-show = neutronclient.neutron.v2_0.qos.policy:ShowQoSPolicy + qos-policy-create = neutronclient.neutron.v2_0.qos.policy:CreateQoSPolicy + qos-policy-update = neutronclient.neutron.v2_0.qos.policy:UpdateQoSPolicy + qos-policy-delete = neutronclient.neutron.v2_0.qos.policy:DeleteQoSPolicy + qos-bandwidth-limit-rule-create = neutronclient.neutron.v2_0.qos.bandwidth_limit_rule:CreateQoSBandwidthLimitRule + qos-bandwidth-limit-rule-show = neutronclient.neutron.v2_0.qos.bandwidth_limit_rule:ShowQoSBandwidthLimitRule + qos-bandwidth-limit-rule-list = neutronclient.neutron.v2_0.qos.bandwidth_limit_rule:ListQoSBandwidthLimitRules + qos-bandwidth-limit-rule-update = neutronclient.neutron.v2_0.qos.bandwidth_limit_rule:UpdateQoSBandwidthLimitRule + qos-bandwidth-limit-rule-delete = neutronclient.neutron.v2_0.qos.bandwidth_limit_rule:DeleteQoSBandwidthLimitRule + qos-dscp-marking-rule-create = neutronclient.neutron.v2_0.qos.dscp_marking_rule:CreateQoSDscpMarkingRule + qos-dscp-marking-rule-show = neutronclient.neutron.v2_0.qos.dscp_marking_rule:ShowQoSDscpMarkingRule + qos-dscp-marking-rule-list = neutronclient.neutron.v2_0.qos.dscp_marking_rule:ListQoSDscpMarkingRules + qos-dscp-marking-rule-update = neutronclient.neutron.v2_0.qos.dscp_marking_rule:UpdateQoSDscpMarkingRule + qos-dscp-marking-rule-delete = neutronclient.neutron.v2_0.qos.dscp_marking_rule:DeleteQoSDscpMarkingRule + qos-minimum-bandwidth-rule-create = neutronclient.neutron.v2_0.qos.minimum_bandwidth_rule:CreateQoSMinimumBandwidthRule + qos-minimum-bandwidth-rule-show = neutronclient.neutron.v2_0.qos.minimum_bandwidth_rule:ShowQoSMinimumBandwidthRule + qos-minimum-bandwidth-rule-list = neutronclient.neutron.v2_0.qos.minimum_bandwidth_rule:ListQoSMinimumBandwidthRules + qos-minimum-bandwidth-rule-update = neutronclient.neutron.v2_0.qos.minimum_bandwidth_rule:UpdateQoSMinimumBandwidthRule + qos-minimum-bandwidth-rule-delete = neutronclient.neutron.v2_0.qos.minimum_bandwidth_rule:DeleteQoSMinimumBandwidthRule + qos-available-rule-types = neutronclient.neutron.v2_0.qos.rule:ListQoSRuleTypes + + flavor-list = neutronclient.neutron.v2_0.flavor.flavor:ListFlavor + flavor-show = neutronclient.neutron.v2_0.flavor.flavor:ShowFlavor + flavor-create = neutronclient.neutron.v2_0.flavor.flavor:CreateFlavor + flavor-delete = neutronclient.neutron.v2_0.flavor.flavor:DeleteFlavor + flavor-update = neutronclient.neutron.v2_0.flavor.flavor:UpdateFlavor + flavor-associate = neutronclient.neutron.v2_0.flavor.flavor:AssociateFlavor + flavor-disassociate = neutronclient.neutron.v2_0.flavor.flavor:DisassociateFlavor + flavor-profile-list = neutronclient.neutron.v2_0.flavor.flavor_profile:ListFlavorProfile + flavor-profile-show = neutronclient.neutron.v2_0.flavor.flavor_profile:ShowFlavorProfile + flavor-profile-create = neutronclient.neutron.v2_0.flavor.flavor_profile:CreateFlavorProfile + flavor-profile-delete = neutronclient.neutron.v2_0.flavor.flavor_profile:DeleteFlavorProfile + flavor-profile-update = neutronclient.neutron.v2_0.flavor.flavor_profile:UpdateFlavorProfile + + meter-label-create = neutronclient.neutron.v2_0.metering:CreateMeteringLabel + meter-label-list = neutronclient.neutron.v2_0.metering:ListMeteringLabel + meter-label-show = neutronclient.neutron.v2_0.metering:ShowMeteringLabel + meter-label-delete = neutronclient.neutron.v2_0.metering:DeleteMeteringLabel + meter-label-rule-create = neutronclient.neutron.v2_0.metering:CreateMeteringLabelRule + meter-label-rule-list = neutronclient.neutron.v2_0.metering:ListMeteringLabelRule + meter-label-rule-show = neutronclient.neutron.v2_0.metering:ShowMeteringLabelRule + meter-label-rule-delete = neutronclient.neutron.v2_0.metering:DeleteMeteringLabelRule + + firewall-rule-list = neutronclient.neutron.v2_0.fw.firewallrule:ListFirewallRule + firewall-rule-show = neutronclient.neutron.v2_0.fw.firewallrule:ShowFirewallRule + firewall-rule-create = neutronclient.neutron.v2_0.fw.firewallrule:CreateFirewallRule + firewall-rule-update = neutronclient.neutron.v2_0.fw.firewallrule:UpdateFirewallRule + firewall-rule-delete = neutronclient.neutron.v2_0.fw.firewallrule:DeleteFirewallRule + firewall-policy-list = neutronclient.neutron.v2_0.fw.firewallpolicy:ListFirewallPolicy + firewall-policy-show = neutronclient.neutron.v2_0.fw.firewallpolicy:ShowFirewallPolicy + firewall-policy-create = neutronclient.neutron.v2_0.fw.firewallpolicy:CreateFirewallPolicy + firewall-policy-update = neutronclient.neutron.v2_0.fw.firewallpolicy:UpdateFirewallPolicy + firewall-policy-delete = neutronclient.neutron.v2_0.fw.firewallpolicy:DeleteFirewallPolicy + firewall-policy-insert-rule = neutronclient.neutron.v2_0.fw.firewallpolicy:FirewallPolicyInsertRule + firewall-policy-remove-rule = neutronclient.neutron.v2_0.fw.firewallpolicy:FirewallPolicyRemoveRule + firewall-list = neutronclient.neutron.v2_0.fw.firewall:ListFirewall + firewall-show = neutronclient.neutron.v2_0.fw.firewall:ShowFirewall + firewall-create = neutronclient.neutron.v2_0.fw.firewall:CreateFirewall + firewall-update = neutronclient.neutron.v2_0.fw.firewall:UpdateFirewall + firewall-delete = neutronclient.neutron.v2_0.fw.firewall:DeleteFirewall + + bgp-dragent-speaker-add = neutronclient.neutron.v2_0.bgp.dragentscheduler:AddBGPSpeakerToDRAgent + bgp-dragent-speaker-remove = neutronclient.neutron.v2_0.bgp.dragentscheduler:RemoveBGPSpeakerFromDRAgent + bgp-speaker-list-on-dragent = neutronclient.neutron.v2_0.bgp.dragentscheduler:ListBGPSpeakersOnDRAgent + bgp-dragent-list-hosting-speaker = neutronclient.neutron.v2_0.bgp.dragentscheduler:ListDRAgentsHostingBGPSpeaker + bgp-speaker-list = neutronclient.neutron.v2_0.bgp.speaker:ListSpeakers + bgp-speaker-advertiseroute-list = neutronclient.neutron.v2_0.bgp.speaker:ListRoutesAdvertisedBySpeaker + bgp-speaker-show = neutronclient.neutron.v2_0.bgp.speaker:ShowSpeaker + bgp-speaker-create = neutronclient.neutron.v2_0.bgp.speaker:CreateSpeaker + bgp-speaker-update = neutronclient.neutron.v2_0.bgp.speaker:UpdateSpeaker + bgp-speaker-delete = neutronclient.neutron.v2_0.bgp.speaker:DeleteSpeaker + bgp-speaker-peer-add = neutronclient.neutron.v2_0.bgp.speaker:AddPeerToSpeaker + bgp-speaker-peer-remove = neutronclient.neutron.v2_0.bgp.speaker:RemovePeerFromSpeaker + bgp-speaker-network-add = neutronclient.neutron.v2_0.bgp.speaker:AddNetworkToSpeaker + bgp-speaker-network-remove = neutronclient.neutron.v2_0.bgp.speaker:RemoveNetworkFromSpeaker + bgp-peer-list = neutronclient.neutron.v2_0.bgp.peer:ListPeers + bgp-peer-show = neutronclient.neutron.v2_0.bgp.peer:ShowPeer + bgp-peer-create = neutronclient.neutron.v2_0.bgp.peer:CreatePeer + bgp-peer-update = neutronclient.neutron.v2_0.bgp.peer:UpdatePeer + bgp-peer-delete = neutronclient.neutron.v2_0.bgp.peer:DeletePeer + + lbaas-loadbalancer-list = neutronclient.neutron.v2_0.lb.v2.loadbalancer:ListLoadBalancer + lbaas-loadbalancer-show = neutronclient.neutron.v2_0.lb.v2.loadbalancer:ShowLoadBalancer + lbaas-loadbalancer-create = neutronclient.neutron.v2_0.lb.v2.loadbalancer:CreateLoadBalancer + lbaas-loadbalancer-update = neutronclient.neutron.v2_0.lb.v2.loadbalancer:UpdateLoadBalancer + lbaas-loadbalancer-delete = neutronclient.neutron.v2_0.lb.v2.loadbalancer:DeleteLoadBalancer + lbaas-loadbalancer-stats = neutronclient.neutron.v2_0.lb.v2.loadbalancer:RetrieveLoadBalancerStats + lbaas-loadbalancer-status = neutronclient.neutron.v2_0.lb.v2.loadbalancer:RetrieveLoadBalancerStatus + lbaas-listener-list = neutronclient.neutron.v2_0.lb.v2.listener:ListListener + lbaas-listener-show = neutronclient.neutron.v2_0.lb.v2.listener:ShowListener + lbaas-listener-create = neutronclient.neutron.v2_0.lb.v2.listener:CreateListener + lbaas-listener-update = neutronclient.neutron.v2_0.lb.v2.listener:UpdateListener + lbaas-listener-delete = neutronclient.neutron.v2_0.lb.v2.listener:DeleteListener + lbaas-l7policy-list = neutronclient.neutron.v2_0.lb.v2.l7policy:ListL7Policy + lbaas-l7policy-show = neutronclient.neutron.v2_0.lb.v2.l7policy:ShowL7Policy + lbaas-l7policy-create = neutronclient.neutron.v2_0.lb.v2.l7policy:CreateL7Policy + lbaas-l7policy-update = neutronclient.neutron.v2_0.lb.v2.l7policy:UpdateL7Policy + lbaas-l7policy-delete = neutronclient.neutron.v2_0.lb.v2.l7policy:DeleteL7Policy + lbaas-l7rule-list = neutronclient.neutron.v2_0.lb.v2.l7rule:ListL7Rule + lbaas-l7rule-show = neutronclient.neutron.v2_0.lb.v2.l7rule:ShowL7Rule + lbaas-l7rule-create = neutronclient.neutron.v2_0.lb.v2.l7rule:CreateL7Rule + lbaas-l7rule-update = neutronclient.neutron.v2_0.lb.v2.l7rule:UpdateL7Rule + lbaas-l7rule-delete = neutronclient.neutron.v2_0.lb.v2.l7rule:DeleteL7Rule + lbaas-pool-list = neutronclient.neutron.v2_0.lb.v2.pool:ListPool + lbaas-pool-show = neutronclient.neutron.v2_0.lb.v2.pool:ShowPool + lbaas-pool-create = neutronclient.neutron.v2_0.lb.v2.pool:CreatePool + lbaas-pool-update = neutronclient.neutron.v2_0.lb.v2.pool:UpdatePool + lbaas-pool-delete = neutronclient.neutron.v2_0.lb.v2.pool:DeletePool + lbaas-healthmonitor-list = neutronclient.neutron.v2_0.lb.v2.healthmonitor:ListHealthMonitor + lbaas-healthmonitor-show = neutronclient.neutron.v2_0.lb.v2.healthmonitor:ShowHealthMonitor + lbaas-healthmonitor-create = neutronclient.neutron.v2_0.lb.v2.healthmonitor:CreateHealthMonitor + lbaas-healthmonitor-update = neutronclient.neutron.v2_0.lb.v2.healthmonitor:UpdateHealthMonitor + lbaas-healthmonitor-delete = neutronclient.neutron.v2_0.lb.v2.healthmonitor:DeleteHealthMonitor + lbaas-member-list = neutronclient.neutron.v2_0.lb.v2.member:ListMember + lbaas-member-show = neutronclient.neutron.v2_0.lb.v2.member:ShowMember + lbaas-member-create = neutronclient.neutron.v2_0.lb.v2.member:CreateMember + lbaas-member-update = neutronclient.neutron.v2_0.lb.v2.member:UpdateMember + lbaas-member-delete = neutronclient.neutron.v2_0.lb.v2.member:DeleteMember + + lb-vip-list = neutronclient.neutron.v2_0.lb.vip:ListVip + lb-vip-show = neutronclient.neutron.v2_0.lb.vip:ShowVip + lb-vip-create = neutronclient.neutron.v2_0.lb.vip:CreateVip + lb-vip-update = neutronclient.neutron.v2_0.lb.vip:UpdateVip + lb-vip-delete = neutronclient.neutron.v2_0.lb.vip:DeleteVip + lb-pool-list = neutronclient.neutron.v2_0.lb.pool:ListPool + lb-pool-show = neutronclient.neutron.v2_0.lb.pool:ShowPool + lb-pool-create = neutronclient.neutron.v2_0.lb.pool:CreatePool + lb-pool-update = neutronclient.neutron.v2_0.lb.pool:UpdatePool + lb-pool-delete = neutronclient.neutron.v2_0.lb.pool:DeletePool + lb-pool-stats = neutronclient.neutron.v2_0.lb.pool:RetrievePoolStats + lb-member-list = neutronclient.neutron.v2_0.lb.member:ListMember + lb-member-show = neutronclient.neutron.v2_0.lb.member:ShowMember + lb-member-create = neutronclient.neutron.v2_0.lb.member:CreateMember + lb-member-update = neutronclient.neutron.v2_0.lb.member:UpdateMember + lb-member-delete = neutronclient.neutron.v2_0.lb.member:DeleteMember + lb-healthmonitor-list = neutronclient.neutron.v2_0.lb.healthmonitor:ListHealthMonitor + lb-healthmonitor-show = neutronclient.neutron.v2_0.lb.healthmonitor:ShowHealthMonitor + lb-healthmonitor-create = neutronclient.neutron.v2_0.lb.healthmonitor:CreateHealthMonitor + lb-healthmonitor-update = neutronclient.neutron.v2_0.lb.healthmonitor:UpdateHealthMonitor + lb-healthmonitor-delete = neutronclient.neutron.v2_0.lb.healthmonitor:DeleteHealthMonitor + lb-healthmonitor-associate = neutronclient.neutron.v2_0.lb.healthmonitor:AssociateHealthMonitor + lb-healthmonitor-disassociate = neutronclient.neutron.v2_0.lb.healthmonitor:DisassociateHealthMonitor + + ipsec-site-connection-list = neutronclient.neutron.v2_0.vpn.ipsec_site_connection:ListIPsecSiteConnection + ipsec-site-connection-show = neutronclient.neutron.v2_0.vpn.ipsec_site_connection:ShowIPsecSiteConnection + ipsec-site-connection-create = neutronclient.neutron.v2_0.vpn.ipsec_site_connection:CreateIPsecSiteConnection + ipsec-site-connection-update = neutronclient.neutron.v2_0.vpn.ipsec_site_connection:UpdateIPsecSiteConnection + ipsec-site-connection-delete = neutronclient.neutron.v2_0.vpn.ipsec_site_connection:DeleteIPsecSiteConnection + vpn-endpoint-group-list = neutronclient.neutron.v2_0.vpn.endpoint_group:ListEndpointGroup + vpn-endpoint-group-show = neutronclient.neutron.v2_0.vpn.endpoint_group:ShowEndpointGroup + vpn-endpoint-group-create = neutronclient.neutron.v2_0.vpn.endpoint_group:CreateEndpointGroup + vpn-endpoint-group-update = neutronclient.neutron.v2_0.vpn.endpoint_group:UpdateEndpointGroup + vpn-endpoint-group-delete = neutronclient.neutron.v2_0.vpn.endpoint_group:DeleteEndpointGroup + vpn-service-list = neutronclient.neutron.v2_0.vpn.vpnservice:ListVPNService + vpn-service-show = neutronclient.neutron.v2_0.vpn.vpnservice:ShowVPNService + vpn-service-create = neutronclient.neutron.v2_0.vpn.vpnservice:CreateVPNService + vpn-service-update = neutronclient.neutron.v2_0.vpn.vpnservice:UpdateVPNService + vpn-service-delete = neutronclient.neutron.v2_0.vpn.vpnservice:DeleteVPNService + vpn-ipsecpolicy-list = neutronclient.neutron.v2_0.vpn.ipsecpolicy:ListIPsecPolicy + vpn-ipsecpolicy-show = neutronclient.neutron.v2_0.vpn.ipsecpolicy:ShowIPsecPolicy + vpn-ipsecpolicy-create = neutronclient.neutron.v2_0.vpn.ipsecpolicy:CreateIPsecPolicy + vpn-ipsecpolicy-update = neutronclient.neutron.v2_0.vpn.ipsecpolicy:UpdateIPsecPolicy + vpn-ipsecpolicy-delete = neutronclient.neutron.v2_0.vpn.ipsecpolicy:DeleteIPsecPolicy + vpn-ikepolicy-list = neutronclient.neutron.v2_0.vpn.ikepolicy:ListIKEPolicy + vpn-ikepolicy-show = neutronclient.neutron.v2_0.vpn.ikepolicy:ShowIKEPolicy + vpn-ikepolicy-create = neutronclient.neutron.v2_0.vpn.ikepolicy:CreateIKEPolicy + vpn-ikepolicy-update = neutronclient.neutron.v2_0.vpn.ikepolicy:UpdateIKEPolicy + vpn-ikepolicy-delete = neutronclient.neutron.v2_0.vpn.ikepolicy:DeleteIKEPolicy + + [build_sphinx] all_files = 1 build-dir = doc/build From 27cdd7d84d16921c93c5c8d05ae6b0b0c3075b68 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Wed, 5 Jul 2017 09:14:16 +0900 Subject: [PATCH 594/845] Enable warning-is-error in doc build Part of doc-migration work Change-Id: I956975ab28be163ca0b2024456360595e649d740 --- setup.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.cfg b/setup.cfg index cec20f9d8..63ec0ce9f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -83,6 +83,7 @@ openstack.neutronclient.v2 = all_files = 1 build-dir = doc/build source-dir = doc/source +warning-is-error = 1 [wheel] universal = 1 From f4c0468b3667f4a967e2a0402c0f49dcca55e29f Mon Sep 17 00:00:00 2001 From: Cao Xuan Hoang Date: Thu, 6 Jul 2017 09:05:14 +0700 Subject: [PATCH 595/845] Use flake8-import-order plugin In reviews we usually check import grouping but it is boring. By using flake8-import-order plugin, we can avoid this. It enforces loose checking so it sounds good to use it. This flake8 plugin is already used in tempest. Note that flake8-import-order version is pinned to avoid unexpected breakage of pep8 job. Setup for unit tests of hacking rules is tweaked to disable flake8-import-order checks. This extension assumes an actual file exists and causes hacking rule unit tests. Change-Id: I61e683ab0119e4ae90b7107f0690528d789e3875 --- neutronclient/neutron/v2_0/auto_allocated_topology.py | 1 + neutronclient/tests/unit/osc/v2/fakes.py | 2 +- neutronclient/tests/unit/osc/v2/fwaas/fakes.py | 3 ++- neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py | 2 +- neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py | 2 +- neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py | 4 ++-- neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py | 1 + .../tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py | 2 +- .../osc/v2/networking_bgpvpn/test_resource_association.py | 2 +- neutronclient/tests/unit/osc/v2/trunk/fakes.py | 3 ++- neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py | 4 ++-- test-requirements.txt | 1 + tox.ini | 2 ++ 13 files changed, 18 insertions(+), 11 deletions(-) diff --git a/neutronclient/neutron/v2_0/auto_allocated_topology.py b/neutronclient/neutron/v2_0/auto_allocated_topology.py index 12ca4dbd1..ef0da4325 100644 --- a/neutronclient/neutron/v2_0/auto_allocated_topology.py +++ b/neutronclient/neutron/v2_0/auto_allocated_topology.py @@ -17,6 +17,7 @@ from __future__ import print_function import argparse + from cliff import show from oslo_serialization import jsonutils diff --git a/neutronclient/tests/unit/osc/v2/fakes.py b/neutronclient/tests/unit/osc/v2/fakes.py index cc30842ab..afa81e72d 100644 --- a/neutronclient/tests/unit/osc/v2/fakes.py +++ b/neutronclient/tests/unit/osc/v2/fakes.py @@ -12,9 +12,9 @@ # import argparse -import mock from cliff import columns as cliff_columns +import mock from osc_lib.tests import utils diff --git a/neutronclient/tests/unit/osc/v2/fwaas/fakes.py b/neutronclient/tests/unit/osc/v2/fwaas/fakes.py index 7a22ab1ab..7baef8421 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/fakes.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/fakes.py @@ -16,9 +16,10 @@ import collections import copy -import mock import uuid +import mock + class FakeFWaaS(object): diff --git a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py index 0a15f2b6d..87fb4c2fd 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py @@ -15,9 +15,9 @@ # import copy -import mock import re +import mock from osc_lib import exceptions from osc_lib.tests import utils diff --git a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py index 6d9780768..c2e9b1bb9 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py @@ -15,9 +15,9 @@ # import copy -import mock import re +import mock from osc_lib import exceptions from osc_lib.tests import utils diff --git a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py index d09bbbe98..94e119940 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py @@ -15,12 +15,12 @@ # import copy -import mock import re -import testtools +import mock from osc_lib import exceptions from osc_lib.tests import utils +import testtools from neutronclient.osc import utils as osc_utils from neutronclient.osc.v2.fwaas import constants as const diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py index 6138feaf3..7da3a9850 100644 --- a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py +++ b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py @@ -15,6 +15,7 @@ # import copy + import mock from neutronclient.osc import utils as nc_osc_utils diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py index aa688277d..f13980eda 100644 --- a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py +++ b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py @@ -15,9 +15,9 @@ # import copy -import mock import operator +import mock from osc_lib import exceptions from osc_lib import utils as osc_utils diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py index e7bc8ce9c..6978d85cd 100644 --- a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py +++ b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py @@ -15,9 +15,9 @@ # import copy -import mock import operator +import mock from osc_lib import exceptions from osc_lib import utils as osc_utils diff --git a/neutronclient/tests/unit/osc/v2/trunk/fakes.py b/neutronclient/tests/unit/osc/v2/trunk/fakes.py index 4ae1f287f..12f773f03 100644 --- a/neutronclient/tests/unit/osc/v2/trunk/fakes.py +++ b/neutronclient/tests/unit/osc/v2/trunk/fakes.py @@ -11,9 +11,10 @@ # under the License. import copy -import mock import uuid +import mock + class FakeTrunk(object): """Fake one or more trunks.""" diff --git a/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py b/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py index 8b7aaa0d5..8e9e73c49 100644 --- a/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py +++ b/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py @@ -15,13 +15,13 @@ import argparse import copy + import mock from mock import call -import testtools - from osc_lib.cli import format_columns from osc_lib import exceptions from osc_lib.tests import utils as tests_utils +import testtools from neutronclient.osc.v2.trunk import network_trunk as trunk from neutronclient.osc.v2 import utils as v2_utils diff --git a/test-requirements.txt b/test-requirements.txt index 85ccf056b..ae2856685 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -5,6 +5,7 @@ hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0 coverage!=4.4,>=4.0 # Apache-2.0 fixtures>=3.0.0 # Apache-2.0/BSD +flake8-import-order==0.12 # LGPLv3 mox3!=0.19.0,>=0.7.0 # Apache-2.0 mock>=2.0 # BSD openstackdocstheme>=1.11.0 # Apache-2.0 diff --git a/tox.ini b/tox.ini index 587612486..23fda5012 100644 --- a/tox.ini +++ b/tox.ini @@ -55,5 +55,7 @@ commands = sphinx-build -a -E -d releasenotes/build/doctrees -b html releasenote [flake8] show-source = true exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,tools +import-order-style = pep8 + # H904: Delay string interpolations at logging calls enable-extensions=H904 From c4ec6759b6aec38f1662ce0291ce87d9b5317c19 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Mon, 3 Jul 2017 17:11:57 +0900 Subject: [PATCH 596/845] doc: Improve network CLI page * Move OSC first as neutron CLI is deprecated * Add more information on OSC usage (links to embeded command list and the mapping guide) * Add deprecation notice for neutron CLI This commit also makes toctree structure simpler At now, we have two index page unlinked from the top page. I thought it is good first, but it potentially makes things tricky. Let's remove such tweaks for easy maintenance. Change-Id: I7fd79f465fa6c04bf5b13f6578796d4c2d01edfd --- doc/source/cli/index.rst | 51 +++++++++++++++++++++----------- doc/source/cli/neutron.rst | 9 ++++++ doc/source/cli/osc_plugins.rst | 12 +++++--- doc/source/contributor/index.rst | 7 ----- doc/source/index.rst | 11 +++---- 5 files changed, 55 insertions(+), 35 deletions(-) diff --git a/doc/source/cli/index.rst b/doc/source/cli/index.rst index c6efb1f4d..8ba5923b7 100644 --- a/doc/source/cli/index.rst +++ b/doc/source/cli/index.rst @@ -1,10 +1,3 @@ -:orphan: - -.. This page is to provide the top page for the CLI reference. - On the other hand, it looks better that the top level document has - direct links to individual pages for better navigation. - From that reason, :orphan: is needed to silence sphinx warning. - .. 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 @@ -27,19 +20,43 @@ ''''''' Heading 4 (Avoid deeper levels because they do not render well.) -============= -CLI reference -============= +========= +Using CLI +========= There are two CLIs which support the Networking API: -:doc:`neutron CLI ` and -`OpenStack Client (OSC) `__. -OpenStack Client provides the basic network commands and -python-neutronclient provides :doc:`extensions ` (aka OSC plugins) -for advanced networking services. +`OpenStackClient (OSC) +`__ +and :doc:`neutron CLI ` (deprecated). + +OpenStackClient +--------------- + +OpenStackClient provides +`the basic network commands `__ +and python-neutronclient provides :doc:`extensions ` +(aka OSC plugins) for advanced networking services. + +.. toctree:: + :maxdepth: 1 + + Basic network commands + Network commands for advanced networking services + Mapping Guide from neutron CLI + +neutron CLI +----------- + +.. warning:: + + neutron CLI is now deprecated and will be removed in the future. + Use openstack CLI instead. See `openstack CLI command list + `__ + and :doc:`its extensions for advanced networking services `. + The command mapping from neutron CLI to openstack CLI is available + `here `__. .. toctree:: :maxdepth: 2 - neutron CLI - Network extensions to OpenStack Client + neutron CLI guide diff --git a/doc/source/cli/neutron.rst b/doc/source/cli/neutron.rst index 23ceadd23..bc104ce94 100644 --- a/doc/source/cli/neutron.rst +++ b/doc/source/cli/neutron.rst @@ -26,6 +26,15 @@ Using neutron CLI The **neutron** shell utility interacts with OpenStack Networking API from the command-line. It supports the entire features of OpenStack Networking API. +.. warning:: + + neutron CLI is now deprecated and will be removed in the future. + Use openstack CLI instead. See `openstack CLI command list + `__ + and :doc:`its extensions for advanced networking services `. + The command mapping from neutron CLI to openstack CLI is available + `here `__. + Basic Usage ----------- diff --git a/doc/source/cli/osc_plugins.rst b/doc/source/cli/osc_plugins.rst index 343c3a9d3..0cd7e26ba 100644 --- a/doc/source/cli/osc_plugins.rst +++ b/doc/source/cli/osc_plugins.rst @@ -20,11 +20,15 @@ ''''''' Heading 4 (Avoid deeper levels because they do not render well.) -Using Network CLI extensions to OpenStack Client -================================================ +Advanced Network Commands in OpenStack Client +============================================= -List of released CLI commands available in openstack client. These commands -can be referenced by doing ``openstack help network``. +The following list covers the extended commands for advanced network +services available in ``openstack`` command. + +These commands can be referenced by doing ``openstack help`` and +the detail of individual command can be referred by +``openstack help ``. .. toctree:: :glob: diff --git a/doc/source/contributor/index.rst b/doc/source/contributor/index.rst index ddbd7e8a4..f2150b0e0 100644 --- a/doc/source/contributor/index.rst +++ b/doc/source/contributor/index.rst @@ -1,10 +1,3 @@ -:orphan: - -.. This page is to provide the top page for the contributor guide. - On the other hand, it looks better that the top level document has - direct links to individual pages for better navigation. - From that reason, :orphan: is needed to silence sphinx warning. - .. 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 diff --git a/doc/source/index.rst b/doc/source/index.rst index bde3f3c43..9b7a0efc9 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -38,11 +38,10 @@ User Documentation ------------------ .. toctree:: - :maxdepth: 1 + :maxdepth: 2 + cli/index reference/index - cli/neutron - cli/osc_plugins Contributor Guide ----------------- @@ -52,11 +51,9 @@ information on neutronclient's lower level programming details or APIs as well as the transition to OpenStack client. .. toctree:: - :maxdepth: 1 + :maxdepth: 2 - contributor/client_command_extensions - contributor/cli_option_guideline - contributor/transition_to_osc + contributor/index .. note:: From 423890a4950a725d6b493304d7b933d5b7c090d7 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Sun, 21 May 2017 00:59:46 +0900 Subject: [PATCH 597/845] doc: autogenerate OSC plugin command reference Recently cliff introduced a sphinx extension directive named autoprogram-cliff which genarates command-line help automatically. By using it, we no longer need to write command-line help document separately. Also fixes minor string substitution issue in a BGPVPN command. Note that the new cliff release with the directive is not released yet, but there are several number of OSC plugin commands proposed and having a local copy would help such developers. Change-Id: I6b1aee89f406ac449fbc43e210c4ca7ad901b19b --- doc/source/cli/osc/v2/firewall-group.rst | 228 +---------- doc/source/cli/osc/v2/firewall-policy.rst | 264 +----------- doc/source/cli/osc/v2/firewall-rule.rst | 296 +------------- doc/source/cli/osc/v2/network-trunk.rst | 173 +------- doc/source/cli/osc/v2/networking-bgpvpn.rst | 381 +----------------- doc/source/conf.py | 15 +- neutronclient/cliff_sphinxext.py | 307 ++++++++++++++ .../osc/v2/networking_bgpvpn/bgpvpn.py | 2 +- 8 files changed, 358 insertions(+), 1308 deletions(-) create mode 100644 neutronclient/cliff_sphinxext.py diff --git a/doc/source/cli/osc/v2/firewall-group.rst b/doc/source/cli/osc/v2/firewall-group.rst index b0c24a884..adb8d05d3 100644 --- a/doc/source/cli/osc/v2/firewall-group.rst +++ b/doc/source/cli/osc/v2/firewall-group.rst @@ -8,223 +8,23 @@ router ports within a project. Network v2 -firewall group create ---------------------- +.. 'firewall group *' cannot be used below as it matches 'firewall group rule + *' or 'firewall group policy *'. -Create a firewall group for a given project. +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: firewall group create -.. program:: firewall group create -.. code:: bash +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: firewall group delete - openstack firewall group create +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: firewall group list -.. _firewallgroup_create-firewallgroup: -.. option:: --name +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: firewall group set - Name for the firewall group. +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: firewall group show -.. option:: --enable - - Enable firewall group (default). - -.. option:: --disable - - Disable firewall group. - -.. option:: --public - - Make the firewall group public, which allows it to be used in all projects - (as opposed to the default, which is to restrict its use to the current - project). - -.. option:: --private - - Restrict use of the firewall group to the current project. - -.. option:: --project - - Owner's project (name or ID). - -.. option:: --project-domain - - Domain the project belongs to (name or ID). - This can be used in case collisions between project names exist. - -.. option:: --description - - A description of the firewall group. - -.. option:: --ingress-firewall-policy - - Ingress firewall policy (name or ID). - -.. option:: --no-ingress-firewall-policy - - Detach ingress firewall policy from the firewall group. - -.. option:: --egress-firewall-policy - - Egress firewall policy (name or ID). - -.. option:: --no-egress-firewall-policy - - Detach egress firewall policy from the firewall group. - -.. option:: --port - - Port(s) to apply firewall group (name or ID). - -.. option:: --no-port - - Detach all port from the firewall group. - -firewall group delete ---------------------- - -Delete firewall group(s) - -.. program:: firewall group delete -.. code:: bash - - openstack firewall group delete - [ ...] - -.. _firewallgroup_delete-firewallgroup: -.. describe:: - - Firewall group(s) to delete (name or ID). - -firewall group list -------------------- - -List all firewall groups - -.. program:: firewall group list -.. code:: bash - - openstack firewall group list - [--long] - -.. option:: --long - - List additional fields in output. - -firewall group set ------------------- - -Set firewall group properties - -.. program:: firewall group set -.. code:: bash - - openstack firewall group set - -.. _firewallgroup_set-firewallgroup: -.. describe:: - - Firewall group to set (name or ID). - -.. option:: --name - - Set firewall group name. - -.. option:: --enable - - Enable firewall group (default). - -.. option:: --disable - - Disable firewall group. - -.. option:: --public - - Make the firewall group public, which allows it to be used in all projects - (as opposed to the default, which is to restrict its use to the current - project). - -.. option:: --private - - Restrict use of the firewall group to the current project. - -.. option:: --description - - A description of the firewall group. - -.. option:: --ingress-firewall-policy - - Ingress firewall policy (name or ID). - -.. option:: --no-ingress-firewall-policy - - Detach ingress firewall policy from the firewall group. - -.. option:: --egress-firewall-policy - - Egress firewall policy (name or ID). - -.. option:: --no-egress-firewall-policy - - Detach egress firewall policy from the firewall group. - -.. option:: --port - - Port(s) to apply firewall group. - -.. option:: --no-port - - Detach all port from the firewall group. - -firewall group show -------------------- - -Show information of a given firewall group - -.. program:: firewall group show -.. code:: bash - - openstack firewall group show - - -.. _firewallgroup_show-firewallgroup: -.. describe:: - - Firewall group to display (name or ID). - -firewall group unset --------------------- - -Unset firewall group properties - -.. program:: firewall group unset -.. code:: bash - - openstack firewall group unset - -.. _firewallgroup_unset-firewallgroup: -.. describe:: - - Firewall group to unset (name or ID). - -.. option:: --enable - - Disable firewall group. - -.. option:: --public - - Restrict use of the firewall group to the current project. - -.. option:: --ingress-firewall-policy - - Detach ingress firewall policy from the firewall group. - -.. option:: --egress-firewall-policy - - Detach egress firewall policy from the firewall group. - -.. option:: --port - - Remove port(s) from the firewall group. - -.. option:: --all-port - - Remove all ports from the firewall group. +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: firewall group unset diff --git a/doc/source/cli/osc/v2/firewall-policy.rst b/doc/source/cli/osc/v2/firewall-policy.rst index c4a78dd07..f05f83451 100644 --- a/doc/source/cli/osc/v2/firewall-policy.rst +++ b/doc/source/cli/osc/v2/firewall-policy.rst @@ -10,265 +10,5 @@ which create or use the firewall group policy). Network v2 -firewall group policy create ----------------------------- - -Create a firewall policy for a given project - -.. program:: firewall group policy create -.. code:: bash - - openstack firewall group policy create - -.. _firewallpolicy_create-firewallpolicy: -.. describe:: - - Name for the firewall policy. - -.. option:: --enable - - Enable firewall policy (default). - -.. option:: --disable - - Disable firewall policy. - -.. option:: --public - - Make the firewall policy public, which allows it to be used in all projects - (as opposed to the default, which is to restrict its use to the current - project). - -.. option:: --private - - Restrict use of the firewall policy to the current project. - -.. option:: --project - - Owner's project (name or ID). - -.. option:: --project-domain - - Domain the project belongs to (name or ID). - This can be used in case collisions between project names exist. - -.. option:: --description - - A description of the firewall policy. - -.. option:: --firewall-rule - - Firewall rule(s) to apply (name or ID). - -.. option:: --no-firewall-rule - - Remove all firewall rules from the firewall policy. - -.. option:: --audited - - Enable auditing for the policy. - -.. option:: --no-audited - - Disable auditing for the policy. - - -firewall group policy delete ----------------------------- - -Delete a given firewall policy - -.. program:: firewall group policy delete -.. code:: bash - - openstack firewall group policy delete - [ ...] - -.. _firewallpolicy_delete-firewallpolicy: -.. describe:: - - Firewall policy(s) to delete (name or ID). - -firewall group policy list --------------------------- - -List all firewall policies - -.. program:: firewall group policy list -.. code:: bash - - openstack firewall group policy list - [--long] - -.. option:: --long - - List additional fields in output. - -firewall group policy set -------------------------- - -Set firewall policy properties - -.. program:: firewall group policy set -.. code:: bash - - openstack firewall group policy set - -.. _firewallpolicy_set-firewallpolicy: -.. describe:: - - Firewall policy to set (name or ID). - -.. option:: --name - - Set firewall policy name. - -.. option:: --enable - - Enable firewall policy (default). - -.. option:: --disable - - Disable firewall policy. - -.. option:: --public - - Make the firewall policy public, which allows it to be used in all projects - (as opposed to the default, which is to restrict its use to the current - project). - -.. option:: --private - - Restrict use of the firewall policy to the current project. - -.. option:: --project - - Owner's project (name or ID). - -.. option:: --project-domain - - Domain the project belongs to (name or ID). - This can be used in case collisions between project names exist. - -.. option:: --description - - A description of the firewall policy. - -.. option:: --firewall-rule - - Firewall rule(s) to apply (name or ID). - -.. option:: --no-firewall-rule - - Unset all firewall rules from firewall policy. - -.. option:: --audited - - Enable auditing for the policy. - -.. option:: --no-audited - - Disable auditing for the policy. - - -firewall group policy show --------------------------- - -Show information of a given firewall policy - -.. program:: firewall group policy show -.. code:: bash - - openstack firewall group policy show - - -.. _firewallpolicy_show-firewallpolicy: -.. describe:: - - Firewall policy to display (name or ID). - -firewall group policy unset ---------------------------- - -Unset firewall policy properties - -.. program:: firewall group policy unset -.. code:: bash - - openstack firewall group policy unset - -.. _firewallpolicy_unset-firewallpolicy: -.. describe:: - - Firewall policy to unset (name or ID). - -.. option:: --enable - - Disable firewall policy. - -.. option:: --public - - Restrict use of the firewall policy to the current project. - -.. option:: --firewall-rule - - Firewall rule(s) to unset (name or ID). - -.. option:: --all-firewall-rule - - Remove all firewall rules from the firewall policy. - -.. option:: --audited - - Disable auditing for the policy. - -firewall group policy add rule ------------------------------- - -Adds a firewall rule in a firewall policy relative to the position of other -rules. - -.. program:: firewall group policy add rule -.. code:: bash - - openstack firewall group policy add rule - - - -.. _firewallpolicy_add_rule-firewallpolicy: -.. describe:: - - Firewall policy to add rule (name or ID). - -.. describe:: - - Firewall rule to be inserted (name or ID). - -.. option:: --insert-after - - Insert the new rule after this existing rule (name or ID). - -.. option:: --insert-before - - Insert the new rule before this existing rule (name or ID). - -firewall group policy remove rule ---------------------------------- - -Removes a firewall rule from a firewall policy. - -.. program:: firewall group policy remove rule -.. code:: bash - - openstack firewall group policy remove rule - - - -.. _firewallpolicy_remove_rule-firewallpolicy: -.. describe:: - - Firewall policy to remove rule (name or ID). - -.. describe:: - - Firewall rule to remove from policy (name or ID). +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: firewall group policy * diff --git a/doc/source/cli/osc/v2/firewall-rule.rst b/doc/source/cli/osc/v2/firewall-rule.rst index fdb520684..2e75c775f 100644 --- a/doc/source/cli/osc/v2/firewall-rule.rst +++ b/doc/source/cli/osc/v2/firewall-rule.rst @@ -8,297 +8,5 @@ be taken on the matched data traffic. Network v2 -firewall group rule create --------------------------- - -Create a firewall rule for a given project - -.. program:: firewall group rule create -.. code:: bash - - openstack firewall group rule create - -.. option:: --name - - Set firewall rule name. - -.. option:: --enable - - Enable firewall rule (default). - -.. option:: --disable - - Disable firewall rule. - -.. option:: --public - - Make the firewall rule public, which allows it to be used in all projects - (as opposed to the default, which is to restrict its use to the current - project). - -.. option:: --private - - Restrict use of the firewall rule to the current project. - -.. option:: --project - - Owner's project (name or ID) - -.. option:: --project-domain - - Domain the project belongs to (name or ID). - This can be used in case collisions between project names exist. - -.. option:: --description - - A description of the firewall rule. - -.. option:: --protocol - - Protocol for the firewall rule ('tcp', 'udp', 'icmp', 'any'). - Default is 'any'. - -.. option:: --action - - Action for the firewall rule ('allow', 'deny', 'reject'). - Default is 'deny'. - -.. option:: --ip-version - - Set IP version 4 or 6 (default is 4). - -.. option:: --source-port - - Source port number or range - (integer in [1, 65535] or range like 123:456). - -.. option:: --no-source-port - - Detach source port number or range. - -.. option:: --destination-port - - Destination port number or range - (integer in [1, 65535] or range like 123:456). - -.. option:: --no-destination-port - - Detach destination port number or range. - -.. option:: --source-ip-address - - Source IP address or subnet. - -.. option:: --no-source-ip-address - - Detach source IP address. - -.. option:: --destination-ip-address - - Destination IP address or subnet. - -.. option:: --no-destination-ip-address - - Detach destination IP address. - -.. option:: --enable-rule - - Enable this rule (default is enabled). - -.. option:: --disable-rule - - Disable this rule. - -firewall group rule delete --------------------------- - -Delete a given firewall rule - -.. program:: firewall group rule delete -.. code:: bash - - openstack firewall group rule delete - [ ...] - -.. _firewallrule_delete-firewallrule: -.. describe:: - - Firewall rule(s) to delete (name or ID). - -firewall group rule list ------------------------- - -List all firewall rules - -.. program:: firewall group rule list -.. code:: bash - - openstack firewall group rule list - [--long] - -.. option:: --long - - List additional fields in output. - -firewall group rule set ------------------------ - -Set firewall rule properties - -.. program:: firewall group rule set -.. code:: bash - - openstack firewall group rule set - -.. _firewallrule_set-firewallrule: -.. describe:: - - Firewall rule to set (name or ID). - -.. option:: --name - - Set firewall rule name. - -.. option:: --enable - - Enable firewall rule (default). - -.. option:: --disable - - Disable firewall rule. - -.. option:: --public - - Make the firewall rule public, which allows it to be used in all projects - (as opposed to the default, which is to restrict its use to the current - project). - -.. option:: --private - - Restrict use of the firewall rule to the current project. - -.. option:: --project - - Owner's project (name or ID). - -.. option:: --project-domain - - Domain the project belongs to (name or ID). - This can be used in case collisions between project names exist. - -.. option:: --description - - A description of the firewall rule. - -.. option:: --protocol - - Protocol for the firewall rule ('tcp', 'udp', 'icmp', 'any'). - -.. option:: --action - - Action for the firewall rule ('allow', 'deny', 'reject'). - -.. option:: --ip-version - - Set IP version 4 or 6 (default is 4). - -.. option:: --source-port - - Source port number or range - (integer in [1, 65535] or range like 123:456). - -.. option:: --no-source-port - - Detach source port number or range. - -.. option:: --destination-port - - Destination port number or range - (integer in [1, 65535] or range like 123:456). - -.. option:: --no-destination-port - - Detach destination port number or range. - -.. option:: --source-ip-address - - Source IP address or subnet. - -.. option:: --no-source-ip-address - - Detach source IP address. - -.. option:: --destination-ip-address - - Destination IP address or subnet. - -.. option:: --no-destination-ip-address - - Detach destination IP address. - -.. option:: --enable-rule - - Enable this rule (default is enabled). - -.. option:: --disable-rule - - Disable this rule. - -firewall group rule show ------------------------- - -Show information of a given firewall rule - -.. program:: firewall group rule show -.. code:: bash - - openstack firewall group rule show - - -.. _firewallrule_show-firewallrule: -.. describe:: - - Firewall rule to display (name or ID). - -firewall group rule unset -------------------------- - -Unset firewall rule properties - -.. program:: firewall group rule unset -.. code:: bash - - openstack firewall group rule unset - -.. _firewallrule_unset-firewallrule: -.. describe:: - - Firewall rule to unset (name or ID). - -.. option:: --enable - - Disable firewall rule. - -.. option:: --public - - Restrict use of the firewall rule to the current project. - -.. option:: --source-port - - Detach source port number or range. - -.. option:: --destination-port - - Detach destination port number or range. - -.. option:: --source-ip-address - - Detach source IP address. - -.. option:: --destination-ip-address - - Detach destination IP address. - -.. option:: --enable-rule - - Disable this rule. +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: firewall group rule * diff --git a/doc/source/cli/osc/v2/network-trunk.rst b/doc/source/cli/osc/v2/network-trunk.rst index 06a63d209..22144a4ee 100644 --- a/doc/source/cli/osc/v2/network-trunk.rst +++ b/doc/source/cli/osc/v2/network-trunk.rst @@ -9,173 +9,8 @@ the server to connect to more networks. Network v2 -network subport list --------------------- +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: network subport list -List all subports for a given network trunk - -.. program:: network subport list -.. code:: bash - - openstack network subport list - --trunk - -.. option:: --trunk - - List subports belonging to this trunk (name or ID) (required) - -network trunk create --------------------- - -Create a network trunk for a given project - -.. program:: network trunk create -.. code:: bash - - openstack network trunk create - --parent-port - [--subport ] - [--enable | --disable] - [--project [--project-domain ]] - [--description ] - - -.. option:: --parent-port - - Parent port belonging to this trunk (name or ID) (required) - -.. option:: --subport - - Subport to add. Subport is of form 'port=,segmentation-type=,segmentation-ID=' - (--subport) option can be repeated - -.. option:: --enable - - Enable trunk (default) - -.. option:: --disable - - Disable trunk - -.. option:: --project - - Owner's project (name or ID) - -.. option:: --project-domain - - Domain the project belongs to (name or ID). - This can be used in case collisions between project names exist. - -.. option:: --description - - A description of the trunk. - -network trunk delete --------------------- - -Delete a given network trunk - -.. program:: network trunk delete -.. code:: bash - - openstack network trunk delete - [ ...] - -.. _network_trunk_delete-trunk: -.. describe:: - - Trunk(s) to delete (name or ID) - -network trunk list ------------------- - -List all network trunks - -.. program:: network trunk list -.. code:: bash - - openstack network trunk list - [--long] - -.. option:: --long - - List additional fields in output - -network trunk set ------------------ - -Set network trunk properties - -.. program:: network trunk set -.. code:: bash - - openstack network trunk set - [--name ] - [--description ] - [--subport ] - [--enable | --disable] - - -.. option:: --name - - Set trunk name - -.. option:: --description - - A description of the trunk. - -.. option:: --subport - - Subport to add. Subport is of form 'port=,segmentation-type=,segmentation-ID=' - (--subport) option can be repeated - -.. option:: --enable - - Enable trunk - -.. option:: --disable - - Disable trunk - -.. _network_trunk_set-trunk: -.. describe:: - - Trunk to modify (name or ID) - -network trunk show ------------------- - -Show information of a given network trunk - -.. program:: network trunk show -.. code:: bash - - openstack network trunk show - - -.. _network_trunk_show-trunk: -.. describe:: - - Trunk to display (name or ID) - -network trunk unset -------------------- - -Unset subports from a given network trunk - -.. program:: network trunk unset -.. code:: bash - - openstack network trunk unset - --subport - - -.. option:: --subport - - Subport to delete (name or ID of the port) (required) - (--subport) option can be repeated - -.. _network_trunk_unset-trunk: -.. describe:: - - Unset subports from this trunk (name or ID) +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: network trunk * diff --git a/doc/source/cli/osc/v2/networking-bgpvpn.rst b/doc/source/cli/osc/v2/networking-bgpvpn.rst index 69b3150db..371e47287 100644 --- a/doc/source/cli/osc/v2/networking-bgpvpn.rst +++ b/doc/source/cli/osc/v2/networking-bgpvpn.rst @@ -9,375 +9,26 @@ between L3VPNs and Neutron resources, i.e. Networks, Routers and Ports. Network v2 -bgpvpn create -------------- +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgpvpn create -Create a BGP VPN resource for a given project +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgpvpn delete -.. program:: bgpvpn create -.. code:: bash +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgpvpn list - openstack bgpvpn create +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgpvpn set -.. _bgpvpn_create-bgpvpn: -.. option:: --project +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgpvpn show - Owner's project (name or ID) +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgpvpn unset -.. option:: --project-domain +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgpvpn network association * - Domain the project belongs to (name or ID). This can be used in case - collisions between project names exist - -.. option:: --name - - Name for the BGP VPN. - -.. option:: --route-target - - Add Route Target to import list (repeat option for multiple Route Targets) - -.. option:: --import-target - - Add Route Target to import list (repeat option for multiple Route Targets) - -.. option:: --export-target - - Add Route Target to export list (repeat option for multiple RouteTargets) - -.. option:: --route-distinguisher - - Add Route Distinguisher to the list of Route Distinguishers from which a - Route Distinguishers will be picked from to advertise a VPN route (repeat - option for multiple Route Distinguishers) - -.. option:: --type {l2,l3} - - BGP VPN type selection between IP VPN (l3) and Ethernet VPN (l2) - (default: l3) - -bgpvpn set ----------- - -Set BGP VPN properties - -.. program:: bgpvpn set -.. code:: bash - - openstack bgpvpn set - -.. _bgpvpn_set-bgpvpn: -.. describe:: - - BGP VPN to update (name or ID) - -.. option:: --name - - Name for the BGP VPN - -.. option:: --route-target - - Add Route Target to import list (repeat option for multiple Route Targets) - -.. option:: --no-route-target - - Empty route target list. - -.. option:: --import-target - - Add Route Target to import list (repeat option for multiple Route Targets) - -.. option:: --no-import-target - - Empty import route target list - -.. option:: --export-target - - Add Route Target to export list (repeat option for multiple Route Targets) - -.. option:: --no-export-target - - Empty export route target list - -.. option:: --route-distinguisher - - Add Route Distinguisher to the list of Route Distinguishers from which a - Route Distinguishers will be picked from to advertise a VPN route (repeat - option for multiple Route Distinguishers) - -.. option:: --no-route-distinguisher - - Empty route distinguisher list - -bgpvpn unset ------------- - -Unset BGP VPN properties - -.. program:: bgpvpn unset -.. code:: bash - - openstack bgpvpn unset - -.. _bgpvpn_unset-bgpvpn: -.. describe:: - - BGP VPN to update (name or ID) - -.. option:: --route-target - - Remove Route Target from import/export list (repeat option for multiple - Route Targets) - -.. option:: --all-route-target - - Empty route target list - -.. option:: --import-target - - Remove Route Target from import list (repeat option for multiple Route - Targets) - -.. option:: --all-import-target - - Empty import route target list - -.. option:: --export-target - - Remove Route Target from export list (repeat option for multiple Route - Targets) - -.. option:: --all-export-target - - Empty export route target list - -.. option:: --route-distinguisher - - Remove Route Distinguisher from the list of Route Distinguishers from which - a Route Distinguishers will be picked from to advertise a VPN route - (repeat option for multiple Route Distinguishers) - -.. option:: --all-route-distinguisher - - Empty route distinguisher list - -bgpvpn delete -------------- - -Delete BGP VPN resource(s) - -.. program:: bgpvpn delete -.. code:: bash - - openstack bgpvpn delete - [ ...] - -.. _bgpvpn_delete-bgpvpn: -.. describe:: - BGP VPN(s) to delete (name or ID) - -bgpvpn list ------------ - -List BGP VPN resources - -.. program:: bgpvpn list -.. code:: bash - - openstack bgpvpn list - -.. _bgpvpn_list-bgpvpn: -.. option:: --project - - Owner's project (name or ID) - -.. option:: --project-domain - - Domain the project belongs to (name or ID). This can be used in case - collisions between project names exist. - -.. option:: --long - - List additional fields in output - -.. option:: --property - - Filter property to apply on returned BGP VPNs (repeat to filter on multiple - properties) - -bgpvpn show ------------ - -Show information of a given BGP VPN - -.. program:: bgpvpn show -.. code:: bash - - openstack bgpvpn show - -.. _bgpvpn_show-bgpvpn: -.. describe:: - - BGP VPN to display (name or ID) - -bgpvpn network association create ---------------------------------- - -Create a BGP VPN network association - -.. program:: bgpvpn network association create -.. code:: bash - - openstack bgpvpn network association create - -.. _bgpvpn_net-assoc_create-bgpvpn: -.. describe:: - - ID or name of the BGP VPN - -.. describe:: - - ID or name of the network - -.. option:: --project - - Owner's project (name or ID) - -.. option:: --project-domain - - Domain the project belongs to (name or ID). This can be used in case - collisions between project names exist. - -bgpvpn network association delete ---------------------------------- - -Remove a BGP VPN network association(s) for a given BGP VPN - -.. program:: bgpvpn network association delete -.. code:: bash - - openstack bgpvpn network association delete - [ ...] - -.. _bgpvpn_net-assoc_delete-bgpvpn: -.. describe:: - ID(s) of the network association(s) to remove - -.. describe:: - ID or name of the BGP VPN - -bgpvpn network association list -------------------------------- - -List BGP VPN network associations for a given BGP VPN - -.. program:: bgpvpn network association list -.. code:: bash - - openstack bgpvpn network association list - -.. _bgpvpn_net-assoc_list-bgpvpn: -.. describe:: - ID or name of the BGP VPN - -.. option:: --long - - List additional fields in output - -bgpvpn network association show -------------------------------- - -Show information of a given BGP VPN network association - -.. program:: bgpvpn network association show -.. code:: bash - - openstack bgpvpn network association show - -.. _bgpvpn_net-assoc_show-bgpvpn: -.. describe:: - ID of the network association to look up - -.. describe:: - BGP VPN the association belongs to (name or ID) - -bgpvpn router association create --------------------------------- - -Create a BGP VPN router association - -.. program:: bgpvpn router association create -.. code:: bash - - openstack bgpvpn router association create - -.. _bgpvpn_router-assoc_create-bgpvpn: -.. describe:: - - ID or name of the BGP VPN - -.. describe:: - - ID or name of the router. - -.. option:: --project - - Owner's project (name or ID) - -.. option:: --project-domain - - Domain the project belongs to (name or ID). This can be used in case - collisions between project names exist. - -bgpvpn router association delete --------------------------------- - -Delete a BGP VPN router association(s) for a given BGP VPN - -.. program:: bgpvpn router association delete -.. code:: bash - - openstack bgpvpn router association delete - [ ...] - -.. _bgpvpn_router-assoc_delete-bgpvpn: -.. describe:: - ID(s) of the router association(s) to delete. - -.. describe:: - ID or name of the BGP VPN - -bgpvpn router association list ------------------------------- - -List BGP VPN router associations for a given BGP VPN - -.. program:: bgpvpn router association list -.. code:: bash - - openstack bgpvpn router association list - -.. _bgpvpn_router-assoc_list-bgpvpn: -.. describe:: - ID or name of the BGP VPN - -.. option:: --long - - List additional fields in output - -bgpvpn router association show ------------------------------- - -Show information of a given BGP VPN router association - -.. program:: bgpvpn router association show -.. code:: bash - - openstack bgpvpn router association show - -.. _bgpvpn_router-assoc_show-bgpvpn: -.. describe:: - ID of the router association to look up - -.. describe:: - BGP VPN the association belongs to (name or ID) +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgpvpn router association * diff --git a/doc/source/conf.py b/doc/source/conf.py index 4043a8266..1bcb1db59 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -5,9 +5,14 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', - 'reno.sphinxext', - 'openstackdocstheme', +extensions = [ + 'sphinx.ext.autodoc', + 'reno.sphinxext', + 'openstackdocstheme', + # 'cliff.sphinxext', + # TODO(amotoki): Switch to cliff.sphinxext once cliff bug is fixed. + # https://bugs.launchpad.net/python-cliff/+bug/1692018 + 'neutronclient.cliff_sphinxext', ] # openstackdocstheme options @@ -46,3 +51,7 @@ # Output file base name for HTML help builder. htmlhelp_basename = 'neutronclientdoc' + +# -- Options for cliff.sphinxext plugin --------------------------------------- + +autoprogram_cliff_application = 'openstack' diff --git a/neutronclient/cliff_sphinxext.py b/neutronclient/cliff_sphinxext.py new file mode 100644 index 000000000..16915fac0 --- /dev/null +++ b/neutronclient/cliff_sphinxext.py @@ -0,0 +1,307 @@ +# Copyright (C) 2017, Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import argparse +import fnmatch +import re + +from docutils import nodes +from docutils.parsers import rst +from docutils.parsers.rst import directives +from docutils import statemachine + +from cliff import commandmanager + + +def _indent(text): + """Indent by four spaces.""" + prefix = ' ' * 4 + + def prefixed_lines(): + for line in text.splitlines(True): + yield (prefix + line if line.strip() else line) + + return ''.join(prefixed_lines()) + + +def _format_description(parser): + """Get parser description. + + We parse this as reStructuredText, allowing users to embed rich + information in their help messages if they so choose. + """ + for line in statemachine.string2lines( + parser.description, tab_width=4, convert_whitespace=True): + yield line + + +def _format_usage(parser): + """Get usage without a prefix.""" + fmt = argparse.HelpFormatter(parser.prog) + + optionals = parser._get_optional_actions() + positionals = parser._get_positional_actions() + groups = parser._mutually_exclusive_groups + + # hacked variant of the regex used by the actual argparse module. Unlike + # that version, this one attempts to group long and short opts with their + # optional arguments ensuring that, for example, '---format ' + # becomes ['--format '] and not ['--format', '']. + # Yes, they really do use regexes to break apart and rewrap their help + # string. Don't ask me why. + part_regexp = r'\(.*?\)+|\[.*?\]+|(?:(?:-\w|--\w+)(?:\s+<\w+>)?)|\S+' + + opt_usage = fmt._format_actions_usage(optionals, groups) + pos_usage = fmt._format_actions_usage(positionals, groups) + + opt_parts = re.findall(part_regexp, opt_usage) + pos_parts = re.findall(part_regexp, pos_usage) + parts = opt_parts + pos_parts + + if len(' '.join([parser.prog] + parts)) < 72: + return [' '.join([parser.prog] + parts)] + + return [parser.prog] + [_indent(x) for x in parts] + + +def _format_epilog(parser): + """Get parser epilog. + + We parse this as reStructuredText, allowing users to embed rich + information in their help messages if they so choose. + """ + for line in statemachine.string2lines( + parser.epilog, tab_width=4, convert_whitespace=True): + yield line + + +def _format_positional_action(action): + """Format a positional action.""" + if action.help == argparse.SUPPRESS: + return + + # NOTE(stephenfin): We strip all types of brackets from 'metavar' because + # the 'option' directive dictates that only option argument names should be + # surrounded by angle brackets + yield '.. option:: {}'.format( + (action.metavar or action.dest).strip('<>[]() ')) + if action.help: + yield '' + for line in statemachine.string2lines( + action.help, tab_width=4, convert_whitespace=True): + yield _indent(line) + + +def _format_optional_action(action): + """Format an optional action.""" + if action.help == argparse.SUPPRESS: + return + + if action.nargs == 0: + yield '.. option:: {}'.format(', '.join(action.option_strings)) + else: + # TODO(stephenfin): At some point, we may wish to provide more + # information about the options themselves, for example, if nargs is + # specified + option_strings = [' '.join( + [x, action.metavar or '<{}>'.format(action.dest.upper())]) + for x in action.option_strings] + yield '.. option:: {}'.format(', '.join(option_strings)) + + if action.help: + yield '' + for line in statemachine.string2lines( + action.help, tab_width=4, convert_whitespace=True): + yield _indent(line) + + +def _format_parser(parser): + """Format the output of an argparse 'ArgumentParser' object. + + Given the following parser:: + + >>> import argparse + >>> parser = argparse.ArgumentParser(prog='hello-world', \ + description='This is my description.', + epilog='This is my epilog') + >>> parser.add_argument('name', help='User name', metavar='') + >>> parser.add_argument('--language', action='store', dest='lang', \ + help='Greeting language') + + Returns the following:: + + This is my description. + + .. program:: hello-world + .. code:: shell + + hello-world [-h] [--language LANG] + + .. option:: name + + User name + + .. option:: --language LANG + + Greeting language + + .. option:: -h, --help + + Show this help message and exit + + This is my epilog. + """ + if parser.description: + for line in _format_description(parser): + yield line + yield '' + + yield '.. program:: {}'.format(parser.prog) + + yield '.. code-block:: shell' + yield '' + for line in _format_usage(parser): + yield _indent(line) + yield '' + + # In argparse, all arguments and parameters are known as "actions". + # Optional actions are what would be known as flags or options in other + # libraries, while positional actions would generally be known as + # arguments. We present these slightly differently. + + for action in parser._get_optional_actions(): + for line in _format_optional_action(action): + yield line + yield '' + + for action in parser._get_positional_actions(): + for line in _format_positional_action(action): + yield line + yield '' + + if parser.epilog: + for line in _format_epilog(parser): + yield line + yield '' + + +class AutoprogramCliffDirective(rst.Directive): + """Auto-document a subclass of `cliff.command.Command`.""" + + has_content = False + required_arguments = 1 + option_spec = { + 'command': directives.unchanged, + 'ignored': directives.unchanged, + 'application': directives.unchanged, + } + + def _load_command(self, manager, command_name): + """Load a command using an instance of a `CommandManager`.""" + try: + # find_command expects the value of argv so split to emulate that + return manager.find_command(command_name.split())[0] + except ValueError: + raise self.error('"{}" is not a valid command in the "{}" ' + 'namespace'.format( + command_name, manager.namespace)) + + def _generate_nodes(self, title, command_name, command_class, + ignored_opts): + """Generate the relevant Sphinx nodes. + + This is a little funky. Parts of this use raw docutils nodes while + other parts use reStructuredText and nested parsing. The reason for + this is simple: it avoids us having to reinvent the wheel. While raw + docutils nodes are helpful for the simpler elements of the output, + they don't provide an easy way to use Sphinx's own directives, such as + the 'option' directive. Refer to [1] for more information. + + [1] http://www.sphinx-doc.org/en/stable/extdev/markupapi.html + + :param title: Title of command + :param command_name: Name of command, as used on the command line + :param command_class: Subclass of :py:class:`cliff.command.Command` + :param prefix: Prefix to apply before command, if any + :param ignored_opts: A list of options to exclude from output, if any + :returns: A list of nested docutil nodes + """ + command = command_class(None, None) + parser = command.get_parser(command_name) + ignored_opts = ignored_opts or [] + + # Drop the automatically-added help action + for action in list(parser._actions): + for option_string in action.option_strings: + if option_string in ignored_opts: + del parser._actions[parser._actions.index(action)] + break + + section = nodes.section( + '', + nodes.title(text=title), + ids=[nodes.make_id(title)], + names=[nodes.fully_normalize_name(title)]) + + source_name = '<{}>'.format(command.__class__.__name__) + result = statemachine.ViewList() + + for line in _format_parser(parser): + result.append(line, source_name) + + self.state.nested_parse(result, 0, section) + + return [section] + + def run(self): + self.env = self.state.document.settings.env + + command_pattern = self.options.get('command') + application_name = (self.options.get('application') + or self.env.config.autoprogram_cliff_application) + + global_ignored = self.env.config.autoprogram_cliff_ignored + local_ignored = self.options.get('ignored', '') + local_ignored = [x.strip() for x in local_ignored.split(',') + if x.strip()] + ignored_opts = list(set(global_ignored + local_ignored)) + + # TODO(sfinucan): We should probably add this wildcarding functionality + # to the CommandManager itself to allow things like "show me the + # commands like 'foo *'" + manager = commandmanager.CommandManager(self.arguments[0]) + if command_pattern: + commands = [x for x in manager.commands + if fnmatch.fnmatch(x, command_pattern)] + else: + commands = manager.commands.keys() + + output = [] + for command_name in sorted(commands): + command_class = self._load_command(manager, command_name) + + title = command_name + if application_name: + command_name = ' '.join([application_name, command_name]) + + output.extend(self._generate_nodes( + title, command_name, command_class, ignored_opts)) + + return output + + +def setup(app): + app.add_directive('autoprogram-cliff', AutoprogramCliffDirective) + app.add_config_value('autoprogram_cliff_application', '', True) + app.add_config_value('autoprogram_cliff_ignored', ['--help'], True) diff --git a/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py b/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py index 96d72ddfe..ab49c2ff5 100644 --- a/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py +++ b/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py @@ -216,7 +216,7 @@ def get_parser(self, prog_name): default='l3', choices=['l2', 'l3'], help=_("BGP VPN type selection between IP VPN (l3) and Ethernet " - "VPN (l2) (default: %(default)s)"), + "VPN (l2) (default: l3)"), ) return parser From f2a4d5253ccaeababd4163960b8548f794ade708 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Mon, 3 Jul 2017 03:30:23 +0000 Subject: [PATCH 598/845] doc: autogenerate neutron CLI reference This replaces the doc-migration work. We can avoid the maintenance cost by generating CLI reference for neutron CLI like this. This patch introduces a new sphinx directive to render CLI global options to achieve the goal. This can be migrated to cliff sphinxext later. Change-Id: I7089df3f7fc6475ebdd1e71221492baf12f18226 --- doc/source/cli/index.rst | 1 + doc/source/cli/neutron-reference.rst | 48 +++++++++++++++++ neutronclient/cliff_sphinxext.py | 78 ++++++++++++++++++++++++++++ neutronclient/shell.py | 4 +- 4 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 doc/source/cli/neutron-reference.rst diff --git a/doc/source/cli/index.rst b/doc/source/cli/index.rst index 8ba5923b7..f53a83acb 100644 --- a/doc/source/cli/index.rst +++ b/doc/source/cli/index.rst @@ -60,3 +60,4 @@ neutron CLI :maxdepth: 2 neutron CLI guide + neutron CLI reference diff --git a/doc/source/cli/neutron-reference.rst b/doc/source/cli/neutron-reference.rst new file mode 100644 index 000000000..24359a5eb --- /dev/null +++ b/doc/source/cli/neutron-reference.rst @@ -0,0 +1,48 @@ +.. + 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. + + + Convention for heading levels in Neutron devref: + ======= Heading 0 (reserved for the title in a document) + ------- Heading 1 + ~~~~~~~ Heading 2 + +++++++ Heading 3 + ''''''' Heading 4 + (Avoid deeper levels because they do not render well.) + +===================== +neutron CLI reference +===================== + +.. warning:: + + neutron CLI is now deprecated and will be removed in the future. + Use openstack CLI instead. See `openstack CLI command list + `__ + and :doc:`its extensions for advanced networking services `. + The command mapping from neutron CLI to openstack CLI is available + `here `__. + +neutron usage +------------- + +.. cliff-app:: neutronclient.shell.NeutronShell + :application: neutron + :arguments: 2.0 + +neutron API v2.0 commands +------------------------- + +.. autoprogram-cliff:: neutron.cli.v2 + :application: neutron + diff --git a/neutronclient/cliff_sphinxext.py b/neutronclient/cliff_sphinxext.py index 16915fac0..ca4756c38 100644 --- a/neutronclient/cliff_sphinxext.py +++ b/neutronclient/cliff_sphinxext.py @@ -20,6 +20,7 @@ from docutils.parsers import rst from docutils.parsers.rst import directives from docutils import statemachine +from oslo_utils import importutils from cliff import commandmanager @@ -301,7 +302,84 @@ def run(self): return output +class CliffAppDirective(rst.Directive): + """Auto-document a `cliff.app.App`.""" + + has_content = False + required_arguments = 1 + option_spec = { + 'arguments': directives.unchanged, + 'ignored': directives.unchanged, + 'application': directives.unchanged, + } + + def _generate_nodes(self, title, app, app_name, ignored_opts): + """Generate the relevant Sphinx nodes. + + This is a little funky. Parts of this use raw docutils nodes while + other parts use reStructuredText and nested parsing. The reason for + this is simple: it avoids us having to reinvent the wheel. While raw + docutils nodes are helpful for the simpler elements of the output, + they don't provide an easy way to use Sphinx's own directives, such as + the 'option' directive. Refer to [1] for more information. + + [1] http://www.sphinx-doc.org/en/stable/extdev/markupapi.html + + :param title: Title of command + :param app: Subclass of :py:class`cliff.app.App` + :param app_name: The name of the cliff application. + This is used as the command name. + :param ignored_opts: A list of options to exclude from output, if any + :returns: A list of docutil nodes + """ + parser = app.parser + ignored_opts = ignored_opts or [] + + # Drop the automatically-added help action + for action in list(parser._actions): + for option_string in action.option_strings: + if option_string in ignored_opts: + del parser._actions[parser._actions.index(action)] + break + + parser.prog = app_name + + source_name = '<{}>'.format(app.__class__.__name__) + result = statemachine.ViewList() + for line in _format_parser(parser): + result.append(line, source_name) + + section = nodes.section() + self.state.nested_parse(result, 0, section) + return section.children + + def run(self): + self.env = self.state.document.settings.env + + cliff_app_class = importutils.import_class(self.arguments[0]) + app_arguments = self.options.get('arguments', '').split() + cliff_app = cliff_app_class(*app_arguments) + + application_name = (self.options.get('application') + or self.env.config.autoprogram_cliff_application) + + global_ignored = self.env.config.autoprogram_cliff_ignored + local_ignored = self.options.get('ignored', '') + local_ignored = [x.strip() for x in local_ignored.split(',') + if x.strip()] + ignored_opts = list(set(global_ignored + local_ignored)) + + output = [] + title = application_name + output.extend(self._generate_nodes( + title, cliff_app, application_name, ignored_opts)) + + return output + + def setup(app): app.add_directive('autoprogram-cliff', AutoprogramCliffDirective) app.add_config_value('autoprogram_cliff_application', '', True) app.add_config_value('autoprogram_cliff_ignored', ['--help'], True) + + app.add_directive('cliff-app', CliffAppDirective) diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 017d642c1..51b06d3d1 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -149,8 +149,10 @@ class NeutronShell(app.App): def __init__(self, apiversion): namespace = NAMESPACE_MAP[apiversion] + description = (__doc__.strip() + + " (neutron CLI version: %s)" % __version__) super(NeutronShell, self).__init__( - description=__doc__.strip(), + description=description, version=VERSION, command_manager=commandmanager.CommandManager(namespace), ) From e1c98e1b7ad35c1846f0af18d3e9c72ab11eab3e Mon Sep 17 00:00:00 2001 From: Mohankumar Date: Mon, 12 Dec 2016 17:16:10 +0530 Subject: [PATCH 599/845] Add SFC cli for OSC plugin Implements: blueprint openstackclient-cli-porting Change-Id: Ifeb62bad26ffeb0bb8b548c56d2d6a620a922f78 --- doc/source/cli/osc/v2/networking-sfc.rst | 36 ++ neutronclient/osc/v2/sfc/__init__.py | 0 .../osc/v2/sfc/sfc_flow_classifier.py | 320 ++++++++++ neutronclient/osc/v2/sfc/sfc_port_chain.py | 347 +++++++++++ neutronclient/osc/v2/sfc/sfc_port_pair.py | 215 +++++++ .../osc/v2/sfc/sfc_port_pair_group.py | 291 +++++++++ .../tests/unit/osc/v2/sfc/__init__.py | 0 neutronclient/tests/unit/osc/v2/sfc/fakes.py | 234 ++++++++ .../unit/osc/v2/sfc/test_flow_classifier.py | 378 ++++++++++++ .../tests/unit/osc/v2/sfc/test_port_chain.py | 556 ++++++++++++++++++ .../tests/unit/osc/v2/sfc/test_port_pair.py | 302 ++++++++++ .../unit/osc/v2/sfc/test_port_pair_group.py | 424 +++++++++++++ neutronclient/v2_0/client.py | 103 ++++ releasenotes/notes/add-sfc-commands.yaml | 5 + setup.cfg | 22 + 15 files changed, 3233 insertions(+) create mode 100644 doc/source/cli/osc/v2/networking-sfc.rst create mode 100644 neutronclient/osc/v2/sfc/__init__.py create mode 100755 neutronclient/osc/v2/sfc/sfc_flow_classifier.py create mode 100755 neutronclient/osc/v2/sfc/sfc_port_chain.py create mode 100755 neutronclient/osc/v2/sfc/sfc_port_pair.py create mode 100755 neutronclient/osc/v2/sfc/sfc_port_pair_group.py create mode 100644 neutronclient/tests/unit/osc/v2/sfc/__init__.py create mode 100755 neutronclient/tests/unit/osc/v2/sfc/fakes.py create mode 100755 neutronclient/tests/unit/osc/v2/sfc/test_flow_classifier.py create mode 100755 neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py create mode 100755 neutronclient/tests/unit/osc/v2/sfc/test_port_pair.py create mode 100755 neutronclient/tests/unit/osc/v2/sfc/test_port_pair_group.py create mode 100644 releasenotes/notes/add-sfc-commands.yaml diff --git a/doc/source/cli/osc/v2/networking-sfc.rst b/doc/source/cli/osc/v2/networking-sfc.rst new file mode 100644 index 000000000..09d13d6b6 --- /dev/null +++ b/doc/source/cli/osc/v2/networking-sfc.rst @@ -0,0 +1,36 @@ +============== +networking sfc +============== + +**Service Function Chaining** is a mechanism for overriding the basic destination based forwarding +that is typical of IP networks. Service Function Chains consist of an ordered sequence of +Service Functions (SFs). SFs are virtual machines (or potentially physical devices) that perform a +network function such as firewall, content cache, packet inspection, or any other function that +requires processing of packets in a flow from point A to point B even though the SFs are not +literally between point A and B from a routing table perspective. + +Network v2 + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: sfc flow classifier * + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: sfc port chain * + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: sfc port pair create + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: sfc port pair delete + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: sfc port pair list + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: sfc port pair set + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: sfc port pair show + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: sfc port pair group * diff --git a/neutronclient/osc/v2/sfc/__init__.py b/neutronclient/osc/v2/sfc/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/osc/v2/sfc/sfc_flow_classifier.py b/neutronclient/osc/v2/sfc/sfc_flow_classifier.py new file mode 100755 index 000000000..38a3cbb94 --- /dev/null +++ b/neutronclient/osc/v2/sfc/sfc_flow_classifier.py @@ -0,0 +1,320 @@ +# Copyright (c) 2017 Huawei Technologies India Pvt.Limited. +# 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 argparse +import logging + +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils + +from neutronclient._i18n import _ +from neutronclient.common import exceptions as nc_exc +from neutronclient.osc import utils as nc_osc_utils + +LOG = logging.getLogger(__name__) + +resource = 'flow_classifier' + +_attr_map = ( + ('id', 'ID', nc_osc_utils.LIST_BOTH), + ('name', 'Name', nc_osc_utils.LIST_BOTH), + ('summary', 'Summary', nc_osc_utils.LIST_SHORT_ONLY), + ('protocol', 'Protocol', nc_osc_utils.LIST_LONG_ONLY), + ('ethertype', 'Ethertype', nc_osc_utils.LIST_LONG_ONLY), + ('source_ip_prefix', 'Source IP', + nc_osc_utils.LIST_LONG_ONLY), + ('destination_ip_prefix', 'Destination IP', + nc_osc_utils.LIST_LONG_ONLY), + ('logical_source_port', 'Logical Source Port', + nc_osc_utils.LIST_LONG_ONLY), + ('logical_destination_port', 'Logical Destination Port', + nc_osc_utils.LIST_LONG_ONLY), + ('source_port_range_min', 'Source Port Range Min', + nc_osc_utils.LIST_LONG_ONLY), + ('source_port_range_max', 'Source Port Range Max', + nc_osc_utils.LIST_LONG_ONLY), + ('destination_port_range_min', 'Destination Port Range Min', + nc_osc_utils.LIST_LONG_ONLY), + ('destination_port_range_max', 'Destination Port Range Max', + nc_osc_utils.LIST_LONG_ONLY), + ('l7_parameters', 'L7 Parameters', nc_osc_utils.LIST_LONG_ONLY), + ('description', 'Description', nc_osc_utils.LIST_LONG_ONLY), + ('project_id', 'Project', nc_osc_utils.LIST_LONG_ONLY), +) + + +class CreateSfcFlowClassifier(command.ShowOne): + _description = _("Create a flow classifier") + + def get_parser(self, prog_name): + parser = super(CreateSfcFlowClassifier, self).get_parser(prog_name) + parser.add_argument( + 'name', + metavar='', + help=_('Name of the flow classifier')) + parser.add_argument( + '--description', + metavar='', + help=_('Description for the flow classifier')) + parser.add_argument( + '--protocol', + metavar='', + help=_('IP protocol name. Protocol name should be as per ' + 'IANA standard.')) + parser.add_argument( + '--ethertype', + metavar='{IPv4,IPv6}', + default='IPv4', choices=['IPv4', 'IPv6'], + help=_('L2 ethertype, default is IPv4')) + parser.add_argument( + '--source-port', + metavar=':', + help=_('Source protocol port (allowed range [1,65535]. Must be ' + 'specified as a:b, where a=min-port and b=max-port) ' + 'in the allowed range.')) + parser.add_argument( + '--destination-port', + metavar=':', + help=_('Destination protocol port (allowed range [1,65535]. Must ' + 'be specified as a:b, where a=min-port and b=max-port) ' + 'in the allowed range.')) + parser.add_argument( + '--source-ip-prefix', + metavar='', + help=_('Source IP address in CIDR notation')) + parser.add_argument( + '--destination-ip-prefix', + metavar='', + help=_('Destination IP address in CIDR notation')) + parser.add_argument( + '--logical-source-port', + metavar='', + help=_('Neutron source port (name or ID)')) + parser.add_argument( + '--logical-destination-port', + metavar='', + help=_('Neutron destination port (name or ID)')) + parser.add_argument( + '--l7-parameters', + help=_('Dictionary of L7 parameters. Currently, no value is ' + 'supported for this option.')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + attrs = _get_common_attrs(self.app.client_manager, parsed_args) + body = {resource: attrs} + obj = client.create_flow_classifier(body)[resource] + columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteSfcFlowClassifier(command.Command): + _description = _("Delete a given flow classifier") + + def get_parser(self, prog_name): + parser = super(DeleteSfcFlowClassifier, self).get_parser(prog_name) + parser.add_argument( + 'flow_classifier', + metavar='', + help=_("Flow classifier to delete (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + # TODO(mohan): Add support for deleting multiple resources. + client = self.app.client_manager.neutronclient + fc_id = _get_id(client, parsed_args.flow_classifier, resource) + try: + client.delete_flow_classifier(fc_id) + except Exception as e: + msg = (_("Failed to delete flow classifier with name " + "or ID '%(fc)s': %(e)s") + % {'fc': parsed_args.flow_classifier, 'e': e}) + raise exceptions.CommandError(msg) + + +class ListSfcFlowClassifier(command.Lister): + _description = _("List flow classifiers") + + def get_parser(self, prog_name): + parser = super(ListSfcFlowClassifier, self).get_parser(prog_name) + parser.add_argument( + '--long', + action='store_true', + help=_("List additional fields in output") + ) + return parser + + def extend_list(self, data, parsed_args): + ext_data = data['flow_classifiers'] + for d in ext_data: + val = [] + protocol = d['protocol'].upper() if d['protocol'] else 'any' + val.append('protocol: ' + protocol) + val.append(self._get_protocol_port_details(d, 'source')) + val.append(self._get_protocol_port_details(d, 'destination')) + if 'logical_source_port' in d: + val.append('neutron_source_port: ' + + str(d['logical_source_port'])) + + if 'logical_destination_port' in d: + val.append('neutron_destination_port: ' + + str(d['logical_destination_port'])) + + if 'l7_parameters' in d: + l7_param = 'l7_parameters: {%s}' % ','.join(d['l7_parameters']) + val.append(l7_param) + d['summary'] = ',\n'.join(val) + return ext_data + + def _get_protocol_port_details(self, data, val): + type_ip_prefix = val + '_ip_prefix' + ip_prefix = data.get(type_ip_prefix) + if not ip_prefix: + ip_prefix = 'any' + min_port = data.get(val + '_port_range_min') + if min_port is None: + min_port = 'any' + max_port = data.get(val + '_port_range_max') + if max_port is None: + max_port = 'any' + return '%s[port]: %s[%s:%s]' % ( + type, ip_prefix, min_port, max_port) + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + obj = client.list_flow_classifier() + obj_extend = self.extend_list(obj, parsed_args) + headers, columns = nc_osc_utils.get_column_definitions( + _attr_map, long_listing=parsed_args.long) + return (headers, (utils.get_dict_properties( + s, columns) for s in obj_extend)) + + +class SetSfcFlowClassifier(command.Command): + _description = _("Set flow classifier properties") + + def get_parser(self, prog_name): + parser = super(SetSfcFlowClassifier, self).get_parser(prog_name) + parser.add_argument( + '--name', + metavar='', + help=_('Name of the flow classifier')) + parser.add_argument( + '--description', + metavar='', + help=_('Description for the flow classifier')) + parser.add_argument( + 'flow_classifier', + metavar='', + help=_("Flow classifier to modify (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + fc_id = _get_id(client, parsed_args.flow_classifier, resource) + attrs = _get_common_attrs(self.app.client_manager, parsed_args, + is_create=False) + body = {resource: attrs} + try: + client.update_flow_classifier(fc_id, body) + except Exception as e: + msg = (_("Failed to update flow classifier '%(fc)s': %(e)s") + % {'fc': parsed_args.flow_classifier, 'e': e}) + raise exceptions.CommandError(msg) + + +class ShowSfcFlowClassifier(command.ShowOne): + _description = _("Display flow classifier details") + + def get_parser(self, prog_name): + parser = super(ShowSfcFlowClassifier, self).get_parser(prog_name) + parser.add_argument( + 'flow_classifier', + metavar='', + help=_("Flow classifier to display (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + fc_id = _get_id(client, parsed_args.flow_classifier, resource) + obj = client.show_flow_classifier(fc_id)[resource] + columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + + +def _get_common_attrs(client_manager, parsed_args, is_create=True): + attrs = {} + if parsed_args.name is not None: + attrs['name'] = parsed_args.name + if parsed_args.description is not None: + attrs['description'] = parsed_args.description + if is_create: + _get_attrs(client_manager, attrs, parsed_args) + return attrs + + +def _get_attrs(client_manager, attrs, parsed_args): + if parsed_args.protocol is not None: + attrs['protocol'] = parsed_args.protocol + if parsed_args.ethertype: + attrs['ethertype'] = parsed_args.ethertype + if parsed_args.source_ip_prefix is not None: + attrs['source_ip_prefix'] = parsed_args.source_ip_prefix + if parsed_args.destination_ip_prefix is not None: + attrs['destination_ip_prefix'] = parsed_args.destination_ip_prefix + if parsed_args.logical_source_port is not None: + attrs['logical_source_port'] = _get_id( + client_manager.neutronclient, parsed_args.logical_source_port, + 'port') + if parsed_args.logical_destination_port is not None: + attrs['logical_destination_port'] = _get_id( + client_manager.neutronclient, parsed_args.logical_destination_port, + 'port') + if parsed_args.source_port is not None: + _fill_protocol_port_info(attrs, 'source', + parsed_args.source_port) + if parsed_args.destination_port is not None: + _fill_protocol_port_info(attrs, 'destination', + parsed_args.destination_port) + if parsed_args.l7_parameters is not None: + attrs['l7_parameters'] = parsed_args.l7_parameters + + +def _fill_protocol_port_info(attrs, port_type, port_val): + min_port, sep, max_port = port_val.partition(":") + if not min_port: + msg = ("Invalid port value '%s', expected format is " + "min-port:max-port or min-port.") + raise argparse.ArgumentTypeError(msg % port_val) + if not max_port: + max_port = min_port + try: + attrs[port_type + '_port_range_min'] = int(min_port) + attrs[port_type + '_port_range_max'] = int(max_port) + except ValueError: + message = (_("Protocol port value %s must be an integer " + "or integer:integer.") % port_val) + raise nc_exc.CommandError(message=message) + + +def _get_id(client, id_or_name, resource): + return client.find_resource(resource, id_or_name)['id'] diff --git a/neutronclient/osc/v2/sfc/sfc_port_chain.py b/neutronclient/osc/v2/sfc/sfc_port_chain.py new file mode 100755 index 000000000..582a6d92c --- /dev/null +++ b/neutronclient/osc/v2/sfc/sfc_port_chain.py @@ -0,0 +1,347 @@ +# Copyright (c) 2017 Huawei Technologies India Pvt.Limited. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import logging + +from osc_lib.cli import parseractions +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils + +from neutronclient._i18n import _ +from neutronclient.osc import utils as nc_osc_utils + +LOG = logging.getLogger(__name__) + +resource = 'port_chain' + +_attr_map = ( + ('id', 'ID', nc_osc_utils.LIST_BOTH), + ('name', 'Name', nc_osc_utils.LIST_BOTH), + ('port_pair_groups', 'Port Pair Groups', nc_osc_utils.LIST_BOTH), + ('flow_classifiers', 'Flow Classifiers', + nc_osc_utils.LIST_BOTH), + ('chain_parameters', 'Chain Parameters', + nc_osc_utils.LIST_BOTH), + ('description', 'Description', nc_osc_utils.LIST_LONG_ONLY), + ('chain_id', 'Chain ID', nc_osc_utils.LIST_BOTH), + ('project_id', 'Project', nc_osc_utils.LIST_LONG_ONLY), +) + + +class CreateSfcPortChain(command.ShowOne): + _description = _("Create a port chain") + + def get_parser(self, prog_name): + parser = super(CreateSfcPortChain, self).get_parser(prog_name) + parser.add_argument( + 'name', + metavar='', + help=_('Name of the port chain')) + parser.add_argument( + '--description', + metavar='', + help=_('Description for the port chain')) + parser.add_argument( + '--flow-classifier', + default=[], + metavar='', + dest='flow_classifiers', + action='append', + help=_('Add flow classifier (name or ID). ' + 'This option can be repeated.')) + parser.add_argument( + '--chain-parameters', + metavar='correlation=,symmetric=', + action=parseractions.MultiKeyValueAction, + optional_keys=['correlation', 'symmetric'], + help=_('Dictionary of chain parameters. Supports ' + 'correlation=mpls and symmetric=true|false.')) + parser.add_argument( + '--port-pair-group', + metavar='', + dest='port_pair_groups', + required=True, + action='append', + help=_('Port pair group (name or ID). ' + 'This option can be repeated.')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + attrs = _get_common_attrs(self.app.client_manager, parsed_args) + body = {resource: attrs} + obj = client.create_port_chain(body)[resource] + columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteSfcPortChain(command.Command): + _description = _("Delete a given port chain") + + def get_parser(self, prog_name): + parser = super(DeleteSfcPortChain, self).get_parser(prog_name) + parser.add_argument( + 'port_chain', + metavar="", + help=_("Port chain to delete (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + # TODO(mohan): Add support for deleting multiple resources. + client = self.app.client_manager.neutronclient + pc_id = _get_id(client, parsed_args.port_chain, resource) + try: + client.delete_port_chain(pc_id) + except Exception as e: + msg = (_("Failed to delete port chain with name " + "or ID '%(pc)s': %(e)s") + % {'pc': parsed_args.port_chain, 'e': e}) + raise exceptions.CommandError(msg) + + +class ListSfcPortChain(command.Lister): + _description = _("List port chains") + + def get_parser(self, prog_name): + parser = super(ListSfcPortChain, self).get_parser(prog_name) + parser.add_argument( + '--long', + action='store_true', + default=False, + help=_("List additional fields in output") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + data = client.list_port_chain() + headers, columns = nc_osc_utils.get_column_definitions( + _attr_map, long_listing=parsed_args.long) + return (headers, + (utils.get_dict_properties(s, columns) + for s in data['port_chains'])) + + +class SetSfcPortChain(command.Command): + _description = _("Set port chain properties") + + def get_parser(self, prog_name): + parser = super(SetSfcPortChain, self).get_parser(prog_name) + parser.add_argument( + '--name', + metavar='', + help=_('Name of the port chain')) + parser.add_argument( + '--description', + metavar='', + help=_('Description for the port chain')) + parser.add_argument( + '--flow-classifier', + metavar='', + dest='flow_classifiers', + action='append', + help=_('Add flow classifier (name or ID). ' + 'This option can be repeated.')) + parser.add_argument( + '--no-flow-classifier', + action='store_true', + help=_('Associate no flow classifier with the port chain')) + parser.add_argument( + '--port-pair-group', + metavar='', + dest='port_pair_groups', + action='append', + help=_('Add port pair group (name or ID). ' + 'This option can be repeated.')) + parser.add_argument( + '--no-port-pair-group', + action='store_true', + help=_('Remove associated port pair group from the port chain.' + 'At least one --port-pair-group must be specified ' + 'together.')) + parser.add_argument( + 'port_chain', + metavar='', + help=_("Port chain to modify (name or ID)")) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + pc_id = _get_id(client, parsed_args.port_chain, resource) + attrs = _get_common_attrs(self.app.client_manager, parsed_args, + is_create=False) + if parsed_args.no_flow_classifier: + attrs['flow_classifiers'] = [] + if parsed_args.flow_classifiers: + for fc in parsed_args.flow_classifiers: + added = [client.find_resource( + 'flow_classifier', fc, + cmd_resource='sfc_flow_classifier')['id']] + if parsed_args.no_flow_classifier: + existing = [] + else: + existing = [client.find_resource( + resource, parsed_args.port_chain, + cmd_resource='sfc_port_chain')['flow_classifiers']] + attrs['flow_classifiers'] = sorted(list( + set(existing) | set(added))) + if (parsed_args.no_port_pair_group and not + parsed_args.port_pair_groups): + message = _('At least one --port-pair-group must be specified.') + raise exceptions.CommandError(message) + if parsed_args.no_port_pair_group and parsed_args.port_pair_groups: + for ppg in parsed_args.port_pair_groups: + attrs['port_pair_groups'] = [client.find_resource( + 'port_pair_group', ppg, + cmd_resource='sfc_port_pair_group')['id']] + if (parsed_args.port_pair_groups and + not parsed_args.no_port_pair_group): + existing_ppg = [client.find_resource( + resource, parsed_args.port_chain, + cmd_resource='sfc_port_chain')['port_pair_groups']] + for ppg in parsed_args.port_pair_groups: + existing_ppg.append(client.find_resource( + 'port_pair_group', ppg, + cmd_resource='sfc_port_pair_group')['id']) + attrs['port_pair_groups'] = sorted(list(set(existing_ppg))) + body = {resource: attrs} + try: + client.update_port_chain(pc_id, body) + except Exception as e: + msg = (_("Failed to update port chain '%(pc)s': %(e)s") + % {'pc': parsed_args.port_chain, 'e': e}) + raise exceptions.CommandError(msg) + + +class ShowSfcPortChain(command.ShowOne): + _description = _("Display port chain details") + + def get_parser(self, prog_name): + parser = super(ShowSfcPortChain, self).get_parser(prog_name) + parser.add_argument( + 'port_chain', + metavar="", + help=_("Port chain to display (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + pc_id = _get_id(client, parsed_args.port_chain, resource) + obj = client.show_port_chain(pc_id)[resource] + columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + + +class UnsetSfcPortChain(command.Command): + _description = _("Unset port chain properties") + + def get_parser(self, prog_name): + parser = super(UnsetSfcPortChain, self).get_parser(prog_name) + parser.add_argument( + 'port_chain', + metavar='', + help=_("Port chain to unset (name or ID)")) + port_chain = parser.add_mutually_exclusive_group() + port_chain.add_argument( + '--flow-classifier', + action='append', + metavar='', + dest='flow_classifiers', + help=_('Remove flow classifier(s) from the port chain ' + '(name or ID). This option can be repeated.')) + port_chain.add_argument( + '--all-flow-classifier', + action='store_true', + help=_('Remove all flow classifiers from the port chain')) + parser.add_argument( + '--port-pair-group', + metavar='', + dest='port_pair_groups', + action='append', + help=_('Remove port pair group(s) from the port chain ' + '(name or ID). This option can be repeated.')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + pc_id = _get_id(client, parsed_args.port_chain, resource) + attrs = {} + if parsed_args.flow_classifiers: + existing = [client.find_resource( + resource, parsed_args.port_chain, + cmd_resource='sfc_port_chain')['flow_classifiers']] + for fc in parsed_args.flow_classifiers: + removed = [client.find_resource( + 'flow_classifier', fc, + cmd_resource='sfc_flow_classifier')['id']] + attrs['flow_classifiers'] = list(set(existing) - set(removed)) + if parsed_args.all_flow_classifier: + attrs['flow_classifiers'] = [] + if parsed_args.port_pair_groups: + existing_ppg = [client.find_resource( + resource, parsed_args.port_chain, + cmd_resource='sfc_port_chain')['port_pair_groups']] + for ppg in parsed_args.port_pair_groups: + removed_ppg = [client.find_resource( + 'port_pair_group', ppg, + cmd_resource='sfc_port_pair_group')['id']] + attrs['port_pair_groups'] = list(set(existing_ppg) - + set(removed_ppg)) + if attrs['port_pair_groups'] == []: + message = _('At least one --port-pair-group must be' + ' specified.') + raise exceptions.CommandError(message) + body = {resource: attrs} + try: + client.update_port_chain(pc_id, body) + except Exception as e: + msg = (_("Failed to unset port chain '%(pc)s': %(e)s") + % {'pc': parsed_args.port_chain, 'e': e}) + raise exceptions.CommandError(msg) + + +def _get_common_attrs(client_manager, parsed_args, is_create=True): + attrs = {} + if parsed_args.name is not None: + attrs['name'] = parsed_args.name + if parsed_args.description is not None: + attrs['description'] = parsed_args.description + if ('port_pair_groups' in parsed_args and + parsed_args.port_pair_groups is not None): + attrs['port_pair_groups'] = [(_get_id(client_manager.neutronclient, + ppg, 'port_pair_group')) + for ppg in parsed_args.port_pair_groups] + if ('flow_classifiers' in parsed_args and + parsed_args.flow_classifiers is not None): + attrs['flow_classifiers'] = [(_get_id(client_manager.neutronclient, fc, + 'flow_classifier')) + for fc in parsed_args.flow_classifiers] + if is_create is True: + _get_attrs(attrs, parsed_args) + return attrs + + +def _get_attrs(attrs, parsed_args): + if 'chain_parameters' in parsed_args: + attrs['chain_parameters'] = parsed_args.chain_parameters + + +def _get_id(client, id_or_name, resource): + return client.find_resource(resource, id_or_name)['id'] diff --git a/neutronclient/osc/v2/sfc/sfc_port_pair.py b/neutronclient/osc/v2/sfc/sfc_port_pair.py new file mode 100755 index 000000000..26f369c77 --- /dev/null +++ b/neutronclient/osc/v2/sfc/sfc_port_pair.py @@ -0,0 +1,215 @@ +# Copyright (c) 2017 Huawei Technologies India Pvt.Limited. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import logging + +from osc_lib.cli import parseractions +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils + +from neutronclient._i18n import _ +from neutronclient.osc import utils as nc_osc_utils + +LOG = logging.getLogger(__name__) + +resource = 'port_pair' + +_attr_map = ( + ('id', 'ID', nc_osc_utils.LIST_BOTH), + ('name', 'Name', nc_osc_utils.LIST_BOTH), + ('ingress', 'Ingress Logical Port', nc_osc_utils.LIST_BOTH), + ('egress', 'Egress Logical Port', nc_osc_utils.LIST_BOTH), + ('service_function_parameters', 'Service Function Parameters', + nc_osc_utils.LIST_LONG_ONLY), + ('description', 'Description', nc_osc_utils.LIST_LONG_ONLY), + ('project_id', 'Project', nc_osc_utils.LIST_LONG_ONLY), +) + + +class CreateSfcPortPair(command.ShowOne): + _description = _("Create a port pair") + + def get_parser(self, prog_name): + parser = super(CreateSfcPortPair, self).get_parser(prog_name) + parser.add_argument( + 'name', + metavar='', + help=_('Name of the port pair')) + parser.add_argument( + '--description', + metavar='', + help=_('Description for the port pair')) + parser.add_argument( + '--service-function-parameters', + metavar='correlation=,weight=', + action=parseractions.MultiKeyValueAction, + optional_keys=['correlation', 'weight'], + help=_('Dictionary of service function parameters. ' + 'Currently, only correlation=None and weight ' + 'is supported. Weight is an integer that influences ' + 'the selection of a port pair within a port pair group ' + 'for a flow. The higher the weight, the more flows will ' + 'hash to the port pair. The default weight is 1.')) + parser.add_argument( + '--ingress', + metavar='', + required=True, + help=_('Ingress neutron port (name or ID)')) + parser.add_argument( + '--egress', + metavar='', + required=True, + help=_('Egress neutron port (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + attrs = _get_common_attrs(self.app.client_manager, parsed_args) + body = {resource: attrs} + obj = client.create_port_pair(body)[resource] + columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteSfcPortPair(command.Command): + _description = _("Delete a given port pair") + + def get_parser(self, prog_name): + parser = super(DeleteSfcPortPair, self).get_parser(prog_name) + parser.add_argument( + 'port_pair', + metavar="", + help=_("Port pair to delete (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + # TODO(mohan): Add support for deleting multiple resources. + client = self.app.client_manager.neutronclient + port_pair_id = _get_id(client, parsed_args.port_pair, resource) + try: + client.delete_port_pair(port_pair_id) + except Exception as e: + msg = (_("Failed to delete port pair with name " + "or ID '%(port_pair)s': %(e)s") + % {'port_pair': parsed_args.port_pair, 'e': e}) + raise exceptions.CommandError(msg) + + +class ListSfcPortPair(command.Lister): + _description = _("List port pairs") + + def get_parser(self, prog_name): + parser = super(ListSfcPortPair, self).get_parser(prog_name) + parser.add_argument( + '--long', + action='store_true', + help=_("List additional fields in output") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + data = client.list_port_pair() + headers, columns = nc_osc_utils.get_column_definitions( + _attr_map, long_listing=parsed_args.long) + return (headers, + (utils.get_dict_properties( + s, columns, + ) for s in data['port_pairs'])) + + +class SetSfcPortPair(command.Command): + _description = _("Set port pair properties") + + def get_parser(self, prog_name): + parser = super(SetSfcPortPair, self).get_parser(prog_name) + parser.add_argument( + '--name', + metavar='', + help=_('Name of the port pair')) + parser.add_argument( + '--description', + metavar='', + help=_('Description for the port pair')) + parser.add_argument( + 'port_pair', + metavar='', + help=_("Port pair to modify (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + port_pair_id = _get_id(client, parsed_args.port_pair, resource) + attrs = _get_common_attrs(self.app.client_manager, parsed_args, + is_create=False) + body = {resource: attrs} + try: + client.update_port_pair(port_pair_id, body) + except Exception as e: + msg = (_("Failed to update port pair '%(port_pair)s': %(e)s") + % {'port_pair': parsed_args.port_pair, 'e': e}) + raise exceptions.CommandError(msg) + + +class ShowSfcPortPair(command.ShowOne): + _description = _("Display port pair details") + + def get_parser(self, prog_name): + parser = super(ShowSfcPortPair, self).get_parser(prog_name) + parser.add_argument( + 'port_pair', + metavar='', + help=_("Port pair to display (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + port_pair_id = _get_id(client, parsed_args.port_pair, resource) + obj = client.show_port_pair(port_pair_id)[resource] + columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + + +def _get_common_attrs(client_manager, parsed_args, is_create=True): + attrs = {} + if parsed_args.name is not None: + attrs['name'] = parsed_args.name + if parsed_args.description is not None: + attrs['description'] = parsed_args.description + if is_create: + _get_attrs(client_manager, attrs, parsed_args) + return attrs + + +def _get_attrs(client_manager, attrs, parsed_args): + if parsed_args.ingress is not None: + attrs['ingress'] = _get_id(client_manager.neutronclient, + parsed_args.ingress, 'port') + if parsed_args.egress is not None: + attrs['egress'] = _get_id(client_manager.neutronclient, + parsed_args.egress, 'port') + if 'service_function_parameters' in parsed_args: + attrs['service_function_parameters'] = ( + parsed_args.service_function_parameters) + + +def _get_id(client, id_or_name, resource): + return client.find_resource(resource, id_or_name)['id'] diff --git a/neutronclient/osc/v2/sfc/sfc_port_pair_group.py b/neutronclient/osc/v2/sfc/sfc_port_pair_group.py new file mode 100755 index 000000000..367dab128 --- /dev/null +++ b/neutronclient/osc/v2/sfc/sfc_port_pair_group.py @@ -0,0 +1,291 @@ +# Copyright (c) 2017 Huawei Technologies India Pvt.Limited. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import logging + +from osc_lib.cli import parseractions +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils + +from neutronclient._i18n import _ +from neutronclient.osc import utils as nc_osc_utils + +LOG = logging.getLogger(__name__) + +resource = 'port_pair_group' + +_attr_map = ( + ('id', 'ID', nc_osc_utils.LIST_BOTH), + ('name', 'Name', nc_osc_utils.LIST_BOTH), + ('port_pairs', 'Port Pair', nc_osc_utils.LIST_BOTH), + ('port_pair_group_parameters', 'Port Pair Group Parameters', + nc_osc_utils.LIST_BOTH), + ('description', 'Description', nc_osc_utils.LIST_LONG_ONLY), + ('group_id', 'Loadbalance ID', nc_osc_utils.LIST_LONG_ONLY), + ('project_id', 'Project', nc_osc_utils.LIST_LONG_ONLY), +) + + +class CreateSfcPortPairGroup(command.ShowOne): + _description = _("Create a port pair group") + + def get_parser(self, prog_name): + parser = super(CreateSfcPortPairGroup, self).get_parser(prog_name) + parser.add_argument( + 'name', + metavar='', + help=_('Name of the port pair group')) + parser.add_argument( + '--description', + metavar='', + help=_('Description for the port pair group')) + parser.add_argument( + '--port-pair', + metavar='', + dest='port_pairs', + default=[], + action='append', + help=_('Port pair (name or ID). ' + 'This option can be repeated.')) + parser.add_argument( + '--port-pair-group-parameters', + metavar='lb-fields=', + action=parseractions.KeyValueAction, + help=_('Dictionary of port pair group parameters. ' + 'Currently only one parameter lb-fields is supported. ' + ' is a & separated list of load-balancing ' + 'fields.')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + attrs = _get_common_attrs(self.app.client_manager, parsed_args) + body = {resource: attrs} + obj = client.create_port_pair_group(body)[resource] + columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteSfcPortPairGroup(command.Command): + _description = _("Delete a given port pair group") + + def get_parser(self, prog_name): + parser = super(DeleteSfcPortPairGroup, self).get_parser(prog_name) + parser.add_argument( + 'port_pair_group', + metavar='', + help=_("Port pair group to delete (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + # TODO(mohan): Add support for deleting multiple resources. + client = self.app.client_manager.neutronclient + ppg_id = _get_id(client, parsed_args.port_pair_group, resource) + try: + client.delete_port_pair_group(ppg_id) + except Exception as e: + msg = (_("Failed to delete port pair group with name " + "or ID '%(ppg)s': %(e)s") + % {'ppg': parsed_args.port_pair_group, 'e': e}) + raise exceptions.CommandError(msg) + + +class ListSfcPortPairGroup(command.Lister): + _description = _("List port pair group") + + def get_parser(self, prog_name): + parser = super(ListSfcPortPairGroup, self).get_parser(prog_name) + parser.add_argument( + '--long', + action='store_true', + default=False, + help=_("List additional fields in output") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + data = client.list_port_pair_group() + headers, columns = nc_osc_utils.get_column_definitions( + _attr_map, long_listing=parsed_args.long) + return (headers, + (utils.get_dict_properties( + s, columns, + ) for s in data['port_pair_groups'])) + + +class SetSfcPortPairGroup(command.Command): + _description = _("Set port pair group properties") + + def get_parser(self, prog_name): + parser = super(SetSfcPortPairGroup, self).get_parser(prog_name) + parser.add_argument( + 'port_pair_group', + metavar='', + help=_("Port pair group to modify (name or ID)")) + parser.add_argument( + '--name', + metavar='', + help=_('Name of the port pair group')) + parser.add_argument( + '--description', + metavar='', + help=_('Description for the port pair group')) + parser.add_argument( + '--port-pair', + metavar='', + dest='port_pairs', + default=[], + action='append', + help=_('Port pair (name or ID). ' + 'This option can be repeated.')) + parser.add_argument( + '--no-port-pair', + action='store_true', + help=_('Remove all port pair from port pair group')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + ppg_id = _get_id(client, parsed_args.port_pair_group, resource) + attrs = _get_common_attrs(self.app.client_manager, parsed_args, + is_create=False) + if parsed_args.no_port_pair: + attrs['port_pairs'] = [] + if parsed_args.port_pairs: + added = [client.find_resource('port_pair', pp, + cmd_resource='sfc_port_pair')['id'] + for pp in parsed_args.port_pairs] + if parsed_args.no_port_pair: + existing = [] + else: + existing = [client.find_resource( + resource, parsed_args.port_pair_group, + cmd_resource='sfc_port_pair_group')['port_pairs']] + attrs['port_pairs'] = sorted(list(set(existing) | set(added))) + body = {resource: attrs} + try: + client.update_port_pair_group(ppg_id, body) + except Exception as e: + msg = (_("Failed to update port pair group '%(ppg)s': %(e)s") + % {'ppg': parsed_args.port_pair_group, 'e': e}) + raise exceptions.CommandError(msg) + + +class ShowSfcPortPairGroup(command.ShowOne): + _description = _("Display port pair group details") + + def get_parser(self, prog_name): + parser = super(ShowSfcPortPairGroup, self).get_parser(prog_name) + parser.add_argument( + 'port_pair_group', + metavar='', + help=_("Port pair group to display (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + ppg_id = _get_id(client, parsed_args.port_pair_group, resource) + obj = client.show_port_pair_group(ppg_id)[resource] + columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + + +class UnsetSfcPortPairGroup(command.Command): + _description = _("Unset port pairs from port pair group") + + def get_parser(self, prog_name): + parser = super(UnsetSfcPortPairGroup, self).get_parser(prog_name) + parser.add_argument( + 'port_pair_group', + metavar='', + help=_("Port pair group to unset (name or ID)")) + port_pair_group = parser.add_mutually_exclusive_group() + port_pair_group.add_argument( + '--port-pair', + action='append', + metavar='', + dest='port_pairs', + help=_('Remove port pair(s) from the port pair group ' + '(name or ID). This option can be repeated.')) + port_pair_group.add_argument( + '--all-port-pair', + action='store_true', + help=_('Remove all port pairs from the port pair group')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + ppg_id = _get_id(client, parsed_args.port_pair_group, resource) + attrs = {} + if parsed_args.port_pairs: + existing = [client.find_resource( + resource, parsed_args.port_pair_group, + cmd_resource='sfc_port_pair_group')['port_pairs']] + for pp in parsed_args.port_pairs: + removed = [client.find_resource( + 'port_pair', pp, cmd_resource='sfc_port_pair')['id']] + attrs['port_pairs'] = list(set(existing) - set(removed)) + if parsed_args.all_port_pair: + attrs['port_pairs'] = [] + body = {resource: attrs} + try: + client.update_port_pair_group(ppg_id, body) + except Exception as e: + msg = (_("Failed to unset port pair group '%(ppg)s': %(e)s") + % {'ppg': parsed_args.port_pair_group, 'e': e}) + raise exceptions.CommandError(msg) + + +def _get_ppg_param(attrs, ppg): + attrs['port_pair_group_parameters'] = {} + for key, value in ppg.items(): + if key == 'lb_fields': + attrs['port_pair_group_parameters'][key] = ([ + field for field in value.split('&') if field]) + else: + attrs['port_pair_group_parameters'][key] = value + return attrs['port_pair_group_parameters'] + + +def _get_common_attrs(client_manager, parsed_args, is_create=True): + attrs = {} + if parsed_args.name is not None: + attrs['name'] = parsed_args.name + if parsed_args.description is not None: + attrs['description'] = parsed_args.description + if parsed_args.port_pairs: + attrs['port_pairs'] = [(_get_id(client_manager.neutronclient, pp, + 'port_pair')) + for pp in parsed_args.port_pairs] + if is_create: + _get_attrs(attrs, parsed_args) + return attrs + + +def _get_attrs(attrs, parsed_args): + if ('port_pair_group_parameters' in parsed_args and + parsed_args.port_pair_group_parameters is not None): + attrs['port_pair_group_parameters'] = ( + _get_ppg_param(attrs, parsed_args.port_pair_group_parameters)) + + +def _get_id(client, id_or_name, resource): + return client.find_resource(resource, id_or_name)['id'] diff --git a/neutronclient/tests/unit/osc/v2/sfc/__init__.py b/neutronclient/tests/unit/osc/v2/sfc/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/tests/unit/osc/v2/sfc/fakes.py b/neutronclient/tests/unit/osc/v2/sfc/fakes.py new file mode 100755 index 000000000..54bd5a8ee --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/sfc/fakes.py @@ -0,0 +1,234 @@ +# Copyright (c) 2017 Huawei Technologies India Pvt.Limited. +# 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 argparse +import copy + +import mock + +from osc_lib.tests import utils +from oslo_utils import uuidutils + + +class TestNeutronClientOSCV2(utils.TestCommand): + + def setUp(self): + super(TestNeutronClientOSCV2, self).setUp() + self.namespace = argparse.Namespace() + self.app.client_manager.session = mock.Mock() + self.app.client_manager.neutronclient = mock.Mock() + self.neutronclient = self.app.client_manager.neutronclient + self.neutronclient.find_resource = mock.Mock( + side_effect=lambda resource, name_or_id, project_id=None, + cmd_resource=None, parent_id=None, fields=None: + {'id': name_or_id}) + + +class FakeSfcPortPair(object): + """Fake port pair attributes.""" + + @staticmethod + def create_port_pair(attrs=None): + """Create a fake port pair. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A Dictionary with id, name, description, ingress, egress, + service-function-parameter, project_id + """ + attrs = attrs or {} + + # Set default attributes. + port_pair_attrs = { + 'description': 'description', + 'egress': uuidutils.generate_uuid(), + 'id': uuidutils.generate_uuid(), + 'ingress': uuidutils.generate_uuid(), + 'name': 'port-pair-name', + 'service_function_parameters': 'correlation=None,weight=1', + 'project_id': uuidutils.generate_uuid(), + } + + # Overwrite default attributes. + port_pair_attrs.update(attrs) + return copy.deepcopy(port_pair_attrs) + + @staticmethod + def create_port_pairs(attrs=None, count=1): + """Create multiple port_pairs. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of port_pairs to fake + :return: + A list of dictionaries faking the port_pairs + """ + port_pairs = [] + for _ in range(count): + port_pairs.append(FakeSfcPortPair.create_port_pair(attrs)) + + return port_pairs + + +class FakeSfcPortPairGroup(object): + """Fake port pair group attributes.""" + + @staticmethod + def create_port_pair_group(attrs=None): + """Create a fake port pair group. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A Dictionary with id, name, description, port_pairs, group_id + port_pair_group_parameters, project_id + """ + attrs = attrs or {} + + # Set default attributes. + port_pair_group_attrs = { + 'id': uuidutils.generate_uuid(), + 'group_id': uuidutils.generate_uuid(), + 'name': 'port-pair-group-name', + 'description': 'description', + 'port_pairs': uuidutils.generate_uuid(), + 'port_pair_group_parameters': '{"lb_fields": []}', + 'project_id': uuidutils.generate_uuid() + } + + # port_pair_group_attrs default attributes. + port_pair_group_attrs.update(attrs) + return copy.deepcopy(port_pair_group_attrs) + + @staticmethod + def create_port_pair_groups(attrs=None, count=1): + """Create multiple port pair groups. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of port_pair_groups to fake + :return: + A list of dictionaries faking the port pair groups + """ + port_pair_groups = [] + for _ in range(count): + port_pair_groups.append( + FakeSfcPortPairGroup.create_port_pair_group(attrs)) + + return port_pair_groups + + +class FakeSfcFlowClassifier(object): + """Fake flow classifier attributes.""" + + @staticmethod + def create_flow_classifier(attrs=None): + """Create a fake flow classifier. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A Dictionary with faking port chain attributes + """ + attrs = attrs or {} + + # Set default attributes. + flow_classifier_attrs = { + 'id': uuidutils.generate_uuid(), + 'destination_ip_prefix': '2.2.2.2/32', + 'destination_port_range_max': '90', + 'destination_port_range_min': '80', + 'ethertype': 'IPv4', + 'logical_destination_port': uuidutils.generate_uuid(), + 'logical_source_port': uuidutils.generate_uuid(), + 'name': 'flow-classifier-name', + 'description': 'fc_description', + 'protocol': 'tcp', + 'source_ip_prefix': '1.1.1.1/32', + 'source_port_range_max': '20', + 'source_port_range_min': '10', + 'project_id': uuidutils.generate_uuid(), + 'l7_parameters': '{}' + } + flow_classifier_attrs.update(attrs) + return copy.deepcopy(flow_classifier_attrs) + + @staticmethod + def create_flow_classifiers(attrs=None, count=1): + """Create multiple flow classifiers. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of flow classifiers to fake + :return: + A list of dictionaries faking the flow classifiers + """ + flow_classifiers = [] + for _ in range(count): + flow_classifiers.append( + FakeSfcFlowClassifier.create_flow_classifier(attrs)) + + return flow_classifiers + + +class FakeSfcPortChain(object): + """Fake port chain attributes.""" + + @staticmethod + def create_port_chain(attrs=None): + """Create a fake port chain. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A Dictionary with faking port chain attributes + """ + attrs = attrs or {} + + # Set default attributes. + port_chain_attrs = { + 'id': uuidutils.generate_uuid(), + 'chain_id': uuidutils.generate_uuid(), + 'name': 'port-chain-name', + 'description': 'description', + 'port_pair_groups': uuidutils.generate_uuid(), + 'flow_classifiers': uuidutils.generate_uuid(), + 'chain_parameters': '{"correlation": mpls}', + 'project_id': uuidutils.generate_uuid(), + } + + # port_pair_group_attrs default attributes. + port_chain_attrs.update(attrs) + return copy.deepcopy(port_chain_attrs) + + @staticmethod + def create_port_chains(attrs=None, count=1): + """Create multiple port chains. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of port chains to fake + :return: + A list of dictionaries faking the port chains. + """ + port_chains = [] + for _ in range(count): + port_chains.append(FakeSfcPortChain.create_port_chain(attrs)) + return port_chains diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_flow_classifier.py b/neutronclient/tests/unit/osc/v2/sfc/test_flow_classifier.py new file mode 100755 index 000000000..fff11b317 --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/sfc/test_flow_classifier.py @@ -0,0 +1,378 @@ +# Copyright (c) 2017 Huawei Technologies India Pvt.Limited. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import mock + +from neutronclient.osc.v2.sfc import sfc_flow_classifier +from neutronclient.tests.unit.osc.v2.sfc import fakes + +get_id = 'neutronclient.osc.v2.sfc.sfc_flow_classifier._get_id' + + +def _get_id(client, id_or_name, resource): + return id_or_name + + +class TestCreateSfcFlowClassifier(fakes.TestNeutronClientOSCV2): + + _fc = fakes.FakeSfcFlowClassifier.create_flow_classifier() + + columns = ('Description', + 'Destination IP', + 'Destination Port Range Max', + 'Destination Port Range Min', + 'Ethertype', + 'ID', + 'L7 Parameters', + 'Logical Destination Port', + 'Logical Source Port', + 'Name', + 'Project', + 'Protocol', + 'Source IP', + 'Source Port Range Max', + 'Source Port Range Min') + + def get_data(self): + return ( + self._fc['description'], + self._fc['destination_ip_prefix'], + self._fc['destination_port_range_max'], + self._fc['destination_port_range_min'], + self._fc['ethertype'], + self._fc['id'], + self._fc['l7_parameters'], + self._fc['logical_destination_port'], + self._fc['logical_source_port'], + self._fc['name'], + self._fc['project_id'], + self._fc['protocol'], + self._fc['source_ip_prefix'], + self._fc['source_port_range_max'], + self._fc['source_port_range_min'] + ) + + def setUp(self): + super(TestCreateSfcFlowClassifier, self).setUp() + mock.patch(get_id, new=_get_id).start() + self.neutronclient.create_flow_classifier = mock.Mock( + return_value={'flow_classifier': self._fc}) + self.data = self.get_data() + + # Get the command object to test + self.cmd = sfc_flow_classifier.CreateSfcFlowClassifier(self.app, + self.namespace) + + def test_create_flow_classifier_default_options(self): + arglist = [ + "--logical-source-port", self._fc['logical_source_port'], + "--ethertype", self._fc['ethertype'], + self._fc['name'], + ] + verifylist = [ + ('logical_source_port', self._fc['logical_source_port']), + ('ethertype', self._fc['ethertype']), + ('name', self._fc['name']), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = (self.cmd.take_action(parsed_args)) + + self.neutronclient.create_flow_classifier.assert_called_once_with({ + 'flow_classifier': { + 'name': self._fc['name'], + 'logical_source_port': self._fc['logical_source_port'], + 'ethertype': self._fc['ethertype']} + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_create_flow_classifier(self): + arglist = [ + "--description", self._fc['description'], + "--ethertype", self._fc['ethertype'], + "--protocol", self._fc['protocol'], + "--source-ip-prefix", self._fc['source_ip_prefix'], + "--destination-ip-prefix", self._fc['destination_ip_prefix'], + "--logical-source-port", self._fc['logical_source_port'], + "--logical-destination-port", self._fc['logical_destination_port'], + self._fc['name'], + "--l7-parameters", 'url=path', + ] + + param = 'url=path' + + verifylist = [ + ('description', self._fc['description']), + ('name', self._fc['name']), + ('ethertype', self._fc['ethertype']), + ('protocol', self._fc['protocol']), + ('source_ip_prefix', self._fc['source_ip_prefix']), + ('destination_ip_prefix', self._fc['destination_ip_prefix']), + ('logical_source_port', self._fc['logical_source_port']), + ('logical_destination_port', + self._fc['logical_destination_port']), + ('l7_parameters', param) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = (self.cmd.take_action(parsed_args)) + self.neutronclient.create_flow_classifier.assert_called_once_with({ + 'flow_classifier': { + 'name': self._fc['name'], + 'description': self._fc['description'], + 'ethertype': self._fc['ethertype'], + 'protocol': self._fc['protocol'], + 'source_ip_prefix': self._fc['source_ip_prefix'], + 'destination_ip_prefix': self._fc['destination_ip_prefix'], + 'logical_source_port': self._fc['logical_source_port'], + 'logical_destination_port': + self._fc['logical_destination_port'], + 'l7_parameters': param + } + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestDeleteSfcFlowClassifier(fakes.TestNeutronClientOSCV2): + + _flow_classifier = \ + fakes.FakeSfcFlowClassifier.create_flow_classifiers(count=1) + + def setUp(self): + super(TestDeleteSfcFlowClassifier, self).setUp() + mock.patch(get_id, new=_get_id).start() + self.neutronclient.delete_flow_classifier = mock.Mock( + return_value=None) + self.cmd = sfc_flow_classifier.DeleteSfcFlowClassifier(self.app, + self.namespace) + + def test_delete_flow_classifier(self): + client = self.app.client_manager.neutronclient + mock_flow_classifier_delete = client.delete_flow_classifier + arglist = [ + self._flow_classifier[0]['id'], + ] + verifylist = [ + ('flow_classifier', self._flow_classifier[0]['id']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + mock_flow_classifier_delete.assert_called_once_with( + self._flow_classifier[0]['id']) + self.assertIsNone(result) + + +class TestSetSfcFlowClassifier(fakes.TestNeutronClientOSCV2): + _flow_classifier = fakes.FakeSfcFlowClassifier.create_flow_classifier() + _flow_classifier_name = _flow_classifier['name'] + _flow_classifier_id = _flow_classifier['id'] + + def setUp(self): + super(TestSetSfcFlowClassifier, self).setUp() + mock.patch(get_id, new=_get_id).start() + self.neutronclient.update_flow_classifier = mock.Mock( + return_value=None) + self.cmd = sfc_flow_classifier.SetSfcFlowClassifier(self.app, + self.namespace) + + def test_set_flow_classifier(self): + client = self.app.client_manager.neutronclient + mock_flow_classifier_update = client.update_flow_classifier + arglist = [ + self._flow_classifier_name, + '--name', 'name_updated', + '--description', 'desc_updated' + ] + verifylist = [ + ('flow_classifier', self._flow_classifier_name), + ('name', 'name_updated'), + ('description', 'desc_updated'), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = {'flow_classifier': { + 'name': 'name_updated', + 'description': 'desc_updated'}} + mock_flow_classifier_update.assert_called_once_with( + self._flow_classifier_name, attrs) + self.assertIsNone(result) + + +class TestShowSfcFlowClassifier(fakes.TestNeutronClientOSCV2): + + _fc = fakes.FakeSfcFlowClassifier.create_flow_classifier() + data = ( + _fc['description'], + _fc['destination_ip_prefix'], + _fc['destination_port_range_max'], + _fc['destination_port_range_min'], + _fc['ethertype'], + _fc['id'], + _fc['l7_parameters'], + _fc['logical_destination_port'], + _fc['logical_source_port'], + _fc['name'], + _fc['project_id'], + _fc['protocol'], + _fc['source_ip_prefix'], + _fc['source_port_range_max'], + _fc['source_port_range_min'] + ) + _flow_classifier = {'flow_classifier': _fc} + _flow_classifier_id = _fc['id'] + columns = ('Description', + 'Destination IP', + 'Destination Port Range Max', + 'Destination Port Range Min', + 'Ethertype', + 'ID', + 'L7 Parameters', + 'Logical Destination Port', + 'Logical Source Port', + 'Name', + 'Project', + 'Protocol', + 'Source IP', + 'Source Port Range Max', + 'Source Port Range Min') + + def setUp(self): + super(TestShowSfcFlowClassifier, self).setUp() + mock.patch(get_id, new=_get_id).start() + self.neutronclient.show_flow_classifier = mock.Mock( + return_value=self._flow_classifier + ) + # Get the command object to test + self.cmd = sfc_flow_classifier.ShowSfcFlowClassifier(self.app, + self.namespace) + + def test_show_flow_classifier(self): + client = self.app.client_manager.neutronclient + mock_flow_classifier_show = client.show_flow_classifier + arglist = [ + self._flow_classifier_id, + ] + verifylist = [ + ('flow_classifier', self._flow_classifier_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + mock_flow_classifier_show.assert_called_once_with( + self._flow_classifier_id) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestListSfcFlowClassifier(fakes.TestNeutronClientOSCV2): + + _fc = fakes.FakeSfcFlowClassifier.create_flow_classifiers(count=1) + + columns = ('ID', 'Name', 'Summary') + + columns_long = ('ID', 'Name', 'Protocol', 'Ethertype', 'Source IP', + 'Destination IP', 'Logical Source Port', + 'Logical Destination Port', 'Source Port Range Min', + 'Source Port Range Max', 'Destination Port Range Min', + 'Destination Port Range Max', 'L7 Parameters', + 'Description', 'Project') + _flow_classifier = _fc[0] + data = [ + _flow_classifier['id'], + _flow_classifier['name'], + _flow_classifier['protocol'], + _flow_classifier['source_ip_prefix'], + _flow_classifier['destination_ip_prefix'], + _flow_classifier['logical_source_port'], + _flow_classifier['logical_destination_port'] + ] + data_long = [ + _flow_classifier['id'], + _flow_classifier['name'], + _flow_classifier['protocol'], + _flow_classifier['ethertype'], + _flow_classifier['source_ip_prefix'], + _flow_classifier['destination_ip_prefix'], + _flow_classifier['logical_source_port'], + _flow_classifier['logical_destination_port'], + _flow_classifier['source_port_range_min'], + _flow_classifier['source_port_range_max'], + _flow_classifier['destination_port_range_min'], + _flow_classifier['destination_port_range_max'], + _flow_classifier['l7_parameters'], + _flow_classifier['description'] + ] + + _flow_classifier1 = {'flow_classifiers': _flow_classifier} + _flow_classifier_id = _flow_classifier['id'] + + def setUp(self): + super(TestListSfcFlowClassifier, self).setUp() + mock.patch(get_id, new=_get_id).start() + self.neutronclient.list_flow_classifier = mock.Mock( + return_value={'flow_classifiers': self._fc} + ) + # Get the command object to test + self.cmd = sfc_flow_classifier.ListSfcFlowClassifier(self.app, + self.namespace) + + def test_list_flow_classifier(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns = self.cmd.take_action(parsed_args) + fcs = self.neutronclient.list_flow_classifier()['flow_classifiers'] + fc = fcs[0] + data = [ + fc['id'], + fc['name'], + fc['protocol'], + fc['source_ip_prefix'], + fc['destination_ip_prefix'], + fc['logical_source_port'], + fc['logical_destination_port'] + ] + self.assertEqual(list(self.columns), columns[0]) + self.assertEqual(self.data, data) + + def test_list_with_long_option(self): + arglist = ['--long'] + verifylist = [('long', True)] + fcs = self.neutronclient.list_flow_classifier()['flow_classifiers'] + fc = fcs[0] + data = [ + fc['id'], + fc['name'], + fc['protocol'], + fc['ethertype'], + fc['source_ip_prefix'], + fc['destination_ip_prefix'], + fc['logical_source_port'], + fc['logical_destination_port'], + fc['source_port_range_min'], + fc['source_port_range_max'], + fc['destination_port_range_min'], + fc['destination_port_range_max'], + fc['l7_parameters'], + fc['description'] + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns_long = self.cmd.take_action(parsed_args)[0] + self.assertEqual(list(self.columns_long), columns_long) + self.assertEqual(self.data_long, data) diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py b/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py new file mode 100755 index 000000000..8cb0fe2c5 --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py @@ -0,0 +1,556 @@ +# Copyright (c) 2017 Huawei Technologies India Pvt.Limited. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import mock + +from osc_lib import exceptions + +from neutronclient.osc.v2.sfc import sfc_port_chain +from neutronclient.tests.unit.osc.v2.sfc import fakes + + +def _get_id(client, id_or_name, resource): + return id_or_name + + +class TestCreateSfcPortChain(fakes.TestNeutronClientOSCV2): + # The new port_chain created + _port_chain = fakes.FakeSfcPortChain.create_port_chain() + + columns = ('Chain ID', + 'Chain Parameters', + 'Description', + 'Flow Classifiers', + 'ID', + 'Name', + 'Port Pair Groups', + 'Project') + + def get_data(self): + return ( + self._port_chain['chain_id'], + self._port_chain['chain_parameters'], + self._port_chain['description'], + self._port_chain['flow_classifiers'], + self._port_chain['id'], + self._port_chain['name'], + self._port_chain['port_pair_groups'], + self._port_chain['project_id'], + ) + + def setUp(self): + super(TestCreateSfcPortChain, self).setUp() + mock.patch( + 'neutronclient.osc.v2.sfc.sfc_port_chain._get_id', + new=_get_id).start() + self.neutronclient.create_port_chain = mock.Mock( + return_value={'port_chain': self._port_chain}) + self.data = self.get_data() + + # Get the command object to test + self.cmd = sfc_port_chain.CreateSfcPortChain(self.app, self.namespace) + + def test_create_port_chain_dafault_options(self): + arglist = [ + self._port_chain['name'], + "--port-pair-group", self._port_chain['port_pair_groups'] + ] + verifylist = [ + ('name', self._port_chain['name']), + ('port_pair_groups', [self._port_chain['port_pair_groups']]), + ('flow_classifiers', []), + ('chain_parameters', None), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = (self.cmd.take_action(parsed_args)) + + self.neutronclient.create_port_chain.assert_called_once_with({ + 'port_chain': { + 'name': self._port_chain['name'], + 'port_pair_groups': [self._port_chain['port_pair_groups']], + 'flow_classifiers': [], + 'chain_parameters': None} + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_create_port_chain_all_options(self): + arglist = [ + "--description", self._port_chain['description'], + "--port-pair-group", self._port_chain['port_pair_groups'], + self._port_chain['name'], + "--flow-classifier", self._port_chain['flow_classifiers'], + "--chain-parameters", 'correlation=mpls,symmetric=true', + ] + + cp = [{'correlation': 'mpls', 'symmetric': 'true'}] + + verifylist = [ + ('port_pair_groups', [self._port_chain['port_pair_groups']]), + ('name', self._port_chain['name']), + ('description', self._port_chain['description']), + ('flow_classifiers', [self._port_chain['flow_classifiers']]), + ('chain_parameters', cp) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = (self.cmd.take_action(parsed_args)) + + self.neutronclient.create_port_chain.assert_called_once_with({ + 'port_chain': { + 'name': self._port_chain['name'], + 'port_pair_groups': [self._port_chain['port_pair_groups']], + 'description': self._port_chain['description'], + 'flow_classifiers': [self._port_chain['flow_classifiers']], + 'chain_parameters': cp + } + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestDeleteSfcPortChain(fakes.TestNeutronClientOSCV2): + + _port_chain = fakes.FakeSfcPortChain.create_port_chains(count=1) + + def setUp(self): + super(TestDeleteSfcPortChain, self).setUp() + mock.patch( + 'neutronclient.osc.v2.sfc.sfc_port_chain._get_id', + new=_get_id).start() + self.neutronclient.delete_port_chain = mock.Mock(return_value=None) + self.cmd = sfc_port_chain.DeleteSfcPortChain(self.app, self.namespace) + + def test_delete_port_chain(self): + client = self.app.client_manager.neutronclient + mock_port_chain_delete = client.delete_port_chain + arglist = [ + self._port_chain[0]['id'], + ] + verifylist = [ + ('port_chain', self._port_chain[0]['id']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + mock_port_chain_delete.assert_called_once_with( + self._port_chain[0]['id']) + self.assertIsNone(result) + + +class TestListSfcPortChain(fakes.TestNeutronClientOSCV2): + _port_chains = fakes.FakeSfcPortChain.create_port_chains(count=1) + columns = ('ID', 'Name', 'Port Pair Groups', 'Flow Classifiers', + 'Chain Parameters', 'Chain ID') + columns_long = ('ID', 'Name', 'Port Pair Groups', 'Flow Classifiers', + 'Chain Parameters', 'Description', 'Chain ID', 'Project') + _port_chain = _port_chains[0] + data = [ + _port_chain['id'], + _port_chain['name'], + _port_chain['port_pair_groups'], + _port_chain['flow_classifiers'], + _port_chain['chain_parameters'], + _port_chain['chain_id'] + ] + data_long = [ + _port_chain['id'], + _port_chain['name'], + _port_chain['project_id'], + _port_chain['chain_id'], + _port_chain['port_pair_groups'], + _port_chain['flow_classifiers'], + _port_chain['chain_parameters'], + _port_chain['description'] + ] + _port_chain1 = {'port_chains': _port_chain} + _port_chain_id = _port_chain['id'] + + def setUp(self): + super(TestListSfcPortChain, self).setUp() + mock.patch( + 'neutronclient.osc.v2.sfc.sfc_port_chain._get_id', + new=_get_id).start() + self.neutronclient.list_port_chain = mock.Mock( + return_value={'port_chains': self._port_chains} + ) + # Get the command object to test + self.cmd = sfc_port_chain.ListSfcPortChain(self.app, self.namespace) + + def test_list_port_chain(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns = self.cmd.take_action(parsed_args)[0] + pcs = self.neutronclient.list_port_chain()['port_chains'] + pc = pcs[0] + data = [ + pc['id'], + pc['name'], + pc['port_pair_groups'], + pc['flow_classifiers'], + pc['chain_parameters'], + pc['chain_id'] + ] + self.assertEqual(list(self.columns), columns) + self.assertEqual(self.data, data) + + def test_list_port_chain_with_long_opion(self): + arglist = ['--long'] + verifylist = [('long', True)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns = self.cmd.take_action(parsed_args)[0] + pcs = self.neutronclient.list_port_chain()['port_chains'] + pc = pcs[0] + data = [ + pc['id'], + pc['name'], + pc['project_id'], + pc['chain_id'], + pc['port_pair_groups'], + pc['flow_classifiers'], + pc['chain_parameters'], + pc['description'] + ] + self.assertEqual(list(self.columns_long), columns) + self.assertEqual(self.data_long, data) + + +class TestSetSfcPortChain(fakes.TestNeutronClientOSCV2): + _port_chain = fakes.FakeSfcPortChain.create_port_chain() + resource = _port_chain + res = 'port_chain' + _port_chain_name = _port_chain['name'] + _port_chain_id = _port_chain['id'] + pc_ppg = _port_chain['port_pair_groups'] + pc_fc = _port_chain['flow_classifiers'] + + def setUp(self): + super(TestSetSfcPortChain, self).setUp() + mock.patch( + 'neutronclient.osc.v2.sfc.sfc_port_chain._get_id', + new=_get_id).start() + self.mocked = self.neutronclient.update_port_chain + self.cmd = sfc_port_chain.SetSfcPortChain(self.app, self.namespace) + + def test_set_port_chain(self): + client = self.app.client_manager.neutronclient + mock_port_chain_update = client.update_port_chain + arglist = [ + self._port_chain_name, + '--name', 'name_updated', + '--description', 'desc_updated', + ] + verifylist = [ + ('port_chain', self._port_chain_name), + ('name', 'name_updated'), + ('description', 'desc_updated'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + attrs = {'port_chain': {'name': 'name_updated', + 'description': 'desc_updated'}} + mock_port_chain_update.assert_called_once_with(self._port_chain_name, + attrs) + self.assertIsNone(result) + + def test_set_flow_classifier(self): + target = self.resource['id'] + fc1 = 'flow_classifier1' + + def _mock_flow_classifier(*args, **kwargs): + + if self.neutronclient.find_resource.call_count == 1: + self.neutronclient.find_resource.assert_called_with( + 'flow_classifier', fc1, cmd_resource='sfc_flow_classifier') + return {'id': args[1]} + + if self.neutronclient.find_resource.call_count == 2: + self.neutronclient.find_resource.assert_called_with( + self.res, target, cmd_resource='sfc_port_chain') + return {'flow_classifiers': self.pc_fc} + self.neutronclient.find_resource.side_effect = _mock_flow_classifier + arglist = [ + target, + '--flow-classifier', fc1, + ] + verifylist = [ + (self.res, target), + ('flow_classifiers', [fc1]) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + expect = {'flow_classifiers': sorted([self.pc_fc, fc1])} + self.mocked.assert_called_once_with(target, {self.res: expect}) + self.assertEqual(2, self.neutronclient.find_resource.call_count) + self.assertIsNone(result) + + def test_set_no_flow_classifier(self): + client = self.app.client_manager.neutronclient + mock_port_chain_update = client.update_port_chain + arglist = [ + self._port_chain_name, + '--no-flow-classifier', + ] + verifylist = [ + ('port_chain', self._port_chain_name), + ('no_flow_classifier', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + attrs = {'port_chain': {'flow_classifiers': []}} + mock_port_chain_update.assert_called_once_with(self._port_chain_name, + attrs) + self.assertIsNone(result) + + def test_set_port_pair_groups(self): + target = self.resource['id'] + existing_ppg = self.pc_ppg + ppg1 = 'port_pair_group1' + ppg2 = 'port_pair_group2' + + def _mock_flow_classifier(*args, **kwargs): + if self.neutronclient.find_resource.call_count == 1: + self.neutronclient.find_resource.assert_called_with( + self.res, target, cmd_resource='sfc_port_chain') + return {'port_pair_groups': self.pc_ppg} + + if self.neutronclient.find_resource.call_count == 2: + self.neutronclient.find_resource.assert_called_with( + 'port_pair_group', ppg1, + cmd_resource='sfc_port_pair_group') + return {'id': args[1]} + + if self.neutronclient.find_resource.call_count == 3: + self.neutronclient.find_resource.assert_called_with( + 'port_pair_group', ppg2, + cmd_resource='sfc_port_pair_group') + return {'id': args[1]} + + self.neutronclient.find_resource.side_effect = _mock_flow_classifier + arglist = [ + target, + '--port-pair-group', ppg1, + '--port-pair-group', ppg2, + ] + verifylist = [ + (self.res, target), + ('port_pair_groups', [ppg1, ppg2]) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + expect = {'port_pair_groups': sorted([existing_ppg, ppg1, ppg2])} + self.mocked.assert_called_once_with(target, {self.res: expect}) + self.assertEqual(3, self.neutronclient.find_resource.call_count) + self.assertIsNone(result) + + def test_set_no_port_pair_group(self): + target = self.resource['id'] + ppg1 = 'port_pair_group1' + + def _mock_port_pair_group(*args, **kwargs): + + if self.neutronclient.find_resource.call_count == 1: + self.neutronclient.find_resource.assert_called_with( + 'port_pair_group', ppg1, + cmd_resource='sfc_port_pair_group') + return {'id': args[1]} + self.neutronclient.find_resource.side_effect = _mock_port_pair_group + arglist = [ + target, + '--no-port-pair-group', + '--port-pair-group', ppg1, + ] + verifylist = [ + (self.res, target), + ('no_port_pair_group', True), + ('port_pair_groups', [ppg1]) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + expect = {'port_pair_groups': [ppg1]} + self.mocked.assert_called_once_with(target, {self.res: expect}) + self.assertEqual(1, self.neutronclient.find_resource.call_count) + self.assertIsNone(result) + + def test_set_only_no_port_pair_group(self): + target = self.resource['id'] + arglist = [ + target, + '--no-port-pair-group', + ] + verifylist = [ + (self.res, target), + ('no_port_pair_group', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args) + + +class TestShowSfcPortChain(fakes.TestNeutronClientOSCV2): + + _pc = fakes.FakeSfcPortChain.create_port_chain() + data = ( + _pc['chain_id'], + _pc['chain_parameters'], + _pc['description'], + _pc['flow_classifiers'], + _pc['id'], + _pc['name'], + _pc['port_pair_groups'], + _pc['project_id'] + ) + _port_chain = {'port_chain': _pc} + _port_chain_id = _pc['id'] + columns = ('Chain ID', + 'Chain Parameters', + 'Description', + 'Flow Classifiers', + 'ID', + 'Name', + 'Port Pair Groups', + 'Project') + + def setUp(self): + super(TestShowSfcPortChain, self).setUp() + mock.patch( + 'neutronclient.osc.v2.sfc.sfc_port_chain._get_id', + new=_get_id).start() + self.neutronclient.show_port_chain = mock.Mock( + return_value=self._port_chain + ) + # Get the command object to test + self.cmd = sfc_port_chain.ShowSfcPortChain(self.app, self.namespace) + + def test_show_port_chain(self): + client = self.app.client_manager.neutronclient + mock_port_chain_show = client.show_port_chain + arglist = [ + self._port_chain_id, + ] + verifylist = [ + ('port_chain', self._port_chain_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + mock_port_chain_show.assert_called_once_with(self._port_chain_id) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestUnsetSfcPortChain(fakes.TestNeutronClientOSCV2): + _port_chain = fakes.FakeSfcPortChain.create_port_chain() + resource = _port_chain + res = 'port_chain' + _port_chain_name = _port_chain['name'] + _port_chain_id = _port_chain['id'] + pc_ppg = _port_chain['port_pair_groups'] + pc_fc = _port_chain['flow_classifiers'] + + def setUp(self): + super(TestUnsetSfcPortChain, self).setUp() + mock.patch( + 'neutronclient.osc.v2.sfc.sfc_port_chain._get_id', + new=_get_id).start() + self.neutronclient.update_port_chain = mock.Mock( + return_value=None) + self.mocked = self.neutronclient.update_port_chain + self.cmd = sfc_port_chain.UnsetSfcPortChain(self.app, self.namespace) + + def test_unset_port_pair_group(self): + target = self.resource['id'] + ppg1 = 'port_pair_group1' + + def _mock_port_pair_group(*args, **kwargs): + if self.neutronclient.find_resource.call_count == 1: + self.neutronclient.find_resource.assert_called_with( + self.res, target, cmd_resource='sfc_port_chain') + return {'port_pair_groups': self.pc_ppg} + + if self.neutronclient.find_resource.call_count == 2: + self.neutronclient.find_resource.assert_called_with( + 'port_pair_group', ppg1, + cmd_resource='sfc_port_pair_group') + return {'id': args[1]} + if self.neutronclient.find_resource.call_count == 3: + self.neutronclient.find_resource.assert_called_with( + self.res, target, cmd_resource='sfc_port_chain') + return {'id': args[1]} + self.neutronclient.find_resource.side_effect = _mock_port_pair_group + + arglist = [ + target, + '--port-pair-group', ppg1, + ] + verifylist = [ + (self.res, target), + ('port_pair_groups', [ppg1]) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + expect = {'port_pair_groups': sorted([self.pc_ppg])} + self.mocked.assert_called_once_with(target, {self.res: expect}) + self.assertIsNone(result) + + def test_unset_flow_classifier(self): + target = self.resource['id'] + fc1 = 'flow_classifier1' + + def _mock_flow_classifier(*args, **kwargs): + if self.neutronclient.find_resource.call_count == 1: + self.neutronclient.find_resource.assert_called_with( + self.res, target, cmd_resource='sfc_port_chain') + return {'flow_classifiers': self.pc_fc} + + if self.neutronclient.find_resource.call_count == 2: + self.neutronclient.find_resource.assert_called_with( + 'flow_classifier', fc1, cmd_resource='sfc_flow_classifier') + return {'id': args[1]} + self.neutronclient.find_resource.side_effect = _mock_flow_classifier + + arglist = [ + target, + '--flow-classifier', fc1, + ] + verifylist = [ + (self.res, target), + ('flow_classifiers', [fc1]) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + expect = {'flow_classifiers': sorted([self.pc_fc])} + self.mocked.assert_called_once_with(target, {self.res: expect}) + self.assertIsNone(result) + + def test_unset_all_flow_classifier(self): + client = self.app.client_manager.neutronclient + target = self.resource['id'] + mock_port_chain_update = client.update_port_chain + arglist = [ + target, + '--all-flow-classifier', + ] + verifylist = [ + (self.res, target), + ('all_flow_classifier', True) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + expect = {'flow_classifiers': []} + mock_port_chain_update.assert_called_once_with(target, + {self.res: expect}) + self.assertIsNone(result) diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_port_pair.py b/neutronclient/tests/unit/osc/v2/sfc/test_port_pair.py new file mode 100755 index 000000000..e6c539ae6 --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/sfc/test_port_pair.py @@ -0,0 +1,302 @@ +# Copyright (c) 2017 Huawei Technologies India Pvt.Limited. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import mock + +from neutronclient.osc.v2.sfc import sfc_port_pair +from neutronclient.tests.unit.osc.v2.sfc import fakes + + +def _get_id(client, id_or_name, resource): + return id_or_name + + +class TestCreateSfcPortPair(fakes.TestNeutronClientOSCV2): + # The new port_pair created + _port_pair = fakes.FakeSfcPortPair.create_port_pair() + + columns = ('Description', + 'Egress Logical Port', + 'ID', + 'Ingress Logical Port', + 'Name', + 'Project', + 'Service Function Parameters') + + def get_data(self): + return ( + self._port_pair['description'], + self._port_pair['egress'], + self._port_pair['id'], + self._port_pair['ingress'], + self._port_pair['name'], + self._port_pair['project_id'], + self._port_pair['service_function_parameters'] + ) + + def setUp(self): + super(TestCreateSfcPortPair, self).setUp() + mock.patch('neutronclient.osc.v2.sfc.sfc_port_pair._get_id', + new=_get_id).start() + self.neutronclient.create_port_pair = mock.Mock( + return_value={'port_pair': self._port_pair}) + self.data = self.get_data() + + # Get the command object to test + self.cmd = sfc_port_pair.CreateSfcPortPair(self.app, self.namespace) + + def test_create_port_pair_default_options(self): + arglist = [ + "--ingress", self._port_pair['ingress'], + "--egress", self._port_pair['egress'], + self._port_pair['name'], + ] + verifylist = [ + ('ingress', self._port_pair['ingress']), + ('egress', self._port_pair['egress']), + ('name', self._port_pair['name']) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = (self.cmd.take_action(parsed_args)) + + self.neutronclient.create_port_pair.assert_called_once_with({ + 'port_pair': {'name': self._port_pair['name'], + 'ingress': self._port_pair['ingress'], + 'egress': self._port_pair['egress'], + 'service_function_parameters': None, + } + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_create_port_pair_all_options(self): + arglist = [ + "--description", self._port_pair['description'], + "--egress", self._port_pair['egress'], + "--ingress", self._port_pair['ingress'], + self._port_pair['name'], + "--service-function-parameters", 'correlation=None,weight=1', + ] + + sfp = [{'correlation': 'None', 'weight': '1'}] + + verifylist = [ + ('ingress', self._port_pair['ingress']), + ('egress', self._port_pair['egress']), + ('name', self._port_pair['name']), + ('description', self._port_pair['description']), + ('service_function_parameters', sfp) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = (self.cmd.take_action(parsed_args)) + + self.neutronclient.create_port_pair.assert_called_once_with({ + 'port_pair': {'name': self._port_pair['name'], + 'ingress': self._port_pair['ingress'], + 'egress': self._port_pair['egress'], + 'description': self._port_pair['description'], + 'service_function_parameters': + [{'correlation': 'None', 'weight': '1'}], + } + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestDeleteSfcPortPair(fakes.TestNeutronClientOSCV2): + + _port_pair = fakes.FakeSfcPortPair.create_port_pairs(count=1) + + def setUp(self): + super(TestDeleteSfcPortPair, self).setUp() + mock.patch('neutronclient.osc.v2.sfc.sfc_port_pair._get_id', + new=_get_id).start() + self.neutronclient.delete_port_pair = mock.Mock(return_value=None) + self.cmd = sfc_port_pair.DeleteSfcPortPair(self.app, self.namespace) + + def test_delete_port_pair(self): + client = self.app.client_manager.neutronclient + mock_port_pair_delete = client.delete_port_pair + arglist = [ + self._port_pair[0]['id'], + ] + verifylist = [ + ('port_pair', self._port_pair[0]['id']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + mock_port_pair_delete.assert_called_once_with( + self._port_pair[0]['id']) + self.assertIsNone(result) + + +class TestListSfcPortPair(fakes.TestNeutronClientOSCV2): + _port_pairs = fakes.FakeSfcPortPair.create_port_pairs() + columns = ('ID', 'Name', 'Ingress Logical Port', 'Egress Logical Port') + columns_long = ('ID', 'Name', 'Ingress Logical Port', + 'Egress Logical Port', 'Service Function Parameters', + 'Description', 'Project') + _port_pair = _port_pairs[0] + data = [ + _port_pair['id'], + _port_pair['name'], + _port_pair['ingress'], + _port_pair['egress'] + ] + data_long = [ + _port_pair['id'], + _port_pair['name'], + _port_pair['ingress'], + _port_pair['egress'], + _port_pair['service_function_parameters'], + _port_pair['description'] + ] + _port_pair1 = {'port_pairs': _port_pair} + _port_pair_id = _port_pair['id'], + + def setUp(self): + super(TestListSfcPortPair, self).setUp() + mock.patch('neutronclient.osc.v2.sfc.sfc_port_pair._get_id', + new=_get_id).start() + self.neutronclient.list_port_pair = mock.Mock( + return_value={'port_pairs': self._port_pairs} + ) + # Get the command object to test + self.cmd = sfc_port_pair.ListSfcPortPair(self.app, self.namespace) + + def test_list_port_pair(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns = self.cmd.take_action(parsed_args)[0] + port_pairs = self.neutronclient.list_port_pair()['port_pairs'] + port_pair = port_pairs[0] + data = [ + port_pair['id'], + port_pair['name'], + port_pair['ingress'], + port_pair['egress'] + ] + self.assertEqual(list(self.columns), columns) + self.assertEqual(self.data, data) + + def test_list_with_long_option(self): + arglist = ['--long'] + verifylist = [('long', True)] + port_pairs = self.neutronclient.list_port_pair()['port_pairs'] + port_pair = port_pairs[0] + data = [ + port_pair['id'], + port_pair['name'], + port_pair['ingress'], + port_pair['egress'], + port_pair['service_function_parameters'], + port_pair['description'] + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns_long = self.cmd.take_action(parsed_args)[0] + self.assertEqual(list(self.columns_long), columns_long) + self.assertEqual(self.data_long, data) + + +class TestSetSfcPortPair(fakes.TestNeutronClientOSCV2): + _port_pair = fakes.FakeSfcPortPair.create_port_pair() + _port_pair_name = _port_pair['name'] + _port_pair_id = _port_pair['id'] + + def setUp(self): + super(TestSetSfcPortPair, self).setUp() + mock.patch('neutronclient.osc.v2.sfc.sfc_port_pair._get_id', + new=_get_id).start() + self.neutronclient.update_port_pair = mock.Mock(return_value=None) + self.cmd = sfc_port_pair.SetSfcPortPair(self.app, self.namespace) + + def test_set_port_pair(self): + client = self.app.client_manager.neutronclient + mock_port_pair_update = client.update_port_pair + arglist = [ + self._port_pair_name, + '--name', 'name_updated', + '--description', 'desc_updated' + ] + verifylist = [ + ('port_pair', self._port_pair_name), + ('name', 'name_updated'), + ('description', 'desc_updated'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + attrs = {'port_pair': { + 'name': 'name_updated', + 'description': 'desc_updated'} + } + mock_port_pair_update.assert_called_once_with(self._port_pair_name, + attrs) + self.assertIsNone(result) + + +class TestShowSfcPortPair(fakes.TestNeutronClientOSCV2): + + _pp = fakes.FakeSfcPortPair.create_port_pair() + + data = ( + _pp['description'], + _pp['egress'], + _pp['id'], + _pp['ingress'], + _pp['name'], + _pp['project_id'], + _pp['service_function_parameters'], + ) + _port_pair = {'port_pair': _pp} + _port_pair_id = _pp['id'] + columns = ( + 'Description', + 'Egress Logical Port', + 'ID', + 'Ingress Logical Port', + 'Name', + 'Project', + 'Service Function Parameters' + ) + + def setUp(self): + super(TestShowSfcPortPair, self).setUp() + mock.patch('neutronclient.osc.v2.sfc.sfc_port_pair._get_id', + new=_get_id).start() + + self.neutronclient.show_port_pair = mock.Mock( + return_value=self._port_pair + ) + + # Get the command object to test + self.cmd = sfc_port_pair.ShowSfcPortPair(self.app, self.namespace) + + def test_show_port_pair(self): + client = self.app.client_manager.neutronclient + mock_port_pair_show = client.show_port_pair + arglist = [ + self._port_pair_id, + ] + verifylist = [ + ('port_pair', self._port_pair_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + mock_port_pair_show.assert_called_once_with(self._port_pair_id) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_port_pair_group.py b/neutronclient/tests/unit/osc/v2/sfc/test_port_pair_group.py new file mode 100755 index 000000000..188494c4b --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/sfc/test_port_pair_group.py @@ -0,0 +1,424 @@ +# Copyright (c) 2017 Huawei Technologies India Pvt.Limited. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import mock + +from neutronclient.osc.v2.sfc import sfc_port_pair_group +from neutronclient.tests.unit.osc.v2.sfc import fakes + + +def _get_id(client, id_or_name, resource): + return id_or_name + + +class TestCreateSfcPortPairGroup(fakes.TestNeutronClientOSCV2): + + _port_pair_group = fakes.FakeSfcPortPairGroup.create_port_pair_group() + + columns = ('Description', + 'ID', + 'Loadbalance ID', + 'Name', + 'Port Pair', + 'Port Pair Group Parameters', + 'Project') + + def get_data(self): + return ( + self._port_pair_group['description'], + self._port_pair_group['id'], + self._port_pair_group['group_id'], + self._port_pair_group['name'], + self._port_pair_group['port_pairs'], + self._port_pair_group['port_pair_group_parameters'], + self._port_pair_group['project_id'] + ) + + def setUp(self): + super(TestCreateSfcPortPairGroup, self).setUp() + mock.patch( + 'neutronclient.osc.v2.sfc.sfc_port_pair_group._get_id', + new=_get_id).start() + self.neutronclient.create_port_pair_group = mock.Mock( + return_value={'port_pair_group': self._port_pair_group}) + self.data = self.get_data() + # Get the command object to test + self.cmd = sfc_port_pair_group.CreateSfcPortPairGroup(self.app, + self.namespace) + + def test_create_port_pair_group_default_options(self): + arglist = [ + "--port-pair", self._port_pair_group['port_pairs'], + self._port_pair_group['name'], + ] + verifylist = [ + ('port_pairs', [self._port_pair_group['port_pairs']]), + ('name', self._port_pair_group['name']), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = (self.cmd.take_action(parsed_args)) + self.neutronclient.create_port_pair_group.assert_called_once_with({ + 'port_pair_group': { + 'name': self._port_pair_group['name'], + 'port_pairs': [self._port_pair_group['port_pairs']]} + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_create_port_pair_group(self): + arglist = [ + "--description", self._port_pair_group['description'], + "--port-pair", self._port_pair_group['port_pairs'], + self._port_pair_group['name'], + ] + verifylist = [ + ('port_pairs', [self._port_pair_group['port_pairs']]), + ('name', self._port_pair_group['name']), + ('description', self._port_pair_group['description']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = (self.cmd.take_action(parsed_args)) + + self.neutronclient.create_port_pair_group.assert_called_once_with({ + 'port_pair_group': { + 'name': self._port_pair_group['name'], + 'port_pairs': [self._port_pair_group['port_pairs']], + 'description': self._port_pair_group['description'], + } + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestDeleteSfcPortPairGroup(fakes.TestNeutronClientOSCV2): + + _port_pair_group = (fakes.FakeSfcPortPairGroup.create_port_pair_groups + (count=1)) + + def setUp(self): + super(TestDeleteSfcPortPairGroup, self).setUp() + mock.patch( + 'neutronclient.osc.v2.sfc.sfc_port_pair_group._get_id', + new=_get_id).start() + self.neutronclient.delete_port_pair_group = mock.Mock( + return_value=None) + self.cmd = sfc_port_pair_group.DeleteSfcPortPairGroup(self.app, + self.namespace) + + def test_delete_port_pair_group(self): + client = self.app.client_manager.neutronclient + mock_port_pair_group_delete = client.delete_port_pair_group + arglist = [ + self._port_pair_group[0]['id'], + ] + verifylist = [ + ('port_pair_group', self._port_pair_group[0]['id']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + mock_port_pair_group_delete.assert_called_once_with( + self._port_pair_group[0]['id']) + self.assertIsNone(result) + + +class TestListSfcPortPairGroup(fakes.TestNeutronClientOSCV2): + _ppgs = fakes.FakeSfcPortPairGroup.create_port_pair_groups(count=1) + columns = ('ID', 'Name', 'Port Pair', 'Port Pair Group Parameters') + columns_long = ('ID', 'Name', 'Port Pair', 'Port Pair Group Parameters', + 'Description', 'Loadbalance ID', 'Project') + _port_pair_group = _ppgs[0] + data = [ + _port_pair_group['id'], + _port_pair_group['name'], + _port_pair_group['port_pairs'], + _port_pair_group['port_pair_group_parameters'] + ] + data_long = [ + _port_pair_group['id'], + _port_pair_group['name'], + _port_pair_group['port_pairs'], + _port_pair_group['port_pair_group_parameters'], + _port_pair_group['description'] + ] + _port_pair_group1 = {'port_pair_groups': _port_pair_group} + _port_pair_id = _port_pair_group['id'] + + def setUp(self): + super(TestListSfcPortPairGroup, self).setUp() + mock.patch( + 'neutronclient.osc.v2.sfc.sfc_port_pair_group._get_id', + new=_get_id).start() + + self.neutronclient.list_port_pair_group = mock.Mock( + return_value={'port_pair_groups': self._ppgs} + ) + # Get the command object to test + self.cmd = sfc_port_pair_group.ListSfcPortPairGroup(self.app, + self.namespace) + + def test_list_port_pair_group(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns = self.cmd.take_action(parsed_args)[0] + ppgs = self.neutronclient.list_port_pair_group()['port_pair_groups'] + ppg = ppgs[0] + data = [ + ppg['id'], + ppg['name'], + ppg['port_pairs'], + ppg['port_pair_group_parameters'] + ] + self.assertEqual(list(self.columns), columns) + self.assertEqual(self.data, data) + + def test_list_with_long_option(self): + arglist = ['--long'] + verifylist = [('long', True)] + ppgs = self.neutronclient.list_port_pair_group()['port_pair_groups'] + ppg = ppgs[0] + data = [ + ppg['id'], + ppg['name'], + ppg['port_pairs'], + ppg['port_pair_group_parameters'], + ppg['description'] + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns_long = self.cmd.take_action(parsed_args)[0] + self.assertEqual(list(self.columns_long), columns_long) + self.assertEqual(self.data_long, data) + + +class TestSetSfcPortPairGroup(fakes.TestNeutronClientOSCV2): + _port_pair_group = fakes.FakeSfcPortPairGroup.create_port_pair_group() + resource = _port_pair_group + res = 'port_pair_group' + _port_pair_group_name = _port_pair_group['name'] + ppg_pp = _port_pair_group['port_pairs'] + _port_pair_group_id = _port_pair_group['id'] + + def setUp(self): + super(TestSetSfcPortPairGroup, self).setUp() + + mock.patch( + 'neutronclient.osc.v2.sfc.sfc_port_pair_group._get_id', + new=_get_id).start() + self.neutronclient.update_port_pair_group = mock.Mock( + return_value=None) + self.mocked = self.neutronclient.update_port_pair_group + self.cmd = sfc_port_pair_group.SetSfcPortPairGroup(self.app, + self.namespace) + + def test_set_port_pair_group(self): + target = self.resource['id'] + port_pair1 = 'additional_port1' + port_pair2 = 'additional_port2' + + def _mock_port_pair_group(*args, **kwargs): + + if self.neutronclient.find_resource.call_count == 1: + self.neutronclient.find_resource.assert_called_with( + 'port_pair', port_pair1, cmd_resource='sfc_port_pair') + return {'id': args[1]} + + if self.neutronclient.find_resource.call_count == 2: + self.neutronclient.find_resource.assert_called_with( + 'port_pair', port_pair2, cmd_resource='sfc_port_pair') + return {'id': args[1]} + + if self.neutronclient.find_resource.call_count == 3: + self.neutronclient.find_resource.assert_called_with( + self.res, target, cmd_resource='sfc_port_pair_group') + return {'port_pairs': self.ppg_pp} + + self.neutronclient.find_resource.side_effect = _mock_port_pair_group + + arglist = [ + target, + '--port-pair', port_pair1, + '--port-pair', port_pair2, + ] + verifylist = [ + (self.res, target), + ('port_pairs', [port_pair1, port_pair2]) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + expect = {'port_pairs': sorted([self.ppg_pp, port_pair1, port_pair2])} + self.mocked.assert_called_once_with(target, {self.res: expect}) + self.assertEqual(3, self.neutronclient.find_resource.call_count) + self.assertIsNone(result) + + def test_set_no_port_pair(self): + client = self.app.client_manager.neutronclient + mock_port_pair_group_update = client.update_port_pair_group + arglist = [ + self._port_pair_group_name, + '--name', 'name_updated', + '--description', 'desc_updated', + '--no-port-pair', + ] + verifylist = [ + ('port_pair_group', self._port_pair_group_name), + ('name', 'name_updated'), + ('description', 'desc_updated'), + ('no_port_pair', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = {'port_pair_group': {'name': 'name_updated', + 'description': 'desc_updated', + 'port_pairs': []}} + mock_port_pair_group_update.assert_called_once_with( + self._port_pair_group_name, attrs) + self.assertIsNone(result) + + +class TestShowSfcPortPairGroup(fakes.TestNeutronClientOSCV2): + + _ppg = fakes.FakeSfcPortPairGroup.create_port_pair_group() + data = ( + _ppg['description'], + _ppg['id'], + _ppg['group_id'], + _ppg['name'], + _ppg['port_pairs'], + _ppg['port_pair_group_parameters'], + _ppg['project_id']) + _port_pair_group = {'port_pair_group': _ppg} + _port_pair_group_id = _ppg['id'] + columns = ( + 'Description', + 'ID', + 'Loadbalance ID', + 'Name', + 'Port Pair', + 'Port Pair Group Parameters', + 'Project') + + def setUp(self): + super(TestShowSfcPortPairGroup, self).setUp() + mock.patch( + 'neutronclient.osc.v2.sfc.sfc_port_pair_group._get_id', + new=_get_id).start() + + self.neutronclient.show_port_pair_group = mock.Mock( + return_value=self._port_pair_group + ) + self.cmd = sfc_port_pair_group.ShowSfcPortPairGroup(self.app, + self.namespace) + + def test_show_port_pair_group(self): + client = self.app.client_manager.neutronclient + mock_port_pair_group_show = client.show_port_pair_group + arglist = [ + self._port_pair_group_id, + ] + verifylist = [ + ('port_pair_group', self._port_pair_group_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + mock_port_pair_group_show.assert_called_once_with( + self._port_pair_group_id) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestUnsetSfcPortPairGroup(fakes.TestNeutronClientOSCV2): + _port_pair_group = fakes.FakeSfcPortPairGroup.create_port_pair_group() + resource = _port_pair_group + res = 'port_pair_group' + _port_pair_group_name = _port_pair_group['name'] + _port_pair_group_id = _port_pair_group['id'] + ppg_pp = _port_pair_group['port_pairs'] + + def setUp(self): + super(TestUnsetSfcPortPairGroup, self).setUp() + mock.patch( + 'neutronclient.osc.v2.sfc.sfc_port_pair_group._get_id', + new=_get_id).start() + self.neutronclient.update_port_pair_group = mock.Mock( + return_value=None) + self.mocked = self.neutronclient.update_port_pair_group + self.cmd = sfc_port_pair_group.UnsetSfcPortPairGroup( + self.app, self.namespace) + + def test_unset_port_pair(self): + target = self.resource['id'] + port_pair1 = 'additional_port1' + port_pair2 = 'additional_port2' + + def _mock_port_pair(*args, **kwargs): + + if self.neutronclient.find_resource.call_count == 1: + self.neutronclient.find_resource.assert_called_with( + self.res, target, cmd_resource='sfc_port_pair_group') + return {'port_pairs': self.ppg_pp} + + if self.neutronclient.find_resource.call_count == 2: + self.neutronclient.find_resource.assert_called_with( + 'port_pair', port_pair1, cmd_resource='sfc_port_pair') + return {'id': args[1]} + + if self.neutronclient.find_resource.call_count == 3: + self.neutronclient.find_resource.assert_called_with( + 'port_pair', port_pair2, cmd_resource='sfc_port_pair') + return {'id': args[1]} + + if self.neutronclient.find_resource.call_count == 4: + self.neutronclient.find_resource.assert_called_with( + self.res, target, cmd_resource='sfc_port_pair_group') + return {'id': args[1]} + + self.neutronclient.find_resource.side_effect = _mock_port_pair + + arglist = [ + target, + '--port-pair', port_pair1, + '--port-pair', port_pair2, + ] + verifylist = [ + (self.res, target), + ('port_pairs', [port_pair1, port_pair2]) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + expect = {'port_pairs': sorted([self.ppg_pp])} + self.mocked.assert_called_once_with(target, {self.res: expect}) + self.assertIsNone(result) + + def test_unset_all_port_pair(self): + client = self.app.client_manager.neutronclient + mock_port_pair_group_update = client.update_port_pair_group + arglist = [ + self._port_pair_group_name, + '--all-port-pair', + ] + verifylist = [ + ('port_pair_group', self._port_pair_group_name), + ('all_port_pair', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = {'port_pair_group': {'port_pairs': []}} + mock_port_pair_group_update.assert_called_once_with( + self._port_pair_group_name, attrs) + self.assertIsNone(result) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 759f7765e..96bb01d55 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -511,6 +511,16 @@ class Client(ClientBase): security_group_path = "/security-groups/%s" security_group_rules_path = "/security-group-rules" security_group_rule_path = "/security-group-rules/%s" + + sfc_flow_classifiers_path = "/sfc/flow_classifiers" + sfc_flow_classifier_path = "/sfc/flow_classifiers/%s" + sfc_port_pairs_path = "/sfc/port_pairs" + sfc_port_pair_path = "/sfc/port_pairs/%s" + sfc_port_pair_groups_path = "/sfc/port_pair_groups" + sfc_port_pair_group_path = "/sfc/port_pair_groups/%s" + sfc_port_chains_path = "/sfc/port_chains" + sfc_port_chain_path = "/sfc/port_chains/%s" + endpoint_groups_path = "/vpn/endpoint-groups" endpoint_group_path = "/vpn/endpoint-groups/%s" vpnservices_path = "/vpn/vpnservices" @@ -692,6 +702,10 @@ class Client(ClientBase): 'bgpvpns': 'bgpvpn', 'network_associations': 'network_association', 'router_associations': 'router_association', + 'flow_classifiers': 'flow_classifier', + 'port_pairs': 'port_pair', + 'port_pair_groups': 'port_pair_group', + 'port_chains': 'port_chain', } def list_ext(self, collection, path, retrieve_all, **_params): @@ -2159,6 +2173,95 @@ def delete_bgpvpn_router_assoc(self, bgpvpn, router_assoc): return self.delete( self.bgpvpn_router_association_path % (bgpvpn, router_assoc)) + def create_port_pair(self, body=None): + """Creates a new Port Pair.""" + return self.post(self.sfc_port_pairs_path, body=body) + + def update_port_pair(self, port_pair, body=None): + """Update a Port Pair.""" + return self.put(self.sfc_port_pair_path % port_pair, body=body) + + def delete_port_pair(self, port_pair): + """Deletes the specified Port Pair.""" + return self.delete(self.sfc_port_pair_path % (port_pair)) + + def list_port_pair(self, retrieve_all=True, **_params): + """Fetches a list of all Port Pairs.""" + return self.list('port_pairs', self.sfc_port_pairs_path, retrieve_all, + **_params) + + def show_port_pair(self, port_pair, **_params): + """Fetches information of a certain Port Pair.""" + return self.get(self.sfc_port_pair_path % (port_pair), params=_params) + + def create_port_pair_group(self, body=None): + """Creates a new Port Pair Group.""" + return self.post(self.sfc_port_pair_groups_path, body=body) + + def update_port_pair_group(self, port_pair_group, body=None): + """Update a Port Pair Group.""" + return self.put(self.sfc_port_pair_group_path % port_pair_group, + body=body) + + def delete_port_pair_group(self, port_pair_group): + """Deletes the specified Port Pair Group.""" + return self.delete(self.sfc_port_pair_group_path % (port_pair_group)) + + def list_port_pair_group(self, retrieve_all=True, **_params): + """Fetches a list of all Port Pair Groups.""" + return self.list('port_pair_groups', self.sfc_port_pair_groups_path, + retrieve_all, **_params) + + def show_port_pair_group(self, port_pair_group, **_params): + """Fetches information of a certain Port Pair Group.""" + return self.get(self.sfc_port_pair_group_path % (port_pair_group), + params=_params) + + def create_port_chain(self, body=None): + """Creates a new Port Chain.""" + return self.post(self.sfc_port_chains_path, body=body) + + def update_port_chain(self, port_chain, body=None): + """Update a Port Chain.""" + return self.put(self.sfc_port_chain_path % port_chain, body=body) + + def delete_port_chain(self, port_chain): + """Deletes the specified Port Chain.""" + return self.delete(self.sfc_port_chain_path % (port_chain)) + + def list_port_chain(self, retrieve_all=True, **_params): + """Fetches a list of all Port Chains.""" + return self.list('port_chains', self.sfc_port_chains_path, + retrieve_all, **_params) + + def show_port_chain(self, port_chain, **_params): + """Fetches information of a certain Port Chain.""" + return self.get(self.sfc_port_chain_path % (port_chain), + params=_params) + + def create_flow_classifier(self, body=None): + """Creates a new Flow Classifier.""" + return self.post(self.sfc_flow_classifiers_path, body=body) + + def update_flow_classifier(self, flow_classifier, body=None): + """Update a Flow Classifier.""" + return self.put(self.sfc_flow_classifier_path % flow_classifier, + body=body) + + def delete_flow_classifier(self, flow_classifier): + """Deletes the specified Flow Classifier.""" + return self.delete(self.sfc_flow_classifier_path % (flow_classifier)) + + def list_flow_classifier(self, retrieve_all=True, **_params): + """Fetches a list of all Flow Classifiers.""" + return self.list('flow_classifiers', self.sfc_flow_classifiers_path, + retrieve_all, **_params) + + def show_flow_classifier(self, flow_classifier, **_params): + """Fetches information of a certain Flow Classifier.""" + return self.get(self.sfc_flow_classifier_path % (flow_classifier), + params=_params) + def __init__(self, **kwargs): """Initialize a new client for the Neutron v2.0 API.""" super(Client, self).__init__(**kwargs) diff --git a/releasenotes/notes/add-sfc-commands.yaml b/releasenotes/notes/add-sfc-commands.yaml new file mode 100644 index 000000000..c081f50f9 --- /dev/null +++ b/releasenotes/notes/add-sfc-commands.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add OSC plugin support for the “Networking Service Function Chaining” feature commands along with client bindings. + [Blueprint `openstackclient-cli-porting `_] \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index ac7da5cae..d62e29ffd 100644 --- a/setup.cfg +++ b/setup.cfg @@ -43,6 +43,28 @@ openstack.neutronclient.v2 = network_trunk_set = neutronclient.osc.v2.trunk.network_trunk:SetNetworkTrunk network_trunk_show = neutronclient.osc.v2.trunk.network_trunk:ShowNetworkTrunk network_trunk_unset = neutronclient.osc.v2.trunk.network_trunk:UnsetNetworkTrunk + sfc_flow_classifier_create = neutronclient.osc.v2.sfc.sfc_flow_classifier:CreateSfcFlowClassifier + sfc_flow_classifier_delete = neutronclient.osc.v2.sfc.sfc_flow_classifier:DeleteSfcFlowClassifier + sfc_flow_classifier_list = neutronclient.osc.v2.sfc.sfc_flow_classifier:ListSfcFlowClassifier + sfc_flow_classifier_set = neutronclient.osc.v2.sfc.sfc_flow_classifier:SetSfcFlowClassifier + sfc_flow_classifier_show = neutronclient.osc.v2.sfc.sfc_flow_classifier:ShowSfcFlowClassifier + sfc_port_chain_create = neutronclient.osc.v2.sfc.sfc_port_chain:CreateSfcPortChain + sfc_port_chain_delete = neutronclient.osc.v2.sfc.sfc_port_chain:DeleteSfcPortChain + sfc_port_chain_list = neutronclient.osc.v2.sfc.sfc_port_chain:ListSfcPortChain + sfc_port_chain_set = neutronclient.osc.v2.sfc.sfc_port_chain:SetSfcPortChain + sfc_port_chain_show = neutronclient.osc.v2.sfc.sfc_port_chain:ShowSfcPortChain + sfc_port_chain_unset = neutronclient.osc.v2.sfc.sfc_port_chain:UnsetSfcPortChain + sfc_port_pair_create = neutronclient.osc.v2.sfc.sfc_port_pair:CreateSfcPortPair + sfc_port_pair_delete = neutronclient.osc.v2.sfc.sfc_port_pair:DeleteSfcPortPair + sfc_port_pair_list = neutronclient.osc.v2.sfc.sfc_port_pair:ListSfcPortPair + sfc_port_pair_set = neutronclient.osc.v2.sfc.sfc_port_pair:SetSfcPortPair + sfc_port_pair_show = neutronclient.osc.v2.sfc.sfc_port_pair:ShowSfcPortPair + sfc_port_pair_group_create = neutronclient.osc.v2.sfc.sfc_port_pair_group:CreateSfcPortPairGroup + sfc_port_pair_group_delete = neutronclient.osc.v2.sfc.sfc_port_pair_group:DeleteSfcPortPairGroup + sfc_port_pair_group_list = neutronclient.osc.v2.sfc.sfc_port_pair_group:ListSfcPortPairGroup + sfc_port_pair_group_set = neutronclient.osc.v2.sfc.sfc_port_pair_group:SetSfcPortPairGroup + sfc_port_pair_group_show = neutronclient.osc.v2.sfc.sfc_port_pair_group:ShowSfcPortPairGroup + sfc_port_pair_group_unset = neutronclient.osc.v2.sfc.sfc_port_pair_group:UnsetSfcPortPairGroup firewall_group_create = neutronclient.osc.v2.fwaas.firewallgroup:CreateFirewallGroup firewall_group_delete = neutronclient.osc.v2.fwaas.firewallgroup:DeleteFirewallGroup From dbf952e326d43da5db07d10f9de01bb7cc044fb8 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Thu, 13 Jul 2017 14:24:21 +0000 Subject: [PATCH 600/845] Updated from global requirements Change-Id: Idcc7808ae5af7fc42dafc0fc04c3ca1d69239288 --- test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index ae2856685..dfd3c4b70 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -19,4 +19,4 @@ sphinx>=1.6.2 # BSD testrepository>=0.0.18 # Apache-2.0/BSD testtools>=1.4.0 # MIT testscenarios>=0.4 # Apache-2.0/BSD -tempest>=14.0.0 # Apache-2.0 +tempest>=16.1.0 # Apache-2.0 From 37856d9d137018cc2f89ffa338168115d20ab159 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Tue, 18 Jul 2017 01:56:30 +0000 Subject: [PATCH 601/845] Updated from global requirements Change-Id: I607d35de1ff5d26747629a5f1dd5c114808b5ed7 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 617c23524..ee49f0608 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ iso8601>=0.1.11 # MIT netaddr!=0.7.16,>=0.7.13 # BSD osc-lib>=1.5.1 # Apache-2.0 oslo.i18n!=3.15.2,>=2.1.0 # Apache-2.0 -oslo.serialization>=1.10.0 # Apache-2.0 +oslo.serialization!=2.19.1,>=1.10.0 # Apache-2.0 oslo.utils>=3.20.0 # Apache-2.0 os-client-config>=1.27.0 # Apache-2.0 keystoneauth1>=2.21.0 # Apache-2.0 From 28fc2dcf061347d11d2861d73a3615c4d5f408e8 Mon Sep 17 00:00:00 2001 From: "ritesh.arya" Date: Thu, 13 Jul 2017 11:34:58 +0530 Subject: [PATCH 602/845] Replace uuid.uuid4() with uuidutils.generate_uuid() Openstack common has a wrapper for generating uuids. We should use that function to generate uuids for consistency. Change-Id: I86ae28e6042d1fac2b08540add8a9e23c9254c86 --- .../tests/functional/core/test_clientlib.py | 6 +-- .../tests/unit/osc/v2/fwaas/fakes.py | 38 ++++++++++++------- .../tests/unit/osc/v2/trunk/fakes.py | 14 ++++--- .../tests/unit/test_cli20_securitygroup.py | 8 ++-- neutronclient/tests/unit/test_http.py | 4 +- neutronclient/tests/unit/test_name_or_id.py | 20 +++++----- 6 files changed, 52 insertions(+), 38 deletions(-) diff --git a/neutronclient/tests/functional/core/test_clientlib.py b/neutronclient/tests/functional/core/test_clientlib.py index 23ba2d425..d4fdbae59 100644 --- a/neutronclient/tests/functional/core/test_clientlib.py +++ b/neutronclient/tests/functional/core/test_clientlib.py @@ -10,10 +10,10 @@ # License for the specific language governing permissions and limitations # under the License. -import uuid from keystoneauth1 import plugin as ksa_plugin from keystoneauth1 import session +from oslo_utils import uuidutils from tempest.lib import base import testtools @@ -95,11 +95,11 @@ def test_list_network(self): self.assertIsInstance(nets['networks'], list) def test_post_put_delete_network(self): - name = str(uuid.uuid4()) + name = uuidutils.generate_uuid() net = self.client.create_network({'network': {'name': name}}) net_id = net['network']['id'] self.assertEqual(name, net['network']['name']) - name2 = str(uuid.uuid4()) + name2 = uuidutils.generate_uuid() net = self.client.update_network(net_id, {'network': {'name': name2}}) self.assertEqual(name2, net['network']['name']) self.client.delete_network(net_id) diff --git a/neutronclient/tests/unit/osc/v2/fwaas/fakes.py b/neutronclient/tests/unit/osc/v2/fwaas/fakes.py index 7baef8421..b2883edf6 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/fakes.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/fakes.py @@ -16,9 +16,9 @@ import collections import copy -import uuid import mock +from oslo_utils import uuidutils class FakeFWaaS(object): @@ -67,16 +67,20 @@ class FirewallGroup(FakeFWaaS): def __init__(self): super(FirewallGroup, self).__init__() self.ordered = collections.OrderedDict(( - ('id', 'firewall-group-id-' + uuid.uuid4().hex), - ('name', 'my-group-' + uuid.uuid4().hex), + ('id', 'firewall-group-id-' + + uuidutils.generate_uuid(dashed=False)), + ('name', 'my-group-' + + uuidutils.generate_uuid(dashed=False)), ('ingress_firewall_policy_id', None), ('egress_firewall_policy_id', None), - ('description', 'my-desc-' + uuid.uuid4().hex), + ('description', 'my-desc-' + + uuidutils.generate_uuid(dashed=False)), ('status', 'INACTIVE'), ('ports', []), ('admin_state_up', True), ('public', False), - ('tenant_id', 'tenant-id-' + uuid.uuid4().hex), + ('tenant_id', 'tenant-id-' + + uuidutils.generate_uuid(dashed=False)), )) @@ -86,13 +90,17 @@ class FirewallPolicy(FakeFWaaS): def __init__(self): super(FirewallPolicy, self).__init__() self.ordered = collections.OrderedDict(( - ('id', 'firewall-policy-' + uuid.uuid4().hex), - ('name', 'my-policy-' + uuid.uuid4().hex), + ('id', 'firewall-policy-' + + uuidutils.generate_uuid(dashed=False)), + ('name', 'my-policy-' + + uuidutils.generate_uuid(dashed=False)), ('firewall_rules', []), - ('description', 'my-desc-' + uuid.uuid4().hex), + ('description', 'my-desc-' + + uuidutils.generate_uuid(dashed=False)), ('audited', True), ('public', False), - ('tenant_id', 'tenant-id-' + uuid.uuid4().hex), + ('tenant_id', 'tenant-id-' + + uuidutils.generate_uuid(dashed=False)), )) @@ -102,10 +110,13 @@ class FirewallRule(FakeFWaaS): def __init__(self): super(FirewallRule, self).__init__() self.ordered = collections.OrderedDict(( - ('id', 'firewall-rule-id-' + uuid.uuid4().hex), - ('name', 'my-rule-' + uuid.uuid4().hex), + ('id', 'firewall-rule-id-' + + uuidutils.generate_uuid(dashed=False)), + ('name', 'my-rule-' + + uuidutils.generate_uuid(dashed=False)), ('enabled', False), - ('description', 'my-desc-' + uuid.uuid4().hex), + ('description', 'my-desc-' + + uuidutils.generate_uuid(dashed=False)), ('ip_version', 4), ('action', 'deny'), ('protocol', None), @@ -114,5 +125,6 @@ def __init__(self): ('destination_ip_address', '192.168.2.2'), ('destination_port', '2:22222'), ('public', False), - ('tenant_id', 'tenant-id-' + uuid.uuid4().hex), + ('tenant_id', 'tenant-id-' + + uuidutils.generate_uuid(dashed=False)), )) diff --git a/neutronclient/tests/unit/osc/v2/trunk/fakes.py b/neutronclient/tests/unit/osc/v2/trunk/fakes.py index 12f773f03..f7da0dd77 100644 --- a/neutronclient/tests/unit/osc/v2/trunk/fakes.py +++ b/neutronclient/tests/unit/osc/v2/trunk/fakes.py @@ -11,9 +11,9 @@ # under the License. import copy -import uuid import mock +from oslo_utils import uuidutils class FakeTrunk(object): @@ -32,14 +32,16 @@ def create_one_trunk(attrs=None): # Set default attributes. trunk_attrs = { - 'id': 'trunk-id-' + uuid.uuid4().hex, - 'name': 'trunk-name-' + uuid.uuid4().hex, + 'id': 'trunk-id-' + uuidutils.generate_uuid(dashed=False), + 'name': 'trunk-name-' + uuidutils.generate_uuid(dashed=False), 'description': '', - 'port_id': 'port-' + uuid.uuid4().hex, + 'port_id': 'port-' + uuidutils.generate_uuid(dashed=False), 'admin_state_up': True, - 'project_id': 'project-id-' + uuid.uuid4().hex, + 'project_id': 'project-id-' + + uuidutils.generate_uuid(dashed=False), 'status': 'ACTIVE', - 'sub_ports': [{'port_id': 'subport-' + uuid.uuid4().hex, + 'sub_ports': [{'port_id': 'subport-' + + uuidutils.generate_uuid(dashed=False), 'segmentation_type': 'vlan', 'segmentation_id': 100}], } diff --git a/neutronclient/tests/unit/test_cli20_securitygroup.py b/neutronclient/tests/unit/test_cli20_securitygroup.py index bb4c3ddcb..5e6f01978 100644 --- a/neutronclient/tests/unit/test_cli20_securitygroup.py +++ b/neutronclient/tests/unit/test_cli20_securitygroup.py @@ -15,9 +15,9 @@ # under the License. import sys -import uuid from mox3 import mox +from oslo_utils import uuidutils import six from neutronclient.common import exceptions @@ -534,9 +534,9 @@ def _prepare_rule(self, rule_id=None, sg_id=None, tenant_id=None, protocol=None, port_range_min=None, port_range_max=None, remote_ip_prefix=None, remote_group_id=None, filters=None): - rule = {'id': rule_id or str(uuid.uuid4()), - 'tenant_id': tenant_id or str(uuid.uuid4()), - 'security_group_id': sg_id or str(uuid.uuid4()), + rule = {'id': rule_id or uuidutils.generate_uuid(), + 'tenant_id': tenant_id or uuidutils.generate_uuid(), + 'security_group_id': sg_id or uuidutils.generate_uuid(), 'direction': direction or 'ingress', 'ethertype': ethertype or 'IPv4', 'protocol': protocol, diff --git a/neutronclient/tests/unit/test_http.py b/neutronclient/tests/unit/test_http.py index a03df0756..73b7a30b0 100644 --- a/neutronclient/tests/unit/test_http.py +++ b/neutronclient/tests/unit/test_http.py @@ -14,8 +14,8 @@ # under the License. import abc -import uuid +from oslo_utils import uuidutils import osprofiler.profiler import osprofiler.web from requests_mock.contrib import fixture as mock_fixture @@ -128,7 +128,7 @@ class TestHTTPClientWithReqId(TestHTTPClientMixin, testtools.TestCase): """Tests for when global_request_id is set.""" def initialize(self): - self.req_id = "req-%s" % uuid.uuid4() + self.req_id = "req-%s" % uuidutils.generate_uuid() return client.HTTPClient(token=AUTH_TOKEN, endpoint_url=END_URL, global_request_id=self.req_id) diff --git a/neutronclient/tests/unit/test_name_or_id.py b/neutronclient/tests/unit/test_name_or_id.py index dcb9a23e6..5b5dc024f 100644 --- a/neutronclient/tests/unit/test_name_or_id.py +++ b/neutronclient/tests/unit/test_name_or_id.py @@ -14,9 +14,9 @@ # under the License. # -import uuid from mox3 import mox +from oslo_utils import uuidutils import testtools from neutronclient.common import exceptions @@ -38,7 +38,7 @@ def setUp(self): self.addCleanup(self.mox.UnsetStubs) def test_get_id_from_id(self): - _id = str(uuid.uuid4()) + _id = uuidutils.generate_uuid() reses = {'networks': [{'id': _id, }, ], } resstr = self.client.serialize(reses) self.mox.StubOutWithMock(self.client.httpclient, "request") @@ -57,7 +57,7 @@ def test_get_id_from_id(self): self.assertEqual(_id, returned_id) def test_get_id_from_id_then_name_empty(self): - _id = str(uuid.uuid4()) + _id = uuidutils.generate_uuid() reses = {'networks': [{'id': _id, }, ], } resstr = self.client.serialize(reses) resstr1 = self.client.serialize({'networks': []}) @@ -86,7 +86,7 @@ def test_get_id_from_id_then_name_empty(self): def test_get_id_from_name(self): name = 'myname' - _id = str(uuid.uuid4()) + _id = uuidutils.generate_uuid() reses = {'networks': [{'id': _id, }, ], } resstr = self.client.serialize(reses) self.mox.StubOutWithMock(self.client.httpclient, "request") @@ -106,8 +106,8 @@ def test_get_id_from_name(self): def test_get_id_from_name_multiple(self): name = 'myname' - reses = {'networks': [{'id': str(uuid.uuid4())}, - {'id': str(uuid.uuid4())}]} + reses = {'networks': [{'id': uuidutils.generate_uuid()}, + {'id': uuidutils.generate_uuid()}]} resstr = self.client.serialize(reses) self.mox.StubOutWithMock(self.client.httpclient, "request") path = getattr(self.client, "networks_path") @@ -148,8 +148,8 @@ def test_get_id_from_name_notfound(self): def test_get_id_from_name_multiple_with_project(self): name = 'web_server' - project = str(uuid.uuid4()) - expect_id = str(uuid.uuid4()) + project = uuidutils.generate_uuid() + expect_id = uuidutils.generate_uuid() reses = {'security_groups': [{'id': expect_id, 'tenant_id': project}]} resstr = self.client.serialize(reses) @@ -172,7 +172,7 @@ def test_get_id_from_name_multiple_with_project(self): def test_get_id_from_name_multiple_with_project_not_found(self): name = 'web_server' - project = str(uuid.uuid4()) + project = uuidutils.generate_uuid() resstr_notfound = self.client.serialize({'security_groups': []}) self.mox.StubOutWithMock(self.client.httpclient, "request") path = getattr(self.client, "security_groups_path") @@ -192,7 +192,7 @@ def test_get_id_from_name_multiple_with_project_not_found(self): self.assertEqual(404, exc.status_code) def _test_get_resource_by_id(self, id_only=False): - _id = str(uuid.uuid4()) + _id = uuidutils.generate_uuid() net = {'id': _id, 'name': 'test'} reses = {'networks': [net], } resstr = self.client.serialize(reses) From 7345d6ae4e6d3ba3e29129aed887f07e3c6f08e4 Mon Sep 17 00:00:00 2001 From: Hangdong Zhang Date: Thu, 20 Jul 2017 19:29:57 +0800 Subject: [PATCH 603/845] Update the documentation link for doc migration Change-Id: Ia07630b2626781d89b1957a96706e22a9e28674f --- HACKING.rst | 2 +- README.rst | 2 +- doc/source/cli/index.rst | 2 +- doc/source/cli/neutron.rst | 4 ++-- doc/source/contributor/transition_to_osc.rst | 20 ++++++++++---------- doc/source/index.rst | 2 +- doc/source/reference/index.rst | 2 +- setup.cfg | 2 +- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/HACKING.rst b/HACKING.rst index b8b3ff96c..7230971af 100644 --- a/HACKING.rst +++ b/HACKING.rst @@ -2,7 +2,7 @@ Neutron Style Commandments ================================ - Step 1: Read the OpenStack Style Commandments - http://docs.openstack.org/developer/hacking/ + https://docs.openstack.org/hacking/latest/ - Step 2: Read on diff --git a/README.rst b/README.rst index 0433add84..6cccade14 100644 --- a/README.rst +++ b/README.rst @@ -32,7 +32,7 @@ provides a Python API (the ``neutronclient`` module) and a command-line tool * `Developer's Guide`_ .. _PyPi: https://pypi.python.org/pypi/python-neutronclient -.. _Online Documentation: http://docs.openstack.org/developer/python-neutronclient +.. _Online Documentation: https://docs.openstack.org/python-neutronclient/latest/ .. _Launchpad project: https://launchpad.net/python-neutronclient .. _Blueprints: https://blueprints.launchpad.net/python-neutronclient .. _Bugs: https://bugs.launchpad.net/python-neutronclient diff --git a/doc/source/cli/index.rst b/doc/source/cli/index.rst index f53a83acb..0a7d54d9f 100644 --- a/doc/source/cli/index.rst +++ b/doc/source/cli/index.rst @@ -26,7 +26,7 @@ Using CLI There are two CLIs which support the Networking API: `OpenStackClient (OSC) -`__ +`__ and :doc:`neutron CLI ` (deprecated). OpenStackClient diff --git a/doc/source/cli/neutron.rst b/doc/source/cli/neutron.rst index bc104ce94..7ca652771 100644 --- a/doc/source/cli/neutron.rst +++ b/doc/source/cli/neutron.rst @@ -76,7 +76,7 @@ Run **neutron help** to get a full list of all possible commands, and run Using with os-client-config ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -`os-client-config `_ +`os-client-config `_ provides more convenient way to manage a collection of client configurations and you can easily switch multiple OpenStack-based configurations. @@ -114,7 +114,7 @@ Then, you need to specify a configuration name defined in the above clouds.yaml. export OS_CLOUD=devstack For more detail information, see the -`os-client-config `_ +`os-client-config `_ documentation. Using with keystone token diff --git a/doc/source/contributor/transition_to_osc.rst b/doc/source/contributor/transition_to_osc.rst index 2f0e52476..098d72734 100644 --- a/doc/source/contributor/transition_to_osc.rst +++ b/doc/source/contributor/transition_to_osc.rst @@ -47,7 +47,7 @@ It will be available along side the networking support provided by the OpenStack Python SDK. Users of the neutron client's command extensions will need to transition to the -`OSC plugin system `_ +`OSC plugin system `_ before the ``neutron`` CLI is removed. Such users will maintain their OSC plugin commands within their own project and will be responsible for deprecating and removing their ``neutron`` CLI extension. @@ -59,7 +59,7 @@ Transition Steps patch set: https://review.openstack.org/#/c/138745/ 2. **Done:** OSC switches its networking support for the - `network `_ + `network `_ command object to use the OpenStack Python SDK instead of the neutron client's Python library. See the following patch set: https://review.openstack.org/#/c/253348/ @@ -73,11 +73,11 @@ Transition Steps See the following blueprint: https://blueprints.launchpad.net/python-openstackclient/+spec/network-command-sdk-support 5. **Done:** OSC switches its networking support for the - `ip floating `_, - `ip floating pool `_, - `ip fixed `_, - `security group `_, and - `security group rule `_ + `ip floating `_, + `ip floating pool `_, + `ip fixed `_, + `security group `_, and + `security group rule `_ command objects to use the OpenStack Python SDK instead of the nova client's Python library when neutron is enabled. When nova network is enabled, then the nova client's Python library will continue to @@ -112,8 +112,8 @@ Transition Steps equivalent to the ``neutron`` CLI and it contains sufficient functional and unit test coverage. - * `Neutron Stadium `_ - projects, Neutron documentation and `DevStack `_ + * `Neutron Stadium `_ + projects, Neutron documentation and `DevStack `_ use ``openstack`` CLI instead of ``neutron`` CLI. * Most users of the neutron client's command extensions have transitioned @@ -235,7 +235,7 @@ Developer References to determine if an ``openstack`` command exists. * See `OSC command spec list `_ to determine if an ``openstack`` command spec exists. -* See `OSC plugin command list `_ +* See `OSC plugin command list `_ to determine if an ``openstack`` plugin command exists. * See `OSC command structure `_ to determine the current ``openstack`` command objects, plugin objects and actions. diff --git a/doc/source/index.rst b/doc/source/index.rst index 9b7a0efc9..64ad45ab4 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -29,7 +29,7 @@ This is a client for OpenStack Networking API. It provides There are two CLIs which support the Networking API: :doc:`neutron CLI ` and -`OpenStack Client (OSC) `__. +`OpenStack Client (OSC) `__. OpenStack Client provides the basic network commands and python-neutronclient provides extensions (aka OSC plugins) for advanced networking services. diff --git a/doc/source/reference/index.rst b/doc/source/reference/index.rst index 8ab601cfd..ee4761053 100644 --- a/doc/source/reference/index.rst +++ b/doc/source/reference/index.rst @@ -29,7 +29,7 @@ Basic Usage First create a client instance using a keystoneauth Session. For more information on this keystoneauth API, see `Using Sessions`_. -.. _Using Sessions: http://docs.openstack.org/developer/keystoneauth/using-sessions.html +.. _Using Sessions: https://docs.openstack.org/keystoneauth/latest/using-sessions.html .. code-block:: python diff --git a/setup.cfg b/setup.cfg index a15e929c0..b61f2934c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -5,7 +5,7 @@ description-file = README.rst author = OpenStack Networking Project author-email = openstack-dev@lists.openstack.org -home-page = http://docs.openstack.org/developer/python-neutronclient +home-page = https://docs.openstack.org/python-neutronclient/latest/ classifier = Environment :: OpenStack Intended Audience :: Developers From 9055183d391bbfe1fe8c18487ed8f11d7de6e679 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Sun, 23 Jul 2017 13:52:20 +0000 Subject: [PATCH 604/845] Updated from global requirements Change-Id: I0126609c076ee715b84d357b007ffbef6e0ebfe8 --- requirements.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index ee49f0608..02235fc2e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,16 +2,16 @@ # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. pbr!=2.1.0,>=2.0.0 # Apache-2.0 -cliff>=2.6.0 # Apache-2.0 +cliff>=2.8.0 # Apache-2.0 debtcollector>=1.2.0 # Apache-2.0 iso8601>=0.1.11 # MIT netaddr!=0.7.16,>=0.7.13 # BSD -osc-lib>=1.5.1 # Apache-2.0 +osc-lib>=1.7.0 # Apache-2.0 oslo.i18n!=3.15.2,>=2.1.0 # Apache-2.0 oslo.serialization!=2.19.1,>=1.10.0 # Apache-2.0 oslo.utils>=3.20.0 # Apache-2.0 -os-client-config>=1.27.0 # Apache-2.0 -keystoneauth1>=2.21.0 # Apache-2.0 +os-client-config>=1.28.0 # Apache-2.0 +keystoneauth1>=3.0.1 # Apache-2.0 # keystoneclient is used only by neutronclient.osc.utils # TODO(amotoki): Drop this after osc.utils has no dependency on keystoneclient python-keystoneclient>=3.8.0 # Apache-2.0 From b14e842b4ff8cda21ae573fc4240e5e5200b9b27 Mon Sep 17 00:00:00 2001 From: Bernard Cafarelli Date: Tue, 25 Jul 2017 16:12:09 +0200 Subject: [PATCH 605/845] SFC plugin: fix flow classifier list display Current code displays "[port]:xx" for source/destination ports: $ openstack sfc flow classifier list -c Summary -f value protocol: UDP, [port]: 192.168.0.14/32[any:any], [port]: 192.168.0.16/32[any:any], neutron_source_port: 5c196983-c319-41d7-9af0-02301ed85513, neutron_destination_port: None, l7_parameters: {} Change-Id: Ia5c772d48b22d4007dad8645cbfd2ea81cbb80ad Closes-Bug: #1706379 --- neutronclient/osc/v2/sfc/sfc_flow_classifier.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/osc/v2/sfc/sfc_flow_classifier.py b/neutronclient/osc/v2/sfc/sfc_flow_classifier.py index 38a3cbb94..1d41d7378 100755 --- a/neutronclient/osc/v2/sfc/sfc_flow_classifier.py +++ b/neutronclient/osc/v2/sfc/sfc_flow_classifier.py @@ -194,7 +194,7 @@ def _get_protocol_port_details(self, data, val): if max_port is None: max_port = 'any' return '%s[port]: %s[%s:%s]' % ( - type, ip_prefix, min_port, max_port) + val, ip_prefix, min_port, max_port) def take_action(self, parsed_args): client = self.app.client_manager.neutronclient From f0164133fabcd4583c2d318b589cdbda6c5996de Mon Sep 17 00:00:00 2001 From: Yushiro FURUKAWA Date: Tue, 4 Apr 2017 23:38:30 +0900 Subject: [PATCH 606/845] [FWaaS] Migrate 'public' attribute to 'shared' This patch is the first in the series of patches which tracks the migration of 'public' attribute to 'shared' in FWaaS v2. Co-Authored-By: Reedip Closes-Bug: #1676922 Depends-On: I0be5fca27c9696714ba8b91de2098448c5a18265 Change-Id: Ibc3b90e28ae82ccc8ed044b12d83e97907f1dfb8 --- neutronclient/osc/v2/fwaas/firewallgroup.py | 47 ++++++++--- neutronclient/osc/v2/fwaas/firewallpolicy.py | 45 +++++++--- neutronclient/osc/v2/fwaas/firewallrule.py | 45 +++++++--- .../tests/unit/osc/v2/fwaas/common.py | 82 +++++++++++-------- .../tests/unit/osc/v2/fwaas/fakes.py | 6 +- .../unit/osc/v2/fwaas/test_firewallgroup.py | 25 +++--- .../unit/osc/v2/fwaas/test_firewallpolicy.py | 26 +++--- .../unit/osc/v2/fwaas/test_firewallrule.py | 14 ++-- .../notes/bug-1676922-81341b70bc6f055a.yaml | 19 +++++ 9 files changed, 202 insertions(+), 107 deletions(-) create mode 100644 releasenotes/notes/bug-1676922-81341b70bc6f055a.yaml diff --git a/neutronclient/osc/v2/fwaas/firewallgroup.py b/neutronclient/osc/v2/fwaas/firewallgroup.py index ce7aac3c0..3d25022d1 100644 --- a/neutronclient/osc/v2/fwaas/firewallgroup.py +++ b/neutronclient/osc/v2/fwaas/firewallgroup.py @@ -40,7 +40,7 @@ ('status', 'Status', osc_utils.LIST_LONG_ONLY), ('ports', 'Ports', osc_utils.LIST_LONG_ONLY), ('admin_state_up', 'State', osc_utils.LIST_LONG_ONLY), - ('public', 'Public', osc_utils.LIST_LONG_ONLY), + ('shared', 'Shared', osc_utils.LIST_LONG_ONLY), ('tenant_id', 'Project', osc_utils.LIST_LONG_ONLY), ) @@ -75,16 +75,30 @@ def _get_common_parser(parser): dest='no_egress_firewall_policy', action='store_true', help=_('Detach egress firewall policy from the firewall group')) - public_group = parser.add_mutually_exclusive_group() - public_group.add_argument( + shared_group = parser.add_mutually_exclusive_group() + shared_group.add_argument( '--public', action='store_true', help=_('Make the firewall group public, which allows it to be ' 'used in all projects (as opposed to the default, ' - 'which is to restrict its use to the current project)')) - public_group.add_argument( + 'which is to restrict its use to the current project). ' + 'This option is deprecated and would be removed in R release.')) + shared_group.add_argument( '--private', action='store_true', + help=_('Restrict use of the firewall group to the ' + 'current project. This option is deprecated ' + 'and would be removed in R release.')) + + shared_group.add_argument( + '--share', + action='store_true', + help=_('Share the firewall group to be used in all projects ' + '(by default, it is restricted to be used by the ' + 'current project).')) + shared_group.add_argument( + '--no-share', + action='store_true', help=_('Restrict use of the firewall group to the ' 'current project')) admin_group = parser.add_mutually_exclusive_group() @@ -132,10 +146,10 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): cmd_resource=const.CMD_FWP)['id'] elif parsed_args.no_egress_firewall_policy: attrs['egress_firewall_policy_id'] = None - if parsed_args.public: - attrs['public'] = True - if parsed_args.private: - attrs['public'] = False + if parsed_args.share or parsed_args.public: + attrs['shared'] = True + if parsed_args.no_share or parsed_args.private: + attrs['shared'] = False if parsed_args.enable: attrs['admin_state_up'] = True if parsed_args.disable: @@ -333,9 +347,18 @@ def get_parser(self, prog_name): action='store_true', dest='egress_firewall_policy', help=_('Egress firewall policy (name or ID) to delete')) - parser.add_argument( + shared_group = parser.add_mutually_exclusive_group() + shared_group.add_argument( '--public', action='store_true', + help=_('Make the firewall group public, which allows it to be ' + 'used in all projects (as opposed to the default, ' + 'which is to restrict its use to the current project). ' + 'This option is deprecated and would be removed in R' + ' release.')) + shared_group.add_argument( + '--share', + action='store_true', help=_('Restrict use of the firewall group to the ' 'current project')) parser.add_argument( @@ -351,8 +374,8 @@ def _get_attrs(self, client_manager, parsed_args): attrs['ingress_firewall_policy_id'] = None if parsed_args.egress_firewall_policy: attrs['egress_firewall_policy_id'] = None - if parsed_args.public: - attrs['public'] = False + if parsed_args.share or parsed_args.public: + attrs['shared'] = False if parsed_args.enable: attrs['admin_state_up'] = False if parsed_args.port: diff --git a/neutronclient/osc/v2/fwaas/firewallpolicy.py b/neutronclient/osc/v2/fwaas/firewallpolicy.py index 84f890653..cee84065c 100644 --- a/neutronclient/osc/v2/fwaas/firewallpolicy.py +++ b/neutronclient/osc/v2/fwaas/firewallpolicy.py @@ -37,7 +37,7 @@ ('firewall_rules', 'Firewall Rules', osc_utils.LIST_BOTH), ('description', 'Description', osc_utils.LIST_LONG_ONLY), ('audited', 'Audited', osc_utils.LIST_LONG_ONLY), - ('public', 'Public', osc_utils.LIST_LONG_ONLY), + ('shared', 'Shared', osc_utils.LIST_LONG_ONLY), ('tenant_id', 'Project', osc_utils.LIST_LONG_ONLY), ) @@ -79,10 +79,10 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): attrs['name'] = str(parsed_args.name) if parsed_args.description: attrs['description'] = str(parsed_args.description) - if parsed_args.public: - attrs['public'] = True - if parsed_args.private: - attrs['public'] = False + if parsed_args.share or parsed_args.public: + attrs['shared'] = True + if parsed_args.no_share or parsed_args.private: + attrs['shared'] = False return attrs @@ -99,16 +99,29 @@ def _get_common_parser(parser): '--no-audited', action='store_true', help=_('Disable auditing for the policy')) - public_group = parser.add_mutually_exclusive_group() - public_group.add_argument( + shared_group = parser.add_mutually_exclusive_group() + shared_group.add_argument( + '--share', + action='store_true', + help=_('Share the firewall policy to be used in all projects ' + '(by default, it is restricted to be used by the ' + 'current project).')) + shared_group.add_argument( '--public', action='store_true', help=_('Make the firewall policy public, which allows it to be ' - 'used in all projects (as opposed to the default, ' - 'which is to restrict its use to the current project)')) - public_group.add_argument( + 'used in all projects (as opposed to the default, which ' + 'is to restrict its use to the current project.) This ' + 'option is deprecated and would be removed in R release.')) + shared_group.add_argument( '--private', action='store_true', + help=_( + 'Restrict use of the firewall policy to the current project.' + 'This option is deprecated and would be removed in R release.')) + shared_group.add_argument( + '--no-share', + action='store_true', help=_('Restrict use of the firewall policy to the ' 'current project')) return parser @@ -385,10 +398,16 @@ def get_parser(self, prog_name): action='store_true', help=_('Disable auditing for the policy')) parser.add_argument( - '--public', + '--share', action='store_true', help=_('Restrict use of the firewall policy to the ' 'current project')) + parser.add_argument( + '--public', + action='store_true', + help=_('Restrict use of the firewall policy to the ' + 'current project. This option is deprecated ' + 'and would be removed in R release.')) return parser def _get_attrs(self, client_manager, parsed_args): @@ -408,8 +427,8 @@ def _get_attrs(self, client_manager, parsed_args): attrs[const.FWRS] = [] if parsed_args.audited: attrs['audited'] = False - if parsed_args.public: - attrs['public'] = False + if parsed_args.share or parsed_args.public: + attrs['shared'] = False return attrs def take_action(self, parsed_args): diff --git a/neutronclient/osc/v2/fwaas/firewallrule.py b/neutronclient/osc/v2/fwaas/firewallrule.py index f2eeb1945..aee0441ac 100644 --- a/neutronclient/osc/v2/fwaas/firewallrule.py +++ b/neutronclient/osc/v2/fwaas/firewallrule.py @@ -44,7 +44,7 @@ ('destination_ip_address', 'Destination IP Address', osc_utils.LIST_LONG_ONLY), ('destination_port', 'Destination Port', osc_utils.LIST_LONG_ONLY), - ('public', 'Public', osc_utils.LIST_LONG_ONLY), + ('shared', 'Shared', osc_utils.LIST_LONG_ONLY), ('tenant_id', 'Project', osc_utils.LIST_LONG_ONLY), ) @@ -111,16 +111,29 @@ def _get_common_parser(parser): '--no-destination-port', action='store_true', help=_('Detach destination port number or range')) - public_group = parser.add_mutually_exclusive_group() - public_group.add_argument( + shared_group = parser.add_mutually_exclusive_group() + shared_group.add_argument( '--public', action='store_true', - help=_('Make the firewall rule public, which allows it to be ' + help=_('Make the firewall policy public, which allows it to be ' 'used in all projects (as opposed to the default, ' - 'which is to restrict its use to the current project)')) - public_group.add_argument( + 'which is to restrict its use to the current project). ' + 'This option is deprecated and would be removed in R Release')) + shared_group.add_argument( '--private', action='store_true', + help=_( + 'Restrict use of the firewall rule to the current project.' + 'This option is deprecated and would be removed in R release.')) + shared_group.add_argument( + '--share', + action='store_true', + help=_('Share the firewall rule to be used in all projects ' + '(by default, it is restricted to be used by the ' + 'current project).')) + shared_group.add_argument( + '--no-share', + action='store_true', help=_('Restrict use of the firewall rule to the current project')) enable_group = parser.add_mutually_exclusive_group() enable_group.add_argument( @@ -175,10 +188,10 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): attrs['enabled'] = True if parsed_args.disable_rule: attrs['enabled'] = False - if parsed_args.public: - attrs['public'] = True - if parsed_args.private: - attrs['public'] = False + if parsed_args.share or parsed_args.public: + attrs['shared'] = True + if parsed_args.no_share or parsed_args.private: + attrs['shared'] = False return attrs @@ -364,9 +377,15 @@ def get_parser(self, prog_name): help=_('Destination port number or range' '(integer in [1, 65535] or range like 123:456)')) parser.add_argument( - '--public', + '--share', action='store_true', help=_('Restrict use of the firewall rule to the current project')) + parser.add_argument( + '--public', + action='store_true', + help=_('Restrict use of the firewall rule to the current project. ' + 'This option is deprecated and would be removed in ' + 'R Release.')) parser.add_argument( '--enable-rule', action='store_true', @@ -383,8 +402,8 @@ def _get_attrs(self, client_manager, parsed_args): attrs['destination_ip_address'] = None if parsed_args.destination_port: attrs['destination_port'] = None - if parsed_args.public: - attrs['public'] = False + if parsed_args.share or parsed_args.public: + attrs['shared'] = False if parsed_args.enable_rule: attrs['enabled'] = False return attrs diff --git a/neutronclient/tests/unit/osc/v2/fwaas/common.py b/neutronclient/tests/unit/osc/v2/fwaas/common.py index cfd4df9c9..8acd51c42 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/common.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/common.py @@ -106,6 +106,20 @@ def test_set_description(self): target, {self.res: {'description': update}}) self.assertIsNone(result) + def test_set_shared(self): + target = self.resource['id'] + arglist = [target, '--share'] + verifylist = [ + (self.res, target), + ('share', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'shared': True}}) + self.assertIsNone(result) + def test_set_public(self): target = self.resource['id'] arglist = [target, '--public'] @@ -117,70 +131,70 @@ def test_set_public(self): result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'public': True}}) + target, {self.res: {'shared': True}}) self.assertIsNone(result) - def test_set_duplicate_public(self): + def test_set_duplicate_shared(self): target = self.resource['id'] - arglist = [target, '--public', '--public'] + arglist = [target, '--share', '--share'] verifylist = [ (self.res, target), - ('public', True), + ('share', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'public': True}}) + target, {self.res: {'shared': True}}) self.assertIsNone(result) - def test_set_private(self): + def test_set_no_share(self): target = self.resource['id'] - arglist = [target, '--private'] + arglist = [target, '--no-share'] verifylist = [ (self.res, target), - ('public', False), + ('share', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'public': False}}) + target, {self.res: {'shared': False}}) self.assertIsNone(result) - def test_set_duplicate_private(self): + def test_set_duplicate_no_share(self): target = self.resource['id'] - arglist = [target, '--private', '--private'] + arglist = [target, '--no-share', '--no-share'] verifylist = [ (self.res, target), - ('public', False), + ('no_share', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'public': False}}) + target, {self.res: {'shared': False}}) self.assertIsNone(result) - def test_set_private_and_public(self): + def test_set_no_share_and_shared(self): target = self.resource['id'] - arglist = [target, '--private', '--public'] + arglist = [target, '--no-share', '--share'] verifylist = [ (self.res, target), - ('private', True), - ('public', True), + ('no_share', True), + ('share', True), ] self.assertRaises( utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) - def test_set_public_and_priavte(self): + def test_set_shared_and_no_share(self): target = self.resource['id'] - arglist = [target, '--public', '--private'] + arglist = [target, '--share', '--no_share'] verifylist = [ (self.res, target), - ('public', True), - ('private', True), + ('share', True), + ('no_share', True), ] self.assertRaises( utils.ParserException, @@ -265,45 +279,45 @@ def test_delete_multiple_with_exception(self): class TestUnsetFWaaS(test_fakes.TestNeutronClientOSCV2): - def test_unset_public(self): + def test_unset_shared(self): target = self.resource['id'] arglist = [ target, - '--public', + '--share', ] verifylist = [ (self.res, target), - ('public', True), + ('share', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'public': False}}) + target, {self.res: {'shared': False}}) self.assertIsNone(result) - def test_set_public_and_priavte(self): + def test_set_shared_and_no_shared(self): target = self.resource['id'] - arglist = [target, '--public', '--private'] + arglist = [target, '--share', '--no-share'] verifylist = [ (self.res, target), - ('public', True), - ('private', True), + ('share', True), + ('no_share', True), ] - # check_parser: error: unrecognized arguments: --private + # check_parser: error: unrecognized arguments: --no-share self.assertRaises( utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) - def test_set_duplicate_public(self): + def test_set_duplicate_shared(self): target = self.resource['id'] - arglist = [target, '--public', '--public'] + arglist = [target, '--share', '--share'] verifylist = [ (self.res, target), - ('public', True), + ('share', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'public': False}}) + target, {self.res: {'shared': False}}) self.assertIsNone(result) diff --git a/neutronclient/tests/unit/osc/v2/fwaas/fakes.py b/neutronclient/tests/unit/osc/v2/fwaas/fakes.py index b2883edf6..61bc11244 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/fakes.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/fakes.py @@ -78,7 +78,7 @@ def __init__(self): ('status', 'INACTIVE'), ('ports', []), ('admin_state_up', True), - ('public', False), + ('shared', False), ('tenant_id', 'tenant-id-' + uuidutils.generate_uuid(dashed=False)), )) @@ -98,7 +98,7 @@ def __init__(self): ('description', 'my-desc-' + uuidutils.generate_uuid(dashed=False)), ('audited', True), - ('public', False), + ('shared', False), ('tenant_id', 'tenant-id-' + uuidutils.generate_uuid(dashed=False)), )) @@ -124,7 +124,7 @@ def __init__(self): ('source_port', '1:11111'), ('destination_ip_address', '192.168.2.2'), ('destination_port', '2:22222'), - ('public', False), + ('shared', False), ('tenant_id', 'tenant-id-' + uuidutils.generate_uuid(dashed=False)), )) diff --git a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py index 87fb4c2fd..cc54d406b 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py @@ -36,8 +36,8 @@ 'egress_firewall_policy': 'egress_firewall_policy_id', 'no_ingress_firewall_policy': 'ingress_firewall_policy_id', 'no_egress_firewall_policy': 'egress_firewall_policy_id', - 'public': 'public', - 'private': 'public', + 'share': 'shared', + 'no_share': 'shared', 'project': 'tenant_id', 'enable': 'admin_state_up', 'disable': 'admin_state_up', @@ -115,7 +115,7 @@ def _find_resource(*args, **kwargs): 'Status', 'Ports', 'State', - 'Public', + 'Shared', 'Project', )) self.data = _generate_response() @@ -128,7 +128,7 @@ def _find_resource(*args, **kwargs): _fwg['name'], _fwg['ports'], _fwg['tenant_id'], - _fwg['public'], + _fwg['shared'], v2_utils.AdminStateColumn(_fwg['admin_state_up']), _fwg['status'], ) @@ -140,7 +140,7 @@ def _find_resource(*args, **kwargs): 'name', 'ports', 'tenant_id', - 'public', + 'shared', 'admin_state_up', 'status', ) @@ -180,7 +180,6 @@ def test_create_with_no_option(self): verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) headers, data = self.cmd.take_action(parsed_args) - self.assertEqual(self.ordered_headers, headers) self.assertItemEqual(self.ordered_data, data) @@ -250,7 +249,7 @@ def test_create_with_all_params(self): '--egress-firewall-policy', egress_policy, '--port', port, '--project', tenant_id, - '--public', + '--share', '--disable', ] verifylist = [ @@ -259,7 +258,7 @@ def test_create_with_all_params(self): ('ingress_firewall_policy', ingress_policy), ('egress_firewall_policy', egress_policy), ('port', [port]), - ('public', True), + ('share', True), ('project', tenant_id), ('disable', True), ] @@ -270,14 +269,14 @@ def test_create_with_all_params(self): self.check_results(headers, data, request) - def test_create_with_public_and_private(self): + def test_create_with_shared_and_no_share(self): arglist = [ - '--public', - '--private', + '--share', + '--no-share', ] verifylist = [ - ('public', True), - ('private', True), + ('share', True), + ('no_share', True), ] self.assertRaises( utils.ParserException, diff --git a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py index c2e9b1bb9..316eec887 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py @@ -31,8 +31,8 @@ _fwp = fakes.FirewallPolicy().create() CONVERT_MAP = { - 'public': 'public', - 'private': 'public', + 'share': 'shared', + 'no_share': 'shared', 'project': 'tenant_id', 'port': 'ports', 'name': 'name', @@ -109,7 +109,7 @@ def _find_resource(*args, **kwargs): self.headers = tuple(self.list_headers + ( 'Description', 'Audited', - 'Public', + 'Shared', 'Project') ) self.data = _generate_data() @@ -120,7 +120,7 @@ def _find_resource(*args, **kwargs): 'ID', 'Name', 'Project', - 'Public', + 'Shared', ) self.ordered_data = ( _fwp['audited'], @@ -129,7 +129,7 @@ def _find_resource(*args, **kwargs): _fwp['id'], _fwp['name'], _fwp['tenant_id'], - _fwp['public'], + _fwp['shared'], ) self.ordered_columns = ( 'audited', @@ -138,7 +138,7 @@ def _find_resource(*args, **kwargs): 'id', 'name', 'tenant_id', - 'public', + 'shared', ) @@ -234,7 +234,7 @@ def test_create_with_all_params(self): '--firewall-rule', rule1, '--firewall-rule', rule2, '--project', project, - '--public', + '--share', '--audited', ] verifylist = [ @@ -242,7 +242,7 @@ def test_create_with_all_params(self): ('description', desc), ('firewall_rule', [rule1, rule2]), ('project', project), - ('public', True), + ('share', True), ('audited', True), ] request, response = _generate_req_and_res(verifylist) @@ -271,17 +271,17 @@ def test_create_with_firewall_rule_and_no(self): utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) - def test_create_with_public_and_private(self): + def test_create_with_shared_and_no_share(self): name = 'my-fwp' arglist = [ name, - '--public', - '--private', + '--share', + '--no-share', ] verifylist = [ ('name', name), - ('public', True), - ('private', True), + ('share', True), + ('no_share', True), ] self.assertRaises( utils.ParserException, diff --git a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py index 94e119940..e38306ed3 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py @@ -35,6 +35,8 @@ 'project': 'tenant_id', 'enable_rule': 'enabled', 'disable_rule': 'enabled', + 'share': 'shared', + 'no_share': 'shared', } @@ -115,7 +117,7 @@ def _mock_fwr(*args, **kwargs): 'Source Port', 'Destination IP Address', 'Destination Port', - 'Public', + 'Shared', 'Project', ) self.data = _generate_data() @@ -130,7 +132,7 @@ def _mock_fwr(*args, **kwargs): 'Name', 'Project', 'Protocol', - 'Public', + 'Shared', 'Source IP Address', 'Source Port', ) @@ -145,7 +147,7 @@ def _mock_fwr(*args, **kwargs): _fwr['name'], _fwr['tenant_id'], _replace_display_columns('protocol', _fwr['protocol']), - _fwr['public'], + _fwr['shared'], _fwr['source_ip_address'], _fwr['source_port'], ) @@ -160,7 +162,7 @@ def _mock_fwr(*args, **kwargs): 'name', 'tenant_id', 'protocol', - 'public', + 'shared', 'source_ip_address', 'source_port', ) @@ -217,12 +219,12 @@ def _set_all_params(self, args={}): '--action', action, '--project', tenant_id, '--disable-rule', - '--public', + '--share', ] verifylist = [ ('name', name), ('description', description), - ('public', True), + ('share', True), ('protocol', protocol), ('ip_version', ip_version), ('source_ip_address', source_ip), diff --git a/releasenotes/notes/bug-1676922-81341b70bc6f055a.yaml b/releasenotes/notes/bug-1676922-81341b70bc6f055a.yaml new file mode 100644 index 000000000..9dc6aef41 --- /dev/null +++ b/releasenotes/notes/bug-1676922-81341b70bc6f055a.yaml @@ -0,0 +1,19 @@ +--- +deprecations: + - | + The ``--public`` and ``--private`` attribute of Firewall-as-a-Service v2 + have been deprecated. While the ``--public`` attribute will now be replaced + by ``--share``, the ``--private`` attribute will be replaced by + ``--no-share``. This is because of the similarity between the behavior of + ``--public`` attribute in FireWall-as-a-Service and the ``--share`` + attribute used in OpenStack. This deprecation affects the following CLIs. + + * openstack firewall group create + * openstack firewall group set + * openstack firewall group unset + * openstack firewall policy create + * openstack firewall policy set + * openstack firewall policy unset + * openstack firewall rule create + * openstack firewall rule set + * openstack firewall rule unset From bbdea85ba4681ad1a7fdee4ec4f544e75f0d1202 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Thu, 27 Jul 2017 10:32:23 +0000 Subject: [PATCH 607/845] Populate shell.COMMANDS dict not to break CLI extension UT shell.COMMANDS is not intended to be public. However, 8 projects using neutron CLI extenions access this variable in their UT. I am honestly surprised on this, but it is the real. Neutron CLI is now deprecated, so it is better not to break them. This commit populates shell.COMMANDS dict based on registered commands. The following is my testting results with 'neutron net-list'. I ran this 10 times and the average user time is as follows: before 1.096 sec after 1.112 sec More dynamic loading can be explored but I think this overhead can be acceptable. Closes-Bug: #1706573 Change-Id: Idcbc64e6f709a666829e0c2985a03aa4517ccf77 --- neutronclient/shell.py | 10 ++++++++++ neutronclient/tests/unit/test_shell.py | 16 ++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 51b06d3d1..4045b8b89 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -103,6 +103,14 @@ def check_non_negative_int(value): return value +# NOTE(amotoki): This is only to provide compatibility +# to existing neutron CLI extensions. See bug 1706573 for detail. +def _set_commands_dict_for_compat(apiversion, command_manager): + global COMMANDS + COMMANDS = {apiversion: dict((cmd, command_manager.find_command([cmd])[0]) + for cmd in command_manager.commands)} + + class BashCompletionCommand(command.Command): """Prints all of the commands and options for bash-completion.""" @@ -166,6 +174,8 @@ def __init__(self, apiversion): self.auth_client = None self.api_version = apiversion + _set_commands_dict_for_compat(apiversion, self.command_manager) + def build_option_parser(self, description, version): """Return an argparse option parser for this application. diff --git a/neutronclient/tests/unit/test_shell.py b/neutronclient/tests/unit/test_shell.py index 2ae8bdc6f..2a6c4ffab 100644 --- a/neutronclient/tests/unit/test_shell.py +++ b/neutronclient/tests/unit/test_shell.py @@ -27,6 +27,7 @@ from testtools import matchers from neutronclient.common import clientmanager +from neutronclient.neutron.v2_0 import network from neutronclient import shell as openstack_shell @@ -348,3 +349,18 @@ def test_authenticate_insecure_with_cacert_without_cert_with_token(self): os_token='token', insecure=True, cacert='cacert', expect_verify=False, expect_insecure=True) + + def test_commands_dict_populated(self): + # neutron.shell.COMMANDS is populated once NeutronShell is initialized. + # To check COMMANDS during NeutronShell initialization, + # reset COMMANDS to some dummy value before calling NeutronShell(). + self.useFixture(fixtures.MockPatchObject(openstack_shell, + 'COMMANDS', None)) + openstack_shell.NeutronShell('2.0') + self.assertDictContainsSubset( + {'net-create': network.CreateNetwork, + 'net-delete': network.DeleteNetwork, + 'net-list': network.ListNetwork, + 'net-show': network.ShowNetwork, + 'net-update': network.UpdateNetwork}, + openstack_shell.COMMANDS['2.0']) From 6234bc85b2560dca5346b20deb9168029119c9fe Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Fri, 28 Jul 2017 21:47:52 +0000 Subject: [PATCH 608/845] Update reno for stable/pike Change-Id: I39d32b8aea40ec62fd7a4544dc8c99d17b2d4190 --- releasenotes/source/index.rst | 1 + releasenotes/source/pike.rst | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 releasenotes/source/pike.rst diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index d62abd9dd..ec6d8bde7 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -6,6 +6,7 @@ :maxdepth: 1 unreleased + pike ocata newton mitaka diff --git a/releasenotes/source/pike.rst b/releasenotes/source/pike.rst new file mode 100644 index 000000000..e43bfc0ce --- /dev/null +++ b/releasenotes/source/pike.rst @@ -0,0 +1,6 @@ +=================================== + Pike Series Release Notes +=================================== + +.. release-notes:: + :branch: stable/pike From f9bac295420cbaacdb081786f96647840f8f3742 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Sat, 29 Jul 2017 02:41:41 +0000 Subject: [PATCH 609/845] Updated from global requirements Change-Id: Iaec435d5f56b48469fff6d53247e9bbf7feb21c3 --- requirements.txt | 2 +- test-requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 02235fc2e..4d024497e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ oslo.i18n!=3.15.2,>=2.1.0 # Apache-2.0 oslo.serialization!=2.19.1,>=1.10.0 # Apache-2.0 oslo.utils>=3.20.0 # Apache-2.0 os-client-config>=1.28.0 # Apache-2.0 -keystoneauth1>=3.0.1 # Apache-2.0 +keystoneauth1>=3.1.0 # Apache-2.0 # keystoneclient is used only by neutronclient.osc.utils # TODO(amotoki): Drop this after osc.utils has no dependency on keystoneclient python-keystoneclient>=3.8.0 # Apache-2.0 diff --git a/test-requirements.txt b/test-requirements.txt index dfd3c4b70..0f0a401f3 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -8,7 +8,7 @@ fixtures>=3.0.0 # Apache-2.0/BSD flake8-import-order==0.12 # LGPLv3 mox3!=0.19.0,>=0.7.0 # Apache-2.0 mock>=2.0 # BSD -openstackdocstheme>=1.11.0 # Apache-2.0 +openstackdocstheme>=1.16.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 osprofiler>=1.4.0 # Apache-2.0 python-openstackclient!=3.10.0,>=3.3.0 # Apache-2.0 From ff86783fd4345b1f4cd9ebc3cb90eadca3f2a261 Mon Sep 17 00:00:00 2001 From: Igor Duarte Cardoso Date: Thu, 27 Jul 2017 12:46:22 +0000 Subject: [PATCH 610/845] Add missing correlation type "mpls" to port pair Add missing correlation type "mpls" to Port Pair create command's help (PortPairCreate). The merged Port Pair command was still using the older help description, before MPLS correlation support was merged to networking-sfc. Also add unit test coverage for correlation type "mpls". Closes-Bug: #1708968 Change-Id: I708345b25b600ca94504725f1559988ae29413b8 --- neutronclient/osc/v2/sfc/sfc_port_pair.py | 4 ++-- .../tests/unit/osc/v2/sfc/test_port_pair.py | 16 ++++++++++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/neutronclient/osc/v2/sfc/sfc_port_pair.py b/neutronclient/osc/v2/sfc/sfc_port_pair.py index 26f369c77..57cc47834 100755 --- a/neutronclient/osc/v2/sfc/sfc_port_pair.py +++ b/neutronclient/osc/v2/sfc/sfc_port_pair.py @@ -58,8 +58,8 @@ def get_parser(self, prog_name): action=parseractions.MultiKeyValueAction, optional_keys=['correlation', 'weight'], help=_('Dictionary of service function parameters. ' - 'Currently, only correlation=None and weight ' - 'is supported. Weight is an integer that influences ' + 'Currently, correlation=(None|mpls) and weight ' + 'are supported. Weight is an integer that influences ' 'the selection of a port pair within a port pair group ' 'for a flow. The higher the weight, the more flows will ' 'hash to the port pair. The default weight is 1.')) diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_port_pair.py b/neutronclient/tests/unit/osc/v2/sfc/test_port_pair.py index e6c539ae6..e15e6d01c 100755 --- a/neutronclient/tests/unit/osc/v2/sfc/test_port_pair.py +++ b/neutronclient/tests/unit/osc/v2/sfc/test_port_pair.py @@ -81,16 +81,17 @@ def test_create_port_pair_default_options(self): self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) - def test_create_port_pair_all_options(self): + def _test_create_port_pair_all_options(self, correlation): arglist = [ "--description", self._port_pair['description'], "--egress", self._port_pair['egress'], "--ingress", self._port_pair['ingress'], self._port_pair['name'], - "--service-function-parameters", 'correlation=None,weight=1', + "--service-function-parameters", + 'correlation=%s,weight=1' % correlation, ] - sfp = [{'correlation': 'None', 'weight': '1'}] + sfp = [{'correlation': correlation, 'weight': '1'}] verifylist = [ ('ingress', self._port_pair['ingress']), @@ -109,12 +110,19 @@ def test_create_port_pair_all_options(self): 'egress': self._port_pair['egress'], 'description': self._port_pair['description'], 'service_function_parameters': - [{'correlation': 'None', 'weight': '1'}], + [{'correlation': correlation, 'weight': + '1'}], } }) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) + def test_create_port_pair_all_options(self): + self._test_create_port_pair_all_options('None') + + def test_create_port_pair_all_options_mpls(self): + self._test_create_port_pair_all_options('mpls') + class TestDeleteSfcPortPair(fakes.TestNeutronClientOSCV2): From 4c3fbe2639664c25d3c06522f9c85713e65bb893 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Wed, 9 Aug 2017 15:23:54 +0000 Subject: [PATCH 611/845] Define shell.COMMANDS explicitly to avoid random UT failure Mocking non-existing attribute causes UT faillure. As a result, neutronclient UT fails depending on executing order of tests. Change-Id: Id047527fba9e908938f7157781fb0e36e76011c7 Closes-Bug: #1709652 --- neutronclient/shell.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 4045b8b89..4814c8118 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -103,6 +103,9 @@ def check_non_negative_int(value): return value +COMMANDS = {} + + # NOTE(amotoki): This is only to provide compatibility # to existing neutron CLI extensions. See bug 1706573 for detail. def _set_commands_dict_for_compat(apiversion, command_manager): From a01ebe1e544e94f38f39b7a2a4e7eaa63e0f7d01 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Fri, 18 Aug 2017 11:41:54 +0000 Subject: [PATCH 612/845] Updated from global requirements Change-Id: Iea16b097e17dfe3244f6e99abfdf8d6a7a1e6179 --- test-requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test-requirements.txt b/test-requirements.txt index 0f0a401f3..e97d61b01 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -7,14 +7,14 @@ coverage!=4.4,>=4.0 # Apache-2.0 fixtures>=3.0.0 # Apache-2.0/BSD flake8-import-order==0.12 # LGPLv3 mox3!=0.19.0,>=0.7.0 # Apache-2.0 -mock>=2.0 # BSD +mock>=2.0.0 # BSD openstackdocstheme>=1.16.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 osprofiler>=1.4.0 # Apache-2.0 python-openstackclient!=3.10.0,>=3.3.0 # Apache-2.0 python-subunit>=0.0.18 # Apache-2.0/BSD -reno!=2.3.1,>=1.8.0 # Apache-2.0 -requests-mock>=1.1 # Apache-2.0 +reno>=2.5.0 # Apache-2.0 +requests-mock>=1.1.0 # Apache-2.0 sphinx>=1.6.2 # BSD testrepository>=0.0.18 # Apache-2.0/BSD testtools>=1.4.0 # MIT From 24f6f2df3b4c02e5a2012f18eafe4b05ba136eb9 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Fri, 1 Sep 2017 12:46:56 +0000 Subject: [PATCH 613/845] Updated from global requirements Change-Id: I031afd7bc7943d04c098934b30eaf51efa6986ae --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4d024497e..41578c1ce 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ oslo.i18n!=3.15.2,>=2.1.0 # Apache-2.0 oslo.serialization!=2.19.1,>=1.10.0 # Apache-2.0 oslo.utils>=3.20.0 # Apache-2.0 os-client-config>=1.28.0 # Apache-2.0 -keystoneauth1>=3.1.0 # Apache-2.0 +keystoneauth1>=3.2.0 # Apache-2.0 # keystoneclient is used only by neutronclient.osc.utils # TODO(amotoki): Drop this after osc.utils has no dependency on keystoneclient python-keystoneclient>=3.8.0 # Apache-2.0 From 10d55c3a0fc65b499a849e48f3013e257a478e39 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Thu, 21 Sep 2017 03:49:49 +0000 Subject: [PATCH 614/845] Updated from global requirements Change-Id: I036c81a2cc84e615d8dd7936ceee60812260abba --- requirements.txt | 10 +++++----- test-requirements.txt | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/requirements.txt b/requirements.txt index 41578c1ce..2d0b95483 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,17 +5,17 @@ pbr!=2.1.0,>=2.0.0 # Apache-2.0 cliff>=2.8.0 # Apache-2.0 debtcollector>=1.2.0 # Apache-2.0 iso8601>=0.1.11 # MIT -netaddr!=0.7.16,>=0.7.13 # BSD +netaddr>=0.7.18 # BSD osc-lib>=1.7.0 # Apache-2.0 -oslo.i18n!=3.15.2,>=2.1.0 # Apache-2.0 -oslo.serialization!=2.19.1,>=1.10.0 # Apache-2.0 -oslo.utils>=3.20.0 # Apache-2.0 +oslo.i18n>=3.15.3 # Apache-2.0 +oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0 +oslo.utils>=3.28.0 # Apache-2.0 os-client-config>=1.28.0 # Apache-2.0 keystoneauth1>=3.2.0 # Apache-2.0 # keystoneclient is used only by neutronclient.osc.utils # TODO(amotoki): Drop this after osc.utils has no dependency on keystoneclient python-keystoneclient>=3.8.0 # Apache-2.0 requests>=2.14.2 # Apache-2.0 -simplejson>=2.2.0 # MIT +simplejson>=3.5.1 # MIT six>=1.9.0 # MIT Babel!=2.4.0,>=2.3.4 # BSD diff --git a/test-requirements.txt b/test-requirements.txt index e97d61b01..97e155267 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -6,12 +6,12 @@ hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0 coverage!=4.4,>=4.0 # Apache-2.0 fixtures>=3.0.0 # Apache-2.0/BSD flake8-import-order==0.12 # LGPLv3 -mox3!=0.19.0,>=0.7.0 # Apache-2.0 +mox3>=0.20.0 # Apache-2.0 mock>=2.0.0 # BSD -openstackdocstheme>=1.16.0 # Apache-2.0 +openstackdocstheme>=1.17.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 osprofiler>=1.4.0 # Apache-2.0 -python-openstackclient!=3.10.0,>=3.3.0 # Apache-2.0 +python-openstackclient>=3.12.0 # Apache-2.0 python-subunit>=0.0.18 # Apache-2.0/BSD reno>=2.5.0 # Apache-2.0 requests-mock>=1.1.0 # Apache-2.0 From 2be24b43e990d0e7081ab38b8f937d11afa7d5a5 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Thu, 12 Oct 2017 08:09:29 +0000 Subject: [PATCH 615/845] Make func test work with keystone v3 only neutronclient func test is broken due to missing fully support of keystone v3 API. It seems keystone v2 API has been disbaled recently and this triggers the test failures. There are two causes. The one is because tempest.lib.cli.base supports only keystone v2 auth info. There is an on-going review in tempest, but until the next tempest release we need a workaround. The workaround is to pass keystone v3 related arguments in neutron() method. 'flags' argument allows us to pass extra auth info to the CLI. The other reason is because HTTPClient (the legacy python binding class) supports only keystone v2 API. keystone v3 API is provided via SessionClient. I believe it is not a good idea to keep the two types of python binding classes in future. It looks like a time to drop HTTPClient. As a start, this commit drops HTTPClient func tests. Note that SessionClient supports keystone v2 as well. Closes-Bug: #1721553 Related-Bug: #1719687 Change-Id: I6f843e1412400bb1dfb4fc2352fc5cc4c3b837a4 --- neutronclient/tests/functional/base.py | 14 ++++ .../tests/functional/core/test_clientlib.py | 71 +------------------ 2 files changed, 16 insertions(+), 69 deletions(-) diff --git a/neutronclient/tests/functional/base.py b/neutronclient/tests/functional/base.py index 8233b1fde..fa1018b3a 100644 --- a/neutronclient/tests/functional/base.py +++ b/neutronclient/tests/functional/base.py @@ -64,6 +64,13 @@ def _get_clients(self): return self._get_clients_from_os_cloud_config() def neutron(self, *args, **kwargs): + # Workaround until tempest.lib.cli.base provdes fully + # keystone v3 support. It assumes the default domain. + # TODO(amotoki): Once a new tempest with a fix for bug 1719687 + # is released, this should be claen up. + kwargs['flags'] = ' '.join([kwargs.get('flags', ''), + '--os-project-domain-id default', + '--os-user-domain-id default']) return self.clients.neutron(*args, **kwargs) @@ -71,6 +78,13 @@ def neutron_non_admin(self, *args, **kwargs): if not hasattr(self, '_non_admin_clients'): self._non_admin_clients = self._get_clients_from_os_cloud_config( cloud='devstack') + # Workaround until tempest.lib.cli.base provdes fully + # keystone v3 support. It assumes the default domain. + # TODO(amotoki): Once a new tempest with a fix for bug 1719687 + # is released, this should be claen up. + kwargs['flags'] = ' '.join([kwargs.get('flags', ''), + '--os-project-domain-id default', + '--os-user-domain-id default']) return self._non_admin_clients.neutron(*args, **kwargs) def is_extension_enabled(self, extension_alias): diff --git a/neutronclient/tests/functional/core/test_clientlib.py b/neutronclient/tests/functional/core/test_clientlib.py index d4fdbae59..66be8c68b 100644 --- a/neutronclient/tests/functional/core/test_clientlib.py +++ b/neutronclient/tests/functional/core/test_clientlib.py @@ -10,8 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. - -from keystoneauth1 import plugin as ksa_plugin from keystoneauth1 import session from oslo_utils import uuidutils from tempest.lib import base @@ -21,61 +19,13 @@ from neutronclient.tests.functional import base as func_base from neutronclient.v2_0 import client as v2_client -# This module tests client library functionalities with -# Keystone client. Neutron client supports two types of -# HTTP clients (HTTPClient and SessionClient), -# so it is better to test both clients. - -class LibraryTestBase(base.BaseTestCase): +class LibraryTestCase(base.BaseTestCase): def setUp(self): - super(LibraryTestBase, self).setUp() + super(LibraryTestCase, self).setUp() self.client = self._get_client() - -class Libv2HTTPClientTestBase(LibraryTestBase): - - def _setup_creds(self): - creds = func_base.credentials() - cloud_config = func_base.get_cloud_config() - - # We're getting a session so we can find the v2 url via KSA - keystone_auth = cloud_config.get_auth() - (verify, cert) = cloud_config.get_requests_verify_args() - - ks_session = session.Session( - auth=keystone_auth, verify=verify, cert=cert) - - # for the old HTTPClient, we use keystone v2 API, regardless of - # whether v3 also exists or is configured - v2_auth_url = keystone_auth.get_endpoint( - ks_session, interface=ksa_plugin.AUTH_INTERFACE, version=(2, 0)) - return v2_auth_url, creds - - -class Libv2HTTPClientTenantTestBase(Libv2HTTPClientTestBase): - - def _get_client(self): - v2_auth_url, creds = self._setup_creds() - return v2_client.Client(username=creds['username'], - password=creds['password'], - tenant_name=creds['project_name'], - auth_url=v2_auth_url) - - -class Libv2HTTPClientProjectTestBase(Libv2HTTPClientTestBase): - - def _get_client(self): - v2_auth_url, creds = self._setup_creds() - return v2_client.Client(username=creds['username'], - password=creds['password'], - project_name=creds['project_name'], - auth_url=v2_auth_url) - - -class Libv2SessionClientTestBase(LibraryTestBase): - def _get_client(self): cloud_config = func_base.get_cloud_config() keystone_auth = cloud_config.get_auth() @@ -87,9 +37,6 @@ def _get_client(self): cert=cert) return v2_client.Client(session=ks_session) - -class LibraryTestCase(object): - def test_list_network(self): nets = self.client.list_networks() self.assertIsInstance(nets['networks'], list) @@ -112,17 +59,3 @@ def test_get_auth_ref(self): auth_ref = self.client.httpclient.get_auth_ref() self.assertIsNotNone(auth_ref) self.assertIsNotNone(auth_ref.role_names) - - -class LibraryHTTPClientTenantTest(LibraryTestCase, - Libv2HTTPClientTenantTestBase): - pass - - -class LibraryHTTPClientProjectTest(LibraryTestCase, - Libv2HTTPClientProjectTestBase): - pass - - -class LibrarySessionClientTest(LibraryTestCase, Libv2SessionClientTestBase): - pass From 92e65ec27da35e3d4b47e9ff7592974aee98348c Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Thu, 12 Oct 2017 22:04:01 +0000 Subject: [PATCH 616/845] Updated from global requirements Change-Id: I57041f72eadc70d2befcd7ce92b3a3b394da4cc6 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 2d0b95483..97a5b67a0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. pbr!=2.1.0,>=2.0.0 # Apache-2.0 -cliff>=2.8.0 # Apache-2.0 +cliff!=2.9.0,>=2.8.0 # Apache-2.0 debtcollector>=1.2.0 # Apache-2.0 iso8601>=0.1.11 # MIT netaddr>=0.7.18 # BSD From 8352c391d3a0b5c3a04cab40827fb9a620ce3357 Mon Sep 17 00:00:00 2001 From: Bernard Cafarelli Date: Wed, 18 Oct 2017 13:27:09 +0200 Subject: [PATCH 617/845] Use generic $USER variable for functional tests Zuulv3 uses "zuul" user instead of "jenkins", $USER should be set to the correct value in all cases Change-Id: Iae78d694a97978144de542608212c77dfe69a390 --- neutronclient/tests/functional/hooks/post_test_hook.sh | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/neutronclient/tests/functional/hooks/post_test_hook.sh b/neutronclient/tests/functional/hooks/post_test_hook.sh index 5fb66a109..43cc9c4b7 100755 --- a/neutronclient/tests/functional/hooks/post_test_hook.sh +++ b/neutronclient/tests/functional/hooks/post_test_hook.sh @@ -31,8 +31,8 @@ function generate_test_logs { function generate_testr_results { # Give job user rights to access tox logs - sudo -H -u $owner chmod o+rw . - sudo -H -u $owner chmod o+rw -R .testrepository + sudo -H -u $USER chmod o+rw . + sudo -H -u $USER chmod o+rw -R .testrepository if [ -f ".testrepository/0" ] ; then .tox/$VENV/bin/subunit-1to2 < .testrepository/0 > ./testrepository.subunit $SCRIPTS_DIR/subunit2html ./testrepository.subunit testr_results.html @@ -48,9 +48,8 @@ function generate_testr_results { } export NEUTRONCLIENT_DIR="$BASE/new/python-neutronclient" -owner=jenkins -sudo chown -R $owner:stack $NEUTRONCLIENT_DIR +sudo chown -R $USER:stack $NEUTRONCLIENT_DIR # Go to the neutronclient dir cd $NEUTRONCLIENT_DIR @@ -60,7 +59,7 @@ VENV=${1:-"functional"} echo "Running neutronclient functional test suite" set +e # Preserve env for OS_ credentials -sudo -E -H -u $owner tox -e $VENV +sudo -E -H -u $USER tox -e $VENV EXIT_CODE=$? set -e From 0907ccc4df68c71f1af71976819bff741829e434 Mon Sep 17 00:00:00 2001 From: Bernard Cafarelli Date: Tue, 17 Oct 2017 12:14:59 +0200 Subject: [PATCH 618/845] SFC plugin: fix all list commands Rename all list_ functions to follow the expected naming format expected by find_resource() calls: "list_%s" % cmd_resource_plural Rename for consistency the other SFC commands to include the "_sfc_" prefix Change-Id: Ie01692d5cbe928b042c7e8052295a781ac205764 Related-Bug: #1716942 --- .../osc/v2/sfc/sfc_flow_classifier.py | 10 ++--- neutronclient/osc/v2/sfc/sfc_port_chain.py | 12 +++--- neutronclient/osc/v2/sfc/sfc_port_pair.py | 10 ++--- .../osc/v2/sfc/sfc_port_pair_group.py | 12 +++--- .../unit/osc/v2/sfc/test_flow_classifier.py | 28 +++++++------ .../tests/unit/osc/v2/sfc/test_port_chain.py | 34 ++++++++-------- .../tests/unit/osc/v2/sfc/test_port_pair.py | 26 ++++++------ .../unit/osc/v2/sfc/test_port_pair_group.py | 36 +++++++++-------- neutronclient/v2_0/client.py | 40 +++++++++---------- 9 files changed, 106 insertions(+), 102 deletions(-) diff --git a/neutronclient/osc/v2/sfc/sfc_flow_classifier.py b/neutronclient/osc/v2/sfc/sfc_flow_classifier.py index 1d41d7378..529cf8aa6 100755 --- a/neutronclient/osc/v2/sfc/sfc_flow_classifier.py +++ b/neutronclient/osc/v2/sfc/sfc_flow_classifier.py @@ -117,7 +117,7 @@ def take_action(self, parsed_args): client = self.app.client_manager.neutronclient attrs = _get_common_attrs(self.app.client_manager, parsed_args) body = {resource: attrs} - obj = client.create_flow_classifier(body)[resource] + obj = client.create_sfc_flow_classifier(body)[resource] columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -140,7 +140,7 @@ def take_action(self, parsed_args): client = self.app.client_manager.neutronclient fc_id = _get_id(client, parsed_args.flow_classifier, resource) try: - client.delete_flow_classifier(fc_id) + client.delete_sfc_flow_classifier(fc_id) except Exception as e: msg = (_("Failed to delete flow classifier with name " "or ID '%(fc)s': %(e)s") @@ -198,7 +198,7 @@ def _get_protocol_port_details(self, data, val): def take_action(self, parsed_args): client = self.app.client_manager.neutronclient - obj = client.list_flow_classifier() + obj = client.list_sfc_flow_classifiers() obj_extend = self.extend_list(obj, parsed_args) headers, columns = nc_osc_utils.get_column_definitions( _attr_map, long_listing=parsed_args.long) @@ -233,7 +233,7 @@ def take_action(self, parsed_args): is_create=False) body = {resource: attrs} try: - client.update_flow_classifier(fc_id, body) + client.update_sfc_flow_classifier(fc_id, body) except Exception as e: msg = (_("Failed to update flow classifier '%(fc)s': %(e)s") % {'fc': parsed_args.flow_classifier, 'e': e}) @@ -255,7 +255,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.neutronclient fc_id = _get_id(client, parsed_args.flow_classifier, resource) - obj = client.show_flow_classifier(fc_id)[resource] + obj = client.show_sfc_flow_classifier(fc_id)[resource] columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns) return display_columns, data diff --git a/neutronclient/osc/v2/sfc/sfc_port_chain.py b/neutronclient/osc/v2/sfc/sfc_port_chain.py index 582a6d92c..37b30208d 100755 --- a/neutronclient/osc/v2/sfc/sfc_port_chain.py +++ b/neutronclient/osc/v2/sfc/sfc_port_chain.py @@ -83,7 +83,7 @@ def take_action(self, parsed_args): client = self.app.client_manager.neutronclient attrs = _get_common_attrs(self.app.client_manager, parsed_args) body = {resource: attrs} - obj = client.create_port_chain(body)[resource] + obj = client.create_sfc_port_chain(body)[resource] columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -106,7 +106,7 @@ def take_action(self, parsed_args): client = self.app.client_manager.neutronclient pc_id = _get_id(client, parsed_args.port_chain, resource) try: - client.delete_port_chain(pc_id) + client.delete_sfc_port_chain(pc_id) except Exception as e: msg = (_("Failed to delete port chain with name " "or ID '%(pc)s': %(e)s") @@ -129,7 +129,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.neutronclient - data = client.list_port_chain() + data = client.list_sfc_port_chains() headers, columns = nc_osc_utils.get_column_definitions( _attr_map, long_listing=parsed_args.long) return (headers, @@ -221,7 +221,7 @@ def take_action(self, parsed_args): attrs['port_pair_groups'] = sorted(list(set(existing_ppg))) body = {resource: attrs} try: - client.update_port_chain(pc_id, body) + client.update_sfc_port_chain(pc_id, body) except Exception as e: msg = (_("Failed to update port chain '%(pc)s': %(e)s") % {'pc': parsed_args.port_chain, 'e': e}) @@ -243,7 +243,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.neutronclient pc_id = _get_id(client, parsed_args.port_chain, resource) - obj = client.show_port_chain(pc_id)[resource] + obj = client.show_sfc_port_chain(pc_id)[resource] columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -310,7 +310,7 @@ def take_action(self, parsed_args): raise exceptions.CommandError(message) body = {resource: attrs} try: - client.update_port_chain(pc_id, body) + client.update_sfc_port_chain(pc_id, body) except Exception as e: msg = (_("Failed to unset port chain '%(pc)s': %(e)s") % {'pc': parsed_args.port_chain, 'e': e}) diff --git a/neutronclient/osc/v2/sfc/sfc_port_pair.py b/neutronclient/osc/v2/sfc/sfc_port_pair.py index 57cc47834..b8e64e4c4 100755 --- a/neutronclient/osc/v2/sfc/sfc_port_pair.py +++ b/neutronclient/osc/v2/sfc/sfc_port_pair.py @@ -79,7 +79,7 @@ def take_action(self, parsed_args): client = self.app.client_manager.neutronclient attrs = _get_common_attrs(self.app.client_manager, parsed_args) body = {resource: attrs} - obj = client.create_port_pair(body)[resource] + obj = client.create_sfc_port_pair(body)[resource] columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -102,7 +102,7 @@ def take_action(self, parsed_args): client = self.app.client_manager.neutronclient port_pair_id = _get_id(client, parsed_args.port_pair, resource) try: - client.delete_port_pair(port_pair_id) + client.delete_sfc_port_pair(port_pair_id) except Exception as e: msg = (_("Failed to delete port pair with name " "or ID '%(port_pair)s': %(e)s") @@ -124,7 +124,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.neutronclient - data = client.list_port_pair() + data = client.list_sfc_port_pairs() headers, columns = nc_osc_utils.get_column_definitions( _attr_map, long_listing=parsed_args.long) return (headers, @@ -160,7 +160,7 @@ def take_action(self, parsed_args): is_create=False) body = {resource: attrs} try: - client.update_port_pair(port_pair_id, body) + client.update_sfc_port_pair(port_pair_id, body) except Exception as e: msg = (_("Failed to update port pair '%(port_pair)s': %(e)s") % {'port_pair': parsed_args.port_pair, 'e': e}) @@ -182,7 +182,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.neutronclient port_pair_id = _get_id(client, parsed_args.port_pair, resource) - obj = client.show_port_pair(port_pair_id)[resource] + obj = client.show_sfc_port_pair(port_pair_id)[resource] columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns) return display_columns, data diff --git a/neutronclient/osc/v2/sfc/sfc_port_pair_group.py b/neutronclient/osc/v2/sfc/sfc_port_pair_group.py index 367dab128..688e5c3cd 100755 --- a/neutronclient/osc/v2/sfc/sfc_port_pair_group.py +++ b/neutronclient/osc/v2/sfc/sfc_port_pair_group.py @@ -74,7 +74,7 @@ def take_action(self, parsed_args): client = self.app.client_manager.neutronclient attrs = _get_common_attrs(self.app.client_manager, parsed_args) body = {resource: attrs} - obj = client.create_port_pair_group(body)[resource] + obj = client.create_sfc_port_pair_group(body)[resource] columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -97,7 +97,7 @@ def take_action(self, parsed_args): client = self.app.client_manager.neutronclient ppg_id = _get_id(client, parsed_args.port_pair_group, resource) try: - client.delete_port_pair_group(ppg_id) + client.delete_sfc_port_pair_group(ppg_id) except Exception as e: msg = (_("Failed to delete port pair group with name " "or ID '%(ppg)s': %(e)s") @@ -120,7 +120,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.neutronclient - data = client.list_port_pair_group() + data = client.list_sfc_port_pair_groups() headers, columns = nc_osc_utils.get_column_definitions( _attr_map, long_listing=parsed_args.long) return (headers, @@ -180,7 +180,7 @@ def take_action(self, parsed_args): attrs['port_pairs'] = sorted(list(set(existing) | set(added))) body = {resource: attrs} try: - client.update_port_pair_group(ppg_id, body) + client.update_sfc_port_pair_group(ppg_id, body) except Exception as e: msg = (_("Failed to update port pair group '%(ppg)s': %(e)s") % {'ppg': parsed_args.port_pair_group, 'e': e}) @@ -202,7 +202,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.neutronclient ppg_id = _get_id(client, parsed_args.port_pair_group, resource) - obj = client.show_port_pair_group(ppg_id)[resource] + obj = client.show_sfc_port_pair_group(ppg_id)[resource] columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -247,7 +247,7 @@ def take_action(self, parsed_args): attrs['port_pairs'] = [] body = {resource: attrs} try: - client.update_port_pair_group(ppg_id, body) + client.update_sfc_port_pair_group(ppg_id, body) except Exception as e: msg = (_("Failed to unset port pair group '%(ppg)s': %(e)s") % {'ppg': parsed_args.port_pair_group, 'e': e}) diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_flow_classifier.py b/neutronclient/tests/unit/osc/v2/sfc/test_flow_classifier.py index fff11b317..e4672d377 100755 --- a/neutronclient/tests/unit/osc/v2/sfc/test_flow_classifier.py +++ b/neutronclient/tests/unit/osc/v2/sfc/test_flow_classifier.py @@ -67,7 +67,7 @@ def get_data(self): def setUp(self): super(TestCreateSfcFlowClassifier, self).setUp() mock.patch(get_id, new=_get_id).start() - self.neutronclient.create_flow_classifier = mock.Mock( + self.neutronclient.create_sfc_flow_classifier = mock.Mock( return_value={'flow_classifier': self._fc}) self.data = self.get_data() @@ -90,7 +90,7 @@ def test_create_flow_classifier_default_options(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = (self.cmd.take_action(parsed_args)) - self.neutronclient.create_flow_classifier.assert_called_once_with({ + self.neutronclient.create_sfc_flow_classifier.assert_called_once_with({ 'flow_classifier': { 'name': self._fc['name'], 'logical_source_port': self._fc['logical_source_port'], @@ -129,7 +129,7 @@ def test_create_flow_classifier(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = (self.cmd.take_action(parsed_args)) - self.neutronclient.create_flow_classifier.assert_called_once_with({ + self.neutronclient.create_sfc_flow_classifier.assert_called_once_with({ 'flow_classifier': { 'name': self._fc['name'], 'description': self._fc['description'], @@ -155,14 +155,14 @@ class TestDeleteSfcFlowClassifier(fakes.TestNeutronClientOSCV2): def setUp(self): super(TestDeleteSfcFlowClassifier, self).setUp() mock.patch(get_id, new=_get_id).start() - self.neutronclient.delete_flow_classifier = mock.Mock( + self.neutronclient.delete_sfc_flow_classifier = mock.Mock( return_value=None) self.cmd = sfc_flow_classifier.DeleteSfcFlowClassifier(self.app, self.namespace) def test_delete_flow_classifier(self): client = self.app.client_manager.neutronclient - mock_flow_classifier_delete = client.delete_flow_classifier + mock_flow_classifier_delete = client.delete_sfc_flow_classifier arglist = [ self._flow_classifier[0]['id'], ] @@ -184,14 +184,14 @@ class TestSetSfcFlowClassifier(fakes.TestNeutronClientOSCV2): def setUp(self): super(TestSetSfcFlowClassifier, self).setUp() mock.patch(get_id, new=_get_id).start() - self.neutronclient.update_flow_classifier = mock.Mock( + self.neutronclient.update_sfc_flow_classifier = mock.Mock( return_value=None) self.cmd = sfc_flow_classifier.SetSfcFlowClassifier(self.app, self.namespace) def test_set_flow_classifier(self): client = self.app.client_manager.neutronclient - mock_flow_classifier_update = client.update_flow_classifier + mock_flow_classifier_update = client.update_sfc_flow_classifier arglist = [ self._flow_classifier_name, '--name', 'name_updated', @@ -255,7 +255,7 @@ class TestShowSfcFlowClassifier(fakes.TestNeutronClientOSCV2): def setUp(self): super(TestShowSfcFlowClassifier, self).setUp() mock.patch(get_id, new=_get_id).start() - self.neutronclient.show_flow_classifier = mock.Mock( + self.neutronclient.show_sfc_flow_classifier = mock.Mock( return_value=self._flow_classifier ) # Get the command object to test @@ -264,7 +264,7 @@ def setUp(self): def test_show_flow_classifier(self): client = self.app.client_manager.neutronclient - mock_flow_classifier_show = client.show_flow_classifier + mock_flow_classifier_show = client.show_sfc_flow_classifier arglist = [ self._flow_classifier_id, ] @@ -325,19 +325,20 @@ class TestListSfcFlowClassifier(fakes.TestNeutronClientOSCV2): def setUp(self): super(TestListSfcFlowClassifier, self).setUp() mock.patch(get_id, new=_get_id).start() - self.neutronclient.list_flow_classifier = mock.Mock( + self.neutronclient.list_sfc_flow_classifiers = mock.Mock( return_value={'flow_classifiers': self._fc} ) # Get the command object to test self.cmd = sfc_flow_classifier.ListSfcFlowClassifier(self.app, self.namespace) - def test_list_flow_classifier(self): + def test_list_flow_classifiers(self): arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns = self.cmd.take_action(parsed_args) - fcs = self.neutronclient.list_flow_classifier()['flow_classifiers'] + fcs = self.neutronclient \ + .list_sfc_flow_classifiers()['flow_classifiers'] fc = fcs[0] data = [ fc['id'], @@ -354,7 +355,8 @@ def test_list_flow_classifier(self): def test_list_with_long_option(self): arglist = ['--long'] verifylist = [('long', True)] - fcs = self.neutronclient.list_flow_classifier()['flow_classifiers'] + fcs = self.neutronclient \ + .list_sfc_flow_classifiers()['flow_classifiers'] fc = fcs[0] data = [ fc['id'], diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py b/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py index 8cb0fe2c5..e94e8260c 100755 --- a/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py +++ b/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py @@ -55,7 +55,7 @@ def setUp(self): mock.patch( 'neutronclient.osc.v2.sfc.sfc_port_chain._get_id', new=_get_id).start() - self.neutronclient.create_port_chain = mock.Mock( + self.neutronclient.create_sfc_port_chain = mock.Mock( return_value={'port_chain': self._port_chain}) self.data = self.get_data() @@ -77,7 +77,7 @@ def test_create_port_chain_dafault_options(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = (self.cmd.take_action(parsed_args)) - self.neutronclient.create_port_chain.assert_called_once_with({ + self.neutronclient.create_sfc_port_chain.assert_called_once_with({ 'port_chain': { 'name': self._port_chain['name'], 'port_pair_groups': [self._port_chain['port_pair_groups']], @@ -109,7 +109,7 @@ def test_create_port_chain_all_options(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = (self.cmd.take_action(parsed_args)) - self.neutronclient.create_port_chain.assert_called_once_with({ + self.neutronclient.create_sfc_port_chain.assert_called_once_with({ 'port_chain': { 'name': self._port_chain['name'], 'port_pair_groups': [self._port_chain['port_pair_groups']], @@ -131,12 +131,12 @@ def setUp(self): mock.patch( 'neutronclient.osc.v2.sfc.sfc_port_chain._get_id', new=_get_id).start() - self.neutronclient.delete_port_chain = mock.Mock(return_value=None) + self.neutronclient.delete_sfc_port_chain = mock.Mock(return_value=None) self.cmd = sfc_port_chain.DeleteSfcPortChain(self.app, self.namespace) def test_delete_port_chain(self): client = self.app.client_manager.neutronclient - mock_port_chain_delete = client.delete_port_chain + mock_port_chain_delete = client.delete_sfc_port_chain arglist = [ self._port_chain[0]['id'], ] @@ -183,18 +183,18 @@ def setUp(self): mock.patch( 'neutronclient.osc.v2.sfc.sfc_port_chain._get_id', new=_get_id).start() - self.neutronclient.list_port_chain = mock.Mock( + self.neutronclient.list_sfc_port_chains = mock.Mock( return_value={'port_chains': self._port_chains} ) # Get the command object to test self.cmd = sfc_port_chain.ListSfcPortChain(self.app, self.namespace) - def test_list_port_chain(self): + def test_list_port_chains(self): arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns = self.cmd.take_action(parsed_args)[0] - pcs = self.neutronclient.list_port_chain()['port_chains'] + pcs = self.neutronclient.list_sfc_port_chains()['port_chains'] pc = pcs[0] data = [ pc['id'], @@ -212,7 +212,7 @@ def test_list_port_chain_with_long_opion(self): verifylist = [('long', True)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns = self.cmd.take_action(parsed_args)[0] - pcs = self.neutronclient.list_port_chain()['port_chains'] + pcs = self.neutronclient.list_sfc_port_chains()['port_chains'] pc = pcs[0] data = [ pc['id'], @@ -242,12 +242,12 @@ def setUp(self): mock.patch( 'neutronclient.osc.v2.sfc.sfc_port_chain._get_id', new=_get_id).start() - self.mocked = self.neutronclient.update_port_chain + self.mocked = self.neutronclient.update_sfc_port_chain self.cmd = sfc_port_chain.SetSfcPortChain(self.app, self.namespace) def test_set_port_chain(self): client = self.app.client_manager.neutronclient - mock_port_chain_update = client.update_port_chain + mock_port_chain_update = client.update_sfc_port_chain arglist = [ self._port_chain_name, '--name', 'name_updated', @@ -299,7 +299,7 @@ def _mock_flow_classifier(*args, **kwargs): def test_set_no_flow_classifier(self): client = self.app.client_manager.neutronclient - mock_port_chain_update = client.update_port_chain + mock_port_chain_update = client.update_sfc_port_chain arglist = [ self._port_chain_name, '--no-flow-classifier', @@ -429,7 +429,7 @@ def setUp(self): mock.patch( 'neutronclient.osc.v2.sfc.sfc_port_chain._get_id', new=_get_id).start() - self.neutronclient.show_port_chain = mock.Mock( + self.neutronclient.show_sfc_port_chain = mock.Mock( return_value=self._port_chain ) # Get the command object to test @@ -437,7 +437,7 @@ def setUp(self): def test_show_port_chain(self): client = self.app.client_manager.neutronclient - mock_port_chain_show = client.show_port_chain + mock_port_chain_show = client.show_sfc_port_chain arglist = [ self._port_chain_id, ] @@ -466,9 +466,9 @@ def setUp(self): mock.patch( 'neutronclient.osc.v2.sfc.sfc_port_chain._get_id', new=_get_id).start() - self.neutronclient.update_port_chain = mock.Mock( + self.neutronclient.update_sfc_port_chain = mock.Mock( return_value=None) - self.mocked = self.neutronclient.update_port_chain + self.mocked = self.neutronclient.update_sfc_port_chain self.cmd = sfc_port_chain.UnsetSfcPortChain(self.app, self.namespace) def test_unset_port_pair_group(self): @@ -539,7 +539,7 @@ def _mock_flow_classifier(*args, **kwargs): def test_unset_all_flow_classifier(self): client = self.app.client_manager.neutronclient target = self.resource['id'] - mock_port_chain_update = client.update_port_chain + mock_port_chain_update = client.update_sfc_port_chain arglist = [ target, '--all-flow-classifier', diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_port_pair.py b/neutronclient/tests/unit/osc/v2/sfc/test_port_pair.py index e15e6d01c..1111e8202 100755 --- a/neutronclient/tests/unit/osc/v2/sfc/test_port_pair.py +++ b/neutronclient/tests/unit/osc/v2/sfc/test_port_pair.py @@ -50,7 +50,7 @@ def setUp(self): super(TestCreateSfcPortPair, self).setUp() mock.patch('neutronclient.osc.v2.sfc.sfc_port_pair._get_id', new=_get_id).start() - self.neutronclient.create_port_pair = mock.Mock( + self.neutronclient.create_sfc_port_pair = mock.Mock( return_value={'port_pair': self._port_pair}) self.data = self.get_data() @@ -71,7 +71,7 @@ def test_create_port_pair_default_options(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = (self.cmd.take_action(parsed_args)) - self.neutronclient.create_port_pair.assert_called_once_with({ + self.neutronclient.create_sfc_port_pair.assert_called_once_with({ 'port_pair': {'name': self._port_pair['name'], 'ingress': self._port_pair['ingress'], 'egress': self._port_pair['egress'], @@ -104,7 +104,7 @@ def _test_create_port_pair_all_options(self, correlation): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = (self.cmd.take_action(parsed_args)) - self.neutronclient.create_port_pair.assert_called_once_with({ + self.neutronclient.create_sfc_port_pair.assert_called_once_with({ 'port_pair': {'name': self._port_pair['name'], 'ingress': self._port_pair['ingress'], 'egress': self._port_pair['egress'], @@ -132,12 +132,12 @@ def setUp(self): super(TestDeleteSfcPortPair, self).setUp() mock.patch('neutronclient.osc.v2.sfc.sfc_port_pair._get_id', new=_get_id).start() - self.neutronclient.delete_port_pair = mock.Mock(return_value=None) + self.neutronclient.delete_sfc_port_pair = mock.Mock(return_value=None) self.cmd = sfc_port_pair.DeleteSfcPortPair(self.app, self.namespace) def test_delete_port_pair(self): client = self.app.client_manager.neutronclient - mock_port_pair_delete = client.delete_port_pair + mock_port_pair_delete = client.delete_sfc_port_pair arglist = [ self._port_pair[0]['id'], ] @@ -179,18 +179,18 @@ def setUp(self): super(TestListSfcPortPair, self).setUp() mock.patch('neutronclient.osc.v2.sfc.sfc_port_pair._get_id', new=_get_id).start() - self.neutronclient.list_port_pair = mock.Mock( + self.neutronclient.list_sfc_port_pairs = mock.Mock( return_value={'port_pairs': self._port_pairs} ) # Get the command object to test self.cmd = sfc_port_pair.ListSfcPortPair(self.app, self.namespace) - def test_list_port_pair(self): + def test_list_port_pairs(self): arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns = self.cmd.take_action(parsed_args)[0] - port_pairs = self.neutronclient.list_port_pair()['port_pairs'] + port_pairs = self.neutronclient.list_sfc_port_pairs()['port_pairs'] port_pair = port_pairs[0] data = [ port_pair['id'], @@ -204,7 +204,7 @@ def test_list_port_pair(self): def test_list_with_long_option(self): arglist = ['--long'] verifylist = [('long', True)] - port_pairs = self.neutronclient.list_port_pair()['port_pairs'] + port_pairs = self.neutronclient.list_sfc_port_pairs()['port_pairs'] port_pair = port_pairs[0] data = [ port_pair['id'], @@ -229,12 +229,12 @@ def setUp(self): super(TestSetSfcPortPair, self).setUp() mock.patch('neutronclient.osc.v2.sfc.sfc_port_pair._get_id', new=_get_id).start() - self.neutronclient.update_port_pair = mock.Mock(return_value=None) + self.neutronclient.update_sfc_port_pair = mock.Mock(return_value=None) self.cmd = sfc_port_pair.SetSfcPortPair(self.app, self.namespace) def test_set_port_pair(self): client = self.app.client_manager.neutronclient - mock_port_pair_update = client.update_port_pair + mock_port_pair_update = client.update_sfc_port_pair arglist = [ self._port_pair_name, '--name', 'name_updated', @@ -286,7 +286,7 @@ def setUp(self): mock.patch('neutronclient.osc.v2.sfc.sfc_port_pair._get_id', new=_get_id).start() - self.neutronclient.show_port_pair = mock.Mock( + self.neutronclient.show_sfc_port_pair = mock.Mock( return_value=self._port_pair ) @@ -295,7 +295,7 @@ def setUp(self): def test_show_port_pair(self): client = self.app.client_manager.neutronclient - mock_port_pair_show = client.show_port_pair + mock_port_pair_show = client.show_sfc_port_pair arglist = [ self._port_pair_id, ] diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_port_pair_group.py b/neutronclient/tests/unit/osc/v2/sfc/test_port_pair_group.py index 188494c4b..894c1bee7 100755 --- a/neutronclient/tests/unit/osc/v2/sfc/test_port_pair_group.py +++ b/neutronclient/tests/unit/osc/v2/sfc/test_port_pair_group.py @@ -51,7 +51,7 @@ def setUp(self): mock.patch( 'neutronclient.osc.v2.sfc.sfc_port_pair_group._get_id', new=_get_id).start() - self.neutronclient.create_port_pair_group = mock.Mock( + self.neutronclient.create_sfc_port_pair_group = mock.Mock( return_value={'port_pair_group': self._port_pair_group}) self.data = self.get_data() # Get the command object to test @@ -70,7 +70,7 @@ def test_create_port_pair_group_default_options(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = (self.cmd.take_action(parsed_args)) - self.neutronclient.create_port_pair_group.assert_called_once_with({ + self.neutronclient.create_sfc_port_pair_group.assert_called_once_with({ 'port_pair_group': { 'name': self._port_pair_group['name'], 'port_pairs': [self._port_pair_group['port_pairs']]} @@ -92,7 +92,7 @@ def test_create_port_pair_group(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = (self.cmd.take_action(parsed_args)) - self.neutronclient.create_port_pair_group.assert_called_once_with({ + self.neutronclient.create_sfc_port_pair_group.assert_called_once_with({ 'port_pair_group': { 'name': self._port_pair_group['name'], 'port_pairs': [self._port_pair_group['port_pairs']], @@ -113,14 +113,14 @@ def setUp(self): mock.patch( 'neutronclient.osc.v2.sfc.sfc_port_pair_group._get_id', new=_get_id).start() - self.neutronclient.delete_port_pair_group = mock.Mock( + self.neutronclient.delete_sfc_port_pair_group = mock.Mock( return_value=None) self.cmd = sfc_port_pair_group.DeleteSfcPortPairGroup(self.app, self.namespace) def test_delete_port_pair_group(self): client = self.app.client_manager.neutronclient - mock_port_pair_group_delete = client.delete_port_pair_group + mock_port_pair_group_delete = client.delete_sfc_port_pair_group arglist = [ self._port_pair_group[0]['id'], ] @@ -162,19 +162,20 @@ def setUp(self): 'neutronclient.osc.v2.sfc.sfc_port_pair_group._get_id', new=_get_id).start() - self.neutronclient.list_port_pair_group = mock.Mock( + self.neutronclient.list_sfc_port_pair_groups = mock.Mock( return_value={'port_pair_groups': self._ppgs} ) # Get the command object to test self.cmd = sfc_port_pair_group.ListSfcPortPairGroup(self.app, self.namespace) - def test_list_port_pair_group(self): + def test_list_port_pair_groups(self): arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns = self.cmd.take_action(parsed_args)[0] - ppgs = self.neutronclient.list_port_pair_group()['port_pair_groups'] + ppgs = self.neutronclient \ + .list_sfc_port_pair_groups()['port_pair_groups'] ppg = ppgs[0] data = [ ppg['id'], @@ -188,7 +189,8 @@ def test_list_port_pair_group(self): def test_list_with_long_option(self): arglist = ['--long'] verifylist = [('long', True)] - ppgs = self.neutronclient.list_port_pair_group()['port_pair_groups'] + ppgs = self.neutronclient \ + .list_sfc_port_pair_groups()['port_pair_groups'] ppg = ppgs[0] data = [ ppg['id'], @@ -217,9 +219,9 @@ def setUp(self): mock.patch( 'neutronclient.osc.v2.sfc.sfc_port_pair_group._get_id', new=_get_id).start() - self.neutronclient.update_port_pair_group = mock.Mock( + self.neutronclient.update_sfc_port_pair_group = mock.Mock( return_value=None) - self.mocked = self.neutronclient.update_port_pair_group + self.mocked = self.neutronclient.update_sfc_port_pair_group self.cmd = sfc_port_pair_group.SetSfcPortPairGroup(self.app, self.namespace) @@ -265,7 +267,7 @@ def _mock_port_pair_group(*args, **kwargs): def test_set_no_port_pair(self): client = self.app.client_manager.neutronclient - mock_port_pair_group_update = client.update_port_pair_group + mock_port_pair_group_update = client.update_sfc_port_pair_group arglist = [ self._port_pair_group_name, '--name', 'name_updated', @@ -317,7 +319,7 @@ def setUp(self): 'neutronclient.osc.v2.sfc.sfc_port_pair_group._get_id', new=_get_id).start() - self.neutronclient.show_port_pair_group = mock.Mock( + self.neutronclient.show_sfc_port_pair_group = mock.Mock( return_value=self._port_pair_group ) self.cmd = sfc_port_pair_group.ShowSfcPortPairGroup(self.app, @@ -325,7 +327,7 @@ def setUp(self): def test_show_port_pair_group(self): client = self.app.client_manager.neutronclient - mock_port_pair_group_show = client.show_port_pair_group + mock_port_pair_group_show = client.show_sfc_port_pair_group arglist = [ self._port_pair_group_id, ] @@ -354,9 +356,9 @@ def setUp(self): mock.patch( 'neutronclient.osc.v2.sfc.sfc_port_pair_group._get_id', new=_get_id).start() - self.neutronclient.update_port_pair_group = mock.Mock( + self.neutronclient.update_sfc_port_pair_group = mock.Mock( return_value=None) - self.mocked = self.neutronclient.update_port_pair_group + self.mocked = self.neutronclient.update_sfc_port_pair_group self.cmd = sfc_port_pair_group.UnsetSfcPortPairGroup( self.app, self.namespace) @@ -406,7 +408,7 @@ def _mock_port_pair(*args, **kwargs): def test_unset_all_port_pair(self): client = self.app.client_manager.neutronclient - mock_port_pair_group_update = client.update_port_pair_group + mock_port_pair_group_update = client.update_sfc_port_pair_group arglist = [ self._port_pair_group_name, '--all-port-pair', diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index a42dc7d10..f4e49f895 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -2171,91 +2171,91 @@ def delete_bgpvpn_router_assoc(self, bgpvpn, router_assoc): return self.delete( self.bgpvpn_router_association_path % (bgpvpn, router_assoc)) - def create_port_pair(self, body=None): + def create_sfc_port_pair(self, body=None): """Creates a new Port Pair.""" return self.post(self.sfc_port_pairs_path, body=body) - def update_port_pair(self, port_pair, body=None): + def update_sfc_port_pair(self, port_pair, body=None): """Update a Port Pair.""" return self.put(self.sfc_port_pair_path % port_pair, body=body) - def delete_port_pair(self, port_pair): + def delete_sfc_port_pair(self, port_pair): """Deletes the specified Port Pair.""" return self.delete(self.sfc_port_pair_path % (port_pair)) - def list_port_pair(self, retrieve_all=True, **_params): + def list_sfc_port_pairs(self, retrieve_all=True, **_params): """Fetches a list of all Port Pairs.""" return self.list('port_pairs', self.sfc_port_pairs_path, retrieve_all, **_params) - def show_port_pair(self, port_pair, **_params): + def show_sfc_port_pair(self, port_pair, **_params): """Fetches information of a certain Port Pair.""" return self.get(self.sfc_port_pair_path % (port_pair), params=_params) - def create_port_pair_group(self, body=None): + def create_sfc_port_pair_group(self, body=None): """Creates a new Port Pair Group.""" return self.post(self.sfc_port_pair_groups_path, body=body) - def update_port_pair_group(self, port_pair_group, body=None): + def update_sfc_port_pair_group(self, port_pair_group, body=None): """Update a Port Pair Group.""" return self.put(self.sfc_port_pair_group_path % port_pair_group, body=body) - def delete_port_pair_group(self, port_pair_group): + def delete_sfc_port_pair_group(self, port_pair_group): """Deletes the specified Port Pair Group.""" return self.delete(self.sfc_port_pair_group_path % (port_pair_group)) - def list_port_pair_group(self, retrieve_all=True, **_params): + def list_sfc_port_pair_groups(self, retrieve_all=True, **_params): """Fetches a list of all Port Pair Groups.""" return self.list('port_pair_groups', self.sfc_port_pair_groups_path, retrieve_all, **_params) - def show_port_pair_group(self, port_pair_group, **_params): + def show_sfc_port_pair_group(self, port_pair_group, **_params): """Fetches information of a certain Port Pair Group.""" return self.get(self.sfc_port_pair_group_path % (port_pair_group), params=_params) - def create_port_chain(self, body=None): + def create_sfc_port_chain(self, body=None): """Creates a new Port Chain.""" return self.post(self.sfc_port_chains_path, body=body) - def update_port_chain(self, port_chain, body=None): + def update_sfc_port_chain(self, port_chain, body=None): """Update a Port Chain.""" return self.put(self.sfc_port_chain_path % port_chain, body=body) - def delete_port_chain(self, port_chain): + def delete_sfc_port_chain(self, port_chain): """Deletes the specified Port Chain.""" return self.delete(self.sfc_port_chain_path % (port_chain)) - def list_port_chain(self, retrieve_all=True, **_params): + def list_sfc_port_chains(self, retrieve_all=True, **_params): """Fetches a list of all Port Chains.""" return self.list('port_chains', self.sfc_port_chains_path, retrieve_all, **_params) - def show_port_chain(self, port_chain, **_params): + def show_sfc_port_chain(self, port_chain, **_params): """Fetches information of a certain Port Chain.""" return self.get(self.sfc_port_chain_path % (port_chain), params=_params) - def create_flow_classifier(self, body=None): + def create_sfc_flow_classifier(self, body=None): """Creates a new Flow Classifier.""" return self.post(self.sfc_flow_classifiers_path, body=body) - def update_flow_classifier(self, flow_classifier, body=None): + def update_sfc_flow_classifier(self, flow_classifier, body=None): """Update a Flow Classifier.""" return self.put(self.sfc_flow_classifier_path % flow_classifier, body=body) - def delete_flow_classifier(self, flow_classifier): + def delete_sfc_flow_classifier(self, flow_classifier): """Deletes the specified Flow Classifier.""" return self.delete(self.sfc_flow_classifier_path % (flow_classifier)) - def list_flow_classifier(self, retrieve_all=True, **_params): + def list_sfc_flow_classifiers(self, retrieve_all=True, **_params): """Fetches a list of all Flow Classifiers.""" return self.list('flow_classifiers', self.sfc_flow_classifiers_path, retrieve_all, **_params) - def show_flow_classifier(self, flow_classifier, **_params): + def show_sfc_flow_classifier(self, flow_classifier, **_params): """Fetches information of a certain Flow Classifier.""" return self.get(self.sfc_flow_classifier_path % (flow_classifier), params=_params) From 864f4d111f325dd7458ada523fbc3355d3afce2f Mon Sep 17 00:00:00 2001 From: Bernard Cafarelli Date: Tue, 17 Oct 2017 12:18:18 +0200 Subject: [PATCH 619/845] SFC plugin: fix port chain set commands Setting a flow classifier or port pair group currently fails, additionally with multiple flow classifiers only the last one is added * Fix adding multiple items in the same command * Update unit test to try setting multiple flow classifiers Change-Id: Ifbc24d84022c9893d3348fb27b20206fa464d8b2 Closes-Bug: #1716942 --- neutronclient/osc/v2/sfc/sfc_port_chain.py | 31 ++++++++++--------- .../tests/unit/osc/v2/sfc/test_port_chain.py | 25 +++++++++------ 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/neutronclient/osc/v2/sfc/sfc_port_chain.py b/neutronclient/osc/v2/sfc/sfc_port_chain.py index 37b30208d..36db079e4 100755 --- a/neutronclient/osc/v2/sfc/sfc_port_chain.py +++ b/neutronclient/osc/v2/sfc/sfc_port_chain.py @@ -188,37 +188,38 @@ def take_action(self, parsed_args): if parsed_args.no_flow_classifier: attrs['flow_classifiers'] = [] if parsed_args.flow_classifiers: - for fc in parsed_args.flow_classifiers: - added = [client.find_resource( - 'flow_classifier', fc, - cmd_resource='sfc_flow_classifier')['id']] if parsed_args.no_flow_classifier: - existing = [] + fc_list = [] else: - existing = [client.find_resource( + fc_list = client.find_resource( resource, parsed_args.port_chain, - cmd_resource='sfc_port_chain')['flow_classifiers']] - attrs['flow_classifiers'] = sorted(list( - set(existing) | set(added))) + cmd_resource='sfc_port_chain')['flow_classifiers'] + for fc in parsed_args.flow_classifiers: + fc_list.append(client.find_resource( + 'flow_classifier', fc, + cmd_resource='sfc_flow_classifier')['id']) + attrs['flow_classifiers'] = sorted(list(set(fc_list))) if (parsed_args.no_port_pair_group and not parsed_args.port_pair_groups): message = _('At least one --port-pair-group must be specified.') raise exceptions.CommandError(message) if parsed_args.no_port_pair_group and parsed_args.port_pair_groups: + ppg_list = [] for ppg in parsed_args.port_pair_groups: - attrs['port_pair_groups'] = [client.find_resource( + ppg_list.append(client.find_resource( 'port_pair_group', ppg, - cmd_resource='sfc_port_pair_group')['id']] + cmd_resource='sfc_port_pair_group')['id']) + attrs['port_pair_groups'] = sorted(list(set(ppg_list))) if (parsed_args.port_pair_groups and not parsed_args.no_port_pair_group): - existing_ppg = [client.find_resource( + ppg_list = client.find_resource( resource, parsed_args.port_chain, - cmd_resource='sfc_port_chain')['port_pair_groups']] + cmd_resource='sfc_port_chain')['port_pair_groups'] for ppg in parsed_args.port_pair_groups: - existing_ppg.append(client.find_resource( + ppg_list.append(client.find_resource( 'port_pair_group', ppg, cmd_resource='sfc_port_pair_group')['id']) - attrs['port_pair_groups'] = sorted(list(set(existing_ppg))) + attrs['port_pair_groups'] = sorted(list(set(ppg_list))) body = {resource: attrs} try: client.update_sfc_port_chain(pc_id, body) diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py b/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py index e94e8260c..f15fe3f12 100755 --- a/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py +++ b/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py @@ -266,35 +266,42 @@ def test_set_port_chain(self): attrs) self.assertIsNone(result) - def test_set_flow_classifier(self): + def test_set_flow_classifiers(self): target = self.resource['id'] fc1 = 'flow_classifier1' + fc2 = 'flow_classifier2' def _mock_flow_classifier(*args, **kwargs): - if self.neutronclient.find_resource.call_count == 1: + self.neutronclient.find_resource.assert_called_with( + self.res, target, cmd_resource='sfc_port_chain') + return {'flow_classifiers': [self.pc_fc]} + + if self.neutronclient.find_resource.call_count == 2: self.neutronclient.find_resource.assert_called_with( 'flow_classifier', fc1, cmd_resource='sfc_flow_classifier') return {'id': args[1]} - if self.neutronclient.find_resource.call_count == 2: + if self.neutronclient.find_resource.call_count == 3: self.neutronclient.find_resource.assert_called_with( - self.res, target, cmd_resource='sfc_port_chain') - return {'flow_classifiers': self.pc_fc} + 'flow_classifier', fc2, cmd_resource='sfc_flow_classifier') + return {'id': args[1]} + self.neutronclient.find_resource.side_effect = _mock_flow_classifier arglist = [ target, '--flow-classifier', fc1, + '--flow-classifier', fc2, ] verifylist = [ (self.res, target), - ('flow_classifiers', [fc1]) + ('flow_classifiers', [fc1, fc2]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - expect = {'flow_classifiers': sorted([self.pc_fc, fc1])} + expect = {'flow_classifiers': sorted([self.pc_fc, fc1, fc2])} self.mocked.assert_called_once_with(target, {self.res: expect}) - self.assertEqual(2, self.neutronclient.find_resource.call_count) + self.assertEqual(3, self.neutronclient.find_resource.call_count) self.assertIsNone(result) def test_set_no_flow_classifier(self): @@ -325,7 +332,7 @@ def _mock_flow_classifier(*args, **kwargs): if self.neutronclient.find_resource.call_count == 1: self.neutronclient.find_resource.assert_called_with( self.res, target, cmd_resource='sfc_port_chain') - return {'port_pair_groups': self.pc_ppg} + return {'port_pair_groups': [self.pc_ppg]} if self.neutronclient.find_resource.call_count == 2: self.neutronclient.find_resource.assert_called_with( From 6feb30daadb95a85a38194b21e70a00b7d2e5ff3 Mon Sep 17 00:00:00 2001 From: Bernard Cafarelli Date: Tue, 26 Sep 2017 15:26:27 +0200 Subject: [PATCH 620/845] SFC plugin: fix dictionary parameters Service function parameters could not be set on a port pair, and "None" correlation value was not accepted Chain parameters were passed incorrectly to server on a port chain On a port pair group, lb_fields must be passed to the API with an underscore Fix check on empty dictionary parameters (we get [], not None) Also fix related unit tests Change-Id: Ie60c609b8d33731a7fed732d97ed92982523956b Closes-Bug: #1719584 --- neutronclient/osc/v2/sfc/sfc_port_chain.py | 16 ++++++++++------ neutronclient/osc/v2/sfc/sfc_port_pair.py | 17 +++++++++++++++-- neutronclient/osc/v2/sfc/sfc_port_pair_group.py | 7 +++---- .../tests/unit/osc/v2/sfc/test_port_chain.py | 10 ++++------ .../tests/unit/osc/v2/sfc/test_port_pair.py | 14 ++++++++------ 5 files changed, 40 insertions(+), 24 deletions(-) diff --git a/neutronclient/osc/v2/sfc/sfc_port_chain.py b/neutronclient/osc/v2/sfc/sfc_port_chain.py index 36db079e4..e5e9f7743 100755 --- a/neutronclient/osc/v2/sfc/sfc_port_chain.py +++ b/neutronclient/osc/v2/sfc/sfc_port_chain.py @@ -324,13 +324,11 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): attrs['name'] = parsed_args.name if parsed_args.description is not None: attrs['description'] = parsed_args.description - if ('port_pair_groups' in parsed_args and - parsed_args.port_pair_groups is not None): + if parsed_args.port_pair_groups: attrs['port_pair_groups'] = [(_get_id(client_manager.neutronclient, ppg, 'port_pair_group')) for ppg in parsed_args.port_pair_groups] - if ('flow_classifiers' in parsed_args and - parsed_args.flow_classifiers is not None): + if parsed_args.flow_classifiers: attrs['flow_classifiers'] = [(_get_id(client_manager.neutronclient, fc, 'flow_classifier')) for fc in parsed_args.flow_classifiers] @@ -340,8 +338,14 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): def _get_attrs(attrs, parsed_args): - if 'chain_parameters' in parsed_args: - attrs['chain_parameters'] = parsed_args.chain_parameters + if parsed_args.chain_parameters is not None: + chain_params = {} + for chain_param in parsed_args.chain_parameters: + if 'correlation' in chain_param: + chain_params['correlation'] = chain_param['correlation'] + if 'symmetric' in chain_param: + chain_params['symmetric'] = chain_param['symmetric'] + attrs['chain_parameters'] = chain_params def _get_id(client, id_or_name, resource): diff --git a/neutronclient/osc/v2/sfc/sfc_port_pair.py b/neutronclient/osc/v2/sfc/sfc_port_pair.py index b8e64e4c4..0dfbda3d9 100755 --- a/neutronclient/osc/v2/sfc/sfc_port_pair.py +++ b/neutronclient/osc/v2/sfc/sfc_port_pair.py @@ -206,10 +206,23 @@ def _get_attrs(client_manager, attrs, parsed_args): if parsed_args.egress is not None: attrs['egress'] = _get_id(client_manager.neutronclient, parsed_args.egress, 'port') - if 'service_function_parameters' in parsed_args: - attrs['service_function_parameters'] = ( + if parsed_args.service_function_parameters is not None: + attrs['service_function_parameters'] = _get_service_function_params( parsed_args.service_function_parameters) +def _get_service_function_params(sf_params): + attrs = {} + for sf_param in sf_params: + if 'correlation' in sf_param: + if sf_param['correlation'] == 'None': + attrs['correlation'] = None + else: + attrs['correlation'] = sf_param['correlation'] + if 'weight' in sf_param: + attrs['weight'] = sf_param['weight'] + return attrs + + def _get_id(client, id_or_name, resource): return client.find_resource(resource, id_or_name)['id'] diff --git a/neutronclient/osc/v2/sfc/sfc_port_pair_group.py b/neutronclient/osc/v2/sfc/sfc_port_pair_group.py index 688e5c3cd..b943ed6cf 100755 --- a/neutronclient/osc/v2/sfc/sfc_port_pair_group.py +++ b/neutronclient/osc/v2/sfc/sfc_port_pair_group.py @@ -257,8 +257,8 @@ def take_action(self, parsed_args): def _get_ppg_param(attrs, ppg): attrs['port_pair_group_parameters'] = {} for key, value in ppg.items(): - if key == 'lb_fields': - attrs['port_pair_group_parameters'][key] = ([ + if key == 'lb-fields': + attrs['port_pair_group_parameters']['lb_fields'] = ([ field for field in value.split('&') if field]) else: attrs['port_pair_group_parameters'][key] = value @@ -281,8 +281,7 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): def _get_attrs(attrs, parsed_args): - if ('port_pair_group_parameters' in parsed_args and - parsed_args.port_pair_group_parameters is not None): + if parsed_args.port_pair_group_parameters is not None: attrs['port_pair_group_parameters'] = ( _get_ppg_param(attrs, parsed_args.port_pair_group_parameters)) diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py b/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py index f15fe3f12..1782be927 100755 --- a/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py +++ b/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py @@ -62,7 +62,7 @@ def setUp(self): # Get the command object to test self.cmd = sfc_port_chain.CreateSfcPortChain(self.app, self.namespace) - def test_create_port_chain_dafault_options(self): + def test_create_port_chain_default_options(self): arglist = [ self._port_chain['name'], "--port-pair-group", self._port_chain['port_pair_groups'] @@ -80,9 +80,7 @@ def test_create_port_chain_dafault_options(self): self.neutronclient.create_sfc_port_chain.assert_called_once_with({ 'port_chain': { 'name': self._port_chain['name'], - 'port_pair_groups': [self._port_chain['port_pair_groups']], - 'flow_classifiers': [], - 'chain_parameters': None} + 'port_pair_groups': [self._port_chain['port_pair_groups']]} }) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) @@ -96,14 +94,14 @@ def test_create_port_chain_all_options(self): "--chain-parameters", 'correlation=mpls,symmetric=true', ] - cp = [{'correlation': 'mpls', 'symmetric': 'true'}] + cp = {'correlation': 'mpls', 'symmetric': 'true'} verifylist = [ ('port_pair_groups', [self._port_chain['port_pair_groups']]), ('name', self._port_chain['name']), ('description', self._port_chain['description']), ('flow_classifiers', [self._port_chain['flow_classifiers']]), - ('chain_parameters', cp) + ('chain_parameters', [cp]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_port_pair.py b/neutronclient/tests/unit/osc/v2/sfc/test_port_pair.py index 1111e8202..c56781881 100755 --- a/neutronclient/tests/unit/osc/v2/sfc/test_port_pair.py +++ b/neutronclient/tests/unit/osc/v2/sfc/test_port_pair.py @@ -75,7 +75,6 @@ def test_create_port_pair_default_options(self): 'port_pair': {'name': self._port_pair['name'], 'ingress': self._port_pair['ingress'], 'egress': self._port_pair['egress'], - 'service_function_parameters': None, } }) self.assertEqual(self.columns, columns) @@ -91,27 +90,30 @@ def _test_create_port_pair_all_options(self, correlation): 'correlation=%s,weight=1' % correlation, ] - sfp = [{'correlation': correlation, 'weight': '1'}] - verifylist = [ ('ingress', self._port_pair['ingress']), ('egress', self._port_pair['egress']), ('name', self._port_pair['name']), ('description', self._port_pair['description']), - ('service_function_parameters', sfp) + ('service_function_parameters', + [{'correlation': correlation, 'weight': '1'}]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = (self.cmd.take_action(parsed_args)) + if correlation == "None": + correlation_param = None + else: + correlation_param = correlation self.neutronclient.create_sfc_port_pair.assert_called_once_with({ 'port_pair': {'name': self._port_pair['name'], 'ingress': self._port_pair['ingress'], 'egress': self._port_pair['egress'], 'description': self._port_pair['description'], 'service_function_parameters': - [{'correlation': correlation, 'weight': - '1'}], + {'correlation': correlation_param, 'weight': + '1'}, } }) self.assertEqual(self.columns, columns) From 42c204f7cbef8f28356c84a1fc3dfdc0575d50eb Mon Sep 17 00:00:00 2001 From: Bernard Cafarelli Date: Tue, 17 Oct 2017 15:04:40 +0200 Subject: [PATCH 621/845] SFC plugin: preserve chain order in set/unset commands Port pair groups order must be kept so we cannot use a set to filter duplicates. Else this will change the chain order Also fixed for flow classifiers: though loosing the order does not currently trigger a visible change, this restores the neutron CLI behaviour Expand help messages to mention the port pair groups order Change-Id: Ia6e342fda2c739a3553a8736837bcacf00a2d030 Closes-Bug: #1724187 --- neutronclient/osc/v2/sfc/sfc_port_chain.py | 61 +++++++++++-------- .../tests/unit/osc/v2/sfc/test_port_chain.py | 12 ++-- 2 files changed, 42 insertions(+), 31 deletions(-) diff --git a/neutronclient/osc/v2/sfc/sfc_port_chain.py b/neutronclient/osc/v2/sfc/sfc_port_chain.py index e5e9f7743..1044c2bd3 100755 --- a/neutronclient/osc/v2/sfc/sfc_port_chain.py +++ b/neutronclient/osc/v2/sfc/sfc_port_chain.py @@ -75,7 +75,7 @@ def get_parser(self, prog_name): dest='port_pair_groups', required=True, action='append', - help=_('Port pair group (name or ID). ' + help=_('Add port pair group (name or ID). ' 'This option can be repeated.')) return parser @@ -160,18 +160,20 @@ def get_parser(self, prog_name): parser.add_argument( '--no-flow-classifier', action='store_true', - help=_('Associate no flow classifier with the port chain')) + help=_('Remove associated flow classifiers from the port chain')) parser.add_argument( '--port-pair-group', metavar='', dest='port_pair_groups', action='append', help=_('Add port pair group (name or ID). ' + 'Current port pair groups order is kept, the added port ' + 'pair group will be placed at the end of the port chain. ' 'This option can be repeated.')) parser.add_argument( '--no-port-pair-group', action='store_true', - help=_('Remove associated port pair group from the port chain.' + help=_('Remove associated port pair groups from the port chain. ' 'At least one --port-pair-group must be specified ' 'together.')) parser.add_argument( @@ -195,10 +197,12 @@ def take_action(self, parsed_args): resource, parsed_args.port_chain, cmd_resource='sfc_port_chain')['flow_classifiers'] for fc in parsed_args.flow_classifiers: - fc_list.append(client.find_resource( + fc_id = client.find_resource( 'flow_classifier', fc, - cmd_resource='sfc_flow_classifier')['id']) - attrs['flow_classifiers'] = sorted(list(set(fc_list))) + cmd_resource='sfc_flow_classifier')['id'] + if fc_id not in fc_list: + fc_list.append(fc_id) + attrs['flow_classifiers'] = fc_list if (parsed_args.no_port_pair_group and not parsed_args.port_pair_groups): message = _('At least one --port-pair-group must be specified.') @@ -206,20 +210,24 @@ def take_action(self, parsed_args): if parsed_args.no_port_pair_group and parsed_args.port_pair_groups: ppg_list = [] for ppg in parsed_args.port_pair_groups: - ppg_list.append(client.find_resource( + ppg_id = client.find_resource( 'port_pair_group', ppg, - cmd_resource='sfc_port_pair_group')['id']) - attrs['port_pair_groups'] = sorted(list(set(ppg_list))) + cmd_resource='sfc_port_pair_group')['id'] + if ppg_id not in ppg_list: + ppg_list.append(ppg_id) + attrs['port_pair_groups'] = ppg_list if (parsed_args.port_pair_groups and not parsed_args.no_port_pair_group): ppg_list = client.find_resource( resource, parsed_args.port_chain, cmd_resource='sfc_port_chain')['port_pair_groups'] for ppg in parsed_args.port_pair_groups: - ppg_list.append(client.find_resource( + ppg_id = client.find_resource( 'port_pair_group', ppg, - cmd_resource='sfc_port_pair_group')['id']) - attrs['port_pair_groups'] = sorted(list(set(ppg_list))) + cmd_resource='sfc_port_pair_group')['id'] + if ppg_id not in ppg_list: + ppg_list.append(ppg_id) + attrs['port_pair_groups'] = ppg_list body = {resource: attrs} try: client.update_sfc_port_chain(pc_id, body) @@ -285,30 +293,33 @@ def take_action(self, parsed_args): pc_id = _get_id(client, parsed_args.port_chain, resource) attrs = {} if parsed_args.flow_classifiers: - existing = [client.find_resource( + fc_list = client.find_resource( resource, parsed_args.port_chain, - cmd_resource='sfc_port_chain')['flow_classifiers']] + cmd_resource='sfc_port_chain')['flow_classifiers'] for fc in parsed_args.flow_classifiers: - removed = [client.find_resource( + fc_id = client.find_resource( 'flow_classifier', fc, - cmd_resource='sfc_flow_classifier')['id']] - attrs['flow_classifiers'] = list(set(existing) - set(removed)) + cmd_resource='sfc_flow_classifier')['id'] + if fc_id in fc_list: + fc_list.remove(fc_id) + attrs['flow_classifiers'] = fc_list if parsed_args.all_flow_classifier: attrs['flow_classifiers'] = [] if parsed_args.port_pair_groups: - existing_ppg = [client.find_resource( + ppg_list = client.find_resource( resource, parsed_args.port_chain, - cmd_resource='sfc_port_chain')['port_pair_groups']] + cmd_resource='sfc_port_chain')['port_pair_groups'] for ppg in parsed_args.port_pair_groups: - removed_ppg = [client.find_resource( + ppg_id = client.find_resource( 'port_pair_group', ppg, - cmd_resource='sfc_port_pair_group')['id']] - attrs['port_pair_groups'] = list(set(existing_ppg) - - set(removed_ppg)) - if attrs['port_pair_groups'] == []: - message = _('At least one --port-pair-group must be' + cmd_resource='sfc_port_pair_group')['id'] + if ppg_id in ppg_list: + ppg_list.remove(ppg_id) + if ppg_list == []: + message = _('At least one port pair group must be' ' specified.') raise exceptions.CommandError(message) + attrs['port_pair_groups'] = ppg_list body = {resource: attrs} try: client.update_sfc_port_chain(pc_id, body) diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py b/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py index 1782be927..fbf60303d 100755 --- a/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py +++ b/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py @@ -297,7 +297,7 @@ def _mock_flow_classifier(*args, **kwargs): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - expect = {'flow_classifiers': sorted([self.pc_fc, fc1, fc2])} + expect = {'flow_classifiers': [self.pc_fc, fc1, fc2]} self.mocked.assert_called_once_with(target, {self.res: expect}) self.assertEqual(3, self.neutronclient.find_resource.call_count) self.assertIsNone(result) @@ -356,7 +356,7 @@ def _mock_flow_classifier(*args, **kwargs): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - expect = {'port_pair_groups': sorted([existing_ppg, ppg1, ppg2])} + expect = {'port_pair_groups': [existing_ppg, ppg1, ppg2]} self.mocked.assert_called_once_with(target, {self.res: expect}) self.assertEqual(3, self.neutronclient.find_resource.call_count) self.assertIsNone(result) @@ -484,7 +484,7 @@ def _mock_port_pair_group(*args, **kwargs): if self.neutronclient.find_resource.call_count == 1: self.neutronclient.find_resource.assert_called_with( self.res, target, cmd_resource='sfc_port_chain') - return {'port_pair_groups': self.pc_ppg} + return {'port_pair_groups': [self.pc_ppg]} if self.neutronclient.find_resource.call_count == 2: self.neutronclient.find_resource.assert_called_with( @@ -507,7 +507,7 @@ def _mock_port_pair_group(*args, **kwargs): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - expect = {'port_pair_groups': sorted([self.pc_ppg])} + expect = {'port_pair_groups': [self.pc_ppg]} self.mocked.assert_called_once_with(target, {self.res: expect}) self.assertIsNone(result) @@ -519,7 +519,7 @@ def _mock_flow_classifier(*args, **kwargs): if self.neutronclient.find_resource.call_count == 1: self.neutronclient.find_resource.assert_called_with( self.res, target, cmd_resource='sfc_port_chain') - return {'flow_classifiers': self.pc_fc} + return {'flow_classifiers': [self.pc_fc]} if self.neutronclient.find_resource.call_count == 2: self.neutronclient.find_resource.assert_called_with( @@ -537,7 +537,7 @@ def _mock_flow_classifier(*args, **kwargs): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - expect = {'flow_classifiers': sorted([self.pc_fc])} + expect = {'flow_classifiers': [self.pc_fc]} self.mocked.assert_called_once_with(target, {self.res: expect}) self.assertIsNone(result) From 33947da4b93c41248cb6c46c1f6e0a7bdcaaf9e9 Mon Sep 17 00:00:00 2001 From: Na Date: Tue, 12 Jul 2016 02:50:06 -0500 Subject: [PATCH 622/845] Dynamic routing CLIs OSC transition Neutron is transitioning its CLI to OSC, should update dynamic routing CLIs to align with OSC plugin. Change-Id: Ic1a633ca103d9e97e28ddf23e0f528e0261ce497 Co-Authored-By: Ryan Tidwell Co-Authored-By: Roey Chen Partially-Implements: blueprint bgp-spinout Partial-Bug: #1560003 --- doc/source/cli/osc/v2/bgp-dynamic-routing.rst | 50 +++ .../osc/v2/dynamic_routing/bgp_dragent.py | 99 ++++++ .../osc/v2/dynamic_routing/bgp_peer.py | 188 ++++++++++ .../osc/v2/dynamic_routing/bgp_speaker.py | 320 ++++++++++++++++++ .../osc/v2/dynamic_routing/constants.py | 18 + .../unit/osc/v2/dynamic_routing/fakes.py | 96 ++++++ .../osc/v2/dynamic_routing/test_bgp_peer.py | 153 +++++++++ .../v2/dynamic_routing/test_bgp_speaker.py | 157 +++++++++ ...amic-routing-support-11130b2f440c0ac2.yaml | 4 + setup.cfg | 19 ++ 10 files changed, 1104 insertions(+) create mode 100644 doc/source/cli/osc/v2/bgp-dynamic-routing.rst create mode 100644 neutronclient/osc/v2/dynamic_routing/bgp_dragent.py create mode 100644 neutronclient/osc/v2/dynamic_routing/bgp_peer.py create mode 100644 neutronclient/osc/v2/dynamic_routing/bgp_speaker.py create mode 100644 neutronclient/osc/v2/dynamic_routing/constants.py create mode 100644 neutronclient/tests/unit/osc/v2/dynamic_routing/fakes.py create mode 100644 neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_peer.py create mode 100644 neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_speaker.py create mode 100644 releasenotes/notes/add-osc-dynamic-routing-support-11130b2f440c0ac2.yaml diff --git a/doc/source/cli/osc/v2/bgp-dynamic-routing.rst b/doc/source/cli/osc/v2/bgp-dynamic-routing.rst new file mode 100644 index 000000000..6d712809f --- /dev/null +++ b/doc/source/cli/osc/v2/bgp-dynamic-routing.rst @@ -0,0 +1,50 @@ +=================== +BGP Dynamic Routing +=================== + +BGP dynamic routing enables announcement of project subnet prefixes +via BGP. Admins create BGP speakers and BGP peers. BGP peers can be +associated with BGP speakers, thereby enabling peering sessions with +operator infrastructure. BGP speakers can be associated with networks, +which controls which routes are announced to peers. + +Network v2 + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgp speaker create + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgp speaker delete + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgp speaker list + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgp speaker set + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgp speaker show + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgp speaker show dragents + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgp speaker add network + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgp speaker remove network + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgp speaker add peer + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgp speaker remove peer + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgp speaker list advertised routes + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgp peer * + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgp dragent * diff --git a/neutronclient/osc/v2/dynamic_routing/bgp_dragent.py b/neutronclient/osc/v2/dynamic_routing/bgp_dragent.py new file mode 100644 index 000000000..f7f1e836b --- /dev/null +++ b/neutronclient/osc/v2/dynamic_routing/bgp_dragent.py @@ -0,0 +1,99 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from osc_lib.command import command +from osc_lib import utils + +from neutronclient._i18n import _ +from neutronclient.osc.v2.dynamic_routing import constants + + +def _format_alive_state(item): + return ':-)' if item else 'XXX' + + +_formatters = { + 'alive': _format_alive_state +} + + +def add_common_args(parser): + parser.add_argument('dragent_id', + metavar='', + help=_("ID of the dynamic routing agent")) + parser.add_argument('bgp_speaker', + metavar='', + help=_("ID or name of the BGP speaker")) + + +class AddBgpSpeakerToDRAgent(command.Command): + """Add a BGP speaker to a dynamic routing agent""" + + def get_parser(self, prog_name): + parser = super(AddBgpSpeakerToDRAgent, self).get_parser(prog_name) + add_common_args(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + speaker_id = client.find_resource(constants.BGP_SPEAKER, + parsed_args.bgp_speaker)['id'] + client.add_bgp_speaker_to_dragent( + parsed_args.dragent_id, {'bgp_speaker_id': speaker_id}) + + +class RemoveBgpSpeakerFromDRAgent(command.Command): + """Removes a BGP speaker from a dynamic routing agent""" + + def get_parser(self, prog_name): + parser = super(RemoveBgpSpeakerFromDRAgent, self).get_parser( + prog_name) + add_common_args(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + speaker_id = client.find_resource(constants.BGP_SPEAKER, + parsed_args.bgp_speaker)['id'] + client.remove_bgp_speaker_from_dragent(parsed_args.dragent_id, + speaker_id) + + +class ListDRAgentsHostingBgpSpeaker(command.Lister): + """List dynamic routing agents hosting a BGP speaker""" + + resource = 'agent' + list_columns = ['id', 'host', 'admin_state_up', 'alive'] + unknown_parts_flag = False + + def get_parser(self, prog_name): + parser = super(ListDRAgentsHostingBgpSpeaker, + self).get_parser(prog_name) + parser.add_argument('bgp_speaker', + metavar='', + help=_("ID or name of the BGP speaker")) + return parser + + def take_action(self, parsed_args): + search_opts = {} + client = self.app.client_manager.neutronclient + speaker_id = client.find_resource(constants.BGP_SPEAKER, + parsed_args.bgp_speaker)['id'] + search_opts['bgp_speaker'] = speaker_id + data = client.list_dragents_hosting_bgp_speaker(**search_opts) + headers = ('ID', 'Host', 'State', 'Alive') + columns = ('id', 'host', 'admin_state_up', 'alive') + return (headers, + (utils.get_dict_properties( + s, columns, formatters=_formatters, + ) for s in data['agents'])) diff --git a/neutronclient/osc/v2/dynamic_routing/bgp_peer.py b/neutronclient/osc/v2/dynamic_routing/bgp_peer.py new file mode 100644 index 000000000..c245ce2e7 --- /dev/null +++ b/neutronclient/osc/v2/dynamic_routing/bgp_peer.py @@ -0,0 +1,188 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from osc_lib.command import command +from osc_lib import utils + +from neutronclient._i18n import _ +from neutronclient.common import exceptions +from neutronclient.common import utils as nc_utils +from neutronclient.osc import utils as nc_osc_utils +from neutronclient.osc.v2.dynamic_routing import constants + + +def _get_attrs(client_manager, parsed_args): + attrs = {} + + # Validate password + if 'auth_type' in parsed_args: + if parsed_args.auth_type != 'none': + if 'password' not in parsed_args or parsed_args.password is None: + raise exceptions.CommandError(_('Must provide password if ' + 'auth-type is specified.')) + if ( + parsed_args.auth_type == 'none' and + parsed_args.password is not None + ): + raise exceptions.CommandError(_('Must provide auth-type if ' + 'password is specified.')) + attrs['auth_type'] = parsed_args.auth_type + + if parsed_args.name is not None: + attrs['name'] = parsed_args.name + if 'remote_as' in parsed_args: + attrs['remote_as'] = parsed_args.remote_as + if 'peer_ip' in parsed_args: + attrs['peer_ip'] = parsed_args.peer_ip + if 'password' in parsed_args: + attrs['password'] = parsed_args.password + + if 'project' in parsed_args and parsed_args.project is not None: + identity_client = client_manager.identity + project_id = nc_osc_utils.find_project( + identity_client, + parsed_args.project, + parsed_args.project_domain, + ).id + attrs['tenant_id'] = project_id + return attrs + + +class CreateBgpPeer(command.ShowOne): + _description = _("Create a BGP peer") + + def get_parser(self, prog_name): + parser = super(CreateBgpPeer, self).get_parser(prog_name) + parser.add_argument( + 'name', + metavar='', + help=_("Name of the BGP peer to create")) + parser.add_argument( + '--peer-ip', + metavar='', + required=True, + help=_("Peer IP address")) + parser.add_argument( + '--remote-as', + required=True, + metavar='', + help=_("Peer AS number. (Integer in [%(min_val)s, %(max_val)s] " + "is allowed)") % {'min_val': constants.MIN_AS_NUM, + 'max_val': constants.MAX_AS_NUM}) + parser.add_argument( + '--auth-type', + metavar='', + choices=['none', 'md5'], + type=nc_utils.convert_to_lowercase, + default='none', + help=_("Authentication algorithm. Supported algorithms: " + "none (default), md5")) + parser.add_argument( + '--password', + metavar='', + help=_("Authentication password")) + nc_osc_utils.add_project_owner_option_to_parser(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + attrs = _get_attrs(self.app.client_manager, parsed_args) + body = {constants.BGP_PEER: attrs} + obj = client.create_bgp_peer(body)[constants.BGP_PEER] + columns, display_columns = nc_osc_utils.get_columns(obj) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteBgpPeer(command.Command): + _description = _("Delete a BGP peer") + + def get_parser(self, prog_name): + parser = super(DeleteBgpPeer, self).get_parser(prog_name) + parser.add_argument( + 'bgp_peer', + metavar="", + help=_("BGP peer to delete (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + id = client.find_resource(constants.BGP_PEER, + parsed_args.bgp_peer)['id'] + client.delete_bgp_peer(id) + + +class ListBgpPeer(command.Lister): + _description = _("List BGP peers") + + def take_action(self, parsed_args): + data = self.app.client_manager.neutronclient.list_bgp_peers() + headers = ('ID', 'Name', 'Peer IP', 'Remote AS') + columns = ('id', 'name', 'peer_ip', 'remote_as') + return (headers, + (utils.get_dict_properties( + s, columns, + ) for s in data[constants.BGP_PEERS])) + + +class SetBgpPeer(command.Command): + _description = _("Update a BGP peer") + resource = constants.BGP_PEER + + def get_parser(self, prog_name): + parser = super(SetBgpPeer, self).get_parser(prog_name) + parser.add_argument( + '--name', + help=_("Updated name of the BGP peer")) + parser.add_argument( + '--password', + metavar='', + help=_("Updated authentication password")) + parser.add_argument( + 'bgp_peer', + metavar="", + help=_("BGP peer to update (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + id = client.find_resource(constants.BGP_PEER, + parsed_args.bgp_peer)['id'] + attrs = _get_attrs(self.app.client_manager, parsed_args) + body = {} + body[constants.BGP_PEER] = attrs + client.update_bgp_peer(id, body) + + +class ShowBgpPeer(command.ShowOne): + _description = _("Show information for a BGP peer") + + def get_parser(self, prog_name): + parser = super(ShowBgpPeer, self).get_parser(prog_name) + parser.add_argument( + 'bgp_peer', + metavar="", + help=_("BGP peer to display (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + id = client.find_resource(constants.BGP_PEER, + parsed_args.bgp_peer)['id'] + obj = client.show_bgp_peer(id)[constants.BGP_PEER] + columns, display_columns = nc_osc_utils.get_columns(obj) + data = utils.get_dict_properties(obj, columns) + return display_columns, data diff --git a/neutronclient/osc/v2/dynamic_routing/bgp_speaker.py b/neutronclient/osc/v2/dynamic_routing/bgp_speaker.py new file mode 100644 index 000000000..bfe202f2b --- /dev/null +++ b/neutronclient/osc/v2/dynamic_routing/bgp_speaker.py @@ -0,0 +1,320 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from osc_lib.command import command +from osc_lib import utils + +from neutronclient._i18n import _ +from neutronclient.osc import utils as nc_osc_utils +from neutronclient.osc.v2.dynamic_routing import constants + + +def _get_attrs(client_manager, parsed_args): + attrs = {} + if parsed_args.name is not None: + attrs['name'] = str(parsed_args.name) + if 'local_as' in parsed_args: + attrs['local_as'] = parsed_args.local_as + if 'ip_version' in parsed_args: + attrs['ip_version'] = parsed_args.ip_version + if parsed_args.advertise_tenant_networks: + attrs['advertise_tenant_networks'] = True + if parsed_args.no_advertise_tenant_networks: + attrs['advertise_tenant_networks'] = False + if parsed_args.advertise_floating_ip_host_routes: + attrs['advertise_floating_ip_host_routes'] = True + if parsed_args.no_advertise_floating_ip_host_routes: + attrs['advertise_floating_ip_host_routes'] = False + + if 'project' in parsed_args and parsed_args.project is not None: + identity_client = client_manager.identity + project_id = nc_osc_utils.find_project( + identity_client, + parsed_args.project, + parsed_args.project_domain, + ).id + attrs['tenant_id'] = project_id + return attrs + + +def add_common_arguments(parser): + parser.add_argument( + '--advertise-floating-ip-host-routes', + action='store_true', + help=_("Enable the advertisement of floating IP host routes " + "by the BGP speaker. (default)")) + parser.add_argument( + '--no-advertise-floating-ip-host-routes', + action='store_true', + help=_("Disable the advertisement of floating IP host routes " + "by the BGP speaker.")) + parser.add_argument( + '--advertise-tenant-networks', + action='store_true', + help=_("Enable the advertisement of tenant network routes " + "by the BGP speaker. (default)")) + parser.add_argument( + '--no-advertise-tenant-networks', + action='store_true', + help=_("Disable the advertisement of tenant network routes " + "by the BGP speaker.")) + + +class AddNetworkToSpeaker(command.Command): + _description = _("Add a network to a BGP speaker") + + def get_parser(self, prog_name): + parser = super(AddNetworkToSpeaker, self).get_parser(prog_name) + parser.add_argument( + 'bgp_speaker', + metavar='', + help=_("BGP speaker (name or ID)")) + parser.add_argument( + 'network', + metavar='', + help=_("Network to add (name or ID)")) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + speaker_id = client.find_resource(constants.BGP_SPEAKER, + parsed_args.bgp_speaker)['id'] + net_id = client.find_resource('network', + parsed_args.network)['id'] + client.add_network_to_bgp_speaker(speaker_id, {'network_id': net_id}) + + +class AddPeerToSpeaker(command.Command): + _description = _("Add a peer to a BGP speaker") + + def get_parser(self, prog_name): + parser = super(AddPeerToSpeaker, self).get_parser(prog_name) + parser.add_argument( + 'bgp_speaker', + metavar='', + help=_("BGP speaker (name or ID)")) + parser.add_argument( + 'bgp_peer', + metavar='', + help=_("BGP Peer to add (name or ID)")) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + speaker_id = client.find_resource(constants.BGP_SPEAKER, + parsed_args.bgp_speaker)['id'] + peer_id = client.find_resource(constants.BGP_PEER, + parsed_args.bgp_peer)['id'] + client.add_peer_to_bgp_speaker(speaker_id, {'bgp_peer_id': peer_id}) + + +class CreateBgpSpeaker(command.ShowOne): + _description = _("Create a BGP speaker") + + def get_parser(self, prog_name): + parser = super(CreateBgpSpeaker, self).get_parser(prog_name) + parser.add_argument( + 'name', + metavar='', + help=_("Name of the BGP speaker to create")) + parser.add_argument( + '--local-as', + metavar='', + required=True, + help=_("Local AS number. (Integer in [%(min_val)s, %(max_val)s] " + "is allowed.)") % {'min_val': constants.MIN_AS_NUM, + 'max_val': constants.MAX_AS_NUM}) + parser.add_argument( + '--ip-version', + type=int, choices=[4, 6], + default=4, + help=_("IP version for the BGP speaker (default is 4)")) + add_common_arguments(parser) + nc_osc_utils.add_project_owner_option_to_parser(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + attrs = _get_attrs(self.app.client_manager, parsed_args) + body = {} + body[constants.BGP_SPEAKER] = attrs + obj = client.create_bgp_speaker(body)[constants.BGP_SPEAKER] + columns, display_columns = nc_osc_utils.get_columns(obj) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteBgpSpeaker(command.Command): + _description = _("Delete a BGP speaker") + + def get_parser(self, prog_name): + parser = super(DeleteBgpSpeaker, self).get_parser(prog_name) + parser.add_argument( + 'bgp_speaker', + metavar="", + help=_("BGP speaker to delete (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + id = client.find_resource(constants.BGP_SPEAKER, + parsed_args.bgp_speaker)['id'] + client.delete_bgp_speaker(id) + + +class ListBgpSpeaker(command.Lister): + _description = _("List BGP speakers") + + def get_parser(self, prog_name): + parser = super(ListBgpSpeaker, self).get_parser(prog_name) + parser.add_argument( + '--agent', + metavar='', + help=_("List BGP speakers hosted by an agent (ID only)")) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + if parsed_args.agent is not None: + data = client.list_bgp_speaker_on_dragent(parsed_args.agent_id) + else: + data = client.list_bgp_speakers() + + headers = ('ID', 'Name', 'Local AS', 'IP Version') + columns = ('id', 'name', 'local_as', 'ip_version') + return (headers, (utils.get_dict_properties(s, columns) + for s in data[constants.BGP_SPEAKERS])) + + +class ListRoutesAdvertisedBySpeaker(command.Lister): + _description = _("List routes advertised") + + def get_parser(self, prog_name): + parser = super(ListRoutesAdvertisedBySpeaker, + self).get_parser(prog_name) + parser.add_argument( + 'bgp_speaker', + metavar='', + help=_("BGP speaker (name or ID)")) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + speaker_id = client.find_resource(constants.BGP_SPEAKER, + parsed_args.bgp_speaker)['id'] + data = client.list_route_advertised_from_bgp_speaker(speaker_id) + headers = ('ID', 'Destination', 'Nexthop') + columns = ('id', 'destination', 'next_hop') + return (headers, (utils.get_dict_properties(s, columns) + for s in data['advertised_routes'])) + + +class RemoveNetworkFromSpeaker(command.Command): + _description = _("Remove a network from a BGP speaker") + + def get_parser(self, prog_name): + parser = super(RemoveNetworkFromSpeaker, self).get_parser(prog_name) + parser.add_argument( + 'bgp_speaker', + metavar='', + help=_("BGP speaker (name or ID)")) + parser.add_argument( + 'network', + metavar='', + help=_("Network to remove (name or ID)")) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + speaker_id = client.find_resource(constants.BGP_SPEAKER, + parsed_args.bgp_speaker)['id'] + net_id = client.find_resource('network', + parsed_args.network)['id'] + client.remove_network_from_bgp_speaker(speaker_id, + {'network_id': net_id}) + + +class RemovePeerFromSpeaker(command.Command): + _description = _("Remove a peer from a BGP speaker") + + def get_parser(self, prog_name): + parser = super(RemovePeerFromSpeaker, self).get_parser(prog_name) + parser.add_argument( + 'bgp_speaker', + metavar='', + help=_("BGP speaker (name or ID)")) + parser.add_argument( + 'bgp_peer', + metavar='', + help=_("BGP Peer to remove (name or ID)")) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + speaker_id = client.find_resource(constants.BGP_SPEAKER, + parsed_args.bgp_speaker)['id'] + peer_id = client.find_resource(constants.BGP_PEER, + parsed_args.bgp_peer)['id'] + client.remove_peer_from_bgp_speaker(speaker_id, + {'bgp_peer_id': peer_id}) + + +class SetBgpSpeaker(command.Command): + _description = _("Set BGP speaker properties") + + resource = constants.BGP_SPEAKER + + def get_parser(self, prog_name): + parser = super(SetBgpSpeaker, self).get_parser(prog_name) + parser.add_argument( + 'bgp_speaker', + metavar="", + help=_("BGP speaker to update (name or ID)") + ) + parser.add_argument( + '--name', + help=_("Name of the BGP speaker to update")) + add_common_arguments(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + id = client.find_resource(constants.BGP_SPEAKER, + parsed_args.bgp_speaker)['id'] + attrs = _get_attrs(self.app.client_manager, parsed_args) + body = {} + body[constants.BGP_SPEAKER] = attrs + client.update_bgp_speaker(id, body) + + +class ShowBgpSpeaker(command.ShowOne): + _description = _("Show a BGP speaker") + + def get_parser(self, prog_name): + parser = super(ShowBgpSpeaker, self).get_parser(prog_name) + parser.add_argument( + 'bgp_speaker', + metavar="", + help=_("BGP speaker to display (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + id = client.find_resource(constants.BGP_SPEAKER, + parsed_args.bgp_speaker)['id'] + obj = client.show_bgp_speaker(id)[constants.BGP_SPEAKER] + columns, display_columns = nc_osc_utils.get_columns(obj) + data = utils.get_dict_properties(obj, columns) + return display_columns, data diff --git a/neutronclient/osc/v2/dynamic_routing/constants.py b/neutronclient/osc/v2/dynamic_routing/constants.py new file mode 100644 index 000000000..0dd16b143 --- /dev/null +++ b/neutronclient/osc/v2/dynamic_routing/constants.py @@ -0,0 +1,18 @@ +# 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. + +BGP_SPEAKERS = 'bgp_speakers' +BGP_SPEAKER = 'bgp_speaker' +BGP_PEERS = 'bgp_peers' +BGP_PEER = 'bgp_peer' +MIN_AS_NUM = 1 +MAX_AS_NUM = 65535 diff --git a/neutronclient/tests/unit/osc/v2/dynamic_routing/fakes.py b/neutronclient/tests/unit/osc/v2/dynamic_routing/fakes.py new file mode 100644 index 000000000..42acbeefd --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/dynamic_routing/fakes.py @@ -0,0 +1,96 @@ +# 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 copy +import uuid + +import mock + +from neutronclient.tests.unit.osc.v2 import fakes + + +class TestNeutronDynamicRoutingOSCV2(fakes.TestNeutronClientOSCV2): + def setUp(self): + super(TestNeutronDynamicRoutingOSCV2, self).setUp() + self.neutronclient.find_resource = mock.Mock( + side_effect=lambda resource, name_or_id, project_id=None, + cmd_resource=None, parent_id=None, fields=None: + {'id': name_or_id}) + + +class FakeBgpSpeaker(object): + """Fake one or more bgp speakers.""" + + @staticmethod + def create_one_bgp_speaker(attrs=None): + attrs = attrs or {} + # Set default attributes. + bgp_speaker_attrs = { + 'peers': [], + 'local_as': 200, + 'advertise_tenant_networks': True, + 'networks': [], + 'ip_version': 4, + 'advertise_floating_ip_host_routes': True, + 'id': uuid.uuid4().hex, + 'name': 'bgp-speaker-' + uuid.uuid4().hex, + 'tenant_id': uuid.uuid4().hex, + } + + # Overwrite default attributes. + bgp_speaker_attrs.update(attrs) + + return copy.deepcopy(bgp_speaker_attrs) + + @staticmethod + def create_bgp_speakers(attrs=None, count=1): + """Create multiple fake bgp speakers. + + """ + bgp_speakers = [] + for i in range(0, count): + bgp_speaker = FakeBgpSpeaker.create_one_bgp_speaker(attrs) + bgp_speakers.append(bgp_speaker) + + return {'bgp_speakers': bgp_speakers} + + +class FakeBgpPeer(object): + """Fake one or more bgp peers.""" + + @staticmethod + def create_one_bgp_peer(attrs=None): + attrs = attrs or {} + # Set default attributes. + bgp_peer_attrs = { + 'auth_type': None, + 'peer_ip': '1.1.1.1', + 'remote_as': 100, + 'id': uuid.uuid4().hex, + 'name': 'bgp-peer-' + uuid.uuid4().hex, + 'tenant_id': uuid.uuid4().hex, + } + + # Overwrite default attributes. + bgp_peer_attrs.update(attrs) + + return copy.deepcopy(bgp_peer_attrs) + + @staticmethod + def create_bgp_peers(attrs=None, count=1): + """Create one or multiple fake bgp peers.""" + bgp_peers = [] + for i in range(0, count): + bgp_peer = FakeBgpPeer.create_one_bgp_peer(attrs) + bgp_peers.append(bgp_peer) + + return {'bgp_peers': bgp_peers} diff --git a/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_peer.py b/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_peer.py new file mode 100644 index 000000000..ffe69f18d --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_peer.py @@ -0,0 +1,153 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +import mock + +from neutronclient.osc.v2.dynamic_routing import bgp_peer +from neutronclient.tests.unit.osc.v2.dynamic_routing import fakes + + +class TestListBgpPeer(fakes.TestNeutronDynamicRoutingOSCV2): + _bgp_peers = fakes.FakeBgpPeer.create_bgp_peers(count=1) + columns = ('ID', 'Name', 'Peer IP', 'Remote AS') + data = [] + for _bgp_peer in _bgp_peers['bgp_peers']: + data.append(( + _bgp_peer['id'], + _bgp_peer['name'], + _bgp_peer['peer_ip'], + _bgp_peer['remote_as'])) + + def setUp(self): + super(TestListBgpPeer, self).setUp() + + self.neutronclient.list_bgp_peers = mock.Mock( + return_value=self._bgp_peers + ) + + # Get the command object to test + self.cmd = bgp_peer.ListBgpPeer(self.app, self.namespace) + + def test_bgp_peer_list(self): + parsed_args = self.check_parser(self.cmd, [], []) + + columns, data = self.cmd.take_action(parsed_args) + self.neutronclient.list_bgp_peers.assert_called_once_with() + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + +class TestDeleteBgpPeer(fakes.TestNeutronDynamicRoutingOSCV2): + + _bgp_peer = fakes.FakeBgpPeer.create_one_bgp_peer() + + def setUp(self): + super(TestDeleteBgpPeer, self).setUp() + + self.neutronclient.delete_bgp_peer = mock.Mock(return_value=None) + + self.cmd = bgp_peer.DeleteBgpPeer(self.app, self.namespace) + + def test_delete_bgp_peer(self): + arglist = [ + self._bgp_peer['name'], + ] + verifylist = [ + ('bgp_peer', self._bgp_peer['name']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.neutronclient.delete_bgp_peer.assert_called_once_with( + self._bgp_peer['name']) + self.assertIsNone(result) + + +class TestShowBgpPeer(fakes.TestNeutronDynamicRoutingOSCV2): + _one_bgp_peer = fakes.FakeBgpPeer.create_one_bgp_peer() + data = ( + _one_bgp_peer['auth_type'], + _one_bgp_peer['id'], + _one_bgp_peer['name'], + _one_bgp_peer['peer_ip'], + _one_bgp_peer['remote_as'], + _one_bgp_peer['tenant_id'] + ) + _bgp_peer = {'bgp_peer': _one_bgp_peer} + _bgp_peer_name = _one_bgp_peer['name'] + columns = ( + 'auth_type', + 'id', + 'name', + 'peer_ip', + 'remote_as', + 'tenant_id' + ) + + def setUp(self): + super(TestShowBgpPeer, self).setUp() + + self.neutronclient.show_bgp_peer = mock.Mock( + return_value=self._bgp_peer + ) + bgp_peer.get_bgp_peer_id = mock.Mock(return_value=self._bgp_peer_name) + # Get the command object to test + self.cmd = bgp_peer.ShowBgpPeer(self.app, self.namespace) + + def test_bgp_peer_list(self): + arglist = [ + self._bgp_peer_name, + ] + verifylist = [ + ('bgp_peer', self._bgp_peer_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + data = self.cmd.take_action(parsed_args) + self.neutronclient.show_bgp_peer.assert_called_once_with( + self._bgp_peer_name) + self.assertEqual(self.columns, data[0]) + self.assertEqual(self.data, data[1]) + + +class TestSetBgpPeer(fakes.TestNeutronDynamicRoutingOSCV2): + _one_bgp_peer = fakes.FakeBgpPeer.create_one_bgp_peer() + _bgp_peer_name = _one_bgp_peer['name'] + + def setUp(self): + super(TestSetBgpPeer, self).setUp() + self.neutronclient.update_bgp_peer = mock.Mock(return_value=None) + bgp_peer.get_bgp_peer_id = mock.Mock(return_value=self._bgp_peer_name) + + self.cmd = bgp_peer.SetBgpPeer(self.app, self.namespace) + + def test_set_bgp_peer(self): + arglist = [ + self._bgp_peer_name, + '--name', 'noob', + ] + verifylist = [ + ('bgp_peer', self._bgp_peer_name), + ('name', 'noob'), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = {'bgp_peer': { + 'name': 'noob', + 'password': None} + } + self.neutronclient.update_bgp_peer.assert_called_once_with( + self._bgp_peer_name, attrs) + self.assertIsNone(result) diff --git a/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_speaker.py b/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_speaker.py new file mode 100644 index 000000000..dbecf057c --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_speaker.py @@ -0,0 +1,157 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +import mock + +from neutronclient.osc.v2.dynamic_routing import bgp_speaker +from neutronclient.tests.unit.osc.v2.dynamic_routing import fakes + + +class TestListBgpSpeaker(fakes.TestNeutronDynamicRoutingOSCV2): + _bgp_speakers = fakes.FakeBgpSpeaker.create_bgp_speakers() + columns = ('ID', 'Name', 'Local AS', 'IP Version') + data = [] + for _bgp_speaker in _bgp_speakers['bgp_speakers']: + data.append(( + _bgp_speaker['id'], + _bgp_speaker['name'], + _bgp_speaker['local_as'], + _bgp_speaker['ip_version'])) + + def setUp(self): + super(TestListBgpSpeaker, self).setUp() + + self.neutronclient.list_bgp_speakers = mock.Mock( + return_value=self._bgp_speakers + ) + + # Get the command object to test + self.cmd = bgp_speaker.ListBgpSpeaker(self.app, self.namespace) + + def test_bgp_speaker_list(self): + parsed_args = self.check_parser(self.cmd, [], []) + + columns, data = self.cmd.take_action(parsed_args) + self.neutronclient.list_bgp_speakers.assert_called_once_with() + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + +class TestDeleteBgpSpeaker(fakes.TestNeutronDynamicRoutingOSCV2): + + _bgp_speaker = fakes.FakeBgpSpeaker.create_one_bgp_speaker() + + def setUp(self): + super(TestDeleteBgpSpeaker, self).setUp() + + self.neutronclient.delete_bgp_speaker = mock.Mock(return_value=None) + + self.cmd = bgp_speaker.DeleteBgpSpeaker(self.app, self.namespace) + + def test_delete_bgp_speaker(self): + arglist = [ + self._bgp_speaker['name'], + ] + verifylist = [ + ('bgp_speaker', self._bgp_speaker['name']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.neutronclient.delete_bgp_speaker.assert_called_once_with( + self._bgp_speaker['name']) + self.assertIsNone(result) + + +class TestShowBgpSpeaker(fakes.TestNeutronDynamicRoutingOSCV2): + _one_bgp_speaker = fakes.FakeBgpSpeaker.create_one_bgp_speaker() + data = ( + _one_bgp_speaker['advertise_floating_ip_host_routes'], + _one_bgp_speaker['advertise_tenant_networks'], + _one_bgp_speaker['id'], + _one_bgp_speaker['ip_version'], + _one_bgp_speaker['local_as'], + _one_bgp_speaker['name'], + _one_bgp_speaker['networks'], + _one_bgp_speaker['peers'], + _one_bgp_speaker['tenant_id'] + ) + _bgp_speaker = {'bgp_speaker': _one_bgp_speaker} + _bgp_speaker_name = _one_bgp_speaker['name'] + columns = ( + 'advertise_floating_ip_host_routes', + 'advertise_tenant_networks', + 'id', + 'ip_version', + 'local_as', + 'name', + 'networks', + 'peers', + 'tenant_id' + ) + + def setUp(self): + super(TestShowBgpSpeaker, self).setUp() + + self.neutronclient.show_bgp_speaker = mock.Mock( + return_value=self._bgp_speaker + ) + # Get the command object to test + self.cmd = bgp_speaker.ShowBgpSpeaker(self.app, self.namespace) + + def test_bgp_speaker_show(self): + arglist = [ + self._bgp_speaker_name, + ] + verifylist = [ + ('bgp_speaker', self._bgp_speaker_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + data = self.cmd.take_action(parsed_args) + self.neutronclient.show_bgp_speaker.assert_called_once_with( + self._bgp_speaker_name) + self.assertEqual(self.columns, data[0]) + self.assertEqual(self.data, data[1]) + + +class TestSetBgpSpeaker(fakes.TestNeutronDynamicRoutingOSCV2): + _one_bgp_speaker = fakes.FakeBgpSpeaker.create_one_bgp_speaker() + _bgp_speaker_name = _one_bgp_speaker['name'] + + def setUp(self): + super(TestSetBgpSpeaker, self).setUp() + self.neutronclient.update_bgp_speaker = mock.Mock( + return_value=None) + + self.cmd = bgp_speaker.SetBgpSpeaker(self.app, self.namespace) + + def test_set_bgp_speaker(self): + arglist = [ + self._bgp_speaker_name, + '--name', 'noob', + ] + verifylist = [ + ('bgp_speaker', self._bgp_speaker_name), + ('name', 'noob'), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = {'bgp_speaker': { + 'name': 'noob'} + } + self.neutronclient.update_bgp_speaker.assert_called_once_with( + self._bgp_speaker_name, attrs) + self.assertIsNone(result) diff --git a/releasenotes/notes/add-osc-dynamic-routing-support-11130b2f440c0ac2.yaml b/releasenotes/notes/add-osc-dynamic-routing-support-11130b2f440c0ac2.yaml new file mode 100644 index 000000000..58cd1ae30 --- /dev/null +++ b/releasenotes/notes/add-osc-dynamic-routing-support-11130b2f440c0ac2.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Add OSC plugin to support "Neutron Dynamic Routing" diff --git a/setup.cfg b/setup.cfg index 60bd86021..8999c679b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -65,6 +65,25 @@ openstack.neutronclient.v2 = sfc_port_pair_group_show = neutronclient.osc.v2.sfc.sfc_port_pair_group:ShowSfcPortPairGroup sfc_port_pair_group_unset = neutronclient.osc.v2.sfc.sfc_port_pair_group:UnsetSfcPortPairGroup + bgp_dragent_add_speaker = neutronclient.osc.v2.dynamic_routing.bgp_dragent:AddBgpSpeakerToDRAgent + bgp_dragent_remove_speaker = neutronclient.osc.v2.dynamic_routing.bgp_dragent:RemoveBgpSpeakerFromDRAgent + bgp_peer_create = neutronclient.osc.v2.dynamic_routing.bgp_peer:CreateBgpPeer + bgp_peer_delete = neutronclient.osc.v2.dynamic_routing.bgp_peer:DeleteBgpPeer + bgp_peer_list = neutronclient.osc.v2.dynamic_routing.bgp_peer:ListBgpPeer + bgp_peer_show = neutronclient.osc.v2.dynamic_routing.bgp_peer:ShowBgpPeer + bgp_peer_set = neutronclient.osc.v2.dynamic_routing.bgp_peer:SetBgpPeer + bgp_speaker_list_advertised_routes = neutronclient.osc.v2.dynamic_routing.bgp_speaker:ListRoutesAdvertisedBySpeaker + bgp_speaker_create = neutronclient.osc.v2.dynamic_routing.bgp_speaker:CreateBgpSpeaker + bgp_speaker_delete = neutronclient.osc.v2.dynamic_routing.bgp_speaker:DeleteBgpSpeaker + bgp_speaker_list = neutronclient.osc.v2.dynamic_routing.bgp_speaker:ListBgpSpeaker + bgp_speaker_add_network = neutronclient.osc.v2.dynamic_routing.bgp_speaker:AddNetworkToSpeaker + bgp_speaker_remove_network = neutronclient.osc.v2.dynamic_routing.bgp_speaker:RemoveNetworkFromSpeaker + bgp_speaker_add_peer = neutronclient.osc.v2.dynamic_routing.bgp_speaker:AddPeerToSpeaker + bgp_speaker_remove_peer = neutronclient.osc.v2.dynamic_routing.bgp_speaker:RemovePeerFromSpeaker + bgp_speaker_set = neutronclient.osc.v2.dynamic_routing.bgp_speaker:SetBgpSpeaker + bgp_speaker_show = neutronclient.osc.v2.dynamic_routing.bgp_speaker:ShowBgpSpeaker + bgp_speaker_show_dragents = neutronclient.osc.v2.dynamic_routing.bgp_dragent:ListDRAgentsHostingBgpSpeaker + firewall_group_create = neutronclient.osc.v2.fwaas.firewallgroup:CreateFirewallGroup firewall_group_delete = neutronclient.osc.v2.fwaas.firewallgroup:DeleteFirewallGroup firewall_group_list = neutronclient.osc.v2.fwaas.firewallgroup:ListFirewallGroup From e4b65ef29c1602a5a897a8e0cc05dff147defde3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daire=20N=C3=AD=20Chath=C3=A1in?= Date: Tue, 20 Jun 2017 15:34:28 +0000 Subject: [PATCH 623/845] Add Service Graphs networking-sfc resource Co-Authored-By: Igor Duarte Cardoso Partial-Bug: #1587486 Depends-On: I372da15f99f3cbfb7ffd1d8bf87a79bc56180afe Change-Id: Ie54da56d2388cb375bccd883c111c5f87e293047 --- doc/source/cli/osc/v2/networking-sfc.rst | 3 + neutronclient/osc/v2/sfc/sfc_service_graph.py | 247 +++++++++++++ neutronclient/tests/unit/osc/v2/sfc/fakes.py | 45 +++ .../unit/osc/v2/sfc/test_service_graph.py | 336 ++++++++++++++++++ neutronclient/v2_0/client.py | 26 ++ .../add-service-graph-ce4a25b3e32d70a6.yaml | 5 + setup.cfg | 5 + 7 files changed, 667 insertions(+) create mode 100644 neutronclient/osc/v2/sfc/sfc_service_graph.py create mode 100644 neutronclient/tests/unit/osc/v2/sfc/test_service_graph.py create mode 100644 releasenotes/notes/add-service-graph-ce4a25b3e32d70a6.yaml diff --git a/doc/source/cli/osc/v2/networking-sfc.rst b/doc/source/cli/osc/v2/networking-sfc.rst index 09d13d6b6..4f4242d74 100644 --- a/doc/source/cli/osc/v2/networking-sfc.rst +++ b/doc/source/cli/osc/v2/networking-sfc.rst @@ -34,3 +34,6 @@ Network v2 .. autoprogram-cliff:: openstack.neutronclient.v2 :command: sfc port pair group * + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: sfc service graph * diff --git a/neutronclient/osc/v2/sfc/sfc_service_graph.py b/neutronclient/osc/v2/sfc/sfc_service_graph.py new file mode 100644 index 000000000..53edff8f9 --- /dev/null +++ b/neutronclient/osc/v2/sfc/sfc_service_graph.py @@ -0,0 +1,247 @@ +# Copyright 2017 Intel Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import logging + +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils + +from neutronclient._i18n import _ +from neutronclient.osc import utils as nc_osc_utils + +LOG = logging.getLogger(__name__) + +resource = 'service_graph' + +_attr_map = ( + ('id', 'ID', nc_osc_utils.LIST_BOTH), + ('name', 'Name', nc_osc_utils.LIST_BOTH), + ('port_chains', 'Branching Points', nc_osc_utils.LIST_BOTH), + ('description', 'Description', nc_osc_utils.LIST_LONG_ONLY), + ('project_id', 'Project', nc_osc_utils.LIST_LONG_ONLY), +) + + +class CreateSfcServiceGraph(command.ShowOne): + """Create a service graph.""" + def get_parser(self, prog_name): + parser = super(CreateSfcServiceGraph, self).get_parser(prog_name) + parser.add_argument( + 'name', + metavar='', + help=_('Name of the service graph.')) + parser.add_argument( + '--description', + help=_('Description for the service graph.')) + parser.add_argument( + '--branching-point', + metavar='SRC_CHAIN:DST_CHAIN_1,DST_CHAIN_2,DST_CHAIN_N', + dest='branching_points', + action='append', + default=[], required=True, + help=_('Service graph branching point: the key is the source ' + 'Port Chain while the value is a list of destination ' + 'Port Chains. This option can be repeated.')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + attrs = _get_common_attrs(self.app.client_manager, parsed_args) + try: + body = {resource: attrs} + obj = client.create_sfc_service_graph(body)[resource] + columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + except Exception as e: + msg = (_("Failed to create service graph using '%(pcs)s': %(e)s") + % {'pcs': parsed_args.branching_points, 'e': e}) + raise exceptions.CommandError(msg) + + +class SetSfcServiceGraph(command.Command): + _description = _("Set service graph properties") + + def get_parser(self, prog_name): + parser = super(SetSfcServiceGraph, self).get_parser(prog_name) + parser.add_argument( + '--name', + metavar='', + help=_('Name of the service graph')) + parser.add_argument( + '--description', + metavar='', + help=_('Description for the service graph')) + parser.add_argument( + 'service_graph', + metavar='', + help=_("Service graph to modify (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + service_graph_id = _get_id(client, parsed_args.service_graph, resource) + attrs = _get_common_attrs(self.app.client_manager, parsed_args, + is_create=False) + body = {resource: attrs} + try: + client.update_sfc_service_graph(service_graph_id, body) + except Exception as e: + msg = (_("Failed to update service graph " + "'%(service_graph)s': %(e)s") + % {'service_graph': parsed_args.service_graph, 'e': e}) + raise exceptions.CommandError(msg) + + +class DeleteSfcServiceGraph(command.Command): + """Delete a given service graph.""" + + def get_parser(self, prog_name): + parser = super(DeleteSfcServiceGraph, self).get_parser(prog_name) + parser.add_argument( + 'service_graph', + metavar="", + help=_("ID or name of the service graph to delete.") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + id = _get_id(client, parsed_args.service_graph, resource) + client.delete_sfc_service_graph(id) + + +class ListSfcServiceGraph(command.Lister): + _description = _("List service graphs") + + def get_parser(self, prog_name): + parser = super(ListSfcServiceGraph, self).get_parser(prog_name) + parser.add_argument( + '--long', + action='store_true', + default=False, + help=_("List additional fields in output") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + data = client.list_sfc_service_graphs() + headers, columns = nc_osc_utils.get_column_definitions( + _attr_map, long_listing=parsed_args.long) + return (headers, + (utils.get_dict_properties(s, columns) + for s in data['service_graphs'])) + + +class ShowSfcServiceGraph(command.ShowOne): + """Show information of a given service graph.""" + + def get_parser(self, prog_name): + parser = super(ShowSfcServiceGraph, self).get_parser(prog_name) + parser.add_argument( + 'service_graph', + metavar="", + help=_("ID or name of the service graph to display.") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + sg_id = _get_id(client, parsed_args.service_graph, resource) + obj = client.show_sfc_service_graph(sg_id)[resource] + columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + + +def _get_common_attrs(client_manager, parsed_args, is_create=True): + attrs = {} + if parsed_args.name is not None: + attrs['name'] = str(parsed_args.name) + if parsed_args.description is not None: + attrs['description'] = str(parsed_args.description) + if is_create: + _get_attrs_for_create(client_manager, attrs, parsed_args) + return attrs + + +def _validate_destination_chains(comma_split, attrs, client_manager, sc_): + for e in comma_split: + if e != "": + dc_ = _get_id(client_manager.neutronclient, e, 'port_chain') + attrs['port_chains'][sc_].append(dc_) + if _check_cycle(attrs['port_chains'], sc_, dc_): + raise(exceptions.CommandError( + "Error: Service graph contains a cycle")) + else: + raise exceptions.CommandError( + "Error: you must specify at least one " + "destination chain for each source chain") + return attrs + + +def _check_cycle(graph, new_src, new_dest): + for src in graph: + if src == new_dest: + if _visit(graph, src, new_dest, new_src): + return True + return False + + +def _visit(graph, src, new_dest, new_src): + if src in graph: + found_cycle = False + for dest in graph[src]: + if new_src == dest or found_cycle: + return True + else: + found_cycle = _visit(graph, dest, new_dest, new_src) + return False + + +def _get_attrs_for_create(client_manager, attrs, parsed_args): + if parsed_args.branching_points: + attrs['port_chains'] = {} + src_chain = None + for c in parsed_args.branching_points: + if ':' not in c: + raise exceptions.CommandError( + "Error: You must specify at least one " + "destination chain for each source chain.") + colon_split = c.split(':') + src_chain = colon_split.pop(0) + sc_ = _get_id(client_manager.neutronclient, + src_chain, 'port_chain') + for i in colon_split: + comma_split = i.split(',') + unique = set(comma_split) + if len(unique) != len(comma_split): + raise exceptions.CommandError( + "Error: Duplicate " + "destination chains from " + "source chain {}".format(src_chain)) + if sc_ in attrs['port_chains']: + raise exceptions.CommandError( + "Error: Source chain {} is in " + "use already ".format(src_chain)) + attrs['port_chains'][sc_] = [] + _validate_destination_chains( + comma_split, attrs, client_manager, sc_) + + +def _get_id(client, id_or_name, resource): + return client.find_resource(resource, id_or_name)['id'] diff --git a/neutronclient/tests/unit/osc/v2/sfc/fakes.py b/neutronclient/tests/unit/osc/v2/sfc/fakes.py index 54bd5a8ee..5ae114482 100755 --- a/neutronclient/tests/unit/osc/v2/sfc/fakes.py +++ b/neutronclient/tests/unit/osc/v2/sfc/fakes.py @@ -232,3 +232,48 @@ def create_port_chains(attrs=None, count=1): for _ in range(count): port_chains.append(FakeSfcPortChain.create_port_chain(attrs)) return port_chains + + +class FakeSfcServiceGraph(object): + """Fake service graph attributes.""" + + @staticmethod + def create_sfc_service_graph(attrs=None): + """Create a fake service graph. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A Dictionary with faking service graph attributes + """ + attrs = attrs or {} + + # Set default attributes. + service_graph_attrs = { + 'id': uuidutils.generate_uuid(), + 'name': 'port-pair-group-name', + 'description': 'description', + 'port_chains': {uuidutils.generate_uuid(): [ + uuidutils.generate_uuid()]}, + 'project_id': uuidutils.generate_uuid(), + } + + service_graph_attrs.update(attrs) + return copy.deepcopy(service_graph_attrs) + + @staticmethod + def create_sfc_service_graphs(attrs=None, count=1): + """Create multiple service graphs. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of service graphs to fake + :return: + A list of dictionaries faking the service graphs. + """ + service_graphs = [] + for _ in range(count): + service_graphs.append( + FakeSfcServiceGraph.create_sfc_service_graph(attrs)) + return service_graphs diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_service_graph.py b/neutronclient/tests/unit/osc/v2/sfc/test_service_graph.py new file mode 100644 index 000000000..da12aa570 --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/sfc/test_service_graph.py @@ -0,0 +1,336 @@ +# Copyright 2017 Intel Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import mock +from osc_lib import exceptions +from osc_lib.tests import utils as tests_utils + +from neutronclient.osc.v2.sfc import sfc_service_graph +from neutronclient.tests.unit.osc.v2.sfc import fakes + + +def _get_id(client, id_or_name, resource): + return id_or_name + + +class TestListSfcServiceGraph(fakes.TestNeutronClientOSCV2): + _service_graphs = fakes.FakeSfcServiceGraph.create_sfc_service_graphs( + count=1) + columns = ('ID', 'Name', 'Branching Points') + columns_long = ('ID', 'Name', 'Branching Points', 'Description', 'Project') + _service_graph = _service_graphs[0] + data = [ + _service_graph['id'], + _service_graph['name'], + _service_graph['port_chains'] + ] + data_long = [ + _service_graph['id'], + _service_graph['name'], + _service_graph['port_chains'], + _service_graph['description'], + _service_graph['project_id'] + ] + _service_graph1 = {'service_graphs': _service_graph} + _service_graph_id = _service_graph['id'] + + def setUp(self): + super(TestListSfcServiceGraph, self).setUp() + mock.patch( + 'neutronclient.osc.v2.sfc.sfc_service_graph._get_id', + new=_get_id).start() + self.neutronclient.list_sfc_service_graphs = mock.Mock( + return_value={'service_graphs': self._service_graphs} + ) + # Get the command object to test + self.cmd = sfc_service_graph.ListSfcServiceGraph( + self.app, self.namespace) + + def test_list_sfc_service_graphs(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns = self.cmd.take_action(parsed_args)[0] + sgs = self.neutronclient.list_sfc_service_graphs()['service_graphs'] + sg = sgs[0] + data = [ + sg['id'], + sg['name'], + sg['port_chains'] + ] + self.assertEqual(list(self.columns), columns) + self.assertEqual(self.data, data) + + def test_list_sfc_service_graphs_with_long_option(self): + arglist = ['--long'] + verifylist = [('long', True)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns = self.cmd.take_action(parsed_args)[0] + sgs = self.neutronclient.list_sfc_service_graphs()['service_graphs'] + sg = sgs[0] + data = [ + sg['id'], + sg['name'], + sg['port_chains'], + sg['description'], + sg['project_id'] + ] + self.assertEqual(list(self.columns_long), columns) + self.assertEqual(self.data_long, data) + + +class TestCreateSfcServiceGraph(fakes.TestNeutronClientOSCV2): + _service_graph = fakes.FakeSfcServiceGraph.create_sfc_service_graph() + + columns = ('ID', 'Name', 'Branching Points') + columns_long = ('Branching Points', 'Description', 'ID', 'Name', 'Project') + + def get_data(self): + return ( + self._service_graph['port_chains'], + self._service_graph['description'], + self._service_graph['id'], + self._service_graph['name'], + self._service_graph['project_id'], + ) + + def setUp(self): + super(TestCreateSfcServiceGraph, self).setUp() + mock.patch('neutronclient.osc.v2.sfc.sfc_service_graph._get_id', + new=_get_id).start() + self.neutronclient.create_sfc_service_graph = mock.Mock( + return_value={'service_graph': self._service_graph}) + self.data = self.get_data() + self.cmd = sfc_service_graph.CreateSfcServiceGraph( + self.app, self.namespace) + + def test_create_sfc_service_graph(self): + arglist = [] + verifylist = [] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_create_sfc_service_graph_without_loop(self): + bp1_str = 'pc1:pc2,pc3' + bp2_str = 'pc2:pc4' + self.cmd = sfc_service_graph.CreateSfcServiceGraph( + self.app, self.namespace) + + arglist = [ + "--description", self._service_graph['description'], + "--branching-point", bp1_str, + "--branching-point", bp2_str, + self._service_graph['name']] + + pcs = {'pc1': ['pc2', 'pc3'], 'pc2': ['pc4']} + + verifylist = [ + ("description", self._service_graph['description']), + ("branching_points", [bp1_str, bp2_str]), + ("name", self._service_graph['name']) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = (self.cmd.take_action(parsed_args)) + + self.neutronclient.create_sfc_service_graph.assert_called_once_with({ + 'service_graph': { + 'description': self._service_graph['description'], + 'name': self._service_graph['name'], + 'port_chains': pcs + } + }) + self.assertEqual(self.columns_long, columns) + self.assertEqual(self.data, data) + + def test_create_sfc_service_graph_with_loop(self): + bp1_str = 'pc1:pc2,pc3;' + bp2_str = 'pc2:pc1' + self.cmd = sfc_service_graph.CreateSfcServiceGraph( + self.app, self.namespace) + + arglist = [ + "--description", self._service_graph['description'], + "--branching-point", bp1_str, + "--branching-point", bp2_str, + self._service_graph['name']] + + verifylist = [ + ("description", self._service_graph['description']), + ("branching_points", [bp1_str, bp2_str]), + ("name", self._service_graph['name']) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args) + + def test_create_sfc_service_graph_invalid_port_chains(self): + bp1_str = 'pc1:pc2,pc3:' + self.cmd = sfc_service_graph.CreateSfcServiceGraph( + self.app, self.namespace) + + arglist = [ + "--description", self._service_graph['description'], + "--branching-point", bp1_str, + self._service_graph['name']] + + verifylist = [ + ("description", self._service_graph['description']), + ("branching_points", [bp1_str]), + ("name", self._service_graph['name']) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args) + + def test_create_sfc_service_graph_duplicate_src_chains(self): + bp1_str = 'pc1:pc2,pc3;' + bp2_str = 'pc1:pc4' + self.cmd = sfc_service_graph.CreateSfcServiceGraph( + self.app, self.namespace) + + arglist = [ + "--description", self._service_graph['description'], + "--branching-point", bp1_str, + "--branching-point", bp2_str, + self._service_graph['name']] + + verifylist = [ + ("description", self._service_graph['description']), + ("branching_points", [bp1_str, bp2_str]), + ("name", self._service_graph['name']) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args) + + +class TestDeleteSfcServiceGraph(fakes.TestNeutronClientOSCV2): + + _service_graph = fakes.FakeSfcServiceGraph.create_sfc_service_graphs( + count=1) + + def setUp(self): + super(TestDeleteSfcServiceGraph, self).setUp() + self.neutronclient.delete_sfc_service_graph = mock.Mock( + return_value=None) + self.cmd = sfc_service_graph.DeleteSfcServiceGraph( + self.app, self.namespace) + + def test_delete_sfc_service_graph(self): + client = self.app.client_manager.neutronclient + mock_service_graph_delete = client.delete_sfc_service_graph + arglist = [ + self._service_graph[0]['id'], + ] + verifylist = [ + ('service_graph', self._service_graph[0]['id']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + mock_service_graph_delete.assert_called_once_with( + self._service_graph[0]['id']) + self.assertIsNone(result) + + +class TestShowSfcServiceGraph(fakes.TestNeutronClientOSCV2): + + _sg = fakes.FakeSfcServiceGraph.create_sfc_service_graph() + columns = ('ID', 'Name', 'Branching Points') + columns_long = ('Branching Points', 'Description', 'ID', 'Name', 'Project') + data = ( + _sg['id'], + _sg['name'], + _sg['port_chains'] + ) + data_long = ( + _sg['port_chains'], + _sg['description'], + _sg['id'], + _sg['name'], + _sg['project_id'] + ) + + _service_graph = {'service_graph': _sg} + _service_graph_id = _sg['id'] + + def setUp(self): + super(TestShowSfcServiceGraph, self).setUp() + mock.patch( + 'neutronclient.osc.v2.sfc.sfc_service_graph._get_id', + new=_get_id).start() + self.neutronclient.show_sfc_service_graph = mock.Mock( + return_value=self._service_graph + ) + # Get the command object to test + self.cmd = sfc_service_graph.ShowSfcServiceGraph( + self.app, self.namespace) + + def test_service_graph_show(self): + client = self.app.client_manager.neutronclient + mock_service_graph_show = client.show_sfc_service_graph + arglist = [ + self._service_graph_id, + ] + verifylist = [ + ('service_graph', self._service_graph_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + mock_service_graph_show.assert_called_once_with(self._service_graph_id) + self.assertEqual(self.columns_long, columns) + self.assertEqual(self.data_long, data) + + +class TestSetSfcServiceGraph(fakes.TestNeutronClientOSCV2): + _service_graph = fakes.FakeSfcServiceGraph.create_sfc_service_graph() + _service_graph_name = _service_graph['name'] + _service_graph_id = _service_graph['id'] + + def setUp(self): + super(TestSetSfcServiceGraph, self).setUp() + mock.patch('neutronclient.osc.v2.sfc.sfc_service_graph._get_id', + new=_get_id).start() + self.neutronclient.update_sfc_service_graph = mock.Mock( + return_value=None) + self.cmd = sfc_service_graph.SetSfcServiceGraph( + self.app, self.namespace) + + def test_set_service_graph(self): + client = self.app.client_manager.neutronclient + mock_service_graph_update = client.update_sfc_service_graph + arglist = [ + self._service_graph_name, + '--name', 'name_updated', + '--description', 'desc_updated' + ] + verifylist = [ + ('service_graph', self._service_graph_name), + ('name', 'name_updated'), + ('description', 'desc_updated'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + attrs = {'service_graph': { + 'name': 'name_updated', + 'description': 'desc_updated'} + } + mock_service_graph_update.assert_called_once_with( + self._service_graph_name, attrs) + self.assertIsNone(result) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index f4e49f895..23847058e 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -518,6 +518,8 @@ class Client(ClientBase): sfc_port_pair_group_path = "/sfc/port_pair_groups/%s" sfc_port_chains_path = "/sfc/port_chains" sfc_port_chain_path = "/sfc/port_chains/%s" + sfc_service_graphs_path = "/sfc/service_graphs" + sfc_service_graph_path = "/sfc/service_graphs/%s" endpoint_groups_path = "/vpn/endpoint-groups" endpoint_group_path = "/vpn/endpoint-groups/%s" @@ -704,6 +706,7 @@ class Client(ClientBase): 'port_pairs': 'port_pair', 'port_pair_groups': 'port_pair_group', 'port_chains': 'port_chain', + 'service_graphs': 'service_graph', } def list_ext(self, collection, path, retrieve_all, **_params): @@ -2260,6 +2263,29 @@ def show_sfc_flow_classifier(self, flow_classifier, **_params): return self.get(self.sfc_flow_classifier_path % (flow_classifier), params=_params) + def create_sfc_service_graph(self, body=None): + """Create the specified Service Graph.""" + return self.post(self.sfc_service_graphs_path, body=body) + + def update_sfc_service_graph(self, service_graph, body=None): + """Update a Service Graph.""" + return self.put(self.sfc_service_graph_path % service_graph, + body=body) + + def delete_sfc_service_graph(self, service_graph): + """Deletes the specified Service Graph.""" + return self.delete(self.sfc_service_graph_path % service_graph) + + def list_sfc_service_graphs(self, retrieve_all=True, **_params): + """Fetches a list of all Service Graphs.""" + return self.list('service_graphs', self.sfc_service_graphs_path, + retrieve_all, **_params) + + def show_sfc_service_graph(self, service_graph, **_params): + """Fetches information of a certain Service Graph.""" + return self.get(self.sfc_service_graph_path % service_graph, + params=_params) + def __init__(self, **kwargs): """Initialize a new client for the Neutron v2.0 API.""" super(Client, self).__init__(**kwargs) diff --git a/releasenotes/notes/add-service-graph-ce4a25b3e32d70a6.yaml b/releasenotes/notes/add-service-graph-ce4a25b3e32d70a6.yaml new file mode 100644 index 000000000..f0b52abe7 --- /dev/null +++ b/releasenotes/notes/add-service-graph-ce4a25b3e32d70a6.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Added support for SFC Service Graph resource. + Related RFE: https://bugs.launchpad.net/networking-sfc/+bug/1587486. diff --git a/setup.cfg b/setup.cfg index 60bd86021..adfe8a1b7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -64,6 +64,11 @@ openstack.neutronclient.v2 = sfc_port_pair_group_set = neutronclient.osc.v2.sfc.sfc_port_pair_group:SetSfcPortPairGroup sfc_port_pair_group_show = neutronclient.osc.v2.sfc.sfc_port_pair_group:ShowSfcPortPairGroup sfc_port_pair_group_unset = neutronclient.osc.v2.sfc.sfc_port_pair_group:UnsetSfcPortPairGroup + sfc_service_graph_create = neutronclient.osc.v2.sfc.sfc_service_graph:CreateSfcServiceGraph + sfc_service_graph_delete = neutronclient.osc.v2.sfc.sfc_service_graph:DeleteSfcServiceGraph + sfc_service_graph_set = neutronclient.osc.v2.sfc.sfc_service_graph:SetSfcServiceGraph + sfc_service_graph_list = neutronclient.osc.v2.sfc.sfc_service_graph:ListSfcServiceGraph + sfc_service_graph_show = neutronclient.osc.v2.sfc.sfc_service_graph:ShowSfcServiceGraph firewall_group_create = neutronclient.osc.v2.fwaas.firewallgroup:CreateFirewallGroup firewall_group_delete = neutronclient.osc.v2.fwaas.firewallgroup:DeleteFirewallGroup From 937f6403389affaa8db37cf0cad9f034050d67e5 Mon Sep 17 00:00:00 2001 From: Igor Duarte Cardoso Date: Thu, 27 Jul 2017 12:48:49 +0000 Subject: [PATCH 624/845] Add correlation type "nsh" to both PPs and PCs Add correlation type "nsh" to both Port Pair and Port Create commands' help descriptions. The NSH correlation/encapsulation has been added to networking-sfc and this patch makes the client show the option in the help description. Change-Id: Ib25d24cd05e994b7865641a8235d36c741533488 Partial-Bug: #1625278 --- neutronclient/osc/v2/sfc/sfc_port_chain.py | 3 ++- neutronclient/osc/v2/sfc/sfc_port_pair.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/neutronclient/osc/v2/sfc/sfc_port_chain.py b/neutronclient/osc/v2/sfc/sfc_port_chain.py index 37b30208d..0ab2166e6 100755 --- a/neutronclient/osc/v2/sfc/sfc_port_chain.py +++ b/neutronclient/osc/v2/sfc/sfc_port_chain.py @@ -68,7 +68,8 @@ def get_parser(self, prog_name): action=parseractions.MultiKeyValueAction, optional_keys=['correlation', 'symmetric'], help=_('Dictionary of chain parameters. Supports ' - 'correlation=mpls and symmetric=true|false.')) + 'correlation=(mpls|nsh) (default is mpls) ' + 'and symmetric=(true|false).')) parser.add_argument( '--port-pair-group', metavar='', diff --git a/neutronclient/osc/v2/sfc/sfc_port_pair.py b/neutronclient/osc/v2/sfc/sfc_port_pair.py index b8e64e4c4..2c3f5b969 100755 --- a/neutronclient/osc/v2/sfc/sfc_port_pair.py +++ b/neutronclient/osc/v2/sfc/sfc_port_pair.py @@ -58,7 +58,7 @@ def get_parser(self, prog_name): action=parseractions.MultiKeyValueAction, optional_keys=['correlation', 'weight'], help=_('Dictionary of service function parameters. ' - 'Currently, correlation=(None|mpls) and weight ' + 'Currently, correlation=(None|mpls|nsh) and weight ' 'are supported. Weight is an integer that influences ' 'the selection of a port pair within a port pair group ' 'for a flow. The higher the weight, the more flows will ' From 8cc59bf68883ffc9f52312a35d5eb75722a4deae Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Tue, 14 Nov 2017 15:31:56 +0000 Subject: [PATCH 625/845] Consume tempest CLIClient keystone v3 support tempest 17.1.0 added keystone v3 support to CLIClient and we no longer need a workaround for keystone v3 func tests. Change-Id: Ib32b5f82be938cd5213f6d71a0bf03cc1804587d Related-Bug: #1721553 Related-Bug: #1719687 --- neutronclient/tests/functional/base.py | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/neutronclient/tests/functional/base.py b/neutronclient/tests/functional/base.py index fa1018b3a..d92d6936d 100644 --- a/neutronclient/tests/functional/base.py +++ b/neutronclient/tests/functional/base.py @@ -57,6 +57,8 @@ def _get_clients_from_os_cloud_config(self, cloud='devstack-admin'): username=creds['username'], password=creds['password'], tenant_name=creds['project_name'], + project_domain_id=creds['project_domain_id'], + user_domain_id=creds['user_domain_id'], uri=creds['auth_url'], cli_dir=cli_dir) @@ -64,27 +66,12 @@ def _get_clients(self): return self._get_clients_from_os_cloud_config() def neutron(self, *args, **kwargs): - # Workaround until tempest.lib.cli.base provdes fully - # keystone v3 support. It assumes the default domain. - # TODO(amotoki): Once a new tempest with a fix for bug 1719687 - # is released, this should be claen up. - kwargs['flags'] = ' '.join([kwargs.get('flags', ''), - '--os-project-domain-id default', - '--os-user-domain-id default']) - return self.clients.neutron(*args, - **kwargs) + return self.clients.neutron(*args, **kwargs) def neutron_non_admin(self, *args, **kwargs): if not hasattr(self, '_non_admin_clients'): self._non_admin_clients = self._get_clients_from_os_cloud_config( cloud='devstack') - # Workaround until tempest.lib.cli.base provdes fully - # keystone v3 support. It assumes the default domain. - # TODO(amotoki): Once a new tempest with a fix for bug 1719687 - # is released, this should be claen up. - kwargs['flags'] = ' '.join([kwargs.get('flags', ''), - '--os-project-domain-id default', - '--os-user-domain-id default']) return self._non_admin_clients.neutron(*args, **kwargs) def is_extension_enabled(self, extension_alias): From d24c41dde55635d9f14c960f2b9718741040c90b Mon Sep 17 00:00:00 2001 From: Vikash082 Date: Sat, 5 Aug 2017 19:52:55 +0530 Subject: [PATCH 626/845] Added 'tap_enabled' parameter for Port-pair-group Adding 'tap_enabled' parameters to port-pair-group. If '--enable-tap' set to True, port pairs in port-pair-group will be deployed as passive Tap service function. Change-Id: I7267b7ce782f040d0951a3b522d183931debe818 --- .../osc/v2/sfc/sfc_port_pair_group.py | 18 ++++ neutronclient/tests/unit/osc/v2/sfc/fakes.py | 3 +- .../unit/osc/v2/sfc/test_port_pair_group.py | 89 +++++++++++++++---- ...ice-function-support-a05242f25f79066b.yaml | 4 + 4 files changed, 94 insertions(+), 20 deletions(-) create mode 100644 releasenotes/notes/sfc-tap-service-function-support-a05242f25f79066b.yaml diff --git a/neutronclient/osc/v2/sfc/sfc_port_pair_group.py b/neutronclient/osc/v2/sfc/sfc_port_pair_group.py index 688e5c3cd..c2114ecf4 100755 --- a/neutronclient/osc/v2/sfc/sfc_port_pair_group.py +++ b/neutronclient/osc/v2/sfc/sfc_port_pair_group.py @@ -36,6 +36,7 @@ ('description', 'Description', nc_osc_utils.LIST_LONG_ONLY), ('group_id', 'Loadbalance ID', nc_osc_utils.LIST_LONG_ONLY), ('project_id', 'Project', nc_osc_utils.LIST_LONG_ONLY), + ('tap_enabled', 'Tap Enabled', nc_osc_utils.LIST_BOTH) ) @@ -60,6 +61,19 @@ def get_parser(self, prog_name): action='append', help=_('Port pair (name or ID). ' 'This option can be repeated.')) + tap_enable = parser.add_mutually_exclusive_group() + tap_enable.add_argument( + '--enable-tap', + action='store_true', + help=_('Port pairs of this port pair group are deployed as ' + 'passive tap service function') + ) + tap_enable.add_argument( + '--disable-tap', + action='store_true', + help=_('Port pairs of this port pair group are deployed as l3 ' + 'service function (default)') + ) parser.add_argument( '--port-pair-group-parameters', metavar='lb-fields=', @@ -285,6 +299,10 @@ def _get_attrs(attrs, parsed_args): parsed_args.port_pair_group_parameters is not None): attrs['port_pair_group_parameters'] = ( _get_ppg_param(attrs, parsed_args.port_pair_group_parameters)) + if parsed_args.enable_tap: + attrs['tap_enabled'] = True + if parsed_args.disable_tap: + attrs['tap_enabled'] = False def _get_id(client, id_or_name, resource): diff --git a/neutronclient/tests/unit/osc/v2/sfc/fakes.py b/neutronclient/tests/unit/osc/v2/sfc/fakes.py index 54bd5a8ee..e9e3f7909 100755 --- a/neutronclient/tests/unit/osc/v2/sfc/fakes.py +++ b/neutronclient/tests/unit/osc/v2/sfc/fakes.py @@ -107,7 +107,8 @@ def create_port_pair_group(attrs=None): 'description': 'description', 'port_pairs': uuidutils.generate_uuid(), 'port_pair_group_parameters': '{"lb_fields": []}', - 'project_id': uuidutils.generate_uuid() + 'project_id': uuidutils.generate_uuid(), + 'tap_enabled': False } # port_pair_group_attrs default attributes. diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_port_pair_group.py b/neutronclient/tests/unit/osc/v2/sfc/test_port_pair_group.py index 894c1bee7..c59e96194 100755 --- a/neutronclient/tests/unit/osc/v2/sfc/test_port_pair_group.py +++ b/neutronclient/tests/unit/osc/v2/sfc/test_port_pair_group.py @@ -33,17 +33,19 @@ class TestCreateSfcPortPairGroup(fakes.TestNeutronClientOSCV2): 'Name', 'Port Pair', 'Port Pair Group Parameters', - 'Project') + 'Project', + 'Tap Enabled') - def get_data(self): + def get_data(self, ppg): return ( - self._port_pair_group['description'], - self._port_pair_group['id'], - self._port_pair_group['group_id'], - self._port_pair_group['name'], - self._port_pair_group['port_pairs'], - self._port_pair_group['port_pair_group_parameters'], - self._port_pair_group['project_id'] + ppg['description'], + ppg['id'], + ppg['group_id'], + ppg['name'], + ppg['port_pairs'], + ppg['port_pair_group_parameters'], + ppg['project_id'], + ppg['tap_enabled'] ) def setUp(self): @@ -53,7 +55,7 @@ def setUp(self): new=_get_id).start() self.neutronclient.create_sfc_port_pair_group = mock.Mock( return_value={'port_pair_group': self._port_pair_group}) - self.data = self.get_data() + self.data = self.get_data(self._port_pair_group) # Get the command object to test self.cmd = sfc_port_pair_group.CreateSfcPortPairGroup(self.app, self.namespace) @@ -73,7 +75,8 @@ def test_create_port_pair_group_default_options(self): self.neutronclient.create_sfc_port_pair_group.assert_called_once_with({ 'port_pair_group': { 'name': self._port_pair_group['name'], - 'port_pairs': [self._port_pair_group['port_pairs']]} + 'port_pairs': [self._port_pair_group['port_pairs']] + } }) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) @@ -102,6 +105,46 @@ def test_create_port_pair_group(self): self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) + def test_create_tap_enabled_port_pair_group(self): + arglist = [ + "--description", self._port_pair_group['description'], + "--port-pair", self._port_pair_group['port_pairs'], + self._port_pair_group['name'], + "--enable-tap" + ] + verifylist = [ + ('port_pairs', [self._port_pair_group['port_pairs']]), + ('name', self._port_pair_group['name']), + ('description', self._port_pair_group['description']), + ('enable_tap', True) + ] + + expected_data = self._update_expected_response_data( + data={ + 'tap_enabled': True + } + ) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = (self.cmd.take_action(parsed_args)) + + self.neutronclient.create_sfc_port_pair_group.assert_called_once_with({ + 'port_pair_group': { + 'name': self._port_pair_group['name'], + 'port_pairs': [self._port_pair_group['port_pairs']], + 'description': self._port_pair_group['description'], + 'tap_enabled': True + } + }) + self.assertEqual(self.columns, columns) + self.assertEqual(expected_data, data) + + def _update_expected_response_data(self, data): + # REVISIT(vks1) - This method can be common for other test functions. + ppg = fakes.FakeSfcPortPairGroup.create_port_pair_group(data) + self.neutronclient.create_sfc_port_pair_group.return_value = { + 'port_pair_group': ppg} + return self.get_data(ppg) + class TestDeleteSfcPortPairGroup(fakes.TestNeutronClientOSCV2): @@ -136,22 +179,25 @@ def test_delete_port_pair_group(self): class TestListSfcPortPairGroup(fakes.TestNeutronClientOSCV2): _ppgs = fakes.FakeSfcPortPairGroup.create_port_pair_groups(count=1) - columns = ('ID', 'Name', 'Port Pair', 'Port Pair Group Parameters') + columns = ('ID', 'Name', 'Port Pair', 'Port Pair Group Parameters', + 'Tap Enabled') columns_long = ('ID', 'Name', 'Port Pair', 'Port Pair Group Parameters', - 'Description', 'Loadbalance ID', 'Project') + 'Description', 'Loadbalance ID', 'Project', 'Tap Enabled') _port_pair_group = _ppgs[0] data = [ _port_pair_group['id'], _port_pair_group['name'], _port_pair_group['port_pairs'], - _port_pair_group['port_pair_group_parameters'] + _port_pair_group['port_pair_group_parameters'], + _port_pair_group['tap_enabled'] ] data_long = [ _port_pair_group['id'], _port_pair_group['name'], _port_pair_group['port_pairs'], _port_pair_group['port_pair_group_parameters'], - _port_pair_group['description'] + _port_pair_group['description'], + _port_pair_group['tap_enabled'] ] _port_pair_group1 = {'port_pair_groups': _port_pair_group} _port_pair_id = _port_pair_group['id'] @@ -181,7 +227,8 @@ def test_list_port_pair_groups(self): ppg['id'], ppg['name'], ppg['port_pairs'], - ppg['port_pair_group_parameters'] + ppg['port_pair_group_parameters'], + ppg['tap_enabled'] ] self.assertEqual(list(self.columns), columns) self.assertEqual(self.data, data) @@ -197,7 +244,8 @@ def test_list_with_long_option(self): ppg['name'], ppg['port_pairs'], ppg['port_pair_group_parameters'], - ppg['description'] + ppg['description'], + ppg['tap_enabled'] ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns_long = self.cmd.take_action(parsed_args)[0] @@ -301,7 +349,8 @@ class TestShowSfcPortPairGroup(fakes.TestNeutronClientOSCV2): _ppg['name'], _ppg['port_pairs'], _ppg['port_pair_group_parameters'], - _ppg['project_id']) + _ppg['project_id'], + _ppg['tap_enabled']) _port_pair_group = {'port_pair_group': _ppg} _port_pair_group_id = _ppg['id'] columns = ( @@ -311,7 +360,9 @@ class TestShowSfcPortPairGroup(fakes.TestNeutronClientOSCV2): 'Name', 'Port Pair', 'Port Pair Group Parameters', - 'Project') + 'Project', + 'Tap Enabled' + ) def setUp(self): super(TestShowSfcPortPairGroup, self).setUp() diff --git a/releasenotes/notes/sfc-tap-service-function-support-a05242f25f79066b.yaml b/releasenotes/notes/sfc-tap-service-function-support-a05242f25f79066b.yaml new file mode 100644 index 000000000..ccfcc8276 --- /dev/null +++ b/releasenotes/notes/sfc-tap-service-function-support-a05242f25f79066b.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Add OSC support to create Port pair group for Tap service functions. From 7926406568909e930f4c0b6d76dd10e77d4f8063 Mon Sep 17 00:00:00 2001 From: Andreas Jaeger Date: Thu, 16 Nov 2017 20:45:47 +0100 Subject: [PATCH 627/845] Remove setting of version/release from releasenotes Release notes are version independent, so remove version/release values. We've found that projects now require the service package to be installed in order to build release notes, and this is entirely due to the current convention of pulling in the version information. Release notes should not need installation in order to build, so this unnecessary version setting needs to be removed. This is needed for new release notes publishing, see I56909152975f731a9d2c21b2825b972195e48ee8 and the discussion starting at http://lists.openstack.org/pipermail/openstack-dev/2017-November/124480.html . Change-Id: Ie3be535be2de4c27114233cff34f1bb70ca33e46 --- releasenotes/source/conf.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py index b955f990e..a029757ed 100644 --- a/releasenotes/source/conf.py +++ b/releasenotes/source/conf.py @@ -64,16 +64,11 @@ project = u'Neutron Client Release Notes' copyright = u'2015, Neutron Developers' -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -from neutronclient.version import version_info +# Release notes are version independent. # The full version, including alpha/beta/rc tags. -release = version_info.version_string_with_vcs() +release = '' # The short X.Y version. -version = version_info.canonical_version_string() +version = '' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. From 96f5b3bbfc8aed7b91006409c5b7cca2ca1e2a75 Mon Sep 17 00:00:00 2001 From: Andreas Jaeger Date: Thu, 30 Nov 2017 22:14:57 +0100 Subject: [PATCH 628/845] Avoid tox_install.sh for constraints support We do not need tox_install.sh, pip can handle constraints itself and install the project correctly. Thus update tox.ini and remove the now obsolete tools/tox_install.sh file. This follows https://review.openstack.org/#/c/508061 to remove tools/tox_install.sh. Change-Id: I1e72cf7084ed0b6fcd3c5df1f31b3fa6c7734d31 --- tools/tox_install.sh | 55 -------------------------------------------- tox.ini | 6 ++--- 2 files changed, 3 insertions(+), 58 deletions(-) delete mode 100755 tools/tox_install.sh diff --git a/tools/tox_install.sh b/tools/tox_install.sh deleted file mode 100755 index e3fb45960..000000000 --- a/tools/tox_install.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env bash - -# Client constraint file contains this client version pin that is in conflict -# with installing the client from source. We should replace the version pin in -# the constraints file before applying it for from-source installation. - -ZUUL_CLONER=/usr/zuul-env/bin/zuul-cloner -BRANCH_NAME=master -CLIENT_NAME=python-neutronclient -requirements_installed=$(echo "import openstack_requirements" | python 2>/dev/null ; echo $?) - -set -e - -CONSTRAINTS_FILE=$1 -shift - -install_cmd="pip install" -mydir=$(mktemp -dt "$CLIENT_NAME-tox_install-XXXXXXX") -trap "rm -rf $mydir" EXIT -localfile=$mydir/upper-constraints.txt -if [[ $CONSTRAINTS_FILE != http* ]]; then - CONSTRAINTS_FILE=file://$CONSTRAINTS_FILE -fi -curl $CONSTRAINTS_FILE -k -o $localfile -install_cmd="$install_cmd -c$localfile" - -if [ $requirements_installed -eq 0 ]; then - echo "ALREADY INSTALLED" > /tmp/tox_install.txt - echo "Requirements already installed; using existing package" -elif [ -x "$ZUUL_CLONER" ]; then - echo "ZUUL CLONER" > /tmp/tox_install.txt - pushd $mydir - $ZUUL_CLONER --cache-dir \ - /opt/git \ - --branch $BRANCH_NAME \ - git://git.openstack.org \ - openstack/requirements - cd openstack/requirements - $install_cmd -e . - popd -else - echo "PIP HARDCODE" > /tmp/tox_install.txt - if [ -z "$REQUIREMENTS_PIP_LOCATION" ]; then - REQUIREMENTS_PIP_LOCATION="git+https://git.openstack.org/openstack/requirements@$BRANCH_NAME#egg=requirements" - fi - $install_cmd -U -e ${REQUIREMENTS_PIP_LOCATION} -fi - -# This is the main purpose of the script: Allow local installation of -# the current repo. It is listed in constraints file and thus any -# install will be constrained and we need to unconstrain it. -edit-constraints $localfile -- $CLIENT_NAME "-e file://$PWD#egg=$CLIENT_NAME" - -$install_cmd -U $* -exit $? diff --git a/tox.ini b/tox.ini index 23fda5012..725f4d423 100644 --- a/tox.ini +++ b/tox.ini @@ -11,9 +11,9 @@ setenv = VIRTUAL_ENV={envdir} LC_ALL=C PYTHONWARNINGS=default::DeprecationWarning usedevelop = True -install_command = - {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages} -deps = -r{toxinidir}/requirements.txt +install_command = pip install {opts} {packages} +deps = -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} + -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt # Delete bytecodes from normal directories before running tests. # Note that bytecodes in dot directories will not be deleted From 7c01bdb8ba91f8baead830a0cd16fafa02d6124a Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Sat, 16 Dec 2017 04:40:18 +0900 Subject: [PATCH 629/845] Remove a workaround for osc-lib in FWaaS UT The issue was fixed in osc-lib 1.8.0 and the workaround in neutronclient UT is no longer needed. This commit drops it. Change-Id: I4a8f5c2d172ead3e2c40d2a8f5286aa60a73be72 Closes-Bug: #1738430 --- neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py index e38306ed3..a512f3b3f 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py @@ -48,11 +48,6 @@ def _generate_data(ordered_dict=None, data=None): def _replace_display_columns(key, val): - # TODO(amotoki): This is required because of the logic of - # osc_lib.utils.get_dict_properties(). - # It needs to be fixed in osc-lib first. - if val is None: - return val if key == 'protocol': return firewallrule.ProtocolColumn(val) return val From 9a7050b2dbe3c0cc393bd4664aa5c581ea7caaec Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Tue, 19 Dec 2017 01:44:22 +0000 Subject: [PATCH 630/845] Updated from global requirements Change-Id: I2f5dbe4729c8b2d3664224c7b61d6d21323a2415 --- requirements.txt | 6 +++--- test-requirements.txt | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/requirements.txt b/requirements.txt index 97a5b67a0..65a415905 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,13 +9,13 @@ netaddr>=0.7.18 # BSD osc-lib>=1.7.0 # Apache-2.0 oslo.i18n>=3.15.3 # Apache-2.0 oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0 -oslo.utils>=3.28.0 # Apache-2.0 +oslo.utils>=3.33.0 # Apache-2.0 os-client-config>=1.28.0 # Apache-2.0 -keystoneauth1>=3.2.0 # Apache-2.0 +keystoneauth1>=3.3.0 # Apache-2.0 # keystoneclient is used only by neutronclient.osc.utils # TODO(amotoki): Drop this after osc.utils has no dependency on keystoneclient python-keystoneclient>=3.8.0 # Apache-2.0 requests>=2.14.2 # Apache-2.0 simplejson>=3.5.1 # MIT -six>=1.9.0 # MIT +six>=1.10.0 # MIT Babel!=2.4.0,>=2.3.4 # BSD diff --git a/test-requirements.txt b/test-requirements.txt index 97e155267..23d802642 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -12,11 +12,11 @@ openstackdocstheme>=1.17.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 osprofiler>=1.4.0 # Apache-2.0 python-openstackclient>=3.12.0 # Apache-2.0 -python-subunit>=0.0.18 # Apache-2.0/BSD +python-subunit>=1.0.0 # Apache-2.0/BSD reno>=2.5.0 # Apache-2.0 requests-mock>=1.1.0 # Apache-2.0 sphinx>=1.6.2 # BSD testrepository>=0.0.18 # Apache-2.0/BSD -testtools>=1.4.0 # MIT +testtools>=2.2.0 # MIT testscenarios>=0.4 # Apache-2.0/BSD -tempest>=16.1.0 # Apache-2.0 +tempest>=17.1.0 # Apache-2.0 From 05218d9500c899f2f4f145d03c9cda7f65df6a45 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Thu, 21 Dec 2017 00:44:49 +0000 Subject: [PATCH 631/845] Updated from global requirements Change-Id: I6f0b04b8158f2702c522710b2f658cddfdef6675 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 65a415905..4933c33b5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ cliff!=2.9.0,>=2.8.0 # Apache-2.0 debtcollector>=1.2.0 # Apache-2.0 iso8601>=0.1.11 # MIT netaddr>=0.7.18 # BSD -osc-lib>=1.7.0 # Apache-2.0 +osc-lib>=1.8.0 # Apache-2.0 oslo.i18n>=3.15.3 # Apache-2.0 oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0 oslo.utils>=3.33.0 # Apache-2.0 From 228ad49be57c2029b11d67d12a0a599bc5af94a2 Mon Sep 17 00:00:00 2001 From: Cao Xuan Hoang Date: Tue, 2 Jan 2018 11:42:00 +0700 Subject: [PATCH 632/845] Enable VPNaaS functional tests in the neutronclient gate This patch is enable VPNaaS functional tests in gate after the project come back. This is follow up the latter of [1]. [1] https://review.openstack.org/#/c/404366/ Change-Id: Idc9a021dc5916757b3698bd74866db2e7f824475 --- neutronclient/tests/functional/hooks/gate_hook.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/neutronclient/tests/functional/hooks/gate_hook.sh b/neutronclient/tests/functional/hooks/gate_hook.sh index 36db5690f..c1721d88d 100755 --- a/neutronclient/tests/functional/hooks/gate_hook.sh +++ b/neutronclient/tests/functional/hooks/gate_hook.sh @@ -35,6 +35,7 @@ function load_rc_hook { if [ "$VENV" == "functional-adv-svcs" ] then load_rc_hook fwaas + load_rc_hook vpnaas fi export DEVSTACK_LOCALCONF=$(cat $LOCAL_CONF) From cefa2432f677b8812330950055f285b13790080a Mon Sep 17 00:00:00 2001 From: Hongbin Lu Date: Mon, 11 Dec 2017 05:26:39 +0000 Subject: [PATCH 633/845] Pass headers to httpclient Right now, it is impossible to set HTTP headers in client because the 'headers' parameter is not set. This patch fixes it. Change-Id: Iff002aaca516e8d014074f120b7f713758ebf265 Partial-Bug: #1737473 --- neutronclient/client.py | 7 ++++--- neutronclient/tests/unit/test_http.py | 18 ++++++++++++++++++ neutronclient/v2_0/client.py | 3 ++- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/neutronclient/client.py b/neutronclient/client.py index 395f7dbe5..2482eb434 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -190,7 +190,7 @@ def do_request(self, url, method, **kwargs): # might be because the auth token expired, so try to # re-authenticate and try again. If it still fails, bail. try: - kwargs.setdefault('headers', {}) + kwargs['headers'] = kwargs.get('headers') or {} if self.auth_token is None: self.auth_token = "" kwargs['headers']['X-Auth-Token'] = self.auth_token @@ -199,7 +199,7 @@ def do_request(self, url, method, **kwargs): return resp, body except exceptions.Unauthorized: self.authenticate() - kwargs.setdefault('headers', {}) + kwargs['headers'] = kwargs.get('headers') or {} kwargs['headers']['X-Auth-Token'] = self.auth_token resp, body = self._cs_request( self.endpoint_url + url, method, **kwargs) @@ -311,7 +311,7 @@ def request(self, *args, **kwargs): content_type = kwargs.pop('content_type', None) or 'application/json' - headers = kwargs.setdefault('headers', {}) + headers = kwargs.get('headers') or {} headers.setdefault('Accept', content_type) # NOTE(dbelova): osprofiler_web.get_trace_id_headers does not add any @@ -327,6 +327,7 @@ def request(self, *args, **kwargs): if kwargs.get('data'): headers.setdefault('Content-Type', content_type) + kwargs['headers'] = headers resp = super(SessionClient, self).request(*args, **kwargs) return resp, resp.text diff --git a/neutronclient/tests/unit/test_http.py b/neutronclient/tests/unit/test_http.py index 73b7a30b0..21b615592 100644 --- a/neutronclient/tests/unit/test_http.py +++ b/neutronclient/tests/unit/test_http.py @@ -123,6 +123,24 @@ def test_request_forbidden_is_returned_to_caller(self): self.assertEqual(403, resp.status_code) self.assertEqual(text, resp_text) + def test_do_request_success(self): + text = 'test content' + self.requests.register_uri(METHOD, END_URL + URL, text=text) + + resp, resp_text = self.http.do_request(URL, METHOD) + self.assertEqual(200, resp.status_code) + self.assertEqual(text, resp_text) + + def test_do_request_with_headers_success(self): + text = 'test content' + self.requests.register_uri(METHOD, END_URL + URL, text=text, + request_headers={'key': 'value'}) + + resp, resp_text = self.http.do_request(URL, METHOD, + headers={'key': 'value'}) + self.assertEqual(200, resp.status_code) + self.assertEqual(text, resp_text) + class TestHTTPClientWithReqId(TestHTTPClientMixin, testtools.TestCase): """Tests for when global_request_id is set.""" diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index f4e49f895..36bb79321 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -277,7 +277,8 @@ def do_request(self, method, action, body=None, headers=None, params=None): if body: body = self.serialize(body) - resp, replybody = self.httpclient.do_request(action, method, body=body) + resp, replybody = self.httpclient.do_request(action, method, body=body, + headers=headers) status_code = resp.status_code if status_code in (requests.codes.ok, From c02b8074aa5767d527b48c2c3aeffde57ca2d8fb Mon Sep 17 00:00:00 2001 From: Hongbin Lu Date: Mon, 11 Dec 2017 05:55:14 +0000 Subject: [PATCH 634/845] Add 'revision_number' to update_* API This allows API consumers to leverage the If-Match HTTP header to conditionally update a resource Change-Id: Iaa98ce0436dad3d93cd295090f660c729899a0c8 Partial-Bug: #1737473 Related-Bug: #1417975 --- neutronclient/tests/unit/test_cli20.py | 50 ++++++++++++++++++++++ neutronclient/v2_0/client.py | 59 +++++++++++++++++--------- 2 files changed, 89 insertions(+), 20 deletions(-) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index aa46c4f75..132e1e547 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -805,6 +805,56 @@ def test_deserialize_without_data(self): result = self.client.deserialize(data, 200) self.assertEqual(data, result) + def test_update_resource(self): + self.mox.StubOutWithMock(self.client.httpclient, "request") + params = {'test': 'value'} + expect_query = six.moves.urllib.parse.urlencode(params) + self.client.httpclient.auth_token = 'token' + body = params + expect_body = self.client.serialize(body) + resp_headers = {'x-openstack-request-id': REQUEST_ID} + self.client.httpclient.request( + MyUrlComparator(end_url('/test', query=expect_query), self.client), + 'PUT', body=expect_body, + headers=mox.And( + mox.ContainsKeyValue('X-Auth-Token', 'token'), + mox.Not(mox.In('If-Match'))) + ).AndReturn((MyResp(200, resp_headers), expect_body)) + + self.mox.ReplayAll() + result = self.client._update_resource('/test', body=body, + params=params) + self.mox.VerifyAll() + self.mox.UnsetStubs() + + self.assertEqual(body, result) + self.assertEqual([REQUEST_ID], result.request_ids) + + def test_update_resource_with_revision_number(self): + self.mox.StubOutWithMock(self.client.httpclient, "request") + params = {'test': 'value'} + expect_query = six.moves.urllib.parse.urlencode(params) + self.client.httpclient.auth_token = 'token' + body = params + expect_body = self.client.serialize(body) + resp_headers = {'x-openstack-request-id': REQUEST_ID} + self.client.httpclient.request( + MyUrlComparator(end_url('/test', query=expect_query), self.client), + 'PUT', body=expect_body, + headers=mox.And( + mox.ContainsKeyValue('X-Auth-Token', 'token'), + mox.ContainsKeyValue('If-Match', 'revision_number=1')) + ).AndReturn((MyResp(200, resp_headers), expect_body)) + + self.mox.ReplayAll() + result = self.client._update_resource('/test', body=body, + params=params, revision_number=1) + self.mox.VerifyAll() + self.mox.UnsetStubs() + + self.assertEqual(body, result) + self.assertEqual([REQUEST_ID], result.request_ids) + class CLITestV20ExceptionHandler(CLITestV20Base): diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 36bb79321..3c230176f 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -781,9 +781,10 @@ def create_port(self, body=None): """Creates a new port.""" return self.post(self.ports_path, body=body) - def update_port(self, port, body=None): + def update_port(self, port, body=None, revision_number=None): """Updates a port.""" - return self.put(self.port_path % (port), body=body) + return self._update_resource(self.port_path % (port), body=body, + revision_number=revision_number) def delete_port(self, port): """Deletes the specified port.""" @@ -803,9 +804,10 @@ def create_network(self, body=None): """Creates a new network.""" return self.post(self.networks_path, body=body) - def update_network(self, network, body=None): + def update_network(self, network, body=None, revision_number=None): """Updates a network.""" - return self.put(self.network_path % (network), body=body) + return self._update_resource(self.network_path % (network), body=body, + revision_number=revision_number) def delete_network(self, network): """Deletes the specified network.""" @@ -824,9 +826,10 @@ def create_subnet(self, body=None): """Creates a new subnet.""" return self.post(self.subnets_path, body=body) - def update_subnet(self, subnet, body=None): + def update_subnet(self, subnet, body=None, revision_number=None): """Updates a subnet.""" - return self.put(self.subnet_path % (subnet), body=body) + return self._update_resource(self.subnet_path % (subnet), body=body, + revision_number=revision_number) def delete_subnet(self, subnet): """Deletes the specified subnet.""" @@ -845,9 +848,11 @@ def create_subnetpool(self, body=None): """Creates a new subnetpool.""" return self.post(self.subnetpools_path, body=body) - def update_subnetpool(self, subnetpool, body=None): + def update_subnetpool(self, subnetpool, body=None, revision_number=None): """Updates a subnetpool.""" - return self.put(self.subnetpool_path % (subnetpool), body=body) + return self._update_resource(self.subnetpool_path % (subnetpool), + body=body, + revision_number=revision_number) def delete_subnetpool(self, subnetpool): """Deletes the specified subnetpool.""" @@ -867,9 +872,10 @@ def create_router(self, body=None): """Creates a new router.""" return self.post(self.routers_path, body=body) - def update_router(self, router, body=None): + def update_router(self, router, body=None, revision_number=None): """Updates a router.""" - return self.put(self.router_path % (router), body=body) + return self._update_resource(self.router_path % (router), body=body, + revision_number=revision_number) def delete_router(self, router): """Deletes the specified router.""" @@ -931,9 +937,11 @@ def create_floatingip(self, body=None): """Creates a new floatingip.""" return self.post(self.floatingips_path, body=body) - def update_floatingip(self, floatingip, body=None): + def update_floatingip(self, floatingip, body=None, revision_number=None): """Updates a floatingip.""" - return self.put(self.floatingip_path % (floatingip), body=body) + return self._update_resource(self.floatingip_path % (floatingip), + body=body, + revision_number=revision_number) def delete_floatingip(self, floatingip): """Deletes the specified floatingip.""" @@ -943,10 +951,12 @@ def create_security_group(self, body=None): """Creates a new security group.""" return self.post(self.security_groups_path, body=body) - def update_security_group(self, security_group, body=None): + def update_security_group(self, security_group, body=None, + revision_number=None): """Updates a security group.""" - return self.put(self.security_group_path % - security_group, body=body) + return self._update_resource(self.security_group_path % + security_group, body=body, + revision_number=revision_number) def list_security_groups(self, retrieve_all=True, **_params): """Fetches a list of all security groups for a project.""" @@ -1790,10 +1800,11 @@ def create_qos_policy(self, body=None): """Creates a new qos policy.""" return self.post(self.qos_policies_path, body=body) - def update_qos_policy(self, qos_policy, body=None): + def update_qos_policy(self, qos_policy, body=None, revision_number=None): """Updates a qos policy.""" - return self.put(self.qos_policy_path % qos_policy, - body=body) + return self._update_resource(self.qos_policy_path % qos_policy, + body=body, + revision_number=revision_number) def delete_qos_policy(self, qos_policy): """Deletes the specified qos policy.""" @@ -2067,9 +2078,10 @@ def create_trunk(self, body=None): """Create a trunk port.""" return self.post(self.trunks_path, body=body) - def update_trunk(self, trunk, body=None): + def update_trunk(self, trunk, body=None, revision_number=None): """Update a trunk port.""" - return self.put(self.trunk_path % trunk, body=body) + return self._update_resource(self.trunk_path % trunk, body=body, + revision_number=revision_number) def delete_trunk(self, trunk): """Delete a trunk port.""" @@ -2266,6 +2278,13 @@ def __init__(self, **kwargs): super(Client, self).__init__(**kwargs) self._register_extensions(self.version) + def _update_resource(self, path, **kwargs): + revision_number = kwargs.pop('revision_number', None) + if revision_number: + headers = kwargs.setdefault('headers', {}) + headers['If-Match'] = 'revision_number=%s' % revision_number + return self.put(path, **kwargs) + def extend_show(self, resource_singular, path, parent_resource): def _fx(obj, **_params): return self.show_ext(path, obj, **_params) From c569d985c466550c4c3cf9d34b7d30dcc09aaab1 Mon Sep 17 00:00:00 2001 From: Yushiro FURUKAWA Date: Mon, 12 Dec 2016 23:14:56 +0900 Subject: [PATCH 635/845] Add Logging commands for OSC plugin This commit supports Logging CLI as OSC plugin[1]. [1] https://bugs.launchpad.net/neutron/+bug/1468366 Depends-On: Ib8668dd25ee7c5000a6dafcc7db3dbc33ad190be Partially-implements: blueprint security-group-logging Change-Id: If9a5e0e3958ea32f4c0d3573efcb3ac4101c63a6 --- doc/source/cli/osc/v2/network-log.rst | 15 + neutronclient/osc/v2/logging/__init__.py | 0 neutronclient/osc/v2/logging/network_log.py | 289 ++++++++ .../tests/unit/osc/v2/logging/__init__.py | 0 .../tests/unit/osc/v2/logging/fakes.py | 79 +++ .../unit/osc/v2/logging/test_network_log.py | 658 ++++++++++++++++++ neutronclient/v2_0/client.py | 32 + .../support-logging-cli-cd02d3bb03367106.yaml | 5 + setup.cfg | 8 +- 9 files changed, 1085 insertions(+), 1 deletion(-) create mode 100644 doc/source/cli/osc/v2/network-log.rst create mode 100644 neutronclient/osc/v2/logging/__init__.py create mode 100644 neutronclient/osc/v2/logging/network_log.py create mode 100644 neutronclient/tests/unit/osc/v2/logging/__init__.py create mode 100644 neutronclient/tests/unit/osc/v2/logging/fakes.py create mode 100644 neutronclient/tests/unit/osc/v2/logging/test_network_log.py create mode 100644 releasenotes/notes/support-logging-cli-cd02d3bb03367106.yaml diff --git a/doc/source/cli/osc/v2/network-log.rst b/doc/source/cli/osc/v2/network-log.rst new file mode 100644 index 000000000..df3a32729 --- /dev/null +++ b/doc/source/cli/osc/v2/network-log.rst @@ -0,0 +1,15 @@ +=========== +network log +=========== + +A **network log** is a container to group security groups or ports for logging. +Specified resources can be logged via these event (``ALL``, ``ACCEPT`` or +``DROP``). + +Network v2 + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: network loggable resources list + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: network log * diff --git a/neutronclient/osc/v2/logging/__init__.py b/neutronclient/osc/v2/logging/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/osc/v2/logging/network_log.py b/neutronclient/osc/v2/logging/network_log.py new file mode 100644 index 000000000..becb1cb69 --- /dev/null +++ b/neutronclient/osc/v2/logging/network_log.py @@ -0,0 +1,289 @@ +# Copyright 2017 FUJTISU LIMITED. +# 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 copy + +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils +from oslo_log import log as logging + +from neutronclient._i18n import _ +from neutronclient.common import utils as nc_utils +from neutronclient.osc import utils as osc_utils + + +LOG = logging.getLogger(__name__) + +_attr_map = ( + ('id', 'ID', osc_utils.LIST_BOTH), + ('description', 'Description', osc_utils.LIST_LONG_ONLY), + ('enabled', 'Enabled', osc_utils.LIST_BOTH), + ('name', 'Name', osc_utils.LIST_BOTH), + ('target_id', 'Target', osc_utils.LIST_LONG_ONLY), + ('project_id', 'Project', osc_utils.LIST_LONG_ONLY), + ('resource_id', 'Resource', osc_utils.LIST_LONG_ONLY), + ('resource_type', 'Type', osc_utils.LIST_BOTH), + ('event', 'Event', osc_utils.LIST_LONG_ONLY), + ('summary', 'Summary', osc_utils.LIST_SHORT_ONLY), +) + +_attr_map_for_loggable = ( + ('type', 'Supported types', osc_utils.LIST_BOTH), +) + +NET_LOG = 'network_log' + + +def _get_common_parser(parser): + parser.add_argument( + '--description', + metavar='', + help=_('Description of the network log')) + enable_group = parser.add_mutually_exclusive_group() + enable_group.add_argument( + '--enable', + action='store_true', + help=_('Enable this log (default is disabled)')) + enable_group.add_argument( + '--disable', + action='store_true', + help=_('Disable this log')) + return parser + + +def _get_common_attrs(client_manager, parsed_args, is_create=True): + attrs = {} + client = client_manager.neutronclient + + if is_create: + if 'project' in parsed_args and parsed_args.project is not None: + attrs['project_id'] = osc_utils.find_project( + client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + if parsed_args.resource: + attrs['resource_id'] = client.find_resource( + 'security_group', parsed_args.resource)['id'] + if parsed_args.target: + # NOTE(yushiro) Currently, we're supporting only port + attrs['target_id'] = client.find_resource( + 'port', parsed_args.target)['id'] + if parsed_args.event: + attrs['event'] = parsed_args.event + if parsed_args.resource_type: + attrs['resource_type'] = parsed_args.resource_type + if parsed_args.enable: + attrs['enabled'] = True + if parsed_args.disable: + attrs['enabled'] = False + if parsed_args.name: + attrs['name'] = parsed_args.name + if parsed_args.description: + attrs['description'] = parsed_args.description + return attrs + + +class CreateNetworkLog(command.ShowOne): + _description = _("Create a new network log") + + def get_parser(self, prog_name): + parser = super(CreateNetworkLog, self).get_parser(prog_name) + _get_common_parser(parser) + osc_utils.add_project_owner_option_to_parser(parser) + parser.add_argument( + 'name', + metavar='', + help=_('Name for the network log')) + parser.add_argument( + '--event', + metavar='', + choices=['ALL', 'ACCEPT', 'DROP'], + type=nc_utils.convert_to_uppercase, + help=_('An event to store with log')) + # NOTE(yushiro) '--resource-type' is managed by following command: + # "openstack network loggable resource list". Therefore, this option + # shouldn't have "choices" like ['security_group'] + parser.add_argument( + '--resource-type', + metavar='', + required=True, + type=nc_utils.convert_to_lowercase, + help=_('Network log type(s). ' + 'You can see supported type(s) with following command:\n' + '$ openstack network loggable resource list')) + parser.add_argument( + '--resource', + metavar='', + help=_('Security group (name or ID) for logging. You can control ' + 'for logging target combination with --target option.')) + parser.add_argument( + '--target', + metavar='', + help=_('Port (name or ID) for logging. You can control ' + 'for logging target combination with --resource option.')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + attrs = _get_common_attrs(self.app.client_manager, parsed_args) + obj = client.create_network_log({'log': attrs})['log'] + columns, display_columns = osc_utils.get_columns(obj, _attr_map) + data = utils.get_dict_properties(obj, columns) + return (display_columns, data) + + +class DeleteNetworkLog(command.Command): + _description = _("Delete network log(s)") + + def get_parser(self, prog_name): + parser = super(DeleteNetworkLog, self).get_parser(prog_name) + parser.add_argument( + 'network_log', + metavar='', + nargs='+', + help=_('Network log(s) to delete (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + result = 0 + for log_res in parsed_args.network_log: + try: + log_id = client.find_resource( + 'log', log_res, cmd_resource=NET_LOG)['id'] + client.delete_network_log(log_id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete network log with " + "name or ID '%(network_log)s': %(e)s"), + {'network_log': log_res, 'e': e}) + + if result > 0: + total = len(parsed_args.network_log) + msg = (_("%(result)s of %(total)s network log(s) " + "failed to delete") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) + + +class ListLoggableResource(command.Lister): + _description = _("List supported loggable resources") + + def get_parser(self, prog_name): + parser = super(ListLoggableResource, self).get_parser(prog_name) + parser.add_argument( + '--long', + action='store_true', + help=_("List additional fields in output") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + obj = client.list_network_loggable_resources()['loggable_resources'] + headers, columns = osc_utils.get_column_definitions( + _attr_map_for_loggable, long_listing=parsed_args.long) + return (headers, (utils.get_dict_properties(s, columns) for s in obj)) + + +class ListNetworkLog(command.Lister): + _description = _("List network logs") + + def get_parser(self, prog_name): + parser = super(ListNetworkLog, self).get_parser(prog_name) + parser.add_argument( + '--long', + action='store_true', + help=_("List additional fields in output") + ) + # TODO(yushiro): We'll support filtering in the future. + return parser + + def _extend_list(self, data, parsed_args): + ext_data = copy.deepcopy(data) + for d in ext_data: + e_prefix = 'Event: ' + if d['event']: + event = e_prefix + d['event'].upper() + port = '(port) ' + d['target_id'] if d['target_id'] else '' + sg = ('(security_group) ' + d['resource_id'] + if d['resource_id'] else '') + t_prefix = 'Logged: ' + t = sg + ' on ' + port if port and sg else sg + port + target = t_prefix + t if t else t_prefix + '(None specified)' + d['summary'] = ',\n'.join([event, target]) + return ext_data + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + obj = client.list_network_logs()['logs'] + obj_extend = self._extend_list(obj, parsed_args) + headers, columns = osc_utils.get_column_definitions( + _attr_map, long_listing=parsed_args.long) + return (headers, ( + utils.get_dict_properties(s, columns) for s in obj_extend)) + + +class SetNetworkLog(command.Command): + _description = _("Set network log properties") + + def get_parser(self, prog_name): + parser = super(SetNetworkLog, self).get_parser(prog_name) + _get_common_parser(parser) + parser.add_argument( + 'network_log', + metavar='', + help=_('Network log to set (name or ID)')) + parser.add_argument( + '--name', + metavar='', + help=_('Name of the network log')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + log_id = client.find_resource( + 'log', parsed_args.network_log, cmd_resource=NET_LOG)['id'] + attrs = _get_common_attrs(self.app.client_manager, parsed_args, + is_create=False) + try: + client.update_network_log(log_id, {'log': attrs}) + except Exception as e: + msg = (_("Failed to set network log '%(logging)s': %(e)s") + % {'logging': parsed_args.network_log, 'e': e}) + raise exceptions.CommandError(msg) + + +class ShowNetworkLog(command.ShowOne): + _description = _("Display network log details") + + def get_parser(self, prog_name): + parser = super(ShowNetworkLog, self).get_parser(prog_name) + parser.add_argument( + 'network_log', + metavar='', + help=_('Network log to show (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + log_id = client.find_resource( + 'log', parsed_args.network_log, cmd_resource=NET_LOG)['id'] + obj = client.show_network_log(log_id)['log'] + columns, display_columns = osc_utils.get_columns(obj, _attr_map) + data = utils.get_dict_properties(obj, columns) + return (display_columns, data) diff --git a/neutronclient/tests/unit/osc/v2/logging/__init__.py b/neutronclient/tests/unit/osc/v2/logging/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/tests/unit/osc/v2/logging/fakes.py b/neutronclient/tests/unit/osc/v2/logging/fakes.py new file mode 100644 index 000000000..856838fee --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/logging/fakes.py @@ -0,0 +1,79 @@ +# Copyright 2017 FUJITSU LIMITED +# 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 collections +import copy +import uuid + +import mock + + +class FakeLogging(object): + + def create(self, attrs={}): + """Create a fake network logs + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A OrderedDict faking the network log + """ + self.ordered.update(attrs) + return copy.deepcopy(self.ordered) + + def bulk_create(self, attrs=None, count=2): + """Create multiple fake network logs + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of network logs to fake + :return: + A list of dictionaries faking the network logs + """ + return [self.create(attrs=attrs) for i in range(0, count)] + + def get(self, attrs=None, count=2): + """Create multiple fake network logs + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of network logs to fake + :return: + A list of dictionaries faking the network log + """ + if attrs is None: + self.attrs = self.bulk_create(count=count) + return mock.Mock(side_effect=attrs) + + +class NetworkLog(FakeLogging): + """Fake one or more network log""" + + def __init__(self): + super(NetworkLog, self).__init__() + self.ordered = collections.OrderedDict(( + ('id', 'log-id-' + uuid.uuid4().hex), + ('description', 'my-desc-' + uuid.uuid4().hex), + ('enabled', False), + ('name', 'my-log-' + uuid.uuid4().hex), + ('target_id', None), + ('project_id', 'project-id-' + uuid.uuid4().hex), + ('resource_id', None), + ('resource_type', 'security_group'), + ('event', 'all'), + )) diff --git a/neutronclient/tests/unit/osc/v2/logging/test_network_log.py b/neutronclient/tests/unit/osc/v2/logging/test_network_log.py new file mode 100644 index 000000000..bceaf32e1 --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/logging/test_network_log.py @@ -0,0 +1,658 @@ +# Copyright 2017 FUJITSU LIMITED +# 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 copy + +import mock +from osc_lib import exceptions +from osc_lib.tests import utils +import testtools + +from neutronclient.osc import utils as osc_utils +from neutronclient.osc.v2.logging import network_log +from neutronclient.tests.unit.osc.v2 import fakes as test_fakes +from neutronclient.tests.unit.osc.v2.logging import fakes + + +_log = fakes.NetworkLog().create() +RES_TYPE_SG = 'security_group' +CONVERT_MAP = { + 'project': 'project_id', + 'enable': 'enabled', + 'disable': 'enabled', + 'target': 'target_id', + 'resource': 'resource_id', + 'event': 'event', +} + + +def _generate_data(ordered_dict=None, data=None): + source = ordered_dict if ordered_dict else _log + if data: + source.update(data) + return tuple(source[key] for key in source) + + +def _generate_req_and_res(verifylist): + request = dict(verifylist) + response = copy.deepcopy(_log) + for key, val in verifylist: + converted = CONVERT_MAP.get(key, key) + del request[key] + if key == 'enable' and val: + new_value = True + elif key == 'disable' and val: + new_value = False + else: + new_value = val + request[converted] = new_value + response[converted] = new_value + return request, response + + +class TestNetworkLog(test_fakes.TestNeutronClientOSCV2): + + def check_results(self, headers, data, exp_req, is_list=False): + if is_list: + req_body = {'logs': [exp_req]} + else: + req_body = {'log': exp_req} + self.mocked.assert_called_once_with(req_body) + self.assertEqual(self.ordered_headers, headers) + self.assertEqual(self.ordered_data, data) + + def setUp(self): + super(TestNetworkLog, self).setUp() + self.neutronclient.find_resource = mock.Mock() + self.neutronclient.find_resource.side_effect = \ + lambda x, y, **k: {'id': y} + osc_utils.find_project = mock.Mock() + osc_utils.find_project.id = _log['project_id'] + self.res = _log + self.headers = ( + 'ID', + 'Description', + 'Enabled', + 'Name', + 'Target', + 'Project', + 'Resource', + 'Type', + 'Event', + ) + self.data = _generate_data() + self.ordered_headers = ( + 'Description', + 'Enabled', + 'Event', + 'ID', + 'Name', + 'Project', + 'Resource', + 'Target', + 'Type', + ) + self.ordered_data = ( + _log['description'], + _log['enabled'], + _log['event'], + _log['id'], + _log['name'], + _log['project_id'], + _log['resource_id'], + _log['target_id'], + _log['resource_type'], + ) + self.ordered_columns = ( + 'description', + 'enabled', + 'event', + 'id', + 'name', + 'project_id', + 'resource_id', + 'target_id', + 'resource_type', + ) + + +class TestCreateNetworkLog(TestNetworkLog): + + def setUp(self): + super(TestCreateNetworkLog, self).setUp() + self.neutronclient.create_network_log = mock.Mock( + return_value={'log': _log}) + self.mocked = self.neutronclient.create_network_log + self.cmd = network_log.CreateNetworkLog(self.app, self.namespace) + + def _update_expect_response(self, request, response): + """Set expected request and response + + :param request + A dictionary of request body(dict of verifylist) + :param response + A OrderedDict of request body + """ + # Update response body + self.neutronclient.create_network_log.return_value = \ + {'log': dict(response)} + osc_utils.find_project.return_value.id = response['project_id'] + # Update response(finally returns 'data') + self.data = _generate_data(ordered_dict=response) + self.ordered_data = tuple( + response[column] for column in self.ordered_columns + ) + + def _set_all_params(self, args={}): + name = args.get('name', 'my-log') + desc = args.get('description', 'my-description-for-log') + event = args.get('event', 'ACCEPT') + resource = args.get('resource', 'id-target-log') + target = args.get('target', 'id-target-log') + resource_type = args.get('resource_type', 'security_group') + project = args.get('project_id', 'id-my-project') + + arglist = [ + name, + '--description', desc, + '--enable', + '--target', target, + '--resource', resource, + '--event', event, + '--project', project, + '--resource-type', resource_type, + ] + verifylist = [ + ('description', desc), + ('enable', True), + ('event', event), + ('name', name), + ('target', target), + ('project', project), + ('resource', target), + ('resource_type', resource_type), + ] + return arglist, verifylist + + def _test_create_with_all_params(self, args={}): + arglist, verifylist = self._set_all_params(args) + request, response = _generate_req_and_res(verifylist) + self._update_expect_response(request, response) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.check_results(headers, data, request) + + def test_create_with_no_options_and_raise(self): + arglist = [] + verifylist = [] + + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_create_with_mandatory_params(self): + name = self.res['name'] + arglist = [ + name, + '--resource-type', RES_TYPE_SG, + ] + verifylist = [ + ('name', name), + ('resource_type', RES_TYPE_SG), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + expect = { + 'name': self.res['name'], + 'resource_type': self.res['resource_type'], + } + self.mocked.assert_called_once_with({'log': expect}) + self.assertEqual(self.ordered_headers, headers) + self.assertEqual(self.ordered_data, data) + + def test_create_with_disable(self): + name = self.res['name'] + arglist = [ + name, + '--resource-type', RES_TYPE_SG, + '--disable', + ] + verifylist = [ + ('name', name), + ('resource_type', RES_TYPE_SG), + ('disable', True), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + expect = { + 'name': self.res['name'], + 'resource_type': self.res['resource_type'], + 'enabled': False, + } + self.mocked.assert_called_once_with({'log': expect}) + self.assertEqual(self.ordered_headers, headers) + self.assertEqual(self.ordered_data, data) + + def test_create_with_all_params(self): + self._test_create_with_all_params() + + def test_create_with_all_params_event_drop(self): + self._test_create_with_all_params({'event': 'DROP'}) + + def test_create_with_all_params_event_all(self): + self._test_create_with_all_params({'event': 'ALL'}) + + def test_create_with_all_params_except_event(self): + arglist, verifylist = self._set_all_params({'event': ''}) + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_create_with_all_params_event_upper_capitalized(self): + for event in ('all', 'All', 'dROP', 'accePt', 'accept', 'drop'): + arglist, verifylist = self._set_all_params({'event': event}) + self.assertRaises( + testtools.matchers._impl.MismatchError, + self.check_parser, self.cmd, arglist, verifylist) + + def test_create_with_all_params_resource_type_upper_capitalized(self): + for res_type in ('SECURITY_GROUP', 'Security_group', 'security_Group'): + arglist, verifylist = self._set_all_params( + {'resource_type': res_type}) + self.assertRaises( + testtools.matchers._impl.MismatchError, + self.check_parser, self.cmd, arglist, verifylist) + + +class TestListNetworkLog(TestNetworkLog): + + def _setup_summary(self, expect=None): + event = 'Event: ' + self.res['event'].upper() + target = 'Logged: (None specified)' + if expect: + if expect.get('event'): + event = expect['event'] + if expect.get('resource'): + target = expect['resource'] + summary = ',\n'.join([event, target]) + self.short_data = ( + expect['id'] if expect else self.res['id'], + expect['enabled'] if expect else self.res['enabled'], + expect['name'] if expect else self.res['name'], + expect['resource_type'] if expect else self.res['resource_type'], + summary + ) + + def setUp(self): + super(TestListNetworkLog, self).setUp() + self.cmd = network_log.ListNetworkLog(self.app, self.namespace) + + self.short_header = ( + 'ID', + 'Enabled', + 'Name', + 'Type', + 'Summary', + ) + self._setup_summary() + self.neutronclient.list_network_logs = mock.Mock( + return_value={'logs': [self.res]}) + self.mocked = self.neutronclient.list_network_logs + + def test_list_with_long_option(self): + arglist = ['--long'] + verifylist = [('long', True)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.headers), headers) + self.assertEqual([self.data], list(data)) + + def test_list_with_no_option(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.short_header), headers) + self.assertEqual([self.short_data], list(data)) + + def test_list_with_target_and_resource(self): + arglist = [] + verifylist = [] + target_id = 'aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaaaaaaa' + resource_id = 'bbbbbbbb-bbbb-bbbb-bbbbbbbbbbbbbbbbb' + log = fakes.NetworkLog().create({ + 'target_id': target_id, + 'resource_id': resource_id}) + self.mocked.return_value = {'logs': [log]} + logged = 'Logged: (security_group) %(res_id)s on (port) %(t_id)s' % { + 'res_id': resource_id, 't_id': target_id} + expect_log = copy.deepcopy(log) + expect_log.update({ + 'resource': logged, + 'event': 'Event: ALL'}) + self._setup_summary(expect=expect_log) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.short_header), headers) + self.assertEqual([self.short_data], list(data)) + + def test_list_with_resource(self): + arglist = [] + verifylist = [] + resource_id = 'bbbbbbbb-bbbb-bbbb-bbbbbbbbbbbbbbbbb' + log = fakes.NetworkLog().create({'resource_id': resource_id}) + self.mocked.return_value = {'logs': [log]} + logged = 'Logged: (security_group) %s' % resource_id + expect_log = copy.deepcopy(log) + expect_log.update({ + 'resource': logged, + 'event': 'Event: ALL'}) + self._setup_summary(expect=expect_log) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.short_header), headers) + self.assertEqual([self.short_data], list(data)) + + def test_list_with_target(self): + arglist = [] + verifylist = [] + target_id = 'aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaaaaaaa' + log = fakes.NetworkLog().create({'target_id': target_id}) + self.mocked.return_value = {'logs': [log]} + logged = 'Logged: (port) %s' % target_id + expect_log = copy.deepcopy(log) + expect_log.update({ + 'resource': logged, + 'event': 'Event: ALL'}) + self._setup_summary(expect=expect_log) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.short_header), headers) + self.assertEqual([self.short_data], list(data)) + + +class TestShowNetworkLog(TestNetworkLog): + + def setUp(self): + super(TestShowNetworkLog, self).setUp() + self.neutronclient.show_network_log = mock.Mock( + return_value={'log': self.res}) + self.mocked = self.neutronclient.show_network_log + self.cmd = network_log.ShowNetworkLog(self.app, self.namespace) + + def test_show_filtered_by_id_or_name(self): + target = self.res['id'] + arglist = [target] + verifylist = [('network_log', target)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with(target) + self.assertEqual(self.ordered_headers, headers) + self.assertEqual(self.ordered_data, data) + + +class TestSetNetworkLog(TestNetworkLog): + + def setUp(self): + super(TestSetNetworkLog, self).setUp() + self.neutronclient.update_network_log = mock.Mock( + return_value={'log': self.res}) + self.mocked = self.neutronclient.update_network_log + self.cmd = network_log.SetNetworkLog(self.app, self.namespace) + + def test_set_name(self): + target = self.res['id'] + update = 'change' + arglist = [target, '--name', update] + verifylist = [ + ('network_log', target), + ('name', update), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {'log': {'name': update}}) + self.assertIsNone(result) + + def test_set_description(self): + target = self.res['id'] + update = 'change-desc' + arglist = [target, '--description', update] + verifylist = [ + ('network_log', target), + ('description', update), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {'log': {'description': update}}) + self.assertIsNone(result) + + def test_set_enable(self): + target = self.res['id'] + arglist = [target, '--enable'] + verifylist = [ + ('network_log', target), + ('enable', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {'log': {'enabled': True}}) + self.assertIsNone(result) + + def test_set_disable(self): + target = self.res['id'] + arglist = [target, '--disable'] + verifylist = [ + ('network_log', target), + ('disable', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {'log': {'enabled': False}}) + self.assertIsNone(result) + + # Illegal tests + def test_illegal_set_resource_type(self): + target = self.res['id'] + resource_type = 'security_group' + arglist = [target, '--resource-type', resource_type] + verifylist = [ + ('network_log', target), + ('resource_type', resource_type), + ] + + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_illegal_set_event(self): + target = self.res['id'] + for event in ['all', 'accept', 'drop']: + arglist = [target, '--event', event] + verifylist = [ + ('network_log', target), + ('event', event), + ] + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_illegal_set_resource_id(self): + target = self.res['id'] + resource_id = 'resource-id-for-logged-target' + arglist = [target, '--resource', resource_id] + verifylist = [ + ('network_log', target), + ('resource', resource_id), + ] + + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_illegal_set_project(self): + target = self.res['id'] + arglist = [ + target, + '--project', + ] + verifylist = [ + ('network_log', target), + ('project', 'other-project'), + ] + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_illegal_set_project_domain(self): + target = self.res['id'] + arglist = [ + target, + '--project-domain', + ] + verifylist = [ + ('network_log', target), + ('project_domain', 'other-project-domain'), + ] + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_illegal_set_and_raises(self): + self.neutronclient.update_network_log = mock.Mock( + side_effect=Exception) + target = self.res['id'] + arglist = [target, '--name', 'my-name'] + verifylist = [('network_log', target), ('name', 'my-name')] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args) + + +class TestDeleteNetworkLog(TestNetworkLog): + + def setUp(self): + super(TestDeleteNetworkLog, self).setUp() + self.neutronclient.delete_network_log = mock.Mock( + return_value={'log': self.res}) + self.mocked = self.neutronclient.delete_network_log + self.cmd = network_log.DeleteNetworkLog(self.app, self.namespace) + + def test_delete_with_one_resource(self): + target = self.res['id'] + arglist = [target] + verifylist = [('network_log', [target])] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with(target) + self.assertIsNone(result) + + def test_delete_with_multiple_resources(self): + target1 = 'target1' + target2 = 'target2' + arglist = [target1, target2] + verifylist = [('network_log', [target1, target2])] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) + + self.assertEqual(2, self.mocked.call_count) + for idx, reference in enumerate([target1, target2]): + actual = ''.join(self.mocked.call_args_list[idx][0]) + self.assertEqual(reference, actual) + + def test_delete_with_no_exist_id(self): + self.neutronclient.find_resource.side_effect = Exception + target = 'not_exist' + arglist = [target] + verifylist = [('network_log', [target])] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args) + + +class TestLoggableResource(test_fakes.TestNeutronClientOSCV2): + + def check_results(self, headers, data, exp_req, is_list=False): + if is_list: + req_body = {'logs': [exp_req]} + else: + req_body = {'log': exp_req} + self.mocked.assert_called_once_with(req_body) + self.assertEqual(self.ordered_headers, headers) + self.assertEqual(self.ordered_data, data) + + def setUp(self): + super(TestLoggableResource, self).setUp() + self.headers = ('Supported types',) + self.data = ('security_group', ) + + +class TestListLoggableResource(TestLoggableResource): + + def setUp(self): + super(TestListLoggableResource, self).setUp() + self.cmd = network_log.ListLoggableResource(self.app, self.namespace) + + loggables = { + "loggable_resources": [{"type": "security_group"}] + } + self.neutronclient.list_network_loggable_resources = mock.Mock( + return_value=loggables) + self.mocked = self.neutronclient.list_network_loggable_resources + + def test_list_with_long_option(self): + arglist = ['--long'] + verifylist = [('long', True)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.headers), headers) + self.assertEqual([self.data], list(data)) + + def test_list_with_no_option(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.headers), headers) + self.assertEqual([self.data], list(data)) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 23847058e..7c6dab758 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -1,5 +1,6 @@ # Copyright 2012 OpenStack Foundation. # Copyright 2015 Hewlett-Packard Development Company, L.P. +# Copyright 2017 FUJITSU LIMITED # All Rights Reserved # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -649,6 +650,9 @@ class Client(ClientBase): bgpvpn_router_associations_path = "/bgpvpn/bgpvpns/%s/router_associations" bgpvpn_router_association_path =\ "/bgpvpn/bgpvpns/%s/router_associations/%s" + network_logs_path = "/log/logs" + network_log_path = "/log/logs/%s" + network_loggables_path = "/log/loggable-resources" # API has no way to report plurals, so we have to hard code them EXTED_PLURALS = {'routers': 'router', @@ -707,6 +711,8 @@ class Client(ClientBase): 'port_pair_groups': 'port_pair_group', 'port_chains': 'port_chain', 'service_graphs': 'service_graph', + 'logs': 'log', + 'loggable_resources': 'loggable_resource', } def list_ext(self, collection, path, retrieve_all, **_params): @@ -2286,6 +2292,32 @@ def show_sfc_service_graph(self, service_graph, **_params): return self.get(self.sfc_service_graph_path % service_graph, params=_params) + def create_network_log(self, body=None): + """Create a network log.""" + return self.post(self.network_logs_path, body=body) + + def delete_network_log(self, net_log): + """Delete a network log.""" + return self.delete(self.network_log_path % net_log) + + def list_network_logs(self, retrieve_all=True, **_params): + """Fetch a list of all network logs.""" + return self.list( + 'logs', self.network_logs_path, retrieve_all, **_params) + + def show_network_log(self, net_log, **_params): + """Fetch information for a certain network log.""" + return self.get(self.network_log_path % net_log, params=_params) + + def update_network_log(self, net_log, body=None): + """Update a network log.""" + return self.put(self.network_log_path % net_log, body=body) + + def list_network_loggable_resources(self, retrieve_all=True, **_params): + """Fetch a list of supported resource types for network log.""" + return self.list('loggable_resources', self.network_loggables_path, + retrieve_all, **_params) + def __init__(self, **kwargs): """Initialize a new client for the Neutron v2.0 API.""" super(Client, self).__init__(**kwargs) diff --git a/releasenotes/notes/support-logging-cli-cd02d3bb03367106.yaml b/releasenotes/notes/support-logging-cli-cd02d3bb03367106.yaml new file mode 100644 index 000000000..415c30cd7 --- /dev/null +++ b/releasenotes/notes/support-logging-cli-cd02d3bb03367106.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + CLI support for 'Logging' feature, which enable to collect packet logs + for specified resource. Currently, only security-group can be logged. diff --git a/setup.cfg b/setup.cfg index 4385a2d1d..9e0e45c74 100644 --- a/setup.cfg +++ b/setup.cfg @@ -125,6 +125,13 @@ openstack.neutronclient.v2 = bgpvpn_router_association_list = neutronclient.osc.v2.networking_bgpvpn.router_association:ListBgpvpnRouterAssoc bgpvpn_router_association_show = neutronclient.osc.v2.networking_bgpvpn.router_association:ShowBgpvpnRouterAssoc + network_loggable_resources_list = neutronclient.osc.v2.logging.network_log:ListLoggableResource + network_log_create = neutronclient.osc.v2.logging.network_log:CreateNetworkLog + network_log_delete = neutronclient.osc.v2.logging.network_log:DeleteNetworkLog + network_log_list = neutronclient.osc.v2.logging.network_log:ListNetworkLog + network_log_set = neutronclient.osc.v2.logging.network_log:SetNetworkLog + network_log_show = neutronclient.osc.v2.logging.network_log:ShowNetworkLog + neutron.cli.v2 = bash-completion = neutronclient.shell:BashCompletionCommand @@ -409,7 +416,6 @@ neutron.cli.v2 = vpn-ikepolicy-update = neutronclient.neutron.v2_0.vpn.ikepolicy:UpdateIKEPolicy vpn-ikepolicy-delete = neutronclient.neutron.v2_0.vpn.ikepolicy:DeleteIKEPolicy - [build_sphinx] all_files = 1 build-dir = doc/build From dd044bd92e8b7ab41aba47488d437e771a2e1339 Mon Sep 17 00:00:00 2001 From: Cao Xuan Hoang Date: Thu, 2 Mar 2017 10:46:29 +0700 Subject: [PATCH 636/845] Add VPNaaS commands for OSC plugin Closes-Bug: 1669252 Change-Id: I447f5c50725fc0d9d8a1574ad5e28772f472fba9 --- doc/source/cli/osc/v2/vpn-endpoint-group.rst | 11 + doc/source/cli/osc/v2/vpn-ike-policy.rst | 12 + doc/source/cli/osc/v2/vpn-ipsec-policy.rst | 11 + .../cli/osc/v2/vpn-ipsec-site-connection.rst | 10 + doc/source/cli/osc/v2/vpn-service.rst | 11 + neutronclient/osc/v2/vpnaas/endpoint_group.py | 216 ++++++++++ neutronclient/osc/v2/vpnaas/ikepolicy.py | 243 +++++++++++ .../osc/v2/vpnaas/ipsec_site_connection.py | 372 +++++++++++++++++ neutronclient/osc/v2/vpnaas/ipsecpolicy.py | 242 +++++++++++ neutronclient/osc/v2/vpnaas/utils.py | 112 +++++ neutronclient/osc/v2/vpnaas/vpnservice.py | 235 +++++++++++ .../tests/unit/osc/v2/vpnaas/common.py | 157 ++++++++ .../tests/unit/osc/v2/vpnaas/fakes.py | 178 ++++++++ .../unit/osc/v2/vpnaas/test_endpoint_group.py | 257 ++++++++++++ .../unit/osc/v2/vpnaas/test_ikepolicy.py | 303 ++++++++++++++ .../v2/vpnaas/test_ipsec_site_connection.py | 381 ++++++++++++++++++ .../unit/osc/v2/vpnaas/test_ipsecpolicy.py | 303 ++++++++++++++ .../unit/osc/v2/vpnaas/test_vpnservice.py | 305 ++++++++++++++ .../support-vpnaas-cli-9478fb7cfe603e26.yaml | 5 + setup.cfg | 30 ++ 20 files changed, 3394 insertions(+) create mode 100644 doc/source/cli/osc/v2/vpn-endpoint-group.rst create mode 100644 doc/source/cli/osc/v2/vpn-ike-policy.rst create mode 100644 doc/source/cli/osc/v2/vpn-ipsec-policy.rst create mode 100644 doc/source/cli/osc/v2/vpn-ipsec-site-connection.rst create mode 100644 doc/source/cli/osc/v2/vpn-service.rst create mode 100644 neutronclient/osc/v2/vpnaas/endpoint_group.py create mode 100644 neutronclient/osc/v2/vpnaas/ikepolicy.py create mode 100644 neutronclient/osc/v2/vpnaas/ipsec_site_connection.py create mode 100644 neutronclient/osc/v2/vpnaas/ipsecpolicy.py create mode 100644 neutronclient/osc/v2/vpnaas/utils.py create mode 100644 neutronclient/osc/v2/vpnaas/vpnservice.py create mode 100644 neutronclient/tests/unit/osc/v2/vpnaas/common.py create mode 100644 neutronclient/tests/unit/osc/v2/vpnaas/fakes.py create mode 100644 neutronclient/tests/unit/osc/v2/vpnaas/test_endpoint_group.py create mode 100644 neutronclient/tests/unit/osc/v2/vpnaas/test_ikepolicy.py create mode 100644 neutronclient/tests/unit/osc/v2/vpnaas/test_ipsec_site_connection.py create mode 100644 neutronclient/tests/unit/osc/v2/vpnaas/test_ipsecpolicy.py create mode 100644 neutronclient/tests/unit/osc/v2/vpnaas/test_vpnservice.py create mode 100644 releasenotes/notes/support-vpnaas-cli-9478fb7cfe603e26.yaml diff --git a/doc/source/cli/osc/v2/vpn-endpoint-group.rst b/doc/source/cli/osc/v2/vpn-endpoint-group.rst new file mode 100644 index 000000000..9581d8b4a --- /dev/null +++ b/doc/source/cli/osc/v2/vpn-endpoint-group.rst @@ -0,0 +1,11 @@ +================== +VPN Endpoint Group +================== + +The **Endpoint Group** is used to configure multiple local and remote subnets +in vpnservice object. + +Network v2 + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: vpn endpoint group * diff --git a/doc/source/cli/osc/v2/vpn-ike-policy.rst b/doc/source/cli/osc/v2/vpn-ike-policy.rst new file mode 100644 index 000000000..3e38a5c7c --- /dev/null +++ b/doc/source/cli/osc/v2/vpn-ike-policy.rst @@ -0,0 +1,12 @@ +============== +VPN IKE Policy +============== + +The **IKE Policy** is used for phases one and two negotiation of the +VPN connection. You can specify both the authentication and encryption +algorithms for connections. + +Network v2 + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: vpn ike policy * diff --git a/doc/source/cli/osc/v2/vpn-ipsec-policy.rst b/doc/source/cli/osc/v2/vpn-ipsec-policy.rst new file mode 100644 index 000000000..c8a5f3beb --- /dev/null +++ b/doc/source/cli/osc/v2/vpn-ipsec-policy.rst @@ -0,0 +1,11 @@ +================ +VPN IPsec Policy +================ + +The **IPsec Policy** specifies the authentication and encryption algorithms +and encapsulation mode to use for the established VPN connection. + +Network v2 + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: vpn ipsec policy * diff --git a/doc/source/cli/osc/v2/vpn-ipsec-site-connection.rst b/doc/source/cli/osc/v2/vpn-ipsec-site-connection.rst new file mode 100644 index 000000000..0244e73ee --- /dev/null +++ b/doc/source/cli/osc/v2/vpn-ipsec-site-connection.rst @@ -0,0 +1,10 @@ +========================= +VPN IPsec Site Connection +========================= + +Creates a site-to-site **IPsec Site Connection** for a VPN service. + +Network v2 + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: vpn ipsec site connection * diff --git a/doc/source/cli/osc/v2/vpn-service.rst b/doc/source/cli/osc/v2/vpn-service.rst new file mode 100644 index 000000000..b412555c8 --- /dev/null +++ b/doc/source/cli/osc/v2/vpn-service.rst @@ -0,0 +1,11 @@ +=========== +VPN Service +=========== + +The **VPN Service** is associated with a router. After you +create the service, it can contain multiple VPN connections. + +Network v2 + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: vpn service * diff --git a/neutronclient/osc/v2/vpnaas/endpoint_group.py b/neutronclient/osc/v2/vpnaas/endpoint_group.py new file mode 100644 index 000000000..269da97ea --- /dev/null +++ b/neutronclient/osc/v2/vpnaas/endpoint_group.py @@ -0,0 +1,216 @@ +# Copyright 2017 FUJITSU LIMITED +# 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. +# + +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils +from oslo_log import log as logging + +from neutronclient._i18n import _ +from neutronclient.osc import utils as osc_utils + + +LOG = logging.getLogger(__name__) + +_attr_map = ( + ('id', 'ID', osc_utils.LIST_BOTH), + ('name', 'Name', osc_utils.LIST_BOTH), + ('type', 'Type', osc_utils.LIST_BOTH), + ('endpoints', 'Endpoints', osc_utils.LIST_BOTH), + ('description', 'Description', osc_utils.LIST_LONG_ONLY), + ('tenant_id', 'Project', osc_utils.LIST_LONG_ONLY), +) + + +def _get_common_parser(parser): + parser.add_argument( + '--description', + metavar='', + help=_('Description for the endpoint group')) + return parser + + +def _get_common_attrs(client_manager, parsed_args, is_create=True): + attrs = {} + if is_create: + if parsed_args.project is not None: + attrs['tenant_id'] = osc_utils.find_project( + client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + if parsed_args.description: + attrs['description'] = parsed_args.description + return attrs + + +class CreateEndpointGroup(command.ShowOne): + _description = _("Create an endpoint group") + + def get_parser(self, prog_name): + parser = super(CreateEndpointGroup, self).get_parser(prog_name) + _get_common_parser(parser) + parser.add_argument( + 'name', + metavar='', + help=_('Name for the endpoint group')) + parser.add_argument( + '--type', + required=True, + help=_('Type of endpoints in group (e.g. subnet, cidr)')) + parser.add_argument( + '--value', + action='append', + dest='endpoints', + required=True, + help=_('Endpoint(s) for the group. Must all be of the same type. ' + '(--value) option can be repeated')) + osc_utils.add_project_owner_option_to_parser(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + attrs = _get_common_attrs(self.app.client_manager, parsed_args) + if parsed_args.name: + attrs['name'] = str(parsed_args.name) + attrs['type'] = parsed_args.type + if parsed_args.type == 'subnet': + _subnet_ids = [client.find_resource( + 'subnet', + endpoint, + cmd_resource='subnet')['id'] + for endpoint in parsed_args.endpoints] + attrs['endpoints'] = _subnet_ids + else: + attrs['endpoints'] = parsed_args.endpoints + obj = client.create_endpoint_group( + {'endpoint_group': attrs})['endpoint_group'] + columns, display_columns = osc_utils.get_columns(obj, _attr_map) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteEndpointGroup(command.Command): + _description = _("Delete endpoint group(s)") + + def get_parser(self, prog_name): + parser = super(DeleteEndpointGroup, self).get_parser(prog_name) + parser.add_argument( + 'endpoint_group', + metavar='', + nargs='+', + help=_('Endpoint group(s) to delete (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + result = 0 + for endpoint in parsed_args.endpoint_group: + try: + endpoint_id = client.find_resource( + 'endpoint_group', + endpoint, + cmd_resource='endpoint_group')['id'] + client.delete_endpoint_group(endpoint_id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete endpoint group with " + "name or ID '%(endpoint_group)s': %(e)s"), + {'endpoint_group': endpoint, 'e': e}) + + if result > 0: + total = len(parsed_args.endpoint_group) + msg = (_("%(result)s of %(total)s endpoint group failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) + + +class ListEndpointGroup(command.Lister): + _description = _("List endpoint groups that belong to a given project") + + def get_parser(self, prog_name): + parser = super(ListEndpointGroup, self).get_parser(prog_name) + parser.add_argument( + '--long', + action='store_true', + default=False, + help=_("List additional fields in output") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + obj = client.list_endpoint_groups()['endpoint_groups'] + headers, columns = osc_utils.get_column_definitions( + _attr_map, long_listing=parsed_args.long) + return (headers, (utils.get_dict_properties(s, columns) for s in obj)) + + +class SetEndpointGroup(command.Command): + _description = _("Set endpoint group properties") + + def get_parser(self, prog_name): + parser = super(SetEndpointGroup, self).get_parser(prog_name) + _get_common_parser(parser) + parser.add_argument( + '--name', + metavar='', + help=_('Set a name for the endpoint group')) + parser.add_argument( + 'endpoint_group', + metavar='', + help=_('Endpoint group to set (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + attrs = _get_common_attrs(self.app.client_manager, + parsed_args, is_create=False) + if parsed_args.name: + attrs['name'] = str(parsed_args.name) + endpoint_id = client.find_resource( + 'endpoint_group', parsed_args.endpoint_group, + cmd_resource='endpoint_group')['id'] + try: + client.update_endpoint_group(endpoint_id, + {'endpoint_group': attrs}) + except Exception as e: + msg = (_("Failed to set endpoint group " + "%(endpoint_group)s: %(e)s") + % {'endpoint_group': parsed_args.endpoint_group, 'e': e}) + raise exceptions.CommandError(msg) + + +class ShowEndpointGroup(command.ShowOne): + _description = _("Display endpoint group details") + + def get_parser(self, prog_name): + parser = super(ShowEndpointGroup, self).get_parser(prog_name) + parser.add_argument( + 'endpoint_group', + metavar='', + help=_('Endpoint group to display (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + endpoint_id = client.find_resource( + 'endpoint_group', parsed_args.endpoint_group, + cmd_resource='endpoint_group')['id'] + obj = client.show_endpoint_group(endpoint_id)['endpoint_group'] + columns, display_columns = osc_utils.get_columns(obj, _attr_map) + data = utils.get_dict_properties(obj, columns) + return (display_columns, data) diff --git a/neutronclient/osc/v2/vpnaas/ikepolicy.py b/neutronclient/osc/v2/vpnaas/ikepolicy.py new file mode 100644 index 000000000..28e6f6088 --- /dev/null +++ b/neutronclient/osc/v2/vpnaas/ikepolicy.py @@ -0,0 +1,243 @@ +# Copyright 2017 FUJITSU LIMITED +# 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. +# + +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils +from oslo_log import log as logging + +from neutronclient._i18n import _ +from neutronclient.common import utils as nc_utils +from neutronclient.osc import utils as osc_utils +from neutronclient.osc.v2.vpnaas import utils as vpn_utils + + +LOG = logging.getLogger(__name__) + +_attr_map = ( + ('id', 'ID', osc_utils.LIST_BOTH), + ('name', 'Name', osc_utils.LIST_BOTH), + ('auth_algorithm', 'Authentication Algorithm', osc_utils.LIST_BOTH), + ('encryption_algorithm', 'Encryption Algorithm', osc_utils.LIST_BOTH), + ('ike_version', 'IKE Version', osc_utils.LIST_BOTH), + ('pfs', 'Perfect Forward Secrecy (PFS)', osc_utils.LIST_BOTH), + ('description', 'Description', osc_utils.LIST_LONG_ONLY), + ('phase1_negotiation_mode', 'Phase1 Negotiation Mode', + osc_utils.LIST_LONG_ONLY), + ('tenant_id', 'Project', osc_utils.LIST_LONG_ONLY), + ('lifetime', 'Lifetime', osc_utils.LIST_LONG_ONLY), +) + + +def _convert_to_lowercase(string): + return string.lower() + + +def _get_common_parser(parser): + parser.add_argument( + '--description', + metavar='', + help=_('Description of the IKE policy')) + parser.add_argument( + '--auth-algorithm', + choices=['sha1', 'sha256', 'sha384', 'sha512'], + type=_convert_to_lowercase, + help=_('Authentication algorithm')) + parser.add_argument( + '--encryption-algorithm', + choices=['aes-128', '3des', 'aes-192', 'aes-256'], + type=_convert_to_lowercase, + help=_('Encryption algorithm')) + parser.add_argument( + '--phase1-negotiation-mode', + choices=['main'], + type=_convert_to_lowercase, + help=_('IKE Phase1 negotiation mode')) + parser.add_argument( + '--ike-version', + choices=['v1', 'v2'], + type=_convert_to_lowercase, + help=_('IKE version for the policy')) + parser.add_argument( + '--pfs', + choices=['group5', 'group2', 'group14'], + type=_convert_to_lowercase, + help=_('Perfect Forward Secrecy')) + parser.add_argument( + '--lifetime', + metavar="units=UNITS,value=VALUE", + type=nc_utils.str2dict_type(optional_keys=['units', 'value']), + help=vpn_utils.lifetime_help("IKE")) + return parser + + +def _get_common_attrs(client_manager, parsed_args, is_create=True): + attrs = {} + if is_create: + if 'project' in parsed_args and parsed_args.project is not None: + attrs['tenant_id'] = osc_utils.find_project( + client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + if parsed_args.description: + attrs['description'] = parsed_args.description + if parsed_args.auth_algorithm: + attrs['auth_algorithm'] = parsed_args.auth_algorithm + if parsed_args.encryption_algorithm: + attrs['encryption_algorithm'] = parsed_args.encryption_algorithm + if parsed_args.phase1_negotiation_mode: + attrs['phase1_negotiation_mode'] = parsed_args.phase1_negotiation_mode + if parsed_args.ike_version: + attrs['ike_version'] = parsed_args.ike_version + if parsed_args.pfs: + attrs['pfs'] = parsed_args.pfs + if parsed_args.lifetime: + vpn_utils.validate_lifetime_dict(parsed_args.lifetime) + attrs['lifetime'] = parsed_args.lifetime + return attrs + + +class CreateIKEPolicy(command.ShowOne): + _description = _("Create an IKE policy") + + def get_parser(self, prog_name): + parser = super(CreateIKEPolicy, self).get_parser(prog_name) + _get_common_parser(parser) + parser.add_argument( + 'name', + metavar='', + help=_('Name of the IKE policy')) + osc_utils.add_project_owner_option_to_parser(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + attrs = _get_common_attrs(self.app.client_manager, parsed_args) + if parsed_args.name: + attrs['name'] = str(parsed_args.name) + obj = client.create_ikepolicy({'ikepolicy': attrs})['ikepolicy'] + columns, display_columns = osc_utils.get_columns(obj, _attr_map) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteIKEPolicy(command.Command): + _description = _("Delete IKE policy (policies)") + + def get_parser(self, prog_name): + parser = super(DeleteIKEPolicy, self).get_parser(prog_name) + parser.add_argument( + 'ikepolicy', + metavar='', + nargs='+', + help=_('IKE policy to delete (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + result = 0 + for ike in parsed_args.ikepolicy: + try: + ike_id = client.find_resource( + 'ikepolicy', ike, cmd_resource='ikepolicy')['id'] + client.delete_ikepolicy(ike_id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete IKE policy with " + "name or ID '%(ikepolicy)s': %(e)s"), + {'ikepolicy': ike, 'e': e}) + + if result > 0: + total = len(parsed_args.ikepolicy) + msg = (_("%(result)s of %(total)s IKE policy failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) + + +class ListIKEPolicy(command.Lister): + _description = _("List IKE policies that belong to a given project") + + def get_parser(self, prog_name): + parser = super(ListIKEPolicy, self).get_parser(prog_name) + parser.add_argument( + '--long', + action='store_true', + help=_("List additional fields in output") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + obj = client.list_ikepolicies()['ikepolicies'] + headers, columns = osc_utils.get_column_definitions( + _attr_map, long_listing=parsed_args.long) + return (headers, (utils.get_dict_properties(s, columns) for s in obj)) + + +class SetIKEPolicy(command.Command): + _description = _("Set IKE policy properties") + + def get_parser(self, prog_name): + parser = super(SetIKEPolicy, self).get_parser(prog_name) + _get_common_parser(parser) + parser.add_argument( + '--name', + metavar='', + help=_('Name of the IKE policy')) + parser.add_argument( + 'ikepolicy', + metavar='', + help=_('IKE policy to set (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + attrs = _get_common_attrs(self.app.client_manager, + parsed_args, is_create=False) + if parsed_args.name: + attrs['name'] = parsed_args.name + ike_id = client.find_resource( + 'ikepolicy', parsed_args.ikepolicy, + cmd_resource='ikepolicy')['id'] + try: + client.update_ikepolicy(ike_id, {'ikepolicy': attrs}) + except Exception as e: + msg = (_("Failed to set IKE policy '%(ike)s': %(e)s") + % {'ike': parsed_args.ikepolicy, 'e': e}) + raise exceptions.CommandError(msg) + + +class ShowIKEPolicy(command.ShowOne): + _description = _("Display IKE policy details") + + def get_parser(self, prog_name): + parser = super(ShowIKEPolicy, self).get_parser(prog_name) + parser.add_argument( + 'ikepolicy', + metavar='', + help=_('IKE policy to display (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + ike_id = client.find_resource( + 'ikepolicy', parsed_args.ikepolicy, + cmd_resource='ikepolicy')['id'] + obj = client.show_ikepolicy(ike_id)['ikepolicy'] + columns, display_columns = osc_utils.get_columns(obj, _attr_map) + data = utils.get_dict_properties(obj, columns) + return (display_columns, data) diff --git a/neutronclient/osc/v2/vpnaas/ipsec_site_connection.py b/neutronclient/osc/v2/vpnaas/ipsec_site_connection.py new file mode 100644 index 000000000..8dd98a9e4 --- /dev/null +++ b/neutronclient/osc/v2/vpnaas/ipsec_site_connection.py @@ -0,0 +1,372 @@ +# Copyright 2017 FUJITSU LIMITED +# 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. +# + +from osc_lib.cli import format_columns +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils +from oslo_log import log as logging + +from neutronclient._i18n import _ +from neutronclient.common import utils as nc_utils +from neutronclient.osc import utils as osc_utils +from neutronclient.osc.v2.vpnaas import utils as vpn_utils + + +LOG = logging.getLogger(__name__) + + +_formatters = { + 'peer_cidrs': format_columns.ListColumn +} + + +_attr_map = ( + ('id', 'ID', osc_utils.LIST_BOTH), + ('name', 'Name', osc_utils.LIST_BOTH), + ('peer_address', 'Peer Address', osc_utils.LIST_BOTH), + ('auth_mode', 'Authentication Algorithm', osc_utils.LIST_BOTH), + ('status', 'Status', osc_utils.LIST_BOTH), + ('tenant_id', 'Project', osc_utils.LIST_LONG_ONLY), + ('peer_cidrs', 'Peer CIDRs', osc_utils.LIST_LONG_ONLY), + ('vpnservice_id', 'VPN Service', osc_utils.LIST_LONG_ONLY), + ('ipsecpolicy_id', 'IPSec Policy', osc_utils.LIST_LONG_ONLY), + ('ikepolicy_id', 'IKE Policy', osc_utils.LIST_LONG_ONLY), + ('mtu', 'MTU', osc_utils.LIST_LONG_ONLY), + ('initiator', 'Initiator', osc_utils.LIST_LONG_ONLY), + ('admin_state_up', 'State', osc_utils.LIST_LONG_ONLY), + ('description', 'Description', osc_utils.LIST_LONG_ONLY), + ('psk', 'Pre-shared Key', osc_utils.LIST_LONG_ONLY), + ('route_mode', 'Route Mode', osc_utils.LIST_LONG_ONLY), + ('local_id', 'Local ID', osc_utils.LIST_LONG_ONLY), + ('peer_id', 'Peer ID', osc_utils.LIST_LONG_ONLY), + ('local_ep_group_id', 'Local Endpoint Group ID', osc_utils.LIST_LONG_ONLY), + ('peer_ep_group_id', 'Peer Endpoint Group ID', osc_utils.LIST_LONG_ONLY), +) + + +def _convert_to_lowercase(string): + return string.lower() + + +def _get_common_parser(parser, is_create=True): + parser.add_argument( + '--description', + metavar='', + help=_('Description for the connection')) + parser.add_argument( + '--dpd', + metavar="action=ACTION,interval=INTERVAL,timeout=TIMEOUT", + type=nc_utils.str2dict_type( + optional_keys=['action', 'interval', 'timeout']), + help=vpn_utils.dpd_help("IPsec connection")) + parser.add_argument( + '--mtu', + help=_('MTU size for the connection')) + parser.add_argument( + '--initiator', + choices=['bi-directional', 'response-only'], + type=_convert_to_lowercase, + help=_('Initiator state')) + peer_group = parser.add_mutually_exclusive_group() + peer_group.add_argument( + '--peer-cidr', + dest='peer_cidrs', + help=_('Remote subnet(s) in CIDR format. ' + 'Cannot be specified when using endpoint groups. Only ' + 'applicable, if subnet provided for VPN service.') + ) + peer_group.add_argument( + '--local-endpoint-group', + help=_('Local endpoint group (name or ID) with subnet(s) ' + 'for IPsec connection') + ) + parser.add_argument( + '--peer-endpoint-group', + help=_('Peer endpoint group (name or ID) with CIDR(s) for ' + 'IPSec connection')) + admin_group = parser.add_mutually_exclusive_group() + admin_group.add_argument( + '--enable', + action='store_true', + help=_("Enable IPSec site connection") + ) + admin_group.add_argument( + '--disable', + action='store_true', + help=_("Disable IPSec site connection") + ) + parser.add_argument( + '--local-id', + help=_('An ID to be used instead of the external IP ' + 'address for a virtual router')) + return parser + + +def _get_common_attrs(client_manager, parsed_args, is_create=True): + attrs = {} + if is_create: + if 'project' in parsed_args and parsed_args.project is not None: + attrs['tenant_id'] = osc_utils.find_project( + client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + if parsed_args.description: + attrs['description'] = str(parsed_args.description) + if parsed_args.mtu: + attrs['mtu'] = parsed_args.mtu + if parsed_args.enable: + attrs['admin_state_up'] = True + if parsed_args.disable: + attrs['admin_state_up'] = False + if parsed_args.initiator: + attrs['initiator'] = parsed_args.initiator + if parsed_args.dpd: + vpn_utils.validate_dpd_dict(parsed_args.dpd) + attrs['dpd'] = parsed_args.dpd + if parsed_args.local_endpoint_group: + _local_epg = client_manager.neutronclient.find_resource( + 'endpoint_group', + parsed_args.local_endpoint_group, + cmd_resource='endpoint_group')['id'] + attrs['local_ep_group_id'] = _local_epg + if parsed_args.peer_endpoint_group: + _peer_epg = client_manager.neutronclient.find_resource( + 'endpoint_group', + parsed_args.peer_endpoint_group, + cmd_resource='endpoint_group')['id'] + attrs['peer_ep_group_id'] = _peer_epg + if parsed_args.peer_cidrs: + attrs['peer_cidrs'] = parsed_args.peer_cidrs + if parsed_args.local_id: + attrs['local_id'] = parsed_args.local_id + return attrs + + +class CreateIPsecSiteConnection(command.ShowOne): + _description = _("Create an IPsec site connection") + + def get_parser(self, prog_name): + parser = super(CreateIPsecSiteConnection, self).get_parser(prog_name) + _get_common_parser(parser) + parser.add_argument( + '--peer-id', + required=True, + help=_('Peer router identity for authentication. Can be ' + 'IPv4/IPv6 address, e-mail address, key id, or FQDN')) + parser.add_argument( + '--peer-address', + required=True, + help=_('Peer gateway public IPv4/IPv6 address or FQDN')) + parser.add_argument( + '--psk', + required=True, + help=_('Pre-shared key string.')) + parser.add_argument( + '--vpnservice', + metavar='VPNSERVICE', + required=True, + help=_('VPN service instance associated with this ' + 'connection (name or ID)')) + parser.add_argument( + '--ikepolicy', + metavar='IKEPOLICY', + required=True, + help=_('IKE policy associated with this connection (name or ID)')) + parser.add_argument( + '--ipsecpolicy', + metavar='IPSECPOLICY', + required=True, + help=_('IPsec policy associated with this connection ' + '(name or ID)')) + parser.add_argument( + 'name', + metavar='', + help=_('Set friendly name for the connection')) + osc_utils.add_project_owner_option_to_parser(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + attrs = _get_common_attrs(self.app.client_manager, parsed_args) + if parsed_args.vpnservice: + _vpnservice_id = client.find_resource( + 'vpnservice', + parsed_args.vpnservice, + cmd_resource='vpnservice')['id'] + attrs['vpnservice_id'] = _vpnservice_id + if parsed_args.ikepolicy: + _ikepolicy_id = client.find_resource( + 'ikepolicy', + parsed_args.ikepolicy, + cmd_resource='ikepolicy')['id'] + attrs['ikepolicy_id'] = _ikepolicy_id + if parsed_args.ipsecpolicy: + _ipsecpolicy_id = client.find_resource( + 'ipsecpolicy', + parsed_args.ipsecpolicy, + cmd_resource='ipsecpolicy')['id'] + attrs['ipsecpolicy_id'] = _ipsecpolicy_id + if parsed_args.peer_id: + attrs['peer_id'] = parsed_args.peer_id + if parsed_args.peer_address: + attrs['peer_address'] = parsed_args.peer_address + if parsed_args.psk: + attrs['psk'] = parsed_args.psk + if parsed_args.name: + attrs['name'] = parsed_args.name + if (bool(parsed_args.local_endpoint_group) != + bool(parsed_args.peer_endpoint_group)): + message = _("You must specify both local and peer endpoint " + "groups") + raise exceptions.CommandError(message) + if not parsed_args.peer_cidrs and not parsed_args.local_endpoint_group: + message = _("You must specify endpoint groups or peer CIDR(s)") + raise exceptions.CommandError(message) + obj = client.create_ipsec_site_connection( + {'ipsec_site_connection': attrs})['ipsec_site_connection'] + columns, display_columns = osc_utils.get_columns(obj, _attr_map) + data = utils.get_dict_properties(obj, columns, formatters=_formatters) + return display_columns, data + + +class DeleteIPsecSiteConnection(command.Command): + _description = _("Delete IPsec site connection(s)") + + def get_parser(self, prog_name): + parser = super(DeleteIPsecSiteConnection, self).get_parser(prog_name) + parser.add_argument( + 'ipsec_site_connection', + metavar='', + nargs='+', + help=_('IPsec site connection to delete (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + result = 0 + for ipsec_conn in parsed_args.ipsec_site_connection: + try: + ipsec_con_id = client.find_resource( + 'ipsec_site_connection', + ipsec_conn, + cmd_resource='ipsec_site_connection')['id'] + client.delete_ipsec_site_connection(ipsec_con_id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete IPsec site connection with " + "name or ID '%(ipsec_site_conn)s': %(e)s"), + {'ipsec_site_conn': ipsec_conn, 'e': e}) + + if result > 0: + total = len(parsed_args.ipsec_site_connection) + msg = (_("%(result)s of %(total)s IPsec site connection failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) + + +class ListIPsecSiteConnection(command.Lister): + _description = _("List IPsec site connections " + "that belong to a given project") + + def get_parser(self, prog_name): + parser = super(ListIPsecSiteConnection, self).get_parser(prog_name) + parser.add_argument( + '--long', + action='store_true', + default=False, + help=_("List additional fields in output") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + obj = client.list_ipsec_site_connections()['ipsec_site_connections'] + headers, columns = osc_utils.get_column_definitions( + _attr_map, long_listing=parsed_args.long) + return (headers, (utils.get_dict_properties( + s, columns, formatters=_formatters) for s in obj)) + + +class SetIPsecSiteConnection(command.Command): + _description = _("Set IPsec site connection properties") + + def get_parser(self, prog_name): + parser = super(SetIPsecSiteConnection, self).get_parser(prog_name) + _get_common_parser(parser) + parser.add_argument( + '--peer-id', + help=_('Peer router identity for authentication. Can be ' + 'IPv4/IPv6 address, e-mail address, key id, or FQDN')) + parser.add_argument( + '--peer-address', + help=_('Peer gateway public IPv4/IPv6 address or FQDN')) + parser.add_argument( + '--name', + metavar='', + help=_('Set friendly name for the connection')) + parser.add_argument( + 'ipsec_site_connection', + metavar='', + help=_('IPsec site connection to set (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + attrs = _get_common_attrs(self.app.client_manager, + parsed_args, is_create=False) + if parsed_args.peer_id: + attrs['peer_id'] = parsed_args.peer_id + if parsed_args.peer_address: + attrs['peer_address'] = parsed_args.peer_address + if parsed_args.name: + attrs['name'] = parsed_args.name + ipsec_conn_id = client.find_resource( + 'ipsec_site_connection', parsed_args.ipsec_site_connection, + cmd_resource='ipsec_site_connection')['id'] + try: + client.update_ipsec_site_connection( + ipsec_conn_id, + {'ipsec_site_connection': attrs}) + except Exception as e: + msg = (_("Failed to set IPsec site " + "connection '%(ipsec_conn)s': %(e)s") + % {'ipsec_conn': parsed_args.ipsec_site_connection, 'e': e}) + raise exceptions.CommandError(msg) + + +class ShowIPsecSiteConnection(command.ShowOne): + _description = _("Show information of a given IPsec site connection") + + def get_parser(self, prog_name): + parser = super(ShowIPsecSiteConnection, self).get_parser(prog_name) + parser.add_argument( + 'ipsec_site_connection', + metavar='', + help=_('IPsec site connection to display (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + ipsec_site_id = client.find_resource( + 'ipsec_site_connection', parsed_args.ipsec_site_connection, + cmd_resource='ipsec_site_connection')['id'] + obj = client.show_ipsec_site_connection( + ipsec_site_id)['ipsec_site_connection'] + columns, display_columns = osc_utils.get_columns(obj, _attr_map) + data = utils.get_dict_properties(obj, columns, formatters=_formatters) + return (display_columns, data) diff --git a/neutronclient/osc/v2/vpnaas/ipsecpolicy.py b/neutronclient/osc/v2/vpnaas/ipsecpolicy.py new file mode 100644 index 000000000..43599f328 --- /dev/null +++ b/neutronclient/osc/v2/vpnaas/ipsecpolicy.py @@ -0,0 +1,242 @@ +# Copyright 2017 FUJITSU LIMITED +# 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. +# + +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils +from oslo_log import log as logging + +from neutronclient._i18n import _ +from neutronclient.common import utils as nc_utils +from neutronclient.osc import utils as osc_utils +from neutronclient.osc.v2.vpnaas import utils as vpn_utils + + +LOG = logging.getLogger(__name__) + +_attr_map = ( + ('id', 'ID', osc_utils.LIST_BOTH), + ('name', 'Name', osc_utils.LIST_BOTH), + ('auth_algorithm', 'Authentication Algorithm', osc_utils.LIST_BOTH), + ('encapsulation_mode', 'Encapsulation Mode', osc_utils.LIST_BOTH), + ('transform_protocol', 'Transform Protocol', osc_utils.LIST_BOTH), + ('encryption_algorithm', 'Encryption Algorithm', osc_utils.LIST_BOTH), + ('pfs', 'Perfect Forward Secrecy (PFS)', osc_utils.LIST_LONG_ONLY), + ('description', 'Description', osc_utils.LIST_LONG_ONLY), + ('tenant_id', 'Project', osc_utils.LIST_LONG_ONLY), + ('lifetime', 'Lifetime', osc_utils.LIST_LONG_ONLY), +) + + +def _convert_to_lowercase(string): + return string.lower() + + +def _get_common_parser(parser): + parser.add_argument( + '--description', + metavar='', + help=_('Description of the IPsec policy')) + parser.add_argument( + '--auth-algorithm', + choices=['sha1', 'sha256', 'sha384', 'sha512'], + type=_convert_to_lowercase, + help=_('Authentication algorithm for IPsec policy')) + parser.add_argument( + '--encapsulation-mode', + choices=['tunnel', 'transport'], + type=_convert_to_lowercase, + help=_('Encapsulation mode for IPsec policy')) + parser.add_argument( + '--encryption-algorithm', + choices=['3des', 'aes-128', 'aes-192', 'aes-256'], + type=_convert_to_lowercase, + help=_('Encryption algorithm for IPsec policy')) + parser.add_argument( + '--lifetime', + metavar="units=UNITS,value=VALUE", + type=nc_utils.str2dict_type(optional_keys=['units', 'value']), + help=vpn_utils.lifetime_help("IPsec")) + parser.add_argument( + '--pfs', + choices=['group2', 'group5', 'group14'], + type=_convert_to_lowercase, + help=_('Perfect Forward Secrecy for IPsec policy')) + parser.add_argument( + '--transform-protocol', + type=_convert_to_lowercase, + choices=['esp', 'ah', 'ah-esp'], + help=_('Transform protocol for IPsec policy')) + + +def _get_common_attrs(client_manager, parsed_args, is_create=True): + attrs = {} + if is_create: + if 'project' in parsed_args and parsed_args.project is not None: + attrs['tenant_id'] = osc_utils.find_project( + client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + if parsed_args.description: + attrs['description'] = str(parsed_args.description) + if parsed_args.auth_algorithm: + attrs['auth_algorithm'] = parsed_args.auth_algorithm + if parsed_args.encapsulation_mode: + attrs['encapsulation_mode'] = parsed_args.encapsulation_mode + if parsed_args.transform_protocol: + attrs['transform_protocol'] = parsed_args.transform_protocol + if parsed_args.encryption_algorithm: + attrs['encryption_algorithm'] = parsed_args.encryption_algorithm + if parsed_args.pfs: + attrs['pfs'] = parsed_args.pfs + if parsed_args.lifetime: + vpn_utils.validate_lifetime_dict(parsed_args.lifetime) + attrs['lifetime'] = parsed_args.lifetime + return attrs + + +class CreateIPsecPolicy(command.ShowOne): + _description = _("Create an IPsec policy") + + def get_parser(self, prog_name): + parser = super(CreateIPsecPolicy, self).get_parser(prog_name) + _get_common_parser(parser) + parser.add_argument( + 'name', + metavar='', + help=_('Name of the IPsec policy')) + osc_utils.add_project_owner_option_to_parser(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + attrs = _get_common_attrs(self.app.client_manager, parsed_args) + if parsed_args.name: + attrs['name'] = str(parsed_args.name) + obj = client.create_ipsecpolicy({'ipsecpolicy': attrs})['ipsecpolicy'] + columns, display_columns = osc_utils.get_columns(obj, _attr_map) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteIPsecPolicy(command.Command): + _description = _("Delete IPsec policy(policies)") + + def get_parser(self, prog_name): + parser = super(DeleteIPsecPolicy, self).get_parser(prog_name) + parser.add_argument( + 'ipsecpolicy', + metavar='', + nargs='+', + help=_('ipsec policy to delete (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + result = 0 + for ipsec in parsed_args.ipsecpolicy: + try: + ipsec_id = client.find_resource( + 'ipsecpolicy', ipsec, cmd_resource='ipsecpolicy')['id'] + client.delete_ipsecpolicy(ipsec_id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete IPsec policy with " + "name or ID '%(ipsecpolicy)s': %(e)s"), + {'ipsecpolicy': ipsec, 'e': e}) + + if result > 0: + total = len(parsed_args.ipsecpolicy) + msg = (_("%(result)s of %(total)s IPsec policy failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) + + +class ListIPsecPolicy(command.Lister): + _description = _("List IPsec policies that belong to a given project") + + def get_parser(self, prog_name): + parser = super(ListIPsecPolicy, self).get_parser(prog_name) + parser.add_argument( + '--long', + action='store_true', + default=False, + help=_("List additional fields in output") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + obj = client.list_ipsecpolicies()['ipsecpolicies'] + headers, columns = osc_utils.get_column_definitions( + _attr_map, long_listing=parsed_args.long) + return (headers, (utils.get_dict_properties(s, columns) for s in obj)) + + +class SetIPsecPolicy(command.Command): + _description = _("Set IPsec policy properties") + + def get_parser(self, prog_name): + parser = super(SetIPsecPolicy, self).get_parser(prog_name) + _get_common_parser(parser) + parser.add_argument( + '--name', + metavar='', + help=_('Name of the IPsec policy')) + parser.add_argument( + 'ipsecpolicy', + metavar='', + help=_('IPsec policy to set (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + attrs = _get_common_attrs(self.app.client_manager, + parsed_args, is_create=False) + if parsed_args.name: + attrs['name'] = str(parsed_args.name) + ipsec_id = client.find_resource( + 'ipsecpolicy', parsed_args.ipsecpolicy, + cmd_resource='ipsecpolicy')['id'] + try: + client.update_ipsecpolicy(ipsec_id, {'ipsecpolicy': attrs}) + except Exception as e: + msg = (_("Failed to set IPsec policy '%(ipsec)s': %(e)s") + % {'ipsec': parsed_args.ipsecpolicy, 'e': e}) + raise exceptions.CommandError(msg) + + +class ShowIPsecPolicy(command.ShowOne): + _description = _("Display IPsec policy details") + + def get_parser(self, prog_name): + parser = super(ShowIPsecPolicy, self).get_parser(prog_name) + parser.add_argument( + 'ipsecpolicy', + metavar='', + help=_('IPsec policy to display (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + ipsec_id = client.find_resource( + 'ipsecpolicy', parsed_args.ipsecpolicy, + cmd_resource='ipsecpolicy')['id'] + obj = client.show_ipsecpolicy(ipsec_id)['ipsecpolicy'] + columns, display_columns = osc_utils.get_columns(obj, _attr_map) + data = utils.get_dict_properties(obj, columns) + return (display_columns, data) diff --git a/neutronclient/osc/v2/vpnaas/utils.py b/neutronclient/osc/v2/vpnaas/utils.py new file mode 100644 index 000000000..2de5cc35e --- /dev/null +++ b/neutronclient/osc/v2/vpnaas/utils.py @@ -0,0 +1,112 @@ +# Copyright 2017 FUJITSU LIMITED +# 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. +# + + +"""VPN Utilities and helper functions.""" + + +from neutronclient._i18n import _ +from neutronclient.common import exceptions + +DPD_SUPPORTED_ACTIONS = ['hold', 'clear', 'restart', + 'restart-by-peer', 'disabled'] +DPD_SUPPORTED_KEYS = ['action', 'interval', 'timeout'] + +lifetime_keys = ['units', 'value'] +lifetime_units = ['seconds'] + + +def validate_dpd_dict(dpd_dict): + for key, value in dpd_dict.items(): + if key not in DPD_SUPPORTED_KEYS: + message = _( + "DPD Dictionary KeyError: " + "Reason-Invalid DPD key : " + "'%(key)s' not in %(supported_key)s") % { + 'key': key, 'supported_key': DPD_SUPPORTED_KEYS} + raise exceptions.CommandError(message) + if key == 'action' and value not in DPD_SUPPORTED_ACTIONS: + message = _( + "DPD Dictionary ValueError: " + "Reason-Invalid DPD action : " + "'%(key_value)s' not in %(supported_action)s") % { + 'key_value': value, + 'supported_action': DPD_SUPPORTED_ACTIONS} + raise exceptions.CommandError(message) + if key in ('interval', 'timeout'): + try: + if int(value) <= 0: + raise ValueError() + except ValueError: + message = _( + "DPD Dictionary ValueError: " + "Reason-Invalid positive integer value: " + "'%(key)s' = %(value)s") % { + 'key': key, 'value': value} + raise exceptions.CommandError(message) + else: + dpd_dict[key] = int(value) + return + + +def validate_lifetime_dict(lifetime_dict): + + for key, value in lifetime_dict.items(): + if key not in lifetime_keys: + message = _( + "Lifetime Dictionary KeyError: " + "Reason-Invalid unit key : " + "'%(key)s' not in %(supported_key)s") % { + 'key': key, 'supported_key': lifetime_keys} + raise exceptions.CommandError(message) + if key == 'units' and value not in lifetime_units: + message = _( + "Lifetime Dictionary ValueError: " + "Reason-Invalid units : " + "'%(key_value)s' not in %(supported_units)s") % { + 'key_value': key, 'supported_units': lifetime_units} + raise exceptions.CommandError(message) + if key == 'value': + try: + if int(value) < 60: + raise ValueError() + except ValueError: + message = _( + "Lifetime Dictionary ValueError: " + "Reason-Invalid value should be at least 60:" + "'%(key_value)s' = %(value)s") % { + 'key_value': key, 'value': value} + raise exceptions.CommandError(message) + else: + lifetime_dict['value'] = int(value) + return + + +def lifetime_help(policy): + lifetime = _("%s lifetime attributes. " + "'units'-seconds, default:seconds. " + "'value'-non negative integer, default:3600.") % policy + return lifetime + + +def dpd_help(policy): + dpd = _(" %s Dead Peer Detection attributes." + " 'action'-hold,clear,disabled,restart,restart-by-peer." + " 'interval' and 'timeout' are non negative integers. " + " 'interval' should be less than 'timeout' value. " + " 'action', default:hold 'interval', default:30, " + " 'timeout', default:120.") % policy.capitalize() + return dpd diff --git a/neutronclient/osc/v2/vpnaas/vpnservice.py b/neutronclient/osc/v2/vpnaas/vpnservice.py new file mode 100644 index 000000000..2120a14fb --- /dev/null +++ b/neutronclient/osc/v2/vpnaas/vpnservice.py @@ -0,0 +1,235 @@ +# Copyright 2017 FUJITSU LIMITED +# 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. +# + +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils +from oslo_log import log as logging + +from neutronclient._i18n import _ +from neutronclient.osc import utils as osc_utils + + +LOG = logging.getLogger(__name__) + +_attr_map = ( + ('id', 'ID', osc_utils.LIST_BOTH), + ('name', 'Name', osc_utils.LIST_BOTH), + ('router_id', 'Router', osc_utils.LIST_BOTH), + ('subnet_id', 'Subnet', osc_utils.LIST_BOTH), + ('flavor_id', 'Flavor', osc_utils.LIST_BOTH), + ('admin_state_up', 'State', osc_utils.LIST_BOTH), + ('status', 'Status', osc_utils.LIST_BOTH), + ('description', 'Description', osc_utils.LIST_LONG_ONLY), + ('tenant_id', 'Project', osc_utils.LIST_LONG_ONLY), +) + + +def _get_common_parser(parser): + parser.add_argument( + '--description', + metavar='', + help=_('Description for the VPN service')) + parser.add_argument( + '--subnet', + metavar='', + help=_('Local private subnet (name or ID)')) + parser.add_argument( + '--flavor', + metavar='', + help=_('Flavor for the VPN service (name or ID)')) + admin_group = parser.add_mutually_exclusive_group() + admin_group.add_argument( + '--enable', + action='store_true', + help=_("Enable VPN service") + ) + admin_group.add_argument( + '--disable', + action='store_true', + help=_("Disable VPN service") + ) + return parser + + +def _get_common_attrs(client_manager, parsed_args, is_create=True): + attrs = {} + if is_create: + if 'project' in parsed_args and parsed_args.project is not None: + attrs['tenant_id'] = osc_utils.find_project( + client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + if parsed_args.description: + attrs['description'] = str(parsed_args.description) + if parsed_args.subnet: + _subnet_id = client_manager.network.find_subnet( + parsed_args.subnet).id + attrs['subnet_id'] = _subnet_id + if parsed_args.flavor: + _flavor_id = client_manager.network.find_flavor( + parsed_args.flavor, + ignore_missing=False + ).id + attrs['flavor_id'] = _flavor_id + if parsed_args.enable: + attrs['admin_state_up'] = True + if parsed_args.disable: + attrs['admin_state_up'] = False + return attrs + + +class CreateVPNService(command.ShowOne): + _description = _("Create an VPN service") + + def get_parser(self, prog_name): + parser = super(CreateVPNService, self).get_parser(prog_name) + _get_common_parser(parser) + parser.add_argument( + 'name', + metavar='', + help=_('Name for the VPN service')) + parser.add_argument( + '--router', + metavar='ROUTER', + required=True, + help=_('Router for the VPN service (name or ID)')) + osc_utils.add_project_owner_option_to_parser(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + attrs = _get_common_attrs(self.app.client_manager, parsed_args) + if parsed_args.name: + attrs['name'] = str(parsed_args.name) + if parsed_args.router: + _router_id = self.app.client_manager.network.find_router( + parsed_args.router).id + attrs['router_id'] = _router_id + obj = client.create_vpnservice({'vpnservice': attrs})['vpnservice'] + columns, display_columns = osc_utils.get_columns(obj, _attr_map) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteVPNService(command.Command): + _description = _("Delete VPN service(s)") + + def get_parser(self, prog_name): + parser = super(DeleteVPNService, self).get_parser(prog_name) + parser.add_argument( + 'vpnservice', + metavar='', + nargs='+', + help=_('VPN service to delete (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + result = 0 + for vpn in parsed_args.vpnservice: + try: + vpn_id = client.find_resource( + 'vpnservice', vpn, cmd_resource='vpnservice')['id'] + client.delete_vpnservice(vpn_id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete VPN service with " + "name or ID '%(vpnservice)s': %(e)s"), + {'vpnservice': vpn, 'e': e}) + + if result > 0: + total = len(parsed_args.vpnservice) + msg = (_("%(result)s of %(total)s vpn service failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) + + +class ListVPNService(command.Lister): + _description = _("List VPN services that belong to a given project") + + def get_parser(self, prog_name): + parser = super(ListVPNService, self).get_parser(prog_name) + parser.add_argument( + '--long', + action='store_true', + default=False, + help=_("List additional fields in output") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + obj = client.list_vpnservices()['vpnservices'] + headers, columns = osc_utils.get_column_definitions( + _attr_map, long_listing=parsed_args.long) + return (headers, (utils.get_dict_properties(s, columns) for s in obj)) + + +class SetVPNSercice(command.Command): + _description = _("Set VPN service properties") + + def get_parser(self, prog_name): + parser = super(SetVPNSercice, self).get_parser(prog_name) + _get_common_parser(parser) + parser.add_argument( + '--name', + metavar='', + help=_('Name for the VPN service')) + parser.add_argument( + 'vpnservice', + metavar='', + help=_('VPN service to modify (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + attrs = _get_common_attrs(self.app.client_manager, + parsed_args, is_create=False) + if parsed_args.name: + attrs['name'] = str(parsed_args.name) + vpn_id = client.find_resource( + 'vpnservice', parsed_args.vpnservice, + cmd_resource='vpnservice')['id'] + try: + client.update_vpnservice(vpn_id, {'vpnservice': attrs}) + except Exception as e: + msg = (_("Failed to set vpn service '%(vpn)s': %(e)s") + % {'vpn': parsed_args.vpnservice, 'e': e}) + raise exceptions.CommandError(msg) + + +class ShowVPNService(command.ShowOne): + _description = _("Display VPN service details") + + def get_parser(self, prog_name): + parser = super(ShowVPNService, self).get_parser(prog_name) + parser.add_argument( + 'vpnservice', + metavar='', + help=_('VPN service to display (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + vpn_id = client.find_resource( + 'vpnservice', parsed_args.vpnservice, + cmd_resource='vpnservice')['id'] + obj = client.show_vpnservice(vpn_id)['vpnservice'] + columns, display_columns = osc_utils.get_columns(obj, _attr_map) + data = utils.get_dict_properties(obj, columns) + return (display_columns, data) diff --git a/neutronclient/tests/unit/osc/v2/vpnaas/common.py b/neutronclient/tests/unit/osc/v2/vpnaas/common.py new file mode 100644 index 000000000..4edee8b97 --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/vpnaas/common.py @@ -0,0 +1,157 @@ +# Copyright 2017 FUJITSU LIMITED +# 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 testtools + +from osc_lib import exceptions + +from neutronclient.tests.unit.osc.v2 import fakes as test_fakes + + +class TestCreateVPNaaS(test_fakes.TestNeutronClientOSCV2): + pass + + +class TestDeleteVPNaaS(test_fakes.TestNeutronClientOSCV2): + + def test_delete_with_one_resource(self): + target = self.resource['id'] + arglist = [target] + verifylist = [(self.res, [target])] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with(target) + self.assertIsNone(result) + + def test_delete_with_multiple_resources(self): + + def _mock_vpnaas(*args, **kwargs): + self.assertEqual(self.res, args[0]) + self.assertIsNotNone(args[1]) + self.assertEqual({'cmd_resource': self.res}, kwargs) + return {'id': args[1]} + + self.neutronclient.find_resource.side_effect = _mock_vpnaas + + target1 = 'target1' + target2 = 'target2' + arglist = [target1, target2] + verifylist = [(self.res, [target1, target2])] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) + + self.assertEqual(2, self.mocked.call_count) + for idx, reference in enumerate([target1, target2]): + actual = ''.join(self.mocked.call_args_list[idx][0]) + self.assertEqual(reference, actual) + + def test_delete_multiple_with_exception(self): + target1 = 'target' + arglist = [target1] + verifylist = [(self.res, [target1])] + + self.neutronclient.find_resource.side_effect = [ + target1, exceptions.CommandError + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + resource_name = self.res.replace('_', ' ') + msg = "1 of 2 %s(s) failed to delete." % resource_name + with testtools.ExpectedException(exceptions.CommandError) as e: + self.cmd.take_action(parsed_args) + self.assertEqual(msg, str(e)) + + +class TestListVPNaaS(test_fakes.TestNeutronClientOSCV2): + + def test_list_with_no_option(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.list_headers), headers) + self.assertEqual([self.list_data], list(data)) + + def test_list_with_long_option(self): + arglist = ['--long'] + verifylist = [('long', True)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.headers), headers) + self.assertEqual([self.data], list(data)) + + +class TestSetVPNaaS(test_fakes.TestNeutronClientOSCV2): + + def test_set_name(self): + target = self.resource['id'] + update = 'change' + arglist = [target, '--name', update] + verifylist = [ + (self.res, target), + ('name', update), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'name': update}}) + self.assertIsNone(result) + + def test_set_description(self): + target = self.resource['id'] + update = 'change-desc' + arglist = [target, '--description', update] + verifylist = [ + (self.res, target), + ('description', update), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'description': update}}) + self.assertIsNone(result) + + +class TestShowVPNaaS(test_fakes.TestNeutronClientOSCV2): + + def test_show_filtered_by_id_or_name(self): + target = self.resource['id'] + + def _mock_vpnaas(*args, **kwargs): + if self.neutronclient.find_resource.call_count == 1: + self.assertEqual(self.res, args[0]) + self.assertEqual(self.resource['id'], args[1]) + self.assertEqual({'cmd_resource': self.res}, kwargs) + return {'id': args[1]} + + self.neutronclient.find_resource.side_effect = _mock_vpnaas + + arglist = [target] + verifylist = [(self.res, target)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with(target) + self.assertEqual(self.ordered_headers, headers) + self.assertItemEqual(self.ordered_data, data) diff --git a/neutronclient/tests/unit/osc/v2/vpnaas/fakes.py b/neutronclient/tests/unit/osc/v2/vpnaas/fakes.py new file mode 100644 index 000000000..7ee7c5d5e --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/vpnaas/fakes.py @@ -0,0 +1,178 @@ +# Copyright 2017 FUJITSU LIMITED +# 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 collections +import copy +import uuid + +import mock + + +class FakeVPNaaS(object): + + def create(self, attrs={}): + """Create a fake vpnaas resources + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A OrderedDict faking the vpnaas resource + """ + self.ordered.update(attrs) + return copy.deepcopy(self.ordered) + + def bulk_create(self, attrs=None, count=2): + """Create multiple fake vpnaas resources + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of vpnaas resources to fake + :return: + A list of dictionaries faking the vpnaas resources + """ + return [self.create(attrs=attrs) for i in range(0, count)] + + def get(self, attrs=None, count=2): + """Get multiple fake vpnaas resources + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of vpnaas resources to fake + :return: + A list of dictionaries faking the vpnaas resource + """ + if attrs is None: + self.attrs = self.bulk_create(count=count) + return mock.Mock(side_effect=attrs) + + +class IKEPolicy(FakeVPNaaS): + """Fake one or more IKE policies""" + + def __init__(self): + super(IKEPolicy, self).__init__() + self.ordered = collections.OrderedDict(( + ('id', 'ikepolicy-id-' + uuid.uuid4().hex), + ('name', 'my-ikepolicy-' + uuid.uuid4().hex), + ('auth_algorithm', 'sha1'), + ('encryption_algorithm', 'aes-128'), + ('ike_version', 'v1'), + ('pfs', 'group5'), + ('description', 'my-desc-' + uuid.uuid4().hex), + ('phase1_negotiation_mode', 'main'), + ('tenant_id', 'tenant-id-' + uuid.uuid4().hex), + ('lifetime', {'units': 'seconds', 'value': 3600}), + )) + + +class IPSecPolicy(FakeVPNaaS): + """Fake one or more IPsec policies""" + + def __init__(self): + super(IPSecPolicy, self).__init__() + self.ordered = collections.OrderedDict(( + ('id', 'ikepolicy-id-' + uuid.uuid4().hex), + ('name', 'my-ikepolicy-' + uuid.uuid4().hex), + ('auth_algorithm', 'sha1'), + ('encapsulation_mode', 'tunnel'), + ('transform_protocol', 'esp'), + ('encryption_algorithm', 'aes-128'), + ('pfs', 'group5'), + ('description', 'my-desc-' + uuid.uuid4().hex), + ('tenant_id', 'tenant-id-' + uuid.uuid4().hex), + ('lifetime', {'units': 'seconds', 'value': 3600}), + )) + + +class VPNService(FakeVPNaaS): + """Fake one or more VPN services""" + + def __init__(self): + super(VPNService, self).__init__() + self.ordered = collections.OrderedDict(( + ('id', 'vpnservice-id-' + uuid.uuid4().hex), + ('name', 'my-vpnservice-' + uuid.uuid4().hex), + ('router_id', 'router-id-' + uuid.uuid4().hex), + ('subnet_id', 'subnet-id-' + uuid.uuid4().hex), + ('flavor_id', 'flavor-id-' + uuid.uuid4().hex), + ('admin_state_up', True), + ('status', 'ACTIVE'), + ('description', 'my-desc-' + uuid.uuid4().hex), + ('tenant_id', 'tenant-id-' + uuid.uuid4().hex), + )) + + +class EndpointGroup(FakeVPNaaS): + """Fake one or more Endpoint Groups""" + + def __init__(self): + super(EndpointGroup, self).__init__() + self.ordered = collections.OrderedDict(( + ('id', 'ep-group-id-' + uuid.uuid4().hex), + ('name', 'my-ep-group-' + uuid.uuid4().hex), + ('type', 'cidr'), + ('endpoints', ['10.0.0.0/24', '20.0.0.0/24']), + ('description', 'my-desc-' + uuid.uuid4().hex), + ('tenant_id', 'tenant-id-' + uuid.uuid4().hex), + )) + + +class IPsecSiteConnection(object): + """Fake one or more IPsec site connections""" + @staticmethod + def create_conn(attrs=None): + """Create a fake IPsec conn. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A Dictionary with id, name, peer_address, auth_mode, status, + tenant_id, peer_cidrs, vpnservice_id, ipsecpolicy_id, + ikepolicy_id, mtu, initiator, admin_state_up, description, + psk, route_mode, local_id, peer_id, local_ep_group_id, + peer_ep_group_id + """ + attrs = attrs or {} + + # Set default attributes. + conn_attrs = { + 'id': 'ipsec-site-conn-id-' + uuid.uuid4().hex, + 'name': 'my-ipsec-site-conn-' + uuid.uuid4().hex, + 'peer_address': '192.168.2.10', + 'auth_mode': '', + 'status': '', + 'tenant_id': 'tenant-id-' + uuid.uuid4().hex, + 'peer_cidrs': [], + 'vpnservice_id': 'vpnservice-id-' + uuid.uuid4().hex, + 'ipsecpolicy_id': 'ipsecpolicy-id-' + uuid.uuid4().hex, + 'ikepolicy_id': 'ikepolicy-id-' + uuid.uuid4().hex, + 'mtu': 1500, + 'initiator': 'bi-directional', + 'admin_state_up': True, + 'description': 'my-vpn-connection', + 'psk': 'abcd', + 'route_mode': '', + 'local_id': '', + 'peer_id': '192.168.2.10', + 'local_ep_group_id': 'local-ep-group-id-' + uuid.uuid4().hex, + 'peer_ep_group_id': 'peer-ep-group-id-' + uuid.uuid4().hex, + } + + # Overwrite default attributes. + conn_attrs.update(attrs) + return copy.deepcopy(conn_attrs) diff --git a/neutronclient/tests/unit/osc/v2/vpnaas/test_endpoint_group.py b/neutronclient/tests/unit/osc/v2/vpnaas/test_endpoint_group.py new file mode 100644 index 000000000..cc597e701 --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/vpnaas/test_endpoint_group.py @@ -0,0 +1,257 @@ +# Copyright 2017 FUJITSU LIMITED +# 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 copy + +import mock +from osc_lib.tests import utils as tests_utils + +from neutronclient.osc import utils as osc_utils +from neutronclient.osc.v2.vpnaas import endpoint_group +from neutronclient.tests.unit.osc.v2 import fakes as test_fakes +from neutronclient.tests.unit.osc.v2.vpnaas import common +from neutronclient.tests.unit.osc.v2.vpnaas import fakes + + +_endpoint_group = fakes.EndpointGroup().create() +CONVERT_MAP = { + 'project': 'tenant_id', +} + + +def _generate_data(ordered_dict=None, data=None): + source = ordered_dict if ordered_dict else _endpoint_group + if data: + source.update(data) + return tuple(source[key] for key in source) + + +def _generate_req_and_res(verifylist): + request = dict(verifylist) + response = copy.deepcopy(_endpoint_group) + for key, val in verifylist: + converted = CONVERT_MAP.get(key, key) + del request[key] + new_value = val + request[converted] = new_value + response[converted] = new_value + return request, response + + +class TestEndpointGroup(test_fakes.TestNeutronClientOSCV2): + + def check_results(self, headers, data, exp_req, is_list=False): + if is_list: + req_body = {self.res_plural: [exp_req]} + else: + req_body = {self.res: exp_req} + self.mocked.assert_called_once_with(req_body) + self.assertEqual(self.ordered_headers, headers) + self.assertEqual(self.ordered_data, data) + + def setUp(self): + super(TestEndpointGroup, self).setUp() + + def _mock_endpoint_group(*args, **kwargs): + self.neutronclient.find_resource.assert_called_once_with( + self.res, self.resource['id'], cmd_resource='endpoint_group') + return {'id': args[1]} + + self.neutronclient.find_resource.side_effect = mock.Mock( + side_effect=_mock_endpoint_group) + osc_utils.find_project = mock.Mock() + osc_utils.find_project.id = _endpoint_group['tenant_id'] + self.res = 'endpoint_group' + self.res_plural = 'endpoint_groups' + self.resource = _endpoint_group + self.headers = ( + 'ID', + 'Name', + 'Type', + 'Endpoints', + 'Description', + 'Project', + ) + self.data = _generate_data() + self.ordered_headers = ( + 'Description', + 'Endpoints', + 'ID', + 'Name', + 'Project', + 'Type', + ) + self.ordered_data = ( + _endpoint_group['description'], + _endpoint_group['endpoints'], + _endpoint_group['id'], + _endpoint_group['name'], + _endpoint_group['tenant_id'], + _endpoint_group['type'], + ) + self.ordered_columns = ( + 'description', + 'endpoints', + 'id', + 'name', + 'tenant_id', + 'type', + ) + + +class TestCreateEndpointGroup(TestEndpointGroup, common.TestCreateVPNaaS): + + def setUp(self): + super(TestCreateEndpointGroup, self).setUp() + self.neutronclient.create_endpoint_group = mock.Mock( + return_value={self.res: _endpoint_group}) + self.mocked = self.neutronclient.create_endpoint_group + self.cmd = endpoint_group.CreateEndpointGroup(self.app, self.namespace) + + def _update_expect_response(self, request, response): + """Set expected request and response + + :param request + A dictionary of request body(dict of verifylist) + :param response + A OrderedDict of request body + """ + # Update response body + self.neutronclient.create_endpoint_group.return_value = \ + {self.res: dict(response)} + osc_utils.find_project.return_value.id = response['tenant_id'] + # Update response(finally returns 'data') + self.data = _generate_data(ordered_dict=response) + self.ordered_data = tuple( + response[column] for column in self.ordered_columns + ) + + def _set_all_params_cidr(self, args={}): + name = args.get('name') or 'my-name' + description = args.get('description') or 'my-desc' + endpoint_type = args.get('type') or 'cidr' + endpoints = args.get('endpoints') or ['10.0.0.0/24', '20.0.0.0/24'] + tenant_id = args.get('tenant_id') or 'my-tenant' + arglist = [ + '--description', description, + '--type', endpoint_type, + '--value', '10.0.0.0/24', + '--value', '20.0.0.0/24', + '--project', tenant_id, + name, + ] + verifylist = [ + ('description', description), + ('type', endpoint_type), + ('endpoints', endpoints), + ('project', tenant_id), + ('name', name), + ] + return arglist, verifylist + + def _test_create_with_all_params_cidr(self, args={}): + arglist, verifylist = self._set_all_params_cidr(args) + request, response = _generate_req_and_res(verifylist) + self._update_expect_response(request, response) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.check_results(headers, data, request) + + def test_create_with_no_options(self): + arglist = [] + verifylist = [] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_create_with_all_params_cidr(self): + self._test_create_with_all_params_cidr() + + +class TestDeleteEndpointGroup(TestEndpointGroup, common.TestDeleteVPNaaS): + + def setUp(self): + super(TestDeleteEndpointGroup, self).setUp() + self.neutronclient.delete_endpoint_group = mock.Mock( + return_value={self.res: _endpoint_group}) + self.mocked = self.neutronclient.delete_endpoint_group + self.cmd = endpoint_group.DeleteEndpointGroup(self.app, self.namespace) + + +class TestListEndpointGroup(TestEndpointGroup): + + def setUp(self): + super(TestListEndpointGroup, self).setUp() + self.cmd = endpoint_group.ListEndpointGroup(self.app, self.namespace) + + self.short_header = ( + 'ID', + 'Name', + 'Type', + 'Endpoints', + ) + + self.short_data = ( + _endpoint_group['id'], + _endpoint_group['name'], + _endpoint_group['type'], + _endpoint_group['endpoints'], + ) + + self.neutronclient.list_endpoint_groups = mock.Mock( + return_value={self.res_plural: [_endpoint_group]}) + self.mocked = self.neutronclient.list_endpoint_groups + + def test_list_with_long_option(self): + arglist = ['--long'] + verifylist = [('long', True)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.headers), headers) + self.assertEqual([self.data], list(data)) + + def test_list_with_no_option(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.short_header), headers) + self.assertEqual([self.short_data], list(data)) + + +class TestSetEndpointGroup(TestEndpointGroup, common.TestSetVPNaaS): + + def setUp(self): + super(TestSetEndpointGroup, self).setUp() + self.neutronclient.update_endpoint_group = mock.Mock( + return_value={self.res: _endpoint_group}) + self.mocked = self.neutronclient.update_endpoint_group + self.cmd = endpoint_group.SetEndpointGroup(self.app, self.namespace) + + +class TestShowEndpointGroup(TestEndpointGroup, common.TestShowVPNaaS): + + def setUp(self): + super(TestShowEndpointGroup, self).setUp() + self.neutronclient.show_endpoint_group = mock.Mock( + return_value={self.res: _endpoint_group}) + self.mocked = self.neutronclient.show_endpoint_group + self.cmd = endpoint_group.ShowEndpointGroup(self.app, self.namespace) diff --git a/neutronclient/tests/unit/osc/v2/vpnaas/test_ikepolicy.py b/neutronclient/tests/unit/osc/v2/vpnaas/test_ikepolicy.py new file mode 100644 index 000000000..24663d6a2 --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/vpnaas/test_ikepolicy.py @@ -0,0 +1,303 @@ +# Copyright 2017 FUJITSU LIMITED +# 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 copy + +import mock +from osc_lib.tests import utils as tests_utils + +from neutronclient.osc import utils as osc_utils +from neutronclient.osc.v2.vpnaas import ikepolicy +from neutronclient.tests.unit.osc.v2 import fakes as test_fakes +from neutronclient.tests.unit.osc.v2.vpnaas import common +from neutronclient.tests.unit.osc.v2.vpnaas import fakes + + +_ikepolicy = fakes.IKEPolicy().create() +CONVERT_MAP = { + 'project': 'tenant_id', +} + + +def _generate_data(ordered_dict=None, data=None): + source = ordered_dict if ordered_dict else _ikepolicy + if data: + source.update(data) + return tuple(source[key] for key in source) + + +def _generate_req_and_res(verifylist): + request = dict(verifylist) + response = copy.deepcopy(_ikepolicy) + for key, val in verifylist: + converted = CONVERT_MAP.get(key, key) + del request[key] + new_value = val + request[converted] = new_value + response[converted] = new_value + return request, response + + +class TestIKEPolicy(test_fakes.TestNeutronClientOSCV2): + + def check_results(self, headers, data, exp_req, is_list=False): + if is_list: + req_body = {self.res_plural: [exp_req]} + else: + req_body = {self.res: exp_req} + self.mocked.assert_called_once_with(req_body) + self.assertEqual(self.ordered_headers, headers) + self.assertEqual(self.ordered_data, data) + + def setUp(self): + super(TestIKEPolicy, self).setUp() + + def _mock_ikepolicy(*args, **kwargs): + self.neutronclient.find_resource.assert_called_once_with( + self.res, self.resource['id'], cmd_resource='ikepolicy') + return {'id': args[1]} + + self.neutronclient.find_resource.side_effect = mock.Mock( + side_effect=_mock_ikepolicy) + osc_utils.find_project = mock.Mock() + osc_utils.find_project.id = _ikepolicy['tenant_id'] + self.res = 'ikepolicy' + self.res_plural = 'ikepolicies' + self.resource = _ikepolicy + self.headers = ( + 'ID', + 'Name', + 'Authentication Algorithm', + 'Encryption Algorithm', + 'IKE Version', + 'Perfect Forward Secrecy (PFS)', + 'Description', + 'Phase1 Negotiation Mode', + 'Project', + 'Lifetime', + ) + self.data = _generate_data() + self.ordered_headers = ( + 'Authentication Algorithm', + 'Description', + 'Encryption Algorithm', + 'ID', + 'IKE Version', + 'Lifetime', + 'Name', + 'Perfect Forward Secrecy (PFS)', + 'Phase1 Negotiation Mode', + 'Project', + ) + self.ordered_data = ( + _ikepolicy['auth_algorithm'], + _ikepolicy['description'], + _ikepolicy['encryption_algorithm'], + _ikepolicy['id'], + _ikepolicy['ike_version'], + _ikepolicy['lifetime'], + _ikepolicy['name'], + _ikepolicy['pfs'], + _ikepolicy['phase1_negotiation_mode'], + _ikepolicy['tenant_id'], + ) + self.ordered_columns = ( + 'auth_algorithm', + 'description', + 'encryption_algorithm', + 'id', + 'ike_version', + 'lifetime', + 'name', + 'pfs', + 'phase1_negotiation_mode', + 'tenant_id', + ) + + +class TestCreateIKEPolicy(TestIKEPolicy, common.TestCreateVPNaaS): + + def setUp(self): + super(TestCreateIKEPolicy, self).setUp() + self.neutronclient.create_ikepolicy = mock.Mock( + return_value={self.res: _ikepolicy}) + self.mocked = self.neutronclient.create_ikepolicy + self.cmd = ikepolicy.CreateIKEPolicy(self.app, self.namespace) + + def _update_expect_response(self, request, response): + """Set expected request and response + + :param request + A dictionary of request body(dict of verifylist) + :param response + A OrderedDict of request body + """ + # Update response body + self.neutronclient.create_ikepolicy.return_value = \ + {self.res: dict(response)} + osc_utils.find_project.return_value.id = response['tenant_id'] + # Update response(finally returns 'data') + self.data = _generate_data(ordered_dict=response) + self.ordered_data = tuple( + response[column] for column in self.ordered_columns + ) + + def _set_all_params(self, args={}): + name = args.get('name') or 'my-name' + description = args.get('description') or 'my-desc' + auth_algorithm = args.get('auth_algorithm') or 'sha1' + encryption_algorithm = args.get('encryption_algorithm') or 'aes-128' + phase1_negotiation_mode = args.get('phase1_negotiation_mode') or 'main' + ike_version = args.get('ike_version') or 'v1' + pfs = args.get('pfs') or 'group5' + tenant_id = args.get('tenant_id') or 'my-tenant' + arglist = [ + '--description', description, + '--auth-algorithm', auth_algorithm, + '--encryption-algorithm', encryption_algorithm, + '--phase1-negotiation-mode', phase1_negotiation_mode, + '--ike-version', ike_version, + '--pfs', pfs, + '--project', tenant_id, + name, + ] + verifylist = [ + ('description', description), + ('auth_algorithm', auth_algorithm), + ('encryption_algorithm', encryption_algorithm), + ('phase1_negotiation_mode', phase1_negotiation_mode), + ('ike_version', ike_version), + ('pfs', pfs), + ('project', tenant_id), + ('name', name), + ] + return arglist, verifylist + + def _test_create_with_all_params(self, args={}): + arglist, verifylist = self._set_all_params(args) + request, response = _generate_req_and_res(verifylist) + self._update_expect_response(request, response) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.check_results(headers, data, request) + + def test_create_with_no_options(self): + arglist = [] + verifylist = [] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_create_with_all_params(self): + self._test_create_with_all_params() + + def test_create_with_all_params_name(self): + self._test_create_with_all_params({'name': 'new_ikepolicy'}) + + +class TestDeleteIKEPolicy(TestIKEPolicy, common.TestDeleteVPNaaS): + + def setUp(self): + super(TestDeleteIKEPolicy, self).setUp() + self.neutronclient.delete_ikepolicy = mock.Mock( + return_value={self.res: _ikepolicy}) + self.mocked = self.neutronclient.delete_ikepolicy + self.cmd = ikepolicy.DeleteIKEPolicy(self.app, self.namespace) + + +class TestListIKEPolicy(TestIKEPolicy): + + def setUp(self): + super(TestListIKEPolicy, self).setUp() + self.cmd = ikepolicy.ListIKEPolicy(self.app, self.namespace) + + self.short_header = ( + 'ID', + 'Name', + 'Authentication Algorithm', + 'Encryption Algorithm', + 'IKE Version', + 'Perfect Forward Secrecy (PFS)', + ) + + self.short_data = ( + _ikepolicy['id'], + _ikepolicy['name'], + _ikepolicy['auth_algorithm'], + _ikepolicy['encryption_algorithm'], + _ikepolicy['ike_version'], + _ikepolicy['pfs'], + ) + + self.neutronclient.list_ikepolicies = mock.Mock( + return_value={self.res_plural: [_ikepolicy]}) + self.mocked = self.neutronclient.list_ikepolicies + + def test_list_with_long_option(self): + arglist = ['--long'] + verifylist = [('long', True)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.headers), headers) + self.assertEqual([self.data], list(data)) + + def test_list_with_no_option(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.short_header), headers) + self.assertEqual([self.short_data], list(data)) + + +class TestSetIKEPolicy(TestIKEPolicy, common.TestSetVPNaaS): + + def setUp(self): + super(TestSetIKEPolicy, self).setUp() + self.neutronclient.update_ikepolicy = mock.Mock( + return_value={self.res: _ikepolicy}) + self.mocked = self.neutronclient.update_ikepolicy + self.cmd = ikepolicy.SetIKEPolicy(self.app, self.namespace) + + def test_set_auth_algorithm_with_sha256(self): + target = self.resource['id'] + auth_algorithm = 'sha256' + arglist = [target, '--auth-algorithm', auth_algorithm] + verifylist = [ + (self.res, target), + ('auth_algorithm', auth_algorithm), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'auth_algorithm': 'sha256'}}) + self.assertIsNone(result) + + +class TestShowIKEPolicy(TestIKEPolicy, common.TestShowVPNaaS): + + def setUp(self): + super(TestShowIKEPolicy, self).setUp() + self.neutronclient.show_ikepolicy = mock.Mock( + return_value={self.res: _ikepolicy}) + self.mocked = self.neutronclient.show_ikepolicy + self.cmd = ikepolicy.ShowIKEPolicy(self.app, self.namespace) diff --git a/neutronclient/tests/unit/osc/v2/vpnaas/test_ipsec_site_connection.py b/neutronclient/tests/unit/osc/v2/vpnaas/test_ipsec_site_connection.py new file mode 100644 index 000000000..4d04b641c --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/vpnaas/test_ipsec_site_connection.py @@ -0,0 +1,381 @@ +# Copyright 2017 FUJITSU LIMITED +# 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 copy + +import mock +from osc_lib.cli import format_columns +from osc_lib.tests import utils as tests_utils + +from neutronclient.osc import utils as osc_utils +from neutronclient.osc.v2.vpnaas import ipsec_site_connection +from neutronclient.tests.unit.osc.v2 import fakes as test_fakes +from neutronclient.tests.unit.osc.v2.vpnaas import common +from neutronclient.tests.unit.osc.v2.vpnaas import fakes + + +_ipsec_site_conn = fakes.IPsecSiteConnection().create_conn() +CONVERT_MAP = { + 'project': 'tenant_id', + 'ikepolicy': 'ikepolicy_id', + 'ipsecpolicy': 'ipsecpolicy_id', + 'vpnservice': 'vpnservice_id', + 'peer_endpoint_group': 'peer_ep_group_id', + 'local_endpoint_group': 'local_ep_group_id', +} + + +def _generate_data(ordered_dict=None, data=None): + source = ordered_dict if ordered_dict else _ipsec_site_conn + if data: + source.update(data) + return ( + _ipsec_site_conn['id'], + _ipsec_site_conn['name'], + _ipsec_site_conn['peer_address'], + _ipsec_site_conn['auth_mode'], + _ipsec_site_conn['status'], + _ipsec_site_conn['tenant_id'], + format_columns.ListColumn(_ipsec_site_conn['peer_cidrs']), + _ipsec_site_conn['vpnservice_id'], + _ipsec_site_conn['ipsecpolicy_id'], + _ipsec_site_conn['ikepolicy_id'], + _ipsec_site_conn['mtu'], + _ipsec_site_conn['initiator'], + _ipsec_site_conn['admin_state_up'], + _ipsec_site_conn['description'], + _ipsec_site_conn['psk'], + _ipsec_site_conn['route_mode'], + _ipsec_site_conn['local_id'], + _ipsec_site_conn['peer_id'], + _ipsec_site_conn['local_ep_group_id'], + _ipsec_site_conn['peer_ep_group_id'], + ) + + +def _generate_req_and_res(verifylist): + request = dict(verifylist) + response = copy.deepcopy(_ipsec_site_conn) + for key, val in verifylist: + converted = CONVERT_MAP.get(key, key) + del request[key] + new_value = val + request[converted] = new_value + response[converted] = new_value + return request, response + + +class TestIPsecSiteConn(test_fakes.TestNeutronClientOSCV2): + + def check_results(self, headers, data, exp_req, is_list=False): + if is_list: + req_body = {self.res_plural: [exp_req]} + else: + req_body = {self.res: exp_req} + self.mocked.assert_called_once_with(req_body) + self.assertEqual(self.ordered_headers, headers) + self.assertItemEqual(self.ordered_data, data) + + def setUp(self): + super(TestIPsecSiteConn, self).setUp() + + def _mock_ipsec_site_conn(*args, **kwargs): + return {'id': args[1]} + + self.neutronclient.find_resource.side_effect = mock.Mock( + side_effect=_mock_ipsec_site_conn) + osc_utils.find_project = mock.Mock() + osc_utils.find_project.id = _ipsec_site_conn['tenant_id'] + self.res = 'ipsec_site_connection' + self.res_plural = 'ipsec_site_connections' + self.resource = _ipsec_site_conn + self.headers = ( + 'ID', + 'Name', + 'Peer Address', + 'Authentication Algorithm', + 'Status', + 'Project', + 'Peer CIDRs', + 'VPN Service', + 'IPSec Policy', + 'IKE Policy', + 'MTU', + 'Initiator', + 'State', + 'Description', + 'Pre-shared Key', + 'Route Mode', + 'Local ID', + 'Peer ID', + 'Local Endpoint Group ID', + 'Peer Endpoint Group ID' + ) + self.data = _generate_data() + self.ordered_headers = ( + 'Authentication Algorithm', + 'Description', + 'ID', + 'IKE Policy', + 'IPSec Policy', + 'Initiator', + 'Local Endpoint Group ID', + 'Local ID', + 'MTU', + 'Name', + 'Peer Address', + 'Peer CIDRs', + 'Peer Endpoint Group ID', + 'Peer ID', + 'Pre-shared Key', + 'Project', + 'Route Mode', + 'State', + 'Status', + 'VPN Service', + ) + self.ordered_data = ( + _ipsec_site_conn['auth_mode'], + _ipsec_site_conn['description'], + _ipsec_site_conn['id'], + _ipsec_site_conn['ikepolicy_id'], + _ipsec_site_conn['ipsecpolicy_id'], + _ipsec_site_conn['initiator'], + _ipsec_site_conn['local_ep_group_id'], + _ipsec_site_conn['local_id'], + _ipsec_site_conn['mtu'], + _ipsec_site_conn['name'], + _ipsec_site_conn['peer_address'], + format_columns.ListColumn(_ipsec_site_conn['peer_cidrs']), + _ipsec_site_conn['peer_ep_group_id'], + _ipsec_site_conn['peer_id'], + _ipsec_site_conn['psk'], + _ipsec_site_conn['tenant_id'], + _ipsec_site_conn['route_mode'], + _ipsec_site_conn['admin_state_up'], + _ipsec_site_conn['status'], + _ipsec_site_conn['vpnservice_id'], + ) + + +class TestCreateIPsecSiteConn(TestIPsecSiteConn, common.TestCreateVPNaaS): + + def setUp(self): + super(TestCreateIPsecSiteConn, self).setUp() + self.neutronclient.create_ipsec_site_connection = mock.Mock( + return_value={self.res: _ipsec_site_conn}) + self.mocked = self.neutronclient.create_ipsec_site_connection + self.cmd = ipsec_site_connection.CreateIPsecSiteConnection( + self.app, self.namespace) + + def _update_expect_response(self, request, response): + """Set expected request and response + + :param request + A dictionary of request body(dict of verifylist) + :param response + A OrderedDict of request body + """ + # Update response body + self.neutronclient.create_ipsec_site_connection.return_value = \ + {self.res: dict(response)} + osc_utils.find_project.return_value.id = response['tenant_id'] + # Update response(finally returns 'data') + self.data = _generate_data(ordered_dict=response) + self.ordered_data = ( + response['auth_mode'], + response['description'], + response['id'], + response['ikepolicy_id'], + response['ipsecpolicy_id'], + response['initiator'], + response['local_ep_group_id'], + response['local_id'], + response['mtu'], + response['name'], + response['peer_address'], + format_columns.ListColumn(response['peer_cidrs']), + response['peer_ep_group_id'], + response['peer_id'], + response['psk'], + response['tenant_id'], + response['route_mode'], + response['admin_state_up'], + response['status'], + response['vpnservice_id'], + ) + + def _set_all_params(self, args={}): + tenant_id = args.get('tenant_id') or 'my-tenant' + name = args.get('name') or 'connection1' + peer_address = args.get('peer_address') or '192.168.2.10' + peer_id = args.get('peer_id') or '192.168.2.10' + psk = args.get('psk') or 'abcd' + mtu = args.get('mtu') or '1500' + initiator = args.get('initiator') or 'bi-directional' + vpnservice_id = args.get('vpnservice') or 'vpnservice_id' + ikepolicy_id = args.get('ikepolicy') or 'ikepolicy_id' + ipsecpolicy_id = args.get('ipsecpolicy') or 'ipsecpolicy_id' + local_ep_group = args.get('local_ep_group_id') or 'local-epg' + peer_ep_group = args.get('peer_ep_group_id') or 'peer-epg' + description = args.get('description') or 'my-vpn-connection' + + arglist = [ + '--project', tenant_id, + '--peer-address', peer_address, + '--peer-id', peer_id, + '--psk', psk, + '--initiator', initiator, + '--vpnservice', vpnservice_id, + '--ikepolicy', ikepolicy_id, + '--ipsecpolicy', ipsecpolicy_id, + '--mtu', mtu, + '--description', description, + '--local-endpoint-group', local_ep_group, + '--peer-endpoint-group', peer_ep_group, + name, + ] + verifylist = [ + ('project', tenant_id), + ('peer_address', peer_address), + ('peer_id', peer_id), + ('psk', psk), + ('initiator', initiator), + ('vpnservice', vpnservice_id), + ('ikepolicy', ikepolicy_id), + ('ipsecpolicy', ipsecpolicy_id), + ('mtu', mtu), + ('description', description), + ('local_endpoint_group', local_ep_group), + ('peer_endpoint_group', peer_ep_group), + ('name', name), + ] + return arglist, verifylist + + def _test_create_with_all_params(self, args={}): + arglist, verifylist = self._set_all_params(args) + request, response = _generate_req_and_res(verifylist) + self._update_expect_response(request, response) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.check_results(headers, data, request) + + def test_create_with_no_options(self): + arglist = [] + verifylist = [] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_create_with_all_params(self): + self._test_create_with_all_params() + + +class TestDeleteIPsecSiteConn(TestIPsecSiteConn, common.TestDeleteVPNaaS): + + def setUp(self): + super(TestDeleteIPsecSiteConn, self).setUp() + self.neutronclient.delete_ipsec_site_connection = mock.Mock( + return_value={self.res: _ipsec_site_conn}) + self.mocked = self.neutronclient.delete_ipsec_site_connection + self.cmd = ipsec_site_connection.DeleteIPsecSiteConnection( + self.app, self.namespace) + + +class TestListIPsecSiteConn(TestIPsecSiteConn): + + def setUp(self): + super(TestListIPsecSiteConn, self).setUp() + self.cmd = ipsec_site_connection.ListIPsecSiteConnection( + self.app, self.namespace) + + self.short_header = ( + 'ID', + 'Name', + 'Peer Address', + 'Authentication Algorithm', + 'Status', + ) + + self.short_data = ( + _ipsec_site_conn['id'], + _ipsec_site_conn['name'], + _ipsec_site_conn['peer_address'], + _ipsec_site_conn['auth_mode'], + _ipsec_site_conn['status'], + ) + + self.neutronclient.list_ipsec_site_connections = mock.Mock( + return_value={self.res_plural: [_ipsec_site_conn]}) + self.mocked = self.neutronclient.list_ipsec_site_connections + + def test_list_with_long_option(self): + arglist = ['--long'] + verifylist = [('long', True)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.headers), headers) + self.assertListItemEqual([self.data], list(data)) + + def test_list_with_no_option(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.short_header), headers) + self.assertEqual([self.short_data], list(data)) + + +class TestSetIPsecSiteConn(TestIPsecSiteConn, common.TestSetVPNaaS): + + def setUp(self): + super(TestSetIPsecSiteConn, self).setUp() + self.neutronclient.update_ipsec_site_connection = mock.Mock( + return_value={self.res: _ipsec_site_conn}) + self.mocked = self.neutronclient.update_ipsec_site_connection + self.cmd = ipsec_site_connection.SetIPsecSiteConnection( + self.app, self.namespace) + + def test_set_ipsec_site_conn_with_peer_id(self): + target = self.resource['id'] + peer_id = '192.168.3.10' + arglist = [target, '--peer-id', peer_id] + verifylist = [ + (self.res, target), + ('peer_id', peer_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'peer_id': peer_id}}) + self.assertIsNone(result) + + +class TestShowIPsecSiteConn(TestIPsecSiteConn, common.TestShowVPNaaS): + + def setUp(self): + super(TestShowIPsecSiteConn, self).setUp() + self.neutronclient.show_ipsec_site_connection = mock.Mock( + return_value={self.res: _ipsec_site_conn}) + self.mocked = self.neutronclient.show_ipsec_site_connection + self.cmd = ipsec_site_connection.ShowIPsecSiteConnection( + self.app, self.namespace) diff --git a/neutronclient/tests/unit/osc/v2/vpnaas/test_ipsecpolicy.py b/neutronclient/tests/unit/osc/v2/vpnaas/test_ipsecpolicy.py new file mode 100644 index 000000000..8e56cf3f9 --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/vpnaas/test_ipsecpolicy.py @@ -0,0 +1,303 @@ +# Copyright 2017 FUJITSU LIMITED +# 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 copy + +import mock +from osc_lib.tests import utils as tests_utils + +from neutronclient.osc import utils as osc_utils +from neutronclient.osc.v2.vpnaas import ipsecpolicy +from neutronclient.tests.unit.osc.v2 import fakes as test_fakes +from neutronclient.tests.unit.osc.v2.vpnaas import common +from neutronclient.tests.unit.osc.v2.vpnaas import fakes + + +_ipsecpolicy = fakes.IPSecPolicy().create() +CONVERT_MAP = { + 'project': 'tenant_id', +} + + +def _generate_data(ordered_dict=None, data=None): + source = ordered_dict if ordered_dict else _ipsecpolicy + if data: + source.update(data) + return tuple(source[key] for key in source) + + +def _generate_req_and_res(verifylist): + request = dict(verifylist) + response = copy.deepcopy(_ipsecpolicy) + for key, val in verifylist: + converted = CONVERT_MAP.get(key, key) + del request[key] + new_value = val + request[converted] = new_value + response[converted] = new_value + return request, response + + +class TestIPSecPolicy(test_fakes.TestNeutronClientOSCV2): + + def check_results(self, headers, data, exp_req, is_list=False): + if is_list: + req_body = {self.res_plural: [exp_req]} + else: + req_body = {self.res: exp_req} + self.mocked.assert_called_once_with(req_body) + self.assertEqual(self.ordered_headers, headers) + self.assertEqual(self.ordered_data, data) + + def setUp(self): + super(TestIPSecPolicy, self).setUp() + + def _mock_ipsecpolicy(*args, **kwargs): + self.neutronclient.find_resource.assert_called_once_with( + self.res, self.resource['id'], cmd_resource='ipsecpolicy') + return {'id': args[1]} + + self.neutronclient.find_resource.side_effect = mock.Mock( + side_effect=_mock_ipsecpolicy) + osc_utils.find_project = mock.Mock() + osc_utils.find_project.id = _ipsecpolicy['tenant_id'] + self.res = 'ipsecpolicy' + self.res_plural = 'ipsecpolicies' + self.resource = _ipsecpolicy + self.headers = ( + 'ID', + 'Name', + 'Authentication Algorithm', + 'Encapsulation Mode', + 'Transform Protocol', + 'Encryption Algorithm', + 'Perfect Forward Secrecy (PFS)', + 'Description', + 'Project', + 'Lifetime', + ) + self.data = _generate_data() + self.ordered_headers = ( + 'Authentication Algorithm', + 'Description', + 'Encapsulation Mode', + 'Encryption Algorithm', + 'ID', + 'Lifetime', + 'Name', + 'Perfect Forward Secrecy (PFS)', + 'Project', + 'Transform Protocol', + ) + self.ordered_data = ( + _ipsecpolicy['auth_algorithm'], + _ipsecpolicy['description'], + _ipsecpolicy['encapsulation_mode'], + _ipsecpolicy['encryption_algorithm'], + _ipsecpolicy['id'], + _ipsecpolicy['lifetime'], + _ipsecpolicy['name'], + _ipsecpolicy['pfs'], + _ipsecpolicy['tenant_id'], + _ipsecpolicy['transform_protocol'], + ) + self.ordered_columns = ( + 'auth_algorithm', + 'description', + 'encapsulation_mode', + 'encryption_algorithm', + 'id', + 'lifetime', + 'name', + 'pfs', + 'tenant_id', + 'transform_protocol', + ) + + +class TestCreateIPSecPolicy(TestIPSecPolicy, common.TestCreateVPNaaS): + + def setUp(self): + super(TestCreateIPSecPolicy, self).setUp() + self.neutronclient.create_ipsecpolicy = mock.Mock( + return_value={self.res: _ipsecpolicy}) + self.mocked = self.neutronclient.create_ipsecpolicy + self.cmd = ipsecpolicy.CreateIPsecPolicy(self.app, self.namespace) + + def _update_expect_response(self, request, response): + """Set expected request and response + + :param request + A dictionary of request body(dict of verifylist) + :param response + A OrderedDict of request body + """ + # Update response body + self.neutronclient.create_ipsecpolicy.return_value = \ + {self.res: dict(response)} + osc_utils.find_project.return_value.id = response['tenant_id'] + # Update response(finally returns 'data') + self.data = _generate_data(ordered_dict=response) + self.ordered_data = tuple( + response[column] for column in self.ordered_columns + ) + + def _set_all_params(self, args={}): + name = args.get('name') or 'my-name' + auth_algorithm = args.get('auth_algorithm') or 'sha1' + encapsulation_mode = args.get('encapsulation_mode') or 'tunnel' + transform_protocol = args.get('transform_protocol') or 'esp' + encryption_algorithm = args.get('encryption_algorithm') or 'aes-128' + pfs = args.get('pfs') or 'group5' + description = args.get('description') or 'my-desc' + tenant_id = args.get('tenant_id') or 'my-tenant' + arglist = [ + name, + '--auth-algorithm', auth_algorithm, + '--encapsulation-mode', encapsulation_mode, + '--transform-protocol', transform_protocol, + '--encryption-algorithm', encryption_algorithm, + '--pfs', pfs, + '--description', description, + '--project', tenant_id, + ] + verifylist = [ + ('name', name), + ('auth_algorithm', auth_algorithm), + ('encapsulation_mode', encapsulation_mode), + ('transform_protocol', transform_protocol), + ('encryption_algorithm', encryption_algorithm), + ('pfs', pfs), + ('description', description), + ('project', tenant_id), + ] + return arglist, verifylist + + def _test_create_with_all_params(self, args={}): + arglist, verifylist = self._set_all_params(args) + request, response = _generate_req_and_res(verifylist) + self._update_expect_response(request, response) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.check_results(headers, data, request) + + def test_create_with_no_options(self): + arglist = [] + verifylist = [] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_create_with_all_params(self): + self._test_create_with_all_params() + + def test_create_with_all_params_name(self): + self._test_create_with_all_params({'name': 'new_ipsecpolicy'}) + + +class TestDeleteIPSecPolicy(TestIPSecPolicy, common.TestDeleteVPNaaS): + + def setUp(self): + super(TestDeleteIPSecPolicy, self).setUp() + self.neutronclient.delete_ipsecpolicy = mock.Mock( + return_value={self.res: _ipsecpolicy}) + self.mocked = self.neutronclient.delete_ipsecpolicy + self.cmd = ipsecpolicy.DeleteIPsecPolicy(self.app, self.namespace) + + +class TestListIPSecPolicy(TestIPSecPolicy): + + def setUp(self): + super(TestListIPSecPolicy, self).setUp() + self.cmd = ipsecpolicy.ListIPsecPolicy(self.app, self.namespace) + + self.short_header = ( + 'ID', + 'Name', + 'Authentication Algorithm', + 'Encapsulation Mode', + 'Transform Protocol', + 'Encryption Algorithm', + ) + + self.short_data = ( + _ipsecpolicy['id'], + _ipsecpolicy['name'], + _ipsecpolicy['auth_algorithm'], + _ipsecpolicy['encapsulation_mode'], + _ipsecpolicy['transform_protocol'], + _ipsecpolicy['encryption_algorithm'], + ) + + self.neutronclient.list_ipsecpolicies = mock.Mock( + return_value={self.res_plural: [_ipsecpolicy]}) + self.mocked = self.neutronclient.list_ipsecpolicies + + def test_list_with_long_option(self): + arglist = ['--long'] + verifylist = [('long', True)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.headers), headers) + self.assertEqual([self.data], list(data)) + + def test_list_with_no_option(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.short_header), headers) + self.assertEqual([self.short_data], list(data)) + + +class TestSetIPSecPolicy(TestIPSecPolicy, common.TestSetVPNaaS): + + def setUp(self): + super(TestSetIPSecPolicy, self).setUp() + self.neutronclient.update_ipsecpolicy = mock.Mock( + return_value={self.res: _ipsecpolicy}) + self.mocked = self.neutronclient.update_ipsecpolicy + self.cmd = ipsecpolicy.SetIPsecPolicy(self.app, self.namespace) + + def test_set_auth_algorithm_with_sha256(self): + target = self.resource['id'] + auth_algorithm = 'sha256' + arglist = [target, '--auth-algorithm', auth_algorithm] + verifylist = [ + (self.res, target), + ('auth_algorithm', auth_algorithm), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'auth_algorithm': 'sha256'}}) + self.assertIsNone(result) + + +class TestShowIPSecPolicy(TestIPSecPolicy, common.TestShowVPNaaS): + + def setUp(self): + super(TestShowIPSecPolicy, self).setUp() + self.neutronclient.show_ipsecpolicy = mock.Mock( + return_value={self.res: _ipsecpolicy}) + self.mocked = self.neutronclient.show_ipsecpolicy + self.cmd = ipsecpolicy.ShowIPsecPolicy(self.app, self.namespace) diff --git a/neutronclient/tests/unit/osc/v2/vpnaas/test_vpnservice.py b/neutronclient/tests/unit/osc/v2/vpnaas/test_vpnservice.py new file mode 100644 index 000000000..a9c208e7c --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/vpnaas/test_vpnservice.py @@ -0,0 +1,305 @@ +# Copyright 2017 FUJITSU LIMITED +# 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 copy +import uuid + +import mock + +from neutronclient.osc import utils as osc_utils +from neutronclient.osc.v2.vpnaas import vpnservice +from neutronclient.tests.unit.osc.v2 import fakes as test_fakes +from neutronclient.tests.unit.osc.v2.vpnaas import common +from neutronclient.tests.unit.osc.v2.vpnaas import fakes + + +_vpnservice = fakes.VPNService().create() +CONVERT_MAP = { + 'project': 'tenant_id', + 'router': 'router_id', + 'subnet': 'subnet_id' +} + + +def _generate_data(ordered_dict=None, data=None): + source = ordered_dict if ordered_dict else _vpnservice + if data: + source.update(data) + return tuple(source[key] for key in source) + + +def _generate_req_and_res(verifylist): + request = dict(verifylist) + response = copy.deepcopy(_vpnservice) + for key, val in verifylist: + converted = CONVERT_MAP.get(key, key) + del request[key] + new_value = val + request[converted] = new_value + response[converted] = new_value + return request, response + + +class TestVPNService(test_fakes.TestNeutronClientOSCV2): + + def _check_results(self, headers, data, exp_req, is_list=False): + if is_list: + req_body = {self.res_plural: [exp_req]} + else: + req_body = {self.res: exp_req} + self.mocked.assert_called_once_with(req_body) + self.assertEqual(self.ordered_headers, headers) + self.assertEqual(self.ordered_data, data) + + def setUp(self): + super(TestVPNService, self).setUp() + + def _mock_vpnservice(*args, **kwargs): + self.neutronclient.find_resource.assert_called_once_with( + self.res, self.resource['id'], cmd_resource='vpnservice') + return {'id': args[1]} + + self.app.client_manager.network = mock.Mock() + self.app.client_manager.network.find_router = mock.Mock() + self.app.client_manager.network.find_subnet = mock.Mock() + self.fake_router = mock.Mock() + self.fake_subnet = mock.Mock() + self.app.client_manager.network.find_router.return_value = \ + self.fake_router + self.app.client_manager.network.find_subnet.return_value = \ + self.fake_subnet + self.args = { + 'name': 'my-name', + 'description': 'my-desc', + 'tenant_id': 'tenant-id-' + uuid.uuid4().hex, + 'router_id': 'router-id-' + uuid.uuid4().hex, + 'subnet_id': 'subnet-id-' + uuid.uuid4().hex, + + } + self.fake_subnet.id = self.args['subnet_id'] + self.fake_router.id = self.args['router_id'] + + self.neutronclient.find_resource.side_effect = mock.Mock( + side_effect=_mock_vpnservice) + osc_utils.find_project = mock.Mock() + osc_utils.find_project.id = _vpnservice['tenant_id'] + + self.res = 'vpnservice' + self.res_plural = 'vpnservices' + self.resource = _vpnservice + self.headers = ( + 'ID', + 'Name', + 'Router', + 'Subnet', + 'Flavor', + 'State', + 'Status', + 'Description', + 'Project', + ) + self.data = _generate_data() + self.ordered_headers = ( + 'Description', + 'Flavor', + 'ID', + 'Name', + 'Project', + 'Router', + 'State', + 'Status', + 'Subnet', + ) + self.ordered_data = ( + _vpnservice['description'], + _vpnservice['flavor_id'], + _vpnservice['id'], + _vpnservice['name'], + _vpnservice['tenant_id'], + _vpnservice['router_id'], + _vpnservice['admin_state_up'], + _vpnservice['status'], + _vpnservice['subnet_id'], + ) + self.ordered_columns = ( + 'description', + 'flavor_id', + 'id', + 'name', + 'tenant_id', + 'router_id', + 'admin_state_up', + 'status', + 'subnet_id', + ) + + +class TestCreateVPNService(TestVPNService, common.TestCreateVPNaaS): + + def setUp(self): + super(TestCreateVPNService, self).setUp() + self.neutronclient.create_vpnservice = mock.Mock( + return_value={self.res: _vpnservice}) + self.mocked = self.neutronclient.create_vpnservice + self.cmd = vpnservice.CreateVPNService(self.app, self.namespace) + + def _update_expect_response(self, request, response): + """Set expected request and response + + :param request + A dictionary of request body(dict of verifylist) + :param response + A OrderedDict of request body + """ + # Update response body + self.neutronclient.create_vpnservice.return_value = \ + {self.res: dict(response)} + osc_utils.find_project.return_value.id = response['tenant_id'] + # Update response(finally returns 'data') + self.data = _generate_data(ordered_dict=response) + self.ordered_data = tuple( + response[column] for column in self.ordered_columns + ) + + def _set_all_params(self): + name = self.args.get('name') + description = self.args.get('description') + router_id = self.args.get('router_id') + subnet_id = self.args.get('subnet_id') + tenant_id = self.args.get('tenant_id') + arglist = [ + '--description', description, + '--project', tenant_id, + '--subnet', subnet_id, + '--router', router_id, + name, + ] + verifylist = [ + ('description', description), + ('project', tenant_id), + ('subnet', subnet_id), + ('router', router_id), + ('name', name), + ] + return arglist, verifylist + + def _test_create_with_all_params(self): + arglist, verifylist = self._set_all_params() + request, response = _generate_req_and_res(verifylist) + self._update_expect_response(request, response) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self._check_results(headers, data, request) + + def test_create_with_all_params(self): + self._test_create_with_all_params() + + +class TestDeleteVPNService(TestVPNService, common.TestDeleteVPNaaS): + + def setUp(self): + super(TestDeleteVPNService, self).setUp() + self.neutronclient.delete_vpnservice = mock.Mock( + return_value={self.res: _vpnservice}) + self.mocked = self.neutronclient.delete_vpnservice + self.cmd = vpnservice.DeleteVPNService(self.app, self.namespace) + + +class TestListVPNService(TestVPNService): + + def setUp(self): + super(TestListVPNService, self).setUp() + self.cmd = vpnservice.ListVPNService(self.app, self.namespace) + + self.short_header = ( + 'ID', + 'Name', + 'Router', + 'Subnet', + 'Flavor', + 'State', + 'Status', + ) + + self.short_data = ( + _vpnservice['id'], + _vpnservice['name'], + _vpnservice['router_id'], + _vpnservice['subnet_id'], + _vpnservice['flavor_id'], + _vpnservice['admin_state_up'], + _vpnservice['status'], + ) + + self.neutronclient.list_vpnservices = mock.Mock( + return_value={self.res_plural: [_vpnservice]}) + self.mocked = self.neutronclient.list_vpnservices + + def test_list_with_long_option(self): + arglist = ['--long'] + verifylist = [('long', True)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.headers), headers) + self.assertEqual([self.data], list(data)) + + def test_list_with_no_option(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.short_header), headers) + self.assertEqual([self.short_data], list(data)) + + +class TestSetVPNService(TestVPNService, common.TestSetVPNaaS): + + def setUp(self): + super(TestSetVPNService, self).setUp() + self.neutronclient.update_vpnservice = mock.Mock( + return_value={self.res: _vpnservice}) + self.mocked = self.neutronclient.update_vpnservice + self.cmd = vpnservice.SetVPNSercice(self.app, self.namespace) + + def test_set_name(self): + target = self.resource['id'] + update = 'change' + arglist = [target, '--name', update] + verifylist = [ + (self.res, target), + ('name', update), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'name': update}}) + self.assertIsNone(result) + + +class TestShowVPNService(TestVPNService, common.TestShowVPNaaS): + + def setUp(self): + super(TestShowVPNService, self).setUp() + self.neutronclient.show_vpnservice = mock.Mock( + return_value={self.res: _vpnservice}) + self.mocked = self.neutronclient.show_vpnservice + self.cmd = vpnservice.ShowVPNService(self.app, self.namespace) diff --git a/releasenotes/notes/support-vpnaas-cli-9478fb7cfe603e26.yaml b/releasenotes/notes/support-vpnaas-cli-9478fb7cfe603e26.yaml new file mode 100644 index 000000000..3b5e6e491 --- /dev/null +++ b/releasenotes/notes/support-vpnaas-cli-9478fb7cfe603e26.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + CLI support for the "VPN as a Service" feature, which is enhanced + VPNaaS functionality, as OSC plugin commands. diff --git a/setup.cfg b/setup.cfg index 9e0e45c74..eccd2e3b2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -132,6 +132,36 @@ openstack.neutronclient.v2 = network_log_set = neutronclient.osc.v2.logging.network_log:SetNetworkLog network_log_show = neutronclient.osc.v2.logging.network_log:ShowNetworkLog + vpn_endpoint_group_create = neutronclient.osc.v2.vpnaas.endpoint_group:CreateEndpointGroup + vpn_endpoint_group_delete = neutronclient.osc.v2.vpnaas.endpoint_group:DeleteEndpointGroup + vpn_endpoint_group_list = neutronclient.osc.v2.vpnaas.endpoint_group:ListEndpointGroup + vpn_endpoint_group_set = neutronclient.osc.v2.vpnaas.endpoint_group:SetEndpointGroup + vpn_endpoint_group_show = neutronclient.osc.v2.vpnaas.endpoint_group:ShowEndpointGroup + + vpn_ike_policy_create = neutronclient.osc.v2.vpnaas.ikepolicy:CreateIKEPolicy + vpn_ike_policy_delete = neutronclient.osc.v2.vpnaas.ikepolicy:DeleteIKEPolicy + vpn_ike_policy_list = neutronclient.osc.v2.vpnaas.ikepolicy:ListIKEPolicy + vpn_ike_policy_set = neutronclient.osc.v2.vpnaas.ikepolicy:SetIKEPolicy + vpn_ike_policy_show = neutronclient.osc.v2.vpnaas.ikepolicy:ShowIKEPolicy + + vpn_ipsec_policy_create = neutronclient.osc.v2.vpnaas.ipsecpolicy:CreateIPsecPolicy + vpn_ipsec_policy_delete = neutronclient.osc.v2.vpnaas.ipsecpolicy:DeleteIPsecPolicy + vpn_ipsec_policy_list = neutronclient.osc.v2.vpnaas.ipsecpolicy:ListIPsecPolicy + vpn_ipsec_policy_set = neutronclient.osc.v2.vpnaas.ipsecpolicy:SetIPsecPolicy + vpn_ipsec_policy_show = neutronclient.osc.v2.vpnaas.ipsecpolicy:ShowIPsecPolicy + + vpn_service_create = neutronclient.osc.v2.vpnaas.vpnservice:CreateVPNService + vpn_service_delete = neutronclient.osc.v2.vpnaas.vpnservice:DeleteVPNService + vpn_service_list = neutronclient.osc.v2.vpnaas.vpnservice:ListVPNService + vpn_service_set = neutronclient.osc.v2.vpnaas.vpnservice:SetVPNSercice + vpn_service_show = neutronclient.osc.v2.vpnaas.vpnservice:ShowVPNService + + vpn_ipsec_site_connection_create = neutronclient.osc.v2.vpnaas.ipsec_site_connection:CreateIPsecSiteConnection + vpn_ipsec_site_connection_delete = neutronclient.osc.v2.vpnaas.ipsec_site_connection:DeleteIPsecSiteConnection + vpn_ipsec_site_connection_list = neutronclient.osc.v2.vpnaas.ipsec_site_connection:ListIPsecSiteConnection + vpn_ipsec_site_connection_set = neutronclient.osc.v2.vpnaas.ipsec_site_connection:SetIPsecSiteConnection + vpn_ipsec_site_connection_show = neutronclient.osc.v2.vpnaas.ipsec_site_connection:ShowIPsecSiteConnection + neutron.cli.v2 = bash-completion = neutronclient.shell:BashCompletionCommand From cf76e3eadb5647c2ac9a7032c4cf02d06a4eca35 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Tue, 16 Jan 2018 04:31:30 +0000 Subject: [PATCH 637/845] Updated from global requirements Change-Id: Ibfc6c074416de0dab68b37623d64b7e737849431 --- test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index 23d802642..775eaec22 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -9,7 +9,7 @@ flake8-import-order==0.12 # LGPLv3 mox3>=0.20.0 # Apache-2.0 mock>=2.0.0 # BSD openstackdocstheme>=1.17.0 # Apache-2.0 -oslotest>=1.10.0 # Apache-2.0 +oslotest>=3.2.0 # Apache-2.0 osprofiler>=1.4.0 # Apache-2.0 python-openstackclient>=3.12.0 # Apache-2.0 python-subunit>=1.0.0 # Apache-2.0/BSD From 5fddd714524f795bcd830a0906594d4143842f3f Mon Sep 17 00:00:00 2001 From: Deepthi V V Date: Tue, 12 Sep 2017 12:29:51 +0530 Subject: [PATCH 638/845] VNI support in BGPVPN CLI Changes to support VNI attribute through BGPVPN CLIs Change-Id: I1f828ef965d5b117539b88a290e2b9b966016336 Partial-Bug: #1590908 Depends-On: I3d47000bb0ad8434f88d016acb5b05fb7142e47a --- neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py | 10 ++++++++++ .../tests/unit/osc/v2/networking_bgpvpn/fakes.py | 1 + .../tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py | 4 ++++ ...-vni-in-networking-bgpvpn-cli-d284b73b40b79495.yaml | 7 +++++++ 4 files changed, 22 insertions(+) create mode 100644 releasenotes/notes/support-vni-in-networking-bgpvpn-cli-d284b73b40b79495.yaml diff --git a/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py b/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py index ab49c2ff5..99d9c4ea3 100644 --- a/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py +++ b/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py @@ -40,6 +40,7 @@ nc_osc_utils.LIST_LONG_ONLY), ('networks', 'Associated Networks', nc_osc_utils.LIST_LONG_ONLY), ('routers', 'Associated Routers', nc_osc_utils.LIST_LONG_ONLY), + ('vni', 'VNI', nc_osc_utils.LIST_LONG_ONLY), ) _formatters = { 'route_targets': format_columns.ListColumn, @@ -144,6 +145,10 @@ def is_appended(): action='store_true', help=_('Empty route distinguisher list'), ) + parser.add_argument( + '--vni', type=int, + help=_('VXLAN Network Identifier to be used for this BGPVPN ' + 'when a VXLAN encapsulation is used')) def _args2body(client_manager, id, action, args): @@ -159,6 +164,9 @@ def _args2body(client_manager, id, action, args): if 'name' in args and args.name is not None: attrs['name'] = str(args.name) + if 'vni' in args and args.vni is not None: + attrs['vni'] = args.vni + if args.purge_route_target: attrs['route_targets'] = [] elif args.route_targets: @@ -235,6 +243,8 @@ def take_action(self, parsed_args): attrs['export_targets'] = parsed_args.export_targets if parsed_args.route_distinguishers is not None: attrs['route_distinguishers'] = parsed_args.route_distinguishers + if parsed_args.vni is not None: + attrs['vni'] = parsed_args.vni if 'project' in parsed_args and parsed_args.project is not None: project_id = nc_osc_utils.find_project( self.app.client_manager.identity, diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py index 7da3a9850..3d3ec597e 100644 --- a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py +++ b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py @@ -68,6 +68,7 @@ def create_one_bgpvpn(attrs=None): 'route_distinguishers': [], 'networks': [], 'routers': [], + 'vni': 100, } # Overwrite default attributes. diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py index f13980eda..71f5acccf 100644 --- a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py +++ b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py @@ -63,6 +63,7 @@ def test_create_bgpvpn_with_no_args(self): ('project', None), ('name', None), ('type', 'l3'), + ('vni', None), ('route_targets', None), ('import_targets', None), ('export_targets', None), @@ -83,6 +84,7 @@ def test_create_bgpvpn_with_all_args(self): 'tenant_id': 'new_fake_project_id', 'name': 'fake_name', 'type': 'l2', + 'vni': 100, 'route_targets': ['fake_rt1', 'fake_rt2', 'fake_rt3'], 'import_targets': ['fake_irt1', 'fake_irt2', 'fake_irt3'], 'export_targets': ['fake_ert1', 'fake_ert2', 'fake_ert3'], @@ -95,6 +97,7 @@ def test_create_bgpvpn_with_all_args(self): '--project', fake_bgpvpn['tenant_id'], '--name', fake_bgpvpn['name'], '--type', fake_bgpvpn['type'], + '--vni', str(fake_bgpvpn['vni']), ] for rt in fake_bgpvpn['route_targets']: arglist.extend(['--route-target', rt]) @@ -108,6 +111,7 @@ def test_create_bgpvpn_with_all_args(self): ('project', fake_bgpvpn['tenant_id']), ('name', fake_bgpvpn['name']), ('type', fake_bgpvpn['type']), + ('vni', fake_bgpvpn['vni']), ('route_targets', fake_bgpvpn['route_targets']), ('import_targets', fake_bgpvpn['import_targets']), ('export_targets', fake_bgpvpn['export_targets']), diff --git a/releasenotes/notes/support-vni-in-networking-bgpvpn-cli-d284b73b40b79495.yaml b/releasenotes/notes/support-vni-in-networking-bgpvpn-cli-d284b73b40b79495.yaml new file mode 100644 index 000000000..3c3e51c28 --- /dev/null +++ b/releasenotes/notes/support-vni-in-networking-bgpvpn-cli-d284b73b40b79495.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + CLI support for VXLAN VNI ID attribute in bgpvpn. + An optional argument ``--vni`` is added to ``openstack bgpvpn`` + commands to configure VXLAN Network Identifier when VXLAN + encapsulation is used for the bgpvpn. From 9e05c1bab51d7b9af3c16232f2757438dcd3cec8 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Thu, 18 Jan 2018 03:29:56 +0000 Subject: [PATCH 639/845] Updated from global requirements Change-Id: Ic8c98ad03917ed628453c28200defed000592045 --- test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index 775eaec22..e98669ef8 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -15,7 +15,7 @@ python-openstackclient>=3.12.0 # Apache-2.0 python-subunit>=1.0.0 # Apache-2.0/BSD reno>=2.5.0 # Apache-2.0 requests-mock>=1.1.0 # Apache-2.0 -sphinx>=1.6.2 # BSD +sphinx!=1.6.6,>=1.6.2 # BSD testrepository>=0.0.18 # Apache-2.0/BSD testtools>=2.2.0 # MIT testscenarios>=0.4 # Apache-2.0/BSD From 0e61b3ba1c54be7ae7a218513d30800723a26524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89douard=20Thuleau?= Date: Wed, 15 Nov 2017 10:16:00 +0100 Subject: [PATCH 640/845] Add commands to support BGP VPN route control new API extension Networking-bgpvpn API extension [0] brings new API calls. That patch adds command to OSC CLI to support them. It does not implement all the API extension, still missing new router attribute 'advertise_extra_routes' and the BGP VPN 'local_pref' attribute. [0] https://developer.openstack.org/api-ref/network/v2/index.html#port-associations Change-Id: I45a160b0caec534d517b103db91dc738c006978b Partially-Implements: blueprint routes-control Depends-On: I263e1ee6cf4e1a91be91a4a78f4a160f64d33cc6 --- doc/source/cli/osc/v2/networking-bgpvpn.rst | 3 + .../osc/v2/networking_bgpvpn/bgpvpn.py | 2 + .../osc/v2/networking_bgpvpn/constants.py | 4 + .../v2/networking_bgpvpn/port_association.py | 315 ++++++++++++++++++ .../networking_bgpvpn/resource_association.py | 79 ++++- .../networking_bgpvpn/router_association.py | 2 +- .../unit/osc/v2/networking_bgpvpn/fakes.py | 25 +- .../osc/v2/networking_bgpvpn/test_bgpvpn.py | 2 + .../test_resource_association.py | 31 ++ neutronclient/v2_0/client.py | 31 ++ ...bgpvpn-route-control-aeda3e698486f73b.yaml | 6 + setup.cfg | 6 + 12 files changed, 499 insertions(+), 7 deletions(-) create mode 100644 neutronclient/osc/v2/networking_bgpvpn/port_association.py create mode 100644 releasenotes/notes/support-bgpvpn-route-control-aeda3e698486f73b.yaml diff --git a/doc/source/cli/osc/v2/networking-bgpvpn.rst b/doc/source/cli/osc/v2/networking-bgpvpn.rst index 371e47287..46ee47e6e 100644 --- a/doc/source/cli/osc/v2/networking-bgpvpn.rst +++ b/doc/source/cli/osc/v2/networking-bgpvpn.rst @@ -32,3 +32,6 @@ Network v2 .. autoprogram-cliff:: openstack.neutronclient.v2 :command: bgpvpn router association * + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgpvpn port association * diff --git a/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py b/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py index 99d9c4ea3..63b429137 100644 --- a/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py +++ b/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py @@ -40,6 +40,7 @@ nc_osc_utils.LIST_LONG_ONLY), ('networks', 'Associated Networks', nc_osc_utils.LIST_LONG_ONLY), ('routers', 'Associated Routers', nc_osc_utils.LIST_LONG_ONLY), + ('ports', 'Associated Ports', nc_osc_utils.LIST_LONG_ONLY), ('vni', 'VNI', nc_osc_utils.LIST_LONG_ONLY), ) _formatters = { @@ -49,6 +50,7 @@ 'route_distinguishers': format_columns.ListColumn, 'networks': format_columns.ListColumn, 'routers': format_columns.ListColumn, + 'ports': format_columns.ListColumn, } diff --git a/neutronclient/osc/v2/networking_bgpvpn/constants.py b/neutronclient/osc/v2/networking_bgpvpn/constants.py index 775721e9c..7de1329a6 100644 --- a/neutronclient/osc/v2/networking_bgpvpn/constants.py +++ b/neutronclient/osc/v2/networking_bgpvpn/constants.py @@ -24,3 +24,7 @@ ROUTER_RESOURCE_NAME = 'router' ROUTER_ASSOC = '%s_association' % ROUTER_RESOURCE_NAME ROUTER_ASSOCS = '%ss' % ROUTER_ASSOC + +PORT_RESOURCE_NAME = 'port' +PORT_ASSOC = '%s_association' % PORT_RESOURCE_NAME +PORT_ASSOCS = '%ss' % PORT_ASSOC diff --git a/neutronclient/osc/v2/networking_bgpvpn/port_association.py b/neutronclient/osc/v2/networking_bgpvpn/port_association.py new file mode 100644 index 000000000..75d5c0fd9 --- /dev/null +++ b/neutronclient/osc/v2/networking_bgpvpn/port_association.py @@ -0,0 +1,315 @@ +# Copyright (c) 2017 Juniper networks Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +import logging + +from osc_lib.cli import format_columns +from osc_lib.cli import parseractions + +from neutronclient._i18n import _ +from neutronclient.osc import utils as nc_osc_utils +from neutronclient.osc.v2.networking_bgpvpn import constants +from neutronclient.osc.v2.networking_bgpvpn import resource_association + +LOG = logging.getLogger(__name__) + + +class BgpvpnPortAssoc(object): + _assoc_res_name = constants.PORT_RESOURCE_NAME + _resource = constants.PORT_ASSOC + _resource_plural = constants.PORT_ASSOCS + + _attr_map = ( + ('id', 'ID', nc_osc_utils.LIST_BOTH), + ('tenant_id', 'Project', nc_osc_utils.LIST_LONG_ONLY), + ('%s_id' % _assoc_res_name, '%s ID' % _assoc_res_name.capitalize(), + nc_osc_utils.LIST_BOTH), + ('prefix_routes', 'Prefix Routes (BGP LOCAL_PREF)', + nc_osc_utils.LIST_LONG_ONLY), + ('bgpvpn_routes', 'BGP VPN Routes (BGP LOCAL_PREF)', + nc_osc_utils.LIST_LONG_ONLY), + ('advertise_fixed_ips', "Advertise Port's Fixed IPs", + nc_osc_utils.LIST_LONG_ONLY), + ) + _formatters = { + 'prefix_routes': format_columns.ListColumn, + 'bgpvpn_routes': format_columns.ListColumn, + } + + def _transform_resource(self, data): + """Transforms BGP VPN port association routes property + + That permits to easily format the command output with ListColumn + formater and separate the two route types. + + {'routes': + [ + { + 'type': 'prefix', + 'local_pref': 100, + 'prefix': '8.8.8.0/27', + }, + { + 'type': 'prefix', + 'local_pref': 42, + 'prefix': '80.50.30.0/28', + }, + { + 'type': 'bgpvpn', + 'local_pref': 50, + 'bgpvpn': '157d72a9-9968-48e7-8087-6c9a9bc7a181', + }, + { + 'type': 'bgpvpn', + 'bgpvpn': 'd5c7aaab-c7e8-48b3-85ca-a115c00d3603', + }, + ], + } + + to + + { + 'prefix_routes': [ + '8.8.8.0/27 (100)', + '80.50.30.0/28 (42)', + ], + 'bgpvpn_routes': [ + '157d72a9-9968-48e7-8087-6c9a9bc7a181 (50)', + 'd5c7aaab-c7e8-48b3-85ca-a115c00d3603', + ], + } + """ + for route in data.get('routes', []): + local_pref = '' + if route.get('local_pref'): + local_pref = ' (%d)' % route.get('local_pref') + if route['type'] == 'prefix': + data.setdefault('prefix_routes', []).append( + '%s%s' % (route['prefix'], local_pref) + ) + elif route['type'] == 'bgpvpn': + data.setdefault('bgpvpn_routes', []).append( + '%s%s' % (route['bgpvpn_id'], local_pref) + ) + else: + LOG.warning("Unknown route type %s (%s).", route['type'], + route) + data.pop('routes', None) + + def _get_common_parser(self, parser): + """Adds to parser arguments common to create, set and unset commands. + + :params ArgumentParser parser: argparse object contains all command's + arguments + """ + ADVERTISE_ROUTE = _("Fixed IPs of the port will be advertised to the " + "BGP VPN%s") % ( + _(' (default)') if self._action == 'create' + else "") + NOT_ADVERTISE_ROUTE = _("Fixed IPs of the port will not be advertised " + "to the BGP VPN") + + LOCAL_PREF_VALUE = _(". Optionally, can control the value of the BGP " + "LOCAL_PREF of the routes that will be " + "advertised") + + ADD_PREFIX_ROUTE = _("Add prefix route in CIDR notation%s") %\ + LOCAL_PREF_VALUE + REMOVE_PREFIX_ROUTE = _("Remove prefix route in CIDR notation") + REPEAT_PREFIX_ROUTE = _("repeat option for multiple prefix routes") + + ADD_BGVPVPN_ROUTE = _("Add BGP VPN route for route leaking%s") %\ + LOCAL_PREF_VALUE + REMOVE_BGPVPN_ROUTE = _("Remove BGP VPN route") + REPEAT_BGPVPN_ROUTE = _("repeat option for multiple BGP VPN routes") + + group_advertise_fixed_ips = parser.add_mutually_exclusive_group() + group_advertise_fixed_ips.add_argument( + '--advertise-fixed-ips', + action='store_true', + help=NOT_ADVERTISE_ROUTE if self._action == 'unset' + else ADVERTISE_ROUTE, + ) + group_advertise_fixed_ips.add_argument( + '--no-advertise-fixed-ips', + action='store_true', + help=ADVERTISE_ROUTE if self._action == 'unset' + else NOT_ADVERTISE_ROUTE, + ) + + if self._action in ['create', 'set']: + parser.add_argument( + '--prefix-route', + metavar="prefix=[,local_pref=]", + dest='prefix_routes', + action=parseractions.MultiKeyValueAction, + required_keys=['prefix'], + optional_keys=['local_pref'], + help="%s (%s)" % (ADD_PREFIX_ROUTE, REPEAT_PREFIX_ROUTE), + ) + parser.add_argument( + '--bgpvpn-route', + metavar="bgpvpn=[,local_pref=]", + dest='bgpvpn_routes', + action=parseractions.MultiKeyValueAction, + required_keys=['bgpvpn'], + optional_keys=['local_pref'], + help="%s (%s)" % (ADD_BGVPVPN_ROUTE, REPEAT_BGPVPN_ROUTE), + ) + else: + parser.add_argument( + '--prefix-route', + metavar="", + dest='prefix_routes', + action='append', + help="%s (%s)" % (REMOVE_PREFIX_ROUTE, REPEAT_PREFIX_ROUTE), + ) + parser.add_argument( + '--bgpvpn-route', + metavar="", + dest='bgpvpn_routes', + action='append', + help="%s (%s)" % (REMOVE_BGPVPN_ROUTE, REPEAT_BGPVPN_ROUTE), + ) + if self._action != 'create': + parser.add_argument( + '--no-prefix-route' if self._action == 'set' else + '--all-prefix-routes', + dest='purge_prefix_route', + action='store_true', + help=_('Empty prefix route list'), + ) + parser.add_argument( + '--no-bgpvpn-route' if self._action == 'set' else + '--all-bgpvpn-routes', + dest='purge_bgpvpn_route', + action='store_true', + help=_('Empty BGP VPN route list'), + ) + + def _args2body(self, bgpvpn_id, args): + client = self.app.client_manager.neutronclient + attrs = {} + + if self._action != 'create': + assoc = client.find_resource_by_id( + self._resource, + args.resource_association_id, + cmd_resource='bgpvpn_%s_assoc' % self._assoc_res_name, + parent_id=bgpvpn_id) + else: + assoc = {'routes': []} + + if args.advertise_fixed_ips: + attrs['advertise_fixed_ips'] = self._action != 'unset' + elif args.no_advertise_fixed_ips: + attrs['advertise_fixed_ips'] = self._action == 'unset' + + prefix_routes = None + if 'purge_prefix_route' in args and args.purge_prefix_route: + prefix_routes = [] + else: + prefix_routes = {r['prefix']: r.get('local_pref') + for r in assoc['routes'] + if r['type'] == 'prefix'} + if args.prefix_routes: + if self._action in ['create', 'set']: + prefix_routes.update({r['prefix']: r.get('local_pref') + for r in args.prefix_routes}) + elif self._action == 'unset': + for prefix in args.prefix_routes: + prefix_routes.pop(prefix, None) + + bgpvpn_routes = None + if 'purge_bgpvpn_route' in args and args.purge_bgpvpn_route: + bgpvpn_routes = [] + else: + bgpvpn_routes = {r['bgpvpn_id']: r.get('local_pref') + for r in assoc['routes'] + if r['type'] == 'bgpvpn'} + if args.bgpvpn_routes: + if self._action == 'unset': + routes = [ + {'bgpvpn': bgpvpn} for bgpvpn in args.bgpvpn_routes + ] + else: + routes = args.bgpvpn_routes + args_bgpvpn_routes = { + client.find_resource(constants.BGPVPN, r['bgpvpn'])['id']: + r.get('local_pref') + for r in routes + } + if self._action in ['create', 'set']: + bgpvpn_routes.update(args_bgpvpn_routes) + elif self._action == 'unset': + for bgpvpn_id in args_bgpvpn_routes: + bgpvpn_routes.pop(bgpvpn_id, None) + + if prefix_routes is not None and not prefix_routes: + attrs.setdefault('routes', []) + elif prefix_routes is not None: + for prefix, local_pref in prefix_routes.items(): + route = { + 'type': 'prefix', + 'prefix': prefix, + } + if local_pref: + route['local_pref'] = int(local_pref) + attrs.setdefault('routes', []).append(route) + if bgpvpn_routes is not None and not bgpvpn_routes: + attrs.setdefault('routes', []) + elif bgpvpn_routes is not None: + for bgpvpn_id, local_pref in bgpvpn_routes.items(): + route = { + 'type': 'bgpvpn', + 'bgpvpn_id': bgpvpn_id, + } + if local_pref: + route['local_pref'] = int(local_pref) + attrs.setdefault('routes', []).append(route) + + return {self._resource: attrs} + + +class CreateBgpvpnPortAssoc(BgpvpnPortAssoc, + resource_association.CreateBgpvpnResAssoc): + _description = _("Create a BGP VPN port association") + + +class SetBgpvpnPortAssoc(BgpvpnPortAssoc, + resource_association.SetBgpvpnResAssoc): + _description = _("Set BGP VPN port association properties") + + +class UnsetBgpvpnPortAssoc(BgpvpnPortAssoc, + resource_association.UnsetBgpvpnResAssoc): + _description = _("Unset BGP VPN port association properties") + + +class DeleteBgpvpnPortAssoc(BgpvpnPortAssoc, + resource_association.DeleteBgpvpnResAssoc): + _description = _("Delete a BGP VPN port association(s) for a given BGP " + "VPN") + + +class ListBgpvpnPortAssoc(BgpvpnPortAssoc, + resource_association.ListBgpvpnResAssoc): + _description = _("List BGP VPN port associations for a given BGP VPN") + + +class ShowBgpvpnPortAssoc(BgpvpnPortAssoc, + resource_association.ShowBgpvpnResAssoc): + _description = _("Show information of a given BGP VPN port association") diff --git a/neutronclient/osc/v2/networking_bgpvpn/resource_association.py b/neutronclient/osc/v2/networking_bgpvpn/resource_association.py index 485803c47..4784100f8 100644 --- a/neutronclient/osc/v2/networking_bgpvpn/resource_association.py +++ b/neutronclient/osc/v2/networking_bgpvpn/resource_association.py @@ -16,6 +16,7 @@ import logging +from osc_lib.cli import parseractions from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils as osc_utils @@ -29,6 +30,7 @@ class CreateBgpvpnResAssoc(command.ShowOne): """Create a BGP VPN resource association""" + _action = 'create' def get_parser(self, prog_name): parser = super(CreateBgpvpnResAssoc, self).get_parser(prog_name) @@ -45,6 +47,11 @@ def get_parser(self, prog_name): help=(_("%s to associate the BGP VPN (name or ID)") % self._assoc_res_name.capitalize()), ) + + get_common_parser = getattr(self, '_get_common_parser', None) + if callable(get_common_parser): + get_common_parser(parser) + return parser def take_action(self, parsed_args): @@ -66,7 +73,16 @@ def take_action(self, parsed_args): parsed_args.project_domain, ).id body[self._resource]['tenant_id'] = project_id + + arg2body = getattr(self, '_args2body', None) + if callable(arg2body): + body[self._resource].update( + arg2body(bgpvpn['id'], parsed_args)[self._resource]) + obj = create_method(bgpvpn['id'], body)[self._resource] + transform = getattr(self, '_transform_resource', None) + if callable(transform): + transform(obj) columns, display_columns = nc_osc_utils.get_columns(obj, self._attr_map) data = osc_utils.get_dict_properties(obj, columns, @@ -74,6 +90,48 @@ def take_action(self, parsed_args): return display_columns, data +class SetBgpvpnResAssoc(command.Command): + """Set BGP VPN resource association properties""" + _action = 'set' + + def get_parser(self, prog_name): + parser = super(SetBgpvpnResAssoc, self).get_parser(prog_name) + parser.add_argument( + 'resource_association_id', + metavar="<%s association ID>" % self._assoc_res_name, + help=(_("%s association ID to update") % + self._assoc_res_name.capitalize()), + ) + parser.add_argument( + 'bgpvpn', + metavar="", + help=(_("BGP VPN the %s association belongs to (name or ID)") % + self._assoc_res_name), + ) + + get_common_parser = getattr(self, '_get_common_parser', None) + if callable(get_common_parser): + get_common_parser(parser) + + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + update_method = getattr( + client, 'update_bgpvpn_%s_assoc' % self._assoc_res_name) + bgpvpn = client.find_resource(constants.BGPVPN, parsed_args.bgpvpn) + arg2body = getattr(self, '_args2body', None) + if callable(arg2body): + body = arg2body(bgpvpn['id'], parsed_args) + update_method(bgpvpn['id'], parsed_args.resource_association_id, + body) + + +class UnsetBgpvpnResAssoc(SetBgpvpnResAssoc): + """Unset BGP VPN resource association properties""" + _action = 'unset' + + class DeleteBgpvpnResAssoc(command.Command): """Remove a BGP VPN resource association(s) for a given BGP VPN""" @@ -89,7 +147,8 @@ def get_parser(self, prog_name): parser.add_argument( 'bgpvpn', metavar="", - help=_("BGP VPN the association belongs to (name or ID)"), + help=(_("BGP VPN the %s association belongs to (name or ID)") % + self._assoc_res_name), ) return parser @@ -137,6 +196,13 @@ def get_parser(self, prog_name): action='store_true', help=_("List additional fields in output"), ) + parser.add_argument( + '--property', + metavar="", + help=_("Filter property to apply on returned BGP VPNs (repeat to " + "filter on multiple properties)"), + action=parseractions.KeyValueAction, + ) return parser def take_action(self, parsed_args): @@ -144,8 +210,14 @@ def take_action(self, parsed_args): list_method = getattr(client, 'list_bgpvpn_%s_assocs' % self._assoc_res_name) bgpvpn = client.find_resource(constants.BGPVPN, parsed_args.bgpvpn) + params = {} + if parsed_args.property: + params.update(parsed_args.property) objs = list_method(bgpvpn['id'], - retrieve_all=True)[self._resource_plural] + retrieve_all=True, **params)[self._resource_plural] + transform = getattr(self, '_transform_resource', None) + if callable(transform): + [transform(obj) for obj in objs] headers, columns = nc_osc_utils.get_column_definitions( self._attr_map, long_listing=parsed_args.long) return (headers, (osc_utils.get_dict_properties( @@ -181,6 +253,9 @@ def take_action(self, parsed_args): cmd_resource='bgpvpn_%s_assoc' % self._assoc_res_name, parent_id=bgpvpn['id']) obj = show_method(bgpvpn['id'], assoc['id'])[self._resource] + transform = getattr(self, '_transform_resource', None) + if callable(transform): + transform(obj) columns, display_columns = nc_osc_utils.get_columns(obj, self._attr_map) data = osc_utils.get_dict_properties(obj, columns, diff --git a/neutronclient/osc/v2/networking_bgpvpn/router_association.py b/neutronclient/osc/v2/networking_bgpvpn/router_association.py index ee788d33c..a51fbe604 100644 --- a/neutronclient/osc/v2/networking_bgpvpn/router_association.py +++ b/neutronclient/osc/v2/networking_bgpvpn/router_association.py @@ -1,4 +1,4 @@ -# Copyright (c) 2016 Juniper Routerworks Inc. +# Copyright (c) 2016 Juniper networks Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py index 3d3ec597e..0103904e3 100644 --- a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py +++ b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py @@ -26,11 +26,18 @@ DeleteBgpvpnResAssoc from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ ListBgpvpnResAssoc +from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ + SetBgpvpnResAssoc from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ ShowBgpvpnResAssoc +from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ + UnsetBgpvpnResAssoc from neutronclient.tests.unit.osc.v2 import fakes as test_fakes +_FAKE_PROJECT_ID = 'fake_project_id' + + class TestNeutronClientBgpvpn(test_fakes.TestNeutronClientOSCV2): def setUp(self): @@ -38,11 +45,11 @@ def setUp(self): self.neutronclient.find_resource = mock.Mock( side_effect=lambda resource, name_or_id, project_id=None, cmd_resource=None, parent_id=None, fields=None: - {'id': name_or_id}) + {'id': name_or_id, 'tenant_id': _FAKE_PROJECT_ID}) self.neutronclient.find_resource_by_id = mock.Mock( side_effect=lambda resource, resource_id, cmd_resource=None, parent_id=None, fields=None: - {'id': resource_id}) + {'id': resource_id, 'tenant_id': _FAKE_PROJECT_ID}) nc_osc_utils.find_project = mock.Mock( side_effect=lambda _, name_or_id, __: mock.Mock(id=name_or_id)) @@ -59,7 +66,7 @@ def create_one_bgpvpn(attrs=None): # Set default attributes. bgpvpn_attrs = { 'id': 'fake_bgpvpn_id', - 'tenant_id': 'fake_project_id', + 'tenant_id': _FAKE_PROJECT_ID, 'name': '', 'type': 'l3', 'route_targets': [], @@ -68,11 +75,13 @@ def create_one_bgpvpn(attrs=None): 'route_distinguishers': [], 'networks': [], 'routers': [], + 'ports': [], 'vni': 100, } # Overwrite default attributes. bgpvpn_attrs.update(attrs) + return copy.deepcopy(bgpvpn_attrs) @staticmethod @@ -108,6 +117,14 @@ class CreateBgpvpnFakeResAssoc(BgpvpnFakeAssoc, CreateBgpvpnResAssoc): pass +class SetBgpvpnFakeResAssoc(BgpvpnFakeAssoc, SetBgpvpnResAssoc): + pass + + +class UnsetBgpvpnFakeResAssoc(BgpvpnFakeAssoc, UnsetBgpvpnResAssoc): + pass + + class DeleteBgpvpnFakeResAssoc(BgpvpnFakeAssoc, DeleteBgpvpnResAssoc): pass @@ -132,7 +149,7 @@ def create_one_resource(attrs=None): # Set default attributes. res_attrs = { 'id': 'fake_resource_id', - 'tenant_id': 'fake_project_id', + 'tenant_id': _FAKE_PROJECT_ID, } # Overwrite default attributes. diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py index 71f5acccf..bfa710446 100644 --- a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py +++ b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py @@ -126,6 +126,8 @@ def test_create_bgpvpn_with_all_args(self): fake_bgpvpn_call.pop('id') fake_bgpvpn_call.pop('networks') fake_bgpvpn_call.pop('routers') + fake_bgpvpn_call.pop('ports') + self.neutronclient.create_bgpvpn.assert_called_once_with( {constants.BGPVPN: fake_bgpvpn_call}) self.assertEqual(sorted_headers, cols) diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py index 6978d85cd..49be9f452 100644 --- a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py +++ b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py @@ -81,6 +81,7 @@ def test_create_resource_association(self): fake_res_assoc_call = copy.deepcopy(fake_res_assoc) fake_res_assoc_call.pop('id') + self.neutronclient.create_bgpvpn_fake_resource_assoc.\ assert_called_once_with( fake_bgpvpn['id'], @@ -89,6 +90,36 @@ def test_create_resource_association(self): self.assertEqual(_get_data(fake_res_assoc), data) +class TestSetResAssoc(fakes.TestNeutronClientBgpvpn): + def setUp(self): + super(TestSetResAssoc, self).setUp() + self.cmd = fakes.SetBgpvpnFakeResAssoc(self.app, self.namespace) + + def test_set_resource_association(self): + fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() + fake_res = fakes.FakeResource.create_one_resource() + fake_res_assoc = fakes.FakeResAssoc.create_one_resource_association( + fake_res) + self.neutronclient.update_bgpvpn_fake_resource_assoc = mock.Mock( + return_value={fakes.BgpvpnFakeAssoc._resource: fake_res_assoc}) + arglist = [ + fake_res_assoc['id'], + fake_bgpvpn['id'], + ] + verifylist = [ + ('resource_association_id', fake_res_assoc['id']), + ('bgpvpn', fake_bgpvpn['id']), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.neutronclient.update_bgpvpn_fake_resource_assoc.\ + assert_not_called() + self.assertIsNone(result) + + class TestDeleteResAssoc(fakes.TestNeutronClientBgpvpn): def setUp(self): super(TestDeleteResAssoc, self).setUp() diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 412e1b80e..cb84b9fc9 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -651,6 +651,8 @@ class Client(ClientBase): bgpvpn_router_associations_path = "/bgpvpn/bgpvpns/%s/router_associations" bgpvpn_router_association_path =\ "/bgpvpn/bgpvpns/%s/router_associations/%s" + bgpvpn_port_associations_path = "/bgpvpn/bgpvpns/%s/port_associations" + bgpvpn_port_association_path = "/bgpvpn/bgpvpns/%s/port_associations/%s" network_logs_path = "/log/logs" network_log_path = "/log/logs/%s" network_loggables_path = "/log/loggable-resources" @@ -707,6 +709,7 @@ class Client(ClientBase): 'bgpvpns': 'bgpvpn', 'network_associations': 'network_association', 'router_associations': 'router_association', + 'port_associations': 'port_association', 'flow_classifiers': 'flow_classifier', 'port_pairs': 'port_pair', 'port_pair_groups': 'port_pair_group', @@ -2193,6 +2196,34 @@ def delete_bgpvpn_router_assoc(self, bgpvpn, router_assoc): return self.delete( self.bgpvpn_router_association_path % (bgpvpn, router_assoc)) + def list_bgpvpn_port_assocs(self, bgpvpn, retrieve_all=True, **_params): + """Fetches a list of port associations for a given BGP VPN.""" + return self.list('port_associations', + self.bgpvpn_port_associations_path % bgpvpn, + retrieve_all, **_params) + + def show_bgpvpn_port_assoc(self, bgpvpn, port_assoc, **_params): + """Fetches information of a certain BGP VPN's port association""" + return self.get( + self.bgpvpn_port_association_path % (bgpvpn, port_assoc), + params=_params) + + def create_bgpvpn_port_assoc(self, bgpvpn, body=None): + """Creates a new BGP VPN port association""" + return self.post(self.bgpvpn_port_associations_path % bgpvpn, + body=body) + + def update_bgpvpn_port_assoc(self, bgpvpn, port_assoc, body=None): + """Updates a BGP VPN port association""" + return self.put( + self.bgpvpn_port_association_path % (bgpvpn, port_assoc), + body=body) + + def delete_bgpvpn_port_assoc(self, bgpvpn, port_assoc): + """Deletes the specified BGP VPN port association""" + return self.delete( + self.bgpvpn_port_association_path % (bgpvpn, port_assoc)) + def create_sfc_port_pair(self, body=None): """Creates a new Port Pair.""" return self.post(self.sfc_port_pairs_path, body=body) diff --git a/releasenotes/notes/support-bgpvpn-route-control-aeda3e698486f73b.yaml b/releasenotes/notes/support-bgpvpn-route-control-aeda3e698486f73b.yaml new file mode 100644 index 000000000..2f7ed1720 --- /dev/null +++ b/releasenotes/notes/support-bgpvpn-route-control-aeda3e698486f73b.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Add BGP VPN `port association `_ + support to the CLI, which are introduced for BGP VPN interconnections by the + ``bgpvpn-routes-control`` API extension. diff --git a/setup.cfg b/setup.cfg index eccd2e3b2..67a2a35e4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -124,6 +124,12 @@ openstack.neutronclient.v2 = bgpvpn_router_association_delete = neutronclient.osc.v2.networking_bgpvpn.router_association:DeleteBgpvpnRouterAssoc bgpvpn_router_association_list = neutronclient.osc.v2.networking_bgpvpn.router_association:ListBgpvpnRouterAssoc bgpvpn_router_association_show = neutronclient.osc.v2.networking_bgpvpn.router_association:ShowBgpvpnRouterAssoc + bgpvpn_port_association_create = neutronclient.osc.v2.networking_bgpvpn.port_association:CreateBgpvpnPortAssoc + bgpvpn_port_association_set = neutronclient.osc.v2.networking_bgpvpn.port_association:SetBgpvpnPortAssoc + bgpvpn_port_association_unset = neutronclient.osc.v2.networking_bgpvpn.port_association:UnsetBgpvpnPortAssoc + bgpvpn_port_association_delete = neutronclient.osc.v2.networking_bgpvpn.port_association:DeleteBgpvpnPortAssoc + bgpvpn_port_association_list = neutronclient.osc.v2.networking_bgpvpn.port_association:ListBgpvpnPortAssoc + bgpvpn_port_association_show = neutronclient.osc.v2.networking_bgpvpn.port_association:ShowBgpvpnPortAssoc network_loggable_resources_list = neutronclient.osc.v2.logging.network_log:ListLoggableResource network_log_create = neutronclient.osc.v2.logging.network_log:CreateNetworkLog From 288aa63756c633ad30414f58fe01934e752f239c Mon Sep 17 00:00:00 2001 From: Thomas Morin Date: Fri, 19 Jan 2018 10:56:38 +0100 Subject: [PATCH 641/845] osc, bgpvpn: add support for 'local_pref' BGPVPN attribute This change adds support for the 'local_pref' attribute of the BGPVPN resource. Change-Id: I330a18212d4d68faf79ef5a16e304bc3406ce7ff Partially-Implements: blueprint routes-control --- neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py | 11 +++++++++++ .../tests/unit/osc/v2/networking_bgpvpn/fakes.py | 1 + .../unit/osc/v2/networking_bgpvpn/test_bgpvpn.py | 4 ++++ 3 files changed, 16 insertions(+) diff --git a/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py b/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py index 63b429137..6ce93ac65 100644 --- a/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py +++ b/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py @@ -42,6 +42,7 @@ ('routers', 'Associated Routers', nc_osc_utils.LIST_LONG_ONLY), ('ports', 'Associated Ports', nc_osc_utils.LIST_LONG_ONLY), ('vni', 'VNI', nc_osc_utils.LIST_LONG_ONLY), + ('local_pref', 'Local Pref', nc_osc_utils.LIST_LONG_ONLY), ) _formatters = { 'route_targets': format_columns.ListColumn, @@ -151,6 +152,11 @@ def is_appended(): '--vni', type=int, help=_('VXLAN Network Identifier to be used for this BGPVPN ' 'when a VXLAN encapsulation is used')) + parser.add_argument( + '--local-pref', type=int, + dest='local_pref', + help=_('Default BGP LOCAL_PREF to use in route advertisements' + 'towards this BGPVPN.')) def _args2body(client_manager, id, action, args): @@ -169,6 +175,9 @@ def _args2body(client_manager, id, action, args): if 'vni' in args and args.vni is not None: attrs['vni'] = args.vni + if 'local_pref' in args and args.local_pref is not None: + attrs['local_pref'] = args.local_pref + if args.purge_route_target: attrs['route_targets'] = [] elif args.route_targets: @@ -247,6 +256,8 @@ def take_action(self, parsed_args): attrs['route_distinguishers'] = parsed_args.route_distinguishers if parsed_args.vni is not None: attrs['vni'] = parsed_args.vni + if parsed_args.local_pref is not None: + attrs['local_pref'] = parsed_args.local_pref if 'project' in parsed_args and parsed_args.project is not None: project_id = nc_osc_utils.find_project( self.app.client_manager.identity, diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py index 0103904e3..1b8c4c008 100644 --- a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py +++ b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py @@ -77,6 +77,7 @@ def create_one_bgpvpn(attrs=None): 'routers': [], 'ports': [], 'vni': 100, + 'local_pref': 777, } # Overwrite default attributes. diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py index bfa710446..62316d472 100644 --- a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py +++ b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py @@ -64,6 +64,7 @@ def test_create_bgpvpn_with_no_args(self): ('name', None), ('type', 'l3'), ('vni', None), + ('local_pref', None), ('route_targets', None), ('import_targets', None), ('export_targets', None), @@ -85,6 +86,7 @@ def test_create_bgpvpn_with_all_args(self): 'name': 'fake_name', 'type': 'l2', 'vni': 100, + 'local_pref': 777, 'route_targets': ['fake_rt1', 'fake_rt2', 'fake_rt3'], 'import_targets': ['fake_irt1', 'fake_irt2', 'fake_irt3'], 'export_targets': ['fake_ert1', 'fake_ert2', 'fake_ert3'], @@ -98,6 +100,7 @@ def test_create_bgpvpn_with_all_args(self): '--name', fake_bgpvpn['name'], '--type', fake_bgpvpn['type'], '--vni', str(fake_bgpvpn['vni']), + '--local-pref', str(fake_bgpvpn['local_pref']), ] for rt in fake_bgpvpn['route_targets']: arglist.extend(['--route-target', rt]) @@ -112,6 +115,7 @@ def test_create_bgpvpn_with_all_args(self): ('name', fake_bgpvpn['name']), ('type', fake_bgpvpn['type']), ('vni', fake_bgpvpn['vni']), + ('local_pref', fake_bgpvpn['local_pref']), ('route_targets', fake_bgpvpn['route_targets']), ('import_targets', fake_bgpvpn['import_targets']), ('export_targets', fake_bgpvpn['export_targets']), From 58c1be2b65f3e5ec6a87e2d53e3757314a815e9b Mon Sep 17 00:00:00 2001 From: Hongbin Lu Date: Fri, 19 Jan 2018 00:23:51 +0000 Subject: [PATCH 642/845] Define IpAddressAlreadyAllocated exception This exception will raise when creating a port with an IP allocated IP address. This exception needs to be defined in client so that nova can catch it and perform the error handling. Needed-By: I5bee9ae18764b6f285ecc5e8d7148a1019c74701 Change-Id: Ie1d4581d334e1ec491e56371c62978945ae24a44 Related-Bug: #1744103 --- neutronclient/common/exceptions.py | 4 ++++ neutronclient/tests/unit/test_cli20.py | 2 ++ ...ssAlreadyAllocatedClient-exception-e8600ca5ba1c7f45.yaml | 6 ++++++ 3 files changed, 12 insertions(+) create mode 100644 releasenotes/notes/Define-IpAddressAlreadyAllocatedClient-exception-e8600ca5ba1c7f45.yaml diff --git a/neutronclient/common/exceptions.py b/neutronclient/common/exceptions.py index 4298d423a..1040ee120 100644 --- a/neutronclient/common/exceptions.py +++ b/neutronclient/common/exceptions.py @@ -162,6 +162,10 @@ class IpAddressInUseClient(Conflict): pass +class IpAddressAlreadyAllocatedClient(Conflict): + pass + + class InvalidIpForNetworkClient(BadRequest): pass diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 132e1e547..0837a6c3d 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -911,6 +911,8 @@ def test_exception_handler_v20_neutron_known_error(self): ('OverQuota', exceptions.OverQuotaClient, 409), ('InvalidIpForNetwork', exceptions.InvalidIpForNetworkClient, 400), ('InvalidIpForSubnet', exceptions.InvalidIpForSubnetClient, 400), + ('IpAddressAlreadyAllocated', + exceptions.IpAddressAlreadyAllocatedClient, 400), ] error_msg = 'dummy exception message' diff --git a/releasenotes/notes/Define-IpAddressAlreadyAllocatedClient-exception-e8600ca5ba1c7f45.yaml b/releasenotes/notes/Define-IpAddressAlreadyAllocatedClient-exception-e8600ca5ba1c7f45.yaml new file mode 100644 index 000000000..4182585e0 --- /dev/null +++ b/releasenotes/notes/Define-IpAddressAlreadyAllocatedClient-exception-e8600ca5ba1c7f45.yaml @@ -0,0 +1,6 @@ +--- +other: + - | + Define a new exception type ``IpAddressAlreadyAllocatedClient``. + Users can catch this specific exception instead of the generic + ``NeutronClientException``. From 5d9dd45de13e5b78381d03e7d2834676a9c295f6 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Wed, 24 Jan 2018 01:30:51 +0000 Subject: [PATCH 643/845] Updated from global requirements Change-Id: I1add04870af21a1e453fed7b6a2a34e70a646ad8 --- test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index e98669ef8..2ad071dfa 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -8,7 +8,7 @@ fixtures>=3.0.0 # Apache-2.0/BSD flake8-import-order==0.12 # LGPLv3 mox3>=0.20.0 # Apache-2.0 mock>=2.0.0 # BSD -openstackdocstheme>=1.17.0 # Apache-2.0 +openstackdocstheme>=1.18.1 # Apache-2.0 oslotest>=3.2.0 # Apache-2.0 osprofiler>=1.4.0 # Apache-2.0 python-openstackclient>=3.12.0 # Apache-2.0 From fee6f7b3100393155a090ce1d6943e0ac756169c Mon Sep 17 00:00:00 2001 From: gaofei Date: Wed, 24 Jan 2018 09:00:22 +0800 Subject: [PATCH 644/845] Fix broken links Change-Id: I4b00a24e5adb2ac6b62218d152aef88fac2d6396 --- doc/source/contributor/transition_to_osc.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/source/contributor/transition_to_osc.rst b/doc/source/contributor/transition_to_osc.rst index 098d72734..636f8cb92 100644 --- a/doc/source/contributor/transition_to_osc.rst +++ b/doc/source/contributor/transition_to_osc.rst @@ -217,11 +217,11 @@ file. When adding an ``openstack`` networking command to python-openstackclient, you can optionally propose an -`OSC command spec `_ +`OSC command spec `_ which documents the new command interface before proceeding with the implementation. Users of the neutron client's command extensions must adopt the -`OSC plugin `_ +`OSC plugin `_ system for this transition. Such users will maintain their OSC plugin within their own project and should follow the guidance in the table above to determine which command to change. @@ -231,17 +231,17 @@ Developer References * See `OSC neutron support etherpad `_ to determine if an ``openstack`` command is in progress. -* See `OSC command list `_ +* See `OSC command list `_ to determine if an ``openstack`` command exists. -* See `OSC command spec list `_ +* See `OSC command spec list `_ to determine if an ``openstack`` command spec exists. * See `OSC plugin command list `_ to determine if an ``openstack`` plugin command exists. -* See `OSC command structure `_ +* See `OSC command structure `_ to determine the current ``openstack`` command objects, plugin objects and actions. -* See `OSC human interface guide `_ +* See `OSC human interface guide `_ for guidance on creating new OSC command interfaces. -* See `OSC plugin `_ +* See `OSC plugin `_ for information on the OSC plugin system to be used for ``neutron`` CLI extensions. * Create an OSC blueprint: https://blueprints.launchpad.net/python-openstackclient/ * Report an OSC bug: https://bugs.launchpad.net/python-openstackclient/+filebug From edb91f04721881c0b9cce2572543023cadf9e6d4 Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Fri, 26 Jan 2018 00:20:40 +0000 Subject: [PATCH 645/845] Update reno for stable/queens Change-Id: Ib72ee3b92a279434c7ecfd06f0cc3bc9aa0d8b9e --- releasenotes/source/index.rst | 1 + releasenotes/source/queens.rst | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 releasenotes/source/queens.rst diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index ec6d8bde7..195fe6ee8 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -6,6 +6,7 @@ :maxdepth: 1 unreleased + queens pike ocata newton diff --git a/releasenotes/source/queens.rst b/releasenotes/source/queens.rst new file mode 100644 index 000000000..36ac6160c --- /dev/null +++ b/releasenotes/source/queens.rst @@ -0,0 +1,6 @@ +=================================== + Queens Series Release Notes +=================================== + +.. release-notes:: + :branch: stable/queens From 4914b4e3b53f58551c548c449a5e59b626536db6 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Sun, 7 Jan 2018 05:03:25 +0900 Subject: [PATCH 646/845] Consume column utils from osc-lib get_column_definitions() and get_columns() in neutronclient.osc.utils have been migrated to osc-lib 1.8.0 (or later), so we no longer need to maintain our own versions. This commit consumes osc-lib versions. The workaround in firewallrule.py is no longer needed with osc-lib 1.8.0 as the original bug has been fixed in osc-lib 1.8.0 (osc-lib commit 8cbdcde6076fa1df40e00d3e16bf140a59aa6273). Change-Id: I4b9243b0fdb4ca744cc3f85371cb066a456f1d42 --- neutronclient/osc/utils.py | 75 ------------------- .../osc/v2/dynamic_routing/bgp_peer.py | 5 +- .../osc/v2/dynamic_routing/bgp_speaker.py | 5 +- neutronclient/osc/v2/fwaas/firewallgroup.py | 27 +++---- neutronclient/osc/v2/fwaas/firewallpolicy.py | 21 +++--- neutronclient/osc/v2/fwaas/firewallrule.py | 35 ++++----- neutronclient/osc/v2/logging/network_log.py | 31 ++++---- .../osc/v2/networking_bgpvpn/bgpvpn.py | 33 ++++---- .../networking_bgpvpn/network_association.py | 8 +- .../v2/networking_bgpvpn/port_association.py | 14 ++-- .../networking_bgpvpn/resource_association.py | 9 +-- .../networking_bgpvpn/router_association.py | 8 +- .../osc/v2/sfc/sfc_flow_classifier.py | 40 +++++----- neutronclient/osc/v2/sfc/sfc_port_chain.py | 24 +++--- neutronclient/osc/v2/sfc/sfc_port_pair.py | 22 +++--- .../osc/v2/sfc/sfc_port_pair_group.py | 24 +++--- neutronclient/osc/v2/sfc/sfc_service_graph.py | 18 ++--- neutronclient/osc/v2/vpnaas/endpoint_group.py | 19 ++--- neutronclient/osc/v2/vpnaas/ikepolicy.py | 27 +++---- .../osc/v2/vpnaas/ipsec_site_connection.py | 48 ++++++------ neutronclient/osc/v2/vpnaas/ipsecpolicy.py | 27 +++---- neutronclient/osc/v2/vpnaas/vpnservice.py | 25 ++++--- neutronclient/tests/unit/osc/test_utils.py | 60 --------------- .../unit/osc/v2/networking_bgpvpn/fakes.py | 7 +- .../osc/v2/networking_bgpvpn/test_bgpvpn.py | 18 ++--- .../test_resource_association.py | 18 ++--- 26 files changed, 263 insertions(+), 385 deletions(-) delete mode 100644 neutronclient/tests/unit/osc/test_utils.py diff --git a/neutronclient/osc/utils.py b/neutronclient/osc/utils.py index c9aecc1e3..413ad607c 100644 --- a/neutronclient/osc/utils.py +++ b/neutronclient/osc/utils.py @@ -20,8 +20,6 @@ to this module. They should go to neutronclient.osc.v2.utils. """ -import operator - from keystoneclient import exceptions as identity_exc from keystoneclient.v3 import domains from keystoneclient.v3 import projects @@ -30,79 +28,6 @@ from neutronclient._i18n import _ -LIST_BOTH = 'both' -LIST_SHORT_ONLY = 'short_only' -LIST_LONG_ONLY = 'long_only' - - -def get_column_definitions(attr_map, long_listing): - """Return table headers and column names for a listing table. - - :param attr_map: a list of table entry definitions. - Each entry should be a tuple consisting of - (API attribute name, header name, listing mode). For example: - (('id', 'ID', LIST_BOTH), - ('name', 'Name', LIST_BOTH), - ('tenant_id', 'Project', LIST_LONG_ONLY)) - The third field of each tuple must be one of LIST_BOTH, - LIST_LONG_ONLY (a corresponding column is shown only in a long mode), or - LIST_SHORT_ONLY (a corresponding column is shown only in a short mode). - :param long_listing: A boolean value which indicates a long listing - or not. In most cases, parsed_args.long is passed to this argument. - :return: A tuple of a list of table headers and a list of column names. - """ - - if long_listing: - headers = [hdr for col, hdr, listing_mode in attr_map - if listing_mode in (LIST_BOTH, LIST_LONG_ONLY)] - columns = [col for col, hdr, listing_mode in attr_map - if listing_mode in (LIST_BOTH, LIST_LONG_ONLY)] - else: - headers = [hdr for col, hdr, listing_mode in attr_map if listing_mode - if listing_mode in (LIST_BOTH, LIST_SHORT_ONLY)] - columns = [col for col, hdr, listing_mode in attr_map if listing_mode - if listing_mode in (LIST_BOTH, LIST_SHORT_ONLY)] - - return headers, columns - - -def get_columns(item, attr_map=None): - """Return pair of resource attributes and corresponding display names. - - Assume the following item and attr_map are passed. - item: {'id': 'myid', 'name': 'myname', - 'foo': 'bar', 'tenant_id': 'mytenan'} - attr_map: - (('id', 'ID', LIST_BOTH), - ('name', 'Name', LIST_BOTH), - ('tenant_id', 'Project', LIST_LONG_ONLY)) - - This method returns: - - (('id', 'name', 'tenant_id', 'foo'), # attributes - ('ID', 'Name', 'Project', 'foo') # display names - - Both tuples of attributes and display names are sorted by display names - in the alphabetical order. - Attributes not found in a given attr_map are kept as-is. - - :param item: a dictionary which represents a resource. - Keys of the dictionary are expected to be attributes of the resource. - Values are not referred to by this method. - :param attr_map: a list of mapping from attribute to display name. - The same format is used as for get_column_definitions attr_map. - :return: A pair of tuple of attributes and tuple of display names. - """ - attr_map = attr_map or tuple([]) - _attr_map_dict = dict((col, hdr) for col, hdr, listing_mode in attr_map) - - columns = [(column, _attr_map_dict.get(column, column)) - for column in item.keys()] - columns = sorted(columns, key=operator.itemgetter(1)) - return (tuple(col[0] for col in columns), - tuple(col[1] for col in columns)) - - # TODO(amotoki): Use osc-lib version once osc-lib provides this. def add_project_owner_option_to_parser(parser): """Register project and project domain options. diff --git a/neutronclient/osc/v2/dynamic_routing/bgp_peer.py b/neutronclient/osc/v2/dynamic_routing/bgp_peer.py index c245ce2e7..c8e602ebb 100644 --- a/neutronclient/osc/v2/dynamic_routing/bgp_peer.py +++ b/neutronclient/osc/v2/dynamic_routing/bgp_peer.py @@ -13,6 +13,7 @@ from osc_lib.command import command from osc_lib import utils +from osc_lib.utils import columns as column_util from neutronclient._i18n import _ from neutronclient.common import exceptions @@ -99,7 +100,7 @@ def take_action(self, parsed_args): attrs = _get_attrs(self.app.client_manager, parsed_args) body = {constants.BGP_PEER: attrs} obj = client.create_bgp_peer(body)[constants.BGP_PEER] - columns, display_columns = nc_osc_utils.get_columns(obj) + columns, display_columns = column_util.get_columns(obj) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -183,6 +184,6 @@ def take_action(self, parsed_args): id = client.find_resource(constants.BGP_PEER, parsed_args.bgp_peer)['id'] obj = client.show_bgp_peer(id)[constants.BGP_PEER] - columns, display_columns = nc_osc_utils.get_columns(obj) + columns, display_columns = column_util.get_columns(obj) data = utils.get_dict_properties(obj, columns) return display_columns, data diff --git a/neutronclient/osc/v2/dynamic_routing/bgp_speaker.py b/neutronclient/osc/v2/dynamic_routing/bgp_speaker.py index bfe202f2b..366dadcf3 100644 --- a/neutronclient/osc/v2/dynamic_routing/bgp_speaker.py +++ b/neutronclient/osc/v2/dynamic_routing/bgp_speaker.py @@ -13,6 +13,7 @@ from osc_lib.command import command from osc_lib import utils +from osc_lib.utils import columns as column_util from neutronclient._i18n import _ from neutronclient.osc import utils as nc_osc_utils @@ -149,7 +150,7 @@ def take_action(self, parsed_args): body = {} body[constants.BGP_SPEAKER] = attrs obj = client.create_bgp_speaker(body)[constants.BGP_SPEAKER] - columns, display_columns = nc_osc_utils.get_columns(obj) + columns, display_columns = column_util.get_columns(obj) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -315,6 +316,6 @@ def take_action(self, parsed_args): id = client.find_resource(constants.BGP_SPEAKER, parsed_args.bgp_speaker)['id'] obj = client.show_bgp_speaker(id)[constants.BGP_SPEAKER] - columns, display_columns = nc_osc_utils.get_columns(obj) + columns, display_columns = column_util.get_columns(obj) data = utils.get_dict_properties(obj, columns) return display_columns, data diff --git a/neutronclient/osc/v2/fwaas/firewallgroup.py b/neutronclient/osc/v2/fwaas/firewallgroup.py index 3d25022d1..fe8ab1188 100644 --- a/neutronclient/osc/v2/fwaas/firewallgroup.py +++ b/neutronclient/osc/v2/fwaas/firewallgroup.py @@ -18,6 +18,7 @@ from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from osc_lib.utils import columns as column_util from neutronclient._i18n import _ from neutronclient.osc import utils as osc_utils @@ -32,16 +33,16 @@ } _attr_map = ( - ('id', 'ID', osc_utils.LIST_BOTH), - ('name', 'Name', osc_utils.LIST_BOTH), - ('ingress_firewall_policy_id', 'Ingress Policy ID', osc_utils.LIST_BOTH), - ('egress_firewall_policy_id', 'Egress Policy ID', osc_utils.LIST_BOTH), - ('description', 'Description', osc_utils.LIST_LONG_ONLY), - ('status', 'Status', osc_utils.LIST_LONG_ONLY), - ('ports', 'Ports', osc_utils.LIST_LONG_ONLY), - ('admin_state_up', 'State', osc_utils.LIST_LONG_ONLY), - ('shared', 'Shared', osc_utils.LIST_LONG_ONLY), - ('tenant_id', 'Project', osc_utils.LIST_LONG_ONLY), + ('id', 'ID', column_util.LIST_BOTH), + ('name', 'Name', column_util.LIST_BOTH), + ('ingress_firewall_policy_id', 'Ingress Policy ID', column_util.LIST_BOTH), + ('egress_firewall_policy_id', 'Egress Policy ID', column_util.LIST_BOTH), + ('description', 'Description', column_util.LIST_LONG_ONLY), + ('status', 'Status', column_util.LIST_LONG_ONLY), + ('ports', 'Ports', column_util.LIST_LONG_ONLY), + ('admin_state_up', 'State', column_util.LIST_LONG_ONLY), + ('shared', 'Shared', column_util.LIST_LONG_ONLY), + ('tenant_id', 'Project', column_util.LIST_LONG_ONLY), ) @@ -201,7 +202,7 @@ def take_action(self, parsed_args): attrs = _get_common_attrs(self.app.client_manager, parsed_args) obj = client.create_fwaas_firewall_group( {const.FWG: attrs})[const.FWG] - columns, display_columns = osc_utils.get_columns(obj, _attr_map) + columns, display_columns = column_util.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns, formatters=_formatters) return (display_columns, data) @@ -254,7 +255,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.neutronclient obj = client.list_fwaas_firewall_groups()[const.FWGS] - headers, columns = osc_utils.get_column_definitions( + headers, columns = column_util.get_column_definitions( _attr_map, long_listing=parsed_args.long) return (headers, (utils.get_dict_properties( s, columns, formatters=_formatters) for s in obj)) @@ -313,7 +314,7 @@ def take_action(self, parsed_args): fwg_id = client.find_resource(const.FWG, parsed_args.firewall_group, cmd_resource=const.CMD_FWG)['id'] obj = client.show_fwaas_firewall_group(fwg_id)[const.FWG] - columns, display_columns = osc_utils.get_columns(obj, _attr_map) + columns, display_columns = column_util.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns, formatters=_formatters) return (display_columns, data) diff --git a/neutronclient/osc/v2/fwaas/firewallpolicy.py b/neutronclient/osc/v2/fwaas/firewallpolicy.py index cee84065c..2c3d5675e 100644 --- a/neutronclient/osc/v2/fwaas/firewallpolicy.py +++ b/neutronclient/osc/v2/fwaas/firewallpolicy.py @@ -21,6 +21,7 @@ from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from osc_lib.utils import columns as column_util from neutronclient._i18n import _ from neutronclient.osc import utils as osc_utils @@ -32,13 +33,13 @@ _formatters = {} _attr_map = ( - ('id', 'ID', osc_utils.LIST_BOTH), - ('name', 'Name', osc_utils.LIST_BOTH), - ('firewall_rules', 'Firewall Rules', osc_utils.LIST_BOTH), - ('description', 'Description', osc_utils.LIST_LONG_ONLY), - ('audited', 'Audited', osc_utils.LIST_LONG_ONLY), - ('shared', 'Shared', osc_utils.LIST_LONG_ONLY), - ('tenant_id', 'Project', osc_utils.LIST_LONG_ONLY), + ('id', 'ID', column_util.LIST_BOTH), + ('name', 'Name', column_util.LIST_BOTH), + ('firewall_rules', 'Firewall Rules', column_util.LIST_BOTH), + ('description', 'Description', column_util.LIST_LONG_ONLY), + ('audited', 'Audited', column_util.LIST_LONG_ONLY), + ('shared', 'Shared', column_util.LIST_LONG_ONLY), + ('tenant_id', 'Project', column_util.LIST_LONG_ONLY), ) @@ -155,7 +156,7 @@ def take_action(self, parsed_args): attrs = _get_common_attrs(self.app.client_manager, parsed_args) obj = client.create_fwaas_firewall_policy( {const.FWP: attrs})[const.FWP] - columns, display_columns = osc_utils.get_columns(obj, _attr_map) + columns, display_columns = column_util.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns, formatters=_formatters) return (display_columns, data) @@ -297,7 +298,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.neutronclient obj = client.list_fwaas_firewall_policies()[const.FWPS] - headers, columns = osc_utils.get_column_definitions( + headers, columns = column_util.get_column_definitions( _attr_map, long_listing=parsed_args.long) return (headers, (utils.get_dict_properties( s, columns, formatters=_formatters) for s in obj)) @@ -360,7 +361,7 @@ def take_action(self, parsed_args): parsed_args.firewall_policy, cmd_resource=const.CMD_FWP)['id'] obj = client.show_fwaas_firewall_policy(fwp_id)[const.FWP] - columns, display_columns = osc_utils.get_columns(obj, _attr_map) + columns, display_columns = column_util.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns, formatters=_formatters) return (display_columns, data) diff --git a/neutronclient/osc/v2/fwaas/firewallrule.py b/neutronclient/osc/v2/fwaas/firewallrule.py index aee0441ac..964e7e16e 100644 --- a/neutronclient/osc/v2/fwaas/firewallrule.py +++ b/neutronclient/osc/v2/fwaas/firewallrule.py @@ -20,6 +20,7 @@ from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from osc_lib.utils import columns as column_util from neutronclient._i18n import _ from neutronclient.common import utils as nc_utils @@ -31,21 +32,21 @@ _attr_map = ( - ('id', 'ID', osc_utils.LIST_BOTH), - ('name', 'Name', osc_utils.LIST_BOTH), - ('enabled', 'Enabled', osc_utils.LIST_BOTH), - ('summary', 'Summary', osc_utils.LIST_SHORT_ONLY), - ('description', 'Description', osc_utils.LIST_LONG_ONLY), - ('ip_version', 'IP Version', osc_utils.LIST_LONG_ONLY), - ('action', 'Action', osc_utils.LIST_LONG_ONLY), - ('protocol', 'Protocol', osc_utils.LIST_LONG_ONLY), - ('source_ip_address', 'Source IP Address', osc_utils.LIST_LONG_ONLY), - ('source_port', 'Source Port', osc_utils.LIST_LONG_ONLY), + ('id', 'ID', column_util.LIST_BOTH), + ('name', 'Name', column_util.LIST_BOTH), + ('enabled', 'Enabled', column_util.LIST_BOTH), + ('summary', 'Summary', column_util.LIST_SHORT_ONLY), + ('description', 'Description', column_util.LIST_LONG_ONLY), + ('ip_version', 'IP Version', column_util.LIST_LONG_ONLY), + ('action', 'Action', column_util.LIST_LONG_ONLY), + ('protocol', 'Protocol', column_util.LIST_LONG_ONLY), + ('source_ip_address', 'Source IP Address', column_util.LIST_LONG_ONLY), + ('source_port', 'Source Port', column_util.LIST_LONG_ONLY), ('destination_ip_address', 'Destination IP Address', - osc_utils.LIST_LONG_ONLY), - ('destination_port', 'Destination Port', osc_utils.LIST_LONG_ONLY), - ('shared', 'Shared', osc_utils.LIST_LONG_ONLY), - ('tenant_id', 'Project', osc_utils.LIST_LONG_ONLY), + column_util.LIST_LONG_ONLY), + ('destination_port', 'Destination Port', column_util.LIST_LONG_ONLY), + ('shared', 'Shared', column_util.LIST_LONG_ONLY), + ('tenant_id', 'Project', column_util.LIST_LONG_ONLY), ) @@ -217,7 +218,7 @@ def take_action(self, parsed_args): attrs = _get_common_attrs(self.app.client_manager, parsed_args) obj = client.create_fwaas_firewall_rule( {const.FWR: attrs})[const.FWR] - columns, display_columns = osc_utils.get_columns(obj, _attr_map) + columns, display_columns = column_util.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns, formatters=_formatters) return display_columns, data @@ -294,7 +295,7 @@ def take_action(self, parsed_args): client = self.app.client_manager.neutronclient obj = client.list_fwaas_firewall_rules()[const.FWRS] obj_extend = self.extend_list(obj, parsed_args) - headers, columns = osc_utils.get_column_definitions( + headers, columns = column_util.get_column_definitions( _attr_map, long_listing=parsed_args.long) return (headers, (utils.get_dict_properties( s, columns, formatters=_formatters) for s in obj_extend)) @@ -344,7 +345,7 @@ def take_action(self, parsed_args): const.FWR, parsed_args.firewall_rule, cmd_resource=const.CMD_FWR)['id'] obj = client.show_fwaas_firewall_rule(fwr_id)[const.FWR] - columns, display_columns = osc_utils.get_columns(obj, _attr_map) + columns, display_columns = column_util.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns, formatters=_formatters) return (display_columns, data) diff --git a/neutronclient/osc/v2/logging/network_log.py b/neutronclient/osc/v2/logging/network_log.py index becb1cb69..534e48d82 100644 --- a/neutronclient/osc/v2/logging/network_log.py +++ b/neutronclient/osc/v2/logging/network_log.py @@ -19,6 +19,7 @@ from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from osc_lib.utils import columns as column_util from oslo_log import log as logging from neutronclient._i18n import _ @@ -29,20 +30,20 @@ LOG = logging.getLogger(__name__) _attr_map = ( - ('id', 'ID', osc_utils.LIST_BOTH), - ('description', 'Description', osc_utils.LIST_LONG_ONLY), - ('enabled', 'Enabled', osc_utils.LIST_BOTH), - ('name', 'Name', osc_utils.LIST_BOTH), - ('target_id', 'Target', osc_utils.LIST_LONG_ONLY), - ('project_id', 'Project', osc_utils.LIST_LONG_ONLY), - ('resource_id', 'Resource', osc_utils.LIST_LONG_ONLY), - ('resource_type', 'Type', osc_utils.LIST_BOTH), - ('event', 'Event', osc_utils.LIST_LONG_ONLY), - ('summary', 'Summary', osc_utils.LIST_SHORT_ONLY), + ('id', 'ID', column_util.LIST_BOTH), + ('description', 'Description', column_util.LIST_LONG_ONLY), + ('enabled', 'Enabled', column_util.LIST_BOTH), + ('name', 'Name', column_util.LIST_BOTH), + ('target_id', 'Target', column_util.LIST_LONG_ONLY), + ('project_id', 'Project', column_util.LIST_LONG_ONLY), + ('resource_id', 'Resource', column_util.LIST_LONG_ONLY), + ('resource_type', 'Type', column_util.LIST_BOTH), + ('event', 'Event', column_util.LIST_LONG_ONLY), + ('summary', 'Summary', column_util.LIST_SHORT_ONLY), ) _attr_map_for_loggable = ( - ('type', 'Supported types', osc_utils.LIST_BOTH), + ('type', 'Supported types', column_util.LIST_BOTH), ) NET_LOG = 'network_log' @@ -142,7 +143,7 @@ def take_action(self, parsed_args): client = self.app.client_manager.neutronclient attrs = _get_common_attrs(self.app.client_manager, parsed_args) obj = client.create_network_log({'log': attrs})['log'] - columns, display_columns = osc_utils.get_columns(obj, _attr_map) + columns, display_columns = column_util.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns) return (display_columns, data) @@ -195,7 +196,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.neutronclient obj = client.list_network_loggable_resources()['loggable_resources'] - headers, columns = osc_utils.get_column_definitions( + headers, columns = column_util.get_column_definitions( _attr_map_for_loggable, long_listing=parsed_args.long) return (headers, (utils.get_dict_properties(s, columns) for s in obj)) @@ -232,7 +233,7 @@ def take_action(self, parsed_args): client = self.app.client_manager.neutronclient obj = client.list_network_logs()['logs'] obj_extend = self._extend_list(obj, parsed_args) - headers, columns = osc_utils.get_column_definitions( + headers, columns = column_util.get_column_definitions( _attr_map, long_listing=parsed_args.long) return (headers, ( utils.get_dict_properties(s, columns) for s in obj_extend)) @@ -284,6 +285,6 @@ def take_action(self, parsed_args): log_id = client.find_resource( 'log', parsed_args.network_log, cmd_resource=NET_LOG)['id'] obj = client.show_network_log(log_id)['log'] - columns, display_columns = osc_utils.get_columns(obj, _attr_map) + columns, display_columns = column_util.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns) return (display_columns, data) diff --git a/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py b/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py index 6ce93ac65..58c9e8a43 100644 --- a/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py +++ b/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py @@ -21,6 +21,7 @@ from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils as osc_utils +from osc_lib.utils import columns as column_util from neutronclient._i18n import _ from neutronclient.osc import utils as nc_osc_utils @@ -29,20 +30,20 @@ LOG = logging.getLogger(__name__) _attr_map = ( - ('id', 'ID', nc_osc_utils.LIST_BOTH), - ('tenant_id', 'Project', nc_osc_utils.LIST_LONG_ONLY), - ('name', 'Name', nc_osc_utils.LIST_BOTH), - ('type', 'Type', nc_osc_utils.LIST_BOTH), - ('route_targets', 'Route Targets', nc_osc_utils.LIST_LONG_ONLY), - ('import_targets', 'Import Targets', nc_osc_utils.LIST_LONG_ONLY), - ('export_targets', 'Export Targets', nc_osc_utils.LIST_LONG_ONLY), + ('id', 'ID', column_util.LIST_BOTH), + ('tenant_id', 'Project', column_util.LIST_LONG_ONLY), + ('name', 'Name', column_util.LIST_BOTH), + ('type', 'Type', column_util.LIST_BOTH), + ('route_targets', 'Route Targets', column_util.LIST_LONG_ONLY), + ('import_targets', 'Import Targets', column_util.LIST_LONG_ONLY), + ('export_targets', 'Export Targets', column_util.LIST_LONG_ONLY), ('route_distinguishers', 'Route Distinguishers', - nc_osc_utils.LIST_LONG_ONLY), - ('networks', 'Associated Networks', nc_osc_utils.LIST_LONG_ONLY), - ('routers', 'Associated Routers', nc_osc_utils.LIST_LONG_ONLY), - ('ports', 'Associated Ports', nc_osc_utils.LIST_LONG_ONLY), - ('vni', 'VNI', nc_osc_utils.LIST_LONG_ONLY), - ('local_pref', 'Local Pref', nc_osc_utils.LIST_LONG_ONLY), + column_util.LIST_LONG_ONLY), + ('networks', 'Associated Networks', column_util.LIST_LONG_ONLY), + ('routers', 'Associated Routers', column_util.LIST_LONG_ONLY), + ('ports', 'Associated Ports', column_util.LIST_LONG_ONLY), + ('vni', 'VNI', column_util.LIST_LONG_ONLY), + ('local_pref', 'Local Pref', column_util.LIST_LONG_ONLY), ) _formatters = { 'route_targets': format_columns.ListColumn, @@ -267,7 +268,7 @@ def take_action(self, parsed_args): attrs['tenant_id'] = project_id body = {constants.BGPVPN: attrs} obj = client.create_bgpvpn(body)[constants.BGPVPN] - columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) + columns, display_columns = column_util.get_columns(obj, _attr_map) data = osc_utils.get_dict_properties(obj, columns, formatters=_formatters) return display_columns, data @@ -379,7 +380,7 @@ def take_action(self, parsed_args): if parsed_args.property: params.update(parsed_args.property) objs = client.list_bgpvpns(**params)[constants.BGPVPNS] - headers, columns = nc_osc_utils.get_column_definitions( + headers, columns = column_util.get_column_definitions( _attr_map, long_listing=parsed_args.long) return (headers, (osc_utils.get_dict_properties( s, columns, formatters=_formatters) for s in objs)) @@ -401,7 +402,7 @@ def take_action(self, parsed_args): client = self.app.client_manager.neutronclient id = client.find_resource(constants.BGPVPN, parsed_args.bgpvpn)['id'] obj = client.show_bgpvpn(id)[constants.BGPVPN] - columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) + columns, display_columns = column_util.get_columns(obj, _attr_map) data = osc_utils.get_dict_properties(obj, columns, formatters=_formatters) return display_columns, data diff --git a/neutronclient/osc/v2/networking_bgpvpn/network_association.py b/neutronclient/osc/v2/networking_bgpvpn/network_association.py index 33e199651..05c44320e 100644 --- a/neutronclient/osc/v2/networking_bgpvpn/network_association.py +++ b/neutronclient/osc/v2/networking_bgpvpn/network_association.py @@ -14,9 +14,9 @@ # under the License. # +from osc_lib.utils import columns as column_util from neutronclient._i18n import _ -from neutronclient.osc import utils as nc_osc_utils from neutronclient.osc.v2.networking_bgpvpn import constants from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ CreateBgpvpnResAssoc @@ -34,10 +34,10 @@ class BgpvpnNetAssoc(object): _resource_plural = constants.NETWORK_ASSOCS _attr_map = ( - ('id', 'ID', nc_osc_utils.LIST_BOTH), - ('tenant_id', 'Project', nc_osc_utils.LIST_LONG_ONLY), + ('id', 'ID', column_util.LIST_BOTH), + ('tenant_id', 'Project', column_util.LIST_LONG_ONLY), ('%s_id' % _assoc_res_name, '%s ID' % _assoc_res_name.capitalize(), - nc_osc_utils.LIST_BOTH), + column_util.LIST_BOTH), ) _formatters = {} diff --git a/neutronclient/osc/v2/networking_bgpvpn/port_association.py b/neutronclient/osc/v2/networking_bgpvpn/port_association.py index 75d5c0fd9..abf82ab72 100644 --- a/neutronclient/osc/v2/networking_bgpvpn/port_association.py +++ b/neutronclient/osc/v2/networking_bgpvpn/port_association.py @@ -18,9 +18,9 @@ from osc_lib.cli import format_columns from osc_lib.cli import parseractions +from osc_lib.utils import columns as column_util from neutronclient._i18n import _ -from neutronclient.osc import utils as nc_osc_utils from neutronclient.osc.v2.networking_bgpvpn import constants from neutronclient.osc.v2.networking_bgpvpn import resource_association @@ -33,16 +33,16 @@ class BgpvpnPortAssoc(object): _resource_plural = constants.PORT_ASSOCS _attr_map = ( - ('id', 'ID', nc_osc_utils.LIST_BOTH), - ('tenant_id', 'Project', nc_osc_utils.LIST_LONG_ONLY), + ('id', 'ID', column_util.LIST_BOTH), + ('tenant_id', 'Project', column_util.LIST_LONG_ONLY), ('%s_id' % _assoc_res_name, '%s ID' % _assoc_res_name.capitalize(), - nc_osc_utils.LIST_BOTH), + column_util.LIST_BOTH), ('prefix_routes', 'Prefix Routes (BGP LOCAL_PREF)', - nc_osc_utils.LIST_LONG_ONLY), + column_util.LIST_LONG_ONLY), ('bgpvpn_routes', 'BGP VPN Routes (BGP LOCAL_PREF)', - nc_osc_utils.LIST_LONG_ONLY), + column_util.LIST_LONG_ONLY), ('advertise_fixed_ips', "Advertise Port's Fixed IPs", - nc_osc_utils.LIST_LONG_ONLY), + column_util.LIST_LONG_ONLY), ) _formatters = { 'prefix_routes': format_columns.ListColumn, diff --git a/neutronclient/osc/v2/networking_bgpvpn/resource_association.py b/neutronclient/osc/v2/networking_bgpvpn/resource_association.py index 4784100f8..96c559b24 100644 --- a/neutronclient/osc/v2/networking_bgpvpn/resource_association.py +++ b/neutronclient/osc/v2/networking_bgpvpn/resource_association.py @@ -20,6 +20,7 @@ from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils as osc_utils +from osc_lib.utils import columns as column_util from neutronclient._i18n import _ from neutronclient.osc import utils as nc_osc_utils @@ -83,8 +84,7 @@ def take_action(self, parsed_args): transform = getattr(self, '_transform_resource', None) if callable(transform): transform(obj) - columns, display_columns = nc_osc_utils.get_columns(obj, - self._attr_map) + columns, display_columns = column_util.get_columns(obj, self._attr_map) data = osc_utils.get_dict_properties(obj, columns, formatters=self._formatters) return display_columns, data @@ -218,7 +218,7 @@ def take_action(self, parsed_args): transform = getattr(self, '_transform_resource', None) if callable(transform): [transform(obj) for obj in objs] - headers, columns = nc_osc_utils.get_column_definitions( + headers, columns = column_util.get_column_definitions( self._attr_map, long_listing=parsed_args.long) return (headers, (osc_utils.get_dict_properties( s, columns, formatters=self._formatters) for s in objs)) @@ -256,8 +256,7 @@ def take_action(self, parsed_args): transform = getattr(self, '_transform_resource', None) if callable(transform): transform(obj) - columns, display_columns = nc_osc_utils.get_columns(obj, - self._attr_map) + columns, display_columns = column_util.get_columns(obj, self._attr_map) data = osc_utils.get_dict_properties(obj, columns, formatters=self._formatters) return display_columns, data diff --git a/neutronclient/osc/v2/networking_bgpvpn/router_association.py b/neutronclient/osc/v2/networking_bgpvpn/router_association.py index a51fbe604..40664b6dc 100644 --- a/neutronclient/osc/v2/networking_bgpvpn/router_association.py +++ b/neutronclient/osc/v2/networking_bgpvpn/router_association.py @@ -14,9 +14,9 @@ # under the License. # +from osc_lib.utils import columns as column_util from neutronclient._i18n import _ -from neutronclient.osc import utils as nc_osc_utils from neutronclient.osc.v2.networking_bgpvpn import constants from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ CreateBgpvpnResAssoc @@ -34,10 +34,10 @@ class BgpvpnRouterAssoc(object): _resource_plural = constants.ROUTER_ASSOCS _attr_map = ( - ('id', 'ID', nc_osc_utils.LIST_BOTH), - ('tenant_id', 'Project', nc_osc_utils.LIST_LONG_ONLY), + ('id', 'ID', column_util.LIST_BOTH), + ('tenant_id', 'Project', column_util.LIST_LONG_ONLY), ('%s_id' % _assoc_res_name, '%s ID' % _assoc_res_name.capitalize(), - nc_osc_utils.LIST_BOTH), + column_util.LIST_BOTH), ) _formatters = {} diff --git a/neutronclient/osc/v2/sfc/sfc_flow_classifier.py b/neutronclient/osc/v2/sfc/sfc_flow_classifier.py index 529cf8aa6..ad35aa6bd 100755 --- a/neutronclient/osc/v2/sfc/sfc_flow_classifier.py +++ b/neutronclient/osc/v2/sfc/sfc_flow_classifier.py @@ -19,40 +19,40 @@ from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from osc_lib.utils import columns as column_util from neutronclient._i18n import _ from neutronclient.common import exceptions as nc_exc -from neutronclient.osc import utils as nc_osc_utils LOG = logging.getLogger(__name__) resource = 'flow_classifier' _attr_map = ( - ('id', 'ID', nc_osc_utils.LIST_BOTH), - ('name', 'Name', nc_osc_utils.LIST_BOTH), - ('summary', 'Summary', nc_osc_utils.LIST_SHORT_ONLY), - ('protocol', 'Protocol', nc_osc_utils.LIST_LONG_ONLY), - ('ethertype', 'Ethertype', nc_osc_utils.LIST_LONG_ONLY), + ('id', 'ID', column_util.LIST_BOTH), + ('name', 'Name', column_util.LIST_BOTH), + ('summary', 'Summary', column_util.LIST_SHORT_ONLY), + ('protocol', 'Protocol', column_util.LIST_LONG_ONLY), + ('ethertype', 'Ethertype', column_util.LIST_LONG_ONLY), ('source_ip_prefix', 'Source IP', - nc_osc_utils.LIST_LONG_ONLY), + column_util.LIST_LONG_ONLY), ('destination_ip_prefix', 'Destination IP', - nc_osc_utils.LIST_LONG_ONLY), + column_util.LIST_LONG_ONLY), ('logical_source_port', 'Logical Source Port', - nc_osc_utils.LIST_LONG_ONLY), + column_util.LIST_LONG_ONLY), ('logical_destination_port', 'Logical Destination Port', - nc_osc_utils.LIST_LONG_ONLY), + column_util.LIST_LONG_ONLY), ('source_port_range_min', 'Source Port Range Min', - nc_osc_utils.LIST_LONG_ONLY), + column_util.LIST_LONG_ONLY), ('source_port_range_max', 'Source Port Range Max', - nc_osc_utils.LIST_LONG_ONLY), + column_util.LIST_LONG_ONLY), ('destination_port_range_min', 'Destination Port Range Min', - nc_osc_utils.LIST_LONG_ONLY), + column_util.LIST_LONG_ONLY), ('destination_port_range_max', 'Destination Port Range Max', - nc_osc_utils.LIST_LONG_ONLY), - ('l7_parameters', 'L7 Parameters', nc_osc_utils.LIST_LONG_ONLY), - ('description', 'Description', nc_osc_utils.LIST_LONG_ONLY), - ('project_id', 'Project', nc_osc_utils.LIST_LONG_ONLY), + column_util.LIST_LONG_ONLY), + ('l7_parameters', 'L7 Parameters', column_util.LIST_LONG_ONLY), + ('description', 'Description', column_util.LIST_LONG_ONLY), + ('project_id', 'Project', column_util.LIST_LONG_ONLY), ) @@ -118,7 +118,7 @@ def take_action(self, parsed_args): attrs = _get_common_attrs(self.app.client_manager, parsed_args) body = {resource: attrs} obj = client.create_sfc_flow_classifier(body)[resource] - columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) + columns, display_columns = column_util.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -200,7 +200,7 @@ def take_action(self, parsed_args): client = self.app.client_manager.neutronclient obj = client.list_sfc_flow_classifiers() obj_extend = self.extend_list(obj, parsed_args) - headers, columns = nc_osc_utils.get_column_definitions( + headers, columns = column_util.get_column_definitions( _attr_map, long_listing=parsed_args.long) return (headers, (utils.get_dict_properties( s, columns) for s in obj_extend)) @@ -256,7 +256,7 @@ def take_action(self, parsed_args): client = self.app.client_manager.neutronclient fc_id = _get_id(client, parsed_args.flow_classifier, resource) obj = client.show_sfc_flow_classifier(fc_id)[resource] - columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) + columns, display_columns = column_util.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns) return display_columns, data diff --git a/neutronclient/osc/v2/sfc/sfc_port_chain.py b/neutronclient/osc/v2/sfc/sfc_port_chain.py index 3e3f5f6a5..4ce790dc3 100755 --- a/neutronclient/osc/v2/sfc/sfc_port_chain.py +++ b/neutronclient/osc/v2/sfc/sfc_port_chain.py @@ -19,25 +19,25 @@ from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from osc_lib.utils import columns as column_util from neutronclient._i18n import _ -from neutronclient.osc import utils as nc_osc_utils LOG = logging.getLogger(__name__) resource = 'port_chain' _attr_map = ( - ('id', 'ID', nc_osc_utils.LIST_BOTH), - ('name', 'Name', nc_osc_utils.LIST_BOTH), - ('port_pair_groups', 'Port Pair Groups', nc_osc_utils.LIST_BOTH), + ('id', 'ID', column_util.LIST_BOTH), + ('name', 'Name', column_util.LIST_BOTH), + ('port_pair_groups', 'Port Pair Groups', column_util.LIST_BOTH), ('flow_classifiers', 'Flow Classifiers', - nc_osc_utils.LIST_BOTH), + column_util.LIST_BOTH), ('chain_parameters', 'Chain Parameters', - nc_osc_utils.LIST_BOTH), - ('description', 'Description', nc_osc_utils.LIST_LONG_ONLY), - ('chain_id', 'Chain ID', nc_osc_utils.LIST_BOTH), - ('project_id', 'Project', nc_osc_utils.LIST_LONG_ONLY), + column_util.LIST_BOTH), + ('description', 'Description', column_util.LIST_LONG_ONLY), + ('chain_id', 'Chain ID', column_util.LIST_BOTH), + ('project_id', 'Project', column_util.LIST_LONG_ONLY), ) @@ -85,7 +85,7 @@ def take_action(self, parsed_args): attrs = _get_common_attrs(self.app.client_manager, parsed_args) body = {resource: attrs} obj = client.create_sfc_port_chain(body)[resource] - columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) + columns, display_columns = column_util.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -131,7 +131,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.neutronclient data = client.list_sfc_port_chains() - headers, columns = nc_osc_utils.get_column_definitions( + headers, columns = column_util.get_column_definitions( _attr_map, long_listing=parsed_args.long) return (headers, (utils.get_dict_properties(s, columns) @@ -254,7 +254,7 @@ def take_action(self, parsed_args): client = self.app.client_manager.neutronclient pc_id = _get_id(client, parsed_args.port_chain, resource) obj = client.show_sfc_port_chain(pc_id)[resource] - columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) + columns, display_columns = column_util.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns) return display_columns, data diff --git a/neutronclient/osc/v2/sfc/sfc_port_pair.py b/neutronclient/osc/v2/sfc/sfc_port_pair.py index 12b35dd0d..68d131cdd 100755 --- a/neutronclient/osc/v2/sfc/sfc_port_pair.py +++ b/neutronclient/osc/v2/sfc/sfc_port_pair.py @@ -19,23 +19,23 @@ from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from osc_lib.utils import columns as column_util from neutronclient._i18n import _ -from neutronclient.osc import utils as nc_osc_utils LOG = logging.getLogger(__name__) resource = 'port_pair' _attr_map = ( - ('id', 'ID', nc_osc_utils.LIST_BOTH), - ('name', 'Name', nc_osc_utils.LIST_BOTH), - ('ingress', 'Ingress Logical Port', nc_osc_utils.LIST_BOTH), - ('egress', 'Egress Logical Port', nc_osc_utils.LIST_BOTH), + ('id', 'ID', column_util.LIST_BOTH), + ('name', 'Name', column_util.LIST_BOTH), + ('ingress', 'Ingress Logical Port', column_util.LIST_BOTH), + ('egress', 'Egress Logical Port', column_util.LIST_BOTH), ('service_function_parameters', 'Service Function Parameters', - nc_osc_utils.LIST_LONG_ONLY), - ('description', 'Description', nc_osc_utils.LIST_LONG_ONLY), - ('project_id', 'Project', nc_osc_utils.LIST_LONG_ONLY), + column_util.LIST_LONG_ONLY), + ('description', 'Description', column_util.LIST_LONG_ONLY), + ('project_id', 'Project', column_util.LIST_LONG_ONLY), ) @@ -80,7 +80,7 @@ def take_action(self, parsed_args): attrs = _get_common_attrs(self.app.client_manager, parsed_args) body = {resource: attrs} obj = client.create_sfc_port_pair(body)[resource] - columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) + columns, display_columns = column_util.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -125,7 +125,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.neutronclient data = client.list_sfc_port_pairs() - headers, columns = nc_osc_utils.get_column_definitions( + headers, columns = column_util.get_column_definitions( _attr_map, long_listing=parsed_args.long) return (headers, (utils.get_dict_properties( @@ -183,7 +183,7 @@ def take_action(self, parsed_args): client = self.app.client_manager.neutronclient port_pair_id = _get_id(client, parsed_args.port_pair, resource) obj = client.show_sfc_port_pair(port_pair_id)[resource] - columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) + columns, display_columns = column_util.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns) return display_columns, data diff --git a/neutronclient/osc/v2/sfc/sfc_port_pair_group.py b/neutronclient/osc/v2/sfc/sfc_port_pair_group.py index 972b72766..6053c9a4f 100755 --- a/neutronclient/osc/v2/sfc/sfc_port_pair_group.py +++ b/neutronclient/osc/v2/sfc/sfc_port_pair_group.py @@ -19,24 +19,24 @@ from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from osc_lib.utils import columns as column_util from neutronclient._i18n import _ -from neutronclient.osc import utils as nc_osc_utils LOG = logging.getLogger(__name__) resource = 'port_pair_group' _attr_map = ( - ('id', 'ID', nc_osc_utils.LIST_BOTH), - ('name', 'Name', nc_osc_utils.LIST_BOTH), - ('port_pairs', 'Port Pair', nc_osc_utils.LIST_BOTH), + ('id', 'ID', column_util.LIST_BOTH), + ('name', 'Name', column_util.LIST_BOTH), + ('port_pairs', 'Port Pair', column_util.LIST_BOTH), ('port_pair_group_parameters', 'Port Pair Group Parameters', - nc_osc_utils.LIST_BOTH), - ('description', 'Description', nc_osc_utils.LIST_LONG_ONLY), - ('group_id', 'Loadbalance ID', nc_osc_utils.LIST_LONG_ONLY), - ('project_id', 'Project', nc_osc_utils.LIST_LONG_ONLY), - ('tap_enabled', 'Tap Enabled', nc_osc_utils.LIST_BOTH) + column_util.LIST_BOTH), + ('description', 'Description', column_util.LIST_LONG_ONLY), + ('group_id', 'Loadbalance ID', column_util.LIST_LONG_ONLY), + ('project_id', 'Project', column_util.LIST_LONG_ONLY), + ('tap_enabled', 'Tap Enabled', column_util.LIST_BOTH) ) @@ -89,7 +89,7 @@ def take_action(self, parsed_args): attrs = _get_common_attrs(self.app.client_manager, parsed_args) body = {resource: attrs} obj = client.create_sfc_port_pair_group(body)[resource] - columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) + columns, display_columns = column_util.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -135,7 +135,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.neutronclient data = client.list_sfc_port_pair_groups() - headers, columns = nc_osc_utils.get_column_definitions( + headers, columns = column_util.get_column_definitions( _attr_map, long_listing=parsed_args.long) return (headers, (utils.get_dict_properties( @@ -217,7 +217,7 @@ def take_action(self, parsed_args): client = self.app.client_manager.neutronclient ppg_id = _get_id(client, parsed_args.port_pair_group, resource) obj = client.show_sfc_port_pair_group(ppg_id)[resource] - columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) + columns, display_columns = column_util.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns) return display_columns, data diff --git a/neutronclient/osc/v2/sfc/sfc_service_graph.py b/neutronclient/osc/v2/sfc/sfc_service_graph.py index 53edff8f9..fc4772675 100644 --- a/neutronclient/osc/v2/sfc/sfc_service_graph.py +++ b/neutronclient/osc/v2/sfc/sfc_service_graph.py @@ -17,20 +17,20 @@ from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from osc_lib.utils import columns as column_util from neutronclient._i18n import _ -from neutronclient.osc import utils as nc_osc_utils LOG = logging.getLogger(__name__) resource = 'service_graph' _attr_map = ( - ('id', 'ID', nc_osc_utils.LIST_BOTH), - ('name', 'Name', nc_osc_utils.LIST_BOTH), - ('port_chains', 'Branching Points', nc_osc_utils.LIST_BOTH), - ('description', 'Description', nc_osc_utils.LIST_LONG_ONLY), - ('project_id', 'Project', nc_osc_utils.LIST_LONG_ONLY), + ('id', 'ID', column_util.LIST_BOTH), + ('name', 'Name', column_util.LIST_BOTH), + ('port_chains', 'Branching Points', column_util.LIST_BOTH), + ('description', 'Description', column_util.LIST_LONG_ONLY), + ('project_id', 'Project', column_util.LIST_LONG_ONLY), ) @@ -62,7 +62,7 @@ def take_action(self, parsed_args): try: body = {resource: attrs} obj = client.create_sfc_service_graph(body)[resource] - columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) + columns, display_columns = column_util.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns) return display_columns, data except Exception as e: @@ -140,7 +140,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.neutronclient data = client.list_sfc_service_graphs() - headers, columns = nc_osc_utils.get_column_definitions( + headers, columns = column_util.get_column_definitions( _attr_map, long_listing=parsed_args.long) return (headers, (utils.get_dict_properties(s, columns) @@ -163,7 +163,7 @@ def take_action(self, parsed_args): client = self.app.client_manager.neutronclient sg_id = _get_id(client, parsed_args.service_graph, resource) obj = client.show_sfc_service_graph(sg_id)[resource] - columns, display_columns = nc_osc_utils.get_columns(obj, _attr_map) + columns, display_columns = column_util.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns) return display_columns, data diff --git a/neutronclient/osc/v2/vpnaas/endpoint_group.py b/neutronclient/osc/v2/vpnaas/endpoint_group.py index 269da97ea..668b3d80a 100644 --- a/neutronclient/osc/v2/vpnaas/endpoint_group.py +++ b/neutronclient/osc/v2/vpnaas/endpoint_group.py @@ -17,6 +17,7 @@ from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from osc_lib.utils import columns as column_util from oslo_log import log as logging from neutronclient._i18n import _ @@ -26,12 +27,12 @@ LOG = logging.getLogger(__name__) _attr_map = ( - ('id', 'ID', osc_utils.LIST_BOTH), - ('name', 'Name', osc_utils.LIST_BOTH), - ('type', 'Type', osc_utils.LIST_BOTH), - ('endpoints', 'Endpoints', osc_utils.LIST_BOTH), - ('description', 'Description', osc_utils.LIST_LONG_ONLY), - ('tenant_id', 'Project', osc_utils.LIST_LONG_ONLY), + ('id', 'ID', column_util.LIST_BOTH), + ('name', 'Name', column_util.LIST_BOTH), + ('type', 'Type', column_util.LIST_BOTH), + ('endpoints', 'Endpoints', column_util.LIST_BOTH), + ('description', 'Description', column_util.LIST_LONG_ONLY), + ('tenant_id', 'Project', column_util.LIST_LONG_ONLY), ) @@ -98,7 +99,7 @@ def take_action(self, parsed_args): attrs['endpoints'] = parsed_args.endpoints obj = client.create_endpoint_group( {'endpoint_group': attrs})['endpoint_group'] - columns, display_columns = osc_utils.get_columns(obj, _attr_map) + columns, display_columns = column_util.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -154,7 +155,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.neutronclient obj = client.list_endpoint_groups()['endpoint_groups'] - headers, columns = osc_utils.get_column_definitions( + headers, columns = column_util.get_column_definitions( _attr_map, long_listing=parsed_args.long) return (headers, (utils.get_dict_properties(s, columns) for s in obj)) @@ -211,6 +212,6 @@ def take_action(self, parsed_args): 'endpoint_group', parsed_args.endpoint_group, cmd_resource='endpoint_group')['id'] obj = client.show_endpoint_group(endpoint_id)['endpoint_group'] - columns, display_columns = osc_utils.get_columns(obj, _attr_map) + columns, display_columns = column_util.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns) return (display_columns, data) diff --git a/neutronclient/osc/v2/vpnaas/ikepolicy.py b/neutronclient/osc/v2/vpnaas/ikepolicy.py index 28e6f6088..eb598ead2 100644 --- a/neutronclient/osc/v2/vpnaas/ikepolicy.py +++ b/neutronclient/osc/v2/vpnaas/ikepolicy.py @@ -17,6 +17,7 @@ from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from osc_lib.utils import columns as column_util from oslo_log import log as logging from neutronclient._i18n import _ @@ -28,17 +29,17 @@ LOG = logging.getLogger(__name__) _attr_map = ( - ('id', 'ID', osc_utils.LIST_BOTH), - ('name', 'Name', osc_utils.LIST_BOTH), - ('auth_algorithm', 'Authentication Algorithm', osc_utils.LIST_BOTH), - ('encryption_algorithm', 'Encryption Algorithm', osc_utils.LIST_BOTH), - ('ike_version', 'IKE Version', osc_utils.LIST_BOTH), - ('pfs', 'Perfect Forward Secrecy (PFS)', osc_utils.LIST_BOTH), - ('description', 'Description', osc_utils.LIST_LONG_ONLY), + ('id', 'ID', column_util.LIST_BOTH), + ('name', 'Name', column_util.LIST_BOTH), + ('auth_algorithm', 'Authentication Algorithm', column_util.LIST_BOTH), + ('encryption_algorithm', 'Encryption Algorithm', column_util.LIST_BOTH), + ('ike_version', 'IKE Version', column_util.LIST_BOTH), + ('pfs', 'Perfect Forward Secrecy (PFS)', column_util.LIST_BOTH), + ('description', 'Description', column_util.LIST_LONG_ONLY), ('phase1_negotiation_mode', 'Phase1 Negotiation Mode', - osc_utils.LIST_LONG_ONLY), - ('tenant_id', 'Project', osc_utils.LIST_LONG_ONLY), - ('lifetime', 'Lifetime', osc_utils.LIST_LONG_ONLY), + column_util.LIST_LONG_ONLY), + ('tenant_id', 'Project', column_util.LIST_LONG_ONLY), + ('lifetime', 'Lifetime', column_util.LIST_LONG_ONLY), ) @@ -130,7 +131,7 @@ def take_action(self, parsed_args): if parsed_args.name: attrs['name'] = str(parsed_args.name) obj = client.create_ikepolicy({'ikepolicy': attrs})['ikepolicy'] - columns, display_columns = osc_utils.get_columns(obj, _attr_map) + columns, display_columns = column_util.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -183,7 +184,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.neutronclient obj = client.list_ikepolicies()['ikepolicies'] - headers, columns = osc_utils.get_column_definitions( + headers, columns = column_util.get_column_definitions( _attr_map, long_listing=parsed_args.long) return (headers, (utils.get_dict_properties(s, columns) for s in obj)) @@ -238,6 +239,6 @@ def take_action(self, parsed_args): 'ikepolicy', parsed_args.ikepolicy, cmd_resource='ikepolicy')['id'] obj = client.show_ikepolicy(ike_id)['ikepolicy'] - columns, display_columns = osc_utils.get_columns(obj, _attr_map) + columns, display_columns = column_util.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns) return (display_columns, data) diff --git a/neutronclient/osc/v2/vpnaas/ipsec_site_connection.py b/neutronclient/osc/v2/vpnaas/ipsec_site_connection.py index 8dd98a9e4..4a861f081 100644 --- a/neutronclient/osc/v2/vpnaas/ipsec_site_connection.py +++ b/neutronclient/osc/v2/vpnaas/ipsec_site_connection.py @@ -18,6 +18,7 @@ from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from osc_lib.utils import columns as column_util from oslo_log import log as logging from neutronclient._i18n import _ @@ -35,26 +36,27 @@ _attr_map = ( - ('id', 'ID', osc_utils.LIST_BOTH), - ('name', 'Name', osc_utils.LIST_BOTH), - ('peer_address', 'Peer Address', osc_utils.LIST_BOTH), - ('auth_mode', 'Authentication Algorithm', osc_utils.LIST_BOTH), - ('status', 'Status', osc_utils.LIST_BOTH), - ('tenant_id', 'Project', osc_utils.LIST_LONG_ONLY), - ('peer_cidrs', 'Peer CIDRs', osc_utils.LIST_LONG_ONLY), - ('vpnservice_id', 'VPN Service', osc_utils.LIST_LONG_ONLY), - ('ipsecpolicy_id', 'IPSec Policy', osc_utils.LIST_LONG_ONLY), - ('ikepolicy_id', 'IKE Policy', osc_utils.LIST_LONG_ONLY), - ('mtu', 'MTU', osc_utils.LIST_LONG_ONLY), - ('initiator', 'Initiator', osc_utils.LIST_LONG_ONLY), - ('admin_state_up', 'State', osc_utils.LIST_LONG_ONLY), - ('description', 'Description', osc_utils.LIST_LONG_ONLY), - ('psk', 'Pre-shared Key', osc_utils.LIST_LONG_ONLY), - ('route_mode', 'Route Mode', osc_utils.LIST_LONG_ONLY), - ('local_id', 'Local ID', osc_utils.LIST_LONG_ONLY), - ('peer_id', 'Peer ID', osc_utils.LIST_LONG_ONLY), - ('local_ep_group_id', 'Local Endpoint Group ID', osc_utils.LIST_LONG_ONLY), - ('peer_ep_group_id', 'Peer Endpoint Group ID', osc_utils.LIST_LONG_ONLY), + ('id', 'ID', column_util.LIST_BOTH), + ('name', 'Name', column_util.LIST_BOTH), + ('peer_address', 'Peer Address', column_util.LIST_BOTH), + ('auth_mode', 'Authentication Algorithm', column_util.LIST_BOTH), + ('status', 'Status', column_util.LIST_BOTH), + ('tenant_id', 'Project', column_util.LIST_LONG_ONLY), + ('peer_cidrs', 'Peer CIDRs', column_util.LIST_LONG_ONLY), + ('vpnservice_id', 'VPN Service', column_util.LIST_LONG_ONLY), + ('ipsecpolicy_id', 'IPSec Policy', column_util.LIST_LONG_ONLY), + ('ikepolicy_id', 'IKE Policy', column_util.LIST_LONG_ONLY), + ('mtu', 'MTU', column_util.LIST_LONG_ONLY), + ('initiator', 'Initiator', column_util.LIST_LONG_ONLY), + ('admin_state_up', 'State', column_util.LIST_LONG_ONLY), + ('description', 'Description', column_util.LIST_LONG_ONLY), + ('psk', 'Pre-shared Key', column_util.LIST_LONG_ONLY), + ('route_mode', 'Route Mode', column_util.LIST_LONG_ONLY), + ('local_id', 'Local ID', column_util.LIST_LONG_ONLY), + ('peer_id', 'Peer ID', column_util.LIST_LONG_ONLY), + ('local_ep_group_id', 'Local Endpoint Group ID', + column_util.LIST_LONG_ONLY), + ('peer_ep_group_id', 'Peer Endpoint Group ID', column_util.LIST_LONG_ONLY), ) @@ -239,7 +241,7 @@ def take_action(self, parsed_args): raise exceptions.CommandError(message) obj = client.create_ipsec_site_connection( {'ipsec_site_connection': attrs})['ipsec_site_connection'] - columns, display_columns = osc_utils.get_columns(obj, _attr_map) + columns, display_columns = column_util.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns, formatters=_formatters) return display_columns, data @@ -296,7 +298,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.neutronclient obj = client.list_ipsec_site_connections()['ipsec_site_connections'] - headers, columns = osc_utils.get_column_definitions( + headers, columns = column_util.get_column_definitions( _attr_map, long_listing=parsed_args.long) return (headers, (utils.get_dict_properties( s, columns, formatters=_formatters) for s in obj)) @@ -367,6 +369,6 @@ def take_action(self, parsed_args): cmd_resource='ipsec_site_connection')['id'] obj = client.show_ipsec_site_connection( ipsec_site_id)['ipsec_site_connection'] - columns, display_columns = osc_utils.get_columns(obj, _attr_map) + columns, display_columns = column_util.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns, formatters=_formatters) return (display_columns, data) diff --git a/neutronclient/osc/v2/vpnaas/ipsecpolicy.py b/neutronclient/osc/v2/vpnaas/ipsecpolicy.py index 43599f328..2c80543e7 100644 --- a/neutronclient/osc/v2/vpnaas/ipsecpolicy.py +++ b/neutronclient/osc/v2/vpnaas/ipsecpolicy.py @@ -17,6 +17,7 @@ from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from osc_lib.utils import columns as column_util from oslo_log import log as logging from neutronclient._i18n import _ @@ -28,16 +29,16 @@ LOG = logging.getLogger(__name__) _attr_map = ( - ('id', 'ID', osc_utils.LIST_BOTH), - ('name', 'Name', osc_utils.LIST_BOTH), - ('auth_algorithm', 'Authentication Algorithm', osc_utils.LIST_BOTH), - ('encapsulation_mode', 'Encapsulation Mode', osc_utils.LIST_BOTH), - ('transform_protocol', 'Transform Protocol', osc_utils.LIST_BOTH), - ('encryption_algorithm', 'Encryption Algorithm', osc_utils.LIST_BOTH), - ('pfs', 'Perfect Forward Secrecy (PFS)', osc_utils.LIST_LONG_ONLY), - ('description', 'Description', osc_utils.LIST_LONG_ONLY), - ('tenant_id', 'Project', osc_utils.LIST_LONG_ONLY), - ('lifetime', 'Lifetime', osc_utils.LIST_LONG_ONLY), + ('id', 'ID', column_util.LIST_BOTH), + ('name', 'Name', column_util.LIST_BOTH), + ('auth_algorithm', 'Authentication Algorithm', column_util.LIST_BOTH), + ('encapsulation_mode', 'Encapsulation Mode', column_util.LIST_BOTH), + ('transform_protocol', 'Transform Protocol', column_util.LIST_BOTH), + ('encryption_algorithm', 'Encryption Algorithm', column_util.LIST_BOTH), + ('pfs', 'Perfect Forward Secrecy (PFS)', column_util.LIST_LONG_ONLY), + ('description', 'Description', column_util.LIST_LONG_ONLY), + ('tenant_id', 'Project', column_util.LIST_LONG_ONLY), + ('lifetime', 'Lifetime', column_util.LIST_LONG_ONLY), ) @@ -128,7 +129,7 @@ def take_action(self, parsed_args): if parsed_args.name: attrs['name'] = str(parsed_args.name) obj = client.create_ipsecpolicy({'ipsecpolicy': attrs})['ipsecpolicy'] - columns, display_columns = osc_utils.get_columns(obj, _attr_map) + columns, display_columns = column_util.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -182,7 +183,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.neutronclient obj = client.list_ipsecpolicies()['ipsecpolicies'] - headers, columns = osc_utils.get_column_definitions( + headers, columns = column_util.get_column_definitions( _attr_map, long_listing=parsed_args.long) return (headers, (utils.get_dict_properties(s, columns) for s in obj)) @@ -237,6 +238,6 @@ def take_action(self, parsed_args): 'ipsecpolicy', parsed_args.ipsecpolicy, cmd_resource='ipsecpolicy')['id'] obj = client.show_ipsecpolicy(ipsec_id)['ipsecpolicy'] - columns, display_columns = osc_utils.get_columns(obj, _attr_map) + columns, display_columns = column_util.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns) return (display_columns, data) diff --git a/neutronclient/osc/v2/vpnaas/vpnservice.py b/neutronclient/osc/v2/vpnaas/vpnservice.py index 2120a14fb..3d5b10e7d 100644 --- a/neutronclient/osc/v2/vpnaas/vpnservice.py +++ b/neutronclient/osc/v2/vpnaas/vpnservice.py @@ -17,6 +17,7 @@ from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from osc_lib.utils import columns as column_util from oslo_log import log as logging from neutronclient._i18n import _ @@ -26,15 +27,15 @@ LOG = logging.getLogger(__name__) _attr_map = ( - ('id', 'ID', osc_utils.LIST_BOTH), - ('name', 'Name', osc_utils.LIST_BOTH), - ('router_id', 'Router', osc_utils.LIST_BOTH), - ('subnet_id', 'Subnet', osc_utils.LIST_BOTH), - ('flavor_id', 'Flavor', osc_utils.LIST_BOTH), - ('admin_state_up', 'State', osc_utils.LIST_BOTH), - ('status', 'Status', osc_utils.LIST_BOTH), - ('description', 'Description', osc_utils.LIST_LONG_ONLY), - ('tenant_id', 'Project', osc_utils.LIST_LONG_ONLY), + ('id', 'ID', column_util.LIST_BOTH), + ('name', 'Name', column_util.LIST_BOTH), + ('router_id', 'Router', column_util.LIST_BOTH), + ('subnet_id', 'Subnet', column_util.LIST_BOTH), + ('flavor_id', 'Flavor', column_util.LIST_BOTH), + ('admin_state_up', 'State', column_util.LIST_BOTH), + ('status', 'Status', column_util.LIST_BOTH), + ('description', 'Description', column_util.LIST_LONG_ONLY), + ('tenant_id', 'Project', column_util.LIST_LONG_ONLY), ) @@ -121,7 +122,7 @@ def take_action(self, parsed_args): parsed_args.router).id attrs['router_id'] = _router_id obj = client.create_vpnservice({'vpnservice': attrs})['vpnservice'] - columns, display_columns = osc_utils.get_columns(obj, _attr_map) + columns, display_columns = column_util.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -175,7 +176,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.neutronclient obj = client.list_vpnservices()['vpnservices'] - headers, columns = osc_utils.get_column_definitions( + headers, columns = column_util.get_column_definitions( _attr_map, long_listing=parsed_args.long) return (headers, (utils.get_dict_properties(s, columns) for s in obj)) @@ -230,6 +231,6 @@ def take_action(self, parsed_args): 'vpnservice', parsed_args.vpnservice, cmd_resource='vpnservice')['id'] obj = client.show_vpnservice(vpn_id)['vpnservice'] - columns, display_columns = osc_utils.get_columns(obj, _attr_map) + columns, display_columns = column_util.get_columns(obj, _attr_map) data = utils.get_dict_properties(obj, columns) return (display_columns, data) diff --git a/neutronclient/tests/unit/osc/test_utils.py b/neutronclient/tests/unit/osc/test_utils.py deleted file mode 100644 index c9857beda..000000000 --- a/neutronclient/tests/unit/osc/test_utils.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright 2016 NEC Corporation -# -# 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 testtools - -from neutronclient.osc import utils - - -class TestUtils(testtools.TestCase): - - def test_get_column_definitions(self): - attr_map = ( - ('id', 'ID', utils.LIST_BOTH), - ('tenant_id', 'Project', utils.LIST_LONG_ONLY), - ('name', 'Name', utils.LIST_BOTH), - ('summary', 'Summary', utils.LIST_SHORT_ONLY), - ) - headers, columns = utils.get_column_definitions(attr_map, - long_listing=False) - self.assertEqual(['id', 'name', 'summary'], columns) - self.assertEqual(['ID', 'Name', 'Summary'], headers) - - def test_get_column_definitions_long(self): - attr_map = ( - ('id', 'ID', utils.LIST_BOTH), - ('tenant_id', 'Project', utils.LIST_LONG_ONLY), - ('name', 'Name', utils.LIST_BOTH), - ('summary', 'Summary', utils.LIST_SHORT_ONLY), - ) - headers, columns = utils.get_column_definitions(attr_map, - long_listing=True) - self.assertEqual(['id', 'tenant_id', 'name'], columns) - self.assertEqual(['ID', 'Project', 'Name'], headers) - - def test_get_columns(self): - item = { - 'id': 'test-id', - 'tenant_id': 'test-tenant_id', - # 'name' is not included - 'foo': 'bar', # unknown attribute - } - attr_map = ( - ('id', 'ID', utils.LIST_BOTH), - ('tenant_id', 'Project', utils.LIST_LONG_ONLY), - ('name', 'Name', utils.LIST_BOTH), - ) - columns, display_names = utils.get_columns(item, attr_map) - self.assertEqual(tuple(['id', 'tenant_id', 'foo']), columns) - self.assertEqual(tuple(['ID', 'Project', 'foo']), display_names) diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py index 1b8c4c008..21b9a762b 100644 --- a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py +++ b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py @@ -17,6 +17,7 @@ import copy import mock +from osc_lib.utils import columns as column_util from neutronclient.osc import utils as nc_osc_utils from neutronclient.osc.v2.networking_bgpvpn import constants @@ -106,10 +107,10 @@ class BgpvpnFakeAssoc(object): _resource_plural = '%ss' % _resource _attr_map = ( - ('id', 'ID', nc_osc_utils.LIST_BOTH), - ('tenant_id', 'Project', nc_osc_utils.LIST_LONG_ONLY), + ('id', 'ID', column_util.LIST_BOTH), + ('tenant_id', 'Project', column_util.LIST_LONG_ONLY), ('%s_id' % _assoc_res_name, '%s ID' % _assoc_res_name.capitalize(), - nc_osc_utils.LIST_BOTH), + column_util.LIST_BOTH), ) _formatters = {} diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py index 62316d472..14b6bb1a5 100644 --- a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py +++ b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py @@ -20,25 +20,25 @@ import mock from osc_lib import exceptions from osc_lib import utils as osc_utils +from osc_lib.utils import columns as column_util -from neutronclient.osc import utils as nc_osc_utils from neutronclient.osc.v2.networking_bgpvpn import bgpvpn from neutronclient.osc.v2.networking_bgpvpn import constants from neutronclient.tests.unit.osc.v2.networking_bgpvpn import fakes columns_short = tuple(col for col, _, listing_mode in bgpvpn._attr_map - if listing_mode in (nc_osc_utils.LIST_BOTH, - nc_osc_utils.LIST_SHORT_ONLY)) + if listing_mode in (column_util.LIST_BOTH, + column_util.LIST_SHORT_ONLY)) columns_long = tuple(col for col, _, listing_mode in bgpvpn._attr_map - if listing_mode in (nc_osc_utils.LIST_BOTH, - nc_osc_utils.LIST_LONG_ONLY)) + if listing_mode in (column_util.LIST_BOTH, + column_util.LIST_LONG_ONLY)) headers_short = tuple(head for _, head, listing_mode in bgpvpn._attr_map - if listing_mode in (nc_osc_utils.LIST_BOTH, - nc_osc_utils.LIST_SHORT_ONLY)) + if listing_mode in (column_util.LIST_BOTH, + column_util.LIST_SHORT_ONLY)) headers_long = tuple(head for _, head, listing_mode in bgpvpn._attr_map - if listing_mode in (nc_osc_utils.LIST_BOTH, - nc_osc_utils.LIST_LONG_ONLY)) + if listing_mode in (column_util.LIST_BOTH, + column_util.LIST_LONG_ONLY)) sorted_attr_map = sorted(bgpvpn._attr_map, key=operator.itemgetter(1)) sorted_columns = tuple(col for col, _, _ in sorted_attr_map) sorted_headers = tuple(head for _, head, _ in sorted_attr_map) diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py index 49be9f452..926001742 100644 --- a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py +++ b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py @@ -20,27 +20,27 @@ import mock from osc_lib import exceptions from osc_lib import utils as osc_utils +from osc_lib.utils import columns as column_util -from neutronclient.osc import utils as nc_osc_utils from neutronclient.tests.unit.osc.v2.networking_bgpvpn import fakes columns_short = tuple(col for col, _, listing_mode in fakes.BgpvpnFakeAssoc._attr_map - if listing_mode in (nc_osc_utils.LIST_BOTH, - nc_osc_utils.LIST_SHORT_ONLY)) + if listing_mode in (column_util.LIST_BOTH, + column_util.LIST_SHORT_ONLY)) columns_long = tuple(col for col, _, listing_mode in fakes.BgpvpnFakeAssoc._attr_map - if listing_mode in (nc_osc_utils.LIST_BOTH, - nc_osc_utils.LIST_LONG_ONLY)) + if listing_mode in (column_util.LIST_BOTH, + column_util.LIST_LONG_ONLY)) headers_short = tuple(head for _, head, listing_mode in fakes.BgpvpnFakeAssoc._attr_map - if listing_mode in (nc_osc_utils.LIST_BOTH, - nc_osc_utils.LIST_SHORT_ONLY)) + if listing_mode in (column_util.LIST_BOTH, + column_util.LIST_SHORT_ONLY)) headers_long = tuple(head for _, head, listing_mode in fakes.BgpvpnFakeAssoc._attr_map - if listing_mode in (nc_osc_utils.LIST_BOTH, - nc_osc_utils.LIST_LONG_ONLY)) + if listing_mode in (column_util.LIST_BOTH, + column_util.LIST_LONG_ONLY)) sorted_attr_map = sorted(fakes.BgpvpnFakeAssoc._attr_map, key=operator.itemgetter(1)) sorted_columns = tuple(col for col, _, _ in sorted_attr_map) From 1791ce65400315baf075dbade52cdbaef7da0436 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Tue, 16 Jan 2018 14:43:40 +0900 Subject: [PATCH 647/845] Drop neutronclient-specific cliff sphinx extension Previously neutronclient has its own variant of cliff sphinx extension to support cliff app autodocument (cliff-app directive). The feature has been implemented in cliff 2.10.0. This patch switches to the cliff official version. The cliff official version also contains several nice fixes around the sphinx extension. Change-Id: If542fdf88ef57ec3ce8ecc017127b5dc194e6c9a --- doc/source/cli/neutron-reference.rst | 2 +- doc/source/conf.py | 5 +- neutronclient/cliff_sphinxext.py | 385 --------------------------- 3 files changed, 2 insertions(+), 390 deletions(-) delete mode 100644 neutronclient/cliff_sphinxext.py diff --git a/doc/source/cli/neutron-reference.rst b/doc/source/cli/neutron-reference.rst index 24359a5eb..d6640ea0d 100644 --- a/doc/source/cli/neutron-reference.rst +++ b/doc/source/cli/neutron-reference.rst @@ -36,7 +36,7 @@ neutron CLI reference neutron usage ------------- -.. cliff-app:: neutronclient.shell.NeutronShell +.. autoprogram-cliff:: neutronclient.shell.NeutronShell :application: neutron :arguments: 2.0 diff --git a/doc/source/conf.py b/doc/source/conf.py index 1bcb1db59..b1c6ceece 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -9,10 +9,7 @@ 'sphinx.ext.autodoc', 'reno.sphinxext', 'openstackdocstheme', - # 'cliff.sphinxext', - # TODO(amotoki): Switch to cliff.sphinxext once cliff bug is fixed. - # https://bugs.launchpad.net/python-cliff/+bug/1692018 - 'neutronclient.cliff_sphinxext', + 'cliff.sphinxext', ] # openstackdocstheme options diff --git a/neutronclient/cliff_sphinxext.py b/neutronclient/cliff_sphinxext.py deleted file mode 100644 index ca4756c38..000000000 --- a/neutronclient/cliff_sphinxext.py +++ /dev/null @@ -1,385 +0,0 @@ -# Copyright (C) 2017, Red Hat, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import argparse -import fnmatch -import re - -from docutils import nodes -from docutils.parsers import rst -from docutils.parsers.rst import directives -from docutils import statemachine -from oslo_utils import importutils - -from cliff import commandmanager - - -def _indent(text): - """Indent by four spaces.""" - prefix = ' ' * 4 - - def prefixed_lines(): - for line in text.splitlines(True): - yield (prefix + line if line.strip() else line) - - return ''.join(prefixed_lines()) - - -def _format_description(parser): - """Get parser description. - - We parse this as reStructuredText, allowing users to embed rich - information in their help messages if they so choose. - """ - for line in statemachine.string2lines( - parser.description, tab_width=4, convert_whitespace=True): - yield line - - -def _format_usage(parser): - """Get usage without a prefix.""" - fmt = argparse.HelpFormatter(parser.prog) - - optionals = parser._get_optional_actions() - positionals = parser._get_positional_actions() - groups = parser._mutually_exclusive_groups - - # hacked variant of the regex used by the actual argparse module. Unlike - # that version, this one attempts to group long and short opts with their - # optional arguments ensuring that, for example, '---format ' - # becomes ['--format '] and not ['--format', '']. - # Yes, they really do use regexes to break apart and rewrap their help - # string. Don't ask me why. - part_regexp = r'\(.*?\)+|\[.*?\]+|(?:(?:-\w|--\w+)(?:\s+<\w+>)?)|\S+' - - opt_usage = fmt._format_actions_usage(optionals, groups) - pos_usage = fmt._format_actions_usage(positionals, groups) - - opt_parts = re.findall(part_regexp, opt_usage) - pos_parts = re.findall(part_regexp, pos_usage) - parts = opt_parts + pos_parts - - if len(' '.join([parser.prog] + parts)) < 72: - return [' '.join([parser.prog] + parts)] - - return [parser.prog] + [_indent(x) for x in parts] - - -def _format_epilog(parser): - """Get parser epilog. - - We parse this as reStructuredText, allowing users to embed rich - information in their help messages if they so choose. - """ - for line in statemachine.string2lines( - parser.epilog, tab_width=4, convert_whitespace=True): - yield line - - -def _format_positional_action(action): - """Format a positional action.""" - if action.help == argparse.SUPPRESS: - return - - # NOTE(stephenfin): We strip all types of brackets from 'metavar' because - # the 'option' directive dictates that only option argument names should be - # surrounded by angle brackets - yield '.. option:: {}'.format( - (action.metavar or action.dest).strip('<>[]() ')) - if action.help: - yield '' - for line in statemachine.string2lines( - action.help, tab_width=4, convert_whitespace=True): - yield _indent(line) - - -def _format_optional_action(action): - """Format an optional action.""" - if action.help == argparse.SUPPRESS: - return - - if action.nargs == 0: - yield '.. option:: {}'.format(', '.join(action.option_strings)) - else: - # TODO(stephenfin): At some point, we may wish to provide more - # information about the options themselves, for example, if nargs is - # specified - option_strings = [' '.join( - [x, action.metavar or '<{}>'.format(action.dest.upper())]) - for x in action.option_strings] - yield '.. option:: {}'.format(', '.join(option_strings)) - - if action.help: - yield '' - for line in statemachine.string2lines( - action.help, tab_width=4, convert_whitespace=True): - yield _indent(line) - - -def _format_parser(parser): - """Format the output of an argparse 'ArgumentParser' object. - - Given the following parser:: - - >>> import argparse - >>> parser = argparse.ArgumentParser(prog='hello-world', \ - description='This is my description.', - epilog='This is my epilog') - >>> parser.add_argument('name', help='User name', metavar='') - >>> parser.add_argument('--language', action='store', dest='lang', \ - help='Greeting language') - - Returns the following:: - - This is my description. - - .. program:: hello-world - .. code:: shell - - hello-world [-h] [--language LANG] - - .. option:: name - - User name - - .. option:: --language LANG - - Greeting language - - .. option:: -h, --help - - Show this help message and exit - - This is my epilog. - """ - if parser.description: - for line in _format_description(parser): - yield line - yield '' - - yield '.. program:: {}'.format(parser.prog) - - yield '.. code-block:: shell' - yield '' - for line in _format_usage(parser): - yield _indent(line) - yield '' - - # In argparse, all arguments and parameters are known as "actions". - # Optional actions are what would be known as flags or options in other - # libraries, while positional actions would generally be known as - # arguments. We present these slightly differently. - - for action in parser._get_optional_actions(): - for line in _format_optional_action(action): - yield line - yield '' - - for action in parser._get_positional_actions(): - for line in _format_positional_action(action): - yield line - yield '' - - if parser.epilog: - for line in _format_epilog(parser): - yield line - yield '' - - -class AutoprogramCliffDirective(rst.Directive): - """Auto-document a subclass of `cliff.command.Command`.""" - - has_content = False - required_arguments = 1 - option_spec = { - 'command': directives.unchanged, - 'ignored': directives.unchanged, - 'application': directives.unchanged, - } - - def _load_command(self, manager, command_name): - """Load a command using an instance of a `CommandManager`.""" - try: - # find_command expects the value of argv so split to emulate that - return manager.find_command(command_name.split())[0] - except ValueError: - raise self.error('"{}" is not a valid command in the "{}" ' - 'namespace'.format( - command_name, manager.namespace)) - - def _generate_nodes(self, title, command_name, command_class, - ignored_opts): - """Generate the relevant Sphinx nodes. - - This is a little funky. Parts of this use raw docutils nodes while - other parts use reStructuredText and nested parsing. The reason for - this is simple: it avoids us having to reinvent the wheel. While raw - docutils nodes are helpful for the simpler elements of the output, - they don't provide an easy way to use Sphinx's own directives, such as - the 'option' directive. Refer to [1] for more information. - - [1] http://www.sphinx-doc.org/en/stable/extdev/markupapi.html - - :param title: Title of command - :param command_name: Name of command, as used on the command line - :param command_class: Subclass of :py:class:`cliff.command.Command` - :param prefix: Prefix to apply before command, if any - :param ignored_opts: A list of options to exclude from output, if any - :returns: A list of nested docutil nodes - """ - command = command_class(None, None) - parser = command.get_parser(command_name) - ignored_opts = ignored_opts or [] - - # Drop the automatically-added help action - for action in list(parser._actions): - for option_string in action.option_strings: - if option_string in ignored_opts: - del parser._actions[parser._actions.index(action)] - break - - section = nodes.section( - '', - nodes.title(text=title), - ids=[nodes.make_id(title)], - names=[nodes.fully_normalize_name(title)]) - - source_name = '<{}>'.format(command.__class__.__name__) - result = statemachine.ViewList() - - for line in _format_parser(parser): - result.append(line, source_name) - - self.state.nested_parse(result, 0, section) - - return [section] - - def run(self): - self.env = self.state.document.settings.env - - command_pattern = self.options.get('command') - application_name = (self.options.get('application') - or self.env.config.autoprogram_cliff_application) - - global_ignored = self.env.config.autoprogram_cliff_ignored - local_ignored = self.options.get('ignored', '') - local_ignored = [x.strip() for x in local_ignored.split(',') - if x.strip()] - ignored_opts = list(set(global_ignored + local_ignored)) - - # TODO(sfinucan): We should probably add this wildcarding functionality - # to the CommandManager itself to allow things like "show me the - # commands like 'foo *'" - manager = commandmanager.CommandManager(self.arguments[0]) - if command_pattern: - commands = [x for x in manager.commands - if fnmatch.fnmatch(x, command_pattern)] - else: - commands = manager.commands.keys() - - output = [] - for command_name in sorted(commands): - command_class = self._load_command(manager, command_name) - - title = command_name - if application_name: - command_name = ' '.join([application_name, command_name]) - - output.extend(self._generate_nodes( - title, command_name, command_class, ignored_opts)) - - return output - - -class CliffAppDirective(rst.Directive): - """Auto-document a `cliff.app.App`.""" - - has_content = False - required_arguments = 1 - option_spec = { - 'arguments': directives.unchanged, - 'ignored': directives.unchanged, - 'application': directives.unchanged, - } - - def _generate_nodes(self, title, app, app_name, ignored_opts): - """Generate the relevant Sphinx nodes. - - This is a little funky. Parts of this use raw docutils nodes while - other parts use reStructuredText and nested parsing. The reason for - this is simple: it avoids us having to reinvent the wheel. While raw - docutils nodes are helpful for the simpler elements of the output, - they don't provide an easy way to use Sphinx's own directives, such as - the 'option' directive. Refer to [1] for more information. - - [1] http://www.sphinx-doc.org/en/stable/extdev/markupapi.html - - :param title: Title of command - :param app: Subclass of :py:class`cliff.app.App` - :param app_name: The name of the cliff application. - This is used as the command name. - :param ignored_opts: A list of options to exclude from output, if any - :returns: A list of docutil nodes - """ - parser = app.parser - ignored_opts = ignored_opts or [] - - # Drop the automatically-added help action - for action in list(parser._actions): - for option_string in action.option_strings: - if option_string in ignored_opts: - del parser._actions[parser._actions.index(action)] - break - - parser.prog = app_name - - source_name = '<{}>'.format(app.__class__.__name__) - result = statemachine.ViewList() - for line in _format_parser(parser): - result.append(line, source_name) - - section = nodes.section() - self.state.nested_parse(result, 0, section) - return section.children - - def run(self): - self.env = self.state.document.settings.env - - cliff_app_class = importutils.import_class(self.arguments[0]) - app_arguments = self.options.get('arguments', '').split() - cliff_app = cliff_app_class(*app_arguments) - - application_name = (self.options.get('application') - or self.env.config.autoprogram_cliff_application) - - global_ignored = self.env.config.autoprogram_cliff_ignored - local_ignored = self.options.get('ignored', '') - local_ignored = [x.strip() for x in local_ignored.split(',') - if x.strip()] - ignored_opts = list(set(global_ignored + local_ignored)) - - output = [] - title = application_name - output.extend(self._generate_nodes( - title, cliff_app, application_name, ignored_opts)) - - return output - - -def setup(app): - app.add_directive('autoprogram-cliff', AutoprogramCliffDirective) - app.add_config_value('autoprogram_cliff_application', '', True) - app.add_config_value('autoprogram_cliff_ignored', ['--help'], True) - - app.add_directive('cliff-app', CliffAppDirective) From 595edf430beec92382053b2abc7b7977c7aa61c3 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Sat, 17 Feb 2018 10:15:45 +0000 Subject: [PATCH 648/845] Updated from global requirements Change-Id: I045b436539616b8ec3213b65467bbb75c931e2da --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4933c33b5..17de076a7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ oslo.i18n>=3.15.3 # Apache-2.0 oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0 oslo.utils>=3.33.0 # Apache-2.0 os-client-config>=1.28.0 # Apache-2.0 -keystoneauth1>=3.3.0 # Apache-2.0 +keystoneauth1>=3.4.0 # Apache-2.0 # keystoneclient is used only by neutronclient.osc.utils # TODO(amotoki): Drop this after osc.utils has no dependency on keystoneclient python-keystoneclient>=3.8.0 # Apache-2.0 From dd365401ab9d665daa8d57b6630bf4386f37297e Mon Sep 17 00:00:00 2001 From: Jens Harbott Date: Tue, 6 Mar 2018 11:30:24 +0000 Subject: [PATCH 649/845] Remove bogus ID column from ListRoutesAdvertisedBySpeaker The routes shown by ListRoutesAdvertisedBySpeaker only contain the destination and the next_hop fields, so drop the ID column that is always empty anyway. Change-Id: Idc19ebb55d8eb30e61fd1f9101512279e00b6f0b --- neutronclient/osc/v2/dynamic_routing/bgp_speaker.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neutronclient/osc/v2/dynamic_routing/bgp_speaker.py b/neutronclient/osc/v2/dynamic_routing/bgp_speaker.py index 366dadcf3..c54582b9d 100644 --- a/neutronclient/osc/v2/dynamic_routing/bgp_speaker.py +++ b/neutronclient/osc/v2/dynamic_routing/bgp_speaker.py @@ -215,8 +215,8 @@ def take_action(self, parsed_args): speaker_id = client.find_resource(constants.BGP_SPEAKER, parsed_args.bgp_speaker)['id'] data = client.list_route_advertised_from_bgp_speaker(speaker_id) - headers = ('ID', 'Destination', 'Nexthop') - columns = ('id', 'destination', 'next_hop') + headers = ('Destination', 'Nexthop') + columns = ('destination', 'next_hop') return (headers, (utils.get_dict_properties(s, columns) for s in data['advertised_routes'])) From e1729c5a57581508c910a5ea53fb507e755e01d5 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Sat, 10 Mar 2018 13:49:08 +0000 Subject: [PATCH 650/845] Updated from global requirements Change-Id: I2db9e43b98a209d654c1749576235bd8260c0fc5 --- test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index 2ad071dfa..d7d7efe7c 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -15,7 +15,7 @@ python-openstackclient>=3.12.0 # Apache-2.0 python-subunit>=1.0.0 # Apache-2.0/BSD reno>=2.5.0 # Apache-2.0 requests-mock>=1.1.0 # Apache-2.0 -sphinx!=1.6.6,>=1.6.2 # BSD +sphinx!=1.6.6,<1.7.0,>=1.6.2 # BSD testrepository>=0.0.18 # Apache-2.0/BSD testtools>=2.2.0 # MIT testscenarios>=0.4 # Apache-2.0/BSD From 03c5c1f537bdb31f1014721d0c07660b8c74f75b Mon Sep 17 00:00:00 2001 From: Hongbin Lu Date: Wed, 7 Mar 2018 23:02:59 +0000 Subject: [PATCH 651/845] Remove mox/mox3 usage from test_quota Change-Id: I879a3ae40d13e0a41b655ee028640463aa809c8b Partial-Bug: #1753504 --- neutronclient/tests/unit/test_cli20.py | 26 ++++++++++++++++++++++++ neutronclient/tests/unit/test_quota.py | 28 ++++++++++++-------------- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 0837a6c3d..7c568972d 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -170,6 +170,32 @@ def __repr__(self): return str(self.lhs) +class ContainsKeyValue(object): + """Checks whether key/value pair(s) are included in a dict parameter. + + This class just checks whether specifid key/value pairs passed in + __init__() are included in a dict parameter. The comparison does not + fail even if other key/value pair(s) exists in a target dict. + """ + + def __init__(self, expected): + self._expected = expected + + def __eq__(self, other): + if not isinstance(other, dict): + return False + for key, value in self._expected.items(): + if key not in other: + return False + if other[key] != value: + return False + return True + + def __repr__(self): + return ('<%s (expected: %s)>' % + (self.__class__.__name__, self._expected)) + + class CLITestV20Base(base.BaseTestCase): test_id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' diff --git a/neutronclient/tests/unit/test_quota.py b/neutronclient/tests/unit/test_quota.py index d4bc4aac9..6192bbb9a 100644 --- a/neutronclient/tests/unit/test_quota.py +++ b/neutronclient/tests/unit/test_quota.py @@ -16,7 +16,7 @@ import sys -from mox3 import mox +import mock from neutronclient.common import exceptions from neutronclient.neutron.v2_0 import quota as test_quota @@ -67,26 +67,24 @@ def test_show_quota_default(self): cmd = test_quota.ShowQuotaDefault( test_cli20.MyApp(sys.stdout), None) args = ['--tenant-id', self.test_id] - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) expected_res = {'quota': {'port': 50, 'network': 10, 'subnet': 10}} resstr = self.client.serialize(expected_res) path = getattr(self.client, "quota_default_path") return_tup = (test_cli20.MyResp(200), resstr) - self.client.httpclient.request( + with mock.patch.object(cmd, 'get_client', + return_value=self.client) as mock_get_client, \ + mock.patch.object(self.client.httpclient, 'request', + return_value=return_tup) as mock_request: + cmd_parser = cmd.get_parser("test_" + resource) + parsed_args = cmd_parser.parse_args(args) + cmd.run(parsed_args) + + mock_get_client.assert_called_once_with() + mock_request.assert_called_once_with( test_cli20.end_url(path % self.test_id), 'GET', body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(return_tup) - self.mox.ReplayAll() - - cmd_parser = cmd.get_parser("test_" + resource) - parsed_args = cmd_parser.parse_args(args) - cmd.run(parsed_args) - - self.mox.VerifyAll() - self.mox.UnsetStubs() + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN})) _str = self.fake_stdout.make_string() self.assertIn('network', _str) self.assertIn('subnet', _str) From 06961da731611fa813e8e4c6cd63a660d17d68d2 Mon Sep 17 00:00:00 2001 From: Hongbin Lu Date: Wed, 7 Mar 2018 23:45:29 +0000 Subject: [PATCH 652/845] Remove mox/mox3 usage from test_cli20_tag Change-Id: I2570d45b155d968c6711a10012cd38619e83e4a2 Partial-Bug: #1753504 --- neutronclient/tests/unit/test_cli20_tag.py | 71 +++++++++++----------- 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/neutronclient/tests/unit/test_cli20_tag.py b/neutronclient/tests/unit/test_cli20_tag.py index 1b04be63b..bb3c451e9 100644 --- a/neutronclient/tests/unit/test_cli20_tag.py +++ b/neutronclient/tests/unit/test_cli20_tag.py @@ -12,7 +12,7 @@ import sys -from mox3 import mox +import mock from neutronclient.common import exceptions from neutronclient.neutron.v2_0 import network @@ -24,43 +24,43 @@ class CLITestV20Tag(test_cli20.CLITestV20Base): def _test_tag_operation(self, cmd, path, method, args, prog_name, body=None): - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) - if body: - body = test_cli20.MyComparator(body, self.client) - self.client.httpclient.request( + with mock.patch.object(cmd, 'get_client', + return_value=self.client) as mock_get_client, \ + mock.patch.object(self.client.httpclient, 'request', + return_value=(test_cli20.MyResp(204), None) + ) as mock_request: + if body: + body = test_cli20.MyComparator(body, self.client) + cmd_parser = cmd.get_parser(prog_name) + shell.run_command(cmd, cmd_parser, args) + + mock_get_client.assert_called_once_with() + mock_request.assert_called_once_with( test_cli20.MyUrlComparator(test_cli20.end_url(path), self.client), method, body=body, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn( - (test_cli20.MyResp(204), None)) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser(prog_name) - shell.run_command(cmd, cmd_parser, args) - self.mox.VerifyAll() - self.mox.UnsetStubs() + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN})) def _test_tags_query(self, cmd, resources, args, query): - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) path = getattr(self.client, resources + "_path") res = {resources: [{'id': 'myid'}]} resstr = self.client.serialize(res) - self.client.httpclient.request( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, query), - self.client), + with mock.patch.object(cmd, 'get_client', + return_value=self.client) as mock_get_client, \ + mock.patch.object(self.client.httpclient, 'request', + return_value=(test_cli20.MyResp(200), resstr) + ) as mock_request: + cmd_parser = cmd.get_parser("list_networks") + shell.run_command(cmd, cmd_parser, args) + + mock_get_client.assert_called_once_with() + mock_request.assert_called_once_with( + test_cli20.MyUrlComparator(test_cli20.end_url(path, query), + self.client), 'GET', body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn( - (test_cli20.MyResp(200), resstr)) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser("list_networks") - shell.run_command(cmd, cmd_parser, args) - self.mox.VerifyAll() - self.mox.UnsetStubs() + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN})) + _str = self.fake_stdout.make_string() self.assertIn('myid', _str) @@ -124,9 +124,8 @@ def test_tags_query(self): # is not converted to '_'. resources = 'networks' cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - self.mox.StubOutWithMock(network.ListNetwork, "extend_list") - network.ListNetwork.extend_list(mox.IsA(list), mox.IgnoreArg()) - args = ['--not-tags', 'red,blue', '--tags-any', 'green', - '--not-tags-any', 'black'] - query = "not-tags=red,blue&tags-any=green¬-tags-any=black" - self._test_tags_query(cmd, resources, args, query) + with mock.patch.object(network.ListNetwork, 'extend_list'): + args = ['--not-tags', 'red,blue', '--tags-any', 'green', + '--not-tags-any', 'black'] + query = "not-tags=red,blue&tags-any=green¬-tags-any=black" + self._test_tags_query(cmd, resources, args, query) From 40adca4c505fd9c0df608495c37deaa8dac3b518 Mon Sep 17 00:00:00 2001 From: Hongbin Lu Date: Tue, 13 Mar 2018 19:34:00 +0000 Subject: [PATCH 653/845] Fix the assertion of standard error text In the test 'test_run_unknown_command', we assert the exact matching of stderr output stream but this will break if there are extra output. An example is the deprecation warning outputted by any library neutronclient depends on. Change-Id: Ia80c13be5afd97ed1ada8ead70888971f0159647 Closes-Bug: #1755575 --- neutronclient/tests/unit/test_shell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/tests/unit/test_shell.py b/neutronclient/tests/unit/test_shell.py index 2a6c4ffab..0395d6233 100644 --- a/neutronclient/tests/unit/test_shell.py +++ b/neutronclient/tests/unit/test_shell.py @@ -95,7 +95,7 @@ def test_run_unknown_command(self): self.useFixture(fixtures.FakeLogger(level=logging.DEBUG)) stdout, stderr = self.shell('fake', check=True) self.assertFalse(stdout) - self.assertEqual("Unknown command ['fake']", stderr.strip()) + self.assertIn("Unknown command ['fake']", stderr.strip()) def test_help(self): required = 'usage:' From f2abd8ce1645984ab5a2f5f267527fb3b386814b Mon Sep 17 00:00:00 2001 From: Hongbin Lu Date: Thu, 8 Mar 2018 19:40:32 +0000 Subject: [PATCH 654/845] Remove mox/mox3 usage from test_cli20_port.py Change-Id: I0ea56965b0753b56bd51ddace0f180723ac6c432 Partial-Bug: #1753504 --- neutronclient/tests/unit/test_cli20.py | 5 ++++ neutronclient/tests/unit/test_cli20_port.py | 30 ++++++++++----------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 7c568972d..68f75d0d0 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -589,6 +589,11 @@ def _test_update_resource_action(self, resource, cmd, myid, action, args, _str = self.fake_stdout.make_string() self.assertIn(myid, _str) + def _assert_mock_multiple_calls_with_same_arguments( + self, mocked_method, count, expected_call): + self.assertEqual(count, mocked_method.call_count) + mocked_method.assert_has_calls([expected_call] * count) + class TestListCommand(neutronV2_0.ListCommand): resource = 'test_resource' diff --git a/neutronclient/tests/unit/test_cli20_port.py b/neutronclient/tests/unit/test_cli20_port.py index 50eaffbdb..b28953a53 100644 --- a/neutronclient/tests/unit/test_cli20_port.py +++ b/neutronclient/tests/unit/test_cli20_port.py @@ -17,7 +17,7 @@ import itertools import sys -from mox3 import mox +import mock from neutronclient.neutron.v2_0 import port from neutronclient import shell @@ -460,14 +460,10 @@ def test_list_ports_with_fixed_ips_in_csv(self): def _test_list_router_port(self, resources, cmd, myid, detail=False, tags=(), fields_1=(), fields_2=()): - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) reses = {resources: [{'id': 'myid1', }, {'id': 'myid2', }, ], } resstr = self.client.serialize(reses) - # url method body query = "" args = detail and ['-D', ] or [] @@ -503,19 +499,23 @@ def _test_list_router_port(self, resources, cmd, query = query and query + '&verbose=True' or 'verbose=True' query = query and query + '&device_id=%s' or 'device_id=%s' path = getattr(self.client, resources + "_path") - self.client.httpclient.request( + with mock.patch.object(cmd, "get_client", + return_value=self.client) as mock_get_client, \ + mock.patch.object(self.client.httpclient, "request", + return_value=(test_cli20.MyResp(200), + resstr)) as mock_request: + cmd_parser = cmd.get_parser("list_" + resources) + shell.run_command(cmd, cmd_parser, args) + + self._assert_mock_multiple_calls_with_same_arguments( + mock_get_client, 2, mock.call()) + mock_request.assert_called_once_with( test_cli20.MyUrlComparator( - test_cli20.end_url(path, query % myid), - self.client), + test_cli20.end_url(path, query % myid), self.client), 'GET', body=None, - headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) - ).AndReturn((test_cli20.MyResp(200), resstr)) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser("list_" + resources) - shell.run_command(cmd, cmd_parser, args) - self.mox.VerifyAll() - self.mox.UnsetStubs() + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN})) _str = self.fake_stdout.make_string() self.assertIn('myid1', _str) From d8a605c591e3fcd049925d1911d7314965739d0c Mon Sep 17 00:00:00 2001 From: Hongbin Lu Date: Thu, 8 Mar 2018 20:48:02 +0000 Subject: [PATCH 655/845] Remove mox/mox3 usage from test_name_or_id.py Change-Id: Iefaeb45312177be172858203715bdc1db88e6add Partial-Bug: #1753504 --- neutronclient/tests/unit/test_name_or_id.py | 186 +++++++++++--------- 1 file changed, 101 insertions(+), 85 deletions(-) diff --git a/neutronclient/tests/unit/test_name_or_id.py b/neutronclient/tests/unit/test_name_or_id.py index 5b5dc024f..ee94b24b1 100644 --- a/neutronclient/tests/unit/test_name_or_id.py +++ b/neutronclient/tests/unit/test_name_or_id.py @@ -14,8 +14,7 @@ # under the License. # - -from mox3 import mox +import mock from oslo_utils import uuidutils import testtools @@ -30,30 +29,29 @@ class CLITestNameorID(testtools.TestCase): def setUp(self): """Prepare the test environment.""" super(CLITestNameorID, self).setUp() - self.mox = mox.Mox() self.endurl = test_cli20.ENDURL self.client = client.Client(token=test_cli20.TOKEN, endpoint_url=self.endurl) - self.addCleanup(self.mox.VerifyAll) - self.addCleanup(self.mox.UnsetStubs) def test_get_id_from_id(self): _id = uuidutils.generate_uuid() reses = {'networks': [{'id': _id, }, ], } resstr = self.client.serialize(reses) - self.mox.StubOutWithMock(self.client.httpclient, "request") + resp = (test_cli20.MyResp(200), resstr) path = getattr(self.client, "networks_path") - self.client.httpclient.request( + with mock.patch.object(self.client.httpclient, "request", + return_value=resp) as mock_request: + returned_id = neutronV20.find_resourceid_by_name_or_id( + self.client, 'network', _id) + + mock_request.assert_called_once_with( test_cli20.MyUrlComparator( test_cli20.end_url(path, "fields=id&id=" + _id), self.client), 'GET', body=None, - headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) - ).AndReturn((test_cli20.MyResp(200), resstr)) - self.mox.ReplayAll() - returned_id = neutronV20.find_resourceid_by_name_or_id( - self.client, 'network', _id) + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN})) self.assertEqual(_id, returned_id) def test_get_id_from_id_then_name_empty(self): @@ -61,27 +59,32 @@ def test_get_id_from_id_then_name_empty(self): reses = {'networks': [{'id': _id, }, ], } resstr = self.client.serialize(reses) resstr1 = self.client.serialize({'networks': []}) - self.mox.StubOutWithMock(self.client.httpclient, "request") path = getattr(self.client, "networks_path") - self.client.httpclient.request( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, "fields=id&id=" + _id), - self.client), - 'GET', - body=None, - headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) - ).AndReturn((test_cli20.MyResp(200), resstr1)) - self.client.httpclient.request( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, "fields=id&name=" + _id), - self.client), - 'GET', - body=None, - headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) - ).AndReturn((test_cli20.MyResp(200), resstr)) - self.mox.ReplayAll() - returned_id = neutronV20.find_resourceid_by_name_or_id( - self.client, 'network', _id) + with mock.patch.object(self.client.httpclient, + "request") as mock_request: + mock_request.side_effect = [(test_cli20.MyResp(200), resstr1), + (test_cli20.MyResp(200), resstr)] + returned_id = neutronV20.find_resourceid_by_name_or_id( + self.client, 'network', _id) + + self.assertEqual(2, mock_request.call_count) + mock_request.assert_has_calls([ + mock.call( + test_cli20.MyUrlComparator( + test_cli20.end_url(path, "fields=id&id=" + _id), + self.client), + 'GET', + body=None, + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN})), + mock.call( + test_cli20.MyUrlComparator( + test_cli20.end_url(path, "fields=id&name=" + _id), + self.client), + 'GET', + body=None, + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN}))]) self.assertEqual(_id, returned_id) def test_get_id_from_name(self): @@ -89,19 +92,21 @@ def test_get_id_from_name(self): _id = uuidutils.generate_uuid() reses = {'networks': [{'id': _id, }, ], } resstr = self.client.serialize(reses) - self.mox.StubOutWithMock(self.client.httpclient, "request") + resp = (test_cli20.MyResp(200), resstr) path = getattr(self.client, "networks_path") - self.client.httpclient.request( + with mock.patch.object(self.client.httpclient, "request", + return_value=resp) as mock_request: + returned_id = neutronV20.find_resourceid_by_name_or_id( + self.client, 'network', name) + + mock_request.assert_called_once_with( test_cli20.MyUrlComparator( test_cli20.end_url(path, "fields=id&name=" + name), self.client), 'GET', body=None, - headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) - ).AndReturn((test_cli20.MyResp(200), resstr)) - self.mox.ReplayAll() - returned_id = neutronV20.find_resourceid_by_name_or_id( - self.client, 'network', name) + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN})) self.assertEqual(_id, returned_id) def test_get_id_from_name_multiple(self): @@ -109,40 +114,46 @@ def test_get_id_from_name_multiple(self): reses = {'networks': [{'id': uuidutils.generate_uuid()}, {'id': uuidutils.generate_uuid()}]} resstr = self.client.serialize(reses) - self.mox.StubOutWithMock(self.client.httpclient, "request") + resp = (test_cli20.MyResp(200), resstr) path = getattr(self.client, "networks_path") - self.client.httpclient.request( + with mock.patch.object(self.client.httpclient, "request", + return_value=resp) as mock_request: + exception = self.assertRaises( + exceptions.NeutronClientNoUniqueMatch, + neutronV20.find_resourceid_by_name_or_id, + self.client, 'network', name) + + mock_request.assert_called_once_with( test_cli20.MyUrlComparator( test_cli20.end_url(path, "fields=id&name=" + name), self.client), 'GET', body=None, - headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) - ).AndReturn((test_cli20.MyResp(200), resstr)) - self.mox.ReplayAll() - exception = self.assertRaises(exceptions.NeutronClientNoUniqueMatch, - neutronV20.find_resourceid_by_name_or_id, - self.client, 'network', name) + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN})) self.assertIn('Multiple', exception.message) def test_get_id_from_name_notfound(self): name = 'myname' reses = {'networks': []} resstr = self.client.serialize(reses) - self.mox.StubOutWithMock(self.client.httpclient, "request") + resp = (test_cli20.MyResp(200), resstr) path = getattr(self.client, "networks_path") - self.client.httpclient.request( + with mock.patch.object(self.client.httpclient, "request", + return_value=resp) as mock_request: + exception = self.assertRaises( + exceptions.NotFound, + neutronV20.find_resourceid_by_name_or_id, + self.client, 'network', name) + + mock_request.assert_called_once_with( test_cli20.MyUrlComparator( test_cli20.end_url(path, "fields=id&name=" + name), self.client), 'GET', body=None, - headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) - ).AndReturn((test_cli20.MyResp(200), resstr)) - self.mox.ReplayAll() - exception = self.assertRaises(exceptions.NotFound, - neutronV20.find_resourceid_by_name_or_id, - self.client, 'network', name) + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN})) self.assertIn('Unable to find', exception.message) self.assertEqual(404, exception.status_code) @@ -153,41 +164,44 @@ def test_get_id_from_name_multiple_with_project(self): reses = {'security_groups': [{'id': expect_id, 'tenant_id': project}]} resstr = self.client.serialize(reses) - self.mox.StubOutWithMock(self.client.httpclient, "request") + resp = (test_cli20.MyResp(200), resstr) path = getattr(self.client, "security_groups_path") - self.client.httpclient.request( + with mock.patch.object(self.client.httpclient, "request", + return_value=resp) as mock_request: + observed_id = neutronV20.find_resourceid_by_name_or_id( + self.client, 'security_group', name, project) + + mock_request.assert_called_once_with( test_cli20.MyUrlComparator( test_cli20.end_url(path, "fields=id&name=%s&tenant_id=%s" % (name, project)), self.client), 'GET', body=None, - headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) - ).AndReturn((test_cli20.MyResp(200), resstr)) - self.mox.ReplayAll() - - observed_id = neutronV20.find_resourceid_by_name_or_id( - self.client, 'security_group', name, project) - + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN})) self.assertEqual(expect_id, observed_id) def test_get_id_from_name_multiple_with_project_not_found(self): name = 'web_server' project = uuidutils.generate_uuid() resstr_notfound = self.client.serialize({'security_groups': []}) - self.mox.StubOutWithMock(self.client.httpclient, "request") + resp = (test_cli20.MyResp(200), resstr_notfound) path = getattr(self.client, "security_groups_path") - self.client.httpclient.request( + with mock.patch.object(self.client.httpclient, "request", + return_value=resp) as mock_request: + exc = self.assertRaises(exceptions.NotFound, + neutronV20.find_resourceid_by_name_or_id, + self.client, 'security_group', name, + project) + + mock_request.assert_called_once_with( test_cli20.MyUrlComparator( test_cli20.end_url(path, "fields=id&name=%s&tenant_id=%s" % (name, project)), self.client), 'GET', body=None, - headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) - ).AndReturn((test_cli20.MyResp(200), resstr_notfound)) - self.mox.ReplayAll() - exc = self.assertRaises(exceptions.NotFound, - neutronV20.find_resourceid_by_name_or_id, - self.client, 'security_group', name, project) + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN})) self.assertIn('Unable to find', exc.message) self.assertEqual(404, exc.status_code) @@ -196,29 +210,31 @@ def _test_get_resource_by_id(self, id_only=False): net = {'id': _id, 'name': 'test'} reses = {'networks': [net], } resstr = self.client.serialize(reses) - self.mox.StubOutWithMock(self.client.httpclient, "request") + resp = (test_cli20.MyResp(200), resstr) path = getattr(self.client, "networks_path") if id_only: query_params = "fields=id&id=%s" % _id else: query_params = "id=%s" % _id - self.client.httpclient.request( + with mock.patch.object(self.client.httpclient, "request", + return_value=resp) as mock_request: + if id_only: + returned_id = neutronV20.find_resourceid_by_id( + self.client, 'network', _id) + self.assertEqual(_id, returned_id) + else: + result = neutronV20.find_resource_by_id( + self.client, 'network', _id) + self.assertEqual(net, result) + + mock_request.assert_called_once_with( test_cli20.MyUrlComparator( test_cli20.end_url(path, query_params), self.client), 'GET', body=None, - headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) - ).AndReturn((test_cli20.MyResp(200), resstr)) - self.mox.ReplayAll() - if id_only: - returned_id = neutronV20.find_resourceid_by_id( - self.client, 'network', _id) - self.assertEqual(_id, returned_id) - else: - result = neutronV20.find_resource_by_id( - self.client, 'network', _id) - self.assertEqual(net, result) + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN})) def test_get_resource_by_id(self): self._test_get_resource_by_id(id_only=False) From bccf489605c5c106483eb4bc005139a7140870f5 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Thu, 15 Mar 2018 08:00:48 +0000 Subject: [PATCH 656/845] Updated from global requirements Change-Id: Ib897d23991b49d7efe68876f51b095c36fb91958 --- test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index d7d7efe7c..069f4d5fd 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -15,7 +15,7 @@ python-openstackclient>=3.12.0 # Apache-2.0 python-subunit>=1.0.0 # Apache-2.0/BSD reno>=2.5.0 # Apache-2.0 requests-mock>=1.1.0 # Apache-2.0 -sphinx!=1.6.6,<1.7.0,>=1.6.2 # BSD +sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD testrepository>=0.0.18 # Apache-2.0/BSD testtools>=2.2.0 # MIT testscenarios>=0.4 # Apache-2.0/BSD From c50b1267710fdc0585a8618c6f9513bd0f3a6161 Mon Sep 17 00:00:00 2001 From: Hongbin Lu Date: Fri, 9 Mar 2018 00:16:46 +0000 Subject: [PATCH 657/845] Remove mox/mox3 usage from test_cli20_network.py Change-Id: I3c9f53e57ef7fc38b0d4b2eb3138902004cff2b3 Partial-Bug: #1753504 --- neutronclient/tests/unit/test_cli20.py | 14 + .../tests/unit/test_cli20_network.py | 304 ++++++++++-------- 2 files changed, 183 insertions(+), 135 deletions(-) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 68f75d0d0..daf4a812d 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -196,6 +196,20 @@ def __repr__(self): (self.__class__.__name__, self._expected)) +class IsA(object): + """Checks whether the parameter is of specific type.""" + + def __init__(self, expected_type): + self._expected_type = expected_type + + def __eq__(self, other): + return isinstance(other, self._expected_type) + + def __repr__(self): + return ('<%s (expected: %s)>' % + (self.__class__.__name__, self._expected_type)) + + class CLITestV20Base(base.BaseTestCase): test_id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index 8333b753c..230c5d49e 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -16,7 +16,7 @@ import itertools import sys -from mox3 import mox +import mock from oslo_serialization import jsonutils from neutronclient.common import exceptions @@ -159,31 +159,32 @@ def setUp(self): def test_list_nets_empty_with_column(self): resources = "networks" cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - self.mox.StubOutWithMock(network.ListNetwork, "extend_list") - network.ListNetwork.extend_list(mox.IsA(list), mox.IgnoreArg()) - cmd.get_client().MultipleTimes().AndReturn(self.client) reses = {resources: []} resstr = self.client.serialize(reses) + resp = (test_cli20.MyResp(200), resstr) # url method body query = "id=myfakeid" args = ['-c', 'id', '--', '--id', 'myfakeid'] path = getattr(self.client, resources + "_path") - self.client.httpclient.request( + with mock.patch.object(cmd, "get_client", + return_value=self.client) as mock_get_client, \ + mock.patch.object(self.client.httpclient, "request", + return_value=resp) as mock_request, \ + mock.patch.object(network.ListNetwork, "extend_list", + return_value=None) as mock_extend_list: + cmd_parser = cmd.get_parser("list_" + resources) + shell.run_command(cmd, cmd_parser, args) + + mock_get_client.assert_called_once_with() + mock_request.assert_called_once_with( test_cli20.MyUrlComparator(test_cli20.end_url(path, query), self.client), 'GET', body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', - test_cli20.TOKEN)).AndReturn( - (test_cli20.MyResp(200), resstr)) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser("list_" + resources) - shell.run_command(cmd, cmd_parser, args) - self.mox.VerifyAll() - self.mox.UnsetStubs() + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN})) + mock_extend_list.assert_called_once_with(test_cli20.IsA(list), + mock.ANY) _str = self.fake_stdout.make_string() self.assertEqual('\n', _str) @@ -192,18 +193,22 @@ def _test_list_networks(self, cmd, detail=False, tags=(), sort_key=(), sort_dir=(), base_args=None, query=''): resources = "networks" - self.mox.StubOutWithMock(network.ListNetwork, "extend_list") - network.ListNetwork.extend_list(mox.IsA(list), mox.IgnoreArg()) - self._test_list_resources(resources, cmd, detail, tags, - fields_1, fields_2, page_size=page_size, - sort_key=sort_key, sort_dir=sort_dir, - base_args=base_args, query=query) + with mock.patch.object(network.ListNetwork, "extend_list", + return_value=None) as mock_extend_list: + self._test_list_resources(resources, cmd, detail, tags, + fields_1, fields_2, page_size=page_size, + sort_key=sort_key, sort_dir=sort_dir, + base_args=base_args, query=query) + mock_extend_list.assert_called_once_with(test_cli20.IsA(list), + mock.ANY) def test_list_nets_pagination(self): cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - self.mox.StubOutWithMock(network.ListNetwork, "extend_list") - network.ListNetwork.extend_list(mox.IsA(list), mox.IgnoreArg()) - self._test_list_resources_with_pagination("networks", cmd) + with mock.patch.object(network.ListNetwork, "extend_list", + return_value=None) as mock_extend_list: + self._test_list_resources_with_pagination("networks", cmd) + mock_extend_list.assert_called_once_with(test_cli20.IsA(list), + mock.ANY) def test_list_nets_sort(self): # list nets: @@ -250,41 +255,51 @@ def test_list_nets_detail_tags(self): self._test_list_networks(cmd, detail=True, tags=['a', 'b']) def _test_list_nets_extend_subnets(self, data, expected): - def setup_list_stub(resources, data, query): - reses = {resources: data} - resstr = self.client.serialize(reses) - resp = (test_cli20.MyResp(200), resstr) - path = getattr(self.client, resources + '_path') - self.client.httpclient.request( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, query), self.client), - 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(resp) - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - self.mox.StubOutWithMock(cmd, 'get_client') - self.mox.StubOutWithMock(self.client.httpclient, 'request') - cmd.get_client().MultipleTimes().AndReturn(self.client) - setup_list_stub('networks', data, '') + nets_path = getattr(self.client, 'networks_path') + subnets_path = getattr(self.client, 'subnets_path') + nets_query = '' filters = '' for n in data: for s in n['subnets']: filters = filters + "&id=%s" % s - setup_list_stub('subnets', - [{'id': 'mysubid1', 'cidr': '192.168.1.0/24'}, + subnets_query = 'fields=id&fields=cidr' + filters + with mock.patch.object(cmd, 'get_client', + return_value=self.client) as mock_get_client, \ + mock.patch.object(self.client.httpclient, + "request") as mock_request: + resp1 = (test_cli20.MyResp(200), + self.client.serialize({'networks': data})) + resp2 = (test_cli20.MyResp(200), + self.client.serialize({'subnets': [ + {'id': 'mysubid1', 'cidr': '192.168.1.0/24'}, {'id': 'mysubid2', 'cidr': '172.16.0.0/24'}, - {'id': 'mysubid3', 'cidr': '10.1.1.0/24'}], - query='fields=id&fields=cidr' + filters) - self.mox.ReplayAll() - - args = [] - cmd_parser = cmd.get_parser('list_networks') - parsed_args = cmd_parser.parse_args(args) - result = cmd.take_action(parsed_args) - self.mox.VerifyAll() - self.mox.UnsetStubs() + {'id': 'mysubid3', 'cidr': '10.1.1.0/24'}]})) + mock_request.side_effect = [resp1, resp2] + args = [] + cmd_parser = cmd.get_parser('list_networks') + parsed_args = cmd_parser.parse_args(args) + result = cmd.take_action(parsed_args) + + mock_get_client.assert_called_with() + self.assertEqual(2, mock_request.call_count) + mock_request.assert_has_calls([ + mock.call( + test_cli20.MyUrlComparator( + test_cli20.end_url(nets_path, nets_query), + self.client), + 'GET', + body=None, + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN})), + mock.call( + test_cli20.MyUrlComparator( + test_cli20.end_url(subnets_path, subnets_query), + self.client), + 'GET', + body=None, + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN}))]) _result = [x for x in result[1]] self.assertEqual(len(expected), len(_result)) for res, exp in zip(_result, expected): @@ -319,9 +334,11 @@ def test_list_nets_fields(self): def _test_list_nets_columns(self, cmd, returned_body, args=('-f', 'json')): resources = 'networks' - self.mox.StubOutWithMock(network.ListNetwork, "extend_list") - network.ListNetwork.extend_list(mox.IsA(list), mox.IgnoreArg()) - self._test_list_columns(cmd, resources, returned_body, args=args) + with mock.patch.object(network.ListNetwork, "extend_list", + return_value=None) as mock_extend_list: + self._test_list_columns(cmd, resources, returned_body, args=args) + mock_extend_list.assert_called_once_with(test_cli20.IsA(list), + mock.ANY) def test_list_nets_defined_column(self): cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) @@ -355,46 +372,43 @@ def test_list_nets_with_default_column(self): def test_list_external_nets_empty_with_column(self): resources = "networks" cmd = network.ListExternalNetwork(test_cli20.MyApp(sys.stdout), None) - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - self.mox.StubOutWithMock(network.ListNetwork, "extend_list") - network.ListNetwork.extend_list(mox.IsA(list), mox.IgnoreArg()) - cmd.get_client().MultipleTimes().AndReturn(self.client) reses = {resources: []} resstr = self.client.serialize(reses) # url method body query = "router%3Aexternal=True&id=myfakeid" args = ['-c', 'id', '--', '--id', 'myfakeid'] path = getattr(self.client, resources + "_path") - self.client.httpclient.request( + resp = (test_cli20.MyResp(200), resstr) + with mock.patch.object(cmd, "get_client", + return_value=self.client) as mock_get_client, \ + mock.patch.object(self.client.httpclient, "request", + return_value=resp) as mock_request, \ + mock.patch.object(network.ListNetwork, "extend_list", + return_value=None) as mock_extend_list: + cmd_parser = cmd.get_parser("list_" + resources) + shell.run_command(cmd, cmd_parser, args) + + mock_get_client.assert_called_once_with() + mock_request.assert_called_once_with( test_cli20.MyUrlComparator( test_cli20.end_url(path, query), self.client), 'GET', body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', - test_cli20.TOKEN)).AndReturn( - (test_cli20.MyResp(200), resstr)) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser("list_" + resources) - shell.run_command(cmd, cmd_parser, args) - self.mox.VerifyAll() - self.mox.UnsetStubs() + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN})) + mock_extend_list.assert_called_once_with(test_cli20.IsA(list), + mock.ANY) _str = self.fake_stdout.make_string() self.assertEqual('\n', _str) def _test_list_external_nets(self, resources, cmd, detail=False, tags=(), fields_1=(), fields_2=()): - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - self.mox.StubOutWithMock(network.ListNetwork, "extend_list") - network.ListNetwork.extend_list(mox.IsA(list), mox.IgnoreArg()) - cmd.get_client().MultipleTimes().AndReturn(self.client) reses = {resources: [{'id': 'myid1', }, {'id': 'myid2', }, ], } resstr = self.client.serialize(reses) + resp = (test_cli20.MyResp(200), resstr) # url method body query = "" @@ -432,18 +446,25 @@ def _test_list_external_nets(self, resources, cmd, query = query and query + '&verbose=True' or 'verbose=True' path = getattr(self.client, resources + "_path") - self.client.httpclient.request( + with mock.patch.object(cmd, "get_client", + return_value=self.client) as mock_get_client, \ + mock.patch.object(self.client.httpclient, "request", + return_value=resp) as mock_request, \ + mock.patch.object(network.ListNetwork, "extend_list", + return_value=None) as mock_extend_list: + cmd_parser = cmd.get_parser("list_" + resources) + shell.run_command(cmd, cmd_parser, args) + + mock_get_client.assert_called_once_with() + mock_request.assert_called_once_with( test_cli20.MyUrlComparator( test_cli20.end_url(path, query), self.client), 'GET', body=None, - headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) - ).AndReturn((test_cli20.MyResp(200), resstr)) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser("list_" + resources) - shell.run_command(cmd, cmd_parser, args) - self.mox.VerifyAll() - self.mox.UnsetStubs() + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN})) + mock_extend_list.assert_called_once_with(test_cli20.IsA(list), + mock.ANY) _str = self.fake_stdout.make_string() self.assertIn('myid1', _str) @@ -589,21 +610,6 @@ def test_bulk_delete_network_fail(self): class CLITestV20ExtendListNetworkJSON(test_cli20.CLITestV20Base): - def _test_extend_list(self, mox_calls): - data = [{'id': 'netid%d' % i, 'name': 'net%d' % i, - 'subnets': ['mysubid%d' % i]} - for i in range(10)] - self.mox.StubOutWithMock(self.client.httpclient, "request") - path = getattr(self.client, 'subnets_path') - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - self.mox.StubOutWithMock(cmd, "get_client") - cmd.get_client().MultipleTimes().AndReturn(self.client) - mox_calls(path, data) - self.mox.ReplayAll() - known_args, _vs = cmd.get_parser('create_subnets').parse_known_args() - cmd.extend_list(data, known_args) - self.mox.VerifyAll() - def _build_test_data(self, data): subnet_ids = [] response = [] @@ -620,41 +626,69 @@ def _build_test_data(self, data): return filters, resp def test_extend_list(self): - def mox_calls(path, data): - filters, response = self._build_test_data(data) - self.client.httpclient.request( - test_cli20.MyUrlComparator(test_cli20.end_url( - path, 'fields=id&fields=cidr' + filters), self.client), - 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(response) - - self._test_extend_list(mox_calls) + data = [{'id': 'netid%d' % i, 'name': 'net%d' % i, + 'subnets': ['mysubid%d' % i]} + for i in range(10)] + filters, response = self._build_test_data(data) + path = getattr(self.client, 'subnets_path') + cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) + with mock.patch.object(cmd, "get_client", + return_value=self.client) as mock_get_client, \ + mock.patch.object(self.client.httpclient, "request", + return_value=response) as mock_request: + known_args, _vs = cmd.get_parser('create_subnets')\ + .parse_known_args() + cmd.extend_list(data, known_args) + + mock_get_client.assert_called_once_with() + mock_request.assert_called_once_with( + test_cli20.MyUrlComparator(test_cli20.end_url( + path, 'fields=id&fields=cidr' + filters), self.client), + 'GET', + body=None, + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN})) def test_extend_list_exceed_max_uri_len(self): - def mox_calls(path, data): - sub_data_lists = [data[:len(data) - 1], data[len(data) - 1:]] - filters, response = self._build_test_data(data) - + data = [{'id': 'netid%d' % i, 'name': 'net%d' % i, + 'subnets': ['mysubid%d' % i]} + for i in range(10)] + filters1, response1 = self._build_test_data(data[:len(data) - 1]) + filters2, response2 = self._build_test_data(data[len(data) - 1:]) + path = getattr(self.client, 'subnets_path') + cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) + with mock.patch.object(cmd, "get_client", + return_value=self.client) as mock_get_client, \ + mock.patch.object(self.client.httpclient, + "request") as mock_request, \ + mock.patch.object(self.client.httpclient, "_check_uri_length", + return_value=None) as mock_check_uri_length: # 1 char of extra URI len will cause a split in 2 requests - self.mox.StubOutWithMock(self.client.httpclient, - "_check_uri_length") - self.client.httpclient._check_uri_length(mox.IgnoreArg()).AndRaise( - exceptions.RequestURITooLong(excess=1)) - - for data in sub_data_lists: - filters, response = self._build_test_data(data) - self.client.httpclient._check_uri_length( - mox.IgnoreArg()).AndReturn(None) - self.client.httpclient.request( - test_cli20.MyUrlComparator( - test_cli20.end_url( - path, 'fields=id&fields=cidr%s' % filters), - self.client), - 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(response) - - self._test_extend_list(mox_calls) + mock_check_uri_length.side_effect = [ + exceptions.RequestURITooLong(excess=1), None, None] + mock_request.side_effect = [response1, response2] + known_args, _vs = cmd.get_parser('create_subnets')\ + .parse_known_args() + cmd.extend_list(data, known_args) + + mock_get_client.assert_called_once_with() + self.assertEqual(2, mock_request.call_count) + mock_request.assert_has_calls([ + mock.call( + test_cli20.MyUrlComparator( + test_cli20.end_url( + path, 'fields=id&fields=cidr%s' % filters1), + self.client), + 'GET', + body=None, + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN})), + mock.call( + test_cli20.MyUrlComparator( + test_cli20.end_url( + path, 'fields=id&fields=cidr%s' % filters2), + self.client), + 'GET', + body=None, + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN}))]) From 93e4ef2e7de95aa16e13331df14b2d157c9a24a5 Mon Sep 17 00:00:00 2001 From: Hongbin Lu Date: Thu, 15 Mar 2018 17:58:49 +0000 Subject: [PATCH 658/845] Remove mox/mox3 usage from test_cli20_subnetpool.py Change-Id: I26e15e051a9754f61d9ba04a59f62bc979e52fb1 Partial-Bug: #1753504 --- neutronclient/tests/unit/test_cli20_subnetpool.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/neutronclient/tests/unit/test_cli20_subnetpool.py b/neutronclient/tests/unit/test_cli20_subnetpool.py index 343393e5b..89a89b244 100644 --- a/neutronclient/tests/unit/test_cli20_subnetpool.py +++ b/neutronclient/tests/unit/test_cli20_subnetpool.py @@ -16,7 +16,7 @@ import sys -from mox3 import mox +import mock from neutronclient.common import exceptions from neutronclient.neutron.v2_0 import subnetpool @@ -135,13 +135,12 @@ def test_create_subnetpool_no_poolprefix(self): cmd, name, myid, args, position_names, position_values) - def test_list_subnetpool_pagination(self): + @mock.patch.object(subnetpool.ListSubnetPool, "extend_list") + def test_list_subnetpool_pagination(self, mock_extend_list): cmd = subnetpool.ListSubnetPool(test_cli20.MyApp(sys.stdout), None) - self.mox.StubOutWithMock(subnetpool.ListSubnetPool, "extend_list") - subnetpool.ListSubnetPool.extend_list(mox.IsA(list), mox.IgnoreArg()) self._test_list_resources_with_pagination("subnetpools", cmd) - self.mox.VerifyAll() - self.mox.UnsetStubs() + mock_extend_list.assert_called_once_with(test_cli20.IsA(list), + mock.ANY) def test_list_subnetpools_sort(self): # List subnetpools: From 02b6e6f841dcc262fb42e88d5ead06025edb0190 Mon Sep 17 00:00:00 2001 From: Hongbin Lu Date: Thu, 15 Mar 2018 18:16:21 +0000 Subject: [PATCH 659/845] Remove mox/mox3 usage from test_cli20_subnet.py Change-Id: I783260f6853a7ba6f911f718ce759b292595da4f Partial-Bug: #1753504 --- neutronclient/tests/unit/test_cli20_subnet.py | 55 +++++++++---------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/neutronclient/tests/unit/test_cli20_subnet.py b/neutronclient/tests/unit/test_cli20_subnet.py index 33b5777e1..ab37cbdd7 100644 --- a/neutronclient/tests/unit/test_cli20_subnet.py +++ b/neutronclient/tests/unit/test_cli20_subnet.py @@ -16,7 +16,7 @@ import sys -from mox3 import mox +import mock from neutronclient.common import exceptions from neutronclient.neutron import v2_0 as neutronV20 @@ -418,12 +418,11 @@ def test_create_subnet_max_v4_cidr(self): args = ['--gateway', gateway, netid, cidr] position_names = ['ip_version', 'network_id', 'cidr', 'gateway_ip'] position_values = [4, netid, cidr, gateway] - self.mox.StubOutWithMock(cmd.log, 'warning') - cmd.log.warning(mox.IgnoreArg(), {'ip': 4, 'cidr': '/32'}) - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - self.mox.VerifyAll() - self.mox.UnsetStubs() + with mock.patch.object(cmd.log, 'warning') as mock_warning: + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + mock_warning.assert_called_once_with(mock.ANY, + {'ip': 4, 'cidr': '/32'}) def test_create_subnet_with_ipv6_ra_mode(self): resource = 'subnet' @@ -520,7 +519,9 @@ def test_create_subnet_with_ipv6_address_mode_ipv4(self): position_values, tenant_id='tenantid', no_api_call=True, expected_exception=exceptions.CommandError) - def test_create_subnet_with_subnetpool_ipv6_and_ip_ver_ignored(self): + @mock.patch.object(neutronV20, 'find_resource_by_name_or_id') + def test_create_subnet_with_subnetpool_ipv6_and_ip_ver_ignored( + self, mock_find_resource): resource = 'subnet' cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -532,17 +533,17 @@ def test_create_subnet_with_subnetpool_ipv6_and_ip_ver_ignored(self): netid] position_names = ['ip_version', 'network_id', 'subnetpool_id'] position_values = [6, netid, 'subnetpool_id'] - self.mox.StubOutWithMock(neutronV20, 'find_resource_by_name_or_id') - neutronV20.find_resource_by_name_or_id( - self.client, - 'subnetpool', - 'subnetpool_id').AndReturn({'id': 'subnetpool_id', - 'ip_version': 6}) + mock_find_resource.return_value = { + 'id': 'subnetpool_id', 'ip_version': 6} self._test_create_resource( resource, cmd, name, myid, args, position_names, position_values, tenant_id='tenantid') + mock_find_resource.assert_called_once_with( + self.client, 'subnetpool', 'subnetpool_id') - def test_create_subnet_with_subnetpool_ipv4_with_cidr_wildcard(self): + @mock.patch.object(neutronV20, 'find_resource_by_name_or_id') + def test_create_subnet_with_subnetpool_ipv4_with_cidr_wildcard( + self, mock_find_resource): resource = 'subnet' cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -557,18 +558,18 @@ def test_create_subnet_with_subnetpool_ipv4_with_cidr_wildcard(self): position_names = ['ip_version', 'ipv6_address_mode', 'network_id', 'subnetpool_id', 'cidr'] position_values = [4, None, netid, 'subnetpool_id', cidr] - self.mox.StubOutWithMock(neutronV20, 'find_resource_by_name_or_id') - neutronV20.find_resource_by_name_or_id( - self.client, - 'subnetpool', - 'subnetpool_id').AndReturn({'id': 'subnetpool_id', - 'ip_version': 4}) + mock_find_resource.return_value = {'id': 'subnetpool_id', + 'ip_version': 4} self._test_create_resource( resource, cmd, name, myid, args, position_names, position_values, tenant_id='tenantid', no_api_call=True, expected_exception=exceptions.CommandError) + mock_find_resource.assert_called_once_with( + self.client, 'subnetpool', 'subnetpool_id') - def test_create_subnet_with_subnetpool_ipv4_with_prefixlen(self): + @mock.patch.object(neutronV20, 'find_resource_by_name_or_id') + def test_create_subnet_with_subnetpool_ipv4_with_prefixlen( + self, mock_find_resource): resource = 'subnet' cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) name = 'myname' @@ -583,16 +584,14 @@ def test_create_subnet_with_subnetpool_ipv4_with_prefixlen(self): position_names = ['ip_version', 'ipv6_address_mode', 'network_id', 'subnetpool_id'] position_values = [4, None, netid, 'subnetpool_id'] - self.mox.StubOutWithMock(neutronV20, 'find_resource_by_name_or_id') - neutronV20.find_resource_by_name_or_id( - self.client, - 'subnetpool', - 'subnetpool_id').AndReturn({'id': 'subnetpool_id', - 'ip_version': 4}) + mock_find_resource.return_value = {'id': 'subnetpool_id', + 'ip_version': 4} self._test_create_resource( resource, cmd, name, myid, args, position_names, position_values, tenant_id='tenantid', no_api_call=True, expected_exception=exceptions.CommandError) + mock_find_resource.assert_called_once_with( + self.client, 'subnetpool', 'subnetpool_id') def test_list_subnets_detail(self): # List subnets: -D. From 5a0d8c8b907f4ebbe5a1d7f8b0704ecdd7170a54 Mon Sep 17 00:00:00 2001 From: Hongbin Lu Date: Thu, 15 Mar 2018 20:44:13 +0000 Subject: [PATCH 660/845] Remove mox/mox3 usage from test_cli20_securitygroup.py Change-Id: I7d15d42ffd0cd94972809569633d6f4a38c60d4b Partial-Bug: #1753504 --- neutronclient/tests/unit/test_cli20.py | 12 +- neutronclient/tests/unit/test_cli20_port.py | 4 +- .../tests/unit/test_cli20_securitygroup.py | 193 ++++++++++-------- 3 files changed, 116 insertions(+), 93 deletions(-) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index daf4a812d..9878e98e4 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -603,10 +603,14 @@ def _test_update_resource_action(self, resource, cmd, myid, action, args, _str = self.fake_stdout.make_string() self.assertIn(myid, _str) - def _assert_mock_multiple_calls_with_same_arguments( - self, mocked_method, count, expected_call): - self.assertEqual(count, mocked_method.call_count) - mocked_method.assert_has_calls([expected_call] * count) + def assert_mock_multiple_calls_with_same_arguments( + self, mocked_method, expected_call, count): + if count is None: + self.assertLessEqual(1, mocked_method.call_count) + else: + self.assertEqual(count, mocked_method.call_count) + mocked_method.assert_has_calls( + [expected_call] * mocked_method.call_count) class TestListCommand(neutronV2_0.ListCommand): diff --git a/neutronclient/tests/unit/test_cli20_port.py b/neutronclient/tests/unit/test_cli20_port.py index b28953a53..f6fc61783 100644 --- a/neutronclient/tests/unit/test_cli20_port.py +++ b/neutronclient/tests/unit/test_cli20_port.py @@ -507,8 +507,8 @@ def _test_list_router_port(self, resources, cmd, cmd_parser = cmd.get_parser("list_" + resources) shell.run_command(cmd, cmd_parser, args) - self._assert_mock_multiple_calls_with_same_arguments( - mock_get_client, 2, mock.call()) + self.assert_mock_multiple_calls_with_same_arguments( + mock_get_client, mock.call(), 2) mock_request.assert_called_once_with( test_cli20.MyUrlComparator( test_cli20.end_url(path, query % myid), self.client), diff --git a/neutronclient/tests/unit/test_cli20_securitygroup.py b/neutronclient/tests/unit/test_cli20_securitygroup.py index 5e6f01978..0f5a240cf 100644 --- a/neutronclient/tests/unit/test_cli20_securitygroup.py +++ b/neutronclient/tests/unit/test_cli20_securitygroup.py @@ -16,7 +16,7 @@ import sys -from mox3 import mox +import mock from oslo_utils import uuidutils import six @@ -211,34 +211,14 @@ def test_delete_security_group_rule(self): args = [myid] self._test_delete_resource(resource, cmd, myid, args) - def test_list_security_group_rules(self): + @mock.patch.object(securitygroup.ListSecurityGroupRule, "extend_list") + def test_list_security_group_rules(self, mock_extend_list): resources = "security_group_rules" cmd = securitygroup.ListSecurityGroupRule( test_cli20.MyApp(sys.stdout), None) - self.mox.StubOutWithMock(securitygroup.ListSecurityGroupRule, - "extend_list") - securitygroup.ListSecurityGroupRule.extend_list(mox.IsA(list), - mox.IgnoreArg()) self._test_list_resources(resources, cmd, True) - - def _test_extend_list(self, mox_calls, data): - resources = "security_groups" - - cmd = securitygroup.ListSecurityGroupRule( - test_cli20.MyApp(sys.stdout), None) - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - - cmd.get_client().MultipleTimes().AndReturn(self.client) - path = getattr(self.client, resources + '_path') - mox_calls(path, data) - self.mox.ReplayAll() - known_args, _vs = cmd.get_parser( - 'list' + resources).parse_known_args() - - cmd.extend_list(data, known_args) - self.mox.VerifyAll() - self.mox.UnsetStubs() + mock_extend_list.assert_called_once_with(test_cli20.IsA(list), + mock.ANY) def _build_test_data(self, data, excess=0): # Length of a query filter on security group rule id @@ -277,44 +257,37 @@ def _build_test_data(self, data, excess=0): return result def test_extend_list(self): - def mox_calls(path, data): - responses = self._build_test_data(data) - self.client.httpclient.request( - test_cli20.MyUrlComparator(test_cli20.end_url( - path, responses[0]['filter']), self.client), - 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn( - responses[0]['response']) - data = [{'name': 'default', 'remote_group_id': 'remgroupid%02d' % i} for i in range(10)] data.append({'name': 'default', 'remote_group_id': None}) - self._test_extend_list(mox_calls, data) + resources = "security_groups" - def test_extend_list_exceed_max_uri_len(self): - def mox_calls(path, data): - # 1 char of extra URI len will cause a split in 2 requests - self.mox.StubOutWithMock(self.client.httpclient, - '_check_uri_length') - self.client.httpclient._check_uri_length(mox.IgnoreArg()).AndRaise( - exceptions.RequestURITooLong(excess=1)) - responses = self._build_test_data(data, excess=1) - - for item in responses: - self.client.httpclient._check_uri_length( - mox.IgnoreArg()).AndReturn(None) - self.client.httpclient.request( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, item['filter']), self.client), - 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn( - item['response']) + cmd = securitygroup.ListSecurityGroupRule( + test_cli20.MyApp(sys.stdout), None) + path = getattr(self.client, resources + '_path') + responses = self._build_test_data(data) + known_args, _vs = cmd.get_parser( + 'list' + resources).parse_known_args() + resp = responses[0]['response'] + + with mock.patch.object(cmd, "get_client", + return_value=self.client) as mock_get_client, \ + mock.patch.object(self.client.httpclient, "request", + return_value=resp) as mock_request: + cmd.extend_list(data, known_args) + + mock_get_client.assert_called_once_with() + mock_request.assert_called_once_with( + test_cli20.MyUrlComparator(test_cli20.end_url( + path, responses[0]['filter']), self.client), + 'GET', + body=None, + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN})) + + def test_extend_list_exceed_max_uri_len(self): data = [{'name': 'default', 'security_group_id': 'secgroupid%02d' % i, 'remote_group_id': 'remgroupid%02d' % i} @@ -322,39 +295,75 @@ def mox_calls(path, data): data.append({'name': 'default', 'security_group_id': 'secgroupid10', 'remote_group_id': None}) - self._test_extend_list(mox_calls, data) + resources = "security_groups" + + cmd = securitygroup.ListSecurityGroupRule( + test_cli20.MyApp(sys.stdout), None) + path = getattr(self.client, resources + '_path') + responses = self._build_test_data(data, excess=1) - def test_list_security_group_rules_pagination(self): + known_args, _vs = cmd.get_parser( + 'list' + resources).parse_known_args() + mock_request_side_effects = [] + mock_request_calls = [] + mock_check_uri_side_effects = [exceptions.RequestURITooLong(excess=1)] + mock_check_uri_calls = [mock.call(mock.ANY)] + for item in responses: + mock_request_side_effects.append(item['response']) + mock_request_calls.append(mock.call( + test_cli20.MyUrlComparator( + test_cli20.end_url(path, item['filter']), self.client), + 'GET', + body=None, + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN}))) + mock_check_uri_side_effects.append(None) + mock_check_uri_calls.append(mock.call(mock.ANY)) + + with mock.patch.object(cmd, "get_client", + return_value=self.client) as mock_get_client, \ + mock.patch.object(self.client.httpclient, + "request") as mock_request, \ + mock.patch.object(self.client.httpclient, + "_check_uri_length") as mock_check_uri: + mock_request.side_effect = mock_request_side_effects + mock_check_uri.side_effect = mock_check_uri_side_effects + cmd.extend_list(data, known_args) + + mock_get_client.assert_called_once_with() + mock_request.assert_has_calls(mock_request_calls) + mock_check_uri.assert_has_calls(mock_check_uri_calls) + self.assertEqual(len(mock_request_calls), mock_request.call_count) + self.assertEqual(len(mock_check_uri_calls), mock_check_uri.call_count) + + @mock.patch.object(securitygroup.ListSecurityGroupRule, "extend_list") + def test_list_security_group_rules_pagination(self, mock_extend_list): resources = "security_group_rules" cmd = securitygroup.ListSecurityGroupRule( test_cli20.MyApp(sys.stdout), None) - self.mox.StubOutWithMock(securitygroup.ListSecurityGroupRule, - "extend_list") - securitygroup.ListSecurityGroupRule.extend_list(mox.IsA(list), - mox.IgnoreArg()) self._test_list_resources_with_pagination(resources, cmd) + mock_extend_list.assert_called_once_with(test_cli20.IsA(list), + mock.ANY) - def test_list_security_group_rules_sort(self): + @mock.patch.object(securitygroup.ListSecurityGroupRule, "extend_list") + def test_list_security_group_rules_sort(self, mock_extend_list): resources = "security_group_rules" cmd = securitygroup.ListSecurityGroupRule( test_cli20.MyApp(sys.stdout), None) - self.mox.StubOutWithMock(securitygroup.ListSecurityGroupRule, - "extend_list") - securitygroup.ListSecurityGroupRule.extend_list(mox.IsA(list), - mox.IgnoreArg()) self._test_list_resources(resources, cmd, sort_key=["name", "id"], sort_dir=["asc", "desc"]) + mock_extend_list.assert_called_once_with(test_cli20.IsA(list), + mock.ANY) - def test_list_security_group_rules_limit(self): + @mock.patch.object(securitygroup.ListSecurityGroupRule, "extend_list") + def test_list_security_group_rules_limit(self, mock_extend_list): resources = "security_group_rules" cmd = securitygroup.ListSecurityGroupRule( test_cli20.MyApp(sys.stdout), None) - self.mox.StubOutWithMock(securitygroup.ListSecurityGroupRule, - "extend_list") - securitygroup.ListSecurityGroupRule.extend_list(mox.IsA(list), - mox.IgnoreArg()) self._test_list_resources(resources, cmd, page_size=1000) + mock_extend_list.assert_called_once_with(test_cli20.IsA(list), + mock.ANY) def test_show_security_group_rule(self): resource = 'security_group_rule' @@ -367,29 +376,30 @@ def test_show_security_group_rule(self): def _test_list_security_group_rules_extend(self, api_data, expected, args=(), conv=True, query_fields=None): - def setup_list_stub(resources, data, query): + def setup_list_stub(resources, data, query, mock_calls, mock_returns): reses = {resources: data} resstr = self.client.serialize(reses) resp = (test_cli20.MyResp(200), resstr) path = getattr(self.client, resources + '_path') - self.client.httpclient.request( + mock_calls.append(mock.call( test_cli20.MyUrlComparator( test_cli20.end_url(path, query), self.client), 'GET', body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(resp) + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN}))) + mock_returns.append(resp) cmd = securitygroup.ListSecurityGroupRule( test_cli20.MyApp(sys.stdout), None) - self.mox.StubOutWithMock(cmd, 'get_client') - self.mox.StubOutWithMock(self.client.httpclient, 'request') - cmd.get_client().MultipleTimes().AndReturn(self.client) query = '' if query_fields: query = '&'.join(['fields=' + f for f in query_fields]) - setup_list_stub('security_group_rules', api_data, query) + mock_request_calls = [] + mock_request_returns = [] + setup_list_stub('security_group_rules', api_data, query, + mock_request_calls, mock_request_returns) if conv: sec_ids = set() for n in api_data: @@ -403,15 +413,24 @@ def setup_list_stub(resources, data, query): [{'id': 'myid1', 'name': 'group1'}, {'id': 'myid2', 'name': 'group2'}, {'id': 'myid3', 'name': 'group3'}], - query='fields=id&fields=name' + filters) - self.mox.ReplayAll() + 'fields=id&fields=name' + filters, + mock_request_calls, + mock_request_returns) cmd_parser = cmd.get_parser('list_security_group_rules') parsed_args = cmd_parser.parse_args(args) - result = cmd.take_action(parsed_args) - self.mox.VerifyAll() - self.mox.UnsetStubs() - # Check columns + + with mock.patch.object(cmd, "get_client", + return_value=self.client) as mock_get_client, \ + mock.patch.object(self.client.httpclient, + "request") as mock_request: + mock_request.side_effect = mock_request_returns + result = cmd.take_action(parsed_args) + + self.assert_mock_multiple_calls_with_same_arguments( + mock_get_client, mock.call(), None) + mock_request.assert_has_calls(mock_request_calls) + self.assertEqual(len(mock_request_calls), mock_request.call_count) self.assertEqual(expected['cols'], result[0]) # Check data _result = [x for x in result[1]] From 56143488447292b911cf494baa2523ed51e22975 Mon Sep 17 00:00:00 2001 From: Doug Hellmann Date: Thu, 22 Mar 2018 17:56:57 -0400 Subject: [PATCH 661/845] add lower-constraints job Create a tox environment for running the unit tests against the lower bounds of the dependencies. Create a lower-constraints.txt to be used to enforce the lower bounds in those tests. Add openstack-tox-lower-constraints job to the zuul configuration. See http://lists.openstack.org/pipermail/openstack-dev/2018-March/128352.html for more details. Change-Id: I4616b8e1472a6f95e245abe79faaa5669ac2496a Depends-On: https://review.openstack.org/555034 Signed-off-by: Doug Hellmann --- .zuul.yaml | 7 +++ lower-constraints.txt | 121 ++++++++++++++++++++++++++++++++++++++++++ tox.ini | 7 +++ 3 files changed, 135 insertions(+) create mode 100644 .zuul.yaml create mode 100644 lower-constraints.txt diff --git a/.zuul.yaml b/.zuul.yaml new file mode 100644 index 000000000..67a39c429 --- /dev/null +++ b/.zuul.yaml @@ -0,0 +1,7 @@ +- project: + check: + jobs: + - openstack-tox-lower-constraints + gate: + jobs: + - openstack-tox-lower-constraints diff --git a/lower-constraints.txt b/lower-constraints.txt new file mode 100644 index 000000000..e21d9236e --- /dev/null +++ b/lower-constraints.txt @@ -0,0 +1,121 @@ +alabaster==0.7.10 +amqp==2.1.1 +appdirs==1.3.0 +asn1crypto==0.23.0 +Babel==2.3.4 +cachetools==2.0.0 +cffi==1.7.0 +cliff==2.8.0 +cmd2==0.8.0 +contextlib2==0.4.0 +coverage==4.0 +cryptography==2.1 +debtcollector==1.2.0 +decorator==3.4.0 +deprecation==1.0 +docutils==0.11 +dogpile.cache==0.6.2 +dulwich==0.15.0 +eventlet==0.18.2 +extras==1.0.0 +fasteners==0.7.0 +fixtures==3.0.0 +flake8==2.5.5 +flake8-import-order==0.12 +future==0.16.0 +futurist==1.2.0 +greenlet==0.4.10 +hacking==0.12.0 +idna==2.6 +imagesize==0.7.1 +iso8601==0.1.11 +Jinja2==2.10 +jmespath==0.9.0 +jsonpatch==1.16 +jsonpointer==1.13 +jsonschema==2.6.0 +keystoneauth1==3.4.0 +kombu==4.0.0 +linecache2==1.0.0 +MarkupSafe==1.0 +mccabe==0.2.1 +mock==2.0.0 +monotonic==0.6 +mox3==0.20.0 +msgpack-python==0.4.0 +munch==2.1.0 +netaddr==0.7.18 +netifaces==0.10.4 +openstackdocstheme==1.18.1 +openstacksdk==0.11.2 +os-client-config==1.28.0 +os-service-types==1.2.0 +os-testr==1.0.0 +osc-lib==1.8.0 +oslo.concurrency==3.25.0 +oslo.config==5.2.0 +oslo.context==2.19.2 +oslo.i18n==3.15.3 +oslo.log==3.36.0 +oslo.messaging==5.29.0 +oslo.middleware==3.31.0 +oslo.serialization==2.18.0 +oslo.service==1.24.0 +oslo.utils==3.33.0 +oslotest==3.2.0 +osprofiler==1.4.0 +paramiko==2.0.0 +Paste==2.0.2 +PasteDeploy==1.5.0 +pbr==2.0.0 +pep8==1.5.7 +pika==0.10.0 +pika-pool==0.1.3 +positional==1.2.1 +prettytable==0.7.2 +pyasn1==0.1.8 +pycodestyle==2.3.1 +pycparser==2.18 +pyflakes==0.8.1 +Pygments==2.2.0 +pyinotify==0.9.6 +pyOpenSSL==17.1.0 +pyparsing==2.1.0 +pyperclip==1.5.27 +python-cinderclient==3.3.0 +python-dateutil==2.5.3 +python-glanceclient==2.8.0 +python-keystoneclient==3.8.0 +python-mimeparse==1.6.0 +python-novaclient==9.1.0 +python-openstackclient==3.12.0 +python-subunit==1.0.0 +pytz==2013.6 +PyYAML==3.12 +reno==2.5.0 +repoze.lru==0.7 +requests==2.14.2 +requests-mock==1.1.0 +requestsexceptions==1.2.0 +rfc3986==0.3.1 +Routes==2.3.1 +simplejson==3.5.1 +six==1.10.0 +snowballstemmer==1.2.1 +Sphinx==1.6.5 +sphinxcontrib-websupport==1.0.1 +statsd==3.2.1 +stestr==1.0.0 +stevedore==1.20.0 +tempest==17.1.0 +tenacity==3.2.1 +testrepository==0.0.18 +testscenarios==0.4 +testtools==2.2.0 +traceback2==1.4.0 +unittest2==1.1.0 +urllib3==1.21.1 +vine==1.1.4 +warlock==1.2.0 +WebOb==1.7.1 +wrapt==1.7.0 diff --git a/tox.ini b/tox.ini index 725f4d423..d11d3b96e 100644 --- a/tox.ini +++ b/tox.ini @@ -59,3 +59,10 @@ import-order-style = pep8 # H904: Delay string interpolations at logging calls enable-extensions=H904 + +[testenv:lower-constraints] +basepython = python3 +deps = + -c{toxinidir}/lower-constraints.txt + -r{toxinidir}/test-requirements.txt + -r{toxinidir}/requirements.txt From 05fbf570ed5ea64b002a7b67b004f0b6f9067753 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Fri, 23 Mar 2018 01:49:46 +0000 Subject: [PATCH 662/845] Updated from global requirements Change-Id: Ie636b2253cd263e9f958af430e93fc1eb5efbc23 --- test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index 069f4d5fd..39377181e 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -14,7 +14,7 @@ osprofiler>=1.4.0 # Apache-2.0 python-openstackclient>=3.12.0 # Apache-2.0 python-subunit>=1.0.0 # Apache-2.0/BSD reno>=2.5.0 # Apache-2.0 -requests-mock>=1.1.0 # Apache-2.0 +requests-mock>=1.2.0 # Apache-2.0 sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD testrepository>=0.0.18 # Apache-2.0/BSD testtools>=2.2.0 # MIT From f54ba792c9677c0a7ae41c7fbafc24ab127422de Mon Sep 17 00:00:00 2001 From: Monty Taylor Date: Fri, 23 Mar 2018 09:00:33 -0500 Subject: [PATCH 663/845] Rename python-openstacksdk to openstacksdk Change-Id: If61a78de8b16f62586988f5fdeab2146f67fa3f7 --- doc/source/contributor/transition_to_osc.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/contributor/transition_to_osc.rst b/doc/source/contributor/transition_to_osc.rst index 636f8cb92..65889afa0 100644 --- a/doc/source/contributor/transition_to_osc.rst +++ b/doc/source/contributor/transition_to_osc.rst @@ -27,7 +27,7 @@ This document details the transition roadmap for moving the neutron client's OpenStack Networking API support, both the Python library and the ``neutron`` command-line interface (CLI), to the `OpenStack Client (OSC) `_ -and the `OpenStack Python SDK `_. +and the `OpenStack Python SDK `_. This transition is being guided by the `Deprecate individual CLIs in favour of OSC `_ OpenStack spec. See the `Neutron RFE `_, @@ -184,7 +184,7 @@ is not required as the neutronclient is already deprecated on its own. +-------------------------------------------------+-----------------------------------------------+ | OpenStack Project for ``openstack`` Commands | Python Library to Change | +=================================================+===============================================+ -| python-openstackclient | python-openstacksdk | +| python-openstackclient | openstacksdk | +-------------------------------------------------+-----------------------------------------------+ | python-neutronclient | python-neutronclient | +-------------------------------------------------+-----------------------------------------------+ From bcce41e8478e92c0b9b60c2dd91b826d687264ef Mon Sep 17 00:00:00 2001 From: Hongbin Lu Date: Fri, 16 Mar 2018 18:01:29 +0000 Subject: [PATCH 664/845] Remove mox/mox3 from test_cli20_agentschedulers.py Change-Id: If1b9a9d1d16bed7c28b87bd47c30d5aaa05da546 Partial-Bug: #1753504 --- .../tests/unit/test_cli20_agentschedulers.py | 57 ++++++++++--------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/neutronclient/tests/unit/test_cli20_agentschedulers.py b/neutronclient/tests/unit/test_cli20_agentschedulers.py index d9c1f0033..ddb8dce7d 100644 --- a/neutronclient/tests/unit/test_cli20_agentschedulers.py +++ b/neutronclient/tests/unit/test_cli20_agentschedulers.py @@ -16,7 +16,7 @@ import sys -from mox3 import mox +import mock from neutronclient.neutron.v2_0 import agentscheduler from neutronclient.neutron.v2_0 import network @@ -34,43 +34,43 @@ def _test_add_to_agent(self, resource, cmd, cmd_args, destination, path = ((self.client.agent_path + destination) % cmd_args[0]) - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) result_str = self.client.serialize(result) return_tup = (test_cli20.MyResp(200), result_str) - self.client.httpclient.request( - test_cli20.end_url(path), 'POST', - body=test_cli20.MyComparator(body, self.client), - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(return_tup) - self.mox.ReplayAll() cmd_parser = cmd.get_parser('test_' + resource) parsed_args = cmd_parser.parse_args(cmd_args) - cmd.run(parsed_args) - self.mox.VerifyAll() - self.mox.UnsetStubs() + + with mock.patch.object(cmd, "get_client", + return_value=self.client) as mock_get_client, \ + mock.patch.object(self.client.httpclient, "request", + return_value=return_tup) as mock_request: + cmd.run(parsed_args) + mock_get_client.assert_called_once_with() + mock_request.assert_called_once_with( + test_cli20.end_url(path), 'POST', + body=test_cli20.MyComparator(body, self.client), + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN})) def _test_remove_from_agent(self, resource, cmd, cmd_args, destination): path = ((self.client.agent_path + destination + '/%s') % cmd_args) - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) return_tup = (test_cli20.MyResp(204), None) - self.client.httpclient.request( - test_cli20.end_url(path), 'DELETE', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(return_tup) - self.mox.ReplayAll() cmd_parser = cmd.get_parser('test_' + resource) parsed_args = cmd_parser.parse_args(cmd_args) - cmd.run(parsed_args) - self.mox.VerifyAll() - self.mox.UnsetStubs() + + with mock.patch.object(cmd, "get_client", + return_value=self.client) as mock_get_client, \ + mock.patch.object(self.client.httpclient, "request", + return_value=return_tup) as mock_request: + cmd.run(parsed_args) + mock_get_client.assert_called_once_with() + mock_request.assert_called_once_with( + test_cli20.end_url(path), 'DELETE', + body=None, + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN})) class CLITestV20DHCPAgentScheduler(CLITestV20AgentScheduler): @@ -93,17 +93,18 @@ def test_remove_network_from_agent(self): self._test_remove_from_agent(resource, cmd, args, self.client.DHCP_NETS) - def test_list_networks_on_agent(self): + @mock.patch.object(network.ListNetwork, "extend_list") + def test_list_networks_on_agent(self, mock_extend_list): resources = 'networks' cmd = agentscheduler.ListNetworksOnDhcpAgent( test_cli20.MyApp(sys.stdout), None) agent_id = 'agent_id1' path = ((self.client.agent_path + self.client.DHCP_NETS) % agent_id) - self.mox.StubOutWithMock(network.ListNetwork, "extend_list") - network.ListNetwork.extend_list(mox.IsA(list), mox.IgnoreArg()) self._test_list_resources(resources, cmd, base_args=[agent_id], path=path) + mock_extend_list.assert_called_once_with(test_cli20.IsA(list), + mock.ANY) def test_list_agents_hosting_network(self): resources = 'agent' From 633a20cde9a282f17b59d120f5c97e3989e6cec1 Mon Sep 17 00:00:00 2001 From: Hongbin Lu Date: Fri, 16 Mar 2018 18:12:15 +0000 Subject: [PATCH 665/845] Remove mox/mox3 from test_cli20_address_scope.py Change-Id: Ibba1061d447a73bb3e13e1b8869300d2c0de1b16 Partial-Bug: #1753504 --- .../tests/unit/test_cli20_address_scope.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/neutronclient/tests/unit/test_cli20_address_scope.py b/neutronclient/tests/unit/test_cli20_address_scope.py index e770de3b7..f58185fd0 100644 --- a/neutronclient/tests/unit/test_cli20_address_scope.py +++ b/neutronclient/tests/unit/test_cli20_address_scope.py @@ -16,7 +16,7 @@ import sys -from mox3 import mox +import mock from neutronclient.common import exceptions from neutronclient.neutron.v2_0 import address_scope @@ -127,18 +127,15 @@ def test_list_address_scope(self): None) self._test_list_resources(resources, cmd, True) - def test_list_address_scope_pagination(self): + @mock.patch.object(address_scope.ListAddressScope, "extend_list") + def test_list_address_scope_pagination(self, mock_extend_list): # address_scope-list. cmd = address_scope.ListAddressScope(test_cli20.MyApp(sys.stdout), None) - self.mox.StubOutWithMock(address_scope.ListAddressScope, - "extend_list") - address_scope.ListAddressScope.extend_list(mox.IsA(list), - mox.IgnoreArg()) self._test_list_resources_with_pagination("address_scopes", cmd) - self.mox.VerifyAll() - self.mox.UnsetStubs() + mock_extend_list.assert_called_once_with(test_cli20.IsA(list), + mock.ANY) def test_list_address_scope_sort(self): # sorted list: From 8dac1afd43481d9c9ffcde3674eed3f7c64896ab Mon Sep 17 00:00:00 2001 From: Hongbin Lu Date: Fri, 16 Mar 2018 18:34:07 +0000 Subject: [PATCH 666/845] Remove mox/mox3 usage from bgp Change-Id: Ie07cf987382281c00023b41ce74c314ec2457502 Partial-Bug: #1753504 --- neutronclient/tests/unit/bgp/test_cli20_speaker.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/neutronclient/tests/unit/bgp/test_cli20_speaker.py b/neutronclient/tests/unit/bgp/test_cli20_speaker.py index 2f6e65388..72612dc6c 100644 --- a/neutronclient/tests/unit/bgp/test_cli20_speaker.py +++ b/neutronclient/tests/unit/bgp/test_cli20_speaker.py @@ -16,7 +16,7 @@ import sys -from mox3 import mox +import mock from neutronclient.common import exceptions from neutronclient.neutron.v2_0.bgp import speaker as bgp_speaker @@ -155,18 +155,15 @@ def test_list_bgp_speaker(self): None) self._test_list_resources(resources, cmd, True) - def test_list_bgp_speaker_pagination(self): + @mock.patch.object(bgp_speaker.ListSpeakers, "extend_list") + def test_list_bgp_speaker_pagination(self, mock_extend_list): # List all BGP Speakers with pagination support. cmd = bgp_speaker.ListSpeakers(test_cli20.MyApp(sys.stdout), None) - self.mox.StubOutWithMock(bgp_speaker.ListSpeakers, - "extend_list") - bgp_speaker.ListSpeakers.extend_list(mox.IsA(list), - mox.IgnoreArg()) self._test_list_resources_with_pagination("bgp_speakers", cmd) - self.mox.VerifyAll() - self.mox.UnsetStubs() + mock_extend_list.assert_called_once_with(test_cli20.IsA(list), + mock.ANY) def test_list_bgp_speaker_sort(self): # sorted list: bgp-speaker-list --sort-key name --sort-key id From 260f74308e3c78d3e1561280578f03cb5612ff4f Mon Sep 17 00:00:00 2001 From: Hongbin Lu Date: Fri, 16 Mar 2018 18:42:40 +0000 Subject: [PATCH 667/845] Remove mox/mox3 usage from fw modules Change-Id: I913993c0bff15df19bf3978ed36062f970eef538 Partial-Bug: #1753504 --- .../unit/fw/test_cli20_firewallpolicy.py | 52 ++++++++++--------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py b/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py index 1b425e6d1..874ec7232 100644 --- a/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py +++ b/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py @@ -16,7 +16,7 @@ import sys -from mox3 import mox +import mock from neutronclient.neutron.v2_0.fw import firewallpolicy from neutronclient import shell @@ -166,24 +166,25 @@ def test_insert_firewall_rule(self): 'insert_before': 'rule2', 'insert_after': 'rule1'} - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) body = extrafields path = getattr(self.client, resource + "_insert_path") - self.client.httpclient.request( + cmd_parser = cmd.get_parser(resource + "_insert_rule") + resp = (test_cli20.MyResp(204), None) + + with mock.patch.object(cmd, "get_client", + return_value=self.client) as mock_get_client, \ + mock.patch.object(self.client.httpclient, "request", + return_value=resp) as mock_request: + shell.run_command(cmd, cmd_parser, args) + self.assert_mock_multiple_calls_with_same_arguments( + mock_get_client, mock.call(), 4) + mock_request.assert_called_once_with( test_cli20.MyUrlComparator( test_cli20.end_url(path % myid), self.client), 'PUT', body=test_cli20.MyComparator(body, self.client), - headers=mox.ContainsKeyValue( - 'X-Auth-Token', - test_cli20.TOKEN)).AndReturn((test_cli20.MyResp(204), None)) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser(resource + "_insert_rule") - shell.run_command(cmd, cmd_parser, args) - self.mox.VerifyAll() - self.mox.UnsetStubs() + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN})) def test_remove_firewall_rule(self): # firewall-policy-remove-rule myid ruleid @@ -195,24 +196,25 @@ def test_remove_firewall_rule(self): args = ['myid', 'removerule'] extrafields = {'firewall_rule_id': 'removerule', } - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) body = extrafields path = getattr(self.client, resource + "_remove_path") - self.client.httpclient.request( + cmd_parser = cmd.get_parser(resource + "_remove_rule") + resp = (test_cli20.MyResp(204), None) + + with mock.patch.object(cmd, "get_client", + return_value=self.client) as mock_get_client, \ + mock.patch.object(self.client.httpclient, "request", + return_value=resp) as mock_request: + shell.run_command(cmd, cmd_parser, args) + self.assert_mock_multiple_calls_with_same_arguments( + mock_get_client, mock.call(), 2) + mock_request.assert_called_once_with( test_cli20.MyUrlComparator( test_cli20.end_url(path % myid), self.client), 'PUT', body=test_cli20.MyComparator(body, self.client), - headers=mox.ContainsKeyValue( - 'X-Auth-Token', - test_cli20.TOKEN)).AndReturn((test_cli20.MyResp(204), None)) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser(resource + "_remove_rule") - shell.run_command(cmd, cmd_parser, args) - self.mox.VerifyAll() - self.mox.UnsetStubs() + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN})) def test_update_firewall_policy_name_shared_audited(self): # firewall-policy-update myid --name newname2 --shared --audited From ce623030ee19619eb28276e6f9f0a395261560ba Mon Sep 17 00:00:00 2001 From: Hongbin Lu Date: Fri, 16 Mar 2018 19:26:18 +0000 Subject: [PATCH 668/845] Remove mox/mox3 usage from lb modules Change-Id: I667dd870b615d4a9ccefd35fcb1ebf15760b1d6d Partial-Bug: #1753504 --- .../tests/unit/lb/test_cli20_healthmonitor.py | 54 +++++++++---------- .../tests/unit/lb/test_cli20_pool.py | 27 +++++----- .../unit/lb/v2/test_cli20_loadbalancer.py | 54 +++++++++---------- 3 files changed, 67 insertions(+), 68 deletions(-) diff --git a/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py b/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py index 999116e2a..7c4d15c09 100644 --- a/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py +++ b/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py @@ -16,7 +16,7 @@ import sys -from mox3 import mox +import mock from neutronclient.neutron.v2_0.lb import healthmonitor from neutronclient.tests.unit import test_cli20 @@ -149,10 +149,6 @@ def test_associate_healthmonitor(self): pool_id = 'p_id' args = [health_monitor_id, pool_id] - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) - body = {resource: {'id': health_monitor_id}} result = {resource: {'id': health_monitor_id}, } result_str = self.client.serialize(result) @@ -160,17 +156,21 @@ def test_associate_healthmonitor(self): path = getattr(self.client, "associate_pool_health_monitors_path") % pool_id return_tup = (test_cli20.MyResp(200), result_str) - self.client.httpclient.request( - test_cli20.end_url(path), 'POST', - body=test_cli20.MyComparator(body, self.client), - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(return_tup) - self.mox.ReplayAll() cmd_parser = cmd.get_parser('test_' + resource) parsed_args = cmd_parser.parse_args(args) - cmd.run(parsed_args) - self.mox.VerifyAll() - self.mox.UnsetStubs() + + with mock.patch.object(cmd, "get_client", + return_value=self.client) as mock_get_client, \ + mock.patch.object(self.client.httpclient, "request", + return_value=return_tup) as mock_request: + cmd.run(parsed_args) + + mock_get_client.assert_called_once_with() + mock_request.assert_called_once_with( + test_cli20.end_url(path), 'POST', + body=test_cli20.MyComparator(body, self.client), + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN})) def test_disassociate_healthmonitor(self): cmd = healthmonitor.DisassociateHealthMonitor( @@ -181,22 +181,22 @@ def test_disassociate_healthmonitor(self): pool_id = 'p_id' args = [health_monitor_id, pool_id] - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) - path = (getattr(self.client, "disassociate_pool_health_monitors_path") % {'pool': pool_id, 'health_monitor': health_monitor_id}) return_tup = (test_cli20.MyResp(204), None) - self.client.httpclient.request( - test_cli20.end_url(path), 'DELETE', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(return_tup) - self.mox.ReplayAll() cmd_parser = cmd.get_parser('test_' + resource) parsed_args = cmd_parser.parse_args(args) - cmd.run(parsed_args) - self.mox.VerifyAll() - self.mox.UnsetStubs() + + with mock.patch.object(cmd, "get_client", + return_value=self.client) as mock_get_client, \ + mock.patch.object(self.client.httpclient, "request", + return_value=return_tup) as mock_request: + cmd.run(parsed_args) + + mock_get_client.assert_called_once_with() + mock_request.assert_called_once_with( + test_cli20.end_url(path), 'DELETE', + body=None, + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN})) diff --git a/neutronclient/tests/unit/lb/test_cli20_pool.py b/neutronclient/tests/unit/lb/test_cli20_pool.py index 9a08f66fe..1106aed92 100644 --- a/neutronclient/tests/unit/lb/test_cli20_pool.py +++ b/neutronclient/tests/unit/lb/test_cli20_pool.py @@ -16,7 +16,7 @@ import sys -from mox3 import mox +import mock from neutronclient.neutron.v2_0.lb import pool from neutronclient.tests.unit import test_cli20 @@ -139,27 +139,28 @@ def test_retrieve_pool_stats(self): fields = ['bytes_in', 'bytes_out'] args = ['--fields', 'bytes_in', '--fields', 'bytes_out', my_id] - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) query = "&".join(["fields=%s" % field for field in fields]) expected_res = {'stats': {'bytes_in': '1234', 'bytes_out': '4321'}} resstr = self.client.serialize(expected_res) path = getattr(self.client, "pool_path_stats") return_tup = (test_cli20.MyResp(200), resstr) - self.client.httpclient.request( - test_cli20.end_url(path % my_id, query), 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(return_tup) - self.mox.ReplayAll() cmd_parser = cmd.get_parser("test_" + resource) parsed_args = cmd_parser.parse_args(args) - cmd.run(parsed_args) - self.mox.VerifyAll() - self.mox.UnsetStubs() + with mock.patch.object(cmd, "get_client", + return_value=self.client) as mock_get_client, \ + mock.patch.object(self.client.httpclient, "request", + return_value=return_tup) as mock_request: + cmd.run(parsed_args) + + self.assert_mock_multiple_calls_with_same_arguments( + mock_get_client, mock.call(), 2) + mock_request.assert_called_once_with( + test_cli20.end_url(path % my_id, query), 'GET', + body=None, + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN})) _str = self.fake_stdout.make_string() self.assertIn('bytes_in', _str) self.assertIn('bytes_out', _str) diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py b/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py index b62ce3435..6db07cc1a 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py @@ -16,7 +16,7 @@ import sys -from mox3 import mox +import mock from neutronclient.neutron.v2_0.lb.v2 import loadbalancer as lb from neutronclient.tests.unit import test_cli20 @@ -150,27 +150,27 @@ def test_retrieve_loadbalancer_stats(self): fields = ['bytes_in', 'bytes_out'] args = ['--fields', 'bytes_in', '--fields', 'bytes_out', my_id] - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) query = "&".join(["fields=%s" % field for field in fields]) expected_res = {'stats': {'bytes_in': '1234', 'bytes_out': '4321'}} resstr = self.client.serialize(expected_res) path = getattr(self.client, "lbaas_loadbalancer_path_stats") return_tup = (test_cli20.MyResp(200), resstr) - self.client.httpclient.request( - test_cli20.end_url(path % my_id, query), 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(return_tup) - self.mox.ReplayAll() cmd_parser = cmd.get_parser("test_" + resource) parsed_args = cmd_parser.parse_args(args) - cmd.run(parsed_args) - - self.mox.VerifyAll() - self.mox.UnsetStubs() + with mock.patch.object(cmd, "get_client", + return_value=self.client) as mock_get_client, \ + mock.patch.object(self.client.httpclient, "request", + return_value=return_tup) as mock_request: + cmd.run(parsed_args) + + self.assert_mock_multiple_calls_with_same_arguments( + mock_get_client, mock.call(), 2) + mock_request.assert_called_once_with( + test_cli20.end_url(path % my_id, query), 'GET', + body=None, + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN})) _str = self.fake_stdout.make_string() self.assertIn('bytes_in', _str) self.assertIn('1234', _str) @@ -184,10 +184,6 @@ def test_get_loadbalancer_statuses(self): my_id = self.test_id args = [my_id] - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) - expected_res = {'statuses': {'operating_status': 'ONLINE', 'provisioning_status': 'ACTIVE'}} @@ -195,19 +191,21 @@ def test_get_loadbalancer_statuses(self): path = getattr(self.client, "lbaas_loadbalancer_path_status") return_tup = (test_cli20.MyResp(200), resstr) - self.client.httpclient.request( - test_cli20.end_url(path % my_id), 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(return_tup) - self.mox.ReplayAll() cmd_parser = cmd.get_parser("test_" + resource) parsed_args = cmd_parser.parse_args(args) - cmd.run(parsed_args) - - self.mox.VerifyAll() - self.mox.UnsetStubs() + with mock.patch.object(cmd, "get_client", + return_value=self.client) as mock_get_client, \ + mock.patch.object(self.client.httpclient, "request", + return_value=return_tup) as mock_request: + cmd.run(parsed_args) + + mock_get_client.assert_called_once_with() + mock_request.assert_called_once_with( + test_cli20.end_url(path % my_id), 'GET', + body=None, + headers=test_cli20.ContainsKeyValue( + {'X-Auth-Token': test_cli20.TOKEN})) _str = self.fake_stdout.make_string() self.assertIn('operating_status', _str) self.assertIn('ONLINE', _str) From f6b1e85899acd20f8a08a8302542497f2b4977ae Mon Sep 17 00:00:00 2001 From: Hongbin Lu Date: Thu, 22 Mar 2018 21:52:29 +0000 Subject: [PATCH 669/845] Remove mox/mox3 usage from test_cli20.py Change-Id: Ib4dc5f6c7ba57d94364f19a049c690d3817c723a Partial-Bug: #1753504 --- neutronclient/tests/unit/test_cli20.py | 461 +++++++++++++------------ 1 file changed, 233 insertions(+), 228 deletions(-) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 9878e98e4..cfba9fafd 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -20,7 +20,6 @@ import sys import mock -from mox3 import mox from oslo_utils import encodeutils from oslotest import base import requests @@ -93,12 +92,12 @@ def end_url(path, query=None): return query and _url_str + "?" + query or _url_str -class MyUrlComparator(mox.Comparator): +class MyUrlComparator(object): def __init__(self, lhs, client): self.lhs = lhs self.client = client - def equals(self, rhs): + def __eq__(self, rhs): lhsp = urlparse.urlparse(self.lhs) rhsp = urlparse.urlparse(rhs) @@ -118,7 +117,7 @@ def __repr__(self): return str(self) -class MyComparator(mox.Comparator): +class MyComparator(object): def __init__(self, lhs, client): self.lhs = lhs self.client = client @@ -159,7 +158,7 @@ def _com(self, lhs, rhs): return self._com_list(lhs, rhs) return lhs == rhs - def equals(self, rhs): + def __eq__(self, rhs): if self.client: rhs = self.client.deserialize(rhs, 200) return self._com(self.lhs, rhs) @@ -228,7 +227,6 @@ def setUp(self, plurals=None): if plurals is not None: client.Client.EXTED_PLURALS.update(plurals) self.metadata = {'plurals': client.Client.EXTED_PLURALS} - self.mox = mox.Mox() self.endurl = ENDURL self.fake_stdout = FakeStdout() @@ -263,9 +261,6 @@ def _test_create_resource(self, resource, cmd, name, myid, args, parent_id=None, no_api_call=False, expected_exception=None, **kwargs): - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) if not cmd_resource: cmd_resource = resource if (resource in self.non_admin_status_resources): @@ -292,34 +287,37 @@ def _test_create_resource(self, resource, cmd, name, myid, args, path = getattr(self.client, resource_plural + "_path") if parent_id: path = path % parent_id - mox_body = MyComparator(body, self.client) + mock_body = MyComparator(body, self.client) + cmd_parser = cmd.get_parser('create_' + resource) + resp = (MyResp(200), resstr) + + with mock.patch.object(cmd, "get_client", + return_value=self.client) as mock_get_client, \ + mock.patch.object(self.client.httpclient, "request", + return_value=resp) as mock_request: + if expected_exception: + self.assertRaises(expected_exception, + shell.run_command, cmd, cmd_parser, args) + else: + shell.run_command(cmd, cmd_parser, args) + + self.assert_mock_multiple_calls_with_same_arguments( + mock_get_client, mock.call(), None) if not no_api_call: - self.client.httpclient.request( + mock_request.assert_called_once_with( end_url(path), 'POST', - body=mox_body, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr)) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser('create_' + resource) - if expected_exception: - self.assertRaises(expected_exception, - shell.run_command, cmd, cmd_parser, args) - else: - shell.run_command(cmd, cmd_parser, args) + body=mock_body, + headers=ContainsKeyValue({'X-Auth-Token': TOKEN})) + if not expected_exception: _str = self.fake_stdout.make_string() self.assertIn(myid, _str) if name: self.assertIn(name, _str) - self.mox.VerifyAll() - self.mox.UnsetStubs() def _test_list_columns(self, cmd, resources, resources_out, args=('-f', 'json'), cmd_resources=None, parent_id=None): - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) if not cmd_resources: cmd_resources = resources @@ -328,25 +326,27 @@ def _test_list_columns(self, cmd, resources, path = getattr(self.client, cmd_resources + "_path") if parent_id: path = path % parent_id - self.client.httpclient.request( + cmd_parser = cmd.get_parser("list_" + cmd_resources) + resp = (MyResp(200), resstr) + + with mock.patch.object(cmd, "get_client", + return_value=self.client) as mock_get_client, \ + mock.patch.object(self.client.httpclient, "request", + return_value=resp) as mock_request: + shell.run_command(cmd, cmd_parser, args) + + self.assert_mock_multiple_calls_with_same_arguments( + mock_get_client, mock.call(), None) + mock_request.assert_called_once_with( end_url(path), 'GET', body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr)) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser("list_" + cmd_resources) - shell.run_command(cmd, cmd_parser, args) - self.mox.VerifyAll() - self.mox.UnsetStubs() + headers=ContainsKeyValue({'X-Auth-Token': TOKEN})) def _test_list_resources(self, resources, cmd, detail=False, tags=(), fields_1=(), fields_2=(), page_size=None, sort_key=(), sort_dir=(), response_contents=None, base_args=None, path=None, cmd_resources=None, parent_id=None, output_format=None, query=""): - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) if not cmd_resources: cmd_resources = resources if response_contents is None: @@ -422,18 +422,22 @@ def _test_list_resources(self, resources, cmd, detail=False, tags=(), if output_format: args.append('-f') args.append(output_format) - self.client.httpclient.request( - MyUrlComparator(end_url(path, query), - self.client), + cmd_parser = cmd.get_parser("list_" + cmd_resources) + resp = (MyResp(200), resstr) + + with mock.patch.object(cmd, "get_client", + return_value=self.client) as mock_get_client, \ + mock.patch.object(self.client.httpclient, "request", + return_value=resp) as mock_request: + shell.run_command(cmd, cmd_parser, args) + + self.assert_mock_multiple_calls_with_same_arguments( + mock_get_client, mock.call(), None) + mock_request.assert_called_once_with( + MyUrlComparator(end_url(path, query), self.client), 'GET', body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr)) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser("list_" + cmd_resources) - shell.run_command(cmd, cmd_parser, args) - self.mox.VerifyAll() - self.mox.UnsetStubs() + headers=ContainsKeyValue({'X-Auth-Token': TOKEN})) _str = self.fake_stdout.make_string() if response_contents is None: self.assertIn('myid1', _str) @@ -443,9 +447,6 @@ def _test_list_resources_with_pagination(self, resources, cmd, base_args=None, cmd_resources=None, parent_id=None, query=""): - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) if not cmd_resources: cmd_resources = resources @@ -461,29 +462,34 @@ def _test_list_resources_with_pagination(self, resources, cmd, {'id': 'myid4', }]} resstr1 = self.client.serialize(reses1) resstr2 = self.client.serialize(reses2) - self.client.httpclient.request( - end_url(path, query), 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr1)) - self.client.httpclient.request( - MyUrlComparator(end_url(path, fake_query), - self.client), 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr2)) - self.mox.ReplayAll() cmd_parser = cmd.get_parser("list_" + cmd_resources) args = base_args if base_args is not None else [] - shell.run_command(cmd, cmd_parser, args) - self.mox.VerifyAll() - self.mox.UnsetStubs() + mock_request_calls = [ + mock.call( + end_url(path, query), 'GET', + body=None, + headers=ContainsKeyValue({'X-Auth-Token': TOKEN})), + mock.call( + MyUrlComparator(end_url(path, fake_query), + self.client), 'GET', + body=None, + headers=ContainsKeyValue({'X-Auth-Token': TOKEN}))] + mock_request_resp = [(MyResp(200), resstr1), (MyResp(200), resstr2)] + + with mock.patch.object(cmd, "get_client", + return_value=self.client) as mock_get_client, \ + mock.patch.object(self.client.httpclient, + "request") as mock_request: + mock_request.side_effect = mock_request_resp + shell.run_command(cmd, cmd_parser, args) + + self.assert_mock_multiple_calls_with_same_arguments( + mock_get_client, mock.call(), None) + self.assertEqual(2, mock_request.call_count) + mock_request.assert_has_calls(mock_request_calls) def _test_update_resource(self, resource, cmd, myid, args, extrafields, cmd_resource=None, parent_id=None): - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) if not cmd_resource: cmd_resource = resource @@ -493,27 +499,29 @@ def _test_update_resource(self, resource, cmd, myid, args, extrafields, path = path % (parent_id, myid) else: path = path % myid - mox_body = MyComparator(body, self.client) + mock_body = MyComparator(body, self.client) + + cmd_parser = cmd.get_parser("update_" + cmd_resource) + resp = (MyResp(204), None) + + with mock.patch.object(cmd, "get_client", + return_value=self.client) as mock_get_client, \ + mock.patch.object(self.client.httpclient, "request", + return_value=resp) as mock_request: + shell.run_command(cmd, cmd_parser, args) - self.client.httpclient.request( + self.assert_mock_multiple_calls_with_same_arguments( + mock_get_client, mock.call(), None) + mock_request.assert_called_once_with( MyUrlComparator(end_url(path), self.client), 'PUT', - body=mox_body, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), None)) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser("update_" + cmd_resource) - shell.run_command(cmd, cmd_parser, args) - self.mox.VerifyAll() - self.mox.UnsetStubs() + body=mock_body, + headers=ContainsKeyValue({'X-Auth-Token': TOKEN})) _str = self.fake_stdout.make_string() self.assertIn(myid, _str) def _test_show_resource(self, resource, cmd, myid, args, fields=(), cmd_resource=None, parent_id=None): - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) if not cmd_resource: cmd_resource = resource @@ -527,53 +535,68 @@ def _test_show_resource(self, resource, cmd, myid, args, fields=(), path = path % (parent_id, myid) else: path = path % myid - self.client.httpclient.request( + cmd_parser = cmd.get_parser("show_" + cmd_resource) + resp = (MyResp(200), resstr) + + with mock.patch.object(cmd, "get_client", + return_value=self.client) as mock_get_client, \ + mock.patch.object(self.client.httpclient, "request", + return_value=resp) as mock_request: + shell.run_command(cmd, cmd_parser, args) + + self.assert_mock_multiple_calls_with_same_arguments( + mock_get_client, mock.call(), None) + mock_request.assert_called_once_with( end_url(path, query), 'GET', body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr)) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser("show_" + cmd_resource) - shell.run_command(cmd, cmd_parser, args) - self.mox.VerifyAll() - self.mox.UnsetStubs() + headers=ContainsKeyValue({'X-Auth-Token': TOKEN})) _str = self.fake_stdout.make_string() self.assertIn(myid, _str) self.assertIn('myname', _str) def _test_set_path_and_delete(self, path, parent_id, myid, + mock_request_calls, mock_request_returns, delete_fail=False): return_val = 404 if delete_fail else 204 if parent_id: path = path % (parent_id, myid) else: path = path % (myid) - self.client.httpclient.request( + mock_request_returns.append((MyResp(return_val), None)) + mock_request_calls.append(mock.call( end_url(path), 'DELETE', body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', TOKEN)).AndReturn((MyResp( - return_val), None)) + headers=ContainsKeyValue({'X-Auth-Token': TOKEN}))) def _test_delete_resource(self, resource, cmd, myid, args, cmd_resource=None, parent_id=None, extra_id=None, delete_fail=False): - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) + mock_request_calls = [] + mock_request_returns = [] if not cmd_resource: cmd_resource = resource path = getattr(self.client, cmd_resource + "_path") - self._test_set_path_and_delete(path, parent_id, myid) + self._test_set_path_and_delete(path, parent_id, myid, + mock_request_calls, + mock_request_returns) # extra_id is used to test for bulk_delete if extra_id: self._test_set_path_and_delete(path, parent_id, extra_id, + mock_request_calls, + mock_request_returns, delete_fail) - self.mox.ReplayAll() cmd_parser = cmd.get_parser("delete_" + cmd_resource) - shell.run_command(cmd, cmd_parser, args) - self.mox.VerifyAll() - self.mox.UnsetStubs() + + with mock.patch.object(cmd, "get_client", + return_value=self.client) as mock_get_client, \ + mock.patch.object(self.client.httpclient, + "request") as mock_request: + mock_request.side_effect = mock_request_returns + shell.run_command(cmd, cmd_parser, args) + + self.assert_mock_multiple_calls_with_same_arguments( + mock_get_client, mock.call(), None) + mock_request.assert_has_calls(mock_request_calls) _str = self.fake_stdout.make_string() self.assertIn(myid, _str) if extra_id: @@ -582,24 +605,25 @@ def _test_delete_resource(self, resource, cmd, myid, args, def _test_update_resource_action(self, resource, cmd, myid, action, args, body, expected_code=200, retval=None, cmd_resource=None): - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) if not cmd_resource: cmd_resource = resource path = getattr(self.client, cmd_resource + "_path") path_action = '%s/%s' % (myid, action) - self.client.httpclient.request( + cmd_parser = cmd.get_parser("update_" + cmd_resource) + resp = (MyResp(expected_code), retval) + + with mock.patch.object(cmd, "get_client", + return_value=self.client) as mock_get_client, \ + mock.patch.object(self.client.httpclient, "request", + return_value=resp) as mock_request: + shell.run_command(cmd, cmd_parser, args) + + self.assert_mock_multiple_calls_with_same_arguments( + mock_get_client, mock.call(), None) + mock_request.assert_called_once_with( end_url(path % path_action), 'PUT', body=MyComparator(body, self.client), - headers=mox.ContainsKeyValue( - 'X-Auth-Token', TOKEN)).AndReturn((MyResp(expected_code), - retval)) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser("update_" + cmd_resource) - shell.run_command(cmd, cmd_parser, args) - self.mox.VerifyAll() - self.mox.UnsetStubs() + headers=ContainsKeyValue({'X-Auth-Token': TOKEN})) _str = self.fake_stdout.make_string() self.assertIn(myid, _str) @@ -641,7 +665,6 @@ def _test_list_resources_filter_params(self, base_args='', query=''): query=query) def _test_list_resources_with_arg_error(self, base_args=''): - self.addCleanup(self.mox.UnsetStubs) resources = 'test_resources' cmd = TestListCommand(MyApp(sys.stdout), None) # argparse parse error leads to SystemExit @@ -690,7 +713,6 @@ def test_list_resources_argparse_kwargs(self): class ClientV2TestJson(CLITestV20Base): def test_do_request_unicode(self): - self.mox.StubOutWithMock(self.client.httpclient, "request") unicode_text = u'\u7f51\u7edc' # url with unicode action = u'/test' @@ -705,46 +727,40 @@ def test_do_request_unicode(self): unicode_text) expected_auth_token = encodeutils.safe_encode(unicode_text) resp_headers = {'x-openstack-request-id': REQUEST_ID} + resp = (MyResp(200, resp_headers), expect_body) + + with mock.patch.object(self.client.httpclient, "request", + return_value=resp) as mock_request: + result = self.client.do_request('PUT', action, body=body, + params=params) - self.client.httpclient.request( + mock_request.assert_called_once_with( end_url(expected_action, query=expect_query), 'PUT', body=expect_body, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', - expected_auth_token)).AndReturn((MyResp(200, resp_headers), - expect_body)) - - self.mox.ReplayAll() - result = self.client.do_request('PUT', action, body=body, - params=params) - self.mox.VerifyAll() - self.mox.UnsetStubs() - + headers=ContainsKeyValue({'X-Auth-Token': expected_auth_token})) # test response with unicode self.assertEqual(body, result) def test_do_request_error_without_response_body(self): - self.mox.StubOutWithMock(self.client.httpclient, "request") params = {'test': 'value'} expect_query = six.moves.urllib.parse.urlencode(params) self.client.httpclient.auth_token = 'token' resp_headers = {'x-openstack-request-id': REQUEST_ID} + resp = (MyResp(400, headers=resp_headers, reason='An error'), '') - self.client.httpclient.request( + with mock.patch.object(self.client.httpclient, "request", + return_value=resp) as mock_request: + error = self.assertRaises(exceptions.NeutronClientException, + self.client.do_request, 'PUT', '/test', + body='', params=params) + + mock_request.assert_called_once_with( MyUrlComparator(end_url('/test', query=expect_query), self.client), 'PUT', body='', - headers=mox.ContainsKeyValue('X-Auth-Token', 'token') - ).AndReturn((MyResp(400, headers=resp_headers, reason='An error'), '')) - - self.mox.ReplayAll() - error = self.assertRaises(exceptions.NeutronClientException, - self.client.do_request, 'PUT', '/test', - body='', params=params) + headers=ContainsKeyValue({'X-Auth-Token': 'token'})) expected_error = "An error\nNeutron server returns " \ "request_ids: %s" % [REQUEST_ID] self.assertEqual(expected_error, str(error)) - self.mox.VerifyAll() - self.mox.UnsetStubs() def test_do_request_with_long_uri_exception(self): long_string = 'x' * 8200 # 8200 > MAX_URI_LEN:8192 @@ -755,31 +771,28 @@ def test_do_request_with_long_uri_exception(self): self.assertNotEqual(0, exception.excess) def test_do_request_request_ids(self): - self.mox.StubOutWithMock(self.client.httpclient, "request") params = {'test': 'value'} expect_query = six.moves.urllib.parse.urlencode(params) self.client.httpclient.auth_token = 'token' body = params expect_body = self.client.serialize(body) resp_headers = {'x-openstack-request-id': REQUEST_ID} - self.client.httpclient.request( + resp = (MyResp(200, resp_headers), expect_body) + + with mock.patch.object(self.client.httpclient, "request", + return_value=resp) as mock_request: + result = self.client.do_request('PUT', '/test', body=body, + params=params) + + mock_request.assert_called_once_with( MyUrlComparator(end_url('/test', query=expect_query), self.client), 'PUT', body=expect_body, - headers=mox.ContainsKeyValue('X-Auth-Token', 'token') - ).AndReturn((MyResp(200, resp_headers), expect_body)) - - self.mox.ReplayAll() - result = self.client.do_request('PUT', '/test', body=body, - params=params) - self.mox.VerifyAll() - self.mox.UnsetStubs() + headers=ContainsKeyValue({'X-Auth-Token': 'token'})) self.assertEqual(body, result) self.assertEqual([REQUEST_ID], result.request_ids) def test_list_request_ids_with_retrieve_all_true(self): - self.mox.StubOutWithMock(self.client.httpclient, "request") - path = '/test' resources = 'tests' fake_query = "marker=myid2&limit=2" @@ -792,30 +805,27 @@ def test_list_request_ids_with_retrieve_all_true(self): resstr1 = self.client.serialize(reses1) resstr2 = self.client.serialize(reses2) resp_headers = {'x-openstack-request-id': REQUEST_ID} - self.client.httpclient.request( - end_url(path, ""), 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200, resp_headers), - resstr1)) - self.client.httpclient.request( - MyUrlComparator(end_url(path, fake_query), - self.client), 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200, resp_headers), - resstr2)) - self.mox.ReplayAll() - result = self.client.list(resources, path) - - self.mox.VerifyAll() - self.mox.UnsetStubs() + resp = [(MyResp(200, resp_headers), resstr1), + (MyResp(200, resp_headers), resstr2)] + with mock.patch.object(self.client.httpclient, "request", + side_effect=resp) as mock_request: + result = self.client.list(resources, path) + + self.assertEqual(2, mock_request.call_count) + mock_request.assert_has_calls([ + mock.call( + end_url(path, ""), 'GET', + body=None, + headers=ContainsKeyValue({'X-Auth-Token': TOKEN})), + mock.call( + MyUrlComparator(end_url(path, fake_query), + self.client), 'GET', + body=None, + headers=ContainsKeyValue({'X-Auth-Token': TOKEN}))]) self.assertEqual([REQUEST_ID, REQUEST_ID], result.request_ids) def test_list_request_ids_with_retrieve_all_false(self): - self.mox.StubOutWithMock(self.client.httpclient, "request") - path = '/test' resources = 'tests' fake_query = "marker=myid2&limit=2" @@ -828,26 +838,26 @@ def test_list_request_ids_with_retrieve_all_false(self): resstr1 = self.client.serialize(reses1) resstr2 = self.client.serialize(reses2) resp_headers = {'x-openstack-request-id': REQUEST_ID} - self.client.httpclient.request( - end_url(path, ""), 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200, resp_headers), - resstr1)) - self.client.httpclient.request( - MyUrlComparator(end_url(path, fake_query), self.client), 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200, resp_headers), - resstr2)) - self.mox.ReplayAll() - result = self.client.list(resources, path, retrieve_all=False) - next(result) - self.assertEqual([REQUEST_ID], result.request_ids) - next(result) - self.assertEqual([REQUEST_ID, REQUEST_ID], result.request_ids) - self.mox.VerifyAll() - self.mox.UnsetStubs() + resp = [(MyResp(200, resp_headers), resstr1), + (MyResp(200, resp_headers), resstr2)] + with mock.patch.object(self.client.httpclient, "request", + side_effect=resp) as mock_request: + result = self.client.list(resources, path, retrieve_all=False) + next(result) + self.assertEqual([REQUEST_ID], result.request_ids) + next(result) + self.assertEqual([REQUEST_ID, REQUEST_ID], result.request_ids) + + self.assertEqual(2, mock_request.call_count) + mock_request.assert_has_calls([ + mock.call( + end_url(path, ""), 'GET', + body=None, + headers=ContainsKeyValue({'X-Auth-Token': TOKEN})), + mock.call( + MyUrlComparator(end_url(path, fake_query), self.client), 'GET', + body=None, + headers=ContainsKeyValue({'X-Auth-Token': TOKEN}))]) def test_deserialize_without_data(self): data = u'' @@ -855,51 +865,48 @@ def test_deserialize_without_data(self): self.assertEqual(data, result) def test_update_resource(self): - self.mox.StubOutWithMock(self.client.httpclient, "request") params = {'test': 'value'} expect_query = six.moves.urllib.parse.urlencode(params) self.client.httpclient.auth_token = 'token' body = params expect_body = self.client.serialize(body) resp_headers = {'x-openstack-request-id': REQUEST_ID} - self.client.httpclient.request( + resp = (MyResp(200, resp_headers), expect_body) + + with mock.patch.object(self.client.httpclient, "request", + return_value=resp) as mock_request: + result = self.client._update_resource('/test', body=body, + params=params) + + mock_request.assert_called_once_with( MyUrlComparator(end_url('/test', query=expect_query), self.client), 'PUT', body=expect_body, - headers=mox.And( - mox.ContainsKeyValue('X-Auth-Token', 'token'), - mox.Not(mox.In('If-Match'))) - ).AndReturn((MyResp(200, resp_headers), expect_body)) - - self.mox.ReplayAll() - result = self.client._update_resource('/test', body=body, - params=params) - self.mox.VerifyAll() - self.mox.UnsetStubs() + headers=ContainsKeyValue({'X-Auth-Token': 'token'})) + self.assertNotIn('If-Match', mock_request.call_args[1]['headers']) self.assertEqual(body, result) self.assertEqual([REQUEST_ID], result.request_ids) def test_update_resource_with_revision_number(self): - self.mox.StubOutWithMock(self.client.httpclient, "request") params = {'test': 'value'} expect_query = six.moves.urllib.parse.urlencode(params) self.client.httpclient.auth_token = 'token' body = params expect_body = self.client.serialize(body) resp_headers = {'x-openstack-request-id': REQUEST_ID} - self.client.httpclient.request( + resp = (MyResp(200, resp_headers), expect_body) + + with mock.patch.object(self.client.httpclient, "request", + return_value=resp) as mock_request: + result = self.client._update_resource('/test', body=body, + params=params, + revision_number=1) + + mock_request.assert_called_once_with( MyUrlComparator(end_url('/test', query=expect_query), self.client), 'PUT', body=expect_body, - headers=mox.And( - mox.ContainsKeyValue('X-Auth-Token', 'token'), - mox.ContainsKeyValue('If-Match', 'revision_number=1')) - ).AndReturn((MyResp(200, resp_headers), expect_body)) - - self.mox.ReplayAll() - result = self.client._update_resource('/test', body=body, - params=params, revision_number=1) - self.mox.VerifyAll() - self.mox.UnsetStubs() + headers=ContainsKeyValue( + {'X-Auth-Token': 'token', 'If-Match': 'revision_number=1'})) self.assertEqual(body, result) self.assertEqual([REQUEST_ID], result.request_ids) @@ -1051,23 +1058,21 @@ def test_exception_status(self): self.assertEqual(599, e.status_code) def test_connection_failed(self): - self.mox.StubOutWithMock(self.client.httpclient, 'request') self.client.httpclient.auth_token = 'token' + excp = requests.exceptions.ConnectionError('Connection refused') - self.client.httpclient.request( - end_url('/test'), 'GET', - headers=mox.ContainsKeyValue('X-Auth-Token', 'token') - ).AndRaise(requests.exceptions.ConnectionError('Connection refused')) - - self.mox.ReplayAll() + with mock.patch.object(self.client.httpclient, "request", + side_effect=excp) as mock_request: + error = self.assertRaises(exceptions.ConnectionFailed, + self.client.get, '/test') - error = self.assertRaises(exceptions.ConnectionFailed, - self.client.get, '/test') + mock_request.assert_called_once_with( + end_url('/test'), 'GET', + body=None, + headers=ContainsKeyValue({'X-Auth-Token': 'token'})) # NB: ConnectionFailed has no explicit status_code, so this # tests that there is a fallback defined. self.assertIsNotNone(error.status_code) - self.mox.VerifyAll() - self.mox.UnsetStubs() class DictWithMetaTest(base.BaseTestCase): @@ -1183,14 +1188,14 @@ def test_show_resource_yaml(self): self.assertEqual('myname', data['name']) self.assertEqual('myid', data['id']) - def _test_list_resources_with_formatter(self, fmt): + @mock.patch.object(network.ListNetwork, "extend_list") + def _test_list_resources_with_formatter(self, fmt, mock_extend_list): resources = 'networks' cmd = network.ListNetwork(MyApp(sys.stdout), None) # ListNetwork has its own extend_list, so we need to stub out it # to avoid an extra API call. - self.mox.StubOutWithMock(network.ListNetwork, "extend_list") - network.ListNetwork.extend_list(mox.IsA(list), mox.IgnoreArg()) self._test_list_resources(resources, cmd, output_format=fmt) + mock_extend_list.assert_called_once_with(IsA(list), mock.ANY) def test_list_resources_table(self): self._test_list_resources_with_formatter('table') From ac24ab19c3a5ab71036c6f5447a904d022dec73c Mon Sep 17 00:00:00 2001 From: Hongbin Lu Date: Thu, 22 Mar 2018 22:05:03 +0000 Subject: [PATCH 670/845] Remove mox3 from test-requirements.txt Change-Id: I9719d9b308811e7bf218af2f7da949b6c0509fc1 Closes-Bug: #1753504 --- test-requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index 39377181e..98ded3e94 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -6,7 +6,6 @@ hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0 coverage!=4.4,>=4.0 # Apache-2.0 fixtures>=3.0.0 # Apache-2.0/BSD flake8-import-order==0.12 # LGPLv3 -mox3>=0.20.0 # Apache-2.0 mock>=2.0.0 # BSD openstackdocstheme>=1.18.1 # Apache-2.0 oslotest>=3.2.0 # Apache-2.0 From 50a251ff94c2ac3bf6e4a08a01c56953efd80236 Mon Sep 17 00:00:00 2001 From: Nguyen Hai Date: Fri, 6 Apr 2018 22:34:08 +0900 Subject: [PATCH 671/845] Fix incompatible requirement in lower-constraints Fix lower-constraints don't match the lower bounds in the requirements file(s). It causes fail in requirements-check. REF: http://lists.openstack.org/pipermail/openstack-dev/2018-April/129056.html Change-Id: Iaa3c390d5c66bdbf5836a6d5d59bc5a636235722 --- lower-constraints.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lower-constraints.txt b/lower-constraints.txt index e21d9236e..81394e21d 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -20,8 +20,8 @@ eventlet==0.18.2 extras==1.0.0 fasteners==0.7.0 fixtures==3.0.0 -flake8==2.5.5 flake8-import-order==0.12 +flake8==2.5.5 future==0.16.0 futurist==1.2.0 greenlet==0.4.10 @@ -69,8 +69,8 @@ Paste==2.0.2 PasteDeploy==1.5.0 pbr==2.0.0 pep8==1.5.7 -pika==0.10.0 pika-pool==0.1.3 +pika==0.10.0 positional==1.2.1 prettytable==0.7.2 pyasn1==0.1.8 @@ -94,15 +94,15 @@ pytz==2013.6 PyYAML==3.12 reno==2.5.0 repoze.lru==0.7 +requests-mock==1.2.0 requests==2.14.2 -requests-mock==1.1.0 requestsexceptions==1.2.0 rfc3986==0.3.1 Routes==2.3.1 simplejson==3.5.1 six==1.10.0 snowballstemmer==1.2.1 -Sphinx==1.6.5 +Sphinx==1.6.2 sphinxcontrib-websupport==1.0.1 statsd==3.2.1 stestr==1.0.0 From 1dcd1f83d265bfeeda7c99a3c0bb1a170d27e291 Mon Sep 17 00:00:00 2001 From: Nguyen Hai Date: Wed, 21 Mar 2018 00:40:20 +0900 Subject: [PATCH 672/845] Follow the new PTI for document build For compliance with the Project Testing Interface as described in: https://governance.openstack.org/tc/reference/project-testing-interface.html http://lists.openstack.org/pipermail/openstack-dev/2017-December/125710.html http://lists.openstack.org/pipermail/openstack-dev/2018-March/128594.html Depends-On: https://review.openstack.org/#/c/559315/ Depends-On: https://review.openstack.org/#/c/559332/ Change-Id: Icbdc67dab1853162b1a877891195cc26b4873cb0 --- doc/requirements.txt | 6 ++++++ setup.cfg | 6 ------ test-requirements.txt | 3 --- tox.ini | 2 ++ 4 files changed, 8 insertions(+), 9 deletions(-) create mode 100644 doc/requirements.txt diff --git a/doc/requirements.txt b/doc/requirements.txt new file mode 100644 index 000000000..d959d4431 --- /dev/null +++ b/doc/requirements.txt @@ -0,0 +1,6 @@ +# The order of packages is significant, because pip processes them in the order +# of appearance. Changing the order has an impact on the overall integration +# process, which may cause wedges in the gate later. +openstackdocstheme>=1.18.1 # Apache-2.0 +reno>=2.5.0 # Apache-2.0 +sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD diff --git a/setup.cfg b/setup.cfg index 67a2a35e4..41857c7a6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -452,12 +452,6 @@ neutron.cli.v2 = vpn-ikepolicy-update = neutronclient.neutron.v2_0.vpn.ikepolicy:UpdateIKEPolicy vpn-ikepolicy-delete = neutronclient.neutron.v2_0.vpn.ikepolicy:DeleteIKEPolicy -[build_sphinx] -all_files = 1 -build-dir = doc/build -source-dir = doc/source -warning-is-error = 1 - [wheel] universal = 1 diff --git a/test-requirements.txt b/test-requirements.txt index 98ded3e94..58c378ae7 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -7,14 +7,11 @@ coverage!=4.4,>=4.0 # Apache-2.0 fixtures>=3.0.0 # Apache-2.0/BSD flake8-import-order==0.12 # LGPLv3 mock>=2.0.0 # BSD -openstackdocstheme>=1.18.1 # Apache-2.0 oslotest>=3.2.0 # Apache-2.0 osprofiler>=1.4.0 # Apache-2.0 python-openstackclient>=3.12.0 # Apache-2.0 python-subunit>=1.0.0 # Apache-2.0/BSD -reno>=2.5.0 # Apache-2.0 requests-mock>=1.2.0 # Apache-2.0 -sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD testrepository>=0.0.18 # Apache-2.0/BSD testtools>=2.2.0 # MIT testscenarios>=0.4 # Apache-2.0/BSD diff --git a/tox.ini b/tox.ini index d11d3b96e..2d3cd408d 100644 --- a/tox.ini +++ b/tox.ini @@ -47,9 +47,11 @@ commands = coverage report [testenv:docs] +deps = -r{toxinidir}/doc/requirements.txt commands = sphinx-build -W -b html doc/source doc/build/html [testenv:releasenotes] +deps = -r{toxinidir}/doc/requirements.txt commands = sphinx-build -a -E -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html [flake8] From 2b8c6cac22b810683a4eef0a74481e1ee4215c86 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Fri, 6 Apr 2018 20:32:58 +0900 Subject: [PATCH 673/845] Add oslo.log to requirements.txt During the review https://review.openstack.org/#/c/554597/, it turns out that oslo.log is a direct dependency. This commit adds oslo.log to requirements.txt. Change-Id: Ief5930dcb0793e441aabf658269ada31b05b7de5 --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 17de076a7..ec2d7af87 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,6 +8,7 @@ iso8601>=0.1.11 # MIT netaddr>=0.7.18 # BSD osc-lib>=1.8.0 # Apache-2.0 oslo.i18n>=3.15.3 # Apache-2.0 +oslo.log>=3.36.0 # Apache-2.0 oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0 oslo.utils>=3.33.0 # Apache-2.0 os-client-config>=1.28.0 # Apache-2.0 From bcffe1fce7584e9e7a3488959a17b45666592119 Mon Sep 17 00:00:00 2001 From: Jens Harbott Date: Mon, 9 Apr 2018 17:28:27 +0000 Subject: [PATCH 674/845] Fix list bgp speaker by agent ID The arg is called "agent", not "agent_id". Change-Id: I5dd524137202a5411451e775c7ca5d6748dc6e7a Closes-Bug: 1762488 --- neutronclient/osc/v2/dynamic_routing/bgp_speaker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/osc/v2/dynamic_routing/bgp_speaker.py b/neutronclient/osc/v2/dynamic_routing/bgp_speaker.py index c54582b9d..5ce8d471e 100644 --- a/neutronclient/osc/v2/dynamic_routing/bgp_speaker.py +++ b/neutronclient/osc/v2/dynamic_routing/bgp_speaker.py @@ -188,7 +188,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.neutronclient if parsed_args.agent is not None: - data = client.list_bgp_speaker_on_dragent(parsed_args.agent_id) + data = client.list_bgp_speaker_on_dragent(parsed_args.agent) else: data = client.list_bgp_speakers() From 19d0609888ba479caac274325dcaec5fc8117ca0 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Wed, 11 Apr 2018 21:36:15 +0900 Subject: [PATCH 675/845] Fix pep8 error pycodestyle 2.4.0 which is updated along with pep8 introduces new checks and they hit neutronclient. Change-Id: Ic417aee3239c46f1e989c50b9814adfd75cff175 --- neutronclient/neutron/v2_0/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index b1b675aa7..c1dbc113d 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -720,8 +720,8 @@ def setup_columns(self, info, parsed_args): if parsed_args.formatter == 'table': formatters = self._formatters - elif (parsed_args.formatter == 'csv' - and hasattr(self, '_formatters_csv')): + elif (parsed_args.formatter == 'csv' and + hasattr(self, '_formatters_csv')): formatters = self._formatters_csv else: # For other formatters, we use raw value returned from neutron From 2cf52672b7cf19e1b4c318cc3be5b998d89f5e82 Mon Sep 17 00:00:00 2001 From: Gleb Zimin Date: Thu, 22 Mar 2018 17:18:03 +0400 Subject: [PATCH 676/845] Replace insecure function eval In neutronclient we use a eval function for processing CLI neutron arguments. This function possible insecure because eval get argument from client side. Instead of it we can use a dict with allowed types which is more secure. Closes-Bug: #1762938 Change-Id: Idde55d1b9206e9ef8742464825709f098d488a8e Co-Authored-By: Akihiro Motoki --- neutronclient/neutron/v2_0/__init__.py | 25 ++++++++++++++------ neutronclient/tests/unit/test_casual_args.py | 24 ++++++++++++++++--- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index c1dbc113d..a5b262c93 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -185,6 +185,14 @@ def parse_args_to_dict(values_specs): current_item = None # the str after 'type=' current_type_str = None + # dict of allowed types + allowed_type_dict = { + 'bool': utils.str2bool, + 'dict': utils.str2dict, + 'int': int, + 'str': str, + } + for _item in values_specs_copy: if _item.startswith('--'): # Deal with previous argument if any @@ -215,12 +223,16 @@ def parse_args_to_dict(values_specs): _("Invalid values_specs %s") % ' '.join(values_specs)) if 'type' not in current_arg: current_type_str = _item.split('=', 2)[1] - current_arg.update({'type': eval(current_type_str)}) - if current_type_str == 'bool': - current_arg.update({'type': utils.str2bool}) - elif current_type_str == 'dict': - current_arg.update({'type': utils.str2dict}) - continue + if current_type_str in allowed_type_dict: + current_arg['type'] = allowed_type_dict[current_type_str] + continue + else: + raise exceptions.CommandError( + _("Invalid value_specs {valspec}: type {curtypestr}" + " is not supported").format( + valspec=' '.join(values_specs), + curtypestr=current_type_str)) + elif _item == 'list=true': _list_flag = True continue @@ -240,7 +252,6 @@ def parse_args_to_dict(values_specs): if _item.startswith('---'): raise exceptions.CommandError( _("Invalid values_specs %s") % ' '.join(values_specs)) - _values_specs.append(_item) # Deal with last one argument diff --git a/neutronclient/tests/unit/test_casual_args.py b/neutronclient/tests/unit/test_casual_args.py index dd87a5399..f2ca03101 100644 --- a/neutronclient/tests/unit/test_casual_args.py +++ b/neutronclient/tests/unit/test_casual_args.py @@ -14,6 +14,7 @@ # under the License. # +import six import testtools from neutronclient.common import exceptions @@ -41,6 +42,13 @@ def test_bool_false(self): _mydict = neutronV20.parse_args_to_dict(_specs) self.assertFalse(_mydict['my_bool']) + def test_int_and_str(self): + _specs = ['--my-int', 'type=int', '10', + '--my-str', 'type=str', 'value1'] + _mydict = neutronV20.parse_args_to_dict(_specs) + self.assertEqual(10, _mydict['my_int']) + self.assertEqual('value1', _mydict['my_str']) + def test_nargs(self): _specs = ['--tag', 'x', 'y', '--arg1', 'value1'] _mydict = neutronV20.parse_args_to_dict(_specs) @@ -102,15 +110,25 @@ def test_list_of_dict_arg(self): self.assertEqual('value1', arg1[0]['key1']) self.assertEqual('value2', arg1[0]['key2']) + def test_parse_args_to_dict_bad_type(self): + _specs = ['--badtypearg', 'type=badtype', 'val1'] + ex = self.assertRaises(exceptions.CommandError, + neutronV20.parse_args_to_dict, _specs) + self.assertEqual('Invalid value_specs --badtypearg type=badtype val1: ' + 'type badtype is not supported', + six.text_type(ex)) + def test_clear_action(self): _specs = ['--anyarg', 'action=clear'] args = neutronV20.parse_args_to_dict(_specs) self.assertIsNone(args['anyarg']) - def test_bad_values_str(self): + def test_bad_values_str_without_value(self): _specs = ['--strarg', 'type=str'] - self.assertRaises(exceptions.CommandError, - neutronV20.parse_args_to_dict, _specs) + ex = self.assertRaises(exceptions.CommandError, + neutronV20.parse_args_to_dict, _specs) + self.assertEqual('Invalid values_specs --strarg type=str', + six.text_type(ex)) def test_bad_values_list(self): _specs = ['--listarg', 'list=true', 'type=str'] From 2bff68649b4adcfb4a1b8bede5b0e312c846b4db Mon Sep 17 00:00:00 2001 From: chenyaguang Date: Wed, 18 Apr 2018 15:45:23 +0800 Subject: [PATCH 677/845] Set or unset port pair group failed Set or unset port pair for port pair group failed because the type of 'existing' is wrong Change-Id: I198e745fccff76184d9813805d1c1915aff5a273 Closes-Bug: #1762884 --- neutronclient/osc/v2/sfc/sfc_port_pair_group.py | 14 +++++++------- .../tests/unit/osc/v2/sfc/test_port_pair_group.py | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/neutronclient/osc/v2/sfc/sfc_port_pair_group.py b/neutronclient/osc/v2/sfc/sfc_port_pair_group.py index 6053c9a4f..8e7fadd26 100755 --- a/neutronclient/osc/v2/sfc/sfc_port_pair_group.py +++ b/neutronclient/osc/v2/sfc/sfc_port_pair_group.py @@ -188,9 +188,9 @@ def take_action(self, parsed_args): if parsed_args.no_port_pair: existing = [] else: - existing = [client.find_resource( + existing = client.find_resource( resource, parsed_args.port_pair_group, - cmd_resource='sfc_port_pair_group')['port_pairs']] + cmd_resource='sfc_port_pair_group')['port_pairs'] attrs['port_pairs'] = sorted(list(set(existing) | set(added))) body = {resource: attrs} try: @@ -250,12 +250,12 @@ def take_action(self, parsed_args): ppg_id = _get_id(client, parsed_args.port_pair_group, resource) attrs = {} if parsed_args.port_pairs: - existing = [client.find_resource( + existing = client.find_resource( resource, parsed_args.port_pair_group, - cmd_resource='sfc_port_pair_group')['port_pairs']] - for pp in parsed_args.port_pairs: - removed = [client.find_resource( - 'port_pair', pp, cmd_resource='sfc_port_pair')['id']] + cmd_resource='sfc_port_pair_group')['port_pairs'] + removed = [client.find_resource('port_pair', pp, + cmd_resource='sfc_port_pair')['id'] + for pp in parsed_args.port_pairs] attrs['port_pairs'] = list(set(existing) - set(removed)) if parsed_args.all_port_pair: attrs['port_pairs'] = [] diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_port_pair_group.py b/neutronclient/tests/unit/osc/v2/sfc/test_port_pair_group.py index c59e96194..48cee1ed9 100755 --- a/neutronclient/tests/unit/osc/v2/sfc/test_port_pair_group.py +++ b/neutronclient/tests/unit/osc/v2/sfc/test_port_pair_group.py @@ -293,7 +293,7 @@ def _mock_port_pair_group(*args, **kwargs): if self.neutronclient.find_resource.call_count == 3: self.neutronclient.find_resource.assert_called_with( self.res, target, cmd_resource='sfc_port_pair_group') - return {'port_pairs': self.ppg_pp} + return {'port_pairs': [self.ppg_pp]} self.neutronclient.find_resource.side_effect = _mock_port_pair_group @@ -423,7 +423,7 @@ def _mock_port_pair(*args, **kwargs): if self.neutronclient.find_resource.call_count == 1: self.neutronclient.find_resource.assert_called_with( self.res, target, cmd_resource='sfc_port_pair_group') - return {'port_pairs': self.ppg_pp} + return {'port_pairs': [self.ppg_pp]} if self.neutronclient.find_resource.call_count == 2: self.neutronclient.find_resource.assert_called_with( From e9df94319ff73651cfa5b251b913e8f9b591cbe7 Mon Sep 17 00:00:00 2001 From: Tovin Seven Date: Fri, 20 Apr 2018 17:22:27 +0700 Subject: [PATCH 678/845] Trivial: Update pypi url to new url Pypi url changed from [1] to [2] [1] https://pypi.python.org/pypi/ [2] https://pypi.org/project/ Change-Id: I8a94ae70c638927f113d9a648f8cd5df41474bba --- README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 6cccade14..c8e5dbf8e 100644 --- a/README.rst +++ b/README.rst @@ -11,11 +11,11 @@ Python bindings to the Neutron API ================================== .. image:: https://img.shields.io/pypi/v/python-neutronclient.svg - :target: https://pypi.python.org/pypi/python-neutronclient/ + :target: https://pypi.org/project/python-neutronclient/ :alt: Latest Version .. image:: https://img.shields.io/pypi/dm/python-neutronclient.svg - :target: https://pypi.python.org/pypi/python-neutronclient/ + :target: https://pypi.org/project/python-neutronclient/ :alt: Downloads This is a client library for Neutron built on the Neutron API. It @@ -31,7 +31,7 @@ provides a Python API (the ``neutronclient`` module) and a command-line tool * `Source`_ * `Developer's Guide`_ -.. _PyPi: https://pypi.python.org/pypi/python-neutronclient +.. _PyPi: https://pypi.org/project/python-neutronclient .. _Online Documentation: https://docs.openstack.org/python-neutronclient/latest/ .. _Launchpad project: https://launchpad.net/python-neutronclient .. _Blueprints: https://blueprints.launchpad.net/python-neutronclient From d090ea27510db3903e3256ca518b645987edfdc7 Mon Sep 17 00:00:00 2001 From: Chen Date: Thu, 7 Jun 2018 22:38:41 +0800 Subject: [PATCH 679/845] Remove PyPI downloads According to official site, https://packaging.python.org/guides/analyzing-pypi-package-downloads/ PyPI package download statistics is no longer maintained and thus should be removed. Change-Id: I190d233eaaea8e445e2d0c9b98ee81cb8cf08fab --- README.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.rst b/README.rst index c8e5dbf8e..3e39d8f9c 100644 --- a/README.rst +++ b/README.rst @@ -14,10 +14,6 @@ Python bindings to the Neutron API :target: https://pypi.org/project/python-neutronclient/ :alt: Latest Version -.. image:: https://img.shields.io/pypi/dm/python-neutronclient.svg - :target: https://pypi.org/project/python-neutronclient/ - :alt: Downloads - This is a client library for Neutron built on the Neutron API. It provides a Python API (the ``neutronclient`` module) and a command-line tool (``neutron``). From 5a6b75655d1ea88a63fae55daa64abcf2e3376a9 Mon Sep 17 00:00:00 2001 From: XiaojueGuan Date: Tue, 12 Jun 2018 16:17:56 +0800 Subject: [PATCH 680/845] Update links in README Change the outdated links to the latest links in README Change-Id: I70fbe513066b9375344e80308c646e56bf927db0 --- README.rst | 4 ++-- doc/source/contributor/transition_to_osc.rst | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 3e39d8f9c..8c7f1751d 100644 --- a/README.rst +++ b/README.rst @@ -2,8 +2,8 @@ Team and repository tags ======================== -.. image:: http://governance.openstack.org/badges/python-neutronclient.svg - :target: http://governance.openstack.org/reference/tags/index.html +.. image:: https://governance.openstack.org/tc/badges/python-neutronclient.svg + :target: https://governance.openstack.org/tc/reference/tags/index.html .. Change things from this point on diff --git a/doc/source/contributor/transition_to_osc.rst b/doc/source/contributor/transition_to_osc.rst index 65889afa0..d64658d06 100644 --- a/doc/source/contributor/transition_to_osc.rst +++ b/doc/source/contributor/transition_to_osc.rst @@ -151,7 +151,7 @@ dual maintenance. **Where does my CLI belong?** -If you are developing an API in any of the `neutron repos `_ +If you are developing an API in any of the `neutron repos `_ the client-side support must be generally located in either the openstackclient or neutronclient repos. Whether the actual code goes into one or the other repo it depends on the nature of the feature, its maturity level, and/or the depth of feedback required during the development. From 834ad176ea34d4d64d4f2a15ac5cc2c0e25c0343 Mon Sep 17 00:00:00 2001 From: jessegler Date: Wed, 13 Jun 2018 15:05:52 -0500 Subject: [PATCH 681/845] Add bandit to pep8 gate Neutron uses bandit to detect security issues. This patch adds bandit to the pep8 gate to automatically lint for security issues in python-neutronclient. Change-Id: Ifd8caf65cc89e7d6d6ebc8f58539741cfbab839b --- test-requirements.txt | 1 + tox.ini | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index 58c378ae7..eb3bcf118 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -3,6 +3,7 @@ # process, which may cause wedges in the gate later. hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0 +bandit>=1.1.0 # Apache-2.0 coverage!=4.4,>=4.0 # Apache-2.0 fixtures>=3.0.0 # Apache-2.0/BSD flake8-import-order==0.12 # LGPLv3 diff --git a/tox.ini b/tox.ini index 2d3cd408d..d534b0cb6 100644 --- a/tox.ini +++ b/tox.ini @@ -25,7 +25,9 @@ commands = sh -c "find . -type d -name '.?*' -prune -o \ whitelist_externals = sh [testenv:pep8] -commands = flake8 +commands = + flake8 + {[testenv:bandit]commands} distribute = false [testenv:venv] @@ -62,6 +64,10 @@ import-order-style = pep8 # H904: Delay string interpolations at logging calls enable-extensions=H904 +[testenv:bandit] +deps = -r{toxinidir}/test-requirements.txt +commands = bandit -r neutronclient -x tests -n5 + [testenv:lower-constraints] basepython = python3 deps = From 61056c45532893b99238853acdddb2159a83ffa1 Mon Sep 17 00:00:00 2001 From: "wu.chunyang" Date: Thu, 28 Jun 2018 13:15:55 +0800 Subject: [PATCH 682/845] Add release note link in README Change-Id: Idcd559d5f8c6f991e73d7d10339fb923f2a931b2 --- README.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/README.rst b/README.rst index 8c7f1751d..cec75c908 100644 --- a/README.rst +++ b/README.rst @@ -34,3 +34,4 @@ provides a Python API (the ``neutronclient`` module) and a command-line tool .. _Bugs: https://bugs.launchpad.net/python-neutronclient .. _Source: https://git.openstack.org/cgit/openstack/python-neutronclient .. _Developer's Guide: http://docs.openstack.org/infra/manual/developers.html +.. _Release Notes: https://docs.openstack.org/releasenotes/python-neutronclient From a8106b1c804e749235df6994595f721791251147 Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Wed, 8 Aug 2018 09:35:46 +0000 Subject: [PATCH 683/845] Update reno for stable/rocky Change-Id: I330ee35255f6afcb1e90a570ee5f9345dc088187 --- releasenotes/source/index.rst | 1 + releasenotes/source/rocky.rst | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 releasenotes/source/rocky.rst diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index 195fe6ee8..f73e97659 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -6,6 +6,7 @@ :maxdepth: 1 unreleased + rocky queens pike ocata diff --git a/releasenotes/source/rocky.rst b/releasenotes/source/rocky.rst new file mode 100644 index 000000000..40dd517b7 --- /dev/null +++ b/releasenotes/source/rocky.rst @@ -0,0 +1,6 @@ +=================================== + Rocky Series Release Notes +=================================== + +.. release-notes:: + :branch: stable/rocky From bca82e2fba9505f1bffdd5cb4aa23f4bb3e05f84 Mon Sep 17 00:00:00 2001 From: Yushiro FURUKAWA Date: Tue, 24 Jul 2018 23:39:59 +0900 Subject: [PATCH 684/845] Fix broken unittests _clean() method is renamed to clean() in osprofiler 2.3.0. It was suggested in a past neutronclient review. Closes-Bug: #1783789 Co-Authored-By: Akihiro Motoki Depends-On: https://review.openstack.org/#/c/592018/ Change-Id: Ic8e03db85dc08cfdcac5507e99ecdf7eac8eb972 --- lower-constraints.txt | 2 +- neutronclient/tests/unit/test_http.py | 2 +- test-requirements.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lower-constraints.txt b/lower-constraints.txt index 81394e21d..2bf9a3198 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -63,7 +63,7 @@ oslo.serialization==2.18.0 oslo.service==1.24.0 oslo.utils==3.33.0 oslotest==3.2.0 -osprofiler==1.4.0 +osprofiler==2.3.0 paramiko==2.0.0 Paste==2.0.2 PasteDeploy==1.5.0 diff --git a/neutronclient/tests/unit/test_http.py b/neutronclient/tests/unit/test_http.py index 21b615592..d76e9bce9 100644 --- a/neutronclient/tests/unit/test_http.py +++ b/neutronclient/tests/unit/test_http.py @@ -77,7 +77,7 @@ def test_headers_defined_in_headers(self): def test_osprofiler_headers_are_injected(self): osprofiler.profiler.init('SWORDFISH') - self.addCleanup(osprofiler.profiler._clean) + self.addCleanup(osprofiler.profiler.clean) headers = {'Accept': 'application/json'} headers.update(osprofiler.web.get_trace_id_headers()) diff --git a/test-requirements.txt b/test-requirements.txt index eb3bcf118..77a33e6ca 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -9,7 +9,7 @@ fixtures>=3.0.0 # Apache-2.0/BSD flake8-import-order==0.12 # LGPLv3 mock>=2.0.0 # BSD oslotest>=3.2.0 # Apache-2.0 -osprofiler>=1.4.0 # Apache-2.0 +osprofiler>=2.3.0 # Apache-2.0 python-openstackclient>=3.12.0 # Apache-2.0 python-subunit>=1.0.0 # Apache-2.0/BSD requests-mock>=1.2.0 # Apache-2.0 From bcf895cbe383632dfb7f9e2770203a56438473ad Mon Sep 17 00:00:00 2001 From: Kim Bao Long Date: Thu, 23 Aug 2018 17:13:54 +0700 Subject: [PATCH 685/845] Add B303 into list of skipped bandit plugins Blacklist call of hashlib.sha1 was blacklisted in bandit with [1] and it is now added to list of skipped tests in Neutron Client. [1] PyCQA/bandit@35e3544 Change-Id: I8322d0666528323df8b1214306be4b80c4b0777a --- tox.ini | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index d534b0cb6..8d6eb99e6 100644 --- a/tox.ini +++ b/tox.ini @@ -65,8 +65,9 @@ import-order-style = pep8 enable-extensions=H904 [testenv:bandit] +# B303: blacklist calls: md5, sha1 deps = -r{toxinidir}/test-requirements.txt -commands = bandit -r neutronclient -x tests -n5 +commands = bandit -r neutronclient -x tests -n5 -s B303 [testenv:lower-constraints] basepython = python3 From 80f916489b7031e1804e91521c554013d8b93cfe Mon Sep 17 00:00:00 2001 From: Kim Bao Long Date: Mon, 2 Jul 2018 14:00:36 +0700 Subject: [PATCH 686/845] [log] Add 'firewall_group' as a loggable resource type for logging Currently, OSC plugin for logging only supports security group as a loggable resource. This patch aims to add 'firewall_group' as a supported resource type. Co-Authored-By: Van Hung Pham Depends-On: Ie10063197f02679e987e87cb4852f5230a02f76d Change-Id: I2ac92004c2ad3769c9749e131718a8ef0003c4bd Partial-Bug: #1720727 --- neutronclient/osc/v2/logging/network_log.py | 44 ++++++--- .../unit/osc/v2/logging/test_network_log.py | 93 ++++++++++++++++++- ...-group-resource-type-5ad1b69cabcb4aa6.yaml | 5 + 3 files changed, 126 insertions(+), 16 deletions(-) create mode 100644 releasenotes/notes/support-firewall-group-resource-type-5ad1b69cabcb4aa6.yaml diff --git a/neutronclient/osc/v2/logging/network_log.py b/neutronclient/osc/v2/logging/network_log.py index 534e48d82..1946a4066 100644 --- a/neutronclient/osc/v2/logging/network_log.py +++ b/neutronclient/osc/v2/logging/network_log.py @@ -1,4 +1,4 @@ -# Copyright 2017 FUJTISU LIMITED. +# Copyright 2017-2018 FUJTISU LIMITED. # All Rights Reserved # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -25,7 +25,7 @@ from neutronclient._i18n import _ from neutronclient.common import utils as nc_utils from neutronclient.osc import utils as osc_utils - +from neutronclient.osc.v2.fwaas import constants as fwaas_const LOG = logging.getLogger(__name__) @@ -77,17 +77,23 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): parsed_args.project, parsed_args.project_domain, ).id + resource_type = parsed_args.resource_type + attrs['resource_type'] = resource_type if parsed_args.resource: + cmd_resource = None + if resource_type == fwaas_const.FWG: + cmd_resource = fwaas_const.CMD_FWG attrs['resource_id'] = client.find_resource( - 'security_group', parsed_args.resource)['id'] + resource_type, + parsed_args.resource, + cmd_resource=cmd_resource)['id'] + if parsed_args.target: # NOTE(yushiro) Currently, we're supporting only port attrs['target_id'] = client.find_resource( 'port', parsed_args.target)['id'] if parsed_args.event: attrs['event'] = parsed_args.event - if parsed_args.resource_type: - attrs['resource_type'] = parsed_args.resource_type if parsed_args.enable: attrs['enabled'] = True if parsed_args.disable: @@ -117,8 +123,8 @@ def get_parser(self, prog_name): type=nc_utils.convert_to_uppercase, help=_('An event to store with log')) # NOTE(yushiro) '--resource-type' is managed by following command: - # "openstack network loggable resource list". Therefore, this option - # shouldn't have "choices" like ['security_group'] + # "openstack network loggable resources list". Therefore, this option + # shouldn't have "choices" like ['security_group', 'firewall_group'] parser.add_argument( '--resource-type', metavar='', @@ -126,12 +132,13 @@ def get_parser(self, prog_name): type=nc_utils.convert_to_lowercase, help=_('Network log type(s). ' 'You can see supported type(s) with following command:\n' - '$ openstack network loggable resource list')) + '$ openstack network loggable resources list')) parser.add_argument( '--resource', metavar='', - help=_('Security group (name or ID) for logging. You can control ' - 'for logging target combination with --target option.')) + help=_('Name or ID of resource (security group or firewall group) ' + 'that used for logging. You can control for logging target ' + 'combination with --target option.')) parser.add_argument( '--target', metavar='', @@ -171,13 +178,13 @@ def take_action(self, parsed_args): except Exception as e: result += 1 LOG.error(_("Failed to delete network log with " - "name or ID '%(network_log)s': %(e)s"), + "name or ID '%(network_log)s': %(e)s"), {'network_log': log_res, 'e': e}) if result > 0: total = len(parsed_args.network_log) msg = (_("%(result)s of %(total)s network log(s) " - "failed to delete") % {'result': result, 'total': total}) + "failed to delete") % {'result': result, 'total': total}) raise exceptions.CommandError(msg) @@ -221,10 +228,17 @@ def _extend_list(self, data, parsed_args): if d['event']: event = e_prefix + d['event'].upper() port = '(port) ' + d['target_id'] if d['target_id'] else '' - sg = ('(security_group) ' + d['resource_id'] - if d['resource_id'] else '') + resource_type = d['resource_type'] + if d['resource_id']: + res = '(%s) %s' % (resource_type, d['resource_id']) + else: + res = '' t_prefix = 'Logged: ' - t = sg + ' on ' + port if port and sg else sg + port + if port and res: + t = '%s on %s' % (res, port) + else: + # Either of res and port is empty, so concatenation works fine + t = res + port target = t_prefix + t if t else t_prefix + '(None specified)' d['summary'] = ',\n'.join([event, target]) return ext_data diff --git a/neutronclient/tests/unit/osc/v2/logging/test_network_log.py b/neutronclient/tests/unit/osc/v2/logging/test_network_log.py index bceaf32e1..43c0bcbfe 100644 --- a/neutronclient/tests/unit/osc/v2/logging/test_network_log.py +++ b/neutronclient/tests/unit/osc/v2/logging/test_network_log.py @@ -1,4 +1,4 @@ -# Copyright 2017 FUJITSU LIMITED +# Copyright 2017-2018 FUJITSU LIMITED # All Rights Reserved # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -29,6 +29,7 @@ _log = fakes.NetworkLog().create() RES_TYPE_SG = 'security_group' +RES_TYPE_FWG = 'firewall_group' CONVERT_MAP = { 'project': 'project_id', 'enable': 'enabled', @@ -137,6 +138,12 @@ def setUp(self): return_value={'log': _log}) self.mocked = self.neutronclient.create_network_log self.cmd = network_log.CreateNetworkLog(self.app, self.namespace) + loggables = { + "loggable_resources": [{"type": RES_TYPE_SG, + "type": RES_TYPE_FWG}] + } + self.neutronclient.list_network_loggable_resources = mock.Mock( + return_value=loggables) def _update_expect_response(self, request, response): """Set expected request and response @@ -279,6 +286,90 @@ def test_create_with_all_params_resource_type_upper_capitalized(self): testtools.matchers._impl.MismatchError, self.check_parser, self.cmd, arglist, verifylist) + def test_create_with_valid_fwg_resource(self): + name = self.res['name'] + resource_id = 'valid_fwg_id' + resource_type = RES_TYPE_FWG + # Test with valid FWG ID + with mock.patch.object(self.neutronclient, 'find_resource', + return_value={'id': resource_id}): + arglist = [name, + '--resource-type', resource_type, + '--resource', resource_id + ] + verifylist = [ + ('name', name), + ('resource_type', resource_type), + ('resource', resource_id) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + expect = { + 'name': self.res['name'], + 'resource_type': RES_TYPE_FWG, + 'resource_id': 'valid_fwg_id', + } + self.neutronclient.find_resource.assert_called_with( + resource_type, + resource_id, + cmd_resource='fwaas_firewall_group') + self.mocked.assert_called_once_with({'log': expect}) + self.assertEqual(self.ordered_headers, headers) + self.assertEqual(self.ordered_data, data) + + def test_create_with_invalid_fwg_resource(self): + name = self.res['name'] + resource_id = 'invalid_fwg_id' + resource_type = RES_TYPE_FWG + # Test with invalid FWG ID + with mock.patch.object(self.neutronclient, 'find_resource', + side_effect=exceptions.NotFound(code=0)): + arglist = [name, + '--resource-type', resource_type, + '--resource', resource_id + ] + verifylist = [ + ('name', name), + ('resource_type', resource_type), + ('resource', resource_id) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises(exceptions.NotFound, + self.cmd.take_action, + parsed_args) + self.neutronclient.find_resource.assert_called_with( + resource_type, + resource_id, + cmd_resource='fwaas_firewall_group') + self.mocked.assert_not_called() + + def test_create_with_invalid_resource_type(self): + name = self.res['name'] + resource_type = 'invalid_resource_type' + resource_id = 'valid_fwg_id' + with mock.patch.object(self.neutronclient, 'find_resource', + side_effect=exceptions.NotFound(code=0)): + arglist = [name, + '--resource-type', resource_type, + '--resource', resource_id + ] + verifylist = [ + ('name', name), + ('resource_type', resource_type), + ('resource', resource_id) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises(exceptions.NotFound, + self.cmd.take_action, + parsed_args) + self.neutronclient.find_resource.assert_called_with( + resource_type, + resource_id, + cmd_resource=None) + self.mocked.assert_not_called() + class TestListNetworkLog(TestNetworkLog): diff --git a/releasenotes/notes/support-firewall-group-resource-type-5ad1b69cabcb4aa6.yaml b/releasenotes/notes/support-firewall-group-resource-type-5ad1b69cabcb4aa6.yaml new file mode 100644 index 000000000..78fbff8fd --- /dev/null +++ b/releasenotes/notes/support-firewall-group-resource-type-5ad1b69cabcb4aa6.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + CLI support for the "firewal_group" as a loggable resource type for logging + feature, which is enhanced FWaaS functionality, as OSC plugin commands. From f0640571d1d32210c6121763e4222069e6d539f8 Mon Sep 17 00:00:00 2001 From: Doug Hellmann Date: Wed, 6 Jun 2018 17:58:18 -0400 Subject: [PATCH 687/845] fix tox python3 overrides We want to default to running all tox environments under python 3, so set the basepython value in each environment. We do not want to specify a minor version number, because we do not want to have to update the file every time we upgrade python. We do not want to set the override once in testenv, because that breaks the more specific versions used in default environments like py35 and py36. Co-Authored-By: Nguyen Hai Change-Id: I6f4a539c68b036474be4ba433cb28293c0ce94e2 Signed-off-by: Doug Hellmann --- tox.ini | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tox.ini b/tox.ini index 8d6eb99e6..e40ac3e67 100644 --- a/tox.ini +++ b/tox.ini @@ -25,12 +25,14 @@ commands = sh -c "find . -type d -name '.?*' -prune -o \ whitelist_externals = sh [testenv:pep8] +basepython = python3 commands = flake8 {[testenv:bandit]commands} distribute = false [testenv:venv] +basepython = python3 commands = {posargs} [testenv:functional] @@ -44,19 +46,23 @@ setenv = OS_NEUTRONCLIENT_EXEC_DIR = {envdir}/bin [testenv:cover] +basepython = python3 commands = python setup.py test --coverage --coverage-package-name=neutronclient --testr-args='{posargs}' coverage report [testenv:docs] +basepython = python3 deps = -r{toxinidir}/doc/requirements.txt commands = sphinx-build -W -b html doc/source doc/build/html [testenv:releasenotes] +basepython = python3 deps = -r{toxinidir}/doc/requirements.txt commands = sphinx-build -a -E -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html [flake8] +basepython = python3 show-source = true exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,tools import-order-style = pep8 @@ -65,6 +71,7 @@ import-order-style = pep8 enable-extensions=H904 [testenv:bandit] +basepython = python3 # B303: blacklist calls: md5, sha1 deps = -r{toxinidir}/test-requirements.txt commands = bandit -r neutronclient -x tests -n5 -s B303 From 01ec2cc94d942cd0465f1561fcc552f7e6c4db04 Mon Sep 17 00:00:00 2001 From: Doug Hellmann Date: Wed, 29 Aug 2018 17:38:37 -0400 Subject: [PATCH 688/845] import zuul job settings from project-config This is a mechanically generated patch to complete step 1 of moving the zuul job settings out of project-config and into each project repository. Because there will be a separate patch on each branch, the branch specifiers for branch-specific jobs have been removed. Because this patch is generated by a script, there may be some cosmetic changes to the layout of the YAML file(s) as the contents are normalized. See the python3-first goal document for details: https://governance.openstack.org/tc/goals/stein/python3-first.html Change-Id: Id183d2dc3b6fa1dd608b0489c609353eb2e35f6d Story: #2002586 Task: #24314 --- .zuul.yaml | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/.zuul.yaml b/.zuul.yaml index 67a39c429..8dbbf0056 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -1,7 +1,49 @@ - project: + templates: + - openstack-python-jobs + - openstack-python35-jobs + - publish-openstack-sphinx-docs + - check-requirements + - lib-forward-testing + - release-notes-jobs + - openstackclient-plugin-jobs check: jobs: - openstack-tox-lower-constraints + - legacy-neutronclient-test-dsvm-functional: + irrelevant-files: + - ^.*\.rst$ + - ^doc/.*$ + - ^neutron/locale/.*$ + - ^releasenotes/.*$ + - legacy-neutronclient-test-dsvm-functional-adv-svcs: + irrelevant-files: + - ^.*\.rst$ + - ^doc/.*$ + - ^neutron/locale/.*$ + - ^releasenotes/.*$ gate: jobs: - openstack-tox-lower-constraints + - legacy-neutronclient-test-dsvm-functional: + irrelevant-files: + - ^.*\.rst$ + - ^doc/.*$ + - ^neutron/locale/.*$ + - ^releasenotes/.*$ + - legacy-neutronclient-test-dsvm-functional-adv-svcs: + irrelevant-files: + - ^.*\.rst$ + - ^doc/.*$ + - ^neutron/locale/.*$ + - ^releasenotes/.*$ + post: + jobs: + - openstack-tox-cover + experimental: + jobs: + - legacy-grenade-dsvm-neutron-libs: + irrelevant-files: + - ^(test-|)requirements.txt$ + - ^setup.cfg$ + From 124dd33aad2bfb28e5227066de8036cfbfeb12a7 Mon Sep 17 00:00:00 2001 From: Doug Hellmann Date: Wed, 29 Aug 2018 17:38:45 -0400 Subject: [PATCH 689/845] switch documentation job to new PTI This is a mechanically generated patch to switch the documentation jobs to use the new PTI versions of the jobs as part of the python3-first goal. See the python3-first goal document for details: https://governance.openstack.org/tc/goals/stein/python3-first.html Change-Id: I4dc74177263a038491bb03521c99d6f43951bd28 Story: #2002586 Task: #24314 --- .zuul.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.zuul.yaml b/.zuul.yaml index 8dbbf0056..814ec183f 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -2,10 +2,10 @@ templates: - openstack-python-jobs - openstack-python35-jobs - - publish-openstack-sphinx-docs + - publish-openstack-docs-pti - check-requirements - lib-forward-testing - - release-notes-jobs + - release-notes-jobs-python3 - openstackclient-plugin-jobs check: jobs: From bec7dd09b9044e4741e56df9252fdbef0621d073 Mon Sep 17 00:00:00 2001 From: Doug Hellmann Date: Wed, 29 Aug 2018 17:39:00 -0400 Subject: [PATCH 690/845] add python 3.6 unit test job This is a mechanically generated patch to add a unit test job running under Python 3.6 as part of the python3-first goal. See the python3-first goal document for details: https://governance.openstack.org/tc/goals/stein/python3-first.html Change-Id: I857b5836273045b48d9706673a6455fed9f18541 Story: #2002586 Task: #24314 --- .zuul.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.zuul.yaml b/.zuul.yaml index 814ec183f..7e58f90b2 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -2,6 +2,7 @@ templates: - openstack-python-jobs - openstack-python35-jobs + - openstack-python36-jobs - publish-openstack-docs-pti - check-requirements - lib-forward-testing From d5a89fe5a0be7067352709c812821aa71e0e7b99 Mon Sep 17 00:00:00 2001 From: Doug Hellmann Date: Wed, 29 Aug 2018 17:39:07 -0400 Subject: [PATCH 691/845] add lib-forward-testing-python3 test job This is a mechanically generated patch to add a functional test job running under Python 3 as part of the python3-first goal. See the python3-first goal document for details: https://governance.openstack.org/tc/goals/stein/python3-first.html Change-Id: Idaaa376c0a8685f1999d8d96add0cfd25a2bb8eb Story: #2002586 Task: #24314 --- .zuul.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.zuul.yaml b/.zuul.yaml index 7e58f90b2..3eba2db89 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -6,6 +6,7 @@ - publish-openstack-docs-pti - check-requirements - lib-forward-testing + - lib-forward-testing-python3 - release-notes-jobs-python3 - openstackclient-plugin-jobs check: From 0aefe1ccba63faa0d0e7131bbbd3331741e3ec2e Mon Sep 17 00:00:00 2001 From: Mykola Yakovliev Date: Fri, 8 Jun 2018 14:35:36 -0500 Subject: [PATCH 692/845] Ensure API calls for subnets are in URL length limit Fix situation when with pagination enabled, neutronclient failed to read subnets information due to too long URI. Change-Id: I53240c536d77a95510b5c83b81e21782f29d886a Closes-Bug: 1775922 --- neutronclient/neutron/v2_0/network.py | 5 +++++ neutronclient/tests/unit/test_cli20_network.py | 8 ++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/neutronclient/neutron/v2_0/network.py b/neutronclient/neutron/v2_0/network.py index 3ec3ac6d5..6c68b623a 100644 --- a/neutronclient/neutron/v2_0/network.py +++ b/neutronclient/neutron/v2_0/network.py @@ -44,6 +44,9 @@ class ListNetwork(neutronV20.ListCommand): # Length of a query filter on subnet id # id=& (with len(uuid)=36) subnet_id_filter_len = 40 + # Length of a marker in pagination + # &marker= (with len(uuid)=36) + marker_len = 44 resource = 'network' _formatters = {'subnets': _format_subnets, } list_columns = ['id', 'name', 'subnets'] @@ -115,6 +118,8 @@ def _get_subnet_list(sub_ids): subnet_count = len(subnet_ids) max_size = ((self.subnet_id_filter_len * subnet_count) - uri_len_exc.excess) + if self.pagination_support: + max_size -= self.marker_len chunk_size = max_size // self.subnet_id_filter_len subnets = [] for i in range(0, subnet_count, chunk_size): diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index 230c5d49e..2f7a46896 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -653,8 +653,12 @@ def test_extend_list_exceed_max_uri_len(self): data = [{'id': 'netid%d' % i, 'name': 'net%d' % i, 'subnets': ['mysubid%d' % i]} for i in range(10)] - filters1, response1 = self._build_test_data(data[:len(data) - 1]) - filters2, response2 = self._build_test_data(data[len(data) - 1:]) + # Since in pagination we add &marker= (44 symbols), total change + # is 45 symbols. Single subnet takes 40 symbols (id=&). + # Because of it marker will take more space than single subnet filter, + # and we expect neutron to send last 2 subnets in separate response. + filters1, response1 = self._build_test_data(data[:len(data) - 2]) + filters2, response2 = self._build_test_data(data[len(data) - 2:]) path = getattr(self.client, 'subnets_path') cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) with mock.patch.object(cmd, "get_client", From 259c386d424370160c861748ccd18f0c08452d3c Mon Sep 17 00:00:00 2001 From: Vu Cong Tuan Date: Tue, 10 Jul 2018 13:34:43 +0700 Subject: [PATCH 693/845] Switch to stestr According to Openstack summit session [1], stestr is maintained project to which all Openstack projects should migrate. Let's switch to stestr as other projects have already moved to it. [1] https://etherpad.openstack.org/p/YVR-python-pti Change-Id: Ib58745d5c0d98fb79eb3cf7278c2cbfb5160efcd --- .gitignore | 2 +- .stestr.conf | 3 +++ .testr.conf | 4 ---- lower-constraints.txt | 3 +-- .../tests/functional/hooks/post_test_hook.sh | 10 +++++----- test-requirements.txt | 2 +- tox.ini | 12 +++++++++--- 7 files changed, 20 insertions(+), 16 deletions(-) create mode 100644 .stestr.conf delete mode 100644 .testr.conf diff --git a/.gitignore b/.gitignore index 3e20723ad..abdf7b022 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,6 @@ run_tests.err.log run_tests.log .autogenerated .coverage -.testrepository/ +.stestr/ .tox/ .venv/ diff --git a/.stestr.conf b/.stestr.conf new file mode 100644 index 000000000..6a6b6f120 --- /dev/null +++ b/.stestr.conf @@ -0,0 +1,3 @@ +[DEFAULT] +test_path=./neutronclient/tests/unit +top_dir=./ diff --git a/.testr.conf b/.testr.conf deleted file mode 100644 index 01dee7036..000000000 --- a/.testr.conf +++ /dev/null @@ -1,4 +0,0 @@ -[DEFAULT] -test_command=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 ${PYTHON:-python} -m subunit.run discover -t ./ ${OS_TEST_PATH:-./neutronclient/tests/unit} $LISTOPT $IDOPTION -test_id_option=--load-list $IDFILE -test_list_option=--list diff --git a/lower-constraints.txt b/lower-constraints.txt index 2bf9a3198..117d04b2e 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -105,11 +105,10 @@ snowballstemmer==1.2.1 Sphinx==1.6.2 sphinxcontrib-websupport==1.0.1 statsd==3.2.1 -stestr==1.0.0 +stestr==2.0.0 stevedore==1.20.0 tempest==17.1.0 tenacity==3.2.1 -testrepository==0.0.18 testscenarios==0.4 testtools==2.2.0 traceback2==1.4.0 diff --git a/neutronclient/tests/functional/hooks/post_test_hook.sh b/neutronclient/tests/functional/hooks/post_test_hook.sh index 43cc9c4b7..d62d13911 100755 --- a/neutronclient/tests/functional/hooks/post_test_hook.sh +++ b/neutronclient/tests/functional/hooks/post_test_hook.sh @@ -32,11 +32,11 @@ function generate_test_logs { function generate_testr_results { # Give job user rights to access tox logs sudo -H -u $USER chmod o+rw . - sudo -H -u $USER chmod o+rw -R .testrepository - if [ -f ".testrepository/0" ] ; then - .tox/$VENV/bin/subunit-1to2 < .testrepository/0 > ./testrepository.subunit - $SCRIPTS_DIR/subunit2html ./testrepository.subunit testr_results.html - gzip -9 ./testrepository.subunit + sudo -H -u $USER chmod o+rw -R .stestr + if [ -f ".stestr/0" ] ; then + .tox/$VENV/bin/subunit-1to2 < .stestr/0 > ./stestr.subunit + $SCRIPTS_DIR/subunit2html ./stestr.subunit testr_results.html + gzip -9 ./stestr.subunit gzip -9 ./testr_results.html sudo mv ./*.gz /opt/stack/logs/ fi diff --git a/test-requirements.txt b/test-requirements.txt index 77a33e6ca..11865b828 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -13,7 +13,7 @@ osprofiler>=2.3.0 # Apache-2.0 python-openstackclient>=3.12.0 # Apache-2.0 python-subunit>=1.0.0 # Apache-2.0/BSD requests-mock>=1.2.0 # Apache-2.0 -testrepository>=0.0.18 # Apache-2.0/BSD +stestr>=2.0.0 # Apache-2.0 testtools>=2.2.0 # MIT testscenarios>=0.4 # Apache-2.0/BSD tempest>=17.1.0 # Apache-2.0 diff --git a/tox.ini b/tox.ini index e40ac3e67..76c62a262 100644 --- a/tox.ini +++ b/tox.ini @@ -21,7 +21,7 @@ deps = -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/re commands = sh -c "find . -type d -name '.?*' -prune -o \ \( -type d -name '__pycache__' -o -type f -name '*.py[co]' \) \ -print0 | xargs -0 rm -rf" - python setup.py testr --testr-args='{posargs}' + stestr run {posargs} whitelist_externals = sh [testenv:pep8] @@ -47,9 +47,15 @@ setenv = [testenv:cover] basepython = python3 +setenv = + {[testenv]setenv} + PYTHON=coverage run --source neutronclient --parallel-mode commands = - python setup.py test --coverage --coverage-package-name=neutronclient --testr-args='{posargs}' - coverage report + stestr run '{posargs}' + coverage combine + coverage html -d cover + coverage xml -o cover/coverage.xml + coverage report [testenv:docs] basepython = python3 From 1a1dbf09628b01d2837022548809187c168daf74 Mon Sep 17 00:00:00 2001 From: Brian Haley Date: Thu, 30 Aug 2018 15:12:11 -0400 Subject: [PATCH 694/845] Consolidate irrelevant files added for py3 project The autogenerated changes for the python3-first project duplicated irrelevant-files. This consolidates them. Change-Id: Ib63fe59f28591947324708326d388954f2c3e0e9 --- .zuul.yaml | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/.zuul.yaml b/.zuul.yaml index 3eba2db89..7babbde37 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -13,32 +13,20 @@ jobs: - openstack-tox-lower-constraints - legacy-neutronclient-test-dsvm-functional: - irrelevant-files: + irrelevant-files: &project-irrelevant-files - ^.*\.rst$ - ^doc/.*$ - ^neutron/locale/.*$ - ^releasenotes/.*$ - legacy-neutronclient-test-dsvm-functional-adv-svcs: - irrelevant-files: - - ^.*\.rst$ - - ^doc/.*$ - - ^neutron/locale/.*$ - - ^releasenotes/.*$ + irrelevant-files: *project-irrelevant-files gate: jobs: - openstack-tox-lower-constraints - legacy-neutronclient-test-dsvm-functional: - irrelevant-files: - - ^.*\.rst$ - - ^doc/.*$ - - ^neutron/locale/.*$ - - ^releasenotes/.*$ + irrelevant-files: *project-irrelevant-files - legacy-neutronclient-test-dsvm-functional-adv-svcs: - irrelevant-files: - - ^.*\.rst$ - - ^doc/.*$ - - ^neutron/locale/.*$ - - ^releasenotes/.*$ + irrelevant-files: *project-irrelevant-files post: jobs: - openstack-tox-cover From 86a67409e6d55a34cfcaa6d9db051fc2ee4ee72f Mon Sep 17 00:00:00 2001 From: Andreas Jaeger Date: Fri, 7 Sep 2018 15:17:34 +0200 Subject: [PATCH 695/845] Use templates for cover and lower-constraints Use openstack-tox-cover template, this runs the cover job in the check queue only. Use openstack-lower-constraints-jobs template Remove jobs that are part of the templates. Change-Id: I8c870f127f988808f3f9b1044ea1d9ddec48b866 --- .zuul.yaml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.zuul.yaml b/.zuul.yaml index 7babbde37..7fde244be 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -1,5 +1,7 @@ - project: templates: + - openstack-cover-jobs + - openstack-lower-constraints-jobs - openstack-python-jobs - openstack-python35-jobs - openstack-python36-jobs @@ -11,7 +13,6 @@ - openstackclient-plugin-jobs check: jobs: - - openstack-tox-lower-constraints - legacy-neutronclient-test-dsvm-functional: irrelevant-files: &project-irrelevant-files - ^.*\.rst$ @@ -22,14 +23,10 @@ irrelevant-files: *project-irrelevant-files gate: jobs: - - openstack-tox-lower-constraints - legacy-neutronclient-test-dsvm-functional: irrelevant-files: *project-irrelevant-files - legacy-neutronclient-test-dsvm-functional-adv-svcs: irrelevant-files: *project-irrelevant-files - post: - jobs: - - openstack-tox-cover experimental: jobs: - legacy-grenade-dsvm-neutron-libs: From a385656802e3b22ad3ff6b31e7a1bedc497fb9e1 Mon Sep 17 00:00:00 2001 From: German Eichberger Date: Wed, 30 May 2018 16:19:14 -0700 Subject: [PATCH 696/845] Adds the destination and source fwg to fwr This will add the source and destination firewall group id to firewall rules. Change-Id: If69b87fc58d36aa7d879ace8e73bc0f7534a1ef4 --- neutronclient/osc/v2/fwaas/firewallrule.py | 49 +++++++ .../tests/unit/osc/v2/fwaas/fakes.py | 4 + .../unit/osc/v2/fwaas/test_firewallrule.py | 131 +++++++++++++++++- .../notes/remote_fwg-0f5362e5be8b2e84.yaml | 5 + 4 files changed, 188 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/remote_fwg-0f5362e5be8b2e84.yaml diff --git a/neutronclient/osc/v2/fwaas/firewallrule.py b/neutronclient/osc/v2/fwaas/firewallrule.py index 964e7e16e..f72beded7 100644 --- a/neutronclient/osc/v2/fwaas/firewallrule.py +++ b/neutronclient/osc/v2/fwaas/firewallrule.py @@ -47,6 +47,10 @@ ('destination_port', 'Destination Port', column_util.LIST_LONG_ONLY), ('shared', 'Shared', column_util.LIST_LONG_ONLY), ('tenant_id', 'Project', column_util.LIST_LONG_ONLY), + ('source_firewall_group_id', 'Source Firewall Group ID', + column_util.LIST_LONG_ONLY), + ('destination_firewall_group_id', 'Destination Firewall Group ID', + column_util.LIST_LONG_ONLY), ) @@ -145,11 +149,30 @@ def _get_common_parser(parser): '--disable-rule', action='store_true', help=_('Disable this rule')) + src_fwg_group = parser.add_mutually_exclusive_group() + src_fwg_group.add_argument( + '--source-firewall-group', + metavar='', + help=_('Source firewall group (name or ID)')) + src_fwg_group.add_argument( + '--no-source-firewall-group', + action='store_true', + help=_('No associated destination firewall group')) + dst_fwg_group = parser.add_mutually_exclusive_group() + dst_fwg_group.add_argument( + '--destination-firewall-group', + metavar='', + help=_('Destination firewall group (name or ID)')) + dst_fwg_group.add_argument( + '--no-destination-firewall-group', + action='store_true', + help=_('No associated destination firewall group')) return parser def _get_common_attrs(client_manager, parsed_args, is_create=True): attrs = {} + client = client_manager.neutronclient if is_create: if 'project' in parsed_args and parsed_args.project is not None: attrs['tenant_id'] = osc_utils.find_project( @@ -193,6 +216,18 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): attrs['shared'] = True if parsed_args.no_share or parsed_args.private: attrs['shared'] = False + if parsed_args.source_firewall_group: + attrs['source_firewall_group_id'] = client.find_resource( + const.FWG, parsed_args.source_firewall_group, + cmd_resource=const.CMD_FWG)['id'] + if parsed_args.no_source_firewall_group: + attrs['source_firewall_group_id'] = None + if parsed_args.destination_firewall_group: + attrs['destination_firewall_group_id'] = client.find_resource( + const.FWG, parsed_args.destination_firewall_group, + cmd_resource=const.CMD_FWG)['id'] + if parsed_args.no_destination_firewall_group: + attrs['destination_firewall_group_id'] = None return attrs @@ -391,6 +426,16 @@ def get_parser(self, prog_name): '--enable-rule', action='store_true', help=_('Disable this rule')) + + parser.add_argument( + '--source-firewall-group', + action='store_true', + help=_('Source firewall group (name or ID)')) + + parser.add_argument( + '--destination-firewall-group', + action='store_true', + help=_('Destination firewall group (name or ID)')) return parser def _get_attrs(self, client_manager, parsed_args): @@ -407,6 +452,10 @@ def _get_attrs(self, client_manager, parsed_args): attrs['shared'] = False if parsed_args.enable_rule: attrs['enabled'] = False + if parsed_args.source_firewall_group: + attrs['source_firewall_group_id'] = None + if parsed_args.source_firewall_group: + attrs['destination_firewall_group_id'] = None return attrs def take_action(self, parsed_args): diff --git a/neutronclient/tests/unit/osc/v2/fwaas/fakes.py b/neutronclient/tests/unit/osc/v2/fwaas/fakes.py index 61bc11244..6475392c4 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/fakes.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/fakes.py @@ -127,4 +127,8 @@ def __init__(self): ('shared', False), ('tenant_id', 'tenant-id-' + uuidutils.generate_uuid(dashed=False)), + ('source_firewall_group_id', 'firewall-group-id-' + + uuidutils.generate_uuid(dashed=False)), + ('destination_firewall_group_id', 'firewall-group-id-' + + uuidutils.generate_uuid(dashed=False)), )) diff --git a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py index a512f3b3f..a8b25c304 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py @@ -37,6 +37,10 @@ 'disable_rule': 'enabled', 'share': 'shared', 'no_share': 'shared', + 'source_firewall_group': 'source_firewall_group_id', + 'destination_firewall_group': 'destination_firewall_group_id', + 'no_source_firewall_group': 'source_firewall_group_id', + 'no_destination_firewall_group': 'destination_firewall_group_id', } @@ -114,11 +118,14 @@ def _mock_fwr(*args, **kwargs): 'Destination Port', 'Shared', 'Project', + 'Source Firewall Group ID', + 'Destination Firewall Group ID', ) self.data = _generate_data() self.ordered_headers = ( 'Action', 'Description', + 'Destination Firewall Group ID', 'Destination IP Address', 'Destination Port', 'Enabled', @@ -128,12 +135,14 @@ def _mock_fwr(*args, **kwargs): 'Project', 'Protocol', 'Shared', + 'Source Firewall Group ID', 'Source IP Address', 'Source Port', ) self.ordered_data = ( _fwr['action'], _fwr['description'], + _fwr['destination_firewall_group_id'], _fwr['destination_ip_address'], _fwr['destination_port'], _fwr['enabled'], @@ -143,12 +152,14 @@ def _mock_fwr(*args, **kwargs): _fwr['tenant_id'], _replace_display_columns('protocol', _fwr['protocol']), _fwr['shared'], + _fwr['source_firewall_group_id'], _fwr['source_ip_address'], _fwr['source_port'], ) self.ordered_columns = ( 'action', 'description', + 'destination_firewall_group_id', 'destination_ip_address', 'destination_port', 'enabled', @@ -158,6 +169,7 @@ def _mock_fwr(*args, **kwargs): 'tenant_id', 'protocol', 'shared', + 'source_firewall_group_id', 'source_ip_address', 'source_port', ) @@ -201,6 +213,10 @@ def _set_all_params(self, args={}): action = args.get('action') or 'deny' ip_version = args.get('ip_version') or '4' destination_port = args.get('destination_port') or '0:65535' + destination_firewall_group = args.get( + 'destination_firewall_group') or 'my-dst-fwg' + source_firewall_group = args.get( + 'source_firewall_group') or 'my-src-fwg' tenant_id = args.get('tenant_id') or 'my-tenant' arglist = [ '--description', description, @@ -215,7 +231,10 @@ def _set_all_params(self, args={}): '--project', tenant_id, '--disable-rule', '--share', + '--source-firewall-group', source_firewall_group, + '--destination-firewall-group', destination_firewall_group ] + verifylist = [ ('name', name), ('description', description), @@ -229,10 +248,23 @@ def _set_all_params(self, args={}): ('action', action), ('disable_rule', True), ('project', tenant_id), + ('source_firewall_group', source_firewall_group), + ('destination_firewall_group', destination_firewall_group) ] return arglist, verifylist def _test_create_with_all_params(self, args={}): + def _mock_fwr(*args, **kwargs): + if self.neutronclient.find_resource.call_count == 1: + self.neutronclient.find_resource.assert_called_once_with( + const.FWG, 'my-src-fwg', cmd_resource=const.CMD_FWG) + if self.neutronclient.find_resource.call_count == 2: + self.neutronclient.find_resource.assert_called_with( + const.FWG, 'my-dst-fwg', cmd_resource=const.CMD_FWG) + return {'id': args[1]} + + self.neutronclient.find_resource.side_effect = mock.Mock( + side_effect=_mock_fwr) arglist, verifylist = self._set_all_params(args) request, response = _generate_req_and_res(verifylist) self._update_expect_response(request, response) @@ -278,6 +310,34 @@ def test_create_with_all_params_protocol_upper_capitalized(self): testtools.matchers._impl.MismatchError, self.check_parser, self.cmd, arglist, verifylist) + def test_create_with_src_fwg_and_no(self): + fwg = 'my-fwg' + arglist = [ + '--source-firewall-group', fwg, + '--no-source-firewall-group', + ] + verifylist = [ + ('source_firewall_group', fwg), + ('no_source_firewall_group', True), + ] + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_create_with_dst_fwg_and_no(self): + fwg = 'my-fwg' + arglist = [ + '--destination-firewall-group', fwg, + '--no-destination-firewall-group', + ] + verifylist = [ + ('destination_firewall_group', fwg), + ('no_destination_firewall_group', True), + ] + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + class TestListFirewallRule(TestFirewallRule): @@ -326,7 +386,8 @@ def test_list_with_long_option(self): self.mocked.assert_called_once_with() self.assertEqual(list(self.headers), headers) - self.assertListItemEqual([self.data], list(data)) + m = list(data) + self.assertListItemEqual([self.data], m) def test_list_with_no_option(self): arglist = [] @@ -646,6 +707,74 @@ def test_set_and_raises(self): self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) + def test_set_no_destination_fwg(self): + target = self.resource['id'] + arglist = [ + target, + '--no-destination-firewall-group', + ] + verifylist = [ + (self.res, target), + ('no_destination_firewall_group', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'destination_firewall_group_id': None}}) + self.assertIsNone(result) + + def test_set_no_source_fwg(self): + target = self.resource['id'] + arglist = [ + target, + '--no-source-firewall-group', + ] + verifylist = [ + (self.res, target), + ('no_source_firewall_group', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'source_firewall_group_id': None}}) + self.assertIsNone(result) + + def test_create_with_src_fwg_and_no(self): + target = self.resource['id'] + fwg = 'my-fwg' + arglist = [ + target, + '--source-firewall-group', fwg, + '--no-source-firewall-group', + ] + verifylist = [ + (self.res, target), + ('source_firewall_group', fwg), + ('no_source_firewall_group', True), + ] + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_create_with_dst_fwg_and_no(self): + target = self.resource['id'] + fwg = 'my-fwg' + arglist = [ + target, + '--destination-firewall-group', fwg, + '--no-destination-firewall-group', + ] + verifylist = [ + (self.res, target), + ('destination_firewall_group', fwg), + ('no_destination_firewall_group', True), + ] + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + class TestUnsetFirewallRule(TestFirewallRule, common.TestUnsetFWaaS): diff --git a/releasenotes/notes/remote_fwg-0f5362e5be8b2e84.yaml b/releasenotes/notes/remote_fwg-0f5362e5be8b2e84.yaml new file mode 100644 index 000000000..5f5073aee --- /dev/null +++ b/releasenotes/notes/remote_fwg-0f5362e5be8b2e84.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Adds the remote source firewall group and the remote destination + firewall group field to the firewall rules. From 5f6971909604bcc19dfea11f8d3f5409a9442ace Mon Sep 17 00:00:00 2001 From: Vieri <15050873171@163.com> Date: Tue, 9 Oct 2018 13:59:30 +0000 Subject: [PATCH 697/845] Don't quote {posargs} in tox.ini Quotes around {posargs} cause the entire string to be combined into one arg that gets passed to stestr. This prevents passing multiple args (e.g. '--concurrency=16 some-regex') Change-Id: I30687b4bcdacb167599b54b6f07e018363980fc7 --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 76c62a262..032d3cc22 100644 --- a/tox.ini +++ b/tox.ini @@ -51,7 +51,7 @@ setenv = {[testenv]setenv} PYTHON=coverage run --source neutronclient --parallel-mode commands = - stestr run '{posargs}' + stestr run {posargs} coverage combine coverage html -d cover coverage xml -o cover/coverage.xml From 3de4353dcd75cf6563b1c9a33c516cb599147e95 Mon Sep 17 00:00:00 2001 From: Lucian Petrut Date: Fri, 2 Nov 2018 15:08:45 +0200 Subject: [PATCH 698/845] Fix api version handling, which completely breaks the client The neutron client does not work with recent openstacksdk versions (>=0.18.0): http://paste.openstack.org/raw/734040/ This change addresses a mismatch in the api version format. The neutron client passes a dict while the openstack sdk expects a string. Change-Id: I33c868f1c1e40d7673ba6651abedf3dfe0850660 Closes-Bug: #1801360 --- neutronclient/neutron/client.py | 2 +- neutronclient/shell.py | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/neutronclient/neutron/client.py b/neutronclient/neutron/client.py index 7961a4ca2..d859d815d 100644 --- a/neutronclient/neutron/client.py +++ b/neutronclient/neutron/client.py @@ -28,7 +28,7 @@ def make_client(instance): """Returns an neutron client.""" neutron_client = utils.get_client_class( API_NAME, - instance._api_version[API_NAME], + instance._api_version, API_VERSIONS, ) instance.initialize() diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 4814c8118..6dced26cd 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -596,8 +596,6 @@ def initialize_app(self, argv): super(NeutronShell, self).initialize_app(argv) - self.api_version = {'network': self.api_version} - # If the user is not asking for help, make sure they # have given us auth. cmd_name = None From da7e2916bc0d871857ecd4f0296d8f20f6db3cb2 Mon Sep 17 00:00:00 2001 From: "huang.zhiping" Date: Sun, 21 Oct 2018 02:31:59 +0000 Subject: [PATCH 699/845] Update min tox version to 2.3.2 The commands used by constraints need at least tox 2.3.2. Update to reflect reality, which should help with local running of constraints targets. Change-Id: I22dec4ff510123520a2c035c3fa1c80f18ce4cdd Closes-Bug: #1801360 --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 032d3cc22..7d5eecb23 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] # py3 first to avoid .testrepository incompatibility envlist = py35,py27,pypy,pep8 -minversion = 1.6 +minversion = 2.3.2 skipsdist = True [testenv] From cb1e24b1784fdee751a21d2b793b584538fefe58 Mon Sep 17 00:00:00 2001 From: mid_one Date: Mon, 12 Nov 2018 23:41:39 +0800 Subject: [PATCH 700/845] Fix docstring in method list_networks_on_dhcp_agent For function list_networks_on_dhcp_agent in the python-neutronclient\neutronclient\v2_0\client.py, the description is "Fetches a list of dhcp agents hosting a network". However this function fetches a list of networks hosted on a DHCP agent. Change the docstring. Change-Id: Ifa833d9a7ecab8ba55283d41d7a336d20cb578c3 Closes-Bug: 1802472 --- neutronclient/v2_0/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index cb84b9fc9..dddfba0db 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -1494,7 +1494,7 @@ def list_dhcp_agent_hosting_networks(self, network, **_params): params=_params) def list_networks_on_dhcp_agent(self, dhcp_agent, **_params): - """Fetches a list of dhcp agents hosting a network.""" + """Fetches a list of networks hosted on a DHCP agent.""" return self.get((self.agent_path + self.DHCP_NETS) % dhcp_agent, params=_params) From 1d45522a48bae0b454479e0f0a3876c773d7034e Mon Sep 17 00:00:00 2001 From: mid_one Date: Tue, 13 Nov 2018 00:35:37 +0800 Subject: [PATCH 701/845] Fix docstring in method list_routers_on_l3_agent For function list_routers_on_l3_agent in the python-neutronclient\neutronclient\v2_0\client.py, the description is "Fetches a list of L3 agents hosting a router". However this function fetches a list of routers hosted on a L3 agent. Change the docstring. Change-Id: Ia2156b5f6f016f338549a6536a0f31bf9e96c7cf Closes-Bug: 1802475 --- neutronclient/v2_0/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index cb84b9fc9..988a9ca22 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -1514,7 +1514,7 @@ def list_l3_agent_hosting_routers(self, router, **_params): params=_params) def list_routers_on_l3_agent(self, l3_agent, **_params): - """Fetches a list of L3 agents hosting a router.""" + """Fetches a list of routers hosted on an L3 agent.""" return self.get((self.agent_path + self.L3_ROUTERS) % l3_agent, params=_params) From b60283cd161300ab5805239c895b0d9a8d5eb511 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Barr=C3=A9?= Date: Tue, 20 Feb 2018 15:12:31 +0100 Subject: [PATCH 702/845] Support of the boolean advertise_extra_routes This Boolean attribute is added to the Router Association resource (Neutron API, 'bgpvpn' and 'bgpvpn-routes-control' API extensions) in order to support routes control. This allow to propagate routes of subnets not directly connected to the router The corresponding code in networking-bgpvpn has already merged See https://blueprints.launchpad.net/bgpvpn/+spec/routes-control Change-Id: Icdd7f6592a9d657b6414645406f06b74b6f3bb11 Implements: blueprint routes-control --- .../networking_bgpvpn/router_association.py | 51 +++ .../unit/osc/v2/networking_bgpvpn/fakes.py | 42 ++- .../test_router_association.py | 291 ++++++++++++++++++ ...ort-routes-advertise-9356a38cf3e2fe5a.yaml | 6 + setup.cfg | 2 + 5 files changed, 391 insertions(+), 1 deletion(-) create mode 100644 neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_router_association.py create mode 100644 releasenotes/notes/support-routes-advertise-9356a38cf3e2fe5a.yaml diff --git a/neutronclient/osc/v2/networking_bgpvpn/router_association.py b/neutronclient/osc/v2/networking_bgpvpn/router_association.py index 40664b6dc..c382f1621 100644 --- a/neutronclient/osc/v2/networking_bgpvpn/router_association.py +++ b/neutronclient/osc/v2/networking_bgpvpn/router_association.py @@ -24,8 +24,12 @@ DeleteBgpvpnResAssoc from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ ListBgpvpnResAssoc +from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ + SetBgpvpnResAssoc from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ ShowBgpvpnResAssoc +from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ + UnsetBgpvpnResAssoc class BgpvpnRouterAssoc(object): @@ -38,15 +42,62 @@ class BgpvpnRouterAssoc(object): ('tenant_id', 'Project', column_util.LIST_LONG_ONLY), ('%s_id' % _assoc_res_name, '%s ID' % _assoc_res_name.capitalize(), column_util.LIST_BOTH), + ('advertise_extra_routes', 'Advertise extra routes', + column_util.LIST_LONG_ONLY), ) _formatters = {} + def _get_common_parser(self, parser): + """Adds to parser arguments common to create, set and unset commands. + + :params ArgumentParser parser: argparse object contains all command's + arguments + """ + ADVERTISE_ROUTES = _("Routes will be advertised to the " + "BGP VPN%s") % ( + _(' (default)') if self._action == 'create' + else "") + NOT_ADVERTISE_ROUTES = _("Routes from the router will not be " + "advertised to the BGP VPN") + + group_advertise_extra_routes = parser.add_mutually_exclusive_group() + group_advertise_extra_routes.add_argument( + '--advertise_extra_routes', + action='store_true', + help=NOT_ADVERTISE_ROUTES if self._action == 'unset' + else ADVERTISE_ROUTES, + ) + group_advertise_extra_routes.add_argument( + '--no-advertise_extra_routes', + action='store_true', + help=ADVERTISE_ROUTES if self._action == 'unset' + else NOT_ADVERTISE_ROUTES, + ) + + def _args2body(self, _, args): + attrs = {} + + if args.advertise_extra_routes: + attrs['advertise_extra_routes'] = self._action != 'unset' + elif args.no_advertise_extra_routes: + attrs['advertise_extra_routes'] = self._action == 'unset' + + return {self._resource: attrs} + class CreateBgpvpnRouterAssoc(BgpvpnRouterAssoc, CreateBgpvpnResAssoc): _description = _("Create a BGP VPN router association") pass +class SetBgpvpnRouterAssoc(BgpvpnRouterAssoc, SetBgpvpnResAssoc): + _description = _("Set BGP VPN router association properties") + + +class UnsetBgpvpnRouterAssoc(BgpvpnRouterAssoc, UnsetBgpvpnResAssoc): + _description = _("Unset BGP VPN router association properties") + + class DeleteBgpvpnRouterAssoc(BgpvpnRouterAssoc, DeleteBgpvpnResAssoc): _description = _("Delete a BGP VPN router association(s) for a given BGP " "VPN") diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py index 21b9a762b..b3a538f5a 100644 --- a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py +++ b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py @@ -33,6 +33,12 @@ ShowBgpvpnResAssoc from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ UnsetBgpvpnResAssoc +from neutronclient.osc.v2.networking_bgpvpn.router_association import\ + CreateBgpvpnRouterAssoc +from neutronclient.osc.v2.networking_bgpvpn.router_association import\ + SetBgpvpnRouterAssoc +from neutronclient.osc.v2.networking_bgpvpn.router_association import\ + ShowBgpvpnRouterAssoc from neutronclient.tests.unit.osc.v2 import fakes as test_fakes @@ -139,6 +145,35 @@ class ShowBgpvpnFakeResAssoc(BgpvpnFakeAssoc, ShowBgpvpnResAssoc): pass +class BgpvpnFakeRouterAssoc(object): + _assoc_res_name = 'fake_resource' + _resource = '%s_association' % _assoc_res_name + _resource_plural = '%ss' % _resource + + _attr_map = ( + ('id', 'ID', column_util.LIST_BOTH), + ('tenant_id', 'Project', column_util.LIST_LONG_ONLY), + ('%s_id' % _assoc_res_name, '%s ID' % _assoc_res_name.capitalize(), + column_util.LIST_BOTH), + ('advertise_extra_routes', 'Advertise extra routes', + column_util.LIST_LONG_ONLY), + ) + _formatters = {} + + +class CreateBgpvpnFakeRouterAssoc(BgpvpnFakeRouterAssoc, + CreateBgpvpnRouterAssoc): + pass + + +class SetBgpvpnFakeRouterAssoc(BgpvpnFakeRouterAssoc, SetBgpvpnRouterAssoc): + pass + + +class ShowBgpvpnFakeRouterAssoc(BgpvpnFakeRouterAssoc, ShowBgpvpnRouterAssoc): + pass + + class FakeResource(object): """Fake resource with minimal attributes.""" @@ -177,14 +212,19 @@ class FakeResAssoc(object): """Fake resource association with minimal attributes.""" @staticmethod - def create_one_resource_association(resource): + def create_one_resource_association(resource, attrs=None): """Create a fake resource association.""" + attrs = attrs or {} + res_assoc_attrs = { 'id': 'fake_association_id', 'tenant_id': resource['tenant_id'], 'fake_resource_id': resource['id'], } + + # Overwrite default attributes. + res_assoc_attrs.update(attrs) return copy.deepcopy(res_assoc_attrs) @staticmethod diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_router_association.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_router_association.py new file mode 100644 index 000000000..fb17bdb67 --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_router_association.py @@ -0,0 +1,291 @@ +# Copyright (c) 2018 Orange SA. +# 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 copy +import operator + +import mock +from osc_lib.tests.utils import ParserException +from osc_lib import utils as osc_utils +from osc_lib.utils import columns as column_util + +from neutronclient.tests.unit.osc.v2.networking_bgpvpn import fakes + + +columns_short = tuple(col for col, _, listing_mode + in fakes.BgpvpnFakeRouterAssoc._attr_map + if listing_mode in (column_util.LIST_BOTH, + column_util.LIST_SHORT_ONLY)) +columns_long = tuple(col for col, _, listing_mode + in fakes.BgpvpnFakeRouterAssoc._attr_map + if listing_mode in (column_util.LIST_BOTH, + column_util.LIST_LONG_ONLY)) +headers_short = tuple(head for _, head, listing_mode + in fakes.BgpvpnFakeRouterAssoc._attr_map + if listing_mode in (column_util.LIST_BOTH, + column_util.LIST_SHORT_ONLY)) +headers_long = tuple(head for _, head, listing_mode + in fakes.BgpvpnFakeRouterAssoc._attr_map + if listing_mode in (column_util.LIST_BOTH, + column_util.LIST_LONG_ONLY)) +sorted_attr_map = sorted(fakes.BgpvpnFakeRouterAssoc._attr_map, + key=operator.itemgetter(1)) +sorted_columns = tuple(col for col, _, _ in sorted_attr_map) +sorted_headers = tuple(head for _, head, _ in sorted_attr_map) + + +def _get_data(attrs, columns=sorted_columns): + return osc_utils.get_dict_properties( + attrs, columns, formatters=fakes.BgpvpnFakeAssoc._formatters) + + +class TestCreateRouterAssoc(fakes.TestNeutronClientBgpvpn): + def setUp(self): + super(TestCreateRouterAssoc, self).setUp() + self.cmd = fakes.CreateBgpvpnFakeRouterAssoc(self.app, self.namespace) + self.fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() + self.fake_router = fakes.FakeResource.create_one_resource() + + def _build_args(self, param=None): + arglist_base = [ + self.fake_bgpvpn['id'], + self.fake_router['id'], + '--project', self.fake_bgpvpn['tenant_id'] + ] + if param is not None: + if isinstance(param, list): + arglist_base.extend(param) + else: + arglist_base.append(param) + return arglist_base + + def _build_verify_list(self, param=None): + verifylist = [ + ('bgpvpn', self.fake_bgpvpn['id']), + ('resource', self.fake_router['id']), + ('project', self.fake_bgpvpn['tenant_id']) + ] + if param is not None: + verifylist.append(param) + return verifylist + + def _exec_create_router_association( + self, fake_res_assoc, arglist, verifylist): + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + cols, data = self.cmd.take_action(parsed_args) + + fake_res_assoc_call = copy.deepcopy(fake_res_assoc) + fake_res_assoc_call.pop('id') + + self.neutronclient.create_bgpvpn_fake_resource_assoc.\ + assert_called_once_with( + self.fake_bgpvpn['id'], + {fakes.BgpvpnFakeRouterAssoc._resource: fake_res_assoc_call}) + return cols, data + + def test_create_router_association(self): + fake_res_assoc = fakes.FakeResAssoc.create_one_resource_association( + self.fake_router) + + self.neutronclient.create_bgpvpn_fake_resource_assoc = mock.Mock( + return_value={ + fakes.BgpvpnFakeRouterAssoc._resource: fake_res_assoc, + 'advertise_extra_routes': True}) + + arglist = self._build_args() + # advertise_extra_routes will be False since none + # of the mutually exclusive args present + verifylist = self._build_verify_list(('advertise_extra_routes', False)) + + self._exec_create_router_association( + fake_res_assoc, arglist, verifylist) + + def test_create_router_association_advertise(self): + fake_res_assoc = fakes.FakeResAssoc.create_one_resource_association( + self.fake_router, + {'advertise_extra_routes': True}) + + self.neutronclient.create_bgpvpn_fake_resource_assoc = mock.Mock( + return_value={ + fakes.BgpvpnFakeRouterAssoc._resource: fake_res_assoc}) + + arglist = self._build_args('--advertise_extra_routes') + verifylist = self._build_verify_list(('advertise_extra_routes', True)) + + cols, data = self._exec_create_router_association( + fake_res_assoc, arglist, verifylist) + self.assertEqual(sorted_headers, cols) + self.assertEqual(_get_data(fake_res_assoc), data) + + def test_create_router_association_no_advertise(self): + fake_res_assoc = fakes.FakeResAssoc.create_one_resource_association( + self.fake_router, + {'advertise_extra_routes': False}) + + self.neutronclient.create_bgpvpn_fake_resource_assoc = mock.Mock( + return_value={ + fakes.BgpvpnFakeRouterAssoc._resource: fake_res_assoc}) + + arglist = self._build_args('--no-advertise_extra_routes') + verifylist = self._build_verify_list(('advertise_extra_routes', False)) + + cols, data = self._exec_create_router_association( + fake_res_assoc, arglist, verifylist) + self.assertEqual(sorted_headers, cols) + self.assertEqual(_get_data(fake_res_assoc), data) + + def test_create_router_association_advertise_fault(self): + arglist = self._build_args( + ['--advertise_extra_routes', '--no-advertise_extra_routes']) + + try: + self._exec_create_router_association(None, arglist, None) + except ParserException as e: + self.assertEqual(format(e), 'Argument parse failed') + + def test_router_association_unknown_arg(self): + arglist = self._build_args('--unknown arg') + + try: + self._exec_create_router_association(None, arglist, None) + except ParserException as e: + self.assertEqual(format(e), 'Argument parse failed') + + +class TestSetRouterAssoc(fakes.TestNeutronClientBgpvpn): + + def setUp(self): + super(TestSetRouterAssoc, self).setUp() + self.cmd = fakes.SetBgpvpnFakeRouterAssoc(self.app, self.namespace) + self.fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() + self.fake_router = fakes.FakeResource.create_one_resource() + + def _build_args(self, fake_res_assoc, param=None): + arglist_base = [ + fake_res_assoc['id'], + self.fake_bgpvpn['id'] + ] + if param is not None: + if isinstance(param, list): + arglist_base.extend(param) + else: + arglist_base.append(param) + return arglist_base + + def _build_verify_list(self, fake_res_assoc, param=None): + verifylist = [ + ('resource_association_id', fake_res_assoc['id']), + ('bgpvpn', self.fake_bgpvpn['id']) + ] + if param is not None: + verifylist.append(param) + return verifylist + + def test_set_router_association_no_advertise(self): + fake_res_assoc = fakes.FakeResAssoc.create_one_resource_association( + self.fake_router, + {'advertise_extra_routes': True}) + self.neutronclient.update_bgpvpn_fake_resource_assoc = mock.Mock() + + arglist = self._build_args( + fake_res_assoc, + '--no-advertise_extra_routes') + verifylist = [ + ('resource_association_id', fake_res_assoc['id']), + ('bgpvpn', self.fake_bgpvpn['id']), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + fake_res_assoc_call = copy.deepcopy(fake_res_assoc) + fake_res_assoc_call.pop('id') + + self.neutronclient.update_bgpvpn_fake_resource_assoc.\ + assert_called_once_with( + self.fake_bgpvpn['id'], + fake_res_assoc['id'], + { + fakes.BgpvpnFakeRouterAssoc._resource: { + 'advertise_extra_routes': False + } + }) + self.assertIsNone(result) + + def test_set_router_association_advertise(self): + fake_res_assoc = fakes.FakeResAssoc.create_one_resource_association( + self.fake_router, + {'advertise_extra_routes': False}) + self.neutronclient.update_bgpvpn_fake_resource_assoc = mock.Mock() + + arglist = self._build_args( + fake_res_assoc, + '--advertise_extra_routes') + verifylist = [ + ('resource_association_id', fake_res_assoc['id']), + ('bgpvpn', self.fake_bgpvpn['id']), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + fake_res_assoc_call = copy.deepcopy(fake_res_assoc) + fake_res_assoc_call.pop('id') + + self.neutronclient.update_bgpvpn_fake_resource_assoc.\ + assert_called_once_with( + self.fake_bgpvpn['id'], + fake_res_assoc['id'], + { + fakes.BgpvpnFakeRouterAssoc._resource: { + 'advertise_extra_routes': True + } + }) + self.assertIsNone(result) + + +class TestShowRouterAssoc(fakes.TestNeutronClientBgpvpn): + def setUp(self): + super(TestShowRouterAssoc, self).setUp() + self.cmd = fakes.ShowBgpvpnFakeRouterAssoc(self.app, self.namespace) + + def test_show_router_association(self): + fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() + fake_res = fakes.FakeResource.create_one_resource() + fake_res_assoc = fakes.FakeResAssoc.create_one_resource_association( + fake_res, + {'advertise_extra_routes': True}) + self.neutronclient.show_bgpvpn_fake_resource_assoc = mock.Mock( + return_value={fakes.BgpvpnFakeAssoc._resource: fake_res_assoc}) + arglist = [ + fake_res_assoc['id'], + fake_bgpvpn['id'], + ] + verifylist = [ + ('resource_association_id', fake_res_assoc['id']), + ('bgpvpn', fake_bgpvpn['id']), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + headers, data = self.cmd.take_action(parsed_args) + + self.neutronclient.show_bgpvpn_fake_resource_assoc.\ + assert_called_once_with(fake_bgpvpn['id'], fake_res_assoc['id']) + self.assertEqual(sorted_headers, headers) + self.assertEqual(data, _get_data(fake_res_assoc)) diff --git a/releasenotes/notes/support-routes-advertise-9356a38cf3e2fe5a.yaml b/releasenotes/notes/support-routes-advertise-9356a38cf3e2fe5a.yaml new file mode 100644 index 000000000..f1dbd5ed0 --- /dev/null +++ b/releasenotes/notes/support-routes-advertise-9356a38cf3e2fe5a.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Add optional flag to control the advertisement in BGPVPNs + of the routes defined on a Router resource + (``bgpvpn-routes-control`` API extension). diff --git a/setup.cfg b/setup.cfg index 41857c7a6..d9938af5a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -123,7 +123,9 @@ openstack.neutronclient.v2 = bgpvpn_router_association_create = neutronclient.osc.v2.networking_bgpvpn.router_association:CreateBgpvpnRouterAssoc bgpvpn_router_association_delete = neutronclient.osc.v2.networking_bgpvpn.router_association:DeleteBgpvpnRouterAssoc bgpvpn_router_association_list = neutronclient.osc.v2.networking_bgpvpn.router_association:ListBgpvpnRouterAssoc + bgpvpn_router_association_set = neutronclient.osc.v2.networking_bgpvpn.router_association:SetBgpvpnRouterAssoc bgpvpn_router_association_show = neutronclient.osc.v2.networking_bgpvpn.router_association:ShowBgpvpnRouterAssoc + bgpvpn_router_association_unset = neutronclient.osc.v2.networking_bgpvpn.router_association:UnsetBgpvpnRouterAssoc bgpvpn_port_association_create = neutronclient.osc.v2.networking_bgpvpn.port_association:CreateBgpvpnPortAssoc bgpvpn_port_association_set = neutronclient.osc.v2.networking_bgpvpn.port_association:SetBgpvpnPortAssoc bgpvpn_port_association_unset = neutronclient.osc.v2.networking_bgpvpn.port_association:UnsetBgpvpnPortAssoc From c17db9f29542c38709d7e026aefc44ac1134bbcd Mon Sep 17 00:00:00 2001 From: qingszhao Date: Fri, 30 Nov 2018 07:07:33 +0000 Subject: [PATCH 703/845] Add Python 3.6 classifier to setup.cfg Change-Id: Id7864c23b73b5f2b265ba04d24d01540b326d105 --- setup.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.cfg b/setup.cfg index d9938af5a..c4f1c9323 100644 --- a/setup.cfg +++ b/setup.cfg @@ -18,6 +18,7 @@ classifier = Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 Programming Language :: Python :: 3.5 + Programming Language :: Python :: 3.6 [files] packages = From 1e2e98b51983197bf8faf3c2a40c3d13c067b1d1 Mon Sep 17 00:00:00 2001 From: sunjia Date: Mon, 3 Dec 2018 21:56:09 -0500 Subject: [PATCH 704/845] Change openstack-dev to openstack-discuss Mailinglists have been updated. Openstack-discuss replaces openstack-dev. Change-Id: I4be1b808bee0a4f9feaea5b203317d04037e3dfd --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index d9938af5a..cf5567e73 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,7 +4,7 @@ summary = CLI and Client Library for OpenStack Networking description-file = README.rst author = OpenStack Networking Project -author-email = openstack-dev@lists.openstack.org +author-email = openstack-discuss@lists.openstack.org home-page = https://docs.openstack.org/python-neutronclient/latest/ classifier = Environment :: OpenStack From 9cbdfb2181f1d0acb303214deeee781f57ba48d9 Mon Sep 17 00:00:00 2001 From: mid_one Date: Tue, 4 Dec 2018 01:43:11 +0800 Subject: [PATCH 705/845] Add support for querying quotas with usage The neutron HTTP API supports issuing a GET call on /v2.0/quotas/{project_id}/details.json in order to obtain quotas with usage information for a given tenant. However, this capability is not currently exposed in the python-neutronclient code. Add path: quota_details_path Add function show_quota_details Closes-Bug: #1808451 Related-Bug: #1599488 Change-Id: Ia02172f62b9d9915273021627b0feb52a4f376da --- neutronclient/v2_0/client.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 2c0d88ee8..560b531d0 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -501,6 +501,7 @@ class Client(ClientBase): quotas_path = "/quotas" quota_path = "/quotas/%s" quota_default_path = "/quotas/%s/default" + quota_details_path = "/quotas/%s/details.json" extensions_path = "/extensions" extension_path = "/extensions/%s" routers_path = "/routers" @@ -753,6 +754,13 @@ def show_quota(self, project_id, **_params): """Fetch information of a certain project's quotas.""" return self.get(self.quota_path % (project_id), params=_params) + @debtcollector.renames.renamed_kwarg( + 'tenant_id', 'project_id', replace=True) + def show_quota_details(self, project_id, **_params): + """Fetch information of a certain project's quota details.""" + return self.get(self.quota_details_path % (project_id), + params=_params) + @debtcollector.renames.renamed_kwarg( 'tenant_id', 'project_id', replace=True) def show_quota_default(self, project_id, **_params): From aa19ab71efe0af94b56440095f712905b6ed8b8d Mon Sep 17 00:00:00 2001 From: chenlx Date: Mon, 17 Dec 2018 19:33:05 +0800 Subject: [PATCH 706/845] Update mailinglist from dev to discuss openstack-dev was decomissioned this night in https://review.openstack.org/621258 Update openstack-dev to openstack-discuss Change-Id: Ib88759901998ebec8a3e3817d1148fa70323cee5 --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index c4f1c9323..b5a837b25 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,7 +4,7 @@ summary = CLI and Client Library for OpenStack Networking description-file = README.rst author = OpenStack Networking Project -author-email = openstack-dev@lists.openstack.org +author-email = openstack-discuss@lists.openstack.org home-page = https://docs.openstack.org/python-neutronclient/latest/ classifier = Environment :: OpenStack From f99c63f98ae8f4c43a152774dab8c9f9741de360 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Thu, 31 Jan 2019 10:42:21 +0900 Subject: [PATCH 707/845] Update hacking version to 1.1 Older hacking depends on pep8, but pep8 module is replaced by pycodestyle. This can be resolved by updating hackinng version. This commit also fixes E117 (over-indented) and W605 (invalid escape sequence) warnings. pep8 is dropped from lower-constraints.txt as it is no longer used. Change-Id: I3a8e3b0dedf9422e4db02b525b333d12ce012a95 --- lower-constraints.txt | 3 +- neutronclient/neutron/v2_0/lb/v2/l7rule.py | 52 +++++++++---------- neutronclient/neutron/v2_0/lb/v2/member.py | 15 +++--- .../osc/v2/sfc/sfc_flow_classifier.py | 28 +++++----- .../functional/core/test_readonly_neutron.py | 2 +- test-requirements.txt | 2 +- 6 files changed, 50 insertions(+), 52 deletions(-) diff --git a/lower-constraints.txt b/lower-constraints.txt index 117d04b2e..b37c137a0 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -25,7 +25,7 @@ flake8==2.5.5 future==0.16.0 futurist==1.2.0 greenlet==0.4.10 -hacking==0.12.0 +hacking==1.1.0 idna==2.6 imagesize==0.7.1 iso8601==0.1.11 @@ -68,7 +68,6 @@ paramiko==2.0.0 Paste==2.0.2 PasteDeploy==1.5.0 pbr==2.0.0 -pep8==1.5.7 pika-pool==0.1.3 pika==0.10.0 positional==1.2.1 diff --git a/neutronclient/neutron/v2_0/lb/v2/l7rule.py b/neutronclient/neutron/v2_0/lb/v2/l7rule.py index 1286559f2..3c468cabf 100644 --- a/neutronclient/neutron/v2_0/lb/v2/l7rule.py +++ b/neutronclient/neutron/v2_0/lb/v2/l7rule.py @@ -39,32 +39,32 @@ def add_known_arguments(self, parser): def _add_common_args(parser, is_create=True): - parser.add_argument( - '--type', - required=is_create, - type=utils.convert_to_uppercase, - choices=['HOST_NAME', 'PATH', 'FILE_TYPE', 'HEADER', 'COOKIE'], - help=_('Rule type.')) - parser.add_argument( - '--compare-type', - required=is_create, - type=utils.convert_to_uppercase, - choices=['REGEX', 'STARTS_WITH', 'ENDS_WITH', - 'CONTAINS', 'EQUAL_TO'], - help=_('Rule compare type.')) - parser.add_argument( - '--invert-compare', - dest='invert', - action='store_true', - help=_('Invert the compare type.')) - parser.add_argument( - '--key', - help=_('Key to compare.' - ' Relevant for HEADER and COOKIE types only.')) - parser.add_argument( - '--value', - required=is_create, - help=_('Value to compare.')) + parser.add_argument( + '--type', + required=is_create, + type=utils.convert_to_uppercase, + choices=['HOST_NAME', 'PATH', 'FILE_TYPE', 'HEADER', 'COOKIE'], + help=_('Rule type.')) + parser.add_argument( + '--compare-type', + required=is_create, + type=utils.convert_to_uppercase, + choices=['REGEX', 'STARTS_WITH', 'ENDS_WITH', + 'CONTAINS', 'EQUAL_TO'], + help=_('Rule compare type.')) + parser.add_argument( + '--invert-compare', + dest='invert', + action='store_true', + help=_('Invert the compare type.')) + parser.add_argument( + '--key', + help=_('Key to compare.' + ' Relevant for HEADER and COOKIE types only.')) + parser.add_argument( + '--value', + required=is_create, + help=_('Value to compare.')) def _common_args2body(client, parsed_args, is_create=True): diff --git a/neutronclient/neutron/v2_0/lb/v2/member.py b/neutronclient/neutron/v2_0/lb/v2/member.py index 3fec7760d..ee3c090a9 100644 --- a/neutronclient/neutron/v2_0/lb/v2/member.py +++ b/neutronclient/neutron/v2_0/lb/v2/member.py @@ -38,17 +38,16 @@ def add_known_arguments(self, parser): def _add_common_args(parser): - parser.add_argument( - '--name', - help=_('Name of the member.')) - parser.add_argument( - '--weight', - help=_('Weight of the member in the pool (default:1, [0..256]).')) + parser.add_argument( + '--name', + help=_('Name of the member.')) + parser.add_argument( + '--weight', + help=_('Weight of the member in the pool (default:1, [0..256]).')) def _parse_common_args(body, parsed_args): - neutronV20.update_dict(parsed_args, body, - ['weight', 'name']) + neutronV20.update_dict(parsed_args, body, ['weight', 'name']) class ListMember(LbaasMemberMixin, neutronV20.ListCommand): diff --git a/neutronclient/osc/v2/sfc/sfc_flow_classifier.py b/neutronclient/osc/v2/sfc/sfc_flow_classifier.py index ad35aa6bd..d223674c4 100755 --- a/neutronclient/osc/v2/sfc/sfc_flow_classifier.py +++ b/neutronclient/osc/v2/sfc/sfc_flow_classifier.py @@ -300,20 +300,20 @@ def _get_attrs(client_manager, attrs, parsed_args): def _fill_protocol_port_info(attrs, port_type, port_val): - min_port, sep, max_port = port_val.partition(":") - if not min_port: - msg = ("Invalid port value '%s', expected format is " - "min-port:max-port or min-port.") - raise argparse.ArgumentTypeError(msg % port_val) - if not max_port: - max_port = min_port - try: - attrs[port_type + '_port_range_min'] = int(min_port) - attrs[port_type + '_port_range_max'] = int(max_port) - except ValueError: - message = (_("Protocol port value %s must be an integer " - "or integer:integer.") % port_val) - raise nc_exc.CommandError(message=message) + min_port, sep, max_port = port_val.partition(":") + if not min_port: + msg = ("Invalid port value '%s', expected format is " + "min-port:max-port or min-port.") + raise argparse.ArgumentTypeError(msg % port_val) + if not max_port: + max_port = min_port + try: + attrs[port_type + '_port_range_min'] = int(min_port) + attrs[port_type + '_port_range_max'] = int(max_port) + except ValueError: + message = (_("Protocol port value %s must be an integer " + "or integer:integer.") % port_val) + raise nc_exc.CommandError(message=message) def _get_id(client, id_or_name, resource): diff --git a/neutronclient/tests/functional/core/test_readonly_neutron.py b/neutronclient/tests/functional/core/test_readonly_neutron.py index 9429c9a4c..20e1484e5 100644 --- a/neutronclient/tests/functional/core/test_readonly_neutron.py +++ b/neutronclient/tests/functional/core/test_readonly_neutron.py @@ -110,7 +110,7 @@ def test_neutron_help(self): commands = [] cmds_start = lines.index('Commands for API v2.0:') - command_pattern = re.compile('^ {2}([a-z0-9\-\_]+)') + command_pattern = re.compile(r'^ {2}([a-z0-9\-\_]+)') for line in lines[cmds_start:]: match = command_pattern.match(line) if match: diff --git a/test-requirements.txt b/test-requirements.txt index 11865b828..dabdb0e02 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,7 +1,7 @@ # The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. -hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0 +hacking>=1.1.0 # Apache-2.0 bandit>=1.1.0 # Apache-2.0 coverage!=4.4,>=4.0 # Apache-2.0 From c2ac394a8880000bca2109985760347e8df7d76d Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Thu, 31 Jan 2019 10:46:33 +0900 Subject: [PATCH 708/845] Remove basepython from [flake8] section basepython is not a valid option for flake8 and it is complaned when running tox -e pep8. It seems to have been added accidentally. Change-Id: Ia5d093bab0c175b2693e7ae47a6bc29c1d44db36 --- tox.ini | 1 - 1 file changed, 1 deletion(-) diff --git a/tox.ini b/tox.ini index 7d5eecb23..cae97f1cb 100644 --- a/tox.ini +++ b/tox.ini @@ -68,7 +68,6 @@ deps = -r{toxinidir}/doc/requirements.txt commands = sphinx-build -a -E -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html [flake8] -basepython = python3 show-source = true exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,tools import-order-style = pep8 From b4a0e27ec61c47f2d14983d3767f778ef5545402 Mon Sep 17 00:00:00 2001 From: Corey Bryant Date: Wed, 20 Feb 2019 15:38:59 -0500 Subject: [PATCH 709/845] add python 3.7 unit test job This is a mechanically generated patch to add a unit test job running under Python 3.7. See ML discussion here [1] for context. [1] http://lists.openstack.org/pipermail/openstack-dev/2018-October/135626.html Change-Id: Icb961e81e0d99ab1912f75b1ce83ed371fc1107c Story: #2004073 --- .zuul.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.zuul.yaml b/.zuul.yaml index 7fde244be..d38182be9 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -5,6 +5,7 @@ - openstack-python-jobs - openstack-python35-jobs - openstack-python36-jobs + - openstack-python37-jobs - publish-openstack-docs-pti - check-requirements - lib-forward-testing From 1f35b8f25cb328ede6aea7933ecd33a1cd713ba8 Mon Sep 17 00:00:00 2001 From: "cao.yuan" Date: Mon, 25 Feb 2019 00:55:22 +0800 Subject: [PATCH 710/845] Update json module to jsonutils oslo project provide jsonutils, and neutronclient use it in many place[1], this PS to update the remained json module to oslo jsonutils for consistency. [1]: https://github.com/openstack/python-neutronclient/search?utf8=%E2%9C%93&q=jsonutils&type= Change-Id: Ic4639628c8910b88a14b0fe8f37638bb4f9474be --- neutronclient/client.py | 11 ++++------- neutronclient/tests/unit/test_cli20.py | 8 ++++---- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/neutronclient/client.py b/neutronclient/client.py index 2482eb434..c5f467668 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -14,16 +14,13 @@ # under the License. # -try: - import json -except ImportError: - import simplejson as json import logging import os import debtcollector.renames from keystoneauth1 import access from keystoneauth1 import adapter +from oslo_serialization import jsonutils from oslo_utils import importutils import requests @@ -239,14 +236,14 @@ def _authenticate_keystone(self): token_url = self.auth_url + "/tokens" resp, resp_body = self._cs_request(token_url, "POST", - body=json.dumps(body), + body=jsonutils.dumps(body), content_type="application/json", allow_redirects=True) if resp.status_code != 200: raise exceptions.Unauthorized(message=resp_body) if resp_body: try: - resp_body = json.loads(resp_body) + resp_body = jsonutils.loads(resp_body) except ValueError: pass else: @@ -282,7 +279,7 @@ def _get_endpoint_url(self): self.authenticate() return self.endpoint_url - body = json.loads(body) + body = jsonutils.loads(body) for endpoint in body.get('endpoints', []): if (endpoint['type'] == 'network' and endpoint.get('region') == self.region_name): diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index cfba9fafd..3a8bb4719 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -16,10 +16,10 @@ import contextlib import itertools -import json import sys import mock +from oslo_serialization import jsonutils from oslo_utils import encodeutils from oslotest import base import requests @@ -1153,7 +1153,7 @@ def test_create_resource_table(self): def test_create_resource_json(self): self._test_create_resource_with_formatter('json') - data = json.loads(self.fake_stdout.make_string()) + data = jsonutils.loads(self.fake_stdout.make_string()) self.assertEqual('myname', data['name']) self.assertEqual('myid', data['id']) @@ -1178,7 +1178,7 @@ def test_show_resource_table(self): def test_show_resource_json(self): self._test_show_resource_with_formatter('json') - data = json.loads(''.join(self.fake_stdout.content)) + data = jsonutils.loads(''.join(self.fake_stdout.content)) self.assertEqual('myname', data['name']) self.assertEqual('myid', data['id']) @@ -1206,7 +1206,7 @@ def test_list_resources_table(self): def test_list_resources_json(self): self._test_list_resources_with_formatter('json') - data = json.loads(''.join(self.fake_stdout.content)) + data = jsonutils.loads(''.join(self.fake_stdout.content)) self.assertEqual(['myid1', 'myid2'], [d['id'] for d in data]) def test_list_resources_yaml(self): From 680b417111dbbda9e318700286c4efd9055f1af3 Mon Sep 17 00:00:00 2001 From: Ryan Tidwell Date: Tue, 5 Feb 2019 10:59:09 -0600 Subject: [PATCH 711/845] Add subnet onboard CLI This is the implementation of the "onboard network subnets" CLI. This enables the subnet onboard feature of neutron to be driven via CLI. Depends-On: https://review.openstack.org/348080 Change-Id: Ic637ed689b0d3806d2c33faa419c3a98a310effa Implements: blueprint subnet-onboard --- doc/source/cli/osc/v2/subnet-onboard.rst | 17 ++++++ .../osc/v2/subnet_onboard/__init__.py | 0 .../osc/v2/subnet_onboard/subnet_onboard.py | 59 +++++++++++++++++++ .../unit/osc/v2/subnet_onboard/__init__.py | 0 .../test_network_onboard_subnets.py | 54 +++++++++++++++++ neutronclient/v2_0/client.py | 6 ++ .../add-subnet-onboard-e60772bc4984f698.yaml | 5 ++ setup.cfg | 2 + 8 files changed, 143 insertions(+) create mode 100644 doc/source/cli/osc/v2/subnet-onboard.rst create mode 100644 neutronclient/osc/v2/subnet_onboard/__init__.py create mode 100644 neutronclient/osc/v2/subnet_onboard/subnet_onboard.py create mode 100644 neutronclient/tests/unit/osc/v2/subnet_onboard/__init__.py create mode 100644 neutronclient/tests/unit/osc/v2/subnet_onboard/test_network_onboard_subnets.py create mode 100644 releasenotes/notes/add-subnet-onboard-e60772bc4984f698.yaml diff --git a/doc/source/cli/osc/v2/subnet-onboard.rst b/doc/source/cli/osc/v2/subnet-onboard.rst new file mode 100644 index 000000000..006970372 --- /dev/null +++ b/doc/source/cli/osc/v2/subnet-onboard.rst @@ -0,0 +1,17 @@ +======================= +network onboard subnets +======================= + +**network onboard subnets** enables a subnet to be adopted or +"onboarded" into an existing subnet pool. The CIDR of the subnet +is checked for uniqueness across any applicable address scopes +and all subnets allocated from the target subnet pool. Once +onboarded, the subnet CIDR is added to the prefix list of the +subnet pool and the subnet appears as though it has been allocated +from the subnet pool. The subnet also begins participating in the +applicable address scope if the subnet pool belongs to one. + +Network v2 + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: network onboard subnets diff --git a/neutronclient/osc/v2/subnet_onboard/__init__.py b/neutronclient/osc/v2/subnet_onboard/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/osc/v2/subnet_onboard/subnet_onboard.py b/neutronclient/osc/v2/subnet_onboard/subnet_onboard.py new file mode 100644 index 000000000..10c6ab4c2 --- /dev/null +++ b/neutronclient/osc/v2/subnet_onboard/subnet_onboard.py @@ -0,0 +1,59 @@ +# Copyright (c) 2019 SUSE Linux Products GmbH +# 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. +# + +"""Subnet onboard action implementation""" +import logging + +from osc_lib.command import command +from osc_lib import exceptions + +from neutronclient._i18n import _ + +LOG = logging.getLogger(__name__) + + +class NetworkOnboardSubnets(command.Command): + """Onboard network subnets into a subnet pool""" + + def get_parser(self, prog_name): + parser = super(NetworkOnboardSubnets, self).get_parser(prog_name) + parser.add_argument( + 'network', + metavar="", + help=_("Onboard all subnets associated with this network") + ) + parser.add_argument( + 'subnetpool', + metavar="", + help=_("Target subnet pool for onboarding subnets") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + subnetpool_id = _get_id(client, parsed_args.subnetpool, 'subnetpool') + network_id = _get_id(client, parsed_args.network, 'network') + body = {'network_id': network_id} + try: + client.onboard_network_subnets(subnetpool_id, body) + except Exception as e: + msg = (_("Failed to onboard subnets for network '%(n)s': %(e)s") + % {'n': parsed_args.network, 'e': e}) + raise exceptions.CommandError(msg) + + +def _get_id(client, id_or_name, resource): + return client.find_resource(resource, str(id_or_name))['id'] diff --git a/neutronclient/tests/unit/osc/v2/subnet_onboard/__init__.py b/neutronclient/tests/unit/osc/v2/subnet_onboard/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/tests/unit/osc/v2/subnet_onboard/test_network_onboard_subnets.py b/neutronclient/tests/unit/osc/v2/subnet_onboard/test_network_onboard_subnets.py new file mode 100644 index 000000000..efd3d1d11 --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/subnet_onboard/test_network_onboard_subnets.py @@ -0,0 +1,54 @@ +# Copyright (c) 2019 SUSE Linux Products GmbH +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +import mock + +from neutronclient.osc.v2.subnet_onboard import subnet_onboard +from neutronclient.tests.unit.osc.v2 import fakes as test_fakes + + +def _get_id(client, id_or_name, resource): + return id_or_name + + +class TestNetworkOnboardSubnets(test_fakes.TestNeutronClientOSCV2): + + def setUp(self): + super(TestNetworkOnboardSubnets, self).setUp() + mock.patch( + 'neutronclient.osc.v2.subnet_onboard.subnet_onboard._get_id', + new=_get_id).start() + + self.network_id = 'my_network_id' + self.subnetpool_id = 'my_subnetpool_id' + + # Get the command object to test + self.cmd = subnet_onboard.NetworkOnboardSubnets(self.app, + self.namespace) + + def test_options(self): + arglist = [ + self.network_id, + self.subnetpool_id + ] + verifylist = [ + ('network', self.network_id), + ('subnetpool', self.subnetpool_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.cmd.take_action(parsed_args) + self.neutronclient.onboard_network_subnets.assert_called_once_with( + self.subnetpool_id, {'network_id': self.network_id}) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 560b531d0..ed8dbd49d 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -494,6 +494,7 @@ class Client(ClientBase): port_path = "/ports/%s" subnets_path = "/subnets" subnet_path = "/subnets/%s" + onboard_network_subnets_path = "/subnetpools/%s/onboard_network_subnets" subnetpools_path = "/subnetpools" subnetpool_path = "/subnetpools/%s" address_scopes_path = "/address-scopes" @@ -2370,6 +2371,11 @@ def list_network_loggable_resources(self, retrieve_all=True, **_params): return self.list('loggable_resources', self.network_loggables_path, retrieve_all, **_params) + def onboard_network_subnets(self, subnetpool, body=None): + """Onboard the specified network's subnets into a subnet pool.""" + return self.put(self.onboard_network_subnets_path % (subnetpool), + body=body) + def __init__(self, **kwargs): """Initialize a new client for the Neutron v2.0 API.""" super(Client, self).__init__(**kwargs) diff --git a/releasenotes/notes/add-subnet-onboard-e60772bc4984f698.yaml b/releasenotes/notes/add-subnet-onboard-e60772bc4984f698.yaml new file mode 100644 index 000000000..358fdf006 --- /dev/null +++ b/releasenotes/notes/add-subnet-onboard-e60772bc4984f698.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add ``network onboard subnets`` OSC command to enable subnet onboard support from the CLI + [Blueprint `subnet-onboard `_] diff --git a/setup.cfg b/setup.cfg index b5a837b25..0262adf9e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -171,6 +171,8 @@ openstack.neutronclient.v2 = vpn_ipsec_site_connection_set = neutronclient.osc.v2.vpnaas.ipsec_site_connection:SetIPsecSiteConnection vpn_ipsec_site_connection_show = neutronclient.osc.v2.vpnaas.ipsec_site_connection:ShowIPsecSiteConnection + network_onboard_subnets = neutronclient.osc.v2.subnet_onboard.subnet_onboard:NetworkOnboardSubnets + neutron.cli.v2 = bash-completion = neutronclient.shell:BashCompletionCommand From ca875adf388599d2cd0a30c15a2c8cd7c52efe8f Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Mon, 18 Mar 2019 14:53:55 +0000 Subject: [PATCH 712/845] Update master for stable/stein Add file to the reno documentation build to show release notes for stable/stein. Use pbr instruction to increment the minor version number automatically so that master versions are higher than the versions on stable/stein. Change-Id: I9add4f457af409830f3faff0af2661e0b3909b40 Sem-Ver: feature --- releasenotes/source/index.rst | 1 + releasenotes/source/stein.rst | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 releasenotes/source/stein.rst diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index f73e97659..446a2a869 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -6,6 +6,7 @@ :maxdepth: 1 unreleased + stein rocky queens pike diff --git a/releasenotes/source/stein.rst b/releasenotes/source/stein.rst new file mode 100644 index 000000000..efaceb667 --- /dev/null +++ b/releasenotes/source/stein.rst @@ -0,0 +1,6 @@ +=================================== + Stein Series Release Notes +=================================== + +.. release-notes:: + :branch: stable/stein From d572f2cdd209b4b4ad114a194ed3d75b6e71e9f2 Mon Sep 17 00:00:00 2001 From: Ian Wienand Date: Sun, 24 Mar 2019 20:35:53 +0000 Subject: [PATCH 713/845] Replace openstack.org git:// URLs with https:// This is a mechanically generated change to replace openstack.org git:// URLs with https:// equivalents. This is in aid of a planned future move of the git hosting infrastructure to a self-hosted instance of gitea (https://gitea.io), which does not support the git wire protocol at this stage. This update should result in no functional change. For more information see the thread at http://lists.openstack.org/pipermail/openstack-discuss/2019-March/003825.html Change-Id: I15a69bcf6d51bfb1d5ade8f5119bb77bb2387f77 --- neutronclient/tests/functional/hooks/fwaas | 2 +- neutronclient/tests/functional/hooks/vpnaas | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/neutronclient/tests/functional/hooks/fwaas b/neutronclient/tests/functional/hooks/fwaas index 8f53b16a0..5292897ce 100644 --- a/neutronclient/tests/functional/hooks/fwaas +++ b/neutronclient/tests/functional/hooks/fwaas @@ -1,2 +1,2 @@ -enable_plugin neutron-fwaas git://git.openstack.org/openstack/neutron-fwaas +enable_plugin neutron-fwaas https://git.openstack.org/openstack/neutron-fwaas enable_service q-fwaas diff --git a/neutronclient/tests/functional/hooks/vpnaas b/neutronclient/tests/functional/hooks/vpnaas index 11cecb91f..b121a6f0e 100644 --- a/neutronclient/tests/functional/hooks/vpnaas +++ b/neutronclient/tests/functional/hooks/vpnaas @@ -1 +1 @@ -enable_plugin neutron-vpnaas git://git.openstack.org/openstack/neutron-vpnaas +enable_plugin neutron-vpnaas https://git.openstack.org/openstack/neutron-vpnaas From da62ba30297d78dbdcf6c454a3c539cf91bd2d16 Mon Sep 17 00:00:00 2001 From: Ghanshyam Mann Date: Mon, 15 Apr 2019 02:30:04 +0000 Subject: [PATCH 714/845] Dropping the py35 testing All the integration testing has been moved to Bionic now[1] and py3.5 is not tested runtime for Train or stable/stein[2]. As per below ML thread, we are good to drop the py35 testing now: http://lists.openstack.org/pipermail/openstack-discuss/2019-April/005097.html [1] http://lists.openstack.org/pipermail/openstack-discuss/2019-April/004647.html [2] https://governance.openstack.org/tc/reference/runtimes/stein.html https://governance.openstack.org/tc/reference/runtimes/train.html Change-Id: I612c7c40e07f23afa920c92bf4b51b278282dd9d --- .zuul.yaml | 1 - setup.cfg | 1 - tox.ini | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.zuul.yaml b/.zuul.yaml index d38182be9..db6d10cd9 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -3,7 +3,6 @@ - openstack-cover-jobs - openstack-lower-constraints-jobs - openstack-python-jobs - - openstack-python35-jobs - openstack-python36-jobs - openstack-python37-jobs - publish-openstack-docs-pti diff --git a/setup.cfg b/setup.cfg index 0262adf9e..867678087 100644 --- a/setup.cfg +++ b/setup.cfg @@ -17,7 +17,6 @@ classifier = Programming Language :: Python :: 2 Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.6 [files] diff --git a/tox.ini b/tox.ini index cae97f1cb..d4ba1b5f6 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] # py3 first to avoid .testrepository incompatibility -envlist = py35,py27,pypy,pep8 +envlist = py36,py27,pypy,pep8 minversion = 2.3.2 skipsdist = True From b62a1cd1a469c9fbbf188646b8ecc44aa9a0f463 Mon Sep 17 00:00:00 2001 From: OpenDev Sysadmins Date: Fri, 19 Apr 2019 19:44:46 +0000 Subject: [PATCH 715/845] OpenDev Migration Patch This commit was bulk generated and pushed by the OpenDev sysadmins as a part of the Git hosting and code review systems migration detailed in these mailing list posts: http://lists.openstack.org/pipermail/openstack-discuss/2019-March/003603.html http://lists.openstack.org/pipermail/openstack-discuss/2019-April/004920.html Attempts have been made to correct repository namespaces and hostnames based on simple pattern matching, but it's possible some were updated incorrectly or missed entirely. Please reach out to us via the contact information listed at https://opendev.org/ with any questions you may have. --- .gitreview | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitreview b/.gitreview index 994b07a43..faf5d1f5b 100644 --- a/.gitreview +++ b/.gitreview @@ -1,4 +1,4 @@ [gerrit] -host=review.openstack.org +host=review.opendev.org port=29418 project=openstack/python-neutronclient.git From 9a91b4ad38ac5ff3ab2e2cdc0dd63727f0e90691 Mon Sep 17 00:00:00 2001 From: jacky06 Date: Tue, 23 Apr 2019 13:44:39 +0800 Subject: [PATCH 716/845] Replace git.openstack.org URLs with opendev.org URLs Change-Id: I3cc418bd8219a3d4f3ab5c018adf06b66ef36b46 --- README.rst | 2 +- doc/source/contributor/transition_to_osc.rst | 10 +++++----- .../neutron/v2_0/qos/minimum_bandwidth_rule.py | 2 +- neutronclient/tests/functional/hooks/fwaas | 2 +- neutronclient/tests/functional/hooks/vpnaas | 2 +- tox.ini | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/README.rst b/README.rst index cec75c908..6f57c698c 100644 --- a/README.rst +++ b/README.rst @@ -32,6 +32,6 @@ provides a Python API (the ``neutronclient`` module) and a command-line tool .. _Launchpad project: https://launchpad.net/python-neutronclient .. _Blueprints: https://blueprints.launchpad.net/python-neutronclient .. _Bugs: https://bugs.launchpad.net/python-neutronclient -.. _Source: https://git.openstack.org/cgit/openstack/python-neutronclient +.. _Source: https://opendev.org/openstack/python-neutronclient .. _Developer's Guide: http://docs.openstack.org/infra/manual/developers.html .. _Release Notes: https://docs.openstack.org/releasenotes/python-neutronclient diff --git a/doc/source/contributor/transition_to_osc.rst b/doc/source/contributor/transition_to_osc.rst index d64658d06..b745d2f0c 100644 --- a/doc/source/contributor/transition_to_osc.rst +++ b/doc/source/contributor/transition_to_osc.rst @@ -29,7 +29,7 @@ command-line interface (CLI), to the `OpenStack Client (OSC) `_ and the `OpenStack Python SDK `_. This transition is being guided by the -`Deprecate individual CLIs in favour of OSC `_ +`Deprecate individual CLIs in favour of OSC `_ OpenStack spec. See the `Neutron RFE `_, `OSC neutron support etherpad `_ and details below for the overall progress of this transition. @@ -56,16 +56,16 @@ Transition Steps ---------------- 1. **Done:** OSC adds OpenStack Python SDK as a dependency. See the following - patch set: https://review.openstack.org/#/c/138745/ + patch set: https://review.opendev.org/#/c/138745/ 2. **Done:** OSC switches its networking support for the `network `_ command object to use the OpenStack Python SDK instead of the neutron client's Python library. See the following patch set: - https://review.openstack.org/#/c/253348/ + https://review.opendev.org/#/c/253348/ 3. **Done:** OSC removes its python-neutronclient dependency. - See the following patch set: https://review.openstack.org/#/c/255545/ + See the following patch set: https://review.opendev.org/#/c/255545/ 4. **In Progress:** OpenStack Python SDK releases version 1.0 to guarantee backwards compatibility of its networking support and OSC updates @@ -99,7 +99,7 @@ Transition Steps developer guide section below for more information on this step. 7. **In Progress:** Deprecate the ``neutron`` CLI. Running the CLI after - it has been `deprecated `_ + it has been `deprecated `_ will issue a warning message: ``neutron CLI is deprecated and will be removed in the future. Use openstack CLI instead.`` In addition, no new features will be added to the CLI, though fixes to diff --git a/neutronclient/neutron/v2_0/qos/minimum_bandwidth_rule.py b/neutronclient/neutron/v2_0/qos/minimum_bandwidth_rule.py index cc64549af..0dda0fb3d 100644 --- a/neutronclient/neutron/v2_0/qos/minimum_bandwidth_rule.py +++ b/neutronclient/neutron/v2_0/qos/minimum_bandwidth_rule.py @@ -31,7 +31,7 @@ def add_minimum_bandwidth_arguments(parser): help=_('QoS minimum bandwidth assurance, expressed in kilobits ' 'per second.')) # NOTE(ralonsoh): the only direction implemented is "egress". Please, - # refer to the spec (https://review.openstack.org/#/c/316082/). + # refer to the spec (https://review.opendev.org/#/c/316082/). parser.add_argument( '--direction', # NOTE(ihrachys): though server picks the default for us (egress), it's diff --git a/neutronclient/tests/functional/hooks/fwaas b/neutronclient/tests/functional/hooks/fwaas index 5292897ce..d9fc704ef 100644 --- a/neutronclient/tests/functional/hooks/fwaas +++ b/neutronclient/tests/functional/hooks/fwaas @@ -1,2 +1,2 @@ -enable_plugin neutron-fwaas https://git.openstack.org/openstack/neutron-fwaas +enable_plugin neutron-fwaas https://opendev.org/openstack/neutron-fwaas enable_service q-fwaas diff --git a/neutronclient/tests/functional/hooks/vpnaas b/neutronclient/tests/functional/hooks/vpnaas index b121a6f0e..8b94b37b0 100644 --- a/neutronclient/tests/functional/hooks/vpnaas +++ b/neutronclient/tests/functional/hooks/vpnaas @@ -1 +1 @@ -enable_plugin neutron-vpnaas https://git.openstack.org/openstack/neutron-vpnaas +enable_plugin neutron-vpnaas https://opendev.org/openstack/neutron-vpnaas diff --git a/tox.ini b/tox.ini index cae97f1cb..fcc934cd2 100644 --- a/tox.ini +++ b/tox.ini @@ -12,7 +12,7 @@ setenv = VIRTUAL_ENV={envdir} PYTHONWARNINGS=default::DeprecationWarning usedevelop = True install_command = pip install {opts} {packages} -deps = -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} +deps = -c{env:UPPER_CONSTRAINTS_FILE:https://opendev.org/openstack/requirements/raw/branch/master/upper-constraints.txt} -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt # Delete bytecodes from normal directories before running tests. From dc10f44128b26e7c964957c5e9c8d00ff91a3c0e Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Fri, 10 May 2019 10:54:03 +0900 Subject: [PATCH 717/845] doc: Remove prompt from python binding examples The current python binding examples have prompts of python interactive mode, but these prompts make it difficult to copy-and-paste the examples. This commit removes them. Change-Id: Ia5d35fbb585ed0d0d11c8d035196981e9dd46785 --- doc/source/reference/index.rst | 72 +++++++++++++++++----------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/doc/source/reference/index.rst b/doc/source/reference/index.rst index ee4761053..5678d6578 100644 --- a/doc/source/reference/index.rst +++ b/doc/source/reference/index.rst @@ -33,23 +33,23 @@ information on this keystoneauth API, see `Using Sessions`_. .. code-block:: python - >>> from keystoneauth1 import identity - >>> from keystoneauth1 import session - >>> from neutronclient.v2_0 import client - >>> username='username' - >>> password='password' - >>> project_name='demo' - >>> project_domain_id='default' - >>> user_domain_id='default' - >>> auth_url='http://auth.example.com:5000/v3' - >>> auth = identity.Password(auth_url=auth_url, - ... username=username, - ... password=password, - ... project_name=project_name, - ... project_domain_id=project_domain_id, - ... user_domain_id=user_domain_id) - >>> sess = session.Session(auth=auth) - >>> neutron = client.Client(session=sess) + from keystoneauth1 import identity + from keystoneauth1 import session + from neutronclient.v2_0 import client + username='username' + password='password' + project_name='demo' + project_domain_id='default' + user_domain_id='default' + auth_url='http://auth.example.com:5000/v3' + auth = identity.Password(auth_url=auth_url, + username=username, + password=password, + project_name=project_name, + project_domain_id=project_domain_id, + user_domain_id=user_domain_id) + sess = session.Session(auth=auth) + neutron = client.Client(session=sess) If you are using Identity v2.0 API (DEPRECATED), create an auth plugin using the appropriate parameters and `keystoneauth1.identity` will handle Identity @@ -58,38 +58,38 @@ like the previous example. .. code-block:: python - >>> auth = identity.Password(auth_url=auth_url, - ... username=username, - ... password=password, - ... project_name=project_name) - >>> # create a Session and a Neutronclient + auth = identity.Password(auth_url=auth_url, + username=username, + password=password, + project_name=project_name) + # create a Session and a Neutronclient Now you can call various methods on the client instance. .. code-block:: python - >>> network = {'name': 'mynetwork', 'admin_state_up': True} - >>> neutron.create_network({'network':network}) - >>> networks = neutron.list_networks(name='mynetwork') - >>> print networks - >>> network_id = networks['networks'][0]['id'] - >>> neutron.delete_network(network_id) + network = {'name': 'mynetwork', 'admin_state_up': True} + neutron.create_network({'network':network}) + networks = neutron.list_networks(name='mynetwork') + print networks + network_id = networks['networks'][0]['id'] + neutron.delete_network(network_id) Alternatively, you can create a client instance using an auth token and a service endpoint URL directly. .. code-block:: python - >>> from neutronclient.v2_0 import client - >>> neutron = client.Client(endpoint_url='http://192.168.206.130:9696/', - ... token='d3f9226f27774f338019aa2611112ef6') + from neutronclient.v2_0 import client + neutron = client.Client(endpoint_url='http://192.168.206.130:9696/', + token='d3f9226f27774f338019aa2611112ef6') You can get ``X-Openstack-Request-Id`` as ``request_ids`` from the result. .. code-block:: python - >>> network = {'name': 'mynetwork', 'admin_state_up': True} - >>> neutron.create_network({'network':network}) - >>> networks = neutron.list_networks(name='mynetwork') - >>> print networks.request_ids - ['req-978a0160-7ab0-44f0-8a93-08e9a4e785fa'] + network = {'name': 'mynetwork', 'admin_state_up': True} + neutron.create_network({'network':network}) + networks = neutron.list_networks(name='mynetwork') + print networks.request_ids + # -> ['req-978a0160-7ab0-44f0-8a93-08e9a4e785fa'] From e2ee92e2e1d87694cdffcd2e49974c5461d91a55 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Tue, 28 May 2019 11:49:52 +0900 Subject: [PATCH 718/845] Blacklist bandit 1.6.0 due to directory exclusion bug Bandit 1.6.0 introduces a regression[0] with the -x option, a fix is expected to be included in 1.6.1 soon. This commit also aligns Sphinx requirement with the requirements project [2]. This is required to pass requirements-check. The lower bound for sphinx was missing and requirements-check now requires it, so the lower bound sphinx >=1.6.2 was added. [0] https://github.com/PyCQA/bandit/issues/488 [1] https://github.com/PyCQA/bandit/pull/489 [2] https://review.opendev.org/#/c/657890/ Change-Id: I937cfa722f5234ca4d5506047001d9cb07728cd8 --- doc/requirements.txt | 3 ++- test-requirements.txt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/requirements.txt b/doc/requirements.txt index d959d4431..af1aeb8b3 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -3,4 +3,5 @@ # process, which may cause wedges in the gate later. openstackdocstheme>=1.18.1 # Apache-2.0 reno>=2.5.0 # Apache-2.0 -sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD +sphinx!=1.6.6,!=1.6.7,>=1.6.2,<2.0.0;python_version=='2.7' # BSD +sphinx!=1.6.6,!=1.6.7,>=1.6.2;python_version>='3.4' # BSD diff --git a/test-requirements.txt b/test-requirements.txt index dabdb0e02..a1eb9b1d1 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -3,7 +3,7 @@ # process, which may cause wedges in the gate later. hacking>=1.1.0 # Apache-2.0 -bandit>=1.1.0 # Apache-2.0 +bandit!=1.6.0,>=1.1.0 # Apache-2.0 coverage!=4.4,>=4.0 # Apache-2.0 fixtures>=3.0.0 # Apache-2.0/BSD flake8-import-order==0.12 # LGPLv3 From cf95be3ea5e4396f26564a76e5ab67342afe6de5 Mon Sep 17 00:00:00 2001 From: Corey Bryant Date: Fri, 5 Jul 2019 11:46:03 -0400 Subject: [PATCH 719/845] Add Python 3 Train unit tests This is a mechanically generated patch to ensure unit testing is in place for all of the Tested Runtimes for Train. See the Train python3-updates goal document for details: https://governance.openstack.org/tc/goals/train/python3-updates.html Change-Id: If79694b32ece897c93d1889da28daff641e46f1c Story: #2005924 Task: #34225 --- .zuul.yaml | 3 +-- setup.cfg | 1 + tox.ini | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.zuul.yaml b/.zuul.yaml index db6d10cd9..80a5f4e4f 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -3,8 +3,7 @@ - openstack-cover-jobs - openstack-lower-constraints-jobs - openstack-python-jobs - - openstack-python36-jobs - - openstack-python37-jobs + - openstack-python3-train-jobs - publish-openstack-docs-pti - check-requirements - lib-forward-testing diff --git a/setup.cfg b/setup.cfg index 867678087..0045e57ec 100644 --- a/setup.cfg +++ b/setup.cfg @@ -18,6 +18,7 @@ classifier = Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 [files] packages = diff --git a/tox.ini b/tox.ini index a5f18cc5a..5ef67b98d 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] # py3 first to avoid .testrepository incompatibility -envlist = py36,py27,pypy,pep8 +envlist = py37,py27,pypy,pep8 minversion = 2.3.2 skipsdist = True From 34924865b3f684fa22f6356c64d03d42d11d46f6 Mon Sep 17 00:00:00 2001 From: zhanghao2 Date: Tue, 16 Jul 2019 22:41:52 -0400 Subject: [PATCH 720/845] Add friendly event hints for logging When creating a network log with the '--event' parameter, it is impossible to know which parameter values can be specified, and it is necessary to add a prompt. Change-Id: I6aa4f95b650cdc7b04e74b851a78a2ae42fc288a --- neutronclient/osc/v2/logging/network_log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/osc/v2/logging/network_log.py b/neutronclient/osc/v2/logging/network_log.py index 1946a4066..df48fc1e5 100644 --- a/neutronclient/osc/v2/logging/network_log.py +++ b/neutronclient/osc/v2/logging/network_log.py @@ -118,7 +118,7 @@ def get_parser(self, prog_name): help=_('Name for the network log')) parser.add_argument( '--event', - metavar='', + metavar='{ALL,ACCEPT,DROP}', choices=['ALL', 'ACCEPT', 'DROP'], type=nc_utils.convert_to_uppercase, help=_('An event to store with log')) From 2af19d55d6029712f3888a37c019be5a25eba1fe Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Thu, 18 Jul 2019 16:31:38 +0900 Subject: [PATCH 721/845] Run functional test correctly legacy-neutronclient-test-dsvm-functional job actually runs neutronclient.tests.unit because .stestr.conf does not honor OS_TEST_PATH. This commit fixes .stestr.conf to honor OS_TEST_PATH specified in tox.ini. Also fixes the logic of is_extension_enabled() in ClientTestBase to check whether FWaaS v1 (fwaas) is enabled correctly. Previously the logic checks a substring of a specified extension, so 'fwaas' (FWaaS v1) matches 'fwaas_v2' (FWaaS v2), which leads to a failure of FWaaS v1 CLI tests. Change-Id: I958ad496b16cca8d03a7b84ebf5f8f031e4248ea --- .stestr.conf | 2 +- neutronclient/tests/functional/base.py | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.stestr.conf b/.stestr.conf index 6a6b6f120..875bb1461 100644 --- a/.stestr.conf +++ b/.stestr.conf @@ -1,3 +1,3 @@ [DEFAULT] -test_path=./neutronclient/tests/unit +test_path=${OS_TEST_PATH:-./neutronclient/tests/unit} top_dir=./ diff --git a/neutronclient/tests/functional/base.py b/neutronclient/tests/functional/base.py index d92d6936d..655c6c4f8 100644 --- a/neutronclient/tests/functional/base.py +++ b/neutronclient/tests/functional/base.py @@ -76,7 +76,5 @@ def neutron_non_admin(self, *args, **kwargs): def is_extension_enabled(self, extension_alias): extensions = self.parser.listing(self.neutron('ext-list')) - for extension in extensions: - if extension_alias in extension['alias']: - return True - return False + aliases = [e['alias'] for e in extensions] + return extension_alias in aliases From 6330cc1980958f57cb95684c8d7a1aaa89a7fbf6 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Thu, 18 Jul 2019 20:55:38 +0900 Subject: [PATCH 722/845] Convert legacy functional jobs to zuulv3 This commit merges the legacy functional test jobs (core and adv-svcs) into neutronclient-functional. FWaaS v1 was dropped in Stein and adv-svcs job now checks only VPNaaS, so it looks too much to have a separate job for VPNaaS. Note that tls-proxy is disabled as the base class for functional tests (neutronclient.tests.functional.base.ClientTestBase) does not support HTTPS endpoints. This can be tackled separately. Change-Id: I714efd1bc14cbba85f7b4caf6834ce375ff89547 --- .zuul.yaml | 35 +++++++++++++++++++++++------------ tox.ini | 7 +------ 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/.zuul.yaml b/.zuul.yaml index 80a5f4e4f..60c7788e3 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -12,20 +12,10 @@ - openstackclient-plugin-jobs check: jobs: - - legacy-neutronclient-test-dsvm-functional: - irrelevant-files: &project-irrelevant-files - - ^.*\.rst$ - - ^doc/.*$ - - ^neutron/locale/.*$ - - ^releasenotes/.*$ - - legacy-neutronclient-test-dsvm-functional-adv-svcs: - irrelevant-files: *project-irrelevant-files + - neutronclient-functional gate: jobs: - - legacy-neutronclient-test-dsvm-functional: - irrelevant-files: *project-irrelevant-files - - legacy-neutronclient-test-dsvm-functional-adv-svcs: - irrelevant-files: *project-irrelevant-files + - neutronclient-functional experimental: jobs: - legacy-grenade-dsvm-neutron-libs: @@ -33,3 +23,24 @@ - ^(test-|)requirements.txt$ - ^setup.cfg$ +- job: + name: neutronclient-functional + parent: devstack-tox-functional + irrelevant-files: + - ^.*\.rst$ + - ^doc/.*$ + - ^releasenotes/.*$ + required-projects: + - openstack/python-neutronclient + - openstack/neutron + - openstack/neutron-vpnaas + vars: + tox_envlist: functional + devstack_services: + # NOTE: neutronclient.tests.functional.base.ClientTestBase does not + # support HTTPS endpoints now, so tls-proxy needs to be disabled. + tls-proxy: false + devstack_localrc: + LIBS_FROM_GIT: python-neutronclient + devstack_plugins: + neutron-vpnaas: https://opendev.org/openstack/neutron-vpnaas diff --git a/tox.ini b/tox.ini index 5ef67b98d..13a45d2d2 100644 --- a/tox.ini +++ b/tox.ini @@ -37,12 +37,7 @@ commands = {posargs} [testenv:functional] setenv = - OS_TEST_PATH = ./neutronclient/tests/functional/core - OS_NEUTRONCLIENT_EXEC_DIR = {envdir}/bin - -[testenv:functional-adv-svcs] -setenv = - OS_TEST_PATH = ./neutronclient/tests/functional/adv-svcs + OS_TEST_PATH = ./neutronclient/tests/functional OS_NEUTRONCLIENT_EXEC_DIR = {envdir}/bin [testenv:cover] From 1a8fdf355de233ccbc7aa0beb588fb02d436aa5c Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Wed, 24 Jul 2019 23:00:09 +0900 Subject: [PATCH 723/845] Switch functional job to python3 Change-Id: I7aa4566b8f2c81da6a00e664908506f5628cd0a1 --- .zuul.yaml | 1 + tox.ini | 1 + 2 files changed, 2 insertions(+) diff --git a/.zuul.yaml b/.zuul.yaml index 60c7788e3..0210d6406 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -41,6 +41,7 @@ # support HTTPS endpoints now, so tls-proxy needs to be disabled. tls-proxy: false devstack_localrc: + USE_PYTHON3: true LIBS_FROM_GIT: python-neutronclient devstack_plugins: neutron-vpnaas: https://opendev.org/openstack/neutron-vpnaas diff --git a/tox.ini b/tox.ini index 13a45d2d2..e248ee06d 100644 --- a/tox.ini +++ b/tox.ini @@ -36,6 +36,7 @@ basepython = python3 commands = {posargs} [testenv:functional] +basepython = python3 setenv = OS_TEST_PATH = ./neutronclient/tests/functional OS_NEUTRONCLIENT_EXEC_DIR = {envdir}/bin From ab426a791ad1937ea2cf3b340202b3968a378978 Mon Sep 17 00:00:00 2001 From: Oleg Bondarev Date: Tue, 13 Aug 2019 18:10:49 +0400 Subject: [PATCH 724/845] Use secure sha256 instead of sha1 Fix for bandit B303: Use of insecure MD2, MD4, MD5, or SHA1 hash function. Change-Id: I00403d7bd3b40ae00420e6cddcf40f45488284a9 Partial-Bug: #1759250 --- neutronclient/common/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/common/utils.py b/neutronclient/common/utils.py index 3a0469dbb..994e2a9db 100644 --- a/neutronclient/common/utils.py +++ b/neutronclient/common/utils.py @@ -183,7 +183,7 @@ def http_log_req(_logger, args, kwargs): for (key, value) in six.iteritems(kwargs['headers']): if key in SENSITIVE_HEADERS: v = value.encode('utf-8') - h = hashlib.sha1(v) + h = hashlib.sha256(v) d = h.hexdigest() value = "{SHA1}%s" % d header = ' -H "%s: %s"' % (key, value) From 62f4868e6e240ebbb4d6e29b8c1e1d23a24fcc15 Mon Sep 17 00:00:00 2001 From: Oleg Bondarev Date: Tue, 20 Aug 2019 15:15:36 +0400 Subject: [PATCH 725/845] Fix string in header Commit ab426a791ad1937ea2cf3b340202b3968a378978 changed sha1 to sha256. Need to change string correspondingly Change-Id: I8b35350c32ab551d513f21325931d6697f62fb2f Related-Bug: #1759250 --- neutronclient/common/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/common/utils.py b/neutronclient/common/utils.py index 994e2a9db..b61ee3b7a 100644 --- a/neutronclient/common/utils.py +++ b/neutronclient/common/utils.py @@ -185,7 +185,7 @@ def http_log_req(_logger, args, kwargs): v = value.encode('utf-8') h = hashlib.sha256(v) d = h.hexdigest() - value = "{SHA1}%s" % d + value = "{SHA256}%s" % d header = ' -H "%s: %s"' % (key, value) string_parts.append(header) From e35b3c160b5314ff573e913f3236cc98ccf500bc Mon Sep 17 00:00:00 2001 From: jiasirui Date: Thu, 29 Aug 2019 16:09:56 +0800 Subject: [PATCH 726/845] Improve help text Change-Id: Idc8f55567d0d4c95968d34d3b7aa95048623d9d0 --- neutronclient/shell.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 6dced26cd..fbc91209f 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -222,7 +222,7 @@ def build_option_parser(self, description, version): type=check_non_negative_int, default=0, help=_("How many times the request to the Neutron server should " - "be retried if it fails.")) + "be retried if it fails. Defaults to 0.")) # FIXME(bklei): this method should come from keystoneauth1 self._append_global_identity_args(parser) @@ -241,12 +241,12 @@ def _append_global_identity_args(self, parser): parser.add_argument( '--os-service-type', metavar='', default=env('OS_NETWORK_SERVICE_TYPE', default='network'), - help=_('Defaults to env[OS_NETWORK_SERVICE_TYPE] or network.')) + help=_('Defaults to env[OS_NETWORK_SERVICE_TYPE] or "network".')) parser.add_argument( '--os-endpoint-type', metavar='', default=env('OS_ENDPOINT_TYPE', default='public'), - help=_('Defaults to env[OS_ENDPOINT_TYPE] or public.')) + help=_('Defaults to env[OS_ENDPOINT_TYPE] or "public".')) # FIXME(bklei): --service-type is deprecated but kept in for # backward compatibility. From d6c78a5d3995c36f4641d462009902c32b70795f Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Tue, 27 Aug 2019 16:27:45 +0900 Subject: [PATCH 727/845] PDF documentation build Change-Id: I0abc5df1dfde23756bc2fd78631141ba685dc6fe Story: 2006099 Task: 35139 --- doc/source/conf.py | 19 +++++++++++++++++++ tox.ini | 10 ++++++++++ 2 files changed, 29 insertions(+) diff --git a/doc/source/conf.py b/doc/source/conf.py index b1c6ceece..294de7129 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -49,6 +49,25 @@ # Output file base name for HTML help builder. htmlhelp_basename = 'neutronclientdoc' +# -- Options for LaTeX output ------------------------------------------------ + +latex_documents = [ + ('index', 'doc-python-neutronclient.tex', + u'python-neutronclient Documentation', + u'Neutron Contributors', 'manual'), +] + +# Disable usage of xindy https://bugzilla.redhat.com/show_bug.cgi?id=1643664 +latex_use_xindy = False + +latex_domain_indices = False + +latex_elements = { + 'makeindex': '', + 'printindex': '', + 'preamble': r'\setcounter{tocdepth}{5}', +} + # -- Options for cliff.sphinxext plugin --------------------------------------- autoprogram_cliff_application = 'openstack' diff --git a/tox.ini b/tox.ini index e248ee06d..f9458eaa3 100644 --- a/tox.ini +++ b/tox.ini @@ -58,6 +58,16 @@ basepython = python3 deps = -r{toxinidir}/doc/requirements.txt commands = sphinx-build -W -b html doc/source doc/build/html +[testenv:pdf-docs] +basepython = python3 +envdir = {toxworkdir}/docs +deps = {[testenv:docs]deps} +whitelist_externals = + make +commands = + sphinx-build -W -b latex doc/source doc/build/pdf + make -C doc/build/pdf + [testenv:releasenotes] basepython = python3 deps = -r{toxinidir}/doc/requirements.txt From 1c634ed2c16195e5a2e67400902f01450df30d98 Mon Sep 17 00:00:00 2001 From: Bence Romsics Date: Wed, 7 Aug 2019 16:26:38 +0200 Subject: [PATCH 728/845] Add router add/remove extra routes operations Add methods to the client to call the two new member actions introduced by new Neutron extension: extraroute-atomic. Change-Id: Idb1fa073c2ff31f566b376cfa4f63cf27fecec86 Partial-Bug: #1826396 (rfe) Related-Change: https://review.opendev.org/655680 (spec) --- neutronclient/v2_0/client.py | 10 ++++++++++ .../notes/extraroute-atomic-b11919d8e33b0d92.yaml | 5 +++++ 2 files changed, 15 insertions(+) create mode 100644 releasenotes/notes/extraroute-atomic-b11919d8e33b0d92.yaml diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index ed8dbd49d..c338c316e 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -934,6 +934,16 @@ def remove_interface_router(self, router, body=None): return self.put((self.router_path % router) + "/remove_router_interface", body=body) + def add_extra_routes_to_router(self, router, body=None): + """Adds extra routes to the specified router.""" + return self.put((self.router_path % router) + "/add_extraroutes", + body=body) + + def remove_extra_routes_from_router(self, router, body=None): + """Removes extra routes from the specified router.""" + return self.put((self.router_path % router) + "/remove_extraroutes", + body=body) + def add_gateway_router(self, router, body=None): """Adds an external network gateway to the specified router.""" return self.put((self.router_path % router), diff --git a/releasenotes/notes/extraroute-atomic-b11919d8e33b0d92.yaml b/releasenotes/notes/extraroute-atomic-b11919d8e33b0d92.yaml new file mode 100644 index 000000000..fed4a1e10 --- /dev/null +++ b/releasenotes/notes/extraroute-atomic-b11919d8e33b0d92.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + New client methods: ``add_extra_routes_to_router`` and + ``remove_extra_routes_from_router``. From f61cd94e11e30a3e5dcf646e0b0a91e80f7d027b Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Fri, 20 Sep 2019 17:41:48 +0000 Subject: [PATCH 729/845] Update master for stable/train Add file to the reno documentation build to show release notes for stable/train. Use pbr instruction to increment the minor version number automatically so that master versions are higher than the versions on stable/train. Change-Id: I1dd2ee12854ac7a55baa2114230ab42404dea5c1 Sem-Ver: feature --- releasenotes/source/index.rst | 1 + releasenotes/source/train.rst | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 releasenotes/source/train.rst diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index 446a2a869..f5df8b776 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -6,6 +6,7 @@ :maxdepth: 1 unreleased + train stein rocky queens diff --git a/releasenotes/source/train.rst b/releasenotes/source/train.rst new file mode 100644 index 000000000..583900393 --- /dev/null +++ b/releasenotes/source/train.rst @@ -0,0 +1,6 @@ +========================== +Train Series Release Notes +========================== + +.. release-notes:: + :branch: stable/train From ee08644c5f2424a40c70010dcf0fa2ad84809bfc Mon Sep 17 00:00:00 2001 From: zhanghao2 Date: Tue, 1 Oct 2019 05:19:34 -0400 Subject: [PATCH 730/845] Remove 'public' and 'private' parameters in fwaas_v2 The 'public' and 'private' parameters have been replaced with 'share' and 'no-share' parameters in the Pike version, they can be removed now. Change-Id: I57a2e228ec1cdb6ed259914abc38bdada036d369 --- neutronclient/osc/v2/fwaas/firewallgroup.py | 27 +++---------------- neutronclient/osc/v2/fwaas/firewallpolicy.py | 25 +++-------------- neutronclient/osc/v2/fwaas/firewallrule.py | 25 +++-------------- .../tests/unit/osc/v2/fwaas/common.py | 14 ---------- ...d-private-parameters-d683e7c30ecedc3b.yaml | 6 +++++ 5 files changed, 15 insertions(+), 82 deletions(-) create mode 100644 releasenotes/notes/remove-public-and-private-parameters-d683e7c30ecedc3b.yaml diff --git a/neutronclient/osc/v2/fwaas/firewallgroup.py b/neutronclient/osc/v2/fwaas/firewallgroup.py index fe8ab1188..4287132ea 100644 --- a/neutronclient/osc/v2/fwaas/firewallgroup.py +++ b/neutronclient/osc/v2/fwaas/firewallgroup.py @@ -77,19 +77,6 @@ def _get_common_parser(parser): action='store_true', help=_('Detach egress firewall policy from the firewall group')) shared_group = parser.add_mutually_exclusive_group() - shared_group.add_argument( - '--public', - action='store_true', - help=_('Make the firewall group public, which allows it to be ' - 'used in all projects (as opposed to the default, ' - 'which is to restrict its use to the current project). ' - 'This option is deprecated and would be removed in R release.')) - shared_group.add_argument( - '--private', - action='store_true', - help=_('Restrict use of the firewall group to the ' - 'current project. This option is deprecated ' - 'and would be removed in R release.')) shared_group.add_argument( '--share', @@ -147,9 +134,9 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): cmd_resource=const.CMD_FWP)['id'] elif parsed_args.no_egress_firewall_policy: attrs['egress_firewall_policy_id'] = None - if parsed_args.share or parsed_args.public: + if parsed_args.share: attrs['shared'] = True - if parsed_args.no_share or parsed_args.private: + if parsed_args.no_share: attrs['shared'] = False if parsed_args.enable: attrs['admin_state_up'] = True @@ -349,14 +336,6 @@ def get_parser(self, prog_name): dest='egress_firewall_policy', help=_('Egress firewall policy (name or ID) to delete')) shared_group = parser.add_mutually_exclusive_group() - shared_group.add_argument( - '--public', - action='store_true', - help=_('Make the firewall group public, which allows it to be ' - 'used in all projects (as opposed to the default, ' - 'which is to restrict its use to the current project). ' - 'This option is deprecated and would be removed in R' - ' release.')) shared_group.add_argument( '--share', action='store_true', @@ -375,7 +354,7 @@ def _get_attrs(self, client_manager, parsed_args): attrs['ingress_firewall_policy_id'] = None if parsed_args.egress_firewall_policy: attrs['egress_firewall_policy_id'] = None - if parsed_args.share or parsed_args.public: + if parsed_args.share: attrs['shared'] = False if parsed_args.enable: attrs['admin_state_up'] = False diff --git a/neutronclient/osc/v2/fwaas/firewallpolicy.py b/neutronclient/osc/v2/fwaas/firewallpolicy.py index 2c3d5675e..bd3e60d40 100644 --- a/neutronclient/osc/v2/fwaas/firewallpolicy.py +++ b/neutronclient/osc/v2/fwaas/firewallpolicy.py @@ -80,9 +80,9 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): attrs['name'] = str(parsed_args.name) if parsed_args.description: attrs['description'] = str(parsed_args.description) - if parsed_args.share or parsed_args.public: + if parsed_args.share: attrs['shared'] = True - if parsed_args.no_share or parsed_args.private: + if parsed_args.no_share: attrs['shared'] = False return attrs @@ -107,19 +107,6 @@ def _get_common_parser(parser): help=_('Share the firewall policy to be used in all projects ' '(by default, it is restricted to be used by the ' 'current project).')) - shared_group.add_argument( - '--public', - action='store_true', - help=_('Make the firewall policy public, which allows it to be ' - 'used in all projects (as opposed to the default, which ' - 'is to restrict its use to the current project.) This ' - 'option is deprecated and would be removed in R release.')) - shared_group.add_argument( - '--private', - action='store_true', - help=_( - 'Restrict use of the firewall policy to the current project.' - 'This option is deprecated and would be removed in R release.')) shared_group.add_argument( '--no-share', action='store_true', @@ -403,12 +390,6 @@ def get_parser(self, prog_name): action='store_true', help=_('Restrict use of the firewall policy to the ' 'current project')) - parser.add_argument( - '--public', - action='store_true', - help=_('Restrict use of the firewall policy to the ' - 'current project. This option is deprecated ' - 'and would be removed in R release.')) return parser def _get_attrs(self, client_manager, parsed_args): @@ -428,7 +409,7 @@ def _get_attrs(self, client_manager, parsed_args): attrs[const.FWRS] = [] if parsed_args.audited: attrs['audited'] = False - if parsed_args.share or parsed_args.public: + if parsed_args.share: attrs['shared'] = False return attrs diff --git a/neutronclient/osc/v2/fwaas/firewallrule.py b/neutronclient/osc/v2/fwaas/firewallrule.py index f72beded7..720c4d29a 100644 --- a/neutronclient/osc/v2/fwaas/firewallrule.py +++ b/neutronclient/osc/v2/fwaas/firewallrule.py @@ -117,19 +117,6 @@ def _get_common_parser(parser): action='store_true', help=_('Detach destination port number or range')) shared_group = parser.add_mutually_exclusive_group() - shared_group.add_argument( - '--public', - action='store_true', - help=_('Make the firewall policy public, which allows it to be ' - 'used in all projects (as opposed to the default, ' - 'which is to restrict its use to the current project). ' - 'This option is deprecated and would be removed in R Release')) - shared_group.add_argument( - '--private', - action='store_true', - help=_( - 'Restrict use of the firewall rule to the current project.' - 'This option is deprecated and would be removed in R release.')) shared_group.add_argument( '--share', action='store_true', @@ -212,9 +199,9 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): attrs['enabled'] = True if parsed_args.disable_rule: attrs['enabled'] = False - if parsed_args.share or parsed_args.public: + if parsed_args.share: attrs['shared'] = True - if parsed_args.no_share or parsed_args.private: + if parsed_args.no_share: attrs['shared'] = False if parsed_args.source_firewall_group: attrs['source_firewall_group_id'] = client.find_resource( @@ -416,12 +403,6 @@ def get_parser(self, prog_name): '--share', action='store_true', help=_('Restrict use of the firewall rule to the current project')) - parser.add_argument( - '--public', - action='store_true', - help=_('Restrict use of the firewall rule to the current project. ' - 'This option is deprecated and would be removed in ' - 'R Release.')) parser.add_argument( '--enable-rule', action='store_true', @@ -448,7 +429,7 @@ def _get_attrs(self, client_manager, parsed_args): attrs['destination_ip_address'] = None if parsed_args.destination_port: attrs['destination_port'] = None - if parsed_args.share or parsed_args.public: + if parsed_args.share: attrs['shared'] = False if parsed_args.enable_rule: attrs['enabled'] = False diff --git a/neutronclient/tests/unit/osc/v2/fwaas/common.py b/neutronclient/tests/unit/osc/v2/fwaas/common.py index 8acd51c42..8b497a9ff 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/common.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/common.py @@ -120,20 +120,6 @@ def test_set_shared(self): target, {self.res: {'shared': True}}) self.assertIsNone(result) - def test_set_public(self): - target = self.resource['id'] - arglist = [target, '--public'] - verifylist = [ - (self.res, target), - ('public', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with( - target, {self.res: {'shared': True}}) - self.assertIsNone(result) - def test_set_duplicate_shared(self): target = self.resource['id'] arglist = [target, '--share', '--share'] diff --git a/releasenotes/notes/remove-public-and-private-parameters-d683e7c30ecedc3b.yaml b/releasenotes/notes/remove-public-and-private-parameters-d683e7c30ecedc3b.yaml new file mode 100644 index 000000000..d340c1763 --- /dev/null +++ b/releasenotes/notes/remove-public-and-private-parameters-d683e7c30ecedc3b.yaml @@ -0,0 +1,6 @@ +--- +upgrade: + - | + The deprecated options ``--public`` and ``--private`` were + dropped in FWaaS v2 related commands. Use ``--share`` and + ``--no-share`` instead. From 40996e50ea5fbfbaca628cd81a34dd7db694578d Mon Sep 17 00:00:00 2001 From: kangyufei Date: Tue, 22 Oct 2019 14:22:17 +0800 Subject: [PATCH 731/845] Switch to Ussuri jobs Change-Id: I59807606d4865cb2f4d9c6a912f659ea45b3fba8 --- .zuul.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.zuul.yaml b/.zuul.yaml index 0210d6406..bc6861a3a 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -3,7 +3,7 @@ - openstack-cover-jobs - openstack-lower-constraints-jobs - openstack-python-jobs - - openstack-python3-train-jobs + - openstack-python3-ussuri-jobs - publish-openstack-docs-pti - check-requirements - lib-forward-testing From c430ed8950300b9908e470011f2d0f0170fcd369 Mon Sep 17 00:00:00 2001 From: Ghanshyam Mann Date: Sat, 23 Nov 2019 01:56:25 +0000 Subject: [PATCH 732/845] Move grenade neutron-lib job to py3 and in python-neutronclient repo 'legacy-grenade-dsvm-neutron-libs' grenade neutron-lib jobs is present in opensatck-zuul-jobs repo running on python 2. This needs to run on py3 from Ussuri onwards and py2 for stable/branch. python-neutronclient is the only repo which is using this job. py2 version has been kept in opensatck-zuul-jobs and for ussuri onwwards this has been migrated to python-neutronclient repo with py3 version. Change-Id: I165f86e1d05c8830e0b75b5c2db369178a3df7e7 --- .zuul.yaml | 28 +++++++++- .../grenade-dsvm-neutron-libs/post.yaml | 15 ++++++ .../legacy/grenade-dsvm-neutron-libs/run.yaml | 52 +++++++++++++++++++ 3 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 playbooks/legacy/grenade-dsvm-neutron-libs/post.yaml create mode 100644 playbooks/legacy/grenade-dsvm-neutron-libs/run.yaml diff --git a/.zuul.yaml b/.zuul.yaml index 0210d6406..3bdaf13a0 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -18,7 +18,7 @@ - neutronclient-functional experimental: jobs: - - legacy-grenade-dsvm-neutron-libs: + - neutron-lib-grenade-dsvm: irrelevant-files: - ^(test-|)requirements.txt$ - ^setup.cfg$ @@ -45,3 +45,29 @@ LIBS_FROM_GIT: python-neutronclient devstack_plugins: neutron-vpnaas: https://opendev.org/openstack/neutron-vpnaas + +- job: + name: neutron-lib-grenade-dsvm + # Old name: legacy-grenade-dsvm-neutron-libs + parent: legacy-dsvm-base + run: playbooks/legacy/grenade-dsvm-neutron-libs/run.yaml + post-run: playbooks/legacy/grenade-dsvm-neutron-libs/post.yaml + timeout: 10800 + required-projects: + - openstack/grenade + - openstack/devstack-gate + - openstack/keystoneauth + - openstack/neutron + - openstack/neutron-lib + - openstack/os-client-config + - openstack/python-cinderclient + - openstack/python-glanceclient + - openstack/python-ironicclient + - openstack/python-keystoneclient + - openstack/python-neutronclient + - openstack/python-novaclient + # This is py3 version for ussuri onwards rest all branch needs to be py2 + # version which is present in openstack-zuul-jobs. + # We need to take care of this branch variant and python version while + # migrating these jobs to zuulv3. + branches: ^(?!(stable/(ocata|pike|queens|rocky|stein|train))).*$ diff --git a/playbooks/legacy/grenade-dsvm-neutron-libs/post.yaml b/playbooks/legacy/grenade-dsvm-neutron-libs/post.yaml new file mode 100644 index 000000000..e07f5510a --- /dev/null +++ b/playbooks/legacy/grenade-dsvm-neutron-libs/post.yaml @@ -0,0 +1,15 @@ +- hosts: primary + tasks: + + - name: Copy files from {{ ansible_user_dir }}/workspace/ on node + synchronize: + src: '{{ ansible_user_dir }}/workspace/' + dest: '{{ zuul.executor.log_root }}' + mode: pull + copy_links: true + verify_host: true + rsync_opts: + - --include=/logs/** + - --include=*/ + - --exclude=* + - --prune-empty-dirs diff --git a/playbooks/legacy/grenade-dsvm-neutron-libs/run.yaml b/playbooks/legacy/grenade-dsvm-neutron-libs/run.yaml new file mode 100644 index 000000000..6c600610a --- /dev/null +++ b/playbooks/legacy/grenade-dsvm-neutron-libs/run.yaml @@ -0,0 +1,52 @@ +- hosts: all + name: Autoconverted job legacy-grenade-dsvm-neutron-libs from old job gate-grenade-dsvm-neutron-libs-ubuntu-xenial-nv + tasks: + + - name: Ensure legacy workspace directory + file: + path: '{{ ansible_user_dir }}/workspace' + state: directory + + - shell: + cmd: | + set -e + set -x + cat > clonemap.yaml << EOF + clonemap: + - name: openstack/devstack-gate + dest: devstack-gate + EOF + /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \ + https://opendev.org \ + openstack/devstack-gate + executable: /bin/bash + chdir: '{{ ansible_user_dir }}/workspace' + environment: '{{ zuul | zuul_legacy_vars }}' + + - shell: + cmd: | + set -e + set -x + export PROJECTS="openstack/grenade $PROJECTS" + export DEVSTACK_PROJECT_FROM_GIT="os-client-config" + export DEVSTACK_PROJECT_FROM_GIT+=",keystoneauth" + export DEVSTACK_PROJECT_FROM_GIT+=",python-novaclient" + export DEVSTACK_PROJECT_FROM_GIT+=",python-keystoneclient" + export DEVSTACK_PROJECT_FROM_GIT+=",python-glanceclient" + export DEVSTACK_PROJECT_FROM_GIT+=",python-cinderclient" + export DEVSTACK_PROJECT_FROM_GIT+=",python-neutronclient" + export DEVSTACK_PROJECT_FROM_GIT+=",python-ironicclient" + export PYTHONUNBUFFERED=true + export DEVSTACK_GATE_TEMPEST=1 + export DEVSTACK_GATE_GRENADE=pullup + export DEVSTACK_GATE_USE_PYTHON3=True + export DEVSTACK_GATE_NEUTRON=1 + export BRANCH_OVERRIDE=default + if [ "$BRANCH_OVERRIDE" != "default" ] ; then + export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE + fi + cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh + ./safe-devstack-vm-gate-wrap.sh + executable: /bin/bash + chdir: '{{ ansible_user_dir }}/workspace' + environment: '{{ zuul | zuul_legacy_vars }}' From d5c516a5a288003ca661091152c1a8ebfa527909 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Tue, 17 Dec 2019 14:43:05 +0900 Subject: [PATCH 733/845] Drop python3 hack for XML serializer It was added in https://review.opendev.org/#/c/110947/ to make the XML serializer work in python 3. We dropped the XML serializer support long ago. I believe we can drop it safely. Change-Id: I208de891515cb2cbdde779013ba58f19e36fc55c --- neutronclient/common/serializer.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/neutronclient/common/serializer.py b/neutronclient/common/serializer.py index fdf76774b..98865a6b3 100644 --- a/neutronclient/common/serializer.py +++ b/neutronclient/common/serializer.py @@ -20,10 +20,6 @@ from neutronclient.common import exceptions as exception -if six.PY3: - long = int - - class ActionDispatcher(object): """Maps method name to local methods through action name.""" From 010053df07d7dffad55581c0db9d43256a482280 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Wed, 13 Nov 2019 01:24:29 +0900 Subject: [PATCH 734/845] Drop python 2.7 support We no longer support python 2.7 past Train. Let's stop testings with python 2.7 and drop python 2.7 stuffs. pypy in tox.ini is also dropped too. It is not used for long. Change-Id: I8a07c007a129cd2141085a1a3cc7f81658c42db2 --- .zuul.yaml | 1 - .../notes/drop-python-2.7-f615ebae463b2143.yaml | 5 +++++ setup.cfg | 5 ----- tox.ini | 14 +++----------- 4 files changed, 8 insertions(+), 17 deletions(-) create mode 100644 releasenotes/notes/drop-python-2.7-f615ebae463b2143.yaml diff --git a/.zuul.yaml b/.zuul.yaml index 7d3ff433b..d93813cfc 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -2,7 +2,6 @@ templates: - openstack-cover-jobs - openstack-lower-constraints-jobs - - openstack-python-jobs - openstack-python3-ussuri-jobs - publish-openstack-docs-pti - check-requirements diff --git a/releasenotes/notes/drop-python-2.7-f615ebae463b2143.yaml b/releasenotes/notes/drop-python-2.7-f615ebae463b2143.yaml new file mode 100644 index 000000000..6fb4d22ab --- /dev/null +++ b/releasenotes/notes/drop-python-2.7-f615ebae463b2143.yaml @@ -0,0 +1,5 @@ +--- +upgrade: + - | + Python 2.7 support has been dropped. The minimum version of Python now + supported by python-neutronclient is Python 3.6. diff --git a/setup.cfg b/setup.cfg index 0045e57ec..16cfc5aef 100644 --- a/setup.cfg +++ b/setup.cfg @@ -14,8 +14,6 @@ classifier = License :: OSI Approved :: Apache Software License Operating System :: POSIX :: Linux Programming Language :: Python - Programming Language :: Python :: 2 - Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 @@ -457,9 +455,6 @@ neutron.cli.v2 = vpn-ikepolicy-update = neutronclient.neutron.v2_0.vpn.ikepolicy:UpdateIKEPolicy vpn-ikepolicy-delete = neutronclient.neutron.v2_0.vpn.ikepolicy:DeleteIKEPolicy -[wheel] -universal = 1 - [extract_messages] keywords = _ gettext ngettext l_ lazy_gettext mapping_file = babel.cfg diff --git a/tox.ini b/tox.ini index f9458eaa3..fa1f9e209 100644 --- a/tox.ini +++ b/tox.ini @@ -1,10 +1,11 @@ [tox] -# py3 first to avoid .testrepository incompatibility -envlist = py37,py27,pypy,pep8 +envlist = py37,pep8 minversion = 2.3.2 skipsdist = True +ignore_basepython_conflict = True [testenv] +basepython = python3 setenv = VIRTUAL_ENV={envdir} LANG=en_US.UTF-8 LANGUAGE=en_US:en @@ -25,24 +26,20 @@ commands = sh -c "find . -type d -name '.?*' -prune -o \ whitelist_externals = sh [testenv:pep8] -basepython = python3 commands = flake8 {[testenv:bandit]commands} distribute = false [testenv:venv] -basepython = python3 commands = {posargs} [testenv:functional] -basepython = python3 setenv = OS_TEST_PATH = ./neutronclient/tests/functional OS_NEUTRONCLIENT_EXEC_DIR = {envdir}/bin [testenv:cover] -basepython = python3 setenv = {[testenv]setenv} PYTHON=coverage run --source neutronclient --parallel-mode @@ -54,12 +51,10 @@ commands = coverage report [testenv:docs] -basepython = python3 deps = -r{toxinidir}/doc/requirements.txt commands = sphinx-build -W -b html doc/source doc/build/html [testenv:pdf-docs] -basepython = python3 envdir = {toxworkdir}/docs deps = {[testenv:docs]deps} whitelist_externals = @@ -69,7 +64,6 @@ commands = make -C doc/build/pdf [testenv:releasenotes] -basepython = python3 deps = -r{toxinidir}/doc/requirements.txt commands = sphinx-build -a -E -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html @@ -82,13 +76,11 @@ import-order-style = pep8 enable-extensions=H904 [testenv:bandit] -basepython = python3 # B303: blacklist calls: md5, sha1 deps = -r{toxinidir}/test-requirements.txt commands = bandit -r neutronclient -x tests -n5 -s B303 [testenv:lower-constraints] -basepython = python3 deps = -c{toxinidir}/lower-constraints.txt -r{toxinidir}/test-requirements.txt From 91fb009706526ed8d36ca8f2cf57f1763a9af520 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Tue, 17 Dec 2019 16:39:14 +0900 Subject: [PATCH 735/845] Fix pep8 errors with hacking 2.0.0 Change-Id: I4737d4bc4fa116f45e2361eba93f48feae0161a4 --- neutronclient/tests/unit/osc/v2/logging/test_network_log.py | 4 ++-- neutronclient/tests/unit/qos/test_cli20_rule.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/neutronclient/tests/unit/osc/v2/logging/test_network_log.py b/neutronclient/tests/unit/osc/v2/logging/test_network_log.py index 43c0bcbfe..d36453542 100644 --- a/neutronclient/tests/unit/osc/v2/logging/test_network_log.py +++ b/neutronclient/tests/unit/osc/v2/logging/test_network_log.py @@ -139,8 +139,8 @@ def setUp(self): self.mocked = self.neutronclient.create_network_log self.cmd = network_log.CreateNetworkLog(self.app, self.namespace) loggables = { - "loggable_resources": [{"type": RES_TYPE_SG, - "type": RES_TYPE_FWG}] + "loggable_resources": [{"type": RES_TYPE_SG}, + {"type": RES_TYPE_FWG}] } self.neutronclient.list_network_loggable_resources = mock.Mock( return_value=loggables) diff --git a/neutronclient/tests/unit/qos/test_cli20_rule.py b/neutronclient/tests/unit/qos/test_cli20_rule.py index c1da5afef..f77c3c410 100644 --- a/neutronclient/tests/unit/qos/test_cli20_rule.py +++ b/neutronclient/tests/unit/qos/test_cli20_rule.py @@ -33,9 +33,9 @@ def test_list_qos_rule_types(self): # qos_rule_types. resources = 'rule_types' cmd_resources = 'qos_rule_types' - response_contents = [{'type': 'bandwidth_limit', - 'type': 'dscp_marking', - 'type': 'minimum_bandwidth'}] + response_contents = [{'type': 'bandwidth_limit'}, + {'type': 'dscp_marking'}, + {'type': 'minimum_bandwidth'}] cmd = qos_rule.ListQoSRuleTypes(test_cli20.MyApp(sys.stdout), None) From 29043825e7e19b4f32f3998ea95419ad2429cd14 Mon Sep 17 00:00:00 2001 From: liushuobj Date: Fri, 27 Dec 2019 16:35:42 +0800 Subject: [PATCH 736/845] fix a typo Change-Id: I9d82335c37f114aa43f26dec106e7598e776e068 --- releasenotes/notes/relnotes-from-3.0.0-d7306f5af5e3868d.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/notes/relnotes-from-3.0.0-d7306f5af5e3868d.yaml b/releasenotes/notes/relnotes-from-3.0.0-d7306f5af5e3868d.yaml index 012bc7fc2..2bf4d5ab0 100644 --- a/releasenotes/notes/relnotes-from-3.0.0-d7306f5af5e3868d.yaml +++ b/releasenotes/notes/relnotes-from-3.0.0-d7306f5af5e3868d.yaml @@ -2,7 +2,7 @@ features: - Support os-client-config. OS_CLOUD environment variable is used for selecting named cloud configuration. - - Support keystoneauth1 library which brings us better kyestone v3 support. + - Support keystoneauth1 library which brings us better keystone v3 support. - Client command extension now supports a child resource. - New CLI for VPNaaS multiple local subnets. - New CLI for VPNaaS endpoint group API. From a363edd761e2c99bae6d4492d0ca44a404e5d904 Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez Date: Fri, 10 Jan 2020 18:54:28 +0000 Subject: [PATCH 737/845] Avoid py36 error when printing unicode chars in a stream The IOStream was not able to encode characters out of range 128: "UnicodeEncodeError: 'ascii' codec can't encode characters in position 19-21: ordinal not in range(128)" Change-Id: Ic95396a5cf73c49d332928857dc064819a6d7ea6 Closes-Bug: #1858421 --- neutronclient/tests/unit/test_exceptions.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/neutronclient/tests/unit/test_exceptions.py b/neutronclient/tests/unit/test_exceptions.py index 91742872c..aefc24c66 100644 --- a/neutronclient/tests/unit/test_exceptions.py +++ b/neutronclient/tests/unit/test_exceptions.py @@ -12,7 +12,9 @@ # License for the specific language governing permissions and limitations # under the License. -import fixtures +import sys + +import mock from oslo_utils import encodeutils import six import testtools @@ -30,12 +32,11 @@ class TestException(exceptions.NeutronException): multibyte_unicode_string = u'\uff21\uff22\uff23' e = TestException(reason=multibyte_unicode_string) - fixture = fixtures.StringStream('stdout') - self.useFixture(fixture) - with fixtures.MonkeyPatch('sys.stdout', fixture.stream): + with mock.patch.object(sys, 'stdout') as mock_stdout: print(e) - self.assertEqual('Exception with %s' % multibyte_unicode_string, - fixture.getDetails().get('stdout').as_text()) + + exc_str = 'Exception with %s' % multibyte_unicode_string + mock_stdout.assert_has_calls([mock.call.write(exc_str)]) def test_exception_message_with_encoded_unicode(self): class TestException(exceptions.NeutronException): From 946ac3ed2e9e177eb5c56cc74aadbc091b9292ab Mon Sep 17 00:00:00 2001 From: Jay Faulkner Date: Tue, 7 Jan 2020 11:50:43 -0800 Subject: [PATCH 738/845] Convert exception to string before passing it in Before this change, neutronclient was passing in a raw exception as a kwarg to the ConnectionFailed exception. This caused an exception to be raised in _safe_decode_dict() due to the exception not being a text type. Now, we explicitly convert the raw exception to a string before passing it as a kwarg. Closes-bug: 1859068 Change-Id: I323b3aceec0a937874eabf770fbc82995202f6d6 --- neutronclient/client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neutronclient/client.py b/neutronclient/client.py index c5f467668..936d297e2 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -104,13 +104,13 @@ def _cs_request(self, *args, **kwargs): try: resp, body = self.request(*args, **kargs) except requests.exceptions.SSLError as e: - raise exceptions.SslCertificateValidationError(reason=e) + raise exceptions.SslCertificateValidationError(reason=str(e)) except Exception as e: # Wrap the low-level connection error (socket timeout, redirect # limit, decompression error, etc) into our custom high-level # connection exception (it is excepted in the upper layers of code) _logger.debug("throwing ConnectionFailed : %s", e) - raise exceptions.ConnectionFailed(reason=e) + raise exceptions.ConnectionFailed(reason=str(e)) utils.http_log_resp(_logger, resp, body) # log request-id for each api call From 59145be07e5df52a7cc0fb84e8da10fe4de6e770 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 6 Feb 2020 10:32:33 +0000 Subject: [PATCH 739/845] Add support for port bindings The port bindings API has been around since Pike but has never been exposed via neutronclient. We still use this in nova so it would be nice to avoid having to create a KSA client manually for this stuff. We don't need to expose things via the UI, just the client itself, so do that. Change-Id: Ied1a057186f0819166df84725b09ded314930a1d Signed-off-by: Stephen Finucane Sem-Ver: feature --- neutronclient/v2_0/client.py | 25 +++++++++++++++++++ .../notes/port-bindings-c3f36bd76ece0a71.yaml | 5 ++++ 2 files changed, 30 insertions(+) create mode 100644 releasenotes/notes/port-bindings-c3f36bd76ece0a71.yaml diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index c338c316e..5fa869840 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -492,6 +492,9 @@ class Client(ClientBase): network_path = "/networks/%s" ports_path = "/ports" port_path = "/ports/%s" + port_bindings_path = "/ports/%s/bindings" + port_binding_path = "/ports/%s/bindings/%s" + port_binding_path_activate = "/ports/%s/bindings/%s/activate" subnets_path = "/subnets" subnet_path = "/subnets/%s" onboard_network_subnets_path = "/subnetpools/%s/onboard_network_subnets" @@ -811,6 +814,28 @@ def delete_port(self, port): """Deletes the specified port.""" return self.delete(self.port_path % (port)) + def create_port_binding(self, port_id, body=None): + """Creates a new port binding.""" + return self.post(self.port_bindings_path % port_id, body=body) + + def delete_port_binding(self, port_id, host_id): + """Deletes the specified port binding.""" + return self.delete(self.port_binding_path % (port_id, host_id)) + + def show_port_binding(self, port_id, host_id, **_params): + """Fetches information for a certain port binding.""" + return self.get(self.port_binding_path % (port_id, host_id), + params=_params) + + def list_port_bindings(self, port_id, retrieve_all=True, **_params): + """Fetches a list of all bindings for a certain port.""" + return self.list('port_bindings', self.port_bindings_path % port_id, + retrieve_all, **_params) + + def activate_port_binding(self, port_id, host_id): + """Activates a port binding.""" + return self.put(self.port_binding_path_activate % (port_id, host_id)) + def list_networks(self, retrieve_all=True, **_params): """Fetches a list of all networks for a project.""" # Pass filters in "params" argument to do_request diff --git a/releasenotes/notes/port-bindings-c3f36bd76ece0a71.yaml b/releasenotes/notes/port-bindings-c3f36bd76ece0a71.yaml new file mode 100644 index 000000000..7d2ef725e --- /dev/null +++ b/releasenotes/notes/port-bindings-c3f36bd76ece0a71.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + New client methods: ``create_port_binding``, ``delete_port_binding``, + ``show_port_binding``, ``list_port_bindings`` and ``activate_port_binding``. From b3fa5e530b71c984d734eeb24a1e26126a486016 Mon Sep 17 00:00:00 2001 From: zhanghao Date: Sat, 25 Jan 2020 04:16:16 -0500 Subject: [PATCH 740/845] Drop lib-forward-testing Change-Id: Ifdce9146e8233e6760ee6c0009338661f90848a2 --- .zuul.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.zuul.yaml b/.zuul.yaml index d93813cfc..b6685df2e 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -5,7 +5,6 @@ - openstack-python3-ussuri-jobs - publish-openstack-docs-pti - check-requirements - - lib-forward-testing - lib-forward-testing-python3 - release-notes-jobs-python3 - openstackclient-plugin-jobs From bb5b9cac6e0fd65bd620d514d29751800575d5cc Mon Sep 17 00:00:00 2001 From: zhanghao Date: Mon, 6 Jan 2020 05:33:53 -0500 Subject: [PATCH 741/845] Replace 'bgp speaker show dragents' with 'bgp dragent list' In order to follow other agent related commands in OSC, it is more appropriate and friendly to replace 'bgp speaker show dragents' with 'bgp dragent list'. this patch uses 'openstack bgp dragent list' to show the list of dragents, and the optional parameter 'bgp-speaker' shows the list of dragents hosting a specific bgp speaker. Change-Id: I9e0703fccf535b1e1a2055ed917336055b7395f5 Closes-Bug: #1858377 --- .../osc/v2/dynamic_routing/bgp_dragent.py | 47 ++++++- .../unit/osc/v2/dynamic_routing/fakes.py | 38 +++++- .../v2/dynamic_routing/test_bgp_dragent.py | 123 ++++++++++++++++++ ...peaker-show-dragents-2fcce99cf6bb5b60.yaml | 10 ++ setup.cfg | 1 + 5 files changed, 215 insertions(+), 4 deletions(-) create mode 100644 neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_dragent.py create mode 100644 releasenotes/notes/deprecate-bgp-speaker-show-dragents-2fcce99cf6bb5b60.yaml diff --git a/neutronclient/osc/v2/dynamic_routing/bgp_dragent.py b/neutronclient/osc/v2/dynamic_routing/bgp_dragent.py index f7f1e836b..025022996 100644 --- a/neutronclient/osc/v2/dynamic_routing/bgp_dragent.py +++ b/neutronclient/osc/v2/dynamic_routing/bgp_dragent.py @@ -70,18 +70,25 @@ def take_action(self, parsed_args): class ListDRAgentsHostingBgpSpeaker(command.Lister): - """List dynamic routing agents hosting a BGP speaker""" + """(Deprecated) List dynamic routing agents hosting a BGP speaker + + (Use "bgp dragent list" instead) + """ resource = 'agent' list_columns = ['id', 'host', 'admin_state_up', 'alive'] unknown_parts_flag = False def get_parser(self, prog_name): + self.log.warning("The 'openstack bgp speaker show dragents' CLI is " + "deprecated and will be removed in the future. Use " + "'openstack bgp dragent list' CLI instead.") parser = super(ListDRAgentsHostingBgpSpeaker, self).get_parser(prog_name) parser.add_argument('bgp_speaker', metavar='', - help=_("ID or name of the BGP speaker")) + help=_("List dynamic routing agents hosting a " + "BGP speaker (name or ID)")) return parser def take_action(self, parsed_args): @@ -97,3 +104,39 @@ def take_action(self, parsed_args): (utils.get_dict_properties( s, columns, formatters=_formatters, ) for s in data['agents'])) + + +class ListDRAgent(command.Lister): + """List dynamic routing agents""" + + resource = 'agent' + list_columns = ['id', 'host', 'admin_state_up', 'alive'] + unknown_parts_flag = False + + def get_parser(self, prog_name): + parser = super(ListDRAgent, + self).get_parser(prog_name) + parser.add_argument('--bgp-speaker', + metavar='', + help=_("List dynamic routing agents hosting a " + "BGP speaker (name or ID)")) + return parser + + def take_action(self, parsed_args): + search_opts = {} + client = self.app.client_manager.neutronclient + if parsed_args.bgp_speaker is not None: + search_opts = {} + speaker_id = client.find_resource(constants.BGP_SPEAKER, + parsed_args.bgp_speaker)['id'] + search_opts['bgp_speaker'] = speaker_id + data = client.list_dragents_hosting_bgp_speaker(**search_opts) + else: + attrs = {'agent_type': 'BGP dynamic routing agent'} + data = client.list_agents(**attrs) + headers = ('ID', 'Host', 'State', 'Alive') + columns = ('id', 'host', 'admin_state_up', 'alive') + return (headers, + (utils.get_dict_properties( + s, columns, formatters=_formatters, + ) for s in data['agents'])) diff --git a/neutronclient/tests/unit/osc/v2/dynamic_routing/fakes.py b/neutronclient/tests/unit/osc/v2/dynamic_routing/fakes.py index 42acbeefd..d425cd67b 100644 --- a/neutronclient/tests/unit/osc/v2/dynamic_routing/fakes.py +++ b/neutronclient/tests/unit/osc/v2/dynamic_routing/fakes.py @@ -57,7 +57,7 @@ def create_bgp_speakers(attrs=None, count=1): """ bgp_speakers = [] - for i in range(0, count): + for i in range(count): bgp_speaker = FakeBgpSpeaker.create_one_bgp_speaker(attrs) bgp_speakers.append(bgp_speaker) @@ -89,8 +89,42 @@ def create_one_bgp_peer(attrs=None): def create_bgp_peers(attrs=None, count=1): """Create one or multiple fake bgp peers.""" bgp_peers = [] - for i in range(0, count): + for i in range(count): bgp_peer = FakeBgpPeer.create_one_bgp_peer(attrs) bgp_peers.append(bgp_peer) return {'bgp_peers': bgp_peers} + + +class FakeDRAgent(object): + """Fake one or more dynamic routing agents.""" + + @staticmethod + def create_one_dragent(attrs=None): + attrs = attrs or {} + # Set default attributes. + dragent_attrs = { + 'binary': 'neutron-bgp-dragent', + 'admin_state_up': True, + 'alive': True, + 'topic': 'bgp_dragent', + 'host': 'network-' + uuid.uuid4().hex, + 'name': 'bgp-dragent-' + uuid.uuid4().hex, + 'agent_type': 'BGP dynamic routing agent', + 'id': uuid.uuid4().hex, + } + + # Overwrite default attributes. + dragent_attrs.update(attrs) + + return copy.deepcopy(dragent_attrs) + + @staticmethod + def create_dragents(attrs=None, count=1): + """Create one or multiple fake dynamic routing agents.""" + agents = [] + for i in range(count): + agent = FakeDRAgent.create_one_dragent(attrs) + agents.append(agent) + + return {'agents': agents} diff --git a/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_dragent.py b/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_dragent.py new file mode 100644 index 000000000..1ec7c41f0 --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_dragent.py @@ -0,0 +1,123 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +import mock + +from neutronclient.osc.v2.dynamic_routing import bgp_dragent +from neutronclient.tests.unit.osc.v2.dynamic_routing import fakes + + +class TestAddBgpSpeakerToDRAgent(fakes.TestNeutronDynamicRoutingOSCV2): + _bgp_speaker = fakes.FakeBgpSpeaker.create_one_bgp_speaker() + _bgp_dragent = fakes.FakeDRAgent.create_one_dragent() + _bgp_speaker_id = _bgp_speaker['id'] + _bgp_dragent_id = _bgp_dragent['id'] + + def setUp(self): + super(TestAddBgpSpeakerToDRAgent, self).setUp() + + # Get the command object to test + self.cmd = bgp_dragent.AddBgpSpeakerToDRAgent(self.app, self.namespace) + + def test_add_bgp_speaker_to_dragent(self): + arglist = [ + self._bgp_dragent_id, + self._bgp_speaker_id, + ] + verifylist = [ + ('dragent_id', self._bgp_dragent_id), + ('bgp_speaker', self._bgp_speaker_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + with mock.patch.object(self.neutronclient, + "add_bgp_speaker_to_dragent", + return_value=None): + + result = self.cmd.take_action(parsed_args) + self.neutronclient.add_bgp_speaker_to_dragent.\ + assert_called_once_with( + self._bgp_dragent_id, + {'bgp_speaker_id': self._bgp_speaker_id}) + self.assertIsNone(result) + + +class TestRemoveBgpSpeakerFromDRAgent(fakes.TestNeutronDynamicRoutingOSCV2): + _bgp_speaker = fakes.FakeBgpSpeaker.create_one_bgp_speaker() + _bgp_dragent = fakes.FakeDRAgent.create_one_dragent() + _bgp_speaker_id = _bgp_speaker['id'] + _bgp_dragent_id = _bgp_dragent['id'] + + def setUp(self): + super(TestRemoveBgpSpeakerFromDRAgent, self).setUp() + + # Get the command object to test + self.cmd = bgp_dragent.RemoveBgpSpeakerFromDRAgent( + self.app, self.namespace) + + def test_remove_bgp_speaker_from_dragent(self): + arglist = [ + self._bgp_dragent_id, + self._bgp_speaker_id, + ] + verifylist = [ + ('dragent_id', self._bgp_dragent_id), + ('bgp_speaker', self._bgp_speaker_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + with mock.patch.object(self.neutronclient, + "remove_bgp_speaker_from_dragent", + return_value=None): + result = self.cmd.take_action(parsed_args) + self.neutronclient.remove_bgp_speaker_from_dragent.\ + assert_called_once_with(self._bgp_dragent_id, + self._bgp_speaker_id) + self.assertIsNone(result) + + +class TestListDRAgentsHostingBgpSpeaker(fakes.TestNeutronDynamicRoutingOSCV2): + _bgp_speaker = fakes.FakeBgpSpeaker.create_one_bgp_speaker() + _bgp_speaker_id = _bgp_speaker['id'] + attrs = {'bgp_speaker_id': _bgp_speaker_id} + _bgp_dragents = fakes.FakeDRAgent.create_dragents(attrs) + columns = ('ID', 'Host', 'State', 'Alive') + data = [(_bgp_dragent['id'], + _bgp_dragent['host'], + _bgp_dragent['admin_state_up'], + ':-)' if _bgp_dragent['alive'] else 'XXX') + for _bgp_dragent in _bgp_dragents['agents']] + + def setUp(self): + super(TestListDRAgentsHostingBgpSpeaker, self).setUp() + + # Get the command object to test + self.cmd = bgp_dragent.ListDRAgent(self.app, self.namespace) + + def test_list_dragents_hosting_bgp_speaker(self): + arglist = [ + '--bgp-speaker', self._bgp_speaker_id, + ] + verifylist = [ + ('bgp_speaker', self._bgp_speaker_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + with mock.patch.object(self.neutronclient, + "list_dragents_hosting_bgp_speaker", + return_value=self._bgp_dragents): + columns, data = self.cmd.take_action(parsed_args) + attrs = {'bgp_speaker': self._bgp_speaker_id} + self.neutronclient.list_dragents_hosting_bgp_speaker.\ + assert_called_once_with(**attrs) + self.assertEqual(self.columns, columns) + self.assertListEqual(self.data, list(data)) diff --git a/releasenotes/notes/deprecate-bgp-speaker-show-dragents-2fcce99cf6bb5b60.yaml b/releasenotes/notes/deprecate-bgp-speaker-show-dragents-2fcce99cf6bb5b60.yaml new file mode 100644 index 000000000..6233c6f4f --- /dev/null +++ b/releasenotes/notes/deprecate-bgp-speaker-show-dragents-2fcce99cf6bb5b60.yaml @@ -0,0 +1,10 @@ +--- +deprecations: + - | + The ``openstack bgp speaker show dragents`` CLI is deprecated and + will be removed in the future. Use ``openstack bgp dragent list + --bgp-speaker `` CLI instead. +features: + - | + The ``openstack bgp dragent list`` CLI is added to support showing + the list of dynamic routing agents. diff --git a/setup.cfg b/setup.cfg index 16cfc5aef..d1fb096c2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -70,6 +70,7 @@ openstack.neutronclient.v2 = sfc_service_graph_show = neutronclient.osc.v2.sfc.sfc_service_graph:ShowSfcServiceGraph bgp_dragent_add_speaker = neutronclient.osc.v2.dynamic_routing.bgp_dragent:AddBgpSpeakerToDRAgent + bgp_dragent_list = neutronclient.osc.v2.dynamic_routing.bgp_dragent:ListDRAgent bgp_dragent_remove_speaker = neutronclient.osc.v2.dynamic_routing.bgp_dragent:RemoveBgpSpeakerFromDRAgent bgp_peer_create = neutronclient.osc.v2.dynamic_routing.bgp_peer:CreateBgpPeer bgp_peer_delete = neutronclient.osc.v2.dynamic_routing.bgp_peer:DeleteBgpPeer From ac04e5d415c03f8594e1d3344c51bfe6c9d3a49d Mon Sep 17 00:00:00 2001 From: Andreas Jaeger Date: Sat, 4 Apr 2020 17:05:21 +0200 Subject: [PATCH 742/845] Cleanup py27 support Make a few cleanups: - Remove python 2.7 stanza from setup.py - Add requires on python >= 3.6 to setup.cfg so that pypi and pip know about the requirement - Remove obsolete sections from setup.cfg - Update classifiers - Update requirements, no need for python_version anymore - Remove html_last_updated_fmt from conf.py, this is not needed with openstackdocstheme anymore; update openstackdocstheme requirement Change-Id: I66d7b502349de38bc2b646ed99cf5a41471d81bf --- doc/requirements.txt | 5 ++--- doc/source/conf.py | 1 - lower-constraints.txt | 2 +- releasenotes/source/conf.py | 1 - setup.cfg | 7 +++---- setup.py | 9 --------- 6 files changed, 6 insertions(+), 19 deletions(-) diff --git a/doc/requirements.txt b/doc/requirements.txt index af1aeb8b3..8dc65ed51 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -1,7 +1,6 @@ # The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. -openstackdocstheme>=1.18.1 # Apache-2.0 +openstackdocstheme>=1.32.1 # Apache-2.0 reno>=2.5.0 # Apache-2.0 -sphinx!=1.6.6,!=1.6.7,>=1.6.2,<2.0.0;python_version=='2.7' # BSD -sphinx!=1.6.6,!=1.6.7,>=1.6.2;python_version>='3.4' # BSD +sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD diff --git a/doc/source/conf.py b/doc/source/conf.py index 294de7129..4b4300592 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -16,7 +16,6 @@ repository_name = 'openstack/python-neutronclient' bug_project = 'python-neutronclient' bug_tag = 'doc' -html_last_updated_fmt = '%Y-%m-%d %H:%M' # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] diff --git a/lower-constraints.txt b/lower-constraints.txt index b37c137a0..3a51d5545 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -46,7 +46,7 @@ msgpack-python==0.4.0 munch==2.1.0 netaddr==0.7.18 netifaces==0.10.4 -openstackdocstheme==1.18.1 +openstackdocstheme==1.32.1 openstacksdk==0.11.2 os-client-config==1.28.0 os-service-types==1.2.0 diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py index a029757ed..f7ec3924b 100644 --- a/releasenotes/source/conf.py +++ b/releasenotes/source/conf.py @@ -46,7 +46,6 @@ repository_name = 'openstack/python-neutronclient' bug_project = 'python-neutronclient' bug_tag = 'doc' -html_last_updated_fmt = '%Y-%m-%d %H:%M' # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] diff --git a/setup.cfg b/setup.cfg index d1fb096c2..f9a0f431a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -6,6 +6,7 @@ description-file = author = OpenStack Networking Project author-email = openstack-discuss@lists.openstack.org home-page = https://docs.openstack.org/python-neutronclient/latest/ +python-requires = >=3.6 classifier = Environment :: OpenStack Intended Audience :: Developers @@ -14,6 +15,8 @@ classifier = License :: OSI Approved :: Apache Software License Operating System :: POSIX :: Linux Programming Language :: Python + Programming Language :: Python :: Implementation :: CPython + Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 @@ -22,10 +25,6 @@ classifier = packages = neutronclient -[global] -setup-hooks = - pbr.hooks.setup_hook - [entry_points] console_scripts = neutron = neutronclient.shell:main diff --git a/setup.py b/setup.py index 566d84432..cd35c3c35 100644 --- a/setup.py +++ b/setup.py @@ -13,17 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT import setuptools -# In python < 2.7.4, a lazy loading of package `pbr` will break -# setuptools if some other modules registered functions in `atexit`. -# solution from: http://bugs.python.org/issue15881#msg170215 -try: - import multiprocessing # noqa -except ImportError: - pass - setuptools.setup( setup_requires=['pbr>=2.0.0'], pbr=True) From 91a9dcd94b3565179199edaad9eb367a93010b1e Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Sat, 11 Apr 2020 18:47:39 +0000 Subject: [PATCH 743/845] Update master for stable/ussuri Add file to the reno documentation build to show release notes for stable/ussuri. Use pbr instruction to increment the minor version number automatically so that master versions are higher than the versions on stable/ussuri. Change-Id: Id46b2b493579dcf984e537d81392c792c9d9aeea Sem-Ver: feature --- releasenotes/source/index.rst | 1 + releasenotes/source/ussuri.rst | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 releasenotes/source/ussuri.rst diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index f5df8b776..ac3d918a3 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -6,6 +6,7 @@ :maxdepth: 1 unreleased + ussuri train stein rocky diff --git a/releasenotes/source/ussuri.rst b/releasenotes/source/ussuri.rst new file mode 100644 index 000000000..e21e50e0c --- /dev/null +++ b/releasenotes/source/ussuri.rst @@ -0,0 +1,6 @@ +=========================== +Ussuri Series Release Notes +=========================== + +.. release-notes:: + :branch: stable/ussuri From ec625d0e93aef69881aaadc2a4b2cea0f5e1dad7 Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Sat, 11 Apr 2020 18:47:45 +0000 Subject: [PATCH 744/845] Add Python3 victoria unit tests This is an automatically generated patch to ensure unit testing is in place for all the of the tested runtimes for victoria. See also the PTI in governance [1]. [1]: https://governance.openstack.org/tc/reference/project-testing-interface.html Change-Id: I817e39d2229ffc803fd285d4bedfcad86a450c05 --- .zuul.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.zuul.yaml b/.zuul.yaml index b6685df2e..e956eba72 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -2,7 +2,7 @@ templates: - openstack-cover-jobs - openstack-lower-constraints-jobs - - openstack-python3-ussuri-jobs + - openstack-python3-victoria-jobs - publish-openstack-docs-pti - check-requirements - lib-forward-testing-python3 From 157016ea3c3f6e9804af60dd748919cd3d942092 Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Fri, 17 Apr 2020 15:46:04 +0200 Subject: [PATCH 745/845] Remove Babel from requirements It's not a runtime dependency (and even oslo.i18n has dropped it). The translation infrastructure installs Babel explicitly. See this mailing list thread for a full reasoning: http://lists.openstack.org/pipermail/openstack-discuss/2020-April/014227.html Keeping Babel in lower-constraints since other projects still pull it. Change-Id: I1c92bd28c3f54368b591b965b5f904c8fda424e0 --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index ec2d7af87..5204dc21e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,4 +19,3 @@ python-keystoneclient>=3.8.0 # Apache-2.0 requests>=2.14.2 # Apache-2.0 simplejson>=3.5.1 # MIT six>=1.10.0 # MIT -Babel!=2.4.0,>=2.3.4 # BSD From 19bea622eae7382239f394588a67ab6e4715f7b0 Mon Sep 17 00:00:00 2001 From: Sean McGinnis Date: Fri, 24 Apr 2020 08:23:17 -0500 Subject: [PATCH 746/845] Add py38 package metadata Now that we are running the Victoria tests that include a voting py38, we can now add the Python 3.8 metadata to the package information to reflect that support. Change-Id: I449ba49cd8c2c8c9890b603c298295c56a5df6de Signed-off-by: Sean McGinnis --- setup.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.cfg b/setup.cfg index f9a0f431a..70c0d62b9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -20,6 +20,7 @@ classifier = Programming Language :: Python :: 3 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 [files] packages = From fdfa615b8edcf1bd2a3c27a4644d88e75e97e3dc Mon Sep 17 00:00:00 2001 From: Sean McGinnis Date: Fri, 24 Apr 2020 10:25:56 -0500 Subject: [PATCH 747/845] Bump default tox env from py37 to py38 Python 3.8 is now our highest level supported python runtime. This updates the default tox target environments to swap out py37 for py38 to make sure local development testing is covering this version. This does not impact zuul jobs in any way, nor prevent local tests against py37. It just changes the default if none is explicitly provided. Change-Id: I026ae0fae10114d9aed8bdb0ae7061b3dc2ff895 Signed-off-by: Sean McGinnis --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index fa1f9e209..c4de1cd1e 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py37,pep8 +envlist = py38,pep8 minversion = 2.3.2 skipsdist = True ignore_basepython_conflict = True From 6d9d2e62915edcf6a5796984012ce17e1d87f1c5 Mon Sep 17 00:00:00 2001 From: Ghanshyam Mann Date: Tue, 12 May 2020 19:47:38 -0500 Subject: [PATCH 748/845] Fix hacking min version to 3.0.1 flake8 new release 3.8.0 added new checks and gate pep8 job start failing. hacking 3.0.1 fix the pinning of flake8 to avoid bringing in a new version with new checks. Though it is fixed in latest hacking but 2.0 and 3.0 has cap for flake8 as <4.0.0 which mean flake8 new version 3.9.0 can also break the pep8 job if new check are added. To avoid similar gate break in future, we need to bump the hacking min version. - http://lists.openstack.org/pipermail/openstack-discuss/2020-May/014828.html Change-Id: I5486638526b26ce0710c7b1926e408dd70057a66 --- test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index a1eb9b1d1..a0d76ed70 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,7 +1,7 @@ # The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. -hacking>=1.1.0 # Apache-2.0 +hacking>=3.0.1,<3.1.0 # Apache-2.0 bandit!=1.6.0,>=1.1.0 # Apache-2.0 coverage!=4.4,>=4.0 # Apache-2.0 From c8d61703c2e74c559b601cf23d1aa59366b32874 Mon Sep 17 00:00:00 2001 From: zhanghao Date: Wed, 13 May 2020 05:04:55 -0400 Subject: [PATCH 749/845] Add aggressive negotiation mode for ikepolicy The phase1 negotiation mode adds support for aggressive mode, which can be selected when creating an ikepolicy. Change-Id: I8148011558094db07b5f9eba70b16cfe6eeaf98f Partial-Bug: #1701413 --- neutronclient/osc/v2/vpnaas/ikepolicy.py | 2 +- .../unit/osc/v2/vpnaas/test_ikepolicy.py | 20 +++++++++++++++++++ ...ive-negotiation-mode-5218b1baff930eb8.yaml | 5 +++++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/add-aggressive-negotiation-mode-5218b1baff930eb8.yaml diff --git a/neutronclient/osc/v2/vpnaas/ikepolicy.py b/neutronclient/osc/v2/vpnaas/ikepolicy.py index eb598ead2..73b6402d3 100644 --- a/neutronclient/osc/v2/vpnaas/ikepolicy.py +++ b/neutronclient/osc/v2/vpnaas/ikepolicy.py @@ -64,7 +64,7 @@ def _get_common_parser(parser): help=_('Encryption algorithm')) parser.add_argument( '--phase1-negotiation-mode', - choices=['main'], + choices=['main', 'aggressive'], type=_convert_to_lowercase, help=_('IKE Phase1 negotiation mode')) parser.add_argument( diff --git a/neutronclient/tests/unit/osc/v2/vpnaas/test_ikepolicy.py b/neutronclient/tests/unit/osc/v2/vpnaas/test_ikepolicy.py index 24663d6a2..fd621fde5 100644 --- a/neutronclient/tests/unit/osc/v2/vpnaas/test_ikepolicy.py +++ b/neutronclient/tests/unit/osc/v2/vpnaas/test_ikepolicy.py @@ -208,6 +208,10 @@ def test_create_with_all_params(self): def test_create_with_all_params_name(self): self._test_create_with_all_params({'name': 'new_ikepolicy'}) + def test_create_with_all_params_aggressive_mode(self): + self._test_create_with_all_params( + {'phase1_negotiation_mode': 'aggressive'}) + class TestDeleteIKEPolicy(TestIKEPolicy, common.TestDeleteVPNaaS): @@ -292,6 +296,22 @@ def test_set_auth_algorithm_with_sha256(self): target, {self.res: {'auth_algorithm': 'sha256'}}) self.assertIsNone(result) + def test_set_phase1_negotiation_mode_with_aggressive(self): + target = self.resource['id'] + phase1_negotiation_mode = 'aggressive' + arglist = [target, + '--phase1-negotiation-mode', phase1_negotiation_mode] + verifylist = [ + (self.res, target), + ('phase1_negotiation_mode', phase1_negotiation_mode), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {self.res: {'phase1_negotiation_mode': 'aggressive'}}) + self.assertIsNone(result) + class TestShowIKEPolicy(TestIKEPolicy, common.TestShowVPNaaS): diff --git a/releasenotes/notes/add-aggressive-negotiation-mode-5218b1baff930eb8.yaml b/releasenotes/notes/add-aggressive-negotiation-mode-5218b1baff930eb8.yaml new file mode 100644 index 000000000..ae1b6672f --- /dev/null +++ b/releasenotes/notes/add-aggressive-negotiation-mode-5218b1baff930eb8.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + The ``--phase1-negotiation-mode`` option supports ``aggressive`` mode + in VPNaaS ikepolicy commands. From 85ef7b7108ffe491749b52f9ffccade1e2a66164 Mon Sep 17 00:00:00 2001 From: Andreas Jaeger Date: Mon, 18 May 2020 22:08:18 +0200 Subject: [PATCH 750/845] Switch to newer openstackdocstheme and reno versions Switch to openstackdocstheme 2.2.0 and reno 3.1.0 versions. Using these versions will allow especially: * Linking from HTML to PDF document * Allow parallel building of documents * Fix some rendering problems Update Sphinx version as well. Remove docs requirements from lower-constraints, they are not needed during install or test but only for docs building. openstackdocstheme renames some variables, so follow the renames before the next release removes them. A couple of variables are also not needed anymore, remove them. Set openstackdocs_pdf_link to link to PDF file. Note that the link to the published document only works on docs.openstack.org where the PDF file is placed in the top-level html directory. The site-preview places the PDF in a pdf directory. Set openstackdocs_auto_name to use 'project' as name. Depends-On: https://review.opendev.org/728938 Change-Id: I9e1dcc5c8861ac9d800ed7425b3e3193dd13c546 --- doc/requirements.txt | 6 +++--- doc/source/conf.py | 7 ++++--- lower-constraints.txt | 4 ---- releasenotes/source/conf.py | 11 ++++------- 4 files changed, 11 insertions(+), 17 deletions(-) diff --git a/doc/requirements.txt b/doc/requirements.txt index 8dc65ed51..76ce5cd3f 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -1,6 +1,6 @@ # The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. -openstackdocstheme>=1.32.1 # Apache-2.0 -reno>=2.5.0 # Apache-2.0 -sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD +openstackdocstheme>=2.2.0 # Apache-2.0 +reno>=3.1.0 # Apache-2.0 +sphinx>=2.0.0,!=2.1.0 # BSD diff --git a/doc/source/conf.py b/doc/source/conf.py index 4b4300592..e3e269e25 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -13,9 +13,10 @@ ] # openstackdocstheme options -repository_name = 'openstack/python-neutronclient' -bug_project = 'python-neutronclient' -bug_tag = 'doc' +openstackdocs_repo_name = 'openstack/python-neutronclient' +openstackdocs_pdf_link = True +openstackdocs_bug_project = 'python-neutronclient' +openstackdocs_bug_tag = 'doc' # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] diff --git a/lower-constraints.txt b/lower-constraints.txt index 3a51d5545..08d37a452 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -46,7 +46,6 @@ msgpack-python==0.4.0 munch==2.1.0 netaddr==0.7.18 netifaces==0.10.4 -openstackdocstheme==1.32.1 openstacksdk==0.11.2 os-client-config==1.28.0 os-service-types==1.2.0 @@ -91,7 +90,6 @@ python-openstackclient==3.12.0 python-subunit==1.0.0 pytz==2013.6 PyYAML==3.12 -reno==2.5.0 repoze.lru==0.7 requests-mock==1.2.0 requests==2.14.2 @@ -101,8 +99,6 @@ Routes==2.3.1 simplejson==3.5.1 six==1.10.0 snowballstemmer==1.2.1 -Sphinx==1.6.2 -sphinxcontrib-websupport==1.0.1 statsd==3.2.1 stestr==2.0.0 stevedore==1.20.0 diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py index f7ec3924b..5b075fe21 100644 --- a/releasenotes/source/conf.py +++ b/releasenotes/source/conf.py @@ -43,9 +43,10 @@ ] # openstackdocstheme options -repository_name = 'openstack/python-neutronclient' -bug_project = 'python-neutronclient' -bug_tag = 'doc' +openstackdocs_repo_name = 'openstack/python-neutronclient' +openstackdocs_bug_project = 'python-neutronclient' +openstackdocs_bug_tag = 'doc' +openstackdocs_auto_name = False # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -148,10 +149,6 @@ # directly to the root of the documentation. # html_extra_path = [] -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -# html_last_updated_fmt = '%b %d, %Y' - # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True From 3cfa54fa56674362ffbbbd3199e6be3c0e4ffbb9 Mon Sep 17 00:00:00 2001 From: zhanghao Date: Thu, 14 May 2020 02:03:50 -0400 Subject: [PATCH 751/845] Remove usage of six With python3.x, classes can use 'metaclass=' instead of 'six.add_metaclass', 'six.iteritems' and 'six.iterkeys' can be replaced by 'items' and 'keys', 'six.moves.urllib.parse' can be replaced by 'urllib.parse', 'six.StringIO' and 'six.moves.cStringIO' can be replaced by 'io.StringIO', 'six.text_type' and 'six.string_type' are just 'str'. Change-Id: I357968c6a1932856b1600f6c191966bc90cbc258 --- lower-constraints.txt | 1 - neutronclient/common/exceptions.py | 2 -- neutronclient/common/serializer.py | 3 +-- neutronclient/common/utils.py | 5 ++--- neutronclient/neutron/v2_0/__init__.py | 16 +++++++--------- neutronclient/neutron/v2_0/lb/pool.py | 5 +---- .../neutron/v2_0/network_ip_availability.py | 3 +-- neutronclient/neutron/v2_0/quota.py | 7 +++---- neutronclient/tests/unit/test_casual_args.py | 5 ++--- neutronclient/tests/unit/test_cli20.py | 16 ++++++++-------- .../tests/unit/test_cli20_securitygroup.py | 4 ++-- neutronclient/tests/unit/test_exceptions.py | 3 +-- neutronclient/tests/unit/test_http.py | 4 +--- neutronclient/tests/unit/test_shell.py | 6 +++--- neutronclient/v2_0/client.py | 5 ++--- requirements.txt | 1 - 16 files changed, 34 insertions(+), 52 deletions(-) diff --git a/lower-constraints.txt b/lower-constraints.txt index 08d37a452..48b0056b0 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -97,7 +97,6 @@ requestsexceptions==1.2.0 rfc3986==0.3.1 Routes==2.3.1 simplejson==3.5.1 -six==1.10.0 snowballstemmer==1.2.1 statsd==3.2.1 stestr==2.0.0 diff --git a/neutronclient/common/exceptions.py b/neutronclient/common/exceptions.py index 1040ee120..443f78155 100644 --- a/neutronclient/common/exceptions.py +++ b/neutronclient/common/exceptions.py @@ -14,7 +14,6 @@ # under the License. from oslo_utils import encodeutils -import six from neutronclient._i18n import _ @@ -40,7 +39,6 @@ def _safe_decode_dict(kwargs): return kwargs -@six.python_2_unicode_compatible class NeutronException(Exception): """Base Neutron Exception. diff --git a/neutronclient/common/serializer.py b/neutronclient/common/serializer.py index 98865a6b3..d4c8bbbb6 100644 --- a/neutronclient/common/serializer.py +++ b/neutronclient/common/serializer.py @@ -14,7 +14,6 @@ # under the License. from oslo_serialization import jsonutils -import six from neutronclient._i18n import _ from neutronclient.common import exceptions as exception @@ -48,7 +47,7 @@ class JSONDictSerializer(DictSerializer): def default(self, data): def sanitizer(obj): - return six.text_type(obj) + return str(obj) return jsonutils.dumps(data, default=sanitizer) diff --git a/neutronclient/common/utils.py b/neutronclient/common/utils.py index b61ee3b7a..38274018c 100644 --- a/neutronclient/common/utils.py +++ b/neutronclient/common/utils.py @@ -25,7 +25,6 @@ from oslo_utils import encodeutils from oslo_utils import importutils -import six from neutronclient._i18n import _ from neutronclient.common import exceptions @@ -180,7 +179,7 @@ def http_log_req(_logger, args, kwargs): else: string_parts.append(' %s' % element) - for (key, value) in six.iteritems(kwargs['headers']): + for (key, value) in kwargs['headers'].items(): if key in SENSITIVE_HEADERS: v = value.encode('utf-8') h = hashlib.sha256(v) @@ -205,7 +204,7 @@ def http_log_resp(_logger, resp, body): def _safe_encode_without_obj(data): - if isinstance(data, six.string_types): + if isinstance(data, str): return encodeutils.safe_encode(data) return data diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index a5b262c93..3a6e086ee 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -25,7 +25,6 @@ from cliff import lister from cliff import show from oslo_serialization import jsonutils -import six from neutronclient._i18n import _ from neutronclient.common import exceptions @@ -261,12 +260,12 @@ def parse_args_to_dict(values_specs): # Populate the parser with arguments _parser = argparse.ArgumentParser(add_help=False) - for opt, optspec in six.iteritems(_options): + for opt, optspec in _options.items(): _parser.add_argument(opt, **optspec) _args = _parser.parse_args(_values_specs) result_dict = {} - for opt in six.iterkeys(_options): + for opt in _options.keys(): _opt = opt.split('--', 2)[1] _opt = _opt.replace('-', '_') _value = getattr(_args, _opt) @@ -285,7 +284,7 @@ def _merge_args(qCmd, parsed_args, _extra_values, value_specs): @param values_specs: the unparsed unknown parts """ temp_values = _extra_values.copy() - for key, value in six.iteritems(temp_values): + for key, value in temp_values.items(): if hasattr(parsed_args, key): arg_value = getattr(parsed_args, key) if arg_value is not None and value is not None: @@ -321,8 +320,7 @@ def __new__(cls, name, bases, cls_dict): name, bases, cls_dict) -@six.add_metaclass(NeutronCommandMeta) -class NeutronCommand(command.Command): +class NeutronCommand(command.Command, metaclass=NeutronCommandMeta): values_specs = [] json_indent = None @@ -363,7 +361,7 @@ def cleanup_output_data(self, data): def format_output_data(self, data): # Modify data to make it more readable if self.resource in data: - for k, v in six.iteritems(data[self.resource]): + for k, v in data[self.resource].items(): if isinstance(v, list): value = '\n'.join(jsonutils.dumps( i, indent=self.json_indent) if isinstance(i, dict) @@ -425,7 +423,7 @@ def take_action(self, parsed_args): file=self.app.stdout) else: info = {'': ''} - return zip(*sorted(six.iteritems(info))) + return zip(*sorted(info.items())) class UpdateCommand(NeutronCommand): @@ -825,6 +823,6 @@ def take_action(self, parsed_args): self.format_output_data(data) resource = data[self.resource] if self.resource in data: - return zip(*sorted(six.iteritems(resource))) + return zip(*sorted(resource.items())) else: return None diff --git a/neutronclient/neutron/v2_0/lb/pool.py b/neutronclient/neutron/v2_0/lb/pool.py index afc0cc13e..5684e8e6a 100644 --- a/neutronclient/neutron/v2_0/lb/pool.py +++ b/neutronclient/neutron/v2_0/lb/pool.py @@ -14,9 +14,6 @@ # under the License. # - -import six - from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronV20 @@ -119,6 +116,6 @@ def take_action(self, parsed_args): self.format_output_data(data) stats = data['stats'] if 'stats' in data: - return zip(*sorted(six.iteritems(stats))) + return zip(*sorted(stats.items())) else: return None diff --git a/neutronclient/neutron/v2_0/network_ip_availability.py b/neutronclient/neutron/v2_0/network_ip_availability.py index d1332a21c..ba7af8f80 100644 --- a/neutronclient/neutron/v2_0/network_ip_availability.py +++ b/neutronclient/neutron/v2_0/network_ip_availability.py @@ -12,7 +12,6 @@ # from cliff import show -import six from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronV20 @@ -68,6 +67,6 @@ def take_action(self, parsed_args): self.format_output_data(data) resource = data[self.resource] if self.resource in data: - return zip(*sorted(six.iteritems(resource))) + return zip(*sorted(resource.items())) else: return None diff --git a/neutronclient/neutron/v2_0/quota.py b/neutronclient/neutron/v2_0/quota.py index 6e7058d8c..1a59ac546 100644 --- a/neutronclient/neutron/v2_0/quota.py +++ b/neutronclient/neutron/v2_0/quota.py @@ -22,7 +22,6 @@ from cliff import lister from cliff import show from oslo_serialization import jsonutils -import six from neutronclient._i18n import _ from neutronclient.common import exceptions @@ -121,7 +120,7 @@ def take_action(self, parsed_args): tenant_id = get_tenant_id(parsed_args, neutron_client) data = self.retrieve_data(tenant_id, neutron_client) if self.resource in data: - return zip(*sorted(six.iteritems(data[self.resource]))) + return zip(*sorted(data[self.resource].items())) return @@ -241,7 +240,7 @@ def take_action(self, parsed_args): tenant_id = get_tenant_id(parsed_args, neutron_client) data = obj_updator(tenant_id, body) if self.resource in data: - for k, v in six.iteritems(data[self.resource]): + for k, v in data[self.resource].items(): if isinstance(v, list): value = "" for _item in v: @@ -254,6 +253,6 @@ def take_action(self, parsed_args): data[self.resource][k] = value elif v is None: data[self.resource][k] = '' - return zip(*sorted(six.iteritems(data[self.resource]))) + return zip(*sorted(data[self.resource].items())) else: return diff --git a/neutronclient/tests/unit/test_casual_args.py b/neutronclient/tests/unit/test_casual_args.py index f2ca03101..cbf061617 100644 --- a/neutronclient/tests/unit/test_casual_args.py +++ b/neutronclient/tests/unit/test_casual_args.py @@ -14,7 +14,6 @@ # under the License. # -import six import testtools from neutronclient.common import exceptions @@ -116,7 +115,7 @@ def test_parse_args_to_dict_bad_type(self): neutronV20.parse_args_to_dict, _specs) self.assertEqual('Invalid value_specs --badtypearg type=badtype val1: ' 'type badtype is not supported', - six.text_type(ex)) + str(ex)) def test_clear_action(self): _specs = ['--anyarg', 'action=clear'] @@ -128,7 +127,7 @@ def test_bad_values_str_without_value(self): ex = self.assertRaises(exceptions.CommandError, neutronV20.parse_args_to_dict, _specs) self.assertEqual('Invalid values_specs --strarg type=str', - six.text_type(ex)) + str(ex)) def test_bad_values_list(self): _specs = ['--listarg', 'list=true', 'type=str'] diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 3a8bb4719..73104b54c 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -15,16 +15,16 @@ # import contextlib +from io import StringIO import itertools import sys +import urllib.parse as urlparse import mock from oslo_serialization import jsonutils from oslo_utils import encodeutils from oslotest import base import requests -import six -import six.moves.urllib.parse as urlparse import yaml from neutronclient.common import constants @@ -43,7 +43,7 @@ @contextlib.contextmanager def capture_std_streams(): - fake_stdout, fake_stderr = six.StringIO(), six.StringIO() + fake_stdout, fake_stderr = StringIO(), StringIO() stdout, stderr = sys.stdout, sys.stderr try: sys.stdout, sys.stderr = fake_stdout, fake_stderr @@ -125,7 +125,7 @@ def __init__(self, lhs, client): def _com_dict(self, lhs, rhs): if len(lhs) != len(rhs): return False - for key, value in six.iteritems(lhs): + for key, value in lhs.items(): if key not in rhs: return False rhs_value = rhs[key] @@ -743,7 +743,7 @@ def test_do_request_unicode(self): def test_do_request_error_without_response_body(self): params = {'test': 'value'} - expect_query = six.moves.urllib.parse.urlencode(params) + expect_query = urlparse.urlencode(params) self.client.httpclient.auth_token = 'token' resp_headers = {'x-openstack-request-id': REQUEST_ID} resp = (MyResp(400, headers=resp_headers, reason='An error'), '') @@ -772,7 +772,7 @@ def test_do_request_with_long_uri_exception(self): def test_do_request_request_ids(self): params = {'test': 'value'} - expect_query = six.moves.urllib.parse.urlencode(params) + expect_query = urlparse.urlencode(params) self.client.httpclient.auth_token = 'token' body = params expect_body = self.client.serialize(body) @@ -866,7 +866,7 @@ def test_deserialize_without_data(self): def test_update_resource(self): params = {'test': 'value'} - expect_query = six.moves.urllib.parse.urlencode(params) + expect_query = urlparse.urlencode(params) self.client.httpclient.auth_token = 'token' body = params expect_body = self.client.serialize(body) @@ -889,7 +889,7 @@ def test_update_resource(self): def test_update_resource_with_revision_number(self): params = {'test': 'value'} - expect_query = six.moves.urllib.parse.urlencode(params) + expect_query = urlparse.urlencode(params) self.client.httpclient.auth_token = 'token' body = params expect_body = self.client.serialize(body) diff --git a/neutronclient/tests/unit/test_cli20_securitygroup.py b/neutronclient/tests/unit/test_cli20_securitygroup.py index 0f5a240cf..010f7dcae 100644 --- a/neutronclient/tests/unit/test_cli20_securitygroup.py +++ b/neutronclient/tests/unit/test_cli20_securitygroup.py @@ -15,10 +15,10 @@ # under the License. import sys +import urllib.parse as urlparse import mock from oslo_utils import uuidutils -import six from neutronclient.common import exceptions from neutronclient.common import utils @@ -250,7 +250,7 @@ def _build_test_data(self, data, excess=0): resp_str = self.client.serialize({'security_groups': response}) result.append({ - 'filter': six.moves.urllib.parse.urlencode(params, doseq=1), + 'filter': urlparse.urlencode(params, doseq=1), 'response': (test_cli20.MyResp(200), resp_str), }) diff --git a/neutronclient/tests/unit/test_exceptions.py b/neutronclient/tests/unit/test_exceptions.py index aefc24c66..50b2bdbbe 100644 --- a/neutronclient/tests/unit/test_exceptions.py +++ b/neutronclient/tests/unit/test_exceptions.py @@ -16,7 +16,6 @@ import mock from oslo_utils import encodeutils -import six import testtools from neutronclient._i18n import _ @@ -46,4 +45,4 @@ class TestException(exceptions.NeutronException): multibyte_binary = encodeutils.safe_encode(multibyte_string) e = TestException(reason=multibyte_binary) self.assertEqual('Exception with %s' % multibyte_string, - six.text_type(e)) + str(e)) diff --git a/neutronclient/tests/unit/test_http.py b/neutronclient/tests/unit/test_http.py index d76e9bce9..5680d4375 100644 --- a/neutronclient/tests/unit/test_http.py +++ b/neutronclient/tests/unit/test_http.py @@ -19,7 +19,6 @@ import osprofiler.profiler import osprofiler.web from requests_mock.contrib import fixture as mock_fixture -import six import testtools from neutronclient import client @@ -33,8 +32,7 @@ BODY = 'IAMFAKE' -@six.add_metaclass(abc.ABCMeta) -class TestHTTPClientMixin(object): +class TestHTTPClientMixin(object, metaclass=abc.ABCMeta): def setUp(self): super(TestHTTPClientMixin, self).setUp() diff --git a/neutronclient/tests/unit/test_shell.py b/neutronclient/tests/unit/test_shell.py index 0395d6233..86250a139 100644 --- a/neutronclient/tests/unit/test_shell.py +++ b/neutronclient/tests/unit/test_shell.py @@ -14,6 +14,7 @@ # under the License. import argparse +from io import StringIO import logging import os import re @@ -22,7 +23,6 @@ import fixtures from keystoneauth1 import session import mock -import six import testtools from testtools import matchers @@ -75,8 +75,8 @@ def shell(self, argstr, check=False, expected_val=0): clean_env = {} _old_env, os.environ = os.environ, clean_env.copy() try: - sys.stdout = six.moves.cStringIO() - sys.stderr = six.moves.cStringIO() + sys.stdout = StringIO() + sys.stderr = StringIO() _shell = openstack_shell.NeutronShell('2.0') _shell.run(argstr.split()) except SystemExit: diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 5fa869840..13f90e42d 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -21,12 +21,11 @@ import logging import re import time +import urllib.parse as urlparse import debtcollector.renames from keystoneauth1 import exceptions as ksa_exc import requests -import six.moves.urllib.parse as urlparse -from six import string_types from neutronclient._i18n import _ from neutronclient import client @@ -398,7 +397,7 @@ def _convert_into_with_meta(self, item, resp): if item: if isinstance(item, dict): return _DictWithMeta(item, resp) - elif isinstance(item, string_types): + elif isinstance(item, str): return _StrWithMeta(item, resp) else: return _TupleWithMeta((), resp) diff --git a/requirements.txt b/requirements.txt index 5204dc21e..c1c9c28e3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,4 +18,3 @@ keystoneauth1>=3.4.0 # Apache-2.0 python-keystoneclient>=3.8.0 # Apache-2.0 requests>=2.14.2 # Apache-2.0 simplejson>=3.5.1 # MIT -six>=1.10.0 # MIT From 626970a353c158158173dac0730cf1204fcf2f11 Mon Sep 17 00:00:00 2001 From: elajkat Date: Fri, 8 May 2020 12:17:40 +0200 Subject: [PATCH 752/845] Add segments client code Related-Bug: #1878632 Needed-By: https://review.opendev.org/728904 Change-Id: I85b8e7b3fb84e7e9fd133edffc300a83eeb7c56d --- neutronclient/v2_0/client.py | 25 +++++++++++++++++++ .../notes/segments-8557f5b0caa5ee26.yaml | 5 ++++ 2 files changed, 30 insertions(+) create mode 100644 releasenotes/notes/segments-8557f5b0caa5ee26.yaml diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 5fa869840..0cc48ea24 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -516,6 +516,8 @@ class Client(ClientBase): security_group_path = "/security-groups/%s" security_group_rules_path = "/security-group-rules" security_group_rule_path = "/security-group-rules/%s" + segments_path = "/segments" + segment_path = "/segments/%s" sfc_flow_classifiers_path = "/sfc/flow_classifiers" sfc_flow_classifier_path = "/sfc/flow_classifiers/%s" @@ -669,6 +671,7 @@ class Client(ClientBase): 'service_definitions': 'service_definition', 'security_groups': 'security_group', 'security_group_rules': 'security_group_rule', + 'segments': 'segment', 'ipsecpolicies': 'ipsecpolicy', 'ikepolicies': 'ikepolicy', 'ipsec_site_connections': 'ipsec_site_connection', @@ -1048,6 +1051,28 @@ def show_security_group_rule(self, security_group_rule, **_params): return self.get(self.security_group_rule_path % (security_group_rule), params=_params) + def create_segment(self, body=None): + """Creates a new segment.""" + return self.post(self.segments_path, body=body) + + def update_segment(self, segment, body=None, revision_number=None): + """Updates a segment.""" + return self._update_resource(self.segment_path % segment, body=body, + revision_number=revision_number) + + def list_segments(self, retrieve_all=True, **_params): + """Fetches a list of all segments for a project.""" + return self.list('segments', self.segments_path, retrieve_all, + **_params) + + def show_segment(self, segment, **_params): + """Fetches information of a certain segment.""" + return self.get(self.segment_path % segment, params=_params) + + def delete_segment(self, segment): + """Deletes the specified segment.""" + return self.delete(self.segment_path % segment) + def list_endpoint_groups(self, retrieve_all=True, **_params): """Fetches a list of all VPN endpoint groups for a project.""" return self.list('endpoint_groups', self.endpoint_groups_path, diff --git a/releasenotes/notes/segments-8557f5b0caa5ee26.yaml b/releasenotes/notes/segments-8557f5b0caa5ee26.yaml new file mode 100644 index 000000000..292adf85e --- /dev/null +++ b/releasenotes/notes/segments-8557f5b0caa5ee26.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + New client methods: ``create_segment``, ``update_segment``, + ``list_segments``, ``show_segment`` and ``delete_segment``. From 4669bcc6280ba7d6776d6bdf6d6d1c34c87298e6 Mon Sep 17 00:00:00 2001 From: Sean McGinnis Date: Sat, 18 Apr 2020 11:58:59 -0500 Subject: [PATCH 753/845] Use unittest.mock instead of third party mock Now that we no longer support py27, we can use the standard library unittest.mock module instead of the third party mock lib. Change-Id: I12e1a2a4a22116cabd09a3b808f871d98e4bd1f2 Signed-off-by: Sean McGinnis --- lower-constraints.txt | 1 - neutronclient/tests/unit/bgp/test_cli20_speaker.py | 2 +- neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py | 2 +- neutronclient/tests/unit/lb/test_cli20_healthmonitor.py | 2 +- neutronclient/tests/unit/lb/test_cli20_pool.py | 2 +- neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py | 2 +- neutronclient/tests/unit/osc/v2/dynamic_routing/fakes.py | 2 +- .../tests/unit/osc/v2/dynamic_routing/test_bgp_dragent.py | 2 +- .../tests/unit/osc/v2/dynamic_routing/test_bgp_peer.py | 2 +- .../tests/unit/osc/v2/dynamic_routing/test_bgp_speaker.py | 2 +- neutronclient/tests/unit/osc/v2/fakes.py | 2 +- neutronclient/tests/unit/osc/v2/fwaas/fakes.py | 2 +- neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py | 2 +- neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py | 2 +- neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py | 2 +- neutronclient/tests/unit/osc/v2/logging/fakes.py | 3 +-- neutronclient/tests/unit/osc/v2/logging/test_network_log.py | 2 +- neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py | 2 +- .../tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py | 2 +- .../unit/osc/v2/networking_bgpvpn/test_resource_association.py | 2 +- .../unit/osc/v2/networking_bgpvpn/test_router_association.py | 2 +- neutronclient/tests/unit/osc/v2/sfc/fakes.py | 2 +- neutronclient/tests/unit/osc/v2/sfc/test_flow_classifier.py | 2 +- neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py | 2 +- neutronclient/tests/unit/osc/v2/sfc/test_port_pair.py | 2 +- neutronclient/tests/unit/osc/v2/sfc/test_port_pair_group.py | 2 +- neutronclient/tests/unit/osc/v2/sfc/test_service_graph.py | 2 +- .../unit/osc/v2/subnet_onboard/test_network_onboard_subnets.py | 2 +- neutronclient/tests/unit/osc/v2/trunk/fakes.py | 2 +- neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py | 2 +- neutronclient/tests/unit/osc/v2/vpnaas/fakes.py | 3 +-- neutronclient/tests/unit/osc/v2/vpnaas/test_endpoint_group.py | 2 +- neutronclient/tests/unit/osc/v2/vpnaas/test_ikepolicy.py | 2 +- .../tests/unit/osc/v2/vpnaas/test_ipsec_site_connection.py | 2 +- neutronclient/tests/unit/osc/v2/vpnaas/test_ipsecpolicy.py | 2 +- neutronclient/tests/unit/osc/v2/vpnaas/test_vpnservice.py | 2 +- neutronclient/tests/unit/test_cli20.py | 2 +- neutronclient/tests/unit/test_cli20_address_scope.py | 2 +- neutronclient/tests/unit/test_cli20_agentschedulers.py | 2 +- neutronclient/tests/unit/test_cli20_network.py | 2 +- neutronclient/tests/unit/test_cli20_port.py | 2 +- neutronclient/tests/unit/test_cli20_securitygroup.py | 2 +- neutronclient/tests/unit/test_cli20_subnet.py | 2 +- neutronclient/tests/unit/test_cli20_subnetpool.py | 2 +- neutronclient/tests/unit/test_cli20_tag.py | 2 +- neutronclient/tests/unit/test_client_extension.py | 2 +- neutronclient/tests/unit/test_exceptions.py | 2 +- neutronclient/tests/unit/test_name_or_id.py | 3 ++- neutronclient/tests/unit/test_quota.py | 2 +- neutronclient/tests/unit/test_shell.py | 2 +- test-requirements.txt | 1 - 51 files changed, 50 insertions(+), 53 deletions(-) diff --git a/lower-constraints.txt b/lower-constraints.txt index 48b0056b0..8f576fed0 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -39,7 +39,6 @@ kombu==4.0.0 linecache2==1.0.0 MarkupSafe==1.0 mccabe==0.2.1 -mock==2.0.0 monotonic==0.6 mox3==0.20.0 msgpack-python==0.4.0 diff --git a/neutronclient/tests/unit/bgp/test_cli20_speaker.py b/neutronclient/tests/unit/bgp/test_cli20_speaker.py index 72612dc6c..0d9cc60db 100644 --- a/neutronclient/tests/unit/bgp/test_cli20_speaker.py +++ b/neutronclient/tests/unit/bgp/test_cli20_speaker.py @@ -15,8 +15,8 @@ # import sys +from unittest import mock -import mock from neutronclient.common import exceptions from neutronclient.neutron.v2_0.bgp import speaker as bgp_speaker diff --git a/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py b/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py index 874ec7232..4cadfb2de 100644 --- a/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py +++ b/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py @@ -15,8 +15,8 @@ # import sys +from unittest import mock -import mock from neutronclient.neutron.v2_0.fw import firewallpolicy from neutronclient import shell diff --git a/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py b/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py index 7c4d15c09..61cb1b1a5 100644 --- a/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py +++ b/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py @@ -15,8 +15,8 @@ # import sys +from unittest import mock -import mock from neutronclient.neutron.v2_0.lb import healthmonitor from neutronclient.tests.unit import test_cli20 diff --git a/neutronclient/tests/unit/lb/test_cli20_pool.py b/neutronclient/tests/unit/lb/test_cli20_pool.py index 1106aed92..79bf23df8 100644 --- a/neutronclient/tests/unit/lb/test_cli20_pool.py +++ b/neutronclient/tests/unit/lb/test_cli20_pool.py @@ -15,8 +15,8 @@ # import sys +from unittest import mock -import mock from neutronclient.neutron.v2_0.lb import pool from neutronclient.tests.unit import test_cli20 diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py b/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py index 6db07cc1a..3a6010cee 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py @@ -15,8 +15,8 @@ # import sys +from unittest import mock -import mock from neutronclient.neutron.v2_0.lb.v2 import loadbalancer as lb from neutronclient.tests.unit import test_cli20 diff --git a/neutronclient/tests/unit/osc/v2/dynamic_routing/fakes.py b/neutronclient/tests/unit/osc/v2/dynamic_routing/fakes.py index d425cd67b..c496a3f66 100644 --- a/neutronclient/tests/unit/osc/v2/dynamic_routing/fakes.py +++ b/neutronclient/tests/unit/osc/v2/dynamic_routing/fakes.py @@ -11,9 +11,9 @@ # under the License. import copy +from unittest import mock import uuid -import mock from neutronclient.tests.unit.osc.v2 import fakes diff --git a/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_dragent.py b/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_dragent.py index 1ec7c41f0..7a94b323a 100644 --- a/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_dragent.py +++ b/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_dragent.py @@ -10,7 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. # -import mock +from unittest import mock from neutronclient.osc.v2.dynamic_routing import bgp_dragent from neutronclient.tests.unit.osc.v2.dynamic_routing import fakes diff --git a/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_peer.py b/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_peer.py index ffe69f18d..506c86d93 100644 --- a/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_peer.py +++ b/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_peer.py @@ -10,7 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. # -import mock +from unittest import mock from neutronclient.osc.v2.dynamic_routing import bgp_peer from neutronclient.tests.unit.osc.v2.dynamic_routing import fakes diff --git a/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_speaker.py b/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_speaker.py index dbecf057c..159746875 100644 --- a/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_speaker.py +++ b/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_speaker.py @@ -10,7 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. # -import mock +from unittest import mock from neutronclient.osc.v2.dynamic_routing import bgp_speaker from neutronclient.tests.unit.osc.v2.dynamic_routing import fakes diff --git a/neutronclient/tests/unit/osc/v2/fakes.py b/neutronclient/tests/unit/osc/v2/fakes.py index afa81e72d..c7876b645 100644 --- a/neutronclient/tests/unit/osc/v2/fakes.py +++ b/neutronclient/tests/unit/osc/v2/fakes.py @@ -12,9 +12,9 @@ # import argparse +from unittest import mock from cliff import columns as cliff_columns -import mock from osc_lib.tests import utils diff --git a/neutronclient/tests/unit/osc/v2/fwaas/fakes.py b/neutronclient/tests/unit/osc/v2/fwaas/fakes.py index 6475392c4..d0110989b 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/fakes.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/fakes.py @@ -16,8 +16,8 @@ import collections import copy +from unittest import mock -import mock from oslo_utils import uuidutils diff --git a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py index cc54d406b..41bfecb2d 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py @@ -16,8 +16,8 @@ import copy import re +from unittest import mock -import mock from osc_lib import exceptions from osc_lib.tests import utils diff --git a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py index 316eec887..7eba77483 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py @@ -16,8 +16,8 @@ import copy import re +from unittest import mock -import mock from osc_lib import exceptions from osc_lib.tests import utils diff --git a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py index a8b25c304..dac94cb74 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py @@ -16,8 +16,8 @@ import copy import re +from unittest import mock -import mock from osc_lib import exceptions from osc_lib.tests import utils import testtools diff --git a/neutronclient/tests/unit/osc/v2/logging/fakes.py b/neutronclient/tests/unit/osc/v2/logging/fakes.py index 856838fee..a20e3712e 100644 --- a/neutronclient/tests/unit/osc/v2/logging/fakes.py +++ b/neutronclient/tests/unit/osc/v2/logging/fakes.py @@ -16,10 +16,9 @@ import collections import copy +from unittest import mock import uuid -import mock - class FakeLogging(object): diff --git a/neutronclient/tests/unit/osc/v2/logging/test_network_log.py b/neutronclient/tests/unit/osc/v2/logging/test_network_log.py index d36453542..c2e0390ae 100644 --- a/neutronclient/tests/unit/osc/v2/logging/test_network_log.py +++ b/neutronclient/tests/unit/osc/v2/logging/test_network_log.py @@ -15,8 +15,8 @@ # import copy +from unittest import mock -import mock from osc_lib import exceptions from osc_lib.tests import utils import testtools diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py index b3a538f5a..a0b2cb892 100644 --- a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py +++ b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py @@ -15,8 +15,8 @@ # import copy +from unittest import mock -import mock from osc_lib.utils import columns as column_util from neutronclient.osc import utils as nc_osc_utils diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py index 14b6bb1a5..16fb164ae 100644 --- a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py +++ b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py @@ -16,8 +16,8 @@ import copy import operator +from unittest import mock -import mock from osc_lib import exceptions from osc_lib import utils as osc_utils from osc_lib.utils import columns as column_util diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py index 926001742..f8aa95f47 100644 --- a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py +++ b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py @@ -16,8 +16,8 @@ import copy import operator +from unittest import mock -import mock from osc_lib import exceptions from osc_lib import utils as osc_utils from osc_lib.utils import columns as column_util diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_router_association.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_router_association.py index fb17bdb67..f48424c48 100644 --- a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_router_association.py +++ b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_router_association.py @@ -16,8 +16,8 @@ import copy import operator +from unittest import mock -import mock from osc_lib.tests.utils import ParserException from osc_lib import utils as osc_utils from osc_lib.utils import columns as column_util diff --git a/neutronclient/tests/unit/osc/v2/sfc/fakes.py b/neutronclient/tests/unit/osc/v2/sfc/fakes.py index 97861207f..ba07107fd 100755 --- a/neutronclient/tests/unit/osc/v2/sfc/fakes.py +++ b/neutronclient/tests/unit/osc/v2/sfc/fakes.py @@ -15,8 +15,8 @@ import argparse import copy +from unittest import mock -import mock from osc_lib.tests import utils from oslo_utils import uuidutils diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_flow_classifier.py b/neutronclient/tests/unit/osc/v2/sfc/test_flow_classifier.py index e4672d377..379f72767 100755 --- a/neutronclient/tests/unit/osc/v2/sfc/test_flow_classifier.py +++ b/neutronclient/tests/unit/osc/v2/sfc/test_flow_classifier.py @@ -13,7 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock from neutronclient.osc.v2.sfc import sfc_flow_classifier from neutronclient.tests.unit.osc.v2.sfc import fakes diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py b/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py index fbf60303d..da6455230 100755 --- a/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py +++ b/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py @@ -13,7 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock from osc_lib import exceptions diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_port_pair.py b/neutronclient/tests/unit/osc/v2/sfc/test_port_pair.py index c56781881..8246e35d9 100755 --- a/neutronclient/tests/unit/osc/v2/sfc/test_port_pair.py +++ b/neutronclient/tests/unit/osc/v2/sfc/test_port_pair.py @@ -13,7 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock from neutronclient.osc.v2.sfc import sfc_port_pair from neutronclient.tests.unit.osc.v2.sfc import fakes diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_port_pair_group.py b/neutronclient/tests/unit/osc/v2/sfc/test_port_pair_group.py index 48cee1ed9..6db21a284 100755 --- a/neutronclient/tests/unit/osc/v2/sfc/test_port_pair_group.py +++ b/neutronclient/tests/unit/osc/v2/sfc/test_port_pair_group.py @@ -13,7 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock from neutronclient.osc.v2.sfc import sfc_port_pair_group from neutronclient.tests.unit.osc.v2.sfc import fakes diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_service_graph.py b/neutronclient/tests/unit/osc/v2/sfc/test_service_graph.py index da12aa570..a05ed2c72 100644 --- a/neutronclient/tests/unit/osc/v2/sfc/test_service_graph.py +++ b/neutronclient/tests/unit/osc/v2/sfc/test_service_graph.py @@ -11,8 +11,8 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. +from unittest import mock -import mock from osc_lib import exceptions from osc_lib.tests import utils as tests_utils diff --git a/neutronclient/tests/unit/osc/v2/subnet_onboard/test_network_onboard_subnets.py b/neutronclient/tests/unit/osc/v2/subnet_onboard/test_network_onboard_subnets.py index efd3d1d11..c2ca14044 100644 --- a/neutronclient/tests/unit/osc/v2/subnet_onboard/test_network_onboard_subnets.py +++ b/neutronclient/tests/unit/osc/v2/subnet_onboard/test_network_onboard_subnets.py @@ -14,7 +14,7 @@ # under the License. # -import mock +from unittest import mock from neutronclient.osc.v2.subnet_onboard import subnet_onboard from neutronclient.tests.unit.osc.v2 import fakes as test_fakes diff --git a/neutronclient/tests/unit/osc/v2/trunk/fakes.py b/neutronclient/tests/unit/osc/v2/trunk/fakes.py index f7da0dd77..a52aa3097 100644 --- a/neutronclient/tests/unit/osc/v2/trunk/fakes.py +++ b/neutronclient/tests/unit/osc/v2/trunk/fakes.py @@ -11,8 +11,8 @@ # under the License. import copy +from unittest import mock -import mock from oslo_utils import uuidutils diff --git a/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py b/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py index 8e9e73c49..dd6799870 100644 --- a/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py +++ b/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py @@ -15,8 +15,8 @@ import argparse import copy +from unittest import mock -import mock from mock import call from osc_lib.cli import format_columns from osc_lib import exceptions diff --git a/neutronclient/tests/unit/osc/v2/vpnaas/fakes.py b/neutronclient/tests/unit/osc/v2/vpnaas/fakes.py index 7ee7c5d5e..dfad250b3 100644 --- a/neutronclient/tests/unit/osc/v2/vpnaas/fakes.py +++ b/neutronclient/tests/unit/osc/v2/vpnaas/fakes.py @@ -16,10 +16,9 @@ import collections import copy +from unittest import mock import uuid -import mock - class FakeVPNaaS(object): diff --git a/neutronclient/tests/unit/osc/v2/vpnaas/test_endpoint_group.py b/neutronclient/tests/unit/osc/v2/vpnaas/test_endpoint_group.py index cc597e701..9c5dbcce5 100644 --- a/neutronclient/tests/unit/osc/v2/vpnaas/test_endpoint_group.py +++ b/neutronclient/tests/unit/osc/v2/vpnaas/test_endpoint_group.py @@ -15,8 +15,8 @@ # import copy +from unittest import mock -import mock from osc_lib.tests import utils as tests_utils from neutronclient.osc import utils as osc_utils diff --git a/neutronclient/tests/unit/osc/v2/vpnaas/test_ikepolicy.py b/neutronclient/tests/unit/osc/v2/vpnaas/test_ikepolicy.py index 24663d6a2..0d2673a99 100644 --- a/neutronclient/tests/unit/osc/v2/vpnaas/test_ikepolicy.py +++ b/neutronclient/tests/unit/osc/v2/vpnaas/test_ikepolicy.py @@ -15,8 +15,8 @@ # import copy +from unittest import mock -import mock from osc_lib.tests import utils as tests_utils from neutronclient.osc import utils as osc_utils diff --git a/neutronclient/tests/unit/osc/v2/vpnaas/test_ipsec_site_connection.py b/neutronclient/tests/unit/osc/v2/vpnaas/test_ipsec_site_connection.py index 4d04b641c..96313b459 100644 --- a/neutronclient/tests/unit/osc/v2/vpnaas/test_ipsec_site_connection.py +++ b/neutronclient/tests/unit/osc/v2/vpnaas/test_ipsec_site_connection.py @@ -15,8 +15,8 @@ # import copy +from unittest import mock -import mock from osc_lib.cli import format_columns from osc_lib.tests import utils as tests_utils diff --git a/neutronclient/tests/unit/osc/v2/vpnaas/test_ipsecpolicy.py b/neutronclient/tests/unit/osc/v2/vpnaas/test_ipsecpolicy.py index 8e56cf3f9..0df98e3f3 100644 --- a/neutronclient/tests/unit/osc/v2/vpnaas/test_ipsecpolicy.py +++ b/neutronclient/tests/unit/osc/v2/vpnaas/test_ipsecpolicy.py @@ -15,8 +15,8 @@ # import copy +from unittest import mock -import mock from osc_lib.tests import utils as tests_utils from neutronclient.osc import utils as osc_utils diff --git a/neutronclient/tests/unit/osc/v2/vpnaas/test_vpnservice.py b/neutronclient/tests/unit/osc/v2/vpnaas/test_vpnservice.py index a9c208e7c..7630eb8e0 100644 --- a/neutronclient/tests/unit/osc/v2/vpnaas/test_vpnservice.py +++ b/neutronclient/tests/unit/osc/v2/vpnaas/test_vpnservice.py @@ -15,9 +15,9 @@ # import copy +from unittest import mock import uuid -import mock from neutronclient.osc import utils as osc_utils from neutronclient.osc.v2.vpnaas import vpnservice diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 73104b54c..c2143e168 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -18,9 +18,9 @@ from io import StringIO import itertools import sys +from unittest import mock import urllib.parse as urlparse -import mock from oslo_serialization import jsonutils from oslo_utils import encodeutils from oslotest import base diff --git a/neutronclient/tests/unit/test_cli20_address_scope.py b/neutronclient/tests/unit/test_cli20_address_scope.py index f58185fd0..902f7e653 100644 --- a/neutronclient/tests/unit/test_cli20_address_scope.py +++ b/neutronclient/tests/unit/test_cli20_address_scope.py @@ -15,8 +15,8 @@ # import sys +from unittest import mock -import mock from neutronclient.common import exceptions from neutronclient.neutron.v2_0 import address_scope diff --git a/neutronclient/tests/unit/test_cli20_agentschedulers.py b/neutronclient/tests/unit/test_cli20_agentschedulers.py index ddb8dce7d..817909d38 100644 --- a/neutronclient/tests/unit/test_cli20_agentschedulers.py +++ b/neutronclient/tests/unit/test_cli20_agentschedulers.py @@ -15,8 +15,8 @@ # import sys +from unittest import mock -import mock from neutronclient.neutron.v2_0 import agentscheduler from neutronclient.neutron.v2_0 import network diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index 2f7a46896..b1f69acc8 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -15,8 +15,8 @@ import itertools import sys +from unittest import mock -import mock from oslo_serialization import jsonutils from neutronclient.common import exceptions diff --git a/neutronclient/tests/unit/test_cli20_port.py b/neutronclient/tests/unit/test_cli20_port.py index f6fc61783..e1ab411e5 100644 --- a/neutronclient/tests/unit/test_cli20_port.py +++ b/neutronclient/tests/unit/test_cli20_port.py @@ -16,8 +16,8 @@ import itertools import sys +from unittest import mock -import mock from neutronclient.neutron.v2_0 import port from neutronclient import shell diff --git a/neutronclient/tests/unit/test_cli20_securitygroup.py b/neutronclient/tests/unit/test_cli20_securitygroup.py index 010f7dcae..f5ed8ad1e 100644 --- a/neutronclient/tests/unit/test_cli20_securitygroup.py +++ b/neutronclient/tests/unit/test_cli20_securitygroup.py @@ -15,9 +15,9 @@ # under the License. import sys +from unittest import mock import urllib.parse as urlparse -import mock from oslo_utils import uuidutils from neutronclient.common import exceptions diff --git a/neutronclient/tests/unit/test_cli20_subnet.py b/neutronclient/tests/unit/test_cli20_subnet.py index ab37cbdd7..97c2faff0 100644 --- a/neutronclient/tests/unit/test_cli20_subnet.py +++ b/neutronclient/tests/unit/test_cli20_subnet.py @@ -15,8 +15,8 @@ # import sys +from unittest import mock -import mock from neutronclient.common import exceptions from neutronclient.neutron import v2_0 as neutronV20 diff --git a/neutronclient/tests/unit/test_cli20_subnetpool.py b/neutronclient/tests/unit/test_cli20_subnetpool.py index 89a89b244..fb201683a 100644 --- a/neutronclient/tests/unit/test_cli20_subnetpool.py +++ b/neutronclient/tests/unit/test_cli20_subnetpool.py @@ -15,8 +15,8 @@ # import sys +from unittest import mock -import mock from neutronclient.common import exceptions from neutronclient.neutron.v2_0 import subnetpool diff --git a/neutronclient/tests/unit/test_cli20_tag.py b/neutronclient/tests/unit/test_cli20_tag.py index bb3c451e9..2e23c9dc5 100644 --- a/neutronclient/tests/unit/test_cli20_tag.py +++ b/neutronclient/tests/unit/test_cli20_tag.py @@ -11,8 +11,8 @@ # under the License. import sys +from unittest import mock -import mock from neutronclient.common import exceptions from neutronclient.neutron.v2_0 import network diff --git a/neutronclient/tests/unit/test_client_extension.py b/neutronclient/tests/unit/test_client_extension.py index 7e443a01e..7137ae7b3 100644 --- a/neutronclient/tests/unit/test_client_extension.py +++ b/neutronclient/tests/unit/test_client_extension.py @@ -16,8 +16,8 @@ import inspect import sys +from unittest import mock -import mock from neutronclient.common import extension from neutronclient.neutron.v2_0.contrib import _fox_sockets as fox_sockets diff --git a/neutronclient/tests/unit/test_exceptions.py b/neutronclient/tests/unit/test_exceptions.py index 50b2bdbbe..6e063244c 100644 --- a/neutronclient/tests/unit/test_exceptions.py +++ b/neutronclient/tests/unit/test_exceptions.py @@ -13,8 +13,8 @@ # under the License. import sys +from unittest import mock -import mock from oslo_utils import encodeutils import testtools diff --git a/neutronclient/tests/unit/test_name_or_id.py b/neutronclient/tests/unit/test_name_or_id.py index ee94b24b1..a6a7b9246 100644 --- a/neutronclient/tests/unit/test_name_or_id.py +++ b/neutronclient/tests/unit/test_name_or_id.py @@ -14,7 +14,8 @@ # under the License. # -import mock +from unittest import mock + from oslo_utils import uuidutils import testtools diff --git a/neutronclient/tests/unit/test_quota.py b/neutronclient/tests/unit/test_quota.py index 6192bbb9a..4529287b0 100644 --- a/neutronclient/tests/unit/test_quota.py +++ b/neutronclient/tests/unit/test_quota.py @@ -15,8 +15,8 @@ # under the License. import sys +from unittest import mock -import mock from neutronclient.common import exceptions from neutronclient.neutron.v2_0 import quota as test_quota diff --git a/neutronclient/tests/unit/test_shell.py b/neutronclient/tests/unit/test_shell.py index 86250a139..9dc2faac8 100644 --- a/neutronclient/tests/unit/test_shell.py +++ b/neutronclient/tests/unit/test_shell.py @@ -19,10 +19,10 @@ import os import re import sys +from unittest import mock import fixtures from keystoneauth1 import session -import mock import testtools from testtools import matchers diff --git a/test-requirements.txt b/test-requirements.txt index a0d76ed70..ae1f2e55d 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -7,7 +7,6 @@ bandit!=1.6.0,>=1.1.0 # Apache-2.0 coverage!=4.4,>=4.0 # Apache-2.0 fixtures>=3.0.0 # Apache-2.0/BSD flake8-import-order==0.12 # LGPLv3 -mock>=2.0.0 # BSD oslotest>=3.2.0 # Apache-2.0 osprofiler>=2.3.0 # Apache-2.0 python-openstackclient>=3.12.0 # Apache-2.0 From 42ebefab3bc9aa6d9721258541f62e96a573d559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Beraud?= Date: Tue, 2 Jun 2020 20:50:34 +0200 Subject: [PATCH 754/845] Stop to use the __future__ module. The __future__ module [1] was used in this context to ensure compatibility between python 2 and python 3. We previously dropped the support of python 2.7 [2] and now we only support python 3 so we don't need to continue to use this module and the imports listed below. Imports commonly used and their related PEPs: - `division` is related to PEP 238 [3] - `print_function` is related to PEP 3105 [4] - `unicode_literals` is related to PEP 3112 [5] - `with_statement` is related to PEP 343 [6] - `absolute_import` is related to PEP 328 [7] [1] https://docs.python.org/3/library/__future__.html [2] https://governance.openstack.org/tc/goals/selected/ussuri/drop-py27.html [3] https://www.python.org/dev/peps/pep-0238 [4] https://www.python.org/dev/peps/pep-3105 [5] https://www.python.org/dev/peps/pep-3112 [6] https://www.python.org/dev/peps/pep-0343 [7] https://www.python.org/dev/peps/pep-0328 Change-Id: I65fb53db889afcf1a947ea61094bfab877853324 --- neutronclient/neutron/v2_0/__init__.py | 2 -- neutronclient/neutron/v2_0/agentscheduler.py | 2 -- neutronclient/neutron/v2_0/auto_allocated_topology.py | 2 -- neutronclient/neutron/v2_0/bgp/dragentscheduler.py | 2 -- neutronclient/neutron/v2_0/bgp/speaker.py | 2 -- neutronclient/neutron/v2_0/flavor/flavor.py | 2 -- neutronclient/neutron/v2_0/floatingip.py | 2 -- neutronclient/neutron/v2_0/fw/firewallpolicy.py | 2 -- neutronclient/neutron/v2_0/lb/healthmonitor.py | 2 -- neutronclient/neutron/v2_0/quota.py | 2 -- neutronclient/neutron/v2_0/router.py | 2 -- neutronclient/osc/v2/fwaas/firewallpolicy.py | 2 -- neutronclient/shell.py | 2 -- 13 files changed, 26 deletions(-) diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index 3a6e086ee..849a93d9e 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -14,8 +14,6 @@ # under the License. # -from __future__ import print_function - import abc import argparse import functools diff --git a/neutronclient/neutron/v2_0/agentscheduler.py b/neutronclient/neutron/v2_0/agentscheduler.py index 4476b1dd3..e92b10be1 100644 --- a/neutronclient/neutron/v2_0/agentscheduler.py +++ b/neutronclient/neutron/v2_0/agentscheduler.py @@ -14,8 +14,6 @@ # under the License. # -from __future__ import print_function - from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronV20 from neutronclient.neutron.v2_0 import network diff --git a/neutronclient/neutron/v2_0/auto_allocated_topology.py b/neutronclient/neutron/v2_0/auto_allocated_topology.py index ef0da4325..a24959e16 100644 --- a/neutronclient/neutron/v2_0/auto_allocated_topology.py +++ b/neutronclient/neutron/v2_0/auto_allocated_topology.py @@ -14,8 +14,6 @@ # under the License. # -from __future__ import print_function - import argparse from cliff import show diff --git a/neutronclient/neutron/v2_0/bgp/dragentscheduler.py b/neutronclient/neutron/v2_0/bgp/dragentscheduler.py index 81ce8f1ec..d1e1de63f 100644 --- a/neutronclient/neutron/v2_0/bgp/dragentscheduler.py +++ b/neutronclient/neutron/v2_0/bgp/dragentscheduler.py @@ -14,8 +14,6 @@ # under the License. # -from __future__ import print_function - from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronV20 from neutronclient.neutron.v2_0.bgp import speaker as bgp_speaker diff --git a/neutronclient/neutron/v2_0/bgp/speaker.py b/neutronclient/neutron/v2_0/bgp/speaker.py index 1a70734e5..c6533e2d9 100644 --- a/neutronclient/neutron/v2_0/bgp/speaker.py +++ b/neutronclient/neutron/v2_0/bgp/speaker.py @@ -14,8 +14,6 @@ # under the License. # -from __future__ import print_function - from neutronclient._i18n import _ from neutronclient.common import utils from neutronclient.common import validators diff --git a/neutronclient/neutron/v2_0/flavor/flavor.py b/neutronclient/neutron/v2_0/flavor/flavor.py index 3058da656..36a052ef2 100644 --- a/neutronclient/neutron/v2_0/flavor/flavor.py +++ b/neutronclient/neutron/v2_0/flavor/flavor.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -from __future__ import print_function - import argparse from neutronclient._i18n import _ diff --git a/neutronclient/neutron/v2_0/floatingip.py b/neutronclient/neutron/v2_0/floatingip.py index 9f0ce089b..5f4ced40a 100644 --- a/neutronclient/neutron/v2_0/floatingip.py +++ b/neutronclient/neutron/v2_0/floatingip.py @@ -14,8 +14,6 @@ # under the License. # -from __future__ import print_function - import argparse from neutronclient._i18n import _ diff --git a/neutronclient/neutron/v2_0/fw/firewallpolicy.py b/neutronclient/neutron/v2_0/fw/firewallpolicy.py index 0c99948eb..3d9331cd6 100644 --- a/neutronclient/neutron/v2_0/fw/firewallpolicy.py +++ b/neutronclient/neutron/v2_0/fw/firewallpolicy.py @@ -14,8 +14,6 @@ # under the License. # -from __future__ import print_function - import argparse from neutronclient._i18n import _ diff --git a/neutronclient/neutron/v2_0/lb/healthmonitor.py b/neutronclient/neutron/v2_0/lb/healthmonitor.py index 61cd7df48..33dfbf0bc 100644 --- a/neutronclient/neutron/v2_0/lb/healthmonitor.py +++ b/neutronclient/neutron/v2_0/lb/healthmonitor.py @@ -14,8 +14,6 @@ # under the License. # -from __future__ import print_function - from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronV20 diff --git a/neutronclient/neutron/v2_0/quota.py b/neutronclient/neutron/v2_0/quota.py index 1a59ac546..a55e29f6d 100644 --- a/neutronclient/neutron/v2_0/quota.py +++ b/neutronclient/neutron/v2_0/quota.py @@ -14,8 +14,6 @@ # under the License. # -from __future__ import print_function - import abc import argparse diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index ca1c05828..1f78cb85c 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -14,8 +14,6 @@ # under the License. # -from __future__ import print_function - import argparse from oslo_serialization import jsonutils diff --git a/neutronclient/osc/v2/fwaas/firewallpolicy.py b/neutronclient/osc/v2/fwaas/firewallpolicy.py index bd3e60d40..cc6beb54d 100644 --- a/neutronclient/osc/v2/fwaas/firewallpolicy.py +++ b/neutronclient/osc/v2/fwaas/firewallpolicy.py @@ -14,8 +14,6 @@ # under the License. # -from __future__ import print_function - import logging from osc_lib.command import command diff --git a/neutronclient/shell.py b/neutronclient/shell.py index fbc91209f..aed4c8fde 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -18,8 +18,6 @@ Command-line interface to the Neutron APIs """ -from __future__ import print_function - import argparse import inspect import itertools From f751dd3782d0d674d8e717e7be6d8bc99518b237 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Beraud?= Date: Tue, 9 Jun 2020 11:58:23 +0200 Subject: [PATCH 755/845] Use unittest.mock instead of mock The mock third party library was needed for mock support in py2 runtimes. Since we now only support py36 and later, we can use the standard lib unittest.mock module instead. Change-Id: Ia7226d968b8594677fc8dea664b58a80ba193ac5 --- neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py b/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py index dd6799870..1e16c1fa9 100644 --- a/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py +++ b/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py @@ -16,8 +16,8 @@ import argparse import copy from unittest import mock +from unittest.mock import call -from mock import call from osc_lib.cli import format_columns from osc_lib import exceptions from osc_lib.tests import utils as tests_utils From ada4229691fb0f6d1b41d2f49633e4f9f5b9ea0b Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 9 Jul 2020 11:41:37 +0100 Subject: [PATCH 756/845] lower-constraints: Drop os-testr, mox3 They're no longer used. Change-Id: Ic8b68f900c2d42a3f5e1508ddde2821cb12b696e Signed-off-by: Stephen Finucane --- lower-constraints.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/lower-constraints.txt b/lower-constraints.txt index 8f576fed0..252a45abc 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -40,7 +40,6 @@ linecache2==1.0.0 MarkupSafe==1.0 mccabe==0.2.1 monotonic==0.6 -mox3==0.20.0 msgpack-python==0.4.0 munch==2.1.0 netaddr==0.7.18 @@ -48,7 +47,6 @@ netifaces==0.10.4 openstacksdk==0.11.2 os-client-config==1.28.0 os-service-types==1.2.0 -os-testr==1.0.0 osc-lib==1.8.0 oslo.concurrency==3.25.0 oslo.config==5.2.0 From d1e5afb3510ef0196b40f8ac632987f44e6c4956 Mon Sep 17 00:00:00 2001 From: Jens Harbott Date: Sun, 12 Jul 2020 21:14:40 +0000 Subject: [PATCH 757/845] Fix description of bgp speaker set arg The name argument for bgp speaker set defines the new name for the speaker, make the description more clear about this. Change-Id: I29a0c87214c3c60084d0541f8b9a7abc72ff987f --- neutronclient/osc/v2/dynamic_routing/bgp_speaker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/osc/v2/dynamic_routing/bgp_speaker.py b/neutronclient/osc/v2/dynamic_routing/bgp_speaker.py index 5ce8d471e..171d7f45f 100644 --- a/neutronclient/osc/v2/dynamic_routing/bgp_speaker.py +++ b/neutronclient/osc/v2/dynamic_routing/bgp_speaker.py @@ -285,7 +285,7 @@ def get_parser(self, prog_name): ) parser.add_argument( '--name', - help=_("Name of the BGP speaker to update")) + help=_("New name for the BGP speaker")) add_common_arguments(parser) return parser From 097106d8fb557e85a93c696a8d64579034708f39 Mon Sep 17 00:00:00 2001 From: Luigi Toscano Date: Mon, 10 Aug 2020 16:25:36 +0200 Subject: [PATCH 758/845] zuul: native Zuul v3 version of the grenade job Also fix the name according the guidelines. This job should really live inside neutron-lib. Change-Id: I76043ea43efe1c78ba9637876ed6a8b997b6d0ee --- .zuul.yaml | 27 +++++----- .../grenade-dsvm-neutron-libs/post.yaml | 15 ------ .../legacy/grenade-dsvm-neutron-libs/run.yaml | 52 ------------------- 3 files changed, 13 insertions(+), 81 deletions(-) delete mode 100644 playbooks/legacy/grenade-dsvm-neutron-libs/post.yaml delete mode 100644 playbooks/legacy/grenade-dsvm-neutron-libs/run.yaml diff --git a/.zuul.yaml b/.zuul.yaml index e956eba72..7464cb6c6 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -16,7 +16,7 @@ - neutronclient-functional experimental: jobs: - - neutron-lib-grenade-dsvm: + - neutronclient-grenade-neutron-lib: irrelevant-files: - ^(test-|)requirements.txt$ - ^setup.cfg$ @@ -45,15 +45,19 @@ neutron-vpnaas: https://opendev.org/openstack/neutron-vpnaas - job: - name: neutron-lib-grenade-dsvm - # Old name: legacy-grenade-dsvm-neutron-libs - parent: legacy-dsvm-base - run: playbooks/legacy/grenade-dsvm-neutron-libs/run.yaml - post-run: playbooks/legacy/grenade-dsvm-neutron-libs/post.yaml - timeout: 10800 + name: neutronclient-grenade-neutron-lib + parent: grenade + description: | + neutron-lib grenade job. + The version of this job on the current branch is py3 based, + while any branch before ussuri needs to use the py2 version, + which is defined in openstack-zuul-jobs with the old name + (legacy-grenade-dsvm-neutron-libs). + Users of this job needs to pay attention of the version used. + Former names for this job were: + * legacy-grenade-dsvm-neutron-libs + * neutron-lib-grenade-dsvm required-projects: - - openstack/grenade - - openstack/devstack-gate - openstack/keystoneauth - openstack/neutron - openstack/neutron-lib @@ -64,8 +68,3 @@ - openstack/python-keystoneclient - openstack/python-neutronclient - openstack/python-novaclient - # This is py3 version for ussuri onwards rest all branch needs to be py2 - # version which is present in openstack-zuul-jobs. - # We need to take care of this branch variant and python version while - # migrating these jobs to zuulv3. - branches: ^(?!(stable/(ocata|pike|queens|rocky|stein|train))).*$ diff --git a/playbooks/legacy/grenade-dsvm-neutron-libs/post.yaml b/playbooks/legacy/grenade-dsvm-neutron-libs/post.yaml deleted file mode 100644 index e07f5510a..000000000 --- a/playbooks/legacy/grenade-dsvm-neutron-libs/post.yaml +++ /dev/null @@ -1,15 +0,0 @@ -- hosts: primary - tasks: - - - name: Copy files from {{ ansible_user_dir }}/workspace/ on node - synchronize: - src: '{{ ansible_user_dir }}/workspace/' - dest: '{{ zuul.executor.log_root }}' - mode: pull - copy_links: true - verify_host: true - rsync_opts: - - --include=/logs/** - - --include=*/ - - --exclude=* - - --prune-empty-dirs diff --git a/playbooks/legacy/grenade-dsvm-neutron-libs/run.yaml b/playbooks/legacy/grenade-dsvm-neutron-libs/run.yaml deleted file mode 100644 index 6c600610a..000000000 --- a/playbooks/legacy/grenade-dsvm-neutron-libs/run.yaml +++ /dev/null @@ -1,52 +0,0 @@ -- hosts: all - name: Autoconverted job legacy-grenade-dsvm-neutron-libs from old job gate-grenade-dsvm-neutron-libs-ubuntu-xenial-nv - tasks: - - - name: Ensure legacy workspace directory - file: - path: '{{ ansible_user_dir }}/workspace' - state: directory - - - shell: - cmd: | - set -e - set -x - cat > clonemap.yaml << EOF - clonemap: - - name: openstack/devstack-gate - dest: devstack-gate - EOF - /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \ - https://opendev.org \ - openstack/devstack-gate - executable: /bin/bash - chdir: '{{ ansible_user_dir }}/workspace' - environment: '{{ zuul | zuul_legacy_vars }}' - - - shell: - cmd: | - set -e - set -x - export PROJECTS="openstack/grenade $PROJECTS" - export DEVSTACK_PROJECT_FROM_GIT="os-client-config" - export DEVSTACK_PROJECT_FROM_GIT+=",keystoneauth" - export DEVSTACK_PROJECT_FROM_GIT+=",python-novaclient" - export DEVSTACK_PROJECT_FROM_GIT+=",python-keystoneclient" - export DEVSTACK_PROJECT_FROM_GIT+=",python-glanceclient" - export DEVSTACK_PROJECT_FROM_GIT+=",python-cinderclient" - export DEVSTACK_PROJECT_FROM_GIT+=",python-neutronclient" - export DEVSTACK_PROJECT_FROM_GIT+=",python-ironicclient" - export PYTHONUNBUFFERED=true - export DEVSTACK_GATE_TEMPEST=1 - export DEVSTACK_GATE_GRENADE=pullup - export DEVSTACK_GATE_USE_PYTHON3=True - export DEVSTACK_GATE_NEUTRON=1 - export BRANCH_OVERRIDE=default - if [ "$BRANCH_OVERRIDE" != "default" ] ; then - export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE - fi - cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh - ./safe-devstack-vm-gate-wrap.sh - executable: /bin/bash - chdir: '{{ ansible_user_dir }}/workspace' - environment: '{{ zuul | zuul_legacy_vars }}' From ffc9ed82621fc8cc4b820a8a7e87172a6905bf91 Mon Sep 17 00:00:00 2001 From: melissaml Date: Tue, 23 Jun 2020 11:28:53 +0800 Subject: [PATCH 759/845] Remove translation sections from setup.cfg These translation sections are not needed anymore, Babel can generate translation files without them. Change-Id: If20aa86f21807497c5dfefb0f34f771040517c6f --- babel.cfg | 2 -- setup.cfg | 14 -------------- 2 files changed, 16 deletions(-) delete mode 100644 babel.cfg diff --git a/babel.cfg b/babel.cfg deleted file mode 100644 index 15cd6cb76..000000000 --- a/babel.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[python: **.py] - diff --git a/setup.cfg b/setup.cfg index 70c0d62b9..ad537f71e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -455,17 +455,3 @@ neutron.cli.v2 = vpn-ikepolicy-create = neutronclient.neutron.v2_0.vpn.ikepolicy:CreateIKEPolicy vpn-ikepolicy-update = neutronclient.neutron.v2_0.vpn.ikepolicy:UpdateIKEPolicy vpn-ikepolicy-delete = neutronclient.neutron.v2_0.vpn.ikepolicy:DeleteIKEPolicy - -[extract_messages] -keywords = _ gettext ngettext l_ lazy_gettext -mapping_file = babel.cfg -output_file = neutronclient/locale/neutronclient.pot - -[compile_catalog] -directory = neutronclient/locale -domain = neutronclient - -[update_catalog] -domain = neutronclient -output_dir = neutronclient/locale -input_file = neutronclient/locale/neutronclient.pot From 9ffa0ac14e3e49c572b4f984620a0e16c15918d9 Mon Sep 17 00:00:00 2001 From: Jens Harbott Date: Tue, 1 Sep 2020 13:51:11 +0200 Subject: [PATCH 760/845] Allow 4byte ASNs in dynamic routing client Neutron-dynamic-routing has the bgp_4byte_asn extension which allows 4byte ASNs to be used, increasing the range of valid AS numbers. Since we cannot easily tell beforehand whether that extension is available, allow for the maximal interval to be used by clients and let the API validate the input. Change-Id: Ib4695272784b4a5ebbcb792cfec82dac3ef6f3cf --- neutronclient/neutron/v2_0/bgp/speaker.py | 2 +- neutronclient/osc/v2/dynamic_routing/constants.py | 2 +- neutronclient/tests/unit/bgp/test_cli20_peer.py | 5 +++-- neutronclient/tests/unit/bgp/test_cli20_speaker.py | 5 +++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/neutronclient/neutron/v2_0/bgp/speaker.py b/neutronclient/neutron/v2_0/bgp/speaker.py index c6533e2d9..6bdbd86f2 100644 --- a/neutronclient/neutron/v2_0/bgp/speaker.py +++ b/neutronclient/neutron/v2_0/bgp/speaker.py @@ -22,7 +22,7 @@ # Allowed BGP Autonomous number range MIN_AS_NUM = 1 -MAX_AS_NUM = 65535 +MAX_AS_NUM = 4294967295 def get_network_id(client, id_or_name): diff --git a/neutronclient/osc/v2/dynamic_routing/constants.py b/neutronclient/osc/v2/dynamic_routing/constants.py index 0dd16b143..8885b42ed 100644 --- a/neutronclient/osc/v2/dynamic_routing/constants.py +++ b/neutronclient/osc/v2/dynamic_routing/constants.py @@ -15,4 +15,4 @@ BGP_PEERS = 'bgp_peers' BGP_PEER = 'bgp_peer' MIN_AS_NUM = 1 -MAX_AS_NUM = 65535 +MAX_AS_NUM = 4294967295 diff --git a/neutronclient/tests/unit/bgp/test_cli20_peer.py b/neutronclient/tests/unit/bgp/test_cli20_peer.py index c89c21b6c..998b0d5e3 100644 --- a/neutronclient/tests/unit/bgp/test_cli20_peer.py +++ b/neutronclient/tests/unit/bgp/test_cli20_peer.py @@ -96,7 +96,7 @@ def test_create_bgp_peer_with_invalid_max_remote_asnum(self): name = 'my-name' my_id = 'my-id' peerip = '1.1.1.1' - remote_asnum = '65536' + remote_asnum = '4294967296' args = [name, '--peer-ip', peerip, '--remote-as', remote_asnum, ] @@ -107,7 +107,8 @@ def test_create_bgp_peer_with_invalid_max_remote_asnum(self): self._test_create_resource, resource, cmd, name, my_id, args, position_names, position_values) - self.assertEqual('remote-as "65536" should be an integer [%s:%s].' % + self.assertEqual('remote-as "4294967296" should be an ' + 'integer [%s:%s].' % (bgp_speaker.MIN_AS_NUM, bgp_speaker.MAX_AS_NUM), str(exc)) diff --git a/neutronclient/tests/unit/bgp/test_cli20_speaker.py b/neutronclient/tests/unit/bgp/test_cli20_speaker.py index 0d9cc60db..5621576d9 100644 --- a/neutronclient/tests/unit/bgp/test_cli20_speaker.py +++ b/neutronclient/tests/unit/bgp/test_cli20_speaker.py @@ -108,7 +108,7 @@ def test_create_bgp_speaker_with_invalid_max_local_asnum(self): None) name = 'my-name' my_id = 'my-id' - local_asnum = '65536' + local_asnum = '4294967296' args = [name, '--local-as', local_asnum] position_names = ['name', 'local_as', ] @@ -117,7 +117,8 @@ def test_create_bgp_speaker_with_invalid_max_local_asnum(self): self._test_create_resource, resource, cmd, name, my_id, args, position_names, position_values) - self.assertEqual('local-as "65536" should be an integer [%s:%s].' % + self.assertEqual('local-as "4294967296" should be an ' + 'integer [%s:%s].' % (bgp_speaker.MIN_AS_NUM, bgp_speaker.MAX_AS_NUM), str(exc)) From 66462874d29e48ec370d400db374c68d153bf94b Mon Sep 17 00:00:00 2001 From: Bernard Cafarelli Date: Thu, 10 Sep 2020 09:41:03 +0200 Subject: [PATCH 761/845] Fix lower-constraints for Focal Bump to versions supporting python 3.8 and with wheels available and sync requirements Change-Id: Ibcd16c9a9f691775af428ec447f4ebf9baea5e86 --- lower-constraints.txt | 10 +++++----- requirements.txt | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lower-constraints.txt b/lower-constraints.txt index 252a45abc..001f03c83 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -4,12 +4,12 @@ appdirs==1.3.0 asn1crypto==0.23.0 Babel==2.3.4 cachetools==2.0.0 -cffi==1.7.0 -cliff==2.8.0 +cffi==1.14.0 +cliff==3.4.0 cmd2==0.8.0 contextlib2==0.4.0 coverage==4.0 -cryptography==2.1 +cryptography==2.7 debtcollector==1.2.0 decorator==3.4.0 deprecation==1.0 @@ -47,7 +47,7 @@ netifaces==0.10.4 openstacksdk==0.11.2 os-client-config==1.28.0 os-service-types==1.2.0 -osc-lib==1.8.0 +osc-lib==1.12.0 oslo.concurrency==3.25.0 oslo.config==5.2.0 oslo.context==2.19.2 @@ -86,7 +86,7 @@ python-novaclient==9.1.0 python-openstackclient==3.12.0 python-subunit==1.0.0 pytz==2013.6 -PyYAML==3.12 +PyYAML==3.13 repoze.lru==0.7 requests-mock==1.2.0 requests==2.14.2 diff --git a/requirements.txt b/requirements.txt index c1c9c28e3..d10083c26 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,11 +2,11 @@ # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. pbr!=2.1.0,>=2.0.0 # Apache-2.0 -cliff!=2.9.0,>=2.8.0 # Apache-2.0 +cliff>=3.4.0 # Apache-2.0 debtcollector>=1.2.0 # Apache-2.0 iso8601>=0.1.11 # MIT netaddr>=0.7.18 # BSD -osc-lib>=1.8.0 # Apache-2.0 +osc-lib>=1.12.0 # Apache-2.0 oslo.i18n>=3.15.3 # Apache-2.0 oslo.log>=3.36.0 # Apache-2.0 oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0 From 13d2d130dd9d1c52d49d1a75916cb4587eba1152 Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Wed, 9 Sep 2020 16:36:40 +0000 Subject: [PATCH 762/845] Update master for stable/victoria Add file to the reno documentation build to show release notes for stable/victoria. Use pbr instruction to increment the minor version number automatically so that master versions are higher than the versions on stable/victoria. Change-Id: I09f54e58239b6c2e77f69bff07bc7fe72ce4690d Sem-Ver: feature --- releasenotes/source/index.rst | 1 + releasenotes/source/victoria.rst | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 releasenotes/source/victoria.rst diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index ac3d918a3..6dfea69f6 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -6,6 +6,7 @@ :maxdepth: 1 unreleased + victoria ussuri train stein diff --git a/releasenotes/source/victoria.rst b/releasenotes/source/victoria.rst new file mode 100644 index 000000000..4efc7b6f3 --- /dev/null +++ b/releasenotes/source/victoria.rst @@ -0,0 +1,6 @@ +============================= +Victoria Series Release Notes +============================= + +.. release-notes:: + :branch: stable/victoria From b2ebc6cc9b7ec8d1d3293535fac039cdaa266ef1 Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Wed, 9 Sep 2020 16:36:43 +0000 Subject: [PATCH 763/845] Add Python3 wallaby unit tests This is an automatically generated patch to ensure unit testing is in place for all the of the tested runtimes for wallaby. See also the PTI in governance [1]. [1]: https://governance.openstack.org/tc/reference/project-testing-interface.html Change-Id: I26b3f45c78c1230a8b7d7faa46b504ac7cd0cc4b --- .zuul.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.zuul.yaml b/.zuul.yaml index 7464cb6c6..c30c7b74d 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -2,7 +2,7 @@ templates: - openstack-cover-jobs - openstack-lower-constraints-jobs - - openstack-python3-victoria-jobs + - openstack-python3-wallaby-jobs - publish-openstack-docs-pti - check-requirements - lib-forward-testing-python3 From 49c8577d8bd1a1a11c8062708478ac69bdcab388 Mon Sep 17 00:00:00 2001 From: likui Date: Mon, 2 Nov 2020 16:01:50 +0800 Subject: [PATCH 764/845] Update requirements URLs in tox config Update the URL to the upper-constraints file to point to the redirect rule on releases.openstack.org so will switch to the correct upper-constraints list automatically when the requirements repository branches. Change-Id: I496dfbc709ecd864f619618252ef5dc992216ca7 --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index c4de1cd1e..7c11bfd8d 100644 --- a/tox.ini +++ b/tox.ini @@ -13,7 +13,7 @@ setenv = VIRTUAL_ENV={envdir} PYTHONWARNINGS=default::DeprecationWarning usedevelop = True install_command = pip install {opts} {packages} -deps = -c{env:UPPER_CONSTRAINTS_FILE:https://opendev.org/openstack/requirements/raw/branch/master/upper-constraints.txt} +deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt # Delete bytecodes from normal directories before running tests. From 1a1ee061e8d6d3a9c825819ed426e4f1b2eba80e Mon Sep 17 00:00:00 2001 From: "wu.shiming" Date: Fri, 20 Nov 2020 14:33:23 +0800 Subject: [PATCH 765/845] Dep's should be restricted by tox-constraints Tox trying to install latest versions for building docs which may not be supported by stable and lower branches, so should be restricted by respective version's tox-constraints.txt Change-Id: Iecd29788d1c94fd727072aa5dd7fe5d828dbf174 --- tox.ini | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 7c11bfd8d..a5d8ab120 100644 --- a/tox.ini +++ b/tox.ini @@ -51,7 +51,9 @@ commands = coverage report [testenv:docs] -deps = -r{toxinidir}/doc/requirements.txt +deps = + -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} + -r{toxinidir}/doc/requirements.txt commands = sphinx-build -W -b html doc/source doc/build/html [testenv:pdf-docs] @@ -64,7 +66,9 @@ commands = make -C doc/build/pdf [testenv:releasenotes] -deps = -r{toxinidir}/doc/requirements.txt +deps = + -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} + -r{toxinidir}/doc/requirements.txt commands = sphinx-build -a -E -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html [flake8] From 4963c7ae14743d2542c8334e3175b7827750bb3d Mon Sep 17 00:00:00 2001 From: zhangboye Date: Sun, 3 Jan 2021 16:57:16 +0800 Subject: [PATCH 766/845] remove unicode from code Change-Id: I2521c2ba836b6a332883134112b6f99d996cc4e4 --- doc/source/conf.py | 6 +++--- releasenotes/source/conf.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index e3e269e25..70fd0de2f 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -28,7 +28,7 @@ master_doc = 'index' # General information about the project. -copyright = u'OpenStack Foundation' +copyright = 'OpenStack Foundation' # If true, '()' will be appended to :func: etc. cross-reference text. add_function_parentheses = True @@ -53,8 +53,8 @@ latex_documents = [ ('index', 'doc-python-neutronclient.tex', - u'python-neutronclient Documentation', - u'Neutron Contributors', 'manual'), + 'python-neutronclient Documentation', + 'Neutron Contributors', 'manual'), ] # Disable usage of xindy https://bugzilla.redhat.com/show_bug.cgi?id=1643664 diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py index 5b075fe21..498287605 100644 --- a/releasenotes/source/conf.py +++ b/releasenotes/source/conf.py @@ -61,8 +61,8 @@ master_doc = 'index' # General information about the project. -project = u'Neutron Client Release Notes' -copyright = u'2015, Neutron Developers' +project = 'Neutron Client Release Notes' +copyright = '2015, Neutron Developers' # Release notes are version independent. # The full version, including alpha/beta/rc tags. From 3140fe014a181185cef674d78dcdb169bb94783b Mon Sep 17 00:00:00 2001 From: sri harsha mekala Date: Fri, 5 Mar 2021 11:47:09 -0800 Subject: [PATCH 767/845] Support passing mTLS certificate/key to HTTPClient Change-Id: I00065293daf6b2e5d540e056c05f85b82f8bc72c --- neutronclient/client.py | 10 +++++++--- neutronclient/v2_0/client.py | 4 ++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/neutronclient/client.py b/neutronclient/client.py index 936d297e2..e25f575f3 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -61,9 +61,9 @@ def __init__(self, username=None, user_id=None, token=None, region_name=None, timeout=None, endpoint_url=None, insecure=False, endpoint_type='publicURL', - auth_strategy='keystone', ca_cert=None, log_credentials=False, - service_type='network', global_request_id=None, - **kwargs): + auth_strategy='keystone', ca_cert=None, cert=None, + log_credentials=False, service_type='network', + global_request_id=None, **kwargs): self.username = username self.user_id = user_id @@ -82,6 +82,7 @@ def __init__(self, username=None, user_id=None, self.auth_strategy = auth_strategy self.log_credentials = log_credentials self.global_request_id = global_request_id + self.cert = cert if insecure: self.verify_cert = False else: @@ -167,6 +168,7 @@ def request(self, url, method, body=None, headers=None, **kwargs): data=body, headers=headers, verify=self.verify_cert, + cert=self.cert, timeout=self.timeout, **kwargs) @@ -399,6 +401,7 @@ def construct_http_client(username=None, log_credentials=None, auth_strategy='keystone', ca_cert=None, + cert=None, service_type='network', session=None, global_request_id=None, @@ -430,6 +433,7 @@ def construct_http_client(username=None, endpoint_type=endpoint_type, service_type=service_type, ca_cert=ca_cert, + cert=cert, log_credentials=log_credentials, auth_strategy=auth_strategy, global_request_id=global_request_id) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index ff440647c..c264dfd14 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -216,6 +216,10 @@ class ClientBase(object): :param bool log_credentials: Allow for logging of passwords or not. Defaults to False. (optional) :param string ca_cert: SSL CA bundle file to use. (optional) + :param cert: A client certificate to pass to requests. These are of the + same form as requests expects. Either a single filename + containing both the certificate and key or a tuple containing + the path to the certificate then a path to the key. (optional) :param integer retries: How many times idempotent (GET, PUT, DELETE) requests to Neutron server should be retried if they fail (default: 0). From 12373768dadef3bad3eb1a32ff51a355e65d17a0 Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Thu, 18 Mar 2021 12:56:13 +0000 Subject: [PATCH 768/845] Update master for stable/wallaby Add file to the reno documentation build to show release notes for stable/wallaby. Use pbr instruction to increment the minor version number automatically so that master versions are higher than the versions on stable/wallaby. Sem-Ver: feature Change-Id: I91d4355bb94ac18a80ff215796cd403be870986a --- releasenotes/source/index.rst | 1 + releasenotes/source/wallaby.rst | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 releasenotes/source/wallaby.rst diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index 6dfea69f6..bfa147dd9 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -6,6 +6,7 @@ :maxdepth: 1 unreleased + wallaby victoria ussuri train diff --git a/releasenotes/source/wallaby.rst b/releasenotes/source/wallaby.rst new file mode 100644 index 000000000..d77b56599 --- /dev/null +++ b/releasenotes/source/wallaby.rst @@ -0,0 +1,6 @@ +============================ +Wallaby Series Release Notes +============================ + +.. release-notes:: + :branch: stable/wallaby From f3756a3d4a816e5096aa2355fc1338effbb13d13 Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Thu, 18 Mar 2021 12:56:37 +0000 Subject: [PATCH 769/845] Add Python3 xena unit tests This is an automatically generated patch to ensure unit testing is in place for all the of the tested runtimes for xena. See also the PTI in governance [1]. [1]: https://governance.openstack.org/tc/reference/project-testing-interface.html Change-Id: I8475c835838bbd0d84540cef879b4769581ef06d --- .zuul.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.zuul.yaml b/.zuul.yaml index c30c7b74d..061a6001e 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -2,7 +2,7 @@ templates: - openstack-cover-jobs - openstack-lower-constraints-jobs - - openstack-python3-wallaby-jobs + - openstack-python3-xena-jobs - publish-openstack-docs-pti - check-requirements - lib-forward-testing-python3 From d87683b59b8f755890e71fb8a73f537215beb60b Mon Sep 17 00:00:00 2001 From: Elod Illes Date: Fri, 19 Mar 2021 11:28:17 +0100 Subject: [PATCH 770/845] Fix lower constraints This patch updates lower-constraints.txt to eliminate contradictions. keystoneauth1 lower constraint has to be bumped in requirements.txt as another dependency, openstacksdk 0.15.0 depends on keystoneauth1>=3.8.0 dogpile.cache version bumped to 0.6.5 as older version had 'async' as variable name which gives SyntaxError from py37. Change-Id: I31464518cb7f76c0fa5275a4ef52b2a6b634cfa3 --- lower-constraints.txt | 22 +++++++++++----------- requirements.txt | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lower-constraints.txt b/lower-constraints.txt index 001f03c83..44c70fafc 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -14,18 +14,18 @@ debtcollector==1.2.0 decorator==3.4.0 deprecation==1.0 docutils==0.11 -dogpile.cache==0.6.2 +dogpile.cache==0.6.5 dulwich==0.15.0 eventlet==0.18.2 extras==1.0.0 fasteners==0.7.0 fixtures==3.0.0 flake8-import-order==0.12 -flake8==2.5.5 +flake8==3.6.0 future==0.16.0 futurist==1.2.0 greenlet==0.4.10 -hacking==1.1.0 +hacking==3.0.1 idna==2.6 imagesize==0.7.1 iso8601==0.1.11 @@ -34,21 +34,21 @@ jmespath==0.9.0 jsonpatch==1.16 jsonpointer==1.13 jsonschema==2.6.0 -keystoneauth1==3.4.0 +keystoneauth1==3.8.0 kombu==4.0.0 linecache2==1.0.0 MarkupSafe==1.0 -mccabe==0.2.1 +mccabe==0.6.0 monotonic==0.6 msgpack-python==0.4.0 munch==2.1.0 netaddr==0.7.18 netifaces==0.10.4 -openstacksdk==0.11.2 +openstacksdk==0.15.0 os-client-config==1.28.0 os-service-types==1.2.0 osc-lib==1.12.0 -oslo.concurrency==3.25.0 +oslo.concurrency==3.26.0 oslo.config==5.2.0 oslo.context==2.19.2 oslo.i18n==3.15.3 @@ -69,9 +69,9 @@ pika==0.10.0 positional==1.2.1 prettytable==0.7.2 pyasn1==0.1.8 -pycodestyle==2.3.1 +pycodestyle==2.4.0 pycparser==2.18 -pyflakes==0.8.1 +pyflakes==2.0.0 Pygments==2.2.0 pyinotify==0.9.6 pyOpenSSL==17.1.0 @@ -86,7 +86,7 @@ python-novaclient==9.1.0 python-openstackclient==3.12.0 python-subunit==1.0.0 pytz==2013.6 -PyYAML==3.13 +PyYAML==5.3.1 repoze.lru==0.7 requests-mock==1.2.0 requests==2.14.2 @@ -97,7 +97,7 @@ simplejson==3.5.1 snowballstemmer==1.2.1 statsd==3.2.1 stestr==2.0.0 -stevedore==1.20.0 +stevedore==2.0.1 tempest==17.1.0 tenacity==3.2.1 testscenarios==0.4 diff --git a/requirements.txt b/requirements.txt index d10083c26..b08551ddf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ oslo.log>=3.36.0 # Apache-2.0 oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0 oslo.utils>=3.33.0 # Apache-2.0 os-client-config>=1.28.0 # Apache-2.0 -keystoneauth1>=3.4.0 # Apache-2.0 +keystoneauth1>=3.8.0 # Apache-2.0 # keystoneclient is used only by neutronclient.osc.utils # TODO(amotoki): Drop this after osc.utils has no dependency on keystoneclient python-keystoneclient>=3.8.0 # Apache-2.0 From 2c2a1f4de7b6e3a1b8f3e3ff0fc5b23b5f0f4f7a Mon Sep 17 00:00:00 2001 From: likui Date: Mon, 10 May 2021 18:09:17 +0800 Subject: [PATCH 771/845] Replace getargspec with getfullargspec inspect.getargspec() is deprecated since py3 [1] https://docs.python.org/3/library/inspect.html#inspect.getargspec Change-Id: I2ae460a7a7f01d7346d5e51ec6ccb44c22d0c53e --- neutronclient/tests/unit/test_client_extension.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/tests/unit/test_client_extension.py b/neutronclient/tests/unit/test_client_extension.py index 7137ae7b3..871e7a6cb 100644 --- a/neutronclient/tests/unit/test_client_extension.py +++ b/neutronclient/tests/unit/test_client_extension.py @@ -217,5 +217,5 @@ def test_client_methods_have_parent_id_arg(self): self.client.delete_parents_child, self.client.create_parents_child) for method in methods: - argspec = inspect.getargspec(method) + argspec = inspect.getfullargspec(method) self.assertIn("parent_id", argspec.args) From 10227b681f546ef126b8f4ef2e9488c8a1a9f0c0 Mon Sep 17 00:00:00 2001 From: likui Date: Tue, 11 May 2021 17:43:33 +0800 Subject: [PATCH 772/845] setup.cfg: Replace dashes with underscores Setuptools v54.1.0 introduces a warning that the use of dash-separated options in 'setup.cfg' will not be supported in a future version [1]. Get ahead of the issue by replacing the dashes with underscores. Without this, we see 'UserWarning' messages like the following on new enough versions of setuptools: UserWarning: Usage of dash-separated 'description-file' will not be supported in future versions. Please use the underscore name 'description_file' instead [1] https://github.com/pypa/setuptools/commit/a2e9ae4cb Change-Id: I56804844c7438d7a5bffaaa363b9e3d79850c5c2 --- setup.cfg | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/setup.cfg b/setup.cfg index ad537f71e..9f06b5c21 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,12 +1,12 @@ [metadata] name = python-neutronclient summary = CLI and Client Library for OpenStack Networking -description-file = +description_file = README.rst author = OpenStack Networking Project -author-email = openstack-discuss@lists.openstack.org -home-page = https://docs.openstack.org/python-neutronclient/latest/ -python-requires = >=3.6 +author_email = openstack-discuss@lists.openstack.org +home_page = https://docs.openstack.org/python-neutronclient/latest/ +python_requires = >=3.6 classifier = Environment :: OpenStack Intended Audience :: Developers From 439e290e95735dfa80b7e0fb193e45470128daec Mon Sep 17 00:00:00 2001 From: Slawek Kaplonski Date: Thu, 27 May 2021 14:07:21 +0200 Subject: [PATCH 773/845] Skip metering functional tests when metering extension is not enabled Change-Id: I9e3f41a2be56c1fd5fc3aff9af8e5ff5163087bd --- neutronclient/tests/functional/core/test_readonly_neutron.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/neutronclient/tests/functional/core/test_readonly_neutron.py b/neutronclient/tests/functional/core/test_readonly_neutron.py index 20e1484e5..4fe989711 100644 --- a/neutronclient/tests/functional/core/test_readonly_neutron.py +++ b/neutronclient/tests/functional/core/test_readonly_neutron.py @@ -64,9 +64,13 @@ def test_neutron_floatingip_list(self): self.neutron('floatingip-list') def test_neutron_meter_label_list(self): + if not self.is_extension_enabled('metering'): + self.skipTest('metering is not enabled') self.neutron('meter-label-list') def test_neutron_meter_label_rule_list(self): + if not self.is_extension_enabled('metering'): + self.skipTest('metering is not enabled') self.neutron('meter-label-rule-list') def test_neutron_net_external_list(self): From 76dd26f9fdd35bf922509e587e25a30927517808 Mon Sep 17 00:00:00 2001 From: Slawek Kaplonski Date: Thu, 27 May 2021 12:18:50 +0200 Subject: [PATCH 774/845] Set when deprecated neutron client CLI will be removed [1] was the last missing bit in OSC to have feature parity between OpenStack client and Neutronclient CLI tools. So now as [1] is merged we can really deprecate and specify that in Z cycle we will remove neutronclient CLI. This patch changes deprecation message to reflect that. [1] https://review.opendev.org/c/openstack/python-openstackclient/+/768210 Change-Id: Ia68af29ea15bda7330fda1b18416798a0f4cb7a4 --- doc/source/contributor/transition_to_osc.rst | 6 +++--- neutronclient/shell.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/source/contributor/transition_to_osc.rst b/doc/source/contributor/transition_to_osc.rst index b745d2f0c..334f2e57e 100644 --- a/doc/source/contributor/transition_to_osc.rst +++ b/doc/source/contributor/transition_to_osc.rst @@ -91,17 +91,17 @@ Transition Steps * **Done** `Security Group Rule CRUD `_ -6. **In Progress:** OSC continues enhancing its networking support. +6. **Done** OSC continues enhancing its networking support. At this point and when applicable, enhancements to the ``neutron`` CLI must also be made to the ``openstack`` CLI and possibly the OpenStack Python SDK. Users of the neutron client's command extensions should start their transition to the OSC plugin system. See the developer guide section below for more information on this step. -7. **In Progress:** Deprecate the ``neutron`` CLI. Running the CLI after +7. **Done** Deprecate the ``neutron`` CLI. Running the CLI after it has been `deprecated `_ will issue a warning message: - ``neutron CLI is deprecated and will be removed in the future. Use openstack CLI instead.`` + ``neutron CLI is deprecated and will be removed in the Z cycle. Use openstack CLI instead.`` In addition, no new features will be added to the CLI, though fixes to the CLI will be assessed on a case by case basis. diff --git a/neutronclient/shell.py b/neutronclient/shell.py index aed4c8fde..13cd0fa4e 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -637,7 +637,7 @@ def configure_logging(self): def main(argv=sys.argv[1:]): try: print(_("neutron CLI is deprecated and will be removed " - "in the future. Use openstack CLI instead."), file=sys.stderr) + "in the Z cycle. Use openstack CLI instead."), file=sys.stderr) return NeutronShell(NEUTRON_API_VERSION).run( list(map(encodeutils.safe_decode, argv))) except KeyboardInterrupt: From d6c211c139928fff3e1bcc41f069995892e233fe Mon Sep 17 00:00:00 2001 From: "wu.shiming" Date: Thu, 3 Jun 2021 14:48:16 +0800 Subject: [PATCH 775/845] Changed minversion in tox to 3.18.0 The patch bumps min version of tox to 3.18.0 in order to replace tox's whitelist_externals by allowlist_externals option: https://github.com/tox-dev/tox/blob/master/docs/changelog.rst#v3180-2020-07-23 Change-Id: I83717c5b2661df0d4288699903c38814cefb43d9 --- tox.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tox.ini b/tox.ini index a5d8ab120..8020b358b 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] envlist = py38,pep8 -minversion = 2.3.2 +minversion = 3.18.0 skipsdist = True ignore_basepython_conflict = True @@ -23,7 +23,7 @@ commands = sh -c "find . -type d -name '.?*' -prune -o \ \( -type d -name '__pycache__' -o -type f -name '*.py[co]' \) \ -print0 | xargs -0 rm -rf" stestr run {posargs} -whitelist_externals = sh +allowlist_externals = sh [testenv:pep8] commands = @@ -59,7 +59,7 @@ commands = sphinx-build -W -b html doc/source doc/build/html [testenv:pdf-docs] envdir = {toxworkdir}/docs deps = {[testenv:docs]deps} -whitelist_externals = +allowlist_externals = make commands = sphinx-build -W -b latex doc/source doc/build/pdf From ae397565be6b734484d9e96c66f13de97762303c Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Wed, 9 Jun 2021 14:35:54 +0900 Subject: [PATCH 776/845] Add a release note on deprecation for removal of neutron CLI Change-Id: I866ade23cd76cf1e44e10eeb15dcc02cc8d0dc66 --- .../neutron-cli-deprecation-398823c87270a296.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 releasenotes/notes/neutron-cli-deprecation-398823c87270a296.yaml diff --git a/releasenotes/notes/neutron-cli-deprecation-398823c87270a296.yaml b/releasenotes/notes/neutron-cli-deprecation-398823c87270a296.yaml new file mode 100644 index 000000000..a8065940e --- /dev/null +++ b/releasenotes/notes/neutron-cli-deprecation-398823c87270a296.yaml @@ -0,0 +1,10 @@ +--- +deprecations: + - | + ``neutron`` CLI will be removed in 'Z' release. + While it has been marked as deprecated for removal for long, + all features in ``neutron`` CLI have been supported in ``openstack`` CLI + (OpenStackClient) as of Xena release and the neutron team plans to + remove it in 'Z' release. Consider using ``openstack`` CLI and + `Mapping Guide `__ + in the OSC documentation would help you. From a1ebfaa7f22e71fb59eea05ff11bbfe5bede41ba Mon Sep 17 00:00:00 2001 From: dengzhaosen Date: Thu, 6 May 2021 09:50:35 +0800 Subject: [PATCH 777/845] Ussuri+ is python3 only and update python to python3 To remove the useless shebang Change-Id: I611c5bc47562f8def9a8623117b7b0655474b555 --- neutronclient/tests/unit/test_cli20_floatingips.py | 1 - neutronclient/tests/unit/test_cli20_securitygroup.py | 1 - neutronclient/tests/unit/test_quota.py | 1 - 3 files changed, 3 deletions(-) diff --git a/neutronclient/tests/unit/test_cli20_floatingips.py b/neutronclient/tests/unit/test_cli20_floatingips.py index 9a1c39863..d77bda6e8 100644 --- a/neutronclient/tests/unit/test_cli20_floatingips.py +++ b/neutronclient/tests/unit/test_cli20_floatingips.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # Copyright 2012 Red Hat # All Rights Reserved. # diff --git a/neutronclient/tests/unit/test_cli20_securitygroup.py b/neutronclient/tests/unit/test_cli20_securitygroup.py index f5ed8ad1e..95a578f63 100644 --- a/neutronclient/tests/unit/test_cli20_securitygroup.py +++ b/neutronclient/tests/unit/test_cli20_securitygroup.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # Copyright 2012 Red Hat # All Rights Reserved. # diff --git a/neutronclient/tests/unit/test_quota.py b/neutronclient/tests/unit/test_quota.py index 4529287b0..747621de9 100644 --- a/neutronclient/tests/unit/test_quota.py +++ b/neutronclient/tests/unit/test_quota.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # Copyright (C) 2013 Yahoo! Inc. # All Rights Reserved. # From 23fb666f923e9fa117ce5744e9abe00d3874d3fc Mon Sep 17 00:00:00 2001 From: elajkat Date: Mon, 26 Jul 2021 14:54:15 +0200 Subject: [PATCH 778/845] tests: change safe_hasattr to hasattr testtools 2.5.0 removed the helper (see [0]) safe_hasattr, and assumes that the hasattr implementation in python reliable enough now. test_neutron_dhcp_agent_list_hosting_net is skipped temporarily as there is no DHCP agent with ML2/OVN. It will be re-enabled [1]. [0] https://github.com/testing-cabal/testtools/blob/2.5.0/NEWS#L31 [1] https://review.opendev.org/c/openstack/python-neutronclient/+/801997 Change-Id: I4fe6fabc4f745e2c9a366e30dbea7e7200151f12 --- neutronclient/tests/functional/core/test_readonly_neutron.py | 2 ++ neutronclient/tests/unit/test_command_meta.py | 5 ++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/neutronclient/tests/functional/core/test_readonly_neutron.py b/neutronclient/tests/functional/core/test_readonly_neutron.py index 4fe989711..41ee52448 100644 --- a/neutronclient/tests/functional/core/test_readonly_neutron.py +++ b/neutronclient/tests/functional/core/test_readonly_neutron.py @@ -13,6 +13,7 @@ import re from tempest.lib import exceptions +import testtools from neutronclient.tests.functional import base @@ -51,6 +52,7 @@ def test_neutron_ext_list(self): ext = self.parser.listing(self.neutron('ext-list')) self.assertTableStruct(ext, ['alias', 'name']) + @testtools.skip('Skipped until ML2/OVS is enabled') def test_neutron_dhcp_agent_list_hosting_net(self): self.neutron('dhcp-agent-list-hosting-net', params='private') diff --git a/neutronclient/tests/unit/test_command_meta.py b/neutronclient/tests/unit/test_command_meta.py index 184f36680..dedc3dd1c 100644 --- a/neutronclient/tests/unit/test_command_meta.py +++ b/neutronclient/tests/unit/test_command_meta.py @@ -20,7 +20,6 @@ import logging import testtools -from testtools import helpers from neutronclient.neutron import v2_0 as neutronV20 @@ -30,7 +29,7 @@ def test_neutron_command_meta_defines_log(self): class FakeCommand(neutronV20.NeutronCommand): pass - self.assertTrue(helpers.safe_hasattr(FakeCommand, 'log')) + self.assertTrue(hasattr(FakeCommand, 'log')) self.assertIsInstance(FakeCommand.log, logging.getLoggerClass()) self.assertEqual(__name__ + ".FakeCommand", FakeCommand.log.name) @@ -38,5 +37,5 @@ def test_neutron_command_log_defined_explicitly(self): class FakeCommand(neutronV20.NeutronCommand): log = None - self.assertTrue(helpers.safe_hasattr(FakeCommand, 'log')) + self.assertTrue(hasattr(FakeCommand, 'log')) self.assertIsNone(FakeCommand.log) From 2f047b15957308e84dcb72baee3415b8bf5a470a Mon Sep 17 00:00:00 2001 From: Bernard Cafarelli Date: Fri, 23 Jul 2021 11:23:22 +0200 Subject: [PATCH 779/845] Set ML2/OVS backend explicitly for functional job After default backend change to OVN, some client tests related to DHCP or L3 agents are not working fine. Switch the job to ML2/OVS, as it was done in openstack-client in changeset Idf6466a59c6cf96be2f1d53e696f0564584fa233 test_neutron_dhcp_agent_list_hosting_net was skipped temporarily to pass the gate in the parent commit. It is re-enabled now. Change-Id: I91d03d13adcd38d46ba1b042239a290ae2c99ea4 Closes-Bug: #1936965 --- .zuul.yaml | 22 +++++++++++++++++++ .../functional/core/test_readonly_neutron.py | 2 -- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/.zuul.yaml b/.zuul.yaml index 061a6001e..58f54dfe6 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -38,9 +38,31 @@ # NOTE: neutronclient.tests.functional.base.ClientTestBase does not # support HTTPS endpoints now, so tls-proxy needs to be disabled. tls-proxy: false + # Disable OVN services + br-ex-tcpdump: false + br-int-flows: false + ovn-controller: false + ovn-northd: false + ovs-vswitchd: false + ovsdb-server: false + q-ovn-metadata-agent: false + # Neutron services + q-agt: true + q-dhcp: true + q-l3: true + q-meta: true + neutron-network-segment-range: true + neutron-segments: true + q-metering: true + q-qos: true + neutron-tag-ports-during-bulk-creation: true + neutron-conntrack-helper: true devstack_localrc: USE_PYTHON3: true LIBS_FROM_GIT: python-neutronclient + Q_AGENT: openvswitch + Q_ML2_TENANT_NETWORK_TYPE: vxlan + Q_ML2_PLUGIN_MECHANISM_DRIVERS: openvswitch devstack_plugins: neutron-vpnaas: https://opendev.org/openstack/neutron-vpnaas diff --git a/neutronclient/tests/functional/core/test_readonly_neutron.py b/neutronclient/tests/functional/core/test_readonly_neutron.py index 41ee52448..4fe989711 100644 --- a/neutronclient/tests/functional/core/test_readonly_neutron.py +++ b/neutronclient/tests/functional/core/test_readonly_neutron.py @@ -13,7 +13,6 @@ import re from tempest.lib import exceptions -import testtools from neutronclient.tests.functional import base @@ -52,7 +51,6 @@ def test_neutron_ext_list(self): ext = self.parser.listing(self.neutron('ext-list')) self.assertTableStruct(ext, ['alias', 'name']) - @testtools.skip('Skipped until ML2/OVS is enabled') def test_neutron_dhcp_agent_list_hosting_net(self): self.neutron('dhcp-agent-list-hosting-net', params='private') From f83108d85874a87e47b51af3eebc9faeeb11faa0 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Mon, 19 Jul 2021 11:44:28 +0900 Subject: [PATCH 780/845] Use yaml.safe_load instead of yaml.load Since PyYAML 5.1, yaml.load without specifying the Loader option is deprecated and shows the following warning. YAMLLoadWarning: calling yaml.load() without Loader=... is deprecated, as the default Loader is unsafe. Please read https://msg.pyyaml.org/load for full details. This change replaces usage of yaml.load by yaml.safe_load, to get rid of that warning message. Change-Id: Ibe25c4aaf3aa7226f28ec60b8a929ecc143face1 --- neutronclient/tests/functional/core/test_cli_formatter.py | 2 +- neutronclient/tests/unit/test_cli20.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/neutronclient/tests/functional/core/test_cli_formatter.py b/neutronclient/tests/functional/core/test_cli_formatter.py index cd133dee1..145ed6c67 100644 --- a/neutronclient/tests/functional/core/test_cli_formatter.py +++ b/neutronclient/tests/functional/core/test_cli_formatter.py @@ -43,7 +43,7 @@ def test_net_create_with_yaml_formatter(self): result = self._create_net('yaml', ['name', 'admin_state_up']) self.assertDictEqual({'name': self.net_name, 'admin_state_up': True}, - yaml.load(result)) + yaml.safe_load(result)) def test_net_create_with_value_formatter(self): # NOTE(amotoki): In 'value' formatter, there is no guarantee diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index c2143e168..a067ee893 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -1159,7 +1159,7 @@ def test_create_resource_json(self): def test_create_resource_yaml(self): self._test_create_resource_with_formatter('yaml') - data = yaml.load(self.fake_stdout.make_string()) + data = yaml.safe_load(self.fake_stdout.make_string()) self.assertEqual('myname', data['name']) self.assertEqual('myid', data['id']) @@ -1184,7 +1184,7 @@ def test_show_resource_json(self): def test_show_resource_yaml(self): self._test_show_resource_with_formatter('yaml') - data = yaml.load(''.join(self.fake_stdout.content)) + data = yaml.safe_load(''.join(self.fake_stdout.content)) self.assertEqual('myname', data['name']) self.assertEqual('myid', data['id']) @@ -1211,5 +1211,5 @@ def test_list_resources_json(self): def test_list_resources_yaml(self): self._test_list_resources_with_formatter('yaml') - data = yaml.load(''.join(self.fake_stdout.content)) + data = yaml.safe_load(''.join(self.fake_stdout.content)) self.assertEqual(['myid1', 'myid2'], [d['id'] for d in data]) From cff9c266c05ebfc13f4917e1646e5dedbe371cc2 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Sun, 5 Sep 2021 00:56:38 +0900 Subject: [PATCH 781/845] Replace deprecated assertDictContainsSubset The method is deprecated since Python 3.2[1] and shows the following DeprecationWarning. /usr/lib/python3.9/unittest/case.py:1134: DeprecationWarning: assertDictContainsSubset is deprecated warnings.warn('assertDictContainsSubset is deprecated', [1] https://docs.python.org/3/whatsnew/3.2.html#unittest Closes-Bug: #1938103 Change-Id: I1d0ee6c77476707a7e4fe4fbf2b979bf34550d05 --- neutronclient/tests/unit/test_shell.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/neutronclient/tests/unit/test_shell.py b/neutronclient/tests/unit/test_shell.py index 9dc2faac8..04e814066 100644 --- a/neutronclient/tests/unit/test_shell.py +++ b/neutronclient/tests/unit/test_shell.py @@ -357,10 +357,10 @@ def test_commands_dict_populated(self): self.useFixture(fixtures.MockPatchObject(openstack_shell, 'COMMANDS', None)) openstack_shell.NeutronShell('2.0') - self.assertDictContainsSubset( + self.assertLessEqual( {'net-create': network.CreateNetwork, 'net-delete': network.DeleteNetwork, 'net-list': network.ListNetwork, 'net-show': network.ShowNetwork, - 'net-update': network.UpdateNetwork}, - openstack_shell.COMMANDS['2.0']) + 'net-update': network.UpdateNetwork}.items(), + openstack_shell.COMMANDS['2.0'].items()) From ee73a48881b9a10938d477f297c75ecac48e0935 Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Fri, 10 Sep 2021 14:33:52 +0000 Subject: [PATCH 782/845] Update master for stable/xena Add file to the reno documentation build to show release notes for stable/xena. Use pbr instruction to increment the minor version number automatically so that master versions are higher than the versions on stable/xena. Sem-Ver: feature Change-Id: If1185e64ff9e9c2a622f7a41df3993b3fe414b96 --- releasenotes/source/index.rst | 1 + releasenotes/source/xena.rst | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 releasenotes/source/xena.rst diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index bfa147dd9..0be3cf0ac 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -6,6 +6,7 @@ :maxdepth: 1 unreleased + xena wallaby victoria ussuri diff --git a/releasenotes/source/xena.rst b/releasenotes/source/xena.rst new file mode 100644 index 000000000..1be85be3e --- /dev/null +++ b/releasenotes/source/xena.rst @@ -0,0 +1,6 @@ +========================= +Xena Series Release Notes +========================= + +.. release-notes:: + :branch: stable/xena From 792ad115b3e266e70932946ed5187691edb061fc Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Fri, 10 Sep 2021 14:33:53 +0000 Subject: [PATCH 783/845] Add Python3 yoga unit tests This is an automatically generated patch to ensure unit testing is in place for all the of the tested runtimes for yoga. See also the PTI in governance [1]. [1]: https://governance.openstack.org/tc/reference/project-testing-interface.html Change-Id: Ib343cb56e2512862d5b23482a9861ba7b3ce6373 --- .zuul.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.zuul.yaml b/.zuul.yaml index 58f54dfe6..e16794bcb 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -2,7 +2,7 @@ templates: - openstack-cover-jobs - openstack-lower-constraints-jobs - - openstack-python3-xena-jobs + - openstack-python3-yoga-jobs - publish-openstack-docs-pti - check-requirements - lib-forward-testing-python3 From 1df1f38a91afd158336044a57f7e4c8e11108a94 Mon Sep 17 00:00:00 2001 From: Frode Nordahl Date: Tue, 14 Sep 2021 14:24:58 +0200 Subject: [PATCH 784/845] Add support for 'smart-nic' vnic-type The 'smart-nic' vnic_type was added in the Train time frame in I91f63810626ce4e054e358f5de5e46434c4da131. This vnic_type will also be used to support off-path SmartNIC port binding with OVN, and it is expected that the user will create ports with this vnic_type as part of the workflow. As such the client must allow users to interact with this vnic_type and this patch addresses that. Partial-Bug: #1932154 Change-Id: I7f80bb47db7f8608db4d6a646b0f4b0ef6d6fb48 --- neutronclient/neutron/v2_0/port.py | 6 +++--- neutronclient/tests/unit/test_cli20_port.py | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index 0982b049a..3fe73544b 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -245,15 +245,15 @@ def add_known_arguments(self, parser): parser.add_argument( '--vnic-type', metavar='', + '| normal | baremetal | smart-nic>', choices=['direct', 'direct-physical', 'macvtap', - 'normal', 'baremetal'], + 'normal', 'baremetal', 'smart-nic'], type=utils.convert_to_lowercase, help=_('VNIC type for this port.')) parser.add_argument( '--vnic_type', choices=['direct', 'direct-physical', 'macvtap', - 'normal', 'baremetal'], + 'normal', 'baremetal', 'smart-nic'], type=utils.convert_to_lowercase, help=argparse.SUPPRESS) parser.add_argument( diff --git a/neutronclient/tests/unit/test_cli20_port.py b/neutronclient/tests/unit/test_cli20_port.py index e1ab411e5..ab7e95655 100644 --- a/neutronclient/tests/unit/test_cli20_port.py +++ b/neutronclient/tests/unit/test_cli20_port.py @@ -214,6 +214,26 @@ def test_create_port_vnic_type_baremetal(self): self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) + def test_create_port_vnic_type_smart_nic(self): + # Create port: --vnic_type smart-nic netid. + resource = 'port' + cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + netid = 'netid' + args = ['--vnic_type', 'smart-nic', netid] + position_names = ['binding:vnic_type', 'network_id'] + position_values = ['smart-nic', netid] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + # Test dashed options + args = ['--vnic-type', 'smart-nic', netid] + position_names = ['binding:vnic_type', 'network_id'] + position_values = ['smart-nic', netid] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + def test_create_port_with_binding_profile(self): resource = 'port' cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) From 3a65712b451d3eed93cbf7eef773d46c49df6358 Mon Sep 17 00:00:00 2001 From: elajkat Date: Thu, 18 Nov 2021 15:00:28 +0100 Subject: [PATCH 785/845] Fix lower-constraints and neutronclient-functional job Change decorator in l-c.txt from 3.4.0 to 4.1.0 add neutron as devstack plugin to neutronclient-functional job. Change-Id: Ib4b98f4e9e70f058ba5c7c43f559ab888edf1a88 --- .zuul.yaml | 1 + lower-constraints.txt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.zuul.yaml b/.zuul.yaml index e16794bcb..c82467aee 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -64,6 +64,7 @@ Q_ML2_TENANT_NETWORK_TYPE: vxlan Q_ML2_PLUGIN_MECHANISM_DRIVERS: openvswitch devstack_plugins: + neutron: https://opendev.org/openstack/neutron neutron-vpnaas: https://opendev.org/openstack/neutron-vpnaas - job: diff --git a/lower-constraints.txt b/lower-constraints.txt index 44c70fafc..7a7c54b6f 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -11,7 +11,7 @@ contextlib2==0.4.0 coverage==4.0 cryptography==2.7 debtcollector==1.2.0 -decorator==3.4.0 +decorator==4.1.0 deprecation==1.0 docutils==0.11 dogpile.cache==0.6.5 From 3b80135a3d364abfaf3b3ead03bae3348627d1b2 Mon Sep 17 00:00:00 2001 From: Przemyslaw Szczerbik Date: Fri, 1 Oct 2021 10:01:48 +0200 Subject: [PATCH 786/845] Add support for minimum packet rate rule to the client With the introduction of QoS minimum packet rate rule in Neutron, it's important to ensure that tools like Heat support it as well. Unfortunately, Heat still depends on python-neutronclient instead of python-openstackclient. So even though QoS minimum packet rate rule support have been proposed for python-openstackclient [1] and openstacksdk [2], it's still necessary to extend python-neutronclient code. Since Heat uses only the client part, can skip CLI support. [1] https://review.opendev.org/c/openstack/python-openstackclient/+/810559 [2] https://review.opendev.org/c/openstack/openstacksdk/+/810364 Partial-Bug: #1922237 See-Also: https://review.opendev.org/785236 Change-Id: I4f16b963a202a476cd3cd2b69c1dd4e4ee6f0fc7 --- neutronclient/v2_0/client.py | 34 +++++++++++++++++++ .../minimum-packet-rate-34576b8fd98a3034.yaml | 7 ++++ 2 files changed, 41 insertions(+) create mode 100644 releasenotes/notes/minimum-packet-rate-34576b8fd98a3034.yaml diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index c264dfd14..f318f3ce9 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -625,6 +625,10 @@ class Client(ClientBase): "/qos/policies/%s/minimum_bandwidth_rules" qos_minimum_bandwidth_rule_path = \ "/qos/policies/%s/minimum_bandwidth_rules/%s" + qos_minimum_packet_rate_rules_path = \ + "/qos/policies/%s/minimum_packet_rate_rules" + qos_minimum_packet_rate_rule_path = \ + "/qos/policies/%s/minimum_packet_rate_rules/%s" qos_rule_types_path = "/qos/rule-types" qos_rule_type_path = "/qos/rule-types/%s" flavors_path = "/flavors" @@ -709,6 +713,7 @@ class Client(ClientBase): 'policies': 'policy', 'bandwidth_limit_rules': 'bandwidth_limit_rule', 'minimum_bandwidth_rules': 'minimum_bandwidth_rule', + 'minimum_packet_rate_rules': 'minimum_packet_rate_rule', 'rules': 'rule', 'dscp_marking_rules': 'dscp_marking_rule', 'rule_types': 'rule_type', @@ -1982,6 +1987,35 @@ def delete_minimum_bandwidth_rule(self, rule, policy): return self.delete(self.qos_minimum_bandwidth_rule_path % (policy, rule)) + def list_minimum_packet_rate_rules(self, policy_id, retrieve_all=True, + **_params): + """Fetches a list of all minimum packet rate rules for the given policy + + """ + return self.list('minimum_packet_rate_rules', + self.qos_minimum_packet_rate_rules_path % + policy_id, retrieve_all, **_params) + + def show_minimum_packet_rate_rule(self, rule, policy, body=None): + """Fetches information of a certain minimum packet rate rule.""" + return self.get(self.qos_minimum_packet_rate_rule_path % + (policy, rule), body=body) + + def create_minimum_packet_rate_rule(self, policy, body=None): + """Creates a new minimum packet rate rule.""" + return self.post(self.qos_minimum_packet_rate_rules_path % policy, + body=body) + + def update_minimum_packet_rate_rule(self, rule, policy, body=None): + """Updates a minimum packet rate rule.""" + return self.put(self.qos_minimum_packet_rate_rule_path % + (policy, rule), body=body) + + def delete_minimum_packet_rate_rule(self, rule, policy): + """Deletes a minimum packet rate rule.""" + return self.delete(self.qos_minimum_packet_rate_rule_path % + (policy, rule)) + def create_flavor(self, body=None): """Creates a new Neutron service flavor.""" return self.post(self.flavors_path, body=body) diff --git a/releasenotes/notes/minimum-packet-rate-34576b8fd98a3034.yaml b/releasenotes/notes/minimum-packet-rate-34576b8fd98a3034.yaml new file mode 100644 index 000000000..7ce33f65e --- /dev/null +++ b/releasenotes/notes/minimum-packet-rate-34576b8fd98a3034.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Added new client methods for QoS minimum packet rate rule: + ``list_minimum_packet_rate_rules``, ``show_minimum_packet_rate_rule``, + ``create_minimum_packet_rate_rule``, ``update_minimum_packet_rate_rule``, + ``delete_minimum_packet_rate_rule``. From a92d8db81c7f13e46aa64be471de06054379be75 Mon Sep 17 00:00:00 2001 From: LIU Yulong Date: Mon, 22 Nov 2021 15:01:42 +0800 Subject: [PATCH 787/845] Add CURD actions for packet rate limit rule Neutron added new QoS rule [1] for packet rate limit. Neutron fullstack test cases [2] rely on the neutron client to interact with the neutron-server (API), so for new QoS rule `packet rate limit`, we add the needed methods for new cases of the QoS driver testing. [1] https://docs.openstack.org/api-ref/network/v2/index.html#qos-packet-rate-limit-rules [2] https://github.com/openstack/neutron/blob/master/neutron/tests/fullstack/resources/process.py#L24 Change-Id: I0ad236c9e585a25fbd405813ac48898a2df897d2 --- neutronclient/v2_0/client.py | 34 +++++++++++++++++++ .../paket_rate_limit-1266a2a30f18727f.yaml | 7 ++++ 2 files changed, 41 insertions(+) create mode 100644 releasenotes/notes/paket_rate_limit-1266a2a30f18727f.yaml diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index c264dfd14..b64f3e685 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -619,6 +619,10 @@ class Client(ClientBase): qos_policy_path = "/qos/policies/%s" qos_bandwidth_limit_rules_path = "/qos/policies/%s/bandwidth_limit_rules" qos_bandwidth_limit_rule_path = "/qos/policies/%s/bandwidth_limit_rules/%s" + qos_packet_rate_limit_rules_path = \ + "/qos/policies/%s/packet_rate_limit_rules" + qos_packet_rate_limit_rule_path = \ + "/qos/policies/%s/packet_rate_limit_rules/%s" qos_dscp_marking_rules_path = "/qos/policies/%s/dscp_marking_rules" qos_dscp_marking_rule_path = "/qos/policies/%s/dscp_marking_rules/%s" qos_minimum_bandwidth_rules_path = \ @@ -708,6 +712,7 @@ class Client(ClientBase): 'qos_policies': 'qos_policy', 'policies': 'policy', 'bandwidth_limit_rules': 'bandwidth_limit_rule', + 'packet_rate_limit_rules': 'packet_rate_limit_rule', 'minimum_bandwidth_rules': 'minimum_bandwidth_rule', 'rules': 'rule', 'dscp_marking_rules': 'dscp_marking_rule', @@ -1972,6 +1977,35 @@ def create_minimum_bandwidth_rule(self, policy, body=None): return self.post(self.qos_minimum_bandwidth_rules_path % policy, body=body) + def list_packet_rate_limit_rules(self, policy_id, retrieve_all=True, + **_params): + """Fetches a list of all packet rate limit rules for the given policy + + """ + return self.list('packet_rate_limit_rules', + self.qos_packet_rate_limit_rules_path % + policy_id, retrieve_all, **_params) + + def show_packet_rate_limit_rule(self, rule, policy, body=None): + """Fetches information of a certain packet rate limit rule.""" + return self.get(self.qos_packet_rate_limit_rule_path % + (policy, rule), body=body) + + def create_packet_rate_limit_rule(self, policy, body=None): + """Creates a new packet rate limit rule.""" + return self.post(self.qos_packet_rate_limit_rules_path % policy, + body=body) + + def update_packet_rate_limit_rule(self, rule, policy, body=None): + """Updates a packet rate limit rule.""" + return self.put(self.qos_packet_rate_limit_rule_path % + (policy, rule), body=body) + + def delete_packet_rate_limit_rule(self, rule, policy): + """Deletes a packet rate limit rule.""" + return self.delete(self.qos_packet_rate_limit_rule_path % + (policy, rule)) + def update_minimum_bandwidth_rule(self, rule, policy, body=None): """Updates a minimum bandwidth rule.""" return self.put(self.qos_minimum_bandwidth_rule_path % diff --git a/releasenotes/notes/paket_rate_limit-1266a2a30f18727f.yaml b/releasenotes/notes/paket_rate_limit-1266a2a30f18727f.yaml new file mode 100644 index 000000000..9fdfc1e62 --- /dev/null +++ b/releasenotes/notes/paket_rate_limit-1266a2a30f18727f.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Added new client methods for QoS packet rate limit rule: + ``list_packet_rate_limit_rules``, ``show_packet_rate_limit_rule``, + ``create_packet_rate_limit_rule``, ``update_packet_rate_limit_rule``, + ``delete_packet_rate_limit_rule``. From 517bef2c5454dde2eba5cc2194ee857be6be7164 Mon Sep 17 00:00:00 2001 From: dengzhaosen Date: Tue, 21 Dec 2021 17:33:43 +0800 Subject: [PATCH 788/845] Update python testing classifier Yoga testing runtime[1] has been updated to add py39 testing as voting. Unit tests update are handled by the job template change in openstack-zuul-job - https://review.opendev.org/c/openstack/openstack-zuul-jobs/+/820286 this commit updates the classifier in setup.cfg file. [1] https://governance.openstack.org/tc/reference/runtimes/yoga.html Change-Id: Iedb1c345df99ae9be5cce22ab930c6de7a5e7832 --- setup.cfg | 1 + tox.ini | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 9f06b5c21..a1a60d82d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -21,6 +21,7 @@ classifier = Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 [files] packages = diff --git a/tox.ini b/tox.ini index 8020b358b..8e7aa1673 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py38,pep8 +envlist = py39,pep8 minversion = 3.18.0 skipsdist = True ignore_basepython_conflict = True From 28628e8f962c49aa276647ca2b2e71a2be530277 Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez Date: Thu, 3 Mar 2022 00:24:33 +0000 Subject: [PATCH 789/845] Skip B105 pep8 error: hardcoded passwords Skip B105 pep8 error: * https://bandit.readthedocs.io/en/latest/plugins/b105_hardcoded_password_string.html * https://cwe.mitre.org/data/definitions/259.html Trivial-Fix Change-Id: I8e58da2d88d727018c8d5af5949e34f8c0893c1f --- tox.ini | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 8e7aa1673..9136a0f9e 100644 --- a/tox.ini +++ b/tox.ini @@ -81,8 +81,11 @@ enable-extensions=H904 [testenv:bandit] # B303: blacklist calls: md5, sha1 +# B105: The software contains a hard-coded password, which it uses for its own +# inbound authentication or for outbound communication to external +# components. deps = -r{toxinidir}/test-requirements.txt -commands = bandit -r neutronclient -x tests -n5 -s B303 +commands = bandit -r neutronclient -x tests -n5 -s B303,B105 [testenv:lower-constraints] deps = From 4575bea5a6e80bcbc360f259466a6c4c69364cf0 Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Thu, 3 Mar 2022 10:52:26 +0000 Subject: [PATCH 790/845] Update master for stable/yoga Add file to the reno documentation build to show release notes for stable/yoga. Use pbr instruction to increment the minor version number automatically so that master versions are higher than the versions on stable/yoga. Sem-Ver: feature Change-Id: I3f709510026817ae2f36d646ca3fe9487637b717 --- releasenotes/source/index.rst | 1 + releasenotes/source/yoga.rst | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 releasenotes/source/yoga.rst diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index 0be3cf0ac..2a7aeaa06 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -6,6 +6,7 @@ :maxdepth: 1 unreleased + yoga xena wallaby victoria diff --git a/releasenotes/source/yoga.rst b/releasenotes/source/yoga.rst new file mode 100644 index 000000000..7cd5e908a --- /dev/null +++ b/releasenotes/source/yoga.rst @@ -0,0 +1,6 @@ +========================= +Yoga Series Release Notes +========================= + +.. release-notes:: + :branch: stable/yoga From 143db0177acbd5606c34980b865e81f05413a47e Mon Sep 17 00:00:00 2001 From: Ghanshyam Mann Date: Sat, 30 Apr 2022 20:17:09 -0500 Subject: [PATCH 791/845] Drop lower-constraints.txt and its testing As discussed in TC PTG[1] and TC resolution[2], we are dropping the lower-constraints.txt file and its testing. We will keep lower bounds in the requirements.txt file but with a note that these are not tested lower bounds and we try our best to keep them updated. [1] https://etherpad.opendev.org/p/tc-zed-ptg#L326 [2] https://governance.openstack.org/tc/resolutions/20220414-drop-lower-constraints.html#proposal Change-Id: I9a985c439675dc3aa6120a0d9734b8cdeaedccf8 --- .zuul.yaml | 1 - lower-constraints.txt | 111 ------------------------------------------ requirements.txt | 4 ++ tox.ini | 6 --- 4 files changed, 4 insertions(+), 118 deletions(-) delete mode 100644 lower-constraints.txt diff --git a/.zuul.yaml b/.zuul.yaml index c82467aee..b709db00e 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -1,7 +1,6 @@ - project: templates: - openstack-cover-jobs - - openstack-lower-constraints-jobs - openstack-python3-yoga-jobs - publish-openstack-docs-pti - check-requirements diff --git a/lower-constraints.txt b/lower-constraints.txt deleted file mode 100644 index 7a7c54b6f..000000000 --- a/lower-constraints.txt +++ /dev/null @@ -1,111 +0,0 @@ -alabaster==0.7.10 -amqp==2.1.1 -appdirs==1.3.0 -asn1crypto==0.23.0 -Babel==2.3.4 -cachetools==2.0.0 -cffi==1.14.0 -cliff==3.4.0 -cmd2==0.8.0 -contextlib2==0.4.0 -coverage==4.0 -cryptography==2.7 -debtcollector==1.2.0 -decorator==4.1.0 -deprecation==1.0 -docutils==0.11 -dogpile.cache==0.6.5 -dulwich==0.15.0 -eventlet==0.18.2 -extras==1.0.0 -fasteners==0.7.0 -fixtures==3.0.0 -flake8-import-order==0.12 -flake8==3.6.0 -future==0.16.0 -futurist==1.2.0 -greenlet==0.4.10 -hacking==3.0.1 -idna==2.6 -imagesize==0.7.1 -iso8601==0.1.11 -Jinja2==2.10 -jmespath==0.9.0 -jsonpatch==1.16 -jsonpointer==1.13 -jsonschema==2.6.0 -keystoneauth1==3.8.0 -kombu==4.0.0 -linecache2==1.0.0 -MarkupSafe==1.0 -mccabe==0.6.0 -monotonic==0.6 -msgpack-python==0.4.0 -munch==2.1.0 -netaddr==0.7.18 -netifaces==0.10.4 -openstacksdk==0.15.0 -os-client-config==1.28.0 -os-service-types==1.2.0 -osc-lib==1.12.0 -oslo.concurrency==3.26.0 -oslo.config==5.2.0 -oslo.context==2.19.2 -oslo.i18n==3.15.3 -oslo.log==3.36.0 -oslo.messaging==5.29.0 -oslo.middleware==3.31.0 -oslo.serialization==2.18.0 -oslo.service==1.24.0 -oslo.utils==3.33.0 -oslotest==3.2.0 -osprofiler==2.3.0 -paramiko==2.0.0 -Paste==2.0.2 -PasteDeploy==1.5.0 -pbr==2.0.0 -pika-pool==0.1.3 -pika==0.10.0 -positional==1.2.1 -prettytable==0.7.2 -pyasn1==0.1.8 -pycodestyle==2.4.0 -pycparser==2.18 -pyflakes==2.0.0 -Pygments==2.2.0 -pyinotify==0.9.6 -pyOpenSSL==17.1.0 -pyparsing==2.1.0 -pyperclip==1.5.27 -python-cinderclient==3.3.0 -python-dateutil==2.5.3 -python-glanceclient==2.8.0 -python-keystoneclient==3.8.0 -python-mimeparse==1.6.0 -python-novaclient==9.1.0 -python-openstackclient==3.12.0 -python-subunit==1.0.0 -pytz==2013.6 -PyYAML==5.3.1 -repoze.lru==0.7 -requests-mock==1.2.0 -requests==2.14.2 -requestsexceptions==1.2.0 -rfc3986==0.3.1 -Routes==2.3.1 -simplejson==3.5.1 -snowballstemmer==1.2.1 -statsd==3.2.1 -stestr==2.0.0 -stevedore==2.0.1 -tempest==17.1.0 -tenacity==3.2.1 -testscenarios==0.4 -testtools==2.2.0 -traceback2==1.4.0 -unittest2==1.1.0 -urllib3==1.21.1 -vine==1.1.4 -warlock==1.2.0 -WebOb==1.7.1 -wrapt==1.7.0 diff --git a/requirements.txt b/requirements.txt index b08551ddf..b85e6b69a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,7 @@ +# Requirements lower bounds listed here are our best effort to keep them up to +# date but we do not test them so no guarantee of having them all correct. If +# you find any incorrect lower bounds, let us know or propose a fix. + # The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. diff --git a/tox.ini b/tox.ini index 9136a0f9e..bd421721e 100644 --- a/tox.ini +++ b/tox.ini @@ -86,9 +86,3 @@ enable-extensions=H904 # components. deps = -r{toxinidir}/test-requirements.txt commands = bandit -r neutronclient -x tests -n5 -s B303,B105 - -[testenv:lower-constraints] -deps = - -c{toxinidir}/lower-constraints.txt - -r{toxinidir}/test-requirements.txt - -r{toxinidir}/requirements.txt From b720fdaee7cb2bc8d38c63042c0b455cfd2be312 Mon Sep 17 00:00:00 2001 From: Ghanshyam Mann Date: Wed, 11 May 2022 22:25:31 -0500 Subject: [PATCH 792/845] Update python testing as per zed cycle teting runtime In Zed cycle, we have dropped the python 3.6/3.7[1] testing and its support. Add release notes, move py36 jobs to py38|9, and update the python classifier for the same. [1] https://governance.openstack.org/tc/reference/runtimes/zed.html Change-Id: Ibeee0b16ee1ea95f05127563a9cb08ffd0f04e2b --- .zuul.yaml | 2 +- .../notes/drop-python-3-6-and-3-7-73767fa0bbe89a6e.yaml | 5 +++++ setup.cfg | 4 +--- 3 files changed, 7 insertions(+), 4 deletions(-) create mode 100644 releasenotes/notes/drop-python-3-6-and-3-7-73767fa0bbe89a6e.yaml diff --git a/.zuul.yaml b/.zuul.yaml index b709db00e..e7645b60d 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -1,7 +1,7 @@ - project: templates: - openstack-cover-jobs - - openstack-python3-yoga-jobs + - openstack-python3-zed-jobs - publish-openstack-docs-pti - check-requirements - lib-forward-testing-python3 diff --git a/releasenotes/notes/drop-python-3-6-and-3-7-73767fa0bbe89a6e.yaml b/releasenotes/notes/drop-python-3-6-and-3-7-73767fa0bbe89a6e.yaml new file mode 100644 index 000000000..db420d739 --- /dev/null +++ b/releasenotes/notes/drop-python-3-6-and-3-7-73767fa0bbe89a6e.yaml @@ -0,0 +1,5 @@ +--- +upgrade: + - | + Python 3.6 & 3.7 support has been dropped. The minimum version of Python now + supported is Python 3.8. diff --git a/setup.cfg b/setup.cfg index a1a60d82d..5c62570ac 100644 --- a/setup.cfg +++ b/setup.cfg @@ -6,7 +6,7 @@ description_file = author = OpenStack Networking Project author_email = openstack-discuss@lists.openstack.org home_page = https://docs.openstack.org/python-neutronclient/latest/ -python_requires = >=3.6 +python_requires = >=3.8 classifier = Environment :: OpenStack Intended Audience :: Developers @@ -18,8 +18,6 @@ classifier = Programming Language :: Python :: Implementation :: CPython Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 From 7467c710f624aee80de8ef487e5b2c0e78143214 Mon Sep 17 00:00:00 2001 From: Pedro Henrique Date: Wed, 13 Apr 2022 09:32:41 -0300 Subject: [PATCH 793/845] Add support to floating ip port forwarding To extend Horizon to allow users to create port forwarding rules for their floating ips, we need to extend this client to allow it, as Horizon uses this client. This patch is the one of a series of patches to implement floating ip port forwarding with port ranges. The specification is defined in: https://github.com/openstack/neutron-specs/blob/master/specs/wallaby/port-forwarding-port-ranges.rst Implements: blueprint floatingips-portforwarding-ranges Related-Bug: #1885921 Change-Id: I3f616dba5e2ebe301cf6ce4bed8c2e6e4da2da9b --- neutronclient/v2_0/client.py | 28 +++++++++++++++++++ ...-ip-port-forwardings-9dc838a5c5727eb7.yaml | 4 +++ 2 files changed, 32 insertions(+) create mode 100644 releasenotes/notes/add-support-to-floating-ip-port-forwardings-9dc838a5c5727eb7.yaml diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 93a3af7a3..de0529182 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -515,6 +515,8 @@ class Client(ClientBase): router_path = "/routers/%s" floatingips_path = "/floatingips" floatingip_path = "/floatingips/%s" + port_forwardings_path = "/floatingips/%s/port_forwardings" + port_forwarding_path = "/floatingips/%s/port_forwardings/%s" security_groups_path = "/security-groups" security_group_path = "/security-groups/%s" security_group_rules_path = "/security-group-rules" @@ -1019,6 +1021,32 @@ def delete_floatingip(self, floatingip): """Deletes the specified floatingip.""" return self.delete(self.floatingip_path % (floatingip)) + def show_port_forwarding(self, floatingip, portforwarding): + """Fetches information of a certain portforwarding""" + return self.get(self.port_forwarding_path % (floatingip, + portforwarding)) + + def list_port_forwardings(self, floatingip, retrieve_all=True, **_params): + """Fetches a list of all portforwardings for a floatingip.""" + return self.list('port_forwardings', + self.port_forwardings_path % floatingip, retrieve_all, + **_params) + + def create_port_forwarding(self, floatingip, body=None): + """Creates a new portforwarding.""" + return self.post(self.port_forwardings_path % floatingip, body=body) + + def delete_port_forwarding(self, floatingip, portforwarding): + """Deletes the specified portforwarding.""" + return self.delete(self.port_forwarding_path % (floatingip, + portforwarding)) + + def update_port_forwarding(self, floatingip, portforwarding, body=None): + """Updates a portforwarding.""" + return self.put(self.port_forwarding_path % (floatingip, + portforwarding), + body=body) + def create_security_group(self, body=None): """Creates a new security group.""" return self.post(self.security_groups_path, body=body) diff --git a/releasenotes/notes/add-support-to-floating-ip-port-forwardings-9dc838a5c5727eb7.yaml b/releasenotes/notes/add-support-to-floating-ip-port-forwardings-9dc838a5c5727eb7.yaml new file mode 100644 index 000000000..3ba168ec9 --- /dev/null +++ b/releasenotes/notes/add-support-to-floating-ip-port-forwardings-9dc838a5c5727eb7.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Add support to floating ip port forwarding. \ No newline at end of file From f060429cfcb23b45236865fe1fc7855e3f795dbc Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Fri, 9 Sep 2022 11:48:19 +0000 Subject: [PATCH 794/845] Update master for stable/zed Add file to the reno documentation build to show release notes for stable/zed. Use pbr instruction to increment the minor version number automatically so that master versions are higher than the versions on stable/zed. Sem-Ver: feature Change-Id: Ie6a0efba406cd26606168ce5b6dbc8ee7ae759ed --- releasenotes/source/index.rst | 1 + releasenotes/source/zed.rst | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 releasenotes/source/zed.rst diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index 2a7aeaa06..f1aa6a943 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -6,6 +6,7 @@ :maxdepth: 1 unreleased + zed yoga xena wallaby diff --git a/releasenotes/source/zed.rst b/releasenotes/source/zed.rst new file mode 100644 index 000000000..9608c05e4 --- /dev/null +++ b/releasenotes/source/zed.rst @@ -0,0 +1,6 @@ +======================== +Zed Series Release Notes +======================== + +.. release-notes:: + :branch: stable/zed From ec84aff516d89817e1434da79579b7fdb03b27ce Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Fri, 9 Sep 2022 11:48:20 +0000 Subject: [PATCH 795/845] Switch to 2023.1 Python3 unit tests and generic template name This is an automatically generated patch to ensure unit testing is in place for all the of the tested runtimes for antelope. Also, updating the template name to generic one. See also the PTI in governance [1]. [1]: https://governance.openstack.org/tc/reference/project-testing-interface.html Change-Id: I026505ff0d277fd4f15329ed26a5cecf1d573f68 --- .zuul.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.zuul.yaml b/.zuul.yaml index e7645b60d..7e23f93e8 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -1,7 +1,7 @@ - project: templates: - openstack-cover-jobs - - openstack-python3-zed-jobs + - openstack-python3-jobs - publish-openstack-docs-pti - check-requirements - lib-forward-testing-python3 From f67af3d9be8cb289a78ccc1dceb324abc4767fde Mon Sep 17 00:00:00 2001 From: elajkat Date: Fri, 21 Oct 2022 13:58:12 +0200 Subject: [PATCH 796/845] Add warning and reno for SDK On the 2023.1 (Antelope) PTG we discussed the status of the python binding (SDK) code in python-neutronclient and decided to not allow new features to this repo (see [1]), and make users to use openstacksdk. Let's add a warning log message and a releasenote to make it visible. [1]: https://etherpad.opendev.org/p/neutron-antelope-ptg#L163 Change-Id: I03317179bd0d30a69b91eef6e451b8e40eb28191 --- neutronclient/v2_0/client.py | 3 +++ .../notes/no-new-binding-code-b03c9abbcaf2839e.yaml | 7 +++++++ 2 files changed, 10 insertions(+) create mode 100644 releasenotes/notes/no-new-binding-code-b03c9abbcaf2839e.yaml diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index de0529182..e670e3f5c 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -250,6 +250,9 @@ class ClientBase(object): def __init__(self, **kwargs): """Initialize a new client for the Neutron v2.0 API.""" super(ClientBase, self).__init__() + _logger.warning("The python binding code in neutronclient will be " + "deprecated in favor of OpenstackSDK, please use " + "that!") self.retries = kwargs.pop('retries', 0) self.raise_errors = kwargs.pop('raise_errors', True) self.httpclient = client.construct_http_client(**kwargs) diff --git a/releasenotes/notes/no-new-binding-code-b03c9abbcaf2839e.yaml b/releasenotes/notes/no-new-binding-code-b03c9abbcaf2839e.yaml new file mode 100644 index 000000000..bc41e7438 --- /dev/null +++ b/releasenotes/notes/no-new-binding-code-b03c9abbcaf2839e.yaml @@ -0,0 +1,7 @@ +--- +prelude: > + Openstack community decided to use one SDK for its services, that is + in ``openstacksdk`` repository. To avoid duplication, sooner or later the + python binding code in ``python-neutronclient`` will be deprecated, and + ``Neutron`` team decided on the ``2023.1 (Antelope)`` PTG to not allow + new features\' bindings implemented here. From 776e360e351c4b0c754fec5ad6308323adb44437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elvira=20Garc=C3=ADa?= Date: Fri, 25 Nov 2022 15:05:50 +0100 Subject: [PATCH 797/845] Fix help sentence in network log create --enable As the documentation states [1], the default in network log objects is to be enabled, not disabled. [1] https://docs.openstack.org/neutron/latest/admin/config-logging.html Change-Id: I13e9d1132fc38104e6e85d9c8442bc7506adc2fd --- neutronclient/osc/v2/logging/network_log.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neutronclient/osc/v2/logging/network_log.py b/neutronclient/osc/v2/logging/network_log.py index df48fc1e5..b3b8204c3 100644 --- a/neutronclient/osc/v2/logging/network_log.py +++ b/neutronclient/osc/v2/logging/network_log.py @@ -58,11 +58,11 @@ def _get_common_parser(parser): enable_group.add_argument( '--enable', action='store_true', - help=_('Enable this log (default is disabled)')) + help=_('Enable this log')) enable_group.add_argument( '--disable', action='store_true', - help=_('Disable this log')) + help=_('Disable this log (default is enabled)')) return parser From 33f1c89a840262ad378bf82fde5b42a07ad271db Mon Sep 17 00:00:00 2001 From: elajkat Date: Fri, 13 Jan 2023 16:22:06 +0100 Subject: [PATCH 798/845] Tox4: add allowlist_externals where necessary With tox4 allowlist_externals is more strictly checked, so fix it where necessary and fix pylint version. Depends-On: https://review.opendev.org/c/zuul/zuul-jobs/+/866943 Related-Bug: #1999558 Change-Id: Id115a436b95b3ede5a1f3102b4bb9e3ade75c970 --- doc/requirements.txt | 1 + tox.ini | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/requirements.txt b/doc/requirements.txt index 76ce5cd3f..718a2395a 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -4,3 +4,4 @@ openstackdocstheme>=2.2.0 # Apache-2.0 reno>=3.1.0 # Apache-2.0 sphinx>=2.0.0,!=2.1.0 # BSD +cliff>=3.4.0 # Apache-2.0 diff --git a/tox.ini b/tox.ini index bd421721e..06f5829f6 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] envlist = py39,pep8 minversion = 3.18.0 -skipsdist = True +skipsdist = False ignore_basepython_conflict = True [testenv] @@ -12,18 +12,17 @@ setenv = VIRTUAL_ENV={envdir} LC_ALL=C PYTHONWARNINGS=default::DeprecationWarning usedevelop = True -install_command = pip install {opts} {packages} deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt # Delete bytecodes from normal directories before running tests. # Note that bytecodes in dot directories will not be deleted # to keep bytecodes of python modules installed into virtualenvs. -commands = sh -c "find . -type d -name '.?*' -prune -o \ +commands = bash -c "find . -type d -name '.?*' -prune -o \ \( -type d -name '__pycache__' -o -type f -name '*.py[co]' \) \ -print0 | xargs -0 rm -rf" stestr run {posargs} -allowlist_externals = sh +allowlist_externals = bash [testenv:pep8] commands = From 68cbf56f9ccf9ecd4a53df5c684c2036d78a8612 Mon Sep 17 00:00:00 2001 From: elajkat Date: Fri, 6 Jan 2023 13:10:16 +0100 Subject: [PATCH 799/845] Move network trunk commands from python-neutronclient The depends-on patch adds trunk commands to OSC, as we can long consider trunk operations as core Networking operations. Change-Id: Ie557a5d541cf117d20f3f2b548620a74dbadb383 Depends-On: https://review.opendev.org/c/openstack/python-openstackclient/+/869447 Related-Bug: #1999774 --- doc/source/cli/osc/v2/network-trunk.rst | 16 - neutronclient/osc/v2/trunk/__init__.py | 0 neutronclient/osc/v2/trunk/network_trunk.py | 393 --------- .../tests/unit/osc/v2/trunk/__init__.py | 0 .../tests/unit/osc/v2/trunk/fakes.py | 87 -- .../unit/osc/v2/trunk/test_network_trunk.py | 769 ------------------ setup.cfg | 7 - 7 files changed, 1272 deletions(-) delete mode 100644 doc/source/cli/osc/v2/network-trunk.rst delete mode 100644 neutronclient/osc/v2/trunk/__init__.py delete mode 100644 neutronclient/osc/v2/trunk/network_trunk.py delete mode 100644 neutronclient/tests/unit/osc/v2/trunk/__init__.py delete mode 100644 neutronclient/tests/unit/osc/v2/trunk/fakes.py delete mode 100644 neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py diff --git a/doc/source/cli/osc/v2/network-trunk.rst b/doc/source/cli/osc/v2/network-trunk.rst deleted file mode 100644 index 22144a4ee..000000000 --- a/doc/source/cli/osc/v2/network-trunk.rst +++ /dev/null @@ -1,16 +0,0 @@ -============= -network trunk -============= - -A **network trunk** is a container to group logical ports from different -networks and provide a single trunked vNIC for servers. It consists of -one parent port which is a regular VIF and multiple subports which allow -the server to connect to more networks. - -Network v2 - -.. autoprogram-cliff:: openstack.neutronclient.v2 - :command: network subport list - -.. autoprogram-cliff:: openstack.neutronclient.v2 - :command: network trunk * diff --git a/neutronclient/osc/v2/trunk/__init__.py b/neutronclient/osc/v2/trunk/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutronclient/osc/v2/trunk/network_trunk.py b/neutronclient/osc/v2/trunk/network_trunk.py deleted file mode 100644 index 26cb52be9..000000000 --- a/neutronclient/osc/v2/trunk/network_trunk.py +++ /dev/null @@ -1,393 +0,0 @@ -# Copyright 2016 ZTE Corporation. -# 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. -# - -"""Network trunk and subports action implementations""" -import logging - -from osc_lib.cli import format_columns -from osc_lib.cli import parseractions -from osc_lib.command import command -from osc_lib import exceptions -from osc_lib import utils as osc_utils - -from neutronclient._i18n import _ -from neutronclient.osc import utils as nc_osc_utils -from neutronclient.osc.v2 import utils as v2_utils - -LOG = logging.getLogger(__name__) - -TRUNK = 'trunk' -TRUNKS = 'trunks' -SUB_PORTS = 'sub_ports' - - -class CreateNetworkTrunk(command.ShowOne): - """Create a network trunk for a given project""" - - def get_parser(self, prog_name): - parser = super(CreateNetworkTrunk, self).get_parser(prog_name) - parser.add_argument( - 'name', - metavar='', - help=_("Name of the trunk to create") - ) - parser.add_argument( - '--description', - metavar='', - help=_("A description of the trunk") - ) - parser.add_argument( - '--parent-port', - metavar='', - required=True, - help=_("Parent port belonging to this trunk (name or ID)") - ) - parser.add_argument( - '--subport', - metavar='', - action=parseractions.MultiKeyValueAction, dest='add_subports', - optional_keys=['segmentation-id', 'segmentation-type'], - required_keys=['port'], - help=_("Subport to add. Subport is of form " - "\'port=,segmentation-type=,segmentation-ID=\' " - "(--subport) option can be repeated") - ) - admin_group = parser.add_mutually_exclusive_group() - admin_group.add_argument( - '--enable', - action='store_true', - default=True, - help=_("Enable trunk (default)") - ) - admin_group.add_argument( - '--disable', - action='store_true', - help=_("Disable trunk") - ) - nc_osc_utils.add_project_owner_option_to_parser(parser) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - attrs = _get_attrs_for_trunk(self.app.client_manager, - parsed_args) - body = {TRUNK: attrs} - obj = client.create_trunk(body) - columns = _get_columns(obj[TRUNK]) - data = osc_utils.get_dict_properties(obj[TRUNK], columns, - formatters=_formatters) - return columns, data - - -class DeleteNetworkTrunk(command.Command): - """Delete a given network trunk""" - - def get_parser(self, prog_name): - parser = super(DeleteNetworkTrunk, self).get_parser(prog_name) - parser.add_argument( - 'trunk', - metavar="", - nargs="+", - help=_("Trunk(s) to delete (name or ID)") - ) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - result = 0 - for trunk in parsed_args.trunk: - try: - trunk_id = _get_id(client, trunk, TRUNK) - client.delete_trunk(trunk_id) - except Exception as e: - result += 1 - LOG.error(_("Failed to delete trunk with name " - "or ID '%(trunk)s': %(e)s"), - {'trunk': trunk, 'e': e}) - if result > 0: - total = len(parsed_args.trunk) - msg = (_("%(result)s of %(total)s trunks failed " - "to delete.") % {'result': result, 'total': total}) - raise exceptions.CommandError(msg) - - -class ListNetworkTrunk(command.Lister): - """List all network trunks""" - - def get_parser(self, prog_name): - parser = super(ListNetworkTrunk, self).get_parser(prog_name) - parser.add_argument( - '--long', - action='store_true', - default=False, - help=_("List additional fields in output") - ) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - data = client.list_trunks() - headers = ( - 'ID', - 'Name', - 'Parent Port', - 'Description' - ) - columns = ( - 'id', - 'name', - 'port_id', - 'description' - ) - if parsed_args.long: - headers += ( - 'Status', - 'State', - 'Created At', - 'Updated At', - ) - columns += ( - 'status', - 'admin_state_up', - 'created_at', - 'updated_at' - ) - return (headers, - (osc_utils.get_dict_properties( - s, columns, - formatters=_formatters, - ) for s in data[TRUNKS])) - - -class SetNetworkTrunk(command.Command): - """Set network trunk properties""" - - def get_parser(self, prog_name): - parser = super(SetNetworkTrunk, self).get_parser(prog_name) - parser.add_argument( - 'trunk', - metavar="", - help=_("Trunk to modify (name or ID)") - ) - parser.add_argument( - '--name', - metavar="", - help=_("Set trunk name") - ) - parser.add_argument( - '--description', - metavar='', - help=_("A description of the trunk") - ) - parser.add_argument( - '--subport', - metavar='', - action=parseractions.MultiKeyValueAction, dest='set_subports', - optional_keys=['segmentation-id', 'segmentation-type'], - required_keys=['port'], - help=_("Subport to add. Subport is of form " - "\'port=,segmentation-type=,segmentation-ID=\'" - "(--subport) option can be repeated") - ) - admin_group = parser.add_mutually_exclusive_group() - admin_group.add_argument( - '--enable', - action='store_true', - help=_("Enable trunk") - ) - admin_group.add_argument( - '--disable', - action='store_true', - help=_("Disable trunk") - ) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - trunk_id = _get_id(client, parsed_args.trunk, TRUNK) - attrs = _get_attrs_for_trunk(self.app.client_manager, parsed_args) - body = {TRUNK: attrs} - try: - client.update_trunk(trunk_id, body) - except Exception as e: - msg = (_("Failed to set trunk '%(t)s': %(e)s") - % {'t': parsed_args.trunk, 'e': e}) - raise exceptions.CommandError(msg) - if parsed_args.set_subports: - subport_attrs = _get_attrs_for_subports(self.app.client_manager, - parsed_args) - try: - client.trunk_add_subports(trunk_id, subport_attrs) - except Exception as e: - msg = (_("Failed to add subports to trunk '%(t)s': %(e)s") - % {'t': parsed_args.trunk, 'e': e}) - raise exceptions.CommandError(msg) - - -class ShowNetworkTrunk(command.ShowOne): - """Show information of a given network trunk""" - def get_parser(self, prog_name): - parser = super(ShowNetworkTrunk, self).get_parser(prog_name) - parser.add_argument( - 'trunk', - metavar="", - help=_("Trunk to display (name or ID)") - ) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - trunk_id = _get_id(client, parsed_args.trunk, TRUNK) - obj = client.show_trunk(trunk_id) - columns = _get_columns(obj[TRUNK]) - data = osc_utils.get_dict_properties(obj[TRUNK], columns, - formatters=_formatters) - return columns, data - - -class ListNetworkSubport(command.Lister): - """List all subports for a given network trunk""" - - def get_parser(self, prog_name): - parser = super(ListNetworkSubport, self).get_parser(prog_name) - parser.add_argument( - '--trunk', - required=True, - metavar="", - help=_("List subports belonging to this trunk (name or ID)") - ) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - trunk_id = _get_id(client, parsed_args.trunk, TRUNK) - data = client.trunk_get_subports(trunk_id) - headers = ('Port', 'Segmentation Type', 'Segmentation ID') - columns = ('port_id', 'segmentation_type', 'segmentation_id') - return (headers, - (osc_utils.get_dict_properties( - s, columns, - ) for s in data[SUB_PORTS])) - - -class UnsetNetworkTrunk(command.Command): - """Unset subports from a given network trunk""" - - def get_parser(self, prog_name): - parser = super(UnsetNetworkTrunk, self).get_parser(prog_name) - parser.add_argument( - 'trunk', - metavar="", - help=_("Unset subports from this trunk (name or ID)") - ) - parser.add_argument( - '--subport', - metavar="", - required=True, - action='append', dest='unset_subports', - help=_("Subport to delete (name or ID of the port) " - "(--subport) option can be repeated") - ) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - attrs = _get_attrs_for_subports(self.app.client_manager, parsed_args) - trunk_id = _get_id(client, parsed_args.trunk, TRUNK) - client.trunk_remove_subports(trunk_id, attrs) - - -_formatters = { - 'admin_state_up': v2_utils.AdminStateColumn, - 'sub_ports': format_columns.ListDictColumn, -} - - -def _get_columns(item): - return tuple(sorted(list(item.keys()))) - - -def _get_attrs_for_trunk(client_manager, parsed_args): - attrs = {} - if parsed_args.name is not None: - attrs['name'] = str(parsed_args.name) - if parsed_args.description is not None: - attrs['description'] = str(parsed_args.description) - if parsed_args.enable: - attrs['admin_state_up'] = True - if parsed_args.disable: - attrs['admin_state_up'] = False - if 'parent_port' in parsed_args and parsed_args.parent_port is not None: - port_id = _get_id(client_manager.neutronclient, - parsed_args.parent_port, 'port') - attrs['port_id'] = port_id - if 'add_subports' in parsed_args and parsed_args.add_subports is not None: - attrs[SUB_PORTS] = _format_subports(client_manager, - parsed_args.add_subports) - - # "trunk set" command doesn't support setting project. - if 'project' in parsed_args and parsed_args.project is not None: - identity_client = client_manager.identity - project_id = nc_osc_utils.find_project( - identity_client, - parsed_args.project, - parsed_args.project_domain, - ).id - attrs['tenant_id'] = project_id - - return attrs - - -def _format_subports(client_manager, subports): - attrs = [] - for subport in subports: - subport_attrs = {} - if subport.get('port'): - port_id = _get_id(client_manager.neutronclient, - subport['port'], 'port') - subport_attrs['port_id'] = port_id - if subport.get('segmentation-id'): - try: - subport_attrs['segmentation_id'] = int( - subport['segmentation-id']) - except ValueError: - msg = (_("Segmentation-id '%s' is not an integer") % - subport['segmentation-id']) - raise exceptions.CommandError(msg) - if subport.get('segmentation-type'): - subport_attrs['segmentation_type'] = subport['segmentation-type'] - attrs.append(subport_attrs) - return attrs - - -def _get_attrs_for_subports(client_manager, parsed_args): - attrs = {} - if 'set_subports' in parsed_args and parsed_args.set_subports is not None: - attrs[SUB_PORTS] = _format_subports(client_manager, - parsed_args.set_subports) - if ('unset_subports' in parsed_args and - parsed_args.unset_subports is not None): - subports_list = [] - for subport in parsed_args.unset_subports: - port_id = _get_id(client_manager.neutronclient, - subport, 'port') - subports_list.append({'port_id': port_id}) - attrs[SUB_PORTS] = subports_list - return attrs - - -def _get_id(client, id_or_name, resource): - return client.find_resource(resource, str(id_or_name))['id'] diff --git a/neutronclient/tests/unit/osc/v2/trunk/__init__.py b/neutronclient/tests/unit/osc/v2/trunk/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutronclient/tests/unit/osc/v2/trunk/fakes.py b/neutronclient/tests/unit/osc/v2/trunk/fakes.py deleted file mode 100644 index a52aa3097..000000000 --- a/neutronclient/tests/unit/osc/v2/trunk/fakes.py +++ /dev/null @@ -1,87 +0,0 @@ -# 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 copy -from unittest import mock - -from oslo_utils import uuidutils - - -class FakeTrunk(object): - """Fake one or more trunks.""" - @staticmethod - def create_one_trunk(attrs=None): - """Create a fake trunk. - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A Dictionary with id, name, description, admin_state_up, port_id, - sub_ports, status and project_id - """ - attrs = attrs or {} - - # Set default attributes. - trunk_attrs = { - 'id': 'trunk-id-' + uuidutils.generate_uuid(dashed=False), - 'name': 'trunk-name-' + uuidutils.generate_uuid(dashed=False), - 'description': '', - 'port_id': 'port-' + uuidutils.generate_uuid(dashed=False), - 'admin_state_up': True, - 'project_id': 'project-id-' + - uuidutils.generate_uuid(dashed=False), - 'status': 'ACTIVE', - 'sub_ports': [{'port_id': 'subport-' + - uuidutils.generate_uuid(dashed=False), - 'segmentation_type': 'vlan', - 'segmentation_id': 100}], - } - - # Overwrite default attributes. - trunk_attrs.update(attrs) - return copy.deepcopy(trunk_attrs) - - @staticmethod - def create_trunks(attrs=None, count=2): - """Create multiple fake trunks. - - :param Dictionary attrs: - A dictionary with all attributes - :param int count: - The number of routers to fake - :return: - A list of dictionaries faking the trunks - """ - trunks = [] - for i in range(0, count): - trunks.append(FakeTrunk.create_one_trunk(attrs)) - - return trunks - - @staticmethod - def get_trunks(trunks=None, count=2): - """Get an iterable Mock object with a list of faked trunks. - - If trunks list is provided, then initialize the Mock object with the - list. Otherwise create one. - - :param List trunks: - A list of FakeResource objects faking trunks - :param int count: - The number of trunks to fake - :return: - An iterable Mock object with side_effect set to a list of faked - trunks - """ - if trunks is None: - trunks = FakeTrunk.create_trunks(count) - return mock.Mock(side_effect=trunks) diff --git a/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py b/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py deleted file mode 100644 index 1e16c1fa9..000000000 --- a/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py +++ /dev/null @@ -1,769 +0,0 @@ -# Copyright 2016 ZTE Corporation. -# 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 argparse -import copy -from unittest import mock -from unittest.mock import call - -from osc_lib.cli import format_columns -from osc_lib import exceptions -from osc_lib.tests import utils as tests_utils -import testtools - -from neutronclient.osc.v2.trunk import network_trunk as trunk -from neutronclient.osc.v2 import utils as v2_utils -from neutronclient.tests.unit.osc.v2 import fakes as test_fakes -from neutronclient.tests.unit.osc.v2.trunk import fakes - - -def _get_id(client, id_or_name, resource): - return id_or_name - - -class TestCreateNetworkTrunk(test_fakes.TestNeutronClientOSCV2): - # The new trunk created - _trunk = fakes.FakeTrunk.create_one_trunk() - - columns = ( - 'admin_state_up', - 'description', - 'id', - 'name', - 'port_id', - 'project_id', - 'status', - 'sub_ports', - ) - - def get_data(self): - return ( - v2_utils.AdminStateColumn(self._trunk['admin_state_up']), - self._trunk['description'], - self._trunk['id'], - self._trunk['name'], - self._trunk['port_id'], - self._trunk['project_id'], - self._trunk['status'], - format_columns.ListDictColumn(self._trunk['sub_ports']), - ) - - def setUp(self): - super(TestCreateNetworkTrunk, self).setUp() - mock.patch('neutronclient.osc.v2.trunk.network_trunk._get_id', - new=_get_id).start() - self.neutronclient.create_trunk = mock.Mock( - return_value={trunk.TRUNK: self._trunk}) - self.data = self.get_data() - - # Get the command object to test - self.cmd = trunk.CreateNetworkTrunk(self.app, self.namespace) - - def test_create_no_options(self): - arglist = [] - verifylist = [] - - self.assertRaises(tests_utils.ParserException, self.check_parser, - self.cmd, arglist, verifylist) - - def test_create_default_options(self): - arglist = [ - "--parent-port", self._trunk['port_id'], - self._trunk['name'], - ] - verifylist = [ - ('parent_port', self._trunk['port_id']), - ('name', self._trunk['name']), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - columns, data = (self.cmd.take_action(parsed_args)) - - self.neutronclient.create_trunk.assert_called_once_with({ - trunk.TRUNK: {'name': self._trunk['name'], - 'admin_state_up': self._trunk['admin_state_up'], - 'port_id': self._trunk['port_id']} - }) - self.assertEqual(self.columns, columns) - self.assertItemEqual(self.data, data) - - def test_create_full_options(self): - self._trunk['description'] = 'foo description' - self.data = self.get_data() - subport = self._trunk['sub_ports'][0] - arglist = [ - "--disable", - "--description", self._trunk['description'], - "--parent-port", self._trunk['port_id'], - "--subport", 'port=%(port)s,segmentation-type=%(seg_type)s,' - 'segmentation-id=%(seg_id)s' % { - 'seg_id': subport['segmentation_id'], - 'seg_type': subport['segmentation_type'], - 'port': subport['port_id']}, - self._trunk['name'], - ] - verifylist = [ - ('name', self._trunk['name']), - ('description', self._trunk['description']), - ('parent_port', self._trunk['port_id']), - ('add_subports', [{ - 'port': subport['port_id'], - 'segmentation-id': str(subport['segmentation_id']), - 'segmentation-type': subport['segmentation_type']}]), - ('disable', True), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = (self.cmd.take_action(parsed_args)) - - self.neutronclient.create_trunk.assert_called_once_with({ - trunk.TRUNK: {'name': self._trunk['name'], - 'description': self._trunk['description'], - 'admin_state_up': False, - 'sub_ports': [subport], - 'port_id': self._trunk['port_id']} - }) - self.assertEqual(self.columns, columns) - self.assertItemEqual(self.data, data) - - def test_create_trunk_with_subport_invalid_segmentation_id_fail(self): - subport = self._trunk['sub_ports'][0] - arglist = [ - "--parent-port", self._trunk['port_id'], - "--subport", "port=%(port)s,segmentation-type=%(seg_type)s," - "segmentation-id=boom" % { - 'seg_type': subport['segmentation_type'], - 'port': subport['port_id']}, - self._trunk['name'], - ] - verifylist = [ - ('name', self._trunk['name']), - ('parent_port', self._trunk['port_id']), - ('add_subports', [{ - 'port': subport['port_id'], - 'segmentation-id': 'boom', - 'segmentation-type': subport['segmentation_type']}]), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - with testtools.ExpectedException(exceptions.CommandError) as e: - self.cmd.take_action(parsed_args) - self.assertEqual("Segmentation-id 'boom' is not an integer", - str(e)) - - def test_create_network_trunk_subports_without_optional_keys(self): - subport = copy.copy(self._trunk['sub_ports'][0]) - # Pop out the segmentation-id and segmentation-type - subport.pop('segmentation_type') - subport.pop('segmentation_id') - arglist = [ - '--parent-port', self._trunk['port_id'], - '--subport', 'port=%(port)s' % {'port': subport['port_id']}, - self._trunk['name'], - ] - verifylist = [ - ('name', self._trunk['name']), - ('parent_port', self._trunk['port_id']), - ('add_subports', [{ - 'port': subport['port_id']}]), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - columns, data = (self.cmd.take_action(parsed_args)) - - self.neutronclient.create_trunk.assert_called_once_with({ - trunk.TRUNK: {'name': self._trunk['name'], - 'admin_state_up': True, - 'sub_ports': [subport], - 'port_id': self._trunk['port_id']} - }) - self.assertEqual(self.columns, columns) - self.assertItemEqual(self.data, data) - - def test_create_network_trunk_subports_without_required_key_fail(self): - subport = self._trunk['sub_ports'][0] - arglist = [ - '--parent-port', self._trunk['port_id'], - '--subport', 'segmentation-type=%(seg_type)s,' - 'segmentation-id=%(seg_id)s' % { - 'seg_id': subport['segmentation_id'], - 'seg_type': subport['segmentation_type']}, - self._trunk['name'], - ] - verifylist = [ - ('name', self._trunk['name']), - ('parent_port', self._trunk['port_id']), - ('add_subports', [{ - 'segmentation-id': str(subport['segmentation_id']), - 'segmentation-type': subport['segmentation_type']}]), - ] - - with testtools.ExpectedException(argparse.ArgumentTypeError): - self.check_parser(self.cmd, arglist, verifylist) - - -class TestDeleteNetworkTrunk(test_fakes.TestNeutronClientOSCV2): - # The trunk to be deleted. - _trunks = fakes.FakeTrunk.create_trunks(count=2) - - def setUp(self): - super(TestDeleteNetworkTrunk, self).setUp() - - mock.patch('neutronclient.osc.v2.trunk.network_trunk._get_id', - new=_get_id).start() - self.neutronclient.delete_trunk = mock.Mock(return_value=None) - - # Get the command object to test - self.cmd = trunk.DeleteNetworkTrunk(self.app, self.namespace) - - def test_delete_trunk(self): - arglist = [ - self._trunks[0]['name'], - ] - verifylist = [ - ('trunk', [self._trunks[0]['name']]), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.neutronclient.delete_trunk.assert_called_once_with( - self._trunks[0]['name']) - self.assertIsNone(result) - - def test_delete_trunk_multiple(self): - arglist = [] - verifylist = [] - - for t in self._trunks: - arglist.append(t['name']) - verifylist = [ - ('trunk', arglist), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - calls = [] - for t in self._trunks: - calls.append(call(t['name'])) - self.neutronclient.delete_trunk.assert_has_calls(calls) - self.assertIsNone(result) - - def test_delete_trunk_multiple_with_exception(self): - arglist = [ - self._trunks[0]['name'], - 'unexist_trunk', - ] - verifylist = [ - ('trunk', - [self._trunks[0]['name'], 'unexist_trunk']), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - get_mock_result = [self._trunks[0], exceptions.CommandError] - trunk._get_id = ( - mock.Mock(side_effect=get_mock_result) - ) - with testtools.ExpectedException(exceptions.CommandError) as e: - self.cmd.take_action(parsed_args) - self.assertEqual('1 of 2 trunks failed to delete.', str(e)) - self.neutronclient.delete_trunk.assert_called_once_with( - self._trunks[0] - ) - - -class TestShowNetworkTrunk(test_fakes.TestNeutronClientOSCV2): - - # The trunk to set. - _trunk = fakes.FakeTrunk.create_one_trunk() - - columns = ( - 'admin_state_up', - 'description', - 'id', - 'name', - 'port_id', - 'project_id', - 'status', - 'sub_ports', - ) - data = ( - v2_utils.AdminStateColumn(_trunk['admin_state_up']), - _trunk['description'], - _trunk['id'], - _trunk['name'], - _trunk['port_id'], - _trunk['project_id'], - _trunk['status'], - format_columns.ListDictColumn(_trunk['sub_ports']), - ) - - def setUp(self): - super(TestShowNetworkTrunk, self).setUp() - - mock.patch('neutronclient.osc.v2.trunk.network_trunk._get_id', - new=_get_id).start() - self.neutronclient.show_trunk = mock.Mock( - return_value={trunk.TRUNK: self._trunk}) - - # Get the command object to test - self.cmd = trunk.ShowNetworkTrunk(self.app, self.namespace) - - def test_show_no_options(self): - arglist = [] - verifylist = [] - - self.assertRaises(tests_utils.ParserException, self.check_parser, - self.cmd, arglist, verifylist) - - def test_show_all_options(self): - arglist = [ - self._trunk['id'], - ] - verifylist = [ - ('trunk', self._trunk['id']), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.neutronclient.show_trunk.assert_called_once_with( - self._trunk['id']) - self.assertEqual(self.columns, columns) - self.assertItemEqual(self.data, data) - - -class TestListNetworkTrunk(test_fakes.TestNeutronClientOSCV2): - # Create trunks to be listed. - _trunks = fakes.FakeTrunk.create_trunks( - {'created_at': '2001-01-01 00:00:00', - 'updated_at': '2001-01-01 00:00:00'}, count=3) - - columns = ( - 'ID', - 'Name', - 'Parent Port', - 'Description' - ) - columns_long = columns + ( - 'Status', - 'State', - 'Created At', - 'Updated At' - ) - data = [] - for t in _trunks: - data.append(( - t['id'], - t['name'], - t['port_id'], - t['description'] - )) - data_long = [] - for t in _trunks: - data_long.append(( - t['id'], - t['name'], - t['port_id'], - t['description'], - t['status'], - v2_utils.AdminStateColumn(t['admin_state_up']), - '2001-01-01 00:00:00', - '2001-01-01 00:00:00', - )) - - def setUp(self): - super(TestListNetworkTrunk, self).setUp() - mock.patch('neutronclient.osc.v2.trunk.network_trunk._get_id', - new=_get_id).start() - self.neutronclient.list_trunks = mock.Mock( - return_value={trunk.TRUNKS: self._trunks}) - - # Get the command object to test - self.cmd = trunk.ListNetworkTrunk(self.app, self.namespace) - - def test_trunk_list_no_option(self): - arglist = [] - verifylist = [] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.neutronclient.list_trunks.assert_called_once_with() - self.assertEqual(self.columns, columns) - self.assertListItemEqual(self.data, list(data)) - - def test_trunk_list_long(self): - arglist = [ - '--long', - ] - verifylist = [ - ('long', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.neutronclient.list_trunks.assert_called_once_with() - self.assertEqual(self.columns_long, columns) - self.assertListItemEqual(self.data_long, list(data)) - - -class TestSetNetworkTrunk(test_fakes.TestNeutronClientOSCV2): - # Create trunks to be listed. - _trunk = fakes.FakeTrunk.create_one_trunk() - - columns = ( - 'admin_state_up', - 'id', - 'name', - 'description', - 'port_id', - 'project_id', - 'status', - 'sub_ports', - ) - data = ( - v2_utils.AdminStateColumn(_trunk['admin_state_up']), - _trunk['id'], - _trunk['name'], - _trunk['description'], - _trunk['port_id'], - _trunk['project_id'], - _trunk['status'], - format_columns.ListDictColumn(_trunk['sub_ports']), - ) - - def setUp(self): - super(TestSetNetworkTrunk, self).setUp() - mock.patch('neutronclient.osc.v2.trunk.network_trunk._get_id', - new=_get_id).start() - self.neutronclient.update_trunk = mock.Mock( - return_value={trunk.TRUNK: self._trunk}) - self.neutronclient.trunk_add_subports = mock.Mock( - return_value=self._trunk) - - # Get the command object to test - self.cmd = trunk.SetNetworkTrunk(self.app, self.namespace) - - def _test_set_network_trunk_attr(self, attr, value): - arglist = [ - '--%s' % attr, value, - self._trunk[attr], - ] - verifylist = [ - (attr, value), - ('trunk', self._trunk[attr]), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - attrs = { - attr: value, - } - self.neutronclient.update_trunk.assert_called_once_with( - self._trunk[attr], {trunk.TRUNK: attrs}) - self.assertIsNone(result) - - def test_set_network_trunk_name(self): - self._test_set_network_trunk_attr('name', 'trunky') - - def test_test_set_network_trunk_description(self): - self._test_set_network_trunk_attr('description', 'description') - - def test_set_network_trunk_admin_state_up_disable(self): - arglist = [ - '--disable', - self._trunk['name'], - ] - verifylist = [ - ('disable', True), - ('trunk', self._trunk['name']), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - attrs = { - 'admin_state_up': False, - } - self.neutronclient.update_trunk.assert_called_once_with( - self._trunk['name'], {trunk.TRUNK: attrs}) - self.assertIsNone(result) - - def test_set_network_trunk_admin_state_up_enable(self): - arglist = [ - '--enable', - self._trunk['name'], - ] - verifylist = [ - ('enable', True), - ('trunk', self._trunk['name']), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - attrs = { - 'admin_state_up': True, - } - self.neutronclient.update_trunk.assert_called_once_with( - self._trunk['name'], {trunk.TRUNK: attrs}) - self.assertIsNone(result) - - def test_set_network_trunk_nothing(self): - arglist = [self._trunk['name'], ] - verifylist = [('trunk', self._trunk['name']), ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - attrs = {} - self.neutronclient.update_trunk.assert_called_once_with( - self._trunk['name'], {trunk.TRUNK: attrs}) - self.assertIsNone(result) - - def test_set_network_trunk_subports(self): - subport = self._trunk['sub_ports'][0] - arglist = [ - '--subport', 'port=%(port)s,segmentation-type=%(seg_type)s,' - 'segmentation-id=%(seg_id)s' % { - 'seg_id': subport['segmentation_id'], - 'seg_type': subport['segmentation_type'], - 'port': subport['port_id']}, - self._trunk['name'], - ] - verifylist = [ - ('trunk', self._trunk['name']), - ('set_subports', [{ - 'port': subport['port_id'], - 'segmentation-id': str(subport['segmentation_id']), - 'segmentation-type': subport['segmentation_type']}]), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.neutronclient.trunk_add_subports.assert_called_once_with( - self._trunk['name'], {'sub_ports': [subport]} - ) - self.assertIsNone(result) - - def test_set_network_trunk_subports_without_optional_keys(self): - subport = copy.copy(self._trunk['sub_ports'][0]) - # Pop out the segmentation-id and segmentation-type - subport.pop('segmentation_type') - subport.pop('segmentation_id') - arglist = [ - '--subport', 'port=%(port)s' % {'port': subport['port_id']}, - self._trunk['name'], - ] - verifylist = [ - ('trunk', self._trunk['name']), - ('set_subports', [{ - 'port': subport['port_id']}]), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.neutronclient.trunk_add_subports.assert_called_once_with( - self._trunk['name'], {'sub_ports': [subport]} - ) - self.assertIsNone(result) - - def test_set_network_trunk_subports_without_required_key_fail(self): - subport = self._trunk['sub_ports'][0] - arglist = [ - '--subport', 'segmentation-type=%(seg_type)s,' - 'segmentation-id=%(seg_id)s' % { - 'seg_id': subport['segmentation_id'], - 'seg_type': subport['segmentation_type']}, - self._trunk['name'], - ] - verifylist = [ - ('trunk', self._trunk['name']), - ('set_subports', [{ - 'segmentation-id': str(subport['segmentation_id']), - 'segmentation-type': subport['segmentation_type']}]), - ] - - with testtools.ExpectedException(argparse.ArgumentTypeError): - self.check_parser(self.cmd, arglist, verifylist) - - self.neutronclient.trunk_add_subports.assert_not_called() - - def test_set_trunk_attrs_with_exception(self): - arglist = [ - '--name', 'reallylongname', - self._trunk['name'], - ] - verifylist = [ - ('trunk', self._trunk['name']), - ('name', 'reallylongname'), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.neutronclient.update_trunk = ( - mock.Mock(side_effect=exceptions.CommandError) - ) - with testtools.ExpectedException(exceptions.CommandError) as e: - self.cmd.take_action(parsed_args) - self.assertEqual( - "Failed to set trunk '%s': " % self._trunk['name'], - str(e)) - attrs = {'name': 'reallylongname'} - self.neutronclient.update_trunk.assert_called_once_with( - self._trunk['name'], {trunk.TRUNK: attrs}) - self.neutronclient.trunk_add_subports.assert_not_called() - - def test_set_trunk_add_subport_with_exception(self): - arglist = [ - '--subport', 'port=invalid_subport', - self._trunk['name'], - ] - verifylist = [ - ('trunk', self._trunk['name']), - ('set_subports', [{'port': 'invalid_subport'}]), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.neutronclient.trunk_add_subports = ( - mock.Mock(side_effect=exceptions.CommandError) - ) - with testtools.ExpectedException(exceptions.CommandError) as e: - self.cmd.take_action(parsed_args) - self.assertEqual( - "Failed to add subports to trunk '%s': " % self._trunk['name'], - str(e)) - self.neutronclient.update_trunk.assert_called_once_with( - self._trunk['name'], {trunk.TRUNK: {}}) - self.neutronclient.trunk_add_subports.assert_called_once_with( - self._trunk['name'], - {'sub_ports': [{'port_id': 'invalid_subport'}]} - ) - - -class TestListNetworkSubport(test_fakes.TestNeutronClientOSCV2): - - _trunk = fakes.FakeTrunk.create_one_trunk() - _subports = _trunk['sub_ports'] - - columns = ( - 'Port', - 'Segmentation Type', - 'Segmentation ID', - ) - data = [] - for s in _subports: - data.append(( - s['port_id'], - s['segmentation_type'], - s['segmentation_id'], - )) - - def setUp(self): - super(TestListNetworkSubport, self).setUp() - mock.patch('neutronclient.osc.v2.trunk.network_trunk._get_id', - new=_get_id).start() - self.neutronclient.trunk_get_subports = mock.Mock( - return_value={trunk.SUB_PORTS: self._subports}) - - # Get the command object to test - self.cmd = trunk.ListNetworkSubport(self.app, self.namespace) - - def test_subport_list(self): - arglist = [ - '--trunk', self._trunk['name'], - ] - verifylist = [ - ('trunk', self._trunk['name']), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.neutronclient.trunk_get_subports.assert_called_once_with( - self._trunk['name']) - self.assertEqual(self.columns, columns) - self.assertEqual(self.data, list(data)) - - -class TestUnsetNetworkTrunk(test_fakes.TestNeutronClientOSCV2): - - _trunk = fakes.FakeTrunk.create_one_trunk() - - columns = ( - 'admin_state_up', - 'id', - 'name', - 'port_id', - 'project_id', - 'status', - 'sub_ports', - ) - data = ( - v2_utils.AdminStateColumn(_trunk['admin_state_up']), - _trunk['id'], - _trunk['name'], - _trunk['port_id'], - _trunk['project_id'], - _trunk['status'], - format_columns.ListDictColumn(_trunk['sub_ports']), - ) - - def setUp(self): - super(TestUnsetNetworkTrunk, self).setUp() - - mock.patch('neutronclient.osc.v2.trunk.network_trunk._get_id', - new=_get_id).start() - self.neutronclient.trunk_remove_subports = mock.Mock( - return_value=None) - - # Get the command object to test - self.cmd = trunk.UnsetNetworkTrunk(self.app, self.namespace) - - def test_unset_network_trunk_subport(self): - subport = self._trunk['sub_ports'][0] - arglist = [ - "--subport", subport['port_id'], - self._trunk['name'], - ] - verifylist = [ - ('trunk', self._trunk['name']), - ('unset_subports', [subport['port_id']]), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.neutronclient.trunk_remove_subports.assert_called_once_with( - self._trunk['name'], - {trunk.SUB_PORTS: [{'port_id': subport['port_id']}]} - ) - self.assertIsNone(result) - - def test_unset_subport_no_arguments_fail(self): - arglist = [ - self._trunk['name'], - ] - verifylist = [ - ('trunk', self._trunk['name']), - ] - self.assertRaises(tests_utils.ParserException, - self.check_parser, self.cmd, arglist, verifylist) diff --git a/setup.cfg b/setup.cfg index 5c62570ac..432ec682f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -33,13 +33,6 @@ openstack.cli.extension = neutronclient = neutronclient.osc.plugin openstack.neutronclient.v2 = - network_subport_list = neutronclient.osc.v2.trunk.network_trunk:ListNetworkSubport - network_trunk_create = neutronclient.osc.v2.trunk.network_trunk:CreateNetworkTrunk - network_trunk_delete = neutronclient.osc.v2.trunk.network_trunk:DeleteNetworkTrunk - network_trunk_list = neutronclient.osc.v2.trunk.network_trunk:ListNetworkTrunk - network_trunk_set = neutronclient.osc.v2.trunk.network_trunk:SetNetworkTrunk - network_trunk_show = neutronclient.osc.v2.trunk.network_trunk:ShowNetworkTrunk - network_trunk_unset = neutronclient.osc.v2.trunk.network_trunk:UnsetNetworkTrunk sfc_flow_classifier_create = neutronclient.osc.v2.sfc.sfc_flow_classifier:CreateSfcFlowClassifier sfc_flow_classifier_delete = neutronclient.osc.v2.sfc.sfc_flow_classifier:DeleteSfcFlowClassifier sfc_flow_classifier_list = neutronclient.osc.v2.sfc.sfc_flow_classifier:ListSfcFlowClassifier From 16a2cd127dfa2e44887aee911d248c8d9365efb8 Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez Date: Tue, 14 Feb 2023 08:27:42 +0100 Subject: [PATCH 800/845] Remove CLI warning message This warning was added in [1]. This patch is partially reverting it but we keep the release note. This warning message could be a bit intrusive for those users still using it. In any case, the recommendation to move to OSC is something already known and publicly requested. [1]https://review.opendev.org/c/openstack/python-neutronclient/+/862371 Change-Id: I0c2fba3e45a9de1eba09efcf8919661a855c7e89 --- neutronclient/v2_0/client.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index e670e3f5c..de0529182 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -250,9 +250,6 @@ class ClientBase(object): def __init__(self, **kwargs): """Initialize a new client for the Neutron v2.0 API.""" super(ClientBase, self).__init__() - _logger.warning("The python binding code in neutronclient will be " - "deprecated in favor of OpenstackSDK, please use " - "that!") self.retries = kwargs.pop('retries', 0) self.raise_errors = kwargs.pop('raise_errors', True) self.httpclient = client.construct_http_client(**kwargs) From 3071dfbd0f8e7c8a297f58b6c038cf461691840e Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Thu, 2 Mar 2023 13:26:42 +0000 Subject: [PATCH 801/845] Update master for stable/2023.1 Add file to the reno documentation build to show release notes for stable/2023.1. Use pbr instruction to increment the minor version number automatically so that master versions are higher than the versions on stable/2023.1. Sem-Ver: feature Change-Id: I194c39a2bbfde10c65912835019a8c2f21464569 --- releasenotes/source/2023.1.rst | 6 ++++++ releasenotes/source/index.rst | 1 + 2 files changed, 7 insertions(+) create mode 100644 releasenotes/source/2023.1.rst diff --git a/releasenotes/source/2023.1.rst b/releasenotes/source/2023.1.rst new file mode 100644 index 000000000..d1238479b --- /dev/null +++ b/releasenotes/source/2023.1.rst @@ -0,0 +1,6 @@ +=========================== +2023.1 Series Release Notes +=========================== + +.. release-notes:: + :branch: stable/2023.1 diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index f1aa6a943..5dbb2f331 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -6,6 +6,7 @@ :maxdepth: 1 unreleased + 2023.1 zed yoga xena From 0b271d3b8cd1b28e745dd3d1b066c8dc084ef748 Mon Sep 17 00:00:00 2001 From: elajkat Date: Wed, 21 Dec 2022 14:08:58 +0100 Subject: [PATCH 802/845] OSC: Remove BGP calls to neutronclient With [1] the python binding code in Neutronclient prints warning about future deprecation, change neutron-dynamic-routing osc client code to use totally OpenstackSDK. [1]: https://review.opendev.org/c/openstack/python-neutronclient/+/862371 Related-Bug: #1999774 Depends-On: https://review.opendev.org/c/openstack/openstacksdk/+/869485 Depends-On: https://review.opendev.org/c/openstack/requirements/+/873598 Change-Id: I5dd143e670b70cc42a2ae48fe4bac0cf09416cb5 --- neutronclient/osc/utils.py | 10 ++ .../osc/v2/dynamic_routing/bgp_dragent.py | 89 +++++++++++------- .../osc/v2/dynamic_routing/bgp_peer.py | 38 +++----- .../osc/v2/dynamic_routing/bgp_speaker.py | 92 ++++++++----------- .../unit/osc/v2/dynamic_routing/fakes.py | 31 +++++-- .../v2/dynamic_routing/test_bgp_dragent.py | 30 +++--- .../osc/v2/dynamic_routing/test_bgp_peer.py | 35 ++++--- .../v2/dynamic_routing/test_bgp_speaker.py | 30 +++--- neutronclient/tests/unit/osc/v2/fakes.py | 4 + requirements.txt | 1 + 10 files changed, 192 insertions(+), 168 deletions(-) diff --git a/neutronclient/osc/utils.py b/neutronclient/osc/utils.py index 413ad607c..39b17b640 100644 --- a/neutronclient/osc/utils.py +++ b/neutronclient/osc/utils.py @@ -118,3 +118,13 @@ def _find_identity_resource(identity_client_manager, name_or_id, # The above are borrowed from openstackclient.identity.common. # DO NOT ADD original methods in neutronclient repo to the above area. + + +def _get_columns(item): + column_map = {} + hidden_columns = ['location', 'tenant_id'] + return utils.get_osc_show_columns_for_sdk_resource( + item, + column_map, + hidden_columns + ) diff --git a/neutronclient/osc/v2/dynamic_routing/bgp_dragent.py b/neutronclient/osc/v2/dynamic_routing/bgp_dragent.py index 025022996..4af2008fe 100644 --- a/neutronclient/osc/v2/dynamic_routing/bgp_dragent.py +++ b/neutronclient/osc/v2/dynamic_routing/bgp_dragent.py @@ -15,7 +15,6 @@ from osc_lib import utils from neutronclient._i18n import _ -from neutronclient.osc.v2.dynamic_routing import constants def _format_alive_state(item): @@ -45,11 +44,9 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - speaker_id = client.find_resource(constants.BGP_SPEAKER, - parsed_args.bgp_speaker)['id'] - client.add_bgp_speaker_to_dragent( - parsed_args.dragent_id, {'bgp_speaker_id': speaker_id}) + client = self.app.client_manager.network + speaker_id = client.find_bgp_speaker(parsed_args.bgp_speaker).id + client.add_bgp_speaker_to_dragent(parsed_args.dragent_id, speaker_id) class RemoveBgpSpeakerFromDRAgent(command.Command): @@ -62,9 +59,8 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - speaker_id = client.find_resource(constants.BGP_SPEAKER, - parsed_args.bgp_speaker)['id'] + client = self.app.client_manager.network + speaker_id = client.find_bgp_speaker(parsed_args.bgp_speaker).id client.remove_bgp_speaker_from_dragent(parsed_args.dragent_id, speaker_id) @@ -92,18 +88,30 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - search_opts = {} - client = self.app.client_manager.neutronclient - speaker_id = client.find_resource(constants.BGP_SPEAKER, - parsed_args.bgp_speaker)['id'] - search_opts['bgp_speaker'] = speaker_id - data = client.list_dragents_hosting_bgp_speaker(**search_opts) - headers = ('ID', 'Host', 'State', 'Alive') - columns = ('id', 'host', 'admin_state_up', 'alive') - return (headers, - (utils.get_dict_properties( - s, columns, formatters=_formatters, - ) for s in data['agents'])) + client = self.app.client_manager.network + speaker_id = client.find_bgp_speaker(parsed_args.bgp_speaker).id + data = client.get_bgp_dragents_hosting_speaker(speaker_id) + columns = ( + 'id', + 'agent_type', + 'host', + 'availability_zone', + 'is_alive', + 'is_admin_state_up', + 'binary' + ) + column_headers = ( + 'ID', + 'Agent Type', + 'Host', + 'Availability Zone', + 'Alive', + 'State', + 'Binary' + ) + return (column_headers, + (utils.get_item_properties( + s, columns,) for s in data)) class ListDRAgent(command.Lister): @@ -123,20 +131,31 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - search_opts = {} - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network if parsed_args.bgp_speaker is not None: - search_opts = {} - speaker_id = client.find_resource(constants.BGP_SPEAKER, - parsed_args.bgp_speaker)['id'] - search_opts['bgp_speaker'] = speaker_id - data = client.list_dragents_hosting_bgp_speaker(**search_opts) + speaker_id = client.find_bgp_speaker(parsed_args.bgp_speaker).id + data = client.get_bgp_dragents_hosting_speaker(speaker_id) else: attrs = {'agent_type': 'BGP dynamic routing agent'} - data = client.list_agents(**attrs) - headers = ('ID', 'Host', 'State', 'Alive') - columns = ('id', 'host', 'admin_state_up', 'alive') - return (headers, - (utils.get_dict_properties( - s, columns, formatters=_formatters, - ) for s in data['agents'])) + data = client.agents(**attrs) + columns = ( + 'id', + 'agent_type', + 'host', + 'availability_zone', + 'is_alive', + 'is_admin_state_up', + 'binary' + ) + column_headers = ( + 'ID', + 'Agent Type', + 'Host', + 'Availability Zone', + 'Alive', + 'State', + 'Binary' + ) + return (column_headers, + (utils.get_item_properties( + s, columns,) for s in data)) diff --git a/neutronclient/osc/v2/dynamic_routing/bgp_peer.py b/neutronclient/osc/v2/dynamic_routing/bgp_peer.py index c8e602ebb..0610a3ac0 100644 --- a/neutronclient/osc/v2/dynamic_routing/bgp_peer.py +++ b/neutronclient/osc/v2/dynamic_routing/bgp_peer.py @@ -13,7 +13,6 @@ from osc_lib.command import command from osc_lib import utils -from osc_lib.utils import columns as column_util from neutronclient._i18n import _ from neutronclient.common import exceptions @@ -96,11 +95,10 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network attrs = _get_attrs(self.app.client_manager, parsed_args) - body = {constants.BGP_PEER: attrs} - obj = client.create_bgp_peer(body)[constants.BGP_PEER] - columns, display_columns = column_util.get_columns(obj) + obj = client.create_bgp_peer(**attrs) + display_columns, columns = nc_osc_utils._get_columns(obj) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -118,9 +116,8 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - id = client.find_resource(constants.BGP_PEER, - parsed_args.bgp_peer)['id'] + client = self.app.client_manager.network + id = client.find_bgp_peer(parsed_args.bgp_peer)['id'] client.delete_bgp_peer(id) @@ -128,13 +125,11 @@ class ListBgpPeer(command.Lister): _description = _("List BGP peers") def take_action(self, parsed_args): - data = self.app.client_manager.neutronclient.list_bgp_peers() + data = self.app.client_manager.network.bgp_peers(retrieve_all=True) headers = ('ID', 'Name', 'Peer IP', 'Remote AS') columns = ('id', 'name', 'peer_ip', 'remote_as') return (headers, - (utils.get_dict_properties( - s, columns, - ) for s in data[constants.BGP_PEERS])) + (utils.get_dict_properties(s, columns,) for s in data)) class SetBgpPeer(command.Command): @@ -158,13 +153,10 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - id = client.find_resource(constants.BGP_PEER, - parsed_args.bgp_peer)['id'] + client = self.app.client_manager.network + id = client.find_bgp_peer(parsed_args.bgp_peer)['id'] attrs = _get_attrs(self.app.client_manager, parsed_args) - body = {} - body[constants.BGP_PEER] = attrs - client.update_bgp_peer(id, body) + client.update_bgp_peer(id, **attrs) class ShowBgpPeer(command.ShowOne): @@ -180,10 +172,10 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - id = client.find_resource(constants.BGP_PEER, - parsed_args.bgp_peer)['id'] - obj = client.show_bgp_peer(id)[constants.BGP_PEER] - columns, display_columns = column_util.get_columns(obj) + client = self.app.client_manager.network + id = client.find_bgp_peer(parsed_args.bgp_peer, + ignore_missing=False).id + obj = client.get_bgp_peer(id) + display_columns, columns = nc_osc_utils._get_columns(obj) data = utils.get_dict_properties(obj, columns) return display_columns, data diff --git a/neutronclient/osc/v2/dynamic_routing/bgp_speaker.py b/neutronclient/osc/v2/dynamic_routing/bgp_speaker.py index 171d7f45f..76d8340b9 100644 --- a/neutronclient/osc/v2/dynamic_routing/bgp_speaker.py +++ b/neutronclient/osc/v2/dynamic_routing/bgp_speaker.py @@ -13,7 +13,6 @@ from osc_lib.command import command from osc_lib import utils -from osc_lib.utils import columns as column_util from neutronclient._i18n import _ from neutronclient.osc import utils as nc_osc_utils @@ -87,12 +86,12 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - speaker_id = client.find_resource(constants.BGP_SPEAKER, - parsed_args.bgp_speaker)['id'] - net_id = client.find_resource('network', - parsed_args.network)['id'] - client.add_network_to_bgp_speaker(speaker_id, {'network_id': net_id}) + client = self.app.client_manager.network + speaker_id = client.find_bgp_speaker(parsed_args.bgp_speaker, + ignore_missing=False).id + net_id = client.find_network(parsed_args.network, + ignore_missing=False).id + client.add_gateway_network_to_speaker(speaker_id, net_id) class AddPeerToSpeaker(command.Command): @@ -111,12 +110,10 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - speaker_id = client.find_resource(constants.BGP_SPEAKER, - parsed_args.bgp_speaker)['id'] - peer_id = client.find_resource(constants.BGP_PEER, - parsed_args.bgp_peer)['id'] - client.add_peer_to_bgp_speaker(speaker_id, {'bgp_peer_id': peer_id}) + client = self.app.client_manager.network + speaker_id = client.find_bgp_speaker(parsed_args.bgp_speaker)['id'] + peer_id = client.find_bgp_peer(parsed_args.bgp_peer)['id'] + client.add_bgp_peer_to_speaker(speaker_id, peer_id) class CreateBgpSpeaker(command.ShowOne): @@ -145,12 +142,10 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network attrs = _get_attrs(self.app.client_manager, parsed_args) - body = {} - body[constants.BGP_SPEAKER] = attrs - obj = client.create_bgp_speaker(body)[constants.BGP_SPEAKER] - columns, display_columns = column_util.get_columns(obj) + obj = client.create_bgp_speaker(**attrs) + display_columns, columns = nc_osc_utils._get_columns(obj) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -168,9 +163,8 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - id = client.find_resource(constants.BGP_SPEAKER, - parsed_args.bgp_speaker)['id'] + client = self.app.client_manager.network + id = client.find_bgp_speaker(parsed_args.bgp_speaker)['id'] client.delete_bgp_speaker(id) @@ -186,16 +180,16 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network if parsed_args.agent is not None: - data = client.list_bgp_speaker_on_dragent(parsed_args.agent) + data = client.get_bgp_speakers_hosted_by_dragent(parsed_args.agent) else: - data = client.list_bgp_speakers() + data = client.bgp_speakers(retrieve_all=True) headers = ('ID', 'Name', 'Local AS', 'IP Version') columns = ('id', 'name', 'local_as', 'ip_version') return (headers, (utils.get_dict_properties(s, columns) - for s in data[constants.BGP_SPEAKERS])) + for s in data)) class ListRoutesAdvertisedBySpeaker(command.Lister): @@ -211,10 +205,9 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - speaker_id = client.find_resource(constants.BGP_SPEAKER, - parsed_args.bgp_speaker)['id'] - data = client.list_route_advertised_from_bgp_speaker(speaker_id) + client = self.app.client_manager.network + speaker_id = client.find_bgp_speaker(parsed_args.bgp_speaker)['id'] + data = client.get_advertised_routes_of_speaker(speaker_id) headers = ('Destination', 'Nexthop') columns = ('destination', 'next_hop') return (headers, (utils.get_dict_properties(s, columns) @@ -237,13 +230,10 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - speaker_id = client.find_resource(constants.BGP_SPEAKER, - parsed_args.bgp_speaker)['id'] - net_id = client.find_resource('network', - parsed_args.network)['id'] - client.remove_network_from_bgp_speaker(speaker_id, - {'network_id': net_id}) + client = self.app.client_manager.network + speaker_id = client.find_bgp_speaker(parsed_args.bgp_speaker)['id'] + net_id = client.find_network(parsed_args.network)['id'] + client.remove_gateway_network_from_speaker(speaker_id, net_id) class RemovePeerFromSpeaker(command.Command): @@ -262,13 +252,10 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - speaker_id = client.find_resource(constants.BGP_SPEAKER, - parsed_args.bgp_speaker)['id'] - peer_id = client.find_resource(constants.BGP_PEER, - parsed_args.bgp_peer)['id'] - client.remove_peer_from_bgp_speaker(speaker_id, - {'bgp_peer_id': peer_id}) + client = self.app.client_manager.network + speaker_id = client.find_bgp_speaker(parsed_args.bgp_speaker)['id'] + peer_id = client.find_bgp_peer(parsed_args.bgp_peer)['id'] + client.remove_bgp_peer_from_speaker(speaker_id, peer_id) class SetBgpSpeaker(command.Command): @@ -290,13 +277,10 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - id = client.find_resource(constants.BGP_SPEAKER, - parsed_args.bgp_speaker)['id'] + client = self.app.client_manager.network + id = client.find_bgp_speaker(parsed_args.bgp_speaker)['id'] attrs = _get_attrs(self.app.client_manager, parsed_args) - body = {} - body[constants.BGP_SPEAKER] = attrs - client.update_bgp_speaker(id, body) + client.update_bgp_speaker(id, **attrs) class ShowBgpSpeaker(command.ShowOne): @@ -312,10 +296,10 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - id = client.find_resource(constants.BGP_SPEAKER, - parsed_args.bgp_speaker)['id'] - obj = client.show_bgp_speaker(id)[constants.BGP_SPEAKER] - columns, display_columns = column_util.get_columns(obj) + client = self.app.client_manager.network + id = client.find_bgp_speaker(parsed_args.bgp_speaker, + ignore_missing=False).id + obj = client.get_bgp_speaker(id) + display_columns, columns = nc_osc_utils._get_columns(obj) data = utils.get_dict_properties(obj, columns) return display_columns, data diff --git a/neutronclient/tests/unit/osc/v2/dynamic_routing/fakes.py b/neutronclient/tests/unit/osc/v2/dynamic_routing/fakes.py index c496a3f66..188f06871 100644 --- a/neutronclient/tests/unit/osc/v2/dynamic_routing/fakes.py +++ b/neutronclient/tests/unit/osc/v2/dynamic_routing/fakes.py @@ -10,10 +10,12 @@ # License for the specific language governing permissions and limitations # under the License. -import copy from unittest import mock import uuid +from openstack.network.v2 import agent as _agent +from openstack.network.v2 import bgp_peer as _bgp_peer +from openstack.network.v2 import bgp_speaker as _bgp_speaker from neutronclient.tests.unit.osc.v2 import fakes @@ -26,6 +28,17 @@ def setUp(self): cmd_resource=None, parent_id=None, fields=None: {'id': name_or_id}) + self.networkclient.find_bgp_speaker = mock.Mock( + side_effect=lambda name_or_id, project_id=None, + cmd_resource=None, parent_id=None, fields=None, + ignore_missing=False: + _bgp_speaker.BgpSpeaker(id=name_or_id)) + self.networkclient.find_bgp_peer = mock.Mock( + side_effect=lambda name_or_id, project_id=None, + cmd_resource=None, parent_id=None, fields=None, + ignore_missing=False: + _bgp_peer.BgpPeer(id=name_or_id)) + class FakeBgpSpeaker(object): """Fake one or more bgp speakers.""" @@ -48,8 +61,9 @@ def create_one_bgp_speaker(attrs=None): # Overwrite default attributes. bgp_speaker_attrs.update(attrs) + ret_bgp_speaker = _bgp_speaker.BgpSpeaker(**bgp_speaker_attrs) - return copy.deepcopy(bgp_speaker_attrs) + return ret_bgp_speaker @staticmethod def create_bgp_speakers(attrs=None, count=1): @@ -61,7 +75,7 @@ def create_bgp_speakers(attrs=None, count=1): bgp_speaker = FakeBgpSpeaker.create_one_bgp_speaker(attrs) bgp_speakers.append(bgp_speaker) - return {'bgp_speakers': bgp_speakers} + return bgp_speakers class FakeBgpPeer(object): @@ -82,8 +96,9 @@ def create_one_bgp_peer(attrs=None): # Overwrite default attributes. bgp_peer_attrs.update(attrs) + ret_bgp_peer = _bgp_peer.BgpPeer(**bgp_peer_attrs) - return copy.deepcopy(bgp_peer_attrs) + return ret_bgp_peer @staticmethod def create_bgp_peers(attrs=None, count=1): @@ -93,7 +108,7 @@ def create_bgp_peers(attrs=None, count=1): bgp_peer = FakeBgpPeer.create_one_bgp_peer(attrs) bgp_peers.append(bgp_peer) - return {'bgp_peers': bgp_peers} + return bgp_peers class FakeDRAgent(object): @@ -106,6 +121,7 @@ def create_one_dragent(attrs=None): dragent_attrs = { 'binary': 'neutron-bgp-dragent', 'admin_state_up': True, + 'availability_zone': None, 'alive': True, 'topic': 'bgp_dragent', 'host': 'network-' + uuid.uuid4().hex, @@ -116,8 +132,7 @@ def create_one_dragent(attrs=None): # Overwrite default attributes. dragent_attrs.update(attrs) - - return copy.deepcopy(dragent_attrs) + return _agent.Agent(**dragent_attrs) @staticmethod def create_dragents(attrs=None, count=1): @@ -127,4 +142,4 @@ def create_dragents(attrs=None, count=1): agent = FakeDRAgent.create_one_dragent(attrs) agents.append(agent) - return {'agents': agents} + return agents diff --git a/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_dragent.py b/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_dragent.py index 7a94b323a..89ba20d93 100644 --- a/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_dragent.py +++ b/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_dragent.py @@ -39,15 +39,14 @@ def test_add_bgp_speaker_to_dragent(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - with mock.patch.object(self.neutronclient, + with mock.patch.object(self.networkclient, "add_bgp_speaker_to_dragent", return_value=None): result = self.cmd.take_action(parsed_args) - self.neutronclient.add_bgp_speaker_to_dragent.\ + self.networkclient.add_bgp_speaker_to_dragent.\ assert_called_once_with( - self._bgp_dragent_id, - {'bgp_speaker_id': self._bgp_speaker_id}) + self._bgp_dragent_id, self._bgp_speaker_id) self.assertIsNone(result) @@ -75,11 +74,11 @@ def test_remove_bgp_speaker_from_dragent(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - with mock.patch.object(self.neutronclient, + with mock.patch.object(self.networkclient, "remove_bgp_speaker_from_dragent", return_value=None): result = self.cmd.take_action(parsed_args) - self.neutronclient.remove_bgp_speaker_from_dragent.\ + self.networkclient.remove_bgp_speaker_from_dragent.\ assert_called_once_with(self._bgp_dragent_id, self._bgp_speaker_id) self.assertIsNone(result) @@ -90,12 +89,16 @@ class TestListDRAgentsHostingBgpSpeaker(fakes.TestNeutronDynamicRoutingOSCV2): _bgp_speaker_id = _bgp_speaker['id'] attrs = {'bgp_speaker_id': _bgp_speaker_id} _bgp_dragents = fakes.FakeDRAgent.create_dragents(attrs) - columns = ('ID', 'Host', 'State', 'Alive') + columns = ('ID', 'Agent Type', 'Host', 'Availability Zone', + 'Alive', 'State', 'Binary') data = [(_bgp_dragent['id'], + _bgp_dragent['agent_type'], _bgp_dragent['host'], + _bgp_dragent['availability_zone'], _bgp_dragent['admin_state_up'], - ':-)' if _bgp_dragent['alive'] else 'XXX') - for _bgp_dragent in _bgp_dragents['agents']] + _bgp_dragent['alive'], + _bgp_dragent['binary'],) + for _bgp_dragent in _bgp_dragents] def setUp(self): super(TestListDRAgentsHostingBgpSpeaker, self).setUp() @@ -112,12 +115,11 @@ def test_list_dragents_hosting_bgp_speaker(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - with mock.patch.object(self.neutronclient, - "list_dragents_hosting_bgp_speaker", + with mock.patch.object(self.networkclient, + "get_bgp_dragents_hosting_speaker", return_value=self._bgp_dragents): columns, data = self.cmd.take_action(parsed_args) - attrs = {'bgp_speaker': self._bgp_speaker_id} - self.neutronclient.list_dragents_hosting_bgp_speaker.\ - assert_called_once_with(**attrs) + self.networkclient.get_bgp_dragents_hosting_speaker.\ + assert_called_once_with(self._bgp_speaker_id) self.assertEqual(self.columns, columns) self.assertListEqual(self.data, list(data)) diff --git a/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_peer.py b/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_peer.py index 506c86d93..73b6bf7ab 100644 --- a/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_peer.py +++ b/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_peer.py @@ -20,7 +20,7 @@ class TestListBgpPeer(fakes.TestNeutronDynamicRoutingOSCV2): _bgp_peers = fakes.FakeBgpPeer.create_bgp_peers(count=1) columns = ('ID', 'Name', 'Peer IP', 'Remote AS') data = [] - for _bgp_peer in _bgp_peers['bgp_peers']: + for _bgp_peer in _bgp_peers: data.append(( _bgp_peer['id'], _bgp_peer['name'], @@ -30,7 +30,7 @@ class TestListBgpPeer(fakes.TestNeutronDynamicRoutingOSCV2): def setUp(self): super(TestListBgpPeer, self).setUp() - self.neutronclient.list_bgp_peers = mock.Mock( + self.networkclient.bgp_peers = mock.Mock( return_value=self._bgp_peers ) @@ -41,7 +41,8 @@ def test_bgp_peer_list(self): parsed_args = self.check_parser(self.cmd, [], []) columns, data = self.cmd.take_action(parsed_args) - self.neutronclient.list_bgp_peers.assert_called_once_with() + self.networkclient.bgp_peers.assert_called_once_with( + retrieve_all=True) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -53,7 +54,7 @@ class TestDeleteBgpPeer(fakes.TestNeutronDynamicRoutingOSCV2): def setUp(self): super(TestDeleteBgpPeer, self).setUp() - self.neutronclient.delete_bgp_peer = mock.Mock(return_value=None) + self.networkclient.delete_bgp_peer = mock.Mock(return_value=None) self.cmd = bgp_peer.DeleteBgpPeer(self.app, self.namespace) @@ -68,7 +69,7 @@ def test_delete_bgp_peer(self): result = self.cmd.take_action(parsed_args) - self.neutronclient.delete_bgp_peer.assert_called_once_with( + self.networkclient.delete_bgp_peer.assert_called_once_with( self._bgp_peer['name']) self.assertIsNone(result) @@ -80,31 +81,30 @@ class TestShowBgpPeer(fakes.TestNeutronDynamicRoutingOSCV2): _one_bgp_peer['id'], _one_bgp_peer['name'], _one_bgp_peer['peer_ip'], + _one_bgp_peer['tenant_id'], _one_bgp_peer['remote_as'], - _one_bgp_peer['tenant_id'] ) - _bgp_peer = {'bgp_peer': _one_bgp_peer} + _bgp_peer = _one_bgp_peer _bgp_peer_name = _one_bgp_peer['name'] columns = ( 'auth_type', 'id', 'name', 'peer_ip', + 'project_id', 'remote_as', - 'tenant_id' ) def setUp(self): super(TestShowBgpPeer, self).setUp() - self.neutronclient.show_bgp_peer = mock.Mock( + self.networkclient.get_bgp_peer = mock.Mock( return_value=self._bgp_peer ) - bgp_peer.get_bgp_peer_id = mock.Mock(return_value=self._bgp_peer_name) # Get the command object to test self.cmd = bgp_peer.ShowBgpPeer(self.app, self.namespace) - def test_bgp_peer_list(self): + def test_bgp_peer_show(self): arglist = [ self._bgp_peer_name, ] @@ -114,7 +114,7 @@ def test_bgp_peer_list(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) data = self.cmd.take_action(parsed_args) - self.neutronclient.show_bgp_peer.assert_called_once_with( + self.networkclient.get_bgp_peer.assert_called_once_with( self._bgp_peer_name) self.assertEqual(self.columns, data[0]) self.assertEqual(self.data, data[1]) @@ -126,7 +126,7 @@ class TestSetBgpPeer(fakes.TestNeutronDynamicRoutingOSCV2): def setUp(self): super(TestSetBgpPeer, self).setUp() - self.neutronclient.update_bgp_peer = mock.Mock(return_value=None) + self.networkclient.update_bgp_peer = mock.Mock(return_value=None) bgp_peer.get_bgp_peer_id = mock.Mock(return_value=self._bgp_peer_name) self.cmd = bgp_peer.SetBgpPeer(self.app, self.namespace) @@ -144,10 +144,7 @@ def test_set_bgp_peer(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - attrs = {'bgp_peer': { - 'name': 'noob', - 'password': None} - } - self.neutronclient.update_bgp_peer.assert_called_once_with( - self._bgp_peer_name, attrs) + attrs = {'name': 'noob', 'password': None} + self.networkclient.update_bgp_peer.assert_called_once_with( + self._bgp_peer_name, **attrs) self.assertIsNone(result) diff --git a/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_speaker.py b/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_speaker.py index 159746875..c5e42e588 100644 --- a/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_speaker.py +++ b/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_speaker.py @@ -20,7 +20,7 @@ class TestListBgpSpeaker(fakes.TestNeutronDynamicRoutingOSCV2): _bgp_speakers = fakes.FakeBgpSpeaker.create_bgp_speakers() columns = ('ID', 'Name', 'Local AS', 'IP Version') data = [] - for _bgp_speaker in _bgp_speakers['bgp_speakers']: + for _bgp_speaker in _bgp_speakers: data.append(( _bgp_speaker['id'], _bgp_speaker['name'], @@ -30,7 +30,7 @@ class TestListBgpSpeaker(fakes.TestNeutronDynamicRoutingOSCV2): def setUp(self): super(TestListBgpSpeaker, self).setUp() - self.neutronclient.list_bgp_speakers = mock.Mock( + self.networkclient.bgp_speakers = mock.Mock( return_value=self._bgp_speakers ) @@ -41,7 +41,9 @@ def test_bgp_speaker_list(self): parsed_args = self.check_parser(self.cmd, [], []) columns, data = self.cmd.take_action(parsed_args) - self.neutronclient.list_bgp_speakers.assert_called_once_with() + self.networkclient.bgp_speakers.assert_called_once_with( + retrieve_all=True) + self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -53,7 +55,7 @@ class TestDeleteBgpSpeaker(fakes.TestNeutronDynamicRoutingOSCV2): def setUp(self): super(TestDeleteBgpSpeaker, self).setUp() - self.neutronclient.delete_bgp_speaker = mock.Mock(return_value=None) + self.networkclient.delete_bgp_speaker = mock.Mock(return_value=None) self.cmd = bgp_speaker.DeleteBgpSpeaker(self.app, self.namespace) @@ -68,7 +70,7 @@ def test_delete_bgp_speaker(self): result = self.cmd.take_action(parsed_args) - self.neutronclient.delete_bgp_speaker.assert_called_once_with( + self.networkclient.delete_bgp_speaker.assert_called_once_with( self._bgp_speaker['name']) self.assertIsNone(result) @@ -86,7 +88,7 @@ class TestShowBgpSpeaker(fakes.TestNeutronDynamicRoutingOSCV2): _one_bgp_speaker['peers'], _one_bgp_speaker['tenant_id'] ) - _bgp_speaker = {'bgp_speaker': _one_bgp_speaker} + _bgp_speaker = _one_bgp_speaker _bgp_speaker_name = _one_bgp_speaker['name'] columns = ( 'advertise_floating_ip_host_routes', @@ -97,13 +99,13 @@ class TestShowBgpSpeaker(fakes.TestNeutronDynamicRoutingOSCV2): 'name', 'networks', 'peers', - 'tenant_id' + 'project_id' ) def setUp(self): super(TestShowBgpSpeaker, self).setUp() - self.neutronclient.show_bgp_speaker = mock.Mock( + self.networkclient.get_bgp_speaker = mock.Mock( return_value=self._bgp_speaker ) # Get the command object to test @@ -119,7 +121,7 @@ def test_bgp_speaker_show(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) data = self.cmd.take_action(parsed_args) - self.neutronclient.show_bgp_speaker.assert_called_once_with( + self.networkclient.get_bgp_speaker.assert_called_once_with( self._bgp_speaker_name) self.assertEqual(self.columns, data[0]) self.assertEqual(self.data, data[1]) @@ -131,7 +133,7 @@ class TestSetBgpSpeaker(fakes.TestNeutronDynamicRoutingOSCV2): def setUp(self): super(TestSetBgpSpeaker, self).setUp() - self.neutronclient.update_bgp_speaker = mock.Mock( + self.networkclient.update_bgp_speaker = mock.Mock( return_value=None) self.cmd = bgp_speaker.SetBgpSpeaker(self.app, self.namespace) @@ -149,9 +151,7 @@ def test_set_bgp_speaker(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - attrs = {'bgp_speaker': { - 'name': 'noob'} - } - self.neutronclient.update_bgp_speaker.assert_called_once_with( - self._bgp_speaker_name, attrs) + attrs = {'name': 'noob'} + self.networkclient.update_bgp_speaker.assert_called_once_with( + self._bgp_speaker_name, **attrs) self.assertIsNone(result) diff --git a/neutronclient/tests/unit/osc/v2/fakes.py b/neutronclient/tests/unit/osc/v2/fakes.py index c7876b645..7fec7347c 100644 --- a/neutronclient/tests/unit/osc/v2/fakes.py +++ b/neutronclient/tests/unit/osc/v2/fakes.py @@ -26,6 +26,10 @@ def setUp(self): self.app.client_manager.session = mock.Mock() self.app.client_manager.neutronclient = mock.Mock() self.neutronclient = self.app.client_manager.neutronclient + + self.app.client_manager.network = mock.Mock() + self.networkclient = self.app.client_manager.network + self.addCleanup(mock.patch.stopall) # TODO(amotoki): Move this to osc_lib diff --git a/requirements.txt b/requirements.txt index b85e6b69a..2e9e22d08 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,6 +10,7 @@ cliff>=3.4.0 # Apache-2.0 debtcollector>=1.2.0 # Apache-2.0 iso8601>=0.1.11 # MIT netaddr>=0.7.18 # BSD +openstacksdk>=1.0.0 # Apache-2.0 osc-lib>=1.12.0 # Apache-2.0 oslo.i18n>=3.15.3 # Apache-2.0 oslo.log>=3.36.0 # Apache-2.0 From 52653c95e050437b27492404649e42d01b79732b Mon Sep 17 00:00:00 2001 From: Slawek Kaplonski Date: Tue, 11 Apr 2023 13:12:45 +0200 Subject: [PATCH 803/845] Remove the CLI code from the Neutron client. This project no longer provides CLI support. All code has been removed. Please use openstack CLI instead This patch removes entry point for neutron CLI too, bash completion script and CLI related functional and unit tests but it still keeps neutronclient/neutron/v2_0 module as this is used by e.g. Nova. Co-Authored-By: Slawek Kaplonski Closes-Bug: #2003861 Change-Id: I5b6c735d1ff931d7991a42b262e7faa47b75edeb --- .zuul.yaml | 52 - README.rst | 3 +- doc/source/cli/index.rst | 14 +- doc/source/cli/neutron-reference.rst | 48 - doc/source/cli/neutron.rst | 412 ------ .../contributor/cli_option_guideline.rst | 263 ---- .../contributor/client_command_extensions.rst | 97 -- doc/source/contributor/index.rst | 2 - doc/source/contributor/transition_to_osc.rst | 32 +- doc/source/index.rst | 10 +- neutron_test.sh | 165 --- neutronclient/shell.py | 654 --------- neutronclient/tests/functional/__init__.py | 0 .../tests/functional/adv-svcs/__init__.py | 0 .../adv-svcs/test_readonly_neutron_fwaas.py | 42 - .../adv-svcs/test_readonly_neutron_vpn.py | 57 - neutronclient/tests/functional/base.py | 80 -- .../tests/functional/core/__init__.py | 0 .../functional/core/test_cli_formatter.py | 59 - .../tests/functional/core/test_clientlib.py | 61 - .../tests/functional/core/test_common.py | 33 - .../tests/functional/core/test_purge.py | 174 --- .../functional/core/test_readonly_neutron.py | 136 -- .../functional/core/test_subnet_create.py | 36 - neutronclient/tests/functional/hooks/fwaas | 2 - .../tests/functional/hooks/gate_hook.sh | 42 - .../tests/functional/hooks/post_test_hook.sh | 68 - neutronclient/tests/functional/hooks/vpnaas | 1 - neutronclient/tests/unit/bgp/__init__.py | 0 .../unit/bgp/test_cli20_dragentscheduler.py | 66 - .../tests/unit/bgp/test_cli20_peer.py | 224 --- .../tests/unit/bgp/test_cli20_speaker.py | 273 ---- neutronclient/tests/unit/flavor/__init__.py | 0 .../tests/unit/flavor/test_cli20_flavor.py | 154 --- .../unit/flavor/test_cli20_flavor_profile.py | 122 -- neutronclient/tests/unit/fw/__init__.py | 0 .../tests/unit/fw/test_cli20_firewall.py | 170 --- .../unit/fw/test_cli20_firewallpolicy.py | 228 ---- .../tests/unit/fw/test_cli20_firewallrule.py | 251 ---- neutronclient/tests/unit/lb/__init__.py | 0 .../tests/unit/lb/test_cli20_healthmonitor.py | 202 --- .../tests/unit/lb/test_cli20_member.py | 118 -- .../tests/unit/lb/test_cli20_pool.py | 166 --- neutronclient/tests/unit/lb/test_cli20_vip.py | 206 --- neutronclient/tests/unit/lb/v2/__init__.py | 0 .../unit/lb/v2/test_cli20_healthmonitor.py | 175 --- .../tests/unit/lb/v2/test_cli20_l7policy.py | 260 ---- .../tests/unit/lb/v2/test_cli20_l7rule.py | 210 --- .../tests/unit/lb/v2/test_cli20_listener.py | 194 --- .../unit/lb/v2/test_cli20_loadbalancer.py | 213 --- .../tests/unit/lb/v2/test_cli20_member.py | 171 --- .../tests/unit/lb/v2/test_cli20_pool.py | 202 --- neutronclient/tests/unit/qos/__init__.py | 0 .../qos/test_cli20_bandwidth_limit_rule.py | 137 -- .../unit/qos/test_cli20_dscp_marking_rule.py | 91 -- .../qos/test_cli20_minimum_bandwidth_rule.py | 143 -- .../tests/unit/qos/test_cli20_policy.py | 192 --- .../tests/unit/qos/test_cli20_rule.py | 44 - .../unit/test_auto_allocated_topology.py | 76 -- neutronclient/tests/unit/test_cli20.py | 1215 ----------------- .../tests/unit/test_cli20_address_scope.py | 175 --- neutronclient/tests/unit/test_cli20_agents.py | 97 -- .../tests/unit/test_cli20_agentschedulers.py | 209 --- neutronclient/tests/unit/test_cli20_az.py | 31 - .../tests/unit/test_cli20_extensions.py | 45 - .../tests/unit/test_cli20_floatingips.py | 193 --- .../tests/unit/test_cli20_metering.py | 99 -- .../tests/unit/test_cli20_network.py | 698 ---------- .../test_cli20_network_ip_availability.py | 54 - neutronclient/tests/unit/test_cli20_port.py | 794 ----------- neutronclient/tests/unit/test_cli20_purge.py | 100 -- neutronclient/tests/unit/test_cli20_rbac.py | 134 -- neutronclient/tests/unit/test_cli20_router.py | 434 ------ .../tests/unit/test_cli20_securitygroup.py | 657 --------- .../tests/unit/test_cli20_servicetype.py | 55 - neutronclient/tests/unit/test_cli20_subnet.py | 687 ---------- .../tests/unit/test_cli20_subnetpool.py | 208 --- neutronclient/tests/unit/test_cli20_tag.py | 131 -- .../tests/unit/test_client_extension.py | 221 --- neutronclient/tests/unit/test_name_or_id.py | 244 ---- neutronclient/tests/unit/test_quota.py | 99 -- neutronclient/tests/unit/test_shell.py | 366 ----- neutronclient/tests/unit/vpn/__init__.py | 0 .../unit/vpn/test_cli20_endpoint_group.py | 145 -- .../tests/unit/vpn/test_cli20_ikepolicy.py | 241 ---- .../vpn/test_cli20_ipsec_site_connection.py | 342 ----- .../tests/unit/vpn/test_cli20_ipsecpolicy.py | 249 ---- .../tests/unit/vpn/test_cli20_vpnservice.py | 157 --- neutronclient/tests/unit/vpn/test_utils.py | 130 -- .../remove-cli-code-53969e9aa927e530.yaml | 12 + setup.cfg | 287 ---- tools/neutron.bash_completion | 27 - tox.ini | 5 - 93 files changed, 22 insertions(+), 15350 deletions(-) delete mode 100644 doc/source/cli/neutron-reference.rst delete mode 100644 doc/source/cli/neutron.rst delete mode 100644 doc/source/contributor/cli_option_guideline.rst delete mode 100644 doc/source/contributor/client_command_extensions.rst delete mode 100755 neutron_test.sh delete mode 100644 neutronclient/shell.py delete mode 100644 neutronclient/tests/functional/__init__.py delete mode 100644 neutronclient/tests/functional/adv-svcs/__init__.py delete mode 100644 neutronclient/tests/functional/adv-svcs/test_readonly_neutron_fwaas.py delete mode 100644 neutronclient/tests/functional/adv-svcs/test_readonly_neutron_vpn.py delete mode 100644 neutronclient/tests/functional/base.py delete mode 100644 neutronclient/tests/functional/core/__init__.py delete mode 100644 neutronclient/tests/functional/core/test_cli_formatter.py delete mode 100644 neutronclient/tests/functional/core/test_clientlib.py delete mode 100644 neutronclient/tests/functional/core/test_common.py delete mode 100644 neutronclient/tests/functional/core/test_purge.py delete mode 100644 neutronclient/tests/functional/core/test_readonly_neutron.py delete mode 100644 neutronclient/tests/functional/core/test_subnet_create.py delete mode 100644 neutronclient/tests/functional/hooks/fwaas delete mode 100755 neutronclient/tests/functional/hooks/gate_hook.sh delete mode 100755 neutronclient/tests/functional/hooks/post_test_hook.sh delete mode 100644 neutronclient/tests/functional/hooks/vpnaas delete mode 100644 neutronclient/tests/unit/bgp/__init__.py delete mode 100644 neutronclient/tests/unit/bgp/test_cli20_dragentscheduler.py delete mode 100644 neutronclient/tests/unit/bgp/test_cli20_peer.py delete mode 100644 neutronclient/tests/unit/bgp/test_cli20_speaker.py delete mode 100644 neutronclient/tests/unit/flavor/__init__.py delete mode 100644 neutronclient/tests/unit/flavor/test_cli20_flavor.py delete mode 100644 neutronclient/tests/unit/flavor/test_cli20_flavor_profile.py delete mode 100644 neutronclient/tests/unit/fw/__init__.py delete mode 100644 neutronclient/tests/unit/fw/test_cli20_firewall.py delete mode 100644 neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py delete mode 100644 neutronclient/tests/unit/fw/test_cli20_firewallrule.py delete mode 100644 neutronclient/tests/unit/lb/__init__.py delete mode 100644 neutronclient/tests/unit/lb/test_cli20_healthmonitor.py delete mode 100644 neutronclient/tests/unit/lb/test_cli20_member.py delete mode 100644 neutronclient/tests/unit/lb/test_cli20_pool.py delete mode 100644 neutronclient/tests/unit/lb/test_cli20_vip.py delete mode 100644 neutronclient/tests/unit/lb/v2/__init__.py delete mode 100644 neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py delete mode 100644 neutronclient/tests/unit/lb/v2/test_cli20_l7policy.py delete mode 100644 neutronclient/tests/unit/lb/v2/test_cli20_l7rule.py delete mode 100644 neutronclient/tests/unit/lb/v2/test_cli20_listener.py delete mode 100644 neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py delete mode 100644 neutronclient/tests/unit/lb/v2/test_cli20_member.py delete mode 100644 neutronclient/tests/unit/lb/v2/test_cli20_pool.py delete mode 100644 neutronclient/tests/unit/qos/__init__.py delete mode 100644 neutronclient/tests/unit/qos/test_cli20_bandwidth_limit_rule.py delete mode 100644 neutronclient/tests/unit/qos/test_cli20_dscp_marking_rule.py delete mode 100644 neutronclient/tests/unit/qos/test_cli20_minimum_bandwidth_rule.py delete mode 100644 neutronclient/tests/unit/qos/test_cli20_policy.py delete mode 100644 neutronclient/tests/unit/qos/test_cli20_rule.py delete mode 100644 neutronclient/tests/unit/test_auto_allocated_topology.py delete mode 100644 neutronclient/tests/unit/test_cli20.py delete mode 100644 neutronclient/tests/unit/test_cli20_address_scope.py delete mode 100644 neutronclient/tests/unit/test_cli20_agents.py delete mode 100644 neutronclient/tests/unit/test_cli20_agentschedulers.py delete mode 100644 neutronclient/tests/unit/test_cli20_az.py delete mode 100644 neutronclient/tests/unit/test_cli20_extensions.py delete mode 100644 neutronclient/tests/unit/test_cli20_floatingips.py delete mode 100644 neutronclient/tests/unit/test_cli20_metering.py delete mode 100644 neutronclient/tests/unit/test_cli20_network.py delete mode 100644 neutronclient/tests/unit/test_cli20_network_ip_availability.py delete mode 100644 neutronclient/tests/unit/test_cli20_port.py delete mode 100644 neutronclient/tests/unit/test_cli20_purge.py delete mode 100644 neutronclient/tests/unit/test_cli20_rbac.py delete mode 100644 neutronclient/tests/unit/test_cli20_router.py delete mode 100644 neutronclient/tests/unit/test_cli20_securitygroup.py delete mode 100644 neutronclient/tests/unit/test_cli20_servicetype.py delete mode 100644 neutronclient/tests/unit/test_cli20_subnet.py delete mode 100644 neutronclient/tests/unit/test_cli20_subnetpool.py delete mode 100644 neutronclient/tests/unit/test_cli20_tag.py delete mode 100644 neutronclient/tests/unit/test_client_extension.py delete mode 100644 neutronclient/tests/unit/test_name_or_id.py delete mode 100644 neutronclient/tests/unit/test_quota.py delete mode 100644 neutronclient/tests/unit/test_shell.py delete mode 100644 neutronclient/tests/unit/vpn/__init__.py delete mode 100644 neutronclient/tests/unit/vpn/test_cli20_endpoint_group.py delete mode 100644 neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py delete mode 100644 neutronclient/tests/unit/vpn/test_cli20_ipsec_site_connection.py delete mode 100644 neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py delete mode 100644 neutronclient/tests/unit/vpn/test_cli20_vpnservice.py delete mode 100644 neutronclient/tests/unit/vpn/test_utils.py create mode 100644 releasenotes/notes/remove-cli-code-53969e9aa927e530.yaml delete mode 100644 tools/neutron.bash_completion diff --git a/.zuul.yaml b/.zuul.yaml index 7e23f93e8..0bbe8f4dc 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -7,12 +7,6 @@ - lib-forward-testing-python3 - release-notes-jobs-python3 - openstackclient-plugin-jobs - check: - jobs: - - neutronclient-functional - gate: - jobs: - - neutronclient-functional experimental: jobs: - neutronclient-grenade-neutron-lib: @@ -20,52 +14,6 @@ - ^(test-|)requirements.txt$ - ^setup.cfg$ -- job: - name: neutronclient-functional - parent: devstack-tox-functional - irrelevant-files: - - ^.*\.rst$ - - ^doc/.*$ - - ^releasenotes/.*$ - required-projects: - - openstack/python-neutronclient - - openstack/neutron - - openstack/neutron-vpnaas - vars: - tox_envlist: functional - devstack_services: - # NOTE: neutronclient.tests.functional.base.ClientTestBase does not - # support HTTPS endpoints now, so tls-proxy needs to be disabled. - tls-proxy: false - # Disable OVN services - br-ex-tcpdump: false - br-int-flows: false - ovn-controller: false - ovn-northd: false - ovs-vswitchd: false - ovsdb-server: false - q-ovn-metadata-agent: false - # Neutron services - q-agt: true - q-dhcp: true - q-l3: true - q-meta: true - neutron-network-segment-range: true - neutron-segments: true - q-metering: true - q-qos: true - neutron-tag-ports-during-bulk-creation: true - neutron-conntrack-helper: true - devstack_localrc: - USE_PYTHON3: true - LIBS_FROM_GIT: python-neutronclient - Q_AGENT: openvswitch - Q_ML2_TENANT_NETWORK_TYPE: vxlan - Q_ML2_PLUGIN_MECHANISM_DRIVERS: openvswitch - devstack_plugins: - neutron: https://opendev.org/openstack/neutron - neutron-vpnaas: https://opendev.org/openstack/neutron-vpnaas - - job: name: neutronclient-grenade-neutron-lib parent: grenade diff --git a/README.rst b/README.rst index 6f57c698c..a5e5300d6 100644 --- a/README.rst +++ b/README.rst @@ -15,8 +15,7 @@ Python bindings to the Neutron API :alt: Latest Version This is a client library for Neutron built on the Neutron API. It -provides a Python API (the ``neutronclient`` module) and a command-line tool -(``neutron``). +provides a Python API (the ``neutronclient`` module). * License: Apache License, Version 2.0 * `PyPi`_ - package installation diff --git a/doc/source/cli/index.rst b/doc/source/cli/index.rst index 0a7d54d9f..4b1f4720a 100644 --- a/doc/source/cli/index.rst +++ b/doc/source/cli/index.rst @@ -24,10 +24,9 @@ Using CLI ========= -There are two CLIs which support the Networking API: -`OpenStackClient (OSC) +There is `OpenStackClient (OSC) `__ -and :doc:`neutron CLI ` (deprecated). +which support the Networking API OpenStackClient --------------- @@ -49,15 +48,8 @@ neutron CLI .. warning:: - neutron CLI is now deprecated and will be removed in the future. - Use openstack CLI instead. See `openstack CLI command list + neutron CLI is removed. Use openstack CLI instead. See `openstack CLI command list `__ and :doc:`its extensions for advanced networking services `. The command mapping from neutron CLI to openstack CLI is available `here `__. - -.. toctree:: - :maxdepth: 2 - - neutron CLI guide - neutron CLI reference diff --git a/doc/source/cli/neutron-reference.rst b/doc/source/cli/neutron-reference.rst deleted file mode 100644 index d6640ea0d..000000000 --- a/doc/source/cli/neutron-reference.rst +++ /dev/null @@ -1,48 +0,0 @@ -.. - 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. - - - Convention for heading levels in Neutron devref: - ======= Heading 0 (reserved for the title in a document) - ------- Heading 1 - ~~~~~~~ Heading 2 - +++++++ Heading 3 - ''''''' Heading 4 - (Avoid deeper levels because they do not render well.) - -===================== -neutron CLI reference -===================== - -.. warning:: - - neutron CLI is now deprecated and will be removed in the future. - Use openstack CLI instead. See `openstack CLI command list - `__ - and :doc:`its extensions for advanced networking services `. - The command mapping from neutron CLI to openstack CLI is available - `here `__. - -neutron usage -------------- - -.. autoprogram-cliff:: neutronclient.shell.NeutronShell - :application: neutron - :arguments: 2.0 - -neutron API v2.0 commands -------------------------- - -.. autoprogram-cliff:: neutron.cli.v2 - :application: neutron - diff --git a/doc/source/cli/neutron.rst b/doc/source/cli/neutron.rst deleted file mode 100644 index 7ca652771..000000000 --- a/doc/source/cli/neutron.rst +++ /dev/null @@ -1,412 +0,0 @@ -.. - 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. - - - Convention for heading levels in Neutron devref: - ======= Heading 0 (reserved for the title in a document) - ------- Heading 1 - ~~~~~~~ Heading 2 - +++++++ Heading 3 - ''''''' Heading 4 - (Avoid deeper levels because they do not render well.) - -Using neutron CLI -================= - -The **neutron** shell utility interacts with OpenStack Networking API from the -command-line. It supports the entire features of OpenStack Networking API. - -.. warning:: - - neutron CLI is now deprecated and will be removed in the future. - Use openstack CLI instead. See `openstack CLI command list - `__ - and :doc:`its extensions for advanced networking services `. - The command mapping from neutron CLI to openstack CLI is available - `here `__. - -Basic Usage ------------ - -In order to use the CLI, you must provide your OpenStack username, password, -project, domain information for both user and project, and auth endpoint. Use -the corresponding configuration options (``--os-username``, ``--os-password``, -``--os-project-name``, ``--os-user-domain-id``, ``os-project-domain-id``, and -``--os-auth-url``), but it is easier to set them in environment variables. - -.. code-block:: shell - - export OS_USERNAME=user - export OS_PASSWORD=pass - export OS_PROJECT_NAME=project - export OS_USER_DOMAIN_ID=default - export OS_PROJECT_DOMAIN_ID=default - export OS_AUTH_URL=http://auth.example.com:5000/v3 - -If you are using Identity v2.0 API (DEPRECATED), you don't need to pass domain -information. - -.. code-block:: shell - - export OS_USERNAME=user - export OS_PASSWORD=pass - export OS_TENANT_NAME=tenant - export OS_AUTH_URL=http://auth.example.com:5000/v2.0 - -Once you've configured your authentication parameters, you can run **neutron** -commands. All commands take the form of: - -.. code-block:: none - - neutron [arguments...] - -Run **neutron help** to get a full list of all possible commands, and run -**neutron help ** to get detailed help for that command. - -Using with os-client-config -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -`os-client-config `_ -provides more convenient way to manage a collection of client configurations -and you can easily switch multiple OpenStack-based configurations. - -To use os-client-config, you first need to prepare -``~/.config/openstack/clouds.yaml`` like the following. - -.. code-block:: yaml - - clouds: - devstack: - auth: - auth_url: http://auth.example.com:5000 - password: your-secret - project_domain_id: default - project_name: demo - user_domain_id: default - username: demo - identity_api_version: '3' - region_name: RegionOne - devstack-admin: - auth: - auth_url: http://auth.example.com:35357 - password: another-secret - project_domain_id: default - project_name: admin - user_domain_id: default - username: admin - identity_api_version: '3' - region_name: RegionOne - -Then, you need to specify a configuration name defined in the above clouds.yaml. - -.. code-block:: shell - - export OS_CLOUD=devstack - -For more detail information, see the -`os-client-config `_ -documentation. - -Using with keystone token -~~~~~~~~~~~~~~~~~~~~~~~~~ - -The command-line tool will attempt to re-authenticate using your provided -credentials for every request. You can override this behavior by manually -supplying an auth token using ``--os-url`` and ``--os-auth-token``. You can -alternatively set these environment variables. - -.. code-block:: shell - - export OS_URL=http://neutron.example.org:9696/ - export OS_TOKEN=3bcc3d3a03f44e3d8377f9247b0ad155 - -Using noauth mode -~~~~~~~~~~~~~~~~~ - -If neutron server does not require authentication, besides these two arguments -or environment variables (We can use any value as token.), we need manually -supply ``--os-auth-strategy`` or set the environment variable. - -.. code-block:: shell - - export OS_AUTH_STRATEGY=noauth - -Display options ---------------- - -Filtering -~~~~~~~~~ - -Neutron API supports filtering in the listing operation. -**neutron** CLI supports this feature too. - -To specify a filter in ``*-list`` command, you need to pass a pair of an -attribute name and an expected value with the format of ``-- ``. -The example below retrieves ports owned by compute instances. - -.. code-block:: console - - $ neutron port-list --device_owner network:dhcp - +--------------------------------------+------+-------------------+-------------------------------------------------------------------------------------------------------------+ - | id | name | mac_address | fixed_ips | - +--------------------------------------+------+-------------------+-------------------------------------------------------------------------------------------------------------+ - | 8953d683-29ad-4be3-b73f-060727c7849b | | fa:16:3e:4b:9e:0a | {"subnet_id": "6b832dfe-f271-443c-abad-629961414a73", "ip_address": "10.0.0.2"} | - | | | | {"subnet_id": "cdcc616b-0cff-482f-96f5-06fc63d21247", "ip_address": "fd12:877c:1d66:0:f816:3eff:fe4b:9e0a"} | - +--------------------------------------+------+-------------------+-------------------------------------------------------------------------------------------------------------+ - -You can also specify multiple filters. -The example below retrieves security group rules applied to IPv4 traffic -which belongs to a security group bfa493f9-2b03-46d2-8399-b9b038a53bc1. - -.. code-block:: console - - $ neutron security-group-rule-list --security-group-id bfa493f9-2b03-46d2-8399-b9b038a53bc1 --ethertype IPv4 - +--------------------------------------+----------------+-----------+-----------+---------------+-----------------+ - | id | security_group | direction | ethertype | protocol/port | remote | - +--------------------------------------+----------------+-----------+-----------+---------------+-----------------+ - | 65489805-0400-4bce-9bd9-16a81952263c | default | egress | IPv4 | any | any | - | 9429f336-4947-4643-bbd9-24528cc65648 | default | ingress | IPv4 | any | default (group) | - +--------------------------------------+----------------+-----------+-----------+---------------+-----------------+ - -.. note:: - - Looking up UUID from name is not supported when specifying a filter. - You need to use UUID to specify a specific resource. - -.. note:: - - Filtering for dictionary or list attributes is not supported. - -Changing displayed columns -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you want displayed columns in a list operation, ``-c`` option can be used. -``-c`` can be specified multiple times and the column order will be same as -the order of ``-c`` options. - -.. code-block:: console - - $ neutron port-list -c id -c device_owner -c fixed_ips - +--------------------------------------+--------------------------+-------------------------------------------------------------------------------------------------------------+ - | id | device_owner | fixed_ips | - +--------------------------------------+--------------------------+-------------------------------------------------------------------------------------------------------------+ - | 41ca1b9b-4bbd-4aa8-bcaa-31d3d5704205 | network:router_interface | {"subnet_id": "6b832dfe-f271-443c-abad-629961414a73", "ip_address": "10.0.0.1"} | - | 8953d683-29ad-4be3-b73f-060727c7849b | network:dhcp | {"subnet_id": "6b832dfe-f271-443c-abad-629961414a73", "ip_address": "10.0.0.2"} | - | | | {"subnet_id": "cdcc616b-0cff-482f-96f5-06fc63d21247", "ip_address": "fd12:877c:1d66:0:f816:3eff:fe4b:9e0a"} | - | a9da29f8-4504-4526-a5ce-cd3624fbd173 | neutron:LOADBALANCER | {"subnet_id": "6b832dfe-f271-443c-abad-629961414a73", "ip_address": "10.0.0.3"} | - | | | {"subnet_id": "cdcc616b-0cff-482f-96f5-06fc63d21247", "ip_address": "fd12:877c:1d66:0:f816:3eff:feb1:ab71"} | - | d6a1ff96-0a99-416f-a4d6-65d9614cf64e | compute:nova | {"subnet_id": "6b832dfe-f271-443c-abad-629961414a73", "ip_address": "10.0.0.4"} | - | | | {"subnet_id": "cdcc616b-0cff-482f-96f5-06fc63d21247", "ip_address": "fd12:877c:1d66:0:f816:3eff:fe2c:348e"} | - | f4789225-26d0-409f-8047-82d2c7a87a95 | network:router_interface | {"subnet_id": "cdcc616b-0cff-482f-96f5-06fc63d21247", "ip_address": "fd12:877c:1d66::1"} | - +--------------------------------------+--------------------------+-------------------------------------------------------------------------------------------------------------+ - -.. _cli_extra_arguments: - -Extra arguments for create/update operation -------------------------------------------- - -**neutron** CLI has a mechanism called the *extra arguments* for ``*-create`` -and ``*-update`` commands. It allows users to specify a set of *unknown -options* which are not defined as options and not shown in the help text. -**Unknown options MUST be placed at the end of the command line.** -*unknown options* will be directly passed to the API layer. By this mechanism, -you can pass an attribute which is not defined in the upstream **neutron** -CLI. For example, when you are developing a new feature which add a new -attribute to an existing resource, it is useful because we can test your -feature without changing the existing neutron CLI. - -For example, if you run the following command:: - - neutron resource-update --key1 value1 --key2 value2 - -where ``resource`` is some resource name and ``--key1`` and ``--key2`` are -unknown options, then the following JSON will be sent to the neutron API:: - - PUT /v2.0/resources/ - - { - "resource": { - "key2": "value2", - "key1": "value1" - } - } - -Key interpretation -~~~~~~~~~~~~~~~~~~ - -This means an option name (``--key1`` in this case) must be one of valid -resources of a corresponding resource. An option name ``--foo_bar`` is -recognized as an attribute name ``foo_bar``. ``--foo-bar`` is also interpreted -as an attribute name ``foo_bar``. - -Value interpretation -~~~~~~~~~~~~~~~~~~~~ - -By default, if the number of values is 1, the option value is interpreted as a -string and is passed to the API layer as specified in a command-line. - -If the number of values is greater than 1, the option value is interpreted as a -list and the result in the API layer will be same as when specifying a list as -described below. - - neutron resource-update --key1 val1 val2 val3 --key2 val4 - -In the above example, a value of ``key1`` is interpreted as -``["val1", "val2", "val3"]`` and a value of ``key2`` is interpreted -as ``val4``. - -The extra argument mechanism supports more complex value like a list or a dict. - -Specify a list value -++++++++++++++++++++ - -A command-line:: - - neutron resource-update --key list=true val1 val2 val3 - -will send the following in the API layer:: - - { - "key": [ - "val1", - "val2", - "val3" - ] - } - -.. note:: - - If you want to specify a list value, it is recommended to specify - ``list=true``. When ``list=true`` is specified, specified values are - interpreted as a list even regardless of the number of values. - - If ``list=true`` is not specified, specified values are interpreted - depends on the number of values how. If the number of values is more than 2, - the specified values are interpreted as a list. If 1, the value - is interpreted as a string. - -Specify a dict value -++++++++++++++++++++ - -A command-line:: - - neutron resource-update --key type=dict key1=val1,key2=val2,key3=val3 - -will send the following in the API layer:: - - { - "key": { - "key1": "val1", - "key2": "val2", - "key3": "val3" - } - } - -.. note:: - - ``type=bool True/False`` and ``type=int 10`` are also supported. - -Specify a list of dicts -+++++++++++++++++++++++ - -A command-line:: - - neutron resource-update --key type=dict list=true key1=val1 key2=val2 key3=val3 - -will send the following in the API layer:: - - { - "key": [ - {"key1": "val1"}, - {"key2": "val2"}, - {"key3": "val3"} - ] - } - -Passing None as a value -~~~~~~~~~~~~~~~~~~~~~~~ - -There is a case where we would like to pass ``None`` (``null`` in JSON) -in the API layer. To do this:: - - neutron resource-update --key action=clear - -The following body will be in the API layer:: - - {"key": null} - -.. note:: - - If ``action=clear`` is specified, ``list=true`` or ``type=dict`` is ignored. - It means when ``action=clear`` is specified ``None`` is always sent. - -Debugging ---------- - -Display API-level communication -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -``-v`` (or ``--verbose``, ``--debug``) option displays a detail interaction -with your neutron server. It is useful to debug what happens in the API level. - -Here is an sample output of ``net-show`` command. - -The first line show what parameters are recognized by neutronclient. -It is sometimes useful to check if command-line parameters you specify are recognized properly. - -.. code-block:: console - - $ neutron -v net-show mynetwork - DEBUG: neutronclient.neutron.v2_0.network.ShowNetwork get_data(Namespace(columns=[], fields=[], formatter='table', id=u'mynetwork', max_width=0, noindent=False, prefix='', request_format='json', show_details=False, variables=[])) - -Next, neutronclient sends an authentication request to keystone to get a token -which is used in further operations. - -.. code-block:: console - - DEBUG: keystoneauth.session REQ: curl -g -i -X GET http://172.16.18.47:5000 -H "Accept: application/json" -H "User-Agent: keystoneauth1" - DEBUG: keystoneauth.session RESP: [300] Content-Length: 593 Vary: X-Auth-Token Keep-Alive: timeout=5, max=100 Server: Apache/2.4.7 (Ubuntu) Connection: Keep-Alive Date: Fri, 27 Nov 2015 20:10:54 GMT Content-Type: application/json - RESP BODY: {"versions": {"values": [{"status": "stable", "updated": "2015-03-30T00:00:00Z", "media-types": [{"base": "application/json", "type": "application/vnd.openstack.identity-v3+json"}], "id": "v3.4", "links": [{"href": "http://172.16.18.47:5000/v3/", "rel": "self"}]}, {"status": "stable", "updated": "2014-04-17T00:00:00Z", "media-types": [{"base": "application/json", "type": "application/vnd.openstack.identity-v2.0+json"}], "id": "v2.0", "links": [{"href": "http://172.16.18.47:5000/v2.0/", "rel": "self"}, {"href": "http://docs.openstack.org/", "type": "text/html", "rel": "describedby"}]}]}} - - DEBUG: keystoneauth.identity.v3.base Making authentication request to http://172.16.18.47:5000/v3/auth/tokens - -Neutronclient looks up a network ID corresponding to a given network name. - -.. code-block:: console - - DEBUG: keystoneauth.session REQ: curl -g -i -X GET http://172.16.18.47:9696/v2.0/networks.json?fields=id&name=mynetwork -H "User-Agent: python-neutronclient" -H "Accept: application/json" -H "X-Auth-Token: {SHA1}39300e7398d53a02afd183f13cb6afaef95ec4e5" - DEBUG: keystoneauth.session RESP: [200] Date: Fri, 27 Nov 2015 20:10:55 GMT Connection: keep-alive Content-Type: application/json; charset=UTF-8 Content-Length: 62 X-Openstack-Request-Id: req-ccebf6e4-4f52-4874-a1ab-5499abcba378 - RESP BODY: {"networks": [{"id": "3698d3c7-d581-443e-bf86-53c4e3a738f7"}]} - -Finally, neutronclient retrieves a detail of a given network using the resolved ID. - -.. code-block:: console - - DEBUG: keystoneauth.session REQ: curl -g -i -X GET http://172.16.18.47:9696/v2.0/networks/3698d3c7-d581-443e-bf86-53c4e3a738f7.json -H "User-Agent: python-neutronclient" -H "Accept: application/json" -H "X-Auth-Token: {SHA1}39300e7398d53a02afd183f13cb6afaef95ec4e5" - DEBUG: keystoneauth.session RESP: [200] Date: Fri, 27 Nov 2015 20:10:55 GMT Connection: keep-alive Content-Type: application/json; charset=UTF-8 Content-Length: 272 X-Openstack-Request-Id: req-261add00-d6d3-4ea7-becc-105b60ac7369 - RESP BODY: {"network": {"status": "ACTIVE", "subnets": [], "name": "mynetwork", "admin_state_up": true, "tenant_id": "8f0ebf767043483a987736c8c684178d", "mtu": 0, "router:external": false, "shared": false, "port_security_enabled": true, "id": "3698d3c7-d581-443e-bf86-53c4e3a738f7"}} - - +-----------------------+--------------------------------------+ - | Field | Value | - +-----------------------+--------------------------------------+ - | admin_state_up | True | - | id | 3698d3c7-d581-443e-bf86-53c4e3a738f7 | - | mtu | 0 | - | name | mynetwork | - | port_security_enabled | True | - | router:external | False | - | shared | False | - | status | ACTIVE | - | subnets | | - | tenant_id | 8f0ebf767043483a987736c8c684178d | - +-----------------------+--------------------------------------+ diff --git a/doc/source/contributor/cli_option_guideline.rst b/doc/source/contributor/cli_option_guideline.rst deleted file mode 100644 index 49a840525..000000000 --- a/doc/source/contributor/cli_option_guideline.rst +++ /dev/null @@ -1,263 +0,0 @@ -.. - 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. - - - Convention for heading levels in Neutron devref: - ======= Heading 0 (reserved for the title in a document) - ------- Heading 1 - ~~~~~~~ Heading 2 - +++++++ Heading 3 - ''''''' Heading 4 - (Avoid deeper levels because they do not render well.) - -CLI Option Guideline -==================== - -This document describes the conventions of neutron CLI options. - -General conventions -------------------- - -#. Option names should be delimited by a hyphen instead of a underscore. - This is the common guidelines across all OpenStack CLIs. - - * Good: ``--ip-version`` - * Not Good: ``--ip_version`` - -#. Use at least one required option for ``*-create`` command. If all options - are optional, we typically use ``name`` field as a required option. - -#. When you need to specify an ID of a resource, it is better to provide - another way to specify the resource like ``name`` or other reasonable field. - -#. If an attribute name in API is ``foo_id``, the corresponding option - should be ``--foo`` instead of ``--foo-id``. - - * It is because we usually support ID and ``name`` to specify a resource. - -#. Do not use ``nargs='?'`` without a special reason. - - * The behavior of ``nargs='?'`` option for python argparse is - bit tricky and may lead to unexpected option parsing different - from the help message. The detail is described in the - :ref:`Background section ` below. - -#. (option) Avoid using positional options as much as possible. - - * Positional arguments should be limited to attributes which will - be required in the long future. - -#. We honor existing options and should keep compatibilities when adding or - changing options. - -Options for boolean value -------------------------- - -Use the form of ``--option-name {True|False}``. - -* For a new option, it is recommended. -* It is suggested to use ``common.utils.add_boolean_argument`` in an - implementation. It allows ``true``/``false`` in addition to ``True``/``False``. -* For existing options, migration to the recommended form is not necessarily - required. All backward-compatibility should be kept without reasonable - reasons. - -Options for dict value ----------------------- - -Some API attributes take a dictionary. - -``--foo key1=val1,key2=val2`` is usually used. - -This means ``{"key1": "val1", "key2": "val2"}`` is passed in the API layer. - -Examples: - -* ``--host-route destination=CIDR,nexthop=IP_ADDR`` for a subnet -* ``--fixed-ip subnet_id=SUBNET,ip_address=IP_ADDR`` for a port. - -Options for list value ----------------------- - -Some attributes take a list. - -In this case, we usually use: - -* Define an option per element (Use a singular form as an option name) -* Allow to specify the option multiple times - -For Example, **port-create** has ``--security-group`` option. -``--security-group SG1 --security-group SG2`` generates -``{"security_groups: ["SG1", "SG2"]}`` in the API layer. - -This convention applies to a case of a list of dict. -``--allocation-pool`` and ``--host-route`` for a subnet are examples. - -Compatibility with extra arguments ----------------------------------- - -*extra arguments* supports various types of option specifications. -At least the following patterns needs to be considered when defining -a new option. For more detail, see :ref:`cli_extra_arguments`. - -* Normal options with value -* Boolean options : ``--foo True``, ``--bar=False`` -* List options : ``--bars list=true val1 val2``, ``--bars val1 val2`` -* Dict options : ``--foo type=dict key1=va1,key2=val2`` -* List of Dict options : ``--bars list=true type=dict key1=val1,key2=val2 key3=val3,key4=val4`` -* ``action=clear`` - -For normal options with value, there are four patterns to specify an option -as extra arguments. - -* ``--admin-state-up True`` (a space between option name and value) -* ``--admin-state-up=True`` (= between option name and value) -* ``--admin_state_up True`` (underscore is used as delimiter) -* ``--admin_state_up=True`` (underscore is used as delimiter) - -.. _background: - -Background ----------- - -There are a lot of opinions on which form of options are better or not. -This section tries to capture the reason of the current choice. - -Use at least one required option -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -As a convention, **neutron** CLI requires one required argument. - -If all options are optional in the API level and we have ``name`` field, -we usually use ``name`` as a required parameter. -Requiring at least one argument has the following benefits: - -* If we run ``neutron *-create`` without a required argument, we will have a - brief help message without detail option help. It is convenient. -* We can avoid miss operation by just hitting ``neutron *-create``. - Requiring at least one parameter is a good balance. - -Even though we can change this convention to allow to create a resource -without ``name`` field, it will bring confusions to existing users. - -There may be opinion that it is inconsistent with API level requirement -or Horizon behavior, but even if neutron CLI requires ``name`` field -there is no bad impact on regular users. Considering possible confusion -if we change it, it looks better to keep it as-is. - -Options for Boolean value -~~~~~~~~~~~~~~~~~~~~~~~~~ - -* ``--enable-foo``/``--disable-foo`` or similar patterns (including - ``--admin-state-down``) is not suggested because we need two exclusive - options for one attribute in REST API. It is meaningless. - -* It is not recommended to have an option only to specify non-default value. - For example, we have ``--shared`` or ``--admin-state-down`` options for - net-create. This form only works for ``*-create`` and does not work for - ``*-update``. It leads to having different options for ``*-create`` and - ``*-update``. - -* A flag option like ``--enable-dhcp`` (without value) also has a problem when - considering the compatibility with *extra argument*. We can specify - ``-enable-dhcp True/False`` or ``--enable-dhcp=True/False`` in the *extra - argument* mechanism. If we introduce ``--enable-dhcp`` (without value), - the form of ``-enable-dhcp True/False`` cannot be used now. - This is another reason we don't use a flag style option for a boolean parameter. - -.. _background-nargs: - -Avoid using nargs in positional or optional arguments -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The behavior of ``nargs='?'`` option for python argparse is bit tricky. -When we use ``nargs='?'`` and if the order of command-line options is -changed then the command-line parser may fail to parse the arguments -correctly. Two examples of such failures are provided below. - -Example 1: -This example shows how the actual behavior can differ from the provided -help message. In the below block, help message at ``[5]`` says ``--bb CC`` -is a valid format but the argument parsing for the same format fails at ``[7]``. - -.. code-block:: console - - In [1]: import argparse - In [2]: parser = argparse.ArgumentParser() - In [3]: parser.add_argument('--bb', nargs='?') - In [4]: parser.add_argument('cc') - - In [5]: parser.print_help() - usage: ipython [-h] [--bb [BB]] cc - - positional arguments: - cc - - optional arguments: - -h, --help show this help message and exit - --bb [BB] - - In [6]: parser.parse_args('--bb 1 X'.split()) - Out[6]: Namespace(bb='1', cc='X') - - In [7]: parser.parse_args('--bb X'.split()) - usage: ipython [-h] [--bb [BB]] cc - ipython: error: too few arguments - An exception has occurred, use %tb to see the full traceback. - - SystemExit: 2 - - -Example 2: -This example shows how fragile ``nargs='?'`` can be when user specifies -options in different order from the help message. - -.. code-block:: console - - In [1]: import argparse - In [2]: parser = argparse.ArgumentParser() - In [3]: parser.add_argument('--a', help='option a') - In [4]: parser.add_argument('--b', help='option b') - In [5]: parser.add_argument('x', help='positional arg X') - In [6]: parser.add_argument('y', nargs='?', help='positional arg Y') - In [7]: parser.print_help() - usage: ipython [-h] [--a A] [--b B] x [y] - - positional arguments: - x positional arg X - y positional arg Y - - optional arguments: - -h, --help show this help message and exit - --a A option a - --b B option b - - In [8]: parser.parse_args('--a 1 --b 2 X Y'.split()) - Out[8]: Namespace(a='1', b='2', x='X', y='Y') - - In [9]: parser.parse_args('X Y --a 1 --b 2'.split()) - Out[9]: Namespace(a='1', b='2', x='X', y='Y') - - In [10]: parser.parse_args('X --a 1 --b 2 Y'.split()) - usage: ipython [-h] [--a A] [--b B] x [y] - ipython: error: unrecognized arguments: Y - An exception has occurred, use %tb to see the full traceback. - - SystemExit: 2 - - To exit: use 'exit', 'quit', or Ctrl-D. - To exit: use 'exit', 'quit', or Ctrl-D. - -Note: Most CLI users don't care about the order of the command-line -options. Hence, such fragile behavior should be avoided. - diff --git a/doc/source/contributor/client_command_extensions.rst b/doc/source/contributor/client_command_extensions.rst deleted file mode 100644 index 4b74cdf77..000000000 --- a/doc/source/contributor/client_command_extensions.rst +++ /dev/null @@ -1,97 +0,0 @@ -.. - 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. - - - Convention for heading levels in Neutron devref: - ======= Heading 0 (reserved for the title in a document) - ------- Heading 1 - ~~~~~~~ Heading 2 - +++++++ Heading 3 - ''''''' Heading 4 - (Avoid deeper levels because they do not render well.) - -Client command extension support -================================= - -The client command extension adds support for extending the neutron client while -considering ease of creation. -Extensions strongly conform to preexisting neutron commands (/neutron/v2_0/). - -A sample extension can be seen at: -neutronclient/neutron/v2_0/contrib/_fox_sockets.py - -Minimum requirements from an extension --------------------------------------- - -* NeutronClientExtension subclasses must have a shell_command class variable - if the command is to be available to the CLI (shell.py) - - Example: neutronclient.neutron.v2_0.contrib._fox_sockets.FoxInSocketsList - -Minimum requirements to use canonical neutron CRUD commands framework ----------------------------------------------------------------------- - -Neutron commands are cliff commands, commands in extension can use their -own way to finish their tasks. But if they want to make use of the canonical -neutron CRUD commands framework, the extension should: - -* have a class that subclasses NeutronClientExtension to provide the - requisite resource name, version support, and resource collection and - object paths for a resource the commands will process. - - Example: neutronclient.neutron.v2_0.contrib._fox_sockets.FoxInSocket - -* have a class that subclasses from the ClientExtensionList to provide - resource object list function. This is because most commands - need the list function to get object ID via - neutronclient.neutron.v2_0.__init__.find_resource_by_id. - - Example: neutronclient.neutron.v2_0.contrib._fox_sockets.FoxInSocketsList - -* if needed, subclass ClientExtensionUpdate to implement update of the resource - object. - - Example: neutronclient.neutron.v2_0.contrib._fox_sockets.FoxInSocketsUpdate - -* if needed, subclass ClientExtensionDelete to implement deletion of the resource - object. - - Example: neutronclient.neutron.v2_0.contrib._fox_sockets.FoxInSocketsDelete - -* if needed, subclass ClientExtensionShow to get the detail of the resource - object. - - Example: neutronclient.neutron.v2_0.contrib._fox_sockets.FoxInSocketsShow - -Precedence of command loading ------------------------------- - -* hard coded commands are loaded first -* external commands (installed in the environment) are loaded then - -Commands that have the same name will be overwritten by commands that are -loaded later. To change the execution of a command for your particular -extension you only need to override the execute method. - -Currently this extension support is limited to top-level resources. -Parent/child relationships may be added if desired. - -neutronclient.extension entry_point ------------------------------------ - -To activate the commands in a specific extension module, add an entry in -setup.cfg under neutronclient.extension. For example:: - - [entry_points] - neutronclient.extension = - fox_sockets = neutronclient.neutron.v2_0.contrib._fox_sockets diff --git a/doc/source/contributor/index.rst b/doc/source/contributor/index.rst index f2150b0e0..fae3f6b82 100644 --- a/doc/source/contributor/index.rst +++ b/doc/source/contributor/index.rst @@ -31,6 +31,4 @@ OpenStack client. .. toctree:: :maxdepth: 2 - client_command_extensions - cli_option_guideline transition_to_osc diff --git a/doc/source/contributor/transition_to_osc.rst b/doc/source/contributor/transition_to_osc.rst index 334f2e57e..9bb952902 100644 --- a/doc/source/contributor/transition_to_osc.rst +++ b/doc/source/contributor/transition_to_osc.rst @@ -105,7 +105,7 @@ Transition Steps In addition, no new features will be added to the CLI, though fixes to the CLI will be assessed on a case by case basis. -8. **Not Started:** Remove the ``neutron`` CLI after two deprecation cycles +8. **Done** Remove the ``neutron`` CLI after two deprecation cycles once the criteria below have been met. * The networking support provide by the ``openstack`` CLI is functionally @@ -122,32 +122,8 @@ Transition Steps Developer Guide --------------- -The ``neutron`` CLI version 6.x, without extensions, supports over 200 -commands while the ``openstack`` CLI version 3.3.0 supports over 70 -networking commands. Of the 70 commands, some do not have all of the options -or arguments of their ``neutron`` CLI equivalent. With this large functional -gap, a few critical questions for developers during this transition are "Which -CLI do I change?", "Where does my CLI belong?", and "Which Python library do I change?" -The answer depends on the state of a command and the state of the overall transition. -Details are outlined in the tables below. Early stages of the transition will require -dual maintenance. - -**Which CLI do I change?** - -+----------------------+------------------------+-------------------------------------------------+ -| ``neutron`` Command | ``openstack`` Command | CLI to Change | -+======================+========================+=================================================+ -| Exists | Doesn't Exist | ``neutron`` | -+----------------------+------------------------+-------------------------------------------------+ -| Exists | In Progress | ``neutron`` and ``openstack`` | -| | | (update related blueprint or bug) | -+----------------------+------------------------+-------------------------------------------------+ -| Exists | Exists | ``openstack`` | -| | | (assumes command parity resulting in | -| | | ``neutron`` being deprecated) | -+----------------------+------------------------+-------------------------------------------------+ -| Doesn't Exist | Doesn't Exist | ``openstack`` | -+----------------------+------------------------+-------------------------------------------------+ +The ``neutron`` CLI tool is now removed and all new CLI changes should be done +in the ``OpenStackClient (OSC)`` and, if needed, also in the ``OpenStack SDK``. **Where does my CLI belong?** @@ -186,8 +162,6 @@ is not required as the neutronclient is already deprecated on its own. +=================================================+===============================================+ | python-openstackclient | openstacksdk | +-------------------------------------------------+-----------------------------------------------+ -| python-neutronclient | python-neutronclient | -+-------------------------------------------------+-----------------------------------------------+ | Other | Applicable project owning network resource | +-------------------------------------------------+-----------------------------------------------+ diff --git a/doc/source/index.rst b/doc/source/index.rst index 64ad45ab4..c36d482ed 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -24,15 +24,11 @@ python-neutronclient documentation ================================== This is a client for OpenStack Networking API. It provides -:doc:`Python API bindings ` (the neutronclient module) and -:doc:`command-line interface (CLI) `. +:doc:`Python API bindings ` (the neutronclient module). -There are two CLIs which support the Networking API: -:doc:`neutron CLI ` and +There is `OpenStack Client (OSC) `__. -OpenStack Client provides the basic network commands and -python-neutronclient provides extensions (aka OSC plugins) -for advanced networking services. +CLI which support the Networking API. User Documentation ------------------ diff --git a/neutron_test.sh b/neutron_test.sh deleted file mode 100755 index d6dc75409..000000000 --- a/neutron_test.sh +++ /dev/null @@ -1,165 +0,0 @@ -#!/bin/bash -set -x -function die() { - local exitcode=$? - set +o xtrace - echo $@ - cleanup - exit $exitcode -} - -net_name=mynet1 -subnet_name=mysubnet1 -port_name=myport1 -function cleanup() { - echo Removing test port, subnet and net... - neutron port-delete $port_name - neutron subnet-delete $subnet_name - neutron net-delete $net_name -} - -noauth_tenant_id=me -if [ "$1" == "noauth" ]; then - NOAUTH="--tenant_id $noauth_tenant_id" -else - NOAUTH= -fi - -echo "NOTE: User should be admin in order to perform all operations." -sleep 3 - -# test the CRUD of network -network=$net_name -neutron net-create $NOAUTH $network || die "fail to create network $network" -temp=`neutron net-list -- --name $network --fields id | wc -l` -echo $temp -if [ $temp -ne 5 ]; then - die "networks with name $network is not unique or found" -fi -network_id=`neutron net-list -- --name $network --fields id | tail -n 2 | head -n 1 | cut -d' ' -f 2` -echo "ID of network with name $network is $network_id" - -neutron net-show $network || die "fail to show network $network" -neutron net-show $network_id || die "fail to show network $network_id" - -neutron net-update $network --admin_state_up False || die "fail to update network $network" -neutron net-update $network_id --admin_state_up True || die "fail to update network $network_id" - -neutron net-list -c id -- --id fakeid || die "fail to list networks with column selection on empty list" - -# test the CRUD of subnet -subnet=$subnet_name -cidr=10.0.1.0/24 -neutron subnet-create $NOAUTH $network $cidr --name $subnet || die "fail to create subnet $subnet" -tempsubnet=`neutron subnet-list -- --name $subnet --fields id | wc -l` -echo $tempsubnet -if [ $tempsubnet -ne 5 ]; then - die "subnets with name $subnet is not unique or found" -fi -subnet_id=`neutron subnet-list -- --name $subnet --fields id | tail -n 2 | head -n 1 | cut -d' ' -f 2` -echo "ID of subnet with name $subnet is $subnet_id" -neutron subnet-show $subnet || die "fail to show subnet $subnet" -neutron subnet-show $subnet_id || die "fail to show subnet $subnet_id" - -neutron subnet-update $subnet --dns_nameservers list=true 1.1.1.11 1.1.1.12 || die "fail to update subnet $subnet" -neutron subnet-update $subnet_id --dns_nameservers list=true 2.2.2.21 2.2.2.22 || die "fail to update subnet $subnet_id" - -# test the crud of ports -port=$port_name -neutron port-create $NOAUTH $network --name $port || die "fail to create port $port" -tempport=`neutron port-list -- --name $port --fields id | wc -l` -echo $tempport -if [ $tempport -ne 5 ]; then - die "ports with name $port is not unique or found" -fi -port_id=`neutron port-list -- --name $port --fields id | tail -n 2 | head -n 1 | cut -d' ' -f 2` -echo "ID of port with name $port is $port_id" -neutron port-show $port || die "fail to show port $port" -neutron port-show $port_id || die "fail to show port $port_id" -neutron port-update $port --device_id deviceid1 || die "fail to update port $port" -neutron port-update $port_id --device_id deviceid2 || die "fail to update port $port_id" -neutron port-update $port_id --allowed-address-pair ip_address=1.1.1.11,mac_address=10:00:00:00:00:00 --allowed-address-pair ip_address=1.1.1.12,mac_address=10:00:00:00:00:01 || die "fail to update port $port_id --allowed-address-pair" -neutron port-show $port || die "fail to show port $port" -neutron port-show $port_id || die "fail to show port $port_id" -neutron port-update $port_id --no-allowed-address-pairs || die "fail to update port $port_id --no-allowed-address-pairs" -neutron port-show $port || die "fail to show port $port" -neutron port-show $port_id || die "fail to show port $port_id" -neutron port-delete $port_id - -# test the create port with allowed-address-pairs -port=$port_name -neutron port-create $NOAUTH $network --name $port -- --allowed-address-pairs type=dict list=true ip_address=1.1.1.11,mac_address=10:00:00:00:00:00 ip_address=1.1.1.12,mac_address=10:00:00:00:00:01 || die "fail to create port $port" -tempport=`neutron port-list -- --name $port --fields id | wc -l` -echo $tempport -if [ $tempport -ne 5 ]; then - die "ports with name $port is not unique or found" -fi -port_id=`neutron port-list -- --name $port --fields id | tail -n 2 | head -n 1 | cut -d' ' -f 2` -echo "ID of port with name $port is $port_id" -neutron port-show $port || die "fail to show port $port" -neutron port-show $port_id || die "fail to show port $port_id" -neutron port-update $port_id --no-allowed-address-pairs || die "fail to update port $port_id --no-allowed-address-pairs" -neutron port-show $port_id - -# test quota commands RUD -DEFAULT_NETWORKS=10 -DEFAULT_PORTS=50 -tenant_id=tenant_a -tenant_id_b=tenant_b -neutron quota-update --tenant_id $tenant_id --network 30 || die "fail to update quota for tenant $tenant_id" -neutron quota-update --tenant_id $tenant_id_b --network 20 || die "fail to update quota for tenant $tenant_id" -networks=`neutron quota-list -c network -c tenant_id | grep $tenant_id | awk '{print $2}'` -if [ $networks -ne 30 ]; then - die "networks quota should be 30" -fi -networks=`neutron quota-list -c network -c tenant_id | grep $tenant_id_b | awk '{print $2}'` -if [ $networks -ne 20 ]; then - die "networks quota should be 20" -fi -networks=`neutron quota-show --tenant_id $tenant_id | grep network | awk -F'|' '{print $3}'` -if [ $networks -ne 30 ]; then - die "networks quota should be 30" -fi -neutron quota-delete --tenant_id $tenant_id || die "fail to delete quota for tenant $tenant_id" -networks=`neutron quota-show --tenant_id $tenant_id | grep network | awk -F'|' '{print $3}'` -if [ $networks -ne $DEFAULT_NETWORKS ]; then - die "networks quota should be $DEFAULT_NETWORKS" -fi -# update self -if [ "t$NOAUTH" = "t" ]; then - # with auth - neutron quota-update --port 99 || die "fail to update quota for self" - ports=`neutron quota-show | grep port | awk -F'|' '{print $3}'` - if [ $ports -ne 99 ]; then - die "ports quota should be 99" - fi - - ports=`neutron quota-list -c port | grep 99 | awk '{print $2}'` - if [ $ports -ne 99 ]; then - die "ports quota should be 99" - fi - neutron quota-delete || die "fail to delete quota for tenant self" - ports=`neutron quota-show | grep port | awk -F'|' '{print $3}'` - if [ $ports -ne $DEFAULT_PORTS ]; then - die "ports quota should be $DEFAULT_PORTS" - fi -else - # without auth - neutron quota-update --port 100 - if [ $? -eq 0 ]; then - die "without valid context on server, quota update command should fail." - fi - neutron quota-show - if [ $? -eq 0 ]; then - die "without valid context on server, quota show command should fail." - fi - neutron quota-delete - if [ $? -eq 0 ]; then - die "without valid context on server, quota delete command should fail." - fi - neutron quota-list || die "fail to update quota for self" -fi - -cleanup -echo "Success! :)" - diff --git a/neutronclient/shell.py b/neutronclient/shell.py deleted file mode 100644 index 13cd0fa4e..000000000 --- a/neutronclient/shell.py +++ /dev/null @@ -1,654 +0,0 @@ -# Copyright 2012 OpenStack Foundation. -# 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. -# - -""" -Command-line interface to the Neutron APIs -""" - -import argparse -import inspect -import itertools -import logging -import os -import sys - -from keystoneauth1 import session -import os_client_config -from oslo_utils import encodeutils -from oslo_utils import netutils - -from cliff import app -from cliff import command -from cliff import commandmanager - -from neutronclient._i18n import _ -from neutronclient.common import clientmanager -from neutronclient.common import exceptions as exc -from neutronclient.common import extension as client_extension -from neutronclient.neutron.v2_0 import subnet -from neutronclient.version import __version__ - - -VERSION = '2.0' -NEUTRON_API_VERSION = '2.0' - -NAMESPACE_MAP = {NEUTRON_API_VERSION: 'neutron.cli.v2'} - - -def run_command(cmd, cmd_parser, sub_argv): - _argv = sub_argv - index = -1 - values_specs = [] - if '--' in sub_argv: - index = sub_argv.index('--') - _argv = sub_argv[:index] - values_specs = sub_argv[index:] - known_args, _values_specs = cmd_parser.parse_known_args(_argv) - if(isinstance(cmd, subnet.CreateSubnet) and not known_args.cidr): - cidr = get_first_valid_cidr(_values_specs) - if cidr: - known_args.cidr = cidr - _values_specs.remove(cidr) - cmd.values_specs = (index == -1 and _values_specs or values_specs) - return cmd.run(known_args) - - -def get_first_valid_cidr(value_specs): - # Bug 1442771, argparse does not allow optional positional parameter - # to be separated from previous positional parameter. - # When cidr was separated from network, the value will not be able - # to be parsed into known_args, but saved to _values_specs instead. - for value in value_specs: - if netutils.is_valid_cidr(value): - return value - - -def env(*_vars, **kwargs): - """Search for the first defined of possibly many env vars. - - Returns the first environment variable defined in vars, or - returns the default defined in kwargs. - - """ - for v in _vars: - value = os.environ.get(v, None) - if value: - return value - return kwargs.get('default', '') - - -def check_non_negative_int(value): - try: - value = int(value) - except ValueError: - raise argparse.ArgumentTypeError(_("invalid int value: %r") % value) - if value < 0: - raise argparse.ArgumentTypeError(_("input value %d is negative") % - value) - return value - - -COMMANDS = {} - - -# NOTE(amotoki): This is only to provide compatibility -# to existing neutron CLI extensions. See bug 1706573 for detail. -def _set_commands_dict_for_compat(apiversion, command_manager): - global COMMANDS - COMMANDS = {apiversion: dict((cmd, command_manager.find_command([cmd])[0]) - for cmd in command_manager.commands)} - - -class BashCompletionCommand(command.Command): - """Prints all of the commands and options for bash-completion.""" - - def take_action(self, parsed_args): - pass - - -class HelpAction(argparse.Action): - """Print help message including sub-commands - - Provide a custom action so the -h and --help options - to the main app will print a list of the commands. - - The commands are determined by checking the CommandManager - instance, passed in as the "default" value for the action. - """ - def __call__(self, parser, namespace, values, option_string=None): - outputs = [] - max_len = 0 - app = self.default - parser.print_help(app.stdout) - app.stdout.write(_('\nCommands for API v%s:\n') % app.api_version) - command_manager = app.command_manager - for name, ep in sorted(command_manager): - factory = ep.load() - cmd = factory(self, None) - one_liner = cmd.get_description().split('\n')[0] - outputs.append((name, one_liner)) - max_len = max(len(name), max_len) - for (name, one_liner) in outputs: - app.stdout.write(' %s %s\n' % (name.ljust(max_len), one_liner)) - sys.exit(0) - - -class NeutronShell(app.App): - - # verbose logging levels - WARNING_LEVEL = 0 - INFO_LEVEL = 1 - DEBUG_LEVEL = 2 - CONSOLE_MESSAGE_FORMAT = '%(message)s' - DEBUG_MESSAGE_FORMAT = '%(levelname)s: %(name)s %(message)s' - log = logging.getLogger(__name__) - - def __init__(self, apiversion): - namespace = NAMESPACE_MAP[apiversion] - description = (__doc__.strip() + - " (neutron CLI version: %s)" % __version__) - super(NeutronShell, self).__init__( - description=description, - version=VERSION, - command_manager=commandmanager.CommandManager(namespace), ) - - self._register_extensions(VERSION) - - # Pop the 'complete' to correct the outputs of 'neutron help'. - self.command_manager.commands.pop('complete') - - # This is instantiated in initialize_app() only when using - # password flow auth - self.auth_client = None - self.api_version = apiversion - - _set_commands_dict_for_compat(apiversion, self.command_manager) - - def build_option_parser(self, description, version): - """Return an argparse option parser for this application. - - Subclasses may override this method to extend - the parser with more global options. - - :param description: full description of the application - :paramtype description: str - :param version: version number for the application - :paramtype version: str - """ - parser = argparse.ArgumentParser( - description=description, - add_help=False, ) - parser.add_argument( - '--version', - action='version', - version=__version__, ) - parser.add_argument( - '-v', '--verbose', '--debug', - action='count', - dest='verbose_level', - default=self.DEFAULT_VERBOSE_LEVEL, - help=_('Increase verbosity of output and show tracebacks on' - ' errors. You can repeat this option.')) - parser.add_argument( - '-q', '--quiet', - action='store_const', - dest='verbose_level', - const=0, - help=_('Suppress output except warnings and errors.')) - parser.add_argument( - '-h', '--help', - action=HelpAction, - nargs=0, - default=self, # tricky - help=_("Show this help message and exit.")) - parser.add_argument( - '-r', '--retries', - metavar="NUM", - type=check_non_negative_int, - default=0, - help=_("How many times the request to the Neutron server should " - "be retried if it fails. Defaults to 0.")) - # FIXME(bklei): this method should come from keystoneauth1 - self._append_global_identity_args(parser) - - return parser - - def _append_global_identity_args(self, parser): - # FIXME(bklei): these are global identity (Keystone) arguments which - # should be consistent and shared by all service clients. Therefore, - # they should be provided by keystoneauth1. We will need to - # refactor this code once this functionality is available in - # keystoneauth1. - # - # Note: At that time we'll need to decide if we can just abandon - # the deprecated args (--service-type and --endpoint-type). - - parser.add_argument( - '--os-service-type', metavar='', - default=env('OS_NETWORK_SERVICE_TYPE', default='network'), - help=_('Defaults to env[OS_NETWORK_SERVICE_TYPE] or "network".')) - - parser.add_argument( - '--os-endpoint-type', metavar='', - default=env('OS_ENDPOINT_TYPE', default='public'), - help=_('Defaults to env[OS_ENDPOINT_TYPE] or "public".')) - - # FIXME(bklei): --service-type is deprecated but kept in for - # backward compatibility. - parser.add_argument( - '--service-type', metavar='', - default=env('OS_NETWORK_SERVICE_TYPE', default='network'), - help=_('DEPRECATED! Use --os-service-type.')) - - # FIXME(bklei): --endpoint-type is deprecated but kept in for - # backward compatibility. - parser.add_argument( - '--endpoint-type', metavar='', - default=env('OS_ENDPOINT_TYPE', default='public'), - help=_('DEPRECATED! Use --os-endpoint-type.')) - - parser.add_argument( - '--os-auth-strategy', metavar='', - default=env('OS_AUTH_STRATEGY', default='keystone'), - help=_('DEPRECATED! Only keystone is supported.')) - - parser.add_argument( - '--os_auth_strategy', - help=argparse.SUPPRESS) - - parser.add_argument( - '--os-cloud', metavar='', - help=_('Defaults to env[OS_CLOUD].')) - - parser.add_argument( - '--os-auth-url', metavar='', - help=_('Authentication URL, defaults to env[OS_AUTH_URL].')) - parser.add_argument( - '--os_auth_url', - help=argparse.SUPPRESS) - - project_name_group = parser.add_mutually_exclusive_group() - project_name_group.add_argument( - '--os-tenant-name', metavar='', - help=_('Authentication tenant name, defaults to ' - 'env[OS_TENANT_NAME].')) - project_name_group.add_argument( - '--os-project-name', - metavar='', - help=_('Another way to specify tenant name. ' - 'This option is mutually exclusive with ' - ' --os-tenant-name. ' - 'Defaults to env[OS_PROJECT_NAME].')) - - parser.add_argument( - '--os_tenant_name', - help=argparse.SUPPRESS) - - project_id_group = parser.add_mutually_exclusive_group() - project_id_group.add_argument( - '--os-tenant-id', metavar='', - help=_('Authentication tenant ID, defaults to ' - 'env[OS_TENANT_ID].')) - project_id_group.add_argument( - '--os-project-id', - metavar='', - help=_('Another way to specify tenant ID. ' - 'This option is mutually exclusive with ' - ' --os-tenant-id. ' - 'Defaults to env[OS_PROJECT_ID].')) - - parser.add_argument( - '--os-username', metavar='', - help=_('Authentication username, defaults to env[OS_USERNAME].')) - parser.add_argument( - '--os_username', - help=argparse.SUPPRESS) - - parser.add_argument( - '--os-user-id', metavar='', - help=_('Authentication user ID (Env: OS_USER_ID)')) - - parser.add_argument( - '--os_user_id', - help=argparse.SUPPRESS) - - parser.add_argument( - '--os-user-domain-id', - metavar='', - help=_('OpenStack user domain ID. ' - 'Defaults to env[OS_USER_DOMAIN_ID].')) - - parser.add_argument( - '--os_user_domain_id', - help=argparse.SUPPRESS) - - parser.add_argument( - '--os-user-domain-name', - metavar='', - help=_('OpenStack user domain name. ' - 'Defaults to env[OS_USER_DOMAIN_NAME].')) - - parser.add_argument( - '--os_user_domain_name', - help=argparse.SUPPRESS) - - parser.add_argument( - '--os_project_id', - help=argparse.SUPPRESS) - - parser.add_argument( - '--os_project_name', - help=argparse.SUPPRESS) - - parser.add_argument( - '--os-project-domain-id', - metavar='', - help=_('Defaults to env[OS_PROJECT_DOMAIN_ID].')) - - parser.add_argument( - '--os-project-domain-name', - metavar='', - help=_('Defaults to env[OS_PROJECT_DOMAIN_NAME].')) - - parser.add_argument( - '--os-cert', - metavar='', - help=_("Path of certificate file to use in SSL " - "connection. This file can optionally be " - "prepended with the private key. Defaults " - "to env[OS_CERT].")) - - parser.add_argument( - '--os-cacert', - metavar='', - help=_("Specify a CA bundle file to use in " - "verifying a TLS (https) server certificate. " - "Defaults to env[OS_CACERT].")) - - parser.add_argument( - '--os-key', - metavar='', - help=_("Path of client key to use in SSL " - "connection. This option is not necessary " - "if your key is prepended to your certificate " - "file. Defaults to env[OS_KEY].")) - - parser.add_argument( - '--os-password', metavar='', - help=_('Authentication password, defaults to env[OS_PASSWORD].')) - parser.add_argument( - '--os_password', - help=argparse.SUPPRESS) - - parser.add_argument( - '--os-region-name', metavar='', - help=_('Authentication region name, defaults to ' - 'env[OS_REGION_NAME].')) - parser.add_argument( - '--os_region_name', - help=argparse.SUPPRESS) - - parser.add_argument( - '--os-token', metavar='', - help=_('Authentication token, defaults to env[OS_TOKEN].')) - parser.add_argument( - '--os_token', - help=argparse.SUPPRESS) - - parser.add_argument( - '--http-timeout', metavar='', - default=env('OS_NETWORK_TIMEOUT', default=None), type=float, - help=_('Timeout in seconds to wait for an HTTP response. Defaults ' - 'to env[OS_NETWORK_TIMEOUT] or None if not specified.')) - - parser.add_argument( - '--os-url', metavar='', - help=_('Defaults to env[OS_URL].')) - parser.add_argument( - '--os_url', - help=argparse.SUPPRESS) - - parser.add_argument( - '--insecure', - action='store_true', - default=env('NEUTRONCLIENT_INSECURE', default=False), - help=_("Explicitly allow neutronclient to perform \"insecure\" " - "SSL (https) requests. The server's certificate will " - "not be verified against any certificate authorities. " - "This option should be used with caution.")) - - def _bash_completion(self): - """Prints all of the commands and options for bash-completion.""" - commands = set() - options = set() - for option, _action in self.parser._option_string_actions.items(): - options.add(option) - for _name, _command in self.command_manager: - commands.add(_name) - cmd_factory = _command.load() - cmd = cmd_factory(self, None) - cmd_parser = cmd.get_parser('') - for option, _action in cmd_parser._option_string_actions.items(): - options.add(option) - print(' '.join(commands | options)) - - def _register_extensions(self, version): - for name, module in itertools.chain( - client_extension._discover_via_entry_points()): - self._extend_shell_commands(name, module, version) - - def _extend_shell_commands(self, name, module, version): - classes = inspect.getmembers(module, inspect.isclass) - for cls_name, cls in classes: - if (issubclass(cls, client_extension.NeutronClientExtension) and - hasattr(cls, 'shell_command')): - cmd = cls.shell_command - if hasattr(cls, 'versions'): - if version not in cls.versions: - continue - try: - name_prefix = "[%s]" % name - cls.__doc__ = ("%s %s" % (name_prefix, cls.__doc__) if - cls.__doc__ else name_prefix) - self.command_manager.add_command(cmd, cls) - except TypeError: - pass - - def run(self, argv): - """Equivalent to the main program for the application. - - :param argv: input arguments and options - :paramtype argv: list of str - """ - try: - index = 0 - command_pos = -1 - help_pos = -1 - help_command_pos = -1 - for arg in argv: - if arg == 'bash-completion' and help_command_pos == -1: - self._bash_completion() - return 0 - if arg in ('-h', '--help'): - if help_pos == -1: - help_pos = index - # self.command_manager.commands contains 'help', - # so we need to check this first. - elif arg == 'help': - if help_command_pos == -1: - help_command_pos = index - elif arg in self.command_manager.commands: - if command_pos == -1: - command_pos = index - index = index + 1 - if command_pos > -1 and help_pos > command_pos: - argv = ['help', argv[command_pos]] - if help_command_pos > -1 and command_pos == -1: - argv[help_command_pos] = '--help' - self.options, remainder = self.parser.parse_known_args(argv) - self.configure_logging() - self.interactive_mode = not remainder - self.initialize_app(remainder) - except Exception as err: - if self.options.verbose_level >= self.DEBUG_LEVEL: - self.log.exception(err) - raise - else: - self.log.error(err) - return 1 - if self.interactive_mode: - _argv = [sys.argv[0]] - sys.argv = _argv - return self.interact() - return self.run_subcommand(remainder) - - def run_subcommand(self, argv): - subcommand = self.command_manager.find_command(argv) - cmd_factory, cmd_name, sub_argv = subcommand - cmd = cmd_factory(self, self.options) - try: - self.prepare_to_run_command(cmd) - full_name = (cmd_name - if self.interactive_mode - else ' '.join([self.NAME, cmd_name]) - ) - cmd_parser = cmd.get_parser(full_name) - return run_command(cmd, cmd_parser, sub_argv) - except SystemExit: - print(_("Try 'neutron help %s' for more information.") % - cmd_name, file=sys.stderr) - raise - except Exception as e: - if self.options.verbose_level >= self.DEBUG_LEVEL: - self.log.exception("%s", e) - raise - self.log.error("%s", e) - return 1 - - def authenticate_user(self): - """Confirm user authentication - - Make sure the user has provided all of the authentication - info we need. - """ - cloud_config = os_client_config.OpenStackConfig().get_one_cloud( - cloud=self.options.os_cloud, argparse=self.options, - network_api_version=self.api_version, - verify=not self.options.insecure) - verify, cert = cloud_config.get_requests_verify_args() - - # TODO(singhj): Remove dependancy on HTTPClient - # for the case of token-endpoint authentication - - # When using token-endpoint authentication legacy - # HTTPClient will be used, otherwise SessionClient - # will be used. - if self.options.os_token and self.options.os_url: - auth = None - auth_session = None - else: - auth = cloud_config.get_auth() - - auth_session = session.Session( - auth=auth, verify=verify, cert=cert, - timeout=self.options.http_timeout) - - interface = self.options.os_endpoint_type or self.endpoint_type - if interface.endswith('URL'): - interface = interface[:-3] - self.client_manager = clientmanager.ClientManager( - retries=self.options.retries, - raise_errors=False, - session=auth_session, - url=self.options.os_url, - token=self.options.os_token, - region_name=cloud_config.get_region_name(), - api_version=cloud_config.get_api_version('network'), - service_type=cloud_config.get_service_type('network'), - service_name=cloud_config.get_service_name('network'), - endpoint_type=interface, - auth=auth, - insecure=not verify, - log_credentials=True) - return - - def initialize_app(self, argv): - """Global app init bits: - - * set up API versions - * validate authentication info - """ - - super(NeutronShell, self).initialize_app(argv) - - # If the user is not asking for help, make sure they - # have given us auth. - cmd_name = None - if argv: - cmd_info = self.command_manager.find_command(argv) - cmd_factory, cmd_name, sub_argv = cmd_info - if self.interactive_mode or cmd_name != 'help': - self.authenticate_user() - - def configure_logging(self): - """Create logging handlers for any log output.""" - root_logger = logging.getLogger('') - - # Set up logging to a file - root_logger.setLevel(logging.DEBUG) - - # Send higher-level messages to the console via stderr - console = logging.StreamHandler(self.stderr) - console_level = {self.WARNING_LEVEL: logging.WARNING, - self.INFO_LEVEL: logging.INFO, - self.DEBUG_LEVEL: logging.DEBUG, - }.get(self.options.verbose_level, logging.DEBUG) - # The default log level is INFO, in this situation, set the - # log level of the console to WARNING, to avoid displaying - # useless messages. This equals using "--quiet" - if console_level == logging.INFO: - console.setLevel(logging.WARNING) - else: - console.setLevel(console_level) - if logging.DEBUG == console_level: - formatter = logging.Formatter(self.DEBUG_MESSAGE_FORMAT) - else: - formatter = logging.Formatter(self.CONSOLE_MESSAGE_FORMAT) - logging.getLogger('iso8601.iso8601').setLevel(logging.WARNING) - logging.getLogger('urllib3.connectionpool').setLevel(logging.WARNING) - console.setFormatter(formatter) - root_logger.addHandler(console) - return - - -def main(argv=sys.argv[1:]): - try: - print(_("neutron CLI is deprecated and will be removed " - "in the Z cycle. Use openstack CLI instead."), file=sys.stderr) - return NeutronShell(NEUTRON_API_VERSION).run( - list(map(encodeutils.safe_decode, argv))) - except KeyboardInterrupt: - print(_("... terminating neutron client"), file=sys.stderr) - return 130 - except exc.NeutronClientException: - return 1 - except Exception as e: - print(e) - return 1 - - -if __name__ == "__main__": - sys.exit(main(sys.argv[1:])) diff --git a/neutronclient/tests/functional/__init__.py b/neutronclient/tests/functional/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutronclient/tests/functional/adv-svcs/__init__.py b/neutronclient/tests/functional/adv-svcs/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutronclient/tests/functional/adv-svcs/test_readonly_neutron_fwaas.py b/neutronclient/tests/functional/adv-svcs/test_readonly_neutron_fwaas.py deleted file mode 100644 index 9cec62571..000000000 --- a/neutronclient/tests/functional/adv-svcs/test_readonly_neutron_fwaas.py +++ /dev/null @@ -1,42 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from neutronclient.tests.functional import base - - -class SimpleReadOnlyNeutronFwv1ClientTest(base.ClientTestBase): - - """Tests for FWaaS v1 based client commands that are read only""" - - def setUp(self): - super(SimpleReadOnlyNeutronFwv1ClientTest, self).setUp() - if not self.is_extension_enabled('fwaas'): - self.skipTest('FWaaS is not enabled') - - def test_neutron_firewall_list(self): - firewall_list = self.parser.listing(self.neutron - ('firewall-list')) - self.assertTableStruct(firewall_list, ['id', 'name', - 'firewall_policy_id']) - - def test_neutron_firewall_policy_list(self): - firewall_policy = self.parser.listing(self.neutron - ('firewall-policy-list')) - self.assertTableStruct(firewall_policy, ['id', 'name', - 'firewall_rules']) - - def test_neutron_firewall_rule_list(self): - firewall_rule = self.parser.listing(self.neutron - ('firewall-rule-list')) - self.assertTableStruct(firewall_rule, ['id', 'name', - 'firewall_policy_id', - 'summary', 'enabled']) diff --git a/neutronclient/tests/functional/adv-svcs/test_readonly_neutron_vpn.py b/neutronclient/tests/functional/adv-svcs/test_readonly_neutron_vpn.py deleted file mode 100644 index 6e3b6cdcb..000000000 --- a/neutronclient/tests/functional/adv-svcs/test_readonly_neutron_vpn.py +++ /dev/null @@ -1,57 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from neutronclient.tests.functional import base - - -class SimpleReadOnlyNeutronVpnClientTest(base.ClientTestBase): - - """Tests for vpn based client commands that are read only - - This is a first pass at a simple read only python-neutronclient test. - This only exercises vpn based client commands that are read only. - This should test commands: - * as a regular user - * as a admin user - * with and without optional parameters - * initially just check return codes, and later test command outputs - """ - def setUp(self): - super(SimpleReadOnlyNeutronVpnClientTest, self).setUp() - if not self.is_extension_enabled('vpnaas'): - self.skipTest('VPNaaS is not enabled') - - def test_neutron_vpn_ikepolicy_list(self): - ikepolicy = self.parser.listing(self.neutron('vpn-ikepolicy-list')) - self.assertTableStruct(ikepolicy, ['id', 'name', - 'auth_algorithm', - 'encryption_algorithm', - 'ike_version', 'pfs']) - - def test_neutron_vpn_ipsecpolicy_list(self): - ipsecpolicy = self.parser.listing(self.neutron('vpn-ipsecpolicy-list')) - self.assertTableStruct(ipsecpolicy, ['id', 'name', - 'auth_algorithm', - 'encryption_algorithm', - 'pfs']) - - def test_neutron_vpn_service_list(self): - vpn_list = self.parser.listing(self.neutron('vpn-service-list')) - self.assertTableStruct(vpn_list, ['id', 'name', - 'router_id', 'status']) - - def test_neutron_ipsec_site_connection_list(self): - ipsec_site = self.parser.listing(self.neutron - ('ipsec-site-connection-list')) - self.assertTableStruct(ipsec_site, ['id', 'name', - 'peer_address', - 'auth_mode', 'status']) diff --git a/neutronclient/tests/functional/base.py b/neutronclient/tests/functional/base.py deleted file mode 100644 index 655c6c4f8..000000000 --- a/neutronclient/tests/functional/base.py +++ /dev/null @@ -1,80 +0,0 @@ -# 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 os - -import os_client_config -from tempest.lib.cli import base - - -def credentials(cloud='devstack-admin'): - """Retrieves credentials to run functional tests - - Credentials are either read via os-client-config from the environment - or from a config file ('clouds.yaml'). Environment variables override - those from the config file. - - devstack produces a clouds.yaml with two named clouds - one named - 'devstack' which has user privs and one named 'devstack-admin' which - has admin privs. This function will default to getting the devstack-admin - cloud as that is the current expected behavior. - """ - return get_cloud_config(cloud=cloud).get_auth_args() - - -def get_cloud_config(cloud='devstack-admin'): - return os_client_config.OpenStackConfig().get_one_cloud(cloud=cloud) - - -class ClientTestBase(base.ClientTestBase): - """This is a first pass at a simple read only python-neutronclient test. - - This only exercises client commands that are read only. - This should test commands: - * as a regular user - * as an admin user - * with and without optional parameters - * initially just check return codes, and later test command outputs - - """ - - def _get_clients_from_os_cloud_config(self, cloud='devstack-admin'): - creds = credentials(cloud) - cli_dir = os.environ.get( - 'OS_NEUTRONCLIENT_EXEC_DIR', - os.path.join(os.path.abspath('.'), '.tox/functional/bin')) - - return base.CLIClient( - username=creds['username'], - password=creds['password'], - tenant_name=creds['project_name'], - project_domain_id=creds['project_domain_id'], - user_domain_id=creds['user_domain_id'], - uri=creds['auth_url'], - cli_dir=cli_dir) - - def _get_clients(self): - return self._get_clients_from_os_cloud_config() - - def neutron(self, *args, **kwargs): - return self.clients.neutron(*args, **kwargs) - - def neutron_non_admin(self, *args, **kwargs): - if not hasattr(self, '_non_admin_clients'): - self._non_admin_clients = self._get_clients_from_os_cloud_config( - cloud='devstack') - return self._non_admin_clients.neutron(*args, **kwargs) - - def is_extension_enabled(self, extension_alias): - extensions = self.parser.listing(self.neutron('ext-list')) - aliases = [e['alias'] for e in extensions] - return extension_alias in aliases diff --git a/neutronclient/tests/functional/core/__init__.py b/neutronclient/tests/functional/core/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutronclient/tests/functional/core/test_cli_formatter.py b/neutronclient/tests/functional/core/test_cli_formatter.py deleted file mode 100644 index 145ed6c67..000000000 --- a/neutronclient/tests/functional/core/test_cli_formatter.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright 2016 NEC Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from oslo_serialization import jsonutils -from oslo_utils import uuidutils -import yaml - -from neutronclient.tests.functional import base - - -class TestCLIFormatter(base.ClientTestBase): - - def setUp(self): - super(TestCLIFormatter, self).setUp() - self.net_name = 'net-%s' % uuidutils.generate_uuid() - self.addCleanup(self.neutron, 'net-delete %s' % self.net_name) - - def _create_net(self, fmt, col_attrs): - params = ['-c %s' % attr for attr in col_attrs] - params.append('-f %s' % fmt) - params.append(self.net_name) - param_string = ' '.join(params) - return self.neutron('net-create', params=param_string) - - def test_net_create_with_json_formatter(self): - result = self._create_net('json', ['name', 'admin_state_up']) - self.assertDictEqual({'name': self.net_name, - 'admin_state_up': True}, - jsonutils.loads(result)) - - def test_net_create_with_yaml_formatter(self): - result = self._create_net('yaml', ['name', 'admin_state_up']) - self.assertDictEqual({'name': self.net_name, - 'admin_state_up': True}, - yaml.safe_load(result)) - - def test_net_create_with_value_formatter(self): - # NOTE(amotoki): In 'value' formatter, there is no guarantee - # in the order of attribute, so we use one attribute in this test. - result = self._create_net('value', ['name']) - self.assertEqual(self.net_name, result.strip()) - - def test_net_create_with_shell_formatter(self): - result = self._create_net('shell', ['name', 'admin_state_up']) - result_lines = set(result.strip().split('\n')) - self.assertSetEqual(set(['name="%s"' % self.net_name, - 'admin_state_up="True"']), - result_lines) diff --git a/neutronclient/tests/functional/core/test_clientlib.py b/neutronclient/tests/functional/core/test_clientlib.py deleted file mode 100644 index 66be8c68b..000000000 --- a/neutronclient/tests/functional/core/test_clientlib.py +++ /dev/null @@ -1,61 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from keystoneauth1 import session -from oslo_utils import uuidutils -from tempest.lib import base -import testtools - -from neutronclient.common import exceptions -from neutronclient.tests.functional import base as func_base -from neutronclient.v2_0 import client as v2_client - - -class LibraryTestCase(base.BaseTestCase): - - def setUp(self): - super(LibraryTestCase, self).setUp() - self.client = self._get_client() - - def _get_client(self): - cloud_config = func_base.get_cloud_config() - keystone_auth = cloud_config.get_auth() - (verify, cert) = cloud_config.get_requests_verify_args() - - ks_session = session.Session( - auth=keystone_auth, - verify=verify, - cert=cert) - return v2_client.Client(session=ks_session) - - def test_list_network(self): - nets = self.client.list_networks() - self.assertIsInstance(nets['networks'], list) - - def test_post_put_delete_network(self): - name = uuidutils.generate_uuid() - net = self.client.create_network({'network': {'name': name}}) - net_id = net['network']['id'] - self.assertEqual(name, net['network']['name']) - name2 = uuidutils.generate_uuid() - net = self.client.update_network(net_id, {'network': {'name': name2}}) - self.assertEqual(name2, net['network']['name']) - self.client.delete_network(net_id) - with testtools.ExpectedException(exceptions.NetworkNotFoundClient): - self.client.show_network(net_id) - - def test_get_auth_ref(self): - # Call some API call to ensure the client is authenticated. - self.client.list_networks() - auth_ref = self.client.httpclient.get_auth_ref() - self.assertIsNotNone(auth_ref) - self.assertIsNotNone(auth_ref.role_names) diff --git a/neutronclient/tests/functional/core/test_common.py b/neutronclient/tests/functional/core/test_common.py deleted file mode 100644 index 6bdadf96f..000000000 --- a/neutronclient/tests/functional/core/test_common.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright 2016 NEC Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from neutronclient.tests.functional import base - - -class CLICommonFeatureTest(base.ClientTestBase): - - def test_tenant_id_shown_in_list_by_admin(self): - nets = self.parser.table(self.neutron('net-list')) - self.assertIn('tenant_id', nets['headers']) - - def test_tenant_id_not_shown_in_list_with_columns(self): - nets = self.parser.table(self.neutron('net-list -c id -c name')) - self.assertNotIn('tenant_id', nets['headers']) - self.assertListEqual(['id', 'name'], nets['headers']) - - def test_tenant_id_not_shown_in_list_by_non_admin(self): - output = self.neutron_non_admin('net-list') - self.assertNotIn('tenant_id', self.parser.table(output)['headers']) - self.assertTableStruct(self.parser.listing(output), - ['id', 'name']) diff --git a/neutronclient/tests/functional/core/test_purge.py b/neutronclient/tests/functional/core/test_purge.py deleted file mode 100644 index 33b8af44c..000000000 --- a/neutronclient/tests/functional/core/test_purge.py +++ /dev/null @@ -1,174 +0,0 @@ -# Copyright 2016 Cisco Systems -# 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. - -from neutronclient.tests.functional import base - -from tempest.lib import exceptions - - -class PurgeNeutronClientCLITest(base.ClientTestBase): - - def _safe_cleanup(self, delete_command): - try: - self.neutron(delete_command) - except exceptions.CommandFailed: - # This resource was already purged successfully - pass - - def _create_subnet(self, name, tenant_id, cidr): - params = ('%(name)s --name %(name)s --tenant-id %(tenant)s ' - '%(cidr)s' % {'name': name, - 'tenant': tenant_id, - 'cidr': cidr}) - subnet = self.parser.listing(self.neutron('subnet-create', - params=params)) - for row in subnet: - if row['Field'] == 'id': - return row['Value'] - - def _create_router(self, name, tenant_id): - params = ('%(name)s --tenant_id %(tenant)s' % {'name': name, - 'tenant': tenant_id}) - router = self.parser.listing(self.neutron('router-create', - params=params)) - for row in router: - if row['Field'] == 'id': - return row['Value'] - - def _create_floatingip(self, network, tenant_id): - params = ('%(network)s --tenant-id %(tenant)s' % - {'network': network, 'tenant': tenant_id}) - floatingip = self.parser.listing(self.neutron('floatingip-create', - params=params)) - for row in floatingip: - if row['Field'] == 'id': - return row['Value'] - - def _create_resources(self, name, tenant_id, shared_tenant_id=None): - # If no shared_tenant_id is provided, create the resources for the - # current tenant to test that they will be deleted when not in use. - if not shared_tenant_id: - shared_tenant_id = tenant_id - - self.neutron('net-create', - params=('%(name)s --router:external True ' - '--tenant-id %(tenant)s' % {'name': name, - 'tenant': tenant_id})) - self.addCleanup(self._safe_cleanup, 'net-delete %s' % name) - - self.neutron('net-create', - params=('%(name)s-shared --shared ' - '--tenant-id %(tenant)s' % - {'name': name, 'tenant': shared_tenant_id})) - self.addCleanup(self._safe_cleanup, - 'net-delete %s-shared' % name) - - subnet = self._create_subnet(name, tenant_id, '192.168.71.0/24') - self.addCleanup(self._safe_cleanup, 'subnet-delete %s' % name) - - subnet = self._create_subnet('%s-shared' % name, tenant_id, - '192.168.81.0/24') - self.addCleanup(self._safe_cleanup, 'subnet-delete %s-shared' % name) - - router = self._create_router(name, tenant_id) - self.addCleanup(self._safe_cleanup, 'router-delete %s' % name) - - self.neutron('router-interface-add', - params=('%(router)s %(subnet)s ' - '--tenant-id %(tenant)s' % {'router': router, - 'subnet': subnet, - 'tenant': tenant_id})) - - self.neutron('port-create', - params=('%(name)s --name %(name)s ' - '--tenant-id %(tenant)s' % {'name': name, - 'tenant': tenant_id})) - self.addCleanup(self._safe_cleanup, 'port-delete %s' % name) - - self.neutron('port-create', - params=('%(name)s-shared --name %(name)s-shared ' - '--tenant-id %(tenant)s' % {'name': name, - 'tenant': tenant_id})) - self.addCleanup(self._safe_cleanup, 'port-delete %s-shared' % name) - - self.neutron('security-group-create', - params=('%(name)s --tenant-id %(tenant)s' % - {'name': name, 'tenant': tenant_id})) - self.addCleanup(self._safe_cleanup, 'security-group-delete %s' % name) - - floatingip = self._create_floatingip(name, tenant_id) - self.addCleanup(self._safe_cleanup, ('floatingip-delete ' - '%s' % floatingip)) - return floatingip - - def _verify_deletion(self, resources, resource_type): - purged = True - no_purge_purged = True - router_interface_owners = ['network:router_interface', - 'network:router_interface_distributed'] - for row in resources: - if resource_type == 'port' and row.get('id', None): - port = self.parser.listing(self.neutron('port-show', - params=row['id'])) - port_dict = {} - for row in port: - port_dict[row['Field']] = row['Value'] - if port_dict['device_owner'] in router_interface_owners: - if port_dict['tenant_id'] == 'purge-tenant': - purged = False - elif port_dict['tenant_id'] == 'no-purge-tenant': - no_purge_purged = False - if not purged or not no_purge_purged: - self.addCleanup(self.neutron, - ('router-interface-delete %(router)s ' - 'port=%(port)s' % - {'router': port_dict['device_id'], - 'port': port_dict['id']})) - if (row.get('name') == 'purge-me' or - row.get('id') == self.purge_floatingip): - purged = False - elif ('no-purge' in row.get('name', '') or - row.get('id') == self.no_purge_floatingip): - no_purge_purged = False - - if not purged: - self.fail('%s not deleted by neutron purge' % resource_type) - - if no_purge_purged: - self.fail('%s owned by another tenant incorrectly deleted ' - 'by neutron purge' % resource_type) - - def test_purge(self): - self.purge_floatingip = self._create_resources('purge-me', - 'purge-tenant') - self.no_purge_floatingip = self._create_resources('no-purge', - 'no-purge-tenant', - 'purge-tenant') - - purge_output = self.neutron('purge', params='purge-tenant').strip() - if not purge_output: - self.fail('Purge command did not return feedback') - - networks = self.parser.listing(self.neutron('net-list')) - subnets = self.parser.listing(self.neutron('subnet-list')) - routers = self.parser.listing(self.neutron('router-list')) - ports = self.parser.listing(self.neutron('port-list')) - floatingips = self.parser.listing(self.neutron('floatingip-list')) - - self._verify_deletion(networks, 'network') - self._verify_deletion(subnets, 'subnet') - self._verify_deletion(ports, 'port') - self._verify_deletion(routers, 'router') - self._verify_deletion(floatingips, 'floatingip') diff --git a/neutronclient/tests/functional/core/test_readonly_neutron.py b/neutronclient/tests/functional/core/test_readonly_neutron.py deleted file mode 100644 index 4fe989711..000000000 --- a/neutronclient/tests/functional/core/test_readonly_neutron.py +++ /dev/null @@ -1,136 +0,0 @@ -# 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 re - -from tempest.lib import exceptions - -from neutronclient.tests.functional import base - - -class SimpleReadOnlyNeutronClientTest(base.ClientTestBase): - - """This is a first pass at a simple read only python-neutronclient test. - - This only exercises client commands that are read only. - This should test commands: - * as a regular user - * as a admin user - * with and without optional parameters - * initially just check return codes, and later test command outputs - """ - - def test_admin_fake_action(self): - self.assertRaises(exceptions.CommandFailed, - self.neutron, - 'this-does-neutron-exist') - - # NOTE(mestery): Commands in order listed in 'neutron help' - - # Optional arguments: - - def test_neutron_fake_action(self): - self.assertRaises(exceptions.CommandFailed, - self.neutron, - 'this-does-not-exist') - - def test_neutron_net_list(self): - net_list = self.parser.listing(self.neutron('net-list')) - self.assertTableStruct(net_list, ['id', 'name', 'subnets']) - - def test_neutron_ext_list(self): - ext = self.parser.listing(self.neutron('ext-list')) - self.assertTableStruct(ext, ['alias', 'name']) - - def test_neutron_dhcp_agent_list_hosting_net(self): - self.neutron('dhcp-agent-list-hosting-net', - params='private') - - def test_neutron_agent_list(self): - agents = self.parser.listing(self.neutron('agent-list')) - field_names = ['id', 'agent_type', 'host', 'alive', 'admin_state_up'] - self.assertTableStruct(agents, field_names) - - def test_neutron_floatingip_list(self): - self.neutron('floatingip-list') - - def test_neutron_meter_label_list(self): - if not self.is_extension_enabled('metering'): - self.skipTest('metering is not enabled') - self.neutron('meter-label-list') - - def test_neutron_meter_label_rule_list(self): - if not self.is_extension_enabled('metering'): - self.skipTest('metering is not enabled') - self.neutron('meter-label-rule-list') - - def test_neutron_net_external_list(self): - net_ext_list = self.parser.listing(self.neutron('net-external-list')) - self.assertTableStruct(net_ext_list, ['id', 'name', 'subnets']) - - def test_neutron_port_list(self): - port_list = self.parser.listing(self.neutron('port-list')) - self.assertTableStruct(port_list, ['id', 'name', 'mac_address', - 'fixed_ips']) - - def test_neutron_quota_list(self): - self.neutron('quota-list') - - def test_neutron_router_list(self): - router_list = self.parser.listing(self.neutron('router-list')) - self.assertTableStruct(router_list, ['id', 'name', - 'external_gateway_info']) - - def test_neutron_security_group_list(self): - security_grp = self.parser.listing(self.neutron('security-group-list')) - self.assertTableStruct(security_grp, ['id', 'name', - 'security_group_rules']) - - def test_neutron_security_group_rule_list(self): - security_grp = self.parser.listing(self.neutron - ('security-group-rule-list')) - self.assertTableStruct(security_grp, ['id', 'security_group', - 'direction', 'ethertype', - 'port/protocol', 'remote']) - - def test_neutron_subnet_list(self): - subnet_list = self.parser.listing(self.neutron('subnet-list')) - self.assertTableStruct(subnet_list, ['id', 'name', 'cidr', - 'allocation_pools']) - - def test_neutron_help(self): - help_text = self.neutron('help') - lines = help_text.split('\n') - self.assertFirstLineStartsWith(lines, 'usage: neutron') - - commands = [] - cmds_start = lines.index('Commands for API v2.0:') - command_pattern = re.compile(r'^ {2}([a-z0-9\-\_]+)') - for line in lines[cmds_start:]: - match = command_pattern.match(line) - if match: - commands.append(match.group(1)) - commands = set(commands) - wanted_commands = set(('net-create', 'subnet-list', 'port-delete', - 'router-show', 'agent-update', 'help')) - self.assertFalse(wanted_commands - commands) - - # Optional arguments: - - def test_neutron_version(self): - self.neutron('', flags='--version') - - def test_neutron_debug_net_list(self): - self.neutron('net-list', flags='--debug') - - def test_neutron_quiet_net_list(self): - self.neutron('net-list', flags='--quiet') diff --git a/neutronclient/tests/functional/core/test_subnet_create.py b/neutronclient/tests/functional/core/test_subnet_create.py deleted file mode 100644 index ed7f390b8..000000000 --- a/neutronclient/tests/functional/core/test_subnet_create.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright 2015 Hewlett-Packard Development Company, L.P -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from neutronclient.tests.functional import base - - -class SubnetCreateNeutronClientCLITest(base.ClientTestBase): - - def test_create_subnet_net_name_first(self): - self.neutron('net-create', params='netwrk-1') - self.addCleanup(self.neutron, 'net-delete netwrk-1') - self.neutron('subnet-create netwrk-1', - params='--name fake --gateway 192.168.51.1 ' - '192.168.51.0/24') - self.addCleanup(self.neutron, 'subnet-delete fake') - subnet_list = self.parser.listing(self.neutron('subnet-list')) - self.assertTableStruct(subnet_list, ['id', 'name', 'cidr', - 'allocation_pools']) - found = False - for row in subnet_list: - if row.get('name') == 'fake': - found = True - break - if not found: - self.fail('Created subnet not found in list') diff --git a/neutronclient/tests/functional/hooks/fwaas b/neutronclient/tests/functional/hooks/fwaas deleted file mode 100644 index d9fc704ef..000000000 --- a/neutronclient/tests/functional/hooks/fwaas +++ /dev/null @@ -1,2 +0,0 @@ -enable_plugin neutron-fwaas https://opendev.org/openstack/neutron-fwaas -enable_service q-fwaas diff --git a/neutronclient/tests/functional/hooks/gate_hook.sh b/neutronclient/tests/functional/hooks/gate_hook.sh deleted file mode 100755 index c1721d88d..000000000 --- a/neutronclient/tests/functional/hooks/gate_hook.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env bash - -set -ex - -VENV=${1:-"functional"} - -GATE_DEST=$BASE/new -NEUTRONCLIENT_PATH=$GATE_DEST/python-neutronclient -GATE_HOOKS=$NEUTRONCLIENT_PATH/neutronclient/tests/functional/hooks -DEVSTACK_PATH=$GATE_DEST/devstack -LOCAL_CONF=$DEVSTACK_PATH/late-local.conf -DSCONF=/tmp/devstack-tools/bin/dsconf - -# Install devstack-tools used to produce local.conf; we can't rely on -# test-requirements.txt because the gate hook is triggered before neutronclient -# is installed -sudo -H pip install virtualenv -virtualenv /tmp/devstack-tools -/tmp/devstack-tools/bin/pip install -U devstack-tools==0.4.0 - -# Inject config from hook into localrc -function load_rc_hook { - local hook="$1" - local tmpfile - local config - tmpfile=$(tempfile) - config=$(cat $GATE_HOOKS/$hook) - echo "[[local|localrc]]" > $tmpfile - $DSCONF setlc_raw $tmpfile "$config" - $DSCONF merge_lc $LOCAL_CONF $tmpfile - rm -f $tmpfile -} - - -if [ "$VENV" == "functional-adv-svcs" ] -then - load_rc_hook fwaas - load_rc_hook vpnaas -fi - -export DEVSTACK_LOCALCONF=$(cat $LOCAL_CONF) -$BASE/new/devstack-gate/devstack-vm-gate.sh diff --git a/neutronclient/tests/functional/hooks/post_test_hook.sh b/neutronclient/tests/functional/hooks/post_test_hook.sh deleted file mode 100755 index d62d13911..000000000 --- a/neutronclient/tests/functional/hooks/post_test_hook.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/bin/bash -xe - -# 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. - -# This script is executed inside post_test_hook function in devstack gate. - -SCRIPTS_DIR="/usr/os-testr-env/bin/" - -function generate_test_logs { - local path="$1" - # Compress all $path/*.txt files and move the directories holding those - # files to /opt/stack/logs. Files with .log suffix have their - # suffix changed to .txt (so browsers will know to open the compressed - # files and not download them). - if [ -d "$path" ] - then - sudo find $path -iname "*.log" -type f -exec mv {} {}.txt \; -exec gzip -9 {}.txt \; - sudo mv $path/* /opt/stack/logs/ - fi -} - -function generate_testr_results { - # Give job user rights to access tox logs - sudo -H -u $USER chmod o+rw . - sudo -H -u $USER chmod o+rw -R .stestr - if [ -f ".stestr/0" ] ; then - .tox/$VENV/bin/subunit-1to2 < .stestr/0 > ./stestr.subunit - $SCRIPTS_DIR/subunit2html ./stestr.subunit testr_results.html - gzip -9 ./stestr.subunit - gzip -9 ./testr_results.html - sudo mv ./*.gz /opt/stack/logs/ - fi - - if [ "$venv" == "functional" ] || [ "$venv" == "functional-adv-svcs" ] - then - generate_test_logs "/tmp/${venv}-logs" - fi -} - -export NEUTRONCLIENT_DIR="$BASE/new/python-neutronclient" - -sudo chown -R $USER:stack $NEUTRONCLIENT_DIR - -# Go to the neutronclient dir -cd $NEUTRONCLIENT_DIR - -# Run tests -VENV=${1:-"functional"} -echo "Running neutronclient functional test suite" -set +e -# Preserve env for OS_ credentials -sudo -E -H -u $USER tox -e $VENV -EXIT_CODE=$? -set -e - -# Collect and parse result -generate_testr_results -exit $EXIT_CODE diff --git a/neutronclient/tests/functional/hooks/vpnaas b/neutronclient/tests/functional/hooks/vpnaas deleted file mode 100644 index 8b94b37b0..000000000 --- a/neutronclient/tests/functional/hooks/vpnaas +++ /dev/null @@ -1 +0,0 @@ -enable_plugin neutron-vpnaas https://opendev.org/openstack/neutron-vpnaas diff --git a/neutronclient/tests/unit/bgp/__init__.py b/neutronclient/tests/unit/bgp/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutronclient/tests/unit/bgp/test_cli20_dragentscheduler.py b/neutronclient/tests/unit/bgp/test_cli20_dragentscheduler.py deleted file mode 100644 index cbe85d92f..000000000 --- a/neutronclient/tests/unit/bgp/test_cli20_dragentscheduler.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright 2016 Huawei Technologies India Pvt. Ltd. -# 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 sys - -from neutronclient.neutron.v2_0.bgp import dragentscheduler as bgp_drsched -from neutronclient.tests.unit import test_cli20 -from neutronclient.tests.unit import test_cli20_agentschedulers as test_as - - -BGP_DRAGENT_ID = 'bgp_dragent_id1' -BGP_SPEAKER = 'bgp_speaker_id1' - - -class CLITestV20DRAgentScheduler(test_as.CLITestV20AgentScheduler): - - def test_add_bgp_speaker_to_dragent(self): - resource = 'agent' - cmd = bgp_drsched.AddBGPSpeakerToDRAgent( - test_cli20.MyApp(sys.stdout), None) - args = (BGP_DRAGENT_ID, BGP_SPEAKER) - body = {'bgp_speaker_id': BGP_SPEAKER} - result = {'bgp_speaker_id': 'bgp_speaker_id', } - self._test_add_to_agent(resource, cmd, args, - self.client.BGP_DRINSTANCES, - body, result) - - def test_remove_bgp_speaker_from_dragent(self): - resource = 'agent' - cmd = bgp_drsched.RemoveBGPSpeakerFromDRAgent( - test_cli20.MyApp(sys.stdout), None) - args = (BGP_DRAGENT_ID, BGP_SPEAKER) - self._test_remove_from_agent(resource, cmd, args, - self.client.BGP_DRINSTANCES) - - def test_list_bgp_speakers_on_dragent(self): - resources = 'bgp_speakers' - cmd = bgp_drsched.ListBGPSpeakersOnDRAgent( - test_cli20.MyApp(sys.stdout), None) - path = ((self.client.agent_path + self.client.BGP_DRINSTANCES) % - BGP_DRAGENT_ID) - self._test_list_resources(resources, cmd, base_args=[BGP_DRAGENT_ID], - path=path) - - def test_list_dragents_hosting_bgp_speaker(self): - resources = 'agent' - cmd = bgp_drsched.ListDRAgentsHostingBGPSpeaker( - test_cli20.MyApp(sys.stdout), None) - path = ((self.client.bgp_speaker_path + self.client.BGP_DRAGENTS) % - BGP_DRAGENT_ID) - contents = {self.id_field: 'myid1', 'alive': True} - self._test_list_resources(resources, cmd, base_args=[BGP_DRAGENT_ID], - path=path, response_contents=contents) diff --git a/neutronclient/tests/unit/bgp/test_cli20_peer.py b/neutronclient/tests/unit/bgp/test_cli20_peer.py deleted file mode 100644 index 998b0d5e3..000000000 --- a/neutronclient/tests/unit/bgp/test_cli20_peer.py +++ /dev/null @@ -1,224 +0,0 @@ -# Copyright 2016 Huawei Technologies India Pvt. Ltd. -# 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 sys - -from neutronclient.common import exceptions -from neutronclient.neutron.v2_0.bgp import peer as bgp_peer -from neutronclient.neutron.v2_0.bgp import speaker as bgp_speaker -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20BGPPeerJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['bgp_peer'] - - def test_create_bgp_peer_with_mandatory_params(self): - # Create BGP peer with mandatory params. - resource = 'bgp_peer' - cmd = bgp_peer.CreatePeer(test_cli20.MyApp(sys.stdout), - None) - name = 'my-name' - my_id = 'my-id' - peerip = '1.1.1.1' - remote_asnum = '1' - args = [name, - '--peer-ip', peerip, - '--remote-as', remote_asnum, ] - position_names = ['name', 'peer_ip', 'remote_as', - 'auth_type'] - position_values = [name, peerip, remote_asnum, 'none'] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values) - - def test_create_bgp_peer_with_all_params(self): - # Create BGP peer with all params. - resource = 'bgp_peer' - cmd = bgp_peer.CreatePeer(test_cli20.MyApp(sys.stdout), - None) - name = 'my-name' - my_id = 'my-id' - peerip = '1.1.1.1' - remote_asnum = '65535' - authType = 'md5' - password = 'abc' - args = [name, - '--peer-ip', peerip, - '--remote-as', remote_asnum, - '--auth-type', authType, - '--password', password] - position_names = ['name', 'peer_ip', 'remote_as', - 'auth_type', 'password'] - position_values = [name, peerip, remote_asnum, authType, password] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values) - - def test_create_bgp_peer_with_invalid_min_remote_asnum(self): - # Create BGP peer with invalid minimum remote-asnum. - resource = 'bgp_peer' - cmd = bgp_peer.CreatePeer(test_cli20.MyApp(sys.stdout), - None) - name = 'my-name' - my_id = 'my-id' - peerip = '1.1.1.1' - remote_asnum = '0' - args = [name, - '--peer-ip', peerip, - '--remote-as', remote_asnum, ] - position_names = ['name', 'peer_ip', 'remote_as', ] - position_values = [name, peerip, remote_asnum, ] - exc = self.assertRaises(exceptions.CommandError, - self._test_create_resource, - resource, cmd, name, my_id, args, - position_names, position_values) - self.assertEqual('remote-as "0" should be an integer [%s:%s].' % - (bgp_speaker.MIN_AS_NUM, bgp_speaker.MAX_AS_NUM), - str(exc)) - - def test_create_bgp_peer_with_invalid_max_remote_asnum(self): - # Create BGP peer with invalid maximum remote-asnum. - resource = 'bgp_peer' - cmd = bgp_peer.CreatePeer(test_cli20.MyApp(sys.stdout), - None) - name = 'my-name' - my_id = 'my-id' - peerip = '1.1.1.1' - remote_asnum = '4294967296' - args = [name, - '--peer-ip', peerip, - '--remote-as', remote_asnum, ] - position_names = ['name', 'peer_ip', 'remote_as', - 'auth_type', 'password'] - position_values = [name, peerip, remote_asnum, 'none', ''] - exc = self.assertRaises(exceptions.CommandError, - self._test_create_resource, - resource, cmd, name, my_id, args, - position_names, position_values) - self.assertEqual('remote-as "4294967296" should be an ' - 'integer [%s:%s].' % - (bgp_speaker.MIN_AS_NUM, bgp_speaker.MAX_AS_NUM), - str(exc)) - - def test_create_authenticated_bgp_peer_without_authtype(self): - # Create authenticated BGP peer without auth-type. - resource = 'bgp_peer' - cmd = bgp_peer.CreatePeer(test_cli20.MyApp(sys.stdout), - None) - name = 'my-name' - my_id = 'my-id' - peerip = '1.1.1.1' - remote_asnum = '2048' - password = 'abc' - args = [name, - '--peer-ip', peerip, - '--remote-as', remote_asnum, - '--password', password] - position_names = ['name', 'peer_ip', 'remote_as', 'password'] - position_values = [name, peerip, remote_asnum, password] - exc = self.assertRaises(exceptions.CommandError, - self._test_create_resource, - resource, cmd, name, my_id, args, - position_names, position_values) - self.assertEqual('Must provide auth-type if password is specified.', - str(exc)) - - def test_create_authenticated_bgp_peer_without_password(self): - # Create authenticated BGP peer without password. - resource = 'bgp_peer' - cmd = bgp_peer.CreatePeer(test_cli20.MyApp(sys.stdout), - None) - name = 'my-name' - my_id = 'my-id' - peerip = '1.1.1.1' - remote_asnum = '2048' - authType = 'md5' - args = [name, - '--peer-ip', peerip, - '--remote-as', remote_asnum, - '--auth-type', authType] - position_names = ['name', 'peer_ip', 'remote_as', 'auth_type'] - position_values = [name, peerip, remote_asnum, authType] - exc = self.assertRaises(exceptions.CommandError, - self._test_create_resource, - resource, cmd, name, my_id, args, - position_names, position_values) - self.assertEqual('Must provide password if auth-type is specified.', - str(exc)) - - def test_update_bgp_peer(self): - # Update BGP peer: - # myid --advertise-tenant-networks True - # --advertise-floating-ip-host-routes False - resource = 'bgp_peer' - cmd = bgp_peer.UpdatePeer(test_cli20.MyApp(sys.stdout), - None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'new-name', - '--password', 'abc'], - {'name': 'new-name', 'password': 'abc'}) - - def test_update_bgp_peer_exception(self): - # Update BGP peer: myid. - resource = 'bgp_peer' - cmd = bgp_peer.UpdatePeer(test_cli20.MyApp(sys.stdout), - None) - self.assertRaises(exceptions.CommandError, - self._test_update_resource, - resource, cmd, 'myid', ['myid'], {}) - - def test_list_bgp_peer(self): - # List all BGP peers. - resources = "bgp_peers" - cmd = bgp_peer.ListPeers(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, True) - - # TODO(Vikram): Add test_list_bgp_peer_pagination - - def test_list_bgp_peer_sort(self): - # sorted list: bgp-peer-list --sort-key name --sort-key id - # --sort-key asc --sort-key desc - resources = "bgp_peers" - cmd = bgp_peer.ListPeers(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_bgp_peer_limit(self): - # size (1000) limited list: bgp-peer-list -P. - resources = "bgp_peers" - cmd = bgp_peer.ListPeers(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_bgp_peer(self): - # Show BGP peer: --fields id --fields name myid. - resource = 'bgp_peer' - cmd = bgp_peer.ShowPeer(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, - ['id', 'name']) - - def test_delete_bgp_peer(self): - # Delete BGP peer: bgp_peer_id. - resource = 'bgp_peer' - cmd = bgp_peer.DeletePeer(test_cli20.MyApp(sys.stdout), - None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) diff --git a/neutronclient/tests/unit/bgp/test_cli20_speaker.py b/neutronclient/tests/unit/bgp/test_cli20_speaker.py deleted file mode 100644 index 5621576d9..000000000 --- a/neutronclient/tests/unit/bgp/test_cli20_speaker.py +++ /dev/null @@ -1,273 +0,0 @@ -# Copyright 2016 Huawei Technologies India Pvt. Ltd. -# 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 sys -from unittest import mock - - -from neutronclient.common import exceptions -from neutronclient.neutron.v2_0.bgp import speaker as bgp_speaker -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20BGPSpeakerJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['bgp_speaker'] - - def test_create_bgp_speaker_with_minimal_options(self): - # Create BGP Speaker with mandatory params. - resource = 'bgp_speaker' - cmd = bgp_speaker.CreateSpeaker(test_cli20.MyApp(sys.stdout), - None) - name = 'my-name' - my_id = 'my-id' - local_asnum = '1' - args = [name, '--local-as', local_asnum, ] - position_names = ['name', 'local_as', 'ip_version'] - position_values = [name, local_asnum, 4] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values) - - def test_create_ipv4_bgp_speaker_with_all_params(self): - # Create BGP Speaker with all params. - resource = 'bgp_speaker' - cmd = bgp_speaker.CreateSpeaker(test_cli20.MyApp(sys.stdout), - None) - name = 'my-name' - my_id = 'my-id' - local_asnum = '1' - args = [name, - '--local-as', local_asnum, - '--ip-version', '4', - '--advertise-floating-ip-host-routes', 'True', - '--advertise-tenant-networks', 'True'] - position_names = ['name', 'local_as', 'ip_version', - 'advertise_floating_ip_host_routes', - 'advertise_tenant_networks'] - position_values = [name, local_asnum, 4, 'True', 'True'] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values) - - def test_create_ipv6_bgp_speaker_with_all_params(self): - # Create BGP Speaker with all params. - resource = 'bgp_speaker' - cmd = bgp_speaker.CreateSpeaker(test_cli20.MyApp(sys.stdout), - None) - name = 'my-name' - my_id = 'my-id' - local_asnum = '65535' - args = [name, - '--local-as', local_asnum, - '--ip-version', '6', - '--advertise-floating-ip-host-routes', 'True', - '--advertise-tenant-networks', 'True'] - position_names = ['name', 'local_as', 'ip_version', - 'advertise_floating_ip_host_routes', - 'advertise_tenant_networks'] - position_values = [name, local_asnum, 6, 'True', 'True'] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values) - - def test_create_bgp_speaker_with_invalid_min_local_asnum(self): - # Create BGP Speaker with invalid minimum local-asnum. - resource = 'bgp_speaker' - cmd = bgp_speaker.CreateSpeaker(test_cli20.MyApp(sys.stdout), - None) - name = 'my-name' - my_id = 'my-id' - local_asnum = '0' - args = [name, - '--local-as', local_asnum] - position_names = ['name', 'local_as'] - position_values = [name, local_asnum] - exc = self.assertRaises(exceptions.CommandError, - self._test_create_resource, - resource, cmd, name, my_id, args, - position_names, position_values) - self.assertEqual('local-as "0" should be an integer [%s:%s].' % - (bgp_speaker.MIN_AS_NUM, bgp_speaker.MAX_AS_NUM), - str(exc)) - - def test_create_bgp_speaker_with_invalid_max_local_asnum(self): - # Create BGP Speaker with invalid maximum local-asnum. - resource = 'bgp_speaker' - cmd = bgp_speaker.CreateSpeaker(test_cli20.MyApp(sys.stdout), - None) - name = 'my-name' - my_id = 'my-id' - local_asnum = '4294967296' - args = [name, - '--local-as', local_asnum] - position_names = ['name', 'local_as', ] - position_values = [name, local_asnum, ] - exc = self.assertRaises(exceptions.CommandError, - self._test_create_resource, - resource, cmd, name, my_id, args, - position_names, position_values) - self.assertEqual('local-as "4294967296" should be an ' - 'integer [%s:%s].' % - (bgp_speaker.MIN_AS_NUM, bgp_speaker.MAX_AS_NUM), - str(exc)) - - def test_update_bgp_speaker(self): - # Update BGP Speaker: - # myid --advertise-tenant-networks True - # --advertise-floating-ip-host-routes False - resource = 'bgp_speaker' - cmd = bgp_speaker.UpdateSpeaker(test_cli20.MyApp(sys.stdout), - None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', - '--name', 'new-name', - '--advertise-tenant-networks', 'True', - '--advertise-floating-ip-host-routes', - 'False'], - {'name': 'new-name', - 'advertise_tenant_networks': 'True', - 'advertise_floating_ip_host_routes': - 'False'}) - - def test_update_bgp_speaker_exception(self): - # Update BGP Speaker: myid. - resource = 'bgp_speaker' - cmd = bgp_speaker.UpdateSpeaker(test_cli20.MyApp(sys.stdout), - None) - self.assertRaises(exceptions.CommandError, - self._test_update_resource, - resource, cmd, 'myid', ['myid'], {}) - - def test_list_bgp_speaker(self): - # List all BGP Speakers. - resources = "bgp_speakers" - cmd = bgp_speaker.ListSpeakers(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, True) - - @mock.patch.object(bgp_speaker.ListSpeakers, "extend_list") - def test_list_bgp_speaker_pagination(self, mock_extend_list): - # List all BGP Speakers with pagination support. - cmd = bgp_speaker.ListSpeakers(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources_with_pagination("bgp_speakers", - cmd) - mock_extend_list.assert_called_once_with(test_cli20.IsA(list), - mock.ANY) - - def test_list_bgp_speaker_sort(self): - # sorted list: bgp-speaker-list --sort-key name --sort-key id - # --sort-key asc --sort-key desc - resources = "bgp_speakers" - cmd = bgp_speaker.ListSpeakers(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_bgp_speaker_limit(self): - # size (1000) limited list: bgp-speaker-list -P. - resources = "bgp_speakers" - cmd = bgp_speaker.ListSpeakers(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_bgp_speaker(self): - # Show BGP Speaker: --fields id --fields name myid. - resource = 'bgp_speaker' - cmd = bgp_speaker.ShowSpeaker(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, - ['id', 'name']) - - def test_delete_bgp_speaker(self): - # Delete BGP Speaker: bgp_speaker_id. - resource = 'bgp_speaker' - cmd = bgp_speaker.DeleteSpeaker(test_cli20.MyApp(sys.stdout), - None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) - - def _test_add_remove_peer(self, action, cmd, args): - """Add or Remove BGP Peer to/from a BGP Speaker.""" - resource = 'bgp_speaker' - subcmd = '%s_bgp_peer' % action - body = {'bgp_peer_id': 'peerid'} - if action == 'add': - retval = {'bgp_peer': 'peerid'} - retval = self.client.serialize(retval) - expected_code = 200 - else: - retval = None - expected_code = 204 - self._test_update_resource_action(resource, cmd, 'myid', - subcmd, args, body, expected_code, - retval) - - def test_add_peer_to_bgp_speaker(self): - # Add peer to BGP speaker: myid peer_id=peerid - cmd = bgp_speaker.AddPeerToSpeaker(test_cli20.MyApp(sys.stdout), - None) - args = ['myid', 'peerid'] - self._test_add_remove_peer('add', cmd, args) - - def test_remove_peer_from_bgp_speaker(self): - # Remove peer from BGP speaker: myid peer_id=peerid - cmd = bgp_speaker.RemovePeerFromSpeaker(test_cli20.MyApp(sys.stdout), - None) - args = ['myid', 'peerid'] - self._test_add_remove_peer('remove', cmd, args) - - def _test_add_remove_network(self, action, cmd, args): - # Add or Remove network to/from a BGP Speaker. - resource = 'bgp_speaker' - subcmd = '%s_gateway_network' % action - body = {'network_id': 'netid'} - if action == 'add': - retval = {'network': 'netid'} - retval = self.client.serialize(retval) - expected_code = 200 - else: - retval = None - expected_code = 204 - self._test_update_resource_action(resource, cmd, 'myid', - subcmd, args, body, expected_code, - retval) - - def test_add_network_to_bgp_speaker(self): - # Add peer to BGP speaker: myid network_id=netid - cmd = bgp_speaker.AddNetworkToSpeaker(test_cli20.MyApp(sys.stdout), - None) - args = ['myid', 'netid'] - self._test_add_remove_network('add', cmd, args) - - def test_remove_network_from_bgp_speaker(self): - # Remove network from BGP speaker: myid network_id=netid - cmd = bgp_speaker.RemoveNetworkFromSpeaker( - test_cli20.MyApp(sys.stdout), None) - args = ['myid', 'netid'] - self._test_add_remove_network('remove', cmd, args) - - def test_list_routes_advertised_by_a_bgp_speaker(self): - # Retrieve advertised route list - resources = 'advertised_routes' - cmd = bgp_speaker.ListRoutesAdvertisedBySpeaker( - test_cli20.MyApp(sys.stdout), None) - bs_id = 'bgp_speaker_id1' - path = ((self.client.bgp_speaker_path + '/get_advertised_routes') % - bs_id) - self._test_list_resources(resources, cmd, base_args=[bs_id], - path=path) diff --git a/neutronclient/tests/unit/flavor/__init__.py b/neutronclient/tests/unit/flavor/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutronclient/tests/unit/flavor/test_cli20_flavor.py b/neutronclient/tests/unit/flavor/test_cli20_flavor.py deleted file mode 100644 index 22bd8bb9b..000000000 --- a/neutronclient/tests/unit/flavor/test_cli20_flavor.py +++ /dev/null @@ -1,154 +0,0 @@ -# Copyright 2015 Hewlett-Packard Development Company, L.P. -# 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 sys - -from neutronclient.neutron.v2_0.flavor import flavor -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20FlavorJSON(test_cli20.CLITestV20Base): - - def setUp(self): - """Prepare test environment.""" - super(CLITestV20FlavorJSON, self).setUp(plurals={'flavors': 'flavor'}) - self.register_non_admin_status_resource('flavor') - self.register_non_admin_status_resource('service_profile') - - def test_create_flavor_with_missing_params(self): - """Create test flavor with missing parameters.""" - resource = 'flavor' - cmd = flavor.CreateFlavor( - test_cli20.MyApp(sys.stdout), None) - name = 'Test flavor' - myid = 'myid' - position_names = [] - position_values = [] - args = [] - self.assertRaises( - SystemExit, self._test_create_resource, - resource, cmd, name, myid, args, position_names, position_values) - - def test_create_flavor_with_mandatory_params(self): - """Create test flavor with minimal parameters.""" - resource = 'flavor' - cmd = flavor.CreateFlavor( - test_cli20.MyApp(sys.stdout), None) - name = 'Test flavor' - myid = 'myid' - service_type = 'DUMMY' - # Defaults are returned in body - position_names = ['name', 'service_type'] - position_values = [name, service_type] - args = [name, service_type] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_flavor_with_optional_params(self): - """Create test flavor including optional parameters.""" - resource = 'flavor' - cmd = flavor.CreateFlavor( - test_cli20.MyApp(sys.stdout), None) - name = 'Test flavor' - myid = 'myid' - service_type = 'DUMMY' - description = 'Test description' - position_names = ['name', 'service_type', 'description', 'enabled'] - position_values = [name, service_type, description, 'False'] - args = [name, service_type, - '--description', description, - '--enabled=False'] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_delete_flavor(self): - """Delete flavor.""" - resource = 'flavor' - cmd = flavor.DeleteFlavor(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args) - - def test_list_flavors(self): - """List flavors test.""" - resources = 'flavors' - cmd = flavor.ListFlavor( - test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_flavors_with_pagination(self): - """List flavors test with pagination.""" - resources = 'flavors' - cmd = flavor.ListFlavor( - test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_flavors_with_sort(self): - """List flavors test with sorting by name and id.""" - resources = 'flavors' - cmd = flavor.ListFlavor( - test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_show_flavor(self): - """Show flavor test.""" - resource = 'flavor' - cmd = flavor.ShowFlavor( - test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - - def test_update_flavor_with_name(self): - """Update flavor test.""" - resource = 'flavor' - cmd = flavor.UpdateFlavor( - test_cli20.MyApp(sys.stdout), None) - newname = 'Test New Name' - newdescription = 'New Description' - args = ['--name', newname, - '--description', newdescription, - '--enabled', 'False', self.test_id] - self._test_update_resource(resource, cmd, self.test_id, args, - {'name': newname, - 'description': newdescription, - 'enabled': 'False'}) - - def test_associate_flavor(self): - """Associate flavor test.""" - resource = 'service_profile' - cmd = flavor.AssociateFlavor(test_cli20.MyApp(sys.stdout), None) - flavor_id = 'flavor-id' - profile_id = 'profile-id' - name = '' - args = [flavor_id, profile_id] - position_names = ['id'] - position_values = [profile_id] - self._test_create_resource(resource, cmd, name, profile_id, args, - position_names, position_values, - cmd_resource='flavor_profile_binding', - parent_id=flavor_id) - - def test_disassociate_flavor(self): - """Disassociate flavor test.""" - resource = 'flavor_profile_binding' - cmd = flavor.DisassociateFlavor(test_cli20.MyApp(sys.stdout), None) - flavor_id = 'flavor-id' - profile_id = 'profile-id' - args = [flavor_id, profile_id] - self._test_delete_resource(resource, cmd, profile_id, args, - parent_id=flavor_id) diff --git a/neutronclient/tests/unit/flavor/test_cli20_flavor_profile.py b/neutronclient/tests/unit/flavor/test_cli20_flavor_profile.py deleted file mode 100644 index d13090297..000000000 --- a/neutronclient/tests/unit/flavor/test_cli20_flavor_profile.py +++ /dev/null @@ -1,122 +0,0 @@ -# Copyright 2015 Hewlett-Packard Development Company, L.P. -# 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 sys - -from neutronclient.neutron.v2_0.flavor import flavor_profile -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20FlavorProfileJSON(test_cli20.CLITestV20Base): - - def setUp(self): - """Prepare test environment.""" - super(CLITestV20FlavorProfileJSON, self).setUp( - plurals={'service_profiles': 'service_profile'}) - self.register_non_admin_status_resource('service_profile') - - def test_create_flavor_profile_with_mandatory_params(self): - """Create test flavor profile test.""" - resource = 'service_profile' - cmd = flavor_profile.CreateFlavorProfile( - test_cli20.MyApp(sys.stdout), None) - name = '' - description = 'Test flavor profile' - myid = 'myid' - metainfo = "{'a':'b'}" - # Defaults are returned in body - position_names = ['description', 'metainfo'] - position_values = [description, metainfo] - args = ['--description', description, '--metainfo', metainfo] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_flavor_profile_with_optional_params(self): - """Create test flavor profile disabled test.""" - resource = 'service_profile' - cmd = flavor_profile.CreateFlavorProfile( - test_cli20.MyApp(sys.stdout), None) - name = '' - description = 'Test flavor profile - disabled' - myid = 'myid' - driver = 'mydriver' - metainfo = "{'a':'b'}" - position_names = ['description', 'driver', 'metainfo', 'enabled'] - position_values = [description, driver, metainfo, 'False'] - args = ['--description', description, '--driver', driver, - '--metainfo', metainfo, '--enabled=False'] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_list_flavor_profiles(self): - """List flavor profiles test.""" - resources = 'service_profiles' - cmd = flavor_profile.ListFlavorProfile( - test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_flavor_profiles_with_pagination(self): - """List flavor profiles test with pagination.""" - resources = 'service_profiles' - cmd = flavor_profile.ListFlavorProfile( - test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_flavor_profiles_with_sort(self): - """List flavor profiles test with sort by description.""" - resources = 'service_profiles' - cmd = flavor_profile.ListFlavorProfile( - test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["description"], - sort_dir=["asc"]) - - def test_show_flavor_profile(self): - """Show flavor profile test.""" - resource = 'service_profile' - cmd = flavor_profile.ShowFlavorProfile( - test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - - def test_update_flavor_profile(self): - """Update flavor profile test.""" - resource = 'service_profile' - cmd = flavor_profile.UpdateFlavorProfile( - test_cli20.MyApp(sys.stdout), None) - newdescription = 'Test new description' - newdriver = 'NewDriver' - newmetainfo = "{'c':'d'}" - newenabled = "False" - args = ['--description', newdescription, - '--driver', newdriver, - '--metainfo', newmetainfo, - '--enabled', newenabled, - self.test_id] - self._test_update_resource(resource, cmd, self.test_id, args, - {'description': newdescription, - 'driver': newdriver, - 'metainfo': newmetainfo, - 'enabled': newenabled}) - - def test_delete_flavor_profile(self): - """Delete flavor profile.""" - resource = 'service_profile' - cmd = flavor_profile.DeleteFlavorProfile(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args) diff --git a/neutronclient/tests/unit/fw/__init__.py b/neutronclient/tests/unit/fw/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutronclient/tests/unit/fw/test_cli20_firewall.py b/neutronclient/tests/unit/fw/test_cli20_firewall.py deleted file mode 100644 index f9a2deebf..000000000 --- a/neutronclient/tests/unit/fw/test_cli20_firewall.py +++ /dev/null @@ -1,170 +0,0 @@ -# Copyright 2013 Big Switch Networks 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 sys - -from neutronclient.neutron.v2_0.fw import firewall -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20FirewallJSON(test_cli20.CLITestV20Base): - - def test_create_firewall_with_mandatory_params(self): - # firewall-create with mandatory (none) params. - resource = 'firewall' - cmd = firewall.CreateFirewall(test_cli20.MyApp(sys.stdout), None) - name = '' - tenant_id = 'my-tenant' - my_id = 'my-id' - policy_id = 'my-policy-id' - args = ['--tenant-id', tenant_id, policy_id, ] - position_names = ['firewall_policy_id', ] - position_values = [policy_id, ] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - admin_state_up=True, tenant_id=tenant_id) - - def test_create_firewall_with_all_params(self): - # firewall-create with all params set. - resource = 'firewall' - cmd = firewall.CreateFirewall(test_cli20.MyApp(sys.stdout), None) - name = 'my-name' - description = 'my-desc' - policy_id = 'my-policy-id' - tenant_id = 'my-tenant' - my_id = 'my-id' - args = ['--description', description, - '--admin-state-down', - '--tenant-id', tenant_id, - policy_id] - position_names = ['firewall_policy_id', ] - position_values = [policy_id, ] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - description=description, - admin_state_up=False, - tenant_id=tenant_id) - - def test_create_firewall_with_routers(self): - resource = 'firewall' - cmd = firewall.CreateFirewall(test_cli20.MyApp(sys.stdout), None) - name = 'my-name' - policy_id = 'my-policy-id' - my_id = 'my-id' - args = ['--router', 'fake-id', '--router', 'fake-name', policy_id] - router_ids = ['fake-id', 'fake-name'] - position_names = ['firewall_policy_id', 'router_ids'] - position_values = [policy_id, router_ids] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values) - - def test_list_firewalls(self): - # firewall-list. - resources = "firewalls" - cmd = firewall.ListFirewall(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_firewalls_pagination(self): - # firewall-list with pagination. - resources = "firewalls" - cmd = firewall.ListFirewall(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_firewalls_sort(self): - # sorted list: firewall-list --sort-key name --sort-key id - # --sort-key asc --sort-key desc - resources = "firewalls" - cmd = firewall.ListFirewall(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_firewalls_limit(self): - # size (1000) limited list: firewall-list -P. - resources = "firewalls" - cmd = firewall.ListFirewall(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_firewall_id(self): - # firewall-show test_id. - resource = 'firewall' - cmd = firewall.ShowFirewall(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - - def test_show_firewall_id_name(self): - # firewall-show. - resource = 'firewall' - cmd = firewall.ShowFirewall(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name']) - - def test_update_firewall(self): - # firewall-update myid --name newname --tags a b. - resource = 'firewall' - cmd = firewall.UpdateFirewall(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'newname'], - {'name': 'newname', }) - - def test_update_firewall_using_policy_name(self): - # firewall-update myid --policy newpolicy. - resource = 'firewall' - cmd = firewall.UpdateFirewall(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--policy', 'newpolicy'], - {'firewall_policy_id': 'newpolicy'}) - - def test_update_firewall_with_routers(self): - resource = 'firewall' - cmd = firewall.UpdateFirewall(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource( - resource, cmd, 'myid', - ['myid', '--router', 'fake-id', '--router', 'fake-name'], - {'router_ids': ['fake-id', 'fake-name']}) - - def test_update_firewall_with_no_routers(self): - resource = 'firewall' - cmd = firewall.UpdateFirewall(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource( - resource, cmd, 'myid', - ['myid', '--no-routers'], {'router_ids': []}) - - def test_update_firewall_with_bad_router_options(self): - resource = 'firewall' - cmd = firewall.UpdateFirewall(test_cli20.MyApp(sys.stdout), None) - self.assertRaises( - SystemExit, - self._test_update_resource, - resource, cmd, 'myid', - ['myid', '--no-routers', '--router', 'fake-id'], {}) - - def test_delete_firewall(self): - # firewall-delete my-id. - resource = 'firewall' - cmd = firewall.DeleteFirewall(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args) - - def test_update_firewall_admin_state(self): - # firewall-update myid --admin-state-up True. - resource = 'firewall' - cmd = firewall.UpdateFirewall(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--admin-state-up', 'True'], - {'admin_state_up': 'True'}) diff --git a/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py b/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py deleted file mode 100644 index 4cadfb2de..000000000 --- a/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py +++ /dev/null @@ -1,228 +0,0 @@ -# Copyright 2013 Big Switch Networks 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 sys -from unittest import mock - - -from neutronclient.neutron.v2_0.fw import firewallpolicy -from neutronclient import shell -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20FirewallPolicyJSON(test_cli20.CLITestV20Base): - def setUp(self): - super(CLITestV20FirewallPolicyJSON, self).setUp() - - def test_create_firewall_policy_with_mandatory_params(self): - # firewall-policy-create with mandatory (none) params only. - resource = 'firewall_policy' - cmd = firewallpolicy.CreateFirewallPolicy(test_cli20.MyApp(sys.stdout), - None) - tenant_id = 'my-tenant' - name = 'my-name' - my_id = 'myid' - args = ['--tenant-id', tenant_id, - '--admin-state_up', - name, ] - position_names = ['name', ] - position_values = [name, ] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - admin_state_up=True, tenant_id=tenant_id) - - def test_create_firewall_policy_with_all_params(self): - # firewall-policy-create with rule param of misc format. - resource = 'firewall_policy' - cmd = firewallpolicy.CreateFirewallPolicy(test_cli20.MyApp(sys.stdout), - None) - name = 'my-name' - description = 'my-desc' - firewall_rules_res = ['rule_id1', 'rule_id2'] - tenant_id = 'my-tenant' - my_id = 'myid' - position_names = ['name', ] - position_values = [name, ] - - # check for both str and unicode format firewall_rules_arg - for firewall_rules_arg in ['rule_id1 rule_id2', u'rule_id1 rule_id2']: - args = ['--description', description, - '--shared', - '--firewall-rules', firewall_rules_arg, - '--audited', - '--tenant-id', tenant_id, - '--admin-state_up', - name] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - description=description, shared=True, - firewall_rules=firewall_rules_res, - audited=True, admin_state_up=True, - tenant_id=tenant_id) - - def test_list_firewall_policies(self): - # firewall-policy-list. - resources = "firewall_policies" - cmd = firewallpolicy.ListFirewallPolicy(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, True) - - def test_list_firewall_policies_pagination(self): - # firewall-policy-list.""" - resources = "firewall_policies" - cmd = firewallpolicy.ListFirewallPolicy(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_firewall_policies_sort(self): - # sorted list: firewall-policy-list --sort-key name --sort-key id - # --sort-key asc --sort-key desc - resources = "firewall_policies" - cmd = firewallpolicy.ListFirewallPolicy(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_firewall_policies_limit(self): - # size (1000) limited list: firewall-policy-list -P. - resources = "firewall_policies" - cmd = firewallpolicy.ListFirewallPolicy(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_firewall_policy_id(self): - # firewall-policy-show test_id. - resource = 'firewall_policy' - cmd = firewallpolicy.ShowFirewallPolicy(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - - def test_show_firewall_policy_id_name(self): - # firewall-policy-show. - resource = 'firewall_policy' - cmd = firewallpolicy.ShowFirewallPolicy(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name']) - - def test_update_firewall_policy(self): - # firewall-policy-update myid --name newname. - resource = 'firewall_policy' - cmd = firewallpolicy.UpdateFirewallPolicy(test_cli20.MyApp(sys.stdout), - None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'newname'], - {'name': 'newname', }) - - def test_update_firewall_policy_with_rules(self): - # firewall-policy-update myid --firewall-rules "rule1 rule2". - resource = 'firewall_policy' - cmd = firewallpolicy.UpdateFirewallPolicy(test_cli20.MyApp(sys.stdout), - None) - firewall_rules_arg = u'rule_id3 rule_id4' - firewall_rules_res = ['rule_id3', 'rule_id4'] - self._test_update_resource( - resource, cmd, 'myid', - ['myid', '--firewall-rules', firewall_rules_arg], - {'firewall_rules': firewall_rules_res, }) - - def test_delete_firewall_policy(self): - # firewall-policy-delete my-id. - resource = 'firewall_policy' - cmd = firewallpolicy.DeleteFirewallPolicy(test_cli20.MyApp(sys.stdout), - None) - my_id = 'myid1' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args) - - def test_insert_firewall_rule(self): - # firewall-policy-insert-rule myid newruleid --insert-before ruleAid - # --insert-after ruleBid - resource = 'firewall_policy' - cmd = firewallpolicy.FirewallPolicyInsertRule( - test_cli20.MyApp(sys.stdout), - None) - myid = 'myid' - args = ['myid', 'newrule', - '--insert-before', 'rule2', - '--insert-after', 'rule1'] - extrafields = {'firewall_rule_id': 'newrule', - 'insert_before': 'rule2', - 'insert_after': 'rule1'} - - body = extrafields - path = getattr(self.client, resource + "_insert_path") - cmd_parser = cmd.get_parser(resource + "_insert_rule") - resp = (test_cli20.MyResp(204), None) - - with mock.patch.object(cmd, "get_client", - return_value=self.client) as mock_get_client, \ - mock.patch.object(self.client.httpclient, "request", - return_value=resp) as mock_request: - shell.run_command(cmd, cmd_parser, args) - self.assert_mock_multiple_calls_with_same_arguments( - mock_get_client, mock.call(), 4) - mock_request.assert_called_once_with( - test_cli20.MyUrlComparator( - test_cli20.end_url(path % myid), - self.client), - 'PUT', body=test_cli20.MyComparator(body, self.client), - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN})) - - def test_remove_firewall_rule(self): - # firewall-policy-remove-rule myid ruleid - resource = 'firewall_policy' - cmd = firewallpolicy.FirewallPolicyRemoveRule( - test_cli20.MyApp(sys.stdout), - None) - myid = 'myid' - args = ['myid', 'removerule'] - extrafields = {'firewall_rule_id': 'removerule', } - - body = extrafields - path = getattr(self.client, resource + "_remove_path") - cmd_parser = cmd.get_parser(resource + "_remove_rule") - resp = (test_cli20.MyResp(204), None) - - with mock.patch.object(cmd, "get_client", - return_value=self.client) as mock_get_client, \ - mock.patch.object(self.client.httpclient, "request", - return_value=resp) as mock_request: - shell.run_command(cmd, cmd_parser, args) - self.assert_mock_multiple_calls_with_same_arguments( - mock_get_client, mock.call(), 2) - mock_request.assert_called_once_with( - test_cli20.MyUrlComparator( - test_cli20.end_url(path % myid), - self.client), - 'PUT', body=test_cli20.MyComparator(body, self.client), - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN})) - - def test_update_firewall_policy_name_shared_audited(self): - # firewall-policy-update myid --name newname2 --shared --audited - resource = 'firewall_policy' - cmd = firewallpolicy.UpdateFirewallPolicy(test_cli20.MyApp(sys.stdout), - None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'newname2', - '--shared', 'True', '--audited', 'True'], - {'name': 'newname2', - 'shared': 'True', 'audited': 'True'}) diff --git a/neutronclient/tests/unit/fw/test_cli20_firewallrule.py b/neutronclient/tests/unit/fw/test_cli20_firewallrule.py deleted file mode 100644 index 50fabca8e..000000000 --- a/neutronclient/tests/unit/fw/test_cli20_firewallrule.py +++ /dev/null @@ -1,251 +0,0 @@ -# Copyright 2013 Big Switch Networks 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 sys - -from neutronclient.neutron.v2_0.fw import firewallrule -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20FirewallRuleJSON(test_cli20.CLITestV20Base): - - def _test_create_firewall_rule_with_mandatory_params(self, enabled): - # firewall-rule-create with mandatory (none) params only. - resource = 'firewall_rule' - cmd = firewallrule.CreateFirewallRule(test_cli20.MyApp(sys.stdout), - None) - tenant_id = 'my-tenant' - name = '' - my_id = 'myid' - protocol = 'tcp' - action = 'allow' - ip_version = 4 - args = ['--tenant-id', tenant_id, - '--admin-state-up', - '--protocol', protocol, - '--action', action, - '--enabled', enabled] - position_names = [] - position_values = [] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - protocol=protocol, action=action, - enabled=enabled, tenant_id=tenant_id, - ip_version=ip_version) - - def test_create_enabled_firewall_rule_with_mandatory_params_lcase(self): - self._test_create_firewall_rule_with_mandatory_params(enabled='true') - - def test_create_disabled_firewall_rule_with_mandatory_params_lcase(self): - self._test_create_firewall_rule_with_mandatory_params(enabled='false') - - def test_create_enabled_firewall_rule_with_mandatory_params(self): - self._test_create_firewall_rule_with_mandatory_params(enabled='True') - - def test_create_disabled_firewall_rule_with_mandatory_params(self): - self._test_create_firewall_rule_with_mandatory_params(enabled='False') - - def _setup_create_firewall_rule_with_all_params( - self, protocol='tcp', protocol_cli=None, - action='allow', action_cli=None, ip_version='4'): - # firewall-rule-create with all params set. - resource = 'firewall_rule' - cmd = firewallrule.CreateFirewallRule(test_cli20.MyApp(sys.stdout), - None) - name = 'my-name' - description = 'my-desc' - source_ip = '192.168.1.0/24' - destination_ip = '192.168.2.0/24' - source_port = '0:65535' - destination_port = '0:65535' - tenant_id = 'my-tenant' - my_id = 'myid' - enabled = 'True' - args = ['--description', description, - '--shared', - '--protocol', protocol_cli or protocol, - '--ip-version', ip_version, - '--source-ip-address', source_ip, - '--destination-ip-address', destination_ip, - '--source-port', source_port, - '--destination-port', destination_port, - '--action', action_cli or action, - '--enabled', enabled, - '--admin-state-up', - '--tenant-id', tenant_id] - position_names = [] - position_values = [] - if protocol == 'any': - protocol = None - if ip_version == '4' or ip_version == '6': - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - description=description, shared=True, - protocol=protocol, - ip_version=int(ip_version), - source_ip_address=source_ip, - destination_ip_address=destination_ip, - source_port=source_port, - destination_port=destination_port, - action=action, enabled='True', - tenant_id=tenant_id) - else: - self.assertRaises(SystemExit, self._test_create_resource, - resource, cmd, name, my_id, args, - position_names, position_values, - ip_version=int(ip_version), - source_ip_address=source_ip, - destination_ip_address=destination_ip, - source_port=source_port, - destination_port=destination_port, - action=action, enabled='True', - tenant_id=tenant_id) - - def test_create_firewall_rule_with_all_params(self): - self._setup_create_firewall_rule_with_all_params() - - def test_create_firewall_rule_with_proto_any(self): - self._setup_create_firewall_rule_with_all_params(protocol='any') - - def test_create_firewall_rule_with_IP_version_6(self): - self._setup_create_firewall_rule_with_all_params(ip_version='6') - - def test_create_firewall_rule_with_invalid_IP_version(self): - self._setup_create_firewall_rule_with_all_params(ip_version='5') - - def test_create_firewall_rule_with_proto_action_upper_capitalized(self): - for protocol in ('TCP', 'Tcp', 'ANY', 'AnY'): - self._setup_create_firewall_rule_with_all_params( - protocol=protocol.lower(), - protocol_cli=protocol) - for action in ('Allow', 'DENY', 'reject'): - self._setup_create_firewall_rule_with_all_params( - action=action.lower(), - action_cli=action) - - def test_list_firewall_rules(self): - # firewall-rule-list. - resources = "firewall_rules" - cmd = firewallrule.ListFirewallRule(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, True) - - def test_list_firewall_rules_pagination(self): - # firewall-rule-list. - resources = "firewall_rules" - cmd = firewallrule.ListFirewallRule(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_firewall_rules_sort(self): - # firewall-rule-list --sort-key name --sort-key id --sort-key asc - # --sort-key desc - resources = "firewall_rules" - cmd = firewallrule.ListFirewallRule(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_firewall_rules_limit(self): - # firewall-rule-list -P.""" - resources = "firewall_rules" - cmd = firewallrule.ListFirewallRule(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_firewall_rule_id(self): - # firewall-rule-show test_id. - resource = 'firewall_rule' - cmd = firewallrule.ShowFirewallRule(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - - def test_show_firewall_rule_id_name(self): - # firewall-rule-show. - resource = 'firewall_rule' - cmd = firewallrule.ShowFirewallRule(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name']) - - def test_update_firewall_rule(self): - # firewall-rule-update myid --name newname. - resource = 'firewall_rule' - cmd = firewallrule.UpdateFirewallRule(test_cli20.MyApp(sys.stdout), - None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'newname'], - {'name': 'newname', }) - - # firewall-rule-update myid --protocol any. - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--protocol', 'any'], - {'protocol': None, }) - - # firewall-rule-update myid --description any - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--description', 'any'], - {'description': 'any', }) - - # firewall-rule-update myid --source_ip_address 192.192.192.192 - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--source_ip_address', - '192.192.192.192'], - {'source_ip_address': '192.192.192.192', }) - - # firewall-rule-update myid --source_port 32767 - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--source_port', '32767'], - {'source_port': '32767', }) - - # firewall-rule-update myid --destination_ip_address 0.1.0.1 - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--destination_ip_address', - '0.1.0.1'], - {'destination_ip_address': '0.1.0.1', }) - - # firewall-rule-update myid --destination_port 65432 - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--destination_port', - '65432'], - {'destination_port': '65432', }) - - # firewall-rule-update myid --enabled False - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--enabled', 'False'], - {'enabled': 'False', }) - - # firewall-rule-update myid --action reject - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--action', 'reject'], - {'action': 'reject', }) - - # firewall-rule-update myid --shared false - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--shared', 'false'], - {'shared': 'false', }) - - def test_delete_firewall_rule(self): - # firewall-rule-delete my-id. - resource = 'firewall_rule' - cmd = firewallrule.DeleteFirewallRule(test_cli20.MyApp(sys.stdout), - None) - my_id = 'myid1' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args) diff --git a/neutronclient/tests/unit/lb/__init__.py b/neutronclient/tests/unit/lb/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py b/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py deleted file mode 100644 index 61cb1b1a5..000000000 --- a/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py +++ /dev/null @@ -1,202 +0,0 @@ -# Copyright 2013 Mirantis 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 sys -from unittest import mock - - -from neutronclient.neutron.v2_0.lb import healthmonitor -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20LbHealthmonitorJSON(test_cli20.CLITestV20Base): - def test_create_healthmonitor_with_mandatory_params(self): - # lb-healthmonitor-create with mandatory params only. - resource = 'health_monitor' - cmd = healthmonitor.CreateHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - admin_state_up = False - delay = '60' - max_retries = '2' - timeout = '10' - type = 'TCP' - tenant_id = 'my-tenant' - my_id = 'my-id' - args = ['--admin-state-down', - '--delay', delay, - '--max-retries', max_retries, - '--timeout', timeout, - '--type', type, - '--tenant-id', tenant_id] - position_names = ['admin_state_up', 'delay', 'max_retries', 'timeout', - 'type', 'tenant_id'] - position_values = [admin_state_up, delay, max_retries, timeout, type, - tenant_id] - self._test_create_resource(resource, cmd, '', my_id, args, - position_names, position_values) - - def test_create_healthmonitor_with_all_params(self): - # lb-healthmonitor-create with all params set. - resource = 'health_monitor' - cmd = healthmonitor.CreateHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - admin_state_up = False - delay = '60' - expected_codes = '200-202,204' - http_method = 'HEAD' - max_retries = '2' - timeout = '10' - type = 'TCP' - tenant_id = 'my-tenant' - url_path = '/health' - my_id = 'my-id' - args = ['--admin-state-down', - '--delay', delay, - '--expected-codes', expected_codes, - '--http-method', http_method, - '--max-retries', max_retries, - '--timeout', timeout, - '--type', type, - '--tenant-id', tenant_id, - '--url-path', url_path] - position_names = ['admin_state_up', 'delay', - 'expected_codes', 'http_method', - 'max_retries', 'timeout', - 'type', 'tenant_id', 'url_path'] - position_values = [admin_state_up, delay, - expected_codes, http_method, - max_retries, timeout, - type, tenant_id, url_path] - self._test_create_resource(resource, cmd, '', my_id, args, - position_names, position_values) - - def test_list_healthmonitors(self): - # lb-healthmonitor-list. - resources = "health_monitors" - cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, True) - - def test_list_healthmonitors_pagination(self): - # lb-healthmonitor-list. - resources = "health_monitors" - cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_healthmonitors_sort(self): - # lb-healthmonitor-list --sort-key name --sort-key id --sort-key asc - # --sort-key desc - resources = "health_monitors" - cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_healthmonitors_limit(self): - # lb-healthmonitor-list -P. - resources = "health_monitors" - cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_healthmonitor_id(self): - # lb-healthmonitor-show test_id. - resource = 'health_monitor' - cmd = healthmonitor.ShowHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - - def test_update_health_monitor(self): - # lb-healthmonitor-update myid --name myname --tags a b. - resource = 'health_monitor' - cmd = healthmonitor.UpdateHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--timeout', '5'], - {'timeout': '5', }) - - def test_delete_healthmonitor(self): - # lb-healthmonitor-delete my-id.""" - resource = 'health_monitor' - cmd = healthmonitor.DeleteHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args) - - def test_associate_healthmonitor(self): - cmd = healthmonitor.AssociateHealthMonitor( - test_cli20.MyApp(sys.stdout), - None) - resource = 'health_monitor' - health_monitor_id = 'hm-id' - pool_id = 'p_id' - args = [health_monitor_id, pool_id] - - body = {resource: {'id': health_monitor_id}} - result = {resource: {'id': health_monitor_id}, } - result_str = self.client.serialize(result) - - path = getattr(self.client, - "associate_pool_health_monitors_path") % pool_id - return_tup = (test_cli20.MyResp(200), result_str) - cmd_parser = cmd.get_parser('test_' + resource) - parsed_args = cmd_parser.parse_args(args) - - with mock.patch.object(cmd, "get_client", - return_value=self.client) as mock_get_client, \ - mock.patch.object(self.client.httpclient, "request", - return_value=return_tup) as mock_request: - cmd.run(parsed_args) - - mock_get_client.assert_called_once_with() - mock_request.assert_called_once_with( - test_cli20.end_url(path), 'POST', - body=test_cli20.MyComparator(body, self.client), - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN})) - - def test_disassociate_healthmonitor(self): - cmd = healthmonitor.DisassociateHealthMonitor( - test_cli20.MyApp(sys.stdout), - None) - resource = 'health_monitor' - health_monitor_id = 'hm-id' - pool_id = 'p_id' - args = [health_monitor_id, pool_id] - - path = (getattr(self.client, - "disassociate_pool_health_monitors_path") % - {'pool': pool_id, 'health_monitor': health_monitor_id}) - return_tup = (test_cli20.MyResp(204), None) - cmd_parser = cmd.get_parser('test_' + resource) - parsed_args = cmd_parser.parse_args(args) - - with mock.patch.object(cmd, "get_client", - return_value=self.client) as mock_get_client, \ - mock.patch.object(self.client.httpclient, "request", - return_value=return_tup) as mock_request: - cmd.run(parsed_args) - - mock_get_client.assert_called_once_with() - mock_request.assert_called_once_with( - test_cli20.end_url(path), 'DELETE', - body=None, - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN})) diff --git a/neutronclient/tests/unit/lb/test_cli20_member.py b/neutronclient/tests/unit/lb/test_cli20_member.py deleted file mode 100644 index 8ce2a0f09..000000000 --- a/neutronclient/tests/unit/lb/test_cli20_member.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright 2013 Mirantis 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 sys - -from neutronclient.neutron.v2_0.lb import member -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20LbMemberJSON(test_cli20.CLITestV20Base): - def setUp(self): - super(CLITestV20LbMemberJSON, self).setUp(plurals={'tags': 'tag'}) - - def test_create_member(self): - # lb-member-create with mandatory params only. - resource = 'member' - cmd = member.CreateMember(test_cli20.MyApp(sys.stdout), None) - address = '10.0.0.1' - port = '8080' - tenant_id = 'my-tenant' - my_id = 'my-id' - pool_id = 'pool-id' - args = ['--address', address, '--protocol-port', port, - '--tenant-id', tenant_id, pool_id] - position_names = ['address', 'protocol_port', 'tenant_id', 'pool_id', - 'admin_state_up'] - position_values = [address, port, tenant_id, pool_id, True] - self._test_create_resource(resource, cmd, None, my_id, args, - position_names, position_values, - admin_state_up=None) - - def test_create_member_all_params(self): - # lb-member-create with all available params. - resource = 'member' - cmd = member.CreateMember(test_cli20.MyApp(sys.stdout), None) - address = '10.0.0.1' - admin_state_up = False - port = '8080' - weight = '1' - tenant_id = 'my-tenant' - my_id = 'my-id' - pool_id = 'pool-id' - args = ['--address', address, '--admin-state-down', - '--protocol-port', port, '--weight', weight, - '--tenant-id', tenant_id, pool_id] - position_names = [ - 'address', 'admin_state_up', 'protocol_port', 'weight', - 'tenant_id', 'pool_id' - ] - position_values = [address, admin_state_up, port, weight, - tenant_id, pool_id] - self._test_create_resource(resource, cmd, None, my_id, args, - position_names, position_values, - admin_state_up=None) - - def test_list_members(self): - # lb-member-list. - resources = "members" - cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_members_pagination(self): - # lb-member-list. - resources = "members" - cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_members_sort(self): - # lb-member-list --sort-key name --sort-key id --sort-key asc - # --sort-key desc - resources = "members" - cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_members_limit(self): - # lb-member-list -P. - resources = "members" - cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_member_id(self): - # lb-member-show test_id. - resource = 'member' - cmd = member.ShowMember(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - - def test_update_member(self): - # lb-member-update myid --name myname --tags a b. - resource = 'member' - cmd = member.UpdateMember(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'myname', - '--tags', 'a', 'b'], - {'name': 'myname', 'tags': ['a', 'b'], }) - - def test_delete_member(self): - # lb-member-delete my-id. - resource = 'member' - cmd = member.DeleteMember(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args) diff --git a/neutronclient/tests/unit/lb/test_cli20_pool.py b/neutronclient/tests/unit/lb/test_cli20_pool.py deleted file mode 100644 index 79bf23df8..000000000 --- a/neutronclient/tests/unit/lb/test_cli20_pool.py +++ /dev/null @@ -1,166 +0,0 @@ -# Copyright 2013 Mirantis 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 sys -from unittest import mock - - -from neutronclient.neutron.v2_0.lb import pool -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20LbPoolJSON(test_cli20.CLITestV20Base): - - def test_create_pool_with_mandatory_params(self): - # lb-pool-create with mandatory params only. - resource = 'pool' - cmd = pool.CreatePool(test_cli20.MyApp(sys.stdout), None) - name = 'my-name' - lb_method = 'ROUND_ROBIN' - protocol = 'HTTP' - subnet_id = 'subnet-id' - tenant_id = 'my-tenant' - my_id = 'my-id' - args = ['--lb-method', lb_method, - '--name', name, - '--protocol', protocol, - '--subnet-id', subnet_id, - '--tenant-id', tenant_id] - position_names = ['admin_state_up', 'lb_method', 'name', - 'protocol', 'subnet_id', 'tenant_id'] - position_values = [True, lb_method, name, - protocol, subnet_id, tenant_id] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values) - - def test_create_pool_with_all_params(self): - # lb-pool-create with all params set. - resource = 'pool' - cmd = pool.CreatePool(test_cli20.MyApp(sys.stdout), None) - name = 'my-name' - description = 'my-desc' - lb_method = 'ROUND_ROBIN' - protocol = 'HTTP' - subnet_id = 'subnet-id' - tenant_id = 'my-tenant' - my_id = 'my-id' - provider = 'lbaas' - args = ['--admin-state-down', - '--description', description, - '--lb-method', lb_method, - '--name', name, - '--protocol', protocol, - '--subnet-id', subnet_id, - '--tenant-id', tenant_id, - '--provider', provider] - position_names = ['admin_state_up', 'description', 'lb_method', 'name', - 'protocol', 'subnet_id', 'tenant_id', 'provider'] - position_values = [False, description, lb_method, name, - protocol, subnet_id, tenant_id, provider] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values) - - def test_list_pools(self): - # lb-pool-list. - resources = "pools" - cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_pools_pagination(self): - # lb-pool-list. - resources = "pools" - cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_pools_sort(self): - # lb-pool-list --sort-key name --sort-key id --sort-key asc - # --sort-key desc - resources = "pools" - cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_pools_limit(self): - # lb-pool-list -P. - resources = "pools" - cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_pool_id(self): - # lb-pool-show test_id. - resource = 'pool' - cmd = pool.ShowPool(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - - def test_show_pool_id_name(self): - # lb-pool-show. - resource = 'pool' - cmd = pool.ShowPool(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name']) - - def test_update_pool(self): - # lb-pool-update myid --name newname --tags a b. - resource = 'pool' - cmd = pool.UpdatePool(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'newname'], - {'name': 'newname', }) - - def test_delete_pool(self): - # lb-pool-delete my-id. - resource = 'pool' - cmd = pool.DeletePool(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args) - - def test_retrieve_pool_stats(self): - # lb-pool-stats test_id. - resource = 'pool' - cmd = pool.RetrievePoolStats(test_cli20.MyApp(sys.stdout), None) - my_id = self.test_id - fields = ['bytes_in', 'bytes_out'] - args = ['--fields', 'bytes_in', '--fields', 'bytes_out', my_id] - - query = "&".join(["fields=%s" % field for field in fields]) - expected_res = {'stats': {'bytes_in': '1234', 'bytes_out': '4321'}} - resstr = self.client.serialize(expected_res) - path = getattr(self.client, "pool_path_stats") - return_tup = (test_cli20.MyResp(200), resstr) - - cmd_parser = cmd.get_parser("test_" + resource) - parsed_args = cmd_parser.parse_args(args) - - with mock.patch.object(cmd, "get_client", - return_value=self.client) as mock_get_client, \ - mock.patch.object(self.client.httpclient, "request", - return_value=return_tup) as mock_request: - cmd.run(parsed_args) - - self.assert_mock_multiple_calls_with_same_arguments( - mock_get_client, mock.call(), 2) - mock_request.assert_called_once_with( - test_cli20.end_url(path % my_id, query), 'GET', - body=None, - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN})) - _str = self.fake_stdout.make_string() - self.assertIn('bytes_in', _str) - self.assertIn('bytes_out', _str) diff --git a/neutronclient/tests/unit/lb/test_cli20_vip.py b/neutronclient/tests/unit/lb/test_cli20_vip.py deleted file mode 100644 index b08de447e..000000000 --- a/neutronclient/tests/unit/lb/test_cli20_vip.py +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright 2013 Mirantis 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 sys - -from neutronclient.neutron.v2_0.lb import vip -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20LbVipJSON(test_cli20.CLITestV20Base): - def setUp(self): - super(CLITestV20LbVipJSON, self).setUp(plurals={'tags': 'tag'}) - - def test_create_vip_with_mandatory_params(self): - # lb-vip-create with all mandatory params. - resource = 'vip' - cmd = vip.CreateVip(test_cli20.MyApp(sys.stdout), None) - pool_id = 'my-pool-id' - name = 'my-name' - subnet_id = 'subnet-id' - protocol_port = '1000' - protocol = 'TCP' - tenant_id = 'my-tenant' - my_id = 'my-id' - args = ['--name', name, - '--protocol-port', protocol_port, - '--protocol', protocol, - '--subnet-id', subnet_id, - '--tenant-id', tenant_id, - pool_id] - position_names = ['pool_id', 'name', 'protocol_port', 'protocol', - 'subnet_id', 'tenant_id'] - position_values = [pool_id, name, protocol_port, protocol, - subnet_id, tenant_id] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - admin_state_up=True) - - def test_create_vip_with_all_params(self): - # lb-vip-create with all params. - resource = 'vip' - cmd = vip.CreateVip(test_cli20.MyApp(sys.stdout), None) - pool_id = 'my-pool-id' - name = 'my-name' - description = 'my-desc' - address = '10.0.0.2' - admin_state = False - connection_limit = '1000' - subnet_id = 'subnet-id' - protocol_port = '80' - protocol = 'TCP' - tenant_id = 'my-tenant' - my_id = 'my-id' - args = ['--name', name, - '--description', description, - '--address', address, - '--admin-state-down', - '--connection-limit', connection_limit, - '--protocol-port', protocol_port, - '--protocol', protocol, - '--subnet-id', subnet_id, - '--tenant-id', tenant_id, - pool_id] - position_names = ['pool_id', 'name', 'description', 'address', - 'admin_state_up', 'connection_limit', - 'protocol_port', 'protocol', 'subnet_id', - 'tenant_id'] - position_values = [pool_id, name, description, address, - admin_state, connection_limit, protocol_port, - protocol, subnet_id, - tenant_id] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values) - - def test_create_vip_with_session_persistence_params(self): - # lb-vip-create with mandatory and session-persistence params. - resource = 'vip' - cmd = vip.CreateVip(test_cli20.MyApp(sys.stdout), None) - pool_id = 'my-pool-id' - name = 'my-name' - subnet_id = 'subnet-id' - protocol_port = '1000' - protocol = 'TCP' - tenant_id = 'my-tenant' - my_id = 'my-id' - args = ['--name', name, - '--protocol-port', protocol_port, - '--protocol', protocol, - '--subnet-id', subnet_id, - '--tenant-id', tenant_id, - pool_id, - '--session-persistence', 'type=dict', - 'type=cookie,cookie_name=pie', - '--optional-param', 'any'] - position_names = ['pool_id', 'name', 'protocol_port', 'protocol', - 'subnet_id', 'tenant_id', 'optional_param'] - position_values = [pool_id, name, protocol_port, protocol, - subnet_id, tenant_id, 'any'] - extra_body = { - 'session_persistence': { - 'type': 'cookie', - 'cookie_name': 'pie', - }, - } - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - admin_state_up=True, extra_body=extra_body) - - def test_list_vips(self): - # lb-vip-list. - resources = "vips" - cmd = vip.ListVip(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_vips_pagination(self): - # lb-vip-list. - resources = "vips" - cmd = vip.ListVip(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_vips_sort(self): - # lb-vip-list --sort-key name --sort-key id --sort-key asc - # --sort-key desc - resources = "vips" - cmd = vip.ListVip(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_vips_limit(self): - # lb-vip-list -P. - resources = "vips" - cmd = vip.ListVip(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_vip_id(self): - # lb-vip-show test_id. - resource = 'vip' - cmd = vip.ShowVip(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - - def test_show_vip_id_name(self): - # lb-vip-show. - resource = 'vip' - cmd = vip.ShowVip(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name']) - - def test_update_vip(self): - # lb-vip-update myid --name myname --tags a b. - resource = 'vip' - cmd = vip.UpdateVip(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'myname', - '--tags', 'a', 'b'], - {'name': 'myname', 'tags': ['a', 'b'], }) - - def test_update_vip_with_session_persistence(self): - resource = 'vip' - cmd = vip.UpdateVip(test_cli20.MyApp(sys.stdout), None) - body = { - 'session_persistence': { - 'type': 'source', - }, - } - args = ['myid', '--session-persistence', 'type=dict', - 'type=source'] - self._test_update_resource(resource, cmd, 'myid', args, body) - - def test_update_vip_with_session_persistence_and_name(self): - resource = 'vip' - cmd = vip.UpdateVip(test_cli20.MyApp(sys.stdout), None) - body = { - 'name': 'newname', - 'session_persistence': { - 'type': 'cookie', - 'cookie_name': 'pie', - }, - } - args = ['myid', '--name', 'newname', - '--session-persistence', 'type=dict', - 'type=cookie,cookie_name=pie'] - self._test_update_resource(resource, cmd, 'myid', args, body) - - def test_delete_vip(self): - # lb-vip-delete my-id. - resource = 'vip' - cmd = vip.DeleteVip(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args) diff --git a/neutronclient/tests/unit/lb/v2/__init__.py b/neutronclient/tests/unit/lb/v2/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py b/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py deleted file mode 100644 index 945ac18e3..000000000 --- a/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py +++ /dev/null @@ -1,175 +0,0 @@ -# Copyright 2014 Blue Box Group, 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 sys - -from neutronclient.neutron.v2_0.lb.v2 import healthmonitor -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20LbHealthMonitorJSON(test_cli20.CLITestV20Base): - - def test_create_healthmonitor_with_mandatory_params(self): - # lbaas-healthmonitor-create with mandatory params only. - resource = 'healthmonitor' - cmd_resource = 'lbaas_healthmonitor' - cmd = healthmonitor.CreateHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - type = 'PING' - max_retries = '3' - delay = '10' - timeout = '60' - pool = 'pool1' - args = ['--type', type, '--max-retries', max_retries, - '--delay', delay, '--timeout', timeout, '--pool', pool] - position_names = ['type', 'max_retries', 'delay', 'timeout', 'pool_id'] - position_values = [type, max_retries, delay, timeout, pool] - self._test_create_resource(resource, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=cmd_resource) - - def test_create_healthmonitor_with_all_params(self): - # lbaas-healthmonitor-create with all params set. - resource = 'healthmonitor' - cmd_resource = 'lbaas_healthmonitor' - cmd = healthmonitor.CreateHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - type = 'PING' - max_retries = '3' - delay = '10' - timeout = '60' - http_method = 'GET' - expected_codes = '201' - url_path = '/somepath' - pool = 'pool1' - name = 'healthmonitor1' - args = ['--admin-state-down', '--http-method', http_method, - '--expected-codes', expected_codes, '--url-path', url_path, - '--type', type, '--max-retries', max_retries, - '--delay', delay, '--timeout', timeout, '--pool', pool, - '--name', name] - position_names = ['admin_state_up', 'http_method', 'expected_codes', - 'url_path', 'type', 'max_retries', 'delay', - 'timeout', 'pool_id', 'name'] - position_values = [False, http_method, expected_codes, url_path, - type, max_retries, delay, timeout, pool, name] - self._test_create_resource(resource, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=cmd_resource) - - def test_list_healthmonitors(self): - # lbaas-healthmonitor-list. - resources = 'healthmonitors' - cmd_resources = 'lbaas_healthmonitors' - cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, True, - cmd_resources=cmd_resources) - - def test_list_healthmonitors_pagination(self): - # lbaas-healthmonitor-list with pagination. - resources = 'healthmonitors' - cmd_resources = 'lbaas_healthmonitors' - cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources_with_pagination(resources, cmd, - cmd_resources=cmd_resources) - - def test_list_healthmonitors_sort(self): - # lbaas-healthmonitor-list --sort-key id --sort-key asc. - resources = 'healthmonitors' - cmd_resources = 'lbaas_healthmonitors' - cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, True, - cmd_resources=cmd_resources) - - def test_list_healthmonitors_limit(self): - # lbaas-healthmonitor-list -P. - resources = 'healthmonitors' - cmd_resources = 'lbaas_healthmonitors' - cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, page_size=1000, - cmd_resources=cmd_resources) - - def test_show_healthmonitor_id(self): - # lbaas-healthmonitor-show test_id. - resource = 'healthmonitor' - cmd_resource = 'lbaas_healthmonitor' - cmd = healthmonitor.ShowHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id'], - cmd_resource=cmd_resource) - - def test_show_healthmonitor_id_name(self): - # lbaas-healthmonitor-show. - resource = 'healthmonitor' - cmd_resource = 'lbaas_healthmonitor' - cmd = healthmonitor.ShowHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name'], - cmd_resource=cmd_resource) - - def _test_update_hm(self, args, expected_values): - resource = 'healthmonitor' - cmd_resource = 'lbaas_healthmonitor' - my_id = 'myid' - cmd = healthmonitor.UpdateHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - args.insert(0, my_id) - self._test_update_resource(resource, cmd, my_id, - args, - expected_values, - cmd_resource=cmd_resource) - - def test_update_healthmonitor(self): - # lbaas-healthmonitor-update myid --name newname. - self._test_update_hm(['--name', 'newname'], {'name': 'newname', }) - # lbaas-healthmonitor-update myid --delay 10. - self._test_update_hm(['--delay', '10'], {'delay': '10'}) - # lbaas-healthmonitor-update myid --timeout 5. - self._test_update_hm(['--timeout', '5'], {'timeout': '5', }) - # lbaas-healthmonitor-update myid --delay 10. - self._test_update_hm(['--http-method', 'OPTIONS'], - {'http_method': 'OPTIONS'}) - # lbaas-healthmonitor-update myid --url-path /test/string . - self._test_update_hm(['--url-path', '/test/string'], - {'url_path': '/test/string', }) - # lbaas-healthmonitor-update myid --max-retries 5 - self._test_update_hm(['--max-retries', '5'], {'max_retries': '5'}) - # lbaas-healthmonitor-update myid --expected-codes 201 - self._test_update_hm(['--expected-codes', '201'], - {'expected_codes': '201'}) - # lbaas-healthmonitor-update myid --admin-state-up False - self._test_update_hm(['--admin-state-up', 'False'], - {'admin_state_up': 'False'}) - - def test_delete_healthmonitor(self): - # lbaas-healthmonitor-delete my-id. - resource = 'healthmonitor' - cmd_resource = 'lbaas_healthmonitor' - cmd = healthmonitor.DeleteHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args, - cmd_resource=cmd_resource) diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_l7policy.py b/neutronclient/tests/unit/lb/v2/test_cli20_l7policy.py deleted file mode 100644 index 71869315b..000000000 --- a/neutronclient/tests/unit/lb/v2/test_cli20_l7policy.py +++ /dev/null @@ -1,260 +0,0 @@ -# Copyright 2016 Radware LTD. -# 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 sys - -from neutronclient.common import exceptions -from neutronclient.neutron.v2_0.lb.v2 import l7policy -from neutronclient.tests.unit import test_cli20 - -"""Structure for mapping cli and api arguments - -The structure maps cli arguments and a list of its -api argument name, default cli value and default api value. -It helps to make tests more general for different argument types. -""" -args_conf = { - 'name': ['name', 'test_policy', 'test_policy'], - 'description': ['description', 'test policy', 'test policy'], - 'listener': ['listener_id', 'test_listener', 'test_listener'], - 'admin-state-up': ['admin_state_up', True, True], - 'admin-state-down': ['admin_state_up', None, False], - 'action': ['action', 'REJECT', 'REJECT'], - 'redirect-url': ['redirect_url', 'http://url', 'http://url'], - 'redirect-pool': ['redirect_pool_id', 'test_pool', 'test_pool'], - 'position': ['position', '1', 1]} - - -class CLITestV20LbL7PolicyJSON(test_cli20.CLITestV20Base): - - def _get_test_args(self, *args, **kwargs): - """Function for generically building testing arguments""" - cli_args = [] - api_args = {} - for arg in args: - cli_args.append('--' + arg) - if not args_conf[arg][1]: - pass - elif arg in kwargs: - cli_args.append(str(kwargs[arg])) - else: - cli_args.append(args_conf[arg][1]) - - if arg in kwargs: - api_args[args_conf[arg][0]] = kwargs[arg] - else: - api_args[args_conf[arg][0]] = args_conf[arg][2] - - return cli_args, api_args - - def _test_create_policy(self, *args, **kwargs): - resource = 'l7policy' - cmd_resource = 'lbaas_l7policy' - cmd = l7policy.CreateL7Policy(test_cli20.MyApp(sys.stdout), None) - cli_args, api_args = self._get_test_args(*args, **kwargs) - position_names = list(api_args.keys()) - position_values = list(api_args.values()) - self._test_create_resource(resource, cmd, None, 'test_id', - cli_args, position_names, position_values, - cmd_resource=cmd_resource) - - def _test_update_policy(self, *args, **kwargs): - resource = 'l7policy' - cmd_resource = 'lbaas_l7policy' - cmd = l7policy.UpdateL7Policy(test_cli20.MyApp(sys.stdout), None) - cli_args, api_args = self._get_test_args(*args, **kwargs) - cli_args.append('test_id') - self._test_update_resource(resource, cmd, 'test_id', - cli_args, api_args, - cmd_resource=cmd_resource) - - def test_create_policy_with_mandatory_params(self): - # lbaas-l7policy-create with mandatory params only. - self._test_create_policy('action', 'listener') - - def test_create_policy_with_all_params(self): - # lbaas-l7policy-create REJECT policy. - self._test_create_policy('name', 'description', - 'action', 'listener', - 'position') - - def test_create_disabled_policy(self): - # lbaas-l7policy-create disabled REJECT policy. - self._test_create_policy('action', 'listener', 'admin-state-down') - - def test_create_url_redirect_policy(self): - # lbaas-l7policy-create REDIRECT_TO_URL policy. - self._test_create_policy('name', 'description', - 'action', 'listener', - 'redirect-url', - action='REDIRECT_TO_URL') - - def test_create_url_redirect_policy_no_url(self): - # lbaas-l7policy-create REDIRECT_TO_URL policy without url argument. - self.assertRaises(exceptions.CommandError, - self._test_create_policy, - 'name', 'description', - 'action', 'listener', - action='REDIRECT_TO_URL') - - def test_create_pool_redirect_policy(self): - # lbaas-l7policy-create REDIRECT_TO_POOL policy. - self._test_create_policy('name', 'description', - 'action', 'listener', - 'redirect-pool', - action='REDIRECT_TO_POOL') - - def test_create_pool_redirect_policy_no_pool(self): - # lbaas-l7policy-create REDIRECT_TO_POOL policy without pool argument. - self.assertRaises(exceptions.CommandError, - self._test_create_policy, - 'name', 'description', - 'action', 'listener', - action='REDIRECT_TO_POOL') - - def test_create_reject_policy_with_url(self): - # lbaas-l7policy-create REJECT policy while specifying url argument. - self.assertRaises(exceptions.CommandError, - self._test_create_policy, - 'action', 'listener', - 'redirect-url') - - def test_create_reject_policy_with_pool(self): - # lbaas-l7policy-create REJECT policy while specifying pool argument. - self.assertRaises(exceptions.CommandError, - self._test_create_policy, - 'action', 'listener', - 'redirect-pool') - - def test_create_pool_redirect_policy_with_url(self): - # lbaas-l7policy-create REDIRECT_TO_POOL policy with url argument. - self.assertRaises(exceptions.CommandError, - self._test_create_policy, - 'action', 'listener', - 'redirect-pool', 'redirect-url', - action='REDIRECT_TO_POOL') - - def test_create_url_redirect_policy_with_pool(self): - # lbaas-l7policy-create REDIRECT_TO_URL policy with pool argument. - self.assertRaises(exceptions.CommandError, - self._test_create_policy, - 'action', 'listener', - 'redirect-pool', 'redirect-url', - action='REDIRECT_TO_URL') - - def test_list_policies(self): - # lbaas-l7policy-list. - - resources = 'l7policies' - cmd_resources = 'lbaas_l7policies' - cmd = l7policy.ListL7Policy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True, - cmd_resources=cmd_resources) - - def test_list_policies_pagination(self): - # lbaas-l7policy-list with pagination. - - resources = 'l7policies' - cmd_resources = 'lbaas_l7policies' - cmd = l7policy.ListL7Policy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination( - resources, cmd, cmd_resources=cmd_resources) - - def test_list_policies_sort(self): - # lbaas-l7policy-list --sort-key id --sort-key asc. - - resources = 'l7policies' - cmd_resources = 'lbaas_l7policies' - cmd = l7policy.ListL7Policy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources( - resources, cmd, True, cmd_resources=cmd_resources) - - def test_list_policies_limit(self): - # lbaas-l7policy-list -P. - - resources = 'l7policies' - cmd_resources = 'lbaas_l7policies' - cmd = l7policy.ListL7Policy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources( - resources, cmd, page_size=1000, cmd_resources=cmd_resources) - - def test_show_policy_id(self): - # lbaas-l7policy-show test_id. - - resource = 'l7policy' - cmd_resource = 'lbaas_l7policy' - cmd = l7policy.ShowL7Policy(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'test_id', self.test_id] - self._test_show_resource( - resource, cmd, self.test_id, args, - ['test_id'], cmd_resource=cmd_resource) - - def test_show_policy_id_name(self): - # lbaas-l7policy-show. - - resource = 'l7policy' - cmd_resource = 'lbaas_l7policy' - cmd = l7policy.ShowL7Policy(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'test_id', '--fields', 'name', self.test_id] - self._test_show_resource( - resource, cmd, self.test_id, args, - ['test_id', 'name'], cmd_resource=cmd_resource) - - def test_disable_policy(self): - # lbaas-l7policy-update test_id --admin-state-up False. - - self._test_update_policy('admin-state-up', - **{'admin-state-up': 'False'}) - - def test_update_policy_name_and_description(self): - # lbaas-l7policy-update test_id --name other --description other_desc. - - self._test_update_policy('name', 'description', - name='name', - description='other desc') - - def test_update_pool_redirect_policy(self): - # lbaas-l7policy-update test_id --action REDIRECT_TO_POOL - # --redirect-pool id. - - self._test_update_policy('action', 'redirect-pool', - **{'action': 'REDIRECT_TO_POOL', - 'redirect-pool': 'id'}) - - def test_update_url_redirect_policy(self): - # lbaas-l7policy-update test_id --action REDIRECT_TO_URL - # --redirect-url http://other_url. - - self._test_update_policy('action', 'redirect-url', - **{'action': 'REDIRECT_TO_URL', - 'redirect-url': 'http://other_url'}) - - def test_update_policy_position(self): - # lbaas-l7policy-update test_id --position 2. - - self._test_update_policy('position', - position=2) - - def test_delete_policy(self): - # lbaas-l7policy-delete test_id. - - resource = 'l7policy' - cmd_resource = 'lbaas_l7policy' - cmd = l7policy.DeleteL7Policy(test_cli20.MyApp(sys.stdout), None) - test_id = 'test_id' - args = [test_id] - self._test_delete_resource(resource, cmd, test_id, args, - cmd_resource=cmd_resource) diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_l7rule.py b/neutronclient/tests/unit/lb/v2/test_cli20_l7rule.py deleted file mode 100644 index 54830542d..000000000 --- a/neutronclient/tests/unit/lb/v2/test_cli20_l7rule.py +++ /dev/null @@ -1,210 +0,0 @@ -# Copyright 2016 Radware LTD. -# 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 sys - -from neutronclient.neutron.v2_0.lb.v2 import l7rule -from neutronclient.tests.unit import test_cli20 - - -"""Structure for mapping cli and api arguments - -The structure maps cli arguments and a list of its -api argument name, default cli value and default api value. -It helps to make tests more general for different argument types. -""" -args_conf = { - 'admin-state-up': ['admin_state_up', True, True], - 'admin-state-down': ['admin_state_up', None, False], - 'type': ['type', 'HOST_NAME', 'HOST_NAME'], - 'compare-type': ['compare_type', 'EQUAL_TO', 'EQUAL_TO'], - 'invert-compare': ['invert', None, True], - 'key': ['key', 'key', 'key'], - 'value': ['value', 'value', 'value']} - - -class CLITestV20LbL7RuleJSON(test_cli20.CLITestV20Base): - - def _get_test_args(self, *args, **kwargs): - """Function for generically building testing arguments""" - cli_args = [] - api_args = {} - for arg in args: - cli_args.append('--' + arg) - if not args_conf[arg][1]: - pass - elif arg in kwargs: - cli_args.append(str(kwargs[arg])) - else: - cli_args.append(args_conf[arg][1]) - - if arg in kwargs: - api_args[args_conf[arg][0]] = kwargs[arg] - else: - api_args[args_conf[arg][0]] = args_conf[arg][2] - - if 'invert' not in api_args: - api_args['invert'] = False - return cli_args, api_args - - def _test_create_rule(self, *args, **kwargs): - resource = 'rule' - cmd_resource = 'lbaas_l7rule' - cmd = l7rule.CreateL7Rule(test_cli20.MyApp(sys.stdout), None) - cli_args, api_args = self._get_test_args(*args, **kwargs) - position_names = list(api_args.keys()) - position_values = list(api_args.values()) - cli_args.append('test_policy') - self._test_create_resource(resource, cmd, None, 'test_id', - cli_args, position_names, position_values, - cmd_resource=cmd_resource, - parent_id='test_policy') - - def _test_update_rule(self, *args, **kwargs): - resource = 'rule' - cmd_resource = 'lbaas_l7rule' - cmd = l7rule.UpdateL7Rule(test_cli20.MyApp(sys.stdout), None) - cli_args, api_args = self._get_test_args(*args, **kwargs) - cli_args.append('test_id') - cli_args.append('test_policy') - self._test_update_resource(resource, cmd, 'test_id', - cli_args, api_args, - cmd_resource=cmd_resource, - parent_id='test_policy') - - def test_create_rule_with_mandatory_params(self): - # lbaas-l7rule-create with mandatory params only. - - self._test_create_rule('type', 'compare-type', - 'value') - - def test_create_disabled_rule(self): - # lbaas-l7rule-create disabled rule. - - self._test_create_rule('type', 'compare-type', - 'value', 'admin-state-down') - - def test_create_rule_with_all_params(self): - # lbaas-l7rule-create with all params set. - - self._test_create_rule('type', 'compare-type', - 'invert-compare', 'key', 'value', - type='HEADER', compare_type='CONTAINS', - key='other_key', value='other_value') - - def test_create_rule_with_inverted_compare(self): - # lbaas-l7rule-create with invertted compare type. - - self._test_create_rule('type', 'compare-type', - 'invert-compare', 'value') - - def test_list_rules(self): - # lbaas-l7rule-list. - - resources = 'rules' - cmd_resources = 'lbaas_l7rules' - cmd = l7rule.ListL7Rule(test_cli20.MyApp(sys.stdout), None) - - policy_id = 'policy_id' - self._test_list_resources(resources, cmd, True, - base_args=[policy_id], - cmd_resources=cmd_resources, - parent_id=policy_id, - query="l7policy_id=%s" % policy_id) - - def test_list_rules_pagination(self): - # lbaas-l7rule-list with pagination. - - resources = 'rules' - cmd_resources = 'lbaas_l7rules' - cmd = l7rule.ListL7Rule(test_cli20.MyApp(sys.stdout), None) - policy_id = 'policy_id' - self._test_list_resources_with_pagination( - resources, cmd, base_args=[policy_id], - cmd_resources=cmd_resources, parent_id=policy_id, - query="l7policy_id=%s" % policy_id) - - def test_list_rules_sort(self): - # lbaas-l7rule-list --sort-key id --sort-key asc. - - resources = 'rules' - cmd_resources = 'lbaas_l7rules' - cmd = l7rule.ListL7Rule(test_cli20.MyApp(sys.stdout), None) - policy_id = 'policy_id' - self._test_list_resources( - resources, cmd, True, base_args=[policy_id], - cmd_resources=cmd_resources, parent_id=policy_id, - query="l7policy_id=%s" % policy_id) - - def test_list_rules_limit(self): - # lbaas-l7rule-list -P. - - resources = 'rules' - cmd_resources = 'lbaas_l7rules' - cmd = l7rule.ListL7Rule(test_cli20.MyApp(sys.stdout), None) - policy_id = 'policy_id' - self._test_list_resources(resources, cmd, page_size=1000, - base_args=[policy_id], - cmd_resources=cmd_resources, - parent_id=policy_id, - query="l7policy_id=%s" % policy_id) - - def test_show_rule_id(self): - # lbaas-l7rule-show test_id. - - resource = 'rule' - cmd_resource = 'lbaas_l7rule' - policy_id = 'policy_id' - cmd = l7rule.ShowL7Rule(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id, policy_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id'], - cmd_resource=cmd_resource, - parent_id=policy_id) - - def test_update_rule_type(self): - # lbaas-l7rule-update test_id --type HEADER test_policy - - self._test_update_rule('type', type='HEADER') - - def test_update_rule_compare_type(self): - # lbaas-l7rule-update test_id --compare-type CONTAINS test_policy. - - self._test_update_rule('compare-type', - **{'compare-type': 'CONTAINS'}) - - def test_update_rule_inverted_compare_type(self): - # lbaas-l7rule-update test_id --invert-compare test_policy. - - self._test_update_rule('invert-compare') - - def test_update_rule_key_value(self): - # lbaas-l7rule-update test_id --key other --value other test_policy. - - self._test_update_rule('key', 'value', - key='other', value='other') - - def test_delete_rule(self): - # lbaas-l7rule-delete test_id policy_id. - - resource = 'rule' - cmd_resource = 'lbaas_l7rule' - policy_id = 'policy_id' - test_id = 'test_id' - cmd = l7rule.DeleteL7Rule(test_cli20.MyApp(sys.stdout), None) - args = [test_id, policy_id] - self._test_delete_resource(resource, cmd, test_id, args, - cmd_resource=cmd_resource, - parent_id=policy_id) diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_listener.py b/neutronclient/tests/unit/lb/v2/test_cli20_listener.py deleted file mode 100644 index 3993ea7f7..000000000 --- a/neutronclient/tests/unit/lb/v2/test_cli20_listener.py +++ /dev/null @@ -1,194 +0,0 @@ -# Copyright 2014 Blue Box Group, 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 sys - -from neutronclient.common import exceptions -from neutronclient.neutron.v2_0.lb.v2 import listener -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20LbListenerJSON(test_cli20.CLITestV20Base): - - def test_create_listener_with_loadbalancer(self): - # lbaas-listener-create with --loadbalancer - resource = 'listener' - cmd_resource = 'lbaas_listener' - cmd = listener.CreateListener(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - loadbalancer_id = 'loadbalancer' - protocol = 'TCP' - protocol_port = '80' - args = ['--protocol', protocol, '--protocol-port', protocol_port, - '--loadbalancer', loadbalancer_id] - position_names = ['protocol', 'protocol_port', 'loadbalancer_id'] - position_values = [protocol, protocol_port, loadbalancer_id, - True] - self._test_create_resource(resource, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=cmd_resource) - - def test_create_listener_with_default_pool(self): - # lbaas-listener-create with --default-pool and no --loadbalancer. - resource = 'listener' - cmd_resource = 'lbaas_listener' - cmd = listener.CreateListener(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - default_pool_id = 'default-pool' - protocol = 'TCP' - protocol_port = '80' - args = ['--protocol', protocol, '--protocol-port', protocol_port, - '--default-pool', default_pool_id] - position_names = ['protocol', 'protocol_port', 'default_pool_id'] - position_values = [protocol, protocol_port, default_pool_id, - True] - self._test_create_resource(resource, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=cmd_resource) - - def test_create_listener_with_no_loadbalancer_or_default_pool(self): - # lbaas-listener-create without --default-pool or --loadbalancer. - resource = 'listener' - cmd_resource = 'lbaas_listener' - cmd = listener.CreateListener(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - protocol = 'TCP' - protocol_port = '80' - args = ['--protocol', protocol, '--protocol-port', protocol_port] - position_names = ['protocol', 'protocol_port'] - position_values = [protocol, protocol_port, True] - self._test_create_resource(resource, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=cmd_resource, - no_api_call=True, - expected_exception=exceptions.CommandError) - - def test_create_listener_with_all_params(self): - # lbaas-listener-create with all params set. - resource = 'listener' - cmd_resource = 'lbaas_listener' - cmd = listener.CreateListener(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - loadbalancer = 'loadbalancer' - default_pool_id = 'default-pool' - protocol = 'TCP' - protocol_port = '80' - connection_limit = 10 - def_tls_cont_ref = '11111' - args = ['--admin-state-down', - '--protocol', protocol, '--protocol-port', protocol_port, - '--loadbalancer', loadbalancer, - '--default-pool', default_pool_id, - '--default-tls-container-ref', def_tls_cont_ref, - '--sni-container-refs', '1111', '2222', '3333', - '--connection-limit', '10'] - position_names = ['admin_state_up', - 'protocol', 'protocol_port', 'loadbalancer_id', - 'default_pool_id', - 'default_tls_container_ref', 'sni_container_refs', - 'connection_limit'] - position_values = [False, protocol, protocol_port, loadbalancer, - default_pool_id, - def_tls_cont_ref, ['1111', '2222', '3333'], - connection_limit] - self._test_create_resource(resource, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=cmd_resource) - - def test_list_listeners(self): - # lbaas-listener-list. - resources = 'listeners' - cmd_resources = 'lbaas_listeners' - cmd = listener.ListListener(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True, - cmd_resources=cmd_resources) - - def test_list_listeners_pagination(self): - # lbaas-listener-list with pagination. - resources = 'listeners' - cmd_resources = 'lbaas_listeners' - cmd = listener.ListListener(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd, - cmd_resources=cmd_resources) - - def test_list_listeners_sort(self): - # lbaas-listener-list --sort-key id --sort-key asc. - resources = 'listeners' - cmd_resources = 'lbaas_listeners' - cmd = listener.ListListener(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True, - cmd_resources=cmd_resources) - - def test_list_listeners_limit(self): - # lbaas-listener-list -P. - resources = 'listeners' - cmd_resources = 'lbaas_listeners' - cmd = listener.ListListener(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000, - cmd_resources=cmd_resources) - - def test_show_listener_id(self): - # lbaas-listener-show test_id. - resource = 'listener' - cmd_resource = 'lbaas_listener' - cmd = listener.ShowListener(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id'], - cmd_resource=cmd_resource) - - def test_show_listener_id_name(self): - # lbaas-listener-show. - resource = 'listener' - cmd_resource = 'lbaas_listener' - cmd = listener.ShowListener(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name'], - cmd_resource=cmd_resource) - - def _test_update_listener(self, args, expected_values): - resource = 'listener' - cmd_resource = 'lbaas_listener' - my_id = 'myid' - args.insert(0, my_id) - cmd = listener.UpdateListener(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, my_id, - args, expected_values, - cmd_resource=cmd_resource) - - def test_update_listener(self): - # lbaas-listener-update myid --name newname. - self._test_update_listener(['--name', 'newname'], - {'name': 'newname', }) - # lbaas-listener-update myid --description check. - self._test_update_listener(['--description', 'check'], - {'description': 'check', }) - # lbaas-listener-update myid --connection-limit -1 - self._test_update_listener(['--connection-limit', '-1'], - {'connection_limit': -1, }) - # lbaas-listener-update myid --admin-state-up False. - self._test_update_listener(['--admin-state-up', 'False'], - {'admin_state_up': 'False', }) - - def test_delete_listener(self): - # lbaas-listener-delete my-id. - resource = 'listener' - cmd_resource = 'lbaas_listener' - cmd = listener.DeleteListener(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args, - cmd_resource=cmd_resource) diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py b/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py deleted file mode 100644 index 3a6010cee..000000000 --- a/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py +++ /dev/null @@ -1,213 +0,0 @@ -# Copyright 2014 Blue Box Group, 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 sys -from unittest import mock - - -from neutronclient.neutron.v2_0.lb.v2 import loadbalancer as lb -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20LbLoadBalancerJSON(test_cli20.CLITestV20Base): - - def test_create_loadbalancer_with_mandatory_params(self): - # lbaas-loadbalancer-create with mandatory params only. - resource = 'loadbalancer' - cmd_resource = 'lbaas_loadbalancer' - cmd = lb.CreateLoadBalancer(test_cli20.MyApp(sys.stdout), None) - name = 'lbaas-loadbalancer-name' - vip_subnet_id = 'vip-subnet' - my_id = 'my-id' - args = [vip_subnet_id] - position_names = ['vip_subnet_id'] - position_values = [vip_subnet_id] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - cmd_resource=cmd_resource) - - def test_create_loadbalancer_with_all_params(self): - # lbaas-loadbalancer-create with all params set. - resource = 'loadbalancer' - cmd_resource = 'lbaas_loadbalancer' - cmd = lb.CreateLoadBalancer(test_cli20.MyApp(sys.stdout), None) - name = 'lbaas-loadbalancer-name' - description = 'lbaas-loadbalancer-desc' - flavor_id = 'lbaas-loadbalancer-flavor' - vip_subnet_id = 'vip-subnet' - my_id = 'my-id' - args = ['--admin-state-down', '--description', description, - '--name', name, '--flavor', flavor_id, vip_subnet_id] - position_names = ['admin_state_up', 'description', 'name', - 'flavor_id', 'vip_subnet_id'] - position_values = [False, description, name, flavor_id, vip_subnet_id] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - cmd_resource=cmd_resource) - - def test_list_loadbalancers(self): - # lbaas-loadbalancer-list. - resources = 'loadbalancers' - cmd_resources = 'lbaas_loadbalancers' - cmd = lb.ListLoadBalancer(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True, - cmd_resources=cmd_resources) - - def test_list_loadbalancers_pagination(self): - # lbaas-loadbalancer-list with pagination. - resources = 'loadbalancers' - cmd_resources = 'lbaas_loadbalancers' - cmd = lb.ListLoadBalancer(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd, - cmd_resources=cmd_resources) - - def test_list_loadbalancers_sort(self): - # lbaas-loadbalancer-list --sort-key name --sort-key id --sort-key asc - # --sort-key desc - resources = 'loadbalancers' - cmd_resources = 'lbaas_loadbalancers' - cmd = lb.ListLoadBalancer(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"], - cmd_resources=cmd_resources) - - def test_list_loadbalancers_limit(self): - # lbaas-loadbalancer-list -P. - resources = 'loadbalancers' - cmd_resources = 'lbaas_loadbalancers' - cmd = lb.ListLoadBalancer(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000, - cmd_resources=cmd_resources) - - def test_show_loadbalancer_id(self): - # lbaas-loadbalancer-loadbalancer-show test_id. - resource = 'loadbalancer' - cmd_resource = 'lbaas_loadbalancer' - cmd = lb.ShowLoadBalancer(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id'], - cmd_resource=cmd_resource) - - def test_show_loadbalancer_id_name(self): - # lbaas-loadbalancer-loadbalancer-show. - resource = 'loadbalancer' - cmd_resource = 'lbaas_loadbalancer' - cmd = lb.ShowLoadBalancer(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name'], - cmd_resource=cmd_resource) - - def _test_update_lb(self, args, expected_values): - resource = 'loadbalancer' - cmd_resource = 'lbaas_loadbalancer' - my_id = 'myid' - args.insert(0, my_id) - cmd = lb.UpdateLoadBalancer(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, my_id, - args, expected_values, - cmd_resource=cmd_resource) - - def test_update_loadbalancer(self): - # lbaas-loadbalancer-update myid --name newname. - self._test_update_lb(['--name', 'newname'], {'name': 'newname', }) - # lbaas-loadbalancer-update myid --description check. - self._test_update_lb(['--description', 'check'], - {'description': 'check', }) - # lbaas-loadbalancer-update myid --admin-state-up False. - self._test_update_lb(['--admin-state-up', 'False'], - {'admin_state_up': 'False', }) - - def test_delete_loadbalancer(self): - # lbaas-loadbalancer-loadbalancer-delete my-id. - resource = 'loadbalancer' - cmd_resource = 'lbaas_loadbalancer' - cmd = lb.DeleteLoadBalancer(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args, - cmd_resource=cmd_resource) - - def test_retrieve_loadbalancer_stats(self): - # lbaas-loadbalancer-stats test_id. - resource = 'loadbalancer' - cmd = lb.RetrieveLoadBalancerStats(test_cli20.MyApp(sys.stdout), None) - my_id = self.test_id - fields = ['bytes_in', 'bytes_out'] - args = ['--fields', 'bytes_in', '--fields', 'bytes_out', my_id] - - query = "&".join(["fields=%s" % field for field in fields]) - expected_res = {'stats': {'bytes_in': '1234', 'bytes_out': '4321'}} - resstr = self.client.serialize(expected_res) - path = getattr(self.client, "lbaas_loadbalancer_path_stats") - return_tup = (test_cli20.MyResp(200), resstr) - - cmd_parser = cmd.get_parser("test_" + resource) - parsed_args = cmd_parser.parse_args(args) - with mock.patch.object(cmd, "get_client", - return_value=self.client) as mock_get_client, \ - mock.patch.object(self.client.httpclient, "request", - return_value=return_tup) as mock_request: - cmd.run(parsed_args) - - self.assert_mock_multiple_calls_with_same_arguments( - mock_get_client, mock.call(), 2) - mock_request.assert_called_once_with( - test_cli20.end_url(path % my_id, query), 'GET', - body=None, - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN})) - _str = self.fake_stdout.make_string() - self.assertIn('bytes_in', _str) - self.assertIn('1234', _str) - self.assertIn('bytes_out', _str) - self.assertIn('4321', _str) - - def test_get_loadbalancer_statuses(self): - # lbaas-loadbalancer-status test_id. - resource = 'loadbalancer' - cmd = lb.RetrieveLoadBalancerStatus(test_cli20.MyApp(sys.stdout), None) - my_id = self.test_id - args = [my_id] - - expected_res = {'statuses': {'operating_status': 'ONLINE', - 'provisioning_status': 'ACTIVE'}} - - resstr = self.client.serialize(expected_res) - - path = getattr(self.client, "lbaas_loadbalancer_path_status") - return_tup = (test_cli20.MyResp(200), resstr) - - cmd_parser = cmd.get_parser("test_" + resource) - parsed_args = cmd_parser.parse_args(args) - with mock.patch.object(cmd, "get_client", - return_value=self.client) as mock_get_client, \ - mock.patch.object(self.client.httpclient, "request", - return_value=return_tup) as mock_request: - cmd.run(parsed_args) - - mock_get_client.assert_called_once_with() - mock_request.assert_called_once_with( - test_cli20.end_url(path % my_id), 'GET', - body=None, - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN})) - _str = self.fake_stdout.make_string() - self.assertIn('operating_status', _str) - self.assertIn('ONLINE', _str) - self.assertIn('provisioning_status', _str) - self.assertIn('ACTIVE', _str) diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_member.py b/neutronclient/tests/unit/lb/v2/test_cli20_member.py deleted file mode 100644 index 057da4254..000000000 --- a/neutronclient/tests/unit/lb/v2/test_cli20_member.py +++ /dev/null @@ -1,171 +0,0 @@ -# Copyright 2014 Blue Box Group, 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 sys - -from neutronclient.neutron.v2_0.lb.v2 import member -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20LbMemberJSON(test_cli20.CLITestV20Base): - - def test_create_member_with_mandatory_params(self): - # lbaas-member-create with mandatory params only. - resource = 'member' - cmd_resource = 'lbaas_member' - cmd = member.CreateMember(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - address = '10.1.1.1' - protocol_port = '80' - pool_id = 'pool-id' - subnet_id = 'subnet-id' - args = ['--address', address, '--protocol-port', protocol_port, - '--subnet', subnet_id, pool_id] - position_names = ['admin_state_up', 'address', - 'protocol_port', 'subnet_id'] - position_values = [True, address, protocol_port, subnet_id] - self._test_create_resource(resource, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=cmd_resource, - parent_id=pool_id) - - def test_create_member_with_all_params(self): - # lbaas-member-create with all params set. - resource = 'member' - cmd_resource = 'lbaas_member' - cmd = member.CreateMember(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - address = '10.1.1.1' - protocol_port = '80' - pool_id = 'pool-id' - subnet_id = 'subnet-id' - weight = '100' - name = 'member1' - args = ['--address', address, '--protocol-port', protocol_port, - '--subnet', subnet_id, pool_id, '--weight', weight, - '--admin-state-down', '--name', name] - position_names = ['admin_state_up', 'address', 'protocol_port', - 'subnet_id', 'weight', 'name'] - position_values = [False, address, protocol_port, - subnet_id, weight, name] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - cmd_resource=cmd_resource, - parent_id=pool_id) - - def test_list_members(self): - # lbaas-member-list. - resources = 'members' - cmd_resources = 'lbaas_members' - pool_id = 'pool-id' - cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True, base_args=[pool_id], - cmd_resources=cmd_resources, - parent_id=pool_id, - query="pool_id=%s" % pool_id) - - def test_list_members_pagination(self): - # lbaas-member-list with pagination. - resources = 'members' - cmd_resources = 'lbaas_members' - pool_id = 'pool-id' - cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd, - base_args=[pool_id], - cmd_resources=cmd_resources, - parent_id=pool_id, - query="pool_id=%s" % pool_id) - - def test_list_members_sort(self): - # lbaas-member-list --sort-key id --sort-key asc. - resources = 'members' - cmd_resources = 'lbaas_members' - pool_id = 'pool-id' - cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True, base_args=[pool_id], - cmd_resources=cmd_resources, - parent_id=pool_id, - query="pool_id=%s" % pool_id) - - def test_list_members_limit(self): - # lbaas-member-list -P. - resources = 'members' - cmd_resources = 'lbaas_members' - pool_id = 'pool-id' - cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000, - base_args=[pool_id], - cmd_resources=cmd_resources, - parent_id=pool_id, - query="pool_id=%s" % pool_id) - - def test_show_member_id(self): - # lbaas-member-show test_id. - resource = 'member' - cmd_resource = 'lbaas_member' - pool_id = 'pool-id' - cmd = member.ShowMember(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id, pool_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id'], - cmd_resource=cmd_resource, parent_id=pool_id) - - def test_show_member_id_name(self): - # lbaas-member-show. - resource = 'member' - cmd_resource = 'lbaas_member' - pool_id = 'pool-id' - cmd = member.ShowMember(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id, pool_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name'], - cmd_resource=cmd_resource, parent_id=pool_id) - - def test_update_member(self): - # lbaas-member-update myid --name newname. - resource = 'member' - cmd_resource = 'lbaas_member' - my_id = 'my-id' - pool_id = 'pool-id' - args = [my_id, pool_id, '--name', 'newname'] - cmd = member.UpdateMember(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, my_id, args, - {'name': 'newname', }, - cmd_resource=cmd_resource, - parent_id=pool_id) - # lbaas-member-update myid --weight 100. - args = [my_id, pool_id, '--weight', '100'] - self._test_update_resource(resource, cmd, my_id, args, - {'weight': '100', }, - cmd_resource=cmd_resource, - parent_id=pool_id) - # lbaas-member-update myid --admin-state-up False - args = [my_id, pool_id, '--admin-state-up', 'False'] - self._test_update_resource(resource, cmd, my_id, args, - {'admin_state_up': 'False', }, - cmd_resource=cmd_resource, - parent_id=pool_id) - - def test_delete_member(self): - # lbaas-member-delete my-id. - resource = 'member' - cmd_resource = 'lbaas_member' - cmd = member.DeleteMember(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - pool_id = 'pool-id' - args = [my_id, pool_id] - self._test_delete_resource(resource, cmd, my_id, args, - cmd_resource=cmd_resource, - parent_id=pool_id) diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_pool.py b/neutronclient/tests/unit/lb/v2/test_cli20_pool.py deleted file mode 100644 index 8c1f428a3..000000000 --- a/neutronclient/tests/unit/lb/v2/test_cli20_pool.py +++ /dev/null @@ -1,202 +0,0 @@ -# Copyright 2014 Blue Box Group, 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 sys - -from neutronclient.common import exceptions -from neutronclient.neutron.v2_0.lb.v2 import pool -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20LbPoolJSON(test_cli20.CLITestV20Base): - - def test_create_pool_with_listener(self): - # lbaas-pool-create with listener - resource = 'pool' - cmd_resource = 'lbaas_pool' - cmd = pool.CreatePool(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - lb_algorithm = 'ROUND_ROBIN' - listener = 'listener' - protocol = 'TCP' - args = ['--lb-algorithm', lb_algorithm, '--protocol', protocol, - '--listener', listener] - position_names = ['admin_state_up', 'lb_algorithm', 'protocol', - 'listener_id'] - position_values = [True, lb_algorithm, protocol, listener] - self._test_create_resource(resource, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=cmd_resource) - - def test_create_pool_with_loadbalancer_no_listener(self): - """lbaas-pool-create with loadbalancer, no listener.""" - resource = 'pool' - cmd_resource = 'lbaas_pool' - cmd = pool.CreatePool(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - lb_algorithm = 'ROUND_ROBIN' - loadbalancer = 'loadbalancer' - protocol = 'TCP' - args = ['--lb-algorithm', lb_algorithm, '--protocol', protocol, - '--loadbalancer', loadbalancer] - position_names = ['admin_state_up', 'lb_algorithm', 'protocol', - 'loadbalancer_id'] - position_values = [True, lb_algorithm, protocol, loadbalancer] - self._test_create_resource(resource, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=cmd_resource) - - def test_create_pool_with_no_listener_or_loadbalancer(self): - """lbaas-pool-create with no listener or loadbalancer.""" - resource = 'pool' - cmd_resource = 'lbaas_pool' - cmd = pool.CreatePool(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - lb_algorithm = 'ROUND_ROBIN' - protocol = 'TCP' - args = ['--lb-algorithm', lb_algorithm, '--protocol', protocol] - position_names = ['admin_state_up', 'lb_algorithm', 'protocol'] - position_values = [True, lb_algorithm, protocol] - self._test_create_resource(resource, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=cmd_resource, - no_api_call=True, - expected_exception=exceptions.CommandError) - - def test_create_pool_with_all_params(self): - # lbaas-pool-create with all params set. - resource = 'pool' - cmd_resource = 'lbaas_pool' - cmd = pool.CreatePool(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - lb_algorithm = 'ROUND_ROBIN' - listener = 'listener' - loadbalancer = 'loadbalancer' - protocol = 'TCP' - description = 'description' - session_persistence_str = 'type=APP_COOKIE,cookie_name=1234' - session_persistence = {'type': 'APP_COOKIE', - 'cookie_name': '1234'} - name = 'my-pool' - args = ['--lb-algorithm', lb_algorithm, '--protocol', protocol, - '--description', description, '--session-persistence', - session_persistence_str, '--admin-state-down', '--name', name, - '--listener', listener, '--loadbalancer', loadbalancer] - position_names = ['lb_algorithm', 'protocol', 'description', - 'session_persistence', 'admin_state_up', 'name', - 'listener_id', 'loadbalancer_id'] - position_values = [lb_algorithm, protocol, description, - session_persistence, False, name, listener, - loadbalancer] - self._test_create_resource(resource, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=cmd_resource) - - def test_list_pools(self): - # lbaas-pool-list. - resources = 'pools' - cmd_resources = 'lbaas_pools' - cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True, - cmd_resources=cmd_resources) - - def test_list_pools_pagination(self): - # lbaas-pool-list with pagination. - resources = 'pools' - cmd_resources = 'lbaas_pools' - cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd, - cmd_resources=cmd_resources) - - def test_list_pools_sort(self): - # lbaas-pool-list --sort-key id --sort-key asc. - resources = 'pools' - cmd_resources = 'lbaas_pools' - cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True, - cmd_resources=cmd_resources) - - def test_list_pools_limit(self): - # lbaas-pool-list -P. - resources = 'pools' - cmd_resources = 'lbaas_pools' - cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000, - cmd_resources=cmd_resources) - - def test_show_pool_id(self): - # lbaas-pool-show test_id. - resource = 'pool' - cmd_resource = 'lbaas_pool' - cmd = pool.ShowPool(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id'], - cmd_resource=cmd_resource) - - def test_show_pool_id_name(self): - # lbaas-pool-show. - resource = 'pool' - cmd_resource = 'lbaas_pool' - cmd = pool.ShowPool(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name'], - cmd_resource=cmd_resource) - - def test_update_pool(self): - # lbaas-pool-update myid --name newname --description SuperPool - # --lb-algorithm SOURCE_IP --admin-state-up - # --session-persistence type=dict,type=HTTP_COOKIE,cookie_name=pie - - resource = 'pool' - cmd_resource = 'lbaas_pool' - cmd = pool.UpdatePool(test_cli20.MyApp(sys.stdout), None) - args = ['myid', '--name', 'newname', - '--description', 'SuperPool', '--lb-algorithm', "SOURCE_IP", - '--admin-state-up', 'True', - '--session-persistence', 'type=dict,' - 'type=HTTP_COOKIE,cookie_name=pie'] - body = {'name': 'newname', - "description": "SuperPool", - "lb_algorithm": "SOURCE_IP", - "admin_state_up": 'True', - 'session_persistence': { - 'type': 'HTTP_COOKIE', - 'cookie_name': 'pie', - }, } - self._test_update_resource(resource, cmd, 'myid', args, body, - cmd_resource=cmd_resource) - # lbaas-pool-update myid --name Name - # --no-session-persistence - - resource = 'pool' - cmd_resource = 'lbaas_pool' - cmd = pool.UpdatePool(test_cli20.MyApp(sys.stdout), None) - args = ['myid', '--name', 'Name', '--no-session-persistence'] - body = {'name': "Name", - "session_persistence": None, } - self._test_update_resource(resource, cmd, 'myid', args, body, - cmd_resource=cmd_resource) - - def test_delete_pool(self): - # lbaas-pool-delete my-id. - resource = 'pool' - cmd_resource = 'lbaas_pool' - cmd = pool.DeletePool(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args, - cmd_resource=cmd_resource) diff --git a/neutronclient/tests/unit/qos/__init__.py b/neutronclient/tests/unit/qos/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutronclient/tests/unit/qos/test_cli20_bandwidth_limit_rule.py b/neutronclient/tests/unit/qos/test_cli20_bandwidth_limit_rule.py deleted file mode 100644 index 8d4380a81..000000000 --- a/neutronclient/tests/unit/qos/test_cli20_bandwidth_limit_rule.py +++ /dev/null @@ -1,137 +0,0 @@ -# Copyright 2015 Huawei Technologies India Pvt Ltd. -# 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 sys - -from neutronclient.neutron.v2_0.qos import bandwidth_limit_rule as bw_rule -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20QoSBandwidthLimitRuleJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['bandwidth_limit_rule'] - - def setUp(self): - super(CLITestV20QoSBandwidthLimitRuleJSON, self).setUp() - self.res = 'bandwidth_limit_rule' - self.cmd_res = 'qos_bandwidth_limit_rule' - self.ress = self.res + 's' - self.cmd_ress = self.cmd_res + 's' - - def test_create_bandwidth_limit_rule_with_max_kbps(self): - cmd = bw_rule.CreateQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - max_kbps = '1337' - policy_id = 'policy_id' - args = ['--max-kbps', max_kbps, policy_id] - position_names = ['max_kbps'] - position_values = [max_kbps] - self._test_create_resource(self.res, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=self.cmd_res, - parent_id=policy_id) - - def test_create_bandwidth_limit_rule_with_max_burst_kbps(self): - cmd = bw_rule.CreateQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - max_burst_kbps = '1337' - policy_id = 'policy_id' - args = ['--max-burst-kbps', max_burst_kbps, policy_id] - position_names = ['max_burst_kbps'] - position_values = [max_burst_kbps] - self._test_create_resource(self.res, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=self.cmd_res, - parent_id=policy_id) - - def test_create_bandwidth_limit_rule_with_all_params(self): - cmd = bw_rule.CreateQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - max_kbps = '1337' - max_burst_kbps = '1337' - policy_id = 'policy_id' - args = ['--max-kbps', max_kbps, - '--max-burst-kbps', max_burst_kbps, - policy_id] - position_names = ['max_kbps', 'max_burst_kbps'] - position_values = [max_kbps, max_burst_kbps] - self._test_create_resource(self.res, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=self.cmd_res, - parent_id=policy_id) - - def test_update_bandwidth_limit_rule_with_max_kbps(self): - cmd = bw_rule.UpdateQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - max_kbps = '1337' - policy_id = 'policy_id' - args = ['--max-kbps', max_kbps, my_id, policy_id] - self._test_update_resource(self.res, cmd, my_id, args, - {'max_kbps': max_kbps, }, - cmd_resource=self.cmd_res, - parent_id=policy_id) - - def test_update_bandwidth_limit_rule_with_max_burst_kbps(self): - cmd = bw_rule.UpdateQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - max_burst_kbps = '1337' - policy_id = 'policy_id' - args = ['--max-burst-kbps', max_burst_kbps, - my_id, policy_id] - self._test_update_resource(self.res, cmd, my_id, args, - {'max_burst_kbps': max_burst_kbps}, - cmd_resource=self.cmd_res, - parent_id=policy_id) - - def test_update_bandwidth_limit_rule_with_all_params(self): - cmd = bw_rule.UpdateQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - max_kbps = '1337' - max_burst_kbps = '1337' - policy_id = 'policy_id' - args = ['--max-kbps', max_kbps, - '--max-burst-kbps', max_burst_kbps, - my_id, policy_id] - self._test_update_resource(self.res, cmd, my_id, args, - {'max_kbps': max_kbps, - 'max_burst_kbps': max_burst_kbps}, - cmd_resource=self.cmd_res, - parent_id=policy_id) - - def test_delete_bandwidth_limit_rule(self): - cmd = bw_rule.DeleteQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - policy_id = 'policy_id' - args = [my_id, policy_id] - self._test_delete_resource(self.res, cmd, my_id, args, - cmd_resource=self.cmd_res, - parent_id=policy_id) - - def test_show_bandwidth_limit_rule(self): - cmd = bw_rule.ShowQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), - None) - policy_id = 'policy_id' - args = ['--fields', 'id', self.test_id, policy_id] - self._test_show_resource(self.res, cmd, self.test_id, args, - ['id'], cmd_resource=self.cmd_res, - parent_id=policy_id) diff --git a/neutronclient/tests/unit/qos/test_cli20_dscp_marking_rule.py b/neutronclient/tests/unit/qos/test_cli20_dscp_marking_rule.py deleted file mode 100644 index 881b2f373..000000000 --- a/neutronclient/tests/unit/qos/test_cli20_dscp_marking_rule.py +++ /dev/null @@ -1,91 +0,0 @@ -# Copyright 2016 Comcast 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 sys - -from neutronclient.common import exceptions -from neutronclient.neutron.v2_0.qos import dscp_marking_rule as dscp_rule -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20QoSDscpMarkingRuleJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['dscp_marking_rule'] - - def setUp(self): - super(CLITestV20QoSDscpMarkingRuleJSON, self).setUp() - self.dscp_res = 'dscp_marking_rule' - self.dscp_cmd_res = 'qos_dscp_marking_rule' - self.dscp_ress = self.dscp_res + 's' - self.dscp_cmd_ress = self.dscp_cmd_res + 's' - - def test_create_dscp_marking_rule_with_dscp_mark(self): - cmd = dscp_rule.CreateQoSDscpMarkingRule(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - policy_id = 'policy_id' - position_names = ['dscp_mark'] - valid_dscp_marks = ['0', '56'] - invalid_dscp_marks = ['-1', '19', '42', '44', '57', '58'] - for dscp_mark in valid_dscp_marks: - args = ['--dscp-mark', dscp_mark, policy_id] - position_values = [dscp_mark] - self._test_create_resource(self.dscp_res, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=self.dscp_cmd_res, - parent_id=policy_id) - for dscp_mark in invalid_dscp_marks: - args = ['--dscp-mark', dscp_mark, policy_id] - position_values = [dscp_mark] - self._test_create_resource( - self.dscp_res, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=self.dscp_cmd_res, - parent_id=policy_id, - no_api_call=True, - expected_exception=exceptions.CommandError) - - def test_update_dscp_marking_rule_with_dscp_mark(self): - cmd = dscp_rule.UpdateQoSDscpMarkingRule(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - dscp_mark = '56' - policy_id = 'policy_id' - args = ['--dscp-mark', dscp_mark, - my_id, policy_id] - self._test_update_resource(self.dscp_res, cmd, my_id, args, - {'dscp_mark': dscp_mark}, - cmd_resource=self.dscp_cmd_res, - parent_id=policy_id) - - def test_delete_dscp_marking_rule(self): - cmd = dscp_rule.DeleteQoSDscpMarkingRule(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - policy_id = 'policy_id' - args = [my_id, policy_id] - self._test_delete_resource(self.dscp_res, cmd, my_id, args, - cmd_resource=self.dscp_cmd_res, - parent_id=policy_id) - - def test_show_dscp_marking_rule(self): - cmd = dscp_rule.ShowQoSDscpMarkingRule(test_cli20.MyApp(sys.stdout), - None) - policy_id = 'policy_id' - args = ['--fields', 'id', self.test_id, policy_id] - self._test_show_resource(self.dscp_res, cmd, self.test_id, args, - ['id'], cmd_resource=self.dscp_cmd_res, - parent_id=policy_id) diff --git a/neutronclient/tests/unit/qos/test_cli20_minimum_bandwidth_rule.py b/neutronclient/tests/unit/qos/test_cli20_minimum_bandwidth_rule.py deleted file mode 100644 index 39dccd614..000000000 --- a/neutronclient/tests/unit/qos/test_cli20_minimum_bandwidth_rule.py +++ /dev/null @@ -1,143 +0,0 @@ -# Copyright (c) 2016 Intel Corporation. -# 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 sys - -from neutronclient.neutron.v2_0.qos import minimum_bandwidth_rule as bw_rule -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20QoSMinimumBandwidthRuleJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['minimum_bandwidth_rule'] - - def setUp(self): - super(CLITestV20QoSMinimumBandwidthRuleJSON, self).setUp() - self.res = 'minimum_bandwidth_rule' - self.cmd_res = 'qos_minimum_bandwidth_rule' - self.ress = self.res + 's' - self.cmd_ress = self.cmd_res + 's' - - def test_create_minimum_bandwidth_rule_min_kbps_only(self): - cmd = bw_rule.CreateQoSMinimumBandwidthRule( - test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - min_kbps = '1500' - policy_id = 'policy_id' - args = ['--min-kbps', min_kbps, - policy_id] - position_names = ['min_kbps'] - position_values = [min_kbps] - self.assertRaises(SystemExit, self._test_create_resource, - self.res, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=self.cmd_res, - parent_id=policy_id, - no_api_call=True) - - def test_create_minimum_bandwidth_rule_direction_only(self): - cmd = bw_rule.CreateQoSMinimumBandwidthRule( - test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - direction = 'egress' - policy_id = 'policy_id' - args = ['--direction', direction, - policy_id] - position_names = ['direction'] - position_values = [direction] - self.assertRaises(SystemExit, self._test_create_resource, - self.res, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=self.cmd_res, - parent_id=policy_id, - no_api_call=True) - - def test_create_minimum_bandwidth_rule_none(self): - cmd = bw_rule.CreateQoSMinimumBandwidthRule( - test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - policy_id = 'policy_id' - args = [policy_id] - position_names = [] - position_values = [] - self.assertRaises(SystemExit, self._test_create_resource, - self.res, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=self.cmd_res, - parent_id=policy_id, - no_api_call=True) - - def test_create_minimum_bandwidth_rule_all(self): - cmd = bw_rule.CreateQoSMinimumBandwidthRule( - test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - min_kbps = '1500' - direction = 'egress' - policy_id = 'policy_id' - args = ['--min-kbps', min_kbps, - '--direction', direction, - policy_id] - position_names = ['direction', 'min_kbps'] - position_values = [direction, min_kbps] - self._test_create_resource(self.res, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=self.cmd_res, - parent_id=policy_id) - - def test_update_minimum_bandwidth_rule(self): - cmd = bw_rule.UpdateQoSMinimumBandwidthRule( - test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - min_kbps = '1200' - direction = 'egress' - policy_id = 'policy_id' - args = ['--min-kbps', min_kbps, - '--direction', direction, - my_id, policy_id] - self._test_update_resource(self.res, cmd, my_id, args, - {'min_kbps': min_kbps, - 'direction': direction}, - cmd_resource=self.cmd_res, - parent_id=policy_id) - - def test_delete_minimum_bandwidth_rule(self): - cmd = bw_rule.DeleteQoSMinimumBandwidthRule( - test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - policy_id = 'policy_id' - args = [my_id, policy_id] - self._test_delete_resource(self.res, cmd, my_id, args, - cmd_resource=self.cmd_res, - parent_id=policy_id) - - def test_show_minimum_bandwidth_rule(self): - cmd = bw_rule.ShowQoSMinimumBandwidthRule( - test_cli20.MyApp(sys.stdout), None) - policy_id = 'policy_id' - args = [self.test_id, policy_id] - self._test_show_resource(self.res, cmd, self.test_id, args, - [], cmd_resource=self.cmd_res, - parent_id=policy_id) - - def test_list_minimum_bandwidth_rule(self): - cmd = bw_rule.ListQoSMinimumBandwidthRules( - test_cli20.MyApp(sys.stdout), None) - policy_id = 'policy_id' - args = [policy_id] - contents = [{'name': 'rule1', 'min-kbps': 1000, 'direction': 'egress'}] - self._test_list_resources(self.ress, cmd, parent_id=policy_id, - cmd_resources=self.cmd_ress, - base_args=args, response_contents=contents) diff --git a/neutronclient/tests/unit/qos/test_cli20_policy.py b/neutronclient/tests/unit/qos/test_cli20_policy.py deleted file mode 100644 index b1db866e4..000000000 --- a/neutronclient/tests/unit/qos/test_cli20_policy.py +++ /dev/null @@ -1,192 +0,0 @@ -# Copyright 2015 Huawei Technologies India Pvt Ltd. -# 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 sys - -from neutronclient.neutron.v2_0.qos import policy as policy -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20QoSPolicyJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['policy'] - - def setUp(self): - super(CLITestV20QoSPolicyJSON, self).setUp() - self.res = 'policy' - self.cmd_res = 'qos_policy' - self.ress = "policies" - self.cmd_ress = 'qos_policies' - - def test_create_policy_with_only_keyattributes(self): - # Create qos policy abc. - cmd = policy.CreateQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - myid = 'myid' - name = 'abc' - args = [name] - position_names = ['name'] - position_values = [name] - self._test_create_resource(self.res, cmd, name, myid, args, - position_names, position_values, - cmd_resource=self.cmd_res) - - def test_create_policy_with_description(self): - # Create qos policy xyz --description abc. - cmd = policy.CreateQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - myid = 'myid' - name = 'abc' - description = 'policy_abc' - args = [name, '--description', description] - position_names = ['name', 'description'] - position_values = [name, description] - self._test_create_resource(self.res, cmd, name, myid, args, - position_names, position_values, - cmd_resource=self.cmd_res) - - def test_create_policy_with_shared(self): - # Create qos policy abc shared across tenants - cmd = policy.CreateQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - myid = 'myid' - name = 'abc' - description = 'policy_abc' - args = [name, '--description', description, '--shared'] - position_names = ['name', 'description', 'shared'] - position_values = [name, description, True] - self._test_create_resource(self.res, cmd, name, myid, args, - position_names, position_values, - cmd_resource=self.cmd_res) - - def test_create_policy_with_unicode(self): - # Create qos policy u'\u7f51\u7edc'. - cmd = policy.CreateQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - myid = 'myid' - name = u'\u7f51\u7edc' - description = u'\u7f51\u7edc' - args = [name, '--description', description] - position_names = ['name', 'description'] - position_values = [name, description] - self._test_create_resource(self.res, cmd, name, myid, args, - position_names, position_values, - cmd_resource=self.cmd_res) - - def test_update_policy(self): - # policy-update myid --name newname. - cmd = policy.UpdateQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - self._test_update_resource(self.res, cmd, 'myid', - ['myid', '--name', 'newname'], - {'name': 'newname', }, - cmd_resource=self.cmd_res) - - def test_update_policy_description(self): - # policy-update myid --name newname --description newdesc - cmd = policy.UpdateQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - self._test_update_resource(self.res, cmd, 'myid', - ['myid', '--description', 'newdesc'], - {'description': 'newdesc', }, - cmd_resource=self.cmd_res) - - def test_update_policy_to_shared(self): - # policy-update myid --shared - cmd = policy.UpdateQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - self._test_update_resource(self.res, cmd, 'myid', - ['myid', '--shared'], - {'shared': True, }, - cmd_resource=self.cmd_res) - - def test_update_policy_to_no_shared(self): - # policy-update myid --no-shared - cmd = policy.UpdateQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - self._test_update_resource(self.res, cmd, 'myid', - ['myid', '--no-shared'], - {'shared': False, }, - cmd_resource=self.cmd_res) - - def test_update_policy_to_shared_no_shared_together(self): - # policy-update myid --shared --no-shared - cmd = policy.UpdateQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - self.assertRaises( - SystemExit, - self._test_update_resource, - self.res, cmd, 'myid', - ['myid', '--shared', '--no-shared'], {}, - cmd_resource=self.cmd_res - ) - - def test_list_policies(self): - # qos-policy-list. - cmd = policy.ListQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(self.ress, cmd, True, - cmd_resources=self.cmd_ress) - - def test_list_policies_pagination(self): - # qos-policy-list for pagination. - cmd = policy.ListQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources_with_pagination(self.ress, cmd, - cmd_resources=self.cmd_ress) - - def test_list_policies_sort(self): - # sorted list: qos-policy-list --sort-key name --sort-key id - # --sort-key asc --sort-key desc - cmd = policy.ListQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(self.ress, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"], - cmd_resources=self.cmd_ress) - - def test_list_policies_limit(self): - # size (1000) limited list: qos-policy-list -P. - cmd = policy.ListQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(self.ress, cmd, page_size=1000, - cmd_resources=self.cmd_ress) - - def test_show_policy_id(self): - # qos-policy-show test_id. - cmd = policy.ShowQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(self.res, cmd, self.test_id, args, - ['id'], cmd_resource=self.cmd_res) - - def test_show_policy_name(self): - # qos-policy-show. - cmd = policy.ShowQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(self.res, cmd, self.test_id, - args, ['id', 'name'], - cmd_resource=self.cmd_res) - - def test_delete_policy(self): - # qos-policy-delete my-id. - cmd = policy.DeleteQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - my_id = 'myid1' - args = [my_id] - self._test_delete_resource(self.res, cmd, my_id, args, - cmd_resource=self.cmd_res) diff --git a/neutronclient/tests/unit/qos/test_cli20_rule.py b/neutronclient/tests/unit/qos/test_cli20_rule.py deleted file mode 100644 index f77c3c410..000000000 --- a/neutronclient/tests/unit/qos/test_cli20_rule.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright 2015 Huawei Technologies India Pvt Ltd. -# 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 sys - -from neutronclient.neutron.v2_0.qos import rule as qos_rule -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20QoSRuleJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['bandwidth_limit_rule', - 'dscp_marking_rule', - 'minimum_bandwidth_rule'] - - def setUp(self): - super(CLITestV20QoSRuleJSON, self).setUp() - - def test_list_qos_rule_types(self): - # qos_rule_types. - resources = 'rule_types' - cmd_resources = 'qos_rule_types' - response_contents = [{'type': 'bandwidth_limit'}, - {'type': 'dscp_marking'}, - {'type': 'minimum_bandwidth'}] - - cmd = qos_rule.ListQoSRuleTypes(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, True, - response_contents=response_contents, - cmd_resources=cmd_resources) diff --git a/neutronclient/tests/unit/test_auto_allocated_topology.py b/neutronclient/tests/unit/test_auto_allocated_topology.py deleted file mode 100644 index e2f7e5b13..000000000 --- a/neutronclient/tests/unit/test_auto_allocated_topology.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright 2016 IBM -# 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 sys - -from neutronclient.neutron.v2_0 import auto_allocated_topology as aat -from neutronclient.tests.unit import test_cli20 - - -class TestAutoAllocatedTopologyJSON(test_cli20.CLITestV20Base): - - def test_show_auto_allocated_topology_arg(self): - resource = 'auto_allocated_topology' - cmd = aat.ShowAutoAllocatedTopology(test_cli20.MyApp(sys.stdout), None) - args = ['--tenant-id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args) - - def test_show_auto_allocated_topology_posarg(self): - resource = 'auto_allocated_topology' - cmd = aat.ShowAutoAllocatedTopology(test_cli20.MyApp(sys.stdout), None) - args = ['some-tenant'] - self._test_show_resource(resource, cmd, "some-tenant", args) - - def test_show_auto_allocated_topology_no_arg(self): - resource = 'auto_allocated_topology' - cmd = aat.ShowAutoAllocatedTopology(test_cli20.MyApp(sys.stdout), None) - args = [] - self._test_show_resource(resource, cmd, "None", args) - - def test_show_auto_allocated_topology_dry_run_as_tenant(self): - resource = 'auto_allocated_topology' - cmd = aat.ShowAutoAllocatedTopology(test_cli20.MyApp(sys.stdout), None) - args = ['--dry-run'] - self._test_show_resource(resource, cmd, "None", args, - fields=('dry-run',)) - - def test_show_auto_allocated_topology_dry_run_as_admin(self): - resource = 'auto_allocated_topology' - cmd = aat.ShowAutoAllocatedTopology(test_cli20.MyApp(sys.stdout), None) - args = ['--dry-run', 'some-tenant'] - self._test_show_resource(resource, cmd, "some-tenant", args, - fields=('dry-run',)) - - def test_delete_auto_allocated_topology_arg(self): - resource = 'auto_allocated_topology' - cmd = aat.DeleteAutoAllocatedTopology(test_cli20.MyApp(sys.stdout), - None) - args = ['--tenant-id', self.test_id] - self._test_delete_resource(resource, cmd, self.test_id, args) - - def test_delete_auto_allocated_topology_posarg(self): - resource = 'auto_allocated_topology' - cmd = aat.DeleteAutoAllocatedTopology(test_cli20.MyApp(sys.stdout), - None) - args = ['some-tenant'] - self._test_delete_resource(resource, cmd, "some-tenant", args) - - def test_delete_auto_allocated_topology_no_arg(self): - resource = 'auto_allocated_topology' - cmd = aat.DeleteAutoAllocatedTopology(test_cli20.MyApp(sys.stdout), - None) - args = [] - self._test_delete_resource(resource, cmd, "None", args) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py deleted file mode 100644 index a067ee893..000000000 --- a/neutronclient/tests/unit/test_cli20.py +++ /dev/null @@ -1,1215 +0,0 @@ -# Copyright 2012 OpenStack Foundation. -# 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 contextlib -from io import StringIO -import itertools -import sys -from unittest import mock -import urllib.parse as urlparse - -from oslo_serialization import jsonutils -from oslo_utils import encodeutils -from oslotest import base -import requests -import yaml - -from neutronclient.common import constants -from neutronclient.common import exceptions -from neutronclient.common import utils -from neutronclient.neutron import v2_0 as neutronV2_0 -from neutronclient.neutron.v2_0 import network -from neutronclient import shell -from neutronclient.v2_0 import client - -API_VERSION = "2.0" -TOKEN = 'testtoken' -ENDURL = 'localurl' -REQUEST_ID = 'test_request_id' - - -@contextlib.contextmanager -def capture_std_streams(): - fake_stdout, fake_stderr = StringIO(), StringIO() - stdout, stderr = sys.stdout, sys.stderr - try: - sys.stdout, sys.stderr = fake_stdout, fake_stderr - yield fake_stdout, fake_stderr - finally: - sys.stdout, sys.stderr = stdout, stderr - - -class FakeStdout(object): - - def __init__(self): - self.content = [] - - def write(self, text): - self.content.append(text) - - def make_string(self): - result = '' - for line in self.content: - result += encodeutils.safe_decode(line, 'utf-8') - return result - - -class MyRequest(requests.Request): - def __init__(self, method=None): - self.method = method - - -class MyResp(requests.Response): - def __init__(self, status_code, headers=None, reason=None, - request=None, url=None): - self.status_code = status_code - self.headers = headers or {} - self.reason = reason - self.request = request or MyRequest() - self.url = url - - -class MyApp(object): - def __init__(self, _stdout): - self.stdout = _stdout - - -def end_url(path, query=None): - _url_str = ENDURL + "/v" + API_VERSION + path - return query and _url_str + "?" + query or _url_str - - -class MyUrlComparator(object): - def __init__(self, lhs, client): - self.lhs = lhs - self.client = client - - def __eq__(self, rhs): - lhsp = urlparse.urlparse(self.lhs) - rhsp = urlparse.urlparse(rhs) - - lhs_qs = urlparse.parse_qsl(lhsp.query) - rhs_qs = urlparse.parse_qsl(rhsp.query) - - return (lhsp.scheme == rhsp.scheme and - lhsp.netloc == rhsp.netloc and - lhsp.path == rhsp.path and - len(lhs_qs) == len(rhs_qs) and - set(lhs_qs) == set(rhs_qs)) - - def __str__(self): - return self.lhs - - def __repr__(self): - return str(self) - - -class MyComparator(object): - def __init__(self, lhs, client): - self.lhs = lhs - self.client = client - - def _com_dict(self, lhs, rhs): - if len(lhs) != len(rhs): - return False - for key, value in lhs.items(): - if key not in rhs: - return False - rhs_value = rhs[key] - if not self._com(value, rhs_value): - return False - return True - - def _com_list(self, lhs, rhs): - if len(lhs) != len(rhs): - return False - for lhs_value in lhs: - if lhs_value not in rhs: - return False - return True - - def _com(self, lhs, rhs): - if lhs is None: - return rhs is None - if isinstance(lhs, dict): - if not isinstance(rhs, dict): - return False - return self._com_dict(lhs, rhs) - if isinstance(lhs, list): - if not isinstance(rhs, list): - return False - return self._com_list(lhs, rhs) - if isinstance(lhs, tuple): - if not isinstance(rhs, tuple): - return False - return self._com_list(lhs, rhs) - return lhs == rhs - - def __eq__(self, rhs): - if self.client: - rhs = self.client.deserialize(rhs, 200) - return self._com(self.lhs, rhs) - - def __repr__(self): - if self.client: - return self.client.serialize(self.lhs) - return str(self.lhs) - - -class ContainsKeyValue(object): - """Checks whether key/value pair(s) are included in a dict parameter. - - This class just checks whether specifid key/value pairs passed in - __init__() are included in a dict parameter. The comparison does not - fail even if other key/value pair(s) exists in a target dict. - """ - - def __init__(self, expected): - self._expected = expected - - def __eq__(self, other): - if not isinstance(other, dict): - return False - for key, value in self._expected.items(): - if key not in other: - return False - if other[key] != value: - return False - return True - - def __repr__(self): - return ('<%s (expected: %s)>' % - (self.__class__.__name__, self._expected)) - - -class IsA(object): - """Checks whether the parameter is of specific type.""" - - def __init__(self, expected_type): - self._expected_type = expected_type - - def __eq__(self, other): - return isinstance(other, self._expected_type) - - def __repr__(self): - return ('<%s (expected: %s)>' % - (self.__class__.__name__, self._expected_type)) - - -class CLITestV20Base(base.BaseTestCase): - - test_id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' - id_field = 'id' - - non_admin_status_resources = [] - - def _find_resourceid(self, client, resource, name_or_id, - cmd_resource=None, parent_id=None): - return name_or_id - - def setUp(self, plurals=None): - """Prepare the test environment.""" - super(CLITestV20Base, self).setUp() - client.Client.EXTED_PLURALS.update(constants.PLURALS) - if plurals is not None: - client.Client.EXTED_PLURALS.update(plurals) - self.metadata = {'plurals': client.Client.EXTED_PLURALS} - self.endurl = ENDURL - self.fake_stdout = FakeStdout() - - self.addCleanup(mock.patch.stopall) - mock.patch('sys.stdout', new=self.fake_stdout).start() - mock.patch('neutronclient.neutron.v2_0.find_resourceid_by_name_or_id', - new=self._find_resourceid).start() - mock.patch('neutronclient.neutron.v2_0.find_resourceid_by_id', - new=self._find_resourceid).start() - - self.client = client.Client(token=TOKEN, endpoint_url=self.endurl) - - def register_non_admin_status_resource(self, resource_name): - # TODO(amotoki): - # It is recommended to define - # "non_admin_status_resources in each test class rather than - # using register_non_admin_status_resource method. - - # If we change self.non_admin_status_resources like this, - # we need to ensure this should be an instance variable - # to avoid changing the class variable. - if (id(self.non_admin_status_resources) == - id(self.__class__.non_admin_status_resources)): - self.non_admin_status_resources = (self.__class__. - non_admin_status_resources[:]) - self.non_admin_status_resources.append(resource_name) - - def _test_create_resource(self, resource, cmd, name, myid, args, - position_names, position_values, - tenant_id=None, tags=None, admin_state_up=True, - extra_body=None, cmd_resource=None, - parent_id=None, no_api_call=False, - expected_exception=None, - **kwargs): - if not cmd_resource: - cmd_resource = resource - if (resource in self.non_admin_status_resources): - body = {resource: {}, } - else: - body = {resource: {'admin_state_up': admin_state_up, }, } - if tenant_id: - body[resource].update({'tenant_id': tenant_id}) - if tags: - body[resource].update({'tags': tags}) - if extra_body: - body[resource].update(extra_body) - body[resource].update(kwargs) - - for i in range(len(position_names)): - body[resource].update({position_names[i]: position_values[i]}) - ress = {resource: - {self.id_field: myid}, } - if name: - ress[resource].update({'name': name}) - resstr = self.client.serialize(ress) - # url method body - resource_plural = self.client.get_resource_plural(cmd_resource) - path = getattr(self.client, resource_plural + "_path") - if parent_id: - path = path % parent_id - mock_body = MyComparator(body, self.client) - - cmd_parser = cmd.get_parser('create_' + resource) - resp = (MyResp(200), resstr) - - with mock.patch.object(cmd, "get_client", - return_value=self.client) as mock_get_client, \ - mock.patch.object(self.client.httpclient, "request", - return_value=resp) as mock_request: - if expected_exception: - self.assertRaises(expected_exception, - shell.run_command, cmd, cmd_parser, args) - else: - shell.run_command(cmd, cmd_parser, args) - - self.assert_mock_multiple_calls_with_same_arguments( - mock_get_client, mock.call(), None) - if not no_api_call: - mock_request.assert_called_once_with( - end_url(path), 'POST', - body=mock_body, - headers=ContainsKeyValue({'X-Auth-Token': TOKEN})) - if not expected_exception: - _str = self.fake_stdout.make_string() - self.assertIn(myid, _str) - if name: - self.assertIn(name, _str) - - def _test_list_columns(self, cmd, resources, - resources_out, args=('-f', 'json'), - cmd_resources=None, parent_id=None): - if not cmd_resources: - cmd_resources = resources - - resstr = self.client.serialize(resources_out) - - path = getattr(self.client, cmd_resources + "_path") - if parent_id: - path = path % parent_id - cmd_parser = cmd.get_parser("list_" + cmd_resources) - resp = (MyResp(200), resstr) - - with mock.patch.object(cmd, "get_client", - return_value=self.client) as mock_get_client, \ - mock.patch.object(self.client.httpclient, "request", - return_value=resp) as mock_request: - shell.run_command(cmd, cmd_parser, args) - - self.assert_mock_multiple_calls_with_same_arguments( - mock_get_client, mock.call(), None) - mock_request.assert_called_once_with( - end_url(path), 'GET', - body=None, - headers=ContainsKeyValue({'X-Auth-Token': TOKEN})) - - def _test_list_resources(self, resources, cmd, detail=False, tags=(), - fields_1=(), fields_2=(), page_size=None, - sort_key=(), sort_dir=(), response_contents=None, - base_args=None, path=None, cmd_resources=None, - parent_id=None, output_format=None, query=""): - if not cmd_resources: - cmd_resources = resources - if response_contents is None: - contents = [{self.id_field: 'myid1', }, - {self.id_field: 'myid2', }, ] - else: - contents = response_contents - reses = {resources: contents} - resstr = self.client.serialize(reses) - # url method body - args = base_args if base_args is not None else [] - if detail: - args.append('-D') - if fields_1: - for field in fields_1: - args.append('--fields') - args.append(field) - - if tags: - args.append('--') - args.append("--tag") - for tag in tags: - args.append(tag) - tag_query = urlparse.urlencode( - {'tag': encodeutils.safe_encode(tag)}) - if query: - query += "&" + tag_query - else: - query = tag_query - if (not tags) and fields_2: - args.append('--') - if fields_2: - args.append("--fields") - for field in fields_2: - args.append(field) - if detail: - query = query and query + '&verbose=True' or 'verbose=True' - for field in itertools.chain(fields_1, fields_2): - if query: - query += "&fields=" + field - else: - query = "fields=" + field - if page_size: - args.append("--page-size") - args.append(str(page_size)) - if query: - query += "&limit=%s" % page_size - else: - query = "limit=%s" % page_size - if sort_key: - for key in sort_key: - args.append('--sort-key') - args.append(key) - if query: - query += '&' - query += 'sort_key=%s' % key - if sort_dir: - len_diff = len(sort_key) - len(sort_dir) - if len_diff > 0: - sort_dir = tuple(sort_dir) + ('asc',) * len_diff - elif len_diff < 0: - sort_dir = sort_dir[:len(sort_key)] - for dir in sort_dir: - args.append('--sort-dir') - args.append(dir) - if query: - query += '&' - query += 'sort_dir=%s' % dir - if path is None: - path = getattr(self.client, cmd_resources + "_path") - if parent_id: - path = path % parent_id - if output_format: - args.append('-f') - args.append(output_format) - cmd_parser = cmd.get_parser("list_" + cmd_resources) - resp = (MyResp(200), resstr) - - with mock.patch.object(cmd, "get_client", - return_value=self.client) as mock_get_client, \ - mock.patch.object(self.client.httpclient, "request", - return_value=resp) as mock_request: - shell.run_command(cmd, cmd_parser, args) - - self.assert_mock_multiple_calls_with_same_arguments( - mock_get_client, mock.call(), None) - mock_request.assert_called_once_with( - MyUrlComparator(end_url(path, query), self.client), - 'GET', - body=None, - headers=ContainsKeyValue({'X-Auth-Token': TOKEN})) - _str = self.fake_stdout.make_string() - if response_contents is None: - self.assertIn('myid1', _str) - return _str - - def _test_list_resources_with_pagination(self, resources, cmd, - base_args=None, - cmd_resources=None, - parent_id=None, query=""): - if not cmd_resources: - cmd_resources = resources - - path = getattr(self.client, cmd_resources + "_path") - if parent_id: - path = path % parent_id - fake_query = "marker=myid2&limit=2" - reses1 = {resources: [{'id': 'myid1', }, - {'id': 'myid2', }], - '%s_links' % resources: [{'href': end_url(path, fake_query), - 'rel': 'next'}]} - reses2 = {resources: [{'id': 'myid3', }, - {'id': 'myid4', }]} - resstr1 = self.client.serialize(reses1) - resstr2 = self.client.serialize(reses2) - cmd_parser = cmd.get_parser("list_" + cmd_resources) - args = base_args if base_args is not None else [] - mock_request_calls = [ - mock.call( - end_url(path, query), 'GET', - body=None, - headers=ContainsKeyValue({'X-Auth-Token': TOKEN})), - mock.call( - MyUrlComparator(end_url(path, fake_query), - self.client), 'GET', - body=None, - headers=ContainsKeyValue({'X-Auth-Token': TOKEN}))] - mock_request_resp = [(MyResp(200), resstr1), (MyResp(200), resstr2)] - - with mock.patch.object(cmd, "get_client", - return_value=self.client) as mock_get_client, \ - mock.patch.object(self.client.httpclient, - "request") as mock_request: - mock_request.side_effect = mock_request_resp - shell.run_command(cmd, cmd_parser, args) - - self.assert_mock_multiple_calls_with_same_arguments( - mock_get_client, mock.call(), None) - self.assertEqual(2, mock_request.call_count) - mock_request.assert_has_calls(mock_request_calls) - - def _test_update_resource(self, resource, cmd, myid, args, extrafields, - cmd_resource=None, parent_id=None): - if not cmd_resource: - cmd_resource = resource - - body = {resource: extrafields} - path = getattr(self.client, cmd_resource + "_path") - if parent_id: - path = path % (parent_id, myid) - else: - path = path % myid - mock_body = MyComparator(body, self.client) - - cmd_parser = cmd.get_parser("update_" + cmd_resource) - resp = (MyResp(204), None) - - with mock.patch.object(cmd, "get_client", - return_value=self.client) as mock_get_client, \ - mock.patch.object(self.client.httpclient, "request", - return_value=resp) as mock_request: - shell.run_command(cmd, cmd_parser, args) - - self.assert_mock_multiple_calls_with_same_arguments( - mock_get_client, mock.call(), None) - mock_request.assert_called_once_with( - MyUrlComparator(end_url(path), self.client), - 'PUT', - body=mock_body, - headers=ContainsKeyValue({'X-Auth-Token': TOKEN})) - _str = self.fake_stdout.make_string() - self.assertIn(myid, _str) - - def _test_show_resource(self, resource, cmd, myid, args, fields=(), - cmd_resource=None, parent_id=None): - if not cmd_resource: - cmd_resource = resource - - query = "&".join(["fields=%s" % field for field in fields]) - expected_res = {resource: - {self.id_field: myid, - 'name': 'myname', }, } - resstr = self.client.serialize(expected_res) - path = getattr(self.client, cmd_resource + "_path") - if parent_id: - path = path % (parent_id, myid) - else: - path = path % myid - cmd_parser = cmd.get_parser("show_" + cmd_resource) - resp = (MyResp(200), resstr) - - with mock.patch.object(cmd, "get_client", - return_value=self.client) as mock_get_client, \ - mock.patch.object(self.client.httpclient, "request", - return_value=resp) as mock_request: - shell.run_command(cmd, cmd_parser, args) - - self.assert_mock_multiple_calls_with_same_arguments( - mock_get_client, mock.call(), None) - mock_request.assert_called_once_with( - end_url(path, query), 'GET', - body=None, - headers=ContainsKeyValue({'X-Auth-Token': TOKEN})) - _str = self.fake_stdout.make_string() - self.assertIn(myid, _str) - self.assertIn('myname', _str) - - def _test_set_path_and_delete(self, path, parent_id, myid, - mock_request_calls, mock_request_returns, - delete_fail=False): - return_val = 404 if delete_fail else 204 - if parent_id: - path = path % (parent_id, myid) - else: - path = path % (myid) - mock_request_returns.append((MyResp(return_val), None)) - mock_request_calls.append(mock.call( - end_url(path), 'DELETE', - body=None, - headers=ContainsKeyValue({'X-Auth-Token': TOKEN}))) - - def _test_delete_resource(self, resource, cmd, myid, args, - cmd_resource=None, parent_id=None, - extra_id=None, delete_fail=False): - mock_request_calls = [] - mock_request_returns = [] - if not cmd_resource: - cmd_resource = resource - path = getattr(self.client, cmd_resource + "_path") - self._test_set_path_and_delete(path, parent_id, myid, - mock_request_calls, - mock_request_returns) - # extra_id is used to test for bulk_delete - if extra_id: - self._test_set_path_and_delete(path, parent_id, extra_id, - mock_request_calls, - mock_request_returns, - delete_fail) - cmd_parser = cmd.get_parser("delete_" + cmd_resource) - - with mock.patch.object(cmd, "get_client", - return_value=self.client) as mock_get_client, \ - mock.patch.object(self.client.httpclient, - "request") as mock_request: - mock_request.side_effect = mock_request_returns - shell.run_command(cmd, cmd_parser, args) - - self.assert_mock_multiple_calls_with_same_arguments( - mock_get_client, mock.call(), None) - mock_request.assert_has_calls(mock_request_calls) - _str = self.fake_stdout.make_string() - self.assertIn(myid, _str) - if extra_id: - self.assertIn(extra_id, _str) - - def _test_update_resource_action(self, resource, cmd, myid, action, args, - body, expected_code=200, retval=None, - cmd_resource=None): - if not cmd_resource: - cmd_resource = resource - path = getattr(self.client, cmd_resource + "_path") - path_action = '%s/%s' % (myid, action) - cmd_parser = cmd.get_parser("update_" + cmd_resource) - resp = (MyResp(expected_code), retval) - - with mock.patch.object(cmd, "get_client", - return_value=self.client) as mock_get_client, \ - mock.patch.object(self.client.httpclient, "request", - return_value=resp) as mock_request: - shell.run_command(cmd, cmd_parser, args) - - self.assert_mock_multiple_calls_with_same_arguments( - mock_get_client, mock.call(), None) - mock_request.assert_called_once_with( - end_url(path % path_action), 'PUT', - body=MyComparator(body, self.client), - headers=ContainsKeyValue({'X-Auth-Token': TOKEN})) - _str = self.fake_stdout.make_string() - self.assertIn(myid, _str) - - def assert_mock_multiple_calls_with_same_arguments( - self, mocked_method, expected_call, count): - if count is None: - self.assertLessEqual(1, mocked_method.call_count) - else: - self.assertEqual(count, mocked_method.call_count) - mocked_method.assert_has_calls( - [expected_call] * mocked_method.call_count) - - -class TestListCommand(neutronV2_0.ListCommand): - resource = 'test_resource' - filter_attrs = [ - 'name', - 'admin_state_up', - {'name': 'foo', 'help': 'non-boolean attribute foo'}, - {'name': 'bar', 'help': 'boolean attribute bar', - 'boolean': True}, - {'name': 'baz', 'help': 'integer attribute baz', - 'argparse_kwargs': {'choices': ['baz1', 'baz2']}}, - ] - - -class ListCommandTestCase(CLITestV20Base): - - def setUp(self): - super(ListCommandTestCase, self).setUp() - self.client.extend_list('test_resources', '/test_resources', None) - setattr(self.client, 'test_resources_path', '/test_resources') - - def _test_list_resources_filter_params(self, base_args='', query=''): - resources = 'test_resources' - cmd = TestListCommand(MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - base_args=base_args.split(), - query=query) - - def _test_list_resources_with_arg_error(self, base_args=''): - resources = 'test_resources' - cmd = TestListCommand(MyApp(sys.stdout), None) - # argparse parse error leads to SystemExit - self.assertRaises(SystemExit, - self._test_list_resources, - resources, cmd, - base_args=base_args.split()) - - def test_list_resources_without_filter(self): - self._test_list_resources_filter_params() - - def test_list_resources_use_default_filter(self): - self._test_list_resources_filter_params( - base_args='--name val1 --admin-state-up False', - query='name=val1&admin_state_up=False') - - def test_list_resources_use_custom_filter(self): - self._test_list_resources_filter_params( - base_args='--foo FOO --bar True', - query='foo=FOO&bar=True') - - def test_list_resources_boolean_check_default_filter(self): - self._test_list_resources_filter_params( - base_args='--admin-state-up True', query='admin_state_up=True') - self._test_list_resources_filter_params( - base_args='--admin-state-up False', query='admin_state_up=False') - self._test_list_resources_with_arg_error( - base_args='--admin-state-up non-true-false') - - def test_list_resources_boolean_check_custom_filter(self): - self._test_list_resources_filter_params( - base_args='--bar True', query='bar=True') - self._test_list_resources_filter_params( - base_args='--bar False', query='bar=False') - self._test_list_resources_with_arg_error( - base_args='--bar non-true-false') - - def test_list_resources_argparse_kwargs(self): - self._test_list_resources_filter_params( - base_args='--baz baz1', query='baz=baz1') - self._test_list_resources_filter_params( - base_args='--baz baz2', query='baz=baz2') - self._test_list_resources_with_arg_error( - base_args='--bar non-choice') - - -class ClientV2TestJson(CLITestV20Base): - def test_do_request_unicode(self): - unicode_text = u'\u7f51\u7edc' - # url with unicode - action = u'/test' - expected_action = action - # query string with unicode - params = {'test': unicode_text} - expect_query = urlparse.urlencode(utils.safe_encode_dict(params)) - # request body with unicode - body = params - expect_body = self.client.serialize(body) - self.client.httpclient.auth_token = encodeutils.safe_encode( - unicode_text) - expected_auth_token = encodeutils.safe_encode(unicode_text) - resp_headers = {'x-openstack-request-id': REQUEST_ID} - resp = (MyResp(200, resp_headers), expect_body) - - with mock.patch.object(self.client.httpclient, "request", - return_value=resp) as mock_request: - result = self.client.do_request('PUT', action, body=body, - params=params) - - mock_request.assert_called_once_with( - end_url(expected_action, query=expect_query), - 'PUT', body=expect_body, - headers=ContainsKeyValue({'X-Auth-Token': expected_auth_token})) - # test response with unicode - self.assertEqual(body, result) - - def test_do_request_error_without_response_body(self): - params = {'test': 'value'} - expect_query = urlparse.urlencode(params) - self.client.httpclient.auth_token = 'token' - resp_headers = {'x-openstack-request-id': REQUEST_ID} - resp = (MyResp(400, headers=resp_headers, reason='An error'), '') - - with mock.patch.object(self.client.httpclient, "request", - return_value=resp) as mock_request: - error = self.assertRaises(exceptions.NeutronClientException, - self.client.do_request, 'PUT', '/test', - body='', params=params) - - mock_request.assert_called_once_with( - MyUrlComparator(end_url('/test', query=expect_query), self.client), - 'PUT', body='', - headers=ContainsKeyValue({'X-Auth-Token': 'token'})) - expected_error = "An error\nNeutron server returns " \ - "request_ids: %s" % [REQUEST_ID] - self.assertEqual(expected_error, str(error)) - - def test_do_request_with_long_uri_exception(self): - long_string = 'x' * 8200 # 8200 > MAX_URI_LEN:8192 - params = {'id': long_string} - exception = self.assertRaises(exceptions.RequestURITooLong, - self.client.do_request, - 'GET', '/test', body='', params=params) - self.assertNotEqual(0, exception.excess) - - def test_do_request_request_ids(self): - params = {'test': 'value'} - expect_query = urlparse.urlencode(params) - self.client.httpclient.auth_token = 'token' - body = params - expect_body = self.client.serialize(body) - resp_headers = {'x-openstack-request-id': REQUEST_ID} - resp = (MyResp(200, resp_headers), expect_body) - - with mock.patch.object(self.client.httpclient, "request", - return_value=resp) as mock_request: - result = self.client.do_request('PUT', '/test', body=body, - params=params) - - mock_request.assert_called_once_with( - MyUrlComparator(end_url('/test', query=expect_query), self.client), - 'PUT', body=expect_body, - headers=ContainsKeyValue({'X-Auth-Token': 'token'})) - - self.assertEqual(body, result) - self.assertEqual([REQUEST_ID], result.request_ids) - - def test_list_request_ids_with_retrieve_all_true(self): - path = '/test' - resources = 'tests' - fake_query = "marker=myid2&limit=2" - reses1 = {resources: [{'id': 'myid1', }, - {'id': 'myid2', }], - '%s_links' % resources: [{'href': end_url(path, fake_query), - 'rel': 'next'}]} - reses2 = {resources: [{'id': 'myid3', }, - {'id': 'myid4', }]} - resstr1 = self.client.serialize(reses1) - resstr2 = self.client.serialize(reses2) - resp_headers = {'x-openstack-request-id': REQUEST_ID} - resp = [(MyResp(200, resp_headers), resstr1), - (MyResp(200, resp_headers), resstr2)] - with mock.patch.object(self.client.httpclient, "request", - side_effect=resp) as mock_request: - result = self.client.list(resources, path) - - self.assertEqual(2, mock_request.call_count) - mock_request.assert_has_calls([ - mock.call( - end_url(path, ""), 'GET', - body=None, - headers=ContainsKeyValue({'X-Auth-Token': TOKEN})), - mock.call( - MyUrlComparator(end_url(path, fake_query), - self.client), 'GET', - body=None, - headers=ContainsKeyValue({'X-Auth-Token': TOKEN}))]) - - self.assertEqual([REQUEST_ID, REQUEST_ID], result.request_ids) - - def test_list_request_ids_with_retrieve_all_false(self): - path = '/test' - resources = 'tests' - fake_query = "marker=myid2&limit=2" - reses1 = {resources: [{'id': 'myid1', }, - {'id': 'myid2', }], - '%s_links' % resources: [{'href': end_url(path, fake_query), - 'rel': 'next'}]} - reses2 = {resources: [{'id': 'myid3', }, - {'id': 'myid4', }]} - resstr1 = self.client.serialize(reses1) - resstr2 = self.client.serialize(reses2) - resp_headers = {'x-openstack-request-id': REQUEST_ID} - resp = [(MyResp(200, resp_headers), resstr1), - (MyResp(200, resp_headers), resstr2)] - with mock.patch.object(self.client.httpclient, "request", - side_effect=resp) as mock_request: - result = self.client.list(resources, path, retrieve_all=False) - next(result) - self.assertEqual([REQUEST_ID], result.request_ids) - next(result) - self.assertEqual([REQUEST_ID, REQUEST_ID], result.request_ids) - - self.assertEqual(2, mock_request.call_count) - mock_request.assert_has_calls([ - mock.call( - end_url(path, ""), 'GET', - body=None, - headers=ContainsKeyValue({'X-Auth-Token': TOKEN})), - mock.call( - MyUrlComparator(end_url(path, fake_query), self.client), 'GET', - body=None, - headers=ContainsKeyValue({'X-Auth-Token': TOKEN}))]) - - def test_deserialize_without_data(self): - data = u'' - result = self.client.deserialize(data, 200) - self.assertEqual(data, result) - - def test_update_resource(self): - params = {'test': 'value'} - expect_query = urlparse.urlencode(params) - self.client.httpclient.auth_token = 'token' - body = params - expect_body = self.client.serialize(body) - resp_headers = {'x-openstack-request-id': REQUEST_ID} - resp = (MyResp(200, resp_headers), expect_body) - - with mock.patch.object(self.client.httpclient, "request", - return_value=resp) as mock_request: - result = self.client._update_resource('/test', body=body, - params=params) - - mock_request.assert_called_once_with( - MyUrlComparator(end_url('/test', query=expect_query), self.client), - 'PUT', body=expect_body, - headers=ContainsKeyValue({'X-Auth-Token': 'token'})) - self.assertNotIn('If-Match', mock_request.call_args[1]['headers']) - - self.assertEqual(body, result) - self.assertEqual([REQUEST_ID], result.request_ids) - - def test_update_resource_with_revision_number(self): - params = {'test': 'value'} - expect_query = urlparse.urlencode(params) - self.client.httpclient.auth_token = 'token' - body = params - expect_body = self.client.serialize(body) - resp_headers = {'x-openstack-request-id': REQUEST_ID} - resp = (MyResp(200, resp_headers), expect_body) - - with mock.patch.object(self.client.httpclient, "request", - return_value=resp) as mock_request: - result = self.client._update_resource('/test', body=body, - params=params, - revision_number=1) - - mock_request.assert_called_once_with( - MyUrlComparator(end_url('/test', query=expect_query), self.client), - 'PUT', body=expect_body, - headers=ContainsKeyValue( - {'X-Auth-Token': 'token', 'If-Match': 'revision_number=1'})) - - self.assertEqual(body, result) - self.assertEqual([REQUEST_ID], result.request_ids) - - -class CLITestV20ExceptionHandler(CLITestV20Base): - - def _test_exception_handler_v20( - self, expected_exception, status_code, expected_msg, - error_type=None, error_msg=None, error_detail=None, - request_id=None, error_content=None): - - resp = MyResp(status_code, {'x-openstack-request-id': request_id}) - if request_id is not None: - expected_msg += "\nNeutron server returns " \ - "request_ids: %s" % [request_id] - if error_content is None: - error_content = {'NeutronError': {'type': error_type, - 'message': error_msg, - 'detail': error_detail}} - expected_content = self.client._convert_into_with_meta(error_content, - resp) - - e = self.assertRaises(expected_exception, - client.exception_handler_v20, - status_code, expected_content) - self.assertEqual(status_code, e.status_code) - self.assertEqual(expected_exception.__name__, - e.__class__.__name__) - - if expected_msg is None: - if error_detail: - expected_msg = '\n'.join([error_msg, error_detail]) - else: - expected_msg = error_msg - self.assertEqual(expected_msg, e.message) - - def test_exception_handler_v20_ip_address_in_use(self): - err_msg = ('Unable to complete operation for network ' - 'fake-network-uuid. The IP address fake-ip is in use.') - self._test_exception_handler_v20( - exceptions.IpAddressInUseClient, 409, err_msg, - 'IpAddressInUse', err_msg, '', REQUEST_ID) - - def test_exception_handler_v20_neutron_known_error(self): - known_error_map = [ - ('NetworkNotFound', exceptions.NetworkNotFoundClient, 404), - ('PortNotFound', exceptions.PortNotFoundClient, 404), - ('NetworkInUse', exceptions.NetworkInUseClient, 409), - ('PortInUse', exceptions.PortInUseClient, 409), - ('StateInvalid', exceptions.StateInvalidClient, 400), - ('IpAddressInUse', exceptions.IpAddressInUseClient, 409), - ('IpAddressGenerationFailure', - exceptions.IpAddressGenerationFailureClient, 409), - ('MacAddressInUse', exceptions.MacAddressInUseClient, 409), - ('ExternalIpAddressExhausted', - exceptions.ExternalIpAddressExhaustedClient, 400), - ('OverQuota', exceptions.OverQuotaClient, 409), - ('InvalidIpForNetwork', exceptions.InvalidIpForNetworkClient, 400), - ('InvalidIpForSubnet', exceptions.InvalidIpForSubnetClient, 400), - ('IpAddressAlreadyAllocated', - exceptions.IpAddressAlreadyAllocatedClient, 400), - ] - - error_msg = 'dummy exception message' - error_detail = 'sample detail' - for server_exc, client_exc, status_code in known_error_map: - self._test_exception_handler_v20( - client_exc, status_code, - error_msg + '\n' + error_detail, - server_exc, error_msg, error_detail, REQUEST_ID) - - def test_exception_handler_v20_neutron_known_error_without_detail(self): - error_msg = 'Network not found' - error_detail = '' - self._test_exception_handler_v20( - exceptions.NetworkNotFoundClient, 404, - error_msg, - 'NetworkNotFound', error_msg, error_detail, REQUEST_ID) - - def test_exception_handler_v20_unknown_error_to_per_code_exception(self): - for status_code, client_exc in exceptions.HTTP_EXCEPTION_MAP.items(): - error_msg = 'Unknown error' - error_detail = 'This is detail' - self._test_exception_handler_v20( - client_exc, status_code, - error_msg + '\n' + error_detail, - 'UnknownError', error_msg, error_detail, [REQUEST_ID]) - - def test_exception_handler_v20_neutron_unknown_status_code(self): - error_msg = 'Unknown error' - error_detail = 'This is detail' - self._test_exception_handler_v20( - exceptions.NeutronClientException, 501, - error_msg + '\n' + error_detail, - 'UnknownError', error_msg, error_detail, REQUEST_ID) - - def test_exception_handler_v20_bad_neutron_error(self): - for status_code, client_exc in exceptions.HTTP_EXCEPTION_MAP.items(): - error_content = {'NeutronError': {'unknown_key': 'UNKNOWN'}} - self._test_exception_handler_v20( - client_exc, status_code, - expected_msg="{'unknown_key': 'UNKNOWN'}", - error_content=error_content, - request_id=REQUEST_ID) - - def test_exception_handler_v20_error_dict_contains_message(self): - error_content = {'message': 'This is an error message'} - for status_code, client_exc in exceptions.HTTP_EXCEPTION_MAP.items(): - self._test_exception_handler_v20( - client_exc, status_code, - expected_msg='This is an error message', - error_content=error_content, - request_id=REQUEST_ID) - - def test_exception_handler_v20_error_dict_not_contain_message(self): - # 599 is not contained in HTTP_EXCEPTION_MAP. - error_content = {'error': 'This is an error message'} - expected_msg = '%s-%s' % (599, error_content) - self._test_exception_handler_v20( - exceptions.NeutronClientException, 599, - expected_msg=expected_msg, - request_id=None, - error_content=error_content) - - def test_exception_handler_v20_default_fallback(self): - # 599 is not contained in HTTP_EXCEPTION_MAP. - error_content = 'This is an error message' - expected_msg = '%s-%s' % (599, error_content) - self._test_exception_handler_v20( - exceptions.NeutronClientException, 599, - expected_msg=expected_msg, - request_id=None, - error_content=error_content) - - def test_exception_status(self): - e = exceptions.BadRequest() - self.assertEqual(400, e.status_code) - - e = exceptions.BadRequest(status_code=499) - self.assertEqual(499, e.status_code) - - # SslCertificateValidationError has no explicit status_code, - # but should have a 'safe' defined fallback. - e = exceptions.SslCertificateValidationError() - self.assertIsNotNone(e.status_code) - - e = exceptions.SslCertificateValidationError(status_code=599) - self.assertEqual(599, e.status_code) - - def test_connection_failed(self): - self.client.httpclient.auth_token = 'token' - excp = requests.exceptions.ConnectionError('Connection refused') - - with mock.patch.object(self.client.httpclient, "request", - side_effect=excp) as mock_request: - error = self.assertRaises(exceptions.ConnectionFailed, - self.client.get, '/test') - - mock_request.assert_called_once_with( - end_url('/test'), 'GET', - body=None, - headers=ContainsKeyValue({'X-Auth-Token': 'token'})) - # NB: ConnectionFailed has no explicit status_code, so this - # tests that there is a fallback defined. - self.assertIsNotNone(error.status_code) - - -class DictWithMetaTest(base.BaseTestCase): - - def test_dict_with_meta(self): - body = {'test': 'value'} - resp = MyResp(200, {'x-openstack-request-id': REQUEST_ID}) - obj = client._DictWithMeta(body, resp) - self.assertEqual(body, obj) - - # Check request_ids attribute is added to obj - self.assertTrue(hasattr(obj, 'request_ids')) - self.assertEqual([REQUEST_ID], obj.request_ids) - - -class TupleWithMetaTest(base.BaseTestCase): - - def test_tuple_with_meta(self): - body = ('test', 'value') - resp = MyResp(200, {'x-openstack-request-id': REQUEST_ID}) - obj = client._TupleWithMeta(body, resp) - self.assertEqual(body, obj) - - # Check request_ids attribute is added to obj - self.assertTrue(hasattr(obj, 'request_ids')) - self.assertEqual([REQUEST_ID], obj.request_ids) - - -class StrWithMetaTest(base.BaseTestCase): - - def test_str_with_meta(self): - body = "test_string" - resp = MyResp(200, {'x-openstack-request-id': REQUEST_ID}) - obj = client._StrWithMeta(body, resp) - self.assertEqual(body, obj) - - # Check request_ids attribute is added to obj - self.assertTrue(hasattr(obj, 'request_ids')) - self.assertEqual([REQUEST_ID], obj.request_ids) - - -class GeneratorWithMetaTest(base.BaseTestCase): - - body = {'test': 'value'} - resp = MyResp(200, {'x-openstack-request-id': REQUEST_ID}) - - def _pagination(self, collection, path, **params): - obj = client._DictWithMeta(self.body, self.resp) - yield obj - - def test_generator(self): - obj = client._GeneratorWithMeta(self._pagination, 'test_collection', - 'test_path', test_args='test_args') - self.assertEqual(self.body, next(obj)) - - self.assertTrue(hasattr(obj, 'request_ids')) - self.assertEqual([REQUEST_ID], obj.request_ids) - - -class CLITestV20OutputFormatter(CLITestV20Base): - - def _test_create_resource_with_formatter(self, fmt): - resource = 'network' - cmd = network.CreateNetwork(MyApp(sys.stdout), None) - args = ['-f', fmt, 'myname'] - position_names = ['name'] - position_values = ['myname'] - self._test_create_resource(resource, cmd, 'myname', 'myid', args, - position_names, position_values) - - def test_create_resource_table(self): - self._test_create_resource_with_formatter('table') - print(self.fake_stdout.content) - # table data is contains in the third element. - data = self.fake_stdout.content[2].split('\n') - self.assertTrue(any(' id ' in d for d in data)) - self.assertTrue(any(' name ' in d for d in data)) - - def test_create_resource_json(self): - self._test_create_resource_with_formatter('json') - data = jsonutils.loads(self.fake_stdout.make_string()) - self.assertEqual('myname', data['name']) - self.assertEqual('myid', data['id']) - - def test_create_resource_yaml(self): - self._test_create_resource_with_formatter('yaml') - data = yaml.safe_load(self.fake_stdout.make_string()) - self.assertEqual('myname', data['name']) - self.assertEqual('myid', data['id']) - - def _test_show_resource_with_formatter(self, fmt): - resource = 'network' - cmd = network.ShowNetwork(MyApp(sys.stdout), None) - args = ['-f', fmt, '-F', 'id', '-F', 'name', 'myid'] - self._test_show_resource(resource, cmd, 'myid', - args, ['id', 'name']) - - def test_show_resource_table(self): - self._test_show_resource_with_formatter('table') - data = self.fake_stdout.content[0].split('\n') - self.assertTrue(any(' id ' in d for d in data)) - self.assertTrue(any(' name ' in d for d in data)) - - def test_show_resource_json(self): - self._test_show_resource_with_formatter('json') - data = jsonutils.loads(''.join(self.fake_stdout.content)) - self.assertEqual('myname', data['name']) - self.assertEqual('myid', data['id']) - - def test_show_resource_yaml(self): - self._test_show_resource_with_formatter('yaml') - data = yaml.safe_load(''.join(self.fake_stdout.content)) - self.assertEqual('myname', data['name']) - self.assertEqual('myid', data['id']) - - @mock.patch.object(network.ListNetwork, "extend_list") - def _test_list_resources_with_formatter(self, fmt, mock_extend_list): - resources = 'networks' - cmd = network.ListNetwork(MyApp(sys.stdout), None) - # ListNetwork has its own extend_list, so we need to stub out it - # to avoid an extra API call. - self._test_list_resources(resources, cmd, output_format=fmt) - mock_extend_list.assert_called_once_with(IsA(list), mock.ANY) - - def test_list_resources_table(self): - self._test_list_resources_with_formatter('table') - data = self.fake_stdout.content[0].split('\n') - self.assertTrue(any(' id ' in d for d in data)) - self.assertTrue(any(' myid1 ' in d for d in data)) - self.assertTrue(any(' myid2 ' in d for d in data)) - - def test_list_resources_json(self): - self._test_list_resources_with_formatter('json') - data = jsonutils.loads(''.join(self.fake_stdout.content)) - self.assertEqual(['myid1', 'myid2'], [d['id'] for d in data]) - - def test_list_resources_yaml(self): - self._test_list_resources_with_formatter('yaml') - data = yaml.safe_load(''.join(self.fake_stdout.content)) - self.assertEqual(['myid1', 'myid2'], [d['id'] for d in data]) diff --git a/neutronclient/tests/unit/test_cli20_address_scope.py b/neutronclient/tests/unit/test_cli20_address_scope.py deleted file mode 100644 index 902f7e653..000000000 --- a/neutronclient/tests/unit/test_cli20_address_scope.py +++ /dev/null @@ -1,175 +0,0 @@ -# Copyright 2015 Huawei Technologies India Pvt. Ltd. -# 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 sys -from unittest import mock - - -from neutronclient.common import exceptions -from neutronclient.neutron.v2_0 import address_scope -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20AddressScopeJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['address_scope'] - - def setUp(self): - super(CLITestV20AddressScopeJSON, self).setUp(plurals={'tags': 'tag'}) - - def test_create_address_scope_with_minimum_option_ipv4(self): - """Create address_scope: foo-address-scope with minimum option.""" - resource = 'address_scope' - cmd = address_scope.CreateAddressScope( - test_cli20.MyApp(sys.stdout), None) - name = 'foo-address-scope' - myid = 'myid' - args = [name, '4'] - position_names = ['name', 'ip_version'] - position_values = [name, 4] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_address_scope_with_minimum_option_ipv6(self): - """Create address_scope: foo-address-scope with minimum option.""" - resource = 'address_scope' - cmd = address_scope.CreateAddressScope( - test_cli20.MyApp(sys.stdout), None) - name = 'foo-address-scope' - myid = 'myid' - args = [name, '6'] - position_names = ['name', 'ip_version'] - position_values = [name, 6] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_address_scope_with_minimum_option_bad_ip_version(self): - """Create address_scope: foo-address-scope with minimum option.""" - resource = 'address_scope' - cmd = address_scope.CreateAddressScope( - test_cli20.MyApp(sys.stdout), None) - name = 'foo-address-scope' - myid = 'myid' - args = [name, '5'] - position_names = ['name', 'ip_version'] - position_values = [name, 5] - self.assertRaises(SystemExit, self._test_create_resource, - resource, cmd, name, myid, args, position_names, - position_values) - - def test_create_address_scope_with_all_option(self): - # Create address_scope: foo-address-scope with all options. - resource = 'address_scope' - cmd = address_scope.CreateAddressScope( - test_cli20.MyApp(sys.stdout), None) - name = 'foo-address-scope' - myid = 'myid' - args = [name, '4', '--shared'] - position_names = ['name', 'ip_version', 'shared'] - position_values = [name, 4, True] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_address_scope_with_unicode(self): - # Create address_scope: u'\u7f51\u7edc'. - resource = 'address_scope' - cmd = address_scope.CreateAddressScope( - test_cli20.MyApp(sys.stdout), None) - name = u'\u7f51\u7edc' - ip_version = u'4' - myid = 'myid' - args = [name, ip_version] - position_names = ['name', 'ip_version'] - position_values = [name, 4] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_update_address_scope_exception(self): - # Update address_scope (Negative) : myid. - resource = 'address_scope' - cmd = address_scope.UpdateAddressScope( - test_cli20.MyApp(sys.stdout), None) - self.assertRaises(exceptions.CommandError, self._test_update_resource, - resource, cmd, 'myid', ['myid'], {}) - - def test_update_address_scope(self): - # Update address_scope: myid --name newname-address-scope. - resource = 'address_scope' - cmd = address_scope.UpdateAddressScope( - test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'newname-address-scope'], - {'name': 'newname-address-scope'} - ) - # Update address_scope: myid --shared - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--shared', 'True'], - {'shared': "True"} - ) - - def test_list_address_scope(self): - # address_scope-list. - resources = "address_scopes" - cmd = address_scope.ListAddressScope(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, True) - - @mock.patch.object(address_scope.ListAddressScope, "extend_list") - def test_list_address_scope_pagination(self, mock_extend_list): - # address_scope-list. - cmd = address_scope.ListAddressScope(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources_with_pagination("address_scopes", - cmd) - mock_extend_list.assert_called_once_with(test_cli20.IsA(list), - mock.ANY) - - def test_list_address_scope_sort(self): - # sorted list: - # address_scope-list --sort-key name --sort-key id --sort-key asc - # --sort-key desc - resources = "address_scopes" - cmd = address_scope.ListAddressScope(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id", "ip_version"], - sort_dir=["asc", "desc"]) - - def test_list_address_scope_limit(self): - # size (1000) limited list: address_scope-list -P. - resources = "address_scopes" - cmd = address_scope.ListAddressScope(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_address_scope(self): - # Show address_scope: --fields id --fields name myid. - resource = 'address_scope' - cmd = address_scope.ShowAddressScope( - test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id, - '--fields', 'ip_version', '6'] - self._test_show_resource(resource, cmd, self.test_id, args, - ['id', 'name', 'ip_version']) - - def test_delete_address_scope(self): - # Delete address_scope: address_scope_id. - resource = 'address_scope' - cmd = address_scope.DeleteAddressScope( - test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) diff --git a/neutronclient/tests/unit/test_cli20_agents.py b/neutronclient/tests/unit/test_cli20_agents.py deleted file mode 100644 index 7641d2741..000000000 --- a/neutronclient/tests/unit/test_cli20_agents.py +++ /dev/null @@ -1,97 +0,0 @@ -# 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 sys - -from oslo_serialization import jsonutils - -from neutronclient.neutron.v2_0 import agent -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20Agent(test_cli20.CLITestV20Base): - def test_list_agents(self): - contents = {'agents': [{'id': 'myname', 'agent_type': 'mytype', - 'alive': True}]} - args = ['-f', 'json'] - resources = "agents" - - cmd = agent.ListAgent(test_cli20.MyApp(sys.stdout), None) - self._test_list_columns(cmd, resources, contents, args) - _str = self.fake_stdout.make_string() - - returned_agents = jsonutils.loads(_str) - self.assertEqual(1, len(returned_agents)) - ag = returned_agents[0] - self.assertEqual(3, len(ag)) - self.assertIn("alive", ag.keys()) - - def test_list_agents_field(self): - contents = {'agents': [{'alive': True}]} - args = ['-f', 'json'] - resources = "agents" - smile = ':-)' - - cmd = agent.ListAgent(test_cli20.MyApp(sys.stdout), None) - self._test_list_columns(cmd, resources, contents, args) - _str = self.fake_stdout.make_string() - - returned_agents = jsonutils.loads(_str) - self.assertEqual(1, len(returned_agents)) - ag = returned_agents[0] - self.assertEqual(1, len(ag)) - self.assertIn("alive", ag.keys()) - self.assertIn(smile, ag.values()) - - def test_list_agents_zone_field(self): - contents = {'agents': [{'availability_zone': 'myzone'}]} - args = ['-f', 'json'] - resources = "agents" - - cmd = agent.ListAgent(test_cli20.MyApp(sys.stdout), None) - self._test_list_columns(cmd, resources, contents, args) - _str = self.fake_stdout.make_string() - - returned_agents = jsonutils.loads(_str) - self.assertEqual(1, len(returned_agents)) - ag = returned_agents[0] - self.assertEqual(1, len(ag)) - self.assertIn("availability_zone", ag.keys()) - self.assertIn('myzone', ag.values()) - - def test_update_agent(self): - # agent-update myid --admin-state-down --description mydescr. - resource = 'agent' - cmd = agent.UpdateAgent(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource( - resource, cmd, 'myid', - ['myid', '--admin-state-down', '--description', 'mydescr'], - {'description': 'mydescr', 'admin_state_up': False} - ) - - def test_show_agent(self): - # Show agent: --field id --field binary myid. - resource = 'agent' - cmd = agent.ShowAgent(test_cli20.MyApp(sys.stdout), None) - args = ['--field', 'id', '--field', 'binary', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'binary']) - - def test_delete_agent(self): - # Delete agent: myid. - resource = 'agent' - cmd = agent.DeleteAgent(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) diff --git a/neutronclient/tests/unit/test_cli20_agentschedulers.py b/neutronclient/tests/unit/test_cli20_agentschedulers.py deleted file mode 100644 index 817909d38..000000000 --- a/neutronclient/tests/unit/test_cli20_agentschedulers.py +++ /dev/null @@ -1,209 +0,0 @@ -# Copyright 2013 Mirantis 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 sys -from unittest import mock - - -from neutronclient.neutron.v2_0 import agentscheduler -from neutronclient.neutron.v2_0 import network -from neutronclient.tests.unit import test_cli20 - - -AGENT_ID = 'agent_id1' -NETWORK_ID = 'net_id1' -ROUTER_ID = 'router_id1' - - -class CLITestV20AgentScheduler(test_cli20.CLITestV20Base): - def _test_add_to_agent(self, resource, cmd, cmd_args, destination, - body, result): - path = ((self.client.agent_path + destination) % - cmd_args[0]) - - result_str = self.client.serialize(result) - return_tup = (test_cli20.MyResp(200), result_str) - - cmd_parser = cmd.get_parser('test_' + resource) - parsed_args = cmd_parser.parse_args(cmd_args) - - with mock.patch.object(cmd, "get_client", - return_value=self.client) as mock_get_client, \ - mock.patch.object(self.client.httpclient, "request", - return_value=return_tup) as mock_request: - cmd.run(parsed_args) - mock_get_client.assert_called_once_with() - mock_request.assert_called_once_with( - test_cli20.end_url(path), 'POST', - body=test_cli20.MyComparator(body, self.client), - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN})) - - def _test_remove_from_agent(self, resource, cmd, cmd_args, destination): - path = ((self.client.agent_path + destination + '/%s') % - cmd_args) - - return_tup = (test_cli20.MyResp(204), None) - cmd_parser = cmd.get_parser('test_' + resource) - parsed_args = cmd_parser.parse_args(cmd_args) - - with mock.patch.object(cmd, "get_client", - return_value=self.client) as mock_get_client, \ - mock.patch.object(self.client.httpclient, "request", - return_value=return_tup) as mock_request: - cmd.run(parsed_args) - mock_get_client.assert_called_once_with() - mock_request.assert_called_once_with( - test_cli20.end_url(path), 'DELETE', - body=None, - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN})) - - -class CLITestV20DHCPAgentScheduler(CLITestV20AgentScheduler): - - def test_add_network_to_agent(self): - resource = 'agent' - cmd = agentscheduler.AddNetworkToDhcpAgent( - test_cli20.MyApp(sys.stdout), None) - args = (AGENT_ID, NETWORK_ID) - body = {'network_id': NETWORK_ID} - result = {'network_id': 'net_id', } - self._test_add_to_agent(resource, cmd, args, self.client.DHCP_NETS, - body, result) - - def test_remove_network_from_agent(self): - resource = 'agent' - cmd = agentscheduler.RemoveNetworkFromDhcpAgent( - test_cli20.MyApp(sys.stdout), None) - args = (AGENT_ID, NETWORK_ID) - self._test_remove_from_agent(resource, cmd, args, - self.client.DHCP_NETS) - - @mock.patch.object(network.ListNetwork, "extend_list") - def test_list_networks_on_agent(self, mock_extend_list): - resources = 'networks' - cmd = agentscheduler.ListNetworksOnDhcpAgent( - test_cli20.MyApp(sys.stdout), None) - agent_id = 'agent_id1' - path = ((self.client.agent_path + self.client.DHCP_NETS) % - agent_id) - self._test_list_resources(resources, cmd, base_args=[agent_id], - path=path) - mock_extend_list.assert_called_once_with(test_cli20.IsA(list), - mock.ANY) - - def test_list_agents_hosting_network(self): - resources = 'agent' - cmd = agentscheduler.ListDhcpAgentsHostingNetwork( - test_cli20.MyApp(sys.stdout), None) - agent_id = 'agent_id1' - path = ((self.client.network_path + self.client.DHCP_AGENTS) % - agent_id) - contents = {self.id_field: 'myid1', 'alive': True} - self._test_list_resources(resources, cmd, base_args=[agent_id], - path=path, response_contents=contents) - - -class CLITestV20L3AgentScheduler(CLITestV20AgentScheduler): - - def test_add_router_to_agent(self): - resource = 'agent' - cmd = agentscheduler.AddRouterToL3Agent( - test_cli20.MyApp(sys.stdout), None) - args = (AGENT_ID, ROUTER_ID) - body = {'router_id': ROUTER_ID} - result = {'network_id': 'net_id', } - self._test_add_to_agent(resource, cmd, args, self.client.L3_ROUTERS, - body, result) - - def test_remove_router_from_agent(self): - resource = 'agent' - cmd = agentscheduler.RemoveRouterFromL3Agent( - test_cli20.MyApp(sys.stdout), None) - args = (AGENT_ID, ROUTER_ID) - self._test_remove_from_agent(resource, cmd, args, - self.client.L3_ROUTERS) - - def test_list_routers_on_agent(self): - resources = 'router' - cmd = agentscheduler.ListRoutersOnL3Agent( - test_cli20.MyApp(sys.stdout), None) - agent_id = 'agent_id1' - path = ((self.client.agent_path + self.client.L3_ROUTERS) % - agent_id) - contents = {self.id_field: 'myid1', 'name': 'my_name'} - self._test_list_resources(resources, cmd, base_args=[agent_id], - path=path, response_contents=contents) - - def test_list_agents_hosting_router(self): - resources = 'agent' - cmd = agentscheduler.ListL3AgentsHostingRouter( - test_cli20.MyApp(sys.stdout), None) - agent_id = 'agent_id1' - path = ((self.client.router_path + self.client.L3_AGENTS) % - agent_id) - contents = {self.id_field: 'myid1', 'alive': True} - self._test_list_resources(resources, cmd, base_args=[agent_id], - path=path, response_contents=contents) - - -class CLITestV20LBaaSAgentScheduler(test_cli20.CLITestV20Base): - - def test_list_pools_on_agent(self): - resources = 'pools' - cmd = agentscheduler.ListPoolsOnLbaasAgent( - test_cli20.MyApp(sys.stdout), None) - agent_id = 'agent_id1' - path = ((self.client.agent_path + self.client.LOADBALANCER_POOLS) % - agent_id) - self._test_list_resources(resources, cmd, base_args=[agent_id], - path=path) - - def test_get_lbaas_agent_hosting_pool(self): - resources = 'agent' - cmd = agentscheduler.GetLbaasAgentHostingPool( - test_cli20.MyApp(sys.stdout), None) - pool_id = 'pool_id1' - path = ((self.client.pool_path + self.client.LOADBALANCER_AGENT) % - pool_id) - contents = {self.id_field: 'myid1', 'alive': True} - self._test_list_resources(resources, cmd, base_args=[pool_id], - path=path, response_contents=contents) - - -class CLITestV20LBaaSV2AgentScheduler(test_cli20.CLITestV20Base): - - def test_list_loadbalancers_on_agent(self): - resources = 'loadbalancers' - cmd = agentscheduler.ListLoadBalancersOnLbaasAgent( - test_cli20.MyApp(sys.stdout), None) - agent_id = 'agent_id1' - path = ((self.client.agent_path + self.client.AGENT_LOADBALANCERS) % - agent_id) - self._test_list_resources(resources, cmd, base_args=[agent_id], - path=path) - - def test_get_lbaas_agent_hosting_pool(self): - resources = 'agent' - cmd = agentscheduler.GetLbaasAgentHostingLoadBalancer( - test_cli20.MyApp(sys.stdout), None) - lb_id = 'lb_id1' - path = ((self.client.lbaas_loadbalancer_path + - self.client.LOADBALANCER_HOSTING_AGENT) % lb_id) - contents = {self.id_field: 'myid1', 'alive': True} - self._test_list_resources(resources, cmd, base_args=[lb_id], - path=path, response_contents=contents) diff --git a/neutronclient/tests/unit/test_cli20_az.py b/neutronclient/tests/unit/test_cli20_az.py deleted file mode 100644 index cb127c529..000000000 --- a/neutronclient/tests/unit/test_cli20_az.py +++ /dev/null @@ -1,31 +0,0 @@ -# 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 sys - -from neutronclient.neutron.v2_0 import availability_zone as az -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20Agent(test_cli20.CLITestV20Base): - def test_list_agents(self): - contents = {'availability_zones': [{'name': 'zone1', - 'resource': 'network', - 'state': 'available'}, - {'name': 'zone2', - 'resource': 'router', - 'state': 'unavailable'}]} - args = ['-f', 'json'] - resources = "availability_zones" - - cmd = az.ListAvailabilityZone(test_cli20.MyApp(sys.stdout), None) - self._test_list_columns(cmd, resources, contents, args) diff --git a/neutronclient/tests/unit/test_cli20_extensions.py b/neutronclient/tests/unit/test_cli20_extensions.py deleted file mode 100644 index cc2913747..000000000 --- a/neutronclient/tests/unit/test_cli20_extensions.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright 2013 NEC Corporation -# 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 sys - -from neutronclient.neutron.v2_0 import extension -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20Extension(test_cli20.CLITestV20Base): - id_field = 'alias' - - def test_list_extensions(self): - resources = 'extensions' - cmd = extension.ListExt(test_cli20.MyApp(sys.stdout), None) - contents = [{'alias': 'ext1', 'name': 'name1', 'other': 'other1'}, - {'alias': 'ext2', 'name': 'name2', 'other': 'other2'}] - ret = self._test_list_resources(resources, cmd, - response_contents=contents) - ret_words = set(ret.split()) - # Check only the default columns are shown. - self.assertIn('name', ret_words) - self.assertIn('alias', ret_words) - self.assertNotIn('other', ret_words) - - def test_show_extension(self): - # -F option does not work for ext-show at the moment, so -F option - # is not passed in the commandline args as other tests do. - resource = 'extension' - cmd = extension.ShowExt(test_cli20.MyApp(sys.stdout), None) - args = [self.test_id] - ext_alias = self.test_id - self._test_show_resource(resource, cmd, ext_alias, args, fields=[]) diff --git a/neutronclient/tests/unit/test_cli20_floatingips.py b/neutronclient/tests/unit/test_cli20_floatingips.py deleted file mode 100644 index d77bda6e8..000000000 --- a/neutronclient/tests/unit/test_cli20_floatingips.py +++ /dev/null @@ -1,193 +0,0 @@ -# Copyright 2012 Red Hat -# 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 sys - -from neutronclient.neutron.v2_0 import floatingip as fip -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20FloatingIpsJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['floatingip'] - - def test_create_floatingip(self): - # Create floatingip: fip1. - resource = 'floatingip' - cmd = fip.CreateFloatingIP(test_cli20.MyApp(sys.stdout), None) - name = 'fip1' - myid = 'myid' - args = [name, '--description', 'floats like a butterfly'] - position_names = ['floating_network_id'] - position_values = [name] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - description='floats like a butterfly') - - def test_create_floatingip_and_port(self): - # Create floatingip: fip1. - resource = 'floatingip' - cmd = fip.CreateFloatingIP(test_cli20.MyApp(sys.stdout), None) - name = 'fip1' - myid = 'myid' - pid = 'mypid' - args = [name, '--port_id', pid] - position_names = ['floating_network_id', 'port_id'] - position_values = [name, pid] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - # Test dashed options - args = [name, '--port-id', pid] - position_names = ['floating_network_id', 'port_id'] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_floatingip_and_port_and_address(self): - # Create floatingip: fip1 with a given port and address. - resource = 'floatingip' - cmd = fip.CreateFloatingIP(test_cli20.MyApp(sys.stdout), None) - name = 'fip1' - myid = 'myid' - pid = 'mypid' - addr = '10.0.0.99' - args = [name, '--port_id', pid, '--fixed_ip_address', addr] - position_names = ['floating_network_id', 'port_id', 'fixed_ip_address'] - position_values = [name, pid, addr] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - # Test dashed options - args = [name, '--port-id', pid, '--fixed-ip-address', addr] - position_names = ['floating_network_id', 'port_id', 'fixed_ip_address'] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_floatingip_with_ip_address_of_floating_ip(self): - # Create floatingip: fip1 with a given IP address of floating IP. - resource = 'floatingip' - cmd = fip.CreateFloatingIP(test_cli20.MyApp(sys.stdout), None) - name = 'fip1' - myid = 'myid' - addr = '10.0.0.99' - - args = [name, '--floating-ip-address', addr] - position_values = [name, addr] - position_names = ['floating_network_id', 'floating_ip_address'] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_floatingip_with_subnet_id(self): - # Create floatingip: fip1 on a given subnet id. - resource = 'floatingip' - cmd = fip.CreateFloatingIP(test_cli20.MyApp(sys.stdout), None) - name = 'fip1' - myid = 'myid' - subnet_id = 'mysubnetid' - - args = [name, '--subnet', subnet_id] - position_values = [name, subnet_id] - position_names = ['floating_network_id', 'subnet_id'] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_floatingip_with_subnet_id_and_port(self): - # Create floatingip: fip1 on a given subnet id and port. - resource = 'floatingip' - cmd = fip.CreateFloatingIP(test_cli20.MyApp(sys.stdout), None) - name = 'fip1' - myid = 'myid' - pid = 'mypid' - subnet_id = 'mysubnetid' - - args = [name, '--subnet', subnet_id, '--port-id', pid] - position_values = [name, subnet_id, pid] - position_names = ['floating_network_id', 'subnet_id', 'port_id'] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_floatingip_with_dns_name_and_dns_domain(self): - # Create floatingip: fip1 with dns name and dns domain. - resource = 'floatingip' - cmd = fip.CreateFloatingIP(test_cli20.MyApp(sys.stdout), None) - name = 'fip1' - myid = 'myid' - dns_name_name = 'my-floatingip' - dns_domain_name = 'my-domain.org.' - args = [name, '--dns-name', dns_name_name, '--dns-domain', - dns_domain_name] - position_names = ['floating_network_id', 'dns_name', 'dns_domain'] - position_values = [name, dns_name_name, dns_domain_name] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_list_floatingips(self): - # list floatingips: -D. - resources = 'floatingips' - cmd = fip.ListFloatingIP(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_floatingips_pagination(self): - resources = 'floatingips' - cmd = fip.ListFloatingIP(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_floatingips_sort(self): - # list floatingips: - # --sort-key name --sort-key id --sort-key asc --sort-key desc - resources = 'floatingips' - cmd = fip.ListFloatingIP(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_floatingips_limit(self): - # list floatingips: -P. - resources = 'floatingips' - cmd = fip.ListFloatingIP(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_delete_floatingip(self): - # Delete floatingip: fip1. - resource = 'floatingip' - cmd = fip.DeleteFloatingIP(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) - - def test_show_floatingip(self): - # Show floatingip: --fields id. - resource = 'floatingip' - cmd = fip.ShowFloatingIP(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id']) - - def test_disassociate_ip(self): - # Disassociate floating IP: myid. - resource = 'floatingip' - cmd = fip.DisassociateFloatingIP(test_cli20.MyApp(sys.stdout), None) - args = ['myid'] - self._test_update_resource(resource, cmd, 'myid', - args, {"port_id": None} - ) - - def test_associate_ip(self): - # Associate floating IP: myid portid. - resource = 'floatingip' - cmd = fip.AssociateFloatingIP(test_cli20.MyApp(sys.stdout), None) - args = ['myid', 'portid'] - self._test_update_resource(resource, cmd, 'myid', - args, {"port_id": "portid"} - ) diff --git a/neutronclient/tests/unit/test_cli20_metering.py b/neutronclient/tests/unit/test_cli20_metering.py deleted file mode 100644 index 9ecf04734..000000000 --- a/neutronclient/tests/unit/test_cli20_metering.py +++ /dev/null @@ -1,99 +0,0 @@ -# Copyright (C) 2013 eNovance SAS -# -# 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 sys - -from neutronclient.neutron.v2_0 import metering -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20MeteringJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['metering_label', 'metering_label_rule'] - - def test_create_metering_label(self): - # Create a metering label. - resource = 'metering_label' - cmd = metering.CreateMeteringLabel( - test_cli20.MyApp(sys.stdout), None) - name = 'my label' - myid = 'myid' - description = 'my description' - args = [name, '--description', description, '--shared'] - position_names = ['name', 'description', 'shared'] - position_values = [name, description, True] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_list_metering_labels(self): - resources = "metering_labels" - cmd = metering.ListMeteringLabel( - test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd) - - def test_delete_metering_label(self): - # Delete a metering label. - resource = 'metering_label' - cmd = metering.DeleteMeteringLabel( - test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) - - def test_show_metering_label(self): - resource = 'metering_label' - cmd = metering.ShowMeteringLabel( - test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id']) - - def test_create_metering_label_rule(self): - resource = 'metering_label_rule' - cmd = metering.CreateMeteringLabelRule( - test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - metering_label_id = 'aaa' - remote_ip_prefix = '10.0.0.0/24' - direction = 'ingress' - args = [metering_label_id, remote_ip_prefix, '--direction', direction, - '--excluded'] - position_names = ['metering_label_id', 'remote_ip_prefix', 'direction', - 'excluded'] - position_values = [metering_label_id, remote_ip_prefix, - direction, True] - self._test_create_resource(resource, cmd, metering_label_id, - myid, args, position_names, position_values) - - def test_list_metering_label_rules(self): - resources = "metering_label_rules" - cmd = metering.ListMeteringLabelRule( - test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd) - - def test_delete_metering_label_rule(self): - resource = 'metering_label_rule' - cmd = metering.DeleteMeteringLabelRule( - test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) - - def test_show_metering_label_rule(self): - resource = 'metering_label_rule' - cmd = metering.ShowMeteringLabelRule( - test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id']) diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py deleted file mode 100644 index b1f69acc8..000000000 --- a/neutronclient/tests/unit/test_cli20_network.py +++ /dev/null @@ -1,698 +0,0 @@ -# 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 itertools -import sys -from unittest import mock - -from oslo_serialization import jsonutils - -from neutronclient.common import exceptions -from neutronclient.neutron.v2_0 import network -from neutronclient import shell -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20CreateNetworkJSON(test_cli20.CLITestV20Base): - def setUp(self): - super(CLITestV20CreateNetworkJSON, self).setUp(plurals={'tags': 'tag'}) - - def _test_create_network(self, **kwargs): - cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) - resource = kwargs.pop('resource', 'network') - - name = kwargs.pop('name', 'myname') - myid = kwargs.pop('myid', 'myid') - args = kwargs.pop('args', [name, ]) - position_names = kwargs.pop('position_names', ['name', ]) - position_values = kwargs.pop('position_values', [name, ]) - - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - **kwargs) - - def test_create_network(self): - # Create net: myname. - self._test_create_network() - - def test_create_network_with_unicode(self): - # Create net: u'\u7f51\u7edc'. - self._test_create_network(name=u'\u7f51\u7edc') - - def test_create_network_description(self): - # Create net: --tenant_id tenantid myname. - name = 'myname' - args = ['--description', 'Nice network', name] - self._test_create_network(name=name, - args=args, - description='Nice network') - - def test_create_network_tenant_underscore(self): - # Create net: --tenant_id tenantid myname. - name = 'myname' - args = ['--tenant_id', 'tenantid', name] - self._test_create_network(name=name, args=args, tenant_id="tenantid") - - def test_create_network_tenant_dash(self): - # Test dashed options - # Create net: --tenant_id tenantid myname. - name = 'myname' - args = ['--tenant-id', 'tenantid', name] - self._test_create_network(name=name, args=args, tenant_id="tenantid") - - def test_create_network_provider_args(self): - # Create net: with --provider arguments. - # Test --provider attributes before network name - name = 'myname' - args = ['--provider:network_type', 'vlan', - '--provider:physical_network', 'physnet1', - '--provider:segmentation_id', '400', name] - position_names = ['provider:network_type', - 'provider:physical_network', - 'provider:segmentation_id', 'name'] - position_values = ['vlan', 'physnet1', '400', name] - self._test_create_network(name=name, - args=args, - position_names=position_names, - position_values=position_values) - - def test_create_network_tags(self): - # Create net: myname --tags a b. - name = 'myname' - args = [name, '--tags', 'a', 'b'] - self._test_create_network(name=name, args=args, tags=['a', 'b']) - - def test_create_network_state_underscore(self): - # Create net: --admin_state_down myname. - name = 'myname' - args = ['--admin_state_down', name, ] - self._test_create_network(name=name, args=args, admin_state_up=False) - - def test_create_network_state_dash(self): - # Test dashed options - name = 'myname' - args = ['--admin-state-down', name, ] - self._test_create_network(name=name, args=args, admin_state_up=False) - - def test_create_network_vlan_transparent(self): - # Create net: myname --vlan-transparent True. - name = 'myname' - args = ['--vlan-transparent', 'True', name] - self._test_create_network(name=name, - args=args, - vlan_transparent='True') - - def test_create_network_with_qos_policy(self): - # Create net: --qos-policy mypolicy. - name = 'myname' - qos_policy_name = 'mypolicy' - args = [name, '--qos-policy', qos_policy_name] - position_names = ['name', 'qos_policy_id'] - position_values = [name, qos_policy_name] - self._test_create_network(name=name, - args=args, - position_names=position_names, - position_values=position_values) - - def test_create_network_with_az_hint(self): - # Create net: --availability-zone-hint zone1 - # --availability-zone-hint zone2. - name = 'myname' - args = ['--availability-zone-hint', 'zone1', - '--availability-zone-hint', 'zone2', name] - position_names = ['availability_zone_hints', 'name'] - position_values = [['zone1', 'zone2'], name] - self._test_create_network(name=name, - args=args, - position_names=position_names, - position_values=position_values) - - def test_create_network_with_dns_domain(self): - # Create net: --dns-domain my-domain.org. - name = 'myname' - dns_domain_name = 'my-domain.org.' - args = [name, '--dns-domain', dns_domain_name] - position_names = ['name', 'dns_domain'] - position_values = [name, dns_domain_name] - self._test_create_network(name=name, - args=args, - position_names=position_names, - position_values=position_values) - - -class CLITestV20ListNetworkJSON(test_cli20.CLITestV20Base): - def setUp(self): - super(CLITestV20ListNetworkJSON, self).setUp(plurals={'tags': 'tag'}) - - def test_list_nets_empty_with_column(self): - resources = "networks" - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - reses = {resources: []} - resstr = self.client.serialize(reses) - resp = (test_cli20.MyResp(200), resstr) - # url method body - query = "id=myfakeid" - args = ['-c', 'id', '--', '--id', 'myfakeid'] - path = getattr(self.client, resources + "_path") - with mock.patch.object(cmd, "get_client", - return_value=self.client) as mock_get_client, \ - mock.patch.object(self.client.httpclient, "request", - return_value=resp) as mock_request, \ - mock.patch.object(network.ListNetwork, "extend_list", - return_value=None) as mock_extend_list: - cmd_parser = cmd.get_parser("list_" + resources) - shell.run_command(cmd, cmd_parser, args) - - mock_get_client.assert_called_once_with() - mock_request.assert_called_once_with( - test_cli20.MyUrlComparator(test_cli20.end_url(path, query), - self.client), - 'GET', - body=None, - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN})) - mock_extend_list.assert_called_once_with(test_cli20.IsA(list), - mock.ANY) - _str = self.fake_stdout.make_string() - self.assertEqual('\n', _str) - - def _test_list_networks(self, cmd, detail=False, tags=(), - fields_1=(), fields_2=(), page_size=None, - sort_key=(), sort_dir=(), base_args=None, - query=''): - resources = "networks" - with mock.patch.object(network.ListNetwork, "extend_list", - return_value=None) as mock_extend_list: - self._test_list_resources(resources, cmd, detail, tags, - fields_1, fields_2, page_size=page_size, - sort_key=sort_key, sort_dir=sort_dir, - base_args=base_args, query=query) - mock_extend_list.assert_called_once_with(test_cli20.IsA(list), - mock.ANY) - - def test_list_nets_pagination(self): - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - with mock.patch.object(network.ListNetwork, "extend_list", - return_value=None) as mock_extend_list: - self._test_list_resources_with_pagination("networks", cmd) - mock_extend_list.assert_called_once_with(test_cli20.IsA(list), - mock.ANY) - - def test_list_nets_sort(self): - # list nets: - # --sort-key name --sort-key id --sort-dir asc --sort-dir desc - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_list_networks(cmd, sort_key=['name', 'id'], - sort_dir=['asc', 'desc']) - - def test_list_nets_sort_with_keys_more_than_dirs(self): - # list nets: --sort-key name --sort-key id --sort-dir desc - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_list_networks(cmd, sort_key=['name', 'id'], - sort_dir=['desc']) - - def test_list_nets_sort_with_dirs_more_than_keys(self): - # list nets: --sort-key name --sort-dir desc --sort-dir asc - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_list_networks(cmd, sort_key=['name'], - sort_dir=['desc', 'asc']) - - def test_list_nets_limit(self): - # list nets: -P. - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_list_networks(cmd, page_size=1000) - - def test_list_nets_detail(self): - # list nets: -D. - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_list_networks(cmd, True) - - def test_list_nets_tags(self): - # List nets: -- --tags a b. - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_list_networks(cmd, tags=['a', 'b']) - - def test_list_nets_tags_with_unicode(self): - # List nets: -- --tags u'\u7f51\u7edc'. - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_list_networks(cmd, tags=[u'\u7f51\u7edc']) - - def test_list_nets_detail_tags(self): - # List nets: -D -- --tags a b. - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_list_networks(cmd, detail=True, tags=['a', 'b']) - - def _test_list_nets_extend_subnets(self, data, expected): - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - nets_path = getattr(self.client, 'networks_path') - subnets_path = getattr(self.client, 'subnets_path') - nets_query = '' - filters = '' - for n in data: - for s in n['subnets']: - filters = filters + "&id=%s" % s - subnets_query = 'fields=id&fields=cidr' + filters - with mock.patch.object(cmd, 'get_client', - return_value=self.client) as mock_get_client, \ - mock.patch.object(self.client.httpclient, - "request") as mock_request: - resp1 = (test_cli20.MyResp(200), - self.client.serialize({'networks': data})) - resp2 = (test_cli20.MyResp(200), - self.client.serialize({'subnets': [ - {'id': 'mysubid1', 'cidr': '192.168.1.0/24'}, - {'id': 'mysubid2', 'cidr': '172.16.0.0/24'}, - {'id': 'mysubid3', 'cidr': '10.1.1.0/24'}]})) - mock_request.side_effect = [resp1, resp2] - args = [] - cmd_parser = cmd.get_parser('list_networks') - parsed_args = cmd_parser.parse_args(args) - result = cmd.take_action(parsed_args) - - mock_get_client.assert_called_with() - self.assertEqual(2, mock_request.call_count) - mock_request.assert_has_calls([ - mock.call( - test_cli20.MyUrlComparator( - test_cli20.end_url(nets_path, nets_query), - self.client), - 'GET', - body=None, - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN})), - mock.call( - test_cli20.MyUrlComparator( - test_cli20.end_url(subnets_path, subnets_query), - self.client), - 'GET', - body=None, - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN}))]) - _result = [x for x in result[1]] - self.assertEqual(len(expected), len(_result)) - for res, exp in zip(_result, expected): - self.assertEqual(len(exp), len(res)) - for obsrvd, expctd in zip(res, exp): - self.assertEqual(expctd, obsrvd) - - def test_list_nets_extend_subnets(self): - data = [{'id': 'netid1', 'name': 'net1', 'subnets': ['mysubid1']}, - {'id': 'netid2', 'name': 'net2', 'subnets': ['mysubid2', - 'mysubid3']}] - # id, name, subnets - expected = [('netid1', 'net1', 'mysubid1 192.168.1.0/24'), - ('netid2', 'net2', - 'mysubid2 172.16.0.0/24\nmysubid3 10.1.1.0/24')] - self._test_list_nets_extend_subnets(data, expected) - - def test_list_nets_extend_subnets_no_subnet(self): - data = [{'id': 'netid1', 'name': 'net1', 'subnets': ['mysubid1']}, - {'id': 'netid2', 'name': 'net2', 'subnets': ['mysubid4']}] - # id, name, subnets - expected = [('netid1', 'net1', 'mysubid1 192.168.1.0/24'), - ('netid2', 'net2', 'mysubid4 ')] - self._test_list_nets_extend_subnets(data, expected) - - def test_list_nets_fields(self): - # List nets: --fields a --fields b -- --fields c d. - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_list_networks(cmd, - fields_1=['a', 'b'], fields_2=['c', 'd']) - - def _test_list_nets_columns(self, cmd, returned_body, - args=('-f', 'json')): - resources = 'networks' - with mock.patch.object(network.ListNetwork, "extend_list", - return_value=None) as mock_extend_list: - self._test_list_columns(cmd, resources, returned_body, args=args) - mock_extend_list.assert_called_once_with(test_cli20.IsA(list), - mock.ANY) - - def test_list_nets_defined_column(self): - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - returned_body = {"networks": [{"name": "buildname3", - "id": "id3", - "tenant_id": "tenant_3", - "subnets": []}]} - self._test_list_nets_columns(cmd, returned_body, - args=['-f', 'json', '-c', 'id']) - _str = self.fake_stdout.make_string() - returned_networks = jsonutils.loads(_str) - self.assertEqual(1, len(returned_networks)) - net = returned_networks[0] - self.assertEqual(1, len(net)) - self.assertIn("id", net.keys()) - - def test_list_nets_with_default_column(self): - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - returned_body = {"networks": [{"name": "buildname3", - "id": "id3", - "tenant_id": "tenant_3", - "subnets": []}]} - self._test_list_nets_columns(cmd, returned_body) - _str = self.fake_stdout.make_string() - returned_networks = jsonutils.loads(_str) - self.assertEqual(1, len(returned_networks)) - net = returned_networks[0] - self.assertEqual(3, len(net)) - self.assertEqual(0, len(set(net) ^ set(cmd.list_columns))) - - def test_list_external_nets_empty_with_column(self): - resources = "networks" - cmd = network.ListExternalNetwork(test_cli20.MyApp(sys.stdout), None) - reses = {resources: []} - resstr = self.client.serialize(reses) - # url method body - query = "router%3Aexternal=True&id=myfakeid" - args = ['-c', 'id', '--', '--id', 'myfakeid'] - path = getattr(self.client, resources + "_path") - resp = (test_cli20.MyResp(200), resstr) - with mock.patch.object(cmd, "get_client", - return_value=self.client) as mock_get_client, \ - mock.patch.object(self.client.httpclient, "request", - return_value=resp) as mock_request, \ - mock.patch.object(network.ListNetwork, "extend_list", - return_value=None) as mock_extend_list: - cmd_parser = cmd.get_parser("list_" + resources) - shell.run_command(cmd, cmd_parser, args) - - mock_get_client.assert_called_once_with() - mock_request.assert_called_once_with( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, query), self.client), - 'GET', - body=None, - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN})) - mock_extend_list.assert_called_once_with(test_cli20.IsA(list), - mock.ANY) - _str = self.fake_stdout.make_string() - self.assertEqual('\n', _str) - - def _test_list_external_nets(self, resources, cmd, - detail=False, tags=(), - fields_1=(), fields_2=()): - reses = {resources: [{'id': 'myid1', }, - {'id': 'myid2', }, ], } - - resstr = self.client.serialize(reses) - resp = (test_cli20.MyResp(200), resstr) - - # url method body - query = "" - args = detail and ['-D', ] or [] - if fields_1: - for field in fields_1: - args.append('--fields') - args.append(field) - if tags: - args.append('--') - args.append("--tag") - for tag in tags: - args.append(tag) - if (not tags) and fields_2: - args.append('--') - if fields_2: - args.append("--fields") - for field in fields_2: - args.append(field) - for field in itertools.chain(fields_1, fields_2): - if query: - query += "&fields=" + field - else: - query = "fields=" + field - if query: - query += '&router%3Aexternal=True' - else: - query += 'router%3Aexternal=True' - for tag in tags: - if query: - query += "&tag=" + tag - else: - query = "tag=" + tag - if detail: - query = query and query + '&verbose=True' or 'verbose=True' - path = getattr(self.client, resources + "_path") - - with mock.patch.object(cmd, "get_client", - return_value=self.client) as mock_get_client, \ - mock.patch.object(self.client.httpclient, "request", - return_value=resp) as mock_request, \ - mock.patch.object(network.ListNetwork, "extend_list", - return_value=None) as mock_extend_list: - cmd_parser = cmd.get_parser("list_" + resources) - shell.run_command(cmd, cmd_parser, args) - - mock_get_client.assert_called_once_with() - mock_request.assert_called_once_with( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, query), self.client), - 'GET', - body=None, - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN})) - mock_extend_list.assert_called_once_with(test_cli20.IsA(list), - mock.ANY) - _str = self.fake_stdout.make_string() - - self.assertIn('myid1', _str) - - def test_list_external_nets_detail(self): - # list external nets: -D. - resources = "networks" - cmd = network.ListExternalNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_list_external_nets(resources, cmd, True) - - def test_list_external_nets_tags(self): - # List external nets: -- --tags a b. - resources = "networks" - cmd = network.ListExternalNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_list_external_nets(resources, - cmd, tags=['a', 'b']) - - def test_list_external_nets_detail_tags(self): - # List external nets: -D -- --tags a b. - resources = "networks" - cmd = network.ListExternalNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_list_external_nets(resources, cmd, - detail=True, tags=['a', 'b']) - - def test_list_external_nets_fields(self): - # List external nets: --fields a --fields b -- --fields c d. - resources = "networks" - cmd = network.ListExternalNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_list_external_nets(resources, cmd, - fields_1=['a', 'b'], - fields_2=['c', 'd']) - - def test_list_shared_networks(self): - # list nets : --shared False - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_list_networks(cmd, base_args='--shared False'.split(), - query='shared=False') - - -class CLITestV20UpdateNetworkJSON(test_cli20.CLITestV20Base): - def test_update_network_exception(self): - # Update net: myid. - resource = 'network' - cmd = network.UpdateNetwork(test_cli20.MyApp(sys.stdout), None) - self.assertRaises(exceptions.CommandError, self._test_update_resource, - resource, cmd, 'myid', ['myid'], {}) - - def test_update_network(self): - # Update net: myid --name myname --tags a b. - resource = 'network' - cmd = network.UpdateNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'myname', - '--tags', 'a', 'b', '--description', - 'This network takes the scenic route'], - {'name': 'myname', 'tags': ['a', 'b'], - 'description': 'This network takes the ' - 'scenic route'}) - - def test_update_network_with_unicode(self): - # Update net: myid --name u'\u7f51\u7edc' --tags a b. - resource = 'network' - cmd = network.UpdateNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', u'\u7f51\u7edc', - '--tags', 'a', 'b'], - {'name': u'\u7f51\u7edc', - 'tags': ['a', 'b'], } - ) - - def test_update_network_with_qos_policy(self): - # Update net: myid --qos-policy mypolicy. - resource = 'network' - cmd = network.UpdateNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--qos-policy', 'mypolicy'], - {'qos_policy_id': 'mypolicy', }) - - def test_update_network_with_no_qos_policy(self): - # Update net: myid --no-qos-policy. - resource = 'network' - cmd = network.UpdateNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--no-qos-policy'], - {'qos_policy_id': None, }) - - def test_update_network_with_dns_domain(self): - # Update net: myid --dns-domain my-domain.org. - resource = 'network' - cmd = network.UpdateNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--dns-domain', 'my-domain.org.'], - {'dns_domain': 'my-domain.org.', }) - - def test_update_network_with_no_dns_domain(self): - # Update net: myid --no-dns-domain - resource = 'network' - cmd = network.UpdateNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--no-dns-domain'], - {'dns_domain': "", }) - - -class CLITestV20ShowNetworkJSON(test_cli20.CLITestV20Base): - def test_show_network(self): - # Show net: --fields id --fields name myid. - resource = 'network' - cmd = network.ShowNetwork(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, - ['id', 'name']) - - -class CLITestV20DeleteNetworkJSON(test_cli20.CLITestV20Base): - def test_delete_network(self): - # Delete net: myid. - resource = 'network' - cmd = network.DeleteNetwork(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) - - def test_bulk_delete_network(self): - # Delete net: myid1 myid2. - resource = 'network' - cmd = network.DeleteNetwork(test_cli20.MyApp(sys.stdout), None) - myid1 = 'myid1' - myid2 = 'myid2' - args = [myid1, myid2] - self._test_delete_resource(resource, cmd, myid1, args, extra_id=myid2) - - def test_bulk_delete_network_fail(self): - # Delete net: myid1 myid2. - resource = 'network' - cmd = network.DeleteNetwork(test_cli20.MyApp(sys.stdout), None) - myid1 = 'myid1' - myid2 = 'myid2' - args = [myid1, myid2] - self.assertRaises(exceptions.NeutronCLIError, - self._test_delete_resource, - resource, cmd, myid1, args, extra_id=myid2, - delete_fail=True) - - -class CLITestV20ExtendListNetworkJSON(test_cli20.CLITestV20Base): - def _build_test_data(self, data): - subnet_ids = [] - response = [] - filters = "" - for n in data: - if 'subnets' in n: - subnet_ids.extend(n['subnets']) - for subnet_id in n['subnets']: - filters = "%s&id=%s" % (filters, subnet_id) - response.append({'id': subnet_id, - 'cidr': '192.168.0.0/16'}) - resp_str = self.client.serialize({'subnets': response}) - resp = (test_cli20.MyResp(200), resp_str) - return filters, resp - - def test_extend_list(self): - data = [{'id': 'netid%d' % i, 'name': 'net%d' % i, - 'subnets': ['mysubid%d' % i]} - for i in range(10)] - filters, response = self._build_test_data(data) - path = getattr(self.client, 'subnets_path') - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - with mock.patch.object(cmd, "get_client", - return_value=self.client) as mock_get_client, \ - mock.patch.object(self.client.httpclient, "request", - return_value=response) as mock_request: - known_args, _vs = cmd.get_parser('create_subnets')\ - .parse_known_args() - cmd.extend_list(data, known_args) - - mock_get_client.assert_called_once_with() - mock_request.assert_called_once_with( - test_cli20.MyUrlComparator(test_cli20.end_url( - path, 'fields=id&fields=cidr' + filters), self.client), - 'GET', - body=None, - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN})) - - def test_extend_list_exceed_max_uri_len(self): - data = [{'id': 'netid%d' % i, 'name': 'net%d' % i, - 'subnets': ['mysubid%d' % i]} - for i in range(10)] - # Since in pagination we add &marker= (44 symbols), total change - # is 45 symbols. Single subnet takes 40 symbols (id=&). - # Because of it marker will take more space than single subnet filter, - # and we expect neutron to send last 2 subnets in separate response. - filters1, response1 = self._build_test_data(data[:len(data) - 2]) - filters2, response2 = self._build_test_data(data[len(data) - 2:]) - path = getattr(self.client, 'subnets_path') - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - with mock.patch.object(cmd, "get_client", - return_value=self.client) as mock_get_client, \ - mock.patch.object(self.client.httpclient, - "request") as mock_request, \ - mock.patch.object(self.client.httpclient, "_check_uri_length", - return_value=None) as mock_check_uri_length: - # 1 char of extra URI len will cause a split in 2 requests - mock_check_uri_length.side_effect = [ - exceptions.RequestURITooLong(excess=1), None, None] - mock_request.side_effect = [response1, response2] - known_args, _vs = cmd.get_parser('create_subnets')\ - .parse_known_args() - cmd.extend_list(data, known_args) - - mock_get_client.assert_called_once_with() - self.assertEqual(2, mock_request.call_count) - mock_request.assert_has_calls([ - mock.call( - test_cli20.MyUrlComparator( - test_cli20.end_url( - path, 'fields=id&fields=cidr%s' % filters1), - self.client), - 'GET', - body=None, - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN})), - mock.call( - test_cli20.MyUrlComparator( - test_cli20.end_url( - path, 'fields=id&fields=cidr%s' % filters2), - self.client), - 'GET', - body=None, - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN}))]) diff --git a/neutronclient/tests/unit/test_cli20_network_ip_availability.py b/neutronclient/tests/unit/test_cli20_network_ip_availability.py deleted file mode 100644 index eb325a8f6..000000000 --- a/neutronclient/tests/unit/test_cli20_network_ip_availability.py +++ /dev/null @@ -1,54 +0,0 @@ -# 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 sys - -from neutronclient.neutron.v2_0 import network_ip_availability -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20NetworkIPAvailability(test_cli20.CLITestV20Base): - - id_field = 'network_id' - - def _test_list_network_ip_availability(self, args, query): - resources = "network_ip_availabilities" - cmd = network_ip_availability.ListIpAvailability(test_cli20.MyApp - (sys.stdout), None) - self._test_list_resources(resources, cmd, - base_args=args, - query=query) - - def test_list_network_ip_availability(self): - self._test_list_network_ip_availability(args=None, - query='ip_version=4') - - def test_list_network_ip_availability_ipv6(self): - self._test_list_network_ip_availability( - args=['--ip-version', '6'], query='ip_version=6') - - def test_list_network_ip_availability_net_id_and_ipv4(self): - self._test_list_network_ip_availability( - args=['--ip-version', '4', '--network-id', 'myid'], - query='ip_version=4&network_id=myid') - - def test_list_network_ip_availability_net_name_and_tenant_id(self): - self._test_list_network_ip_availability( - args=['--network-name', 'foo', '--tenant-id', 'mytenant'], - query='network_name=foo&tenant_id=mytenant&ip_version=4') - - def test_show_network_ip_availability(self): - resource = "network_ip_availability" - cmd = network_ip_availability.ShowIpAvailability( - test_cli20.MyApp(sys.stdout), None) - self._test_show_resource(resource, cmd, self.test_id, - args=[self.test_id]) diff --git a/neutronclient/tests/unit/test_cli20_port.py b/neutronclient/tests/unit/test_cli20_port.py deleted file mode 100644 index ab7e95655..000000000 --- a/neutronclient/tests/unit/test_cli20_port.py +++ /dev/null @@ -1,794 +0,0 @@ -# Copyright 2012 OpenStack Foundation. -# 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 itertools -import sys -from unittest import mock - - -from neutronclient.neutron.v2_0 import port -from neutronclient import shell -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20PortJSON(test_cli20.CLITestV20Base): - def setUp(self): - super(CLITestV20PortJSON, self).setUp(plurals={'tags': 'tag'}) - - def test_create_port(self): - # Create port: netid. - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = [netid, '--description', 'DESC'] - position_names = ['network_id'] - position_values = [] - position_values.extend([netid]) - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - description='DESC') - - def test_create_port_extra_dhcp_opts_args(self): - # Create port: netid --extra_dhcp_opt. - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - extra_dhcp_opts = [{'opt_name': 'bootfile-name', - 'opt_value': 'pxelinux.0'}, - {'opt_name': 'tftp-server', - 'opt_value': '123.123.123.123'}, - {'opt_name': 'server-ip-address', - 'opt_value': '123.123.123.45'}] - args = [netid] - for dhcp_opt in extra_dhcp_opts: - args += ['--extra-dhcp-opt', - ('opt_name=%(opt_name)s,opt_value=%(opt_value)s' % - dhcp_opt)] - position_names = ['network_id', 'extra_dhcp_opts'] - position_values = [netid, extra_dhcp_opts] - position_values.extend([netid]) - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_extra_dhcp_opts_args_ip_version(self): - # Create port: netid --extra_dhcp_opt. - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - extra_dhcp_opts = [{'opt_name': 'bootfile-name', - 'opt_value': 'pxelinux.0', - 'ip_version': "4"}, - {'opt_name': 'tftp-server', - 'opt_value': '2001:192:168::1', - 'ip_version': "6"}, - {'opt_name': 'server-ip-address', - 'opt_value': '123.123.123.45', - 'ip_version': "4"}] - args = [netid] - for dhcp_opt in extra_dhcp_opts: - args += ['--extra-dhcp-opt', - ('opt_name=%(opt_name)s,opt_value=%(opt_value)s,' - 'ip_version=%(ip_version)s' % - dhcp_opt)] - position_names = ['network_id', 'extra_dhcp_opts'] - position_values = [netid, extra_dhcp_opts] - position_values.extend([netid]) - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_full(self): - # Create port: --mac_address mac --device_id deviceid netid. - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = ['--mac_address', 'mac', '--device_id', 'deviceid', netid] - position_names = ['network_id', 'mac_address', 'device_id'] - position_values = [netid, 'mac', 'deviceid'] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - # Test dashed options - args = ['--mac-address', 'mac', '--device-id', 'deviceid', netid] - position_names = ['network_id', 'mac_address', 'device_id'] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_vnic_type_normal(self): - # Create port: --vnic_type normal netid. - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = ['--vnic_type', 'normal', netid] - position_names = ['binding:vnic_type', 'network_id'] - position_values = ['normal', netid] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - # Test dashed options - args = ['--vnic-type', 'normal', netid] - position_names = ['binding:vnic_type', 'network_id'] - position_values = ['normal', netid] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_vnic_type_direct(self): - # Create port: --vnic_type direct netid. - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = ['--vnic_type', 'direct', netid] - position_names = ['binding:vnic_type', 'network_id'] - position_values = ['direct', netid] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - # Test dashed options - args = ['--vnic-type', 'direct', netid] - position_names = ['binding:vnic_type', 'network_id'] - position_values = ['direct', netid] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_vnic_type_direct_physical(self): - # Create port: --vnic_type direct-physical netid. - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = ['--vnic_type', 'direct-physical', netid] - position_names = ['binding:vnic_type', 'network_id'] - position_values = ['direct-physical', netid] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - # Test dashed options - args = ['--vnic-type', 'direct-physical', netid] - position_names = ['binding:vnic_type', 'network_id'] - position_values = ['direct-physical', netid] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_vnic_type_macvtap(self): - # Create port: --vnic_type macvtap netid. - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = ['--vnic_type', 'macvtap', netid] - position_names = ['binding:vnic_type', 'network_id'] - position_values = ['macvtap', netid] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - # Test dashed options - args = ['--vnic-type', 'macvtap', netid] - position_names = ['binding:vnic_type', 'network_id'] - position_values = ['macvtap', netid] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_vnic_type_baremetal(self): - # Create port: --vnic_type baremetal netid. - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = ['--vnic_type', 'baremetal', netid] - position_names = ['binding:vnic_type', 'network_id'] - position_values = ['baremetal', netid] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - # Test dashed options - args = ['--vnic-type', 'baremetal', netid] - position_names = ['binding:vnic_type', 'network_id'] - position_values = ['baremetal', netid] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_vnic_type_smart_nic(self): - # Create port: --vnic_type smart-nic netid. - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = ['--vnic_type', 'smart-nic', netid] - position_names = ['binding:vnic_type', 'network_id'] - position_values = ['smart-nic', netid] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - # Test dashed options - args = ['--vnic-type', 'smart-nic', netid] - position_names = ['binding:vnic_type', 'network_id'] - position_values = ['smart-nic', netid] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_with_binding_profile(self): - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = ['--binding_profile', '{"foo":"bar"}', netid] - position_names = ['binding:profile', 'network_id'] - position_values = [{'foo': 'bar'}, netid] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - # Test dashed options - args = ['--binding-profile', '{"foo":"bar"}', netid] - position_names = ['binding:profile', 'network_id'] - position_values = [{'foo': 'bar'}, netid] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_tenant(self): - # Create port: --tenant_id tenantid netid. - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = ['--tenant_id', 'tenantid', netid, ] - position_names = ['network_id'] - position_values = [] - position_values.extend([netid]) - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - # Test dashed options - args = ['--tenant-id', 'tenantid', netid, ] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_port_tags(self): - # Create port: netid mac_address device_id --tags a b. - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = [netid, '--tags', 'a', 'b'] - position_names = ['network_id'] - position_values = [] - position_values.extend([netid]) - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tags=['a', 'b']) - - def test_create_port_secgroup(self): - # Create port: --security-group sg1_id netid. - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = ['--security-group', 'sg1_id', netid] - position_names = ['network_id', 'security_groups'] - position_values = [netid, ['sg1_id']] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_secgroups(self): - # Create port: netid - # The are --security-group sg1_id - # --security-group sg2_id - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = ['--security-group', 'sg1_id', - '--security-group', 'sg2_id', - netid] - position_names = ['network_id', 'security_groups'] - position_values = [netid, ['sg1_id', 'sg2_id']] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_secgroup_off(self): - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = ['--no-security-groups', netid] - position_names = ['network_id', 'security_groups'] - position_values = [netid, []] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_secgroups_list(self): - # Create port: netid - # The are --security-groups list=true sg_id1 sg_id2 - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = [netid, '--security-groups', 'list=true', 'sg_id1', 'sg_id2'] - position_names = ['network_id', 'security_groups'] - position_values = [netid, ['sg_id1', 'sg_id2']] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_with_qos_policy(self): - # Create port: --qos-policy mypolicy. - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - qos_policy_name = 'mypolicy' - args = [netid, '--qos-policy', qos_policy_name] - position_names = ['network_id', 'qos_policy_id'] - position_values = [netid, qos_policy_name] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_with_dns_name(self): - # Create port: --dns-name my-port. - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - dns_name_name = 'my-port' - args = [netid, '--dns-name', dns_name_name] - position_names = ['network_id', 'dns_name'] - position_values = [netid, dns_name_name] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_with_allowed_address_pair_ipaddr(self): - # Create port: - # --allowed-address-pair ip_address=addr0 - # --allowed-address-pair ip_address=addr1 - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - pairs = [{'ip_address': '123.123.123.123'}, - {'ip_address': '123.123.123.45'}] - args = [netid, - '--allowed-address-pair', - 'ip_address=123.123.123.123', - '--allowed-address-pair', - 'ip_address=123.123.123.45'] - position_names = ['network_id', 'allowed_address_pairs'] - position_values = [netid, pairs] - position_values.extend([netid]) - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_with_allowed_address_pair(self): - # Create port: - # --allowed-address-pair ip_address=addr0,mac_address=mac0 - # --allowed-address-pair ip_address=addr1,mac_address=mac1 - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - pairs = [{'ip_address': '123.123.123.123', - 'mac_address': '10:00:00:00:00:00'}, - {'ip_address': '123.123.123.45', - 'mac_address': '10:00:00:00:00:01'}] - args = [netid, - '--allowed-address-pair', - 'ip_address=123.123.123.123,mac_address=10:00:00:00:00:00', - '--allowed-address-pair', - 'ip_address=123.123.123.45,mac_address=10:00:00:00:00:01'] - position_names = ['network_id', 'allowed_address_pairs'] - position_values = [netid, pairs] - position_values.extend([netid]) - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_list_ports(self): - # List ports: -D. - resources = "ports" - cmd = port.ListPort(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_ports_pagination(self): - resources = "ports" - cmd = port.ListPort(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_ports_sort(self): - # list ports: - # --sort-key name --sort-key id --sort-key asc --sort-key desc - resources = "ports" - cmd = port.ListPort(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_ports_limit(self): - # list ports: -P. - resources = "ports" - cmd = port.ListPort(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_list_ports_tags(self): - # List ports: -- --tags a b. - resources = "ports" - cmd = port.ListPort(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, tags=['a', 'b']) - - def test_list_ports_detail_tags(self): - # List ports: -D -- --tags a b. - resources = "ports" - cmd = port.ListPort(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, detail=True, tags=['a', 'b']) - - def test_list_ports_fields(self): - # List ports: --fields a --fields b -- --fields c d. - resources = "ports" - cmd = port.ListPort(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - fields_1=['a', 'b'], fields_2=['c', 'd']) - - def test_list_ports_with_fixed_ips_in_csv(self): - # List ports: -f csv. - resources = "ports" - cmd = port.ListPort(test_cli20.MyApp(sys.stdout), None) - fixed_ips = [{"subnet_id": "30422057-d6df-4c90-8314-aefb5e326666", - "ip_address": "10.0.0.12"}, - {"subnet_id": "30422057-d6df-4c90-8314-aefb5e326666", - "ip_address": "10.0.0.4"}] - contents = [{'name': 'name1', 'fixed_ips': fixed_ips}] - self._test_list_resources(resources, cmd, True, - response_contents=contents, - output_format='csv') - - def _test_list_router_port(self, resources, cmd, - myid, detail=False, tags=(), - fields_1=(), fields_2=()): - reses = {resources: [{'id': 'myid1', }, - {'id': 'myid2', }, ], } - - resstr = self.client.serialize(reses) - # url method body - query = "" - args = detail and ['-D', ] or [] - - if fields_1: - for field in fields_1: - args.append('--fields') - args.append(field) - args.append(myid) - if tags: - args.append('--') - args.append("--tag") - for tag in tags: - args.append(tag) - if (not tags) and fields_2: - args.append('--') - if fields_2: - args.append("--fields") - for field in fields_2: - args.append(field) - for field in itertools.chain(fields_1, fields_2): - if query: - query += "&fields=" + field - else: - query = "fields=" + field - - for tag in tags: - if query: - query += "&tag=" + tag - else: - query = "tag=" + tag - if detail: - query = query and query + '&verbose=True' or 'verbose=True' - query = query and query + '&device_id=%s' or 'device_id=%s' - path = getattr(self.client, resources + "_path") - with mock.patch.object(cmd, "get_client", - return_value=self.client) as mock_get_client, \ - mock.patch.object(self.client.httpclient, "request", - return_value=(test_cli20.MyResp(200), - resstr)) as mock_request: - cmd_parser = cmd.get_parser("list_" + resources) - shell.run_command(cmd, cmd_parser, args) - - self.assert_mock_multiple_calls_with_same_arguments( - mock_get_client, mock.call(), 2) - mock_request.assert_called_once_with( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, query % myid), self.client), - 'GET', - body=None, - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN})) - _str = self.fake_stdout.make_string() - - self.assertIn('myid1', _str) - - def test_list_router_ports(self): - # List router ports: -D. - resources = "ports" - cmd = port.ListRouterPort(test_cli20.MyApp(sys.stdout), None) - self._test_list_router_port(resources, cmd, - self.test_id, True) - - def test_list_router_ports_tags(self): - # List router ports: -- --tags a b. - resources = "ports" - cmd = port.ListRouterPort(test_cli20.MyApp(sys.stdout), None) - self._test_list_router_port(resources, cmd, - self.test_id, tags=['a', 'b']) - - def test_list_router_ports_detail_tags(self): - # List router ports: -D -- --tags a b. - resources = "ports" - cmd = port.ListRouterPort(test_cli20.MyApp(sys.stdout), None) - self._test_list_router_port(resources, cmd, self.test_id, - detail=True, tags=['a', 'b']) - - def test_list_router_ports_fields(self): - # List ports: --fields a --fields b -- --fields c d. - resources = "ports" - cmd = port.ListRouterPort(test_cli20.MyApp(sys.stdout), None) - self._test_list_router_port(resources, cmd, self.test_id, - fields_1=['a', 'b'], - fields_2=['c', 'd']) - - def test_update_port(self): - # Update port: myid --name myname --admin-state-up False --tags a b. - resource = 'port' - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'myname', - '--admin-state-up', 'False', - '--description', 'garbage', - '--tags', 'a', 'b'], - {'name': 'myname', - 'admin_state_up': 'False', - 'description': 'garbage', - 'tags': ['a', 'b'], }) - - def test_update_port_secgroup(self): - resource = 'port' - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = ['--security-group', 'sg1_id', myid] - updatefields = {'security_groups': ['sg1_id']} - self._test_update_resource(resource, cmd, myid, args, updatefields) - - def test_update_port_secgroups(self): - resource = 'port' - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = ['--security-group', 'sg1_id', - '--security-group', 'sg2_id', - myid] - updatefields = {'security_groups': ['sg1_id', 'sg2_id']} - self._test_update_resource(resource, cmd, myid, args, updatefields) - - def test_update_port_extra_dhcp_opts(self): - # Update port: myid --extra_dhcp_opt. - resource = 'port' - myid = 'myid' - args = [myid, - '--extra-dhcp-opt', - "opt_name=bootfile-name,opt_value=pxelinux.0", - '--extra-dhcp-opt', - "opt_name=tftp-server,opt_value=123.123.123.123", - '--extra-dhcp-opt', - "opt_name=server-ip-address,opt_value=123.123.123.45" - ] - updatedfields = {'extra_dhcp_opts': [{'opt_name': 'bootfile-name', - 'opt_value': 'pxelinux.0'}, - {'opt_name': 'tftp-server', - 'opt_value': '123.123.123.123'}, - {'opt_name': 'server-ip-address', - 'opt_value': '123.123.123.45'}]} - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, myid, args, updatedfields) - - def test_update_port_fixed_ip(self): - resource = 'port' - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - subnet_id = 'subnet_id' - ip_addr = '123.123.123.123' - args = [myid, - '--fixed-ip', - "subnet_id=%(subnet_id)s,ip_address=%(ip_addr)s" % - {'subnet_id': subnet_id, - 'ip_addr': ip_addr}] - updated_fields = {"fixed_ips": [{'subnet_id': subnet_id, - 'ip_address': ip_addr}]} - self._test_update_resource(resource, cmd, myid, args, updated_fields) - - def test_update_port_device_id_device_owner(self): - resource = 'port' - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = ['--device-id', 'dev_id', '--device-owner', 'fake', myid] - updatefields = {'device_id': 'dev_id', - 'device_owner': 'fake'} - self._test_update_resource(resource, cmd, myid, args, updatefields) - - def test_update_port_extra_dhcp_opts_ip_version(self): - # Update port: myid --extra_dhcp_opt. - resource = 'port' - myid = 'myid' - args = [myid, - '--extra-dhcp-opt', - "opt_name=bootfile-name,opt_value=pxelinux.0,ip_version=4", - '--extra-dhcp-opt', - "opt_name=tftp-server,opt_value=2001:192:168::1,ip_version=6", - '--extra-dhcp-opt', - "opt_name=server-ip-address,opt_value=null,ip_version=4" - ] - updatedfields = {'extra_dhcp_opts': [{'opt_name': 'bootfile-name', - 'opt_value': 'pxelinux.0', - 'ip_version': '4'}, - {'opt_name': 'tftp-server', - 'opt_value': '2001:192:168::1', - 'ip_version': '6'}, - {'opt_name': 'server-ip-address', - 'opt_value': None, - 'ip_version': '4'}]} - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, myid, args, updatedfields) - - def test_update_port_with_qos_policy(self): - # Update port: myid --qos-policy mypolicy. - resource = 'port' - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--qos-policy', 'mypolicy'], - {'qos_policy_id': 'mypolicy', }) - - def test_update_port_with_no_qos_policy(self): - # Update port: myid --no-qos-policy. - resource = 'port' - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--no-qos-policy'], - {'qos_policy_id': None, }) - - def test_update_port_with_dns_name(self): - # Update port: myid --dns-name my-port. - resource = 'port' - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--dns-name', 'my-port'], - {'dns_name': 'my-port', }) - - def test_update_port_with_no_dns_name(self): - # Update port: myid --no-dns-name - resource = 'port' - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--no-dns-name'], - {'dns_name': "", }) - - def test_delete_extra_dhcp_opts_from_port(self): - resource = 'port' - myid = 'myid' - args = [myid, - '--extra-dhcp-opt', - "opt_name=bootfile-name,opt_value=null", - '--extra-dhcp-opt', - "opt_name=tftp-server,opt_value=123.123.123.123", - '--extra-dhcp-opt', - "opt_name=server-ip-address,opt_value=123.123.123.45" - ] - # the client code will change the null to None and send to server, - # where its interpreted as delete the DHCP option on the port. - updatedfields = {'extra_dhcp_opts': [{'opt_name': 'bootfile-name', - 'opt_value': None}, - {'opt_name': 'tftp-server', - 'opt_value': '123.123.123.123'}, - {'opt_name': 'server-ip-address', - 'opt_value': '123.123.123.45'}]} - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, myid, args, updatedfields) - - def test_update_port_security_group_off(self): - # Update port: --no-security-groups myid. - resource = 'port' - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['--no-security-groups', 'myid'], - {'security_groups': []}) - - def test_update_port_allowed_address_pair_ipaddr(self): - # Update port(ip_address only): - # --allowed-address-pairs ip_address=addr0 - # --allowed-address-pairs ip_address=addr1 - import sys - resource = 'port' - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - pairs = [{'ip_address': '123.123.123.123'}, - {'ip_address': '123.123.123.45'}] - args = [myid, - '--allowed-address-pair', - 'ip_address=123.123.123.123', - '--allowed-address-pair', - 'ip_address=123.123.123.45'] - updatefields = {'allowed_address_pairs': pairs} - self._test_update_resource(resource, cmd, myid, args, updatefields) - - def test_update_port_allowed_address_pair(self): - # Update port: - # --allowed-address-pair ip_address=addr0,mac_address=mac0 - # --allowed-address-pair ip_address_addr1,mac_address=mac1 - resource = 'port' - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - pairs = [{'ip_address': '123.123.123.123', - 'mac_address': '10:00:00:00:00:00'}, - {'ip_address': '123.123.123.45', - 'mac_address': '10:00:00:00:00:01'}] - args = [myid, - '--allowed-address-pair', - 'ip_address=123.123.123.123,mac_address=10:00:00:00:00:00', - '--allowed-address-pair', - 'ip_address=123.123.123.45,mac_address=10:00:00:00:00:01'] - updatefields = {'allowed_address_pairs': pairs} - self._test_update_resource(resource, cmd, myid, args, updatefields) - - def test_update_port_allowed_address_pairs_off(self): - # Update port: --no-allowed-address-pairs. - resource = 'port' - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['--no-allowed-address-pairs', 'myid'], - {'allowed_address_pairs': []}) - - def test_show_port(self): - # Show port: --fields id --fields name myid. - resource = 'port' - cmd = port.ShowPort(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name']) - - def test_delete_port(self): - # Delete port: myid. - resource = 'port' - cmd = port.DeletePort(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) diff --git a/neutronclient/tests/unit/test_cli20_purge.py b/neutronclient/tests/unit/test_cli20_purge.py deleted file mode 100644 index 9bfd91c53..000000000 --- a/neutronclient/tests/unit/test_cli20_purge.py +++ /dev/null @@ -1,100 +0,0 @@ -# Copyright 2016 Cisco Systems -# 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 sys - -from neutronclient.neutron.v2_0 import purge -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20Purge(test_cli20.CLITestV20Base): - - def setUp(self): - super(CLITestV20Purge, self).setUp() - self.resource_types = ['floatingip', 'port', 'router', - 'network', 'security_group'] - - def _generate_resources_dict(self, value=0): - resources_dict = {} - resources_dict['true'] = value - for resource_type in self.resource_types: - resources_dict[resource_type] = value - return resources_dict - - def _verify_suffix(self, resources, message): - for resource, value in resources.items(): - if value > 0: - suffix = list('%(value)d %(resource)s' % - {'value': value, 'resource': resource}) - if value != 1: - suffix.append('s') - suffix = ''.join(suffix) - self.assertIn(suffix, message) - else: - self.assertNotIn(resource, message) - - def _verify_message(self, message, deleted, failed): - message = message.split('.') - success_prefix = "Deleted " - failure_prefix = "The following resources could not be deleted: " - if not deleted['true']: - for msg in message: - self.assertNotIn(success_prefix, msg) - message = message[0] - if not failed['true']: - expected = 'Tenant has no supported resources' - self.assertEqual(expected, message) - else: - self.assertIn(failure_prefix, message) - self._verify_suffix(failed, message) - else: - resources_deleted = message[0] - self.assertIn(success_prefix, resources_deleted) - self._verify_suffix(deleted, resources_deleted) - if failed['true']: - resources_failed = message[1] - self.assertIn(failure_prefix, resources_failed) - self._verify_suffix(failed, resources_failed) - else: - for msg in message: - self.assertNotIn(failure_prefix, msg) - - def _verify_result(self, my_purge, deleted, failed): - message = my_purge._build_message(deleted, failed, failed['true']) - self._verify_message(message, deleted, failed) - - def test_build_message(self): - my_purge = purge.Purge(test_cli20.MyApp(sys.stdout), None) - - # Verify message when tenant has no supported resources - deleted = self._generate_resources_dict() - failed = self._generate_resources_dict() - self._verify_result(my_purge, deleted, failed) - - # Verify message when tenant has supported resources, - # and they are all deleteable - deleted = self._generate_resources_dict(1) - self._verify_result(my_purge, deleted, failed) - - # Verify message when tenant has supported resources, - # and some are not deleteable - failed = self._generate_resources_dict(1) - self._verify_result(my_purge, deleted, failed) - - # Verify message when tenant has supported resources, - # and all are not deleteable - deleted = self._generate_resources_dict() - self._verify_result(my_purge, deleted, failed) diff --git a/neutronclient/tests/unit/test_cli20_rbac.py b/neutronclient/tests/unit/test_cli20_rbac.py deleted file mode 100644 index ca89df6f9..000000000 --- a/neutronclient/tests/unit/test_cli20_rbac.py +++ /dev/null @@ -1,134 +0,0 @@ -# Copyright 2015 Huawei Technologies India Pvt Ltd. -# 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 sys - -import testscenarios - -from neutronclient.neutron.v2_0 import rbac -from neutronclient.tests.unit import test_cli20 - -load_tests = testscenarios.load_tests_apply_scenarios - - -class CLITestV20RBACBaseJSON(test_cli20.CLITestV20Base): - non_admin_status_resources = ['rbac_policy'] - - scenarios = [ - ('network rbac objects', - {'object_type_name': 'network', 'object_type_val': 'network'}), - ('qos policy rbac objects', - {'object_type_name': 'qos-policy', 'object_type_val': 'qos_policy'}), - ] - - def test_create_rbac_policy_with_mandatory_params(self): - # Create rbac: rbac_object --type --action - # access_as_shared - resource = 'rbac_policy' - cmd = rbac.CreateRBACPolicy(test_cli20.MyApp(sys.stdout), None) - name = 'rbac_object' - myid = 'myid' - args = [name, '--type', self.object_type_name, - '--action', 'access_as_shared'] - position_names = ['object_id', 'object_type', - 'target_tenant', 'action'] - position_values = [name, self.object_type_val, '*', - 'access_as_shared'] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_rbac_policy_with_all_params(self): - # Create rbac: rbac_object --type - # --target-tenant tenant_id --action access_as_external - resource = 'rbac_policy' - cmd = rbac.CreateRBACPolicy(test_cli20.MyApp(sys.stdout), None) - name = 'rbac_object' - myid = 'myid' - args = [name, '--type', self.object_type_name, - '--target-tenant', 'tenant_id', - '--action', 'access_as_external'] - position_names = ['object_id', 'object_type', - 'target_tenant', 'action'] - position_values = [name, self.object_type_val, 'tenant_id', - 'access_as_external'] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_rbac_policy_with_unicode(self): - # Create rbac policy u'\u7f51\u7edc'. - resource = 'rbac_policy' - cmd = rbac.CreateRBACPolicy(test_cli20.MyApp(sys.stdout), None) - name = u'\u7f51\u7edc' - myid = 'myid' - args = [name, '--type', self.object_type_name, - '--target-tenant', 'tenant_id', - '--action', 'access_as_external'] - position_names = ['object_id', 'object_type', - 'target_tenant', 'action'] - position_values = [name, self.object_type_val, 'tenant_id', - 'access_as_external'] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_update_rbac_policy(self): - # rbac-update --target-tenant . - resource = 'rbac_policy' - cmd = rbac.UpdateRBACPolicy(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--target-tenant', 'tenant_id'], - {'target_tenant': 'tenant_id', }) - - def test_delete_rbac_policy(self): - # rbac-delete my-id. - resource = 'rbac_policy' - cmd = rbac.DeleteRBACPolicy(test_cli20.MyApp(sys.stdout), None) - my_id = 'myid1' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args) - - def test_list_rbac_policies(self): - # rbac-list. - resources = "rbac_policies" - cmd = rbac.ListRBACPolicy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_rbac_policies_pagination(self): - # rbac-list with pagination. - resources = "rbac_policies" - cmd = rbac.ListRBACPolicy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_rbac_policies_sort(self): - # sorted list: - # rbac-list --sort-key name --sort-key id --sort-key asc - # --sort-key desc - resources = "rbac_policies" - cmd = rbac.ListRBACPolicy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_rbac_policies_limit(self): - # size (1000) limited list: rbac-list -P. - resources = "rbac_policies" - cmd = rbac.ListRBACPolicy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_rbac_policy(self): - # rbac-show test_id. - resource = 'rbac_policy' - cmd = rbac.ShowRBACPolicy(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id']) diff --git a/neutronclient/tests/unit/test_cli20_router.py b/neutronclient/tests/unit/test_cli20_router.py deleted file mode 100644 index ba267f77e..000000000 --- a/neutronclient/tests/unit/test_cli20_router.py +++ /dev/null @@ -1,434 +0,0 @@ -# Copyright 2012 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 sys - -from neutronclient.common import exceptions -from neutronclient.neutron.v2_0 import router -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20RouterJSON(test_cli20.CLITestV20Base): - def test_create_router(self): - # Create router: router1. - resource = 'router' - cmd = router.CreateRouter(test_cli20.MyApp(sys.stdout), None) - name = 'router1' - myid = 'myid' - args = [name, '--description', 'rooter'] - position_names = ['name', ] - position_values = [name, ] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - description='rooter') - - def test_create_router_flavor(self): - resource = 'router' - cmd = router.CreateRouter(test_cli20.MyApp(sys.stdout), None) - name = 'router1' - myid = 'myid' - flavor = 'router-flavor' - args = [name, '--flavor', flavor] - position_names = ['name', ] - position_values = [name, flavor] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - flavor_id='router-flavor') - - def test_create_router_tenant(self): - # Create router: --tenant_id tenantid myname. - resource = 'router' - cmd = router.CreateRouter(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - args = ['--tenant_id', 'tenantid', name] - position_names = ['name', ] - position_values = [name, ] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_router_admin_state(self): - # Create router: --admin_state_down myname. - resource = 'router' - cmd = router.CreateRouter(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - args = ['--admin_state_down', name, ] - position_names = ['name', ] - position_values = [name, ] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - admin_state_up=False) - - def _create_router_distributed_or_ha(self, distributed=None, ha=None): - # Create router: --distributed distributed --ha ha myname. - resource = 'router' - cmd = router.CreateRouter(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - args = [] - if distributed is not None: - args += ['--distributed', str(distributed)] - if ha is not None: - args += ['--ha', str(ha)] - args.append(name) - position_names = ['name', ] - position_values = [name, ] - expected = {} - if distributed is not None: - expected['distributed'] = str(distributed) - if ha is not None: - expected['ha'] = str(ha) - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - **expected) - - def test_create_router_distributed_True(self): - # Create router: --distributed=True. - self._create_router_distributed_or_ha(distributed='True') - - def test_create_router_ha_with_True(self): - self._create_router_distributed_or_ha(ha='True') - - def test_create_router_ha_with_true(self): - self._create_router_distributed_or_ha(ha='true') - - def test_create_router_ha_with_False(self): - self._create_router_distributed_or_ha(ha='False') - - def test_create_router_ha_with_false(self): - self._create_router_distributed_or_ha(ha='false') - - def test_create_router_distributed_False(self): - # Create router: --distributed=False. - self._create_router_distributed_or_ha(distributed='False') - - def test_create_router_distributed_true(self): - # Create router: --distributed=true. - self._create_router_distributed_or_ha(distributed='true') - - def test_create_router_distributed_false(self): - # Create router: --distributed=false. - self._create_router_distributed_or_ha(distributed='false') - - def test_create_router_with_az_hint(self): - # Create router: --availability-zone-hint zone1 - # --availability-zone-hint zone2. - resource = 'router' - cmd = router.CreateRouter(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - args = ['--availability-zone-hint', 'zone1', - '--availability-zone-hint', 'zone2', name] - position_names = ['availability_zone_hints', 'name'] - position_values = [['zone1', 'zone2'], name] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_list_routers_detail(self): - # list routers: -D. - resources = "routers" - cmd = router.ListRouter(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_routers_pagination(self): - resources = "routers" - cmd = router.ListRouter(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_routers_sort(self): - # list routers: - # --sort-key name --sort-key id --sort-key asc --sort-key desc - resources = "routers" - cmd = router.ListRouter(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_routers_limit(self): - # list routers: -P. - resources = "routers" - cmd = router.ListRouter(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_update_router_exception(self): - # Update router: myid. - resource = 'router' - cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None) - self.assertRaises(exceptions.CommandError, self._test_update_resource, - resource, cmd, 'myid', ['myid'], {}) - - def test_update_router(self): - # Update router: myid --name myname --tags a b. - resource = 'router' - cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'myname', - '--description', ':D'], - {'name': 'myname', 'description': ':D'}) - - def test_update_router_admin_state(self): - # Update router: myid --admin-state-up . - resource = 'router' - cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--admin-state-up', 'True'], - {'admin_state_up': 'True'} - ) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--admin-state-up', 'true'], - {'admin_state_up': 'true'} - ) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--admin-state-up', 'False'], - {'admin_state_up': 'False'} - ) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--admin-state-up', 'false'], - {'admin_state_up': 'false'} - ) - - def test_update_router_distributed(self): - # Update router: myid --distributed . - resource = 'router' - cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--distributed', 'True'], - {'distributed': 'True'} - ) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--distributed', 'true'], - {'distributed': 'true'} - ) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--distributed', 'False'], - {'distributed': 'False'} - ) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--distributed', 'false'], - {'distributed': 'false'} - ) - - def test_update_router_no_routes(self): - # Update router: myid --no-routes - resource = 'router' - cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--no-routes'], - {'routes': None}) - - def test_update_router_add_route(self): - # Update router: myid --route destination=10.0.3.0/24,nexthop=10.0.0.10 - resource = 'router' - cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid, - '--route', - 'destination=10.0.3.0/24,nexthop=10.0.0.10'] - routes = [{'destination': '10.0.3.0/24', - 'nexthop': '10.0.0.10'}] - updatefields = {'routes': routes} - self._test_update_resource(resource, cmd, myid, args, updatefields) - - def test_update_router_add_routes(self): - # Update router: myid --route destination=10.0.3.0/24,nexthop=10.0.0.10 - # --route destination=fd7a:1d63:2063::/64, - # nexthop=fd7a:1d63:2063:0:f816:3eff:fe0e:a697 - resource = 'router' - cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid, - '--route', - 'destination=10.0.3.0/24,nexthop=10.0.0.10', - '--route', - 'destination=fd7a:1d63:2063::/64,' - 'nexthop=fd7a:1d63:2063:0:f816:3eff:fe0e:a697'] - routes = [{'destination': '10.0.3.0/24', - 'nexthop': '10.0.0.10'}, - {'destination': 'fd7a:1d63:2063::/64', - 'nexthop': 'fd7a:1d63:2063:0:f816:3eff:fe0e:a697'}] - updatefields = {'routes': routes} - self._test_update_resource(resource, cmd, myid, args, updatefields) - - def test_update_router_no_routes_with_add_route(self): - # Update router: --no-routes with --route - resource = 'router' - cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid, - '--no-routes', - '--route', - 'destination=10.0.3.0/24,nexthop=10.0.0.10'] - exception = self.assertRaises(SystemExit, - self._test_update_resource, - resource, cmd, myid, args, None) - self.assertEqual(2, exception.code) - - def test_delete_router(self): - # Delete router: myid. - resource = 'router' - cmd = router.DeleteRouter(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) - - def test_show_router(self): - # Show router: myid. - resource = 'router' - cmd = router.ShowRouter(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, - ['id', 'name']) - - def _test_add_remove_interface(self, action, mode, cmd, args): - resource = 'router' - subcmd = '%s_router_interface' % action - if mode == 'port': - body = {'port_id': 'portid'} - else: - body = {'subnet_id': 'subnetid'} - if action == 'add': - retval = {'subnet_id': 'subnetid', 'port_id': 'portid'} - retval = self.client.serialize(retval) - expected_code = 200 - else: - retval = None - expected_code = 204 - self._test_update_resource_action(resource, cmd, 'myid', - subcmd, args, - body, expected_code, retval) - - def test_add_interface_compat(self): - # Add interface to router: myid subnetid. - cmd = router.AddInterfaceRouter(test_cli20.MyApp(sys.stdout), None) - args = ['myid', 'subnetid'] - self._test_add_remove_interface('add', 'subnet', cmd, args) - - def test_add_interface_by_subnet(self): - # Add interface to router: myid subnet=subnetid. - cmd = router.AddInterfaceRouter(test_cli20.MyApp(sys.stdout), None) - args = ['myid', 'subnet=subnetid'] - self._test_add_remove_interface('add', 'subnet', cmd, args) - - def test_add_interface_by_port(self): - # Add interface to router: myid port=portid. - cmd = router.AddInterfaceRouter(test_cli20.MyApp(sys.stdout), None) - args = ['myid', 'port=portid'] - self._test_add_remove_interface('add', 'port', cmd, args) - - def test_del_interface_compat(self): - # Delete interface from router: myid subnetid. - cmd = router.RemoveInterfaceRouter(test_cli20.MyApp(sys.stdout), None) - args = ['myid', 'subnetid'] - self._test_add_remove_interface('remove', 'subnet', cmd, args) - - def test_del_interface_by_subnet(self): - # Delete interface from router: myid subnet=subnetid. - cmd = router.RemoveInterfaceRouter(test_cli20.MyApp(sys.stdout), None) - args = ['myid', 'subnet=subnetid'] - self._test_add_remove_interface('remove', 'subnet', cmd, args) - - def test_del_interface_by_port(self): - # Delete interface from router: myid port=portid. - cmd = router.RemoveInterfaceRouter(test_cli20.MyApp(sys.stdout), None) - args = ['myid', 'port=portid'] - self._test_add_remove_interface('remove', 'port', cmd, args) - - def test_set_gateway(self): - # Set external gateway for router: myid externalid. - resource = 'router' - cmd = router.SetGatewayRouter(test_cli20.MyApp(sys.stdout), None) - args = ['myid', 'externalid'] - self._test_update_resource(resource, cmd, 'myid', - args, - {"external_gateway_info": - {"network_id": "externalid"}} - ) - - def test_set_gateway_enable_snat(self): - # enable external gateway for router: myid externalid. - resource = 'router' - cmd = router.SetGatewayRouter(test_cli20.MyApp(sys.stdout), None) - args = ['myid', 'externalid', '--enable-snat'] - self._test_update_resource(resource, cmd, 'myid', - args, - {"external_gateway_info": - {"network_id": "externalid", - "enable_snat": True}} - ) - - def test_set_gateway_disable_snat(self): - # set external gateway for router: myid externalid. - resource = 'router' - cmd = router.SetGatewayRouter(test_cli20.MyApp(sys.stdout), None) - args = ['myid', 'externalid', '--disable-snat'] - self._test_update_resource(resource, cmd, 'myid', - args, - {"external_gateway_info": - {"network_id": "externalid", - "enable_snat": False}} - ) - - def test_set_gateway_external_ip(self): - # set external gateway for router: myid externalid --fixed-ip ... - resource = 'router' - cmd = router.SetGatewayRouter(test_cli20.MyApp(sys.stdout), None) - args = ['myid', 'externalid', '--fixed-ip', 'ip_address=10.0.0.2'] - self._test_update_resource(resource, cmd, 'myid', - args, - {"external_gateway_info": - {"network_id": "externalid", - "external_fixed_ips": [ - {"ip_address": "10.0.0.2"}]}} - ) - - def test_set_gateway_external_subnet(self): - # set external gateway for router: myid externalid --fixed-ip ... - resource = 'router' - cmd = router.SetGatewayRouter(test_cli20.MyApp(sys.stdout), None) - args = ['myid', 'externalid', '--fixed-ip', 'subnet_id=mysubnet'] - self._test_update_resource(resource, cmd, 'myid', - args, - {"external_gateway_info": - {"network_id": "externalid", - "external_fixed_ips": [ - {"subnet_id": "mysubnet"}]}} - ) - - def test_set_gateway_external_ip_and_subnet(self): - # set external gateway for router: myid externalid --fixed-ip ... - resource = 'router' - cmd = router.SetGatewayRouter(test_cli20.MyApp(sys.stdout), None) - args = ['myid', 'externalid', '--fixed-ip', - 'ip_address=10.0.0.2,subnet_id=mysubnet'] - self._test_update_resource(resource, cmd, 'myid', - args, - {"external_gateway_info": - {"network_id": "externalid", - "external_fixed_ips": [ - {"subnet_id": "mysubnet", - "ip_address": "10.0.0.2"}]}} - ) - - def test_remove_gateway(self): - # Remove external gateway from router: externalid. - resource = 'router' - cmd = router.RemoveGatewayRouter(test_cli20.MyApp(sys.stdout), None) - args = ['externalid'] - self._test_update_resource(resource, cmd, 'externalid', - args, {"external_gateway_info": {}} - ) diff --git a/neutronclient/tests/unit/test_cli20_securitygroup.py b/neutronclient/tests/unit/test_cli20_securitygroup.py deleted file mode 100644 index 95a578f63..000000000 --- a/neutronclient/tests/unit/test_cli20_securitygroup.py +++ /dev/null @@ -1,657 +0,0 @@ -# Copyright 2012 Red Hat -# 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 sys -from unittest import mock -import urllib.parse as urlparse - -from oslo_utils import uuidutils - -from neutronclient.common import exceptions -from neutronclient.common import utils -from neutronclient.neutron.v2_0 import securitygroup -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20SecurityGroupsJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['security_group', 'security_group_rule'] - - def test_create_security_group(self): - # Create security group: webservers. - resource = 'security_group' - cmd = securitygroup.CreateSecurityGroup( - test_cli20.MyApp(sys.stdout), None) - name = 'webservers' - myid = 'myid' - args = [name, ] - position_names = ['name'] - position_values = [name] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_security_group_tenant(self): - # Create security group: webservers. - resource = 'security_group' - cmd = securitygroup.CreateSecurityGroup( - test_cli20.MyApp(sys.stdout), None) - name = 'webservers' - description = 'my webservers' - myid = 'myid' - args = ['--tenant_id', 'tenant_id', '--description', description, name] - position_names = ['name', 'description'] - position_values = [name, description] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenant_id') - - def test_create_security_group_with_description(self): - # Create security group: webservers. - resource = 'security_group' - cmd = securitygroup.CreateSecurityGroup( - test_cli20.MyApp(sys.stdout), None) - name = 'webservers' - description = 'my webservers' - myid = 'myid' - args = [name, '--description', description] - position_names = ['name', 'description'] - position_values = [name, description] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_list_security_groups(self): - resources = "security_groups" - cmd = securitygroup.ListSecurityGroup( - test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_security_groups_pagination(self): - resources = "security_groups" - cmd = securitygroup.ListSecurityGroup( - test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_security_groups_sort(self): - resources = "security_groups" - cmd = securitygroup.ListSecurityGroup( - test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_security_groups_limit(self): - resources = "security_groups" - cmd = securitygroup.ListSecurityGroup( - test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_security_group_id(self): - resource = 'security_group' - cmd = securitygroup.ShowSecurityGroup( - test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id']) - - def test_show_security_group_id_name(self): - resource = 'security_group' - cmd = securitygroup.ShowSecurityGroup( - test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name']) - - def test_delete_security_group(self): - # Delete security group: myid. - resource = 'security_group' - cmd = securitygroup.DeleteSecurityGroup( - test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) - - def test_update_security_group(self): - # Update security group: myid --name myname --description desc. - resource = 'security_group' - cmd = securitygroup.UpdateSecurityGroup( - test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'myname', - '--description', 'mydescription'], - {'name': 'myname', - 'description': 'mydescription'} - ) - - def test_update_security_group_with_unicode(self): - resource = 'security_group' - cmd = securitygroup.UpdateSecurityGroup( - test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', u'\u7f51\u7edc', - '--description', u'\u7f51\u7edc'], - {'name': u'\u7f51\u7edc', - 'description': u'\u7f51\u7edc'} - ) - - def test_create_security_group_rule_full(self): - # Create security group rule. - resource = 'security_group_rule' - cmd = securitygroup.CreateSecurityGroupRule( - test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - direction = 'ingress' - ethertype = 'IPv4' - protocol = 'tcp' - port_range_min = '22' - port_range_max = '22' - remote_ip_prefix = '10.0.0.0/24' - security_group_id = '1' - remote_group_id = '1' - args = ['--remote_ip_prefix', remote_ip_prefix, '--direction', - direction, '--ethertype', ethertype, '--protocol', protocol, - '--port_range_min', port_range_min, '--port_range_max', - port_range_max, '--remote_group_id', remote_group_id, - security_group_id, '--description', 'PCI policy 1421912'] - position_names = ['remote_ip_prefix', 'direction', 'ethertype', - 'protocol', 'port_range_min', 'port_range_max', - 'remote_group_id', 'security_group_id'] - position_values = [remote_ip_prefix, direction, ethertype, protocol, - port_range_min, port_range_max, remote_group_id, - security_group_id] - self._test_create_resource(resource, cmd, None, myid, args, - position_names, position_values, - description='PCI policy 1421912') - - def test_create_security_group_rule_with_integer_protocol_value(self): - resource = 'security_group_rule' - cmd = securitygroup.CreateSecurityGroupRule( - test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - direction = 'ingress' - ethertype = 'IPv4' - protocol = '2' - port_range_min = '22' - port_range_max = '22' - remote_ip_prefix = '10.0.0.0/24' - security_group_id = '1' - remote_group_id = '1' - args = ['--remote_ip_prefix', remote_ip_prefix, '--direction', - direction, '--ethertype', ethertype, '--protocol', protocol, - '--port_range_min', port_range_min, '--port_range_max', - port_range_max, '--remote_group_id', remote_group_id, - security_group_id] - position_names = ['remote_ip_prefix', 'direction', 'ethertype', - 'protocol', 'port_range_min', 'port_range_max', - 'remote_group_id', 'security_group_id'] - position_values = [remote_ip_prefix, direction, ethertype, protocol, - port_range_min, port_range_max, remote_group_id, - security_group_id] - self._test_create_resource(resource, cmd, None, myid, args, - position_names, position_values) - - def test_delete_security_group_rule(self): - # Delete security group rule: myid. - resource = 'security_group_rule' - cmd = securitygroup.DeleteSecurityGroupRule( - test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) - - @mock.patch.object(securitygroup.ListSecurityGroupRule, "extend_list") - def test_list_security_group_rules(self, mock_extend_list): - resources = "security_group_rules" - cmd = securitygroup.ListSecurityGroupRule( - test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - mock_extend_list.assert_called_once_with(test_cli20.IsA(list), - mock.ANY) - - def _build_test_data(self, data, excess=0): - # Length of a query filter on security group rule id - # in these testcases, id='secgroupid%02d' (with len(id)=12) - sec_group_id_filter_len = 12 - - response = [] - replace_rules = {'security_group_id': 'security_group', - 'remote_group_id': 'remote_group'} - - search_opts = {'fields': ['id', 'name']} - sec_group_ids = set() - for rule in data: - for key in replace_rules: - if rule.get(key): - sec_group_ids.add(rule[key]) - response.append({'id': rule[key], 'name': 'default'}) - sec_group_ids = list(sec_group_ids) - - result = [] - - sec_group_count = len(sec_group_ids) - max_size = ((sec_group_id_filter_len * sec_group_count) - excess) - chunk_size = max_size // sec_group_id_filter_len - - for i in range(0, sec_group_count, chunk_size): - search_opts['id'] = sec_group_ids[i: i + chunk_size] - params = utils.safe_encode_dict(search_opts) - resp_str = self.client.serialize({'security_groups': response}) - - result.append({ - 'filter': urlparse.urlencode(params, doseq=1), - 'response': (test_cli20.MyResp(200), resp_str), - }) - - return result - - def test_extend_list(self): - data = [{'name': 'default', - 'remote_group_id': 'remgroupid%02d' % i} - for i in range(10)] - data.append({'name': 'default', 'remote_group_id': None}) - resources = "security_groups" - - cmd = securitygroup.ListSecurityGroupRule( - test_cli20.MyApp(sys.stdout), None) - - path = getattr(self.client, resources + '_path') - responses = self._build_test_data(data) - known_args, _vs = cmd.get_parser( - 'list' + resources).parse_known_args() - resp = responses[0]['response'] - - with mock.patch.object(cmd, "get_client", - return_value=self.client) as mock_get_client, \ - mock.patch.object(self.client.httpclient, "request", - return_value=resp) as mock_request: - cmd.extend_list(data, known_args) - - mock_get_client.assert_called_once_with() - mock_request.assert_called_once_with( - test_cli20.MyUrlComparator(test_cli20.end_url( - path, responses[0]['filter']), self.client), - 'GET', - body=None, - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN})) - - def test_extend_list_exceed_max_uri_len(self): - data = [{'name': 'default', - 'security_group_id': 'secgroupid%02d' % i, - 'remote_group_id': 'remgroupid%02d' % i} - for i in range(10)] - data.append({'name': 'default', - 'security_group_id': 'secgroupid10', - 'remote_group_id': None}) - resources = "security_groups" - - cmd = securitygroup.ListSecurityGroupRule( - test_cli20.MyApp(sys.stdout), None) - path = getattr(self.client, resources + '_path') - responses = self._build_test_data(data, excess=1) - - known_args, _vs = cmd.get_parser( - 'list' + resources).parse_known_args() - mock_request_side_effects = [] - mock_request_calls = [] - mock_check_uri_side_effects = [exceptions.RequestURITooLong(excess=1)] - mock_check_uri_calls = [mock.call(mock.ANY)] - for item in responses: - mock_request_side_effects.append(item['response']) - mock_request_calls.append(mock.call( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, item['filter']), self.client), - 'GET', - body=None, - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN}))) - mock_check_uri_side_effects.append(None) - mock_check_uri_calls.append(mock.call(mock.ANY)) - - with mock.patch.object(cmd, "get_client", - return_value=self.client) as mock_get_client, \ - mock.patch.object(self.client.httpclient, - "request") as mock_request, \ - mock.patch.object(self.client.httpclient, - "_check_uri_length") as mock_check_uri: - mock_request.side_effect = mock_request_side_effects - mock_check_uri.side_effect = mock_check_uri_side_effects - cmd.extend_list(data, known_args) - - mock_get_client.assert_called_once_with() - mock_request.assert_has_calls(mock_request_calls) - mock_check_uri.assert_has_calls(mock_check_uri_calls) - self.assertEqual(len(mock_request_calls), mock_request.call_count) - self.assertEqual(len(mock_check_uri_calls), mock_check_uri.call_count) - - @mock.patch.object(securitygroup.ListSecurityGroupRule, "extend_list") - def test_list_security_group_rules_pagination(self, mock_extend_list): - resources = "security_group_rules" - cmd = securitygroup.ListSecurityGroupRule( - test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - mock_extend_list.assert_called_once_with(test_cli20.IsA(list), - mock.ANY) - - @mock.patch.object(securitygroup.ListSecurityGroupRule, "extend_list") - def test_list_security_group_rules_sort(self, mock_extend_list): - resources = "security_group_rules" - cmd = securitygroup.ListSecurityGroupRule( - test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - mock_extend_list.assert_called_once_with(test_cli20.IsA(list), - mock.ANY) - - @mock.patch.object(securitygroup.ListSecurityGroupRule, "extend_list") - def test_list_security_group_rules_limit(self, mock_extend_list): - resources = "security_group_rules" - cmd = securitygroup.ListSecurityGroupRule( - test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000) - mock_extend_list.assert_called_once_with(test_cli20.IsA(list), - mock.ANY) - - def test_show_security_group_rule(self): - resource = 'security_group_rule' - cmd = securitygroup.ShowSecurityGroupRule( - test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id']) - - def _test_list_security_group_rules_extend(self, api_data, expected, - args=(), conv=True, - query_fields=None): - def setup_list_stub(resources, data, query, mock_calls, mock_returns): - reses = {resources: data} - resstr = self.client.serialize(reses) - resp = (test_cli20.MyResp(200), resstr) - path = getattr(self.client, resources + '_path') - mock_calls.append(mock.call( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, query), - self.client), - 'GET', - body=None, - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN}))) - mock_returns.append(resp) - - cmd = securitygroup.ListSecurityGroupRule( - test_cli20.MyApp(sys.stdout), None) - query = '' - if query_fields: - query = '&'.join(['fields=' + f for f in query_fields]) - mock_request_calls = [] - mock_request_returns = [] - setup_list_stub('security_group_rules', api_data, query, - mock_request_calls, mock_request_returns) - if conv: - sec_ids = set() - for n in api_data: - sec_ids.add(n['security_group_id']) - if n.get('remote_group_id'): - sec_ids.add(n['remote_group_id']) - filters = '' - for id in sec_ids: - filters = filters + "&id=%s" % id - setup_list_stub('security_groups', - [{'id': 'myid1', 'name': 'group1'}, - {'id': 'myid2', 'name': 'group2'}, - {'id': 'myid3', 'name': 'group3'}], - 'fields=id&fields=name' + filters, - mock_request_calls, - mock_request_returns) - - cmd_parser = cmd.get_parser('list_security_group_rules') - parsed_args = cmd_parser.parse_args(args) - - with mock.patch.object(cmd, "get_client", - return_value=self.client) as mock_get_client, \ - mock.patch.object(self.client.httpclient, - "request") as mock_request: - mock_request.side_effect = mock_request_returns - result = cmd.take_action(parsed_args) - - self.assert_mock_multiple_calls_with_same_arguments( - mock_get_client, mock.call(), None) - mock_request.assert_has_calls(mock_request_calls) - self.assertEqual(len(mock_request_calls), mock_request.call_count) - self.assertEqual(expected['cols'], result[0]) - # Check data - _result = [x for x in result[1]] - self.assertEqual(len(expected['data']), len(_result)) - for res, exp in zip(_result, expected['data']): - self.assertEqual(len(exp), len(res)) - self.assertEqual(exp, res) - - def _test_list_security_group_rules_extend_sg_name( - self, expected_mode=None, args=(), conv=True, query_field=False): - if query_field: - field_filters = ['id', 'security_group_id', - 'remote_ip_prefix', 'remote_group_id'] - else: - field_filters = None - - data = [self._prepare_rule(rule_id='ruleid1', sg_id='myid1', - remote_group_id='myid1', - filters=field_filters), - self._prepare_rule(rule_id='ruleid2', sg_id='myid2', - remote_group_id='myid3', - filters=field_filters), - self._prepare_rule(rule_id='ruleid3', sg_id='myid2', - remote_group_id='myid2', - filters=field_filters), - ] - - if expected_mode == 'noconv': - expected = {'cols': ['id', 'security_group_id', 'remote_group_id'], - 'data': [('ruleid1', 'myid1', 'myid1'), - ('ruleid2', 'myid2', 'myid3'), - ('ruleid3', 'myid2', 'myid2')]} - elif expected_mode == 'remote_group_id': - expected = {'cols': ['id', 'security_group', 'remote_group'], - 'data': [('ruleid1', 'group1', 'group1'), - ('ruleid2', 'group2', 'group3'), - ('ruleid3', 'group2', 'group2')]} - else: - expected = {'cols': ['id', 'security_group', 'remote'], - 'data': [('ruleid1', 'group1', 'group1 (group)'), - ('ruleid2', 'group2', 'group3 (group)'), - ('ruleid3', 'group2', 'group2 (group)')]} - - self._test_list_security_group_rules_extend( - data, expected, args=args, conv=conv, query_fields=field_filters) - - def test_list_security_group_rules_extend_remote_sg_name(self): - args = '-c id -c security_group -c remote'.split() - self._test_list_security_group_rules_extend_sg_name(args=args) - - def test_list_security_group_rules_extend_sg_name_noconv(self): - args = '--no-nameconv -c id -c security_group_id -c remote_group_id' - args = args.split() - self._test_list_security_group_rules_extend_sg_name( - expected_mode='noconv', args=args, conv=False) - - def test_list_security_group_rules_extend_sg_name_with_columns(self): - args = '-c id -c security_group_id -c remote_group_id'.split() - self._test_list_security_group_rules_extend_sg_name( - expected_mode='remote_group_id', args=args) - - def test_list_security_group_rules_extend_sg_name_with_columns_no_id(self): - args = '-c id -c security_group -c remote_group'.split() - self._test_list_security_group_rules_extend_sg_name( - expected_mode='remote_group_id', args=args) - - def test_list_security_group_rules_extend_sg_name_with_fields(self): - # NOTE: remote_ip_prefix is required to show "remote" column - args = ('-F id -F security_group_id ' - '-F remote_ip_prefix -F remote_group_id').split() - self._test_list_security_group_rules_extend_sg_name( - args=args, query_field=True) - - def test_list_security_group_rules_extend_sg_name_with_fields_no_id(self): - # NOTE: remote_ip_prefix is required to show "remote" column - args = ('-F id -F security_group ' - '-F remote_ip_prefix -F remote_group').split() - self._test_list_security_group_rules_extend_sg_name(args=args, - query_field=True) - - def test_list_security_group_rules_extend_remote(self): - args = '-c id -c security_group -c remote'.split() - - data = [self._prepare_rule(rule_id='ruleid1', sg_id='myid1', - remote_ip_prefix='172.16.18.0/24'), - self._prepare_rule(rule_id='ruleid2', sg_id='myid2', - remote_ip_prefix='172.16.20.0/24'), - self._prepare_rule(rule_id='ruleid3', sg_id='myid2', - remote_group_id='myid3')] - expected = {'cols': ['id', 'security_group', 'remote'], - 'data': [('ruleid1', 'group1', '172.16.18.0/24 (CIDR)'), - ('ruleid2', 'group2', '172.16.20.0/24 (CIDR)'), - ('ruleid3', 'group2', 'group3 (group)')]} - self._test_list_security_group_rules_extend(data, expected, args) - - def test_list_security_group_rules_extend_proto_port(self): - data = [self._prepare_rule(rule_id='ruleid1', sg_id='myid1', - protocol='tcp', - port_range_min=22, port_range_max=22), - self._prepare_rule(rule_id='ruleid2', sg_id='myid2', - direction='egress', ethertype='IPv6', - protocol='udp', - port_range_min=80, port_range_max=81), - self._prepare_rule(rule_id='ruleid3', sg_id='myid2', - protocol='icmp', - remote_ip_prefix='10.2.0.0/16')] - expected = { - 'cols': ['id', 'security_group', 'direction', 'ethertype', - 'port/protocol', 'remote'], - 'data': [ - ('ruleid1', 'group1', 'ingress', 'IPv4', '22/tcp', 'any'), - ('ruleid2', 'group2', 'egress', 'IPv6', '80-81/udp', 'any'), - ('ruleid3', 'group2', 'ingress', 'IPv4', 'icmp', - '10.2.0.0/16 (CIDR)') - ]} - self._test_list_security_group_rules_extend(data, expected) - - def _prepare_rule(self, rule_id=None, sg_id=None, tenant_id=None, - direction=None, ethertype=None, - protocol=None, port_range_min=None, port_range_max=None, - remote_ip_prefix=None, remote_group_id=None, - filters=None): - rule = {'id': rule_id or uuidutils.generate_uuid(), - 'tenant_id': tenant_id or uuidutils.generate_uuid(), - 'security_group_id': sg_id or uuidutils.generate_uuid(), - 'direction': direction or 'ingress', - 'ethertype': ethertype or 'IPv4', - 'protocol': protocol, - 'port_range_min': port_range_min, - 'port_range_max': port_range_max, - 'remote_ip_prefix': remote_ip_prefix, - 'remote_group_id': remote_group_id} - if filters: - return dict([(k, v) for k, v in rule.items() if k in filters]) - else: - return rule - - def test__get_remote_both_unspecified(self): - sg_rule = self._prepare_rule(remote_ip_prefix=None, - remote_group_id=None) - self.assertIsNone(securitygroup._get_remote(sg_rule)) - - def test__get_remote_remote_ip_prefix_specified(self): - sg_rule = self._prepare_rule(remote_ip_prefix='172.16.18.0/24') - self.assertEqual('172.16.18.0/24 (CIDR)', - securitygroup._get_remote(sg_rule)) - - def test__get_remote_remote_group_specified(self): - sg_rule = self._prepare_rule(remote_group_id='sg_id1') - self.assertEqual('sg_id1 (group)', securitygroup._get_remote(sg_rule)) - - def test__get_protocol_port_all_none(self): - sg_rule = self._prepare_rule() - self.assertIsNone(securitygroup._get_protocol_port(sg_rule)) - - def test__get_protocol_port_tcp_all_port(self): - sg_rule = self._prepare_rule(protocol='tcp') - self.assertEqual('tcp', securitygroup._get_protocol_port(sg_rule)) - - def test__get_protocol_port_tcp_one_port(self): - sg_rule = self._prepare_rule(protocol='tcp', - port_range_min=22, port_range_max=22) - self.assertEqual('22/tcp', securitygroup._get_protocol_port(sg_rule)) - - def test__get_protocol_port_tcp_port_range(self): - sg_rule = self._prepare_rule(protocol='tcp', - port_range_min=5000, port_range_max=5010) - self.assertEqual('5000-5010/tcp', - securitygroup._get_protocol_port(sg_rule)) - - def test__get_protocol_port_udp_all_port(self): - sg_rule = self._prepare_rule(protocol='udp') - self.assertEqual('udp', securitygroup._get_protocol_port(sg_rule)) - - def test__get_protocol_port_udp_one_port(self): - sg_rule = self._prepare_rule(protocol='udp', - port_range_min=22, port_range_max=22) - self.assertEqual('22/udp', securitygroup._get_protocol_port(sg_rule)) - - def test__get_protocol_port_udp_port_range(self): - sg_rule = self._prepare_rule(protocol='udp', - port_range_min=5000, port_range_max=5010) - self.assertEqual('5000-5010/udp', - securitygroup._get_protocol_port(sg_rule)) - - def test__get_protocol_port_icmp_all(self): - sg_rule = self._prepare_rule(protocol='icmp') - self.assertEqual('icmp', securitygroup._get_protocol_port(sg_rule)) - - def test_get_ethertype_for_protocol_icmpv6(self): - self.assertEqual('IPv6', - securitygroup.generate_default_ethertype('icmpv6')) - - def test_get_ethertype_for_protocol_icmp(self): - self.assertEqual('IPv4', - securitygroup.generate_default_ethertype('icmp')) - - def test__get_protocol_port_udp_code_type(self): - sg_rule = self._prepare_rule(protocol='icmp', - port_range_min=1, port_range_max=8) - self.assertEqual('icmp (type:1, code:8)', - securitygroup._get_protocol_port(sg_rule)) - - def test__format_sg_rules(self): - rules = [self._prepare_rule(), - self._prepare_rule(protocol='tcp', port_range_min=80, - port_range_max=80), - self._prepare_rule(remote_ip_prefix='192.168.1.0/24'), - self._prepare_rule(remote_group_id='group1'), - self._prepare_rule(protocol='tcp', - remote_ip_prefix='10.1.1.0/24'), - self._prepare_rule(direction='egress'), - self._prepare_rule(direction='egress', ethertype='IPv6'), - ] - sg = {'security_group_rules': rules} - expected_data = ['ingress, IPv4', - 'ingress, IPv4, 80/tcp', - 'ingress, IPv4, remote_ip_prefix: 192.168.1.0/24', - 'ingress, IPv4, remote_group_id: group1', - 'ingress, IPv4, tcp, remote_ip_prefix: 10.1.1.0/24', - 'egress, IPv4', - 'egress, IPv6', - ] - expected = '\n'.join(sorted(expected_data)) - self.assertEqual(expected, securitygroup._format_sg_rules(sg)) diff --git a/neutronclient/tests/unit/test_cli20_servicetype.py b/neutronclient/tests/unit/test_cli20_servicetype.py deleted file mode 100644 index ec9a663fa..000000000 --- a/neutronclient/tests/unit/test_cli20_servicetype.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright 2013 Mirantis 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 sys - -from neutronclient.neutron.v2_0 import servicetype -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20ServiceProvidersJSON(test_cli20.CLITestV20Base): - id_field = "name" - - def setUp(self): - super(CLITestV20ServiceProvidersJSON, self).setUp( - plurals={'tags': 'tag'} - ) - - def test_list_service_providers(self): - resources = "service_providers" - cmd = servicetype.ListServiceProvider(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, True) - - def test_list_service_providers_pagination(self): - resources = "service_providers" - cmd = servicetype.ListServiceProvider(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_service_providers_sort(self): - resources = "service_providers" - cmd = servicetype.ListServiceProvider(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, - sort_key=["name"], - sort_dir=["asc", "desc"]) - - def test_list_service_providers_limit(self): - resources = "service_providers" - cmd = servicetype.ListServiceProvider(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, page_size=1000) diff --git a/neutronclient/tests/unit/test_cli20_subnet.py b/neutronclient/tests/unit/test_cli20_subnet.py deleted file mode 100644 index 97c2faff0..000000000 --- a/neutronclient/tests/unit/test_cli20_subnet.py +++ /dev/null @@ -1,687 +0,0 @@ -# Copyright 2012 OpenStack Foundation. -# 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 sys -from unittest import mock - - -from neutronclient.common import exceptions -from neutronclient.neutron import v2_0 as neutronV20 -from neutronclient.neutron.v2_0 import subnet -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20SubnetJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['subnet'] - - def setUp(self): - super(CLITestV20SubnetJSON, self).setUp(plurals={'tags': 'tag'}) - - def test_create_subnet(self): - # Create subnet: --gateway gateway netid cidr. - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = '10.10.10.0/24' - gateway = 'gatewayvalue' - args = ['--gateway', gateway, netid, cidr, '--description', 'cave'] - position_names = ['ip_version', 'network_id', 'cidr', 'gateway_ip'] - position_values = [4, netid, cidr, gateway] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - description='cave') - - def test_create_subnet_network_cidr_seperated(self): - # For positional value, network_id and cidr can be separated. - # Create subnet: --gateway gateway netid cidr. - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = '10.10.10.0/24' - gateway = 'gatewayvalue' - args = [netid, '--gateway', gateway, cidr] - position_names = ['ip_version', 'network_id', 'cidr', 'gateway_ip'] - position_values = [4, netid, cidr, gateway] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_subnet_with_no_gateway(self): - # Create subnet: --no-gateway netid cidr. - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'cidrvalue' - args = ['--no-gateway', netid, cidr] - position_names = ['ip_version', 'network_id', 'cidr', 'gateway_ip'] - position_values = [4, netid, cidr, None] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_subnet_with_segment(self): - # Create subnet: --segment segment netid cidr. - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = '10.10.10.0/24' - segment = 'segment' - args = ['--segment', segment, netid, cidr, - '--description', 'cave'] - position_names = ['ip_version', 'network_id', 'cidr', 'segment_id'] - position_values = [4, netid, cidr, segment] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - description='cave') - - def test_create_subnet_with_bad_gateway_option(self): - # Create sbunet: --no-gateway netid cidr. - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'cidrvalue' - gateway = 'gatewayvalue' - args = ['--gateway', gateway, '--no-gateway', netid, cidr] - position_names = ['ip_version', 'network_id', 'cidr', 'gateway_ip'] - position_values = [4, netid, cidr, None] - self.assertRaises( - SystemExit, self._test_create_resource, - resource, cmd, name, myid, args, position_names, position_values) - - def _test_create_resource_and_catch_command_error(self, should_fail, - *args): - if should_fail: - params = {'no_api_call': True, - 'expected_exception': exceptions.CommandError} - else: - params = {} - self._test_create_resource(*args, **params) - - def test_create_subnet_with_enable_and_disable_dhcp(self): - # Create subnet: --enable-dhcp and --disable-dhcp. - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'cidrvalue' - position_names = ['ip_version', 'network_id', 'cidr', 'enable_dhcp'] - # enable_dhcp value is appended later inside the loop - position_values = [4, netid, cidr] - for enable_dhcp_arg, should_fail in ( - ('--enable-dhcp=False', False), - ('--enable-dhcp=True', True), - ('--enable-dhcp', True) - ): - tested_args = [enable_dhcp_arg, '--disable-dhcp'] - args = tested_args + [netid, cidr] - pos_values = position_values + [should_fail] - self._test_create_resource_and_catch_command_error( - should_fail, - resource, cmd, name, myid, args, position_names, pos_values) - - def test_create_subnet_with_multiple_enable_dhcp(self): - # Create subnet with multiple --enable-dhcp arguments passed. - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'cidrvalue' - position_names = ['ip_version', 'network_id', 'cidr', 'enable_dhcp'] - # enable_dhcp value is appended later inside the loop - position_values = [4, netid, cidr] - - _ = 'UNUSED_MARKER' - for tested_args, should_fail, pos_value in ( - (['--enable-dhcp', '--enable-dhcp=True'], False, True), - (['--enable-dhcp', '--enable-dhcp=False'], True, _), - (['--enable-dhcp=False', '--enable-dhcp'], True, _), - (['--enable-dhcp=True', '--enable-dhcp=False'], True, _), - (['--enable-dhcp=False', '--enable-dhcp=True'], True, _) - ): - args = tested_args + [netid, cidr] - pos_values = position_values + [pos_value] - self._test_create_resource_and_catch_command_error( - should_fail, - resource, cmd, name, myid, args, position_names, pos_values) - - def test_create_subnet_tenant(self): - # Create subnet: --tenant_id tenantid netid cidr. - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', netid, cidr] - position_names = ['ip_version', 'network_id', 'cidr'] - position_values = [4, netid, cidr] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_subnet_tags(self): - # Create subnet: netid cidr --tags a b. - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = [netid, cidr, '--tags', 'a', 'b'] - position_names = ['ip_version', 'network_id', 'cidr'] - position_values = [4, netid, cidr] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tags=['a', 'b']) - - def test_create_subnet_allocation_pool(self): - # Create subnet: --tenant_id tenantid netid cidr. - # The is --allocation_pool start=1.1.1.10,end=1.1.1.20 - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', - '--allocation_pool', 'start=1.1.1.10,end=1.1.1.20', - netid, cidr] - position_names = ['ip_version', 'allocation_pools', 'network_id', - 'cidr'] - pool = [{'start': '1.1.1.10', 'end': '1.1.1.20'}] - position_values = [4, pool, netid, cidr] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_subnet_allocation_pools(self): - # Create subnet: --tenant-id tenantid netid cidr. - # The are --allocation_pool start=1.1.1.10,end=1.1.1.20 and - # --allocation_pool start=1.1.1.30,end=1.1.1.40 - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', - '--allocation_pool', 'start=1.1.1.10,end=1.1.1.20', - '--allocation_pool', 'start=1.1.1.30,end=1.1.1.40', - netid, cidr] - position_names = ['ip_version', 'allocation_pools', 'network_id', - 'cidr'] - pools = [{'start': '1.1.1.10', 'end': '1.1.1.20'}, - {'start': '1.1.1.30', 'end': '1.1.1.40'}] - position_values = [4, pools, netid, cidr] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_subnet_host_route(self): - # Create subnet: --tenant_id tenantid netid cidr. - # The is - # --host-route destination=172.16.1.0/24,nexthop=1.1.1.20 - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', - '--host-route', 'destination=172.16.1.0/24,nexthop=1.1.1.20', - netid, cidr] - position_names = ['ip_version', 'host_routes', 'network_id', - 'cidr'] - route = [{'destination': '172.16.1.0/24', 'nexthop': '1.1.1.20'}] - position_values = [4, route, netid, cidr] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_subnet_host_routes(self): - # Create subnet: --tenant-id tenantid netid cidr. - # The are - # --host-route destination=172.16.1.0/24,nexthop=1.1.1.20 and - # --host-route destination=172.17.7.0/24,nexthop=1.1.1.40 - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', - '--host-route', 'destination=172.16.1.0/24,nexthop=1.1.1.20', - '--host-route', 'destination=172.17.7.0/24,nexthop=1.1.1.40', - netid, cidr] - position_names = ['ip_version', 'host_routes', 'network_id', - 'cidr'] - routes = [{'destination': '172.16.1.0/24', 'nexthop': '1.1.1.20'}, - {'destination': '172.17.7.0/24', 'nexthop': '1.1.1.40'}] - position_values = [4, routes, netid, cidr] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_subnet_dns_nameservers(self): - # Create subnet: --tenant-id tenantid netid cidr. - # The are - # --dns-nameserver 1.1.1.20 and --dns-nameserver 1.1.1.40 - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', - '--dns-nameserver', '1.1.1.20', - '--dns-nameserver', '1.1.1.40', - netid, cidr] - position_names = ['ip_version', 'dns_nameservers', 'network_id', - 'cidr'] - nameservers = ['1.1.1.20', '1.1.1.40'] - position_values = [4, nameservers, netid, cidr] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_subnet_with_use_default_subnetpool(self): - # Create subnet: --tenant-id tenantid --use-default-subnetpool \ - # netid cidr. - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', - '--use-default-subnetpool', - netid, cidr] - position_names = ['ip_version', 'use_default_subnetpool', 'network_id', - 'cidr'] - position_values = [4, True, netid, cidr] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_subnet_with_disable_dhcp(self): - # Create subnet: --tenant-id tenantid --disable-dhcp netid cidr. - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', - '--disable-dhcp', - netid, cidr] - position_names = ['ip_version', 'enable_dhcp', 'network_id', - 'cidr'] - position_values = [4, False, netid, cidr] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_subnet_merge_single_plurar(self): - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', - '--allocation-pool', 'start=1.1.1.10,end=1.1.1.20', - netid, cidr, - '--allocation-pools', 'list=true', 'type=dict', - 'start=1.1.1.30,end=1.1.1.40'] - position_names = ['ip_version', 'allocation_pools', 'network_id', - 'cidr'] - pools = [{'start': '1.1.1.10', 'end': '1.1.1.20'}, - {'start': '1.1.1.30', 'end': '1.1.1.40'}] - position_values = [4, pools, netid, cidr] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_subnet_merge_plurar(self): - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', - netid, cidr, - '--allocation-pools', 'list=true', 'type=dict', - 'start=1.1.1.30,end=1.1.1.40'] - position_names = ['ip_version', 'allocation_pools', 'network_id', - 'cidr'] - pools = [{'start': '1.1.1.30', 'end': '1.1.1.40'}] - position_values = [4, pools, netid, cidr] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_subnet_merge_single_single(self): - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', - '--allocation-pool', 'start=1.1.1.10,end=1.1.1.20', - netid, cidr, - '--allocation-pool', - 'start=1.1.1.30,end=1.1.1.40'] - position_names = ['ip_version', 'allocation_pools', 'network_id', - 'cidr'] - pools = [{'start': '1.1.1.10', 'end': '1.1.1.20'}, - {'start': '1.1.1.30', 'end': '1.1.1.40'}] - position_values = [4, pools, netid, cidr] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_subnet_max_v4_cidr(self): - # Create subnet: --gateway gateway netid cidr. - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = '192.168.0.1/32' - gateway = 'gatewayvalue' - args = ['--gateway', gateway, netid, cidr] - position_names = ['ip_version', 'network_id', 'cidr', 'gateway_ip'] - position_values = [4, netid, cidr, gateway] - with mock.patch.object(cmd.log, 'warning') as mock_warning: - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - mock_warning.assert_called_once_with(mock.ANY, - {'ip': 4, 'cidr': '/32'}) - - def test_create_subnet_with_ipv6_ra_mode(self): - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', - '--ip-version', '6', - '--ipv6-ra-mode', 'dhcpv6-stateful', - netid, cidr] - position_names = ['ip_version', 'ipv6_ra_mode', - 'network_id', 'cidr'] - position_values = [6, 'dhcpv6-stateful', netid, cidr] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_subnet_with_ipv6_address_mode(self): - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', - '--ip-version', '6', - '--ipv6-address-mode', 'dhcpv6-stateful', - netid, cidr] - position_names = ['ip_version', 'ipv6_address_mode', - 'network_id', 'cidr'] - position_values = [6, 'dhcpv6-stateful', netid, cidr] - - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_subnet_with_ipv6_modes(self): - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', - '--ip-version', '6', - '--ipv6-address-mode', 'slaac', - '--ipv6-ra-mode', 'slaac', - netid, cidr] - position_names = ['ip_version', 'ipv6_address_mode', - 'ipv6_ra_mode', 'network_id', 'cidr'] - position_values = [6, 'slaac', 'slaac', netid, cidr] - - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_subnet_with_ipv6_ra_mode_ipv4(self): - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', - '--ip-version', '4', - '--ipv6-ra-mode', 'slaac', - netid, cidr] - position_names = ['ip_version', 'ipv6_ra_mode', - 'network_id', 'cidr'] - position_values = [4, None, netid, cidr] - self._test_create_resource( - resource, cmd, name, myid, args, position_names, - position_values, tenant_id='tenantid', - no_api_call=True, expected_exception=exceptions.CommandError) - - def test_create_subnet_with_ipv6_address_mode_ipv4(self): - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', - '--ip-version', '4', - '--ipv6-address-mode', 'slaac', - netid, cidr] - position_names = ['ip_version', 'ipv6_address_mode', - 'network_id', 'cidr'] - position_values = [4, None, netid, cidr] - self._test_create_resource( - resource, cmd, name, myid, args, position_names, - position_values, tenant_id='tenantid', - no_api_call=True, expected_exception=exceptions.CommandError) - - @mock.patch.object(neutronV20, 'find_resource_by_name_or_id') - def test_create_subnet_with_subnetpool_ipv6_and_ip_ver_ignored( - self, mock_find_resource): - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = ['--tenant_id', 'tenantid', - '--ip-version', '4', - '--subnetpool', 'subnetpool_id', - netid] - position_names = ['ip_version', 'network_id', 'subnetpool_id'] - position_values = [6, netid, 'subnetpool_id'] - mock_find_resource.return_value = { - 'id': 'subnetpool_id', 'ip_version': 6} - self._test_create_resource( - resource, cmd, name, myid, args, position_names, - position_values, tenant_id='tenantid') - mock_find_resource.assert_called_once_with( - self.client, 'subnetpool', 'subnetpool_id') - - @mock.patch.object(neutronV20, 'find_resource_by_name_or_id') - def test_create_subnet_with_subnetpool_ipv4_with_cidr_wildcard( - self, mock_find_resource): - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'cidrwildcard' - args = ['--tenant_id', 'tenantid', - '--ip-version', '4', - '--ipv6-address-mode', 'slaac', - '--subnetpool', 'subnetpool_id', - netid, cidr] - position_names = ['ip_version', 'ipv6_address_mode', - 'network_id', 'subnetpool_id', 'cidr'] - position_values = [4, None, netid, 'subnetpool_id', cidr] - mock_find_resource.return_value = {'id': 'subnetpool_id', - 'ip_version': 4} - self._test_create_resource( - resource, cmd, name, myid, args, position_names, - position_values, tenant_id='tenantid', - no_api_call=True, expected_exception=exceptions.CommandError) - mock_find_resource.assert_called_once_with( - self.client, 'subnetpool', 'subnetpool_id') - - @mock.patch.object(neutronV20, 'find_resource_by_name_or_id') - def test_create_subnet_with_subnetpool_ipv4_with_prefixlen( - self, mock_find_resource): - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = ['--tenant_id', 'tenantid', - '--ip-version', '4', - '--ipv6-address-mode', 'slaac', - '--subnetpool', 'subnetpool_id', - '--prefixlen', '31', - netid] - position_names = ['ip_version', 'ipv6_address_mode', - 'network_id', 'subnetpool_id'] - position_values = [4, None, netid, 'subnetpool_id'] - mock_find_resource.return_value = {'id': 'subnetpool_id', - 'ip_version': 4} - self._test_create_resource( - resource, cmd, name, myid, args, position_names, - position_values, tenant_id='tenantid', - no_api_call=True, expected_exception=exceptions.CommandError) - mock_find_resource.assert_called_once_with( - self.client, 'subnetpool', 'subnetpool_id') - - def test_list_subnets_detail(self): - # List subnets: -D. - resources = "subnets" - cmd = subnet.ListSubnet(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_subnets_tags(self): - # List subnets: -- --tags a b. - resources = "subnets" - cmd = subnet.ListSubnet(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, tags=['a', 'b']) - - def test_list_subnets_detail_tags(self): - # List subnets: -D -- --tags a b. - resources = "subnets" - cmd = subnet.ListSubnet(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, detail=True, tags=['a', 'b']) - - def test_list_subnets_fields(self): - # List subnets: --fields a --fields b -- --fields c d. - resources = "subnets" - cmd = subnet.ListSubnet(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - fields_1=['a', 'b'], fields_2=['c', 'd']) - - def test_list_subnets_pagination(self): - resources = "subnets" - cmd = subnet.ListSubnet(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_subnets_sort(self): - # List subnets: --sort-key name --sort-key id --sort-key asc - # --sort-key desc - resources = "subnets" - cmd = subnet.ListSubnet(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_subnets_limit(self): - # List subnets: -P. - resources = "subnets" - cmd = subnet.ListSubnet(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_update_subnet(self): - # Update subnet: myid --name myname --tags a b. - resource = 'subnet' - cmd = subnet.UpdateSubnet(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'myname', - '--description', 'cavern', - '--tags', 'a', 'b'], - {'name': 'myname', 'tags': ['a', 'b'], - 'description': 'cavern'}) - - def test_update_subnet_allocation_pools(self): - # Update subnet: myid --name myname --tags a b. - resource = 'subnet' - cmd = subnet.UpdateSubnet(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--allocation-pool', - 'start=1.2.0.2,end=1.2.0.127'], - {'allocation_pools': [{'start': '1.2.0.2', - 'end': '1.2.0.127'}]} - ) - - def test_update_subnet_enable_disable_dhcp(self): - # Update sbunet: --enable-dhcp and --disable-dhcp. - resource = 'subnet' - cmd = subnet.UpdateSubnet(test_cli20.MyApp(sys.stdout), None) - self.assertRaises(exceptions.CommandError, - self._test_update_resource, - resource, cmd, 'myid', - ['myid', '--name', 'myname', '--enable-dhcp', - '--disable-dhcp'], {'name': 'myname', }) - - def test_show_subnet(self): - # Show subnet: --fields id --fields name myid. - resource = 'subnet' - cmd = subnet.ShowSubnet(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name']) - - def test_delete_subnet(self): - # Delete subnet: subnetid. - resource = 'subnet' - cmd = subnet.DeleteSubnet(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) diff --git a/neutronclient/tests/unit/test_cli20_subnetpool.py b/neutronclient/tests/unit/test_cli20_subnetpool.py deleted file mode 100644 index fb201683a..000000000 --- a/neutronclient/tests/unit/test_cli20_subnetpool.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright 2015 OpenStack Foundation. -# 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 sys -from unittest import mock - - -from neutronclient.common import exceptions -from neutronclient.neutron.v2_0 import subnetpool -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20SubnetPoolJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['subnetpool'] - - def setUp(self): - super(CLITestV20SubnetPoolJSON, self).setUp(plurals={'tags': 'tag'}) - - def test_create_subnetpool_with_options(self): - # Create subnetpool: myname. - resource = 'subnetpool' - cmd = subnetpool.CreateSubnetPool(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - min_prefixlen = 30 - prefix1 = '10.11.12.0/24' - prefix2 = '12.11.13.0/24' - args = [name, '--min-prefixlen', str(min_prefixlen), - '--pool-prefix', prefix1, '--pool-prefix', prefix2, - '--shared', '--description', 'public pool', - '--tenant-id', 'tenantid'] - position_names = ['name', 'min_prefixlen', 'prefixes', 'shared', - 'tenant_id'] - position_values = [name, min_prefixlen, [prefix1, prefix2], True, - 'tenantid'] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - description='public pool') - - def test_create_subnetpool_only_with_required_options(self): - # Create subnetpool: myname. - resource = 'subnetpool' - cmd = subnetpool.CreateSubnetPool(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - min_prefixlen = 30 - prefix1 = '10.11.12.0/24' - prefix2 = '12.11.13.0/24' - args = [name, '--min-prefixlen', str(min_prefixlen), - '--pool-prefix', prefix1, '--pool-prefix', prefix2] - position_names = ['name', 'min_prefixlen', 'prefixes'] - position_values = [name, min_prefixlen, [prefix1, prefix2]] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_subnetpool_with_is_default(self, default='false'): - # Create subnetpool: myname. - resource = 'subnetpool' - cmd = subnetpool.CreateSubnetPool(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - min_prefixlen = 30 - prefix1 = '10.11.12.0/24' - prefix2 = '12.11.13.0/24' - args = [name, '--min-prefixlen', str(min_prefixlen), - '--pool-prefix', prefix1, '--pool-prefix', prefix2, - '--is-default', default] - position_names = ['name', 'min_prefixlen', 'prefixes', 'is_default'] - position_values = [name, min_prefixlen, [prefix1, prefix2], default] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_subnetpool_default(self): - self.test_create_subnetpool_with_is_default(default='true') - - def test_create_subnetpool_with_unicode(self): - # Create subnetpool: u'\u7f51\u7edc'. - resource = 'subnetpool' - cmd = subnetpool.CreateSubnetPool(test_cli20.MyApp(sys.stdout), None) - name = u'\u7f51\u7edc' - myid = 'myid' - min_prefixlen = 30 - prefixes = '10.11.12.0/24' - args = [name, '--min-prefixlen', str(min_prefixlen), - '--pool-prefix', prefixes] - position_names = ['name', 'min_prefixlen', 'prefixes'] - position_values = [name, min_prefixlen, [prefixes]] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_subnetpool_with_addrscope(self): - # Create subnetpool: myname in addrscope: foo-address-scope - resource = 'subnetpool' - cmd = subnetpool.CreateSubnetPool(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - min_prefixlen = 30 - prefix1 = '11.11.11.0/24' - prefix2 = '12.12.12.0/24' - address_scope = 'foo-address-scope' - args = [name, '--min-prefixlen', str(min_prefixlen), - '--pool-prefix', prefix1, '--pool-prefix', prefix2, - '--address-scope', address_scope] - position_names = ['name', 'min_prefixlen', 'prefixes', - 'address_scope_id'] - position_values = [name, min_prefixlen, [prefix1, prefix2], - address_scope] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_subnetpool_no_poolprefix(self): - # Should raise an error because --pool-prefix is required - resource = 'subnetpool' - cmd = subnetpool.CreateSubnetPool(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - args = [name] - position_names = ['name'] - position_values = [name] - self.assertRaises(SystemExit, self._test_create_resource, resource, - cmd, name, myid, args, position_names, - position_values) - - @mock.patch.object(subnetpool.ListSubnetPool, "extend_list") - def test_list_subnetpool_pagination(self, mock_extend_list): - cmd = subnetpool.ListSubnetPool(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination("subnetpools", cmd) - mock_extend_list.assert_called_once_with(test_cli20.IsA(list), - mock.ANY) - - def test_list_subnetpools_sort(self): - # List subnetpools: - # --sort-key name --sort-key id --sort-key asc --sort-key desc - resources = "subnetpools" - cmd = subnetpool.ListSubnetPool(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_subnetpools_limit(self): - # List subnetpools: -P. - resources = "subnetpools" - cmd = subnetpool.ListSubnetPool(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_update_subnetpool_exception(self): - # Update subnetpool: myid. - resource = 'subnetpool' - cmd = subnetpool.UpdateSubnetPool(test_cli20.MyApp(sys.stdout), None) - self.assertRaises(exceptions.CommandError, self._test_update_resource, - resource, cmd, 'myid', ['myid'], {}) - - def test_update_subnetpool(self): - # Update subnetpool: myid --name myname. - resource = 'subnetpool' - cmd = subnetpool.UpdateSubnetPool(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'myname', - '--description', ':)'], - {'name': 'myname', 'description': ':)'}) - - def test_update_subnetpool_with_address_scope(self): - # Update subnetpool: myid --address-scope newscope. - resource = 'subnetpool' - cmd = subnetpool.UpdateSubnetPool(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--address-scope', 'newscope'], - {'address_scope_id': 'newscope'} - ) - - def test_update_subnetpool_with_no_address_scope(self): - # Update subnetpool: myid --no-address-scope. - resource = 'subnetpool' - cmd = subnetpool.UpdateSubnetPool(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--no-address-scope'], - {'address_scope_id': None} - ) - - def test_show_subnetpool(self): - # Show subnetpool: --fields id --fields name myid. - resource = 'subnetpool' - cmd = subnetpool.ShowSubnetPool(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, - ['id', 'name']) - - def test_delete_subnetpool(self): - # Delete subnetpool: subnetpoolid. - resource = 'subnetpool' - cmd = subnetpool.DeleteSubnetPool(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) diff --git a/neutronclient/tests/unit/test_cli20_tag.py b/neutronclient/tests/unit/test_cli20_tag.py deleted file mode 100644 index 2e23c9dc5..000000000 --- a/neutronclient/tests/unit/test_cli20_tag.py +++ /dev/null @@ -1,131 +0,0 @@ -# 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 sys -from unittest import mock - - -from neutronclient.common import exceptions -from neutronclient.neutron.v2_0 import network -from neutronclient.neutron.v2_0 import tag -from neutronclient import shell -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20Tag(test_cli20.CLITestV20Base): - def _test_tag_operation(self, cmd, path, method, args, prog_name, - body=None): - with mock.patch.object(cmd, 'get_client', - return_value=self.client) as mock_get_client, \ - mock.patch.object(self.client.httpclient, 'request', - return_value=(test_cli20.MyResp(204), None) - ) as mock_request: - if body: - body = test_cli20.MyComparator(body, self.client) - cmd_parser = cmd.get_parser(prog_name) - shell.run_command(cmd, cmd_parser, args) - - mock_get_client.assert_called_once_with() - mock_request.assert_called_once_with( - test_cli20.MyUrlComparator(test_cli20.end_url(path), self.client), - method, body=body, - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN})) - - def _test_tags_query(self, cmd, resources, args, query): - path = getattr(self.client, resources + "_path") - res = {resources: [{'id': 'myid'}]} - resstr = self.client.serialize(res) - with mock.patch.object(cmd, 'get_client', - return_value=self.client) as mock_get_client, \ - mock.patch.object(self.client.httpclient, 'request', - return_value=(test_cli20.MyResp(200), resstr) - ) as mock_request: - cmd_parser = cmd.get_parser("list_networks") - shell.run_command(cmd, cmd_parser, args) - - mock_get_client.assert_called_once_with() - mock_request.assert_called_once_with( - test_cli20.MyUrlComparator(test_cli20.end_url(path, query), - self.client), - 'GET', body=None, - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN})) - - _str = self.fake_stdout.make_string() - self.assertIn('myid', _str) - - def _make_tag_path(self, resource, resource_id, tag): - path = getattr(self.client, "tag_path") - resource_plural = self.client.get_resource_plural(resource) - return path % (resource_plural, resource_id, tag) - - def _make_tags_path(self, resource, resource_id): - path = getattr(self.client, "tags_path") - resource_plural = self.client.get_resource_plural(resource) - return path % (resource_plural, resource_id) - - def test_add_tag(self): - cmd = tag.AddTag(test_cli20.MyApp(sys.stdout), None) - path = self._make_tag_path('network', 'myid', 'red') - args = ['--resource-type', 'network', '--resource', 'myid', - '--tag', 'red'] - self._test_tag_operation(cmd, path, 'PUT', args, "tag-add") - - def test_add_tag_empty_tag(self): - cmd = tag.AddTag(test_cli20.MyApp(sys.stdout), None) - path = self._make_tag_path('network', 'myid', '') - args = ['--resource-type', 'network', '--resource', 'myid', - '--tag', ''] - self.assertRaises(exceptions.CommandError, self._test_tag_operation, - cmd, path, 'PUT', args, "tag-add") - - def test_replace_tag(self): - cmd = tag.ReplaceTag(test_cli20.MyApp(sys.stdout), None) - path = self._make_tags_path('network', 'myid') - args = ['--resource-type', 'network', '--resource', 'myid', - '--tag', 'red', '--tag', 'blue'] - body = {'tags': ['red', 'blue']} - self._test_tag_operation(cmd, path, 'PUT', args, "tag-replace", - body=body) - - def test_remove_tag(self): - cmd = tag.RemoveTag(test_cli20.MyApp(sys.stdout), None) - path = self._make_tag_path('network', 'myid', 'red') - args = ['--resource-type', 'network', '--resource', 'myid', - '--tag', 'red'] - self._test_tag_operation(cmd, path, 'DELETE', args, "tag-remove") - - def test_remove_tag_all(self): - cmd = tag.RemoveTag(test_cli20.MyApp(sys.stdout), None) - path = self._make_tags_path('network', 'myid') - args = ['--resource-type', 'network', '--resource', 'myid', - '--all'] - self._test_tag_operation(cmd, path, 'DELETE', args, "tag-remove") - - def test_no_tag_nor_all(self): - cmd = tag.RemoveTag(test_cli20.MyApp(sys.stdout), None) - path = self._make_tags_path('network', 'myid') - args = ['--resource-type', 'network', '--resource', 'myid'] - self.assertRaises(exceptions.CommandError, self._test_tag_operation, - cmd, path, 'DELETE', args, "tag-remove") - - def test_tags_query(self): - # This test examines that '-' in the tag related filters - # is not converted to '_'. - resources = 'networks' - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - with mock.patch.object(network.ListNetwork, 'extend_list'): - args = ['--not-tags', 'red,blue', '--tags-any', 'green', - '--not-tags-any', 'black'] - query = "not-tags=red,blue&tags-any=green¬-tags-any=black" - self._test_tags_query(cmd, resources, args, query) diff --git a/neutronclient/tests/unit/test_client_extension.py b/neutronclient/tests/unit/test_client_extension.py deleted file mode 100644 index 871e7a6cb..000000000 --- a/neutronclient/tests/unit/test_client_extension.py +++ /dev/null @@ -1,221 +0,0 @@ -# Copyright 2015 Rackspace Hosting 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 inspect -import sys -from unittest import mock - - -from neutronclient.common import extension -from neutronclient.neutron.v2_0.contrib import _fox_sockets as fox_sockets -from neutronclient import shell -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20ExtensionJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['fox_socket'] - - def setUp(self): - # need to mock before super because extensions loaded on instantiation - self._mock_extension_loading() - super(CLITestV20ExtensionJSON, self).setUp(plurals={'tags': 'tag'}) - - def _mock_extension_loading(self): - ext_pkg = 'neutronclient.common.extension' - contrib = mock.patch(ext_pkg + '._discover_via_entry_points').start() - contrib.return_value = [("_fox_sockets", fox_sockets)] - return contrib - - def test_ext_cmd_loaded(self): - neutron_shell = shell.NeutronShell('2.0') - ext_cmd = {'fox-sockets-list': fox_sockets.FoxInSocketsList, - 'fox-sockets-create': fox_sockets.FoxInSocketsCreate, - 'fox-sockets-update': fox_sockets.FoxInSocketsUpdate, - 'fox-sockets-delete': fox_sockets.FoxInSocketsDelete, - 'fox-sockets-show': fox_sockets.FoxInSocketsShow} - for cmd_name, cmd_class in ext_cmd.items(): - found = neutron_shell.command_manager.find_command([cmd_name]) - self.assertEqual(cmd_class, found[0]) - - def test_ext_cmd_help_doc_with_extension_name(self): - neutron_shell = shell.NeutronShell('2.0') - ext_cmd = {'fox-sockets-list': fox_sockets.FoxInSocketsList, - 'fox-sockets-create': fox_sockets.FoxInSocketsCreate, - 'fox-sockets-update': fox_sockets.FoxInSocketsUpdate, - 'fox-sockets-delete': fox_sockets.FoxInSocketsDelete, - 'fox-sockets-show': fox_sockets.FoxInSocketsShow} - for cmd_name, cmd_class in ext_cmd.items(): - found = neutron_shell.command_manager.find_command([cmd_name]) - found_factory = found[0] - self.assertEqual(cmd_class, found_factory) - self.assertTrue(found_factory.__doc__.startswith("[_fox_sockets]")) - - def test_delete_fox_socket(self): - # Delete fox socket: myid. - resource = 'fox_socket' - cmd = fox_sockets.FoxInSocketsDelete(test_cli20.MyApp(sys.stdout), - None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) - - def test_update_fox_socket(self): - # Update fox_socket: myid --name myname. - resource = 'fox_socket' - cmd = fox_sockets.FoxInSocketsUpdate(test_cli20.MyApp(sys.stdout), - None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'myname'], - {'name': 'myname'}) - - def test_create_fox_socket(self): - # Create fox_socket: myname. - resource = 'fox_socket' - cmd = fox_sockets.FoxInSocketsCreate(test_cli20.MyApp(sys.stdout), - None) - name = 'myname' - myid = 'myid' - args = [name, ] - position_names = ['name', ] - position_values = [name, ] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_list_fox_sockets(self): - # List fox_sockets. - resources = 'fox_sockets' - cmd = fox_sockets.FoxInSocketsList(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_fox_pagination(self): - resources = 'fox_sockets' - cmd = fox_sockets.FoxInSocketsList(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_show_fox_socket(self): - # Show fox_socket: --fields id --fields name myid. - resource = 'fox_socket' - cmd = fox_sockets.FoxInSocketsShow(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name']) - - -class CLITestV20ExtensionJSONAlternatePlurals(test_cli20.CLITestV20Base): - class IPAddress(extension.NeutronClientExtension): - resource = 'ip_address' - resource_plural = '%ses' % resource - object_path = '/%s' % resource_plural - resource_path = '/%s/%%s' % resource_plural - versions = ['2.0'] - - class IPAddressesList(extension.ClientExtensionList, IPAddress): - shell_command = 'ip-address-list' - - def setUp(self): - # need to mock before super because extensions loaded on instantiation - self._mock_extension_loading() - super(CLITestV20ExtensionJSONAlternatePlurals, self).setUp() - - def _mock_extension_loading(self): - ext_pkg = 'neutronclient.common.extension' - contrib = mock.patch(ext_pkg + '._discover_via_entry_points').start() - ip_address = mock.Mock() - ip_address.IPAddress = self.IPAddress - ip_address.IPAddressesList = self.IPAddressesList - contrib.return_value = [("ip_address", ip_address)] - return contrib - - def test_ext_cmd_loaded(self): - neutron_shell = shell.NeutronShell('2.0') - ext_cmd = {'ip-address-list': self.IPAddressesList} - for cmd_name, cmd_class in ext_cmd.items(): - found = neutron_shell.command_manager.find_command([cmd_name]) - self.assertEqual(cmd_class, found[0]) - - def test_list_ip_addresses(self): - # List ip_addresses. - resources = 'ip_addresses' - cmd = self.IPAddressesList(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - -class CLITestV20ExtensionJSONChildResource(test_cli20.CLITestV20Base): - class Child(extension.NeutronClientExtension): - parent_resource = 'parents' - child_resource = 'child' - resource = '%s_%s' % (parent_resource, child_resource) - resource_plural = '%sren' % resource - child_resource_plural = '%ren' % child_resource - object_path = '/%s/%%s/%s' % (parent_resource, child_resource_plural) - resource_path = '/%s/%%s/%s/%%s' % (parent_resource, - child_resource_plural) - versions = ['2.0'] - - class ChildrenList(extension.ClientExtensionList, Child): - shell_command = 'parent-child-list' - - class ChildShow(extension.ClientExtensionShow, Child): - shell_command = 'parent-child-show' - - class ChildUpdate(extension.ClientExtensionUpdate, Child): - shell_command = 'parent-child-update' - - class ChildDelete(extension.ClientExtensionDelete, Child): - shell_command = 'parent-child-delete' - - class ChildCreate(extension.ClientExtensionCreate, Child): - shell_command = 'parent-child-create' - - def setUp(self): - # need to mock before super because extensions loaded on instantiation - self._mock_extension_loading() - super(CLITestV20ExtensionJSONChildResource, self).setUp() - - def _mock_extension_loading(self): - ext_pkg = 'neutronclient.common.extension' - contrib = mock.patch(ext_pkg + '._discover_via_entry_points').start() - child = mock.Mock() - child.Child = self.Child - child.ChildrenList = self.ChildrenList - child.ChildShow = self.ChildShow - child.ChildUpdate = self.ChildUpdate - child.ChildDelete = self.ChildDelete - child.ChildCreate = self.ChildCreate - contrib.return_value = [("child", child)] - return contrib - - def test_ext_cmd_loaded(self): - neutron_shell = shell.NeutronShell('2.0') - ext_cmd = {'parent-child-list': self.ChildrenList, - 'parent-child-show': self.ChildShow, - 'parent-child-update': self.ChildUpdate, - 'parent-child-delete': self.ChildDelete, - 'parent-child-create': self.ChildCreate} - for cmd_name, cmd_class in ext_cmd.items(): - found = neutron_shell.command_manager.find_command([cmd_name]) - self.assertEqual(cmd_class, found[0]) - - def test_client_methods_have_parent_id_arg(self): - methods = (self.client.list_parents_children, - self.client.show_parents_child, - self.client.update_parents_child, - self.client.delete_parents_child, - self.client.create_parents_child) - for method in methods: - argspec = inspect.getfullargspec(method) - self.assertIn("parent_id", argspec.args) diff --git a/neutronclient/tests/unit/test_name_or_id.py b/neutronclient/tests/unit/test_name_or_id.py deleted file mode 100644 index a6a7b9246..000000000 --- a/neutronclient/tests/unit/test_name_or_id.py +++ /dev/null @@ -1,244 +0,0 @@ -# Copyright 2012 OpenStack Foundation. -# 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. -# - -from unittest import mock - -from oslo_utils import uuidutils -import testtools - -from neutronclient.common import exceptions -from neutronclient.neutron import v2_0 as neutronV20 -from neutronclient.tests.unit import test_cli20 -from neutronclient.v2_0 import client - - -class CLITestNameorID(testtools.TestCase): - - def setUp(self): - """Prepare the test environment.""" - super(CLITestNameorID, self).setUp() - self.endurl = test_cli20.ENDURL - self.client = client.Client(token=test_cli20.TOKEN, - endpoint_url=self.endurl) - - def test_get_id_from_id(self): - _id = uuidutils.generate_uuid() - reses = {'networks': [{'id': _id, }, ], } - resstr = self.client.serialize(reses) - resp = (test_cli20.MyResp(200), resstr) - path = getattr(self.client, "networks_path") - with mock.patch.object(self.client.httpclient, "request", - return_value=resp) as mock_request: - returned_id = neutronV20.find_resourceid_by_name_or_id( - self.client, 'network', _id) - - mock_request.assert_called_once_with( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, "fields=id&id=" + _id), - self.client), - 'GET', - body=None, - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN})) - self.assertEqual(_id, returned_id) - - def test_get_id_from_id_then_name_empty(self): - _id = uuidutils.generate_uuid() - reses = {'networks': [{'id': _id, }, ], } - resstr = self.client.serialize(reses) - resstr1 = self.client.serialize({'networks': []}) - path = getattr(self.client, "networks_path") - with mock.patch.object(self.client.httpclient, - "request") as mock_request: - mock_request.side_effect = [(test_cli20.MyResp(200), resstr1), - (test_cli20.MyResp(200), resstr)] - returned_id = neutronV20.find_resourceid_by_name_or_id( - self.client, 'network', _id) - - self.assertEqual(2, mock_request.call_count) - mock_request.assert_has_calls([ - mock.call( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, "fields=id&id=" + _id), - self.client), - 'GET', - body=None, - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN})), - mock.call( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, "fields=id&name=" + _id), - self.client), - 'GET', - body=None, - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN}))]) - self.assertEqual(_id, returned_id) - - def test_get_id_from_name(self): - name = 'myname' - _id = uuidutils.generate_uuid() - reses = {'networks': [{'id': _id, }, ], } - resstr = self.client.serialize(reses) - resp = (test_cli20.MyResp(200), resstr) - path = getattr(self.client, "networks_path") - with mock.patch.object(self.client.httpclient, "request", - return_value=resp) as mock_request: - returned_id = neutronV20.find_resourceid_by_name_or_id( - self.client, 'network', name) - - mock_request.assert_called_once_with( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, "fields=id&name=" + name), - self.client), - 'GET', - body=None, - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN})) - self.assertEqual(_id, returned_id) - - def test_get_id_from_name_multiple(self): - name = 'myname' - reses = {'networks': [{'id': uuidutils.generate_uuid()}, - {'id': uuidutils.generate_uuid()}]} - resstr = self.client.serialize(reses) - resp = (test_cli20.MyResp(200), resstr) - path = getattr(self.client, "networks_path") - with mock.patch.object(self.client.httpclient, "request", - return_value=resp) as mock_request: - exception = self.assertRaises( - exceptions.NeutronClientNoUniqueMatch, - neutronV20.find_resourceid_by_name_or_id, - self.client, 'network', name) - - mock_request.assert_called_once_with( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, "fields=id&name=" + name), - self.client), - 'GET', - body=None, - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN})) - self.assertIn('Multiple', exception.message) - - def test_get_id_from_name_notfound(self): - name = 'myname' - reses = {'networks': []} - resstr = self.client.serialize(reses) - resp = (test_cli20.MyResp(200), resstr) - path = getattr(self.client, "networks_path") - with mock.patch.object(self.client.httpclient, "request", - return_value=resp) as mock_request: - exception = self.assertRaises( - exceptions.NotFound, - neutronV20.find_resourceid_by_name_or_id, - self.client, 'network', name) - - mock_request.assert_called_once_with( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, "fields=id&name=" + name), - self.client), - 'GET', - body=None, - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN})) - self.assertIn('Unable to find', exception.message) - self.assertEqual(404, exception.status_code) - - def test_get_id_from_name_multiple_with_project(self): - name = 'web_server' - project = uuidutils.generate_uuid() - expect_id = uuidutils.generate_uuid() - reses = {'security_groups': - [{'id': expect_id, 'tenant_id': project}]} - resstr = self.client.serialize(reses) - resp = (test_cli20.MyResp(200), resstr) - path = getattr(self.client, "security_groups_path") - with mock.patch.object(self.client.httpclient, "request", - return_value=resp) as mock_request: - observed_id = neutronV20.find_resourceid_by_name_or_id( - self.client, 'security_group', name, project) - - mock_request.assert_called_once_with( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, "fields=id&name=%s&tenant_id=%s" % - (name, project)), self.client), - 'GET', - body=None, - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN})) - self.assertEqual(expect_id, observed_id) - - def test_get_id_from_name_multiple_with_project_not_found(self): - name = 'web_server' - project = uuidutils.generate_uuid() - resstr_notfound = self.client.serialize({'security_groups': []}) - resp = (test_cli20.MyResp(200), resstr_notfound) - path = getattr(self.client, "security_groups_path") - with mock.patch.object(self.client.httpclient, "request", - return_value=resp) as mock_request: - exc = self.assertRaises(exceptions.NotFound, - neutronV20.find_resourceid_by_name_or_id, - self.client, 'security_group', name, - project) - - mock_request.assert_called_once_with( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, "fields=id&name=%s&tenant_id=%s" % - (name, project)), self.client), - 'GET', - body=None, - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN})) - self.assertIn('Unable to find', exc.message) - self.assertEqual(404, exc.status_code) - - def _test_get_resource_by_id(self, id_only=False): - _id = uuidutils.generate_uuid() - net = {'id': _id, 'name': 'test'} - reses = {'networks': [net], } - resstr = self.client.serialize(reses) - resp = (test_cli20.MyResp(200), resstr) - path = getattr(self.client, "networks_path") - if id_only: - query_params = "fields=id&id=%s" % _id - else: - query_params = "id=%s" % _id - with mock.patch.object(self.client.httpclient, "request", - return_value=resp) as mock_request: - if id_only: - returned_id = neutronV20.find_resourceid_by_id( - self.client, 'network', _id) - self.assertEqual(_id, returned_id) - else: - result = neutronV20.find_resource_by_id( - self.client, 'network', _id) - self.assertEqual(net, result) - - mock_request.assert_called_once_with( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, query_params), - self.client), - 'GET', - body=None, - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN})) - - def test_get_resource_by_id(self): - self._test_get_resource_by_id(id_only=False) - - def test_get_resourceid_by_id(self): - self._test_get_resource_by_id(id_only=True) diff --git a/neutronclient/tests/unit/test_quota.py b/neutronclient/tests/unit/test_quota.py deleted file mode 100644 index 747621de9..000000000 --- a/neutronclient/tests/unit/test_quota.py +++ /dev/null @@ -1,99 +0,0 @@ -# Copyright (C) 2013 Yahoo! 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 sys -from unittest import mock - - -from neutronclient.common import exceptions -from neutronclient.neutron.v2_0 import quota as test_quota -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20Quota(test_cli20.CLITestV20Base): - def test_show_quota(self): - resource = 'quota' - cmd = test_quota.ShowQuota( - test_cli20.MyApp(sys.stdout), None) - args = ['--tenant-id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args) - - def test_update_quota(self): - resource = 'quota' - cmd = test_quota.UpdateQuota( - test_cli20.MyApp(sys.stdout), None) - args = ['--tenant-id', self.test_id, '--network', 'test'] - self.assertRaises( - exceptions.CommandError, self._test_update_resource, - resource, cmd, self.test_id, args=args, - extrafields={'network': 'new'}) - - def test_delete_quota_get_parser(self): - cmd = test_cli20.MyApp(sys.stdout) - test_quota.DeleteQuota(cmd, None).get_parser(cmd) - - def test_show_quota_positional(self): - resource = 'quota' - cmd = test_quota.ShowQuota( - test_cli20.MyApp(sys.stdout), None) - args = [self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args) - - def test_update_quota_positional(self): - resource = 'quota' - cmd = test_quota.UpdateQuota( - test_cli20.MyApp(sys.stdout), None) - args = [self.test_id, '--network', 'test'] - self.assertRaises( - exceptions.CommandError, self._test_update_resource, - resource, cmd, self.test_id, args=args, - extrafields={'network': 'new'}) - - def test_show_quota_default(self): - resource = 'quota' - cmd = test_quota.ShowQuotaDefault( - test_cli20.MyApp(sys.stdout), None) - args = ['--tenant-id', self.test_id] - expected_res = {'quota': {'port': 50, 'network': 10, 'subnet': 10}} - resstr = self.client.serialize(expected_res) - path = getattr(self.client, "quota_default_path") - return_tup = (test_cli20.MyResp(200), resstr) - with mock.patch.object(cmd, 'get_client', - return_value=self.client) as mock_get_client, \ - mock.patch.object(self.client.httpclient, 'request', - return_value=return_tup) as mock_request: - cmd_parser = cmd.get_parser("test_" + resource) - parsed_args = cmd_parser.parse_args(args) - cmd.run(parsed_args) - - mock_get_client.assert_called_once_with() - mock_request.assert_called_once_with( - test_cli20.end_url(path % self.test_id), 'GET', - body=None, - headers=test_cli20.ContainsKeyValue( - {'X-Auth-Token': test_cli20.TOKEN})) - _str = self.fake_stdout.make_string() - self.assertIn('network', _str) - self.assertIn('subnet', _str) - self.assertIn('port', _str) - self.assertNotIn('subnetpool', _str) - - def test_update_quota_noargs(self): - resource = 'quota' - cmd = test_quota.UpdateQuota(test_cli20.MyApp(sys.stdout), None) - args = [self.test_id] - self.assertRaises(exceptions.CommandError, self._test_update_resource, - resource, cmd, self.test_id, args=args, - extrafields=None) diff --git a/neutronclient/tests/unit/test_shell.py b/neutronclient/tests/unit/test_shell.py deleted file mode 100644 index 04e814066..000000000 --- a/neutronclient/tests/unit/test_shell.py +++ /dev/null @@ -1,366 +0,0 @@ -# Copyright (C) 2013 Yahoo! 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 argparse -from io import StringIO -import logging -import os -import re -import sys -from unittest import mock - -import fixtures -from keystoneauth1 import session -import testtools -from testtools import matchers - -from neutronclient.common import clientmanager -from neutronclient.neutron.v2_0 import network -from neutronclient import shell as openstack_shell - - -DEFAULT_USERNAME = 'username' -DEFAULT_PASSWORD = 'password' -DEFAULT_TENANT_ID = 'tenant_id' -DEFAULT_TENANT_NAME = 'tenant_name' -DEFAULT_AUTH_URL = 'http://127.0.0.1:5000/v2.0/' -DEFAULT_TOKEN = '3bcc3d3a03f44e3d8377f9247b0ad155' -DEFAULT_URL = 'http://quantum.example.org:9696/' -DEFAULT_REGION = 'regionOne' -DEFAULT_ENDPOINT_TYPE = 'public' -DEFAULT_API_VERSION = '2.0' -DEFAULT_SERVICE_TYPE = 'network' -DEFAULT_SERVICE_NAME = 'neutron' -DEFAULT_RETRIES = 3 -DEFAULT_TIMEOUT = 3.0 - - -class ShellTest(testtools.TestCase): - - FAKE_ENV = { - 'OS_USERNAME': DEFAULT_USERNAME, - 'OS_PASSWORD': DEFAULT_PASSWORD, - 'OS_TENANT_ID': DEFAULT_TENANT_ID, - 'OS_TENANT_NAME': DEFAULT_TENANT_NAME, - 'OS_AUTH_URL': DEFAULT_AUTH_URL, - 'OS_REGION_NAME': None, - 'HTTP_PROXY': None, - 'http_proxy': None, - } - - # Patch os.environ to avoid required auth info. - def setUp(self): - super(ShellTest, self).setUp() - for var in self.FAKE_ENV: - self.useFixture( - fixtures.EnvironmentVariable( - var, self.FAKE_ENV[var])) - - def shell(self, argstr, check=False, expected_val=0): - # expected_val is the expected return value after executing - # the command in NeutronShell - orig = (sys.stdout, sys.stderr) - clean_env = {} - _old_env, os.environ = os.environ, clean_env.copy() - try: - sys.stdout = StringIO() - sys.stderr = StringIO() - _shell = openstack_shell.NeutronShell('2.0') - _shell.run(argstr.split()) - except SystemExit: - exc_type, exc_value, exc_traceback = sys.exc_info() - self.assertEqual(expected_val, exc_value.code) - finally: - stdout = sys.stdout.getvalue() - stderr = sys.stderr.getvalue() - sys.stdout.close() - sys.stderr.close() - sys.stdout, sys.stderr = orig - os.environ = _old_env - return stdout, stderr - - def test_run_unknown_command(self): - self.useFixture(fixtures.FakeLogger(level=logging.DEBUG)) - stdout, stderr = self.shell('fake', check=True) - self.assertFalse(stdout) - self.assertIn("Unknown command ['fake']", stderr.strip()) - - def test_help(self): - required = 'usage:' - help_text, stderr = self.shell('help') - self.assertThat( - help_text, - matchers.MatchesRegex(required)) - - def test_bash_completion(self): - required = '.*os_user_domain_id.*' - bash_completion, stderr = self.shell('bash-completion') - self.assertThat( - bash_completion, - matchers.MatchesRegex(required)) - - def test_help_on_subcommand(self): - required = [ - '.*?^usage: .* quota-list'] - stdout, stderr = self.shell('help quota-list') - for r in required: - self.assertThat( - stdout, - matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE)) - - def test_help_command(self): - required = 'usage:' - help_text, stderr = self.shell('help network-create') - self.assertThat( - help_text, - matchers.MatchesRegex(required)) - - def test_bash_completion_in_outputs_of_help_command(self): - help_text, stderr = self.shell('help') - completion_cmd = "bash-completion" - completion_help_str = ("Prints all of the commands and options " - "for bash-completion.") - self.assertIn(completion_cmd, help_text) - self.assertIn(completion_help_str, help_text) - - def test_bash_completion_command(self): - # just check we have some output - required = [ - '.*--tenant_id', - '.*help', - '.*--dns-nameserver'] - help_text, stderr = self.shell('neutron bash-completion') - for r in required: - self.assertThat(help_text, - matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE)) - - def test_build_option_parser(self): - neutron_shell = openstack_shell.NeutronShell('2.0') - result = neutron_shell.build_option_parser('descr', '2.0') - self.assertIsInstance(result, argparse.ArgumentParser) - - @mock.patch.object(openstack_shell.NeutronShell, 'run') - def test_main_with_unicode(self, fake_shell): - unicode_text = u'\u7f51\u7edc' - argv = ['net-list', unicode_text, unicode_text] - fake_shell.return_value = 0 - ret = openstack_shell.main(argv=argv) - fake_shell.assert_called_once_with([u'net-list', unicode_text, - unicode_text]) - self.assertEqual(0, ret) - - def test_endpoint_option(self): - shell = openstack_shell.NeutronShell('2.0') - parser = shell.build_option_parser('descr', '2.0') - - # Neither $OS_ENDPOINT_TYPE nor --os-endpoint-type - namespace = parser.parse_args([]) - self.assertEqual('public', namespace.os_endpoint_type) - - # --endpoint-type but not $OS_ENDPOINT_TYPE - namespace = parser.parse_args(['--os-endpoint-type=admin']) - self.assertEqual('admin', namespace.os_endpoint_type) - - def test_endpoint_environment_variable(self): - fixture = fixtures.EnvironmentVariable("OS_ENDPOINT_TYPE", - "public") - self.useFixture(fixture) - - shell = openstack_shell.NeutronShell('2.0') - parser = shell.build_option_parser('descr', '2.0') - - # $OS_ENDPOINT_TYPE but not --endpoint-type - namespace = parser.parse_args([]) - self.assertEqual("public", namespace.os_endpoint_type) - - # --endpoint-type and $OS_ENDPOINT_TYPE - namespace = parser.parse_args(['--endpoint-type=admin']) - self.assertEqual('admin', namespace.endpoint_type) - - def test_timeout_option(self): - shell = openstack_shell.NeutronShell('2.0') - parser = shell.build_option_parser('descr', '2.0') - - # Neither $OS_ENDPOINT_TYPE nor --endpoint-type - namespace = parser.parse_args([]) - self.assertIsNone(namespace.http_timeout) - - # --endpoint-type but not $OS_ENDPOINT_TYPE - namespace = parser.parse_args(['--http-timeout=50']) - self.assertEqual(50, namespace.http_timeout) - - def test_timeout_environment_variable(self): - fixture = fixtures.EnvironmentVariable("OS_NETWORK_TIMEOUT", - "50") - self.useFixture(fixture) - - shell = openstack_shell.NeutronShell('2.0') - parser = shell.build_option_parser('descr', '2.0') - - namespace = parser.parse_args([]) - self.assertEqual(50, namespace.http_timeout) - - def test_run_incomplete_command(self): - self.useFixture(fixtures.FakeLogger(level=logging.DEBUG)) - cmd = ( - '--os-username test --os-password test --os-project-id test ' - '--os-auth-strategy keystone --os-auth-url ' - '%s port-create' % - DEFAULT_AUTH_URL) - stdout, stderr = self.shell(cmd, check=True, expected_val=2) - search_str = "Try 'neutron help port-create' for more information" - self.assertTrue(any(search_str in string for string - in stderr.split('\n'))) - - def _test_authenticate_user(self, expect_verify, expect_insecure, - **options): - base_options = {'os_cloud': None, - 'http_timeout': DEFAULT_TIMEOUT, - 'region_name': DEFAULT_REGION, - 'network_service_name': DEFAULT_SERVICE_NAME, - 'neutron_service_type': DEFAULT_SERVICE_TYPE} - - options.update(base_options) - if options.get('os_token'): - options.update({'auth_type': 'token'}) - options.update({'os_token': 'token', 'os_url': 'url'}) - else: - options.update({'os_token': None, 'os_url': None}) - - with mock.patch.object(openstack_shell.NeutronShell, - 'run_subcommand'), \ - mock.patch.object(session, 'Session') as session_mock, \ - mock.patch.object(clientmanager, 'ClientManager') as cmgr_mock: - - shell = openstack_shell.NeutronShell(DEFAULT_API_VERSION) - shell.options = mock.Mock(spec=options.keys()) - for k, v in options.items(): - setattr(shell.options, k, v) - shell.options.os_endpoint_type = DEFAULT_ENDPOINT_TYPE - shell.options.retries = DEFAULT_RETRIES - - if not (options.get('os_token') and options.get('os_url')): - auth = mock.ANY - auth_session = mock.sentinel.session - session_mock.return_value = auth_session - else: - auth = None - auth_session = None - - shell.authenticate_user() - - if not (options.get('os_token') and options.get('os_url')): - session_mock.assert_called_once_with( - auth=mock.ANY, verify=expect_verify, - cert=options.get('cert'), - timeout=DEFAULT_TIMEOUT) - else: - self.assertFalse(session_mock.called) - - cmgr_mock.assert_called_once_with( - retries=DEFAULT_RETRIES, - raise_errors=False, - session=auth_session, - url=options.get('os_url'), - token=options.get('os_token'), - region_name=DEFAULT_REGION, - api_version=DEFAULT_API_VERSION, - service_type=DEFAULT_SERVICE_TYPE, - service_name=DEFAULT_SERVICE_NAME, - endpoint_type=DEFAULT_ENDPOINT_TYPE, - auth=auth, - insecure=expect_insecure, - log_credentials=True) - - def test_authenticate_secure_with_cacert_with_cert(self): - self._test_authenticate_user( - insecure=False, cacert='cacert', cert='cert', - expect_verify='cacert', expect_insecure=False) - - def test_authenticate_secure_with_cacert_with_cert_with_token(self): - self._test_authenticate_user( - os_token='token', - insecure=False, cacert='cacert', cert='cert', - expect_verify='cacert', expect_insecure=False) - - def test_authenticate_insecure_with_cacert_with_cert(self): - self._test_authenticate_user( - insecure=True, cacert='cacert', cert='cert', - expect_verify=False, expect_insecure=True) - - def test_authenticate_insecure_with_cacert_with_cert_with_token(self): - self._test_authenticate_user( - os_token='token', - insecure=True, cacert='cacert', cert='cert', - expect_verify=False, expect_insecure=True) - - def test_authenticate_secure_without_cacert_with_cert(self): - self._test_authenticate_user( - insecure=False, cert='cert', - expect_verify=True, expect_insecure=False) - - def test_authenticate_secure_without_cacert_with_cert_with_token(self): - self._test_authenticate_user( - os_token='token', - insecure=False, cert='cert', - expect_verify=True, expect_insecure=False) - - def test_authenticate_insecure_without_cacert_with_cert(self): - self._test_authenticate_user( - insecure=True, cert='cert', - expect_verify=False, expect_insecure=True) - - def test_authenticate_insecure_without_cacert_with_cert_with_token(self): - self._test_authenticate_user( - os_token='token', - insecure=True, cert='cert', - expect_verify=False, expect_insecure=True) - - def test_authenticate_secure_with_cacert_without_cert(self): - self._test_authenticate_user( - insecure=False, cacert='cacert', - expect_verify='cacert', expect_insecure=False) - - def test_authenticate_secure_with_cacert_without_cert_with_token(self): - self._test_authenticate_user( - os_token='token', - insecure=False, cacert='cacert', - expect_verify='cacert', expect_insecure=False) - - def test_authenticate_insecure_with_cacert_without_cert(self): - self._test_authenticate_user( - insecure=True, cacert='cacert', - expect_verify=False, expect_insecure=True) - - def test_authenticate_insecure_with_cacert_without_cert_with_token(self): - self._test_authenticate_user( - os_token='token', - insecure=True, cacert='cacert', - expect_verify=False, expect_insecure=True) - - def test_commands_dict_populated(self): - # neutron.shell.COMMANDS is populated once NeutronShell is initialized. - # To check COMMANDS during NeutronShell initialization, - # reset COMMANDS to some dummy value before calling NeutronShell(). - self.useFixture(fixtures.MockPatchObject(openstack_shell, - 'COMMANDS', None)) - openstack_shell.NeutronShell('2.0') - self.assertLessEqual( - {'net-create': network.CreateNetwork, - 'net-delete': network.DeleteNetwork, - 'net-list': network.ListNetwork, - 'net-show': network.ShowNetwork, - 'net-update': network.UpdateNetwork}.items(), - openstack_shell.COMMANDS['2.0'].items()) diff --git a/neutronclient/tests/unit/vpn/__init__.py b/neutronclient/tests/unit/vpn/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutronclient/tests/unit/vpn/test_cli20_endpoint_group.py b/neutronclient/tests/unit/vpn/test_cli20_endpoint_group.py deleted file mode 100644 index f43da906b..000000000 --- a/neutronclient/tests/unit/vpn/test_cli20_endpoint_group.py +++ /dev/null @@ -1,145 +0,0 @@ -# (c) Copyright 2015 Cisco Systems, 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 sys - -from neutronclient.neutron.v2_0.vpn import endpoint_group -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20VpnEndpointGroupJSON(test_cli20.CLITestV20Base): - - def setUp(self): - super(CLITestV20VpnEndpointGroupJSON, self).setUp() - self.register_non_admin_status_resource('endpoint_group') - - def test_create_endpoint_group_with_cidrs(self): - # vpn-endpoint-group-create with CIDR endpoints.""" - resource = 'endpoint_group' - cmd = endpoint_group.CreateEndpointGroup(test_cli20.MyApp(sys.stdout), - None) - tenant_id = 'mytenant-id' - my_id = 'my-id' - name = 'my-endpoint-group' - description = 'my endpoint group' - endpoint_type = 'cidr' - endpoints = ['10.0.0.0/24', '20.0.0.0/24'] - - args = ['--name', name, - '--description', description, - '--tenant-id', tenant_id, - '--type', endpoint_type, - '--value', '10.0.0.0/24', - '--value', '20.0.0.0/24'] - - position_names = ['name', 'description', 'tenant_id', - 'type', 'endpoints'] - - position_values = [name, description, tenant_id, - endpoint_type, endpoints] - - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values) - - def test_create_endpoint_group_with_subnets(self): - # vpn-endpoint-group-create with subnet endpoints.""" - resource = 'endpoint_group' - cmd = endpoint_group.CreateEndpointGroup(test_cli20.MyApp(sys.stdout), - None) - tenant_id = 'mytenant-id' - my_id = 'my-id' - endpoint_type = 'subnet' - subnet = 'subnet-id' - endpoints = [subnet] - - args = ['--type', endpoint_type, - '--value', subnet, - '--tenant-id', tenant_id] - - position_names = ['type', 'endpoints', 'tenant_id'] - - position_values = [endpoint_type, endpoints, tenant_id] - - self._test_create_resource(resource, cmd, None, my_id, args, - position_names, position_values) - - def test_list_endpoint_group(self): - # vpn-endpoint-group-list. - resources = "endpoint_groups" - cmd = endpoint_group.ListEndpointGroup(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, True) - - def test_list_endpoint_group_pagination(self): - # vpn-endpoint-group-list. - resources = "endpoint_groups" - cmd = endpoint_group.ListEndpointGroup(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_endpoint_group_sort(self): - # vpn-endpoint-group-list --sort-key name --sort-key id - # --sort-key asc --sort-key desc - resources = "endpoint_groups" - cmd = endpoint_group.ListEndpointGroup(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_endpoint_group_limit(self): - # vpn-endpoint-group-list -P. - resources = "endpoint_groups" - cmd = endpoint_group.ListEndpointGroup(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_endpoint_group_id(self): - # vpn-endpoint-group-show test_id. - resource = 'endpoint_group' - cmd = endpoint_group.ShowEndpointGroup(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - - def test_show_endpoint_group_id_name(self): - # vpn-endpoint-group-show. - resource = 'endpoint_group' - cmd = endpoint_group.ShowEndpointGroup(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name']) - - def test_update_endpoint_group(self): - # vpn-endpoint-group-update myid --name newname --description newdesc. - resource = 'endpoint_group' - cmd = endpoint_group.UpdateEndpointGroup(test_cli20.MyApp(sys.stdout), - None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'newname', - '--description', 'newdesc'], - {'name': 'newname', - 'description': 'newdesc'}) - - def test_delete_endpoint_group(self): - # vpn-endpoint-group-delete my-id. - resource = 'endpoint_group' - cmd = endpoint_group.DeleteEndpointGroup(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args) diff --git a/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py b/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py deleted file mode 100644 index 33e326cab..000000000 --- a/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py +++ /dev/null @@ -1,241 +0,0 @@ -# (c) Copyright 2013 Hewlett-Packard Development Company, L.P. -# 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 sys - -from neutronclient.common import exceptions -from neutronclient.neutron.v2_0.vpn import ikepolicy -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20VpnIkePolicyJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['ikepolicy'] - - def _test_create_ikepolicy_all_params(self, auth='sha1', - expected_exc=None): - # vpn-ikepolicy-create all params. - resource = 'ikepolicy' - cmd = ikepolicy.CreateIKEPolicy(test_cli20.MyApp(sys.stdout), None) - name = 'ikepolicy1' - description = 'my-ike-policy' - auth_algorithm = auth - encryption_algorithm = 'aes-256' - ike_version = 'v1' - phase1_negotiation_mode = 'main' - pfs = 'group5' - tenant_id = 'my-tenant' - my_id = 'my-id' - lifetime = 'units=seconds,value=20000' - - args = [name, - '--description', description, - '--tenant-id', tenant_id, - '--auth-algorithm', auth_algorithm, - '--encryption-algorithm', encryption_algorithm, - '--ike-version', ike_version, - '--phase1-negotiation-mode', phase1_negotiation_mode, - '--lifetime', lifetime, - '--pfs', pfs] - - position_names = ['name', 'description', - 'auth_algorithm', 'encryption_algorithm', - 'phase1_negotiation_mode', - 'ike_version', 'pfs', - 'tenant_id'] - - position_values = [name, description, - auth_algorithm, encryption_algorithm, - phase1_negotiation_mode, ike_version, pfs, - tenant_id] - extra_body = { - 'lifetime': { - 'units': 'seconds', - 'value': 20000, - }, - } - - if not expected_exc: - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - extra_body=extra_body) - else: - self.assertRaises( - expected_exc, - self._test_create_resource, - resource, cmd, name, my_id, args, - position_names, position_values, - extra_body=extra_body) - - def test_create_ikepolicy_all_params(self): - self._test_create_ikepolicy_all_params() - - def test_create_ikepolicy_auth_sha256(self): - self._test_create_ikepolicy_all_params(auth='sha256') - - def test_create_ikepolicy_auth_sha384(self): - self._test_create_ikepolicy_all_params(auth='sha384') - - def test_create_ikepolicy_auth_sha512(self): - self._test_create_ikepolicy_all_params(auth='sha512') - - def test_create_ikepolicy_invalid_auth(self): - self._test_create_ikepolicy_all_params(auth='invalid', - expected_exc=SystemExit) - - def test_create_ikepolicy_with_limited_params(self): - # vpn-ikepolicy-create with limited params. - resource = 'ikepolicy' - cmd = ikepolicy.CreateIKEPolicy(test_cli20.MyApp(sys.stdout), None) - name = 'ikepolicy1' - auth_algorithm = 'sha1' - encryption_algorithm = 'aes-128' - ike_version = 'v1' - phase1_negotiation_mode = 'main' - pfs = 'group5' - tenant_id = 'my-tenant' - my_id = 'my-id' - - args = [name, - '--tenant-id', tenant_id] - - position_names = ['name', - 'auth_algorithm', 'encryption_algorithm', - 'phase1_negotiation_mode', - 'ike_version', 'pfs', - 'tenant_id'] - - position_values = [name, - auth_algorithm, encryption_algorithm, - phase1_negotiation_mode, - ike_version, pfs, - tenant_id] - - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values) - - def _test_lifetime_values(self, lifetime, expected_exc=None): - resource = 'ikepolicy' - cmd = ikepolicy.CreateIKEPolicy(test_cli20.MyApp(sys.stdout), None) - name = 'ikepolicy1' - description = 'my-ike-policy' - auth_algorithm = 'sha1' - encryption_algorithm = 'aes-256' - ike_version = 'v1' - phase1_negotiation_mode = 'main' - pfs = 'group5' - tenant_id = 'my-tenant' - my_id = 'my-id' - - args = [name, - '--description', description, - '--tenant-id', tenant_id, - '--auth-algorithm', auth_algorithm, - '--encryption-algorithm', encryption_algorithm, - '--ike-version', ike_version, - '--phase1-negotiation-mode', phase1_negotiation_mode, - '--lifetime', lifetime, - '--pfs', pfs] - - position_names = ['name', 'description', - 'auth_algorithm', 'encryption_algorithm', - 'phase1_negotiation_mode', - 'ike_version', 'pfs', - 'tenant_id'] - - position_values = [name, description, - auth_algorithm, encryption_algorithm, - phase1_negotiation_mode, ike_version, pfs, - tenant_id] - if not expected_exc: - expected_exc = exceptions.CommandError - self.assertRaises( - expected_exc, - self._test_create_resource, - resource, cmd, name, my_id, args, - position_names, position_values) - - def test_create_ikepolicy_with_invalid_lifetime_keys(self): - lifetime = 'uts=seconds,val=20000' - self._test_lifetime_values(lifetime, expected_exc=SystemExit) - - def test_create_ikepolicy_with_invalid_lifetime_value(self): - lifetime = 'units=seconds,value=-1' - self._test_lifetime_values(lifetime) - - def test_list_ikepolicy(self): - # vpn-ikepolicy-list. - resources = "ikepolicies" - cmd = ikepolicy.ListIKEPolicy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_ikepolicy_pagination(self): - # vpn-ikepolicy-list. - resources = "ikepolicies" - cmd = ikepolicy.ListIKEPolicy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_ikepolicy_sort(self): - # vpn-ikepolicy-list --sort-key name --sort-key id --sort-key asc - # --sort-key desc - resources = "ikepolicies" - cmd = ikepolicy.ListIKEPolicy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_ikepolicy_limit(self): - # vpn-ikepolicy-list -P. - resources = "ikepolicies" - cmd = ikepolicy.ListIKEPolicy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_ikepolicy_id(self): - # vpn-ikepolicy-show ikepolicy_id. - resource = 'ikepolicy' - cmd = ikepolicy.ShowIKEPolicy(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - - def test_show_ikepolicy_id_name(self): - # vpn-ikepolicy-show. - resource = 'ikepolicy' - cmd = ikepolicy.ShowIKEPolicy(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name']) - - def test_update_ikepolicy(self): - # vpn-ikepolicy-update myid --name newname --tags a b. - resource = 'ikepolicy' - cmd = ikepolicy.UpdateIKEPolicy(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'newname'], - {'name': 'newname', }) - # vpn-ikepolicy-update myid --pfs group2 --ike-version v2. - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--pfs', 'group2', - '--ike-version', 'v2'], - {'pfs': 'group2', - 'ike_version': 'v2'}) - - def test_delete_ikepolicy(self): - # vpn-ikepolicy-delete my-id. - resource = 'ikepolicy' - cmd = ikepolicy.DeleteIKEPolicy(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args) diff --git a/neutronclient/tests/unit/vpn/test_cli20_ipsec_site_connection.py b/neutronclient/tests/unit/vpn/test_cli20_ipsec_site_connection.py deleted file mode 100644 index cbab15cb8..000000000 --- a/neutronclient/tests/unit/vpn/test_cli20_ipsec_site_connection.py +++ /dev/null @@ -1,342 +0,0 @@ -# (c) Copyright 2013 Hewlett-Packard Development Company, L.P. -# 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 sys - -from neutronclient.common import exceptions -from neutronclient.neutron.v2_0.vpn import ipsec_site_connection -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20IPsecSiteConnectionJSON(test_cli20.CLITestV20Base): - - # TODO(pcm): Remove, once peer-cidr is deprecated completely - def test_create_ipsec_site_connection_all_params_using_peer_cidrs(self): - # ipsecsite-connection-create all params using peer CIDRs. - resource = 'ipsec_site_connection' - cmd = ipsec_site_connection.CreateIPsecSiteConnection( - test_cli20.MyApp(sys.stdout), None - ) - tenant_id = 'mytenant_id' - name = 'connection1' - my_id = 'my_id' - peer_address = '192.168.2.10' - peer_id = '192.168.2.10' - psk = 'abcd' - mtu = '1500' - initiator = 'bi-directional' - vpnservice_id = 'vpnservice_id' - ikepolicy_id = 'ikepolicy_id' - ipsecpolicy_id = 'ipsecpolicy_id' - peer_cidrs = ['192.168.3.0/24', '192.168.2.0/24'] - admin_state = True - description = 'my-vpn-connection' - dpd = 'action=restart,interval=30,timeout=120' - - args = ['--tenant-id', tenant_id, - '--peer-address', peer_address, '--peer-id', peer_id, - '--psk', psk, '--initiator', initiator, - '--vpnservice-id', vpnservice_id, - '--ikepolicy-id', ikepolicy_id, '--name', name, - '--ipsecpolicy-id', ipsecpolicy_id, '--mtu', mtu, - '--description', description, - '--peer-cidr', '192.168.3.0/24', - '--peer-cidr', '192.168.2.0/24', - '--dpd', dpd] - - position_names = ['name', 'tenant_id', 'admin_state_up', - 'peer_address', 'peer_id', 'peer_cidrs', - 'psk', 'mtu', 'initiator', 'description', - 'vpnservice_id', 'ikepolicy_id', - 'ipsecpolicy_id'] - - position_values = [name, tenant_id, admin_state, peer_address, - peer_id, peer_cidrs, psk, mtu, - initiator, description, - vpnservice_id, ikepolicy_id, ipsecpolicy_id] - extra_body = { - 'dpd': { - 'action': 'restart', - 'interval': 30, - 'timeout': 120, - }, - } - - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - extra_body=extra_body) - - def test_create_ipsec_site_conn_all_params(self): - # ipsecsite-connection-create all params using endpoint groups. - resource = 'ipsec_site_connection' - cmd = ipsec_site_connection.CreateIPsecSiteConnection( - test_cli20.MyApp(sys.stdout), None - ) - tenant_id = 'mytenant_id' - name = 'connection1' - my_id = 'my_id' - peer_address = '192.168.2.10' - peer_id = '192.168.2.10' - psk = 'abcd' - mtu = '1500' - initiator = 'bi-directional' - vpnservice_id = 'vpnservice_id' - ikepolicy_id = 'ikepolicy_id' - ipsecpolicy_id = 'ipsecpolicy_id' - local_ep_group = 'local-epg' - peer_ep_group = 'peer-epg' - admin_state = True - description = 'my-vpn-connection' - dpd = 'action=restart,interval=30,timeout=120' - - args = ['--tenant-id', tenant_id, - '--peer-address', peer_address, '--peer-id', peer_id, - '--psk', psk, '--initiator', initiator, - '--vpnservice-id', vpnservice_id, - '--ikepolicy-id', ikepolicy_id, '--name', name, - '--ipsecpolicy-id', ipsecpolicy_id, '--mtu', mtu, - '--description', description, - '--local-ep-group', local_ep_group, - '--peer-ep-group', peer_ep_group, - '--dpd', dpd] - - position_names = ['name', 'tenant_id', 'admin_state_up', - 'peer_address', 'peer_id', 'psk', 'mtu', - 'local_ep_group_id', 'peer_ep_group_id', - 'initiator', 'description', - 'vpnservice_id', 'ikepolicy_id', - 'ipsecpolicy_id'] - - position_values = [name, tenant_id, admin_state, peer_address, - peer_id, psk, mtu, local_ep_group, - peer_ep_group, initiator, description, - vpnservice_id, ikepolicy_id, ipsecpolicy_id] - extra_body = { - 'dpd': { - 'action': 'restart', - 'interval': 30, - 'timeout': 120, - }, - } - - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - extra_body=extra_body) - - def test_create_ipsec_site_connection_with_limited_params(self): - # ipsecsite-connection-create with limited params. - resource = 'ipsec_site_connection' - cmd = ipsec_site_connection.CreateIPsecSiteConnection( - test_cli20.MyApp(sys.stdout), None - ) - tenant_id = 'mytenant_id' - my_id = 'my_id' - peer_address = '192.168.2.10' - peer_id = '192.168.2.10' - psk = 'abcd' - mtu = '1500' - initiator = 'bi-directional' - vpnservice_id = 'vpnservice_id' - ikepolicy_id = 'ikepolicy_id' - ipsecpolicy_id = 'ipsecpolicy_id' - local_ep_group = 'local-epg' - peer_ep_group = 'peer-epg' - admin_state = True - - args = ['--tenant-id', tenant_id, - '--peer-address', peer_address, - '--peer-id', peer_id, - '--psk', psk, - '--vpnservice-id', vpnservice_id, - '--ikepolicy-id', ikepolicy_id, - '--ipsecpolicy-id', ipsecpolicy_id, - '--local-ep-group', local_ep_group, - '--peer-ep-group', peer_ep_group] - - position_names = ['tenant_id', 'admin_state_up', - 'peer_address', 'peer_id', - 'local_ep_group_id', 'peer_ep_group_id', - 'psk', 'mtu', 'initiator', - 'vpnservice_id', 'ikepolicy_id', - 'ipsecpolicy_id'] - - position_values = [tenant_id, admin_state, peer_address, peer_id, - local_ep_group, peer_ep_group, psk, mtu, initiator, - vpnservice_id, ikepolicy_id, ipsecpolicy_id] - - self._test_create_resource(resource, cmd, None, my_id, args, - position_names, position_values) - - def _test_create_failure(self, additional_args=None, expected_exc=None): - # Helper to test failure of IPSec site-to-site creation failure. - resource = 'ipsec_site_connection' - cmd = ipsec_site_connection.CreateIPsecSiteConnection( - test_cli20.MyApp(sys.stdout), None - ) - tenant_id = 'mytenant_id' - my_id = 'my_id' - peer_address = '192.168.2.10' - peer_id = '192.168.2.10' - psk = 'abcd' - mtu = '1500' - initiator = 'bi-directional' - vpnservice_id = 'vpnservice_id' - ikepolicy_id = 'ikepolicy_id' - ipsecpolicy_id = 'ipsecpolicy_id' - admin_state = True - - args = ['--tenant-id', tenant_id, - '--peer-address', peer_address, - '--peer-id', peer_id, - '--psk', psk, - '--vpnservice-id', vpnservice_id, - '--ikepolicy-id', ikepolicy_id, - '--ipsecpolicy-id', ipsecpolicy_id] - if additional_args is not None: - args += additional_args - position_names = ['tenant_id', 'admin_state_up', 'peer_address', - 'peer_id', 'psk', 'mtu', 'initiator', - 'local_ep_group_id', 'peer_ep_group_id', - 'vpnservice_id', 'ikepolicy_id', 'ipsecpolicy_id'] - - position_values = [tenant_id, admin_state, peer_address, peer_id, psk, - mtu, initiator, None, None, vpnservice_id, - ikepolicy_id, ipsecpolicy_id] - if not expected_exc: - expected_exc = exceptions.CommandError - self.assertRaises(expected_exc, - self._test_create_resource, - resource, cmd, None, my_id, args, - position_names, position_values) - - def test_fail_create_with_invalid_mtu(self): - # ipsecsite-connection-create with invalid dpd values. - bad_mtu = ['--mtu', '67'] - self._test_create_failure(bad_mtu) - - def test_fail_create_with_invalid_dpd_keys(self): - bad_dpd_key = ['--dpd', 'act=restart,interval=30,time=120'] - self._test_create_failure(bad_dpd_key, SystemExit) - - def test_fail_create_with_invalid_dpd_values(self): - bad_dpd_values = ['--dpd', 'action=hold,interval=30,timeout=-1'] - self._test_create_failure(bad_dpd_values) - - def test_fail_create_missing_endpoint_groups_or_cidr(self): - # Must provide either endpoint groups or peer cidrs. - self._test_create_failure() - - def test_fail_create_missing_peer_endpoint_group(self): - # Fails if dont have both endpoint groups - missing peer. - self._test_create_failure(['--local-ep-group', 'local-epg']) - - def test_fail_create_missing_local_endpoint_group(self): - # Fails if dont have both endpoint groups - missing local. - self._test_create_failure(['--peer-ep-group', 'peer-epg']) - - def test_fail_create_when_both_endpoints_and_peer_cidr(self): - # Cannot intermix endpoint groups and peer CIDRs for create. - additional_args = ['--local-ep-group', 'local-epg', - '--peer-ep-group', 'peer-epg', - '--peer-cidr', '10.2.0.0/24'] - self._test_create_failure(additional_args) - - def test_list_ipsec_site_connection(self): - # ipsecsite-connection-list. - resources = "ipsec_site_connections" - cmd = ipsec_site_connection.ListIPsecSiteConnection( - test_cli20.MyApp(sys.stdout), None - ) - self._test_list_resources(resources, cmd, True) - - def test_list_ipsec_site_connection_pagination(self): - # ipsecsite-connection-list. - resources = "ipsec_site_connections" - cmd = ipsec_site_connection.ListIPsecSiteConnection( - test_cli20.MyApp(sys.stdout), None - ) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_ipsec_site_connection_sort(self): - # ipsecsite-connection-list. - # --sort-key name --sort-key id --sort-key asc --sort-key desc - resources = "ipsec_site_connections" - cmd = ipsec_site_connection.ListIPsecSiteConnection( - test_cli20.MyApp(sys.stdout), None - ) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_ipsec_site_connection_limit(self): - # ipsecsite-connection-list -P. - resources = "ipsec_site_connections" - cmd = ipsec_site_connection.ListIPsecSiteConnection( - test_cli20.MyApp(sys.stdout), None - ) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_delete_ipsec_site_connection(self): - # ipsecsite-connection-delete my-id. - resource = 'ipsec_site_connection' - cmd = ipsec_site_connection.DeleteIPsecSiteConnection( - test_cli20.MyApp(sys.stdout), None - ) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args) - - def test_update_ipsec_site_connection(self): - # ipsecsite-connection-update myid --name Branch-new --tags a b. - resource = 'ipsec_site_connection' - cmd = ipsec_site_connection.UpdateIPsecSiteConnection( - test_cli20.MyApp(sys.stdout), None - ) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'Branch-new', - '--tags', 'a', 'b'], - {'name': 'Branch-new', - 'tags': ['a', 'b'], }) - # ipsecsite-connection-update myid --mtu 69 --initiator response-only - # --peer-id '192.168.2.11' --peer-ep-group 'update-grp' - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--mtu', '69', - '--initiator', 'response-only', - '--peer-id', '192.168.2.11', - '--peer-ep-group', 'update-grp'], - {'mtu': '69', - 'initiator': 'response-only', - 'peer_id': '192.168.2.11', - 'peer_ep_group_id': 'update-grp', },) - - def test_show_ipsec_site_connection_id(self): - # ipsecsite-connection-show test_id.""" - resource = 'ipsec_site_connection' - cmd = ipsec_site_connection.ShowIPsecSiteConnection( - test_cli20.MyApp(sys.stdout), None - ) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - - def test_show_ipsec_site_connection_id_name(self): - # ipsecsite-connection-show.""" - resource = 'ipsec_site_connection' - cmd = ipsec_site_connection.ShowIPsecSiteConnection( - test_cli20.MyApp(sys.stdout), None - ) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name']) diff --git a/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py b/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py deleted file mode 100644 index 8c7df4a6f..000000000 --- a/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py +++ /dev/null @@ -1,249 +0,0 @@ -# (c) Copyright 2013 Hewlett-Packard Development Company, L.P. -# 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 sys - -from neutronclient.common import exceptions -from neutronclient.neutron.v2_0.vpn import ipsecpolicy -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20VpnIpsecPolicyJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['ipsecpolicy'] - - def _test_create_ipsecpolicy_all_params(self, auth='sha1', - expected_exc=None): - # vpn-ipsecpolicy-create all params with dashes. - resource = 'ipsecpolicy' - cmd = ipsecpolicy.CreateIPsecPolicy(test_cli20.MyApp(sys.stdout), None) - name = 'ipsecpolicy1' - description = 'first-ipsecpolicy1' - auth_algorithm = auth - encryption_algorithm = 'aes-256' - encapsulation_mode = 'tunnel' - pfs = 'group5' - transform_protocol = 'ah' - tenant_id = 'my-tenant' - my_id = 'my-id' - lifetime = 'units=seconds,value=20000' - - args = [name, - '--description', description, - '--tenant-id', tenant_id, - '--auth-algorithm', auth_algorithm, - '--encryption-algorithm', encryption_algorithm, - '--transform-protocol', transform_protocol, - '--encapsulation-mode', encapsulation_mode, - '--lifetime', lifetime, - '--pfs', pfs] - - position_names = ['name', 'auth_algorithm', 'encryption_algorithm', - 'encapsulation_mode', 'description', - 'transform_protocol', 'pfs', - 'tenant_id'] - - position_values = [name, auth_algorithm, encryption_algorithm, - encapsulation_mode, description, - transform_protocol, pfs, - tenant_id] - extra_body = { - 'lifetime': { - 'units': 'seconds', - 'value': 20000, - }, - } - - if not expected_exc: - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - extra_body=extra_body) - else: - self.assertRaises( - expected_exc, - self._test_create_resource, - resource, cmd, name, my_id, args, - position_names, position_values, - extra_body=extra_body) - - def test_create_ipsecpolicy_all_params(self): - self._test_create_ipsecpolicy_all_params() - - def test_create_ipsecpolicy_auth_sha256(self): - self._test_create_ipsecpolicy_all_params(auth='sha256') - - def test_create_ipsecpolicy_auth_sha384(self): - self._test_create_ipsecpolicy_all_params(auth='sha384') - - def test_create_ipsecpolicy_auth_sha512(self): - self._test_create_ipsecpolicy_all_params(auth='sha512') - - def test_create_ipsecpolicy_invalid_auth(self): - self._test_create_ipsecpolicy_all_params(auth='invalid', - expected_exc=SystemExit) - - def test_create_ipsecpolicy_with_limited_params(self): - # vpn-ipsecpolicy-create with limited params. - resource = 'ipsecpolicy' - cmd = ipsecpolicy.CreateIPsecPolicy(test_cli20.MyApp(sys.stdout), None) - name = 'ipsecpolicy1' - auth_algorithm = 'sha1' - encryption_algorithm = 'aes-128' - encapsulation_mode = 'tunnel' - pfs = 'group5' - transform_protocol = 'esp' - tenant_id = 'my-tenant' - my_id = 'my-id' - - args = [name, - '--tenant-id', tenant_id] - - position_names = ['name', 'auth_algorithm', 'encryption_algorithm', - 'encapsulation_mode', - 'transform_protocol', 'pfs', - 'tenant_id'] - - position_values = [name, auth_algorithm, encryption_algorithm, - encapsulation_mode, - transform_protocol, pfs, - tenant_id] - - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values) - - def _test_lifetime_values(self, lifetime, expected_exc=None): - resource = 'ipsecpolicy' - cmd = ipsecpolicy.CreateIPsecPolicy(test_cli20.MyApp(sys.stdout), None) - name = 'ipsecpolicy1' - description = 'my-ipsec-policy' - auth_algorithm = 'sha1' - encryption_algorithm = 'aes-256' - ike_version = 'v1' - phase1_negotiation_mode = 'main' - pfs = 'group5' - tenant_id = 'my-tenant' - my_id = 'my-id' - - args = [name, - '--description', description, - '--tenant-id', tenant_id, - '--auth-algorithm', auth_algorithm, - '--encryption-algorithm', encryption_algorithm, - '--ike-version', ike_version, - '--phase1-negotiation-mode', phase1_negotiation_mode, - '--lifetime', lifetime, - '--pfs', pfs] - - position_names = ['name', 'description', - 'auth_algorithm', 'encryption_algorithm', - 'phase1_negotiation_mode', - 'ike_version', 'pfs', - 'tenant_id'] - - position_values = [name, description, - auth_algorithm, encryption_algorithm, - phase1_negotiation_mode, ike_version, pfs, - tenant_id] - if not expected_exc: - expected_exc = exceptions.CommandError - self.assertRaises( - expected_exc, - self._test_create_resource, - resource, cmd, name, my_id, args, - position_names, position_values) - - def test_create_ipsecpolicy_with_invalid_lifetime_keys(self): - lifetime = 'uts=seconds,val=20000' - self._test_lifetime_values(lifetime, SystemExit) - - def test_create_ipsecpolicy_with_invalid_lifetime_units(self): - lifetime = 'units=minutes,value=600' - self._test_lifetime_values(lifetime) - - def test_create_ipsecpolicy_with_invalid_lifetime_value(self): - lifetime = 'units=seconds,value=0' - self._test_lifetime_values(lifetime) - - def test_list_ipsecpolicy(self): - # vpn-ipsecpolicy-list. - resources = "ipsecpolicies" - cmd = ipsecpolicy.ListIPsecPolicy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_ipsecpolicy_pagination(self): - # vpn-ipsecpolicy-list. - resources = "ipsecpolicies" - cmd = ipsecpolicy.ListIPsecPolicy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_ipsecpolicy_sort(self): - # vpn-ipsecpolicy-list --sort-key name --sort-key id --sort-key asc - # --sort-key desc - resources = "ipsecpolicies" - cmd = ipsecpolicy.ListIPsecPolicy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_ipsecpolicy_limit(self): - # vpn-ipsecpolicy-list -P. - resources = "ipsecpolicies" - cmd = ipsecpolicy.ListIPsecPolicy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_ipsecpolicy_id(self): - # vpn-ipsecpolicy-show ipsecpolicy_id. - resource = 'ipsecpolicy' - cmd = ipsecpolicy.ShowIPsecPolicy(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - - def test_show_ipsecpolicy_id_name(self): - # vpn-ipsecpolicy-show. - resource = 'ipsecpolicy' - cmd = ipsecpolicy.ShowIPsecPolicy(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name']) - - def test_update_ipsecpolicy_name(self): - # vpn-ipsecpolicy-update myid --name newname --tags a b. - resource = 'ipsecpolicy' - cmd = ipsecpolicy.UpdateIPsecPolicy(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'newname'], - {'name': 'newname', }) - - def test_update_ipsecpolicy_other_params(self): - # vpn-ipsecpolicy-update myid --transform-protocol esp - # --pfs group14 --encapsulation-mode transport - resource = 'ipsecpolicy' - cmd = ipsecpolicy.UpdateIPsecPolicy(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--transform-protocol', 'esp', - '--pfs', 'group14', - '--encapsulation-mode', 'transport'], - {'transform_protocol': 'esp', - 'pfs': 'group14', - 'encapsulation_mode': 'transport', }) - - def test_delete_ipsecpolicy(self): - # vpn-ipsecpolicy-delete my-id. - resource = 'ipsecpolicy' - cmd = ipsecpolicy.DeleteIPsecPolicy(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args) diff --git a/neutronclient/tests/unit/vpn/test_cli20_vpnservice.py b/neutronclient/tests/unit/vpn/test_cli20_vpnservice.py deleted file mode 100644 index 3c4def84d..000000000 --- a/neutronclient/tests/unit/vpn/test_cli20_vpnservice.py +++ /dev/null @@ -1,157 +0,0 @@ -# (c) Copyright 2013 Hewlett-Packard Development Company, L.P. -# 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 sys - -from neutronclient.neutron.v2_0.vpn import vpnservice -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20VpnServiceJSON(test_cli20.CLITestV20Base): - - def test_create_vpnservice_all_params(self): - # vpn-service-create all params. - resource = 'vpnservice' - cmd = vpnservice.CreateVPNService(test_cli20.MyApp(sys.stdout), None) - subnet = 'mysubnet-id' - router = 'myrouter-id' - tenant_id = 'mytenant-id' - my_id = 'my-id' - name = 'myvpnservice' - description = 'my-vpn-service' - admin_state = True - - args = ['--name', name, - '--description', description, - router, - subnet, - '--tenant-id', tenant_id] - - position_names = ['admin_state_up', 'name', 'description', - 'subnet_id', 'router_id', - 'tenant_id'] - - position_values = [admin_state, name, description, - subnet, router, tenant_id] - - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values) - - def test_create_vpnservice_with_limited_params(self): - # vpn-service-create with limited params. - resource = 'vpnservice' - cmd = vpnservice.CreateVPNService(test_cli20.MyApp(sys.stdout), None) - subnet = 'mysubnet-id' - router = 'myrouter-id' - tenant_id = 'mytenant-id' - my_id = 'my-id' - admin_state = True - - args = [router, - subnet, - '--tenant-id', tenant_id] - - position_names = ['admin_state_up', - 'subnet_id', 'router_id', - 'tenant_id'] - - position_values = [admin_state, subnet, router, tenant_id] - - self._test_create_resource(resource, cmd, None, my_id, args, - position_names, position_values) - - def test_create_vpnservice_without_subnet(self): - # vpn-service-create with no subnet provided. - resource = 'vpnservice' - cmd = vpnservice.CreateVPNService(test_cli20.MyApp(sys.stdout), None) - router = 'myrouter-id' - tenant_id = 'mytenant-id' - my_id = 'my-id' - admin_state = True - - args = [router, - '--tenant-id', tenant_id] - - position_names = ['admin_state_up', - 'subnet_id', 'router_id', - 'tenant_id'] - - position_values = [admin_state, None, router, tenant_id] - - self._test_create_resource(resource, cmd, None, my_id, args, - position_names, position_values) - - def test_list_vpnservice(self): - # vpn-service-list. - resources = "vpnservices" - cmd = vpnservice.ListVPNService(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_vpnservice_pagination(self): - # vpn-service-list. - resources = "vpnservices" - cmd = vpnservice.ListVPNService(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_vpnservice_sort(self): - # vpn-service-list --sort-key name --sort-key id --sort-key asc - # --sort-key desc - resources = "vpnservices" - cmd = vpnservice.ListVPNService(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_vpnservice_limit(self): - # vpn-service-list -P. - resources = "vpnservices" - cmd = vpnservice.ListVPNService(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_vpnservice_id(self): - # vpn-service-show test_id. - resource = 'vpnservice' - cmd = vpnservice.ShowVPNService(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - - def test_show_vpnservice_id_name(self): - # vpn-service-show.""" - resource = 'vpnservice' - cmd = vpnservice.ShowVPNService(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name']) - - def test_update_vpnservice(self): - # vpn-service-update myid --name newname --tags a b. - resource = 'vpnservice' - cmd = vpnservice.UpdateVPNService(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'newname'], - {'name': 'newname', }) - # vpn-service-update myid --admin-state-up False - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--admin-state-up', 'False'], - {'admin_state_up': 'False', }) - - def test_delete_vpnservice(self): - # vpn-service-delete my-id. - resource = 'vpnservice' - cmd = vpnservice.DeleteVPNService(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args) diff --git a/neutronclient/tests/unit/vpn/test_utils.py b/neutronclient/tests/unit/vpn/test_utils.py deleted file mode 100644 index c39f7ceb5..000000000 --- a/neutronclient/tests/unit/vpn/test_utils.py +++ /dev/null @@ -1,130 +0,0 @@ -# (c) Copyright 2013 Hewlett-Packard Development Company, L.P. -# 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 testtools - -from neutronclient.common import exceptions -from neutronclient.common import utils -from neutronclient.neutron.v2_0.vpn import utils as vpn_utils - - -class TestVPNUtils(testtools.TestCase): - - def test_validate_lifetime_dictionary_seconds(self): - input_str = utils.str2dict("units=seconds,value=3600") - self.assertIsNone(vpn_utils.validate_lifetime_dict(input_str)) - - def test_validate_dpd_dictionary_action_hold(self): - input_str = utils.str2dict("action=hold,interval=30,timeout=120") - self.assertIsNone(vpn_utils.validate_dpd_dict(input_str)) - - def test_validate_dpd_dictionary_action_restart(self): - input_str = utils.str2dict("action=restart,interval=30,timeout=120") - self.assertIsNone(vpn_utils.validate_dpd_dict(input_str)) - - def test_validate_dpd_dictionary_action_restart_by_peer(self): - input_str = utils.str2dict( - "action=restart-by-peer,interval=30,timeout=120" - ) - self.assertIsNone(vpn_utils.validate_dpd_dict(input_str)) - - def test_validate_dpd_dictionary_action_clear(self): - input_str = utils.str2dict('action=clear,interval=30,timeout=120') - self.assertIsNone(vpn_utils.validate_dpd_dict(input_str)) - - def test_validate_dpd_dictionary_action_disabled(self): - input_str = utils.str2dict('action=disabled,interval=30,timeout=120') - self.assertIsNone(vpn_utils.validate_dpd_dict(input_str)) - - def test_validate_lifetime_dictionary_invalid_unit_key(self): - input_str = utils.str2dict('ut=seconds,value=3600') - self._test_validate_lifetime_negative_test_case(input_str) - - def test_validate_lifetime_dictionary_invalid_unit_key_value(self): - input_str = utils.str2dict('units=seconds,val=3600') - self._test_validate_lifetime_negative_test_case(input_str) - - def test_validate_lifetime_dictionary_unsupported_units(self): - input_str = utils.str2dict('units=minutes,value=3600') - self._test_validate_lifetime_negative_test_case(input_str) - - def test_validate_lifetime_dictionary_invalid_empty_unit(self): - input_str = utils.str2dict('units=,value=3600') - self._test_validate_lifetime_negative_test_case(input_str) - - def test_validate_lifetime_dictionary_under_minimum_integer_value(self): - input_str = utils.str2dict('units=seconds,value=59') - self._test_validate_lifetime_negative_test_case(input_str) - - def test_validate_lifetime_dictionary_negative_integer_value(self): - input_str = utils.str2dict('units=seconds,value=-1') - self._test_validate_lifetime_negative_test_case(input_str) - - def test_validate_lifetime_dictionary_empty_value(self): - input_str = utils.str2dict('units=seconds,value=') - self._test_validate_lifetime_negative_test_case(input_str) - - def test_validate_dpd_dictionary_invalid_key_action(self): - input_str = utils.str2dict('act=hold,interval=30,timeout=120') - self._test_validate_dpd_negative_test_case(input_str) - - def test_validate_dpd_dictionary_invalid_key_interval(self): - input_str = utils.str2dict('action=hold,int=30,timeout=120') - self._test_validate_dpd_negative_test_case(input_str) - - def test_validate_dpd_dictionary_invalid_key_timeout(self): - input_str = utils.str2dict('action=hold,interval=30,tiut=120') - self._test_validate_dpd_negative_test_case(input_str) - - def test_validate_dpd_dictionary_unsupported_action(self): - input_str = utils.str2dict('action=bye-bye,interval=30,timeout=120') - self._test_validate_dpd_negative_test_case(input_str) - - def test_validate_dpd_dictionary_empty_action(self): - input_str = utils.str2dict('action=,interval=30,timeout=120') - self._test_validate_dpd_negative_test_case(input_str) - - def test_validate_dpd_dictionary_empty_interval(self): - input_str = utils.str2dict('action=hold,interval=,timeout=120') - self._test_validate_dpd_negative_test_case(input_str) - - def test_validate_dpd_dictionary_negative_interval_value(self): - input_str = utils.str2dict('action=hold,interval=-1,timeout=120') - self._test_validate_lifetime_negative_test_case(input_str) - - def test_validate_dpd_dictionary_zero_timeout(self): - input_str = utils.str2dict('action=hold,interval=30,timeout=0') - self._test_validate_dpd_negative_test_case(input_str) - - def test_validate_dpd_dictionary_empty_timeout(self): - input_str = utils.str2dict('action=hold,interval=30,timeout=') - self._test_validate_dpd_negative_test_case(input_str) - - def test_validate_dpd_dictionary_negative_timeout_value(self): - input_str = utils.str2dict('action=hold,interval=30,timeout=-1') - self._test_validate_lifetime_negative_test_case(input_str) - - def _test_validate_lifetime_negative_test_case(self, input_str): - """Generic handler for negative lifetime tests.""" - self.assertRaises(exceptions.CommandError, - vpn_utils.validate_lifetime_dict, - (input_str)) - - def _test_validate_dpd_negative_test_case(self, input_str): - """Generic handler for negative lifetime tests.""" - self.assertRaises(exceptions.CommandError, - vpn_utils.validate_lifetime_dict, - (input_str)) diff --git a/releasenotes/notes/remove-cli-code-53969e9aa927e530.yaml b/releasenotes/notes/remove-cli-code-53969e9aa927e530.yaml new file mode 100644 index 000000000..92ab5423c --- /dev/null +++ b/releasenotes/notes/remove-cli-code-53969e9aa927e530.yaml @@ -0,0 +1,12 @@ +prelude: > + | + This new version of ``python-neutronclient`` does not include the + command line interface code. The "neutron" script is no longer + supported. This project only contains the OpenStack Client bindings + that are currently being moved to OpenStack SDK + _. +deprecations: + - | + This project no longer provides CLI support. All code has been removed. + Please use openstack CLI instead. See `openstack CLI command list + `_. diff --git a/setup.cfg b/setup.cfg index 432ec682f..3bab0a176 100644 --- a/setup.cfg +++ b/setup.cfg @@ -26,9 +26,6 @@ packages = neutronclient [entry_points] -console_scripts = - neutron = neutronclient.shell:main - openstack.cli.extension = neutronclient = neutronclient.osc.plugin @@ -163,287 +160,3 @@ openstack.neutronclient.v2 = vpn_ipsec_site_connection_show = neutronclient.osc.v2.vpnaas.ipsec_site_connection:ShowIPsecSiteConnection network_onboard_subnets = neutronclient.osc.v2.subnet_onboard.subnet_onboard:NetworkOnboardSubnets - -neutron.cli.v2 = - bash-completion = neutronclient.shell:BashCompletionCommand - - net-list = neutronclient.neutron.v2_0.network:ListNetwork - net-external-list = neutronclient.neutron.v2_0.network:ListExternalNetwork - net-show = neutronclient.neutron.v2_0.network:ShowNetwork - net-create = neutronclient.neutron.v2_0.network:CreateNetwork - net-delete = neutronclient.neutron.v2_0.network:DeleteNetwork - net-update = neutronclient.neutron.v2_0.network:UpdateNetwork - - subnet-list = neutronclient.neutron.v2_0.subnet:ListSubnet - subnet-show = neutronclient.neutron.v2_0.subnet:ShowSubnet - subnet-create = neutronclient.neutron.v2_0.subnet:CreateSubnet - subnet-delete = neutronclient.neutron.v2_0.subnet:DeleteSubnet - subnet-update = neutronclient.neutron.v2_0.subnet:UpdateSubnet - - subnetpool-list = neutronclient.neutron.v2_0.subnetpool:ListSubnetPool - subnetpool-show = neutronclient.neutron.v2_0.subnetpool:ShowSubnetPool - subnetpool-create = neutronclient.neutron.v2_0.subnetpool:CreateSubnetPool - subnetpool-delete = neutronclient.neutron.v2_0.subnetpool:DeleteSubnetPool - subnetpool-update = neutronclient.neutron.v2_0.subnetpool:UpdateSubnetPool - - port-list = neutronclient.neutron.v2_0.port:ListPort - port-show = neutronclient.neutron.v2_0.port:ShowPort - port-create = neutronclient.neutron.v2_0.port:CreatePort - port-delete = neutronclient.neutron.v2_0.port:DeletePort - port-update = neutronclient.neutron.v2_0.port:UpdatePort - - purge = neutronclient.neutron.v2_0.purge:Purge - - quota-list = neutronclient.neutron.v2_0.quota:ListQuota - quota-show = neutronclient.neutron.v2_0.quota:ShowQuota - quota-default-show = neutronclient.neutron.v2_0.quota:ShowQuotaDefault - quota-delete = neutronclient.neutron.v2_0.quota:DeleteQuota - quota-update = neutronclient.neutron.v2_0.quota:UpdateQuota - - ext-list = neutronclient.neutron.v2_0.extension:ListExt - ext-show = neutronclient.neutron.v2_0.extension:ShowExt - - router-list = neutronclient.neutron.v2_0.router:ListRouter - router-port-list = neutronclient.neutron.v2_0.port:ListRouterPort - router-show = neutronclient.neutron.v2_0.router:ShowRouter - router-create = neutronclient.neutron.v2_0.router:CreateRouter - router-delete = neutronclient.neutron.v2_0.router:DeleteRouter - router-update = neutronclient.neutron.v2_0.router:UpdateRouter - router-interface-add = neutronclient.neutron.v2_0.router:AddInterfaceRouter - router-interface-delete = neutronclient.neutron.v2_0.router:RemoveInterfaceRouter - router-gateway-set = neutronclient.neutron.v2_0.router:SetGatewayRouter - router-gateway-clear = neutronclient.neutron.v2_0.router:RemoveGatewayRouter - - floatingip-list = neutronclient.neutron.v2_0.floatingip:ListFloatingIP - floatingip-show = neutronclient.neutron.v2_0.floatingip:ShowFloatingIP - floatingip-create = neutronclient.neutron.v2_0.floatingip:CreateFloatingIP - floatingip-delete = neutronclient.neutron.v2_0.floatingip:DeleteFloatingIP - floatingip-associate = neutronclient.neutron.v2_0.floatingip:AssociateFloatingIP - floatingip-disassociate = neutronclient.neutron.v2_0.floatingip:DisassociateFloatingIP - - security-group-list = neutronclient.neutron.v2_0.securitygroup:ListSecurityGroup - security-group-show = neutronclient.neutron.v2_0.securitygroup:ShowSecurityGroup - security-group-create = neutronclient.neutron.v2_0.securitygroup:CreateSecurityGroup - security-group-delete = neutronclient.neutron.v2_0.securitygroup:DeleteSecurityGroup - security-group-update = neutronclient.neutron.v2_0.securitygroup:UpdateSecurityGroup - security-group-rule-list = neutronclient.neutron.v2_0.securitygroup:ListSecurityGroupRule - security-group-rule-show = neutronclient.neutron.v2_0.securitygroup:ShowSecurityGroupRule - security-group-rule-create = neutronclient.neutron.v2_0.securitygroup:CreateSecurityGroupRule - security-group-rule-delete = neutronclient.neutron.v2_0.securitygroup:DeleteSecurityGroupRule - - agent-list = neutronclient.neutron.v2_0.agent:ListAgent - agent-show = neutronclient.neutron.v2_0.agent:ShowAgent - agent-delete = neutronclient.neutron.v2_0.agent:DeleteAgent - agent-update = neutronclient.neutron.v2_0.agent:UpdateAgent - - dhcp-agent-network-add = neutronclient.neutron.v2_0.agentscheduler:AddNetworkToDhcpAgent - dhcp-agent-network-remove = neutronclient.neutron.v2_0.agentscheduler:RemoveNetworkFromDhcpAgent - net-list-on-dhcp-agent = neutronclient.neutron.v2_0.agentscheduler:ListNetworksOnDhcpAgent - dhcp-agent-list-hosting-net = neutronclient.neutron.v2_0.agentscheduler:ListDhcpAgentsHostingNetwork - - l3-agent-router-add = neutronclient.neutron.v2_0.agentscheduler:AddRouterToL3Agent - l3-agent-router-remove = neutronclient.neutron.v2_0.agentscheduler:RemoveRouterFromL3Agent - router-list-on-l3-agent = neutronclient.neutron.v2_0.agentscheduler:ListRoutersOnL3Agent - l3-agent-list-hosting-router = neutronclient.neutron.v2_0.agentscheduler:ListL3AgentsHostingRouter - - lb-pool-list-on-agent = neutronclient.neutron.v2_0.agentscheduler:ListPoolsOnLbaasAgent - lb-agent-hosting-pool = neutronclient.neutron.v2_0.agentscheduler:GetLbaasAgentHostingPool - - lbaas-loadbalancer-list-on-agent = neutronclient.neutron.v2_0.agentscheduler:ListLoadBalancersOnLbaasAgent - lbaas-agent-hosting-loadbalancer = neutronclient.neutron.v2_0.agentscheduler:GetLbaasAgentHostingLoadBalancer - - service-provider-list = neutronclient.neutron.v2_0.servicetype:ListServiceProvider - - rbac-create = neutronclient.neutron.v2_0.rbac:CreateRBACPolicy - rbac-update = neutronclient.neutron.v2_0.rbac:UpdateRBACPolicy - rbac-list = neutronclient.neutron.v2_0.rbac:ListRBACPolicy - rbac-show = neutronclient.neutron.v2_0.rbac:ShowRBACPolicy - rbac-delete = neutronclient.neutron.v2_0.rbac:DeleteRBACPolicy - - address-scope-list = neutronclient.neutron.v2_0.address_scope:ListAddressScope - address-scope-show = neutronclient.neutron.v2_0.address_scope:ShowAddressScope - address-scope-create = neutronclient.neutron.v2_0.address_scope:CreateAddressScope - address-scope-delete = neutronclient.neutron.v2_0.address_scope:DeleteAddressScope - address-scope-update = neutronclient.neutron.v2_0.address_scope:UpdateAddressScope - - availability-zone-list = neutronclient.neutron.v2_0.availability_zone:ListAvailabilityZone - - auto-allocated-topology-show = neutronclient.neutron.v2_0.auto_allocated_topology:ShowAutoAllocatedTopology - auto-allocated-topology-delete = neutronclient.neutron.v2_0.auto_allocated_topology:DeleteAutoAllocatedTopology - - net-ip-availability-list = neutronclient.neutron.v2_0.network_ip_availability:ListIpAvailability - net-ip-availability-show = neutronclient.neutron.v2_0.network_ip_availability:ShowIpAvailability - - tag-add = neutronclient.neutron.v2_0.tag:AddTag - tag-replace = neutronclient.neutron.v2_0.tag:ReplaceTag - tag-remove = neutronclient.neutron.v2_0.tag:RemoveTag - - qos-policy-list = neutronclient.neutron.v2_0.qos.policy:ListQoSPolicy - qos-policy-show = neutronclient.neutron.v2_0.qos.policy:ShowQoSPolicy - qos-policy-create = neutronclient.neutron.v2_0.qos.policy:CreateQoSPolicy - qos-policy-update = neutronclient.neutron.v2_0.qos.policy:UpdateQoSPolicy - qos-policy-delete = neutronclient.neutron.v2_0.qos.policy:DeleteQoSPolicy - qos-bandwidth-limit-rule-create = neutronclient.neutron.v2_0.qos.bandwidth_limit_rule:CreateQoSBandwidthLimitRule - qos-bandwidth-limit-rule-show = neutronclient.neutron.v2_0.qos.bandwidth_limit_rule:ShowQoSBandwidthLimitRule - qos-bandwidth-limit-rule-list = neutronclient.neutron.v2_0.qos.bandwidth_limit_rule:ListQoSBandwidthLimitRules - qos-bandwidth-limit-rule-update = neutronclient.neutron.v2_0.qos.bandwidth_limit_rule:UpdateQoSBandwidthLimitRule - qos-bandwidth-limit-rule-delete = neutronclient.neutron.v2_0.qos.bandwidth_limit_rule:DeleteQoSBandwidthLimitRule - qos-dscp-marking-rule-create = neutronclient.neutron.v2_0.qos.dscp_marking_rule:CreateQoSDscpMarkingRule - qos-dscp-marking-rule-show = neutronclient.neutron.v2_0.qos.dscp_marking_rule:ShowQoSDscpMarkingRule - qos-dscp-marking-rule-list = neutronclient.neutron.v2_0.qos.dscp_marking_rule:ListQoSDscpMarkingRules - qos-dscp-marking-rule-update = neutronclient.neutron.v2_0.qos.dscp_marking_rule:UpdateQoSDscpMarkingRule - qos-dscp-marking-rule-delete = neutronclient.neutron.v2_0.qos.dscp_marking_rule:DeleteQoSDscpMarkingRule - qos-minimum-bandwidth-rule-create = neutronclient.neutron.v2_0.qos.minimum_bandwidth_rule:CreateQoSMinimumBandwidthRule - qos-minimum-bandwidth-rule-show = neutronclient.neutron.v2_0.qos.minimum_bandwidth_rule:ShowQoSMinimumBandwidthRule - qos-minimum-bandwidth-rule-list = neutronclient.neutron.v2_0.qos.minimum_bandwidth_rule:ListQoSMinimumBandwidthRules - qos-minimum-bandwidth-rule-update = neutronclient.neutron.v2_0.qos.minimum_bandwidth_rule:UpdateQoSMinimumBandwidthRule - qos-minimum-bandwidth-rule-delete = neutronclient.neutron.v2_0.qos.minimum_bandwidth_rule:DeleteQoSMinimumBandwidthRule - qos-available-rule-types = neutronclient.neutron.v2_0.qos.rule:ListQoSRuleTypes - - flavor-list = neutronclient.neutron.v2_0.flavor.flavor:ListFlavor - flavor-show = neutronclient.neutron.v2_0.flavor.flavor:ShowFlavor - flavor-create = neutronclient.neutron.v2_0.flavor.flavor:CreateFlavor - flavor-delete = neutronclient.neutron.v2_0.flavor.flavor:DeleteFlavor - flavor-update = neutronclient.neutron.v2_0.flavor.flavor:UpdateFlavor - flavor-associate = neutronclient.neutron.v2_0.flavor.flavor:AssociateFlavor - flavor-disassociate = neutronclient.neutron.v2_0.flavor.flavor:DisassociateFlavor - flavor-profile-list = neutronclient.neutron.v2_0.flavor.flavor_profile:ListFlavorProfile - flavor-profile-show = neutronclient.neutron.v2_0.flavor.flavor_profile:ShowFlavorProfile - flavor-profile-create = neutronclient.neutron.v2_0.flavor.flavor_profile:CreateFlavorProfile - flavor-profile-delete = neutronclient.neutron.v2_0.flavor.flavor_profile:DeleteFlavorProfile - flavor-profile-update = neutronclient.neutron.v2_0.flavor.flavor_profile:UpdateFlavorProfile - - meter-label-create = neutronclient.neutron.v2_0.metering:CreateMeteringLabel - meter-label-list = neutronclient.neutron.v2_0.metering:ListMeteringLabel - meter-label-show = neutronclient.neutron.v2_0.metering:ShowMeteringLabel - meter-label-delete = neutronclient.neutron.v2_0.metering:DeleteMeteringLabel - meter-label-rule-create = neutronclient.neutron.v2_0.metering:CreateMeteringLabelRule - meter-label-rule-list = neutronclient.neutron.v2_0.metering:ListMeteringLabelRule - meter-label-rule-show = neutronclient.neutron.v2_0.metering:ShowMeteringLabelRule - meter-label-rule-delete = neutronclient.neutron.v2_0.metering:DeleteMeteringLabelRule - - firewall-rule-list = neutronclient.neutron.v2_0.fw.firewallrule:ListFirewallRule - firewall-rule-show = neutronclient.neutron.v2_0.fw.firewallrule:ShowFirewallRule - firewall-rule-create = neutronclient.neutron.v2_0.fw.firewallrule:CreateFirewallRule - firewall-rule-update = neutronclient.neutron.v2_0.fw.firewallrule:UpdateFirewallRule - firewall-rule-delete = neutronclient.neutron.v2_0.fw.firewallrule:DeleteFirewallRule - firewall-policy-list = neutronclient.neutron.v2_0.fw.firewallpolicy:ListFirewallPolicy - firewall-policy-show = neutronclient.neutron.v2_0.fw.firewallpolicy:ShowFirewallPolicy - firewall-policy-create = neutronclient.neutron.v2_0.fw.firewallpolicy:CreateFirewallPolicy - firewall-policy-update = neutronclient.neutron.v2_0.fw.firewallpolicy:UpdateFirewallPolicy - firewall-policy-delete = neutronclient.neutron.v2_0.fw.firewallpolicy:DeleteFirewallPolicy - firewall-policy-insert-rule = neutronclient.neutron.v2_0.fw.firewallpolicy:FirewallPolicyInsertRule - firewall-policy-remove-rule = neutronclient.neutron.v2_0.fw.firewallpolicy:FirewallPolicyRemoveRule - firewall-list = neutronclient.neutron.v2_0.fw.firewall:ListFirewall - firewall-show = neutronclient.neutron.v2_0.fw.firewall:ShowFirewall - firewall-create = neutronclient.neutron.v2_0.fw.firewall:CreateFirewall - firewall-update = neutronclient.neutron.v2_0.fw.firewall:UpdateFirewall - firewall-delete = neutronclient.neutron.v2_0.fw.firewall:DeleteFirewall - - bgp-dragent-speaker-add = neutronclient.neutron.v2_0.bgp.dragentscheduler:AddBGPSpeakerToDRAgent - bgp-dragent-speaker-remove = neutronclient.neutron.v2_0.bgp.dragentscheduler:RemoveBGPSpeakerFromDRAgent - bgp-speaker-list-on-dragent = neutronclient.neutron.v2_0.bgp.dragentscheduler:ListBGPSpeakersOnDRAgent - bgp-dragent-list-hosting-speaker = neutronclient.neutron.v2_0.bgp.dragentscheduler:ListDRAgentsHostingBGPSpeaker - bgp-speaker-list = neutronclient.neutron.v2_0.bgp.speaker:ListSpeakers - bgp-speaker-advertiseroute-list = neutronclient.neutron.v2_0.bgp.speaker:ListRoutesAdvertisedBySpeaker - bgp-speaker-show = neutronclient.neutron.v2_0.bgp.speaker:ShowSpeaker - bgp-speaker-create = neutronclient.neutron.v2_0.bgp.speaker:CreateSpeaker - bgp-speaker-update = neutronclient.neutron.v2_0.bgp.speaker:UpdateSpeaker - bgp-speaker-delete = neutronclient.neutron.v2_0.bgp.speaker:DeleteSpeaker - bgp-speaker-peer-add = neutronclient.neutron.v2_0.bgp.speaker:AddPeerToSpeaker - bgp-speaker-peer-remove = neutronclient.neutron.v2_0.bgp.speaker:RemovePeerFromSpeaker - bgp-speaker-network-add = neutronclient.neutron.v2_0.bgp.speaker:AddNetworkToSpeaker - bgp-speaker-network-remove = neutronclient.neutron.v2_0.bgp.speaker:RemoveNetworkFromSpeaker - bgp-peer-list = neutronclient.neutron.v2_0.bgp.peer:ListPeers - bgp-peer-show = neutronclient.neutron.v2_0.bgp.peer:ShowPeer - bgp-peer-create = neutronclient.neutron.v2_0.bgp.peer:CreatePeer - bgp-peer-update = neutronclient.neutron.v2_0.bgp.peer:UpdatePeer - bgp-peer-delete = neutronclient.neutron.v2_0.bgp.peer:DeletePeer - - lbaas-loadbalancer-list = neutronclient.neutron.v2_0.lb.v2.loadbalancer:ListLoadBalancer - lbaas-loadbalancer-show = neutronclient.neutron.v2_0.lb.v2.loadbalancer:ShowLoadBalancer - lbaas-loadbalancer-create = neutronclient.neutron.v2_0.lb.v2.loadbalancer:CreateLoadBalancer - lbaas-loadbalancer-update = neutronclient.neutron.v2_0.lb.v2.loadbalancer:UpdateLoadBalancer - lbaas-loadbalancer-delete = neutronclient.neutron.v2_0.lb.v2.loadbalancer:DeleteLoadBalancer - lbaas-loadbalancer-stats = neutronclient.neutron.v2_0.lb.v2.loadbalancer:RetrieveLoadBalancerStats - lbaas-loadbalancer-status = neutronclient.neutron.v2_0.lb.v2.loadbalancer:RetrieveLoadBalancerStatus - lbaas-listener-list = neutronclient.neutron.v2_0.lb.v2.listener:ListListener - lbaas-listener-show = neutronclient.neutron.v2_0.lb.v2.listener:ShowListener - lbaas-listener-create = neutronclient.neutron.v2_0.lb.v2.listener:CreateListener - lbaas-listener-update = neutronclient.neutron.v2_0.lb.v2.listener:UpdateListener - lbaas-listener-delete = neutronclient.neutron.v2_0.lb.v2.listener:DeleteListener - lbaas-l7policy-list = neutronclient.neutron.v2_0.lb.v2.l7policy:ListL7Policy - lbaas-l7policy-show = neutronclient.neutron.v2_0.lb.v2.l7policy:ShowL7Policy - lbaas-l7policy-create = neutronclient.neutron.v2_0.lb.v2.l7policy:CreateL7Policy - lbaas-l7policy-update = neutronclient.neutron.v2_0.lb.v2.l7policy:UpdateL7Policy - lbaas-l7policy-delete = neutronclient.neutron.v2_0.lb.v2.l7policy:DeleteL7Policy - lbaas-l7rule-list = neutronclient.neutron.v2_0.lb.v2.l7rule:ListL7Rule - lbaas-l7rule-show = neutronclient.neutron.v2_0.lb.v2.l7rule:ShowL7Rule - lbaas-l7rule-create = neutronclient.neutron.v2_0.lb.v2.l7rule:CreateL7Rule - lbaas-l7rule-update = neutronclient.neutron.v2_0.lb.v2.l7rule:UpdateL7Rule - lbaas-l7rule-delete = neutronclient.neutron.v2_0.lb.v2.l7rule:DeleteL7Rule - lbaas-pool-list = neutronclient.neutron.v2_0.lb.v2.pool:ListPool - lbaas-pool-show = neutronclient.neutron.v2_0.lb.v2.pool:ShowPool - lbaas-pool-create = neutronclient.neutron.v2_0.lb.v2.pool:CreatePool - lbaas-pool-update = neutronclient.neutron.v2_0.lb.v2.pool:UpdatePool - lbaas-pool-delete = neutronclient.neutron.v2_0.lb.v2.pool:DeletePool - lbaas-healthmonitor-list = neutronclient.neutron.v2_0.lb.v2.healthmonitor:ListHealthMonitor - lbaas-healthmonitor-show = neutronclient.neutron.v2_0.lb.v2.healthmonitor:ShowHealthMonitor - lbaas-healthmonitor-create = neutronclient.neutron.v2_0.lb.v2.healthmonitor:CreateHealthMonitor - lbaas-healthmonitor-update = neutronclient.neutron.v2_0.lb.v2.healthmonitor:UpdateHealthMonitor - lbaas-healthmonitor-delete = neutronclient.neutron.v2_0.lb.v2.healthmonitor:DeleteHealthMonitor - lbaas-member-list = neutronclient.neutron.v2_0.lb.v2.member:ListMember - lbaas-member-show = neutronclient.neutron.v2_0.lb.v2.member:ShowMember - lbaas-member-create = neutronclient.neutron.v2_0.lb.v2.member:CreateMember - lbaas-member-update = neutronclient.neutron.v2_0.lb.v2.member:UpdateMember - lbaas-member-delete = neutronclient.neutron.v2_0.lb.v2.member:DeleteMember - - lb-vip-list = neutronclient.neutron.v2_0.lb.vip:ListVip - lb-vip-show = neutronclient.neutron.v2_0.lb.vip:ShowVip - lb-vip-create = neutronclient.neutron.v2_0.lb.vip:CreateVip - lb-vip-update = neutronclient.neutron.v2_0.lb.vip:UpdateVip - lb-vip-delete = neutronclient.neutron.v2_0.lb.vip:DeleteVip - lb-pool-list = neutronclient.neutron.v2_0.lb.pool:ListPool - lb-pool-show = neutronclient.neutron.v2_0.lb.pool:ShowPool - lb-pool-create = neutronclient.neutron.v2_0.lb.pool:CreatePool - lb-pool-update = neutronclient.neutron.v2_0.lb.pool:UpdatePool - lb-pool-delete = neutronclient.neutron.v2_0.lb.pool:DeletePool - lb-pool-stats = neutronclient.neutron.v2_0.lb.pool:RetrievePoolStats - lb-member-list = neutronclient.neutron.v2_0.lb.member:ListMember - lb-member-show = neutronclient.neutron.v2_0.lb.member:ShowMember - lb-member-create = neutronclient.neutron.v2_0.lb.member:CreateMember - lb-member-update = neutronclient.neutron.v2_0.lb.member:UpdateMember - lb-member-delete = neutronclient.neutron.v2_0.lb.member:DeleteMember - lb-healthmonitor-list = neutronclient.neutron.v2_0.lb.healthmonitor:ListHealthMonitor - lb-healthmonitor-show = neutronclient.neutron.v2_0.lb.healthmonitor:ShowHealthMonitor - lb-healthmonitor-create = neutronclient.neutron.v2_0.lb.healthmonitor:CreateHealthMonitor - lb-healthmonitor-update = neutronclient.neutron.v2_0.lb.healthmonitor:UpdateHealthMonitor - lb-healthmonitor-delete = neutronclient.neutron.v2_0.lb.healthmonitor:DeleteHealthMonitor - lb-healthmonitor-associate = neutronclient.neutron.v2_0.lb.healthmonitor:AssociateHealthMonitor - lb-healthmonitor-disassociate = neutronclient.neutron.v2_0.lb.healthmonitor:DisassociateHealthMonitor - - ipsec-site-connection-list = neutronclient.neutron.v2_0.vpn.ipsec_site_connection:ListIPsecSiteConnection - ipsec-site-connection-show = neutronclient.neutron.v2_0.vpn.ipsec_site_connection:ShowIPsecSiteConnection - ipsec-site-connection-create = neutronclient.neutron.v2_0.vpn.ipsec_site_connection:CreateIPsecSiteConnection - ipsec-site-connection-update = neutronclient.neutron.v2_0.vpn.ipsec_site_connection:UpdateIPsecSiteConnection - ipsec-site-connection-delete = neutronclient.neutron.v2_0.vpn.ipsec_site_connection:DeleteIPsecSiteConnection - vpn-endpoint-group-list = neutronclient.neutron.v2_0.vpn.endpoint_group:ListEndpointGroup - vpn-endpoint-group-show = neutronclient.neutron.v2_0.vpn.endpoint_group:ShowEndpointGroup - vpn-endpoint-group-create = neutronclient.neutron.v2_0.vpn.endpoint_group:CreateEndpointGroup - vpn-endpoint-group-update = neutronclient.neutron.v2_0.vpn.endpoint_group:UpdateEndpointGroup - vpn-endpoint-group-delete = neutronclient.neutron.v2_0.vpn.endpoint_group:DeleteEndpointGroup - vpn-service-list = neutronclient.neutron.v2_0.vpn.vpnservice:ListVPNService - vpn-service-show = neutronclient.neutron.v2_0.vpn.vpnservice:ShowVPNService - vpn-service-create = neutronclient.neutron.v2_0.vpn.vpnservice:CreateVPNService - vpn-service-update = neutronclient.neutron.v2_0.vpn.vpnservice:UpdateVPNService - vpn-service-delete = neutronclient.neutron.v2_0.vpn.vpnservice:DeleteVPNService - vpn-ipsecpolicy-list = neutronclient.neutron.v2_0.vpn.ipsecpolicy:ListIPsecPolicy - vpn-ipsecpolicy-show = neutronclient.neutron.v2_0.vpn.ipsecpolicy:ShowIPsecPolicy - vpn-ipsecpolicy-create = neutronclient.neutron.v2_0.vpn.ipsecpolicy:CreateIPsecPolicy - vpn-ipsecpolicy-update = neutronclient.neutron.v2_0.vpn.ipsecpolicy:UpdateIPsecPolicy - vpn-ipsecpolicy-delete = neutronclient.neutron.v2_0.vpn.ipsecpolicy:DeleteIPsecPolicy - vpn-ikepolicy-list = neutronclient.neutron.v2_0.vpn.ikepolicy:ListIKEPolicy - vpn-ikepolicy-show = neutronclient.neutron.v2_0.vpn.ikepolicy:ShowIKEPolicy - vpn-ikepolicy-create = neutronclient.neutron.v2_0.vpn.ikepolicy:CreateIKEPolicy - vpn-ikepolicy-update = neutronclient.neutron.v2_0.vpn.ikepolicy:UpdateIKEPolicy - vpn-ikepolicy-delete = neutronclient.neutron.v2_0.vpn.ikepolicy:DeleteIKEPolicy diff --git a/tools/neutron.bash_completion b/tools/neutron.bash_completion deleted file mode 100644 index 15ed41776..000000000 --- a/tools/neutron.bash_completion +++ /dev/null @@ -1,27 +0,0 @@ -_neutron_opts="" # lazy init -_neutron_flags="" # lazy init -_neutron_opts_exp="" # lazy init -_neutron() -{ - local cur prev nbc cflags - COMPREPLY=() - cur="${COMP_WORDS[COMP_CWORD]}" - prev="${COMP_WORDS[COMP_CWORD-1]}" - - if [ "x$_neutron_opts" == "x" ] ; then - nbc="`neutron bash-completion`" - _neutron_opts="`echo "$nbc" | sed -e "s/--[a-z0-9_-]*//g" -e "s/[ ][ ]*/ /g"`" - _neutron_flags="`echo " $nbc" | sed -e "s/ [^-][^-][a-z0-9_-]*//g" -e "s/[ ][ ]*/ /g"`" - _neutron_opts_exp="`echo "$_neutron_opts" | sed -e "s/[ ]/|/g"`" - fi - - if [[ " ${COMP_WORDS[@]} " =~ " "($_neutron_opts_exp)" " && "$prev" != "help" ]] ; then - COMPLETION_CACHE=~/.neutronclient/*/*-cache - cflags="$_neutron_flags "$(cat $COMPLETION_CACHE 2> /dev/null | tr '\n' ' ') - COMPREPLY=($(compgen -W "${cflags}" -- ${cur})) - else - COMPREPLY=($(compgen -W "${_neutron_opts}" -- ${cur})) - fi - return 0 -} -complete -F _neutron neutron diff --git a/tox.ini b/tox.ini index 06f5829f6..afdf530e2 100644 --- a/tox.ini +++ b/tox.ini @@ -33,11 +33,6 @@ distribute = false [testenv:venv] commands = {posargs} -[testenv:functional] -setenv = - OS_TEST_PATH = ./neutronclient/tests/functional - OS_NEUTRONCLIENT_EXEC_DIR = {envdir}/bin - [testenv:cover] setenv = {[testenv]setenv} From 79c7de463051a5ac832dd26057b5e3710f5ea19a Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 17 May 2023 13:03:51 +0100 Subject: [PATCH 804/845] Remove 'bgp speaker show dragents' This has been deprecated for ages and generates an annoying warning when building OSC docs. Time to cull it. Change-Id: I76e4c7ab742d0bf27fecfda8fab41035618a4e24 Signed-off-by: Stephen Finucane --- doc/source/cli/osc/v2/bgp-dynamic-routing.rst | 3 -- .../osc/v2/dynamic_routing/bgp_dragent.py | 49 ------------------- .../v2/dynamic_routing/test_bgp_dragent.py | 41 ---------------- ...peaker-show-dragents-0a0db4b72b2feffc.yaml | 6 +++ setup.cfg | 1 - 5 files changed, 6 insertions(+), 94 deletions(-) create mode 100644 releasenotes/notes/remove-bgp-speaker-show-dragents-0a0db4b72b2feffc.yaml diff --git a/doc/source/cli/osc/v2/bgp-dynamic-routing.rst b/doc/source/cli/osc/v2/bgp-dynamic-routing.rst index 6d712809f..c86bb45ee 100644 --- a/doc/source/cli/osc/v2/bgp-dynamic-routing.rst +++ b/doc/source/cli/osc/v2/bgp-dynamic-routing.rst @@ -25,9 +25,6 @@ Network v2 .. autoprogram-cliff:: openstack.neutronclient.v2 :command: bgp speaker show -.. autoprogram-cliff:: openstack.neutronclient.v2 - :command: bgp speaker show dragents - .. autoprogram-cliff:: openstack.neutronclient.v2 :command: bgp speaker add network diff --git a/neutronclient/osc/v2/dynamic_routing/bgp_dragent.py b/neutronclient/osc/v2/dynamic_routing/bgp_dragent.py index 4af2008fe..91dc2a75b 100644 --- a/neutronclient/osc/v2/dynamic_routing/bgp_dragent.py +++ b/neutronclient/osc/v2/dynamic_routing/bgp_dragent.py @@ -65,55 +65,6 @@ def take_action(self, parsed_args): speaker_id) -class ListDRAgentsHostingBgpSpeaker(command.Lister): - """(Deprecated) List dynamic routing agents hosting a BGP speaker - - (Use "bgp dragent list" instead) - """ - - resource = 'agent' - list_columns = ['id', 'host', 'admin_state_up', 'alive'] - unknown_parts_flag = False - - def get_parser(self, prog_name): - self.log.warning("The 'openstack bgp speaker show dragents' CLI is " - "deprecated and will be removed in the future. Use " - "'openstack bgp dragent list' CLI instead.") - parser = super(ListDRAgentsHostingBgpSpeaker, - self).get_parser(prog_name) - parser.add_argument('bgp_speaker', - metavar='', - help=_("List dynamic routing agents hosting a " - "BGP speaker (name or ID)")) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - speaker_id = client.find_bgp_speaker(parsed_args.bgp_speaker).id - data = client.get_bgp_dragents_hosting_speaker(speaker_id) - columns = ( - 'id', - 'agent_type', - 'host', - 'availability_zone', - 'is_alive', - 'is_admin_state_up', - 'binary' - ) - column_headers = ( - 'ID', - 'Agent Type', - 'Host', - 'Availability Zone', - 'Alive', - 'State', - 'Binary' - ) - return (column_headers, - (utils.get_item_properties( - s, columns,) for s in data)) - - class ListDRAgent(command.Lister): """List dynamic routing agents""" diff --git a/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_dragent.py b/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_dragent.py index 89ba20d93..6842cbbd1 100644 --- a/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_dragent.py +++ b/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_dragent.py @@ -82,44 +82,3 @@ def test_remove_bgp_speaker_from_dragent(self): assert_called_once_with(self._bgp_dragent_id, self._bgp_speaker_id) self.assertIsNone(result) - - -class TestListDRAgentsHostingBgpSpeaker(fakes.TestNeutronDynamicRoutingOSCV2): - _bgp_speaker = fakes.FakeBgpSpeaker.create_one_bgp_speaker() - _bgp_speaker_id = _bgp_speaker['id'] - attrs = {'bgp_speaker_id': _bgp_speaker_id} - _bgp_dragents = fakes.FakeDRAgent.create_dragents(attrs) - columns = ('ID', 'Agent Type', 'Host', 'Availability Zone', - 'Alive', 'State', 'Binary') - data = [(_bgp_dragent['id'], - _bgp_dragent['agent_type'], - _bgp_dragent['host'], - _bgp_dragent['availability_zone'], - _bgp_dragent['admin_state_up'], - _bgp_dragent['alive'], - _bgp_dragent['binary'],) - for _bgp_dragent in _bgp_dragents] - - def setUp(self): - super(TestListDRAgentsHostingBgpSpeaker, self).setUp() - - # Get the command object to test - self.cmd = bgp_dragent.ListDRAgent(self.app, self.namespace) - - def test_list_dragents_hosting_bgp_speaker(self): - arglist = [ - '--bgp-speaker', self._bgp_speaker_id, - ] - verifylist = [ - ('bgp_speaker', self._bgp_speaker_id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - with mock.patch.object(self.networkclient, - "get_bgp_dragents_hosting_speaker", - return_value=self._bgp_dragents): - columns, data = self.cmd.take_action(parsed_args) - self.networkclient.get_bgp_dragents_hosting_speaker.\ - assert_called_once_with(self._bgp_speaker_id) - self.assertEqual(self.columns, columns) - self.assertListEqual(self.data, list(data)) diff --git a/releasenotes/notes/remove-bgp-speaker-show-dragents-0a0db4b72b2feffc.yaml b/releasenotes/notes/remove-bgp-speaker-show-dragents-0a0db4b72b2feffc.yaml new file mode 100644 index 000000000..5d4fdda2c --- /dev/null +++ b/releasenotes/notes/remove-bgp-speaker-show-dragents-0a0db4b72b2feffc.yaml @@ -0,0 +1,6 @@ +--- +upgrade: + - | + The ``openstack bgp speaker show dragents`` CLI is removed. It was + deprecated in the 7.1.0 release (Ussuri). Use ``openstack bgp dragent list + --bgp-speaker `` CLI instead. diff --git a/setup.cfg b/setup.cfg index 3bab0a176..7af675d1d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -76,7 +76,6 @@ openstack.neutronclient.v2 = bgp_speaker_remove_peer = neutronclient.osc.v2.dynamic_routing.bgp_speaker:RemovePeerFromSpeaker bgp_speaker_set = neutronclient.osc.v2.dynamic_routing.bgp_speaker:SetBgpSpeaker bgp_speaker_show = neutronclient.osc.v2.dynamic_routing.bgp_speaker:ShowBgpSpeaker - bgp_speaker_show_dragents = neutronclient.osc.v2.dynamic_routing.bgp_dragent:ListDRAgentsHostingBgpSpeaker firewall_group_create = neutronclient.osc.v2.fwaas.firewallgroup:CreateFirewallGroup firewall_group_delete = neutronclient.osc.v2.fwaas.firewallgroup:DeleteFirewallGroup From d4124aed1ff260eb108b0433cc54625d93ead0d3 Mon Sep 17 00:00:00 2001 From: elajkat Date: Tue, 28 Feb 2023 10:06:23 +0100 Subject: [PATCH 805/845] OSC: Remove BGPVPN calls to neutronclient With [1] the python binding code in Neutronclient prints warning about future deprecation, change networking-bgpvpn osc client code to use totally OpenstackSDK. [1]: https://review.opendev.org/c/openstack/python-neutronclient/+/862371 Change-Id: Iae1378ffabda3d30257e35a6148e205a9f495eb5 Related-Bug: #1999774 Depends-On: https://review.opendev.org/c/openstack/openstacksdk/+/875530 --- .../osc/v2/networking_bgpvpn/bgpvpn.py | 42 ++-- .../v2/networking_bgpvpn/port_association.py | 13 +- .../networking_bgpvpn/resource_association.py | 77 +++---- .../networking_bgpvpn/router_association.py | 5 +- .../unit/osc/v2/networking_bgpvpn/fakes.py | 212 ++++++++++-------- .../osc/v2/networking_bgpvpn/test_bgpvpn.py | 160 +++++++------ .../test_resource_association.py | 131 ++++++----- .../test_router_association.py | 105 ++++----- requirements.txt | 2 +- 9 files changed, 390 insertions(+), 357 deletions(-) diff --git a/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py b/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py index 58c9e8a43..cf4c69d66 100644 --- a/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py +++ b/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py @@ -25,13 +25,12 @@ from neutronclient._i18n import _ from neutronclient.osc import utils as nc_osc_utils -from neutronclient.osc.v2.networking_bgpvpn import constants LOG = logging.getLogger(__name__) _attr_map = ( ('id', 'ID', column_util.LIST_BOTH), - ('tenant_id', 'Project', column_util.LIST_LONG_ONLY), + ('project_id', 'Project', column_util.LIST_LONG_ONLY), ('name', 'Name', column_util.LIST_BOTH), ('type', 'Type', column_util.LIST_BOTH), ('route_targets', 'Route Targets', column_util.LIST_LONG_ONLY), @@ -166,7 +165,7 @@ def _args2body(client_manager, id, action, args): args.purge_export_target and args.purge_route_distinguisher) and (args.route_targets or args.import_targets or args.export_targets or args.route_distinguishers)): - bgpvpn = client_manager.neutronclient.show_bgpvpn(id)['bgpvpn'] + bgpvpn = client_manager.network.get_bgpvpn(id) attrs = {} @@ -221,7 +220,7 @@ def _args2body(client_manager, id, action, args): set(bgpvpn['route_distinguishers']) - set(args.route_distinguishers)) - return {constants.BGPVPN: attrs} + return attrs class CreateBgpvpn(command.ShowOne): @@ -241,7 +240,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network attrs = {} if parsed_args.name is not None: attrs['name'] = str(parsed_args.name) @@ -266,9 +265,8 @@ def take_action(self, parsed_args): parsed_args.project_domain, ).id attrs['tenant_id'] = project_id - body = {constants.BGPVPN: attrs} - obj = client.create_bgpvpn(body)[constants.BGPVPN] - columns, display_columns = column_util.get_columns(obj, _attr_map) + obj = client.create_bgpvpn(**attrs) + display_columns, columns = nc_osc_utils._get_columns(obj) data = osc_utils.get_dict_properties(obj, columns, formatters=_formatters) return display_columns, data @@ -288,10 +286,10 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - id = client.find_resource(constants.BGPVPN, parsed_args.bgpvpn)['id'] + client = self.app.client_manager.network + id = client.find_bgpvpn(parsed_args.bgpvpn)['id'] body = _args2body(self.app.client_manager, id, 'set', parsed_args) - client.update_bgpvpn(id, body) + client.update_bgpvpn(id, **body) class UnsetBgpvpn(command.Command): @@ -308,10 +306,10 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - id = client.find_resource(constants.BGPVPN, parsed_args.bgpvpn)['id'] + client = self.app.client_manager.network + id = client.find_bgpvpn(parsed_args.bgpvpn)['id'] body = _args2body(self.app.client_manager, id, 'unset', parsed_args) - client.update_bgpvpn(id, body) + client.update_bgpvpn(id, **body) class DeleteBgpvpn(command.Command): @@ -328,11 +326,11 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network fails = 0 for id_or_name in parsed_args.bgpvpns: try: - id = client.find_resource(constants.BGPVPN, id_or_name)['id'] + id = client.find_bgpvpn(id_or_name)['id'] client.delete_bgpvpn(id) LOG.warning("BGP VPN %(id)s deleted", {'id': id}) except Exception as e: @@ -368,7 +366,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network params = {} if parsed_args.project is not None: project_id = nc_osc_utils.find_project( @@ -379,7 +377,7 @@ def take_action(self, parsed_args): params['tenant_id'] = project_id if parsed_args.property: params.update(parsed_args.property) - objs = client.list_bgpvpns(**params)[constants.BGPVPNS] + objs = client.bgpvpns(**params) headers, columns = column_util.get_column_definitions( _attr_map, long_listing=parsed_args.long) return (headers, (osc_utils.get_dict_properties( @@ -399,10 +397,10 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - id = client.find_resource(constants.BGPVPN, parsed_args.bgpvpn)['id'] - obj = client.show_bgpvpn(id)[constants.BGPVPN] - columns, display_columns = column_util.get_columns(obj, _attr_map) + client = self.app.client_manager.network + id = client.find_bgpvpn(parsed_args.bgpvpn)['id'] + obj = client.get_bgpvpn(id) + display_columns, columns = nc_osc_utils._get_columns(obj) data = osc_utils.get_dict_properties(obj, columns, formatters=_formatters) return display_columns, data diff --git a/neutronclient/osc/v2/networking_bgpvpn/port_association.py b/neutronclient/osc/v2/networking_bgpvpn/port_association.py index abf82ab72..24cd2d207 100644 --- a/neutronclient/osc/v2/networking_bgpvpn/port_association.py +++ b/neutronclient/osc/v2/networking_bgpvpn/port_association.py @@ -108,6 +108,7 @@ def _transform_resource(self, data): LOG.warning("Unknown route type %s (%s).", route['type'], route) data.pop('routes', None) + return data def _get_common_parser(self, parser): """Adds to parser arguments common to create, set and unset commands. @@ -201,15 +202,13 @@ def _get_common_parser(self, parser): ) def _args2body(self, bgpvpn_id, args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network attrs = {} if self._action != 'create': - assoc = client.find_resource_by_id( - self._resource, + assoc = client.find_bgpvpn_port_association( args.resource_association_id, - cmd_resource='bgpvpn_%s_assoc' % self._assoc_res_name, - parent_id=bgpvpn_id) + bgpvpn_id=bgpvpn_id) else: assoc = {'routes': []} @@ -248,7 +247,7 @@ def _args2body(self, bgpvpn_id, args): else: routes = args.bgpvpn_routes args_bgpvpn_routes = { - client.find_resource(constants.BGPVPN, r['bgpvpn'])['id']: + client.find_bgpvpn(r['bgpvpn']).id: r.get('local_pref') for r in routes } @@ -281,7 +280,7 @@ def _args2body(self, bgpvpn_id, args): route['local_pref'] = int(local_pref) attrs.setdefault('routes', []).append(route) - return {self._resource: attrs} + return attrs class CreateBgpvpnPortAssoc(BgpvpnPortAssoc, diff --git a/neutronclient/osc/v2/networking_bgpvpn/resource_association.py b/neutronclient/osc/v2/networking_bgpvpn/resource_association.py index 96c559b24..f5a0804dc 100644 --- a/neutronclient/osc/v2/networking_bgpvpn/resource_association.py +++ b/neutronclient/osc/v2/networking_bgpvpn/resource_association.py @@ -24,7 +24,6 @@ from neutronclient._i18n import _ from neutronclient.osc import utils as nc_osc_utils -from neutronclient.osc.v2.networking_bgpvpn import constants LOG = logging.getLogger(__name__) @@ -56,35 +55,32 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network create_method = getattr( - client, 'create_bgpvpn_%s_assoc' % self._assoc_res_name) - bgpvpn = client.find_resource(constants.BGPVPN, parsed_args.bgpvpn) - assoc_res = client.find_resource(self._assoc_res_name, - parsed_args.resource) - body = { - self._resource: { - '%s_id' % self._assoc_res_name: assoc_res['id'], - }, - } + client, 'create_bgpvpn_%s_association' % self._assoc_res_name) + bgpvpn = client.find_bgpvpn(parsed_args.bgpvpn) + find_res_method = getattr( + client, 'find_%s' % self._assoc_res_name) + assoc_res = find_res_method(parsed_args.resource) + body = {'%s_id' % self._assoc_res_name: assoc_res['id']} if 'project' in parsed_args and parsed_args.project is not None: project_id = nc_osc_utils.find_project( self.app.client_manager.identity, parsed_args.project, parsed_args.project_domain, ).id - body[self._resource]['tenant_id'] = project_id + body['tenant_id'] = project_id arg2body = getattr(self, '_args2body', None) if callable(arg2body): - body[self._resource].update( - arg2body(bgpvpn['id'], parsed_args)[self._resource]) + body.update( + arg2body(bgpvpn['id'], parsed_args)) - obj = create_method(bgpvpn['id'], body)[self._resource] + obj = create_method(bgpvpn['id'], **body) transform = getattr(self, '_transform_resource', None) if callable(transform): transform(obj) - columns, display_columns = column_util.get_columns(obj, self._attr_map) + display_columns, columns = nc_osc_utils._get_columns(obj) data = osc_utils.get_dict_properties(obj, columns, formatters=self._formatters) return display_columns, data @@ -116,15 +112,15 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network update_method = getattr( - client, 'update_bgpvpn_%s_assoc' % self._assoc_res_name) - bgpvpn = client.find_resource(constants.BGPVPN, parsed_args.bgpvpn) + client, 'update_bgpvpn_%s_association' % self._assoc_res_name) + bgpvpn = client.find_bgpvpn(parsed_args.bgpvpn) arg2body = getattr(self, '_args2body', None) if callable(arg2body): body = arg2body(bgpvpn['id'], parsed_args) update_method(bgpvpn['id'], parsed_args.resource_association_id, - body) + **body) class UnsetBgpvpnResAssoc(SetBgpvpnResAssoc): @@ -153,10 +149,10 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network delete_method = getattr( - client, 'delete_bgpvpn_%s_assoc' % self._assoc_res_name) - bgpvpn = client.find_resource(constants.BGPVPN, parsed_args.bgpvpn) + client, 'delete_bgpvpn_%s_association' % self._assoc_res_name) + bgpvpn = client.find_bgpvpn(parsed_args.bgpvpn) fails = 0 for id in parsed_args.resource_association_ids: try: @@ -206,22 +202,27 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network list_method = getattr(client, - 'list_bgpvpn_%s_assocs' % self._assoc_res_name) - bgpvpn = client.find_resource(constants.BGPVPN, parsed_args.bgpvpn) + 'bgpvpn_%s_associations' % self._assoc_res_name) + bgpvpn = client.find_bgpvpn(parsed_args.bgpvpn) params = {} if parsed_args.property: params.update(parsed_args.property) objs = list_method(bgpvpn['id'], - retrieve_all=True, **params)[self._resource_plural] + retrieve_all=True, **params) transform = getattr(self, '_transform_resource', None) + transformed_objs = [] if callable(transform): - [transform(obj) for obj in objs] + for obj in objs: + transformed_objs.append(transform(obj)) + else: + transformed_objs = list(objs) headers, columns = column_util.get_column_definitions( self._attr_map, long_listing=parsed_args.long) return (headers, (osc_utils.get_dict_properties( - s, columns, formatters=self._formatters) for s in objs)) + s, columns, formatters=self._formatters) + for s in transformed_objs)) class ShowBgpvpnResAssoc(command.ShowOne): @@ -243,20 +244,16 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - show_method = getattr(client, - 'show_bgpvpn_%s_assoc' % self._assoc_res_name) - bgpvpn = client.find_resource(constants.BGPVPN, parsed_args.bgpvpn) - assoc = client.find_resource_by_id( - self._resource, - parsed_args.resource_association_id, - cmd_resource='bgpvpn_%s_assoc' % self._assoc_res_name, - parent_id=bgpvpn['id']) - obj = show_method(bgpvpn['id'], assoc['id'])[self._resource] + client = self.app.client_manager.network + show_method = getattr( + client, 'get_bgpvpn_%s_association' % self._assoc_res_name) + bgpvpn = client.find_bgpvpn(parsed_args.bgpvpn) + obj = show_method(bgpvpn['id'], + parsed_args.resource_association_id) transform = getattr(self, '_transform_resource', None) if callable(transform): transform(obj) - columns, display_columns = column_util.get_columns(obj, self._attr_map) + display_columns, columns = nc_osc_utils._get_columns(obj) data = osc_utils.get_dict_properties(obj, columns, formatters=self._formatters) return display_columns, data diff --git a/neutronclient/osc/v2/networking_bgpvpn/router_association.py b/neutronclient/osc/v2/networking_bgpvpn/router_association.py index c382f1621..f4cdde09c 100644 --- a/neutronclient/osc/v2/networking_bgpvpn/router_association.py +++ b/neutronclient/osc/v2/networking_bgpvpn/router_association.py @@ -75,14 +75,13 @@ def _get_common_parser(self, parser): ) def _args2body(self, _, args): - attrs = {} - + attrs = {'advertise_extra_routes': False} if args.advertise_extra_routes: attrs['advertise_extra_routes'] = self._action != 'unset' elif args.no_advertise_extra_routes: attrs['advertise_extra_routes'] = self._action == 'unset' - return {self._resource: attrs} + return attrs class CreateBgpvpnRouterAssoc(BgpvpnRouterAssoc, CreateBgpvpnResAssoc): diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py index a0b2cb892..0a2c3fe9c 100644 --- a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py +++ b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py @@ -17,10 +17,11 @@ import copy from unittest import mock +from openstack.network.v2 import bgpvpn as _bgpvpn +from openstack import resource as sdk_resource from osc_lib.utils import columns as column_util from neutronclient.osc import utils as nc_osc_utils -from neutronclient.osc.v2.networking_bgpvpn import constants from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ CreateBgpvpnResAssoc from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ @@ -61,50 +62,45 @@ def setUp(self): side_effect=lambda _, name_or_id, __: mock.Mock(id=name_or_id)) -class FakeBgpvpn(object): - """Fake BGP VPN with attributes.""" - - @staticmethod - def create_one_bgpvpn(attrs=None): - """Create a fake BGP VPN.""" - - attrs = attrs or {} - - # Set default attributes. - bgpvpn_attrs = { - 'id': 'fake_bgpvpn_id', - 'tenant_id': _FAKE_PROJECT_ID, - 'name': '', - 'type': 'l3', - 'route_targets': [], - 'import_targets': [], - 'export_targets': [], - 'route_distinguishers': [], - 'networks': [], - 'routers': [], - 'ports': [], - 'vni': 100, - 'local_pref': 777, - } +def create_one_bgpvpn(attrs=None): + """Create a fake BGP VPN.""" + + attrs = attrs or {} - # Overwrite default attributes. - bgpvpn_attrs.update(attrs) + # Set default attributes. + bgpvpn_attrs = { + 'id': 'fake_bgpvpn_id', + 'tenant_id': _FAKE_PROJECT_ID, + 'name': '', + 'type': 'l3', + 'route_targets': [], + 'import_targets': [], + 'export_targets': [], + 'route_distinguishers': [], + 'networks': [], + 'routers': [], + 'ports': [], + 'vni': 100, + 'local_pref': 777, + } - return copy.deepcopy(bgpvpn_attrs) + # Overwrite default attributes. + bgpvpn_attrs.update(attrs) + return _bgpvpn.BgpVpn(**bgpvpn_attrs) - @staticmethod - def create_bgpvpns(attrs=None, count=1): - """Create multiple fake BGP VPN.""" - bgpvpns = [] - for i in range(0, count): - if attrs is None: - attrs = {'id': 'fake_id%d' % i} - elif getattr(attrs, 'id', None) is None: - attrs['id'] = 'fake_id%d' % i - bgpvpns.append(FakeBgpvpn.create_one_bgpvpn(attrs)) +def create_bgpvpns(attrs=None, count=1): + """Create multiple fake BGP VPN.""" - return {constants.BGPVPNS: bgpvpns} + bgpvpns = [] + for i in range(0, count): + if attrs is None: + attrs = {'id': 'fake_id%d' % i} + elif getattr(attrs, 'id', None) is None: + attrs['id'] = 'fake_id%d' % i + bgpvpns.append(create_one_bgpvpn(attrs)) + + return bgpvpns class BgpvpnFakeAssoc(object): @@ -114,9 +110,10 @@ class BgpvpnFakeAssoc(object): _attr_map = ( ('id', 'ID', column_util.LIST_BOTH), - ('tenant_id', 'Project', column_util.LIST_LONG_ONLY), ('%s_id' % _assoc_res_name, '%s ID' % _assoc_res_name.capitalize(), column_util.LIST_BOTH), + ('name', 'Name', column_util.LIST_BOTH), + ('project_id', 'Project ID', column_util.LIST_BOTH), ) _formatters = {} @@ -152,11 +149,12 @@ class BgpvpnFakeRouterAssoc(object): _attr_map = ( ('id', 'ID', column_util.LIST_BOTH), - ('tenant_id', 'Project', column_util.LIST_LONG_ONLY), ('%s_id' % _assoc_res_name, '%s ID' % _assoc_res_name.capitalize(), column_util.LIST_BOTH), ('advertise_extra_routes', 'Advertise extra routes', column_util.LIST_LONG_ONLY), + ('name', 'Name', column_util.LIST_BOTH), + ('project_id', 'Project ID', column_util.LIST_BOTH), ) _formatters = {} @@ -174,71 +172,99 @@ class ShowBgpvpnFakeRouterAssoc(BgpvpnFakeRouterAssoc, ShowBgpvpnRouterAssoc): pass -class FakeResource(object): - """Fake resource with minimal attributes.""" +class FakeResource(sdk_resource.Resource): + resource_key = 'fakeresource' + resources_key = 'fakeresources' + base_path = '/bgpvpn/fakeresources' - @staticmethod - def create_one_resource(attrs=None): - """Create a fake resource.""" + _allow_unknown_attrs_in_body = True - attrs = attrs or {} + # capabilities + allow_create = True + allow_fetch = True + allow_commit = True + allow_delete = True + allow_list = True - # Set default attributes. - res_attrs = { - 'id': 'fake_resource_id', - 'tenant_id': _FAKE_PROJECT_ID, - } + id = sdk_resource.Body('id') + tenant_id = sdk_resource.Body('tenant_id', deprecated=True) + project_id = sdk_resource.Body('project_id', alias='tenant_id') + + +class FakeResoureAssociation(sdk_resource.Resource): + resource_key = 'fakeresourceassociation' + resources_key = 'fakeresourceassociations' + base_path = '/bgpvpn/fakeresourceassociations' + + _allow_unknown_attrs_in_body = True + + # capabilities + allow_create = True + allow_fetch = True + allow_commit = True + allow_delete = True + allow_list = True + + id = sdk_resource.Body('id') + tenant_id = sdk_resource.Body('tenant_id', deprecated=True) + project_id = sdk_resource.Body('project_id', alias='tenant_id') + + +def create_one_resource(attrs=None): + """Create a fake resource.""" + attrs = attrs or {} + + # Set default attributes. + res_attrs = { + 'id': 'fake_resource_id', + 'tenant_id': _FAKE_PROJECT_ID, + } + + # Overwrite default attributes. + res_attrs.update(attrs) + return FakeResource(**res_attrs) + + +def create_resources(attrs=None, count=1): + """Create multiple fake resources.""" + + resources = [] + for i in range(0, count): + if attrs is None: + attrs = {'id': 'fake_id%d' % i} + elif getattr(attrs, 'id', None) is None: + attrs['id'] = 'fake_id%d' % i + resources.append(create_one_resource(attrs)) - # Overwrite default attributes. - res_attrs.update(attrs) - return copy.deepcopy(res_attrs) + return resources - @staticmethod - def create_resources(attrs=None, count=1): - """Create multiple fake resources.""" - resources = [] - for i in range(0, count): - if attrs is None: - attrs = {'id': 'fake_id%d' % i} - elif getattr(attrs, 'id', None) is None: - attrs['id'] = 'fake_id%d' % i - resources.append(FakeResource.create_one_resource(attrs)) +def create_one_resource_association(resource, attrs=None): + """Create a fake resource association.""" - return {'%ss' % BgpvpnFakeAssoc._assoc_res_name: resources} + attrs = attrs or {} + res_assoc_attrs = { + 'id': 'fake_association_id', + 'tenant_id': resource['tenant_id'], + 'fake_resource_id': resource['id'], + } -class FakeResAssoc(object): - """Fake resource association with minimal attributes.""" + # Overwrite default attributes. + res_assoc_attrs.update(attrs) + return FakeResoureAssociation(**res_assoc_attrs) - @staticmethod - def create_one_resource_association(resource, attrs=None): - """Create a fake resource association.""" - attrs = attrs or {} +def create_resource_associations(resources): + """Create multiple fake resource associations.""" + res_assocs = [] + for idx, resource in enumerate(resources): res_assoc_attrs = { - 'id': 'fake_association_id', + 'id': 'fake_association_id%d' % idx, 'tenant_id': resource['tenant_id'], 'fake_resource_id': resource['id'], } + res_assocs.append(copy.deepcopy(res_assoc_attrs)) - # Overwrite default attributes. - res_assoc_attrs.update(attrs) - return copy.deepcopy(res_assoc_attrs) - - @staticmethod - def create_resource_associations(resources): - """Create multiple fake resource associations.""" - - res_assocs = [] - for idx, resource in enumerate( - resources['%ss' % BgpvpnFakeAssoc._assoc_res_name]): - res_assoc_attrs = { - 'id': 'fake_association_id%d' % idx, - 'tenant_id': resource['tenant_id'], - 'fake_resource_id': resource['id'], - } - res_assocs.append(copy.deepcopy(res_assoc_attrs)) - - return {BgpvpnFakeAssoc._resource_plural: res_assocs} + return res_assocs diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py index 16fb164ae..3f1632027 100644 --- a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py +++ b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py @@ -23,7 +23,6 @@ from osc_lib.utils import columns as column_util from neutronclient.osc.v2.networking_bgpvpn import bgpvpn -from neutronclient.osc.v2.networking_bgpvpn import constants from neutronclient.tests.unit.osc.v2.networking_bgpvpn import fakes @@ -55,9 +54,9 @@ def setUp(self): self.cmd = bgpvpn.CreateBgpvpn(self.app, self.namespace) def test_create_bgpvpn_with_no_args(self): - fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() - self.neutronclient.create_bgpvpn = mock.Mock( - return_value={constants.BGPVPN: fake_bgpvpn}) + fake_bgpvpn = fakes.create_one_bgpvpn() + self.networkclient.create_bgpvpn = mock.Mock( + return_value=fake_bgpvpn) arglist = [] verifylist = [ ('project', None), @@ -75,10 +74,10 @@ def test_create_bgpvpn_with_no_args(self): cols, data = self.cmd.take_action(parsed_args) - self.neutronclient.create_bgpvpn.assert_called_once_with( - {constants.BGPVPN: {'type': 'l3'}}) - self.assertEqual(sorted_headers, cols) - self.assertItemEqual(_get_data(fake_bgpvpn), data) + self.networkclient.create_bgpvpn.assert_called_once_with( + **{'type': 'l3'}) + + self.assertEqual(sorted(sorted_columns), sorted(cols)) def test_create_bgpvpn_with_all_args(self): attrs = { @@ -92,9 +91,9 @@ def test_create_bgpvpn_with_all_args(self): 'export_targets': ['fake_ert1', 'fake_ert2', 'fake_ert3'], 'route_distinguishers': ['fake_rd1', 'fake_rd2', 'fake_rd3'], } - fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn(attrs) - self.neutronclient.create_bgpvpn = mock.Mock( - return_value={constants.BGPVPN: fake_bgpvpn}) + fake_bgpvpn = fakes.create_one_bgpvpn(attrs) + self.networkclient.create_bgpvpn = mock.Mock( + return_value=fake_bgpvpn) arglist = [ '--project', fake_bgpvpn['tenant_id'], '--name', fake_bgpvpn['name'], @@ -126,21 +125,18 @@ def test_create_bgpvpn_with_all_args(self): cols, data = self.cmd.take_action(parsed_args) - fake_bgpvpn_call = copy.deepcopy(fake_bgpvpn) - fake_bgpvpn_call.pop('id') - fake_bgpvpn_call.pop('networks') - fake_bgpvpn_call.pop('routers') - fake_bgpvpn_call.pop('ports') + fake_bgpvpn_call = copy.deepcopy(attrs) - self.neutronclient.create_bgpvpn.assert_called_once_with( - {constants.BGPVPN: fake_bgpvpn_call}) - self.assertEqual(sorted_headers, cols) - self.assertItemEqual(_get_data(fake_bgpvpn), data) + self.networkclient.create_bgpvpn.assert_called_once_with( + **fake_bgpvpn_call) + self.assertEqual(sorted(sorted_columns), sorted(cols)) class TestSetBgpvpn(fakes.TestNeutronClientBgpvpn): def setUp(self): super(TestSetBgpvpn, self).setUp() + self.networkclient.find_bgpvpn = mock.Mock( + side_effect=lambda name_or_id: {'id': name_or_id}) self.cmd = bgpvpn.SetBgpvpn(self.app, self.namespace) def test_set_bgpvpn(self): @@ -150,10 +146,10 @@ def test_set_bgpvpn(self): 'export_targets': ['set_ert1', 'set_ert2', 'set_ert3'], 'route_distinguishers': ['set_rd1', 'set_rd2', 'set_rd3'], } - fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn(attrs) - self.neutronclient.show_bgpvpn = mock.Mock( - return_value={constants.BGPVPN: fake_bgpvpn}) - self.neutronclient.update_bgpvpn = mock.Mock() + fake_bgpvpn = fakes.create_one_bgpvpn(attrs) + self.networkclient.get_bgpvpn = mock.Mock( + return_value=fake_bgpvpn) + self.networkclient.update_bgpvpn = mock.Mock() arglist = [ fake_bgpvpn['id'], '--name', 'set_name', @@ -190,14 +186,14 @@ def test_set_bgpvpn(self): 'route_distinguishers': list( set(fake_bgpvpn['route_distinguishers']) | set(['set_rd1'])), } - self.neutronclient.update_bgpvpn.assert_called_once_with( - fake_bgpvpn['id'], {constants.BGPVPN: attrs}) + self.networkclient.update_bgpvpn.assert_called_once_with( + fake_bgpvpn['id'], **attrs) self.assertIsNone(result) def test_set_bgpvpn_with_purge_list(self): - fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() - self.neutronclient.show_bgpvpn = mock.Mock( - return_value={constants.BGPVPN: fake_bgpvpn}) + fake_bgpvpn = fakes.create_one_bgpvpn() + self.networkclient.get_bgpvpn = mock.Mock( + return_value=fake_bgpvpn) self.neutronclient.update_bgpvpn = mock.Mock() arglist = [ fake_bgpvpn['id'], @@ -232,14 +228,16 @@ def test_set_bgpvpn_with_purge_list(self): 'export_targets': [], 'route_distinguishers': [], } - self.neutronclient.update_bgpvpn.assert_called_once_with( - fake_bgpvpn['id'], {constants.BGPVPN: attrs}) + self.networkclient.update_bgpvpn.assert_called_once_with( + fake_bgpvpn['id'], **attrs) self.assertIsNone(result) class TestUnsetBgpvpn(fakes.TestNeutronClientBgpvpn): def setUp(self): super(TestUnsetBgpvpn, self).setUp() + self.networkclient.find_bgpvpn = mock.Mock( + side_effect=lambda name_or_id: {'id': name_or_id}) self.cmd = bgpvpn.UnsetBgpvpn(self.app, self.namespace) def test_unset_bgpvpn(self): @@ -249,10 +247,10 @@ def test_unset_bgpvpn(self): 'export_targets': ['unset_ert1', 'unset_ert2', 'unset_ert3'], 'route_distinguishers': ['unset_rd1', 'unset_rd2', 'unset_rd3'], } - fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn(attrs) - self.neutronclient.show_bgpvpn = mock.Mock( - return_value={constants.BGPVPN: fake_bgpvpn}) - self.neutronclient.update_bgpvpn = mock.Mock() + fake_bgpvpn = fakes.create_one_bgpvpn(attrs) + self.networkclient.get_bgpvpn = mock.Mock( + return_value=fake_bgpvpn) + self.networkclient.update_bgpvpn = mock.Mock() arglist = [ fake_bgpvpn['id'], '--route-target', 'unset_rt1', @@ -286,14 +284,14 @@ def test_unset_bgpvpn(self): 'route_distinguishers': list( set(fake_bgpvpn['route_distinguishers']) - set(['unset_rd1'])), } - self.neutronclient.update_bgpvpn.assert_called_once_with( - fake_bgpvpn['id'], {constants.BGPVPN: attrs}) + self.networkclient.update_bgpvpn.assert_called_once_with( + fake_bgpvpn['id'], **attrs) self.assertIsNone(result) def test_unset_bgpvpn_with_purge_list(self): - fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() - self.neutronclient.show_bgpvpn = mock.Mock( - return_value={constants.BGPVPN: fake_bgpvpn}) + fake_bgpvpn = fakes.create_one_bgpvpn() + self.networkclient.show_bgpvpn = mock.Mock( + return_value=fake_bgpvpn) self.neutronclient.update_bgpvpn = mock.Mock() arglist = [ fake_bgpvpn['id'], @@ -328,21 +326,21 @@ def test_unset_bgpvpn_with_purge_list(self): 'export_targets': [], 'route_distinguishers': [], } - self.neutronclient.update_bgpvpn.assert_called_once_with( - fake_bgpvpn['id'], {constants.BGPVPN: attrs}) + self.networkclient.update_bgpvpn.assert_called_once_with( + fake_bgpvpn['id'], **attrs) self.assertIsNone(result) class TestDeleteBgpvpn(fakes.TestNeutronClientBgpvpn): def setUp(self): super(TestDeleteBgpvpn, self).setUp() - self.neutronclient.find_resource = mock.Mock( - side_effect=lambda _, name_or_id: {'id': name_or_id}) + self.networkclient.find_bgpvpn = mock.Mock( + side_effect=lambda name_or_id: {'id': name_or_id}) self.cmd = bgpvpn.DeleteBgpvpn(self.app, self.namespace) def test_delete_one_bgpvpn(self): - fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() - self.neutronclient.delete_bgpvpn = mock.Mock() + fake_bgpvpn = fakes.create_one_bgpvpn() + self.networkclient.delete_bgpvpn = mock.Mock() arglist = [ fake_bgpvpn['id'], ] @@ -354,15 +352,14 @@ def test_delete_one_bgpvpn(self): result = self.cmd.take_action(parsed_args) - self.neutronclient.delete_bgpvpn.assert_called_once_with( + self.networkclient.delete_bgpvpn.assert_called_once_with( fake_bgpvpn['id']) self.assertIsNone(result) def test_delete_multi_bpgvpn(self): - fake_bgpvpns = fakes.FakeBgpvpn.create_bgpvpns(count=3) - fake_bgpvpn_ids = [fake_bgpvpn['id'] for fake_bgpvpn in - fake_bgpvpns[constants.BGPVPNS]] - self.neutronclient.delete_bgpvpn = mock.Mock() + fake_bgpvpns = fakes.create_bgpvpns(count=3) + fake_bgpvpn_ids = [fake_bgpvpn['id'] for fake_bgpvpn in fake_bgpvpns] + self.networkclient.delete_bgpvpn = mock.Mock() arglist = fake_bgpvpn_ids verifylist = [ ('bgpvpns', fake_bgpvpn_ids), @@ -372,20 +369,19 @@ def test_delete_multi_bpgvpn(self): result = self.cmd.take_action(parsed_args) - self.neutronclient.delete_bgpvpn.assert_has_calls( + self.networkclient.delete_bgpvpn.assert_has_calls( [mock.call(id) for id in fake_bgpvpn_ids]) self.assertIsNone(result) def test_delete_multi_bpgvpn_with_unknown(self): count = 3 - fake_bgpvpns = fakes.FakeBgpvpn.create_bgpvpns(count=count) - fake_bgpvpn_ids = [fake_bgpvpn['id'] for fake_bgpvpn in - fake_bgpvpns[constants.BGPVPNS]] + fake_bgpvpns = fakes.create_bgpvpns(count=count) + fake_bgpvpn_ids = [fake_bgpvpn['id'] for fake_bgpvpn in fake_bgpvpns] def raise_unknonw_resource(resource_path, name_or_id): if str(count - 2) in name_or_id: raise Exception() - self.neutronclient.delete_bgpvpn = mock.Mock( + self.networkclient.delete_bgpvpn = mock.Mock( side_effect=raise_unknonw_resource) arglist = fake_bgpvpn_ids verifylist = [ @@ -397,7 +393,7 @@ def raise_unknonw_resource(resource_path, name_or_id): self.assertRaises(exceptions.CommandError, self.cmd.take_action, parsed_args) - self.neutronclient.delete_bgpvpn.assert_has_calls( + self.networkclient.delete_bgpvpn.assert_has_calls( [mock.call(id) for id in fake_bgpvpn_ids]) @@ -408,8 +404,8 @@ def setUp(self): def test_list_all_bgpvpn(self): count = 3 - fake_bgpvpns = fakes.FakeBgpvpn.create_bgpvpns(count=count) - self.neutronclient.list_bgpvpns = mock.Mock(return_value=fake_bgpvpns) + fake_bgpvpns = fakes.create_bgpvpns(count=count) + self.networkclient.bgpvpns = mock.Mock(return_value=fake_bgpvpns) arglist = [] verifylist = [] @@ -417,17 +413,17 @@ def test_list_all_bgpvpn(self): headers, data = self.cmd.take_action(parsed_args) - self.neutronclient.list_bgpvpns.assert_called_once() + self.networkclient.bgpvpns.assert_called_once() self.assertEqual(headers, list(headers_short)) self.assertListItemEqual( list(data), [_get_data(fake_bgpvpn, columns_short) for fake_bgpvpn - in fake_bgpvpns[constants.BGPVPNS]]) + in fake_bgpvpns]) def test_list_all_bgpvpn_long_mode(self): count = 3 - fake_bgpvpns = fakes.FakeBgpvpn.create_bgpvpns(count=count) - self.neutronclient.list_bgpvpns = mock.Mock(return_value=fake_bgpvpns) + fake_bgpvpns = fakes.create_bgpvpns(count=count) + self.networkclient.bgpvpns = mock.Mock(return_value=fake_bgpvpns) arglist = [ '--long', ] @@ -439,20 +435,20 @@ def test_list_all_bgpvpn_long_mode(self): headers, data = self.cmd.take_action(parsed_args) - self.neutronclient.list_bgpvpns.assert_called_once() + self.networkclient.bgpvpns.assert_called_once() self.assertEqual(headers, list(headers_long)) self.assertListItemEqual( list(data), [_get_data(fake_bgpvpn, columns_long) for fake_bgpvpn - in fake_bgpvpns[constants.BGPVPNS]]) + in fake_bgpvpns]) def test_list_project_bgpvpn(self): count = 3 project_id = 'list_fake_project_id' attrs = {'tenant_id': project_id} - fake_bgpvpns = fakes.FakeBgpvpn.create_bgpvpns(count=count, - attrs=attrs) - self.neutronclient.list_bgpvpns = mock.Mock(return_value=fake_bgpvpns) + fake_bgpvpns = fakes.create_bgpvpns(count=count, + attrs=attrs) + self.networkclient.bgpvpns = mock.Mock(return_value=fake_bgpvpns) arglist = [ '--project', project_id, ] @@ -464,24 +460,23 @@ def test_list_project_bgpvpn(self): headers, data = self.cmd.take_action(parsed_args) - self.neutronclient.list_bgpvpns.assert_called_once_with( + self.networkclient.bgpvpns.assert_called_once_with( tenant_id=project_id) self.assertEqual(headers, list(headers_short)) self.assertListItemEqual( list(data), [_get_data(fake_bgpvpn, columns_short) for fake_bgpvpn - in fake_bgpvpns[constants.BGPVPNS]]) + in fake_bgpvpns]) def test_list_bgpvpn_with_filters(self): count = 3 name = 'fake_id0' layer_type = 'l2' attrs = {'type': layer_type} - fake_bgpvpns = fakes.FakeBgpvpn.create_bgpvpns(count=count, - attrs=attrs) - returned_bgpvpn = fake_bgpvpns[constants.BGPVPNS][0] - self.neutronclient.list_bgpvpns = mock.Mock( - return_value={constants.BGPVPNS: [returned_bgpvpn]}) + fake_bgpvpns = fakes.create_bgpvpns(count=count, + attrs=attrs) + returned_bgpvpn = fake_bgpvpns[0] + self.networkclient.bgpvpns = mock.Mock(return_value=[returned_bgpvpn]) arglist = [ '--property', 'name=%s' % name, '--property', 'type=%s' % layer_type, @@ -494,7 +489,7 @@ def test_list_bgpvpn_with_filters(self): headers, data = self.cmd.take_action(parsed_args) - self.neutronclient.list_bgpvpns.assert_called_once_with( + self.networkclient.bgpvpns.assert_called_once_with( name=name, type=layer_type) self.assertEqual(headers, list(headers_short)) @@ -506,11 +501,13 @@ class TestShowBgpvpn(fakes.TestNeutronClientBgpvpn): def setUp(self): super(TestShowBgpvpn, self).setUp() self.cmd = bgpvpn.ShowBgpvpn(self.app, self.namespace) + self.networkclient.find_bgpvpn = mock.Mock( + side_effect=lambda name_or_id: {'id': name_or_id}) def test_show_bgpvpn(self): - fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() - self.neutronclient.show_bgpvpn = mock.Mock( - return_value={constants.BGPVPN: fake_bgpvpn}) + fake_bgpvpn = fakes.create_one_bgpvpn() + self.networkclient.get_bgpvpn = mock.Mock( + return_value=fake_bgpvpn) arglist = [ fake_bgpvpn['id'], ] @@ -522,7 +519,6 @@ def test_show_bgpvpn(self): headers, data = self.cmd.take_action(parsed_args) - self.neutronclient.show_bgpvpn.assert_called_once_with( + self.networkclient.get_bgpvpn.assert_called_once_with( fake_bgpvpn['id']) - self.assertEqual(sorted_headers, headers) - self.assertItemEqual(_get_data(fake_bgpvpn), data) + self.assertEqual(sorted(sorted_columns), sorted(headers)) diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py index f8aa95f47..1bdca4db5 100644 --- a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py +++ b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py @@ -14,7 +14,6 @@ # under the License. # -import copy import operator from unittest import mock @@ -55,15 +54,21 @@ def _get_data(attrs, columns=sorted_columns): class TestCreateResAssoc(fakes.TestNeutronClientBgpvpn): def setUp(self): super(TestCreateResAssoc, self).setUp() + self.networkclient.find_bgpvpn = mock.Mock( + side_effect=lambda name_or_id: {'id': name_or_id}) + self.networkclient.find_fake_resource = mock.Mock( + side_effect=lambda name_or_id: {'id': name_or_id}) self.cmd = fakes.CreateBgpvpnFakeResAssoc(self.app, self.namespace) def test_create_resource_association(self): - fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() - fake_res = fakes.FakeResource.create_one_resource() - fake_res_assoc = fakes.FakeResAssoc.create_one_resource_association( + fake_bgpvpn = fakes.create_one_bgpvpn() + fake_res = fakes.create_one_resource() + fake_res_assoc = fakes.create_one_resource_association( fake_res) - self.neutronclient.create_bgpvpn_fake_resource_assoc = mock.Mock( - return_value={fakes.BgpvpnFakeAssoc._resource: fake_res_assoc}) + self.networkclient.create_bgpvpn_fake_resource_association = mock.Mock( + return_value=fake_res_assoc) + self.networkclient.find_bgpvpn_fake_resource_association = mock.Mock( + side_effect=lambda name_or_id: {'id': name_or_id}) arglist = [ fake_bgpvpn['id'], fake_res['id'], @@ -79,14 +84,16 @@ def test_create_resource_association(self): cols, data = self.cmd.take_action(parsed_args) - fake_res_assoc_call = copy.deepcopy(fake_res_assoc) - fake_res_assoc_call.pop('id') + fake_res_assoc_call = { + 'fake_resource_id': 'fake_resource_id', + 'tenant_id': 'fake_project_id' + } - self.neutronclient.create_bgpvpn_fake_resource_assoc.\ + self.networkclient.create_bgpvpn_fake_resource_association.\ assert_called_once_with( fake_bgpvpn['id'], - {fakes.BgpvpnFakeAssoc._resource: fake_res_assoc_call}) - self.assertEqual(sorted_headers, cols) + **fake_res_assoc_call) + self.assertEqual(sorted_columns, cols) self.assertEqual(_get_data(fake_res_assoc), data) @@ -96,11 +103,11 @@ def setUp(self): self.cmd = fakes.SetBgpvpnFakeResAssoc(self.app, self.namespace) def test_set_resource_association(self): - fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() - fake_res = fakes.FakeResource.create_one_resource() - fake_res_assoc = fakes.FakeResAssoc.create_one_resource_association( + fake_bgpvpn = fakes.create_one_bgpvpn() + fake_res = fakes.create_one_resource() + fake_res_assoc = fakes.create_one_resource_association( fake_res) - self.neutronclient.update_bgpvpn_fake_resource_assoc = mock.Mock( + self.networkclient.update_bgpvpn_fake_resource_assoc = mock.Mock( return_value={fakes.BgpvpnFakeAssoc._resource: fake_res_assoc}) arglist = [ fake_res_assoc['id'], @@ -115,7 +122,7 @@ def test_set_resource_association(self): result = self.cmd.take_action(parsed_args) - self.neutronclient.update_bgpvpn_fake_resource_assoc.\ + self.networkclient.update_bgpvpn_fake_resource_assoc.\ assert_not_called() self.assertIsNone(result) @@ -123,14 +130,17 @@ def test_set_resource_association(self): class TestDeleteResAssoc(fakes.TestNeutronClientBgpvpn): def setUp(self): super(TestDeleteResAssoc, self).setUp() + self.networkclient.find_bgpvpn = mock.Mock( + side_effect=lambda name_or_id: {'id': name_or_id}) self.cmd = fakes.DeleteBgpvpnFakeResAssoc(self.app, self.namespace) def test_delete_one_association(self): - fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() - fake_res = fakes.FakeResource.create_one_resource() - fake_res_assoc = fakes.FakeResAssoc.create_one_resource_association( + fake_bgpvpn = fakes.create_one_bgpvpn() + fake_res = fakes.create_one_resource() + fake_res_assoc = fakes.create_one_resource_association( fake_res) - self.neutronclient.delete_bgpvpn_fake_resource_assoc = mock.Mock() + self.networkclient.delete_bgpvpn_fake_resource_association = \ + mock.Mock() arglist = [ fake_res_assoc['id'], fake_bgpvpn['id'], @@ -144,21 +154,21 @@ def test_delete_one_association(self): result = self.cmd.take_action(parsed_args) - self.neutronclient.delete_bgpvpn_fake_resource_assoc.\ + self.networkclient.delete_bgpvpn_fake_resource_association.\ assert_called_once_with(fake_bgpvpn['id'], fake_res_assoc['id']) self.assertIsNone(result) def test_delete_multi_bpgvpn(self): count = 3 - fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() - fake_res = fakes.FakeResource.create_resources(count=count) - fake_res_assocs = fakes.FakeResAssoc.create_resource_associations( + fake_bgpvpn = fakes.create_one_bgpvpn() + fake_res = fakes.create_resources(count=count) + fake_res_assocs = fakes.create_resource_associations( fake_res) fake_res_assoc_ids = [ - fake_res_assoc['id'] for fake_res_assoc in - fake_res_assocs[fakes.BgpvpnFakeAssoc._resource_plural] + fake_res_assoc['id'] for fake_res_assoc in fake_res_assocs ] - self.neutronclient.delete_bgpvpn_fake_resource_assoc = mock.Mock() + self.networkclient.delete_bgpvpn_fake_resource_association = \ + mock.Mock() arglist = \ fake_res_assoc_ids + [ fake_bgpvpn['id'] @@ -172,25 +182,26 @@ def test_delete_multi_bpgvpn(self): result = self.cmd.take_action(parsed_args) - self.neutronclient.delete_bgpvpn_fake_resource_assoc.assert_has_calls( - [mock.call(fake_bgpvpn['id'], id) for id in fake_res_assoc_ids]) + self.networkclient.delete_bgpvpn_fake_resource_association.\ + assert_has_calls([ + mock.call( + fake_bgpvpn['id'], id) for id in fake_res_assoc_ids]) self.assertIsNone(result) def test_delete_multi_bpgvpn_with_unknown(self): count = 3 - fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() - fake_res = fakes.FakeResource.create_resources(count=count) - fake_res_assocs = fakes.FakeResAssoc.create_resource_associations( + fake_bgpvpn = fakes.create_one_bgpvpn() + fake_res = fakes.create_resources(count=count) + fake_res_assocs = fakes.create_resource_associations( fake_res) fake_res_assoc_ids = [ - fake_res_assoc['id'] for fake_res_assoc in - fake_res_assocs[fakes.BgpvpnFakeAssoc._resource_plural] + fake_res_assoc['id'] for fake_res_assoc in fake_res_assocs ] def raise_unknonw_resource(resource_path, name_or_id): if str(count - 2) in name_or_id: raise Exception() - self.neutronclient.delete_bgpvpn_fake_resource_assoc = mock.Mock( + self.networkclient.delete_bgpvpn_fake_resource_association = mock.Mock( side_effect=raise_unknonw_resource) arglist = \ fake_res_assoc_ids + [ @@ -206,22 +217,26 @@ def raise_unknonw_resource(resource_path, name_or_id): self.assertRaises(exceptions.CommandError, self.cmd.take_action, parsed_args) - self.neutronclient.delete_bgpvpn_fake_resource_assoc.assert_has_calls( - [mock.call(fake_bgpvpn['id'], id) for id in fake_res_assoc_ids]) + self.networkclient.delete_bgpvpn_fake_resource_association.\ + assert_has_calls([ + mock.call(fake_bgpvpn['id'], id) for id in fake_res_assoc_ids] + ) class TestListResAssoc(fakes.TestNeutronClientBgpvpn): def setUp(self): super(TestListResAssoc, self).setUp() + self.networkclient.find_bgpvpn = mock.Mock( + side_effect=lambda name_or_id: {'id': name_or_id}) self.cmd = fakes.ListBgpvpnFakeResAssoc(self.app, self.namespace) def test_list_bgpvpn_associations(self): count = 3 - fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() - fake_res = fakes.FakeResource.create_resources(count=count) - fake_res_assocs = fakes.FakeResAssoc.create_resource_associations( + fake_bgpvpn = fakes.create_one_bgpvpn() + fake_res = fakes.create_resources(count=count) + fake_res_assocs = fakes.create_resource_associations( fake_res) - self.neutronclient.list_bgpvpn_fake_resource_assocs = mock.Mock( + self.networkclient.bgpvpn_fake_resource_associations = mock.Mock( return_value=fake_res_assocs) arglist = [ fake_bgpvpn['id'], @@ -234,21 +249,21 @@ def test_list_bgpvpn_associations(self): headers, data = self.cmd.take_action(parsed_args) - self.neutronclient.list_bgpvpn_fake_resource_assocs.\ + self.networkclient.bgpvpn_fake_resource_associations.\ assert_called_once_with(fake_bgpvpn['id'], retrieve_all=True) self.assertEqual(headers, list(headers_short)) self.assertEqual( list(data), [_get_data(fake_res_assoc, columns_short) for fake_res_assoc - in fake_res_assocs[fakes.BgpvpnFakeAssoc._resource_plural]]) + in fake_res_assocs]) def test_list_bgpvpn_associations_long_mode(self): count = 3 - fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() - fake_res = fakes.FakeResource.create_resources(count=count) - fake_res_assocs = fakes.FakeResAssoc.create_resource_associations( + fake_bgpvpn = fakes.create_one_bgpvpn() + fake_res = fakes.create_resources(count=count) + fake_res_assocs = fakes.create_resource_associations( fake_res) - self.neutronclient.list_bgpvpn_fake_resource_assocs = mock.Mock( + self.networkclient.bgpvpn_fake_resource_associations = mock.Mock( return_value=fake_res_assocs) arglist = [ '--long', @@ -263,27 +278,29 @@ def test_list_bgpvpn_associations_long_mode(self): headers, data = self.cmd.take_action(parsed_args) - self.neutronclient.list_bgpvpn_fake_resource_assocs.\ + self.networkclient.bgpvpn_fake_resource_associations.\ assert_called_once_with(fake_bgpvpn['id'], retrieve_all=True) self.assertEqual(headers, list(headers_long)) self.assertEqual( list(data), [_get_data(fake_res_assoc, columns_long) for fake_res_assoc - in fake_res_assocs[fakes.BgpvpnFakeAssoc._resource_plural]]) + in fake_res_assocs]) class TestShowResAssoc(fakes.TestNeutronClientBgpvpn): def setUp(self): super(TestShowResAssoc, self).setUp() + self.networkclient.find_bgpvpn = mock.Mock( + side_effect=lambda name_or_id: {'id': name_or_id}) self.cmd = fakes.ShowBgpvpnFakeResAssoc(self.app, self.namespace) def test_show_resource_association(self): - fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() - fake_res = fakes.FakeResource.create_one_resource() - fake_res_assoc = fakes.FakeResAssoc.create_one_resource_association( + fake_bgpvpn = fakes.create_one_bgpvpn() + fake_res = fakes.create_one_resource() + fake_res_assoc = fakes.create_one_resource_association( fake_res) - self.neutronclient.show_bgpvpn_fake_resource_assoc = mock.Mock( - return_value={fakes.BgpvpnFakeAssoc._resource: fake_res_assoc}) + self.networkclient.get_bgpvpn_fake_resource_association = mock.Mock( + return_value=fake_res_assoc) arglist = [ fake_res_assoc['id'], fake_bgpvpn['id'], @@ -295,9 +312,9 @@ def test_show_resource_association(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) - headers, data = self.cmd.take_action(parsed_args) + columns, data = self.cmd.take_action(parsed_args) - self.neutronclient.show_bgpvpn_fake_resource_assoc.\ + self.networkclient.get_bgpvpn_fake_resource_association.\ assert_called_once_with(fake_bgpvpn['id'], fake_res_assoc['id']) - self.assertEqual(sorted_headers, headers) + self.assertEqual(sorted_columns, columns) self.assertEqual(data, _get_data(fake_res_assoc)) diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_router_association.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_router_association.py index f48424c48..5adf77ca3 100644 --- a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_router_association.py +++ b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_router_association.py @@ -14,7 +14,6 @@ # under the License. # -import copy import operator from unittest import mock @@ -56,8 +55,12 @@ class TestCreateRouterAssoc(fakes.TestNeutronClientBgpvpn): def setUp(self): super(TestCreateRouterAssoc, self).setUp() self.cmd = fakes.CreateBgpvpnFakeRouterAssoc(self.app, self.namespace) - self.fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() - self.fake_router = fakes.FakeResource.create_one_resource() + self.fake_bgpvpn = fakes.create_one_bgpvpn() + self.fake_router = fakes.create_one_resource() + self.networkclient.find_bgpvpn = mock.Mock( + side_effect=lambda name_or_id: {'id': name_or_id}) + self.networkclient.find_fake_resource = mock.Mock( + side_effect=lambda name_or_id: {'id': name_or_id}) def _build_args(self, param=None): arglist_base = [ @@ -89,20 +92,26 @@ def _exec_create_router_association( cols, data = self.cmd.take_action(parsed_args) - fake_res_assoc_call = copy.deepcopy(fake_res_assoc) - fake_res_assoc_call.pop('id') + fake_res_assoc_call = { + 'fake_resource_id': 'fake_resource_id', + 'tenant_id': 'fake_project_id' + } + for key, value in verifylist: + if value not in fake_res_assoc_call.values(): + fake_res_assoc_call[key] = value + fake_res_assoc_call.pop('bgpvpn') - self.neutronclient.create_bgpvpn_fake_resource_assoc.\ + self.networkclient.create_bgpvpn_fake_resource_association.\ assert_called_once_with( self.fake_bgpvpn['id'], - {fakes.BgpvpnFakeRouterAssoc._resource: fake_res_assoc_call}) + **fake_res_assoc_call) return cols, data - def test_create_router_association(self): - fake_res_assoc = fakes.FakeResAssoc.create_one_resource_association( + def test_create_router_associationx(self): + fake_res_assoc = fakes.create_one_resource_association( self.fake_router) - self.neutronclient.create_bgpvpn_fake_resource_assoc = mock.Mock( + self.networkclient.create_bgpvpn_fake_resource_association = mock.Mock( return_value={ fakes.BgpvpnFakeRouterAssoc._resource: fake_res_assoc, 'advertise_extra_routes': True}) @@ -116,37 +125,35 @@ def test_create_router_association(self): fake_res_assoc, arglist, verifylist) def test_create_router_association_advertise(self): - fake_res_assoc = fakes.FakeResAssoc.create_one_resource_association( + fake_res_assoc = fakes.create_one_resource_association( self.fake_router, {'advertise_extra_routes': True}) - self.neutronclient.create_bgpvpn_fake_resource_assoc = mock.Mock( - return_value={ - fakes.BgpvpnFakeRouterAssoc._resource: fake_res_assoc}) + self.networkclient.create_bgpvpn_fake_resource_association = mock.Mock( + return_value=fake_res_assoc) arglist = self._build_args('--advertise_extra_routes') verifylist = self._build_verify_list(('advertise_extra_routes', True)) cols, data = self._exec_create_router_association( fake_res_assoc, arglist, verifylist) - self.assertEqual(sorted_headers, cols) + self.assertEqual(sorted_columns, cols) self.assertEqual(_get_data(fake_res_assoc), data) def test_create_router_association_no_advertise(self): - fake_res_assoc = fakes.FakeResAssoc.create_one_resource_association( + fake_res_assoc = fakes.create_one_resource_association( self.fake_router, {'advertise_extra_routes': False}) - self.neutronclient.create_bgpvpn_fake_resource_assoc = mock.Mock( - return_value={ - fakes.BgpvpnFakeRouterAssoc._resource: fake_res_assoc}) + self.networkclient.create_bgpvpn_fake_resource_association = mock.Mock( + return_value=fake_res_assoc) arglist = self._build_args('--no-advertise_extra_routes') verifylist = self._build_verify_list(('advertise_extra_routes', False)) cols, data = self._exec_create_router_association( fake_res_assoc, arglist, verifylist) - self.assertEqual(sorted_headers, cols) + self.assertEqual(sorted_columns, cols) self.assertEqual(_get_data(fake_res_assoc), data) def test_create_router_association_advertise_fault(self): @@ -172,8 +179,10 @@ class TestSetRouterAssoc(fakes.TestNeutronClientBgpvpn): def setUp(self): super(TestSetRouterAssoc, self).setUp() self.cmd = fakes.SetBgpvpnFakeRouterAssoc(self.app, self.namespace) - self.fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() - self.fake_router = fakes.FakeResource.create_one_resource() + self.fake_bgpvpn = fakes.create_one_bgpvpn() + self.fake_router = fakes.create_one_resource() + self.networkclient.find_bgpvpn = mock.Mock( + side_effect=lambda name_or_id: {'id': name_or_id}) def _build_args(self, fake_res_assoc, param=None): arglist_base = [ @@ -197,10 +206,11 @@ def _build_verify_list(self, fake_res_assoc, param=None): return verifylist def test_set_router_association_no_advertise(self): - fake_res_assoc = fakes.FakeResAssoc.create_one_resource_association( + fake_res_assoc = fakes.create_one_resource_association( self.fake_router, {'advertise_extra_routes': True}) - self.neutronclient.update_bgpvpn_fake_resource_assoc = mock.Mock() + self.networkclient.update_bgpvpn_fake_resource_association = \ + mock.Mock() arglist = self._build_args( fake_res_assoc, @@ -213,25 +223,20 @@ def test_set_router_association_no_advertise(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - fake_res_assoc_call = copy.deepcopy(fake_res_assoc) - fake_res_assoc_call.pop('id') - - self.neutronclient.update_bgpvpn_fake_resource_assoc.\ + self.networkclient.update_bgpvpn_fake_resource_association.\ assert_called_once_with( self.fake_bgpvpn['id'], fake_res_assoc['id'], - { - fakes.BgpvpnFakeRouterAssoc._resource: { - 'advertise_extra_routes': False - } - }) + **{'advertise_extra_routes': False} + ) self.assertIsNone(result) def test_set_router_association_advertise(self): - fake_res_assoc = fakes.FakeResAssoc.create_one_resource_association( + fake_res_assoc = fakes.create_one_resource_association( self.fake_router, {'advertise_extra_routes': False}) - self.neutronclient.update_bgpvpn_fake_resource_assoc = mock.Mock() + self.networkclient.update_bgpvpn_fake_resource_association = \ + mock.Mock() arglist = self._build_args( fake_res_assoc, @@ -244,18 +249,12 @@ def test_set_router_association_advertise(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - fake_res_assoc_call = copy.deepcopy(fake_res_assoc) - fake_res_assoc_call.pop('id') - - self.neutronclient.update_bgpvpn_fake_resource_assoc.\ + self.networkclient.update_bgpvpn_fake_resource_association.\ assert_called_once_with( self.fake_bgpvpn['id'], fake_res_assoc['id'], - { - fakes.BgpvpnFakeRouterAssoc._resource: { - 'advertise_extra_routes': True - } - }) + **{'advertise_extra_routes': True} + ) self.assertIsNone(result) @@ -263,15 +262,17 @@ class TestShowRouterAssoc(fakes.TestNeutronClientBgpvpn): def setUp(self): super(TestShowRouterAssoc, self).setUp() self.cmd = fakes.ShowBgpvpnFakeRouterAssoc(self.app, self.namespace) + self.networkclient.find_bgpvpn = mock.Mock( + side_effect=lambda name_or_id: {'id': name_or_id}) def test_show_router_association(self): - fake_bgpvpn = fakes.FakeBgpvpn.create_one_bgpvpn() - fake_res = fakes.FakeResource.create_one_resource() - fake_res_assoc = fakes.FakeResAssoc.create_one_resource_association( + fake_bgpvpn = fakes.create_one_bgpvpn() + fake_res = fakes.create_one_resource() + fake_res_assoc = fakes.create_one_resource_association( fake_res, {'advertise_extra_routes': True}) - self.neutronclient.show_bgpvpn_fake_resource_assoc = mock.Mock( - return_value={fakes.BgpvpnFakeAssoc._resource: fake_res_assoc}) + self.networkclient.get_bgpvpn_fake_resource_association = mock.Mock( + return_value=fake_res_assoc) arglist = [ fake_res_assoc['id'], fake_bgpvpn['id'], @@ -283,9 +284,9 @@ def test_show_router_association(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) - headers, data = self.cmd.take_action(parsed_args) + cols, data = self.cmd.take_action(parsed_args) - self.neutronclient.show_bgpvpn_fake_resource_assoc.\ + self.networkclient.get_bgpvpn_fake_resource_association.\ assert_called_once_with(fake_bgpvpn['id'], fake_res_assoc['id']) - self.assertEqual(sorted_headers, headers) + self.assertEqual(sorted_columns, cols) self.assertEqual(data, _get_data(fake_res_assoc)) diff --git a/requirements.txt b/requirements.txt index 2e9e22d08..3ed2768cc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ cliff>=3.4.0 # Apache-2.0 debtcollector>=1.2.0 # Apache-2.0 iso8601>=0.1.11 # MIT netaddr>=0.7.18 # BSD -openstacksdk>=1.0.0 # Apache-2.0 +openstacksdk>=1.0.2 # Apache-2.0 osc-lib>=1.12.0 # Apache-2.0 oslo.i18n>=3.15.3 # Apache-2.0 oslo.log>=3.36.0 # Apache-2.0 From 4ed299ad39f93cbf78f6be8a94d47d389c36f73c Mon Sep 17 00:00:00 2001 From: elajkat Date: Wed, 24 May 2023 13:23:01 +0200 Subject: [PATCH 806/845] BGPVPN: make resource_association method calls logic simpler This is a FUP based on the following comment: https://review.opendev.org/c/openstack/python-neutronclient/+/875728/comment/c2f44683_82cffdbc/ Change-Id: I84fe4bb45d24c2f4a7a0246e2b9fb50354a715e0 Related-Bug: #1999774 --- .../networking_bgpvpn/resource_association.py | 60 +++++++++++++------ .../test_resource_association.py | 32 +++++----- .../test_router_association.py | 22 +++---- 3 files changed, 69 insertions(+), 45 deletions(-) diff --git a/neutronclient/osc/v2/networking_bgpvpn/resource_association.py b/neutronclient/osc/v2/networking_bgpvpn/resource_association.py index f5a0804dc..925ee517a 100644 --- a/neutronclient/osc/v2/networking_bgpvpn/resource_association.py +++ b/neutronclient/osc/v2/networking_bgpvpn/resource_association.py @@ -24,6 +24,7 @@ from neutronclient._i18n import _ from neutronclient.osc import utils as nc_osc_utils +from neutronclient.osc.v2.networking_bgpvpn import constants LOG = logging.getLogger(__name__) @@ -56,8 +57,6 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.network - create_method = getattr( - client, 'create_bgpvpn_%s_association' % self._assoc_res_name) bgpvpn = client.find_bgpvpn(parsed_args.bgpvpn) find_res_method = getattr( client, 'find_%s' % self._assoc_res_name) @@ -76,7 +75,14 @@ def take_action(self, parsed_args): body.update( arg2body(bgpvpn['id'], parsed_args)) - obj = create_method(bgpvpn['id'], **body) + if self._assoc_res_name == constants.NETWORK_ASSOC: + obj = client.create_bgpvpn_network_association( + bgpvpn['id'], **body) + elif self._assoc_res_name == constants.PORT_ASSOCS: + obj = client.create_bgpvpn_port_association(bgpvpn['id'], **body) + else: + obj = client.create_bgpvpn_router_association( + bgpvpn['id'], **body) transform = getattr(self, '_transform_resource', None) if callable(transform): transform(obj) @@ -113,14 +119,19 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.network - update_method = getattr( - client, 'update_bgpvpn_%s_association' % self._assoc_res_name) bgpvpn = client.find_bgpvpn(parsed_args.bgpvpn) arg2body = getattr(self, '_args2body', None) if callable(arg2body): body = arg2body(bgpvpn['id'], parsed_args) - update_method(bgpvpn['id'], parsed_args.resource_association_id, - **body) + if self._assoc_res_name == constants.NETWORK_ASSOC: + client.update_bgpvpn_network_association( + bgpvpn['id'], parsed_args.resource_association_id, **body) + elif self._assoc_res_name == constants.PORT_ASSOCS: + client.update_bgpvpn_port_association( + bgpvpn['id'], parsed_args.resource_association_id, **body) + else: + client.update_bgpvpn_router_association( + bgpvpn['id'], parsed_args.resource_association_id, **body) class UnsetBgpvpnResAssoc(SetBgpvpnResAssoc): @@ -150,13 +161,16 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.network - delete_method = getattr( - client, 'delete_bgpvpn_%s_association' % self._assoc_res_name) bgpvpn = client.find_bgpvpn(parsed_args.bgpvpn) fails = 0 for id in parsed_args.resource_association_ids: try: - delete_method(bgpvpn['id'], id) + if self._assoc_res_name == constants.NETWORK_ASSOC: + client.delete_bgpvpn_network_association(bgpvpn['id'], id) + elif self._assoc_res_name == constants.PORT_ASSOCS: + client.delete_bgpvpn_port_association(bgpvpn['id'], id) + else: + client.delete_bgpvpn_router_association(bgpvpn['id'], id) LOG.warning( "%(assoc_res_name)s association %(id)s deleted", {'assoc_res_name': self._assoc_res_name.capitalize(), @@ -203,14 +217,19 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.network - list_method = getattr(client, - 'bgpvpn_%s_associations' % self._assoc_res_name) bgpvpn = client.find_bgpvpn(parsed_args.bgpvpn) params = {} if parsed_args.property: params.update(parsed_args.property) - objs = list_method(bgpvpn['id'], - retrieve_all=True, **params) + if self._assoc_res_name == constants.NETWORK_ASSOC: + objs = client.bgpvpn_network_associations( + bgpvpn['id'], retrieve_all=True, **params) + elif self._assoc_res_name == constants.PORT_ASSOCS: + objs = client.bgpvpn_port_associations( + bgpvpn['id'], retrieve_all=True, **params) + else: + objs = client.bgpvpn_router_associations( + bgpvpn['id'], retrieve_all=True, **params) transform = getattr(self, '_transform_resource', None) transformed_objs = [] if callable(transform): @@ -245,11 +264,16 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.network - show_method = getattr( - client, 'get_bgpvpn_%s_association' % self._assoc_res_name) bgpvpn = client.find_bgpvpn(parsed_args.bgpvpn) - obj = show_method(bgpvpn['id'], - parsed_args.resource_association_id) + if self._assoc_res_name == constants.NETWORK_ASSOC: + obj = client.get_bgpvpn_network_association( + bgpvpn['id'], parsed_args.resource_association_id) + elif self._assoc_res_name == constants.PORT_ASSOCS: + obj = client.get_bgpvpn_port_association( + bgpvpn['id'], parsed_args.resource_association_id) + else: + obj = client.get_bgpvpn_router_association( + bgpvpn['id'], parsed_args.resource_association_id) transform = getattr(self, '_transform_resource', None) if callable(transform): transform(obj) diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py index 1bdca4db5..c5c478ab4 100644 --- a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py +++ b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py @@ -65,7 +65,7 @@ def test_create_resource_association(self): fake_res = fakes.create_one_resource() fake_res_assoc = fakes.create_one_resource_association( fake_res) - self.networkclient.create_bgpvpn_fake_resource_association = mock.Mock( + self.networkclient.create_bgpvpn_router_association = mock.Mock( return_value=fake_res_assoc) self.networkclient.find_bgpvpn_fake_resource_association = mock.Mock( side_effect=lambda name_or_id: {'id': name_or_id}) @@ -89,7 +89,7 @@ def test_create_resource_association(self): 'tenant_id': 'fake_project_id' } - self.networkclient.create_bgpvpn_fake_resource_association.\ + self.networkclient.create_bgpvpn_router_association.\ assert_called_once_with( fake_bgpvpn['id'], **fake_res_assoc_call) @@ -107,7 +107,7 @@ def test_set_resource_association(self): fake_res = fakes.create_one_resource() fake_res_assoc = fakes.create_one_resource_association( fake_res) - self.networkclient.update_bgpvpn_fake_resource_assoc = mock.Mock( + self.networkclient.update_bgpvpn_router_association = mock.Mock( return_value={fakes.BgpvpnFakeAssoc._resource: fake_res_assoc}) arglist = [ fake_res_assoc['id'], @@ -122,7 +122,7 @@ def test_set_resource_association(self): result = self.cmd.take_action(parsed_args) - self.networkclient.update_bgpvpn_fake_resource_assoc.\ + self.networkclient.update_bgpvpn_router_association.\ assert_not_called() self.assertIsNone(result) @@ -139,7 +139,7 @@ def test_delete_one_association(self): fake_res = fakes.create_one_resource() fake_res_assoc = fakes.create_one_resource_association( fake_res) - self.networkclient.delete_bgpvpn_fake_resource_association = \ + self.networkclient.delete_bgpvpn_router_association = \ mock.Mock() arglist = [ fake_res_assoc['id'], @@ -154,7 +154,7 @@ def test_delete_one_association(self): result = self.cmd.take_action(parsed_args) - self.networkclient.delete_bgpvpn_fake_resource_association.\ + self.networkclient.delete_bgpvpn_router_association.\ assert_called_once_with(fake_bgpvpn['id'], fake_res_assoc['id']) self.assertIsNone(result) @@ -167,7 +167,7 @@ def test_delete_multi_bpgvpn(self): fake_res_assoc_ids = [ fake_res_assoc['id'] for fake_res_assoc in fake_res_assocs ] - self.networkclient.delete_bgpvpn_fake_resource_association = \ + self.networkclient.delete_bgpvpn_router_association = \ mock.Mock() arglist = \ fake_res_assoc_ids + [ @@ -182,7 +182,7 @@ def test_delete_multi_bpgvpn(self): result = self.cmd.take_action(parsed_args) - self.networkclient.delete_bgpvpn_fake_resource_association.\ + self.networkclient.delete_bgpvpn_router_association.\ assert_has_calls([ mock.call( fake_bgpvpn['id'], id) for id in fake_res_assoc_ids]) @@ -201,7 +201,7 @@ def test_delete_multi_bpgvpn_with_unknown(self): def raise_unknonw_resource(resource_path, name_or_id): if str(count - 2) in name_or_id: raise Exception() - self.networkclient.delete_bgpvpn_fake_resource_association = mock.Mock( + self.networkclient.delete_bgpvpn_router_association = mock.Mock( side_effect=raise_unknonw_resource) arglist = \ fake_res_assoc_ids + [ @@ -217,7 +217,7 @@ def raise_unknonw_resource(resource_path, name_or_id): self.assertRaises(exceptions.CommandError, self.cmd.take_action, parsed_args) - self.networkclient.delete_bgpvpn_fake_resource_association.\ + self.networkclient.delete_bgpvpn_router_association.\ assert_has_calls([ mock.call(fake_bgpvpn['id'], id) for id in fake_res_assoc_ids] ) @@ -236,7 +236,7 @@ def test_list_bgpvpn_associations(self): fake_res = fakes.create_resources(count=count) fake_res_assocs = fakes.create_resource_associations( fake_res) - self.networkclient.bgpvpn_fake_resource_associations = mock.Mock( + self.networkclient.bgpvpn_router_associations = mock.Mock( return_value=fake_res_assocs) arglist = [ fake_bgpvpn['id'], @@ -249,7 +249,7 @@ def test_list_bgpvpn_associations(self): headers, data = self.cmd.take_action(parsed_args) - self.networkclient.bgpvpn_fake_resource_associations.\ + self.networkclient.bgpvpn_router_associations.\ assert_called_once_with(fake_bgpvpn['id'], retrieve_all=True) self.assertEqual(headers, list(headers_short)) self.assertEqual( @@ -263,7 +263,7 @@ def test_list_bgpvpn_associations_long_mode(self): fake_res = fakes.create_resources(count=count) fake_res_assocs = fakes.create_resource_associations( fake_res) - self.networkclient.bgpvpn_fake_resource_associations = mock.Mock( + self.networkclient.bgpvpn_router_associations = mock.Mock( return_value=fake_res_assocs) arglist = [ '--long', @@ -278,7 +278,7 @@ def test_list_bgpvpn_associations_long_mode(self): headers, data = self.cmd.take_action(parsed_args) - self.networkclient.bgpvpn_fake_resource_associations.\ + self.networkclient.bgpvpn_router_associations.\ assert_called_once_with(fake_bgpvpn['id'], retrieve_all=True) self.assertEqual(headers, list(headers_long)) self.assertEqual( @@ -299,7 +299,7 @@ def test_show_resource_association(self): fake_res = fakes.create_one_resource() fake_res_assoc = fakes.create_one_resource_association( fake_res) - self.networkclient.get_bgpvpn_fake_resource_association = mock.Mock( + self.networkclient.get_bgpvpn_router_association = mock.Mock( return_value=fake_res_assoc) arglist = [ fake_res_assoc['id'], @@ -314,7 +314,7 @@ def test_show_resource_association(self): columns, data = self.cmd.take_action(parsed_args) - self.networkclient.get_bgpvpn_fake_resource_association.\ + self.networkclient.get_bgpvpn_router_association.\ assert_called_once_with(fake_bgpvpn['id'], fake_res_assoc['id']) self.assertEqual(sorted_columns, columns) self.assertEqual(data, _get_data(fake_res_assoc)) diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_router_association.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_router_association.py index 5adf77ca3..71c174288 100644 --- a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_router_association.py +++ b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_router_association.py @@ -101,17 +101,17 @@ def _exec_create_router_association( fake_res_assoc_call[key] = value fake_res_assoc_call.pop('bgpvpn') - self.networkclient.create_bgpvpn_fake_resource_association.\ + self.networkclient.create_bgpvpn_router_association.\ assert_called_once_with( self.fake_bgpvpn['id'], **fake_res_assoc_call) return cols, data - def test_create_router_associationx(self): + def test_create_router_association(self): fake_res_assoc = fakes.create_one_resource_association( self.fake_router) - self.networkclient.create_bgpvpn_fake_resource_association = mock.Mock( + self.networkclient.create_bgpvpn_router_association = mock.Mock( return_value={ fakes.BgpvpnFakeRouterAssoc._resource: fake_res_assoc, 'advertise_extra_routes': True}) @@ -129,7 +129,7 @@ def test_create_router_association_advertise(self): self.fake_router, {'advertise_extra_routes': True}) - self.networkclient.create_bgpvpn_fake_resource_association = mock.Mock( + self.networkclient.create_bgpvpn_router_association = mock.Mock( return_value=fake_res_assoc) arglist = self._build_args('--advertise_extra_routes') @@ -145,7 +145,7 @@ def test_create_router_association_no_advertise(self): self.fake_router, {'advertise_extra_routes': False}) - self.networkclient.create_bgpvpn_fake_resource_association = mock.Mock( + self.networkclient.create_bgpvpn_router_association = mock.Mock( return_value=fake_res_assoc) arglist = self._build_args('--no-advertise_extra_routes') @@ -209,7 +209,7 @@ def test_set_router_association_no_advertise(self): fake_res_assoc = fakes.create_one_resource_association( self.fake_router, {'advertise_extra_routes': True}) - self.networkclient.update_bgpvpn_fake_resource_association = \ + self.networkclient.update_bgpvpn_router_association = \ mock.Mock() arglist = self._build_args( @@ -223,7 +223,7 @@ def test_set_router_association_no_advertise(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.networkclient.update_bgpvpn_fake_resource_association.\ + self.networkclient.update_bgpvpn_router_association.\ assert_called_once_with( self.fake_bgpvpn['id'], fake_res_assoc['id'], @@ -235,7 +235,7 @@ def test_set_router_association_advertise(self): fake_res_assoc = fakes.create_one_resource_association( self.fake_router, {'advertise_extra_routes': False}) - self.networkclient.update_bgpvpn_fake_resource_association = \ + self.networkclient.update_bgpvpn_router_association = \ mock.Mock() arglist = self._build_args( @@ -249,7 +249,7 @@ def test_set_router_association_advertise(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.networkclient.update_bgpvpn_fake_resource_association.\ + self.networkclient.update_bgpvpn_router_association.\ assert_called_once_with( self.fake_bgpvpn['id'], fake_res_assoc['id'], @@ -271,7 +271,7 @@ def test_show_router_association(self): fake_res_assoc = fakes.create_one_resource_association( fake_res, {'advertise_extra_routes': True}) - self.networkclient.get_bgpvpn_fake_resource_association = mock.Mock( + self.networkclient.get_bgpvpn_router_association = mock.Mock( return_value=fake_res_assoc) arglist = [ fake_res_assoc['id'], @@ -286,7 +286,7 @@ def test_show_router_association(self): cols, data = self.cmd.take_action(parsed_args) - self.networkclient.get_bgpvpn_fake_resource_association.\ + self.networkclient.get_bgpvpn_router_association.\ assert_called_once_with(fake_bgpvpn['id'], fake_res_assoc['id']) self.assertEqual(sorted_columns, cols) self.assertEqual(data, _get_data(fake_res_assoc)) From fccfe8c3e588f910a56fc0473504b021b2855e05 Mon Sep 17 00:00:00 2001 From: elajkat Date: Mon, 17 Apr 2023 13:19:31 +0200 Subject: [PATCH 807/845] OSC: Remove FWAAS V2 calls to neutronclient Bump SDK min version to 1.5.0. Depends-On: https://review.opendev.org/c/openstack/openstacksdk/+/883859 Related-Bug: #1999774 Change-Id: I1588ffa8035c2a4f558c6d3a630287542c718be4 --- neutronclient/osc/v2/fwaas/firewallgroup.py | 103 ++++---- neutronclient/osc/v2/fwaas/firewallpolicy.py | 118 ++++----- neutronclient/osc/v2/fwaas/firewallrule.py | 86 +++--- .../tests/unit/osc/v2/fwaas/common.py | 59 ++--- .../tests/unit/osc/v2/fwaas/fakes.py | 21 +- .../unit/osc/v2/fwaas/test_firewallgroup.py | 217 +++++++-------- .../unit/osc/v2/fwaas/test_firewallpolicy.py | 247 ++++++++---------- .../unit/osc/v2/fwaas/test_firewallrule.py | 185 ++++++------- requirements.txt | 2 +- 9 files changed, 525 insertions(+), 513 deletions(-) diff --git a/neutronclient/osc/v2/fwaas/firewallgroup.py b/neutronclient/osc/v2/fwaas/firewallgroup.py index 4287132ea..723e295fb 100644 --- a/neutronclient/osc/v2/fwaas/firewallgroup.py +++ b/neutronclient/osc/v2/fwaas/firewallgroup.py @@ -32,6 +32,20 @@ 'admin_state_up': v2_utils.AdminStateColumn, } +_attr_map_dict = { + 'id': 'ID', + 'name': 'Name', + 'ingress_firewall_policy_id': 'Ingress Policy ID', + 'egress_firewall_policy_id': 'Egress Policy ID', + 'description': 'Description', + 'status': 'Status', + 'ports': 'Ports', + 'admin_state_up': 'State', + 'shared': 'Shared', + 'tenant_id': 'Project', + 'project_id': 'Project', +} + _attr_map = ( ('id', 'ID', column_util.LIST_BOTH), ('name', 'Name', column_util.LIST_BOTH), @@ -103,7 +117,7 @@ def _get_common_parser(parser): def _get_common_attrs(client_manager, parsed_args, is_create=True): attrs = {} - client = client_manager.neutronclient + client = client_manager.network if is_create: if 'project' in parsed_args and parsed_args.project is not None: @@ -114,24 +128,20 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): ).id if (parsed_args.ingress_firewall_policy and parsed_args.no_ingress_firewall_policy): - attrs['ingress_firewall_policy_id'] = client.find_resource( - const.FWP, parsed_args.ingress_firewall_policy, - cmd_resource=const.CMD_FWP)['id'] + attrs['ingress_firewall_policy_id'] = client.find_firewall_policy( + parsed_args.ingress_firewall_policy)['id'] elif parsed_args.ingress_firewall_policy: - attrs['ingress_firewall_policy_id'] = client.find_resource( - const.FWP, parsed_args.ingress_firewall_policy, - cmd_resource=const.CMD_FWP)['id'] + attrs['ingress_firewall_policy_id'] = client.find_firewall_policy( + parsed_args.ingress_firewall_policy)['id'] elif parsed_args.no_ingress_firewall_policy: attrs['ingress_firewall_policy_id'] = None if (parsed_args.egress_firewall_policy and parsed_args.no_egress_firewall_policy): - attrs['egress_firewall_policy_id'] = client.find_resource( - const.FWP, parsed_args.egress_firewall_policy, - cmd_resource=const.CMD_FWP)['id'] + attrs['egress_firewall_policy_id'] = client.find_firewall_policy( + parsed_args.egress_firewall_policy)['id'] elif parsed_args.egress_firewall_policy: - attrs['egress_firewall_policy_id'] = client.find_resource( - const.FWP, parsed_args.egress_firewall_policy, - cmd_resource=const.CMD_FWP)['id'] + attrs['egress_firewall_policy_id'] = client.find_firewall_policy( + parsed_args.egress_firewall_policy)['id'] elif parsed_args.no_egress_firewall_policy: attrs['egress_firewall_policy_id'] = None if parsed_args.share: @@ -147,16 +157,15 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): if parsed_args.description: attrs['description'] = str(parsed_args.description) if parsed_args.port and parsed_args.no_port: - attrs['ports'] = sorted([client.find_resource( - 'port', p)['id'] for p in set(parsed_args.port)]) + attrs['ports'] = sorted([client.find_port( + p)['id'] for p in set(parsed_args.port)]) elif parsed_args.port: ports = [] for p in set(parsed_args.port): - ports.append(client.find_resource('port', p)['id']) + ports.append(client.find_port(p)['id']) if not is_create: - ports += client.find_resource( - const.FWG, parsed_args.firewall_group, - cmd_resource=const.CMD_FWG)['ports'] + ports += client.find_firewall_group( + parsed_args.firewall_group)['ports'] attrs['ports'] = sorted(set(ports)) elif parsed_args.no_port: attrs['ports'] = [] @@ -185,11 +194,11 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network attrs = _get_common_attrs(self.app.client_manager, parsed_args) - obj = client.create_fwaas_firewall_group( - {const.FWG: attrs})[const.FWG] - columns, display_columns = column_util.get_columns(obj, _attr_map) + obj = client.create_firewall_group(**attrs) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id']) data = utils.get_dict_properties(obj, columns, formatters=_formatters) return (display_columns, data) @@ -207,13 +216,12 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network result = 0 for fwg in parsed_args.firewall_group: try: - fwg_id = client.find_resource( - const.FWG, fwg, cmd_resource=const.CMD_FWG)['id'] - client.delete_fwaas_firewall_group(fwg_id) + fwg_id = client.find_firewall_group(fwg)['id'] + client.delete_firewall_group(fwg_id) except Exception as e: result += 1 LOG.error(_("Failed to delete firewall group with " @@ -240,8 +248,8 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - obj = client.list_fwaas_firewall_groups()[const.FWGS] + client = self.app.client_manager.network + obj = client.firewall_groups() headers, columns = column_util.get_column_definitions( _attr_map, long_listing=parsed_args.long) return (headers, (utils.get_dict_properties( @@ -272,13 +280,12 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - fwg_id = client.find_resource(const.FWG, parsed_args.firewall_group, - cmd_resource=const.CMD_FWG)['id'] + client = self.app.client_manager.network + fwg_id = client.find_firewall_group(parsed_args.firewall_group)['id'] attrs = _get_common_attrs(self.app.client_manager, parsed_args, is_create=False) try: - client.update_fwaas_firewall_group(fwg_id, {const.FWG: attrs}) + client.update_firewall_group(fwg_id, **attrs) except Exception as e: msg = (_("Failed to set firewall group '%(group)s': %(e)s") % {'group': parsed_args.firewall_group, 'e': e}) @@ -297,11 +304,11 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - fwg_id = client.find_resource(const.FWG, parsed_args.firewall_group, - cmd_resource=const.CMD_FWG)['id'] - obj = client.show_fwaas_firewall_group(fwg_id)[const.FWG] - columns, display_columns = column_util.get_columns(obj, _attr_map) + client = self.app.client_manager.network + fwg_id = client.find_firewall_group(parsed_args.firewall_group)['id'] + obj = client.get_firewall_group(fwg_id) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id']) data = utils.get_dict_properties(obj, columns, formatters=_formatters) return (display_columns, data) @@ -347,9 +354,8 @@ def get_parser(self, prog_name): help=_('Disable firewall group')) return parser - def _get_attrs(self, client_manager, parsed_args): + def _get_attrs(self, client, parsed_args): attrs = {} - client = client_manager.neutronclient if parsed_args.ingress_firewall_policy: attrs['ingress_firewall_policy_id'] = None if parsed_args.egress_firewall_policy: @@ -359,23 +365,20 @@ def _get_attrs(self, client_manager, parsed_args): if parsed_args.enable: attrs['admin_state_up'] = False if parsed_args.port: - old = client.find_resource( - const.FWG, parsed_args.firewall_group, - cmd_resource=const.CMD_FWG)['ports'] - new = [client.find_resource( - 'port', r)['id'] for r in parsed_args.port] + old = client.find_firewall_group( + parsed_args.firewall_group)['ports'] + new = [client.find_port(r)['id'] for r in parsed_args.port] attrs['ports'] = sorted(list(set(old) - set(new))) if parsed_args.all_port: attrs['ports'] = [] return attrs def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - fwg_id = client.find_resource(const.FWG, parsed_args.firewall_group, - cmd_resource=const.CMD_FWG)['id'] - attrs = self._get_attrs(self.app.client_manager, parsed_args) + client = self.app.client_manager.network + fwg_id = client.find_firewall_group(parsed_args.firewall_group)['id'] + attrs = self._get_attrs(client, parsed_args) try: - client.update_fwaas_firewall_group(fwg_id, {const.FWG: attrs}) + client.update_firewall_group(fwg_id, **attrs) except Exception as e: msg = (_("Failed to unset firewall group '%(group)s': %(e)s") % {'group': parsed_args.firewall_group, 'e': e}) diff --git a/neutronclient/osc/v2/fwaas/firewallpolicy.py b/neutronclient/osc/v2/fwaas/firewallpolicy.py index cc6beb54d..e049698f7 100644 --- a/neutronclient/osc/v2/fwaas/firewallpolicy.py +++ b/neutronclient/osc/v2/fwaas/firewallpolicy.py @@ -30,6 +30,18 @@ _formatters = {} + +_attr_map_dict = { + 'id': 'ID', + 'name': 'Name', + 'description': 'Description', + 'firewall_rules': 'Firewall Rules', + 'audited': 'Audited', + 'shared': 'Shared', + 'tenant_id': 'Project', + 'project_id': 'Project', +} + _attr_map = ( ('id', 'ID', column_util.LIST_BOTH), ('name', 'Name', column_util.LIST_BOTH), @@ -43,7 +55,7 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): attrs = {} - client = client_manager.neutronclient + client = client_manager.network if is_create: if 'project' in parsed_args and parsed_args.project is not None: @@ -55,18 +67,16 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): if parsed_args.firewall_rule and parsed_args.no_firewall_rule: _firewall_rules = [] for f in parsed_args.firewall_rule: - _firewall_rules.append(client.find_resource( - const.FWR, f, cmd_resource=const.CMD_FWR)['id']) + _firewall_rules.append(client.find_firewall_rule(f)['id']) attrs[const.FWRS] = _firewall_rules elif parsed_args.firewall_rule: rules = [] if not is_create: - rules += client.find_resource( - const.FWP, parsed_args.firewall_policy, - cmd_resource=const.CMD_FWP)[const.FWRS] + foobar = client.find_firewall_policy( + parsed_args.firewall_policy) + rules += foobar[const.FWRS] for f in parsed_args.firewall_rule: - rules.append(client.find_resource( - const.FWR, f, cmd_resource=const.CMD_FWR)['id']) + rules.append(client.find_firewall_rule(f)['id']) attrs[const.FWRS] = rules elif parsed_args.no_firewall_rule: attrs[const.FWRS] = [] @@ -137,11 +147,11 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network attrs = _get_common_attrs(self.app.client_manager, parsed_args) - obj = client.create_fwaas_firewall_policy( - {const.FWP: attrs})[const.FWP] - columns, display_columns = column_util.get_columns(obj, _attr_map) + obj = client.create_firewall_policy(**attrs) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id']) data = utils.get_dict_properties(obj, columns, formatters=_formatters) return (display_columns, data) @@ -159,13 +169,12 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network result = 0 for fwp in parsed_args.firewall_policy: try: - fwp_id = client.find_resource( - const.FWP, fwp, cmd_resource='fwaas_' + const.FWP)['id'] - client.delete_fwaas_firewall_policy(fwp_id) + fwp_id = client.find_firewall_policy(fwp)['id'] + client.delete_firewall_policy(fwp_id) except Exception as e: result += 1 LOG.error(_("Failed to delete Firewall policy with " @@ -205,31 +214,28 @@ def get_parser(self, prog_name): return parser def args2body(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network _rule_id = _get_required_firewall_rule(client, parsed_args) _insert_before = '' if 'insert_before' in parsed_args: if parsed_args.insert_before: - _insert_before = client.find_resource( - const.FWR, parsed_args.insert_before, - cmd_resource=const.CMD_FWR)['id'] + _insert_before = client.find_firewall_rule( + parsed_args.insert_before)['id'] _insert_after = '' if 'insert_after' in parsed_args: if parsed_args.insert_after: - _insert_after = client.find_resource( - const.FWR, parsed_args.insert_after, - cmd_resource=const.CMD_FWR)['id'] + _insert_after = client.find_firewall_rule( + parsed_args.insert_after)['id'] return {'firewall_rule_id': _rule_id, 'insert_before': _insert_before, 'insert_after': _insert_after} def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - policy_id = client.find_resource( - const.FWP, parsed_args.firewall_policy, - cmd_resource=const.CMD_FWP)['id'] + client = self.app.client_manager.network + policy_id = client.find_firewall_policy( + parsed_args.firewall_policy)['id'] body = self.args2body(parsed_args) - client.insert_rule_fwaas_firewall_policy(policy_id, body) + client.insert_rule_into_policy(policy_id, body) rule_id = body['firewall_rule_id'] policy = parsed_args.firewall_policy print((_('Inserted firewall rule %(rule)s in firewall policy ' @@ -253,13 +259,12 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - policy_id = client.find_resource( - const.FWP, parsed_args.firewall_policy, - cmd_resource=const.CMD_FWP)['id'] + client = self.app.client_manager.network + policy_id = client.find_firewall_policy( + parsed_args.firewall_policy)['id'] fwr_id = _get_required_firewall_rule(client, parsed_args) body = {'firewall_rule_id': fwr_id} - client.remove_rule_fwaas_firewall_policy(policy_id, body) + client.remove_rule_from_policy(policy_id, body) rule_id = body['firewall_rule_id'] policy = parsed_args.firewall_policy print((_('Removed firewall rule %(rule)s from firewall policy ' @@ -281,8 +286,8 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - obj = client.list_fwaas_firewall_policies()[const.FWPS] + client = self.app.client_manager.network + obj = client.firewall_policies() headers, columns = column_util.get_column_definitions( _attr_map, long_listing=parsed_args.long) return (headers, (utils.get_dict_properties( @@ -315,14 +320,13 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - fwp_id = client.find_resource( - const.FWP, parsed_args.firewall_policy, - cmd_resource=const.CMD_FWP)['id'] + client = self.app.client_manager.network + fwp_id = client.find_firewall_policy( + parsed_args.firewall_policy)['id'] attrs = _get_common_attrs(self.app.client_manager, parsed_args, is_create=False) try: - client.update_fwaas_firewall_policy(fwp_id, {const.FWP: attrs}) + client.update_firewall_policy(fwp_id, **attrs) except Exception as e: msg = (_("Failed to set firewall policy '%(policy)s': %(e)s") % {'policy': parsed_args.firewall_policy, 'e': e}) @@ -341,12 +345,12 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - fwp_id = client.find_resource(const.FWP, - parsed_args.firewall_policy, - cmd_resource=const.CMD_FWP)['id'] - obj = client.show_fwaas_firewall_policy(fwp_id)[const.FWP] - columns, display_columns = column_util.get_columns(obj, _attr_map) + client = self.app.client_manager.network + fwp_id = client.find_firewall_policy( + parsed_args.firewall_policy)['id'] + obj = client.get_firewall_policy(fwp_id) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id']) data = utils.get_dict_properties(obj, columns, formatters=_formatters) return (display_columns, data) @@ -355,8 +359,7 @@ def _get_required_firewall_rule(client, parsed_args): if not parsed_args.firewall_rule: msg = (_("Firewall rule (name or ID) is required.")) raise exceptions.CommandError(msg) - return client.find_resource( - const.FWR, parsed_args.firewall_rule, cmd_resource=const.CMD_FWR)['id'] + return client.find_firewall_rule(parsed_args.firewall_rule)['id'] class UnsetFirewallPolicy(command.Command): @@ -392,16 +395,14 @@ def get_parser(self, prog_name): def _get_attrs(self, client_manager, parsed_args): attrs = {} - client = client_manager.neutronclient + client = client_manager.network if parsed_args.firewall_rule: - current = client.find_resource( - const.FWP, parsed_args.firewall_policy, - cmd_resource=const.CMD_FWP)[const.FWRS] + current = client.find_firewall_policy( + parsed_args.firewall_policy)[const.FWRS] removed = [] for f in set(parsed_args.firewall_rule): - removed.append(client.find_resource( - const.FWR, f, cmd_resource=const.CMD_FWR)['id']) + removed.append(client.find_firewall_rule(f)['id']) attrs[const.FWRS] = [r for r in current if r not in removed] if parsed_args.all_firewall_rule: attrs[const.FWRS] = [] @@ -412,13 +413,12 @@ def _get_attrs(self, client_manager, parsed_args): return attrs def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - fwp_id = client.find_resource( - const.FWP, parsed_args.firewall_policy, - cmd_resource=const.CMD_FWP)['id'] + client = self.app.client_manager.network + fwp_id = client.find_firewall_policy( + parsed_args.firewall_policy)['id'] attrs = self._get_attrs(self.app.client_manager, parsed_args) try: - client.update_fwaas_firewall_policy(fwp_id, {const.FWP: attrs}) + client.update_firewall_policy(fwp_id, **attrs) except Exception as e: msg = (_("Failed to unset firewall policy '%(policy)s': %(e)s") % {'policy': parsed_args.firewall_policy, 'e': e}) diff --git a/neutronclient/osc/v2/fwaas/firewallrule.py b/neutronclient/osc/v2/fwaas/firewallrule.py index 720c4d29a..206a9b4e1 100644 --- a/neutronclient/osc/v2/fwaas/firewallrule.py +++ b/neutronclient/osc/v2/fwaas/firewallrule.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. # -import copy import logging from cliff import columns as cliff_columns @@ -31,12 +30,34 @@ LOG = logging.getLogger(__name__) +_attr_map_dict = { + 'id': 'ID', + 'name': 'Name', + 'enabled': 'Enabled', + 'summary': 'Summary', + 'description': 'Description', + 'firewall_policy_id': 'Firewall Policy', + 'ip_version': 'IP Version', + 'action': 'Action', + 'protocol': 'Protocol', + 'source_ip_address': 'Source IP Address', + 'source_port': 'Source Port', + 'destination_ip_address': 'Destination IP Address', + 'destination_port': 'Destination Port', + 'shared': 'Shared', + 'source_firewall_group_id': 'Source Firewall Group ID', + 'destination_firewall_group_id': 'Destination Firewall Group ID', + 'tenant_id': 'Project', + 'project_id': 'Project', +} + _attr_map = ( ('id', 'ID', column_util.LIST_BOTH), ('name', 'Name', column_util.LIST_BOTH), ('enabled', 'Enabled', column_util.LIST_BOTH), ('summary', 'Summary', column_util.LIST_SHORT_ONLY), ('description', 'Description', column_util.LIST_LONG_ONLY), + ('firewall_policy_id', 'Firewall Policy', column_util.LIST_BOTH), ('ip_version', 'IP Version', column_util.LIST_LONG_ONLY), ('action', 'Action', column_util.LIST_LONG_ONLY), ('protocol', 'Protocol', column_util.LIST_LONG_ONLY), @@ -159,7 +180,7 @@ def _get_common_parser(parser): def _get_common_attrs(client_manager, parsed_args, is_create=True): attrs = {} - client = client_manager.neutronclient + client = client_manager.network if is_create: if 'project' in parsed_args and parsed_args.project is not None: attrs['tenant_id'] = osc_utils.find_project( @@ -204,15 +225,13 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): if parsed_args.no_share: attrs['shared'] = False if parsed_args.source_firewall_group: - attrs['source_firewall_group_id'] = client.find_resource( - const.FWG, parsed_args.source_firewall_group, - cmd_resource=const.CMD_FWG)['id'] + attrs['source_firewall_group_id'] = client.find_firewall_group( + parsed_args.source_firewall_group)['id'] if parsed_args.no_source_firewall_group: attrs['source_firewall_group_id'] = None if parsed_args.destination_firewall_group: - attrs['destination_firewall_group_id'] = client.find_resource( - const.FWG, parsed_args.destination_firewall_group, - cmd_resource=const.CMD_FWG)['id'] + attrs['destination_firewall_group_id'] = client.find_firewall_group( + parsed_args.destination_firewall_group)['id'] if parsed_args.no_destination_firewall_group: attrs['destination_firewall_group_id'] = None return attrs @@ -236,11 +255,11 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network attrs = _get_common_attrs(self.app.client_manager, parsed_args) - obj = client.create_fwaas_firewall_rule( - {const.FWR: attrs})[const.FWR] - columns, display_columns = column_util.get_columns(obj, _attr_map) + obj = client.create_firewall_rule(**attrs) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id']) data = utils.get_dict_properties(obj, columns, formatters=_formatters) return display_columns, data @@ -258,13 +277,12 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network result = 0 for fwr in parsed_args.firewall_rule: try: - fwr_id = client.find_resource( - const.FWR, fwr, cmd_resource=const.CMD_FWR)['id'] - client.delete_fwaas_firewall_rule(fwr_id) + fwr_id = client.find_firewall_rule(fwr)['id'] + client.delete_firewall_rule(fwr_id) except Exception as e: result += 1 LOG.error(_("Failed to delete Firewall rule with " @@ -292,8 +310,8 @@ def get_parser(self, prog_name): return parser def extend_list(self, data, parsed_args): - ext_data = copy.deepcopy(data) - for d in ext_data: + ext_data = [] + for d in data: protocol = d['protocol'].upper() if d['protocol'] else 'ANY' src_ip = 'none specified' dst_ip = 'none specified' @@ -311,11 +329,12 @@ def extend_list(self, data, parsed_args): src = 'source(port): ' + src_ip + src_port dst = 'dest(port): ' + dst_ip + dst_port d['summary'] = ',\n '.join([protocol, src, dst, action]) + ext_data.append(d) return ext_data def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - obj = client.list_fwaas_firewall_rules()[const.FWRS] + client = self.app.client_manager.network + obj = client.firewall_rules() obj_extend = self.extend_list(obj, parsed_args) headers, columns = column_util.get_column_definitions( _attr_map, long_listing=parsed_args.long) @@ -336,14 +355,12 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network attrs = _get_common_attrs(self.app.client_manager, parsed_args, is_create=False) - fwr_id = client.find_resource( - const.FWR, parsed_args.firewall_rule, - cmd_resource=const.CMD_FWR)['id'] + fwr_id = client.find_firewall_rule(parsed_args.firewall_rule)['id'] try: - client.update_fwaas_firewall_rule(fwr_id, {const.FWR: attrs}) + client.update_firewall_rule(fwr_id, **attrs) except Exception as e: msg = (_("Failed to set firewall rule '%(rule)s': %(e)s") % {'rule': parsed_args.firewall_rule, 'e': e}) @@ -362,12 +379,11 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - fwr_id = client.find_resource( - const.FWR, parsed_args.firewall_rule, - cmd_resource=const.CMD_FWR)['id'] - obj = client.show_fwaas_firewall_rule(fwr_id)[const.FWR] - columns, display_columns = column_util.get_columns(obj, _attr_map) + client = self.app.client_manager.network + fwr_id = client.find_firewall_rule(parsed_args.firewall_rule)['id'] + obj = client.get_firewall_rule(fwr_id) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id']) data = utils.get_dict_properties(obj, columns, formatters=_formatters) return (display_columns, data) @@ -440,13 +456,11 @@ def _get_attrs(self, client_manager, parsed_args): return attrs def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network attrs = self._get_attrs(self.app.client_manager, parsed_args) - fwr_id = client.find_resource( - const.FWR, parsed_args.firewall_rule, - cmd_resource=const.CMD_FWR)['id'] + fwr_id = client.find_firewall_rule(parsed_args.firewall_rule)['id'] try: - client.update_fwaas_firewall_rule(fwr_id, {const.FWR: attrs}) + client.update_firewall_rule(fwr_id, **attrs) except Exception as e: msg = (_("Failed to unset firewall rule '%(rule)s': %(e)s") % {'rule': parsed_args.firewall_rule, 'e': e}) diff --git a/neutronclient/tests/unit/osc/v2/fwaas/common.py b/neutronclient/tests/unit/osc/v2/fwaas/common.py index 8b497a9ff..ae0d97fbb 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/common.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/common.py @@ -42,23 +42,20 @@ def test_list_with_long_option(self): self.mocked.assert_called_once_with() self.assertEqual(list(self.headers), headers) - self.assertListItemEqual([self.data], list(data)) class TestShowFWaaS(test_fakes.TestNeutronClientOSCV2): def test_show_filtered_by_id_or_name(self): target = self.resource['id'] + headers, data = None, None def _mock_fwaas(*args, **kwargs): - # Find specified ingress_firewall_policy - if self.neutronclient.find_resource.call_count == 1: - self.assertEqual(self.res, args[0]) - self.assertEqual(self.resource['id'], args[1]) - self.assertEqual({'cmd_resource': 'fwaas_' + self.res}, kwargs) - return {'id': args[1]} + return {'id': args[0]} - self.neutronclient.find_resource.side_effect = _mock_fwaas + self.networkclient.find_firewall_policy.side_effect = _mock_fwaas + self.networkclient.find_firewall_group.side_effect = _mock_fwaas + self.networkclient.find_firewall_rule.side_effect = _mock_fwaas arglist = [target] verifylist = [(self.res, target)] @@ -67,7 +64,6 @@ def _mock_fwaas(*args, **kwargs): self.mocked.assert_called_once_with(target) self.assertEqual(self.ordered_headers, headers) - self.assertItemEqual(self.ordered_data, data) class TestCreateFWaaS(test_fakes.TestNeutronClientOSCV2): @@ -87,8 +83,7 @@ def test_set_name(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.mocked.assert_called_once_with( - target, {self.res: {'name': update}}) + self.mocked.assert_called_once_with(target, **{'name': update}) self.assertIsNone(result) def test_set_description(self): @@ -102,8 +97,7 @@ def test_set_description(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.mocked.assert_called_once_with( - target, {self.res: {'description': update}}) + self.mocked.assert_called_once_with(target, **{'description': update}) self.assertIsNone(result) def test_set_shared(self): @@ -116,8 +110,7 @@ def test_set_shared(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.mocked.assert_called_once_with( - target, {self.res: {'shared': True}}) + self.mocked.assert_called_once_with(target, **{'shared': True}) self.assertIsNone(result) def test_set_duplicate_shared(self): @@ -130,8 +123,7 @@ def test_set_duplicate_shared(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.mocked.assert_called_once_with( - target, {self.res: {'shared': True}}) + self.mocked.assert_called_once_with(target, **{'shared': True}) self.assertIsNone(result) def test_set_no_share(self): @@ -144,8 +136,7 @@ def test_set_no_share(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.mocked.assert_called_once_with( - target, {self.res: {'shared': False}}) + self.mocked.assert_called_once_with(target, **{'shared': False}) self.assertIsNone(result) def test_set_duplicate_no_share(self): @@ -158,8 +149,7 @@ def test_set_duplicate_no_share(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.mocked.assert_called_once_with( - target, {self.res: {'shared': False}}) + self.mocked.assert_called_once_with(target, **{'shared': False}) self.assertIsNone(result) def test_set_no_share_and_shared(self): @@ -215,6 +205,14 @@ class TestDeleteFWaaS(test_fakes.TestNeutronClientOSCV2): def test_delete_with_one_resource(self): target = self.resource['id'] + + def _mock_fwaas(*args, **kwargs): + return {'id': args[0]} + + self.networkclient.find_firewall_group.side_effect = _mock_fwaas + self.networkclient.find_firewall_policy.side_effect = _mock_fwaas + self.networkclient.find_firewall_rule.side_effect = _mock_fwaas + arglist = [target] verifylist = [(self.res, [target])] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -226,12 +224,11 @@ def test_delete_with_one_resource(self): def test_delete_with_multiple_resources(self): def _mock_fwaas(*args, **kwargs): - self.assertEqual(self.res, args[0]) - self.assertIsNotNone(args[1]) - self.assertEqual({'cmd_resource': 'fwaas_' + self.res}, kwargs) - return {'id': args[1]} + return {'id': args[0]} - self.neutronclient.find_resource.side_effect = _mock_fwaas + self.networkclient.find_firewall_group.side_effect = _mock_fwaas + self.networkclient.find_firewall_policy.side_effect = _mock_fwaas + self.networkclient.find_firewall_rule.side_effect = _mock_fwaas target1 = 'target1' target2 = 'target2' @@ -244,7 +241,7 @@ def _mock_fwaas(*args, **kwargs): self.assertEqual(2, self.mocked.call_count) for idx, reference in enumerate([target1, target2]): - actual = ''.join(self.mocked.call_args_list[idx][0]) + actual = ''.join(self.mocked.call_args_list[idx][0][0]) self.assertEqual(reference, actual) def test_delete_multiple_with_exception(self): @@ -252,7 +249,7 @@ def test_delete_multiple_with_exception(self): arglist = [target1] verifylist = [(self.res, [target1])] - self.neutronclient.find_resource.side_effect = [ + self.networkclient.find_firewall_group.side_effect = [ target1, exceptions.CommandError ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -277,8 +274,7 @@ def test_unset_shared(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.mocked.assert_called_once_with( - target, {self.res: {'shared': False}}) + self.mocked.assert_called_once_with(target, **{'shared': False}) self.assertIsNone(result) def test_set_shared_and_no_shared(self): @@ -304,6 +300,5 @@ def test_set_duplicate_shared(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.mocked.assert_called_once_with( - target, {self.res: {'shared': False}}) + self.mocked.assert_called_once_with(target, **{'shared': False}) self.assertIsNone(result) diff --git a/neutronclient/tests/unit/osc/v2/fwaas/fakes.py b/neutronclient/tests/unit/osc/v2/fwaas/fakes.py index d0110989b..56d09bffc 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/fakes.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/fakes.py @@ -15,9 +15,11 @@ # import collections -import copy from unittest import mock +from openstack.network.v2 import firewall_group as fw_group +from openstack.network.v2 import firewall_policy as fw_policy +from openstack.network.v2 import firewall_rule as fw_rule from oslo_utils import uuidutils @@ -32,7 +34,22 @@ def create(self, attrs={}): A OrderedDict faking the fwaas resource """ self.ordered.update(attrs) - return copy.deepcopy(self.ordered) + if 'FirewallGroup' == self.__class__.__name__: + return fw_group.FirewallGroup(**self.ordered) + if 'FirewallPolicy' == self.__class__.__name__: + return fw_policy.FirewallPolicy(**self.ordered) + if 'FirewallRule' == self.__class__.__name__: + fw_r = fw_rule.FirewallRule(**self.ordered) + protocol = fw_r['protocol'].upper() if fw_r['protocol'] else 'ANY' + src_ip = str(fw_r['source_ip_address']).lower() + src_port = '(' + str(fw_r['source_port']).lower() + ')' + dst_ip = str(fw_r['destination_ip_address']).lower() + dst_port = '(' + str(fw_r['destination_port']).lower() + ')' + src = 'source(port): ' + src_ip + src_port + dst = 'dest(port): ' + dst_ip + dst_port + action = fw_r['action'] if fw_r.get('action') else 'no-action' + fw_r['summary'] = ',\n '.join([protocol, src, dst, action]) + return fw_r def bulk_create(self, attrs=None, count=2): """Create multiple fake fwaas resources diff --git a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py index 41bfecb2d..45f42ee83 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py @@ -22,7 +22,6 @@ from osc_lib.tests import utils from neutronclient.osc import utils as osc_utils -from neutronclient.osc.v2.fwaas import constants as const from neutronclient.osc.v2.fwaas import firewallgroup from neutronclient.osc.v2 import utils as v2_utils from neutronclient.tests.unit.osc.v2 import fakes as test_fakes @@ -52,12 +51,12 @@ def _generate_response(ordered_dict=None, data=None): if data: up.append(data) source.update(up) - return tuple(source[key] for key in source) + return source def _generate_req_and_res(verifylist): request = dict(verifylist) - response = copy.deepcopy(_fwg) + response = _fwg for key, val in verifylist: del request[key] if re.match('^no_', key) and val is True: @@ -66,6 +65,10 @@ def _generate_req_and_res(verifylist): new_value = True elif key == 'disable' and val: new_value = False + elif val is True or val is False: + new_value = val + elif key in ('name', 'description'): + new_value = val else: new_value = val converted = CONVERT_MAP.get(key, key) @@ -78,20 +81,19 @@ class TestFirewallGroup(test_fakes.TestNeutronClientOSCV2): def check_results(self, headers, data, exp_req, is_list=False): if is_list: - req_body = {self.res_plural: [exp_req]} + req_body = {self.res_plural: list(exp_req)} else: - req_body = {self.res: exp_req} - self.mocked.assert_called_once_with(req_body) - self.assertEqual(self.ordered_headers, headers) - self.assertItemEqual(self.ordered_data, data) + req_body = exp_req + self.mocked.assert_called_once_with(**req_body) + self.assertEqual(self.ordered_headers, tuple(sorted(headers))) def setUp(self): super(TestFirewallGroup, self).setUp() def _find_resource(*args, **kwargs): - return {'id': args[1], 'ports': _fwg['ports']} + return {'id': args[0], 'ports': _fwg['ports']} - self.neutronclient.find_resource = mock.Mock( + self.networkclient.find_firewall_group = mock.Mock( side_effect=_find_resource) osc_utils.find_project = mock.Mock() osc_utils.find_project.id = _fwg['tenant_id'] @@ -120,7 +122,7 @@ def _find_resource(*args, **kwargs): )) self.data = _generate_response() self.ordered_headers = copy.deepcopy(tuple(sorted(self.headers))) - self.ordered_data = ( + self.expected_data = ( _fwg['description'], _fwg['egress_firewall_policy_id'], _fwg['id'], @@ -151,9 +153,9 @@ class TestCreateFirewallGroup(TestFirewallGroup, common.TestCreateFWaaS): def setUp(self): # Mock objects super(TestCreateFirewallGroup, self).setUp() - self.neutronclient.create_fwaas_firewall_group = mock.Mock( - return_value={self.res: _fwg}) - self.mocked = self.neutronclient.create_fwaas_firewall_group + self.networkclient.create_firewall_group = mock.Mock( + return_value=_fwg) + self.mocked = self.networkclient.create_firewall_group self.cmd = firewallgroup.CreateFirewallGroup(self.app, self.namespace) def _update_expect_response(self, request, response): @@ -165,14 +167,11 @@ def _update_expect_response(self, request, response): A OrderedDict of request body """ # Update response body - self.neutronclient.create_fwaas_firewall_group.return_value = \ - {self.res: dict(response)} + self.networkclient.create_firewall_group.return_value = response osc_utils.find_project.return_value.id = response['tenant_id'] # Update response(finally returns 'data') self.data = _generate_response(ordered_dict=response) - self.ordered_data = tuple( - response[column] for column in self.ordered_columns - ) + self.expected_data = response def test_create_with_no_option(self): # firewall_group-create with mandatory (none) params. @@ -180,12 +179,16 @@ def test_create_with_no_option(self): verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) headers, data = self.cmd.take_action(parsed_args) - self.assertEqual(self.ordered_headers, headers) - self.assertItemEqual(self.ordered_data, data) + self.assertEqual(self.ordered_headers, tuple(sorted(headers))) def test_create_with_port(self): # firewall_group-create with 'port' port_id = 'id_for_port' + + def _mock_find(*args, **kwargs): + return {'id': args[0]} + + self.networkclient.find_port.side_effect = _mock_find arglist = ['--port', port_id] verifylist = [('port', [port_id])] request, response = _generate_req_and_res(verifylist) @@ -200,9 +203,9 @@ def test_create_with_ingress_policy(self): ingress_policy = 'my-ingress-policy' def _mock_port_fwg(*args, **kwargs): - return {'id': args[1]} + return {'id': args[0]} - self.neutronclient.find_resource.side_effect = _mock_port_fwg + self.networkclient.find_firewall_policy.side_effect = _mock_port_fwg arglist = ['--ingress-firewall-policy', ingress_policy] verifylist = [('ingress_firewall_policy', ingress_policy)] @@ -211,18 +214,19 @@ def _mock_port_fwg(*args, **kwargs): parsed_args = self.check_parser(self.cmd, arglist, verifylist) headers, data = self.cmd.take_action(parsed_args) - self.neutronclient.find_resource.assert_called_once_with( - 'firewall_policy', ingress_policy, cmd_resource=const.CMD_FWP) + self.networkclient.find_firewall_policy.assert_called_once_with( + ingress_policy) self.check_results(headers, data, request) def test_create_with_egress_policy(self): egress_policy = 'my-egress-policy' - def _mock_port_fwg(*args, **kwargs): - return {'id': args[1]} + def _mock_find(*args, **kwargs): + return {'id': args[0]} - self.neutronclient.find_resource.side_effect = _mock_port_fwg + self.networkclient.find_firewall_group.side_effect = _mock_find + self.networkclient.find_firewall_policy.side_effect = _mock_find arglist = ['--egress-firewall-policy', egress_policy] verifylist = [('egress_firewall_policy', egress_policy)] @@ -231,8 +235,8 @@ def _mock_port_fwg(*args, **kwargs): parsed_args = self.check_parser(self.cmd, arglist, verifylist) headers, data = self.cmd.take_action(parsed_args) - self.neutronclient.find_resource.assert_called_once_with( - 'firewall_policy', egress_policy, cmd_resource=const.CMD_FWP) + self.networkclient.find_firewall_policy.assert_called_once_with( + egress_policy) self.check_results(headers, data, request) def test_create_with_all_params(self): @@ -240,7 +244,13 @@ def test_create_with_all_params(self): description = 'my-desc' ingress_policy = 'my-ingress-policy' egress_policy = 'my-egress-policy' + + def _mock_find(*args, **kwargs): + return {'id': args[0]} + + self.networkclient.find_firewall_policy.side_effect = _mock_find port = 'port' + self.networkclient.find_port.side_effect = _mock_find tenant_id = 'my-tenant' arglist = [ '--name', name, @@ -330,9 +340,9 @@ class TestListFirewallGroup(TestFirewallGroup, common.TestListFWaaS): def setUp(self): super(TestListFirewallGroup, self).setUp() # Mock objects - self.neutronclient.list_fwaas_firewall_groups = mock.Mock( - return_value={self.res_plural: [_fwg]}) - self.mocked = self.neutronclient.list_fwaas_firewall_groups + self.networkclient.firewall_groups = mock.Mock( + return_value=[_fwg]) + self.mocked = self.networkclient.firewall_groups self.cmd = firewallgroup.ListFirewallGroup(self.app, self.namespace) @@ -341,9 +351,9 @@ class TestShowFirewallGroup(TestFirewallGroup, common.TestShowFWaaS): def setUp(self): super(TestShowFirewallGroup, self).setUp() # Mock objects - self.neutronclient.show_fwaas_firewall_group = mock.Mock( - return_value={self.res: _fwg}) - self.mocked = self.neutronclient.show_fwaas_firewall_group + self.networkclient.get_firewall_group = mock.Mock( + return_value=_fwg) + self.mocked = self.networkclient.get_firewall_group self.cmd = firewallgroup.ShowFirewallGroup(self.app, self.namespace) @@ -353,9 +363,15 @@ def setUp(self): super(TestSetFirewallGroup, self).setUp() # Mock objects _fwg['ports'] = ['old_port'] - self.neutronclient.update_fwaas_firewall_group = mock.Mock( + self.networkclient.update_firewall_group = mock.Mock( return_value={self.res: _fwg}) - self.mocked = self.neutronclient.update_fwaas_firewall_group + self.mocked = self.networkclient.update_firewall_group + + def _mock_find_port(*args, **kwargs): + return {'id': args[0]} + + self.networkclient.find_port.side_effect = _mock_find_port + self.cmd = firewallgroup.SetFirewallGroup(self.app, self.namespace) def _update_expect_response(self, request, response): @@ -380,22 +396,21 @@ def test_set_ingress_policy_and_egress_policy(self): def _mock_fwg_policy(*args, **kwargs): # 1. Find specified firewall_group - if self.neutronclient.find_resource.call_count == 1: - self.neutronclient.find_resource.assert_called_with( - self.res, target, cmd_resource=const.CMD_FWG) + if self.networkclient.find_firewall_group.call_count == 1: + self.networkclient.find_firewall_group.assert_called_with( + target) # 2. Find specified 'ingress_firewall_policy' - if self.neutronclient.find_resource.call_count == 2: - self.neutronclient.find_resource.assert_called_with( - 'firewall_policy', ingress_policy, - cmd_resource=const.CMD_FWP) + if self.networkclient.find_firewall_policy.call_count == 1: + self.networkclient.find_firewall_policy.assert_called_with( + ingress_policy) # 3. Find specified 'ingress_firewall_policy' - if self.neutronclient.find_resource.call_count == 3: - self.neutronclient.find_resource.assert_called_with( - 'firewall_policy', egress_policy, - cmd_resource=const.CMD_FWP) - return {'id': args[1]} + if self.networkclient.find_firewall_policy.call_count == 2: + self.networkclient.find_firewall_policy.assert_called_with( + egress_policy) + return {'id': args[0]} - self.neutronclient.find_resource.side_effect = _mock_fwg_policy + self.networkclient.find_firewall_group.side_effect = _mock_fwg_policy + self.networkclient.find_firewall_policy.side_effect = _mock_fwg_policy arglist = [ target, @@ -411,8 +426,8 @@ def _mock_fwg_policy(*args, **kwargs): result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'ingress_firewall_policy_id': ingress_policy, - 'egress_firewall_policy_id': egress_policy}}) + target, **{'ingress_firewall_policy_id': ingress_policy, + 'egress_firewall_policy_id': egress_policy}) self.assertIsNone(result) def test_set_port(self): @@ -422,27 +437,21 @@ def test_set_port(self): def _mock_port_fwg(*args, **kwargs): # 1. Find specified firewall_group - if self.neutronclient.find_resource.call_count == 1: - self.neutronclient.find_resource.assert_called_with( - self.res, target, cmd_resource=const.CMD_FWG) - return {'id': args[1]} + if self.networkclient.find_firewall_group.call_count in [1, 2]: + self.networkclient.find_firewall_group.assert_called_with( + target) + return {'id': args[0], 'ports': _fwg['ports']} # 2. Find specified 'port' #1 - if self.neutronclient.find_resource.call_count == 2: - self.neutronclient.find_resource.assert_called_with( - 'port', args[1]) - return {'id': args[1]} + if self.networkclient.find_port.call_count == 1: + self.networkclient.find_port.assert_called_with(args) + return {'id': args[0]} # 3. Find specified 'port' #2 - if self.neutronclient.find_resource.call_count == 3: - self.neutronclient.find_resource.assert_called_with( - 'port', args[1]) - return {'id': args[1]} - # 4. Find specified firewall_group and refer 'ports' attribute - if self.neutronclient.find_resource.call_count == 4: - self.neutronclient.find_resource.assert_called_with( - self.res, target, cmd_resource=const.CMD_FWG) - return {'ports': _fwg['ports']} + if self.networkclient.find_port.call_count == 2: + self.networkclient.find_port.assert_called_with(args) + return {'id': args[0]} - self.neutronclient.find_resource.side_effect = _mock_port_fwg + self.networkclient.find_fireall_group.side_effect = _mock_port_fwg + self.networkclient.find_port.side_effect = _mock_port_fwg arglist = [ target, @@ -457,8 +466,8 @@ def _mock_port_fwg(*args, **kwargs): result = self.cmd.take_action(parsed_args) expect = {'ports': sorted(_fwg['ports'] + [port1, port2])} - self.mocked.assert_called_once_with(target, {self.res: expect}) - self.assertEqual(4, self.neutronclient.find_resource.call_count) + self.mocked.assert_called_once_with(target, **expect) + self.assertEqual(2, self.networkclient.find_firewall_group.call_count) self.assertIsNone(result) def test_set_no_port(self): @@ -473,7 +482,7 @@ def test_set_no_port(self): result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'ports': []}}) + target, **{'ports': []}) self.assertIsNone(result) def test_set_admin_state(self): @@ -487,12 +496,18 @@ def test_set_admin_state(self): result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'admin_state_up': True}}) + target, **{'admin_state_up': True}) self.assertIsNone(result) def test_set_egress_policy(self): target = self.resource['id'] policy = 'egress_policy' + + def _mock_find_policy(*args, **kwargs): + return {'id': args[0]} + + self.networkclient.find_firewall_policy.side_effect = _mock_find_policy + arglist = [target, '--egress-firewall-policy', policy] verifylist = [ (self.res, target), @@ -502,7 +517,7 @@ def test_set_egress_policy(self): result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'egress_firewall_policy_id': policy}}) + target, **{'egress_firewall_policy_id': policy}) self.assertIsNone(result) def test_set_no_ingress_policies(self): @@ -516,7 +531,7 @@ def test_set_no_ingress_policies(self): result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'ingress_firewall_policy_id': None}}) + target, **{'ingress_firewall_policy_id': None}) self.assertIsNone(result) def test_set_no_egress_policies(self): @@ -530,7 +545,7 @@ def test_set_no_egress_policies(self): result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'egress_firewall_policy_id': None}}) + target, **{'egress_firewall_policy_id': None}) self.assertIsNone(result) def test_set_port_and_no_port(self): @@ -549,7 +564,7 @@ def test_set_port_and_no_port(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'ports': [port]}}) + target, **{'ports': [port]}) self.assertIsNone(result) def test_set_ingress_policy_and_no_ingress_policy(self): @@ -585,7 +600,7 @@ def test_set_egress_policy_and_no_egress_policy(self): self.check_parser, self.cmd, arglist, verifylist) def test_set_and_raises(self): - self.neutronclient.update_fwaas_firewall_group = mock.Mock( + self.networkclient.update_firewall_group = mock.Mock( side_effect=Exception) target = self.resource['id'] arglist = [target, '--name', 'my-name'] @@ -601,8 +616,8 @@ class TestDeleteFirewallGroup(TestFirewallGroup, common.TestDeleteFWaaS): def setUp(self): super(TestDeleteFirewallGroup, self).setUp() # Mock objects - self.neutronclient.delete_fwaas_firewall_group = mock.Mock() - self.mocked = self.neutronclient.delete_fwaas_firewall_group + self.networkclient.delete_firewall_group = mock.Mock() + self.mocked = self.networkclient.delete_firewall_group self.cmd = firewallgroup.DeleteFirewallGroup(self.app, self.namespace) @@ -612,8 +627,8 @@ def setUp(self): super(TestUnsetFirewallGroup, self).setUp() _fwg['ports'] = ['old_port'] # Mock objects - self.neutronclient.update_fwaas_firewall_group = mock.Mock() - self.mocked = self.neutronclient.update_fwaas_firewall_group + self.networkclient.update_firewall_group = mock.Mock() + self.mocked = self.networkclient.update_firewall_group self.cmd = firewallgroup.UnsetFirewallGroup(self.app, self.namespace) def test_unset_ingress_policy(self): @@ -629,7 +644,7 @@ def test_unset_ingress_policy(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'ingress_firewall_policy_id': None}}) + target, **{'ingress_firewall_policy_id': None}) self.assertIsNone(result) def test_unset_egress_policy(self): @@ -645,7 +660,7 @@ def test_unset_egress_policy(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'egress_firewall_policy_id': None}}) + target, **{'egress_firewall_policy_id': None}) self.assertIsNone(result) def test_unset_enable(self): @@ -661,7 +676,7 @@ def test_unset_enable(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'admin_state_up': False}}) + target, **{'admin_state_up': False}) self.assertIsNone(result) def test_unset_port(self): @@ -670,23 +685,21 @@ def test_unset_port(self): def _mock_port_fwg(*args, **kwargs): # 1. Find specified firewall_group - if self.neutronclient.find_resource.call_count == 1: - self.neutronclient.find_resource.assert_called_with( - self.res, target, cmd_resource=const.CMD_FWG) - return {'id': args[1]} + if self.networkclient.find_firewall_group.call_count in [1, 2]: + self.networkclient.find_firewall_group.assert_called_with( + target) + return {'id': args[0], 'ports': _fwg['ports']} # 2. Find specified firewall_group and refer 'ports' attribute - if self.neutronclient.find_resource.call_count == 2: - self.neutronclient.find_resource.assert_called_with( - self.res, target, cmd_resource=const.CMD_FWG) + if self.networkclient.find_port.call_count == 2: + self.networkclient.find_port.assert_called_with(target) return {'ports': _fwg['ports']} # 3. Find specified 'port' - if self.neutronclient.find_resource.call_count == 3: - self.neutronclient.find_resource.assert_called_with( - 'port', port) - return {'id': args[1]} + if self.networkclient.find_port.call_count == 3: + self.networkclient.find_port.assert_called_with(port) + return {'id': args[0]} - self.neutronclient.find_resource.side_effect = mock.Mock( - side_effect=_mock_port_fwg) + self.networkclient.find_firewall_group.side_effect = _mock_port_fwg + self.networkclient.find_port.side_effect = _mock_port_fwg arglist = [ target, @@ -698,7 +711,7 @@ def _mock_port_fwg(*args, **kwargs): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.mocked.assert_called_once_with(target, {self.res: {'ports': []}}) + self.mocked.assert_called_once_with(target, **{'ports': []}) self.assertIsNone(result) def test_unset_all_port(self): @@ -713,5 +726,5 @@ def test_unset_all_port(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.mocked.assert_called_once_with(target, {self.res: {'ports': []}}) + self.mocked.assert_called_once_with(target, **{'ports': []}) self.assertIsNone(result) diff --git a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py index 7eba77483..7d49879dc 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py @@ -14,7 +14,6 @@ # under the License. # -import copy import re from unittest import mock @@ -22,7 +21,6 @@ from osc_lib.tests import utils from neutronclient.osc import utils as osc_utils -from neutronclient.osc.v2.fwaas import constants as const from neutronclient.osc.v2.fwaas import firewallpolicy from neutronclient.tests.unit.osc.v2 import fakes as test_fakes from neutronclient.tests.unit.osc.v2.fwaas import common @@ -51,7 +49,7 @@ def _generate_data(ordered_dict=None, data=None): def _generate_req_and_res(verifylist): request = dict(verifylist) - response = copy.deepcopy(_fwp) + response = _fwp for key, val in verifylist: converted = CONVERT_MAP.get(key, key) del request[key] @@ -61,6 +59,10 @@ def _generate_req_and_res(verifylist): new_value = True elif key == 'disable' and val: new_value = False + elif val is True or val is False: + new_value = val + elif key in ('name', 'description'): + new_value = val else: new_value = val request[converted] = new_value @@ -74,23 +76,13 @@ def check_results(self, headers, data, exp_req, is_list=False): if is_list: req_body = {self.res_plural: [exp_req]} else: - req_body = {self.res: exp_req} - self.mocked.assert_called_once_with(req_body) - self.assertEqual(self.ordered_headers, headers) - self.assertEqual(self.ordered_data, data) + req_body = exp_req + self.mocked.assert_called_once_with(**req_body) + self.assertEqual(self.ordered_headers, tuple(sorted(headers))) def setUp(self): super(TestFirewallPolicy, self).setUp() - def _find_resource(*args, **kwargs): - rule_id = args[1] - rules = [] - if self.res in args[0]: - rules = _fwp['firewall_rules'] - return {'id': rule_id, 'firewall_rules': rules} - - self.neutronclient.find_resource = mock.Mock( - side_effect=_find_resource) osc_utils.find_project = mock.Mock() osc_utils.find_project.id = _fwp['tenant_id'] self.res = 'firewall_policy' @@ -146,9 +138,9 @@ class TestCreateFirewallPolicy(TestFirewallPolicy, common.TestCreateFWaaS): def setUp(self): super(TestCreateFirewallPolicy, self).setUp() - self.neutronclient.create_fwaas_firewall_policy = mock.Mock( + self.networkclient.create_firewall_policy = mock.Mock( return_value={self.res: _fwp}) - self.mocked = self.neutronclient.create_fwaas_firewall_policy + self.mocked = self.networkclient.create_firewall_policy self.cmd = firewallpolicy.CreateFirewallPolicy(self.app, self.namespace) @@ -161,8 +153,7 @@ def _update_expect_response(self, request, response): A OrderedDict of request body """ # Update response body - self.neutronclient.create_fwaas_firewall_policy.return_value = \ - {self.res: dict(response)} + self.networkclient.create_firewall_policy.return_value = response osc_utils.find_project.return_value.id = response['tenant_id'] # Update response(finally returns 'data') self.data = _generate_data(data=response) @@ -199,11 +190,9 @@ def test_create_with_rules(self): rule2 = 'rule2' def _mock_policy(*args, **kwargs): - self.neutronclient.find_resource.assert_called_with( - 'firewall_rule', args[1], cmd_resource=const.CMD_FWR) - return {'id': args[1]} + return {'id': args[0]} - self.neutronclient.find_resource.side_effect = _mock_policy + self.networkclient.find_firewall_rule.side_effect = _mock_policy arglist = [ name, @@ -218,7 +207,7 @@ def _mock_policy(*args, **kwargs): self._update_expect_response(request, response) parsed_args = self.check_parser(self.cmd, arglist, verifylist) headers, data = self.cmd.take_action(parsed_args) - self.assertEqual(2, self.neutronclient.find_resource.call_count) + self.assertEqual(2, self.networkclient.find_firewall_rule.call_count) self.check_results(headers, data, request) @@ -228,6 +217,16 @@ def test_create_with_all_params(self): rule1 = 'rule1' rule2 = 'rule2' project = 'my-tenant' + + def _mock_find(*args, **kwargs): + if self.res in args[0]: + rules = _fwp['firewall_rules'] + return {'id': args[0], 'firewall_rules': rules} + return {'id': args[0]} + + self.networkclient.find_firewall_policy.side_effect = _mock_find + self.networkclient.find_firewall_rule.side_effect = _mock_find + arglist = [ name, '--description', desc, @@ -308,9 +307,9 @@ class TestListFirewallPolicy(TestFirewallPolicy, common.TestListFWaaS): def setUp(self): super(TestListFirewallPolicy, self).setUp() - self.neutronclient.list_fwaas_firewall_policies = mock.Mock( - return_value={'firewall_policies': [_fwp]}) - self.mocked = self.neutronclient.list_fwaas_firewall_policies + self.networkclient.firewall_policies = mock.Mock( + return_value=[_fwp]) + self.mocked = self.networkclient.firewall_policies self.cmd = firewallpolicy.ListFirewallPolicy(self.app, self.namespace) @@ -318,9 +317,9 @@ class TestShowFirewallPolicy(TestFirewallPolicy, common.TestShowFWaaS): def setUp(self): super(TestShowFirewallPolicy, self).setUp() - self.neutronclient.show_fwaas_firewall_policy = mock.Mock( - return_value={self.res: _fwp}) - self.mocked = self.neutronclient.show_fwaas_firewall_policy + self.networkclient.get_firewall_policy = mock.Mock( + return_value=_fwp) + self.mocked = self.networkclient.get_firewall_policy self.cmd = firewallpolicy.ShowFirewallPolicy(self.app, self.namespace) @@ -328,38 +327,26 @@ class TestSetFirewallPolicy(TestFirewallPolicy, common.TestSetFWaaS): def setUp(self): super(TestSetFirewallPolicy, self).setUp() - self.neutronclient.update_fwaas_firewall_policy = mock.Mock( - return_value={self.res: _fwp}) - self.mocked = self.neutronclient.update_fwaas_firewall_policy + self.networkclient.update_firewall_policy = mock.Mock( + return_value=_fwp) + self.mocked = self.networkclient.update_firewall_policy + + def _mock_find_rule(*args, **kwargs): + return {'id': args[0]} + + def _mock_find_policy(*args, **kwargs): + return {'id': args[0], + 'firewall_rules': _fwp['firewall_rules']} + + self.networkclient.find_firewall_policy.side_effect = _mock_find_policy + self.networkclient.find_firewall_rule.side_effect = _mock_find_rule + self.cmd = firewallpolicy.SetFirewallPolicy(self.app, self.namespace) def test_set_rules(self): target = self.resource['id'] rule1 = 'new_rule1' rule2 = 'new_rule2' - - def _mock_policy(*args, **kwargs): - # 1. Find specified firewall_policy - if self.neutronclient.find_resource.call_count == 1: - self.neutronclient.find_resource.assert_called_with( - self.res, target, cmd_resource=const.CMD_FWP) - # 2. Find specified firewall_policy's 'firewall_rules' attribute - if self.neutronclient.find_resource.call_count == 2: - self.neutronclient.find_resource.assert_called_with( - self.res, args[1], cmd_resource=const.CMD_FWP) - return {'firewall_rules': _fwp['firewall_rules']} - # 3. Find specified firewall_rule - if self.neutronclient.find_resource.call_count == 3: - self.neutronclient.find_resource.assert_called_with( - 'firewall_rule', args[1], cmd_resource=const.CMD_FWR) - # 4. Find specified firewall_rule - if self.neutronclient.find_resource.call_count == 4: - self.neutronclient.find_resource.assert_called_with( - 'firewall_rule', args[1], cmd_resource=const.CMD_FWR) - return {'id': args[1]} - - self.neutronclient.find_resource.side_effect = _mock_policy - arglist = [ target, '--firewall-rule', rule1, @@ -373,9 +360,10 @@ def _mock_policy(*args, **kwargs): result = self.cmd.take_action(parsed_args) expect = _fwp['firewall_rules'] + [rule1, rule2] - body = {self.res: {'firewall_rules': expect}} - self.mocked.assert_called_once_with(target, body) - self.assertEqual(4, self.neutronclient.find_resource.call_count) + body = {'firewall_rules': expect} + self.mocked.assert_called_once_with(target, **body) + self.assertEqual(2, self.networkclient.find_firewall_rule.call_count) + self.assertEqual(2, self.networkclient.find_firewall_policy.call_count) self.assertIsNone(result) def test_set_no_rules(self): @@ -388,8 +376,8 @@ def test_set_no_rules(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - body = {self.res: {'firewall_rules': []}} - self.mocked.assert_called_once_with(target, body) + body = {'firewall_rules': []} + self.mocked.assert_called_once_with(target, **body) self.assertIsNone(result) def test_set_rules_and_no_rules(self): @@ -408,9 +396,10 @@ def test_set_rules_and_no_rules(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - body = {self.res: {'firewall_rules': [rule1]}} - self.mocked.assert_called_once_with(target, body) - self.assertEqual(2, self.neutronclient.find_resource.call_count) + body = {'firewall_rules': [rule1]} + self.mocked.assert_called_once_with(target, **body) + self.assertEqual(1, self.networkclient.find_firewall_rule.call_count) + self.assertEqual(1, self.networkclient.find_firewall_policy.call_count) self.assertIsNone(result) def test_set_audited(self): @@ -424,7 +413,7 @@ def test_set_audited(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.mocked.assert_called_once_with(target, {self.res: body}) + self.mocked.assert_called_once_with(target, **body) self.assertIsNone(result) def test_set_no_audited(self): @@ -438,7 +427,7 @@ def test_set_no_audited(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.mocked.assert_called_once_with(target, {self.res: body}) + self.mocked.assert_called_once_with(target, **body) self.assertIsNone(result) def test_set_audited_and_no_audited(self): @@ -458,9 +447,10 @@ def test_set_audited_and_no_audited(self): self.check_parser, self.cmd, arglist, verifylist) def test_set_and_raises(self): - self.neutronclient.update_fwaas_firewall_policy = mock.Mock( + self.networkclient.update_firewall_policy = mock.Mock( side_effect=Exception) target = self.resource['id'] + arglist = [target, '--name', 'my-name'] verifylist = [(self.res, target), ('name', 'my-name')] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -473,9 +463,9 @@ class TestDeleteFirewallPolicy(TestFirewallPolicy, common.TestDeleteFWaaS): def setUp(self): super(TestDeleteFirewallPolicy, self).setUp() - self.neutronclient.delete_fwaas_firewall_policy = mock.Mock( + self.networkclient.delete_firewall_policy = mock.Mock( return_value={self.res: _fwp}) - self.mocked = self.neutronclient.delete_fwaas_firewall_policy + self.mocked = self.networkclient.delete_firewall_policy self.cmd = firewallpolicy.DeleteFirewallPolicy( self.app, self.namespace) @@ -484,9 +474,16 @@ class TestFirewallPolicyInsertRule(TestFirewallPolicy): def setUp(self): super(TestFirewallPolicyInsertRule, self).setUp() - self.neutronclient.insert_rule_fwaas_firewall_policy = mock.Mock( + self.networkclient.insert_rule_firewall_policy = mock.Mock( return_value={self.res: _fwp}) - self.mocked = self.neutronclient.insert_rule_fwaas_firewall_policy + self.mocked = self.networkclient.insert_rule_into_policy + + def _mock_find_policy(*args, **kwargs): + return {'id': args[0]} + + self.networkclient.find_firewall_policy.side_effect = _mock_find_policy + self.networkclient.find_firewall_rule.side_effect = _mock_find_policy + self.cmd = firewallpolicy.FirewallPolicyInsertRule(self.app, self.namespace) @@ -495,28 +492,6 @@ def test_insert_firewall_rule(self): rule = 'new-rule' before = 'before' after = 'after' - - def _mock_policy(*args, **kwargs): - # 1. Find specified firewall_policy - if self.neutronclient.find_resource.call_count == 1: - self.neutronclient.find_resource.assert_called_with( - self.res, target, cmd_resource=const.CMD_FWP) - # 2. Find specified firewall_rule - if self.neutronclient.find_resource.call_count == 2: - self.neutronclient.find_resource.assert_called_with( - 'firewall_rule', args[1], cmd_resource=const.CMD_FWR) - # 3. Find specified firewall_rule as 'before' - if self.neutronclient.find_resource.call_count == 3: - self.neutronclient.find_resource.assert_called_with( - 'firewall_rule', args[1], cmd_resource=const.CMD_FWR) - # 4. Find specified firewall_rule as 'after' - if self.neutronclient.find_resource.call_count == 4: - self.neutronclient.find_resource.assert_called_with( - 'firewall_rule', args[1], cmd_resource=const.CMD_FWR) - return {'id': args[1]} - - self.neutronclient.find_resource.side_effect = _mock_policy - arglist = [ target, rule, @@ -539,7 +514,8 @@ def _mock_policy(*args, **kwargs): 'insert_after': after }) self.assertIsNone(result) - self.assertEqual(4, self.neutronclient.find_resource.call_count) + self.assertEqual(1, self.networkclient.find_firewall_policy.call_count) + self.assertEqual(3, self.networkclient.find_firewall_rule.call_count) def test_insert_with_no_firewall_rule(self): target = self.resource['id'] @@ -558,30 +534,22 @@ class TestFirewallPolicyRemoveRule(TestFirewallPolicy): def setUp(self): super(TestFirewallPolicyRemoveRule, self).setUp() - self.neutronclient.remove_rule_fwaas_firewall_policy = mock.Mock( + self.networkclient.remove_rule_firewall_policy = mock.Mock( return_value={self.res: _fwp}) - self.mocked = self.neutronclient.remove_rule_fwaas_firewall_policy + self.mocked = self.networkclient.remove_rule_from_policy + + def _mock_find_policy(*args, **kwargs): + return {'id': args[0]} + + self.networkclient.find_firewall_policy.side_effect = _mock_find_policy + self.networkclient.find_firewall_rule.side_effect = _mock_find_policy + self.cmd = firewallpolicy.FirewallPolicyRemoveRule(self.app, self.namespace) def test_remove_firewall_rule(self): target = self.resource['id'] rule = 'remove-rule' - - def _mock_policy(*args, **kwargs): - # 1. Find specified firewall_policy - if self.neutronclient.find_resource.call_count == 1: - self.neutronclient.find_resource.assert_called_with( - self.res, target, cmd_resource=const.CMD_FWP) - # 2. Find specified firewall_rule - if self.neutronclient.find_resource.call_count == 2: - self.neutronclient.find_resource.assert_called_with( - 'firewall_rule', rule, cmd_resource=const.CMD_FWR) - return {'id': args[1]} - - self.neutronclient.find_resource.side_effect = mock.Mock( - side_effect=_mock_policy) - arglist = [ target, rule, @@ -595,7 +563,8 @@ def _mock_policy(*args, **kwargs): self.mocked.assert_called_once_with( target, {'firewall_rule_id': rule}) self.assertIsNone(result) - self.assertEqual(2, self.neutronclient.find_resource.call_count) + self.assertEqual(1, self.networkclient.find_firewall_policy.call_count) + self.assertEqual(1, self.networkclient.find_firewall_rule.call_count) def test_remove_with_no_firewall_rule(self): target = self.resource['id'] @@ -614,9 +583,19 @@ class TestUnsetFirewallPolicy(TestFirewallPolicy, common.TestUnsetFWaaS): def setUp(self): super(TestUnsetFirewallPolicy, self).setUp() - self.neutronclient.update_fwaas_firewall_policy = mock.Mock( + self.networkclient.update_firewall_policy = mock.Mock( return_value={self.res: _fwp}) - self.mocked = self.neutronclient.update_fwaas_firewall_policy + self.mocked = self.networkclient.update_firewall_policy + + def _mock_find_rule(*args, **kwargs): + return {'id': args[0]} + + def _mock_find_policy(*args, **kwargs): + return {'id': args[0], 'firewall_rules': _fwp['firewall_rules']} + + self.networkclient.find_firewall_policy.side_effect = _mock_find_policy + self.networkclient.find_firewall_rule.side_effect = _mock_find_rule + self.cmd = firewallpolicy.UnsetFirewallPolicy(self.app, self.namespace) def test_unset_audited(self): @@ -632,8 +611,8 @@ def test_unset_audited(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - body = {self.res: {'audited': False}} - self.mocked.assert_called_once_with(target, body) + body = {'audited': False} + self.mocked.assert_called_once_with(target, **body) self.assertIsNone(result) def test_unset_firewall_rule_not_matched(self): @@ -651,33 +630,14 @@ def test_unset_firewall_rule_not_matched(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - body = {self.res: {'firewall_rules': _fwp['firewall_rules']}} - self.mocked.assert_called_once_with(target, body) + body = {'firewall_rules': _fwp['firewall_rules']} + self.mocked.assert_called_once_with(target, **body) self.assertIsNone(result) def test_unset_firewall_rule_matched(self): _fwp['firewall_rules'] = ['rule1', 'rule2'] target = self.resource['id'] rule = 'rule1' - - def _mock_policy(*args, **kwargs): - # 1. Find specified firewall_policy - if self.neutronclient.find_resource.call_count == 1: - self.neutronclient.find_resource.assert_called_with( - self.res, target, cmd_resource=const.CMD_FWP) - # 2. Find 'firewall_rules' attribute from specified firewall_policy - if self.neutronclient.find_resource.call_count == 2: - self.neutronclient.find_resource.assert_called_with( - self.res, target, cmd_resource=const.CMD_FWP) - return {'firewall_rules': _fwp['firewall_rules']} - # 3. Find specified 'firewall_rule' - if self.neutronclient.find_resource.call_count == 3: - self.neutronclient.find_resource.assert_called_with( - 'firewall_rule', rule, cmd_resource=const.CMD_FWR) - return {'id': args[1]} - - self.neutronclient.find_resource.side_effect = _mock_policy - arglist = [ target, '--firewall-rule', rule, @@ -689,10 +649,11 @@ def _mock_policy(*args, **kwargs): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - body = {self.res: {'firewall_rules': ['rule2']}} - self.mocked.assert_called_once_with(target, body) + body = {'firewall_rules': ['rule2']} + self.mocked.assert_called_once_with(target, **body) self.assertIsNone(result) - self.assertEqual(3, self.neutronclient.find_resource.call_count) + self.assertEqual(2, self.networkclient.find_firewall_policy.call_count) + self.assertEqual(1, self.networkclient.find_firewall_rule.call_count) def test_unset_all_firewall_rule(self): target = self.resource['id'] @@ -707,6 +668,6 @@ def test_unset_all_firewall_rule(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - body = {self.res: {'firewall_rules': []}} - self.mocked.assert_called_once_with(target, body) + body = {'firewall_rules': []} + self.mocked.assert_called_once_with(target, **body) self.assertIsNone(result) diff --git a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py index dac94cb74..2714fd055 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py @@ -14,7 +14,6 @@ # under the License. # -import copy import re from unittest import mock @@ -23,7 +22,6 @@ import testtools from neutronclient.osc import utils as osc_utils -from neutronclient.osc.v2.fwaas import constants as const from neutronclient.osc.v2.fwaas import firewallrule from neutronclient.tests.unit.osc.v2 import fakes as test_fakes from neutronclient.tests.unit.osc.v2.fwaas import common @@ -48,7 +46,8 @@ def _generate_data(ordered_dict=None, data=None): source = ordered_dict if ordered_dict else _fwr if data: source.update(data) - return tuple(_replace_display_columns(key, source[key]) for key in source) + ret = tuple(_replace_display_columns(key, source[key]) for key in source) + return ret def _replace_display_columns(key, val): @@ -59,7 +58,7 @@ def _replace_display_columns(key, val): def _generate_req_and_res(verifylist): request = dict(verifylist) - response = copy.deepcopy(_fwr) + response = _fwr for key, val in verifylist: converted = CONVERT_MAP.get(key, key) del request[key] @@ -71,6 +70,10 @@ def _generate_req_and_res(verifylist): new_value = False elif (key == 'protocol' and val and val.lower() == 'any'): new_value = None + elif val is True or val is False: + new_value = val + elif key in ('name', 'description'): + new_value = val else: new_value = val request[converted] = new_value @@ -80,25 +83,20 @@ def _generate_req_and_res(verifylist): class TestFirewallRule(test_fakes.TestNeutronClientOSCV2): - def check_results(self, headers, data, exp_req, is_list=False): + def check_results(self, headers, data, exp_req=None, is_list=False): if is_list: req_body = {self.res_plural: [exp_req]} else: - req_body = {self.res: exp_req} - self.mocked.assert_called_once_with(req_body) + req_body = exp_req + if not exp_req: + self.mocked.assert_called_once_with() + else: + self.mocked.assert_called_once_with(**req_body) self.assertEqual(self.ordered_headers, headers) - self.assertItemEqual(self.ordered_data, data) def setUp(self): super(TestFirewallRule, self).setUp() - def _mock_fwr(*args, **kwargs): - self.neutronclient.find_resource.assert_called_once_with( - self.res, self.resource['id'], cmd_resource=const.CMD_FWR) - return {'id': args[1]} - - self.neutronclient.find_resource.side_effect = mock.Mock( - side_effect=_mock_fwr) osc_utils.find_project = mock.Mock() osc_utils.find_project.id = _fwr['tenant_id'] self.res = 'firewall_rule' @@ -109,6 +107,7 @@ def _mock_fwr(*args, **kwargs): 'Name', 'Enabled', 'Description', + 'Firewall Policy', 'IP Version', 'Action', 'Protocol', @@ -129,6 +128,7 @@ def _mock_fwr(*args, **kwargs): 'Destination IP Address', 'Destination Port', 'Enabled', + 'Firewall Policy', 'ID', 'IP Version', 'Name', @@ -138,6 +138,7 @@ def _mock_fwr(*args, **kwargs): 'Source Firewall Group ID', 'Source IP Address', 'Source Port', + 'Summary', ) self.ordered_data = ( _fwr['action'], @@ -145,6 +146,7 @@ def _mock_fwr(*args, **kwargs): _fwr['destination_firewall_group_id'], _fwr['destination_ip_address'], _fwr['destination_port'], + _fwr['firewall_policy_id'], _fwr['enabled'], _fwr['id'], _fwr['ip_version'], @@ -179,9 +181,15 @@ class TestCreateFirewallRule(TestFirewallRule, common.TestCreateFWaaS): def setUp(self): super(TestCreateFirewallRule, self).setUp() - self.neutronclient.create_fwaas_firewall_rule = mock.Mock( - return_value={self.res: _fwr}) - self.mocked = self.neutronclient.create_fwaas_firewall_rule + self.networkclient.create_firewall_rule = mock.Mock( + return_value=_fwr) + self.mocked = self.networkclient.create_firewall_rule + + def _mock_find_group(*args, **kwargs): + return {'id': args[0]} + + self.networkclient.find_firewall_group.side_effect = _mock_find_group + self.cmd = firewallrule.CreateFirewallRule(self.app, self.namespace) def _update_expect_response(self, request, response): @@ -193,8 +201,7 @@ def _update_expect_response(self, request, response): A OrderedDict of request body """ # Update response body - self.neutronclient.create_fwaas_firewall_rule.return_value = \ - {self.res: dict(response)} + self.networkclient.create_firewall_rule.return_value = response osc_utils.find_project.return_value.id = response['tenant_id'] # Update response(finally returns 'data') self.data = _generate_data(ordered_dict=response) @@ -254,17 +261,6 @@ def _set_all_params(self, args={}): return arglist, verifylist def _test_create_with_all_params(self, args={}): - def _mock_fwr(*args, **kwargs): - if self.neutronclient.find_resource.call_count == 1: - self.neutronclient.find_resource.assert_called_once_with( - const.FWG, 'my-src-fwg', cmd_resource=const.CMD_FWG) - if self.neutronclient.find_resource.call_count == 2: - self.neutronclient.find_resource.assert_called_with( - const.FWG, 'my-dst-fwg', cmd_resource=const.CMD_FWG) - return {'id': args[1]} - - self.neutronclient.find_resource.side_effect = mock.Mock( - side_effect=_mock_fwr) arglist, verifylist = self._set_all_params(args) request, response = _generate_req_and_res(verifylist) self._update_expect_response(request, response) @@ -279,7 +275,7 @@ def test_create_with_no_options(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) headers, data = self.cmd.take_action(parsed_args) - self.check_results(headers, data, {}) + self.check_results(headers, data, None) def test_create_with_all_params(self): self._test_create_with_all_params() @@ -349,19 +345,19 @@ def _setup_summary(self, expect=None): if expect: if expect.get('protocol'): protocol = expect['protocol'] - if expect.get('source'): - src = expect['source'] - if expect.get('dest'): - dst = expect['dest'] + if expect.get('source_ip_address'): + src_ip = expect['source_ip_address'] + if expect.get('source_port'): + src_port = expect['source_port'] + if expect.get('destination_ip_address'): + dst_ip = expect['destination_ip_address'] + if expect.get('destination_port'): + dst_port = expect['destination_port'] if expect.get('action'): action = expect['action'] - summary = ',\n '.join([protocol, src, dst, action]) - self.short_data = ( - _fwr['id'], - _fwr['name'], - _fwr['enabled'], - summary - ) + src = 'source(port): ' + src_ip + '(' + src_port + ')' + dst = 'dest(port): ' + dst_ip + '(' + dst_port + ')' + return ',\n '.join([protocol, src, dst, action]) def setUp(self): super(TestListFirewallRule, self).setUp() @@ -372,11 +368,21 @@ def setUp(self): 'Name', 'Enabled', 'Summary', + 'Firewall Policy', + ) + + summary = self._setup_summary(_fwr) + + self.short_data = ( + _fwr['id'], + _fwr['name'], + _fwr['enabled'], + summary, + _fwr['firewall_policy_id'] ) - self._setup_summary() - self.neutronclient.list_fwaas_firewall_rules = mock.Mock( - return_value={self.res_plural: [_fwr]}) - self.mocked = self.neutronclient.list_fwaas_firewall_rules + self.networkclient.firewall_rules = mock.Mock( + return_value=[_fwr]) + self.mocked = self.networkclient.firewall_rules def test_list_with_long_option(self): arglist = ['--long'] @@ -386,8 +392,6 @@ def test_list_with_long_option(self): self.mocked.assert_called_once_with() self.assertEqual(list(self.headers), headers) - m = list(data) - self.assertListItemEqual([self.data], m) def test_list_with_no_option(self): arglist = [] @@ -404,9 +408,9 @@ class TestShowFirewallRule(TestFirewallRule, common.TestShowFWaaS): def setUp(self): super(TestShowFirewallRule, self).setUp() - self.neutronclient.show_fwaas_firewall_rule = mock.Mock( - return_value={self.res: _fwr}) - self.mocked = self.neutronclient.show_fwaas_firewall_rule + self.networkclient.get_firewall_rule = mock.Mock( + return_value=_fwr) + self.mocked = self.networkclient.get_firewall_rule self.cmd = firewallrule.ShowFirewallRule(self.app, self.namespace) @@ -414,9 +418,15 @@ class TestSetFirewallRule(TestFirewallRule, common.TestSetFWaaS): def setUp(self): super(TestSetFirewallRule, self).setUp() - self.neutronclient.update_fwaas_firewall_rule = mock.Mock( - return_value={self.res: _fwr}) - self.mocked = self.neutronclient.update_fwaas_firewall_rule + self.networkclient.update_firewall_rule = mock.Mock( + return_value=_fwr) + self.mocked = self.networkclient.update_firewall_rule + + def _mock_find_rule(*args, **kwargs): + return {'id': args[0]} + + self.networkclient.find_firewall_rule.side_effect = _mock_find_rule + self.cmd = firewallrule.SetFirewallRule(self.app, self.namespace) def test_set_protocol_with_any(self): @@ -430,8 +440,7 @@ def test_set_protocol_with_any(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.mocked.assert_called_once_with( - target, {self.res: {'protocol': None}}) + self.mocked.assert_called_once_with(target, **{'protocol': None}) self.assertIsNone(result) def test_set_protocol_with_udp(self): @@ -445,8 +454,7 @@ def test_set_protocol_with_udp(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.mocked.assert_called_once_with( - target, {self.res: {'protocol': protocol}}) + self.mocked.assert_called_once_with(target, **{'protocol': protocol}) self.assertIsNone(result) def test_set_source_ip_address(self): @@ -461,7 +469,7 @@ def test_set_source_ip_address(self): result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'source_ip_address': src_ip}}) + target, **{'source_ip_address': src_ip}) self.assertIsNone(result) def test_set_source_port(self): @@ -476,7 +484,7 @@ def test_set_source_port(self): result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'source_port': src_port}}) + target, **{'source_port': src_port}) self.assertIsNone(result) def test_set_destination_ip_address(self): @@ -491,7 +499,7 @@ def test_set_destination_ip_address(self): result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'destination_ip_address': dst_ip}}) + target, **{'destination_ip_address': dst_ip}) self.assertIsNone(result) def test_set_destination_port(self): @@ -506,7 +514,7 @@ def test_set_destination_port(self): result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'destination_port': dst_port}}) + target, **{'destination_port': dst_port}) self.assertIsNone(result) def test_set_enable_rule(self): @@ -519,8 +527,7 @@ def test_set_enable_rule(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.mocked.assert_called_once_with( - target, {self.res: {'enabled': True}}) + self.mocked.assert_called_once_with(target, **{'enabled': True}) self.assertIsNone(result) def test_set_disable_rule(self): @@ -533,8 +540,7 @@ def test_set_disable_rule(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.mocked.assert_called_once_with( - target, {self.res: {'enabled': False}}) + self.mocked.assert_called_once_with(target, **{'enabled': False}) self.assertIsNone(result) def test_set_action(self): @@ -548,8 +554,7 @@ def test_set_action(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.mocked.assert_called_once_with( - target, {self.res: {'action': action}}) + self.mocked.assert_called_once_with(target, **{'action': action}) self.assertIsNone(result) def test_set_enable_rule_and_disable_rule(self): @@ -578,7 +583,7 @@ def test_set_no_source_ip_address(self): result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'source_ip_address': None}}) + target, **{'source_ip_address': None}) self.assertIsNone(result) def test_set_no_source_port(self): @@ -594,8 +599,7 @@ def test_set_no_source_port(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.mocked.assert_called_once_with( - target, {self.res: {'source_port': None}}) + self.mocked.assert_called_once_with(target, **{'source_port': None}) self.assertIsNone(result) def test_set_no_destination_ip_address(self): @@ -612,7 +616,7 @@ def test_set_no_destination_ip_address(self): result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'destination_ip_address': None}}) + target, **{'destination_ip_address': None}) self.assertIsNone(result) def test_set_no_destination_port(self): @@ -629,7 +633,7 @@ def test_set_no_destination_port(self): result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'destination_port': None}}) + target, **{'destination_port': None}) self.assertIsNone(result) def test_set_source_ip_address_and_no(self): @@ -697,7 +701,7 @@ def test_set_destination_port_and_no(self): self.check_parser, self.cmd, arglist, verifylist) def test_set_and_raises(self): - self.neutronclient.update_fwaas_firewall_rule = mock.Mock( + self.networkclient.update_firewall_rule = mock.Mock( side_effect=Exception) target = self.resource['id'] arglist = [target, '--name', 'my-name'] @@ -721,7 +725,7 @@ def test_set_no_destination_fwg(self): result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'destination_firewall_group_id': None}}) + target, **{'destination_firewall_group_id': None}) self.assertIsNone(result) def test_set_no_source_fwg(self): @@ -738,7 +742,7 @@ def test_set_no_source_fwg(self): result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'source_firewall_group_id': None}}) + target, **{'source_firewall_group_id': None}) self.assertIsNone(result) def test_create_with_src_fwg_and_no(self): @@ -780,13 +784,19 @@ class TestUnsetFirewallRule(TestFirewallRule, common.TestUnsetFWaaS): def setUp(self): super(TestUnsetFirewallRule, self).setUp() - self.neutronclient.update_fwaas_firewall_rule = mock.Mock( + self.networkclient.update_firewall_rule = mock.Mock( return_value={self.res: _fwr}) - self.mocked = self.neutronclient.update_fwaas_firewall_rule + self.mocked = self.networkclient.update_firewall_rule + + def _mock_find_rule(*args, **kwargs): + return {'id': args[0]} + + self.networkclient.find_firewall_rule.side_effect = _mock_find_rule + self.cmd = firewallrule.UnsetFirewallRule(self.app, self.namespace) def test_unset_protocol_and_raise(self): - self.neutronclient.update_fwaas_firewall_rule.side_effect = Exception + self.networkclient.update_firewall_rule.side_effect = Exception target = self.resource['id'] arglist = [ target, @@ -813,7 +823,7 @@ def test_unset_source_port(self): result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'source_port': None}}) + target, **{'source_port': None}) self.assertIsNone(result) def test_unset_destination_port(self): @@ -830,7 +840,7 @@ def test_unset_destination_port(self): result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'destination_port': None}}) + target, **{'destination_port': None}) self.assertIsNone(result) def test_unset_source_ip_address(self): @@ -847,7 +857,7 @@ def test_unset_source_ip_address(self): result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'source_ip_address': None}}) + target, **{'source_ip_address': None}) self.assertIsNone(result) def test_unset_destination_ip_address(self): @@ -864,7 +874,7 @@ def test_unset_destination_ip_address(self): result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'destination_ip_address': None}}) + target, **{'destination_ip_address': None}) self.assertIsNone(result) def test_unset_enable_rule(self): @@ -881,7 +891,7 @@ def test_unset_enable_rule(self): result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'enabled': False}}) + target, **{'enabled': False}) self.assertIsNone(result) @@ -889,7 +899,6 @@ class TestDeleteFirewallRule(TestFirewallRule, common.TestDeleteFWaaS): def setUp(self): super(TestDeleteFirewallRule, self).setUp() - self.neutronclient.delete_fwaas_firewall_rule = mock.Mock( - return_value={self.res: _fwr}) - self.mocked = self.neutronclient.delete_fwaas_firewall_rule + self.networkclient.delete_firewall_rule = mock.Mock(return_value=_fwr) + self.mocked = self.networkclient.delete_firewall_rule self.cmd = firewallrule.DeleteFirewallRule(self.app, self.namespace) diff --git a/requirements.txt b/requirements.txt index 3ed2768cc..ff8961688 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ cliff>=3.4.0 # Apache-2.0 debtcollector>=1.2.0 # Apache-2.0 iso8601>=0.1.11 # MIT netaddr>=0.7.18 # BSD -openstacksdk>=1.0.2 # Apache-2.0 +openstacksdk>=1.5.0 # Apache-2.0 osc-lib>=1.12.0 # Apache-2.0 oslo.i18n>=3.15.3 # Apache-2.0 oslo.log>=3.36.0 # Apache-2.0 From 6597c421ff90576f02403d93a9a38649eabf64dd Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Thu, 7 Sep 2023 09:42:55 +0000 Subject: [PATCH 808/845] Update master for stable/2023.2 Add file to the reno documentation build to show release notes for stable/2023.2. Use pbr instruction to increment the minor version number automatically so that master versions are higher than the versions on stable/2023.2. Sem-Ver: feature Change-Id: I7d5138a0e6bbbd33dd2bb737f865e54e4541a279 --- releasenotes/source/2023.2.rst | 6 ++++++ releasenotes/source/index.rst | 1 + 2 files changed, 7 insertions(+) create mode 100644 releasenotes/source/2023.2.rst diff --git a/releasenotes/source/2023.2.rst b/releasenotes/source/2023.2.rst new file mode 100644 index 000000000..a4838d7d0 --- /dev/null +++ b/releasenotes/source/2023.2.rst @@ -0,0 +1,6 @@ +=========================== +2023.2 Series Release Notes +=========================== + +.. release-notes:: + :branch: stable/2023.2 diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index 5dbb2f331..dffa77497 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -6,6 +6,7 @@ :maxdepth: 1 unreleased + 2023.2 2023.1 zed yoga From 396432ab06aeace3d4199a8a42d60fd9b2ebf503 Mon Sep 17 00:00:00 2001 From: elajkat Date: Wed, 21 Jun 2023 15:56:28 +0200 Subject: [PATCH 809/845] OSC: Remove VPNAAS V2 calls to neutronclient Depends-On: https://review.opendev.org/c/openstack/openstacksdk/+/886822 Actually the new SDK release is the dependency, so bump minimum SDK version to 1.5.0. Change-Id: I1933dceb3dc28b464e6e9c4ce468abbd03a00ba4 Related-Bug: #1999774 --- neutronclient/osc/v2/vpnaas/endpoint_group.py | 63 +++++----- neutronclient/osc/v2/vpnaas/ikepolicy.py | 58 ++++++---- .../osc/v2/vpnaas/ipsec_site_connection.py | 109 ++++++++++-------- neutronclient/osc/v2/vpnaas/ipsecpolicy.py | 59 ++++++---- neutronclient/osc/v2/vpnaas/vpnservice.py | 64 ++++++---- .../tests/unit/osc/v2/vpnaas/common.py | 56 ++++++--- .../tests/unit/osc/v2/vpnaas/fakes.py | 30 +++-- .../unit/osc/v2/vpnaas/test_endpoint_group.py | 59 +++++----- .../unit/osc/v2/vpnaas/test_ikepolicy.py | 68 +++++------ .../v2/vpnaas/test_ipsec_site_connection.py | 105 ++++++++--------- .../unit/osc/v2/vpnaas/test_ipsecpolicy.py | 72 ++++++------ .../unit/osc/v2/vpnaas/test_vpnservice.py | 82 ++++++------- 12 files changed, 447 insertions(+), 378 deletions(-) diff --git a/neutronclient/osc/v2/vpnaas/endpoint_group.py b/neutronclient/osc/v2/vpnaas/endpoint_group.py index 668b3d80a..e37c2cef4 100644 --- a/neutronclient/osc/v2/vpnaas/endpoint_group.py +++ b/neutronclient/osc/v2/vpnaas/endpoint_group.py @@ -32,9 +32,19 @@ ('type', 'Type', column_util.LIST_BOTH), ('endpoints', 'Endpoints', column_util.LIST_BOTH), ('description', 'Description', column_util.LIST_LONG_ONLY), - ('tenant_id', 'Project', column_util.LIST_LONG_ONLY), + ('project_id', 'Project', column_util.LIST_LONG_ONLY), ) +_attr_map_dict = { + 'id': 'ID', + 'name': 'Name', + 'type': 'Type', + 'endpoints': 'Endpoints', + 'description': 'Description', + 'tenant_id': 'Project', + 'project_id': 'Project', +} + def _get_common_parser(parser): parser.add_argument( @@ -83,23 +93,22 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network attrs = _get_common_attrs(self.app.client_manager, parsed_args) if parsed_args.name: attrs['name'] = str(parsed_args.name) attrs['type'] = parsed_args.type if parsed_args.type == 'subnet': - _subnet_ids = [client.find_resource( - 'subnet', + _subnet_ids = [client.find_subnet( endpoint, - cmd_resource='subnet')['id'] - for endpoint in parsed_args.endpoints] + ignore_missing=False)['id'] + for endpoint in parsed_args.endpoints] attrs['endpoints'] = _subnet_ids else: attrs['endpoints'] = parsed_args.endpoints - obj = client.create_endpoint_group( - {'endpoint_group': attrs})['endpoint_group'] - columns, display_columns = column_util.get_columns(obj, _attr_map) + obj = client.create_vpn_endpoint_group(**attrs) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id']) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -117,15 +126,13 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network result = 0 for endpoint in parsed_args.endpoint_group: try: - endpoint_id = client.find_resource( - 'endpoint_group', - endpoint, - cmd_resource='endpoint_group')['id'] - client.delete_endpoint_group(endpoint_id) + endpoint_id = client.find_vpn_endpoint_group( + endpoint, ignore_missing=False)['id'] + client.delete_vpn_endpoint_group(endpoint_id) except Exception as e: result += 1 LOG.error(_("Failed to delete endpoint group with " @@ -153,8 +160,8 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - obj = client.list_endpoint_groups()['endpoint_groups'] + client = self.app.client_manager.network + obj = client.vpn_endpoint_groups() headers, columns = column_util.get_column_definitions( _attr_map, long_listing=parsed_args.long) return (headers, (utils.get_dict_properties(s, columns) for s in obj)) @@ -177,17 +184,15 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network attrs = _get_common_attrs(self.app.client_manager, parsed_args, is_create=False) if parsed_args.name: attrs['name'] = str(parsed_args.name) - endpoint_id = client.find_resource( - 'endpoint_group', parsed_args.endpoint_group, - cmd_resource='endpoint_group')['id'] + endpoint_id = client.find_vpn_endpoint_group( + parsed_args.endpoint_group, ignore_missing=False)['id'] try: - client.update_endpoint_group(endpoint_id, - {'endpoint_group': attrs}) + client.update_vpn_endpoint_group(endpoint_id, **attrs) except Exception as e: msg = (_("Failed to set endpoint group " "%(endpoint_group)s: %(e)s") @@ -207,11 +212,11 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - endpoint_id = client.find_resource( - 'endpoint_group', parsed_args.endpoint_group, - cmd_resource='endpoint_group')['id'] - obj = client.show_endpoint_group(endpoint_id)['endpoint_group'] - columns, display_columns = column_util.get_columns(obj, _attr_map) + client = self.app.client_manager.network + endpoint_id = client.find_vpn_endpoint_group( + parsed_args.endpoint_group, ignore_missing=False)['id'] + obj = client.get_vpn_endpoint_group(endpoint_id) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id']) data = utils.get_dict_properties(obj, columns) return (display_columns, data) diff --git a/neutronclient/osc/v2/vpnaas/ikepolicy.py b/neutronclient/osc/v2/vpnaas/ikepolicy.py index 73b6402d3..766a550f1 100644 --- a/neutronclient/osc/v2/vpnaas/ikepolicy.py +++ b/neutronclient/osc/v2/vpnaas/ikepolicy.py @@ -38,10 +38,24 @@ ('description', 'Description', column_util.LIST_LONG_ONLY), ('phase1_negotiation_mode', 'Phase1 Negotiation Mode', column_util.LIST_LONG_ONLY), - ('tenant_id', 'Project', column_util.LIST_LONG_ONLY), + ('project_id', 'Project', column_util.LIST_LONG_ONLY), ('lifetime', 'Lifetime', column_util.LIST_LONG_ONLY), ) +_attr_map_dict = { + 'id': 'ID', + 'name': 'Name', + 'auth_algorithm': 'Authentication Algorithm', + 'encryption_algorithm': 'Encryption Algorithm', + 'ike_version': 'IKE Version', + 'pfs': 'Perfect Forward Secrecy (PFS)', + 'phase1_negotiation_mode': 'Phase1 Negotiation Mode', + 'lifetime': 'Lifetime', + 'description': 'Description', + 'tenant_id': 'Project', + 'project_id': 'Project', +} + def _convert_to_lowercase(string): return string.lower() @@ -89,7 +103,7 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): attrs = {} if is_create: if 'project' in parsed_args and parsed_args.project is not None: - attrs['tenant_id'] = osc_utils.find_project( + attrs['project_id'] = osc_utils.find_project( client_manager.identity, parsed_args.project, parsed_args.project_domain, @@ -126,12 +140,13 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network attrs = _get_common_attrs(self.app.client_manager, parsed_args) if parsed_args.name: attrs['name'] = str(parsed_args.name) - obj = client.create_ikepolicy({'ikepolicy': attrs})['ikepolicy'] - columns, display_columns = column_util.get_columns(obj, _attr_map) + obj = client.create_vpn_ike_policy(**attrs) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id', 'units', 'value']) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -149,13 +164,13 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network result = 0 for ike in parsed_args.ikepolicy: try: - ike_id = client.find_resource( - 'ikepolicy', ike, cmd_resource='ikepolicy')['id'] - client.delete_ikepolicy(ike_id) + ike_id = client.find_vpn_ike_policy(ike, + ignore_missing=False)['id'] + client.delete_vpn_ike_policy(ike_id) except Exception as e: result += 1 LOG.error(_("Failed to delete IKE policy with " @@ -182,8 +197,8 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - obj = client.list_ikepolicies()['ikepolicies'] + client = self.app.client_manager.network + obj = client.vpn_ike_policies() headers, columns = column_util.get_column_definitions( _attr_map, long_listing=parsed_args.long) return (headers, (utils.get_dict_properties(s, columns) for s in obj)) @@ -206,16 +221,15 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network attrs = _get_common_attrs(self.app.client_manager, parsed_args, is_create=False) if parsed_args.name: attrs['name'] = parsed_args.name - ike_id = client.find_resource( - 'ikepolicy', parsed_args.ikepolicy, - cmd_resource='ikepolicy')['id'] + ike_id = client.find_vpn_ike_policy(parsed_args.ikepolicy, + ignore_missing=False)['id'] try: - client.update_ikepolicy(ike_id, {'ikepolicy': attrs}) + client.update_vpn_ike_policy(ike_id, **attrs) except Exception as e: msg = (_("Failed to set IKE policy '%(ike)s': %(e)s") % {'ike': parsed_args.ikepolicy, 'e': e}) @@ -234,11 +248,11 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - ike_id = client.find_resource( - 'ikepolicy', parsed_args.ikepolicy, - cmd_resource='ikepolicy')['id'] - obj = client.show_ikepolicy(ike_id)['ikepolicy'] - columns, display_columns = column_util.get_columns(obj, _attr_map) + client = self.app.client_manager.network + ike_id = client.find_vpn_ike_policy(parsed_args.ikepolicy, + ignore_missing=False)['id'] + obj = client.get_vpn_ike_policy(ike_id) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id', 'units', 'value']) data = utils.get_dict_properties(obj, columns) return (display_columns, data) diff --git a/neutronclient/osc/v2/vpnaas/ipsec_site_connection.py b/neutronclient/osc/v2/vpnaas/ipsec_site_connection.py index 4a861f081..1497793b0 100644 --- a/neutronclient/osc/v2/vpnaas/ipsec_site_connection.py +++ b/neutronclient/osc/v2/vpnaas/ipsec_site_connection.py @@ -41,14 +41,14 @@ ('peer_address', 'Peer Address', column_util.LIST_BOTH), ('auth_mode', 'Authentication Algorithm', column_util.LIST_BOTH), ('status', 'Status', column_util.LIST_BOTH), - ('tenant_id', 'Project', column_util.LIST_LONG_ONLY), + ('project_id', 'Project', column_util.LIST_LONG_ONLY), ('peer_cidrs', 'Peer CIDRs', column_util.LIST_LONG_ONLY), ('vpnservice_id', 'VPN Service', column_util.LIST_LONG_ONLY), ('ipsecpolicy_id', 'IPSec Policy', column_util.LIST_LONG_ONLY), ('ikepolicy_id', 'IKE Policy', column_util.LIST_LONG_ONLY), ('mtu', 'MTU', column_util.LIST_LONG_ONLY), ('initiator', 'Initiator', column_util.LIST_LONG_ONLY), - ('admin_state_up', 'State', column_util.LIST_LONG_ONLY), + ('is_admin_state_up', 'State', column_util.LIST_LONG_ONLY), ('description', 'Description', column_util.LIST_LONG_ONLY), ('psk', 'Pre-shared Key', column_util.LIST_LONG_ONLY), ('route_mode', 'Route Mode', column_util.LIST_LONG_ONLY), @@ -57,8 +57,33 @@ ('local_ep_group_id', 'Local Endpoint Group ID', column_util.LIST_LONG_ONLY), ('peer_ep_group_id', 'Peer Endpoint Group ID', column_util.LIST_LONG_ONLY), + ('dpd', 'DPD', column_util.LIST_LONG_ONLY), ) +_attr_map_dict = { + 'id': 'ID', + 'name': 'Name', + 'peer_address': 'Peer Address', + 'auth_mode': 'Authentication Algorithm', + 'status': 'Status', + 'peer_cidrs': 'Peer CIDRs', + 'vpnservice_id': 'VPN Service', + 'ipsecpolicy_id': 'IPSec Policy', + 'ikepolicy_id': 'IKE Policy', + 'mtu': 'MTU', + 'initiator': 'Initiator', + 'is_admin_state_up': 'State', + 'psk': 'Pre-shared Key', + 'route_mode': 'Route Mode', + 'local_id': 'Local ID', + 'peer_id': 'Peer ID', + 'local_ep_group_id': 'Local Endpoint Group ID', + 'peer_ep_group_id': 'Peer Endpoint Group ID', + 'description': 'Description', + 'project_id': 'Project', + 'dpd': 'DPD', +} + def _convert_to_lowercase(string): return string.lower() @@ -122,7 +147,7 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): attrs = {} if is_create: if 'project' in parsed_args and parsed_args.project is not None: - attrs['tenant_id'] = osc_utils.find_project( + attrs['project_id'] = osc_utils.find_project( client_manager.identity, parsed_args.project, parsed_args.project_domain, @@ -141,16 +166,12 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): vpn_utils.validate_dpd_dict(parsed_args.dpd) attrs['dpd'] = parsed_args.dpd if parsed_args.local_endpoint_group: - _local_epg = client_manager.neutronclient.find_resource( - 'endpoint_group', - parsed_args.local_endpoint_group, - cmd_resource='endpoint_group')['id'] + _local_epg = client_manager.network.find_vpn_endpoint_group( + parsed_args.local_endpoint_group)['id'] attrs['local_ep_group_id'] = _local_epg if parsed_args.peer_endpoint_group: - _peer_epg = client_manager.neutronclient.find_resource( - 'endpoint_group', - parsed_args.peer_endpoint_group, - cmd_resource='endpoint_group')['id'] + _peer_epg = client_manager.network.find_vpn_endpoint_group( + parsed_args.peer_endpoint_group)['id'] attrs['peer_ep_group_id'] = _peer_epg if parsed_args.peer_cidrs: attrs['peer_cidrs'] = parsed_args.peer_cidrs @@ -203,25 +224,19 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network attrs = _get_common_attrs(self.app.client_manager, parsed_args) if parsed_args.vpnservice: - _vpnservice_id = client.find_resource( - 'vpnservice', - parsed_args.vpnservice, - cmd_resource='vpnservice')['id'] + _vpnservice_id = client.find_vpn_service( + parsed_args.vpnservice, ignore_missing=False)['id'] attrs['vpnservice_id'] = _vpnservice_id if parsed_args.ikepolicy: - _ikepolicy_id = client.find_resource( - 'ikepolicy', - parsed_args.ikepolicy, - cmd_resource='ikepolicy')['id'] + _ikepolicy_id = client.find_vpn_ike_policy( + parsed_args.ikepolicy, ignore_missing=False)['id'] attrs['ikepolicy_id'] = _ikepolicy_id if parsed_args.ipsecpolicy: - _ipsecpolicy_id = client.find_resource( - 'ipsecpolicy', - parsed_args.ipsecpolicy, - cmd_resource='ipsecpolicy')['id'] + _ipsecpolicy_id = client.find_vpn_ipsec_policy( + parsed_args.ipsecpolicy, ignore_missing=False)['id'] attrs['ipsecpolicy_id'] = _ipsecpolicy_id if parsed_args.peer_id: attrs['peer_id'] = parsed_args.peer_id @@ -239,9 +254,10 @@ def take_action(self, parsed_args): if not parsed_args.peer_cidrs and not parsed_args.local_endpoint_group: message = _("You must specify endpoint groups or peer CIDR(s)") raise exceptions.CommandError(message) - obj = client.create_ipsec_site_connection( - {'ipsec_site_connection': attrs})['ipsec_site_connection'] - columns, display_columns = column_util.get_columns(obj, _attr_map) + obj = client.create_vpn_ipsec_site_connection(**attrs) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id', 'action', + 'timeout', 'interval']) data = utils.get_dict_properties(obj, columns, formatters=_formatters) return display_columns, data @@ -259,15 +275,13 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network result = 0 for ipsec_conn in parsed_args.ipsec_site_connection: try: - ipsec_con_id = client.find_resource( - 'ipsec_site_connection', - ipsec_conn, - cmd_resource='ipsec_site_connection')['id'] - client.delete_ipsec_site_connection(ipsec_con_id) + ipsec_con_id = client.find_vpn_ipsec_site_connection( + ipsec_conn, ignore_missing=False)['id'] + client.delete_vpn_ipsec_site_connection(ipsec_con_id) except Exception as e: result += 1 LOG.error(_("Failed to delete IPsec site connection with " @@ -296,8 +310,8 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - obj = client.list_ipsec_site_connections()['ipsec_site_connections'] + client = self.app.client_manager.network + obj = client.vpn_ipsec_site_connections() headers, columns = column_util.get_column_definitions( _attr_map, long_listing=parsed_args.long) return (headers, (utils.get_dict_properties( @@ -328,7 +342,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network attrs = _get_common_attrs(self.app.client_manager, parsed_args, is_create=False) if parsed_args.peer_id: @@ -337,13 +351,10 @@ def take_action(self, parsed_args): attrs['peer_address'] = parsed_args.peer_address if parsed_args.name: attrs['name'] = parsed_args.name - ipsec_conn_id = client.find_resource( - 'ipsec_site_connection', parsed_args.ipsec_site_connection, - cmd_resource='ipsec_site_connection')['id'] + ipsec_conn_id = client.find_vpn_ipsec_site_connection( + parsed_args.ipsec_site_connection, ignore_missing=False)['id'] try: - client.update_ipsec_site_connection( - ipsec_conn_id, - {'ipsec_site_connection': attrs}) + client.update_vpn_ipsec_site_connection(ipsec_conn_id, **attrs) except Exception as e: msg = (_("Failed to set IPsec site " "connection '%(ipsec_conn)s': %(e)s") @@ -363,12 +374,12 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - ipsec_site_id = client.find_resource( - 'ipsec_site_connection', parsed_args.ipsec_site_connection, - cmd_resource='ipsec_site_connection')['id'] - obj = client.show_ipsec_site_connection( - ipsec_site_id)['ipsec_site_connection'] - columns, display_columns = column_util.get_columns(obj, _attr_map) + client = self.app.client_manager.network + ipsec_site_id = client.find_vpn_ipsec_site_connection( + parsed_args.ipsec_site_connection, ignore_missing=False)['id'] + obj = client.get_vpn_ipsec_site_connection(ipsec_site_id) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id', 'action', + 'timeout', 'interval']) data = utils.get_dict_properties(obj, columns, formatters=_formatters) return (display_columns, data) diff --git a/neutronclient/osc/v2/vpnaas/ipsecpolicy.py b/neutronclient/osc/v2/vpnaas/ipsecpolicy.py index 2c80543e7..877602ffc 100644 --- a/neutronclient/osc/v2/vpnaas/ipsecpolicy.py +++ b/neutronclient/osc/v2/vpnaas/ipsecpolicy.py @@ -37,10 +37,23 @@ ('encryption_algorithm', 'Encryption Algorithm', column_util.LIST_BOTH), ('pfs', 'Perfect Forward Secrecy (PFS)', column_util.LIST_LONG_ONLY), ('description', 'Description', column_util.LIST_LONG_ONLY), - ('tenant_id', 'Project', column_util.LIST_LONG_ONLY), + ('project_id', 'Project', column_util.LIST_LONG_ONLY), ('lifetime', 'Lifetime', column_util.LIST_LONG_ONLY), ) +_attr_map_dict = { + 'id': 'ID', + 'name': 'Name', + 'auth_algorithm': 'Authentication Algorithm', + 'encapsulation_mode': 'Encapsulation Mode', + 'transform_protocol': 'Transform Protocol', + 'encryption_algorithm': 'Encryption Algorithm', + 'pfs': 'Perfect Forward Secrecy (PFS)', + 'lifetime': 'Lifetime', + 'description': 'Description', + 'project_id': 'Project', +} + def _convert_to_lowercase(string): return string.lower() @@ -87,7 +100,7 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): attrs = {} if is_create: if 'project' in parsed_args and parsed_args.project is not None: - attrs['tenant_id'] = osc_utils.find_project( + attrs['project_id'] = osc_utils.find_project( client_manager.identity, parsed_args.project, parsed_args.project_domain, @@ -124,12 +137,14 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network attrs = _get_common_attrs(self.app.client_manager, parsed_args) if parsed_args.name: attrs['name'] = str(parsed_args.name) - obj = client.create_ipsecpolicy({'ipsecpolicy': attrs})['ipsecpolicy'] - columns, display_columns = column_util.get_columns(obj, _attr_map) + obj = client.create_vpn_ipsec_policy(**attrs) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id', + 'phase1_negotiation_mode', 'units', 'value']) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -147,13 +162,13 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network result = 0 for ipsec in parsed_args.ipsecpolicy: try: - ipsec_id = client.find_resource( - 'ipsecpolicy', ipsec, cmd_resource='ipsecpolicy')['id'] - client.delete_ipsecpolicy(ipsec_id) + ipsec_id = client.find_vpn_ipsec_policy( + ipsec, ignore_missing=False)['id'] + client.delete_vpn_ipsec_policy(ipsec_id) except Exception as e: result += 1 LOG.error(_("Failed to delete IPsec policy with " @@ -181,8 +196,8 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - obj = client.list_ipsecpolicies()['ipsecpolicies'] + client = self.app.client_manager.network + obj = client.vpn_ipsec_policies() headers, columns = column_util.get_column_definitions( _attr_map, long_listing=parsed_args.long) return (headers, (utils.get_dict_properties(s, columns) for s in obj)) @@ -205,16 +220,15 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network attrs = _get_common_attrs(self.app.client_manager, parsed_args, is_create=False) if parsed_args.name: attrs['name'] = str(parsed_args.name) - ipsec_id = client.find_resource( - 'ipsecpolicy', parsed_args.ipsecpolicy, - cmd_resource='ipsecpolicy')['id'] + ipsec_id = client.find_vpn_ipsec_policy( + parsed_args.ipsecpolicy, ignore_missing=False)['id'] try: - client.update_ipsecpolicy(ipsec_id, {'ipsecpolicy': attrs}) + client.update_vpn_ipsec_policy(ipsec_id, **attrs) except Exception as e: msg = (_("Failed to set IPsec policy '%(ipsec)s': %(e)s") % {'ipsec': parsed_args.ipsecpolicy, 'e': e}) @@ -233,11 +247,12 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - ipsec_id = client.find_resource( - 'ipsecpolicy', parsed_args.ipsecpolicy, - cmd_resource='ipsecpolicy')['id'] - obj = client.show_ipsecpolicy(ipsec_id)['ipsecpolicy'] - columns, display_columns = column_util.get_columns(obj, _attr_map) + client = self.app.client_manager.network + ipsec_id = client.find_vpn_ipsec_policy( + parsed_args.ipsecpolicy, ignore_missing=False)['id'] + obj = client.get_vpn_ipsec_policy(ipsec_id) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id', + 'phase1_negotiation_mode', 'units', 'value']) data = utils.get_dict_properties(obj, columns) return (display_columns, data) diff --git a/neutronclient/osc/v2/vpnaas/vpnservice.py b/neutronclient/osc/v2/vpnaas/vpnservice.py index 3d5b10e7d..f845a57ff 100644 --- a/neutronclient/osc/v2/vpnaas/vpnservice.py +++ b/neutronclient/osc/v2/vpnaas/vpnservice.py @@ -32,12 +32,24 @@ ('router_id', 'Router', column_util.LIST_BOTH), ('subnet_id', 'Subnet', column_util.LIST_BOTH), ('flavor_id', 'Flavor', column_util.LIST_BOTH), - ('admin_state_up', 'State', column_util.LIST_BOTH), + ('is_admin_state_up', 'State', column_util.LIST_BOTH), ('status', 'Status', column_util.LIST_BOTH), ('description', 'Description', column_util.LIST_LONG_ONLY), - ('tenant_id', 'Project', column_util.LIST_LONG_ONLY), + ('project_id', 'Project', column_util.LIST_LONG_ONLY), ) +_attr_map_dict = { + 'id': 'ID', + 'name': 'Name', + 'router_id': 'Router', + 'subnet_id': 'Subnet', + 'flavor_id': 'Flavor', + 'is_admin_state_up': 'State', + 'status': 'Status', + 'description': 'Description', + 'project_id': 'Project', +} + def _get_common_parser(parser): parser.add_argument( @@ -70,7 +82,7 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): attrs = {} if is_create: if 'project' in parsed_args and parsed_args.project is not None: - attrs['tenant_id'] = osc_utils.find_project( + attrs['project_id'] = osc_utils.find_project( client_manager.identity, parsed_args.project, parsed_args.project_domain, @@ -113,16 +125,18 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network attrs = _get_common_attrs(self.app.client_manager, parsed_args) if parsed_args.name: attrs['name'] = str(parsed_args.name) if parsed_args.router: - _router_id = self.app.client_manager.network.find_router( - parsed_args.router).id + _router_id = client.find_router(parsed_args.router, + ignore_missing=False).id attrs['router_id'] = _router_id - obj = client.create_vpnservice({'vpnservice': attrs})['vpnservice'] - columns, display_columns = column_util.get_columns(obj, _attr_map) + obj = client.create_vpn_service(**attrs) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id', 'external_v4_ip', + 'external_v6_ip']) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -140,13 +154,13 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network result = 0 for vpn in parsed_args.vpnservice: try: - vpn_id = client.find_resource( - 'vpnservice', vpn, cmd_resource='vpnservice')['id'] - client.delete_vpnservice(vpn_id) + vpn_id = client.find_vpn_service(vpn, + ignore_missing=False)['id'] + client.delete_vpn_service(vpn_id) except Exception as e: result += 1 LOG.error(_("Failed to delete VPN service with " @@ -174,8 +188,8 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - obj = client.list_vpnservices()['vpnservices'] + client = self.app.client_manager.network + obj = client.vpn_services() headers, columns = column_util.get_column_definitions( _attr_map, long_listing=parsed_args.long) return (headers, (utils.get_dict_properties(s, columns) for s in obj)) @@ -198,16 +212,15 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network attrs = _get_common_attrs(self.app.client_manager, parsed_args, is_create=False) if parsed_args.name: attrs['name'] = str(parsed_args.name) - vpn_id = client.find_resource( - 'vpnservice', parsed_args.vpnservice, - cmd_resource='vpnservice')['id'] + vpn_id = client.find_vpn_service(parsed_args.vpnservice, + ignore_missing=False)['id'] try: - client.update_vpnservice(vpn_id, {'vpnservice': attrs}) + client.update_vpn_service(vpn_id, **attrs) except Exception as e: msg = (_("Failed to set vpn service '%(vpn)s': %(e)s") % {'vpn': parsed_args.vpnservice, 'e': e}) @@ -226,11 +239,12 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - vpn_id = client.find_resource( - 'vpnservice', parsed_args.vpnservice, - cmd_resource='vpnservice')['id'] - obj = client.show_vpnservice(vpn_id)['vpnservice'] - columns, display_columns = column_util.get_columns(obj, _attr_map) + client = self.app.client_manager.network + vpn_id = client.find_vpn_service(parsed_args.vpnservice, + ignore_missing=False)['id'] + obj = client.get_vpn_service(vpn_id) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id', 'external_v4_ip', + 'external_v6_ip']) data = utils.get_dict_properties(obj, columns) return (display_columns, data) diff --git a/neutronclient/tests/unit/osc/v2/vpnaas/common.py b/neutronclient/tests/unit/osc/v2/vpnaas/common.py index 4edee8b97..7aaebdbe3 100644 --- a/neutronclient/tests/unit/osc/v2/vpnaas/common.py +++ b/neutronclient/tests/unit/osc/v2/vpnaas/common.py @@ -29,6 +29,17 @@ class TestDeleteVPNaaS(test_fakes.TestNeutronClientOSCV2): def test_delete_with_one_resource(self): target = self.resource['id'] + + def _mock_vpnaas(*args, **kwargs): + return {'id': args[0]} + + self.networkclient.find_vpn_endpoint_group.side_effect = _mock_vpnaas + self.networkclient.find_vpn_ipsec_site_connection.side_effect = \ + _mock_vpnaas + self.networkclient.find_vpn_ike_policy.side_effect = _mock_vpnaas + self.networkclient.find_vpn_ipsec_policy.side_effect = _mock_vpnaas + self.networkclient.find_vpn_service.side_effect = _mock_vpnaas + arglist = [target] verifylist = [(self.res, [target])] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -40,12 +51,14 @@ def test_delete_with_one_resource(self): def test_delete_with_multiple_resources(self): def _mock_vpnaas(*args, **kwargs): - self.assertEqual(self.res, args[0]) - self.assertIsNotNone(args[1]) - self.assertEqual({'cmd_resource': self.res}, kwargs) - return {'id': args[1]} + return {'id': args[0]} - self.neutronclient.find_resource.side_effect = _mock_vpnaas + self.networkclient.find_vpn_endpoint_group.side_effect = _mock_vpnaas + self.networkclient.find_vpn_ipsec_site_connection.side_effect = \ + _mock_vpnaas + self.networkclient.find_vpn_ike_policy.side_effect = _mock_vpnaas + self.networkclient.find_vpn_ipsec_policy.side_effect = _mock_vpnaas + self.networkclient.find_vpn_service.side_effect = _mock_vpnaas target1 = 'target1' target2 = 'target2' @@ -66,7 +79,19 @@ def test_delete_multiple_with_exception(self): arglist = [target1] verifylist = [(self.res, [target1])] - self.neutronclient.find_resource.side_effect = [ + self.networkclient.find_vpn_ipsec_site_connection.side_effect = [ + target1, exceptions.CommandError + ] + self.networkclient.find_vpn_endpoint_group.side_effect = [ + target1, exceptions.CommandError + ] + self.networkclient.find_vpn_ike_policy.side_effect = [ + target1, exceptions.CommandError + ] + self.networkclient.find_vpn_service.side_effect = [ + target1, exceptions.CommandError + ] + self.networkclient.find_vpn_ipsec_policy.side_effect = [ target1, exceptions.CommandError ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -114,7 +139,7 @@ def test_set_name(self): result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'name': update}}) + target, **{'name': update}) self.assertIsNone(result) def test_set_description(self): @@ -129,7 +154,7 @@ def test_set_description(self): result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'description': update}}) + target, **{'description': update}) self.assertIsNone(result) @@ -139,13 +164,14 @@ def test_show_filtered_by_id_or_name(self): target = self.resource['id'] def _mock_vpnaas(*args, **kwargs): - if self.neutronclient.find_resource.call_count == 1: - self.assertEqual(self.res, args[0]) - self.assertEqual(self.resource['id'], args[1]) - self.assertEqual({'cmd_resource': self.res}, kwargs) - return {'id': args[1]} - - self.neutronclient.find_resource.side_effect = _mock_vpnaas + return {'id': args[0]} + + self.networkclient.find_vpn_endpoint_group.side_effect = _mock_vpnaas + self.networkclient.find_vpn_ipsec_site_connection.side_effect = \ + _mock_vpnaas + self.networkclient.find_vpn_ike_policy.side_effect = _mock_vpnaas + self.networkclient.find_vpn_ipsec_policy.side_effect = _mock_vpnaas + self.networkclient.find_vpn_service.side_effect = _mock_vpnaas arglist = [target] verifylist = [(self.res, target)] diff --git a/neutronclient/tests/unit/osc/v2/vpnaas/fakes.py b/neutronclient/tests/unit/osc/v2/vpnaas/fakes.py index dfad250b3..2344c0a1f 100644 --- a/neutronclient/tests/unit/osc/v2/vpnaas/fakes.py +++ b/neutronclient/tests/unit/osc/v2/vpnaas/fakes.py @@ -15,10 +15,15 @@ # import collections -import copy from unittest import mock import uuid +from openstack.network.v2 import vpn_endpoint_group as vpn_epg +from openstack.network.v2 import vpn_ike_policy as vpn_ikep +from openstack.network.v2 import vpn_ipsec_policy as vpn_ipsecp +from openstack.network.v2 import vpn_ipsec_site_connection as vpn_sitec +from openstack.network.v2 import vpn_service + class FakeVPNaaS(object): @@ -31,7 +36,14 @@ def create(self, attrs={}): A OrderedDict faking the vpnaas resource """ self.ordered.update(attrs) - return copy.deepcopy(self.ordered) + if 'IKEPolicy' == self.__class__.__name__: + return vpn_ikep.VpnIkePolicy(**self.ordered) + if 'IPSecPolicy' == self.__class__.__name__: + return vpn_ipsecp.VpnIpsecPolicy(**self.ordered) + if 'VPNService' == self.__class__.__name__: + return vpn_service.VpnService(**self.ordered) + if 'EndpointGroup' == self.__class__.__name__: + return vpn_epg.VpnEndpointGroup(**self.ordered) def bulk_create(self, attrs=None, count=2): """Create multiple fake vpnaas resources @@ -74,7 +86,7 @@ def __init__(self): ('pfs', 'group5'), ('description', 'my-desc-' + uuid.uuid4().hex), ('phase1_negotiation_mode', 'main'), - ('tenant_id', 'tenant-id-' + uuid.uuid4().hex), + ('project_id', 'project-id-' + uuid.uuid4().hex), ('lifetime', {'units': 'seconds', 'value': 3600}), )) @@ -93,7 +105,7 @@ def __init__(self): ('encryption_algorithm', 'aes-128'), ('pfs', 'group5'), ('description', 'my-desc-' + uuid.uuid4().hex), - ('tenant_id', 'tenant-id-' + uuid.uuid4().hex), + ('project_id', 'project-id-' + uuid.uuid4().hex), ('lifetime', {'units': 'seconds', 'value': 3600}), )) @@ -112,7 +124,7 @@ def __init__(self): ('admin_state_up', True), ('status', 'ACTIVE'), ('description', 'my-desc-' + uuid.uuid4().hex), - ('tenant_id', 'tenant-id-' + uuid.uuid4().hex), + ('project_id', 'project-id-' + uuid.uuid4().hex), )) @@ -127,7 +139,7 @@ def __init__(self): ('type', 'cidr'), ('endpoints', ['10.0.0.0/24', '20.0.0.0/24']), ('description', 'my-desc-' + uuid.uuid4().hex), - ('tenant_id', 'tenant-id-' + uuid.uuid4().hex), + ('project_id', 'project-id-' + uuid.uuid4().hex), )) @@ -141,7 +153,7 @@ def create_conn(attrs=None): A dictionary with all attributes :return: A Dictionary with id, name, peer_address, auth_mode, status, - tenant_id, peer_cidrs, vpnservice_id, ipsecpolicy_id, + project_id, peer_cidrs, vpnservice_id, ipsecpolicy_id, ikepolicy_id, mtu, initiator, admin_state_up, description, psk, route_mode, local_id, peer_id, local_ep_group_id, peer_ep_group_id @@ -155,7 +167,7 @@ def create_conn(attrs=None): 'peer_address': '192.168.2.10', 'auth_mode': '', 'status': '', - 'tenant_id': 'tenant-id-' + uuid.uuid4().hex, + 'project_id': 'project-id-' + uuid.uuid4().hex, 'peer_cidrs': [], 'vpnservice_id': 'vpnservice-id-' + uuid.uuid4().hex, 'ipsecpolicy_id': 'ipsecpolicy-id-' + uuid.uuid4().hex, @@ -174,4 +186,4 @@ def create_conn(attrs=None): # Overwrite default attributes. conn_attrs.update(attrs) - return copy.deepcopy(conn_attrs) + return vpn_sitec.VpnIPSecSiteConnection(**conn_attrs) diff --git a/neutronclient/tests/unit/osc/v2/vpnaas/test_endpoint_group.py b/neutronclient/tests/unit/osc/v2/vpnaas/test_endpoint_group.py index 9c5dbcce5..e9da939b1 100644 --- a/neutronclient/tests/unit/osc/v2/vpnaas/test_endpoint_group.py +++ b/neutronclient/tests/unit/osc/v2/vpnaas/test_endpoint_group.py @@ -14,7 +14,6 @@ # under the License. # -import copy from unittest import mock from osc_lib.tests import utils as tests_utils @@ -36,12 +35,12 @@ def _generate_data(ordered_dict=None, data=None): source = ordered_dict if ordered_dict else _endpoint_group if data: source.update(data) - return tuple(source[key] for key in source) + return source def _generate_req_and_res(verifylist): request = dict(verifylist) - response = copy.deepcopy(_endpoint_group) + response = _endpoint_group for key, val in verifylist: converted = CONVERT_MAP.get(key, key) del request[key] @@ -55,25 +54,25 @@ class TestEndpointGroup(test_fakes.TestNeutronClientOSCV2): def check_results(self, headers, data, exp_req, is_list=False): if is_list: - req_body = {self.res_plural: [exp_req]} + req_body = {self.res_plural: list(exp_req)} else: - req_body = {self.res: exp_req} - self.mocked.assert_called_once_with(req_body) - self.assertEqual(self.ordered_headers, headers) + req_body = exp_req + self.mocked.assert_called_once_with(**req_body) + self.assertEqual(self.ordered_headers, tuple(sorted(headers))) self.assertEqual(self.ordered_data, data) def setUp(self): super(TestEndpointGroup, self).setUp() def _mock_endpoint_group(*args, **kwargs): - self.neutronclient.find_resource.assert_called_once_with( - self.res, self.resource['id'], cmd_resource='endpoint_group') - return {'id': args[1]} + self.networkclient.find_vpn_endpoint_group.assert_called_once_with( + self.resource['id'], ignore_missing=False) + return {'id': args[0]} - self.neutronclient.find_resource.side_effect = mock.Mock( + self.networkclient.find_vpn_endpoint_group.side_effect = mock.Mock( side_effect=_mock_endpoint_group) osc_utils.find_project = mock.Mock() - osc_utils.find_project.id = _endpoint_group['tenant_id'] + osc_utils.find_project.id = _endpoint_group['project_id'] self.res = 'endpoint_group' self.res_plural = 'endpoint_groups' self.resource = _endpoint_group @@ -99,7 +98,7 @@ def _mock_endpoint_group(*args, **kwargs): _endpoint_group['endpoints'], _endpoint_group['id'], _endpoint_group['name'], - _endpoint_group['tenant_id'], + _endpoint_group['project_id'], _endpoint_group['type'], ) self.ordered_columns = ( @@ -107,7 +106,7 @@ def _mock_endpoint_group(*args, **kwargs): 'endpoints', 'id', 'name', - 'tenant_id', + 'project_id', 'type', ) @@ -116,9 +115,9 @@ class TestCreateEndpointGroup(TestEndpointGroup, common.TestCreateVPNaaS): def setUp(self): super(TestCreateEndpointGroup, self).setUp() - self.neutronclient.create_endpoint_group = mock.Mock( - return_value={self.res: _endpoint_group}) - self.mocked = self.neutronclient.create_endpoint_group + self.networkclient.create_vpn_endpoint_group = mock.Mock( + return_value=_endpoint_group) + self.mocked = self.networkclient.create_vpn_endpoint_group self.cmd = endpoint_group.CreateEndpointGroup(self.app, self.namespace) def _update_expect_response(self, request, response): @@ -144,7 +143,7 @@ def _set_all_params_cidr(self, args={}): description = args.get('description') or 'my-desc' endpoint_type = args.get('type') or 'cidr' endpoints = args.get('endpoints') or ['10.0.0.0/24', '20.0.0.0/24'] - tenant_id = args.get('tenant_id') or 'my-tenant' + tenant_id = args.get('project_id') or 'my-tenant' arglist = [ '--description', description, '--type', endpoint_type, @@ -186,9 +185,8 @@ class TestDeleteEndpointGroup(TestEndpointGroup, common.TestDeleteVPNaaS): def setUp(self): super(TestDeleteEndpointGroup, self).setUp() - self.neutronclient.delete_endpoint_group = mock.Mock( - return_value={self.res: _endpoint_group}) - self.mocked = self.neutronclient.delete_endpoint_group + self.networkclient.delete_vpn_endpoint_group = mock.Mock() + self.mocked = self.networkclient.delete_vpn_endpoint_group self.cmd = endpoint_group.DeleteEndpointGroup(self.app, self.namespace) @@ -212,9 +210,9 @@ def setUp(self): _endpoint_group['endpoints'], ) - self.neutronclient.list_endpoint_groups = mock.Mock( - return_value={self.res_plural: [_endpoint_group]}) - self.mocked = self.neutronclient.list_endpoint_groups + self.networkclient.vpn_endpoint_groups = mock.Mock( + return_value=[_endpoint_group]) + self.mocked = self.networkclient.vpn_endpoint_groups def test_list_with_long_option(self): arglist = ['--long'] @@ -224,7 +222,6 @@ def test_list_with_long_option(self): self.mocked.assert_called_once_with() self.assertEqual(list(self.headers), headers) - self.assertEqual([self.data], list(data)) def test_list_with_no_option(self): arglist = [] @@ -241,9 +238,9 @@ class TestSetEndpointGroup(TestEndpointGroup, common.TestSetVPNaaS): def setUp(self): super(TestSetEndpointGroup, self).setUp() - self.neutronclient.update_endpoint_group = mock.Mock( - return_value={self.res: _endpoint_group}) - self.mocked = self.neutronclient.update_endpoint_group + self.networkclient.update_vpn_endpoint_group = mock.Mock( + return_value=_endpoint_group) + self.mocked = self.networkclient.update_vpn_endpoint_group self.cmd = endpoint_group.SetEndpointGroup(self.app, self.namespace) @@ -251,7 +248,7 @@ class TestShowEndpointGroup(TestEndpointGroup, common.TestShowVPNaaS): def setUp(self): super(TestShowEndpointGroup, self).setUp() - self.neutronclient.show_endpoint_group = mock.Mock( - return_value={self.res: _endpoint_group}) - self.mocked = self.neutronclient.show_endpoint_group + self.networkclient.get_vpn_endpoint_group = mock.Mock( + return_value=_endpoint_group) + self.mocked = self.networkclient.get_vpn_endpoint_group self.cmd = endpoint_group.ShowEndpointGroup(self.app, self.namespace) diff --git a/neutronclient/tests/unit/osc/v2/vpnaas/test_ikepolicy.py b/neutronclient/tests/unit/osc/v2/vpnaas/test_ikepolicy.py index cfe65a26b..bf68611f8 100644 --- a/neutronclient/tests/unit/osc/v2/vpnaas/test_ikepolicy.py +++ b/neutronclient/tests/unit/osc/v2/vpnaas/test_ikepolicy.py @@ -14,7 +14,6 @@ # under the License. # -import copy from unittest import mock from osc_lib.tests import utils as tests_utils @@ -28,7 +27,7 @@ _ikepolicy = fakes.IKEPolicy().create() CONVERT_MAP = { - 'project': 'tenant_id', + 'project': 'project_id', } @@ -36,12 +35,12 @@ def _generate_data(ordered_dict=None, data=None): source = ordered_dict if ordered_dict else _ikepolicy if data: source.update(data) - return tuple(source[key] for key in source) + return source def _generate_req_and_res(verifylist): request = dict(verifylist) - response = copy.deepcopy(_ikepolicy) + response = _ikepolicy for key, val in verifylist: converted = CONVERT_MAP.get(key, key) del request[key] @@ -55,25 +54,25 @@ class TestIKEPolicy(test_fakes.TestNeutronClientOSCV2): def check_results(self, headers, data, exp_req, is_list=False): if is_list: - req_body = {self.res_plural: [exp_req]} + req_body = {self.res_plural: list(exp_req)} else: - req_body = {self.res: exp_req} - self.mocked.assert_called_once_with(req_body) - self.assertEqual(self.ordered_headers, headers) + req_body = exp_req + self.mocked.assert_called_once_with(**req_body) + self.assertEqual(self.ordered_headers, tuple(sorted(headers))) self.assertEqual(self.ordered_data, data) def setUp(self): super(TestIKEPolicy, self).setUp() def _mock_ikepolicy(*args, **kwargs): - self.neutronclient.find_resource.assert_called_once_with( - self.res, self.resource['id'], cmd_resource='ikepolicy') - return {'id': args[1]} + self.networkclient.find_vpn_ike_policy.assert_called_once_with( + self.resource['id'], ignore_missing=False) + return {'id': args[0]} - self.neutronclient.find_resource.side_effect = mock.Mock( + self.networkclient.find_vpn_ike_policy.side_effect = mock.Mock( side_effect=_mock_ikepolicy) osc_utils.find_project = mock.Mock() - osc_utils.find_project.id = _ikepolicy['tenant_id'] + osc_utils.find_project.id = _ikepolicy['project_id'] self.res = 'ikepolicy' self.res_plural = 'ikepolicies' self.resource = _ikepolicy @@ -112,7 +111,7 @@ def _mock_ikepolicy(*args, **kwargs): _ikepolicy['name'], _ikepolicy['pfs'], _ikepolicy['phase1_negotiation_mode'], - _ikepolicy['tenant_id'], + _ikepolicy['project_id'], ) self.ordered_columns = ( 'auth_algorithm', @@ -124,7 +123,7 @@ def _mock_ikepolicy(*args, **kwargs): 'name', 'pfs', 'phase1_negotiation_mode', - 'tenant_id', + 'project_id', ) @@ -132,9 +131,9 @@ class TestCreateIKEPolicy(TestIKEPolicy, common.TestCreateVPNaaS): def setUp(self): super(TestCreateIKEPolicy, self).setUp() - self.neutronclient.create_ikepolicy = mock.Mock( - return_value={self.res: _ikepolicy}) - self.mocked = self.neutronclient.create_ikepolicy + self.networkclient.create_vpn_ike_policy = mock.Mock( + return_value=_ikepolicy) + self.mocked = self.networkclient.create_vpn_ike_policy self.cmd = ikepolicy.CreateIKEPolicy(self.app, self.namespace) def _update_expect_response(self, request, response): @@ -146,9 +145,8 @@ def _update_expect_response(self, request, response): A OrderedDict of request body """ # Update response body - self.neutronclient.create_ikepolicy.return_value = \ - {self.res: dict(response)} - osc_utils.find_project.return_value.id = response['tenant_id'] + self.networkclient.create_vpn_ikepolicy.return_value = response + osc_utils.find_project.return_value.id = response['project_id'] # Update response(finally returns 'data') self.data = _generate_data(ordered_dict=response) self.ordered_data = tuple( @@ -217,9 +215,8 @@ class TestDeleteIKEPolicy(TestIKEPolicy, common.TestDeleteVPNaaS): def setUp(self): super(TestDeleteIKEPolicy, self).setUp() - self.neutronclient.delete_ikepolicy = mock.Mock( - return_value={self.res: _ikepolicy}) - self.mocked = self.neutronclient.delete_ikepolicy + self.networkclient.delete_vpn_ike_policy = mock.Mock() + self.mocked = self.networkclient.delete_vpn_ike_policy self.cmd = ikepolicy.DeleteIKEPolicy(self.app, self.namespace) @@ -247,9 +244,9 @@ def setUp(self): _ikepolicy['pfs'], ) - self.neutronclient.list_ikepolicies = mock.Mock( - return_value={self.res_plural: [_ikepolicy]}) - self.mocked = self.neutronclient.list_ikepolicies + self.networkclient.vpn_ike_policies = mock.Mock( + return_value=[_ikepolicy]) + self.mocked = self.networkclient.vpn_ike_policies def test_list_with_long_option(self): arglist = ['--long'] @@ -259,7 +256,6 @@ def test_list_with_long_option(self): self.mocked.assert_called_once_with() self.assertEqual(list(self.headers), headers) - self.assertEqual([self.data], list(data)) def test_list_with_no_option(self): arglist = [] @@ -276,9 +272,9 @@ class TestSetIKEPolicy(TestIKEPolicy, common.TestSetVPNaaS): def setUp(self): super(TestSetIKEPolicy, self).setUp() - self.neutronclient.update_ikepolicy = mock.Mock( - return_value={self.res: _ikepolicy}) - self.mocked = self.neutronclient.update_ikepolicy + self.networkclient.update_vpn_ike_policy = mock.Mock( + return_value=_ikepolicy) + self.mocked = self.networkclient.update_vpn_ike_policy self.cmd = ikepolicy.SetIKEPolicy(self.app, self.namespace) def test_set_auth_algorithm_with_sha256(self): @@ -293,7 +289,7 @@ def test_set_auth_algorithm_with_sha256(self): result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'auth_algorithm': 'sha256'}}) + target, **{'auth_algorithm': 'sha256'}) self.assertIsNone(result) def test_set_phase1_negotiation_mode_with_aggressive(self): @@ -309,7 +305,7 @@ def test_set_phase1_negotiation_mode_with_aggressive(self): result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'phase1_negotiation_mode': 'aggressive'}}) + target, **{'phase1_negotiation_mode': 'aggressive'}) self.assertIsNone(result) @@ -317,7 +313,7 @@ class TestShowIKEPolicy(TestIKEPolicy, common.TestShowVPNaaS): def setUp(self): super(TestShowIKEPolicy, self).setUp() - self.neutronclient.show_ikepolicy = mock.Mock( - return_value={self.res: _ikepolicy}) - self.mocked = self.neutronclient.show_ikepolicy + self.networkclient.get_vpn_ike_policy = mock.Mock( + return_value=_ikepolicy) + self.mocked = self.networkclient.get_vpn_ike_policy self.cmd = ikepolicy.ShowIKEPolicy(self.app, self.namespace) diff --git a/neutronclient/tests/unit/osc/v2/vpnaas/test_ipsec_site_connection.py b/neutronclient/tests/unit/osc/v2/vpnaas/test_ipsec_site_connection.py index 96313b459..4929ea843 100644 --- a/neutronclient/tests/unit/osc/v2/vpnaas/test_ipsec_site_connection.py +++ b/neutronclient/tests/unit/osc/v2/vpnaas/test_ipsec_site_connection.py @@ -14,7 +14,6 @@ # under the License. # -import copy from unittest import mock from osc_lib.cli import format_columns @@ -29,7 +28,7 @@ _ipsec_site_conn = fakes.IPsecSiteConnection().create_conn() CONVERT_MAP = { - 'project': 'tenant_id', + 'project': 'project_id', 'ikepolicy': 'ikepolicy_id', 'ipsecpolicy': 'ipsecpolicy_id', 'vpnservice': 'vpnservice_id', @@ -42,33 +41,12 @@ def _generate_data(ordered_dict=None, data=None): source = ordered_dict if ordered_dict else _ipsec_site_conn if data: source.update(data) - return ( - _ipsec_site_conn['id'], - _ipsec_site_conn['name'], - _ipsec_site_conn['peer_address'], - _ipsec_site_conn['auth_mode'], - _ipsec_site_conn['status'], - _ipsec_site_conn['tenant_id'], - format_columns.ListColumn(_ipsec_site_conn['peer_cidrs']), - _ipsec_site_conn['vpnservice_id'], - _ipsec_site_conn['ipsecpolicy_id'], - _ipsec_site_conn['ikepolicy_id'], - _ipsec_site_conn['mtu'], - _ipsec_site_conn['initiator'], - _ipsec_site_conn['admin_state_up'], - _ipsec_site_conn['description'], - _ipsec_site_conn['psk'], - _ipsec_site_conn['route_mode'], - _ipsec_site_conn['local_id'], - _ipsec_site_conn['peer_id'], - _ipsec_site_conn['local_ep_group_id'], - _ipsec_site_conn['peer_ep_group_id'], - ) + return source def _generate_req_and_res(verifylist): request = dict(verifylist) - response = copy.deepcopy(_ipsec_site_conn) + response = _ipsec_site_conn for key, val in verifylist: converted = CONVERT_MAP.get(key, key) del request[key] @@ -82,23 +60,23 @@ class TestIPsecSiteConn(test_fakes.TestNeutronClientOSCV2): def check_results(self, headers, data, exp_req, is_list=False): if is_list: - req_body = {self.res_plural: [exp_req]} + req_body = {self.res_plural: list(exp_req)} else: - req_body = {self.res: exp_req} - self.mocked.assert_called_once_with(req_body) - self.assertEqual(self.ordered_headers, headers) + req_body = exp_req + self.mocked.assert_called_once_with(**req_body) + self.assertEqual(self.ordered_headers, tuple(sorted(headers))) self.assertItemEqual(self.ordered_data, data) def setUp(self): super(TestIPsecSiteConn, self).setUp() def _mock_ipsec_site_conn(*args, **kwargs): - return {'id': args[1]} + return {'id': args[0]} - self.neutronclient.find_resource.side_effect = mock.Mock( - side_effect=_mock_ipsec_site_conn) + self.networkclient.find_vpn_ipsec_site_connection.side_effect = \ + mock.Mock(side_effect=_mock_ipsec_site_conn) osc_utils.find_project = mock.Mock() - osc_utils.find_project.id = _ipsec_site_conn['tenant_id'] + osc_utils.find_project.id = _ipsec_site_conn['project_id'] self.res = 'ipsec_site_connection' self.res_plural = 'ipsec_site_connections' self.resource = _ipsec_site_conn @@ -122,11 +100,13 @@ def _mock_ipsec_site_conn(*args, **kwargs): 'Local ID', 'Peer ID', 'Local Endpoint Group ID', - 'Peer Endpoint Group ID' + 'Peer Endpoint Group ID', + 'DPD', ) self.data = _generate_data() self.ordered_headers = ( 'Authentication Algorithm', + 'DPD', 'Description', 'ID', 'IKE Policy', @@ -149,6 +129,7 @@ def _mock_ipsec_site_conn(*args, **kwargs): ) self.ordered_data = ( _ipsec_site_conn['auth_mode'], + _ipsec_site_conn['dpd'], _ipsec_site_conn['description'], _ipsec_site_conn['id'], _ipsec_site_conn['ikepolicy_id'], @@ -163,7 +144,7 @@ def _mock_ipsec_site_conn(*args, **kwargs): _ipsec_site_conn['peer_ep_group_id'], _ipsec_site_conn['peer_id'], _ipsec_site_conn['psk'], - _ipsec_site_conn['tenant_id'], + _ipsec_site_conn['project_id'], _ipsec_site_conn['route_mode'], _ipsec_site_conn['admin_state_up'], _ipsec_site_conn['status'], @@ -175,9 +156,9 @@ class TestCreateIPsecSiteConn(TestIPsecSiteConn, common.TestCreateVPNaaS): def setUp(self): super(TestCreateIPsecSiteConn, self).setUp() - self.neutronclient.create_ipsec_site_connection = mock.Mock( - return_value={self.res: _ipsec_site_conn}) - self.mocked = self.neutronclient.create_ipsec_site_connection + self.networkclient.create_vpn_ipsec_site_connection = mock.Mock( + return_value=_ipsec_site_conn) + self.mocked = self.networkclient.create_vpn_ipsec_site_connection self.cmd = ipsec_site_connection.CreateIPsecSiteConnection( self.app, self.namespace) @@ -190,13 +171,14 @@ def _update_expect_response(self, request, response): A OrderedDict of request body """ # Update response body - self.neutronclient.create_ipsec_site_connection.return_value = \ - {self.res: dict(response)} - osc_utils.find_project.return_value.id = response['tenant_id'] + self.networkclient.create_vpn_ipsec_site_connection.return_value = \ + response + osc_utils.find_project.return_value.id = response['project_id'] # Update response(finally returns 'data') self.data = _generate_data(ordered_dict=response) self.ordered_data = ( response['auth_mode'], + response['dpd'], response['description'], response['id'], response['ikepolicy_id'], @@ -211,7 +193,7 @@ def _update_expect_response(self, request, response): response['peer_ep_group_id'], response['peer_id'], response['psk'], - response['tenant_id'], + response['project_id'], response['route_mode'], response['admin_state_up'], response['status'], @@ -268,6 +250,18 @@ def _set_all_params(self, args={}): def _test_create_with_all_params(self, args={}): arglist, verifylist = self._set_all_params(args) request, response = _generate_req_and_res(verifylist) + + def _mock_endpoint_group(*args, **kwargs): + return {'id': args[0]} + + self.networkclient.find_vpn_endpoint_group.side_effect = mock.Mock( + side_effect=_mock_endpoint_group) + self.networkclient.find_vpn_service.side_effect = mock.Mock( + side_effect=_mock_endpoint_group) + self.networkclient.find_vpn_ike_policy.side_effect = mock.Mock( + side_effect=_mock_endpoint_group) + self.networkclient.find_vpn_ipsec_policy.side_effect = mock.Mock( + side_effect=_mock_endpoint_group) self._update_expect_response(request, response) parsed_args = self.check_parser(self.cmd, arglist, verifylist) headers, data = self.cmd.take_action(parsed_args) @@ -289,9 +283,8 @@ class TestDeleteIPsecSiteConn(TestIPsecSiteConn, common.TestDeleteVPNaaS): def setUp(self): super(TestDeleteIPsecSiteConn, self).setUp() - self.neutronclient.delete_ipsec_site_connection = mock.Mock( - return_value={self.res: _ipsec_site_conn}) - self.mocked = self.neutronclient.delete_ipsec_site_connection + self.networkclient.delete_vpn_ipsec_site_connection = mock.Mock() + self.mocked = self.networkclient.delete_vpn_ipsec_site_connection self.cmd = ipsec_site_connection.DeleteIPsecSiteConnection( self.app, self.namespace) @@ -319,9 +312,9 @@ def setUp(self): _ipsec_site_conn['status'], ) - self.neutronclient.list_ipsec_site_connections = mock.Mock( - return_value={self.res_plural: [_ipsec_site_conn]}) - self.mocked = self.neutronclient.list_ipsec_site_connections + self.networkclient.vpn_ipsec_site_connections = mock.Mock( + return_value=[_ipsec_site_conn]) + self.mocked = self.networkclient.vpn_ipsec_site_connections def test_list_with_long_option(self): arglist = ['--long'] @@ -331,7 +324,6 @@ def test_list_with_long_option(self): self.mocked.assert_called_once_with() self.assertEqual(list(self.headers), headers) - self.assertListItemEqual([self.data], list(data)) def test_list_with_no_option(self): arglist = [] @@ -348,9 +340,9 @@ class TestSetIPsecSiteConn(TestIPsecSiteConn, common.TestSetVPNaaS): def setUp(self): super(TestSetIPsecSiteConn, self).setUp() - self.neutronclient.update_ipsec_site_connection = mock.Mock( - return_value={self.res: _ipsec_site_conn}) - self.mocked = self.neutronclient.update_ipsec_site_connection + self.networkclient.update_vpn_ipsec_site_connection = mock.Mock( + return_value=_ipsec_site_conn) + self.mocked = self.networkclient.update_vpn_ipsec_site_connection self.cmd = ipsec_site_connection.SetIPsecSiteConnection( self.app, self.namespace) @@ -365,8 +357,7 @@ def test_set_ipsec_site_conn_with_peer_id(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.mocked.assert_called_once_with( - target, {self.res: {'peer_id': peer_id}}) + self.mocked.assert_called_once_with(target, **{'peer_id': peer_id}) self.assertIsNone(result) @@ -374,8 +365,8 @@ class TestShowIPsecSiteConn(TestIPsecSiteConn, common.TestShowVPNaaS): def setUp(self): super(TestShowIPsecSiteConn, self).setUp() - self.neutronclient.show_ipsec_site_connection = mock.Mock( - return_value={self.res: _ipsec_site_conn}) - self.mocked = self.neutronclient.show_ipsec_site_connection + self.networkclient.get_vpn_ipsec_site_connection = mock.Mock( + return_value=_ipsec_site_conn) + self.mocked = self.networkclient.get_vpn_ipsec_site_connection self.cmd = ipsec_site_connection.ShowIPsecSiteConnection( self.app, self.namespace) diff --git a/neutronclient/tests/unit/osc/v2/vpnaas/test_ipsecpolicy.py b/neutronclient/tests/unit/osc/v2/vpnaas/test_ipsecpolicy.py index 0df98e3f3..3729a11fe 100644 --- a/neutronclient/tests/unit/osc/v2/vpnaas/test_ipsecpolicy.py +++ b/neutronclient/tests/unit/osc/v2/vpnaas/test_ipsecpolicy.py @@ -14,7 +14,6 @@ # under the License. # -import copy from unittest import mock from osc_lib.tests import utils as tests_utils @@ -28,7 +27,7 @@ _ipsecpolicy = fakes.IPSecPolicy().create() CONVERT_MAP = { - 'project': 'tenant_id', + 'project': 'project_id', } @@ -36,12 +35,12 @@ def _generate_data(ordered_dict=None, data=None): source = ordered_dict if ordered_dict else _ipsecpolicy if data: source.update(data) - return tuple(source[key] for key in source) + return source def _generate_req_and_res(verifylist): request = dict(verifylist) - response = copy.deepcopy(_ipsecpolicy) + response = _ipsecpolicy for key, val in verifylist: converted = CONVERT_MAP.get(key, key) del request[key] @@ -55,10 +54,10 @@ class TestIPSecPolicy(test_fakes.TestNeutronClientOSCV2): def check_results(self, headers, data, exp_req, is_list=False): if is_list: - req_body = {self.res_plural: [exp_req]} + req_body = {self.res_plural: list(exp_req)} else: - req_body = {self.res: exp_req} - self.mocked.assert_called_once_with(req_body) + req_body = exp_req + self.mocked.assert_called_once_with(**req_body) self.assertEqual(self.ordered_headers, headers) self.assertEqual(self.ordered_data, data) @@ -66,14 +65,14 @@ def setUp(self): super(TestIPSecPolicy, self).setUp() def _mock_ipsecpolicy(*args, **kwargs): - self.neutronclient.find_resource.assert_called_once_with( - self.res, self.resource['id'], cmd_resource='ipsecpolicy') - return {'id': args[1]} + self.networkclient.find_vpn_ipsec_policy.assert_called_once_with( + self.resource['id'], ignore_missing=False) + return {'id': args[0]} - self.neutronclient.find_resource.side_effect = mock.Mock( + self.networkclient.find_vpn_ipsec_policy.side_effect = mock.Mock( side_effect=_mock_ipsecpolicy) osc_utils.find_project = mock.Mock() - osc_utils.find_project.id = _ipsecpolicy['tenant_id'] + osc_utils.find_project.id = _ipsecpolicy['project_id'] self.res = 'ipsecpolicy' self.res_plural = 'ipsecpolicies' self.resource = _ipsecpolicy @@ -111,7 +110,7 @@ def _mock_ipsecpolicy(*args, **kwargs): _ipsecpolicy['lifetime'], _ipsecpolicy['name'], _ipsecpolicy['pfs'], - _ipsecpolicy['tenant_id'], + _ipsecpolicy['project_id'], _ipsecpolicy['transform_protocol'], ) self.ordered_columns = ( @@ -123,7 +122,7 @@ def _mock_ipsecpolicy(*args, **kwargs): 'lifetime', 'name', 'pfs', - 'tenant_id', + 'project_id', 'transform_protocol', ) @@ -132,9 +131,9 @@ class TestCreateIPSecPolicy(TestIPSecPolicy, common.TestCreateVPNaaS): def setUp(self): super(TestCreateIPSecPolicy, self).setUp() - self.neutronclient.create_ipsecpolicy = mock.Mock( - return_value={self.res: _ipsecpolicy}) - self.mocked = self.neutronclient.create_ipsecpolicy + self.networkclient.create_vpn_ipsec_policy = mock.Mock( + return_value=_ipsecpolicy) + self.mocked = self.networkclient.create_vpn_ipsec_policy self.cmd = ipsecpolicy.CreateIPsecPolicy(self.app, self.namespace) def _update_expect_response(self, request, response): @@ -146,9 +145,9 @@ def _update_expect_response(self, request, response): A OrderedDict of request body """ # Update response body - self.neutronclient.create_ipsecpolicy.return_value = \ - {self.res: dict(response)} - osc_utils.find_project.return_value.id = response['tenant_id'] + self.networkclient.create_vpn_ipsec_policy.return_value = \ + response + osc_utils.find_project.return_value.id = response['project_id'] # Update response(finally returns 'data') self.data = _generate_data(ordered_dict=response) self.ordered_data = tuple( @@ -163,7 +162,7 @@ def _set_all_params(self, args={}): encryption_algorithm = args.get('encryption_algorithm') or 'aes-128' pfs = args.get('pfs') or 'group5' description = args.get('description') or 'my-desc' - tenant_id = args.get('tenant_id') or 'my-tenant' + project_id = args.get('project_id') or 'my-project' arglist = [ name, '--auth-algorithm', auth_algorithm, @@ -172,7 +171,7 @@ def _set_all_params(self, args={}): '--encryption-algorithm', encryption_algorithm, '--pfs', pfs, '--description', description, - '--project', tenant_id, + '--project', project_id, ] verifylist = [ ('name', name), @@ -182,7 +181,7 @@ def _set_all_params(self, args={}): ('encryption_algorithm', encryption_algorithm), ('pfs', pfs), ('description', description), - ('project', tenant_id), + ('project', project_id), ] return arglist, verifylist @@ -192,7 +191,6 @@ def _test_create_with_all_params(self, args={}): self._update_expect_response(request, response) parsed_args = self.check_parser(self.cmd, arglist, verifylist) headers, data = self.cmd.take_action(parsed_args) - self.check_results(headers, data, request) def test_create_with_no_options(self): @@ -213,9 +211,8 @@ class TestDeleteIPSecPolicy(TestIPSecPolicy, common.TestDeleteVPNaaS): def setUp(self): super(TestDeleteIPSecPolicy, self).setUp() - self.neutronclient.delete_ipsecpolicy = mock.Mock( - return_value={self.res: _ipsecpolicy}) - self.mocked = self.neutronclient.delete_ipsecpolicy + self.networkclient.delete_vpn_ipsec_policy = mock.Mock() + self.mocked = self.networkclient.delete_vpn_ipsec_policy self.cmd = ipsecpolicy.DeleteIPsecPolicy(self.app, self.namespace) @@ -243,9 +240,9 @@ def setUp(self): _ipsecpolicy['encryption_algorithm'], ) - self.neutronclient.list_ipsecpolicies = mock.Mock( - return_value={self.res_plural: [_ipsecpolicy]}) - self.mocked = self.neutronclient.list_ipsecpolicies + self.networkclient.vpn_ipsec_policies = mock.Mock( + return_value=[_ipsecpolicy]) + self.mocked = self.networkclient.vpn_ipsec_policies def test_list_with_long_option(self): arglist = ['--long'] @@ -255,7 +252,6 @@ def test_list_with_long_option(self): self.mocked.assert_called_once_with() self.assertEqual(list(self.headers), headers) - self.assertEqual([self.data], list(data)) def test_list_with_no_option(self): arglist = [] @@ -272,9 +268,9 @@ class TestSetIPSecPolicy(TestIPSecPolicy, common.TestSetVPNaaS): def setUp(self): super(TestSetIPSecPolicy, self).setUp() - self.neutronclient.update_ipsecpolicy = mock.Mock( - return_value={self.res: _ipsecpolicy}) - self.mocked = self.neutronclient.update_ipsecpolicy + self.networkclient.update_vpn_ipsec_policy = mock.Mock( + return_value=_ipsecpolicy) + self.mocked = self.networkclient.update_vpn_ipsec_policy self.cmd = ipsecpolicy.SetIPsecPolicy(self.app, self.namespace) def test_set_auth_algorithm_with_sha256(self): @@ -289,7 +285,7 @@ def test_set_auth_algorithm_with_sha256(self): result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'auth_algorithm': 'sha256'}}) + target, **{'auth_algorithm': 'sha256'}) self.assertIsNone(result) @@ -297,7 +293,7 @@ class TestShowIPSecPolicy(TestIPSecPolicy, common.TestShowVPNaaS): def setUp(self): super(TestShowIPSecPolicy, self).setUp() - self.neutronclient.show_ipsecpolicy = mock.Mock( - return_value={self.res: _ipsecpolicy}) - self.mocked = self.neutronclient.show_ipsecpolicy + self.networkclient.get_vpn_ipsec_policy = mock.Mock( + return_value=_ipsecpolicy) + self.mocked = self.networkclient.get_vpn_ipsec_policy self.cmd = ipsecpolicy.ShowIPsecPolicy(self.app, self.namespace) diff --git a/neutronclient/tests/unit/osc/v2/vpnaas/test_vpnservice.py b/neutronclient/tests/unit/osc/v2/vpnaas/test_vpnservice.py index 7630eb8e0..458c7f654 100644 --- a/neutronclient/tests/unit/osc/v2/vpnaas/test_vpnservice.py +++ b/neutronclient/tests/unit/osc/v2/vpnaas/test_vpnservice.py @@ -14,7 +14,6 @@ # under the License. # -import copy from unittest import mock import uuid @@ -28,7 +27,7 @@ _vpnservice = fakes.VPNService().create() CONVERT_MAP = { - 'project': 'tenant_id', + 'project': 'project_id', 'router': 'router_id', 'subnet': 'subnet_id' } @@ -38,12 +37,12 @@ def _generate_data(ordered_dict=None, data=None): source = ordered_dict if ordered_dict else _vpnservice if data: source.update(data) - return tuple(source[key] for key in source) + return source def _generate_req_and_res(verifylist): request = dict(verifylist) - response = copy.deepcopy(_vpnservice) + response = _vpnservice for key, val in verifylist: converted = CONVERT_MAP.get(key, key) del request[key] @@ -57,10 +56,10 @@ class TestVPNService(test_fakes.TestNeutronClientOSCV2): def _check_results(self, headers, data, exp_req, is_list=False): if is_list: - req_body = {self.res_plural: [exp_req]} + req_body = {self.res_plural: list(exp_req)} else: - req_body = {self.res: exp_req} - self.mocked.assert_called_once_with(req_body) + req_body = exp_req + self.mocked.assert_called_once_with(**req_body) self.assertEqual(self.ordered_headers, headers) self.assertEqual(self.ordered_data, data) @@ -68,23 +67,20 @@ def setUp(self): super(TestVPNService, self).setUp() def _mock_vpnservice(*args, **kwargs): - self.neutronclient.find_resource.assert_called_once_with( - self.res, self.resource['id'], cmd_resource='vpnservice') - return {'id': args[1]} + self.networkclient.find_vpn_service.assert_called_once_with( + self.resource['id'], ignore_missing=False) + return {'id': args[0]} - self.app.client_manager.network = mock.Mock() - self.app.client_manager.network.find_router = mock.Mock() - self.app.client_manager.network.find_subnet = mock.Mock() + self.networkclient.find_router = mock.Mock() + self.networkclient.find_subnet = mock.Mock() self.fake_router = mock.Mock() self.fake_subnet = mock.Mock() - self.app.client_manager.network.find_router.return_value = \ - self.fake_router - self.app.client_manager.network.find_subnet.return_value = \ - self.fake_subnet + self.networkclient.find_router.return_value = self.fake_router + self.networkclient.find_subnet.return_value = self.fake_subnet self.args = { 'name': 'my-name', 'description': 'my-desc', - 'tenant_id': 'tenant-id-' + uuid.uuid4().hex, + 'project_id': 'project-id-' + uuid.uuid4().hex, 'router_id': 'router-id-' + uuid.uuid4().hex, 'subnet_id': 'subnet-id-' + uuid.uuid4().hex, @@ -92,10 +88,10 @@ def _mock_vpnservice(*args, **kwargs): self.fake_subnet.id = self.args['subnet_id'] self.fake_router.id = self.args['router_id'] - self.neutronclient.find_resource.side_effect = mock.Mock( + self.networkclient.find_vpn_service.side_effect = mock.Mock( side_effect=_mock_vpnservice) osc_utils.find_project = mock.Mock() - osc_utils.find_project.id = _vpnservice['tenant_id'] + osc_utils.find_project.id = _vpnservice['project_id'] self.res = 'vpnservice' self.res_plural = 'vpnservices' @@ -128,7 +124,7 @@ def _mock_vpnservice(*args, **kwargs): _vpnservice['flavor_id'], _vpnservice['id'], _vpnservice['name'], - _vpnservice['tenant_id'], + _vpnservice['project_id'], _vpnservice['router_id'], _vpnservice['admin_state_up'], _vpnservice['status'], @@ -139,7 +135,7 @@ def _mock_vpnservice(*args, **kwargs): 'flavor_id', 'id', 'name', - 'tenant_id', + 'project_id', 'router_id', 'admin_state_up', 'status', @@ -151,9 +147,9 @@ class TestCreateVPNService(TestVPNService, common.TestCreateVPNaaS): def setUp(self): super(TestCreateVPNService, self).setUp() - self.neutronclient.create_vpnservice = mock.Mock( - return_value={self.res: _vpnservice}) - self.mocked = self.neutronclient.create_vpnservice + self.networkclient.create_vpn_service = mock.Mock( + return_value=_vpnservice) + self.mocked = self.networkclient.create_vpn_service self.cmd = vpnservice.CreateVPNService(self.app, self.namespace) def _update_expect_response(self, request, response): @@ -165,9 +161,8 @@ def _update_expect_response(self, request, response): A OrderedDict of request body """ # Update response body - self.neutronclient.create_vpnservice.return_value = \ - {self.res: dict(response)} - osc_utils.find_project.return_value.id = response['tenant_id'] + self.networkclient.create_vpn_service.return_value = response + osc_utils.find_project.return_value.id = response['project_id'] # Update response(finally returns 'data') self.data = _generate_data(ordered_dict=response) self.ordered_data = tuple( @@ -179,17 +174,17 @@ def _set_all_params(self): description = self.args.get('description') router_id = self.args.get('router_id') subnet_id = self.args.get('subnet_id') - tenant_id = self.args.get('tenant_id') + project_id = self.args.get('project_id') arglist = [ '--description', description, - '--project', tenant_id, + '--project', project_id, '--subnet', subnet_id, '--router', router_id, name, ] verifylist = [ ('description', description), - ('project', tenant_id), + ('project', project_id), ('subnet', subnet_id), ('router', router_id), ('name', name), @@ -213,9 +208,8 @@ class TestDeleteVPNService(TestVPNService, common.TestDeleteVPNaaS): def setUp(self): super(TestDeleteVPNService, self).setUp() - self.neutronclient.delete_vpnservice = mock.Mock( - return_value={self.res: _vpnservice}) - self.mocked = self.neutronclient.delete_vpnservice + self.networkclient.delete_vpn_service = mock.Mock() + self.mocked = self.networkclient.delete_vpn_service self.cmd = vpnservice.DeleteVPNService(self.app, self.namespace) @@ -245,9 +239,8 @@ def setUp(self): _vpnservice['status'], ) - self.neutronclient.list_vpnservices = mock.Mock( - return_value={self.res_plural: [_vpnservice]}) - self.mocked = self.neutronclient.list_vpnservices + self.networkclient.vpn_services = mock.Mock(return_value=[_vpnservice]) + self.mocked = self.networkclient.vpn_services def test_list_with_long_option(self): arglist = ['--long'] @@ -257,7 +250,6 @@ def test_list_with_long_option(self): self.mocked.assert_called_once_with() self.assertEqual(list(self.headers), headers) - self.assertEqual([self.data], list(data)) def test_list_with_no_option(self): arglist = [] @@ -274,9 +266,9 @@ class TestSetVPNService(TestVPNService, common.TestSetVPNaaS): def setUp(self): super(TestSetVPNService, self).setUp() - self.neutronclient.update_vpnservice = mock.Mock( - return_value={self.res: _vpnservice}) - self.mocked = self.neutronclient.update_vpnservice + self.networkclient.update_vpn_service = mock.Mock( + return_value=_vpnservice) + self.mocked = self.networkclient.update_vpn_service self.cmd = vpnservice.SetVPNSercice(self.app, self.namespace) def test_set_name(self): @@ -291,7 +283,7 @@ def test_set_name(self): result = self.cmd.take_action(parsed_args) self.mocked.assert_called_once_with( - target, {self.res: {'name': update}}) + target, **{'name': update}) self.assertIsNone(result) @@ -299,7 +291,7 @@ class TestShowVPNService(TestVPNService, common.TestShowVPNaaS): def setUp(self): super(TestShowVPNService, self).setUp() - self.neutronclient.show_vpnservice = mock.Mock( - return_value={self.res: _vpnservice}) - self.mocked = self.neutronclient.show_vpnservice + self.networkclient.get_vpn_service = mock.Mock( + return_value=_vpnservice) + self.mocked = self.networkclient.get_vpn_service self.cmd = vpnservice.ShowVPNService(self.app, self.namespace) From b9152a5042b107d7fcb9c84463e3760b3d02af40 Mon Sep 17 00:00:00 2001 From: elajkat Date: Thu, 29 Jun 2023 16:17:18 +0200 Subject: [PATCH 810/845] OSC: Remove SFC V2 calls to neutronclient Change SFC CLI to use SDK instead of deprecated neutronclient python bindings. Add possibility to delete multiple resources as it is common for other delete commands. Bump SDK minimum version to 1.5.0. Depends-On: https://review.opendev.org/c/openstack/openstacksdk/+/887387 Actually the new SDK release is the dependency. Related-Bug: #1999774 Change-Id: Ic22b8163155904113db8a4acf1fe7d75ae100a84 --- .../osc/v2/sfc/sfc_flow_classifier.py | 98 +++++--- neutronclient/osc/v2/sfc/sfc_port_chain.py | 147 +++++++----- neutronclient/osc/v2/sfc/sfc_port_pair.py | 89 ++++--- .../osc/v2/sfc/sfc_port_pair_group.py | 112 +++++---- neutronclient/osc/v2/sfc/sfc_service_graph.py | 81 ++++--- neutronclient/tests/unit/osc/v2/sfc/fakes.py | 62 +++-- .../unit/osc/v2/sfc/test_flow_classifier.py | 95 ++++---- .../tests/unit/osc/v2/sfc/test_port_chain.py | 224 +++++++----------- .../tests/unit/osc/v2/sfc/test_port_pair.py | 99 ++++---- .../unit/osc/v2/sfc/test_port_pair_group.py | 194 ++++++--------- .../unit/osc/v2/sfc/test_service_graph.py | 72 +++--- 11 files changed, 653 insertions(+), 620 deletions(-) diff --git a/neutronclient/osc/v2/sfc/sfc_flow_classifier.py b/neutronclient/osc/v2/sfc/sfc_flow_classifier.py index d223674c4..775730fc2 100755 --- a/neutronclient/osc/v2/sfc/sfc_flow_classifier.py +++ b/neutronclient/osc/v2/sfc/sfc_flow_classifier.py @@ -55,6 +55,26 @@ ('project_id', 'Project', column_util.LIST_LONG_ONLY), ) +_attr_map_dict = { + 'id': 'ID', + 'name': 'Name', + 'description': 'Description', + 'summary': 'Summary', + 'protocol': 'Protocol', + 'ethertype': 'Ethertype', + 'source_ip_prefix': 'Source IP', + 'destination_ip_prefix': 'Destination IP', + 'logical_source_port': 'Logical Source Port', + 'logical_destination_port': 'Logical Destination Port', + 'source_port_range_min': 'Source Port Range Min', + 'source_port_range_max': 'Source Port Range Max', + 'destination_port_range_min': 'Destination Port Range Min', + 'destination_port_range_max': 'Destination Port Range Max', + 'l7_parameters': 'L7 Parameters', + 'tenant_id': 'Project', + 'project_id': 'Project', +} + class CreateSfcFlowClassifier(command.ShowOne): _description = _("Create a flow classifier") @@ -114,11 +134,11 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network attrs = _get_common_attrs(self.app.client_manager, parsed_args) - body = {resource: attrs} - obj = client.create_sfc_flow_classifier(body)[resource] - columns, display_columns = column_util.get_columns(obj, _attr_map) + obj = client.create_sfc_flow_classifier(**attrs) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id', 'summary']) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -131,20 +151,27 @@ def get_parser(self, prog_name): parser.add_argument( 'flow_classifier', metavar='', - help=_("Flow classifier to delete (name or ID)") + nargs='+', + help=_("Flow classifier(s) to delete (name or ID)") ) return parser def take_action(self, parsed_args): - # TODO(mohan): Add support for deleting multiple resources. - client = self.app.client_manager.neutronclient - fc_id = _get_id(client, parsed_args.flow_classifier, resource) - try: - client.delete_sfc_flow_classifier(fc_id) - except Exception as e: - msg = (_("Failed to delete flow classifier with name " - "or ID '%(fc)s': %(e)s") - % {'fc': parsed_args.flow_classifier, 'e': e}) + client = self.app.client_manager.network + result = 0 + for fcl in parsed_args.flow_classifier: + try: + fc_id = client.find_sfc_flow_classifier( + fcl, ignore_missing=False)['id'] + client.delete_sfc_flow_classifier(fc_id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete flow classifier with name " + "or ID '%(fc)s': %(e)s"), {'fc': fcl, 'e': e}) + if result > 0: + total = len(parsed_args.flow_classifier) + msg = (_("%(result)s of %(total)s flow classifier(s) " + "failed to delete.") % {'result': result, 'total': total}) raise exceptions.CommandError(msg) @@ -161,8 +188,8 @@ def get_parser(self, prog_name): return parser def extend_list(self, data, parsed_args): - ext_data = data['flow_classifiers'] - for d in ext_data: + ext_data = [] + for d in data: val = [] protocol = d['protocol'].upper() if d['protocol'] else 'any' val.append('protocol: ' + protocol) @@ -180,6 +207,7 @@ def extend_list(self, data, parsed_args): l7_param = 'l7_parameters: {%s}' % ','.join(d['l7_parameters']) val.append(l7_param) d['summary'] = ',\n'.join(val) + ext_data.append(d) return ext_data def _get_protocol_port_details(self, data, val): @@ -197,8 +225,8 @@ def _get_protocol_port_details(self, data, val): val, ip_prefix, min_port, max_port) def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - obj = client.list_sfc_flow_classifiers() + client = self.app.client_manager.network + obj = client.sfc_flow_classifiers() obj_extend = self.extend_list(obj, parsed_args) headers, columns = column_util.get_column_definitions( _attr_map, long_listing=parsed_args.long) @@ -227,13 +255,13 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - fc_id = _get_id(client, parsed_args.flow_classifier, resource) + client = self.app.client_manager.network + fc_id = client.find_sfc_flow_classifier(parsed_args.flow_classifier, + ignore_missing=False)['id'] attrs = _get_common_attrs(self.app.client_manager, parsed_args, is_create=False) - body = {resource: attrs} try: - client.update_sfc_flow_classifier(fc_id, body) + client.update_sfc_flow_classifier(fc_id, **attrs) except Exception as e: msg = (_("Failed to update flow classifier '%(fc)s': %(e)s") % {'fc': parsed_args.flow_classifier, 'e': e}) @@ -253,10 +281,12 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - fc_id = _get_id(client, parsed_args.flow_classifier, resource) - obj = client.show_sfc_flow_classifier(fc_id)[resource] - columns, display_columns = column_util.get_columns(obj, _attr_map) + client = self.app.client_manager.network + fc_id = client.find_sfc_flow_classifier(parsed_args.flow_classifier, + ignore_missing=False)['id'] + obj = client.get_sfc_flow_classifier(fc_id) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id', 'summary']) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -282,13 +312,13 @@ def _get_attrs(client_manager, attrs, parsed_args): if parsed_args.destination_ip_prefix is not None: attrs['destination_ip_prefix'] = parsed_args.destination_ip_prefix if parsed_args.logical_source_port is not None: - attrs['logical_source_port'] = _get_id( - client_manager.neutronclient, parsed_args.logical_source_port, - 'port') + attrs['logical_source_port'] = client_manager.network.find_port( + parsed_args.logical_source_port, ignore_missing=False + )['id'] if parsed_args.logical_destination_port is not None: - attrs['logical_destination_port'] = _get_id( - client_manager.neutronclient, parsed_args.logical_destination_port, - 'port') + attrs['logical_destination_port'] = client_manager.network.find_port( + parsed_args.logical_destination_port, ignore_missing=False + )['id'] if parsed_args.source_port is not None: _fill_protocol_port_info(attrs, 'source', parsed_args.source_port) @@ -314,7 +344,3 @@ def _fill_protocol_port_info(attrs, port_type, port_val): message = (_("Protocol port value %s must be an integer " "or integer:integer.") % port_val) raise nc_exc.CommandError(message=message) - - -def _get_id(client, id_or_name, resource): - return client.find_resource(resource, id_or_name)['id'] diff --git a/neutronclient/osc/v2/sfc/sfc_port_chain.py b/neutronclient/osc/v2/sfc/sfc_port_chain.py index 4ce790dc3..d96007b85 100755 --- a/neutronclient/osc/v2/sfc/sfc_port_chain.py +++ b/neutronclient/osc/v2/sfc/sfc_port_chain.py @@ -36,10 +36,20 @@ ('chain_parameters', 'Chain Parameters', column_util.LIST_BOTH), ('description', 'Description', column_util.LIST_LONG_ONLY), - ('chain_id', 'Chain ID', column_util.LIST_BOTH), ('project_id', 'Project', column_util.LIST_LONG_ONLY), ) +_attr_map_dict = { + 'id': 'ID', + 'name': 'Name', + 'port_pair_groups': 'Port Pair Groups', + 'flow_classifiers': 'Flow Classifiers', + 'chain_parameters': 'Chain Parameters', + 'description': 'Description', + 'tenant_id': 'Project', + 'project_id': 'Project', +} + class CreateSfcPortChain(command.ShowOne): _description = _("Create a port chain") @@ -81,11 +91,11 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network attrs = _get_common_attrs(self.app.client_manager, parsed_args) - body = {resource: attrs} - obj = client.create_sfc_port_chain(body)[resource] - columns, display_columns = column_util.get_columns(obj, _attr_map) + obj = client.create_sfc_port_chain(**attrs) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id']) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -98,20 +108,28 @@ def get_parser(self, prog_name): parser.add_argument( 'port_chain', metavar="", - help=_("Port chain to delete (name or ID)") + nargs='+', + help=_("Port chain(s) to delete (name or ID)") ) return parser def take_action(self, parsed_args): - # TODO(mohan): Add support for deleting multiple resources. - client = self.app.client_manager.neutronclient - pc_id = _get_id(client, parsed_args.port_chain, resource) - try: - client.delete_sfc_port_chain(pc_id) - except Exception as e: - msg = (_("Failed to delete port chain with name " - "or ID '%(pc)s': %(e)s") - % {'pc': parsed_args.port_chain, 'e': e}) + client = self.app.client_manager.network + result = 0 + for pc in parsed_args.port_chain: + try: + pc_id = client.find_sfc_port_chain( + pc, ignore_missing=False)['id'] + client.delete_sfc_port_chain(pc_id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete port chain with name " + "or ID '%(pc)s': %(e)s"), {'pc': pc, 'e': e}) + if result > 0: + total = len(parsed_args.port_chain) + msg = (_("%(result)s of %(total)s port chain(s) " + "failed to delete.") % {'result': result, + 'total': total}) raise exceptions.CommandError(msg) @@ -129,13 +147,13 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - data = client.list_sfc_port_chains() + client = self.app.client_manager.network + data = client.sfc_port_chains() headers, columns = column_util.get_column_definitions( _attr_map, long_listing=parsed_args.long) return (headers, (utils.get_dict_properties(s, columns) - for s in data['port_chains'])) + for s in data)) class SetSfcPortChain(command.Command): @@ -184,8 +202,9 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - pc_id = _get_id(client, parsed_args.port_chain, resource) + client = self.app.client_manager.network + pc_id = client.find_sfc_port_chain(parsed_args.port_chain, + ignore_missing=False)['id'] attrs = _get_common_attrs(self.app.client_manager, parsed_args, is_create=False) if parsed_args.no_flow_classifier: @@ -194,13 +213,14 @@ def take_action(self, parsed_args): if parsed_args.no_flow_classifier: fc_list = [] else: - fc_list = client.find_resource( - resource, parsed_args.port_chain, - cmd_resource='sfc_port_chain')['flow_classifiers'] + fc_list = client.find_sfc_port_chain( + parsed_args.port_chain, + ignore_missing=False + )['flow_classifiers'] for fc in parsed_args.flow_classifiers: - fc_id = client.find_resource( - 'flow_classifier', fc, - cmd_resource='sfc_flow_classifier')['id'] + fc_id = client.find_sfc_flow_classifier( + fc, + ignore_missing=False)['id'] if fc_id not in fc_list: fc_list.append(fc_id) attrs['flow_classifiers'] = fc_list @@ -211,27 +231,25 @@ def take_action(self, parsed_args): if parsed_args.no_port_pair_group and parsed_args.port_pair_groups: ppg_list = [] for ppg in parsed_args.port_pair_groups: - ppg_id = client.find_resource( - 'port_pair_group', ppg, - cmd_resource='sfc_port_pair_group')['id'] + ppg_id = client.find_sfc_port_pair_group( + ppg, ignore_missing=False)['id'] if ppg_id not in ppg_list: ppg_list.append(ppg_id) attrs['port_pair_groups'] = ppg_list if (parsed_args.port_pair_groups and not parsed_args.no_port_pair_group): - ppg_list = client.find_resource( - resource, parsed_args.port_chain, - cmd_resource='sfc_port_chain')['port_pair_groups'] + ppg_list = client.find_sfc_port_chain( + parsed_args.port_chain, + ignore_missing=False + )['port_pair_groups'] for ppg in parsed_args.port_pair_groups: - ppg_id = client.find_resource( - 'port_pair_group', ppg, - cmd_resource='sfc_port_pair_group')['id'] + ppg_id = client.find_sfc_port_pair_group( + ppg, ignore_missing=False)['id'] if ppg_id not in ppg_list: ppg_list.append(ppg_id) attrs['port_pair_groups'] = ppg_list - body = {resource: attrs} try: - client.update_sfc_port_chain(pc_id, body) + client.update_sfc_port_chain(pc_id, **attrs) except Exception as e: msg = (_("Failed to update port chain '%(pc)s': %(e)s") % {'pc': parsed_args.port_chain, 'e': e}) @@ -251,10 +269,12 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - pc_id = _get_id(client, parsed_args.port_chain, resource) - obj = client.show_sfc_port_chain(pc_id)[resource] - columns, display_columns = column_util.get_columns(obj, _attr_map) + client = self.app.client_manager.network + pc_id = client.find_sfc_port_chain(parsed_args.port_chain, + ignore_missing=False)['id'] + obj = client.get_sfc_port_chain(pc_id) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id']) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -290,30 +310,31 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - pc_id = _get_id(client, parsed_args.port_chain, resource) + client = self.app.client_manager.network + pc_id = client.find_sfc_port_chain(parsed_args.port_chain, + ignore_missing=False)['id'] attrs = {} if parsed_args.flow_classifiers: - fc_list = client.find_resource( - resource, parsed_args.port_chain, - cmd_resource='sfc_port_chain')['flow_classifiers'] + fc_list = client.find_sfc_port_chain( + parsed_args.port_chain, ignore_missing=False + )['flow_classifiers'] for fc in parsed_args.flow_classifiers: - fc_id = client.find_resource( - 'flow_classifier', fc, - cmd_resource='sfc_flow_classifier')['id'] + fc_id = client.find_sfc_flow_classifier( + fc, + ignore_missing=False)['id'] if fc_id in fc_list: fc_list.remove(fc_id) attrs['flow_classifiers'] = fc_list if parsed_args.all_flow_classifier: attrs['flow_classifiers'] = [] if parsed_args.port_pair_groups: - ppg_list = client.find_resource( - resource, parsed_args.port_chain, - cmd_resource='sfc_port_chain')['port_pair_groups'] + ppg_list = client.find_sfc_port_chain( + parsed_args.port_chain, + ignore_missing=False)['port_pair_groups'] for ppg in parsed_args.port_pair_groups: - ppg_id = client.find_resource( - 'port_pair_group', ppg, - cmd_resource='sfc_port_pair_group')['id'] + ppg_id = client.find_sfc_port_pair_group( + ppg, + ignore_missing=False)['id'] if ppg_id in ppg_list: ppg_list.remove(ppg_id) if ppg_list == []: @@ -321,9 +342,8 @@ def take_action(self, parsed_args): ' specified.') raise exceptions.CommandError(message) attrs['port_pair_groups'] = ppg_list - body = {resource: attrs} try: - client.update_sfc_port_chain(pc_id, body) + client.update_sfc_port_chain(pc_id, **attrs) except Exception as e: msg = (_("Failed to unset port chain '%(pc)s': %(e)s") % {'pc': parsed_args.port_chain, 'e': e}) @@ -332,17 +352,18 @@ def take_action(self, parsed_args): def _get_common_attrs(client_manager, parsed_args, is_create=True): attrs = {} + client = client_manager.network if parsed_args.name is not None: attrs['name'] = parsed_args.name if parsed_args.description is not None: attrs['description'] = parsed_args.description if parsed_args.port_pair_groups: - attrs['port_pair_groups'] = [(_get_id(client_manager.neutronclient, - ppg, 'port_pair_group')) + attrs['port_pair_groups'] = [client.find_sfc_port_pair_group( + ppg, ignore_missing=False)['id'] for ppg in parsed_args.port_pair_groups] if parsed_args.flow_classifiers: - attrs['flow_classifiers'] = [(_get_id(client_manager.neutronclient, fc, - 'flow_classifier')) + attrs['flow_classifiers'] = [client.find_sfc_flow_classifier( + fc, ignore_missing=False)['id'] for fc in parsed_args.flow_classifiers] if is_create is True: _get_attrs(attrs, parsed_args) @@ -358,7 +379,3 @@ def _get_attrs(attrs, parsed_args): if 'symmetric' in chain_param: chain_params['symmetric'] = chain_param['symmetric'] attrs['chain_parameters'] = chain_params - - -def _get_id(client, id_or_name, resource): - return client.find_resource(resource, id_or_name)['id'] diff --git a/neutronclient/osc/v2/sfc/sfc_port_pair.py b/neutronclient/osc/v2/sfc/sfc_port_pair.py index 68d131cdd..11e8e7631 100755 --- a/neutronclient/osc/v2/sfc/sfc_port_pair.py +++ b/neutronclient/osc/v2/sfc/sfc_port_pair.py @@ -38,6 +38,17 @@ ('project_id', 'Project', column_util.LIST_LONG_ONLY), ) +_attr_map_dict = { + 'id': 'ID', + 'name': 'Name', + 'description': 'Description', + 'ingress': 'Ingress Logical Port', + 'egress': 'Egress Logical Port', + 'service_function_parameters': 'Service Function Parameters', + 'tenant_id': 'Project', + 'project_id': 'Project', +} + class CreateSfcPortPair(command.ShowOne): _description = _("Create a port pair") @@ -76,11 +87,11 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network attrs = _get_common_attrs(self.app.client_manager, parsed_args) - body = {resource: attrs} - obj = client.create_sfc_port_pair(body)[resource] - columns, display_columns = column_util.get_columns(obj, _attr_map) + obj = client.create_sfc_port_pair(**attrs) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id']) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -93,20 +104,29 @@ def get_parser(self, prog_name): parser.add_argument( 'port_pair', metavar="", - help=_("Port pair to delete (name or ID)") + nargs='+', + help=_("Port pair(s) to delete (name or ID)") ) return parser def take_action(self, parsed_args): - # TODO(mohan): Add support for deleting multiple resources. - client = self.app.client_manager.neutronclient - port_pair_id = _get_id(client, parsed_args.port_pair, resource) - try: - client.delete_sfc_port_pair(port_pair_id) - except Exception as e: - msg = (_("Failed to delete port pair with name " - "or ID '%(port_pair)s': %(e)s") - % {'port_pair': parsed_args.port_pair, 'e': e}) + client = self.app.client_manager.network + result = 0 + for pp in parsed_args.port_pair: + try: + port_pair_id = client.find_sfc_port_pair( + pp, ignore_missing=False)['id'] + client.delete_sfc_port_pair(port_pair_id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete port pair with name " + "or ID '%(port_pair)s': %(e)s"), + {'port_pair': pp, 'e': e}) + if result > 0: + total = len(parsed_args.port_pair) + msg = (_("%(result)s of %(total)s port pair(s) " + "failed to delete.") % {'result': result, + 'total': total}) raise exceptions.CommandError(msg) @@ -123,14 +143,14 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - data = client.list_sfc_port_pairs() + client = self.app.client_manager.network + data = client.sfc_port_pairs() headers, columns = column_util.get_column_definitions( _attr_map, long_listing=parsed_args.long) return (headers, (utils.get_dict_properties( s, columns, - ) for s in data['port_pairs'])) + ) for s in data)) class SetSfcPortPair(command.Command): @@ -154,13 +174,14 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - port_pair_id = _get_id(client, parsed_args.port_pair, resource) + client = self.app.client_manager.network + port_pair_id = client.find_sfc_port_pair( + parsed_args.port_pair, ignore_missing=False + )['id'] attrs = _get_common_attrs(self.app.client_manager, parsed_args, is_create=False) - body = {resource: attrs} try: - client.update_sfc_port_pair(port_pair_id, body) + client.update_sfc_port_pair(port_pair_id, **attrs) except Exception as e: msg = (_("Failed to update port pair '%(port_pair)s': %(e)s") % {'port_pair': parsed_args.port_pair, 'e': e}) @@ -180,10 +201,13 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - port_pair_id = _get_id(client, parsed_args.port_pair, resource) - obj = client.show_sfc_port_pair(port_pair_id)[resource] - columns, display_columns = column_util.get_columns(obj, _attr_map) + client = self.app.client_manager.network + port_pair_id = client.find_sfc_port_pair( + parsed_args.port_pair, ignore_missing=False + )['id'] + obj = client.get_sfc_port_pair(port_pair_id) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id']) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -200,12 +224,15 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): def _get_attrs(client_manager, attrs, parsed_args): + client = client_manager.network if parsed_args.ingress is not None: - attrs['ingress'] = _get_id(client_manager.neutronclient, - parsed_args.ingress, 'port') + attrs['ingress'] = client.find_port( + parsed_args.ingress, ignore_missing=False + )['id'] if parsed_args.egress is not None: - attrs['egress'] = _get_id(client_manager.neutronclient, - parsed_args.egress, 'port') + attrs['egress'] = client.find_port( + parsed_args.egress, ignore_missing=False + )['id'] if parsed_args.service_function_parameters is not None: attrs['service_function_parameters'] = _get_service_function_params( parsed_args.service_function_parameters) @@ -222,7 +249,3 @@ def _get_service_function_params(sf_params): if 'weight' in sf_param: attrs['weight'] = sf_param['weight'] return attrs - - -def _get_id(client, id_or_name, resource): - return client.find_resource(resource, id_or_name)['id'] diff --git a/neutronclient/osc/v2/sfc/sfc_port_pair_group.py b/neutronclient/osc/v2/sfc/sfc_port_pair_group.py index 8e7fadd26..5ad3ebea7 100755 --- a/neutronclient/osc/v2/sfc/sfc_port_pair_group.py +++ b/neutronclient/osc/v2/sfc/sfc_port_pair_group.py @@ -34,11 +34,21 @@ ('port_pair_group_parameters', 'Port Pair Group Parameters', column_util.LIST_BOTH), ('description', 'Description', column_util.LIST_LONG_ONLY), - ('group_id', 'Loadbalance ID', column_util.LIST_LONG_ONLY), ('project_id', 'Project', column_util.LIST_LONG_ONLY), - ('tap_enabled', 'Tap Enabled', column_util.LIST_BOTH) + ('is_tap_enabled', 'Tap Enabled', column_util.LIST_BOTH) ) +_attr_map_dict = { + 'id': 'ID', + 'name': 'Name', + 'description': 'Description', + 'port_pairs': 'Port Pair', + 'port_pair_group_parameters': 'Port Pair Group Parameters', + 'is_tap_enabled': 'Tap Enabled', + 'tenant_id': 'Project', + 'project_id': 'Project', +} + class CreateSfcPortPairGroup(command.ShowOne): _description = _("Create a port pair group") @@ -85,11 +95,11 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network attrs = _get_common_attrs(self.app.client_manager, parsed_args) - body = {resource: attrs} - obj = client.create_sfc_port_pair_group(body)[resource] - columns, display_columns = column_util.get_columns(obj, _attr_map) + obj = client.create_sfc_port_pair_group(**attrs) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id']) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -102,20 +112,28 @@ def get_parser(self, prog_name): parser.add_argument( 'port_pair_group', metavar='', - help=_("Port pair group to delete (name or ID)") + nargs='+', + help=_("Port pair group(s) to delete (name or ID)") ) return parser def take_action(self, parsed_args): - # TODO(mohan): Add support for deleting multiple resources. - client = self.app.client_manager.neutronclient - ppg_id = _get_id(client, parsed_args.port_pair_group, resource) - try: - client.delete_sfc_port_pair_group(ppg_id) - except Exception as e: - msg = (_("Failed to delete port pair group with name " - "or ID '%(ppg)s': %(e)s") - % {'ppg': parsed_args.port_pair_group, 'e': e}) + client = self.app.client_manager.network + result = 0 + for ppg in parsed_args.port_pair_group: + try: + ppg_id = client.find_sfc_port_pair_group( + ppg, ignore_missing=False)['id'] + client.delete_sfc_port_pair_group(ppg_id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete port pair group with name " + "or ID '%(ppg)s': %(e)s"), {'ppg': ppg, 'e': e}) + if result > 0: + total = len(parsed_args.port_pair_group) + msg = (_("%(result)s of %(total)s port pair group(s) " + "failed to delete.") % {'result': result, + 'total': total}) raise exceptions.CommandError(msg) @@ -133,14 +151,14 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - data = client.list_sfc_port_pair_groups() + client = self.app.client_manager.network + data = client.sfc_port_pair_groups() headers, columns = column_util.get_column_definitions( _attr_map, long_listing=parsed_args.long) return (headers, (utils.get_dict_properties( s, columns, - ) for s in data['port_pair_groups'])) + ) for s in data)) class SetSfcPortPairGroup(command.Command): @@ -175,26 +193,26 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - ppg_id = _get_id(client, parsed_args.port_pair_group, resource) + client = self.app.client_manager.network + ppg_id = client.find_sfc_port_pair_group( + parsed_args.port_pair_group)['id'] attrs = _get_common_attrs(self.app.client_manager, parsed_args, is_create=False) if parsed_args.no_port_pair: attrs['port_pairs'] = [] if parsed_args.port_pairs: - added = [client.find_resource('port_pair', pp, - cmd_resource='sfc_port_pair')['id'] + added = [client.find_sfc_port_pair(pp, + ignore_missing=False)['id'] for pp in parsed_args.port_pairs] if parsed_args.no_port_pair: existing = [] else: - existing = client.find_resource( - resource, parsed_args.port_pair_group, - cmd_resource='sfc_port_pair_group')['port_pairs'] + existing = client.find_sfc_port_pair_group( + parsed_args.port_pair_group, + ignore_missing=False)['port_pairs'] attrs['port_pairs'] = sorted(list(set(existing) | set(added))) - body = {resource: attrs} try: - client.update_sfc_port_pair_group(ppg_id, body) + client.update_sfc_port_pair_group(ppg_id, **attrs) except Exception as e: msg = (_("Failed to update port pair group '%(ppg)s': %(e)s") % {'ppg': parsed_args.port_pair_group, 'e': e}) @@ -214,10 +232,12 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - ppg_id = _get_id(client, parsed_args.port_pair_group, resource) - obj = client.show_sfc_port_pair_group(ppg_id)[resource] - columns, display_columns = column_util.get_columns(obj, _attr_map) + client = self.app.client_manager.network + ppg_id = client.find_sfc_port_pair_group( + parsed_args.port_pair_group, ignore_missing=False)['id'] + obj = client.get_sfc_port_pair_group(ppg_id) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id']) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -246,22 +266,22 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - ppg_id = _get_id(client, parsed_args.port_pair_group, resource) + client = self.app.client_manager.network + ppg_id = client.find_sfc_port_pair_group( + parsed_args.port_pair_group, ignore_missing=False)['id'] attrs = {} if parsed_args.port_pairs: - existing = client.find_resource( - resource, parsed_args.port_pair_group, - cmd_resource='sfc_port_pair_group')['port_pairs'] - removed = [client.find_resource('port_pair', pp, - cmd_resource='sfc_port_pair')['id'] + existing = client.find_sfc_port_pair_group( + parsed_args.port_pair_group, + ignore_missing=False)['port_pairs'] + removed = [client.find_sfc_port_pair(pp, + ignore_missing=False)['id'] for pp in parsed_args.port_pairs] attrs['port_pairs'] = list(set(existing) - set(removed)) if parsed_args.all_port_pair: attrs['port_pairs'] = [] - body = {resource: attrs} try: - client.update_sfc_port_pair_group(ppg_id, body) + client.update_sfc_port_pair_group(ppg_id, **attrs) except Exception as e: msg = (_("Failed to unset port pair group '%(ppg)s': %(e)s") % {'ppg': parsed_args.port_pair_group, 'e': e}) @@ -280,15 +300,17 @@ def _get_ppg_param(attrs, ppg): def _get_common_attrs(client_manager, parsed_args, is_create=True): + client = client_manager.network attrs = {} if parsed_args.name is not None: attrs['name'] = parsed_args.name if parsed_args.description is not None: attrs['description'] = parsed_args.description if parsed_args.port_pairs: - attrs['port_pairs'] = [(_get_id(client_manager.neutronclient, pp, - 'port_pair')) + attrs['port_pairs'] = [client.find_sfc_port_pair( + pp, ignore_missing=False)['id'] for pp in parsed_args.port_pairs] + if is_create: _get_attrs(attrs, parsed_args) return attrs @@ -302,7 +324,3 @@ def _get_attrs(attrs, parsed_args): attrs['tap_enabled'] = True if parsed_args.disable_tap: attrs['tap_enabled'] = False - - -def _get_id(client, id_or_name, resource): - return client.find_resource(resource, id_or_name)['id'] diff --git a/neutronclient/osc/v2/sfc/sfc_service_graph.py b/neutronclient/osc/v2/sfc/sfc_service_graph.py index fc4772675..6c87d95fe 100644 --- a/neutronclient/osc/v2/sfc/sfc_service_graph.py +++ b/neutronclient/osc/v2/sfc/sfc_service_graph.py @@ -33,6 +33,15 @@ ('project_id', 'Project', column_util.LIST_LONG_ONLY), ) +_attr_map_dict = { + 'id': 'ID', + 'name': 'Name', + 'description': 'Description', + 'port_chains': 'Branching Points', + 'tenant_id': 'Project', + 'project_id': 'Project', +} + class CreateSfcServiceGraph(command.ShowOne): """Create a service graph.""" @@ -57,12 +66,13 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network attrs = _get_common_attrs(self.app.client_manager, parsed_args) try: - body = {resource: attrs} - obj = client.create_sfc_service_graph(body)[resource] - columns, display_columns = column_util.get_columns(obj, _attr_map) + obj = client.create_sfc_service_graph(**attrs) + display_columns, columns = \ + utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id']) data = utils.get_dict_properties(obj, columns) return display_columns, data except Exception as e: @@ -92,13 +102,13 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - service_graph_id = _get_id(client, parsed_args.service_graph, resource) + client = self.app.client_manager.network + service_graph_id = client.find_sfc_service_graph( + parsed_args.service_graph, ignore_missing=False)['id'] attrs = _get_common_attrs(self.app.client_manager, parsed_args, is_create=False) - body = {resource: attrs} try: - client.update_sfc_service_graph(service_graph_id, body) + client.update_sfc_service_graph(service_graph_id, **attrs) except Exception as e: msg = (_("Failed to update service graph " "'%(service_graph)s': %(e)s") @@ -114,14 +124,30 @@ def get_parser(self, prog_name): parser.add_argument( 'service_graph', metavar="", - help=_("ID or name of the service graph to delete.") + nargs='+', + help=_("ID or name of the service graph(s) to delete.") ) return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - id = _get_id(client, parsed_args.service_graph, resource) - client.delete_sfc_service_graph(id) + client = self.app.client_manager.network + result = 0 + for sg in parsed_args.service_graph: + try: + sg_id = client.find_sfc_service_graph( + sg, ignore_missing=False)['id'] + client.delete_sfc_service_graph(sg_id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete service graph with name " + "or ID '%(sg)s': %(e)s"), + {'sg': sg, 'e': e}) + if result > 0: + total = len(parsed_args.service_graph) + msg = (_("%(result)s of %(total)s service graph(s) " + "failed to delete.") % {'result': result, + 'total': total}) + raise exceptions.CommandError(msg) class ListSfcServiceGraph(command.Lister): @@ -138,13 +164,13 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - data = client.list_sfc_service_graphs() + client = self.app.client_manager.network + data = client.sfc_service_graphs() headers, columns = column_util.get_column_definitions( _attr_map, long_listing=parsed_args.long) return (headers, (utils.get_dict_properties(s, columns) - for s in data['service_graphs'])) + for s in data)) class ShowSfcServiceGraph(command.ShowOne): @@ -160,10 +186,12 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - sg_id = _get_id(client, parsed_args.service_graph, resource) - obj = client.show_sfc_service_graph(sg_id)[resource] - columns, display_columns = column_util.get_columns(obj, _attr_map) + client = self.app.client_manager.network + sg_id = client.find_sfc_service_graph(parsed_args.service_graph, + ignore_missing=False)['id'] + obj = client.get_sfc_service_graph(sg_id) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id']) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -179,10 +207,10 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): return attrs -def _validate_destination_chains(comma_split, attrs, client_manager, sc_): +def _validate_destination_chains(comma_split, attrs, client, sc_): for e in comma_split: if e != "": - dc_ = _get_id(client_manager.neutronclient, e, 'port_chain') + dc_ = client.find_sfc_port_chain(e, ignore_missing=False)['id'] attrs['port_chains'][sc_].append(dc_) if _check_cycle(attrs['port_chains'], sc_, dc_): raise(exceptions.CommandError( @@ -214,6 +242,7 @@ def _visit(graph, src, new_dest, new_src): def _get_attrs_for_create(client_manager, attrs, parsed_args): + client = client_manager.network if parsed_args.branching_points: attrs['port_chains'] = {} src_chain = None @@ -224,8 +253,8 @@ def _get_attrs_for_create(client_manager, attrs, parsed_args): "destination chain for each source chain.") colon_split = c.split(':') src_chain = colon_split.pop(0) - sc_ = _get_id(client_manager.neutronclient, - src_chain, 'port_chain') + sc_ = client.find_sfc_port_chain(src_chain, + ignore_missing=False)['id'] for i in colon_split: comma_split = i.split(',') unique = set(comma_split) @@ -240,8 +269,4 @@ def _get_attrs_for_create(client_manager, attrs, parsed_args): "use already ".format(src_chain)) attrs['port_chains'][sc_] = [] _validate_destination_chains( - comma_split, attrs, client_manager, sc_) - - -def _get_id(client, id_or_name, resource): - return client.find_resource(resource, id_or_name)['id'] + comma_split, attrs, client, sc_) diff --git a/neutronclient/tests/unit/osc/v2/sfc/fakes.py b/neutronclient/tests/unit/osc/v2/sfc/fakes.py index ba07107fd..390c67e4e 100755 --- a/neutronclient/tests/unit/osc/v2/sfc/fakes.py +++ b/neutronclient/tests/unit/osc/v2/sfc/fakes.py @@ -14,13 +14,18 @@ # under the License. import argparse -import copy from unittest import mock from osc_lib.tests import utils from oslo_utils import uuidutils +from openstack.network.v2 import sfc_flow_classifier as flow_classifier +from openstack.network.v2 import sfc_port_chain as port_chain +from openstack.network.v2 import sfc_port_pair as port_pair +from openstack.network.v2 import sfc_port_pair_group as port_pair_group +from openstack.network.v2 import sfc_service_graph as service_graph + class TestNeutronClientOSCV2(utils.TestCommand): @@ -28,12 +33,32 @@ def setUp(self): super(TestNeutronClientOSCV2, self).setUp() self.namespace = argparse.Namespace() self.app.client_manager.session = mock.Mock() - self.app.client_manager.neutronclient = mock.Mock() - self.neutronclient = self.app.client_manager.neutronclient - self.neutronclient.find_resource = mock.Mock( - side_effect=lambda resource, name_or_id, project_id=None, - cmd_resource=None, parent_id=None, fields=None: - {'id': name_or_id}) + self.app.client_manager.network = mock.Mock() + self.network = self.app.client_manager.network + self.network.find_sfc_flow_classifier = mock.Mock( + side_effect=lambda name_or_id, ignore_missing=False: + {'id': name_or_id} + ) + self.network.find_sfc_port_chain = mock.Mock( + side_effect=lambda name_or_id, ignore_missing=False: + {'id': name_or_id} + ) + self.network.find_sfc_port_pair = mock.Mock( + side_effect=lambda name_or_id, ignore_missing=False: + {'id': name_or_id} + ) + self.network.find_sfc_port_pair_group = mock.Mock( + side_effect=lambda name_or_id, ignore_missing=False: + {'id': name_or_id} + ) + self.network.find_sfc_service_graph = mock.Mock( + side_effect=lambda name_or_id, ignore_missing=False: + {'id': name_or_id} + ) + self.network.find_port = mock.Mock( + side_effect=lambda name_or_id, ignore_missing=False: + {'id': name_or_id} + ) class FakeSfcPortPair(object): @@ -58,13 +83,14 @@ def create_port_pair(attrs=None): 'id': uuidutils.generate_uuid(), 'ingress': uuidutils.generate_uuid(), 'name': 'port-pair-name', - 'service_function_parameters': 'correlation=None,weight=1', + 'service_function_parameters': [('correlation', None), + ('weight', 1)], 'project_id': uuidutils.generate_uuid(), } # Overwrite default attributes. port_pair_attrs.update(attrs) - return copy.deepcopy(port_pair_attrs) + return port_pair.SfcPortPair(**port_pair_attrs) @staticmethod def create_port_pairs(attrs=None, count=1): @@ -102,18 +128,16 @@ def create_port_pair_group(attrs=None): # Set default attributes. port_pair_group_attrs = { 'id': uuidutils.generate_uuid(), - 'group_id': uuidutils.generate_uuid(), 'name': 'port-pair-group-name', 'description': 'description', 'port_pairs': uuidutils.generate_uuid(), - 'port_pair_group_parameters': '{"lb_fields": []}', + 'port_pair_group_parameters': {"lb_fields": []}, 'project_id': uuidutils.generate_uuid(), 'tap_enabled': False } - # port_pair_group_attrs default attributes. port_pair_group_attrs.update(attrs) - return copy.deepcopy(port_pair_group_attrs) + return port_pair_group.SfcPortPairGroup(**port_pair_group_attrs) @staticmethod def create_port_pair_groups(attrs=None, count=1): @@ -164,10 +188,10 @@ def create_flow_classifier(attrs=None): 'source_port_range_max': '20', 'source_port_range_min': '10', 'project_id': uuidutils.generate_uuid(), - 'l7_parameters': '{}' + 'l7_parameters': {} } flow_classifier_attrs.update(attrs) - return copy.deepcopy(flow_classifier_attrs) + return flow_classifier.SfcFlowClassifier(**flow_classifier_attrs) @staticmethod def create_flow_classifiers(attrs=None, count=1): @@ -205,18 +229,16 @@ def create_port_chain(attrs=None): # Set default attributes. port_chain_attrs = { 'id': uuidutils.generate_uuid(), - 'chain_id': uuidutils.generate_uuid(), 'name': 'port-chain-name', 'description': 'description', 'port_pair_groups': uuidutils.generate_uuid(), 'flow_classifiers': uuidutils.generate_uuid(), - 'chain_parameters': '{"correlation": mpls}', + 'chain_parameters': {"correlation": "mpls", "symmetric": False}, 'project_id': uuidutils.generate_uuid(), } - # port_pair_group_attrs default attributes. port_chain_attrs.update(attrs) - return copy.deepcopy(port_chain_attrs) + return port_chain.SfcPortChain(**port_chain_attrs) @staticmethod def create_port_chains(attrs=None, count=1): @@ -260,7 +282,7 @@ def create_sfc_service_graph(attrs=None): } service_graph_attrs.update(attrs) - return copy.deepcopy(service_graph_attrs) + return service_graph.SfcServiceGraph(**service_graph_attrs) @staticmethod def create_sfc_service_graphs(attrs=None, count=1): diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_flow_classifier.py b/neutronclient/tests/unit/osc/v2/sfc/test_flow_classifier.py index 379f72767..6a9e691f9 100755 --- a/neutronclient/tests/unit/osc/v2/sfc/test_flow_classifier.py +++ b/neutronclient/tests/unit/osc/v2/sfc/test_flow_classifier.py @@ -15,15 +15,12 @@ from unittest import mock +from osc_lib import exceptions +import testtools + from neutronclient.osc.v2.sfc import sfc_flow_classifier from neutronclient.tests.unit.osc.v2.sfc import fakes -get_id = 'neutronclient.osc.v2.sfc.sfc_flow_classifier._get_id' - - -def _get_id(client, id_or_name, resource): - return id_or_name - class TestCreateSfcFlowClassifier(fakes.TestNeutronClientOSCV2): @@ -43,7 +40,8 @@ class TestCreateSfcFlowClassifier(fakes.TestNeutronClientOSCV2): 'Protocol', 'Source IP', 'Source Port Range Max', - 'Source Port Range Min') + 'Source Port Range Min', + 'Summary',) def get_data(self): return ( @@ -66,9 +64,8 @@ def get_data(self): def setUp(self): super(TestCreateSfcFlowClassifier, self).setUp() - mock.patch(get_id, new=_get_id).start() - self.neutronclient.create_sfc_flow_classifier = mock.Mock( - return_value={'flow_classifier': self._fc}) + self.network.create_sfc_flow_classifier = mock.Mock( + return_value=self._fc) self.data = self.get_data() # Get the command object to test @@ -90,14 +87,13 @@ def test_create_flow_classifier_default_options(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = (self.cmd.take_action(parsed_args)) - self.neutronclient.create_sfc_flow_classifier.assert_called_once_with({ - 'flow_classifier': { - 'name': self._fc['name'], - 'logical_source_port': self._fc['logical_source_port'], - 'ethertype': self._fc['ethertype']} - }) + self.network.create_sfc_flow_classifier.assert_called_once_with( + **{'name': self._fc['name'], + 'logical_source_port': self._fc['logical_source_port'], + 'ethertype': self._fc['ethertype'] + } + ) self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) def test_create_flow_classifier(self): arglist = [ @@ -129,8 +125,8 @@ def test_create_flow_classifier(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = (self.cmd.take_action(parsed_args)) - self.neutronclient.create_sfc_flow_classifier.assert_called_once_with({ - 'flow_classifier': { + self.network.create_sfc_flow_classifier.assert_called_once_with( + **{ 'name': self._fc['name'], 'description': self._fc['description'], 'ethertype': self._fc['ethertype'], @@ -142,9 +138,8 @@ def test_create_flow_classifier(self): self._fc['logical_destination_port'], 'l7_parameters': param } - }) + ) self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) class TestDeleteSfcFlowClassifier(fakes.TestNeutronClientOSCV2): @@ -154,20 +149,19 @@ class TestDeleteSfcFlowClassifier(fakes.TestNeutronClientOSCV2): def setUp(self): super(TestDeleteSfcFlowClassifier, self).setUp() - mock.patch(get_id, new=_get_id).start() - self.neutronclient.delete_sfc_flow_classifier = mock.Mock( + self.network.delete_sfc_flow_classifier = mock.Mock( return_value=None) self.cmd = sfc_flow_classifier.DeleteSfcFlowClassifier(self.app, self.namespace) def test_delete_flow_classifier(self): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network mock_flow_classifier_delete = client.delete_sfc_flow_classifier arglist = [ self._flow_classifier[0]['id'], ] verifylist = [ - ('flow_classifier', self._flow_classifier[0]['id']), + ('flow_classifier', [self._flow_classifier[0]['id']]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) @@ -175,6 +169,22 @@ def test_delete_flow_classifier(self): self._flow_classifier[0]['id']) self.assertIsNone(result) + def test_delete_multiple_flow_classifiers_with_exception(self): + client = self.app.client_manager.network + target1 = self._flow_classifier[0]['id'] + arglist = [target1] + verifylist = [('flow_classifier', [target1])] + + client.find_sfc_flow_classifier.side_effect = [ + target1, exceptions.CommandError + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + msg = "1 of 2 flow classifier(s) failed to delete." + with testtools.ExpectedException(exceptions.CommandError) as e: + self.cmd.take_action(parsed_args) + self.assertEqual(msg, str(e)) + class TestSetSfcFlowClassifier(fakes.TestNeutronClientOSCV2): _flow_classifier = fakes.FakeSfcFlowClassifier.create_flow_classifier() @@ -183,14 +193,13 @@ class TestSetSfcFlowClassifier(fakes.TestNeutronClientOSCV2): def setUp(self): super(TestSetSfcFlowClassifier, self).setUp() - mock.patch(get_id, new=_get_id).start() - self.neutronclient.update_sfc_flow_classifier = mock.Mock( + self.network.update_sfc_flow_classifier = mock.Mock( return_value=None) self.cmd = sfc_flow_classifier.SetSfcFlowClassifier(self.app, self.namespace) def test_set_flow_classifier(self): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network mock_flow_classifier_update = client.update_sfc_flow_classifier arglist = [ self._flow_classifier_name, @@ -206,11 +215,11 @@ def test_set_flow_classifier(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - attrs = {'flow_classifier': { + attrs = { 'name': 'name_updated', - 'description': 'desc_updated'}} + 'description': 'desc_updated'} mock_flow_classifier_update.assert_called_once_with( - self._flow_classifier_name, attrs) + self._flow_classifier_name, **attrs) self.assertIsNone(result) @@ -234,7 +243,7 @@ class TestShowSfcFlowClassifier(fakes.TestNeutronClientOSCV2): _fc['source_port_range_max'], _fc['source_port_range_min'] ) - _flow_classifier = {'flow_classifier': _fc} + _flow_classifier = _fc _flow_classifier_id = _fc['id'] columns = ('Description', 'Destination IP', @@ -250,12 +259,12 @@ class TestShowSfcFlowClassifier(fakes.TestNeutronClientOSCV2): 'Protocol', 'Source IP', 'Source Port Range Max', - 'Source Port Range Min') + 'Source Port Range Min', + 'Summary',) def setUp(self): super(TestShowSfcFlowClassifier, self).setUp() - mock.patch(get_id, new=_get_id).start() - self.neutronclient.show_sfc_flow_classifier = mock.Mock( + self.network.get_sfc_flow_classifier = mock.Mock( return_value=self._flow_classifier ) # Get the command object to test @@ -263,8 +272,8 @@ def setUp(self): self.namespace) def test_show_flow_classifier(self): - client = self.app.client_manager.neutronclient - mock_flow_classifier_show = client.show_sfc_flow_classifier + client = self.app.client_manager.network + mock_flow_classifier_show = client.get_sfc_flow_classifier arglist = [ self._flow_classifier_id, ] @@ -277,7 +286,6 @@ def test_show_flow_classifier(self): mock_flow_classifier_show.assert_called_once_with( self._flow_classifier_id) self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) class TestListSfcFlowClassifier(fakes.TestNeutronClientOSCV2): @@ -324,9 +332,8 @@ class TestListSfcFlowClassifier(fakes.TestNeutronClientOSCV2): def setUp(self): super(TestListSfcFlowClassifier, self).setUp() - mock.patch(get_id, new=_get_id).start() - self.neutronclient.list_sfc_flow_classifiers = mock.Mock( - return_value={'flow_classifiers': self._fc} + self.network.sfc_flow_classifiers = mock.Mock( + return_value=self._fc ) # Get the command object to test self.cmd = sfc_flow_classifier.ListSfcFlowClassifier(self.app, @@ -337,8 +344,7 @@ def test_list_flow_classifiers(self): verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns = self.cmd.take_action(parsed_args) - fcs = self.neutronclient \ - .list_sfc_flow_classifiers()['flow_classifiers'] + fcs = self.network.sfc_flow_classifiers() fc = fcs[0] data = [ fc['id'], @@ -355,8 +361,7 @@ def test_list_flow_classifiers(self): def test_list_with_long_option(self): arglist = ['--long'] verifylist = [('long', True)] - fcs = self.neutronclient \ - .list_sfc_flow_classifiers()['flow_classifiers'] + fcs = self.network.sfc_flow_classifiers() fc = fcs[0] data = [ fc['id'], diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py b/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py index da6455230..9b414c22c 100755 --- a/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py +++ b/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py @@ -16,6 +16,7 @@ from unittest import mock from osc_lib import exceptions +import testtools from neutronclient.osc.v2.sfc import sfc_port_chain from neutronclient.tests.unit.osc.v2.sfc import fakes @@ -29,8 +30,7 @@ class TestCreateSfcPortChain(fakes.TestNeutronClientOSCV2): # The new port_chain created _port_chain = fakes.FakeSfcPortChain.create_port_chain() - columns = ('Chain ID', - 'Chain Parameters', + columns = ('Chain Parameters', 'Description', 'Flow Classifiers', 'ID', @@ -40,7 +40,6 @@ class TestCreateSfcPortChain(fakes.TestNeutronClientOSCV2): def get_data(self): return ( - self._port_chain['chain_id'], self._port_chain['chain_parameters'], self._port_chain['description'], self._port_chain['flow_classifiers'], @@ -52,11 +51,8 @@ def get_data(self): def setUp(self): super(TestCreateSfcPortChain, self).setUp() - mock.patch( - 'neutronclient.osc.v2.sfc.sfc_port_chain._get_id', - new=_get_id).start() - self.neutronclient.create_sfc_port_chain = mock.Mock( - return_value={'port_chain': self._port_chain}) + self.network.create_sfc_port_chain = mock.Mock( + return_value=self._port_chain) self.data = self.get_data() # Get the command object to test @@ -77,11 +73,12 @@ def test_create_port_chain_default_options(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = (self.cmd.take_action(parsed_args)) - self.neutronclient.create_sfc_port_chain.assert_called_once_with({ - 'port_chain': { + self.network.create_sfc_port_chain.assert_called_once_with( + **{ 'name': self._port_chain['name'], - 'port_pair_groups': [self._port_chain['port_pair_groups']]} - }) + 'port_pair_groups': [self._port_chain['port_pair_groups']] + } + ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) @@ -107,15 +104,15 @@ def test_create_port_chain_all_options(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = (self.cmd.take_action(parsed_args)) - self.neutronclient.create_sfc_port_chain.assert_called_once_with({ - 'port_chain': { + self.network.create_sfc_port_chain.assert_called_once_with( + **{ 'name': self._port_chain['name'], 'port_pair_groups': [self._port_chain['port_pair_groups']], 'description': self._port_chain['description'], 'flow_classifiers': [self._port_chain['flow_classifiers']], 'chain_parameters': cp - } - }) + } + ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) @@ -126,20 +123,17 @@ class TestDeleteSfcPortChain(fakes.TestNeutronClientOSCV2): def setUp(self): super(TestDeleteSfcPortChain, self).setUp() - mock.patch( - 'neutronclient.osc.v2.sfc.sfc_port_chain._get_id', - new=_get_id).start() - self.neutronclient.delete_sfc_port_chain = mock.Mock(return_value=None) + self.network.delete_sfc_port_chain = mock.Mock(return_value=None) self.cmd = sfc_port_chain.DeleteSfcPortChain(self.app, self.namespace) def test_delete_port_chain(self): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network mock_port_chain_delete = client.delete_sfc_port_chain arglist = [ self._port_chain[0]['id'], ] verifylist = [ - ('port_chain', self._port_chain[0]['id']), + ('port_chain', [self._port_chain[0]['id']]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) @@ -147,13 +141,29 @@ def test_delete_port_chain(self): self._port_chain[0]['id']) self.assertIsNone(result) + def test_delete_multiple_port_chains_with_exception(self): + client = self.app.client_manager.network + target1 = self._port_chain[0]['id'] + arglist = [target1] + verifylist = [('port_chain', [target1])] + + client.find_sfc_port_chain.side_effect = [ + target1, exceptions.CommandError + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + msg = "1 of 2 port chain(s) failed to delete." + with testtools.ExpectedException(exceptions.CommandError) as e: + self.cmd.take_action(parsed_args) + self.assertEqual(msg, str(e)) + class TestListSfcPortChain(fakes.TestNeutronClientOSCV2): _port_chains = fakes.FakeSfcPortChain.create_port_chains(count=1) columns = ('ID', 'Name', 'Port Pair Groups', 'Flow Classifiers', - 'Chain Parameters', 'Chain ID') + 'Chain Parameters') columns_long = ('ID', 'Name', 'Port Pair Groups', 'Flow Classifiers', - 'Chain Parameters', 'Description', 'Chain ID', 'Project') + 'Chain Parameters', 'Description', 'Project') _port_chain = _port_chains[0] data = [ _port_chain['id'], @@ -161,13 +171,11 @@ class TestListSfcPortChain(fakes.TestNeutronClientOSCV2): _port_chain['port_pair_groups'], _port_chain['flow_classifiers'], _port_chain['chain_parameters'], - _port_chain['chain_id'] ] data_long = [ _port_chain['id'], _port_chain['name'], _port_chain['project_id'], - _port_chain['chain_id'], _port_chain['port_pair_groups'], _port_chain['flow_classifiers'], _port_chain['chain_parameters'], @@ -178,11 +186,8 @@ class TestListSfcPortChain(fakes.TestNeutronClientOSCV2): def setUp(self): super(TestListSfcPortChain, self).setUp() - mock.patch( - 'neutronclient.osc.v2.sfc.sfc_port_chain._get_id', - new=_get_id).start() - self.neutronclient.list_sfc_port_chains = mock.Mock( - return_value={'port_chains': self._port_chains} + self.network.sfc_port_chains = mock.Mock( + return_value=self._port_chains ) # Get the command object to test self.cmd = sfc_port_chain.ListSfcPortChain(self.app, self.namespace) @@ -192,7 +197,7 @@ def test_list_port_chains(self): verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns = self.cmd.take_action(parsed_args)[0] - pcs = self.neutronclient.list_sfc_port_chains()['port_chains'] + pcs = self.network.sfc_port_chains() pc = pcs[0] data = [ pc['id'], @@ -200,7 +205,6 @@ def test_list_port_chains(self): pc['port_pair_groups'], pc['flow_classifiers'], pc['chain_parameters'], - pc['chain_id'] ] self.assertEqual(list(self.columns), columns) self.assertEqual(self.data, data) @@ -210,13 +214,12 @@ def test_list_port_chain_with_long_opion(self): verifylist = [('long', True)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns = self.cmd.take_action(parsed_args)[0] - pcs = self.neutronclient.list_sfc_port_chains()['port_chains'] + pcs = self.network.sfc_port_chains() pc = pcs[0] data = [ pc['id'], pc['name'], pc['project_id'], - pc['chain_id'], pc['port_pair_groups'], pc['flow_classifiers'], pc['chain_parameters'], @@ -237,14 +240,11 @@ class TestSetSfcPortChain(fakes.TestNeutronClientOSCV2): def setUp(self): super(TestSetSfcPortChain, self).setUp() - mock.patch( - 'neutronclient.osc.v2.sfc.sfc_port_chain._get_id', - new=_get_id).start() - self.mocked = self.neutronclient.update_sfc_port_chain + self.mocked = self.network.update_sfc_port_chain self.cmd = sfc_port_chain.SetSfcPortChain(self.app, self.namespace) def test_set_port_chain(self): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network mock_port_chain_update = client.update_sfc_port_chain arglist = [ self._port_chain_name, @@ -258,10 +258,9 @@ def test_set_port_chain(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - attrs = {'port_chain': {'name': 'name_updated', - 'description': 'desc_updated'}} + attrs = {'name': 'name_updated', 'description': 'desc_updated'} mock_port_chain_update.assert_called_once_with(self._port_chain_name, - attrs) + **attrs) self.assertIsNone(result) def test_set_flow_classifiers(self): @@ -269,23 +268,12 @@ def test_set_flow_classifiers(self): fc1 = 'flow_classifier1' fc2 = 'flow_classifier2' - def _mock_flow_classifier(*args, **kwargs): - if self.neutronclient.find_resource.call_count == 1: - self.neutronclient.find_resource.assert_called_with( - self.res, target, cmd_resource='sfc_port_chain') - return {'flow_classifiers': [self.pc_fc]} - - if self.neutronclient.find_resource.call_count == 2: - self.neutronclient.find_resource.assert_called_with( - 'flow_classifier', fc1, cmd_resource='sfc_flow_classifier') - return {'id': args[1]} - - if self.neutronclient.find_resource.call_count == 3: - self.neutronclient.find_resource.assert_called_with( - 'flow_classifier', fc2, cmd_resource='sfc_flow_classifier') - return {'id': args[1]} - - self.neutronclient.find_resource.side_effect = _mock_flow_classifier + self.network.find_sfc_port_chain = mock.Mock( + side_effect=lambda name_or_id, ignore_missing=False: + {'id': name_or_id, 'flow_classifiers': [self.pc_fc]} + ) + self.network.find_sfc_flow_classifier.side_effect = \ + lambda name_or_id, ignore_missing=False: {'id': name_or_id} arglist = [ target, '--flow-classifier', fc1, @@ -298,12 +286,11 @@ def _mock_flow_classifier(*args, **kwargs): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) expect = {'flow_classifiers': [self.pc_fc, fc1, fc2]} - self.mocked.assert_called_once_with(target, {self.res: expect}) - self.assertEqual(3, self.neutronclient.find_resource.call_count) + self.mocked.assert_called_once_with(target, **expect) self.assertIsNone(result) def test_set_no_flow_classifier(self): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network mock_port_chain_update = client.update_sfc_port_chain arglist = [ self._port_chain_name, @@ -315,9 +302,9 @@ def test_set_no_flow_classifier(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - attrs = {'port_chain': {'flow_classifiers': []}} + attrs = {'flow_classifiers': []} mock_port_chain_update.assert_called_once_with(self._port_chain_name, - attrs) + **attrs) self.assertIsNone(result) def test_set_port_pair_groups(self): @@ -326,25 +313,10 @@ def test_set_port_pair_groups(self): ppg1 = 'port_pair_group1' ppg2 = 'port_pair_group2' - def _mock_flow_classifier(*args, **kwargs): - if self.neutronclient.find_resource.call_count == 1: - self.neutronclient.find_resource.assert_called_with( - self.res, target, cmd_resource='sfc_port_chain') - return {'port_pair_groups': [self.pc_ppg]} - - if self.neutronclient.find_resource.call_count == 2: - self.neutronclient.find_resource.assert_called_with( - 'port_pair_group', ppg1, - cmd_resource='sfc_port_pair_group') - return {'id': args[1]} - - if self.neutronclient.find_resource.call_count == 3: - self.neutronclient.find_resource.assert_called_with( - 'port_pair_group', ppg2, - cmd_resource='sfc_port_pair_group') - return {'id': args[1]} - - self.neutronclient.find_resource.side_effect = _mock_flow_classifier + self.network.find_sfc_port_chain = mock.Mock( + side_effect=lambda name_or_id, ignore_missing=False: + {'id': name_or_id, 'port_pair_groups': [self.pc_ppg]} + ) arglist = [ target, '--port-pair-group', ppg1, @@ -357,22 +329,13 @@ def _mock_flow_classifier(*args, **kwargs): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) expect = {'port_pair_groups': [existing_ppg, ppg1, ppg2]} - self.mocked.assert_called_once_with(target, {self.res: expect}) - self.assertEqual(3, self.neutronclient.find_resource.call_count) + self.mocked.assert_called_once_with(target, **expect) self.assertIsNone(result) def test_set_no_port_pair_group(self): target = self.resource['id'] ppg1 = 'port_pair_group1' - def _mock_port_pair_group(*args, **kwargs): - - if self.neutronclient.find_resource.call_count == 1: - self.neutronclient.find_resource.assert_called_with( - 'port_pair_group', ppg1, - cmd_resource='sfc_port_pair_group') - return {'id': args[1]} - self.neutronclient.find_resource.side_effect = _mock_port_pair_group arglist = [ target, '--no-port-pair-group', @@ -386,8 +349,7 @@ def _mock_port_pair_group(*args, **kwargs): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) expect = {'port_pair_groups': [ppg1]} - self.mocked.assert_called_once_with(target, {self.res: expect}) - self.assertEqual(1, self.neutronclient.find_resource.call_count) + self.mocked.assert_called_once_with(target, **expect) self.assertIsNone(result) def test_set_only_no_port_pair_group(self): @@ -409,7 +371,6 @@ class TestShowSfcPortChain(fakes.TestNeutronClientOSCV2): _pc = fakes.FakeSfcPortChain.create_port_chain() data = ( - _pc['chain_id'], _pc['chain_parameters'], _pc['description'], _pc['flow_classifiers'], @@ -418,10 +379,9 @@ class TestShowSfcPortChain(fakes.TestNeutronClientOSCV2): _pc['port_pair_groups'], _pc['project_id'] ) - _port_chain = {'port_chain': _pc} + _port_chain = _pc _port_chain_id = _pc['id'] - columns = ('Chain ID', - 'Chain Parameters', + columns = ('Chain Parameters', 'Description', 'Flow Classifiers', 'ID', @@ -431,18 +391,15 @@ class TestShowSfcPortChain(fakes.TestNeutronClientOSCV2): def setUp(self): super(TestShowSfcPortChain, self).setUp() - mock.patch( - 'neutronclient.osc.v2.sfc.sfc_port_chain._get_id', - new=_get_id).start() - self.neutronclient.show_sfc_port_chain = mock.Mock( + self.network.get_sfc_port_chain = mock.Mock( return_value=self._port_chain ) # Get the command object to test self.cmd = sfc_port_chain.ShowSfcPortChain(self.app, self.namespace) def test_show_port_chain(self): - client = self.app.client_manager.neutronclient - mock_port_chain_show = client.show_sfc_port_chain + client = self.app.client_manager.network + mock_port_chain_show = client.get_sfc_port_chain arglist = [ self._port_chain_id, ] @@ -468,34 +425,21 @@ class TestUnsetSfcPortChain(fakes.TestNeutronClientOSCV2): def setUp(self): super(TestUnsetSfcPortChain, self).setUp() - mock.patch( - 'neutronclient.osc.v2.sfc.sfc_port_chain._get_id', - new=_get_id).start() - self.neutronclient.update_sfc_port_chain = mock.Mock( + self.network.update_sfc_port_chain = mock.Mock( return_value=None) - self.mocked = self.neutronclient.update_sfc_port_chain + self.mocked = self.network.update_sfc_port_chain self.cmd = sfc_port_chain.UnsetSfcPortChain(self.app, self.namespace) def test_unset_port_pair_group(self): target = self.resource['id'] ppg1 = 'port_pair_group1' - def _mock_port_pair_group(*args, **kwargs): - if self.neutronclient.find_resource.call_count == 1: - self.neutronclient.find_resource.assert_called_with( - self.res, target, cmd_resource='sfc_port_chain') - return {'port_pair_groups': [self.pc_ppg]} - - if self.neutronclient.find_resource.call_count == 2: - self.neutronclient.find_resource.assert_called_with( - 'port_pair_group', ppg1, - cmd_resource='sfc_port_pair_group') - return {'id': args[1]} - if self.neutronclient.find_resource.call_count == 3: - self.neutronclient.find_resource.assert_called_with( - self.res, target, cmd_resource='sfc_port_chain') - return {'id': args[1]} - self.neutronclient.find_resource.side_effect = _mock_port_pair_group + self.network.find_sfc_port_chain = mock.Mock( + side_effect=lambda name_or_id, ignore_missing=False: + {'id': name_or_id, 'port_pair_groups': [self.pc_ppg]} + ) + self.network.find_sfc_port_pair_group.side_effect = \ + lambda name_or_id, ignore_missing=False: {'id': name_or_id} arglist = [ target, @@ -508,24 +452,18 @@ def _mock_port_pair_group(*args, **kwargs): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) expect = {'port_pair_groups': [self.pc_ppg]} - self.mocked.assert_called_once_with(target, {self.res: expect}) + self.mocked.assert_called_once_with(target, **expect) self.assertIsNone(result) def test_unset_flow_classifier(self): target = self.resource['id'] fc1 = 'flow_classifier1' - - def _mock_flow_classifier(*args, **kwargs): - if self.neutronclient.find_resource.call_count == 1: - self.neutronclient.find_resource.assert_called_with( - self.res, target, cmd_resource='sfc_port_chain') - return {'flow_classifiers': [self.pc_fc]} - - if self.neutronclient.find_resource.call_count == 2: - self.neutronclient.find_resource.assert_called_with( - 'flow_classifier', fc1, cmd_resource='sfc_flow_classifier') - return {'id': args[1]} - self.neutronclient.find_resource.side_effect = _mock_flow_classifier + self.network.find_sfc_port_chain = mock.Mock( + side_effect=lambda name_or_id, ignore_missing=False: + {'id': name_or_id, 'flow_classifiers': [self.pc_fc]} + ) + self.network.find_sfc_flow_classifier.side_effect = \ + lambda name_or_id, ignore_missing=False: {'id': name_or_id} arglist = [ target, @@ -538,11 +476,11 @@ def _mock_flow_classifier(*args, **kwargs): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) expect = {'flow_classifiers': [self.pc_fc]} - self.mocked.assert_called_once_with(target, {self.res: expect}) + self.mocked.assert_called_once_with(target, **expect) self.assertIsNone(result) def test_unset_all_flow_classifier(self): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network target = self.resource['id'] mock_port_chain_update = client.update_sfc_port_chain arglist = [ @@ -557,5 +495,5 @@ def test_unset_all_flow_classifier(self): result = self.cmd.take_action(parsed_args) expect = {'flow_classifiers': []} mock_port_chain_update.assert_called_once_with(target, - {self.res: expect}) + **expect) self.assertIsNone(result) diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_port_pair.py b/neutronclient/tests/unit/osc/v2/sfc/test_port_pair.py index 8246e35d9..e0610a360 100755 --- a/neutronclient/tests/unit/osc/v2/sfc/test_port_pair.py +++ b/neutronclient/tests/unit/osc/v2/sfc/test_port_pair.py @@ -15,14 +15,13 @@ from unittest import mock +from osc_lib import exceptions +import testtools + from neutronclient.osc.v2.sfc import sfc_port_pair from neutronclient.tests.unit.osc.v2.sfc import fakes -def _get_id(client, id_or_name, resource): - return id_or_name - - class TestCreateSfcPortPair(fakes.TestNeutronClientOSCV2): # The new port_pair created _port_pair = fakes.FakeSfcPortPair.create_port_pair() @@ -48,10 +47,8 @@ def get_data(self): def setUp(self): super(TestCreateSfcPortPair, self).setUp() - mock.patch('neutronclient.osc.v2.sfc.sfc_port_pair._get_id', - new=_get_id).start() - self.neutronclient.create_sfc_port_pair = mock.Mock( - return_value={'port_pair': self._port_pair}) + self.network.create_sfc_port_pair = mock.Mock( + return_value=self._port_pair) self.data = self.get_data() # Get the command object to test @@ -71,12 +68,11 @@ def test_create_port_pair_default_options(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = (self.cmd.take_action(parsed_args)) - self.neutronclient.create_sfc_port_pair.assert_called_once_with({ - 'port_pair': {'name': self._port_pair['name'], - 'ingress': self._port_pair['ingress'], - 'egress': self._port_pair['egress'], - } - }) + self.network.create_sfc_port_pair.assert_called_once_with( + **{'name': self._port_pair['name'], + 'ingress': self._port_pair['ingress'], + 'egress': self._port_pair['egress']} + ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) @@ -106,16 +102,14 @@ def _test_create_port_pair_all_options(self, correlation): correlation_param = None else: correlation_param = correlation - self.neutronclient.create_sfc_port_pair.assert_called_once_with({ - 'port_pair': {'name': self._port_pair['name'], - 'ingress': self._port_pair['ingress'], - 'egress': self._port_pair['egress'], - 'description': self._port_pair['description'], - 'service_function_parameters': - {'correlation': correlation_param, 'weight': - '1'}, - } - }) + self.network.create_sfc_port_pair.assert_called_once_with( + **{'name': self._port_pair['name'], + 'ingress': self._port_pair['ingress'], + 'egress': self._port_pair['egress'], + 'description': self._port_pair['description'], + 'service_function_parameters': + {'correlation': correlation_param, 'weight': '1'}} + ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) @@ -132,19 +126,17 @@ class TestDeleteSfcPortPair(fakes.TestNeutronClientOSCV2): def setUp(self): super(TestDeleteSfcPortPair, self).setUp() - mock.patch('neutronclient.osc.v2.sfc.sfc_port_pair._get_id', - new=_get_id).start() - self.neutronclient.delete_sfc_port_pair = mock.Mock(return_value=None) + self.network.delete_sfc_port_pair = mock.Mock(return_value=None) self.cmd = sfc_port_pair.DeleteSfcPortPair(self.app, self.namespace) def test_delete_port_pair(self): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network mock_port_pair_delete = client.delete_sfc_port_pair arglist = [ self._port_pair[0]['id'], ] verifylist = [ - ('port_pair', self._port_pair[0]['id']), + ('port_pair', [self._port_pair[0]['id']]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) @@ -152,6 +144,22 @@ def test_delete_port_pair(self): self._port_pair[0]['id']) self.assertIsNone(result) + def test_delete_multiple_port_pairs_with_exception(self): + client = self.app.client_manager.network + target1 = self._port_pair[0]['id'] + arglist = [target1] + verifylist = [('port_pair', [target1])] + + client.find_sfc_port_pair.side_effect = [ + target1, exceptions.CommandError + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + msg = "1 of 2 port pair(s) failed to delete." + with testtools.ExpectedException(exceptions.CommandError) as e: + self.cmd.take_action(parsed_args) + self.assertEqual(msg, str(e)) + class TestListSfcPortPair(fakes.TestNeutronClientOSCV2): _port_pairs = fakes.FakeSfcPortPair.create_port_pairs() @@ -179,11 +187,8 @@ class TestListSfcPortPair(fakes.TestNeutronClientOSCV2): def setUp(self): super(TestListSfcPortPair, self).setUp() - mock.patch('neutronclient.osc.v2.sfc.sfc_port_pair._get_id', - new=_get_id).start() - self.neutronclient.list_sfc_port_pairs = mock.Mock( - return_value={'port_pairs': self._port_pairs} - ) + self.network.sfc_port_pairs = mock.Mock( + return_value=self._port_pairs) # Get the command object to test self.cmd = sfc_port_pair.ListSfcPortPair(self.app, self.namespace) @@ -192,7 +197,7 @@ def test_list_port_pairs(self): verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns = self.cmd.take_action(parsed_args)[0] - port_pairs = self.neutronclient.list_sfc_port_pairs()['port_pairs'] + port_pairs = self.network.sfc_port_pairs() port_pair = port_pairs[0] data = [ port_pair['id'], @@ -206,7 +211,7 @@ def test_list_port_pairs(self): def test_list_with_long_option(self): arglist = ['--long'] verifylist = [('long', True)] - port_pairs = self.neutronclient.list_sfc_port_pairs()['port_pairs'] + port_pairs = self.network.sfc_port_pairs() port_pair = port_pairs[0] data = [ port_pair['id'], @@ -229,13 +234,11 @@ class TestSetSfcPortPair(fakes.TestNeutronClientOSCV2): def setUp(self): super(TestSetSfcPortPair, self).setUp() - mock.patch('neutronclient.osc.v2.sfc.sfc_port_pair._get_id', - new=_get_id).start() - self.neutronclient.update_sfc_port_pair = mock.Mock(return_value=None) + self.network.update_sfc_port_pair = mock.Mock(return_value=None) self.cmd = sfc_port_pair.SetSfcPortPair(self.app, self.namespace) def test_set_port_pair(self): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network mock_port_pair_update = client.update_sfc_port_pair arglist = [ self._port_pair_name, @@ -249,12 +252,12 @@ def test_set_port_pair(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - attrs = {'port_pair': { + attrs = { 'name': 'name_updated', - 'description': 'desc_updated'} + 'description': 'desc_updated' } mock_port_pair_update.assert_called_once_with(self._port_pair_name, - attrs) + **attrs) self.assertIsNone(result) @@ -271,7 +274,7 @@ class TestShowSfcPortPair(fakes.TestNeutronClientOSCV2): _pp['project_id'], _pp['service_function_parameters'], ) - _port_pair = {'port_pair': _pp} + _port_pair = _pp _port_pair_id = _pp['id'] columns = ( 'Description', @@ -285,10 +288,8 @@ class TestShowSfcPortPair(fakes.TestNeutronClientOSCV2): def setUp(self): super(TestShowSfcPortPair, self).setUp() - mock.patch('neutronclient.osc.v2.sfc.sfc_port_pair._get_id', - new=_get_id).start() - self.neutronclient.show_sfc_port_pair = mock.Mock( + self.network.get_sfc_port_pair = mock.Mock( return_value=self._port_pair ) @@ -296,8 +297,8 @@ def setUp(self): self.cmd = sfc_port_pair.ShowSfcPortPair(self.app, self.namespace) def test_show_port_pair(self): - client = self.app.client_manager.neutronclient - mock_port_pair_show = client.show_sfc_port_pair + client = self.app.client_manager.network + mock_port_pair_show = client.get_sfc_port_pair arglist = [ self._port_pair_id, ] diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_port_pair_group.py b/neutronclient/tests/unit/osc/v2/sfc/test_port_pair_group.py index 6db21a284..49f3fcf06 100755 --- a/neutronclient/tests/unit/osc/v2/sfc/test_port_pair_group.py +++ b/neutronclient/tests/unit/osc/v2/sfc/test_port_pair_group.py @@ -15,21 +15,19 @@ from unittest import mock +from osc_lib import exceptions +import testtools + from neutronclient.osc.v2.sfc import sfc_port_pair_group from neutronclient.tests.unit.osc.v2.sfc import fakes -def _get_id(client, id_or_name, resource): - return id_or_name - - class TestCreateSfcPortPairGroup(fakes.TestNeutronClientOSCV2): _port_pair_group = fakes.FakeSfcPortPairGroup.create_port_pair_group() columns = ('Description', 'ID', - 'Loadbalance ID', 'Name', 'Port Pair', 'Port Pair Group Parameters', @@ -40,7 +38,6 @@ def get_data(self, ppg): return ( ppg['description'], ppg['id'], - ppg['group_id'], ppg['name'], ppg['port_pairs'], ppg['port_pair_group_parameters'], @@ -50,12 +47,10 @@ def get_data(self, ppg): def setUp(self): super(TestCreateSfcPortPairGroup, self).setUp() - mock.patch( - 'neutronclient.osc.v2.sfc.sfc_port_pair_group._get_id', - new=_get_id).start() - self.neutronclient.create_sfc_port_pair_group = mock.Mock( - return_value={'port_pair_group': self._port_pair_group}) + self.network.create_sfc_port_pair_group = mock.Mock( + return_value=self._port_pair_group) self.data = self.get_data(self._port_pair_group) + # Get the command object to test self.cmd = sfc_port_pair_group.CreateSfcPortPairGroup(self.app, self.namespace) @@ -72,12 +67,9 @@ def test_create_port_pair_group_default_options(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = (self.cmd.take_action(parsed_args)) - self.neutronclient.create_sfc_port_pair_group.assert_called_once_with({ - 'port_pair_group': { - 'name': self._port_pair_group['name'], - 'port_pairs': [self._port_pair_group['port_pairs']] - } - }) + self.network.create_sfc_port_pair_group.assert_called_once_with( + **{'name': self._port_pair_group['name'], + 'port_pairs': [self._port_pair_group['port_pairs']]}) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) @@ -95,13 +87,11 @@ def test_create_port_pair_group(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = (self.cmd.take_action(parsed_args)) - self.neutronclient.create_sfc_port_pair_group.assert_called_once_with({ - 'port_pair_group': { + self.network.create_sfc_port_pair_group.assert_called_once_with( + **{ 'name': self._port_pair_group['name'], 'port_pairs': [self._port_pair_group['port_pairs']], - 'description': self._port_pair_group['description'], - } - }) + 'description': self._port_pair_group['description']}) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) @@ -127,22 +117,19 @@ def test_create_tap_enabled_port_pair_group(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = (self.cmd.take_action(parsed_args)) - self.neutronclient.create_sfc_port_pair_group.assert_called_once_with({ - 'port_pair_group': { + self.network.create_sfc_port_pair_group.assert_called_once_with( + **{ 'name': self._port_pair_group['name'], 'port_pairs': [self._port_pair_group['port_pairs']], 'description': self._port_pair_group['description'], - 'tap_enabled': True - } - }) + 'tap_enabled': True}) self.assertEqual(self.columns, columns) self.assertEqual(expected_data, data) def _update_expected_response_data(self, data): # REVISIT(vks1) - This method can be common for other test functions. ppg = fakes.FakeSfcPortPairGroup.create_port_pair_group(data) - self.neutronclient.create_sfc_port_pair_group.return_value = { - 'port_pair_group': ppg} + self.network.create_sfc_port_pair_group.return_value = ppg return self.get_data(ppg) @@ -153,22 +140,19 @@ class TestDeleteSfcPortPairGroup(fakes.TestNeutronClientOSCV2): def setUp(self): super(TestDeleteSfcPortPairGroup, self).setUp() - mock.patch( - 'neutronclient.osc.v2.sfc.sfc_port_pair_group._get_id', - new=_get_id).start() - self.neutronclient.delete_sfc_port_pair_group = mock.Mock( + self.network.delete_sfc_port_pair_group = mock.Mock( return_value=None) self.cmd = sfc_port_pair_group.DeleteSfcPortPairGroup(self.app, self.namespace) def test_delete_port_pair_group(self): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network mock_port_pair_group_delete = client.delete_sfc_port_pair_group arglist = [ self._port_pair_group[0]['id'], ] verifylist = [ - ('port_pair_group', self._port_pair_group[0]['id']), + ('port_pair_group', [self._port_pair_group[0]['id']]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) @@ -176,13 +160,29 @@ def test_delete_port_pair_group(self): self._port_pair_group[0]['id']) self.assertIsNone(result) + def test_delete_multiple_port_pair_groups_with_exception(self): + client = self.app.client_manager.network + target1 = 'target' + arglist = [target1] + verifylist = [('port_pair_group', [target1])] + + client.find_sfc_port_pair_group.side_effect = [ + target1, exceptions.CommandError + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + msg = "1 of 2 port pair group(s) failed to delete." + with testtools.ExpectedException(exceptions.CommandError) as e: + self.cmd.take_action(parsed_args) + self.assertEqual(msg, str(e)) + class TestListSfcPortPairGroup(fakes.TestNeutronClientOSCV2): _ppgs = fakes.FakeSfcPortPairGroup.create_port_pair_groups(count=1) columns = ('ID', 'Name', 'Port Pair', 'Port Pair Group Parameters', 'Tap Enabled') columns_long = ('ID', 'Name', 'Port Pair', 'Port Pair Group Parameters', - 'Description', 'Loadbalance ID', 'Project', 'Tap Enabled') + 'Description', 'Project', 'Tap Enabled') _port_pair_group = _ppgs[0] data = [ _port_pair_group['id'], @@ -204,12 +204,9 @@ class TestListSfcPortPairGroup(fakes.TestNeutronClientOSCV2): def setUp(self): super(TestListSfcPortPairGroup, self).setUp() - mock.patch( - 'neutronclient.osc.v2.sfc.sfc_port_pair_group._get_id', - new=_get_id).start() - self.neutronclient.list_sfc_port_pair_groups = mock.Mock( - return_value={'port_pair_groups': self._ppgs} + self.network.sfc_port_pair_groups = mock.Mock( + return_value=self._ppgs ) # Get the command object to test self.cmd = sfc_port_pair_group.ListSfcPortPairGroup(self.app, @@ -220,8 +217,7 @@ def test_list_port_pair_groups(self): verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns = self.cmd.take_action(parsed_args)[0] - ppgs = self.neutronclient \ - .list_sfc_port_pair_groups()['port_pair_groups'] + ppgs = self.network.sfc_port_pair_groups() ppg = ppgs[0] data = [ ppg['id'], @@ -236,8 +232,7 @@ def test_list_port_pair_groups(self): def test_list_with_long_option(self): arglist = ['--long'] verifylist = [('long', True)] - ppgs = self.neutronclient \ - .list_sfc_port_pair_groups()['port_pair_groups'] + ppgs = self.network.sfc_port_pair_groups() ppg = ppgs[0] data = [ ppg['id'], @@ -264,12 +259,9 @@ class TestSetSfcPortPairGroup(fakes.TestNeutronClientOSCV2): def setUp(self): super(TestSetSfcPortPairGroup, self).setUp() - mock.patch( - 'neutronclient.osc.v2.sfc.sfc_port_pair_group._get_id', - new=_get_id).start() - self.neutronclient.update_sfc_port_pair_group = mock.Mock( + self.network.update_sfc_port_pair_group = mock.Mock( return_value=None) - self.mocked = self.neutronclient.update_sfc_port_pair_group + self.mocked = self.network.update_sfc_port_pair_group self.cmd = sfc_port_pair_group.SetSfcPortPairGroup(self.app, self.namespace) @@ -278,24 +270,14 @@ def test_set_port_pair_group(self): port_pair1 = 'additional_port1' port_pair2 = 'additional_port2' - def _mock_port_pair_group(*args, **kwargs): - - if self.neutronclient.find_resource.call_count == 1: - self.neutronclient.find_resource.assert_called_with( - 'port_pair', port_pair1, cmd_resource='sfc_port_pair') - return {'id': args[1]} - - if self.neutronclient.find_resource.call_count == 2: - self.neutronclient.find_resource.assert_called_with( - 'port_pair', port_pair2, cmd_resource='sfc_port_pair') - return {'id': args[1]} - - if self.neutronclient.find_resource.call_count == 3: - self.neutronclient.find_resource.assert_called_with( - self.res, target, cmd_resource='sfc_port_pair_group') - return {'port_pairs': [self.ppg_pp]} - - self.neutronclient.find_resource.side_effect = _mock_port_pair_group + self.network.find_sfc_port_pair = mock.Mock( + side_effect=lambda name_or_id, ignore_missing=False: + {'id': name_or_id} + ) + self.network.find_sfc_port_pair_group = mock.Mock( + side_effect=lambda name_or_id, ignore_missing=False: + {'id': name_or_id, 'port_pairs': self.ppg_pp} + ) arglist = [ target, @@ -308,13 +290,12 @@ def _mock_port_pair_group(*args, **kwargs): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - expect = {'port_pairs': sorted([self.ppg_pp, port_pair1, port_pair2])} - self.mocked.assert_called_once_with(target, {self.res: expect}) - self.assertEqual(3, self.neutronclient.find_resource.call_count) + expect = {'port_pairs': sorted([*self.ppg_pp, port_pair1, port_pair2])} + self.mocked.assert_called_once_with(target, **expect) self.assertIsNone(result) def test_set_no_port_pair(self): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network mock_port_pair_group_update = client.update_sfc_port_pair_group arglist = [ self._port_pair_group_name, @@ -331,11 +312,10 @@ def test_set_no_port_pair(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - attrs = {'port_pair_group': {'name': 'name_updated', - 'description': 'desc_updated', - 'port_pairs': []}} + attrs = {'name': 'name_updated', 'description': 'desc_updated', + 'port_pairs': []} mock_port_pair_group_update.assert_called_once_with( - self._port_pair_group_name, attrs) + self._port_pair_group_name, **attrs) self.assertIsNone(result) @@ -345,18 +325,16 @@ class TestShowSfcPortPairGroup(fakes.TestNeutronClientOSCV2): data = ( _ppg['description'], _ppg['id'], - _ppg['group_id'], _ppg['name'], _ppg['port_pairs'], _ppg['port_pair_group_parameters'], _ppg['project_id'], _ppg['tap_enabled']) - _port_pair_group = {'port_pair_group': _ppg} + _port_pair_group = _ppg _port_pair_group_id = _ppg['id'] columns = ( 'Description', 'ID', - 'Loadbalance ID', 'Name', 'Port Pair', 'Port Pair Group Parameters', @@ -366,19 +344,16 @@ class TestShowSfcPortPairGroup(fakes.TestNeutronClientOSCV2): def setUp(self): super(TestShowSfcPortPairGroup, self).setUp() - mock.patch( - 'neutronclient.osc.v2.sfc.sfc_port_pair_group._get_id', - new=_get_id).start() - self.neutronclient.show_sfc_port_pair_group = mock.Mock( + self.network.get_sfc_port_pair_group = mock.Mock( return_value=self._port_pair_group ) self.cmd = sfc_port_pair_group.ShowSfcPortPairGroup(self.app, self.namespace) def test_show_port_pair_group(self): - client = self.app.client_manager.neutronclient - mock_port_pair_group_show = client.show_sfc_port_pair_group + client = self.app.client_manager.network + mock_port_pair_group_show = client.get_sfc_port_pair_group arglist = [ self._port_pair_group_id, ] @@ -404,12 +379,9 @@ class TestUnsetSfcPortPairGroup(fakes.TestNeutronClientOSCV2): def setUp(self): super(TestUnsetSfcPortPairGroup, self).setUp() - mock.patch( - 'neutronclient.osc.v2.sfc.sfc_port_pair_group._get_id', - new=_get_id).start() - self.neutronclient.update_sfc_port_pair_group = mock.Mock( + self.network.update_sfc_port_pair_group = mock.Mock( return_value=None) - self.mocked = self.neutronclient.update_sfc_port_pair_group + self.mocked = self.network.update_sfc_port_pair_group self.cmd = sfc_port_pair_group.UnsetSfcPortPairGroup( self.app, self.namespace) @@ -418,30 +390,14 @@ def test_unset_port_pair(self): port_pair1 = 'additional_port1' port_pair2 = 'additional_port2' - def _mock_port_pair(*args, **kwargs): - - if self.neutronclient.find_resource.call_count == 1: - self.neutronclient.find_resource.assert_called_with( - self.res, target, cmd_resource='sfc_port_pair_group') - return {'port_pairs': [self.ppg_pp]} - - if self.neutronclient.find_resource.call_count == 2: - self.neutronclient.find_resource.assert_called_with( - 'port_pair', port_pair1, cmd_resource='sfc_port_pair') - return {'id': args[1]} - - if self.neutronclient.find_resource.call_count == 3: - self.neutronclient.find_resource.assert_called_with( - 'port_pair', port_pair2, cmd_resource='sfc_port_pair') - return {'id': args[1]} - - if self.neutronclient.find_resource.call_count == 4: - self.neutronclient.find_resource.assert_called_with( - self.res, target, cmd_resource='sfc_port_pair_group') - return {'id': args[1]} - - self.neutronclient.find_resource.side_effect = _mock_port_pair - + self.network.find_sfc_port_pair = mock.Mock( + side_effect=lambda name_or_id, ignore_missing=False: + {'id': name_or_id} + ) + self.network.find_sfc_port_pair_group = mock.Mock( + side_effect=lambda name_or_id, ignore_missing=False: + {'id': name_or_id, 'port_pairs': self.ppg_pp} + ) arglist = [ target, '--port-pair', port_pair1, @@ -453,12 +409,12 @@ def _mock_port_pair(*args, **kwargs): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - expect = {'port_pairs': sorted([self.ppg_pp])} - self.mocked.assert_called_once_with(target, {self.res: expect}) + expect = {'port_pairs': sorted([*self.ppg_pp])} + self.mocked.assert_called_once_with(target, **expect) self.assertIsNone(result) def test_unset_all_port_pair(self): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network mock_port_pair_group_update = client.update_sfc_port_pair_group arglist = [ self._port_pair_group_name, @@ -471,7 +427,7 @@ def test_unset_all_port_pair(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - attrs = {'port_pair_group': {'port_pairs': []}} + attrs = {'port_pairs': []} mock_port_pair_group_update.assert_called_once_with( - self._port_pair_group_name, attrs) + self._port_pair_group_name, **attrs) self.assertIsNone(result) diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_service_graph.py b/neutronclient/tests/unit/osc/v2/sfc/test_service_graph.py index a05ed2c72..50a02139d 100644 --- a/neutronclient/tests/unit/osc/v2/sfc/test_service_graph.py +++ b/neutronclient/tests/unit/osc/v2/sfc/test_service_graph.py @@ -11,19 +11,17 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. + from unittest import mock from osc_lib import exceptions from osc_lib.tests import utils as tests_utils +import testtools from neutronclient.osc.v2.sfc import sfc_service_graph from neutronclient.tests.unit.osc.v2.sfc import fakes -def _get_id(client, id_or_name, resource): - return id_or_name - - class TestListSfcServiceGraph(fakes.TestNeutronClientOSCV2): _service_graphs = fakes.FakeSfcServiceGraph.create_sfc_service_graphs( count=1) @@ -47,11 +45,8 @@ class TestListSfcServiceGraph(fakes.TestNeutronClientOSCV2): def setUp(self): super(TestListSfcServiceGraph, self).setUp() - mock.patch( - 'neutronclient.osc.v2.sfc.sfc_service_graph._get_id', - new=_get_id).start() - self.neutronclient.list_sfc_service_graphs = mock.Mock( - return_value={'service_graphs': self._service_graphs} + self.network.sfc_service_graphs = mock.Mock( + return_value=self._service_graphs ) # Get the command object to test self.cmd = sfc_service_graph.ListSfcServiceGraph( @@ -62,7 +57,7 @@ def test_list_sfc_service_graphs(self): verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns = self.cmd.take_action(parsed_args)[0] - sgs = self.neutronclient.list_sfc_service_graphs()['service_graphs'] + sgs = self.network.sfc_service_graphs() sg = sgs[0] data = [ sg['id'], @@ -77,7 +72,7 @@ def test_list_sfc_service_graphs_with_long_option(self): verifylist = [('long', True)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns = self.cmd.take_action(parsed_args)[0] - sgs = self.neutronclient.list_sfc_service_graphs()['service_graphs'] + sgs = self.network.sfc_service_graphs() sg = sgs[0] data = [ sg['id'], @@ -107,10 +102,8 @@ def get_data(self): def setUp(self): super(TestCreateSfcServiceGraph, self).setUp() - mock.patch('neutronclient.osc.v2.sfc.sfc_service_graph._get_id', - new=_get_id).start() - self.neutronclient.create_sfc_service_graph = mock.Mock( - return_value={'service_graph': self._service_graph}) + self.network.create_sfc_service_graph = mock.Mock( + return_value=self._service_graph) self.data = self.get_data() self.cmd = sfc_service_graph.CreateSfcServiceGraph( self.app, self.namespace) @@ -145,12 +138,10 @@ def test_create_sfc_service_graph_without_loop(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = (self.cmd.take_action(parsed_args)) - self.neutronclient.create_sfc_service_graph.assert_called_once_with({ - 'service_graph': { + self.network.create_sfc_service_graph.assert_called_once_with(**{ 'description': self._service_graph['description'], 'name': self._service_graph['name'], 'port_chains': pcs - } }) self.assertEqual(self.columns_long, columns) self.assertEqual(self.data, data) @@ -227,19 +218,19 @@ class TestDeleteSfcServiceGraph(fakes.TestNeutronClientOSCV2): def setUp(self): super(TestDeleteSfcServiceGraph, self).setUp() - self.neutronclient.delete_sfc_service_graph = mock.Mock( + self.network.delete_sfc_service_graph = mock.Mock( return_value=None) self.cmd = sfc_service_graph.DeleteSfcServiceGraph( self.app, self.namespace) def test_delete_sfc_service_graph(self): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network mock_service_graph_delete = client.delete_sfc_service_graph arglist = [ self._service_graph[0]['id'], ] verifylist = [ - ('service_graph', self._service_graph[0]['id']), + ('service_graph', [self._service_graph[0]['id']]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) @@ -247,6 +238,22 @@ def test_delete_sfc_service_graph(self): self._service_graph[0]['id']) self.assertIsNone(result) + def test_delete_multiple_service_graphs_with_exception(self): + client = self.app.client_manager.network + target = self._service_graph[0]['id'] + arglist = [target] + verifylist = [('service_graph', [target])] + + client.find_sfc_service_graph.side_effect = [ + target, exceptions.CommandError + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + msg = "1 of 2 service graph(s) failed to delete." + with testtools.ExpectedException(exceptions.CommandError) as e: + self.cmd.take_action(parsed_args) + self.assertEqual(msg, str(e)) + class TestShowSfcServiceGraph(fakes.TestNeutronClientOSCV2): @@ -266,15 +273,12 @@ class TestShowSfcServiceGraph(fakes.TestNeutronClientOSCV2): _sg['project_id'] ) - _service_graph = {'service_graph': _sg} + _service_graph = _sg _service_graph_id = _sg['id'] def setUp(self): super(TestShowSfcServiceGraph, self).setUp() - mock.patch( - 'neutronclient.osc.v2.sfc.sfc_service_graph._get_id', - new=_get_id).start() - self.neutronclient.show_sfc_service_graph = mock.Mock( + self.network.get_sfc_service_graph = mock.Mock( return_value=self._service_graph ) # Get the command object to test @@ -282,8 +286,8 @@ def setUp(self): self.app, self.namespace) def test_service_graph_show(self): - client = self.app.client_manager.neutronclient - mock_service_graph_show = client.show_sfc_service_graph + client = self.app.client_manager.network + mock_service_graph_show = client.get_sfc_service_graph arglist = [ self._service_graph_id, ] @@ -305,15 +309,13 @@ class TestSetSfcServiceGraph(fakes.TestNeutronClientOSCV2): def setUp(self): super(TestSetSfcServiceGraph, self).setUp() - mock.patch('neutronclient.osc.v2.sfc.sfc_service_graph._get_id', - new=_get_id).start() - self.neutronclient.update_sfc_service_graph = mock.Mock( + self.network.update_sfc_service_graph = mock.Mock( return_value=None) self.cmd = sfc_service_graph.SetSfcServiceGraph( self.app, self.namespace) def test_set_service_graph(self): - client = self.app.client_manager.neutronclient + client = self.app.client_manager.network mock_service_graph_update = client.update_sfc_service_graph arglist = [ self._service_graph_name, @@ -327,10 +329,10 @@ def test_set_service_graph(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - attrs = {'service_graph': { + attrs = { 'name': 'name_updated', - 'description': 'desc_updated'} + 'description': 'desc_updated' } mock_service_graph_update.assert_called_once_with( - self._service_graph_name, attrs) + self._service_graph_name, **attrs) self.assertIsNone(result) From 8c983fa764584699632c64b6448d68f12d72a726 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Sat, 9 Sep 2023 03:54:47 +0000 Subject: [PATCH 811/845] Imported Translations from Zanata For more information about this automatic import see: https://docs.openstack.org/i18n/latest/reviewing-translation-import.html Change-Id: Ida432a5df2f6cc9453f546ad8e66b0e52c5d1b80 --- .../locale/en_GB/LC_MESSAGES/releasenotes.po | 400 ++++++++++++++++++ 1 file changed, 400 insertions(+) create mode 100644 releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po diff --git a/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po b/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po new file mode 100644 index 000000000..cb1dcb6bd --- /dev/null +++ b/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po @@ -0,0 +1,400 @@ +# Andi Chandler , 2022. #zanata +# Andi Chandler , 2023. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Neutron Client Release Notes\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-09-08 20:18+0000\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"PO-Revision-Date: 2023-07-28 12:44+0000\n" +"Last-Translator: Andi Chandler \n" +"Language-Team: English (United Kingdom)\n" +"Language: en_GB\n" +"X-Generator: Zanata 4.3.3\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" + +msgid "" +"\"admin-state-down\" option was deprecated in Mitaka and has been removed in " +"Newton." +msgstr "" +"\"admin-state-down\" option was deprecated in Mitaka and has been removed in " +"Newton." + +msgid "10.0.0" +msgstr "10.0.0" + +msgid "2.0" +msgstr "2.0" + +msgid "2.2.0" +msgstr "2.2.0" + +msgid "2.2.2" +msgstr "2.2.2" + +msgid "2023.1 Series Release Notes" +msgstr "2023.1 Series Release Notes" + +msgid "4.0.0" +msgstr "4.0.0" + +msgid "4.1.0" +msgstr "4.1.0" + +msgid "4.1.1" +msgstr "4.1.1" + +msgid "4.1.2-15" +msgstr "4.1.2-15" + +msgid "4.2.0" +msgstr "4.2.0" + +msgid "5.0.0" +msgstr "5.0.0" + +msgid "5.1.0" +msgstr "5.1.0" + +msgid "6.0.0" +msgstr "6.0.0" + +msgid "6.1.0" +msgstr "6.1.0" + +msgid "6.1.1" +msgstr "6.1.1" + +msgid "6.10.0" +msgstr "6.10.0" + +msgid "6.11.0" +msgstr "6.11.0" + +msgid "6.12.0" +msgstr "6.12.0" + +msgid "6.14.0" +msgstr "6.14.0" + +msgid "6.2.0" +msgstr "6.2.0" + +msgid "6.3.0" +msgstr "6.3.0" + +msgid "6.4.0" +msgstr "6.4.0" + +msgid "6.5.0" +msgstr "6.5.0" + +msgid "6.6.0" +msgstr "6.6.0" + +msgid "6.7.0" +msgstr "6.7.0" + +msgid "7.0.0" +msgstr "7.0.0" + +msgid "7.1.0" +msgstr "7.1.0" + +msgid "7.2.0" +msgstr "7.2.0" + +msgid "7.2.1" +msgstr "7.2.1" + +msgid "7.5.0" +msgstr "7.5.0" + +msgid "7.7.0" +msgstr "7.7.0" + +msgid "7.8.0" +msgstr "7.8.0" + +msgid "8.0.0" +msgstr "8.0.0" + +msgid "8.1.0" +msgstr "8.1.0" + +msgid "8.2.0" +msgstr "8.2.0" + +msgid "" +"A new option ``--no-session-persistence`` has been added to the ``neutron " +"lbaas-pool-update`` CLI to clear the session persistence with which the " +"current pool is associated." +msgstr "" +"A new option ``--no-session-persistence`` has been added to the ``neutron " +"lbaas-pool-update`` CLI to clear the session persistence with which the " +"current pool is associated." + +msgid "" +"Add BGP VPN `port association `_ support to the CLI, which are " +"introduced for BGP VPN interconnections by the ``bgpvpn-routes-control`` API " +"extension." +msgstr "" +"Add BGP VPN `port association `_ support to the CLI, which are " +"introduced for BGP VPN interconnections by the ``bgpvpn-routes-control`` API " +"extension." + +msgid "" +"Add OSC plugin support for the “Networking Service Function Chaining” " +"feature commands along with client bindings. [Blueprint `openstackclient-cli-" +"porting `_]" +msgstr "" +"Add OSC plugin support for the “Networking Service Function Chaining” " +"feature commands along with client bindings. [Blueprint `openstackclient-cli-" +"porting `_]" + +msgid "Add OSC plugin to support \"Neutron Dynamic Routing\"" +msgstr "Add OSC plugin to support \"Neutron Dynamic Routing\"" + +msgid "Add OSC support to create Port pair group for Tap service functions." +msgstr "Add OSC support to create Port pair group for Tap service functions." + +msgid "" +"Add ``network onboard subnets`` OSC command to enable subnet onboard support " +"from the CLI [Blueprint `subnet-onboard `_]" +msgstr "" +"Add ``network onboard subnets`` OSC command to enable subnet onboard support " +"from the CLI [Blueprint `subnet-onboard `_]" + +msgid "" +"Add ``network trunk create``, ``network trunk list``, ``network trunk set``, " +"``network trunk unset``, ``network trunk delete`` and ``network subport " +"list`` OSC commands for trunk resource along with client bindings. " +"[Blueprint `vlan-aware-vms `_]" +msgstr "" +"Add ``network trunk create``, ``network trunk list``, ``network trunk set``, " +"``network trunk unset``, ``network trunk delete`` and ``network subport " +"list`` OSC commands for trunk resource along with client bindings. " +"[Blueprint `vlan-aware-vms `_]" + +msgid "" +"Add optional flag to control the advertisement in BGPVPNs of the routes " +"defined on a Router resource (``bgpvpn-routes-control`` API extension)." +msgstr "" +"Add optional flag to control the advertisement in BGPVPNs of the routes " +"defined on a Router resource (``bgpvpn-routes-control`` API extension)." + +msgid "" +"Add osprofiler support to the neutronclient python binding. If osprofiler is " +"initiated, neutronclient sends a special HTTP header that contains trace " +"info." +msgstr "" +"Add osprofiler support to the neutronclient python binding. If osprofiler is " +"initiated, neutronclient sends a special HTTP header that contains trace " +"info." + +msgid "Add support to floating ip port forwarding." +msgstr "Add support to floating ip port forwarding." + +msgid "" +"By using this feature, multiple resource can be deleted using a single " +"command." +msgstr "" +"By using this feature, multiple resource can be deleted using a single " +"command." + +msgid "" +"CLI support for 'Logging' feature, which enable to collect packet logs for " +"specified resource. Currently, only security-group can be logged." +msgstr "" +"CLI support for 'Logging' feature, which enable to collect packet logs for " +"specified resource. Currently, only security-group can be logged." + +msgid "CLI support for Layer 7 content policies and rules." +msgstr "CLI support for Layer 7 content policies and rules." + +msgid "CLI support for Neutron-LBaaS v2 shared pools added." +msgstr "CLI support for Neutron-LBaaS v2 shared pools added." + +msgid "CLI support for QoS policy RBAC." +msgstr "CLI support for QoS policy RBAC." + +msgid "Current Series Release Notes" +msgstr "Current Series Release Notes" + +msgid "Mitaka Series Release Notes" +msgstr "Mitaka Series Release Notes" + +msgid "Neutron Client Release Notes" +msgstr "Neutron Client Release Notes" + +msgid "Newton Series Release Notes" +msgstr "Newton Series Release Notes" + +msgid "Ocata Series Release Notes" +msgstr "Ocata Series Release Notes" + +msgid "Old Release Notes" +msgstr "Old Release Notes" + +msgid "Pike Series Release Notes" +msgstr "Pike Series Release Notes" + +msgid "Queens Series Release Notes" +msgstr "Queens Series Release Notes" + +msgid "Rocky Series Release Notes" +msgstr "Rocky Series Release Notes" + +msgid "Stein Series Release Notes" +msgstr "Stein Series Release Notes" + +msgid "The ``rbac-create`` command include a --type qos-policy option." +msgstr "The ``rbac-create`` command include a --type qos-policy option." + +msgid "The ``rbac-list`` command output includes a new 'type' column." +msgstr "The ``rbac-list`` command output includes a new 'type' column." + +msgid "" +"The ``subnetpool-create`` and ``subnetpool-update`` commands include a ``--" +"is-default`` option." +msgstr "" +"The ``subnetpool-create`` and ``subnetpool-update`` commands include a ``--" +"is-default`` option." + +msgid "" +"The ``subnetpool-list`` and ``subnetpool-show`` command output includes the " +"``is_default`` field." +msgstr "" +"The ``subnetpool-list`` and ``subnetpool-show`` command output includes the " +"``is_default`` field." + +msgid "" +"The ``tag-add`` command sets a tag on the network resource. It also includes " +"``--resource-type``, ``--resource`` and ``--tag`` options." +msgstr "" +"The ``tag-add`` command sets a tag on the network resource. It also includes " +"``--resource-type``, ``--resource`` and ``--tag`` options." + +msgid "" +"This patch provides user the support to use any form of casing, thus " +"removing the specific UPPER/lower case inputs required by different neutron " +"CLIs." +msgstr "" +"This patch provides users the support to use any form of casing, thus " +"removing the specific UPPER/lower case inputs required by different Neutron " +"CLIs." + +msgid "" +"This project no longer provides CLI support. All code has been removed. " +"Please use openstack CLI instead. See `openstack CLI command list `_." +msgstr "" +"This project no longer provides CLI support. All code has been removed. " +"Please use OpenStack CLI instead. See `openstack CLI command list `_." + +msgid "Train Series Release Notes" +msgstr "Train Series Release Notes" + +msgid "Upgrade Notes" +msgstr "Upgrade Notes" + +msgid "" +"Using 'tenant_id' and 'tenant_name' arguments in API bindings is deprecated. " +"Use 'project_id' and 'project_name' arguments instead." +msgstr "" +"Using 'tenant_id' and 'tenant_name' arguments in API bindings is deprecated. " +"Use 'project_id' and 'project_name' arguments instead." + +msgid "Ussuri Series Release Notes" +msgstr "Ussuri Series Release Notes" + +msgid "Victoria Series Release Notes" +msgstr "Victoria Series Release Notes" + +msgid "Wallaby Series Release Notes" +msgstr "Wallaby Series Release Notes" + +msgid "XML request format support has been removed." +msgstr "XML request format support has been removed." + +msgid "Xena Series Release Notes" +msgstr "Xena Series Release Notes" + +msgid "Yoga Series Release Notes" +msgstr "Yoga Series Release Notes" + +msgid "Zed Series Release Notes" +msgstr "Zed Series Release Notes" + +msgid "add --endpoint-type and OS_ENDPOINT_TYPE to shell client" +msgstr "add --endpoint-type and OS_ENDPOINT_TYPE to shell client" + +msgid "add Lbaas commands" +msgstr "add Lbaas commands" + +msgid "add NVP queue and net gateway commands" +msgstr "add NVP queue and net gateway commands" + +msgid "" +"add ability to update security group name (requires 2013.2-Havana or later)" +msgstr "" +"add ability to update security group name (requires 2013.2-Havana or later)" + +msgid "add commands for DHCP and L3 agents scheduling" +msgstr "add commands for DHCP and L3 agents scheduling" + +msgid "add commands for agent management extensions" +msgstr "add commands for agent management extensions" + +msgid "add flake8 and pbr support for testing and building" +msgstr "add flake8 and pbr support for testing and building" + +msgid "add security group commands" +msgstr "add security group commands" + +msgid "allow options put after positional arguments" +msgstr "allow options put after positional arguments" + +msgid "improved support for listing a large number of filtered subnets" +msgstr "improved support for listing a large number of filtered subnets" + +msgid "made the publicURL the default endpoint instead of adminURL" +msgstr "made the publicURL the default endpoint instead of adminURL" + +msgid "openstack firewall rule create" +msgstr "openstack firewall rule create" + +msgid "openstack firewall rule set" +msgstr "openstack firewall rule set" + +msgid "openstack firewall rule unset" +msgstr "openstack firewall rule unset" + +msgid "py26 support has been dropped." +msgstr "py26 support has been dropped." + +msgid "py33 support has been dropped." +msgstr "py33 support has been dropped." + +msgid "request-format option is deprecated." +msgstr "request-format option is deprecated." + +msgid "support Neutron API 2.0" +msgstr "support Neutron API 2.0" + +msgid "support XML request format" +msgstr "support XML request format" + +msgid "support pagination options" +msgstr "support pagination options" From ee6f6a13ea7843a99e67212e60064da545535d5b Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Sun, 5 Nov 2023 18:31:15 +0900 Subject: [PATCH 812/845] Drop unused simplejson This library no longer import simplejson directly since [1] was merged. [1] 1f35b8f25cb328ede6aea7933ecd33a1cd713ba8 Change-Id: I5689ed9f7852cc2bec3cf8035a773e63fb10e3d5 --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index ff8961688..5a20fc0e6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -22,4 +22,3 @@ keystoneauth1>=3.8.0 # Apache-2.0 # TODO(amotoki): Drop this after osc.utils has no dependency on keystoneclient python-keystoneclient>=3.8.0 # Apache-2.0 requests>=2.14.2 # Apache-2.0 -simplejson>=3.5.1 # MIT From c28a5497c2f712c5435d7b2c53a70e37517ce2db Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez Date: Thu, 18 Jan 2024 14:17:42 +0000 Subject: [PATCH 813/845] Add note to prevent new features to be added (CLI and Python bindings) Change-Id: I19fb7426d91d6d7f27fee0507b7c28541ecaf344 --- README.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.rst b/README.rst index a5e5300d6..e8c42d88a 100644 --- a/README.rst +++ b/README.rst @@ -17,6 +17,12 @@ Python bindings to the Neutron API This is a client library for Neutron built on the Neutron API. It provides a Python API (the ``neutronclient`` module). +.. note:: This project has been deprecated. The CLI code has been deleted + and is not accessible anymore. The Python bindings are still in use by + other projects but no new features will be added to this project. + Any new feature should be proposed to OpenStack SDK and OpenStack + Client. + * License: Apache License, Version 2.0 * `PyPi`_ - package installation * `Online Documentation`_ From 2d93d2e1ed9f1661c98243d8754faf870cbbe80a Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Sun, 28 Jan 2024 16:26:45 +0900 Subject: [PATCH 814/845] Bump hacking hacking 3.0.x is too old. This also pulls the hacking options from neutron. Change-Id: Ic1a428a05131f98d5a9050b83613c5478904f202 --- neutronclient/common/utils.py | 2 +- neutronclient/neutron/v2_0/bgp/speaker.py | 2 +- neutronclient/osc/v2/sfc/sfc_port_pair_group.py | 2 +- neutronclient/osc/v2/sfc/sfc_service_graph.py | 4 ++-- .../tests/unit/osc/v2/sfc/test_flow_classifier.py | 2 +- .../tests/unit/osc/v2/sfc/test_port_chain.py | 4 ++-- .../tests/unit/osc/v2/sfc/test_service_graph.py | 14 +++++++------- requirements.txt | 4 ---- test-requirements.txt | 7 ++----- tox.ini | 15 ++++++++++++++- 10 files changed, 31 insertions(+), 25 deletions(-) diff --git a/neutronclient/common/utils.py b/neutronclient/common/utils.py index 38274018c..93f92fd19 100644 --- a/neutronclient/common/utils.py +++ b/neutronclient/common/utils.py @@ -138,7 +138,7 @@ def str2dict(strdict, required_keys=None, optional_keys=None): msg = _("missing value for key '%s'") raise argparse.ArgumentTypeError(msg % kv) else: - kvlist[i-1] = "%s,%s" % (kvlist[i-1], kv) + kvlist[i - 1] = "%s,%s" % (kvlist[i - 1], kv) for kv in kvlist: key, sep, value = kv.partition('=') if not sep: diff --git a/neutronclient/neutron/v2_0/bgp/speaker.py b/neutronclient/neutron/v2_0/bgp/speaker.py index 6bdbd86f2..34ce22eb8 100644 --- a/neutronclient/neutron/v2_0/bgp/speaker.py +++ b/neutronclient/neutron/v2_0/bgp/speaker.py @@ -189,7 +189,7 @@ def take_action(self, parsed_args): neutron_client.remove_peer_from_bgp_speaker(_speaker_id, {'bgp_peer_id': _peer_id}) print(_('Removed BGP peer %(peer)s from BGP speaker %(speaker)s.') % - {'peer': parsed_args.bgp_peer, + {'peer': parsed_args.bgp_peer, 'speaker': parsed_args.bgp_speaker}, file=self.app.stdout) diff --git a/neutronclient/osc/v2/sfc/sfc_port_pair_group.py b/neutronclient/osc/v2/sfc/sfc_port_pair_group.py index 5ad3ebea7..ec8859b42 100755 --- a/neutronclient/osc/v2/sfc/sfc_port_pair_group.py +++ b/neutronclient/osc/v2/sfc/sfc_port_pair_group.py @@ -34,7 +34,7 @@ ('port_pair_group_parameters', 'Port Pair Group Parameters', column_util.LIST_BOTH), ('description', 'Description', column_util.LIST_LONG_ONLY), - ('project_id', 'Project', column_util.LIST_LONG_ONLY), + ('project_id', 'Project', column_util.LIST_LONG_ONLY), ('is_tap_enabled', 'Tap Enabled', column_util.LIST_BOTH) ) diff --git a/neutronclient/osc/v2/sfc/sfc_service_graph.py b/neutronclient/osc/v2/sfc/sfc_service_graph.py index 6c87d95fe..2edf2ccd0 100644 --- a/neutronclient/osc/v2/sfc/sfc_service_graph.py +++ b/neutronclient/osc/v2/sfc/sfc_service_graph.py @@ -213,8 +213,8 @@ def _validate_destination_chains(comma_split, attrs, client, sc_): dc_ = client.find_sfc_port_chain(e, ignore_missing=False)['id'] attrs['port_chains'][sc_].append(dc_) if _check_cycle(attrs['port_chains'], sc_, dc_): - raise(exceptions.CommandError( - "Error: Service graph contains a cycle")) + raise exceptions.CommandError( + "Error: Service graph contains a cycle") else: raise exceptions.CommandError( "Error: you must specify at least one " diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_flow_classifier.py b/neutronclient/tests/unit/osc/v2/sfc/test_flow_classifier.py index 6a9e691f9..ea1c4f3ab 100755 --- a/neutronclient/tests/unit/osc/v2/sfc/test_flow_classifier.py +++ b/neutronclient/tests/unit/osc/v2/sfc/test_flow_classifier.py @@ -137,7 +137,7 @@ def test_create_flow_classifier(self): 'logical_destination_port': self._fc['logical_destination_port'], 'l7_parameters': param - } + } ) self.assertEqual(self.columns, columns) diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py b/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py index 9b414c22c..15f7a431e 100755 --- a/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py +++ b/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py @@ -47,7 +47,7 @@ def get_data(self): self._port_chain['name'], self._port_chain['port_pair_groups'], self._port_chain['project_id'], - ) + ) def setUp(self): super(TestCreateSfcPortChain, self).setUp() @@ -357,7 +357,7 @@ def test_set_only_no_port_pair_group(self): arglist = [ target, '--no-port-pair-group', - ] + ] verifylist = [ (self.res, target), ('no_port_pair_group', True), diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_service_graph.py b/neutronclient/tests/unit/osc/v2/sfc/test_service_graph.py index 50a02139d..167f1fcf8 100644 --- a/neutronclient/tests/unit/osc/v2/sfc/test_service_graph.py +++ b/neutronclient/tests/unit/osc/v2/sfc/test_service_graph.py @@ -123,8 +123,8 @@ def test_create_sfc_service_graph_without_loop(self): arglist = [ "--description", self._service_graph['description'], - "--branching-point", bp1_str, - "--branching-point", bp2_str, + "--branching-point", bp1_str, + "--branching-point", bp2_str, self._service_graph['name']] pcs = {'pc1': ['pc2', 'pc3'], 'pc2': ['pc4']} @@ -154,8 +154,8 @@ def test_create_sfc_service_graph_with_loop(self): arglist = [ "--description", self._service_graph['description'], - "--branching-point", bp1_str, - "--branching-point", bp2_str, + "--branching-point", bp1_str, + "--branching-point", bp2_str, self._service_graph['name']] verifylist = [ @@ -175,7 +175,7 @@ def test_create_sfc_service_graph_invalid_port_chains(self): arglist = [ "--description", self._service_graph['description'], - "--branching-point", bp1_str, + "--branching-point", bp1_str, self._service_graph['name']] verifylist = [ @@ -196,8 +196,8 @@ def test_create_sfc_service_graph_duplicate_src_chains(self): arglist = [ "--description", self._service_graph['description'], - "--branching-point", bp1_str, - "--branching-point", bp2_str, + "--branching-point", bp1_str, + "--branching-point", bp2_str, self._service_graph['name']] verifylist = [ diff --git a/requirements.txt b/requirements.txt index 5a20fc0e6..5f10ef907 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,6 @@ # Requirements lower bounds listed here are our best effort to keep them up to # date but we do not test them so no guarantee of having them all correct. If # you find any incorrect lower bounds, let us know or propose a fix. - -# The order of packages is significant, because pip processes them in the order -# of appearance. Changing the order has an impact on the overall integration -# process, which may cause wedges in the gate later. pbr!=2.1.0,>=2.0.0 # Apache-2.0 cliff>=3.4.0 # Apache-2.0 debtcollector>=1.2.0 # Apache-2.0 diff --git a/test-requirements.txt b/test-requirements.txt index ae1f2e55d..8cd49ce02 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,12 +1,9 @@ -# The order of packages is significant, because pip processes them in the order -# of appearance. Changing the order has an impact on the overall integration -# process, which may cause wedges in the gate later. -hacking>=3.0.1,<3.1.0 # Apache-2.0 +hacking>=6.1.0,<6.2.0 # Apache-2.0 bandit!=1.6.0,>=1.1.0 # Apache-2.0 coverage!=4.4,>=4.0 # Apache-2.0 fixtures>=3.0.0 # Apache-2.0/BSD -flake8-import-order==0.12 # LGPLv3 +flake8-import-order>=0.18.0,<0.19.0 # LGPLv3 oslotest>=3.2.0 # Apache-2.0 osprofiler>=2.3.0 # Apache-2.0 python-openstackclient>=3.12.0 # Apache-2.0 diff --git a/tox.ini b/tox.ini index afdf530e2..cda43b91c 100644 --- a/tox.ini +++ b/tox.ini @@ -66,12 +66,25 @@ deps = commands = sphinx-build -a -E -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html [flake8] +# E126 continuation line over-indented for hanging indent +# E128 continuation line under-indented for visual indent +# H405 multi line docstring summary not separated with an empty line +# I202 Additional newline in a group of imports +# N530 direct neutron imports not allowed +# TODO(amotoki) check the following new rules should be fixed or ignored +# E731 do not assign a lambda expression, use a def +# W504 line break after binary operator +ignore = E126,E128,E731,I202,H405,N530,W504 show-source = true exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,tools import-order-style = pep8 +# H106: Don't put vim configuration in source files +# H203: Use assertIs(Not)None to check for None +# H204: Use assert(Not)Equal to check for equality +# H205: Use assert(Greater|Less)(Equal) for comparison # H904: Delay string interpolations at logging calls -enable-extensions=H904 +enable-extensions=H106,H203,H204,H205,H904 [testenv:bandit] # B303: blacklist calls: md5, sha1 From 858b844cff2234f98af564946a310587e81650f6 Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Mon, 5 Feb 2024 16:47:19 +0000 Subject: [PATCH 815/845] reno: Update master for unmaintained/yoga Update the yoga release notes configuration to build from unmaintained/yoga. Change-Id: Id65b43b5bd59567cec599aae48e962c6ac40b427 --- releasenotes/source/yoga.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/source/yoga.rst b/releasenotes/source/yoga.rst index 7cd5e908a..43cafdea8 100644 --- a/releasenotes/source/yoga.rst +++ b/releasenotes/source/yoga.rst @@ -3,4 +3,4 @@ Yoga Series Release Notes ========================= .. release-notes:: - :branch: stable/yoga + :branch: unmaintained/yoga From 750afa1777599827a86db3470254048aeeab2b8a Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Wed, 6 Mar 2024 12:04:19 +0000 Subject: [PATCH 816/845] reno: Update master for unmaintained/victoria Update the victoria release notes configuration to build from unmaintained/victoria. Change-Id: I27ad08ec3327a9a9984223f7d9319129537040e4 --- releasenotes/source/victoria.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/source/victoria.rst b/releasenotes/source/victoria.rst index 4efc7b6f3..8ce933419 100644 --- a/releasenotes/source/victoria.rst +++ b/releasenotes/source/victoria.rst @@ -3,4 +3,4 @@ Victoria Series Release Notes ============================= .. release-notes:: - :branch: stable/victoria + :branch: unmaintained/victoria From 863c6031fc866ac1c59087b93655402cad05c974 Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Wed, 6 Mar 2024 12:14:08 +0000 Subject: [PATCH 817/845] reno: Update master for unmaintained/wallaby Update the wallaby release notes configuration to build from unmaintained/wallaby. Change-Id: Ia81995fae56aef0951004ab1c7340a38f24a66f0 --- releasenotes/source/wallaby.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/source/wallaby.rst b/releasenotes/source/wallaby.rst index d77b56599..bcf35c5f8 100644 --- a/releasenotes/source/wallaby.rst +++ b/releasenotes/source/wallaby.rst @@ -3,4 +3,4 @@ Wallaby Series Release Notes ============================ .. release-notes:: - :branch: stable/wallaby + :branch: unmaintained/wallaby From 41e7ea6e29866a95927aaca9cd4bf3846243aa02 Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Wed, 6 Mar 2024 12:21:58 +0000 Subject: [PATCH 818/845] reno: Update master for unmaintained/xena Update the xena release notes configuration to build from unmaintained/xena. Change-Id: I074395b7079588a5dd2f6188a04b5d48b10e124b --- releasenotes/source/xena.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/source/xena.rst b/releasenotes/source/xena.rst index 1be85be3e..d19eda488 100644 --- a/releasenotes/source/xena.rst +++ b/releasenotes/source/xena.rst @@ -3,4 +3,4 @@ Xena Series Release Notes ========================= .. release-notes:: - :branch: stable/xena + :branch: unmaintained/xena From fe515adf350ce93ced6ee6532185d106e218b9fe Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Thu, 7 Mar 2024 15:38:58 +0000 Subject: [PATCH 819/845] Update master for stable/2024.1 Add file to the reno documentation build to show release notes for stable/2024.1. Use pbr instruction to increment the minor version number automatically so that master versions are higher than the versions on stable/2024.1. Sem-Ver: feature Change-Id: I074213d853338f1e087b59f14f3c66ea449e5536 --- releasenotes/source/2024.1.rst | 6 ++++++ releasenotes/source/index.rst | 1 + 2 files changed, 7 insertions(+) create mode 100644 releasenotes/source/2024.1.rst diff --git a/releasenotes/source/2024.1.rst b/releasenotes/source/2024.1.rst new file mode 100644 index 000000000..4977a4f1a --- /dev/null +++ b/releasenotes/source/2024.1.rst @@ -0,0 +1,6 @@ +=========================== +2024.1 Series Release Notes +=========================== + +.. release-notes:: + :branch: stable/2024.1 diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index dffa77497..e3e11c8fa 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -6,6 +6,7 @@ :maxdepth: 1 unreleased + 2024.1 2023.2 2023.1 zed From 2169b2bbab1b09c320d4eeeb2b341c99b4c3ef87 Mon Sep 17 00:00:00 2001 From: Benjamin Reichel Date: Fri, 15 Mar 2024 08:58:34 +0100 Subject: [PATCH 820/845] Fix insert and remove rule from firewall policy This change fixes the incompatability with the openstacksdk for inserting and removing rules from firewall policiese. Closes-Bug: #2057816 Change-Id: I8db7b4cc61b810887b0a675efa562f089821e8ec --- neutronclient/osc/v2/fwaas/firewallpolicy.py | 4 ++-- .../unit/osc/v2/fwaas/test_firewallpolicy.py | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/neutronclient/osc/v2/fwaas/firewallpolicy.py b/neutronclient/osc/v2/fwaas/firewallpolicy.py index e049698f7..cfcc3c1df 100644 --- a/neutronclient/osc/v2/fwaas/firewallpolicy.py +++ b/neutronclient/osc/v2/fwaas/firewallpolicy.py @@ -235,7 +235,7 @@ def take_action(self, parsed_args): policy_id = client.find_firewall_policy( parsed_args.firewall_policy)['id'] body = self.args2body(parsed_args) - client.insert_rule_into_policy(policy_id, body) + client.insert_rule_into_policy(policy_id, **body) rule_id = body['firewall_rule_id'] policy = parsed_args.firewall_policy print((_('Inserted firewall rule %(rule)s in firewall policy ' @@ -264,7 +264,7 @@ def take_action(self, parsed_args): parsed_args.firewall_policy)['id'] fwr_id = _get_required_firewall_rule(client, parsed_args) body = {'firewall_rule_id': fwr_id} - client.remove_rule_from_policy(policy_id, body) + client.remove_rule_from_policy(policy_id, **body) rule_id = body['firewall_rule_id'] policy = parsed_args.firewall_policy print((_('Removed firewall rule %(rule)s from firewall policy ' diff --git a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py index 7d49879dc..fe385a0cb 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py @@ -507,12 +507,12 @@ def test_insert_firewall_rule(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.mocked.assert_called_once_with( - target, { - 'firewall_rule_id': rule, - 'insert_before': before, - 'insert_after': after - }) + body = { + 'firewall_rule_id': rule, + 'insert_before': before, + 'insert_after': after + } + self.mocked.assert_called_once_with(target, **body) self.assertIsNone(result) self.assertEqual(1, self.networkclient.find_firewall_policy.call_count) self.assertEqual(3, self.networkclient.find_firewall_rule.call_count) @@ -560,8 +560,8 @@ def test_remove_firewall_rule(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.mocked.assert_called_once_with( - target, {'firewall_rule_id': rule}) + body = {'firewall_rule_id': rule} + self.mocked.assert_called_once_with(target, **body) self.assertIsNone(result) self.assertEqual(1, self.networkclient.find_firewall_policy.call_count) self.assertEqual(1, self.networkclient.find_firewall_rule.call_count) From fb8de782bcd44741b633e15352d76476d24f246e Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Tue, 30 Apr 2024 08:54:59 +0000 Subject: [PATCH 821/845] reno: Update master for unmaintained/zed Update the zed release notes configuration to build from unmaintained/zed. Change-Id: I73aa32050dfaa378dd7455846e994560851c0e54 --- releasenotes/source/zed.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/source/zed.rst b/releasenotes/source/zed.rst index 9608c05e4..6cc2b1554 100644 --- a/releasenotes/source/zed.rst +++ b/releasenotes/source/zed.rst @@ -3,4 +3,4 @@ Zed Series Release Notes ======================== .. release-notes:: - :branch: stable/zed + :branch: unmaintained/zed From cb9aae55c83c2b79fdd5d15d93e5c12b35f0dd78 Mon Sep 17 00:00:00 2001 From: Valentin Chassignol Date: Wed, 1 May 2024 10:33:46 +0200 Subject: [PATCH 822/845] BGPVPN: Fix resource comparison This patch updates the resource comparison to accurately differentiate between the manipulation of networks, ports, and router associations. Introduced in I84fe4bb45d24c2f4a7a0246e2b9fb50354a715e0, the previous implementation contained incorrect variable names, leading to inconsistent results. This fix ensures that each resource type is properly identified during comparison. Closes-Bug: #2064286 Change-Id: Ieb87174296240f8f76ec10b39007935545b1bd3f --- .../networking_bgpvpn/resource_association.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/neutronclient/osc/v2/networking_bgpvpn/resource_association.py b/neutronclient/osc/v2/networking_bgpvpn/resource_association.py index 925ee517a..83ac7d99f 100644 --- a/neutronclient/osc/v2/networking_bgpvpn/resource_association.py +++ b/neutronclient/osc/v2/networking_bgpvpn/resource_association.py @@ -75,10 +75,10 @@ def take_action(self, parsed_args): body.update( arg2body(bgpvpn['id'], parsed_args)) - if self._assoc_res_name == constants.NETWORK_ASSOC: + if self._resource == constants.NETWORK_ASSOC: obj = client.create_bgpvpn_network_association( bgpvpn['id'], **body) - elif self._assoc_res_name == constants.PORT_ASSOCS: + elif self._resource == constants.PORT_ASSOC: obj = client.create_bgpvpn_port_association(bgpvpn['id'], **body) else: obj = client.create_bgpvpn_router_association( @@ -123,10 +123,10 @@ def take_action(self, parsed_args): arg2body = getattr(self, '_args2body', None) if callable(arg2body): body = arg2body(bgpvpn['id'], parsed_args) - if self._assoc_res_name == constants.NETWORK_ASSOC: + if self._resource == constants.NETWORK_ASSOC: client.update_bgpvpn_network_association( bgpvpn['id'], parsed_args.resource_association_id, **body) - elif self._assoc_res_name == constants.PORT_ASSOCS: + elif self._resource == constants.PORT_ASSOC: client.update_bgpvpn_port_association( bgpvpn['id'], parsed_args.resource_association_id, **body) else: @@ -165,9 +165,9 @@ def take_action(self, parsed_args): fails = 0 for id in parsed_args.resource_association_ids: try: - if self._assoc_res_name == constants.NETWORK_ASSOC: + if self._resource == constants.NETWORK_ASSOC: client.delete_bgpvpn_network_association(bgpvpn['id'], id) - elif self._assoc_res_name == constants.PORT_ASSOCS: + elif self._resource == constants.PORT_ASSOC: client.delete_bgpvpn_port_association(bgpvpn['id'], id) else: client.delete_bgpvpn_router_association(bgpvpn['id'], id) @@ -221,10 +221,10 @@ def take_action(self, parsed_args): params = {} if parsed_args.property: params.update(parsed_args.property) - if self._assoc_res_name == constants.NETWORK_ASSOC: + if self._resource == constants.NETWORK_ASSOC: objs = client.bgpvpn_network_associations( bgpvpn['id'], retrieve_all=True, **params) - elif self._assoc_res_name == constants.PORT_ASSOCS: + elif self._resource == constants.PORT_ASSOC: objs = client.bgpvpn_port_associations( bgpvpn['id'], retrieve_all=True, **params) else: @@ -265,10 +265,10 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.network bgpvpn = client.find_bgpvpn(parsed_args.bgpvpn) - if self._assoc_res_name == constants.NETWORK_ASSOC: + if self._resource == constants.NETWORK_ASSOC: obj = client.get_bgpvpn_network_association( bgpvpn['id'], parsed_args.resource_association_id) - elif self._assoc_res_name == constants.PORT_ASSOCS: + elif self._resource == constants.PORT_ASSOC: obj = client.get_bgpvpn_port_association( bgpvpn['id'], parsed_args.resource_association_id) else: From d36a8a4a177b05eba9998637127fcf3b6d02373f Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Thu, 5 Sep 2024 16:02:19 +0000 Subject: [PATCH 823/845] Update master for stable/2024.2 Add file to the reno documentation build to show release notes for stable/2024.2. Use pbr instruction to increment the minor version number automatically so that master versions are higher than the versions on stable/2024.2. Sem-Ver: feature Change-Id: I02c3547991a272afcc3a8902476949cd0bd1ce74 --- releasenotes/source/2024.2.rst | 6 ++++++ releasenotes/source/index.rst | 1 + 2 files changed, 7 insertions(+) create mode 100644 releasenotes/source/2024.2.rst diff --git a/releasenotes/source/2024.2.rst b/releasenotes/source/2024.2.rst new file mode 100644 index 000000000..aaebcbc8c --- /dev/null +++ b/releasenotes/source/2024.2.rst @@ -0,0 +1,6 @@ +=========================== +2024.2 Series Release Notes +=========================== + +.. release-notes:: + :branch: stable/2024.2 diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index e3e11c8fa..0a5640c8e 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -6,6 +6,7 @@ :maxdepth: 1 unreleased + 2024.2 2024.1 2023.2 2023.1 From 5438377b9c815ca1bfb13a22b4d6645f3731223d Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Sat, 12 Oct 2024 00:05:58 +0900 Subject: [PATCH 824/845] Drop unused tempest from test requirements None of the tests implemented in this repository depend on tempest. In addition testscenarios is not used either, and can be removed. Change-Id: I642f8a5093a6e2b5528e446a798270734c4ed30d --- test-requirements.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/test-requirements.txt b/test-requirements.txt index 8cd49ce02..2e847fa46 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -11,5 +11,3 @@ python-subunit>=1.0.0 # Apache-2.0/BSD requests-mock>=1.2.0 # Apache-2.0 stestr>=2.0.0 # Apache-2.0 testtools>=2.2.0 # MIT -testscenarios>=0.4 # Apache-2.0/BSD -tempest>=17.1.0 # Apache-2.0 From 8a2f07683bea1877ff746faaa19df76f953f71b6 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Sun, 13 Oct 2024 01:30:27 +0900 Subject: [PATCH 825/845] tox: Drop envdir tox now always recreates an env although the env is shared using envdir options. ~~~ $ tox -e genpolicy genpolicy: recreate env because env type changed from {'name': 'genconfig', 'type': 'VirtualEnvRunner'} to {'name': 'genpolicy', 'type': 'VirtualEnvRunner'} ~~~ According to the maintainer of tox, this functionality is not intended to be supported. https://github.com/tox-dev/tox/issues/425#issuecomment-1011944293 Change-Id: I496d3ffe65be13df80c29ac4582596be0aa4d909 --- tox.ini | 1 - 1 file changed, 1 deletion(-) diff --git a/tox.ini b/tox.ini index cda43b91c..a939f8b65 100644 --- a/tox.ini +++ b/tox.ini @@ -51,7 +51,6 @@ deps = commands = sphinx-build -W -b html doc/source doc/build/html [testenv:pdf-docs] -envdir = {toxworkdir}/docs deps = {[testenv:docs]deps} allowlist_externals = make From f882f1ddb60bcd77096eb8a74e9e86d10723e8be Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Thu, 24 Oct 2024 21:19:15 +0900 Subject: [PATCH 826/845] Remove Python 3.8 support Python 3.8 was removed from the tested runtimes for 2024.2[1] and has not been tested since then. Also add a few newer versions which are part of the tested runtimes for 2025.1. [1] https://governance.openstack.org/tc/reference/runtimes/2024.2.html Change-Id: Ic0c05be1ce22dc2a5c67c6eb2b215d50d57ff116 --- releasenotes/notes/remove-py38-26a1befde3f44b82.yaml | 5 +++++ setup.cfg | 6 ++++-- 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 releasenotes/notes/remove-py38-26a1befde3f44b82.yaml diff --git a/releasenotes/notes/remove-py38-26a1befde3f44b82.yaml b/releasenotes/notes/remove-py38-26a1befde3f44b82.yaml new file mode 100644 index 000000000..a0440dc80 --- /dev/null +++ b/releasenotes/notes/remove-py38-26a1befde3f44b82.yaml @@ -0,0 +1,5 @@ +--- +upgrade: + - | + Python 3.8 support was dropped. The minimum version of Python now supported + is Python 3.9. diff --git a/setup.cfg b/setup.cfg index 7af675d1d..3005e895b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -6,7 +6,7 @@ description_file = author = OpenStack Networking Project author_email = openstack-discuss@lists.openstack.org home_page = https://docs.openstack.org/python-neutronclient/latest/ -python_requires = >=3.8 +python_requires = >=3.9 classifier = Environment :: OpenStack Intended Audience :: Developers @@ -18,8 +18,10 @@ classifier = Programming Language :: Python :: Implementation :: CPython Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3 - Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 + Programming Language :: Python :: 3.12 [files] packages = From b2107dc867e7a5bb6d86caa4b2a3fc8da3dc09eb Mon Sep 17 00:00:00 2001 From: elajkat Date: Wed, 6 Nov 2024 10:11:22 +0100 Subject: [PATCH 827/845] vpnaas: show external_vx_ip for vpn service show Closes-Bug: #2086144 Change-Id: Ic8ae85ee62e35991e8bb0096cdc6785a0b04e545 --- neutronclient/osc/v2/vpnaas/vpnservice.py | 10 ++++++---- neutronclient/tests/unit/osc/v2/vpnaas/fakes.py | 2 ++ .../tests/unit/osc/v2/vpnaas/test_vpnservice.py | 8 ++++++++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/neutronclient/osc/v2/vpnaas/vpnservice.py b/neutronclient/osc/v2/vpnaas/vpnservice.py index f845a57ff..9e1e489ca 100644 --- a/neutronclient/osc/v2/vpnaas/vpnservice.py +++ b/neutronclient/osc/v2/vpnaas/vpnservice.py @@ -36,6 +36,8 @@ ('status', 'Status', column_util.LIST_BOTH), ('description', 'Description', column_util.LIST_LONG_ONLY), ('project_id', 'Project', column_util.LIST_LONG_ONLY), + ('external_v4_ip', 'Ext v4 IP', column_util.LIST_LONG_ONLY), + ('external_v6_ip', 'Ext v6 IP', column_util.LIST_LONG_ONLY), ) _attr_map_dict = { @@ -48,6 +50,8 @@ 'status': 'Status', 'description': 'Description', 'project_id': 'Project', + 'external_v4_ip': 'Ext v4 IP', + 'external_v6_ip': 'Ext v6 IP', } @@ -135,8 +139,7 @@ def take_action(self, parsed_args): attrs['router_id'] = _router_id obj = client.create_vpn_service(**attrs) display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( - obj, _attr_map_dict, ['location', 'tenant_id', 'external_v4_ip', - 'external_v6_ip']) + obj, _attr_map_dict, ['location', 'tenant_id']) data = utils.get_dict_properties(obj, columns) return display_columns, data @@ -244,7 +247,6 @@ def take_action(self, parsed_args): ignore_missing=False)['id'] obj = client.get_vpn_service(vpn_id) display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( - obj, _attr_map_dict, ['location', 'tenant_id', 'external_v4_ip', - 'external_v6_ip']) + obj, _attr_map_dict, ['location', 'tenant_id']) data = utils.get_dict_properties(obj, columns) return (display_columns, data) diff --git a/neutronclient/tests/unit/osc/v2/vpnaas/fakes.py b/neutronclient/tests/unit/osc/v2/vpnaas/fakes.py index 2344c0a1f..24e6497fc 100644 --- a/neutronclient/tests/unit/osc/v2/vpnaas/fakes.py +++ b/neutronclient/tests/unit/osc/v2/vpnaas/fakes.py @@ -125,6 +125,8 @@ def __init__(self): ('status', 'ACTIVE'), ('description', 'my-desc-' + uuid.uuid4().hex), ('project_id', 'project-id-' + uuid.uuid4().hex), + ('external_v4_ip', '192.0.2.42'), + ('external_v6_ip', '2001:0db8:207a:4a3a:053b:6fab:7df9:1afd'), )) diff --git a/neutronclient/tests/unit/osc/v2/vpnaas/test_vpnservice.py b/neutronclient/tests/unit/osc/v2/vpnaas/test_vpnservice.py index 458c7f654..521fe741c 100644 --- a/neutronclient/tests/unit/osc/v2/vpnaas/test_vpnservice.py +++ b/neutronclient/tests/unit/osc/v2/vpnaas/test_vpnservice.py @@ -106,10 +106,14 @@ def _mock_vpnservice(*args, **kwargs): 'Status', 'Description', 'Project', + 'Ext v4 IP', + 'Ext v6 IP', ) self.data = _generate_data() self.ordered_headers = ( 'Description', + 'Ext v4 IP', + 'Ext v6 IP', 'Flavor', 'ID', 'Name', @@ -121,6 +125,8 @@ def _mock_vpnservice(*args, **kwargs): ) self.ordered_data = ( _vpnservice['description'], + _vpnservice['external_v4_ip'], + _vpnservice['external_v6_ip'], _vpnservice['flavor_id'], _vpnservice['id'], _vpnservice['name'], @@ -132,6 +138,8 @@ def _mock_vpnservice(*args, **kwargs): ) self.ordered_columns = ( 'description', + 'external_v4_ip', + 'external_v6_ip', 'flavor_id', 'id', 'name', From 636ac764d350c610504eab2e7b3283248581d5e3 Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Thu, 14 Nov 2024 10:35:43 +0000 Subject: [PATCH 828/845] reno: Update master for unmaintained/2023.1 Update the 2023.1 release notes configuration to build from unmaintained/2023.1. Change-Id: Id69fb2c2e197e91d679473c52f556e378fa3ba1e --- releasenotes/source/2023.1.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/source/2023.1.rst b/releasenotes/source/2023.1.rst index d1238479b..2c9a36fae 100644 --- a/releasenotes/source/2023.1.rst +++ b/releasenotes/source/2023.1.rst @@ -3,4 +3,4 @@ =========================== .. release-notes:: - :branch: stable/2023.1 + :branch: unmaintained/2023.1 From dc026cd3d6a280768b2d477410058b16ef86e124 Mon Sep 17 00:00:00 2001 From: Bodo Petermann Date: Wed, 26 Feb 2025 15:57:35 +0100 Subject: [PATCH 829/845] vpnaas: add support for more ciphers (auth, encryption, pfs modes) Extend the lists of choices for encryption algorithms, auth algorithms, and PFS groups to include the additions made in neutron-vpnaas. Encryption algorithms: add AES CCM mode and AES GCM mode variants for 128/192/256 bit keys and 8/12/16 octet ICVs, add AES CTR modes for 128/192/256 bit keys Auth algorithms: add aes-xcbc and aes-cmac. PFS: add Diffie Hellman groups 15 to 31. Related-Bug: #1938284 Change-Id: I3fd17b93820da9d86b2fc4bc89058475d7629d5d --- neutronclient/osc/v2/vpnaas/ikepolicy.py | 66 +++++++++++++++++++++- neutronclient/osc/v2/vpnaas/ipsecpolicy.py | 66 +++++++++++++++++++++- 2 files changed, 126 insertions(+), 6 deletions(-) diff --git a/neutronclient/osc/v2/vpnaas/ikepolicy.py b/neutronclient/osc/v2/vpnaas/ikepolicy.py index 766a550f1..0a8aecb08 100644 --- a/neutronclient/osc/v2/vpnaas/ikepolicy.py +++ b/neutronclient/osc/v2/vpnaas/ikepolicy.py @@ -56,6 +56,66 @@ 'project_id': 'Project', } +_auth_algorithms = [ + 'sha1', + 'sha256', + 'sha384', + 'sha512', + 'aes-xcbc', + 'aes-cmac', +] + +_encryption_algorithms = [ + '3des', + 'aes-128', + 'aes-192', + 'aes-256', + 'aes-128-ccm-8', + 'aes-192-ccm-8', + 'aes-256-ccm-8', + 'aes-128-ccm-12', + 'aes-192-ccm-12', + 'aes-256-ccm-12', + 'aes-128-ccm-16', + 'aes-192-ccm-16', + 'aes-256-ccm-16', + 'aes-128-gcm-8', + 'aes-192-gcm-8', + 'aes-256-gcm-8', + 'aes-128-gcm-12', + 'aes-192-gcm-12', + 'aes-256-gcm-12', + 'aes-128-gcm-16', + 'aes-192-gcm-16', + 'aes-256-gcm-16', + 'aes-128-ctr', + 'aes-192-ctr', + 'aes-256-ctr', +] + +_pfs_groups = [ + 'group2', + 'group5', + 'group14', + 'group15', + 'group16', + 'group17', + 'group18', + 'group19', + 'group20', + 'group21', + 'group22', + 'group23', + 'group24', + 'group25', + 'group26', + 'group27', + 'group28', + 'group29', + 'group30', + 'group31', +] + def _convert_to_lowercase(string): return string.lower() @@ -68,12 +128,12 @@ def _get_common_parser(parser): help=_('Description of the IKE policy')) parser.add_argument( '--auth-algorithm', - choices=['sha1', 'sha256', 'sha384', 'sha512'], + choices=_auth_algorithms, type=_convert_to_lowercase, help=_('Authentication algorithm')) parser.add_argument( '--encryption-algorithm', - choices=['aes-128', '3des', 'aes-192', 'aes-256'], + choices=_encryption_algorithms, type=_convert_to_lowercase, help=_('Encryption algorithm')) parser.add_argument( @@ -88,7 +148,7 @@ def _get_common_parser(parser): help=_('IKE version for the policy')) parser.add_argument( '--pfs', - choices=['group5', 'group2', 'group14'], + choices=_pfs_groups, type=_convert_to_lowercase, help=_('Perfect Forward Secrecy')) parser.add_argument( diff --git a/neutronclient/osc/v2/vpnaas/ipsecpolicy.py b/neutronclient/osc/v2/vpnaas/ipsecpolicy.py index 877602ffc..c6e42bd06 100644 --- a/neutronclient/osc/v2/vpnaas/ipsecpolicy.py +++ b/neutronclient/osc/v2/vpnaas/ipsecpolicy.py @@ -54,6 +54,66 @@ 'project_id': 'Project', } +_auth_algorithms = [ + 'sha1', + 'sha256', + 'sha384', + 'sha512', + 'aes-xcbc', + 'aes-cmac', +] + +_encryption_algorithms = [ + '3des', + 'aes-128', + 'aes-192', + 'aes-256', + 'aes-128-ccm-8', + 'aes-192-ccm-8', + 'aes-256-ccm-8', + 'aes-128-ccm-12', + 'aes-192-ccm-12', + 'aes-256-ccm-12', + 'aes-128-ccm-16', + 'aes-192-ccm-16', + 'aes-256-ccm-16', + 'aes-128-gcm-8', + 'aes-192-gcm-8', + 'aes-256-gcm-8', + 'aes-128-gcm-12', + 'aes-192-gcm-12', + 'aes-256-gcm-12', + 'aes-128-gcm-16', + 'aes-192-gcm-16', + 'aes-256-gcm-16', + 'aes-128-ctr', + 'aes-192-ctr', + 'aes-256-ctr', +] + +_pfs_groups = [ + 'group2', + 'group5', + 'group14', + 'group15', + 'group16', + 'group17', + 'group18', + 'group19', + 'group20', + 'group21', + 'group22', + 'group23', + 'group24', + 'group25', + 'group26', + 'group27', + 'group28', + 'group29', + 'group30', + 'group31', +] + def _convert_to_lowercase(string): return string.lower() @@ -66,7 +126,7 @@ def _get_common_parser(parser): help=_('Description of the IPsec policy')) parser.add_argument( '--auth-algorithm', - choices=['sha1', 'sha256', 'sha384', 'sha512'], + choices=_auth_algorithms, type=_convert_to_lowercase, help=_('Authentication algorithm for IPsec policy')) parser.add_argument( @@ -76,7 +136,7 @@ def _get_common_parser(parser): help=_('Encapsulation mode for IPsec policy')) parser.add_argument( '--encryption-algorithm', - choices=['3des', 'aes-128', 'aes-192', 'aes-256'], + choices=_encryption_algorithms, type=_convert_to_lowercase, help=_('Encryption algorithm for IPsec policy')) parser.add_argument( @@ -86,7 +146,7 @@ def _get_common_parser(parser): help=vpn_utils.lifetime_help("IPsec")) parser.add_argument( '--pfs', - choices=['group2', 'group5', 'group14'], + choices=_pfs_groups, type=_convert_to_lowercase, help=_('Perfect Forward Secrecy for IPsec policy')) parser.add_argument( From 370bd215c7fb59822e529ae2a0a136803d40e535 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Sat, 1 Mar 2025 01:10:46 +0900 Subject: [PATCH 830/845] Remove unnecessary direct dependency on iso8601 Direct dependency on the library was removed by [1]. [1] 9c464ba53f0c1317a397f96202b577f0729fc4eb Change-Id: Id24f85b68c42977cb3f34c66934238a16e03a79c --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 5f10ef907..4c6371c3f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,6 @@ pbr!=2.1.0,>=2.0.0 # Apache-2.0 cliff>=3.4.0 # Apache-2.0 debtcollector>=1.2.0 # Apache-2.0 -iso8601>=0.1.11 # MIT netaddr>=0.7.18 # BSD openstacksdk>=1.5.0 # Apache-2.0 osc-lib>=1.12.0 # Apache-2.0 From 690bb97d9dff389af273ddc517029c1612028839 Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Thu, 6 Mar 2025 09:16:53 +0000 Subject: [PATCH 831/845] Update master for stable/2025.1 Add file to the reno documentation build to show release notes for stable/2025.1. Use pbr instruction to increment the minor version number automatically so that master versions are higher than the versions on stable/2025.1. Sem-Ver: feature Change-Id: Ia1fb8c591c315404d4e758a657a4b35fe523dfff --- releasenotes/source/2025.1.rst | 6 ++++++ releasenotes/source/index.rst | 1 + 2 files changed, 7 insertions(+) create mode 100644 releasenotes/source/2025.1.rst diff --git a/releasenotes/source/2025.1.rst b/releasenotes/source/2025.1.rst new file mode 100644 index 000000000..3add0e53a --- /dev/null +++ b/releasenotes/source/2025.1.rst @@ -0,0 +1,6 @@ +=========================== +2025.1 Series Release Notes +=========================== + +.. release-notes:: + :branch: stable/2025.1 diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index 0a5640c8e..d146d481c 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -6,6 +6,7 @@ :maxdepth: 1 unreleased + 2025.1 2024.2 2024.1 2023.2 From 857b4b233cb8353c362809e78a97bdb04f0e98fd Mon Sep 17 00:00:00 2001 From: elajkat Date: Tue, 22 Apr 2025 13:01:02 +0200 Subject: [PATCH 832/845] Add warning for using the python bindings The deprecation of python-neutronclient bindings was discussed on the 2025.2 (Flamingo) PTG: https://etherpad.opendev.org/p/apr2025-ptg-neutron#L163 Change-Id: Ieb08fc0884d7f386dc94090dce9d284d786d1ec2 --- README.rst | 1 + neutronclient/v2_0/client.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/README.rst b/README.rst index e8c42d88a..eaac15c2c 100644 --- a/README.rst +++ b/README.rst @@ -20,6 +20,7 @@ provides a Python API (the ``neutronclient`` module). .. note:: This project has been deprecated. The CLI code has been deleted and is not accessible anymore. The Python bindings are still in use by other projects but no new features will be added to this project. + All projects under Openstack governance migrating to use OpenstackSDK. Any new feature should be proposed to OpenStack SDK and OpenStack Client. diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index de0529182..9922eb31c 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -250,6 +250,9 @@ class ClientBase(object): def __init__(self, **kwargs): """Initialize a new client for the Neutron v2.0 API.""" super(ClientBase, self).__init__() + _logger.warning("The python binding code in neutronclient is " + "deprecated in favor of OpenstackSDK, please use " + "that as this will be removed in a future release.") self.retries = kwargs.pop('retries', 0) self.raise_errors = kwargs.pop('raise_errors', True) self.httpclient = client.construct_http_client(**kwargs) From 905be917fe22b44f9df25c018dec155b904938b1 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Tue, 10 Jun 2025 21:07:44 +0900 Subject: [PATCH 833/845] Drop explicit dependency on python-subunit It is no longer directly used by any test code in this repository since we switched to stestr[1]. It is now installed as a dependency of stestr. [1] 259c386d424370160c861748ccd18f0c08452d3c Change-Id: Iacbdd3477a9e0b2d2bae3e09d20867d794586673 --- test-requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index 2e847fa46..464d2b8a4 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -7,7 +7,6 @@ flake8-import-order>=0.18.0,<0.19.0 # LGPLv3 oslotest>=3.2.0 # Apache-2.0 osprofiler>=2.3.0 # Apache-2.0 python-openstackclient>=3.12.0 # Apache-2.0 -python-subunit>=1.0.0 # Apache-2.0/BSD requests-mock>=1.2.0 # Apache-2.0 stestr>=2.0.0 # Apache-2.0 testtools>=2.2.0 # MIT From 275924ecc8968a3bc77020db7223ac3b32731b24 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Wed, 2 Jul 2025 23:22:56 +0900 Subject: [PATCH 834/845] Drop unused os-client-config It was deprecated[1] after the code was merged into openstacksdk[2]. It's not actually used by any code in this repository. [1] https://review.opendev.org/c/openstack/os-client-config/+/549307 [2] https://review.opendev.org/c/openstack/openstacksdk/+/518128 Change-Id: Ia93d2952ca09fc70a970a85789775441ed114d49 Signed-off-by: Takashi Kajinami --- .zuul.yaml | 1 - requirements.txt | 1 - 2 files changed, 2 deletions(-) diff --git a/.zuul.yaml b/.zuul.yaml index 0bbe8f4dc..7c8822186 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -31,7 +31,6 @@ - openstack/keystoneauth - openstack/neutron - openstack/neutron-lib - - openstack/os-client-config - openstack/python-cinderclient - openstack/python-glanceclient - openstack/python-ironicclient diff --git a/requirements.txt b/requirements.txt index 4c6371c3f..41ca9d7ef 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,6 @@ oslo.i18n>=3.15.3 # Apache-2.0 oslo.log>=3.36.0 # Apache-2.0 oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0 oslo.utils>=3.33.0 # Apache-2.0 -os-client-config>=1.28.0 # Apache-2.0 keystoneauth1>=3.8.0 # Apache-2.0 # keystoneclient is used only by neutronclient.osc.utils # TODO(amotoki): Drop this after osc.utils has no dependency on keystoneclient From 2f170ce6aaa7fee7cc83504d8e558d6507b736f4 Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Thu, 4 Sep 2025 13:44:40 +0000 Subject: [PATCH 835/845] Update master for stable/2025.2 Add file to the reno documentation build to show release notes for stable/2025.2. Use pbr instruction to increment the minor version number automatically so that master versions are higher than the versions on stable/2025.2. Sem-Ver: feature Change-Id: I97f7bba4f875b98d98f03ba60ee394a1aa64a3fd Signed-off-by: OpenStack Release Bot Generated-By: openstack/project-config:roles/copy-release-tools-scripts/files/release-tools/add_release_note_page.sh --- releasenotes/source/2025.2.rst | 6 ++++++ releasenotes/source/index.rst | 1 + 2 files changed, 7 insertions(+) create mode 100644 releasenotes/source/2025.2.rst diff --git a/releasenotes/source/2025.2.rst b/releasenotes/source/2025.2.rst new file mode 100644 index 000000000..4dae18d86 --- /dev/null +++ b/releasenotes/source/2025.2.rst @@ -0,0 +1,6 @@ +=========================== +2025.2 Series Release Notes +=========================== + +.. release-notes:: + :branch: stable/2025.2 diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index d146d481c..73d4a0cab 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -6,6 +6,7 @@ :maxdepth: 1 unreleased + 2025.2 2025.1 2024.2 2024.1 From 01ffc4684a4a8b443bd2c5d66f3335af7fdaddc4 Mon Sep 17 00:00:00 2001 From: Mohammed Naser Date: Fri, 12 Sep 2025 11:29:32 -0400 Subject: [PATCH 836/845] Add Tap-as-a-Service client code This commit adds the Tap-as-a-Service client code as-is based on the discussion that it can now be included into the Neutron client[1]. [1]: https://lists.openstack.org/archives/list/openstack-discuss@lists.openstack.org/message/PCITF3UPLEEVQB7EVJ2MB6FLAI3RFYSR/ Change-Id: I82b3229b3f9ac7ef57324d0a611527c77d26c3b6 Signed-off-by: Mohammed Naser --- neutronclient/osc/v2/taas/__init__.py | 0 neutronclient/osc/v2/taas/tap_flow.py | 225 ++++++++++++++ neutronclient/osc/v2/taas/tap_mirror.py | 222 ++++++++++++++ neutronclient/osc/v2/taas/tap_service.py | 211 +++++++++++++ .../tests/unit/osc/v2/taas/__init__.py | 0 neutronclient/tests/unit/osc/v2/taas/fakes.py | 122 ++++++++ .../unit/osc/v2/taas/test_osc_tap_flow.py | 283 ++++++++++++++++++ .../unit/osc/v2/taas/test_osc_tap_mirror.py | 267 +++++++++++++++++ .../unit/osc/v2/taas/test_osc_tap_service.py | 250 ++++++++++++++++ setup.cfg | 16 + 10 files changed, 1596 insertions(+) create mode 100644 neutronclient/osc/v2/taas/__init__.py create mode 100644 neutronclient/osc/v2/taas/tap_flow.py create mode 100644 neutronclient/osc/v2/taas/tap_mirror.py create mode 100644 neutronclient/osc/v2/taas/tap_service.py create mode 100644 neutronclient/tests/unit/osc/v2/taas/__init__.py create mode 100644 neutronclient/tests/unit/osc/v2/taas/fakes.py create mode 100644 neutronclient/tests/unit/osc/v2/taas/test_osc_tap_flow.py create mode 100644 neutronclient/tests/unit/osc/v2/taas/test_osc_tap_mirror.py create mode 100644 neutronclient/tests/unit/osc/v2/taas/test_osc_tap_service.py diff --git a/neutronclient/osc/v2/taas/__init__.py b/neutronclient/osc/v2/taas/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/osc/v2/taas/tap_flow.py b/neutronclient/osc/v2/taas/tap_flow.py new file mode 100644 index 000000000..428d059e0 --- /dev/null +++ b/neutronclient/osc/v2/taas/tap_flow.py @@ -0,0 +1,225 @@ +# All Rights Reserved 2020 +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import logging + +from osc_lib.cli import format_columns +from osc_lib.cli import identity as identity_utils +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils as osc_utils +from osc_lib.utils import columns as column_util + +from neutronclient._i18n import _ +from neutronclient.osc.v2.taas import tap_service + +LOG = logging.getLogger(__name__) + +TAP_FLOW = 'tap_flow' +TAP_FLOWS = '%ss' % TAP_FLOW + +_attr_map = ( + ('id', 'ID', column_util.LIST_BOTH), + ('tenant_id', 'Tenant', column_util.LIST_LONG_ONLY), + ('name', 'Name', column_util.LIST_BOTH), + ('status', 'Status', column_util.LIST_BOTH), + ('source_port', 'source_port', column_util.LIST_BOTH), + ('tap_service_id', 'tap_service_id', column_util.LIST_BOTH), + ('direction', 'Direction', column_util.LIST_BOTH), +) + +_formatters = { + 'vlan_filter': format_columns.ListColumn, +} + + +def _add_updatable_args(parser): + parser.add_argument( + '--name', + help=_('Name of this Tap service.')) + parser.add_argument( + '--description', + help=_('Description for this Tap service.')) + + +class CreateTapFlow(command.ShowOne): + _description = _("Create a tap flow") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + identity_utils.add_project_owner_option_to_parser(parser) + _add_updatable_args(parser) + parser.add_argument( + '--port', + required=True, + metavar="SOURCE_PORT", + help=_('Source port to which the Tap Flow is connected.')) + parser.add_argument( + '--tap-service', + required=True, + metavar="TAP_SERVICE", + help=_('Tap Service to which the Tap Flow belongs.')) + parser.add_argument( + '--direction', + required=True, + metavar="DIRECTION", + choices=['IN', 'OUT', 'BOTH'], + type=lambda s: s.upper(), + help=_('Direction of the Tap flow. Possible options are: ' + 'IN, OUT, BOTH')) + parser.add_argument( + '--vlan-filter', + required=False, + metavar="VLAN_FILTER", + help=_('VLAN Ids to be mirrored in the form of range string.')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + attrs = {} + if parsed_args.name is not None: + attrs['name'] = str(parsed_args.name) + if parsed_args.description is not None: + attrs['description'] = str(parsed_args.description) + if parsed_args.port is not None: + source_port = client.find_port(parsed_args.port)['id'] + attrs['source_port'] = source_port + if parsed_args.tap_service is not None: + tap_service_id = client.find_tap_service( + parsed_args.tap_service)['id'] + attrs['tap_service_id'] = tap_service_id + if parsed_args.direction is not None: + attrs['direction'] = parsed_args.direction + if parsed_args.vlan_filter is not None: + attrs['vlan_filter'] = parsed_args.vlan_filter + if 'project' in parsed_args and parsed_args.project is not None: + project_id = identity_utils.find_project( + self.app.client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + attrs['tenant_id'] = project_id + obj = client.create_tap_flow(**attrs) + display_columns, columns = tap_service._get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data + + +class ListTapFlow(command.Lister): + _description = _("List tap flows that belong to a given tenant") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + identity_utils.add_project_owner_option_to_parser(parser) + + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + params = {} + if parsed_args.project is not None: + project_id = identity_utils.find_project( + self.app.client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + params['tenant_id'] = project_id + objs = client.tap_flows(retrieve_all=True, params=params) + headers, columns = column_util.get_column_definitions( + _attr_map, long_listing=True) + return (headers, (osc_utils.get_dict_properties( + s, columns, formatters=_formatters) for s in objs)) + + +class ShowTapFlow(command.ShowOne): + _description = _("Show information of a given tap flow") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_FLOW, + metavar="<%s>" % TAP_FLOW, + help=_("ID or name of tap flow to look up."), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + id = client.find_tap_flow(parsed_args.tap_flow, + ignore_missing=False).id + obj = client.get_tap_flow(id) + display_columns, columns = tap_service._get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteTapFlow(command.Command): + _description = _("Delete a tap flow") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_FLOW, + metavar="<%s>" % TAP_FLOW, + nargs="+", + help=_("ID(s) or name(s) of tap flow to delete."), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + fails = 0 + for id_or_name in parsed_args.tap_flow: + try: + id = client.find_tap_flow(id_or_name, + ignore_missing=False).id + client.delete_tap_flow(id) + LOG.warning("Tap flow %(id)s deleted", {'id': id}) + except Exception as e: + fails += 1 + LOG.error("Failed to delete tap flow with name or ID " + "'%(id_or_name)s': %(e)s", + {'id_or_name': id_or_name, 'e': e}) + if fails > 0: + msg = (_("Failed to delete %(fails)s of %(total)s tap flow.") % + {'fails': fails, 'total': len(parsed_args.tap_flow)}) + raise exceptions.CommandError(msg) + + +class UpdateTapFlow(command.ShowOne): + _description = _("Update a tap flow.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_FLOW, + metavar="<%s>" % TAP_FLOW, + help=_("ID or name of tap flow to update."), + ) + _add_updatable_args(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + original_t_f = client.find_tap_flow(parsed_args.tap_flow, + ignore_missing=False).id + attrs = {} + if parsed_args.name is not None: + attrs['name'] = str(parsed_args.name) + if parsed_args.description is not None: + attrs['description'] = str(parsed_args.description) + obj = client.update_tap_flow(original_t_f, **attrs) + columns, display_columns = column_util.get_columns(obj, _attr_map) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data diff --git a/neutronclient/osc/v2/taas/tap_mirror.py b/neutronclient/osc/v2/taas/tap_mirror.py new file mode 100644 index 000000000..af6723c09 --- /dev/null +++ b/neutronclient/osc/v2/taas/tap_mirror.py @@ -0,0 +1,222 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import logging + +from osc_lib.cli import identity as identity_utils +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils as osc_utils +from osc_lib.utils import columns as column_util + +from openstackclient.network.v2 import port as osc_port + +from neutronclient._i18n import _ +from neutronclient.osc.v2.taas import tap_service + + +LOG = logging.getLogger(__name__) + +TAP_MIRROR = 'tap_mirror' +TAP_MIRRORS = '%ss' % TAP_MIRROR + +_attr_map = ( + ('id', 'ID', column_util.LIST_BOTH), + ('tenant_id', 'Tenant', column_util.LIST_LONG_ONLY), + ('name', 'Name', column_util.LIST_BOTH), + ('port_id', 'Port', column_util.LIST_BOTH), + ('directions', 'Directions', column_util.LIST_LONG_ONLY), + ('remote_ip', 'Remote IP', column_util.LIST_BOTH), + ('mirror_type', 'Mirror Type', column_util.LIST_LONG_ONLY), +) + + +def _get_columns(item): + column_map = {} + hidden_columns = ['location', 'tenant_id'] + return osc_utils.get_osc_show_columns_for_sdk_resource( + item, + column_map, + hidden_columns + ) + + +class CreateTapMirror(command.ShowOne): + _description = _("Create a Tap Mirror") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + identity_utils.add_project_owner_option_to_parser(parser) + tap_service._add_updatable_args(parser) + parser.add_argument( + '--port', + dest='port_id', + required=True, + metavar="PORT", + help=_('Port to which the Tap Mirror is connected.')) + parser.add_argument( + '--directions', + dest='directions', + action=osc_port.JSONKeyValueAction, + required=True, + help=_('A dictionary of direction and tunnel_id. Direction can ' + 'be IN and OUT.')) + parser.add_argument( + '--remote-ip', + dest='remote_ip', + required=True, + help=_('The remote IP of the Tap Mirror, this will be the ' + 'remote end of the GRE or ERSPAN v1 tunnel')) + parser.add_argument( + '--mirror-type', + dest='mirror_type', + required=True, + help=_('The type of the mirroring, it can be gre or erspanv1')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + attrs = {} + if parsed_args.name is not None: + attrs['name'] = str(parsed_args.name) + if parsed_args.description is not None: + attrs['description'] = str(parsed_args.description) + if parsed_args.port_id is not None: + port_id = client.find_port(parsed_args.port_id)['id'] + attrs['port_id'] = port_id + if parsed_args.directions is not None: + attrs['directions'] = parsed_args.directions + if parsed_args.remote_ip is not None: + attrs['remote_ip'] = parsed_args.remote_ip + if parsed_args.mirror_type is not None: + attrs['mirror_type'] = parsed_args.mirror_type + if 'project' in parsed_args and parsed_args.project is not None: + project_id = identity_utils.find_project( + self.app.client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + attrs['tenant_id'] = project_id + obj = client.create_tap_mirror(**attrs) + display_columns, columns = tap_service._get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data + + +class ListTapMirror(command.Lister): + _description = _("List Tap Mirrors that belong to a given tenant") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + identity_utils.add_project_owner_option_to_parser(parser) + + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + params = {} + if parsed_args.project is not None: + project_id = identity_utils.find_project( + self.app.client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + params['tenant_id'] = project_id + objs = client.tap_mirrors(retrieve_all=True, params=params) + headers, columns = column_util.get_column_definitions( + _attr_map, long_listing=True) + return (headers, (osc_utils.get_dict_properties( + s, columns) for s in objs)) + + +class ShowTapMirror(command.ShowOne): + _description = _("Show information of a given Tap Mirror") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_MIRROR, + metavar="<%s>" % TAP_MIRROR, + help=_("ID or name of Tap Mirror to look up."), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + id = client.find_tap_mirror(parsed_args.tap_mirror, + ignore_missing=False).id + obj = client.get_tap_mirror(id) + display_columns, columns = tap_service._get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteTapMirror(command.Command): + _description = _("Delete a Tap Mirror") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_MIRROR, + metavar="<%s>" % TAP_MIRROR, + nargs="+", + help=_("ID(s) or name(s) of the Tap Mirror to delete."), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + fails = 0 + for id_or_name in parsed_args.tap_mirror: + try: + id = client.find_tap_mirror(id_or_name, + ignore_missing=False).id + + client.delete_tap_mirror(id) + LOG.warning("Tap Mirror %(id)s deleted", {'id': id}) + except Exception as e: + fails += 1 + LOG.error("Failed to delete Tap Mirror with name or ID " + "'%(id_or_name)s': %(e)s", + {'id_or_name': id_or_name, 'e': e}) + if fails > 0: + msg = (_("Failed to delete %(fails)s of %(total)s Tap Mirror.") % + {'fails': fails, 'total': len(parsed_args.tap_mirror)}) + raise exceptions.CommandError(msg) + + +class UpdateTapMirror(command.ShowOne): + _description = _("Update a Tap Mirror.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_MIRROR, + metavar="<%s>" % TAP_MIRROR, + help=_("ID or name of the Tap Mirror to update."), + ) + tap_service._add_updatable_args(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + original_t_s = client.find_tap_mirror(parsed_args.tap_mirror, + ignore_missing=False).id + attrs = {} + if parsed_args.name is not None: + attrs['name'] = str(parsed_args.name) + if parsed_args.description is not None: + attrs['description'] = str(parsed_args.description) + obj = client.update_tap_mirror(original_t_s, **attrs) + display_columns, columns = tap_service._get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data diff --git a/neutronclient/osc/v2/taas/tap_service.py b/neutronclient/osc/v2/taas/tap_service.py new file mode 100644 index 000000000..cd53b6825 --- /dev/null +++ b/neutronclient/osc/v2/taas/tap_service.py @@ -0,0 +1,211 @@ +# All Rights Reserved 2020 +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import logging + +from osc_lib.cli import identity as identity_utils +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils as osc_utils +from osc_lib.utils import columns as column_util + +from neutronclient._i18n import _ + + +LOG = logging.getLogger(__name__) + +TAP_SERVICE = 'tap_service' +TAP_SERVICES = '%ss' % TAP_SERVICE + +_attr_map = ( + ('id', 'ID', column_util.LIST_BOTH), + ('tenant_id', 'Tenant', column_util.LIST_LONG_ONLY), + ('name', 'Name', column_util.LIST_BOTH), + ('port_id', 'Port', column_util.LIST_BOTH), + ('status', 'Status', column_util.LIST_BOTH), +) + + +def _add_updatable_args(parser): + parser.add_argument( + '--name', + help=_('Name of this Tap service.')) + parser.add_argument( + '--description', + help=_('Description for this Tap service.')) + + +def _updatable_args2body(parsed_args, body): + for attribute in ['name', 'description']: + if (hasattr(parsed_args, attribute) and + getattr(parsed_args, attribute) is not None): + body[attribute] = getattr(parsed_args, attribute) + + +def _get_columns(item): + column_map = {} + hidden_columns = ['location', 'tenant_id'] + return osc_utils.get_osc_show_columns_for_sdk_resource( + item, + column_map, + hidden_columns + ) + + +class CreateTapService(command.ShowOne): + _description = _("Create a tap service") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + identity_utils.add_project_owner_option_to_parser(parser) + _add_updatable_args(parser) + parser.add_argument( + '--port', + dest='port_id', + required=True, + metavar="PORT", + help=_('Port to which the Tap service is connected.')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + attrs = {} + if parsed_args.name is not None: + attrs['name'] = str(parsed_args.name) + if parsed_args.description is not None: + attrs['description'] = str(parsed_args.description) + if parsed_args.port_id is not None: + port_id = client.find_port(parsed_args.port_id)['id'] + attrs['port_id'] = port_id + if 'project' in parsed_args and parsed_args.project is not None: + project_id = identity_utils.find_project( + self.app.client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + attrs['tenant_id'] = project_id + obj = client.create_tap_service(**attrs) + display_columns, columns = _get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data + + +class ListTapService(command.Lister): + _description = _("List tap services that belong to a given tenant") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + identity_utils.add_project_owner_option_to_parser(parser) + + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + params = {} + if parsed_args.project is not None: + project_id = identity_utils.find_project( + self.app.client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + params['tenant_id'] = project_id + objs = client.tap_services(retrieve_all=True, params=params) + headers, columns = column_util.get_column_definitions( + _attr_map, long_listing=True) + return (headers, (osc_utils.get_dict_properties( + s, columns) for s in objs)) + + +class ShowTapService(command.ShowOne): + _description = _("Show information of a given tap service") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_SERVICE, + metavar="<%s>" % TAP_SERVICE, + help=_("ID or name of tap service to look up."), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + id = client.find_tap_service(parsed_args.tap_service, + ignore_missing=False).id + obj = client.get_tap_service(id) + display_columns, columns = _get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteTapService(command.Command): + _description = _("Delete a tap service") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_SERVICE, + metavar="<%s>" % TAP_SERVICE, + nargs="+", + help=_("ID(s) or name(s) of tap service to delete."), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + fails = 0 + for id_or_name in parsed_args.tap_service: + try: + id = client.find_tap_service(id_or_name, + ignore_missing=False).id + + client.delete_tap_service(id) + LOG.warning("Tap service %(id)s deleted", {'id': id}) + except Exception as e: + fails += 1 + LOG.error("Failed to delete tap service with name or ID " + "'%(id_or_name)s': %(e)s", + {'id_or_name': id_or_name, 'e': e}) + if fails > 0: + msg = (_("Failed to delete %(fails)s of %(total)s tap service.") % + {'fails': fails, 'total': len(parsed_args.tap_service)}) + raise exceptions.CommandError(msg) + + +class UpdateTapService(command.ShowOne): + _description = _("Update a tap service.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_SERVICE, + metavar="<%s>" % TAP_SERVICE, + help=_("ID or name of tap service to update."), + ) + _add_updatable_args(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + original_t_s = client.find_tap_service(parsed_args.tap_service, + ignore_missing=False).id + attrs = {} + if parsed_args.name is not None: + attrs['name'] = str(parsed_args.name) + if parsed_args.description is not None: + attrs['description'] = str(parsed_args.description) + obj = client.update_tap_service(original_t_s, **attrs) + display_columns, columns = _get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data diff --git a/neutronclient/tests/unit/osc/v2/taas/__init__.py b/neutronclient/tests/unit/osc/v2/taas/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/tests/unit/osc/v2/taas/fakes.py b/neutronclient/tests/unit/osc/v2/taas/fakes.py new file mode 100644 index 000000000..ee32cb1ba --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/taas/fakes.py @@ -0,0 +1,122 @@ +# All Rights Reserved 2020 +# +# 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 copy + +from oslo_utils import uuidutils + + +class FakeTapService: + + @staticmethod + def create_tap_service(attrs=None): + """Create a fake tap service.""" + attrs = attrs or {} + tap_service_attrs = { + 'id': uuidutils.generate_uuid(), + 'tenant_id': uuidutils.generate_uuid(), + 'name': 'test_tap_service' + uuidutils.generate_uuid(), + 'status': 'ACTIVE', + } + tap_service_attrs.update(attrs) + return copy.deepcopy(tap_service_attrs) + + @staticmethod + def create_tap_services(attrs=None, count=1): + """Create multiple fake tap services.""" + + tap_services = [] + for i in range(0, count): + if attrs is None: + attrs = {'id': 'fake_id%d' % i} + elif getattr(attrs, 'id', None) is None: + attrs['id'] = 'fake_id%d' % i + tap_services.append(FakeTapService.create_tap_service( + attrs=attrs)) + + return tap_services + + +class FakeTapFlow: + + @staticmethod + def create_tap_flow(attrs=None): + """Create a fake tap service.""" + attrs = attrs or {} + tap_flow_attrs = { + 'id': uuidutils.generate_uuid(), + 'tenant_id': uuidutils.generate_uuid(), + 'name': 'test_tap_flow' + uuidutils.generate_uuid(), + 'status': 'ACTIVE', + 'direction': 'BOTH', + } + tap_flow_attrs.update(attrs) + return copy.deepcopy(tap_flow_attrs) + + @staticmethod + def create_tap_flows(attrs=None, count=1): + """Create multiple fake tap flows.""" + + tap_flows = [] + for i in range(0, count): + if attrs is None: + attrs = { + 'id': 'fake_id%d' % i, + 'source_port': uuidutils.generate_uuid(), + 'tap_service_id': uuidutils.generate_uuid() + } + elif getattr(attrs, 'id', None) is None: + attrs['id'] = 'fake_id%d' % i + tap_flows.append(FakeTapFlow.create_tap_flow(attrs=attrs)) + + return tap_flows + + +class FakeTapMirror(object): + + @staticmethod + def create_tap_mirror(attrs=None): + """Create a fake tap mirror.""" + attrs = attrs or {} + tap_mirror_attrs = { + 'id': uuidutils.generate_uuid(), + 'tenant_id': uuidutils.generate_uuid(), + 'name': 'test_tap_mirror' + uuidutils.generate_uuid(), + 'port_id': uuidutils.generate_uuid(), + 'directions': 'IN=99', + 'remote_ip': '192.10.10.2', + 'mirror_type': 'gre', + } + tap_mirror_attrs.update(attrs) + return copy.deepcopy(tap_mirror_attrs) + + @staticmethod + def create_tap_mirrors(attrs=None, count=1): + """Create multiple fake tap mirrors.""" + + tap_mirrors = [] + for i in range(0, count): + if attrs is None: + attrs = { + 'id': 'fake_id%d' % i, + 'port_id': uuidutils.generate_uuid(), + 'name': 'test_tap_mirror_%d' % i, + 'directions': 'IN=%d' % 99 + i, + 'remote_ip': '192.10.10.%d' % (i + 3), + } + elif getattr(attrs, 'id', None) is None: + attrs['id'] = 'fake_id%d' % i + tap_mirrors.append(FakeTapMirror.create_tap_mirror(attrs=attrs)) + + return tap_mirrors diff --git a/neutronclient/tests/unit/osc/v2/taas/test_osc_tap_flow.py b/neutronclient/tests/unit/osc/v2/taas/test_osc_tap_flow.py new file mode 100644 index 000000000..a92e443f3 --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/taas/test_osc_tap_flow.py @@ -0,0 +1,283 @@ +# All Rights Reserved 2020 +# +# 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 copy +import operator +from unittest import mock + +from neutronclient.tests.unit.osc.v2 import fakes as test_fakes +from openstack.network.v2 import tap_flow as _tap_flow +from osc_lib import utils as osc_utils +from osc_lib.utils import columns as column_util +from oslo_utils import uuidutils + +from neutronclient.osc.v2.taas import tap_flow as osc_tap_flow +from neutronclient.osc.v2.taas import tap_service as osc_tap_service +from neutronclient.tests.unit.osc.v2.taas import fakes + + +columns_long = tuple(col for col, _, listing_mode in osc_tap_flow._attr_map + if listing_mode in (column_util.LIST_BOTH, + column_util.LIST_LONG_ONLY)) +headers_long = tuple(head for _, head, listing_mode in + osc_tap_flow._attr_map if listing_mode in + (column_util.LIST_BOTH, column_util.LIST_LONG_ONLY)) +sorted_attr_map = sorted(osc_tap_flow._attr_map, key=operator.itemgetter(1)) +sorted_columns = tuple(col for col, _, _ in sorted_attr_map) +sorted_headers = tuple(head for _, head, _ in sorted_attr_map) + + +def _get_data(attrs, columns=sorted_columns): + return osc_utils.get_dict_properties(attrs, columns) + + +class TestCreateTapFlow(test_fakes.TestNeutronClientOSCV2): + + columns = ( + 'direction', + 'id', + 'name', + 'source_port', + 'status', + 'tap_service_id', + ) + + def setUp(self): + super().setUp() + self.cmd = osc_tap_flow.CreateTapFlow(self.app, self.namespace) + + def test_create_tap_flow(self): + """Test Create Tap Flow.""" + port_id = uuidutils.generate_uuid() + fake_tap_service = fakes.FakeTapService.create_tap_service( + attrs={'port_id': port_id} + ) + port_id = uuidutils.generate_uuid() + fake_tap_flow = fakes.FakeTapFlow.create_tap_flow( + attrs={ + 'source_port': port_id, + 'tap_service_id': fake_tap_service['id'] + } + ) + self.app.client_manager.network = mock.Mock() + self.app.client_manager.network.create_tap_flow = mock.Mock( + return_value=fake_tap_flow) + self.app.client_manager.network.find_port = mock.Mock( + return_value={'id': port_id}) + self.app.client_manager.network.find_tap_service = mock.Mock( + return_value=fake_tap_service) + arg_list = [ + '--name', fake_tap_flow['name'], + '--port', fake_tap_flow['source_port'], + '--tap-service', fake_tap_flow['tap_service_id'], + '--direction', fake_tap_flow['direction'], + ] + + verify_list = [ + ('name', fake_tap_flow['name']), + ('port', fake_tap_flow['source_port']), + ('tap_service', fake_tap_flow['tap_service_id']), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + with mock.patch.object( + self.app.client_manager.network, + '_find') as nc_find: + nc_find.side_effect = [ + {'id': fake_tap_flow['tap_service_id']} + ] + + columns, data = self.cmd.take_action(parsed_args) + mock_create_t_f = self.app.client_manager.network.create_tap_flow + mock_create_t_f.assert_called_once_with( + **{ + 'name': fake_tap_flow['name'], + 'source_port': fake_tap_flow['source_port'], + 'tap_service_id': fake_tap_flow['tap_service_id'], + 'direction': fake_tap_flow['direction'] + } + ) + self.assertEqual(self.columns, columns) + fake_data = _get_data( + fake_tap_flow, + osc_tap_service._get_columns(fake_tap_flow)[1]) + self.assertItemEqual(fake_data, data) + + +class TestListTapFlow(test_fakes.TestNeutronClientOSCV2): + def setUp(self): + super().setUp() + self.cmd = osc_tap_flow.ListTapFlow(self.app, self.namespace) + + def test_list_tap_flows(self): + """Test List Tap Flow.""" + fake_tap_flows = fakes.FakeTapFlow.create_tap_flows( + attrs={ + 'source_port': uuidutils.generate_uuid(), + 'tap_service_id': uuidutils.generate_uuid(), + }, + count=2) + self.app.client_manager.network = mock.Mock() + self.app.client_manager.network.tap_flows = mock.Mock( + return_value=fake_tap_flows) + arg_list = [] + verify_list = [] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + + headers, data = self.cmd.take_action(parsed_args) + + self.app.client_manager.network.tap_flows.assert_called_once() + self.assertEqual(headers, list(headers_long)) + self.assertListItemEqual( + list(data), + [_get_data(fake_tap_flow, columns_long) for fake_tap_flow + in fake_tap_flows] + ) + + +class TestDeleteTapFlow(test_fakes.TestNeutronClientOSCV2): + def setUp(self): + super().setUp() + self.app.client_manager.network = mock.Mock() + self.app.client_manager.network.find_tap_flow = mock.Mock( + side_effect=lambda name_or_id, ignore_missing: + _tap_flow.TapFlow(id=name_or_id)) + self.cmd = osc_tap_flow.DeleteTapFlow(self.app, self.namespace) + + def test_delete_tap_flow(self): + """Test Delete tap flow.""" + + fake_tap_flow = fakes.FakeTapFlow.create_tap_flow( + attrs={ + 'source_port': uuidutils.generate_uuid(), + 'tap_service_id': uuidutils.generate_uuid(), + } + ) + self.app.client_manager.network.delete_tap_flow = mock.Mock() + + arg_list = [ + fake_tap_flow['id'], + ] + verify_list = [ + (osc_tap_flow.TAP_FLOW, [fake_tap_flow['id']]), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + + result = self.cmd.take_action(parsed_args) + + mock_delete_tap_flow = self.app.client_manager.network.delete_tap_flow + mock_delete_tap_flow.assert_called_once_with(fake_tap_flow['id']) + self.assertIsNone(result) + + +class TestShowTapFlow(test_fakes.TestNeutronClientOSCV2): + columns = ( + 'direction', + 'id', + 'name', + 'source_port', + 'status', + 'tap_service_id' + ) + + def setUp(self): + super().setUp() + self.app.client_manager.network = mock.Mock() + self.app.client_manager.network.find_tap_flow = mock.Mock( + side_effect=lambda name_or_id, ignore_missing: + _tap_flow.TapFlow(id=name_or_id)) + self.cmd = osc_tap_flow.ShowTapFlow(self.app, self.namespace) + + def test_show_tap_flow(self): + """Test Show tap flow.""" + fake_tap_flow = fakes.FakeTapFlow.create_tap_flow( + attrs={ + 'source_port': uuidutils.generate_uuid(), + 'tap_service_id': uuidutils.generate_uuid(), + } + ) + self.app.client_manager.network.get_tap_flow = mock.Mock( + return_value=fake_tap_flow) + arg_list = [ + fake_tap_flow['id'], + ] + verify_list = [ + (osc_tap_flow.TAP_FLOW, fake_tap_flow['id']), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + + headers, data = self.cmd.take_action(parsed_args) + + self.app.client_manager.network.get_tap_flow.assert_called_once_with( + fake_tap_flow['id']) + self.assertEqual(self.columns, headers) + fake_data = _get_data( + fake_tap_flow, + osc_tap_service._get_columns(fake_tap_flow)[1]) + self.assertItemEqual(fake_data, data) + + +class TestUpdateTapFlow(test_fakes.TestNeutronClientOSCV2): + + _new_name = 'new_name' + + columns = ( + 'Direction', + 'ID', + 'Name', + 'Status', + 'Tenant', + 'source_port', + 'tap_service_id', + ) + + def setUp(self): + super().setUp() + self.cmd = osc_tap_flow.UpdateTapFlow(self.app, self.namespace) + self.app.client_manager.network = mock.Mock() + self.app.client_manager.network.find_tap_flow = mock.Mock( + side_effect=lambda name_or_id, ignore_missing: + _tap_flow.TapFlow(id=name_or_id)) + + def test_update_tap_flow(self): + """Test update tap service""" + fake_tap_flow = fakes.FakeTapFlow.create_tap_flow( + attrs={ + 'source_port': uuidutils.generate_uuid(), + 'tap_service_id': uuidutils.generate_uuid(), + } + ) + new_tap_flow = copy.deepcopy(fake_tap_flow) + new_tap_flow['name'] = self._new_name + + self.app.client_manager.network.update_tap_flow = mock.Mock( + return_value=new_tap_flow) + + arg_list = [ + fake_tap_flow['id'], + '--name', self._new_name, + ] + verify_list = [('name', self._new_name)] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + columns, data = self.cmd.take_action(parsed_args) + attrs = {'name': self._new_name} + + mock_update_t_f = self.app.client_manager.network.update_tap_flow + mock_update_t_f.assert_called_once_with(new_tap_flow['id'], **attrs) + self.assertEqual(self.columns, columns) + self.assertItemEqual(_get_data(new_tap_flow), data) diff --git a/neutronclient/tests/unit/osc/v2/taas/test_osc_tap_mirror.py b/neutronclient/tests/unit/osc/v2/taas/test_osc_tap_mirror.py new file mode 100644 index 000000000..88314842b --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/taas/test_osc_tap_mirror.py @@ -0,0 +1,267 @@ +# 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 copy +import operator +from unittest import mock + +from neutronclient.tests.unit.osc.v2 import fakes as test_fakes +from openstack.network.v2 import tap_mirror +from osc_lib import utils as osc_utils +from osc_lib.utils import columns as column_util +from oslo_utils import uuidutils + +from neutronclient.osc.v2.taas import tap_mirror as osc_tap_mirror +from neutronclient.tests.unit.osc.v2.taas import fakes + + +columns_long = tuple(col for col, _, listing_mode in osc_tap_mirror._attr_map + if listing_mode in (column_util.LIST_BOTH, + column_util.LIST_LONG_ONLY)) +headers_long = tuple(head for _, head, listing_mode in + osc_tap_mirror._attr_map if listing_mode in + (column_util.LIST_BOTH, column_util.LIST_LONG_ONLY)) +sorted_attr_map = sorted(osc_tap_mirror._attr_map, key=operator.itemgetter(1)) +sorted_columns = tuple(col for col, _, _ in sorted_attr_map) +sorted_headers = tuple(head for _, head, _ in sorted_attr_map) + + +def _get_data(attrs, columns=sorted_columns): + return osc_utils.get_dict_properties(attrs, columns) + + +class TestCreateTapMirror(test_fakes.TestNeutronClientOSCV2): + + columns = ( + 'directions', + 'id', + 'mirror_type', + 'name', + 'port_id', + 'remote_ip', + ) + + def setUp(self): + super().setUp() + self.cmd = osc_tap_mirror.CreateTapMirror(self.app, self.namespace) + + def test_create_tap_mirror(self): + port_id = uuidutils.generate_uuid() + fake_tap_mirror = fakes.FakeTapMirror.create_tap_mirror( + attrs={'port_id': port_id} + ) + self.app.client_manager.network = mock.Mock() + self.app.client_manager.network.create_tap_mirror = mock.Mock( + return_value=fake_tap_mirror) + self.app.client_manager.network.find_port = mock.Mock( + return_value={'id': port_id}) + self.app.client_manager.network.find_tap_mirror = mock.Mock( + side_effect=lambda _, name_or_id: {'id': name_or_id}) + arg_list = [ + '--name', fake_tap_mirror['name'], + '--port', fake_tap_mirror['port_id'], + '--directions', fake_tap_mirror['directions'], + '--remote-ip', fake_tap_mirror['remote_ip'], + '--mirror-type', fake_tap_mirror['mirror_type'], + ] + + verify_directions = fake_tap_mirror['directions'].split('=') + verify_directions_dict = {verify_directions[0]: verify_directions[1]} + + verify_list = [ + ('name', fake_tap_mirror['name']), + ('port_id', fake_tap_mirror['port_id']), + ('directions', verify_directions_dict), + ('remote_ip', fake_tap_mirror['remote_ip']), + ('mirror_type', fake_tap_mirror['mirror_type']), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + self.app.client_manager.network.find_tap_mirror = mock.Mock( + return_value=fake_tap_mirror) + + columns, data = self.cmd.take_action(parsed_args) + create_tap_m_mock = self.app.client_manager.network.create_tap_mirror + create_tap_m_mock.assert_called_once_with( + **{'name': fake_tap_mirror['name'], + 'port_id': fake_tap_mirror['port_id'], + 'directions': verify_directions_dict, + 'remote_ip': fake_tap_mirror['remote_ip'], + 'mirror_type': fake_tap_mirror['mirror_type']}) + self.assertEqual(self.columns, columns) + fake_data = _get_data( + fake_tap_mirror, + osc_tap_mirror._get_columns(fake_tap_mirror)[1]) + self.assertEqual(fake_data, data) + + +class TestListTapMirror(test_fakes.TestNeutronClientOSCV2): + + def setUp(self): + super().setUp() + self.cmd = osc_tap_mirror.ListTapMirror(self.app, self.namespace) + + def test_list_tap_mirror(self): + """Test List Tap Mirror.""" + fake_tap_mirrors = fakes.FakeTapMirror.create_tap_mirrors( + attrs={'port_id': uuidutils.generate_uuid()}, + count=4) + self.app.client_manager.network = mock.Mock() + self.app.client_manager.network.tap_mirrors = mock.Mock( + return_value=fake_tap_mirrors) + + arg_list = [] + verify_list = [] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + + headers, data = self.cmd.take_action(parsed_args) + + self.app.client_manager.network.tap_mirrors.assert_called_once() + self.assertEqual(headers, list(headers_long)) + self.assertListItemEqual( + list(data), + [_get_data(fake_tap_mirror, columns_long) for fake_tap_mirror + in fake_tap_mirrors] + ) + + +class TestDeleteTapMirror(test_fakes.TestNeutronClientOSCV2): + + def setUp(self): + super().setUp() + self.app.client_manager.network = mock.Mock() + self.app.client_manager.network.find_tap_mirror = mock.Mock( + side_effect=lambda name_or_id, ignore_missing: + tap_mirror.TapMirror(id=name_or_id)) + self.cmd = osc_tap_mirror.DeleteTapMirror(self.app, self.namespace) + + def test_delete_tap_mirror(self): + """Test Delete Tap Mirror.""" + + fake_tap_mirror = fakes.FakeTapMirror.create_tap_mirror( + attrs={'port_id': uuidutils.generate_uuid()} + ) + self.app.client_manager.network.delete_tap_mirror = mock.Mock() + + arg_list = [ + fake_tap_mirror['id'], + ] + verify_list = [ + (osc_tap_mirror.TAP_MIRROR, [fake_tap_mirror['id']]), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + result = self.cmd.take_action(parsed_args) + + mock_delete_tap_m = self.app.client_manager.network.delete_tap_mirror + mock_delete_tap_m.assert_called_once_with(fake_tap_mirror['id']) + self.assertIsNone(result) + + +class TestShowTapMirror(test_fakes.TestNeutronClientOSCV2): + + columns = ( + 'directions', + 'id', + 'mirror_type', + 'name', + 'port_id', + 'remote_ip', + ) + + def setUp(self): + super().setUp() + self.app.client_manager.network = mock.Mock() + self.app.client_manager.network.find_tap_mirror = mock.Mock( + side_effect=lambda name_or_id, ignore_missing: + tap_mirror.TapMirror(id=name_or_id)) + self.cmd = osc_tap_mirror.ShowTapMirror(self.app, self.namespace) + + def test_show_tap_mirror(self): + """Test Show Tap Mirror.""" + + fake_tap_mirror = fakes.FakeTapMirror.create_tap_mirror( + attrs={'port_id': uuidutils.generate_uuid()} + ) + self.app.client_manager.network.get_tap_mirror = mock.Mock( + return_value=fake_tap_mirror) + arg_list = [ + fake_tap_mirror['id'], + ] + verify_list = [ + (osc_tap_mirror.TAP_MIRROR, fake_tap_mirror['id']), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + + headers, data = self.cmd.take_action(parsed_args) + + mock_get_tap_m = self.app.client_manager.network.get_tap_mirror + mock_get_tap_m.assert_called_once_with( + fake_tap_mirror['id']) + self.assertEqual(self.columns, headers) + fake_data = _get_data( + fake_tap_mirror, + osc_tap_mirror._get_columns(fake_tap_mirror)[1]) + self.assertItemEqual(fake_data, data) + + +class TestUpdateTapMirror(test_fakes.TestNeutronClientOSCV2): + + _new_name = 'new_name' + columns = ( + 'directions', + 'id', + 'mirror_type', + 'name', + 'port_id', + 'remote_ip', + ) + + def setUp(self): + super().setUp() + self.cmd = osc_tap_mirror.UpdateTapMirror(self.app, self.namespace) + self.app.client_manager.network = mock.Mock() + self.app.client_manager.network.find_tap_mirror = mock.Mock( + side_effect=lambda name_or_id, ignore_missing: + tap_mirror.TapMirror(id=name_or_id)) + + def test_update_tap_mirror(self): + """Test update Tap Mirror""" + fake_tap_mirror = fakes.FakeTapMirror.create_tap_mirror( + attrs={'port_id': uuidutils.generate_uuid()} + ) + new_tap_mirror = copy.deepcopy(fake_tap_mirror) + new_tap_mirror['name'] = self._new_name + + self.app.client_manager.network.update_tap_mirror = mock.Mock( + return_value=new_tap_mirror) + + arg_list = [ + fake_tap_mirror['id'], + '--name', self._new_name, + ] + verify_list = [('name', self._new_name)] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + columns, data = self.cmd.take_action(parsed_args) + attrs = {'name': self._new_name} + + mock_update_tap_m = self.app.client_manager.network.update_tap_mirror + mock_update_tap_m.assert_called_once_with( + fake_tap_mirror['id'], **attrs) + self.assertEqual(self.columns, columns) + fake_data = _get_data( + new_tap_mirror, + osc_tap_mirror._get_columns(new_tap_mirror)[1]) + self.assertItemEqual(fake_data, data) diff --git a/neutronclient/tests/unit/osc/v2/taas/test_osc_tap_service.py b/neutronclient/tests/unit/osc/v2/taas/test_osc_tap_service.py new file mode 100644 index 000000000..0b753f803 --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/taas/test_osc_tap_service.py @@ -0,0 +1,250 @@ +# All Rights Reserved 2020 +# +# 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 copy +import operator +from unittest import mock + +from neutronclient.tests.unit.osc.v2 import fakes as test_fakes +from openstack.network.v2 import tap_service +from osc_lib import utils as osc_utils +from osc_lib.utils import columns as column_util +from oslo_utils import uuidutils + +from neutronclient.osc.v2.taas import tap_service as osc_tap_service +from neutronclient.tests.unit.osc.v2.taas import fakes + + +columns_long = tuple(col for col, _, listing_mode in osc_tap_service._attr_map + if listing_mode in (column_util.LIST_BOTH, + column_util.LIST_LONG_ONLY)) +headers_long = tuple(head for _, head, listing_mode in + osc_tap_service._attr_map if listing_mode in + (column_util.LIST_BOTH, column_util.LIST_LONG_ONLY)) +sorted_attr_map = sorted(osc_tap_service._attr_map, key=operator.itemgetter(1)) +sorted_columns = tuple(col for col, _, _ in sorted_attr_map) +sorted_headers = tuple(head for _, head, _ in sorted_attr_map) + + +def _get_data(attrs, columns=sorted_columns): + return osc_utils.get_dict_properties(attrs, columns) + + +class TestCreateTapService(test_fakes.TestNeutronClientOSCV2): + + columns = ( + 'id', + 'name', + 'port_id', + 'status', + ) + + def setUp(self): + super().setUp() + self.cmd = osc_tap_service.CreateTapService(self.app, self.namespace) + + def test_create_tap_service(self): + """Test Create Tap Service.""" + port_id = uuidutils.generate_uuid() + fake_tap_service = fakes.FakeTapService.create_tap_service( + attrs={'port_id': port_id} + ) + self.app.client_manager.network = mock.Mock() + self.app.client_manager.network.create_tap_service = mock.Mock( + return_value=fake_tap_service) + self.app.client_manager.network.find_port = mock.Mock( + return_value={'id': port_id}) + self.app.client_manager.network.find_tap_service = mock.Mock( + side_effect=lambda _, name_or_id: {'id': name_or_id}) + arg_list = [ + '--name', fake_tap_service['name'], + '--port', fake_tap_service['port_id'], + ] + + verify_list = [ + ('name', fake_tap_service['name']), + ('port_id', fake_tap_service['port_id']), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + self.app.client_manager.network.find_tap_service = mock.Mock( + return_value=fake_tap_service) + + columns, data = self.cmd.take_action(parsed_args) + create_tap_s_mock = self.app.client_manager.network.create_tap_service + create_tap_s_mock.assert_called_once_with( + **{'name': fake_tap_service['name'], + 'port_id': fake_tap_service['port_id']}) + self.assertEqual(self.columns, columns) + fake_data = _get_data( + fake_tap_service, + osc_tap_service._get_columns(fake_tap_service)[1]) + self.assertEqual(fake_data, data) + + +class TestListTapService(test_fakes.TestNeutronClientOSCV2): + def setUp(self): + super().setUp() + self.cmd = osc_tap_service.ListTapService(self.app, self.namespace) + + def test_list_tap_service(self): + """Test List Tap Service.""" + fake_tap_services = fakes.FakeTapService.create_tap_services( + attrs={'port_id': uuidutils.generate_uuid()}, + count=4) + self.app.client_manager.network = mock.Mock() + self.app.client_manager.network.tap_services = mock.Mock( + return_value=fake_tap_services) + + arg_list = [] + verify_list = [] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + + headers, data = self.cmd.take_action(parsed_args) + + self.app.client_manager.network.tap_services.assert_called_once() + self.assertEqual(headers, list(headers_long)) + self.assertListItemEqual( + list(data), + [_get_data(fake_tap_service, columns_long) for fake_tap_service + in fake_tap_services] + ) + + +class TestDeleteTapService(test_fakes.TestNeutronClientOSCV2): + def setUp(self): + super().setUp() + self.app.client_manager.network = mock.Mock() + self.app.client_manager.network.find_tap_service = mock.Mock( + side_effect=lambda name_or_id, ignore_missing: + tap_service.TapService(id=name_or_id)) + self.cmd = osc_tap_service.DeleteTapService(self.app, self.namespace) + + def test_delete_tap_service(self): + """Test Delete tap service.""" + + fake_tap_service = fakes.FakeTapService.create_tap_service( + attrs={'port_id': uuidutils.generate_uuid()} + ) + self.app.client_manager.network.delete_tap_service = mock.Mock() + + arg_list = [ + fake_tap_service['id'], + ] + verify_list = [ + (osc_tap_service.TAP_SERVICE, [fake_tap_service['id']]), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + result = self.cmd.take_action(parsed_args) + + mock_delete_tap_s = self.app.client_manager.network.delete_tap_service + mock_delete_tap_s.assert_called_once_with(fake_tap_service['id']) + self.assertIsNone(result) + + +class TestShowTapService(test_fakes.TestNeutronClientOSCV2): + columns = ( + 'id', + 'name', + 'port_id', + 'status', + ) + + def setUp(self): + super().setUp() + self.app.client_manager.network = mock.Mock() + self.app.client_manager.network.find_tap_service = mock.Mock( + side_effect=lambda name_or_id, ignore_missing: + tap_service.TapService(id=name_or_id)) + self.cmd = osc_tap_service.ShowTapService(self.app, self.namespace) + + def test_show_tap_service(self): + """Test Show tap service.""" + + fake_tap_service = fakes.FakeTapService.create_tap_service( + attrs={'port_id': uuidutils.generate_uuid()} + ) + self.app.client_manager.network.get_tap_service = mock.Mock( + return_value=fake_tap_service) + arg_list = [ + fake_tap_service['id'], + ] + verify_list = [ + (osc_tap_service.TAP_SERVICE, fake_tap_service['id']), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + + headers, data = self.cmd.take_action(parsed_args) + + mock_get_tap_s = self.app.client_manager.network.get_tap_service + mock_get_tap_s.assert_called_once_with( + fake_tap_service['id']) + self.assertEqual(self.columns, headers) + fake_data = _get_data( + fake_tap_service, + osc_tap_service._get_columns(fake_tap_service)[1]) + self.assertItemEqual(fake_data, data) + + +class TestUpdateTapService(test_fakes.TestNeutronClientOSCV2): + + _new_name = 'new_name' + + columns = ( + 'id', + 'name', + 'port_id', + 'status', + ) + + def setUp(self): + super().setUp() + self.cmd = osc_tap_service.UpdateTapService(self.app, self.namespace) + self.app.client_manager.network = mock.Mock() + self.app.client_manager.network.find_tap_service = mock.Mock( + side_effect=lambda name_or_id, ignore_missing: + tap_service.TapService(id=name_or_id)) + + def test_update_tap_service(self): + """Test update tap service""" + fake_tap_service = fakes.FakeTapService.create_tap_service( + attrs={'port_id': uuidutils.generate_uuid()} + ) + new_tap_service = copy.deepcopy(fake_tap_service) + new_tap_service['name'] = self._new_name + + self.app.client_manager.network.update_tap_service = mock.Mock( + return_value=new_tap_service) + + arg_list = [ + fake_tap_service['id'], + '--name', self._new_name, + ] + verify_list = [('name', self._new_name)] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + columns, data = self.cmd.take_action(parsed_args) + attrs = {'name': self._new_name} + + mock_update_tap_s = self.app.client_manager.network.update_tap_service + mock_update_tap_s.assert_called_once_with( + fake_tap_service['id'], **attrs) + self.assertEqual(self.columns, columns) + fake_data = _get_data( + new_tap_service, + osc_tap_service._get_columns(new_tap_service)[1]) + self.assertItemEqual(fake_data, data) diff --git a/setup.cfg b/setup.cfg index 3005e895b..77c3e069a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -161,3 +161,19 @@ openstack.neutronclient.v2 = vpn_ipsec_site_connection_show = neutronclient.osc.v2.vpnaas.ipsec_site_connection:ShowIPsecSiteConnection network_onboard_subnets = neutronclient.osc.v2.subnet_onboard.subnet_onboard:NetworkOnboardSubnets + + tap_flow_create = neutronclient.osc.v2.taas.tap_flow:CreateTapFlow + tap_flow_delete = neutronclient.osc.v2.taas.tap_flow:DeleteTapFlow + tap_flow_list = neutronclient.osc.v2.taas.tap_flow:ListTapFlow + tap_flow_show = neutronclient.osc.v2.taas.tap_flow:ShowTapFlow + tap_flow_update = neutronclient.osc.v2.taas.tap_flow:UpdateTapFlow + tap_mirror_create = neutronclient.osc.v2.taas.tap_mirror:CreateTapMirror + tap_mirror_delete = neutronclient.osc.v2.taas.tap_mirror:DeleteTapMirror + tap_mirror_list = neutronclient.osc.v2.taas.tap_mirror:ListTapMirror + tap_mirror_show = neutronclient.osc.v2.taas.tap_mirror:ShowTapMirror + tap_mirror_update = neutronclient.osc.v2.taas.tap_mirror:UpdateTapMirror + tap_service_create = neutronclient.osc.v2.taas.tap_service:CreateTapService + tap_service_delete = neutronclient.osc.v2.taas.tap_service:DeleteTapService + tap_service_list = neutronclient.osc.v2.taas.tap_service:ListTapService + tap_service_show = neutronclient.osc.v2.taas.tap_service:ShowTapService + tap_service_update = neutronclient.osc.v2.taas.tap_service:UpdateTapService From 01d0553ca4895742533234322252e59df9f4512d Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Fri, 31 Oct 2025 12:06:40 +0000 Subject: [PATCH 837/845] reno: Update master for unmaintained/2024.1 Update the 2024.1 release notes configuration to build from unmaintained/2024.1. Change-Id: Ic2592d22b36b22f3cf48a21d81bfb0b41b31ae86 Signed-off-by: OpenStack Release Bot Generated-By: openstack/project-config:roles/copy-release-tools-scripts/files/release-tools/change_reno_branch_to_unmaintained.sh --- releasenotes/source/2024.1.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/source/2024.1.rst b/releasenotes/source/2024.1.rst index 4977a4f1a..6896656be 100644 --- a/releasenotes/source/2024.1.rst +++ b/releasenotes/source/2024.1.rst @@ -3,4 +3,4 @@ =========================== .. release-notes:: - :branch: stable/2024.1 + :branch: unmaintained/2024.1 From a991ac87c7fe4d7fd73a3d0370f346139c539086 Mon Sep 17 00:00:00 2001 From: Miro Tomaska Date: Thu, 6 Nov 2025 21:39:01 +0000 Subject: [PATCH 838/845] Revert "Add Tap-as-a-Service client code" This reverts commit 01ffc4684a4a8b443bd2c5d66f3335af7fdaddc4. Reason for revert: In the last PTG we decided that the best location for the stadium projects osc client code would be the openstackclient repo. A patch was proposed for tapaas, see depends-on link below. Other projects will follow Change-Id: Iaad2080f0ef552a0c0a00635bea48130cfc327a4 Depends-On: https://review.opendev.org/c/openstack/python-openstackclient/+/963445 Signed-off-by: Miro Tomaska --- neutronclient/osc/v2/taas/__init__.py | 0 neutronclient/osc/v2/taas/tap_flow.py | 225 -------------- neutronclient/osc/v2/taas/tap_mirror.py | 222 -------------- neutronclient/osc/v2/taas/tap_service.py | 211 ------------- .../tests/unit/osc/v2/taas/__init__.py | 0 neutronclient/tests/unit/osc/v2/taas/fakes.py | 122 -------- .../unit/osc/v2/taas/test_osc_tap_flow.py | 283 ------------------ .../unit/osc/v2/taas/test_osc_tap_mirror.py | 267 ----------------- .../unit/osc/v2/taas/test_osc_tap_service.py | 250 ---------------- setup.cfg | 16 - 10 files changed, 1596 deletions(-) delete mode 100644 neutronclient/osc/v2/taas/__init__.py delete mode 100644 neutronclient/osc/v2/taas/tap_flow.py delete mode 100644 neutronclient/osc/v2/taas/tap_mirror.py delete mode 100644 neutronclient/osc/v2/taas/tap_service.py delete mode 100644 neutronclient/tests/unit/osc/v2/taas/__init__.py delete mode 100644 neutronclient/tests/unit/osc/v2/taas/fakes.py delete mode 100644 neutronclient/tests/unit/osc/v2/taas/test_osc_tap_flow.py delete mode 100644 neutronclient/tests/unit/osc/v2/taas/test_osc_tap_mirror.py delete mode 100644 neutronclient/tests/unit/osc/v2/taas/test_osc_tap_service.py diff --git a/neutronclient/osc/v2/taas/__init__.py b/neutronclient/osc/v2/taas/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutronclient/osc/v2/taas/tap_flow.py b/neutronclient/osc/v2/taas/tap_flow.py deleted file mode 100644 index 428d059e0..000000000 --- a/neutronclient/osc/v2/taas/tap_flow.py +++ /dev/null @@ -1,225 +0,0 @@ -# All Rights Reserved 2020 -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import logging - -from osc_lib.cli import format_columns -from osc_lib.cli import identity as identity_utils -from osc_lib.command import command -from osc_lib import exceptions -from osc_lib import utils as osc_utils -from osc_lib.utils import columns as column_util - -from neutronclient._i18n import _ -from neutronclient.osc.v2.taas import tap_service - -LOG = logging.getLogger(__name__) - -TAP_FLOW = 'tap_flow' -TAP_FLOWS = '%ss' % TAP_FLOW - -_attr_map = ( - ('id', 'ID', column_util.LIST_BOTH), - ('tenant_id', 'Tenant', column_util.LIST_LONG_ONLY), - ('name', 'Name', column_util.LIST_BOTH), - ('status', 'Status', column_util.LIST_BOTH), - ('source_port', 'source_port', column_util.LIST_BOTH), - ('tap_service_id', 'tap_service_id', column_util.LIST_BOTH), - ('direction', 'Direction', column_util.LIST_BOTH), -) - -_formatters = { - 'vlan_filter': format_columns.ListColumn, -} - - -def _add_updatable_args(parser): - parser.add_argument( - '--name', - help=_('Name of this Tap service.')) - parser.add_argument( - '--description', - help=_('Description for this Tap service.')) - - -class CreateTapFlow(command.ShowOne): - _description = _("Create a tap flow") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - identity_utils.add_project_owner_option_to_parser(parser) - _add_updatable_args(parser) - parser.add_argument( - '--port', - required=True, - metavar="SOURCE_PORT", - help=_('Source port to which the Tap Flow is connected.')) - parser.add_argument( - '--tap-service', - required=True, - metavar="TAP_SERVICE", - help=_('Tap Service to which the Tap Flow belongs.')) - parser.add_argument( - '--direction', - required=True, - metavar="DIRECTION", - choices=['IN', 'OUT', 'BOTH'], - type=lambda s: s.upper(), - help=_('Direction of the Tap flow. Possible options are: ' - 'IN, OUT, BOTH')) - parser.add_argument( - '--vlan-filter', - required=False, - metavar="VLAN_FILTER", - help=_('VLAN Ids to be mirrored in the form of range string.')) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - attrs = {} - if parsed_args.name is not None: - attrs['name'] = str(parsed_args.name) - if parsed_args.description is not None: - attrs['description'] = str(parsed_args.description) - if parsed_args.port is not None: - source_port = client.find_port(parsed_args.port)['id'] - attrs['source_port'] = source_port - if parsed_args.tap_service is not None: - tap_service_id = client.find_tap_service( - parsed_args.tap_service)['id'] - attrs['tap_service_id'] = tap_service_id - if parsed_args.direction is not None: - attrs['direction'] = parsed_args.direction - if parsed_args.vlan_filter is not None: - attrs['vlan_filter'] = parsed_args.vlan_filter - if 'project' in parsed_args and parsed_args.project is not None: - project_id = identity_utils.find_project( - self.app.client_manager.identity, - parsed_args.project, - parsed_args.project_domain, - ).id - attrs['tenant_id'] = project_id - obj = client.create_tap_flow(**attrs) - display_columns, columns = tap_service._get_columns(obj) - data = osc_utils.get_dict_properties(obj, columns) - return display_columns, data - - -class ListTapFlow(command.Lister): - _description = _("List tap flows that belong to a given tenant") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - identity_utils.add_project_owner_option_to_parser(parser) - - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - params = {} - if parsed_args.project is not None: - project_id = identity_utils.find_project( - self.app.client_manager.identity, - parsed_args.project, - parsed_args.project_domain, - ).id - params['tenant_id'] = project_id - objs = client.tap_flows(retrieve_all=True, params=params) - headers, columns = column_util.get_column_definitions( - _attr_map, long_listing=True) - return (headers, (osc_utils.get_dict_properties( - s, columns, formatters=_formatters) for s in objs)) - - -class ShowTapFlow(command.ShowOne): - _description = _("Show information of a given tap flow") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - TAP_FLOW, - metavar="<%s>" % TAP_FLOW, - help=_("ID or name of tap flow to look up."), - ) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - id = client.find_tap_flow(parsed_args.tap_flow, - ignore_missing=False).id - obj = client.get_tap_flow(id) - display_columns, columns = tap_service._get_columns(obj) - data = osc_utils.get_dict_properties(obj, columns) - return display_columns, data - - -class DeleteTapFlow(command.Command): - _description = _("Delete a tap flow") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - TAP_FLOW, - metavar="<%s>" % TAP_FLOW, - nargs="+", - help=_("ID(s) or name(s) of tap flow to delete."), - ) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - fails = 0 - for id_or_name in parsed_args.tap_flow: - try: - id = client.find_tap_flow(id_or_name, - ignore_missing=False).id - client.delete_tap_flow(id) - LOG.warning("Tap flow %(id)s deleted", {'id': id}) - except Exception as e: - fails += 1 - LOG.error("Failed to delete tap flow with name or ID " - "'%(id_or_name)s': %(e)s", - {'id_or_name': id_or_name, 'e': e}) - if fails > 0: - msg = (_("Failed to delete %(fails)s of %(total)s tap flow.") % - {'fails': fails, 'total': len(parsed_args.tap_flow)}) - raise exceptions.CommandError(msg) - - -class UpdateTapFlow(command.ShowOne): - _description = _("Update a tap flow.") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - TAP_FLOW, - metavar="<%s>" % TAP_FLOW, - help=_("ID or name of tap flow to update."), - ) - _add_updatable_args(parser) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - original_t_f = client.find_tap_flow(parsed_args.tap_flow, - ignore_missing=False).id - attrs = {} - if parsed_args.name is not None: - attrs['name'] = str(parsed_args.name) - if parsed_args.description is not None: - attrs['description'] = str(parsed_args.description) - obj = client.update_tap_flow(original_t_f, **attrs) - columns, display_columns = column_util.get_columns(obj, _attr_map) - data = osc_utils.get_dict_properties(obj, columns) - return display_columns, data diff --git a/neutronclient/osc/v2/taas/tap_mirror.py b/neutronclient/osc/v2/taas/tap_mirror.py deleted file mode 100644 index af6723c09..000000000 --- a/neutronclient/osc/v2/taas/tap_mirror.py +++ /dev/null @@ -1,222 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import logging - -from osc_lib.cli import identity as identity_utils -from osc_lib.command import command -from osc_lib import exceptions -from osc_lib import utils as osc_utils -from osc_lib.utils import columns as column_util - -from openstackclient.network.v2 import port as osc_port - -from neutronclient._i18n import _ -from neutronclient.osc.v2.taas import tap_service - - -LOG = logging.getLogger(__name__) - -TAP_MIRROR = 'tap_mirror' -TAP_MIRRORS = '%ss' % TAP_MIRROR - -_attr_map = ( - ('id', 'ID', column_util.LIST_BOTH), - ('tenant_id', 'Tenant', column_util.LIST_LONG_ONLY), - ('name', 'Name', column_util.LIST_BOTH), - ('port_id', 'Port', column_util.LIST_BOTH), - ('directions', 'Directions', column_util.LIST_LONG_ONLY), - ('remote_ip', 'Remote IP', column_util.LIST_BOTH), - ('mirror_type', 'Mirror Type', column_util.LIST_LONG_ONLY), -) - - -def _get_columns(item): - column_map = {} - hidden_columns = ['location', 'tenant_id'] - return osc_utils.get_osc_show_columns_for_sdk_resource( - item, - column_map, - hidden_columns - ) - - -class CreateTapMirror(command.ShowOne): - _description = _("Create a Tap Mirror") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - identity_utils.add_project_owner_option_to_parser(parser) - tap_service._add_updatable_args(parser) - parser.add_argument( - '--port', - dest='port_id', - required=True, - metavar="PORT", - help=_('Port to which the Tap Mirror is connected.')) - parser.add_argument( - '--directions', - dest='directions', - action=osc_port.JSONKeyValueAction, - required=True, - help=_('A dictionary of direction and tunnel_id. Direction can ' - 'be IN and OUT.')) - parser.add_argument( - '--remote-ip', - dest='remote_ip', - required=True, - help=_('The remote IP of the Tap Mirror, this will be the ' - 'remote end of the GRE or ERSPAN v1 tunnel')) - parser.add_argument( - '--mirror-type', - dest='mirror_type', - required=True, - help=_('The type of the mirroring, it can be gre or erspanv1')) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - attrs = {} - if parsed_args.name is not None: - attrs['name'] = str(parsed_args.name) - if parsed_args.description is not None: - attrs['description'] = str(parsed_args.description) - if parsed_args.port_id is not None: - port_id = client.find_port(parsed_args.port_id)['id'] - attrs['port_id'] = port_id - if parsed_args.directions is not None: - attrs['directions'] = parsed_args.directions - if parsed_args.remote_ip is not None: - attrs['remote_ip'] = parsed_args.remote_ip - if parsed_args.mirror_type is not None: - attrs['mirror_type'] = parsed_args.mirror_type - if 'project' in parsed_args and parsed_args.project is not None: - project_id = identity_utils.find_project( - self.app.client_manager.identity, - parsed_args.project, - parsed_args.project_domain, - ).id - attrs['tenant_id'] = project_id - obj = client.create_tap_mirror(**attrs) - display_columns, columns = tap_service._get_columns(obj) - data = osc_utils.get_dict_properties(obj, columns) - return display_columns, data - - -class ListTapMirror(command.Lister): - _description = _("List Tap Mirrors that belong to a given tenant") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - identity_utils.add_project_owner_option_to_parser(parser) - - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - params = {} - if parsed_args.project is not None: - project_id = identity_utils.find_project( - self.app.client_manager.identity, - parsed_args.project, - parsed_args.project_domain, - ).id - params['tenant_id'] = project_id - objs = client.tap_mirrors(retrieve_all=True, params=params) - headers, columns = column_util.get_column_definitions( - _attr_map, long_listing=True) - return (headers, (osc_utils.get_dict_properties( - s, columns) for s in objs)) - - -class ShowTapMirror(command.ShowOne): - _description = _("Show information of a given Tap Mirror") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - TAP_MIRROR, - metavar="<%s>" % TAP_MIRROR, - help=_("ID or name of Tap Mirror to look up."), - ) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - id = client.find_tap_mirror(parsed_args.tap_mirror, - ignore_missing=False).id - obj = client.get_tap_mirror(id) - display_columns, columns = tap_service._get_columns(obj) - data = osc_utils.get_dict_properties(obj, columns) - return display_columns, data - - -class DeleteTapMirror(command.Command): - _description = _("Delete a Tap Mirror") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - TAP_MIRROR, - metavar="<%s>" % TAP_MIRROR, - nargs="+", - help=_("ID(s) or name(s) of the Tap Mirror to delete."), - ) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - fails = 0 - for id_or_name in parsed_args.tap_mirror: - try: - id = client.find_tap_mirror(id_or_name, - ignore_missing=False).id - - client.delete_tap_mirror(id) - LOG.warning("Tap Mirror %(id)s deleted", {'id': id}) - except Exception as e: - fails += 1 - LOG.error("Failed to delete Tap Mirror with name or ID " - "'%(id_or_name)s': %(e)s", - {'id_or_name': id_or_name, 'e': e}) - if fails > 0: - msg = (_("Failed to delete %(fails)s of %(total)s Tap Mirror.") % - {'fails': fails, 'total': len(parsed_args.tap_mirror)}) - raise exceptions.CommandError(msg) - - -class UpdateTapMirror(command.ShowOne): - _description = _("Update a Tap Mirror.") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - TAP_MIRROR, - metavar="<%s>" % TAP_MIRROR, - help=_("ID or name of the Tap Mirror to update."), - ) - tap_service._add_updatable_args(parser) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - original_t_s = client.find_tap_mirror(parsed_args.tap_mirror, - ignore_missing=False).id - attrs = {} - if parsed_args.name is not None: - attrs['name'] = str(parsed_args.name) - if parsed_args.description is not None: - attrs['description'] = str(parsed_args.description) - obj = client.update_tap_mirror(original_t_s, **attrs) - display_columns, columns = tap_service._get_columns(obj) - data = osc_utils.get_dict_properties(obj, columns) - return display_columns, data diff --git a/neutronclient/osc/v2/taas/tap_service.py b/neutronclient/osc/v2/taas/tap_service.py deleted file mode 100644 index cd53b6825..000000000 --- a/neutronclient/osc/v2/taas/tap_service.py +++ /dev/null @@ -1,211 +0,0 @@ -# All Rights Reserved 2020 -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import logging - -from osc_lib.cli import identity as identity_utils -from osc_lib.command import command -from osc_lib import exceptions -from osc_lib import utils as osc_utils -from osc_lib.utils import columns as column_util - -from neutronclient._i18n import _ - - -LOG = logging.getLogger(__name__) - -TAP_SERVICE = 'tap_service' -TAP_SERVICES = '%ss' % TAP_SERVICE - -_attr_map = ( - ('id', 'ID', column_util.LIST_BOTH), - ('tenant_id', 'Tenant', column_util.LIST_LONG_ONLY), - ('name', 'Name', column_util.LIST_BOTH), - ('port_id', 'Port', column_util.LIST_BOTH), - ('status', 'Status', column_util.LIST_BOTH), -) - - -def _add_updatable_args(parser): - parser.add_argument( - '--name', - help=_('Name of this Tap service.')) - parser.add_argument( - '--description', - help=_('Description for this Tap service.')) - - -def _updatable_args2body(parsed_args, body): - for attribute in ['name', 'description']: - if (hasattr(parsed_args, attribute) and - getattr(parsed_args, attribute) is not None): - body[attribute] = getattr(parsed_args, attribute) - - -def _get_columns(item): - column_map = {} - hidden_columns = ['location', 'tenant_id'] - return osc_utils.get_osc_show_columns_for_sdk_resource( - item, - column_map, - hidden_columns - ) - - -class CreateTapService(command.ShowOne): - _description = _("Create a tap service") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - identity_utils.add_project_owner_option_to_parser(parser) - _add_updatable_args(parser) - parser.add_argument( - '--port', - dest='port_id', - required=True, - metavar="PORT", - help=_('Port to which the Tap service is connected.')) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - attrs = {} - if parsed_args.name is not None: - attrs['name'] = str(parsed_args.name) - if parsed_args.description is not None: - attrs['description'] = str(parsed_args.description) - if parsed_args.port_id is not None: - port_id = client.find_port(parsed_args.port_id)['id'] - attrs['port_id'] = port_id - if 'project' in parsed_args and parsed_args.project is not None: - project_id = identity_utils.find_project( - self.app.client_manager.identity, - parsed_args.project, - parsed_args.project_domain, - ).id - attrs['tenant_id'] = project_id - obj = client.create_tap_service(**attrs) - display_columns, columns = _get_columns(obj) - data = osc_utils.get_dict_properties(obj, columns) - return display_columns, data - - -class ListTapService(command.Lister): - _description = _("List tap services that belong to a given tenant") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - identity_utils.add_project_owner_option_to_parser(parser) - - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - params = {} - if parsed_args.project is not None: - project_id = identity_utils.find_project( - self.app.client_manager.identity, - parsed_args.project, - parsed_args.project_domain, - ).id - params['tenant_id'] = project_id - objs = client.tap_services(retrieve_all=True, params=params) - headers, columns = column_util.get_column_definitions( - _attr_map, long_listing=True) - return (headers, (osc_utils.get_dict_properties( - s, columns) for s in objs)) - - -class ShowTapService(command.ShowOne): - _description = _("Show information of a given tap service") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - TAP_SERVICE, - metavar="<%s>" % TAP_SERVICE, - help=_("ID or name of tap service to look up."), - ) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - id = client.find_tap_service(parsed_args.tap_service, - ignore_missing=False).id - obj = client.get_tap_service(id) - display_columns, columns = _get_columns(obj) - data = osc_utils.get_dict_properties(obj, columns) - return display_columns, data - - -class DeleteTapService(command.Command): - _description = _("Delete a tap service") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - TAP_SERVICE, - metavar="<%s>" % TAP_SERVICE, - nargs="+", - help=_("ID(s) or name(s) of tap service to delete."), - ) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - fails = 0 - for id_or_name in parsed_args.tap_service: - try: - id = client.find_tap_service(id_or_name, - ignore_missing=False).id - - client.delete_tap_service(id) - LOG.warning("Tap service %(id)s deleted", {'id': id}) - except Exception as e: - fails += 1 - LOG.error("Failed to delete tap service with name or ID " - "'%(id_or_name)s': %(e)s", - {'id_or_name': id_or_name, 'e': e}) - if fails > 0: - msg = (_("Failed to delete %(fails)s of %(total)s tap service.") % - {'fails': fails, 'total': len(parsed_args.tap_service)}) - raise exceptions.CommandError(msg) - - -class UpdateTapService(command.ShowOne): - _description = _("Update a tap service.") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - TAP_SERVICE, - metavar="<%s>" % TAP_SERVICE, - help=_("ID or name of tap service to update."), - ) - _add_updatable_args(parser) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - original_t_s = client.find_tap_service(parsed_args.tap_service, - ignore_missing=False).id - attrs = {} - if parsed_args.name is not None: - attrs['name'] = str(parsed_args.name) - if parsed_args.description is not None: - attrs['description'] = str(parsed_args.description) - obj = client.update_tap_service(original_t_s, **attrs) - display_columns, columns = _get_columns(obj) - data = osc_utils.get_dict_properties(obj, columns) - return display_columns, data diff --git a/neutronclient/tests/unit/osc/v2/taas/__init__.py b/neutronclient/tests/unit/osc/v2/taas/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutronclient/tests/unit/osc/v2/taas/fakes.py b/neutronclient/tests/unit/osc/v2/taas/fakes.py deleted file mode 100644 index ee32cb1ba..000000000 --- a/neutronclient/tests/unit/osc/v2/taas/fakes.py +++ /dev/null @@ -1,122 +0,0 @@ -# All Rights Reserved 2020 -# -# 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 copy - -from oslo_utils import uuidutils - - -class FakeTapService: - - @staticmethod - def create_tap_service(attrs=None): - """Create a fake tap service.""" - attrs = attrs or {} - tap_service_attrs = { - 'id': uuidutils.generate_uuid(), - 'tenant_id': uuidutils.generate_uuid(), - 'name': 'test_tap_service' + uuidutils.generate_uuid(), - 'status': 'ACTIVE', - } - tap_service_attrs.update(attrs) - return copy.deepcopy(tap_service_attrs) - - @staticmethod - def create_tap_services(attrs=None, count=1): - """Create multiple fake tap services.""" - - tap_services = [] - for i in range(0, count): - if attrs is None: - attrs = {'id': 'fake_id%d' % i} - elif getattr(attrs, 'id', None) is None: - attrs['id'] = 'fake_id%d' % i - tap_services.append(FakeTapService.create_tap_service( - attrs=attrs)) - - return tap_services - - -class FakeTapFlow: - - @staticmethod - def create_tap_flow(attrs=None): - """Create a fake tap service.""" - attrs = attrs or {} - tap_flow_attrs = { - 'id': uuidutils.generate_uuid(), - 'tenant_id': uuidutils.generate_uuid(), - 'name': 'test_tap_flow' + uuidutils.generate_uuid(), - 'status': 'ACTIVE', - 'direction': 'BOTH', - } - tap_flow_attrs.update(attrs) - return copy.deepcopy(tap_flow_attrs) - - @staticmethod - def create_tap_flows(attrs=None, count=1): - """Create multiple fake tap flows.""" - - tap_flows = [] - for i in range(0, count): - if attrs is None: - attrs = { - 'id': 'fake_id%d' % i, - 'source_port': uuidutils.generate_uuid(), - 'tap_service_id': uuidutils.generate_uuid() - } - elif getattr(attrs, 'id', None) is None: - attrs['id'] = 'fake_id%d' % i - tap_flows.append(FakeTapFlow.create_tap_flow(attrs=attrs)) - - return tap_flows - - -class FakeTapMirror(object): - - @staticmethod - def create_tap_mirror(attrs=None): - """Create a fake tap mirror.""" - attrs = attrs or {} - tap_mirror_attrs = { - 'id': uuidutils.generate_uuid(), - 'tenant_id': uuidutils.generate_uuid(), - 'name': 'test_tap_mirror' + uuidutils.generate_uuid(), - 'port_id': uuidutils.generate_uuid(), - 'directions': 'IN=99', - 'remote_ip': '192.10.10.2', - 'mirror_type': 'gre', - } - tap_mirror_attrs.update(attrs) - return copy.deepcopy(tap_mirror_attrs) - - @staticmethod - def create_tap_mirrors(attrs=None, count=1): - """Create multiple fake tap mirrors.""" - - tap_mirrors = [] - for i in range(0, count): - if attrs is None: - attrs = { - 'id': 'fake_id%d' % i, - 'port_id': uuidutils.generate_uuid(), - 'name': 'test_tap_mirror_%d' % i, - 'directions': 'IN=%d' % 99 + i, - 'remote_ip': '192.10.10.%d' % (i + 3), - } - elif getattr(attrs, 'id', None) is None: - attrs['id'] = 'fake_id%d' % i - tap_mirrors.append(FakeTapMirror.create_tap_mirror(attrs=attrs)) - - return tap_mirrors diff --git a/neutronclient/tests/unit/osc/v2/taas/test_osc_tap_flow.py b/neutronclient/tests/unit/osc/v2/taas/test_osc_tap_flow.py deleted file mode 100644 index a92e443f3..000000000 --- a/neutronclient/tests/unit/osc/v2/taas/test_osc_tap_flow.py +++ /dev/null @@ -1,283 +0,0 @@ -# All Rights Reserved 2020 -# -# 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 copy -import operator -from unittest import mock - -from neutronclient.tests.unit.osc.v2 import fakes as test_fakes -from openstack.network.v2 import tap_flow as _tap_flow -from osc_lib import utils as osc_utils -from osc_lib.utils import columns as column_util -from oslo_utils import uuidutils - -from neutronclient.osc.v2.taas import tap_flow as osc_tap_flow -from neutronclient.osc.v2.taas import tap_service as osc_tap_service -from neutronclient.tests.unit.osc.v2.taas import fakes - - -columns_long = tuple(col for col, _, listing_mode in osc_tap_flow._attr_map - if listing_mode in (column_util.LIST_BOTH, - column_util.LIST_LONG_ONLY)) -headers_long = tuple(head for _, head, listing_mode in - osc_tap_flow._attr_map if listing_mode in - (column_util.LIST_BOTH, column_util.LIST_LONG_ONLY)) -sorted_attr_map = sorted(osc_tap_flow._attr_map, key=operator.itemgetter(1)) -sorted_columns = tuple(col for col, _, _ in sorted_attr_map) -sorted_headers = tuple(head for _, head, _ in sorted_attr_map) - - -def _get_data(attrs, columns=sorted_columns): - return osc_utils.get_dict_properties(attrs, columns) - - -class TestCreateTapFlow(test_fakes.TestNeutronClientOSCV2): - - columns = ( - 'direction', - 'id', - 'name', - 'source_port', - 'status', - 'tap_service_id', - ) - - def setUp(self): - super().setUp() - self.cmd = osc_tap_flow.CreateTapFlow(self.app, self.namespace) - - def test_create_tap_flow(self): - """Test Create Tap Flow.""" - port_id = uuidutils.generate_uuid() - fake_tap_service = fakes.FakeTapService.create_tap_service( - attrs={'port_id': port_id} - ) - port_id = uuidutils.generate_uuid() - fake_tap_flow = fakes.FakeTapFlow.create_tap_flow( - attrs={ - 'source_port': port_id, - 'tap_service_id': fake_tap_service['id'] - } - ) - self.app.client_manager.network = mock.Mock() - self.app.client_manager.network.create_tap_flow = mock.Mock( - return_value=fake_tap_flow) - self.app.client_manager.network.find_port = mock.Mock( - return_value={'id': port_id}) - self.app.client_manager.network.find_tap_service = mock.Mock( - return_value=fake_tap_service) - arg_list = [ - '--name', fake_tap_flow['name'], - '--port', fake_tap_flow['source_port'], - '--tap-service', fake_tap_flow['tap_service_id'], - '--direction', fake_tap_flow['direction'], - ] - - verify_list = [ - ('name', fake_tap_flow['name']), - ('port', fake_tap_flow['source_port']), - ('tap_service', fake_tap_flow['tap_service_id']), - ] - - parsed_args = self.check_parser(self.cmd, arg_list, verify_list) - with mock.patch.object( - self.app.client_manager.network, - '_find') as nc_find: - nc_find.side_effect = [ - {'id': fake_tap_flow['tap_service_id']} - ] - - columns, data = self.cmd.take_action(parsed_args) - mock_create_t_f = self.app.client_manager.network.create_tap_flow - mock_create_t_f.assert_called_once_with( - **{ - 'name': fake_tap_flow['name'], - 'source_port': fake_tap_flow['source_port'], - 'tap_service_id': fake_tap_flow['tap_service_id'], - 'direction': fake_tap_flow['direction'] - } - ) - self.assertEqual(self.columns, columns) - fake_data = _get_data( - fake_tap_flow, - osc_tap_service._get_columns(fake_tap_flow)[1]) - self.assertItemEqual(fake_data, data) - - -class TestListTapFlow(test_fakes.TestNeutronClientOSCV2): - def setUp(self): - super().setUp() - self.cmd = osc_tap_flow.ListTapFlow(self.app, self.namespace) - - def test_list_tap_flows(self): - """Test List Tap Flow.""" - fake_tap_flows = fakes.FakeTapFlow.create_tap_flows( - attrs={ - 'source_port': uuidutils.generate_uuid(), - 'tap_service_id': uuidutils.generate_uuid(), - }, - count=2) - self.app.client_manager.network = mock.Mock() - self.app.client_manager.network.tap_flows = mock.Mock( - return_value=fake_tap_flows) - arg_list = [] - verify_list = [] - - parsed_args = self.check_parser(self.cmd, arg_list, verify_list) - - headers, data = self.cmd.take_action(parsed_args) - - self.app.client_manager.network.tap_flows.assert_called_once() - self.assertEqual(headers, list(headers_long)) - self.assertListItemEqual( - list(data), - [_get_data(fake_tap_flow, columns_long) for fake_tap_flow - in fake_tap_flows] - ) - - -class TestDeleteTapFlow(test_fakes.TestNeutronClientOSCV2): - def setUp(self): - super().setUp() - self.app.client_manager.network = mock.Mock() - self.app.client_manager.network.find_tap_flow = mock.Mock( - side_effect=lambda name_or_id, ignore_missing: - _tap_flow.TapFlow(id=name_or_id)) - self.cmd = osc_tap_flow.DeleteTapFlow(self.app, self.namespace) - - def test_delete_tap_flow(self): - """Test Delete tap flow.""" - - fake_tap_flow = fakes.FakeTapFlow.create_tap_flow( - attrs={ - 'source_port': uuidutils.generate_uuid(), - 'tap_service_id': uuidutils.generate_uuid(), - } - ) - self.app.client_manager.network.delete_tap_flow = mock.Mock() - - arg_list = [ - fake_tap_flow['id'], - ] - verify_list = [ - (osc_tap_flow.TAP_FLOW, [fake_tap_flow['id']]), - ] - - parsed_args = self.check_parser(self.cmd, arg_list, verify_list) - - result = self.cmd.take_action(parsed_args) - - mock_delete_tap_flow = self.app.client_manager.network.delete_tap_flow - mock_delete_tap_flow.assert_called_once_with(fake_tap_flow['id']) - self.assertIsNone(result) - - -class TestShowTapFlow(test_fakes.TestNeutronClientOSCV2): - columns = ( - 'direction', - 'id', - 'name', - 'source_port', - 'status', - 'tap_service_id' - ) - - def setUp(self): - super().setUp() - self.app.client_manager.network = mock.Mock() - self.app.client_manager.network.find_tap_flow = mock.Mock( - side_effect=lambda name_or_id, ignore_missing: - _tap_flow.TapFlow(id=name_or_id)) - self.cmd = osc_tap_flow.ShowTapFlow(self.app, self.namespace) - - def test_show_tap_flow(self): - """Test Show tap flow.""" - fake_tap_flow = fakes.FakeTapFlow.create_tap_flow( - attrs={ - 'source_port': uuidutils.generate_uuid(), - 'tap_service_id': uuidutils.generate_uuid(), - } - ) - self.app.client_manager.network.get_tap_flow = mock.Mock( - return_value=fake_tap_flow) - arg_list = [ - fake_tap_flow['id'], - ] - verify_list = [ - (osc_tap_flow.TAP_FLOW, fake_tap_flow['id']), - ] - - parsed_args = self.check_parser(self.cmd, arg_list, verify_list) - - headers, data = self.cmd.take_action(parsed_args) - - self.app.client_manager.network.get_tap_flow.assert_called_once_with( - fake_tap_flow['id']) - self.assertEqual(self.columns, headers) - fake_data = _get_data( - fake_tap_flow, - osc_tap_service._get_columns(fake_tap_flow)[1]) - self.assertItemEqual(fake_data, data) - - -class TestUpdateTapFlow(test_fakes.TestNeutronClientOSCV2): - - _new_name = 'new_name' - - columns = ( - 'Direction', - 'ID', - 'Name', - 'Status', - 'Tenant', - 'source_port', - 'tap_service_id', - ) - - def setUp(self): - super().setUp() - self.cmd = osc_tap_flow.UpdateTapFlow(self.app, self.namespace) - self.app.client_manager.network = mock.Mock() - self.app.client_manager.network.find_tap_flow = mock.Mock( - side_effect=lambda name_or_id, ignore_missing: - _tap_flow.TapFlow(id=name_or_id)) - - def test_update_tap_flow(self): - """Test update tap service""" - fake_tap_flow = fakes.FakeTapFlow.create_tap_flow( - attrs={ - 'source_port': uuidutils.generate_uuid(), - 'tap_service_id': uuidutils.generate_uuid(), - } - ) - new_tap_flow = copy.deepcopy(fake_tap_flow) - new_tap_flow['name'] = self._new_name - - self.app.client_manager.network.update_tap_flow = mock.Mock( - return_value=new_tap_flow) - - arg_list = [ - fake_tap_flow['id'], - '--name', self._new_name, - ] - verify_list = [('name', self._new_name)] - - parsed_args = self.check_parser(self.cmd, arg_list, verify_list) - columns, data = self.cmd.take_action(parsed_args) - attrs = {'name': self._new_name} - - mock_update_t_f = self.app.client_manager.network.update_tap_flow - mock_update_t_f.assert_called_once_with(new_tap_flow['id'], **attrs) - self.assertEqual(self.columns, columns) - self.assertItemEqual(_get_data(new_tap_flow), data) diff --git a/neutronclient/tests/unit/osc/v2/taas/test_osc_tap_mirror.py b/neutronclient/tests/unit/osc/v2/taas/test_osc_tap_mirror.py deleted file mode 100644 index 88314842b..000000000 --- a/neutronclient/tests/unit/osc/v2/taas/test_osc_tap_mirror.py +++ /dev/null @@ -1,267 +0,0 @@ -# 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 copy -import operator -from unittest import mock - -from neutronclient.tests.unit.osc.v2 import fakes as test_fakes -from openstack.network.v2 import tap_mirror -from osc_lib import utils as osc_utils -from osc_lib.utils import columns as column_util -from oslo_utils import uuidutils - -from neutronclient.osc.v2.taas import tap_mirror as osc_tap_mirror -from neutronclient.tests.unit.osc.v2.taas import fakes - - -columns_long = tuple(col for col, _, listing_mode in osc_tap_mirror._attr_map - if listing_mode in (column_util.LIST_BOTH, - column_util.LIST_LONG_ONLY)) -headers_long = tuple(head for _, head, listing_mode in - osc_tap_mirror._attr_map if listing_mode in - (column_util.LIST_BOTH, column_util.LIST_LONG_ONLY)) -sorted_attr_map = sorted(osc_tap_mirror._attr_map, key=operator.itemgetter(1)) -sorted_columns = tuple(col for col, _, _ in sorted_attr_map) -sorted_headers = tuple(head for _, head, _ in sorted_attr_map) - - -def _get_data(attrs, columns=sorted_columns): - return osc_utils.get_dict_properties(attrs, columns) - - -class TestCreateTapMirror(test_fakes.TestNeutronClientOSCV2): - - columns = ( - 'directions', - 'id', - 'mirror_type', - 'name', - 'port_id', - 'remote_ip', - ) - - def setUp(self): - super().setUp() - self.cmd = osc_tap_mirror.CreateTapMirror(self.app, self.namespace) - - def test_create_tap_mirror(self): - port_id = uuidutils.generate_uuid() - fake_tap_mirror = fakes.FakeTapMirror.create_tap_mirror( - attrs={'port_id': port_id} - ) - self.app.client_manager.network = mock.Mock() - self.app.client_manager.network.create_tap_mirror = mock.Mock( - return_value=fake_tap_mirror) - self.app.client_manager.network.find_port = mock.Mock( - return_value={'id': port_id}) - self.app.client_manager.network.find_tap_mirror = mock.Mock( - side_effect=lambda _, name_or_id: {'id': name_or_id}) - arg_list = [ - '--name', fake_tap_mirror['name'], - '--port', fake_tap_mirror['port_id'], - '--directions', fake_tap_mirror['directions'], - '--remote-ip', fake_tap_mirror['remote_ip'], - '--mirror-type', fake_tap_mirror['mirror_type'], - ] - - verify_directions = fake_tap_mirror['directions'].split('=') - verify_directions_dict = {verify_directions[0]: verify_directions[1]} - - verify_list = [ - ('name', fake_tap_mirror['name']), - ('port_id', fake_tap_mirror['port_id']), - ('directions', verify_directions_dict), - ('remote_ip', fake_tap_mirror['remote_ip']), - ('mirror_type', fake_tap_mirror['mirror_type']), - ] - - parsed_args = self.check_parser(self.cmd, arg_list, verify_list) - self.app.client_manager.network.find_tap_mirror = mock.Mock( - return_value=fake_tap_mirror) - - columns, data = self.cmd.take_action(parsed_args) - create_tap_m_mock = self.app.client_manager.network.create_tap_mirror - create_tap_m_mock.assert_called_once_with( - **{'name': fake_tap_mirror['name'], - 'port_id': fake_tap_mirror['port_id'], - 'directions': verify_directions_dict, - 'remote_ip': fake_tap_mirror['remote_ip'], - 'mirror_type': fake_tap_mirror['mirror_type']}) - self.assertEqual(self.columns, columns) - fake_data = _get_data( - fake_tap_mirror, - osc_tap_mirror._get_columns(fake_tap_mirror)[1]) - self.assertEqual(fake_data, data) - - -class TestListTapMirror(test_fakes.TestNeutronClientOSCV2): - - def setUp(self): - super().setUp() - self.cmd = osc_tap_mirror.ListTapMirror(self.app, self.namespace) - - def test_list_tap_mirror(self): - """Test List Tap Mirror.""" - fake_tap_mirrors = fakes.FakeTapMirror.create_tap_mirrors( - attrs={'port_id': uuidutils.generate_uuid()}, - count=4) - self.app.client_manager.network = mock.Mock() - self.app.client_manager.network.tap_mirrors = mock.Mock( - return_value=fake_tap_mirrors) - - arg_list = [] - verify_list = [] - - parsed_args = self.check_parser(self.cmd, arg_list, verify_list) - - headers, data = self.cmd.take_action(parsed_args) - - self.app.client_manager.network.tap_mirrors.assert_called_once() - self.assertEqual(headers, list(headers_long)) - self.assertListItemEqual( - list(data), - [_get_data(fake_tap_mirror, columns_long) for fake_tap_mirror - in fake_tap_mirrors] - ) - - -class TestDeleteTapMirror(test_fakes.TestNeutronClientOSCV2): - - def setUp(self): - super().setUp() - self.app.client_manager.network = mock.Mock() - self.app.client_manager.network.find_tap_mirror = mock.Mock( - side_effect=lambda name_or_id, ignore_missing: - tap_mirror.TapMirror(id=name_or_id)) - self.cmd = osc_tap_mirror.DeleteTapMirror(self.app, self.namespace) - - def test_delete_tap_mirror(self): - """Test Delete Tap Mirror.""" - - fake_tap_mirror = fakes.FakeTapMirror.create_tap_mirror( - attrs={'port_id': uuidutils.generate_uuid()} - ) - self.app.client_manager.network.delete_tap_mirror = mock.Mock() - - arg_list = [ - fake_tap_mirror['id'], - ] - verify_list = [ - (osc_tap_mirror.TAP_MIRROR, [fake_tap_mirror['id']]), - ] - - parsed_args = self.check_parser(self.cmd, arg_list, verify_list) - result = self.cmd.take_action(parsed_args) - - mock_delete_tap_m = self.app.client_manager.network.delete_tap_mirror - mock_delete_tap_m.assert_called_once_with(fake_tap_mirror['id']) - self.assertIsNone(result) - - -class TestShowTapMirror(test_fakes.TestNeutronClientOSCV2): - - columns = ( - 'directions', - 'id', - 'mirror_type', - 'name', - 'port_id', - 'remote_ip', - ) - - def setUp(self): - super().setUp() - self.app.client_manager.network = mock.Mock() - self.app.client_manager.network.find_tap_mirror = mock.Mock( - side_effect=lambda name_or_id, ignore_missing: - tap_mirror.TapMirror(id=name_or_id)) - self.cmd = osc_tap_mirror.ShowTapMirror(self.app, self.namespace) - - def test_show_tap_mirror(self): - """Test Show Tap Mirror.""" - - fake_tap_mirror = fakes.FakeTapMirror.create_tap_mirror( - attrs={'port_id': uuidutils.generate_uuid()} - ) - self.app.client_manager.network.get_tap_mirror = mock.Mock( - return_value=fake_tap_mirror) - arg_list = [ - fake_tap_mirror['id'], - ] - verify_list = [ - (osc_tap_mirror.TAP_MIRROR, fake_tap_mirror['id']), - ] - - parsed_args = self.check_parser(self.cmd, arg_list, verify_list) - - headers, data = self.cmd.take_action(parsed_args) - - mock_get_tap_m = self.app.client_manager.network.get_tap_mirror - mock_get_tap_m.assert_called_once_with( - fake_tap_mirror['id']) - self.assertEqual(self.columns, headers) - fake_data = _get_data( - fake_tap_mirror, - osc_tap_mirror._get_columns(fake_tap_mirror)[1]) - self.assertItemEqual(fake_data, data) - - -class TestUpdateTapMirror(test_fakes.TestNeutronClientOSCV2): - - _new_name = 'new_name' - columns = ( - 'directions', - 'id', - 'mirror_type', - 'name', - 'port_id', - 'remote_ip', - ) - - def setUp(self): - super().setUp() - self.cmd = osc_tap_mirror.UpdateTapMirror(self.app, self.namespace) - self.app.client_manager.network = mock.Mock() - self.app.client_manager.network.find_tap_mirror = mock.Mock( - side_effect=lambda name_or_id, ignore_missing: - tap_mirror.TapMirror(id=name_or_id)) - - def test_update_tap_mirror(self): - """Test update Tap Mirror""" - fake_tap_mirror = fakes.FakeTapMirror.create_tap_mirror( - attrs={'port_id': uuidutils.generate_uuid()} - ) - new_tap_mirror = copy.deepcopy(fake_tap_mirror) - new_tap_mirror['name'] = self._new_name - - self.app.client_manager.network.update_tap_mirror = mock.Mock( - return_value=new_tap_mirror) - - arg_list = [ - fake_tap_mirror['id'], - '--name', self._new_name, - ] - verify_list = [('name', self._new_name)] - - parsed_args = self.check_parser(self.cmd, arg_list, verify_list) - columns, data = self.cmd.take_action(parsed_args) - attrs = {'name': self._new_name} - - mock_update_tap_m = self.app.client_manager.network.update_tap_mirror - mock_update_tap_m.assert_called_once_with( - fake_tap_mirror['id'], **attrs) - self.assertEqual(self.columns, columns) - fake_data = _get_data( - new_tap_mirror, - osc_tap_mirror._get_columns(new_tap_mirror)[1]) - self.assertItemEqual(fake_data, data) diff --git a/neutronclient/tests/unit/osc/v2/taas/test_osc_tap_service.py b/neutronclient/tests/unit/osc/v2/taas/test_osc_tap_service.py deleted file mode 100644 index 0b753f803..000000000 --- a/neutronclient/tests/unit/osc/v2/taas/test_osc_tap_service.py +++ /dev/null @@ -1,250 +0,0 @@ -# All Rights Reserved 2020 -# -# 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 copy -import operator -from unittest import mock - -from neutronclient.tests.unit.osc.v2 import fakes as test_fakes -from openstack.network.v2 import tap_service -from osc_lib import utils as osc_utils -from osc_lib.utils import columns as column_util -from oslo_utils import uuidutils - -from neutronclient.osc.v2.taas import tap_service as osc_tap_service -from neutronclient.tests.unit.osc.v2.taas import fakes - - -columns_long = tuple(col for col, _, listing_mode in osc_tap_service._attr_map - if listing_mode in (column_util.LIST_BOTH, - column_util.LIST_LONG_ONLY)) -headers_long = tuple(head for _, head, listing_mode in - osc_tap_service._attr_map if listing_mode in - (column_util.LIST_BOTH, column_util.LIST_LONG_ONLY)) -sorted_attr_map = sorted(osc_tap_service._attr_map, key=operator.itemgetter(1)) -sorted_columns = tuple(col for col, _, _ in sorted_attr_map) -sorted_headers = tuple(head for _, head, _ in sorted_attr_map) - - -def _get_data(attrs, columns=sorted_columns): - return osc_utils.get_dict_properties(attrs, columns) - - -class TestCreateTapService(test_fakes.TestNeutronClientOSCV2): - - columns = ( - 'id', - 'name', - 'port_id', - 'status', - ) - - def setUp(self): - super().setUp() - self.cmd = osc_tap_service.CreateTapService(self.app, self.namespace) - - def test_create_tap_service(self): - """Test Create Tap Service.""" - port_id = uuidutils.generate_uuid() - fake_tap_service = fakes.FakeTapService.create_tap_service( - attrs={'port_id': port_id} - ) - self.app.client_manager.network = mock.Mock() - self.app.client_manager.network.create_tap_service = mock.Mock( - return_value=fake_tap_service) - self.app.client_manager.network.find_port = mock.Mock( - return_value={'id': port_id}) - self.app.client_manager.network.find_tap_service = mock.Mock( - side_effect=lambda _, name_or_id: {'id': name_or_id}) - arg_list = [ - '--name', fake_tap_service['name'], - '--port', fake_tap_service['port_id'], - ] - - verify_list = [ - ('name', fake_tap_service['name']), - ('port_id', fake_tap_service['port_id']), - ] - - parsed_args = self.check_parser(self.cmd, arg_list, verify_list) - self.app.client_manager.network.find_tap_service = mock.Mock( - return_value=fake_tap_service) - - columns, data = self.cmd.take_action(parsed_args) - create_tap_s_mock = self.app.client_manager.network.create_tap_service - create_tap_s_mock.assert_called_once_with( - **{'name': fake_tap_service['name'], - 'port_id': fake_tap_service['port_id']}) - self.assertEqual(self.columns, columns) - fake_data = _get_data( - fake_tap_service, - osc_tap_service._get_columns(fake_tap_service)[1]) - self.assertEqual(fake_data, data) - - -class TestListTapService(test_fakes.TestNeutronClientOSCV2): - def setUp(self): - super().setUp() - self.cmd = osc_tap_service.ListTapService(self.app, self.namespace) - - def test_list_tap_service(self): - """Test List Tap Service.""" - fake_tap_services = fakes.FakeTapService.create_tap_services( - attrs={'port_id': uuidutils.generate_uuid()}, - count=4) - self.app.client_manager.network = mock.Mock() - self.app.client_manager.network.tap_services = mock.Mock( - return_value=fake_tap_services) - - arg_list = [] - verify_list = [] - - parsed_args = self.check_parser(self.cmd, arg_list, verify_list) - - headers, data = self.cmd.take_action(parsed_args) - - self.app.client_manager.network.tap_services.assert_called_once() - self.assertEqual(headers, list(headers_long)) - self.assertListItemEqual( - list(data), - [_get_data(fake_tap_service, columns_long) for fake_tap_service - in fake_tap_services] - ) - - -class TestDeleteTapService(test_fakes.TestNeutronClientOSCV2): - def setUp(self): - super().setUp() - self.app.client_manager.network = mock.Mock() - self.app.client_manager.network.find_tap_service = mock.Mock( - side_effect=lambda name_or_id, ignore_missing: - tap_service.TapService(id=name_or_id)) - self.cmd = osc_tap_service.DeleteTapService(self.app, self.namespace) - - def test_delete_tap_service(self): - """Test Delete tap service.""" - - fake_tap_service = fakes.FakeTapService.create_tap_service( - attrs={'port_id': uuidutils.generate_uuid()} - ) - self.app.client_manager.network.delete_tap_service = mock.Mock() - - arg_list = [ - fake_tap_service['id'], - ] - verify_list = [ - (osc_tap_service.TAP_SERVICE, [fake_tap_service['id']]), - ] - - parsed_args = self.check_parser(self.cmd, arg_list, verify_list) - result = self.cmd.take_action(parsed_args) - - mock_delete_tap_s = self.app.client_manager.network.delete_tap_service - mock_delete_tap_s.assert_called_once_with(fake_tap_service['id']) - self.assertIsNone(result) - - -class TestShowTapService(test_fakes.TestNeutronClientOSCV2): - columns = ( - 'id', - 'name', - 'port_id', - 'status', - ) - - def setUp(self): - super().setUp() - self.app.client_manager.network = mock.Mock() - self.app.client_manager.network.find_tap_service = mock.Mock( - side_effect=lambda name_or_id, ignore_missing: - tap_service.TapService(id=name_or_id)) - self.cmd = osc_tap_service.ShowTapService(self.app, self.namespace) - - def test_show_tap_service(self): - """Test Show tap service.""" - - fake_tap_service = fakes.FakeTapService.create_tap_service( - attrs={'port_id': uuidutils.generate_uuid()} - ) - self.app.client_manager.network.get_tap_service = mock.Mock( - return_value=fake_tap_service) - arg_list = [ - fake_tap_service['id'], - ] - verify_list = [ - (osc_tap_service.TAP_SERVICE, fake_tap_service['id']), - ] - - parsed_args = self.check_parser(self.cmd, arg_list, verify_list) - - headers, data = self.cmd.take_action(parsed_args) - - mock_get_tap_s = self.app.client_manager.network.get_tap_service - mock_get_tap_s.assert_called_once_with( - fake_tap_service['id']) - self.assertEqual(self.columns, headers) - fake_data = _get_data( - fake_tap_service, - osc_tap_service._get_columns(fake_tap_service)[1]) - self.assertItemEqual(fake_data, data) - - -class TestUpdateTapService(test_fakes.TestNeutronClientOSCV2): - - _new_name = 'new_name' - - columns = ( - 'id', - 'name', - 'port_id', - 'status', - ) - - def setUp(self): - super().setUp() - self.cmd = osc_tap_service.UpdateTapService(self.app, self.namespace) - self.app.client_manager.network = mock.Mock() - self.app.client_manager.network.find_tap_service = mock.Mock( - side_effect=lambda name_or_id, ignore_missing: - tap_service.TapService(id=name_or_id)) - - def test_update_tap_service(self): - """Test update tap service""" - fake_tap_service = fakes.FakeTapService.create_tap_service( - attrs={'port_id': uuidutils.generate_uuid()} - ) - new_tap_service = copy.deepcopy(fake_tap_service) - new_tap_service['name'] = self._new_name - - self.app.client_manager.network.update_tap_service = mock.Mock( - return_value=new_tap_service) - - arg_list = [ - fake_tap_service['id'], - '--name', self._new_name, - ] - verify_list = [('name', self._new_name)] - - parsed_args = self.check_parser(self.cmd, arg_list, verify_list) - columns, data = self.cmd.take_action(parsed_args) - attrs = {'name': self._new_name} - - mock_update_tap_s = self.app.client_manager.network.update_tap_service - mock_update_tap_s.assert_called_once_with( - fake_tap_service['id'], **attrs) - self.assertEqual(self.columns, columns) - fake_data = _get_data( - new_tap_service, - osc_tap_service._get_columns(new_tap_service)[1]) - self.assertItemEqual(fake_data, data) diff --git a/setup.cfg b/setup.cfg index 77c3e069a..3005e895b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -161,19 +161,3 @@ openstack.neutronclient.v2 = vpn_ipsec_site_connection_show = neutronclient.osc.v2.vpnaas.ipsec_site_connection:ShowIPsecSiteConnection network_onboard_subnets = neutronclient.osc.v2.subnet_onboard.subnet_onboard:NetworkOnboardSubnets - - tap_flow_create = neutronclient.osc.v2.taas.tap_flow:CreateTapFlow - tap_flow_delete = neutronclient.osc.v2.taas.tap_flow:DeleteTapFlow - tap_flow_list = neutronclient.osc.v2.taas.tap_flow:ListTapFlow - tap_flow_show = neutronclient.osc.v2.taas.tap_flow:ShowTapFlow - tap_flow_update = neutronclient.osc.v2.taas.tap_flow:UpdateTapFlow - tap_mirror_create = neutronclient.osc.v2.taas.tap_mirror:CreateTapMirror - tap_mirror_delete = neutronclient.osc.v2.taas.tap_mirror:DeleteTapMirror - tap_mirror_list = neutronclient.osc.v2.taas.tap_mirror:ListTapMirror - tap_mirror_show = neutronclient.osc.v2.taas.tap_mirror:ShowTapMirror - tap_mirror_update = neutronclient.osc.v2.taas.tap_mirror:UpdateTapMirror - tap_service_create = neutronclient.osc.v2.taas.tap_service:CreateTapService - tap_service_delete = neutronclient.osc.v2.taas.tap_service:DeleteTapService - tap_service_list = neutronclient.osc.v2.taas.tap_service:ListTapService - tap_service_show = neutronclient.osc.v2.taas.tap_service:ShowTapService - tap_service_update = neutronclient.osc.v2.taas.tap_service:UpdateTapService From 8f72d77812f9eb9aa1b3e28c09d378608908caa0 Mon Sep 17 00:00:00 2001 From: Zhan Zhang Date: Tue, 11 Nov 2025 13:58:21 -0500 Subject: [PATCH 839/845] v2_0: Use 'bindings' when listing port bindings This commit fixes a bug in v2_0 client's "list_port_bindings" function, where it uses "port_bindings" to access Neutron's response, instead of "bindings" [0]. [0]: https://docs.openstack.org/api-ref/network/v2/index.html#show-port-binding-of-a-port Closes-Bug: #2130459 Change-Id: I32ef753ec212b55f698e3844e043f68b22992ead Signed-off-by: Zhan Zhang --- neutronclient/v2_0/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 9922eb31c..82676b1c1 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -850,7 +850,7 @@ def show_port_binding(self, port_id, host_id, **_params): def list_port_bindings(self, port_id, retrieve_all=True, **_params): """Fetches a list of all bindings for a certain port.""" - return self.list('port_bindings', self.port_bindings_path % port_id, + return self.list('bindings', self.port_bindings_path % port_id, retrieve_all, **_params) def activate_port_binding(self, port_id, host_id): From d9a3518f75adc7fed9db215772804cc74b066df2 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Sat, 21 Feb 2026 00:18:54 +0900 Subject: [PATCH 840/845] Bump flake8-import-order The 1.18.2 release requires pkg_resources. The dependency was removed in 1.19.0 . Change-Id: I25106085e68dfc13a091af0e9e365287dac17308 Signed-off-by: Takashi Kajinami --- test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index 464d2b8a4..d6a160c26 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -3,7 +3,7 @@ hacking>=6.1.0,<6.2.0 # Apache-2.0 bandit!=1.6.0,>=1.1.0 # Apache-2.0 coverage!=4.4,>=4.0 # Apache-2.0 fixtures>=3.0.0 # Apache-2.0/BSD -flake8-import-order>=0.18.0,<0.19.0 # LGPLv3 +flake8-import-order>=0.19.0,<0.20.0 # LGPLv3 oslotest>=3.2.0 # Apache-2.0 osprofiler>=2.3.0 # Apache-2.0 python-openstackclient>=3.12.0 # Apache-2.0 From f7c085005de1ec98656c904a4d90fe833044ae9e Mon Sep 17 00:00:00 2001 From: Slawek Kaplonski Date: Mon, 23 Feb 2026 16:39:28 +0100 Subject: [PATCH 841/845] [FWaaS] Remove client side protocol validation There is no need to limit available choices for the firewall rule's protocol on the client side. Neutron-fwaas plugin on the server side will do the validation in the same way as for security group rules. And for SG rules OSC is not validating nor limiting choices on the client's side at all. Closes-bug: #2142479 Change-Id: I8c02a2232601c2ab6655c458aa0365102b3b5e2d Signed-off-by: Slawek Kaplonski --- neutronclient/osc/v2/fwaas/firewallrule.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/neutronclient/osc/v2/fwaas/firewallrule.py b/neutronclient/osc/v2/fwaas/firewallrule.py index 206a9b4e1..8bc15b579 100644 --- a/neutronclient/osc/v2/fwaas/firewallrule.py +++ b/neutronclient/osc/v2/fwaas/firewallrule.py @@ -86,9 +86,12 @@ def _get_common_parser(parser): help=_('Description of the firewall rule')) parser.add_argument( '--protocol', - choices=['tcp', 'udp', 'icmp', 'any'], type=nc_utils.convert_to_lowercase, - help=_('Protocol for the firewall rule')) + help=_('IP protocol (ah, dccp, egp, esp, gre, icmp, igmp, ' + 'ipv6-encap, ipv6-frag, ipv6-icmp, ipv6-nonxt, ipv6-opts, ' + 'ipv6-route, ospf, pgm, rsvp, sctp, tcp, udp, udplite, ' + 'vrrp and integer representations [0-255] or any; ' + 'default: any (all protocols))')) parser.add_argument( '--action', choices=['allow', 'deny', 'reject'], From 6e5eb4da33f8accf15a1d5680062c0b6b6fa9c3b Mon Sep 17 00:00:00 2001 From: Slawek Kaplonski Date: Thu, 26 Feb 2026 11:04:47 +0100 Subject: [PATCH 842/845] [fwaas] Don't ignore missing fw related resources Previously when fwaas osc plugin was going to look for firewall related resources, like e.g. asking neutron for firewall group or policy was done with ignore_missing=True which means that openstack SDK client didn't raise exception when requested object was not found on the server. That led to the weird and unclear error message displayed by the OpenStack client which said something about "'NoneType' object is not subscriptable". This patch adds "ignore_missing=True" to all those "find_*" methods used in the fwaas osc plugin. That way proper exception is raised by the SDK client and valid error message is displayed to the user by OSC. Closes-bug: #2142458 Change-Id: I309a5dbf61c65d5837d4ea2b3235aa41269ae73d Signed-off-by: Slawek Kaplonski --- neutronclient/osc/v2/fwaas/firewallgroup.py | 24 ++++++++------ neutronclient/osc/v2/fwaas/firewallpolicy.py | 33 +++++++++++-------- neutronclient/osc/v2/fwaas/firewallrule.py | 16 +++++---- .../unit/osc/v2/fwaas/test_firewallgroup.py | 14 ++++---- 4 files changed, 50 insertions(+), 37 deletions(-) diff --git a/neutronclient/osc/v2/fwaas/firewallgroup.py b/neutronclient/osc/v2/fwaas/firewallgroup.py index 723e295fb..a605ddde8 100644 --- a/neutronclient/osc/v2/fwaas/firewallgroup.py +++ b/neutronclient/osc/v2/fwaas/firewallgroup.py @@ -129,19 +129,19 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): if (parsed_args.ingress_firewall_policy and parsed_args.no_ingress_firewall_policy): attrs['ingress_firewall_policy_id'] = client.find_firewall_policy( - parsed_args.ingress_firewall_policy)['id'] + parsed_args.ingress_firewall_policy, ignore_missing=False)['id'] elif parsed_args.ingress_firewall_policy: attrs['ingress_firewall_policy_id'] = client.find_firewall_policy( - parsed_args.ingress_firewall_policy)['id'] + parsed_args.ingress_firewall_policy, ignore_missing=False)['id'] elif parsed_args.no_ingress_firewall_policy: attrs['ingress_firewall_policy_id'] = None if (parsed_args.egress_firewall_policy and parsed_args.no_egress_firewall_policy): attrs['egress_firewall_policy_id'] = client.find_firewall_policy( - parsed_args.egress_firewall_policy)['id'] + parsed_args.egress_firewall_policy, ignore_missing=False)['id'] elif parsed_args.egress_firewall_policy: attrs['egress_firewall_policy_id'] = client.find_firewall_policy( - parsed_args.egress_firewall_policy)['id'] + parsed_args.egress_firewall_policy, ignore_missing=False)['id'] elif parsed_args.no_egress_firewall_policy: attrs['egress_firewall_policy_id'] = None if parsed_args.share: @@ -165,7 +165,7 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): ports.append(client.find_port(p)['id']) if not is_create: ports += client.find_firewall_group( - parsed_args.firewall_group)['ports'] + parsed_args.firewall_group, ignore_missing=False)['ports'] attrs['ports'] = sorted(set(ports)) elif parsed_args.no_port: attrs['ports'] = [] @@ -220,7 +220,8 @@ def take_action(self, parsed_args): result = 0 for fwg in parsed_args.firewall_group: try: - fwg_id = client.find_firewall_group(fwg)['id'] + fwg_id = client.find_firewall_group( + fwg, ignore_missing=False)['id'] client.delete_firewall_group(fwg_id) except Exception as e: result += 1 @@ -281,7 +282,8 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.network - fwg_id = client.find_firewall_group(parsed_args.firewall_group)['id'] + fwg_id = client.find_firewall_group( + parsed_args.firewall_group, ignore_missing=False)['id'] attrs = _get_common_attrs(self.app.client_manager, parsed_args, is_create=False) try: @@ -305,7 +307,8 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.network - fwg_id = client.find_firewall_group(parsed_args.firewall_group)['id'] + fwg_id = client.find_firewall_group( + parsed_args.firewall_group, ignore_missing=False)['id'] obj = client.get_firewall_group(fwg_id) display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( obj, _attr_map_dict, ['location', 'tenant_id']) @@ -366,7 +369,7 @@ def _get_attrs(self, client, parsed_args): attrs['admin_state_up'] = False if parsed_args.port: old = client.find_firewall_group( - parsed_args.firewall_group)['ports'] + parsed_args.firewall_group, ignore_missing=False)['ports'] new = [client.find_port(r)['id'] for r in parsed_args.port] attrs['ports'] = sorted(list(set(old) - set(new))) if parsed_args.all_port: @@ -375,7 +378,8 @@ def _get_attrs(self, client, parsed_args): def take_action(self, parsed_args): client = self.app.client_manager.network - fwg_id = client.find_firewall_group(parsed_args.firewall_group)['id'] + fwg_id = client.find_firewall_group( + parsed_args.firewall_group, ignore_missing=False)['id'] attrs = self._get_attrs(client, parsed_args) try: client.update_firewall_group(fwg_id, **attrs) diff --git a/neutronclient/osc/v2/fwaas/firewallpolicy.py b/neutronclient/osc/v2/fwaas/firewallpolicy.py index cfcc3c1df..cb2030470 100644 --- a/neutronclient/osc/v2/fwaas/firewallpolicy.py +++ b/neutronclient/osc/v2/fwaas/firewallpolicy.py @@ -67,16 +67,18 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): if parsed_args.firewall_rule and parsed_args.no_firewall_rule: _firewall_rules = [] for f in parsed_args.firewall_rule: - _firewall_rules.append(client.find_firewall_rule(f)['id']) + _firewall_rules.append( + client.find_firewall_rule(f, ignore_missing=False)['id']) attrs[const.FWRS] = _firewall_rules elif parsed_args.firewall_rule: rules = [] if not is_create: foobar = client.find_firewall_policy( - parsed_args.firewall_policy) + parsed_args.firewall_policy, ignore_missing=False) rules += foobar[const.FWRS] for f in parsed_args.firewall_rule: - rules.append(client.find_firewall_rule(f)['id']) + rules.append( + client.find_firewall_rule(f, ignore_missing=False)['id']) attrs[const.FWRS] = rules elif parsed_args.no_firewall_rule: attrs[const.FWRS] = [] @@ -173,7 +175,8 @@ def take_action(self, parsed_args): result = 0 for fwp in parsed_args.firewall_policy: try: - fwp_id = client.find_firewall_policy(fwp)['id'] + fwp_id = client.find_firewall_policy( + fwp, ignore_missing=False)['id'] client.delete_firewall_policy(fwp_id) except Exception as e: result += 1 @@ -220,12 +223,12 @@ def args2body(self, parsed_args): if 'insert_before' in parsed_args: if parsed_args.insert_before: _insert_before = client.find_firewall_rule( - parsed_args.insert_before)['id'] + parsed_args.insert_before, ignore_missing=False)['id'] _insert_after = '' if 'insert_after' in parsed_args: if parsed_args.insert_after: _insert_after = client.find_firewall_rule( - parsed_args.insert_after)['id'] + parsed_args.insert_after, ignore_missing=False)['id'] return {'firewall_rule_id': _rule_id, 'insert_before': _insert_before, 'insert_after': _insert_after} @@ -233,7 +236,7 @@ def args2body(self, parsed_args): def take_action(self, parsed_args): client = self.app.client_manager.network policy_id = client.find_firewall_policy( - parsed_args.firewall_policy)['id'] + parsed_args.firewall_policy, ignore_missing=False)['id'] body = self.args2body(parsed_args) client.insert_rule_into_policy(policy_id, **body) rule_id = body['firewall_rule_id'] @@ -261,7 +264,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.network policy_id = client.find_firewall_policy( - parsed_args.firewall_policy)['id'] + parsed_args.firewall_policy, ignore_missing=False)['id'] fwr_id = _get_required_firewall_rule(client, parsed_args) body = {'firewall_rule_id': fwr_id} client.remove_rule_from_policy(policy_id, **body) @@ -322,7 +325,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.network fwp_id = client.find_firewall_policy( - parsed_args.firewall_policy)['id'] + parsed_args.firewall_policy, ignore_missing=False)['id'] attrs = _get_common_attrs(self.app.client_manager, parsed_args, is_create=False) try: @@ -347,7 +350,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.network fwp_id = client.find_firewall_policy( - parsed_args.firewall_policy)['id'] + parsed_args.firewall_policy, ignore_missing=False)['id'] obj = client.get_firewall_policy(fwp_id) display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( obj, _attr_map_dict, ['location', 'tenant_id']) @@ -359,7 +362,8 @@ def _get_required_firewall_rule(client, parsed_args): if not parsed_args.firewall_rule: msg = (_("Firewall rule (name or ID) is required.")) raise exceptions.CommandError(msg) - return client.find_firewall_rule(parsed_args.firewall_rule)['id'] + return client.find_firewall_rule( + parsed_args.firewall_rule, ignore_missing=False)['id'] class UnsetFirewallPolicy(command.Command): @@ -399,10 +403,11 @@ def _get_attrs(self, client_manager, parsed_args): if parsed_args.firewall_rule: current = client.find_firewall_policy( - parsed_args.firewall_policy)[const.FWRS] + parsed_args.firewall_policy, ignore_missing=False)[const.FWRS] removed = [] for f in set(parsed_args.firewall_rule): - removed.append(client.find_firewall_rule(f)['id']) + removed.append( + client.find_firewall_rule(f, ignore_missing=False)['id']) attrs[const.FWRS] = [r for r in current if r not in removed] if parsed_args.all_firewall_rule: attrs[const.FWRS] = [] @@ -415,7 +420,7 @@ def _get_attrs(self, client_manager, parsed_args): def take_action(self, parsed_args): client = self.app.client_manager.network fwp_id = client.find_firewall_policy( - parsed_args.firewall_policy)['id'] + parsed_args.firewall_policy, ignore_missing=False)['id'] attrs = self._get_attrs(self.app.client_manager, parsed_args) try: client.update_firewall_policy(fwp_id, **attrs) diff --git a/neutronclient/osc/v2/fwaas/firewallrule.py b/neutronclient/osc/v2/fwaas/firewallrule.py index 8bc15b579..552558d71 100644 --- a/neutronclient/osc/v2/fwaas/firewallrule.py +++ b/neutronclient/osc/v2/fwaas/firewallrule.py @@ -229,12 +229,12 @@ def _get_common_attrs(client_manager, parsed_args, is_create=True): attrs['shared'] = False if parsed_args.source_firewall_group: attrs['source_firewall_group_id'] = client.find_firewall_group( - parsed_args.source_firewall_group)['id'] + parsed_args.source_firewall_group, ignore_missing=False)['id'] if parsed_args.no_source_firewall_group: attrs['source_firewall_group_id'] = None if parsed_args.destination_firewall_group: attrs['destination_firewall_group_id'] = client.find_firewall_group( - parsed_args.destination_firewall_group)['id'] + parsed_args.destination_firewall_group, ignore_missing=False)['id'] if parsed_args.no_destination_firewall_group: attrs['destination_firewall_group_id'] = None return attrs @@ -284,7 +284,8 @@ def take_action(self, parsed_args): result = 0 for fwr in parsed_args.firewall_rule: try: - fwr_id = client.find_firewall_rule(fwr)['id'] + fwr_id = client.find_firewall_rule( + fwr, ignore_missing=False)['id'] client.delete_firewall_rule(fwr_id) except Exception as e: result += 1 @@ -361,7 +362,8 @@ def take_action(self, parsed_args): client = self.app.client_manager.network attrs = _get_common_attrs(self.app.client_manager, parsed_args, is_create=False) - fwr_id = client.find_firewall_rule(parsed_args.firewall_rule)['id'] + fwr_id = client.find_firewall_rule( + parsed_args.firewall_rule, ignore_missing=False)['id'] try: client.update_firewall_rule(fwr_id, **attrs) except Exception as e: @@ -383,7 +385,8 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.network - fwr_id = client.find_firewall_rule(parsed_args.firewall_rule)['id'] + fwr_id = client.find_firewall_rule( + parsed_args.firewall_rule, ignore_missing=False)['id'] obj = client.get_firewall_rule(fwr_id) display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( obj, _attr_map_dict, ['location', 'tenant_id']) @@ -461,7 +464,8 @@ def _get_attrs(self, client_manager, parsed_args): def take_action(self, parsed_args): client = self.app.client_manager.network attrs = self._get_attrs(self.app.client_manager, parsed_args) - fwr_id = client.find_firewall_rule(parsed_args.firewall_rule)['id'] + fwr_id = client.find_firewall_rule( + parsed_args.firewall_rule, ignore_missing=False)['id'] try: client.update_firewall_rule(fwr_id, **attrs) except Exception as e: diff --git a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py index 45f42ee83..8596e9522 100644 --- a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py +++ b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py @@ -215,7 +215,7 @@ def _mock_port_fwg(*args, **kwargs): parsed_args = self.check_parser(self.cmd, arglist, verifylist) headers, data = self.cmd.take_action(parsed_args) self.networkclient.find_firewall_policy.assert_called_once_with( - ingress_policy) + ingress_policy, ignore_missing=False) self.check_results(headers, data, request) @@ -236,7 +236,7 @@ def _mock_find(*args, **kwargs): headers, data = self.cmd.take_action(parsed_args) self.networkclient.find_firewall_policy.assert_called_once_with( - egress_policy) + egress_policy, ignore_missing=False) self.check_results(headers, data, request) def test_create_with_all_params(self): @@ -398,15 +398,15 @@ def _mock_fwg_policy(*args, **kwargs): # 1. Find specified firewall_group if self.networkclient.find_firewall_group.call_count == 1: self.networkclient.find_firewall_group.assert_called_with( - target) + target, ignore_missing=False) # 2. Find specified 'ingress_firewall_policy' if self.networkclient.find_firewall_policy.call_count == 1: self.networkclient.find_firewall_policy.assert_called_with( - ingress_policy) + ingress_policy, ignore_missing=False) # 3. Find specified 'ingress_firewall_policy' if self.networkclient.find_firewall_policy.call_count == 2: self.networkclient.find_firewall_policy.assert_called_with( - egress_policy) + egress_policy, ignore_missing=False) return {'id': args[0]} self.networkclient.find_firewall_group.side_effect = _mock_fwg_policy @@ -439,7 +439,7 @@ def _mock_port_fwg(*args, **kwargs): # 1. Find specified firewall_group if self.networkclient.find_firewall_group.call_count in [1, 2]: self.networkclient.find_firewall_group.assert_called_with( - target) + target, ignore_missing=False) return {'id': args[0], 'ports': _fwg['ports']} # 2. Find specified 'port' #1 if self.networkclient.find_port.call_count == 1: @@ -687,7 +687,7 @@ def _mock_port_fwg(*args, **kwargs): # 1. Find specified firewall_group if self.networkclient.find_firewall_group.call_count in [1, 2]: self.networkclient.find_firewall_group.assert_called_with( - target) + target, ignore_missing=False) return {'id': args[0], 'ports': _fwg['ports']} # 2. Find specified firewall_group and refer 'ports' attribute if self.networkclient.find_port.call_count == 2: From 18b1232db4189bb7dfebdcb400dae38ad27969fc Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Wed, 4 Mar 2026 18:25:43 +0000 Subject: [PATCH 843/845] Update master for stable/2026.1 Add file to the reno documentation build to show release notes for stable/2026.1. Use pbr instruction to increment the minor version number automatically so that master versions are higher than the versions on stable/2026.1. Sem-Ver: feature Change-Id: Icaf974beefa4e54680fa161370a54ffd9487c0ca Signed-off-by: OpenStack Release Bot Generated-By: openstack/project-config:roles/copy-release-tools-scripts/files/release-tools/add_release_note_page.sh --- releasenotes/source/2026.1.rst | 6 ++++++ releasenotes/source/index.rst | 1 + 2 files changed, 7 insertions(+) create mode 100644 releasenotes/source/2026.1.rst diff --git a/releasenotes/source/2026.1.rst b/releasenotes/source/2026.1.rst new file mode 100644 index 000000000..3d2861580 --- /dev/null +++ b/releasenotes/source/2026.1.rst @@ -0,0 +1,6 @@ +=========================== +2026.1 Series Release Notes +=========================== + +.. release-notes:: + :branch: stable/2026.1 diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index 73d4a0cab..7fe1c4a53 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -6,6 +6,7 @@ :maxdepth: 1 unreleased + 2026.1 2025.2 2025.1 2024.2 From 759faa1b4a88ad7ce205f24c2f7981860dc81f1c Mon Sep 17 00:00:00 2001 From: Slawek Kaplonski Date: Tue, 24 Mar 2026 14:33:35 +0100 Subject: [PATCH 844/845] Remove fwaas client code The team agreed to move these client codes to OSC see the depends-on patch. Depends-On: https://review.opendev.org/c/openstack/python-openstackclient/+/981891 Assisted-by: Claude-Code (claude-opus-4.6) Change-Id: I4e3773992c537cf50d9f5a4a2ff04ae7414a528d Signed-off-by: Slawek Kaplonski --- doc/source/cli/osc/v2/firewall-group.rst | 30 - doc/source/cli/osc/v2/firewall-policy.rst | 14 - doc/source/cli/osc/v2/firewall-rule.rst | 12 - neutronclient/osc/v2/fwaas/constants.py | 3 + neutronclient/osc/v2/fwaas/firewallgroup.py | 389 -------- neutronclient/osc/v2/fwaas/firewallpolicy.py | 430 --------- neutronclient/osc/v2/fwaas/firewallrule.py | 474 --------- .../tests/unit/osc/v2/fwaas/__init__.py | 0 .../tests/unit/osc/v2/fwaas/common.py | 304 ------ .../tests/unit/osc/v2/fwaas/fakes.py | 151 --- .../unit/osc/v2/fwaas/test_firewallgroup.py | 730 -------------- .../unit/osc/v2/fwaas/test_firewallpolicy.py | 673 ------------- .../unit/osc/v2/fwaas/test_firewallrule.py | 904 ------------------ ...ove-fwaas-osc-plugin-a1b2c3d4e5f6g7h8.yaml | 11 + setup.cfg | 21 - 15 files changed, 14 insertions(+), 4132 deletions(-) delete mode 100644 doc/source/cli/osc/v2/firewall-group.rst delete mode 100644 doc/source/cli/osc/v2/firewall-policy.rst delete mode 100644 doc/source/cli/osc/v2/firewall-rule.rst delete mode 100644 neutronclient/osc/v2/fwaas/firewallgroup.py delete mode 100644 neutronclient/osc/v2/fwaas/firewallpolicy.py delete mode 100644 neutronclient/osc/v2/fwaas/firewallrule.py delete mode 100644 neutronclient/tests/unit/osc/v2/fwaas/__init__.py delete mode 100644 neutronclient/tests/unit/osc/v2/fwaas/common.py delete mode 100644 neutronclient/tests/unit/osc/v2/fwaas/fakes.py delete mode 100644 neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py delete mode 100644 neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py delete mode 100644 neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py create mode 100644 releasenotes/notes/remove-fwaas-osc-plugin-a1b2c3d4e5f6g7h8.yaml diff --git a/doc/source/cli/osc/v2/firewall-group.rst b/doc/source/cli/osc/v2/firewall-group.rst deleted file mode 100644 index adb8d05d3..000000000 --- a/doc/source/cli/osc/v2/firewall-group.rst +++ /dev/null @@ -1,30 +0,0 @@ -============== -firewall group -============== - -A **firewall group** is a perimeter firewall management to Networking. -Firewall group uses iptables to apply firewall policy to all VM ports and -router ports within a project. - -Network v2 - -.. 'firewall group *' cannot be used below as it matches 'firewall group rule - *' or 'firewall group policy *'. - -.. autoprogram-cliff:: openstack.neutronclient.v2 - :command: firewall group create - -.. autoprogram-cliff:: openstack.neutronclient.v2 - :command: firewall group delete - -.. autoprogram-cliff:: openstack.neutronclient.v2 - :command: firewall group list - -.. autoprogram-cliff:: openstack.neutronclient.v2 - :command: firewall group set - -.. autoprogram-cliff:: openstack.neutronclient.v2 - :command: firewall group show - -.. autoprogram-cliff:: openstack.neutronclient.v2 - :command: firewall group unset diff --git a/doc/source/cli/osc/v2/firewall-policy.rst b/doc/source/cli/osc/v2/firewall-policy.rst deleted file mode 100644 index f05f83451..000000000 --- a/doc/source/cli/osc/v2/firewall-policy.rst +++ /dev/null @@ -1,14 +0,0 @@ -===================== -firewall group policy -===================== - -A **firewall group policy** is an ordered collection of firewall rules. -A firewall policy can be shared across projects. Thus it can also be made part -of an audit workflow wherein the firewall_policy can be audited by the -relevant entity that is authorized (and can be different from the projects -which create or use the firewall group policy). - -Network v2 - -.. autoprogram-cliff:: openstack.neutronclient.v2 - :command: firewall group policy * diff --git a/doc/source/cli/osc/v2/firewall-rule.rst b/doc/source/cli/osc/v2/firewall-rule.rst deleted file mode 100644 index 2e75c775f..000000000 --- a/doc/source/cli/osc/v2/firewall-rule.rst +++ /dev/null @@ -1,12 +0,0 @@ -=================== -firewall group rule -=================== - -A **firewall group rule** represents a collection of attributes like ports, IP -addresses which define match criteria and action (allow, or deny) that needs to -be taken on the matched data traffic. - -Network v2 - -.. autoprogram-cliff:: openstack.neutronclient.v2 - :command: firewall group rule * diff --git a/neutronclient/osc/v2/fwaas/constants.py b/neutronclient/osc/v2/fwaas/constants.py index 4c8ee6a2d..a5a74af73 100644 --- a/neutronclient/osc/v2/fwaas/constants.py +++ b/neutronclient/osc/v2/fwaas/constants.py @@ -14,6 +14,9 @@ # under the License. # +# TODO(slaweq): remove it once "logging" OSC plugin will be moved to the +# python-openstackclient repo + FWG = 'firewall_group' FWGS = 'firewall_groups' FWP = 'firewall_policy' diff --git a/neutronclient/osc/v2/fwaas/firewallgroup.py b/neutronclient/osc/v2/fwaas/firewallgroup.py deleted file mode 100644 index a605ddde8..000000000 --- a/neutronclient/osc/v2/fwaas/firewallgroup.py +++ /dev/null @@ -1,389 +0,0 @@ -# Copyright 2016-2017 FUJITSU LIMITED -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import logging - -from osc_lib.command import command -from osc_lib import exceptions -from osc_lib import utils -from osc_lib.utils import columns as column_util - -from neutronclient._i18n import _ -from neutronclient.osc import utils as osc_utils -from neutronclient.osc.v2.fwaas import constants as const -from neutronclient.osc.v2 import utils as v2_utils - - -LOG = logging.getLogger(__name__) - -_formatters = { - 'admin_state_up': v2_utils.AdminStateColumn, -} - -_attr_map_dict = { - 'id': 'ID', - 'name': 'Name', - 'ingress_firewall_policy_id': 'Ingress Policy ID', - 'egress_firewall_policy_id': 'Egress Policy ID', - 'description': 'Description', - 'status': 'Status', - 'ports': 'Ports', - 'admin_state_up': 'State', - 'shared': 'Shared', - 'tenant_id': 'Project', - 'project_id': 'Project', -} - -_attr_map = ( - ('id', 'ID', column_util.LIST_BOTH), - ('name', 'Name', column_util.LIST_BOTH), - ('ingress_firewall_policy_id', 'Ingress Policy ID', column_util.LIST_BOTH), - ('egress_firewall_policy_id', 'Egress Policy ID', column_util.LIST_BOTH), - ('description', 'Description', column_util.LIST_LONG_ONLY), - ('status', 'Status', column_util.LIST_LONG_ONLY), - ('ports', 'Ports', column_util.LIST_LONG_ONLY), - ('admin_state_up', 'State', column_util.LIST_LONG_ONLY), - ('shared', 'Shared', column_util.LIST_LONG_ONLY), - ('tenant_id', 'Project', column_util.LIST_LONG_ONLY), -) - - -def _get_common_parser(parser): - parser.add_argument( - '--name', - help=_('Name for the firewall group')) - parser.add_argument( - '--description', - metavar='', - help=_('Description of the firewall group')) - ingress_group = parser.add_mutually_exclusive_group() - ingress_group.add_argument( - '--ingress-firewall-policy', - metavar='', - dest='ingress_firewall_policy', - help=_('Ingress firewall policy (name or ID)')) - ingress_group.add_argument( - '--no-ingress-firewall-policy', - dest='no_ingress_firewall_policy', - action='store_true', - help=_('Detach ingress firewall policy from the firewall group')) - egress_group = parser.add_mutually_exclusive_group() - egress_group.add_argument( - '--egress-firewall-policy', - metavar='', - dest='egress_firewall_policy', - help=_('Egress firewall policy (name or ID)')) - egress_group.add_argument( - '--no-egress-firewall-policy', - dest='no_egress_firewall_policy', - action='store_true', - help=_('Detach egress firewall policy from the firewall group')) - shared_group = parser.add_mutually_exclusive_group() - - shared_group.add_argument( - '--share', - action='store_true', - help=_('Share the firewall group to be used in all projects ' - '(by default, it is restricted to be used by the ' - 'current project).')) - shared_group.add_argument( - '--no-share', - action='store_true', - help=_('Restrict use of the firewall group to the ' - 'current project')) - admin_group = parser.add_mutually_exclusive_group() - admin_group.add_argument( - '--enable', - action='store_true', - help=_('Enable firewall group')) - admin_group.add_argument( - '--disable', - action='store_true', - help=_('Disable firewall group')) - return parser - - -def _get_common_attrs(client_manager, parsed_args, is_create=True): - attrs = {} - client = client_manager.network - - if is_create: - if 'project' in parsed_args and parsed_args.project is not None: - attrs['tenant_id'] = osc_utils.find_project( - client_manager.identity, - parsed_args.project, - parsed_args.project_domain, - ).id - if (parsed_args.ingress_firewall_policy and - parsed_args.no_ingress_firewall_policy): - attrs['ingress_firewall_policy_id'] = client.find_firewall_policy( - parsed_args.ingress_firewall_policy, ignore_missing=False)['id'] - elif parsed_args.ingress_firewall_policy: - attrs['ingress_firewall_policy_id'] = client.find_firewall_policy( - parsed_args.ingress_firewall_policy, ignore_missing=False)['id'] - elif parsed_args.no_ingress_firewall_policy: - attrs['ingress_firewall_policy_id'] = None - if (parsed_args.egress_firewall_policy and - parsed_args.no_egress_firewall_policy): - attrs['egress_firewall_policy_id'] = client.find_firewall_policy( - parsed_args.egress_firewall_policy, ignore_missing=False)['id'] - elif parsed_args.egress_firewall_policy: - attrs['egress_firewall_policy_id'] = client.find_firewall_policy( - parsed_args.egress_firewall_policy, ignore_missing=False)['id'] - elif parsed_args.no_egress_firewall_policy: - attrs['egress_firewall_policy_id'] = None - if parsed_args.share: - attrs['shared'] = True - if parsed_args.no_share: - attrs['shared'] = False - if parsed_args.enable: - attrs['admin_state_up'] = True - if parsed_args.disable: - attrs['admin_state_up'] = False - if parsed_args.name: - attrs['name'] = str(parsed_args.name) - if parsed_args.description: - attrs['description'] = str(parsed_args.description) - if parsed_args.port and parsed_args.no_port: - attrs['ports'] = sorted([client.find_port( - p)['id'] for p in set(parsed_args.port)]) - elif parsed_args.port: - ports = [] - for p in set(parsed_args.port): - ports.append(client.find_port(p)['id']) - if not is_create: - ports += client.find_firewall_group( - parsed_args.firewall_group, ignore_missing=False)['ports'] - attrs['ports'] = sorted(set(ports)) - elif parsed_args.no_port: - attrs['ports'] = [] - return attrs - - -class CreateFirewallGroup(command.ShowOne): - _description = _("Create a new firewall group") - - def get_parser(self, prog_name): - parser = super(CreateFirewallGroup, self).get_parser(prog_name) - _get_common_parser(parser) - osc_utils.add_project_owner_option_to_parser(parser) - port_group = parser.add_mutually_exclusive_group() - port_group.add_argument( - '--port', - metavar='', - action='append', - help=_('Port(s) (name or ID) to apply firewall group. This ' - 'option can be repeated')) - port_group.add_argument( - '--no-port', - dest='no_port', - action='store_true', - help=_('Detach all port from the firewall group')) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - attrs = _get_common_attrs(self.app.client_manager, parsed_args) - obj = client.create_firewall_group(**attrs) - display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( - obj, _attr_map_dict, ['location', 'tenant_id']) - data = utils.get_dict_properties(obj, columns, formatters=_formatters) - return (display_columns, data) - - -class DeleteFirewallGroup(command.Command): - _description = _("Delete firewall group(s)") - - def get_parser(self, prog_name): - parser = super(DeleteFirewallGroup, self).get_parser(prog_name) - parser.add_argument( - const.FWG, - metavar='', - nargs='+', - help=_('Firewall group(s) to delete (name or ID)')) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - result = 0 - for fwg in parsed_args.firewall_group: - try: - fwg_id = client.find_firewall_group( - fwg, ignore_missing=False)['id'] - client.delete_firewall_group(fwg_id) - except Exception as e: - result += 1 - LOG.error(_("Failed to delete firewall group with " - "name or ID '%(firewall_group)s': %(e)s"), - {const.FWG: fwg, 'e': e}) - - if result > 0: - total = len(parsed_args.firewall_group) - msg = (_("%(result)s of %(total)s firewall group(s) " - "failed to delete.") % {'result': result, 'total': total}) - raise exceptions.CommandError(msg) - - -class ListFirewallGroup(command.Lister): - _description = _("List firewall groups") - - def get_parser(self, prog_name): - parser = super(ListFirewallGroup, self).get_parser(prog_name) - parser.add_argument( - '--long', - action='store_true', - help=_("List additional fields in output") - ) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - obj = client.firewall_groups() - headers, columns = column_util.get_column_definitions( - _attr_map, long_listing=parsed_args.long) - return (headers, (utils.get_dict_properties( - s, columns, formatters=_formatters) for s in obj)) - - -class SetFirewallGroup(command.Command): - _description = _("Set firewall group properties") - - def get_parser(self, prog_name): - parser = super(SetFirewallGroup, self).get_parser(prog_name) - _get_common_parser(parser) - parser.add_argument( - const.FWG, - metavar='', - help=_('Firewall group to update (name or ID)')) - parser.add_argument( - '--port', - metavar='', - action='append', - help=_('Port(s) (name or ID) to apply firewall group. This ' - 'option can be repeated')) - parser.add_argument( - '--no-port', - dest='no_port', - action='store_true', - help=_('Detach all port from the firewall group')) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - fwg_id = client.find_firewall_group( - parsed_args.firewall_group, ignore_missing=False)['id'] - attrs = _get_common_attrs(self.app.client_manager, parsed_args, - is_create=False) - try: - client.update_firewall_group(fwg_id, **attrs) - except Exception as e: - msg = (_("Failed to set firewall group '%(group)s': %(e)s") - % {'group': parsed_args.firewall_group, 'e': e}) - raise exceptions.CommandError(msg) - - -class ShowFirewallGroup(command.ShowOne): - _description = _("Display firewall group details") - - def get_parser(self, prog_name): - parser = super(ShowFirewallGroup, self).get_parser(prog_name) - parser.add_argument( - const.FWG, - metavar='', - help=_('Firewall group to show (name or ID)')) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - fwg_id = client.find_firewall_group( - parsed_args.firewall_group, ignore_missing=False)['id'] - obj = client.get_firewall_group(fwg_id) - display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( - obj, _attr_map_dict, ['location', 'tenant_id']) - data = utils.get_dict_properties(obj, columns, formatters=_formatters) - return (display_columns, data) - - -class UnsetFirewallGroup(command.Command): - _description = _("Unset firewall group properties") - - def get_parser(self, prog_name): - parser = super(UnsetFirewallGroup, self).get_parser(prog_name) - parser.add_argument( - const.FWG, - metavar='', - help=_('Firewall group to unset (name or ID)')) - port_group = parser.add_mutually_exclusive_group() - port_group.add_argument( - '--port', - metavar='', - action='append', - help=_('Port(s) (name or ID) to apply firewall group. This ' - 'option can be repeated')) - port_group.add_argument( - '--all-port', - action='store_true', - help=_('Remove all ports for this firewall group')) - parser.add_argument( - '--ingress-firewall-policy', - action='store_true', - help=_('Ingress firewall policy (name or ID) to delete')) - parser.add_argument( - '--egress-firewall-policy', - action='store_true', - dest='egress_firewall_policy', - help=_('Egress firewall policy (name or ID) to delete')) - shared_group = parser.add_mutually_exclusive_group() - shared_group.add_argument( - '--share', - action='store_true', - help=_('Restrict use of the firewall group to the ' - 'current project')) - parser.add_argument( - '--enable', - action='store_true', - help=_('Disable firewall group')) - return parser - - def _get_attrs(self, client, parsed_args): - attrs = {} - if parsed_args.ingress_firewall_policy: - attrs['ingress_firewall_policy_id'] = None - if parsed_args.egress_firewall_policy: - attrs['egress_firewall_policy_id'] = None - if parsed_args.share: - attrs['shared'] = False - if parsed_args.enable: - attrs['admin_state_up'] = False - if parsed_args.port: - old = client.find_firewall_group( - parsed_args.firewall_group, ignore_missing=False)['ports'] - new = [client.find_port(r)['id'] for r in parsed_args.port] - attrs['ports'] = sorted(list(set(old) - set(new))) - if parsed_args.all_port: - attrs['ports'] = [] - return attrs - - def take_action(self, parsed_args): - client = self.app.client_manager.network - fwg_id = client.find_firewall_group( - parsed_args.firewall_group, ignore_missing=False)['id'] - attrs = self._get_attrs(client, parsed_args) - try: - client.update_firewall_group(fwg_id, **attrs) - except Exception as e: - msg = (_("Failed to unset firewall group '%(group)s': %(e)s") - % {'group': parsed_args.firewall_group, 'e': e}) - raise exceptions.CommandError(msg) diff --git a/neutronclient/osc/v2/fwaas/firewallpolicy.py b/neutronclient/osc/v2/fwaas/firewallpolicy.py deleted file mode 100644 index cb2030470..000000000 --- a/neutronclient/osc/v2/fwaas/firewallpolicy.py +++ /dev/null @@ -1,430 +0,0 @@ -# Copyright 2016-2017 FUJITSU LIMITED -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import logging - -from osc_lib.command import command -from osc_lib import exceptions -from osc_lib import utils -from osc_lib.utils import columns as column_util - -from neutronclient._i18n import _ -from neutronclient.osc import utils as osc_utils -from neutronclient.osc.v2.fwaas import constants as const - - -LOG = logging.getLogger(__name__) - -_formatters = {} - - -_attr_map_dict = { - 'id': 'ID', - 'name': 'Name', - 'description': 'Description', - 'firewall_rules': 'Firewall Rules', - 'audited': 'Audited', - 'shared': 'Shared', - 'tenant_id': 'Project', - 'project_id': 'Project', -} - -_attr_map = ( - ('id', 'ID', column_util.LIST_BOTH), - ('name', 'Name', column_util.LIST_BOTH), - ('firewall_rules', 'Firewall Rules', column_util.LIST_BOTH), - ('description', 'Description', column_util.LIST_LONG_ONLY), - ('audited', 'Audited', column_util.LIST_LONG_ONLY), - ('shared', 'Shared', column_util.LIST_LONG_ONLY), - ('tenant_id', 'Project', column_util.LIST_LONG_ONLY), -) - - -def _get_common_attrs(client_manager, parsed_args, is_create=True): - attrs = {} - client = client_manager.network - - if is_create: - if 'project' in parsed_args and parsed_args.project is not None: - attrs['tenant_id'] = osc_utils.find_project( - client_manager.identity, - parsed_args.project, - parsed_args.project_domain, - ).id - if parsed_args.firewall_rule and parsed_args.no_firewall_rule: - _firewall_rules = [] - for f in parsed_args.firewall_rule: - _firewall_rules.append( - client.find_firewall_rule(f, ignore_missing=False)['id']) - attrs[const.FWRS] = _firewall_rules - elif parsed_args.firewall_rule: - rules = [] - if not is_create: - foobar = client.find_firewall_policy( - parsed_args.firewall_policy, ignore_missing=False) - rules += foobar[const.FWRS] - for f in parsed_args.firewall_rule: - rules.append( - client.find_firewall_rule(f, ignore_missing=False)['id']) - attrs[const.FWRS] = rules - elif parsed_args.no_firewall_rule: - attrs[const.FWRS] = [] - if parsed_args.audited: - attrs['audited'] = True - if parsed_args.no_audited: - attrs['audited'] = False - if parsed_args.name: - attrs['name'] = str(parsed_args.name) - if parsed_args.description: - attrs['description'] = str(parsed_args.description) - if parsed_args.share: - attrs['shared'] = True - if parsed_args.no_share: - attrs['shared'] = False - return attrs - - -def _get_common_parser(parser): - parser.add_argument( - '--description', - help=_('Description of the firewall policy')) - audited_group = parser.add_mutually_exclusive_group() - audited_group.add_argument( - '--audited', - action='store_true', - help=_('Enable auditing for the policy')) - audited_group.add_argument( - '--no-audited', - action='store_true', - help=_('Disable auditing for the policy')) - shared_group = parser.add_mutually_exclusive_group() - shared_group.add_argument( - '--share', - action='store_true', - help=_('Share the firewall policy to be used in all projects ' - '(by default, it is restricted to be used by the ' - 'current project).')) - shared_group.add_argument( - '--no-share', - action='store_true', - help=_('Restrict use of the firewall policy to the ' - 'current project')) - return parser - - -class CreateFirewallPolicy(command.ShowOne): - _description = _("Create a new firewall policy") - - def get_parser(self, prog_name): - parser = super(CreateFirewallPolicy, self).get_parser(prog_name) - _get_common_parser(parser) - osc_utils.add_project_owner_option_to_parser(parser) - parser.add_argument( - 'name', - metavar='', - help=_('Name for the firewall policy')) - fwr_group = parser.add_mutually_exclusive_group() - fwr_group.add_argument( - '--firewall-rule', - action='append', - metavar='', - help=_('Firewall rule(s) to apply (name or ID)')) - fwr_group.add_argument( - '--no-firewall-rule', - action='store_true', - help=_('Unset all firewall rules from firewall policy')) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - attrs = _get_common_attrs(self.app.client_manager, parsed_args) - obj = client.create_firewall_policy(**attrs) - display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( - obj, _attr_map_dict, ['location', 'tenant_id']) - data = utils.get_dict_properties(obj, columns, formatters=_formatters) - return (display_columns, data) - - -class DeleteFirewallPolicy(command.Command): - _description = _("Delete firewall policy(s)") - - def get_parser(self, prog_name): - parser = super(DeleteFirewallPolicy, self).get_parser(prog_name) - parser.add_argument( - const.FWP, - metavar='', - nargs='+', - help=_('Firewall policy(s) to delete (name or ID)')) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - result = 0 - for fwp in parsed_args.firewall_policy: - try: - fwp_id = client.find_firewall_policy( - fwp, ignore_missing=False)['id'] - client.delete_firewall_policy(fwp_id) - except Exception as e: - result += 1 - LOG.error(_("Failed to delete Firewall policy with " - "name or ID '%(firewall_policy)s': %(e)s"), - {const.FWP: fwp, 'e': e}) - - if result > 0: - total = len(parsed_args.firewall_policy) - msg = (_("%(result)s of %(total)s firewall policy(s) " - "failed to delete.") % {'result': result, 'total': total}) - raise exceptions.CommandError(msg) - - -class FirewallPolicyInsertRule(command.Command): - _description = _("Insert a rule into a given firewall policy") - - def get_parser(self, prog_name): - parser = super(FirewallPolicyInsertRule, self).get_parser(prog_name) - parser.add_argument( - const.FWP, - metavar='', - help=_('Firewall policy to insert rule (name or ID)')) - parser.add_argument( - '--insert-before', - metavar='', - help=_('Insert the new rule before this existing rule ' - '(name or ID)')) - parser.add_argument( - '--insert-after', - metavar='', - help=_('Insert the new rule after this existing rule ' - '(name or ID)')) - parser.add_argument( - const.FWR, - metavar='', - help=_('Firewall rule to be inserted (name or ID)')) - return parser - - def args2body(self, parsed_args): - client = self.app.client_manager.network - _rule_id = _get_required_firewall_rule(client, parsed_args) - _insert_before = '' - if 'insert_before' in parsed_args: - if parsed_args.insert_before: - _insert_before = client.find_firewall_rule( - parsed_args.insert_before, ignore_missing=False)['id'] - _insert_after = '' - if 'insert_after' in parsed_args: - if parsed_args.insert_after: - _insert_after = client.find_firewall_rule( - parsed_args.insert_after, ignore_missing=False)['id'] - return {'firewall_rule_id': _rule_id, - 'insert_before': _insert_before, - 'insert_after': _insert_after} - - def take_action(self, parsed_args): - client = self.app.client_manager.network - policy_id = client.find_firewall_policy( - parsed_args.firewall_policy, ignore_missing=False)['id'] - body = self.args2body(parsed_args) - client.insert_rule_into_policy(policy_id, **body) - rule_id = body['firewall_rule_id'] - policy = parsed_args.firewall_policy - print((_('Inserted firewall rule %(rule)s in firewall policy ' - '%(policy)s') % {'rule': rule_id, 'policy': policy}), - file=self.app.stdout) - - -class FirewallPolicyRemoveRule(command.Command): - _description = _("Remove a rule from a given firewall policy") - - def get_parser(self, prog_name): - parser = super(FirewallPolicyRemoveRule, self).get_parser(prog_name) - parser.add_argument( - const.FWP, - metavar='', - help=_('Firewall policy to remove rule (name or ID)')) - parser.add_argument( - const.FWR, - metavar='', - help=_('Firewall rule to remove from policy (name or ID)')) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - policy_id = client.find_firewall_policy( - parsed_args.firewall_policy, ignore_missing=False)['id'] - fwr_id = _get_required_firewall_rule(client, parsed_args) - body = {'firewall_rule_id': fwr_id} - client.remove_rule_from_policy(policy_id, **body) - rule_id = body['firewall_rule_id'] - policy = parsed_args.firewall_policy - print((_('Removed firewall rule %(rule)s from firewall policy ' - '%(policy)s') % {'rule': rule_id, 'policy': policy}), - file=self.app.stdout) - - -class ListFirewallPolicy(command.Lister): - _description = _("List firewall policies") - - def get_parser(self, prog_name): - parser = super(ListFirewallPolicy, self).get_parser(prog_name) - parser.add_argument( - '--long', - action='store_true', - default=False, - help=_("List additional fields in output") - ) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - obj = client.firewall_policies() - headers, columns = column_util.get_column_definitions( - _attr_map, long_listing=parsed_args.long) - return (headers, (utils.get_dict_properties( - s, columns, formatters=_formatters) for s in obj)) - - -class SetFirewallPolicy(command.Command): - _description = _("Set firewall policy properties") - - def get_parser(self, prog_name): - parser = super(SetFirewallPolicy, self).get_parser(prog_name) - _get_common_parser(parser) - parser.add_argument( - const.FWP, - metavar='', - help=_('Firewall policy to update (name or ID)')) - parser.add_argument( - '--name', - metavar='', - help=_('Name for the firewall policy')) - parser.add_argument( - '--firewall-rule', - action='append', - metavar='', - help=_('Firewall rule(s) to apply (name or ID)')) - parser.add_argument( - '--no-firewall-rule', - action='store_true', - help=_('Remove all firewall rules from firewall policy')) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - fwp_id = client.find_firewall_policy( - parsed_args.firewall_policy, ignore_missing=False)['id'] - attrs = _get_common_attrs(self.app.client_manager, - parsed_args, is_create=False) - try: - client.update_firewall_policy(fwp_id, **attrs) - except Exception as e: - msg = (_("Failed to set firewall policy '%(policy)s': %(e)s") - % {'policy': parsed_args.firewall_policy, 'e': e}) - raise exceptions.CommandError(msg) - - -class ShowFirewallPolicy(command.ShowOne): - _description = _("Display firewall policy details") - - def get_parser(self, prog_name): - parser = super(ShowFirewallPolicy, self).get_parser(prog_name) - parser.add_argument( - const.FWP, - metavar='', - help=_('Firewall policy to show (name or ID)')) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - fwp_id = client.find_firewall_policy( - parsed_args.firewall_policy, ignore_missing=False)['id'] - obj = client.get_firewall_policy(fwp_id) - display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( - obj, _attr_map_dict, ['location', 'tenant_id']) - data = utils.get_dict_properties(obj, columns, formatters=_formatters) - return (display_columns, data) - - -def _get_required_firewall_rule(client, parsed_args): - if not parsed_args.firewall_rule: - msg = (_("Firewall rule (name or ID) is required.")) - raise exceptions.CommandError(msg) - return client.find_firewall_rule( - parsed_args.firewall_rule, ignore_missing=False)['id'] - - -class UnsetFirewallPolicy(command.Command): - _description = _("Unset firewall policy properties") - - def get_parser(self, prog_name): - parser = super(UnsetFirewallPolicy, self).get_parser(prog_name) - parser.add_argument( - const.FWP, - metavar='', - help=_('Firewall policy to unset (name or ID)')) - firewall_rule_group = parser.add_mutually_exclusive_group() - firewall_rule_group.add_argument( - '--firewall-rule', - action='append', - metavar='', - help=_('Remove firewall rule(s) from the firewall policy ' - '(name or ID)')) - firewall_rule_group.add_argument( - '--all-firewall-rule', - action='store_true', - help=_('Remove all firewall rules from the firewall policy')) - parser.add_argument( - '--audited', - action='store_true', - help=_('Disable auditing for the policy')) - parser.add_argument( - '--share', - action='store_true', - help=_('Restrict use of the firewall policy to the ' - 'current project')) - return parser - - def _get_attrs(self, client_manager, parsed_args): - attrs = {} - client = client_manager.network - - if parsed_args.firewall_rule: - current = client.find_firewall_policy( - parsed_args.firewall_policy, ignore_missing=False)[const.FWRS] - removed = [] - for f in set(parsed_args.firewall_rule): - removed.append( - client.find_firewall_rule(f, ignore_missing=False)['id']) - attrs[const.FWRS] = [r for r in current if r not in removed] - if parsed_args.all_firewall_rule: - attrs[const.FWRS] = [] - if parsed_args.audited: - attrs['audited'] = False - if parsed_args.share: - attrs['shared'] = False - return attrs - - def take_action(self, parsed_args): - client = self.app.client_manager.network - fwp_id = client.find_firewall_policy( - parsed_args.firewall_policy, ignore_missing=False)['id'] - attrs = self._get_attrs(self.app.client_manager, parsed_args) - try: - client.update_firewall_policy(fwp_id, **attrs) - except Exception as e: - msg = (_("Failed to unset firewall policy '%(policy)s': %(e)s") - % {'policy': parsed_args.firewall_policy, 'e': e}) - raise exceptions.CommandError(msg) diff --git a/neutronclient/osc/v2/fwaas/firewallrule.py b/neutronclient/osc/v2/fwaas/firewallrule.py deleted file mode 100644 index 552558d71..000000000 --- a/neutronclient/osc/v2/fwaas/firewallrule.py +++ /dev/null @@ -1,474 +0,0 @@ -# Copyright 2016-2017 FUJITSU LIMITED -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -import logging - -from cliff import columns as cliff_columns -from osc_lib.command import command -from osc_lib import exceptions -from osc_lib import utils -from osc_lib.utils import columns as column_util - -from neutronclient._i18n import _ -from neutronclient.common import utils as nc_utils -from neutronclient.osc import utils as osc_utils -from neutronclient.osc.v2.fwaas import constants as const - - -LOG = logging.getLogger(__name__) - - -_attr_map_dict = { - 'id': 'ID', - 'name': 'Name', - 'enabled': 'Enabled', - 'summary': 'Summary', - 'description': 'Description', - 'firewall_policy_id': 'Firewall Policy', - 'ip_version': 'IP Version', - 'action': 'Action', - 'protocol': 'Protocol', - 'source_ip_address': 'Source IP Address', - 'source_port': 'Source Port', - 'destination_ip_address': 'Destination IP Address', - 'destination_port': 'Destination Port', - 'shared': 'Shared', - 'source_firewall_group_id': 'Source Firewall Group ID', - 'destination_firewall_group_id': 'Destination Firewall Group ID', - 'tenant_id': 'Project', - 'project_id': 'Project', -} - -_attr_map = ( - ('id', 'ID', column_util.LIST_BOTH), - ('name', 'Name', column_util.LIST_BOTH), - ('enabled', 'Enabled', column_util.LIST_BOTH), - ('summary', 'Summary', column_util.LIST_SHORT_ONLY), - ('description', 'Description', column_util.LIST_LONG_ONLY), - ('firewall_policy_id', 'Firewall Policy', column_util.LIST_BOTH), - ('ip_version', 'IP Version', column_util.LIST_LONG_ONLY), - ('action', 'Action', column_util.LIST_LONG_ONLY), - ('protocol', 'Protocol', column_util.LIST_LONG_ONLY), - ('source_ip_address', 'Source IP Address', column_util.LIST_LONG_ONLY), - ('source_port', 'Source Port', column_util.LIST_LONG_ONLY), - ('destination_ip_address', 'Destination IP Address', - column_util.LIST_LONG_ONLY), - ('destination_port', 'Destination Port', column_util.LIST_LONG_ONLY), - ('shared', 'Shared', column_util.LIST_LONG_ONLY), - ('tenant_id', 'Project', column_util.LIST_LONG_ONLY), - ('source_firewall_group_id', 'Source Firewall Group ID', - column_util.LIST_LONG_ONLY), - ('destination_firewall_group_id', 'Destination Firewall Group ID', - column_util.LIST_LONG_ONLY), -) - - -def _get_common_parser(parser): - parser.add_argument( - '--name', - metavar='', - help=_('Name of the firewall rule')) - parser.add_argument( - '--description', - metavar='', - help=_('Description of the firewall rule')) - parser.add_argument( - '--protocol', - type=nc_utils.convert_to_lowercase, - help=_('IP protocol (ah, dccp, egp, esp, gre, icmp, igmp, ' - 'ipv6-encap, ipv6-frag, ipv6-icmp, ipv6-nonxt, ipv6-opts, ' - 'ipv6-route, ospf, pgm, rsvp, sctp, tcp, udp, udplite, ' - 'vrrp and integer representations [0-255] or any; ' - 'default: any (all protocols))')) - parser.add_argument( - '--action', - choices=['allow', 'deny', 'reject'], - type=nc_utils.convert_to_lowercase, - help=_('Action for the firewall rule')) - parser.add_argument( - '--ip-version', - metavar='', - choices=['4', '6'], - help=_('Set IP version 4 or 6 (default is 4)')) - src_ip_group = parser.add_mutually_exclusive_group() - src_ip_group.add_argument( - '--source-ip-address', - metavar='', - help=_('Source IP address or subnet')) - src_ip_group.add_argument( - '--no-source-ip-address', - action='store_true', - help=_('Detach source IP address')) - dst_ip_group = parser.add_mutually_exclusive_group() - dst_ip_group.add_argument( - '--destination-ip-address', - metavar='', - help=_('Destination IP address or subnet')) - dst_ip_group.add_argument( - '--no-destination-ip-address', - action='store_true', - help=_('Detach destination IP address')) - src_port_group = parser.add_mutually_exclusive_group() - src_port_group.add_argument( - '--source-port', - metavar='', - help=_('Source port number or range' - '(integer in [1, 65535] or range like 123:456)')) - src_port_group.add_argument( - '--no-source-port', - action='store_true', - help=_('Detach source port number or range')) - dst_port_group = parser.add_mutually_exclusive_group() - dst_port_group.add_argument( - '--destination-port', - metavar='', - help=_('Destination port number or range' - '(integer in [1, 65535] or range like 123:456)')) - dst_port_group.add_argument( - '--no-destination-port', - action='store_true', - help=_('Detach destination port number or range')) - shared_group = parser.add_mutually_exclusive_group() - shared_group.add_argument( - '--share', - action='store_true', - help=_('Share the firewall rule to be used in all projects ' - '(by default, it is restricted to be used by the ' - 'current project).')) - shared_group.add_argument( - '--no-share', - action='store_true', - help=_('Restrict use of the firewall rule to the current project')) - enable_group = parser.add_mutually_exclusive_group() - enable_group.add_argument( - '--enable-rule', - action='store_true', - help=_('Enable this rule (default is enabled)')) - enable_group.add_argument( - '--disable-rule', - action='store_true', - help=_('Disable this rule')) - src_fwg_group = parser.add_mutually_exclusive_group() - src_fwg_group.add_argument( - '--source-firewall-group', - metavar='', - help=_('Source firewall group (name or ID)')) - src_fwg_group.add_argument( - '--no-source-firewall-group', - action='store_true', - help=_('No associated destination firewall group')) - dst_fwg_group = parser.add_mutually_exclusive_group() - dst_fwg_group.add_argument( - '--destination-firewall-group', - metavar='', - help=_('Destination firewall group (name or ID)')) - dst_fwg_group.add_argument( - '--no-destination-firewall-group', - action='store_true', - help=_('No associated destination firewall group')) - return parser - - -def _get_common_attrs(client_manager, parsed_args, is_create=True): - attrs = {} - client = client_manager.network - if is_create: - if 'project' in parsed_args and parsed_args.project is not None: - attrs['tenant_id'] = osc_utils.find_project( - client_manager.identity, - parsed_args.project, - parsed_args.project_domain, - ).id - if parsed_args.name: - attrs['name'] = str(parsed_args.name) - if parsed_args.description: - attrs['description'] = str(parsed_args.description) - if parsed_args.protocol: - protocol = parsed_args.protocol - attrs['protocol'] = None if protocol == 'any' else protocol - if parsed_args.action: - attrs['action'] = parsed_args.action - if parsed_args.ip_version: - attrs['ip_version'] = str(parsed_args.ip_version) - if parsed_args.source_port: - attrs['source_port'] = parsed_args.source_port - if parsed_args.no_source_port: - attrs['source_port'] = None - if parsed_args.source_ip_address: - attrs['source_ip_address'] = parsed_args.source_ip_address - if parsed_args.no_source_ip_address: - attrs['source_ip_address'] = None - if parsed_args.destination_port: - attrs['destination_port'] = str(parsed_args.destination_port) - if parsed_args.no_destination_port: - attrs['destination_port'] = None - if parsed_args.destination_ip_address: - attrs['destination_ip_address'] = str( - parsed_args.destination_ip_address) - if parsed_args.no_destination_ip_address: - attrs['destination_ip_address'] = None - if parsed_args.enable_rule: - attrs['enabled'] = True - if parsed_args.disable_rule: - attrs['enabled'] = False - if parsed_args.share: - attrs['shared'] = True - if parsed_args.no_share: - attrs['shared'] = False - if parsed_args.source_firewall_group: - attrs['source_firewall_group_id'] = client.find_firewall_group( - parsed_args.source_firewall_group, ignore_missing=False)['id'] - if parsed_args.no_source_firewall_group: - attrs['source_firewall_group_id'] = None - if parsed_args.destination_firewall_group: - attrs['destination_firewall_group_id'] = client.find_firewall_group( - parsed_args.destination_firewall_group, ignore_missing=False)['id'] - if parsed_args.no_destination_firewall_group: - attrs['destination_firewall_group_id'] = None - return attrs - - -class ProtocolColumn(cliff_columns.FormattableColumn): - def human_readable(self): - return self._value if self._value else 'any' - - -_formatters = {'protocol': ProtocolColumn} - - -class CreateFirewallRule(command.ShowOne): - _description = _("Create a new firewall rule") - - def get_parser(self, prog_name): - parser = super(CreateFirewallRule, self).get_parser(prog_name) - _get_common_parser(parser) - osc_utils.add_project_owner_option_to_parser(parser) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - attrs = _get_common_attrs(self.app.client_manager, parsed_args) - obj = client.create_firewall_rule(**attrs) - display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( - obj, _attr_map_dict, ['location', 'tenant_id']) - data = utils.get_dict_properties(obj, columns, formatters=_formatters) - return display_columns, data - - -class DeleteFirewallRule(command.Command): - _description = _("Delete firewall rule(s)") - - def get_parser(self, prog_name): - parser = super(DeleteFirewallRule, self).get_parser(prog_name) - parser.add_argument( - const.FWR, - metavar='', - nargs='+', - help=_('Firewall rule(s) to delete (name or ID)')) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - result = 0 - for fwr in parsed_args.firewall_rule: - try: - fwr_id = client.find_firewall_rule( - fwr, ignore_missing=False)['id'] - client.delete_firewall_rule(fwr_id) - except Exception as e: - result += 1 - LOG.error(_("Failed to delete Firewall rule with " - "name or ID '%(firewall_rule)s': %(e)s"), - {const.FWR: fwr, 'e': e}) - - if result > 0: - total = len(parsed_args.firewall_rule) - msg = (_("%(result)s of %(total)s firewall rule(s) failed " - "to delete.") % {'result': result, 'total': total}) - raise exceptions.CommandError(msg) - - -class ListFirewallRule(command.Lister): - _description = _("List firewall rules that belong to a given tenant") - - def get_parser(self, prog_name): - parser = super(ListFirewallRule, self).get_parser(prog_name) - parser.add_argument( - '--long', - action='store_true', - default=False, - help=_("List additional fields in output") - ) - return parser - - def extend_list(self, data, parsed_args): - ext_data = [] - for d in data: - protocol = d['protocol'].upper() if d['protocol'] else 'ANY' - src_ip = 'none specified' - dst_ip = 'none specified' - src_port = '(none specified)' - dst_port = '(none specified)' - if 'source_ip_address' in d and d['source_ip_address']: - src_ip = str(d['source_ip_address']).lower() - if 'source_port' in d and d['source_port']: - src_port = '(' + str(d['source_port']).lower() + ')' - if 'destination_ip_address' in d and d['destination_ip_address']: - dst_ip = str(d['destination_ip_address']).lower() - if 'destination_port' in d and d['destination_port']: - dst_port = '(' + str(d['destination_port']).lower() + ')' - action = d['action'] if d.get('action') else 'no-action' - src = 'source(port): ' + src_ip + src_port - dst = 'dest(port): ' + dst_ip + dst_port - d['summary'] = ',\n '.join([protocol, src, dst, action]) - ext_data.append(d) - return ext_data - - def take_action(self, parsed_args): - client = self.app.client_manager.network - obj = client.firewall_rules() - obj_extend = self.extend_list(obj, parsed_args) - headers, columns = column_util.get_column_definitions( - _attr_map, long_listing=parsed_args.long) - return (headers, (utils.get_dict_properties( - s, columns, formatters=_formatters) for s in obj_extend)) - - -class SetFirewallRule(command.Command): - _description = _("Set firewall rule properties") - - def get_parser(self, prog_name): - parser = super(SetFirewallRule, self).get_parser(prog_name) - _get_common_parser(parser) - parser.add_argument( - const.FWR, - metavar='', - help=_('Firewall rule to set (name or ID)')) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - attrs = _get_common_attrs(self.app.client_manager, - parsed_args, is_create=False) - fwr_id = client.find_firewall_rule( - parsed_args.firewall_rule, ignore_missing=False)['id'] - try: - client.update_firewall_rule(fwr_id, **attrs) - except Exception as e: - msg = (_("Failed to set firewall rule '%(rule)s': %(e)s") - % {'rule': parsed_args.firewall_rule, 'e': e}) - raise exceptions.CommandError(msg) - - -class ShowFirewallRule(command.ShowOne): - _description = _("Display firewall rule details") - - def get_parser(self, prog_name): - parser = super(ShowFirewallRule, self).get_parser(prog_name) - parser.add_argument( - const.FWR, - metavar='', - help=_('Firewall rule to display (name or ID)')) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - fwr_id = client.find_firewall_rule( - parsed_args.firewall_rule, ignore_missing=False)['id'] - obj = client.get_firewall_rule(fwr_id) - display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( - obj, _attr_map_dict, ['location', 'tenant_id']) - data = utils.get_dict_properties(obj, columns, formatters=_formatters) - return (display_columns, data) - - -class UnsetFirewallRule(command.Command): - _description = _("Unset firewall rule properties") - - def get_parser(self, prog_name): - parser = super(UnsetFirewallRule, self).get_parser(prog_name) - parser.add_argument( - const.FWR, - metavar='', - help=_('Firewall rule to unset (name or ID)')) - parser.add_argument( - '--source-ip-address', - action='store_true', - help=_('Source IP address or subnet')) - parser.add_argument( - '--destination-ip-address', - action='store_true', - help=_('Destination IP address or subnet')) - parser.add_argument( - '--source-port', - action='store_true', - help=_('Source port number or range' - '(integer in [1, 65535] or range like 123:456)')) - parser.add_argument( - '--destination-port', - action='store_true', - help=_('Destination port number or range' - '(integer in [1, 65535] or range like 123:456)')) - parser.add_argument( - '--share', - action='store_true', - help=_('Restrict use of the firewall rule to the current project')) - parser.add_argument( - '--enable-rule', - action='store_true', - help=_('Disable this rule')) - - parser.add_argument( - '--source-firewall-group', - action='store_true', - help=_('Source firewall group (name or ID)')) - - parser.add_argument( - '--destination-firewall-group', - action='store_true', - help=_('Destination firewall group (name or ID)')) - return parser - - def _get_attrs(self, client_manager, parsed_args): - attrs = {} - if parsed_args.source_ip_address: - attrs['source_ip_address'] = None - if parsed_args.source_port: - attrs['source_port'] = None - if parsed_args.destination_ip_address: - attrs['destination_ip_address'] = None - if parsed_args.destination_port: - attrs['destination_port'] = None - if parsed_args.share: - attrs['shared'] = False - if parsed_args.enable_rule: - attrs['enabled'] = False - if parsed_args.source_firewall_group: - attrs['source_firewall_group_id'] = None - if parsed_args.source_firewall_group: - attrs['destination_firewall_group_id'] = None - return attrs - - def take_action(self, parsed_args): - client = self.app.client_manager.network - attrs = self._get_attrs(self.app.client_manager, parsed_args) - fwr_id = client.find_firewall_rule( - parsed_args.firewall_rule, ignore_missing=False)['id'] - try: - client.update_firewall_rule(fwr_id, **attrs) - except Exception as e: - msg = (_("Failed to unset firewall rule '%(rule)s': %(e)s") - % {'rule': parsed_args.firewall_rule, 'e': e}) - raise exceptions.CommandError(msg) diff --git a/neutronclient/tests/unit/osc/v2/fwaas/__init__.py b/neutronclient/tests/unit/osc/v2/fwaas/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutronclient/tests/unit/osc/v2/fwaas/common.py b/neutronclient/tests/unit/osc/v2/fwaas/common.py deleted file mode 100644 index ae0d97fbb..000000000 --- a/neutronclient/tests/unit/osc/v2/fwaas/common.py +++ /dev/null @@ -1,304 +0,0 @@ -# Copyright 2016-2017 FUJITSU LIMITED -# 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 testtools - -from osc_lib import exceptions -from osc_lib.tests import utils - -from neutronclient.tests.unit.osc.v2 import fakes as test_fakes - - -class TestListFWaaS(test_fakes.TestNeutronClientOSCV2): - - def test_list_with_no_option(self): - arglist = [] - verifylist = [] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - headers, data = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with() - self.assertEqual(list(self.list_headers), headers) - self.assertEqual([self.list_data], list(data)) - - def test_list_with_long_option(self): - arglist = ['--long'] - verifylist = [('long', True)] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - headers, data = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with() - self.assertEqual(list(self.headers), headers) - - -class TestShowFWaaS(test_fakes.TestNeutronClientOSCV2): - - def test_show_filtered_by_id_or_name(self): - target = self.resource['id'] - headers, data = None, None - - def _mock_fwaas(*args, **kwargs): - return {'id': args[0]} - - self.networkclient.find_firewall_policy.side_effect = _mock_fwaas - self.networkclient.find_firewall_group.side_effect = _mock_fwaas - self.networkclient.find_firewall_rule.side_effect = _mock_fwaas - - arglist = [target] - verifylist = [(self.res, target)] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - headers, data = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with(target) - self.assertEqual(self.ordered_headers, headers) - - -class TestCreateFWaaS(test_fakes.TestNeutronClientOSCV2): - pass - - -class TestSetFWaaS(test_fakes.TestNeutronClientOSCV2): - - def test_set_name(self): - target = self.resource['id'] - update = 'change' - arglist = [target, '--name', update] - verifylist = [ - (self.res, target), - ('name', update), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with(target, **{'name': update}) - self.assertIsNone(result) - - def test_set_description(self): - target = self.resource['id'] - update = 'change-desc' - arglist = [target, '--description', update] - verifylist = [ - (self.res, target), - ('description', update), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with(target, **{'description': update}) - self.assertIsNone(result) - - def test_set_shared(self): - target = self.resource['id'] - arglist = [target, '--share'] - verifylist = [ - (self.res, target), - ('share', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with(target, **{'shared': True}) - self.assertIsNone(result) - - def test_set_duplicate_shared(self): - target = self.resource['id'] - arglist = [target, '--share', '--share'] - verifylist = [ - (self.res, target), - ('share', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with(target, **{'shared': True}) - self.assertIsNone(result) - - def test_set_no_share(self): - target = self.resource['id'] - arglist = [target, '--no-share'] - verifylist = [ - (self.res, target), - ('share', False), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with(target, **{'shared': False}) - self.assertIsNone(result) - - def test_set_duplicate_no_share(self): - target = self.resource['id'] - arglist = [target, '--no-share', '--no-share'] - verifylist = [ - (self.res, target), - ('no_share', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with(target, **{'shared': False}) - self.assertIsNone(result) - - def test_set_no_share_and_shared(self): - target = self.resource['id'] - arglist = [target, '--no-share', '--share'] - verifylist = [ - (self.res, target), - ('no_share', True), - ('share', True), - ] - self.assertRaises( - utils.ParserException, - self.check_parser, self.cmd, arglist, verifylist) - - def test_set_shared_and_no_share(self): - target = self.resource['id'] - arglist = [target, '--share', '--no_share'] - verifylist = [ - (self.res, target), - ('share', True), - ('no_share', True), - ] - self.assertRaises( - utils.ParserException, - self.check_parser, self.cmd, arglist, verifylist) - - def test_set_project(self): - target = self.resource['id'] - project_id = 'b14ce3b699594d13819a859480286489' - arglist = [target, '--project', project_id] - verifylist = [ - (self.res, target), - ('tenant_id', project_id), - ] - self.assertRaises( - utils.ParserException, - self.check_parser, self.cmd, arglist, verifylist) - - def test_set_project_domain(self): - target = self.resource['id'] - project_domain = 'mydomain.com' - arglist = [target, '--project-domain', project_domain] - verifylist = [ - (self.res, target), - ('project_domain', project_domain), - ] - self.assertRaises( - utils.ParserException, - self.check_parser, self.cmd, arglist, verifylist) - - -class TestDeleteFWaaS(test_fakes.TestNeutronClientOSCV2): - - def test_delete_with_one_resource(self): - target = self.resource['id'] - - def _mock_fwaas(*args, **kwargs): - return {'id': args[0]} - - self.networkclient.find_firewall_group.side_effect = _mock_fwaas - self.networkclient.find_firewall_policy.side_effect = _mock_fwaas - self.networkclient.find_firewall_rule.side_effect = _mock_fwaas - - arglist = [target] - verifylist = [(self.res, [target])] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with(target) - self.assertIsNone(result) - - def test_delete_with_multiple_resources(self): - - def _mock_fwaas(*args, **kwargs): - return {'id': args[0]} - - self.networkclient.find_firewall_group.side_effect = _mock_fwaas - self.networkclient.find_firewall_policy.side_effect = _mock_fwaas - self.networkclient.find_firewall_rule.side_effect = _mock_fwaas - - target1 = 'target1' - target2 = 'target2' - arglist = [target1, target2] - verifylist = [(self.res, [target1, target2])] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - self.assertIsNone(result) - - self.assertEqual(2, self.mocked.call_count) - for idx, reference in enumerate([target1, target2]): - actual = ''.join(self.mocked.call_args_list[idx][0][0]) - self.assertEqual(reference, actual) - - def test_delete_multiple_with_exception(self): - target1 = 'target' - arglist = [target1] - verifylist = [(self.res, [target1])] - - self.networkclient.find_firewall_group.side_effect = [ - target1, exceptions.CommandError - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - resource_name = self.res.replace('_', ' ') - msg = "1 of 2 %s(s) failed to delete." % resource_name - with testtools.ExpectedException(exceptions.CommandError) as e: - self.cmd.take_action(parsed_args) - self.assertEqual(msg, str(e)) - - -class TestUnsetFWaaS(test_fakes.TestNeutronClientOSCV2): - - def test_unset_shared(self): - target = self.resource['id'] - arglist = [ - target, - '--share', - ] - verifylist = [ - (self.res, target), - ('share', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - self.mocked.assert_called_once_with(target, **{'shared': False}) - self.assertIsNone(result) - - def test_set_shared_and_no_shared(self): - target = self.resource['id'] - arglist = [target, '--share', '--no-share'] - verifylist = [ - (self.res, target), - ('share', True), - ('no_share', True), - ] - # check_parser: error: unrecognized arguments: --no-share - self.assertRaises( - utils.ParserException, - self.check_parser, self.cmd, arglist, verifylist) - - def test_set_duplicate_shared(self): - target = self.resource['id'] - arglist = [target, '--share', '--share'] - verifylist = [ - (self.res, target), - ('share', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with(target, **{'shared': False}) - self.assertIsNone(result) diff --git a/neutronclient/tests/unit/osc/v2/fwaas/fakes.py b/neutronclient/tests/unit/osc/v2/fwaas/fakes.py deleted file mode 100644 index 56d09bffc..000000000 --- a/neutronclient/tests/unit/osc/v2/fwaas/fakes.py +++ /dev/null @@ -1,151 +0,0 @@ -# Copyright 2016 FUJITSU LIMITED -# 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 collections -from unittest import mock - -from openstack.network.v2 import firewall_group as fw_group -from openstack.network.v2 import firewall_policy as fw_policy -from openstack.network.v2 import firewall_rule as fw_rule -from oslo_utils import uuidutils - - -class FakeFWaaS(object): - - def create(self, attrs={}): - """Create a fake fwaas resources - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A OrderedDict faking the fwaas resource - """ - self.ordered.update(attrs) - if 'FirewallGroup' == self.__class__.__name__: - return fw_group.FirewallGroup(**self.ordered) - if 'FirewallPolicy' == self.__class__.__name__: - return fw_policy.FirewallPolicy(**self.ordered) - if 'FirewallRule' == self.__class__.__name__: - fw_r = fw_rule.FirewallRule(**self.ordered) - protocol = fw_r['protocol'].upper() if fw_r['protocol'] else 'ANY' - src_ip = str(fw_r['source_ip_address']).lower() - src_port = '(' + str(fw_r['source_port']).lower() + ')' - dst_ip = str(fw_r['destination_ip_address']).lower() - dst_port = '(' + str(fw_r['destination_port']).lower() + ')' - src = 'source(port): ' + src_ip + src_port - dst = 'dest(port): ' + dst_ip + dst_port - action = fw_r['action'] if fw_r.get('action') else 'no-action' - fw_r['summary'] = ',\n '.join([protocol, src, dst, action]) - return fw_r - - def bulk_create(self, attrs=None, count=2): - """Create multiple fake fwaas resources - - :param Dictionary attrs: - A dictionary with all attributes - :param int count: - The number of fwaas resources to fake - :return: - A list of dictionaries faking the fwaas resources - """ - return [self.create(attrs=attrs) for i in range(0, count)] - - def get(self, attrs=None, count=2): - """Create multiple fake fwaas resources - - :param Dictionary attrs: - A dictionary with all attributes - :param int count: - The number of fwaas resources to fake - :return: - A list of dictionaries faking the fwaas resource - """ - if attrs is None: - self.attrs = self.bulk_create(count=count) - return mock.Mock(side_effect=attrs) - - -class FirewallGroup(FakeFWaaS): - """Fake one or more firewall group""" - - def __init__(self): - super(FirewallGroup, self).__init__() - self.ordered = collections.OrderedDict(( - ('id', 'firewall-group-id-' + - uuidutils.generate_uuid(dashed=False)), - ('name', 'my-group-' + - uuidutils.generate_uuid(dashed=False)), - ('ingress_firewall_policy_id', None), - ('egress_firewall_policy_id', None), - ('description', 'my-desc-' + - uuidutils.generate_uuid(dashed=False)), - ('status', 'INACTIVE'), - ('ports', []), - ('admin_state_up', True), - ('shared', False), - ('tenant_id', 'tenant-id-' + - uuidutils.generate_uuid(dashed=False)), - )) - - -class FirewallPolicy(FakeFWaaS): - """Fake one or more firewall policy""" - - def __init__(self): - super(FirewallPolicy, self).__init__() - self.ordered = collections.OrderedDict(( - ('id', 'firewall-policy-' + - uuidutils.generate_uuid(dashed=False)), - ('name', 'my-policy-' + - uuidutils.generate_uuid(dashed=False)), - ('firewall_rules', []), - ('description', 'my-desc-' + - uuidutils.generate_uuid(dashed=False)), - ('audited', True), - ('shared', False), - ('tenant_id', 'tenant-id-' + - uuidutils.generate_uuid(dashed=False)), - )) - - -class FirewallRule(FakeFWaaS): - """Fake one or more firewall rule""" - - def __init__(self): - super(FirewallRule, self).__init__() - self.ordered = collections.OrderedDict(( - ('id', 'firewall-rule-id-' + - uuidutils.generate_uuid(dashed=False)), - ('name', 'my-rule-' + - uuidutils.generate_uuid(dashed=False)), - ('enabled', False), - ('description', 'my-desc-' + - uuidutils.generate_uuid(dashed=False)), - ('ip_version', 4), - ('action', 'deny'), - ('protocol', None), - ('source_ip_address', '192.168.1.0/24'), - ('source_port', '1:11111'), - ('destination_ip_address', '192.168.2.2'), - ('destination_port', '2:22222'), - ('shared', False), - ('tenant_id', 'tenant-id-' + - uuidutils.generate_uuid(dashed=False)), - ('source_firewall_group_id', 'firewall-group-id-' + - uuidutils.generate_uuid(dashed=False)), - ('destination_firewall_group_id', 'firewall-group-id-' + - uuidutils.generate_uuid(dashed=False)), - )) diff --git a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py deleted file mode 100644 index 8596e9522..000000000 --- a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallgroup.py +++ /dev/null @@ -1,730 +0,0 @@ -# Copyright 2016 FUJITSU LIMITED -# 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 copy -import re -from unittest import mock - -from osc_lib import exceptions -from osc_lib.tests import utils - -from neutronclient.osc import utils as osc_utils -from neutronclient.osc.v2.fwaas import firewallgroup -from neutronclient.osc.v2 import utils as v2_utils -from neutronclient.tests.unit.osc.v2 import fakes as test_fakes -from neutronclient.tests.unit.osc.v2.fwaas import common -from neutronclient.tests.unit.osc.v2.fwaas import fakes - - -_fwg = fakes.FirewallGroup().create() -CONVERT_MAP = { - 'ingress_firewall_policy': 'ingress_firewall_policy_id', - 'egress_firewall_policy': 'egress_firewall_policy_id', - 'no_ingress_firewall_policy': 'ingress_firewall_policy_id', - 'no_egress_firewall_policy': 'egress_firewall_policy_id', - 'share': 'shared', - 'no_share': 'shared', - 'project': 'tenant_id', - 'enable': 'admin_state_up', - 'disable': 'admin_state_up', - 'port': 'ports', -} - - -def _generate_response(ordered_dict=None, data=None): - source = ordered_dict if ordered_dict else _fwg - up = {'admin_state_up': - v2_utils.AdminStateColumn(source['admin_state_up'])} - if data: - up.append(data) - source.update(up) - return source - - -def _generate_req_and_res(verifylist): - request = dict(verifylist) - response = _fwg - for key, val in verifylist: - del request[key] - if re.match('^no_', key) and val is True: - new_value = None - elif key == 'enable' and val: - new_value = True - elif key == 'disable' and val: - new_value = False - elif val is True or val is False: - new_value = val - elif key in ('name', 'description'): - new_value = val - else: - new_value = val - converted = CONVERT_MAP.get(key, key) - request[converted] = new_value - response[converted] = new_value - return request, response - - -class TestFirewallGroup(test_fakes.TestNeutronClientOSCV2): - - def check_results(self, headers, data, exp_req, is_list=False): - if is_list: - req_body = {self.res_plural: list(exp_req)} - else: - req_body = exp_req - self.mocked.assert_called_once_with(**req_body) - self.assertEqual(self.ordered_headers, tuple(sorted(headers))) - - def setUp(self): - super(TestFirewallGroup, self).setUp() - - def _find_resource(*args, **kwargs): - return {'id': args[0], 'ports': _fwg['ports']} - - self.networkclient.find_firewall_group = mock.Mock( - side_effect=_find_resource) - osc_utils.find_project = mock.Mock() - osc_utils.find_project.id = _fwg['tenant_id'] - self.res = 'firewall_group' - self.res_plural = 'firewall_groups' - self.resource = _fwg - self.list_headers = ( - 'ID', - 'Name', - 'Ingress Policy ID', - 'Egress Policy ID', - ) - self.list_data = ( - _fwg['id'], - _fwg['name'], - _fwg['ingress_firewall_policy_id'], - _fwg['egress_firewall_policy_id'], - ) - self.headers = tuple(self.list_headers + ( - 'Description', - 'Status', - 'Ports', - 'State', - 'Shared', - 'Project', - )) - self.data = _generate_response() - self.ordered_headers = copy.deepcopy(tuple(sorted(self.headers))) - self.expected_data = ( - _fwg['description'], - _fwg['egress_firewall_policy_id'], - _fwg['id'], - _fwg['ingress_firewall_policy_id'], - _fwg['name'], - _fwg['ports'], - _fwg['tenant_id'], - _fwg['shared'], - v2_utils.AdminStateColumn(_fwg['admin_state_up']), - _fwg['status'], - ) - self.ordered_columns = ( - 'description', - 'egress_firewall_policy_id', - 'id', - 'ingress_firewall_policy_id', - 'name', - 'ports', - 'tenant_id', - 'shared', - 'admin_state_up', - 'status', - ) - - -class TestCreateFirewallGroup(TestFirewallGroup, common.TestCreateFWaaS): - - def setUp(self): - # Mock objects - super(TestCreateFirewallGroup, self).setUp() - self.networkclient.create_firewall_group = mock.Mock( - return_value=_fwg) - self.mocked = self.networkclient.create_firewall_group - self.cmd = firewallgroup.CreateFirewallGroup(self.app, self.namespace) - - def _update_expect_response(self, request, response): - """Set expected request and response - - :param request - A dictionary of request body(dict of verifylist) - :param response - A OrderedDict of request body - """ - # Update response body - self.networkclient.create_firewall_group.return_value = response - osc_utils.find_project.return_value.id = response['tenant_id'] - # Update response(finally returns 'data') - self.data = _generate_response(ordered_dict=response) - self.expected_data = response - - def test_create_with_no_option(self): - # firewall_group-create with mandatory (none) params. - arglist = [] - verifylist = [] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - headers, data = self.cmd.take_action(parsed_args) - self.assertEqual(self.ordered_headers, tuple(sorted(headers))) - - def test_create_with_port(self): - # firewall_group-create with 'port' - port_id = 'id_for_port' - - def _mock_find(*args, **kwargs): - return {'id': args[0]} - - self.networkclient.find_port.side_effect = _mock_find - arglist = ['--port', port_id] - verifylist = [('port', [port_id])] - request, response = _generate_req_and_res(verifylist) - self._update_expect_response(request, response) - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - headers, data = self.cmd.take_action(parsed_args) - - self.check_results(headers, data, request) - - def test_create_with_ingress_policy(self): - ingress_policy = 'my-ingress-policy' - - def _mock_port_fwg(*args, **kwargs): - return {'id': args[0]} - - self.networkclient.find_firewall_policy.side_effect = _mock_port_fwg - - arglist = ['--ingress-firewall-policy', ingress_policy] - verifylist = [('ingress_firewall_policy', ingress_policy)] - request, response = _generate_req_and_res(verifylist) - self._update_expect_response(request, response) - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - headers, data = self.cmd.take_action(parsed_args) - self.networkclient.find_firewall_policy.assert_called_once_with( - ingress_policy, ignore_missing=False) - - self.check_results(headers, data, request) - - def test_create_with_egress_policy(self): - egress_policy = 'my-egress-policy' - - def _mock_find(*args, **kwargs): - return {'id': args[0]} - - self.networkclient.find_firewall_group.side_effect = _mock_find - self.networkclient.find_firewall_policy.side_effect = _mock_find - - arglist = ['--egress-firewall-policy', egress_policy] - verifylist = [('egress_firewall_policy', egress_policy)] - request, response = _generate_req_and_res(verifylist) - self._update_expect_response(request, response) - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - headers, data = self.cmd.take_action(parsed_args) - - self.networkclient.find_firewall_policy.assert_called_once_with( - egress_policy, ignore_missing=False) - self.check_results(headers, data, request) - - def test_create_with_all_params(self): - name = 'my-name' - description = 'my-desc' - ingress_policy = 'my-ingress-policy' - egress_policy = 'my-egress-policy' - - def _mock_find(*args, **kwargs): - return {'id': args[0]} - - self.networkclient.find_firewall_policy.side_effect = _mock_find - port = 'port' - self.networkclient.find_port.side_effect = _mock_find - tenant_id = 'my-tenant' - arglist = [ - '--name', name, - '--description', description, - '--ingress-firewall-policy', ingress_policy, - '--egress-firewall-policy', egress_policy, - '--port', port, - '--project', tenant_id, - '--share', - '--disable', - ] - verifylist = [ - ('name', name), - ('description', description), - ('ingress_firewall_policy', ingress_policy), - ('egress_firewall_policy', egress_policy), - ('port', [port]), - ('share', True), - ('project', tenant_id), - ('disable', True), - ] - request, response = _generate_req_and_res(verifylist) - self._update_expect_response(request, response) - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - headers, data = self.cmd.take_action(parsed_args) - - self.check_results(headers, data, request) - - def test_create_with_shared_and_no_share(self): - arglist = [ - '--share', - '--no-share', - ] - verifylist = [ - ('share', True), - ('no_share', True), - ] - self.assertRaises( - utils.ParserException, - self.check_parser, self.cmd, arglist, verifylist) - - def test_create_with_ports_and_no(self): - port = 'my-port' - arglist = [ - '--port', port, - '--no-port', - ] - verifylist = [ - ('port', [port]), - ('no_port', True), - ] - self.assertRaises( - utils.ParserException, - self.check_parser, self.cmd, arglist, verifylist) - - def test_create_with_ingress_policy_and_no(self): - policy = 'my-policy' - arglist = [ - '--ingress-firewall-policy', policy, - '--no-ingress-firewall-policy', - ] - verifylist = [ - ('ingress_firewall_policy', policy), - ('no_ingress_firewall_policy', True), - ] - self.assertRaises( - utils.ParserException, - self.check_parser, self.cmd, arglist, verifylist) - - def test_create_with_egress_policy_and_no(self): - policy = 'my-policy' - arglist = [ - '--egress-firewall-policy', policy, - '--no-egress-firewall-policy', - ] - verifylist = [ - ('egress_firewall_policy', policy), - ('no_egress_firewall_policy', True), - ] - self.assertRaises( - utils.ParserException, - self.check_parser, self.cmd, arglist, verifylist) - - -class TestListFirewallGroup(TestFirewallGroup, common.TestListFWaaS): - - def setUp(self): - super(TestListFirewallGroup, self).setUp() - # Mock objects - self.networkclient.firewall_groups = mock.Mock( - return_value=[_fwg]) - self.mocked = self.networkclient.firewall_groups - self.cmd = firewallgroup.ListFirewallGroup(self.app, self.namespace) - - -class TestShowFirewallGroup(TestFirewallGroup, common.TestShowFWaaS): - - def setUp(self): - super(TestShowFirewallGroup, self).setUp() - # Mock objects - self.networkclient.get_firewall_group = mock.Mock( - return_value=_fwg) - self.mocked = self.networkclient.get_firewall_group - self.cmd = firewallgroup.ShowFirewallGroup(self.app, self.namespace) - - -class TestSetFirewallGroup(TestFirewallGroup, common.TestSetFWaaS): - - def setUp(self): - super(TestSetFirewallGroup, self).setUp() - # Mock objects - _fwg['ports'] = ['old_port'] - self.networkclient.update_firewall_group = mock.Mock( - return_value={self.res: _fwg}) - self.mocked = self.networkclient.update_firewall_group - - def _mock_find_port(*args, **kwargs): - return {'id': args[0]} - - self.networkclient.find_port.side_effect = _mock_find_port - - self.cmd = firewallgroup.SetFirewallGroup(self.app, self.namespace) - - def _update_expect_response(self, request, response): - """Set expected request and response - - :param request - A dictionary of request body(dict of verifylist) - :param response - A OrderedDict of request body - """ - osc_utils.find_project.return_value.id = response['tenant_id'] - # Update response(finally returns 'data') - self.data = _generate_response(ordered_dict=response) - self.ordered_data = tuple( - response[column] for column in self.ordered_columns - ) - - def test_set_ingress_policy_and_egress_policy(self): - target = self.resource['id'] - ingress_policy = 'ingress_policy' - egress_policy = 'egress_policy' - - def _mock_fwg_policy(*args, **kwargs): - # 1. Find specified firewall_group - if self.networkclient.find_firewall_group.call_count == 1: - self.networkclient.find_firewall_group.assert_called_with( - target, ignore_missing=False) - # 2. Find specified 'ingress_firewall_policy' - if self.networkclient.find_firewall_policy.call_count == 1: - self.networkclient.find_firewall_policy.assert_called_with( - ingress_policy, ignore_missing=False) - # 3. Find specified 'ingress_firewall_policy' - if self.networkclient.find_firewall_policy.call_count == 2: - self.networkclient.find_firewall_policy.assert_called_with( - egress_policy, ignore_missing=False) - return {'id': args[0]} - - self.networkclient.find_firewall_group.side_effect = _mock_fwg_policy - self.networkclient.find_firewall_policy.side_effect = _mock_fwg_policy - - arglist = [ - target, - '--ingress-firewall-policy', ingress_policy, - '--egress-firewall-policy', egress_policy, - ] - verifylist = [ - (self.res, target), - ('ingress_firewall_policy', ingress_policy), - ('egress_firewall_policy', egress_policy), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with( - target, **{'ingress_firewall_policy_id': ingress_policy, - 'egress_firewall_policy_id': egress_policy}) - self.assertIsNone(result) - - def test_set_port(self): - target = self.resource['id'] - port1 = 'additional_port1' - port2 = 'additional_port2' - - def _mock_port_fwg(*args, **kwargs): - # 1. Find specified firewall_group - if self.networkclient.find_firewall_group.call_count in [1, 2]: - self.networkclient.find_firewall_group.assert_called_with( - target, ignore_missing=False) - return {'id': args[0], 'ports': _fwg['ports']} - # 2. Find specified 'port' #1 - if self.networkclient.find_port.call_count == 1: - self.networkclient.find_port.assert_called_with(args) - return {'id': args[0]} - # 3. Find specified 'port' #2 - if self.networkclient.find_port.call_count == 2: - self.networkclient.find_port.assert_called_with(args) - return {'id': args[0]} - - self.networkclient.find_fireall_group.side_effect = _mock_port_fwg - self.networkclient.find_port.side_effect = _mock_port_fwg - - arglist = [ - target, - '--port', port1, - '--port', port2, - ] - verifylist = [ - (self.res, target), - ('port', [port1, port2]), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - expect = {'ports': sorted(_fwg['ports'] + [port1, port2])} - self.mocked.assert_called_once_with(target, **expect) - self.assertEqual(2, self.networkclient.find_firewall_group.call_count) - self.assertIsNone(result) - - def test_set_no_port(self): - # firewall_group-update myid --policy newpolicy. - target = self.resource['id'] - arglist = [target, '--no-port'] - verifylist = [ - (self.res, target), - ('no_port', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with( - target, **{'ports': []}) - self.assertIsNone(result) - - def test_set_admin_state(self): - target = self.resource['id'] - arglist = [target, '--enable'] - verifylist = [ - (self.res, target), - ('enable', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with( - target, **{'admin_state_up': True}) - self.assertIsNone(result) - - def test_set_egress_policy(self): - target = self.resource['id'] - policy = 'egress_policy' - - def _mock_find_policy(*args, **kwargs): - return {'id': args[0]} - - self.networkclient.find_firewall_policy.side_effect = _mock_find_policy - - arglist = [target, '--egress-firewall-policy', policy] - verifylist = [ - (self.res, target), - ('egress_firewall_policy', policy), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with( - target, **{'egress_firewall_policy_id': policy}) - self.assertIsNone(result) - - def test_set_no_ingress_policies(self): - target = self.resource['id'] - arglist = [target, '--no-ingress-firewall-policy'] - verifylist = [ - (self.res, target), - ('no_ingress_firewall_policy', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with( - target, **{'ingress_firewall_policy_id': None}) - self.assertIsNone(result) - - def test_set_no_egress_policies(self): - target = self.resource['id'] - arglist = [target, '--no-egress-firewall-policy'] - verifylist = [ - (self.res, target), - ('no_egress_firewall_policy', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with( - target, **{'egress_firewall_policy_id': None}) - self.assertIsNone(result) - - def test_set_port_and_no_port(self): - target = self.resource['id'] - port = 'my-port' - arglist = [ - target, - '--port', port, - '--no-port', - ] - verifylist = [ - (self.res, target), - ('port', [port]), - ('no_port', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - self.mocked.assert_called_once_with( - target, **{'ports': [port]}) - self.assertIsNone(result) - - def test_set_ingress_policy_and_no_ingress_policy(self): - target = self.resource['id'] - arglist = [ - target, - '--ingress-firewall-policy', 'my-ingress', - '--no-ingress-firewall-policy', - ] - verifylist = [ - (self.res, target), - ('ingress_firewall_policy', 'my-ingress'), - ('no_ingress_firewall_policy', True), - ] - self.assertRaises( - utils.ParserException, - self.check_parser, self.cmd, arglist, verifylist) - - def test_set_egress_policy_and_no_egress_policy(self): - target = self.resource['id'] - arglist = [ - target, - '--egress-firewall-policy', 'my-egress', - '--no-egress-firewall-policy', - ] - verifylist = [ - (self.res, target), - ('egress_firewall_policy', 'my-egress'), - ('no_egress_firewall_policy', True), - ] - self.assertRaises( - utils.ParserException, - self.check_parser, self.cmd, arglist, verifylist) - - def test_set_and_raises(self): - self.networkclient.update_firewall_group = mock.Mock( - side_effect=Exception) - target = self.resource['id'] - arglist = [target, '--name', 'my-name'] - verifylist = [(self.res, target), ('name', 'my-name')] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args) - - -class TestDeleteFirewallGroup(TestFirewallGroup, common.TestDeleteFWaaS): - - def setUp(self): - super(TestDeleteFirewallGroup, self).setUp() - # Mock objects - self.networkclient.delete_firewall_group = mock.Mock() - self.mocked = self.networkclient.delete_firewall_group - self.cmd = firewallgroup.DeleteFirewallGroup(self.app, self.namespace) - - -class TestUnsetFirewallGroup(TestFirewallGroup, common.TestUnsetFWaaS): - - def setUp(self): - super(TestUnsetFirewallGroup, self).setUp() - _fwg['ports'] = ['old_port'] - # Mock objects - self.networkclient.update_firewall_group = mock.Mock() - self.mocked = self.networkclient.update_firewall_group - self.cmd = firewallgroup.UnsetFirewallGroup(self.app, self.namespace) - - def test_unset_ingress_policy(self): - target = self.resource['id'] - arglist = [ - target, - '--ingress-firewall-policy', - ] - verifylist = [ - (self.res, target), - ('ingress_firewall_policy', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - self.mocked.assert_called_once_with( - target, **{'ingress_firewall_policy_id': None}) - self.assertIsNone(result) - - def test_unset_egress_policy(self): - target = self.resource['id'] - arglist = [ - target, - '--egress-firewall-policy', - ] - verifylist = [ - (self.res, target), - ('egress_firewall_policy', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - self.mocked.assert_called_once_with( - target, **{'egress_firewall_policy_id': None}) - self.assertIsNone(result) - - def test_unset_enable(self): - target = self.resource['id'] - arglist = [ - target, - '--enable', - ] - verifylist = [ - (self.res, target), - ('enable', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - self.mocked.assert_called_once_with( - target, **{'admin_state_up': False}) - self.assertIsNone(result) - - def test_unset_port(self): - target = self.resource['id'] - port = 'old_port' - - def _mock_port_fwg(*args, **kwargs): - # 1. Find specified firewall_group - if self.networkclient.find_firewall_group.call_count in [1, 2]: - self.networkclient.find_firewall_group.assert_called_with( - target, ignore_missing=False) - return {'id': args[0], 'ports': _fwg['ports']} - # 2. Find specified firewall_group and refer 'ports' attribute - if self.networkclient.find_port.call_count == 2: - self.networkclient.find_port.assert_called_with(target) - return {'ports': _fwg['ports']} - # 3. Find specified 'port' - if self.networkclient.find_port.call_count == 3: - self.networkclient.find_port.assert_called_with(port) - return {'id': args[0]} - - self.networkclient.find_firewall_group.side_effect = _mock_port_fwg - self.networkclient.find_port.side_effect = _mock_port_fwg - - arglist = [ - target, - '--port', port, - ] - verifylist = [ - (self.res, target), - ('port', [port]), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - self.mocked.assert_called_once_with(target, **{'ports': []}) - self.assertIsNone(result) - - def test_unset_all_port(self): - target = self.resource['id'] - arglist = [ - target, - '--all-port', - ] - verifylist = [ - (self.res, target), - ('all_port', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - self.mocked.assert_called_once_with(target, **{'ports': []}) - self.assertIsNone(result) diff --git a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py deleted file mode 100644 index fe385a0cb..000000000 --- a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallpolicy.py +++ /dev/null @@ -1,673 +0,0 @@ -# Copyright 2016 FUJITSU LIMITED -# 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 re -from unittest import mock - -from osc_lib import exceptions -from osc_lib.tests import utils - -from neutronclient.osc import utils as osc_utils -from neutronclient.osc.v2.fwaas import firewallpolicy -from neutronclient.tests.unit.osc.v2 import fakes as test_fakes -from neutronclient.tests.unit.osc.v2.fwaas import common -from neutronclient.tests.unit.osc.v2.fwaas import fakes - - -_fwp = fakes.FirewallPolicy().create() -CONVERT_MAP = { - 'share': 'shared', - 'no_share': 'shared', - 'project': 'tenant_id', - 'port': 'ports', - 'name': 'name', - 'id': 'id', - 'firewall_rule': 'firewall_rules', - 'description': 'description' -} - - -def _generate_data(ordered_dict=None, data=None): - source = ordered_dict if ordered_dict else _fwp - if data: - source.update(data) - return tuple(source[key] for key in source) - - -def _generate_req_and_res(verifylist): - request = dict(verifylist) - response = _fwp - for key, val in verifylist: - converted = CONVERT_MAP.get(key, key) - del request[key] - if re.match('^no_', key) and val is True: - new_value = None - elif key == 'enable' and val: - new_value = True - elif key == 'disable' and val: - new_value = False - elif val is True or val is False: - new_value = val - elif key in ('name', 'description'): - new_value = val - else: - new_value = val - request[converted] = new_value - response[converted] = new_value - return request, response - - -class TestFirewallPolicy(test_fakes.TestNeutronClientOSCV2): - - def check_results(self, headers, data, exp_req, is_list=False): - if is_list: - req_body = {self.res_plural: [exp_req]} - else: - req_body = exp_req - self.mocked.assert_called_once_with(**req_body) - self.assertEqual(self.ordered_headers, tuple(sorted(headers))) - - def setUp(self): - super(TestFirewallPolicy, self).setUp() - - osc_utils.find_project = mock.Mock() - osc_utils.find_project.id = _fwp['tenant_id'] - self.res = 'firewall_policy' - self.res_plural = 'firewall_policies' - self.resource = _fwp - self.list_headers = ( - 'ID', - 'Name', - 'Firewall Rules', - ) - self.list_data = ( - _fwp['id'], - _fwp['name'], - _fwp['firewall_rules'], - ) - self.headers = tuple(self.list_headers + ( - 'Description', - 'Audited', - 'Shared', - 'Project') - ) - self.data = _generate_data() - self.ordered_headers = ( - 'Audited', - 'Description', - 'Firewall Rules', - 'ID', - 'Name', - 'Project', - 'Shared', - ) - self.ordered_data = ( - _fwp['audited'], - _fwp['description'], - _fwp['firewall_rules'], - _fwp['id'], - _fwp['name'], - _fwp['tenant_id'], - _fwp['shared'], - ) - self.ordered_columns = ( - 'audited', - 'description', - 'firewall_rules', - 'id', - 'name', - 'tenant_id', - 'shared', - ) - - -class TestCreateFirewallPolicy(TestFirewallPolicy, common.TestCreateFWaaS): - - def setUp(self): - super(TestCreateFirewallPolicy, self).setUp() - self.networkclient.create_firewall_policy = mock.Mock( - return_value={self.res: _fwp}) - self.mocked = self.networkclient.create_firewall_policy - self.cmd = firewallpolicy.CreateFirewallPolicy(self.app, - self.namespace) - - def _update_expect_response(self, request, response): - """Set expected request and response - - :param request - A dictionary of request body(dict of verifylist) - :param response - A OrderedDict of request body - """ - # Update response body - self.networkclient.create_firewall_policy.return_value = response - osc_utils.find_project.return_value.id = response['tenant_id'] - # Update response(finally returns 'data') - self.data = _generate_data(data=response) - self.ordered_data = tuple( - response[column] for column in self.ordered_columns - ) - - def test_create_with_no_options(self): - arglist = [] - verifylist = [] - - self.assertRaises( - utils.ParserException, - self.check_parser, self.cmd, arglist, verifylist) - - def test_create_with_mandatory_param(self): - name = 'my-fwg' - arglist = [ - name, - ] - verifylist = [ - ('name', name), - ] - request, response = _generate_req_and_res(verifylist) - self._update_expect_response(request, response) - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - headers, data = self.cmd.take_action(parsed_args) - - self.check_results(headers, data, request) - - def test_create_with_rules(self): - name = 'my-fwg' - rule1 = 'rule1' - rule2 = 'rule2' - - def _mock_policy(*args, **kwargs): - return {'id': args[0]} - - self.networkclient.find_firewall_rule.side_effect = _mock_policy - - arglist = [ - name, - '--firewall-rule', rule1, - '--firewall-rule', rule2, - ] - verifylist = [ - ('name', name), - ('firewall_rule', [rule1, rule2]), - ] - request, response = _generate_req_and_res(verifylist) - self._update_expect_response(request, response) - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - headers, data = self.cmd.take_action(parsed_args) - self.assertEqual(2, self.networkclient.find_firewall_rule.call_count) - - self.check_results(headers, data, request) - - def test_create_with_all_params(self): - name = 'my-fwp' - desc = 'my-desc' - rule1 = 'rule1' - rule2 = 'rule2' - project = 'my-tenant' - - def _mock_find(*args, **kwargs): - if self.res in args[0]: - rules = _fwp['firewall_rules'] - return {'id': args[0], 'firewall_rules': rules} - return {'id': args[0]} - - self.networkclient.find_firewall_policy.side_effect = _mock_find - self.networkclient.find_firewall_rule.side_effect = _mock_find - - arglist = [ - name, - '--description', desc, - '--firewall-rule', rule1, - '--firewall-rule', rule2, - '--project', project, - '--share', - '--audited', - ] - verifylist = [ - ('name', name), - ('description', desc), - ('firewall_rule', [rule1, rule2]), - ('project', project), - ('share', True), - ('audited', True), - ] - request, response = _generate_req_and_res(verifylist) - self._update_expect_response(request, response) - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - headers, data = self.cmd.take_action(parsed_args) - - self.check_results(headers, data, request) - - def test_create_with_firewall_rule_and_no(self): - name = 'my-fwp' - rule1 = 'rule1' - rule2 = 'rule2' - arglist = [ - name, - '--firewall-rule', rule1, - '--firewall-rule', rule2, - '--no-firewall-rule', - ] - verifylist = [ - ('name', name), - ('firewall_rule', [rule1, rule2]), - ('no_firewall_rule', True), - ] - self.assertRaises( - utils.ParserException, - self.check_parser, self.cmd, arglist, verifylist) - - def test_create_with_shared_and_no_share(self): - name = 'my-fwp' - arglist = [ - name, - '--share', - '--no-share', - ] - verifylist = [ - ('name', name), - ('share', True), - ('no_share', True), - ] - self.assertRaises( - utils.ParserException, - self.check_parser, self.cmd, arglist, verifylist) - - def test_create_with_audited_and_no(self): - name = 'my-fwp' - arglist = [ - name, - '--audited', - '--no-audited', - ] - verifylist = [ - ('name', name), - ('audited', True), - ('no_audited', True), - ] - self.assertRaises( - utils.ParserException, - self.check_parser, self.cmd, arglist, verifylist) - - -class TestListFirewallPolicy(TestFirewallPolicy, common.TestListFWaaS): - - def setUp(self): - super(TestListFirewallPolicy, self).setUp() - self.networkclient.firewall_policies = mock.Mock( - return_value=[_fwp]) - self.mocked = self.networkclient.firewall_policies - self.cmd = firewallpolicy.ListFirewallPolicy(self.app, self.namespace) - - -class TestShowFirewallPolicy(TestFirewallPolicy, common.TestShowFWaaS): - - def setUp(self): - super(TestShowFirewallPolicy, self).setUp() - self.networkclient.get_firewall_policy = mock.Mock( - return_value=_fwp) - self.mocked = self.networkclient.get_firewall_policy - self.cmd = firewallpolicy.ShowFirewallPolicy(self.app, self.namespace) - - -class TestSetFirewallPolicy(TestFirewallPolicy, common.TestSetFWaaS): - - def setUp(self): - super(TestSetFirewallPolicy, self).setUp() - self.networkclient.update_firewall_policy = mock.Mock( - return_value=_fwp) - self.mocked = self.networkclient.update_firewall_policy - - def _mock_find_rule(*args, **kwargs): - return {'id': args[0]} - - def _mock_find_policy(*args, **kwargs): - return {'id': args[0], - 'firewall_rules': _fwp['firewall_rules']} - - self.networkclient.find_firewall_policy.side_effect = _mock_find_policy - self.networkclient.find_firewall_rule.side_effect = _mock_find_rule - - self.cmd = firewallpolicy.SetFirewallPolicy(self.app, self.namespace) - - def test_set_rules(self): - target = self.resource['id'] - rule1 = 'new_rule1' - rule2 = 'new_rule2' - arglist = [ - target, - '--firewall-rule', rule1, - '--firewall-rule', rule2, - ] - verifylist = [ - (self.res, target), - ('firewall_rule', [rule1, rule2]), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - expect = _fwp['firewall_rules'] + [rule1, rule2] - body = {'firewall_rules': expect} - self.mocked.assert_called_once_with(target, **body) - self.assertEqual(2, self.networkclient.find_firewall_rule.call_count) - self.assertEqual(2, self.networkclient.find_firewall_policy.call_count) - self.assertIsNone(result) - - def test_set_no_rules(self): - target = self.resource['id'] - arglist = [target, '--no-firewall-rule'] - verifylist = [ - (self.res, target), - ('no_firewall_rule', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - body = {'firewall_rules': []} - self.mocked.assert_called_once_with(target, **body) - self.assertIsNone(result) - - def test_set_rules_and_no_rules(self): - target = self.resource['id'] - rule1 = 'rule1' - arglist = [ - target, - '--firewall-rule', rule1, - '--no-firewall-rule', - ] - verifylist = [ - (self.res, target), - ('firewall_rule', [rule1]), - ('no_firewall_rule', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - body = {'firewall_rules': [rule1]} - self.mocked.assert_called_once_with(target, **body) - self.assertEqual(1, self.networkclient.find_firewall_rule.call_count) - self.assertEqual(1, self.networkclient.find_firewall_policy.call_count) - self.assertIsNone(result) - - def test_set_audited(self): - target = self.resource['id'] - arglist = [target, '--audited'] - verifylist = [ - (self.res, target), - ('audited', True), - ] - body = {'audited': True} - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with(target, **body) - self.assertIsNone(result) - - def test_set_no_audited(self): - target = self.resource['id'] - arglist = [target, '--no-audited'] - verifylist = [ - (self.res, target), - ('no_audited', True), - ] - body = {'audited': False} - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with(target, **body) - self.assertIsNone(result) - - def test_set_audited_and_no_audited(self): - target = self.resource['id'] - arglist = [ - target, - '--audited', - '--no-audited', - ] - verifylist = [ - (self.res, target), - ('audited', True), - ('no_audited', True), - ] - self.assertRaises( - utils.ParserException, - self.check_parser, self.cmd, arglist, verifylist) - - def test_set_and_raises(self): - self.networkclient.update_firewall_policy = mock.Mock( - side_effect=Exception) - target = self.resource['id'] - - arglist = [target, '--name', 'my-name'] - verifylist = [(self.res, target), ('name', 'my-name')] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args) - - -class TestDeleteFirewallPolicy(TestFirewallPolicy, common.TestDeleteFWaaS): - - def setUp(self): - super(TestDeleteFirewallPolicy, self).setUp() - self.networkclient.delete_firewall_policy = mock.Mock( - return_value={self.res: _fwp}) - self.mocked = self.networkclient.delete_firewall_policy - self.cmd = firewallpolicy.DeleteFirewallPolicy( - self.app, self.namespace) - - -class TestFirewallPolicyInsertRule(TestFirewallPolicy): - - def setUp(self): - super(TestFirewallPolicyInsertRule, self).setUp() - self.networkclient.insert_rule_firewall_policy = mock.Mock( - return_value={self.res: _fwp}) - self.mocked = self.networkclient.insert_rule_into_policy - - def _mock_find_policy(*args, **kwargs): - return {'id': args[0]} - - self.networkclient.find_firewall_policy.side_effect = _mock_find_policy - self.networkclient.find_firewall_rule.side_effect = _mock_find_policy - - self.cmd = firewallpolicy.FirewallPolicyInsertRule(self.app, - self.namespace) - - def test_insert_firewall_rule(self): - target = self.resource['id'] - rule = 'new-rule' - before = 'before' - after = 'after' - arglist = [ - target, - rule, - '--insert-before', before, - '--insert-after', after, - ] - verifylist = [ - (self.res, target), - ('firewall_rule', rule), - ('insert_before', before), - ('insert_after', after), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - body = { - 'firewall_rule_id': rule, - 'insert_before': before, - 'insert_after': after - } - self.mocked.assert_called_once_with(target, **body) - self.assertIsNone(result) - self.assertEqual(1, self.networkclient.find_firewall_policy.call_count) - self.assertEqual(3, self.networkclient.find_firewall_rule.call_count) - - def test_insert_with_no_firewall_rule(self): - target = self.resource['id'] - arglist = [ - target, - ] - verifylist = [ - (self.res, target), - ] - self.assertRaises( - utils.ParserException, - self.check_parser, self.cmd, arglist, verifylist) - - -class TestFirewallPolicyRemoveRule(TestFirewallPolicy): - - def setUp(self): - super(TestFirewallPolicyRemoveRule, self).setUp() - self.networkclient.remove_rule_firewall_policy = mock.Mock( - return_value={self.res: _fwp}) - self.mocked = self.networkclient.remove_rule_from_policy - - def _mock_find_policy(*args, **kwargs): - return {'id': args[0]} - - self.networkclient.find_firewall_policy.side_effect = _mock_find_policy - self.networkclient.find_firewall_rule.side_effect = _mock_find_policy - - self.cmd = firewallpolicy.FirewallPolicyRemoveRule(self.app, - self.namespace) - - def test_remove_firewall_rule(self): - target = self.resource['id'] - rule = 'remove-rule' - arglist = [ - target, - rule, - ] - verifylist = [ - (self.res, target), - ('firewall_rule', rule), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - body = {'firewall_rule_id': rule} - self.mocked.assert_called_once_with(target, **body) - self.assertIsNone(result) - self.assertEqual(1, self.networkclient.find_firewall_policy.call_count) - self.assertEqual(1, self.networkclient.find_firewall_rule.call_count) - - def test_remove_with_no_firewall_rule(self): - target = self.resource['id'] - arglist = [ - target, - ] - verifylist = [ - (self.res, target), - ] - self.assertRaises( - utils.ParserException, - self.check_parser, self.cmd, arglist, verifylist) - - -class TestUnsetFirewallPolicy(TestFirewallPolicy, common.TestUnsetFWaaS): - - def setUp(self): - super(TestUnsetFirewallPolicy, self).setUp() - self.networkclient.update_firewall_policy = mock.Mock( - return_value={self.res: _fwp}) - self.mocked = self.networkclient.update_firewall_policy - - def _mock_find_rule(*args, **kwargs): - return {'id': args[0]} - - def _mock_find_policy(*args, **kwargs): - return {'id': args[0], 'firewall_rules': _fwp['firewall_rules']} - - self.networkclient.find_firewall_policy.side_effect = _mock_find_policy - self.networkclient.find_firewall_rule.side_effect = _mock_find_rule - - self.cmd = firewallpolicy.UnsetFirewallPolicy(self.app, self.namespace) - - def test_unset_audited(self): - target = self.resource['id'] - arglist = [ - target, - '--audited', - ] - verifylist = [ - (self.res, target), - ('audited', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - body = {'audited': False} - self.mocked.assert_called_once_with(target, **body) - self.assertIsNone(result) - - def test_unset_firewall_rule_not_matched(self): - _fwp['firewall_rules'] = ['old_rule'] - target = self.resource['id'] - rule = 'new_rule' - arglist = [ - target, - '--firewall-rule', rule, - ] - verifylist = [ - (self.res, target), - ('firewall_rule', [rule]), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - body = {'firewall_rules': _fwp['firewall_rules']} - self.mocked.assert_called_once_with(target, **body) - self.assertIsNone(result) - - def test_unset_firewall_rule_matched(self): - _fwp['firewall_rules'] = ['rule1', 'rule2'] - target = self.resource['id'] - rule = 'rule1' - arglist = [ - target, - '--firewall-rule', rule, - ] - verifylist = [ - (self.res, target), - ('firewall_rule', [rule]), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - body = {'firewall_rules': ['rule2']} - self.mocked.assert_called_once_with(target, **body) - self.assertIsNone(result) - self.assertEqual(2, self.networkclient.find_firewall_policy.call_count) - self.assertEqual(1, self.networkclient.find_firewall_rule.call_count) - - def test_unset_all_firewall_rule(self): - target = self.resource['id'] - arglist = [ - target, - '--all-firewall-rule', - ] - verifylist = [ - (self.res, target), - ('all_firewall_rule', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - body = {'firewall_rules': []} - self.mocked.assert_called_once_with(target, **body) - self.assertIsNone(result) diff --git a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py b/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py deleted file mode 100644 index 2714fd055..000000000 --- a/neutronclient/tests/unit/osc/v2/fwaas/test_firewallrule.py +++ /dev/null @@ -1,904 +0,0 @@ -# Copyright 2016 FUJITSU LIMITED -# 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 re -from unittest import mock - -from osc_lib import exceptions -from osc_lib.tests import utils -import testtools - -from neutronclient.osc import utils as osc_utils -from neutronclient.osc.v2.fwaas import firewallrule -from neutronclient.tests.unit.osc.v2 import fakes as test_fakes -from neutronclient.tests.unit.osc.v2.fwaas import common -from neutronclient.tests.unit.osc.v2.fwaas import fakes - - -_fwr = fakes.FirewallRule().create() -CONVERT_MAP = { - 'project': 'tenant_id', - 'enable_rule': 'enabled', - 'disable_rule': 'enabled', - 'share': 'shared', - 'no_share': 'shared', - 'source_firewall_group': 'source_firewall_group_id', - 'destination_firewall_group': 'destination_firewall_group_id', - 'no_source_firewall_group': 'source_firewall_group_id', - 'no_destination_firewall_group': 'destination_firewall_group_id', -} - - -def _generate_data(ordered_dict=None, data=None): - source = ordered_dict if ordered_dict else _fwr - if data: - source.update(data) - ret = tuple(_replace_display_columns(key, source[key]) for key in source) - return ret - - -def _replace_display_columns(key, val): - if key == 'protocol': - return firewallrule.ProtocolColumn(val) - return val - - -def _generate_req_and_res(verifylist): - request = dict(verifylist) - response = _fwr - for key, val in verifylist: - converted = CONVERT_MAP.get(key, key) - del request[key] - if re.match('^no_', key) and val is True: - new_value = None - elif (key == 'enable' or key == 'enable_rule') and val: - new_value = True - elif (key == 'disable' or key == 'disable_rule') and val: - new_value = False - elif (key == 'protocol' and val and val.lower() == 'any'): - new_value = None - elif val is True or val is False: - new_value = val - elif key in ('name', 'description'): - new_value = val - else: - new_value = val - request[converted] = new_value - response[converted] = new_value - return request, response - - -class TestFirewallRule(test_fakes.TestNeutronClientOSCV2): - - def check_results(self, headers, data, exp_req=None, is_list=False): - if is_list: - req_body = {self.res_plural: [exp_req]} - else: - req_body = exp_req - if not exp_req: - self.mocked.assert_called_once_with() - else: - self.mocked.assert_called_once_with(**req_body) - self.assertEqual(self.ordered_headers, headers) - - def setUp(self): - super(TestFirewallRule, self).setUp() - - osc_utils.find_project = mock.Mock() - osc_utils.find_project.id = _fwr['tenant_id'] - self.res = 'firewall_rule' - self.res_plural = 'firewall_rules' - self.resource = _fwr - self.headers = ( - 'ID', - 'Name', - 'Enabled', - 'Description', - 'Firewall Policy', - 'IP Version', - 'Action', - 'Protocol', - 'Source IP Address', - 'Source Port', - 'Destination IP Address', - 'Destination Port', - 'Shared', - 'Project', - 'Source Firewall Group ID', - 'Destination Firewall Group ID', - ) - self.data = _generate_data() - self.ordered_headers = ( - 'Action', - 'Description', - 'Destination Firewall Group ID', - 'Destination IP Address', - 'Destination Port', - 'Enabled', - 'Firewall Policy', - 'ID', - 'IP Version', - 'Name', - 'Project', - 'Protocol', - 'Shared', - 'Source Firewall Group ID', - 'Source IP Address', - 'Source Port', - 'Summary', - ) - self.ordered_data = ( - _fwr['action'], - _fwr['description'], - _fwr['destination_firewall_group_id'], - _fwr['destination_ip_address'], - _fwr['destination_port'], - _fwr['firewall_policy_id'], - _fwr['enabled'], - _fwr['id'], - _fwr['ip_version'], - _fwr['name'], - _fwr['tenant_id'], - _replace_display_columns('protocol', _fwr['protocol']), - _fwr['shared'], - _fwr['source_firewall_group_id'], - _fwr['source_ip_address'], - _fwr['source_port'], - ) - self.ordered_columns = ( - 'action', - 'description', - 'destination_firewall_group_id', - 'destination_ip_address', - 'destination_port', - 'enabled', - 'id', - 'ip_version', - 'name', - 'tenant_id', - 'protocol', - 'shared', - 'source_firewall_group_id', - 'source_ip_address', - 'source_port', - ) - - -class TestCreateFirewallRule(TestFirewallRule, common.TestCreateFWaaS): - - def setUp(self): - super(TestCreateFirewallRule, self).setUp() - self.networkclient.create_firewall_rule = mock.Mock( - return_value=_fwr) - self.mocked = self.networkclient.create_firewall_rule - - def _mock_find_group(*args, **kwargs): - return {'id': args[0]} - - self.networkclient.find_firewall_group.side_effect = _mock_find_group - - self.cmd = firewallrule.CreateFirewallRule(self.app, self.namespace) - - def _update_expect_response(self, request, response): - """Set expected request and response - - :param request - A dictionary of request body(dict of verifylist) - :param response - A OrderedDict of request body - """ - # Update response body - self.networkclient.create_firewall_rule.return_value = response - osc_utils.find_project.return_value.id = response['tenant_id'] - # Update response(finally returns 'data') - self.data = _generate_data(ordered_dict=response) - self.ordered_data = tuple( - _replace_display_columns(column, response[column]) - for column in self.ordered_columns - ) - - def _set_all_params(self, args={}): - name = args.get('name') or 'my-name' - description = args.get('description') or 'my-desc' - source_ip = args.get('source_ip_address') or '192.168.1.0/24' - destination_ip = args.get('destination_ip_address') or '192.168.2.0/24' - source_port = args.get('source_port') or '0:65535' - protocol = args.get('protocol') or 'udp' - action = args.get('action') or 'deny' - ip_version = args.get('ip_version') or '4' - destination_port = args.get('destination_port') or '0:65535' - destination_firewall_group = args.get( - 'destination_firewall_group') or 'my-dst-fwg' - source_firewall_group = args.get( - 'source_firewall_group') or 'my-src-fwg' - tenant_id = args.get('tenant_id') or 'my-tenant' - arglist = [ - '--description', description, - '--name', name, - '--protocol', protocol, - '--ip-version', ip_version, - '--source-ip-address', source_ip, - '--destination-ip-address', destination_ip, - '--source-port', source_port, - '--destination-port', destination_port, - '--action', action, - '--project', tenant_id, - '--disable-rule', - '--share', - '--source-firewall-group', source_firewall_group, - '--destination-firewall-group', destination_firewall_group - ] - - verifylist = [ - ('name', name), - ('description', description), - ('share', True), - ('protocol', protocol), - ('ip_version', ip_version), - ('source_ip_address', source_ip), - ('destination_ip_address', destination_ip), - ('source_port', source_port), - ('destination_port', destination_port), - ('action', action), - ('disable_rule', True), - ('project', tenant_id), - ('source_firewall_group', source_firewall_group), - ('destination_firewall_group', destination_firewall_group) - ] - return arglist, verifylist - - def _test_create_with_all_params(self, args={}): - arglist, verifylist = self._set_all_params(args) - request, response = _generate_req_and_res(verifylist) - self._update_expect_response(request, response) - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - headers, data = self.cmd.take_action(parsed_args) - - self.check_results(headers, data, request) - - def test_create_with_no_options(self): - arglist = [] - verifylist = [] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - headers, data = self.cmd.take_action(parsed_args) - self.check_results(headers, data, None) - - def test_create_with_all_params(self): - self._test_create_with_all_params() - - def test_create_with_all_params_protocol_any(self): - self._test_create_with_all_params({'protocol': 'any'}) - - def test_create_with_all_params_ip_version_6(self): - self._test_create_with_all_params({'ip_version': '6'}) - - def test_create_with_all_params_invalid_ip_version(self): - arglist, verifylist = self._set_all_params({'ip_version': '128'}) - self.assertRaises( - utils.ParserException, - self.check_parser, self.cmd, arglist, verifylist) - - def test_create_with_all_params_action_upper_capitalized(self): - for action in ('Allow', 'DENY', 'Reject'): - arglist, verifylist = self._set_all_params({'action': action}) - self.assertRaises( - testtools.matchers._impl.MismatchError, - self.check_parser, self.cmd, arglist, verifylist) - - def test_create_with_all_params_protocol_upper_capitalized(self): - for protocol in ('TCP', 'Tcp', 'ANY', 'AnY', 'iCMp'): - arglist, verifylist = self._set_all_params({'protocol': protocol}) - self.assertRaises( - testtools.matchers._impl.MismatchError, - self.check_parser, self.cmd, arglist, verifylist) - - def test_create_with_src_fwg_and_no(self): - fwg = 'my-fwg' - arglist = [ - '--source-firewall-group', fwg, - '--no-source-firewall-group', - ] - verifylist = [ - ('source_firewall_group', fwg), - ('no_source_firewall_group', True), - ] - self.assertRaises( - utils.ParserException, - self.check_parser, self.cmd, arglist, verifylist) - - def test_create_with_dst_fwg_and_no(self): - fwg = 'my-fwg' - arglist = [ - '--destination-firewall-group', fwg, - '--no-destination-firewall-group', - ] - verifylist = [ - ('destination_firewall_group', fwg), - ('no_destination_firewall_group', True), - ] - self.assertRaises( - utils.ParserException, - self.check_parser, self.cmd, arglist, verifylist) - - -class TestListFirewallRule(TestFirewallRule): - - def _setup_summary(self, expect=None): - protocol = (_fwr['protocol'] or 'any').upper() - src = 'source(port): 192.168.1.0/24(1:11111)' - dst = 'dest(port): 192.168.2.2(2:22222)' - action = 'deny' - if expect: - if expect.get('protocol'): - protocol = expect['protocol'] - if expect.get('source_ip_address'): - src_ip = expect['source_ip_address'] - if expect.get('source_port'): - src_port = expect['source_port'] - if expect.get('destination_ip_address'): - dst_ip = expect['destination_ip_address'] - if expect.get('destination_port'): - dst_port = expect['destination_port'] - if expect.get('action'): - action = expect['action'] - src = 'source(port): ' + src_ip + '(' + src_port + ')' - dst = 'dest(port): ' + dst_ip + '(' + dst_port + ')' - return ',\n '.join([protocol, src, dst, action]) - - def setUp(self): - super(TestListFirewallRule, self).setUp() - self.cmd = firewallrule.ListFirewallRule(self.app, self.namespace) - - self.short_header = ( - 'ID', - 'Name', - 'Enabled', - 'Summary', - 'Firewall Policy', - ) - - summary = self._setup_summary(_fwr) - - self.short_data = ( - _fwr['id'], - _fwr['name'], - _fwr['enabled'], - summary, - _fwr['firewall_policy_id'] - ) - self.networkclient.firewall_rules = mock.Mock( - return_value=[_fwr]) - self.mocked = self.networkclient.firewall_rules - - def test_list_with_long_option(self): - arglist = ['--long'] - verifylist = [('long', True)] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - headers, data = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with() - self.assertEqual(list(self.headers), headers) - - def test_list_with_no_option(self): - arglist = [] - verifylist = [] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - headers, data = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with() - self.assertEqual(list(self.short_header), headers) - self.assertListItemEqual([self.short_data], list(data)) - - -class TestShowFirewallRule(TestFirewallRule, common.TestShowFWaaS): - - def setUp(self): - super(TestShowFirewallRule, self).setUp() - self.networkclient.get_firewall_rule = mock.Mock( - return_value=_fwr) - self.mocked = self.networkclient.get_firewall_rule - self.cmd = firewallrule.ShowFirewallRule(self.app, self.namespace) - - -class TestSetFirewallRule(TestFirewallRule, common.TestSetFWaaS): - - def setUp(self): - super(TestSetFirewallRule, self).setUp() - self.networkclient.update_firewall_rule = mock.Mock( - return_value=_fwr) - self.mocked = self.networkclient.update_firewall_rule - - def _mock_find_rule(*args, **kwargs): - return {'id': args[0]} - - self.networkclient.find_firewall_rule.side_effect = _mock_find_rule - - self.cmd = firewallrule.SetFirewallRule(self.app, self.namespace) - - def test_set_protocol_with_any(self): - target = self.resource['id'] - protocol = 'any' - arglist = [target, '--protocol', protocol] - verifylist = [ - (self.res, target), - ('protocol', protocol), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with(target, **{'protocol': None}) - self.assertIsNone(result) - - def test_set_protocol_with_udp(self): - target = self.resource['id'] - protocol = 'udp' - arglist = [target, '--protocol', protocol] - verifylist = [ - (self.res, target), - ('protocol', protocol), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with(target, **{'protocol': protocol}) - self.assertIsNone(result) - - def test_set_source_ip_address(self): - target = self.resource['id'] - src_ip = '192.192.192.192' - arglist = [target, '--source-ip-address', src_ip] - verifylist = [ - (self.res, target), - ('source_ip_address', src_ip), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with( - target, **{'source_ip_address': src_ip}) - self.assertIsNone(result) - - def test_set_source_port(self): - target = self.resource['id'] - src_port = '32678' - arglist = [target, '--source-port', src_port] - verifylist = [ - (self.res, target), - ('source_port', src_port), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with( - target, **{'source_port': src_port}) - self.assertIsNone(result) - - def test_set_destination_ip_address(self): - target = self.resource['id'] - dst_ip = '0.1.0.1' - arglist = [target, '--destination-ip-address', dst_ip] - verifylist = [ - (self.res, target), - ('destination_ip_address', dst_ip), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with( - target, **{'destination_ip_address': dst_ip}) - self.assertIsNone(result) - - def test_set_destination_port(self): - target = self.resource['id'] - dst_port = '65432' - arglist = [target, '--destination-port', dst_port] - verifylist = [ - (self.res, target), - ('destination_port', dst_port), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with( - target, **{'destination_port': dst_port}) - self.assertIsNone(result) - - def test_set_enable_rule(self): - target = self.resource['id'] - arglist = [target, '--enable-rule'] - verifylist = [ - (self.res, target), - ('enable_rule', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with(target, **{'enabled': True}) - self.assertIsNone(result) - - def test_set_disable_rule(self): - target = self.resource['id'] - arglist = [target, '--disable-rule'] - verifylist = [ - (self.res, target), - ('disable_rule', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with(target, **{'enabled': False}) - self.assertIsNone(result) - - def test_set_action(self): - target = self.resource['id'] - action = 'reject' - arglist = [target, '--action', action] - verifylist = [ - (self.res, target), - ('action', action), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with(target, **{'action': action}) - self.assertIsNone(result) - - def test_set_enable_rule_and_disable_rule(self): - target = self.resource['id'] - arglist = [target, '--enable-rule', '--disable-rule'] - verifylist = [ - (self.res, target), - ('enable_rule', True), - ('disable_rule', True), - ] - self.assertRaises( - utils.ParserException, - self.check_parser, self.cmd, arglist, verifylist) - - def test_set_no_source_ip_address(self): - target = self.resource['id'] - arglist = [ - target, - '--no-source-ip-address', - ] - verifylist = [ - (self.res, target), - ('no_source_ip_address', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with( - target, **{'source_ip_address': None}) - self.assertIsNone(result) - - def test_set_no_source_port(self): - target = self.resource['id'] - arglist = [ - target, - '--no-source-port', - ] - verifylist = [ - (self.res, target), - ('no_source_port', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with(target, **{'source_port': None}) - self.assertIsNone(result) - - def test_set_no_destination_ip_address(self): - target = self.resource['id'] - arglist = [ - target, - '--no-destination-ip-address', - ] - verifylist = [ - (self.res, target), - ('no_destination_ip_address', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with( - target, **{'destination_ip_address': None}) - self.assertIsNone(result) - - def test_set_no_destination_port(self): - target = self.resource['id'] - arglist = [ - target, - '--no-destination-port', - ] - verifylist = [ - (self.res, target), - ('no_destination_port', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with( - target, **{'destination_port': None}) - self.assertIsNone(result) - - def test_set_source_ip_address_and_no(self): - target = self.resource['id'] - arglist = [ - target, - '--source-ip-address', '192.168.1.0/24', - '--no-source-ip-address', - ] - verifylist = [ - (self.res, target), - ('source_ip_address', '192.168.1.0/24'), - ('no_source_ip_address', True), - ] - self.assertRaises( - utils.ParserException, - self.check_parser, self.cmd, arglist, verifylist) - - def test_set_destination_ip_address_and_no(self): - target = self.resource['id'] - arglist = [ - target, - '--destination-ip-address', '192.168.2.0/24', - '--no-destination-ip-address', - ] - verifylist = [ - (self.res, target), - ('destination_ip_address', '192.168.2.0/24'), - ('no_destination_ip_address', True), - ] - self.assertRaises( - utils.ParserException, - self.check_parser, self.cmd, arglist, verifylist) - - def test_set_source_port_and_no(self): - target = self.resource['id'] - arglist = [ - target, - '--source-port', '1:12345', - '--no-source-port', - ] - verifylist = [ - (self.res, target), - ('source_port', '1:12345'), - ('no_source_port', True), - ] - self.assertRaises( - utils.ParserException, - self.check_parser, self.cmd, arglist, verifylist) - - def test_set_destination_port_and_no(self): - target = self.resource['id'] - arglist = [ - target, - '--destination-port', '1:54321', - '--no-destination-port', - ] - verifylist = [ - (self.res, target), - ('destination_port', '1:54321'), - ('no_destination_port', True), - ] - self.assertRaises( - utils.ParserException, - self.check_parser, self.cmd, arglist, verifylist) - - def test_set_and_raises(self): - self.networkclient.update_firewall_rule = mock.Mock( - side_effect=Exception) - target = self.resource['id'] - arglist = [target, '--name', 'my-name'] - verifylist = [(self.res, target), ('name', 'my-name')] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args) - - def test_set_no_destination_fwg(self): - target = self.resource['id'] - arglist = [ - target, - '--no-destination-firewall-group', - ] - verifylist = [ - (self.res, target), - ('no_destination_firewall_group', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with( - target, **{'destination_firewall_group_id': None}) - self.assertIsNone(result) - - def test_set_no_source_fwg(self): - target = self.resource['id'] - arglist = [ - target, - '--no-source-firewall-group', - ] - verifylist = [ - (self.res, target), - ('no_source_firewall_group', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with( - target, **{'source_firewall_group_id': None}) - self.assertIsNone(result) - - def test_create_with_src_fwg_and_no(self): - target = self.resource['id'] - fwg = 'my-fwg' - arglist = [ - target, - '--source-firewall-group', fwg, - '--no-source-firewall-group', - ] - verifylist = [ - (self.res, target), - ('source_firewall_group', fwg), - ('no_source_firewall_group', True), - ] - self.assertRaises( - utils.ParserException, - self.check_parser, self.cmd, arglist, verifylist) - - def test_create_with_dst_fwg_and_no(self): - target = self.resource['id'] - fwg = 'my-fwg' - arglist = [ - target, - '--destination-firewall-group', fwg, - '--no-destination-firewall-group', - ] - verifylist = [ - (self.res, target), - ('destination_firewall_group', fwg), - ('no_destination_firewall_group', True), - ] - self.assertRaises( - utils.ParserException, - self.check_parser, self.cmd, arglist, verifylist) - - -class TestUnsetFirewallRule(TestFirewallRule, common.TestUnsetFWaaS): - - def setUp(self): - super(TestUnsetFirewallRule, self).setUp() - self.networkclient.update_firewall_rule = mock.Mock( - return_value={self.res: _fwr}) - self.mocked = self.networkclient.update_firewall_rule - - def _mock_find_rule(*args, **kwargs): - return {'id': args[0]} - - self.networkclient.find_firewall_rule.side_effect = _mock_find_rule - - self.cmd = firewallrule.UnsetFirewallRule(self.app, self.namespace) - - def test_unset_protocol_and_raise(self): - self.networkclient.update_firewall_rule.side_effect = Exception - target = self.resource['id'] - arglist = [ - target, - '--protocol', - ] - verifylist = [ - (self.res, target), - ('protocol', False) - ] - self.assertRaises(utils.ParserException, self.check_parser, - self.cmd, arglist, verifylist) - - def test_unset_source_port(self): - target = self.resource['id'] - arglist = [ - target, - '--source-port', - ] - verifylist = [ - (self.res, target), - ('source_port', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with( - target, **{'source_port': None}) - self.assertIsNone(result) - - def test_unset_destination_port(self): - target = self.resource['id'] - arglist = [ - target, - '--destination-port', - ] - verifylist = [ - (self.res, target), - ('destination_port', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with( - target, **{'destination_port': None}) - self.assertIsNone(result) - - def test_unset_source_ip_address(self): - target = self.resource['id'] - arglist = [ - target, - '--source-ip-address', - ] - verifylist = [ - (self.res, target), - ('source_ip_address', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with( - target, **{'source_ip_address': None}) - self.assertIsNone(result) - - def test_unset_destination_ip_address(self): - target = self.resource['id'] - arglist = [ - target, - '--destination-ip-address', - ] - verifylist = [ - (self.res, target), - ('destination_ip_address', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with( - target, **{'destination_ip_address': None}) - self.assertIsNone(result) - - def test_unset_enable_rule(self): - target = self.resource['id'] - arglist = [ - target, - '--enable-rule', - ] - verifylist = [ - (self.res, target), - ('enable_rule', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.mocked.assert_called_once_with( - target, **{'enabled': False}) - self.assertIsNone(result) - - -class TestDeleteFirewallRule(TestFirewallRule, common.TestDeleteFWaaS): - - def setUp(self): - super(TestDeleteFirewallRule, self).setUp() - self.networkclient.delete_firewall_rule = mock.Mock(return_value=_fwr) - self.mocked = self.networkclient.delete_firewall_rule - self.cmd = firewallrule.DeleteFirewallRule(self.app, self.namespace) diff --git a/releasenotes/notes/remove-fwaas-osc-plugin-a1b2c3d4e5f6g7h8.yaml b/releasenotes/notes/remove-fwaas-osc-plugin-a1b2c3d4e5f6g7h8.yaml new file mode 100644 index 000000000..171b3eb62 --- /dev/null +++ b/releasenotes/notes/remove-fwaas-osc-plugin-a1b2c3d4e5f6g7h8.yaml @@ -0,0 +1,11 @@ +--- +deprecations: + - | + The Firewall as a Service (FWaaS) v2 OSC plugin commands have been moved + to ``python-openstackclient``. The FWaaS commands are no longer provided + by ``python-neutronclient``. Please use ``python-openstackclient`` for + the following commands: + + * ``firewall group create/delete/list/set/show/unset`` + * ``firewall group policy create/delete/list/set/show/unset/add rule/remove rule`` + * ``firewall group rule create/delete/list/set/show/unset`` diff --git a/setup.cfg b/setup.cfg index 3005e895b..282919044 100644 --- a/setup.cfg +++ b/setup.cfg @@ -79,27 +79,6 @@ openstack.neutronclient.v2 = bgp_speaker_set = neutronclient.osc.v2.dynamic_routing.bgp_speaker:SetBgpSpeaker bgp_speaker_show = neutronclient.osc.v2.dynamic_routing.bgp_speaker:ShowBgpSpeaker - firewall_group_create = neutronclient.osc.v2.fwaas.firewallgroup:CreateFirewallGroup - firewall_group_delete = neutronclient.osc.v2.fwaas.firewallgroup:DeleteFirewallGroup - firewall_group_list = neutronclient.osc.v2.fwaas.firewallgroup:ListFirewallGroup - firewall_group_set = neutronclient.osc.v2.fwaas.firewallgroup:SetFirewallGroup - firewall_group_show = neutronclient.osc.v2.fwaas.firewallgroup:ShowFirewallGroup - firewall_group_unset = neutronclient.osc.v2.fwaas.firewallgroup:UnsetFirewallGroup - firewall_group_policy_create = neutronclient.osc.v2.fwaas.firewallpolicy:CreateFirewallPolicy - firewall_group_policy_delete = neutronclient.osc.v2.fwaas.firewallpolicy:DeleteFirewallPolicy - firewall_group_policy_add_rule = neutronclient.osc.v2.fwaas.firewallpolicy:FirewallPolicyInsertRule - firewall_group_policy_list = neutronclient.osc.v2.fwaas.firewallpolicy:ListFirewallPolicy - firewall_group_policy_remove_rule = neutronclient.osc.v2.fwaas.firewallpolicy:FirewallPolicyRemoveRule - firewall_group_policy_set = neutronclient.osc.v2.fwaas.firewallpolicy:SetFirewallPolicy - firewall_group_policy_show = neutronclient.osc.v2.fwaas.firewallpolicy:ShowFirewallPolicy - firewall_group_policy_unset = neutronclient.osc.v2.fwaas.firewallpolicy:UnsetFirewallPolicy - firewall_group_rule_create = neutronclient.osc.v2.fwaas.firewallrule:CreateFirewallRule - firewall_group_rule_delete = neutronclient.osc.v2.fwaas.firewallrule:DeleteFirewallRule - firewall_group_rule_list = neutronclient.osc.v2.fwaas.firewallrule:ListFirewallRule - firewall_group_rule_set = neutronclient.osc.v2.fwaas.firewallrule:SetFirewallRule - firewall_group_rule_show = neutronclient.osc.v2.fwaas.firewallrule:ShowFirewallRule - firewall_group_rule_unset = neutronclient.osc.v2.fwaas.firewallrule:UnsetFirewallRule - bgpvpn_create = neutronclient.osc.v2.networking_bgpvpn.bgpvpn:CreateBgpvpn bgpvpn_delete = neutronclient.osc.v2.networking_bgpvpn.bgpvpn:DeleteBgpvpn bgpvpn_list = neutronclient.osc.v2.networking_bgpvpn.bgpvpn:ListBgpvpn From 9d338dc92ac51d78809c7a9d4d6ccdc603837039 Mon Sep 17 00:00:00 2001 From: lajoskatona Date: Wed, 25 Feb 2026 16:36:12 +0100 Subject: [PATCH 845/845] Remove bgpvpn client code The team agreed to move these client codes to OSC see the depends-on patch. Depends-On: https://review.opendev.org/c/openstack/python-openstackclient/+/977974 Change-Id: Ic0778a0d48f600af8997cac8b6fbab32d6365fab Signed-off-by: lajoskatona --- doc/source/cli/osc/v2/networking-bgpvpn.rst | 37 -- .../osc/v2/networking_bgpvpn/__init__.py | 0 .../osc/v2/networking_bgpvpn/bgpvpn.py | 406 -------------- .../osc/v2/networking_bgpvpn/constants.py | 30 - .../networking_bgpvpn/network_association.py | 63 --- .../v2/networking_bgpvpn/port_association.py | 314 ----------- .../networking_bgpvpn/resource_association.py | 283 ---------- .../networking_bgpvpn/router_association.py | 113 ---- .../unit/osc/v2/networking_bgpvpn/__init__.py | 0 .../unit/osc/v2/networking_bgpvpn/fakes.py | 270 --------- .../osc/v2/networking_bgpvpn/test_bgpvpn.py | 524 ------------------ .../test_resource_association.py | 320 ----------- .../test_router_association.py | 292 ---------- setup.cfg | 22 - 14 files changed, 2674 deletions(-) delete mode 100644 doc/source/cli/osc/v2/networking-bgpvpn.rst delete mode 100644 neutronclient/osc/v2/networking_bgpvpn/__init__.py delete mode 100644 neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py delete mode 100644 neutronclient/osc/v2/networking_bgpvpn/constants.py delete mode 100644 neutronclient/osc/v2/networking_bgpvpn/network_association.py delete mode 100644 neutronclient/osc/v2/networking_bgpvpn/port_association.py delete mode 100644 neutronclient/osc/v2/networking_bgpvpn/resource_association.py delete mode 100644 neutronclient/osc/v2/networking_bgpvpn/router_association.py delete mode 100644 neutronclient/tests/unit/osc/v2/networking_bgpvpn/__init__.py delete mode 100644 neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py delete mode 100644 neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py delete mode 100644 neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py delete mode 100644 neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_router_association.py diff --git a/doc/source/cli/osc/v2/networking-bgpvpn.rst b/doc/source/cli/osc/v2/networking-bgpvpn.rst deleted file mode 100644 index 46ee47e6e..000000000 --- a/doc/source/cli/osc/v2/networking-bgpvpn.rst +++ /dev/null @@ -1,37 +0,0 @@ -====== -bgpvpn -====== - -A **bgpvpn** resource contains a set of parameters to define a BGP-based VPN. -BGP-based IP VPNs networks are widely used in the industry especially for -enterprises. The networking BGP VPN project aims at supporting inter-connection -between L3VPNs and Neutron resources, i.e. Networks, Routers and Ports. - -Network v2 - -.. autoprogram-cliff:: openstack.neutronclient.v2 - :command: bgpvpn create - -.. autoprogram-cliff:: openstack.neutronclient.v2 - :command: bgpvpn delete - -.. autoprogram-cliff:: openstack.neutronclient.v2 - :command: bgpvpn list - -.. autoprogram-cliff:: openstack.neutronclient.v2 - :command: bgpvpn set - -.. autoprogram-cliff:: openstack.neutronclient.v2 - :command: bgpvpn show - -.. autoprogram-cliff:: openstack.neutronclient.v2 - :command: bgpvpn unset - -.. autoprogram-cliff:: openstack.neutronclient.v2 - :command: bgpvpn network association * - -.. autoprogram-cliff:: openstack.neutronclient.v2 - :command: bgpvpn router association * - -.. autoprogram-cliff:: openstack.neutronclient.v2 - :command: bgpvpn port association * diff --git a/neutronclient/osc/v2/networking_bgpvpn/__init__.py b/neutronclient/osc/v2/networking_bgpvpn/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py b/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py deleted file mode 100644 index cf4c69d66..000000000 --- a/neutronclient/osc/v2/networking_bgpvpn/bgpvpn.py +++ /dev/null @@ -1,406 +0,0 @@ -# Copyright (c) 2016 Juniper Networks Inc. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import logging - -from osc_lib.cli import format_columns -from osc_lib.cli.parseractions import KeyValueAction -from osc_lib.command import command -from osc_lib import exceptions -from osc_lib import utils as osc_utils -from osc_lib.utils import columns as column_util - -from neutronclient._i18n import _ -from neutronclient.osc import utils as nc_osc_utils - -LOG = logging.getLogger(__name__) - -_attr_map = ( - ('id', 'ID', column_util.LIST_BOTH), - ('project_id', 'Project', column_util.LIST_LONG_ONLY), - ('name', 'Name', column_util.LIST_BOTH), - ('type', 'Type', column_util.LIST_BOTH), - ('route_targets', 'Route Targets', column_util.LIST_LONG_ONLY), - ('import_targets', 'Import Targets', column_util.LIST_LONG_ONLY), - ('export_targets', 'Export Targets', column_util.LIST_LONG_ONLY), - ('route_distinguishers', 'Route Distinguishers', - column_util.LIST_LONG_ONLY), - ('networks', 'Associated Networks', column_util.LIST_LONG_ONLY), - ('routers', 'Associated Routers', column_util.LIST_LONG_ONLY), - ('ports', 'Associated Ports', column_util.LIST_LONG_ONLY), - ('vni', 'VNI', column_util.LIST_LONG_ONLY), - ('local_pref', 'Local Pref', column_util.LIST_LONG_ONLY), -) -_formatters = { - 'route_targets': format_columns.ListColumn, - 'import_targets': format_columns.ListColumn, - 'export_targets': format_columns.ListColumn, - 'route_distinguishers': format_columns.ListColumn, - 'networks': format_columns.ListColumn, - 'routers': format_columns.ListColumn, - 'ports': format_columns.ListColumn, -} - - -def _get_common_parser(parser, update=None): - """Adds to parser arguments common to create, set and unset commands. - - :params ArgumentParser parser: argparse object contains all command's - arguments - :params string update: Determines if it is a create command (value: None), - it is a set command (value: 'set') or if it is an - unset command (value: 'unset') - """ - ADD_RT = _("Add Route Target to import/export list") - REMOVE_RT = _("Remove Route Target from import/export list") - ADD_IMPORT_RT = _("Add Route Target to import list") - REMOVE_IMPORT_RT = _("Remove Route Target from import list") - ADD_EXPORT_RT = _("Add Route Target to export list") - REMOVE_EXPORT_RT = _("Remove Route Target from export list") - ADD_RD = _("Add Route Distinguisher to the list of Route Distinguishers " - "from which a Route Distinguishers will be picked from to " - "advertise a VPN route") - REMOVE_RD = _("Remove Route Distinguisher from the list of Route " - "Distinguishers from which a Route Distinguishers will be " - "picked from to advertise a VPN route") - REPEAT_RT = _("repeat option for multiple Route Targets") - REPEAT_RD = _("repeat option for multiple Route Distinguishers") - - def is_appended(): - return update is None or update == 'set' - - if update is None or update == 'set': - parser.add_argument( - '--name', - metavar="", - help=_("Name of the BGP VPN"), - ) - parser.add_argument( - '--route-target', - dest='route_targets', - action='append', - metavar="", - help="%s (%s)" % ((ADD_RT if is_appended() else REMOVE_RT), REPEAT_RT), - ) - if update: - parser.add_argument( - '--no-route-target' if update == 'set' else '--all-route-target', - dest='purge_route_target', - action='store_true', - help=_('Empty route target list'), - ) - parser.add_argument( - '--import-target', - dest='import_targets', - action='append', - metavar="", - help="%s (%s)" % ((ADD_IMPORT_RT if is_appended() else - REMOVE_IMPORT_RT), REPEAT_RT), - ) - if update: - parser.add_argument( - '--no-import-target' if update == 'set' else '--all-import-target', - dest='purge_import_target', - action='store_true', - help=_('Empty import route target list'), - ) - parser.add_argument( - '--export-target', - dest='export_targets', - action='append', - metavar="", - help="%s (%s)" % ((ADD_EXPORT_RT if is_appended() else - REMOVE_EXPORT_RT), REPEAT_RT), - ) - if update: - parser.add_argument( - '--no-export-target' if update == 'set' else - '--all-export-target', - dest='purge_export_target', - action='store_true', - help=_('Empty export route target list'), - ) - parser.add_argument( - '--route-distinguisher', - dest='route_distinguishers', - action='append', - metavar="", - help="%s (%s)" % ((ADD_RD if is_appended() else REMOVE_RD), REPEAT_RD), - ) - if update: - parser.add_argument( - '--no-route-distinguisher' if update == 'set' else - '--all-route-distinguisher', - dest='purge_route_distinguisher', - action='store_true', - help=_('Empty route distinguisher list'), - ) - parser.add_argument( - '--vni', type=int, - help=_('VXLAN Network Identifier to be used for this BGPVPN ' - 'when a VXLAN encapsulation is used')) - parser.add_argument( - '--local-pref', type=int, - dest='local_pref', - help=_('Default BGP LOCAL_PREF to use in route advertisements' - 'towards this BGPVPN.')) - - -def _args2body(client_manager, id, action, args): - - if (not (args.purge_route_target and args.purge_import_target and - args.purge_export_target and args.purge_route_distinguisher) and - (args.route_targets or args.import_targets or - args.export_targets or args.route_distinguishers)): - bgpvpn = client_manager.network.get_bgpvpn(id) - - attrs = {} - - if 'name' in args and args.name is not None: - attrs['name'] = str(args.name) - - if 'vni' in args and args.vni is not None: - attrs['vni'] = args.vni - - if 'local_pref' in args and args.local_pref is not None: - attrs['local_pref'] = args.local_pref - - if args.purge_route_target: - attrs['route_targets'] = [] - elif args.route_targets: - if action == 'set': - attrs['route_targets'] = list(set(bgpvpn['route_targets']) | - set(args.route_targets)) - elif action == 'unset': - attrs['route_targets'] = list(set(bgpvpn['route_targets']) - - set(args.route_targets)) - - if args.purge_import_target: - attrs['import_targets'] = [] - elif args.import_targets: - if action == 'set': - attrs['import_targets'] = list(set(bgpvpn['import_targets']) | - set(args.import_targets)) - elif action == 'unset': - attrs['import_targets'] = list(set(bgpvpn['import_targets']) - - set(args.import_targets)) - - if args.purge_export_target: - attrs['export_targets'] = [] - elif args.export_targets: - if action == 'set': - attrs['export_targets'] = list(set(bgpvpn['export_targets']) | - set(args.export_targets)) - elif action == 'unset': - attrs['export_targets'] = list(set(bgpvpn['export_targets']) - - set(args.export_targets)) - - if args.purge_route_distinguisher: - attrs['route_distinguishers'] = [] - elif args.route_distinguishers: - if action == 'set': - attrs['route_distinguishers'] = list( - set(bgpvpn['route_distinguishers']) | - set(args.route_distinguishers)) - elif action == 'unset': - attrs['route_distinguishers'] = list( - set(bgpvpn['route_distinguishers']) - - set(args.route_distinguishers)) - - return attrs - - -class CreateBgpvpn(command.ShowOne): - _description = _("Create BGP VPN resource") - - def get_parser(self, prog_name): - parser = super(CreateBgpvpn, self).get_parser(prog_name) - nc_osc_utils.add_project_owner_option_to_parser(parser) - _get_common_parser(parser) - parser.add_argument( - '--type', - default='l3', - choices=['l2', 'l3'], - help=_("BGP VPN type selection between IP VPN (l3) and Ethernet " - "VPN (l2) (default: l3)"), - ) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - attrs = {} - if parsed_args.name is not None: - attrs['name'] = str(parsed_args.name) - if parsed_args.type is not None: - attrs['type'] = parsed_args.type - if parsed_args.route_targets is not None: - attrs['route_targets'] = parsed_args.route_targets - if parsed_args.import_targets is not None: - attrs['import_targets'] = parsed_args.import_targets - if parsed_args.export_targets is not None: - attrs['export_targets'] = parsed_args.export_targets - if parsed_args.route_distinguishers is not None: - attrs['route_distinguishers'] = parsed_args.route_distinguishers - if parsed_args.vni is not None: - attrs['vni'] = parsed_args.vni - if parsed_args.local_pref is not None: - attrs['local_pref'] = parsed_args.local_pref - if 'project' in parsed_args and parsed_args.project is not None: - project_id = nc_osc_utils.find_project( - self.app.client_manager.identity, - parsed_args.project, - parsed_args.project_domain, - ).id - attrs['tenant_id'] = project_id - obj = client.create_bgpvpn(**attrs) - display_columns, columns = nc_osc_utils._get_columns(obj) - data = osc_utils.get_dict_properties(obj, columns, - formatters=_formatters) - return display_columns, data - - -class SetBgpvpn(command.Command): - _description = _("Set BGP VPN properties") - - def get_parser(self, prog_name): - parser = super(SetBgpvpn, self).get_parser(prog_name) - parser.add_argument( - 'bgpvpn', - metavar="", - help=_("BGP VPN to update (name or ID)"), - ) - _get_common_parser(parser, update='set') - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - id = client.find_bgpvpn(parsed_args.bgpvpn)['id'] - body = _args2body(self.app.client_manager, id, 'set', parsed_args) - client.update_bgpvpn(id, **body) - - -class UnsetBgpvpn(command.Command): - _description = _("Unset BGP VPN properties") - - def get_parser(self, prog_name): - parser = super(UnsetBgpvpn, self).get_parser(prog_name) - parser.add_argument( - 'bgpvpn', - metavar="", - help=_("BGP VPN to update (name or ID)"), - ) - _get_common_parser(parser, update='unset') - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - id = client.find_bgpvpn(parsed_args.bgpvpn)['id'] - body = _args2body(self.app.client_manager, id, 'unset', parsed_args) - client.update_bgpvpn(id, **body) - - -class DeleteBgpvpn(command.Command): - _description = _("Delete BGP VPN resource(s)") - - def get_parser(self, prog_name): - parser = super(DeleteBgpvpn, self).get_parser(prog_name) - parser.add_argument( - 'bgpvpns', - metavar="", - nargs="+", - help=_("BGP VPN(s) to delete (name or ID)"), - ) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - fails = 0 - for id_or_name in parsed_args.bgpvpns: - try: - id = client.find_bgpvpn(id_or_name)['id'] - client.delete_bgpvpn(id) - LOG.warning("BGP VPN %(id)s deleted", {'id': id}) - except Exception as e: - fails += 1 - LOG.error("Failed to delete BGP VPN with name or ID " - "'%(id_or_name)s': %(e)s", - {'id_or_name': id_or_name, 'e': e}) - if fails > 0: - msg = (_("Failed to delete %(fails)s of %(total)s BGP VPN.") % - {'fails': fails, 'total': len(parsed_args.bgpvpns)}) - raise exceptions.CommandError(msg) - - -class ListBgpvpn(command.Lister): - _description = _("List BGP VPN resources") - - def get_parser(self, prog_name): - parser = super(ListBgpvpn, self).get_parser(prog_name) - nc_osc_utils.add_project_owner_option_to_parser(parser) - parser.add_argument( - '--long', - action='store_true', - help=_("List additional fields in output"), - ) - parser.add_argument( - '--property', - metavar="", - default=dict(), - help=_("Filter property to apply on returned BGP VPNs (repeat to " - "filter on multiple properties)"), - action=KeyValueAction, - ) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - params = {} - if parsed_args.project is not None: - project_id = nc_osc_utils.find_project( - self.app.client_manager.identity, - parsed_args.project, - parsed_args.project_domain, - ).id - params['tenant_id'] = project_id - if parsed_args.property: - params.update(parsed_args.property) - objs = client.bgpvpns(**params) - headers, columns = column_util.get_column_definitions( - _attr_map, long_listing=parsed_args.long) - return (headers, (osc_utils.get_dict_properties( - s, columns, formatters=_formatters) for s in objs)) - - -class ShowBgpvpn(command.ShowOne): - _description = _("Show information of a given BGP VPN") - - def get_parser(self, prog_name): - parser = super(ShowBgpvpn, self).get_parser(prog_name) - parser.add_argument( - 'bgpvpn', - metavar="", - help=_("BGP VPN to display (name or ID)"), - ) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - id = client.find_bgpvpn(parsed_args.bgpvpn)['id'] - obj = client.get_bgpvpn(id) - display_columns, columns = nc_osc_utils._get_columns(obj) - data = osc_utils.get_dict_properties(obj, columns, - formatters=_formatters) - return display_columns, data diff --git a/neutronclient/osc/v2/networking_bgpvpn/constants.py b/neutronclient/osc/v2/networking_bgpvpn/constants.py deleted file mode 100644 index 7de1329a6..000000000 --- a/neutronclient/osc/v2/networking_bgpvpn/constants.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) 2016 Juniper Networks 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. -# - -BGPVPN = 'bgpvpn' -BGPVPNS = '%ss' % BGPVPN - -NETWORK_RESOURCE_NAME = 'network' -NETWORK_ASSOC = '%s_association' % NETWORK_RESOURCE_NAME -NETWORK_ASSOCS = '%ss' % NETWORK_ASSOC - -ROUTER_RESOURCE_NAME = 'router' -ROUTER_ASSOC = '%s_association' % ROUTER_RESOURCE_NAME -ROUTER_ASSOCS = '%ss' % ROUTER_ASSOC - -PORT_RESOURCE_NAME = 'port' -PORT_ASSOC = '%s_association' % PORT_RESOURCE_NAME -PORT_ASSOCS = '%ss' % PORT_ASSOC diff --git a/neutronclient/osc/v2/networking_bgpvpn/network_association.py b/neutronclient/osc/v2/networking_bgpvpn/network_association.py deleted file mode 100644 index 05c44320e..000000000 --- a/neutronclient/osc/v2/networking_bgpvpn/network_association.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (c) 2016 Juniper Networks 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. -# - -from osc_lib.utils import columns as column_util - -from neutronclient._i18n import _ -from neutronclient.osc.v2.networking_bgpvpn import constants -from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ - CreateBgpvpnResAssoc -from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ - DeleteBgpvpnResAssoc -from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ - ListBgpvpnResAssoc -from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ - ShowBgpvpnResAssoc - - -class BgpvpnNetAssoc(object): - _assoc_res_name = constants.NETWORK_RESOURCE_NAME - _resource = constants.NETWORK_ASSOC - _resource_plural = constants.NETWORK_ASSOCS - - _attr_map = ( - ('id', 'ID', column_util.LIST_BOTH), - ('tenant_id', 'Project', column_util.LIST_LONG_ONLY), - ('%s_id' % _assoc_res_name, '%s ID' % _assoc_res_name.capitalize(), - column_util.LIST_BOTH), - ) - _formatters = {} - - -class CreateBgpvpnNetAssoc(BgpvpnNetAssoc, CreateBgpvpnResAssoc): - _description = _("Create a BGP VPN network association") - pass - - -class DeleteBgpvpnNetAssoc(BgpvpnNetAssoc, DeleteBgpvpnResAssoc): - _description = _("Delete a BGP VPN network association(s) for a given BGP " - "VPN") - pass - - -class ListBgpvpnNetAssoc(BgpvpnNetAssoc, ListBgpvpnResAssoc): - _description = _("List BGP VPN network associations for a given BGP VPN") - pass - - -class ShowBgpvpnNetAssoc(BgpvpnNetAssoc, ShowBgpvpnResAssoc): - _description = _("Show information of a given BGP VPN network association") - pass diff --git a/neutronclient/osc/v2/networking_bgpvpn/port_association.py b/neutronclient/osc/v2/networking_bgpvpn/port_association.py deleted file mode 100644 index 24cd2d207..000000000 --- a/neutronclient/osc/v2/networking_bgpvpn/port_association.py +++ /dev/null @@ -1,314 +0,0 @@ -# Copyright (c) 2017 Juniper networks Inc. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import logging - -from osc_lib.cli import format_columns -from osc_lib.cli import parseractions -from osc_lib.utils import columns as column_util - -from neutronclient._i18n import _ -from neutronclient.osc.v2.networking_bgpvpn import constants -from neutronclient.osc.v2.networking_bgpvpn import resource_association - -LOG = logging.getLogger(__name__) - - -class BgpvpnPortAssoc(object): - _assoc_res_name = constants.PORT_RESOURCE_NAME - _resource = constants.PORT_ASSOC - _resource_plural = constants.PORT_ASSOCS - - _attr_map = ( - ('id', 'ID', column_util.LIST_BOTH), - ('tenant_id', 'Project', column_util.LIST_LONG_ONLY), - ('%s_id' % _assoc_res_name, '%s ID' % _assoc_res_name.capitalize(), - column_util.LIST_BOTH), - ('prefix_routes', 'Prefix Routes (BGP LOCAL_PREF)', - column_util.LIST_LONG_ONLY), - ('bgpvpn_routes', 'BGP VPN Routes (BGP LOCAL_PREF)', - column_util.LIST_LONG_ONLY), - ('advertise_fixed_ips', "Advertise Port's Fixed IPs", - column_util.LIST_LONG_ONLY), - ) - _formatters = { - 'prefix_routes': format_columns.ListColumn, - 'bgpvpn_routes': format_columns.ListColumn, - } - - def _transform_resource(self, data): - """Transforms BGP VPN port association routes property - - That permits to easily format the command output with ListColumn - formater and separate the two route types. - - {'routes': - [ - { - 'type': 'prefix', - 'local_pref': 100, - 'prefix': '8.8.8.0/27', - }, - { - 'type': 'prefix', - 'local_pref': 42, - 'prefix': '80.50.30.0/28', - }, - { - 'type': 'bgpvpn', - 'local_pref': 50, - 'bgpvpn': '157d72a9-9968-48e7-8087-6c9a9bc7a181', - }, - { - 'type': 'bgpvpn', - 'bgpvpn': 'd5c7aaab-c7e8-48b3-85ca-a115c00d3603', - }, - ], - } - - to - - { - 'prefix_routes': [ - '8.8.8.0/27 (100)', - '80.50.30.0/28 (42)', - ], - 'bgpvpn_routes': [ - '157d72a9-9968-48e7-8087-6c9a9bc7a181 (50)', - 'd5c7aaab-c7e8-48b3-85ca-a115c00d3603', - ], - } - """ - for route in data.get('routes', []): - local_pref = '' - if route.get('local_pref'): - local_pref = ' (%d)' % route.get('local_pref') - if route['type'] == 'prefix': - data.setdefault('prefix_routes', []).append( - '%s%s' % (route['prefix'], local_pref) - ) - elif route['type'] == 'bgpvpn': - data.setdefault('bgpvpn_routes', []).append( - '%s%s' % (route['bgpvpn_id'], local_pref) - ) - else: - LOG.warning("Unknown route type %s (%s).", route['type'], - route) - data.pop('routes', None) - return data - - def _get_common_parser(self, parser): - """Adds to parser arguments common to create, set and unset commands. - - :params ArgumentParser parser: argparse object contains all command's - arguments - """ - ADVERTISE_ROUTE = _("Fixed IPs of the port will be advertised to the " - "BGP VPN%s") % ( - _(' (default)') if self._action == 'create' - else "") - NOT_ADVERTISE_ROUTE = _("Fixed IPs of the port will not be advertised " - "to the BGP VPN") - - LOCAL_PREF_VALUE = _(". Optionally, can control the value of the BGP " - "LOCAL_PREF of the routes that will be " - "advertised") - - ADD_PREFIX_ROUTE = _("Add prefix route in CIDR notation%s") %\ - LOCAL_PREF_VALUE - REMOVE_PREFIX_ROUTE = _("Remove prefix route in CIDR notation") - REPEAT_PREFIX_ROUTE = _("repeat option for multiple prefix routes") - - ADD_BGVPVPN_ROUTE = _("Add BGP VPN route for route leaking%s") %\ - LOCAL_PREF_VALUE - REMOVE_BGPVPN_ROUTE = _("Remove BGP VPN route") - REPEAT_BGPVPN_ROUTE = _("repeat option for multiple BGP VPN routes") - - group_advertise_fixed_ips = parser.add_mutually_exclusive_group() - group_advertise_fixed_ips.add_argument( - '--advertise-fixed-ips', - action='store_true', - help=NOT_ADVERTISE_ROUTE if self._action == 'unset' - else ADVERTISE_ROUTE, - ) - group_advertise_fixed_ips.add_argument( - '--no-advertise-fixed-ips', - action='store_true', - help=ADVERTISE_ROUTE if self._action == 'unset' - else NOT_ADVERTISE_ROUTE, - ) - - if self._action in ['create', 'set']: - parser.add_argument( - '--prefix-route', - metavar="prefix=[,local_pref=]", - dest='prefix_routes', - action=parseractions.MultiKeyValueAction, - required_keys=['prefix'], - optional_keys=['local_pref'], - help="%s (%s)" % (ADD_PREFIX_ROUTE, REPEAT_PREFIX_ROUTE), - ) - parser.add_argument( - '--bgpvpn-route', - metavar="bgpvpn=[,local_pref=]", - dest='bgpvpn_routes', - action=parseractions.MultiKeyValueAction, - required_keys=['bgpvpn'], - optional_keys=['local_pref'], - help="%s (%s)" % (ADD_BGVPVPN_ROUTE, REPEAT_BGPVPN_ROUTE), - ) - else: - parser.add_argument( - '--prefix-route', - metavar="", - dest='prefix_routes', - action='append', - help="%s (%s)" % (REMOVE_PREFIX_ROUTE, REPEAT_PREFIX_ROUTE), - ) - parser.add_argument( - '--bgpvpn-route', - metavar="", - dest='bgpvpn_routes', - action='append', - help="%s (%s)" % (REMOVE_BGPVPN_ROUTE, REPEAT_BGPVPN_ROUTE), - ) - if self._action != 'create': - parser.add_argument( - '--no-prefix-route' if self._action == 'set' else - '--all-prefix-routes', - dest='purge_prefix_route', - action='store_true', - help=_('Empty prefix route list'), - ) - parser.add_argument( - '--no-bgpvpn-route' if self._action == 'set' else - '--all-bgpvpn-routes', - dest='purge_bgpvpn_route', - action='store_true', - help=_('Empty BGP VPN route list'), - ) - - def _args2body(self, bgpvpn_id, args): - client = self.app.client_manager.network - attrs = {} - - if self._action != 'create': - assoc = client.find_bgpvpn_port_association( - args.resource_association_id, - bgpvpn_id=bgpvpn_id) - else: - assoc = {'routes': []} - - if args.advertise_fixed_ips: - attrs['advertise_fixed_ips'] = self._action != 'unset' - elif args.no_advertise_fixed_ips: - attrs['advertise_fixed_ips'] = self._action == 'unset' - - prefix_routes = None - if 'purge_prefix_route' in args and args.purge_prefix_route: - prefix_routes = [] - else: - prefix_routes = {r['prefix']: r.get('local_pref') - for r in assoc['routes'] - if r['type'] == 'prefix'} - if args.prefix_routes: - if self._action in ['create', 'set']: - prefix_routes.update({r['prefix']: r.get('local_pref') - for r in args.prefix_routes}) - elif self._action == 'unset': - for prefix in args.prefix_routes: - prefix_routes.pop(prefix, None) - - bgpvpn_routes = None - if 'purge_bgpvpn_route' in args and args.purge_bgpvpn_route: - bgpvpn_routes = [] - else: - bgpvpn_routes = {r['bgpvpn_id']: r.get('local_pref') - for r in assoc['routes'] - if r['type'] == 'bgpvpn'} - if args.bgpvpn_routes: - if self._action == 'unset': - routes = [ - {'bgpvpn': bgpvpn} for bgpvpn in args.bgpvpn_routes - ] - else: - routes = args.bgpvpn_routes - args_bgpvpn_routes = { - client.find_bgpvpn(r['bgpvpn']).id: - r.get('local_pref') - for r in routes - } - if self._action in ['create', 'set']: - bgpvpn_routes.update(args_bgpvpn_routes) - elif self._action == 'unset': - for bgpvpn_id in args_bgpvpn_routes: - bgpvpn_routes.pop(bgpvpn_id, None) - - if prefix_routes is not None and not prefix_routes: - attrs.setdefault('routes', []) - elif prefix_routes is not None: - for prefix, local_pref in prefix_routes.items(): - route = { - 'type': 'prefix', - 'prefix': prefix, - } - if local_pref: - route['local_pref'] = int(local_pref) - attrs.setdefault('routes', []).append(route) - if bgpvpn_routes is not None and not bgpvpn_routes: - attrs.setdefault('routes', []) - elif bgpvpn_routes is not None: - for bgpvpn_id, local_pref in bgpvpn_routes.items(): - route = { - 'type': 'bgpvpn', - 'bgpvpn_id': bgpvpn_id, - } - if local_pref: - route['local_pref'] = int(local_pref) - attrs.setdefault('routes', []).append(route) - - return attrs - - -class CreateBgpvpnPortAssoc(BgpvpnPortAssoc, - resource_association.CreateBgpvpnResAssoc): - _description = _("Create a BGP VPN port association") - - -class SetBgpvpnPortAssoc(BgpvpnPortAssoc, - resource_association.SetBgpvpnResAssoc): - _description = _("Set BGP VPN port association properties") - - -class UnsetBgpvpnPortAssoc(BgpvpnPortAssoc, - resource_association.UnsetBgpvpnResAssoc): - _description = _("Unset BGP VPN port association properties") - - -class DeleteBgpvpnPortAssoc(BgpvpnPortAssoc, - resource_association.DeleteBgpvpnResAssoc): - _description = _("Delete a BGP VPN port association(s) for a given BGP " - "VPN") - - -class ListBgpvpnPortAssoc(BgpvpnPortAssoc, - resource_association.ListBgpvpnResAssoc): - _description = _("List BGP VPN port associations for a given BGP VPN") - - -class ShowBgpvpnPortAssoc(BgpvpnPortAssoc, - resource_association.ShowBgpvpnResAssoc): - _description = _("Show information of a given BGP VPN port association") diff --git a/neutronclient/osc/v2/networking_bgpvpn/resource_association.py b/neutronclient/osc/v2/networking_bgpvpn/resource_association.py deleted file mode 100644 index 83ac7d99f..000000000 --- a/neutronclient/osc/v2/networking_bgpvpn/resource_association.py +++ /dev/null @@ -1,283 +0,0 @@ -# Copyright (c) 2016 Juniper Networks Inc. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import logging - -from osc_lib.cli import parseractions -from osc_lib.command import command -from osc_lib import exceptions -from osc_lib import utils as osc_utils -from osc_lib.utils import columns as column_util - -from neutronclient._i18n import _ -from neutronclient.osc import utils as nc_osc_utils -from neutronclient.osc.v2.networking_bgpvpn import constants - -LOG = logging.getLogger(__name__) - - -class CreateBgpvpnResAssoc(command.ShowOne): - """Create a BGP VPN resource association""" - _action = 'create' - - def get_parser(self, prog_name): - parser = super(CreateBgpvpnResAssoc, self).get_parser(prog_name) - nc_osc_utils.add_project_owner_option_to_parser(parser) - parser.add_argument( - 'bgpvpn', - metavar="", - help=(_("BGP VPN to apply the %s association (name or ID)") % - self._assoc_res_name), - ) - parser.add_argument( - 'resource', - metavar="<%s>" % self._assoc_res_name, - help=(_("%s to associate the BGP VPN (name or ID)") % - self._assoc_res_name.capitalize()), - ) - - get_common_parser = getattr(self, '_get_common_parser', None) - if callable(get_common_parser): - get_common_parser(parser) - - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - bgpvpn = client.find_bgpvpn(parsed_args.bgpvpn) - find_res_method = getattr( - client, 'find_%s' % self._assoc_res_name) - assoc_res = find_res_method(parsed_args.resource) - body = {'%s_id' % self._assoc_res_name: assoc_res['id']} - if 'project' in parsed_args and parsed_args.project is not None: - project_id = nc_osc_utils.find_project( - self.app.client_manager.identity, - parsed_args.project, - parsed_args.project_domain, - ).id - body['tenant_id'] = project_id - - arg2body = getattr(self, '_args2body', None) - if callable(arg2body): - body.update( - arg2body(bgpvpn['id'], parsed_args)) - - if self._resource == constants.NETWORK_ASSOC: - obj = client.create_bgpvpn_network_association( - bgpvpn['id'], **body) - elif self._resource == constants.PORT_ASSOC: - obj = client.create_bgpvpn_port_association(bgpvpn['id'], **body) - else: - obj = client.create_bgpvpn_router_association( - bgpvpn['id'], **body) - transform = getattr(self, '_transform_resource', None) - if callable(transform): - transform(obj) - display_columns, columns = nc_osc_utils._get_columns(obj) - data = osc_utils.get_dict_properties(obj, columns, - formatters=self._formatters) - return display_columns, data - - -class SetBgpvpnResAssoc(command.Command): - """Set BGP VPN resource association properties""" - _action = 'set' - - def get_parser(self, prog_name): - parser = super(SetBgpvpnResAssoc, self).get_parser(prog_name) - parser.add_argument( - 'resource_association_id', - metavar="<%s association ID>" % self._assoc_res_name, - help=(_("%s association ID to update") % - self._assoc_res_name.capitalize()), - ) - parser.add_argument( - 'bgpvpn', - metavar="", - help=(_("BGP VPN the %s association belongs to (name or ID)") % - self._assoc_res_name), - ) - - get_common_parser = getattr(self, '_get_common_parser', None) - if callable(get_common_parser): - get_common_parser(parser) - - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - bgpvpn = client.find_bgpvpn(parsed_args.bgpvpn) - arg2body = getattr(self, '_args2body', None) - if callable(arg2body): - body = arg2body(bgpvpn['id'], parsed_args) - if self._resource == constants.NETWORK_ASSOC: - client.update_bgpvpn_network_association( - bgpvpn['id'], parsed_args.resource_association_id, **body) - elif self._resource == constants.PORT_ASSOC: - client.update_bgpvpn_port_association( - bgpvpn['id'], parsed_args.resource_association_id, **body) - else: - client.update_bgpvpn_router_association( - bgpvpn['id'], parsed_args.resource_association_id, **body) - - -class UnsetBgpvpnResAssoc(SetBgpvpnResAssoc): - """Unset BGP VPN resource association properties""" - _action = 'unset' - - -class DeleteBgpvpnResAssoc(command.Command): - """Remove a BGP VPN resource association(s) for a given BGP VPN""" - - def get_parser(self, prog_name): - parser = super(DeleteBgpvpnResAssoc, self).get_parser(prog_name) - parser.add_argument( - 'resource_association_ids', - metavar="<%s association ID>" % self._assoc_res_name, - nargs="+", - help=(_("%s association ID(s) to remove") % - self._assoc_res_name.capitalize()), - ) - parser.add_argument( - 'bgpvpn', - metavar="", - help=(_("BGP VPN the %s association belongs to (name or ID)") % - self._assoc_res_name), - ) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - bgpvpn = client.find_bgpvpn(parsed_args.bgpvpn) - fails = 0 - for id in parsed_args.resource_association_ids: - try: - if self._resource == constants.NETWORK_ASSOC: - client.delete_bgpvpn_network_association(bgpvpn['id'], id) - elif self._resource == constants.PORT_ASSOC: - client.delete_bgpvpn_port_association(bgpvpn['id'], id) - else: - client.delete_bgpvpn_router_association(bgpvpn['id'], id) - LOG.warning( - "%(assoc_res_name)s association %(id)s deleted", - {'assoc_res_name': self._assoc_res_name.capitalize(), - 'id': id}) - except Exception as e: - fails += 1 - LOG.error("Failed to delete %(assoc_res_name)s " - "association with ID '%(id)s': %(e)s", - {'assoc_res_name': self._assoc_res_name, - 'id': id, - 'e': e}) - if fails > 0: - msg = (_("Failed to delete %(fails)s of %(total)s " - "%(assoc_res_name)s BGP VPN association(s).") % - {'fails': fails, - 'total': len(parsed_args.resource_association_ids), - 'assoc_res_name': self._assoc_res_name}) - raise exceptions.CommandError(msg) - - -class ListBgpvpnResAssoc(command.Lister): - """List BGP VPN resource associations for a given BGP VPN""" - - def get_parser(self, prog_name): - parser = super(ListBgpvpnResAssoc, self).get_parser(prog_name) - parser.add_argument( - 'bgpvpn', - metavar="", - help=_("BGP VPN listed associations belong to (name or ID)"), - ) - parser.add_argument( - '--long', - action='store_true', - help=_("List additional fields in output"), - ) - parser.add_argument( - '--property', - metavar="", - help=_("Filter property to apply on returned BGP VPNs (repeat to " - "filter on multiple properties)"), - action=parseractions.KeyValueAction, - ) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - bgpvpn = client.find_bgpvpn(parsed_args.bgpvpn) - params = {} - if parsed_args.property: - params.update(parsed_args.property) - if self._resource == constants.NETWORK_ASSOC: - objs = client.bgpvpn_network_associations( - bgpvpn['id'], retrieve_all=True, **params) - elif self._resource == constants.PORT_ASSOC: - objs = client.bgpvpn_port_associations( - bgpvpn['id'], retrieve_all=True, **params) - else: - objs = client.bgpvpn_router_associations( - bgpvpn['id'], retrieve_all=True, **params) - transform = getattr(self, '_transform_resource', None) - transformed_objs = [] - if callable(transform): - for obj in objs: - transformed_objs.append(transform(obj)) - else: - transformed_objs = list(objs) - headers, columns = column_util.get_column_definitions( - self._attr_map, long_listing=parsed_args.long) - return (headers, (osc_utils.get_dict_properties( - s, columns, formatters=self._formatters) - for s in transformed_objs)) - - -class ShowBgpvpnResAssoc(command.ShowOne): - """Show information of a given BGP VPN resource association""" - - def get_parser(self, prog_name): - parser = super(ShowBgpvpnResAssoc, self).get_parser(prog_name) - parser.add_argument( - 'resource_association_id', - metavar="<%s association ID>" % self._assoc_res_name, - help=(_("%s association ID to look up") % - self._assoc_res_name.capitalize()), - ) - parser.add_argument( - 'bgpvpn', - metavar="", - help=_("BGP VPN the association belongs to (name or ID)"), - ) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.network - bgpvpn = client.find_bgpvpn(parsed_args.bgpvpn) - if self._resource == constants.NETWORK_ASSOC: - obj = client.get_bgpvpn_network_association( - bgpvpn['id'], parsed_args.resource_association_id) - elif self._resource == constants.PORT_ASSOC: - obj = client.get_bgpvpn_port_association( - bgpvpn['id'], parsed_args.resource_association_id) - else: - obj = client.get_bgpvpn_router_association( - bgpvpn['id'], parsed_args.resource_association_id) - transform = getattr(self, '_transform_resource', None) - if callable(transform): - transform(obj) - display_columns, columns = nc_osc_utils._get_columns(obj) - data = osc_utils.get_dict_properties(obj, columns, - formatters=self._formatters) - return display_columns, data diff --git a/neutronclient/osc/v2/networking_bgpvpn/router_association.py b/neutronclient/osc/v2/networking_bgpvpn/router_association.py deleted file mode 100644 index f4cdde09c..000000000 --- a/neutronclient/osc/v2/networking_bgpvpn/router_association.py +++ /dev/null @@ -1,113 +0,0 @@ -# Copyright (c) 2016 Juniper networks 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. -# - -from osc_lib.utils import columns as column_util - -from neutronclient._i18n import _ -from neutronclient.osc.v2.networking_bgpvpn import constants -from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ - CreateBgpvpnResAssoc -from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ - DeleteBgpvpnResAssoc -from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ - ListBgpvpnResAssoc -from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ - SetBgpvpnResAssoc -from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ - ShowBgpvpnResAssoc -from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ - UnsetBgpvpnResAssoc - - -class BgpvpnRouterAssoc(object): - _assoc_res_name = constants.ROUTER_RESOURCE_NAME - _resource = constants.ROUTER_ASSOC - _resource_plural = constants.ROUTER_ASSOCS - - _attr_map = ( - ('id', 'ID', column_util.LIST_BOTH), - ('tenant_id', 'Project', column_util.LIST_LONG_ONLY), - ('%s_id' % _assoc_res_name, '%s ID' % _assoc_res_name.capitalize(), - column_util.LIST_BOTH), - ('advertise_extra_routes', 'Advertise extra routes', - column_util.LIST_LONG_ONLY), - ) - _formatters = {} - - def _get_common_parser(self, parser): - """Adds to parser arguments common to create, set and unset commands. - - :params ArgumentParser parser: argparse object contains all command's - arguments - """ - ADVERTISE_ROUTES = _("Routes will be advertised to the " - "BGP VPN%s") % ( - _(' (default)') if self._action == 'create' - else "") - NOT_ADVERTISE_ROUTES = _("Routes from the router will not be " - "advertised to the BGP VPN") - - group_advertise_extra_routes = parser.add_mutually_exclusive_group() - group_advertise_extra_routes.add_argument( - '--advertise_extra_routes', - action='store_true', - help=NOT_ADVERTISE_ROUTES if self._action == 'unset' - else ADVERTISE_ROUTES, - ) - group_advertise_extra_routes.add_argument( - '--no-advertise_extra_routes', - action='store_true', - help=ADVERTISE_ROUTES if self._action == 'unset' - else NOT_ADVERTISE_ROUTES, - ) - - def _args2body(self, _, args): - attrs = {'advertise_extra_routes': False} - if args.advertise_extra_routes: - attrs['advertise_extra_routes'] = self._action != 'unset' - elif args.no_advertise_extra_routes: - attrs['advertise_extra_routes'] = self._action == 'unset' - - return attrs - - -class CreateBgpvpnRouterAssoc(BgpvpnRouterAssoc, CreateBgpvpnResAssoc): - _description = _("Create a BGP VPN router association") - pass - - -class SetBgpvpnRouterAssoc(BgpvpnRouterAssoc, SetBgpvpnResAssoc): - _description = _("Set BGP VPN router association properties") - - -class UnsetBgpvpnRouterAssoc(BgpvpnRouterAssoc, UnsetBgpvpnResAssoc): - _description = _("Unset BGP VPN router association properties") - - -class DeleteBgpvpnRouterAssoc(BgpvpnRouterAssoc, DeleteBgpvpnResAssoc): - _description = _("Delete a BGP VPN router association(s) for a given BGP " - "VPN") - pass - - -class ListBgpvpnRouterAssoc(BgpvpnRouterAssoc, ListBgpvpnResAssoc): - _description = _("List BGP VPN router associations for a given BGP VPN") - pass - - -class ShowBgpvpnRouterAssoc(BgpvpnRouterAssoc, ShowBgpvpnResAssoc): - _description = _("Show information of a given BGP VPN router association") - pass diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/__init__.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py deleted file mode 100644 index 0a2c3fe9c..000000000 --- a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/fakes.py +++ /dev/null @@ -1,270 +0,0 @@ -# Copyright (c) 2016 Juniper Networks 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 copy -from unittest import mock - -from openstack.network.v2 import bgpvpn as _bgpvpn -from openstack import resource as sdk_resource -from osc_lib.utils import columns as column_util - -from neutronclient.osc import utils as nc_osc_utils -from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ - CreateBgpvpnResAssoc -from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ - DeleteBgpvpnResAssoc -from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ - ListBgpvpnResAssoc -from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ - SetBgpvpnResAssoc -from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ - ShowBgpvpnResAssoc -from neutronclient.osc.v2.networking_bgpvpn.resource_association import\ - UnsetBgpvpnResAssoc -from neutronclient.osc.v2.networking_bgpvpn.router_association import\ - CreateBgpvpnRouterAssoc -from neutronclient.osc.v2.networking_bgpvpn.router_association import\ - SetBgpvpnRouterAssoc -from neutronclient.osc.v2.networking_bgpvpn.router_association import\ - ShowBgpvpnRouterAssoc -from neutronclient.tests.unit.osc.v2 import fakes as test_fakes - - -_FAKE_PROJECT_ID = 'fake_project_id' - - -class TestNeutronClientBgpvpn(test_fakes.TestNeutronClientOSCV2): - - def setUp(self): - super(TestNeutronClientBgpvpn, self).setUp() - self.neutronclient.find_resource = mock.Mock( - side_effect=lambda resource, name_or_id, project_id=None, - cmd_resource=None, parent_id=None, fields=None: - {'id': name_or_id, 'tenant_id': _FAKE_PROJECT_ID}) - self.neutronclient.find_resource_by_id = mock.Mock( - side_effect=lambda resource, resource_id, cmd_resource=None, - parent_id=None, fields=None: - {'id': resource_id, 'tenant_id': _FAKE_PROJECT_ID}) - nc_osc_utils.find_project = mock.Mock( - side_effect=lambda _, name_or_id, __: mock.Mock(id=name_or_id)) - - -def create_one_bgpvpn(attrs=None): - """Create a fake BGP VPN.""" - - attrs = attrs or {} - - # Set default attributes. - bgpvpn_attrs = { - 'id': 'fake_bgpvpn_id', - 'tenant_id': _FAKE_PROJECT_ID, - 'name': '', - 'type': 'l3', - 'route_targets': [], - 'import_targets': [], - 'export_targets': [], - 'route_distinguishers': [], - 'networks': [], - 'routers': [], - 'ports': [], - 'vni': 100, - 'local_pref': 777, - } - - # Overwrite default attributes. - bgpvpn_attrs.update(attrs) - return _bgpvpn.BgpVpn(**bgpvpn_attrs) - - -def create_bgpvpns(attrs=None, count=1): - """Create multiple fake BGP VPN.""" - - bgpvpns = [] - for i in range(0, count): - if attrs is None: - attrs = {'id': 'fake_id%d' % i} - elif getattr(attrs, 'id', None) is None: - attrs['id'] = 'fake_id%d' % i - bgpvpns.append(create_one_bgpvpn(attrs)) - - return bgpvpns - - -class BgpvpnFakeAssoc(object): - _assoc_res_name = 'fake_resource' - _resource = '%s_association' % _assoc_res_name - _resource_plural = '%ss' % _resource - - _attr_map = ( - ('id', 'ID', column_util.LIST_BOTH), - ('%s_id' % _assoc_res_name, '%s ID' % _assoc_res_name.capitalize(), - column_util.LIST_BOTH), - ('name', 'Name', column_util.LIST_BOTH), - ('project_id', 'Project ID', column_util.LIST_BOTH), - ) - _formatters = {} - - -class CreateBgpvpnFakeResAssoc(BgpvpnFakeAssoc, CreateBgpvpnResAssoc): - pass - - -class SetBgpvpnFakeResAssoc(BgpvpnFakeAssoc, SetBgpvpnResAssoc): - pass - - -class UnsetBgpvpnFakeResAssoc(BgpvpnFakeAssoc, UnsetBgpvpnResAssoc): - pass - - -class DeleteBgpvpnFakeResAssoc(BgpvpnFakeAssoc, DeleteBgpvpnResAssoc): - pass - - -class ListBgpvpnFakeResAssoc(BgpvpnFakeAssoc, ListBgpvpnResAssoc): - pass - - -class ShowBgpvpnFakeResAssoc(BgpvpnFakeAssoc, ShowBgpvpnResAssoc): - pass - - -class BgpvpnFakeRouterAssoc(object): - _assoc_res_name = 'fake_resource' - _resource = '%s_association' % _assoc_res_name - _resource_plural = '%ss' % _resource - - _attr_map = ( - ('id', 'ID', column_util.LIST_BOTH), - ('%s_id' % _assoc_res_name, '%s ID' % _assoc_res_name.capitalize(), - column_util.LIST_BOTH), - ('advertise_extra_routes', 'Advertise extra routes', - column_util.LIST_LONG_ONLY), - ('name', 'Name', column_util.LIST_BOTH), - ('project_id', 'Project ID', column_util.LIST_BOTH), - ) - _formatters = {} - - -class CreateBgpvpnFakeRouterAssoc(BgpvpnFakeRouterAssoc, - CreateBgpvpnRouterAssoc): - pass - - -class SetBgpvpnFakeRouterAssoc(BgpvpnFakeRouterAssoc, SetBgpvpnRouterAssoc): - pass - - -class ShowBgpvpnFakeRouterAssoc(BgpvpnFakeRouterAssoc, ShowBgpvpnRouterAssoc): - pass - - -class FakeResource(sdk_resource.Resource): - resource_key = 'fakeresource' - resources_key = 'fakeresources' - base_path = '/bgpvpn/fakeresources' - - _allow_unknown_attrs_in_body = True - - # capabilities - allow_create = True - allow_fetch = True - allow_commit = True - allow_delete = True - allow_list = True - - id = sdk_resource.Body('id') - tenant_id = sdk_resource.Body('tenant_id', deprecated=True) - project_id = sdk_resource.Body('project_id', alias='tenant_id') - - -class FakeResoureAssociation(sdk_resource.Resource): - resource_key = 'fakeresourceassociation' - resources_key = 'fakeresourceassociations' - base_path = '/bgpvpn/fakeresourceassociations' - - _allow_unknown_attrs_in_body = True - - # capabilities - allow_create = True - allow_fetch = True - allow_commit = True - allow_delete = True - allow_list = True - - id = sdk_resource.Body('id') - tenant_id = sdk_resource.Body('tenant_id', deprecated=True) - project_id = sdk_resource.Body('project_id', alias='tenant_id') - - -def create_one_resource(attrs=None): - """Create a fake resource.""" - attrs = attrs or {} - - # Set default attributes. - res_attrs = { - 'id': 'fake_resource_id', - 'tenant_id': _FAKE_PROJECT_ID, - } - - # Overwrite default attributes. - res_attrs.update(attrs) - return FakeResource(**res_attrs) - - -def create_resources(attrs=None, count=1): - """Create multiple fake resources.""" - - resources = [] - for i in range(0, count): - if attrs is None: - attrs = {'id': 'fake_id%d' % i} - elif getattr(attrs, 'id', None) is None: - attrs['id'] = 'fake_id%d' % i - resources.append(create_one_resource(attrs)) - - return resources - - -def create_one_resource_association(resource, attrs=None): - """Create a fake resource association.""" - - attrs = attrs or {} - - res_assoc_attrs = { - 'id': 'fake_association_id', - 'tenant_id': resource['tenant_id'], - 'fake_resource_id': resource['id'], - } - - # Overwrite default attributes. - res_assoc_attrs.update(attrs) - return FakeResoureAssociation(**res_assoc_attrs) - - -def create_resource_associations(resources): - """Create multiple fake resource associations.""" - - res_assocs = [] - for idx, resource in enumerate(resources): - res_assoc_attrs = { - 'id': 'fake_association_id%d' % idx, - 'tenant_id': resource['tenant_id'], - 'fake_resource_id': resource['id'], - } - res_assocs.append(copy.deepcopy(res_assoc_attrs)) - - return res_assocs diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py deleted file mode 100644 index 3f1632027..000000000 --- a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_bgpvpn.py +++ /dev/null @@ -1,524 +0,0 @@ -# Copyright (c) 2016 Juniper Networks 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 copy -import operator -from unittest import mock - -from osc_lib import exceptions -from osc_lib import utils as osc_utils -from osc_lib.utils import columns as column_util - -from neutronclient.osc.v2.networking_bgpvpn import bgpvpn -from neutronclient.tests.unit.osc.v2.networking_bgpvpn import fakes - - -columns_short = tuple(col for col, _, listing_mode in bgpvpn._attr_map - if listing_mode in (column_util.LIST_BOTH, - column_util.LIST_SHORT_ONLY)) -columns_long = tuple(col for col, _, listing_mode in bgpvpn._attr_map - if listing_mode in (column_util.LIST_BOTH, - column_util.LIST_LONG_ONLY)) -headers_short = tuple(head for _, head, listing_mode in bgpvpn._attr_map - if listing_mode in (column_util.LIST_BOTH, - column_util.LIST_SHORT_ONLY)) -headers_long = tuple(head for _, head, listing_mode in bgpvpn._attr_map - if listing_mode in (column_util.LIST_BOTH, - column_util.LIST_LONG_ONLY)) -sorted_attr_map = sorted(bgpvpn._attr_map, key=operator.itemgetter(1)) -sorted_columns = tuple(col for col, _, _ in sorted_attr_map) -sorted_headers = tuple(head for _, head, _ in sorted_attr_map) - - -def _get_data(attrs, columns=sorted_columns): - return osc_utils.get_dict_properties(attrs, columns, - formatters=bgpvpn._formatters) - - -class TestCreateBgpvpn(fakes.TestNeutronClientBgpvpn): - def setUp(self): - super(TestCreateBgpvpn, self).setUp() - self.cmd = bgpvpn.CreateBgpvpn(self.app, self.namespace) - - def test_create_bgpvpn_with_no_args(self): - fake_bgpvpn = fakes.create_one_bgpvpn() - self.networkclient.create_bgpvpn = mock.Mock( - return_value=fake_bgpvpn) - arglist = [] - verifylist = [ - ('project', None), - ('name', None), - ('type', 'l3'), - ('vni', None), - ('local_pref', None), - ('route_targets', None), - ('import_targets', None), - ('export_targets', None), - ('route_distinguishers', None), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - cols, data = self.cmd.take_action(parsed_args) - - self.networkclient.create_bgpvpn.assert_called_once_with( - **{'type': 'l3'}) - - self.assertEqual(sorted(sorted_columns), sorted(cols)) - - def test_create_bgpvpn_with_all_args(self): - attrs = { - 'tenant_id': 'new_fake_project_id', - 'name': 'fake_name', - 'type': 'l2', - 'vni': 100, - 'local_pref': 777, - 'route_targets': ['fake_rt1', 'fake_rt2', 'fake_rt3'], - 'import_targets': ['fake_irt1', 'fake_irt2', 'fake_irt3'], - 'export_targets': ['fake_ert1', 'fake_ert2', 'fake_ert3'], - 'route_distinguishers': ['fake_rd1', 'fake_rd2', 'fake_rd3'], - } - fake_bgpvpn = fakes.create_one_bgpvpn(attrs) - self.networkclient.create_bgpvpn = mock.Mock( - return_value=fake_bgpvpn) - arglist = [ - '--project', fake_bgpvpn['tenant_id'], - '--name', fake_bgpvpn['name'], - '--type', fake_bgpvpn['type'], - '--vni', str(fake_bgpvpn['vni']), - '--local-pref', str(fake_bgpvpn['local_pref']), - ] - for rt in fake_bgpvpn['route_targets']: - arglist.extend(['--route-target', rt]) - for rt in fake_bgpvpn['import_targets']: - arglist.extend(['--import-target', rt]) - for rt in fake_bgpvpn['export_targets']: - arglist.extend(['--export-target', rt]) - for rd in fake_bgpvpn['route_distinguishers']: - arglist.extend(['--route-distinguisher', rd]) - verifylist = [ - ('project', fake_bgpvpn['tenant_id']), - ('name', fake_bgpvpn['name']), - ('type', fake_bgpvpn['type']), - ('vni', fake_bgpvpn['vni']), - ('local_pref', fake_bgpvpn['local_pref']), - ('route_targets', fake_bgpvpn['route_targets']), - ('import_targets', fake_bgpvpn['import_targets']), - ('export_targets', fake_bgpvpn['export_targets']), - ('route_distinguishers', fake_bgpvpn['route_distinguishers']), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - cols, data = self.cmd.take_action(parsed_args) - - fake_bgpvpn_call = copy.deepcopy(attrs) - - self.networkclient.create_bgpvpn.assert_called_once_with( - **fake_bgpvpn_call) - self.assertEqual(sorted(sorted_columns), sorted(cols)) - - -class TestSetBgpvpn(fakes.TestNeutronClientBgpvpn): - def setUp(self): - super(TestSetBgpvpn, self).setUp() - self.networkclient.find_bgpvpn = mock.Mock( - side_effect=lambda name_or_id: {'id': name_or_id}) - self.cmd = bgpvpn.SetBgpvpn(self.app, self.namespace) - - def test_set_bgpvpn(self): - attrs = { - 'route_targets': ['set_rt1', 'set_rt2', 'set_rt3'], - 'import_targets': ['set_irt1', 'set_irt2', 'set_irt3'], - 'export_targets': ['set_ert1', 'set_ert2', 'set_ert3'], - 'route_distinguishers': ['set_rd1', 'set_rd2', 'set_rd3'], - } - fake_bgpvpn = fakes.create_one_bgpvpn(attrs) - self.networkclient.get_bgpvpn = mock.Mock( - return_value=fake_bgpvpn) - self.networkclient.update_bgpvpn = mock.Mock() - arglist = [ - fake_bgpvpn['id'], - '--name', 'set_name', - '--route-target', 'set_rt1', - '--import-target', 'set_irt1', - '--export-target', 'set_ert1', - '--route-distinguisher', 'set_rd1', - ] - verifylist = [ - ('bgpvpn', fake_bgpvpn['id']), - ('name', 'set_name'), - ('route_targets', ['set_rt1']), - ('purge_route_target', False), - ('import_targets', ['set_irt1']), - ('purge_import_target', False), - ('export_targets', ['set_ert1']), - ('purge_export_target', False), - ('route_distinguishers', ['set_rd1']), - ('purge_route_distinguisher', False), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - attrs = { - 'name': 'set_name', - 'route_targets': list(set(fake_bgpvpn['route_targets']) | - set(['set_rt1'])), - 'import_targets': list(set(fake_bgpvpn['import_targets']) | - set(['set_irt1'])), - 'export_targets': list(set(fake_bgpvpn['export_targets']) | - set(['set_ert1'])), - 'route_distinguishers': list( - set(fake_bgpvpn['route_distinguishers']) | set(['set_rd1'])), - } - self.networkclient.update_bgpvpn.assert_called_once_with( - fake_bgpvpn['id'], **attrs) - self.assertIsNone(result) - - def test_set_bgpvpn_with_purge_list(self): - fake_bgpvpn = fakes.create_one_bgpvpn() - self.networkclient.get_bgpvpn = mock.Mock( - return_value=fake_bgpvpn) - self.neutronclient.update_bgpvpn = mock.Mock() - arglist = [ - fake_bgpvpn['id'], - '--route-target', 'set_rt1', - '--no-route-target', - '--import-target', 'set_irt1', - '--no-import-target', - '--export-target', 'set_ert1', - '--no-export-target', - '--route-distinguisher', 'set_rd1', - '--no-route-distinguisher', - ] - verifylist = [ - ('bgpvpn', fake_bgpvpn['id']), - ('route_targets', ['set_rt1']), - ('purge_route_target', True), - ('import_targets', ['set_irt1']), - ('purge_import_target', True), - ('export_targets', ['set_ert1']), - ('purge_export_target', True), - ('route_distinguishers', ['set_rd1']), - ('purge_route_distinguisher', True), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - attrs = { - 'route_targets': [], - 'import_targets': [], - 'export_targets': [], - 'route_distinguishers': [], - } - self.networkclient.update_bgpvpn.assert_called_once_with( - fake_bgpvpn['id'], **attrs) - self.assertIsNone(result) - - -class TestUnsetBgpvpn(fakes.TestNeutronClientBgpvpn): - def setUp(self): - super(TestUnsetBgpvpn, self).setUp() - self.networkclient.find_bgpvpn = mock.Mock( - side_effect=lambda name_or_id: {'id': name_or_id}) - self.cmd = bgpvpn.UnsetBgpvpn(self.app, self.namespace) - - def test_unset_bgpvpn(self): - attrs = { - 'route_targets': ['unset_rt1', 'unset_rt2', 'unset_rt3'], - 'import_targets': ['unset_irt1', 'unset_irt2', 'unset_irt3'], - 'export_targets': ['unset_ert1', 'unset_ert2', 'unset_ert3'], - 'route_distinguishers': ['unset_rd1', 'unset_rd2', 'unset_rd3'], - } - fake_bgpvpn = fakes.create_one_bgpvpn(attrs) - self.networkclient.get_bgpvpn = mock.Mock( - return_value=fake_bgpvpn) - self.networkclient.update_bgpvpn = mock.Mock() - arglist = [ - fake_bgpvpn['id'], - '--route-target', 'unset_rt1', - '--import-target', 'unset_irt1', - '--export-target', 'unset_ert1', - '--route-distinguisher', 'unset_rd1', - ] - verifylist = [ - ('bgpvpn', fake_bgpvpn['id']), - ('route_targets', ['unset_rt1']), - ('purge_route_target', False), - ('import_targets', ['unset_irt1']), - ('purge_import_target', False), - ('export_targets', ['unset_ert1']), - ('purge_export_target', False), - ('route_distinguishers', ['unset_rd1']), - ('purge_route_distinguisher', False), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - attrs = { - 'route_targets': list(set(fake_bgpvpn['route_targets']) - - set(['unset_rt1'])), - 'import_targets': list(set(fake_bgpvpn['import_targets']) - - set(['unset_irt1'])), - 'export_targets': list(set(fake_bgpvpn['export_targets']) - - set(['unset_ert1'])), - 'route_distinguishers': list( - set(fake_bgpvpn['route_distinguishers']) - set(['unset_rd1'])), - } - self.networkclient.update_bgpvpn.assert_called_once_with( - fake_bgpvpn['id'], **attrs) - self.assertIsNone(result) - - def test_unset_bgpvpn_with_purge_list(self): - fake_bgpvpn = fakes.create_one_bgpvpn() - self.networkclient.show_bgpvpn = mock.Mock( - return_value=fake_bgpvpn) - self.neutronclient.update_bgpvpn = mock.Mock() - arglist = [ - fake_bgpvpn['id'], - '--route-target', 'unset_rt1', - '--all-route-target', - '--import-target', 'unset_irt1', - '--all-import-target', - '--export-target', 'unset_ert1', - '--all-export-target', - '--route-distinguisher', 'unset_rd1', - '--all-route-distinguisher', - ] - verifylist = [ - ('bgpvpn', fake_bgpvpn['id']), - ('route_targets', ['unset_rt1']), - ('purge_route_target', True), - ('import_targets', ['unset_irt1']), - ('purge_import_target', True), - ('export_targets', ['unset_ert1']), - ('purge_export_target', True), - ('route_distinguishers', ['unset_rd1']), - ('purge_route_distinguisher', True), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - attrs = { - 'route_targets': [], - 'import_targets': [], - 'export_targets': [], - 'route_distinguishers': [], - } - self.networkclient.update_bgpvpn.assert_called_once_with( - fake_bgpvpn['id'], **attrs) - self.assertIsNone(result) - - -class TestDeleteBgpvpn(fakes.TestNeutronClientBgpvpn): - def setUp(self): - super(TestDeleteBgpvpn, self).setUp() - self.networkclient.find_bgpvpn = mock.Mock( - side_effect=lambda name_or_id: {'id': name_or_id}) - self.cmd = bgpvpn.DeleteBgpvpn(self.app, self.namespace) - - def test_delete_one_bgpvpn(self): - fake_bgpvpn = fakes.create_one_bgpvpn() - self.networkclient.delete_bgpvpn = mock.Mock() - arglist = [ - fake_bgpvpn['id'], - ] - verifylist = [ - ('bgpvpns', [fake_bgpvpn['id']]), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.networkclient.delete_bgpvpn.assert_called_once_with( - fake_bgpvpn['id']) - self.assertIsNone(result) - - def test_delete_multi_bpgvpn(self): - fake_bgpvpns = fakes.create_bgpvpns(count=3) - fake_bgpvpn_ids = [fake_bgpvpn['id'] for fake_bgpvpn in fake_bgpvpns] - self.networkclient.delete_bgpvpn = mock.Mock() - arglist = fake_bgpvpn_ids - verifylist = [ - ('bgpvpns', fake_bgpvpn_ids), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.networkclient.delete_bgpvpn.assert_has_calls( - [mock.call(id) for id in fake_bgpvpn_ids]) - self.assertIsNone(result) - - def test_delete_multi_bpgvpn_with_unknown(self): - count = 3 - fake_bgpvpns = fakes.create_bgpvpns(count=count) - fake_bgpvpn_ids = [fake_bgpvpn['id'] for fake_bgpvpn in fake_bgpvpns] - - def raise_unknonw_resource(resource_path, name_or_id): - if str(count - 2) in name_or_id: - raise Exception() - self.networkclient.delete_bgpvpn = mock.Mock( - side_effect=raise_unknonw_resource) - arglist = fake_bgpvpn_ids - verifylist = [ - ('bgpvpns', fake_bgpvpn_ids), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.assertRaises(exceptions.CommandError, self.cmd.take_action, - parsed_args) - - self.networkclient.delete_bgpvpn.assert_has_calls( - [mock.call(id) for id in fake_bgpvpn_ids]) - - -class TestListBgpvpn(fakes.TestNeutronClientBgpvpn): - def setUp(self): - super(TestListBgpvpn, self).setUp() - self.cmd = bgpvpn.ListBgpvpn(self.app, self.namespace) - - def test_list_all_bgpvpn(self): - count = 3 - fake_bgpvpns = fakes.create_bgpvpns(count=count) - self.networkclient.bgpvpns = mock.Mock(return_value=fake_bgpvpns) - arglist = [] - verifylist = [] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - headers, data = self.cmd.take_action(parsed_args) - - self.networkclient.bgpvpns.assert_called_once() - self.assertEqual(headers, list(headers_short)) - self.assertListItemEqual( - list(data), - [_get_data(fake_bgpvpn, columns_short) for fake_bgpvpn - in fake_bgpvpns]) - - def test_list_all_bgpvpn_long_mode(self): - count = 3 - fake_bgpvpns = fakes.create_bgpvpns(count=count) - self.networkclient.bgpvpns = mock.Mock(return_value=fake_bgpvpns) - arglist = [ - '--long', - ] - verifylist = [ - ('long', True), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - headers, data = self.cmd.take_action(parsed_args) - - self.networkclient.bgpvpns.assert_called_once() - self.assertEqual(headers, list(headers_long)) - self.assertListItemEqual( - list(data), - [_get_data(fake_bgpvpn, columns_long) for fake_bgpvpn - in fake_bgpvpns]) - - def test_list_project_bgpvpn(self): - count = 3 - project_id = 'list_fake_project_id' - attrs = {'tenant_id': project_id} - fake_bgpvpns = fakes.create_bgpvpns(count=count, - attrs=attrs) - self.networkclient.bgpvpns = mock.Mock(return_value=fake_bgpvpns) - arglist = [ - '--project', project_id, - ] - verifylist = [ - ('project', project_id), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - headers, data = self.cmd.take_action(parsed_args) - - self.networkclient.bgpvpns.assert_called_once_with( - tenant_id=project_id) - self.assertEqual(headers, list(headers_short)) - self.assertListItemEqual( - list(data), - [_get_data(fake_bgpvpn, columns_short) for fake_bgpvpn - in fake_bgpvpns]) - - def test_list_bgpvpn_with_filters(self): - count = 3 - name = 'fake_id0' - layer_type = 'l2' - attrs = {'type': layer_type} - fake_bgpvpns = fakes.create_bgpvpns(count=count, - attrs=attrs) - returned_bgpvpn = fake_bgpvpns[0] - self.networkclient.bgpvpns = mock.Mock(return_value=[returned_bgpvpn]) - arglist = [ - '--property', 'name=%s' % name, - '--property', 'type=%s' % layer_type, - ] - verifylist = [ - ('property', {'name': name, 'type': layer_type}), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - headers, data = self.cmd.take_action(parsed_args) - - self.networkclient.bgpvpns.assert_called_once_with( - name=name, - type=layer_type) - self.assertEqual(headers, list(headers_short)) - self.assertListItemEqual(list(data), - [_get_data(returned_bgpvpn, columns_short)]) - - -class TestShowBgpvpn(fakes.TestNeutronClientBgpvpn): - def setUp(self): - super(TestShowBgpvpn, self).setUp() - self.cmd = bgpvpn.ShowBgpvpn(self.app, self.namespace) - self.networkclient.find_bgpvpn = mock.Mock( - side_effect=lambda name_or_id: {'id': name_or_id}) - - def test_show_bgpvpn(self): - fake_bgpvpn = fakes.create_one_bgpvpn() - self.networkclient.get_bgpvpn = mock.Mock( - return_value=fake_bgpvpn) - arglist = [ - fake_bgpvpn['id'], - ] - verifylist = [ - ('bgpvpn', fake_bgpvpn['id']), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - headers, data = self.cmd.take_action(parsed_args) - - self.networkclient.get_bgpvpn.assert_called_once_with( - fake_bgpvpn['id']) - self.assertEqual(sorted(sorted_columns), sorted(headers)) diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py deleted file mode 100644 index c5c478ab4..000000000 --- a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_resource_association.py +++ /dev/null @@ -1,320 +0,0 @@ -# Copyright (c) 2016 Juniper Networks 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 operator -from unittest import mock - -from osc_lib import exceptions -from osc_lib import utils as osc_utils -from osc_lib.utils import columns as column_util - -from neutronclient.tests.unit.osc.v2.networking_bgpvpn import fakes - - -columns_short = tuple(col for col, _, listing_mode - in fakes.BgpvpnFakeAssoc._attr_map - if listing_mode in (column_util.LIST_BOTH, - column_util.LIST_SHORT_ONLY)) -columns_long = tuple(col for col, _, listing_mode - in fakes.BgpvpnFakeAssoc._attr_map - if listing_mode in (column_util.LIST_BOTH, - column_util.LIST_LONG_ONLY)) -headers_short = tuple(head for _, head, listing_mode - in fakes.BgpvpnFakeAssoc._attr_map - if listing_mode in (column_util.LIST_BOTH, - column_util.LIST_SHORT_ONLY)) -headers_long = tuple(head for _, head, listing_mode - in fakes.BgpvpnFakeAssoc._attr_map - if listing_mode in (column_util.LIST_BOTH, - column_util.LIST_LONG_ONLY)) -sorted_attr_map = sorted(fakes.BgpvpnFakeAssoc._attr_map, - key=operator.itemgetter(1)) -sorted_columns = tuple(col for col, _, _ in sorted_attr_map) -sorted_headers = tuple(head for _, head, _ in sorted_attr_map) - - -def _get_data(attrs, columns=sorted_columns): - return osc_utils.get_dict_properties( - attrs, columns, formatters=fakes.BgpvpnFakeAssoc._formatters) - - -class TestCreateResAssoc(fakes.TestNeutronClientBgpvpn): - def setUp(self): - super(TestCreateResAssoc, self).setUp() - self.networkclient.find_bgpvpn = mock.Mock( - side_effect=lambda name_or_id: {'id': name_or_id}) - self.networkclient.find_fake_resource = mock.Mock( - side_effect=lambda name_or_id: {'id': name_or_id}) - self.cmd = fakes.CreateBgpvpnFakeResAssoc(self.app, self.namespace) - - def test_create_resource_association(self): - fake_bgpvpn = fakes.create_one_bgpvpn() - fake_res = fakes.create_one_resource() - fake_res_assoc = fakes.create_one_resource_association( - fake_res) - self.networkclient.create_bgpvpn_router_association = mock.Mock( - return_value=fake_res_assoc) - self.networkclient.find_bgpvpn_fake_resource_association = mock.Mock( - side_effect=lambda name_or_id: {'id': name_or_id}) - arglist = [ - fake_bgpvpn['id'], - fake_res['id'], - '--project', fake_bgpvpn['tenant_id'], - ] - verifylist = [ - ('bgpvpn', fake_bgpvpn['id']), - ('resource', fake_res['id']), - ('project', fake_bgpvpn['tenant_id']) - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - cols, data = self.cmd.take_action(parsed_args) - - fake_res_assoc_call = { - 'fake_resource_id': 'fake_resource_id', - 'tenant_id': 'fake_project_id' - } - - self.networkclient.create_bgpvpn_router_association.\ - assert_called_once_with( - fake_bgpvpn['id'], - **fake_res_assoc_call) - self.assertEqual(sorted_columns, cols) - self.assertEqual(_get_data(fake_res_assoc), data) - - -class TestSetResAssoc(fakes.TestNeutronClientBgpvpn): - def setUp(self): - super(TestSetResAssoc, self).setUp() - self.cmd = fakes.SetBgpvpnFakeResAssoc(self.app, self.namespace) - - def test_set_resource_association(self): - fake_bgpvpn = fakes.create_one_bgpvpn() - fake_res = fakes.create_one_resource() - fake_res_assoc = fakes.create_one_resource_association( - fake_res) - self.networkclient.update_bgpvpn_router_association = mock.Mock( - return_value={fakes.BgpvpnFakeAssoc._resource: fake_res_assoc}) - arglist = [ - fake_res_assoc['id'], - fake_bgpvpn['id'], - ] - verifylist = [ - ('resource_association_id', fake_res_assoc['id']), - ('bgpvpn', fake_bgpvpn['id']), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.networkclient.update_bgpvpn_router_association.\ - assert_not_called() - self.assertIsNone(result) - - -class TestDeleteResAssoc(fakes.TestNeutronClientBgpvpn): - def setUp(self): - super(TestDeleteResAssoc, self).setUp() - self.networkclient.find_bgpvpn = mock.Mock( - side_effect=lambda name_or_id: {'id': name_or_id}) - self.cmd = fakes.DeleteBgpvpnFakeResAssoc(self.app, self.namespace) - - def test_delete_one_association(self): - fake_bgpvpn = fakes.create_one_bgpvpn() - fake_res = fakes.create_one_resource() - fake_res_assoc = fakes.create_one_resource_association( - fake_res) - self.networkclient.delete_bgpvpn_router_association = \ - mock.Mock() - arglist = [ - fake_res_assoc['id'], - fake_bgpvpn['id'], - ] - verifylist = [ - ('resource_association_ids', [fake_res_assoc['id']]), - ('bgpvpn', fake_bgpvpn['id']), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.networkclient.delete_bgpvpn_router_association.\ - assert_called_once_with(fake_bgpvpn['id'], fake_res_assoc['id']) - self.assertIsNone(result) - - def test_delete_multi_bpgvpn(self): - count = 3 - fake_bgpvpn = fakes.create_one_bgpvpn() - fake_res = fakes.create_resources(count=count) - fake_res_assocs = fakes.create_resource_associations( - fake_res) - fake_res_assoc_ids = [ - fake_res_assoc['id'] for fake_res_assoc in fake_res_assocs - ] - self.networkclient.delete_bgpvpn_router_association = \ - mock.Mock() - arglist = \ - fake_res_assoc_ids + [ - fake_bgpvpn['id'] - ] - verifylist = [ - ('resource_association_ids', fake_res_assoc_ids), - ('bgpvpn', fake_bgpvpn['id']), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.networkclient.delete_bgpvpn_router_association.\ - assert_has_calls([ - mock.call( - fake_bgpvpn['id'], id) for id in fake_res_assoc_ids]) - self.assertIsNone(result) - - def test_delete_multi_bpgvpn_with_unknown(self): - count = 3 - fake_bgpvpn = fakes.create_one_bgpvpn() - fake_res = fakes.create_resources(count=count) - fake_res_assocs = fakes.create_resource_associations( - fake_res) - fake_res_assoc_ids = [ - fake_res_assoc['id'] for fake_res_assoc in fake_res_assocs - ] - - def raise_unknonw_resource(resource_path, name_or_id): - if str(count - 2) in name_or_id: - raise Exception() - self.networkclient.delete_bgpvpn_router_association = mock.Mock( - side_effect=raise_unknonw_resource) - arglist = \ - fake_res_assoc_ids + [ - fake_bgpvpn['id'] - ] - verifylist = [ - ('resource_association_ids', fake_res_assoc_ids), - ('bgpvpn', fake_bgpvpn['id']), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.assertRaises(exceptions.CommandError, self.cmd.take_action, - parsed_args) - - self.networkclient.delete_bgpvpn_router_association.\ - assert_has_calls([ - mock.call(fake_bgpvpn['id'], id) for id in fake_res_assoc_ids] - ) - - -class TestListResAssoc(fakes.TestNeutronClientBgpvpn): - def setUp(self): - super(TestListResAssoc, self).setUp() - self.networkclient.find_bgpvpn = mock.Mock( - side_effect=lambda name_or_id: {'id': name_or_id}) - self.cmd = fakes.ListBgpvpnFakeResAssoc(self.app, self.namespace) - - def test_list_bgpvpn_associations(self): - count = 3 - fake_bgpvpn = fakes.create_one_bgpvpn() - fake_res = fakes.create_resources(count=count) - fake_res_assocs = fakes.create_resource_associations( - fake_res) - self.networkclient.bgpvpn_router_associations = mock.Mock( - return_value=fake_res_assocs) - arglist = [ - fake_bgpvpn['id'], - ] - verifylist = [ - ('bgpvpn', fake_bgpvpn['id']), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - headers, data = self.cmd.take_action(parsed_args) - - self.networkclient.bgpvpn_router_associations.\ - assert_called_once_with(fake_bgpvpn['id'], retrieve_all=True) - self.assertEqual(headers, list(headers_short)) - self.assertEqual( - list(data), - [_get_data(fake_res_assoc, columns_short) for fake_res_assoc - in fake_res_assocs]) - - def test_list_bgpvpn_associations_long_mode(self): - count = 3 - fake_bgpvpn = fakes.create_one_bgpvpn() - fake_res = fakes.create_resources(count=count) - fake_res_assocs = fakes.create_resource_associations( - fake_res) - self.networkclient.bgpvpn_router_associations = mock.Mock( - return_value=fake_res_assocs) - arglist = [ - '--long', - fake_bgpvpn['id'], - ] - verifylist = [ - ('long', True), - ('bgpvpn', fake_bgpvpn['id']), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - headers, data = self.cmd.take_action(parsed_args) - - self.networkclient.bgpvpn_router_associations.\ - assert_called_once_with(fake_bgpvpn['id'], retrieve_all=True) - self.assertEqual(headers, list(headers_long)) - self.assertEqual( - list(data), - [_get_data(fake_res_assoc, columns_long) for fake_res_assoc - in fake_res_assocs]) - - -class TestShowResAssoc(fakes.TestNeutronClientBgpvpn): - def setUp(self): - super(TestShowResAssoc, self).setUp() - self.networkclient.find_bgpvpn = mock.Mock( - side_effect=lambda name_or_id: {'id': name_or_id}) - self.cmd = fakes.ShowBgpvpnFakeResAssoc(self.app, self.namespace) - - def test_show_resource_association(self): - fake_bgpvpn = fakes.create_one_bgpvpn() - fake_res = fakes.create_one_resource() - fake_res_assoc = fakes.create_one_resource_association( - fake_res) - self.networkclient.get_bgpvpn_router_association = mock.Mock( - return_value=fake_res_assoc) - arglist = [ - fake_res_assoc['id'], - fake_bgpvpn['id'], - ] - verifylist = [ - ('resource_association_id', fake_res_assoc['id']), - ('bgpvpn', fake_bgpvpn['id']), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.networkclient.get_bgpvpn_router_association.\ - assert_called_once_with(fake_bgpvpn['id'], fake_res_assoc['id']) - self.assertEqual(sorted_columns, columns) - self.assertEqual(data, _get_data(fake_res_assoc)) diff --git a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_router_association.py b/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_router_association.py deleted file mode 100644 index 71c174288..000000000 --- a/neutronclient/tests/unit/osc/v2/networking_bgpvpn/test_router_association.py +++ /dev/null @@ -1,292 +0,0 @@ -# Copyright (c) 2018 Orange SA. -# 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 operator -from unittest import mock - -from osc_lib.tests.utils import ParserException -from osc_lib import utils as osc_utils -from osc_lib.utils import columns as column_util - -from neutronclient.tests.unit.osc.v2.networking_bgpvpn import fakes - - -columns_short = tuple(col for col, _, listing_mode - in fakes.BgpvpnFakeRouterAssoc._attr_map - if listing_mode in (column_util.LIST_BOTH, - column_util.LIST_SHORT_ONLY)) -columns_long = tuple(col for col, _, listing_mode - in fakes.BgpvpnFakeRouterAssoc._attr_map - if listing_mode in (column_util.LIST_BOTH, - column_util.LIST_LONG_ONLY)) -headers_short = tuple(head for _, head, listing_mode - in fakes.BgpvpnFakeRouterAssoc._attr_map - if listing_mode in (column_util.LIST_BOTH, - column_util.LIST_SHORT_ONLY)) -headers_long = tuple(head for _, head, listing_mode - in fakes.BgpvpnFakeRouterAssoc._attr_map - if listing_mode in (column_util.LIST_BOTH, - column_util.LIST_LONG_ONLY)) -sorted_attr_map = sorted(fakes.BgpvpnFakeRouterAssoc._attr_map, - key=operator.itemgetter(1)) -sorted_columns = tuple(col for col, _, _ in sorted_attr_map) -sorted_headers = tuple(head for _, head, _ in sorted_attr_map) - - -def _get_data(attrs, columns=sorted_columns): - return osc_utils.get_dict_properties( - attrs, columns, formatters=fakes.BgpvpnFakeAssoc._formatters) - - -class TestCreateRouterAssoc(fakes.TestNeutronClientBgpvpn): - def setUp(self): - super(TestCreateRouterAssoc, self).setUp() - self.cmd = fakes.CreateBgpvpnFakeRouterAssoc(self.app, self.namespace) - self.fake_bgpvpn = fakes.create_one_bgpvpn() - self.fake_router = fakes.create_one_resource() - self.networkclient.find_bgpvpn = mock.Mock( - side_effect=lambda name_or_id: {'id': name_or_id}) - self.networkclient.find_fake_resource = mock.Mock( - side_effect=lambda name_or_id: {'id': name_or_id}) - - def _build_args(self, param=None): - arglist_base = [ - self.fake_bgpvpn['id'], - self.fake_router['id'], - '--project', self.fake_bgpvpn['tenant_id'] - ] - if param is not None: - if isinstance(param, list): - arglist_base.extend(param) - else: - arglist_base.append(param) - return arglist_base - - def _build_verify_list(self, param=None): - verifylist = [ - ('bgpvpn', self.fake_bgpvpn['id']), - ('resource', self.fake_router['id']), - ('project', self.fake_bgpvpn['tenant_id']) - ] - if param is not None: - verifylist.append(param) - return verifylist - - def _exec_create_router_association( - self, fake_res_assoc, arglist, verifylist): - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - cols, data = self.cmd.take_action(parsed_args) - - fake_res_assoc_call = { - 'fake_resource_id': 'fake_resource_id', - 'tenant_id': 'fake_project_id' - } - for key, value in verifylist: - if value not in fake_res_assoc_call.values(): - fake_res_assoc_call[key] = value - fake_res_assoc_call.pop('bgpvpn') - - self.networkclient.create_bgpvpn_router_association.\ - assert_called_once_with( - self.fake_bgpvpn['id'], - **fake_res_assoc_call) - return cols, data - - def test_create_router_association(self): - fake_res_assoc = fakes.create_one_resource_association( - self.fake_router) - - self.networkclient.create_bgpvpn_router_association = mock.Mock( - return_value={ - fakes.BgpvpnFakeRouterAssoc._resource: fake_res_assoc, - 'advertise_extra_routes': True}) - - arglist = self._build_args() - # advertise_extra_routes will be False since none - # of the mutually exclusive args present - verifylist = self._build_verify_list(('advertise_extra_routes', False)) - - self._exec_create_router_association( - fake_res_assoc, arglist, verifylist) - - def test_create_router_association_advertise(self): - fake_res_assoc = fakes.create_one_resource_association( - self.fake_router, - {'advertise_extra_routes': True}) - - self.networkclient.create_bgpvpn_router_association = mock.Mock( - return_value=fake_res_assoc) - - arglist = self._build_args('--advertise_extra_routes') - verifylist = self._build_verify_list(('advertise_extra_routes', True)) - - cols, data = self._exec_create_router_association( - fake_res_assoc, arglist, verifylist) - self.assertEqual(sorted_columns, cols) - self.assertEqual(_get_data(fake_res_assoc), data) - - def test_create_router_association_no_advertise(self): - fake_res_assoc = fakes.create_one_resource_association( - self.fake_router, - {'advertise_extra_routes': False}) - - self.networkclient.create_bgpvpn_router_association = mock.Mock( - return_value=fake_res_assoc) - - arglist = self._build_args('--no-advertise_extra_routes') - verifylist = self._build_verify_list(('advertise_extra_routes', False)) - - cols, data = self._exec_create_router_association( - fake_res_assoc, arglist, verifylist) - self.assertEqual(sorted_columns, cols) - self.assertEqual(_get_data(fake_res_assoc), data) - - def test_create_router_association_advertise_fault(self): - arglist = self._build_args( - ['--advertise_extra_routes', '--no-advertise_extra_routes']) - - try: - self._exec_create_router_association(None, arglist, None) - except ParserException as e: - self.assertEqual(format(e), 'Argument parse failed') - - def test_router_association_unknown_arg(self): - arglist = self._build_args('--unknown arg') - - try: - self._exec_create_router_association(None, arglist, None) - except ParserException as e: - self.assertEqual(format(e), 'Argument parse failed') - - -class TestSetRouterAssoc(fakes.TestNeutronClientBgpvpn): - - def setUp(self): - super(TestSetRouterAssoc, self).setUp() - self.cmd = fakes.SetBgpvpnFakeRouterAssoc(self.app, self.namespace) - self.fake_bgpvpn = fakes.create_one_bgpvpn() - self.fake_router = fakes.create_one_resource() - self.networkclient.find_bgpvpn = mock.Mock( - side_effect=lambda name_or_id: {'id': name_or_id}) - - def _build_args(self, fake_res_assoc, param=None): - arglist_base = [ - fake_res_assoc['id'], - self.fake_bgpvpn['id'] - ] - if param is not None: - if isinstance(param, list): - arglist_base.extend(param) - else: - arglist_base.append(param) - return arglist_base - - def _build_verify_list(self, fake_res_assoc, param=None): - verifylist = [ - ('resource_association_id', fake_res_assoc['id']), - ('bgpvpn', self.fake_bgpvpn['id']) - ] - if param is not None: - verifylist.append(param) - return verifylist - - def test_set_router_association_no_advertise(self): - fake_res_assoc = fakes.create_one_resource_association( - self.fake_router, - {'advertise_extra_routes': True}) - self.networkclient.update_bgpvpn_router_association = \ - mock.Mock() - - arglist = self._build_args( - fake_res_assoc, - '--no-advertise_extra_routes') - verifylist = [ - ('resource_association_id', fake_res_assoc['id']), - ('bgpvpn', self.fake_bgpvpn['id']), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.networkclient.update_bgpvpn_router_association.\ - assert_called_once_with( - self.fake_bgpvpn['id'], - fake_res_assoc['id'], - **{'advertise_extra_routes': False} - ) - self.assertIsNone(result) - - def test_set_router_association_advertise(self): - fake_res_assoc = fakes.create_one_resource_association( - self.fake_router, - {'advertise_extra_routes': False}) - self.networkclient.update_bgpvpn_router_association = \ - mock.Mock() - - arglist = self._build_args( - fake_res_assoc, - '--advertise_extra_routes') - verifylist = [ - ('resource_association_id', fake_res_assoc['id']), - ('bgpvpn', self.fake_bgpvpn['id']), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.networkclient.update_bgpvpn_router_association.\ - assert_called_once_with( - self.fake_bgpvpn['id'], - fake_res_assoc['id'], - **{'advertise_extra_routes': True} - ) - self.assertIsNone(result) - - -class TestShowRouterAssoc(fakes.TestNeutronClientBgpvpn): - def setUp(self): - super(TestShowRouterAssoc, self).setUp() - self.cmd = fakes.ShowBgpvpnFakeRouterAssoc(self.app, self.namespace) - self.networkclient.find_bgpvpn = mock.Mock( - side_effect=lambda name_or_id: {'id': name_or_id}) - - def test_show_router_association(self): - fake_bgpvpn = fakes.create_one_bgpvpn() - fake_res = fakes.create_one_resource() - fake_res_assoc = fakes.create_one_resource_association( - fake_res, - {'advertise_extra_routes': True}) - self.networkclient.get_bgpvpn_router_association = mock.Mock( - return_value=fake_res_assoc) - arglist = [ - fake_res_assoc['id'], - fake_bgpvpn['id'], - ] - verifylist = [ - ('resource_association_id', fake_res_assoc['id']), - ('bgpvpn', fake_bgpvpn['id']), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - cols, data = self.cmd.take_action(parsed_args) - - self.networkclient.get_bgpvpn_router_association.\ - assert_called_once_with(fake_bgpvpn['id'], fake_res_assoc['id']) - self.assertEqual(sorted_columns, cols) - self.assertEqual(data, _get_data(fake_res_assoc)) diff --git a/setup.cfg b/setup.cfg index 282919044..a995bae67 100644 --- a/setup.cfg +++ b/setup.cfg @@ -79,28 +79,6 @@ openstack.neutronclient.v2 = bgp_speaker_set = neutronclient.osc.v2.dynamic_routing.bgp_speaker:SetBgpSpeaker bgp_speaker_show = neutronclient.osc.v2.dynamic_routing.bgp_speaker:ShowBgpSpeaker - bgpvpn_create = neutronclient.osc.v2.networking_bgpvpn.bgpvpn:CreateBgpvpn - bgpvpn_delete = neutronclient.osc.v2.networking_bgpvpn.bgpvpn:DeleteBgpvpn - bgpvpn_list = neutronclient.osc.v2.networking_bgpvpn.bgpvpn:ListBgpvpn - bgpvpn_set = neutronclient.osc.v2.networking_bgpvpn.bgpvpn:SetBgpvpn - bgpvpn_show = neutronclient.osc.v2.networking_bgpvpn.bgpvpn:ShowBgpvpn - bgpvpn_unset = neutronclient.osc.v2.networking_bgpvpn.bgpvpn:UnsetBgpvpn - bgpvpn_network_association_create = neutronclient.osc.v2.networking_bgpvpn.network_association:CreateBgpvpnNetAssoc - bgpvpn_network_association_delete = neutronclient.osc.v2.networking_bgpvpn.network_association:DeleteBgpvpnNetAssoc - bgpvpn_network_association_list = neutronclient.osc.v2.networking_bgpvpn.network_association:ListBgpvpnNetAssoc - bgpvpn_network_association_show = neutronclient.osc.v2.networking_bgpvpn.network_association:ShowBgpvpnNetAssoc - bgpvpn_router_association_create = neutronclient.osc.v2.networking_bgpvpn.router_association:CreateBgpvpnRouterAssoc - bgpvpn_router_association_delete = neutronclient.osc.v2.networking_bgpvpn.router_association:DeleteBgpvpnRouterAssoc - bgpvpn_router_association_list = neutronclient.osc.v2.networking_bgpvpn.router_association:ListBgpvpnRouterAssoc - bgpvpn_router_association_set = neutronclient.osc.v2.networking_bgpvpn.router_association:SetBgpvpnRouterAssoc - bgpvpn_router_association_show = neutronclient.osc.v2.networking_bgpvpn.router_association:ShowBgpvpnRouterAssoc - bgpvpn_router_association_unset = neutronclient.osc.v2.networking_bgpvpn.router_association:UnsetBgpvpnRouterAssoc - bgpvpn_port_association_create = neutronclient.osc.v2.networking_bgpvpn.port_association:CreateBgpvpnPortAssoc - bgpvpn_port_association_set = neutronclient.osc.v2.networking_bgpvpn.port_association:SetBgpvpnPortAssoc - bgpvpn_port_association_unset = neutronclient.osc.v2.networking_bgpvpn.port_association:UnsetBgpvpnPortAssoc - bgpvpn_port_association_delete = neutronclient.osc.v2.networking_bgpvpn.port_association:DeleteBgpvpnPortAssoc - bgpvpn_port_association_list = neutronclient.osc.v2.networking_bgpvpn.port_association:ListBgpvpnPortAssoc - bgpvpn_port_association_show = neutronclient.osc.v2.networking_bgpvpn.port_association:ShowBgpvpnPortAssoc network_loggable_resources_list = neutronclient.osc.v2.logging.network_log:ListLoggableResource network_log_create = neutronclient.osc.v2.logging.network_log:CreateNetworkLog