def _check_and_response(value, warning, critical): """Checks a value and responds with an appropriate code.""" try: if not _check_range(critical, value): _response(_STATUS_CRITICAL, str(value)) elif not _check_range(warning, value): _response(_STATUS_WARNING, str(value)) else: _response(_STATUS_OK, str(value)) except _RangeFormatError as e: _response(_STATUS_UNKNOWN, str(e))
def main(): """The script's main function.""" try: parser = argparse.ArgumentParser( description="XBee Nagios plugin") parser.add_argument("host", help="host") parser.add_argument("metric", choices=["temperature"], help="metric name") parser.add_argument("-w", "--warning", metavar="VALUE", help="warning threshold", required=True) parser.add_argument("-c", "--critical", metavar="VALUE", help="critical threshold", required=True) args = parser.parse_args() if args.metric == "temperature": _check_and_response(_get_temperature(args.host), args.warning, args.critical) else: raise LogicalError() except Exception as e: _response(_STATUS_CRITICAL if isinstance(e, Error) else _STATUS_UNKNOWN, str(e))
def __handle_request(self): """Handles a request.""" try: request = json.loads(self._read_buffer.decode("utf-8")) if ( "method" not in request or any(type(key) is not str for key in request.keys()) or any(type(value) is not str for value in request.values()) ): raise ValueError() except (UnicodeDecodeError, ValueError): LOG.error("%s: got an invalid request %s.", self, bytes(self._read_buffer)) self.close() return LOG.info("%s: request %s", self, request) try: reply = { "result": monitor.request.handle(request.pop("method"), request) } except Exception as e: (LOG.warning if isinstance(e, Error) else LOG.error)( "%s: request failed: %s", self, e) reply = { "error": str(e) if isinstance(e, Error) else "Internal error" } response = json.dumps(reply).encode("utf-8") if self._write(struct.pack(self.__message_size_format, len(response)) + response): self.close()
def _shell_command_full(self): """ Generates a shell command which execution is equal to self.execute() including handling of errors in the middle of the process pipe. """ command = BytesIO() pipe_ok_statuses = [] self._shell_command(command, pipe_ok_statuses) if len(pipe_ok_statuses) == 1: return command.getvalue() command.write(b"; statuses=(${PIPESTATUS[@]});") for process_id, ok_statuses in enumerate(pipe_ok_statuses): if process_id == len(pipe_ok_statuses) - 1: command.write( psys.b(" exit ${{statuses[{0}]}};".format(process_id))) else: command.write( psys.b(" case ${{statuses[{0}]}} in".format(process_id))) if ok_statuses: command.write( psys.b(" {0});;".format("|".join( str(status) for status in ok_statuses)))) command.write(b" *) exit 128;; esac;") return b"bash -c '" + command.getvalue().replace(b"'", b"""'"'"'""") + b"'"
def _shell_command_full(self): """ Generates a shell command which execution is equal to self.execute() including handling of errors in the middle of the process pipe. """ command = BytesIO() pipe_ok_statuses = [] self._shell_command(command, pipe_ok_statuses) if len(pipe_ok_statuses) == 1: return command.getvalue() command.write(b"; statuses=(${PIPESTATUS[@]});") for process_id, ok_statuses in enumerate(pipe_ok_statuses): if process_id == len(pipe_ok_statuses) - 1: command.write(psys.b(" exit ${{statuses[{0}]}};".format(process_id))) else: command.write(psys.b(" case ${{statuses[{0}]}} in".format(process_id))) if ok_statuses: command.write(psys.b(" {0});;".format("|".join(str(status) for status in ok_statuses)))) command.write(b" *) exit 128;; esac;") return b"bash -c '" + command.getvalue().replace(b"'", b"""'"'"'""") + b"'"
def test_pid(test): """Tests pid().""" process = sh.sh("-c", "echo $$") with pytest.raises(psh.InvalidProcessState): process.pid() assert process.execute().stdout().strip() == str(process.pid())
def test_iterator_input(test): """Tests process input from string.""" stdout = "\n".join(str(i) for i in range(0, 10)) def func(): for i in range(0, 10): yield "\n" + str(i) if i else str(i) assert sh.cat(_stdin=func()).execute().stdout() == stdout
def _get_arg_value(value, shell): """Returns an argument string value.""" if type(value) in (bytes, str): return value elif type(value) in (int, float) + (tuple() if PY3 else (long,)): return str(value) elif shell and isinstance(value, Process): return value._shell_command_full() else: raise InvalidArgument("Invalid argument: command arguments must be basic types only")
def _get_arg_value(value, shell): """Returns an argument string value.""" if type(value) in (bytes, str): return value elif type(value) in (int, float) + (tuple() if PY3 else (long, )): return str(value) elif shell and isinstance(value, Process): return value._shell_command_full() else: raise InvalidArgument( "Invalid argument: command arguments must be basic types only")
def test_output_iteration_with_large_data(test): """ Tests iteration over process' output with large amount of data (more than any buffer size). """ stdin = [str(i) + "\n" for i in range(0, 100000)] with sh.cat(_stdin="".join(stdin)) as process: stdout = [line for line in process] assert stdout == stdin
def wait(self, check_status=False, kill=None): """Waits for the process termination. :param check_status: if :py:const:`True`, check the process status code (see :ref:`exit-codes`) and other (communication) errors and raise an exception if any error occurred. :type check_status: bool :param kill: if is not :py:const:`None`, kills the process with the ``kill(signal = kill)`` and waits for its termination. :type kill: int :returns: the process exit status """ if self.__state < _PROCESS_STATE_RUNNING: raise InvalidProcessState("Process is not running") LOG.debug( "Waiting for %s termination%s...", self, "" if kill is None else " killing it with {0} signal".format(kill)) if kill is not None: while self.kill(kill): if self.__join_threads(0.1): break self.__join_threads() if self.__piped_from_process(): self.__stdin_source.wait(check_status=check_status, kill=kill) if check_status: if self.__error is not None: raise self.__error if self.__status not in self.__ok_statuses: raise ExecutionError(str(self), self.__status, self.raw_stdout(), self.raw_stderr()) return self.__status
def wait(self, check_status=False, kill=None): """Waits for the process termination. :param check_status: if :py:const:`True`, check the process status code (see :ref:`exit-codes`) and other (communication) errors and raise an exception if any error occurred. :type check_status: bool :param kill: if is not :py:const:`None`, kills the process with the ``kill(signal = kill)`` and waits for its termination. :type kill: int :returns: the process exit status """ if self.__state < _PROCESS_STATE_RUNNING: raise InvalidProcessState("Process is not running") LOG.debug("Waiting for %s termination%s...", self, "" if kill is None else " killing it with {0} signal".format(kill)) if kill is not None: while self.kill(kill): if self.__join_threads(0.1): break self.__join_threads() if self.__piped_from_process(): self.__stdin_source.wait( check_status=check_status, kill=kill) if check_status: if self.__error is not None: raise self.__error if self.__status not in self.__ok_statuses: raise ExecutionError(str(self), self.__status, self.raw_stdout(), self.raw_stderr()) return self.__status
def test_command_arguments(test): """Tests command argument parsing.""" process = sh.test() assert process.command() == ["test"] assert str(process) == "test" assert bytes(process) == psys.b(str(process)) process = sh.complex_command_name() assert process.command() == ["complex-command-name"] assert str(process) == "complex-command-name" assert bytes(process) == psys.b(str(process)) process = sh("complex command name")("arg1", "arg2") assert process.command() == ["complex command name", "arg1", "arg2"] assert str(process) == "'complex command name' arg1 arg2" assert bytes(process) == psys.b(str(process)) process = sh.test(b"arg", b"space arg", b"carriage\rline", b"line\narg", b"tab\targ", br"slash\arg", b"quote'arg", b'quote"arg', psys.b("тест"), psys.b("тест тест"), "arg", "space arg", "carriage\rline", "line\narg", "tab\targ", r"slash\arg", "quote'arg", 'quote"arg', "тест", "тест тест", 3, 2**128, 2.0) assert process.command() == [ "test", b"arg", b"space arg", b"carriage\rline", b"line\narg", b"tab\targ", br"slash\arg", b"quote'arg", b'quote"arg', psys.b("тест"), psys.b("тест тест"), "arg", "space arg", "carriage\rline", "line\narg", "tab\targ", r"slash\arg", "quote'arg", 'quote"arg', "тест", "тест тест", "3", "340282366920938463463374607431768211456", "2.0" ] assert str(process) == ( "test " r"""arg 'space arg' 'carriage\rline' 'line\narg' 'tab\targ' 'slash\\arg' "quote'arg" 'quote"arg' \xd1\x82\xd0\xb5\xd1\x81\xd1\x82 '\xd1\x82\xd0\xb5\xd1\x81\xd1\x82 \xd1\x82\xd0\xb5\xd1\x81\xd1\x82' """ r"""arg 'space arg' 'carriage\rline' 'line\narg' 'tab\targ' 'slash\\arg' 'quote\'arg' 'quote"arg' тест 'тест тест' """ "3 340282366920938463463374607431768211456 2.0") assert bytes(process) == psys.b(str(process)) process = sh.test("space arg", s="short_arg") assert process.command() == ["test", "-s", "short_arg", "space arg"] assert str(process) == "test -s short_arg 'space arg'" assert bytes(process) == psys.b(str(process)) process = sh.test("arg", long_long_arg="long arg") assert process.command() == ["test", "--long-long-arg", "long arg", "arg"] assert str(process) == "test --long-long-arg 'long arg' arg" assert bytes(process) == psys.b(str(process)) process = sh.test("arg", bool_arg=True) assert process.command() == ["test", "--bool-arg", "arg"] assert str(process) == "test --bool-arg arg" assert bytes(process) == psys.b(str(process)) process = sh.test("arg", bool_arg=False) assert process.command() == ["test", "arg"] assert str(process) == "test arg" assert bytes(process) == psys.b(str(process))
def __communicate(self, poll): """Communicates with the process and waits for its termination.""" pipe_map = {} poll_objects = 0 # Configure the poll object and pipes --> poll.register(self.__termination_fd, poll.POLLIN) poll_objects += 1 for pipe in self.__pipes: fd = pipe.read if pipe.output else pipe.write if fd is None: continue pipe_map[fd] = pipe flags = eintr_retry(fcntl.fcntl)(fd, fcntl.F_GETFL) eintr_retry(fcntl.fcntl)(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK) poll.register(fd, poll.POLLIN if pipe.output else poll.POLLOUT) pipe.close(read=not pipe.output, write=pipe.output) poll_objects += 1 # Configure the poll object and pipes <-- # Communicate with the process --> stdin = None while poll_objects: events = poll.poll() for fd, flags in events: # Process termination if fd == self.__termination_fd: if self.__wait_for_output: poll.unregister(self.__termination_fd) poll_objects -= 1 continue else: poll_objects = 0 break pipe = pipe_map[fd] # stdin if pipe.source == psys.STDIN_FILENO: if stdin is None: try: stdin = next(self.__stdin_generator) try: if type(stdin) not in (bytes, str): raise TypeError("must be a string") stdin = psys.b(stdin) except Exception as e: raise InvalidArgument("Invalid stdin data: {0}", e) except StopIteration: pass except Exception as e: self.__error = e stdin = None if stdin is None: poll.unregister(fd) poll_objects -= 1 pipe.close() else: try: size = eintr_retry(os.write)(fd, stdin) except EnvironmentError as e: # The process closed its stdin if e.errno == errno.EPIPE: poll.unregister(fd) poll_objects -= 1 pipe.close() else: raise e else: if size == len(stdin): stdin = None else: stdin = stdin[size:] # stdout/stderr elif pipe.source in (psys.STDOUT_FILENO, psys.STDERR_FILENO): data = eintr_retry(os.read)(fd, psys.BUFSIZE) if data: self.__on_output(pipe.source, data) else: poll.unregister(fd) poll_objects -= 1 else: raise LogicalError() # Communicate with the process <-- if not self.__wait_for_output: # The process has terminated, but we should continue communication # to get output that we haven't got from it yet. But we must do it # wisely, because the process might fork() itself, so we'll read # its child's output forever. # Maximum output size after process termination (bigger than any # pipe buffer size). max_data_size = 1024 * 1024 for pipe in self.__pipes: if ( pipe.source in (psys.STDOUT_FILENO, psys.STDERR_FILENO) and pipe.read is not None ): size = 0 while size < max_data_size: try: data = eintr_retry(os.read)( pipe.read, min(psys.BUFSIZE, max_data_size - size)) except EnvironmentError as e: if e.errno != errno.EAGAIN: raise e if not self.__truncate_output: self.__error = ProcessOutputWasTruncated( str(self), self.__status, self.__stdout.getvalue(), self.__stderr.getvalue()) break else: if data: size += len(data) self.__on_output(pipe.source, data) else: break
def __communicate(self, poll): """Communicates with the process and waits for its termination.""" pipe_map = {} poll_objects = 0 # Configure the poll object and pipes --> poll.register(self.__termination_fd, poll.POLLIN) poll_objects += 1 for pipe in self.__pipes: fd = pipe.read if pipe.output else pipe.write if fd is None: continue pipe_map[fd] = pipe flags = eintr_retry(fcntl.fcntl)(fd, fcntl.F_GETFL) eintr_retry(fcntl.fcntl)(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK) poll.register(fd, poll.POLLIN if pipe.output else poll.POLLOUT) pipe.close(read=not pipe.output, write=pipe.output) poll_objects += 1 # Configure the poll object and pipes <-- # Communicate with the process --> stdin = None while poll_objects: events = poll.poll() for fd, flags in events: # Process termination if fd == self.__termination_fd: if self.__wait_for_output: poll.unregister(self.__termination_fd) poll_objects -= 1 continue else: poll_objects = 0 break pipe = pipe_map[fd] # stdin if pipe.source == psys.STDIN_FILENO: if stdin is None: try: stdin = next(self.__stdin_generator) try: if type(stdin) not in (bytes, str): raise TypeError("must be a string") stdin = psys.b(stdin) except Exception as e: raise InvalidArgument( "Invalid stdin data: {0}", e) except StopIteration: pass except Exception as e: self.__error = e stdin = None if stdin is None: poll.unregister(fd) poll_objects -= 1 pipe.close() else: try: size = eintr_retry(os.write)(fd, stdin) except EnvironmentError as e: # The process closed its stdin if e.errno == errno.EPIPE: poll.unregister(fd) poll_objects -= 1 pipe.close() else: raise e else: if size == len(stdin): stdin = None else: stdin = stdin[size:] # stdout/stderr elif pipe.source in (psys.STDOUT_FILENO, psys.STDERR_FILENO): data = eintr_retry(os.read)(fd, psys.BUFSIZE) if data: self.__on_output(pipe.source, data) else: poll.unregister(fd) poll_objects -= 1 else: raise LogicalError() # Communicate with the process <-- if not self.__wait_for_output: # The process has terminated, but we should continue communication # to get output that we haven't got from it yet. But we must do it # wisely, because the process might fork() itself, so we'll read # its child's output forever. # Maximum output size after process termination (bigger than any # pipe buffer size). max_data_size = 1024 * 1024 for pipe in self.__pipes: if (pipe.source in (psys.STDOUT_FILENO, psys.STDERR_FILENO) and pipe.read is not None): size = 0 while size < max_data_size: try: data = eintr_retry(os.read)( pipe.read, min(psys.BUFSIZE, max_data_size - size)) except EnvironmentError as e: if e.errno != errno.EAGAIN: raise e if not self.__truncate_output: self.__error = ProcessOutputWasTruncated( str(self), self.__status, self.__stdout.getvalue(), self.__stderr.getvalue()) break else: if data: size += len(data) self.__on_output(pipe.source, data) else: break
def e(error): """Returns an exception error string.""" return os.strerror(error.errno) if isinstance(error, EnvironmentError) else str(error)
def __init__(self, *args, **kwargs): message, args = str(args[0]), args[1:] super(Error, self).__init__( message.format(*args, **kwargs) if args or kwargs else message)
def func(): for i in range(0, 10): yield "\n" + str(i) if i else str(i)
def test_command_arguments(test): """Tests command argument parsing.""" process = sh.test() assert process.command() == [ "test" ] assert str(process) == "test" assert bytes(process) == psys.b(str(process)) process = sh.complex_command_name() assert process.command() == [ "complex-command-name" ] assert str(process) == "complex-command-name" assert bytes(process) == psys.b(str(process)) process = sh("complex command name")("arg1", "arg2") assert process.command() == [ "complex command name", "arg1", "arg2" ] assert str(process) == "'complex command name' arg1 arg2" assert bytes(process) == psys.b(str(process)) process = sh.test( b"arg", b"space arg", b"carriage\rline", b"line\narg", b"tab\targ", br"slash\arg", b"quote'arg", b'quote"arg', psys.b("тест"), psys.b("тест тест"), "arg", "space arg", "carriage\rline", "line\narg", "tab\targ", r"slash\arg", "quote'arg", 'quote"arg', "тест", "тест тест", 3, 2 ** 128, 2.0 ) assert process.command() == [ "test", b"arg", b"space arg", b"carriage\rline", b"line\narg", b"tab\targ", br"slash\arg", b"quote'arg", b'quote"arg', psys.b("тест"), psys.b("тест тест"), "arg", "space arg", "carriage\rline", "line\narg", "tab\targ", r"slash\arg", "quote'arg", 'quote"arg', "тест", "тест тест", "3", "340282366920938463463374607431768211456", "2.0" ] assert str(process) == ("test " r"""arg 'space arg' 'carriage\rline' 'line\narg' 'tab\targ' 'slash\\arg' "quote'arg" 'quote"arg' \xd1\x82\xd0\xb5\xd1\x81\xd1\x82 '\xd1\x82\xd0\xb5\xd1\x81\xd1\x82 \xd1\x82\xd0\xb5\xd1\x81\xd1\x82' """ r"""arg 'space arg' 'carriage\rline' 'line\narg' 'tab\targ' 'slash\\arg' 'quote\'arg' 'quote"arg' тест 'тест тест' """ "3 340282366920938463463374607431768211456 2.0" ) assert bytes(process) == psys.b(str(process)) process = sh.test("space arg", s = "short_arg") assert process.command() == [ "test", "-s", "short_arg", "space arg" ] assert str(process) == "test -s short_arg 'space arg'" assert bytes(process) == psys.b(str(process)) process = sh.test("arg", long_long_arg = "long arg") assert process.command() == [ "test", "--long-long-arg", "long arg", "arg" ] assert str(process) == "test --long-long-arg 'long arg' arg" assert bytes(process) == psys.b(str(process)) process = sh.test("arg", bool_arg = True) assert process.command() == [ "test", "--bool-arg", "arg" ] assert str(process) == "test --bool-arg arg" assert bytes(process) == psys.b(str(process)) process = sh.test("arg", bool_arg = False) assert process.command() == [ "test", "arg" ] assert str(process) == "test arg" assert bytes(process) == psys.b(str(process))