Ejemplo n.º 1
0
    def __init__(self, log, config):
        """Plugin to record memcache stats.

        :param log: A logger
        :type log: logging.RootLogger
        :param config: a plumd.config.Conf configuration helper instance.
        :type config: plumd.config.Conf
        """
        super(Memcache, self).__init__(log, config)
        self.config.defaults(Memcache.defaults)

        # metrics to record
        self.gauges = self.config.get("gauges")
        self.rates = self.config.get("rates")

        # memcached connection - either unix socket or tcp
        spath = self.config.get("socket")
        if spath is None or not os.path.exists(spath):
            host = self.config.get("host")
            port = self.config.get("port")
            self.addr = (host, port)
            self.sfamily = socket.AF_INET
        else:
            self.addr = spath
            self.sfamily = socket.AF_UNIX

        self.socket = None
        self.timeout = config.get("timeout")
        self.calc = Differential()
Ejemplo n.º 2
0
    def __init__(self, log, config):
        """Plugin to measure various kernel metrics from /proc.

        :param log: A logger
        :type log: logging.RootLogger
        :param config: a plumd.config.Conf configuration helper instance.
        :type config: plumd.config.Conf
        """
        super(DiskStats, self).__init__(log, config)
        config.defaults(DiskStats.defaults)
        self.calc = Differential()
        self.proc_file = "{0}/diskstats".format(config.get("proc_path"))
        self.diskstats_dev_re = re.compile(config.get("diskstats_dev_re"))
        self.diskstats_cols = self.config.get("diskstats_cols")
        self.devices = []  # list of device names to record metrics for
        self.enabled = True
        # get list of available devices:
        dat = get_file_map(self.proc_file, 2, 0)
        # key is the device name, exclude ones matching the re
        for key, val in dat.items():
            if not self.diskstats_dev_re.match(key):
                self.devices.append(key)
        # check format of proc file
        ncols = len(get_file(self.proc_file).split("\n")[0].split())
        self.enabled = ncols == DiskStats.proc_colums
        if not self.enabled:
            msg = "DiskStats: invalid format: {0} has {1} cols, not {2}"
            self.log.error(msg.format(self.proc_file, ncols, DiskStats.proc_colums))
Ejemplo n.º 3
0
Archivo: netstat.py Proyecto: s4z/plumd
    def __init__(self, log, config):
        """Plugin to measure various kernel metrics from /proc.

        :param log: A logger
        :type log: logging.RootLogger
        :param config: a plumd.config.Conf configuration helper instance.
        :type config: plumd.config.Conf
        """
        super(Netstat, self).__init__(log, config)
        self.config.defaults(Netstat.defaults)
        self.calc = Differential()
        self.proc_file = "{0}/net/netstat".format(config.get('proc_path'))
        self.gauges = self.config.get('proc_netstat_gauges')
        self.rates = self.config.get('proc_netstat_rates')
Ejemplo n.º 4
0
Archivo: netdev.py Proyecto: s4z/plumd
    def __init__(self, log, config):
        """Plugin to measure network interface metrics from proc file net/dev.

        :param log: A logger
        :type log: logging.RootLogger
        :param config: a plumd.config.Conf configuration helper instance.
        :type config: plumd.config.Conf
        """
        super(NetDev, self).__init__(log, config)
        config.defaults(NetDev.defaults)
        self.proc_file = "{0}/net/dev".format(config.get('proc_path'))
        self.dev_re = re.compile(config.get('proc_netdev_re'))
        self.calc = Differential()
        self.enabled = True
        self.dev_cols = self.get_columns()
Ejemplo n.º 5
0
Archivo: redis.py Proyecto: s4z/plumd
    def __init__(self, log, config):
        """Plugin to record redis metrics.

        :param log: A logger
        :type log: logging.RootLogger
        :param config: a plumd.config.Conf configuration helper instance.
        :type config: plumd.config.Conf
        """
        super(Redis, self).__init__(log, config)
        self.config.defaults(Redis.defaults)

        # metrics to record
        self.gauges = self.config.get('gauges')
        self.rates = self.config.get('rates')
        self.configs = self.config.get('configs')
        self.keys = self.config.get('keys')

        # Redis connection - either unix socket or tcp
        addr = self.config.get('addr')
        addr_type = self.config.get('addr_type').lower()
        if addr_type == "unix":
            sfamily = socket.AF_UNIX
        elif addr_type == "inet":
            try:
                host, port = addr.split(":")
            except AttributeError as e:
                msg = "Redis: invalid address: {0}, (host:port)"
                raise plumd.ConfigError(msg.format(addr))
            addr = (host, int(port))
            sfamily = socket.AF_INET
        else:
            msg = "Redis: unsupported connection type: {0} (unix, inet)"
            raise plumd.ConfigError(msg.format(addr_type))
        timeout = config.get('timeout')
        self.client = RedisClient(self.log, addr, sfamily, timeout)
        self.calc = Differential()
Ejemplo n.º 6
0
Archivo: netstat.py Proyecto: s4z/plumd
class Netstat(plumd.Reader):
    """Plugin to measure various kernel metrics from /proc."""
    defaults = {
        'poll.interval': 10,
        'proc_path': '/proc',
        'proc_netstat_gauges': {
            'TcpExt': ["SyncookiesSent", "SyncookiesRecv",
                       "SyncookiesFailed", "OutOfWindowIcmps", "TW", "TWRecycled",
                       "TWKilled", "DelayedACKLost", "ListenOverflows",
                       "ListenDrops", "TCPLostRetransmit", "TCPRenoFailures",
                       "TCPSackFailures", "TCPLossFailures", "TCPFastRetrans",
                       "TCPForwardRetrans", "TCPSlowStartRetrans", "TCPTimeouts",
                       "TCPLossProbes", "TCPLossProbeRecovery", "TCPAbortOnData",
                       "TCPAbortOnClose", "TCPAbortOnMemory", "TCPAbortOnTimeout",
                       "TCPAbortOnLinger", "TCPAbortFailed", "TCPMemoryPressures",
                       "TCPBacklogDrop", "TCPMinTTLDrop", "TCPTimeWaitOverflow",
                       "TCPFastOpenActive", "TCPFastOpenActiveFail",
                       "TCPFastOpenPassive", "TCPFastOpenPassiveFail",
                       "TCPFastOpenListenOverflow", "TCPSynRetrans"],
            'IpExt': ["InNoRoutes", "InTruncatedPkts", "InBcastPkts",
                      "OutBcastPkts", "InOctets", "OutOctets", "InBcastOctets",
                      "OutBcastOctets", "InCsumErrors", "InNoECTPkts", "InECT1Pkts",
                      "InECT0Pkts", "InCEPkts"]
        },
        'proc_netstat_rates': {}
    }

    def __init__(self, log, config):
        """Plugin to measure various kernel metrics from /proc.

        :param log: A logger
        :type log: logging.RootLogger
        :param config: a plumd.config.Conf configuration helper instance.
        :type config: plumd.config.Conf
        """
        super(Netstat, self).__init__(log, config)
        self.config.defaults(Netstat.defaults)
        self.calc = Differential()
        self.proc_file = "{0}/net/netstat".format(config.get('proc_path'))
        self.gauges = self.config.get('proc_netstat_gauges')
        self.rates = self.config.get('proc_netstat_rates')

    def poll(self):
        """Poll for kernel metrics under proc file net/netstat.

        :rtype: ResultSet
        """
        return plumd.ResultSet(self.check())

    def check(self):
        """Return detailed network statitistics proc file net/netstat.

        Note: ECT1Pkts and ECT0Pkts relate to ECT congestion notifications.

        :rtype: plumd.Result
        """
        # what metrics do we want to record?
        result = plumd.Result("netstat")

        # read the proc file
        dat = None
        with open(self.proc_file, 'r') as f:
            dat = f.read().strip().split("\n")

        # timestamp for Differential calculations
        ts = time.time()

        # split values into lists
        dlist = deque([entry.split() for entry in dat])

        # put lists into key: value dict
        metrics = {}
        while dlist:
            headers = dlist.popleft()
            values = dlist.popleft()
            # { 'IpExt': {'InNoRoutes': 0, ...} } - [:-1] on IpExt: removes :
            metrics[headers[0][:-1]] = dict(zip(headers, values))

        # record gauges
        for ext, mnames in self.gauges.items():
            if ext not in metrics:
                self.log.warn("netstat: unknown extension: {0}".format(ext))
                del(self.gauges[ext])
                continue
            values = metrics[ext]
            for mname in mnames:
                if mname in values:
                    mstr = "{0}.{1}".format(ext, mname)
                    result.add(plumd.Int(mstr, values[mname]))
                else:
                    self.log.warn("netstat: unknown metric {0}".format(mname))
                    self.gauges[ext].remove(mname)

        # record rates
        for ext, mnames in self.rates.items():
            if ext not in metrics:
                self.log.warn("netstat: unknown extension: {0}".format(ext))
                del(self.rates[ext])
                continue
            values = metrics[ext]
            for mname in mnames:
                if mname in values:
                    mstr = "{0}.{1}".format(ext, mname)
                    mval = self.calc.per_second(mstr, float(values[mname]), ts)
                    result.add(plumd.Float(mstr, mval))
                else:
                    self.log.warn("netstat: unknown metric {0}".format(mname))
                    self.rates[ext].remove(mname)

        return [result]
Ejemplo n.º 7
0
class DiskStats(plumd.Reader):

    proc_colums = 14

    """Plugin to measure various kernel metrics from /proc."""
    defaults = {
        "poll.interval": 10,
        "proc_path": "/proc",
        "diskstats_dev_re": "dm-\d",
        "diskstats_cols": [
            "r",
            "r_merge",
            "r_sector",
            "r_time",
            "w",
            "w_merge",
            "w_sector",
            "w_time",
            "io_inprog",
            "io_time",
            "io_weighted_time",
        ],
    }

    def __init__(self, log, config):
        """Plugin to measure various kernel metrics from /proc.

        :param log: A logger
        :type log: logging.RootLogger
        :param config: a plumd.config.Conf configuration helper instance.
        :type config: plumd.config.Conf
        """
        super(DiskStats, self).__init__(log, config)
        config.defaults(DiskStats.defaults)
        self.calc = Differential()
        self.proc_file = "{0}/diskstats".format(config.get("proc_path"))
        self.diskstats_dev_re = re.compile(config.get("diskstats_dev_re"))
        self.diskstats_cols = self.config.get("diskstats_cols")
        self.devices = []  # list of device names to record metrics for
        self.enabled = True
        # get list of available devices:
        dat = get_file_map(self.proc_file, 2, 0)
        # key is the device name, exclude ones matching the re
        for key, val in dat.items():
            if not self.diskstats_dev_re.match(key):
                self.devices.append(key)
        # check format of proc file
        ncols = len(get_file(self.proc_file).split("\n")[0].split())
        self.enabled = ncols == DiskStats.proc_colums
        if not self.enabled:
            msg = "DiskStats: invalid format: {0} has {1} cols, not {2}"
            self.log.error(msg.format(self.proc_file, ncols, DiskStats.proc_colums))

    def poll(self):
        """Poll for kernel metrics under /proc.

        :rtype: ResultSet
        """
        return plumd.ResultSet(self.check())

    def check(self):
        """Return disk io metrics from proc file diskstats.

        :rtype: plumd.Result
        """
        # times in ms
        cols = self.diskstats_cols
        result = plumd.Result("diskstats")
        if not self.enabled:
            return [result]
        dat = {}
        # read and process /proc/diskstats
        dat = get_file_map(self.proc_file, 2, 0)
        ts = time.time()
        for dev in self.devices:
            if dev not in dat:
                continue
            for mname in cols:
                mval = float(dat[dev].popleft())
                mstr = "{0}.{1}".format(dev, mname)
                dval = self.calc.per_second(mstr, mval, ts)
                result.add(plumd.Float(mstr, dval))
        return [result]
Ejemplo n.º 8
0
Archivo: netdev.py Proyecto: s4z/plumd
class NetDev(plumd.Reader):
    """Plugin to measure various kernel metrics from /proc."""

    rename = {
        'packets': 'pkt',
        'compressed': 'compr',
        'multicast': 'mcast',
    }

    defaults = {
        'poll.interval': 10,
        'proc_path': '/proc',
        'proc_netdev_re': "(virbr\d+)|(vnet\d+)"
    }

    def __init__(self, log, config):
        """Plugin to measure network interface metrics from proc file net/dev.

        :param log: A logger
        :type log: logging.RootLogger
        :param config: a plumd.config.Conf configuration helper instance.
        :type config: plumd.config.Conf
        """
        super(NetDev, self).__init__(log, config)
        config.defaults(NetDev.defaults)
        self.proc_file = "{0}/net/dev".format(config.get('proc_path'))
        self.dev_re = re.compile(config.get('proc_netdev_re'))
        self.calc = Differential()
        self.enabled = True
        self.dev_cols = self.get_columns()

    def get_columns(self):
        cols = []
        dat = get_file(self.proc_file)
        lines = deque(dat.split("\n"))
        nlines = len(lines)
        # expect at least one network interface + 2 header lines
        if nlines < 2:
            msg = "NetDev: cannot parse {0}"
            self.log.error(msg.format(self.proc_file))
            self.enabled = False
            return cols

        # skip first line eg. Inter-|   Receive
        lines.popleft()
        # next line has the header values
        hdr_vals = deque(lines.popleft().split("|"))
        # remove eg.  face
        hdr_vals.popleft()
        if len(hdr_vals) != 2:
            msg = "NetDev: cannot parse {0}"
            self.log.error(msg.format(self.proc_files))
            self.enabled = False
            return cols

        # values are receive then transmit
        hdrs = dict(zip(["rx", "tx"], hdr_vals))
        for pfx, metrics in hdrs.items():
            for metric in metrics.split():
                if metric in NetDev.rename:
                    metric = NetDev.rename[metric]
                cols.append("{0}_{1}".format(pfx, metric))
        return cols

    def poll(self):
        """Return network interface metrics from proc file net/dev.

        :rtype: ResultSet
        """
        return plumd.ResultSet(self.check())

    def check(self):
        """Return network interface metrics from proc file net/dev.

        :rtype: list
        """
        result = plumd.Result("netdev")
        if not self.enabled:
            return [result]

        dat = {}
        cols = self.dev_cols
        regexp = self.dev_re

        # read and process /proc/net/dev
        with open(self.proc_file, 'r') as f:
            lines = deque(f.read().strip().split("\n"))
            # remove the two header lines - format fail :|
            lines.popleft()
            lines.popleft()

        # for Differential
        ts = time.time()

        # now it's dev, metrics
        for line in lines:
            dat = line.split()
            dev = dat[0].split(":")[0]
            vals = dat[1:]
            if regexp.match(dev):
                continue
            for key, val in dict(zip(cols, vals)).items():
                mstr = "{0}.{1}".format(dev, key)
                dval = self.calc.per_second(mstr, int(val), ts)
                result.add(plumd.Float(mstr, dval))

        return [result]
Ejemplo n.º 9
0
Archivo: redis.py Proyecto: s4z/plumd
class Redis(plumd.Reader):
    """Plugin to record redis metrics."""

    # default config values
    defaults = {
        'poll.interval': 10,
        'gauges': [
            "aof_current_rewrite_time_sec",
            "aof_enabled",
            "aof_last_rewrite_time_sec",
            "aof_rewrite_in_progress",
            "aof_rewrite_scheduled",
            "blocked_clients",
            "client_biggest_input_buf",
            "client_longest_output_list",
            "connected_clients",
            "connected_slaves",
            "evicted_keys",
            "expired_keys",
            "instantaneous_input_kbps",
            "instantaneous_ops_per_sec",
            "instantaneous_output_kbps",
            "keyspace_hits",
            "keyspace_misses",
            "latest_fork_usec",
            "loading",
            "master_repl_offset",
            "mem_fragmentation_ratio",
            "pubsub_channels",
            "pubsub_patterns",
            "rdb_bgsave_in_progress",
            "rdb_changes_since_last_save",
            "rdb_current_bgsave_time_sec",
            "rdb_last_bgsave_time_sec",
            "rdb_last_save_time",
            "rejected_connections",
            "repl_backlog_active",
            "repl_backlog_first_byte_offset",
            "repl_backlog_histlen",
            "repl_backlog_size",
            "sync_full",
            "sync_partial_err",
            "sync_partial_ok",
            "total_commands_processed",
            "total_connections_received",
            "total_net_input_bytes",
            "total_net_output_bytes",
            "uptime_in_days",
            "uptime_in_seconds",
            "used_cpu_sys",
            "used_cpu_sys_children",
            "used_cpu_user",
            "used_cpu_user_children",
            "used_memory",
            "used_memory_lua",
            "used_memory_peak",
            "used_memory_rss",
            "master_last_io_seconds_ago",
            "master_sync_in_progress",
            "slave_repl_offset",
            "slave_priority",
            "slave_read_only",
            "connected_slaves",
            "master_repl_offset",
            "repl_backlog_active",
            "repl_backlog_size",
            "repl_backlog_first_byte_offset",
            "repl_backlog_histlen"
            "connected_slaves"
            ],
        'rates': [],
        'configs': [
            'maxmemory'
        ],
        'keys': {
            # 'type': { metric_prefix: [key_prefix*, ...] }
            'lists': {},
            'zsets': {},
            'sets': {},
            'hlls': {}
        },
        'addr': '127.0.0.1:6379',
        'addr_type': 'inet',
        'timeout': 10
    }

    def __init__(self, log, config):
        """Plugin to record redis metrics.

        :param log: A logger
        :type log: logging.RootLogger
        :param config: a plumd.config.Conf configuration helper instance.
        :type config: plumd.config.Conf
        """
        super(Redis, self).__init__(log, config)
        self.config.defaults(Redis.defaults)

        # metrics to record
        self.gauges = self.config.get('gauges')
        self.rates = self.config.get('rates')
        self.configs = self.config.get('configs')
        self.keys = self.config.get('keys')

        # Redis connection - either unix socket or tcp
        addr = self.config.get('addr')
        addr_type = self.config.get('addr_type').lower()
        if addr_type == "unix":
            sfamily = socket.AF_UNIX
        elif addr_type == "inet":
            try:
                host, port = addr.split(":")
            except AttributeError as e:
                msg = "Redis: invalid address: {0}, (host:port)"
                raise plumd.ConfigError(msg.format(addr))
            addr = (host, int(port))
            sfamily = socket.AF_INET
        else:
            msg = "Redis: unsupported connection type: {0} (unix, inet)"
            raise plumd.ConfigError(msg.format(addr_type))
        timeout = config.get('timeout')
        self.client = RedisClient(self.log, addr, sfamily, timeout)
        self.calc = Differential()

    def poll(self):
        """Query Redis for metrics.

        :rtype: ResultSet
        """
        # catch exceptions - simply skip the poll on error
        try:
            result = plumd.Result("redis")

            # config values
            self.record_configs(result)

            # key sizes
            self.record_sizes(result)

            # get server metrics
            stats = self.client.info()

            # record gauges, rates
            self.record_metrics(stats, result)

            # replication, if any slaves are connected
            if "slave0" in stats:
                self.record_slaves(stats, result)

            # db metrics, maxmem
            self.record_dbs(stats, result)

            # record lists, zsets, sets and hll sizes
            self.record_sizes(result)

            # and finally command stats - if available
            self.record_cmdstats(result)

        except RedisError as e:
            msg = "Redis: exception during poll: {0}"
            self.log.error(msg.format(e))
        return plumd.ResultSet([result])

    def record_cmdstats(self, result):
        """Record the stats from info commandstats.

        :param result: A result object to add metrics to
        :type result: ResultSet
        """
        ts = time.time()
        name = self.name
        infos = self.client.info("commandstats")
        for key in sorted(infos.keys()):
            vals = infos[key].split(",")
            cstat, cname = key.split("_")
            for val in vals:
                mname, mval = val.split("=")
                metric = "{0}.{1}.{2}.{3}".format(name, cstat, cname, mname)
                result.add(plumd.Float(metric, mval))

    def record_metrics(self, stats, result):
        """Record the configured gauges and metrics

        :param stats: Dictionary returned from info command
        :type stats: dict
        :param result: A result object to add metrics to
        :type result: ResultSet
        """
        ts = time.time()
        name = self.name

        # record gauges
        for stat in self.gauges:
            if stat in stats:
                mname = "{0}.{1}".format(name, stat)
                result.add(plumd.Float(mname, stats[stat]))

        # record rates
        for stat in self.rates:
            if stat in stats:
                mname = "{0}.{1}".format(name, stat)
                mval = self.calc.per_second(mname, float(stats[stat]), ts)
                result.add(plumd.Float(mname, mval))

    def record_dbs(self, stats, result):
        """Record per database metrics into result.

        :param stats: Dictionary returned from info command
        :type stats: dict
        :param result: A result object to add metrics to
        :type result: ResultSet
        """
        # db0:keys=1,expires=0,avg_ttl=0
        name = self.name
        db_fmt = "db{0}"
        metric_fmt = "{0}.db.{1}.{2}"

        for i in xrange(0, len(stats.keys())):
            dbname = db_fmt.format(i)
            if dbname not in stats:
                break
            try:
                vals = stats[dbname].split(",")
                dbmetrics = dict((k, v)
                                 for k, v in (v.split('=') for v in vals))
                for k, v in dbmetrics.items():
                    metric_str = metric_fmt.format(name, i, k)
                    result.add(plumd.Int(metric_str, v))
            except KeyError as E:
                self.log.error("Redis: invalid db entry: {0}".format(dbname))

    def record_slaves(self, stats, result):
        """Record slave metrics into result.

        :param stats: A dictionary returned from info command
        :type stats: dict
        :param result: A ResultSet object to add metrics to
        :type result: ResultSet
        """
        # slave0:ip=127.0.0.1,port=6399,state=online,offset=239,lag=1
        name = self.name
        slave_str = "slave{0}"
        moffstr = 'master_repl_offset'
        moffset = 0
        try:
            moffset = int(stats[moffstr])
        except(TypeError, KeyError) as e:
            self.log.error("Redis: no {0} value".format(moffstr))

        # for each slave entry
        for i in xrange(0, len(stats.keys())):
            sname = slave_str.format(i)
            if sname not in stats:
                break
            try:
                vals = stats[sname].split(",")
                smetrics = dict((k, v)
                                for k, v in (v.split('=') for v in vals))
                sip = smetrics['ip'].replace(".", "_")
                smname = "{0}_{1}".format(sip, smetrics['port'])

                # record offset and lag
                mname = "{0}.slave.{1}.offset".format(name, smname)
                soffset = moffset - int(smetrics['offset'])
                result.add(plumd.Int(mname, soffset))
                mname = "{0}.slave.{1}.lag".format(name, sname)
                result.add(plumd.Int(mname, smetrics['lag']))

                # if slave is online set online = 1, otherwise 0
                sonline = 1 if smetrics['state'] == "online" else 0
                mname = "{0}.slave.{1}.online".format(name, sname)
                result.add(plumd.Int(mname, sonline))
            except(TypeError, KeyError, ValueError) as e:
                self.log.error("Redis: invalid slave entry: {0}".format(sname))

    def record_configs(self, result):
        """Record the configured configuration values.

        :param result: A ResultSet to record max mem to.
        :type result: plumd.ResultSet
        """
        configs = self.configs
        if not configs:
            return
        name = self.name
        for config in self.client.config_get_multi(configs):
            for key, val in config.items():
                mstr = "{0}.configs.{1}".format(name, key)
                result.add(plumd.Float(mstr, val))

    def record_sizes(self, result):
        """For each type of key (list, zset, set, hyperloglog)
        scan for a list of keys matching the prefix and record the
        total number of items for the matching prefix.

        :param result: A ResultSet to record into.
        :type result: plumd.ResultSet
        """
        if not self.keys:
            return
        keys = self.config.get("keys")
        if "lists" in keys:
            self.record_lists(keys['lists'], result)
        if "zsets" in keys:
            self.record_zsets(keys['zsets'], result)
        if "sets" in keys:
            self.record_sets(keys['sets'], result)
        if "hlls" in keys:
            self.record_hlls(keys['hlls'], result)

    def record_lists(self, lconfig, result):
        """Record the total length of the configured lists:

        eg. lconfig: {"metric_name": [ "list*", "of*", "globs*"]}

        :param lconfig: A dict of metric name => globs
        :type lconfig: dict
        :param result: A ResultSet to record into.
        :type result: plumd.ResultSet
        """
        name = self.name
        for mprefix, kprefixes in lconfig.items():
            for prefix in kprefixes:
                # get the total for this prefix
                total = self.client.llen_multi(self.client.scan(prefix))
                mstr = "{0}.sizes.lists.{1}".format(name, mprefix)
                result.add(plumd.Int(mstr, total))

    def record_zsets(self, zconfig, result):
        """Record the total length of the configured zsets:

        eg. zconfig: {"metric_name": [ "list*", "of*", "globs*"]}

        :param zconfig: A dict of metric name => globs
        :type zconfig: dict
        :param result: A ResultSet to record into.
        :type result: plumd.ResultSet
        """
        name = self.name
        for mprefix, kprefixes in zconfig.items():
            for prefix in kprefixes:
                # get the total for this prefix
                total = self.client.zcard_multi(self.client.scan(prefix))
                mstr = "{0}.sizes.zset.{1}".format(name, mprefix)
                result.add(plumd.Int(mstr, total))

    def record_sets(self, sconfig, result):
        """Record the total length of the configured zsets:

        eg. sconfig: {"metric_name": [ "list*", "of*", "globs*"]}

        :param sconfig: A dict of metric name => globs
        :type sconfig: dict
        :param result: A ResultSet to record into.
        :type result: plumd.ResultSet
        """
        name = self.name
        for mprefix, kprefixes in sconfig.items():
            for prefix in kprefixes:
                # get the total for this prefix
                total = self.client.scard_multi(self.client.scan(prefix))
                mstr = "{0}.sizes.set.{1}".format(name, mprefix)
                result.add(plumd.Int(mstr, total))

    def record_hlls(self, hllconfig, result):
        """Record the total length of the configured hlls:

        eg. sconfig: {"metric_name": [ "list*", "of*", "globs*"]}

        :param hllconfig: A dict of metric name => globs
        :type hllconfig: dict
        :param result: A ResultSet to record into.
        :type result: plumd.ResultSet
        """
        name = self.name
        for mprefix, kprefixes in hllconfig.items():
            for prefix in kprefixes:
                # get the total for this prefix
                total = self.client.pfcount_multi(self.client.scan(prefix))
                mstr = "{0}.sizes.hll.{1}".format(name, mprefix)
                result.add(plumd.Int(mstr, total))
Ejemplo n.º 10
0
Archivo: netsnmp.py Proyecto: s4z/plumd
class NetSnmp(plumd.Reader):
    """Plugin to measure various kernel metrics from /proc."""

    defaults = {
        "poll.interval": 10,
        "proc_path": "/proc",
        "proc_netsnmp_gauges": {
            "Ip": [
                "Forwarding",
                "InReceives",
                "InHdrErrors",
                "InAddrErrors",
                "ForwDatagrams",
                "InUnknownProtos",
                "InDiscards",
                "InDelivers",
                "OutRequests",
                "OutDiscards",
                "ReasmTimeout",
                "ReasmReqds",
                "ReasmOKs",
                "ReasmFails",
                "FragOKs",
                "FragFails",
                "FragCreates",
            ],
            "Icmp": [
                "InMsgs",
                "InErrors",
                "InCsumErrors",
                "InDestUnreachs",
                "InTimeExcds",
                "InParmProbs",
                "InSrcQuenchs",
                "InRedirects",
                "InEchos",
                "InEchoReps",
                "OutMsgs",
                "OutErrors",
                "OutDestUnreachs",
                "OutTimeExcds",
                "OutParmProbs",
                "OutSrcQuenchs",
                "OutRedirects",
                "OutEchos",
                "OutEchoReps",
            ],
            "IcmpMsg": ["OutType3"],
            "Tcp": [
                "MaxConn",
                "ActiveOpens",
                "PassiveOpens",
                "AttemptFails",
                "EstabResets",
                "CurrEstab",
                "InSegs",
                "OutSegs",
                "RetransSegs",
                "InErrs",
                "OutRsts",
                "InCsumErrors",
            ],
            "Udp": [
                "InDatagrams",
                "NoPorts",
                "InErrors",
                "OutDatagrams",
                "RcvbufErrors",
                "SndbufErrors",
                "InCsumErrors",
            ],
        },
        "proc_netsnmp_rates": {},
    }

    def __init__(self, log, config):
        """Plugin to measure various kernel metrics from /proc.

        :param log: A logger
        :type log: logging.RootLogger
        :param config: a plumd.config.Conf configuration helper instance.
        :type config: plumd.config.Conf
        """
        super(NetSnmp, self).__init__(log, config)
        self.config.defaults(NetSnmp.defaults)
        self.calc = Differential()
        self.proc_file = "{0}/net/snmp".format(config.get("proc_path"))
        self.filter = Filter()
        self.gauges = self.config.get("proc_netsnmp_gauges")
        self.rates = self.config.get("proc_netsnmp_rates")

    def poll(self):
        """Poll for kernel metrics under /proc.

        :rtype: ResultSet
        """
        return plumd.ResultSet(self.check())

    def check(self):
        """Return network protocol metrics from proc file net/snmp.

        Add entries to the configuration value 'skip_proc_net_snmp' to skip
        metrics.

        Add entries to the configuration value 'net_snmp_items' to match the
        format/order of the proc file net/snmp entries on the system.

        :rtype: plumd.Result
        """
        result = plumd.Result("netsnmp")

        # read the proc file
        dat = []
        with open(self.proc_file, "r") as f:
            dat = f.read().strip().split("\n")

        # timestamp for Differential calculations
        ts = time.time()

        # split values into lists
        dlist = deque([entry.split() for entry in dat])

        # put lists into key: value dict
        metrics = {}
        while dlist:
            headers = dlist.popleft()
            values = dlist.popleft()
            # { 'IpExt': {'InNoRoutes': 0, ...} } - [:-1] on IpExt: removes :
            metrics[headers[0][:-1]] = dict(zip(headers, values))

        # record gauges
        for proto, mnames in self.gauges.items():
            if proto not in metrics:
                self.log.warn("netsnmp: unknown protocol: {0}".format(proto))
                del (self.gauges[proto])
                continue
            values = metrics[proto]
            for mname in mnames:
                if mname in values:
                    mstr = "{0}.{1}".format(proto, mname)
                    result.add(plumd.Int(mstr, values[mname]))
                else:
                    self.log.warn("netstat: unknown metric {0}".format(mname))
                    self.gauges[proto].remove(mname)
                    continue

        # record rates
        for proto, mnames in self.rates.items():
            if proto not in metrics:
                self.log.warn("netsnmp: unknown protocol: {0}".format(proto))
                del (self.gauges[proto])
                continue
            values = metrics[proto]
            for mname in mnames:
                if mname in values:
                    mstr = "{0}.{1}".format(proto, mname)
                    mval = self.calc.per_second(mstr, int(values[mname]), ts)
                    result.add(plumd.Int(mstr, mval))
                else:
                    self.log.warn("netstat: unknown metric {0}".format(mname))
                    self.rates[proto].remove(mname)
                    continue

        return [result]
Ejemplo n.º 11
0
Archivo: stat.py Proyecto: s4z/plumd
class Stat(plumd.Reader):
    """Class to read metrics from /proc/stat"""

    defaults = {
        "proc_stat_gauges": ["procs_running", "procs_blocked"],
        "proc_stat_rates": ["intr", "ctxt", "softirq"],
        "cpu_metrics": ["user", "nice", "system", "idle", "iowait", "irq", "softirq", "steal", "guest", "guest_nice"],
        "per_cpu": False,
    }

    def __init__(self, log, config):
        """Plugin to measure various kernel metrics from /proc/stat

        :param log: A logger
        :type log: logging.RootLogger
        :param config: a plumd.config.Conf configuration helper instance.
        :type config: plumd.config.Conf
        """
        super(Stat, self).__init__(log, config)
        self.config.defaults(Stat.defaults)
        self.calc = Differential()
        self.proc_file = "{0}/stat".format(config.get("proc_path"))
        self.per_cpu = self.config.get("per_cpu")
        self.cpu_metrics = self.config.get("cpu_metrics")
        self.gauges = self.config.get("proc_stat_gauges")
        self.rates = self.config.get("proc_stat_rates")

    def poll(self):
        """Return cpu utilization and process metrics from proc file stat.

        :rtype: plumd.ResultSet
        """
        return plumd.ResultSet(self.check())

    def check(self):
        """Return cpu utilization and process metrics from proc file stat.

        :rtype: collections.deque
        """
        results = deque()
        result = plumd.Result("stat")

        dat = get_file_map(self.proc_file, 0, 0)
        ts = time.time()

        # record gauges
        for i, metric in enumerate(self.gauges):
            if metric not in dat:
                self.log.warn("stat: unknown metric {0}".format(metric))
                del (self.gauges[i])
                continue
            result.add(plumd.Int(metric, dat[metric][0]))

        # record rates
        for i, metric in enumerate(self.rates):
            if metric not in dat:
                self.log.warn("stat: unknown metric {0}".format(metric))
                del (self.rates[i])
                continue
            mval = self.calc.per_second(metric, float(dat[metric][0]), ts)
            result.add(plumd.Int(metric, mval))

        # record cpu
        if "cpu" in dat:
            results.append(self.proc_stat_cpu("cpu", "cpu", dat["cpu"], ts))

        # record each cpu if configured
        if self.per_cpu:
            for i in xrange(0, len(dat)):
                mstr = "cpu{0}".format(i)
                if mstr not in dat:
                    break
                results.append(self.proc_stat_cpu("cpus", mstr, dat[mstr]))

        results.append(result)
        return results

    def proc_stat_cpu(self, rname, key, val, ts):
        """Return cpu utilization metrics in percentage.

        :param rname: The Result name (eg. cpu or cpus)
        :type rname: str
        :param val: A deque populated with the metric values from stat
        :type val: deque
        :rtype: list
        """
        result = plumd.Result(rname)
        cpu = self.cpu_metrics
        for map_val in cpu:
            if not val:
                break
            mstr = "{0}_{1}".format(key, map_val)
            mval = self.calc.per_second(mstr, float(val.popleft()), ts)
            result.add(plumd.Float(mstr, mval))
        return result
Ejemplo n.º 12
0
Archivo: vmstat.py Proyecto: s4z/plumd
class VmStat(plumd.Reader):
    """Plugin to measure various kernel metrics from /proc/vmstat."""
    defaults = {
        'poll.interval': 10,
        'proc_path': '/proc',
        # awk '{ print "\"" $1 "\"," }' /proc/vmstat
        'proc_vmstat_gauges': ["nr_free_pages", "nr_dirtied", "nr_written",
                               "numa_hit", "numa_miss", "numa_foreign",
                               "numa_interleave", "numa_local", "numa_other",
                               "pgpgin", "pgpgout", "pswpin", "pswpout",
                               "pgalloc_dma", "pgalloc_dma32", "pgalloc_normal",
                               "pgalloc_movable", "pgfree", "pgactivate",
                               "pgdeactivate", "pgfault", "pgmajfault",
                               "slabs_scanned", "kswapd_inodesteal",
                               "kswapd_low_wmark_hit_quickly",
                               "kswapd_high_wmark_hit_quickly",
                               "drop_pagecache", "drop_slab",
                               "numa_pte_updates", "numa_huge_pte_updates",
                               "numa_hint_faults", "numa_hint_faults_local",
                               "numa_pages_migrated", "pgmigrate_success",
                               "pgmigrate_fail", "compact_migrate_scanned",
                               "compact_free_scanned", "compact_isolated",
                               "compact_stall", "compact_fail",
                               "compact_success"],
        'proc_vmstat_rates': []
    }

    def __init__(self, log, config):
        """Plugin to measure various kernel metrics from /proc/vmstat.

        :param log: A logger
        :type log: logging.RootLogger
        :param config: a plumd.config.Conf configuration helper instance.
        :type config: plumd.config.Conf
        """
        super(VmStat, self).__init__(log, config)
        self.config.defaults(VmStat.defaults)
        self.calc = Differential()
        self.proc_file = "{0}/vmstat".format(config.get('proc_path'))
        self.gauges = self.config.get('proc_vmstat_gauges')
        self.rates = self.config.get('proc_vmstat_rates')

    def poll(self):
        """Poll for kernel metrics under proc file vmstat.

        :rtype: ResultSet
        """
        return plumd.ResultSet(self.check())

    def check(self):
        """Return metrics from /proc/vmstat.

        :rtype: plumd.Result
        """
        # what metrics do we want to record?
        result = plumd.Result("vmstat")

        # read the proc file
        dat = {}
        with open(self.proc_file, 'r') as f:
            # get a list of values: metric, val, metric, val, etc
            vals = f.read().strip().split()
            # every second item starting at 0, every second item starting at 1
            # take that and put into a dict
            dat = dict(zip(vals[0::2], vals[1::2]))

        # timestamp for Differential calculations
        ts = time.time()

        # now, we have a list of items to record, just need to record them
        for i, metric in enumerate(self.gauges):
            if metric in dat:
                #mval = self.calc.per_second(mstr, int(values[mname]), ts)
                result.add(plumd.Int(metric, dat[metric]))
            else:
                self.log.warn("vmstat: unknown metric {0}".format(metric))
                del(self.gauges[i])

        # and the rates as well, if any
        for i, metric in enumerate(self.rates):
            if metric in dat:
                mval = self.calc.per_second(metric, int(dat[metric]), ts)
                result.add(plumd.Float(metric, mval))
            else:
                self.log.warn("vmstat: unknown metric {0}".format(metric))
                del(self.rates[i])

        return [result]
