├── .gitignore ├── README.md ├── check_xenserver.py ├── config.ini └── parse_rrd.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | __pycache__ 21 | 22 | # Installer logs 23 | pip-log.txt 24 | 25 | # Unit test / coverage reports 26 | .coverage 27 | .tox 28 | nosetests.xml 29 | 30 | # Translations 31 | *.mo 32 | 33 | # Mr Developer 34 | .mr.developer.cfg 35 | .project 36 | .pydevproject 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | nagios-xenserver 2 | ================ 3 | 4 | Nagios check plugin for xenserver 5 | --------------------------------- 6 | 7 | Usage: ./check_xenserver.py [warning level %] [critical level %] 8 | see online help for more informations 9 | 10 | Config file follows ini format. In the [general] section, define the performance data format : can be "pnp4nagios" or other. 11 | The name of the other sections are references, mandatory fields are : 12 | - host: ip or name of the pool master 13 | - username 14 | - password 15 | - exclude_SRs: 16 | 17 | Example : 18 | ``` 19 | [general] 20 | perfdata_format: pnp4nagios 21 | 22 | [Production1] 23 | host: xenserver-prod01.servers.domain.com 24 | username: nagios 25 | password: nagios 26 | exclude_srs: SR1, SR2 27 | 28 | [Dev] 29 | host: 192.168.0.1 30 | username: nagios 31 | password: nagios 32 | ``` 33 | - Uses https to connect to XenServer, if you have a pool, use a poolmaster IP/FQDN 34 | - Uses (python) XenAPI, download it from XenServer http://www.xenserver.org/partners/developing-products-for-xenserver.html and parse_rrd 35 | 36 | Credit for most of the code goes to ppanula, check http://exchange.nagios.org/directory/Plugins/System-Metrics/Storage-Subsystem/check_sr-2Epy/details for original code 37 | 38 | Dated: 03/05/2014 39 | Version: 1.3 40 | 41 | Version history: 42 | ---------------- 43 | - v1.0: Initial release 44 | - v1.1: Config file support + return code for check_hosts 45 | - v1.2: Bug fixes : return status for SRs and Mem, perfdata format 46 | Features : service output for SRs and Mem, 47 | - v1.3: Rewrite of the argument parsing 48 | Code refactoring 49 | Ability to check a single SR 50 | 51 | Todo: 52 | ----- 53 | - Add VMs status check 54 | - Add Multipath enable checking 55 | -------------------------------------------------------------------------------- /check_xenserver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Check XenServer 4 | # (c) copyright Julien Garet 5 | # Licence: GPLv2 or later 6 | # Author: Julien Garet email: julien@garet.info 7 | # Contact if bugs, etc. 8 | # 9 | # Usage: ./check_xenserver.py 10 | # 11 | # - Uses http/s to connect to XenServer, if you have a pool, use a poolmaster IP/FQDN 12 | # - Uses (python) XenAPI, download it from XenServer http://www.xenserver.org/partners/developing-products-for-xenserver.html and parse_rrd 13 | # 14 | # Credit for most of the code goes to ppanula, check http://exchange.nagios.org/directory/Plugins/System-Metrics/Storage-Subsystem/check_sr-2Epy/details for original code 15 | # 16 | # Dated: 10/02/2013 17 | # Version: 1.2 18 | # 19 | # Version history: 20 | # - v1.0: Initial release 21 | # - v1.1: Config file support + return code for check_hosts 22 | # - v1.2: Bug fixes : return status for SRs and Mem, perfdata format 23 | # Features : service output for SRs and Mem, 24 | # 25 | # nagios command definition: 26 | # 27 | # define command{ 28 | # command_name check_xenserver_sr 29 | # command_line $USER1$/check_xenserver.py $ARG1$ "$USER15$" "$USER16$" "$ARG2$" $ARG3$ check_sr 30 | # } 31 | # 32 | # define command{ 33 | # command_name check_xenserver_mem 34 | # command_line $USER1$/check_xenserver.py $ARG1$ "$USER15$" "$USER16$" "$ARG2$" $ARG3$ check_mem 35 | # } 36 | # 37 | # USER16 and USER15 are username and password in resource.cfg 38 | 39 | from __future__ import division 40 | import sys, time, atexit 41 | import XenAPI 42 | 43 | def logout(): 44 | try: 45 | session.xenapi.session.logout() 46 | except: 47 | pass 48 | 49 | atexit.register(logout) 50 | 51 | def humanize_bytes(bytes, precision=2, suffix=True, format="pnp4nagios"): 52 | 53 | if format == "pnp4nagios": 54 | abbrevs = ( 55 | (1<<30L, 'Gb'), 56 | (1<<20L, 'Mb'), 57 | (1<<10L, 'kb'), 58 | (1, 'b') 59 | ) 60 | else: 61 | abbrevs = ( 62 | (1<<50L, 'P'), 63 | (1<<40L, 'T'), 64 | (1<<30L, 'G'), 65 | (1<<20L, 'M'), 66 | (1<<10L, 'k'), 67 | (1, 'b') 68 | ) 69 | 70 | if bytes == 1: 71 | return '1 b' 72 | for factor, _suffix in abbrevs: 73 | if bytes >= factor: 74 | break 75 | 76 | if suffix: 77 | return '%.*f%s' % (precision, bytes / factor, _suffix) 78 | else: 79 | return '%.*f' % (precision, bytes / factor) 80 | 81 | def performancedata(sr_name, suffix, total, alloc, warning, critical, performancedata_format="pnp4nagios"): 82 | 83 | if performancedata_format == "pnp4nagios": 84 | performance_line = "'"+sr_name + suffix + "'=" + \ 85 | str(humanize_bytes(alloc, precision=1, suffix=True, format=performancedata_format)).replace(".",",") + ";" + \ 86 | str(humanize_bytes(warning, precision=1, suffix=True, format=performancedata_format)).replace(".",",") + ";" + \ 87 | str(humanize_bytes(critical, precision=1, suffix=True, format=performancedata_format)).replace(".",",") + ";0.00;" + \ 88 | str(humanize_bytes(total, precision=1, suffix=True, format=performancedata_format)).replace(".",",") +"" 89 | else: 90 | performance_line = "'"+sr_name + suffix + "'=" + \ 91 | str(alloc).replace(".",",") + "B;" + \ 92 | str(warning).replace(".",",")+ ";" + \ 93 | str(critical).replace(".",",") + ";0;" + \ 94 | str(total).replace(".",",") +"" 95 | 96 | return(performance_line) 97 | 98 | def compute(name, size, util, free, warning, critical, performancedata_format, format_suffix): 99 | 100 | total_bytes_b = int(size) 101 | total_alloc_b = int(util) 102 | free_space_b = int(free) 103 | used_percent = 100*float(total_alloc_b)/float(total_bytes_b) 104 | warning_b = int((int(total_bytes_b) / 100) * float(warning)) 105 | critical_b = int((float(total_bytes_b) / 100) * float(critical)) 106 | 107 | info = {} 108 | info['performance'] = performancedata(name, format_suffix, 109 | total_bytes_b, 110 | total_alloc_b, 111 | warning_b, 112 | critical_b, 113 | performancedata_format) 114 | 115 | info['service'] = "%s %s%%, size %s, used %s, free %s" % (name, 116 | str(round(used_percent,2)), 117 | str(humanize_bytes(total_bytes_b, precision=0)), 118 | str(humanize_bytes(total_alloc_b, precision=0)), 119 | str(humanize_bytes(free_space_b, precision=0)) 120 | ) 121 | 122 | return (used_percent, info, total_bytes_b, total_alloc_b) 123 | 124 | def sr(session, sr_name, warning, critical, performancedata_format): 125 | 126 | sr = session.xenapi.SR.get_by_name_label(sr_name) 127 | if sr: 128 | sr_size = session.xenapi.SR.get_physical_size(sr[0]) 129 | sr_phys_util = session.xenapi.SR.get_physical_utilisation(sr[0]) 130 | sr_virtual_alloc = session.xenapi.SR.get_virtual_allocation(sr[0]) 131 | 132 | used_percent, outputdata , total, alloc = compute(sr_name, sr_size, sr_phys_util, str(int(sr_size) - int(sr_phys_util)), warning, critical, performancedata_format, "_used_space") 133 | 134 | if float(used_percent) >= float(critical): 135 | status = "CRITICAL: SR "+ sr_name 136 | exitcode = 2 137 | elif float(used_percent) >= float(warning): 138 | status = "WARNING: SR "+ sr_name 139 | exitcode = 1 140 | else: 141 | status = "OK: SR "+ sr_name 142 | exitcode = 0 143 | 144 | return(exitcode, status, outputdata['service'], outputdata['performance'], total, alloc) 145 | 146 | else: 147 | print "CRITICAL: Cant get SR, check SR name! SR =", sr_name 148 | sys.exit(2) 149 | 150 | def check_sr(session, args): 151 | 152 | warning = args["warning"] 153 | critical = args["critical"] 154 | exclude_srs = args["exclude_srs"] 155 | performancedata_format = args["perfdata"] 156 | 157 | finalexit = 0 158 | output = {} 159 | total_disk = 0 160 | total_alloc = 0 161 | critical_srs = [] 162 | warning_srs = [] 163 | 164 | if not args['sr_name']: 165 | sr_list = session.xenapi.SR.get_all() 166 | else: 167 | sr_list = session.xenapi.SR.get_by_name_label(args['sr_name']) 168 | 169 | for cur_sr in sr_list: 170 | sr_name = session.xenapi.SR.get_name_label(cur_sr) 171 | if sr_name not in exclude_srs: 172 | if session.xenapi.SR.get_shared(cur_sr) and session.xenapi.SR.get_type(cur_sr) != 'iso': 173 | exitcode, status, servicedata, perfdata, total, alloc = sr(session, sr_name, warning, critical, performancedata_format) 174 | if exitcode > finalexit: 175 | finalexit = exitcode 176 | 177 | if exitcode == 2: 178 | critical_srs.append(sr_name) 179 | if exitcode == 1: 180 | warning_srs.append(sr_name) 181 | 182 | output[sr_name] = {} 183 | output[sr_name]['service'] = servicedata 184 | output[sr_name]['perf'] = perfdata 185 | 186 | total_disk += total 187 | total_alloc += alloc 188 | 189 | performance = performancedata("Total", "_used_space", 190 | total_disk, 191 | total_alloc, 192 | (total_disk/100)*float(warning), 193 | (total_disk/100)*float(critical), 194 | performancedata_format) 195 | 196 | 197 | if finalexit == 2: 198 | prefix = "CRITICAL: SR Space" 199 | prefix += " / Critical SRs = ["+", ".join(critical_srs)+"]" 200 | prefix += " / Warning SRs = ["+", ".join(warning_srs)+"]" 201 | elif finalexit == 1: 202 | prefix = "WARNING: SR Space" 203 | prefix += " / Warning SRs = ["+", ".join(warning_srs)+"]" 204 | else: 205 | prefix = "OK: SR Space" 206 | 207 | 208 | print prefix + ' | ' + performance + "\n" + ";\n".join([output[disk_srs]['service'] for disk_srs in output]) + "; | " + " ".join([output[disk_srs]['perf'] for disk_srs in output]) 209 | 210 | sys.exit(finalexit) 211 | 212 | def mem(session, host, warning, critical, performancedata_format): 213 | 214 | if host: 215 | hostname = session.xenapi.host.get_name_label(host) 216 | mem_size = session.xenapi.host_metrics.get_record(session.xenapi.host.get_record(host)['metrics'])['memory_total'] 217 | mem_free = session.xenapi.host_metrics.get_record(session.xenapi.host.get_record(host)['metrics'])['memory_free'] 218 | 219 | used_percent, outputdata , total, alloc = compute(hostname, mem_size, str(int(mem_size) - int(mem_free)), mem_free, warning, critical, performancedata_format, "_used_mem") 220 | 221 | if float(used_percent) >= float(critical): 222 | status = "CRITICAL: MEM "+ hostname 223 | exitcode = 2 224 | elif float(used_percent) >= float(warning): 225 | status = "WARNING: MEM "+ hostname 226 | exitcode = 1 227 | else: 228 | status = "OK: MEM "+ hostname 229 | exitcode = 0 230 | 231 | return(exitcode, status, outputdata['service'], outputdata['performance'], total, alloc) 232 | 233 | else: 234 | print "CRITICAL: Cant get mem, check configuration" 235 | sys.exit(3) 236 | 237 | 238 | def check_mem(session, args): 239 | 240 | warning =args["warning"] 241 | critical = args["critical"] 242 | performancedata_format = args["perfdata"] 243 | 244 | # Initialiaze local variables 245 | finalexit = 0 246 | output = {} 247 | total_mem = 0 248 | total_used = 0 249 | critical_hosts = [] 250 | warning_hosts = [] 251 | 252 | hosts = session.xenapi.host.get_all() 253 | for host in hosts: 254 | hostname = session.xenapi.host.get_name_label(host) 255 | exitcode, status, servicedata, perfdata, total, used = mem(session, host, warning, critical, performancedata_format) 256 | if exitcode > finalexit: 257 | finalexit = exitcode 258 | 259 | if exitcode == 2 : 260 | critical_hosts.append(hostname) 261 | 262 | if exitcode == 1 : 263 | warning_hosts.append(hostname) 264 | 265 | output[hostname] = {} 266 | output[hostname]['service'] = servicedata 267 | output[hostname]['perf'] = perfdata 268 | 269 | total_mem += total 270 | total_used += used 271 | 272 | performance = performancedata("Total", "_mem_used", 273 | total_mem, 274 | total_used, 275 | (total_mem/100)*float(warning), 276 | (total_mem/100)*float(critical), 277 | performancedata_format) 278 | 279 | 280 | if finalexit == 2: 281 | prefix = "CRITICAL: Memory Usage " 282 | prefix += " / Critical on Hosts = ["+", ".join(critical_hosts)+"]" 283 | prefix += " / Warning on Hosts = ["+", ".join(warning_hosts)+"]" 284 | elif finalexit == 1: 285 | prefix = "WARNING: Memory Usage" 286 | prefix += " / Warning on Hosts = ["+", ".join(warning_hosts)+"]" 287 | else: 288 | prefix = "OK: Memory Usage" 289 | 290 | print prefix + " | " + performance + "\n" + ";\n".join([output[hostname]['service'] for hostname in output]) + "; | " + " ".join([output[hostname]['perf'] for hostname in output]) 291 | 292 | sys.exit(finalexit) 293 | 294 | def check_hosts(session, args): 295 | #work out which hosts in the pool are alive, and which dead 296 | hosts=session.xenapi.host.get_all() 297 | hosts_with_status=[(session.xenapi.host.get_name_label(x),session.xenapi.host_metrics.get_live( session.xenapi.host.get_metrics(x) )) for x in hosts] 298 | 299 | live_hosts=[name for (name,status) in hosts_with_status if (status==True)] 300 | dead_hosts=[name for (name,status) in hosts_with_status if not (status==True)] 301 | status="" 302 | if len(live_hosts) == 0: 303 | status = "Critical" 304 | exit = 2 305 | elif len(dead_hosts) > 1: 306 | status = "Warning" 307 | exit = 1 308 | else: 309 | status = "OK" 310 | exit = 0 311 | print status, ": live hosts", live_hosts, "dead hosts", dead_hosts, 312 | 313 | sys.exit(exit) 314 | 315 | def check_cpu(session, args): 316 | 317 | warning = args["warning"] 318 | critical = args["critical"] 319 | 320 | import parse_rrd 321 | params = {} 322 | hosts = session.xenapi.host.get_all_records() 323 | 324 | params['cf'] = "AVERAGE" 325 | params['start'] = int(time.time()) - 300 326 | params['interval'] = 5 327 | params['host'] = "true" 328 | 329 | perfdata = {} 330 | for host in hosts: 331 | v= [] 332 | url = scheme+session.xenapi.host.get_address(host) 333 | rrd_updates = parse_rrd.RRDUpdates() 334 | rrd_updates.refresh(session.handle, params, url) 335 | paramList = ['cpu'+session.xenapi.host_cpu.get_record(i)['number'] for i in session.xenapi.host_cpu.get_all_records() if host in session.xenapi.host_cpu.get_record(i)['host'] ] 336 | for param in rrd_updates.get_host_param_list(): 337 | if param in paramList: 338 | max_time=0 339 | data = "" 340 | for row in range(rrd_updates.get_nrows()): 341 | epoch = rrd_updates.get_row_time(row) 342 | dv = str(rrd_updates.get_host_data(param,row)) 343 | if epoch > max_time: 344 | max_time = epoch 345 | data = dv 346 | if data == "": 347 | data = 0 348 | v.append(float(data)) 349 | perfdata[session.xenapi.host.get_name_label(host)] = reduce(lambda x, y: x+y, v)/len(v) 350 | 351 | exitcode = 0 352 | globalperf = 0 353 | for perf in perfdata: 354 | globalperf += perfdata[perf] 355 | if perfdata[perf] > float(critical)/100: 356 | exitcode = 2 357 | prefix = "CRITICAL: CPU " 358 | elif perfdata[perf] > float(warning)/100: 359 | exitcode = 1 360 | prefix = "WARNING: CPU " 361 | else: 362 | exitcode = 0 363 | prefix = "OK: CPU " 364 | 365 | globalperf = globalperf / len(perfdata) 366 | print prefix + "| 'used_cpu'="+str(round(globalperf, 2)*100)+"%;" + str(warning)+"%;" + str(critical)+"%;0%;100%;\n"+\ 367 | ";\n".join([host+" Used CPU = "+str(round(perfdata[host],2)*100) for host in perfdata]) + "%; |" +\ 368 | " ".join(["'"+host+"_used_cpu'="+str(round(perfdata[host],2)*100)+"%;"+str(warning)+"%;" + str(critical)+"%;0%;100%" for host in perfdata]) 369 | 370 | sys.exit(exitcode) 371 | 372 | 373 | if __name__ == "__main__": 374 | 375 | ### Arguments definition ### 376 | import argparse 377 | 378 | parser = argparse.ArgumentParser() 379 | # Top level parser 380 | parser.add_argument("pool",help="Pool Name as defined in config file or master IP address") 381 | parser.add_argument("config",help="Path to the config file") 382 | 383 | subparsers = parser.add_subparsers(help="checks help", dest="check") 384 | 385 | # Common top level parser for warning and critical level 386 | common_parser = argparse.ArgumentParser(add_help=False) 387 | common_parser.add_argument("warning",help="Warning level as percentage", type=int) 388 | common_parser.add_argument("critical",help="Critical level as percentage", type=int) 389 | 390 | #Check sr parser 391 | parser_sr = subparsers.add_parser("check_sr", help="Check for an SR or all SRs", parents=[common_parser]) 392 | parser_sr.add_argument("--name", help="Optionnaly check a specific SR") 393 | 394 | #Check mem parser 395 | parser_mem = subparsers.add_parser("check_mem", help="Check for mem usage on all hosts", parents=[common_parser]) 396 | 397 | #Check hosts parser 398 | parser_hosts = subparsers.add_parser("check_hosts", help="Check if hosts are alive or not") 399 | 400 | #Check cpu parser 401 | parser_cpu = subparsers.add_parser("check_cpu",help="Check cpu usage", parents=[common_parser]) 402 | args = parser.parse_args() 403 | 404 | ### Configuration parsing ### 405 | import ConfigParser, os 406 | config = ConfigParser.ConfigParser() 407 | config.readfp(open(args.config)) 408 | 409 | host = config.get(args.pool,'host') 410 | 411 | username = config.get(args.pool,"username") 412 | password = config.get(args.pool,"password") 413 | 414 | protocol = config.get(args.pool, "protocol") 415 | 416 | # We store the arguments in a dict to send them to the checks 417 | check_args = {} 418 | 419 | check_args["perfdata"] = config.get("general","perfdata_format") 420 | 421 | if hasattr(args,"warning"): 422 | check_args["warning"] = args.warning 423 | if hasattr(args,"critical"): 424 | check_args["critical"] = args.critical 425 | 426 | if args.check == "check_sr" and hasattr(args, "name"): 427 | check_args['sr_name'] = args.name 428 | 429 | # Test if exclude_srs is defined and add the list to the check_args dict 430 | if config.has_option(args.pool,"exclude_srs"): 431 | exclude_srs = config.get(args.pool,"exclude_srs").split(',') 432 | [x.strip() for x in exclude_srs] 433 | check_args["exclude_srs"] = exclude_srs 434 | 435 | # First acquire a valid session by logging in: 436 | if protocol == "https": 437 | scheme = "https://" 438 | else: 439 | scheme = "http://" 440 | 441 | try: 442 | session = XenAPI.Session(scheme+host) 443 | session.xenapi.login_with_password(username, password) 444 | except XenAPI.Failure, e: 445 | if e.details[0] == "HOST_IS_SLAVE": 446 | session=XenAPI.Session(scheme+e.details[1]) 447 | session.xenapi.login_with_password(username, password) 448 | else: 449 | print "CRITICAL - XenAPI Error : " + e.details[0] 450 | sys.exit(2) 451 | 452 | except: 453 | print "CRITICAL - Connection Error" 454 | sys.exit(2) 455 | 456 | options = { 457 | "check_sr": check_sr, 458 | "check_mem": check_mem, 459 | "check_hosts": check_hosts, 460 | "check_cpu": check_cpu 461 | } 462 | 463 | options[args.check](session,check_args) 464 | 465 | sys.exit(0) 466 | -------------------------------------------------------------------------------- /config.ini: -------------------------------------------------------------------------------- 1 | [10.0.0.1] 2 | username: toto 3 | password: nagios 4 | protocol: http 5 | [192.168.124.129] 6 | username: root 7 | password: root 8 | protocol: https 9 | -------------------------------------------------------------------------------- /parse_rrd.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | #Copyright (C) 2011 by Citrix Systems 4 | # 5 | #Permission is hereby granted, free of charge, to any person obtaining a copy 6 | #of this software and associated documentation files (the "Software"), to deal 7 | #in the Software without restriction, including without limitation the rights 8 | #to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | #copies of the Software, and to permit persons to whom the Software is 10 | #furnished to do so, subject to the following conditions: 11 | # 12 | # 13 | #The above copyright notice and this permission notice shall be included in 14 | #all copies or substantial portions of the Software. 15 | # 16 | #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | #AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | #THE SOFTWARE. 23 | # 24 | # Example code for reading RRDs 25 | # 26 | 27 | import urllib 28 | from xml.dom import minidom 29 | from xml.parsers.expat import ExpatError 30 | import time 31 | 32 | # Per VM dictionary (used by RRDUpdates to look up column numbers by variable names) 33 | class VMReport(dict): 34 | """Used internally by RRDUpdates""" 35 | def __init__(self, uuid): 36 | self.uuid = uuid 37 | 38 | # Per Host dictionary (used by RRDUpdates to look up column numbers by variable names) 39 | class HostReport(dict): 40 | """Used internally by RRDUpdates""" 41 | def __init__(self, uuid): 42 | self.uuid = uuid 43 | 44 | class RRDUpdates: 45 | """ Object used to get and parse the output the http://localhost/rrd_udpates?... 46 | """ 47 | def __init__(self): 48 | # params are what get passed to the CGI executable in the URL 49 | self.params = dict() 50 | self.params['start'] = int(time.time()) - 1000 # For demo purposes! 51 | self.params['host'] = 'true' # include data for host (as well as for VMs) 52 | self.params['cf'] = 'AVERAGE' # consolidation function, each sample averages 12 from the 5 second RRD 53 | self.params['interval'] = '60' 54 | 55 | def get_nrows(self): 56 | return self.rows 57 | 58 | def get_vm_list(self): 59 | return self.vm_reports.keys() 60 | 61 | def get_vm_param_list(self, uuid): 62 | report = self.vm_reports[uuid] 63 | if not report: 64 | return [] 65 | return report.keys() 66 | 67 | def get_vm_data(self, uuid, param, row): 68 | report = self.vm_reports[uuid] 69 | col = report[param] 70 | return self.__lookup_data(col, row) 71 | 72 | def get_host_uuid(self): 73 | report = self.host_report 74 | if not report: 75 | return None 76 | return report.uuid 77 | 78 | def get_host_param_list(self): 79 | report = self.host_report 80 | if not report: 81 | return [] 82 | return report.keys() 83 | 84 | def get_host_data(self, param, row): 85 | report = self.host_report 86 | col = report[param] 87 | return self.__lookup_data(col, row) 88 | 89 | def get_row_time(self,row): 90 | return self.__lookup_timestamp(row) 91 | 92 | # extract float from value () node by col,row 93 | def __lookup_data(self, col, row): 94 | # Note: the nodes are in reverse chronological order, and comprise 95 | # a timestamp node, followed by self.columns data nodes 96 | node = self.data_node.childNodes[self.rows - 1 - row].childNodes[col+1] 97 | return float(node.firstChild.toxml()) # node.firstChild should have nodeType TEXT_NODE 98 | 99 | # extract int from value () node by row 100 | def __lookup_timestamp(self, row): 101 | # Note: the nodes are in reverse chronological order, and comprise 102 | # a timestamp node, followed by self.columns data nodes 103 | node = self.data_node.childNodes[self.rows - 1 - row].childNodes[0] 104 | return int(node.firstChild.toxml()) # node.firstChild should have nodeType TEXT_NODE 105 | 106 | def refresh(self, session, override_params = {}, server = 'http://localhost'): 107 | params = override_params 108 | params['session_id'] = session 109 | params.update(self.params) 110 | paramstr = "&".join(["%s=%s" % (k,params[k]) for k in params]) 111 | url = "%s/rrd_updates?%s" % (server, paramstr) 112 | 113 | #print "RRD Query:\n %s" % url 114 | # this is better than urllib.urlopen() as it raises an Exception on http 401 'Unauthorised' error 115 | # rather than drop into interactive mode 116 | sock = urllib.URLopener().open(url) 117 | xmlsource = sock.read() 118 | sock.close() 119 | xmldoc = minidom.parseString(xmlsource) 120 | self.__parse_xmldoc(xmldoc) 121 | # Update the time used on the next run 122 | self.params['start'] = self.end_time + 1 # avoid retrieving same data twice 123 | 124 | def __parse_xmldoc(self, xmldoc): 125 | 126 | # The 1st node contains meta data (description of the data) 127 | # The 2nd node contains the data 128 | self.meta_node = xmldoc.firstChild.childNodes[0] 129 | self.data_node = xmldoc.firstChild.childNodes[1] 130 | 131 | def lookup_metadata_bytag(name): 132 | return int (self.meta_node.getElementsByTagName(name)[0].firstChild.toxml()) 133 | 134 | # rows = number of samples per variable 135 | # columns = number of variables 136 | self.rows = lookup_metadata_bytag('rows') 137 | self.columns = lookup_metadata_bytag('columns') 138 | 139 | # These indicate the period covered by the data 140 | self.start_time = lookup_metadata_bytag('start') 141 | self.step_time = lookup_metadata_bytag('step') 142 | self.end_time = lookup_metadata_bytag('end') 143 | 144 | # the Node describes the variables 145 | self.legend = self.meta_node.getElementsByTagName('legend')[0] 146 | 147 | # vm_reports matches uuid to per VM report 148 | self.vm_reports = {} 149 | 150 | # There is just one host_report and its uuid should not change! 151 | self.host_report = None 152 | 153 | # Handle each column. (I.e. each variable) 154 | for col in range(self.columns): 155 | self.__handle_col(col) 156 | 157 | def __handle_col(self, col): 158 | # work out how to interpret col from the legend 159 | col_meta_data = self.legend.childNodes[col].firstChild.toxml() 160 | 161 | # vm_or_host will be 'vm' or 'host'. Note that the Control domain counts as a VM! 162 | (cf, vm_or_host, uuid, param) = col_meta_data.split(':') 163 | 164 | if vm_or_host == 'vm': 165 | # Create a report for this VM if it doesn't exist 166 | if not self.vm_reports.has_key(uuid): 167 | self.vm_reports[uuid] = VMReport(uuid) 168 | 169 | # Update the VMReport with the col data and meta data 170 | vm_report = self.vm_reports[uuid] 171 | vm_report[param] = col 172 | 173 | elif vm_or_host == 'host': 174 | # Create a report for the host if it doesn't exist 175 | if not self.host_report: 176 | self.host_report = HostReport(uuid) 177 | elif self.host_report.uuid != uuid: 178 | raise PerfMonException, "Host UUID changed: (was %s, is %s)" % (self.host_report.uuid, uuid) 179 | 180 | # Update the HostReport with the col data and meta data 181 | self.host_report[param] = col 182 | 183 | else: 184 | raise PerfMonException, "Invalid string in : %s" % col_meta_data 185 | 186 | 187 | --------------------------------------------------------------------------------