Revision 121f3bc0

b/image_creator/__init__.py
1
# -*- coding: utf-8 -*-
2
#
1 3
# Copyright 2012 GRNET S.A. All rights reserved.
2 4
#
3 5
# Redistribution and use in source and binary forms, with or
......
31 33
# interpreted as representing official policies, either expressed
32 34
# or implied, of GRNET S.A.
33 35

  
36
"""Package for creating images to be used with Synnefo open source cloud
37
software.
38
"""
39

  
34 40
from image_creator.version import __version__
35 41

  
36 42
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
b/image_creator/bundle_volume.py
1
# -*- coding: utf-8 -*-
2
#
1 3
# Copyright 2012 GRNET S.A. All rights reserved.
2 4
#
3 5
# Redistribution and use in source and binary forms, with or
......
31 33
# interpreted as representing official policies, either expressed
32 34
# or implied, of GRNET S.A.
33 35

  
36
"""This module hosts the code that performes the host bundling operation. By
37
using the create_image method of the BundleVolume class the user can create an
38
image out of the running system.
39
"""
40

  
34 41
import os
35 42
import re
36 43
import tempfile
b/image_creator/dialog_main.py
1 1
#!/usr/bin/env python
2

  
2
# -*- coding: utf-8 -*-
3
#
3 4
# Copyright 2012 GRNET S.A. All rights reserved.
4 5
#
5 6
# Redistribution and use in source and binary forms, with or
......
33 34
# interpreted as representing official policies, either expressed
34 35
# or implied, of GRNET S.A.
35 36

  
37
"""This module is the entrance point for the dialog-based version of the
38
snf-image-creator program. The main function will create a dialog where the
39
user is asked if he wants to use the program in expert or wizard mode.
40
"""
41

  
36 42
import dialog
37 43
import sys
38 44
import os
b/image_creator/dialog_menu.py
1
#!/usr/bin/env python
2

  
1
# -*- coding: utf-8 -*-
2
#
3 3
# Copyright 2012 GRNET S.A. All rights reserved.
4 4
#
5 5
# Redistribution and use in source and binary forms, with or
......
33 33
# interpreted as representing official policies, either expressed
34 34
# or implied, of GRNET S.A.
35 35

  
36
"""This module implements the "expert" mode of the dialog-based version of
37
snf-image-creator.
38
"""
39

  
36 40
import os
37 41
import textwrap
38 42
import StringIO
......
313 317
            if code in (d.DIALOG_CANCEL, d.DIALOG_ESC):
314 318
                continue
315 319
            if len(answer) == 0 and "account" in session:
316
                    del session["account"]
320
                del session["account"]
317 321
            else:
318 322
                token = answer.strip()
319 323
                session['account'] = Kamaki.get_account(token)
b/image_creator/dialog_util.py
1
#!/usr/bin/env python
2

  
1
# -*- coding: utf-8 -*-
2
#
3 3
# Copyright 2012 GRNET S.A. All rights reserved.
4 4
#
5 5
# Redistribution and use in source and binary forms, with or
......
33 33
# interpreted as representing official policies, either expressed
34 34
# or implied, of GRNET S.A.
35 35

  
36
"""Module providing useful functions for the dialog-based version of
37
snf-image-creator.
38
"""
39

  
36 40
import os
37 41
from image_creator.output.dialog import GaugeOutput
38 42
from image_creator.util import MD5
b/image_creator/dialog_wizard.py
1
#!/usr/bin/env python
2

  
1
# -*- coding: utf-8 -*-
2
#
3 3
# Copyright 2012 GRNET S.A. All rights reserved.
4 4
#
5 5
# Redistribution and use in source and binary forms, with or
......
33 33
# interpreted as representing official policies, either expressed
34 34
# or implied, of GRNET S.A.
35 35

  
36
"""This module implements the "wizard" mode of the dialog-based version of
37
snf-image-creator.
38
"""
39

  
36 40
import time
37 41
import StringIO
38 42

  
b/image_creator/disk.py
1
# -*- coding: utf-8 -*-
2
#
1 3
# Copyright 2012 GRNET S.A. All rights reserved.
2 4
#
3 5
# Redistribution and use in source and binary forms, with or
......
31 33
# interpreted as representing official policies, either expressed
32 34
# or implied, of GRNET S.A.
33 35

  
36
"""Module hosting the Disk class."""
37

  
34 38
from image_creator.util import get_command
35 39
from image_creator.util import try_fail_repeat
36 40
from image_creator.util import free_space
......
50 54
blockdev = get_command('blockdev')
51 55

  
52 56

  
53
TMP_CANDIDATES = ['/var/tmp', os.path.expanduser('~'), '/mnt']
57
def get_tmp_dir(default=None):
58
    """Check tmp directory candidates and return the one with the most
59
    available space.
60
    """
61
    if default is not None:
62
        return default
63

  
64
    TMP_CANDIDATES = ['/var/tmp', os.path.expanduser('~'), '/mnt']
65

  
66
    space = map(free_space, TMP_CANDIDATES)
67

  
68
    max_idx = 0
69
    max_val = space[0]
70
    for i, val in zip(range(len(space)), space):
71
        if val > max_val:
72
            max_val = val
73
            max_idx = i
74

  
75
    # Return the candidate path with more available space
76
    return TMP_CANDIDATES[max_idx]
54 77

  
55 78

  
56 79
class Disk(object):
......
71 94
        self.out = output
72 95
        self.meta = {}
73 96
        self.tmp = tempfile.mkdtemp(prefix='.snf_image_creator.',
74
                                    dir=self._get_tmp_dir(tmp))
97
                                    dir=get_tmp_dir(tmp))
75 98

  
76 99
        self._add_cleanup(shutil.rmtree, self.tmp)
77 100

  
78
    def _get_tmp_dir(self, default=None):
79
        """Check tmp directory candidates and return the one with the most
80
        available space.
81
        """
82
        if default is not None:
83
            return default
84

  
85
        space = map(free_space, TMP_CANDIDATES)
86

  
87
        max_idx = 0
88
        max_val = space[0]
89
        for i, val in zip(range(len(space)), space):
90
            if val > max_val:
91
                max_val = val
92
                max_idx = i
93

  
94
        # Return the candidate path with more available space
95
        return TMP_CANDIDATES[max_idx]
96

  
97 101
    def _add_cleanup(self, job, *args):
98 102
        """Add a new job in the cleanup list"""
99 103
        self._cleanup_jobs.append((job, args))
b/image_creator/gpt.py
1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3
#
1 4
# Copyright 2012 GRNET S.A. All rights reserved.
2 5
#
3 6
# Redistribution and use in source and binary forms, with or
......
31 34
# interpreted as representing official policies, either expressed
32 35
# or implied, of GRNET S.A.
33 36

  
37
"""This module provides the code for handling GUID partition tables"""
38

  
34 39
import struct
35 40
import sys
36 41
import uuid
b/image_creator/help/__init__.py
1
# -*- coding: utf-8 -*-
2
#
1 3
# Copyright 2012 GRNET S.A. All rights reserved.
2 4
#
3 5
# Redistribution and use in source and binary forms, with or
......
31 33
# interpreted as representing official policies, either expressed
32 34
# or implied, of GRNET S.A.
33 35

  
36
"""This package hosts the help files of the programe."""
37

  
34 38
import sys
35 39
import os
36 40

  
37 41

  
38 42
def get_help_file(name):
43
    """Returns the full path of a helpfile"""
39 44
    dirname = os.path.dirname(sys.modules[__name__].__file__)
40 45
    return "%s%s%s.rst" % (dirname, os.sep, name)
41 46

  
b/image_creator/image.py
1
# -*- coding: utf-8 -*-
2
#
1 3
# Copyright 2013 GRNET S.A. All rights reserved.
2 4
#
3 5
# Redistribution and use in source and binary forms, with or
b/image_creator/kamaki_wrapper.py
1
# -*- coding: utf-8 -*-
2
#
1 3
# Copyright 2012 GRNET S.A. All rights reserved.
2 4
#
3 5
# Redistribution and use in source and binary forms, with or
......
31 33
# interpreted as representing official policies, either expressed
32 34
# or implied, of GRNET S.A.
33 35

  
36
"""This modules provides the interface for working with the ./kamaki library.
37
The library is used to upload images to and register them with a Synnefo
38
deployment.
39
"""
40

  
34 41
from os.path import basename
35 42

  
36 43
from kamaki.cli.config import Config
......
41 48

  
42 49

  
43 50
class Kamaki(object):
44

  
51
    """Wrapper class for the ./kamaki library"""
45 52
    CONTAINER = "images"
46 53

  
47 54
    @staticmethod
b/image_creator/main.py
1 1
#!/usr/bin/env python
2

  
2
# -*- coding: utf-8 -*-
3
#
3 4
# Copyright 2012 GRNET S.A. All rights reserved.
4 5
#
5 6
# Redistribution and use in source and binary forms, with or
......
33 34
# interpreted as representing official policies, either expressed
34 35
# or implied, of GRNET S.A.
35 36

  
37
"""This module is the entrance point for the non-interactive version of the
38
snf-image-creator program.
39
"""
40

  
36 41
from image_creator import __version__ as version
37 42
from image_creator.disk import Disk
38 43
from image_creator.util import FatalError, MD5
b/image_creator/os_type/__init__.py
1
# -*- coding: utf-8 -*-
2
#
1 3
# Copyright 2012 GRNET S.A. All rights reserved.
2 4
#
3 5
# Redistribution and use in source and binary forms, with or
......
31 33
# interpreted as representing official policies, either expressed
32 34
# or implied, of GRNET S.A.
33 35

  
36
"""This package provides various classes for preparing different Operating
37
Systems for image creation.
38
"""
39

  
34 40
from image_creator.util import FatalError
35 41

  
36 42
import textwrap
......
81 87

  
82 88
    def collect_metadata(self):
