bootstrap-vz/bootstrapvz/plugins/vagrant/tasks.py
Anders Ingemann f62c8ade99 Convert indentation from tabs to spaces (4)
Up until now I didn't see the point of using spaces for indentation.
However, the previous commit (a18bec3) was quite eye opening.
Given that python is an indentation aware language, the amount of
mistakes that went unnoticed because tabs and spaces were used
at the same time (tabs for indentation and spaces for alignment)
were unacceptable.

E101,W191 have been re-enable in the tox flake8 checker and
the documentation has been modified accordingly.

The following files have been left as-is:
* bootstrapvz/common/assets/extlinux/extlinux.conf
* bootstrapvz/common/assets/init.d/expand-root
* bootstrapvz/common/assets/init.d/generate-ssh-hostkeys
* bootstrapvz/common/assets/init.d/squeeze/generate-ssh-hostkeys
* bootstrapvz/plugins/docker_daemon/assets/init.d/docker
* bootstrapvz/providers/ec2/assets/bin/growpart
* bootstrapvz/providers/ec2/assets/grub.d/40_custom
* bootstrapvz/providers/ec2/assets/init.d/ec2-get-credentials
* bootstrapvz/providers/ec2/assets/init.d/ec2-run-user-data
* docs/_static/taskoverview.coffee
* docs/_static/taskoverview.less
* tests/unit/subprocess.sh
2016-06-04 11:38:16 +02:00

232 lines
9.5 KiB
Python

