Statistics
| Branch: | Tag: | Revision:

root / image_creator / os_type / linux.py @ f165adc0

History | View | Annotate | Download (7.4 kB)

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, sysprep
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
    @sysprep()
60
    def fix_acpid(self, print_header=True):
61
        """Replace acpid powerdown action scripts to immediately shutdown the
62
        system without checking if a GUI is running.
63
        """
64

    
65
        if print_header:
66
            output('Fixing acpid powerdown action')
67

    
68
        powerbtn_action = '#!/bin/sh\n\nPATH=/sbin:/bin:/usr/bin\n' \
69
                                'shutdown -h now \"Power button pressed\"\n'
70

    
71
        events_dir = '/etc/acpi/events'
72
        if not self.g.is_dir(events_dir):
73
            warn("No acpid event directory found")
74
            return
75

    
76
        event_exp = re.compile('event=(.+)', re.I)
77
        action_exp = re.compile('action=(.+)', re.I)
78
        for f in self.g.readdir(events_dir):
79
            if f['ftyp'] != 'r':
80
                continue
81

    
82
            fullpath = "%s/%s" % (events_dir, f['name'])
83
            event = ""
84
            action = ""
85
            for line in self.g.cat(fullpath).splitlines():
86
                m = event_exp.match(line)
87
                if m:
88
                    event = m.group(1)
89
                    continue
90
                m = action_exp.match(line)
91
                if m:
92
                    action = m.group(1)
93
                    continue
94

    
95
            if event.strip() == "button[ /]power":
96
                if action:
97
                    if not self.g.is_file(action):
98
                        warn("Acpid action file: %s does not exist" % action)
99
                        return
100
                    self.g.copy_file_to_file(action, \
101
                      "%s.orig.snf-image-creator-%d" % (action, time.time()))
102
                    self.g.write(action, powerbtn_action)
103
                    return
104
                else:
105
                    warn("Acpid event file %s does not contain and action")
106
                    return
107
            elif event.strip() == ".*":
108
                warn("Found action `.*'. Don't know how to handle this." \
109
                    " Please edit \%s' image file manually to make the " \
110
                    "system immediatelly shutdown when an power button acpi " \
111
                    "event occures" % action)
112
                return
113

    
114
    @sysprep()
115
    def persistent_net_rules(self, print_header=True):
116
        """Remove udev rules that will keep network interface names persistent
117
        after hardware changes and reboots. Those rules will be created again
118
        the next time the image runs.
119
        """
120

    
121
        if print_header:
122
            output('Removing persistent network interface names')
123

    
124
        rule_file = '/etc/udev/rules.d/70-persistent-net.rules'
125
        if self.g.is_file(rule_file):
126
            self.g.rm(rule_file)
127

    
128
    @sysprep()
129
    def persistent_devs(self, print_header=True):
130
        """Scan fstab & grub configuration files and replace all non-persistent
131
        device appearences with UUIDs.
132
        """
133

    
134
        if print_header:
135
            output('Replacing fstab & grub non-persistent device appearences')
136

    
137
        # convert all devices in fstab to persistent
138
        persistent_root = self._persistent_fstab()
139

    
140
        # convert all devices in grub1 to persistent
141
        self._persistent_grub1(persistent_root)
142

    
143
    def _persistent_grub1(self, new_root):
144
        if self.g.is_file('/boot/grub/menu.lst'):
145
            grub1 = '/boot/grub/menu.lst'
146
        elif self.g.is_file('/etc/grub.conf'):
147
            grub1 = '/etc/grub.conf'
148
        else:
149
            return
150

    
151
        self.g.aug_init('/', 0)
152
        try:
153
            roots = self.g.aug_match('/files%s/title[*]/kernel/root' % grub1)
154
            for root in roots:
155
                dev = self.g.aug_get(root)
156
                if not self.is_persistent(dev):
157
                    # This is not always correct. Grub may contain root entries
158
                    # for other systems, but we only support 1 OS per hard
159
                    # disk, so this shouldn't harm.
160
                    self.g.aug_set(root, new_root)
161
        finally:
162
            self.g.aug_save()
163
            self.g.aug_close()
164

    
165
    def _persistent_fstab(self):
166
        mpoints = self.g.mountpoints()
167
        if len(mpoints) == 0:
168
            pass  # TODO: error handling
169

    
170
        device_dict = dict([[mpoint, dev] for dev, mpoint in mpoints])
171

    
172
        root_dev = None
173
        new_fstab = ""
174
        fstab = self.g.cat('/etc/fstab')
175
        for line in fstab.splitlines():
176

    
177
            line, dev, mpoint = self._convert_fstab_line(line, device_dict)
178
            new_fstab += "%s\n" % line
179

    
180
            if mpoint == '/':
181
                root_dev = dev
182

    
183
        self.g.write('/etc/fstab', new_fstab)
184
        if root_dev is None:
185
            pass  # TODO: error handling
186

    
187
        return root_dev
188

    
189
    def _convert_fstab_line(self, line, devices):
190
        orig = line
191
        line = line.split('#')[0].strip()
192
        if len(line) == 0:
193
            return orig, "", ""
194

    
195
        entry = line.split()
196
        if len(entry) != 6:
197
            warn("Detected abnormal entry in fstab")
198
            return orig, "", ""
199

    
200
        dev = entry[0]
201
        mpoint = entry[1]
202

    
203
        if not self.is_persistent(dev):
204
            if mpoint in devices:
205
                dev = "UUID=%s" % self.get_uuid(devices[mpoint])
206
                entry[0] = dev
207
            else:
208
                # comment out the entry
209
                entry[0] = "#%s" % dev
210
            return " ".join(entry), dev, mpoint
211

    
212
        return orig, dev, mpoint
213

    
214
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :