Statistics
| Branch: | Tag: | Revision:

root / image_creator / os_type / linux.py @ 3c33e68a

History | View | Annotate | Download (8.5 kB)

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