Skip to content
This repository has been archived by the owner on Oct 14, 2024. It is now read-only.

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
dsgnr committed Oct 10, 2024
0 parents commit 791ebc4
Show file tree
Hide file tree
Showing 11 changed files with 1,739 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
NAUTOBOT_TOKEN=

48 changes: 48 additions & 0 deletions .github/workflows/build_image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Build Image

on:
push:
branches:
- main

env:
IMG: ghcr.io/rss-engineering/nautobot_target_proxy
TAG: latest

jobs:
build-image:
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db

- name: Login to GHCR
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build Image
uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85
with:
context: .
provenance: false
file: Dockerfile
tags: ${{ env.IMG }}:${{ env.TAG }}
load: true
cache-from: |
type=gha,scope=builder
type=gha,scope=app
cache-to: |
type=gha,scope=builder
type=gha,scope=app
target: app
push: ${{ github.event_name != 'pull_request' }}

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.env
19 changes: 19 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
FROM python:3.12.7-alpine AS builder
RUN pip install --upgrade poetry
COPY pyproject.toml poetry.lock /
RUN poetry export --only main -o /tmp/requirements.txt --without-hashes

# App
FROM python:3.12.7-alpine AS app

RUN pip3 install -U pip setuptools wheel

# Copy Python dependencies from the builder stage
COPY --from=builder /tmp/requirements.txt /tmp/requirements.txt

# Install Poetry dependencies
RUN pip3 install -r /tmp/requirements.txt

COPY src /src
WORKDIR /src
EXPOSE 8000
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
A FastAPI application to query Nautobots GraphQL API to scrape targets for use with Prometheus `http_sd_config`.

# Table of Contents

- [Usage](#usage)
- [Documentation](#documentation)

# Usage

Clone the repository
```shell
gh repo clone RSS-Engineering/nautobot_target_proxy
```

Copy the `.env.example` to `.env` and update the appropriate API keys.

Bring up the Docker compose stack
~~~ shell
docker compose up --build
~~~

# Documentation

Swagger API documentation can be found by browsing to localhost:8000/docs


11 changes: 11 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
version: '3.8'
services:
web:
build:
context: .
target: app

ports:
- "8000:8000"
env_file: .env
command: fastapi run
1,488 changes: 1,488 additions & 0 deletions poetry.lock

Large diffs are not rendered by default.

48 changes: 48 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
[tool.poetry]
name = "Nautobot API Scraper"
description = "Simple API to scrape targets from Nautobot for use with other systems"
version = "0.0.1"
authors = ["Project Undercloud Developers"]
license = "MIT"
readme = "README.md"

[tool.poetry.dependencies]
python = "^3.12"

# App requirements
requests = "2.32.3"
fastapi = {version = "0.115.0", extras = ["standard"]}

[tool.poetry.dev-dependencies]
taskipy = "^1.12.0"
pylint = "^2.17.5"
black = "^24.3.0"
isort = {version = "5.12.0", extras = ["pyproject"]}
pycodestyle = "2.11.0"

[tool.black]
line-length = 100
target-version = ['py312']

[tool.isort]
combine_as_imports = true
force_grid_wrap = 0
include_trailing_comma = true
multi_line_output = 3
use_parentheses = true
line_length = 100
wrap_length = 100
ensure_newline_before_comments = true

import_heading_firstparty = "First Party"
import_heading_stdlib = "Standard Library"
import_heading_thirdparty = "Third Party"

known_first_party = ["helpers"]

[tool.taskipy.tasks]
pycodestyle = "poetry run pycodestyle src"
pylint = "poetry run pylint -j 0 --rcfile pyproject.toml src"
isort = "poetry run isort --sp ../pyproject.toml src"
isort_check = "poetry run isort --sp ../pyproject.toml --check-only src"
black = "poetry run black src"
49 changes: 49 additions & 0 deletions src/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Third Party
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

# First Party
from helpers.graphql import query_nautobot_graphql
from helpers.queries import OOB_TARGET_QUERY

app = FastAPI()


@app.exception_handler(Exception)
async def validation_exception_handler(request: Request, exc: Exception):
return JSONResponse(
status_code=500,
content={"detail": f" Exception message is {exc!r}."},
)


@app.get("/targets/oob")
def get_oob_targets():
response = query_nautobot_graphql(OOB_TARGET_QUERY).json()
res = []
for interface in response["data"]["interfaces"]:
for device in interface["ip_addresses"]:
device_name = interface["device"]["name"]
device_uuid = interface["device"]["id"]
core_id = interface["device"]["cf_core_number"]
location = interface["device"]["location"]["name"]
location_parent = interface["device"]["location"]["parent"]["name"]
tenant_uuid = (interface["device"].get("tenant") or {}).get("id", None)
rack = interface["device"]["rack"]["name"]
urn = (
f"urn:rxt:undercloud:{location_parent}:{tenant_uuid or 'tenant'}:"
f"instance:{location}-{rack}:{device_uuid}"
).lower()
res.append(
{
"targets": [device["host"]],
"labels": {
"device_name": device_name,
"core_id": core_id,
"location": location,
"rack": rack,
"urn": urn,
},
}
)
return res
21 changes: 21 additions & 0 deletions src/helpers/graphql.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Standard Library
import os

# Third Party
import requests

GRAPHQL_URL = "https://nautobot.dev.undercloud.rackspace.net/api/graphql/"


def query_nautobot_graphql(query):
nautobot_token = os.environ.get("NAUTOBOT_TOKEN")
if not nautobot_token:
raise RuntimeError("A Nautobot Token is required")
headers = {
"Authorization": f"Token {nautobot_token}",
"Content-Type": "application/json",
"Accept": "application/json",
}
res = requests.post(GRAPHQL_URL, headers=headers, json={"query": query})
res.raise_for_status()
return res
26 changes: 26 additions & 0 deletions src/helpers/queries.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
OOB_TARGET_QUERY = """
{
interfaces(mgmt_only: true, name: ["iDRAC", "iLO"]) {
device {
name
rack {
name
}
id
tenant {
id
}
cf_core_number
location {
name
parent {
name
}
}
}
ip_addresses {
host
}
}
}
"""

0 comments on commit 791ebc4

Please sign in to comment.