From 9c3d7bbbc3dfc6b90577f84cc4b85dbb76cb24ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 18 Oct 2017 17:11:22 +0200 Subject: [PATCH] New server subcommand on test API Tests if a host/port combination is reachable via server_reachable. --- docs/api/util.rst | 85 +++++++++++++++++++++- docs/jsclientlib/util.rst | 52 ++++++++++++- src/octoprint/server/api/__init__.py | 35 ++++++++- src/octoprint/static/js/app/client/util.js | 10 +++ 4 files changed, 177 insertions(+), 5 deletions(-) diff --git a/docs/api/util.rst b/docs/api/util.rst index eb50df4b..1ee12ff2 100644 --- a/docs/api/util.rst +++ b/docs/api/util.rst @@ -68,6 +68,21 @@ Test paths or URLs when the test could be performed. The status code of the response does NOT reflect the test result! + .. _sec-api-util-test-server: + + server + Tests whether a provided server identified by host and port can be reached. Protocol can optionally be specified + as well. Supported parameters are: + + * ``host``: The host to test. IP or host name. Mandatory. + * ``port``: The port to test. Integer. Mandatory. + * ``protocol``: The protocol to test with. ``tcp`` or ``udp``. Optional, defaults to ``tcp``. + * ``timeout``: A timeout for the test, in seconds. If no successful connection to the server could be established + within this time frame, the check will be considered a failure. Optional, defaults to 3.05 seconds. + + The ``server`` command returns :http:statuscode:`200` with a :ref:`Server test result ` + when the test could be performed. The status code of the response does NOT reflect the test result! + Requires admin rights. **Example 1** @@ -226,6 +241,35 @@ Test paths or URLs } } + **Example 6** + + Test whether a server is reachable on a given port via TCP. + + .. sourcecode:: http + + POST /api/util/test HTTP/1.1 + Host: example.com + X-Api-Key: abcdef... + Content-Type: application/json + + { + "command": "server", + "host": "8.8.8.8", + "port": 53 + } + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + { + "host": "8.8.8.8", + "port": 53, + "protocol": "tcp", + "result": true + } + :json command: The command to execute, currently either ``path`` or ``url`` :json path: ``path`` command only: the path to test :json check_type: ``path`` command only: the type of path to test for, either ``file`` or ``dir`` @@ -233,8 +277,11 @@ Test paths or URLs :json url: ``url`` command only: the URL to test :json status: ``url`` command only: one or more expected status codes :json method: ``url`` command only: the HTTP method to use for the check - :json timeout: ``url`` command only: the timeout for the HTTP request + :json timeout: ``url`` and ``server`` commands only: the timeout for the test request :json response: ``url`` command only: whether to include response data and if so in what form + :json host: ``server`` command only: the server to test + :json port: ``server`` command only: the port to test + :json protocol: ``server`` command only: the protocol to test :statuscode 200: No error occurred .. _sec-api-util-datamodel: @@ -244,6 +291,9 @@ Data model .. _sec-api-util-datamodel-pathtestresult: +Path test result +---------------- + .. list-table:: :widths: 15 5 10 30 :header-rows: 1 @@ -275,6 +325,9 @@ Data model .. _sec-api-util-datamodel-urltestresult: +URL test result +--------------- + .. list-table:: :widths: 15 5 10 30 :header-rows: 1 @@ -306,3 +359,33 @@ Data model - object - A dictionary with all headers of the checked URL's response. Only present if ``response`` in the request was set. + +.. _sec-api-util-datamodel-servertestresult: + +Server test result +------------------ + +.. list-table:: + :widths: 15 5 10 30 + :header-rows: 1 + + * - Name + - Multiplicity + - Type + - Description + * - ``host`` + - 1 + - string + - The host that was tested. + * - ``port`` + - 1 + - int + - The port that was tested + * - ``protocol`` + - 1 + - string + - The protocol that was tested, ``tcp`` or ``udp`` + * - ``result`` + - 1 + - bool + - ``true`` if the check passed. diff --git a/docs/jsclientlib/util.rst b/docs/jsclientlib/util.rst index 74b10cbe..4569d1d7 100644 --- a/docs/jsclientlib/util.rst +++ b/docs/jsclientlib/util.rst @@ -126,8 +126,16 @@ .done(function(response) { if (response.result) { // check passed + var content = response.response.content; + var mimeType = "image/jpeg"; + + var headers = response.response.headers; + if (headers && headers["content-type"]) { + mimeType = headers["content-type"].split(";")[0]; + } + var image = $("#someimage"); - image. + image.src = "data:" + mimeType + ";base64," + content; } else { // check failed } @@ -153,6 +161,48 @@ :param object opts: Additional options for the request :returns Promise: A `jQuery Promise `_ for the request's response +.. js:function:: OctoPrintClient.util.testServer(host, port, additional, opts) + + Test if a server is reachable. More options supported by the :ref:`server test command ` + can be provided via the ``additional`` object. + + **Example 1** + + Test if ``8.8.8.8`` is reachable on port 53 within the default timeout. + + .. code-block:: javascript + + OctoPrint.util.testServer("8.8.8.8", 53) + .done(function(response) { + if (response.result) { + // check passed + } else { + // check failed + } + }); + + **Example 2** + + Test if ``127.0.0.1`` is reachable on port 1234 and UDP. + + .. code-block:: javascript + + OctoPrint.util.testUrl("127.0.0.1", 1234, {"protocol": "udp"}) + .done(function(response) { + if (response.result) { + // check passed + } else { + // check failed + } + }); + + + :param string url: Host to test + :param int port: Port to test + :param object additional: Additional parameters for the test command + :param object opts: Additional options for the request + :returns Promise: A `jQuery Promise `_ for the request's response + .. seealso:: :ref:`Util API ` diff --git a/src/octoprint/server/api/__init__.py b/src/octoprint/server/api/__init__.py index b74ff63b..1c43b5e1 100644 --- a/src/octoprint/server/api/__init__.py +++ b/src/octoprint/server/api/__init__.py @@ -243,7 +243,8 @@ def _logout(user): def utilTestPath(): valid_commands = dict( path=["path"], - url=["url"] + url=["url"], + server=["host", "port"] ) command, data, response = get_json_command_from_request(request, valid_commands) @@ -336,7 +337,7 @@ def utilTestPath(): try: timeout = float(data["timeout"]) except: - return make_response("{!r} is not a valid value for timeout (must be int or float)".format(data["timeout"])) + return make_response("{!r} is not a valid value for timeout (must be int or float)".format(data["timeout"]), 400) if "status" in data: request_status = data["status"] @@ -383,5 +384,33 @@ def utilTestPath(): headers=dict(response.headers), content=content ) - return jsonify(**result) + + elif command == "server": + host = data["host"] + try: + port = int(data["port"]) + except: + return make_response("{!r} is not a valid value for port (must be int)".format(data["port"]), 400) + + timeout = 3.05 + if "timeout" in data: + try: + timeout = float(data["timeout"]) + except: + return make_response("{!r} is not a valid value for timeout (must be int or float)".format(data["timeout"]), 400) + + protocol = data.get("protocol", "tcp") + if protocol not in ("tcp", "udp"): + return make_response("{!r} is not a valid value for protocol, must be tcp or udp".format(protocol), 400) + + from octoprint.util import server_reachable + reachable = server_reachable(host, port, timeout=timeout, proto=protocol) + + result = dict(host=host, + port=port, + protocol=protocol, + result=reachable) + + return jsonify(**result) + diff --git a/src/octoprint/static/js/app/client/util.js b/src/octoprint/static/js/app/client/util.js index 721bb938..7c27e75c 100644 --- a/src/octoprint/static/js/app/client/util.js +++ b/src/octoprint/static/js/app/client/util.js @@ -45,6 +45,16 @@ return this.test("url", data, opts); }; + OctoPrintUtilClient.prototype.testServer = function(host, port, additional, opts) { + additional = additional || {}; + + var data = $.extend({}, additional); + data.host = host; + data.port = port; + + return this.test("server", data, opts); + }; + OctoPrintClient.registerComponent("util", OctoPrintUtilClient); return OctoPrintUtilClient; });