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 __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, 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 df(self, long=False): """Returns a list containing the resource usage of all jails""" jail_list = [] for jail, path in self.jails.items(): conf = ioc_json.IOCJson(path).json_load() mountpoint = f"{self.pool}/iocage/jails/{jail}" template = conf["type"] if template == "template": mountpoint = f"{self.pool}/iocage/templates/{jail}" ds = self.zfs.get_dataset(mountpoint) zconf = ds.properties compressratio = zconf["compressratio"].value reservation = zconf["reservation"].value quota = zconf["quota"].value used = zconf["used"].value available = zconf["available"].value jail_list.append( [jail, compressratio, reservation, quota, used, available]) return jail_list
def start(self, jail=None): """Checks jails type and existence, then starts the jail""" if self.rc or self._all: if not jail: self.__jail_order__("start") else: uuid, path = self.__check_jail_existence__() conf = ioc_json.IOCJson(path, silent=self.silent).json_load() err, msg = self.__check_jail_type__(conf["type"], uuid) depends = conf["depends"].split() if not err: for depend in depends: if depend != "none": self.jail = depend self.start() ioc_start.IOCStart(uuid, path, conf, silent=self.silent, callback=self.callback) return False, None else: if jail: return err, msg else: self.callback({"level": "ERROR", "message": msg}) exit(1)
def start(self, jail=None): """Checks jails type and existence, then starts the jail""" if self.rc or self._all: if not jail: self.__jail_order__("start") else: tag, uuid, path = self.__check_jail_existence__() conf = ioc_json.IOCJson(path).json_load() err, msg = self.__check_jail_type__(conf["type"], uuid, tag) if not err: ioc_start.IOCStart(uuid, tag, path, conf, callback=self.callback, silent=self.silent) return False, None else: if jail: return err, msg else: self.callback({"level": "ERROR", "message": msg}) exit(1)
def snap_list(self, long=True): """Gathers a list of snapshots and returns it""" uuid, path = self.__check_jail_existence__() conf = ioc_json.IOCJson(path, silent=self.silent).json_load() snap_list = [] if conf["template"] == "yes": full_path = f"{self.pool}/iocage/templates/{uuid}" else: full_path = f"{self.pool}/iocage/jails/{uuid}" snapshots = self.zfs.get_dataset(full_path) for snap in snapshots.snapshots_recursive: snap_name = snap.name.rsplit("@")[1] if not long else snap.name root_snap_name = snap.name.rsplit("@")[0].split("/")[-1] if root_snap_name == "root": snap_name += "/root" elif root_snap_name != uuid: # basejail datasets. continue creation = snap.properties["creation"].value used = snap.properties["used"].value referenced = snap.properties["referenced"].value snap_list.append([snap_name, creation, referenced, used]) return snap_list
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 __jail_order__(self, action): """Helper to gather lists of all the jails by order and boot order.""" jail_order = {} boot_order = {} _reverse = True if action == 'stop' else False for jail in self.jails: self.jail = jail tag, uuid, path = self.__check_jail_existence__() 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 = collections.OrderedDict( sorted(jail_order.items(), key=operator.itemgetter(1), reverse=_reverse)) boot_order = collections.OrderedDict( sorted(boot_order.items(), key=operator.itemgetter(1), reverse=_reverse)) if self.rc: self.__rc__(boot_order, action) elif self._all: self.__all__(jail_order, action)
def get_iocroot(self): """ Helper to get the iocroot. Return: string: with the iocroot name. """ return ioc_json.IOCJson(self.pool).json_get_value("iocroot")
def stop(self, jail=None): """Stops the jail.""" if self.rc or self._all: if not jail: self.__jail_order__("stop") else: tag, uuid, path = self.__check_jail_existence__() conf = ioc_json.IOCJson(path).json_load() ioc_stop.IOCStop(uuid, tag, path, conf, silent=self.silent)
def __soft_restart__(self): """ Executes a soft reboot by keeping the jail network stack intact, but executing the rc scripts. """ uuid, path = self.__check_jail_existence__() status, jid = self.list("jid", uuid=uuid) conf = ioc_json.IOCJson(path, silent=self.silent).json_load() # These need 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} ({self.jail})" }, _callback=self.callback, silent=self.silent) stop_cmd = [ "setfib", exec_fib, "jexec", f"ioc-{uuid.replace('.', '_')}" ] + 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.replace('.', '_')}" ] + 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"{self.jail} is not running!" }, _callback=self.callback, silent=self.silent)
def __jail_start__(self, jail, silent=False): """Checks jails type and existence, then starts the jail""" tag, uuid, path = self.__check_jail_existence__(jail) conf = ioc_json.IOCJson(path).json_load() err, msg = self.__check_jail_type__(conf, uuid, tag) if not err: ioc_start.IOCStart(uuid, tag, path, conf, silent) return False, None else: return err, msg
def rollback(self, name): """Rolls back a jail and all datasets to the supplied snapshot""" uuid, path = self.__check_jail_existence__() conf = ioc_json.IOCJson(path, silent=self.silent).json_load() status, _ = self.list("jid", uuid=uuid) if status: ioc_common.logit( { "level": "EXCEPTION", "message": f"Please stop {uuid} before trying to" " rollback!" }, _callback=self.callback, silent=self.silent) if conf["template"] == "yes": target = f"{self.pool}/iocage/templates/{uuid}" else: target = f"{self.pool}/iocage/jails/{uuid}" try: datasets = self.zfs.get_dataset(target) self.zfs.get_snapshot(f"{datasets.name}@{name}") except libzfs.ZFSException as err: ioc_common.logit({ "level": "EXCEPTION", "message": err }, _callback=self.callback, silent=self.silent) for dataset in datasets.dependents: if dataset.type == libzfs.DatasetType.FILESYSTEM: self.zfs.get_snapshot(f"{dataset.name}@{name}").rollback() # datasets is actually the parent. self.zfs.get_snapshot(f"{datasets.name}@{name}").rollback() ioc_common.logit( { "level": "INFO", "message": f"Rolled back to: {target}" }, _callback=self.callback, silent=self.silent)
def check_type(uuid, tag, path, _all, soft): """ Checks the jail type and spits out an error or does the specified restart method. """ conf = ioc_json.IOCJson(path).json_load() if conf["type"] in ("jail", "plugin"): if not soft: __hard_restart__(uuid, tag, path, conf) else: __soft_restart__(uuid, tag, path, conf) elif conf["type"] == "basejail": ioc_common.logit({ "level" : "ERROR", "message": "Please run \"iocage migrate\" before trying" f" to restart {uuid} ({tag})" }) if not _all: exit(1) elif conf["type"] == "template": ioc_common.logit({ "level" : "ERROR", "message": "Please convert back to a jail before trying" f" to restart {uuid} ({tag})" }) if not _all: exit(1) else: ioc_common.logit({ "level" : "ERROR", "message": f"{conf['type']} is not a supported jail type." }) if not _all: 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, 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 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 get(self, prop, recursive=False, plugin=False, pool=False): """Get a jail property""" if pool: return self.pool if not recursive: if self.jail == "default": try: return ioc_json.IOCJson().json_get_value(prop, default=True) except KeyError: ioc_common.logit( { "level": "EXCEPTION", "message": f"{prop} is not a valid property!" }, _callback=self.callback, silent=self.silent) uuid, path = self.__check_jail_existence__() status, jid = self.list("jid", uuid=uuid) if prop == "state": if status: state = "up" else: state = "down" return state elif plugin: _prop = prop.split(".") props = ioc_json.IOCJson(path).json_plugin_get_value(_prop) if isinstance(props, dict): return json.dumps(props, indent=4) else: return props[0].decode("utf-8") elif prop == "all": props = ioc_json.IOCJson(path).json_get_value(prop) return props else: try: return ioc_json.IOCJson(path).json_get_value(prop) except KeyError: ioc_common.logit( { "level": "EXCEPTION", "message": f"{prop} is not a valid property!" }, _callback=self.callback, silent=self.silent) else: jail_list = [] for uuid, path in self.jails.items(): try: if prop == "state": status, _ = self.list("jid", uuid=uuid) if status: state = "up" else: state = "down" jail_list.append({uuid: state}) elif prop == "all": props = ioc_json.IOCJson(path).json_get_value(prop) jail_list.append({uuid: props}) else: jail_list.append({ uuid: ioc_json.IOCJson(path).json_get_value(prop) }) except KeyError: ioc_common.logit( { "level": "EXCEPTION", "message": f"{prop} is not a valid property!" }, _callback=self.callback, silent=self.silent) return jail_list
def __init__(self): self.pool = ioc_json.IOCJson().json_get_value("pool") self.zfs = libzfs.ZFS(history=True, history_prefix="<iocage>")
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 set(self, prop, plugin=False): """Sets a property for a jail or plugin""" try: key, value = prop.split("=", 1) except ValueError: ioc_common.logit( { "level": "EXCEPTION", "message": f"{prop} is is missing a value!" }, _callback=self.callback, silent=self.silent) if self.jail == "default": ioc_json.IOCJson().json_check_default_config() default = True else: default = False if not default: uuid, path = self.__check_jail_existence__() iocjson = ioc_json.IOCJson(path, cli=True) if "template" in key: if "templates/" in path and prop != "template=no": ioc_common.logit( { "level": "EXCEPTION", "message": f"{uuid} is already a template!" }, _callback=self.callback, silent=self.silent) elif "template" not in path and prop != "template=yes": ioc_common.logit( { "level": "EXCEPTION", "message": f"{uuid} is already a jail!" }, _callback=self.callback, silent=self.silent) if plugin: _prop = prop.split(".") ioc_json.IOCJson(path, cli=True).json_plugin_set_value(_prop) else: try: # We use this to test if it's a valid property at all. _prop = prop.partition("=")[0] self.get(_prop) # The actual setting of the property. iocjson.json_set_value(prop) except KeyError: _prop = prop.partition("=")[0] ioc_common.logit( { "level": "EXCEPTION", "message": f"{_prop} is not a valid property!" }, _callback=self.callback, silent=self.silent) if key == "ip6_addr": rtsold_enable = "YES" if "accept_rtadv" in value else "NO" ioc_common.set_rcconf(path, "rtsold_enable", rtsold_enable) else: ioc_json.IOCJson(self.iocroot).json_set_value(prop, default=True)
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 __jail_stop__(self, jail, silent=False): """Stops the jail.""" tag, uuid, path = self.__check_jail_existence__(jail) conf = ioc_json.IOCJson(path).json_load() ioc_stop.IOCStop(uuid, tag, path, conf, silent)
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)