~ubuntu-branches/ubuntu/quantal/nova/quantal-proposed

« back to all changes in this revision

Viewing changes to nova/virt/configdrive.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short
  • Date: 2012-08-16 14:04:11 UTC
  • mto: This revision was merged to the branch mainline in revision 84.
  • Revision ID: package-import@ubuntu.com-20120816140411-0mr4n241wmk30t9l
Tags: upstream-2012.2~f3
ImportĀ upstreamĀ versionĀ 2012.2~f3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
 
2
 
 
3
# Copyright 2012 Michael Still and Canonical Inc
 
4
# All Rights Reserved.
 
5
#
 
6
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 
7
#    not use this file except in compliance with the License. You may obtain
 
8
#    a copy of the License at
 
9
#
 
10
#         http://www.apache.org/licenses/LICENSE-2.0
 
11
#
 
12
#    Unless required by applicable law or agreed to in writing, software
 
13
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 
14
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 
15
#    License for the specific language governing permissions and limitations
 
16
#    under the License.
 
17
 
 
18
"""Config Drive v2 helper."""
 
19
 
 
20
import base64
 
21
import json
 
22
import os
 
23
import shutil
 
24
import tempfile
 
25
 
 
26
from nova.api.metadata import base as instance_metadata
 
27
from nova import exception
 
28
from nova import flags
 
29
from nova.openstack.common import cfg
 
30
from nova.openstack.common import log as logging
 
31
from nova import utils
 
32
from nova import version
 
33
from nova.virt.libvirt import utils as virtutils
 
34
 
 
35
LOG = logging.getLogger(__name__)
 
36
 
 
37
configdrive_opts = [
 
38
    cfg.StrOpt('config_drive_format',
 
39
               default='iso9660',
 
40
               help='Config drive format. One of iso9660 (default) or vfat'),
 
41
    cfg.StrOpt('config_drive_tempdir',
 
42
               default=tempfile.tempdir,
 
43
               help=('Where to put temporary files associated with '
 
44
                     'config drive creation')),
 
45
    ]
 
46
 
 
47
FLAGS = flags.FLAGS
 
48
FLAGS.register_opts(configdrive_opts)
 
49
 
 
50
 
 
51
class ConfigDriveBuilder(object):
 
52
    def __init__(self, instance_md=None):
 
53
        self.imagefile = None
 
54
 
 
55
        # TODO(mikal): I don't think I can use utils.tempdir here, because
 
56
        # I need to have the directory last longer than the scope of this
 
57
        # method call
 
58
        self.tempdir = tempfile.mkdtemp(dir=FLAGS.config_drive_tempdir,
 
59
                                        prefix='cd_gen_')
 
60
 
 
61
        if instance_md is not None:
 
62
            self.add_instance_metadata(instance_md)
 
63
 
 
64
    def _add_file(self, path, data):
 
65
        filepath = os.path.join(self.tempdir, path)
 
66
        dirname = os.path.dirname(filepath)
 
67
        virtutils.ensure_tree(dirname)
 
68
        with open(filepath, 'w') as f:
 
69
            f.write(data)
 
70
 
 
71
    def add_instance_metadata(self, instance_md):
 
72
        for (path, value) in instance_md.metadata_for_config_drive():
 
73
            self._add_file(path, value)
 
74
            LOG.debug(_('Added %(filepath)s to config drive'),
 
75
                      {'filepath': path})
 
76
 
 
77
    def _make_iso9660(self, path):
 
78
        utils.execute('genisoimage',
 
79
                      '-o', path,
 
80
                      '-ldots',
 
81
                      '-allow-lowercase',
 
82
                      '-allow-multidot',
 
83
                      '-l',
 
84
                      '-publisher', ('"OpenStack nova %s"'
 
85
                                     % version.version_string()),
 
86
                      '-quiet',
 
87
                      '-J',
 
88
                      '-r',
 
89
                      '-V', 'config-2',
 
90
                      self.tempdir,
 
91
                      attempts=1,
 
92
                      run_as_root=False)
 
93
 
 
94
    def _make_vfat(self, path):
 
95
        # NOTE(mikal): This is a little horrible, but I couldn't find an
 
96
        # equivalent to genisoimage for vfat filesystems. vfat images are
 
97
        # always 64mb.
 
98
        with open(path, 'w') as f:
 
99
            f.truncate(64 * 1024 * 1024)
 
100
 
 
101
        virtutils.mkfs('vfat', path, label='config-2')
 
102
 
 
103
        mounted = False
 
104
        try:
 
105
            mountdir = tempfile.mkdtemp(dir=FLAGS.config_drive_tempdir,
 
106
                                        prefix='cd_mnt_')
 
107
            _out, err = utils.trycmd('mount', '-o', 'loop', path, mountdir,
 
108
                                     run_as_root=True)
 
109
            if err:
 
110
                raise exception.ConfigDriveMountFailed(operation='mount',
 
111
                                                       error=err)
 
112
            mounted = True
 
113
 
 
114
            _out, err = utils.trycmd('chown',
 
115
                                     '%s.%s' % (os.getuid(), os.getgid()),
 
116
                                     mountdir, run_as_root=True)
 
117
            if err:
 
118
                raise exception.ConfigDriveMountFailed(operation='chown',
 
119
                                                       error=err)
 
120
 
 
121
            # NOTE(mikal): I can't just use shutils.copytree here, because the
 
122
            # destination directory already exists. This is annoying.
 
123
            for ent in os.listdir(self.tempdir):
 
124
                shutil.copytree(os.path.join(self.tempdir, ent),
 
125
                                os.path.join(mountdir, ent))
 
126
 
 
127
        finally:
 
128
            if mounted:
 
129
                utils.execute('umount', mountdir, run_as_root=True)
 
130
            shutil.rmtree(mountdir)
 
131
 
 
132
    def make_drive(self, path):
 
133
        if FLAGS.config_drive_format == 'iso9660':
 
134
            self._make_iso9660(path)
 
135
        elif FLAGS.config_drive_format == 'vfat':
 
136
            self._make_vfat(path)
 
137
        else:
 
138
            raise exception.ConfigDriveUnknownFormat(
 
139
                format=FLAGS.config_drive_format)
 
140
 
 
141
    def cleanup(self):
 
142
        if self.imagefile:
 
143
            utils.delete_if_exists(self.imagefile)
 
144
 
 
145
        try:
 
146
            shutil.rmtree(self.tempdir)
 
147
        except OSError, e:
 
148
            LOG.error(_('Could not remove tmpdir: %s'), str(e))