· 8 years ago · Jan 20, 2018, 08:40 AM
1#!/usr/bin/env python
2## Gavin Purcell
3## Gather system resource info from VMware ESXi hosts and create totals by vSphere cluster.
4
5import paramiko
6import socket
7import sys
8import os
9import base64
10from multiprocessing import Process, Queue
11import resource
12from datetime import date
13import pickle
14from shutil import copyfile
15
16dir_path = os.path.dirname(os.path.abspath(__file__)) + '/'
17hosts_by_cluster_pickle = dir_path + 'hosts_by_cluster.pkl'
18html_head_file = dir_path + 'html_head.txt'
19
20port = 22
21user = 'root'
22password = base64.b64decode('') ## PASSWORD REMOVED
23get_sysinfo = 'timeout -t 3 esxcli hardware cpu global get | grep CPU | awk \'{print $3}\'; timeout -t 3 esxcli hardware memory get | grep "Physical Memory" | awk \'{print $3}\''
24expected_itmes_returned = 4
25
26html_title_placeholder = 'title not set'
27html_title = 'Vsphere Cluster Info'
28html_colors = ['#ffddcc', '#e6ccff', '#ffffcc', '#cce6ff']
29webpage_dir = '/var/www/html/'
30favicon_file_name = 'cloudopsvcli_icon.png'
31favicon_file = webpage_dir + favicon_file_name
32html_file_name = 'cluster_info'
33html_file = webpage_dir + html_file_name
34csv_file_name = 'cluster_info.csv'
35csv_file = webpage_dir + csv_file_name
36
37class color:
38 red = '\033[91m'
39 end = '\033[0m'
40
41## For converting from bytes to gigabytes.
42gigabyte_divisor = 1073741824
43
44## Check that the hosts_by_cluster_pickle file exists.
45if os.path.exists(hosts_by_cluster_pickle) == False:
46 print hosts_by_cluster_pickle, "does not exist."
47 sys.exit(1)
48
49## Output files to local directory if webpage_dir doesn't exist.
50if os.path.exists(webpage_dir) == False:
51 print webpage_dir, 'does not exist. Outputting to local directory.'
52 csv_file = dir_path + csv_file_name
53 html_file = dir_path + html_file_name
54
55## Set the ulimit to 2048
56soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
57resource.setrlimit(resource.RLIMIT_NOFILE, (2048, hard))
58
59def ssh_run_cmd(q, user, host, cmd):
60 """Connect and run the command on the host."""
61 ssh = paramiko.SSHClient()
62 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
63 try:
64 ssh.connect(host, port, user, password, timeout=3)
65 stdin, stdout, stderr = ssh.exec_command(cmd)
66 error_msg = stderr.read()
67 vms_on_host = stdout.read().split()
68 q.put(vms_on_host)
69 if error_msg: print error_msg
70 except (socket.error, paramiko.ssh_exception.BadAuthenticationType, paramiko.ssh_exception.SSHException) as e:
71 print '%sCannot connect to %s: %s%s' % (color.red, host, e, color.end)
72 q.put('')
73 finally:
74 ssh.close()
75
76## Get hosts by cluster:
77hosts_by_cluster = {}
78with open(hosts_by_cluster_pickle, 'r') as pickle_file:
79 hosts_by_cluster = pickle.load(pickle_file)
80
81## Create and start processes for getting sysinfo by cluster in parallel.
82jobs = []
83sysinfo_by_host = {}
84for cluster in hosts_by_cluster.keys():
85 for host in hosts_by_cluster[cluster]:
86 sysinfo_by_host[host] = []
87 q = Queue()
88 process = Process(target=ssh_run_cmd, args=(q,user,host,get_sysinfo))
89 jobs.append((process,q,cluster,host))
90 process.start()
91
92## Wait for the processes to finish and append to the lists of vm's.
93for job in jobs:
94 process,q,cluster,host = job[0],job[1],job[2],job[3]
95 print "Waiting for:", cluster, host
96 sysinfo = q.get()
97 process.join()
98 sysinfo_by_host[host].extend(sysinfo)
99
100## Create a table of clusters.
101table_header = ['Cluster','Hosts(up)','Sockets','Cores','Threads','Memory(gigibytes)', 'Hosts(down)']
102table = []
103
104for cluster in hosts_by_cluster.keys():
105 host_cnt,socket_cnt,core_cnt,thread_cnt,memory_cnt,host_down_cnt = [0,0,0,0,0,0]
106 for host in hosts_by_cluster[cluster]:
107 if len(sysinfo_by_host[host]) >= expected_itmes_returned: ## Some hosts are down and don't return anyting.
108 sockets,cores,threads,memory = sysinfo_by_host[host]
109 host_cnt += 1
110 socket_cnt += int(sockets)
111 core_cnt += int(cores)
112 thread_cnt += int(threads)
113 memory_cnt += int(round(float(memory) / gigabyte_divisor))
114 else:
115 host_down_cnt += 1
116 table.append([cluster,host_cnt,socket_cnt,core_cnt,thread_cnt,memory_cnt,host_down_cnt])
117
118table.sort()
119
120## Map html colors to clusters.
121cluster_colors = {}
122for row in table:
123 cluster = row[0]
124 cluster_colors[cluster] = html_colors[0]
125 if 'C02' in cluster:
126 cluster_colors[cluster] = html_colors[1]
127 if 'C03' in cluster:
128 cluster_colors[cluster] = html_colors[2]
129 if 'C09' in cluster:
130 cluster_colors[cluster] = html_colors[3]
131
132## Write out csv file.
133with open(csv_file, 'w') as out_file:
134 table_csv = [table_header] + table
135 for row in table_csv:
136 cluster,host_cnt,socket_cnt,core_cnt,thread_cnt,memory_cnt,host_down_cnt = row
137 line = '%s,%s,%s,%s,%s,%s,%s\n' % (cluster,host_cnt,socket_cnt,core_cnt,thread_cnt,memory_cnt,host_down_cnt)
138 out_file.write(line)
139
140## Check that the html_head file exists:
141if os.path.exists(html_head_file) == False:
142 print html_head_file, "does not exist."
143 sys.exit(2)
144
145## Read in the html head file and set the title.
146with open(html_head_file, 'r') as in_file:
147 html = in_file.read()
148html = html.replace(html_title_placeholder, html_title, 1)
149
150## Create the html for output.
151date = str(date.today())
152html = html + '<body>\n\n'
153html = html + '<table>\n'
154cluster,host_cnt,socket_cnt,core_cnt,thread_cnt,memory_cnt,host_down_cnt = table_header
155html = html + '<tr>\n'
156html = html + '<th>' + cluster + '</th>\n'
157html = html + '<th>' + host_cnt + '</th>\n'
158html = html + '<th>' + socket_cnt + '</th>\n'
159html = html + '<th>' + core_cnt + '</th>\n'
160html = html + '<th>' + thread_cnt + '</th>\n'
161html = html + '<th>' + memory_cnt + '</th>\n'
162html = html + '<th>' + host_down_cnt + '</th>\n'
163html = html + '</tr>\n'
164for row in table:
165 cluster,host_cnt,socket_cnt,core_cnt,thread_cnt,memory_cnt,host_down_cnt = row
166 html = html + '<tr bgcolor="' + cluster_colors[cluster] + '">\n'
167 html = html + '<td>' + cluster + '</td>\n'
168 html = html + '<td>' + str(host_cnt) + '</td>\n'
169 html = html + '<td>' + str(socket_cnt) + '</td>\n'
170 html = html + '<td>' + str(core_cnt) + '</td>\n'
171 html = html + '<td>' + str(thread_cnt) + '</td>\n'
172 html = html + '<td>' + str(memory_cnt) + '</td>\n'
173 html = html + '<td>' + str(host_down_cnt) + '</td>\n'
174 html = html + '</tr>\n'
175html = html + '</table>\n\n'
176html = html + '<br>\n'
177html = html + '<a href="' + csv_file_name + '">' + csv_file_name + '</a>\n'
178html = html + '<br>\n'
179html = html + '<p>' + 'Last updated ' + date + '</p>\n'
180html = html + '</body>\n'
181html = html + '</html>'
182
183## Check that the favicon file exists and copy it to the web page directory:
184if os.path.exists(favicon_file_name) == False:
185 print favicon_file_name, "does not exist."
186else:
187 copyfile(favicon_file_name, favicon_file)
188
189## Write out html file.
190with open(html_file, 'w') as out_file:
191 out_file.write(html)