Compare commits

..

28 Commits

Author SHA1 Message Date
d215dd2615 build 2025-12-17 00:23:14 +01:00
0cbbb443f2 build 2025-12-17 00:10:12 +01:00
91702d8f4a build 2025-12-17 00:08:22 +01:00
4884ba41c4 build 2025-12-17 00:07:18 +01:00
ec47d331d7 build 2025-12-17 00:03:46 +01:00
cc6dc31409 build 2025-12-17 00:01:37 +01:00
609da77e77 build 2025-12-16 23:57:44 +01:00
17daad941e build 2025-12-16 23:55:58 +01:00
cd32ad1d3e build 2025-12-16 23:51:41 +01:00
45a0b0030c build 2025-12-16 23:49:36 +01:00
5d0b488b87 build 2025-12-16 23:47:04 +01:00
5df457bdcb build 2025-12-16 23:43:23 +01:00
8f207f2fad build 2025-12-16 23:41:19 +01:00
c0682d478d build 2025-12-16 23:35:45 +01:00
db1710e065 build 2025-12-16 23:34:22 +01:00
7bac5e84c8 build 2025-12-16 23:31:42 +01:00
3a0117c2a5 build 2025-12-16 23:29:18 +01:00
8b916572cb build 2025-12-16 23:28:03 +01:00
498b88c7ee build 2025-12-16 23:26:43 +01:00
497a1f7947 build 2025-12-16 23:23:58 +01:00
928e4daae6 build 2025-12-16 23:20:52 +01:00
926248fda7 build 2025-12-16 22:44:49 +01:00
ab9e93effe build 2025-12-16 21:28:23 +01:00
fdbf41a819 build 2025-12-16 11:08:44 +01:00
958f050691 build 2025-12-15 21:03:38 +01:00
1dde729887 build 2025-12-15 13:18:45 +01:00
d7f202e2b8 build 2025-12-15 08:35:41 +01:00
fe2f9ddd4b build 2025-12-15 08:30:57 +01:00
2 changed files with 96 additions and 51 deletions

116
port.py
View File

@@ -161,6 +161,7 @@ class Portainer:
data=payload, data=payload,
headers={"X-Gotify-Key": "ASn_fIAd5OVjm8c"} headers={"X-Gotify-Key": "ASn_fIAd5OVjm8c"}
) )
logger.debug(response.text)
# print("Status:", response.status_code) # print("Status:", response.status_code)
# print("Response:", response.text) # print("Response:", response.text)
pass pass
@@ -335,7 +336,6 @@ class Portainer:
return endpoint return endpoint
def refresh_in_containers(self): def refresh_in_containers(self):
print("Refreshing containers")
'''Get a list of containers for a specific endpoint and stack.''' '''Get a list of containers for a specific endpoint and stack.'''
# print(json.dumps(self.all_data,indent=2)) # print(json.dumps(self.all_data,indent=2))
# print(endpoint) # print(endpoint)
@@ -474,38 +474,62 @@ class Portainer:
with ThreadPoolExecutor(max_workers=10) as exe: with ThreadPoolExecutor(max_workers=10) as exe:
exe.map(stop, containers) exe.map(stop, containers)
def update_stack(self, endpoint, stack, autostart, timeout=130): def update_stack(self, args):
'''Update one stack or all stacks on an endpoint.''' '''Update one stack or all stacks on an endpoint.'''
stcs = [] #print("Updating stacks")
if stack == "all": stacks = self.get_stacks(endpoint_id=args.endpoint_id)
for s in self.all_data["webhooks"][endpoint]: stacks_tuples = []
stcs.append([s, self.all_data["webhooks"][endpoint][s]])
else: for s in stacks:
#print(s)
try: try:
stcs.append([stack, self.all_data["webhooks"][endpoint][stack]]) stacks_tuples.append((s['AutoUpdate']['Webhook'],s['Name']))
except Exception as e: # print(s['Name'], " : ", s['AutoUpdate']['Webhook'])
print(f"Error: Stack {stack} not found on endpoint {endpoint}: {e}") except:
stacks_tuples.append((s['Webhook'],s['Name']))
# print(s['Name'], " : ", s['Webhook'])
stacks_dict = dict(stacks_tuples)
print(stacks_dict)
#input(stacks_tuples)
# stacks_tuples = [(s['AutoUpdate']['Webhook'], s['Name']) for s in stacks if "Webhook" in s['AutoUpdate'] ]
# input(stcs)
def update(c): def update(c):
print(f" > Updating {c[0]} on {endpoint}") print(f" > Updating {c[1]} ")
ans = self._api_post_no_body(f"/stacks/webhooks/{c[1]}") ans = self._api_post_no_body(f"/stacks/webhooks/{c[0]}")
logger.debug( logger.debug(
f"Update response for stack {c[0]} on endpoint {endpoint}: {ans}" f"Update response for stack {c[0]} on endpoint {ans}"
) )
# input(stacks_tuples)
if args.debug:
input(args)
stacks_tuples = sorted(stacks_tuples, key=lambda x: x[1])
stack_dict = dict(stacks_tuples)
# input(service_tuples)
if self.args.service_id is None:
#services = [(s["Id"], s["Name"]) for s in self.get_stacks(endpoint_id)]
stacks_tuples.insert(0, ("__ALL__", "[Select ALL]"))
stack_ids = checkboxlist_dialog(
title="Select one stack to update",
text="Choose a service:",
values=stacks_tuples
).run()
stcs = []
input(stack_ids)
def stop(): if args.stack == "all":
cont = [] for s in stack_dict:
for c in self.all_data["containers"][endpoint]: stcs.append([s, stack_dict[s]])
if stack == c or stack == "all": else:
cont += self.all_data["containers"][endpoint][c] for s in stack_dict:
self.stop_containers(endpoint, cont) if s in stack_ids:
stcs.append([s, stack_dict[s]])
print(stcs)
with ThreadPoolExecutor(max_workers=10) as exe: with ThreadPoolExecutor(max_workers=10) as exe:
exe.map(update, stcs) list(exe.map(update, stcs))
if not autostart: input('UPDATED')
if not args.autostart:
time.sleep(120) time.sleep(120)
cont = [] cont = []
for c in self.all_data["containers"][endpoint]: for c in self.all_data["containers"][endpoint]:
@@ -749,7 +773,7 @@ class Portainer:
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()
# print(conts) # print(conts)
self.stop_containers(self.endpoint_name, conts) self.stop_containers(self.endpoint_name, conts)
@@ -817,7 +841,7 @@ class Portainer:
data = [] data = []
stack_names = [] stack_names = []
for stack in stacks: for stack in stacks:
print(stack) # print(stack)
if endpoint is not 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
@@ -838,6 +862,7 @@ class Portainer:
logger.debug( logger.debug(
"KeyError getting endpoint name for stack %s : %s", stack["Name"], e "KeyError getting endpoint name for stack %s : %s", stack["Name"], e
) )
count += 1
data = sorted(data, key=lambda x: x[1]) data = sorted(data, key=lambda x: x[1])
headers = ["StackID", "Name", "Endpoint"] headers = ["StackID", "Name", "Endpoint"]
@@ -862,15 +887,21 @@ class Portainer:
values=service_tuples values=service_tuples
).run() ).run()
elif self.args.service_id == "all": elif self.args.service_id == "all":
service_ids = [s[0] for s in service_tuples if s[0] != "__ALL__" and s[0] != "__ONLY_CHECK__"] service_ids = [s[0] for s in service_tuples if s[0] != "__ALL__" ]
else: else:
service_ids = [self.args.service_id] service_ids = [self.args.service_id]
if "__ONLY_CHECK__" in service_ids or self.args.update is False:
if self.args.update is False:
if "__ONLY_CHECK__" in service_ids:
service_ids.remove("__ONLY_CHECK__")
pull = False pull = False
print("Checking for updates only...") print("Checking for updates only...")
else: else:
print("Checking for updates and pulling updates...")
pull = True pull = True
print("Checking for updates and pulling updates...")
else:
pull = True
print("Checking for updates and pulling updates...")
if "__ALL__" in service_ids: if "__ALL__" in service_ids:
service_ids = [s[0] for s in service_tuples if s[0] != "__ALL__" and s[0] != "__ONLY_CHECK__"] service_ids = [s[0] for s in service_tuples if s[0] != "__ALL__" and s[0] != "__ONLY_CHECK__"]
@@ -899,7 +930,7 @@ class Portainer:
print("?") print("?")
elif resp['Status'] == "outdated": elif resp['Status'] == "outdated":
if pull: if pull:
print("Recreate")
self.recreate_container(service_id, pull) self.recreate_container(service_id, pull)
#print(f"Service {service_dict[service_id]:<{longest}} : updated") #print(f"Service {service_dict[service_id]:<{longest}} : updated")
self.gotify_message(f"Service {service_dict[service_id]} updated") self.gotify_message(f"Service {service_dict[service_id]} updated")
@@ -939,21 +970,30 @@ class Portainer:
text="Choose a service:", text="Choose a service:",
values=service_tuples values=service_tuples
).run() ).run()
if "__ONLY_CHECK__" in service_ids:
self.args.update = False
else:
self.args.update = True
if "__ALL__" in service_ids:
service_ids = [s[0] for s in service_tuples if s[0] != "__ALL__" and s[0] != "__ONLY_CHECK__"]
elif self.args.service_id == "all": elif self.args.service_id == "all":
service_ids = [s[0] for s in service_tuples if s[0] != "__ALL__" and s[0] != "__ONLY_CHECK__"] service_ids = [s[0] for s in service_tuples if s[0] != "__ALL__" and s[0] != "__ONLY_CHECK__"]
else: else:
service_ids = [self.args.service_id] service_ids = [self.args.service_id]
if "__ONLY_CHECK__" in service_ids or self.args.update is False:
if self.args.update:
pull = True
print("Checking for updates and pulling updates...")
else:
pull = False pull = False
print("Checking for updates only...") print("Checking for updates only...")
else:
print("Checking for updates and pulling updates...")
pull = True
if "__ALL__" in service_ids:
service_ids = [s[0] for s in service_tuples if s[0] != "__ALL__" and s[0] != "__ONLY_CHECK__"]
longest = 0 longest = 0
for a in service_dict.items(): for a in service_dict.items():
if a[0] == "__ONLY_CHECK__":
continue
# print(a[1]) # print(a[1])
if len(a[1]) > longest: if len(a[1]) > longest:
longest = len(a[1]) longest = len(a[1])
@@ -1053,12 +1093,12 @@ class Portainer:
def recreate_container(self,service_id, pull=False): def recreate_container(self,service_id, pull=False):
"""Restart a service on an endpoint.""" """Restart a service on an endpoint."""
path = f"/endpoints/{self.endpoint_id}/containers/{service_id}/recreate" path = f"/docker/{self.endpoint_id}/containers/{service_id}/recreate"
print(path) print(path)
params={"pullImage": pull} params={"pullImage": pull}
try: try:
resp = self._api_post(path, json=params, timeout=20) resp = self._api_post(path, json=params, timeout=20)
print(resp) #print(resp)
except ValueError as e: except ValueError as e:
print(f"Error restarting service: {e}") print(f"Error restarting service: {e}")
return [] return []
@@ -1069,7 +1109,7 @@ class Portainer:
params={"serviceID": service_id, "pullImage": pool} params={"serviceID": service_id, "pullImage": pool}
try: try:
resp = self._api_put(path, json=params, timeout=20) resp = self._api_put(path, json=params, timeout=20)
print(resp) # print(resp)
except ValueError as e: except ValueError as e:
print(f"Error restarting service: {e}") print(f"Error restarting service: {e}")
return [] return []

View File

@@ -39,7 +39,7 @@ else:
raise Exception("Failed to authenticate with Vault") raise Exception("Failed to authenticate with Vault")
# Specify the mount point of your KV engine # Specify the mount point of your KV engine
VERSION = "0.1.14" VERSION = "0.1.16"
defaults = { defaults = {
"endpoint_id": "vm01", "endpoint_id": "vm01",
@@ -163,7 +163,7 @@ update_configs(cur_config)
if args.debug: if args.debug:
input(cur_config) input(cur_config)
_LOG_LEVEL = "INFO" _LOG_LEVEL = "DEBUG"
LOG_FILE = "/tmp/portainer.log" LOG_FILE = "/tmp/portainer.log"
if _LOG_LEVEL == "DEBUG": if _LOG_LEVEL == "DEBUG":
logging.basicConfig( logging.basicConfig(
@@ -475,8 +475,8 @@ if __name__ == "__main__":
if args.action == "create_stack": if args.action == "create_stack":
por.action = "create_stack" por.action = "create_stack"
print(cur_config) #print(cur_config)
print(args) #print(args)
args = prompt_missing_args( args = prompt_missing_args(
args, args,
cur_config, cur_config,
@@ -538,9 +538,6 @@ if __name__ == "__main__":
sys.exit() sys.exit()
if args.action == "update_service": if args.action == "update_service":
args = prompt_missing_args( args = prompt_missing_args(
args, args,
cur_config, cur_config,
@@ -576,7 +573,7 @@ if __name__ == "__main__":
("endpoint_id", "Endpoint ID"), ("endpoint_id", "Endpoint ID"),
], ],
) )
por.print_stacks(args.endpoint_id) por.print_stacks(args)
# print(json.dumps(por.all_data, indent=2)) # print(json.dumps(por.all_data, indent=2))
sys.exit() sys.exit()
@@ -585,11 +582,19 @@ if __name__ == "__main__":
print(por.get_containers()) print(por.get_containers())
sys.exit() sys.exit()
if args.action == "update_stack": if args.action == "update_stack":
print("Updating stacks") args = prompt_missing_args(
por.update_stack(args.endpoint_id, args.stack, args.autostart) args,
cur_config,
[
("site", "Site"),
("endpoint_id", "Endpoint ID")
],
)
por.update_stack(args)
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))
sys.exit() sys.exit()