Initial commit
This commit is contained in:
parent
fabf0319db
commit
7c4ad5104c
4 changed files with 181 additions and 0 deletions
41
api/__init__.py
Normal file
41
api/__init__.py
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import requests
|
||||||
|
import enum
|
||||||
|
from typing import Tuple
|
||||||
|
|
||||||
|
|
||||||
|
class ApiAuthType(enum.Enum):
|
||||||
|
NoAuth = 0
|
||||||
|
Header = 1
|
||||||
|
Cookie = 2
|
||||||
|
|
||||||
|
|
||||||
|
class Api(object):
|
||||||
|
base_url: str = None
|
||||||
|
|
||||||
|
def transform(self, data):
|
||||||
|
return data
|
||||||
|
|
||||||
|
def apply_authentication(self) -> Tuple[ApiAuthType, dict]:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def _get(self, endpoint, raw: bool = False):
|
||||||
|
|
||||||
|
url = self.base_url + endpoint
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"Accept": "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
cookies = {}
|
||||||
|
|
||||||
|
ty, auth = self.apply_authentication()
|
||||||
|
if ty == ApiAuthType.Header:
|
||||||
|
headers.update(auth)
|
||||||
|
elif ty == ApiAuthType.Cookie:
|
||||||
|
cookies.update(auth)
|
||||||
|
|
||||||
|
response = requests.get(url, headers=headers, cookies=cookies)
|
||||||
|
if raw:
|
||||||
|
return response
|
||||||
|
|
||||||
|
return self.transform(response.json())
|
32
api/pelican.py
Normal file
32
api/pelican.py
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
from api import Api, ApiAuthType
|
||||||
|
from typing import Tuple
|
||||||
|
|
||||||
|
|
||||||
|
class PelicanApi(Api):
|
||||||
|
def __init__(self, base_url, auth_token):
|
||||||
|
self.base_url = base_url
|
||||||
|
self.auth_token = auth_token
|
||||||
|
|
||||||
|
def apply_authentication(self) -> Tuple[ApiAuthType, dict]:
|
||||||
|
return ApiAuthType.Header, {
|
||||||
|
"Authorization": "Bearer " + self.auth_token
|
||||||
|
}
|
||||||
|
|
||||||
|
def transform(self, data):
|
||||||
|
type = data["object"]
|
||||||
|
if type == "list":
|
||||||
|
return [self.transform(x) for x in data["data"]]
|
||||||
|
|
||||||
|
if type in ["allocation", "server", "node"]:
|
||||||
|
return data["attributes"]
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def nodes(self):
|
||||||
|
return self._get("/application/nodes")
|
||||||
|
|
||||||
|
def servers(self):
|
||||||
|
return self._get("/application/servers")
|
||||||
|
|
||||||
|
def allocations(self, node_id: int):
|
||||||
|
return self._get("/application/nodes/" + str(node_id) + "/allocations")
|
104
main.py
Normal file
104
main.py
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
import aiohttp
|
||||||
|
import asyncio
|
||||||
|
import socket
|
||||||
|
|
||||||
|
import configparser
|
||||||
|
|
||||||
|
cfg = configparser.ConfigParser()
|
||||||
|
|
||||||
|
import asusrouter.modules.port_forwarding
|
||||||
|
from asusrouter import AsusRouter, AsusData
|
||||||
|
from api.pelican import PelicanApi
|
||||||
|
from flask import Flask, request
|
||||||
|
|
||||||
|
RULE_PREFIX = "[auto]::"
|
||||||
|
|
||||||
|
|
||||||
|
def create_rule(label, address, port):
|
||||||
|
return asusrouter.modules.port_forwarding.PortForwardingRule(RULE_PREFIX + label, address, None, "BOTH", None, port)
|
||||||
|
|
||||||
|
|
||||||
|
def rule_to_string(rule: asusrouter.modules.port_forwarding.PortForwardingRule):
|
||||||
|
return f"{rule.name}::{rule.port_external}->{rule.port if rule.port else rule.port_external}@{rule.ip_address}"
|
||||||
|
|
||||||
|
|
||||||
|
def apply_port_changes():
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
|
||||||
|
session = aiohttp.ClientSession(loop=loop)
|
||||||
|
|
||||||
|
pelican_cfg = cfg["pelican"]
|
||||||
|
router_cfg = cfg["router"]
|
||||||
|
pelican = PelicanApi(pelican_cfg["baseurl"], pelican_cfg["auth"])
|
||||||
|
router = AsusRouter(
|
||||||
|
hostname=router_cfg["hostname"],
|
||||||
|
username=router_cfg["username"],
|
||||||
|
password=router_cfg["password"],
|
||||||
|
use_ssl=False,
|
||||||
|
session=session
|
||||||
|
)
|
||||||
|
|
||||||
|
run = loop.run_until_complete
|
||||||
|
|
||||||
|
run(router.async_connect())
|
||||||
|
|
||||||
|
data = run(router.async_get_data(AsusData.PORT_FORWARDING))
|
||||||
|
rules = data["rules"]
|
||||||
|
x: asusrouter.modules.port_forwarding.PortForwardingRule
|
||||||
|
# Remove auto-generated rules
|
||||||
|
rules = [x for x in rules if not x.name.startswith(RULE_PREFIX)]
|
||||||
|
|
||||||
|
# Add new auto-generated rules
|
||||||
|
allocation_server_lookup = {}
|
||||||
|
for server in pelican.servers():
|
||||||
|
allocation_server_lookup[server["allocation"]] = server
|
||||||
|
|
||||||
|
for node in pelican.nodes():
|
||||||
|
node_id = node["id"]
|
||||||
|
allocs = pelican.allocations(node_id)
|
||||||
|
|
||||||
|
node_internal_name = node["name"] + ".pve.local"
|
||||||
|
node_internal_ip = socket.getaddrinfo(node_internal_name, 0)[0][4][0]
|
||||||
|
|
||||||
|
for alloc in allocs:
|
||||||
|
if not alloc["assigned"]:
|
||||||
|
continue
|
||||||
|
|
||||||
|
label = "Allocation " + str(alloc["id"]) + " for " + node_internal_name
|
||||||
|
if alloc["id"] in allocation_server_lookup.keys():
|
||||||
|
svr = allocation_server_lookup[alloc["id"]]
|
||||||
|
label = svr["name"] + "(" + svr["identifier"] + ") allocation"
|
||||||
|
|
||||||
|
rules.append(create_rule(label, node_internal_ip, alloc["port"]))
|
||||||
|
|
||||||
|
run(router.async_apply_port_forwarding_rules(rules))
|
||||||
|
|
||||||
|
print("Currently open ports: ")
|
||||||
|
for rule in rules:
|
||||||
|
print("\t" + rule_to_string(rule))
|
||||||
|
|
||||||
|
run(router.async_disconnect())
|
||||||
|
run(session.close())
|
||||||
|
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/pelican-wh", methods=["GET", "POST"])
|
||||||
|
def start():
|
||||||
|
apply_port_changes()
|
||||||
|
return "Hello, world!"
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
if os.path.exists("/config/config.ini"):
|
||||||
|
cfg.read("/config/config.ini")
|
||||||
|
else:
|
||||||
|
print("No config file detected, exiting...")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
port = int(cfg["app"]["port"])
|
||||||
|
|
||||||
|
app.run(host="0.0.0.0", port=port)
|
4
requirements.txt
Normal file
4
requirements.txt
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
requests~=2.32.3
|
||||||
|
aiohttp~=3.11.13
|
||||||
|
asusrouter~=1.13.1
|
||||||
|
Flask~=3.1.0
|
Loading…
Reference in a new issue