~ubuntu-branches/ubuntu/raring/nova/raring-proposed

« back to all changes in this revision

Viewing changes to nova/virt/disk/vfs/guestfs.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short
  • Date: 2013-01-11 13:06:56 UTC
  • mto: This revision was merged to the branch mainline in revision 96.
  • Revision ID: package-import@ubuntu.com-20130111130656-z9mceux6qpkqomma
Tags: upstream-2013.1~g2
ImportĀ upstreamĀ versionĀ 2013.1~g2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
 
2
 
 
3
# Copyright 2012 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
 
 
17
import guestfs
 
18
 
 
19
from nova import exception
 
20
from nova.openstack.common import log as logging
 
21
from nova.virt.disk.vfs import api as vfs
 
22
 
 
23
LOG = logging.getLogger(__name__)
 
24
 
 
25
guestfs = None
 
26
 
 
27
 
 
28
class VFSGuestFS(vfs.VFS):
 
29
 
 
30
    """
 
31
    This class implements a VFS module that uses the libguestfs APIs
 
32
    to access the disk image. The disk image is never mapped into
 
33
    the host filesystem, thus avoiding any potential for symlink
 
34
    attacks from the guest filesystem.
 
35
    """
 
36
    def __init__(self, imgfile, imgfmt='raw', partition=None):
 
37
        super(VFSGuestFS, self).__init__(imgfile, imgfmt, partition)
 
38
 
 
39
        global guestfs
 
40
        if guestfs is None:
 
41
            guestfs = __import__('guestfs')
 
42
 
 
43
        self.handle = None
 
44
 
 
45
    def setup_os(self):
 
46
        if self.partition == -1:
 
47
            self.setup_os_inspect()
 
48
        else:
 
49
            self.setup_os_static()
 
50
 
 
51
    def setup_os_static(self):
 
52
        LOG.debug(_("Mount guest OS image %(imgfile)s partition %(part)s"),
 
53
                  {'imgfile': self.imgfile, 'part': str(self.partition)})
 
54
 
 
55
        if self.partition:
 
56
            self.handle.mount_options("", "/dev/sda%d" % self.partition, "/")
 
57
        else:
 
58
            self.handle.mount_options("", "/dev/sda", "/")
 
59
 
 
60
    def setup_os_inspect(self):
 
61
        LOG.debug(_("Inspecting guest OS image %s"), self.imgfile)
 
62
        roots = self.handle.inspect_os()
 
63
 
 
64
        if len(roots) == 0:
 
65
            raise exception.NovaException(_("No operating system found in %s"),
 
66
                                          self.imgfile)
 
67
 
 
68
        if len(roots) != 1:
 
69
            LOG.debug(_("Multi-boot OS %(roots)s") % {'roots': str(roots)})
 
70
            raise exception.NovaException(
 
71
                _("Multi-boot operating system found in %s"),
 
72
                self.imgfile)
 
73
 
 
74
        self.setup_os_root(roots[0])
 
75
 
 
76
    def setup_os_root(self, root):
 
77
        LOG.debug(_("Inspecting guest OS root filesystem %s"), root)
 
78
        mounts = self.handle.inspect_get_mountpoints(root)
 
79
 
 
80
        if len(mounts) == 0:
 
81
            raise exception.NovaException(
 
82
                _("No mount points found in %(root)s of %(imgfile)s") %
 
83
                {'root': root, 'imgfile': self.imgfile})
 
84
 
 
85
        mounts.sort(key=lambda mount: mount[1])
 
86
        for mount in mounts:
 
87
            LOG.debug(_("Mounting %(dev)s at %(dir)s") %
 
88
                      {'dev': mount[1], 'dir': mount[0]})
 
89
            self.handle.mount_options("", mount[1], mount[0])
 
90
 
 
91
    def setup(self):
 
92
        LOG.debug(_("Setting up appliance for %(imgfile)s %(imgfmt)s") %
 
93
                  {'imgfile': self.imgfile, 'imgfmt': self.imgfmt})
 
94
        self.handle = guestfs.GuestFS()
 
95
 
 
96
        try:
 
97
            self.handle.add_drive_opts(self.imgfile, format=self.imgfmt)
 
98
            self.handle.launch()
 
99
 
 
100
            self.setup_os()
 
101
 
 
102
            self.handle.aug_init("/", 0)
 
103
        except RuntimeError, e:
 
104
            # dereference object and implicitly close()
 
105
            self.handle = None
 
106
            raise exception.NovaException(
 
107
                _("Error mounting %(imgfile)s with libguestfs (%(e)s)") %
 
108
                {'imgfile': self.imgfile, 'e': e})
 
109
        except Exception:
 
110
            self.handle = None
 
111
            raise
 
112
 
 
113
    def teardown(self):
 
114
        LOG.debug(_("Tearing down appliance"))
 
115
 
 
116
        try:
 
117
            try:
 
118
                self.handle.aug_close()
 
119
            except RuntimeError, e:
 
120
                LOG.warn(_("Failed to close augeas %s"), e)
 
121
 
 
122
            try:
 
123
                self.handle.shutdown()
 
124
            except AttributeError:
 
125
                # Older libguestfs versions haven't an explicit shutdown
 
126
                pass
 
127
            except RuntimeError, e:
 
128
                LOG.warn(_("Failed to shutdown appliance %s"), e)
 
129
 
 
130
            try:
 
131
                self.handle.close()
 
132
            except AttributeError:
 
133
                # Older libguestfs versions haven't an explicit close
 
134
                pass
 
135
            except RuntimeError, e:
 
136
                LOG.warn(_("Failed to close guest handle %s"), e)
 
137
        finally:
 
138
            # dereference object and implicitly close()
 
139
            self.handle = None
 
140
 
 
141
    @staticmethod
 
142
    def _canonicalize_path(path):
 
143
        if path[0] != '/':
 
144
            return '/' + path
 
145
        return path
 
146
 
 
147
    def make_path(self, path):
 
148
        LOG.debug(_("Make directory path=%(path)s") % locals())
 
149
        path = self._canonicalize_path(path)
 
150
        self.handle.mkdir_p(path)
 
151
 
 
152
    def append_file(self, path, content):
 
153
        LOG.debug(_("Append file path=%(path)s") % locals())
 
154
        path = self._canonicalize_path(path)
 
155
        self.handle.write_append(path, content)
 
156
 
 
157
    def replace_file(self, path, content):
 
158
        LOG.debug(_("Replace file path=%(path)s") % locals())
 
159
        path = self._canonicalize_path(path)
 
160
        self.handle.write(path, content)
 
161
 
 
162
    def read_file(self, path):
 
163
        LOG.debug(_("Read file path=%(path)s") % locals())
 
164
        path = self._canonicalize_path(path)
 
165
        return self.handle.read_file(path)
 
166
 
 
167
    def has_file(self, path):
 
168
        LOG.debug(_("Has file path=%(path)s") % locals())
 
169
        path = self._canonicalize_path(path)
 
170
        try:
 
171
            self.handle.stat(path)
 
172
            return True
 
173
        except RuntimeError:
 
174
            return False
 
175
 
 
176
    def set_permissions(self, path, mode):
 
177
        LOG.debug(_("Set permissions path=%(path)s mode=%(mode)s") % locals())
 
178
        path = self._canonicalize_path(path)
 
179
        self.handle.chmod(mode, path)
 
180
 
 
181
    def set_ownership(self, path, user, group):
 
182
        LOG.debug(_("Set ownership path=%(path)s "
 
183
                    "user=%(user)s group=%(group)s") % locals())
 
184
        path = self._canonicalize_path(path)
 
185
        uid = -1
 
186
        gid = -1
 
187
 
 
188
        if user is not None:
 
189
            uid = int(self.handle.aug_get(
 
190
                    "/files/etc/passwd/" + user + "/uid"))
 
191
        if group is not None:
 
192
            gid = int(self.handle.aug_get(
 
193
                    "/files/etc/group/" + group + "/gid"))
 
194
 
 
195
        LOG.debug(_("chown uid=%(uid)d gid=%(gid)s") % locals())
 
196
        self.handle.chown(uid, gid, path)