summaryrefslogtreecommitdiff
path: root/nova/virt/disk/mount/api.py
diff options
context:
space:
mode:
authorDaniel P. Berrange <berrange@redhat.com>2012-11-13 10:46:37 +0000
committerDaniel P. Berrange <berrange@redhat.com>2012-11-14 17:11:53 +0000
commite85156e20b664ccf9e6d0ac7e1006a80555f8922 (patch)
tree7a21aaf47813e908d32a52a365b90c8c9c23189a /nova/virt/disk/mount/api.py
parent16266a4afbfdbed5e3c2334e03e2c9c43b133020 (diff)
Move all mount classes into a subdirectory
The disk API will be growing some new classes in future commits. To avoid ambiguity in module names, move all the mount classes into a subdirectory nova/virt/disk/mount/ blueprint: virt-disk-api-refactoring Change-Id: I03898b4060bd0c488713c9d9f19caebdcd39113c Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Notes
Notes (review): Verified+2: Jenkins Code-Review+2: Vish Ishaya <vishvananda@gmail.com> Code-Review+1: Scott Moser <smoser@ubuntu.com> Approved+1: Michael Still <michael.still@canonical.com> Code-Review+2: Michael Still <michael.still@canonical.com> Submitted-by: Jenkins Submitted-at: Fri, 16 Nov 2012 05:32:54 +0000 Reviewed-on: https://review.openstack.org/16173 Project: openstack/nova Branch: refs/heads/master
Diffstat (limited to 'nova/virt/disk/mount/api.py')
-rw-r--r--nova/virt/disk/mount/api.py162
1 files changed, 162 insertions, 0 deletions
diff --git a/nova/virt/disk/mount/api.py b/nova/virt/disk/mount/api.py
new file mode 100644
index 0000000..e683658
--- /dev/null
+++ b/nova/virt/disk/mount/api.py
@@ -0,0 +1,162 @@
1# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
3# Copyright 2011 Red Hat, Inc.
4#
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
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
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
15# under the License.
16"""Support for mounting virtual image files"""
17
18import os
19
20from nova.openstack.common import log as logging
21from nova import utils
22
23LOG = logging.getLogger(__name__)
24
25
26class Mount(object):
27 """Standard mounting operations, that can be overridden by subclasses.
28
29 The basic device operations provided are get, map and mount,
30 to be called in that order.
31 """
32
33 mode = device_id_string = None # to be overridden in subclasses
34
35 def __init__(self, image, mount_dir, partition=None, device=None):
36
37 # Input
38 self.image = image
39 self.partition = partition
40 self.mount_dir = mount_dir
41
42 # Output
43 self.error = ""
44
45 # Internal
46 self.linked = self.mapped = self.mounted = self.automapped = False
47 self.device = self.mapped_device = device
48
49 # Reset to mounted dir if possible
50 self.reset_dev()
51
52 def reset_dev(self):
53 """Reset device paths to allow unmounting."""
54 if not self.device:
55 return
56
57 self.linked = self.mapped = self.mounted = True
58
59 device = self.device
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)
65
66 def get_dev(self):
67 """Make the image available as a block device in the file system."""
68 self.device = None
69 self.linked = True
70 return True
71
72 def unget_dev(self):
73 """Release the block device from the file system namespace."""
74 self.linked = False
75
76 def map_dev(self):
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),
80 self.partition)
81
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),
86 self.partition)
87 assert(not os.path.exists(map_path))
88
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)
94
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):
98 if not err:
99 err = _('partition %s not found') % self.partition
100 self.error = _('Failed to map partitions: %s') % err
101 else:
102 self.mapped_device = map_path
103 self.mapped = True
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
110 self.mapped = True
111 self.automapped = True
112 else:
113 self.mapped_device = self.device
114 self.mapped = True
115
116 return self.mapped
117
118 def unmap_dev(self):
119 """Remove partitions of the device from the file system namespace."""
120 if not self.mapped:
121 return
122 if self.partition and not self.automapped:
123 utils.execute('kpartx', '-d', self.device, run_as_root=True)
124 self.mapped = False
125 self.automapped = False
126
127 def mnt_dev(self):
128 """Mount the device into the file system."""
129 _out, err = utils.trycmd('mount', self.mapped_device, self.mount_dir,
130 run_as_root=True)
131 if err:
132 self.error = _('Failed to mount filesystem: %s') % err
133 return False
134
135 self.mounted = True
136 return True
137
138 def unmnt_dev(self):
139 """Unmount the device from the file system."""
140 if not self.mounted:
141 return
142 utils.execute('umount', self.mapped_device, run_as_root=True)
143 self.mounted = False
144
145 def do_mount(self):
146 """Call the get, map and mnt operations."""
147 status = False
148 try:
149 status = self.get_dev() and self.map_dev() and self.mnt_dev()
150 finally:
151 if not status:
152 self.do_umount()
153 return status
154
155 def do_umount(self):
156 """Call the unmnt, unmap and unget operations."""
157 if self.mounted:
158 self.unmnt_dev()
159 if self.mapped:
160 self.unmap_dev()
161 if self.linked:
162 self.unget_dev()