Compare commits

...

10 Commits

Author SHA1 Message Date
f561508d2e build 2025-12-21 00:52:21 +01:00
974966fdd8 build 2025-12-21 00:49:24 +01:00
63e158899e build 2025-12-20 15:22:59 +01:00
9336b56f96 build 2025-12-20 15:22:40 +01:00
66fba7b994 build 2025-12-20 15:20:29 +01:00
7804dbb117 build 2025-12-20 15:19:00 +01:00
fb1763e14d build 2025-12-20 15:16:40 +01:00
829891d1ba build 2025-12-20 15:13:14 +01:00
174aab4faa build 2025-12-19 17:23:20 +01:00
9c6445ee03 build 2025-12-19 17:08:15 +01:00
2 changed files with 67 additions and 49 deletions

83
port.py
View File

@@ -315,6 +315,7 @@ class Portainer:
def get_endpoint_id(self):
'''Get endpoint ID from either ID or name input.'''
# input(self.args.endpoint_id)
if self._is_number(self.args.endpoint_id):
self.endpoint_id = self.args.endpoint_id
self.endpoint_name = self.endpoints["by_id"][self.args.endpoint_id]
@@ -394,14 +395,13 @@ class Portainer:
else:
eps = [self.get_endpoint_id()]
#input(eps)
for endpoint in eps:
# print(s)
for endpoint in eps:
#print(self.args.stack)
if self.args.stack in ["all", None]:
# input([id for id in self.all_data["stacks"][endpoint]['by_id'].keys()])
for s in [id for id in self.all_data["stacks"][endpoint]['by_id'].keys()]:
for e in [id for id in self.all_data["stacks"][endpoint]['by_name'].keys()]:
#input(e)
# if s not in self.all_data["stacks"]:
# continue
#input(self.all_data)
@@ -409,36 +409,36 @@ class Portainer:
# print(f"Endpoint {self.all_data["endpoints"]["by_id"][s]} is offline")
continue
# input(self.all_data["stacks"][endpoint]["by_name"])
for e in self.all_data["stacks"][endpoint]["by_name"]:
#input(e)
path = (
f"/endpoints/{endpoint}/docker/containers/json"
f'?all=1&filters={{"label": ["com.docker.compose.project={e}"]}}'
#input(e)
path = (
f"/endpoints/{endpoint}/docker/containers/json"
f'?all=1&filters={{"label": ["com.docker.compose.project={e}"]}}'
)
logging.info(f"request : {path}")
try:
containers = self._api_get(path)
#input(containers)
except Exception as e:
print(f"failed to get containers from {path}: {e}")
continue
contr = []
try:
for c in containers:
# input(c)
cont.append(c["Names"][0].replace("/", ""))
contr.append(c["Names"][0].replace("/", ""))
if self.all_data["endpoints"]["by_id"][endpoint] in data:
data[self.all_data["endpoints"]["by_id"][endpoint]][e] = contr
else:
data[self.all_data["endpoints"]["by_id"][endpoint]] = {
e: contr
}
except Exception as e:
logger.debug(
f"Exception while getting containers for stack {e} ",
f"on endpoint {self.all_data['endpoints']['by_id'][endpoint]}: {e}",
)
logging.info(f"request : {path}")
try:
containers = self._api_get(path)
#input(containers)
except Exception as e:
print(f"failed to get containers from {path}: {e}")
continue
contr = []
try:
for c in containers:
# input(c)
cont.append(c["Names"][0].replace("/", ""))
contr.append(c["Names"][0].replace("/", ""))
if self.all_data["endpoints"]["by_id"][endpoint] in data:
data[self.all_data["endpoints"]["by_id"][endpoint]][e] = contr
else:
data[self.all_data["endpoints"]["by_id"][endpoint]] = {
e: contr
}
except Exception as e:
logger.debug(
f"Exception while getting containers for stack {e} ",
f"on endpoint {self.all_data['endpoints']['by_id'][endpoint]}: {e}",
)
self.all_data["containers"] = data
@@ -834,7 +834,7 @@ class Portainer:
}
self._api_post_file(path, self.endpoint_id, stack, envs, file)
def print_stacks(self, endpoint="all"):
def print_stacks(self, args):
"""Print a table of stacks, optionally filtered by endpoint."""
stacks = self.get_stacks()
count = 0
@@ -842,11 +842,11 @@ class Portainer:
stack_names = []
for stack in stacks:
# print(stack)
if endpoint is not None:
if args.endpoint_id is not None:
if not stack["EndpointId"] in self.endpoints["by_id"]:
continue
if endpoint != "all":
if self.endpoints["by_name"][endpoint] != stack["EndpointId"]:
if args.endpoint_id != "all":
if self.endpoints["by_name"][args.endpoint_id] != stack["EndpointId"]:
continue
try:
stack_names.append(stack["Name"])
@@ -868,12 +868,13 @@ class Portainer:
headers = ["StackID", "Name", "Endpoint"]
print(tabulate.tabulate(data, headers=headers, tablefmt="github"))
print(f"Total stacks: {count}")
input("Continue...")
# print(sorted(stack_names))
def update_containers(self):
all_containers = self.all_data["containers"][self.args.endpoint_id]
#input(all_containers)
service_tuples = [(s[1], s[0]) for s in all_containers if "." not in s[0]]
service_tuples = [(s[1], s[0]) for s in all_containers if "." not in s[0] and not s[0].startswith("runner-")]
service_tuples = sorted(service_tuples, key=lambda x: x[1])
service_dict = dict(service_tuples)
# input(service_tuples)
@@ -930,7 +931,7 @@ class Portainer:
print("?")
elif resp['Status'] == "outdated":
if pull:
print("Recreate")
#print("Recreate")
self.recreate_container(service_id, pull)
#print(f"Service {service_dict[service_id]:<{longest}} : updated")
self.gotify_message(f"Service {service_dict[service_id]} updated")
@@ -1016,7 +1017,7 @@ class Portainer:
self.restart_srv(service_id, pull)
#print(f"Service {service_dict[service_id]:<{longest}} : updated")
self.gotify_message(f"Service {service_dict[service_id]} updated")
print(ok)
print(f"{ok} updated")
else:
print(f"\r\033[4m{service_dict[service_id]:<{longest}}\033[0m ", end="", flush=True)
#print(f"\033[4m{service_dict[service_id]:<{longest}} {err}\033[0m")
@@ -1094,7 +1095,7 @@ class Portainer:
def recreate_container(self,service_id, pull=False):
"""Restart a service on an endpoint."""
path = f"/docker/{self.endpoint_id}/containers/{service_id}/recreate"
print(path)
# print(path)
params={"pullImage": pull}
try:
resp = self._api_post(path, json=params, timeout=20)

View File

@@ -39,7 +39,7 @@ else:
raise Exception("Failed to authenticate with Vault")
# Specify the mount point of your KV engine
VERSION = "0.1.16"
VERSION = "0.1.19"
defaults = {
"endpoint_id": "vm01",
@@ -83,10 +83,6 @@ def load_config(defaults=defaults):
print("Configuration written to /myapps/portainer.conf")
return cur_config
a = load_config(defaults)
# ENV_VARS = [
@@ -109,7 +105,11 @@ def update_configs(cur_config):
print("Configuration written to /myapps/portainer.conf")
parser = argparse.ArgumentParser(
description="Portainer helper - use env vars or pass credentials."
description=f"""\
Portainer helper - use env vars or pass credentials."
version: {VERSION}
""",
formatter_class=argparse.RawTextHelpFormatter,
)
parser.add_argument(
"--base",
@@ -372,7 +372,7 @@ def prompt_missing_args(args_in, defaults_in, fields, action=None,stacks=None):
if __name__ == "__main__":
# 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")
def signal_handler(sig, frame):
logger.warning("Killed manually %s, %s", sig, frame)
print("\nTerminated by user")
@@ -579,7 +579,15 @@ if __name__ == "__main__":
if args.action == "list_containers":
print("Getting containers")
print(por.get_containers())
args = prompt_missing_args(
args,
cur_config,
[
("site", "Site"),
("endpoint_id", "Endpoint ID"),
],
)
print("\n".join(por.get_containers()))
sys.exit()
if args.action == "update_stack":
@@ -613,6 +621,15 @@ if __name__ == "__main__":
sys.exit()
if args.action == "stop_containers":
# TODO: does not work
args = prompt_missing_args(
args,
cur_config,
[
("site", "Site"),
("endpoint_id", "Endpoint ID"),
],
)
if por.all_data["endpoints_status"][args.endpoint_id] != 1:
print(f"Endpoint {por.get_endpoint_name(args.endpoint_id)} is offline")
sys.exit()