ec2-api/ec2api/api/snapshot.py

153 lines
5.6 KiB
Python

# Copyright 2014
# The Cloudscaling Group, Inc.
#
# 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 cinderclient import exceptions as cinder_exception
from ec2api.api import clients
from ec2api.api import common
from ec2api.api import ec2utils
from ec2api.db import api as db_api
from ec2api import exception
from ec2api.i18n import _
"""Snapshot related API implementation
"""
Validator = common.Validator
def create_snapshot(context, volume_id, description=None):
volume = ec2utils.get_db_item(context, volume_id)
cinder = clients.cinder(context)
os_volume = cinder.volumes.get(volume['os_id'])
# NOTE(ft): Easy fix to allow snapshot creation in statuses other than
# AVAILABLE without cinder modifications. Potential race condition
# though. Seems arguably non-fatal.
if os_volume.status not in ['available', 'in-use',
'attaching', 'detaching']:
msg = (_("'%s' is not in a state where snapshots are allowed.") %
volume_id)
raise exception.IncorrectState(reason=msg)
with common.OnCrashCleaner() as cleaner:
os_snapshot = cinder.volume_snapshots.create(
os_volume.id, force=True,
display_description=description)
cleaner.addCleanup(os_snapshot.delete)
snapshot = db_api.add_item(context, 'snap', {'os_id': os_snapshot.id})
cleaner.addCleanup(db_api.delete_item, context, snapshot['id'])
os_snapshot.update(display_name=snapshot['id'])
return _format_snapshot(context, snapshot, os_snapshot,
volume_id=volume_id)
def delete_snapshot(context, snapshot_id):
snapshot = ec2utils.get_db_item(context, snapshot_id)
cinder = clients.cinder(context)
try:
cinder.volume_snapshots.delete(snapshot['os_id'])
except cinder_exception.NotFound:
pass
# NOTE(andrey-mp) Don't delete item from DB until it disappears from Cloud
# It will be deleted by describer in the future
return True
class SnapshotDescriber(common.TaggableItemsDescriber):
KIND = 'snap'
SORT_KEY = 'snapshotId'
FILTER_MAP = {'description': 'description',
'owner-id': 'ownerId',
'progress': 'progress',
'snapshot-id': 'snapshotId',
'start-time': 'startTime',
'status': 'status',
'volume-id': 'volumeId',
'volume-size': 'volumeSize'}
def format(self, snapshot, os_snapshot):
return _format_snapshot(self.context, snapshot, os_snapshot,
self.volumes)
def get_db_items(self):
self.volumes = {vol['os_id']: vol
for vol in db_api.get_items(self.context, 'vol')}
return super(SnapshotDescriber, self).get_db_items()
def get_os_items(self):
return clients.cinder(self.context).volume_snapshots.list()
def get_name(self, os_item):
return ''
def describe_snapshots(context, snapshot_id=None, owner=None,
restorable_by=None, filter=None,
max_results=None, next_token=None):
if snapshot_id and max_results:
msg = _('The parameter snapshotSet cannot be used with the parameter '
'maxResults')
raise exception.InvalidParameterCombination(msg)
snapshot_describer = SnapshotDescriber()
formatted_snapshots = snapshot_describer.describe(
context, ids=snapshot_id, filter=filter,
max_results=max_results, next_token=next_token)
result = {'snapshotSet': formatted_snapshots}
if snapshot_describer.next_token:
result['nextToken'] = snapshot_describer.next_token
return result
def _format_snapshot(context, snapshot, os_snapshot, volumes={},
volume_id=None):
# NOTE(mikal): this is just a set of strings in cinder. If they
# implement an enum, then we should move this code to use it. The
# valid ec2 statuses are "pending", "completed", and "error".
status_map = {'new': 'pending',
'creating': 'pending',
'available': 'completed',
'active': 'completed',
'deleting': 'pending',
'deleted': None,
'error': 'error'}
mapped_status = status_map.get(os_snapshot.status, os_snapshot.status)
if not mapped_status:
return None
if not volume_id and os_snapshot.volume_id:
volume = ec2utils.get_db_item_by_os_id(
context, 'vol', os_snapshot.volume_id, volumes)
volume_id = volume['id']
# NOTE(andrey-mp): ownerId and progress are empty in just created snapshot
ownerId = os_snapshot.project_id
if not ownerId:
ownerId = context.project_id
progress = os_snapshot.progress
if not progress:
progress = '0%'
return {'snapshotId': snapshot['id'],
'volumeId': volume_id,
'status': mapped_status,
'startTime': os_snapshot.created_at,
'progress': progress,
'ownerId': ownerId,
'volumeSize': os_snapshot.size,
'description': os_snapshot.display_description}