diff --git a/NodeInstance.py b/NodeInstance.py index 9c4a6965..87b8a6b4 100644 --- a/NodeInstance.py +++ b/NodeInstance.py @@ -214,7 +214,6 @@ class NodeInstance(): # Set our information in zookeeper self.name = conn.getHostname() - self.cpucount = conn.getCPUMap()[0] self.memused = int(psutil.virtual_memory().used / 1024 / 1024) self.memfree = int(psutil.virtual_memory().free / 1024 / 1024) self.cpuload = os.getloadavg()[0] @@ -222,7 +221,6 @@ class NodeInstance(): keepalive_time = int(time.time()) try: transaction = self.zk.transaction() - transaction.set_data('/nodes/{}/cpucount'.format(self.name), str(self.cpucount).encode('ascii')) transaction.set_data('/nodes/{}/memused'.format(self.name), str(self.memused).encode('ascii')) transaction.set_data('/nodes/{}/memfree'.format(self.name), str(self.memfree).encode('ascii')) transaction.set_data('/nodes/{}/cpuload'.format(self.name), str(self.cpuload).encode('ascii')) @@ -238,7 +236,7 @@ class NodeInstance(): # Display node information to the terminal ansiiprint.echo('{}{} keepalive{}'.format(ansiiprint.purple(), self.name, ansiiprint.end()), '', 't') - ansiiprint.echo('{0}Active domains:{1} {2} {0}CPUs:{1} {3} {0}Free memory [MiB]:{1} {4} {0}Used memory [MiB]:{1} {5} {0}Load:{1} {6}'.format(ansiiprint.bold(), ansiiprint.end(), self.domains_count, self.cpucount, self.memfree, self.memused, self.cpuload), '', 'c') + ansiiprint.echo('{0}Active domains:{1} {2} {0}Free memory [MiB]:{1} {3} {0}Used memory [MiB]:{1} {4} {0}Load:{1} {5}'.format(ansiiprint.bold(), ansiiprint.end(), self.domains_count, self.memfree, self.memused, self.cpuload), '', 'c') # Update our local node lists for node_name in self.t_node: diff --git a/pvc.py b/pvc.py index 45bb12e1..98e77545 100755 --- a/pvc.py +++ b/pvc.py @@ -127,18 +127,21 @@ def ready_host(node_name): ############################################################################### @click.command(name='info', short_help='Show details of a node object') @click.option( - '-n', '--name', 'dom_name', + '-n', '--name', 'node_name', help='Search for this name.' ) @click.option( '-l', '--long', 'long_output', is_flag=True, default=False, help='Display more detailed information.' ) -def node_info(dom_name, dom_uuid, long_output): +def node_info(node_name, long_output): """ Search the cluster for a node's information. """ + # Open a Zookeeper connection + zk = pvcf.startZKConnection(zk_host) + # Verify node is valid try: zk.get('/nodes/{}'.format(node_name)) @@ -146,7 +149,24 @@ def node_info(dom_name, dom_uuid, long_output): click.echo('ERROR: No node named {} is present in the cluster.'.format(node_name)) exit(1) - pass + # Get information about node in a pretty format + information = pvcf.getInformationFromNode(zk, node_name, long_output) + + if information == None: + click.echo('ERROR: Could not find a domain matching that name or UUID.') + return + + click.echo(information) + + if long_output == True: + click.echo('') + click.echo('{}Virtual machines on node:{}'.format(ansiiprint.bold(), ansiiprint.end())) + click.echo('') + # List all VMs on this node + _vm_list(node_name) + + # Close the Zookeeper connection + pvcf.stopZKConnection(zk) ############################################################################### @@ -178,7 +198,7 @@ def node_list(): for node_name in node_list: node_daemon_state[node_name] = zk.get('/nodes/{}/daemonstate'.format(node_name))[0].decode('ascii') node_domain_state[node_name] = zk.get('/nodes/{}/domainstate'.format(node_name))[0].decode('ascii') - node_cpu_count[node_name] = zk.get('/nodes/{}/cpucount'.format(node_name))[0].decode('ascii') + node_cpu_count[node_name] = zk.get('/nodes/{}/staticdata'.format(node_name))[0].decode('ascii').split()[0] node_mem_used[node_name] = zk.get('/nodes/{}/memused'.format(node_name))[0].decode('ascii') node_mem_free[node_name] = zk.get('/nodes/{}/memfree'.format(node_name))[0].decode('ascii') node_mem_total[node_name] = int(node_mem_used[node_name]) + int(node_mem_free[node_name]) @@ -836,6 +856,10 @@ def vm_info(dom_name, dom_uuid, long_output): help='Limit list to this hypervisor.' ) def vm_list(hypervisor): + vm_list(hypervisor) + +# Wrapped function to allow calling from `node info` +def _vm_list(hypervisor): """ List all virtual machines in the cluster. """ diff --git a/pvcd.py b/pvcd.py index 0e049ed5..d60be315 100755 --- a/pvcd.py +++ b/pvcd.py @@ -112,7 +112,6 @@ def zk_listener(state): pass zk.add_listener(zk_listener) -zk.set('/nodes/{}/daemonstate'.format(myhostname), 'init'.encode('ascii')) def cleanup(): try: @@ -125,12 +124,28 @@ def cleanup(): atexit.register(cleanup) +# Gather useful data about our host for staticdata +# Static data format: 'cpu_count', 'arch', 'os', 'kernel' +staticdata = [] +staticdata.append(str(psutil.cpu_count())) +staticdata.append(subprocess.run(['uname', '-r'], stdout=subprocess.PIPE).stdout.decode('ascii').strip()) +staticdata.append(subprocess.run(['uname', '-o'], stdout=subprocess.PIPE).stdout.decode('ascii').strip()) +staticdata.append(subprocess.run(['uname', '-m'], stdout=subprocess.PIPE).stdout.decode('ascii').strip()) +# Print static data on start + print('{0}Node hostname:{1} {2}'.format(ansiiprint.bold(), ansiiprint.end(), myhostname)) print('{0}IPMI hostname:{1} {2}'.format(ansiiprint.bold(), ansiiprint.end(), config['ipmi_hostname'])) +print('{0}Machine details:{1}'.format(ansiiprint.bold(), ansiiprint.end())) +print(' {0}CPUs:{1} {2}'.format(ansiiprint.bold(), ansiiprint.end(), staticdata[0])) +print(' {0}Arch:{1} {2}'.format(ansiiprint.bold(), ansiiprint.end(), staticdata[1])) +print(' {0}OS:{1} {2}'.format(ansiiprint.bold(), ansiiprint.end(), staticdata[2])) +print(' {0}Kernel:{1} {2}'.format(ansiiprint.bold(), ansiiprint.end(), staticdata[3])) # Check if our node exists in Zookeeper, and create it if not if zk.exists('/nodes/{}'.format(myhostname)): print("Node is " + ansiiprint.green() + "present" + ansiiprint.end() + " in Zookeeper") + # Update static data just in case it's changed + zk.set('/nodes/{}/staticdata'.format(myhostname), ' '.join(staticdata).encode('ascii')) else: print("Node is " + ansiiprint.red() + "absent" + ansiiprint.end() + " in Zookeeper; adding new node") keepalive_time = int(time.time()) @@ -138,8 +153,7 @@ else: # Basic state information zk.create('/nodes/{}/daemonstate'.format(myhostname), 'stop'.encode('ascii')) zk.create('/nodes/{}/domainstate'.format(myhostname), 'ready'.encode('ascii')) - zk.create('/nodes/{}/cpucount'.format(myhostname), '0'.encode('ascii')) - zk.create('/nodes/{}/staticdata'.format(myhostname), ''.encode('ascii')) + zk.create('/nodes/{}/staticdata'.format(myhostname), ' '.join(staticdata).encode('ascii')) zk.create('/nodes/{}/memfree'.format(myhostname), '0'.encode('ascii')) zk.create('/nodes/{}/memused'.format(myhostname), '0'.encode('ascii')) zk.create('/nodes/{}/cpuload'.format(myhostname), '0.0'.encode('ascii')) @@ -151,16 +165,7 @@ else: zk.create('/nodes/{}/ipmiusername'.format(myhostname), config['ipmi_username'].encode('ascii')) zk.create('/nodes/{}/ipmipassword'.format(myhostname), config['ipmi_password'].encode('ascii')) - -# Gather useful data about our host for staticdata -# Static data format: 'cpu_count', 'arch', 'kernel', 'os' -staticdata = [] -staticdata.append(str(psutil.cpu_count())) -staticdata.append(subprocess.run(['uname', '-r'], stdout=subprocess.PIPE).stdout.decode('ascii').strip()) -staticdata.append(subprocess.run(['uname', '-m'], stdout=subprocess.PIPE).stdout.decode('ascii').strip()) -staticdata.append(subprocess.run(['uname', '-o'], stdout=subprocess.PIPE).stdout.decode('ascii').strip()) - -zk.set('/nodes/{}/staticdata'.format(myhostname), ' '.join(staticdata).encode('ascii')) +zk.set('/nodes/{}/daemonstate'.format(myhostname), 'init'.encode('ascii')) t_node = dict() s_domain = dict() diff --git a/pvcf.py b/pvcf.py index 2934478d..56c2ce9f 100644 --- a/pvcf.py +++ b/pvcf.py @@ -146,7 +146,67 @@ def getDomainXML(zk, dom_uuid): parsed_xml = lxml.objectify.fromstring(xml) return parsed_xml -# Root function +# Root functions +def getInformationFromNode(zk, node_name, long_output): + node_daemon_state = zk.get('/nodes/{}/daemonstate'.format(node_name))[0].decode('ascii') + node_domain_state = zk.get('/nodes/{}/domainstate'.format(node_name))[0].decode('ascii') + node_cpu_count = zk.get('/nodes/{}/staticdata'.format(node_name))[0].decode('ascii').split()[0] + node_arch = zk.get('/nodes/{}/staticdata'.format(node_name))[0].decode('ascii').split()[1] + node_os = zk.get('/nodes/{}/staticdata'.format(node_name))[0].decode('ascii').split()[2] + node_kernel = zk.get('/nodes/{}/staticdata'.format(node_name))[0].decode('ascii').split()[3] + node_mem_used = zk.get('/nodes/{}/memused'.format(node_name))[0].decode('ascii') + node_mem_free = zk.get('/nodes/{}/memfree'.format(node_name))[0].decode('ascii') + node_mem_total = int(node_mem_used) + int(node_mem_free) + node_domains_count = zk.get('/nodes/{}/domainscount'.format(node_name))[0].decode('ascii') + node_running_domains = zk.get('/nodes/{}/runningdomains'.format(node_name))[0].decode('ascii').split() + node_mem_allocated = 0 + for domain in node_running_domains: + parsed_xml = pvcf.getDomainXML(zk, domain) + duuid, dname, dmemory, dvcpu, dvcputopo = pvcf.getDomainMainDetails(parsed_xml) + node_mem_allocated += int(dmemory) + + if node_daemon_state == 'run': + daemon_state_colour = ansiiprint.green() + elif node_daemon_state == 'stop': + daemon_state_colour = ansiiprint.red() + elif node_daemon_state == 'init': + daemon_state_colour = ansiiprint.yellow() + elif node_daemon_state == 'dead': + daemon_state_colour = ansiiprint.red() + ansiiprint.bold() + else: + daemon_state_colour = ansiiprint.blue() + + if node_domain_state == 'ready': + domain_state_colour = ansiiprint.green() + else: + domain_state_colour = ansiiprint.blue() + + # Format a nice output; do this line-by-line then concat the elements at the end + ainformation = [] + ainformation.append('{}Hypervisor Node information:{}'.format(ansiiprint.bold(), ansiiprint.end())) + ainformation.append('') + # Basic information + ainformation.append('{}Name:{} {}'.format(ansiiprint.purple(), ansiiprint.end(), node_name)) + ainformation.append('{}Daemon State:{} {}{}{}'.format(ansiiprint.purple(), ansiiprint.end(), daemon_state_colour, node_daemon_state, ansiiprint.end())) + ainformation.append('{}Domain State:{} {}{}{}'.format(ansiiprint.purple(), ansiiprint.end(), domain_state_colour, node_domain_state, ansiiprint.end())) + ainformation.append('{}Active Domain Count:{} {}'.format(ansiiprint.purple(), ansiiprint.end(), node_domains_count)) + if long_output == True: + ainformation.append('') + ainformation.append('{}Architecture:{} {}'.format(ansiiprint.purple(), ansiiprint.end(), node_arch)) + ainformation.append('{}Operating System:{} {}'.format(ansiiprint.purple(), ansiiprint.end(), node_os)) + ainformation.append('{}Kernel Version:{} {}'.format(ansiiprint.purple(), ansiiprint.end(), node_kernel)) + ainformation.append('') + ainformation.append('{}CPUs:{} {}'.format(ansiiprint.purple(), ansiiprint.end(), node_cpu_count)) + ainformation.append('{}Total RAM (MiB):{} {}'.format(ansiiprint.purple(), ansiiprint.end(), node_mem_total)) + ainformation.append('{}Used RAM (MiB):{} {}'.format(ansiiprint.purple(), ansiiprint.end(), node_mem_used)) + ainformation.append('{}Free RAM (MiB):{} {}'.format(ansiiprint.purple(), ansiiprint.end(), node_mem_free)) + ainformation.append('{}Allocated RAM (MiB):{} {}'.format(ansiiprint.purple(), ansiiprint.end(), node_mem_allocated)) + + # Join it all together + information = '\n'.join(ainformation) + return information + + def getInformationFromXML(zk, uuid, long_output): # Obtain the contents of the XML from Zookeeper try: