6466N/A#!/usr/bin/python2.7
6466N/A
6466N/A#
6466N/A# Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
6466N/A#
6466N/A
6466N/Aimport os
7175N/Aimport subprocess
6466N/Aimport sys
6466N/Aimport traceback
6466N/A
6466N/Aimport smf_include
6466N/A
6466N/ADOCKER_EXEC="/usr/bin/docker"
7175N/ADOCKER_SUPPORT="/usr/bin/docker-support"
6466N/AMKDIR="/usr/bin/mkdir"
6466N/ASVCADM="/usr/sbin/svcadm"
6466N/ASVCCFG="/usr/sbin/svccfg"
6466N/ASVCPROP="/usr/bin/svcprop"
6466N/AZFS="/usr/sbin/zfs"
6466N/A
6466N/ADOCKER_SVC = 'svc:/application/docker/docker'
6466N/ADOCKER_ROOT="/var/run/docker"
6466N/ADOCKER_MOUNTPOINT="/var/lib/docker"
6466N/A
6466N/A
6466N/Aclass SvcDockerException(Exception):
6466N/A pass
6466N/A
6466N/A
6466N/Aclass SvcDockerCmd(object):
6466N/A def __init__(self, cmd):
6466N/A self.cmd = cmd
6466N/A
6466N/A def run(self, expect_nonzero=None):
7175N/A p = subprocess.Popen(self.cmd, stdout=subprocess.PIPE,
7175N/A stderr=subprocess.PIPE)
6466N/A output, error = p.communicate()
6466N/A if not expect_nonzero and p.returncode != 0:
6466N/A raise SvcDockerException(error)
6466N/A return output
6466N/A
6466N/A
6466N/Adef _get_docker_prop(pname):
6466N/A try:
6466N/A output = SvcDockerCmd([SVCPROP, '-p', pname, DOCKER_SVC]).run()
6466N/A return output.strip('"').strip()
6466N/A except SvcDockerException as e:
6466N/A print "Unable to retrieve property '%s': %s" % (pname, e)
6466N/A sys.exit(1)
6466N/A
6466N/A
6466N/Adef _set_docker_prop(pname, pval):
6466N/A try:
6466N/A SvcDockerCmd(
6466N/A [SVCCFG, '-s', DOCKER_SVC, 'setprop', pname, '=', pval]).run()
6466N/A SvcDockerCmd(cmd = [SVCADM, 'refresh', 'docker']).run()
6466N/A except SvcDockerException as e:
6466N/A print "Unable to set property '%s', value '%s': %s" % \
6466N/A (pname, pval, e)
6466N/A sys.exit(1)
6466N/A
6466N/A
6466N/Adef _get_root_pool():
6466N/A try:
6466N/A return SvcDockerCmd(
6466N/A [ZFS, 'list', '-Ho', 'name', '/']).run().split('/')[0]
6466N/A except SvcDockerException as e:
6466N/A print "Unable to get root pool: %s" % e
6466N/A sys.exit(1)
6466N/A
6466N/A
6466N/Adef _fsname_in_active_be(fsname):
6466N/A try:
6466N/A root_ds = SvcDockerCmd(
6466N/A [ZFS, 'list', '-r', '-Ho', 'name', '/']).run().split()[0]
6466N/A return fsname.startswith(root_ds)
6466N/A except SvcDockerException as e:
6466N/A print "Unable to get active root dataset: %s" % e
6466N/A sys.exit(1)
6466N/A
6466N/A
6466N/Adef _fsname_exists(fsname):
6466N/A try:
6466N/A SvcDockerCmd([ZFS, 'list', fsname]).run()
6466N/A return True
6466N/A except SvcDockerException as e:
6466N/A if "does not exist" in str(e):
6466N/A return False
6466N/A print "Unable to list dataset: %s" % e
6466N/A sys.exit(1)
6466N/A
6466N/A
6466N/Adef _get_mounted_dataset():
6466N/A if not os.path.exists(DOCKER_MOUNTPOINT):
6466N/A return None
6466N/A try:
6466N/A return SvcDockerCmd(
6466N/A [ZFS, 'list', '-Ho', 'name', DOCKER_MOUNTPOINT]).run().strip()
6466N/A except SvcDockerException as e:
6466N/A print "Unable to get mounted Docker dataset: %s" % e
6466N/A sys.exit(1)
6466N/A
6466N/A
6466N/Adef _get_dataset_mountpoint(fsname):
6466N/A try:
6466N/A return SvcDockerCmd(
6466N/A [ZFS, 'list', '-Ho', 'mountpoint', fsname]).run().strip()
6466N/A except SvcDockerException as e:
6466N/A print "Unable to get mountpoint for dataset: %s" % e
6466N/A sys.exit(1)
6466N/A
6466N/A
6466N/Adef _mount_dataset(fsname):
6466N/A try:
6466N/A return SvcDockerCmd([ZFS, 'mount', fsname]).run()
6466N/A except SvcDockerException as e:
6466N/A print "Unable to mount Docker root: %s" % e
6466N/A sys.exit(1)
6466N/A
6466N/A
6466N/Adef _unmount_dataset(fsname):
6466N/A try:
6466N/A return SvcDockerCmd([ZFS, 'unmount', fsname]).run()
6466N/A except SvcDockerException as e:
6466N/A print "Unable to unmount Docker root: %s" % e
6466N/A sys.exit(1)
6466N/A
6466N/A
6466N/Adef _set_ds_props_for_varshare(fsname):
6466N/A # If in VARSHARE (default), make sure we turn setuid/exec/xattr on
6466N/A # (off by default in VARSHARE).
6466N/A for prop in ['setuid', 'exec', 'xattr']:
6466N/A try:
6466N/A SvcDockerCmd([ZFS, 'set', prop + '=on', fsname]).run()
6466N/A except SvcDockerException as e:
6466N/A print "Failed to set '%s' prop on dataset '%s': %s" % \
6466N/A (prop, fsname, error)
6466N/A sys.exit(1)
6466N/A
6466N/A
6466N/Adef _create_docker_dir():
6466N/A if not os.path.exists(DOCKER_ROOT):
6466N/A try:
6466N/A os.mkdir(DOCKER_ROOT, 0770)
6466N/A except OSError as e:
6466N/A print "Unable to create dir '%s': %s" % (DOCKER_ROOT, e)
6466N/A sys.exit(1)
6466N/A
6466N/A
6466N/Adef _init_dataset(fsname):
6466N/A if not fsname:
6466N/A # Default to 'docker' in varshare
6466N/A fsname = os.path.join(_get_root_pool(), "VARSHARE/docker")
6466N/A _set_docker_prop("config/fsname", fsname)
6466N/A
6466N/A if _fsname_in_active_be(fsname):
6466N/A print "config/fsname cannot be child of active root dataset"
6466N/A sys.exit(smf_include.SMF_EXIT_ERR_CONFIG)
6466N/A
6466N/A if _fsname_exists(fsname):
6466N/A if _get_mounted_dataset() != fsname:
6466N/A if _get_dataset_mountpoint(fsname) != DOCKER_MOUNTPOINT:
6466N/A print "Configured dataset '%s' mountpoint must be '%s'" % \
6466N/A (fsname, DOCKER_MOUNTPOINT)
6466N/A sys.exit(smf_include.SMF_EXIT_ERR_CONFIG)
6466N/A _mount_dataset(fsname)
6466N/A if fsname.startswith('rpool/VARSHARE/'):
6466N/A _set_ds_props_for_varshare(fsname)
6466N/A else:
6466N/A # Dataset doesn't exist, try and create it. This may fail if
6466N/A # /var/lib/docker is not empty, or if another dataset is mounted there.
6466N/A try:
6466N/A SvcDockerCmd(
6466N/A [ZFS, 'create', '-o', 'mountpoint=' + DOCKER_MOUNTPOINT,
6466N/A '-o', 'setuid=on', '-o', 'exec=on', '-o', 'xattr=on',
6466N/A '-o', 'compression=on', fsname]).run()
6466N/A except SvcDockerException as e:
6466N/A print "Failed to create dataset '%s' on %s: %s" % \
6466N/A (DOCKER_MOUNTPOINT, fsname, e)
6466N/A sys.exit(1)
6466N/A
6466N/A
6466N/Adef start():
6466N/A # Setup /var/lib/docker and the root dataset
6466N/A _create_docker_dir()
6466N/A fsname = _get_docker_prop("config/fsname")
6466N/A _init_dataset(fsname)
6466N/A
6466N/A # Setup environment variables for the daemon
6466N/A for p in ['http_proxy', 'https_proxy']:
6466N/A v = _get_docker_prop("config/%s" % p)
6466N/A if v:
6466N/A os.putenv(p, v)
6466N/A
7175N/A # Set up the daemon command line and execute
6466N/A dcmd = DOCKER_EXEC + ' daemon'
6466N/A if _get_docker_prop('config/debug') == 'true':
6466N/A dcmd += ' -D'
6466N/A dcmd += ' --exec-root="%s"' % DOCKER_ROOT
6466N/A dcmd += ' --graph="%s"' % DOCKER_MOUNTPOINT
6466N/A dcmd += ' --pidfile="%s/docker.pid"' % DOCKER_ROOT
6466N/A dcmd += ' --storage-opt zfs.fsname=' + fsname
6466N/A smf_include.smf_subprocess(dcmd)
6466N/A sys.exit(smf_include.SMF_EXIT_OK)
6466N/A
6466N/A
6466N/Adef stop():
7175N/A # Kill off any running containers. Use a shell to log progress.
7175N/A p = subprocess.Popen("/usr/bin/docker-support shutdown-containers",
7175N/A shell=True)
7175N/A p.communicate()
7175N/A
7175N/A # Kill the process contract which contains the daemon
6466N/A try:
7175N/A subprocess.check_call(["/usr/bin/pkill", "-c", sys.argv[2]])
7175N/A except subprocess.CalledProcessError as e:
6466N/A # 1 is returncode if no SMF contract processes were matched,
6466N/A # meaning they have already terminated.
6466N/A if e.returncode != 1:
6466N/A print "failed to kill the SMF contract: %s" % e
6466N/A return smf_include.SMF_EXIT_ERR_FATAL
6466N/A
7175N/A # Finally unmount the root dataset from /var/lib/docker
6466N/A _unmount_dataset(DOCKER_MOUNTPOINT)
6466N/A
6466N/A
6466N/Aif __name__ == '__main__':
6466N/A os.putenv('LC_ALL', 'C')
6466N/A try:
6466N/A smf_include.smf_main()
6466N/A except RuntimeError:
6466N/A sys.exit(smf_include.SMF_EXIT_ERR_FATAL)
6466N/A except Exception as err:
6466N/A print 'Unknown error: %s' % err
6466N/A print
6466N/A traceback.print_exc(file=sys.stdout)
6466N/A sys.exit(smf_include.SMF_EXIT_ERR_FATAL)