0N/A#!/usr/bin/python2.7
0N/A#
0N/A# Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
0N/A#
0N/A
0N/Aimport argparse
0N/Aimport httplib
0N/Aimport json
0N/Afrom multiprocessing.dummy import Pool as ThreadPool
0N/Aimport os
0N/Aimport shutil
0N/Aimport socket
0N/Afrom subprocess import Popen, PIPE
0N/Aimport sys
0N/Aimport tempfile
0N/A
0N/ADOCKER_SOCK = "/var/run/docker/docker.sock"
0N/AROOTFS_ARCHIVE = "rootfs.tar.gz"
873N/ADOCKERFILE = """FROM scratch
0N/AADD %(archive)s /
0N/ACMD /bin/bash
0N/A"""
0N/A
0N/A
3215N/Aclass HTTPConnectionSocket(httplib.HTTPConnection):
0N/A """HTTPConnection for local UNIX sockets."""
338N/A def __init__(self, sock_path):
2086N/A httplib.HTTPConnection.__init__(self, 'localhost')
2086N/A self.sock_path = sock_path
2086N/A
0N/A def connect(self):
0N/A # superclass uses self.sock, he'll handle cleanup
0N/A self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
0N/A self.sock.connect(self.sock_path)
0N/A
0N/A
0N/Adef do_api_get(url):
0N/A try:
338N/A con = HTTPConnectionSocket(DOCKER_SOCK)
338N/A con.request("GET", url)
0N/A resp = con.getresponse()
2095N/A except httplib.HTTPException as err:
2095N/A raise RuntimeError("unable to call API: %s" % err)
2095N/A
2095N/A # expect standard success status
2095N/A if resp.status != 200:
2095N/A raise RuntimeError("Unable to query Docker API: status [%s] "
1156N/A "reason [%s]" % (resp.status, resp.reason))
0N/A
0N/A try:
338N/A data = json.loads(resp.read())
338N/A except Exception as err:
338N/A raise RuntimeError("unexpected error parsing JSON: %s" % err)
338N/A
338N/A con.close()
0N/A return data
0N/A
0N/A
0N/Adef do_api_post(url):
0N/A try:
0N/A con = HTTPConnectionSocket(DOCKER_SOCK)
338N/A con.request("POST", url)
0N/A resp = con.getresponse()
0N/A except httplib.HTTPException as err:
338N/A raise RuntimeError("unable to call API: %s" % err)
0N/A
0N/A # expect success and no content
338N/A if resp.status != 204:
0N/A raise RuntimeError("Unable to query Docker API: status [%s] "
0N/A "reason [%s]" % (resp.status, resp.reason))
0N/A con.close()
0N/A
0N/A
0N/Aclass DockerSupportCmd(object):
338N/A def __init__(self, cmd, verbose=False):
338N/A self.cmd = cmd
338N/A self.verbose = verbose
338N/A
0N/A def run(self, expect_nonzero=None):
338N/A if self.verbose:
2086N/A out = None
0N/A else:
0N/A out = PIPE
0N/A p = Popen(self.cmd, stdout=out, stderr=PIPE)
0N/A output, error = p.communicate()
0N/A if not expect_nonzero and p.returncode != 0:
0N/A raise RuntimeError(error)
0N/A return output
0N/A
0N/A
0N/Adef docker_is_online():
0N/A try:
0N/A status = DockerSupportCmd(['/usr/bin/svcs', '-Ho', 'state',
0N/A 'docker']).run().strip()
0N/A return status.startswith('online') or status.startswith('degraded')
338N/A except Exception as err:
338N/A raise RuntimeError("Unable to determine service status: %s" % err)
338N/A
338N/A
338N/Adef get_os_version():
338N/A try:
0N/A output = DockerSupportCmd(['/usr/bin/pkg', 'info', '-r',
338N/A 'osnet/osnet-incorporation']).run()
2086N/A for line in map(str.strip, output.splitlines()):
338N/A if line.startswith("Branch"):
0N/A return line.split(":")[1].strip()
0N/A except Exception as err:
0N/A raise RuntimeError("Unable to determine version: %s" % err)
0N/A
0N/A
0N/Adef create_rootfs_archive(args):
0N/A cmd = ['/usr/lib/brand/solaris-oci/mkimage-solaris']
0N/A if args.devbuild:
0N/A cmd.append('-D')
0N/A if args.profile:
2086N/A if not os.path.exists(args.profile):
2086N/A raise RuntimeError("'%s' not found" % args.profile)
2086N/A cmd.extend(['-c', args.profile])
2086N/A try:
2086N/A # build rootfs, send output to stdout
2086N/A DockerSupportCmd(cmd, verbose=True).run()
2086N/A except Exception as err:
2086N/A raise RuntimeError("mkimage-solaris failure: %s" % err)
2086N/A
2086N/A
2086N/Adef create_base_image(args):
2086N/A if not docker_is_online():
2086N/A raise SystemExit("Docker service not online, is Docker configured?")
2086N/A
2086N/A try:
2086N/A temp_dir = tempfile.mkdtemp(dir="/system/volatile")
2086N/A except Exception as err:
2086N/A raise SystemExit("Could not create build directory: %s" % err)
2086N/A
2086N/A try:
2086N/A print "Creating container rootfs from host publishers..."
0N/A create_rootfs_archive(args)
0N/A except Exception as err:
0N/A raise SystemExit("Failed to create rootfs: %s" % err)
0N/A
338N/A shutil.move(ROOTFS_ARCHIVE, temp_dir)
338N/A
338N/A prev_dir = os.getcwd()
338N/A os.chdir(temp_dir)
338N/A try:
338N/A osversion = get_os_version()
338N/A with open("Dockerfile", "w") as dockerfile:
338N/A dockerfile.write(DOCKERFILE %
0N/A {"archive": ROOTFS_ARCHIVE, "osversion": osversion})
338N/A
2086N/A tag = "solaris:%s" % osversion
338N/A print "Creating Docker base image '%s'..." % tag
0N/A
0N/A DockerSupportCmd(
0N/A ["/usr/bin/docker", "build", "-t", tag, "."], verbose=True).run()
0N/A DockerSupportCmd(
0N/A ["/usr/bin/docker", "tag", tag, "solaris:latest"]).run()
0N/A print "Build complete."
0N/A except Exception as err:
0N/A raise SystemExit("Failed image build: %s" % err)
0N/A finally:
0N/A os.chdir(prev_dir)
0N/A assert os.path.exists(temp_dir)
0N/A shutil.rmtree(temp_dir)
0N/A
0N/A
338N/Adef get_running_container_ids():
338N/A try:
338N/A return [container['Id'] for container in
338N/A do_api_get("http:/containers/json")]
0N/A except RuntimeError as e:
338N/A print "unable to query api for container list: %s" % e
0N/A
338N/A
338N/Adef kill_container(cid):
0N/A try:
338N/A do_api_post("http:/containers/%s/kill" % cid)
2086N/A print "shutdown container [%s]" % cid
338N/A except RuntimeError as e:
338N/A print "unable to kill container [%s]: %s" % (cid, e)
0N/A
0N/A
0N/Adef shutdown_containers(args):
0N/A print "Shutting down all running container instances..."
0N/A if not docker_is_online():
0N/A return
0N/A
0N/A pool = ThreadPool(64)
0N/A pool.map(kill_container, get_running_container_ids())
0N/A pool.close()
0N/A pool.join()
0N/A
0N/A if get_running_container_ids():
0N/A print "NOTE: unable to gracefully shutdown all containers"
0N/A
0N/A
2095N/Adef build_parser():
0N/A parser_main = argparse.ArgumentParser()
0N/A parser_main.add_argument("-v", "--version", action="version",
0N/A version="%(prog)s 0.1")
0N/A
0N/A subparsers = parser_main.add_subparsers(title="sub-commands", metavar="")
0N/A
0N/A parser_create = subparsers.add_parser("create-base-image",
0N/A help="create a base image from host publisher content",
0N/A usage=argparse.SUPPRESS)
338N/A parser_create.add_argument("-D", "--devbuild", action="store_true",
338N/A help="use development build options for the package image")
0N/A parser_create.add_argument("-p", "--profile",
2095N/A help="TEMPORARY: optional syconfig profile")
0N/A parser_create.set_defaults(func=create_base_image)
0N/A
0N/A parser_shutdown = subparsers.add_parser("shutdown-containers",
0N/A help="gracefully kill all running container instances",
0N/A usage=argparse.SUPPRESS)
0N/A parser_shutdown.set_defaults(func=shutdown_containers)
0N/A
0N/A return parser_main
0N/A
338N/A
338N/Adef main():
0N/A parser = build_parser()
2095N/A args = parser.parse_args()
0N/A if not vars(args):
0N/A raise SystemExit(parser.print_help())
0N/A return args.func(args)
0N/A
0N/A
if __name__ == "__main__":
sys.exit(main())