-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathparse_rrd.py
182 lines (148 loc) · 6.95 KB
/
parse_rrd.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#!/usr/bin/python
#
#Copyright (C) 2011 by Citrix Systems
#
#Permission is hereby granted, free of charge, to any person obtaining a copy
#of this software and associated documentation files (the "Software"), to deal
#in the Software without restriction, including without limitation the rights
#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
#copies of the Software, and to permit persons to whom the Software is
#furnished to do so, subject to the following conditions:
#
#
#The above copyright notice and this permission notice shall be included in
#all copies or substantial portions of the Software.
#
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
#THE SOFTWARE.
import XenAPI
import urllib
from xml.dom import minidom
from xml.parsers.expat import ExpatError
import time
# Per VM dictionary (used by RRDUpdates to look up column numbers by variable names)
class VMReport(dict):
"""Used internally by RRDUpdates"""
def __init__(self, uuid):
self.uuid = uuid
# Per Host dictionary (used by RRDUpdates to look up column numbers by variable names)
class HostReport(dict):
"""Used internally by RRDUpdates"""
def __init__(self, uuid):
self.uuid = uuid
class RRDUpdates:
""" Object used to get and parse the output the http://localhost/rrd_udpates?...
"""
def __init__(self):
# params are what get passed to the CGI executable in the URL
self.params = dict()
self.params['start'] = int(time.time()) - 1000 # For demo purposes!
self.params['host'] = 'true' # include data for host (as well as for VMs)
self.params['cf'] = 'AVERAGE' # consolidation function, each sample averages 12 from the 5 second RRD
self.params['interval'] = '60'
def get_nrows(self):
return self.rows
def get_vm_list(self):
return self.vm_reports.keys()
def get_vm_param_list(self, uuid):
report = self.vm_reports[uuid]
if not report:
return []
return report.keys()
def get_vm_data(self, uuid, param, row):
report = self.vm_reports[uuid]
col = report[param]
return self.__lookup_data(col, row)
def get_host_uuid(self):
report = self.host_report
if not report:
return None
return report.uuid
def get_host_param_list(self):
report = self.host_report
if not report:
return []
return report.keys()
def get_host_data(self, param, row):
report = self.host_report
col = report[param]
return self.__lookup_data(col, row)
def get_row_time(self,row):
return self.__lookup_timestamp(row)
# extract float from value (<v>) node by col,row
def __lookup_data(self, col, row):
# Note: the <rows> nodes are in reverse chronological order, and comprise
# a timestamp <t> node, followed by self.columns data <v> nodes
node = self.data_node.childNodes[self.rows - 1 - row].childNodes[col+1]
return float(node.firstChild.toxml()) # node.firstChild should have nodeType TEXT_NODE
# extract int from value (<t>) node by row
def __lookup_timestamp(self, row):
# Note: the <rows> nodes are in reverse chronological order, and comprise
# a timestamp <t> node, followed by self.columns data <v> nodes
node = self.data_node.childNodes[self.rows - 1 - row].childNodes[0]
return int(node.firstChild.toxml()) # node.firstChild should have nodeType TEXT_NODE
def refresh(self, url, override_params):
params = override_params
params.update(self.params)
paramstr = "&".join(["%s=%s" % (k,params[k]) for k in params])
url = url + "/rrd_updates?%s" % (paramstr)
# this is better than urllib.urlopen() as it raises an Exception on http 401 'Unauthorised' error
# rather than drop into interactive mode
sock = urllib.URLopener().open(url)
xmlsource = sock.read()
sock.close()
xmldoc = minidom.parseString(xmlsource)
self.__parse_xmldoc(xmldoc)
# Update the time used on the next run
self.params['start'] = self.end_time + 1 # avoid retrieving same data twice
def __parse_xmldoc(self, xmldoc):
# The 1st node contains meta data (description of the data)
# The 2nd node contains the data
self.meta_node = xmldoc.firstChild.childNodes[0]
self.data_node = xmldoc.firstChild.childNodes[1]
def lookup_metadata_bytag(name):
return int (self.meta_node.getElementsByTagName(name)[0].firstChild.toxml())
# rows = number of samples per variable
# columns = number of variables
self.rows = lookup_metadata_bytag('rows')
self.columns = lookup_metadata_bytag('columns')
# These indicate the period covered by the data
self.start_time = lookup_metadata_bytag('start')
self.step_time = lookup_metadata_bytag('step')
self.end_time = lookup_metadata_bytag('end')
# the <legend> Node describes the variables
self.legend = self.meta_node.getElementsByTagName('legend')[0]
# vm_reports matches uuid to per VM report
self.vm_reports = {}
# There is just one host_report and its uuid should not change!
self.host_report = None
# Handle each column. (I.e. each variable)
for col in range(self.columns):
self.__handle_col(col)
def __handle_col(self, col):
# work out how to interpret col from the legend
col_meta_data = self.legend.childNodes[col].firstChild.toxml()
# vm_or_host will be 'vm' or 'host'. Note that the Control domain counts as a VM!
(cf, vm_or_host, uuid, param) = col_meta_data.split(':')
if vm_or_host == 'vm':
# Create a report for this VM if it doesn't exist
if not self.vm_reports.has_key(uuid):
self.vm_reports[uuid] = VMReport(uuid)
# Update the VMReport with the col data and meta data
vm_report = self.vm_reports[uuid]
vm_report[param] = col
elif vm_or_host == 'host':
# Create a report for the host if it doesn't exist
if not self.host_report:
self.host_report = HostReport(uuid)
elif self.host_report.uuid != uuid:
raise PerfMonException, "Host UUID changed: (was %s, is %s)" % (self.host_report.uuid, uuid)
# Update the HostReport with the col data and meta data
self.host_report[param] = col
else:
raise PerfMonException, "Invalid string in <legend>: %s" % col_meta_data