diff --git a/base/bootstrapinfo.py b/base/bootstrapinfo.py index 9dfe5c4..bcaf881 100644 --- a/base/bootstrapinfo.py +++ b/base/bootstrapinfo.py @@ -55,6 +55,6 @@ class BootstrapInformation(object): self.include_packages = set() self.exclude_packages = set() - self.host_dependencies = set() + self.host_dependencies = {} self.initd = {'install': {}, 'disable': []} diff --git a/common/task_sets.py b/common/task_sets.py index d0afec4..558ebb8 100644 --- a/common/task_sets.py +++ b/common/task_sets.py @@ -12,8 +12,8 @@ from common.tasks import security from common.tasks import locale base_set = [workspace.CreateWorkspace, - host.HostDependencies, - host.CheckHostDependencies, + host.AddExternalCommands, + host.CheckExternalCommands, bootstrap.Bootstrap, workspace.DeleteWorkspace, ] diff --git a/common/tasks/host.py b/common/tasks/host.py index 7bf79dd..fbedaf3 100644 --- a/common/tasks/host.py +++ b/common/tasks/host.py @@ -3,47 +3,49 @@ from common import phases from common.exceptions import TaskError -class HostDependencies(Task): - description = 'Determining required host dependencies' +class AddExternalCommands(Task): + description = 'Determining which external commands are required' phase = phases.preparation @classmethod def run(cls, info): - info.host_dependencies.add('debootstrap') + info.host_dependencies['debootstrap'] = 'debootstrap' from common.fs.loopbackvolume import LoopbackVolume if isinstance(info.volume, LoopbackVolume): - info.host_dependencies.add('qemu-utils') + info.host_dependencies['qemu-img'] = 'qemu-utils' + info.host_dependencies['losetup'] = 'mount' + from common.fs.qemuvolume import QEMUVolume + if isinstance(info.volume, QEMUVolume): + info.host_dependencies['losetup'] = 'qemu-nbd' if 'xfs' in (p.filesystem for p in info.volume.partition_map.partitions): - info.host_dependencies.add('xfsprogs') + info.host_dependencies['mkfs.xfs'] = 'xfsprogs' from base.fs.partitionmaps.none import NoPartitions if not isinstance(info.volume.partition_map, NoPartitions): - info.host_dependencies.update(['parted', 'kpartx']) + info.host_dependencies['parted'] = 'parted' + info.host_dependencies['kpartx'] = 'kpartx' -class CheckHostDependencies(Task): - description = 'Checking installed host packages' +class CheckExternalCommands(Task): + description = 'Checking availability of external commands' phase = phases.preparation - predecessors = [HostDependencies] + predecessors = [AddExternalCommands] @classmethod def run(cls, info): from common.tools import log_check_call from subprocess import CalledProcessError missing_packages = [] - for package in info.host_dependencies: + for command, package in info.host_dependencies.items(): try: - import os.path - if os.path.isfile('/usr/bin/dpkg-query'): - log_check_call(['/usr/bin/dpkg-query', '-s', package]) + log_check_call(['type ' + command], shell=True) except CalledProcessError: - missing_packages.append(package) + msg = ('The command `{command}\' is not available, ' + 'it is located in the package `{package}\'.' + .format(command=command, package=package)) + missing_packages.append(msg) if len(missing_packages) > 0: - pkgs = '\', `'.join(missing_packages) - if len(missing_packages) > 1: - msg = "The packages `{packages}\' are not installed".format(packages=pkgs) - else: - msg = "The package `{packages}\' is not installed".format(packages=pkgs) + msg = '\n'.join(missing_packages) raise TaskError(msg) diff --git a/common/tools.py b/common/tools.py index dc56637..6b39db5 100644 --- a/common/tools.py +++ b/common/tools.py @@ -1,14 +1,14 @@ -def log_check_call(command, stdin=None, env=None): - status, stdout, stderr = log_call(command, stdin, env) +def log_check_call(command, stdin=None, env=None, shell=False): + status, stdout, stderr = log_call(command, stdin, env, shell) if status != 0: from subprocess import CalledProcessError raise CalledProcessError(status, ' '.join(command), '\n'.join(stderr)) return stdout -def log_call(command, stdin=None, env=None): +def log_call(command, stdin=None, env=None, shell=False): import subprocess import select @@ -22,6 +22,7 @@ def log_call(command, stdin=None, env=None): popen_args = {'args': command, 'env': env, + 'shell': shell, 'stdin': subprocess.PIPE, 'stdout': subprocess.PIPE, 'stderr': subprocess.PIPE, } diff --git a/providers/ec2/__init__.py b/providers/ec2/__init__.py index 5038e8d..eee1866 100644 --- a/providers/ec2/__init__.py +++ b/providers/ec2/__init__.py @@ -61,7 +61,7 @@ def resolve_tasks(taskset, manifest): if manifest.volume['partitions']['type'] != 'none': taskset.update(common.task_sets.partitioning_set) - taskset.update([tasks.host.HostDependencies, + taskset.update([tasks.host.AddExternalCommands, tasks.packages.DefaultPackages, tasks.connection.GetCredentials, tasks.host.GetInfo, diff --git a/providers/ec2/tasks/host.py b/providers/ec2/tasks/host.py index 661f01f..355e576 100644 --- a/providers/ec2/tasks/host.py +++ b/providers/ec2/tasks/host.py @@ -3,15 +3,16 @@ from common import phases from common.tasks import host -class HostDependencies(Task): - description = 'Adding required host packages for EC2 bootstrapping' +class AddExternalCommands(Task): + description = 'Determining required external commands for EC2 bootstrapping' phase = phases.preparation - successors = [host.CheckHostDependencies] + successors = [host.CheckExternalCommands] @classmethod def run(cls, info): if info.manifest.volume['backing'] == 's3': - info.host_dependencies.add('euca2ools') + info.host_dependencies['euca-bundle-image'] = 'euca2ools' + info.host_dependencies['euca-upload-bundle'] = 'euca2ools' class GetInfo(Task):