Completed
Push — master ( c326b1...18b9ac )
by Kenny
03:52
created

Memcache.disconnect()   A

Complexity

Conditions 3

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

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