mirror of
https://github.com/kevingruesser/bootstrap-vz.git
synced 2025-10-10 17:19:51 +00:00
Abstract bootstrapping, instance creation, booting etc..
This all now runs using a simple with: statement
This commit is contained in:
parent
a11e466611
commit
9c6af89e78
13 changed files with 158 additions and 100 deletions
|
@ -4,7 +4,3 @@ class Image(object):
|
||||||
|
|
||||||
def __init__(self, manifest):
|
def __init__(self, manifest):
|
||||||
self.manifest = manifest
|
self.manifest = manifest
|
||||||
|
|
||||||
def destroy(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
|
@ -9,10 +9,26 @@ class VirtualBoxImage(Image):
|
||||||
super(VirtualBoxImage, self).__init__(manifest)
|
super(VirtualBoxImage, self).__init__(manifest)
|
||||||
self.image_path = image_path
|
self.image_path = image_path
|
||||||
self.vbox = vboxapi.VirtualBox()
|
self.vbox = vboxapi.VirtualBox()
|
||||||
|
|
||||||
|
def open(self):
|
||||||
self.medium = self.vbox.open_medium(self.image_path, # location
|
self.medium = self.vbox.open_medium(self.image_path, # location
|
||||||
vboxapi.library.DeviceType.hard_disk, # decive_type
|
vboxapi.library.DeviceType.hard_disk, # decive_type
|
||||||
vboxapi.library.AccessMode.read_only, # access_mode
|
vboxapi.library.AccessMode.read_only, # access_mode
|
||||||
False) # force_new_uuid
|
False) # force_new_uuid
|
||||||
|
|
||||||
def destroy(self):
|
def close(self):
|
||||||
self.medium.close()
|
self.medium.close()
|
||||||
|
|
||||||
|
def get_instance(self):
|
||||||
|
import hashlib
|
||||||
|
from ..instances.virtualbox import VirtualBoxInstance
|
||||||
|
image_hash = hashlib.sha1(self.image_path).hexdigest()
|
||||||
|
name = 'bootstrap-vz-{hash}'.format(hash=image_hash[:8])
|
||||||
|
return VirtualBoxInstance(name, self)
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
self.open()
|
||||||
|
return self.get_instance()
|
||||||
|
|
||||||
|
def __exit__(self, type, value, traceback):
|
||||||
|
self.close()
|
||||||
|
|
|
@ -68,6 +68,29 @@ class VirtualBoxInstance(Instance):
|
||||||
self.machine.unregister(vboxapi.library.CleanupMode.unregister_only)
|
self.machine.unregister(vboxapi.library.CleanupMode.unregister_only)
|
||||||
self.machine.remove(delete=True)
|
self.machine.remove(delete=True)
|
||||||
|
|
||||||
|
def up(self):
|
||||||
|
try:
|
||||||
|
self.create()
|
||||||
|
try:
|
||||||
|
self.boot()
|
||||||
|
except Exception as e:
|
||||||
|
self.shutdown()
|
||||||
|
raise e
|
||||||
|
except Exception as e:
|
||||||
|
self.destroy()
|
||||||
|
raise e
|
||||||
|
|
||||||
|
def down(self):
|
||||||
|
self.shutdown()
|
||||||
|
self.destroy()
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
self.up()
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, type, value, traceback):
|
||||||
|
self.down()
|
||||||
|
|
||||||
class Lock(object):
|
class Lock(object):
|
||||||
def __init__(self, machine, session):
|
def __init__(self, machine, session):
|
||||||
self.machine = machine
|
self.machine = machine
|
||||||
|
|
|
@ -18,3 +18,29 @@ import string
|
||||||
pool = string.ascii_uppercase + string.ascii_lowercase + string.digits
|
pool = string.ascii_uppercase + string.ascii_lowercase + string.digits
|
||||||
random_password = ''.join(random.choice(pool) for _ in range(16))
|
random_password = ''.join(random.choice(pool) for _ in range(16))
|
||||||
partials['root_password']['plugins']['root_password']['password'] = random_password
|
partials['root_password']['plugins']['root_password']['password'] = random_password
|
||||||
|
|
||||||
|
|
||||||
|
def merge_manifest_data(standard_partials=[], custom=[]):
|
||||||
|
import yaml
|
||||||
|
manifest_data = [partials[name] for name in standard_partials]
|
||||||
|
manifest_data.extend(yaml.load(data) for data in custom)
|
||||||
|
return merge_dicts(*manifest_data)
|
||||||
|
|
||||||
|
|
||||||
|
# Snatched from here: http://stackoverflow.com/a/7205107
|
||||||
|
def merge_dicts(*args):
|
||||||
|
def merge(a, b, path=None):
|
||||||
|
if path is None:
|
||||||
|
path = []
|
||||||
|
for key in b:
|
||||||
|
if key in a:
|
||||||
|
if isinstance(a[key], dict) and isinstance(b[key], dict):
|
||||||
|
merge(a[key], b[key], path + [str(key)])
|
||||||
|
elif a[key] == b[key]:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise Exception('Conflict at %s' % '.'.join(path + [str(key)]))
|
||||||
|
else:
|
||||||
|
a[key] = b[key]
|
||||||
|
return a
|
||||||
|
return reduce(merge, args, {})
|
||||||
|
|
3
tests/integration/manifests/extlinux.yml
Normal file
3
tests/integration/manifests/extlinux.yml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
---
|
||||||
|
system:
|
||||||
|
bootloader: extlinux
|
4
tests/integration/manifests/gpt.yml
Normal file
4
tests/integration/manifests/gpt.yml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
volume:
|
||||||
|
partitions:
|
||||||
|
type: gpt
|
3
tests/integration/manifests/grub.yml
Normal file
3
tests/integration/manifests/grub.yml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
---
|
||||||
|
system:
|
||||||
|
bootloader: grub
|
4
tests/integration/manifests/msdos.yml
Normal file
4
tests/integration/manifests/msdos.yml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
volume:
|
||||||
|
partitions:
|
||||||
|
type: msdos
|
6
tests/integration/manifests/single_partition.yml
Normal file
6
tests/integration/manifests/single_partition.yml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
volume:
|
||||||
|
partitions:
|
||||||
|
root:
|
||||||
|
filesystem: ext4
|
||||||
|
size: 1GiB
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
volume:
|
volume:
|
||||||
type: none
|
|
||||||
partitions:
|
partitions:
|
||||||
|
type: none
|
||||||
root:
|
root:
|
||||||
filesystem: ext4
|
filesystem: ext4
|
||||||
size: 1GiB
|
size: 1GiB
|
||||||
|
|
|
@ -4,25 +4,6 @@ from bootstrapvz.remote import register_deserialization_handlers
|
||||||
register_deserialization_handlers()
|
register_deserialization_handlers()
|
||||||
|
|
||||||
|
|
||||||
# Snatched from here: http://stackoverflow.com/a/7205107
|
|
||||||
def merge_dicts(*args):
|
|
||||||
def merge(a, b, path=None):
|
|
||||||
if path is None:
|
|
||||||
path = []
|
|
||||||
for key in b:
|
|
||||||
if key in a:
|
|
||||||
if isinstance(a[key], dict) and isinstance(b[key], dict):
|
|
||||||
merge(a[key], b[key], path + [str(key)])
|
|
||||||
elif a[key] == b[key]:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
raise Exception('Conflict at %s' % '.'.join(path + [str(key)]))
|
|
||||||
else:
|
|
||||||
a[key] = b[key]
|
|
||||||
return a
|
|
||||||
return reduce(merge, args, {})
|
|
||||||
|
|
||||||
|
|
||||||
def waituntil(predicate, timeout=5, interval=0.05):
|
def waituntil(predicate, timeout=5, interval=0.05):
|
||||||
import time
|
import time
|
||||||
threshhold = time.time() + timeout
|
threshhold = time.time() + timeout
|
||||||
|
@ -71,18 +52,3 @@ def read_from_socket(socket_path, termination_string, timeout):
|
||||||
raise SocketReadTimeout(msg)
|
raise SocketReadTimeout(msg)
|
||||||
console.close()
|
console.close()
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
def bootstrap(manifest, build_server):
|
|
||||||
from bootstrapvz.remote.build_servers import LocalBuildServer
|
|
||||||
if isinstance(build_server, LocalBuildServer):
|
|
||||||
from bootstrapvz.base.main import run
|
|
||||||
bootstrap_info = run(manifest)
|
|
||||||
else:
|
|
||||||
from bootstrapvz.remote.main import run
|
|
||||||
bootstrap_info = run(manifest, build_server)
|
|
||||||
return bootstrap_info
|
|
||||||
|
|
||||||
|
|
||||||
def test(instance):
|
|
||||||
pass
|
|
||||||
|
|
57
tests/integration/tools/bootable_manifest.py
Normal file
57
tests/integration/tools/bootable_manifest.py
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
from bootstrapvz.remote.build_servers import LocalBuildServer
|
||||||
|
from ..images.virtualbox import VirtualBoxImage
|
||||||
|
|
||||||
|
|
||||||
|
class BootableManifest(object):
|
||||||
|
|
||||||
|
def __init__(self, manifest_data):
|
||||||
|
self.manifest_data = manifest_data
|
||||||
|
|
||||||
|
def pick_build_server(self, path='build-servers.yml'):
|
||||||
|
from bootstrapvz.common.tools import load_data
|
||||||
|
build_servers = load_data(path)
|
||||||
|
from bootstrapvz.remote.build_servers import pick_build_server
|
||||||
|
return pick_build_server(build_servers, self.manifest_data)
|
||||||
|
|
||||||
|
def get_manifest(self, build_server):
|
||||||
|
manifest_data = build_server.apply_build_settings(self.manifest_data)
|
||||||
|
from bootstrapvz.base.manifest import Manifest
|
||||||
|
return Manifest(data=manifest_data)
|
||||||
|
|
||||||
|
def bootstrap(self, manifest, build_server):
|
||||||
|
if isinstance(build_server, LocalBuildServer):
|
||||||
|
from bootstrapvz.base.main import run
|
||||||
|
bootstrap_info = run(manifest)
|
||||||
|
else:
|
||||||
|
from bootstrapvz.remote.main import run
|
||||||
|
bootstrap_info = run(manifest, build_server)
|
||||||
|
return bootstrap_info
|
||||||
|
|
||||||
|
def get_image(self, build_server, bootstrap_info, manifest):
|
||||||
|
if isinstance(build_server, LocalBuildServer):
|
||||||
|
image_path = bootstrap_info.volume.image_path
|
||||||
|
else:
|
||||||
|
import tempfile
|
||||||
|
handle, image_path = tempfile.mkstemp()
|
||||||
|
import os
|
||||||
|
os.close(handle)
|
||||||
|
build_server.download(bootstrap_info.volume.image_path, image_path)
|
||||||
|
build_server.delete(bootstrap_info.volume.image_path)
|
||||||
|
image_type = {'virtualbox': VirtualBoxImage}
|
||||||
|
return image_type.get(self.manifest_data['provider']['name'])(manifest, image_path)
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
self.build_server = self.pick_build_server()
|
||||||
|
self.manifest = self.get_manifest(self.build_server)
|
||||||
|
self.bootstrap_info = self.bootstrap(self.manifest, self.build_server)
|
||||||
|
self.image = self.get_image(self.build_server, self.bootstrap_info, self.manifest)
|
||||||
|
self.image.open()
|
||||||
|
self.instance = self.image.get_instance()
|
||||||
|
self.instance.up()
|
||||||
|
return self.instance
|
||||||
|
|
||||||
|
def __exit__(self, type, value, traceback):
|
||||||
|
if hasattr(self, 'instance'):
|
||||||
|
self.instance.down()
|
||||||
|
if hasattr(self, 'image'):
|
||||||
|
self.image.close()
|
|
@ -1,62 +1,16 @@
|
||||||
import tools
|
from manifests import merge_manifest_data
|
||||||
from manifests import partials
|
from tools.bootable_manifest import BootableManifest
|
||||||
from bootstrapvz.base.manifest import Manifest
|
|
||||||
from bootstrapvz.remote.build_servers import pick_build_server
|
partials = {'vbox': 'provider: {name: virtualbox}',
|
||||||
from . import build_servers
|
'vdi': 'volume: {backing: vdi}',
|
||||||
from images.virtualbox import VirtualBoxImage
|
'vmdk': 'volume: {backing: vmdk}',
|
||||||
from instances.virtualbox import VirtualBoxInstance
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_virtualbox_unpartitioned_extlinux():
|
def test_virtualbox_partitioned_extlinux():
|
||||||
import yaml
|
std_partials = ['base', 'stable64', 'extlinux', 'msdos', 'single_partition',
|
||||||
manifest_data = yaml.load("""
|
'root_password', 'apt_proxy']
|
||||||
provider:
|
custom_partials = [partials['vbox'], partials['vdi']]
|
||||||
name: virtualbox
|
manifest_data = merge_manifest_data(std_partials, custom_partials)
|
||||||
system:
|
with BootableManifest(manifest_data) as instance:
|
||||||
bootloader: extlinux
|
print(instance.console_output)
|
||||||
volume:
|
|
||||||
backing: vdi
|
|
||||||
partitions:
|
|
||||||
type: msdos
|
|
||||||
""")
|
|
||||||
manifest_data = tools.merge_dicts(partials['base'], partials['stable64'], partials['unpartitioned'],
|
|
||||||
partials['root_password'], partials['apt_proxy'],
|
|
||||||
manifest_data)
|
|
||||||
|
|
||||||
build_server = pick_build_server(build_servers, manifest_data)
|
|
||||||
manifest_data = build_server.apply_build_settings(manifest_data)
|
|
||||||
manifest = Manifest(data=manifest_data)
|
|
||||||
|
|
||||||
# bootstrap_info = tools.bootstrap(manifest, build_server)
|
|
||||||
from bootstrapvz.base.bootstrapinfo import BootstrapInformation
|
|
||||||
bootstrap_info = BootstrapInformation(manifest)
|
|
||||||
bootstrap_info.volume.image_path = '/target/debian-wheezy-amd64-141218.vdi'
|
|
||||||
|
|
||||||
from bootstrapvz.remote.build_servers import LocalBuildServer
|
|
||||||
if isinstance(build_server, LocalBuildServer):
|
|
||||||
image_path = bootstrap_info.volume.image_path
|
|
||||||
else:
|
|
||||||
# import tempfile
|
|
||||||
# handle, image_path = tempfile.mkstemp()
|
|
||||||
# import os
|
|
||||||
# os.close(handle)
|
|
||||||
# build_server.download(bootstrap_info.volume.image_path, image_path)
|
|
||||||
# build_server.delete(bootstrap_info.volume.image_path)
|
|
||||||
image_path = '/Users/anders/Workspace/cloud/images/debian-wheezy-amd64-141130.vmdk'
|
|
||||||
|
|
||||||
image = VirtualBoxImage(manifest, image_path)
|
|
||||||
try:
|
|
||||||
instance = VirtualBoxInstance('unpartitioned_extlinux', image)
|
|
||||||
try:
|
|
||||||
instance.create()
|
|
||||||
try:
|
|
||||||
instance.boot()
|
|
||||||
# tools.reachable_with_ssh(instance)
|
|
||||||
finally:
|
|
||||||
instance.shutdown()
|
|
||||||
finally:
|
|
||||||
instance.destroy()
|
|
||||||
finally:
|
|
||||||
image.destroy()
|
|
||||||
import os
|
|
||||||
os.remove(image_path)
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue