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: