Completed
Push — master ( f1337f...ab2213 )
by Kenny
01:06
created

Memcache.__init__()   B

Complexity

Conditions 2

Size

Total Lines 29

Duplication

Lines 29
Ratio 100 %

Importance

Changes 1
Bugs 1 Features 1
Metric Value
cc 2
c 1
b 1
f 1
dl 29
loc 29
rs 8.8571
1
# -*- coding: utf-8 -*-
2
3
__author__ = 'Kenny Freeman'
4
__email__ = '[email protected]'
5
__license__ = "ISCL"
6
__docformat__ = 'reStructuredText'
7
8
import sys
9
10
PY3 = sys.version_info > (3,)
11
12
import time
13
import socket
14
15
import plumd
16
import plumd.plugins
17
from plumd.calc import Differential
18
19
20
class Memcache(plumd.plugins.Reader):
21
    """Plugin to record memcache stats."""
22
23
    STAT_CMD = "stats\n"
24
    RECV_SIZE = 4096
25
26
    # default config values
27
    defaults = {
28
        'poll.interval': 10,
29
        'gauges': [
30
            "auth_cmds",
31
            "auth_errors",
32
            "bytes",
33
            "bytes_read",
34
            "bytes_written",
35
            "cas_badval",
36
            "cas_hits",
37
            "cas_misses",
38
            "cmd_flush",
39
            "cmd_get",
40
            "cmd_set",
41
            "cmd_touch",
42
            "connection_structures",
43
            "conn_yields",
44
            "curr_connections",
45
            "curr_items",
46
            "decr_hits",
47
            "decr_misses",
48
            "delete_hits",
49
            "delete_misses",
50
            "evicted_unfetched",
51
            "evictions",
52
            "expired_unfetched",
53
            "get_hits",
54
            "get_misses",
55
            "hash_bytes",
56
            "hash_is_expanding",
57
            "hash_power_level",
58
            "incr_hits",
59
            "incr_misses",
60
            "limit_maxbytes",
61
            "listen_disabled_num",
62
            "reclaimed",
63
            "reserved_fds",
64
            "rusage_system",
65
            "rusage_user",
66
            "threads",
67
            "total_connections",
68
            "total_items",
69
            "touch_hits",
70
            "touch_misses",
71
            "uptime"
72
        ],
73
        'rates': [],
74
        'host': '127.0.0.1', # memcache server hostname/ip
75
        'port': 11211,       # memcache server port
76
        'socket': None,      # use tcp or unix domain socket
77
        'timeout': 10        # connection timeouts
78
    }
79
80 View Code Duplication
    def __init__(self, log, config):
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
81
        """Plugin to record memcache stats.
82
83
        :param log: A logger
84
        :type log: logging.RootLogger
85
        :param config: a plumd.config.Conf configuration helper instance.
86
        :type config: plumd.config.Conf
87
        """
88
        super(Memcache, self).__init__(log, config)
89
        self.config.defaults(Memcache.defaults)
90
91
        # metrics to record
92
        self.gauges = self.config.get('gauges')
93
        self.rates = self.config.get('rates')
94
95
        # memcached connection - either unix socket or tcp
96
        spath = self.config.get('socket')
97
        if spath is None:
98
            host = self.config.get('host')
99
            port = self.config.get('port')
100
            self.addr = (host, port)
101
            self.sfamily = socket.AF_INET
102
        else:
103
            self.addr = spath
104
            self.sfamily = socket.AF_UNIX
105
106
        self.socket = None
107
        self.timeout = config.get('timeout')
108
        self.calc = Differential()
109
110
111
    def poll(self):
112
        """Query memcache for stats.
113
114
        :rtype: ResultSet
115
        """
116
        result = plumd.Result("memcache")
117
118
        name = self.name
119
        stats = self.get_stats()
120
        ts = time.time()
121
122
        # record gauges
123
        for stat in self.gauges:
124
            if stat in stats:
125
                mname = "{0}.{1}".format(name, stat)
126
                result.add(plumd.Float(mname, stats[stat]))
127
128
        # record rates
129
        for stat in self.rates:
130
            if stat in stats:
131
                mname = "{0}.{1}".format(name, stat)
132
                mval = self.calc.per_second(mname, float(stats[stat]), ts)
133
                result.add(plumd.Float(mname, mval))
134
135
        return plumd.ResultSet([result])
136
137
138
    def get_stats(self):
139
        """Request and read stats from memcache socket."""
140
        stats = {}
141
142
        if not self.socket and not self.connect():
143
            return {}
144
145
        try:
146
            if PY3:
147
                self.socket.sendall(bytes(Memcache.STAT_CMD, 'utf8'))
148
            else:
149
                self.socket.sendall(Memcache.STAT_CMD)
150
151
            st_str = self.socket.recv(Memcache.RECV_SIZE)
152
            self.log.debug("Memcached: read: {0}".format(st_str))
153
154
            for line in st_str.split("\n"):
155
                vals = line.split()
156
                if len(vals) != 3:
157
                    continue
158
                stype, sname, sval = vals
159
                if stype == "STAT":
160
                    msg = "Memcached: {0} = {1}"
161
                    self.log.debug(msg.format(sname, sval))
162
                    stats[sname] = sval
163
164
        except Exception as e:
165
            msg = "Memcached: {0}: poll: exception: {1}"
166
            self.log.error(msg.format(self.addr, e))
167
            self.disconnect()
168
169
        return stats
170
171
172 View Code Duplication
    def connect(self):
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
173
        """Connect to memcached, returns True if sucessful, False otherwise.
174
175
        :rtype: bool
176
        """
177
        self.log.debug("Memcached: connecting: {0}".format(self.addr))
178
        if self.socket:
179
            self.disconnect()
180
        try:
181
            # create the socket
182
            self.socket = socket.socket(self.sfamily,
183
                                        socket.SOCK_STREAM)
184
            # set timeout for socket operations
185
            self.socket.settimeout(self.timeout)
186
            # connect
187
            self.socket.connect(self.addr)
188
            # log and return
189
            msg = "Memcached: connected: {0}"
190
            self.log.info(msg.format(self.addr))
191
            return True
192
        except Exception as e:
193
            # log exception, ensure cleanup is done (disconnect)
194
            msg = "Memcached: {0}: connect: excception: {1}"
195
            self.log.error(msg.format(self.addr, e))
196
            return False
197
        return False
198
199
200
    def disconnect(self):
201
        """Severe the memcached connection."""
202
        if self.socket:
203
            try:
204
                self.socket.close()
205
                self.socket = None
206
            except Exception as e:
207
                msg = "Memcached: dicsonnect: exception {0}".format(e)
208
                self.log.error(msg)
209
                self.socket = None
210