Beispiel #1
0
    def start_network_vnet_addr(self, iface, ip, defaultgw):
        """
        Add an IP address to a vnet interface inside the jail.

        :param iface: The interface to use
        :param ip:  The IP address to assign
        :param defaultgw: The gateway IP to assign to the nic
        :return: If an error occurs it returns the error. Otherwise, it's None
        """

        # Crude check to see if it's a IPv6 address
        if ":" in ip:
            ifconfig = [iface, "inet6", ip, "up"]
            route = ["add", "-6", "default", defaultgw]
        else:
            ifconfig = [iface, ip, "up"]
            route = ["add", "default", defaultgw]

        try:
            # Jail side
            checkoutput(["setfib", self.exec_fib, "jexec", f"ioc-{self.uuid}",
                         "ifconfig"] + ifconfig, stderr=STDOUT)
            checkoutput(["setfib", self.exec_fib, "jexec", f"ioc-{self.uuid}",
                         "route"] + route, stderr=STDOUT)
        except CalledProcessError as err:
            return f"{err.output.decode('utf-8')}".rstrip()
        else:
            return
Beispiel #2
0
def rollback_cmd(jail, name, force):
    """Get a list of jails and print the property."""
    lgr = ioc_logger.Logger('ioc_cli_rollback').getLogger()

    jails, paths = IOCList("uuid").list_datasets()
    pool = IOCJson().json_get_value("pool")

    _jail = {
        tag: uuid
        for (tag, uuid) in jails.items()
        if uuid.startswith(jail) or tag == jail
    }

    if len(_jail) == 1:
        tag, uuid = next(iter(_jail.items()))
        path = paths[tag]
    elif len(_jail) > 1:
        lgr.error("Multiple jails found for" f" {jail}:")
        for t, u in sorted(_jail.items()):
            lgr.error(f"  {u} ({t})")
        raise RuntimeError()
    else:
        lgr.critical(f"{jail} not found!")
        exit(1)

    # Looks like foo/iocage/jails/df0ef69a-57b6-4480-b1f8-88f7b6febbdf@BAR
    conf = IOCJson(path).json_load()

    if conf["template"] == "yes":
        target = f"{pool}/iocage/templates/{tag}"
    else:
        target = f"{pool}/iocage/jails/{uuid}"

    try:
        checkoutput(["zfs", "get", "-H", "creation", target], stderr=PIPE)
    except CalledProcessError:
        lgr.critical(f"Snapshot {target} does not exist!")
        exit(1)

    if not force:
        lgr.warning("\nThis will destroy ALL data created since"
                    f" {name} was taken.\nIncluding ALL snapshots taken after"
                    f" {name} for {uuid} ({tag})")
        if not click.confirm("\nAre you sure?"):
            exit()
    try:
        datasets = Popen(["zfs", "list", "-H", "-r", "-o", "name", target],
                         stdout=PIPE,
                         stderr=PIPE).communicate()[0].decode("utf-8").split()

        for dataset in datasets:
            check_call(["zfs", "rollback", "-r", f"{dataset}@{name}"])

        lgr.info(f"Rolled back to: {target}")
    except CalledProcessError as err:
        lgr.error(f"{err}")
        exit(1)
Beispiel #3
0
def find_bridge_mtu(bridge):
    memberif = [x for x in
                checkoutput(["ifconfig", bridge]).splitlines()
                if x.strip().startswith("member")]

    if not memberif:
        return '1500'

    membermtu = checkoutput(["ifconfig", memberif[0].split()[1]]).split()
    return membermtu[5]
Beispiel #4
0
def rollback_cmd(jail, name, force):
    """Get a list of jails and print the property."""
    lgr = ioc_logger.Logger('ioc_cli_rollback').getLogger()

    jails, paths = IOCList("uuid").list_datasets()
    pool = IOCJson().json_get_value("pool")

    _jail = {
        tag: uuid
        for (tag, uuid) in jails.items()
        if uuid.startswith(jail) or tag == jail
    }

    if len(_jail) == 1:
        tag, uuid = next(iter(_jail.items()))
        path = paths[tag]
    elif len(_jail) > 1:
        lgr.error("Multiple jails found for" " {}:".format(jail))
        for t, u in sorted(_jail.items()):
            lgr.error("  {} ({})".format(u, t))
        raise RuntimeError()
    else:
        lgr.critical("{} not found!".format(jail))
        exit(1)

    # Looks like foo/iocage/jails/df0ef69a-57b6-4480-b1f8-88f7b6febbdf@BAR
    target = "{}{}@{}".format(pool, path, name)
    try:
        checkoutput(["zfs", "get", "-H", "creation", target], stderr=PIPE)
    except CalledProcessError:
        lgr.critical("Snapshot {} does not exist!".format(target))
        exit(1)

    if not force:
        lgr.warning("\nThis will destroy ALL data created since"
                    " {} was taken.".format(name) +
                    "\nIncluding ALL snapshots taken after"
                    " {} for {} ({}).".format(name, uuid, tag))
        if not click.confirm("\nAre you sure?"):
            exit()
    try:
        datasets = Popen([
            "zfs", "list", "-H", "-r", "-o", "name", "{}{}".format(pool, path)
        ],
                         stdout=PIPE,
                         stderr=PIPE).communicate()[0].decode("utf-8").split()

        for dataset in datasets:
            check_call(
                ["zfs", "rollback", "-r", "{}@{}".format(dataset, name)])

        lgr.info("Rolled back to: {}.".format(target))
    except CalledProcessError as err:
        lgr.error("{}".format(err))
        exit(1)
Beispiel #5
0
    def __check_datasets__(self):
        """
        Loops through the required datasets and if there is root
        privilege will then create them.
        """
        datasets = ("iocage", "iocage/download", "iocage/images",
                    "iocage/jails", "iocage/log", "iocage/releases",
                    "iocage/templates")

        mounts = checkoutput([
            "zfs", "get", "-o", "name,value", "-t", "filesystem", "-H",
            "mountpoint"
        ]).splitlines()

        mounts = dict([list(map(str, m.split("\t"))) for m in mounts])
        dups = {
            name: mount
            for name, mount in mounts.items() if mount == "/iocage"
        }

        for dataset in datasets:
            try:
                checkoutput([
                    "zfs", "get", "-H", "creation", "{}/{}".format(
                        self.pool, dataset)
                ],
                            stderr=PIPE)
            except CalledProcessError:
                if os.geteuid() != 0:
                    raise RuntimeError("Run as root to create missing"
                                       " datasets!")

                if "deactivate" not in sys.argv[1:]:
                    self.lgr.info("Creating {}/{}".format(self.pool, dataset))
                    if dataset == "iocage":
                        if len(dups) != 0:
                            mount = "mountpoint=/{}/iocage".format(self.pool)
                        else:
                            mount = "mountpoint=/iocage"

                        Popen([
                            "zfs", "create", "-o", "compression=lz4", "-o",
                            mount, "{}/{}".format(self.pool, dataset)
                        ]).communicate()
                    else:
                        Popen([
                            "zfs", "create", "-o", "compression=lz4",
                            "{}/{}".format(self.pool, dataset)
                        ]).communicate()
Beispiel #6
0
    def exec_jail(self):
        if self.jail_user:
            flag = "-U"
            user = self.jail_user
        else:
            flag = "-u"
            user = self.host_user

        status, _ = IOCList().list_get_jid(self.uuid)
        conf = IOCJson(self.path).json_load()
        exec_fib = conf["exec_fib"]
        if not status:
            if not self.plugin and not self.skip:
                self.lgr.info("{} ({}) is not running, starting jail.".format(
                    self.uuid, self.tag))

            if conf["type"] in ("jail", "plugin"):
                IOCStart(self.uuid, self.tag, self.path, conf, silent=True)
            elif conf["type"] == "basejail":
                raise RuntimeError(
                    "Please run \"iocage migrate\" before trying"
                    " to start {} ({})".format(self.uuid, self.tag))
            elif conf["type"] == "template":
                raise RuntimeError(
                    "Please convert back to a jail before trying"
                    " to start {} ({})".format(self.uuid, self.tag))
            else:
                raise RuntimeError("{} is not a supported jail type.".format(
                    conf["type"]))
            self.lgr.info("\nCommand output:")

        if self.plugin:
            try:
                checkoutput([
                    "setfib", exec_fib, "jexec", flag, user, f"ioc-{self.uuid}"
                ] + list(self.command),
                            stderr=STDOUT)
            except CalledProcessError as err:
                return err.output.decode("utf-8").rstrip()
        else:
            jexec = Popen(
                ["setfib", exec_fib, "jexec", flag, user, f"ioc-{self.uuid}"] +
                list(self.command),
                stdout=PIPE,
                stderr=PIPE)
            msg, err = jexec.communicate()

            return msg, err
Beispiel #7
0
def cli(dataset_type, header, _long, remote, http, plugins, _sort):
    """This passes the arg and calls the jail_datasets function."""
    freebsd_version = ioc_common.checkoutput(["freebsd-version"])

    if dataset_type is None:
        dataset_type = "all"

    if remote:
        if "HBSD" in freebsd_version:
            hardened = True
        else:
            hardened = False

        ioc_fetch.IOCFetch("", http=http,
                           hardened=hardened).fetch_release(_list=True)
    elif plugins:
        ioc_fetch.IOCFetch("").fetch_plugin_index("", _list=True)
    else:
        _list = ioc.IOCage().list(dataset_type, header, _long, _sort)

        if not header:
            if dataset_type == "base":
                for item in _list:
                    ioc_common.logit({"level": "INFO", "message": item})
            else:
                for item in _list:
                    ioc_common.logit({
                        "level": "INFO",
                        "message": "\t".join(item)
                    })
        else:
            ioc_common.logit({"level": "INFO", "message": _list})
Beispiel #8
0
    def clean_templates(self):
        """Cleans all jails and their respective snapshots."""
        self.__clean_stop_jails__()

        datasets = checkoutput(["zfs", "get", "-o", "name,value", "-t",
                                "filesystem", "-H",
                                "origin"]).splitlines()

        datasets = dict([list(map(str, c.split("\t"))) for c in datasets])
        children_dict = {name: mount for name, mount in datasets.items() if
                         "{}/iocage/templates".format(self.pool) in mount}
        template_dict = {name: mount for name, mount in datasets.items() if
                         "{}/iocage/releases".format(self.pool) in mount}

        for jail in children_dict.keys():
            if "/jails" in jail:
                jail = jail.rstrip("/root")
                uuid = jail.split("/")[3]
                path = jail.replace("{}/iocage".format(self.pool),
                                    self.iocroot)
                conf = IOCJson(path).json_load()
                tag = conf["tag"]

                IOCDestroy(uuid, tag, path, silent=True).destroy_jail()

        for template in template_dict.keys():
            if "/templates" in template:
                template = template.rstrip("/root")
                path = template.replace("{}/iocage".format(self.pool),
                                        self.iocroot)
                conf = IOCJson(path).json_load()
                uuid = conf["host_hostuuid"]
                tag = conf["tag"]

                IOCDestroy(uuid, tag, path, silent=True).destroy_jail()
Beispiel #9
0
def list_cmd(dataset_type, header, _long, remote, http, plugins):
    """This passes the arg and calls the jail_datasets function."""
    lgr = logging.getLogger('ioc_cli_list')
    freebsd_version = checkoutput(["freebsd-version"])

    if dataset_type is None:
        dataset_type = "all"

    if remote:
        if "HBSD" in freebsd_version:
            hardened = True
        else:
            hardened = False

        IOCFetch("", http=http, hardened=hardened).fetch_release(_list=True)
    elif plugins:
        IOCFetch("").fetch_plugin_index("", _list=True)
    else:
        _list = IOCList(dataset_type, header, _long).list_datasets()

        if not header:
            if dataset_type == "base":
                for item in _list:
                    lgr.info(item)
            else:
                for item in _list:
                    lgr.info("\t".join(item))
        else:
            lgr.info(_list)
Beispiel #10
0
    def json_plugin_get_value(self, prop):
        from iocage.lib.ioc_exec import IOCExec

        pool, iocroot = _get_pool_and_iocroot()
        conf = self.json_load()
        uuid = conf["host_hostuuid"]
        tag = conf["tag"]
        _path = checkoutput([
            "zfs", "get", "-H", "-o", "value", "mountpoint",
            "{}/iocage/jails/{}".format(pool, uuid)
        ]).rstrip()
        # Plugin variables
        settings = self.json_plugin_load()
        serviceget = settings["serviceget"]
        prop_error = ".".join(prop)

        if "options" in prop:
            _prop = prop[1:]
        else:
            _prop = prop

        prop_cmd = "{},{}".format(serviceget, ",".join(_prop)).split(",")
        try:
            if prop[0] != "all":
                if len(_prop) > 1:
                    return get_nested_key(settings, prop)
                else:
                    return IOCExec(prop_cmd, uuid, tag, _path).exec_jail()
            else:
                return settings
        except KeyError:
            raise RuntimeError(
                "Key: \"{}\" does not exist!".format(prop_error))
Beispiel #11
0
 def list_get_jid(cls, uuid):
     """Return a tuple containing True or False and the jail's id or '-'."""
     try:
         jid = checkoutput(["jls", "-j", "ioc-{}".format(uuid)],
                           stderr=PIPE).split()[5]
         return (True, jid)
     except CalledProcessError:
         return (False, "-")
Beispiel #12
0
    def json_plugin_set_value(self, prop):
        from iocage.lib.ioc_exec import IOCExec
        from iocage.lib.ioc_list import IOCList

        pool, iocroot = _get_pool_and_iocroot()
        conf = self.json_load()
        uuid = conf["host_hostuuid"]
        tag = conf["tag"]
        _path = checkoutput([
            "zfs", "get", "-H", "-o", "value", "mountpoint",
            "{}/iocage/jails/{}".format(pool, uuid)
        ]).rstrip()
        status, _ = IOCList().list_get_jid(uuid)

        # Plugin variables
        settings = self.json_plugin_load()
        serviceset = settings["serviceset"]
        servicerestart = settings["servicerestart"].split()
        keys, _, value = ".".join(prop).partition("=")
        prop = keys.split(".")
        restart = False

        if "options" in prop:
            prop = keys.split(".")[1:]

        prop_cmd = "{},{},{}".format(serviceset, ",".join(prop),
                                     value).split(",")
        setting = settings["options"]

        try:
            while prop:
                current = prop[0]
                key = current
                prop.remove(current)

                if not prop:
                    if setting[current]:
                        try:
                            restart = setting[current]["requirerestart"]
                        except KeyError:
                            pass
                else:
                    setting = setting[current]

            if status:
                # IOCExec will not show this if it doesn't start the jail.
                self.lgr.info("Command output:")
            IOCExec(prop_cmd, uuid, tag, _path).exec_jail()

            if restart:
                self.lgr.info("\n-- Restarting service --")
                self.lgr.info("Command output:")
                IOCExec(servicerestart, uuid, tag, _path).exec_jail()

            self.lgr.info("\nKey: {} has been updated to {}".format(
                keys, value))
        except KeyError:
            raise RuntimeError("Key: \"{}\" does not exist!".format(key))
Beispiel #13
0
    def json_convert_from_zfs(self, uuid, skip=False):
        """Convert to JSON. Accepts a jail UUID"""
        pool, _ = _get_pool_and_iocroot()
        dataset = "{}/iocage/jails/{}".format(pool, uuid)
        jail_zfs_prop = "org.freebsd.iocage:jail_zfs_dataset"

        if geteuid() != 0:
            raise RuntimeError("You need to be root to convert the"
                               " configurations to the new format!")

        cmd = ["zfs", "get", "-H", "-o", "property,value", "all", dataset]

        regex = re.compile("org.freebsd.iocage")

        zfs_get = Popen(
            cmd, stdout=PIPE).communicate()[0].decode("utf-8").split("\n")

        # Find each of the props we want to convert.
        props = [p for p in zfs_get if re.search(regex, p)]

        key_and_value = {"host_domainname": "none"}

        for prop in props:
            prop = prop.partition(":")
            key = prop[2].split("\t")[0]
            value = prop[2].split("\t")[1].strip()

            if key == "type":
                if value == "basejail":
                    # These were just clones on master.
                    value = "jail"
                    key_and_value["basejail"] = "yes"
            key_and_value[key] = value

        if not skip:
            # Set jailed=off and move the jailed dataset.
            checkoutput(
                ["zfs", "set", "jailed=off", "{}/root/data".format(dataset)],
                stderr=PIPE)
            checkoutput([
                "zfs", "rename", "-f", "{}/root/data".format(dataset),
                "{}/data".format(dataset)
            ],
                        stderr=PIPE)
            checkoutput([
                "zfs", "set", "{}=iocage/jails/{}/data".format(
                    jail_zfs_prop, uuid), "{}/data".format(dataset)
            ],
                        stderr=PIPE)
            checkoutput(["zfs", "set", "jailed=on", "{}/data".format(dataset)],
                        stderr=PIPE)

        key_and_value["jail_zfs_dataset"] = "iocage/jails/{}/data".format(uuid)

        self.json_write(key_and_value)
Beispiel #14
0
def hardened(request):
    """Have fetch expect the default HardeneBSD layout instead."""
    freebsd_version = checkoutput(["freebsd-version"])

    if "HBSD" in freebsd_version:
        _hardened = True
    else:
        _hardened = False

    return _hardened
Beispiel #15
0
def update_cmd(jail):
    """Runs update with the command given inside the specified jail."""
    lgr = ioc_logger.Logger('ioc_cli_update').getLogger()

    jails, paths = IOCList("uuid").list_datasets()
    _jail = {
        tag: uuid
        for (tag, uuid) in jails.items()
        if uuid.startswith(jail) or tag == jail
    }

    if len(_jail) == 1:
        tag, uuid = next(iter(_jail.items()))
        path = paths[tag]
    elif len(_jail) > 1:
        lgr.error("Multiple jails found for" " {}:".format(jail))
        for t, u in sorted(_jail.items()):
            lgr.critical("  {} ({})".format(u, t))
        exit(1)
    else:
        lgr.critical("{} not found!".format(jail))
        exit(1)

    freebsd_version = checkoutput(["freebsd-version"])
    status, jid = IOCList.list_get_jid(uuid)
    conf = IOCJson(path).json_load()
    started = False

    if conf["type"] == "jail":
        if not status:
            IOCStart(uuid, tag, path, conf, silent=True)
            status, jid = IOCList.list_get_jid(uuid)
            started = True
    elif conf["type"] == "basejail":
        lgr.critical("Please run \"iocage migrate\" before trying"
                     " to update {} ({})".format(uuid, tag))
        exit(1)
    elif conf["type"] == "template":
        lgr.critical("Please convert back to a jail before trying"
                     " to update {} ({})".format(uuid, tag))
        exit(1)
    else:
        lgr.critical("{} is not a supported jail type.".format(conf["type"]))
        exit(1)

    if "HBSD" in freebsd_version:
        Popen(["hbsd-update", "-j", jid]).communicate()

        if started:
            IOCStop(uuid, tag, path, conf, silent=True)
    else:
        IOCFetch(conf["cloned_release"]).fetch_update(True, uuid, tag)

        if started:
            IOCStop(uuid, tag, path, conf, silent=True)
Beispiel #16
0
    def fetch_plugin_index(self, props, _list=False):
        if self.server == "ftp.freebsd.org":
            git_server = "https://github.com/iXsystems/iocage-ix-plugins.git"
        else:
            git_server = self.server

        try:
            checkoutput([
                "git", "clone", git_server, "{}/.plugin_index".format(
                    self.iocroot)
            ],
                        stderr=STDOUT)
        except CalledProcessError as err:
            if "already exists" in err.output.decode("utf-8").rstrip():
                try:
                    checkoutput([
                        "git", "-C", "{}/.plugin_index".format(self.iocroot),
                        "pull"
                    ],
                                stderr=STDOUT)
                except CalledProcessError as err:
                    raise RuntimeError("{}".format(
                        err.output.decode("utf-8").rstrip()))
            else:
                raise RuntimeError("{}".format(
                    err.output.decode("utf-8").rstrip()))

        with open("{}/.plugin_index/INDEX".format(self.iocroot), "r") as \
                plugins:
            plugins = json.load(plugins)

        _plugins = self.__fetch_sort_plugin__(plugins)
        for p in _plugins:
            self.lgr.info("[{}] {}".format(_plugins.index(p), p))

        if _list:
            return

        plugin = input("\nWhich plugin do you want to create? (EXIT) ")
        plugin = self.__fetch_validate_plugin__(plugin.lower(), _plugins)
        self.fetch_plugin(
            "{}/.plugin_index/{}.json".format(self.iocroot, plugin), props, 0)
Beispiel #17
0
def cli(dataset_type, header, _long, remote, http, plugins, _sort, quick,
        official):
    """This passes the arg and calls the jail_datasets function."""
    freebsd_version = ioc_common.checkoutput(["freebsd-version"])
    iocage = ioc.IOCage(exit_on_error=True, skip_jails=True)

    if dataset_type is None:
        dataset_type = "all"

    if remote and not plugins:
        if "HBSD" in freebsd_version:
            hardened = True
        else:
            hardened = False

        _list = iocage.fetch(list=True,
                             remote=True,
                             http=http,
                             hardened=hardened)
        header = False

    if plugins and remote:
        _list = iocage.fetch(list=True,
                             remote=True,
                             header=header,
                             _long=_long,
                             plugin_file=True,
                             official=official)
    elif not remote:
        _list = iocage.list(dataset_type,
                            header,
                            _long,
                            _sort,
                            plugin=plugins,
                            quick=quick)

    if not header:
        if dataset_type == "base":
            for item in _list:
                ioc_common.logit({"level": "INFO", "message": item})
        else:
            for item in _list:
                if remote and not plugins:
                    ioc_common.logit({"level": "INFO", "message": item})
                else:
                    ioc_common.logit({
                        "level": "INFO",
                        "message": "\t".join(item)
                    })
    else:
        ioc_common.logit({"level": "INFO", "message": _list})
Beispiel #18
0
 def __init__(self, conf, new_release, path):
     self.lgr = logging.getLogger("ioc_upgrade")
     self.pool = IOCJson().json_get_value("pool")
     self.iocroot = IOCJson(self.pool).json_get_value("iocroot")
     self.freebsd_version = checkoutput(["freebsd-version"])
     self.conf = conf
     self.uuid = conf["host_hostuuid"]
     self.host_release = os.uname()[2]
     self.jail_release = conf["cloned_release"]
     self.new_release = new_release
     self.path = path
     self.status, self.jid = IOCList.list_get_jid(self.uuid)
     self._freebsd_version = f"{self.iocroot}/releases/" \
                             f"{new_release}/root/bin/freebsd-version"
Beispiel #19
0
def list_cmd(dataset_type, header, _long, remote, http, plugins):
    """This passes the arg and calls the jail_datasets function."""
    freebsd_version = checkoutput(["freebsd-version"])

    if dataset_type is None:
        dataset_type = "all"

    if remote:
        if "HBSD" in freebsd_version:
            hardened = True
        else:
            hardened = False

        IOCFetch("", http=http, hardened=hardened).fetch_release(
            _list=True)
    elif plugins:
        IOCFetch("").fetch_plugin_index("", _list=True)
    else:
        IOCList(dataset_type, header, _long).list_datasets()
Beispiel #20
0
    def clean_jails(self):
        """Cleans all jails and their respective snapshots."""
        self.__clean_stop_jails__()

        # Faster then one by one deletion
        try:
            checkoutput(["zfs", "destroy", "-r", "-f",
                         "{}/iocage/jails".format(self.pool)],
                        stderr=STDOUT)
            checkoutput(["zfs", "destroy", "-r", "-f",
                         "{}/iocage/templates".format(self.pool)],
                        stderr=STDOUT)
        except CalledProcessError as err:
            if "snapshot" not in err.output.decode("utf-8").rstrip():
                raise RuntimeError(
                    "ERROR: {}".format(err.output.decode("utf-8").rstrip()))

        # Faster then one by one deletion
        try:
            checkoutput(["zfs", "destroy", "-R", "-f",
                         "{}/iocage/releases@%".format(self.pool)],
                        stderr=STDOUT)
        except CalledProcessError as err:
            if "snapshot" not in err.output.decode("utf-8").rstrip():
                raise RuntimeError(
                    "ERROR: {}".format(err.output.decode("utf-8").rstrip()))

        try:
            check_call(["zfs", "create", "-o", "compression=lz4",
                        "{}/iocage/jails".format(self.pool)])
        except CalledProcessError:
            raise RuntimeError("ERROR: Creating {}/iocage/jails "
                               "failed!".format(self.pool))

        if os.path.exists("{}/tags".format(self.iocroot)):
            shutil.rmtree("{}/tags".format(self.iocroot), ignore_errors=True)
            os.mkdir("{}/tags".format(self.iocroot))

        if os.path.exists("{}/log".format(self.iocroot)):
            shutil.rmtree("{}/log".format(self.iocroot), ignore_errors=True)
Beispiel #21
0
def fetch_cmd(http, _file, server, user, password, auth, verify, release,
              plugins, plugin_file, root_dir, props, count, update, eol,
              files):
    """CLI command that calls fetch_release()"""
    freebsd_version = checkoutput(["freebsd-version"])
    arch = os.uname()[4]

    lgr = ioc_logger.Logger("ioc_cli_fetch").getLogger()

    if not files:
        if arch == "arm64":
            files = ("MANIFEST", "base.txz", "doc.txz")
        else:
            files = ("MANIFEST", "base.txz", "lib32.txz", "doc.txz")

    if "HBSD" in freebsd_version:
        if server == "ftp.freebsd.org":
            hardened = True
        else:
            hardened = False
    else:
        hardened = False

    if plugins or plugin_file:
        if plugin_file:
            try:
                with open(plugin_file) as f:
                    return json.load(f)
            except FileNotFoundError:
                lgr.critical("Please supply a file before any properties.")
                exit(1)
            except json.decoder.JSONDecodeError:
                lgr.critical("Invalid JSON file supplied, please supply a "
                             "correctly formatted JSON file.")
                exit(1)

        ip = [
            x for x in props
            if x.startswith("ip4_addr") or x.startswith("ip6_addr")
        ]
        if not ip:
            lgr.critical("An IP address is needed to fetch a "
                         "plugin!\nPlease specify "
                         "ip(4|6)_addr=\"INTERFACE|IPADDRESS\"!")
            exit(1)
        if plugins:
            IOCFetch(release=None).fetch_plugin_index(props)
            exit()

        if count == 1:
            IOCFetch("",
                     server,
                     user,
                     password,
                     auth,
                     root_dir,
                     http=http,
                     _file=_file,
                     verify=verify,
                     hardened=hardened,
                     update=update,
                     eol=eol,
                     files=files).fetch_plugin(plugin_file, props, 0)
        else:
            for j in range(1, count + 1):
                IOCFetch("",
                         server,
                         user,
                         password,
                         auth,
                         root_dir,
                         http=http,
                         _file=_file,
                         verify=verify,
                         hardened=hardened,
                         update=update,
                         eol=eol,
                         files=files).fetch_plugin(plugin_file, props, j)
    else:
        IOCFetch(release,
                 server,
                 user,
                 password,
                 auth,
                 root_dir,
                 http=http,
                 _file=_file,
                 verify=verify,
                 hardened=hardened,
                 update=update,
                 eol=eol,
                 files=files).fetch_release()
Beispiel #22
0
def create_cmd(release, template, count, props, pkglist, basejail, empty,
               short, uuid):
    lgr = ioc_logger.Logger('ioc_cli_create').getLogger()

    if short and uuid:
        lgr.critical(
            "Can't use --short (-s) and --uuid (-u) at the same time!")
        exit(1)

    if not template and not release and not empty:
        lgr.critical("Must supply either --template (-t) or --release (-r)!")
        exit(1)

    if release and "=" in release:
        lgr.critical("Please supply a valid RELEASE!")
        exit(1)

    if template:
        # We don't really care it's not a RELEASE at this point.
        release = template

    if pkglist:
        _pkgformat = """
{
    "pkgs": [
    "foo",
    "bar"
    ]
}"""

        if not os.path.isfile(pkglist):
            lgr.critical("{} does not exist!\nPlease supply a JSON file "
                         "with the format:{}".format(pkglist, _pkgformat))
            exit(1)
        else:
            try:
                # Just try to open the JSON with the right key.
                with open(pkglist, "r") as p:
                    json.load(p)["pkgs"]  # noqa
            except JSONDecodeError:
                lgr.critical("Please supply a valid JSON file with the"
                             f" format:\n{_pkgformat}")
                exit(1)

    pool = IOCJson().json_get_value("pool")
    iocroot = IOCJson(pool).json_get_value("iocroot")

    if not os.path.isdir(
            f"{iocroot}/releases/{release}") and not template and not empty:
        freebsd_version = checkoutput(["freebsd-version"])

        if "HBSD" in freebsd_version:
            hardened = True
        else:
            hardened = False

        IOCFetch(release, hardened=hardened).fetch_release()

    if empty:
        release = "EMPTY"

    if count == 1:
        try:
            IOCCreate(release,
                      props,
                      0,
                      pkglist,
                      template=template,
                      short=short,
                      uuid=uuid,
                      basejail=basejail,
                      empty=empty).create_jail()
        except RuntimeError as err:
            lgr.error(err)
            if template:
                lgr.info("Created Templates:")
                templates = IOCList("template", hdr=False).list_datasets()
                for temp in templates:
                    lgr.info("  {}".format(temp[3]))
    else:
        for j in range(1, count + 1):
            try:
                IOCCreate(release,
                          props,
                          j,
                          pkglist,
                          template=template,
                          short=short,
                          basejail=basejail,
                          empty=empty).create_jail()
            except RuntimeError as err:
                lgr.error(err)
                if template:
                    lgr.info("Created Templates:")
                    templates = IOCList("template", hdr=False).list_datasets()
                    for temp in templates:
                        lgr.info("  {}".format(temp[3]))
                exit(1)
Beispiel #23
0
    def start_network(self, vnet):
        if vnet:
            _, jid = IOCList().list_get_jid(self.uuid)
            ip4_addr = self.get("ip4_addr")
            defaultgw = self.get("defaultrouter")
            nics = self.get("interfaces").split(",")

            for n in nics:
                nic, bridge = n.split(":")

                try:
                    memberif = Popen(["ifconfig", bridge],
                                     stdout=PIPE).communicate()[0].decode(
                                         "utf-8").split()[40]
                    membermtu = Popen([
                        "ifconfig", memberif
                    ], stdout=PIPE).communicate()[0].decode("utf-8").split()[5]

                    for ip in ip4_addr.split(','):
                        iface, ip4 = ip.split("|")
                        if nic != iface:
                            err = "\n  ERROR: Invalid interface supplied: {}"
                            self.lgr.error(err.format(iface))
                            self.lgr.error("  Did you mean {}?\n".format(nic))
                            equal = False
                        else:
                            equal = True

                        if equal:
                            mac_a, mac_b = self.__start_generate_vnet_mac__(
                                nic)
                            epair_a_cmd = ["ifconfig", "epair", "create"]
                            epair_a = Popen(epair_a_cmd,
                                            stdout=PIPE).communicate()[0]
                            epair_a = epair_a.strip()
                            epair_b = re.sub(b"a$", b"b", epair_a)

                            try:
                                # Host side
                                checkoutput([
                                    "ifconfig", epair_a, "name",
                                    "{}:{}".format(nic, jid), "mtu", membermtu
                                ],
                                            stderr=STDOUT)
                                checkoutput([
                                    "ifconfig", "{}:{}".format(nic, jid),
                                    "link", mac_a
                                ],
                                            stderr=STDOUT)
                                checkoutput([
                                    "ifconfig", "{}:{}".format(nic, jid),
                                    "description", "associated with jail:"
                                    " {} ({})".format(self.uuid,
                                                      self.conf["tag"])
                                ],
                                            stderr=STDOUT)

                                # Jail side
                                checkoutput([
                                    "ifconfig", epair_b, "vnet",
                                    "ioc-{}".format(self.uuid)
                                ],
                                            stderr=STDOUT)
                                checkoutput([
                                    "jexec", "ioc-{}".format(
                                        self.uuid), "ifconfig", epair_b,
                                    "name", nic, "mtu", membermtu
                                ],
                                            stderr=STDOUT)
                                checkoutput([
                                    "jexec", "ioc-{}".format(self.uuid),
                                    "ifconfig", nic, "link", mac_b
                                ],
                                            stderr=STDOUT)

                                checkoutput([
                                    "ifconfig", bridge, "addm", "{}:{}".format(
                                        nic, jid), "up"
                                ],
                                            stderr=STDOUT)
                                checkoutput([
                                    "ifconfig", "{}:{}".format(nic, jid), "up"
                                ],
                                            stderr=STDOUT)
                                checkoutput([
                                    "jexec", "ioc-{}".format(self.uuid),
                                    "ifconfig", iface, ip4, "up"
                                ],
                                            stderr=STDOUT)
                                checkoutput([
                                    "jexec", "ioc-{}".format(self.uuid),
                                    "route", "add", "default", defaultgw
                                ],
                                            stderr=STDOUT)
                            except CalledProcessError as err:
                                raise RuntimeError("ERROR: {}".format(
                                    err.output.decode("utf-8").rstrip()))
                except:
                    pass
