¡@

Home 

OpenStack Study: volume_actions.py

OpenStack Index

**** CubicPower OpenStack Study ****

# Copyright 2012 OpenStack Foundation

#

# 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 webob

from oslo import messaging

from cinder.api import extensions

from cinder.api.openstack import wsgi

from cinder.api import xmlutil

from cinder import exception

from cinder.openstack.common import log as logging

from cinder.openstack.common import strutils

from cinder import utils

from cinder import volume

LOG = logging.getLogger(__name__)

**** CubicPower OpenStack Study ****

def authorize(context, action_name):

    action = 'volume_actions:%s' % action_name

    extensions.extension_authorizer('volume', action)(context)

**** CubicPower OpenStack Study ****

class VolumeToImageSerializer(xmlutil.TemplateBuilder):

**** CubicPower OpenStack Study ****

    def construct(self):

        root = xmlutil.TemplateElement('os-volume_upload_image',

                                       selector='os-volume_upload_image')

        root.set('id')

        root.set('updated_at')

        root.set('status')

        root.set('display_description')

        root.set('size')

        root.set('volume_type')

        root.set('image_id')

        root.set('container_format')

        root.set('disk_format')

        root.set('image_name')

        return xmlutil.MasterTemplate(root, 1)

**** CubicPower OpenStack Study ****

class VolumeToImageDeserializer(wsgi.XMLDeserializer):

"""Deserializer to handle xml-formatted requests."""

**** CubicPower OpenStack Study ****

    def default(self, string):

        dom = utils.safe_minidom_parse_string(string)

        action_node = dom.childNodes[0]

        action_name = action_node.tagName

        action_data = {}

        attributes = ["force", "image_name", "container_format", "disk_format"]

        for attr in attributes:

            if action_node.hasAttribute(attr):

                action_data[attr] = action_node.getAttribute(attr)

        if 'force' in action_data and action_data['force'] == 'True':

            action_data['force'] = True

        return {'body': {action_name: action_data}}

**** CubicPower OpenStack Study ****

class VolumeActionsController(wsgi.Controller):

**** CubicPower OpenStack Study ****

    def __init__(self, *args, **kwargs):

        super(VolumeActionsController, self).__init__(*args, **kwargs)

        self.volume_api = volume.API()

    @wsgi.action('os-attach')

**** CubicPower OpenStack Study ****

    def _attach(self, req, id, body):

        """Add attachment metadata."""

        context = req.environ['cinder.context']

        try:

            volume = self.volume_api.get(context, id)

        except exception.VolumeNotFound as error:

            raise webob.exc.HTTPNotFound(explanation=error.msg)

        # instance uuid is an option now

        instance_uuid = None

        if 'instance_uuid' in body['os-attach']:

            instance_uuid = body['os-attach']['instance_uuid']

        host_name = None

        # Keep API backward compatibility

        if 'host_name' in body['os-attach']:

            host_name = body['os-attach']['host_name']

        mountpoint = body['os-attach']['mountpoint']

        if 'mode' in body['os-attach']:

            mode = body['os-attach']['mode']

        else:

            mode = 'rw'

        if instance_uuid and host_name:

            msg = _("Invalid request to attach volume to an "

                    "instance %(instance_uuid)s and a "

                    "host %(host_name)s simultaneously") % {

                        'instance_uuid': instance_uuid,

                        'host_name': host_name,

                    }

            raise webob.exc.HTTPBadRequest(explanation=msg)

        elif instance_uuid is None and host_name is None:

            msg = _("Invalid request to attach volume to an invalid target")

            raise webob.exc.HTTPBadRequest(explanation=msg)

        if mode not in ('rw', 'ro'):

            msg = _("Invalid request to attach volume with an invalid mode. "

                    "Attaching mode should be 'rw' or 'ro'")

            raise webob.exc.HTTPBadRequest(explanation=msg)

        self.volume_api.attach(context, volume,

                               instance_uuid, host_name, mountpoint, mode)

        return webob.Response(status_int=202)

    @wsgi.action('os-detach')

**** CubicPower OpenStack Study ****

    def _detach(self, req, id, body):

        """Clear attachment metadata."""

        context = req.environ['cinder.context']

        try:

            volume = self.volume_api.get(context, id)

        except exception.VolumeNotFound as error:

            raise webob.exc.HTTPNotFound(explanation=error.msg)

        self.volume_api.detach(context, volume)

        return webob.Response(status_int=202)

    @wsgi.action('os-reserve')

