¡@

Home 

OpenStack Study: api.py

OpenStack Index

**** CubicPower OpenStack Study ****

# Copyright 2011 Red Hat, 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.

"""Support for mounting virtual image files."""

import os

import time

from nova.openstack.common.gettextutils import _

from nova.openstack.common import importutils

from nova.openstack.common import log as logging

from nova import utils

LOG = logging.getLogger(__name__)

MAX_DEVICE_WAIT = 30

**** CubicPower OpenStack Study ****

class Mount(object):

"""Standard mounting operations, that can be overridden by subclasses.

The basic device operations provided are get, map and mount,

to be called in that order.

"""

mode = None # to be overridden in subclasses

@staticmethod

**** CubicPower OpenStack Study ****

    def instance_for_format(imgfile, mountdir, partition, imgfmt):

        LOG.debug(_("Instance for format imgfile=%(imgfile)s "

                    "mountdir=%(mountdir)s partition=%(partition)s "

                    "imgfmt=%(imgfmt)s"),

                  {'imgfile': imgfile, 'mountdir': mountdir,

                   'partition': partition, 'imgfmt': imgfmt})

        if imgfmt == "raw":

            LOG.debug(_("Using LoopMount"))

            return importutils.import_object(

                "nova.virt.disk.mount.loop.LoopMount",

                imgfile, mountdir, partition)

        else:

            LOG.debug(_("Using NbdMount"))

            return importutils.import_object(

                "nova.virt.disk.mount.nbd.NbdMount",

                imgfile, mountdir, partition)

    @staticmethod

**** CubicPower OpenStack Study ****

    def instance_for_device(imgfile, mountdir, partition, device):

        LOG.debug(_("Instance for device imgfile=%(imgfile)s "

                    "mountdir=%(mountdir)s partition=%(partition)s "

                    "device=%(device)s"),

                  {'imgfile': imgfile, 'mountdir': mountdir,

                   'partition': partition, 'device': device})

        if "loop" in device:

            LOG.debug(_("Using LoopMount"))

            return importutils.import_object(

                "nova.virt.disk.mount.loop.LoopMount",

                imgfile, mountdir, partition, device)

        else:

            LOG.debug(_("Using NbdMount"))

            return importutils.import_object(

                "nova.virt.disk.mount.nbd.NbdMount",

                imgfile, mountdir, partition, device)

**** CubicPower OpenStack Study ****

    def __init__(self, image, mount_dir, partition=None, device=None):

        # Input

        self.image = image

        self.partition = partition

        self.mount_dir = mount_dir

        # Output

        self.error = ""

        # Internal

        self.linked = self.mapped = self.mounted = self.automapped = False

        self.device = self.mapped_device = device

        # Reset to mounted dir if possible

        self.reset_dev()

**** CubicPower OpenStack Study ****

    def reset_dev(self):

        """Reset device paths to allow unmounting."""

        if not self.device:

            return

        self.linked = self.mapped = self.mounted = True

        device = self.device

        if os.path.isabs(device) and os.path.exists(device):

            if device.startswith('/dev/mapper/'):

                device = os.path.basename(device)

                device, self.partition = device.rsplit('p', 1)

                self.device = os.path.join('/dev', device)

**** CubicPower OpenStack Study ****

    def get_dev(self):

        """Make the image available as a block device in the file system."""

        self.device = None

        self.linked = True

        return True

**** CubicPower OpenStack Study ****

    def _get_dev_retry_helper(self):

        """Some implementations need to retry their get_dev."""

        # NOTE(mikal): This method helps implement retries. The implementation

        # simply calls _get_dev_retry_helper from their get_dev, and implements

        # _inner_get_dev with their device acquisition logic. The NBD

        # implementation has an example.

        start_time = time.time()

        device = self._inner_get_dev()

        while not device:

            LOG.info(_('Device allocation failed. Will retry in 2 seconds.'))

            time.sleep(2)

            if time.time() - start_time > MAX_DEVICE_WAIT:

                LOG.warn(_('Device allocation failed after repeated retries.'))

                return False

            device = self._inner_get_dev()

        return True

