This commit is contained in:
2025-12-02 21:29:55 +01:00
parent 28b745b2cc
commit 5be1560be6
2 changed files with 31 additions and 48 deletions

View File

@@ -163,6 +163,7 @@ class Portainer:
self.get_endpoints() self.get_endpoints()
self.get_stacks(self) self.get_stacks(self)
self.get_containers(self) self.get_containers(self)
return True
def get_stacks(self, endpoint_id="all", timeout=10): def get_stacks(self, endpoint_id="all", timeout=10):
if endpoint_id != "all": if endpoint_id != "all":
@@ -819,4 +820,4 @@ class Portainer:
path = f"/endpoints/{endpoint_id}/docker/secrets/create" path = f"/endpoints/{endpoint_id}/docker/secrets/create"
encoded = base64.b64encode(value.encode()).decode() encoded = base64.b64encode(value.encode()).decode()
data = {"Name": name, "Data": encoded} data = {"Name": name, "Data": encoded}
self.api_post(path, data, timeout=timeout) return self.api_post(path, data, timeout=timeout)

View File

@@ -1,12 +1,19 @@
"""
Portainer API Client module.
This module provides a wrapper for interacting with the Portainer API
to manage endpoints, stacks, and containers.
"""
#!/myapps/venvs/portainer/bin/python3 #!/myapps/venvs/portainer/bin/python3
import os import os
import logging
import sys import sys
import requests
import json import json
import argparse import argparse
from tabulate import tabulate from tabulate import tabulate
from port import Portainer from port import Portainer
import logging
VERSION = "0.0.1" VERSION = "0.0.1"
@@ -123,13 +130,14 @@ logger = logging.getLogger(__name__)
if args.site == "portainer": if args.site == "portainer":
base = os.getenv("PORTAINER_URL", "https://portainer.sectorq.eu/api") base = os.getenv("PORTAINER_URL", "https://portainer.sectorq.eu/api")
portainer_api_key = "ptr_GCNUoFcTOaXm7k8ZxPdQGmrFIamxZPTydbserYofMHc=" PORTAINER_API_KEY = "ptr_GCNUoFcTOaXm7k8ZxPdQGmrFIamxZPTydbserYofMHc="
else: else:
base = os.getenv("PORTAINER_URL", "https://port.sectorq.eu/api") base = os.getenv("PORTAINER_URL", "https://port.sectorq.eu/api")
portainer_api_key = "ptr_/5RkMCT/j3BTaL32vMSDtXFi76yOXRKVFOrUtzMsl5Y=" PORTAINER_API_KEY = "ptr_/5RkMCT/j3BTaL32vMSDtXFi76yOXRKVFOrUtzMsl5Y="
def wl(msg): def wl(msg):
"""Write log message if debug is enabled."""
if args.debug: if args.debug:
print(msg) print(msg)
@@ -143,46 +151,22 @@ def is_number(s):
return False return False
def get_portainer_token(base_url, username=None, password=None, timeout=10): def prompt_missing_args(args_in, defaults_in, fields):
"""
Authenticate to Portainer and return a JWT token.
Reads PORTAINER_USER / PORTAINER_PASS from environment if username/password are not provided.
"""
username = username or os.getenv("PORTAINER_USER")
password = password or os.getenv("PORTAINER_PASS")
if not username or not password:
raise ValueError(
"Username and password must be provided (or set PORTAINER_USER / PORTAINER_PASS)."
)
url = f"{base_url.rstrip('/')}/api/auth"
resp = requests.post(
url, json={"Username": username, "Password": password}, timeout=timeout
)
resp.raise_for_status()
data = resp.json()
token = data.get("jwt") or data.get("JWT") or data.get("token")
if not token:
raise ValueError(f"No token found in response: {data}")
return token
def prompt_missing_args(args, defaults, fields):
""" """
fields = [("arg_name", "Prompt text")] fields = [("arg_name", "Prompt text")]
""" """
for field, text in fields: for field, text in fields:
value = getattr(args, field) value_in = getattr(args_in, field)
default = defaults.get(field) default = defaults_in.get(field)
if value is None: if value_in is None:
if default is not None: if default is not None:
prompt = f"{text} (default={default}) : " prompt = f"{text} (default={default}) : "
value = input(prompt) or default value_in = input(prompt) or default
else: else:
value = input(f"{text}: ") value_in = input(f"{text}: ")
setattr(args, field, value) setattr(args, field, value_in)
return args return args
@@ -216,9 +200,8 @@ if __name__ == "__main__":
ans = input("\nSelect action to perform: ") ans = input("\nSelect action to perform: ")
args.action = actions[int(ans) - 1] args.action = actions[int(ans) - 1]
token = portainer_api_key
# Example: list endpoints # Example: list endpoints
por = Portainer(base, token) por = Portainer(base, PORTAINER_API_KEY, timeout=args.timeout)
if args.action == "secrets": if args.action == "secrets":
if args.endpoint_id is None: if args.endpoint_id is None:
@@ -301,8 +284,7 @@ if __name__ == "__main__":
if args.action == "update_stack": if args.action == "update_stack":
print("Updating stacks") print("Updating stacks")
autostart = True if args.autostart else False por.update_stack(args.endpoint_id, args.stack, args.autostart)
por.update_stack(args.endpoint_id, args.stack, autostart)
sys.exit() sys.exit()
if args.action == "print_all_data": if args.action == "print_all_data":
print(json.dumps(por.all_data, indent=2)) print(json.dumps(por.all_data, indent=2))
@@ -313,11 +295,11 @@ if __name__ == "__main__":
if args.action == "list_endpoints": if args.action == "list_endpoints":
eps = por.get_endpoints() eps = por.get_endpoints()
data = [] export_data = []
for i in eps["by_id"]: for i in eps["by_id"]:
data.append([i, eps["by_id"][i]]) export_data.append([i, eps["by_id"][i]])
headers = ["EndpointId", "Name"] headers = ["EndpointId", "Name"]
print(tabulate(data, headers=headers, tablefmt="github")) print(tabulate(export_data, headers=headers, tablefmt="github"))
sys.exit() sys.exit()
@@ -328,7 +310,7 @@ if __name__ == "__main__":
print(f"Stopping containers on {por.get_endpoint_name(args.endpoint_id)}") print(f"Stopping containers on {por.get_endpoint_name(args.endpoint_id)}")
cont = [] cont = []
for c in por.all_data["containers"][args.endpoint_id]: for c in por.all_data["containers"][args.endpoint_id]:
if args.stack == c or args.stack == "all": if args.stack in (c, "all"):
cont += por.all_data["containers"][args.endpoint_id][c] cont += por.all_data["containers"][args.endpoint_id][c]
por.stop_containers(args.endpoint_id, cont) por.stop_containers(args.endpoint_id, cont)
sys.exit() sys.exit()
@@ -338,7 +320,7 @@ if __name__ == "__main__":
cont = [] cont = []
# input(json.dumps(por.all_data, indent=2)) # input(json.dumps(por.all_data, indent=2))
for c in por.all_data["containers"][args.endpoint_id]: for c in por.all_data["containers"][args.endpoint_id]:
if args.stack == c or args.stack == "all": if args.stack in (c, "all"):
cont += por.all_data["containers"][args.endpoint_id][c] cont += por.all_data["containers"][args.endpoint_id][c]
por.start_containers(args.endpoint_id, cont) por.start_containers(args.endpoint_id, cont)
sys.exit() sys.exit()
@@ -347,7 +329,7 @@ if __name__ == "__main__":
cont = [] cont = []
# input(json.dumps(por.all_data,indent=2)) # input(json.dumps(por.all_data,indent=2))
for c in por.all_data["containers"][args.endpoint_id]: for c in por.all_data["containers"][args.endpoint_id]:
if args.stack == c or args.stack == "all": if args.stack in (c, "all"):
cont += por.all_data["containers"][args.endpoint_id][c] cont += por.all_data["containers"][args.endpoint_id][c]
por.start_containers(args.endpoint_id, cont) por.start_containers(args.endpoint_id, cont)
sys.exit() sys.exit()
@@ -358,6 +340,6 @@ if __name__ == "__main__":
if args.action == "refresh_status": if args.action == "refresh_status":
if args.stack == "all": if args.stack == "all":
print("Stopping all stacks...") print("Stopping all stacks...")
stcks = por.get_stacks(base, token, endpoint_id=args.endpoint_id) stcks = por.get_stacks(endpoint_id=args.endpoint_id)
else: else:
por.refresh_status(base, args.stack_id, token) por.refresh_status(args.stack_id)