ia64/xen-unstable

view tools/python/xen/xend/XendMonitor.py @ 13604:e4d415692ba5

[XEND] Cleanup old domains in statistics monitor.

Signed-off-by: Alastair Tse <atse@xensource.com>
author Alastair Tse <atse@xensource.com>
date Wed Jan 24 15:50:58 2007 +0000 (2007-01-24)
parents 6c087036b3ed
children 32f7d3200a99
line source
1 #============================================================================
2 # This library is free software; you can redistribute it and/or
3 # modify it under the terms of version 2.1 of the GNU Lesser General Public
4 # License as published by the Free Software Foundation.
5 #
6 # This library is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 # Lesser General Public License for more details.
10 #
11 # You should have received a copy of the GNU Lesser General Public
12 # License along with this library; if not, write to the Free Software
13 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 #============================================================================
15 # Copyright (C) 2007 XenSource Ltd.
16 #============================================================================
18 from xen.lowlevel.xc import xc
19 import time
20 import threading
21 import os
22 import re
24 """Monitoring thread to keep track of Xend statistics. """
26 VBD_SYSFS_PATH = '/sys/devices/xen-backend/'
27 VBD_WR_PATH = VBD_SYSFS_PATH + '%s/statistics/wr_req'
28 VBD_RD_PATH = VBD_SYSFS_PATH + '%s/statistics/rd_req'
29 VBD_DOMAIN_RE = r'vbd-(?P<domid>\d+)-(?P<devid>\d+)$'
31 NET_PROCFS_PATH = '/proc/net/dev'
32 PROC_NET_DEV_RE = r'(?P<rx_bytes>\d+)\s+' \
33 r'(?P<rx_packets>\d+)\s+' \
34 r'(?P<rx_errs>\d+)\s+' \
35 r'(?P<rx_drop>\d+)\s+' \
36 r'(?P<rx_fifo>\d+)\s+' \
37 r'(?P<rx_frame>\d+)\s+' \
38 r'(?P<rx_compressed>\d+)\s+' \
39 r'(?P<rx_multicast>\d+)\s+' \
40 r'(?P<tx_bytes>\d+)\s+' \
41 r'(?P<tx_packets>\d+)\s+' \
42 r'(?P<tx_errs>\d+)\s+' \
43 r'(?P<tx_drop>\d+)\s+' \
44 r'(?P<tx_fifo>\d+)\s+' \
45 r'(?P<tx_collisions>\d+)\s+' \
46 r'(?P<tx_carrier>\d+)\s+' \
47 r'(?P<tx_compressed>\d+)\s*$'
50 VIF_DOMAIN_RE = re.compile(r'vif(?P<domid>\d+)\.(?P<iface>\d+):\s*' +
51 PROC_NET_DEV_RE)
52 PIF_RE = re.compile(r'peth(?P<iface>\d+):\s*' + PROC_NET_DEV_RE)
54 # The VBD transfer figures are in "requests" where we don't
55 # really know how many bytes per requests. For now we make
56 # up a number roughly could be.
57 VBD_ROUGH_BYTES_PER_REQUEST = 1024 * 8 * 4
59 # Interval to poll xc, sysfs and proc
60 POLL_INTERVAL = 2.0
62 class XendMonitor(threading.Thread):
63 """Monitors VCPU, VBD, VIF and PIF statistics for Xen API.
65 Polls sysfs and procfs for statistics on VBDs and VIFs respectively.
67 @ivar domain_vcpus_util: Utilisation for VCPUs indexed by domain
68 @type domain_vcpus_util: {domid: {vcpuid: float, vcpuid: float}}
69 @ivar domain_vifs_util: Bytes per second for VIFs indexed by domain
70 @type domain_vifs_util: {domid: {vifid: (rx_bps, tx_bps)}}
71 @ivar domain_vbds_util: Blocks per second for VBDs index by domain.
72 @type domain_vbds_util: {domid: {vbdid: (rd_reqps, wr_reqps)}}
74 """
75 def __init__(self):
76 threading.Thread.__init__(self)
77 self.setDaemon(True)
78 self.xc = xc()
80 self.lock = threading.Lock()
82 # tracks the last polled statistics
83 self._domain_vcpus = {}
84 self._domain_vifs = {}
85 self._domain_vbds = {}
86 self.pifs = {}
88 # instantaneous statistics
89 self._domain_vcpus_util = {}
90 self._domain_vifs_util = {}
91 self._domain_vbds_util = {}
92 self.pifs_util = {}
94 def get_domain_vcpus_util(self):
95 self.lock.acquire()
96 try:
97 return self._domain_vcpus_util
98 finally:
99 self.lock.release()
101 def get_domain_vbds_util(self):
102 self.lock.acquire()
103 try:
104 return self._domain_vbds_util
105 finally:
106 self.lock.release()
108 def get_domain_vifs_util(self):
109 self.lock.acquire()
110 try:
111 return self._domain_vifs_util
112 finally:
113 self.lock.release()
115 def get_pifs_util(self):
116 self.lock.acquire()
117 try:
118 return self.pifs_util
119 finally:
120 self.lock.release()
122 def _get_vif_stats(self):
123 stats = {}
125 if not os.path.exists(NET_PROCFS_PATH):
126 return stats
128 usage_at = time.time()
129 for line in open(NET_PROCFS_PATH):
130 is_vif = re.search(VIF_DOMAIN_RE, line.strip())
131 if not is_vif:
132 continue
134 domid = int(is_vif.group('domid'))
135 vifid = int(is_vif.group('iface'))
136 rx_bytes = int(is_vif.group('rx_bytes'))
137 tx_bytes = int(is_vif.group('tx_bytes'))
138 if not domid in stats:
139 stats[domid] = {}
141 stats[domid][vifid] = (usage_at, rx_bytes, tx_bytes)
143 return stats
145 def _get_pif_stats(self):
146 stats = {}
148 if not os.path.exists(NET_PROCFS_PATH):
149 return stats
151 usage_at = time.time()
152 for line in open(NET_PROCFS_PATH):
153 is_pif = re.search(PIF_RE, line.strip())
154 if not is_pif:
155 continue
157 pifid = int(is_pif.group('iface'))
158 rx_bytes = int(is_pif.group('rx_bytes'))
159 tx_bytes = int(is_pif.group('tx_bytes'))
160 stats[pifid] = (usage_at, rx_bytes, tx_bytes)
162 return stats
164 def _get_vbd_stats(self):
165 stats = {}
167 if not os.path.exists(VBD_SYSFS_PATH):
168 return stats
170 for vbd_path in os.listdir(VBD_SYSFS_PATH):
171 is_vbd = re.search(VBD_DOMAIN_RE, vbd_path)
172 if not is_vbd:
173 continue
175 domid = int(is_vbd.group('domid'))
176 vbdid = int(is_vbd.group('devid'))
177 rd_stat_path = VBD_RD_PATH % vbd_path
178 wr_stat_path = VBD_WR_PATH % vbd_path
180 if not os.path.exists(rd_stat_path) or \
181 not os.path.exists(wr_stat_path):
182 continue
185 try:
186 usage_at = time.time()
187 rd_stat = int(open(rd_stat_path).readline().strip())
188 wr_stat = int(open(wr_stat_path).readline().strip())
189 rd_stat *= VBD_ROUGH_BYTES_PER_REQUEST
190 wr_stat *= VBD_ROUGH_BYTES_PER_REQUEST
192 if domid not in stats:
193 stats[domid] = {}
195 stats[domid][vbdid] = (usage_at, rd_stat, wr_stat)
197 except (IOError, ValueError):
198 continue
200 return stats
202 def _get_cpu_stats(self):
203 stats = {}
204 for domain in self.xc.domain_getinfo():
205 domid = domain['domid']
206 vcpu_count = domain['online_vcpus']
207 stats[domid] = {}
208 for i in range(vcpu_count):
209 vcpu_info = self.xc.vcpu_getinfo(domid, i)
210 usage = vcpu_info['cpu_time']
211 usage_at = time.time()
212 stats[domid][i] = (usage_at, usage)
214 return stats
217 def run(self):
219 # loop every second for stats
220 while True:
221 self.lock.acquire()
222 try:
223 active_domids = set()
224 # Calculate utilisation for VCPUs
226 for domid, cputimes in self._get_cpu_stats().items():
227 active_domids.add(domid)
228 if domid not in self._domain_vcpus:
229 # if not initialised, save current stats
230 # and skip utilisation calculation
231 self._domain_vcpus[domid] = cputimes
232 self._domain_vcpus_util[domid] = {}
233 continue
235 for vcpu, (usage_at, usage) in cputimes.items():
236 if vcpu not in self._domain_vcpus[domid]:
237 continue
239 prv_usage_at, prv_usage = \
240 self._domain_vcpus[domid][vcpu]
241 interval_s = (usage_at - prv_usage_at) * 1000000000
242 if interval_s > 0:
243 util = (usage - prv_usage) / interval_s
244 self._domain_vcpus_util[domid][vcpu] = util
246 self._domain_vcpus[domid] = cputimes
248 # Calculate utilisation for VBDs
250 for domid, vbds in self._get_vbd_stats().items():
251 if domid not in self._domain_vbds:
252 self._domain_vbds[domid] = vbds
253 self._domain_vbds_util[domid] = {}
254 continue
256 for devid, (usage_at, rd, wr) in vbds.items():
257 if devid not in self._domain_vbds[domid]:
258 continue
260 prv_at, prv_rd, prv_wr = \
261 self._domain_vbds[domid][devid]
262 interval = usage_at - prv_at
263 rd_util = (rd - prv_rd)/interval
264 wr_util = (wr - prv_wr)/interval
265 self._domain_vbds_util[domid][devid] = \
266 (rd_util, wr_util)
268 self._domain_vbds[domid] = vbds
271 # Calculate utilisation for VIFs
273 for domid, vifs in self._get_vif_stats().items():
275 if domid not in self._domain_vifs:
276 self._domain_vifs[domid] = vifs
277 self._domain_vifs_util[domid] = {}
278 continue
280 for devid, (usage_at, rx, tx) in vifs.items():
281 if devid not in self._domain_vifs[domid]:
282 continue
284 prv_at, prv_rx, prv_tx = \
285 self._domain_vifs[domid][devid]
286 interval = usage_at - prv_at
287 rx_util = (rx - prv_rx)/interval
288 tx_util = (tx - prv_tx)/interval
290 # note these are flipped around because
291 # we are measuring the host interface,
292 # not the guest interface
293 self._domain_vifs_util[domid][devid] = \
294 (tx_util, rx_util)
296 self._domain_vifs[domid] = vifs
298 # Calculate utilisation for PIFs
300 for pifid, stats in self._get_pif_stats().items():
301 if pifid not in self.pifs:
302 self.pifs[pifid] = stats
303 continue
305 usage_at, rx, tx = stats
306 prv_at, prv_rx, prv_tx = self.pifs[pifid]
307 interval = usage_at - prv_at
308 rx_util = (rx - prv_rx)/interval
309 tx_util = (tx - prv_tx)/interval
311 self.pifs_util[pifid] = (rx_util, tx_util)
312 self.pifs[pifid] = stats
314 for domid in self.domain_vcpus_util.keys():
315 if domid not in active_domids:
316 del self.domain_vcpus_util[domid]
317 del self.domain_vcpus[domid]
318 for domid in self.domain_vifs_util.keys():
319 if domid not in active_domids:
320 del self.domain_vifs_util[domid]
321 del self.domain_vifs[domid]
322 for domid in self.domain_vbds_util.keys():
323 if domid not in active_domids:
324 del self.domain_vbds_util[domid]
325 del self.domain_vbds[domid]
327 finally:
328 self.lock.release()
330 # Sleep a while before next poll
331 time.sleep(POLL_INTERVAL)