~ubuntu-branches/ubuntu/precise/vm-builder/precise

« back to all changes in this revision

Viewing changes to VMBuilder/contrib/cli.py

  • Committer: Bazaar Package Importer
  • Author(s): Soren Hansen
  • Date: 2010-02-22 13:56:18 UTC
  • mfrom: (1.1.8 upstream)
  • Revision ID: james.westby@ubuntu.com-20100222135618-la13e3mu397rg0m1
Tags: 0.12.0-0ubuntu1
* New upstream release. (FFe: LP: #525741)
  - All patches incorporated upstream.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#    Uncomplicated VM Builder
 
2
#    Copyright (C) 2007-2009 Canonical Ltd.
 
3
#    
 
4
#    See AUTHORS for list of contributors
 
5
#
 
6
#    This program is free software: you can redistribute it and/or modify
 
7
#    it under the terms of the GNU General Public License version 3, as
 
8
#    published by the Free Software Foundation.
 
9
#
 
10
#    This program is distributed in the hope that it will be useful,
 
11
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
#    GNU General Public License for more details.
 
14
#
 
15
#    You should have received a copy of the GNU General Public License
 
16
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
17
#
 
18
#    CLI plugin
 
19
import logging
 
20
import optparse
 
21
import os
 
22
import pwd
 
23
import sys
 
24
import VMBuilder
 
25
import VMBuilder.util as util
 
26
from VMBuilder.disk import parse_size
 
27
import VMBuilder.hypervisor
 
28
 
 
29
class CLI(object):
 
30
    arg = 'cli'
 
31
       
 
32
    def main(self):
 
33
        optparser = optparse.OptionParser()
 
34
 
 
35
        self.set_usage(optparser)
 
36
 
 
37
        optparser.add_option('--version', action='callback', callback=self.versioninfo, help='Show version information')
 
38
 
 
39
        group = optparse.OptionGroup(optparser, 'Build options')
 
40
        group.add_option('--config', '-c', type='str', help='Configuration file')
 
41
        group.add_option('--destdir', '-d', type='str', help='Destination directory')
 
42
        group.add_option('--only-chroot', action='store_true', help="Only build the chroot. Don't install it on disk images or anything.")
 
43
        group.add_option('--existing-chroot', help="Use existing chroot.")
 
44
        optparser.add_option_group(group)
 
45
 
 
46
        group = optparse.OptionGroup(optparser, 'Disk')
 
47
        group.add_option('--rootsize', metavar='SIZE', default=4096, help='Size (in MB) of the root filesystem [default: %default]')
 
48
        group.add_option('--optsize', metavar='SIZE', default=0, help='Size (in MB) of the /opt filesystem. If not set, no /opt filesystem will be added.')
 
49
        group.add_option('--swapsize', metavar='SIZE', default=1024, help='Size (in MB) of the swap partition [default: %default]')
 
50
        group.add_option('--raw', metavar='PATH', type='str', help="Specify a file (or block device) to as first disk image.")
 
51
        group.add_option('--part', metavar='PATH', type='str', help="Allows to specify a partition table in PATH each line of partfile should specify (root first): \n    mountpoint size \none per line, separated by space, where size is in megabytes. You can have up to 4 virtual disks, a new disk starts on a line containing only '---'. ie: \n    root 2000 \n    /boot 512 \n    swap 1000 \n    --- \n    /var 8000 \n    /var/log 2000")
 
52
        optparser.add_option_group(group)
 
53
        
 
54
        distro_name = sys.argv[2]
 
55
        distro_class = VMBuilder.get_distro(distro_name)
 
56
        distro = distro_class()
 
57
        self.add_settings_from_context(optparser, distro)
 
58
 
 
59
        hypervisor_name = sys.argv[1]
 
60
        hypervisor_class = VMBuilder.get_hypervisor(hypervisor_name)
 
61
        hypervisor = hypervisor_class(distro)
 
62
        hypervisor.register_hook('fix_ownership', self.fix_ownership)
 
63
        self.add_settings_from_context(optparser, hypervisor)
 
64
 
 
65
        (self.options, args) = optparser.parse_args(sys.argv[2:])
 
66
        for option in dir(self.options):
 
67
            if option.startswith('_') or option in ['ensure_value', 'read_module', 'read_file']:
 
68
                continue
 
69
            val = getattr(self.options, option)
 
70
            if val:
 
71
                if distro.has_setting(option):
 
72
                    distro.set_setting(option, val)
 
73
                elif hypervisor.has_setting(option):
 
74
                    hypervisor.set_setting(option, val)
 
75
        
 
76
        if self.options.existing_chroot:
 
77
            distro.set_chroot_dir(self.options.existing_chroot)
 
78
            distro.call_hooks('preflight_check')
 
79
        else:
 
80
            chroot_dir = util.tmpdir()
 
81
            distro.set_chroot_dir(chroot_dir)
 
82
            distro.build_chroot()
 
83
 
 
84
        if self.options.only_chroot:
 
85
            print 'Chroot can be found in %s' % distro.chroot_dir
 
86
            sys.exit(0)
 
87
 
 
88
        self.set_disk_layout(hypervisor)
 
89
        hypervisor.install_os()
 
90
 
 
91
        destdir = self.options.destdir or ('%s-%s' % (distro_name, hypervisor_name))
 
92
        os.mkdir(destdir)
 
93
        self.fix_ownership(destdir)
 
94
        hypervisor.finalise(destdir)
 
95
        sys.exit(0)
 
96
 
 
97
    def fix_ownership(self, filename):
 
98
        """
 
99
        Change ownership of file to $SUDO_USER.
 
100
 
 
101
        @type  path: string
 
102
        @param path: file or directory to give to $SUDO_USER
 
103
        """
 
104
        if 'SUDO_USER' in os.environ:
 
105
            logging.debug('Changing ownership of %s to %s' % (filename, os.environ['SUDO_USER']))
 
106
            (uid, gid) = pwd.getpwnam(os.environ['SUDO_USER'])[2:4]
 
107
            os.chown(filename, uid, gid)
 
108
 
 
109
    def add_settings_from_context(self, optparser, context):
 
110
        setting_groups = set([setting.setting_group for setting in context._config.values()])
 
111
        for setting_group in setting_groups:
 
112
            optgroup = optparse.OptionGroup(optparser, setting_group.name)
 
113
            for setting in setting_group._settings:
 
114
                args = ['--%s' % setting.name]
 
115
                args += setting.extra_args
 
116
                kwargs = {}
 
117
                if setting.help:
 
118
                    kwargs['help'] = setting.help
 
119
                    if len(setting.extra_args) > 0:
 
120
                        setting.help += " Config option: %s" % setting.name
 
121
                if setting.metavar:
 
122
                    kwargs['metavar'] = setting.metavar
 
123
                if setting.get_default():
 
124
                    kwargs['default'] = setting.get_default()
 
125
                if type(setting) == VMBuilder.plugins.Plugin.BooleanSetting:
 
126
                    kwargs['action'] = 'store_true'
 
127
                if type(setting) == VMBuilder.plugins.Plugin.ListSetting:
 
128
                    kwargs['action'] = 'append'
 
129
                optgroup.add_option(*args, **kwargs)
 
130
            optparser.add_option_group(optgroup)
 
131
 
 
132
    def versioninfo(self, option, opt, value, parser):
 
133
        print '%(major)d.%(minor)d.%(micro)s.r%(revno)d' % VMBuilder.get_version_info()
 
134
        sys.exit(0)
 
135
 
 
136
    def set_usage(self, optparser):
 
137
        optparser.set_usage('%prog hypervisor distro [options]')
 
138
#        optparser.arg_help = (('hypervisor', vm.hypervisor_help), ('distro', vm.distro_help))
 
139
 
 
140
    def handle_args(self, vm, args):
 
141
        if len(args) < 2:
 
142
            vm.optparser.error("You need to specify at least the hypervisor type and the distro")
 
143
        self.hypervisor = vm.get_hypervisor(args[0])
 
144
        self.distro = distro.vm.get_distro(args[1])
 
145
 
 
146
    def set_disk_layout(self, hypervisor):
 
147
        default_filesystem = hypervisor.distro.preferred_filesystem()
 
148
        if not self.options.part:
 
149
            rootsize = parse_size(self.options.rootsize)
 
150
            swapsize = parse_size(self.options.swapsize)
 
151
            optsize = parse_size(self.options.optsize)
 
152
            if hypervisor.preferred_storage == VMBuilder.hypervisor.STORAGE_FS_IMAGE:
 
153
                hypervisor.add_filesystem(size='%dM' % rootsize, type='ext3', mntpnt='/')
 
154
                hypervisor.add_filesystem(size='%dM' % swapsize, type='swap', mntpnt=None)
 
155
                if optsize > 0:
 
156
                    hypervisor.add_filesystem(size='%dM' % optsize, type='ext3', mntpnt='/opt')
 
157
            else:
 
158
                if self.options.raw:
 
159
                    disk = hypervisor.add_disk(filename=self.options.raw, preallocated=True)
 
160
                else:
 
161
                    size = rootsize + swapsize + optsize
 
162
                    tmpfile = util.tmpfile(keep=False)
 
163
                    disk = hypervisor.add_disk(tmpfile, size='%dM' % size)
 
164
                offset = 0
 
165
                disk.add_part(offset, rootsize, default_filesystem, '/')
 
166
                offset += rootsize
 
167
                disk.add_part(offset, swapsize, 'swap', 'swap')
 
168
                offset += swapsize
 
169
                if optsize > 0:
 
170
                    disk.add_part(offset, optsize, default_filesystem, '/opt')
 
171
        else:
 
172
            # We need to parse the file specified
 
173
            if vm.hypervisor.preferred_storage == VMBuilder.hypervisor.STORAGE_FS_IMAGE:
 
174
                try:
 
175
                    for line in file(self.options.part):
 
176
                        elements = line.strip().split(' ')
 
177
                        if elements[0] == 'root':
 
178
                            vm.add_filesystem(elements[1], default_filesystem, mntpnt='/')
 
179
                        elif elements[0] == 'swap':
 
180
                            vm.add_filesystem(elements[1], type='swap', mntpnt=None)
 
181
                        elif elements[0] == '---':
 
182
                            # We just ignore the user's attempt to specify multiple disks
 
183
                            pass
 
184
                        elif len(elements) == 3:
 
185
                            vm.add_filesystem(elements[1], type=default_filesystem, mntpnt=elements[0], devletter='', device=elements[2], dummy=(int(elements[1]) == 0))
 
186
                        else:
 
187
                            vm.add_filesystem(elements[1], type=default_filesystem, mntpnt=elements[0])
 
188
 
 
189
                except IOError, (errno, strerror):
 
190
                    vm.optparser.error("%s parsing --part option: %s" % (errno, strerror))
 
191
            else:
 
192
                try:
 
193
                    curdisk = list()
 
194
                    size = 0
 
195
                    for line in file(part):
 
196
                        pair = line.strip().split(' ',1) 
 
197
                        if pair[0] == '---':
 
198
                            self.do_disk(vm, curdisk, size)
 
199
                            curdisk = list()
 
200
                            size = 0
 
201
                        elif pair[0] != '':
 
202
                            logging.debug("part: %s, size: %d" % (pair[0], int(pair[1])))
 
203
                            curdisk.append((pair[0], pair[1]))
 
204
                            size += int(pair[1])
 
205
 
 
206
                    self.do_disk(vm, curdisk, size)
 
207
 
 
208
                except IOError, (errno, strerror):
 
209
                    vm.optparser.error("%s parsing --part option: %s" % (errno, strerror))
 
210
    
 
211
    def do_disk(self, hypervisor, curdisk, size):
 
212
        default_filesystem = hypervisor.distro.preferred_filesystem()
 
213
        disk = hypervisor.add_disk(size+1)
 
214
        logging.debug("do_disk - size: %d" % size)
 
215
        offset = 0
 
216
        for pair in curdisk:
 
217
            logging.debug("do_disk - part: %s, size: %s, offset: %d" % (pair[0], pair[1], offset))
 
218
            if pair[0] == 'root':
 
219
                disk.add_part(offset, int(pair[1]), default_filesystem, '/')
 
220
            elif pair[0] == 'swap':
 
221
                disk.add_part(offset, int(pair[1]), pair[0], pair[0])
 
222
            else:
 
223
                disk.add_part(offset, int(pair[1]), default_filesystem, pair[0])
 
224
            offset += int(pair[1])
 
225
 
 
226
class UVB(CLI):
 
227
    arg = 'ubuntu-vm-builder'
 
228
 
 
229
    def set_usage(self, vm):
 
230
        optparser.set_usage('%prog hypervisor suite [options]')
 
231
        optparser.arg_help = (('hypervisor', vm.hypervisor_help), ('suite', self.suite_help))
 
232
 
 
233
    def suite_help(self):
 
234
        return 'Suite. Valid options: %s' % " ".join(VMBuilder.plugins.ubuntu.distro.Ubuntu.suites)
 
235
 
 
236
    def handle_args(self, vm, args):
 
237
        if len(args) < 2:
 
238
            vm.optparser.error("You need to specify at least the hypervisor type and the suite")
 
239
        vm.set_hypervisor(args[0])
 
240
        vm.set_distro('ubuntu')
 
241
        vm.suite = args[1]