~ubuntu-branches/ubuntu/utopic/xen/utopic

« back to all changes in this revision

Viewing changes to tools/python/xen/xend/XendMonitor.py

  • Committer: Bazaar Package Importer
  • Author(s): Bastian Blank
  • Date: 2010-05-06 15:47:38 UTC
  • mto: (1.3.1) (15.1.1 sid) (4.1.1 experimental)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20100506154738-agoz0rlafrh1fnq7
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
#============================================================================
 
17
 
 
18
from xen.lowlevel.xc import xc
 
19
import time
 
20
import threading
 
21
import os
 
22
import re
 
23
 
 
24
"""Monitoring thread to keep track of Xend statistics. """
 
25
 
 
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+)$'
 
30
 
 
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*$'
 
48
 
 
49
 
 
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)
 
53
 
 
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.
 
59
 
 
60
    Polls sysfs and procfs for statistics on VBDs and VIFs respectively.
 
61
    
 
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)}}    
 
70
    
 
71
    """
 
72
    def __init__(self):
 
73
        threading.Thread.__init__(self)
 
74
        self.setDaemon(True)
 
75
        self.xc = xc()
 
76
 
 
77
        self.lock = threading.Lock()
 
78
        
 
79
        # tracks the last polled statistics
 
80
        self._domain_vcpus = {}
 
81
        self._domain_vifs = {}
 
82
        self._domain_vbds = {}
 
83
        self.pifs = {}
 
84
 
 
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 = {}
 
91
 
 
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()
 
98
 
 
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()                        
 
105
 
 
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()
 
112
 
 
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()
 
119
 
 
120
    def get_pifs_util(self):
 
121
        self.lock.acquire()
 
122
        try:
 
123
            return self.pifs_util
 
124
        finally:
 
125
            self.lock.release()        
 
126
 
 
127
    def _get_vif_stats(self):
 
128
        stats = {}
 
129
 
 
130
        if not os.path.exists(NET_PROCFS_PATH):
 
131
            return stats
 
132
 
 
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
 
138
            
 
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] = {}
 
145
                
 
146
            stats[domid][vifid] = (usage_at, rx_bytes, tx_bytes)
 
147
 
 
148
        return stats
 
149
 
 
150
    def _get_pif_stats(self):
 
151
        stats = {}
 
152
 
 
153
        if not os.path.exists(NET_PROCFS_PATH):
 
154
            return stats
 
155
        
 
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
 
161
            
 
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)
 
166
 
 
167
        return stats    
 
168
 
 
169
    def _get_vbd_stats(self):
 
170
        stats = {}
 
171
 
 
172
        if not os.path.exists(VBD_SYSFS_PATH):
 
173
            return stats
 
174
        
 
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
 
179
 
 
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
 
184
            
 
185
            if not os.path.exists(rd_stat_path) or \
 
186
                   not os.path.exists(wr_stat_path):
 
187
                continue
 
188
 
 
189
            
 
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] = {}
 
198
 
 
199
                stats[domid][vbdid] = (usage_at, rd_stat, wr_stat)
 
200
                
 
201
            except (IOError, ValueError):
 
202
                continue
 
203
 
 
204
        return stats
 
205
 
 
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)
 
217
 
 
218
        return stats
 
219
            
 
220
 
 
221
    def run(self):
 
222
 
 
223
        # loop every second for stats
 
224
        while True:
 
225
            self.lock.acquire()
 
226
            try:
 
227
                active_domids = []
 
228
                # Calculate utilisation for VCPUs
 
229
                
 
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
 
238
 
 
239
                    for vcpu, (usage_at, usage) in cputimes.items():
 
240
                        if vcpu not in self._domain_vcpus[domid]:
 
241
                            continue
 
242
                    
 
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
 
249
 
 
250
                    self._domain_vcpus[domid] = cputimes
 
251
 
 
252
                # Calculate utilisation for VBDs
 
253
                
 
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
 
259
                
 
260
                    for devid, (usage_at, rd, wr) in vbds.items():
 
261
                        if devid not in self._domain_vbds[domid]:
 
262
                            continue
 
263
                    
 
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)
 
271
                        
 
272
                    self._domain_vbds[domid] = vbds
 
273
                
 
274
 
 
275
                # Calculate utilisation for VIFs
 
276
 
 
277
                for domid, vifs in self._get_vif_stats().items():
 
278
                
 
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
 
284
                
 
285
                    for devid, (usage_at, rx, tx) in vifs.items():
 
286
                        if devid not in self._domain_vifs[domid]:
 
287
                            continue
 
288
                    
 
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
 
294
 
 
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))
 
302
                        
 
303
                    self._domain_vifs[domid] = vifs
 
304
 
 
305
                # Calculate utilisation for PIFs
 
306
 
 
307
                for pifname, stats in self._get_pif_stats().items():
 
308
                    if pifname not in self.pifs:
 
309
                        self.pifs[pifname] = stats
 
310
                        continue
 
311
 
 
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
 
317
 
 
318
                    self.pifs_util[pifname] = (rx_util, tx_util)
 
319
                    self.pifs[pifname] = stats
 
320
 
 
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]
 
334
 
 
335
            finally:
 
336
                self.lock.release()
 
337
 
 
338
            # Sleep a while before next poll
 
339
            time.sleep(POLL_INTERVAL)
 
340