**** CubicPower OpenStack Study ****

    def _reserve(self, req, id, body):

        """Mark volume as reserved."""

        context = req.environ['cinder.context']

        try:

            volume = self.volume_api.get(context, id)

        except exception.VolumeNotFound as error:

            raise webob.exc.HTTPNotFound(explanation=error.msg)

        self.volume_api.reserve_volume(context, volume)

        return webob.Response(status_int=202)

    @wsgi.action('os-unreserve')

**** CubicPower OpenStack Study ****

    def _unreserve(self, req, id, body):

        """Unmark volume as reserved."""

        context = req.environ['cinder.context']

        try:

            volume = self.volume_api.get(context, id)

        except exception.VolumeNotFound as error:

            raise webob.exc.HTTPNotFound(explanation=error.msg)

        self.volume_api.unreserve_volume(context, volume)

        return webob.Response(status_int=202)

    @wsgi.action('os-begin_detaching')

**** CubicPower OpenStack Study ****

    def _begin_detaching(self, req, id, body):

        """Update volume status to 'detaching'."""

        context = req.environ['cinder.context']

        try:

            volume = self.volume_api.get(context, id)

        except exception.VolumeNotFound as error:

            raise webob.exc.HTTPNotFound(explanation=error.msg)

        self.volume_api.begin_detaching(context, volume)

        return webob.Response(status_int=202)

    @wsgi.action('os-roll_detaching')

**** CubicPower OpenStack Study ****

    def _roll_detaching(self, req, id, body):

        """Roll back volume status to 'in-use'."""

        context = req.environ['cinder.context']

        try:

            volume = self.volume_api.get(context, id)

        except exception.VolumeNotFound as error:

            raise webob.exc.HTTPNotFound(explanation=error.msg)

        self.volume_api.roll_detaching(context, volume)

        return webob.Response(status_int=202)

    @wsgi.action('os-initialize_connection')

**** CubicPower OpenStack Study ****

    def _initialize_connection(self, req, id, body):

        """Initialize volume attachment."""

        context = req.environ['cinder.context']

        try:

            volume = self.volume_api.get(context, id)

        except exception.VolumeNotFound as error:

            raise webob.exc.HTTPNotFound(explanation=error.msg)

        try:

            connector = body['os-initialize_connection']['connector']

        except KeyError:

            raise webob.exc.HTTPBadRequest("Must specify 'connector'")

        try:

            info = self.volume_api.initialize_connection(context,

                                                         volume,

                                                         connector)

        except exception.VolumeBackendAPIException as error:

            msg = _("Unable to fetch connection information from backend.")

            raise webob.exc.HTTPInternalServerError(msg)

        return {'connection_info': info}

    @wsgi.action('os-terminate_connection')

**** CubicPower OpenStack Study ****

    def _terminate_connection(self, req, id, body):

        """Terminate volume attachment."""

        context = req.environ['cinder.context']

        try:

            volume = self.volume_api.get(context, id)

        except exception.VolumeNotFound as error:

            raise webob.exc.HTTPNotFound(explanation=error.msg)

        try:

            connector = body['os-terminate_connection']['connector']

        except KeyError:

            raise webob.exc.HTTPBadRequest("Must specify 'connector'")

        try:

            self.volume_api.terminate_connection(context, volume, connector)

        except exception.VolumeBackendAPIException as error:

            msg = _("Unable to terminate volume connection from backend.")

            raise webob.exc.HTTPInternalServerError(explanation=msg)

        return webob.Response(status_int=202)

    @wsgi.response(202)

    @wsgi.action('os-volume_upload_image')

    @wsgi.serializers(xml=VolumeToImageSerializer)

    @wsgi.deserializers(xml=VolumeToImageDeserializer)

