Statistics
| Branch: | Tag: | Revision:

root / image_creator / os_type / linux.py @ 4a2fd05c

History | View | Annotate | Download (8.2 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 remove_swap_entry(self, print_header=True):
130
        """Remove swap entry from /etc/fstab. If swap is the last partition
131
        then the partition will be removed when shrinking is performed. If the
132
        swap partition is not the last partition in the disk or if you are not
133
        going to shrink the image you should probably disable this.
134
        """
135

    
136
        if print_header:
137
            output('Removing swap entry from fstab')
138

    
139
        new_fstab = ""
140
        fstab = self.g.cat('/etc/fstab')
141
        for line in fstab.splitlines():
142

    
143
            entry = line.split('#')[0].strip().split()
144
            if len(entry) == 6 and entry[2] == 'swap':
145
                continue
146

    
147
            new_fstab += "%s\n" % line
148

    
149
        self.g.write('/etc/fstab', new_fstab)
150

    
151
    @sysprep()
152
    def persistent_devs(self, print_header=True):
153
        """Scan fstab & grub configuration files and replace all non-persistent
154
        device appearences with UUIDs.
155
        """
156

    
157
        if print_header:
158
            output('Replacing fstab & grub non-persistent device appearences')
159

    
160
        # convert all devices in fstab to persistent
161
        persistent_root = self._persistent_fstab()
162

    
163
        # convert all devices in grub1 to persistent
164
        self._persistent_grub1(persistent_root)
165

    
166
    def _persistent_grub1(self, new_root):
167
        if self.g.is_file('/boot/grub/menu.lst'):
168
            grub1 = '/boot/grub/menu.lst'
169
        elif self.g.is_file('/etc/grub.conf'):
170
            grub1 = '/etc/grub.conf'
171
        else:
172
            return
173

    
174
        self.g.aug_init('/', 0)
175
        try:
176
            roots = self.g.aug_match('/files%s/title[*]/kernel/root' % grub1)
177
            for root in roots:
178
                dev = self.g.aug_get(root)
179
                if not self.is_persistent(dev):
180
                    # This is not always correct. Grub may contain root entries
181
                    # for other systems, but we only support 1 OS per hard
182
                    # disk, so this shouldn't harm.
183
                    self.g.aug_set(root, new_root)
184
        finally:
185
            self.g.aug_save()
186
            self.g.aug_close()
187

    
188
    def _persistent_fstab(self):
189
        mpoints = self.g.mountpoints()
190
        if len(mpoints) == 0:
191
            pass  # TODO: error handling
192

    
193
        device_dict = dict([[mpoint, dev] for dev, mpoint in mpoints])
194

    
195
        root_dev = None
196
        new_fstab = ""
197
        fstab = self.g.cat('/etc/fstab')
198
        for line in fstab.splitlines():
199

    
200
            line, dev, mpoint = self._convert_fstab_line(line, device_dict)
201
            new_fstab += "%s\n" % line
202

    
203
            if mpoint == '/':
204
                root_dev = dev
205

    
206
        self.g.write('/etc/fstab', new_fstab)
207
        if root_dev is None:
208
            pass  # TODO: error handling
209

    
210
        return root_dev
211

    
212
    def _convert_fstab_line(self, line, devices):
213
        orig = line
214
        line = line.split('#')[0].strip()
215
        if len(line) == 0:
216
            return orig, "", ""
217

    
218
        entry = line.split()
219
        if len(entry) != 6:
220
            warn("Detected abnormal entry in fstab")
221
            return orig, "", ""
222

    
223
        dev = entry[0]
224
        mpoint = entry[1]
225

    
226
        if not self.is_persistent(dev):
227
            if mpoint in devices:
228
                dev = "UUID=%s" % self.get_uuid(devices[mpoint])
229
                entry[0] = dev
230
            else:
231
                # comment out the entry
232
                entry[0] = "#%s" % dev
233
            return " ".join(entry), dev, mpoint
234

    
235
        return orig, dev, mpoint
236

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