From 1b6613c2800438e944f94833c4ae127f116c5cf2 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 20 Dec 2020 16:00:55 -0500 Subject: [PATCH] Add live VNC information to domain output Sets in the node daemon, returns via the API, and shows in the CLI, information about the live VNC listen address and port for VNC-enabled VMs. Closes #115 --- api-daemon/pvcapid/flaskapi.py | 9 +++++++++ client-cli/cli_lib/vm.py | 5 +++++ daemon-common/common.py | 11 +++++++++++ daemon-common/vm.py | 1 + docs/manuals/swagger.json | 13 +++++++++++++ node-daemon/pvcnoded/VMInstance.py | 20 +++++++++++++++++++- 6 files changed, 58 insertions(+), 1 deletion(-) diff --git a/api-daemon/pvcapid/flaskapi.py b/api-daemon/pvcapid/flaskapi.py index a284be3b..1eff293f 100755 --- a/api-daemon/pvcapid/flaskapi.py +++ b/api-daemon/pvcapid/flaskapi.py @@ -1023,6 +1023,15 @@ class API_VM_Root(Resource): console: type: string descritpion: The serial console type of the VM + vnc: + type: object + properties: + listen: + type: string + description: The active VNC listen address or 'None' + port: + type: string + description: The active VNC port or 'None' emulator: type: string description: The binary emulator of the VM diff --git a/client-cli/cli_lib/vm.py b/client-cli/cli_lib/vm.py index 7d97986c..4512caf2 100644 --- a/client-cli/cli_lib/vm.py +++ b/client-cli/cli_lib/vm.py @@ -1071,6 +1071,11 @@ def format_info(config, domain_information, long_output): ainformation.append('{}vCPUs:{} {}'.format(ansiprint.purple(), ansiprint.end(), domain_information['vcpu'])) ainformation.append('{}Topology (S/C/T):{} {}'.format(ansiprint.purple(), ansiprint.end(), domain_information['vcpu_topology'])) + if domain_information['vnc'].get('listen', 'None') != 'None' and domain_information['vnc'].get('port', 'None') != 'None': + ainformation.append('') + ainformation.append('{}VNC listen:{} {}'.format(ansiprint.purple(), ansiprint.end(), domain_information['vnc']['listen'])) + ainformation.append('{}VNC port:{} {}'.format(ansiprint.purple(), ansiprint.end(), domain_information['vnc']['port'])) + if long_output is True: # Virtualization information ainformation.append('') diff --git a/daemon-common/common.py b/daemon-common/common.py index 61efa04d..668891e4 100644 --- a/daemon-common/common.py +++ b/daemon-common/common.py @@ -267,6 +267,13 @@ def getInformationFromXML(zk_conn, uuid): except Exception: domain_profile = None + try: + domain_vnc = zkhandler.readdata(zk_conn, '/domains/{}/vnc'.format(uuid)) + domain_vnc_listen, domain_vnc_port = domain_vnc.split(':') + except Exception: + domain_vnc_listen = 'None' + domain_vnc_port = 'None' + parsed_xml = getDomainXML(zk_conn, uuid) try: @@ -312,6 +319,10 @@ def getInformationFromXML(zk_conn, uuid): 'arch': domain_arch, 'machine': domain_machine, 'console': domain_console, + 'vnc': { + 'listen': domain_vnc_listen, + 'port': domain_vnc_port + }, 'emulator': domain_emulator, 'features': domain_features, 'disks': domain_disks, diff --git a/daemon-common/vm.py b/daemon-common/vm.py index c3e076b8..772560c1 100644 --- a/daemon-common/vm.py +++ b/daemon-common/vm.py @@ -207,6 +207,7 @@ def define_vm(zk_conn, config_data, target_node, node_limit, node_selector, node '/domains/{}/consolelog'.format(dom_uuid): '', '/domains/{}/rbdlist'.format(dom_uuid): formatted_rbd_list, '/domains/{}/profile'.format(dom_uuid): profile, + '/domains/{}/vnc'.format(dom_uuid): '', '/domains/{}/xml'.format(dom_uuid): config_data }) diff --git a/docs/manuals/swagger.json b/docs/manuals/swagger.json index fa194257..e6258169 100644 --- a/docs/manuals/swagger.json +++ b/docs/manuals/swagger.json @@ -1302,6 +1302,19 @@ "description": "The topology of the assigned vCPUs in Sockets/Cores/Threads format", "type": "string" }, + "vnc": { + "properties": { + "listen": { + "description": "The active VNC listen address or 'None'", + "type": "string" + }, + "port": { + "description": "The active VNC port or 'None'", + "type": "string" + } + }, + "type": "object" + }, "xml": { "description": "The raw Libvirt XML definition of the VM", "type": "string" diff --git a/node-daemon/pvcnoded/VMInstance.py b/node-daemon/pvcnoded/VMInstance.py index 350813f7..3a83ba81 100644 --- a/node-daemon/pvcnoded/VMInstance.py +++ b/node-daemon/pvcnoded/VMInstance.py @@ -27,6 +27,8 @@ import json from threading import Thread +from xml.etree import ElementTree + import pvcnoded.zkhandler as zkhandler import pvcnoded.common as common @@ -208,6 +210,21 @@ class VMInstance(object): except Exception as e: self.logger.out('Error removing domain from list: {}'.format(e), state='e') + # Update the VNC live data + def update_vnc(self): + if self.dom is not None: + live_xml = ElementTree.fromstring(self.dom.XMLDesc(0)) + graphics = live_xml.find('./devices/graphics') + if graphics is not None: + self.logger.out('Updating VNC data', state='i', prefix='Domain {}'.format(self.domuuid)) + port = graphics.get('port', '') + listen = graphics.get('listen', '') + zkhandler.writedata(self.zk_conn, {'/domains/{}/vnc'.format(self.domuuid): '{}:{}'.format(listen, port)}) + else: + zkhandler.writedata(self.zk_conn, {'/domains/{}/vnc'.format(self.domuuid): ''}) + else: + zkhandler.writedata(self.zk_conn, {'/domains/{}/vnc'.format(self.domuuid): ''}) + # Start up the VM def start_vm(self): # Start the log watcher @@ -739,7 +756,8 @@ class VMInstance(object): self.removeDomainFromList() # Stop the log watcher self.console_log_instance.stop() - + # Update the VNC information + self.update_vnc() else: # Conditional pass three - Is this VM currently running on this node if running == libvirt.VIR_DOMAIN_RUNNING: