Implement vCPU modification on the CLI
Adds functions for listing and setting the vCPU and topology values from the CLI, without editing the XML directly. References #101
This commit is contained in:
		@@ -254,6 +254,133 @@ def vm_locks(config, vm):
 | 
				
			|||||||
    return retstatus, response.json().get('message', '')
 | 
					    return retstatus, response.json().get('message', '')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def vm_vcpus_set(config, vm, vcpus, topology, restart):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Set the vCPU count of the VM with topology
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Calls vm_info to get the VM XML.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Calls vm_modify to set the VM XML.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    from lxml.objectify import fromstring
 | 
				
			||||||
 | 
					    from lxml.etree import tostring
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    status, domain_information = vm_info(config, vm)
 | 
				
			||||||
 | 
					    if not status:
 | 
				
			||||||
 | 
					        return status, domain_information
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    xml = domain_information.get('xml', None)
 | 
				
			||||||
 | 
					    if xml is None:
 | 
				
			||||||
 | 
					        return False, "VM does not have a valid XML doccument."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        parsed_xml = fromstring(xml)
 | 
				
			||||||
 | 
					    except Exception:
 | 
				
			||||||
 | 
					        return False, 'ERROR: Failed to parse XML data.'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    parsed_xml.vcpu._setText(str(vcpus))
 | 
				
			||||||
 | 
					    parsed_xml.cpu.topology.set('sockets', str(topology[0]))
 | 
				
			||||||
 | 
					    parsed_xml.cpu.topology.set('cores', str(topology[1]))
 | 
				
			||||||
 | 
					    parsed_xml.cpu.topology.set('threads', str(topology[2]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        new_xml = tostring(parsed_xml, pretty_print=True)
 | 
				
			||||||
 | 
					    except Exception:
 | 
				
			||||||
 | 
					        return False, 'ERROR: Failed to dump XML data.'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return vm_modify(config, vm, new_xml, restart)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def vm_vcpus_get(config, vm):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Get the vCPU count of the VM
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Calls vm_info to get VM XML.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Returns a tuple of (vcpus, (sockets, cores, threads))
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    from lxml.objectify import fromstring
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    status, domain_information = vm_info(config, vm)
 | 
				
			||||||
 | 
					    if not status:
 | 
				
			||||||
 | 
					        return status, domain_information
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    xml = domain_information.get('xml', None)
 | 
				
			||||||
 | 
					    if xml is None:
 | 
				
			||||||
 | 
					        return False, "VM does not have a valid XML doccument."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        parsed_xml = fromstring(xml)
 | 
				
			||||||
 | 
					    except Exception:
 | 
				
			||||||
 | 
					        return False, 'ERROR: Failed to parse XML data.'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    vm_vcpus = int(parsed_xml.vcpu.text)
 | 
				
			||||||
 | 
					    vm_sockets = parsed_xml.cpu.topology.attrib.get('sockets')
 | 
				
			||||||
 | 
					    vm_cores = parsed_xml.cpu.topology.attrib.get('cores')
 | 
				
			||||||
 | 
					    vm_threads = parsed_xml.cpu.topology.attrib.get('threads')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return True, (vm_vcpus, (vm_sockets, vm_cores, vm_threads))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def format_vm_vcpus(config, name, vcpus):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Format the output of a vCPU value in a nice table
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    output_list = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    name_length = 5
 | 
				
			||||||
 | 
					    _name_length = len(name) + 1
 | 
				
			||||||
 | 
					    if _name_length > name_length:
 | 
				
			||||||
 | 
					        name_length = _name_length
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    vcpus_length = 6
 | 
				
			||||||
 | 
					    sockets_length = 8
 | 
				
			||||||
 | 
					    cores_length = 6
 | 
				
			||||||
 | 
					    threads_length = 8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    output_list.append(
 | 
				
			||||||
 | 
					        '{bold}{name: <{name_length}}  \
 | 
				
			||||||
 | 
					{vcpus: <{vcpus_length}}   \
 | 
				
			||||||
 | 
					{sockets: <{sockets_length}} \
 | 
				
			||||||
 | 
					{cores: <{cores_length}} \
 | 
				
			||||||
 | 
					{threads: <{threads_length}}{end_bold}'.format(
 | 
				
			||||||
 | 
					            name_length=name_length,
 | 
				
			||||||
 | 
					            vcpus_length=vcpus_length,
 | 
				
			||||||
 | 
					            sockets_length=sockets_length,
 | 
				
			||||||
 | 
					            cores_length=cores_length,
 | 
				
			||||||
 | 
					            threads_length=threads_length,
 | 
				
			||||||
 | 
					            bold=ansiprint.bold(),
 | 
				
			||||||
 | 
					            end_bold=ansiprint.end(),
 | 
				
			||||||
 | 
					            name='Name',
 | 
				
			||||||
 | 
					            vcpus='vCPUs',
 | 
				
			||||||
 | 
					            sockets='Sockets',
 | 
				
			||||||
 | 
					            cores='Cores',
 | 
				
			||||||
 | 
					            threads='Threads'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    output_list.append(
 | 
				
			||||||
 | 
					        '{bold}{name: <{name_length}}  \
 | 
				
			||||||
 | 
					{vcpus: <{vcpus_length}}   \
 | 
				
			||||||
 | 
					{sockets: <{sockets_length}} \
 | 
				
			||||||
 | 
					{cores: <{cores_length}} \
 | 
				
			||||||
 | 
					{threads: <{threads_length}}{end_bold}'.format(
 | 
				
			||||||
 | 
					            name_length=name_length,
 | 
				
			||||||
 | 
					            vcpus_length=vcpus_length,
 | 
				
			||||||
 | 
					            sockets_length=sockets_length,
 | 
				
			||||||
 | 
					            cores_length=cores_length,
 | 
				
			||||||
 | 
					            threads_length=threads_length,
 | 
				
			||||||
 | 
					            bold=ansiprint.bold(),
 | 
				
			||||||
 | 
					            end_bold=ansiprint.end(),
 | 
				
			||||||
 | 
					            name=name,
 | 
				
			||||||
 | 
					            vcpus=vcpus[0],
 | 
				
			||||||
 | 
					            sockets=vcpus[1][0],
 | 
				
			||||||
 | 
					            cores=vcpus[1][1],
 | 
				
			||||||
 | 
					            threads=vcpus[1][2]
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    return '\n'.join(output_list)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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 in the main CLI)
 | 
					    Return console log lines from the API (and display them in a pager in the main CLI)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1036,6 +1036,72 @@ def vm_vcpu():
 | 
				
			|||||||
    pass
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					###############################################################################
 | 
				
			||||||
 | 
					# pvc vm vcpu get
 | 
				
			||||||
 | 
					###############################################################################
 | 
				
			||||||
 | 
					@click.command(name='get', short_help='Get the current vCPU count of a virtual machine.')
 | 
				
			||||||
 | 
					@click.argument(
 | 
				
			||||||
 | 
					    'domain'
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					@click.option(
 | 
				
			||||||
 | 
					    '-r', '--raw', 'raw', is_flag=True, default=False,
 | 
				
			||||||
 | 
					    help='Display the raw value only without formatting.'
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					@cluster_req
 | 
				
			||||||
 | 
					def vm_vcpu_get(domain, raw):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Get the current vCPU count of the virtual machine DOMAIN.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    retcode, retmsg = pvc_vm.vm_vcpus_get(config, domain)
 | 
				
			||||||
 | 
					    if not raw:
 | 
				
			||||||
 | 
					        retmsg = pvc_vm.format_vm_vcpus(config, domain, retmsg)
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        retmsg = retmsg[0]  # Get only the first part of the tuple (vm_vcpus)
 | 
				
			||||||
 | 
					    cleanup(retcode, retmsg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					###############################################################################
 | 
				
			||||||
 | 
					# pvc vm vcpu set
 | 
				
			||||||
 | 
					###############################################################################
 | 
				
			||||||
 | 
					@click.command(name='set', short_help='Set the vCPU count of a virtual machine.')
 | 
				
			||||||
 | 
					@click.argument(
 | 
				
			||||||
 | 
					    'domain'
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					@click.argument(
 | 
				
			||||||
 | 
					    'vcpus'
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					@click.option(
 | 
				
			||||||
 | 
					    '-t', '--topology', 'topology', default=None,
 | 
				
			||||||
 | 
					    help='Use an alternative topology for the vCPUs in the CSV form <sockets>,<cores>,<threads>. SxCxT must equal VCPUS.'
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					@click.option(
 | 
				
			||||||
 | 
					    '-r', '--restart', 'restart', is_flag=True, default=False,
 | 
				
			||||||
 | 
					    help='Immediately restart VM to apply new config.'
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					@cluster_req
 | 
				
			||||||
 | 
					def vm_vcpu_set(domain, vcpus, topology, restart):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Set the vCPU count of the virtual machine DOMAIN to VCPUS.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    By default, the topology of the vCPus is 1 socket, VCPUS cores per socket, 1 thread per core.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if topology is not None:
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            sockets, cores, threads = topology.split(',')
 | 
				
			||||||
 | 
					            if sockets * cores * threads != vcpus:
 | 
				
			||||||
 | 
					                raise
 | 
				
			||||||
 | 
					        except Exception:
 | 
				
			||||||
 | 
					            cleanup(False, "The topology specified is not valid.")
 | 
				
			||||||
 | 
					        topology = (sockets, cores, threads)
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        topology = (1, vcpus, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    retcode, retmsg = pvc_vm.vm_vcpus_set(config, domain, vcpus, topology, restart)
 | 
				
			||||||
 | 
					    cleanup(retcode, retmsg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
###############################################################################
 | 
					###############################################################################
 | 
				
			||||||
# pvc vm memory
 | 
					# pvc vm memory
 | 
				
			||||||
###############################################################################
 | 
					###############################################################################
 | 
				
			||||||
@@ -3898,8 +3964,8 @@ cli_node.add_command(node_unflush)
 | 
				
			|||||||
cli_node.add_command(node_info)
 | 
					cli_node.add_command(node_info)
 | 
				
			||||||
cli_node.add_command(node_list)
 | 
					cli_node.add_command(node_list)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# vm_vcpu.add_command(vm_vcpu_get)
 | 
					vm_vcpu.add_command(vm_vcpu_get)
 | 
				
			||||||
# vm_vcpu.add_command(vm_vcpu_set)
 | 
					vm_vcpu.add_command(vm_vcpu_set)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# vm_memory.add_command(vm_memory_get)
 | 
					# vm_memory.add_command(vm_memory_get)
 | 
				
			||||||
# vm_memory.add_command(vm_memory_set)
 | 
					# vm_memory.add_command(vm_memory_set)
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user