]> granicus.if.org Git - zziplib/blob - docker_mirror.py
libzzip: do not use -Warray-bounds with gcc-4.2
[zziplib] / docker_mirror.py
1 #! /usr/bin/python3
2 # from __future__ import print_function
3
4 __copyright__ = "(C) 2021 Guido Draheim"
5 __contact__ = "https://github.com/gdraheim/docker-mirror-packages-repo"
6 __license__ = "CC0 Creative Commons Zero (Public Domain)"
7 __version__ = "1.6.3007"
8
9 from collections import OrderedDict, namedtuple
10 import os.path
11 import sys
12 import re
13 import json
14 import logging
15 import subprocess
16 import tempfile
17 import shutil
18
19 if sys.version[0] != '2':
20     xrange = range
21     basestring = str
22
23 logg = logging.getLogger("mirror")
24 DOCKER = "docker"
25 ADDHOSTS = False
26 ADDEPEL = False
27 UNIVERSE = False
28
29 LEAP = "opensuse/leap"
30 SUSE = "opensuse"
31 OPENSUSE_VERSIONS = {"42.2": SUSE, "42.3": SUSE, "15.0": LEAP, "15.1": LEAP, "15.2": LEAP, "15.3": LEAP}
32 UBUNTU_LTS = {"16": "16.04", "18": "18.04", "20": "20.04"}
33 UBUNTU_VERSIONS = {"12.04": "precise", "14.04": "trusty", "16.04": "xenial", "17.10": "artful",
34                    "18.04": "bionic", "18.10": "cosmic", "19.04": "disco", "19.10": "eoan",
35                    "20.04": "focal", "20.10": "groovy"}
36 CENTOS_VERSIONS = {"7.0": "7.0.1406", "7.1": "7.1.1503", "7.2": "7.2.1511", "7.3": "7.3.1611",
37                    "7.4": "7.4.1708", "7.5": "7.5.1804", "7.6": "7.6.1810", "7.7": "7.7.1908",
38                    "7.8": "7.8.2003", "7.9": "7.9.2009",
39                    "8.0": "8.0.1905", "8.1": "8.1.1911", "8.2": "8.2.2004", "8.3": "8.3.2011"}
40
41 def decodes(text):
42     if text is None: return None
43     return decodes_(text)
44 def decodes_(text):
45     if isinstance(text, bytes):
46         encoded = sys.getdefaultencoding()
47         if encoded in ["ascii"]:
48             encoded = "utf-8"
49         try:
50             return text.decode(encoded)
51         except:
52             return text.decode("latin-1")
53     return text
54 def output3(cmd, shell=True, debug=True):
55     if isinstance(cmd, basestring):
56         if debug: logg.debug("run: %s", cmd)
57     else:
58         if debug: logg.debug("run: %s", " ".join(["'%s'" % item for item in cmd]))
59     run = subprocess.Popen(cmd, shell=shell, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
60     out, err = run.communicate()
61     return decodes_(out), decodes_(err), run.returncode
62
63 def major(version):
64     if version:
65         return version[0]
66     return version
67 def onlyversion(image):
68     if ":" in image:
69         return image.split(":")[-1]
70     return image
71
72 class DockerMirror:
73     def __init__(self, cname, image, hosts):
74         self.cname = cname  # name of running container
75         self.image = image  # image used to start the container
76         self.hosts = hosts  # domain names for the container
77
78 class DockerMirrorPackagesRepo:
79     def __init__(self, image=None):
80         self._image = image
81     def host_system_image(self):
82         """ returns the docker image name which corresponds to the 
83             operating system distribution of the host system. This
84             image name is the key for the other mirror functions. """
85         distro, version = self.detect_etc_image("/etc")
86         logg.info(":%s:%s host system image detected", distro, version)
87         if distro and version:
88             return "%s:%s" % (distro, version)
89         return ""
90     def detect_etc_image(self, etc):
91         distro, version = "", ""
92         os_release = os.path.join(etc, "os-release")
93         if os.path.exists(os_release):
94             # rhel:7.4 # VERSION="7.4 (Maipo)" ID="rhel" VERSION_ID="7.4"
95             # centos:7.3  # VERSION="7 (Core)" ID="centos" VERSION_ID="7"
96             # centos:7.4  # VERSION="7 (Core)" ID="centos" VERSION_ID="7"
97             # centos:7.7.1908  # VERSION="7 (Core)" ID="centos" VERSION_ID="7"
98             # opensuse:42.3 # VERSION="42.3" ID=opensuse VERSION_ID="42.3"
99             # opensuse/leap:15.0 # VERSION="15.0" ID="opensuse-leap" VERSION_ID="15.0"
100             # ubuntu:16.04 # VERSION="16.04.3 LTS (Xenial Xerus)" ID=ubuntu VERSION_ID="16.04"
101             # ubuntu:18.04 # VERSION="18.04.1 LTS (Bionic Beaver)" ID=ubuntu VERSION_ID="18.04"
102             for line in open(os_release):
103                 key, value = "", ""
104                 m = re.match('^([_\\w]+)=([^"].*).*', line.strip())
105                 if m:
106                     key, value = m.group(1), m.group(2)
107                 m = re.match('^([_\\w]+)="([^"]*)".*', line.strip())
108                 if m:
109                     key, value = m.group(1), m.group(2)
110                 # logg.debug("%s => '%s' '%s'", line.strip(), key, value)
111                 if key in ["ID"]:
112                     distro = value.replace("-", "/")
113                 if key in ["VERSION_ID"]:
114                     version = value
115         redhat_release = os.path.join(etc, "redhat-release")
116         if os.path.exists(redhat_release):
117             for line in open(redhat_release):
118                 m = re.search("release (\\d+[.]\\d+).*", line)
119                 if m:
120                     distro = "rhel"
121                     version = m.group(1)
122         centos_release = os.path.join(etc, "centos-release")
123         if os.path.exists(centos_release):
124             # CentOS Linux release 7.5.1804 (Core)
125             for line in open(centos_release):
126                 m = re.search("release (\\d+[.]\\d+).*", line)
127                 if m:
128                     distro = "centos"
129                     version = m.group(1)
130         return distro, version
131     def detect_base_image(self, image):
132         """ returns the docker image name which corresponds to the 
133             operating system distribution of the image provided. This
134             image name is the key for the other mirror functions. """
135         docker = DOCKER
136         distro, version = "", ""
137         cname = "docker_mirror_detect." + os.path.basename(image).replace(":", ".")
138         cmd = "{docker} rm -f {cname}"
139         out, err, end = output3(cmd.format(**locals()))
140         cmd = "{docker} create --name={cname} {image}"
141         out, err, end = output3(cmd.format(**locals()))
142         if end:
143             logg.info("%s --name %s : %s", image, cname, err.strip())
144         tempdir = tempfile.mkdtemp("docker_mirror_detect")
145         try:
146             distro, version = self.detect_base_image_from(cname, tempdir)
147             logg.info(":%s:%s base image detected", distro, version)
148             if distro and version:
149                 return "%s:%s" % (distro, version)
150         finally:
151             shutil.rmtree(tempdir)
152             cmd = "{docker} rm {cname}"
153             out, err, end = output3(cmd.format(**locals()))
154         return ""
155     def detect_base_image_from(self, cname, tempdir):
156         debug = False
157         docker = DOCKER
158         cmd = "{docker} cp {cname}:/usr/lib/os-release {tempdir}/os-release"
159         out, err, end = output3(cmd.format(**locals()), debug=debug)
160         if not end:
161             logg.debug("get: /usr/lib/os-release copied")
162         else:
163             logg.debug("get: /usr/lib/os-release: %s", err.strip().replace(cname, "{cname}"))
164             cmd = "{docker} cp {cname}:/etc/os-release {tempdir}/os-release"
165             out, err, end = output3(cmd.format(**locals()), debug=debug)
166             if not end:
167                 logg.debug("get: /etc/os-release copied")
168             else:
169                 logg.debug("get: /etc/os-release: %s", err.strip().replace(cname, "{cname}"))
170         cmd = "{docker} cp {cname}:/etc/redhat-release {tempdir}/redhat-release"
171         out, err, end = output3(cmd.format(**locals()), debug=debug)
172         if not end:
173             logg.debug("get: /etc/redhat-release copied")
174         else:
175             logg.debug("get: /etc/redhat-release: %s", err.strip().replace(cname, "{cname}"))
176         cmd = "{docker} cp {cname}:/etc/centos-release {tempdir}/centos-release"
177         out, err, end = output3(cmd.format(**locals()), debug=debug)
178         if not end:
179             logg.debug("get: /etc/centos-release copied")
180         else:
181             logg.debug("get: /etc/centos-release: %s", err.strip().replace(cname, "{cname}"))
182         return self.detect_etc_image(tempdir)
183     def get_docker_latest_image(self, image):
184         """ converts a shorthand version into the version string used on an image name. """
185         if image.startswith("centos:"):
186             return self.get_centos_latest(image)
187         if image.startswith("opensuse/leap:"):
188             return self.get_opensuse_latest(image)
189         if image.startswith("opensuse:"):
190             return self.get_opensuse_latest(image)
191         if image.startswith("ubuntu:"):
192             return self.get_ubuntu_latest(image)
193         return ""
194     def get_docker_latest_version(self, image):
195         """ converts a shorthand version into the version string used on an image name. """
196         if image.startswith("centos:"):
197             version = image[len("centos:"):]
198             return self.get_centos_latest_version(version)
199         if image.startswith("opensuse/leap:"):
200             version = image[len("opensuse/leap:"):]
201             return self.get_opensuse_latest_version(version)
202         if image.startswith("opensuse:"):
203             version = image[len("opensuse:"):]
204             return self.get_opensuse_latest_version(version)
205         if image.startswith("ubuntu:"):
206             version = image[len("ubuntu:"):]
207             return self.get_ubuntu_latest_version(version)
208         return ""
209     def get_docker_mirror(self, image):
210         """ attach local centos-repo / opensuse-repo to docker-start enviroment.
211             Effectivly when it is required to 'docker start centos:x.y' then do
212             'docker start centos-repo:x.y' before and extend the original to 
213             'docker start --add-host mirror...:centos-repo centos:x.y'. """
214         if image.startswith("centos:"):
215             return self.get_centos_docker_mirror(image)
216         if image.startswith("opensuse/leap:"):
217             return self.get_opensuse_docker_mirror(image)
218         if image.startswith("opensuse:"):
219             return self.get_opensuse_docker_mirror(image)
220         if image.startswith("ubuntu:"):
221             return self.get_ubuntu_docker_mirror(image)
222         return None
223     def get_docker_mirrors(self, image):
224         """ attach local centos-repo / opensuse-repo to docker-start enviroment.
225             Effectivly when it is required to 'docker start centos:x.y' then do
226             'docker start centos-repo:x.y' before and extend the original to 
227             'docker start --add-host mirror...:centos-repo centos:x.y'. """
228         mirrors = []
229         if image.startswith("centos:"):
230             mirrors = self.get_centos_docker_mirrors(image)
231             if ADDEPEL:
232                 if "centos" in image:
233                     mirrors += self.get_epel_docker_mirrors(image)
234         if image.startswith("opensuse/leap:"):
235             mirrors = self.get_opensuse_docker_mirrors(image)
236         if image.startswith("opensuse:"):
237             mirrors = self.get_opensuse_docker_mirrors(image)
238         if image.startswith("ubuntu:"):
239             mirrors = self.get_ubuntu_docker_mirrors(image)
240         logg.info(" %s -> %s", image, " ".join([mirror.cname for mirror in mirrors]))
241         return mirrors
242     def get_ubuntu_latest(self, image, default=None):
243         if image.startswith("ubuntu:"):
244             distro = "ubuntu"
245             version = image[len("ubuntu:"):]
246             latest = self.get_ubuntu_latest_version(version)
247             if latest:
248                 return "{distro}:{latest}".format(**locals())
249         if default is not None:
250             return default
251         return image
252     def get_ubuntu_latest_version(self, version):
253         """ allows to use 'ubuntu:18' or 'ubuntu:bionic' """
254         ver = version
255         if ver in ["latest"]:
256             ver = ""
257         if "." not in ver:
258             latest = ""
259             for release in UBUNTU_VERSIONS:
260                 codename = UBUNTU_VERSIONS[release]
261                 if len(ver) >= 3 and codename.startswith(ver):
262                     logg.debug("release (%s) %s", release, codename)
263                     if latest < release:
264                         latest = release
265                 elif release.startswith(ver):
266                     logg.debug("release %s (%s)", release, codename)
267                     if latest < release:
268                         latest = release
269             if latest:
270                 ver = latest
271         return ver or version
272     def get_ubuntu_docker_mirror(self, image):
273         """ detects a local ubuntu mirror or starts a local
274             docker container with a ubunut repo mirror. It
275             will return the extra_hosts setting to start
276             other docker containers"""
277         rmi = "localhost:5000/mirror-packages"
278         rep = "ubuntu-repo"
279         if UNIVERSE: rep = "ubuntu-repo/universe"
280         ver = self.get_ubuntu_latest_version(onlyversion(image))
281         return self.docker_mirror(rmi, rep, ver, "archive.ubuntu.com", "security.ubuntu.com")
282     def get_ubuntu_docker_mirrors(self, image):
283         main = self.get_ubuntu_docker_mirror(image)
284         return [main]
285     def get_centos_latest(self, image, default=None):
286         if image.startswith("centos:"):
287             distro = "centos"
288             version = image[len("centos:"):]
289             latest = self.get_centos_latest_version(version)
290             if latest:
291                 return "{distro}:{latest}".format(**locals())
292         if default is not None:
293             return default
294         return image
295     def get_centos_latest_version(self, version):
296         """ allows to use 'centos:7' or 'centos:7.9' making 'centos:7.9.2009' """
297         ver = version
298         if ver in ["latest"]:
299             ver = ""
300         if "." not in ver:
301             latest = ""
302             for release in CENTOS_VERSIONS:
303                 if release.startswith(ver):
304                     fullrelease = CENTOS_VERSIONS[release]
305                     logg.debug("release %s (%s)", release, fullrelease)
306                     if latest < fullrelease:
307                         latest = fullrelease
308             if latest:
309                 ver = latest
310         if ver in CENTOS_VERSIONS:
311             ver = CENTOS_VERSIONS[ver]
312         return ver or version
313     def get_centos_docker_mirror(self, image):
314         """ detects a local centos mirror or starts a local
315             docker container with a centos repo mirror. It
316             will return the setting for extrahosts"""
317         rmi = "localhost:5000/mirror-packages"
318         rep = "centos-repo"
319         ver = self.get_centos_latest_version(onlyversion(image))
320         return self.docker_mirror(rmi, rep, ver, "mirrorlist.centos.org")
321     def get_centos_docker_mirrors(self, image):
322         main = self.get_centos_docker_mirror(image)
323         return [main]
324     def get_opensuse_latest(self, image, default=None):
325         if image.startswith("opensuse/leap:"):
326             distro = "opensuse/leap"
327             version = image[len("opensuse/leap:"):]
328             latest = self.get_opensuse_latest_version(version)
329             if latest:
330                 if latest in OPENSUSE_VERSIONS:
331                     distro = OPENSUSE_VERSIONS[latest]
332                 return "{distro}:{latest}".format(**locals())
333         if image.startswith("opensuse:"):
334             distro = "opensuse"
335             version = image[len("opensuse:"):]
336             latest = self.get_opensuse_latest_version(version)
337             if latest:
338                 if latest in OPENSUSE_VERSIONS:
339                     distro = OPENSUSE_VERSIONS[latest]
340                 return "{distro}:{latest}".format(**locals())
341         if default is not None:
342             return default
343         return image
344     def get_opensuse_latest_version(self, version):
345         """ allows to use 'opensuse:42' making 'opensuse:42.3' """
346         ver = version
347         if ver in ["latest"]:
348             ver = ""
349         if "." not in ver:
350             latest = ""
351             for release in OPENSUSE_VERSIONS:
352                 if release.startswith(ver):
353                     logg.debug("release %s", release)
354                     # opensuse:42.0 was before opensuse/leap:15.0
355                     release42 = release.replace("42.", "14.")
356                     latest42 = latest.replace("42.", "14.")
357                     if latest42 < release42:
358                         latest = release
359             ver = latest or ver
360         return ver or version
361     def get_opensuse_docker_mirror(self, image):
362         """ detects a local opensuse mirror or starts a local
363             docker container with a centos repo mirror. It
364             will return the extra_hosts setting to start
365             other docker containers"""
366         rmi = "localhost:5000/mirror-packages"
367         rep = "opensuse-repo"
368         ver = self.get_opensuse_latest_version(onlyversion(image))
369         return self.docker_mirror(rmi, rep, ver, "download.opensuse.org")
370     def get_opensuse_docker_mirrors(self, image):
371         main = self.get_opensuse_docker_mirror(image)
372         return [main]
373     def docker_mirror(self, rmi, rep, ver, *hosts):
374         req = rep.replace("/", "-")
375         image = "{rmi}/{rep}:{ver}".format(**locals())
376         cname = "{req}-{ver}".format(**locals())
377         return DockerMirror(cname, image, list(hosts))
378     #
379     def get_extra_mirrors(self, image):
380         mirrors = []
381         if image.startswith("centos:"):
382             version = image[len("centos:"):]
383             mirrors = self.get_epel_docker_mirrors(version)
384         return mirrors
385     def get_epel_docker_mirrors(self, image):
386         main = self.get_epel_docker_mirror(image)
387         return [main]
388     def get_epel_docker_mirror(self, image):
389         """ detects a local epel mirror or starts a local
390             docker container with a epel repo mirror. It
391             will return the setting for extrahosts"""
392         docker = DOCKER
393         rmi = "localhost:5000/mirror-packages"
394         rep = "epel-repo"
395         ver = onlyversion(image)
396         version = self.get_centos_latest_version(ver)
397         # cut the yymm date part from the centos release
398         released = version.split(".")[-1]
399         later = ""
400         before = ""
401         # and then check for actual images around
402         cmd = docker + " images --format '{{.Repository}}:{{.Tag}}'"
403         out, err, end = output3(cmd)
404         if end:
405             logg.error("docker images [%s]\n\t", end, cmd)
406         for line in out.split("\n"):
407             if "/epel-repo:" not in line:
408                 continue
409             tagline = re.sub(".*/epel-repo:", "", line)
410             tagname = re.sub(" .*", "", tagline)
411             created = tagname.split(".")[-1]
412             accepts = tagname.startswith(major(version))
413             logg.debug(": %s (%s) (%s) %s:%s", line.strip(), created, released, major(version), accepts and "x" or "ignore")
414             if created >= released and accepts:
415                 if not later or later > tagname:
416                     later = tagname
417             elif created < released and accepts:
418                 if not before or before < tagname:
419                     before = tagname
420         if later:
421             ver = later
422         elif before:
423             ver = before
424         return self.docker_mirror(rmi, rep, ver, "mirrors.fedoraproject.org")
425     #
426     def ip_container(self, name):
427         docker = DOCKER
428         cmd = "{docker} inspect {name}"
429         out, err, rc = output3(cmd.format(**locals()))
430         if rc:
431             logg.info("%s : %s", cmd, err)
432             logg.debug("no address for %s", name)
433             return None
434         values = json.loads(out)
435         if not values or "NetworkSettings" not in values[0]:
436             logg.critical(" docker inspect %s => %s ", name, values)
437         addr = values[0]["NetworkSettings"]["IPAddress"]
438         assert isinstance(addr, basestring)
439         logg.debug("address %s for %s", addr, name)
440         return addr
441     def start_containers(self, image):
442         mirrors = self.get_docker_mirrors(image)
443         done = {}
444         for mirror in mirrors:
445             addr = self.start_container(mirror.image, mirror.cname)
446             done[mirror.cname] = addr
447         return done
448     def start_container(self, image, container):
449         docker = DOCKER
450         cmd = "{docker} inspect {image}"
451         out, err, ok = output3(cmd.format(**locals()))
452         image_found = json.loads(out)
453         if not image_found:
454             logg.info("image not found: %s", image)
455             return None
456         cmd = "{docker} inspect {container}"
457         out, err, rc = output3(cmd.format(**locals()))
458         container_found = json.loads(out)
459         if not rc and container_found:
460             container_status = container_found[0]["State"]["Status"]
461             logg.info("::: %s -> %s", container, container_status)
462             latest_image_id = image_found[0]["Id"]
463             container_image_id = container_found[0]["Image"]
464             if latest_image_id != container_image_id or container_status not in ["running"]:
465                 cmd = "{docker} rm --force {container}"
466                 out, err, rc = output3(cmd.format(**locals()))
467                 if rc:
468                     logg.debug("%s : %s", cmd, err)
469                 container_found = []
470         if not container_found:
471             cmd = "{docker} run --rm=true --detach --name {container} {image}"
472             out, err, rc = output3(cmd.format(**locals()))
473             if rc:
474                 logg.error("%s : %s", cmd, err)
475         addr = self.ip_container(container)
476         logg.info("%s : %s", container, addr)
477         return addr
478     def stop_containers(self, image):
479         mirrors = self.get_docker_mirrors(image)
480         done = {}
481         for mirror in mirrors:
482             info = self.stop_container(mirror.image, mirror.cname)
483             done[mirror.cname] = info
484         return done
485     def stop_container(self, image, container):
486         docker = DOCKER
487         cmd = "{docker} inspect {container}"
488         out, err, rc = output3(cmd.format(**locals()))
489         container_found = json.loads(out)
490         if not rc and container_found:
491             cmd = "{docker} rm --force {container}"
492             out, err, ok = output3(cmd.format(**locals()))
493             status = container_found[0].get("State", {})
494             started = status.get("StartedAt", "(was not started)")
495             assert isinstance(started, basestring)
496             return started
497         return "(did not exist)"
498     def info_containers(self, image):
499         mirrors = self.get_docker_mirrors(image)
500         done = {}
501         for mirror in mirrors:
502             info = self.info_container(mirror.image, mirror.cname)
503             done[mirror.cname] = info
504         return done
505     def info_container(self, image, container):
506         addr = self.ip_container(container)
507         return addr
508     def get_containers(self, image):
509         mirrors = self.get_docker_mirrors(image)
510         done = []
511         for mirror in mirrors:
512             done.append(mirror.cname)
513         return done
514     def inspect_containers(self, image):
515         mirrors = self.get_docker_mirrors(image)
516         docker = DOCKER
517         done = OrderedDict()
518         for mirror in mirrors:
519             addr = self.ip_container(mirror.cname)
520             done[mirror.cname] = addr
521         return done
522     #
523     def add_hosts(self, image, done={}):
524         mirrors = self.get_docker_mirrors(image)
525         args = []
526         for mirror in mirrors:
527             name = mirror.cname
528             logg.info("name = %s (%s)", name, done)
529             if name in done:
530                 addr = done[name]
531                 if addr:
532                     for host in mirror.hosts:
533                         args += ["--add-host", "%s:%s" % (host, addr)]
534         return args
535     def helps(self):
536         return """helper to start/stop mirror-container with the packages-repo
537         help             this help screen
538         image|detect     the image name matching the local system
539         facts [image]    the json data used to start or stop the containers
540         start [image]    starts the container(s) with the mirror-packages-repo
541         stop  [image]    stops the containers(s) with the mirror-packages-repo
542         addhosts [image] shows the --add-hosts string for the client container
543 """
544     def detect(self, image=None):
545         if not image and self._image:
546             image = self._image
547         if not image or image in ["host", "system"]:
548             return self.host_system_image()
549         latest = self.get_docker_latest_image(image)
550         if latest:
551             return latest
552         else:
553             # actually create a container and look into it
554             return self.detect_base_image(image)
555     def epel(self, image=None):
556         image = self.detect(image)
557         mirrors = self.get_extra_mirrors(image)
558         for mirror in mirrors:
559             return mirror.image
560         return ""
561     def repo(self, image=None):
562         image = self.detect(image)
563         mirrors = self.get_docker_mirrors(image)
564         for mirror in mirrors:
565             if ADDHOSTS:
566                 refer = mirror.image
567                 host = mirror.hosts[0]
568                 return "--add-host={host}:({refer})".format(**locals())
569             else:
570                 return mirror.image
571         return ""
572     def repos(self, image=None):
573         image = self.detect(image)
574         mirrors = self.get_docker_mirrors(image)
575         shown = ""
576         for mirror in mirrors:
577             if ADDHOSTS:
578                 if shown: shown += " "
579                 for host in mirror.hosts:
580                     refer = mirror.image
581                     shown += "--add-host={host}:({refer})".format(**locals())
582             else:
583                 shown += mirror.image + "\n"
584         return shown
585     def facts(self, image=None):
586         image = self.detect(image)
587         mirrors = self.get_docker_mirrors(image)
588         data = {}
589         for mirror in mirrors:
590             data[mirror.cname] = {"image": mirror.image, "name": mirror.cname,
591                                   "hosts": mirror.hosts}
592         return json.dumps(data, indent=2)
593     def starts(self, image=None):
594         image = self.detect(image)
595         done = self.start_containers(image)
596         if ADDHOSTS:
597             return " ".join(self.add_hosts(image, done))
598         else:
599             return json.dumps(done, indent=2)
600     def stops(self, image=None):
601         image = self.detect(image)
602         done = self.stop_containers(image)
603         if ADDHOSTS:
604             names = sorted(done.keys())
605             return " ".join(names)
606         else:
607             return json.dumps(done, indent=2)
608     def infos(self, image=None):
609         image = self.detect(image)
610         done = self.info_containers(image)
611         if ADDHOSTS:
612             return " ".join(self.add_hosts(image, done))
613         else:
614             return json.dumps(done, indent=2)
615     def containers(self, image=None):
616         image = self.detect(image)
617         done = self.get_containers(image)
618         if ADDHOSTS:
619             return " ".join(done)
620         else:
621             return json.dumps(done, indent=2)
622     def inspects(self, image=None):
623         image = self.detect(image)
624         done = self.inspect_containers(image)
625         if ADDHOSTS:
626             return " ".join(self.add_hosts(image, done))
627         else:
628             return json.dumps(done, indent=2)
629
630 if __name__ == "__main__":
631     from argparse import ArgumentParser
632     _o = ArgumentParser(description="""starts local containers representing mirrors of package repo repositories 
633         which are required by a container type. Subsequent 'docker run' can use the '--add-hosts' from this
634         helper script to divert 'pkg install' calls to a local docker container as the real source.""")
635     _o.add_argument("-v", "--verbose", action="count", default=0, help="more logging")
636     _o.add_argument("-a", "--add-hosts", "--add-host", action="store_true", default=ADDHOSTS,
637                     help="show addhost options for 'docker run' [%(default)s]")
638     _o.add_argument("--epel", action="store_true", default=ADDEPEL,
639                     help="addhosts for epel as well [%(default)s]")
640     _o.add_argument("--universe", action="store_true", default=UNIVERSE,
641                     help="addhosts using universe variant [%(default)s]")
642     commands = ["help", "detect", "image", "repo", "info", "facts", "start", "stop"]
643     _o.add_argument("command", nargs="?", default="detect", help="|".join(commands))
644     _o.add_argument("image", nargs="?", default=None, help="defaults to image name of the local host system")
645     opt = _o.parse_args()
646     logging.basicConfig(level=max(0, logging.WARNING - opt.verbose * 10))
647     ADDHOSTS = opt.add_hosts
648     ADDEPEL = opt.epel  # centos epel-repo
649     UNIVERSE = opt.universe  # ubuntu universe repo
650     command = "detect"
651     repo = DockerMirrorPackagesRepo()
652     if opt.command in ["?", "help"]:
653         print(repo.helps())
654     elif opt.command in ["detect", "image"]:
655         print(repo.detect(opt.image))
656     elif opt.command in ["repo", "from"]:
657         print(repo.repo(opt.image))
658     elif opt.command in ["repos", "for"]:
659         print(repo.repos(opt.image))
660     elif opt.command in ["latest"]:
661         print(repo.get_docker_latest_version(opt.image))
662     elif opt.command in ["epel"]:
663         print(repo.epel(opt.image))
664     elif opt.command in ["facts"]:
665         print(repo.facts(opt.image))
666     elif opt.command in ["start", "starts"]:
667         print(repo.starts(opt.image))
668     elif opt.command in ["stop", "stops"]:
669         print(repo.stops(opt.image))
670     elif opt.command in ["show", "shows", "info", "infos"]:
671         print(repo.infos(opt.image))
672     elif opt.command in ["addhost", "add-host", "addhosts", "add-hosts"]:
673         ADDHOSTS = True
674         print(repo.infos(opt.image))
675     elif opt.command in ["inspect"]:
676         print(repo.inspects(opt.image))
677     elif opt.command in ["containers"]:
678         print(repo.containers(opt.image))
679     else:
680         print("unknown command", opt.command)
681         sys.exit(1)