Bump version to 0.2.2
[snf-image-creator] / image_creator / os_type / linux.py
index 724e902..0166129 100644 (file)
@@ -1,10 +1,45 @@
-from image_creator.os_type.unix import Unix
+# Copyright 2012 GRNET S.A. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or
+# without modification, are permitted provided that the following
+# conditions are met:
+#
+#   1. Redistributions of source code must retain the above
+#      copyright notice, this list of conditions and the following
+#      disclaimer.
+#
+#   2. Redistributions in binary form must reproduce the above
+#      copyright notice, this list of conditions and the following
+#      disclaimer in the documentation and/or other materials
+#      provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# The views and conclusions contained in the software and
+# documentation are those of the authors and should not be
+# interpreted as representing official policies, either expressed
+# or implied, of GRNET S.A.
+
+from image_creator.os_type.unix import Unix, sysprep
+
 import re
 import re
+import time
 
 
 class Linux(Unix):
 
 
 class Linux(Unix):
-    def __init__(self, rootdev, ghandler):
-        super(Linux, self).__init__(rootdev, ghandler)
+    def __init__(self, rootdev, ghandler, output):
+        super(Linux, self).__init__(rootdev, ghandler, output)
         self._uuid = dict()
         self._persistent = re.compile('/dev/[hsv]d[a-z][1-9]*')
 
         self._uuid = dict()
         self._persistent = re.compile('/dev/[hsv]d[a-z][1-9]*')
 
@@ -15,44 +50,119 @@ class Linux(Unix):
         if dev in self._uuid:
             return self._uuid[dev]
 
         if dev in self._uuid:
             return self._uuid[dev]
 
-        for attr in self.g.blkid(dev):
-            if attr[0] == 'UUID':
-                self._uuid[dev] = attr[1]
-                return attr[1]
-
-    def sysprep(self):
-        """Prepere system for image creation."""
-        self.sysprep_acpid()
-        self.sysprep_persistent_net_rules()
-        self.sysprep_persistent_devs()
+        uuid = self.g.vfs_uuid(dev)
+        assert len(uuid)
+        self._uuid[dev] = uuid
+        return uuid
 
 
-    def sysprep_acpid(self):
-        """Replace acpid powerdown action scripts to automatically shutdown
-        the system without checking if a GUI is running.
+    @sysprep()
+    def fix_acpid(self, print_header=True):
+        """Replace acpid powerdown action scripts to immediately shutdown the
+        system without checking if a GUI is running.
         """
         """
-        action = '#!/bin/sh\n\nPATH=/sbin:/bin:/usr/bin\n shutdown -h now '
-        '\"Power button pressed\"'
 
 
-        if self.g.is_file('/etc/acpi/powerbtn.sh'):
-            self.g.write(action, '/etc/acpi/powerbtn.sh')
-        elif self.g.is_file('/etc/acpi/actions/power.sh'):
-            self.g.write(actions, '/etc/acpi/actions/power.sh')
-        else:
-            print "Warning: No acpid action file found"
+        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'
+
+        events_dir = '/etc/acpi/events'
+        if not self.g.is_dir(events_dir):
+            self.out.warn("No acpid event directory found")
+            return
 
 
-    def sysprep_persistent_net_rules(self):
+        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':
+                continue
+
+            fullpath = "%s/%s" % (events_dir, f['name'])
+            event = ""
+            action = ""
+            for line in self.g.cat(fullpath).splitlines():
+                m = event_exp.match(line)
+                if m:
+                    event = m.group(1)
+                    continue
+                m = action_exp.match(line)
+                if m:
+                    action = m.group(1)
+                    continue
+
+            if event.strip() in ("button[ /]power", "button/power.*"):
+                if action:
+                    if not self.g.is_file(action):
+                        self.out.warn("Acpid action file: %s does not exist" %
+                                      action)
+                        return
+                    self.g.copy_file_to_file(action,
+                                             "%s.orig.snf-image-creator-%d" %
+                                             (action, time.time()))
+                    self.g.write(action, powerbtn_action)
+                    return
+                else:
+                    self.out.warn("Acpid event file %s does not contain and "
+                                  "action")
+                    return
+            elif event.strip() == ".*":
+                self.out.warn("Found action `.*'. Don't know how to handle "
+                              "this. Please edit `%s' image file manually to "
+                              "make the system immediatelly shutdown when an "
+                              "power button acpi event occures." %
+                              action.strip().split()[0])
+                return
+
+        self.out.warn("No acpi power button event found!")
+
+    @sysprep()
+    def remove_persistent_net_rules(self, print_header=True):
         """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.
         """
         """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)
 
         rule_file = '/etc/udev/rules.d/70-persistent-net.rules'
         if self.g.is_file(rule_file):
             self.g.rm(rule_file)
 
-    def sysprep_persistent_devs(self):
-        """Scan fstab and grub configuration files and replace all
-        non-persistent device appearences with UUIDs.
+    @sysprep()
+    def remove_swap_entry(self, print_header=True):
+        """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():
+
+            entry = line.split('#')[0].strip().split()
+            if len(entry) == 6 and entry[2] == 'swap':
+                continue
+
+            new_fstab += "%s\n" % line
+
+        self.g.write('/etc/fstab', new_fstab)
+
+    @sysprep()
+    def use_persistent_block_device_names(self, print_header=True):
+        """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()
 
         # convert all devices in fstab to persistent
         persistent_root = self._persistent_fstab()
 
@@ -113,7 +223,7 @@ class Linux(Unix):
 
         entry = line.split()
         if len(entry) != 6:
 
         entry = line.split()
         if len(entry) != 6:
-            print "Warning: detected abnorman entry in fstab"
+            self.out.warn("Detected abnormal entry in fstab")
             return orig, "", ""
 
         dev = entry[0]
             return orig, "", ""
 
         dev = entry[0]