From: Costa Tsaousis (ktsaou) Date: Thu, 10 Nov 2016 23:04:36 +0000 (+0200) Subject: re-worked SocketService; #999 X-Git-Tag: v1.5.0~189^2~7 X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a07c0acc8429f770d8abbcfea34a705b2d756de3;p=netdata.git re-worked SocketService; #999 --- diff --git a/python.d/python_modules/base.py b/python.d/python_modules/base.py index cf2f7847..04f660d5 100644 --- a/python.d/python_modules/base.py +++ b/python.d/python_modules/base.py @@ -508,6 +508,79 @@ class SocketService(SimpleService): self.__socket_config = None SimpleService.__init__(self, configuration=configuration, name=name) + def _socketerror(self, msg=None): + if self.unix_socket is not None: + self.error("unix socket '" + self.unix_socket + "':", msg) + else: + if self.__socket_config is not None: + af, socktype, proto, canonname, sa = self.__socket_config + self.error("socket to '" + str(sa[0]) + "' port " + str(sa[1]) + ":", msg) + else: + self.error("unknown socket:", msg) + + def _connect2socket(self, res=None): + """ + Connect to a socket, passing the result of getaddrinfo() + :return: boolean + """ + if res is None: + res = self.__socket_config + if res is None: + self.error("Cannot create socket to 'None':") + return False + + af, socktype, proto, canonname, sa = res + try: + self.debug("creating socket to '" + str(sa[0]) + "', port " + str(sa[1])) + self._sock = socket.socket(af, socktype, proto) + except socket.error as e: + self.error("Failed to create socket to '" + str(sa[0]) + "', port " + str(sa[1]) + ":", str(e)) + self._sock = None + self.__socket_config = None + return False + + try: + self.debug("connecting socket to '" + str(sa[0]) + "', port " + str(sa[1])) + self._sock.connect(sa) + except socket.error as e: + self.error("Failed to connect to '" + str(sa[0]) + "', port " + str(sa[1]) + ":", str(e)) + self._disconnect() + self.__socket_config = None + return False + + self.debug("connected to '" + str(sa[0]) + "', port " + str(sa[1])) + self.__socket_config = res + return True + + def _connect2unixsocket(self, path=None): + """ + Connect to a unix socket, given its filename + :return: boolean + """ + if path is None: + self.error("cannot connect to unix socket 'None'") + return False + + try: + self.debug("attempting DGRAM unix socket '" + str(path) + "'") + self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) + self._sock.connect(path) + self.debug("connected DGRAM unix socket '" + str(path) + "'") + return True + except socket.error as e: + self.error("Failed to connect DGRAM unix socket '" + str(path) + "':", str(e)) + + try: + self.debug("attempting STREAM unix socket '" + str(path) + "'") + self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self._sock.connect(path) + self.debug("connected STREAM unix socket '" + str(path) + "'") + return True + except socket.error as e: + self.error("Failed to connect STREAM unix socket '" + str(path) + "':", str(e)) + self._sock = None + return False + def _connect(self): """ Recreate socket and connect to it since sockets cannot be reused after closing @@ -515,59 +588,24 @@ class SocketService(SimpleService): :return: """ try: - if self.unix_socket is None: - if self.__socket_config is None: - # establish ipv6 or ipv4 connection. - for res in socket.getaddrinfo(self.host, self.port, socket.AF_UNSPEC, socket.SOCK_STREAM): - self.debug("connecting socket to host '" + str(self.host) + "', port " + str(self.port)) - try: - # noinspection SpellCheckingInspection - af, socktype, proto, canonname, sa = res - self._sock = socket.socket(af, socktype, proto) - except socket.error as e: - self.debug("Cannot create socket:", str(e)) - self._sock = None - continue - try: - self._sock.connect(sa) - except socket.error as e: - self.debug("Cannot connect to socket:", str(e)) - self._disconnect() - continue - self.__socket_config = res - break - else: - # connect to socket with previously established configuration - try: - af, socktype, proto, canonname, sa = self.__socket_config - self._sock = socket.socket(af, socktype, proto) - self._sock.connect(sa) - except socket.error as e: - self.debug("Cannot create or connect to socket:", str(e)) - self._disconnect() + if self.unix_socket is not None: + _connect2unixsocket(self.unix_socket) + else: - # connect to unix socket - try: - self.debug("connecting unix socket '" + str(self.unix_socket) + "'") - self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) - self._sock.connect(self.unix_socket) - except socket.error: - self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - self._sock.connect(self.unix_socket) + if self.__socket_config is not None: + self._connect2socket() + else: + for res in socket.getaddrinfo(self.host, self.port, socket.AF_UNSPEC, socket.SOCK_STREAM): + if self._connect2socket(res): break except Exception as e: - self.error(str(e), - "Cannot create socket with following configuration: host:", str(self.host), - "port:", str(self.port), - "socket:", str(self.unix_socket)) self._sock = None + self.__socket_config = None if self._sock is not None: self._sock.setblocking(0) self._sock.settimeout(5) - self.debug("connected with timeout " + str(self._sock.gettimeout())) - else: - self.debug("not connected") + self.debug("set socket timeout to: " + str(self._sock.gettimeout())) def _disconnect(self): """ @@ -576,7 +614,7 @@ class SocketService(SimpleService): """ if self._sock is not None: try: - self.debug("disconnecting") + self.error("closing socket") self._sock.shutdown(2) # 0 - read, 1 - write, 2 - all self._sock.close() except Exception: @@ -594,11 +632,8 @@ class SocketService(SimpleService): self.debug("sending request") self._sock.send(self.request) except Exception as e: + self._socketerror("error sending request:" + str(e)) self._disconnect() - self.error(str(e), - "used configuration: host:", str(self.host), - "port:", str(self.port), - "socket:", str(self.unix_socket)) return False return True @@ -610,22 +645,25 @@ class SocketService(SimpleService): data = "" while True: try: - self.debug("receiving response") + self.debug("waiting response") ready_to_read, _, in_error = select.select([self._sock], [], [], 5) except Exception as e: - self.error("timeout while waiting for response:", str(e)) + self._socketerror("timeout while waiting for response:" + str(e)) self._disconnect() break + if len(ready_to_read) > 0: + self.debug("receiving response") buf = self._sock.recv(4096) if buf is None or len(buf) == 0: # handle server disconnect + self.debug("server closed the connection") self._disconnect() break data += buf.decode(errors='ignore') if self._check_raw_data(data): break else: - self.error("Socket timed out.") + self._socketerror("timeout") self._disconnect() break @@ -638,6 +676,8 @@ class SocketService(SimpleService): """ if self._sock is None: self._connect() + if self._sock is None: + return None # Send request if it is needed if not self._send():