mirror of
https://gitlab.sectorq.eu/jaydee/portainer.git
synced 2025-12-14 18:44:53 +01:00
build
This commit is contained in:
274
port.py
274
port.py
@@ -5,6 +5,9 @@ import uuid
|
|||||||
import shutil
|
import shutil
|
||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
|
import base64
|
||||||
|
import tabulate
|
||||||
|
from git import Repo
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
|
||||||
|
|
||||||
@@ -129,7 +132,7 @@ class Portainer:
|
|||||||
return resp.text
|
return resp.text
|
||||||
|
|
||||||
def api_post_file(self, path, endpoint_id, name, envs, file, timeout=120):
|
def api_post_file(self, path, endpoint_id, name, envs, file, timeout=120):
|
||||||
#input("API POST2 called. Press Enter to continue.")
|
# input("API POST2 called. Press Enter to continue.")
|
||||||
"""Example authenticated GET request to Portainer API."""
|
"""Example authenticated GET request to Portainer API."""
|
||||||
url = f"{self.base_url.rstrip('/')}{path}"
|
url = f"{self.base_url.rstrip('/')}{path}"
|
||||||
headers = {"X-API-Key": f"{self.token}"}
|
headers = {"X-API-Key": f"{self.token}"}
|
||||||
@@ -138,7 +141,7 @@ class Portainer:
|
|||||||
"Name": name,
|
"Name": name,
|
||||||
"Env": json.dumps(envs)
|
"Env": json.dumps(envs)
|
||||||
}
|
}
|
||||||
#print(data)
|
# print(data)
|
||||||
resp = requests.post(url, headers=headers, files=file, data=data, timeout=timeout)
|
resp = requests.post(url, headers=headers, files=file, data=data, timeout=timeout)
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
return resp.json()
|
return resp.json()
|
||||||
@@ -146,7 +149,7 @@ class Portainer:
|
|||||||
def api_post_no_body(self, path, timeout=120):
|
def api_post_no_body(self, path, timeout=120):
|
||||||
"""Example authenticated GET request to Portainer API."""
|
"""Example authenticated GET request to Portainer API."""
|
||||||
url = f"{self.base_url.rstrip('/')}{path}"
|
url = f"{self.base_url.rstrip('/')}{path}"
|
||||||
#print(url)
|
# print(url)
|
||||||
headers = {"X-API-Key": f"{self.token}"}
|
headers = {"X-API-Key": f"{self.token}"}
|
||||||
resp = requests.post(url, headers=headers, timeout=timeout)
|
resp = requests.post(url, headers=headers, timeout=timeout)
|
||||||
return resp.text
|
return resp.text
|
||||||
@@ -156,9 +159,9 @@ class Portainer:
|
|||||||
url = f"{self.base_url.rstrip('/')}{path}"
|
url = f"{self.base_url.rstrip('/')}{path}"
|
||||||
headers = {"X-API-Key": f"{self.token}"}
|
headers = {"X-API-Key": f"{self.token}"}
|
||||||
resp = requests.delete(url, headers=headers, timeout=timeout)
|
resp = requests.delete(url, headers=headers, timeout=timeout)
|
||||||
#print(resp)
|
# print(resp)
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
#print(resp.status_code)
|
# print(resp.status_code)
|
||||||
return resp.status_code
|
return resp.status_code
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
@@ -173,62 +176,62 @@ class Portainer:
|
|||||||
stcks = []
|
stcks = []
|
||||||
stacks = self.api_get(path, timeout=timeout)
|
stacks = self.api_get(path, timeout=timeout)
|
||||||
self.stacks_all = {}
|
self.stacks_all = {}
|
||||||
fail_endponts = [20,39,41]
|
fail_endponts = [20, 39, 41]
|
||||||
#print(json.dumps(stacks,indent=2))
|
# print(json.dumps(stacks,indent=2))
|
||||||
webhooks = {}
|
webhooks = {}
|
||||||
for s in stacks:
|
for s in stacks:
|
||||||
#print(type(s["AutoUpdate"]) )
|
# print(type(s["AutoUpdate"]) )
|
||||||
#input(s)
|
# input(s)
|
||||||
if s['EndpointId'] in fail_endponts:
|
if s['EndpointId'] in fail_endponts:
|
||||||
continue
|
continue
|
||||||
if not s['EndpointId'] in webhooks:
|
if not s['EndpointId'] in webhooks:
|
||||||
try:
|
try:
|
||||||
webhooks[s['EndpointId']] = {"webhook":{}}
|
webhooks[s['EndpointId']] = {"webhook": {}}
|
||||||
webhooks[self.endpoints["by_id"][s['EndpointId']]] = {"webhook":{}}
|
webhooks[self.endpoints["by_id"][s['EndpointId']]] = {"webhook": {}}
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
logger.debug(f"Exception while getting webhooks for endpoint {s['EndpointId']}: {e}")
|
||||||
if not s['EndpointId'] in self.stacks_all:
|
if not s['EndpointId'] in self.stacks_all:
|
||||||
self.stacks_all[s['EndpointId']] = {"by_id":{},"by_name":{}}
|
self.stacks_all[s['EndpointId']] = {"by_id": {}, "by_name": {}}
|
||||||
self.stacks_all[self.endpoints["by_id"][s['EndpointId']]] = {"by_id":{},"by_name":{}}
|
self.stacks_all[self.endpoints["by_id"][s['EndpointId']]] = {"by_id": {}, "by_name": {}}
|
||||||
self.stacks_all[s['EndpointId']]["by_id"][s['Id']] = s['Name']
|
self.stacks_all[s['EndpointId']]["by_id"][s['Id']] = s['Name']
|
||||||
self.stacks_all[self.endpoints["by_id"][s['EndpointId']]]["by_id"][s['Id']] = s['Name']
|
self.stacks_all[self.endpoints["by_id"][s['EndpointId']]]["by_id"][s['Id']] = s['Name']
|
||||||
|
|
||||||
self.stacks_all[s['EndpointId']]["by_name"][s['Name']] = s['Id']
|
self.stacks_all[s['EndpointId']]["by_name"][s['Name']] = s['Id']
|
||||||
self.stacks_all[self.endpoints["by_id"][s['EndpointId']]]["by_name"][s['Name']] = s['Id']
|
self.stacks_all[self.endpoints["by_id"][s['EndpointId']]]["by_name"][s['Name']] = s['Id']
|
||||||
#print(s)
|
# print(s)
|
||||||
|
|
||||||
if "AutoUpdate" in s and s["AutoUpdate"] is not None:
|
if "AutoUpdate" in s and s["AutoUpdate"] is not None:
|
||||||
if type(s["AutoUpdate"]) == dict and "Webhook" in s["AutoUpdate"]:
|
if type(s["AutoUpdate"]) is dict and "Webhook" in s["AutoUpdate"]:
|
||||||
#print(self.endpoints["by_id"][s['EndpointId']], s['Name'], s["AutoUpdate"]['Webhook'])
|
# print(self.endpoints["by_id"][s['EndpointId']], s['Name'], s["AutoUpdate"]['Webhook'])
|
||||||
#print("WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW")
|
# print("WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW")
|
||||||
webhooks[s['EndpointId']][s['Name']] = s['AutoUpdate']['Webhook']
|
webhooks[s['EndpointId']][s['Name']] = s['AutoUpdate']['Webhook']
|
||||||
webhooks[self.endpoints["by_id"][s['EndpointId']]][s['Name']] = s['AutoUpdate']['Webhook']
|
webhooks[self.endpoints["by_id"][s['EndpointId']]][s['Name']] = s['AutoUpdate']['Webhook']
|
||||||
elif s["AutoUpdate"]["Webhook"] != "":
|
elif s["AutoUpdate"]["Webhook"] != "":
|
||||||
webhooks[s['EndpointId']][s['Name']] = s['Webhook']
|
webhooks[s['EndpointId']][s['Name']] = s['Webhook']
|
||||||
webhooks[self.endpoints["by_id"][s['EndpointId']]][s['Name']] = s['Webhook']
|
webhooks[self.endpoints["by_id"][s['EndpointId']]][s['Name']] = s['Webhook']
|
||||||
|
|
||||||
#print(self.stacks_all)
|
# print(self.stacks_all)
|
||||||
if s['EndpointId'] == endpoint_id or endpoint_id == "all":
|
if s['EndpointId'] == endpoint_id or endpoint_id == "all":
|
||||||
stcks.append(s)
|
stcks.append(s)
|
||||||
#print(stcks)
|
# print(stcks)
|
||||||
if stcks is None:
|
if stcks is None:
|
||||||
return []
|
return []
|
||||||
self.stacks = stacks
|
self.stacks = stacks
|
||||||
self.all_data["stacks"] = self.stacks_all
|
self.all_data["stacks"] = self.stacks_all
|
||||||
self.all_data["webhooks"] = webhooks
|
self.all_data["webhooks"] = webhooks
|
||||||
#input(json.dumps(self.stacks_all,indent=2))
|
# input(json.dumps(self.stacks_all,indent=2))
|
||||||
return stcks
|
return stcks
|
||||||
|
|
||||||
def get_stack_id(self,endpoint,stack):
|
def get_stack_id(self, endpoint, stack):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def update_status(self,endpoint,stack):
|
def update_status(self, endpoint, stack):
|
||||||
path = f"/stacks/{self.all_data['stacks'][endpoint]['by_name'][stack]}/images_status?refresh=true"
|
path = f"/stacks/{self.all_data['stacks'][endpoint]['by_name'][stack]}/images_status?refresh=true"
|
||||||
#input(path)
|
# input(path)
|
||||||
stats = self.api_get(path)
|
stats = self.api_get(path)
|
||||||
print(stats)
|
print(stats)
|
||||||
|
|
||||||
def get_endpoint_id(self,endpoint):
|
def get_endpoint_id(self, endpoint):
|
||||||
if self.is_number(endpoint):
|
if self.is_number(endpoint):
|
||||||
self.endpoint_id = endpoint
|
self.endpoint_id = endpoint
|
||||||
self.endpoint_name = self.endpoints["by_id"][endpoint]
|
self.endpoint_name = self.endpoints["by_id"][endpoint]
|
||||||
@@ -238,7 +241,7 @@ class Portainer:
|
|||||||
self.endpoint_id = self.endpoints["by_name"][endpoint]
|
self.endpoint_id = self.endpoints["by_name"][endpoint]
|
||||||
return self.endpoints["by_name"][endpoint]
|
return self.endpoints["by_name"][endpoint]
|
||||||
|
|
||||||
def get_endpoint_name(self,endpoint):
|
def get_endpoint_name(self, endpoint):
|
||||||
if self.is_number(endpoint):
|
if self.is_number(endpoint):
|
||||||
self.endpoint_id = endpoint
|
self.endpoint_id = endpoint
|
||||||
self.endpoint_name = self.endpoints["by_id"][endpoint]
|
self.endpoint_name = self.endpoints["by_id"][endpoint]
|
||||||
@@ -249,72 +252,75 @@ class Portainer:
|
|||||||
return endpoint
|
return endpoint
|
||||||
|
|
||||||
def get_containers(self, endpoint="all", stack="all", timeout=30):
|
def get_containers(self, endpoint="all", stack="all", timeout=30):
|
||||||
#print(json.dumps(self.all_data,indent=2))
|
# print(json.dumps(self.all_data,indent=2))
|
||||||
#print(endpoint)
|
# print(endpoint)
|
||||||
#print(stack)
|
# print(stack)
|
||||||
cont = []
|
cont = []
|
||||||
data = {}
|
data = {}
|
||||||
if endpoint == "all":
|
if endpoint == "all":
|
||||||
|
|
||||||
for s in self.all_data["endpoints"]["by_id"]:
|
for s in self.all_data["endpoints"]["by_id"]:
|
||||||
#print(s)
|
# print(s)
|
||||||
if stack == "all":
|
if stack == "all":
|
||||||
if not s in self.all_data["stacks"]:
|
if s not in self.all_data["stacks"]:
|
||||||
continue
|
continue
|
||||||
if self.all_data["endpoints_status"][s] != 1:
|
if self.all_data["endpoints_status"][s] != 1:
|
||||||
#print(f"Endpoint {self.all_data["endpoints"]["by_id"][s]} is offline")
|
# print(f"Endpoint {self.all_data["endpoints"]["by_id"][s]} is offline")
|
||||||
continue
|
continue
|
||||||
for e in self.all_data["stacks"][s]["by_name"]:
|
for e in self.all_data["stacks"][s]["by_name"]:
|
||||||
path = f"/endpoints/{s}/docker/containers/json?all=1&filters={{\"label\": [\"com.docker.compose.project={e}\"]}}"
|
path = (
|
||||||
|
f"/endpoints/{s}/docker/containers/json"
|
||||||
|
f"?all=1&filters={{\"label\": [\"com.docker.compose.project={e}\"]}}"
|
||||||
|
)
|
||||||
logging.info(f"request : {path}")
|
logging.info(f"request : {path}")
|
||||||
try:
|
try:
|
||||||
containers = self.api_get(path)
|
containers = self.api_get(path)
|
||||||
except:
|
except Exception as e:
|
||||||
print(f"failed to get containers from {path}")
|
print(f"failed to get containers from {path}: {e}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
contr = []
|
contr = []
|
||||||
try:
|
try:
|
||||||
for c in containers:
|
for c in containers:
|
||||||
cont.append(c["Names"][0].replace("/",""))
|
cont.append(c["Names"][0].replace("/", ""))
|
||||||
contr.append(c["Names"][0].replace("/",""))
|
contr.append(c["Names"][0].replace("/", ""))
|
||||||
if self.all_data["endpoints"]["by_id"][s] in data:
|
if self.all_data["endpoints"]["by_id"][s] in data:
|
||||||
data[self.all_data["endpoints"]["by_id"][s]][e] = contr
|
data[self.all_data["endpoints"]["by_id"][s]][e] = contr
|
||||||
else:
|
else:
|
||||||
data[self.all_data["endpoints"]["by_id"][s]] = {e:contr}
|
data[self.all_data["endpoints"]["by_id"][s]] = {e: contr}
|
||||||
except:
|
except Exception as e:
|
||||||
print("lalalal")
|
logger.debug(
|
||||||
#print(data)
|
f"Exception while getting containers for stack {e} ",
|
||||||
|
f"on endpoint {self.all_data['endpoints']['by_id'][s]}: {e}"
|
||||||
|
)
|
||||||
|
# print(data)
|
||||||
self.all_data["containers"] = data
|
self.all_data["containers"] = data
|
||||||
else:
|
else:
|
||||||
self.get_containers()
|
self.get_containers()
|
||||||
|
|
||||||
for i in self.all_data["containers"][endpoint][stack]:
|
for i in self.all_data["containers"][endpoint][stack]:
|
||||||
cont.append(i)
|
cont.append(i)
|
||||||
|
|
||||||
return cont
|
return cont
|
||||||
|
|
||||||
def stop_containers(self, endpoint, containers, timeout=130):
|
def stop_containers(self, endpoint, containers, timeout=130):
|
||||||
if self.all_data["endpoints_status"][endpoint] != 1:
|
if self.all_data["endpoints_status"][endpoint] != 1:
|
||||||
print(f"Endpoint {self.get_endpoint_name(endpoint)} is offline")
|
print(f"Endpoint {self.get_endpoint_name(endpoint)} is offline")
|
||||||
ep_id = self.endpoints["by_name"][endpoint]
|
ep_id = self.endpoints["by_name"][endpoint]
|
||||||
|
|
||||||
def stop(c):
|
def stop(c):
|
||||||
print(f" > Stopping {c}")
|
print(f" > Stopping {c}")
|
||||||
self.api_post_no_body(
|
self.api_post_no_body(
|
||||||
f"/endpoints/{ep_id}/docker/containers/{c}/stop"
|
f"/endpoints/{ep_id}/docker/containers/{c}/stop"
|
||||||
)
|
)
|
||||||
#print(f"✔")
|
# print(f"✔")
|
||||||
with ThreadPoolExecutor(max_workers=10) as exe:
|
with ThreadPoolExecutor(max_workers=10) as exe:
|
||||||
exe.map(stop, containers)
|
exe.map(stop, containers)
|
||||||
# for c in containers:
|
# for c in containers:
|
||||||
# print(f" > Stopping {c}")
|
# print(f" > Stopping {c}")
|
||||||
# self.api_post_no_body(f"/endpoints/{self.endpoints["by_name"][endpoint]}/docker/containers/{c}/stop")
|
# self.api_post_no_body(f"/endpoints/{self.endpoints["by_name"][endpoint]}/docker/containers/{c}/stop")
|
||||||
|
|
||||||
|
|
||||||
# return 0
|
# return 0
|
||||||
|
|
||||||
def start_containers(self, endpoint, containers, timeout=130):
|
def start_containers(self, endpoint, containers, timeout=130):
|
||||||
ep_id = self.endpoints["by_name"][endpoint]
|
ep_id = self.endpoints["by_name"][endpoint]
|
||||||
|
|
||||||
def stop(c):
|
def stop(c):
|
||||||
print(f" > Starting {c}")
|
print(f" > Starting {c}")
|
||||||
self.api_post_no_body(
|
self.api_post_no_body(
|
||||||
@@ -327,22 +333,24 @@ class Portainer:
|
|||||||
stcs = []
|
stcs = []
|
||||||
if stack == "all":
|
if stack == "all":
|
||||||
for s in self.all_data["webhooks"][endpoint]:
|
for s in self.all_data["webhooks"][endpoint]:
|
||||||
stcs.append([s,self.all_data["webhooks"][endpoint][s]])
|
stcs.append([s, self.all_data["webhooks"][endpoint][s]])
|
||||||
else:
|
else:
|
||||||
stcs.append([stack, self.all_data["webhooks"][endpoint][stack]])
|
stcs.append([stack, self.all_data["webhooks"][endpoint][stack]])
|
||||||
|
|
||||||
#input(stcs)
|
# input(stcs)
|
||||||
def update(c):
|
def update(c):
|
||||||
print(f" > Updating {c[0]} on {endpoint}")
|
print(f" > Updating {c[0]} on {endpoint}")
|
||||||
ans = self.api_post_no_body(
|
ans = self.api_post_no_body(
|
||||||
f"/stacks/webhooks/{c[1]}"
|
f"/stacks/webhooks/{c[1]}"
|
||||||
)
|
)
|
||||||
|
logger.debug(f"Update response for stack {c[0]} on endpoint {endpoint}: {ans}")
|
||||||
|
|
||||||
def stop():
|
def stop():
|
||||||
cont = []
|
cont = []
|
||||||
for c in self.all_data["containers"][endpoint]:
|
for c in self.all_data["containers"][endpoint]:
|
||||||
if stack == c or stack== "all":
|
if stack == c or stack == "all":
|
||||||
cont+=self.all_data["containers"][endpoint][c]
|
cont += self.all_data["containers"][endpoint][c]
|
||||||
self.stop_containers(endpoint,cont)
|
self.stop_containers(endpoint, cont)
|
||||||
|
|
||||||
with ThreadPoolExecutor(max_workers=10) as exe:
|
with ThreadPoolExecutor(max_workers=10) as exe:
|
||||||
exe.map(update, stcs)
|
exe.map(update, stcs)
|
||||||
@@ -351,13 +359,13 @@ class Portainer:
|
|||||||
time.sleep(120)
|
time.sleep(120)
|
||||||
cont = []
|
cont = []
|
||||||
for c in self.all_data["containers"][endpoint]:
|
for c in self.all_data["containers"][endpoint]:
|
||||||
if stack == c or stack== "all":
|
if stack == c or stack == "all":
|
||||||
cont+=self.all_data["containers"][endpoint][c]
|
cont += self.all_data["containers"][endpoint][c]
|
||||||
self.stop_containers(endpoint,cont)
|
self.stop_containers(endpoint, cont)
|
||||||
|
|
||||||
def get_endpoints(self, timeout=10):
|
def get_endpoints(self, timeout=10):
|
||||||
endpoints = self.api_get("/endpoints")
|
endpoints = self.api_get("/endpoints")
|
||||||
eps = {"by_id":{}, "by_name":{}}
|
eps = {"by_id": {}, "by_name": {}}
|
||||||
eps_stats = {}
|
eps_stats = {}
|
||||||
for ep in endpoints:
|
for ep in endpoints:
|
||||||
eps['by_id'][ep['Id']] = ep['Name']
|
eps['by_id'][ep['Id']] = ep['Name']
|
||||||
@@ -367,13 +375,13 @@ class Portainer:
|
|||||||
self.endpoints = eps
|
self.endpoints = eps
|
||||||
self.all_data["endpoints"] = eps
|
self.all_data["endpoints"] = eps
|
||||||
self.all_data["endpoints_status"] = eps_stats
|
self.all_data["endpoints_status"] = eps_stats
|
||||||
#input(eps_stats)
|
# input(eps_stats)
|
||||||
#input(eps)
|
# input(eps)
|
||||||
return eps
|
return eps
|
||||||
|
|
||||||
def get_endpoint(self, endpoint_id=None, timeout=30):
|
def get_endpoint(self, endpoint_id=None, timeout=30):
|
||||||
self.get_endpoints()
|
self.get_endpoints()
|
||||||
#print(self.endpoints)
|
# print(self.endpoints)
|
||||||
if self.is_number(endpoint_id):
|
if self.is_number(endpoint_id):
|
||||||
self.endpoint_name = self.endpoints["by_id"][endpoint_id]
|
self.endpoint_name = self.endpoints["by_id"][endpoint_id]
|
||||||
self.endpoint_id = endpoint_id
|
self.endpoint_id = endpoint_id
|
||||||
@@ -390,24 +398,37 @@ class Portainer:
|
|||||||
|
|
||||||
def get_stack(self, stack=None, endpoint_id=None, timeout=None):
|
def get_stack(self, stack=None, endpoint_id=None, timeout=None):
|
||||||
self.get_stacks(endpoint_id)
|
self.get_stacks(endpoint_id)
|
||||||
|
|
||||||
if not self.is_number(endpoint_id):
|
if not self.is_number(endpoint_id):
|
||||||
endpoint_id = int(self.endpoints["by_name"][endpoint_id])
|
endpoint_id = int(self.endpoints["by_name"][endpoint_id])
|
||||||
self.stack_id = []
|
self.stack_id = []
|
||||||
if stack == "all":
|
if stack == "all":
|
||||||
for s in self.stacks:
|
for s in self.stacks:
|
||||||
#print(s)
|
# print(s)
|
||||||
if (endpoint_id == s.get("EndpointId")):
|
if (endpoint_id == s.get("EndpointId")):
|
||||||
self.stack_ids.append(s.get("Id"))
|
self.stack_ids.append(s.get("Id"))
|
||||||
return self.stack_ids
|
return self.stack_ids
|
||||||
else:
|
else:
|
||||||
for s in self.stacks:
|
for s in self.stacks:
|
||||||
#print(s)
|
# print(s)
|
||||||
if (stack is not None and s.get("Id") == stack and endpoint_id == s.get("EndpointId")) or str(s.get("Name")) == str(stack) and endpoint_id == int(s.get("EndpointId")):
|
match_by_id = (
|
||||||
|
stack is not None
|
||||||
|
and s.get("Id") == stack
|
||||||
|
and endpoint_id == s.get("EndpointId")
|
||||||
|
)
|
||||||
|
|
||||||
|
match_by_name = (
|
||||||
|
str(s.get("Name")) == str(stack)
|
||||||
|
and endpoint_id == int(s.get("EndpointId")) # Ensure types match for comparison
|
||||||
|
)
|
||||||
|
|
||||||
|
if match_by_id or match_by_name:
|
||||||
|
# if (stack is not None and s.get("Id") == stack and endpoint_id == s.get("EndpointId"))
|
||||||
|
# or str(s.get("Name")) == str(stack) and endpoint_id == int(s.get("EndpointId")):
|
||||||
self.stack_id = s.get("Id")
|
self.stack_id = s.get("Id")
|
||||||
self.stack_name = s.get("Name")
|
self.stack_name = s.get("Name")
|
||||||
self.stack_ids.append(s.get("Id"))
|
self.stack_ids.append(s.get("Id"))
|
||||||
return s
|
return s
|
||||||
|
|
||||||
raise ValueError(f"Stack not found: {stack}")
|
raise ValueError(f"Stack not found: {stack}")
|
||||||
|
|
||||||
def create_stack(self, endpoint, stack=None, mode="git", autostart=False, swarm=False, timeout=None):
|
def create_stack(self, endpoint, stack=None, mode="git", autostart=False, swarm=False, timeout=None):
|
||||||
@@ -418,7 +439,7 @@ class Portainer:
|
|||||||
else:
|
else:
|
||||||
p = "standalone"
|
p = "standalone"
|
||||||
env_path = f"{self.repo_dir}/{stack}/.env"
|
env_path = f"{self.repo_dir}/{stack}/.env"
|
||||||
#input(swarm_id)
|
# input(swarm_id)
|
||||||
self.endpoint_id = self.get_endpoint_id(endpoint)
|
self.endpoint_id = self.get_endpoint_id(endpoint)
|
||||||
if os.path.exists(self.repo_dir):
|
if os.path.exists(self.repo_dir):
|
||||||
shutil.rmtree(self.repo_dir)
|
shutil.rmtree(self.repo_dir)
|
||||||
@@ -427,11 +448,9 @@ class Portainer:
|
|||||||
Repo.clone_from(self.git_url, self.repo_dir)
|
Repo.clone_from(self.git_url, self.repo_dir)
|
||||||
if mode == "git":
|
if mode == "git":
|
||||||
path = f"/stacks/create/{p}/repository"
|
path = f"/stacks/create/{p}/repository"
|
||||||
|
|
||||||
if self.endpoint_id is not None:
|
if self.endpoint_id is not None:
|
||||||
path += f"?endpointId={self.endpoint_id}"
|
path += f"?endpointId={self.endpoint_id}"
|
||||||
|
|
||||||
|
|
||||||
if stack == "all":
|
if stack == "all":
|
||||||
if self.endpoint_name == "rack":
|
if self.endpoint_name == "rack":
|
||||||
stacks = self.rack_stacks
|
stacks = self.rack_stacks
|
||||||
@@ -443,52 +462,53 @@ class Portainer:
|
|||||||
stacks = self.nas_stacks
|
stacks = self.nas_stacks
|
||||||
else:
|
else:
|
||||||
stacks = [stack]
|
stacks = [stack]
|
||||||
#print(json.dumps(self.stacks_all, indent=2))
|
# print(json.dumps(self.stacks_all, indent=2))
|
||||||
#input(json.dumps(self.stacks_all,indent=2))
|
# input(json.dumps(self.stacks_all, indent=2))
|
||||||
for stack in stacks:
|
for stack in stacks:
|
||||||
if self.endpoint_id in self.stacks_all:
|
if self.endpoint_id in self.stacks_all:
|
||||||
if stack in self.stacks_all[self.endpoint_id]['by_id'] or stack in self.stacks_all[self.endpoint_id]['by_name']:
|
|
||||||
|
# Check if the stack exists by ID or name
|
||||||
|
stack_check = (
|
||||||
|
stack in self.stacks_all[self.endpoint_id]['by_id']
|
||||||
|
or stack in self.stacks_all[self.endpoint_id]['by_name']
|
||||||
|
)
|
||||||
|
if stack_check:
|
||||||
print(f"Stack {stack} already exist")
|
print(f"Stack {stack} already exist")
|
||||||
continue
|
continue
|
||||||
print(f"Working on {stack}")
|
print(f"Working on {stack}")
|
||||||
|
|
||||||
|
|
||||||
envs = []
|
envs = []
|
||||||
if os.path.exists(f"{env_path}"):
|
if os.path.exists(f"{env_path}"):
|
||||||
f = open(f"{env_path}","r")
|
f = open(f"{env_path}", "r")
|
||||||
env_vars = f.read().splitlines()
|
env_vars = f.read().splitlines()
|
||||||
|
|
||||||
for ev in env_vars:
|
for ev in env_vars:
|
||||||
if ev.startswith("#") or ev.strip() == "":
|
if ev.startswith("#") or ev.strip() == "":
|
||||||
continue
|
continue
|
||||||
if "=" in ev:
|
if "=" in ev:
|
||||||
name, value = ev.split("=",1)
|
name, value = ev.split("=", 1)
|
||||||
envs.append({"name": name, "value": value})
|
envs.append({"name": name, "value": value})
|
||||||
f.close()
|
f.close()
|
||||||
#wl(envs)
|
# wl(envs)
|
||||||
for e in envs:
|
for e in envs:
|
||||||
#print(f"Env: {e['name']} = {e['value']}")
|
# print(f"Env: {e['name']} = {e['value']}")
|
||||||
HWS = ["HW_MODE","HW_MODE1","HW_MODE2"]
|
HWS = ["HW_MODE", "HW_MODE1", "HW_MODE2"]
|
||||||
if e['name'] == "RESTART" and self.endpoint_name == "m-server":
|
if e['name'] == "RESTART" and self.endpoint_name == "m-server":
|
||||||
e['value'] = "always"
|
e['value'] = "always"
|
||||||
if e['name'] in HWS:
|
if e['name'] in HWS:
|
||||||
#print("Found HW_MODE env var.")
|
# print("Found HW_MODE env var.")
|
||||||
if self.hw_mode:
|
if self.hw_mode:
|
||||||
e['value'] = "hw"
|
e['value'] = "hw"
|
||||||
else:
|
else:
|
||||||
e['value'] = "cpu"
|
e['value'] = "cpu"
|
||||||
if e['name'] == "LOGGING":
|
if e['name'] == "LOGGING":
|
||||||
#print("Found LOGGING env var.")
|
# print("Found LOGGING env var.")
|
||||||
if self.log_mode:
|
if self.log_mode:
|
||||||
e['value'] = "journald"
|
e['value'] = "journald"
|
||||||
else:
|
else:
|
||||||
e['value'] = "syslog"
|
e['value'] = "syslog"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
uid = uuid.uuid4()
|
uid = uuid.uuid4()
|
||||||
#print(uid)
|
# print(uid)
|
||||||
req = {
|
req = {
|
||||||
"Name": stack,
|
"Name": stack,
|
||||||
"Env": envs,
|
"Env": envs,
|
||||||
@@ -510,7 +530,7 @@ class Portainer:
|
|||||||
"supportRelativePath": True,
|
"supportRelativePath": True,
|
||||||
"repositoryAuthentication": True,
|
"repositoryAuthentication": True,
|
||||||
"fromAppTemplate": False,
|
"fromAppTemplate": False,
|
||||||
"registries": [6,3],
|
"registries": [6, 3],
|
||||||
"FromAppTemplate": False,
|
"FromAppTemplate": False,
|
||||||
"Namespace": "",
|
"Namespace": "",
|
||||||
"CreatedByUserId": "",
|
"CreatedByUserId": "",
|
||||||
@@ -518,7 +538,7 @@ class Portainer:
|
|||||||
"filesystemPath": "/share/docker_data/portainer/portainer-data/",
|
"filesystemPath": "/share/docker_data/portainer/portainer-data/",
|
||||||
"RegistryID": 4,
|
"RegistryID": 4,
|
||||||
"isDetachedFromGit": True,
|
"isDetachedFromGit": True,
|
||||||
"method":"repository",
|
"method": "repository",
|
||||||
"swarmID": None
|
"swarmID": None
|
||||||
}
|
}
|
||||||
if swarm:
|
if swarm:
|
||||||
@@ -528,9 +548,9 @@ class Portainer:
|
|||||||
req["ConfigFilePath"] = f"__swarm/{stack}/{stack}-swarm.yml"
|
req["ConfigFilePath"] = f"__swarm/{stack}/{stack}-swarm.yml"
|
||||||
|
|
||||||
print(json.dumps(req))
|
print(json.dumps(req))
|
||||||
res = self.api_post(path,req)
|
res = self.api_post(path, req)
|
||||||
if "Id" in res:
|
if "Id" in res:
|
||||||
#print("Deploy request OK")
|
# print("Deploy request OK")
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
print(res)
|
print(res)
|
||||||
@@ -538,9 +558,9 @@ class Portainer:
|
|||||||
created = False
|
created = False
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
#print(self.endpoint_id)
|
# print(self.endpoint_id)
|
||||||
#print(stack)
|
# print(stack)
|
||||||
stck2 = self.get_stack(stack, self.endpoint_id)
|
self.get_stack(stack, self.endpoint_id)
|
||||||
created = True
|
created = True
|
||||||
break
|
break
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -550,16 +570,17 @@ class Portainer:
|
|||||||
if tries > 50:
|
if tries > 50:
|
||||||
print(f"Error retrieving stack {stack} after creation: {self.endpoint_name}")
|
print(f"Error retrieving stack {stack} after creation: {self.endpoint_name}")
|
||||||
break
|
break
|
||||||
|
logger.debug(f"Exception while getting stack {stack}: {e}")
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
if stack != "pihole":
|
if stack != "pihole":
|
||||||
#print(autostart)
|
# print(autostart)
|
||||||
if not autostart:
|
if not autostart:
|
||||||
#self.get_stacks()
|
# self.get_stacks()
|
||||||
#self.stop_stack(stack,self.endpoint_id)
|
# self.stop_stack(stack,self.endpoint_id)
|
||||||
conts = self.get_containers(self.endpoint_name,stack)
|
conts = self.get_containers(self.endpoint_name, stack)
|
||||||
#print(conts)
|
# print(conts)
|
||||||
self.stop_containers(self.endpoint_name,conts)
|
self.stop_containers(self.endpoint_name, conts)
|
||||||
|
|
||||||
if mode == "file":
|
if mode == "file":
|
||||||
print("Creating new stack from file...")
|
print("Creating new stack from file...")
|
||||||
@@ -579,7 +600,7 @@ class Portainer:
|
|||||||
for stack in stacks:
|
for stack in stacks:
|
||||||
print(f"Working on {stack}")
|
print(f"Working on {stack}")
|
||||||
if os.path.exists(f"{self.repo_dir}/{stack}/.env"):
|
if os.path.exists(f"{self.repo_dir}/{stack}/.env"):
|
||||||
f = open(f"{self.repo_dir}/{stack}/.env","r")
|
f = open(f"{self.repo_dir}/{stack}/.env", "r")
|
||||||
|
|
||||||
env_vars = f.read().splitlines()
|
env_vars = f.read().splitlines()
|
||||||
envs = []
|
envs = []
|
||||||
@@ -587,13 +608,13 @@ class Portainer:
|
|||||||
if ev.startswith("#") or ev.strip() == "":
|
if ev.startswith("#") or ev.strip() == "":
|
||||||
continue
|
continue
|
||||||
if "=" in ev:
|
if "=" in ev:
|
||||||
name, value = ev.split("=",1)
|
name, value = ev.split("=", 1)
|
||||||
envs.append({"name": name, "value": value})
|
envs.append({"name": name, "value": value})
|
||||||
f.close()
|
f.close()
|
||||||
#wl(envs)
|
# wl(envs)
|
||||||
for e in envs:
|
for e in envs:
|
||||||
#print(f"Env: {e['name']} = {e['value']}")
|
# print(f"Env: {e['name']} = {e['value']}")
|
||||||
HWS = ["HW_MODE","HW_MODE1","HW_MODE2"]
|
HWS = ["HW_MODE", "HW_MODE1", "HW_MODE2"]
|
||||||
if e['name'] == "RESTART" and self.endpoint_name == "m-server":
|
if e['name'] == "RESTART" and self.endpoint_name == "m-server":
|
||||||
e['value'] = "always"
|
e['value'] = "always"
|
||||||
if e['name'] in HWS:
|
if e['name'] in HWS:
|
||||||
@@ -613,15 +634,14 @@ class Portainer:
|
|||||||
# ("filename", file_object)
|
# ("filename", file_object)
|
||||||
"file": ("docker-compose.yml", open(f"/tmp/docker-compose/{stack}/docker-compose.yml", "rb")),
|
"file": ("docker-compose.yml", open(f"/tmp/docker-compose/{stack}/docker-compose.yml", "rb")),
|
||||||
}
|
}
|
||||||
self.api_post_file(path,self.endpoint_id,stack,envs,file)
|
self.api_post_file(path, self.endpoint_id, stack, envs, file)
|
||||||
|
|
||||||
def print_stacks(self,endpoint="all"):
|
def print_stacks(self, endpoint="all"):
|
||||||
stacks = self.get_stacks()
|
stacks = self.get_stacks()
|
||||||
count = 0
|
count = 0
|
||||||
lst = []
|
|
||||||
data = []
|
data = []
|
||||||
for stack in stacks:
|
for stack in stacks:
|
||||||
if endpoint != None:
|
if endpoint is not None:
|
||||||
if not stack['EndpointId'] in self.endpoints['by_id']:
|
if not stack['EndpointId'] in self.endpoints['by_id']:
|
||||||
continue
|
continue
|
||||||
if endpoint != "all":
|
if endpoint != "all":
|
||||||
@@ -631,18 +651,19 @@ class Portainer:
|
|||||||
data.append([stack['Id'], stack['Name'], self.endpoints['by_id'][stack['EndpointId']]])
|
data.append([stack['Id'], stack['Name'], self.endpoints['by_id'][stack['EndpointId']]])
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
data.append([stack['Id'], stack['Name'], "?"])
|
data.append([stack['Id'], stack['Name'], "?"])
|
||||||
|
logger.debug(f"KeyError getting endpoint name for stack {stack['Name']}: {e}")
|
||||||
count += 1
|
count += 1
|
||||||
|
|
||||||
headers = ["StackID", "Name", "Endpoint"]
|
headers = ["StackID", "Name", "Endpoint"]
|
||||||
print(tabulate(data, headers=headers, tablefmt="github"))
|
print(tabulate(data, headers=headers, tablefmt="github"))
|
||||||
print(f"Total stacks: {count}")
|
print(f"Total stacks: {count}")
|
||||||
|
|
||||||
def start_stack(self,stack=None,endpoint_id=None):
|
def start_stack(self, stack=None, endpoint_id=None):
|
||||||
if endpoint_id != None:
|
if endpoint_id is not None:
|
||||||
print("Getting endpoint")
|
print("Getting endpoint")
|
||||||
self.get_endpoint(endpoint_id)
|
self.get_endpoint(endpoint_id)
|
||||||
if stack != None:
|
if stack is not None:
|
||||||
self.get_stack(stack,endpoint_id)
|
self.get_stack(stack, endpoint_id)
|
||||||
for stack in self.stack_ids:
|
for stack in self.stack_ids:
|
||||||
path = f"/stacks/{stack}/start"
|
path = f"/stacks/{stack}/start"
|
||||||
if self.endpoint_id is not None:
|
if self.endpoint_id is not None:
|
||||||
@@ -658,15 +679,15 @@ class Portainer:
|
|||||||
print(f"Stack {self.stacks_all[self.endpoint_id]['by_id'][stack]} : {json.loads(resp)['message']}")
|
print(f"Stack {self.stacks_all[self.endpoint_id]['by_id'][stack]} : {json.loads(resp)['message']}")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def stop_stack(self,stack,endpoint_id):
|
def stop_stack(self, stack, endpoint_id):
|
||||||
print(f"Stopping stack {stack}")
|
print(f"Stopping stack {stack}")
|
||||||
if endpoint_id != None:
|
if endpoint_id is not None:
|
||||||
self.get_endpoint(endpoint_id)
|
self.get_endpoint(endpoint_id)
|
||||||
if stack == "all":
|
if stack == "all":
|
||||||
self.get_stack(stack,endpoint_id)
|
self.get_stack(stack, endpoint_id)
|
||||||
else:
|
else:
|
||||||
if stack != None:
|
if stack is not None:
|
||||||
self.stack_ids = [self.get_stack(stack,endpoint_id)["Id"]]
|
self.stack_ids = [self.get_stack(stack, endpoint_id)["Id"]]
|
||||||
for stack in self.stack_ids:
|
for stack in self.stack_ids:
|
||||||
path = f"/stacks/{stack}/stop"
|
path = f"/stacks/{stack}/stop"
|
||||||
if self.endpoint_id is not None:
|
if self.endpoint_id is not None:
|
||||||
@@ -674,7 +695,7 @@ class Portainer:
|
|||||||
try:
|
try:
|
||||||
resp = self.api_post_no_body(path, timeout=120)
|
resp = self.api_post_no_body(path, timeout=120)
|
||||||
except NameError as e:
|
except NameError as e:
|
||||||
print(f"Error stoping stack: {e}")
|
print(f"Error stopping stack: {e}")
|
||||||
return []
|
return []
|
||||||
if "Id" in json.loads(resp):
|
if "Id" in json.loads(resp):
|
||||||
print(f"Stack {self.stacks_all[self.endpoint_id]['by_id'][stack]} : stopped")
|
print(f"Stack {self.stacks_all[self.endpoint_id]['by_id'][stack]} : stopped")
|
||||||
@@ -694,31 +715,32 @@ class Portainer:
|
|||||||
self.endpoint_name = endpoint_id
|
self.endpoint_name = endpoint_id
|
||||||
self.endpoint_id = self.endpoints["by_name"][endpoint_id]
|
self.endpoint_id = self.endpoints["by_name"][endpoint_id]
|
||||||
|
|
||||||
|
|
||||||
if not self.is_number(endpoint_id):
|
if not self.is_number(endpoint_id):
|
||||||
endpoint_id = int(self.endpoints["by_name"][endpoint_id])
|
endpoint_id = int(self.endpoints["by_name"][endpoint_id])
|
||||||
|
|
||||||
if not self.is_number(stack) and stack != "all":
|
if not self.is_number(stack) and stack != "all":
|
||||||
#print(stack)
|
# print(stack)
|
||||||
#print(self.endpoint_id)
|
# print(self.endpoint_id)
|
||||||
stack = self.get_stack(stack,self.endpoint_id)["Id"]
|
stack = self.get_stack(stack, self.endpoint_id)["Id"]
|
||||||
if stack == "all":
|
if stack == "all":
|
||||||
stacks = self.get_stacks(self.endpoint_id)
|
stacks = self.get_stacks(self.endpoint_id)
|
||||||
paths = []
|
paths = []
|
||||||
for s in stacks:
|
for s in stacks:
|
||||||
#print(f"Delete stack {s['Name']}")
|
# print(f"Delete stack {s['Name']}")
|
||||||
#print(s['EndpointId'], endpoint_id)
|
# print(s['EndpointId'], endpoint_id)
|
||||||
if int(s['EndpointId']) != int(endpoint_id):
|
if int(s['EndpointId']) != int(endpoint_id):
|
||||||
continue
|
continue
|
||||||
#print("Deleting stack:", s['Name'])
|
# print("Deleting stack:", s['Name'])
|
||||||
path = f"/stacks/{s['Id']}"
|
path = f"/stacks/{s['Id']}"
|
||||||
if endpoint_id is not None:
|
if endpoint_id is not None:
|
||||||
path += f"?endpointId={endpoint_id}&removeVolumes=true"
|
path += f"?endpointId={endpoint_id}&removeVolumes=true"
|
||||||
paths.append([self.get_endpoint_name(endpoint_id),s['Name'],path])
|
paths.append([self.get_endpoint_name(endpoint_id), s['Name'], path])
|
||||||
#input(paths)
|
# input(paths)
|
||||||
|
|
||||||
def delete(c):
|
def delete(c):
|
||||||
print(f"Delete stack {c[1]} from {c[0]} ")
|
print(f"Delete stack {c[1]} from {c[0]} ")
|
||||||
out = self.api_delete(c[2])
|
out = self.api_delete(c[2])
|
||||||
|
logger.debug(f"Deleted stack {c[1]} from {c[0]}: {out}")
|
||||||
|
|
||||||
with ThreadPoolExecutor(max_workers=10) as exe:
|
with ThreadPoolExecutor(max_workers=10) as exe:
|
||||||
exe.map(delete, paths)
|
exe.map(delete, paths)
|
||||||
@@ -735,12 +757,12 @@ class Portainer:
|
|||||||
# print(token)
|
# print(token)
|
||||||
stacks = self.api_delete(path)
|
stacks = self.api_delete(path)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
#print(f"Error creating stack: {e}")
|
# print(f"Error creating stack: {e}")
|
||||||
if "Conflict for url" in str(e):
|
if "Conflict for url" in str(e):
|
||||||
print("Stack with this name may already exist.")
|
print("Stack with this name may already exist.")
|
||||||
else:
|
else:
|
||||||
print(f"Error deleting stack: {e}")
|
print(f"Error deleting stack: {e}")
|
||||||
#print(stacks)
|
# print(stacks)
|
||||||
return []
|
return []
|
||||||
if stacks is None:
|
if stacks is None:
|
||||||
return []
|
return []
|
||||||
|
|||||||
119
portainer.py
119
portainer.py
@@ -3,15 +3,11 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import requests
|
import requests
|
||||||
import json
|
import json
|
||||||
import uuid
|
|
||||||
import argparse
|
import argparse
|
||||||
import shutil
|
|
||||||
import time
|
|
||||||
from tabulate import tabulate
|
from tabulate import tabulate
|
||||||
from git import Repo # pip install gitpython
|
|
||||||
from port import Portainer
|
from port import Portainer
|
||||||
import logging
|
import logging
|
||||||
VERSION="0.0.1"
|
VERSION = "0.0.1"
|
||||||
|
|
||||||
defaults = {
|
defaults = {
|
||||||
"endpoint_id": "vm01",
|
"endpoint_id": "vm01",
|
||||||
@@ -24,18 +20,21 @@ defaults = {
|
|||||||
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description="Portainer helper - use env vars or pass credentials.")
|
parser = argparse.ArgumentParser(description="Portainer helper - use env vars or pass credentials.")
|
||||||
parser.add_argument("--base", "-b", default=os.getenv("PORTAINER_URL", \
|
parser.add_argument("--base",
|
||||||
"https://portainer.example.com"),help="Base URL for Portainer (ENV: PORTAINER_URL)")
|
"-b",
|
||||||
|
default=os.getenv("PORTAINER_URL", "https://portainer.example.com"),
|
||||||
|
help="Base URL for Portainer (ENV: PORTAINER_URL)"
|
||||||
|
)
|
||||||
parser.add_argument("--site", "-t", type=str, default=None, help="Site")
|
parser.add_argument("--site", "-t", type=str, default=None, help="Site")
|
||||||
parser.add_argument("--endpoint-id", "-e", type=str, default=None, help="Endpoint ID to limit stack operations")
|
parser.add_argument("--endpoint-id", "-e", type=str, default=None, help="Endpoint ID to limit stack operations")
|
||||||
parser.add_argument("--refresh-environment", "-R", action="store_true", help="List endpoints")
|
parser.add_argument("--refresh-environment", "-R", action="store_true", help="List endpoints")
|
||||||
parser.add_argument("--list-endpoints","-E", action="store_true", help="List endpoints")
|
parser.add_argument("--list-endpoints", "-E", action="store_true", help="List endpoints")
|
||||||
parser.add_argument("--list-stacks", "-l", action="store_true", help="List stacks")
|
parser.add_argument("--list-stacks", "-l", action="store_true", help="List stacks")
|
||||||
parser.add_argument("--print-all-data", "-A", action="store_true", help="List stacks")
|
parser.add_argument("--print-all-data", "-A", action="store_true", help="List stacks")
|
||||||
parser.add_argument("--list-containers", "-c", action="store_true", help="List containers")
|
parser.add_argument("--list-containers", "-c", action="store_true", help="List containers")
|
||||||
parser.add_argument("--update-stack", "-U", action="store_true", help="Update stacls")
|
parser.add_argument("--update-stack", "-U", action="store_true", help="Update stacks")
|
||||||
parser.add_argument("--stop-containers", "-O", action="store_true", help="Stop containers")
|
parser.add_argument("--stop-containers", "-O", action="store_true", help="Stop containers")
|
||||||
parser.add_argument("--start-containers", "-X", action="store_true", help="Stop containers")
|
parser.add_argument("--start-containers", "-X", action="store_true", help="Start containers")
|
||||||
parser.add_argument("--update-status", "-S", action="store_true", help="Update status")
|
parser.add_argument("--update-status", "-S", action="store_true", help="Update status")
|
||||||
parser.add_argument("--get-stack", metavar="NAME_OR_ID", help="Get stack by name or numeric id")
|
parser.add_argument("--get-stack", metavar="NAME_OR_ID", help="Get stack by name or numeric id")
|
||||||
parser.add_argument("--action", "-a", type=str, default=None, help="Action to perform")
|
parser.add_argument("--action", "-a", type=str, default=None, help="Action to perform")
|
||||||
@@ -44,17 +43,17 @@ parser.add_argument("--start-stack", "-x", action='store_true')
|
|||||||
parser.add_argument("--stop-stack", "-o", action='store_true')
|
parser.add_argument("--stop-stack", "-o", action='store_true')
|
||||||
parser.add_argument("--secrets", "-q", action='store_true')
|
parser.add_argument("--secrets", "-q", action='store_true')
|
||||||
parser.add_argument("--debug", "-D", action='store_true')
|
parser.add_argument("--debug", "-D", action='store_true')
|
||||||
parser.add_argument("--create-stack","-n", action='store_true')
|
parser.add_argument("--create-stack", "-n", action='store_true')
|
||||||
parser.add_argument("--create-stack_new2","-N", action='store_true')
|
parser.add_argument("--create-stack_new2", "-N", action='store_true')
|
||||||
parser.add_argument("--gpu","-g", action='store_true')
|
parser.add_argument("--gpu", "-g", action='store_true')
|
||||||
parser.add_argument("--create-stacks","-C", action='store_true')
|
parser.add_argument("--create-stacks", "-C", action='store_true')
|
||||||
parser.add_argument("--refresh-status","-r", action='store_true')
|
parser.add_argument("--refresh-status", "-r", action='store_true')
|
||||||
|
|
||||||
parser.add_argument("--stack", "-s", type=str, help="Stack ID for operations")
|
parser.add_argument("--stack", "-s", type=str, help="Stack ID for operations")
|
||||||
parser.add_argument("--token-only", action="store_true", help="Print auth token and exit")
|
parser.add_argument("--token-only", action="store_true", help="Print auth token and exit")
|
||||||
parser.add_argument("--timeout", type=int, default=10, help="Request timeout seconds")
|
parser.add_argument("--timeout", type=int, default=10, help="Request timeout seconds")
|
||||||
parser.add_argument("--deploy-mode","-m", type=str, default="git", help="Deploy mode")
|
parser.add_argument("--deploy-mode", "-m", type=str, default="git", help="Deploy mode")
|
||||||
parser.add_argument("--stack-mode","-w", default=None, help="Stack mode")
|
parser.add_argument("--stack-mode", "-w", default=None, help="Stack mode")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
print("Running version:", VERSION)
|
print("Running version:", VERSION)
|
||||||
print("Environment:", args.site)
|
print("Environment:", args.site)
|
||||||
@@ -62,16 +61,36 @@ print("Environment:", args.site)
|
|||||||
_LOG_LEVEL = "INFO"
|
_LOG_LEVEL = "INFO"
|
||||||
LOG_FILE = "/tmp/portainer.log"
|
LOG_FILE = "/tmp/portainer.log"
|
||||||
if _LOG_LEVEL == "DEBUG":
|
if _LOG_LEVEL == "DEBUG":
|
||||||
logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG, format='%(asctime)s : %(levelname)s : %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
|
logging.basicConfig(
|
||||||
logging.debug('using debug loging')
|
filename=LOG_FILE,
|
||||||
|
level=logging.DEBUG,
|
||||||
|
format='%(asctime)s : %(levelname)s : %(message)s',
|
||||||
|
datefmt='%m/%d/%Y %I:%M:%S %p'
|
||||||
|
)
|
||||||
|
logging.debug('using debug logging')
|
||||||
elif _LOG_LEVEL == "ERROR":
|
elif _LOG_LEVEL == "ERROR":
|
||||||
logging.basicConfig(filename=LOG_FILE, level=logging.ERROR, format='%(asctime)s : %(levelname)s : %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
|
logging.basicConfig(
|
||||||
logging.info('using error loging')
|
filename=LOG_FILE,
|
||||||
|
level=logging.ERROR,
|
||||||
|
format='%(asctime)s : %(levelname)s : %(message)s',
|
||||||
|
datefmt='%m/%d/%Y %I:%M:%S %p'
|
||||||
|
)
|
||||||
|
logging.info('using error logging')
|
||||||
elif _LOG_LEVEL == "SCAN":
|
elif _LOG_LEVEL == "SCAN":
|
||||||
logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG, format='%(asctime)s : %(levelname)s : %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
|
logging.basicConfig(
|
||||||
logging.info('using error loging')
|
filename=LOG_FILE,
|
||||||
|
level=logging.DEBUG,
|
||||||
|
format='%(asctime)s : %(levelname)s : %(message)s',
|
||||||
|
datefmt='%m/%d/%Y %I:%M:%S %p'
|
||||||
|
)
|
||||||
|
logging.info('using scan logging')
|
||||||
else:
|
else:
|
||||||
logging.basicConfig(filename=LOG_FILE, level=logging.INFO, format='%(asctime)s : %(levelname)s : %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
|
logging.basicConfig(
|
||||||
|
filename=LOG_FILE,
|
||||||
|
level=logging.INFO,
|
||||||
|
format='%(asctime)s : %(levelname)s : %(message)s',
|
||||||
|
datefmt='%m/%d/%Y %I:%M:%S %p'
|
||||||
|
)
|
||||||
logging.info("script started")
|
logging.info("script started")
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -83,9 +102,12 @@ 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):
|
||||||
if args.debug:
|
if args.debug:
|
||||||
print(msg)
|
print(msg)
|
||||||
|
|
||||||
|
|
||||||
def is_number(s):
|
def is_number(s):
|
||||||
"""Check if the input string is a number."""
|
"""Check if the input string is a number."""
|
||||||
try:
|
try:
|
||||||
@@ -95,7 +117,6 @@ def is_number(s):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_portainer_token(base_url, username=None, password=None, timeout=10):
|
def get_portainer_token(base_url, username=None, password=None, timeout=10):
|
||||||
"""
|
"""
|
||||||
Authenticate to Portainer and return a JWT token.
|
Authenticate to Portainer and return a JWT token.
|
||||||
@@ -114,6 +135,8 @@ def get_portainer_token(base_url, username=None, password=None, timeout=10):
|
|||||||
if not token:
|
if not token:
|
||||||
raise ValueError(f"No token found in response: {data}")
|
raise ValueError(f"No token found in response: {data}")
|
||||||
return token
|
return token
|
||||||
|
|
||||||
|
|
||||||
def prompt_missing_args(args, defaults, fields):
|
def prompt_missing_args(args, defaults, fields):
|
||||||
"""
|
"""
|
||||||
fields = [("arg_name", "Prompt text")]
|
fields = [("arg_name", "Prompt text")]
|
||||||
@@ -132,11 +155,27 @@ def prompt_missing_args(args, defaults, fields):
|
|||||||
setattr(args, field, value)
|
setattr(args, field, value)
|
||||||
|
|
||||||
return args
|
return args
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# Example usage: set PORTAINER_USER and PORTAINER_PASS in env, or pass literals below.
|
# Example usage: set PORTAINER_USER and PORTAINER_PASS in env, or pass literals below.
|
||||||
#token = get_portainer_token(base,"admin","l4c1j4yd33Du5lo") # or get_portainer_token(base, "admin", "secret")
|
# token = get_portainer_token(base,"admin","l4c1j4yd33Du5lo") # or get_portainer_token(base, "admin", "secret")
|
||||||
if args.action == None:
|
if args.action is None:
|
||||||
actions = ["delete_stack","create_stack","stop_stack","start_stack","list_stacks","update_stack","secrets","print_all_data","list_endpoints","list_containers","stop_containers","start_containers","refresh_environment","refresh_status","update_status"]
|
actions = ["delete_stack",
|
||||||
|
"create_stack",
|
||||||
|
"stop_stack",
|
||||||
|
"start_stack",
|
||||||
|
"list_stacks",
|
||||||
|
"update_stack",
|
||||||
|
"secrets",
|
||||||
|
"print_all_data",
|
||||||
|
"list_endpoints",
|
||||||
|
"list_containers",
|
||||||
|
"stop_containers",
|
||||||
|
"start_containers",
|
||||||
|
"refresh_environment",
|
||||||
|
"refresh_status",
|
||||||
|
"update_status"]
|
||||||
print("Possible actions: ")
|
print("Possible actions: ")
|
||||||
i = 1
|
i = 1
|
||||||
for a in actions:
|
for a in actions:
|
||||||
@@ -150,7 +189,7 @@ if __name__ == "__main__":
|
|||||||
por = Portainer(base, token)
|
por = Portainer(base, token)
|
||||||
|
|
||||||
if args.action == "secrets":
|
if args.action == "secrets":
|
||||||
if args.endpoint_id == None:
|
if args.endpoint_id is None:
|
||||||
args.endpoint_id = input("Endpoint ID is required for creating secrets : ")
|
args.endpoint_id = input("Endpoint ID is required for creating secrets : ")
|
||||||
|
|
||||||
secrets = {
|
secrets = {
|
||||||
@@ -182,41 +221,39 @@ if __name__ == "__main__":
|
|||||||
("stack_mode", "Stack mode (swarm or compose)"),
|
("stack_mode", "Stack mode (swarm or compose)"),
|
||||||
("deploy_mode", "Deploy mode (git or upload)")
|
("deploy_mode", "Deploy mode (git or upload)")
|
||||||
])
|
])
|
||||||
|
por.create_stack(args.endpoint_id, args.stack, args.deploy_mode, args.autostart, args.stack_mode)
|
||||||
|
|
||||||
por.create_stack(args.endpoint_id,args.stack, args.deploy_mode, args.autostart, args.stack_mode)
|
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
if args.action == "stop_stack":
|
if args.action == "stop_stack":
|
||||||
if args.endpoint_id == None:
|
if args.endpoint_id is None:
|
||||||
args.endpoint_id = input("Endpoint ID is required for stopping stacks : ")
|
args.endpoint_id = input("Endpoint ID is required for stopping stacks : ")
|
||||||
if args.stack == None:
|
if args.stack is None:
|
||||||
args.stack = input("Stack name or ID is required for stopping stacks : ")
|
args.stack = input("Stack name or ID is required for stopping stacks : ")
|
||||||
por.stop_stack(args.stack,args.endpoint_id)
|
por.stop_stack(args.stack, args.endpoint_id)
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
if args.action == "start_stack":
|
if args.action == "start_stack":
|
||||||
if args.endpoint_id == None:
|
if args.endpoint_id is None:
|
||||||
args.endpoint_id = input("Endpoint ID is required for starting stacks : ")
|
args.endpoint_id = input("Endpoint ID is required for starting stacks : ")
|
||||||
if args.stack == None:
|
if args.stack is None:
|
||||||
args.stack = input("Stack name or ID is required for starting stacks : ")
|
args.stack = input("Stack name or ID is required for starting stacks : ")
|
||||||
por.start_stack(args.stack, args.endpoint_id)
|
por.start_stack(args.stack, args.endpoint_id)
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
if args.action == "list_stacks":
|
if args.action == "list_stacks":
|
||||||
por.print_stacks(args.endpoint_id)
|
por.print_stacks(args.endpoint_id)
|
||||||
print(json.dumps(por.all_data,indent=2))
|
print(json.dumps(por.all_data, indent=2))
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
if args.action == "list_containers":
|
if args.action == "list_containers":
|
||||||
print("Getting containers")
|
print("Getting containers")
|
||||||
por.get_containers(args.endpoint_id,args.stack)
|
por.get_containers(args.endpoint_id, args.stack)
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
if args.action == "update_stack":
|
if args.action == "update_stack":
|
||||||
print("Updating stacks")
|
print("Updating stacks")
|
||||||
autostart=True if args.autostart else False
|
autostart = True if args.autostart else False
|
||||||
por.update_stack(args.endpoint_id,args.stack,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))
|
||||||
|
|||||||
Reference in New Issue
Block a user