source: trunk/packages/xen-3.1/xen-3.1/tools/python/xen/xend/XendMonitor.py @ 34

Last change on this file since 34 was 34, checked in by hartmans, 18 years ago

Add xen and xen-common

File size: 11.7 KB
Line 
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
18from xen.lowlevel.xc import xc
19import time
20import threading
21import os
22import re
23
24"""Monitoring thread to keep track of Xend statistics. """
25
26VBD_SYSFS_PATH = '/sys/devices/xen-backend/'
27VBD_WR_PATH = VBD_SYSFS_PATH + '%s/statistics/wr_sect'
28VBD_RD_PATH = VBD_SYSFS_PATH + '%s/statistics/rd_sect'
29VBD_DOMAIN_RE = r'vbd-(?P<domid>\d+)-(?P<devid>\d+)$'
30
31NET_PROCFS_PATH = '/proc/net/dev'
32PROC_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
50VIF_DOMAIN_RE = re.compile(r'vif(?P<domid>\d+)\.(?P<iface>\d+):\s*' +
51                           PROC_NET_DEV_RE)
52PIF_RE = re.compile(r'^\s*(?P<iface>peth\d+):\s*' + PROC_NET_DEV_RE)
53
54# Interval to poll xc, sysfs and proc
55POLL_INTERVAL = 2.0
56SECTOR_SIZE = 512
57class 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_vbds_util: Blocks per second for VBDs index by domain.
67    @type domain_vbds_util: {domid: {vbdid: (rd_reqps, wr_reqps)}}   
68   
69    """
70    def __init__(self):
71        threading.Thread.__init__(self)
72        self.setDaemon(True)
73        self.xc = xc()
74
75        self.lock = threading.Lock()
76       
77        # tracks the last polled statistics
78        self._domain_vcpus = {}
79        self._domain_vifs = {}
80        self._domain_vbds = {}
81        self.pifs = {}
82
83        # instantaneous statistics
84        self._domain_vcpus_util = {}
85        self._domain_vifs_util = {}
86        self._domain_vbds_util = {}
87        self.pifs_util = {}
88
89    def get_domain_vcpus_util(self):
90        self.lock.acquire()
91        try:
92            return self._domain_vcpus_util
93        finally:
94            self.lock.release()
95
96    def get_domain_vbds_util(self):
97        self.lock.acquire()
98        try:
99            return self._domain_vbds_util
100        finally:
101            self.lock.release()                       
102
103    def get_domain_vifs_util(self):
104        self.lock.acquire()
105        try:
106            return self._domain_vifs_util
107        finally:
108            self.lock.release()
109
110    def get_pifs_util(self):
111        self.lock.acquire()
112        try:
113            return self.pifs_util
114        finally:
115            self.lock.release()       
116
117    def _get_vif_stats(self):
118        stats = {}
119
120        if not os.path.exists(NET_PROCFS_PATH):
121            return stats
122
123        usage_at = time.time()       
124        for line in open(NET_PROCFS_PATH):
125            is_vif = re.search(VIF_DOMAIN_RE, line.strip())
126            if not is_vif:
127                continue
128           
129            domid = int(is_vif.group('domid'))
130            vifid = int(is_vif.group('iface'))
131            rx_bytes = int(is_vif.group('rx_bytes'))
132            tx_bytes = int(is_vif.group('tx_bytes'))
133            if not domid in stats:
134                stats[domid] = {}
135               
136            stats[domid][vifid] = (usage_at, rx_bytes, tx_bytes)
137
138        return stats
139
140    def _get_pif_stats(self):
141        stats = {}
142
143        if not os.path.exists(NET_PROCFS_PATH):
144            return stats
145       
146        usage_at = time.time()       
147        for line in open(NET_PROCFS_PATH):
148            is_pif = re.search(PIF_RE, line.strip())
149            if not is_pif:
150                continue
151           
152            pifname = is_pif.group('iface')
153            rx_bytes = int(is_pif.group('rx_bytes'))
154            tx_bytes = int(is_pif.group('tx_bytes'))
155            stats[pifname] = (usage_at, rx_bytes, tx_bytes)
156
157        return stats   
158
159    def _get_vbd_stats(self):
160        stats = {}
161
162        if not os.path.exists(VBD_SYSFS_PATH):
163            return stats
164       
165        for vbd_path in os.listdir(VBD_SYSFS_PATH):
166            is_vbd = re.search(VBD_DOMAIN_RE, vbd_path)
167            if not is_vbd:
168                continue
169
170            domid = int(is_vbd.group('domid'))
171            vbdid = int(is_vbd.group('devid'))
172            rd_stat_path = VBD_RD_PATH % vbd_path
173            wr_stat_path = VBD_WR_PATH % vbd_path
174           
175            if not os.path.exists(rd_stat_path) or \
176                   not os.path.exists(wr_stat_path):
177                continue
178
179           
180            try:
181                usage_at = time.time()
182                rd_stat = int(open(rd_stat_path).readline().strip())
183                wr_stat = int(open(wr_stat_path).readline().strip())
184                rd_stat *= SECTOR_SIZE
185                wr_stat *= SECTOR_SIZE
186                if domid not in stats:
187                    stats[domid] = {}
188
189                stats[domid][vbdid] = (usage_at, rd_stat, wr_stat)
190               
191            except (IOError, ValueError):
192                continue
193
194        return stats
195
196    def _get_cpu_stats(self):
197        stats = {}
198        for domain in self.xc.domain_getinfo():
199            domid = domain['domid']
200            vcpu_count = domain['online_vcpus']
201            stats[domid] = {}
202            for i in range(vcpu_count):
203                vcpu_info = self.xc.vcpu_getinfo(domid, i)
204                usage = vcpu_info['cpu_time']
205                usage_at = time.time()
206                stats[domid][i] = (usage_at, usage)
207
208        return stats
209           
210
211    def run(self):
212
213        # loop every second for stats
214        while True:
215            self.lock.acquire()
216            try:
217                active_domids = []
218                # Calculate utilisation for VCPUs
219               
220                for domid, cputimes in self._get_cpu_stats().items():
221                    active_domids.append(domid)
222                    if domid not in self._domain_vcpus:
223                        # if not initialised, save current stats
224                        # and skip utilisation calculation
225                        self._domain_vcpus[domid] = cputimes
226                        self._domain_vcpus_util[domid] = {}
227                        continue
228
229                    for vcpu, (usage_at, usage) in cputimes.items():
230                        if vcpu not in self._domain_vcpus[domid]:
231                            continue
232                   
233                        prv_usage_at, prv_usage = \
234                                   self._domain_vcpus[domid][vcpu]
235                        interval_s = (usage_at - prv_usage_at) * 1000000000
236                        if interval_s > 0:
237                            util = (usage - prv_usage) / interval_s
238                            self._domain_vcpus_util[domid][vcpu] = util
239
240                    self._domain_vcpus[domid] = cputimes
241
242                # Calculate utilisation for VBDs
243               
244                for domid, vbds in self._get_vbd_stats().items():
245                    if domid not in self._domain_vbds:
246                        self._domain_vbds[domid] = vbds
247                        self._domain_vbds_util[domid] = {}
248                        continue
249               
250                    for devid, (usage_at, rd, wr) in vbds.items():
251                        if devid not in self._domain_vbds[domid]:
252                            continue
253                   
254                        prv_at, prv_rd, prv_wr  = \
255                                self._domain_vbds[domid][devid]
256                        interval = usage_at - prv_at
257                        rd_util = (rd - prv_rd)/interval
258                        wr_util = (wr - prv_wr)/interval
259                        self._domain_vbds_util[domid][devid] = \
260                                 (rd_util, wr_util)
261                       
262                    self._domain_vbds[domid] = vbds
263               
264
265                # Calculate utilisation for VIFs
266
267                for domid, vifs in self._get_vif_stats().items():
268               
269                    if domid not in self._domain_vifs:
270                        self._domain_vifs[domid] = vifs
271                        self._domain_vifs_util[domid] = {}
272                        continue
273               
274                    for devid, (usage_at, rx, tx) in vifs.items():
275                        if devid not in self._domain_vifs[domid]:
276                            continue
277                   
278                        prv_at, prv_rx, prv_tx  = \
279                                self._domain_vifs[domid][devid]
280                        interval = usage_at - prv_at
281                        rx_util = (rx - prv_rx)/interval
282                        tx_util = (tx - prv_tx)/interval
283
284                        # note these are flipped around because
285                        # we are measuring the host interface,
286                        # not the guest interface
287                        self._domain_vifs_util[domid][devid] = \
288                             (tx_util, rx_util)
289                       
290                    self._domain_vifs[domid] = vifs
291
292                # Calculate utilisation for PIFs
293
294                for pifname, stats in self._get_pif_stats().items():
295                    if pifname not in self.pifs:
296                        self.pifs[pifname] = stats
297                        continue
298
299                    usage_at, rx, tx = stats
300                    prv_at, prv_rx, prv_tx  = self.pifs[pifname]
301                    interval = usage_at - prv_at
302                    rx_util = (rx - prv_rx)/interval
303                    tx_util = (tx - prv_tx)/interval
304
305                    self.pifs_util[pifname] = (rx_util, tx_util)
306                    self.pifs[pifname] = stats
307
308                for domid in self._domain_vcpus_util.keys():
309                    if domid not in active_domids:
310                        del self._domain_vcpus_util[domid]
311                        del self._domain_vcpus[domid]
312                for domid in self._domain_vifs_util.keys():
313                    if domid not in active_domids:
314                        del self._domain_vifs_util[domid]
315                        del self._domain_vifs[domid]
316                for domid in self._domain_vbds_util.keys():
317                    if domid not in active_domids:
318                        del self._domain_vbds_util[domid]
319                        del self._domain_vbds[domid]
320
321            finally:
322                self.lock.release()
323
324            # Sleep a while before next poll
325            time.sleep(POLL_INTERVAL)
326
Note: See TracBrowser for help on using the repository browser.