**** CubicPower OpenStack Study ****

    def _volume_upload_image(self, req, id, body):

        """Uploads the specified volume to image service."""

        context = req.environ['cinder.context']

        params = body['os-volume_upload_image']

        if not params.get("image_name"):

            msg = _("No image_name was specified in request.")

            raise webob.exc.HTTPBadRequest(explanation=msg)

        force = params.get('force', False)

        if isinstance(force, basestring):

            try:

                force = strutils.bool_from_string(force, strict=False)

            except ValueError:

                msg = _("Bad value for 'force' parameter.")

                raise webob.exc.HTTPBadRequest(explanation=msg)

        elif not isinstance(force, bool):

            msg = _("'force' is not string or bool.")

            raise webob.exc.HTTPBadRequest(explanation=msg)

        try:

            volume = self.volume_api.get(context, id)

        except exception.VolumeNotFound as error:

            raise webob.exc.HTTPNotFound(explanation=error.msg)

        authorize(context, "upload_image")

        image_metadata = {"container_format": params.get("container_format",

                                                         "bare"),

                          "disk_format": params.get("disk_format", "raw"),

                          "name": params["image_name"]}

        try:

            response = self.volume_api.copy_volume_to_image(context,

                                                            volume,

                                                            image_metadata,

                                                            force)

        except exception.InvalidVolume as error:

            raise webob.exc.HTTPBadRequest(explanation=error.msg)

        except ValueError as error:

            raise webob.exc.HTTPBadRequest(explanation=unicode(error))

        except messaging.RemoteError as error:

            msg = "%(err_type)s: %(err_msg)s" % {'err_type': error.exc_type,

                                                 'err_msg': error.value}

            raise webob.exc.HTTPBadRequest(explanation=msg)

        return {'os-volume_upload_image': response}

    @wsgi.action('os-extend')

**** CubicPower OpenStack Study ****

    def _extend(self, req, id, body):

        """Extend size of volume."""

        context = req.environ['cinder.context']

        try:

            volume = self.volume_api.get(context, id)

        except exception.VolumeNotFound as error:

            raise webob.exc.HTTPNotFound(explanation=error.msg)

        try:

            int(body['os-extend']['new_size'])

        except (KeyError, ValueError, TypeError):

            msg = _("New volume size must be specified as an integer.")

            raise webob.exc.HTTPBadRequest(explanation=msg)

        size = int(body['os-extend']['new_size'])

        self.volume_api.extend(context, volume, size)

        return webob.Response(status_int=202)

    @wsgi.action('os-update_readonly_flag')

**** CubicPower OpenStack Study ****

    def _volume_readonly_update(self, req, id, body):

        """Update volume readonly flag."""

        context = req.environ['cinder.context']

        try:

            volume = self.volume_api.get(context, id)

        except exception.VolumeNotFound as error:

            raise webob.exc.HTTPNotFound(explanation=error.msg)

        try:

            readonly_flag = body['os-update_readonly_flag']['readonly']

        except KeyError:

            msg = _("Must specify readonly in request.")

            raise webob.exc.HTTPBadRequest(explanation=msg)

        if isinstance(readonly_flag, basestring):

            try:

                readonly_flag = strutils.bool_from_string(readonly_flag,

                                                          strict=True)

            except ValueError:

                msg = _("Bad value for 'readonly'")

                raise webob.exc.HTTPBadRequest(explanation=msg)

        elif not isinstance(readonly_flag, bool):

            msg = _("'readonly' not string or bool")

            raise webob.exc.HTTPBadRequest(explanation=msg)

        self.volume_api.update_readonly_flag(context, volume, readonly_flag)

        return webob.Response(status_int=202)

    @wsgi.action('os-retype')

**** CubicPower OpenStack Study ****

    def _retype(self, req, id, body):

        """Change type of existing volume."""

        context = req.environ['cinder.context']

        volume = self.volume_api.get(context, id)

        try:

            new_type = body['os-retype']['new_type']

        except KeyError:

            msg = _("New volume type must be specified.")

            raise webob.exc.HTTPBadRequest(explanation=msg)

        policy = body['os-retype'].get('migration_policy')

        self.volume_api.retype(context, volume, new_type, policy)

        return webob.Response(status_int=202)

**** CubicPower OpenStack Study ****

class Volume_actions(extensions.ExtensionDescriptor):

"""Enable volume actions

"""

name = "VolumeActions"

alias = "os-volume-actions"

namespace = "http://docs.openstack.org/volume/ext/volume-actions/api/v1.1"

updated = "2012-05-31T00:00:00+00:00"

**** CubicPower OpenStack Study ****

    def get_controller_extensions(self):

        controller = VolumeActionsController()

        extension = extensions.ControllerExtension(self, 'volumes', controller)

        return [extension]