1
# Copyright (C) 2015 Canonical Ltd.
3
# Author: Scott Moser <scott.moser@canonical.com>
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.
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
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/>.
20
from curtin.util import (
21
ProcessExecutionError,
30
# import string to execute, python2 package, python3 package
31
('import yaml', 'python-yaml', 'python3-yaml'),
34
REQUIRED_EXECUTABLES = [
35
# executable in PATH, package
39
('mkfs.vfat', 'dosfstools'),
40
('mkfs.btrfs', 'btrfs-tools'),
41
('mkfs.ext4', 'e2fsprogs'),
42
('mkfs.xfs', 'xfsprogs'),
43
('partprobe', 'parted'),
46
('make-bcache', 'bcache-tools'),
47
('iscsiadm', 'open-iscsi'),
50
if lsb_release()['codename'] == "precise":
51
REQUIRED_IMPORTS.append(
52
('import oauth.oauth', 'python-oauth', None),)
54
REQUIRED_IMPORTS.append(
55
('import oauthlib.oauth1', 'python-oauthlib', 'python3-oauthlib'),)
57
if not is_uefi_bootable() and 'arm' in get_architecture():
58
REQUIRED_EXECUTABLES.append(('flash-kernel', 'flash-kernel'))
61
class MissingDeps(Exception):
62
def __init__(self, message, deps):
63
self.message = message
64
if isinstance(deps, str) or deps is None:
66
self.deps = [d for d in deps if d is not None]
67
self.fatal = None in deps
71
if not len(self.deps):
72
return self.message + " Unresolvable."
73
return (self.message +
74
" Unresolvable. Partially resolvable with packages: %s" %
77
return self.message + " Install packages: %s" % ' '.join(self.deps)
80
def check_import(imports, py2pkgs, py3pkgs, message=None):
81
import_group = imports
82
if isinstance(import_group, str):
83
import_group = [import_group]
85
for istr in import_group:
93
if isinstance(imports, str):
94
message = "Failed '%s'." % imports
96
message = "Unable to do any of %s." % import_group
98
if sys.version_info[0] == 2:
103
raise MissingDeps(message, pkgs)
106
def check_executable(cmdname, pkg):
107
if not which(cmdname):
108
raise MissingDeps("Missing program '%s'." % cmdname, pkg)
111
def check_executables(executables=None):
112
if executables is None:
113
executables = REQUIRED_EXECUTABLES
115
for exe, pkg in executables:
117
check_executable(exe, pkg)
118
except MissingDeps as e:
123
def check_imports(imports=None):
125
imports = REQUIRED_IMPORTS
128
for import_str, py2pkg, py3pkg in imports:
130
check_import(import_str, py2pkg, py3pkg)
131
except MissingDeps as e:
136
def find_missing_deps():
137
return check_executables() + check_imports()
140
def install_deps(verbosity=False, dry_run=False, allow_daemons=True):
141
errors = find_missing_deps()
144
sys.stderr.write("No missing dependencies\n")
149
missing_pkgs += e.deps
151
deps_string = ' '.join(sorted(missing_pkgs))
154
sys.stderr.write("Missing dependencies: %s\n" % deps_string)
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")
163
sys.stderr.write("Installing %s\n" % deps_string)
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)
176
# vi: ts=4 expandtab syntax=python