Files
occi-os/api/storage/storageresource.py
2012-06-26 18:17:43 +02:00

245 lines
9.4 KiB
Python

# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright (c) 2012, Intel Performance Learning Solutions Ltd.
#
# 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.
import random
from occi import backend
from occi.extensions import infrastructure
from webob import exc
from nova import exception
from nova import log as logging
from nova import volume
#Hi I'm a logger, use me! :-)
LOG = logging.getLogger('nova.api.occi.backends.storage')
class StorageBackend(backend.KindBackend, backend.ActionBackend):
"""
Backend to handle storage resources.
"""
def __init__(self):
super(StorageBackend, self).__init__()
self.volume_api = volume.API()
def create(self, resource, extras):
"""
Creates a new volume.
"""
if 'occi.storage.size' not in resource.attributes:
exc.HTTPBadRequest()
size = float(resource.attributes['occi.storage.size'])
# TODO(dizz): A blueprint?
# OpenStack deals with size in terms of integer.
# Need to convert float to integer for now and only if the float
# can be losslessly converted to integer
# e.g. See nova/quota.py:allowed_volumes(...)
if not size.is_integer:
msg = _('Volume sizes cannot be specified as fractional floats.')
LOG.error(msg)
raise exc.HTTPBadRequest()
size = str(int(size))
msg = _("Creating volume of %s GB") % size
LOG.info(msg)
disp_name = ''
try:
disp_name = resource.attributes['occi.core.title']
except KeyError:
#Generate more suitable name as it's used for hostname
#where no hostname is supplied.
disp_name = resource.attributes['occi.core.title'] = \
str(random.randrange(0, 99999999)) + \
'-storage.occi-wg.org'
if 'occi.core.summary' in resource.attributes:
disp_descr = resource.attributes['occi.core.summary']
else:
disp_descr = disp_name
snapshot = None
# volume_type can be specified by mixin
volume_type = None
metadata = None
avail_zone = None
new_volume = self.volume_api.create(extras['nova_ctx'],
size,
disp_name,
disp_descr,
snapshot=snapshot,
volume_type=volume_type,
metadata=metadata,
availability_zone=avail_zone)
# Work around problem that instance is lazy-loaded...
new_volume = self.volume_api.get(extras['nova_ctx'], new_volume['id'])
if new_volume['status'] == 'error':
msg = _('There was an error creating the volume')
LOG.error(msg)
raise exc.HTTPServerError(msg)
resource.attributes['occi.core.id'] = str(new_volume['id'])
if new_volume['status'] == 'available':
resource.attributes['occi.storage.state'] = 'online'
resource.actions = [infrastructure.OFFLINE, infrastructure.BACKUP,
infrastructure.SNAPSHOT, infrastructure.RESIZE]
def retrieve(self, entity, extras):
"""
Gets a representation of the storage volume and presents it ready for
rendering by pyssf.
"""
v_id = int(entity.attributes['occi.core.id'])
try:
vol = self.volume_api.get(extras['nova_ctx'], v_id)
except exception.NotFound:
raise exc.HTTPNotFound()
entity.attributes['occi.storage.size'] = str(float(vol['size']))
# OS volume states:
# available, creating, deleting, in-use, error, error_deleting
if vol['status'] == 'available' or vol['status'] == 'in-use':
entity.attributes['occi.storage.state'] = 'online'
entity.actions = [infrastructure.OFFLINE, infrastructure.BACKUP,
infrastructure.SNAPSHOT, infrastructure.RESIZE]
def delete(self, entity, extras):
"""
Deletes the storage resource
"""
msg = _('Removing storage device with id: %s') % entity.identifier
LOG.info(msg)
volume_id = int(entity.attributes['occi.core.id'])
try:
vol = self.volume_api.get(extras['nova_ctx'], volume_id)
self.volume_api.delete(extras['nova_ctx'], vol)
except exception.NotFound:
raise exc.HTTPNotFound()
def action(self, entity, action, attributes, extras):
"""
Executes actions against the target storage resource.
"""
if action not in entity.actions:
raise AttributeError("This action is currently no applicable.")
elif action == infrastructure.ONLINE:
# ONLINE, ready for service, default state of a created volume.
# could this cover the attach functionality in storage link?
# The following is not an approach to use:
# self.volume_api.initialize_connection(context, volume, connector)
# By default storage is ONLINE and can not be brought OFFLINE
msg = _('Online storage action requested resource with id: %s') % \
entity.identifier
LOG.warn(msg)
raise exc.HTTPBadRequest()
elif action == infrastructure.OFFLINE:
# OFFLINE, disconnected? disconnection supported in API otherwise
# not. The following is not an approach to use:
# self.volume_api.terminate_connection(context, volume, connector)
# By default storage cannot be brought OFFLINE
msg = _('Offline storage action requested for resource: %s') % \
entity.identifier
LOG.warn(msg)
raise exc.HTTPBadRequest()
elif action == infrastructure.BACKUP:
# BACKUP: create a complete copy of the volume.
msg = _('Backup action for storage resource with id: %s') % \
entity.identifier
LOG.warn(msg)
raise exc.HTTPBadRequest()
elif action == infrastructure.SNAPSHOT:
# CDMI?!
# SNAPSHOT: create a time-stamped copy of the volume? Supported in
# OS volume API
self._snapshot_storage(entity, extras)
elif action == infrastructure.RESIZE:
# TODO(dizz): not supported by API. A blueprint candidate?
# RESIZE: increase, decrease size of volume. Not supported directly
# by the API
msg = _('Resize storage actio requested resource with id: %s') % \
entity.identifier
LOG.warn(msg)
raise exc.HTTPNotImplemented()
def _snapshot_storage(self, entity, extras, backup=False):
"""
Takes a snapshot of the specified storage resource
"""
msg = _('Snapshoting storage resource with id: %s') % entity.identifier
LOG.info(msg)
volume_id = int(entity.attributes['occi.core.id'])
vol = self.volume_api.get(extras['nova_ctx'], volume_id)
#TODO(dizz): these names/descriptions should be made better.
if backup:
name = 'backup name'
description = 'backup description'
else:
# occi.core.title, occi.core.summary
name = 'snapshot name'
description = 'snapshot description'
self.volume_api.create_snapshot(extras['nova_ctx'],
vol, name, description)
def update(self, old, new, extras):
"""
Updates simple attributes of a storage resource:
occi.core.title, occi.core.summary
"""
# update attributes.
if len(new.attributes) > 0:
msg = _('Updating mutable attributes of volume instance')
LOG.info(msg)
# support only title and summary changes now.
if (('occi.core.title' in new.attributes)
or ('occi.core.title' in new.attributes)):
if len(new.attributes['occi.core.title']) > 0:
old.attributes['occi.core.title'] = \
new.attributes['occi.core.title']
if len(new.attributes['occi.core.summary']) > 0:
old.attributes['occi.core.summary'] = \
new.attributes['occi.core.summary']
else:
msg = _('Cannot update the supplied attributes.')
LOG.error()
raise exc.HTTPBadRequest()
else:
raise exc.HTTPBadRequest()