Add --migration-type and --source-compute to migration-list
The GET /os-migrations API take a migration_type and source_compute filter parameter on the request since the v2.0 API. This adds support for specifying those parameters in the "nova migration-list" CLI and related MigrationManager.list() python API binding methods. A functional test is added which will cover the new options on all three of the decorated do_migration_list shell methods with lower bounds on 2.1, 2.59 and 2.66. Since the only type of migration we can safely generate in a single-node CI job is a resize the test does a resize. As such, _pick_alternate_flavor is moved into the base test class for re-use. Implements blueprint more-migration-list-filters Change-Id: I4be9a06df3e0d40d3990d067ce112247a81b45b4
This commit is contained in:
parent
e281368c96
commit
0e7c99c8ea
@ -2556,13 +2556,26 @@ nova migration-list
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
usage: nova migration-list [--instance-uuid <instance_uuid>] [--host <host>]
|
||||
[--status <status>] [--marker <marker>]
|
||||
[--limit <limit>] [--changes-since <changes_since>]
|
||||
usage: nova migration-list [--instance-uuid <instance_uuid>]
|
||||
[--host <host>]
|
||||
[--status <status>]
|
||||
[--migration-type <migration_type>]
|
||||
[--source-compute <source_compute>]
|
||||
[--marker <marker>]
|
||||
[--limit <limit>]
|
||||
[--changes-since <changes_since>]
|
||||
[--changes-before <changes_before>]
|
||||
|
||||
Print a list of migrations.
|
||||
|
||||
**Examples**
|
||||
|
||||
To see the list of evacuation operations *from* a compute service host:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
nova migration-list --migration-type evacuation --source-compute host.foo.bar
|
||||
|
||||
**Optional arguments:**
|
||||
|
||||
``--instance-uuid <instance_uuid>``
|
||||
@ -2574,6 +2587,17 @@ Print a list of migrations.
|
||||
``--status <status>``
|
||||
Fetch migrations for the given status.
|
||||
|
||||
``--migration-type <migration_type>``
|
||||
Filter migrations by type. Valid values are:
|
||||
|
||||
* evacuation
|
||||
* live-migration
|
||||
* migration
|
||||
* resize
|
||||
|
||||
``--source-compute <source_compute>``
|
||||
Filter migrations by source compute host name.
|
||||
|
||||
``--marker <marker>``
|
||||
The last migration of the previous page; displays list of migrations after
|
||||
"marker". Note that the marker is the migration UUID.
|
||||
|
@ -522,6 +522,27 @@ class ClientTestBase(testtools.TestCase):
|
||||
return {limit.name: limit.value
|
||||
for limit in self.client.limits.get(reserved=True).absolute}
|
||||
|
||||
def _pick_alternate_flavor(self):
|
||||
"""Given the flavor picked in the base class setup, this finds the
|
||||
opposite flavor to use for a resize test. For example, if m1.nano is
|
||||
the flavor, then use m1.micro, but those are only available if Tempest
|
||||
is configured. If m1.tiny, then use m1.small.
|
||||
"""
|
||||
flavor_name = self.flavor.name
|
||||
if flavor_name == 'm1.nano':
|
||||
# This is an upsize test.
|
||||
return 'm1.micro'
|
||||
if flavor_name == 'm1.micro':
|
||||
# This is a downsize test.
|
||||
return 'm1.nano'
|
||||
if flavor_name == 'm1.tiny':
|
||||
# This is an upsize test.
|
||||
return 'm1.small'
|
||||
if flavor_name == 'm1.small':
|
||||
# This is a downsize test.
|
||||
return 'm1.tiny'
|
||||
self.fail('Unable to find alternate for flavor: %s' % flavor_name)
|
||||
|
||||
|
||||
class TenantTestBase(ClientTestBase):
|
||||
"""Base test class for additional tenant and user creation which
|
||||
|
99
novaclient/tests/functional/v2/test_migrations.py
Normal file
99
novaclient/tests/functional/v2/test_migrations.py
Normal file
@ -0,0 +1,99 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from novaclient.tests.functional import base
|
||||
|
||||
|
||||
class TestMigrationList(base.ClientTestBase):
|
||||
"""Tests the "nova migration-list" command."""
|
||||
|
||||
def _filter_migrations(
|
||||
self, version, migration_type, source_compute):
|
||||
"""
|
||||
Filters migrations by --migration-type and --source-compute.
|
||||
|
||||
:param version: The --os-compute-api-version to use.
|
||||
:param migration_type: The type of migrations to filter.
|
||||
:param source_compute: The source compute service hostname to filter.
|
||||
:return: output of the nova migration-list command with filters applied
|
||||
"""
|
||||
return self.nova('migration-list',
|
||||
flags='--os-compute-api-version %s' % version,
|
||||
params='--migration-type %s --source-compute %s' % (
|
||||
migration_type, source_compute))
|
||||
|
||||
def test_migration_list(self):
|
||||
"""Tests creating a server, resizing it and then listing and filtering
|
||||
migrations using various microversion milestones.
|
||||
"""
|
||||
server_id = self._create_server(flavor=self.flavor.id).id
|
||||
# Find the source compute by getting OS-EXT-SRV-ATTR:host from the
|
||||
# nova show output.
|
||||
server = self.nova('show', params='%s' % server_id)
|
||||
source_compute = self._get_value_from_the_table(
|
||||
server, 'OS-EXT-SRV-ATTR:host')
|
||||
# now resize up
|
||||
alternate_flavor = self._pick_alternate_flavor()
|
||||
self.nova('resize',
|
||||
params='%s %s --poll' % (server_id, alternate_flavor))
|
||||
# now confirm the resize
|
||||
self.nova('resize-confirm', params='%s' % server_id)
|
||||
# wait for the server to be active and then check the migration list
|
||||
self._wait_for_state_change(server_id, 'active')
|
||||
# First, list migrations with v2.1 and our server id should be in the
|
||||
# output. There should only be the one migration.
|
||||
migrations = self.nova('migration-list',
|
||||
flags='--os-compute-api-version 2.1')
|
||||
instance_uuid = self._get_column_value_from_single_row_table(
|
||||
migrations, 'Instance UUID')
|
||||
self.assertEqual(server_id, instance_uuid)
|
||||
# A successfully confirmed resize should have the migration status
|
||||
# of "confirmed".
|
||||
migration_status = self._get_column_value_from_single_row_table(
|
||||
migrations, 'Status')
|
||||
self.assertEqual('confirmed', migration_status)
|
||||
# Now listing migrations with 2.23 should give us the Type column which
|
||||
# should have a value of "resize".
|
||||
migrations = self.nova('migration-list',
|
||||
flags='--os-compute-api-version 2.23')
|
||||
migration_type = self._get_column_value_from_single_row_table(
|
||||
migrations, 'Type')
|
||||
self.assertEqual('resize', migration_type)
|
||||
# Filter migrations with v2.1.
|
||||
migrations = self._filter_migrations('2.1', 'resize', source_compute)
|
||||
# Make sure we got something back.
|
||||
src_compute = self._get_column_value_from_single_row_table(
|
||||
migrations, 'Source Compute')
|
||||
self.assertEqual(source_compute, src_compute)
|
||||
# Filter migrations with v2.59 and make sure there is a migration UUID
|
||||
# value in the output.
|
||||
migrations = self._filter_migrations('2.59', 'resize', source_compute)
|
||||
# _get_column_value_from_single_row_table will raise ValueError if a
|
||||
# value is not found for the given column. We don't actually care what
|
||||
# the migration UUID value is just that the filter works and the UUID
|
||||
# is shown.
|
||||
self._get_column_value_from_single_row_table(migrations, 'UUID')
|
||||
# Filter migrations with v2.66, same as 2.59.
|
||||
migrations = self._filter_migrations('2.66', 'resize', source_compute)
|
||||
self._get_column_value_from_single_row_table(migrations, 'UUID')
|
||||
# Now do a negative test to show that filtering on a migration type
|
||||
# that we don't have a migration for will not return anything.
|
||||
migrations = self._filter_migrations(
|
||||
'2.1', 'evacuation', source_compute)
|
||||
self.assertNotIn(server_id, migrations)
|
||||
# Similarly, make sure we don't get anything back when filtering on
|
||||
# a --source-compute that doesn't exist.
|
||||
migrations = self._filter_migrations(
|
||||
'2.66', 'resize', uuidutils.generate_uuid())
|
||||
self.assertNotIn(server_id, migrations)
|
@ -20,27 +20,6 @@ class TestServersResize(base.ClientTestBase):
|
||||
|
||||
COMPUTE_API_VERSION = '2.1'
|
||||
|
||||
def _pick_alternate_flavor(self):
|
||||
"""Given the flavor picked in the base class setup, this finds the
|
||||
opposite flavor to use for a resize test. For example, if m1.nano is
|
||||
the flavor, then use m1.micro, but those are only available if Tempest
|
||||
is configured. If m1.tiny, then use m1.small.
|
||||
"""
|
||||
flavor_name = self.flavor.name
|
||||
if flavor_name == 'm1.nano':
|
||||
# This is an upsize test.
|
||||
return 'm1.micro'
|
||||
if flavor_name == 'm1.micro':
|
||||
# This is a downsize test.
|
||||
return 'm1.nano'
|
||||
if flavor_name == 'm1.tiny':
|
||||
# This is an upsize test.
|
||||
return 'm1.small'
|
||||
if flavor_name == 'm1.small':
|
||||
# This is a downsize test.
|
||||
return 'm1.tiny'
|
||||
self.fail('Unable to find alternate for flavor: %s' % flavor_name)
|
||||
|
||||
def _compare_quota_usage(self, old_usage, new_usage, expect_diff=True):
|
||||
"""Compares the quota usage in the provided AbsoluteLimits."""
|
||||
# For a resize, instance usage shouldn't change.
|
||||
|
@ -28,7 +28,8 @@ class MigrationManager(base.ManagerWithFind):
|
||||
|
||||
def _list_base(self, host=None, status=None, instance_uuid=None,
|
||||
marker=None, limit=None, changes_since=None,
|
||||
changes_before=None):
|
||||
changes_before=None, migration_type=None,
|
||||
source_compute=None):
|
||||
opts = {}
|
||||
if host:
|
||||
opts['host'] = host
|
||||
@ -44,23 +45,34 @@ class MigrationManager(base.ManagerWithFind):
|
||||
opts['changes-since'] = changes_since
|
||||
if changes_before:
|
||||
opts['changes-before'] = changes_before
|
||||
if migration_type:
|
||||
opts['migration_type'] = migration_type
|
||||
if source_compute:
|
||||
opts['source_compute'] = source_compute
|
||||
|
||||
return self._list("/os-migrations", "migrations", filters=opts)
|
||||
|
||||
@api_versions.wraps("2.0", "2.58")
|
||||
def list(self, host=None, status=None, instance_uuid=None):
|
||||
def list(self, host=None, status=None, instance_uuid=None,
|
||||
migration_type=None, source_compute=None):
|
||||
"""
|
||||
Get a list of migrations.
|
||||
:param host: filter migrations by host name (optional).
|
||||
:param status: filter migrations by status (optional).
|
||||
:param instance_uuid: filter migrations by instance uuid (optional).
|
||||
:param migration_type: Filter migrations by type. Valid values are:
|
||||
evacuation, live-migration, migration, resize
|
||||
:param source_compute: Filter migrations by source compute host name.
|
||||
"""
|
||||
return self._list_base(host=host, status=status,
|
||||
instance_uuid=instance_uuid)
|
||||
instance_uuid=instance_uuid,
|
||||
migration_type=migration_type,
|
||||
source_compute=source_compute)
|
||||
|
||||
@api_versions.wraps("2.59", "2.65")
|
||||
def list(self, host=None, status=None, instance_uuid=None,
|
||||
marker=None, limit=None, changes_since=None):
|
||||
marker=None, limit=None, changes_since=None,
|
||||
migration_type=None, source_compute=None):
|
||||
"""
|
||||
Get a list of migrations.
|
||||
:param host: filter migrations by host name (optional).
|
||||
@ -76,16 +88,21 @@ class MigrationManager(base.ManagerWithFind):
|
||||
:param changes_since: only return migrations changed later or equal
|
||||
to a certain point of time. The provided time should be an ISO 8061
|
||||
formatted time. e.g. 2016-03-04T06:27:59Z . (optional).
|
||||
:param migration_type: Filter migrations by type. Valid values are:
|
||||
evacuation, live-migration, migration, resize
|
||||
:param source_compute: Filter migrations by source compute host name.
|
||||
"""
|
||||
return self._list_base(host=host, status=status,
|
||||
instance_uuid=instance_uuid,
|
||||
marker=marker, limit=limit,
|
||||
changes_since=changes_since)
|
||||
changes_since=changes_since,
|
||||
migration_type=migration_type,
|
||||
source_compute=source_compute)
|
||||
|
||||
@api_versions.wraps("2.66")
|
||||
def list(self, host=None, status=None, instance_uuid=None,
|
||||
marker=None, limit=None, changes_since=None,
|
||||
changes_before=None):
|
||||
changes_before=None, migration_type=None, source_compute=None):
|
||||
"""
|
||||
Get a list of migrations.
|
||||
:param host: filter migrations by host name (optional).
|
||||
@ -104,9 +121,14 @@ class MigrationManager(base.ManagerWithFind):
|
||||
:param changes_before: Only return migrations changed earlier or
|
||||
equal to a certain point of time. The provided time should be an ISO
|
||||
8061 formatted time. e.g. 2016-03-05T06:27:59Z . (optional).
|
||||
:param migration_type: Filter migrations by type. Valid values are:
|
||||
evacuation, live-migration, migration, resize
|
||||
:param source_compute: Filter migrations by source compute host name.
|
||||
"""
|
||||
return self._list_base(host=host, status=status,
|
||||
instance_uuid=instance_uuid,
|
||||
marker=marker, limit=limit,
|
||||
changes_since=changes_since,
|
||||
changes_before=changes_before)
|
||||
changes_before=changes_before,
|
||||
migration_type=migration_type,
|
||||
source_compute=source_compute)
|
||||
|
@ -5422,10 +5422,23 @@ def _print_migrations(cs, migrations):
|
||||
dest='status',
|
||||
metavar='<status>',
|
||||
help=_('Fetch migrations for the given status.'))
|
||||
@utils.arg(
|
||||
'--migration-type',
|
||||
dest='migration_type',
|
||||
metavar='<migration_type>',
|
||||
help=_('Filter migrations by type. Valid values are: evacuation, '
|
||||
'live-migration, migration, resize'))
|
||||
@utils.arg(
|
||||
'--source-compute',
|
||||
dest='source_compute',
|
||||
metavar='<source_compute>',
|
||||
help=_('Filter migrations by source compute host name.'))
|
||||
def do_migration_list(cs, args):
|
||||
"""Print a list of migrations."""
|
||||
migrations = cs.migrations.list(args.host, args.status,
|
||||
instance_uuid=args.instance_uuid)
|
||||
instance_uuid=args.instance_uuid,
|
||||
migration_type=args.migration_type,
|
||||
source_compute=args.source_compute)
|
||||
_print_migrations(cs, migrations)
|
||||
|
||||
|
||||
@ -5445,6 +5458,17 @@ def do_migration_list(cs, args):
|
||||
dest='status',
|
||||
metavar='<status>',
|
||||
help=_('Fetch migrations for the given status.'))
|
||||
@utils.arg(
|
||||
'--migration-type',
|
||||
dest='migration_type',
|
||||
metavar='<migration_type>',
|
||||
help=_('Filter migrations by type. Valid values are: evacuation, '
|
||||
'live-migration, migration, resize'))
|
||||
@utils.arg(
|
||||
'--source-compute',
|
||||
dest='source_compute',
|
||||
metavar='<source_compute>',
|
||||
help=_('Filter migrations by source compute host name.'))
|
||||
@utils.arg(
|
||||
'--marker',
|
||||
dest='marker',
|
||||
@ -5483,7 +5507,9 @@ def do_migration_list(cs, args):
|
||||
migrations = cs.migrations.list(args.host, args.status,
|
||||
instance_uuid=args.instance_uuid,
|
||||
marker=args.marker, limit=args.limit,
|
||||
changes_since=args.changes_since)
|
||||
changes_since=args.changes_since,
|
||||
migration_type=args.migration_type,
|
||||
source_compute=args.source_compute)
|
||||
# TODO(yikun): Output a "Marker" column if there is a next link?
|
||||
_print_migrations(cs, migrations)
|
||||
|
||||
@ -5504,6 +5530,17 @@ def do_migration_list(cs, args):
|
||||
dest='status',
|
||||
metavar='<status>',
|
||||
help=_('Fetch migrations for the given status.'))
|
||||
@utils.arg(
|
||||
'--migration-type',
|
||||
dest='migration_type',
|
||||
metavar='<migration_type>',
|
||||
help=_('Filter migrations by type. Valid values are: evacuation, '
|
||||
'live-migration, migration, resize'))
|
||||
@utils.arg(
|
||||
'--source-compute',
|
||||
dest='source_compute',
|
||||
metavar='<source_compute>',
|
||||
help=_('Filter migrations by source compute host name.'))
|
||||
@utils.arg(
|
||||
'--marker',
|
||||
dest='marker',
|
||||
@ -5559,7 +5596,9 @@ def do_migration_list(cs, args):
|
||||
instance_uuid=args.instance_uuid,
|
||||
marker=args.marker, limit=args.limit,
|
||||
changes_since=args.changes_since,
|
||||
changes_before=args.changes_before)
|
||||
changes_before=args.changes_before,
|
||||
migration_type=args.migration_type,
|
||||
source_compute=args.source_compute)
|
||||
_print_migrations(cs, migrations)
|
||||
|
||||
|
||||
|
@ -0,0 +1,9 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
The ``--migration-type`` and ``--source-compute`` options are added to the
|
||||
``nova migration-list`` CLI and related kwargs are added to the
|
||||
``novaclient.v2.migrations.MigrationManager.list`` method. These can be
|
||||
used to filter the list of migrations by type (evacuation, live-migration,
|
||||
migration, resize) and the name of the source compute service host involved
|
||||
in the migration.
|
Loading…
Reference in New Issue
Block a user