Add two server-migration commands and bump migration-list command
1. Add two new commands Add nova client server-migration-list and server-migration-show 2. Bump and old command Add migration_type field for migration-list command Partially implements blueprint live-migration-progress-report Depends-On: Ia92ecbe3c99082e3a34adf4fd29041b1a95ef21e Change-Id: I071198fa9ba0699383bdebf4fab54714a435e6c3
This commit is contained in:
parent
66b4085c9e
commit
e0c7d2c673
@ -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.22")
|
||||
API_MAX_VERSION = api_versions.APIVersion("2.23")
|
||||
|
@ -25,3 +25,54 @@ class Fixture(base.Fixture):
|
||||
self.requests.register_uri('POST', url,
|
||||
status_code=202,
|
||||
headers=self.json_headers)
|
||||
|
||||
get_migrations = {'migrations': [
|
||||
{
|
||||
"created_at": "2016-01-29T13:42:02.000000",
|
||||
"dest_compute": "compute2",
|
||||
"dest_host": "1.2.3.4",
|
||||
"dest_node": "node2",
|
||||
"id": 1,
|
||||
"server_uuid": "4cfba335-03d8-49b2-8c52-e69043d1e8fe",
|
||||
"source_compute": "compute1",
|
||||
"source_node": "node1",
|
||||
"status": "running",
|
||||
"memory_total_bytes": 123456,
|
||||
"memory_processed_bytes": 12345,
|
||||
"memory_remaining_bytes": 120000,
|
||||
"disk_total_bytes": 234567,
|
||||
"disk_processed_bytes": 23456,
|
||||
"disk_remaining_bytes": 230000,
|
||||
"updated_at": "2016-01-29T13:42:02.000000"
|
||||
}]}
|
||||
|
||||
url = self.url('1234', 'migrations')
|
||||
self.requests.register_uri('GET', url,
|
||||
status_code=200,
|
||||
json=get_migrations,
|
||||
headers=self.json_headers)
|
||||
|
||||
get_migration = {'migration': {
|
||||
"created_at": "2016-01-29T13:42:02.000000",
|
||||
"dest_compute": "compute2",
|
||||
"dest_host": "1.2.3.4",
|
||||
"dest_node": "node2",
|
||||
"id": 1,
|
||||
"server_uuid": "4cfba335-03d8-49b2-8c52-e69043d1e8fe",
|
||||
"source_compute": "compute1",
|
||||
"source_node": "node1",
|
||||
"status": "running",
|
||||
"memory_total_bytes": 123456,
|
||||
"memory_processed_bytes": 12345,
|
||||
"memory_remaining_bytes": 120000,
|
||||
"disk_total_bytes": 234567,
|
||||
"disk_processed_bytes": 23456,
|
||||
"disk_remaining_bytes": 230000,
|
||||
"updated_at": "2016-01-29T13:42:02.000000"
|
||||
}}
|
||||
|
||||
url = self.url('1234', 'migrations', '1')
|
||||
self.requests.register_uri('GET', url,
|
||||
status_code=200,
|
||||
json=get_migration,
|
||||
headers=self.json_headers)
|
||||
|
@ -10,6 +10,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient import api_versions
|
||||
from novaclient import extension
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.tests.unit.v2 import fakes
|
||||
@ -30,6 +31,17 @@ class MigrationsTest(utils.TestCase):
|
||||
cs.assert_called('GET', '/os-migrations')
|
||||
for m in ml:
|
||||
self.assertIsInstance(m, migrations.Migration)
|
||||
self.assertRaises(AttributeError, getattr, m, "migration_type")
|
||||
|
||||
def test_list_migrations_v223(self):
|
||||
cs = fakes.FakeClient(extensions=extensions,
|
||||
api_version=api_versions.APIVersion("2.23"))
|
||||
ml = cs.migrations.list()
|
||||
self.assert_request_id(ml, fakes.FAKE_REQUEST_ID_LIST)
|
||||
cs.assert_called('GET', '/os-migrations')
|
||||
for m in ml:
|
||||
self.assertIsInstance(m, migrations.Migration)
|
||||
self.assertEqual(m.migration_type, 'live-migration')
|
||||
|
||||
def test_list_migrations_with_filters(self):
|
||||
ml = cs.migrations.list('host1', 'finished', 'child1')
|
||||
|
@ -23,8 +23,10 @@ import six
|
||||
from six.moves.urllib import parse
|
||||
|
||||
import novaclient
|
||||
from novaclient import api_versions
|
||||
from novaclient import client as base_client
|
||||
from novaclient import exceptions
|
||||
from novaclient.i18n import _
|
||||
from novaclient.tests.unit import fakes
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.v2 import client
|
||||
@ -58,7 +60,8 @@ class FakeClient(fakes.FakeClient, client.Client):
|
||||
'project_id', 'auth_url',
|
||||
extensions=kwargs.get('extensions'),
|
||||
direct_use=False)
|
||||
self.api_version = api_version
|
||||
self.api_version = api_version or api_versions.APIVersion("2.1")
|
||||
kwargs["api_version"] = self.api_version
|
||||
self.client = FakeHTTPClient(**kwargs)
|
||||
|
||||
|
||||
@ -91,6 +94,7 @@ class FakeHTTPClient(base_client.HTTPClient):
|
||||
self.http_log_debug = 'http_log_debug'
|
||||
self.last_request_id = None
|
||||
self.management_url = self.get_endpoint()
|
||||
self.api_version = kwargs.get("api_version")
|
||||
|
||||
def _cs_request(self, url, method, **kwargs):
|
||||
# Check that certain things are called correctly
|
||||
@ -2367,21 +2371,26 @@ class FakeHTTPClient(base_client.HTTPClient):
|
||||
return self.get_os_cells_capacities()
|
||||
|
||||
def get_os_migrations(self, **kw):
|
||||
migrations = {'migrations': [
|
||||
{
|
||||
"created_at": "2012-10-29T13:42:02.000000",
|
||||
"dest_compute": "compute2",
|
||||
"dest_host": "1.2.3.4",
|
||||
"dest_node": "node2",
|
||||
"id": 1234,
|
||||
"instance_uuid": "instance_id_123",
|
||||
"new_instance_type_id": 2,
|
||||
"old_instance_type_id": 1,
|
||||
"source_compute": "compute1",
|
||||
"source_node": "node1",
|
||||
"status": "Done",
|
||||
"updated_at": "2012-10-29T13:42:02.000000"
|
||||
}]}
|
||||
migration = {
|
||||
"created_at": "2012-10-29T13:42:02.000000",
|
||||
"dest_compute": "compute2",
|
||||
"dest_host": "1.2.3.4",
|
||||
"dest_node": "node2",
|
||||
"id": 1234,
|
||||
"instance_uuid": "instance_id_123",
|
||||
"new_instance_type_id": 2,
|
||||
"old_instance_type_id": 1,
|
||||
"source_compute": "compute1",
|
||||
"source_node": "node1",
|
||||
"status": "Done",
|
||||
"updated_at": "2012-10-29T13:42:02.000000"
|
||||
}
|
||||
|
||||
if self.api_version >= api_versions.APIVersion("2.23"):
|
||||
migration.update({"migration_type": "live-migration"})
|
||||
|
||||
migrations = {'migrations': [migration]}
|
||||
|
||||
return (200, FAKE_RESPONSE_HEADERS, migrations)
|
||||
|
||||
def post_os_server_external_events(self, **kw):
|
||||
@ -2435,6 +2444,57 @@ class FakeHTTPClient(base_client.HTTPClient):
|
||||
def post_servers_1234_migrations_1_action(self, body):
|
||||
return (202, {}, None)
|
||||
|
||||
def get_servers_1234_migrations_1(self, **kw):
|
||||
# TODO(Shaohe Feng) this condition check can be a decorator
|
||||
if self.api_version < api_versions.APIVersion("2.23"):
|
||||
raise exceptions.UnsupportedVersion(_("Unsupport version %s")
|
||||
% self.api_version)
|
||||
migration = {"migration": {
|
||||
"created_at": "2016-01-29T13:42:02.000000",
|
||||
"dest_compute": "compute2",
|
||||
"dest_host": "1.2.3.4",
|
||||
"dest_node": "node2",
|
||||
"id": 1,
|
||||
"server_uuid": "4cfba335-03d8-49b2-8c52-e69043d1e8fe",
|
||||
"source_compute": "compute1",
|
||||
"source_node": "node1",
|
||||
"status": "running",
|
||||
"memory_total_bytes": 123456,
|
||||
"memory_processed_bytes": 12345,
|
||||
"memory_remaining_bytes": 120000,
|
||||
"disk_total_bytes": 234567,
|
||||
"disk_processed_bytes": 23456,
|
||||
"disk_remaining_bytes": 230000,
|
||||
"updated_at": "2016-01-29T13:42:02.000000"
|
||||
}}
|
||||
return (200, FAKE_RESPONSE_HEADERS, migration)
|
||||
|
||||
def get_servers_1234_migrations(self, **kw):
|
||||
# TODO(Shaohe Feng) this condition check can be a decorator
|
||||
if self.api_version < api_versions.APIVersion("2.23"):
|
||||
raise exceptions.UnsupportedVersion(_("Unsupport version %s")
|
||||
% self.api_version)
|
||||
migrations = {'migrations': [
|
||||
{
|
||||
"created_at": "2016-01-29T13:42:02.000000",
|
||||
"dest_compute": "compute2",
|
||||
"dest_host": "1.2.3.4",
|
||||
"dest_node": "node2",
|
||||
"id": 1,
|
||||
"server_uuid": "4cfba335-03d8-49b2-8c52-e69043d1e8fe",
|
||||
"source_compute": "compute1",
|
||||
"source_node": "node1",
|
||||
"status": "running",
|
||||
"memory_total_bytes": 123456,
|
||||
"memory_processed_bytes": 12345,
|
||||
"memory_remaining_bytes": 120000,
|
||||
"disk_total_bytes": 234567,
|
||||
"disk_processed_bytes": 23456,
|
||||
"disk_remaining_bytes": 230000,
|
||||
"updated_at": "2016-01-29T13:42:02.000000"
|
||||
}]}
|
||||
return (200, FAKE_RESPONSE_HEADERS, migrations)
|
||||
|
||||
|
||||
class FakeSessionClient(fakes.FakeClient, client.Client):
|
||||
|
||||
@ -2443,6 +2503,7 @@ class FakeSessionClient(fakes.FakeClient, client.Client):
|
||||
'project_id', 'auth_url',
|
||||
extensions=kwargs.get('extensions'),
|
||||
api_version=api_version, direct_use=False)
|
||||
kwargs['api_version'] = api_version
|
||||
self.client = FakeSessionMockClient(**kwargs)
|
||||
|
||||
|
||||
@ -2461,7 +2522,7 @@ class FakeSessionMockClient(base_client.SessionClient, FakeHTTPClient):
|
||||
self.interface = None
|
||||
self.region_name = None
|
||||
self.version = None
|
||||
|
||||
self.api_version = kwargs.get('api_version')
|
||||
self.auth.get_auth_ref.return_value.project_id = 'tenant_id'
|
||||
|
||||
def request(self, url, method, **kwargs):
|
||||
|
@ -14,9 +14,12 @@
|
||||
# under the License.
|
||||
|
||||
from novaclient import api_versions
|
||||
from novaclient import base
|
||||
from novaclient.tests.unit.fixture_data import client
|
||||
from novaclient.tests.unit.fixture_data import server_migrations as data
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.tests.unit.v2 import fakes
|
||||
from novaclient.v2 import server_migrations
|
||||
|
||||
|
||||
class ServerMigrationsTest(utils.FixturedTestCase):
|
||||
@ -31,3 +34,49 @@ class ServerMigrationsTest(utils.FixturedTestCase):
|
||||
body = {'force_complete': None}
|
||||
self.cs.server_migrations.live_migrate_force_complete(1234, 1)
|
||||
self.assert_called('POST', '/servers/1234/migrations/1/action', body)
|
||||
|
||||
|
||||
class ServerMigrationsTestV223(ServerMigrationsTest):
|
||||
|
||||
migration = {
|
||||
"created_at": "2016-01-29T13:42:02.000000",
|
||||
"dest_compute": "compute2",
|
||||
"dest_host": "1.2.3.4",
|
||||
"dest_node": "node2",
|
||||
"id": 1,
|
||||
"server_uuid": "4cfba335-03d8-49b2-8c52-e69043d1e8fe",
|
||||
"source_compute": "compute1",
|
||||
"source_node": "node1",
|
||||
"status": "running",
|
||||
"memory_total_bytes": 123456,
|
||||
"memory_processed_bytes": 12345,
|
||||
"memory_remaining_bytes": 120000,
|
||||
"disk_total_bytes": 234567,
|
||||
"disk_processed_bytes": 23456,
|
||||
"disk_remaining_bytes": 230000,
|
||||
"updated_at": "2016-01-29T13:42:02.000000"
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super(ServerMigrationsTestV223, self).setUp()
|
||||
self.cs.api_version = api_versions.APIVersion("2.23")
|
||||
|
||||
def test_list_migrations(self):
|
||||
ml = self.cs.server_migrations.list(1234)
|
||||
|
||||
self.assertIsInstance(ml, base.ListWithMeta)
|
||||
self.assert_request_id(ml, fakes.FAKE_REQUEST_ID_LIST)
|
||||
for k in self.migration:
|
||||
self.assertEqual(self.migration[k], getattr(ml[0], k))
|
||||
|
||||
self.assert_called('GET', '/servers/1234/migrations')
|
||||
|
||||
def test_get_migration(self):
|
||||
migration = self.cs.server_migrations.get(1234, 1)
|
||||
|
||||
self.assertIsInstance(migration, server_migrations.ServerMigration)
|
||||
for k in migration._info:
|
||||
self.assertEqual(self.migration[k], migration._info[k])
|
||||
self.assert_request_id(migration, fakes.FAKE_REQUEST_ID_LIST)
|
||||
|
||||
self.assert_called('GET', '/servers/1234/migrations/1')
|
||||
|
@ -1687,6 +1687,16 @@ class ShellTest(utils.TestCase):
|
||||
self.assert_called('POST', '/servers/1234/migrations/1/action',
|
||||
{'force_complete': None})
|
||||
|
||||
def test_list_migrations(self):
|
||||
self.run_command('server-migration-list sample-server',
|
||||
api_version='2.23')
|
||||
self.assert_called('GET', '/servers/1234/migrations')
|
||||
|
||||
def test_get_migration(self):
|
||||
self.run_command('server-migration-show sample-server 1',
|
||||
api_version='2.23')
|
||||
self.assert_called('GET', '/servers/1234/migrations/1')
|
||||
|
||||
def test_host_evacuate_live_with_no_target_host(self):
|
||||
self.run_command('host-evacuate-live hyper')
|
||||
self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0)
|
||||
@ -2501,6 +2511,10 @@ class ShellTest(utils.TestCase):
|
||||
self.run_command('migration-list')
|
||||
self.assert_called('GET', '/os-migrations')
|
||||
|
||||
def test_migration_list_v223(self):
|
||||
self.run_command('migration-list', api_version="2.23")
|
||||
self.assert_called('GET', '/os-migrations')
|
||||
|
||||
def test_migration_list_with_filters(self):
|
||||
self.run_command('migration-list --host host1 --cell_name child1 '
|
||||
'--status finished')
|
||||
|
@ -16,6 +16,7 @@ migration interface
|
||||
|
||||
from six.moves.urllib import parse
|
||||
|
||||
from novaclient import api_versions
|
||||
from novaclient import base
|
||||
from novaclient.i18n import _
|
||||
from novaclient.openstack.common import cliutils
|
||||
@ -71,11 +72,11 @@ class MigrationManager(base.ManagerWithFind):
|
||||
help=_('Fetch migrations for the given cell_name.'))
|
||||
def do_migration_list(cs, args):
|
||||
"""Print a list of migrations."""
|
||||
_print_migrations(cs.migrations.list(args.host, args.status,
|
||||
args.cell_name))
|
||||
migrations = cs.migrations.list(args.host, args.status, args.cell_name)
|
||||
_print_migrations(cs, migrations)
|
||||
|
||||
|
||||
def _print_migrations(migrations):
|
||||
def _print_migrations(cs, migrations):
|
||||
fields = ['Source Node', 'Dest Node', 'Source Compute', 'Dest Compute',
|
||||
'Dest Host', 'Status', 'Instance UUID', 'Old Flavor',
|
||||
'New Flavor', 'Created At', 'Updated At']
|
||||
@ -86,6 +87,14 @@ def _print_migrations(migrations):
|
||||
def new_flavor(migration):
|
||||
return migration.new_instance_type_id
|
||||
|
||||
def migration_type(migration):
|
||||
return migration.migration_type
|
||||
|
||||
formatters = {'Old Flavor': old_flavor, 'New Flavor': new_flavor}
|
||||
|
||||
if cs.api_version >= api_versions.APIVersion("2.23"):
|
||||
fields.insert(0, "Id")
|
||||
fields.append("Type")
|
||||
formatters.update({"Type": migration_type})
|
||||
|
||||
utils.print_list(migrations, fields, formatters)
|
||||
|
@ -22,7 +22,7 @@ class ServerMigration(base.Resource):
|
||||
return "<ServerMigration>"
|
||||
|
||||
|
||||
class ServerMigrationsManager(base.Manager):
|
||||
class ServerMigrationsManager(base.ManagerWithFind):
|
||||
resource_class = ServerMigration
|
||||
|
||||
@api_versions.wraps("2.22")
|
||||
@ -40,3 +40,28 @@ class ServerMigrationsManager(base.Manager):
|
||||
base.getid(migration)),
|
||||
body=body)
|
||||
return self.convert_into_with_meta(body, resp)
|
||||
|
||||
@api_versions.wraps("2.23")
|
||||
def get(self, server, migration):
|
||||
"""
|
||||
Get a migration of a specified server
|
||||
|
||||
:param server: The :class:`Server` (or its ID)
|
||||
:param migration: Migration id that will be gotten.
|
||||
:returns: An instance of
|
||||
novaclient.v2.server_migrations.ServerMigration
|
||||
"""
|
||||
return self._get('/servers/%s/migrations/%s' %
|
||||
(base.getid(server), base.getid(migration)),
|
||||
'migration')
|
||||
|
||||
@api_versions.wraps("2.23")
|
||||
def list(self, server):
|
||||
"""
|
||||
Get a migrations list of a specified server
|
||||
|
||||
:param server: The :class:`Server` (or its ID)
|
||||
:returns: An instance of novaclient.base.ListWithMeta
|
||||
"""
|
||||
return self._list(
|
||||
'/servers/%s/migrations' % base.getid(server), "migrations")
|
||||
|
@ -3847,6 +3847,42 @@ def do_live_migration_force_complete(cs, args):
|
||||
cs.server_migrations.live_migrate_force_complete(server, args.migration)
|
||||
|
||||
|
||||
@api_versions.wraps("2.23")
|
||||
@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
|
||||
def do_server_migration_list(cs, args):
|
||||
"""Get the migrations list of specified server."""
|
||||
server = _find_server(cs, args.server)
|
||||
migrations = cs.server_migrations.list(server)
|
||||
|
||||
fields = ['Id', 'Source Node', 'Dest Node', 'Source Compute',
|
||||
'Dest Compute', 'Dest Host', 'Status', 'Server UUID',
|
||||
'Created At', 'Updated At']
|
||||
|
||||
format_name = ["Total Memory Bytes", "Processed Memory Bytes",
|
||||
"Remaining Memory Bytes", "Total Disk Bytes",
|
||||
"Processed Disk Bytes", "Remaining Disk Bytes"]
|
||||
|
||||
format_key = ["memory_total_bytes", "memory_processed_bytes",
|
||||
"memory_remaining_bytes", "disk_total_bytes",
|
||||
"disk_processed_bytes", "disk_remaining_bytes"]
|
||||
|
||||
formatters = map(lambda field: utils._make_field_formatter(field)[1],
|
||||
format_key)
|
||||
formatters = dict(zip(format_name, formatters))
|
||||
|
||||
utils.print_list(migrations, fields + format_name, formatters)
|
||||
|
||||
|
||||
@api_versions.wraps("2.23")
|
||||
@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
|
||||
@cliutils.arg('migration', metavar='<migration>', help=_('ID of migration.'))
|
||||
def do_server_migration_show(cs, args):
|
||||
"""Get the migration of specified server."""
|
||||
server = _find_server(cs, args.server)
|
||||
migration = cs.server_migrations.get(server, args.migration)
|
||||
utils.print_dict(migration._info)
|
||||
|
||||
|
||||
@cliutils.arg(
|
||||
'--all-tenants',
|
||||
action='store_const',
|
||||
|
Loading…
Reference in New Issue
Block a user