diff --git a/ChangeLog b/ChangeLog index 076a4acc2e36374a33e7048ed62c50e9d061097a..7bbb856ec2c553d319ef0333c304824b82f42b0d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2013-04-25, v0.2.9 + * Support kamaki 0.8 + * Fix a bug in util.get_command() + * Move some linux specific code from unix.py to linux.py + 2013-03-28, v0.2.8 * Fix a bug in wizard mode * Cleanup and refine the code diff --git a/docs/conf.py b/docs/conf.py index ee219f630750cadad6e1184f01eaaf73d6a7b687..ef55a6d918b010d599a9f6c7f58d8afc708d6130 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -50,7 +50,7 @@ copyright = u'2012, 2013 GRNET S.A. All rights reserved' # built documents. # # The short X.Y version. -version = '0.2.8' +version = '0.2.9' # The full version, including alpha/beta/rc tags. release = '0.2.8' diff --git a/image_creator/__init__.py b/image_creator/__init__.py index 3192fae92e4d4b00a5419336205dc7770313f658..a57d51e43ea58b49fcf7c9efd1ecf31956c0c0dc 100644 --- a/image_creator/__init__.py +++ b/image_creator/__init__.py @@ -31,6 +31,6 @@ # interpreted as representing official policies, either expressed # or implied, of GRNET S.A. -__version__ = '0.2.8' +__version__ = '0.2.9' # vim: set sta sts=4 shiftwidth=4 sw=4 et ai : diff --git a/image_creator/dialog_main.py b/image_creator/dialog_main.py index d2c9182db9926dd3105b5609b35323c403eecc73..7e6b33dcf30e523320a50c968406fe848358752b 100644 --- a/image_creator/dialog_main.py +++ b/image_creator/dialog_main.py @@ -99,7 +99,8 @@ def create_image(d, media, out, tmp): "image creation process?\n\nChoose <Wizard> to run the wizard," \ " <Expert> to run the snf-image-creator in expert mode or " \ "press ESC to quit the program." \ - % (image.ostype if image.ostype == image.distro else "%s (%s)" % + % (image.ostype if image.ostype == image.distro or + image.distro == "unknown" else "%s (%s)" % (image.ostype, image.distro)) update_background_title(session) diff --git a/image_creator/disk.py b/image_creator/disk.py index edcc4db4302ef341fb8823d75ecf2a856b328276..b4311f8fa44ce3091a7db77d3549c021ccfd2471 100644 --- a/image_creator/disk.py +++ b/image_creator/disk.py @@ -181,7 +181,7 @@ class Disk(object): return "/dev/mapper/%s" % snapshot def get_image(self, media): - """Returns a newly created ImageCreator instance.""" + """Returns a newly created Image instance.""" image = Image(media, self.out) self._images.append(image) @@ -189,8 +189,7 @@ class Disk(object): return image def destroy_image(self, image): - """Destroys an ImageCreator instance previously created by - get_image_creator method. + """Destroys an Image instance previously created by get_image method. """ self._images.remove(image) image.destroy() diff --git a/image_creator/image.py b/image_creator/image.py index a5804a30318f83c4c5611c2673b0fc666ab9a6e9..7d1ddfeef98f6c529bf631923ff2a727ab6e6c6d 100644 --- a/image_creator/image.py +++ b/image_creator/image.py @@ -107,7 +107,9 @@ class Image(object): self.ostype = self.g.inspect_get_type(self.root) self.distro = self.g.inspect_get_distro(self.root) - self.out.success('found a(n) %s system' % self.distro) + self.out.success( + 'found a(n) %s system' % + self.ostype if self.distro == "unknown" else self.distro) def _get_os(self): """Return an OS class instance for this image""" diff --git a/image_creator/kamaki_wrapper.py b/image_creator/kamaki_wrapper.py index 654a9332f40f2f89f92174b4c89e1471f196ff45..3f809d0f4d37a9f230e0d5b79e6c4637e1f5a1b0 100644 --- a/image_creator/kamaki_wrapper.py +++ b/image_creator/kamaki_wrapper.py @@ -61,7 +61,7 @@ class Kamaki(object): def get_account(token): """Return the account corresponding to this token""" config = Config() - astakos = AstakosClient(config.get('astakos', 'url'), token) + astakos = AstakosClient(config.get('user', 'url'), token) try: account = astakos.info() except ClientError as e: @@ -78,7 +78,7 @@ class Kamaki(object): config = Config() - pithos_url = config.get('store', 'url') + pithos_url = config.get('file', 'url') self.pithos_client = PithosClient( pithos_url, self.account['auth_token'], self.account['uuid'], self.CONTAINER) diff --git a/image_creator/os_type/linux.py b/image_creator/os_type/linux.py index 1bee1f2cd2124b4b50877ab3a6232da3843ec329..86638c02092b1bd4bb1ea39a5b47ee6455c25801 100644 --- a/image_creator/os_type/linux.py +++ b/image_creator/os_type/linux.py @@ -44,6 +44,30 @@ class Linux(Unix): self._uuid = dict() self._persistent = re.compile('/dev/[hsv]d[a-z][1-9]*') + self.meta["USERS"] = " ".join(self._get_passworded_users()) + + # Delete the USERS metadata if empty + if not len(self.meta['USERS']): + self.out.warn("No passworded users found!") + del self.meta['USERS'] + + def _get_passworded_users(self): + users = [] + regexp = re.compile('(\S+):((?:!\S+)|(?:[^!*]\S+)|):(?:\S*:){6}') + + for line in self.g.cat('/etc/shadow').splitlines(): + match = regexp.match(line) + if not match: + continue + + user, passwd = match.groups() + if len(passwd) > 0 and passwd[0] == '!': + self.out.warn("Ignoring locked %s account." % user) + else: + users.append(user) + + return users + def is_persistent(self, dev): return not self._persistent.match(dev) @@ -56,6 +80,82 @@ class Linux(Unix): self._uuid[dev] = uuid return uuid + @sysprep(enabled=False) + def remove_user_accounts(self, print_header=True): + """Remove all user accounts with id greater than 1000""" + + if print_header: + self.out.output("Removing all user accounts with id greater than " + "1000") + + if 'USERS' not in self.meta: + return + + # Remove users from /etc/passwd + passwd = [] + removed_users = {} + metadata_users = self.meta['USERS'].split() + for line in self.g.cat('/etc/passwd').splitlines(): + fields = line.split(':') + if int(fields[2]) > 1000: + removed_users[fields[0]] = fields + # remove it from the USERS metadata too + if fields[0] in metadata_users: + metadata_users.remove(fields[0]) + else: + passwd.append(':'.join(fields)) + + self.meta['USERS'] = " ".join(metadata_users) + + # Delete the USERS metadata if empty + if not len(self.meta['USERS']): + del self.meta['USERS'] + + self.g.write('/etc/passwd', '\n'.join(passwd) + '\n') + + # Remove the corresponding /etc/shadow entries + shadow = [] + for line in self.g.cat('/etc/shadow').splitlines(): + fields = line.split(':') + if fields[0] not in removed_users: + shadow.append(':'.join(fields)) + + self.g.write('/etc/shadow', "\n".join(shadow) + '\n') + + # Remove the corresponding /etc/group entries + group = [] + for line in self.g.cat('/etc/group').splitlines(): + fields = line.split(':') + # Remove groups tha have the same name as the removed users + if fields[0] not in removed_users: + group.append(':'.join(fields)) + + self.g.write('/etc/group', '\n'.join(group) + '\n') + + # Remove home directories + for home in [field[5] for field in removed_users.values()]: + if self.g.is_dir(home) and home.startswith('/home/'): + self.g.rm_rf(home) + + @sysprep() + def cleanup_passwords(self, print_header=True): + """Remove all passwords and lock all user accounts""" + + if print_header: + self.out.output("Cleaning up passwords & locking all user " + "accounts") + + shadow = [] + + for line in self.g.cat('/etc/shadow').splitlines(): + fields = line.split(':') + if fields[1] not in ('*', '!'): + fields[1] = '!' + + shadow.append(":".join(fields)) + + self.g.write('/etc/shadow', "\n".join(shadow) + '\n') + @sysprep() def fix_acpid(self, print_header=True): """Replace acpid powerdown action scripts to immediately shutdown the diff --git a/image_creator/os_type/unix.py b/image_creator/os_type/unix.py index 38dc0fba11892fa984b21fc6564883de3c386678..8489d4dec8ce6017f5c77f0bc9efd0ddc56efb9b 100644 --- a/image_creator/os_type/unix.py +++ b/image_creator/os_type/unix.py @@ -39,6 +39,7 @@ from image_creator.os_type import OSBase, sysprep class Unix(OSBase): """OS class for Unix""" sensitive_userdata = [ + '.history', '.bash_history', '.gnupg', '.ssh', @@ -49,105 +50,6 @@ class Unix(OSBase): def __init__(self, rootdev, ghandler, output): super(Unix, self).__init__(rootdev, ghandler, output) - self.meta["USERS"] = " ".join(self._get_passworded_users()) - # Delete the USERS metadata if empty - if not len(self.meta['USERS']): - self.out.warn("No passworded users found!") - del self.meta['USERS'] - - def _get_passworded_users(self): - users = [] - regexp = re.compile('(\S+):((?:!\S+)|(?:[^!*]\S+)|):(?:\S*:){6}') - - for line in self.g.cat('/etc/shadow').splitlines(): - match = regexp.match(line) - if not match: - continue - - user, passwd = match.groups() - if len(passwd) > 0 and passwd[0] == '!': - self.out.warn("Ignoring locked %s account." % user) - else: - users.append(user) - - return users - - @sysprep(enabled=False) - def remove_user_accounts(self, print_header=True): - """Remove all user accounts with id greater than 1000""" - - if print_header: - self.out.output("Removing all user accounts with id greater than " - "1000") - - if 'USERS' not in self.meta: - return - - # Remove users from /etc/passwd - passwd = [] - removed_users = {} - metadata_users = self.meta['USERS'].split() - for line in self.g.cat('/etc/passwd').splitlines(): - fields = line.split(':') - if int(fields[2]) > 1000: - removed_users[fields[0]] = fields - # remove it from the USERS metadata too - if fields[0] in metadata_users: - metadata_users.remove(fields[0]) - else: - passwd.append(':'.join(fields)) - - self.meta['USERS'] = " ".join(metadata_users) - - # Delete the USERS metadata if empty - if not len(self.meta['USERS']): - del self.meta['USERS'] - - self.g.write('/etc/passwd', '\n'.join(passwd) + '\n') - - # Remove the corresponding /etc/shadow entries - shadow = [] - for line in self.g.cat('/etc/shadow').splitlines(): - fields = line.split(':') - if fields[0] not in removed_users: - shadow.append(':'.join(fields)) - - self.g.write('/etc/shadow', "\n".join(shadow) + '\n') - - # Remove the corresponding /etc/group entries - group = [] - for line in self.g.cat('/etc/group').splitlines(): - fields = line.split(':') - # Remove groups tha have the same name as the removed users - if fields[0] not in removed_users: - group.append(':'.join(fields)) - - self.g.write('/etc/group', '\n'.join(group) + '\n') - - # Remove home directories - for home in [field[5] for field in removed_users.values()]: - if self.g.is_dir(home) and home.startswith('/home/'): - self.g.rm_rf(home) - - @sysprep() - def cleanup_passwords(self, print_header=True): - """Remove all passwords and lock all user accounts""" - - if print_header: - self.out.output("Cleaning up passwords & locking all user " - "accounts") - - shadow = [] - - for line in self.g.cat('/etc/shadow').splitlines(): - fields = line.split(':') - if fields[1] not in ('*', '!'): - fields[1] = '!' - - shadow.append(":".join(fields)) - - self.g.write('/etc/shadow', "\n".join(shadow) + '\n') - @sysprep() def cleanup_cache(self, print_header=True): """Remove all regular files under /var/cache""" diff --git a/image_creator/util.py b/image_creator/util.py index 9339fd80aac54dc66c8573113bd21f737db1745c..61a97e933a64c045b335148633d997c01c3a35a4 100644 --- a/image_creator/util.py +++ b/image_creator/util.py @@ -53,7 +53,7 @@ def get_command(command): try: return sh.__getattr__(command) - except sh.CommadNotFount as e: + except sh.CommandNotFound as e: return find_sbin_command(command, e)