direct-io.hg

changeset 13579:6c087036b3ed

[XEND] Add missing XendMonitor.py

Signed-off-by: Alastair Tse <atse@xensource.com>
author Alastair Tse <atse@xensource.com>
date Wed Jan 24 12:26:19 2007 +0000 (2007-01-24)
parents f7a52957b427
children 259470f0856b
files tools/python/xen/xend/XendMonitor.py
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/tools/python/xen/xend/XendMonitor.py	Wed Jan 24 12:26:19 2007 +0000
     1.3 @@ -0,0 +1,318 @@
     1.4 +#============================================================================
     1.5 +# This library is free software; you can redistribute it and/or
     1.6 +# modify it under the terms of version 2.1 of the GNU Lesser General Public
     1.7 +# License as published by the Free Software Foundation.
     1.8 +#
     1.9 +# This library is distributed in the hope that it will be useful,
    1.10 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    1.11 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    1.12 +# Lesser General Public License for more details.
    1.13 +#
    1.14 +# You should have received a copy of the GNU Lesser General Public
    1.15 +# License along with this library; if not, write to the Free Software
    1.16 +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    1.17 +#============================================================================
    1.18 +# Copyright (C) 2007 XenSource Ltd.
    1.19 +#============================================================================
    1.20 +
    1.21 +from xen.lowlevel.xc import xc
    1.22 +import time
    1.23 +import threading
    1.24 +import os
    1.25 +import re
    1.26 +
    1.27 +"""Monitoring thread to keep track of Xend statistics. """
    1.28 +
    1.29 +VBD_SYSFS_PATH = '/sys/devices/xen-backend/'
    1.30 +VBD_WR_PATH = VBD_SYSFS_PATH + '%s/statistics/wr_req'
    1.31 +VBD_RD_PATH = VBD_SYSFS_PATH + '%s/statistics/rd_req'
    1.32 +VBD_DOMAIN_RE = r'vbd-(?P<domid>\d+)-(?P<devid>\d+)$'
    1.33 +
    1.34 +NET_PROCFS_PATH = '/proc/net/dev'
    1.35 +PROC_NET_DEV_RE = r'(?P<rx_bytes>\d+)\s+' \
    1.36 +                  r'(?P<rx_packets>\d+)\s+' \
    1.37 +                  r'(?P<rx_errs>\d+)\s+' \
    1.38 +                  r'(?P<rx_drop>\d+)\s+' \
    1.39 +                  r'(?P<rx_fifo>\d+)\s+' \
    1.40 +                  r'(?P<rx_frame>\d+)\s+' \
    1.41 +                  r'(?P<rx_compressed>\d+)\s+' \
    1.42 +                  r'(?P<rx_multicast>\d+)\s+' \
    1.43 +                  r'(?P<tx_bytes>\d+)\s+' \
    1.44 +                  r'(?P<tx_packets>\d+)\s+' \
    1.45 +                  r'(?P<tx_errs>\d+)\s+' \
    1.46 +                  r'(?P<tx_drop>\d+)\s+' \
    1.47 +                  r'(?P<tx_fifo>\d+)\s+' \
    1.48 +                  r'(?P<tx_collisions>\d+)\s+' \
    1.49 +                  r'(?P<tx_carrier>\d+)\s+' \
    1.50 +                  r'(?P<tx_compressed>\d+)\s*$'
    1.51 +
    1.52 +
    1.53 +VIF_DOMAIN_RE = re.compile(r'vif(?P<domid>\d+)\.(?P<iface>\d+):\s*' +
    1.54 +                           PROC_NET_DEV_RE)
    1.55 +PIF_RE = re.compile(r'peth(?P<iface>\d+):\s*' + PROC_NET_DEV_RE)
    1.56 +
    1.57 +# The VBD transfer figures are in "requests" where we don't
    1.58 +# really know how many bytes per requests. For now we make
    1.59 +# up a number roughly could be.
    1.60 +VBD_ROUGH_BYTES_PER_REQUEST = 1024 * 8 * 4
    1.61 +
    1.62 +# Interval to poll xc, sysfs and proc
    1.63 +POLL_INTERVAL = 2.0
    1.64 +
    1.65 +class XendMonitor(threading.Thread):
    1.66 +    """Monitors VCPU, VBD, VIF and PIF statistics for Xen API.
    1.67 +
    1.68 +    Polls sysfs and procfs for statistics on VBDs and VIFs respectively.
    1.69 +    
    1.70 +    @ivar domain_vcpus_util: Utilisation for VCPUs indexed by domain
    1.71 +    @type domain_vcpus_util: {domid: {vcpuid: float, vcpuid: float}}
    1.72 +    @ivar domain_vifs_util: Bytes per second for VIFs indexed by domain
    1.73 +    @type domain_vifs_util: {domid: {vifid: (rx_bps, tx_bps)}}
    1.74 +    @ivar domain_vbds_util: Blocks per second for VBDs index by domain.
    1.75 +    @type domain_vbds_util: {domid: {vbdid: (rd_reqps, wr_reqps)}}    
    1.76 +    
    1.77 +    """
    1.78 +    def __init__(self):
    1.79 +        threading.Thread.__init__(self)
    1.80 +        self.setDaemon(True)
    1.81 +        self.xc = xc()
    1.82 +
    1.83 +        self.lock = threading.Lock()
    1.84 +        
    1.85 +        # tracks the last polled statistics
    1.86 +        self._domain_vcpus = {}
    1.87 +        self._domain_vifs = {}
    1.88 +        self._domain_vbds = {}
    1.89 +        self.pifs = {}
    1.90 +
    1.91 +        # instantaneous statistics
    1.92 +        self._domain_vcpus_util = {}
    1.93 +        self._domain_vifs_util = {}
    1.94 +        self._domain_vbds_util = {}
    1.95 +        self.pifs_util = {}
    1.96 +
    1.97 +    def get_domain_vcpus_util(self):
    1.98 +        self.lock.acquire()
    1.99 +        try:
   1.100 +            return self._domain_vcpus_util
   1.101 +        finally:
   1.102 +            self.lock.release()
   1.103 +
   1.104 +    def get_domain_vbds_util(self):
   1.105 +        self.lock.acquire()
   1.106 +        try:
   1.107 +            return self._domain_vbds_util
   1.108 +        finally:
   1.109 +            self.lock.release()                        
   1.110 +
   1.111 +    def get_domain_vifs_util(self):
   1.112 +        self.lock.acquire()
   1.113 +        try:
   1.114 +            return self._domain_vifs_util
   1.115 +        finally:
   1.116 +            self.lock.release()
   1.117 +
   1.118 +    def get_pifs_util(self):
   1.119 +        self.lock.acquire()
   1.120 +        try:
   1.121 +            return self.pifs_util
   1.122 +        finally:
   1.123 +            self.lock.release()        
   1.124 +
   1.125 +    def _get_vif_stats(self):
   1.126 +        stats = {}
   1.127 +
   1.128 +        if not os.path.exists(NET_PROCFS_PATH):
   1.129 +            return stats
   1.130 +
   1.131 +        usage_at = time.time()        
   1.132 +        for line in open(NET_PROCFS_PATH):
   1.133 +            is_vif = re.search(VIF_DOMAIN_RE, line.strip())
   1.134 +            if not is_vif:
   1.135 +                continue
   1.136 +            
   1.137 +            domid = int(is_vif.group('domid'))
   1.138 +            vifid = int(is_vif.group('iface'))
   1.139 +            rx_bytes = int(is_vif.group('rx_bytes'))
   1.140 +            tx_bytes = int(is_vif.group('tx_bytes'))
   1.141 +            if not domid in stats:
   1.142 +                stats[domid] = {}
   1.143 +                
   1.144 +            stats[domid][vifid] = (usage_at, rx_bytes, tx_bytes)
   1.145 +
   1.146 +        return stats
   1.147 +
   1.148 +    def _get_pif_stats(self):
   1.149 +        stats = {}
   1.150 +
   1.151 +        if not os.path.exists(NET_PROCFS_PATH):
   1.152 +            return stats
   1.153 +        
   1.154 +        usage_at = time.time()        
   1.155 +        for line in open(NET_PROCFS_PATH):
   1.156 +            is_pif = re.search(PIF_RE, line.strip())
   1.157 +            if not is_pif:
   1.158 +                continue
   1.159 +            
   1.160 +            pifid = int(is_pif.group('iface'))
   1.161 +            rx_bytes = int(is_pif.group('rx_bytes'))
   1.162 +            tx_bytes = int(is_pif.group('tx_bytes'))
   1.163 +            stats[pifid] = (usage_at, rx_bytes, tx_bytes)
   1.164 +
   1.165 +        return stats    
   1.166 +
   1.167 +    def _get_vbd_stats(self):
   1.168 +        stats = {}
   1.169 +
   1.170 +        if not os.path.exists(VBD_SYSFS_PATH):
   1.171 +            return stats
   1.172 +        
   1.173 +        for vbd_path in os.listdir(VBD_SYSFS_PATH):
   1.174 +            is_vbd = re.search(VBD_DOMAIN_RE, vbd_path)
   1.175 +            if not is_vbd:
   1.176 +                continue
   1.177 +
   1.178 +            domid = int(is_vbd.group('domid'))
   1.179 +            vbdid = int(is_vbd.group('devid'))
   1.180 +            rd_stat_path = VBD_RD_PATH % vbd_path
   1.181 +            wr_stat_path = VBD_WR_PATH % vbd_path
   1.182 +            
   1.183 +            if not os.path.exists(rd_stat_path) or \
   1.184 +                   not os.path.exists(wr_stat_path):
   1.185 +                continue
   1.186 +
   1.187 +            
   1.188 +            try:
   1.189 +                usage_at = time.time()
   1.190 +                rd_stat = int(open(rd_stat_path).readline().strip())
   1.191 +                wr_stat = int(open(wr_stat_path).readline().strip())
   1.192 +                rd_stat *= VBD_ROUGH_BYTES_PER_REQUEST
   1.193 +                wr_stat *= VBD_ROUGH_BYTES_PER_REQUEST
   1.194 +                
   1.195 +                if domid not in stats:
   1.196 +                    stats[domid] = {}
   1.197 +
   1.198 +                stats[domid][vbdid] = (usage_at, rd_stat, wr_stat)
   1.199 +                
   1.200 +            except (IOError, ValueError):
   1.201 +                continue
   1.202 +
   1.203 +        return stats
   1.204 +
   1.205 +    def _get_cpu_stats(self):
   1.206 +        stats = {}
   1.207 +        for domain in self.xc.domain_getinfo():
   1.208 +            domid = domain['domid']
   1.209 +            vcpu_count = domain['online_vcpus']
   1.210 +            stats[domid] = {}
   1.211 +            for i in range(vcpu_count):
   1.212 +                vcpu_info = self.xc.vcpu_getinfo(domid, i)
   1.213 +                usage = vcpu_info['cpu_time']
   1.214 +                usage_at = time.time()
   1.215 +                stats[domid][i] = (usage_at, usage)
   1.216 +
   1.217 +        return stats
   1.218 +            
   1.219 +
   1.220 +    def run(self):
   1.221 +
   1.222 +        # loop every second for stats
   1.223 +        while True:
   1.224 +            self.lock.acquire()
   1.225 +            try:
   1.226 +            
   1.227 +                # Calculate utilisation for VCPUs
   1.228 +                
   1.229 +                for domid, cputimes in self._get_cpu_stats().items():
   1.230 +                    if domid not in self._domain_vcpus:
   1.231 +                        # if not initialised, save current stats
   1.232 +                        # and skip utilisation calculation
   1.233 +                        self._domain_vcpus[domid] = cputimes
   1.234 +                        self._domain_vcpus_util[domid] = {}
   1.235 +                        continue
   1.236 +
   1.237 +                    for vcpu, (usage_at, usage) in cputimes.items():
   1.238 +                        if vcpu not in self._domain_vcpus[domid]:
   1.239 +                            continue
   1.240 +                    
   1.241 +                        prv_usage_at, prv_usage = \
   1.242 +                                   self._domain_vcpus[domid][vcpu]
   1.243 +                        interval_s = (usage_at - prv_usage_at) * 1000000000
   1.244 +                        if interval_s > 0:
   1.245 +                            util = (usage - prv_usage) / interval_s
   1.246 +                            self._domain_vcpus_util[domid][vcpu] = util
   1.247 +
   1.248 +                    self._domain_vcpus[domid] = cputimes
   1.249 +
   1.250 +                # Calculate utilisation for VBDs
   1.251 +                
   1.252 +                for domid, vbds in self._get_vbd_stats().items():
   1.253 +                    if domid not in self._domain_vbds:
   1.254 +                        self._domain_vbds[domid] = vbds
   1.255 +                        self._domain_vbds_util[domid] = {}
   1.256 +                        continue
   1.257 +                
   1.258 +                    for devid, (usage_at, rd, wr) in vbds.items():
   1.259 +                        if devid not in self._domain_vbds[domid]:
   1.260 +                            continue
   1.261 +                    
   1.262 +                        prv_at, prv_rd, prv_wr  = \
   1.263 +                                self._domain_vbds[domid][devid]
   1.264 +                        interval = usage_at - prv_at
   1.265 +                        rd_util = (rd - prv_rd)/interval
   1.266 +                        wr_util = (wr - prv_wr)/interval
   1.267 +                        self._domain_vbds_util[domid][devid] = \
   1.268 +                                 (rd_util, wr_util)
   1.269 +                        
   1.270 +                    self._domain_vbds[domid] = vbds
   1.271 +                
   1.272 +
   1.273 +                # Calculate utilisation for VIFs
   1.274 +
   1.275 +                for domid, vifs in self._get_vif_stats().items():
   1.276 +                
   1.277 +                    if domid not in self._domain_vifs:
   1.278 +                        self._domain_vifs[domid] = vifs
   1.279 +                        self._domain_vifs_util[domid] = {}
   1.280 +                        continue
   1.281 +                
   1.282 +                    for devid, (usage_at, rx, tx) in vifs.items():
   1.283 +                        if devid not in self._domain_vifs[domid]:
   1.284 +                            continue
   1.285 +                    
   1.286 +                        prv_at, prv_rx, prv_tx  = \
   1.287 +                                self._domain_vifs[domid][devid]
   1.288 +                        interval = usage_at - prv_at
   1.289 +                        rx_util = (rx - prv_rx)/interval
   1.290 +                        tx_util = (tx - prv_tx)/interval
   1.291 +
   1.292 +                        # note these are flipped around because
   1.293 +                        # we are measuring the host interface,
   1.294 +                        # not the guest interface
   1.295 +                        self._domain_vifs_util[domid][devid] = \
   1.296 +                             (tx_util, rx_util)
   1.297 +                        
   1.298 +                    self._domain_vifs[domid] = vifs
   1.299 +
   1.300 +                # Calculate utilisation for PIFs
   1.301 +
   1.302 +                for pifid, stats in self._get_pif_stats().items():
   1.303 +                    if pifid not in self.pifs:
   1.304 +                        self.pifs[pifid] = stats
   1.305 +                        continue
   1.306 +
   1.307 +                    usage_at, rx, tx = stats
   1.308 +                    prv_at, prv_rx, prv_tx  = self.pifs[pifid]
   1.309 +                    interval = usage_at - prv_at
   1.310 +                    rx_util = (rx - prv_rx)/interval
   1.311 +                    tx_util = (tx - prv_tx)/interval
   1.312 +
   1.313 +                    self.pifs_util[pifid] = (rx_util, tx_util)
   1.314 +                    self.pifs[pifid] = stats
   1.315 +
   1.316 +            finally:
   1.317 +                self.lock.release()
   1.318 +
   1.319 +            # Sleep a while before next poll
   1.320 +            time.sleep(POLL_INTERVAL)
   1.321 +