mirror of
https://github.com/gryf/boxpy.git
synced 2026-02-06 16:25:56 +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:
25
README.rst
25
README.rst
@@ -116,15 +116,10 @@ Default user-script looks as follows:
|
|||||||
gecos: ubuntu
|
gecos: ubuntu
|
||||||
sudo: ALL=(ALL) NOPASSWD:ALL
|
sudo: ALL=(ALL) NOPASSWD:ALL
|
||||||
groups: users, admin
|
groups: users, admin
|
||||||
power_state:
|
|
||||||
mode: poweroff
|
|
||||||
timeout: 10
|
|
||||||
condition: True
|
|
||||||
|
|
||||||
It is really simple, and use ``string.Template`` for exchanging token
|
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
|
``$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
|
log in into the VM using that key.
|
||||||
for making sure the cloud-init finish up and the VM will be started again.
|
|
||||||
|
|
||||||
Note, that you need to be extra careful regarding ``$`` sign. As explained
|
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
|
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
|
filename: /path/to/local/file.txt
|
||||||
|
|
||||||
during processing this file, boxpy will look for ``filename`` key in the yaml
|
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
|
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
|
will be done after template processing, so there will be no interference for
|
||||||
for possible ``$`` characters.
|
possible ``$`` characters.
|
||||||
|
|
||||||
What is more interesting is the fact, that you could use whatever cloud-init
|
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
|
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
|
You can find some real world examples of the yaml cloud-init files that I use
|
||||||
in examples directory.
|
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
|
License
|
||||||
-------
|
-------
|
||||||
|
|||||||
60
box.py
60
box.py
@@ -34,10 +34,9 @@ users:
|
|||||||
gecos: ${realname}
|
gecos: ${realname}
|
||||||
sudo: ALL=(ALL) NOPASSWD:ALL
|
sudo: ALL=(ALL) NOPASSWD:ALL
|
||||||
groups: users, admin
|
groups: users, admin
|
||||||
power_state:
|
no_ssh_fingerprints: true
|
||||||
mode: poweroff
|
ssh:
|
||||||
timeout: 10
|
emit_keys_to_console: false
|
||||||
condition: True
|
|
||||||
boxpy_data:
|
boxpy_data:
|
||||||
cpus: 1
|
cpus: 1
|
||||||
disk_size: 10240
|
disk_size: 10240
|
||||||
@@ -579,12 +578,22 @@ class VBoxManage:
|
|||||||
return Run(['vboxmanage', 'list', 'runningvms']).stdout
|
return Run(['vboxmanage', 'list', 'runningvms']).stdout
|
||||||
|
|
||||||
def destroy(self):
|
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)
|
LOG.info('Removing VM %s.', self.name_or_uuid)
|
||||||
self.poweroff(silent=True)
|
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,
|
if Run(['vboxmanage', 'unregistervm', self.name_or_uuid,
|
||||||
'--delete']).returncode != 0:
|
'--delete']).returncode != 0:
|
||||||
LOG.fatal('Removing VM "%s" failed', self.name_or_uuid)
|
LOG.fatal('Removing VM "%s" failed', self.name_or_uuid)
|
||||||
raise BoxVBoxFailure()
|
return 7
|
||||||
|
|
||||||
def create(self, cpus, memory, port=None):
|
def create(self, cpus, memory, port=None):
|
||||||
LOG.info('Creating VM %s.', self.name_or_uuid)
|
LOG.info('Creating VM %s.', self.name_or_uuid)
|
||||||
@@ -972,6 +981,7 @@ def vmcreate(args, conf=None):
|
|||||||
|
|
||||||
iso = IsoImage(conf)
|
iso = IsoImage(conf)
|
||||||
path_to_iso = iso.get_generated_image()
|
path_to_iso = iso.get_generated_image()
|
||||||
|
vbox.setextradata('iso_path', path_to_iso)
|
||||||
vbox.storageattach('SATA', 0, 'hdd', path_to_disk)
|
vbox.storageattach('SATA', 0, 'hdd', path_to_disk)
|
||||||
vbox.storageattach('IDE', 1, 'dvddrive', path_to_iso)
|
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
|
# give VBox some time to actually change the state of the VM before query
|
||||||
time.sleep(3)
|
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
|
# than, let's try to see if boostraping process has finished
|
||||||
LOG.info('Waiting for cloud init to finish ', end='')
|
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:
|
try:
|
||||||
while True:
|
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='')
|
LOG.info('.', end='')
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
else:
|
continue
|
||||||
LOG.info(' done.')
|
|
||||||
break
|
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:
|
except KeyboardInterrupt:
|
||||||
LOG.warning('\nIterrupted, cleaning up.')
|
LOG.warning('\nIterrupted, cleaning up.')
|
||||||
vbox.poweroff(silent=True)
|
iso.cleanup()
|
||||||
_cleanup(vbox, iso, image, path_to_iso)
|
image.cleanup()
|
||||||
vbox.destroy()
|
vbox.destroy()
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
# dettach ISO image
|
# cleanup
|
||||||
_cleanup(vbox, iso, image, path_to_iso)
|
iso.cleanup()
|
||||||
vbox.poweron()
|
image.cleanup()
|
||||||
|
|
||||||
# reread config to update fields
|
# reread config to update fields
|
||||||
conf = Config(args, vbox)
|
conf = Config(args, vbox)
|
||||||
|
|||||||
Reference in New Issue
Block a user