Skip to content

Commit

Permalink
Make backup and snapshot use objects
Browse files Browse the repository at this point in the history
This makes the backup and snapshot paths use objects.  To make things
cleaner for syncing compute rpcapi and cells rpcapi, the
single snapshot_instance rpc method that was used for both snapshot and
backup has been split into backup_instance and snapshot_instance.  The
appropriate cells versions have been added.

The backup and snapshot tests have also been converted into unit tests and
moved into test_compute_api.py.

Related to blueprint compute-api-objects

Change-Id: Ibdaee07290cf8d61c106702aa7068dd308bc9aac
  • Loading branch information
comstud authored and markmc committed Aug 29, 2013
1 parent ca989cd commit 2be50f0
Show file tree
Hide file tree
Showing 18 changed files with 610 additions and 552 deletions.
2 changes: 1 addition & 1 deletion nova/api/openstack/compute/contrib/admin_actions.py
Expand Up @@ -256,7 +256,7 @@ def _create_backup(self, req, id, body):
raise exc.HTTPBadRequest(explanation=msg)

try:
instance = self.compute_api.get(context, id)
instance = self.compute_api.get(context, id, want_objects=True)
except exception.NotFound:
raise exc.HTTPNotFound(_("Instance not found"))

Expand Down
2 changes: 1 addition & 1 deletion nova/api/openstack/compute/plugins/v3/admin_actions.py
Expand Up @@ -249,7 +249,7 @@ def _create_backup(self, req, id, body):
raise exc.HTTPBadRequest(explanation=msg)

try:
instance = self.compute_api.get(context, id)
instance = self.compute_api.get(context, id, want_objects=True)
except exception.InstanceNotFound as e:
raise exc.HTTPNotFound(explanation=e.format_message())

Expand Down
11 changes: 10 additions & 1 deletion nova/cells/manager.py
Expand Up @@ -65,7 +65,7 @@ class CellsManager(manager.Manager):
Scheduling requests get passed to the scheduler class.
"""
RPC_API_VERSION = '1.23'
RPC_API_VERSION = '1.24'

def __init__(self, *args, **kwargs):
# Mostly for tests.
Expand Down Expand Up @@ -518,3 +518,12 @@ def reset_network(self, ctxt, instance):
def inject_network_info(self, ctxt, instance):
"""Inject networking for an instance in its cell."""
self.msg_runner.inject_network_info(ctxt, instance)

def snapshot_instance(self, ctxt, instance, image_id):
"""Snapshot an instance in its cell."""
self.msg_runner.snapshot_instance(ctxt, instance, image_id)

def backup_instance(self, ctxt, instance, image_id, backup_type, rotation):
"""Backup an instance in its cell."""
self.msg_runner.backup_instance(ctxt, instance, image_id,
backup_type, rotation)
36 changes: 36 additions & 0 deletions nova/cells/messaging.py
Expand Up @@ -31,6 +31,7 @@
from nova.cells import utils as cells_utils
from nova import compute
from nova.compute import rpcapi as compute_rpcapi
from nova.compute import task_states
from nova.compute import vm_states
from nova.consoleauth import rpcapi as consoleauth_rpcapi
from nova import context
Expand Down Expand Up @@ -904,6 +905,27 @@ def inject_network_info(self, message, instance):
self._call_compute_api_with_obj(message.ctxt, instance,
'inject_network_info')

def snapshot_instance(self, message, instance, image_id):
"""Snapshot an instance in its cell."""
instance.refresh()
instance.task_state = task_states.IMAGE_SNAPSHOT
instance.save(expected_task_state=None)
self.compute_rpcapi.snapshot_instance(message.ctxt,
instance,
image_id)

def backup_instance(self, message, instance, image_id,
backup_type, rotation):
"""Backup an instance in its cell."""
instance.refresh()
instance.task_state = task_states.IMAGE_BACKUP
instance.save(expected_task_state=None)
self.compute_rpcapi.backup_instance(message.ctxt,
instance,
image_id,
backup_type,
rotation)


class _BroadcastMessageMethods(_BaseMessageMethods):
"""These are the methods that can be called as a part of a broadcast
Expand Down Expand Up @@ -1692,6 +1714,20 @@ def inject_network_info(self, ctxt, instance):
"""Inject networking for an instance in its cell."""
self._instance_action(ctxt, instance, 'inject_network_info')

def snapshot_instance(self, ctxt, instance, image_id):
"""Snapshot an instance in its cell."""
extra_kwargs = dict(image_id=image_id)
self._instance_action(ctxt, instance, 'snapshot_instance',
extra_kwargs=extra_kwargs)

def backup_instance(self, ctxt, instance, image_id, backup_type,
rotation):
"""Backup an instance in its cell."""
extra_kwargs = dict(image_id=image_id, backup_type=backup_type,
rotation=rotation)
self._instance_action(ctxt, instance, 'backup_instance',
extra_kwargs=extra_kwargs)

@staticmethod
def get_message_types():
return _CELL_MESSAGE_TYPE_TO_MESSAGE_CLS.keys()
Expand Down
19 changes: 19 additions & 0 deletions nova/cells/rpcapi.py
Expand Up @@ -80,6 +80,7 @@ class CellsAPI(rpc_proxy.RpcProxy):
1.21 - Adds revert_resize() and confirm_resize()
1.22 - Adds reset_network()
1.23 - Adds inject_network_info()
1.24 - Adds backup_instance() and snapshot_instance()
'''
BASE_RPC_API_VERSION = '1.0'

Expand Down Expand Up @@ -578,3 +579,21 @@ def inject_network_info(self, ctxt, instance):
self.cast(ctxt,
self.make_msg('inject_network_info', instance=instance),
version='1.23')

def snapshot_instance(self, ctxt, instance, image_id):
if not CONF.cells.enable:
return
self.cast(ctxt,
self.make_msg('snapshot_instance', instance=instance,
image_id=image_id),
version='1.24')

def backup_instance(self, ctxt, instance, image_id, backup_type, rotation):
if not CONF.cells.enable:
return
self.cast(ctxt,
self.make_msg('backup_instance', instance=instance,
image_id=image_id,
backup_type=backup_type,
rotation=rotation),
version='1.24')
81 changes: 38 additions & 43 deletions nova/compute/api.py
Expand Up @@ -1715,67 +1715,66 @@ def live_snapshot(self, context, instance, name, extra_properties=None,
return image_meta

@wrap_check_policy
@check_instance_cell
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED])
def backup(self, context, instance, name, backup_type, rotation,
extra_properties=None, image_id=None):
extra_properties=None):
"""Backup the given instance
:param instance: nova.db.sqlalchemy.models.Instance
:param name: name of the backup or snapshot
name = backup_type # daily backups are called 'daily'
:param name: name of the backup
:param backup_type: 'daily' or 'weekly'
:param rotation: int representing how many backups to keep around;
None if rotation shouldn't be used (as in the case of snapshots)
:param extra_properties: dict of extra image properties to include
when creating the image.
"""
if image_id:
# The image entry has already been created, so just pull the
# metadata.
image_meta = self.image_service.show(context, image_id)
else:
image_meta = self._create_image(context, instance, name,
'backup', backup_type=backup_type,
rotation=rotation, extra_properties=extra_properties)
props_copy = dict(extra_properties, backup_type=backup_type)
image_meta = self._create_image(context, instance, name,
'backup', extra_properties=props_copy)

instance = self.update(context, instance,
task_state=task_states.IMAGE_BACKUP,
expected_task_state=None)
# NOTE(comstud): Any changes to this method should also be made
# to the backup_instance() method in nova/cells/messaging.py

instance.task_state = task_states.IMAGE_BACKUP
instance.save(expected_task_state=None)

self.compute_rpcapi.snapshot_instance(context, instance=instance,
image_id=image_meta['id'], image_type='backup',
backup_type=backup_type, rotation=rotation)
self.compute_rpcapi.backup_instance(context, instance,
image_meta['id'],
backup_type,
rotation)
return image_meta

@wrap_check_policy
@check_instance_cell
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED,
vm_states.PAUSED, vm_states.SUSPENDED])
def snapshot(self, context, instance, name, extra_properties=None,
image_id=None):
def snapshot(self, context, instance, name, extra_properties=None):
"""Snapshot the given instance.
:param instance: nova.db.sqlalchemy.models.Instance
:param name: name of the backup or snapshot
:param name: name of the snapshot
:param extra_properties: dict of extra image properties to include
when creating the image.
:returns: A dict containing image metadata
"""
if image_id:
# The image entry has already been created, so just pull the
# metadata.
image_meta = self.image_service.show(context, image_id)
else:
image_meta = self._create_image(context, instance, name,
'snapshot', extra_properties=extra_properties)
image_meta = self._create_image(context, instance, name,
'snapshot',
extra_properties=extra_properties)

instance = self.update(context, instance,
task_state=task_states.IMAGE_SNAPSHOT,
expected_task_state=None)
# NOTE(comstud): Any changes to this method should also be made
# to the snapshot_instance() method in nova/cells/messaging.py

instance.task_state = task_states.IMAGE_SNAPSHOT
instance.save(expected_task_state=None)

self.compute_rpcapi.snapshot_instance(context, instance,
image_meta['id'])

self.compute_rpcapi.snapshot_instance(context, instance=instance,
image_id=image_meta['id'], image_type='snapshot')
return image_meta

def _create_image(self, context, instance, name, image_type,
backup_type=None, rotation=None, extra_properties=None):
extra_properties=None):
"""Create new image entry in the image service. This new image
will be reserved for the compute manager to upload a snapshot
or backup.
Expand All @@ -1785,11 +1784,11 @@ def _create_image(self, context, instance, name, image_type,
:param name: string for name of the snapshot
:param image_type: snapshot | backup
:param backup_type: daily | weekly
:param rotation: int representing how many backups to keep around;
None if rotation shouldn't be used (as in the case of snapshots)
:param extra_properties: dict of extra image properties to include
"""
if extra_properties is None:
extra_properties = {}
instance_uuid = instance['uuid']

properties = {
Expand All @@ -1804,24 +1803,20 @@ def _create_image(self, context, instance, name, image_type,
}

# Persist base image ref as a Glance image property
system_meta = self.db.instance_system_metadata_get(
context, instance_uuid)
system_meta = instance.system_metadata
base_image_ref = system_meta.get('image_base_image_ref')
if base_image_ref:
properties['base_image_ref'] = base_image_ref

if image_type == 'backup':
properties['backup_type'] = backup_type

elif image_type == 'snapshot':
if image_type == 'snapshot':
min_ram, min_disk = self._get_minram_mindisk_params(context,
instance)
if min_ram is not None:
sent_meta['min_ram'] = min_ram
if min_disk is not None:
sent_meta['min_disk'] = min_disk

properties.update(extra_properties or {})
properties.update(extra_properties)

# Now inherit image properties from the base image
for key, value in system_meta.items():
Expand Down
32 changes: 2 additions & 30 deletions nova/compute/cells_api.py
Expand Up @@ -43,7 +43,8 @@ class ComputeRPCAPIRedirect(object):
'soft_delete_instance', 'pause_instance',
'unpause_instance', 'revert_resize',
'confirm_resize', 'reset_network',
'inject_network_info']
'inject_network_info',
'backup_instance', 'snapshot_instance']

def __init__(self, cells_rpcapi):
self.cells_rpcapi = cells_rpcapi
Expand Down Expand Up @@ -142,35 +143,6 @@ def _check_requested_networks(self, context, requested_networks):
"""
return

def _validate_image_href(self, context, image_href):
"""Override compute API's checking of this. It'll happen in
child cell
"""
return

def backup(self, context, instance, name, backup_type, rotation,
extra_properties=None, image_id=None):
"""Backup the given instance."""
image_meta = super(ComputeCellsAPI, self).backup(context,
instance, name, backup_type, rotation,
extra_properties=extra_properties, image_id=image_id)
image_id = image_meta['id']
self._cast_to_cells(context, instance, 'backup', name,
backup_type=backup_type, rotation=rotation,
extra_properties=extra_properties, image_id=image_id)
return image_meta

def snapshot(self, context, instance, name, extra_properties=None,
image_id=None):
"""Snapshot the given instance."""
image_meta = super(ComputeCellsAPI, self).snapshot(context,
instance, name, extra_properties=extra_properties,
image_id=image_id)
image_id = image_meta['id']
self._cast_to_cells(context, instance, 'snapshot',
name, extra_properties=extra_properties, image_id=image_id)
return image_meta

def create(self, *args, **kwargs):
"""We can use the base functionality, but I left this here just
for completeness.
Expand Down

0 comments on commit 2be50f0

Please sign in to comment.