[microversions] Enable 2.21

2.21 - The os-instance-actions API now returns information from deleted
       instances.

Change-Id: Iff514e4fa9135207c6f8e32e444d45b1b61d8c7c
This commit is contained in:
Andrey Kurilin 2016-02-10 17:37:25 +02:00 committed by Andrey Kurilin
parent ca5b06f6ae
commit cd88097ff5
6 changed files with 120 additions and 10 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.20") API_MAX_VERSION = api_versions.APIVersion("2.21")

View File

@ -308,7 +308,8 @@ class ClientTestBase(testtools.TestCase):
raise ValueError("Unable to find value for column '%s'.") raise ValueError("Unable to find value for column '%s'.")
def _create_server(self, name=None, with_network=True, **kwargs): def _create_server(self, name=None, with_network=True, add_cleanup=True,
**kwargs):
name = name or self.name_generate(prefix='server') name = name or self.name_generate(prefix='server')
if with_network: if with_network:
nics = [{"net-id": self.network.id}] nics = [{"net-id": self.network.id}]
@ -316,7 +317,8 @@ class ClientTestBase(testtools.TestCase):
nics = None nics = None
server = self.client.servers.create(name, self.image, self.flavor, server = self.client.servers.create(name, self.image, self.flavor,
nics=nics, **kwargs) nics=nics, **kwargs)
self.addCleanup(server.delete) if add_cleanup:
self.addCleanup(server.delete)
novaclient.v2.shell._poll_for_status( novaclient.v2.shell._poll_for_status(
self.client.servers.get, server.id, self.client.servers.get, server.id,
'building', ['active']) 'building', ['active'])

View File

@ -0,0 +1,60 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import uuid
import six
from tempest_lib import exceptions
from novaclient.tests.functional import base
class TestInstanceActionCLI(base.ClientTestBase):
COMPUTE_API_VERSION = "2.21"
def _test_cmd_with_not_existing_instance(self, cmd, args):
try:
self.nova("%s %s" % (cmd, args))
except exceptions.CommandFailed as e:
self.assertIn("ERROR (NotFound):", six.text_type(e))
else:
self.fail("%s is not failed on non existing instance." % cmd)
def test_show_action_with_not_existing_instance(self):
name_or_uuid = str(uuid.uuid4())
request_id = str(uuid.uuid4())
self._test_cmd_with_not_existing_instance(
"instance-action", "%s %s" % (name_or_uuid, request_id))
def test_list_actions_with_not_existing_instance(self):
name_or_uuid = str(uuid.uuid4())
self._test_cmd_with_not_existing_instance("instance-action-list",
name_or_uuid)
def test_show_and_list_actions_on_deleted_instance(self):
server = self._create_server(add_cleanup=False)
server.delete()
self.wait_for_resource_delete(server, self.client.servers)
output = self.nova("instance-action-list %s" % server.id)
# NOTE(andreykurilin): output is not a single row table, so we can
# obtain just "create" action. It should be enough for testing
# "nova instance-action <server> <request-id>" command
request_id = self._get_column_value_from_single_row_table(
output, "Request_ID")
output = self.nova("instance-action %s %s" % (server.id, request_id))
# ensure that obtained action is "create".
self.assertEqual("create",
self._get_value_from_the_table(output, "action"))

View File

@ -158,6 +158,15 @@ class FindResourceTestCase(test_utils.TestCase):
output = utils.find_resource(alphanum_manager, '01234') output = utils.find_resource(alphanum_manager, '01234')
self.assertEqual(output, alphanum_manager.get('01234')) self.assertEqual(output, alphanum_manager.get('01234'))
def test_find_without_wrapping_exception(self):
alphanum_manager = FakeManager(True)
self.assertRaises(exceptions.NotFound, utils.find_resource,
alphanum_manager, 'not_exist', wrap_exception=False)
res = alphanum_manager.resources[0]
alphanum_manager.resources.append(res)
self.assertRaises(exceptions.NoUniqueMatch, utils.find_resource,
alphanum_manager, res.name, wrap_exception=False)
class _FakeResult(object): class _FakeResult(object):
def __init__(self, name, value): def __init__(self, name, value):

View File

@ -273,7 +273,7 @@ def print_dict(d, dict_property="Property", dict_value="Value", wrap=0):
print(result) print(result)
def find_resource(manager, name_or_id, **find_args): def find_resource(manager, name_or_id, wrap_exception=True, **find_args):
"""Helper for the _find_* methods.""" """Helper for the _find_* methods."""
# for str id which is not uuid (for Flavor, Keypair and hypervsior in cells # for str id which is not uuid (for Flavor, Keypair and hypervsior in cells
# environments search currently) # environments search currently)
@ -316,7 +316,9 @@ def find_resource(manager, name_or_id, **find_args):
"to be more specific.") % "to be more specific.") %
{'class': manager.resource_class.__name__.lower(), {'class': manager.resource_class.__name__.lower(),
'name': name_or_id}) 'name': name_or_id})
raise exceptions.CommandError(msg) if wrap_exception:
raise exceptions.CommandError(msg)
raise exceptions.NoUniqueMatch(msg)
# finally try to get entity as integer id # finally try to get entity as integer id
try: try:
@ -325,7 +327,9 @@ def find_resource(manager, name_or_id, **find_args):
msg = (_("No %(class)s with a name or ID of '%(name)s' exists.") % msg = (_("No %(class)s with a name or ID of '%(name)s' exists.") %
{'class': manager.resource_class.__name__.lower(), {'class': manager.resource_class.__name__.lower(),
'name': name_or_id}) 'name': name_or_id})
raise exceptions.CommandError(msg) if wrap_exception:
raise exceptions.CommandError(msg)
raise exceptions.NotFound(404, msg)
def _format_servers_list_networks(server): def _format_servers_list_networks(server):

View File

@ -15,7 +15,11 @@
import pprint import pprint
import six
from novaclient import api_versions
from novaclient import base from novaclient import base
from novaclient import exceptions
from novaclient.i18n import _ from novaclient.i18n import _
from novaclient.openstack.common import cliutils from novaclient.openstack.common import cliutils
from novaclient import utils from novaclient import utils
@ -41,17 +45,41 @@ class InstanceActionManager(base.ManagerWithFind):
base.getid(server), 'instanceActions') base.getid(server), 'instanceActions')
@api_versions.wraps("2.0", "2.20")
def _find_server(cs, args):
return utils.find_resource(cs.servers, args.server)
@api_versions.wraps("2.21")
def _find_server(cs, args):
try:
return utils.find_resource(cs.servers, args.server,
wrap_exception=False)
except exceptions.NoUniqueMatch as e:
raise exceptions.CommandError(six.text_type(e))
except exceptions.NotFound:
# The server can be deleted
return args.server
@cliutils.arg( @cliutils.arg(
'server', 'server',
metavar='<server>', metavar='<server>',
help=_('Name or UUID of the server to show an action for.')) help=_('Name or UUID of the server to show actions for.'),
start_version="2.0", end_version="2.20")
@cliutils.arg(
'server',
metavar='<server>',
help=_('Name or UUID of the server to show actions for. Only UUID can be '
'used to show actions for a deleted server.'),
start_version="2.21")
@cliutils.arg( @cliutils.arg(
'request_id', 'request_id',
metavar='<request_id>', metavar='<request_id>',
help=_('Request ID of the action to get.')) help=_('Request ID of the action to get.'))
def do_instance_action(cs, args): def do_instance_action(cs, args):
"""Show an action.""" """Show an action."""
server = utils.find_resource(cs.servers, args.server) server = _find_server(cs, args)
action_resource = cs.instance_action.get(server, args.request_id) action_resource = cs.instance_action.get(server, args.request_id)
action = action_resource._info action = action_resource._info
if 'events' in action: if 'events' in action:
@ -62,10 +90,17 @@ def do_instance_action(cs, args):
@cliutils.arg( @cliutils.arg(
'server', 'server',
metavar='<server>', metavar='<server>',
help=_('Name or UUID of the server to list actions for.')) help=_('Name or UUID of the server to list actions for.'),
start_version="2.0", end_version="2.20")
@cliutils.arg(
'server',
metavar='<server>',
help=_('Name or UUID of the server to list actions for. Only UUID can be '
'used to list actions on a deleted server.'),
start_version="2.21")
def do_instance_action_list(cs, args): def do_instance_action_list(cs, args):
"""List actions on a server.""" """List actions on a server."""
server = utils.find_resource(cs.servers, args.server) server = _find_server(cs, args)
actions = cs.instance_action.list(server) actions = cs.instance_action.list(server)
utils.print_list(actions, utils.print_list(actions,
['Action', 'Request_ID', 'Message', 'Start_Time'], ['Action', 'Request_ID', 'Message', 'Start_Time'],