Add exclude_task decorator in os_type
[snf-image-creator] / image_creator / os_type / linux.py
1 # Copyright 2012 GRNET S.A. All rights reserved.
2 #
3 # Redistribution and use in source and binary forms, with or
4 # without modification, are permitted provided that the following
5 # conditions are met:
6 #
7 #   1. Redistributions of source code must retain the above
8 #      copyright notice, this list of conditions and the following
9 #      disclaimer.
10 #
11 #   2. Redistributions in binary form must reproduce the above
12 #      copyright notice, this list of conditions and the following
13 #      disclaimer in the documentation and/or other materials
14 #      provided with the distribution.
15 #
16 # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 # POSSIBILITY OF SUCH DAMAGE.
28 #
29 # The views and conclusions contained in the software and
30 # documentation are those of the authors and should not be
31 # interpreted as representing official policies, either expressed
32 # or implied, of GRNET S.A.
33
34 from image_creator.os_type.unix import Unix, exclude_task
35 from image_creator.util import warn, output
36
37 import re
38 import time
39
40
41 class Linux(Unix):
42     def __init__(self, rootdev, ghandler):
43         super(Linux, self).__init__(rootdev, ghandler)
44         self._uuid = dict()
45         self._persistent = re.compile('/dev/[hsv]d[a-z][1-9]*')
46
47     def is_persistent(self, dev):
48         return not self._persistent.match(dev)
49
50     def get_uuid(self, dev):
51         if dev in self._uuid:
52             return self._uuid[dev]
53
54         for attr in self.g.blkid(dev):
55             if attr[0] == 'UUID':
56                 self._uuid[dev] = attr[1]
57                 return attr[1]
58
59     def sysprep_fix_acpid(self, print_header=True):
60         """Replace acpid powerdown action scripts to immediately shutdown the
61         system without checking if a GUI is running.
62         """
63
64         if print_header:
65             output('Fixing acpid powerdown action')
66
67         powerbtn_action = '#!/bin/sh\n\nPATH=/sbin:/bin:/usr/bin\n' \
68                                 'shutdown -h now \"Power button pressed\"\n'
69
70         events_dir = '/etc/acpi/events'
71         if not self.g.is_dir(events_dir):
72             warn("No acpid event directory found")
73             return
74
75         event_exp = re.compile('event=(.+)', re.I)
76         action_exp = re.compile('action=(.+)', re.I)
77         for f in self.g.readdir(events_dir):
78             if f['ftyp'] != 'r':
79                 continue
80
81             fullpath = "%s/%s" % (events_dir, f['name'])
82             event = ""
83             action = ""
84             for line in self.g.cat(fullpath).splitlines():
85                 m = event_exp.match(line)
86                 if m:
87                     event = m.group(1)
88                     continue
89                 m = action_exp.match(line)
90                 if m:
91                     action = m.group(1)
92                     continue
93
94             if event.strip() == "button[ /]power":
95                 if action:
96                     if not self.g.is_file(action):
97                         warn("Acpid action file: %s does not exist" % action)
98                         return
99                     self.g.copy_file_to_file(action, \
100                       "%s.orig.snf-image-creator-%d" % (action, time.time()))
101                     self.g.write(action, powerbtn_action)
102                     return
103                 else:
104                     warn("Acpid event file %s does not contain and action")
105                     return
106             elif event.strip() == ".*":
107                 warn("Found action `.*'. Don't know how to handle this." \
108                     " Please edit \%s' image file manually to make the " \
109                     "system immediatelly shutdown when an power button acpi " \
110                     "event occures" % action)
111                 return
112
113     def sysprep_persistent_net_rules(self, print_header=True):
114         """Remove udev rules that will keep network interface names persistent
115         after hardware changes and reboots. Those rules will be created again
116         the next time the image runs.
117         """
118
119         if print_header:
120             output('Removing persistent network interface names')
121
122         rule_file = '/etc/udev/rules.d/70-persistent-net.rules'
123         if self.g.is_file(rule_file):
124             self.g.rm(rule_file)
125
126     def sysprep_persistent_devs(self, print_header=True):
127         """Scan fstab and grub configuration files and replace all
128         non-persistent device appearences with UUIDs.
129         """
130
131         if print_header:
132             output('Replacing fstab & grub non-persistent device appearences')
133
134         # convert all devices in fstab to persistent
135         persistent_root = self._persistent_fstab()
136
137         # convert all devices in grub1 to persistent
138         self._persistent_grub1(persistent_root)
139
140     def _persistent_grub1(self, new_root):
141         if self.g.is_file('/boot/grub/menu.lst'):
142             grub1 = '/boot/grub/menu.lst'
143         elif self.g.is_file('/etc/grub.conf'):
144             grub1 = '/etc/grub.conf'
145         else:
146             return
147
148         self.g.aug_init('/', 0)
149         try:
150             roots = self.g.aug_match('/files%s/title[*]/kernel/root' % grub1)
151             for root in roots:
152                 dev = self.g.aug_get(root)
153                 if not self.is_persistent(dev):
154                     # This is not always correct. Grub may contain root entries
155                     # for other systems, but we only support 1 OS per hard
156                     # disk, so this shouldn't harm.
157                     self.g.aug_set(root, new_root)
158         finally:
159             self.g.aug_save()
160             self.g.aug_close()
161
162     def _persistent_fstab(self):
163         mpoints = self.g.mountpoints()
164         if len(mpoints) == 0:
165             pass  # TODO: error handling
166
167         device_dict = dict([[mpoint, dev] for dev, mpoint in mpoints])
168
169         root_dev = None
170         new_fstab = ""
171         fstab = self.g.cat('/etc/fstab')
172         for line in fstab.splitlines():
173
174             line, dev, mpoint = self._convert_fstab_line(line, device_dict)
175             new_fstab += "%s\n" % line
176
177             if mpoint == '/':
178                 root_dev = dev
179
180         self.g.write('/etc/fstab', new_fstab)
181         if root_dev is None:
182             pass  # TODO: error handling
183
184         return root_dev
185
186     def _convert_fstab_line(self, line, devices):
187         orig = line
188         line = line.split('#')[0].strip()
189         if len(line) == 0:
190             return orig, "", ""
191
192         entry = line.split()
193         if len(entry) != 6:
194             warn("Detected abnormal entry in fstab")
195             return orig, "", ""
196
197         dev = entry[0]
198         mpoint = entry[1]
199
200         if not self.is_persistent(dev):
201             if mpoint in devices:
202                 dev = "UUID=%s" % self.get_uuid(devices[mpoint])
203                 entry[0] = dev
204             else:
205                 # comment out the entry
206                 entry[0] = "#%s" % dev
207             return " ".join(entry), dev, mpoint
208
209         return orig, dev, mpoint
210
211 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :