def destroy_release(self, download=False): """Destroy supplied RELEASE and the download dataset if asked""" path = f"{self.pool}/iocage/releases/{self.jail}" ioc_common.logit( { "level": "INFO", "message": f"Destroying RELEASE dataset: {self.jail}" }, _callback=self.callback, silent=self.silent) ioc_destroy.IOCDestroy().__destroy_parse_datasets__(path, stop=False) if download: path = f"{self.pool}/iocage/download/{self.jail}" ioc_common.logit( { "level": "INFO", "message": f"Destroying RELEASE download dataset: {self.jail}" }, _callback=self.callback, silent=self.silent) ioc_destroy.IOCDestroy().__destroy_parse_datasets__(path, stop=False)
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 activate(self, zpool): """Activates the zpool for iocage usage""" pools = list(self.zfs.pools) prop = "org.freebsd.ioc:active" match = False for pool in pools: if pool.name == zpool: match = True if not match: ioc_common.logit( { "level": "EXCEPTION", "message": f"ZFS pool '{zpool}' not found!" }, _callback=self.callback, silent=self.silent) for pool in pools: ds = self.zfs.get_dataset(pool.name) if pool.name == zpool: ds.properties[prop] = libzfs.ZFSUserProperty("yes") else: ds.properties[prop] = libzfs.ZFSUserProperty("no") self.__remove_activate_comment(pool)
def cli(**kwargs): """CLI command that calls fetch_release()""" release = kwargs.get("release", None) _file = kwargs.get("_file", False) if release is not None: if release.lower() == "latest": release = ioc_common.parse_latest_release() kwargs["release"] = release try: release = float(release.rsplit("-", 1)[0].rsplit("-", 1)[0]) except ValueError: ioc_common.logit({ "level": "EXCEPTION", "message": "Please supply a valid entry." }) host_release = float(os.uname()[2].rsplit("-", 1)[0].rsplit("-", 1)[0]) if host_release < release and not _file: ioc_common.logit({ "level": "EXCEPTION", "message": f"\nHost: {host_release} is not greater than" f" target: {release}\nThis is unsupported." }) ioc.IOCage(exit_on_error=True).fetch(**kwargs)
def cli(zpool): """Calls ZFS set to change the property org.freebsd.ioc:active to yes.""" ioc.IOCage(activate=True).activate(zpool) ioc_common.logit({ "level": "INFO", "message": f"ZFS pool '{zpool}' successfully activated." })
def cli(force, release, download, jails, recursive): """Destroys the jail's 2 datasets and the snapshot from the RELEASE.""" # Want these here, otherwise they're reinstanced for each jail. zfs = libzfs.ZFS(history=True, history_prefix="<iocage>") iocroot = ioc.PoolAndDataset().get_iocroot() if download and not release: ioc_common.logit({ "level" : "EXCEPTION", "message": "--release (-r) must be specified as well!" }, exit_on_error=True) if jails and not release: for jail in jails: if not force: ioc_common.logit({ "level" : "WARNING", "message": f"\nThis will destroy jail {jail}" }) if not click.confirm("\nAre you sure?"): continue # no, continue to next jail child_test(zfs, iocroot, jail, "jail", force=force, recursive=recursive) ioc.IOCage(exit_on_error=True, jail=jail, skip_jails=True).destroy_jail() elif jails and release: for release in jails: if not force: ioc_common.logit({ "level" : "WARNING", "message": f"\nThis will destroy RELEASE: {release}" }) if not click.confirm("\nAre you sure?"): continue child_test(zfs, iocroot, release, "release", force=force, recursive=recursive) ioc.IOCage(exit_on_error=True, jail=release, skip_jails=True).destroy_release(download) elif not jails and release: ioc_common.logit({ "level" : "EXCEPTION", "message": "Please specify one or more RELEASEs!" }, exit_on_error=True) else: ioc_common.logit({ "level" : "EXCEPTION", "message": "Please specify one or more jails!" }, exit_on_error=True)
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(source, props, count, name, _uuid): # At this point we don't care _uuid = name if name else _uuid err, msg = ioc.IOCage(jail=source).create(source, props, count, _uuid=_uuid, clone=True) if err: ioc_common.logit({ "level" : "EXCEPTION", "message": msg })
def __check_jail_existence__(self): """ Helper to check if jail dataset exists Return: tuple: The jails uuid, path """ if os.path.isdir(f"{self.iocroot}/jails/{self.jail}"): path = f"{self.iocroot}/jails/{self.jail}" return self.jail, path elif os.path.isdir(f"{self.iocroot}/templates/{self.jail}"): path = f"{self.iocroot}/templates/{self.jail}" return self.jail, path else: if self.skip_jails: # We skip jails for performance, but if they didn't match be # now need to gather the list and iterate. self.jails = self.list("uuid") # We got a partial, time to search. _jail = { uuid: path for (uuid, path) in self.jails.items() if uuid.startswith(self.jail) } if len(_jail) == 1: uuid, path = next(iter(_jail.items())) return uuid, path elif len(_jail) > 1: msg = f"Multiple jails found for {self.jail}:" for u, p in sorted(_jail.items()): msg += f"\n {u} ({p})" ioc_common.logit({ "level": "EXCEPTION", "message": msg }, _callback=self.callback, silent=self.silent) else: msg = f"{self.jail} not found!" ioc_common.logit({ "level": "EXCEPTION", "message": msg }, _callback=self.callback, silent=self.silent)
def cli(jail, name, force): """Get a list of jails and print the property.""" if not force: ioc_common.logit({ "level" : "WARNING", "message": "\nThis will destroy ALL data created including " f"ALL snapshots taken after the snapshot {name}" }) if not click.confirm("\nAre you sure?"): exit() ioc.IOCage(exit_on_error=True, jail=jail).rollback(name)
def clean(self, d_type): """Destroys all of a specified dataset types.""" if d_type == "jails": ioc_clean.IOCClean().clean_jails() ioc_common.logit( { "level": "INFO", "message": "All iocage jail datasets have been destroyed." }, _callback=self.callback, silent=self.silent) elif d_type == "all": ioc_clean.IOCClean().clean_all() ioc_common.logit( { "level": "INFO", "message": "All iocage datasets have been destroyed." }, _callback=self.callback, silent=self.silent) elif d_type == "release": ioc_clean.IOCClean().clean_releases() ioc_common.logit( { "level": "INFO", "message": "All iocage RELEASE and jail datasets have been" " destroyed." }, _callback=self.callback, silent=self.silent) elif d_type == "template": ioc_clean.IOCClean().clean_templates() ioc_common.logit( { "level": "INFO", "message": "All iocage template datasets have been destroyed." }, _callback=self.callback, silent=self.silent) else: ioc_common.logit( { "level": "EXCEPTIONG", "message": "Please specify a dataset type to clean!" }, _callback=self.callback, silent=self.silent)
def validate_count(ctx, param, value): """Takes a string, removes the commas and returns an int.""" if isinstance(value, str): try: value = value.replace(",", "") return int(value) except ValueError: ioc_common.logit({ "level": "EXCEPTION", "message": f"({value} is not a valid integer." }) else: return int(value)
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": "EXCEPTION", "message": "Please specify a jail first!" }) ioc.IOCage(jail).exec(command, host_user, jail_user)
def cli(header, jail, _long): """Allows a user to show resource usage of all jails.""" table = texttable.Texttable(max_width=0) snap_list = ioc.IOCage(jail).snap_list(_long) 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(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 cli(jail, props, plugin): """Get a list of jails and print the property.""" if not props: # Click doesn't correctly assign the two variables for some reason ioc_common.logit( { "level": "EXCEPTION", "message": "You must specify a jail!" }, exit_on_error=True) for prop in props: ioc.IOCage( exit_on_error=True, jail=jail, skip_jails=True).set(prop, plugin)
def cli(**kwargs): """CLI command that calls fetch_release()""" release = kwargs.get("release", None) host_release = os.uname()[2].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." }) ioc.IOCage(exit_on_error=True).fetch(**kwargs)
def cli(header, _long, _sort): """Allows a user to show resource usage of all jails.""" table = texttable.Texttable(max_width=0) jail_list = ioc.IOCage().df(long=_long) sort = ioc_common.ioc_sort("df", _sort) jail_list.sort(key=sort) if header: jail_list.insert(0, ["NAME", "CRT", "RES", "QTA", "USE", "AVA"]) # We get an infinite float otherwise. table.set_cols_dtype(["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, dataset_type): """Calls the correct destroy function.""" if dataset_type == "jails": msg = { "level": "WARNING", "message": "\nThis will destroy ALL jails and any " "snapshots on a RELEASE," "including templates!" } elif dataset_type == "all": msg = { "level": "WARNING", "message": "\nThis will destroy ALL iocage data!" } elif dataset_type == "release": msg = { "level": "WARNING", "message": "\nThis will destroy ALL fetched RELEASES and" " jails/templates created from them!" } elif dataset_type == "template": msg = { "level": "WARNING", "message": "This will destroy ALL templates and jails" " created from them!" } else: ioc_common.logit( { "level": "EXCEPTION", "message": "Please specify a dataset type to clean!" }, exit_on_error=True) if not force: ioc_common.logit(msg) if not click.confirm("\nAre you sure?"): exit() ioc.IOCage(exit_on_error=True, skip_jails=True).clean(dataset_type)
def cli(rc, force, jails): """ Looks for the jail supplied and passes the uuid, path and configuration location to stop_jail. """ if not jails and not rc: ioc_common.logit({ "level" : "EXCEPTION", "message": 'Usage: iocage stop [OPTIONS] JAILS...\n' '\nError: Missing argument "jails".' }, exit_on_error=True) if rc: ioc.IOCage(exit_on_error=True, rc=rc, silent=True).stop(force=force) else: for jail in jails: ioc.IOCage(exit_on_error=True, jail=jail, rc=rc).stop(force=force)
def export(self): """Will export a jail""" uuid, path = self.__check_jail_existence__() status, _ = self.list("jid", uuid=uuid) if status: ioc_common.logit( { "level": "EXCEPTION", "message": f"{uuid} is runnning, stop the jail before" " exporting!" }, _callback=self.callback, silent=self.silent) ioc_image.IOCImage().export_jail(uuid, path)
def cli(rc, jails): """ Looks for the jail supplied and passes the uuid, path and configuration location to start_jail. """ if not jails and not rc: ioc_common.logit({ "level" : "ERROR", "message": 'Usage: iocage start [OPTIONS] JAILS...\n' '\nError: Missing argument "jails".' }) exit(1) if rc: ioc.IOCage(rc=rc, silent=True).start() else: for jail in jails: ioc.IOCage(jail, rc=rc).start()
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" : "EXCEPTION", "message": "Please specify a jail first!" }, exit_on_error=True) # They haven't set a host_user then, and actually want a jail one, # unsetting the convenience default host_user = "" if jail_user and host_user == "root" else host_user ioc.IOCage(exit_on_error=True, jail=jail).exec(command, host_user, jail_user)
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 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 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 start_network_interface_vnet(self, nic, net_configs, jid): """ Start VNET on interface :param nic: The network interface to assign the IP in the jail :param net_configs: Tuple of IP address and router pairs :param jid: The jails ID """ nic, bridge = nic.split(":") try: membermtu = find_bridge_mtu(bridge) ifaces = [] for addrs, gw in net_configs: if addrs != 'none': for addr in addrs.split(','): iface, ip = addr.split("|") if nic != iface: err = f"\n Invalid interface supplied: {iface}" logit({"level": "ERROR", "message": f"{err}"}) err = f" Did you mean {nic}?\n" logit({"level": "ERROR", "message": f"{err}"}) continue if iface not in ifaces: self.start_network_vnet_iface(nic, bridge, membermtu, jid) ifaces.append(iface) self.start_network_vnet_addr(iface, ip, gw) except CalledProcessError as err: logit({"level": "WARNING", "message": "Network failed to start:" f" {err.output.decode('utf-8')}".rstrip()})
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 child_test(zfs, iocroot, name, _type, force=False): """Tests for dependent children""" if _type == "jail": path = f"{iocroot}/jails/{name}/root" else: # RELEASE path = f"{iocroot}/releases/{name}" # While we would like to catch the zfs exception, it still prints to the # display, this is the next best test. if os.path.isdir(path): children = zfs.get_dataset_by_path(path).snapshots_recursive else: if not force: ioc_common.logit({ "level": "WARNING", "message": "Partial UUID/NAME supplied, cannot check for " "dependant jails." }) if not click.confirm("\nProceed?"): return children = [] else: return _children = [] for child in children: _name = child.name.rsplit("@", 1)[-1] if _type == "jail": path = path.replace(name, _name) if os.path.isdir(path): # We only want jails, not every snap they have. _children.append(f" {_name}\n") else: _children.append(f" {_name}\n") sort = ioc_common.ioc_sort("", "name", data=_children) _children.sort(key=sort) if len(_children) != 0: if not force: ioc_common.logit({ "level": "WARNING", "message": f"\n{name} has dependent jails," " use --force to destroy: " }) ioc_common.logit({ "level": "WARNING", "message": "".join(_children) }) exit(1) else: return
def child_test(zfs, iocroot, name, _type, force=False, recursive=False): """Tests for dependent children""" path = None children = [] paths = [ f"{iocroot}/jails/{name}/root", f"{iocroot}/releases/{name}", f"{iocroot}/templates/{name}/root" ] for p in paths: if os.path.isdir(p): path = p children = zfs.get_dataset_by_path(path).snapshots_recursive break if path is None: if not force: ioc_common.logit({ "level": "WARNING", "message": "Partial UUID/NAME supplied, cannot check for " "dependant jails." }) if not click.confirm("\nProceed?"): exit() else: return _children = [] for child in children: _name = child.name.rsplit("@", 1)[-1] _children.append(f" {_name}\n") sort = ioc_common.ioc_sort("", "name", exit_on_error=True, data=_children) _children.sort(key=sort) if len(_children) != 0: if not force and not recursive: ioc_common.logit({ "level": "WARNING", "message": f"\n{name} has dependent jails" " (who may also have dependents)," " use --recursive to destroy: " }) ioc_common.logit({ "level": "WARNING", "message": "".join(_children) }) exit(1) else: return