Add vm modification function

Adds a function to modify an existing VM object's configuration,
either directly in-editor, or by replacing with a new XML config.
In replacement mode, there is no confirmation or diff, the new
configuration is simply written and reboot triggered if specified.
In editor mode, the users local EDITOR (or `vi`) is started with
a copy of the existing config. Upon writing and exiting, the
operator is presented with a diff of the changes and is asked to
confirm before writing them to Zookeeper.

Closes #13
This commit is contained in:
Joshua Boniface 2018-07-20 00:38:31 -04:00
parent 56773d37d8
commit f9c27aea96
1 changed files with 114 additions and 0 deletions

114
pvc.py
View File

@ -25,6 +25,10 @@ import socket
import time
import uuid
import re
import tempfile
import subprocess
import difflib
import colorama
import click
import lxml.objectify
import configparser
@ -819,6 +823,115 @@ def define_vm(config, target_hypervisor, selector):
stopZKConnection(zk_conn)
###############################################################################
# pvc vm modify
###############################################################################
@click.command(name='modify', short_help='Modify an existing VM configuration.')
@click.option(
'-e', '--editor', 'editor', is_flag=True,
help='Use local editor to modify existing config.'
)
@click.option(
'-r', '--restart', 'restart', is_flag=True,
help='Immediately restart VM to apply new config.'
)
@click.argument(
'domain'
)
@click.argument(
'config', type=click.File(), default=None, required=False
)
def modify_vm(domain, config, editor, restart):
"""
Modify existing virtual machine DOMAIN, either in-editor or with replacement CONFIG. DOMAIN may be a UUID or name.
"""
if editor == False and config == None:
click.echo('Either an XML config file or the "--editor" option must be specified.')
exit(1)
# Open a Zookeeper connection
zk_conn = startZKConnection(zk_host)
# Validate and obtain alternate passed value
if validateUUID(domain):
dom_name = searchClusterByUUID(zk_conn, domain)
dom_uuid = searchClusterByName(zk_conn, dom_name)
else:
dom_uuid = searchClusterByName(zk_conn, domain)
dom_name = searchClusterByUUID(zk_conn, dom_uuid)
if dom_uuid == None:
click.echo('ERROR: Could not find VM "{}" in the cluster!'.format(domain))
stopZKConnection(zk_conn)
exit(1)
# We're operating in edit-in-place mode
if editor == True:
# Grab the current config
current_vm_config = zk_conn.get('/domains/{}/xml'.format(dom_uuid))[0].decode('ascii')
# Write it to a tempfile
fd, path = tempfile.mkstemp()
fw = os.fdopen(fd, 'w')
fw.write(current_vm_config)
fw.close()
# Edit it
editor = os.getenv('EDITOR', 'vi')
subprocess.call('%s %s' % (editor, path), shell=True)
# Open the tempfile to read
with open(path, 'r') as fr:
new_vm_config = fr.read()
fr.close()
# Delete the tempfile
os.unlink(path)
# Show a diff and confirm
diff = list(difflib.unified_diff(current_vm_config.split('\n'), new_vm_config.split('\n'), fromfile='current', tofile='modified', fromfiledate='', tofiledate='', n=3, lineterm=''))
if len(diff) < 1:
click.echo('Aborting with no modifications.')
exit(0)
click.echo('Pending modifications:')
click.echo('')
for line in diff:
if re.match('^\+', line) != None:
click.echo(colorama.Fore.GREEN + line + colorama.Fore.RESET)
elif re.match('^\-', line) != None:
click.echo(colorama.Fore.RED + line + colorama.Fore.RESET)
elif re.match('^\^', line) != None:
click.echo(colorama.Fore.BLUE + line + colorama.Fore.RESET)
else:
click.echo(line)
click.echo('')
click.confirm('Write modifications to Zookeeper?', abort=True)
click.echo('Writing modified config of VM "{}".'.format(dom_name))
# We're operating in replace mode
else:
# Open the XML file
new_vm_config = config.read()
config.close()
click.echo('Replacing config of VM "{}".'.format(dom_name, config))
# Add the modified config to Zookeeper
transaction = zk_conn.transaction()
transaction.set_data('/domains/{}'.format(dom_uuid), dom_name.encode('ascii'))
transaction.set_data('/domains/{}/xml'.format(dom_uuid), new_vm_config.encode('ascii'))
if restart == True:
transaction.set_data('/domains/{}/state'.format(dom_uuid), 'restart'.encode('ascii'))
results = transaction.commit()
# Close the Zookeeper connection
stopZKConnection(zk_conn)
###############################################################################
# pvc vm undefine
###############################################################################
@ -1520,6 +1633,7 @@ node.add_command(node_info)
node.add_command(node_list)
vm.add_command(define_vm)
vm.add_command(modify_vm)
vm.add_command(undefine_vm)
vm.add_command(start_vm)
vm.add_command(restart_vm)