Beispiel #24
0
def cli(force, delete):
    """Migrates all the iocage_legacy develop basejails to clone jails."""
    # TODO: Move to API
    jails = ioc_list.IOCList("uuid").list_datasets()

    if not force:
        ioc_common.logit({
            "level":
            "WARNING",
            "message":
            "\nThis will migrate ALL iocage-legacy develop"
            " basejails to clonejails, it can take a long"
            " time!\nPlease make sure you are not running"
            " this on iocage-legacy 1.7.6 basejails."
        })

        if not click.confirm("\nAre you sure?"):
            exit()

    for uuid, path in jails.items():
        pool = ioc_json.IOCJson().json_get_value("pool")
        iocroot = ioc_json.IOCJson(pool).json_get_value("iocroot")
        jail = f"{pool}/iocage/jails/{uuid}"
        jail_old = f"{pool}/iocage/jails_old/{uuid}"
        conf = ioc_json.IOCJson(path).json_load()

        try:
            tag = conf["tag"]
        except KeyError:
            # These are actually NEW jails.

            continue

        release = conf["cloned_release"]

        if conf["type"] == "basejail":
            try:
                ioc_common.checkoutput(["zfs", "rename", "-p", jail, jail_old],
                                       stderr=su.STDOUT)
            except su.CalledProcessError as err:
                ioc_common.logit(
                    {
                        "level": "EXCEPTION",
                        "message": f"{err.output.decode('utf-8').strip()}"
                    },
                    exit_on_error=True)

            try:
                os.remove(f"{iocroot}/tags/{tag}")
            except OSError:
                pass

            date_fmt_legacy = "%Y-%m-%d@%H:%M:%S"

            # We don't want to rename datasets to a bunch of dates.
            try:
                datetime.datetime.strptime(tag, date_fmt_legacy)
                _name = str(uuid.uuid4())
            except ValueError:
                # They already named this jail, making it like our new ones.
                _name = tag

            new_uuid = ioc_create.IOCCreate(release,
                                            "",
                                            0,
                                            None,
                                            migrate=True,
                                            config=conf,
                                            silent=True,
                                            uuid=_name,
                                            exit_on_error=True).create_jail()
            new_prop = ioc_json.IOCJson(f"{iocroot}/jails/{new_uuid}",
                                        silent=True).json_set_value
            new_prop(f"host_hostname={new_uuid}")
            new_prop(f"host_hostuuid={new_uuid}")
            new_prop("type=jail")
            new_prop(f"jail_zfs_dataset={iocroot}/jails/{new_uuid}/data")

            ioc_common.logit({
                "level":
                "INFO",
                "message":
                f"Copying files for {new_uuid}, please wait..."
            })

            ioc_common.copytree(f"{iocroot}/jails_old/{uuid}/root",
                                f"{iocroot}/jails/{new_uuid}/root",
                                symlinks=True)

            shutil.copy(f"{iocroot}/jails_old/{uuid}/fstab",
                        f"{iocroot}/jails/{new_uuid}/fstab")

            for line in fileinput.input(
                    f"{iocroot}/jails/{new_uuid}/root/etc/"
                    "rc.conf", inplace=1):
                print(
                    line.replace(f'hostname="{uuid}"',
                                 f'hostname="{new_uuid}"').rstrip())

            if delete:
                try:
                    ioc_common.checkoutput(
                        ["zfs", "destroy", "-r", "-f", jail_old],
                        stderr=su.STDOUT)
                except su.CalledProcessError as err:
                    raise RuntimeError(
                        f"{err.output.decode('utf-8').rstrip()}")

                try:
                    su.check_call([
                        "zfs", "destroy", "-r", "-f",
                        f"{pool}/iocage/jails_old"
                    ])
                except su.CalledProcessError:
                    # We just want the top level dataset gone, no big deal.
                    pass

            ioc_common.logit({
                "level":
                "INFO",
                "message":
                f"{uuid} ({tag}) migrated to {new_uuid}!\n"
            })
Beispiel #25
0
    def json_set_value(self, prop, create_func=False):
        """Set a property for the specified jail."""
        # Circular dep! Meh.
        from iocage.lib.ioc_list import IOCList
        from iocage.lib.ioc_create import IOCCreate
        key, _, value = prop.partition("=")

        conf = self.json_load()
        old_tag = conf["tag"]
        uuid = conf["host_hostuuid"]
        status, jid = IOCList.list_get_jid(uuid)
        conf[key] = value
        sysctls_cmd = ["sysctl", "-d", "security.jail.param"]
        jail_param_regex = re.compile("security.jail.param.")
        sysctls_list = Popen(
            sysctls_cmd, stdout=PIPE).communicate()[0].decode("utf-8").split()
        jail_params = [
            p.replace("security.jail.param.", "").replace(":", "")
            for p in sysctls_list if re.match(jail_param_regex, p)
        ]
        single_period = [
            "allow_raw_sockets", "allow_socket_af", "allow_set_hostname"
        ]

        if not create_func:
            if key == "tag":
                conf["tag"] = IOCCreate("", prop,
                                        0).create_link(conf["host_hostuuid"],
                                                       value,
                                                       old_tag=old_tag)
                tag = conf["tag"]

        if key == "template":
            pool, iocroot = _get_pool_and_iocroot()
            old_location = "{}/iocage/jails/{}".format(pool, uuid)
            new_location = "{}/iocage/templates/{}".format(pool, old_tag)

            if status:
                raise RuntimeError(f"{uuid} ({old_tag}) is running.\nPlease"
                                   "stop it first!")

            jails, paths = IOCList("uuid").list_datasets()
            for j in jails:
                _uuid = jails[j]
                _path = f"{paths[j]}/root"
                t_old_path = f"{old_location}/root@{_uuid}"
                t_path = f"{new_location}/root@{_uuid}"

                if _uuid == uuid:
                    continue

                origin = checkoutput(
                    ["zfs", "get", "-H", "-o", "value", "origin",
                     _path]).rstrip()

                if origin == t_old_path or origin == t_path:
                    _status, _ = IOCList.list_get_jid(_uuid)

                    if _status:
                        raise RuntimeError(f"CHILD: {_uuid} ({j}) is"
                                           f" running.\nPlease stop it first!")
            if value == "yes":
                try:
                    checkoutput(
                        ["zfs", "rename", "-p", old_location, new_location],
                        stderr=STDOUT)
                    conf["type"] = "template"

                    self.location = new_location.lstrip(pool).replace(
                        "/iocage", iocroot)
                except CalledProcessError as err:
                    raise RuntimeError("{}".format(
                        err.output.decode("utf-8").rstrip()))

                self.lgr.info("{} ({}) converted to a template.".format(
                    uuid, old_tag))
                self.lgr.disabled = True
            elif value == "no":
                try:
                    checkoutput(
                        ["zfs", "rename", "-p", new_location, old_location],
                        stderr=STDOUT)
                    conf["type"] = "jail"

                    self.location = old_location.lstrip(pool).replace(
                        "/iocage", iocroot)
                except CalledProcessError as err:
                    raise RuntimeError("{}".format(
                        err.output.decode("utf-8").rstrip()))

                self.lgr.info("{} ({}) converted to a jail.".format(
                    uuid, old_tag))
                self.lgr.disabled = True

        self.json_check_prop(key, value, conf)
        self.json_write(conf)
        self.lgr.info("Property: {} has been updated to {}".format(key, value))

        # Used for import
        if not create_func:
            if key == "tag":
                return tag

        # We can attempt to set a property in realtime to jail.
        if status:
            if key in single_period:
                key = key.replace("_", ".", 1)
            else:
                key = key.replace("_", ".")

            if key in jail_params:
                try:
                    checkoutput([
                        "jail", "-m", "jid={}".format(jid), "{}={}".format(
                            key, value)
                    ],
                                stderr=STDOUT)
                except CalledProcessError as err:
                    raise RuntimeError("{}".format(
                        err.output.decode("utf-8").rstrip()))
Beispiel #26
0
    def json_check_prop(self, key, value, conf):
        """
        Checks if the property matches known good values, if it's the
        CLI, deny setting any properties not in this list.
        """
        props = {
            # Network properties
            "interfaces": (":", ","),
            "host_domainname": ("string", ),
            "host_hostname": ("string", ),
            "exec_fib": ("string", ),
            "ip4_addr": ("|", ),
            "ip4_saddrsel": (
                "0",
                "1",
            ),
            "ip4": ("new", "inherit", "none"),
            "ip6_addr": ("|", ),
            "ip6_saddrsel": ("0", "1"),
            "ip6": ("new", "inherit", "none"),
            "defaultrouter": ("string", ),
            "defaultrouter6": ("string", ),
            "resolver": ("string", ),
            "mac_prefix": ("string", ),
            "vnet0_mac": ("string", ),
            "vnet1_mac": ("string", ),
            "vnet2_mac": ("string", ),
            "vnet3_mac": ("string", ),
            # Jail Properties
            "devfs_ruleset": ("string", ),
            "exec_start": ("string", ),
            "exec_stop": ("string", ),
            "exec_prestart": ("string", ),
            "exec_poststart": ("string", ),
            "exec_prestop": ("string", ),
            "exec_poststop": ("string", ),
            "exec_clean": ("0", "1"),
            "exec_timeout": ("string", ),
            "stop_timeout": ("string", ),
            "exec_jail_user": ("string", ),
            "exec_system_jail_user": ("string", ),
            "exec_system_user": ("string", ),
            "mount_devfs": ("0", "1"),
            "mount_fdescfs": ("0", "1"),
            "enforce_statfs": ("0", "1", "2"),
            "children_max": ("string", ),
            "login_flags": ("string", ),
            "securelevel": ("string", ),
            "sysvmsg": ("new", "inherit", "disable"),
            "sysvsem": ("new", "inherit", "disable"),
            "sysvshm": ("new", "inherit", "disable"),
            "allow_set_hostname": ("0", "1"),
            "allow_sysvipc": ("0", "1"),
            "allow_raw_sockets": ("0", "1"),
            "allow_chflags": ("0", "1"),
            "allow_mount": ("0", "1"),
            "allow_mount_devfs": ("0", "1"),
            "allow_mount_nullfs": ("0", "1"),
            "allow_mount_procfs": ("0", "1"),
            "allow_mount_tmpfs": ("0", "1"),
            "allow_mount_zfs": ("0", "1"),
            "allow_quotas": ("0", "1"),
            "allow_socket_af": ("0", "1"),
            # RCTL limits
            "cpuset": ("off", "on"),
            "rlimits": ("off", "on"),
            "memoryuse": ":",
            "memorylocked": ("off", "on"),
            "vmemoryuse": ("off", "on"),
            "maxproc": ("off", "on"),
            "cputime": ("off", "on"),
            "pcpu": ":",
            "datasize": ("off", "on"),
            "stacksize": ("off", "on"),
            "coredumpsize": ("off", "on"),
            "openfiles": ("off", "on"),
            "pseudoterminals": ("off", "on"),
            "swapuse": ("off", "on"),
            "nthr": ("off", "on"),
            "msgqqueued": ("off", "on"),
            "msgqsize": ("off", "on"),
            "nmsgq": ("off", "on"),
            "nsemop": ("off", "on"),
            "nshm": ("off", "on"),
            "shmsize": ("off", "on"),
            "wallclock": ("off", "on"),
            # Custom properties
            "tag": ("string", ),
            "bpf": ("off", "on"),
            "dhcp": ("off", "on"),
            "boot": ("off", "on"),
            "notes": ("string", ),
            "owner": ("string", ),
            "priority": str(tuple(range(1, 100))),
            "hostid": ("string", ),
            "jail_zfs": ("off", "on"),
            "jail_zfs_dataset": ("string", ),
            "jail_zfs_mountpoint": ("string", ),
            "mount_procfs": ("0", "1"),
            "mount_linprocfs": ("0", "1"),
            "vnet": ("off", "on"),
            "template": ("no", "yes"),
            "comment": ("string", )
        }

        zfs_props = {
            # ZFS Props
            "compression": "lz4",
            "origin": "readonly",
            "quota": "none",
            "mountpoint": "readonly",
            "compressratio": "readonly",
            "available": "readonly",
            "used": "readonly",
            "dedup": "off",
            "reservation": "none",
        }

        if key in zfs_props.keys():
            pool, _ = _get_pool_and_iocroot()

            if conf["template"] == "yes":
                _type = "templates"
                uuid = conf["tag"]  # I know, but it's easier this way.
            else:
                _type = "jails"
                uuid = conf["host_hostuuid"]

            if key == "quota":
                if value != "none" and not value.upper().endswith(
                    ("M", "G", "T")):
                    err = f"{value} should have a suffix ending in" \
                          " M, G, or T."
                    raise RuntimeError(err)

            checkoutput([
                "zfs", "set", f"{key}={value}", f"{pool}/iocage/{_type}/{uuid}"
            ])
            return

        if key in props.keys():
            # Either it contains what we expect, or it's a string.
            for k in props[key]:
                if k in value:
                    return

            if props[key][0] == "string":
                return
            else:
                err = f"{value} is not a valid value for {key}.\n"

                if self.cli:
                    self.lgr.error(f"{err}")
                else:
                    err = f"{err}"

                if key not in ("interfaces", "ip4_addr", "ip6_addr",
                               "memoryuse"):
                    msg = f"Value must be {' or '.join(props[key])}"

                    if not self.cli:
                        msg = err + msg

                    raise RuntimeError(msg)
                elif key == "ip4_addr":
                    msg = "IP address must contain both an interface and IP " \
                          "address.\nEXAMPLE: em0|192.168.1.10"

                    if not self.cli:
                        msg = err + msg

                    raise RuntimeError(msg)
                elif key == "ip6_addr":
                    msg = "IP address must contain both an interface and IP " \
                          "address.\nEXAMPLE: em0|fe80::5400:ff:fe54:1"

                    if not self.cli:
                        msg = err + msg

                    raise RuntimeError(msg)
                elif key == "interfaces":
                    msg = "Interfaces must be specified as a pair.\n" \
                          "EXAMPLE: vnet0:bridge0, vnet1:bridge1"

                    if not self.cli:
                        msg = err + msg

                    raise RuntimeError(msg)
                elif key == "memoryuse":
                    msg = "memoryuse requires at minimum a pair.\nEXAMPLE: " \
                          "8g:log"

                    if not self.cli:
                        msg = err + msg

                    raise RuntimeError(msg)
                else:
                    if self.cli:
                        exit(1)
        else:
            if self.cli:
                raise RuntimeError(f"{key} cannot be changed by the user.")
            else:
                if key not in conf.keys():
                    raise RuntimeError(f"{key} is not a valid property!")
Beispiel #27
0
    def __start_jail__(self):
        """
        Takes a UUID, and the user supplied name of a jail, the path and the
        configuration location. It then supplies the jail utility with that
        information in a format it can parse.

        start_jail also checks if the jail is already running, if the
        user wished for procfs or linprocfs to be mounted, and the user's
        specified data that is meant to populate resolv.conf
        will be copied into the jail.
        """
        status, _ = IOCList().list_get_jid(self.uuid)
        userland_version = float(uname()[2][:4])

        # If the jail is not running, let's do this thing.
        if not status:
            mount_procfs = self.conf["mount_procfs"]
            host_domainname = self.conf["host_domainname"]
            host_hostname = self.conf["host_hostname"]
            securelevel = self.conf["securelevel"]
            devfs_ruleset = self.conf["devfs_ruleset"]
            enforce_statfs = self.conf["enforce_statfs"]
            children_max = self.conf["children_max"]
            allow_set_hostname = self.conf["allow_set_hostname"]
            allow_sysvipc = self.conf["allow_sysvipc"]
            allow_raw_sockets = self.conf["allow_raw_sockets"]
            allow_chflags = self.conf["allow_chflags"]
            allow_mount = self.conf["allow_mount"]
            allow_mount_devfs = self.conf["allow_mount_devfs"]
            allow_mount_nullfs = self.conf["allow_mount_nullfs"]
            allow_mount_procfs = self.conf["allow_mount_procfs"]
            allow_mount_tmpfs = self.conf["allow_mount_tmpfs"]
            allow_mount_zfs = self.conf["allow_mount_zfs"]
            allow_quotas = self.conf["allow_quotas"]
            allow_socket_af = self.conf["allow_socket_af"]
            exec_prestart = self.start_findscript("prestart")
            exec_poststart = self.start_findscript("poststart")
            exec_prestop = self.start_findscript("prestop")
            exec_stop = self.conf["exec_stop"]
            exec_clean = self.conf["exec_clean"]
            exec_timeout = self.conf["exec_timeout"]
            stop_timeout = self.conf["stop_timeout"]
            mount_devfs = self.conf["mount_devfs"]
            mount_fdescfs = self.conf["mount_fdescfs"]
            sysvmsg = self.conf["sysvmsg"]
            sysvsem = self.conf["sysvsem"]
            sysvshm = self.conf["sysvshm"]

            if mount_procfs == "1":
                Popen([
                    "mount", "-t", "procfs", "proc", self.path + "/root/proc"
                ]).communicate()

            try:
                mount_linprocfs = self.conf["mount_linprocfs"]

                if mount_linprocfs == "1":
                    if not ospath.isdir("{}/root/compat/linux/proc".format(
                            self.path)):
                        original_path = getcwd()
                        chdir("{}/root".format(self.path))
                        makedirs("compat/linux/proc", 0o755)
                        chdir(original_path)
                    Popen([
                        "mount", "-t", "linprocfs", "linproc",
                        self.path + "/root/compat/linux/proc"
                    ]).communicate()
            except:
                pass

            if self.conf["jail_zfs"] == "on":
                allow_mount = "1"
                enforce_statfs = "1"
                allow_mount_zfs = "1"

                for jdataset in self.conf["jail_zfs_dataset"].split():
                    jdataset = jdataset.strip()

                    try:
                        check_call([
                            "zfs", "get", "-H", "creation", "{}/{}".format(
                                self.pool, jdataset)
                        ],
                                   stdout=PIPE,
                                   stderr=PIPE)
                    except CalledProcessError:
                        checkoutput([
                            "zfs", "create", "-o", "compression=lz4", "-o",
                            "mountpoint=none", "{}/{}".format(
                                self.pool, jdataset)
                        ],
                                    stderr=STDOUT)

                    try:
                        checkoutput([
                            "zfs", "set", "jailed=on", "{}/{}".format(
                                self.pool, jdataset)
                        ],
                                    stderr=STDOUT)
                    except CalledProcessError as err:
                        raise RuntimeError("ERROR: {}".format(
                            err.output.decode("utf-8").rstrip()))

            # FreeBSD 9.3 and under do not support this.
            if userland_version <= 9.3:
                tmpfs = ""
                fdescfs = ""
            else:
                tmpfs = "allow.mount.tmpfs={}".format(allow_mount_tmpfs)
                fdescfs = "mount.fdescfs={}".format(mount_fdescfs)

            # FreeBSD 10.3 and under do not support this.
            if userland_version <= 10.3:
                _sysvmsg = ""
                _sysvsem = ""
                _sysvshm = ""
            else:
                _sysvmsg = "sysvmsg={}".format(sysvmsg)
                _sysvsem = "sysvsem={}".format(sysvsem)
                _sysvshm = "sysvshm={}".format(sysvshm)

            if self.conf["vnet"] == "off":
                ip4_addr = self.conf["ip4_addr"]
                ip4_saddrsel = self.conf["ip4_saddrsel"]
                ip4 = self.conf["ip4"]
                ip6_addr = self.conf["ip6_addr"]
                ip6_saddrsel = self.conf["ip6_saddrsel"]
                ip6 = self.conf["ip6"]

                if ip4_addr == "none":
                    ip4_addr = ""

                if ip6_addr == "none":
                    ip6_addr = ""

                net = [
                    "ip4.addr={}".format(ip4_addr),
                    "ip4.saddrsel={}".format(ip4_saddrsel),
                    "ip4={}".format(ip4), "ip6.addr={}".format(ip6_addr),
                    "ip6.saddrsel={}".format(ip6_saddrsel),
                    "ip6={}".format(ip6)
                ]

                vnet = False
            else:
                net = ["vnet"]
                vnet = True

            self.lgr.info("* Starting {} ({})".format(self.uuid,
                                                      self.conf["tag"]))
            start = Popen([
                x for x in ["jail", "-c"] + net + [
                    "name=ioc-{}".format(self.uuid), "host.domainname={}".
                    format(host_domainname), "host.hostname={}".format(
                        host_hostname), "path={}/root".format(
                            self.path), "securelevel={}".format(securelevel),
                    "host.hostuuid={}".format(self.uuid), "devfs_ruleset={}".
                    format(devfs_ruleset), "enforce_statfs={}".format(
                        enforce_statfs), "children.max={}".format(
                            children_max), "allow.set_hostname={}".format(
                                allow_set_hostname), "allow.sysvipc={}".format(
                                    allow_sysvipc), _sysvmsg, _sysvsem,
                    _sysvshm, "allow.raw_sockets={}".format(allow_raw_sockets),
                    "allow.chflags={}".format(allow_chflags), "allow.mount={}".
                    format(allow_mount), "allow.mount.devfs={}".
                    format(allow_mount_devfs), "allow.mount.nullfs={}".format(
                        allow_mount_nullfs), "allow.mount.procfs={}".format(
                            allow_mount_procfs), tmpfs, "allow.mount.zfs={}".
                    format(allow_mount_zfs), "allow.quotas={}".format(
                        allow_quotas), "allow.socket_af={}".format(
                            allow_socket_af), "exec.prestart={}".format(
                                exec_prestart), "exec.poststart={}".format(
                                    exec_poststart), "exec.prestop={}".format(
                                        exec_prestop),
                    "exec.stop={}".format(exec_stop), "exec.clean={}".format(
                        exec_clean), "exec.timeout={}".format(exec_timeout),
                    "stop.timeout={}".format(stop_timeout),
                    "mount.fstab={}/fstab".format(self.path), "mount.devfs={}".
                    format(mount_devfs), fdescfs, "allow.dying",
                    "exec.consolelog={}/log/ioc-{}-console.log".format(
                        self.iocroot, self.uuid), "persist"
                ] if x != ''
            ])
            start.communicate()

            if start.returncode:
                self.lgr.info("  + Start FAILED")
            else:
                self.lgr.info("  + Started OK")

            os_path = "{}/root/dev/log".format(self.path)
            if not ospath.isfile(os_path) and not ospath.islink(os_path):
                original_path = getcwd()
                chdir("{}/root/dev".format(self.path))
                symlink("../var/run/log", "log")
                chdir(original_path)

            self.start_network(vnet)

            if self.conf["jail_zfs"] == "on":
                for jdataset in self.conf["jail_zfs_dataset"].split():
                    jdataset = jdataset.strip()
                    children = checkoutput([
                        "zfs", "list", "-H", "-r", "-o", "name", "-S", "name",
                        "{}/{}".format(self.pool, jdataset)
                    ])

                    try:
                        checkoutput([
                            "zfs", "jail", "ioc-{}".format(self.uuid),
                            "{}/{}".format(self.pool, jdataset)
                        ],
                                    stderr=STDOUT)
                    except CalledProcessError as err:
                        raise RuntimeError("ERROR: {}".format(
                            err.output.decode("utf-8").rstrip()))

                    for child in children.split():
                        child = child.strip()

                        try:
                            mountpoint = checkoutput([
                                "zfs", "get", "-H", "-o", "value",
                                "mountpoint",
                                "{}/{}".format(self.pool, jdataset)
                            ]).strip()
                            if mountpoint != "none":
                                checkoutput([
                                    "jexec", "ioc-{}".format(self.uuid), "zfs",
                                    "mount", child
                                ],
                                            stderr=STDOUT)
                        except CalledProcessError as err:
                            raise RuntimeError("ERROR: {}".format(
                                err.output.decode("utf-8").rstrip()))

            self.start_generate_resolv()
            # TODO: exec_fib support
            # This needs to be a list.
            exec_start = self.conf["exec_start"].split()

            with open("{}/log/{}-console.log".format(self.iocroot, self.uuid),
                      "a") as f:
                services = check_call(["jexec", "ioc-{}".format(self.uuid)] +
                                      exec_start,
                                      stdout=f,
                                      stderr=PIPE)
            if services:
                self.lgr.info("  + Starting services FAILED")
            else:
                self.lgr.info("  + Starting services OK")

            self.set("last_started={}".format(
                datetime.utcnow().strftime("%F %T")))
            # TODO: DHCP/BPF
        else:
            self.lgr.error("{} ({}) is already running!".format(
                self.uuid, self.conf["tag"]))
Beispiel #28
0
    def json_load(self):
        """Load the JSON at the location given. Returns a JSON object."""
        version = self.json_get_version()
        skip = False

        try:
            with open(self.location + "/config.json", "r") as conf:
                conf = json.load(conf)
        except FileNotFoundError:
            if path.isfile(self.location + "/config"):
                self.json_convert_from_ucl()

                with open(self.location + "/config.json", "r") as conf:
                    conf = json.load(conf)
            else:
                try:
                    dataset = self.location.split("/")

                    for d in dataset:
                        if len(d) == 36:
                            uuid = d
                        elif len(d) == 8:
                            # Hack88 migration to a perm short UUID.
                            pool, iocroot = _get_pool_and_iocroot()
                            from iocage.lib.ioc_list import IOCList

                            full_uuid = checkoutput([
                                "zfs", "get", "-H", "-o", "value",
                                "org.freebsd.iocage:host_hostuuid",
                                self.location
                            ]).rstrip()
                            jail_hostname = checkoutput([
                                "zfs", "get", "-H", "-o", "value",
                                "org.freebsd.iocage:host_hostname",
                                self.location
                            ]).rstrip()
                            short_uuid = full_uuid[:8]
                            full_dataset = "{}/iocage/jails/{}".format(
                                pool, full_uuid)
                            short_dataset = "{}/iocage/jails/{}".format(
                                pool, short_uuid)

                            self.json_convert_from_zfs(full_uuid)
                            with open(self.location + "/config.json",
                                      "r") as conf:
                                conf = json.load(conf)

                            self.lgr.info("hack88 is no longer supported."
                                          "\n{} is being converted to {} "
                                          "permanently.".format(
                                              full_dataset, short_dataset))

                            status, _ = IOCList().list_get_jid(full_uuid)
                            if status:
                                self.lgr.info(
                                    "Stopping jail to migrate UUIDs.")
                                from iocage.lib.ioc_stop import IOCStop
                                IOCStop(full_uuid,
                                        conf["tag"],
                                        self.location,
                                        conf,
                                        silent=True)

                            jail_zfs_prop = \
                                "org.freebsd.iocage:jail_zfs_dataset"
                            uuid_prop = "org.freebsd.iocage:host_hostuuid"
                            host_prop = "org.freebsd.iocage:host_hostname"

                            # Set jailed=off and move the jailed dataset.
                            checkoutput([
                                "zfs", "set", "jailed=off",
                                "{}/data".format(full_dataset)
                            ])

                            # We don't want to change a real hostname.
                            if jail_hostname == full_uuid:
                                checkoutput([
                                    "zfs", "set",
                                    "{}={}".format(host_prop,
                                                   short_uuid), full_dataset
                                ])

                            checkoutput([
                                "zfs", "set",
                                "{}={}".format(uuid_prop,
                                               short_uuid), full_dataset
                            ])
                            checkoutput([
                                "zfs", "set", "{}=iocage/jails/{}/data".format(
                                    jail_zfs_prop, short_uuid),
                                "{}/data".format(full_dataset)
                            ])
                            checkoutput([
                                "zfs", "rename", "-f", full_dataset,
                                short_dataset
                            ])
                            checkoutput([
                                "zfs", "set", "jailed=on",
                                "{}/data".format(short_dataset)
                            ])

                            uuid = short_uuid
                            self.location = "{}/jails/{}".format(
                                iocroot, short_uuid)
                            skip = True

                    self.json_convert_from_zfs(uuid, skip=skip)

                    with open(self.location + "/config.json", "r") as conf:
                        conf = json.load(conf)
                except CalledProcessError:
                    # At this point it should be a real misconfigured jail
                    raise RuntimeError("Configuration is missing!"
                                       f" Please destroy {uuid} and recreate"
                                       " it.")

        try:
            conf_version = conf["CONFIG_VERSION"]

            if version != conf_version:
                conf = self.json_check_config(conf, version)
        except KeyError:
            conf = self.json_check_config(conf, version)

        return conf
