def cli(command, jail): """Runs pkg with the command given inside the specified jail.""" jails, paths = ioc_list.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: ioc_common.logit({ "level": "ERROR", "message": f"Multiple jails found for {jail}:" }) for t, u in sorted(_jail.items()): ioc_common.logit({"level": "ERROR", "message": f" {u} ({t})"}) exit(1) else: ioc_common.logit({"level": "ERROR", "message": f"{jail} not found!"}) exit(1) cmd = ("pkg", ) + command ioc_exec.IOCExec(cmd, uuid, tag, path).exec_jail()
def __soft_restart__(uuid, jail, path, conf): """ Will tear down the jail by running exec_stop and then exec_start, leaving the network stack intact, ideal for VIMAGE. """ getjid = ioc_list.IOCList().list_get_jid(uuid) status, jid = getjid # These needs to be a list. exec_start = conf["exec_start"].split() exec_stop = conf["exec_stop"].split() exec_fib = conf["exec_fib"] if status: ioc_common.logit({ "level" : "INFO", "message": f"Soft restarting {uuid} ({jail})" }) stop_cmd = ["setfib", exec_fib, "jexec", f"ioc-{uuid}"] + exec_stop su.Popen(stop_cmd, stdout=su.PIPE, stderr=su.PIPE).communicate() su.Popen(["pkill", "-j", jid]).communicate() start_cmd = ["setfib", exec_fib, "jexec", f"ioc-{uuid}"] + exec_start su.Popen(start_cmd, stdout=su.PIPE, stderr=su.PIPE).communicate() ioc_json.IOCJson(path, silent=True).json_set_value( f"last_started={datetime.datetime.utcnow().strftime('%F %T')}") else: ioc_common.logit({ "level" : "ERROR", "message": f"{jail} is not running!" }) exit(1)
def __rc__(self, boot_order, action): """Helper to start all jails with boot=on""" # So we can properly start these. self.rc = False for j in boot_order.keys(): # We want this to be the real jail now. self.jail = j tag, uuid, path = self.__check_jail_existence__() status, _ = ioc_list.IOCList().list_get_jid(uuid) if action == 'stop': if status: message = f" Stopping {uuid} ({j})" self.callback({'level': 'INFO', 'message': message}) self.stop(j) else: message = f"{uuid} ({j}) is not running!" self.callback({'level': 'INFO', 'message': message}) elif action == 'start': if not status: err, msg = self.start(j) if err: self.callback({'level': 'ERROR', 'message': msg}) else: message = f"{uuid} ({j}) is already running!" self.callback({'level': 'WARNING', 'message': message})
def cli(prop, jail, plugin): """Get a list of jails and print the property.""" jails, paths = ioc_list.IOCList("uuid").list_datasets(set=True) _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] iocjson = ioc_json.IOCJson(path, cli=True) elif len(_jail) > 1: ioc_common.logit({ "level": "ERROR", "message": f"Multiple jails found for {jail}:" }) for t, u in sorted(_jail.items()): ioc_common.logit({"level": "ERROR", "message": f" {u} ({t})"}) exit(1) else: ioc_common.logit({"level": "ERROR", "message": f"{jail} not found!"}) exit(1) if "template" in prop.split("=")[0]: if "template" in path and prop != "template=no": ioc_common.logit({ "level": "ERROR", "message": f"{uuid} ({tag}) is already a template!" }) exit(1) elif "template" not in path and prop != "template=yes": ioc_common.logit({ "level": "ERROR", "message": f"{uuid} ({tag}) is already a jail!" }) exit(1) if plugin: _prop = prop.split(".") err = ioc_json.IOCJson(path, cli=True).json_plugin_set_value(_prop) if err: exit(1) else: try: # We use this to test if it's a valid property at all. _prop = prop.partition("=")[0] iocjson.json_get_value(_prop) # The actual setting of the property. iocjson.json_set_value(prop) except KeyError: _prop = prop.partition("=")[0] ioc_common.logit({ "level": "ERROR", "message": f"{_prop} is not a valid property!" }) exit(1)
def cli(command, jail, host_user, jail_user): """Runs the command given inside the specified jail as the supplied user.""" # We may be getting ';', '&&' and so forth. Adding the shell for safety. if len(command) == 1: command = ("/bin/sh", "-c") + command if jail.startswith("-"): ioc_common.logit({ "level" : "ERROR", "message": "Please specify a jail first!" }) exit(1) if host_user and jail_user: ioc_common.logit({ "level" : "ERROR", "message": "Please only specify either host_user or" " jail_user, not both!" }) exit(1) jails, paths = ioc_list.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: ioc_common.logit({ "level" : "ERROR", "message": f"Multiple jails found for {jail}:" }) for t, u in sorted(_jail.items()): ioc_common.logit({ "level" : "ERROR", "message": f" {u} ({t})" }) exit(1) else: ioc_common.logit({ "level" : "ERROR", "message": f"{jail} not found!" }) exit(1) msg, err = ioc_exec.IOCExec(command, uuid, tag, path, host_user, jail_user).exec_jail() if err: ioc_common.logit({ "level" : "ERROR", "message": err.decode() }) else: ioc_common.logit({ "level" : "INFO", "message": msg.decode("utf-8") })
def list(lst_type, header=False, long=False, sort="name", uuid=None, plugin=False, quick=False): """Returns a list of lst_type""" if lst_type == "jid": return ioc_list.IOCList().list_get_jid(uuid) return ioc_list.IOCList(lst_type, header, long, sort, plugin=plugin, quick=quick).list_datasets()
def __init__(self, jail=None, rc=False, callback=None, silent=False): self.pool = ioc_json.IOCJson().json_get_value("pool") self.iocroot = ioc_json.IOCJson(self.pool).json_get_value("iocroot") self.zfs = libzfs.ZFS(history=True, history_prefix="<iocage>") self.jails, self._paths = ioc_list.IOCList("uuid").list_datasets() self.jail = jail self.rc = rc self._all = True if self.jail and 'ALL' in self.jail else False self.callback = ioc_common.callback if not callback else callback self.silent = silent
def cli(jail, name): """Get a list of jails and print the property.""" jails, paths = ioc_list.IOCList("uuid").list_datasets() pool = ioc_json.IOCJson().json_get_value("pool") date = datetime.datetime.utcnow().strftime("%F_%T") _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: ioc_common.logit({ "level" : "ERROR", "message": f"Multiple jails found for {jail}:" }) for t, u in sorted(_jail.items()): ioc_common.logit({ "level" : "ERROR", "message": f" {u} ({t})" }) exit(1) else: ioc_common.logit({ "level" : "ERROR", "message": f"{jail} not found!" }) exit(1) # If they don't supply a snapshot name, we will use the date. if not name: name = date # Looks like foo/iocage/jails/df0ef69a-57b6-4480-b1f8-88f7b6febbdf@BAR conf = ioc_json.IOCJson(path).json_load() if conf["template"] == "yes": target = f"{pool}/iocage/templates/{tag}@{name}" else: target = f"{pool}/iocage/jails/{uuid}@{name}" try: su.check_call(["zfs", "snapshot", "-r", target], stderr=su.PIPE) ioc_common.logit({ "level" : "INFO", "message": f"Snapshot: {target} created." }) except su.CalledProcessError: ioc_common.logit({ "level" : "ERROR", "message": "Snapshot already exists!" }) exit(1)
def cli(jail): """Make a recursive snapshot of the jail and export to a file.""" jails, paths = ioc_list.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: ioc_common.logit({ "level": "ERROR", "message": f"Multiple jails found for {jail}:" }) for t, u in sorted(_jail.items()): ioc_common.logit({"level": "ERROR", "message": f" {u} ({t})"}) exit(1) else: ioc_common.logit({"level": "ERROR", "message": f"{jail} not found!"}) exit(1) status, _ = ioc_list.IOCList().list_get_jid(uuid) if status: ioc_common.logit({ "level": "ERROR", "message": f"{uuid} ({tag}) is runnning, stop the jail before" " exporting!" }) exit(1) ioc_image.IOCImage().export_jail(uuid, tag, path)
def cli(jail, name): """Removes a snapshot from a user supplied jail.""" # TODO: Move to API jails = ioc_list.IOCList("uuid").list_datasets() pool = ioc_json.IOCJson().json_get_value("pool") _jail = { uuid: path for (uuid, path) in jails.items() if uuid.startswith(jail) } if len(_jail) == 1: uuid, path = next(iter(_jail.items())) elif len(_jail) > 1: ioc_common.logit({ "level": "ERROR", "message": f"Multiple jails found for {jail}:" }) for u, p in sorted(_jail.items()): ioc_common.logit({"level": "ERROR", "message": f" {u} ({p})"}) exit(1) else: ioc_common.logit( { "level": "EXCEPTION", "message": f"{jail} not found!" }, exit_on_error=True) # Looks like foo/iocage/jails/df0ef69a-57b6-4480-b1f8-88f7b6febbdf@BAR conf = ioc_json.IOCJson(path).json_load() if conf["template"] == "yes": target = f"{pool}/iocage/templates/{uuid}@{name}" else: target = f"{pool}/iocage/jails/{uuid}@{name}" try: su.check_call(["zfs", "destroy", "-r", "-f", target]) ioc_common.logit({ "level": "INFO", "message": f"Snapshot: {target} destroyed." }) except su.CalledProcessError as err: ioc_common.logit({ "level": "EXCEPTION", "message": f"{err}" }, exit_on_error=True) exit(1)
def __all__(self, jail_order, action): # So we can properly start these. self._all = False for j in jail_order: # We want this to be the real jail now. self.jail = j tag, uuid, path = self.__check_jail_existence__() status, _ = ioc_list.IOCList().list_get_jid(uuid) if action == 'stop': self.stop(j) elif action == 'start': if not status: err, msg = self.start(j) if err: self.callback({'level': 'ERROR', 'message': msg}) else: message = f"{uuid} ({j}) is already running!" self.callback({'level': 'WARNING', 'message': message})
def cli(jail, soft): """ Looks for the jail supplied and passes the uuid, path and configuration location to stop_jail and start_jail. """ jails, paths = ioc_list.IOCList("uuid").list_datasets() if jail == "ALL": for j in jails: uuid = jails[j] path = paths[j] check_type(uuid, j, path, True, soft) else: _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] check_type(uuid, tag, path, False, soft) elif len(_jail) > 1: ioc_common.logit({ "level" : "ERROR", "message": f"Multiple jails found for {jail}:" }) for t, u in sorted(_jail.items()): ioc_common.logit({ "level" : "ERROR", "message": f" {u} ({t})" }) exit(1) else: ioc_common.logit({ "level" : "ERROR", "message": f"{jail} not found!" }) exit(1)
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 cli(jail, release): """Runs upgrade with the command given inside the specified jail.""" # TODO: Move to API release = release.rsplit("-", 1)[0].rsplit("-", 1)[0] host_release = os.uname()[2].rsplit("-", 1)[0].rsplit("-", 1)[0] if release is not None: if host_release < release: ioc_common.logit({ "level": "EXCEPTION", "message": f"\nHost: {host_release} is not greater than" f" target: {release}\nThis is unsupported." }) jails = ioc_list.IOCList("uuid").list_datasets() _jail = { uuid: path for (uuid, path) in jails.items() if uuid.startswith(jail) } if len(_jail) == 1: uuid, path = next(iter(_jail.items())) root_path = f"{path}/root" elif len(_jail) > 1: ioc_common.logit({ "level": "ERROR", "message": f"Multiple jails found for {jail}:" }) for u, p in sorted(_jail.items()): ioc_common.logit({"level": "ERROR", "message": f" {u} ({p})"}) exit(1) else: ioc_common.logit( { "level": "EXCEPTION", "message": f"{jail} not found!" }, exit_on_error=True) status, jid = ioc_list.IOCList.list_get_jid(uuid) conf = ioc_json.IOCJson(path).json_load() jail_release = conf["release"] if release in jail_release: ioc_common.logit( { "level": "EXCEPTION", "message": f"Jail: {uuid} is already at version {release}!" }, exit_on_error=True) started = False if conf["release"] == "EMPTY": ioc_common.logit( { "level": "EXCEPTION", "message": "Upgrading is not supported for empty jails." }, exit_on_error=True) if conf["type"] == "jail": if not status: ioc_start.IOCStart(uuid, path, conf, silent=True) started = True new_release = ioc_upgrade.IOCUpgrade(conf, release, root_path).upgrade_jail() elif conf["type"] == "basejail": ioc_common.logit( { "level": "EXCEPTION", "message": "Please run \"iocage migrate\" before trying" f" to upgrade {uuid}" }, exit_on_error=True) elif conf["type"] == "template": ioc_common.logit( { "level": "EXCEPTION", "message": "Please convert back to a jail before trying" f" to upgrade {uuid}" }, exit_on_error=True) else: ioc_common.logit( { "level": "EXCEPTION", "message": f"{conf['type']} is not a supported jail type." }, exit_on_error=True) if started: ioc_stop.IOCStop(uuid, path, conf, silent=True) ioc_common.logit({ "level": "INFO", "message": f"\n{uuid} successfully upgraded from" f" {jail_release} to {new_release}!" })
def cli(action, fstab_string, jail): """ Looks for the jail supplied and passes the uuid, path and configuration location to manipulate the fstab. """ pool = ioc_json.IOCJson().json_get_value("pool") iocroot = ioc_json.IOCJson(pool).json_get_value("iocroot") index = None _index = False fstab_string = list(fstab_string) _jails, paths = ioc_list.IOCList("uuid").list_datasets() if not fstab_string and action != "edit": ioc_common.logit({ "level": "ERROR", "message": "Please supply a fstab entry!" }) exit(1) _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())) elif len(_jail) > 1: ioc_common.logit({ "level": "ERROR", "message": f"Multiple jails found for {jail}:" }) for t, u in sorted(_jail.items()): ioc_common.logit({"level": "ERROR", "message": f" {u} ({t})"}) exit(1) else: ioc_common.logit({"level": "ERROR", "message": f"{jail} not found!"}) exit(1) # The user will expect to supply a string, the API would prefer these # separate. If the user supplies a quoted string, we will split it, # otherwise the format is acceptable to be imported directly. if len(fstab_string) == 1: try: source, destination, fstype, options, dump, _pass = fstab_string[ 0].split() except ValueError: # We're going to assume this is an index number. try: index = int(fstab_string[0]) _index = True source, destination, fstype, options, dump, _pass = "", "", \ "", "", \ "", "" except TypeError: ioc_common.logit({ "level": "ERROR", "message": "Please specify either a valid fstab " "entry or an index number." }) exit(1) except ValueError: # We will assume this is just a source, and will do a readonly # nullfs mount source = fstab_string[0] destination = source fstype = "nullfs" options = "ro" dump = "0" _pass = "******" else: if action != "edit": try: source, destination, fstype, options, dump, _pass = \ fstab_string except ValueError: ioc_common.logit({ "level": "ERROR", "message": "Please specify a valid fstab entry!\n\n" "Example:\n /the/source /dest FSTYPE " "FSOPTIONS FSDUMP FSPASS" }) exit(1) else: source, destination, fstype, options, dump, _pass = "", "", \ "", "", \ "", "" if not _index and action == "add": destination = f"{iocroot}/jails/{uuid}/root{destination}" ioc_fstab.IOCFstab(uuid, tag, action, source, destination, fstype, options, dump, _pass, index=index)
def cli(jail, release): """Runs upgrade with the command given inside the specified jail.""" jails, paths = ioc_list.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] root_path = "{}/root".format(path) elif len(_jail) > 1: ioc_common.logit({ "level": "ERROR", "message": f"Multiple jails found for {jail}:" }) for t, u in sorted(_jail.items()): ioc_common.logit({"level": "ERROR", "message": f" {u} ({t})"}) exit(1) else: ioc_common.logit({"level": "ERROR", "message": f"{jail} not found!"}) exit(1) status, jid = ioc_list.IOCList.list_get_jid(uuid) conf = ioc_json.IOCJson(path).json_load() jail_release = conf["release"] started = False if conf["release"] == "EMPTY": ioc_common.logit({ "level": "ERROR", "message": "Upgrading is not supported for empty jails." }) exit(1) if conf["type"] == "jail": if not status: ioc_start.IOCStart(uuid, tag, path, conf, silent=True) started = True new_release = ioc_upgrade.IOCUpgrade(conf, release, root_path).upgrade_jail() elif conf["type"] == "basejail": ioc_common.logit({ "level": "ERROR", "message": "Please run \"iocage migrate\" before trying" f" to upgrade {uuid} ({tag})" }) exit(1) elif conf["type"] == "template": ioc_common.logit({ "level": "ERROR", "message": "Please convert back to a jail before trying" f" to upgrade {uuid} ({tag})" }) exit(1) else: ioc_common.logit({ "level": "ERROR", "message": f"{conf['type']} is not a supported jail type." }) exit(1) if started: ioc_stop.IOCStop(uuid, tag, path, conf, silent=True) ioc_common.logit({ "level": "INFO", "message": f"\n{uuid} ({tag}) successfully upgraded from" f" {jail_release} to {new_release}!" })
def __init__(self, callback=False): self.jails, self.paths = ioc_list.IOCList("uuid").list_datasets() self.callback = False if not callback else True
def mng_jail(self, rc, jails, action): """Starts and stops jails.""" if action.lower() not in ('start', 'stop'): raise ValueError('You must specify [start|stop] as an action.') jail_order = {} boot_order = {} _reverse = True if action == 'stop' else False _all = True if 'ALL' in jails else False jails = self.jails if rc or _all else jails if len(jails) < 1: if self.callback: message = "Please specify either one or more jails or ALL!" self.__callback__({'level': 'CRITICAL', 'message': message}) exit(1) else: for jail in jails: tag, uuid, path = self.__check_jail_existence__(jail) conf = ioc_json.IOCJson(path).json_load() boot = conf['boot'] priority = conf['priority'] jail_order[jail] = int(priority) # This removes having to grab all the JSON again later. if boot == 'on': boot_order[jail] = int(priority) jail_order = OrderedDict( sorted(jail_order.items(), key=itemgetter(1), reverse=_reverse)) boot_order = OrderedDict( sorted(boot_order.items(), key=itemgetter(1), reverse=_reverse)) if rc: for j in boot_order.keys(): tag, uuid, path = self.__check_jail_existence__(j) status, _ = ioc_list.IOCList().list_get_jid(uuid) if action == 'stop': if status: message = f" Stopping {uuid} ({j})" self.__callback__({ 'level': 'INFO', 'message': message }) self.__jail_stop__(j, True) else: message = f"{uuid} ({j}) is not running!" self.__callback__({ 'level': 'INFO', 'message': message }) elif action == 'start': if not status: err, msg = self.__jail_start__(j) if err: self.__callback__({ 'level': 'ERROR', 'message': msg }) else: message = f"{uuid} ({j}) is already running!" self.__callback__({ 'level': 'WARNING', 'message': message }) exit() else: if _all: for j in jail_order: if action == 'stop': self.__jail_stop__(j) elif action == 'start': err, msg = self.__jail_start__(j) if err: self.__callback__({ 'level': 'WARNING', 'message': msg }) else: if action == 'start': err, msg = self.__jail_start__(jail) if err and msg: self.__callback__({ 'level': 'CRITICAL', 'message': msg }) exit(1) elif action == 'stop': self.__jail_stop__(jail)
def cli(header, _long, _sort): """Allows a user to show resource usage of all jails.""" jails, paths = ioc_list.IOCList("uuid").list_datasets() pool = ioc_json.IOCJson().json_get_value("pool") jail_list = [] table = texttable.Texttable(max_width=0) for jail in jails: full_uuid = jails[jail] if not _long: uuid = full_uuid[:8] else: uuid = full_uuid path = paths[jail] conf = ioc_json.IOCJson(path).json_load() zconf = ["zfs", "get", "-H", "-o", "value"] mountpoint = f"{pool}/iocage/jails/{full_uuid}" tag = conf["tag"] template = conf["type"] if template == "template": mountpoint = f"{pool}/iocage/templates/{tag}" compressratio = su.Popen(zconf + ["compressratio", mountpoint], stdout=su.PIPE).communicate()[0].decode( "utf-8").strip() reservation = su.Popen(zconf + ["reservation", mountpoint], stdout=su.PIPE).communicate()[0].decode( "utf-8").strip() quota = su.Popen(zconf + ["quota", mountpoint], stdout=su.PIPE).communicate()[0].decode( "utf-8").strip() used = su.Popen(zconf + ["used", mountpoint], stdout=su.PIPE).communicate()[0].decode( "utf-8").strip() available = su.Popen(zconf + ["available", mountpoint], stdout=su.PIPE).communicate()[0].decode( "utf-8").strip() jail_list.append([uuid, compressratio, reservation, quota, used, available, tag]) sort = ioc_common.ioc_sort("df", _sort) jail_list.sort(key=sort) if header: jail_list.insert(0, ["UUID", "CRT", "RES", "QTA", "USE", "AVA", "TAG"]) # We get an infinite float otherwise. table.set_cols_dtype(["t", "t", "t", "t", "t", "t", "t"]) table.add_rows(jail_list) ioc_common.logit({ "level" : "INFO", "message": table.draw() }) else: for jail in jail_list: ioc_common.logit({ "level" : "INFO", "message": "\t".join(jail) })
def cli(force, delete): """Migrates all the iocage_legacy develop basejails to clone jails.""" jails, paths = ioc_list.IOCList("uuid").list_datasets() if not force: ioc_common.logit({ "level": "WARNING", "message": "\nThis will migrate ALL basejails to clonejails," " it can take a long time!" }) if not click.confirm("\nAre you sure?"): exit() for tag, uuid 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}" path = paths[tag] conf = ioc_json.IOCJson(path).json_load() release = conf["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": "ERROR", "message": f"{err.output.decode('utf-8').strip()}" }) exit(1) try: os.remove(f"{iocroot}/tags/{tag}") except OSError: pass new_uuid = ioc_create.IOCCreate(release, "", 0, None, migrate=True, config=conf, silent=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": "Copying files for {uuid} ({tag}), 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): ioc_common.logit({ "level": "INFO", "message": 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}" f" ({tag})!\n" })
def cli(header, jail): """Allows a user to show resource usage of all jails.""" jails, paths = ioc_list.IOCList("uuid").list_datasets() pool = ioc_json.IOCJson().json_get_value("pool") snap_list = [] table = texttable.Texttable(max_width=0) _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: ioc_common.logit({ "level" : "ERROR", "message": f"Multiple jails found for {jail}:" }) for t, u in sorted(_jail.items()): ioc_common.logit({ "level" : "ERROR", "message": f" {u} ({t})" }) exit(1) else: ioc_common.logit({ "level" : "ERROR", "message": f"{jail} not found!" }) exit(1) conf = ioc_json.IOCJson(path).json_load() if conf["template"] == "yes": full_path = f"{pool}/iocage/templates/{tag}" else: full_path = f"{pool}/iocage/jails/{uuid}" zconf = ["zfs", "get", "-H", "-o", "value"] snapshots = su.Popen(["zfs", "list", "-r", "-H", "-t", "snapshot", full_path], stdout=su.PIPE, stderr=su.PIPE).communicate()[0].decode( "utf-8").split("\n") for snap in snapshots: # We get an empty list at the end. if snap: snap = snap.split() snapname = snap[0].rsplit("@")[1] root_snapname = snap[0].rsplit("@")[0].split("/")[-1] if root_snapname == "root": snapname += "/root" elif root_snapname != uuid and root_snapname != tag: # basejail datasets. continue creation = su.Popen(zconf + ["creation", snap[0]], stdout=su.PIPE).communicate()[0].decode( "utf-8").strip() used = snap[1] referenced = su.Popen(zconf + ["referenced", snap[0]], stdout=su.PIPE).communicate()[0].decode( "utf-8").strip() snap_list.append([snapname, creation, referenced, used]) if header: snap_list.insert(0, ["NAME", "CREATED", "RSIZE", "USED"]) # We get an infinite float otherwise. table.set_cols_dtype(["t", "t", "t", "t"]) table.add_rows(snap_list) ioc_common.logit({ "level" : "INFO", "message": table.draw() }) else: for snap in snap_list: ioc_common.logit({ "level" : "INFO", "message": "\t".join(snap) })
def cli(force, release, download, jails): """Destroys the jail's 2 datasets and the snapshot from the RELEASE.""" if download and not release: exit("--release (-r) must be specified as well!") if jails and not release: get_jid = ioc_list.IOCList().list_get_jid try: jail_list, paths = ioc_list.IOCList("uuid").list_datasets() except RuntimeError as err: err = str(err) if "Configuration is missing" in err: uuid = err.split()[5] pool = ioc_json.IOCJson().json_get_value("pool") path = f"{pool}/iocage/jails/{uuid}" ioc_destroy.IOCDestroy().__stop_jails__(path.replace(pool, "")) ioc_destroy.IOCDestroy().__destroy_parse_datasets__(path) exit() else: ioc_common.logit({"level": "ERROR", "message": err}) exit(1) for jail in jails: _jail = { tag: uuid for (tag, uuid) in jail_list.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: ioc_common.logit({ "level": "ERROR", "message": f"Multiple jails found for {jail}:" }) for t, u in sorted(_jail.items()): ioc_common.logit({ "level": "ERROR", "message": f" {u} ({t})" }) exit(1) else: ioc_common.logit({ "level": "ERROR", "message": f"{jail} not found!" }) exit(1) if not force: ioc_common.logit({ "level": "WARNING", "message": f"\nThis will destroy jail {uuid} ({tag})" }) if not click.confirm("\nAre you sure?"): continue # no, continue to next jail status, _ = get_jid(uuid) # If the jail is not running, let's do this thing. if status and not force: ioc_common.logit({ "level": "ERROR", "message": f"{uuid} ({tag}) is running.\nPlease stop" " it first!" }) exit(1) elif status and force: ioc_common.logit({ "level": "INFO", "message": f"Stopping {uuid} ({tag})." }) ioc_destroy.IOCDestroy().destroy_jail(path) elif jails and release: pool = ioc_json.IOCJson().json_get_value("pool") for release in jails: path = f"{pool}/iocage/releases/{release}" if not force: ioc_common.logit({ "level": "WARNING", "message": f"\nThis will destroy RELEASE: {release} and " "any jail that was created with it." }) if not click.confirm("\nAre you sure?"): continue ioc_destroy.IOCDestroy().__destroy_parse_datasets__(path) if download: path = f"{pool}/iocage/download/{release}" ioc_destroy.IOCDestroy().__destroy_parse_datasets__(path) elif not jails and release: ioc_common.logit({ "level": "ERROR", "message": "Please specify one or more RELEASEs!" }) exit(1) else: ioc_common.logit({ "level": "ERROR", "message": "Please specify one or more jails!" }) exit(1)
def list(lst_type, header=False, long=False, sort="tag", uuid=None): """Returns a list of lst_type""" if lst_type == "jid": return ioc_list.IOCList().list_get_jid(uuid) return ioc_list.IOCList(lst_type, header, long, sort).list_datasets()
def cli(prop, _all, _pool, jail, recursive, header, plugin): """Get a list of jails and print the property.""" get_jid = ioc_list.IOCList.list_get_jid jails, paths = ioc_list.IOCList("uuid").list_datasets() jail_list = [] table = texttable.Texttable(max_width=0) if _all: # Confusing I know. jail = prop prop = "all" if _pool: pool = ioc_json.IOCJson().json_get_value("pool") ioc_common.logit({"level": "INFO", "message": pool}) exit() if recursive is None: if jail == "": ioc_common.logit({ "level": "ERROR", "message": 'Usage: iocage get [OPTIONS] PROP JAIL\n' 'Missing argument "jail".' }) exit(1) _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: ioc_common.logit({ "level": "ERROR", "message": f"Multiple jails found for {jail}:" }) for t, u in sorted(_jail.items()): ioc_common.logit({"level": "ERROR", "message": f" {u} ({t})"}) exit(1) else: ioc_common.logit({ "level": "ERROR", "message": f"{jail} not found!" }) exit(1) if prop == "state": status, _ = get_jid(path.split("/")[3]) if status: state = "up" else: state = "down" ioc_common.logit({"level": "INFO", "message": state}) elif plugin: _prop = prop.split(".") props = ioc_json.IOCJson(path).json_plugin_get_value(_prop) if isinstance(props, dict): ioc_common.logit({ "level": "INFO", "message": json.dumps(props, indent=4) }) else: ioc_common.logit({ "level": "INFO", "message": props[0].decode("utf-8") }) elif prop == "all": props = ioc_json.IOCJson(path).json_get_value(prop) for p, v in props.items(): ioc_common.logit({"level": "INFO", "message": f"{p}:{v}"}) elif prop == "fstab": pool = ioc_json.IOCJson().json_get_value("pool") iocroot = ioc_json.IOCJson(pool).json_get_value("iocroot") index = 0 with open(f"{iocroot}/jails/{uuid}/fstab", "r") as fstab: for line in fstab.readlines(): line = line.rsplit("#")[0].rstrip() jail_list.append([index, line.replace("\t", " ")]) index += 1 if header: jail_list.insert(0, ["INDEX", "FSTAB ENTRY"]) # We get an infinite float otherwise. table.set_cols_dtype(["t", "t"]) table.add_rows(jail_list) ioc_common.logit({"level": "INFO", "message": table.draw()}) else: for fstab in jail_list: ioc_common.logit({ "level": "INFO", "message": f"{fstab[0]}\t{fstab[1]}" }) else: try: ioc_common.logit({ "level": "INFO", "message": ioc_json.IOCJson(path).json_get_value(prop) }) except: ioc_common.logit({ "level": "ERROR", "message": f"{prop} is not a valid property!" }) exit(1) else: for j in jails: uuid = jails[j] path = paths[j] try: if prop == "state": status, _ = get_jid(path.split("/")[3]) if status: state = "up" else: state = "down" jail_list.append([uuid, j, state]) elif prop == "all": props = ioc_json.IOCJson(path).json_get_value(prop) for p, v in props.items(): jail_list.append([uuid, j, f"{p}:{v}"]) else: jail_list.append( [uuid, j, ioc_json.IOCJson(path).json_get_value(prop)]) except: ioc_common.logit({ "level": "ERROR", "message": f"{prop} is not a valid property!" }) exit(1) # Prints the table if header: jail_list.insert(0, ["UUID", "TAG", f"PROP - {prop}"]) # We get an infinite float otherwise. table.set_cols_dtype(["t", "t", "t"]) table.add_rows(jail_list) ioc_common.logit({"level": "INFO", "message": table.draw()}) else: for jail in jail_list: ioc_common.logit({"level": "INFO", "message": "\t".join(jail)})
def cli(jail, name, force): """Get a list of jails and print the property.""" jails, paths = ioc_list.IOCList("uuid").list_datasets() pool = ioc_json.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: ioc_common.logit({ "level": "ERROR", "message": "Multiple jails found for" f" {jail}:" }) for t, u in sorted(_jail.items()): ioc_common.logit({"level": "ERROR", "message": f" {u} ({t})"}) exit(1) else: ioc_common.logit({"level": "ERROR", "message": f"{jail} not found!"}) exit(1) # Looks like foo/iocage/jails/df0ef69a-57b6-4480-b1f8-88f7b6febbdf@BAR conf = ioc_json.IOCJson(path).json_load() if conf["template"] == "yes": target = f"{pool}/iocage/templates/{tag}" else: target = f"{pool}/iocage/jails/{uuid}" try: ioc_common.checkoutput(["zfs", "get", "-H", "creation", target], stderr=su.PIPE) except su.CalledProcessError: ioc_common.logit({ "level": "ERROR", "message": f"Snapshot {target} does not exist!" }) exit(1) if not force: ioc_common.logit({ "level": "WARNING", "message": "\nThis will destroy ALL data created since" f" {name} was taken.\nIncluding ALL snapshots taken" f" after {name} for {uuid} ({tag})" }) if not click.confirm("\nAre you sure?"): exit() try: datasets = su.Popen( ["zfs", "list", "-H", "-r", "-o", "name", target], stdout=su.PIPE, stderr=su.PIPE).communicate()[0].decode("utf-8").split() for dataset in datasets: su.check_call(["zfs", "rollback", "-r", f"{dataset}@{name}"]) ioc_common.logit({ "level": "INFO", "message": f"Rolled back to: {target}" }) except su.CalledProcessError as err: ioc_common.logit({"level": "ERROR", "message": f"{err}"}) exit(1)
def cli(jail, force): """ Runs jexec to login into the specified jail. Accepts a force flag that will attempt to start the jail if it is not already running. """ jails, paths = ioc_list.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] iocjson = ioc_json.IOCJson(path) conf = iocjson.json_load() login_flags = conf["login_flags"].split() exec_fib = conf["exec_fib"] status, _ = ioc_list.IOCList().list_get_jid(uuid) elif len(_jail) > 1: ioc_common.logit({ "level": "ERROR", "message": f"Multiple jails found for {jail}" }) for t, u in sorted(_jail.items()): ioc_common.logit({"level": "ERROR", "message": f" {u} ({t})"}) exit(1) else: ioc_common.logit({"level": "ERROR", "message": f"{jail} not found!"}) exit(1) if not status and not force: ioc_common.logit({ "level": "ERROR", "message": f"{uuid} ({tag}) is not running!" }) exit(1) if not status and force: ioc_common.logit({ "level": "INFO", "message": f"{uuid} ({tag}) is not running, starting jail." }) if conf["type"] == "jail": ioc_start.IOCStart(uuid, jail, path, conf, silent=True) status = True elif conf["type"] == "basejail": ioc_common.logit({ "level": "ERROR", "message": "Please run \"iocage migrate\" before trying to" f" start {uuid} ({tag})" }) exit(1) elif conf["type"] == "template": ioc_common.logit({ "level": "ERROR", "message": "Please convert back to a jail before trying to" f" start {uuid} ({tag})" }) exit(1) else: ioc_common.logit({ "level": "ERROR", "message": f"{conf['type']} is not a supported jail type." }) exit(1) if status: su.Popen(["setfib", exec_fib, "jexec", f"ioc-{uuid}", "login"] + login_flags).communicate()
def cli(jail): """Runs update with the command given inside the specified jail.""" # TODO: Move to API jails = ioc_list.IOCList("uuid").list_datasets() _jail = { uuid: path for (uuid, path) in jails.items() if uuid.startswith(jail) } if len(_jail) == 1: uuid, path = next(iter(_jail.items())) elif len(_jail) > 1: ioc_common.logit({ "level": "ERROR", "message": f"Multiple jails found for {jail}:" }) for u, p in sorted(_jail.items()): ioc_common.logit({"level": "ERROR", "message": f" {u} ({p})"}) exit(1) else: ioc_common.logit({"level": "ERROR", "message": f"{jail} not found!"}) exit(1) freebsd_version = ioc_common.checkoutput(["freebsd-version"]) status, jid = ioc_list.IOCList.list_get_jid(uuid) conf = ioc_json.IOCJson(path).json_load() started = False if conf["type"] == "jail": if not status: ioc_start.IOCStart(uuid, path, conf, silent=True) status, jid = ioc_list.IOCList.list_get_jid(uuid) started = True elif conf["type"] == "basejail": ioc_common.logit({ "level": "ERROR", "message": "Please run \"iocage migrate\" before trying" f" to update {uuid}" }) exit(1) elif conf["type"] == "template": ioc_common.logit({ "level": "ERROR", "message": "Please convert back to a jail before trying" f" to update {uuid}" }) exit(1) else: ioc_common.logit({ "level": "ERROR", "message": f"{conf['type']} is not a supported jail type." }) exit(1) if "HBSD" in freebsd_version: su.Popen(["hbsd-update", "-j", jid]).communicate() if started: ioc_stop.IOCStop(uuid, path, conf, silent=True) else: ioc_fetch.IOCFetch(conf["cloned_release"]).fetch_update(True, uuid) if started: ioc_stop.IOCStop(uuid, path, conf, silent=True)