83 89
        """Collect metadata about the OS"""
84

  
85 90
        try:
86 91
            if not self.mount(readonly=True):
87 92
                raise FatalError("Unable to mount the media read-only")
......
92 97
        finally:
93 98
            self.umount()
94 99

  
95
    def _do_collect_metadata(self):
96

  
97
        self.meta['ROOT_PARTITION'] = "%d" % self.g.part_to_partnum(self.root)
98
        self.meta['OSFAMILY'] = self.g.inspect_get_type(self.root)
99
        self.meta['OS'] = self.g.inspect_get_distro(self.root)
100
        if self.meta['OS'] == "unknown":
101
            self.meta['OS'] = self.meta['OSFAMILY']
102
        self.meta['DESCRIPTION'] = self.g.inspect_get_product_name(self.root)
103

  
104
    def _is_sysprep(self, obj):
105
        return getattr(obj, 'sysprep', False) and callable(obj)
100
        self.out.output()
106 101

  
107 102
    def list_syspreps(self):
108

  
103
        """Returns a list of sysprep objects"""
109 104
        objs = [getattr(self, name) for name in dir(self)
110 105
                if not name.startswith('_')]
111 106

  
112 107
        return [x for x in objs if self._is_sysprep(x) and x.executed is False]
113 108

  
114 109
    def sysprep_info(self, obj):
110
        """Returns information about a sysprep object"""
115 111
        assert self._is_sysprep(obj), "Object is not a sysprep"
116 112

  
117 113
        return (obj.__name__.replace('_', '-'), textwrap.dedent(obj.__doc__))
......
171 167
                descr = wrapper.fill(textwrap.dedent(sysprep.__doc__))
172 168
                self.out.output('    %s:\n%s\n' % (name, descr))
173 169

  
170
    def do_sysprep(self):
171
        """Prepare system for image creation."""
172

  
173
        try:
174
            if not self.mount(readonly=False):
175
                raise FatalError("Unable to mount the media read-write")
176

  
177
            self.out.output('Preparing system for image creation:')
178

  
179
            tasks = self.list_syspreps()
180
            enabled = filter(lambda x: x.enabled, tasks)
181

  
182
            size = len(enabled)
183
            cnt = 0
184
            for task in enabled:
185
                cnt += 1
186
                self.out.output(('(%d/%d)' % (cnt, size)).ljust(7), False)
187
                task()
188
                setattr(task.im_func, 'executed', True)
189
        finally:
190
            self.umount()
191

  
192
        self.out.output()
193

  
194
    def mount(self, readonly=False):
195
        """Mount image."""
196

  
197
        if getattr(self, "mounted", False):
198
            return True
199

  
200
        mount_type = 'read-only' if readonly else 'read-write'
201
        self.out.output("Mounting the media %s ..." % mount_type, False)
202

  
203
        if not self._do_mount(readonly):
204
            return False
205

  
206
        self.mounted = True
207
        self.out.success('done')
208
        return True
209

  
210
    def umount(self):
211
        """Umount all mounted filesystems."""
212

  
213
        self.out.output("Umounting the media ...", False)
214
        self.g.umount_all()
215
        self.mounted = False
216
        self.out.success('done')
217

  
218
    def _is_sysprep(self, obj):
219
        """Checks if an object is a sysprep"""
220
        return getattr(obj, 'sysprep', False) and callable(obj)
221

  
174 222
    @add_prefix
175
    def ls(self, directory):
223
    def _ls(self, directory):
176 224
        """List the name of all files under a directory"""
177 225
        return self.g.ls(directory)
178 226

  
179 227
    @add_prefix
180
    def find(self, directory):
228
    def _find(self, directory):
181 229
        """List the name of all files recursively under a directory"""
182 230
        return self.g.find(directory)
183 231

  
184
    def foreach_file(self, directory, action, **kargs):
232
    def _foreach_file(self, directory, action, **kargs):
185 233
        """Perform an action recursively on all files under a directory.
186 234

  
187 235
        The following options are allowed:
......
218 266
                continue
219 267

  
220 268
            if has_ftype(f, 'd'):
221
                self.foreach_file(full_path, action, **kargs)
269
                self._foreach_file(full_path, action, **kargs)
222 270

  
223 271
            if has_ftype(f, ftype):
224 272
                action(full_path)
225 273

  
226
    def do_sysprep(self):
227
        """Prepere system for image creation."""
228

  
229
        try:
230
            if not self.mount(readonly=False):
231
                raise FatalError("Unable to mount the media read-write")
232

  
233
            self.out.output('Preparing system for image creation:')
234

  
235
            tasks = self.list_syspreps()
236
            enabled = filter(lambda x: x.enabled, tasks)
237

  
238
            size = len(enabled)
239
            cnt = 0
240
            for task in enabled:
241
                cnt += 1
242
                self.out.output(('(%d/%d)' % (cnt, size)).ljust(7), False)
243
                task()
244
                setattr(task.im_func, 'executed', True)
245
            self.out.output()
246
        finally:
247
            self.umount()
274
    def _do_collect_metadata(self):
275
        """helper method for collect_metadata"""
276
        self.meta['ROOT_PARTITION'] = "%d" % self.g.part_to_partnum(self.root)
277
        self.meta['OSFAMILY'] = self.g.inspect_get_type(self.root)
278
        self.meta['OS'] = self.g.inspect_get_distro(self.root)
279
        if self.meta['OS'] == "unknown":
280
            self.meta['OS'] = self.meta['OSFAMILY']
281
        self.meta['DESCRIPTION'] = self.g.inspect_get_product_name(self.root)
248 282

  
249 283
    def _do_mount(self, readonly):
284
        """helper method for mount"""
250 285
        try:
251 286
            self.g.mount_options('ro' if readonly else 'rw', self.root, '/')
252 287
        except RuntimeError as msg:
......
255 290

  
256 291
        return True
257 292

  
258
    def mount(self, readonly=False):
259
        """Mount image."""
260

  
261
        if getattr(self, "mounted", False):
262
            return True
263

  
264
        mount_type = 'read-only' if readonly else 'read-write'
265
        self.out.output("Mount the media %s ..." % mount_type, False)
266

  
267
        if not self._do_mount(readonly):
268
            return False
269

  
270
        self.mounted = True
271
        self.out.success('done')
272
        return True
273

  
274
    def umount(self):
275
        """Umount all mounted filesystems."""
276
        self.g.umount_all()
277
        self.mounted = False
278

  
279 293
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
b/image_creator/os_type/freebsd.py
1
# -*- coding: utf-8 -*-
2
#
1 3
# Copyright 2012 GRNET S.A. All rights reserved.
2 4
#
3 5
# Redistribution and use in source and binary forms, with or
......
31 33
# interpreted as representing official policies, either expressed
32 34
# or implied, of GRNET S.A.
33 35

  
36
"""This module hosts OS-specific code for FreeBSD."""
37

  
34 38
from image_creator.os_type.unix import Unix, sysprep
35 39

  
36 40
import re
......
41 45
    def __init__(self, rootdev, ghandler, output):
42 46
        super(Freebsd, self).__init__(rootdev, ghandler, output)
43 47

  
44
    def _do_collect_metadata(self):
48
    @sysprep()
49
    def cleanup_password(self, print_header=True):
50
        """Remove all passwords and lock all user accounts"""
51

  
52
        if print_header:
53
            self.out.output("Cleaning up passwords & locking all user "
54
                            "accounts")
55

  
56
        master_passwd = []
57

  
58
        for line in self.g.cat('/etc/master.passwd').splitlines():
59

  
60
            # Check for empty or comment lines
61
            if len(line.split('#')[0]) == 0:
62
                master_passwd.append(line)
63
                continue
64

  
65
            fields = line.split(':')
66
            if fields[1] not in ('*', '!'):
67
                fields[1] = '!'
68

  
69
            master_passwd.append(":".join(fields))
70

  
71
        self.g.write('/etc/master.passwd', "\n".join(master_passwd) + '\n')
72

  
73
        # Make sure no one can login on the system
74
        self.g.rm_rf('/etc/spwd.db')
45 75

  
76
    def _do_collect_metadata(self):
77
        """Collect metadata about the OS"""
46 78
        super(Freebsd, self)._do_collect_metadata()
47 79
        self.meta["USERS"] = " ".join(self._get_passworded_users())
48 80

  
......
56 88
            del self.meta['USERS']
57 89

  
58 90
    def _get_passworded_users(self):
91
        """Returns a list of non-locked user accounts"""
59 92
        users = []
60 93
        regexp = re.compile(
61 94
            '^([^:]+):((?:![^:]+)|(?:[^!*][^:]+)|):(?:[^:]*:){7}(?:[^:]*)'
......
80 113

  
81 114
        critical_mpoints = ('/', '/etc', '/root', '/home', '/var')
82 115

  
83
        # libguestfs can't handle correct freebsd partitions on GUID
84
        # Partition Table. We have to do the translation to linux
85
        # device names ourselves
116
        # libguestfs can't handle correct freebsd partitions on a GUID
117
        # Partition Table. We have to do the translation to linux device names
118
        # ourselves
86 119
        guid_device = re.compile('^/dev/((?:ada)|(?:vtbd))(\d+)p(\d+)$')
87 120

  
88 121
        mopts = "ufstype=ufs2,%s" % ('ro' if readonly else 'rw')
89 122
        for mp, dev in self._mountpoints():
90 123
            match = guid_device.match(dev)
91 124
            if match:
92
                m2 = int(match.group(2))
93
                m3 = int(match.group(3))
94
                dev = '/dev/sd%c%d' % (chr(ord('a') + m2), m3)
125
                group2 = int(match.group(2))
126
                group3 = int(match.group(3))
127
                dev = '/dev/sd%c%d' % (chr(ord('a') + group2), group3)
95 128
            try:
96 129
                self.g.mount_vfs(mopts, 'ufs', dev, mp)
97 130
            except RuntimeError as msg:
......
103 136

  
104 137
        return True
105 138

  
106
    @sysprep()
107
    def cleanup_password(self, print_header=True):
108
        """Remove all passwords and lock all user accounts"""
109

  
110
        if print_header:
111
            self.out.output("Cleaning up passwords & locking all user "
112
                            "accounts")
113

  
114
        master_passwd = []
115

  
116
        for line in self.g.cat('/etc/master.passwd').splitlines():
117

  
118
            # Check for empty or comment lines
119
            if len(line.split('#')[0]) == 0:
120
                master_passwd.append(line)
121
                continue
122

  
123
            fields = line.split(':')
124
            if fields[1] not in ('*', '!'):
125
                fields[1] = '!'
126

  
127
            master_passwd.append(":".join(fields))
128

  
129
        self.g.write('/etc/master.passwd', "\n".join(master_passwd) + '\n')
130

  
131
        # Make sure no one can login on the system
132
        self.g.rm_rf('/etc/spwd.db')
133

  
134 139
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
b/image_creator/os_type/hurd.py
1
# -*- coding: utf-8 -*-
2
#
1 3
# Copyright 2012 GRNET S.A. All rights reserved.
2 4
#
3 5
# Redistribution and use in source and binary forms, with or
......
31 33
# interpreted as representing official policies, either expressed
32 34
# or implied, of GRNET S.A.
33 35

  
36
"""This module hosts OS-specific code for GNU Hurd."""
37

  
34 38
from image_creator.os_type.unix import Unix
35 39

  
36 40

  
b/image_creator/os_type/linux.py
1
# -*- coding: utf-8 -*-
2
#
1 3
# Copyright 2012 GRNET S.A. All rights reserved.
2 4
#
3 5
# Redistribution and use in source and binary forms, with or
......
31 33
# interpreted as representing official policies, either expressed
32 34
# or implied, of GRNET S.A.
33 35

  
36
"""This module hosts OS-specific code for Linux"""
37

  
34 38
from image_creator.os_type.unix import Unix, sysprep
35 39

  
36 40
import re
......
44 48
        self._uuid = dict()
45 49
        self._persistent = re.compile('/dev/[hsv]d[a-z][1-9]*')
46 50

  
47
    def _do_collect_metadata(self):
48
        """Collect metadata about the OS"""
49

  
50
        super(Linux, self)._do_collect_metadata()
51
        self.meta["USERS"] = " ".join(self._get_passworded_users())
52

  
53
        # Delete the USERS metadata if empty
54
        if not len(self.meta['USERS']):
55
            self.out.warn("No passworded users found!")
56
            del self.meta['USERS']
57

  
58
    def _get_passworded_users(self):
59
        users = []
60
        regexp = re.compile('(\S+):((?:!\S+)|(?:[^!*]\S+)|):(?:\S*:){6}')
61

  
62
        for line in self.g.cat('/etc/shadow').splitlines():
63
            match = regexp.match(line)
64
            if not match:
65
                continue
66

  
67
            user, passwd = match.groups()
68
            if len(passwd) > 0 and passwd[0] == '!':
69
                self.out.warn("Ignoring locked %s account." % user)
70
            else:
71
                users.append(user)
72

  
73
        return users
74

  
75
    def is_persistent(self, dev):
76
        return not self._persistent.match(dev)
77

  
78
    def get_uuid(self, dev):
79
        if dev in self._uuid:
80
            return self._uuid[dev]
81

  
82
        uuid = self.g.vfs_uuid(dev)
83
        assert len(uuid)
84
        self._uuid[dev] = uuid
85
        return uuid
86

  
87 51
    @sysprep(enabled=False)
88 52
    def remove_user_accounts(self, print_header=True):
89 53
        """Remove all user accounts with id greater than 1000"""
......
179 143

  
180 144
        event_exp = re.compile('event=(.+)', re.I)
181 145
        action_exp = re.compile('action=(.+)', re.I)
182
        for f in self.g.readdir(events_dir):
183
            if f['ftyp'] != 'r':
146
        for events_file in self.g.readdir(events_dir):
147
            if events_file['ftyp'] != 'r':
184 148
                continue
185 149

  
186
            fullpath = "%s/%s" % (events_dir, f['name'])
150
            fullpath = "%s/%s" % (events_dir, events_file['name'])
187 151
            event = ""
188 152
            action = ""
189 153
            for line in self.g.cat(fullpath).splitlines():
190
                m = event_exp.match(line)
191
                if m:
192
                    event = m.group(1)
154
                match = event_exp.match(line)
155
                if match:
156
                    event = match.group(1)
193 157
                    continue
194
                m = action_exp.match(line)
195
                if m:
196
                    action = m.group(1)
158
                match = action_exp.match(line)
159
                if match:
160
                    action = match.group(1)
197 161
                    continue
198 162

  
199 163
            if event.strip() in ("button[ /]power", "button/power.*"):
......
275 239
        self._persistent_grub1(persistent_root)
276 240

  
277 241
    def _persistent_grub1(self, new_root):
242
        """Replaces non-persistent device name occurencies with persistent
