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
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)
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]
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)
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()
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
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})
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()
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)
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))
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, "-")
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))
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)
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
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)
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)
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})
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"
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()
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)
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()
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)
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
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" })
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()))
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!")
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"]))
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
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()
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]