Add various Windows syspreps
[snf-image-creator] / image_creator / os_type / linux.py
index 86638c0..5ff99aa 100644 (file)
@@ -1,3 +1,5 @@
+# -*- coding: utf-8 -*-
+#
 # Copyright 2012 GRNET S.A. All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or
@@ -31,6 +33,8 @@
 # interpreted as representing official policies, either expressed
 # or implied, of GRNET S.A.
 
+"""This module hosts OS-specific code for Linux"""
+
 from image_creator.os_type.unix import Unix, sysprep
 
 import re
@@ -39,55 +43,15 @@ import time
 
 class Linux(Unix):
     """OS class for Linux"""
-    def __init__(self, rootdev, ghandler, output):
-        super(Linux, self).__init__(rootdev, ghandler, output)
+    def __init__(self, image, **kargs):
+        super(Linux, self).__init__(image, **kargs)
         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)
-
-    def get_uuid(self, dev):
-        if dev in self._uuid:
-            return self._uuid[dev]
-
-        uuid = self.g.vfs_uuid(dev)
-        assert len(uuid)
-        self._uuid[dev] = uuid
-        return uuid
-
-    @sysprep(enabled=False)
-    def remove_user_accounts(self, print_header=True):
+    @sysprep('Removing user accounts with id greater that 1000', enabled=False)
+    def remove_user_accounts(self):
         """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
 
@@ -137,14 +101,10 @@ class Linux(Unix):
             if self.g.is_dir(home) and home.startswith('/home/'):
                 self.g.rm_rf(home)
 
-    @sysprep()
-    def cleanup_passwords(self, print_header=True):
+    @sysprep('Cleaning up password & locking all user accounts')
+    def cleanup_passwords(self):
         """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():
@@ -156,15 +116,12 @@ class Linux(Unix):
 
         self.g.write('/etc/shadow', "\n".join(shadow) + '\n')
 
-    @sysprep()
-    def fix_acpid(self, print_header=True):
+    @sysprep('Fixing acpid powerdown action')
+    def fix_acpid(self):
         """Replace acpid powerdown action scripts to immediately shutdown the
         system without checking if a GUI is running.
         """
 
-        if print_header:
-            self.out.output('Fixing acpid powerdown action')
-
         powerbtn_action = '#!/bin/sh\n\nPATH=/sbin:/bin:/usr/bin\n' \
                           'shutdown -h now "Power button pressed"\n'
 
@@ -175,21 +132,21 @@ class Linux(Unix):
 
         event_exp = re.compile('event=(.+)', re.I)
         action_exp = re.compile('action=(.+)', re.I)
-        for f in self.g.readdir(events_dir):
-            if f['ftyp'] != 'r':
+        for events_file in self.g.readdir(events_dir):
+            if events_file['ftyp'] != 'r':
                 continue
 
-            fullpath = "%s/%s" % (events_dir, f['name'])
+            fullpath = "%s/%s" % (events_dir, events_file['name'])
             event = ""
             action = ""
             for line in self.g.cat(fullpath).splitlines():
-                m = event_exp.match(line)
-                if m:
-                    event = m.group(1)
+                match = event_exp.match(line)
+                if match:
+                    event = match.group(1)
                     continue
-                m = action_exp.match(line)
-                if m:
-                    action = m.group(1)
+                match = action_exp.match(line)
+                if match:
+                    action = match.group(1)
                     continue
 
             if event.strip() in ("button[ /]power", "button/power.*"):
@@ -217,31 +174,25 @@ class Linux(Unix):
 
         self.out.warn("No acpi power button event found!")
 
-    @sysprep()
-    def remove_persistent_net_rules(self, print_header=True):
+    @sysprep('Removing persistent network interface names')
+    def remove_persistent_net_rules(self):
         """Remove udev rules that will keep network interface names persistent
         after hardware changes and reboots. Those rules will be created again
         the next time the image runs.
         """
 
-        if print_header:
-            self.out.output('Removing persistent network interface names')
-
         rule_file = '/etc/udev/rules.d/70-persistent-net.rules'
         if self.g.is_file(rule_file):
             self.g.rm(rule_file)
 
-    @sysprep()
-    def remove_swap_entry(self, print_header=True):
+    @sysprep('Removing swap entry from fstab')
+    def remove_swap_entry(self):
         """Remove swap entry from /etc/fstab. If swap is the last partition
         then the partition will be removed when shrinking is performed. If the
         swap partition is not the last partition in the disk or if you are not
         going to shrink the image you should probably disable this.
         """
 
-        if print_header:
-            self.out.output('Removing swap entry from fstab')
-
         new_fstab = ""
         fstab = self.g.cat('/etc/fstab')
         for line in fstab.splitlines():
@@ -254,16 +205,12 @@ class Linux(Unix):
 
         self.g.write('/etc/fstab', new_fstab)
 
-    @sysprep()
-    def use_persistent_block_device_names(self, print_header=True):
+    @sysprep('Replacing fstab & grub non-persistent device references')
+    def use_persistent_block_device_names(self):
         """Scan fstab & grub configuration files and replace all non-persistent
         device references with UUIDs.
         """
 
-        if print_header:
-            self.out.output("Replacing fstab & grub non-persistent device "
-                            "references")
-
         # convert all devices in fstab to persistent
         persistent_root = self._persistent_fstab()
 
@@ -271,6 +218,9 @@ class Linux(Unix):
         self._persistent_grub1(persistent_root)
 
     def _persistent_grub1(self, new_root):
+        """Replaces non-persistent device name occurencies with persistent
+        ones in GRUB1 configuration files.
+        """
         if self.g.is_file('/boot/grub/menu.lst'):
             grub1 = '/boot/grub/menu.lst'
         elif self.g.is_file('/etc/grub.conf'):
@@ -283,7 +233,7 @@ class Linux(Unix):
             roots = self.g.aug_match('/files%s/title[*]/kernel/root' % grub1)
             for root in roots:
                 dev = self.g.aug_get(root)
-                if not self.is_persistent(dev):
+                if not self._is_persistent(dev):
                     # This is not always correct. Grub may contain root entries
                     # for other systems, but we only support 1 OS per hard
                     # disk, so this shouldn't harm.
@@ -293,6 +243,9 @@ class Linux(Unix):
             self.g.aug_close()
 
     def _persistent_fstab(self):
+        """Replaces non-persistent device name occurencies in /etc/fstab with
+        persistent ones.
+        """
         mpoints = self.g.mountpoints()
         if len(mpoints) == 0:
             pass  # TODO: error handling
@@ -317,6 +270,9 @@ class Linux(Unix):
         return root_dev
 
     def _convert_fstab_line(self, line, devices):
+        """Replace non-persistent device names in an fstab line to their UUID
+        equivalent
+        """
         orig = line
         line = line.split('#')[0].strip()
         if len(line) == 0:
@@ -330,9 +286,9 @@ class Linux(Unix):
         dev = entry[0]
         mpoint = entry[1]
 
-        if not self.is_persistent(dev):
+        if not self._is_persistent(dev):
             if mpoint in devices:
-                dev = "UUID=%s" % self.get_uuid(devices[mpoint])
+                dev = "UUID=%s" % self._get_uuid(devices[mpoint])
                 entry[0] = dev
             else:
                 # comment out the entry
@@ -341,4 +297,46 @@ class Linux(Unix):
 
         return orig, dev, mpoint
 
+    def _do_collect_metadata(self):
+        """Collect metadata about the OS"""
+        super(Linux, self)._do_collect_metadata()
+        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):
+        """Returns a list of non-locked user accounts"""
+        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):
+        """Checks if a device name is persistent."""
+        return not self._persistent.match(dev)
+
+    def _get_uuid(self, dev):
+        """Returns the UUID corresponding to a device"""
+        if dev in self._uuid:
+            return self._uuid[dev]
+
+        uuid = self.g.vfs_uuid(dev)
+        assert len(uuid)
+        self._uuid[dev] = uuid
+        return uuid
+
 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :