@cryptotaxi247 / netdata-1 / commits / a07c0acc8

re-worked SocketService; #999

Costa Tsaousis (ktsaou) committed Nov 11, 2016 at 01:04 UTC a07c0acc8429f770d8abbcfea34a705b2d756de3
1 file changed +93 -53
python.d/python_modules/base.py
+93 -53
@@ -508,6 +508,79 @@ class SocketService(SimpleService):
508 self.__socket_config = None
509 SimpleService.__init__(self, configuration=configuration, name=name)
510
511 + def _socketerror(self, msg=None):
512 + if self.unix_socket is not None:
513 + self.error("unix socket '" + self.unix_socket + "':", msg)
514 + else:
515 + if self.__socket_config is not None:
516 + af, socktype, proto, canonname, sa = self.__socket_config
517 + self.error("socket to '" + str(sa[0]) + "' port " + str(sa[1]) + ":", msg)
518 + else:
519 + self.error("unknown socket:", msg)
520 +
521 + def _connect2socket(self, res=None):
522 + """
523 + Connect to a socket, passing the result of getaddrinfo()
524 + :return: boolean
525 + """
526 + if res is None:
527 + res = self.__socket_config
528 + if res is None:
529 + self.error("Cannot create socket to 'None':")
530 + return False
531 +
532 + af, socktype, proto, canonname, sa = res
533 + try:
534 + self.debug("creating socket to '" + str(sa[0]) + "', port " + str(sa[1]))
535 + self._sock = socket.socket(af, socktype, proto)
536 + except socket.error as e:
537 + self.error("Failed to create socket to '" + str(sa[0]) + "', port " + str(sa[1]) + ":", str(e))
538 + self._sock = None
539 + self.__socket_config = None
540 + return False
541 +
542 + try:
543 + self.debug("connecting socket to '" + str(sa[0]) + "', port " + str(sa[1]))
544 + self._sock.connect(sa)
545 + except socket.error as e:
546 + self.error("Failed to connect to '" + str(sa[0]) + "', port " + str(sa[1]) + ":", str(e))
547 + self._disconnect()
548 + self.__socket_config = None
549 + return False
550 +
551 + self.debug("connected to '" + str(sa[0]) + "', port " + str(sa[1]))
552 + self.__socket_config = res
553 + return True
554 +
555 + def _connect2unixsocket(self, path=None):
556 + """
557 + Connect to a unix socket, given its filename
558 + :return: boolean
559 + """
560 + if path is None:
561 + self.error("cannot connect to unix socket 'None'")
562 + return False
563 +
564 + try:
565 + self.debug("attempting DGRAM unix socket '" + str(path) + "'")
566 + self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
567 + self._sock.connect(path)
568 + self.debug("connected DGRAM unix socket '" + str(path) + "'")
569 + return True
570 + except socket.error as e:
571 + self.error("Failed to connect DGRAM unix socket '" + str(path) + "':", str(e))
572 +
573 + try:
574 + self.debug("attempting STREAM unix socket '" + str(path) + "'")
575 + self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
576 + self._sock.connect(path)
577 + self.debug("connected STREAM unix socket '" + str(path) + "'")
578 + return True
579 + except socket.error as e:
580 + self.error("Failed to connect STREAM unix socket '" + str(path) + "':", str(e))
581 + self._sock = None
582 + return False
583 +
584 def _connect(self):
585 """
586 Recreate socket and connect to it since sockets cannot be reused after closing
@@ -515,59 +588,24 @@ class SocketService(SimpleService):
588 :return:
589 """
590 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 - self.debug("connecting socket to host '" + str(self.host) + "', port " + str(self.port))
523 - try:
524 - # noinspection SpellCheckingInspection
525 - af, socktype, proto, canonname, sa = res
526 - self._sock = socket.socket(af, socktype, proto)
527 - except socket.error as e:
528 - self.debug("Cannot create socket:", str(e))
529 - self._sock = None
530 - continue
531 - try:
532 - self._sock.connect(sa)
533 - except socket.error as e:
534 - self.debug("Cannot connect to socket:", str(e))
535 - self._disconnect()
536 - continue
537 - self.__socket_config = res
538 - break
539 - else:
540 - # connect to socket with previously established configuration
541 - try:
542 - af, socktype, proto, canonname, sa = self.__socket_config
543 - self._sock = socket.socket(af, socktype, proto)
544 - self._sock.connect(sa)
545 - except socket.error as e:
546 - self.debug("Cannot create or connect to socket:", str(e))
547 - self._disconnect()
591 + if self.unix_socket is not None:
592 + _connect2unixsocket(self.unix_socket)
593 +
594 else:
549 - # connect to unix socket
550 - try:
551 - self.debug("connecting unix socket '" + str(self.unix_socket) + "'")
552 - self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
553 - self._sock.connect(self.unix_socket)
554 - except socket.error:
555 - self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
556 - self._sock.connect(self.unix_socket)
595 + if self.__socket_config is not None:
596 + self._connect2socket()
597 + else:
598 + for res in socket.getaddrinfo(self.host, self.port, socket.AF_UNSPEC, socket.SOCK_STREAM):
599 + if self._connect2socket(res): break
600
601 except Exception as e:
559 - self.error(str(e),
560 - "Cannot create socket with following configuration: host:", str(self.host),
561 - "port:", str(self.port),
562 - "socket:", str(self.unix_socket))
602 self._sock = None
603 + self.__socket_config = None
604
605 if self._sock is not None:
606 self._sock.setblocking(0)
607 self._sock.settimeout(5)
568 - self.debug("connected with timeout " + str(self._sock.gettimeout()))
569 - else:
570 - self.debug("not connected")
608 + self.debug("set socket timeout to: " + str(self._sock.gettimeout()))
609
610 def _disconnect(self):
611 """
@@ -576,7 +614,7 @@ class SocketService(SimpleService):
614 """
615 if self._sock is not None:
616 try:
579 - self.debug("disconnecting")
617 + self.error("closing socket")
618 self._sock.shutdown(2) # 0 - read, 1 - write, 2 - all
619 self._sock.close()
620 except Exception:
@@ -594,11 +632,8 @@ class SocketService(SimpleService):
632 self.debug("sending request")
633 self._sock.send(self.request)
634 except Exception as e:
635 + self._socketerror("error sending request:" + str(e))
636 self._disconnect()
598 - self.error(str(e),
599 - "used configuration: host:", str(self.host),
600 - "port:", str(self.port),
601 - "socket:", str(self.unix_socket))
637 return False
638 return True
639
@@ -610,22 +645,25 @@ class SocketService(SimpleService):
645 data = ""
646 while True:
647 try:
613 - self.debug("receiving response")
648 + self.debug("waiting response")
649 ready_to_read, _, in_error = select.select([self._sock], [], [], 5)
650 except Exception as e:
616 - self.error("timeout while waiting for response:", str(e))
651 + self._socketerror("timeout while waiting for response:" + str(e))
652 self._disconnect()
653 break
654 +
655 if len(ready_to_read) > 0:
656 + self.debug("receiving response")
657 buf = self._sock.recv(4096)
658 if buf is None or len(buf) == 0: # handle server disconnect
659 + self.debug("server closed the connection")
660 self._disconnect()
661 break
662 data += buf.decode(errors='ignore')
663 if self._check_raw_data(data):
664 break
665 else:
628 - self.error("Socket timed out.")
666 + self._socketerror("timeout")
667 self._disconnect()
668 break
669
@@ -638,6 +676,8 @@ class SocketService(SimpleService):
676 """
677 if self._sock is None:
678 self._connect()
679 + if self._sock is None:
680 + return None
681
682 # Send request if it is needed
683 if not self._send():