Fix a bug in linux sysprep_* methods
[snf-image-creator] / image_creator / os_type / linux.py
1 from image_creator.os_type.unix import Unix
2 import re
3
4
5 class Linux(Unix):
6     def __init__(self, rootdev, ghandler):
7         super(Linux, self).__init__(rootdev, ghandler)
8         self._uuid = dict()
9         self._persistent = re.compile('/dev/[hsv]d[a-z][1-9]*')
10
11     def is_persistent(self, dev):
12         return not self._persistent.match(dev)
13
14     def get_uuid(self, dev):
15         if dev in self._uuid:
16             return self._uuid[dev]
17
18         for attr in self.g.blkid(dev):
19             if attr[0] == 'UUID':
20                 self._uuid[dev] = attr[1]
21                 return attr[1]
22
23     def sysprep(self):
24         """Prepere system for image creation."""
25         self.sysprep_acpid()
26         self.sysprep_persistent_net_rules()
27         self.sysprep_persistent_devs()
28
29     def sysprep_acpid(self):
30         """Replace acpid powerdown action scripts to automatically shutdown
31         the system without checking if a GUI is running.
32         """
33         action = '#!/bin/sh\n\nPATH=/sbin:/bin:/usr/bin\n shutdown -h now '
34         '\"Power button pressed\"'
35
36         if self.g.is_file('/etc/acpi/powerbtn.sh'):
37             self.g.write('/etc/acpi/powerbtn.sh', action)
38         elif self.g.is_file('/etc/acpi/actions/power.sh'):
39             self.g.write('/etc/acpi/actions/power.sh', action)
40         else:
41             print "Warning: No acpid action file found"
42
43     def sysprep_persistent_net_rules(self):
44         """Remove udev rules that will keep network interface names persistent
45         after hardware changes and reboots. Those rules will be created again
46         the next time the image runs.
47         """
48         rule_file = '/etc/udev/rules.d/70-persistent-net.rules'
49         if self.g.is_file(rule_file):
50             self.g.rm(rule_file)
51
52     def sysprep_persistent_devs(self):
53         """Scan fstab and grub configuration files and replace all
54         non-persistent device appearences with UUIDs.
55         """
56         # convert all devices in fstab to persistent
57         persistent_root = self._persistent_fstab()
58
59         # convert all devices in grub1 to persistent
60         self._persistent_grub1(persistent_root)
61
62     def _persistent_grub1(self, new_root):
63         if self.g.is_file('/boot/grub/menu.lst'):
64             grub1 = '/boot/grub/menu.lst'
65         elif self.g.is_file('/etc/grub.conf'):
66             grub1 = '/etc/grub.conf'
67         else:
68             return
69
70         self.g.aug_init('/', 0)
71         try:
72             roots = self.g.aug_match('/files%s/title[*]/kernel/root' % grub1)
73             for root in roots:
74                 dev = self.g.aug_get(root)
75                 if not self.is_persistent(dev):
76                     # This is not always correct. Grub may contain root entries
77                     # for other systems, but we only support 1 OS per hard
78                     # disk, so this shouldn't harm.
79                     self.g.aug_set(root, new_root)
80         finally:
81             self.g.aug_save()
82             self.g.aug_close()
83
84     def _persistent_fstab(self):
85         mpoints = self.g.mountpoints()
86         if len(mpoints) == 0:
87             pass  # TODO: error handling
88
89         device_dict = dict([[mpoint, dev] for dev, mpoint in mpoints])
90
91         root_dev = None
92         new_fstab = ""
93         fstab = self.g.cat('/etc/fstab')
94         for line in fstab.splitlines():
95
96             line, dev, mpoint = self._convert_fstab_line(line, device_dict)
97             new_fstab += "%s\n" % line
98
99             if mpoint == '/':
100                 root_dev = dev
101
102         self.g.write('/etc/fstab', new_fstab)
103         if root_dev is None:
104             pass  # TODO: error handling
105
106         return root_dev
107
108     def _convert_fstab_line(self, line, devices):
109         orig = line
110         line = line.split('#')[0].strip()
111         if len(line) == 0:
112             return orig, "", ""
113
114         entry = line.split()
115         if len(entry) != 6:
116             print "Warning: detected abnorman entry in fstab"
117             return orig, "", ""
118
119         dev = entry[0]
120         mpoint = entry[1]
121
122         if not self.is_persistent(dev):
123             if mpoint in devices:
124                 dev = "UUID=%s" % self.get_uuid(devices[mpoint])
125                 entry[0] = dev
126             else:
127                 # comment out the entry
128                 entry[0] = "#%s" % dev
129             return " ".join(entry), dev, mpoint
130
131         return orig, dev, mpoint
132
133 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :