From 578c39865d41158de185b90ca4cecc7fbb8f59ae Mon Sep 17 00:00:00 2001 From: Matt Riedemann Date: Thu, 18 Aug 2016 10:03:11 -0400 Subject: [PATCH] Deprecate network-* commands and clamp to microversion 2.35 This introduces a helper to clamp the client microversion to 2.35, which is the last version to support the network proxy. We print a deprecation warning if those commands are used, and mark them as deprecated in the help text. This is a network-specific user-friendly bit of sugar to make sure that nova-network users aren't cut out before we actually drop the support for it on the server side. Note that quotas and limits are special because only the network related resources in those are not returned with 2.36. So this change handles 2.36 separately for quota-update and quota-class-update, and deprecates the network resource quota update arguments for <2.35 as an indication those are going away. As expected, several of the functional tests have to be updated to work with the new world that is microversion 2.36. Related to blueprint deprecate-api-proxies Co-Authored-By: Matt Riedemann Change-Id: Id68c2dbef29b201aa7c8ef9417432feb5596529a --- novaclient/__init__.py | 2 +- novaclient/tests/functional/base.py | 11 +- .../functional/v2/legacy/test_servers.py | 3 +- .../tests/functional/v2/test_networks.py | 46 ++++ novaclient/tests/functional/v2/test_quotas.py | 18 +- .../tests/functional/v2/test_readonly_nova.py | 14 ++ .../functional/v2/test_virtual_interface.py | 3 +- novaclient/tests/unit/v2/test_shell.py | 68 +++++ novaclient/v2/contrib/tenant_networks.py | 5 + novaclient/v2/shell.py | 233 +++++++++++++++++- ...eprecate-network-cli-f0a539528be594d3.yaml | 67 +++++ 11 files changed, 461 insertions(+), 9 deletions(-) create mode 100644 novaclient/tests/functional/v2/test_networks.py create mode 100644 releasenotes/notes/deprecate-network-cli-f0a539528be594d3.yaml diff --git a/novaclient/__init__.py b/novaclient/__init__.py index 6b728d1f9..942f43cad 100644 --- a/novaclient/__init__.py +++ b/novaclient/__init__.py @@ -25,4 +25,4 @@ API_MIN_VERSION = api_versions.APIVersion("2.1") # when client supported the max version, and bumped sequentially, otherwise # the client may break due to server side new version may include some # backward incompatible change. -API_MAX_VERSION = api_versions.APIVersion("2.35") +API_MAX_VERSION = api_versions.APIVersion("2.36") diff --git a/novaclient/tests/functional/base.py b/novaclient/tests/functional/base.py index 494d8aab6..0b2190350 100644 --- a/novaclient/tests/functional/base.py +++ b/novaclient/tests/functional/base.py @@ -214,7 +214,16 @@ class ClientTestBase(testtools.TestCase): # pick some reasonable flavor / image combo self.flavor = pick_flavor(self.client.flavors.list()) self.image = pick_image(self.glance.images.list()) - self.network = pick_network(self.client.networks.list()) + + tested_api_version = self.client.api_version + proxy_api_version = novaclient.api_versions.APIVersion('2.35') + if tested_api_version > proxy_api_version: + self.client.api_version = proxy_api_version + try: + # TODO(mriedem): Get the networks from neutron if using neutron. + self.network = pick_network(self.client.networks.list()) + finally: + self.client.api_version = tested_api_version # create a CLI client in case we'd like to do CLI # testing. tempest.lib does this really weird thing where it diff --git a/novaclient/tests/functional/v2/legacy/test_servers.py b/novaclient/tests/functional/v2/legacy/test_servers.py index a06b3037a..d55fed21c 100644 --- a/novaclient/tests/functional/v2/legacy/test_servers.py +++ b/novaclient/tests/functional/v2/legacy/test_servers.py @@ -56,13 +56,12 @@ class TestServersBootNovaClient(base.ClientTestBase): self._boot_server_with_legacy_bdm() def test_boot_server_with_net_name(self): - network = self.client.networks.list()[0] server_info = self.nova("boot", params=( "%(name)s --flavor %(flavor)s --image %(image)s --poll " "--nic net-name=%(net-name)s" % {"name": str(uuid.uuid4()), "image": self.image.id, "flavor": self.flavor.id, - "net-name": network.label})) + "net-name": self.network.label})) server_id = self._get_value_from_the_table(server_info, "id") self.client.servers.delete(server_id) diff --git a/novaclient/tests/functional/v2/test_networks.py b/novaclient/tests/functional/v2/test_networks.py new file mode 100644 index 000000000..6fa3467f6 --- /dev/null +++ b/novaclient/tests/functional/v2/test_networks.py @@ -0,0 +1,46 @@ +# Copyright 2016 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. + +from novaclient.tests.functional import base + + +class TestNetworkCommandsV2_36(base.ClientTestBase): + """Deprecated network command functional tests.""" + + # Proxy APIs were deprecated in 2.36 but the CLI should fallback to 2.35 + # and emit a warning. + COMPUTE_API_VERSION = "2.36" + + def test_command_deprecation(self): + output = self.nova('network-list', merge_stderr=True) + self.assertIn( + 'is deprecated', output, + 'network-list command did not print deprecation warning') + + def test_limits(self): + """Tests that 2.36 won't return network-related resource limits and + the CLI output won't show them. + """ + output = self.nova('limits') + # assert that SecurityGroups isn't in the table output + self.assertRaises(ValueError, self._get_value_from_the_table, + output, 'SecurityGroups') + + def test_quota_show(self): + """Tests that 2.36 won't return network-related resource quotas and + the CLI output won't show them. + """ + output = self.nova('quota-show') + # assert that security_groups isn't in the table output + self.assertRaises(ValueError, self._get_value_from_the_table, + output, 'security_groups') diff --git a/novaclient/tests/functional/v2/test_quotas.py b/novaclient/tests/functional/v2/test_quotas.py index fdfb06ac3..1d4e6af31 100644 --- a/novaclient/tests/functional/v2/test_quotas.py +++ b/novaclient/tests/functional/v2/test_quotas.py @@ -13,10 +13,10 @@ from novaclient.tests.functional.v2.legacy import test_quotas -class TestQuotasNovaClient(test_quotas.TestQuotasNovaClient): +class TestQuotasNovaClient2_35(test_quotas.TestQuotasNovaClient): """Nova quotas functional tests.""" - COMPUTE_API_VERSION = "2.latest" + COMPUTE_API_VERSION = "2.35" _quota_resources = ['instances', 'cores', 'ram', 'floating_ips', 'fixed_ips', 'metadata_items', @@ -47,3 +47,17 @@ class TestQuotasNovaClient(test_quotas.TestQuotasNovaClient): for quota_name in self._quota_resources: self.assertEqual(getattr(original_quotas, quota_name), getattr(updated_quotas, quota_name) - difference) + + +class TestQuotasNovaClient2_36(TestQuotasNovaClient2_35): + """Nova quotas functional tests.""" + + COMPUTE_API_VERSION = "2.latest" + + # The 2.36 microversion stops proxying network quota resources like + # floating/fixed IPs and security groups/rules. + _quota_resources = ['instances', 'cores', 'ram', + 'metadata_items', 'injected_files', + 'injected_file_content_bytes', + 'injected_file_path_bytes', 'key_pairs', + 'server_groups', 'server_group_members'] diff --git a/novaclient/tests/functional/v2/test_readonly_nova.py b/novaclient/tests/functional/v2/test_readonly_nova.py index 7ef948eea..aee408c9c 100644 --- a/novaclient/tests/functional/v2/test_readonly_nova.py +++ b/novaclient/tests/functional/v2/test_readonly_nova.py @@ -10,6 +10,10 @@ # License for the specific language governing permissions and limitations # under the License. +import six +from tempest.lib import exceptions + +from novaclient import api_versions from novaclient.tests.functional.v2.legacy import test_readonly_nova @@ -22,3 +26,13 @@ class SimpleReadOnlyNovaClientTest( """ COMPUTE_API_VERSION = "2.latest" + + def test_admin_image_list(self): + # The nova images proxy API returns a 404 after 2.35. + if self.client.api_version > api_versions.APIVersion('2.35'): + ex = self.assertRaises(exceptions.CommandFailed, + super(SimpleReadOnlyNovaClientTest, self). + test_admin_image_list) + self.assertIn('NotFound', six.text_type(ex)) + else: + super(SimpleReadOnlyNovaClientTest, self).test_admin_image_list() diff --git a/novaclient/tests/functional/v2/test_virtual_interface.py b/novaclient/tests/functional/v2/test_virtual_interface.py index 46f682485..cef930266 100644 --- a/novaclient/tests/functional/v2/test_virtual_interface.py +++ b/novaclient/tests/functional/v2/test_virtual_interface.py @@ -22,7 +22,6 @@ class TestVirtualInterfacesNovaClient( def test_virtual_interface_list(self): output = super(TestVirtualInterfacesNovaClient, self).test_virtual_interface_list() - network = self.client.networks.list()[0] - self.assertEqual(network.id, + self.assertEqual(self.network.id, self._get_column_value_from_single_row_table( output, "Network ID")) diff --git a/novaclient/tests/unit/v2/test_shell.py b/novaclient/tests/unit/v2/test_shell.py index 821f91081..db8f1abf4 100644 --- a/novaclient/tests/unit/v2/test_shell.py +++ b/novaclient/tests/unit/v2/test_shell.py @@ -693,6 +693,38 @@ class ShellTest(utils.TestCase): }, ) + @mock.patch('novaclient.v2.client.Client.has_neutron', return_value=False) + def test_boot_nics_net_name_nova_net_2_36(self, has_neutron): + orig_find_network = novaclient.v2.shell._find_network_id_novanet + + def stubbed_find_network(cs, net_name): + # assert that we dropped back to 2.35 + self.assertEqual(api_versions.APIVersion('2.35'), + cs.client.api_version) + return orig_find_network(cs, net_name) + + cmd = ('boot --image %s --flavor 1 ' + '--nic net-name=1 some-server' % FAKE_UUID_1) + with mock.patch.object(novaclient.v2.shell, '_find_network_id_novanet', + side_effect=stubbed_find_network) as find_net: + self.run_command(cmd, api_version='2.36') + find_net.assert_called_once_with(self.shell.cs, '1') + self.assert_called_anytime( + 'POST', '/servers', + { + 'server': { + 'flavorRef': '1', + 'name': 'some-server', + 'imageRef': FAKE_UUID_1, + 'min_count': 1, + 'max_count': 1, + 'networks': [ + {'uuid': '1'}, + ], + }, + }, + ) + @mock.patch('novaclient.v2.client.Client.has_neutron', return_value=True) def test_boot_nics_net_name_neutron(self, has_neutron): cmd = ('boot --image %s --flavor 1 ' @@ -3271,3 +3303,39 @@ class PollForStatusTestCase(utils.TestCase): action=action, show_progress=True, silent=False) + + +class ShellUtilTest(utils.TestCase): + def test_deprecated_network_newer(self): + @novaclient.v2.shell.deprecated_network + def tester(cs): + 'foo' + self.assertEqual(api_versions.APIVersion('2.35'), + cs.api_version) + + cs = mock.MagicMock() + cs.api_version = api_versions.APIVersion('2.9999') + tester(cs) + self.assertEqual('DEPRECATED: foo', tester.__doc__) + + def test_deprecated_network_older(self): + @novaclient.v2.shell.deprecated_network + def tester(cs): + 'foo' + # since we didn't need to adjust the api_version the mock won't + # have cs.client.api_version set on it + self.assertFalse(hasattr(cs, 'client')) + # we have to set the attribute back on cs so the decorator can + # set the value on it when we return from this wrapped function + setattr(cs, 'client', mock.MagicMock()) + + cs = mock.MagicMock() + cs.api_version = api_versions.APIVersion('2.1') + # we have to delete the cs.client attribute so hasattr won't return a + # false positive in the wrapped function + del cs.client + tester(cs) + self.assertEqual('DEPRECATED: foo', tester.__doc__) + # the deprecated_network decorator will set cs.client.api_version + # after calling the wrapped function + self.assertEqual(cs.api_version, cs.api_version) diff --git a/novaclient/v2/contrib/tenant_networks.py b/novaclient/v2/contrib/tenant_networks.py index 56c989abc..c088f777c 100644 --- a/novaclient/v2/contrib/tenant_networks.py +++ b/novaclient/v2/contrib/tenant_networks.py @@ -15,6 +15,7 @@ from novaclient import base from novaclient.i18n import _ from novaclient import utils +from novaclient.v2 import shell class TenantNetwork(base.Resource): @@ -60,6 +61,7 @@ def do_net(cs, args): @utils.arg('network_id', metavar='', help='ID of network') +@shell.deprecated_network def do_tenant_network_show(cs, args): """ Show a tenant network. @@ -75,6 +77,7 @@ def do_net_list(cs, args): do_tenant_network_list(cs, args) +@shell.deprecated_network def do_tenant_network_list(cs, args): """ List tenant networks. @@ -106,6 +109,7 @@ def do_net_create(cs, args): 'cidr', metavar='', help=_('IP block to allocate from (ex. 172.16.0.0/24 or 2001:DB8::/64)')) +@shell.deprecated_network def do_tenant_network_create(cs, args): """ Create a tenant network. @@ -123,6 +127,7 @@ def do_net_delete(cs, args): @utils.arg('network_id', metavar='', help='ID of network') +@shell.deprecated_network def do_tenant_network_delete(cs, args): """ Delete a tenant network. diff --git a/novaclient/v2/shell.py b/novaclient/v2/shell.py index 971e9879d..b49656a55 100644 --- a/novaclient/v2/shell.py +++ b/novaclient/v2/shell.py @@ -21,6 +21,7 @@ from __future__ import print_function import argparse import copy import datetime +import functools import getpass import locale import logging @@ -76,6 +77,28 @@ def emit_image_deprecation_warning(command_name): 'instead.' % command_name, file=sys.stderr) +def deprecated_network(fn): + @functools.wraps(fn) + def wrapped(cs, *args, **kwargs): + command_name = '-'.join(fn.__name__.split('_')[1:]) + print('WARNING: Command %s is deprecated and will be removed ' + 'after Nova 15.0.0 is released. Use python-neutronclient ' + 'or python-openstackclient instead.' % command_name, + file=sys.stderr) + # The network proxy API methods were deprecated in 2.36 and will return + # a 404 so we fallback to 2.35 to maintain a transition for CLI users. + want_version = api_versions.APIVersion('2.35') + cur_version = cs.api_version + if cs.api_version > want_version: + cs.api_version = want_version + try: + return fn(cs, *args, **kwargs) + finally: + cs.api_version = cur_version + wrapped.__doc__ = 'DEPRECATED: ' + fn.__doc__ + return wrapped + + def _key_value_pairing(text): try: (k, v) = text.split('=', 1) @@ -954,6 +977,7 @@ def do_flavor_access_remove(cs, args): @utils.arg( 'project_id', metavar='', help=_('The ID of the project.')) +@deprecated_network def do_scrub(cs, args): """Delete networks and security groups associated with a project.""" networks_list = cs.networks.list() @@ -975,6 +999,7 @@ def do_scrub(cs, args): metavar='', help=_('Comma-separated list of fields to display. ' 'Use the show command to see which fields are available.')) +@deprecated_network def do_network_list(cs, args): """Print a list of available networks.""" network_list = cs.networks.list() @@ -989,6 +1014,7 @@ def do_network_list(cs, args): 'network', metavar='', help=_("UUID or label of network.")) +@deprecated_network def do_network_show(cs, args): """Show details about the given network.""" network = utils.find_resource(cs.networks, args.network) @@ -999,6 +1025,7 @@ def do_network_show(cs, args): 'network', metavar='', help=_("UUID or label of network.")) +@deprecated_network def do_network_delete(cs, args): """Delete network by label or id.""" network = utils.find_resource(cs.networks, args.network) @@ -1025,6 +1052,7 @@ def do_network_delete(cs, args): 'network', metavar='', help=_("UUID of network.")) +@deprecated_network def do_network_disassociate(cs, args): """Disassociate host and/or project from the given network.""" if args.host_only: @@ -1043,6 +1071,7 @@ def do_network_disassociate(cs, args): 'host', metavar='', help=_("Name of host")) +@deprecated_network def do_network_associate_host(cs, args): """Associate host with network.""" cs.networks.associate_host(args.network, args.host) @@ -1052,6 +1081,7 @@ def do_network_associate_host(cs, args): 'network', metavar='', help=_("UUID of network.")) +@deprecated_network def do_network_associate_project(cs, args): """Associate project with network.""" cs.networks.associate_project(args.network) @@ -1182,6 +1212,7 @@ def _filter_network_create_options(args): '--allowed-end', dest="allowed_end", help=_('End of allowed addresses for instances.')) +@deprecated_network def do_network_create(cs, args): """Create a network.""" @@ -2291,7 +2322,16 @@ def _find_network_id(cs, net_name): if cs.has_neutron(): return _find_network_id_neutron(cs, net_name) else: - return _find_network_id_novanet(cs, net_name) + # The network proxy API methods were deprecated in 2.36 and will return + # a 404 so we fallback to 2.35 to maintain a transition for CLI users. + want_version = api_versions.APIVersion('2.35') + cur_version = cs.api_version + if cs.api_version > want_version: + cs.api_version = want_version + try: + return _find_network_id_novanet(cs, net_name) + finally: + cs.api_version = cur_version def _find_network_id_novanet(cs, net_name): @@ -2663,12 +2703,14 @@ def do_list_secgroup(cs, args): help=_('Name of Floating IP Pool. (Optional)'), nargs='?', default=None) +@deprecated_network def do_floating_ip_create(cs, args): """Allocate a floating IP for the current tenant.""" _print_floating_ip_list([cs.floating_ips.create(pool=args.pool)]) @utils.arg('address', metavar='
', help=_('IP of Floating IP.')) +@deprecated_network def do_floating_ip_delete(cs, args): """De-allocate a floating IP.""" floating_ips = cs.floating_ips.list() @@ -2679,11 +2721,13 @@ def do_floating_ip_delete(cs, args): args.address) +@deprecated_network def do_floating_ip_list(cs, _args): """List floating IPs.""" _print_floating_ip_list(cs.floating_ips.list()) +@deprecated_network def do_floating_ip_pool_list(cs, _args): """List all floating IP pools.""" utils.print_list(cs.floating_ip_pools.list(), ['name']) @@ -2692,6 +2736,7 @@ def do_floating_ip_pool_list(cs, _args): @utils.arg( '--host', dest='host', metavar='', default=None, help=_('Filter by host.')) +@deprecated_network def do_floating_ip_bulk_list(cs, args): """List all floating IPs (nova-network only).""" utils.print_list(cs.floating_ips_bulk.list(args.host), ['project_id', @@ -2709,6 +2754,7 @@ def do_floating_ip_bulk_list(cs, args): @utils.arg( '--interface', metavar='', default=None, help=_('Interface for new Floating IPs.')) +@deprecated_network def do_floating_ip_bulk_create(cs, args): """Bulk create floating IPs by range (nova-network only).""" cs.floating_ips_bulk.create(args.ip_range, args.pool, args.interface) @@ -2716,6 +2762,7 @@ def do_floating_ip_bulk_create(cs, args): @utils.arg('ip_range', metavar='', help=_('Address range to delete.')) +@deprecated_network def do_floating_ip_bulk_delete(cs, args): """Bulk delete floating IPs by range (nova-network only).""" cs.floating_ips_bulk.delete(args.ip_range) @@ -2730,6 +2777,7 @@ def _print_domain_list(domain_entries): 'project', 'availability_zone']) +@deprecated_network def do_dns_domains(cs, args): """Print a list of available dns domains.""" domains = cs.dns_domains.domains() @@ -2739,6 +2787,7 @@ def do_dns_domains(cs, args): @utils.arg('domain', metavar='', help=_('DNS domain.')) @utils.arg('--ip', metavar='', help=_('IP address.'), default=None) @utils.arg('--name', metavar='', help=_('DNS name.'), default=None) +@deprecated_network def do_dns_list(cs, args): """List current DNS entries for domain and IP or domain and name.""" if not (args.ip or args.name): @@ -2761,6 +2810,7 @@ def do_dns_list(cs, args): metavar='', help=_('DNS type (e.g. "A")'), default='A') +@deprecated_network def do_dns_create(cs, args): """Create a DNS entry for domain, name, and IP.""" cs.dns_entries.create(args.domain, args.name, args.ip, args.type) @@ -2768,12 +2818,14 @@ def do_dns_create(cs, args): @utils.arg('domain', metavar='', help=_('DNS domain.')) @utils.arg('name', metavar='', help=_('DNS name.')) +@deprecated_network def do_dns_delete(cs, args): """Delete the specified DNS entry.""" cs.dns_entries.delete(args.domain, args.name) @utils.arg('domain', metavar='', help=_('DNS domain.')) +@deprecated_network def do_dns_delete_domain(cs, args): """Delete the specified DNS domain.""" cs.dns_domains.delete(args.domain) @@ -2786,6 +2838,7 @@ def do_dns_delete_domain(cs, args): default=None, help=_('Limit access to this domain to servers ' 'in the specified availability zone.')) +@deprecated_network def do_dns_create_private_domain(cs, args): """Create the specified DNS domain.""" cs.dns_domains.create_private(args.domain, @@ -2798,6 +2851,7 @@ def do_dns_create_private_domain(cs, args): help=_('Limit access to this domain to users ' 'of the specified project.'), default=None) +@deprecated_network def do_dns_create_public_domain(cs, args): """Create the specified DNS domain.""" cs.dns_domains.create_public(args.domain, @@ -2875,6 +2929,7 @@ def _get_secgroup(cs, secgroup): metavar='', help=_('Port at end of range.')) @utils.arg('cidr', metavar='', help=_('CIDR for address range.')) +@deprecated_network def do_secgroup_add_rule(cs, args): """Add a rule to a security group.""" secgroup = _get_secgroup(cs, args.secgroup) @@ -2903,6 +2958,7 @@ def do_secgroup_add_rule(cs, args): metavar='', help=_('Port at end of range.')) @utils.arg('cidr', metavar='', help=_('CIDR for address range.')) +@deprecated_network def do_secgroup_delete_rule(cs, args): """Delete a rule from a security group.""" secgroup = _get_secgroup(cs, args.secgroup) @@ -2922,6 +2978,7 @@ def do_secgroup_delete_rule(cs, args): @utils.arg( 'description', metavar='', help=_('Description of security group.')) +@deprecated_network def do_secgroup_create(cs, args): """Create a security group.""" secgroup = cs.security_groups.create(args.name, args.description) @@ -2936,6 +2993,7 @@ def do_secgroup_create(cs, args): @utils.arg( 'description', metavar='', help=_('Description of security group.')) +@deprecated_network def do_secgroup_update(cs, args): """Update a security group.""" sg = _get_secgroup(cs, args.secgroup) @@ -2947,6 +3005,7 @@ def do_secgroup_update(cs, args): 'secgroup', metavar='', help=_('ID or name of security group.')) +@deprecated_network def do_secgroup_delete(cs, args): """Delete a security group.""" secgroup = _get_secgroup(cs, args.secgroup) @@ -2964,6 +3023,7 @@ def do_secgroup_delete(cs, args): default=int(strutils.bool_from_string( os.environ.get("ALL_TENANTS", 'false'), True)), help=_('Display information from all tenants (Admin only).')) +@deprecated_network def do_secgroup_list(cs, args): """List security groups for the current tenant.""" search_opts = {'all_tenants': args.all_tenants} @@ -2978,6 +3038,7 @@ def do_secgroup_list(cs, args): 'secgroup', metavar='', help=_('ID or name of security group.')) +@deprecated_network def do_secgroup_list_rules(cs, args): """List rules for a security group.""" secgroup = _get_secgroup(cs, args.secgroup) @@ -3004,6 +3065,7 @@ def do_secgroup_list_rules(cs, args): 'to_port', metavar='', help=_('Port at end of range.')) +@deprecated_network def do_secgroup_add_group_rule(cs, args): """Add a source group rule to a security group.""" secgroup = _get_secgroup(cs, args.secgroup) @@ -3042,6 +3104,7 @@ def do_secgroup_add_group_rule(cs, args): 'to_port', metavar='', help=_('Port at end of range.')) +@deprecated_network def do_secgroup_delete_group_rule(cs, args): """Delete a source group rule from a security group.""" secgroup = _get_secgroup(cs, args.secgroup) @@ -3973,6 +4036,7 @@ def _print_fixed_ip(cs, fixed_ip): @utils.arg('fixed_ip', metavar='', help=_('Fixed IP Address.')) +@deprecated_network def do_fixed_ip_get(cs, args): """Retrieve info on a fixed IP.""" result = cs.fixed_ips.get(args.fixed_ip) @@ -3980,12 +4044,14 @@ def do_fixed_ip_get(cs, args): @utils.arg('fixed_ip', metavar='', help=_('Fixed IP Address.')) +@deprecated_network def do_fixed_ip_reserve(cs, args): """Reserve a fixed IP.""" cs.fixed_ips.reserve(args.fixed_ip) @utils.arg('fixed_ip', metavar='', help=_('Fixed IP Address.')) +@deprecated_network def do_fixed_ip_unreserve(cs, args): """Unreserve a fixed IP.""" cs.fixed_ips.unreserve(args.fixed_ip) @@ -4450,6 +4516,7 @@ def do_quota_defaults(cs, args): _quota_show(cs.quotas.defaults(project_id)) +@api_versions.wraps("2.0", "2.35") @utils.arg( 'tenant', metavar='', @@ -4479,12 +4546,14 @@ def do_quota_defaults(cs, args): metavar='', type=int, default=None, + action=shell.DeprecatedAction, help=_('New value for the "floating-ips" quota.')) @utils.arg( '--fixed-ips', metavar='', type=int, default=None, + action=shell.DeprecatedAction, help=_('New value for the "fixed-ips" quota.')) @utils.arg( '--metadata-items', @@ -4521,12 +4590,14 @@ def do_quota_defaults(cs, args): metavar='', type=int, default=None, + action=shell.DeprecatedAction, help=_('New value for the "security-groups" quota.')) @utils.arg( '--security-group-rules', metavar='', type=int, default=None, + action=shell.DeprecatedAction, help=_('New value for the "security-group-rules" quota.')) @utils.arg( '--server-groups', @@ -4553,6 +4624,88 @@ def do_quota_update(cs, args): _quota_update(cs.quotas, args.tenant, args) +# 2.36 does not support updating quota for floating IPs, fixed IPs, security +# groups or security group rules. +@api_versions.wraps("2.36") +@utils.arg( + 'tenant', + metavar='', + help=_('ID of tenant to set the quotas for.')) +@utils.arg( + '--user', + metavar='', + default=None, + help=_('ID of user to set the quotas for.')) +@utils.arg( + '--instances', + metavar='', + type=int, default=None, + help=_('New value for the "instances" quota.')) +@utils.arg( + '--cores', + metavar='', + type=int, default=None, + help=_('New value for the "cores" quota.')) +@utils.arg( + '--ram', + metavar='', + type=int, default=None, + help=_('New value for the "ram" quota.')) +@utils.arg( + '--metadata-items', + metavar='', + type=int, + default=None, + help=_('New value for the "metadata-items" quota.')) +@utils.arg( + '--injected-files', + metavar='', + type=int, + default=None, + help=_('New value for the "injected-files" quota.')) +@utils.arg( + '--injected-file-content-bytes', + metavar='', + type=int, + default=None, + help=_('New value for the "injected-file-content-bytes" quota.')) +@utils.arg( + '--injected-file-path-bytes', + metavar='', + type=int, + default=None, + help=_('New value for the "injected-file-path-bytes" quota.')) +@utils.arg( + '--key-pairs', + metavar='', + type=int, + default=None, + help=_('New value for the "key-pairs" quota.')) +@utils.arg( + '--server-groups', + metavar='', + type=int, + default=None, + help=_('New value for the "server-groups" quota.')) +@utils.arg( + '--server-group-members', + metavar='', + type=int, + default=None, + help=_('New value for the "server-group-members" quota.')) +@utils.arg( + '--force', + dest='force', + action="store_true", + default=None, + help=_('Whether force update the quota even if the already used and ' + 'reserved exceeds the new quota.')) +def do_quota_update(cs, args): + """Update the quotas for a tenant/user.""" + + _quota_update(cs.quotas, args.tenant, args) + + @utils.arg( '--tenant', metavar='', @@ -4580,6 +4733,7 @@ def do_quota_class_show(cs, args): _quota_show(cs.quota_classes.get(args.class_name)) +@api_versions.wraps("2.0", "2.35") @utils.arg( 'class_name', metavar='', @@ -4604,12 +4758,14 @@ def do_quota_class_show(cs, args): metavar='', type=int, default=None, + action=shell.DeprecatedAction, help=_('New value for the "floating-ips" quota.')) @utils.arg( '--fixed-ips', metavar='', type=int, default=None, + action=shell.DeprecatedAction, help=_('New value for the "fixed-ips" quota.')) @utils.arg( '--metadata-items', @@ -4646,12 +4802,14 @@ def do_quota_class_show(cs, args): metavar='', type=int, default=None, + action=shell.DeprecatedAction, help=_('New value for the "security-groups" quota.')) @utils.arg( '--security-group-rules', metavar='', type=int, default=None, + action=shell.DeprecatedAction, help=_('New value for the "security-group-rules" quota.')) @utils.arg( '--server-groups', @@ -4671,6 +4829,76 @@ def do_quota_class_update(cs, args): _quota_update(cs.quota_classes, args.class_name, args) +# 2.36 does not support updating quota for floating IPs, fixed IPs, security +# groups or security group rules. +@api_versions.wraps("2.36") +@utils.arg( + 'class_name', + metavar='', + help=_('Name of quota class to set the quotas for.')) +@utils.arg( + '--instances', + metavar='', + type=int, default=None, + help=_('New value for the "instances" quota.')) +@utils.arg( + '--cores', + metavar='', + type=int, default=None, + help=_('New value for the "cores" quota.')) +@utils.arg( + '--ram', + metavar='', + type=int, default=None, + help=_('New value for the "ram" quota.')) +@utils.arg( + '--metadata-items', + metavar='', + type=int, + default=None, + help=_('New value for the "metadata-items" quota.')) +@utils.arg( + '--injected-files', + metavar='', + type=int, + default=None, + help=_('New value for the "injected-files" quota.')) +@utils.arg( + '--injected-file-content-bytes', + metavar='', + type=int, + default=None, + help=_('New value for the "injected-file-content-bytes" quota.')) +@utils.arg( + '--injected-file-path-bytes', + metavar='', + type=int, + default=None, + help=_('New value for the "injected-file-path-bytes" quota.')) +@utils.arg( + '--key-pairs', + metavar='', + type=int, + default=None, + help=_('New value for the "key-pairs" quota.')) +@utils.arg( + '--server-groups', + metavar='', + type=int, + default=None, + help=_('New value for the "server-groups" quota.')) +@utils.arg( + '--server-group-members', + metavar='', + type=int, + default=None, + help=_('New value for the "server-group-members" quota.')) +def do_quota_class_update(cs, args): + """Update the quotas for a quota class.""" + + _quota_update(cs.quota_classes, args.class_name, args) + + @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg( 'host', metavar='', nargs='?', @@ -4871,6 +5099,7 @@ def do_server_group_list(cs, args): _print_server_group_details(cs, server_groups) +@deprecated_network def do_secgroup_list_default_rules(cs, args): """List rules that will be added to the 'default' security group for new tenants. @@ -4892,6 +5121,7 @@ def do_secgroup_list_default_rules(cs, args): metavar='', help=_('Port at end of range.')) @utils.arg('cidr', metavar='', help=_('CIDR for address range.')) +@deprecated_network def do_secgroup_add_default_rule(cs, args): """Add a rule to the set of rules that will be added to the 'default' security group for new tenants (nova-network only). @@ -4916,6 +5146,7 @@ def do_secgroup_add_default_rule(cs, args): metavar='', help=_('Port at end of range.')) @utils.arg('cidr', metavar='', help=_('CIDR for address range.')) +@deprecated_network def do_secgroup_delete_default_rule(cs, args): """Delete a rule from the set of rules that will be added to the 'default' security group for new tenants (nova-network only). diff --git a/releasenotes/notes/deprecate-network-cli-f0a539528be594d3.yaml b/releasenotes/notes/deprecate-network-cli-f0a539528be594d3.yaml new file mode 100644 index 000000000..2d8c86a6d --- /dev/null +++ b/releasenotes/notes/deprecate-network-cli-f0a539528be594d3.yaml @@ -0,0 +1,67 @@ +--- +upgrade: + - | + The ability to update the following network-related resources via the + ``nova quota-update`` and ``nova quota-class-update`` commands is now + deprecated: + + * Fixed IPs + * Floating IPs + * Security Groups + * Security Group Rules + + By default the quota and limits CLIs will not update or show those + resources using microversion >= 2.36. You can still use them, however, by + specifying ``--os-compute-api-version 2.35``. Quota information for network + resources should be retrieved from python-neutronclient or + python-openstackclient. +deprecations: + - | + The following commands are now deprecated: + + * dns-create + * dns-create-private-domain + * dns-create-public-domain + * dns-delete + * dns-delete-domain + * dns-domains + * dns-list + * fixed-ip-get + * fixed-ip-reserve + * fixed-ip-unreserve + * floating-ip-create + * floating-ip-delete + * floating-ip-list + * floating-ip-pool-list + * floating-ip-bulk-create + * floating-ip-bulk-delete + * floating-ip-bulk-list + * network-create + * network-delete + * network-disassociate + * network-associate-host + * network-associate-project + * network-list + * network-show + * scrub + * secgroup-create + * secgroup-delete + * secgroup-list + * secgroup-update + * secgroup-add-group-rule + * secgroup-delete-group-rule + * secgroup-add-rule + * secgroup-delete-rule + * secgroup-list-rules + * secgroup-list-default-rules + * secgroup-add-default-rule + * secgroup-delete-default-rule + * tenant-network-create + * tenant-network-delete + * tenant-network-list + * tenant-network-show + + With the 2.36 microversion these will fail in the API. The CLI will + fallback to passing the 2.35 microversion to ease the transition. Network + resource information should be retrieved from python-neutronclient or + python-openstackclient.