diff options
| author | John Estabrook <jestabro@vyos.io> | 2026-08-24 10:40:13 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-08-24 10:40:13 -0500 |
| commit | 745a4afd9bab8a99fdfdb38febe7bf39ac8e5a25 (patch) | |
| tree | 6d39c115ec2403d3dd45417704adee43d5a22d1f | |
| parent | 0712fac9dddb4dbfede4409867d2973ccbfb5aa2 (diff) | |
| parent | ea65e1d24bf9327edd2244a81e9c307f3001b16f (diff) | |
| download | vyos-1x-745a4afd9bab8a99fdfdb38febe7bf39ac8e5a25.tar.gz vyos-1x-745a4afd9bab8a99fdfdb38febe7bf39ac8e5a25.zip | |
Merge pull request #5415 from jvoss/api_ping
http-api: T9224: add ping endpoint to REST API
| -rw-r--r-- | data/templates/https/nginx.default.j2 | 2 | ||||
| -rw-r--r-- | python/vyos/configsession.py | 8 | ||||
| -rw-r--r-- | src/services/api/rest/models.py | 25 | ||||
| -rw-r--r-- | src/services/api/rest/routers.py | 26 |
4 files changed, 60 insertions, 1 deletions
diff --git a/data/templates/https/nginx.default.j2 b/data/templates/https/nginx.default.j2 index cf87fb309..e0ebe79fc 100644 --- a/data/templates/https/nginx.default.j2 +++ b/data/templates/https/nginx.default.j2 @@ -49,7 +49,7 @@ server { ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK'; # proxy settings for HTTP API, if enabled; 503, if not - location ~ ^/(retrieve|configure|config-file|image|import-pki|container-image|generate|show|reboot|reset|poweroff|traceroute|info|docs|openapi.json|redoc|graphql|renew) { + location ~ ^/(retrieve|configure|config-file|image|import-pki|container-image|generate|show|reboot|reset|poweroff|ping|traceroute|info|docs|openapi.json|redoc|graphql|renew) { {% if api is vyos_defined %} proxy_pass http://unix:/run/api.sock; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; diff --git a/python/vyos/configsession.py b/python/vyos/configsession.py index 609ad79b8..3f1f4f31f 100644 --- a/python/vyos/configsession.py +++ b/python/vyos/configsession.py @@ -88,6 +88,7 @@ RENEW = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'renew'] POWEROFF = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'poweroff'] OP_CMD_ADD = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'add'] OP_CMD_DELETE = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'delete'] +PING = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'ping'] TRACEROUTE = [ '/usr/libexec/vyos/op_mode/mtr_execute.py', 'mtr', @@ -486,6 +487,13 @@ class ConfigSession(object): out = self.__run_command(SHOW + ['container', 'image']) return out + def ping(self, host, count: int = 5, vrf: str | None = None): + cmd = PING + [host, 'count', str(count)] + if vrf is not None: + cmd += ['vrf', vrf] + out = self.__run_command(cmd) + return out + def traceroute(self, host): out = self.__run_command(TRACEROUTE + [host]) return out diff --git a/src/services/api/rest/models.py b/src/services/api/rest/models.py index bfea17344..d3ee3a08e 100644 --- a/src/services/api/rest/models.py +++ b/src/services/api/rest/models.py @@ -304,6 +304,31 @@ class PoweroffModel(ApiModel): } +class PingModel(ApiModel): + op: StrictStr + host: StrictStr + count: StrictInt = 5 + vrf: StrictStr = None + + @field_validator('count') + @classmethod + def check_between(cls, count: int) -> int: + if not 1 <= count <= 10: + raise ValueError('count must be between 1 and 10') + return count + + class Config: + schema_extra = { + 'example': { + 'key': 'id_key', + 'op': 'ping', + 'host': 'host', + 'count': 5, + 'vrf': 'vrf', + } + } + + class TracerouteModel(ApiModel): op: StrictStr host: StrictStr diff --git a/src/services/api/rest/routers.py b/src/services/api/rest/routers.py index fe67d4612..a241cabf8 100644 --- a/src/services/api/rest/routers.py +++ b/src/services/api/rest/routers.py @@ -74,6 +74,7 @@ from .models import RebootModel from .models import ResetModel from .models import RenewModel from .models import ImportPkiModel +from .models import PingModel from .models import PoweroffModel from .models import TracerouteModel @@ -218,6 +219,7 @@ class MultipartRequest(Request): '/container-image', '/image', '/configure-section', + '/ping', '/traceroute', ): if 'path' not in c: @@ -959,6 +961,30 @@ def poweroff_op(data: PoweroffModel): return success(res) +@router.post('/ping') +def ping_op(data: PingModel): + state = SessionState() + session = state.session + + op = data.op + host = data.host + count = data.count + vrf = data.vrf + + try: + if op == 'ping': + res = session.ping(host, count, vrf) + else: + return error(400, f"'{op}' is not a valid operation") + except ConfigSessionError as e: + return error(400, str(e)) + except Exception: + LOG.critical(traceback.format_exc()) + return error(500, 'An internal error occurred. Check the logs for details.') + + return success(res) + + @router.post('/traceroute') def traceroute_op(data: TracerouteModel): state = SessionState() |
