¡@

Home 

OpenStack Study: block_device.py

OpenStack Index

**** CubicPower OpenStack Study ****

# All Rights Reserved.

#

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

import operator

from nova import block_device

from nova.objects import block_device as block_device_obj

from nova.openstack.common import excutils

from nova.openstack.common.gettextutils import _

from nova.openstack.common import jsonutils

from nova.openstack.common import log as logging

from nova.volume import encryptors

LOG = logging.getLogger(__name__)

**** CubicPower OpenStack Study ****

class _NotTransformable(Exception):

pass

**** CubicPower OpenStack Study ****

class _InvalidType(_NotTransformable):

pass

**** CubicPower OpenStack Study ****

class _NoLegacy(Exception):

pass

**** CubicPower OpenStack Study ****

    def wrapped(obj, context, *args, **kwargs):

        ret_val = method(obj, context, *args, **kwargs)

        obj.save(context)

        return ret_val

    return wrapped

**** CubicPower OpenStack Study ****

class DriverBlockDevice(dict):

"""A dict subclass that represents block devices used by the virt layer.

Uses block device objects internally to do the database access.

_fields and _legacy_fields class attributes present a set of fields that

are expected on a certain DriverBlockDevice type. We may have more legacy

versions in the future.

If an attribute access is attempted for a name that is found in the

_proxy_as_attr set, it will be proxied to the underlying object. This

allows us to access stuff that is not part of the data model that all

drivers understand.

The save() method allows us to update the database using the underlying

object. _update_on_save class attribute dictionary keeps the following

mapping:

{'object field name': 'driver dict field name (or None if same)'}

These fields will be updated on the internal object, from the values in the

dict, before the actual database update is done.

"""

_fields = set()

_legacy_fields = set()

_proxy_as_attr = set()

_update_on_save = {'disk_bus': None,

'device_name': None,

'device_type': None}

**** CubicPower OpenStack Study ****

    def __init__(self, bdm):

        # TODO(ndipanov): Remove this check when we have all the rpc methods

        # use objects for block devices.

        if isinstance(bdm, block_device_obj.BlockDeviceMapping):

            self.__dict__['_bdm_obj'] = bdm

        else:

            self.__dict__['_bdm_obj'] = block_device_obj.BlockDeviceMapping()

            self._bdm_obj.update(block_device.BlockDeviceDict(bdm))

            self._bdm_obj.obj_reset_changes()

        if self._bdm_obj.no_device:

            raise _NotTransformable()

        self.update(dict((field, None)

                    for field in self._fields))

        self._transform()

**** CubicPower OpenStack Study ****

    def __getattr__(self, name):

        if name in self._proxy_as_attr:

            return getattr(self._bdm_obj, name)

        else:

            raise AttributeError("Cannot access %s on DriverBlockDevice "

                                  "class" % name)

**** CubicPower OpenStack Study ****

    def __setattr__(self, name, value):

        if name in self._proxy_as_attr:

            return setattr(self._bdm_obj, name, value)

        else:

            raise AttributeError("Cannot access %s on DriverBlockDevice "

                                  "class" % name)

**** CubicPower OpenStack Study ****

    def _transform(self):

        """Transform bdm to the format that is passed to drivers."""

        raise NotImplementedError()

**** CubicPower OpenStack Study ****

    def legacy(self):

        """Basic legacy transformation.

        Basic method will just drop the fields that are not in

        _legacy_fields set. Override this in subclass if needed.

        """

        return dict((key, self.get(key)) for key in self._legacy_fields)

**** CubicPower OpenStack Study ****

    def attach(self, **kwargs):

        """Make the device available to be used by VMs.

        To be overriden in subclasses with the connecting logic for

        the type of device the subclass represents.

        """

        raise NotImplementedError()

**** CubicPower OpenStack Study ****

    def save(self, context):

        for attr_name, key_name in self._update_on_save.iteritems():

            setattr(self._bdm_obj, attr_name, self[key_name or attr_name])

        self._bdm_obj.save(context)

**** CubicPower OpenStack Study ****

class DriverSwapBlockDevice(DriverBlockDevice):

_fields = set(['device_name', 'swap_size', 'disk_bus'])

_legacy_fields = _fields - set(['disk_bus'])

_update_on_save = {'disk_bus': None,

'device_name': None}

**** CubicPower OpenStack Study ****

    def _transform(self):

        if not block_device.new_format_is_swap(self._bdm_obj):

            raise _InvalidType

        self.update({

            'device_name': self._bdm_obj.device_name,

            'swap_size': self._bdm_obj.volume_size or 0,

            'disk_bus': self._bdm_obj.disk_bus

        })

**** CubicPower OpenStack Study ****

class DriverEphemeralBlockDevice(DriverBlockDevice):

_new_only_fields = set(['disk_bus', 'device_type', 'guest_format'])

_fields = set(['device_name', 'size']) | _new_only_fields

_legacy_fields = (_fields - _new_only_fields |

set(['num', 'virtual_name']))

**** CubicPower OpenStack Study ****

    def _transform(self):

        if not block_device.new_format_is_ephemeral(self._bdm_obj):

            raise _InvalidType

        self.update({

            'device_name': self._bdm_obj.device_name,

            'size': self._bdm_obj.volume_size or 0,

            'disk_bus': self._bdm_obj.disk_bus,

            'device_type': self._bdm_obj.device_type,

            'guest_format': self._bdm_obj.guest_format

        })

**** CubicPower OpenStack Study ****

    def legacy(self, num=0):

        legacy_bdm = super(DriverEphemeralBlockDevice, self).legacy()

        legacy_bdm['num'] = num

        legacy_bdm['virtual_name'] = 'ephemeral' + str(num)

        return legacy_bdm

**** CubicPower OpenStack Study ****

class DriverVolumeBlockDevice(DriverBlockDevice):

_legacy_fields = set(['connection_info', 'mount_device',

'delete_on_termination'])

_new_fields = set(['guest_format', 'device_type',

'disk_bus', 'boot_index'])

_fields = _legacy_fields | _new_fields

_valid_source = 'volume'

_valid_destination = 'volume'

_proxy_as_attr = set(['volume_size', 'volume_id'])

_update_on_save = {'disk_bus': None,

'device_name': 'mount_device',

'device_type': None}

**** CubicPower OpenStack Study ****

    def _transform(self):

        if (not self._bdm_obj.source_type == self._valid_source

                or not self._bdm_obj.destination_type ==

                self._valid_destination):

            raise _InvalidType

        self.update(

            dict((k, v) for k, v in self._bdm_obj.iteritems()

                 if k in self._new_fields | set(['delete_on_termination']))

        )

        self['mount_device'] = self._bdm_obj.device_name

        try:

            self['connection_info'] = jsonutils.loads(

                self._bdm_obj.connection_info)

        except TypeError:

            self['connection_info'] = None

    @update_db

**** CubicPower OpenStack Study ****

    def attach(self, context, instance, volume_api, virt_driver,

               do_check_attach=True, do_driver_attach=False):

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

        if do_check_attach:

            volume_api.check_attach(context, volume, instance=instance)

        volume_id = volume['id']

        context = context.elevated()

        connector = virt_driver.get_volume_connector(instance)

        connection_info = volume_api.initialize_connection(context,

                                                           volume_id,

                                                           connector)

        if 'serial' not in connection_info:

            connection_info['serial'] = self.volume_id

        # If do_driver_attach is False, we will attach a volume to an instance

        # at boot time. So actual attach is done by instance creation code.

        if do_driver_attach:

            encryption = encryptors.get_encryption_metadata(

                context, volume_api, volume_id, connection_info)

            try:

                virt_driver.attach_volume(

                        context, connection_info, instance,

                        self['mount_device'], disk_bus=self['disk_bus'],

                        device_type=self['device_type'], encryption=encryption)

            except Exception:  # pylint: disable=W0702

                with excutils.save_and_reraise_exception():

                    LOG.exception(_("Driver failed to attach volume "

                                    "%(volume_id)s at %(mountpoint)s"),

                                  {'volume_id': volume_id,

                                   'mountpoint': self['mount_device']},

                                  context=context, instance=instance)

                    volume_api.terminate_connection(context, volume_id,

                                                    connector)

        self['connection_info'] = connection_info

        volume_api.attach(context, volume_id,

                          instance['uuid'], self['mount_device'])

    @update_db

**** CubicPower OpenStack Study ****

    def refresh_connection_info(self, context, instance,

                                volume_api, virt_driver):

        # NOTE (ndipanov): A no-op if there is no connection info already

        if not self['connection_info']:

            return

        connector = virt_driver.get_volume_connector(instance)

        connection_info = volume_api.initialize_connection(context,

                                                           self.volume_id,

                                                           connector)

        if 'serial' not in connection_info:

            connection_info['serial'] = self.volume_id

        self['connection_info'] = connection_info

**** CubicPower OpenStack Study ****

    def save(self, context):

        # NOTE(ndipanov): we might want to generalize this by adding it to the

        # _update_on_save and adding a transformation function.

        try:

            self._bdm_obj.connection_info = jsonutils.dumps(

                    self.get('connection_info'))

        except TypeError:

            pass

        super(DriverVolumeBlockDevice, self).save(context)

**** CubicPower OpenStack Study ****

class DriverSnapshotBlockDevice(DriverVolumeBlockDevice):

_valid_source = 'snapshot'

_proxy_as_attr = set(['volume_size', 'volume_id', 'snapshot_id'])

**** CubicPower OpenStack Study ****

    def attach(self, context, instance, volume_api,

               virt_driver, wait_func=None):

        if not self.volume_id:

            snapshot = volume_api.get_snapshot(context,

                                               self.snapshot_id)

            vol = volume_api.create(context, self.volume_size,

                                    '', '', snapshot)

            if wait_func:

                wait_func(context, vol['id'])

            self.volume_id = vol['id']

        # Call the volume attach now

        super(DriverSnapshotBlockDevice, self).attach(context, instance,

                                                      volume_api, virt_driver)

**** CubicPower OpenStack Study ****

class DriverImageBlockDevice(DriverVolumeBlockDevice):

