glances_diskio.py 5.9 KB
Newer Older
A
Alessio Sergi 已提交
1 2
# -*- coding: utf-8 -*-
#
3
# This file is part of Glances.
A
Alessio Sergi 已提交
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
#
# Copyright (C) 2014 Nicolargo <nicolas@nicolargo.com>
#
# Glances is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Glances is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
A
PEP 257  
Alessio Sergi 已提交
19 20

"""Disk I/O plugin."""
A
Alessio Sergi 已提交
21

22
# Import Glances libs
A
Alessio Sergi 已提交
23
from glances.core.glances_timer import getTimeSinceLastUpdate
A
Alessio Sergi 已提交
24
from glances.plugins.glances_plugin import GlancesPlugin
A
Alessio Sergi 已提交
25

A
Alessio Sergi 已提交
26 27
import psutil

28 29 30 31 32 33
# Define the history items list
# All items in this list will be historised if the --enable-history tag is set
# 'color' define the graph color in #RGB format
items_history_list = [{'name': 'read_bytes', 'color': '#00FF00', 'label_y': '(B/s)'},
                      {'name': 'write_bytes', 'color': '#FF0000', 'label_y': '(B/s)'}]

A
Alessio Sergi 已提交
34 35

class Plugin(GlancesPlugin):
A
PEP 257  
Alessio Sergi 已提交
36 37

    """Glances' disks I/O plugin.
A
Alessio Sergi 已提交
38 39 40 41

    stats is a list
    """

42
    def __init__(self, args=None):
A
PEP 257  
Alessio Sergi 已提交
43
        """Init the plugin."""
44
        GlancesPlugin.__init__(self, args=args, items_history_list=items_history_list)
A
Alessio Sergi 已提交
45 46 47 48

        # We want to display the stat in the curse interface
        self.display_curse = True

49
        # Init the stats
A
Alessio Sergi 已提交
50
        self.reset()
51

52
    def reset(self):
A
PEP 257  
Alessio Sergi 已提交
53
        """Reset/init the stats."""
54
        self.stats = []
A
Alessio Sergi 已提交
55

56
    def update(self):
A
PEP 257  
Alessio Sergi 已提交
57
        """Update disk I/O stats using the input method."""
58 59 60
        # Reset stats
        self.reset()

61
        if self.get_input() == 'local':
62 63 64 65 66 67 68 69
            # Update stats using the standard system lib
            # Grab the stat using the PsUtil disk_io_counters method
            # read_count: number of reads
            # write_count: number of writes
            # read_bytes: number of bytes read
            # write_bytes: number of bytes written
            # read_time: time spent reading from disk (in milliseconds)
            # write_time: time spent writing to disk (in milliseconds)
N
Nicolargo 已提交
70 71 72 73
            try:
                diskiocounters = psutil.disk_io_counters(perdisk=True)
            except:
                return self.stats
74 75

            # Previous disk IO stats are stored in the diskio_old variable
N
Nicolargo 已提交
76
            if not hasattr(self, 'diskio_old'):
77
                # First call, we init the network_old var
A
Alessio Sergi 已提交
78
                try:
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
                    self.diskio_old = diskiocounters
                except (IOError, UnboundLocalError):
                    pass
            else:
                # By storing time data we enable Rx/s and Tx/s calculations in the
                # XML/RPC API, which would otherwise be overly difficult work
                # for users of the API
                time_since_update = getTimeSinceLastUpdate('disk')

                diskio_new = diskiocounters
                for disk in diskio_new:
                    try:
                        # Try necessary to manage dynamic disk creation/del
                        diskstat = {}
                        diskstat['time_since_update'] = time_since_update
                        diskstat['disk_name'] = disk
                        diskstat['read_bytes'] = (
                            diskio_new[disk].read_bytes -
                            self.diskio_old[disk].read_bytes)
                        diskstat['write_bytes'] = (
                            diskio_new[disk].write_bytes -
                            self.diskio_old[disk].write_bytes)
                    except KeyError:
                        continue
                    else:
                        self.stats.append(diskstat)
A
Alessio Sergi 已提交
105

N
Nicolargo 已提交
106
                # Save stats to compute next bitrate
107
                self.diskio_old = diskio_new
108
        elif self.get_input() == 'snmp':
109
            # Update stats using SNMP
110
            # No standard way for the moment...
111
            pass
A
Alessio Sergi 已提交
112

113 114 115
        # Update the history list
        self.update_stats_history('disk_name')

116
        return self.stats
A
Alessio Sergi 已提交
117 118

    def msg_curse(self, args=None):
A
PEP 257  
Alessio Sergi 已提交
119
        """Return the dict to display in the curse interface."""
A
Alessio Sergi 已提交
120 121 122
        # Init the return message
        ret = []

123
        # Only process if stats exist and display plugin enable...
124
        if self.stats == [] or args.disable_diskio:
125 126
            return ret

A
Alessio Sergi 已提交
127 128
        # Build the string message
        # Header
A
Alessio Sergi 已提交
129
        msg = '{0:9}'.format(_("DISK I/O"))
A
Alessio Sergi 已提交
130
        ret.append(self.curse_add_line(msg, "TITLE"))
A
Alessio Sergi 已提交
131
        msg = '{0:>7}'.format(_("R/s"))
A
Alessio Sergi 已提交
132
        ret.append(self.curse_add_line(msg))
A
Alessio Sergi 已提交
133
        msg = '{0:>7}'.format(_("W/s"))
A
Alessio Sergi 已提交
134 135 136
        ret.append(self.curse_add_line(msg))
        # Disk list (sorted by name)
        for i in sorted(self.stats, key=lambda diskio: diskio['disk_name']):
137
            # Do not display hidden interfaces
138
            if self.is_hide(i['disk_name']):
139
                continue
140 141 142 143
            # Is there an alias for the disk name ?
            disk_name = self.has_alias(i['disk_name'])
            if disk_name is None:
                disk_name = i['disk_name']
A
Alessio Sergi 已提交
144 145
            # New line
            ret.append(self.curse_new_line())
146
            if len(disk_name) > 9:
147
                # Cut disk name if it is too long
148
                disk_name = '_' + disk_name[-8:]
A
Alessio Sergi 已提交
149
            msg = '{0:9}'.format(disk_name)
A
Alessio Sergi 已提交
150 151
            ret.append(self.curse_add_line(msg))
            txps = self.auto_unit(int(i['read_bytes'] // i['time_since_update']))
152
            rxps = self.auto_unit(int(i['write_bytes'] // i['time_since_update']))
A
Alessio Sergi 已提交
153
            msg = '{0:>7}'.format(txps)
A
Alessio Sergi 已提交
154
            ret.append(self.curse_add_line(msg))
A
Alessio Sergi 已提交
155
            msg = '{0:>7}'.format(rxps)
156
            ret.append(self.curse_add_line(msg))
A
Alessio Sergi 已提交
157 158

        return ret