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 <mriedem@us.ibm.com>

Change-Id: Id68c2dbef29b201aa7c8ef9417432feb5596529a
This commit is contained in:
Matt Riedemann 2016-08-18 10:03:11 -04:00 committed by Dan Smith
parent c3b5365cdf
commit 578c39865d
11 changed files with 461 additions and 9 deletions

View File

@ -25,4 +25,4 @@ API_MIN_VERSION = api_versions.APIVersion("2.1")
# when client supported the max version, and bumped sequentially, otherwise # when client supported the max version, and bumped sequentially, otherwise
# the client may break due to server side new version may include some # the client may break due to server side new version may include some
# backward incompatible change. # backward incompatible change.
API_MAX_VERSION = api_versions.APIVersion("2.35") API_MAX_VERSION = api_versions.APIVersion("2.36")

View File

@ -214,7 +214,16 @@ class ClientTestBase(testtools.TestCase):
# pick some reasonable flavor / image combo # pick some reasonable flavor / image combo
self.flavor = pick_flavor(self.client.flavors.list()) self.flavor = pick_flavor(self.client.flavors.list())
self.image = pick_image(self.glance.images.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 # create a CLI client in case we'd like to do CLI
# testing. tempest.lib does this really weird thing where it # testing. tempest.lib does this really weird thing where it

View File

@ -56,13 +56,12 @@ class TestServersBootNovaClient(base.ClientTestBase):
self._boot_server_with_legacy_bdm() self._boot_server_with_legacy_bdm()
def test_boot_server_with_net_name(self): def test_boot_server_with_net_name(self):
network = self.client.networks.list()[0]
server_info = self.nova("boot", params=( server_info = self.nova("boot", params=(
"%(name)s --flavor %(flavor)s --image %(image)s --poll " "%(name)s --flavor %(flavor)s --image %(image)s --poll "
"--nic net-name=%(net-name)s" % {"name": str(uuid.uuid4()), "--nic net-name=%(net-name)s" % {"name": str(uuid.uuid4()),
"image": self.image.id, "image": self.image.id,
"flavor": self.flavor.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") server_id = self._get_value_from_the_table(server_info, "id")
self.client.servers.delete(server_id) self.client.servers.delete(server_id)

View File

@ -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')

View File

@ -13,10 +13,10 @@
from novaclient.tests.functional.v2.legacy import test_quotas from novaclient.tests.functional.v2.legacy import test_quotas
class TestQuotasNovaClient(test_quotas.TestQuotasNovaClient): class TestQuotasNovaClient2_35(test_quotas.TestQuotasNovaClient):
"""Nova quotas functional tests.""" """Nova quotas functional tests."""
COMPUTE_API_VERSION = "2.latest" COMPUTE_API_VERSION = "2.35"
_quota_resources = ['instances', 'cores', 'ram', _quota_resources = ['instances', 'cores', 'ram',
'floating_ips', 'fixed_ips', 'metadata_items', 'floating_ips', 'fixed_ips', 'metadata_items',
@ -47,3 +47,17 @@ class TestQuotasNovaClient(test_quotas.TestQuotasNovaClient):
for quota_name in self._quota_resources: for quota_name in self._quota_resources:
self.assertEqual(getattr(original_quotas, quota_name), self.assertEqual(getattr(original_quotas, quota_name),
getattr(updated_quotas, quota_name) - difference) 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']

View File

@ -10,6 +10,10 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # 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 from novaclient.tests.functional.v2.legacy import test_readonly_nova
@ -22,3 +26,13 @@ class SimpleReadOnlyNovaClientTest(
""" """
COMPUTE_API_VERSION = "2.latest" 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()

View File

@ -22,7 +22,6 @@ class TestVirtualInterfacesNovaClient(
def test_virtual_interface_list(self): def test_virtual_interface_list(self):
output = super(TestVirtualInterfacesNovaClient, output = super(TestVirtualInterfacesNovaClient,
self).test_virtual_interface_list() self).test_virtual_interface_list()
network = self.client.networks.list()[0] self.assertEqual(self.network.id,
self.assertEqual(network.id,
self._get_column_value_from_single_row_table( self._get_column_value_from_single_row_table(
output, "Network ID")) output, "Network ID"))

View File

@ -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) @mock.patch('novaclient.v2.client.Client.has_neutron', return_value=True)
def test_boot_nics_net_name_neutron(self, has_neutron): def test_boot_nics_net_name_neutron(self, has_neutron):
cmd = ('boot --image %s --flavor 1 ' cmd = ('boot --image %s --flavor 1 '
@ -3271,3 +3303,39 @@ class PollForStatusTestCase(utils.TestCase):
action=action, action=action,
show_progress=True, show_progress=True,
silent=False) 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)

View File

@ -15,6 +15,7 @@
from novaclient import base from novaclient import base
from novaclient.i18n import _ from novaclient.i18n import _
from novaclient import utils from novaclient import utils
from novaclient.v2 import shell
class TenantNetwork(base.Resource): class TenantNetwork(base.Resource):
@ -60,6 +61,7 @@ def do_net(cs, args):
@utils.arg('network_id', metavar='<network_id>', help='ID of network') @utils.arg('network_id', metavar='<network_id>', help='ID of network')
@shell.deprecated_network
def do_tenant_network_show(cs, args): def do_tenant_network_show(cs, args):
""" """
Show a tenant network. Show a tenant network.
@ -75,6 +77,7 @@ def do_net_list(cs, args):
do_tenant_network_list(cs, args) do_tenant_network_list(cs, args)
@shell.deprecated_network
def do_tenant_network_list(cs, args): def do_tenant_network_list(cs, args):
""" """
List tenant networks. List tenant networks.
@ -106,6 +109,7 @@ def do_net_create(cs, args):
'cidr', 'cidr',
metavar='<cidr>', metavar='<cidr>',
help=_('IP block to allocate from (ex. 172.16.0.0/24 or 2001:DB8::/64)')) 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): def do_tenant_network_create(cs, args):
""" """
Create a tenant network. Create a tenant network.
@ -123,6 +127,7 @@ def do_net_delete(cs, args):
@utils.arg('network_id', metavar='<network_id>', help='ID of network') @utils.arg('network_id', metavar='<network_id>', help='ID of network')
@shell.deprecated_network
def do_tenant_network_delete(cs, args): def do_tenant_network_delete(cs, args):
""" """
Delete a tenant network. Delete a tenant network.

View File

@ -21,6 +21,7 @@ from __future__ import print_function
import argparse import argparse
import copy import copy
import datetime import datetime
import functools
import getpass import getpass
import locale import locale
import logging import logging
@ -76,6 +77,28 @@ def emit_image_deprecation_warning(command_name):
'instead.' % command_name, file=sys.stderr) '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): def _key_value_pairing(text):
try: try:
(k, v) = text.split('=', 1) (k, v) = text.split('=', 1)
@ -954,6 +977,7 @@ def do_flavor_access_remove(cs, args):
@utils.arg( @utils.arg(
'project_id', metavar='<project_id>', 'project_id', metavar='<project_id>',
help=_('The ID of the project.')) help=_('The ID of the project.'))
@deprecated_network
def do_scrub(cs, args): def do_scrub(cs, args):
"""Delete networks and security groups associated with a project.""" """Delete networks and security groups associated with a project."""
networks_list = cs.networks.list() networks_list = cs.networks.list()
@ -975,6 +999,7 @@ def do_scrub(cs, args):
metavar='<fields>', metavar='<fields>',
help=_('Comma-separated list of fields to display. ' help=_('Comma-separated list of fields to display. '
'Use the show command to see which fields are available.')) 'Use the show command to see which fields are available.'))
@deprecated_network
def do_network_list(cs, args): def do_network_list(cs, args):
"""Print a list of available networks.""" """Print a list of available networks."""
network_list = cs.networks.list() network_list = cs.networks.list()
@ -989,6 +1014,7 @@ def do_network_list(cs, args):
'network', 'network',
metavar='<network>', metavar='<network>',
help=_("UUID or label of network.")) help=_("UUID or label of network."))
@deprecated_network
def do_network_show(cs, args): def do_network_show(cs, args):
"""Show details about the given network.""" """Show details about the given network."""
network = utils.find_resource(cs.networks, args.network) network = utils.find_resource(cs.networks, args.network)
@ -999,6 +1025,7 @@ def do_network_show(cs, args):
'network', 'network',
metavar='<network>', metavar='<network>',
help=_("UUID or label of network.")) help=_("UUID or label of network."))
@deprecated_network
def do_network_delete(cs, args): def do_network_delete(cs, args):
"""Delete network by label or id.""" """Delete network by label or id."""
network = utils.find_resource(cs.networks, args.network) network = utils.find_resource(cs.networks, args.network)
@ -1025,6 +1052,7 @@ def do_network_delete(cs, args):
'network', 'network',
metavar='<network>', metavar='<network>',
help=_("UUID of network.")) help=_("UUID of network."))
@deprecated_network
def do_network_disassociate(cs, args): def do_network_disassociate(cs, args):
"""Disassociate host and/or project from the given network.""" """Disassociate host and/or project from the given network."""
if args.host_only: if args.host_only:
@ -1043,6 +1071,7 @@ def do_network_disassociate(cs, args):
'host', 'host',
metavar='<host>', metavar='<host>',
help=_("Name of host")) help=_("Name of host"))
@deprecated_network
def do_network_associate_host(cs, args): def do_network_associate_host(cs, args):
"""Associate host with network.""" """Associate host with network."""
cs.networks.associate_host(args.network, args.host) cs.networks.associate_host(args.network, args.host)
@ -1052,6 +1081,7 @@ def do_network_associate_host(cs, args):
'network', 'network',
metavar='<network>', metavar='<network>',
help=_("UUID of network.")) help=_("UUID of network."))
@deprecated_network
def do_network_associate_project(cs, args): def do_network_associate_project(cs, args):
"""Associate project with network.""" """Associate project with network."""
cs.networks.associate_project(args.network) cs.networks.associate_project(args.network)
@ -1182,6 +1212,7 @@ def _filter_network_create_options(args):
'--allowed-end', '--allowed-end',
dest="allowed_end", dest="allowed_end",
help=_('End of allowed addresses for instances.')) help=_('End of allowed addresses for instances.'))
@deprecated_network
def do_network_create(cs, args): def do_network_create(cs, args):
"""Create a network.""" """Create a network."""
@ -2291,7 +2322,16 @@ def _find_network_id(cs, net_name):
if cs.has_neutron(): if cs.has_neutron():
return _find_network_id_neutron(cs, net_name) return _find_network_id_neutron(cs, net_name)
else: 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): 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)'), help=_('Name of Floating IP Pool. (Optional)'),
nargs='?', nargs='?',
default=None) default=None)
@deprecated_network
def do_floating_ip_create(cs, args): def do_floating_ip_create(cs, args):
"""Allocate a floating IP for the current tenant.""" """Allocate a floating IP for the current tenant."""
_print_floating_ip_list([cs.floating_ips.create(pool=args.pool)]) _print_floating_ip_list([cs.floating_ips.create(pool=args.pool)])
@utils.arg('address', metavar='<address>', help=_('IP of Floating IP.')) @utils.arg('address', metavar='<address>', help=_('IP of Floating IP.'))
@deprecated_network
def do_floating_ip_delete(cs, args): def do_floating_ip_delete(cs, args):
"""De-allocate a floating IP.""" """De-allocate a floating IP."""
floating_ips = cs.floating_ips.list() floating_ips = cs.floating_ips.list()
@ -2679,11 +2721,13 @@ def do_floating_ip_delete(cs, args):
args.address) args.address)
@deprecated_network
def do_floating_ip_list(cs, _args): def do_floating_ip_list(cs, _args):
"""List floating IPs.""" """List floating IPs."""
_print_floating_ip_list(cs.floating_ips.list()) _print_floating_ip_list(cs.floating_ips.list())
@deprecated_network
def do_floating_ip_pool_list(cs, _args): def do_floating_ip_pool_list(cs, _args):
"""List all floating IP pools.""" """List all floating IP pools."""
utils.print_list(cs.floating_ip_pools.list(), ['name']) utils.print_list(cs.floating_ip_pools.list(), ['name'])
@ -2692,6 +2736,7 @@ def do_floating_ip_pool_list(cs, _args):
@utils.arg( @utils.arg(
'--host', dest='host', metavar='<host>', default=None, '--host', dest='host', metavar='<host>', default=None,
help=_('Filter by host.')) help=_('Filter by host.'))
@deprecated_network
def do_floating_ip_bulk_list(cs, args): def do_floating_ip_bulk_list(cs, args):
"""List all floating IPs (nova-network only).""" """List all floating IPs (nova-network only)."""
utils.print_list(cs.floating_ips_bulk.list(args.host), ['project_id', 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( @utils.arg(
'--interface', metavar='<interface>', default=None, '--interface', metavar='<interface>', default=None,
help=_('Interface for new Floating IPs.')) help=_('Interface for new Floating IPs.'))
@deprecated_network
def do_floating_ip_bulk_create(cs, args): def do_floating_ip_bulk_create(cs, args):
"""Bulk create floating IPs by range (nova-network only).""" """Bulk create floating IPs by range (nova-network only)."""
cs.floating_ips_bulk.create(args.ip_range, args.pool, args.interface) 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='<range>', @utils.arg('ip_range', metavar='<range>',
help=_('Address range to delete.')) help=_('Address range to delete.'))
@deprecated_network
def do_floating_ip_bulk_delete(cs, args): def do_floating_ip_bulk_delete(cs, args):
"""Bulk delete floating IPs by range (nova-network only).""" """Bulk delete floating IPs by range (nova-network only)."""
cs.floating_ips_bulk.delete(args.ip_range) cs.floating_ips_bulk.delete(args.ip_range)
@ -2730,6 +2777,7 @@ def _print_domain_list(domain_entries):
'project', 'availability_zone']) 'project', 'availability_zone'])
@deprecated_network
def do_dns_domains(cs, args): def do_dns_domains(cs, args):
"""Print a list of available dns domains.""" """Print a list of available dns domains."""
domains = cs.dns_domains.domains() domains = cs.dns_domains.domains()
@ -2739,6 +2787,7 @@ def do_dns_domains(cs, args):
@utils.arg('domain', metavar='<domain>', help=_('DNS domain.')) @utils.arg('domain', metavar='<domain>', help=_('DNS domain.'))
@utils.arg('--ip', metavar='<ip>', help=_('IP address.'), default=None) @utils.arg('--ip', metavar='<ip>', help=_('IP address.'), default=None)
@utils.arg('--name', metavar='<name>', help=_('DNS name.'), default=None) @utils.arg('--name', metavar='<name>', help=_('DNS name.'), default=None)
@deprecated_network
def do_dns_list(cs, args): def do_dns_list(cs, args):
"""List current DNS entries for domain and IP or domain and name.""" """List current DNS entries for domain and IP or domain and name."""
if not (args.ip or args.name): if not (args.ip or args.name):
@ -2761,6 +2810,7 @@ def do_dns_list(cs, args):
metavar='<type>', metavar='<type>',
help=_('DNS type (e.g. "A")'), help=_('DNS type (e.g. "A")'),
default='A') default='A')
@deprecated_network
def do_dns_create(cs, args): def do_dns_create(cs, args):
"""Create a DNS entry for domain, name, and IP.""" """Create a DNS entry for domain, name, and IP."""
cs.dns_entries.create(args.domain, args.name, args.ip, args.type) 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='<domain>', help=_('DNS domain.')) @utils.arg('domain', metavar='<domain>', help=_('DNS domain.'))
@utils.arg('name', metavar='<name>', help=_('DNS name.')) @utils.arg('name', metavar='<name>', help=_('DNS name.'))
@deprecated_network
def do_dns_delete(cs, args): def do_dns_delete(cs, args):
"""Delete the specified DNS entry.""" """Delete the specified DNS entry."""
cs.dns_entries.delete(args.domain, args.name) cs.dns_entries.delete(args.domain, args.name)
@utils.arg('domain', metavar='<domain>', help=_('DNS domain.')) @utils.arg('domain', metavar='<domain>', help=_('DNS domain.'))
@deprecated_network
def do_dns_delete_domain(cs, args): def do_dns_delete_domain(cs, args):
"""Delete the specified DNS domain.""" """Delete the specified DNS domain."""
cs.dns_domains.delete(args.domain) cs.dns_domains.delete(args.domain)
@ -2786,6 +2838,7 @@ def do_dns_delete_domain(cs, args):
default=None, default=None,
help=_('Limit access to this domain to servers ' help=_('Limit access to this domain to servers '
'in the specified availability zone.')) 'in the specified availability zone.'))
@deprecated_network
def do_dns_create_private_domain(cs, args): def do_dns_create_private_domain(cs, args):
"""Create the specified DNS domain.""" """Create the specified DNS domain."""
cs.dns_domains.create_private(args.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 ' help=_('Limit access to this domain to users '
'of the specified project.'), 'of the specified project.'),
default=None) default=None)
@deprecated_network
def do_dns_create_public_domain(cs, args): def do_dns_create_public_domain(cs, args):
"""Create the specified DNS domain.""" """Create the specified DNS domain."""
cs.dns_domains.create_public(args.domain, cs.dns_domains.create_public(args.domain,
@ -2875,6 +2929,7 @@ def _get_secgroup(cs, secgroup):
metavar='<to-port>', metavar='<to-port>',
help=_('Port at end of range.')) help=_('Port at end of range.'))
@utils.arg('cidr', metavar='<cidr>', help=_('CIDR for address range.')) @utils.arg('cidr', metavar='<cidr>', help=_('CIDR for address range.'))
@deprecated_network
def do_secgroup_add_rule(cs, args): def do_secgroup_add_rule(cs, args):
"""Add a rule to a security group.""" """Add a rule to a security group."""
secgroup = _get_secgroup(cs, args.secgroup) secgroup = _get_secgroup(cs, args.secgroup)
@ -2903,6 +2958,7 @@ def do_secgroup_add_rule(cs, args):
metavar='<to-port>', metavar='<to-port>',
help=_('Port at end of range.')) help=_('Port at end of range.'))
@utils.arg('cidr', metavar='<cidr>', help=_('CIDR for address range.')) @utils.arg('cidr', metavar='<cidr>', help=_('CIDR for address range.'))
@deprecated_network
def do_secgroup_delete_rule(cs, args): def do_secgroup_delete_rule(cs, args):
"""Delete a rule from a security group.""" """Delete a rule from a security group."""
secgroup = _get_secgroup(cs, args.secgroup) secgroup = _get_secgroup(cs, args.secgroup)
@ -2922,6 +2978,7 @@ def do_secgroup_delete_rule(cs, args):
@utils.arg( @utils.arg(
'description', metavar='<description>', 'description', metavar='<description>',
help=_('Description of security group.')) help=_('Description of security group.'))
@deprecated_network
def do_secgroup_create(cs, args): def do_secgroup_create(cs, args):
"""Create a security group.""" """Create a security group."""
secgroup = cs.security_groups.create(args.name, args.description) secgroup = cs.security_groups.create(args.name, args.description)
@ -2936,6 +2993,7 @@ def do_secgroup_create(cs, args):
@utils.arg( @utils.arg(
'description', metavar='<description>', 'description', metavar='<description>',
help=_('Description of security group.')) help=_('Description of security group.'))
@deprecated_network
def do_secgroup_update(cs, args): def do_secgroup_update(cs, args):
"""Update a security group.""" """Update a security group."""
sg = _get_secgroup(cs, args.secgroup) sg = _get_secgroup(cs, args.secgroup)
@ -2947,6 +3005,7 @@ def do_secgroup_update(cs, args):
'secgroup', 'secgroup',
metavar='<secgroup>', metavar='<secgroup>',
help=_('ID or name of security group.')) help=_('ID or name of security group.'))
@deprecated_network
def do_secgroup_delete(cs, args): def do_secgroup_delete(cs, args):
"""Delete a security group.""" """Delete a security group."""
secgroup = _get_secgroup(cs, args.secgroup) secgroup = _get_secgroup(cs, args.secgroup)
@ -2964,6 +3023,7 @@ def do_secgroup_delete(cs, args):
default=int(strutils.bool_from_string( default=int(strutils.bool_from_string(
os.environ.get("ALL_TENANTS", 'false'), True)), os.environ.get("ALL_TENANTS", 'false'), True)),
help=_('Display information from all tenants (Admin only).')) help=_('Display information from all tenants (Admin only).'))
@deprecated_network
def do_secgroup_list(cs, args): def do_secgroup_list(cs, args):
"""List security groups for the current tenant.""" """List security groups for the current tenant."""
search_opts = {'all_tenants': args.all_tenants} search_opts = {'all_tenants': args.all_tenants}
@ -2978,6 +3038,7 @@ def do_secgroup_list(cs, args):
'secgroup', 'secgroup',
metavar='<secgroup>', metavar='<secgroup>',
help=_('ID or name of security group.')) help=_('ID or name of security group.'))
@deprecated_network
def do_secgroup_list_rules(cs, args): def do_secgroup_list_rules(cs, args):
"""List rules for a security group.""" """List rules for a security group."""
secgroup = _get_secgroup(cs, args.secgroup) secgroup = _get_secgroup(cs, args.secgroup)
@ -3004,6 +3065,7 @@ def do_secgroup_list_rules(cs, args):
'to_port', 'to_port',
metavar='<to-port>', metavar='<to-port>',
help=_('Port at end of range.')) help=_('Port at end of range.'))
@deprecated_network
def do_secgroup_add_group_rule(cs, args): def do_secgroup_add_group_rule(cs, args):
"""Add a source group rule to a security group.""" """Add a source group rule to a security group."""
secgroup = _get_secgroup(cs, args.secgroup) secgroup = _get_secgroup(cs, args.secgroup)
@ -3042,6 +3104,7 @@ def do_secgroup_add_group_rule(cs, args):
'to_port', 'to_port',
metavar='<to-port>', metavar='<to-port>',
help=_('Port at end of range.')) help=_('Port at end of range.'))
@deprecated_network
def do_secgroup_delete_group_rule(cs, args): def do_secgroup_delete_group_rule(cs, args):
"""Delete a source group rule from a security group.""" """Delete a source group rule from a security group."""
secgroup = _get_secgroup(cs, args.secgroup) secgroup = _get_secgroup(cs, args.secgroup)
@ -3973,6 +4036,7 @@ def _print_fixed_ip(cs, fixed_ip):
@utils.arg('fixed_ip', metavar='<fixed_ip>', help=_('Fixed IP Address.')) @utils.arg('fixed_ip', metavar='<fixed_ip>', help=_('Fixed IP Address.'))
@deprecated_network
def do_fixed_ip_get(cs, args): def do_fixed_ip_get(cs, args):
"""Retrieve info on a fixed IP.""" """Retrieve info on a fixed IP."""
result = cs.fixed_ips.get(args.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='<fixed_ip>', help=_('Fixed IP Address.')) @utils.arg('fixed_ip', metavar='<fixed_ip>', help=_('Fixed IP Address.'))
@deprecated_network
def do_fixed_ip_reserve(cs, args): def do_fixed_ip_reserve(cs, args):
"""Reserve a fixed IP.""" """Reserve a fixed IP."""
cs.fixed_ips.reserve(args.fixed_ip) cs.fixed_ips.reserve(args.fixed_ip)
@utils.arg('fixed_ip', metavar='<fixed_ip>', help=_('Fixed IP Address.')) @utils.arg('fixed_ip', metavar='<fixed_ip>', help=_('Fixed IP Address.'))
@deprecated_network
def do_fixed_ip_unreserve(cs, args): def do_fixed_ip_unreserve(cs, args):
"""Unreserve a fixed IP.""" """Unreserve a fixed IP."""
cs.fixed_ips.unreserve(args.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)) _quota_show(cs.quotas.defaults(project_id))
@api_versions.wraps("2.0", "2.35")
@utils.arg( @utils.arg(
'tenant', 'tenant',
metavar='<tenant-id>', metavar='<tenant-id>',
@ -4479,12 +4546,14 @@ def do_quota_defaults(cs, args):
metavar='<floating-ips>', metavar='<floating-ips>',
type=int, type=int,
default=None, default=None,
action=shell.DeprecatedAction,
help=_('New value for the "floating-ips" quota.')) help=_('New value for the "floating-ips" quota.'))
@utils.arg( @utils.arg(
'--fixed-ips', '--fixed-ips',
metavar='<fixed-ips>', metavar='<fixed-ips>',
type=int, type=int,
default=None, default=None,
action=shell.DeprecatedAction,
help=_('New value for the "fixed-ips" quota.')) help=_('New value for the "fixed-ips" quota.'))
@utils.arg( @utils.arg(
'--metadata-items', '--metadata-items',
@ -4521,12 +4590,14 @@ def do_quota_defaults(cs, args):
metavar='<security-groups>', metavar='<security-groups>',
type=int, type=int,
default=None, default=None,
action=shell.DeprecatedAction,
help=_('New value for the "security-groups" quota.')) help=_('New value for the "security-groups" quota.'))
@utils.arg( @utils.arg(
'--security-group-rules', '--security-group-rules',
metavar='<security-group-rules>', metavar='<security-group-rules>',
type=int, type=int,
default=None, default=None,
action=shell.DeprecatedAction,
help=_('New value for the "security-group-rules" quota.')) help=_('New value for the "security-group-rules" quota.'))
@utils.arg( @utils.arg(
'--server-groups', '--server-groups',
@ -4553,6 +4624,88 @@ def do_quota_update(cs, args):
_quota_update(cs.quotas, args.tenant, 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='<tenant-id>',
help=_('ID of tenant to set the quotas for.'))
@utils.arg(
'--user',
metavar='<user-id>',
default=None,
help=_('ID of user to set the quotas for.'))
@utils.arg(
'--instances',
metavar='<instances>',
type=int, default=None,
help=_('New value for the "instances" quota.'))
@utils.arg(
'--cores',
metavar='<cores>',
type=int, default=None,
help=_('New value for the "cores" quota.'))
@utils.arg(
'--ram',
metavar='<ram>',
type=int, default=None,
help=_('New value for the "ram" quota.'))
@utils.arg(
'--metadata-items',
metavar='<metadata-items>',
type=int,
default=None,
help=_('New value for the "metadata-items" quota.'))
@utils.arg(
'--injected-files',
metavar='<injected-files>',
type=int,
default=None,
help=_('New value for the "injected-files" quota.'))
@utils.arg(
'--injected-file-content-bytes',
metavar='<injected-file-content-bytes>',
type=int,
default=None,
help=_('New value for the "injected-file-content-bytes" quota.'))
@utils.arg(
'--injected-file-path-bytes',
metavar='<injected-file-path-bytes>',
type=int,
default=None,
help=_('New value for the "injected-file-path-bytes" quota.'))
@utils.arg(
'--key-pairs',
metavar='<key-pairs>',
type=int,
default=None,
help=_('New value for the "key-pairs" quota.'))
@utils.arg(
'--server-groups',
metavar='<server-groups>',
type=int,
default=None,
help=_('New value for the "server-groups" quota.'))
@utils.arg(
'--server-group-members',
metavar='<server-group-members>',
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( @utils.arg(
'--tenant', '--tenant',
metavar='<tenant-id>', metavar='<tenant-id>',
@ -4580,6 +4733,7 @@ def do_quota_class_show(cs, args):
_quota_show(cs.quota_classes.get(args.class_name)) _quota_show(cs.quota_classes.get(args.class_name))
@api_versions.wraps("2.0", "2.35")
@utils.arg( @utils.arg(
'class_name', 'class_name',
metavar='<class>', metavar='<class>',
@ -4604,12 +4758,14 @@ def do_quota_class_show(cs, args):
metavar='<floating-ips>', metavar='<floating-ips>',
type=int, type=int,
default=None, default=None,
action=shell.DeprecatedAction,
help=_('New value for the "floating-ips" quota.')) help=_('New value for the "floating-ips" quota.'))
@utils.arg( @utils.arg(
'--fixed-ips', '--fixed-ips',
metavar='<fixed-ips>', metavar='<fixed-ips>',
type=int, type=int,
default=None, default=None,
action=shell.DeprecatedAction,
help=_('New value for the "fixed-ips" quota.')) help=_('New value for the "fixed-ips" quota.'))
@utils.arg( @utils.arg(
'--metadata-items', '--metadata-items',
@ -4646,12 +4802,14 @@ def do_quota_class_show(cs, args):
metavar='<security-groups>', metavar='<security-groups>',
type=int, type=int,
default=None, default=None,
action=shell.DeprecatedAction,
help=_('New value for the "security-groups" quota.')) help=_('New value for the "security-groups" quota.'))
@utils.arg( @utils.arg(
'--security-group-rules', '--security-group-rules',
metavar='<security-group-rules>', metavar='<security-group-rules>',
type=int, type=int,
default=None, default=None,
action=shell.DeprecatedAction,
help=_('New value for the "security-group-rules" quota.')) help=_('New value for the "security-group-rules" quota.'))
@utils.arg( @utils.arg(
'--server-groups', '--server-groups',
@ -4671,6 +4829,76 @@ def do_quota_class_update(cs, args):
_quota_update(cs.quota_classes, args.class_name, 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='<class>',
help=_('Name of quota class to set the quotas for.'))
@utils.arg(
'--instances',
metavar='<instances>',
type=int, default=None,
help=_('New value for the "instances" quota.'))
@utils.arg(
'--cores',
metavar='<cores>',
type=int, default=None,
help=_('New value for the "cores" quota.'))
@utils.arg(
'--ram',
metavar='<ram>',
type=int, default=None,
help=_('New value for the "ram" quota.'))
@utils.arg(
'--metadata-items',
metavar='<metadata-items>',
type=int,
default=None,
help=_('New value for the "metadata-items" quota.'))
@utils.arg(
'--injected-files',
metavar='<injected-files>',
type=int,
default=None,
help=_('New value for the "injected-files" quota.'))
@utils.arg(
'--injected-file-content-bytes',
metavar='<injected-file-content-bytes>',
type=int,
default=None,
help=_('New value for the "injected-file-content-bytes" quota.'))
@utils.arg(
'--injected-file-path-bytes',
metavar='<injected-file-path-bytes>',
type=int,
default=None,
help=_('New value for the "injected-file-path-bytes" quota.'))
@utils.arg(
'--key-pairs',
metavar='<key-pairs>',
type=int,
default=None,
help=_('New value for the "key-pairs" quota.'))
@utils.arg(
'--server-groups',
metavar='<server-groups>',
type=int,
default=None,
help=_('New value for the "server-groups" quota.'))
@utils.arg(
'--server-group-members',
metavar='<server-group-members>',
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='<server>', help=_('Name or ID of server.')) @utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@utils.arg( @utils.arg(
'host', metavar='<host>', nargs='?', 'host', metavar='<host>', nargs='?',
@ -4871,6 +5099,7 @@ def do_server_group_list(cs, args):
_print_server_group_details(cs, server_groups) _print_server_group_details(cs, server_groups)
@deprecated_network
def do_secgroup_list_default_rules(cs, args): def do_secgroup_list_default_rules(cs, args):
"""List rules that will be added to the 'default' security group for """List rules that will be added to the 'default' security group for
new tenants. new tenants.
@ -4892,6 +5121,7 @@ def do_secgroup_list_default_rules(cs, args):
metavar='<to-port>', metavar='<to-port>',
help=_('Port at end of range.')) help=_('Port at end of range.'))
@utils.arg('cidr', metavar='<cidr>', help=_('CIDR for address range.')) @utils.arg('cidr', metavar='<cidr>', help=_('CIDR for address range.'))
@deprecated_network
def do_secgroup_add_default_rule(cs, args): def do_secgroup_add_default_rule(cs, args):
"""Add a rule to the set of rules that will be added to the 'default' """Add a rule to the set of rules that will be added to the 'default'
security group for new tenants (nova-network only). security group for new tenants (nova-network only).
@ -4916,6 +5146,7 @@ def do_secgroup_add_default_rule(cs, args):
metavar='<to-port>', metavar='<to-port>',
help=_('Port at end of range.')) help=_('Port at end of range.'))
@utils.arg('cidr', metavar='<cidr>', help=_('CIDR for address range.')) @utils.arg('cidr', metavar='<cidr>', help=_('CIDR for address range.'))
@deprecated_network
def do_secgroup_delete_default_rule(cs, args): def do_secgroup_delete_default_rule(cs, args):
"""Delete a rule from the set of rules that will be added to the """Delete a rule from the set of rules that will be added to the
'default' security group for new tenants (nova-network only). 'default' security group for new tenants (nova-network only).

View File

@ -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.