from bootstrapvz.base import Task
from bootstrapvz.common import phases
from bootstrapvz.common.tasks import workspace
import os
import shutil
assets = os.path.normpath(os.path.join(os.path.dirname(__file__), 'assets'))
class CheckBoxPath(Task):
description = 'Checking if the vagrant box file already exists'
phase = phases.preparation
@classmethod
def run(cls, info):
box_basename = info.manifest.name.format(**info.manifest_vars)
box_name = box_basename + '.box'
box_path = os.path.join(info.manifest.bootstrapper['workspace'], box_name)
if os.path.exists(box_path):
from bootstrapvz.common.exceptions import TaskError
msg = 'The vagrant box `{name}\' already exists at `{path}\''.format(name=box_name, path=box_path)
raise TaskError(msg)
info._vagrant['box_name'] = box_name
info._vagrant['box_path'] = box_path
class CreateVagrantBoxDir(Task):
description = 'Creating directory for the vagrant box'
phase = phases.preparation
predecessors = [workspace.CreateWorkspace, CheckBoxPath]
@classmethod
def run(cls, info):
info._vagrant['folder'] = os.path.join(info.workspace, 'vagrant')
os.mkdir(info._vagrant['folder'])
class AddPackages(Task):
description = 'Add packages that vagrant depends on'
phase = phases.preparation
@classmethod
def run(cls, info):
info.packages.add('openssh-server')
info.packages.add('sudo')
info.packages.add('nfs-client')
class CreateVagrantUser(Task):
description = 'Creating the vagrant user'
phase = phases.system_modification
@classmethod
def run(cls, info):
from bootstrapvz.common.tools import log_check_call
log_check_call(['chroot', info.root,
'useradd',
'--create-home', '--shell', '/bin/bash',
'vagrant'])
class PasswordlessSudo(Task):
description = 'Allowing the vagrant user to use sudo without a password'
phase = phases.system_modification
@classmethod
def run(cls, info):
sudo_vagrant_path = os.path.join(info.root, 'etc/sudoers.d/vagrant')
with open(sudo_vagrant_path, 'w') as sudo_vagrant:
sudo_vagrant.write('vagrant ALL=(ALL) NOPASSWD:ALL')
import stat
ug_read_only = (stat.S_IRUSR | stat.S_IRGRP)
os.chmod(sudo_vagrant_path, ug_read_only)
class AddInsecurePublicKey(Task):
description = 'Adding vagrant insecure public key'
phase = phases.system_modification
predecessors = [CreateVagrantUser]
@classmethod
def run(cls, info):
ssh_dir = os.path.join(info.root, 'home/vagrant/.ssh')
os.mkdir(ssh_dir)
authorized_keys_source_path = os.path.join(assets, 'authorized_keys')
with open(authorized_keys_source_path, 'r') as authorized_keys_source:
insecure_public_key = authorized_keys_source.read()
authorized_keys_path = os.path.join(ssh_dir, 'authorized_keys')
with open(authorized_keys_path, 'a') as authorized_keys:
authorized_keys.write(insecure_public_key)
import stat
os.chmod(ssh_dir, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
os.chmod(authorized_keys_path, stat.S_IRUSR | stat.S_IWUSR)
# We can't do this directly with python, since getpwnam gets its info from the host
from bootstrapvz.common.tools import log_check_call
log_check_call(['chroot', info.root,
'chown', 'vagrant:vagrant',
'/home/vagrant/.ssh', '/home/vagrant/.ssh/authorized_keys'])
class SetRootPassword(Task):
description = 'Setting the root password to `vagrant\''
phase = phases.system_modification
@classmethod
def run(cls, info):
from bootstrapvz.common.tools import log_check_call
log_check_call(['chroot', info.root, 'chpasswd'], 'root:vagrant')
class PackageBox(Task):
description = 'Packaging the volume as a vagrant box'
phase = phases.image_registration
@classmethod
def run(cls, info):
vagrantfile_source = os.path.join(assets, 'Vagrantfile')
vagrantfile = os.path.join(info._vagrant['folder'], 'Vagrantfile')
shutil.copy(vagrantfile_source, vagrantfile)
import random
mac_address = '080027{mac:06X}'.format(mac=random.randrange(16 ** 6))
from bootstrapvz.common.tools import sed_i
sed_i(vagrantfile, '\\[MAC_ADDRESS\\]', mac_address)
metadata_source = os.path.join(assets, 'metadata.json')
metadata = os.path.join(info._vagrant['folder'], 'metadata.json')
shutil.copy(metadata_source, metadata)
from bootstrapvz.common.tools import log_check_call
disk_name = 'box-disk1.' + info.volume.extension
disk_link = os.path.join(info._vagrant['folder'], disk_name)
log_check_call(['ln', '-s', info.volume.image_path, disk_link])
ovf_path = os.path.join(info._vagrant['folder'], 'box.ovf')
cls.write_ovf(info, ovf_path, mac_address, disk_name)
box_files = os.listdir(info._vagrant['folder'])
log_check_call(['tar', '--create', '--gzip', '--dereference',
'--file', info._vagrant['box_path'],
'--directory', info._vagrant['folder']] + box_files
)
import logging
logging.getLogger(__name__).info('The vagrant box has been placed at ' + info._vagrant['box_path'])
@classmethod
def write_ovf(cls, info, destination, mac_address, disk_name):
namespaces = {'ovf': 'http://schemas.dmtf.org/ovf/envelope/1',
'rasd': 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData',
'vssd': 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData',
'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
'vbox': 'http://www.virtualbox.org/ovf/machine',
}
def attr(element, name, value=None):
for prefix, ns in namespaces.iteritems():
name = name.replace(prefix + ':', '{' + ns + '}')
if value is None:
return element.attrib[name]
else:
element.attrib[name] = str(value)
template_path = os.path.join(assets, 'box.ovf')
import xml.etree.ElementTree as ET
template = ET.parse(template_path)
root = template.getroot()
[disk_ref] = root.findall('./ovf:References/ovf:File', namespaces)
attr(disk_ref, 'ovf:href', disk_name)
# List of OVF disk format URIs
# Snatched from VBox source (src/VBox/Main/src-server/ApplianceImpl.cpp:47)
# ISOURI = "http://www.ecma-international.org/publications/standards/Ecma-119.htm"
# VMDKStreamURI = "http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized"
# VMDKSparseURI = "http://www.vmware.com/specifications/vmdk.html#sparse"
# VMDKCompressedURI = "http://www.vmware.com/specifications/vmdk.html#compressed"
# VMDKCompressedURI2 = "http://www.vmware.com/interfaces/specifications/vmdk.html#compressed"
# VHDURI = "http://go.microsoft.com/fwlink/?LinkId=137171"
volume_uuid = info.volume.get_uuid()
[disk] = root.findall('./ovf:DiskSection/ovf:Disk', namespaces)
attr(disk, 'ovf:capacity', info.volume.size.bytes.get_qty_in('B'))
attr(disk, 'ovf:format', info.volume.ovf_uri)
attr(disk, 'vbox:uuid', volume_uuid)
[system] = root.findall('./ovf:VirtualSystem', namespaces)
attr(system, 'ovf:id', info._vagrant['box_name'])
# Set the operating system
[os_section] = system.findall('./ovf:OperatingSystemSection', namespaces)
os_info = {'i386': {'id': 96, 'name': 'Debian'},
'amd64': {'id': 96, 'name': 'Debian_64'}
}.get(info.manifest.system['architecture'])
attr(os_section, 'ovf:id', os_info['id'])
[os_desc] = os_section.findall('./ovf:Description', namespaces)
os_desc.text = os_info['name']
[os_type] = os_section.findall('./vbox:OSType', namespaces)
os_type.text = os_info['name']
[sysid] = system.findall('./ovf:VirtualHardwareSection/ovf:System/'
'vssd:VirtualSystemIdentifier', namespaces)
sysid.text = info._vagrant['box_name']
[machine] = system.findall('./vbox:Machine', namespaces)
import uuid
attr(machine, 'ovf:uuid', uuid.uuid4())
attr(machine, 'ovf:name', info._vagrant['box_name'])
from datetime import datetime
attr(machine, 'ovf:lastStateChange', datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ'))
[nic] = machine.findall('./ovf:Hardware/ovf:Network/ovf:Adapter', namespaces)
attr(machine, 'ovf:MACAddress', mac_address)
[device_img] = machine.findall('./ovf:StorageControllers'
'/ovf:StorageController[@name="SATA Controller"]'
'/ovf:AttachedDevice/ovf:Image', namespaces)
attr(device_img, 'uuid', '{' + str(volume_uuid) + '}')
template.write(destination, xml_declaration=True) # , default_namespace=namespaces['ovf']
class RemoveVagrantBoxDir(Task):
description = 'Removing the vagrant box directory'
phase = phases.cleaning
successors = [workspace.DeleteWorkspace]
@classmethod
def run(cls, info):
shutil.rmtree(info._vagrant['folder'])
del info._vagrant['folder']