Statistics
| Branch: | Tag: | Revision:

root / image_creator / os_type / __init__.py @ 71b0ab28

History | View | Annotate | Download (9.2 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 aa2062ba Nikos Skalkotos
34 e77e66a9 Nikos Skalkotos
from image_creator.util import FatalError
35 979096dd Nikos Skalkotos
36 f165adc0 Nikos Skalkotos
import textwrap
37 8c574358 Nikos Skalkotos
import re
38 8c574358 Nikos Skalkotos
39 8c574358 Nikos Skalkotos
40 bb4db5a8 Nikos Skalkotos
def os_cls(distro, osfamily):
41 88f83027 Nikos Skalkotos
    """Given the distro name and the osfamily, return the appropriate class"""
42 5b801534 Nikos Skalkotos
    module = None
43 5b801534 Nikos Skalkotos
    classname = None
44 5b801534 Nikos Skalkotos
    try:
45 f99fe99d Nikos Skalkotos
        module = __import__("image_creator.os_type.%s" % distro,
46 f99fe99d Nikos Skalkotos
                            fromlist=['image_creator.os_type'])
47 5b801534 Nikos Skalkotos
        classname = distro.capitalize()
48 5b801534 Nikos Skalkotos
    except ImportError:
49 f99fe99d Nikos Skalkotos
        module = __import__("image_creator.os_type.%s" % osfamily,
50 f99fe99d Nikos Skalkotos
                            fromlist=['image_creator.os_type'])
51 5b801534 Nikos Skalkotos
        classname = osfamily.capitalize()
52 5b801534 Nikos Skalkotos
53 5b801534 Nikos Skalkotos
    return getattr(module, classname)
54 5b801534 Nikos Skalkotos
55 5b801534 Nikos Skalkotos
56 0d5a999d Nikos Skalkotos
def add_prefix(target):
57 0d5a999d Nikos Skalkotos
    def wrapper(self, *args):
58 0d5a999d Nikos Skalkotos
        prefix = args[0]
59 0d5a999d Nikos Skalkotos
        return map(lambda x: prefix + x, target(self, *args))
60 0d5a999d Nikos Skalkotos
    return wrapper
61 0d5a999d Nikos Skalkotos
62 51fab9c9 Nikos Skalkotos
63 f165adc0 Nikos Skalkotos
def sysprep(enabled=True):
64 88f83027 Nikos Skalkotos
    """Decorator for system preparation tasks"""
65 f165adc0 Nikos Skalkotos
    def wrapper(func):
66 f165adc0 Nikos Skalkotos
        func.sysprep = True
67 f165adc0 Nikos Skalkotos
        func.enabled = enabled
68 fa65eda1 Nikos Skalkotos
        func.executed = False
69 f165adc0 Nikos Skalkotos
        return func
70 f165adc0 Nikos Skalkotos
    return wrapper
71 8c574358 Nikos Skalkotos
72 51fab9c9 Nikos Skalkotos
73 aa2062ba Nikos Skalkotos
class OSBase(object):
74 3b2f6619 Nikos Skalkotos
    """Basic operating system class"""
75 f165adc0 Nikos Skalkotos
76 e77e66a9 Nikos Skalkotos
    def __init__(self, rootdev, ghandler, output):
77 aa2062ba Nikos Skalkotos
        self.root = rootdev
78 aa2062ba Nikos Skalkotos
        self.g = ghandler
79 e77e66a9 Nikos Skalkotos
        self.out = output
80 5886f568 Nikos Skalkotos
        self.meta = {}
81 71b0ab28 Nikos Skalkotos
82 71b0ab28 Nikos Skalkotos
    def collect_metadata(self):
83 71b0ab28 Nikos Skalkotos
        """Collect metadata about the OS"""
84 71b0ab28 Nikos Skalkotos
85 71b0ab28 Nikos Skalkotos
        try:
86 71b0ab28 Nikos Skalkotos
            if not self.mount(readonly=True):
87 71b0ab28 Nikos Skalkotos
                raise FatalError("Unable to mount the media read-only")
88 71b0ab28 Nikos Skalkotos
89 71b0ab28 Nikos Skalkotos
            self.out.output('Collecting image metadata ...', False)
90 71b0ab28 Nikos Skalkotos
            self._do_collect_metadata()
91 71b0ab28 Nikos Skalkotos
            self.out.success('done')
92 71b0ab28 Nikos Skalkotos
        finally:
93 71b0ab28 Nikos Skalkotos
            self.umount()
94 71b0ab28 Nikos Skalkotos
95 71b0ab28 Nikos Skalkotos
    def _do_collect_metadata(self):
96 71b0ab28 Nikos Skalkotos
97 5886f568 Nikos Skalkotos
        self.meta['ROOT_PARTITION'] = "%d" % self.g.part_to_partnum(self.root)
98 5886f568 Nikos Skalkotos
        self.meta['OSFAMILY'] = self.g.inspect_get_type(self.root)
99 5886f568 Nikos Skalkotos
        self.meta['OS'] = self.g.inspect_get_distro(self.root)
100 d8a12a7e Nikos Skalkotos
        if self.meta['OS'] == "unknown":
101 d8a12a7e Nikos Skalkotos
            self.meta['OS'] = self.meta['OSFAMILY']
102 5886f568 Nikos Skalkotos
        self.meta['DESCRIPTION'] = self.g.inspect_get_product_name(self.root)
103 5886f568 Nikos Skalkotos
104 f165adc0 Nikos Skalkotos
    def _is_sysprep(self, obj):
105 f165adc0 Nikos Skalkotos
        return getattr(obj, 'sysprep', False) and callable(obj)
106 76d4a1c9 Nikos Skalkotos
107 f165adc0 Nikos Skalkotos
    def list_syspreps(self):
108 76d4a1c9 Nikos Skalkotos
109 f99fe99d Nikos Skalkotos
        objs = [getattr(self, name) for name in dir(self)
110 f99fe99d Nikos Skalkotos
                if not name.startswith('_')]
111 f165adc0 Nikos Skalkotos
112 fa65eda1 Nikos Skalkotos
        return [x for x in objs if self._is_sysprep(x) and x.executed is False]
113 f165adc0 Nikos Skalkotos
114 12078c7e Nikos Skalkotos
    def sysprep_info(self, obj):
115 12078c7e Nikos Skalkotos
        assert self._is_sysprep(obj), "Object is not a sysprep"
116 12078c7e Nikos Skalkotos
117 12078c7e Nikos Skalkotos
        return (obj.__name__.replace('_', '-'), textwrap.dedent(obj.__doc__))
118 12078c7e Nikos Skalkotos
119 e7cbfb0a Nikos Skalkotos
    def get_sysprep_by_name(self, name):
120 e7cbfb0a Nikos Skalkotos
        """Returns the sysprep object with the given name"""
121 f165adc0 Nikos Skalkotos
        error_msg = "Syprep operation %s does not exist for %s" % \
122 e7cbfb0a Nikos Skalkotos
                    (name, self.__class__.__name__)
123 f165adc0 Nikos Skalkotos
124 f165adc0 Nikos Skalkotos
        method_name = name.replace('-', '_')
125 f165adc0 Nikos Skalkotos
        method = None
126 f165adc0 Nikos Skalkotos
        try:
127 f165adc0 Nikos Skalkotos
            method = getattr(self, method_name)
128 f165adc0 Nikos Skalkotos
        except AttributeError:
129 f165adc0 Nikos Skalkotos
            raise FatalError(error_msg)
130 f165adc0 Nikos Skalkotos
131 f165adc0 Nikos Skalkotos
        if not self._is_sysprep(method):
132 f165adc0 Nikos Skalkotos
            raise FatalError(error_msg)
133 f165adc0 Nikos Skalkotos
134 e7cbfb0a Nikos Skalkotos
        return method
135 f165adc0 Nikos Skalkotos
136 e7cbfb0a Nikos Skalkotos
    def enable_sysprep(self, obj):
137 dcf9274b Vangelis Koukis
        """Enable a system preparation operation"""
138 e7cbfb0a Nikos Skalkotos
        setattr(obj.im_func, 'enabled', True)
139 f165adc0 Nikos Skalkotos
140 e7cbfb0a Nikos Skalkotos
    def disable_sysprep(self, obj):
141 dcf9274b Vangelis Koukis
        """Disable a system preparation operation"""
142 e7cbfb0a Nikos Skalkotos
        setattr(obj.im_func, 'enabled', False)
143 f165adc0 Nikos Skalkotos
144 f165adc0 Nikos Skalkotos
    def print_syspreps(self):
145 dcf9274b Vangelis Koukis
        """Print enabled and disabled system preparation operations."""
146 76d4a1c9 Nikos Skalkotos
147 2e50092b Nikos Skalkotos
        syspreps = self.list_syspreps()
148 2e50092b Nikos Skalkotos
        enabled = filter(lambda x: x.enabled, syspreps)
149 2e50092b Nikos Skalkotos
        disabled = filter(lambda x: not x.enabled, syspreps)
150 76d4a1c9 Nikos Skalkotos
151 f165adc0 Nikos Skalkotos
        wrapper = textwrap.TextWrapper()
152 f165adc0 Nikos Skalkotos
        wrapper.subsequent_indent = '\t'
153 f165adc0 Nikos Skalkotos
        wrapper.initial_indent = '\t'
154 4a2fd05c Nikos Skalkotos
        wrapper.width = 72
155 f165adc0 Nikos Skalkotos
156 dcf9274b Vangelis Koukis
        self.out.output("Enabled system preparation operations:")
157 f165adc0 Nikos Skalkotos
        if len(enabled) == 0:
158 e77e66a9 Nikos Skalkotos
            self.out.output("(none)")
159 f165adc0 Nikos Skalkotos
        else:
160 f165adc0 Nikos Skalkotos
            for sysprep in enabled:
161 f165adc0 Nikos Skalkotos
                name = sysprep.__name__.replace('_', '-')
162 4a2fd05c Nikos Skalkotos
                descr = wrapper.fill(textwrap.dedent(sysprep.__doc__))
163 e77e66a9 Nikos Skalkotos
                self.out.output('    %s:\n%s\n' % (name, descr))
164 f165adc0 Nikos Skalkotos
165 dcf9274b Vangelis Koukis
        self.out.output("Disabled system preparation operations:")
166 f165adc0 Nikos Skalkotos
        if len(disabled) == 0:
167 e77e66a9 Nikos Skalkotos
            self.out.output("(none)")
168 f165adc0 Nikos Skalkotos
        else:
169 f165adc0 Nikos Skalkotos
            for sysprep in disabled:
170 f165adc0 Nikos Skalkotos
                name = sysprep.__name__.replace('_', '-')
171 4a2fd05c Nikos Skalkotos
                descr = wrapper.fill(textwrap.dedent(sysprep.__doc__))
172 e77e66a9 Nikos Skalkotos
                self.out.output('    %s:\n%s\n' % (name, descr))
173 76d4a1c9 Nikos Skalkotos
174 0d5a999d Nikos Skalkotos
    @add_prefix
175 8c574358 Nikos Skalkotos
    def ls(self, directory):
176 3b2f6619 Nikos Skalkotos
        """List the name of all files under a directory"""
177 8c574358 Nikos Skalkotos
        return self.g.ls(directory)
178 0d5a999d Nikos Skalkotos
179 0d5a999d Nikos Skalkotos
    @add_prefix
180 8c574358 Nikos Skalkotos
    def find(self, directory):
181 3b2f6619 Nikos Skalkotos
        """List the name of all files recursively under a directory"""
182 8c574358 Nikos Skalkotos
        return self.g.find(directory)
183 8c574358 Nikos Skalkotos
184 8c574358 Nikos Skalkotos
    def foreach_file(self, directory, action, **kargs):
185 3b2f6619 Nikos Skalkotos
        """Perform an action recursively on all files under a directory.
186 8c574358 Nikos Skalkotos

187 3b2f6619 Nikos Skalkotos
        The following options are allowed:
188 3b2f6619 Nikos Skalkotos

189 3b2f6619 Nikos Skalkotos
        * maxdepth: If defined the action will not be performed on
190 3b2f6619 Nikos Skalkotos
          files that are below this level of directories under the
191 3b2f6619 Nikos Skalkotos
          directory parameter.
192 3b2f6619 Nikos Skalkotos

193 3b2f6619 Nikos Skalkotos
        * ftype: The action will only be performed on files of this
194 3b2f6619 Nikos Skalkotos
          type. For a list of all allowed filetypes, see here:
195 3b2f6619 Nikos Skalkotos
          http://libguestfs.org/guestfs.3.html#guestfs_readdir
196 3b2f6619 Nikos Skalkotos

197 3b2f6619 Nikos Skalkotos
        * exclude: Exclude all files that follow this pattern.
198 3b2f6619 Nikos Skalkotos
        """
199 8c574358 Nikos Skalkotos
        maxdepth = None if 'maxdepth' not in kargs else kargs['maxdepth']
200 8c574358 Nikos Skalkotos
        if maxdepth == 0:
201 8c574358 Nikos Skalkotos
            return
202 8c574358 Nikos Skalkotos
203 8c574358 Nikos Skalkotos
        # maxdepth -= 1
204 8c574358 Nikos Skalkotos
        maxdepth = None if maxdepth is None else maxdepth - 1
205 8c574358 Nikos Skalkotos
        kargs['maxdepth'] = maxdepth
206 8c574358 Nikos Skalkotos
207 8c574358 Nikos Skalkotos
        exclude = None if 'exclude' not in kargs else kargs['exclude']
208 8c574358 Nikos Skalkotos
        ftype = None if 'ftype' not in kargs else kargs['ftype']
209 8c574358 Nikos Skalkotos
        has_ftype = lambda x, y: y is None and True or x['ftyp'] == y
210 8c574358 Nikos Skalkotos
211 8c574358 Nikos Skalkotos
        for f in self.g.readdir(directory):
212 8c574358 Nikos Skalkotos
            if f['name'] in ('.', '..'):
213 8c574358 Nikos Skalkotos
                continue
214 8c574358 Nikos Skalkotos
215 8c574358 Nikos Skalkotos
            full_path = "%s/%s" % (directory, f['name'])
216 8c574358 Nikos Skalkotos
217 8c574358 Nikos Skalkotos
            if exclude and re.match(exclude, full_path):
218 8c574358 Nikos Skalkotos
                continue
219 8c574358 Nikos Skalkotos
220 8c574358 Nikos Skalkotos
            if has_ftype(f, 'd'):
221 8c574358 Nikos Skalkotos
                self.foreach_file(full_path, action, **kargs)
222 8c574358 Nikos Skalkotos
223 8c574358 Nikos Skalkotos
            if has_ftype(f, ftype):
224 8c574358 Nikos Skalkotos
                action(full_path)
225 0d5a999d Nikos Skalkotos
226 f165adc0 Nikos Skalkotos
    def do_sysprep(self):
227 9cbb5794 Nikos Skalkotos
        """Prepere system for image creation."""
228 22a6d232 Nikos Skalkotos
229 71b0ab28 Nikos Skalkotos
        try:
230 71b0ab28 Nikos Skalkotos
            if not self.mount(readonly=False):
231 71b0ab28 Nikos Skalkotos
                raise FatalError("Unable to mount the media read-write")
232 71b0ab28 Nikos Skalkotos
233 71b0ab28 Nikos Skalkotos
            self.out.output('Preparing system for image creation:')
234 71b0ab28 Nikos Skalkotos
235 71b0ab28 Nikos Skalkotos
            tasks = self.list_syspreps()
236 71b0ab28 Nikos Skalkotos
            enabled = filter(lambda x: x.enabled, tasks)
237 71b0ab28 Nikos Skalkotos
238 71b0ab28 Nikos Skalkotos
            size = len(enabled)
239 71b0ab28 Nikos Skalkotos
            cnt = 0
240 71b0ab28 Nikos Skalkotos
            for task in enabled:
241 71b0ab28 Nikos Skalkotos
                cnt += 1
242 71b0ab28 Nikos Skalkotos
                self.out.output(('(%d/%d)' % (cnt, size)).ljust(7), False)
243 71b0ab28 Nikos Skalkotos
                task()
244 71b0ab28 Nikos Skalkotos
                setattr(task.im_func, 'executed', True)
245 71b0ab28 Nikos Skalkotos
            self.out.output()
246 71b0ab28 Nikos Skalkotos
        finally:
247 71b0ab28 Nikos Skalkotos
            self.umount()
248 71b0ab28 Nikos Skalkotos
249 71b0ab28 Nikos Skalkotos
    def _do_mount(self, readonly):
250 71b0ab28 Nikos Skalkotos
        try:
251 71b0ab28 Nikos Skalkotos
            self.g.mount_options('ro' if readonly else 'rw', self.root, '/')
252 71b0ab28 Nikos Skalkotos
        except RuntimeError as msg:
253 71b0ab28 Nikos Skalkotos
            self.out.warn("unable to mount the root partition: %s" % msg)
254 71b0ab28 Nikos Skalkotos
            return False
255 71b0ab28 Nikos Skalkotos
256 71b0ab28 Nikos Skalkotos
        return True
257 71b0ab28 Nikos Skalkotos
258 71b0ab28 Nikos Skalkotos
    def mount(self, readonly=False):
259 71b0ab28 Nikos Skalkotos
        """Mount image."""
260 71b0ab28 Nikos Skalkotos
261 71b0ab28 Nikos Skalkotos
        if getattr(self, "mounted", False):
262 71b0ab28 Nikos Skalkotos
            return True
263 71b0ab28 Nikos Skalkotos
264 71b0ab28 Nikos Skalkotos
        mount_type = 'read-only' if readonly else 'read-write'
265 71b0ab28 Nikos Skalkotos
        self.out.output("Mount the media %s ..." % mount_type, False)
266 71b0ab28 Nikos Skalkotos
267 71b0ab28 Nikos Skalkotos
        if not self._do_mount(readonly):
268 71b0ab28 Nikos Skalkotos
            return False
269 3f70f242 Nikos Skalkotos
270 71b0ab28 Nikos Skalkotos
        self.mounted = True
271 71b0ab28 Nikos Skalkotos
        self.out.success('done')
272 71b0ab28 Nikos Skalkotos
        return True
273 f8cccab5 Nikos Skalkotos
274 71b0ab28 Nikos Skalkotos
    def umount(self):
275 71b0ab28 Nikos Skalkotos
        """Umount all mounted filesystems."""
276 71b0ab28 Nikos Skalkotos
        self.g.umount_all()
277 71b0ab28 Nikos Skalkotos
        self.mounted = False
278 9cbb5794 Nikos Skalkotos
279 aa2062ba Nikos Skalkotos
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :