~curtin-dev/curtin/trunk

« back to all changes in this revision

Viewing changes to curtin/deps/__init__.py

  • Committer: Scott Moser
  • Date: 2017-12-20 17:33:03 UTC
  • Revision ID: smoser@ubuntu.com-20171220173303-29gha5qb8wpqrd40
README: Mention move of revision control to git.

curtin development has moved its revision control to git.
It is available at
  https://code.launchpad.net/curtin

Clone with
  git clone https://git.launchpad.net/curtin
or
  git clone git+ssh://git.launchpad.net/curtin

For more information see
  http://curtin.readthedocs.io/en/latest/topics/development.html

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#   Copyright (C) 2015 Canonical Ltd.
2
 
#
3
 
#   Author: Scott Moser <scott.moser@canonical.com>
4
 
#
5
 
#   Curtin is free software: you can redistribute it and/or modify it under
6
 
#   the terms of the GNU Affero General Public License as published by the
7
 
#   Free Software Foundation, either version 3 of the License, or (at your
8
 
#   option) any later version.
9
 
#
10
 
#   Curtin is distributed in the hope that it will be useful, but WITHOUT ANY
11
 
#   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12
 
#   FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for
13
 
#   more details.
14
 
#
15
 
#   You should have received a copy of the GNU Affero General Public License
16
 
#   along with Curtin.  If not, see <http://www.gnu.org/licenses/>.
17
 
import os
18
 
import sys
19
 
 
20
 
from curtin.util import (
21
 
    ProcessExecutionError,
22
 
    get_architecture,
23
 
    install_packages,
24
 
    is_uefi_bootable,
25
 
    lsb_release,
26
 
    which,
27
 
)
28
 
 
29
 
REQUIRED_IMPORTS = [
30
 
    # import string to execute, python2 package, python3 package
31
 
    ('import yaml', 'python-yaml', 'python3-yaml'),
32
 
]
33
 
 
34
 
REQUIRED_EXECUTABLES = [
35
 
    # executable in PATH, package
36
 
    ('file', 'file'),
37
 
    ('lvcreate', 'lvm2'),
38
 
    ('mdadm', 'mdadm'),
39
 
    ('mkfs.vfat', 'dosfstools'),
40
 
    ('mkfs.btrfs', 'btrfs-tools'),
41
 
    ('mkfs.ext4', 'e2fsprogs'),
42
 
    ('mkfs.xfs', 'xfsprogs'),
43
 
    ('partprobe', 'parted'),
44
 
    ('sgdisk', 'gdisk'),
45
 
    ('udevadm', 'udev'),
46
 
    ('make-bcache', 'bcache-tools'),
47
 
    ('iscsiadm', 'open-iscsi'),
48
 
]
49
 
 
50
 
if lsb_release()['codename'] == "precise":
51
 
    REQUIRED_IMPORTS.append(
52
 
        ('import oauth.oauth', 'python-oauth', None),)
53
 
else:
54
 
    REQUIRED_IMPORTS.append(
55
 
        ('import oauthlib.oauth1', 'python-oauthlib', 'python3-oauthlib'),)
56
 
 
57
 
if not is_uefi_bootable() and 'arm' in get_architecture():
58
 
    REQUIRED_EXECUTABLES.append(('flash-kernel', 'flash-kernel'))
59
 
 
60
 
 
61
 
class MissingDeps(Exception):
62
 
    def __init__(self, message, deps):
63
 
        self.message = message
64
 
        if isinstance(deps, str) or deps is None:
65
 
            deps = [deps]
66
 
        self.deps = [d for d in deps if d is not None]
67
 
        self.fatal = None in deps
68
 
 
69
 
    def __str__(self):
70
 
        if self.fatal:
71
 
            if not len(self.deps):
72
 
                return self.message + " Unresolvable."
73
 
            return (self.message +
74
 
                    " Unresolvable.  Partially resolvable with packages: %s" %
75
 
                    ' '.join(self.deps))
76
 
        else:
77
 
            return self.message + " Install packages: %s" % ' '.join(self.deps)
78
 
 
79
 
 
80
 
def check_import(imports, py2pkgs, py3pkgs, message=None):
81
 
    import_group = imports
82
 
    if isinstance(import_group, str):
83
 
        import_group = [import_group]
84
 
 
85
 
    for istr in import_group:
86
 
        try:
87
 
            exec(istr)
88
 
            return
89
 
        except ImportError:
90
 
            pass
91
 
 
92
 
    if not message:
93
 
        if isinstance(imports, str):
94
 
            message = "Failed '%s'." % imports
95
 
        else:
96
 
            message = "Unable to do any of %s." % import_group
97
 
 
98
 
    if sys.version_info[0] == 2:
99
 
        pkgs = py2pkgs
100
 
    else:
101
 
        pkgs = py3pkgs
102
 
 
103
 
    raise MissingDeps(message, pkgs)
104
 
 
105
 
 
106
 
def check_executable(cmdname, pkg):
107
 
    if not which(cmdname):
108
 
        raise MissingDeps("Missing program '%s'." % cmdname, pkg)
109
 
 
110
 
 
111
 
def check_executables(executables=None):
112
 
    if executables is None:
113
 
        executables = REQUIRED_EXECUTABLES
114
 
    mdeps = []
115
 
    for exe, pkg in executables:
116
 
        try:
117
 
            check_executable(exe, pkg)
118
 
        except MissingDeps as e:
119
 
            mdeps.append(e)
120
 
    return mdeps
121
 
 
122
 
 
123
 
def check_imports(imports=None):
124
 
    if imports is None:
125
 
        imports = REQUIRED_IMPORTS
126
 
 
127
 
    mdeps = []
128
 
    for import_str, py2pkg, py3pkg in imports:
129
 
        try:
130
 
            check_import(import_str, py2pkg, py3pkg)
131
 
        except MissingDeps as e:
132
 
            mdeps.append(e)
133
 
    return mdeps
134
 
 
135
 
 
136
 
def find_missing_deps():
137
 
    return check_executables() + check_imports()
138
 
 
139
 
 
140
 
def install_deps(verbosity=False, dry_run=False, allow_daemons=True):
141
 
    errors = find_missing_deps()
142
 
    if len(errors) == 0:
143
 
        if verbosity:
144
 
            sys.stderr.write("No missing dependencies\n")
145
 
        return 0
146
 
 
147
 
    missing_pkgs = []
148
 
    for e in errors:
149
 
        missing_pkgs += e.deps
150
 
 
151
 
    deps_string = ' '.join(sorted(missing_pkgs))
152
 
 
153
 
    if dry_run:
154
 
        sys.stderr.write("Missing dependencies: %s\n" % deps_string)
155
 
        return 0
156
 
 
157
 
    if os.geteuid() != 0:
158
 
        sys.stderr.write("Missing dependencies: %s\n" % deps_string)
159
 
        sys.stderr.write("Package installation is not possible as non-root.\n")
160
 
        return 2
161
 
 
162
 
    if verbosity:
163
 
        sys.stderr.write("Installing %s\n" % deps_string)
164
 
 
165
 
    ret = 0
166
 
    try:
167
 
        install_packages(missing_pkgs, allow_daemons=allow_daemons,
168
 
                         aptopts=["--no-install-recommends"])
169
 
    except ProcessExecutionError as e:
170
 
        sys.stderr.write("%s\n" % e)
171
 
        ret = e.exit_code
172
 
 
173
 
    return ret
174
 
 
175
 
 
176
 
# vi: ts=4 expandtab syntax=python