debuggers.hg

view tools/python/xen/xend/XendMonitor.py @ 0:7d21f7218375

Exact replica of unstable on 051908 + README-this
author Mukesh Rathor
date Mon May 19 15:34:57 2008 -0700 (2008-05-19)
parents
children
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_sect'
28 VBD_RD_PATH = VBD_SYSFS_PATH + '%s/statistics/rd_sect'
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'^\s*(?P<iface>peth\d+):\s*' + PROC_NET_DEV_RE)
54 # Interval to poll xc, sysfs and proc
55 POLL_INTERVAL = 2.0
56 SECTOR_SIZE = 512
57 class XendMonitor(threading.Thread):
58 """Monitors VCPU, VBD, VIF and PIF statistics for Xen API.
60 Polls sysfs and procfs for statistics on VBDs and VIFs respectively.
62 @ivar domain_vcpus_util: Utilisation for VCPUs indexed by domain
63 @type domain_vcpus_util: {domid: {vcpuid: float, vcpuid: float}}
64 @ivar domain_vifs_util: Bytes per second for VIFs indexed by domain
65 @type domain_vifs_util: {domid: {vifid: (rx_bps, tx_bps)}}
66 @ivar domain_vifs_stat: Total amount of bytes used for VIFs indexed by domain
67 @type domain_vifs_stat: {domid: {vbdid: (rx, tx)}}
68 @ivar domain_vbds_util: Blocks per second for VBDs index by domain.
69 @type domain_vbds_util: {domid: {vbdid: (rd_reqps, wr_reqps)}}
71 """
72 def __init__(self):
73 threading.Thread.__init__(self)
74 self.setDaemon(True)
75 self.xc = xc()
77 self.lock = threading.Lock()
79 # tracks the last polled statistics
80 self._domain_vcpus = {}
81 self._domain_vifs = {}
82 self._domain_vbds = {}
83 self.pifs = {}
85 # instantaneous statistics
86 self._domain_vcpus_util = {}
87 self._domain_vifs_util = {}
88 self._domain_vifs_stat = {}
89 self._domain_vbds_util = {}
90 self.pifs_util = {}
92 def get_domain_vcpus_util(self):
93 self.lock.acquire()
94 try:
95 return self._domain_vcpus_util
96 finally:
97 self.lock.release()
99 def get_domain_vbds_util(self):
100 self.lock.acquire()
101 try:
102 return self._domain_vbds_util
103 finally:
104 self.lock.release()
106 def get_domain_vifs_util(self):
107 self.lock.acquire()
108 try:
109 return self._domain_vifs_util
110 finally:
111 self.lock.release()
113 def get_domain_vifs_stat(self):
114 self.lock.acquire()
115 try:
116 return self._domain_vifs_stat
117 finally:
118 self.lock.release()
120 def get_pifs_util(self):
121 self.lock.acquire()
122 try:
123 return self.pifs_util
124 finally:
125 self.lock.release()
127 def _get_vif_stats(self):
128 stats = {}
130 if not os.path.exists(NET_PROCFS_PATH):
131 return stats
133 usage_at = time.time()
134 for line in open(NET_PROCFS_PATH):
135 is_vif = re.search(VIF_DOMAIN_RE, line.strip())
136 if not is_vif:
137 continue
139 domid = int(is_vif.group('domid'))
140 vifid = int(is_vif.group('iface'))
141 rx_bytes = int(is_vif.group('rx_bytes'))
142 tx_bytes = int(is_vif.group('tx_bytes'))
143 if not domid in stats:
144 stats[domid] = {}
146 stats[domid][vifid] = (usage_at, rx_bytes, tx_bytes)
148 return stats
150 def _get_pif_stats(self):
151 stats = {}
153 if not os.path.exists(NET_PROCFS_PATH):
154 return stats
156 usage_at = time.time()
157 for line in open(NET_PROCFS_PATH):
158 is_pif = re.search(PIF_RE, line.strip())
159 if not is_pif:
160 continue
162 pifname = is_pif.group('iface')
163 rx_bytes = int(is_pif.group('rx_bytes'))
164 tx_bytes = int(is_pif.group('tx_bytes'))
165 stats[pifname] = (usage_at, rx_bytes, tx_bytes)
167 return stats
169 def _get_vbd_stats(self):
170 stats = {}
172 if not os.path.exists(VBD_SYSFS_PATH):
173 return stats
175 for vbd_path in os.listdir(VBD_SYSFS_PATH):
176 is_vbd = re.search(VBD_DOMAIN_RE, vbd_path)
177 if not is_vbd:
178 continue
180 domid = int(is_vbd.group('domid'))
181 vbdid = int(is_vbd.group('devid'))
182 rd_stat_path = VBD_RD_PATH % vbd_path
183 wr_stat_path = VBD_WR_PATH % vbd_path
185 if not os.path.exists(rd_stat_path) or \
186 not os.path.exists(wr_stat_path):
187 continue
190 try:
191 usage_at = time.time()
192 rd_stat = int(open(rd_stat_path).readline().strip())
193 wr_stat = int(open(wr_stat_path).readline().strip())
194 rd_stat *= SECTOR_SIZE
195 wr_stat *= SECTOR_SIZE
196 if domid not in stats:
197 stats[domid] = {}
199 stats[domid][vbdid] = (usage_at, rd_stat, wr_stat)
201 except (IOError, ValueError):
202 continue
204 return stats
206 def _get_cpu_stats(self):
207 stats = {}
208 for domain in self.xc.domain_getinfo():
209 domid = domain['domid']
210 vcpu_count = domain['online_vcpus']
211 stats[domid] = {}
212 for i in range(vcpu_count):
213 vcpu_info = self.xc.vcpu_getinfo(domid, i)
214 usage = vcpu_info['cpu_time']
215 usage_at = time.time()
216 stats[domid][i] = (usage_at, usage)
218 return stats
221 def run(self):
223 # loop every second for stats
224 while True:
225 self.lock.acquire()
226 try:
227 active_domids = []
228 # Calculate utilisation for VCPUs
230 for domid, cputimes in self._get_cpu_stats().items():
231 active_domids.append(domid)
232 if domid not in self._domain_vcpus:
233 # if not initialised, save current stats
234 # and skip utilisation calculation
235 self._domain_vcpus[domid] = cputimes
236 self._domain_vcpus_util[domid] = {}
237 continue
239 for vcpu, (usage_at, usage) in cputimes.items():
240 if vcpu not in self._domain_vcpus[domid]:
241 continue
243 prv_usage_at, prv_usage = \
244 self._domain_vcpus[domid][vcpu]
245 interval_s = (usage_at - prv_usage_at) * 1000000000
246 if interval_s > 0:
247 util = (usage - prv_usage) / interval_s
248 self._domain_vcpus_util[domid][vcpu] = util
250 self._domain_vcpus[domid] = cputimes
252 # Calculate utilisation for VBDs
254 for domid, vbds in self._get_vbd_stats().items():
255 if domid not in self._domain_vbds:
256 self._domain_vbds[domid] = vbds
257 self._domain_vbds_util[domid] = {}
258 continue
260 for devid, (usage_at, rd, wr) in vbds.items():
261 if devid not in self._domain_vbds[domid]:
262 continue
264 prv_at, prv_rd, prv_wr = \
265 self._domain_vbds[domid][devid]
266 interval = usage_at - prv_at
267 rd_util = (rd - prv_rd)/interval
268 wr_util = (wr - prv_wr)/interval
269 self._domain_vbds_util[domid][devid] = \
270 (rd_util, wr_util)
272 self._domain_vbds[domid] = vbds
275 # Calculate utilisation for VIFs
277 for domid, vifs in self._get_vif_stats().items():
279 if domid not in self._domain_vifs:
280 self._domain_vifs[domid] = vifs
281 self._domain_vifs_util[domid] = {}
282 self._domain_vifs_stat[domid] = {}
283 continue
285 for devid, (usage_at, rx, tx) in vifs.items():
286 if devid not in self._domain_vifs[domid]:
287 continue
289 prv_at, prv_rx, prv_tx = \
290 self._domain_vifs[domid][devid]
291 interval = usage_at - prv_at
292 rx_util = (rx - prv_rx)/interval
293 tx_util = (tx - prv_tx)/interval
295 # note these are flipped around because
296 # we are measuring the host interface,
297 # not the guest interface
298 self._domain_vifs_util[domid][devid] = \
299 (tx_util, rx_util)
300 self._domain_vifs_stat[domid][devid] = \
301 (float(tx), float(rx))
303 self._domain_vifs[domid] = vifs
305 # Calculate utilisation for PIFs
307 for pifname, stats in self._get_pif_stats().items():
308 if pifname not in self.pifs:
309 self.pifs[pifname] = stats
310 continue
312 usage_at, rx, tx = stats
313 prv_at, prv_rx, prv_tx = self.pifs[pifname]
314 interval = usage_at - prv_at
315 rx_util = (rx - prv_rx)/interval
316 tx_util = (tx - prv_tx)/interval
318 self.pifs_util[pifname] = (rx_util, tx_util)
319 self.pifs[pifname] = stats
321 for domid in self._domain_vcpus_util.keys():
322 if domid not in active_domids:
323 del self._domain_vcpus_util[domid]
324 del self._domain_vcpus[domid]
325 for domid in self._domain_vifs_util.keys():
326 if domid not in active_domids:
327 del self._domain_vifs_util[domid]
328 del self._domain_vifs[domid]
329 del self._domain_vifs_stat[domid]
330 for domid in self._domain_vbds_util.keys():
331 if domid not in active_domids:
332 del self._domain_vbds_util[domid]
333 del self._domain_vbds[domid]
335 finally:
336 self.lock.release()
338 # Sleep a while before next poll
339 time.sleep(POLL_INTERVAL)