243
        ones in GRUB1 configuration files.
244
        """
278 245
        if self.g.is_file('/boot/grub/menu.lst'):
279 246
            grub1 = '/boot/grub/menu.lst'
280 247
        elif self.g.is_file('/etc/grub.conf'):
......
287 254
            roots = self.g.aug_match('/files%s/title[*]/kernel/root' % grub1)
288 255
            for root in roots:
289 256
                dev = self.g.aug_get(root)
290
                if not self.is_persistent(dev):
257
                if not self._is_persistent(dev):
291 258
                    # This is not always correct. Grub may contain root entries
292 259
                    # for other systems, but we only support 1 OS per hard
293 260
                    # disk, so this shouldn't harm.
......
297 264
            self.g.aug_close()
298 265

  
299 266
    def _persistent_fstab(self):
267
        """Replaces non-persistent device name occurencies in /etc/fstab with
268
        persistent ones.
269
        """
300 270
        mpoints = self.g.mountpoints()
301 271
        if len(mpoints) == 0:
302 272
            pass  # TODO: error handling
......
321 291
        return root_dev
322 292

  
323 293
    def _convert_fstab_line(self, line, devices):
294
        """Replace non-persistent device names in an fstab line to their UUID
295
        equivalent
296
        """
324 297
        orig = line
325 298
        line = line.split('#')[0].strip()
326 299
        if len(line) == 0:
......
334 307
        dev = entry[0]
335 308
        mpoint = entry[1]
336 309

  
337
        if not self.is_persistent(dev):
310
        if not self._is_persistent(dev):
338 311
            if mpoint in devices:
339
                dev = "UUID=%s" % self.get_uuid(devices[mpoint])
312
                dev = "UUID=%s" % self._get_uuid(devices[mpoint])
340 313
                entry[0] = dev
341 314
            else:
342 315
                # comment out the entry
......
345 318

  
346 319
        return orig, dev, mpoint
347 320

  
321
    def _do_collect_metadata(self):
322
        """Collect metadata about the OS"""
323
        super(Linux, self)._do_collect_metadata()
324
        self.meta["USERS"] = " ".join(self._get_passworded_users())
325

  
326
        # Delete the USERS metadata if empty
327
        if not len(self.meta['USERS']):
328
            self.out.warn("No passworded users found!")
329
            del self.meta['USERS']
330

  
331
    def _get_passworded_users(self):
332
        """Returns a list of non-locked user accounts"""
333
        users = []
334
        regexp = re.compile('(\S+):((?:!\S+)|(?:[^!*]\S+)|):(?:\S*:){6}')
335

  
336
        for line in self.g.cat('/etc/shadow').splitlines():
337
            match = regexp.match(line)
338
            if not match:
339
                continue
340

  
341
            user, passwd = match.groups()
342
            if len(passwd) > 0 and passwd[0] == '!':
343
                self.out.warn("Ignoring locked %s account." % user)
344
            else:
345
                users.append(user)
346

  
347
        return users
348

  
349
    def _is_persistent(self, dev):
350
        """Checks if a device name is persistent."""
351
        return not self._persistent.match(dev)
352

  
353
    def _get_uuid(self, dev):
354
        """Returns the UUID corresponding to a device"""
355
        if dev in self._uuid:
356
            return self._uuid[dev]
357

  
358
        uuid = self.g.vfs_uuid(dev)
359
        assert len(uuid)
360
        self._uuid[dev] = uuid
361
        return uuid
362

  
348 363
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
b/image_creator/os_type/netbsd.py
1
# -*- coding: utf-8 -*-
2
#
1 3
# Copyright 2012 GRNET S.A. All rights reserved.
2 4
#
3 5
# Redistribution and use in source and binary forms, with or
......
31 33
# interpreted as representing official policies, either expressed
32 34
# or implied, of GRNET S.A.
33 35

  
36
"""This module hosts OS-specific code for NetBSD."""
37

  
34 38
from image_creator.os_type.unix import Unix
35 39

  
36 40

  
b/image_creator/os_type/slackware.py
1
# -*- coding: utf-8 -*-
2
#
1 3
# Copyright 2012 GRNET S.A. All rights reserved.
2 4
#
3 5
# Redistribution and use in source and binary forms, with or
......
31 33
# interpreted as representing official policies, either expressed
32 34
# or implied, of GRNET S.A.
33 35

  
36
"""This module hosts OS-specific code for Slackware Linux"""
37

  
34 38
from image_creator.os_type.linux import Linux, sysprep
35 39

  
36 40

  
......
46 50
        # In slackware the metadata about installed packages are
47 51
        # stored in /var/log/packages. Clearing all /var/log files
48 52
        # will destroy the package management system.
49
        self.foreach_file('/var/log', self.g.truncate, ftype='r',
50
                          exclude='/var/log/packages')
53
        self._foreach_file('/var/log', self.g.truncate, ftype='r',
54
                           exclude='/var/log/packages')
51 55

  
52 56
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
b/image_creator/os_type/ubuntu.py
1
# -*- coding: utf-8 -*-
2
#
1 3
# Copyright 2012 GRNET S.A. All rights reserved.
2 4
#
3 5
# Redistribution and use in source and binary forms, with or
......
31 33
# interpreted as representing official policies, either expressed
32 34
# or implied, of GRNET S.A.
33 35

  
36
"""This module hosts OS-specific code for Ubuntu Linux"""
37

  
34 38
from image_creator.os_type.linux import Linux
35 39

  
36 40

  
b/image_creator/os_type/unix.py
1
# -*- coding: utf-8 -*-
2
#
1 3
# Copyright 2012 GRNET S.A. All rights reserved.
2 4
#
3 5
# Redistribution and use in source and binary forms, with or
......
31 33
# interpreted as representing official policies, either expressed
32 34
# or implied, of GRNET S.A.
33 35

  
36
"""This module hosts OS-specific code common to all Unix-like OSs."""
37

  
34 38
import re
35 39

  
36 40
from image_creator.os_type import OSBase, sysprep
......
75 79
            try:
76 80
                self.g.mount_options(mopts, dev, mp)
77 81
            except RuntimeError as msg:
78
                if mp in critical_mpoint:
82
                if mp in critical_mpoints:
79 83
                    self.out.warn('unable to mount %s. Reason: %s' % (mp, msg))
80 84
                    return False
81 85
                else:
......
90 94
        if print_header:
91 95
            self.out.output('Removing files under /var/cache')
92 96

  
93
        self.foreach_file('/var/cache', self.g.rm, ftype='r')
97
        self._foreach_file('/var/cache', self.g.rm, ftype='r')
94 98

  
95 99
    @sysprep()
96 100
    def cleanup_tmp(self, print_header=True):
......
99 103
        if print_header:
100 104
            self.out.output('Removing files under /tmp and /var/tmp')
101 105

  
102
        self.foreach_file('/tmp', self.g.rm_rf, maxdepth=1)
103
        self.foreach_file('/var/tmp', self.g.rm_rf, maxdepth=1)
106
        self._foreach_file('/tmp', self.g.rm_rf, maxdepth=1)
107
        self._foreach_file('/var/tmp', self.g.rm_rf, maxdepth=1)
104 108

  
105 109
    @sysprep()
106 110
    def cleanup_log(self, print_header=True):
......
109 113
        if print_header:
110 114
            self.out.output('Emptying all files under /var/log')
111 115

  
112
        self.foreach_file('/var/log', self.g.truncate, ftype='r')
116
        self._foreach_file('/var/log', self.g.truncate, ftype='r')
113 117

  
114 118
    @sysprep(enabled=False)
115 119
    def cleanup_mail(self, print_header=True):
......
119 123
            self.out.output('Removing files under /var/mail & /var/spool/mail')
120 124

  
121 125
        if self.g.is_dir('/var/spool/mail'):
122
            self.foreach_file('/var/spool/mail', self.g.rm_rf, maxdepth=1)
126
            self._foreach_file('/var/spool/mail', self.g.rm_rf, maxdepth=1)
123 127

  
124
        self.foreach_file('/var/mail', self.g.rm_rf, maxdepth=1)
128
        self._foreach_file('/var/mail', self.g.rm_rf, maxdepth=1)
125 129

  
126 130
    @sysprep()
127 131
    def cleanup_userdata(self, print_header=True):
......
129 133

  
130 134
        homedirs = ['/root']
131 135
        if self.g.is_dir('/home/'):
132
            homedirs += self.ls('/home/')
136
            homedirs += self._ls('/home/')
133 137

  
134 138
        if print_header:
135 139
            self.out.output("Removing sensitive user data under %s" %
......
141 145
                if self.g.is_file(fname):
142 146
                    self.g.scrub_file(fname)
143 147
                elif self.g.is_dir(fname):
144
                    self.foreach_file(fname, self.g.scrub_file, ftype='r')
148
                    self._foreach_file(fname, self.g.scrub_file, ftype='r')
145 149

  
146 150
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
b/image_creator/os_type/windows.py
1
# -*- coding: utf-8 -*-
2
#
1 3
# Copyright 2012 GRNET S.A. All rights reserved.
2 4
#
3 5
# Redistribution and use in source and binary forms, with or
......
31 33
# interpreted as representing official policies, either expressed
32 34
# or implied, of GRNET S.A.
33 35

  
36
"""This module hosts OS-specific code common for the various Microsoft
37
Windows OSs."""
38

  
34 39
from image_creator.os_type import OSBase
35 40

  
36 41
import hivex
......
46 51
        self.meta["USERS"] = " ".join(self._get_users())
47 52

  
48 53
    def _get_users(self):
54
        """Returns a list of users found in the images"""
49 55
        samfd, sam = tempfile.mkstemp()
50 56
        try:
51 57
            systemroot = self.g.inspect_get_windows_systemroot(self.root)
b/image_creator/output/__init__.py
1
# -*- coding: utf-8 -*-
2
#
1 3
# Copyright 2012 GRNET S.A. All rights reserved.
2 4
#
3 5
# Redistribution and use in source and binary forms, with or
......
31 33
# interpreted as representing official policies, either expressed
32 34
# or implied, of GRNET S.A.
33 35

  
36
"""This package is intended to provide output classes for printing messages and
37
progress bars. The user can change the output behaviour of the program by
38
subclassing the Output class and assigning the derived one as the output class
39
of the various parts of the image-creator package.
40
"""
41

  
34 42

  
35 43
class Output(object):
36 44
    """A class for printing program output"""
b/image_creator/output/cli.py
1
# -*- coding: utf-8 -*-
2
#
1 3
# Copyright 2012 GRNET S.A. All rights reserved.
2 4
#
3 5
# Redistribution and use in source and binary forms, with or
......
61 63

  
62 64

  
63 65
def clear(stream):
64
    #clear the page
66
    """Clears the terminal screen."""
65 67
    if stream.isatty():
66 68
        stream.write('\033[H\033[2J')
67 69

  
b/image_creator/output/composite.py
1
# -*- coding: utf-8 -*-
2
#
1 3
# Copyright 2012 GRNET S.A. All rights reserved.
2 4
#
3 5
# Redistribution and use in source and binary forms, with or
......
31 33
# interpreted as representing official policies, either expressed
32 34
# or implied, of GRNET S.A.
33 35

  
36
"""This module implements the CompositeOutput output class"""
37

  
34 38
from image_creator.output import Output
35 39

  
36 40

  
b/image_creator/output/dialog.py
1
# -*- coding: utf-8 -*-
2
#
1 3
# Copyright 2012 GRNET S.A. All rights reserved.
2 4
#
3 5
# Redistribution and use in source and binary forms, with or
......
31 33
# interpreted as representing official policies, either expressed
32 34
# or implied, of GRNET S.A.
33 35

  
36
"""This module provides various dialog-based Output classes"""
37

  
34 38
from image_creator.output import Output
35 39
import time
36 40
import fcntl
b/image_creator/rsync.py
1
# -*- coding: utf-8 -*-
2
#
1 3
# Copyright 2012 GRNET S.A. All rights reserved.
2 4
#
3 5
# Redistribution and use in source and binary forms, with or
......
31 33
# interpreted as representing official policies, either expressed
32 34
# or implied, of GRNET S.A.
33 35

  
36
"""This module provides an interface for the rsync utility"""
37

  
34 38
import subprocess
35 39
import time
36 40
import signal
......
98 102
                                   stdout=subprocess.PIPE, bufsize=0)
99 103
        try:
100 104
            total = 0
101
            for line in iter(dry_run.stdout.readline, b''):
105
            for _ in iter(dry_run.stdout.readline, b''):
102 106
                total += 1
103 107
        finally:
104 108
            dry_run.communicate()
......
113 117
        try:
114 118
            t = time.time()
115 119
            i = 0
116
            for line in iter(run.stdout.readline, b''):
120
            for _ in iter(run.stdout.readline, b''):
117 121
                i += 1
118 122
                current = time.time()
119 123
                if current - t > 0.1:
b/image_creator/util.py
1
# -*- coding: utf-8 -*-
2
#
1 3
# Copyright 2012 GRNET S.A. All rights reserved.
2 4
#
3 5
# Redistribution and use in source and binary forms, with or
......
31 33
# interpreted as representing official policies, either expressed
32 34
# or implied, of GRNET S.A.
33 35

  
36
"""This module provides various helper functions to be used by other parts of
37
the package.
38
"""
39

  
34 40
import sh
35 41
import hashlib
36 42
import time
b/setup.py
1 1
#!/usr/bin/env python
2

  
2
# -*- coding: utf-8 -*-
3
#
3 4
# Copyright 2012 GRNET S.A. All rights reserved.
4 5
#
5 6
# Redistribution and use in source and binary forms, with or

Also available in: Unified diff