Convert VM functions to API client
This commit is contained in:
parent
f4ef08df49
commit
d0b6bb4cc3
|
@ -21,6 +21,7 @@
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import click
|
import click
|
||||||
import requests
|
import requests
|
||||||
|
@ -45,6 +46,291 @@ def get_request_uri(config, endpoint):
|
||||||
#
|
#
|
||||||
# Primary functions
|
# Primary functions
|
||||||
#
|
#
|
||||||
|
def vm_info(config, vm):
|
||||||
|
"""
|
||||||
|
Get information about VM
|
||||||
|
|
||||||
|
API endpoint: GET /api/v1/vm/{vm}
|
||||||
|
API arguments:
|
||||||
|
API schema: {json_data_object}
|
||||||
|
"""
|
||||||
|
request_uri = get_request_uri(config, '/vm/{vm}'.format(vm=vm))
|
||||||
|
response = requests.get(
|
||||||
|
request_uri
|
||||||
|
)
|
||||||
|
|
||||||
|
if config['debug']:
|
||||||
|
print('API endpoint: POST {}'.format(request_uri))
|
||||||
|
print('Response code: {}'.format(response.status_code))
|
||||||
|
print('Response headers: {}'.format(response.headers))
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
return True, response.json()
|
||||||
|
else:
|
||||||
|
return False, response.json()['message']
|
||||||
|
|
||||||
|
def vm_list(config, limit, target_node, target_state):
|
||||||
|
"""
|
||||||
|
Get list information about nodes (limited by {limit}, {target_node}, or {target_state})
|
||||||
|
|
||||||
|
API endpoint: GET /api/v1/vm
|
||||||
|
API arguments: limit={limit}, node={target_node}, state={target_state}
|
||||||
|
API schema: [{json_data_object},{json_data_object},etc.]
|
||||||
|
"""
|
||||||
|
params = dict()
|
||||||
|
if limit:
|
||||||
|
params['limit'] = limit
|
||||||
|
if target_node:
|
||||||
|
params['node'] = target_node
|
||||||
|
if target_state:
|
||||||
|
params['state'] = target_state
|
||||||
|
|
||||||
|
request_uri = get_request_uri(config, '/vm')
|
||||||
|
response = requests.get(
|
||||||
|
request_uri,
|
||||||
|
params=params
|
||||||
|
)
|
||||||
|
|
||||||
|
if config['debug']:
|
||||||
|
print('API endpoint: POST {}'.format(request_uri))
|
||||||
|
print('Response code: {}'.format(response.status_code))
|
||||||
|
print('Response headers: {}'.format(response.headers))
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
return True, response.json()
|
||||||
|
else:
|
||||||
|
return False, response.json()['message']
|
||||||
|
|
||||||
|
def vm_define(config, xml, node, node_limit, node_selector, node_autostart):
|
||||||
|
"""
|
||||||
|
Define a new VM on the cluster
|
||||||
|
|
||||||
|
API endpoint: POST /vm
|
||||||
|
API arguments: xml={xml}, node={node}, limit={node_limit}, selector={node_selector}, autostart={node_autostart}
|
||||||
|
API schema: {"message":"{data}"}
|
||||||
|
"""
|
||||||
|
request_uri = get_request_uri(config, '/vm')
|
||||||
|
response = requests.post(
|
||||||
|
request_uri,
|
||||||
|
params={
|
||||||
|
'xml': xml,
|
||||||
|
'node': node,
|
||||||
|
'limit': node_limit,
|
||||||
|
'selector': node_selector,
|
||||||
|
'autostart': node_autostart
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if config['debug']:
|
||||||
|
print('API endpoint: POST {}'.format(request_uri))
|
||||||
|
print('Response code: {}'.format(response.status_code))
|
||||||
|
print('Response headers: {}'.format(response.headers))
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
retstatus = True
|
||||||
|
else:
|
||||||
|
retstatus = False
|
||||||
|
|
||||||
|
return retstatus, response.json()['message']
|
||||||
|
|
||||||
|
def vm_modify(config, vm, xml, restart):
|
||||||
|
"""
|
||||||
|
Modify the configuration of VM
|
||||||
|
|
||||||
|
API endpoint: POST /vm/{vm}
|
||||||
|
API arguments: xml={xml}, restart={restart}
|
||||||
|
API schema: {"message":"{data}"}
|
||||||
|
"""
|
||||||
|
request_uri = get_request_uri(config, '/vm/{vm}'.format(vm=vm))
|
||||||
|
response = requests.post(
|
||||||
|
request_uri,
|
||||||
|
params={
|
||||||
|
'xml': xml,
|
||||||
|
'restart': restart
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if config['debug']:
|
||||||
|
print('API endpoint: POST {}'.format(request_uri))
|
||||||
|
print('Response code: {}'.format(response.status_code))
|
||||||
|
print('Response headers: {}'.format(response.headers))
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
retstatus = True
|
||||||
|
else:
|
||||||
|
retstatus = False
|
||||||
|
|
||||||
|
return retstatus, response.json()['message']
|
||||||
|
|
||||||
|
def vm_metadata(config, vm, node_limit, node_selector, node_autostart):
|
||||||
|
"""
|
||||||
|
Modify PVC metadata of a VM
|
||||||
|
|
||||||
|
API endpoint: GET /vm/{vm}/meta, POST /vm/{vm}/meta
|
||||||
|
API arguments: limit={node_limit}, selector={node_selector}, autostart={node_autostart}
|
||||||
|
API schema: {"message":"{data}"}
|
||||||
|
"""
|
||||||
|
request_uri = get_request_uri(config, '/vm/{vm}/meta'.format(vm=vm))
|
||||||
|
|
||||||
|
# Get the existing metadata so we can perform a fully dynamic update
|
||||||
|
response = requests.get(
|
||||||
|
request_uri
|
||||||
|
)
|
||||||
|
|
||||||
|
if config['debug']:
|
||||||
|
print('API endpoint: GET {}'.format(request_uri))
|
||||||
|
print('Response code: {}'.format(response.status_code))
|
||||||
|
print('Response headers: {}'.format(response.headers))
|
||||||
|
|
||||||
|
metadata = response.json()
|
||||||
|
|
||||||
|
# Update any params that we've sent
|
||||||
|
if node_limit is not None:
|
||||||
|
metadata['node_limit'] = node_limit
|
||||||
|
else:
|
||||||
|
# Collapse the existing list back down to a CSV
|
||||||
|
metadata['node_limit'] = ','.join(metadata['node_limit'])
|
||||||
|
|
||||||
|
if node_selector is not None:
|
||||||
|
metadata['node_selector'] = node_selector
|
||||||
|
|
||||||
|
if node_autostart is not None:
|
||||||
|
metadata['node_autostart'] = node_autostart
|
||||||
|
|
||||||
|
# Write the new metadata
|
||||||
|
print(metadata['node_limit'])
|
||||||
|
response = requests.post(
|
||||||
|
request_uri,
|
||||||
|
params={
|
||||||
|
'limit': metadata['node_limit'],
|
||||||
|
'selector': metadata['node_selector'],
|
||||||
|
'autostart': metadata['node_autostart']
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if config['debug']:
|
||||||
|
print('API endpoint: POST {}'.format(request_uri))
|
||||||
|
print('Response code: {}'.format(response.status_code))
|
||||||
|
print('Response headers: {}'.format(response.headers))
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
retstatus = True
|
||||||
|
else:
|
||||||
|
retstatus = False
|
||||||
|
|
||||||
|
return retstatus, response.json()['message']
|
||||||
|
|
||||||
|
def vm_remove(config, vm, delete_disks=False):
|
||||||
|
"""
|
||||||
|
Remove a VM
|
||||||
|
|
||||||
|
API endpoint: DELETE /vm/{vm}
|
||||||
|
API arguments: delete_disks={delete_disks}
|
||||||
|
API schema: {"message":"{data}"}
|
||||||
|
"""
|
||||||
|
request_uri = get_request_uri(config, '/vm/{vm}'.format(vm=vm))
|
||||||
|
response = requests.delete(
|
||||||
|
request_uri,
|
||||||
|
params={
|
||||||
|
'delete_disks': delete_disks
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if config['debug']:
|
||||||
|
print('API endpoint: DELETE {}'.format(request_uri))
|
||||||
|
print('Response code: {}'.format(response.status_code))
|
||||||
|
print('Response headers: {}'.format(response.headers))
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
retstatus = True
|
||||||
|
else:
|
||||||
|
retstatus = False
|
||||||
|
|
||||||
|
return retstatus, response.json()['message']
|
||||||
|
|
||||||
|
def vm_state(config, vm, target_state):
|
||||||
|
"""
|
||||||
|
Modify the current state of VM
|
||||||
|
|
||||||
|
API endpoint: POST /vm/{vm}/state
|
||||||
|
API arguments: state={state}
|
||||||
|
API schema: {"message":"{data}"}
|
||||||
|
"""
|
||||||
|
request_uri = get_request_uri(config, '/vm/{vm}/state'.format(vm=vm))
|
||||||
|
response = requests.post(
|
||||||
|
request_uri,
|
||||||
|
params={
|
||||||
|
'state': target_state,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if config['debug']:
|
||||||
|
print('API endpoint: POST {}'.format(request_uri))
|
||||||
|
print('Response code: {}'.format(response.status_code))
|
||||||
|
print('Response headers: {}'.format(response.headers))
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
retstatus = True
|
||||||
|
else:
|
||||||
|
retstatus = False
|
||||||
|
|
||||||
|
return retstatus, response.json()['message']
|
||||||
|
|
||||||
|
def vm_node(config, vm, target_node, action, force=False):
|
||||||
|
"""
|
||||||
|
Modify the current node of VM via {action}
|
||||||
|
|
||||||
|
API endpoint: POST /vm/{vm}/node
|
||||||
|
API arguments: node={target_node}, action={action}, force={force}
|
||||||
|
API schema: {"message":"{data}"}
|
||||||
|
"""
|
||||||
|
request_uri = get_request_uri(config, '/vm/{vm}/node'.format(vm=vm))
|
||||||
|
response = requests.post(
|
||||||
|
request_uri,
|
||||||
|
params={
|
||||||
|
'node': target_node,
|
||||||
|
'action': action,
|
||||||
|
'force': force
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if config['debug']:
|
||||||
|
print('API endpoint: POST {}'.format(request_uri))
|
||||||
|
print('Response code: {}'.format(response.status_code))
|
||||||
|
print('Response headers: {}'.format(response.headers))
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
retstatus = True
|
||||||
|
else:
|
||||||
|
retstatus = False
|
||||||
|
|
||||||
|
return retstatus, response.json()['message']
|
||||||
|
|
||||||
|
def vm_locks(config, vm):
|
||||||
|
"""
|
||||||
|
Flush RBD locks of (stopped) VM
|
||||||
|
|
||||||
|
API endpoint: POST /vm/{vm}/locks
|
||||||
|
API arguments:
|
||||||
|
API schema: {"message":"{data}"}
|
||||||
|
"""
|
||||||
|
request_uri = get_request_uri(config, '/vm/{vm}/locks'.format(vm=vm))
|
||||||
|
response = requests.post(
|
||||||
|
request_uri
|
||||||
|
)
|
||||||
|
|
||||||
|
if config['debug']:
|
||||||
|
print('API endpoint: POST {}'.format(request_uri))
|
||||||
|
print('Response code: {}'.format(response.status_code))
|
||||||
|
print('Response headers: {}'.format(response.headers))
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
retstatus = True
|
||||||
|
else:
|
||||||
|
retstatus = False
|
||||||
|
|
||||||
|
return retstatus, response.json()['message']
|
||||||
|
|
||||||
def view_console_log(config, vm, lines=100):
|
def view_console_log(config, vm, lines=100):
|
||||||
"""
|
"""
|
||||||
Return console log lines from the API and display them in a pager
|
Return console log lines from the API and display them in a pager
|
||||||
|
@ -54,28 +340,15 @@ def view_console_log(config, vm, lines=100):
|
||||||
API schema: {"name":"{vmname}","data":"{console_log}"}
|
API schema: {"name":"{vmname}","data":"{console_log}"}
|
||||||
"""
|
"""
|
||||||
request_uri = get_request_uri(config, '/vm/{vm}/console'.format(vm=vm))
|
request_uri = get_request_uri(config, '/vm/{vm}/console'.format(vm=vm))
|
||||||
if config['debug']:
|
|
||||||
print(
|
|
||||||
'API endpoint: GET {}'.format(request_uri)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Get the data from the API
|
|
||||||
response = requests.get(
|
response = requests.get(
|
||||||
request_uri,
|
request_uri,
|
||||||
params={'lines': lines}
|
params={'lines': lines}
|
||||||
)
|
)
|
||||||
|
|
||||||
if config['debug']:
|
if config['debug']:
|
||||||
print(
|
print('API endpoint: GET {}'.format(request_uri))
|
||||||
'Response code: {}'.format(
|
print('Response code: {}'.format(response.status_code))
|
||||||
response.status_code
|
print('Response headers: {}'.format(response.headers))
|
||||||
)
|
|
||||||
)
|
|
||||||
print(
|
|
||||||
'Response headers: {}'.format(
|
|
||||||
response.headers
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
console_log = response.json()['data']
|
console_log = response.json()['data']
|
||||||
|
|
||||||
|
@ -102,28 +375,15 @@ def follow_console_log(config, vm, lines=10):
|
||||||
API schema: {"name":"{vmname}","data":"{console_log}"}
|
API schema: {"name":"{vmname}","data":"{console_log}"}
|
||||||
"""
|
"""
|
||||||
request_uri = get_request_uri(config, '/vm/{vm}/console'.format(vm=vm))
|
request_uri = get_request_uri(config, '/vm/{vm}/console'.format(vm=vm))
|
||||||
if config['debug']:
|
|
||||||
print(
|
|
||||||
'API endpoint: GET {}'.format(request_uri)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Get the (initial) data from the API
|
|
||||||
response = requests.get(
|
response = requests.get(
|
||||||
request_uri,
|
request_uri,
|
||||||
params={'lines': lines}
|
params={'lines': lines}
|
||||||
)
|
)
|
||||||
|
|
||||||
if config['debug']:
|
if config['debug']:
|
||||||
print(
|
print('API endpoint: GET {}'.format(request_uri))
|
||||||
'Response code: {}'.format(
|
print('Response code: {}'.format(response.status_code))
|
||||||
response.status_code
|
print('Response headers: {}'.format(response.headers))
|
||||||
)
|
|
||||||
)
|
|
||||||
print(
|
|
||||||
'Response headers: {}'.format(
|
|
||||||
response.headers
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
console_log = response.json()['data']
|
console_log = response.json()['data']
|
||||||
|
|
||||||
|
@ -261,11 +521,16 @@ def format_info(config, domain_information, long_output):
|
||||||
net_vni = net_vnis[0]
|
net_vni = net_vnis[0]
|
||||||
else:
|
else:
|
||||||
net_vni = re.sub('br', '', net['source'])
|
net_vni = re.sub('br', '', net['source'])
|
||||||
net_exists = zkhandler.exists(zk_conn, '/networks/{}'.format(net_vni))
|
|
||||||
if not net_exists and net_vni != 'cluster':
|
request_uri = get_request_uri(config, '/network/{net}'.format(net=net_vni))
|
||||||
|
response = requests.get(
|
||||||
|
request_uri
|
||||||
|
)
|
||||||
|
if response.status_code != 200 and net_vni != 'cluster':
|
||||||
net_list.append(ansiprint.red() + net_vni + ansiprint.end() + ' [invalid]')
|
net_list.append(ansiprint.red() + net_vni + ansiprint.end() + ' [invalid]')
|
||||||
else:
|
else:
|
||||||
net_list.append(net_vni)
|
net_list.append(net_vni)
|
||||||
|
|
||||||
ainformation.append('')
|
ainformation.append('')
|
||||||
ainformation.append('{}Networks:{} {}'.format(ansiprint.purple(), ansiprint.end(), ', '.join(net_list)))
|
ainformation.append('{}Networks:{} {}'.format(ansiprint.purple(), ansiprint.end(), ', '.join(net_list)))
|
||||||
|
|
||||||
|
@ -383,6 +648,8 @@ def format_list(config, vm_list, raw):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Keep track of nets we found to be valid to cut down on duplicate API hits
|
||||||
|
valid_net_list = []
|
||||||
# Format the string (elements)
|
# Format the string (elements)
|
||||||
for domain_information in vm_list:
|
for domain_information in vm_list:
|
||||||
if domain_information['state'] == 'start':
|
if domain_information['state'] == 'start':
|
||||||
|
@ -403,9 +670,16 @@ def format_list(config, vm_list, raw):
|
||||||
net_list = []
|
net_list = []
|
||||||
vm_net_colour = ''
|
vm_net_colour = ''
|
||||||
for net_vni in raw_net_list:
|
for net_vni in raw_net_list:
|
||||||
net_exists = zkhandler.exists(zk_conn, '/networks/{}'.format(net_vni))
|
if not net_vni in valid_net_list:
|
||||||
if not net_exists and net_vni != 'cluster':
|
request_uri = get_request_uri(config, '/network/{net}'.format(net=net_vni))
|
||||||
|
response = requests.get(
|
||||||
|
request_uri
|
||||||
|
)
|
||||||
|
if response.status_code != 200 and net_vni != 'cluster':
|
||||||
vm_net_colour = ansiprint.red()
|
vm_net_colour = ansiprint.red()
|
||||||
|
else:
|
||||||
|
valid_net_list.append(net_vni)
|
||||||
|
|
||||||
net_list.append(net_vni)
|
net_list.append(net_vni)
|
||||||
|
|
||||||
vm_list_output.append(
|
vm_list_output.append(
|
||||||
|
@ -442,4 +716,3 @@ def format_list(config, vm_list, raw):
|
||||||
click.echo('\n'.join(sorted(vm_list_output)))
|
click.echo('\n'.join(sorted(vm_list_output)))
|
||||||
|
|
||||||
return True, ''
|
return True, ''
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ import difflib
|
||||||
import re
|
import re
|
||||||
import colorama
|
import colorama
|
||||||
import yaml
|
import yaml
|
||||||
|
import lxml.etree as etree
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
import cli_lib.ansiprint as ansiprint
|
import cli_lib.ansiprint as ansiprint
|
||||||
|
@ -232,16 +233,12 @@ def vm_define(config, target_node, node_limit, node_selector, node_autostart):
|
||||||
Define a new virtual machine from Libvirt XML configuration file CONFIG.
|
Define a new virtual machine from Libvirt XML configuration file CONFIG.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if node_limit:
|
|
||||||
node_limit = node_limit.split(',')
|
|
||||||
|
|
||||||
# Open the XML file
|
# Open the XML file
|
||||||
config_data = config.read()
|
config_data = config.read()
|
||||||
config.close()
|
config.close()
|
||||||
|
|
||||||
zk_conn = pvc_common.startZKConnection(zk_host)
|
|
||||||
retcode, retmsg = pvc_vm.define_vm(zk_conn, config_data, target_node, node_limit, node_selector, node_autostart)
|
retcode, retmsg = pvc_vm.define_vm(zk_conn, config_data, target_node, node_limit, node_selector, node_autostart)
|
||||||
cleanup(retcode, retmsg, zk_conn)
|
cleanup(retcode, retmsg)
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# pvc vm meta
|
# pvc vm meta
|
||||||
|
@ -271,12 +268,8 @@ def vm_meta(domain, node_limit, node_selector, node_autostart):
|
||||||
if node_limit is None and node_selector is None and node_autostart is None:
|
if node_limit is None and node_selector is None and node_autostart is None:
|
||||||
cleanup(False, 'At least one metadata option must be specified to update.')
|
cleanup(False, 'At least one metadata option must be specified to update.')
|
||||||
|
|
||||||
if node_limit:
|
retcode, retmsg = pvc_vm.vm_metadata(config, domain, node_limit, node_selector, node_autostart)
|
||||||
node_limit = node_limit.split(',')
|
cleanup(retcode, retmsg)
|
||||||
|
|
||||||
zk_conn = pvc_common.startZKConnection(zk_host)
|
|
||||||
retcode, retmsg = pvc_vm.modify_vm_metadata(zk_conn, domain, node_limit, node_selector, node_autostart)
|
|
||||||
cleanup(retcode, retmsg, zk_conn)
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# pvc vm modify
|
# pvc vm modify
|
||||||
|
@ -294,31 +287,33 @@ def vm_meta(domain, node_limit, node_selector, node_autostart):
|
||||||
'domain'
|
'domain'
|
||||||
)
|
)
|
||||||
@click.argument(
|
@click.argument(
|
||||||
'config', type=click.File(), default=None, required=False
|
'cfgfile', type=click.File(), default=None, required=False
|
||||||
)
|
)
|
||||||
def vm_modify(domain, config, editor, restart):
|
def vm_modify(domain, cfgfile, editor, restart):
|
||||||
"""
|
"""
|
||||||
Modify existing virtual machine DOMAIN, either in-editor or with replacement CONFIG. DOMAIN may be a UUID or name.
|
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:
|
if editor == False and cfgfile == None:
|
||||||
cleanup(False, 'Either an XML config file or the "--editor" option must be specified.')
|
cleanup(False, 'Either an XML config file or the "--editor" option must be specified.')
|
||||||
|
|
||||||
zk_conn = pvc_common.startZKConnection(zk_host)
|
retcode, vm_information = pvc_vm.vm_info(config, domain)
|
||||||
|
if not retcode and not vm_information.get('name', None):
|
||||||
dom_uuid = pvc_vm.getDomainUUID(zk_conn, domain)
|
|
||||||
if dom_uuid == None:
|
|
||||||
cleanup(False, 'ERROR: Could not find VM "{}" in the cluster!'.format(domain))
|
cleanup(False, 'ERROR: Could not find VM "{}" in the cluster!'.format(domain))
|
||||||
dom_name = pvc_vm.getDomainName(zk_conn, dom_uuid)
|
|
||||||
|
dom_uuid = vm_information.get('uuid')
|
||||||
|
dom_name = vm_information.get('name')
|
||||||
|
|
||||||
if editor == True:
|
if editor == True:
|
||||||
# Grab the current config
|
# Grab the current config
|
||||||
current_vm_config = zk_conn.get('/domains/{}/xml'.format(dom_uuid))[0].decode('ascii')
|
current_vm_cfg_raw = vm_information.get('xml')
|
||||||
|
xml_data = etree.fromstring(current_vm_cfg_raw)
|
||||||
|
current_vm_cfgfile = etree.tostring(xml_data, pretty_print=True).decode('utf8')
|
||||||
|
|
||||||
# Write it to a tempfile
|
# Write it to a tempfile
|
||||||
fd, path = tempfile.mkstemp()
|
fd, path = tempfile.mkstemp()
|
||||||
fw = os.fdopen(fd, 'w')
|
fw = os.fdopen(fd, 'w')
|
||||||
fw.write(current_vm_config)
|
fw.write(current_vm_cfgfile.strip())
|
||||||
fw.close()
|
fw.close()
|
||||||
|
|
||||||
# Edit it
|
# Edit it
|
||||||
|
@ -327,14 +322,14 @@ def vm_modify(domain, config, editor, restart):
|
||||||
|
|
||||||
# Open the tempfile to read
|
# Open the tempfile to read
|
||||||
with open(path, 'r') as fr:
|
with open(path, 'r') as fr:
|
||||||
new_vm_config = fr.read()
|
new_vm_cfgfile = fr.read()
|
||||||
fr.close()
|
fr.close()
|
||||||
|
|
||||||
# Delete the tempfile
|
# Delete the tempfile
|
||||||
os.unlink(path)
|
os.unlink(path)
|
||||||
|
|
||||||
# Show a diff and confirm
|
# 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=''))
|
diff = list(difflib.unified_diff(current_vm_cfgfile.split('\n'), new_vm_cfgfile.split('\n'), fromfile='current', tofile='modified', fromfiledate='', tofiledate='', n=3, lineterm=''))
|
||||||
if len(diff) < 1:
|
if len(diff) < 1:
|
||||||
click.echo('Aborting with no modifications.')
|
click.echo('Aborting with no modifications.')
|
||||||
exit(0)
|
exit(0)
|
||||||
|
@ -355,23 +350,23 @@ def vm_modify(domain, config, editor, restart):
|
||||||
click.confirm('Write modifications to Zookeeper?', abort=True)
|
click.confirm('Write modifications to Zookeeper?', abort=True)
|
||||||
|
|
||||||
if restart:
|
if restart:
|
||||||
click.echo('Writing modified config of VM "{}" and restarting.'.format(dom_name))
|
click.echo('Writing modified configuration of VM "{}" and restarting.'.format(dom_name))
|
||||||
else:
|
else:
|
||||||
click.echo('Writing modified config of VM "{}".'.format(dom_name))
|
click.echo('Writing modified configuration of VM "{}".'.format(dom_name))
|
||||||
|
|
||||||
# We're operating in replace mode
|
# We're operating in replace mode
|
||||||
else:
|
else:
|
||||||
# Open the XML file
|
# Open the XML file
|
||||||
new_vm_config = config.read()
|
new_vm_cfgfile = cfgfile.read()
|
||||||
config.close()
|
cfgfile.close()
|
||||||
|
|
||||||
if restart:
|
if restart:
|
||||||
click.echo('Replacing config of VM "{}" with file "{}" and restarting.'.format(dom_name, config.name))
|
click.echo('Replacing configuration of VM "{}" with file "{}" and restarting.'.format(dom_name, cfgfile.name))
|
||||||
else:
|
else:
|
||||||
click.echo('Replacing config of VM "{}" with file "{}".'.format(dom_name, config.name))
|
click.echo('Replacing configuration of VM "{}" with file "{}".'.format(dom_name, cfgfile.name))
|
||||||
|
|
||||||
retcode, retmsg = pvc_vm.modify_vm(zk_conn, domain, restart, new_vm_config)
|
retcode, retmsg = pvc_vm.vm_modify(config, domain, new_vm_config, restart)
|
||||||
cleanup(retcode, retmsg, zk_conn)
|
cleanup(retcode, retmsg)
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# pvc vm undefine
|
# pvc vm undefine
|
||||||
|
@ -385,15 +380,8 @@ def vm_undefine(domain):
|
||||||
Stop virtual machine DOMAIN and remove it from the cluster database, preserving disks. DOMAIN may be a UUID or name.
|
Stop virtual machine DOMAIN and remove it from the cluster database, preserving disks. DOMAIN may be a UUID or name.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Ensure at least one search method is set
|
retcode, retmsg = pvc_vm.vm_remove(config, domain, delete_disks=False)
|
||||||
if domain == None:
|
cleanup(retcode, retmsg)
|
||||||
click.echo("ERROR: You must specify either a name or UUID value.")
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
# Open a Zookeeper connection
|
|
||||||
zk_conn = pvc_common.startZKConnection(zk_host)
|
|
||||||
retcode, retmsg = pvc_vm.undefine_vm(zk_conn, domain, is_cli=True)
|
|
||||||
cleanup(retcode, retmsg, zk_conn)
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# pvc vm remove
|
# pvc vm remove
|
||||||
|
@ -407,37 +395,8 @@ def vm_remove(domain):
|
||||||
Stop virtual machine DOMAIN and remove it, along with all disks, from the cluster. DOMAIN may be a UUID or name.
|
Stop virtual machine DOMAIN and remove it, along with all disks, from the cluster. DOMAIN may be a UUID or name.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Ensure at least one search method is set
|
retcode, retmsg = pvc_vm.vm_remove(config, domain, delete_disks=True)
|
||||||
if domain == None:
|
cleanup(retcode, retmsg)
|
||||||
click.echo("ERROR: You must specify either a name or UUID value.")
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
# Open a Zookeeper connection
|
|
||||||
zk_conn = pvc_common.startZKConnection(zk_host)
|
|
||||||
retcode, retmsg = pvc_vm.remove_vm(zk_conn, domain, is_cli=True)
|
|
||||||
cleanup(retcode, retmsg, zk_conn)
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# pvc vm dump
|
|
||||||
###############################################################################
|
|
||||||
@click.command(name='dump', short_help='Dump a virtual machine XML to stdout.')
|
|
||||||
@click.argument(
|
|
||||||
'domain'
|
|
||||||
)
|
|
||||||
def vm_dump(domain):
|
|
||||||
"""
|
|
||||||
Dump the Libvirt XML definition of virtual machine DOMAIN to stdout. DOMAIN may be a UUID or name.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Ensure at least one search method is set
|
|
||||||
if domain == None:
|
|
||||||
click.echo("ERROR: You must specify either a name or UUID value.")
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
# Open a Zookeeper connection
|
|
||||||
zk_conn = pvc_common.startZKConnection(zk_host)
|
|
||||||
retcode, retmsg = pvc_vm.dump_vm(zk_conn, domain)
|
|
||||||
cleanup(retcode, retmsg, zk_conn)
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# pvc vm start
|
# pvc vm start
|
||||||
|
@ -451,10 +410,8 @@ def vm_start(domain):
|
||||||
Start virtual machine DOMAIN on its configured node. DOMAIN may be a UUID or name.
|
Start virtual machine DOMAIN on its configured node. DOMAIN may be a UUID or name.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Open a Zookeeper connection
|
retcode, retmsg = pvc_vm.vm_state(config, domain, 'start')
|
||||||
zk_conn = pvc_common.startZKConnection(zk_host)
|
cleanup(retcode, retmsg)
|
||||||
retcode, retmsg = pvc_vm.start_vm(zk_conn, domain)
|
|
||||||
cleanup(retcode, retmsg, zk_conn)
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# pvc vm restart
|
# pvc vm restart
|
||||||
|
@ -468,10 +425,8 @@ def vm_restart(domain):
|
||||||
Restart running virtual machine DOMAIN. DOMAIN may be a UUID or name.
|
Restart running virtual machine DOMAIN. DOMAIN may be a UUID or name.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Open a Zookeeper connection
|
retcode, retmsg = pvc_vm.vm_state(config, domain, 'restart')
|
||||||
zk_conn = pvc_common.startZKConnection(zk_host)
|
cleanup(retcode, retmsg)
|
||||||
retcode, retmsg = pvc_vm.restart_vm(zk_conn, domain)
|
|
||||||
cleanup(retcode, retmsg, zk_conn)
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# pvc vm shutdown
|
# pvc vm shutdown
|
||||||
|
@ -485,10 +440,8 @@ def vm_shutdown(domain):
|
||||||
Gracefully shut down virtual machine DOMAIN. DOMAIN may be a UUID or name.
|
Gracefully shut down virtual machine DOMAIN. DOMAIN may be a UUID or name.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Open a Zookeeper connection
|
retcode, retmsg = pvc_vm.vm_state(config, domain, 'shutdown')
|
||||||
zk_conn = pvc_common.startZKConnection(zk_host)
|
cleanup(retcode, retmsg)
|
||||||
retcode, retmsg = pvc_vm.shutdown_vm(zk_conn, domain)
|
|
||||||
cleanup(retcode, retmsg, zk_conn)
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# pvc vm stop
|
# pvc vm stop
|
||||||
|
@ -502,10 +455,8 @@ def vm_stop(domain):
|
||||||
Forcibly halt (destroy) running virtual machine DOMAIN. DOMAIN may be a UUID or name.
|
Forcibly halt (destroy) running virtual machine DOMAIN. DOMAIN may be a UUID or name.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Open a Zookeeper connection
|
retcode, retmsg = pvc_vm.vm_state(config, domain, 'stop')
|
||||||
zk_conn = pvc_common.startZKConnection(zk_host)
|
cleanup(retcode, retmsg)
|
||||||
retcode, retmsg = pvc_vm.stop_vm(zk_conn, domain)
|
|
||||||
cleanup(retcode, retmsg, zk_conn)
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# pvc vm disable
|
# pvc vm disable
|
||||||
|
@ -521,10 +472,8 @@ def vm_disable(domain):
|
||||||
Use this option for VM that are stopped intentionally or long-term and which should not impact cluster health if stopped. A VM can be started directly from disable state.
|
Use this option for VM that are stopped intentionally or long-term and which should not impact cluster health if stopped. A VM can be started directly from disable state.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Open a Zookeeper connection
|
retcode, retmsg = pvc_vm.vm_state(config, domain, 'disable')
|
||||||
zk_conn = pvc_common.startZKConnection(zk_host)
|
cleanup(retcode, retmsg)
|
||||||
retcode, retmsg = pvc_vm.disable_vm(zk_conn, domain)
|
|
||||||
cleanup(retcode, retmsg, zk_conn)
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# pvc vm move
|
# pvc vm move
|
||||||
|
@ -542,10 +491,8 @@ def vm_move(domain, target_node):
|
||||||
Permanently move virtual machine DOMAIN, via live migration if running and possible, to another node. DOMAIN may be a UUID or name.
|
Permanently move virtual machine DOMAIN, via live migration if running and possible, to another node. DOMAIN may be a UUID or name.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Open a Zookeeper connection
|
retcode, retmsg = pvc_vm.vm_node(config, domain, target_node, 'move', force=False)
|
||||||
zk_conn = pvc_common.startZKConnection(zk_host)
|
cleanup(retcode, retmsg)
|
||||||
retcode, retmsg = pvc_vm.move_vm(zk_conn, domain, target_node)
|
|
||||||
cleanup(retcode, retmsg, zk_conn)
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# pvc vm migrate
|
# pvc vm migrate
|
||||||
|
@ -567,10 +514,8 @@ def vm_migrate(domain, target_node, force_migrate):
|
||||||
Temporarily migrate running virtual machine DOMAIN, via live migration if possible, to another node. DOMAIN may be a UUID or name. If DOMAIN is not running, it will be started on the target node.
|
Temporarily migrate running virtual machine DOMAIN, via live migration if possible, to another node. DOMAIN may be a UUID or name. If DOMAIN is not running, it will be started on the target node.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Open a Zookeeper connection
|
retcode, retmsg = pvc_vm.vm_node(config, domain, target_node, 'migrate', force=force_migrate)
|
||||||
zk_conn = pvc_common.startZKConnection(zk_host)
|
cleanup(retcode, retmsg)
|
||||||
retcode, retmsg = pvc_vm.migrate_vm(zk_conn, domain, target_node, force_migrate, is_cli=True)
|
|
||||||
cleanup(retcode, retmsg, zk_conn)
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# pvc vm unmigrate
|
# pvc vm unmigrate
|
||||||
|
@ -584,10 +529,8 @@ def vm_unmigrate(domain):
|
||||||
Restore previously migrated virtual machine DOMAIN, via live migration if possible, to its original node. DOMAIN may be a UUID or name. If DOMAIN is not running, it will be started on the target node.
|
Restore previously migrated virtual machine DOMAIN, via live migration if possible, to its original node. DOMAIN may be a UUID or name. If DOMAIN is not running, it will be started on the target node.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Open a Zookeeper connection
|
retcode, retmsg = pvc_vm.vm_node(config, domain, None, 'unmigrate', force=False)
|
||||||
zk_conn = pvc_common.startZKConnection(zk_host)
|
cleanup(retcode, retmsg)
|
||||||
retcode, retmsg = pvc_vm.unmigrate_vm(zk_conn, domain)
|
|
||||||
cleanup(retcode, retmsg, zk_conn)
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# pvc vm flush-locks
|
# pvc vm flush-locks
|
||||||
|
@ -601,34 +544,8 @@ def vm_flush_locks(domain):
|
||||||
Flush stale RBD locks for virtual machine DOMAIN. DOMAIN may be a UUID or name. DOMAIN must be in a stopped state before flushing locks.
|
Flush stale RBD locks for virtual machine DOMAIN. DOMAIN may be a UUID or name. DOMAIN must be in a stopped state before flushing locks.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Open a Zookeeper connection
|
retcode, retmsg = pvc_vm.vm_locks(config, domain)
|
||||||
zk_conn = pvc_common.startZKConnection(zk_host)
|
cleanup(retcode, retmsg)
|
||||||
retcode, retmsg = pvc_vm.flush_locks(zk_conn, domain)
|
|
||||||
cleanup(retcode, retmsg, zk_conn)
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# pvc vm info
|
|
||||||
###############################################################################
|
|
||||||
@click.command(name='info', short_help='Show details of a VM object.')
|
|
||||||
@click.argument(
|
|
||||||
'domain'
|
|
||||||
)
|
|
||||||
@click.option(
|
|
||||||
'-l', '--long', 'long_output', is_flag=True, default=False,
|
|
||||||
help='Display more detailed information.'
|
|
||||||
)
|
|
||||||
def vm_info(domain, long_output):
|
|
||||||
"""
|
|
||||||
Show information about virtual machine DOMAIN. DOMAIN may be a UUID or name.
|
|
||||||
"""
|
|
||||||
|
|
||||||
zk_conn = pvc_common.startZKConnection(zk_host)
|
|
||||||
retcode, retdata = pvc_vm.get_info(zk_conn, domain)
|
|
||||||
if retcode:
|
|
||||||
pvc_vm.format_info(zk_conn, retdata, long_output)
|
|
||||||
retdata = ''
|
|
||||||
cleanup(retcode, retdata, zk_conn)
|
|
||||||
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# pvc vm log
|
# pvc vm log
|
||||||
|
@ -656,6 +573,50 @@ def vm_log(domain, lines, follow):
|
||||||
retcode, retmsg = pvc_vm.view_console_log(config, domain, lines)
|
retcode, retmsg = pvc_vm.view_console_log(config, domain, lines)
|
||||||
cleanup(retcode, retmsg)
|
cleanup(retcode, retmsg)
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# pvc vm info
|
||||||
|
###############################################################################
|
||||||
|
@click.command(name='info', short_help='Show details of a VM object.')
|
||||||
|
@click.argument(
|
||||||
|
'domain'
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
'-l', '--long', 'long_output', is_flag=True, default=False,
|
||||||
|
help='Display more detailed information.'
|
||||||
|
)
|
||||||
|
def vm_info(domain, long_output):
|
||||||
|
"""
|
||||||
|
Show information about virtual machine DOMAIN. DOMAIN may be a UUID or name.
|
||||||
|
"""
|
||||||
|
|
||||||
|
retcode, retdata = pvc_vm.vm_info(config, domain)
|
||||||
|
if retcode:
|
||||||
|
pvc_vm.format_info(config, retdata, long_output)
|
||||||
|
retdata = ''
|
||||||
|
cleanup(retcode, retdata)
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# pvc vm dump
|
||||||
|
###############################################################################
|
||||||
|
@click.command(name='dump', short_help='Dump a virtual machine XML to stdout.')
|
||||||
|
@click.argument(
|
||||||
|
'domain'
|
||||||
|
)
|
||||||
|
def vm_dump(domain):
|
||||||
|
"""
|
||||||
|
Dump the Libvirt XML definition of virtual machine DOMAIN to stdout. DOMAIN may be a UUID or name.
|
||||||
|
"""
|
||||||
|
|
||||||
|
retcode, vm_information = pvc_vm.vm_info(config, domain)
|
||||||
|
if not retcode and not vm_information.get('name', None):
|
||||||
|
cleanup(False, 'ERROR: Could not find VM "{}" in the cluster!'.format(domain))
|
||||||
|
|
||||||
|
# Grab the current config
|
||||||
|
current_vm_cfg_raw = vm_information.get('xml')
|
||||||
|
xml_data = etree.fromstring(current_vm_cfg_raw)
|
||||||
|
current_vm_cfgfile = etree.tostring(xml_data, pretty_print=True).decode('utf8')
|
||||||
|
click.echo(current_vm_cfgfile.strip())
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# pvc vm list
|
# pvc vm list
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
@ -682,12 +643,11 @@ def vm_list(target_node, target_state, limit, raw):
|
||||||
NOTE: Red-coloured network lists indicate one or more configured networks are missing/invalid.
|
NOTE: Red-coloured network lists indicate one or more configured networks are missing/invalid.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
zk_conn = pvc_common.startZKConnection(zk_host)
|
retcode, retdata = pvc_vm.vm_list(config, limit, target_node, target_state)
|
||||||
retcode, retdata = pvc_vm.get_list(zk_conn, target_node, target_state, limit)
|
|
||||||
if retcode:
|
if retcode:
|
||||||
pvc_vm.format_list(zk_conn, retdata, raw)
|
pvc_vm.format_list(config, retdata, raw)
|
||||||
retdata = ''
|
retdata = ''
|
||||||
cleanup(retcode, retdata, zk_conn)
|
cleanup(retcode, retdata)
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# pvc network
|
# pvc network
|
||||||
|
|
Loading…
Reference in New Issue