Beispiel #29
0
    def __stop_jail__(self):
        ip4_addr = self.conf["ip4_addr"]
        ip6_addr = self.conf["ip6_addr"]
        vnet = self.conf["vnet"]

        if not self.status:
            self.lgr.error("{} ({}) is not running!".format(
                self.uuid, self.conf["tag"]))
        else:
            self.lgr.info("* Stopping {} ({})".format(self.uuid,
                                                      self.conf["tag"]))

            # TODO: Prestop findscript
            exec_stop = self.conf["exec_stop"].split()
            with open("{}/log/{}-console.log".format(self.iocroot, self.uuid),
                      "a") as f:
                services = check_call(["jexec", "ioc-{}".format(self.uuid)] +
                                      exec_stop,
                                      stdout=f,
                                      stderr=PIPE)
            if services:
                self.lgr.info("  + Stopping services FAILED")
            else:
                self.lgr.info("  + Stopping services OK")

            if self.conf["jail_zfs"] == "on":
                for jdataset in self.conf["jail_zfs_dataset"].split():
                    jdataset = jdataset.strip()
                    children = checkoutput([
                        "zfs", "list", "-H", "-r", "-o", "name", "-S", "name",
                        "{}/{}".format(self.pool, jdataset)
                    ])

                    for child in children.split():
                        child = child.strip()

                        try:
                            checkoutput([
                                "jexec", "ioc-{}".format(self.uuid), "zfs",
                                "umount", child
                            ],
                                        stderr=STDOUT)
                        except CalledProcessError as err:
                            mountpoint = checkoutput([
                                "zfs", "get", "-H", "-o", "value",
                                "mountpoint",
                                "{}/{}".format(self.pool, jdataset)
                            ]).strip()
                            if mountpoint == "none":
                                pass
                            else:
                                raise RuntimeError("ERROR: {}".format(
                                    err.output.decode("utf-8").rstrip()))

                    try:
                        checkoutput([
                            "zfs", "unjail", "ioc-{}".format(self.uuid),
                            "{}/{}".format(self.pool, jdataset)
                        ],
                                    stderr=STDOUT)
                    except CalledProcessError as err:
                        raise RuntimeError("ERROR: {}".format(
                            err.output.decode("utf-8").rstrip()))

            if vnet == "on":
                for nic in self.nics.split(","):
                    nic = nic.split(":")[0]
                    try:
                        checkoutput([
                            "ifconfig", "{}:{}".format(nic, self.jid),
                            "destroy"
                        ],
                                    stderr=STDOUT)
                    except CalledProcessError:
                        pass

            if ip4_addr != "inherit" and vnet == "off":
                if ip4_addr != "none":
                    for ip4 in ip4_addr.split():
                        try:
                            iface, addr = ip4.split("/")[0].split("|")
                            checkoutput(["ifconfig", iface, addr, "-alias"],
                                        stderr=STDOUT)
                        except ValueError:
                            # Likely a misconfigured ip_addr with no interface.
                            self.lgr.error("  ! IP4 address is missing an"
                                           " interface, set ip4_addr to"
                                           " \"INTERFACE|IPADDR\"")
                        except CalledProcessError as err:
                            if "Can't assign requested address" in \
                                    err.output.decode("utf-8"):
                                # They may have a new address that somehow
                                # didn't set correctly. We shouldn't bail on
                                # that.
                                pass
                            else:
                                raise RuntimeError("ERROR: {}".format(
                                    err.output.decode("utf-8").strip()))

            if ip6_addr != "inherit" and vnet == "off":
                if ip6_addr != "none":
                    for ip6 in ip6_addr.split():
                        try:
                            iface, addr = ip6.split("/")[0].split("|")
                            checkoutput(
                                ["ifconfig", iface, "inet6", addr, "-alias"],
                                stderr=STDOUT)
                        except ValueError:
                            # Likely a misconfigured ip_addr with no interface.
                            self.lgr.error("  ! IP6 address is missing an"
                                           " interface, set ip6_addr to"
                                           " \"INTERFACE|IPADDR\"")
                        except CalledProcessError as err:
                            if "Can't assign requested address" in \
                                    err.output.decode("utf-8"):
                                # They may have a new address that somehow
                                # didn't set correctly. We shouldn't bail on
                                # that.
                                pass
                            else:
                                raise RuntimeError("ERROR: {}".format(
                                    err.output.decode("utf-8").strip()))

            stop = check_call(["jail", "-r", "ioc-{}".format(self.uuid)],
                              stderr=PIPE)

            if stop:
                self.lgr.info("  + Removing jail process FAILED")
            else:
                self.lgr.info("  + Removing jail process OK")

            Popen(["umount", "-afF", "{}/fstab".format(self.path)],
                  stderr=PIPE)
            Popen(["umount", "-f", "{}/root/dev/fd".format(self.path)],
                  stderr=PIPE).communicate()
            Popen(["umount", "-f", "{}/root/dev".format(self.path)],
                  stderr=PIPE).communicate()
            Popen(["umount", "-f", "{}/root/proc".format(self.path)],
                  stderr=PIPE).communicate()
            Popen([
                "umount", "-f", "{}/root/compat/linux/proc".format(self.path)
            ],
                  stderr=PIPE).communicate()
Beispiel #30
0
    def json_get_value(self, prop):
        """Returns a string with the specified prop's value."""
        old = False

        if prop == "pool":
            match = 0
            zpools = Popen(
                ["zpool", "list", "-H", "-o", "name"],
                stdout=PIPE).communicate()[0].decode("utf-8").split()

            for zfs in zpools:
                dataset = Popen(
                    [
                        "zfs", "get", "-H", "-o", "value",
                        "org.freebsd.ioc:active", zfs
                    ],
                    stdout=PIPE).communicate()[0].decode("utf-8").strip()

                old_dataset = Popen(
                    ["zpool", "get", "-H", "-o", "value", "comment", zfs],
                    stdout=PIPE).communicate()[0].decode("utf-8").strip()

                if dataset == "yes":
                    _dataset = zfs
                    match += 1
                elif old_dataset == "iocage":
                    _dataset = zfs
                    match += 1
                    old = True

            if match == 1:
                pool = _dataset

                if old:
                    if os.geteuid() != 0:
                        raise RuntimeError("Run as root to migrate old pool"
                                           " activation property!")
                    check_call(["zpool", "set", "comment=-", pool],
                               stderr=PIPE,
                               stdout=PIPE)
                    check_call(
                        ["zfs", "set", "org.freebsd.ioc:active=yes", pool],
                        stderr=PIPE,
                        stdout=PIPE)

                return pool
            elif match >= 2:
                if "deactivate" not in sys.argv[1:]:
                    self.lgr.error("Pools:")
                    for zpool in zpools:
                        self.lgr.error("  {}".format(zpool))
                    raise RuntimeError("You have {} ".format(match) +
                                       "pools marked active for iocage "
                                       "usage.\n"
                                       "Run \"iocage deactivate ZPOOL\" on"
                                       " {} of the".format(match - 1) +
                                       " pools.\n")
            else:
                if len(sys.argv) >= 2 and "activate" in sys.argv[1:]:
                    pass
                else:
                    # We use the first zpool the user has, they are free to
                    # change it.
                    cmd = ["zpool", "list", "-H", "-o", "name"]
                    zpools = Popen(
                        cmd,
                        stdout=PIPE).communicate()[0].decode("utf-8").split()

                    zpool = zpools[0]

                    if os.geteuid() != 0:
                        raise RuntimeError("Run as root to automatically "
                                           "activate the first zpool!")

                    if zpool == "freenas-boot":
                        try:
                            zpool = zpools[1]
                        except IndexError:
                            raise RuntimeError("Please specify a pool to "
                                               "activate with iocage activate "
                                               "POOL")

                    self.lgr.info(f"Setting up zpool [{zpool}] for"
                                  " iocage usage\n If you wish to change"
                                  " please use \"iocage activate\"")

                    Popen(["zfs", "set", "org.freebsd.ioc:active=yes",
                           zpool]).communicate()

                    return zpool
        elif prop == "iocroot":
            # Location in this case is actually the zpool.
            try:
                loc = "{}/iocage".format(self.location)
                mount = checkoutput(
                    ["zfs", "get", "-H", "-o", "value", "mountpoint",
                     loc]).strip()

                if mount != "none":
                    return mount
                else:
                    raise RuntimeError(f"Please set a mountpoint on {loc}")
            except CalledProcessError:
                raise RuntimeError("{} not found!".format(self.location))
        elif prop == "all":
            conf = self.json_load()

            return conf
        else:
            conf = self.json_load()

            if prop == "last_started" and conf[prop] == "none":
                return "never"
            else:
                return conf[prop]