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.