{"id":8593,"library":"rest-connector","title":"pyATS REST Connector","description":"The `rest-connector` package provides the `Rest` connection plugin for the pyATS framework, enabling automated interaction with REST APIs for network devices or other API endpoints. It is part of the larger Cisco pyATS ecosystem and is actively maintained, with a release cadence tied to pyATS itself, typically several releases per year. It simplifies making HTTP requests within pyATS test automation.","status":"active","version":"26.3","language":"en","source_language":"en","source_url":"https://github.com/Cisco-Ecosystem/pyats-rest","tags":["pyats","rest","api","networking","automation","testing","cisco"],"install":[{"cmd":"pip install rest-connector pyats","lang":"bash","label":"Install pyATS and rest-connector"}],"dependencies":[{"reason":"rest-connector is a plugin for the pyATS framework and is typically used within a pyATS testbed environment.","package":"pyats","optional":false},{"reason":"Core HTTP client library used by rest-connector for making requests.","package":"requests","optional":false}],"imports":[{"note":"The core Testbed class is part of pyATS topology, not directly from rest-connector.","wrong":"from rest_connector.topology import Testbed","symbol":"Testbed","correct":"from pyats.topology import Testbed"},{"note":"The Rest connection class is located within the pyATS connections namespace, not at the top level of the rest-connector package.","wrong":"from rest_connector import Rest","symbol":"Rest","correct":"from pyats.connections.rest import Rest"}],"quickstart":{"code":"import os\nfrom pyats.topology import Testbed\n\n# 1. Create a testbed.yaml file:\n#   devices:\n#     my_rest_api:\n#       os: rest\n#       connections:\n#         rest:\n#           class: pyats.connections.rest.Rest\n#           arguments:\n#             host: example.com\n#             port: 443\n#             ssl_verify: True\n#             headers:\n#               Content-Type: application/json\n#             credentials:\n#               username: ${{ REST_USERNAME }}\n#               password: ${{ REST_PASSWORD }}\n\n# Set environment variables for credentials (for a real API)\n# os.environ['REST_USERNAME'] = 'myuser'\n# os.environ['REST_PASSWORD'] = 'mypassword'\n\n# For this example, we'll use a placeholder and mock if needed\n# In a real scenario, example.com/api/data would be queried.\n# For a runnable example that doesn't hit a real server, we assume a simple GET\n# and print the raw text, as full mocking is out of quickstart scope.\n\n# Mocking a testbed for a runnable example without actual file I/O\n# In a real scenario, this would load from a 'testbed.yaml' file.\nclass MockConnection:\n    def __init__(self, host, port, ssl_verify, headers, credentials):\n        self.host = host\n        self.port = port\n        self.ssl_verify = ssl_verify\n        self.headers = headers\n        self.credentials = credentials\n\n    def get(self, path, headers=None, verify=None, auth=None):\n        class MockResponse:\n            def __init__(self, text, status_code=200):\n                self.text = text\n                self.status_code = status_code\n\n            def json(self):\n                import json\n                return json.loads(self.text)\n\n        if path == '/api/data':\n            print(f\"[Mock] GET request to {self.host}:{self.port}{path}\")\n            return MockResponse('{\"status\": \"success\", \"data\": [1,2,3]}')\n        return MockResponse('{\"error\": \"not found\"}', status_code=404)\n\nclass MockRestDevice:\n    def __init__(self, name, connections_config):\n        self.name = name\n        self.rest = MockConnection(\n            connections_config['rest']['arguments']['host'],\n            connections_config['rest']['arguments']['port'],\n            connections_config['rest']['arguments']['ssl_verify'],\n            connections_config['rest']['arguments']['headers'],\n            connections_config['rest']['arguments']['credentials']\n        )\n\n    def connect(self):\n        print(f\"[Mock] Connecting to device {self.name}\")\n        # Simulate connection logic\n\n# Simulate a simple testbed and device using the mock classes\ntestbed_config = {\n    'devices': {\n        'my_rest_api': {\n            'os': 'rest',\n            'connections': {\n                'rest': {\n                    'class': 'pyats.connections.rest.Rest',\n                    'arguments': {\n                        'host': 'example.com',\n                        'port': 443,\n                        'ssl_verify': True,\n                        'headers': {'Content-Type': 'application/json'},\n                        'credentials': {\n                            'username': os.environ.get('REST_USERNAME', 'mockuser'),\n                            'password': os.environ.get('REST_PASSWORD', 'mockpass')\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n\n# In a real scenario, load from file:\n# testbed = Testbed('testbed.yaml')\n# For this quickstart, we'll create a mock Testbed/device\nclass MockTestbed:\n    def __init__(self, config):\n        self.devices = {\n            name: MockRestDevice(name, device_config)\n            for name, device_config in config['devices'].items()\n        }\n\nmock_testbed = MockTestbed(testbed_config)\ndevice = mock_testbed.devices['my_rest_api']\n\n# 2. Connect to the device (initiates the REST session)\ndevice.connect()\n\n# 3. Make an API call\ntry:\n    response = device.rest.get('/api/data')\n    print(f\"API Response Status: {response.status_code}\")\n    if response.status_code == 200:\n        print(f\"API Response JSON: {response.json()}\")\n    else:\n        print(f\"API Error: {response.text}\")\nexcept Exception as e:\n    print(f\"An error occurred: {e}\")","lang":"python","description":"This quickstart demonstrates how to set up a REST API connection using a pyATS testbed configuration. It involves defining a device of `os: rest` type in a YAML testbed file, then connecting to it from Python and making a simple GET request. Credentials are securely fetched from environment variables. A mock setup is provided for immediate execution without a live API or testbed file."},"warnings":[{"fix":"For development, set `ssl_verify: False`. For production, ensure valid, trusted certificates are used, or explicitly trust the CA by providing a `ca_bundle` path in connection arguments.","message":"By default, `ssl_verify` is `True` for secure connections. For internal APIs with self-signed certificates, users often disable it (e.g., `ssl_verify: False` in testbed arguments). This should be strictly avoided in production environments due to significant security risks.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Always define REST devices within a `Testbed` YAML file and load it using `from pyats.topology import Testbed`.","message":"While direct instantiation of `pyats.connections.rest.Rest` is possible, the recommended and most robust way to use `rest-connector` within the pyATS framework is through a `Testbed` definition. This approach handles connection lifecycle, integrates with other pyATS features, and centralizes configuration.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Implement retry mechanisms (e.g., using `tenacity`), check `Retry-After` headers for rate limits, and write explicit loops or functions to handle paginated responses based on API documentation.","message":"REST APIs often impose rate limits or require pagination for large datasets. `rest-connector` provides basic request functionality; users are responsible for implementing logic to handle these API-specific constraints (e.g., retries, sleep intervals, parsing pagination headers/links).","severity":"gotcha","affected_versions":"All versions"},{"fix":"Review the pyATS release notes for any breaking changes related to connection plugins. Update testbed configurations and Python code to align with the new pyATS version's requirements.","message":"Major version upgrades of the core `pyATS` framework (e.g., from 21.x to 22.x) can sometimes introduce changes to connection arguments, credential handling, or underlying library versions that affect `rest-connector` behavior. Always consult the pyATS release notes.","severity":"breaking","affected_versions":"Inter-major pyATS versions"}],"env_vars":null,"last_verified":"2026-04-16T00:00:00.000Z","next_check":"2026-07-15T00:00:00.000Z","problems":[{"fix":"Verify the API endpoint's host and port in your testbed configuration, ensure the service is running, and check network connectivity (e.g., `ping`, `telnet host port`) and firewall rules.","cause":"The target REST API server is not running, is unreachable, or the host/port configured in the testbed is incorrect. A firewall might also be blocking the connection.","error":"requests.exceptions.ConnectionError: ('Connection aborted.', ConnectionRefusedError(111, 'Connection refused'))"},{"fix":"For non-production development or internal systems, set `ssl_verify: False` in your testbed connection arguments (e.g., `arguments: {ssl_verify: False}`). For production, install the correct CA certificate chain on the system or provide its path via the `ca_bundle` argument.","cause":"The SSL certificate of the target API server cannot be verified by the system's trusted certificate authorities. This often happens with self-signed certificates or internal CAs.","error":"requests.exceptions.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate in certificate chain"},{"fix":"Double-check the `username` and `password` or other authentication details (e.g., API key in `headers`) in your testbed definition. Ensure environment variables like `REST_USERNAME` and `REST_PASSWORD` are correctly set if using them.","cause":"The provided credentials (username/password/token) are incorrect, missing, or lack the necessary permissions to access the requested resource on the API.","error":"pyats.connections.rest.exceptions.RestConnectionError: API returned status code 401: Unauthorized"},{"fix":"Inspect the raw response using `response.text` to understand what the API is actually returning. Ensure the API endpoint indeed returns JSON for the specific request, or explicitly handle non-JSON responses before attempting to parse as JSON.","cause":"The API response was not valid JSON or was empty, but the pyATS `rest-connector` implicitly tried to parse it as JSON (e.g., via `response.json()`). This can happen if the API returns plain text, HTML, or an error message that isn't JSON.","error":"json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)"}]}