Add new APIs and deprecate old API for migrations
This patch does two things: 1. Add two APIs /servers/migrations:index/show for server migrations. Two new novaclient commands server-migration-list and server-migration-show will also be added. ref: I071198fa9ba0699383bdebf4fab54714a435e6c3 2. Add ref link for /os-migrations The old top-level resource `/os-migrations` won't be extended anymore. It is deprecated. Adding migration_type for it, also add ref link to /servers/{uuid}/migrations/{id} for it when the migration is an in progress migration. Partially implements blueprint live-migration-progress-report Change-Id: Ia92ecbe3c99082e3a34adf4fd29041b1a95ef21e Co-authored-by: ShaoHe Feng <shaohe.feng@intel.com>
This commit is contained in:
parent
058e21ec98
commit
98e4a64ad3
74
doc/api_samples/os-migrations/v2.23/migrations-get.json
Normal file
74
doc/api_samples/os-migrations/v2.23/migrations-get.json
Normal file
@ -0,0 +1,74 @@
|
||||
{
|
||||
"migrations": [
|
||||
{
|
||||
"created_at": "2016-01-29T13:42:02.000000",
|
||||
"dest_compute": "compute2",
|
||||
"dest_host": "1.2.3.4",
|
||||
"dest_node": "node2",
|
||||
"id": 1,
|
||||
"instance_uuid": "8600d31b-d1a1-4632-b2ff-45c2be1a70ff",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/v2.1/openstack/servers/8600d31b-d1a1-4632-b2ff-45c2be1a70ff/migrations/1",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "http://openstack.example.com/openstack/servers/8600d31b-d1a1-4632-b2ff-45c2be1a70ff/migrations/1",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
],
|
||||
"new_instance_type_id": 2,
|
||||
"old_instance_type_id": 1,
|
||||
"source_compute": "compute1",
|
||||
"source_node": "node1",
|
||||
"status": "running",
|
||||
"migration_type": "live-migration",
|
||||
"updated_at": "2016-01-29T13:42:02.000000"
|
||||
},
|
||||
{
|
||||
"created_at": "2016-01-29T13:42:02.000000",
|
||||
"dest_compute": "compute2",
|
||||
"dest_host": "1.2.3.4",
|
||||
"dest_node": "node2",
|
||||
"id": 2,
|
||||
"instance_uuid": "8600d31b-d1a1-4632-b2ff-45c2be1a70ff",
|
||||
"new_instance_type_id": 2,
|
||||
"old_instance_type_id": 1,
|
||||
"source_compute": "compute1",
|
||||
"source_node": "node1",
|
||||
"status": "error",
|
||||
"migration_type": "live-migration",
|
||||
"updated_at": "2016-01-29T13:42:02.000000"
|
||||
},
|
||||
{
|
||||
"created_at": "2016-01-22T13:42:02.000000",
|
||||
"dest_compute": "compute20",
|
||||
"dest_host": "5.6.7.8",
|
||||
"dest_node": "node20",
|
||||
"id": 3,
|
||||
"instance_uuid": "9128d044-7b61-403e-b766-7547076ff6c1",
|
||||
"new_instance_type_id": 6,
|
||||
"old_instance_type_id": 5,
|
||||
"source_compute": "compute10",
|
||||
"source_node": "node10",
|
||||
"status": "error",
|
||||
"migration_type": "resize",
|
||||
"updated_at": "2016-01-22T13:42:02.000000"
|
||||
},
|
||||
{
|
||||
"created_at": "2016-01-22T13:42:02.000000",
|
||||
"dest_compute": "compute20",
|
||||
"dest_host": "5.6.7.8",
|
||||
"dest_node": "node20",
|
||||
"id": 4,
|
||||
"instance_uuid": "9128d044-7b61-403e-b766-7547076ff6c1",
|
||||
"new_instance_type_id": 6,
|
||||
"old_instance_type_id": 5,
|
||||
"source_compute": "compute10",
|
||||
"source_node": "node10",
|
||||
"status": "migrating",
|
||||
"migration_type": "resize",
|
||||
"updated_at": "2016-01-22T13:42:02.000000"
|
||||
}
|
||||
]
|
||||
}
|
20
doc/api_samples/server-migrations/v2.23/migrations-get.json
Normal file
20
doc/api_samples/server-migrations/v2.23/migrations-get.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
@ -19,7 +19,7 @@
|
||||
}
|
||||
],
|
||||
"status": "CURRENT",
|
||||
"version": "2.22",
|
||||
"version": "2.23",
|
||||
"min_version": "2.1",
|
||||
"updated": "2013-07-23T11:33:21Z"
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
}
|
||||
],
|
||||
"status": "CURRENT",
|
||||
"version": "2.22",
|
||||
"version": "2.23",
|
||||
"min_version": "2.1",
|
||||
"updated": "2013-07-23T11:33:21Z"
|
||||
}
|
||||
|
@ -272,6 +272,8 @@
|
||||
"os_compute_api:servers:trigger_crash_dump": "rule:admin_or_owner",
|
||||
"os_compute_api:servers:migrations:force_complete": "rule:admin_api",
|
||||
"os_compute_api:servers:discoverable": "@",
|
||||
"os_compute_api:servers:migrations:index": "rule:admin_api",
|
||||
"os_compute_api:servers:migrations:show": "rule:admin_api",
|
||||
"os_compute_api:os-access-ips:discoverable": "@",
|
||||
"os_compute_api:os-access-ips": "rule:admin_or_owner",
|
||||
"os_compute_api:os-admin-actions": "rule:admin_api",
|
||||
|
@ -65,6 +65,9 @@ REST_API_VERSION_HISTORY = """REST API Version History:
|
||||
and shelved_offloaded state
|
||||
* 2.21 - Make os-instance-actions read deleted instances
|
||||
* 2.22 - Add API to force live migration to complete
|
||||
* 2.23 - Add index/show API for server migrations.
|
||||
Also add migration_type for /os-migrations and add ref link for it
|
||||
when the migration is an in progress live migration.
|
||||
"""
|
||||
|
||||
# The minimum and maximum versions of the API supported
|
||||
@ -73,7 +76,7 @@ REST_API_VERSION_HISTORY = """REST API Version History:
|
||||
# Note(cyeoh): This only applies for the v2.1 API once microversions
|
||||
# support is fully merged. It does not affect the V2 API.
|
||||
_MIN_API_VERSION = "2.1"
|
||||
_MAX_API_VERSION = "2.22"
|
||||
_MAX_API_VERSION = "2.23"
|
||||
DEFAULT_API_VERSION = _MIN_API_VERSION
|
||||
|
||||
|
||||
|
@ -10,6 +10,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from nova.api.openstack import api_version_request
|
||||
from nova.api.openstack import common
|
||||
from nova.api.openstack import extensions
|
||||
from nova.api.openstack import wsgi
|
||||
from nova import compute
|
||||
@ -23,42 +25,64 @@ def authorize(context, action_name):
|
||||
extensions.os_compute_authorizer(ALIAS)(context, action=action_name)
|
||||
|
||||
|
||||
def output(migrations_obj):
|
||||
"""Returns the desired output of the API from an object.
|
||||
|
||||
From a MigrationsList's object this method returns a list of
|
||||
primitive objects with the only necessary fields.
|
||||
"""
|
||||
detail_keys = ['memory_total', 'memory_processed', 'memory_remaining',
|
||||
'disk_total', 'disk_processed', 'disk_remaining']
|
||||
# Note(Shaohe Feng): We need to leverage the oslo.versionedobjects.
|
||||
# Then we can pass the target version to it's obj_to_primitive.
|
||||
objects = obj_base.obj_to_primitive(migrations_obj)
|
||||
objects = [x for x in objects if not x['hidden']]
|
||||
for obj in objects:
|
||||
del obj['deleted']
|
||||
del obj['deleted_at']
|
||||
del obj['migration_type']
|
||||
del obj['hidden']
|
||||
if 'memory_total' in obj:
|
||||
for key in detail_keys:
|
||||
del obj[key]
|
||||
|
||||
return objects
|
||||
|
||||
|
||||
class MigrationsController(wsgi.Controller):
|
||||
"""Controller for accessing migrations in OpenStack API."""
|
||||
|
||||
_view_builder_class = common.ViewBuilder
|
||||
_collection_name = "servers/%s/migrations"
|
||||
|
||||
def __init__(self):
|
||||
super(MigrationsController, self).__init__()
|
||||
self.compute_api = compute.API()
|
||||
|
||||
def _output(self, req, migrations_obj, add_link=False):
|
||||
"""Returns the desired output of the API from an object.
|
||||
|
||||
From a MigrationsList's object this method returns a list of
|
||||
primitive objects with the only necessary fields.
|
||||
"""
|
||||
detail_keys = ['memory_total', 'memory_processed', 'memory_remaining',
|
||||
'disk_total', 'disk_processed', 'disk_remaining']
|
||||
|
||||
# TODO(Shaohe Feng) we should share the in-progress list.
|
||||
live_migration_in_progress = ['queued', 'preparing',
|
||||
'running', 'post-migrating']
|
||||
|
||||
# Note(Shaohe Feng): We need to leverage the oslo.versionedobjects.
|
||||
# Then we can pass the target version to it's obj_to_primitive.
|
||||
objects = obj_base.obj_to_primitive(migrations_obj)
|
||||
objects = [x for x in objects if not x['hidden']]
|
||||
for obj in objects:
|
||||
del obj['deleted']
|
||||
del obj['deleted_at']
|
||||
del obj['hidden']
|
||||
if 'memory_total' in obj:
|
||||
for key in detail_keys:
|
||||
del obj[key]
|
||||
# NOTE(Shaohe Feng) above version 2.23, add migration_type for all
|
||||
# kinds of migration, but we only add links just for in-progress
|
||||
# live-migration.
|
||||
if add_link and obj['migration_type'] == "live-migration" and (
|
||||
obj["status"] in live_migration_in_progress):
|
||||
obj["links"] = self._view_builder._get_links(
|
||||
req, obj["id"],
|
||||
self._collection_name % obj['instance_uuid'])
|
||||
elif add_link is False:
|
||||
del obj['migration_type']
|
||||
|
||||
return objects
|
||||
|
||||
@extensions.expected_errors(())
|
||||
def index(self, req):
|
||||
"""Return all migrations in progress."""
|
||||
context = req.environ['nova.context']
|
||||
authorize(context, "index")
|
||||
migrations = self.compute_api.get_migrations(context, req.GET)
|
||||
return {'migrations': output(migrations)}
|
||||
|
||||
if api_version_request.is_supported(req, min_version='2.23'):
|
||||
return {'migrations': self._output(req, migrations, True)}
|
||||
|
||||
return {'migrations': self._output(req, migrations)}
|
||||
|
||||
|
||||
class Migrations(extensions.V21APIExtensionBase):
|
||||
|
@ -22,11 +22,39 @@ from nova.api.openstack import wsgi
|
||||
from nova.api import validation
|
||||
from nova import compute
|
||||
from nova import exception
|
||||
from nova.i18n import _
|
||||
|
||||
|
||||
ALIAS = 'servers:migrations'
|
||||
authorize = extensions.os_compute_authorizer(ALIAS)
|
||||
|
||||
|
||||
def output(migration):
|
||||
"""Returns the desired output of the API from an object.
|
||||
|
||||
From a Migrations's object this method returns the primitive
|
||||
object with the only necessary and expected fields.
|
||||
"""
|
||||
return {
|
||||
"created_at": migration.created_at,
|
||||
"dest_compute": migration.dest_compute,
|
||||
"dest_host": migration.dest_host,
|
||||
"dest_node": migration.dest_node,
|
||||
"disk_processed_bytes": migration.disk_processed,
|
||||
"disk_remaining_bytes": migration.disk_remaining,
|
||||
"disk_total_bytes": migration.disk_total,
|
||||
"id": migration.id,
|
||||
"memory_processed_bytes": migration.memory_processed,
|
||||
"memory_remaining_bytes": migration.memory_remaining,
|
||||
"memory_total_bytes": migration.memory_total,
|
||||
"server_uuid": migration.instance_uuid,
|
||||
"source_compute": migration.source_compute,
|
||||
"source_node": migration.source_node,
|
||||
"status": migration.status,
|
||||
"updated_at": migration.updated_at
|
||||
}
|
||||
|
||||
|
||||
class ServerMigrationsController(wsgi.Controller):
|
||||
"""The server migrations API controller for the OpenStack API."""
|
||||
|
||||
@ -58,6 +86,55 @@ class ServerMigrationsController(wsgi.Controller):
|
||||
common.raise_http_conflict_for_instance_invalid_state(
|
||||
state_error, 'force_complete', server_id)
|
||||
|
||||
@wsgi.Controller.api_version("2.23")
|
||||
@extensions.expected_errors(404)
|
||||
def index(self, req, server_id):
|
||||
"""Return all migrations of an instance in progress."""
|
||||
context = req.environ['nova.context']
|
||||
authorize(context, action="index")
|
||||
|
||||
# NOTE(Shaohe Feng) just check the instance is available. To keep
|
||||
# consistency with other API, check it before get migrations.
|
||||
common.get_instance(self.compute_api, context, server_id)
|
||||
|
||||
migrations = self.compute_api.get_migrations_in_progress_by_instance(
|
||||
context, server_id, 'live-migration')
|
||||
|
||||
return {'migrations': [output(migration) for migration in migrations]}
|
||||
|
||||
@wsgi.Controller.api_version("2.23")
|
||||
@extensions.expected_errors(404)
|
||||
def show(self, req, server_id, id):
|
||||
"""Return the migration of an instance in progress by id."""
|
||||
context = req.environ['nova.context']
|
||||
authorize(context, action="show")
|
||||
|
||||
# NOTE(Shaohe Feng) just check the instance is available. To keep
|
||||
# consistency with other API, check it before get migrations.
|
||||
common.get_instance(self.compute_api, context, server_id)
|
||||
|
||||
try:
|
||||
migration = self.compute_api.get_migration_by_id_and_instance(
|
||||
context, id, server_id)
|
||||
except exception.MigrationNotFoundForInstance:
|
||||
msg = _("In-progress live migration %(id)s is not found for"
|
||||
" server %(uuid)s.") % {"id": id, "uuid": server_id}
|
||||
raise exc.HTTPNotFound(explanation=msg)
|
||||
|
||||
if migration.get("migration_type") != "live-migration":
|
||||
msg = _("Migration %(id)s for server %(uuid)s is not"
|
||||
" live-migration.") % {"id": id, "uuid": server_id}
|
||||
raise exc.HTTPNotFound(explanation=msg)
|
||||
|
||||
# TODO(Shaohe Feng) we should share the in-progress list.
|
||||
in_progress = ['queued', 'preparing', 'running', 'post-migrating']
|
||||
if migration.get("status") not in in_progress:
|
||||
msg = _("Live migration %(id)s for server %(uuid)s is not in"
|
||||
" progress.") % {"id": id, "uuid": server_id}
|
||||
raise exc.HTTPNotFound(explanation=msg)
|
||||
|
||||
return {'migration': output(migration)}
|
||||
|
||||
|
||||
class ServerMigrations(extensions.V21APIExtensionBase):
|
||||
"""Server Migrations API."""
|
||||
|
@ -195,3 +195,13 @@ user documentation.
|
||||
{
|
||||
"force_complete": null
|
||||
}
|
||||
|
||||
2.23
|
||||
----
|
||||
|
||||
From this version of the API users can get the migration summary list by
|
||||
index API or the information of a specific migration by get API.
|
||||
And the old top-level resource `/os-migrations` won't be extended anymore.
|
||||
Add migration_type for old /os-migrations API, also add ref link to the
|
||||
/servers/{uuid}/migrations/{id} for it when the migration is an in-progress
|
||||
live-migration.
|
||||
|
@ -3395,6 +3395,18 @@ class API(base.Base):
|
||||
"""Get all migrations for the given filters."""
|
||||
return objects.MigrationList.get_by_filters(context, filters)
|
||||
|
||||
def get_migrations_in_progress_by_instance(self, context, instance_uuid,
|
||||
migration_type=None):
|
||||
"""Get all migrations of an instance in progress."""
|
||||
return objects.MigrationList.get_in_progress_by_instance(
|
||||
context, instance_uuid, migration_type)
|
||||
|
||||
def get_migration_by_id_and_instance(self, context,
|
||||
migration_id, instance_uuid):
|
||||
"""Get the migration of an instance by id."""
|
||||
return objects.Migration.get_by_id_and_instance(
|
||||
context, migration_id, instance_uuid)
|
||||
|
||||
@wrap_check_policy
|
||||
def volume_snapshot_create(self, context, volume_id, create_info):
|
||||
bdm = objects.BlockDeviceMapping.get_by_volume(
|
||||
|
@ -511,6 +511,13 @@ def migration_get_all_by_filters(context, filters):
|
||||
return IMPL.migration_get_all_by_filters(context, filters)
|
||||
|
||||
|
||||
def migration_get_in_progress_by_instance(context, instance_uuid,
|
||||
migration_type=None):
|
||||
"""Finds all migrations of an instance in progress."""
|
||||
return IMPL.migration_get_in_progress_by_instance(context, instance_uuid,
|
||||
migration_type)
|
||||
|
||||
|
||||
####################
|
||||
|
||||
|
||||
|
@ -4547,6 +4547,23 @@ def migration_get_in_progress_by_host_and_node(context, host, node):
|
||||
all()
|
||||
|
||||
|
||||
@main_context_manager.reader
|
||||
def migration_get_in_progress_by_instance(context, instance_uuid,
|
||||
migration_type=None):
|
||||
# TODO(Shaohe Feng) we should share the in-progress list.
|
||||
# TODO(Shaohe Feng) will also summarize all status to a new
|
||||
# MigrationStatus class.
|
||||
query = model_query(context, models.Migration).\
|
||||
filter_by(instance_uuid=instance_uuid).\
|
||||
filter(models.Migration.status.in_(['queued', 'preparing',
|
||||
'running',
|
||||
'post-migrating']))
|
||||
if migration_type:
|
||||
query = query.filter(models.Migration.migration_type == migration_type)
|
||||
|
||||
return query.all()
|
||||
|
||||
|
||||
@main_context_manager.reader
|
||||
def migration_get_all_by_filters(context, filters):
|
||||
query = model_query(context, models.Migration)
|
||||
|
@ -157,7 +157,9 @@ class MigrationList(base.ObjectListBase, base.NovaObject):
|
||||
# Migration <= 1.1
|
||||
# Version 1.1: Added use_slave to get_unconfirmed_by_dest_compute
|
||||
# Version 1.2: Migration version 1.2
|
||||
VERSION = '1.2'
|
||||
# Version 1.3: Added a new function to get in progress migrations
|
||||
# for an instance.
|
||||
VERSION = '1.3'
|
||||
|
||||
fields = {
|
||||
'objects': fields.ListOfObjectsField('Migration'),
|
||||
@ -190,3 +192,11 @@ class MigrationList(base.ObjectListBase, base.NovaObject):
|
||||
db_migrations = db.migration_get_all_by_filters(context, filters)
|
||||
return base.obj_make_list(context, cls(context), objects.Migration,
|
||||
db_migrations)
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_in_progress_by_instance(cls, context, instance_uuid,
|
||||
migration_type=None):
|
||||
db_migrations = db.migration_get_in_progress_by_instance(
|
||||
context, instance_uuid, migration_type)
|
||||
return base.obj_make_list(context, cls(context), objects.Migration,
|
||||
db_migrations)
|
||||
|
@ -0,0 +1,74 @@
|
||||
{
|
||||
"migrations": [
|
||||
{
|
||||
"created_at": "2016-01-29T13:42:02.000000",
|
||||
"dest_compute": "compute2",
|
||||
"dest_host": "1.2.3.4",
|
||||
"dest_node": "node2",
|
||||
"id": 1,
|
||||
"instance_uuid": "%(instance_1)s",
|
||||
"links": [
|
||||
{
|
||||
"href": "%(host)s/v2.1/openstack/servers/%(instance_1)s/migrations/1",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "%(host)s/openstack/servers/%(instance_1)s/migrations/1",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
],
|
||||
"new_instance_type_id": 2,
|
||||
"old_instance_type_id": 1,
|
||||
"source_compute": "compute1",
|
||||
"source_node": "node1",
|
||||
"migration_type": "live-migration",
|
||||
"status": "running",
|
||||
"updated_at": "2016-01-29T13:42:02.000000"
|
||||
},
|
||||
{
|
||||
"created_at": "2016-01-29T13:42:02.000000",
|
||||
"dest_compute": "compute2",
|
||||
"dest_host": "1.2.3.4",
|
||||
"dest_node": "node2",
|
||||
"id": 2,
|
||||
"instance_uuid": "%(instance_1)s",
|
||||
"new_instance_type_id": 2,
|
||||
"old_instance_type_id": 1,
|
||||
"source_compute": "compute1",
|
||||
"source_node": "node1",
|
||||
"migration_type": "live-migration",
|
||||
"status": "error",
|
||||
"updated_at": "2016-01-29T13:42:02.000000"
|
||||
},
|
||||
{
|
||||
"created_at": "2016-01-22T13:42:02.000000",
|
||||
"dest_compute": "compute20",
|
||||
"dest_host": "5.6.7.8",
|
||||
"dest_node": "node20",
|
||||
"id": 3,
|
||||
"instance_uuid": "%(instance_2)s",
|
||||
"new_instance_type_id": 6,
|
||||
"old_instance_type_id": 5,
|
||||
"source_compute": "compute10",
|
||||
"source_node": "node10",
|
||||
"migration_type": "resize",
|
||||
"status": "error",
|
||||
"updated_at": "2016-01-22T13:42:02.000000"
|
||||
},
|
||||
{
|
||||
"created_at": "2016-01-22T13:42:02.000000",
|
||||
"dest_compute": "compute20",
|
||||
"dest_host": "5.6.7.8",
|
||||
"dest_node": "node20",
|
||||
"id": 4,
|
||||
"instance_uuid": "%(instance_2)s",
|
||||
"new_instance_type_id": 6,
|
||||
"old_instance_type_id": 5,
|
||||
"source_compute": "compute10",
|
||||
"source_node": "node10",
|
||||
"migration_type": "resize",
|
||||
"status": "migrating",
|
||||
"updated_at": "2016-01-22T13:42:02.000000"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
{
|
||||
"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": "%(server_uuid)s",
|
||||
"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"
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
{
|
||||
"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": "%(server_uuid_1)s",
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
@ -17,6 +17,8 @@ import datetime
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
from nova import context
|
||||
from nova import objects
|
||||
from nova.tests.functional.api_sample_tests import api_sample_base
|
||||
|
||||
CONF = cfg.CONF
|
||||
@ -24,6 +26,12 @@ CONF.import_opt('osapi_compute_extension',
|
||||
'nova.api.openstack.compute.legacy_v2.extensions')
|
||||
|
||||
|
||||
# NOTE(ShaoHe Feng) here I can not use uuidsentinel, it generate a random
|
||||
# UUID. The uuid in doc/api_samples files is fixed.
|
||||
INSTANCE_UUID_1 = "8600d31b-d1a1-4632-b2ff-45c2be1a70ff"
|
||||
INSTANCE_UUID_2 = "9128d044-7b61-403e-b766-7547076ff6c1"
|
||||
|
||||
|
||||
class MigrationsSamplesJsonTest(api_sample_base.ApiSampleTestBaseV21):
|
||||
ADMIN_API = True
|
||||
extension_name = "os-migrations"
|
||||
@ -86,3 +94,101 @@ class MigrationsSamplesJsonTest(api_sample_base.ApiSampleTestBaseV21):
|
||||
|
||||
self.assertEqual(200, response.status_code)
|
||||
self._verify_response('migrations-get', {}, response, 200)
|
||||
|
||||
|
||||
class MigrationsSamplesJsonTestV2_23(api_sample_base.ApiSampleTestBaseV21):
|
||||
ADMIN_API = True
|
||||
extension_name = "os-migrations"
|
||||
microversion = '2.23'
|
||||
scenarios = [('v2_23', {'api_major_version': 'v2.1'})]
|
||||
|
||||
fake_migrations = [
|
||||
# in-progress live-migration.
|
||||
{
|
||||
'source_node': 'node1',
|
||||
'dest_node': 'node2',
|
||||
'source_compute': 'compute1',
|
||||
'dest_compute': 'compute2',
|
||||
'dest_host': '1.2.3.4',
|
||||
'status': 'running',
|
||||
'instance_uuid': INSTANCE_UUID_1,
|
||||
'old_instance_type_id': 1,
|
||||
'new_instance_type_id': 2,
|
||||
'migration_type': 'live-migration',
|
||||
'hidden': False,
|
||||
'created_at': datetime.datetime(2016, 0o1, 29, 13, 42, 2),
|
||||
'updated_at': datetime.datetime(2016, 0o1, 29, 13, 42, 2),
|
||||
'deleted_at': None,
|
||||
'deleted': False
|
||||
},
|
||||
# non in-progress live-migration.
|
||||
{
|
||||
'source_node': 'node1',
|
||||
'dest_node': 'node2',
|
||||
'source_compute': 'compute1',
|
||||
'dest_compute': 'compute2',
|
||||
'dest_host': '1.2.3.4',
|
||||
'status': 'error',
|
||||
'instance_uuid': INSTANCE_UUID_1,
|
||||
'old_instance_type_id': 1,
|
||||
'new_instance_type_id': 2,
|
||||
'migration_type': 'live-migration',
|
||||
'hidden': False,
|
||||
'created_at': datetime.datetime(2016, 0o1, 29, 13, 42, 2),
|
||||
'updated_at': datetime.datetime(2016, 0o1, 29, 13, 42, 2),
|
||||
'deleted_at': None,
|
||||
'deleted': False
|
||||
},
|
||||
# non in-progress resize.
|
||||
{
|
||||
'source_node': 'node10',
|
||||
'dest_node': 'node20',
|
||||
'source_compute': 'compute10',
|
||||
'dest_compute': 'compute20',
|
||||
'dest_host': '5.6.7.8',
|
||||
'status': 'error',
|
||||
'instance_uuid': INSTANCE_UUID_2,
|
||||
'old_instance_type_id': 5,
|
||||
'new_instance_type_id': 6,
|
||||
'migration_type': 'resize',
|
||||
'hidden': False,
|
||||
'created_at': datetime.datetime(2016, 0o1, 22, 13, 42, 2),
|
||||
'updated_at': datetime.datetime(2016, 0o1, 22, 13, 42, 2),
|
||||
'deleted_at': None,
|
||||
'deleted': False
|
||||
},
|
||||
# in-progress resize.
|
||||
{
|
||||
'source_node': 'node10',
|
||||
'dest_node': 'node20',
|
||||
'source_compute': 'compute10',
|
||||
'dest_compute': 'compute20',
|
||||
'dest_host': '5.6.7.8',
|
||||
'status': 'migrating',
|
||||
'instance_uuid': INSTANCE_UUID_2,
|
||||
'old_instance_type_id': 5,
|
||||
'new_instance_type_id': 6,
|
||||
'migration_type': 'resize',
|
||||
'hidden': False,
|
||||
'created_at': datetime.datetime(2016, 0o1, 22, 13, 42, 2),
|
||||
'updated_at': datetime.datetime(2016, 0o1, 22, 13, 42, 2),
|
||||
'deleted_at': None,
|
||||
'deleted': False
|
||||
}
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
super(MigrationsSamplesJsonTestV2_23, self).setUp()
|
||||
fake_context = context.RequestContext('fake', 'fake')
|
||||
|
||||
for mig in self.fake_migrations:
|
||||
mig_obj = objects.Migration(context=fake_context, **mig)
|
||||
mig_obj.create()
|
||||
|
||||
def test_get_migrations_v2_23(self):
|
||||
response = self._do_get('os-migrations')
|
||||
self.assertEqual(200, response.status_code)
|
||||
self._verify_response(
|
||||
'migrations-get',
|
||||
{"instance_1": INSTANCE_UUID_1, "instance_2": INSTANCE_UUID_2},
|
||||
response, 200)
|
||||
|
@ -13,12 +13,15 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import datetime
|
||||
import mock
|
||||
|
||||
from nova.conductor import manager as conductor_manager
|
||||
from nova import context
|
||||
from nova import db
|
||||
from nova import objects
|
||||
from nova.tests.functional.api_sample_tests import test_servers
|
||||
from nova.tests.unit import fake_instance
|
||||
|
||||
|
||||
class ServerMigrationsSampleJsonTest(test_servers.ServersSampleBase):
|
||||
@ -50,3 +53,108 @@ class ServerMigrationsSampleJsonTest(test_servers.ServersSampleBase):
|
||||
response = self._do_post('servers/%s/migrations/%s/action'
|
||||
% (self.uuid, '3'), 'force_complete', {})
|
||||
self.assertEqual(202, response.status_code)
|
||||
|
||||
def test_get_migration(self):
|
||||
response = self._do_get('servers/fake_id/migrations/1234')
|
||||
self.assertEqual(404, response.status_code)
|
||||
|
||||
def test_list_migrations(self):
|
||||
response = self._do_get('servers/fake_id/migrations')
|
||||
self.assertEqual(404, response.status_code)
|
||||
|
||||
|
||||
class ServerMigrationsSamplesJsonTestV2_23(test_servers.ServersSampleBase):
|
||||
ADMIN_API = True
|
||||
extension_name = "server-migrations"
|
||||
microversion = '2.23'
|
||||
scenarios = [('v2_23', {'api_major_version': 'v2.1'})]
|
||||
UUID_1 = '4cfba335-03d8-49b2-8c52-e69043d1e8fe'
|
||||
UUID_2 = '058fc419-a8a8-4e08-b62c-a9841ef9cd3f'
|
||||
|
||||
fake_migrations = [
|
||||
{
|
||||
'source_node': 'node1',
|
||||
'dest_node': 'node2',
|
||||
'source_compute': 'compute1',
|
||||
'dest_compute': 'compute2',
|
||||
'dest_host': '1.2.3.4',
|
||||
'status': 'running',
|
||||
'instance_uuid': UUID_1,
|
||||
'migration_type': 'live-migration',
|
||||
'hidden': False,
|
||||
'memory_total': 123456,
|
||||
'memory_processed': 12345,
|
||||
'memory_remaining': 120000,
|
||||
'disk_total': 234567,
|
||||
'disk_processed': 23456,
|
||||
'disk_remaining': 230000,
|
||||
'created_at': datetime.datetime(2016, 0o1, 29, 13, 42, 2),
|
||||
'updated_at': datetime.datetime(2016, 0o1, 29, 13, 42, 2),
|
||||
'deleted_at': None,
|
||||
'deleted': False
|
||||
},
|
||||
{
|
||||
'source_node': 'node10',
|
||||
'dest_node': 'node20',
|
||||
'source_compute': 'compute10',
|
||||
'dest_compute': 'compute20',
|
||||
'dest_host': '5.6.7.8',
|
||||
'status': 'migrating',
|
||||
'instance_uuid': UUID_2,
|
||||
'migration_type': 'resize',
|
||||
'hidden': False,
|
||||
'memory_total': 456789,
|
||||
'memory_processed': 56789,
|
||||
'memory_remaining': 45000,
|
||||
'disk_total': 96789,
|
||||
'disk_processed': 6789,
|
||||
'disk_remaining': 96000,
|
||||
'created_at': datetime.datetime(2016, 0o1, 22, 13, 42, 2),
|
||||
'updated_at': datetime.datetime(2016, 0o1, 22, 13, 42, 2),
|
||||
'deleted_at': None,
|
||||
'deleted': False
|
||||
}
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
super(ServerMigrationsSamplesJsonTestV2_23, self).setUp()
|
||||
fake_context = context.RequestContext('fake', 'fake')
|
||||
|
||||
self.mig1 = objects.Migration(
|
||||
context=fake_context, **self.fake_migrations[0])
|
||||
self.mig1.create()
|
||||
|
||||
self.mig2 = objects.Migration(
|
||||
context=fake_context, **self.fake_migrations[1])
|
||||
self.mig2.create()
|
||||
|
||||
fake_ins = fake_instance.fake_db_instance(uuid=self.UUID_1)
|
||||
fake_ins.pop("pci_devices")
|
||||
fake_ins.pop("security_groups")
|
||||
fake_ins.pop("services")
|
||||
fake_ins.pop("tags")
|
||||
fake_ins.pop("info_cache")
|
||||
fake_ins.pop("id")
|
||||
self.instance = objects.Instance(
|
||||
context=fake_context,
|
||||
**fake_ins)
|
||||
self.instance.create()
|
||||
|
||||
def test_get_migration(self):
|
||||
response = self._do_get('servers/%s/migrations/%s' %
|
||||
(self.fake_migrations[0]["instance_uuid"],
|
||||
self.mig1.id))
|
||||
self.assertEqual(200, response.status_code)
|
||||
|
||||
self._verify_response('migrations-get',
|
||||
{"server_uuid": self.UUID_1},
|
||||
response, 200)
|
||||
|
||||
def test_list_migrations(self):
|
||||
response = self._do_get('servers/%s/migrations' %
|
||||
self.fake_migrations[0]["instance_uuid"])
|
||||
self.assertEqual(200, response.status_code)
|
||||
|
||||
self._verify_response('migrations-index',
|
||||
{"server_uuid_1": self.UUID_1},
|
||||
response, 200)
|
||||
|
@ -13,6 +13,7 @@
|
||||
# under the License.
|
||||
|
||||
import datetime
|
||||
import mock
|
||||
|
||||
from oslotest import moxstubout
|
||||
|
||||
@ -25,21 +26,23 @@ from nova import objects
|
||||
from nova.objects import base
|
||||
from nova import test
|
||||
from nova.tests.unit.api.openstack import fakes
|
||||
from nova.tests import uuidsentinel as uuids
|
||||
|
||||
|
||||
fake_migrations = [
|
||||
# in-progress live migration
|
||||
{
|
||||
'id': 1234,
|
||||
'id': 1,
|
||||
'source_node': 'node1',
|
||||
'dest_node': 'node2',
|
||||
'source_compute': 'compute1',
|
||||
'dest_compute': 'compute2',
|
||||
'dest_host': '1.2.3.4',
|
||||
'status': 'Done',
|
||||
'instance_uuid': 'instance_id_123',
|
||||
'status': 'running',
|
||||
'instance_uuid': uuids.instance1,
|
||||
'old_instance_type_id': 1,
|
||||
'new_instance_type_id': 2,
|
||||
'migration_type': 'resize',
|
||||
'migration_type': 'live-migration',
|
||||
'hidden': False,
|
||||
'memory_total': 123456,
|
||||
'memory_processed': 12345,
|
||||
@ -52,15 +55,66 @@ fake_migrations = [
|
||||
'deleted_at': None,
|
||||
'deleted': False
|
||||
},
|
||||
# non in-progress live migration
|
||||
{
|
||||
'id': 5678,
|
||||
'id': 2,
|
||||
'source_node': 'node1',
|
||||
'dest_node': 'node2',
|
||||
'source_compute': 'compute1',
|
||||
'dest_compute': 'compute2',
|
||||
'dest_host': '1.2.3.4',
|
||||
'status': 'error',
|
||||
'instance_uuid': uuids.instance1,
|
||||
'old_instance_type_id': 1,
|
||||
'new_instance_type_id': 2,
|
||||
'migration_type': 'live-migration',
|
||||
'hidden': False,
|
||||
'memory_total': 123456,
|
||||
'memory_processed': 12345,
|
||||
'memory_remaining': 120000,
|
||||
'disk_total': 234567,
|
||||
'disk_processed': 23456,
|
||||
'disk_remaining': 230000,
|
||||
'created_at': datetime.datetime(2012, 10, 29, 13, 42, 2),
|
||||
'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 2),
|
||||
'deleted_at': None,
|
||||
'deleted': False
|
||||
},
|
||||
# in-progress resize
|
||||
{
|
||||
'id': 4,
|
||||
'source_node': 'node10',
|
||||
'dest_node': 'node20',
|
||||
'source_compute': 'compute10',
|
||||
'dest_compute': 'compute20',
|
||||
'dest_host': '5.6.7.8',
|
||||
'status': 'Done',
|
||||
'instance_uuid': 'instance_id_456',
|
||||
'status': 'migrating',
|
||||
'instance_uuid': uuids.instance2,
|
||||
'old_instance_type_id': 5,
|
||||
'new_instance_type_id': 6,
|
||||
'migration_type': 'resize',
|
||||
'hidden': False,
|
||||
'memory_total': 456789,
|
||||
'memory_processed': 56789,
|
||||
'memory_remaining': 45000,
|
||||
'disk_total': 96789,
|
||||
'disk_processed': 6789,
|
||||
'disk_remaining': 96000,
|
||||
'created_at': datetime.datetime(2013, 10, 22, 13, 42, 2),
|
||||
'updated_at': datetime.datetime(2013, 10, 22, 13, 42, 2),
|
||||
'deleted_at': None,
|
||||
'deleted': False
|
||||
},
|
||||
# non in-progress resize
|
||||
{
|
||||
'id': 5,
|
||||
'source_node': 'node10',
|
||||
'dest_node': 'node20',
|
||||
'source_compute': 'compute10',
|
||||
'dest_compute': 'compute20',
|
||||
'dest_host': '5.6.7.8',
|
||||
'status': 'error',
|
||||
'instance_uuid': uuids.instance2,
|
||||
'old_instance_type_id': 5,
|
||||
'new_instance_type_id': 6,
|
||||
'migration_type': 'resize',
|
||||
@ -94,23 +148,26 @@ class FakeRequest(object):
|
||||
class MigrationsTestCaseV21(test.NoDBTestCase):
|
||||
migrations = migrations_v21
|
||||
|
||||
def _migrations_output(self):
|
||||
return self.controller._output(self.req, migrations_obj)
|
||||
|
||||
def setUp(self):
|
||||
"""Run before each test."""
|
||||
super(MigrationsTestCaseV21, self).setUp()
|
||||
self.controller = self.migrations.MigrationsController()
|
||||
self.req = FakeRequest()
|
||||
self.req = fakes.HTTPRequest.blank('', use_admin_context=True)
|
||||
self.context = self.req.environ['nova.context']
|
||||
mox_fixture = self.useFixture(moxstubout.MoxStubout())
|
||||
self.mox = mox_fixture.mox
|
||||
|
||||
def test_index(self):
|
||||
migrations_in_progress = {
|
||||
'migrations': self.migrations.output(migrations_obj)}
|
||||
migrations_in_progress = {'migrations': self._migrations_output()}
|
||||
|
||||
for mig in migrations_in_progress['migrations']:
|
||||
self.assertIn('id', mig)
|
||||
self.assertNotIn('deleted', mig)
|
||||
self.assertNotIn('deleted_at', mig)
|
||||
self.assertNotIn('links', mig)
|
||||
|
||||
filters = {'host': 'host1', 'status': 'migrating',
|
||||
'cell_name': 'ChildCell'}
|
||||
@ -129,9 +186,11 @@ class MigrationsTestCaseV21(test.NoDBTestCase):
|
||||
class MigrationsTestCaseV2(MigrationsTestCaseV21):
|
||||
migrations = migrations_v2
|
||||
|
||||
def _migrations_output(self):
|
||||
return self.migrations.output(migrations_obj)
|
||||
|
||||
def setUp(self):
|
||||
super(MigrationsTestCaseV2, self).setUp()
|
||||
self.req = fakes.HTTPRequest.blank('', use_admin_context=True)
|
||||
self.context = self.req.environ['nova.context']
|
||||
|
||||
def test_index_needs_authorization(self):
|
||||
@ -146,6 +205,40 @@ class MigrationsTestCaseV2(MigrationsTestCaseV21):
|
||||
self.req)
|
||||
|
||||
|
||||
class MigrationsTestCaseV223(MigrationsTestCaseV21):
|
||||
wsgi_api_version = '2.23'
|
||||
|
||||
def setUp(self):
|
||||
"""Run before each test."""
|
||||
super(MigrationsTestCaseV223, self).setUp()
|
||||
self.req = fakes.HTTPRequest.blank(
|
||||
'', version=self.wsgi_api_version, use_admin_context=True)
|
||||
|
||||
def test_index(self):
|
||||
migrations = {'migrations': self.controller._output(
|
||||
self.req, migrations_obj, True)}
|
||||
|
||||
for i, mig in enumerate(migrations['migrations']):
|
||||
# first item is in-progress live migration
|
||||
if i == 0:
|
||||
self.assertIn('links', mig)
|
||||
else:
|
||||
self.assertNotIn('links', mig)
|
||||
|
||||
self.assertIn('migration_type', mig)
|
||||
self.assertIn('id', mig)
|
||||
self.assertNotIn('deleted', mig)
|
||||
self.assertNotIn('deleted_at', mig)
|
||||
|
||||
with mock.patch.object(self.controller.compute_api,
|
||||
'get_migrations') as m_get:
|
||||
m_get.return_value = migrations_obj
|
||||
response = self.controller.index(self.req)
|
||||
self.assertEqual(migrations, response)
|
||||
self.assertIn('links', response['migrations'][0])
|
||||
self.assertIn('migration_type', response['migrations'][0])
|
||||
|
||||
|
||||
class MigrationsPolicyEnforcement(test.NoDBTestCase):
|
||||
def setUp(self):
|
||||
super(MigrationsPolicyEnforcement, self).setUp()
|
||||
@ -161,3 +254,11 @@ class MigrationsPolicyEnforcement(test.NoDBTestCase):
|
||||
self.assertEqual(
|
||||
"Policy doesn't allow %s to be performed." % rule_name,
|
||||
exc.format_message())
|
||||
|
||||
|
||||
class MigrationsPolicyEnforcementV223(MigrationsPolicyEnforcement):
|
||||
wsgi_api_version = '2.23'
|
||||
|
||||
def setUp(self):
|
||||
super(MigrationsPolicyEnforcementV223, self).setUp()
|
||||
self.req = fakes.HTTPRequest.blank('', version=self.wsgi_api_version)
|
||||
|
@ -13,13 +13,77 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
import datetime
|
||||
import mock
|
||||
import webob
|
||||
|
||||
from nova.api.openstack.compute import server_migrations
|
||||
from nova import exception
|
||||
from nova import objects
|
||||
from nova.objects import base
|
||||
from nova import test
|
||||
from nova.tests.unit.api.openstack import fakes
|
||||
from nova.tests import uuidsentinel as uuids
|
||||
|
||||
SERVER_UUID = uuids.server_uuid
|
||||
|
||||
fake_migrations = [
|
||||
{
|
||||
'id': 1234,
|
||||
'source_node': 'node1',
|
||||
'dest_node': 'node2',
|
||||
'source_compute': 'compute1',
|
||||
'dest_compute': 'compute2',
|
||||
'dest_host': '1.2.3.4',
|
||||
'status': 'running',
|
||||
'instance_uuid': SERVER_UUID,
|
||||
'old_instance_type_id': 1,
|
||||
'new_instance_type_id': 2,
|
||||
'migration_type': 'live-migration',
|
||||
'hidden': False,
|
||||
'memory_total': 123456,
|
||||
'memory_processed': 12345,
|
||||
'memory_remaining': 120000,
|
||||
'disk_total': 234567,
|
||||
'disk_processed': 23456,
|
||||
'disk_remaining': 230000,
|
||||
'created_at': datetime.datetime(2012, 10, 29, 13, 42, 2),
|
||||
'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 2),
|
||||
'deleted_at': None,
|
||||
'deleted': False
|
||||
},
|
||||
{
|
||||
'id': 5678,
|
||||
'source_node': 'node10',
|
||||
'dest_node': 'node20',
|
||||
'source_compute': 'compute10',
|
||||
'dest_compute': 'compute20',
|
||||
'dest_host': '5.6.7.8',
|
||||
'status': 'running',
|
||||
'instance_uuid': SERVER_UUID,
|
||||
'old_instance_type_id': 5,
|
||||
'new_instance_type_id': 6,
|
||||
'migration_type': 'live-migration',
|
||||
'hidden': False,
|
||||
'memory_total': 456789,
|
||||
'memory_processed': 56789,
|
||||
'memory_remaining': 45000,
|
||||
'disk_total': 96789,
|
||||
'disk_processed': 6789,
|
||||
'disk_remaining': 96000,
|
||||
'created_at': datetime.datetime(2013, 10, 22, 13, 42, 2),
|
||||
'updated_at': datetime.datetime(2013, 10, 22, 13, 42, 2),
|
||||
'deleted_at': None,
|
||||
'deleted': False
|
||||
}
|
||||
]
|
||||
|
||||
migrations_obj = base.obj_make_list(
|
||||
'fake-context',
|
||||
objects.MigrationList(),
|
||||
objects.Migration,
|
||||
fake_migrations)
|
||||
|
||||
|
||||
class ServerMigrationsTestsV21(test.NoDBTestCase):
|
||||
@ -86,6 +150,117 @@ class ServerMigrationsTestsV21(test.NoDBTestCase):
|
||||
exception.NovaException(), webob.exc.HTTPInternalServerError)
|
||||
|
||||
|
||||
class ServerMigrationsTestsV223(ServerMigrationsTestsV21):
|
||||
wsgi_api_version = '2.23'
|
||||
|
||||
def setUp(self):
|
||||
super(ServerMigrationsTestsV223, self).setUp()
|
||||
self.req = fakes.HTTPRequest.blank('', version=self.wsgi_api_version,
|
||||
use_admin_context=True)
|
||||
self.context = self.req.environ['nova.context']
|
||||
|
||||
@mock.patch('nova.compute.api.API.get_migrations_in_progress_by_instance')
|
||||
@mock.patch('nova.compute.api.API.get')
|
||||
def test_index(self, m_get_instance, m_get_mig):
|
||||
migrations = [server_migrations.output(mig) for mig in migrations_obj]
|
||||
migrations_in_progress = {'migrations': migrations}
|
||||
|
||||
for mig in migrations_in_progress['migrations']:
|
||||
self.assertIn('id', mig)
|
||||
self.assertNotIn('deleted', mig)
|
||||
self.assertNotIn('deleted_at', mig)
|
||||
|
||||
m_get_mig.return_value = migrations_obj
|
||||
response = self.controller.index(self.req, SERVER_UUID)
|
||||
self.assertEqual(migrations_in_progress, response)
|
||||
|
||||
m_get_instance.assert_called_once_with(self.context, SERVER_UUID,
|
||||
expected_attrs=None,
|
||||
want_objects=True)
|
||||
|
||||
@mock.patch('nova.compute.api.API.get')
|
||||
def test_index_invalid_instance(self, m_get_instance):
|
||||
m_get_instance.side_effect = exception.InstanceNotFound(instance_id=1)
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.index,
|
||||
self.req, SERVER_UUID)
|
||||
|
||||
m_get_instance.assert_called_once_with(self.context, SERVER_UUID,
|
||||
expected_attrs=None,
|
||||
want_objects=True)
|
||||
|
||||
@mock.patch('nova.compute.api.API.get_migration_by_id_and_instance')
|
||||
@mock.patch('nova.compute.api.API.get')
|
||||
def test_show(self, m_get_instance, m_get_mig):
|
||||
migrations = [server_migrations.output(mig) for mig in migrations_obj]
|
||||
m_get_mig.return_value = migrations_obj[0]
|
||||
response = self.controller.show(self.req, SERVER_UUID,
|
||||
migrations_obj[0].id)
|
||||
self.assertEqual(migrations[0], response['migration'])
|
||||
|
||||
m_get_instance.assert_called_once_with(self.context, SERVER_UUID,
|
||||
expected_attrs=None,
|
||||
want_objects=True)
|
||||
|
||||
@mock.patch('nova.compute.api.API.get_migration_by_id_and_instance')
|
||||
@mock.patch('nova.compute.api.API.get')
|
||||
def test_show_migration_non_progress(self, m_get_instance, m_get_mig):
|
||||
non_progress_mig = copy.deepcopy(migrations_obj[0])
|
||||
non_progress_mig.status = "reverted"
|
||||
m_get_mig.return_value = non_progress_mig
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.show,
|
||||
self.req, SERVER_UUID,
|
||||
non_progress_mig.id)
|
||||
|
||||
m_get_instance.assert_called_once_with(self.context, SERVER_UUID,
|
||||
expected_attrs=None,
|
||||
want_objects=True)
|
||||
|
||||
@mock.patch('nova.compute.api.API.get_migration_by_id_and_instance')
|
||||
@mock.patch('nova.compute.api.API.get')
|
||||
def test_show_migration_not_live_migration(self, m_get_instance,
|
||||
m_get_mig):
|
||||
non_progress_mig = copy.deepcopy(migrations_obj[0])
|
||||
non_progress_mig.migration_type = "resize"
|
||||
m_get_mig.return_value = non_progress_mig
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.show,
|
||||
self.req, SERVER_UUID,
|
||||
non_progress_mig.id)
|
||||
|
||||
m_get_instance.assert_called_once_with(self.context, SERVER_UUID,
|
||||
expected_attrs=None,
|
||||
want_objects=True)
|
||||
|
||||
@mock.patch('nova.compute.api.API.get_migration_by_id_and_instance')
|
||||
@mock.patch('nova.compute.api.API.get')
|
||||
def test_show_migration_not_exist(self, m_get_instance, m_get_mig):
|
||||
m_get_mig.side_effect = exception.MigrationNotFoundForInstance(
|
||||
migration_id=migrations_obj[0].id,
|
||||
instance_id=SERVER_UUID)
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.show,
|
||||
self.req, SERVER_UUID,
|
||||
migrations_obj[0].id)
|
||||
|
||||
m_get_instance.assert_called_once_with(self.context, SERVER_UUID,
|
||||
expected_attrs=None,
|
||||
want_objects=True)
|
||||
|
||||
@mock.patch('nova.compute.api.API.get')
|
||||
def test_show_migration_invalid_instance(self, m_get_instance):
|
||||
m_get_instance.side_effect = exception.InstanceNotFound(instance_id=1)
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.show,
|
||||
self.req, SERVER_UUID,
|
||||
migrations_obj[0].id)
|
||||
|
||||
m_get_instance.assert_called_once_with(self.context, SERVER_UUID,
|
||||
expected_attrs=None,
|
||||
want_objects=True)
|
||||
|
||||
|
||||
class ServerMigrationsPolicyEnforcementV21(test.NoDBTestCase):
|
||||
wsgi_api_version = '2.22'
|
||||
|
||||
@ -106,3 +281,30 @@ class ServerMigrationsPolicyEnforcementV21(test.NoDBTestCase):
|
||||
self.assertEqual(
|
||||
"Policy doesn't allow %s to be performed." % rule_name,
|
||||
exc.format_message())
|
||||
|
||||
|
||||
class ServerMigrationsPolicyEnforcementV223(
|
||||
ServerMigrationsPolicyEnforcementV21):
|
||||
|
||||
wsgi_api_version = '2.23'
|
||||
|
||||
def setUp(self):
|
||||
super(ServerMigrationsPolicyEnforcementV223, self).setUp()
|
||||
|
||||
def test_migration_index_failed(self):
|
||||
rule_name = "os_compute_api:servers:migrations:index"
|
||||
self.policy.set_rules({rule_name: "project:non_fake"})
|
||||
exc = self.assertRaises(exception.PolicyNotAuthorized,
|
||||
self.controller.index, self.req,
|
||||
fakes.FAKE_UUID)
|
||||
self.assertEqual("Policy doesn't allow %s to be performed." %
|
||||
rule_name, exc.format_message())
|
||||
|
||||
def test_migration_show_failed(self):
|
||||
rule_name = "os_compute_api:servers:migrations:show"
|
||||
self.policy.set_rules({rule_name: "project:non_fake"})
|
||||
exc = self.assertRaises(exception.PolicyNotAuthorized,
|
||||
self.controller.show, self.req,
|
||||
fakes.FAKE_UUID, 1)
|
||||
self.assertEqual("Policy doesn't allow %s to be performed." %
|
||||
rule_name, exc.format_message())
|
||||
|
@ -10179,6 +10179,26 @@ class ComputeAPITestCase(BaseTestCase):
|
||||
self.assertEqual(1, len(migrations))
|
||||
self.assertEqual(migrations[0].id, migration['id'])
|
||||
|
||||
@mock.patch("nova.db.migration_get_in_progress_by_instance")
|
||||
def test_get_migrations_in_progress_by_instance(self, mock_get):
|
||||
migration = test_migration.fake_db_migration(instance_uuid="1234")
|
||||
mock_get.return_value = [migration]
|
||||
db.migration_get_in_progress_by_instance(self.context, "1234")
|
||||
migrations = self.compute_api.get_migrations_in_progress_by_instance(
|
||||
self.context, "1234")
|
||||
self.assertEqual(1, len(migrations))
|
||||
self.assertEqual(migrations[0].id, migration['id'])
|
||||
|
||||
@mock.patch("nova.db.migration_get_by_id_and_instance")
|
||||
def test_get_migration_by_id_and_instance(self, mock_get):
|
||||
migration = test_migration.fake_db_migration(instance_uuid="1234")
|
||||
mock_get.return_value = migration
|
||||
db.migration_get_by_id_and_instance(
|
||||
self.context, migration['id'], uuid)
|
||||
res = self.compute_api.get_migration_by_id_and_instance(
|
||||
self.context, migration['id'], "1234")
|
||||
self.assertEqual(res.id, migration['id'])
|
||||
|
||||
|
||||
class ComputeAPIIpFilterTestCase(test.NoDBTestCase):
|
||||
'''Verifies the IP filtering in the compute API.'''
|
||||
|