1
0
mirror of https://github.com/gryf/boxpy.git synced 2025-12-19 05:30:18 +01:00

Changing approach with detecting if cloud init finished.

Till now, boxpy was based on the fact, that there was power_state
section, and there was a check if VM is down already. That approach have
their own issues.

Now, there will be no more power-off, there is a check by using
`cloud-init status` command through ssh.

Cleanup/destroy parts has needed some modification, so that there will
be no leftovers from cloud init ISO image.

And finally, there was some tweaks for user-data cloud-init part (mainly
for ssh handling), so that `cloud-init status` will not report phony
errors.
This commit is contained in:
2021-06-26 15:40:31 +02:00
parent cc4b4da253
commit a56b76f16d
2 changed files with 55 additions and 30 deletions

View File

@@ -116,15 +116,10 @@ Default user-script looks as follows:
gecos: ubuntu
sudo: ALL=(ALL) NOPASSWD:ALL
groups: users, admin
power_state:
mode: poweroff
timeout: 10
condition: True
It is really simple, and use ``string.Template`` for exchanging token
``$ssh_key`` with default, or provided public key, so that you will be able to
log in into the VM using that key. Section ``power_state`` is used internally
for making sure the cloud-init finish up and the VM will be started again.
log in into the VM using that key.
Note, that you need to be extra careful regarding ``$`` sign. As explained
above ``$ssh_key`` will be used as a "variable" for the template to substitute
@@ -145,10 +140,10 @@ pass filenames to the custom config, instead of filling up
filename: /path/to/local/file.txt
during processing this file, boxpy will look for ``filename`` key in the yaml
file for the ``write_files`` sections, and it will remove that key read the
file for the ``write_files`` sections, and it will remove that key, read the
file and put its contents under ``content`` key. What is more important, that
will be done after template processing, so that there will be no interference
for possible ``$`` characters.
will be done after template processing, so there will be no interference for
possible ``$`` characters.
What is more interesting is the fact, that you could use whatever cloud-init
accepts, and a special section, for keeping configuration, so that you don't
@@ -185,6 +180,18 @@ initialized, just to make you an idea, what could be done with it.
You can find some real world examples of the yaml cloud-init files that I use
in examples directory.
There is special section ``boxpy_data``, where you can place all the
configuration for the VM. Keys are the same as in ``create`` command options.
There is one additional key ``advanced`` which for now can be used for
configuration additional NIC for virtual machine, i.e:
.. code:: yaml
boxpy_data:
advanced:
nic2: intnet
License
-------

56
box.py
View File

@@ -34,10 +34,9 @@ users:
gecos: ${realname}
sudo: ALL=(ALL) NOPASSWD:ALL
groups: users, admin
power_state:
mode: poweroff
timeout: 10
condition: True
no_ssh_fingerprints: true
ssh:
emit_keys_to_console: false
boxpy_data:
cpus: 1
disk_size: 10240
@@ -579,12 +578,22 @@ class VBoxManage:
return Run(['vboxmanage', 'list', 'runningvms']).stdout
def destroy(self):
self.get_vm_info()
if not self.vm_info:
LOG.fatal("Cannot remove VM \"%s\" - it doesn't exist.",
self.name_or_uuid)
return 4
LOG.info('Removing VM %s.', self.name_or_uuid)
self.poweroff(silent=True)
time.sleep(1) # wait a bit, for VM shutdown to complete
# detach cloud image.
self.storageattach('IDE', 1, 'dvddrive', 'none')
self.closemedium('dvd', self.vm_info['iso_path'])
if Run(['vboxmanage', 'unregistervm', self.name_or_uuid,
'--delete']).returncode != 0:
LOG.fatal('Removing VM "%s" failed', self.name_or_uuid)
raise BoxVBoxFailure()
return 7
def create(self, cpus, memory, port=None):
LOG.info('Creating VM %s.', self.name_or_uuid)
@@ -972,6 +981,7 @@ def vmcreate(args, conf=None):
iso = IsoImage(conf)
path_to_iso = iso.get_generated_image()
vbox.setextradata('iso_path', path_to_iso)
vbox.storageattach('SATA', 0, 'hdd', path_to_disk)
vbox.storageattach('IDE', 1, 'dvddrive', path_to_iso)
@@ -986,34 +996,42 @@ def vmcreate(args, conf=None):
# give VBox some time to actually change the state of the VM before query
time.sleep(3)
def _cleanup(vbox, iso, image, path_to_iso):
time.sleep(1) # wait a bit, for VM shutdown to complete
vbox.storageattach('IDE', 1, 'dvddrive', 'none')
vbox.closemedium('dvd', path_to_iso)
iso.cleanup()
image.cleanup()
# than, let's try to see if boostraping process has finished
LOG.info('Waiting for cloud init to finish ', end='')
cmd = ['ssh', '-o', 'StrictHostKeyChecking=no',
'-o', 'UserKnownHostsFile=/dev/null',
'-o', 'ConnectTimeout=2',
'-i', conf.ssh_key_path[:-4],
f'ssh://{DISTROS[conf.distro]["username"]}'
f'@localhost:{vbox.vm_info["port"]}', 'cloud-init status']
try:
while True:
if vbox.vm_info['uuid'] in vbox.get_running_vms():
out = Run(cmd).stdout
LOG.debug2('Out: %s', out)
if (not out) or ('status' in out and 'running' in out):
LOG.info('.', end='')
sys.stdout.flush()
time.sleep(3)
else:
continue
LOG.info(' done.')
break
out = out.split(':')[1].strip()
if out != 'done':
LOG.warning('Cloud init finished with "%s" status. You can log '
'in and investigate.', out)
except KeyboardInterrupt:
LOG.warning('\nIterrupted, cleaning up.')
vbox.poweroff(silent=True)
_cleanup(vbox, iso, image, path_to_iso)
iso.cleanup()
image.cleanup()
vbox.destroy()
return 1
# dettach ISO image
_cleanup(vbox, iso, image, path_to_iso)
vbox.poweron()
# cleanup
iso.cleanup()
image.cleanup()
# reread config to update fields
conf = Config(args, vbox)