1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
3
# Copyright 2011 Red Hat, Inc.
5
# Licensed under the Apache License, Version 2.0 (the "License"); you may
6
# not use this file except in compliance with the License. You may obtain
7
# a copy of the License at
9
# http://www.apache.org/licenses/LICENSE-2.0
11
# Unless required by applicable law or agreed to in writing, software
12
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
# License for the specific language governing permissions and limitations
16
"""Support for mounting virtual image files"""
20
from nova.openstack.common import log as logging
21
from nova import utils
23
LOG = logging.getLogger(__name__)
27
"""Standard mounting operations, that can be overridden by subclasses.
29
The basic device operations provided are get, map and mount,
30
to be called in that order.
33
mode = device_id_string = None # to be overridden in subclasses
35
def __init__(self, image, mount_dir, partition=None, device=None):
39
self.partition = partition
40
self.mount_dir = mount_dir
46
self.linked = self.mapped = self.mounted = self.automapped = False
47
self.device = self.mapped_device = device
49
# Reset to mounted dir if possible
53
"""Reset device paths to allow unmounting."""
57
self.linked = self.mapped = self.mounted = True
60
if os.path.isabs(device) and os.path.exists(device):
61
if device.startswith('/dev/mapper/'):
62
device = os.path.basename(device)
63
device, self.partition = device.rsplit('p', 1)
64
self.device = os.path.join('/dev', device)
67
"""Make the image available as a block device in the file system."""
73
"""Release the block device from the file system namespace."""
77
"""Map partitions of the device to the file system namespace."""
78
assert(os.path.exists(self.device))
79
automapped_path = '/dev/%sp%s' % (os.path.basename(self.device),
82
if self.partition == -1:
83
self.error = _('partition search unsupported with %s') % self.mode
84
elif self.partition and not os.path.exists(automapped_path):
85
map_path = '/dev/mapper/%sp%s' % (os.path.basename(self.device),
87
assert(not os.path.exists(map_path))
89
# Note kpartx can output warnings to stderr and succeed
90
# Also it can output failures to stderr and "succeed"
91
# So we just go on the existence of the mapped device
92
_out, err = utils.trycmd('kpartx', '-a', self.device,
93
run_as_root=True, discard_warnings=True)
95
# Note kpartx does nothing when presented with a raw image,
96
# so given we only use it when we expect a partitioned image, fail
97
if not os.path.exists(map_path):
99
err = _('partition %s not found') % self.partition
100
self.error = _('Failed to map partitions: %s') % err
102
self.mapped_device = map_path
104
elif self.partition and os.path.exists(automapped_path):
105
# Note auto mapping can be enabled with the 'max_part' option
106
# to the nbd or loop kernel modules. Beware of possible races
107
# in the partition scanning for _loop_ devices though
108
# (details in bug 1024586), which are currently uncatered for.
109
self.mapped_device = automapped_path
111
self.automapped = True
113
self.mapped_device = self.device
119
"""Remove partitions of the device from the file system namespace."""
122
if self.partition and not self.automapped:
123
utils.execute('kpartx', '-d', self.device, run_as_root=True)
125
self.automapped = False
128
"""Mount the device into the file system."""
129
_out, err = utils.trycmd('mount', self.mapped_device, self.mount_dir,
132
self.error = _('Failed to mount filesystem: %s') % err
139
"""Unmount the device from the file system."""
142
utils.execute('umount', self.mapped_device, run_as_root=True)
146
"""Call the get, map and mnt operations."""
149
status = self.get_dev() and self.map_dev() and self.mnt_dev()
156
"""Call the unmnt, unmap and unget operations."""