container-manager-mcp 0.0.11__py3-none-any.whl → 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- container_manager_mcp/container_manager.py +508 -524
- container_manager_mcp/container_manager_mcp.py +110 -88
- {container_manager_mcp-0.0.11.dist-info → container_manager_mcp-1.0.0.dist-info}/METADATA +2 -2
- container_manager_mcp-1.0.0.dist-info/RECORD +10 -0
- container_manager_mcp-0.0.11.dist-info/RECORD +0 -10
- {container_manager_mcp-0.0.11.dist-info → container_manager_mcp-1.0.0.dist-info}/WHEEL +0 -0
- {container_manager_mcp-0.0.11.dist-info → container_manager_mcp-1.0.0.dist-info}/entry_points.txt +0 -0
- {container_manager_mcp-0.0.11.dist-info → container_manager_mcp-1.0.0.dist-info}/licenses/LICENSE +0 -0
- {container_manager_mcp-0.0.11.dist-info → container_manager_mcp-1.0.0.dist-info}/top_level.txt +0 -0
@@ -6,10 +6,12 @@ import logging
|
|
6
6
|
import os
|
7
7
|
from abc import ABC, abstractmethod
|
8
8
|
from typing import List, Dict, Optional, Any
|
9
|
-
import
|
9
|
+
import argparse
|
10
10
|
import json
|
11
11
|
import subprocess
|
12
|
-
from datetime import datetime
|
12
|
+
from datetime import datetime
|
13
|
+
import dateutil.parser
|
14
|
+
import platform
|
13
15
|
|
14
16
|
try:
|
15
17
|
import docker
|
@@ -27,6 +29,7 @@ except ImportError:
|
|
27
29
|
|
28
30
|
|
29
31
|
class ContainerManagerBase(ABC):
|
32
|
+
|
30
33
|
def __init__(self, silent: bool = False, log_file: str = None):
|
31
34
|
self.silent = silent
|
32
35
|
self.setup_logging(log_file)
|
@@ -66,6 +69,20 @@ class ContainerManagerBase(ABC):
|
|
66
69
|
size_bytes /= 1024.0
|
67
70
|
return f"{size_bytes:.2f}PB"
|
68
71
|
|
72
|
+
def _parse_timestamp(self, timestamp: Any) -> str:
|
73
|
+
"""Parse timestamp (integer or string) to ISO 8601 string."""
|
74
|
+
if not timestamp:
|
75
|
+
return "unknown"
|
76
|
+
if isinstance(timestamp, (int, float)):
|
77
|
+
return datetime.fromtimestamp(timestamp).strftime("%Y-%m-%dT%H:%M:%S")
|
78
|
+
if isinstance(timestamp, str):
|
79
|
+
try:
|
80
|
+
parsed = dateutil.parser.isoparse(timestamp)
|
81
|
+
return parsed.strftime("%Y-%m-%dT%H:%M:%S")
|
82
|
+
except ValueError:
|
83
|
+
return "unknown"
|
84
|
+
return "unknown"
|
85
|
+
|
69
86
|
@abstractmethod
|
70
87
|
def get_version(self) -> Dict:
|
71
88
|
pass
|
@@ -208,42 +225,6 @@ class DockerManager(ContainerManagerBase):
|
|
208
225
|
self.logger.error(f"Failed to connect to Docker daemon: {str(e)}")
|
209
226
|
raise RuntimeError(f"Failed to connect to Docker: {str(e)}")
|
210
227
|
|
211
|
-
def get_version(self) -> Dict:
|
212
|
-
params = {}
|
213
|
-
try:
|
214
|
-
version = self.client.version()
|
215
|
-
result = {
|
216
|
-
"version": version.get("Version", "unknown"),
|
217
|
-
"api_version": version.get("ApiVersion", "unknown"),
|
218
|
-
"os": version.get("Os", "unknown"),
|
219
|
-
"arch": version.get("Arch", "unknown"),
|
220
|
-
"build_time": version.get("BuildTime", "unknown"),
|
221
|
-
}
|
222
|
-
self.log_action("get_version", params, result)
|
223
|
-
return result
|
224
|
-
except Exception as e:
|
225
|
-
self.log_action("get_version", params, error=e)
|
226
|
-
raise RuntimeError(f"Failed to get version: {str(e)}")
|
227
|
-
|
228
|
-
def get_info(self) -> Dict:
|
229
|
-
params = {}
|
230
|
-
try:
|
231
|
-
info = self.client.info()
|
232
|
-
result = {
|
233
|
-
"containers_total": info.get("Containers", 0),
|
234
|
-
"containers_running": info.get("ContainersRunning", 0),
|
235
|
-
"images": info.get("Images", 0),
|
236
|
-
"driver": info.get("Driver", "unknown"),
|
237
|
-
"platform": f"{info.get('OperatingSystem', 'unknown')} {info.get('Architecture', 'unknown')}",
|
238
|
-
"memory_total": self._format_size(info.get("MemTotal", 0)),
|
239
|
-
"swap_total": self._format_size(info.get("SwapTotal", 0)),
|
240
|
-
}
|
241
|
-
self.log_action("get_info", params, result)
|
242
|
-
return result
|
243
|
-
except Exception as e:
|
244
|
-
self.log_action("get_info", params, error=e)
|
245
|
-
raise RuntimeError(f"Failed to get info: {str(e)}")
|
246
|
-
|
247
228
|
def list_images(self) -> List[Dict]:
|
248
229
|
params = {}
|
249
230
|
try:
|
@@ -257,12 +238,8 @@ class DockerManager(ContainerManagerBase):
|
|
257
238
|
repo_tag.rsplit(":", 1) if ":" in repo_tag else ("<none>", "<none>")
|
258
239
|
)
|
259
240
|
|
260
|
-
created = attrs.get("Created",
|
261
|
-
created_str = (
|
262
|
-
datetime.fromtimestamp(created).strftime("%Y-%m-%dT%H:%M:%S")
|
263
|
-
if created
|
264
|
-
else "unknown"
|
265
|
-
)
|
241
|
+
created = attrs.get("Created", None)
|
242
|
+
created_str = self._parse_timestamp(created)
|
266
243
|
|
267
244
|
size_bytes = attrs.get("Size", 0)
|
268
245
|
size_str = self._format_size(size_bytes) if size_bytes else "0B"
|
@@ -298,12 +275,8 @@ class DockerManager(ContainerManagerBase):
|
|
298
275
|
repository, tag = (
|
299
276
|
repo_tag.rsplit(":", 1) if ":" in repo_tag else (image, tag)
|
300
277
|
)
|
301
|
-
created = attrs.get("Created",
|
302
|
-
created_str = (
|
303
|
-
datetime.fromtimestamp(created).strftime("%Y-%m-%dT%H:%M:%S")
|
304
|
-
if created
|
305
|
-
else "unknown"
|
306
|
-
)
|
278
|
+
created = attrs.get("Created", None)
|
279
|
+
created_str = self._parse_timestamp(created)
|
307
280
|
size_bytes = attrs.get("Size", 0)
|
308
281
|
size_str = self._format_size(size_bytes) if size_bytes else "0B"
|
309
282
|
result = {
|
@@ -321,17 +294,6 @@ class DockerManager(ContainerManagerBase):
|
|
321
294
|
self.log_action("pull_image", params, error=e)
|
322
295
|
raise RuntimeError(f"Failed to pull image: {str(e)}")
|
323
296
|
|
324
|
-
def remove_image(self, image: str, force: bool = False) -> Dict:
|
325
|
-
params = {"image": image, "force": force}
|
326
|
-
try:
|
327
|
-
self.client.images.remove(image, force=force)
|
328
|
-
result = {"removed": image}
|
329
|
-
self.log_action("remove_image", params, result)
|
330
|
-
return result
|
331
|
-
except Exception as e:
|
332
|
-
self.log_action("remove_image", params, error=e)
|
333
|
-
raise RuntimeError(f"Failed to remove image: {str(e)}")
|
334
|
-
|
335
297
|
def list_containers(self, all: bool = False) -> List[Dict]:
|
336
298
|
params = {"all": all}
|
337
299
|
try:
|
@@ -347,12 +309,8 @@ class DockerManager(ContainerManagerBase):
|
|
347
309
|
port_mappings.append(
|
348
310
|
f"{hp.get('HostIp', '0.0.0.0')}:{hp.get('HostPort')}->{container_port}"
|
349
311
|
)
|
350
|
-
created = attrs.get("Created",
|
351
|
-
created_str = (
|
352
|
-
datetime.fromtimestamp(created).strftime("%Y-%m-%dT%H:%M:%S")
|
353
|
-
if created
|
354
|
-
else "unknown"
|
355
|
-
)
|
312
|
+
created = attrs.get("Created", None)
|
313
|
+
created_str = self._parse_timestamp(created)
|
356
314
|
simplified = {
|
357
315
|
"id": attrs.get("Id", "unknown")[7:19],
|
358
316
|
"image": attrs.get("Config", {}).get("Image", "unknown"),
|
@@ -410,12 +368,8 @@ class DockerManager(ContainerManagerBase):
|
|
410
368
|
port_mappings.append(
|
411
369
|
f"{hp.get('HostIp', '0.0.0.0')}:{hp.get('HostPort')}->{container_port}"
|
412
370
|
)
|
413
|
-
created = attrs.get("Created",
|
414
|
-
created_str = (
|
415
|
-
datetime.fromtimestamp(created).strftime("%Y-%m-%dT%H:%M:%S")
|
416
|
-
if created
|
417
|
-
else "unknown"
|
418
|
-
)
|
371
|
+
created = attrs.get("Created", None)
|
372
|
+
created_str = self._parse_timestamp(created)
|
419
373
|
result = {
|
420
374
|
"id": attrs.get("Id", "unknown")[7:19],
|
421
375
|
"image": attrs.get("Config", {}).get("Image", image),
|
@@ -430,6 +384,98 @@ class DockerManager(ContainerManagerBase):
|
|
430
384
|
self.log_action("run_container", params, error=e)
|
431
385
|
raise RuntimeError(f"Failed to run container: {str(e)}")
|
432
386
|
|
387
|
+
def list_networks(self) -> List[Dict]:
|
388
|
+
params = {}
|
389
|
+
try:
|
390
|
+
networks = self.client.networks.list()
|
391
|
+
result = []
|
392
|
+
for net in networks:
|
393
|
+
attrs = net.attrs
|
394
|
+
containers = len(attrs.get("Containers", {}))
|
395
|
+
created = attrs.get("Created", None)
|
396
|
+
created_str = self._parse_timestamp(created)
|
397
|
+
simplified = {
|
398
|
+
"id": attrs.get("Id", "unknown")[7:19],
|
399
|
+
"name": attrs.get("Name", "unknown"),
|
400
|
+
"driver": attrs.get("Driver", "unknown"),
|
401
|
+
"scope": attrs.get("Scope", "unknown"),
|
402
|
+
"containers": containers,
|
403
|
+
"created": created_str,
|
404
|
+
}
|
405
|
+
result.append(simplified)
|
406
|
+
self.log_action("list_networks", params, result)
|
407
|
+
return result
|
408
|
+
except Exception as e:
|
409
|
+
self.log_action("list_networks", params, error=e)
|
410
|
+
raise RuntimeError(f"Failed to list networks: {str(e)}")
|
411
|
+
|
412
|
+
def create_network(self, name: str, driver: str = "bridge") -> Dict:
|
413
|
+
params = {"name": name, "driver": driver}
|
414
|
+
try:
|
415
|
+
network = self.client.networks.create(name, driver=driver)
|
416
|
+
attrs = network.attrs
|
417
|
+
created = attrs.get("Created", None)
|
418
|
+
created_str = self._parse_timestamp(created)
|
419
|
+
result = {
|
420
|
+
"id": attrs.get("Id", "unknown")[7:19],
|
421
|
+
"name": attrs.get("Name", name),
|
422
|
+
"driver": attrs.get("Driver", driver),
|
423
|
+
"scope": attrs.get("Scope", "unknown"),
|
424
|
+
"created": created_str,
|
425
|
+
}
|
426
|
+
self.log_action("create_network", params, result)
|
427
|
+
return result
|
428
|
+
except Exception as e:
|
429
|
+
self.log_action("create_network", params, error=e)
|
430
|
+
raise RuntimeError(f"Failed to create network: {str(e)}")
|
431
|
+
|
432
|
+
def get_version(self) -> Dict:
|
433
|
+
params = {}
|
434
|
+
try:
|
435
|
+
version = self.client.version()
|
436
|
+
result = {
|
437
|
+
"version": version.get("Version", "unknown"),
|
438
|
+
"api_version": version.get("ApiVersion", "unknown"),
|
439
|
+
"os": version.get("Os", "unknown"),
|
440
|
+
"arch": version.get("Arch", "unknown"),
|
441
|
+
"build_time": version.get("BuildTime", "unknown"),
|
442
|
+
}
|
443
|
+
self.log_action("get_version", params, result)
|
444
|
+
return result
|
445
|
+
except Exception as e:
|
446
|
+
self.log_action("get_version", params, error=e)
|
447
|
+
raise RuntimeError(f"Failed to get version: {str(e)}")
|
448
|
+
|
449
|
+
def get_info(self) -> Dict:
|
450
|
+
params = {}
|
451
|
+
try:
|
452
|
+
info = self.client.info()
|
453
|
+
result = {
|
454
|
+
"containers_total": info.get("Containers", 0),
|
455
|
+
"containers_running": info.get("ContainersRunning", 0),
|
456
|
+
"images": info.get("Images", 0),
|
457
|
+
"driver": info.get("Driver", "unknown"),
|
458
|
+
"platform": f"{info.get('OperatingSystem', 'unknown')} {info.get('Architecture', 'unknown')}",
|
459
|
+
"memory_total": self._format_size(info.get("MemTotal", 0)),
|
460
|
+
"swap_total": self._format_size(info.get("SwapTotal", 0)),
|
461
|
+
}
|
462
|
+
self.log_action("get_info", params, result)
|
463
|
+
return result
|
464
|
+
except Exception as e:
|
465
|
+
self.log_action("get_info", params, error=e)
|
466
|
+
raise RuntimeError(f"Failed to get info: {str(e)}")
|
467
|
+
|
468
|
+
def remove_image(self, image: str, force: bool = False) -> Dict:
|
469
|
+
params = {"image": image, "force": force}
|
470
|
+
try:
|
471
|
+
self.client.images.remove(image, force=force)
|
472
|
+
result = {"removed": image}
|
473
|
+
self.log_action("remove_image", params, result)
|
474
|
+
return result
|
475
|
+
except Exception as e:
|
476
|
+
self.log_action("remove_image", params, error=e)
|
477
|
+
raise RuntimeError(f"Failed to remove image: {str(e)}")
|
478
|
+
|
433
479
|
def stop_container(self, container_id: str, timeout: int = 10) -> Dict:
|
434
480
|
params = {"container_id": container_id, "timeout": timeout}
|
435
481
|
try:
|
@@ -535,65 +581,6 @@ class DockerManager(ContainerManagerBase):
|
|
535
581
|
self.log_action("remove_volume", params, error=e)
|
536
582
|
raise RuntimeError(f"Failed to remove volume: {str(e)}")
|
537
583
|
|
538
|
-
def list_networks(self) -> List[Dict]:
|
539
|
-
params = {}
|
540
|
-
try:
|
541
|
-
networks = self.client.networks.list()
|
542
|
-
result = []
|
543
|
-
for net in networks:
|
544
|
-
attrs = net.attrs
|
545
|
-
containers = len(attrs.get("Containers", {}))
|
546
|
-
created = attrs.get("Created", "unknown")
|
547
|
-
if isinstance(created, str):
|
548
|
-
created_str = created
|
549
|
-
else:
|
550
|
-
created_str = (
|
551
|
-
datetime.fromtimestamp(created).strftime("%Y-%m-%dT%H:%M:%S")
|
552
|
-
if created
|
553
|
-
else "unknown"
|
554
|
-
)
|
555
|
-
simplified = {
|
556
|
-
"id": attrs.get("Id", "unknown")[7:19],
|
557
|
-
"name": attrs.get("Name", "unknown"),
|
558
|
-
"driver": attrs.get("Driver", "unknown"),
|
559
|
-
"scope": attrs.get("Scope", "unknown"),
|
560
|
-
"containers": containers,
|
561
|
-
"created": created_str,
|
562
|
-
}
|
563
|
-
result.append(simplified)
|
564
|
-
self.log_action("list_networks", params, result)
|
565
|
-
return result
|
566
|
-
except Exception as e:
|
567
|
-
self.log_action("list_networks", params, error=e)
|
568
|
-
raise RuntimeError(f"Failed to list networks: {str(e)}")
|
569
|
-
|
570
|
-
def create_network(self, name: str, driver: str = "bridge") -> Dict:
|
571
|
-
params = {"name": name, "driver": driver}
|
572
|
-
try:
|
573
|
-
network = self.client.networks.create(name, driver=driver)
|
574
|
-
attrs = network.attrs
|
575
|
-
created = attrs.get("Created", "unknown")
|
576
|
-
if isinstance(created, str):
|
577
|
-
created_str = created
|
578
|
-
else:
|
579
|
-
created_str = (
|
580
|
-
datetime.fromtimestamp(created).strftime("%Y-%m-%dT%H:%M:%S")
|
581
|
-
if created
|
582
|
-
else "unknown"
|
583
|
-
)
|
584
|
-
result = {
|
585
|
-
"id": attrs.get("Id", "unknown")[7:19],
|
586
|
-
"name": attrs.get("Name", name),
|
587
|
-
"driver": attrs.get("Driver", driver),
|
588
|
-
"scope": attrs.get("Scope", "unknown"),
|
589
|
-
"created": created_str,
|
590
|
-
}
|
591
|
-
self.log_action("create_network", params, result)
|
592
|
-
return result
|
593
|
-
except Exception as e:
|
594
|
-
self.log_action("create_network", params, error=e)
|
595
|
-
raise RuntimeError(f"Failed to create network: {str(e)}")
|
596
|
-
|
597
584
|
def remove_network(self, network_id: str) -> Dict:
|
598
585
|
params = {"network_id": network_id}
|
599
586
|
try:
|
@@ -829,52 +816,107 @@ class DockerManager(ContainerManagerBase):
|
|
829
816
|
|
830
817
|
|
831
818
|
class PodmanManager(ContainerManagerBase):
|
832
|
-
def __init__(self, silent: bool = False, log_file: str = None):
|
819
|
+
def __init__(self, silent: bool = False, log_file: Optional[str] = None):
|
833
820
|
super().__init__(silent, log_file)
|
821
|
+
|
834
822
|
if PodmanClient is None:
|
835
823
|
raise ImportError("Please install podman-py: pip install podman")
|
824
|
+
|
825
|
+
base_url = self._autodetect_podman_url()
|
826
|
+
if base_url is None:
|
827
|
+
self.logger.error(
|
828
|
+
"No valid Podman socket found after trying all known locations"
|
829
|
+
)
|
830
|
+
raise RuntimeError("Failed to connect to Podman: No valid socket found")
|
831
|
+
|
836
832
|
try:
|
837
|
-
self.client = PodmanClient()
|
833
|
+
self.client = PodmanClient(base_url=base_url)
|
834
|
+
self.logger.info(f"Connected to Podman with base_url: {base_url}")
|
838
835
|
except PodmanError as e:
|
839
|
-
self.logger.error(
|
840
|
-
|
836
|
+
self.logger.error(
|
837
|
+
f"Failed to connect to Podman daemon with {base_url}: {str(e)}"
|
838
|
+
)
|
839
|
+
raise RuntimeError(f"Failed to connect to Podman with {base_url}: {str(e)}")
|
841
840
|
|
842
|
-
def
|
843
|
-
|
841
|
+
def _is_wsl(self) -> bool:
|
842
|
+
"""Check if running inside WSL2."""
|
844
843
|
try:
|
845
|
-
version
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
"os": version.get("Os", "unknown"),
|
850
|
-
"arch": version.get("Arch", "unknown"),
|
851
|
-
"build_time": version.get("BuildTime", "unknown"),
|
852
|
-
}
|
853
|
-
self.log_action("get_version", params, result)
|
854
|
-
return result
|
855
|
-
except Exception as e:
|
856
|
-
self.log_action("get_version", params, error=e)
|
857
|
-
raise RuntimeError(f"Failed to get version: {str(e)}")
|
844
|
+
with open("/proc/version", "r") as f:
|
845
|
+
return "WSL" in f.read()
|
846
|
+
except FileNotFoundError:
|
847
|
+
return "WSL_DISTRO_NAME" in os.environ
|
858
848
|
|
859
|
-
def
|
860
|
-
|
849
|
+
def _is_podman_machine_running(self) -> bool:
|
850
|
+
"""Check if Podman machine is running (for Windows/WSL2)."""
|
861
851
|
try:
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
852
|
+
result = subprocess.run(
|
853
|
+
["podman", "machine", "list", "--format", "{{.Running}}"],
|
854
|
+
capture_output=True,
|
855
|
+
text=True,
|
856
|
+
check=False,
|
857
|
+
)
|
858
|
+
return "true" in result.stdout.lower()
|
859
|
+
except (subprocess.SubprocessError, FileNotFoundError):
|
860
|
+
return False
|
861
|
+
|
862
|
+
def _try_connect(self, base_url: str) -> Optional[PodmanClient]:
|
863
|
+
"""Attempt to connect to Podman with the given base_url."""
|
864
|
+
try:
|
865
|
+
client = PodmanClient(base_url=base_url)
|
866
|
+
# Test connection
|
867
|
+
client.version()
|
868
|
+
return client
|
869
|
+
except PodmanError as e:
|
870
|
+
self.logger.debug(f"Connection failed for {base_url}: {str(e)}")
|
871
|
+
return None
|
872
|
+
|
873
|
+
def _autodetect_podman_url(self) -> Optional[str]:
|
874
|
+
"""Autodetect the appropriate Podman socket URL based on platform."""
|
875
|
+
# Check for environment variable override
|
876
|
+
base_url = os.environ.get("PODMAN_BASE_URL")
|
877
|
+
if base_url:
|
878
|
+
self.logger.info(f"Using PODMAN_BASE_URL from environment: {base_url}")
|
879
|
+
return base_url
|
880
|
+
|
881
|
+
system = platform.system()
|
882
|
+
is_wsl = self._is_wsl()
|
883
|
+
|
884
|
+
# Define socket candidates based on platform
|
885
|
+
socket_candidates = []
|
886
|
+
if system == "Windows" and not is_wsl:
|
887
|
+
# Windows with Podman machine
|
888
|
+
if self._is_podman_machine_running():
|
889
|
+
socket_candidates.append("npipe:////./pipe/docker_engine")
|
890
|
+
# Fallback to WSL2 distro sockets if running in a mixed setup
|
891
|
+
socket_candidates.extend(
|
892
|
+
[
|
893
|
+
"unix:///mnt/wsl/podman-sockets/podman-machine-default/podman-user.sock", # Rootless
|
894
|
+
"unix:///mnt/wsl/podman-sockets/podman-machine-default/podman-root.sock", # Rootful
|
895
|
+
]
|
896
|
+
)
|
897
|
+
elif system == "Linux" or is_wsl:
|
898
|
+
# Linux or WSL2 distro: prioritize rootless, then rootful
|
899
|
+
uid = os.getuid()
|
900
|
+
socket_candidates.extend(
|
901
|
+
[
|
902
|
+
f"unix:///run/user/{uid}/podman/podman.sock", # Rootless
|
903
|
+
"unix:///run/podman/podman.sock", # Rootful
|
904
|
+
]
|
905
|
+
)
|
906
|
+
|
907
|
+
# Try each socket candidate
|
908
|
+
for url in socket_candidates:
|
909
|
+
# For Unix sockets, check if the file exists (on Linux/WSL2)
|
910
|
+
if url.startswith("unix://") and (system == "Linux" or is_wsl):
|
911
|
+
socket_path = url.replace("unix://", "")
|
912
|
+
if not os.path.exists(socket_path):
|
913
|
+
self.logger.debug(f"Socket {socket_path} does not exist")
|
914
|
+
continue
|
915
|
+
client = self._try_connect(url)
|
916
|
+
if client:
|
917
|
+
return url
|
918
|
+
|
919
|
+
return None
|
878
920
|
|
879
921
|
def list_images(self) -> List[Dict]:
|
880
922
|
params = {}
|
@@ -888,12 +930,8 @@ class PodmanManager(ContainerManagerBase):
|
|
888
930
|
repository, tag = (
|
889
931
|
repo_tag.rsplit(":", 1) if ":" in repo_tag else ("<none>", "<none>")
|
890
932
|
)
|
891
|
-
created = attrs.get("Created",
|
892
|
-
created_str = (
|
893
|
-
datetime.fromtimestamp(created).strftime("%Y-%m-%dT%H:%M:%S")
|
894
|
-
if created
|
895
|
-
else "unknown"
|
896
|
-
)
|
933
|
+
created = attrs.get("Created", None)
|
934
|
+
created_str = self._parse_timestamp(created)
|
897
935
|
size_bytes = attrs.get("Size", 0)
|
898
936
|
size_str = self._format_size(size_bytes) if size_bytes else "0B"
|
899
937
|
simplified = {
|
@@ -926,12 +964,8 @@ class PodmanManager(ContainerManagerBase):
|
|
926
964
|
repository, tag = (
|
927
965
|
repo_tag.rsplit(":", 1) if ":" in repo_tag else (image, tag)
|
928
966
|
)
|
929
|
-
created = attrs.get("Created",
|
930
|
-
created_str = (
|
931
|
-
datetime.fromtimestamp(created).strftime("%Y-%m-%dT%H:%M:%S")
|
932
|
-
if created
|
933
|
-
else "unknown"
|
934
|
-
)
|
967
|
+
created = attrs.get("Created", None)
|
968
|
+
created_str = self._parse_timestamp(created)
|
935
969
|
size_bytes = attrs.get("Size", 0)
|
936
970
|
size_str = self._format_size(size_bytes) if size_bytes else "0B"
|
937
971
|
result = {
|
@@ -949,17 +983,6 @@ class PodmanManager(ContainerManagerBase):
|
|
949
983
|
self.log_action("pull_image", params, error=e)
|
950
984
|
raise RuntimeError(f"Failed to pull image: {str(e)}")
|
951
985
|
|
952
|
-
def remove_image(self, image: str, force: bool = False) -> Dict:
|
953
|
-
params = {"image": image, "force": force}
|
954
|
-
try:
|
955
|
-
self.client.images.remove(image, force=force)
|
956
|
-
result = {"removed": image}
|
957
|
-
self.log_action("remove_image", params, result)
|
958
|
-
return result
|
959
|
-
except Exception as e:
|
960
|
-
self.log_action("remove_image", params, error=e)
|
961
|
-
raise RuntimeError(f"Failed to remove image: {str(e)}")
|
962
|
-
|
963
986
|
def list_containers(self, all: bool = False) -> List[Dict]:
|
964
987
|
params = {"all": all}
|
965
988
|
try:
|
@@ -973,12 +996,8 @@ class PodmanManager(ContainerManagerBase):
|
|
973
996
|
for p in ports
|
974
997
|
if p.get("host_port")
|
975
998
|
]
|
976
|
-
created = attrs.get("Created",
|
977
|
-
created_str = (
|
978
|
-
datetime.fromtimestamp(created).strftime("%Y-%m-%dT%H:%M:%S")
|
979
|
-
if created
|
980
|
-
else "unknown"
|
981
|
-
)
|
999
|
+
created = attrs.get("Created", None)
|
1000
|
+
created_str = self._parse_timestamp(created)
|
982
1001
|
simplified = {
|
983
1002
|
"id": attrs.get("Id", "unknown")[7:19],
|
984
1003
|
"image": attrs.get("Image", "unknown"),
|
@@ -1034,12 +1053,8 @@ class PodmanManager(ContainerManagerBase):
|
|
1034
1053
|
for p in ports
|
1035
1054
|
if p.get("host_port")
|
1036
1055
|
]
|
1037
|
-
created = attrs.get("Created",
|
1038
|
-
created_str = (
|
1039
|
-
datetime.fromtimestamp(created).strftime("%Y-%m-%dT%H:%M:%S")
|
1040
|
-
if created
|
1041
|
-
else "unknown"
|
1042
|
-
)
|
1056
|
+
created = attrs.get("Created", None)
|
1057
|
+
created_str = self._parse_timestamp(created)
|
1043
1058
|
result = {
|
1044
1059
|
"id": attrs.get("Id", "unknown")[7:19],
|
1045
1060
|
"image": attrs.get("Image", image),
|
@@ -1054,6 +1069,99 @@ class PodmanManager(ContainerManagerBase):
|
|
1054
1069
|
self.log_action("run_container", params, error=e)
|
1055
1070
|
raise RuntimeError(f"Failed to run container: {str(e)}")
|
1056
1071
|
|
1072
|
+
def list_networks(self) -> List[Dict]:
|
1073
|
+
params = {}
|
1074
|
+
try:
|
1075
|
+
networks = self.client.networks.list()
|
1076
|
+
result = []
|
1077
|
+
for net in networks:
|
1078
|
+
attrs = net.attrs
|
1079
|
+
containers = len(attrs.get("Containers", {}))
|
1080
|
+
created = attrs.get("Created", None)
|
1081
|
+
created_str = self._parse_timestamp(created)
|
1082
|
+
simplified = {
|
1083
|
+
"id": attrs.get("Id", "unknown")[7:19],
|
1084
|
+
"name": attrs.get("Name", "unknown"),
|
1085
|
+
"driver": attrs.get("Driver", "unknown"),
|
1086
|
+
"scope": attrs.get("Scope", "unknown"),
|
1087
|
+
"containers": containers,
|
1088
|
+
"created": created_str,
|
1089
|
+
}
|
1090
|
+
result.append(simplified)
|
1091
|
+
self.log_action("list_networks", params, result)
|
1092
|
+
return result
|
1093
|
+
except Exception as e:
|
1094
|
+
self.log_action("list_networks", params, error=e)
|
1095
|
+
raise RuntimeError(f"Failed to list networks: {str(e)}")
|
1096
|
+
|
1097
|
+
def create_network(self, name: str, driver: str = "bridge") -> Dict:
|
1098
|
+
params = {"name": name, "driver": driver}
|
1099
|
+
try:
|
1100
|
+
network = self.client.networks.create(name, driver=driver)
|
1101
|
+
attrs = network.attrs
|
1102
|
+
created = attrs.get("Created", None)
|
1103
|
+
created_str = self._parse_timestamp(created)
|
1104
|
+
result = {
|
1105
|
+
"id": attrs.get("Id", "unknown")[7:19],
|
1106
|
+
"name": attrs.get("Name", name),
|
1107
|
+
"driver": attrs.get("Driver", driver),
|
1108
|
+
"scope": attrs.get("Scope", "unknown"),
|
1109
|
+
"created": created_str,
|
1110
|
+
}
|
1111
|
+
self.log_action("create_network", params, result)
|
1112
|
+
return result
|
1113
|
+
except Exception as e:
|
1114
|
+
self.log_action("create_network", params, error=e)
|
1115
|
+
raise RuntimeError(f"Failed to create network: {str(e)}")
|
1116
|
+
|
1117
|
+
def get_version(self) -> Dict:
|
1118
|
+
params = {}
|
1119
|
+
try:
|
1120
|
+
version = self.client.version()
|
1121
|
+
result = {
|
1122
|
+
"version": version.get("Version", "unknown"),
|
1123
|
+
"api_version": version.get("APIVersion", "unknown"),
|
1124
|
+
"os": version.get("Os", "unknown"),
|
1125
|
+
"arch": version.get("Arch", "unknown"),
|
1126
|
+
"build_time": version.get("BuildTime", "unknown"),
|
1127
|
+
}
|
1128
|
+
self.log_action("get_version", params, result)
|
1129
|
+
return result
|
1130
|
+
except Exception as e:
|
1131
|
+
self.log_action("get_version", params, error=e)
|
1132
|
+
raise RuntimeError(f"Failed to get version: {str(e)}")
|
1133
|
+
|
1134
|
+
def get_info(self) -> Dict:
|
1135
|
+
params = {}
|
1136
|
+
try:
|
1137
|
+
info = self.client.info()
|
1138
|
+
host = info.get("host", {})
|
1139
|
+
result = {
|
1140
|
+
"containers_total": info.get("store", {}).get("containers", 0),
|
1141
|
+
"containers_running": host.get("runningContainers", 0),
|
1142
|
+
"images": info.get("store", {}).get("images", 0),
|
1143
|
+
"driver": host.get("graphDriverName", "unknown"),
|
1144
|
+
"platform": f"{host.get('os', 'unknown')} {host.get('arch', 'unknown')}",
|
1145
|
+
"memory_total": self._format_size(host.get("memTotal", 0)),
|
1146
|
+
"swap_total": self._format_size(host.get("swapTotal", 0)),
|
1147
|
+
}
|
1148
|
+
self.log_action("get_info", params, result)
|
1149
|
+
return result
|
1150
|
+
except Exception as e:
|
1151
|
+
self.log_action("get_info", params, error=e)
|
1152
|
+
raise RuntimeError(f"Failed to get info: {str(e)}")
|
1153
|
+
|
1154
|
+
def remove_image(self, image: str, force: bool = False) -> Dict:
|
1155
|
+
params = {"image": image, "force": force}
|
1156
|
+
try:
|
1157
|
+
self.client.images.remove(image, force=force)
|
1158
|
+
result = {"removed": image}
|
1159
|
+
self.log_action("remove_image", params, result)
|
1160
|
+
return result
|
1161
|
+
except Exception as e:
|
1162
|
+
self.log_action("remove_image", params, error=e)
|
1163
|
+
raise RuntimeError(f"Failed to remove image: {str(e)}")
|
1164
|
+
|
1057
1165
|
def stop_container(self, container_id: str, timeout: int = 10) -> Dict:
|
1058
1166
|
params = {"container_id": container_id, "timeout": timeout}
|
1059
1167
|
try:
|
@@ -1159,65 +1267,6 @@ class PodmanManager(ContainerManagerBase):
|
|
1159
1267
|
self.log_action("remove_volume", params, error=e)
|
1160
1268
|
raise RuntimeError(f"Failed to remove volume: {str(e)}")
|
1161
1269
|
|
1162
|
-
def list_networks(self) -> List[Dict]:
|
1163
|
-
params = {}
|
1164
|
-
try:
|
1165
|
-
networks = self.client.networks.list()
|
1166
|
-
result = []
|
1167
|
-
for net in networks:
|
1168
|
-
attrs = net.attrs
|
1169
|
-
containers = len(attrs.get("Containers", {}))
|
1170
|
-
created = attrs.get("Created", "unknown")
|
1171
|
-
if isinstance(created, str):
|
1172
|
-
created_str = created
|
1173
|
-
else:
|
1174
|
-
created_str = (
|
1175
|
-
datetime.fromtimestamp(created).strftime("%Y-%m-%dT%H:%M:%S")
|
1176
|
-
if created
|
1177
|
-
else "unknown"
|
1178
|
-
)
|
1179
|
-
simplified = {
|
1180
|
-
"id": attrs.get("Id", "unknown")[7:19],
|
1181
|
-
"name": attrs.get("Name", "unknown"),
|
1182
|
-
"driver": attrs.get("Driver", "unknown"),
|
1183
|
-
"scope": attrs.get("Scope", "unknown"),
|
1184
|
-
"containers": containers,
|
1185
|
-
"created": created_str,
|
1186
|
-
}
|
1187
|
-
result.append(simplified)
|
1188
|
-
self.log_action("list_networks", params, result)
|
1189
|
-
return result
|
1190
|
-
except Exception as e:
|
1191
|
-
self.log_action("list_networks", params, error=e)
|
1192
|
-
raise RuntimeError(f"Failed to list networks: {str(e)}")
|
1193
|
-
|
1194
|
-
def create_network(self, name: str, driver: str = "bridge") -> Dict:
|
1195
|
-
params = {"name": name, "driver": driver}
|
1196
|
-
try:
|
1197
|
-
network = self.client.networks.create(name, driver=driver)
|
1198
|
-
attrs = network.attrs
|
1199
|
-
created = attrs.get("Created", "unknown")
|
1200
|
-
if isinstance(created, str):
|
1201
|
-
created_str = created
|
1202
|
-
else:
|
1203
|
-
created_str = (
|
1204
|
-
datetime.fromtimestamp(created).strftime("%Y-%m-%dT%H:%M:%S")
|
1205
|
-
if created
|
1206
|
-
else "unknown"
|
1207
|
-
)
|
1208
|
-
result = {
|
1209
|
-
"id": attrs.get("Id", "unknown")[7:19],
|
1210
|
-
"name": attrs.get("Name", name),
|
1211
|
-
"driver": attrs.get("Driver", driver),
|
1212
|
-
"scope": attrs.get("Scope", "unknown"),
|
1213
|
-
"created": created_str,
|
1214
|
-
}
|
1215
|
-
self.log_action("create_network", params, result)
|
1216
|
-
return result
|
1217
|
-
except Exception as e:
|
1218
|
-
self.log_action("create_network", params, error=e)
|
1219
|
-
raise RuntimeError(f"Failed to create network: {str(e)}")
|
1220
|
-
|
1221
1270
|
def remove_network(self, network_id: str) -> Dict:
|
1222
1271
|
params = {"network_id": network_id}
|
1223
1272
|
try:
|
@@ -1316,13 +1365,32 @@ class PodmanManager(ContainerManagerBase):
|
|
1316
1365
|
raise NotImplementedError("Swarm not supported in Podman")
|
1317
1366
|
|
1318
1367
|
|
1319
|
-
# The rest of the file (create_manager, usage, container_manager) remains unchanged
|
1320
|
-
|
1321
|
-
|
1322
1368
|
def create_manager(
|
1323
|
-
manager_type: str, silent: bool = False, log_file: str = None
|
1369
|
+
manager_type: Optional[str] = None, silent: bool = False, log_file: str = None
|
1324
1370
|
) -> ContainerManagerBase:
|
1325
|
-
if manager_type
|
1371
|
+
if manager_type is None:
|
1372
|
+
manager_type = os.environ.get("CONTAINER_MANAGER_TYPE")
|
1373
|
+
if manager_type is None:
|
1374
|
+
# Autodetect
|
1375
|
+
if PodmanClient is not None:
|
1376
|
+
try:
|
1377
|
+
test_client = PodmanClient()
|
1378
|
+
test_client.close()
|
1379
|
+
manager_type = "podman"
|
1380
|
+
except Exception:
|
1381
|
+
pass
|
1382
|
+
if manager_type is None and docker is not None:
|
1383
|
+
try:
|
1384
|
+
test_client = docker.from_env()
|
1385
|
+
test_client.close()
|
1386
|
+
manager_type = "docker"
|
1387
|
+
except Exception:
|
1388
|
+
pass
|
1389
|
+
if manager_type is None:
|
1390
|
+
raise ValueError(
|
1391
|
+
"No supported container manager detected. Set CONTAINER_MANAGER_TYPE or install Docker/Podman."
|
1392
|
+
)
|
1393
|
+
if manager_type.lower() in ["docker", "swarm"]:
|
1326
1394
|
return DockerManager(silent=silent, log_file=log_file)
|
1327
1395
|
elif manager_type.lower() == "podman":
|
1328
1396
|
return PodmanManager(silent=silent, log_file=log_file)
|
@@ -1338,7 +1406,7 @@ Container Manager: A tool to manage containers with Docker, Podman, and Docker S
|
|
1338
1406
|
Usage:
|
1339
1407
|
-h | --help [ See usage for script ]
|
1340
1408
|
-s | --silent [ Suppress output ]
|
1341
|
-
-m | --manager <type> [ docker, podman, swarm; default:
|
1409
|
+
-m | --manager <type> [ docker, podman, swarm; default: auto-detect ]
|
1342
1410
|
--log-file <path> [ Log to specified file (default: container_manager.log in script dir) ]
|
1343
1411
|
|
1344
1412
|
Actions:
|
@@ -1403,262 +1471,178 @@ container_manager.py --manager docker --pull-image nginx --tag latest --list-con
|
|
1403
1471
|
|
1404
1472
|
|
1405
1473
|
def container_manager(argv):
|
1406
|
-
|
1407
|
-
|
1408
|
-
|
1409
|
-
|
1410
|
-
|
1411
|
-
|
1412
|
-
|
1413
|
-
|
1414
|
-
|
1415
|
-
|
1416
|
-
|
1417
|
-
|
1418
|
-
|
1419
|
-
|
1420
|
-
|
1421
|
-
|
1422
|
-
|
1423
|
-
|
1424
|
-
|
1425
|
-
|
1426
|
-
|
1427
|
-
|
1428
|
-
|
1429
|
-
|
1430
|
-
|
1431
|
-
|
1432
|
-
|
1433
|
-
|
1434
|
-
|
1435
|
-
|
1436
|
-
|
1437
|
-
|
1438
|
-
|
1439
|
-
|
1440
|
-
|
1441
|
-
|
1442
|
-
|
1443
|
-
|
1444
|
-
|
1445
|
-
|
1446
|
-
|
1447
|
-
|
1448
|
-
|
1449
|
-
|
1450
|
-
|
1451
|
-
|
1452
|
-
|
1453
|
-
|
1454
|
-
|
1455
|
-
|
1456
|
-
|
1457
|
-
|
1458
|
-
|
1459
|
-
|
1460
|
-
|
1461
|
-
|
1462
|
-
|
1463
|
-
|
1464
|
-
|
1465
|
-
|
1466
|
-
|
1467
|
-
|
1468
|
-
|
1469
|
-
|
1470
|
-
|
1471
|
-
|
1472
|
-
|
1473
|
-
|
1474
|
-
|
1475
|
-
|
1476
|
-
|
1477
|
-
|
1478
|
-
|
1479
|
-
|
1480
|
-
|
1481
|
-
|
1482
|
-
|
1483
|
-
|
1484
|
-
|
1485
|
-
|
1486
|
-
|
1487
|
-
|
1488
|
-
|
1489
|
-
|
1490
|
-
|
1491
|
-
|
1492
|
-
|
1493
|
-
|
1494
|
-
|
1495
|
-
|
1496
|
-
|
1497
|
-
|
1498
|
-
|
1499
|
-
|
1500
|
-
|
1501
|
-
|
1502
|
-
|
1503
|
-
|
1504
|
-
"remove-container=",
|
1505
|
-
"get-container-logs=",
|
1506
|
-
"tail=",
|
1507
|
-
"exec-in-container=",
|
1508
|
-
"exec-command=",
|
1509
|
-
"exec-detach",
|
1510
|
-
"list-volumes",
|
1511
|
-
"create-volume=",
|
1512
|
-
"remove-volume=",
|
1513
|
-
"list-networks",
|
1514
|
-
"create-network=",
|
1515
|
-
"driver=",
|
1516
|
-
"remove-network=",
|
1517
|
-
"compose-up=",
|
1518
|
-
"build",
|
1519
|
-
"compose-down=",
|
1520
|
-
"compose-ps=",
|
1521
|
-
"compose-logs=",
|
1522
|
-
"service=",
|
1523
|
-
"init-swarm",
|
1524
|
-
"advertise-addr=",
|
1525
|
-
"leave-swarm",
|
1526
|
-
"list-nodes",
|
1527
|
-
"list-services",
|
1528
|
-
"create-service=",
|
1529
|
-
"image=",
|
1530
|
-
"replicas=",
|
1531
|
-
"mounts=",
|
1532
|
-
"remove-service=",
|
1533
|
-
],
|
1534
|
-
)
|
1535
|
-
except getopt.GetoptError:
|
1536
|
-
usage()
|
1537
|
-
sys.exit(2)
|
1474
|
+
parser = argparse.ArgumentParser(
|
1475
|
+
description="Container Manager: A tool to manage containers with Docker, Podman, and Docker Swarm!"
|
1476
|
+
)
|
1477
|
+
parser.add_argument("-s", "--silent", action="store_true", help="Suppress output")
|
1478
|
+
parser.add_argument(
|
1479
|
+
"-m",
|
1480
|
+
"--manager",
|
1481
|
+
type=str,
|
1482
|
+
default=None,
|
1483
|
+
help="Container manager type: docker, podman, swarm (default: auto-detect)",
|
1484
|
+
)
|
1485
|
+
parser.add_argument("--log-file", type=str, default=None, help="Path to log file")
|
1486
|
+
parser.add_argument("--get-version", action="store_true", help="Get version info")
|
1487
|
+
parser.add_argument("--get-info", action="store_true", help="Get system info")
|
1488
|
+
parser.add_argument("--list-images", action="store_true", help="List images")
|
1489
|
+
parser.add_argument("--pull-image", type=str, default=None, help="Image to pull")
|
1490
|
+
parser.add_argument("--tag", type=str, default="latest", help="Image tag")
|
1491
|
+
parser.add_argument("--platform", type=str, default=None, help="Platform")
|
1492
|
+
parser.add_argument(
|
1493
|
+
"--remove-image", type=str, default=None, help="Image to remove"
|
1494
|
+
)
|
1495
|
+
parser.add_argument("--force", action="store_true", help="Force removal")
|
1496
|
+
parser.add_argument(
|
1497
|
+
"--list-containers", action="store_true", help="List containers"
|
1498
|
+
)
|
1499
|
+
parser.add_argument("--all", action="store_true", help="Show all containers")
|
1500
|
+
parser.add_argument("--run-container", type=str, default=None, help="Image to run")
|
1501
|
+
parser.add_argument("--name", type=str, default=None, help="Container name")
|
1502
|
+
parser.add_argument("--command", type=str, default=None, help="Command to run")
|
1503
|
+
parser.add_argument("--detach", action="store_true", help="Detach mode")
|
1504
|
+
parser.add_argument("--ports", type=str, default=None, help="Port mappings")
|
1505
|
+
parser.add_argument("--volumes", type=str, default=None, help="Volume mappings")
|
1506
|
+
parser.add_argument(
|
1507
|
+
"--environment", type=str, default=None, help="Environment vars"
|
1508
|
+
)
|
1509
|
+
parser.add_argument(
|
1510
|
+
"--stop-container", type=str, default=None, help="Container to stop"
|
1511
|
+
)
|
1512
|
+
parser.add_argument("--timeout", type=int, default=10, help="Timeout in seconds")
|
1513
|
+
parser.add_argument(
|
1514
|
+
"--remove-container", type=str, default=None, help="Container to remove"
|
1515
|
+
)
|
1516
|
+
parser.add_argument(
|
1517
|
+
"--get-container-logs", type=str, default=None, help="Container logs"
|
1518
|
+
)
|
1519
|
+
parser.add_argument("--tail", type=str, default="all", help="Tail lines")
|
1520
|
+
parser.add_argument(
|
1521
|
+
"--exec-in-container", type=str, default=None, help="Container to exec"
|
1522
|
+
)
|
1523
|
+
parser.add_argument("--exec-command", type=str, default=None, help="Exec command")
|
1524
|
+
parser.add_argument("--exec-detach", action="store_true", help="Detach exec")
|
1525
|
+
parser.add_argument("--list-volumes", action="store_true", help="List volumes")
|
1526
|
+
parser.add_argument(
|
1527
|
+
"--create-volume", type=str, default=None, help="Volume to create"
|
1528
|
+
)
|
1529
|
+
parser.add_argument(
|
1530
|
+
"--remove-volume", type=str, default=None, help="Volume to remove"
|
1531
|
+
)
|
1532
|
+
parser.add_argument("--list-networks", action="store_true", help="List networks")
|
1533
|
+
parser.add_argument(
|
1534
|
+
"--create-network", type=str, default=None, help="Network to create"
|
1535
|
+
)
|
1536
|
+
parser.add_argument("--driver", type=str, default="bridge", help="Network driver")
|
1537
|
+
parser.add_argument(
|
1538
|
+
"--remove-network", type=str, default=None, help="Network to remove"
|
1539
|
+
)
|
1540
|
+
parser.add_argument("--compose-up", type=str, default=None, help="Compose file up")
|
1541
|
+
parser.add_argument("--build", action="store_true", help="Build images")
|
1542
|
+
parser.add_argument(
|
1543
|
+
"--compose-detach", action="store_true", default=True, help="Detach compose"
|
1544
|
+
)
|
1545
|
+
parser.add_argument(
|
1546
|
+
"--compose-down", type=str, default=None, help="Compose file down"
|
1547
|
+
)
|
1548
|
+
parser.add_argument("--compose-ps", type=str, default=None, help="Compose ps")
|
1549
|
+
parser.add_argument("--compose-logs", type=str, default=None, help="Compose logs")
|
1550
|
+
parser.add_argument("--service", type=str, default=None, help="Specific service")
|
1551
|
+
parser.add_argument("--init-swarm", action="store_true", help="Init swarm")
|
1552
|
+
parser.add_argument(
|
1553
|
+
"--advertise-addr", type=str, default=None, help="Advertise address"
|
1554
|
+
)
|
1555
|
+
parser.add_argument("--leave-swarm", action="store_true", help="Leave swarm")
|
1556
|
+
parser.add_argument("--list-nodes", action="store_true", help="List swarm nodes")
|
1557
|
+
parser.add_argument(
|
1558
|
+
"--list-services", action="store_true", help="List swarm services"
|
1559
|
+
)
|
1560
|
+
parser.add_argument(
|
1561
|
+
"--create-service", type=str, default=None, help="Service to create"
|
1562
|
+
)
|
1563
|
+
parser.add_argument("--image", type=str, default=None, help="Service image")
|
1564
|
+
parser.add_argument("--replicas", type=int, default=1, help="Replicas")
|
1565
|
+
parser.add_argument("--mounts", type=str, default=None, help="Mounts")
|
1566
|
+
parser.add_argument(
|
1567
|
+
"--remove-service", type=str, default=None, help="Service to remove"
|
1568
|
+
)
|
1569
|
+
parser.add_argument("-h", "--help", action="store_true", help="Show help")
|
1570
|
+
|
1571
|
+
args = parser.parse_args(argv)
|
1538
1572
|
|
1539
|
-
|
1540
|
-
|
1541
|
-
|
1542
|
-
|
1543
|
-
|
1544
|
-
|
1545
|
-
|
1546
|
-
|
1547
|
-
|
1548
|
-
|
1549
|
-
|
1550
|
-
|
1551
|
-
|
1552
|
-
|
1553
|
-
|
1554
|
-
|
1555
|
-
|
1556
|
-
|
1557
|
-
|
1558
|
-
|
1559
|
-
|
1560
|
-
|
1561
|
-
|
1562
|
-
|
1563
|
-
|
1564
|
-
|
1565
|
-
|
1566
|
-
|
1567
|
-
|
1568
|
-
|
1569
|
-
|
1570
|
-
|
1571
|
-
|
1572
|
-
|
1573
|
-
|
1574
|
-
|
1575
|
-
|
1576
|
-
|
1577
|
-
|
1578
|
-
|
1579
|
-
|
1580
|
-
|
1581
|
-
|
1582
|
-
|
1583
|
-
|
1584
|
-
|
1585
|
-
|
1586
|
-
|
1587
|
-
|
1588
|
-
|
1589
|
-
|
1590
|
-
|
1591
|
-
|
1592
|
-
|
1593
|
-
|
1594
|
-
|
1595
|
-
|
1596
|
-
|
1597
|
-
|
1598
|
-
|
1599
|
-
|
1600
|
-
|
1601
|
-
|
1602
|
-
|
1603
|
-
|
1604
|
-
|
1605
|
-
|
1606
|
-
|
1607
|
-
|
1608
|
-
|
1609
|
-
|
1610
|
-
|
1611
|
-
|
1612
|
-
remove_volume = True
|
1613
|
-
remove_volume_name = arg
|
1614
|
-
elif opt == "--list-networks":
|
1615
|
-
list_networks = True
|
1616
|
-
elif opt == "--create-network":
|
1617
|
-
create_network = True
|
1618
|
-
create_network_name = arg
|
1619
|
-
elif opt == "--driver":
|
1620
|
-
driver = arg
|
1621
|
-
elif opt == "--remove-network":
|
1622
|
-
remove_network = True
|
1623
|
-
remove_network_id = arg
|
1624
|
-
elif opt == "--compose-up":
|
1625
|
-
compose_up = True
|
1626
|
-
compose_up_file = arg
|
1627
|
-
elif opt == "--build":
|
1628
|
-
compose_build = True
|
1629
|
-
elif opt == "--compose-down":
|
1630
|
-
compose_down = True
|
1631
|
-
compose_down_file = arg
|
1632
|
-
elif opt == "--compose-ps":
|
1633
|
-
compose_ps = True
|
1634
|
-
compose_ps_file = arg
|
1635
|
-
elif opt == "--compose-logs":
|
1636
|
-
compose_logs = True
|
1637
|
-
compose_logs_file = arg
|
1638
|
-
elif opt == "--service":
|
1639
|
-
compose_service = arg
|
1640
|
-
elif opt == "--init-swarm":
|
1641
|
-
init_swarm = True
|
1642
|
-
elif opt == "--advertise-addr":
|
1643
|
-
advertise_addr = arg
|
1644
|
-
elif opt == "--leave-swarm":
|
1645
|
-
leave_swarm = True
|
1646
|
-
elif opt == "--list-nodes":
|
1647
|
-
list_nodes = True
|
1648
|
-
elif opt == "--list-services":
|
1649
|
-
list_services = True
|
1650
|
-
elif opt == "--create-service":
|
1651
|
-
create_service = True
|
1652
|
-
create_service_name = arg
|
1653
|
-
elif opt == "--image":
|
1654
|
-
service_image = arg
|
1655
|
-
elif opt == "--replicas":
|
1656
|
-
replicas = int(arg)
|
1657
|
-
elif opt == "--mounts":
|
1658
|
-
mounts_str = arg
|
1659
|
-
elif opt == "--remove-service":
|
1660
|
-
remove_service = True
|
1661
|
-
remove_service_id = arg
|
1573
|
+
if args.help:
|
1574
|
+
usage()
|
1575
|
+
sys.exit(0)
|
1576
|
+
|
1577
|
+
get_version = args.get_version
|
1578
|
+
get_info = args.get_info
|
1579
|
+
list_images = args.list_images
|
1580
|
+
pull_image = args.pull_image is not None
|
1581
|
+
pull_image_str = args.pull_image
|
1582
|
+
tag = args.tag
|
1583
|
+
platform = args.platform
|
1584
|
+
remove_image = args.remove_image is not None
|
1585
|
+
remove_image_str = args.remove_image
|
1586
|
+
force = args.force
|
1587
|
+
list_containers = args.list_containers
|
1588
|
+
all_containers = args.all
|
1589
|
+
run_container = args.run_container is not None
|
1590
|
+
run_image = args.run_container
|
1591
|
+
name = args.name
|
1592
|
+
command = args.command
|
1593
|
+
detach = args.detach
|
1594
|
+
ports_str = args.ports
|
1595
|
+
volumes_str = args.volumes
|
1596
|
+
environment_str = args.environment
|
1597
|
+
stop_container = args.stop_container is not None
|
1598
|
+
stop_container_id = args.stop_container
|
1599
|
+
timeout = args.timeout
|
1600
|
+
remove_container = args.remove_container is not None
|
1601
|
+
remove_container_id = args.remove_container
|
1602
|
+
get_container_logs = args.get_container_logs is not None
|
1603
|
+
container_logs_id = args.get_container_logs
|
1604
|
+
tail = args.tail
|
1605
|
+
exec_in_container = args.exec_in_container is not None
|
1606
|
+
exec_container_id = args.exec_in_container
|
1607
|
+
exec_command = args.exec_command
|
1608
|
+
exec_detach = args.exec_detach
|
1609
|
+
list_volumes = args.list_volumes
|
1610
|
+
create_volume = args.create_volume is not None
|
1611
|
+
create_volume_name = args.create_volume
|
1612
|
+
remove_volume = args.remove_volume is not None
|
1613
|
+
remove_volume_name = args.remove_volume
|
1614
|
+
list_networks = args.list_networks
|
1615
|
+
create_network = args.create_network is not None
|
1616
|
+
create_network_name = args.create_network
|
1617
|
+
driver = args.driver
|
1618
|
+
remove_network = args.remove_network is not None
|
1619
|
+
remove_network_id = args.remove_network
|
1620
|
+
compose_up = args.compose_up is not None
|
1621
|
+
compose_up_file = args.compose_up
|
1622
|
+
compose_build = args.build
|
1623
|
+
compose_detach = args.compose_detach
|
1624
|
+
compose_down = args.compose_down is not None
|
1625
|
+
compose_down_file = args.compose_down
|
1626
|
+
compose_ps = args.compose_ps is not None
|
1627
|
+
compose_ps_file = args.compose_ps
|
1628
|
+
compose_logs = args.compose_logs is not None
|
1629
|
+
compose_logs_file = args.compose_logs
|
1630
|
+
compose_service = args.service
|
1631
|
+
init_swarm = args.init_swarm
|
1632
|
+
advertise_addr = args.advertise_addr
|
1633
|
+
leave_swarm = args.leave_swarm
|
1634
|
+
list_nodes = args.list_nodes
|
1635
|
+
list_services = args.list_services
|
1636
|
+
create_service = args.create_service is not None
|
1637
|
+
create_service_name = args.create_service
|
1638
|
+
service_image = args.image
|
1639
|
+
replicas = args.replicas
|
1640
|
+
mounts_str = args.mounts
|
1641
|
+
remove_service = args.remove_service is not None
|
1642
|
+
remove_service_id = args.remove_service
|
1643
|
+
manager_type = args.manager
|
1644
|
+
silent = args.silent
|
1645
|
+
log_file = args.log_file
|
1662
1646
|
|
1663
1647
|
manager = create_manager(manager_type, silent, log_file)
|
1664
1648
|
|