**** CubicPower OpenStack Study ****

    def _inner_get_dev(self):

        raise NotImplementedError()

**** CubicPower OpenStack Study ****

    def unget_dev(self):

        """Release the block device from the file system namespace."""

        self.linked = False

**** CubicPower OpenStack Study ****

    def map_dev(self):

        """Map partitions of the device to the file system namespace."""

        assert(os.path.exists(self.device))

        LOG.debug(_("Map dev %s"), self.device)

        automapped_path = '/dev/%sp%s' % (os.path.basename(self.device),

                                              self.partition)

        if self.partition == -1:

            self.error = _('partition search unsupported with %s') % self.mode

        elif self.partition and not os.path.exists(automapped_path):

            map_path = '/dev/mapper/%sp%s' % (os.path.basename(self.device),

                                              self.partition)

            assert(not os.path.exists(map_path))

            # Note kpartx can output warnings to stderr and succeed

            # Also it can output failures to stderr and "succeed"

            # So we just go on the existence of the mapped device

            _out, err = utils.trycmd('kpartx', '-a', self.device,

                                     run_as_root=True, discard_warnings=True)

            # Note kpartx does nothing when presented with a raw image,

            # so given we only use it when we expect a partitioned image, fail

            if not os.path.exists(map_path):

                if not err:

                    err = _('partition %s not found') % self.partition

                self.error = _('Failed to map partitions: %s') % err

            else:

                self.mapped_device = map_path

                self.mapped = True

        elif self.partition and os.path.exists(automapped_path):

            # Note auto mapping can be enabled with the 'max_part' option

            # to the nbd or loop kernel modules. Beware of possible races

            # in the partition scanning for _loop_ devices though

            # (details in bug 1024586), which are currently uncatered for.

            self.mapped_device = automapped_path

            self.mapped = True

            self.automapped = True

        else:

            self.mapped_device = self.device

            self.mapped = True

        return self.mapped

**** CubicPower OpenStack Study ****

    def unmap_dev(self):

        """Remove partitions of the device from the file system namespace."""

        if not self.mapped:

            return

        LOG.debug(_("Unmap dev %s"), self.device)

        if self.partition and not self.automapped:

            utils.execute('kpartx', '-d', self.device, run_as_root=True)

        self.mapped = False

        self.automapped = False

**** CubicPower OpenStack Study ****

    def mnt_dev(self):

        """Mount the device into the file system."""

        LOG.debug(_("Mount %(dev)s on %(dir)s") %

                  {'dev': self.mapped_device, 'dir': self.mount_dir})

        _out, err = utils.trycmd('mount', self.mapped_device, self.mount_dir,

                                 discard_warnings=True, run_as_root=True)

        if err:

            self.error = _('Failed to mount filesystem: %s') % err

            LOG.debug(self.error)

            return False

        self.mounted = True

        return True

**** CubicPower OpenStack Study ****

    def unmnt_dev(self):

        """Unmount the device from the file system."""

        if not self.mounted:

            return

        self.flush_dev()

        LOG.debug(_("Umount %s") % self.mapped_device)

        utils.execute('umount', self.mapped_device, run_as_root=True)

        self.mounted = False

**** CubicPower OpenStack Study ****

    def flush_dev(self):

        pass

**** CubicPower OpenStack Study ****

    def do_mount(self):

        """Call the get, map and mnt operations."""

        status = False

        try:

            status = self.get_dev() and self.map_dev() and self.mnt_dev()

        finally:

            if not status:

                LOG.debug(_("Fail to mount, tearing back down"))

                self.do_teardown()

        return status

**** CubicPower OpenStack Study ****

    def do_umount(self):

        """Call the unmnt operation."""

        if self.mounted:

            self.unmnt_dev()

**** CubicPower OpenStack Study ****

    def do_teardown(self):

        """Call the umnt, unmap, and unget operations."""

        if self.mounted:

            self.unmnt_dev()

        if self.mapped:

            self.unmap_dev()

        if self.linked:

            self.unget_dev()