Skip to content

Librenms client

The LibreNMS module interacts with the inventory management system of GAP.

LibreNMSClient

The client for LibreNMS that interacts with the inventory management system.

Source code in gso/services/librenms_client.py
class LibreNMSClient:
    """The client for LibreNMS that interacts with the inventory management system."""

    def __init__(self) -> None:
        """Initialise a new LibreNMS client with an authentication token."""
        config = load_oss_params().MONITORING
        token = config.LIBRENMS.token

        self.base_url = config.LIBRENMS.base_url
        self.snmp_config = config.SNMP

        self.session = requests.Session()
        self.session.mount("https://", HTTPAdapter(max_retries=5))
        self.session.headers.update({
            "User-Agent": f"geant-service-orchestrator/{metadata.version("geant-service-orchestrator")}",
            "Accept": "application/json",
            "Content-Type": "application/json",
            "X-Auth-Token": token,
        })

    def _send_request(
        self, method: Literal["GET", "POST", "PUT", "DELETE"], endpoint: str, data: dict[str, Any] | None = None
    ) -> Response:
        url = self.base_url + endpoint
        logger.debug("LibreNMS - Sending request", extra={"method": method, "endpoint": url, "form_data": data})
        result = self.session.request(method, url, json=data, timeout=(10, 75))
        logger.debug("LibreNMS - Received response", extra=result.__dict__)

        return result

    def get_device(self, fqdn: str) -> dict[str, Any]:
        """Get an existing device from LibreNMS.

        Args:
            fqdn: The FQDN of a device that is retrieved.

        Returns:
            A JSON formatted list of devices that match the queried FQDN.

        Raises:
            HTTPError: Raises an HTTP error 404 when the device is not found
        """
        response = self._send_request("GET", f"/devices/{fqdn}")
        response.raise_for_status()

        return response.json()

    def device_exists(self, fqdn: str) -> bool:
        """Check whether a device exists in LibreNMS.

        Args:
            fqdn: The hostname that should be checked for.

        Returns:
            Whether the device exists or not.
        """
        try:
            device = self.get_device(fqdn)
        except HTTPError as e:
            if e.response.status_code == HTTPStatus.NOT_FOUND:
                return False
            raise

        return device["status"] == "ok"

    def add_device(self, fqdn: str, snmp_version: SNMPVersion) -> dict[str, Any]:
        """Add a new device to LibreNMS.

        Args:
            fqdn: The hostname of the newly added device.
            snmp_version: The SNMP version of the new device, which decides the authentication parameters that LibreNMS
                should use to poll the device.
        """
        device_data = {
            "display": fqdn,
            "hostname": fqdn,
            "sysName": fqdn,
            "snmpver": snmp_version.value,
        }
        device_data.update(getattr(self.snmp_config, snmp_version))

        device = self._send_request("POST", "/devices", device_data)
        device.raise_for_status()

        return device.json()

    def remove_device(self, fqdn: str) -> dict[str, Any]:
        """Remove a device from LibreNMS.

        Args:
            fqdn: The FQDN of the hostname that should get deleted.

        Returns:
            A JSON representation of the device that got removed.

        Raises:
            HTTPError: Raises an exception if the request did not succeed.
        """
        device = self._send_request("DELETE", f"/devices/{fqdn}")
        device.raise_for_status()

        return device.json()

    def validate_device(self, fqdn: str) -> str | None:
        """Validate a device in LibreNMS by fetching the record match the queried FQDN against its hostname.

        Args:
            fqdn: The FQDN of the host that is validated.

        Returns:
            A list of errors, if empty the device is successfully validated.
        """
        error = None
        try:
            device = self.get_device(fqdn)
            received_hostname = device["devices"][0]["hostname"]

            if received_hostname != fqdn:
                error = (
                    f"Device hostname in LibreNMS does not match FQDN.\n"
                    f"Expected '{fqdn}' but got '{received_hostname}'."
                )
        except HTTPError as e:
            if e.response.status_code == HTTPStatus.NOT_FOUND:
                error = "Device does not exist in LibreNMS."
            else:
                raise

        return error

__init__()

Initialise a new LibreNMS client with an authentication token.

Source code in gso/services/librenms_client.py
def __init__(self) -> None:
    """Initialise a new LibreNMS client with an authentication token."""
    config = load_oss_params().MONITORING
    token = config.LIBRENMS.token

    self.base_url = config.LIBRENMS.base_url
    self.snmp_config = config.SNMP

    self.session = requests.Session()
    self.session.mount("https://", HTTPAdapter(max_retries=5))
    self.session.headers.update({
        "User-Agent": f"geant-service-orchestrator/{metadata.version("geant-service-orchestrator")}",
        "Accept": "application/json",
        "Content-Type": "application/json",
        "X-Auth-Token": token,
    })

get_device(fqdn)

Get an existing device from LibreNMS.

Parameters:

Name Type Description Default
fqdn str

The FQDN of a device that is retrieved.

required

Returns:

Type Description
dict[str, Any]

A JSON formatted list of devices that match the queried FQDN.

Raises:

Type Description
HTTPError

Raises an HTTP error 404 when the device is not found

Source code in gso/services/librenms_client.py
def get_device(self, fqdn: str) -> dict[str, Any]:
    """Get an existing device from LibreNMS.

    Args:
        fqdn: The FQDN of a device that is retrieved.

    Returns:
        A JSON formatted list of devices that match the queried FQDN.

    Raises:
        HTTPError: Raises an HTTP error 404 when the device is not found
    """
    response = self._send_request("GET", f"/devices/{fqdn}")
    response.raise_for_status()

    return response.json()

device_exists(fqdn)

Check whether a device exists in LibreNMS.

Parameters:

Name Type Description Default
fqdn str

The hostname that should be checked for.

required

Returns:

Type Description
bool

Whether the device exists or not.

Source code in gso/services/librenms_client.py
def device_exists(self, fqdn: str) -> bool:
    """Check whether a device exists in LibreNMS.

    Args:
        fqdn: The hostname that should be checked for.

    Returns:
        Whether the device exists or not.
    """
    try:
        device = self.get_device(fqdn)
    except HTTPError as e:
        if e.response.status_code == HTTPStatus.NOT_FOUND:
            return False
        raise

    return device["status"] == "ok"

add_device(fqdn, snmp_version)

Add a new device to LibreNMS.

Parameters:

Name Type Description Default
fqdn str

The hostname of the newly added device.

required
snmp_version SNMPVersion

The SNMP version of the new device, which decides the authentication parameters that LibreNMS should use to poll the device.

required
Source code in gso/services/librenms_client.py
def add_device(self, fqdn: str, snmp_version: SNMPVersion) -> dict[str, Any]:
    """Add a new device to LibreNMS.

    Args:
        fqdn: The hostname of the newly added device.
        snmp_version: The SNMP version of the new device, which decides the authentication parameters that LibreNMS
            should use to poll the device.
    """
    device_data = {
        "display": fqdn,
        "hostname": fqdn,
        "sysName": fqdn,
        "snmpver": snmp_version.value,
    }
    device_data.update(getattr(self.snmp_config, snmp_version))

    device = self._send_request("POST", "/devices", device_data)
    device.raise_for_status()

    return device.json()

remove_device(fqdn)

Remove a device from LibreNMS.

Parameters:

Name Type Description Default
fqdn str

The FQDN of the hostname that should get deleted.

required

Returns:

Type Description
dict[str, Any]

A JSON representation of the device that got removed.

Raises:

Type Description
HTTPError

Raises an exception if the request did not succeed.

Source code in gso/services/librenms_client.py
def remove_device(self, fqdn: str) -> dict[str, Any]:
    """Remove a device from LibreNMS.

    Args:
        fqdn: The FQDN of the hostname that should get deleted.

    Returns:
        A JSON representation of the device that got removed.

    Raises:
        HTTPError: Raises an exception if the request did not succeed.
    """
    device = self._send_request("DELETE", f"/devices/{fqdn}")
    device.raise_for_status()

    return device.json()

validate_device(fqdn)

Validate a device in LibreNMS by fetching the record match the queried FQDN against its hostname.

Parameters:

Name Type Description Default
fqdn str

The FQDN of the host that is validated.

required

Returns:

Type Description
str | None

A list of errors, if empty the device is successfully validated.

Source code in gso/services/librenms_client.py
def validate_device(self, fqdn: str) -> str | None:
    """Validate a device in LibreNMS by fetching the record match the queried FQDN against its hostname.

    Args:
        fqdn: The FQDN of the host that is validated.

    Returns:
        A list of errors, if empty the device is successfully validated.
    """
    error = None
    try:
        device = self.get_device(fqdn)
        received_hostname = device["devices"][0]["hostname"]

        if received_hostname != fqdn:
            error = (
                f"Device hostname in LibreNMS does not match FQDN.\n"
                f"Expected '{fqdn}' but got '{received_hostname}'."
            )
    except HTTPError as e:
        if e.response.status_code == HTTPStatus.NOT_FOUND:
            error = "Device does not exist in LibreNMS."
        else:
            raise

    return error