@cryptotaxi247 / netdata-1 / commits / 7d5c11451

1. changed arguments from decode() in base.py, so they can run under CentOS 6.8 with Python 2.6 2. added pass parameters to redis for AUTH command 3. optimized nginx.chart.py and phpfpm.chart.py webpages submenu name 4. optimized phpfpm.chart.py average display

1. changed arguments from decode() in base.py, so they can run under CentOS 6.8 with Python 2.6 2. added pass parameters to redis for AUTH command 3. optimized nginx.chart.py and phpfpm.chart.py webpages submenu name 4. optimized phpfpm.chart.py average display

liu lei committed Nov 17, 2016 at 03:11 UTC 7d5c114516ae6cce23803807d5893d7a617ae243
4 files changed +270 -148
python.d/nginx.chart.py
+4 -4
@@ -22,24 +22,24 @@ ORDER = ['connections', 'requests', 'connection_status', 'connect_rate']
22
23 CHARTS = {
24 'connections': {
25 - 'options': [None, 'nginx Active Connections', 'connections', 'Active Connections', 'nginx.connections', 'line'],
25 + 'options': [None, 'nginx Active Connections', 'connections', 'active connections', 'nginx.connections', 'line'],
26 'lines': [
27 ["active"]
28 ]},
29 'requests': {
30 - 'options': [None, 'nginx Requests', 'requests/s', 'Requests', 'nginx.requests', 'line'],
30 + 'options': [None, 'nginx Requests', 'requests/s', 'requests', 'nginx.requests', 'line'],
31 'lines': [
32 ["requests", None, 'incremental']
33 ]},
34 'connection_status': {
35 - 'options': [None, 'nginx Active Connections by Status', 'connections', 'Active Connection Status', 'nginx.connection_status', 'line'],
35 + 'options': [None, 'nginx Active Connections by Status', 'connections', 'status', 'nginx.connection_status', 'line'],
36 'lines': [
37 ["reading"],
38 ["writing"],
39 ["waiting", "idle"]
40 ]},
41 'connect_rate': {
42 - 'options': [None, 'nginx Connections Rate', 'connections/s', 'Connections Rate', 'nginx.connect_rate', 'line'],
42 + 'options': [None, 'nginx Connections Rate', 'connections/s', 'connections rate', 'nginx.connect_rate', 'line'],
43 'lines': [
44 ["accepts", "accepted", "incremental"],
45 ["handled", None, "incremental"]
python.d/phpfpm.chart.py
+6 -9
@@ -23,37 +23,37 @@ ORDER = ['connections', 'requests', 'performance', 'request_duration', 'request_
23
24 CHARTS = {
25 'connections': {
26 - 'options': [None, 'PHP-FPM Active Connections', 'connections', 'Active Connections', 'phpfpm.connections', 'line'],
26 + 'options': [None, 'PHP-FPM Active Connections', 'connections', 'active connections', 'phpfpm.connections', 'line'],
27 'lines': [
28 ["active"],
29 ["maxActive", 'max active'],
30 ["idle"]
31 ]},
32 'requests': {
33 - 'options': [None, 'PHP-FPM Requests', 'requests/s', 'Requests', 'phpfpm.requests', 'line'],
33 + 'options': [None, 'PHP-FPM Requests', 'requests/s', 'requests', 'phpfpm.requests', 'line'],
34 'lines': [
35 ["requests", None, "incremental"]
36 ]},
37 'performance': {
38 - 'options': [None, 'PHP-FPM Performance', 'status', 'Performance', 'phpfpm.performance', 'line'],
38 + 'options': [None, 'PHP-FPM Performance', 'status', 'performance', 'phpfpm.performance', 'line'],
39 'lines': [
40 ["reached", 'max children reached'],
41 ["slow", 'slow requests']
42 ]},
43 'request_duration': {
44 - 'options': [None, 'PHP-FPM Request Duration', 'milliseconds', 'Request Duration', 'phpfpm.request_duration', 'line'],
44 + 'options': [None, 'PHP-FPM Request Duration', 'milliseconds', 'request duration', 'phpfpm.request_duration', 'line'],
45 'lines': [
46 ["maxReqDur", 'max request duration'],
47 ["avgReqDur", 'average request duration']
48 ]},
49 'request_cpu': {
50 - 'options': [None, 'PHP-FPM Request CPU', 'percent', 'Request CPU', 'phpfpm.request_cpu', 'line'],
50 + 'options': [None, 'PHP-FPM Request CPU', 'percent', 'request CPU', 'phpfpm.request_cpu', 'line'],
51 'lines': [
52 ["maxReqCPU", 'max request cpu'],
53 ["avgReqCPU", 'average request cpu']
54 ]},
55 'request_mem': {
56 - 'options': [None, 'PHP-FPM Request Memory', 'kilobytes', 'Request Memory', 'phpfpm.request_mem', 'line'],
56 + 'options': [None, 'PHP-FPM Request Memory', 'kilobytes', 'request memory', 'phpfpm.request_mem', 'line'],
57 'lines': [
58 ["maxReqMem", 'max request memory'],
59 ["avgReqMem", 'average request memory']
@@ -137,6 +137,3 @@ class Service(UrlService):
137 if len(data) == 0:
138 return None
139 return data
140 -
141 - def check(self):
142 - return UrlService.check(self)
python.d/python_modules/base.py
+183 -94
@@ -116,33 +116,21 @@ class SimpleService(threading.Thread):
116 Return value presents exit status of update()
117 :return: boolean
118 """
119 - t_start = time.time()
120 - timetable = self.timetable
119 + t_start = float(time.time())
120 chart_name = self.chart_name
122 - # check if it is time to execute job update() function
123 - if timetable['next'] > t_start:
124 - self.debug(chart_name, "will be run in", str(int((timetable['next'] - t_start) * 1000)), "ms")
125 - return True
121
127 - since_last = int((t_start - timetable['last']) * 1000000)
128 - self.debug(chart_name,
129 - "ready to run, after", str(int((t_start - timetable['last']) * 1000)),
130 - "ms (update_every:", str(timetable['freq'] * 1000),
131 - "ms, latency:", str(int((t_start - timetable['next']) * 1000)), "ms")
122 + since_last = int((t_start - self.timetable['last']) * 1000000)
123 if self.__first_run:
124 since_last = 0
125 +
126 if not self.update(since_last):
127 self.error("update function failed.")
128 return False
137 - t_end = time.time()
138 - self.timetable['next'] = t_end - (t_end % timetable['freq']) + timetable['freq']
129 +
130 # draw performance graph
140 - run_time = str(int((t_end - t_start) * 1000))
141 - # noinspection SqlNoDataSourceInspection
131 + run_time = int((time.time() - t_start) * 1000)
132 print("BEGIN netdata.plugin_pythond_%s %s\nSET run_time = %s\nEND\n" %
143 - (self.chart_name, str(since_last), run_time))
144 - # sys.stdout.write("BEGIN netdata.plugin_pythond_%s %s\nSET run_time = %s\nEND\n" %
145 - # (self.chart_name, str(since_last), run_time))
133 + (self.chart_name, str(since_last), str(run_time)))
134
135 self.debug(chart_name, "updated in", str(run_time), "ms")
136 self.timetable['last'] = t_start
@@ -155,23 +143,49 @@ class SimpleService(threading.Thread):
143 Exits when job failed or timed out.
144 :return: None
145 """
158 - self.timetable['last'] = time.time()
146 + step = float(self.timetable['freq'])
147 + penalty = 0
148 + self.timetable['last'] = float(time.time() - step)
149 + self.debug("starting data collection - update frequency:", str(step), " retries allowed:", str(self.retries))
150 while True: # run forever, unless something is wrong
151 + now = float(time.time())
152 + next = self.timetable['next'] = now - (now % step) + step + penalty
153 +
154 + # it is important to do this in a loop
155 + # sleep() is interruptable
156 + while now < next:
157 + self.debug("sleeping for", str(next - now), "secs to reach frequency of", str(step), "secs, now:", str(now), " next:", str(next), " penalty:", str(penalty))
158 + time.sleep(next - now)
159 + now = float(time.time())
160 +
161 + # do the job
162 try:
163 status = self._run_once()
164 except Exception as e:
163 - self.error("Something wrong: ", str(e))
165 + self.alert("internal error - aborting data collection: " + str(e))
166 return
165 - if status: # handle retries if update failed
166 - time.sleep(self.timetable['next'] - time.time())
167 +
168 + if status:
169 + # it is good
170 self.retries_left = self.retries
171 + penalty = 0
172 else:
173 + # it failed
174 self.retries_left -= 1
175 if self.retries_left <= 0:
171 - self.error("no more retries. Exiting")
172 - return
176 + if penalty == 0:
177 + penalty = float(self.retries * step) / 2
178 + else:
179 + penalty *= 1.5
180 +
181 + if penalty > 600:
182 + penalty = 600
183 +
184 + self.retries_left = self.retries
185 + self.alert("failed to collect data for " + str(self.retries) + " times - increasing penalty to " + str(penalty) + " sec and trying again")
186 +
187 else:
174 - time.sleep(self.timetable['freq'])
188 + self.error("failed to collect data - " + str(self.retries_left) + " retries left - penalty: " + str(penalty) + " sec")
189
190 # --- CHART ---
191
@@ -318,6 +332,12 @@ class SimpleService(threading.Thread):
332 """
333 msg.error(self.chart_name, *params)
334
335 + def alert(self, *params):
336 + """
337 + Show error message on stderr
338 + """
339 + msg.alert(self.chart_name, *params)
340 +
341 def debug(self, *params):
342 """
343 Show debug message on stderr
@@ -345,10 +365,18 @@ class SimpleService(threading.Thread):
365 :return: boolean
366 """
367 self.debug("Module", str(self.__module__), "doesn't implement check() function. Using default.")
348 - if self._get_data() is None or len(self._get_data()) == 0:
368 + data = self._get_data()
369 +
370 + if data is None:
371 + self.debug("failed to receive data during check().")
372 return False
350 - else:
351 - return True
373 +
374 + if len(data) == 0:
375 + self.debug("empty data during check().")
376 + return False
377 +
378 + self.debug("successfully received data during check(): '" + str(data) + "'")
379 + return True
380
381 def create(self):
382 """
@@ -357,6 +385,7 @@ class SimpleService(threading.Thread):
385 """
386 data = self._get_data()
387 if data is None:
388 + self.debug("failed to receive data during create().")
389 return False
390
391 idx = 0
@@ -380,7 +409,7 @@ class SimpleService(threading.Thread):
409 """
410 data = self._get_data()
411 if data is None:
383 - self.debug("_get_data() returned no data")
412 + self.debug("failed to receive data during update().")
413 return False
414
415 updated = False
@@ -506,8 +535,83 @@ class SocketService(SimpleService):
535 self.unix_socket = None
536 self.request = ""
537 self.__socket_config = None
538 + self.__empty_request = "".encode()
539 SimpleService.__init__(self, configuration=configuration, name=name)
540
541 + def _socketerror(self, message=None):
542 + if self.unix_socket is not None:
543 + self.error("unix socket '" + self.unix_socket + "':", message)
544 + else:
545 + if self.__socket_config is not None:
546 + af, socktype, proto, canonname, sa = self.__socket_config
547 + self.error("socket to '" + str(sa[0]) + "' port " + str(sa[1]) + ":", message)
548 + else:
549 + self.error("unknown socket:", message)
550 +
551 + def _connect2socket(self, res=None):
552 + """
553 + Connect to a socket, passing the result of getaddrinfo()
554 + :return: boolean
555 + """
556 + if res is None:
557 + res = self.__socket_config
558 + if res is None:
559 + self.error("Cannot create socket to 'None':")
560 + return False
561 +
562 + af, socktype, proto, canonname, sa = res
563 + try:
564 + self.debug("creating socket to '" + str(sa[0]) + "', port " + str(sa[1]))
565 + self._sock = socket.socket(af, socktype, proto)
566 + except socket.error as e:
567 + self.error("Failed to create socket to '" + str(sa[0]) + "', port " + str(sa[1]) + ":", str(e))
568 + self._sock = None
569 + self.__socket_config = None
570 + return False
571 +
572 + try:
573 + self.debug("connecting socket to '" + str(sa[0]) + "', port " + str(sa[1]))
574 + self._sock.connect(sa)
575 + except socket.error as e:
576 + self.error("Failed to connect to '" + str(sa[0]) + "', port " + str(sa[1]) + ":", str(e))
577 + self._disconnect()
578 + self.__socket_config = None
579 + return False
580 +
581 + self.debug("connected to '" + str(sa[0]) + "', port " + str(sa[1]))
582 + self.__socket_config = res
583 + return True
584 +
585 + def _connect2unixsocket(self):
586 + """
587 + Connect to a unix socket, given its filename
588 + :return: boolean
589 + """
590 + if self.unix_socket is None:
591 + self.error("cannot connect to unix socket 'None'")
592 + return False
593 +
594 + try:
595 + self.debug("attempting DGRAM unix socket '" + str(self.unix_socket) + "'")
596 + self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
597 + self._sock.connect(self.unix_socket)
598 + self.debug("connected DGRAM unix socket '" + str(self.unix_socket) + "'")
599 + return True
600 + except socket.error as e:
601 + self.debug("Failed to connect DGRAM unix socket '" + str(self.unix_socket) + "':", str(e))
602 +
603 + try:
604 + self.debug("attempting STREAM unix socket '" + str(self.unix_socket) + "'")
605 + self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
606 + self._sock.connect(self.unix_socket)
607 + self.debug("connected STREAM unix socket '" + str(self.unix_socket) + "'")
608 + return True
609 + except socket.error as e:
610 + self.debug("Failed to connect STREAM unix socket '" + str(self.unix_socket) + "':", str(e))
611 + self.error("Failed to connect to unix socket '" + str(self.unix_socket) + "':", str(e))
612 + self._sock = None
613 + return False
614 +
615 def _connect(self):
616 """
617 Recreate socket and connect to it since sockets cannot be reused after closing
@@ -515,63 +619,38 @@ class SocketService(SimpleService):
619 :return:
620 """
621 try:
518 - if self.unix_socket is None:
519 - if self.__socket_config is None:
520 - # establish ipv6 or ipv4 connection.
521 - for res in socket.getaddrinfo(self.host, self.port, socket.AF_UNSPEC, socket.SOCK_STREAM):
522 - try:
523 - # noinspection SpellCheckingInspection
524 - af, socktype, proto, canonname, sa = res
525 - self._sock = socket.socket(af, socktype, proto)
526 - except socket.error as e:
527 - self.debug("Cannot create socket:", str(e))
528 - self._sock = None
529 - continue
530 - try:
531 - self._sock.connect(sa)
532 - except socket.error as e:
533 - self.debug("Cannot connect to socket:", str(e))
534 - self._disconnect()
535 - continue
536 - self.__socket_config = res
537 - break
538 - else:
539 - # connect to socket with previously established configuration
540 - try:
541 - af, socktype, proto, canonname, sa = self.__socket_config
542 - self._sock = socket.socket(af, socktype, proto)
543 - self._sock.connect(sa)
544 - except socket.error as e:
545 - self.debug("Cannot create or connect to socket:", str(e))
546 - self._disconnect()
622 + if self.unix_socket is not None:
623 + self._connect2unixsocket()
624 +
625 else:
548 - # connect to unix socket
549 - try:
550 - self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
551 - self._sock.connect(self.unix_socket)
552 - except socket.error:
553 - self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
554 - self._sock.connect(self.unix_socket)
626 + if self.__socket_config is not None:
627 + self._connect2socket()
628 + else:
629 + for res in socket.getaddrinfo(self.host, self.port, socket.AF_UNSPEC, socket.SOCK_STREAM):
630 + if self._connect2socket(res): break
631
632 except Exception as e:
557 - self.error(str(e),
558 - "Cannot create socket with following configuration: host:", str(self.host),
559 - "port:", str(self.port),
560 - "socket:", str(self.unix_socket))
633 self._sock = None
562 - self._sock.setblocking(0)
634 + self.__socket_config = None
635 +
636 + if self._sock is not None:
637 + self._sock.setblocking(0)
638 + self._sock.settimeout(5)
639 + self.debug("set socket timeout to: " + str(self._sock.gettimeout()))
640
641 def _disconnect(self):
642 """
643 Close socket connection
644 :return:
645 """
569 - try:
570 - self._sock.shutdown(2) # 0 - read, 1 - write, 2 - all
571 - self._sock.close()
572 - except Exception:
573 - pass
574 - self._sock = None
646 + if self._sock is not None:
647 + try:
648 + self.debug("closing socket")
649 + self._sock.shutdown(2) # 0 - read, 1 - write, 2 - all
650 + self._sock.close()
651 + except Exception:
652 + pass
653 + self._sock = None
654
655 def _send(self):
656 """
@@ -579,15 +658,13 @@ class SocketService(SimpleService):
658 :return: boolean
659 """
660 # Send request if it is needed
582 - if self.request != "".encode():
661 + if self.request != self.__empty_request:
662 try:
663 + self.debug("sending request:", str(self.request))
664 self._sock.send(self.request)
665 except Exception as e:
666 + self._socketerror("error sending request:" + str(e))
667 self._disconnect()
587 - self.error(str(e),
588 - "used configuration: host:", str(self.host),
589 - "port:", str(self.port),
590 - "socket:", str(self.unix_socket))
668 return False
669 return True
670
@@ -598,24 +675,28 @@ class SocketService(SimpleService):
675 """
676 data = ""
677 while True:
678 + self.debug("receiving response")
679 try:
602 - ready_to_read, _, in_error = select.select([self._sock], [], [], 5)
680 + buf = self._sock.recv(4096)
681 except Exception as e:
604 - self.debug("SELECT", str(e))
682 + self._socketerror("failed to receive response:" + str(e))
683 self._disconnect()
684 break
607 - if len(ready_to_read) > 0:
608 - buf = self._sock.recv(4096)
609 - if len(buf) == 0 or buf is None: # handle server disconnect
610 - break
611 - data += buf.decode('utf-8', 'ignore')
612 - if self._check_raw_data(data):
613 - break
614 - else:
615 - self.error("Socket timed out.")
685 +
686 + if buf is None or len(buf) == 0: # handle server disconnect
687 + if data == "":
688 + self._socketerror("unexpectedly disconnected")
689 + else:
690 + self.debug("server closed the connection")
691 self._disconnect()
692 break
693
694 + self.debug("received data:", str(buf))
695 + data += buf.decode('utf-8', 'ignore')
696 + if self._check_raw_data(data):
697 + break
698 +
699 + self.debug("final response:", str(data))
700 return data
701
702 def _get_raw_data(self):
@@ -625,6 +706,8 @@ class SocketService(SimpleService):
706 """
707 if self._sock is None:
708 self._connect()
709 + if self._sock is None:
710 + return None
711
712 # Send request if it is needed
713 if not self._send():
@@ -654,6 +737,7 @@ class SocketService(SimpleService):
737 self.name = ""
738 else:
739 self.name = str(self.name)
740 +
741 try:
742 self.unix_socket = str(self.configuration['socket'])
743 except (KeyError, TypeError):
@@ -667,10 +751,12 @@ class SocketService(SimpleService):
751 self.port = int(self.configuration['port'])
752 except (KeyError, TypeError):
753 self.debug("No port specified. Using: '" + str(self.port) + "'")
754 +
755 try:
756 self.request = str(self.configuration['request'])
757 except (KeyError, TypeError):
758 self.debug("No request specified. Using: '" + str(self.request) + "'")
759 +
760 self.request = self.request.encode()
761
762 def check(self):
@@ -724,7 +810,7 @@ class LogService(SimpleService):
810 try:
811 self.log_path = str(self.configuration['path'])
812 except (KeyError, TypeError):
727 - self.error("No path to log specified. Using: '" + self.log_path + "'")
813 + self.info("No path to log specified. Using: '" + self.log_path + "'")
814
815 if os.access(self.log_path, os.R_OK):
816 return True
@@ -779,13 +865,14 @@ class ExecutableService(SimpleService):
865 try:
866 self.command = str(self.configuration['command'])
867 except (KeyError, TypeError):
782 - self.error("No command specified. Using: '" + self.command + "'")
868 + self.info("No command specified. Using: '" + self.command + "'")
869 command = self.command.split(' ')
870
871 for arg in command[1:]:
872 if any(st in arg for st in self.bad_substrings):
873 self.error("Bad command argument:" + " ".join(self.command[1:]))
874 return False
875 +
876 # test command and search for it in /usr/sbin or /sbin when failed
877 base = command[0].split('/')[-1]
878 if self._get_raw_data() is None:
@@ -793,8 +880,10 @@ class ExecutableService(SimpleService):
880 command[0] = prefix + base
881 if os.path.isfile(command[0]):
882 break
883 +
884 self.command = command
885 if self._get_data() is None or len(self._get_data()) == 0:
886 self.error("Command", self.command, "returned no data")
887 return False
888 +
889 return True
python.d/redis.chart.py
+77 -41
@@ -19,40 +19,58 @@ retries = 60
19 # 'unix_socket': None
20 # }}
21
22 -ORDER = ['operations', 'hit_rate', 'memory', 'keys', 'clients', 'slaves']
22 +ORDER = ['operations', 'hit_rate', 'memory', 'keys', 'net', 'connections', 'clients', 'slaves', 'persistence']
23
24 CHARTS = {
25 'operations': {
26 - 'options': [None, 'Operations', 'operations/s', 'Statistics', 'redis.operations', 'line'],
26 + 'options': [None, 'Redis Operations', 'operations/s', 'operations', 'redis.operations', 'line'],
27 'lines': [
28 + ['total_commands_processed', 'commands', 'incremental'],
29 ['instantaneous_ops_per_sec', 'operations', 'absolute']
30 ]},
31 'hit_rate': {
31 - 'options': [None, 'Hit rate', 'percent', 'Statistics', 'redis.hit_rate', 'line'],
32 + 'options': [None, 'Redis Hit rate', 'percent', 'hits', 'redis.hit_rate', 'line'],
33 'lines': [
34 ['hit_rate', 'rate', 'absolute']
35 ]},
36 'memory': {
36 - 'options': [None, 'Memory utilization', 'kilobytes', 'Memory', 'redis.memory', 'line'],
37 + 'options': [None, 'Redis Memory utilization', 'kilobytes', 'memory', 'redis.memory', 'line'],
38 'lines': [
39 ['used_memory', 'total', 'absolute', 1, 1024],
40 ['used_memory_lua', 'lua', 'absolute', 1, 1024]
41 ]},
42 + 'net': {
43 + 'options': [None, 'Redis Bandwidth', 'kilobits/s', 'network', 'redis.net', 'area'],
44 + 'lines': [
45 + ['total_net_input_bytes', 'in', 'incremental', 8, 1024],
46 + ['total_net_output_bytes', 'out', 'incremental', -8, 1024]
47 + ]},
48 'keys': {
42 - 'options': [None, 'Database keys', 'keys', 'Keys', 'redis.keys', 'line'],
49 + 'options': [None, 'Redis Keys per Database', 'keys', 'keys', 'redis.keys', 'line'],
50 'lines': [
51 # lines are created dynamically in `check()` method
52 ]},
53 + 'connections': {
54 + 'options': [None, 'Redis Connections', 'connections/s', 'connections', 'redis.connections', 'line'],
55 + 'lines': [
56 + ['total_connections_received', 'received', 'incremental', 1],
57 + ['rejected_connections', 'rejected', 'incremental', -1]
58 + ]},
59 'clients': {
47 - 'options': [None, 'Clients', 'clients', 'Clients', 'redis.clients', 'line'],
60 + 'options': [None, 'Redis Clients', 'clients', 'connections', 'redis.clients', 'line'],
61 'lines': [
49 - ['connected_clients', 'connected', 'absolute'],
50 - ['blocked_clients', 'blocked', 'absolute']
62 + ['connected_clients', 'connected', 'absolute', 1],
63 + ['blocked_clients', 'blocked', 'absolute', -1]
64 ]},
65 'slaves': {
53 - 'options': [None, 'Slaves', 'slaves', 'Replication', 'redis.slaves', 'line'],
66 + 'options': [None, 'Redis Slaves', 'slaves', 'replication', 'redis.slaves', 'line'],
67 'lines': [
68 ['connected_slaves', 'connected', 'absolute']
69 + ]},
70 + 'persistence': {
71 + 'options': [None, 'Redis Persistence Changes Since Last Save', 'changes', 'persistence', 'redis.rdb_changes', 'line'],
72 + 'lines': [
73 + ['rdb_changes_since_last_save', 'changes', 'absolute']
74 ]}
75 }
76
@@ -61,57 +79,74 @@ class Service(SocketService):
79 def __init__(self, configuration=None, name=None):
80 SocketService.__init__(self, configuration=configuration, name=name)
81 self.request = "INFO\r\n"
64 - self.host = "localhost"
65 - self.port = 6379
66 - self.unix_socket = None
82 self.order = ORDER
83 self.definitions = CHARTS
84 self._keep_alive = True
85 self.chart_name = ""
71 - self.passwd = None
86 + self.passwd = None
87 + self.port = 6379
88 + if 'port' in configuration:
89 + self.port = configuration['port']
90 if 'pass' in configuration:
91 self.passwd = configuration['pass']
92 + if 'host' in configuration:
93 + self.host = configuration['host']
94 + if 'socket' in configuration:
95 + self.unix_socket = configuration['socket']
96
97 def _get_data(self):
98 """
99 Get data from socket
100 :return: dict
101 """
102 + if self.passwd:
103 + info_request = self.request
104 + self.request = "AUTH " + self.passwd + "\r\n"
105 + raw = self._get_raw_data().strip()
106 + if raw != "+OK":
107 + self.error("invalid password")
108 + return None
109 + self.request = info_request
110 +
111 + response = self._get_raw_data()
112 + if response is None:
113 + # error has already been logged
114 + return None
115 +
116 try:
81 - if self.passwd:
82 - info_request = self.request
83 - self.request = "AUTH " + self.passwd + "\r\n"
84 - raw = self._get_raw_data().strip()
85 - if raw != "+OK":
86 - self.error("invalid password")
87 - return None
88 - self.request = info_request
89 - raw = self._get_raw_data().split("\n")
117 + parsed = response.split("\n")
118 except AttributeError:
91 - self.error("no data received")
119 + self.error("response is invalid/empty")
120 return None
121 +
122 data = {}
94 - for line in raw:
95 - if line.startswith(('instantaneous', 'keyspace', 'used_memory', 'connected', 'blocked')):
96 - try:
97 - t = line.split(':')
98 - data[t[0]] = int(t[1])
99 - except (IndexError, ValueError):
100 - pass
101 - elif line.startswith('db'):
123 + for line in parsed:
124 + if len(line) < 5 or line[0] == '$' or line[0] == '#':
125 + continue
126 +
127 + if line.startswith('db'):
128 tmp = line.split(',')[0].replace('keys=', '')
129 record = tmp.split(':')
104 - data[record[0]] = int(record[1])
130 + data[record[0]] = record[1]
131 + continue
132 +
133 + try:
134 + t = line.split(':')
135 + data[t[0]] = t[1]
136 + except (IndexError, ValueError):
137 + self.debug("invalid line received: " + str(line))
138 + pass
139 +
140 + if len(data) == 0:
141 + self.error("received data doesn't have any records")
142 + return None
143 +
144 try:
106 - data['hit_rate'] = int((data['keyspace_hits'] / float(data['keyspace_hits'] + data['keyspace_misses'])) * 100)
145 + data['hit_rate'] = (int(data['keyspace_hits']) * 100) / (int(data['keyspace_hits']) + int(data['keyspace_misses']))
146 except:
147 data['hit_rate'] = 0
148
110 - if len(data) == 0:
111 - self.error("received data doesn't have needed records")
112 - return None
113 - else:
114 - return data
149 + return data
150
151 def _check_raw_data(self, data):
152 """
@@ -123,14 +158,15 @@ class Service(SocketService):
158 length = len(data)
159 supposed = data.split('\n')[0][1:]
160 offset = len(supposed) + 4 # 1 dollar sing, 1 new line character + 1 ending sequence '\r\n'
126 - if (not supposed.isdigit()) :
161 + if not supposed.isdigit():
162 return True
163 supposed = int(supposed)
164 +
165 if length - offset >= supposed:
166 + self.debug("received full response from redis")
167 return True
131 - else:
132 - return False
168
169 + self.debug("waiting more data from redis")
170 return False
171
172 def check(self):