Ejemplo n.º 13
0
class Memcache(plumd.Reader):
    """Plugin to record memcache stats."""

    STAT_CMD = "stats\n"
    RECV_SIZE = 4096

    # default config values
    defaults = {
        "poll.interval": 10,
        "gauges": [
            "auth_cmds",
            "auth_errors",
            "bytes",
            "bytes_read",
            "bytes_written",
            "cas_badval",
            "cas_hits",
            "cas_misses",
            "cmd_flush",
            "cmd_get",
            "cmd_set",
            "cmd_touch",
            "connection_structures",
            "conn_yields",
            "curr_connections",
            "curr_items",
            "decr_hits",
            "decr_misses",
            "delete_hits",
            "delete_misses",
            "evicted_unfetched",
            "evictions",
            "expired_unfetched",
            "get_hits",
            "get_misses",
            "hash_bytes",
            "hash_is_expanding",
            "hash_power_level",
            "incr_hits",
            "incr_misses",
            "limit_maxbytes",
            "listen_disabled_num",
            "reclaimed",
            "reserved_fds",
            "rusage_system",
            "rusage_user",
            "threads",
            "total_connections",
            "total_items",
            "touch_hits",
            "touch_misses",
            "uptime",
        ],
        "rates": [],
        "host": "127.0.0.1",  # memcache server hostname/ip
        "port": 11211,  # memcache server port
        "socket": None,  # use tcp or unix domain socket
        "timeout": 10,  # connection timeouts
    }

    def __init__(self, log, config):
        """Plugin to record memcache stats.

        :param log: A logger
        :type log: logging.RootLogger
        :param config: a plumd.config.Conf configuration helper instance.
        :type config: plumd.config.Conf
        """
        super(Memcache, self).__init__(log, config)
        self.config.defaults(Memcache.defaults)

        # metrics to record
        self.gauges = self.config.get("gauges")
        self.rates = self.config.get("rates")

        # memcached connection - either unix socket or tcp
        spath = self.config.get("socket")
        if spath is None or not os.path.exists(spath):
            host = self.config.get("host")
            port = self.config.get("port")
            self.addr = (host, port)
            self.sfamily = socket.AF_INET
        else:
            self.addr = spath
            self.sfamily = socket.AF_UNIX

        self.socket = None
        self.timeout = config.get("timeout")
        self.calc = Differential()

    def poll(self):
        """Query memcache for stats.

        :rtype: ResultSet
        """
        result = plumd.Result("memcache")

        name = self.name
        stats = self.get_stats()
        ts = time.time()

        # record gauges
        for stat in self.gauges:
            if stat in stats:
                mname = "{0}.{1}".format(name, stat)
                result.add(plumd.Float(mname, stats[stat]))

        # record rates
        for stat in self.rates:
            if stat in stats:
                mname = "{0}.{1}".format(name, stat)
                mval = self.calc.per_second(mname, float(stats[stat]), ts)
                result.add(plumd.Float(mname, mval))

        return plumd.ResultSet([result])

    def get_stats(self):
        """Request and read stats from memcache socket."""
        stats = {}

        if not self.socket and not self.connect():
            return {}

        try:
            if PY3:
                self.socket.sendall(bytes(Memcache.STAT_CMD, "utf8"))
            else:
                self.socket.sendall(Memcache.STAT_CMD)

            st_str = self.socket.recv(Memcache.RECV_SIZE)
            # self.log.debug("Memcached: read: {0}".format(st_str))

            for line in st_str.split("\n"):
                vals = line.split()
                if len(vals) != 3:
                    continue
                stype, sname, sval = vals
                if stype == "STAT":
                    msg = "Memcached: {0} = {1}"
                    # self.log.debug(msg.format(sname, sval))
                    stats[sname] = sval

        except Exception as e:
            msg = "Memcached: {0}: poll: exception: {1}"
            self.log.error(msg.format(self.addr, e))
            self.disconnect()

        return stats

    def connect(self):
        """Connect to memcached, returns True if sucessful, False otherwise.

        :rtype: bool
        """
        # self.log.debug("Memcached: connecting: {0}".format(self.addr))
        if self.socket:
            self.disconnect()
        try:
            # create the socket
            self.socket = socket.socket(self.sfamily, socket.SOCK_STREAM)
            # set timeout for socket operations
            self.socket.settimeout(self.timeout)
            # connect
            self.socket.connect(self.addr)
            # log and return
            msg = "Memcached: connected: {0}"
            self.log.info(msg.format(self.addr))
            return True
        except Exception as e:
            # log exception, ensure cleanup is done (disconnect)
            msg = "Memcached: {0}: connect: excception: {1}"
            self.log.error(msg.format(self.addr, e))
            return False
        return False

    def disconnect(self):
        """Severe the memcached connection."""
        if self.socket:
            try:
                self.socket.close()
                self.socket = None
            except Exception as e:
                msg = "Memcached: dicsonnect: exception {0}".format(e)
                self.log.error(msg)
                self.socket = None