_valid_source = 'image'

_proxy_as_attr = set(['volume_size', 'volume_id', 'image_id'])

**** CubicPower OpenStack Study ****

    def attach(self, context, instance, volume_api,

               virt_driver, wait_func=None):

        if not self.volume_id:

            vol = volume_api.create(context, self.volume_size,

                                    '', '', image_id=self.image_id)

            if wait_func:

                wait_func(context, vol['id'])

            self.volume_id = vol['id']

        super(DriverImageBlockDevice, self).attach(context, instance,

                                                   volume_api, virt_driver)

def _convert_block_devices(device_type, block_device_mapping):

**** CubicPower OpenStack Study ****

def _convert_block_devices(device_type, block_device_mapping):

**** CubicPower OpenStack Study ****

    def _is_transformable(bdm):

        try:

            device_type(bdm)

        except _NotTransformable:

            return False

        return True

    return [device_type(bdm)

            for bdm in block_device_mapping

            if _is_transformable(bdm)]

convert_swap = functools.partial(_convert_block_devices,

                                 DriverSwapBlockDevice)

convert_ephemerals = functools.partial(_convert_block_devices,

                                      DriverEphemeralBlockDevice)

convert_volumes = functools.partial(_convert_block_devices,

                                   DriverVolumeBlockDevice)

convert_snapshots = functools.partial(_convert_block_devices,

                                     DriverSnapshotBlockDevice)

convert_images = functools.partial(_convert_block_devices,

                                     DriverImageBlockDevice)

def attach_block_devices(block_device_mapping, *attach_args, **attach_kwargs):

**** CubicPower OpenStack Study ****

def attach_block_devices(block_device_mapping, *attach_args, **attach_kwargs):

**** CubicPower OpenStack Study ****

    def _log_and_attach(bdm):

        context = attach_args[0]

        instance = attach_args[1]

        LOG.audit(_('Booting with volume %(volume_id)s at %(mountpoint)s'),

                  {'volume_id': bdm.volume_id,

                   'mountpoint': bdm['mount_device']},

                  context=context, instance=instance)

        bdm.attach(*attach_args, **attach_kwargs)

    map(_log_and_attach, block_device_mapping)

    return block_device_mapping

def refresh_conn_infos(block_device_mapping, *refresh_args, **refresh_kwargs):

    map(operator.methodcaller('refresh_connection_info',

                              *refresh_args, **refresh_kwargs),

        block_device_mapping)

    return block_device_mapping

def legacy_block_devices(block_device_mapping):

**** CubicPower OpenStack Study ****

def refresh_conn_infos(block_device_mapping, *refresh_args, **refresh_kwargs):

    map(operator.methodcaller('refresh_connection_info',

                              *refresh_args, **refresh_kwargs),

        block_device_mapping)

    return block_device_mapping

**** CubicPower OpenStack Study ****

def legacy_block_devices(block_device_mapping):

**** CubicPower OpenStack Study ****

    def _has_legacy(bdm):

        try:

            bdm.legacy()

        except _NoLegacy:

            return False

        return True

    bdms = [bdm.legacy()

            for bdm in block_device_mapping

            if _has_legacy(bdm)]

    # Re-enumerate ephemeral devices

    if all(isinstance(bdm, DriverEphemeralBlockDevice)

           for bdm in block_device_mapping):

        for i, dev in enumerate(bdms):

            dev['virtual_name'] = dev['virtual_name'][:-1] + str(i)

            dev['num'] = i

    return bdms

def get_swap(transformed_list):

    """Get the swap device out of the list context.

    The block_device_info needs swap to be a single device,

    not a list - otherwise this is a no-op.

    """

    if not all(isinstance(device, DriverSwapBlockDevice) or

               'swap_size' in device

                for device in transformed_list):

        return transformed_list

    try:

        return transformed_list.pop()

    except IndexError:

        return None

_IMPLEMENTED_CLASSES = (DriverSwapBlockDevice, DriverEphemeralBlockDevice,

                        DriverVolumeBlockDevice, DriverSnapshotBlockDevice,

                        DriverImageBlockDevice)

def is_implemented(bdm):

    for cls in _IMPLEMENTED_CLASSES:

        try:

            cls(bdm)

            return True

        except _NotTransformable:

            pass

    return False

**** CubicPower OpenStack Study ****

def get_swap(transformed_list):

    """Get the swap device out of the list context.

    The block_device_info needs swap to be a single device,

    not a list - otherwise this is a no-op.

    """

    if not all(isinstance(device, DriverSwapBlockDevice) or

               'swap_size' in device

                for device in transformed_list):

        return transformed_list

    try:

        return transformed_list.pop()

    except IndexError:

        return None

_IMPLEMENTED_CLASSES = (DriverSwapBlockDevice, DriverEphemeralBlockDevice,

                        DriverVolumeBlockDevice, DriverSnapshotBlockDevice,

                        DriverImageBlockDevice)

**** CubicPower OpenStack Study ****

def is_implemented(bdm):

    for cls in _IMPLEMENTED_CLASSES:

        try:

            cls(bdm)

            return True

        except _NotTransformable:

            pass

    return False