Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deploy basic Next.js app to start Standalone version of Proxbox with FastAPI backend #158

Merged
merged 34 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
e48aaf5
'proxbox.service' systemd running into port 8800
emersonfelipesp Oct 10, 2023
6dd0027
Add FastAPI paths related to plugin settings
emersonfelipesp Oct 10, 2023
a69e990
Add BaseModels for Netbox and Proxmox sessions & add query paramters …
emersonfelipesp Oct 10, 2023
99692bd
Finish 'PluginConfig' pydantic model to validate 'netbox_proxbox' plu…
emersonfelipesp Oct 10, 2023
cafcad5
Move pydantic models from main to 'backend.schemas' subpackage
emersonfelipesp Oct 10, 2023
ae58c7c
Add routes and schemas packages to better structure the project
emersonfelipesp Oct 10, 2023
9cfe19c
Add /proxbox/settings/{app} path to retrieve both Proxmox and Netbox …
emersonfelipesp Oct 10, 2023
cbfd99b
Rename 'proxbox_settings' function to 'app_settings'
emersonfelipesp Oct 10, 2023
a923054
NetboxSession working, example endpoint: <fastapi>/netbox/status
emersonfelipesp Oct 17, 2023
7eb1fa9
Add '/netbox/openapi' HTTP path and create Annotated var 'NetboxSessi…
emersonfelipesp Oct 18, 2023
805844c
Make pynetbox() assync remove pynetbox attribute from NetboxSession c…
emersonfelipesp Oct 18, 2023
fb71611
Start ProxmoxSession class, not tested yet.
emersonfelipesp Oct 18, 2023
ce1b199
Saves work. ProxboxException created, making error handling better
emersonfelipesp Oct 23, 2023
4e78fbb
'/{top_level}/{second_level}' proxmox dynamic path working with singl…
emersonfelipesp Oct 24, 2023
1ebdcfa
Add Enum validation to '/{top_level}' proxmox path and minor changes
emersonfelipesp Oct 24, 2023
29b6f7f
/proxmox/cluster/resources path working with data validation and resp…
emersonfelipesp Oct 25, 2023
e09689e
Add Query class to describe query parameters of /cluster/resources an…
emersonfelipesp Oct 25, 2023
5bb350f
Separate /proxmox/cluster/{path} routes from proxmox.py into specific…
emersonfelipesp Oct 25, 2023
4d606c7
Create ProxmoxSessions so that I can return not only the session obje…
emersonfelipesp Oct 25, 2023
c27b443
Add docs to 'ProxmoxSession' and 'ProxmoxSessions' and try-excepts to…
emersonfelipesp Oct 26, 2023
db74bf5
proxmox_sessions(), ProxmoxConfigDep and ProxmoxConfigDep returning c…
emersonfelipesp Oct 26, 2023
5ff8d5d
All /proxmox paths working with 'ProxmoxSessionsDep' dependency repre…
emersonfelipesp Oct 27, 2023
36d1d38
Add query descriptions to 'ProxmoxSessionsDep'
emersonfelipesp Oct 27, 2023
0738617
/proxmox/nodes/ path working with pydantic and multi-cluster
emersonfelipesp Oct 27, 2023
b20fc5e
Add first Netbox View 'Clusters' rendering '/proxmox/cluster/status' …
emersonfelipesp Nov 1, 2023
f6ad5bf
Fist React component using Next.js with dynamic data from FastAPI bac…
emersonfelipesp Nov 8, 2023
9fa7a6c
Start styling with tailwind
emersonfelipesp Nov 9, 2023
8a50cb2
Create draft of homepage with tailwind grid and flex
emersonfelipesp Nov 9, 2023
f727064
Split cluster card into components and add navbar
emersonfelipesp Nov 9, 2023
1074c95
Adjust RootLayout with Components
emersonfelipesp Nov 10, 2023
19ac707
Home() component from '/home' path making fetch to backend inside you…
emersonfelipesp Nov 10, 2023
bc73868
Restart ClusterCard style, base CSS already done
emersonfelipesp Nov 14, 2023
1e1d017
Use Composition Pattern to list Proxmox sessions at homepage
emersonfelipesp Nov 14, 2023
70bf70e
Add 'Add New Proxmox Cluster' button (without actions yet)
emersonfelipesp Nov 14, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions contrib/proxbox.service
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[Unit]
Description=Proxbox Service
Documentation=https://docs.netbox.dev.br/
Description=Proxbox Backend Service (FastAPI)
Documentation=https://github.com/netdevopsbr/netbox-proxbox
After=network-online.target
Wants=network-online.target

Expand All @@ -9,10 +9,10 @@ Type=simple

User=netbox
Group=netbox
#PIDFile=/var/tmp/netbox.pid
WorkingDirectory=/opt/netbox
PIDFile=/var/tmp/proxbox.pid
WorkingDirectory=/opt/netbox/netbox/netbox-proxbox

ExecStart=/opt/netbox/venv/bin/uvicorn opt.netboxnetbox.netbox-proxbox.netbox_proxbox.main:app --reload --port 10000
ExecStart=/opt/netbox/venv/bin/uvicorn netbox-proxbox.netbox_proxbox.main:app --host 0.0.0.0 --port 8800 --app-dir /opt/netbox/netbox

Restart=on-failure
RestartSec=30
Expand Down
10 changes: 3 additions & 7 deletions netbox_proxbox/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,22 @@ class ProxboxConfig(PluginConfig):
'password': 'Strong@P4ssword',
'token': {
'name': 'proxbox',
'value': '039az154-23b2-4be0-8d20-b66abc8c4686'
'value': 'PASTE_YOUR_TOKEN_HERE'
},
'ssl': False
}
],
'netbox': {
'domain': 'netbox.example.com', # May also be IP address
'http_port': 80,
'token': '0dd7cddfaee3b38bbffbd2937d44c4a03f9c9d38',
'token': 'PASTE_YOUR_TOKEN_HERE',
'ssl': False,
'settings': {
'virtualmachine_role_id' : 0,
'node_role_id' : 0,
'site_id': 0
}
},
'fastapi': {
'uvicorn_host' : '0.0.0.0',
'uvicorn_port' : '8002',
},
}
}

config = ProxboxConfig
Expand Down
Empty file.
78 changes: 78 additions & 0 deletions netbox_proxbox/backend/enum/proxmox.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
from enum import Enum

class ProxmoxModeOptions(str, Enum):
single = "single"
multi = "multi"

class ProxmoxUpperPaths(str, Enum):
access = "access"
cluster = "cluster"
nodes = "nodes"
storage = "storage"
version = "version"

class ProxmoxAccessPaths(str, Enum):
domains = "domains"
groups = "groups"
openid = "openid"
roles = "roles"
tfa = "tfa"
users = "users"
acl = "acl"
password = "password"
permissions = "permissions"
ticket = "ticket"

class ProxmoxClusterPaths(str, Enum):
acme = "acme"
backup = "backup"
backup_info = "backup-info"
ceph = "ceph"
config = "config"
firewall = "firewall"
ha = "ha"
jobs = "jobs"
mapping = "mapping"
metrics = "metrics"
replication = "replication"
sdn = "sdn"
log = "log"
nextid = "nextid"
options = "options"
resources = "resources"
status = "status"
tasks = "tasks"

class ClusterResourcesType(str, Enum):
vm = "vm"
storage = "storage"
node = "node"
sdn = "sdn"


class ClusterResourcesTypeResponse(str, Enum):
node = "node"
storage = "storage"
pool = "pool"
qemu = "qemu"
lxc = "lxc"
openvz = "openvz"
sdn = "sdn"


class ProxmoxNodesPaths(str, Enum):
node = "node"

class ResourceType(Enum):
node = "node"
storage = "storage"
pool = "pool"
qemu = "qemu"
lxc = "lxc"
openvz = "openvz"
sdn = "sdn"

class NodeStatus(Enum):
unknown = "unknown"
online = "online"
offline = "offline"
10 changes: 10 additions & 0 deletions netbox_proxbox/backend/exception.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class ProxboxException(Exception):
def __init__(
self,
message: str,
detail: str | None = None,
python_exception: str | None = None,
):
self.message = message
self.detail = detail
self.python_exception = python_exception
Empty file.
50 changes: 50 additions & 0 deletions netbox_proxbox/backend/routes/netbox.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from fastapi import APIRouter, Depends

from typing import Annotated, Any

from netbox_proxbox.backend.schemas import PluginConfig
from netbox_proxbox.backend.schemas.netbox import NetboxSessionSchema
from netbox_proxbox.backend.session.netbox import NetboxSession
from netbox_proxbox.backend.routes.proxbox import netbox_settings

router = APIRouter()

async def netbox_session(
netbox_settings: Annotated[NetboxSessionSchema, Depends(netbox_settings)],
):
"""Instantiate 'NetboxSession' class with user parameters and return Netbox HTTP connection to make API calls"""
return await NetboxSession(netbox_settings).pynetbox()

# Make Session reusable
NetboxSessionDep = Annotated[Any, Depends(netbox_session)]

@router.get("/status")
async def netbox_status(
nb: NetboxSessionDep
):
return nb.status()

@router.get("/devices")
async def netbox_devices(nb: NetboxSessionDep):
"Return a list of all devices registered on Netbox."
raw_list = []

device_list = nb.nb_session.dcim.device_roles.all()
for device in device_list:
raw_list.append(device)

return raw_list

@router.get("/openapi")
async def netbox_devices(nb: NetboxSessionDep):
return nb.openapi()

@router.get("/")
async def netbox(
status: Annotated[Any, Depends(netbox_status)],
config: Annotated[Any, Depends(netbox_settings)]
):
return {
"config": config,
"status": status
}
129 changes: 129 additions & 0 deletions netbox_proxbox/backend/routes/proxbox.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
from typing import Annotated

from fastapi import APIRouter, Depends, Query

from netbox_proxbox.backend.schemas import PluginConfig
from netbox_proxbox.backend.schemas.netbox import NetboxSessionSchema
from netbox_proxbox.backend.schemas.proxmox import ProxmoxMultiClusterConfig
from netbox_proxbox.backend.exception import ProxboxException

router = APIRouter()

PROXBOX_PLUGIN_NAME = "netbox_proxbox"

@router.get("/netbox/plugins-config")
async def netbox_plugins_config(
plugin_name: Annotated[
str,
Query(
title="Netbox Plugin Name",
description="Netbox plugin name to get configuration from PLUGINS_CONFIG variable located at Netbox 'configuration.py' file."
)
] | None = PROXBOX_PLUGIN_NAME,
list_all: Annotated[
bool,
Query(
title="List All Plugins",
description="Return all plugins configuration from PLUGINS_CONFIG variable located at Netbox 'configuration.py' file.",
)
] | None = False
):
"""
PLUGIN_CONFIG variable defined by user in Netbox 'configuration.py' file
"""

try:
from netbox.settings import PLUGINS_CONFIG
except Exception as e:
raise ProxboxException(
message = "Could not import PLUGINS CONFIG from configuration.py",
python_exception = f"{e}"
)

# If ?list=all=True
# Return complete PLUGINS_CONFIG (including other plugins)
if list_all:
return PLUGINS_CONFIG

# Message error to not found plugins.
plugin_not_found = f"Could not get '{plugin_name}' plugin config from 'PLUGINS_CONFIG' variable located at Netbox 'configuration.py'"

# Search for configuration of another plugin. This feature is not recommended and may cause security issues, use at your own risk.
if plugin_name != PROXBOX_PLUGIN_NAME:
return PLUGINS_CONFIG.get(plugin_name, {
"error": {
"message": plugin_not_found
}
}
)

try:
return PluginConfig(**PLUGINS_CONFIG.get(plugin_name, {
"error": {
"message": plugin_not_found
}
}
)
)

except Exception as e:
raise ProxboxException(
message = "Plugin configuration at PLUGINS_CONFIG (configuration.py) is probably incorrect.",
detail = "Could not feed 'PluginConfig' pydantic model with config provided from 'PLUGINS_CONFIG'.",
python_exception = f"{e}",
)


@router.get("/netbox/default-settings")
async def proxbox_netbox_default():
"""
Default Plugins settings
"""

from netbox_proxbox import ProxboxConfig
return ProxboxConfig.default_settings


@router.get("/settings")
async def proxbox_settings(
plugins_config: Annotated[PluginConfig, Depends(netbox_plugins_config)],
default_settings: Annotated[PluginConfig, Depends(proxbox_netbox_default)],
list_all: Annotated[
bool,
Query(
title="List All Plugins",
description="Return all plugins configuration from PLUGINS_CONFIG variable located at Netbox 'configuration.py' file.",
)
] | None = False
):
"""
TODO: Compare PLUGINS_CONFIG defined by user with default settings from ProxboxConfig.default_settings
"""
return plugins_config

ProxboxConfigDep = Annotated[PluginConfig, Depends(proxbox_settings)]

@router.get("/settings/netbox")
async def netbox_settings(
proxbox_config: ProxboxConfigDep
):
"""
Get user configuration for Netbox from PLUGINS_CONFIG
"""
return proxbox_config.netbox



@router.get("/settings/proxmox")
async def proxmox_settings(
proxbox_config: ProxboxConfigDep
):
"""
Get user configuration for Proxmox from PLUGINS_CONFIG
"""
return proxbox_config.proxmox


NetboxConfigDep = Annotated[NetboxSessionSchema, Depends(netbox_settings)]
ProxmoxConfigDep = Annotated[ProxmoxMultiClusterConfig, Depends(proxmox_settings)]

Loading
Loading