summaryrefslogtreecommitdiff
path: root/plugins/httpapi
diff options
context:
space:
mode:
authoromnom62 <omnom62@outlook.com>2026-05-06 08:08:27 +1000
committeromnom62 <omnom62@outlook.com>2026-05-06 08:08:27 +1000
commit9a3fa5777e7203d46faf1185cfb56c4fc121d885 (patch)
tree80988400ba3582cfc97ff6445fb8f97c724d3fc6 /plugins/httpapi
parentc2493986714604aa992bea28628b74587b3b51bd (diff)
downloadrest.vyos-9a3fa5777e7203d46faf1185cfb56c4fc121d885.tar.gz
rest.vyos-9a3fa5777e7203d46faf1185cfb56c4fc121d885.zip
More modules
Diffstat (limited to 'plugins/httpapi')
-rw-r--r--plugins/httpapi/vyos.py216
1 files changed, 154 insertions, 62 deletions
diff --git a/plugins/httpapi/vyos.py b/plugins/httpapi/vyos.py
index 9340261..1719254 100644
--- a/plugins/httpapi/vyos.py
+++ b/plugins/httpapi/vyos.py
@@ -1,81 +1,173 @@
-import json
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# GNU General Public License v3.0+
-from urllib.parse import urlencode
+from __future__ import absolute_import, division, print_function
-from ansible.plugins.httpapi import HttpApiBase
+__metaclass__ = type
DOCUMENTATION = r"""
---
-httpapi: vyos
-short_description: VyOS REST API
+name: vyos
+short_description: HttpApi plugin for VyOS REST API
description:
- - HTTPAPI plugin for interacting with VyOS REST API.
-author: Evgeny Molotkov (@eomnom62)
+ - This HttpApi plugin provides methods to connect to VyOS devices via their
+ HTTPS REST API.
+ - Use with C(ansible_connection=ansible.netcommon.httpapi) and
+ C(ansible_network_os=vyos.rest.vyos).
+ - The VyOS REST API must be enabled with
+ C(set service https api keys id ansible key YOUR_KEY),
+ C(set service https api rest), then C(commit && save).
+version_added: "1.0.0"
+author:
+ - VyOS Community (@vyos)
options:
api_key:
- description: VyOS API key
type: str
+ description:
+ - The API key configured on the VyOS device.
+ - Set C(ansible_httpapi_api_key) in inventory or the C(VYOS_API_KEY)
+ environment variable.
+ env:
+ - name: VYOS_API_KEY
vars:
- name: ansible_httpapi_api_key
- env:
- - name: ANSIBLE_HTTPAPI_API_KEY
- ini:
- - section: httpapi
- key: api_key
+ - name: ansible_vyos_api_key
"""
+import json
+import traceback
-class HttpApi(HttpApiBase):
-
- def set_options(self, task_keys=None, var_options=None, direct=None):
- super().set_options(
- task_keys=task_keys,
- var_options=var_options,
- direct=direct,
- )
-
- def send_request(self, path, data=None, method="POST", headers=None):
-
- api_key = self.get_option("api_key")
-
- if not api_key:
- # fallback to inventory vars
- api_key = self._play_context.vars.get("ansible_httpapi_api_key")
-
- if not api_key:
- raise ValueError("api_key is required")
-
- form_data = {}
-
- if data is not None:
- if not isinstance(data, str):
- data = json.dumps(data)
- form_data["data"] = data
-
- form_data["key"] = api_key
-
- body = urlencode(form_data).encode("utf-8")
-
- if headers is None:
- headers = {}
-
- headers["Content-Type"] = "application/x-www-form-urlencoded"
-
- status, response = self.connection.send(
- path,
- body,
- method=method,
- headers=headers,
- )
- if hasattr(response, "read"):
- response = response.read()
+try:
+ from urllib.parse import urlencode
+except ImportError:
+ from urllib import urlencode
- if isinstance(response, bytes):
- response = response.decode("utf-8")
+from ansible.errors import AnsibleConnectionFailure
+from ansible.module_utils._text import to_text
+from ansible.module_utils.connection import ConnectionError
+from ansible.plugins.httpapi import HttpApiBase
- if isinstance(response, str):
- response = json.loads(response)
- return response
+class HttpApi(HttpApiBase):
+ """HttpApi plugin for the VyOS HTTPS REST API."""
+
+ def login(self, username, password):
+ """VyOS uses a static API key — no login endpoint needed."""
+ pass
+
+ def logout(self):
+ pass
+
+ def update_auth(self, response, response_text):
+ return None
+
+ def handle_httperror(self, exc):
+ if exc.code == 401:
+ raise AnsibleConnectionFailure(
+ "VyOS API returned HTTP 401 Unauthorized. "
+ "Check ansible_httpapi_api_key is correct and that "
+ "'set service https api rest' is configured on the device.",
+ )
+ return exc
+
+ def _get_api_key(self):
+ """Read the API key — option, env var, or fail clearly."""
+ try:
+ key = self.get_option("api_key")
+ except Exception:
+ key = None
+ if not key:
+ import os
+
+ key = os.environ.get("VYOS_API_KEY", "")
+ if not key:
+ raise ConnectionError(
+ "No VyOS API key found. Set ansible_httpapi_api_key in "
+ "inventory or export VYOS_API_KEY=<key>.",
+ )
+ return key
+
+ def send_request(self, endpoint, **payload):
+ """POST to a VyOS REST endpoint.
+
+ Args:
+ endpoint (str): API path, e.g. '/configure' or '/retrieve'.
+ Named 'endpoint' not 'path' to avoid collision
+ with the VyOS payload field also called 'path'.
+ **payload: VyOS API fields: op, path, value, url, file, etc.
+
+ Returns:
+ dict: Parsed JSON response from VyOS.
+
+ Raises:
+ ConnectionError: on HTTP error or VyOS success=false response.
+ """
+ try:
+ api_key = self._get_api_key()
+ body = json.dumps(payload)
+ form_data = urlencode({"data": body, "key": api_key})
+
+ response, response_data = self.connection.send(
+ endpoint,
+ data=form_data,
+ method="POST",
+ headers={"Content-Type": "application/x-www-form-urlencoded"},
+ )
+
+ raw = to_text(response_data.getvalue())
+
+ try:
+ result = json.loads(raw)
+ except ValueError:
+ raise ConnectionError(
+ "VyOS API at {ep} returned non-JSON ({code}): {raw}".format(
+ ep=endpoint,
+ code=getattr(response, "status", "?"),
+ raw=raw[:300],
+ ),
+ )
+
+ if not result.get("success"):
+ raise ConnectionError(
+ "VyOS API error [{ep}]: {err}".format(
+ ep=endpoint,
+ err=result.get("error") or "success=false",
+ ),
+ )
+
+ return result
+
+ except (ConnectionError, AnsibleConnectionFailure):
+ raise
+ except Exception as exc:
+ raise ConnectionError(
+ "{exc_type} in send_request({ep}): {exc}\n{tb}".format(
+ exc_type=type(exc).__name__,
+ ep=endpoint,
+ exc=to_text(exc),
+ tb=traceback.format_exc(),
+ ),
+ )
+
+ def get_info(self):
+ """GET /info — the one unauthenticated endpoint."""
+ try:
+ response, response_data = self.connection.send(
+ "/info",
+ data=None,
+ method="GET",
+ )
+ return json.loads(to_text(response_data.getvalue()))
+ except (ConnectionError, AnsibleConnectionFailure):
+ raise
+ except Exception as exc:
+ raise ConnectionError(
+ "{exc_type} in get_info(): {exc}\n{tb}".format(
+ exc_type=type(exc).__name__,
+ exc=to_text(exc),
+ tb=traceback.format_exc(),
+ ),
+ )