def __start_generate_vnet_mac__(self, nic): """ Generates a random MAC address and checks for uniquness. If the jail already has a mac address generated, it will return that instead. """ mac = self.get("{}_mac".format(nic)) if mac == "none": jails, paths = IOCList("uuid").list_datasets() mac_list = [] for jail in jails: path = paths[jail] _conf = IOCJson(path).json_load() mac = _conf["mac_prefix"] mac_list.append(_conf["{}_mac".format(nic)].split(",")) # We have to flatten our list of lists. mac_list = [m for maclist in mac_list for m in maclist] for number in range(16**6): # SO hex_num_a = hex(number)[2:].zfill(6) hex_num_b = hex(number + 1)[2:].zfill(6) gen_mac_a = "{}{}{}{}{}{}{}".format(mac, *hex_num_a) gen_mac_b = "{}{}{}{}{}{}{}".format(mac, *hex_num_b) gen_mac_combined = "{},{}".format(gen_mac_a, gen_mac_b) if gen_mac_a in mac_list or gen_mac_b in mac_list: continue else: self.set("{}_mac={}".format(nic, gen_mac_combined)) return gen_mac_a, gen_mac_b else: mac_a, mac_b = mac.split(",") return mac_a, mac_b
def list_all(self, jails): """List all jails.""" table = Texttable(max_width=0) jail_list = [] for jail in jails: jail = jail.properties["mountpoint"].value conf = IOCJson(jail).json_load() uuid = conf["host_hostuuid"] full_ip4 = conf["ip4_addr"] ip6 = conf["ip6_addr"] jail_root = "{}/iocage/jails/{}/root".format(self.pool, uuid) try: short_ip4 = full_ip4.split("|")[1].split("/")[0] except IndexError: short_ip4 = "-" tag = conf["tag"] boot = conf["boot"] jail_type = conf["type"] full_release = conf["release"] if "HBSD" in full_release: full_release = "{}-STABLE-HBSD".format(full_release.split( ".")[0]) short_release = "{}-STABLE".format(full_release.rsplit("-")[0]) else: short_release = "-".join(full_release.rsplit("-")[:2]) if full_ip4 == "none": full_ip4 = "-" status, jid = self.list_get_jid(uuid) if status: state = "up" else: state = "down" if conf["type"] == "template": template = "-" else: try: template = checkoutput(["zfs", "get", "-H", "-o", "value", "origin", jail_root]).split("/")[3] except IndexError: template = "-" if "release" in template.lower() or "stable" in template.lower(): template = "-" # Append the JID and the UUID to the table if self.full: jail_list.append([jid, uuid, boot, state, tag, jail_type, full_release, full_ip4, ip6, template]) else: jail_list.append([jid, uuid[:8], state, tag, short_release, short_ip4]) list_type = "list_full" if self.full else "list_short" sort = ioc_sort(list_type, self.sort, data=jail_list) jail_list.sort(key=sort) # Prints the table if self.header: if self.full: # We get an infinite float otherwise. table.set_cols_dtype(["t", "t", "t", "t", "t", "t", "t", "t", "t", "t"]) jail_list.insert(0, ["JID", "UUID", "BOOT", "STATE", "TAG", "TYPE", "RELEASE", "IP4", "IP6", "TEMPLATE"]) else: # We get an infinite float otherwise. table.set_cols_dtype(["t", "t", "t", "t", "t", "t"]) jail_list.insert(0, ["JID", "UUID", "STATE", "TAG", "RELEASE", "IP4"]) table.add_rows(jail_list) return table.draw() else: flat_jail = [j for j in jail_list] return flat_jail
def list_datasets(self, set=False): """Lists the datasets of given type.""" if self.list_type == "all" or self.list_type == "uuid": # List the datasets underneath self.POOL/iocroot/jails cmd = [ "zfs", "list", "-rHd", "1", "{}/iocage/jails".format(self.pool) ] # UUIDS are 12345678-1234-1234-1234-123456789012 regex = re.compile("{}/jails/".format(self.iocroot) + "\\w{8}") elif self.list_type == "base": # List the datasets underneath self.POOL/iocroot/releases cmd = [ "zfs", "list", "-rHd", "1", "{}/iocage/releases".format(self.pool) ] # Format is Major.Minor-{RELEASE,STABLE,CURRENT,BETA,ALPHA,RC} regex = re.compile("{}/releases/".format(self.iocroot) + "\\w*.\\w") elif self.list_type == "template": # List the datasets underneath self.POOL/iocroot/releases cmd = [ "zfs", "list", "-rHd", "1", "{}/iocage/templates".format(self.pool) ] regex = re.compile("{}/templates/".format(self.iocroot)) zfs_list = Popen(cmd, stdout=PIPE).communicate()[0].decode("utf-8").split() datasets = [d for d in zfs_list if re.match(regex, d)] if self.list_type == "all": self.list_all(datasets) elif self.list_type == "uuid": jails = {} paths = {} dups = {} for jail in datasets: conf = IOCJson(jail).json_load() if not set and conf["tag"] in jails: # Add the original in dups[paths[conf["tag"]]] = conf["tag"] dups[jail] = conf["tag"] tag = conf["tag"] jails[conf["tag"]] = conf["host_hostuuid"] paths[conf["tag"]] = jail template_cmd = [ "zfs", "list", "-rHd", "1", "{}/iocage/templates".format(self.pool) ] template_regex = re.compile("{}/templates/".format(self.iocroot)) template_zfs_list = Popen( template_cmd, stdout=PIPE).communicate()[0].decode("utf-8").split() template_datasets = [ t for t in template_zfs_list if re.match(template_regex, t) ] for template in template_datasets: conf = IOCJson(template).json_load() jails[conf["tag"]] = conf["host_hostuuid"] paths[conf["tag"]] = template if len(dups): self.lgr.error( "ERROR: Duplicate tag ({}) detected!".format(tag)) for d, t in sorted(dups.items()): u = [ m for m in d.split("/") if len(m) == 36 or len(m) == 8 ][0] self.lgr.error(" {} ({})".format(u, t)) self.lgr.error("\nPlease run \"iocage set tag=NEWTAG " "UUID\" for one of the UUID's.") raise RuntimeError() return jails, paths elif self.list_type == "base": bases = self.list_bases(datasets) if self.return_object: return bases elif self.list_type == "template": templates = self.list_all(datasets) if self.return_object: return templates
def start_cmd(rc, jails): """ Looks for the jail supplied and passes the uuid, path and configuration location to start_jail. """ lgr = logging.getLogger('ioc_cli_start') _jails, paths = IOCList("uuid").list_datasets() if rc: boot_order = {} for j in _jails: path = paths[j] conf = IOCJson(path).json_load() boot = conf["boot"] priority = conf["priority"] if boot == "on": boot_order[j] = int(priority) boot_order = OrderedDict(sorted(boot_order.items(), key=itemgetter(1))) for j in boot_order.keys(): uuid = _jails[j] path = paths[j] conf = IOCJson(path).json_load() status, _ = IOCList().list_get_jid(uuid) if not status: lgr.info(" Starting {} ({})".format(uuid, j)) IOCStart(uuid, j, path, conf, silent=True) else: lgr.info("{} ({}) is already running!".format(uuid, j)) exit() if len(jails) >= 1 and jails[0] == "ALL": if len(_jails) < 1: raise RuntimeError("No jails exist to start!") for j in _jails: uuid = _jails[j] path = paths[j] conf = IOCJson(path).json_load() IOCStart(uuid, j, path, conf) else: if len(jails) < 1: raise RuntimeError("Please specify either one or more jails or " "ALL!") for jail in jails: _jail = {tag: uuid for (tag, uuid) in _jails.items() if uuid.startswith(jail) or tag == jail} if len(_jail) == 1: tag, uuid = next(iter(_jail.items())) path = paths[tag] elif len(_jail) > 1: lgr.error("Multiple jails found for" " {}:".format(jail)) for t, u in sorted(_jail.items()): lgr.error(" {} ({})".format(u, t)) raise RuntimeError() else: raise RuntimeError("{} not found!".format(jail)) conf = IOCJson(path).json_load() if conf["type"] in ("jail", "plugin"): IOCStart(uuid, tag, path, conf) elif conf["type"] == "basejail": raise RuntimeError( "Please run \"iocage migrate\" before trying" " to start {} ({})".format(uuid, tag)) elif conf["type"] == "template": raise RuntimeError( "Please convert back to a jail before trying" " to start {} ({})".format(uuid, tag)) else: raise RuntimeError("{} is not a supported jail type.".format( conf["type"] ))
def create_config(self, jail_uuid, release): """ This sets up the default configuration for a jail. It also does some mild sanity checking on the properties users are supplying. """ version = IOCJson().json_get_version() with open("/etc/hostid", "r") as _file: hostid = _file.read().strip() default_props = { "CONFIG_VERSION": version, # Network properties "interfaces": "vnet0:bridge0,vnet1:bridge1", "host_domainname": "none", "host_hostname": jail_uuid, "exec_fib": "0", "ip4_addr": "none", "ip4_saddrsel": "1", "ip4": "new", "ip6_addr": "none", "ip6_saddrsel": "1", "ip6": "new", "defaultrouter": "none", "defaultrouter6": "none", "resolver": "/etc/resolv.conf", "mac_prefix": "02ff60", "vnet0_mac": "none", "vnet1_mac": "none", "vnet2_mac": "none", "vnet3_mac": "none", # Jail Properties "devfs_ruleset": "4", "exec_start": "/bin/sh /etc/rc", "exec_stop": "/bin/sh /etc/rc.shutdown", "exec_prestart": "/usr/bin/true", "exec_poststart": "/usr/bin/true", "exec_prestop": "/usr/bin/true", "exec_poststop": "/usr/bin/true", "exec_clean": "1", "exec_timeout": "60", "stop_timeout": "30", "exec_jail_user": "******", "exec_system_jail_user": "******", "exec_system_user": "******", "mount_devfs": "1", "mount_fdescfs": "1", "enforce_statfs": "2", "children_max": "0", "login_flags": "-f root", "securelevel": "2", "sysvmsg": "new", "sysvsem": "new", "sysvshm": "new", "host_hostuuid": jail_uuid, "allow_set_hostname": "1", "allow_sysvipc": "0", "allow_raw_sockets": "0", "allow_chflags": "0", "allow_mount": "0", "allow_mount_devfs": "0", "allow_mount_nullfs": "0", "allow_mount_procfs": "0", "allow_mount_tmpfs": "0", "allow_mount_zfs": "0", "allow_quotas": "0", "allow_socket_af": "0", # RCTL limits "cpuset": "off", "rlimits": "off", "memoryuse": "8G:log", "memorylocked": "off", "vmemoryuse": "off", "maxproc": "off", "cputime": "off", "pcpu": "off", "datasize": "off", "stacksize": "off", "coredumpsize": "off", "openfiles": "off", "pseudoterminals": "off", "swapuse": "off", "nthr": "off", "msgqqueued": "off", "msgqsize": "off", "nmsgq": "off", "nsemop": "off", "nshm": "off", "shmsize": "off", "wallclock": "off", # Custom properties "type": "jail", "tag": datetime.utcnow().strftime("%F@%T:%f"), "bpf": "off", "dhcp": "off", "boot": "off", "notes": "none", "owner": "root", "priority": "99", "last_started": "none", "release": release, "cloned_release": self.release, "template": "no", "hostid": hostid, "jail_zfs": "off", "jail_zfs_dataset": "iocage/jails/{}/data".format(jail_uuid), "jail_zfs_mountpoint": "none", "mount_procfs": "0", "mount_linprocfs": "0", "count": "1", "vnet": "off", "basejail": "no", # Sync properties "sync_state": "none", "sync_target": "none", "sync_tgt_zpool": "none", # Native ZFS properties "compression": "lz4", "origin": "readonly", "quota": "none", "mountpoint": "readonly", "compressratio": "readonly", "available": "readonly", "used": "readonly", "dedup": "off", "reservation": "none", } return default_props
def fetch_plugin(self, _json, props, num): """Expects an JSON object.""" with open(_json, "r") as j: conf = json.load(j) self.release = conf["release"] pkg_repos = conf["fingerprints"] freebsd_version = f"{self.iocroot}/releases/{conf['release']}" \ "/root/bin/freebsd-version" if num <= 1: self.lgr.info("Plugin: {}".format(conf["name"])) self.lgr.info(" Using RELEASE: {}".format(self.release)) self.lgr.info( " Post-install Artifact: {}".format(conf["artifact"])) self.lgr.info(" These pkgs will be installed:") for pkg in conf["pkgs"]: self.lgr.info(" - {}".format(pkg)) if not os.path.isdir("{}/releases/{}".format(self.iocroot, self.release)): self.fetch_release() if conf["release"][:4].endswith("-"): # 9.3-RELEASE and under don't actually have this binary. release = conf["release"] else: with open(freebsd_version, "r") as r: for line in r: if line.startswith("USERLAND_VERSION"): release = line.rstrip().partition("=")[2].strip( '"') # We set our properties that we need, and then iterate over the user # supplied properties replacing ours. Finally we add _1, _2 etc to # the tag with the final iteration if the user supplied count. create_props = [f"cloned_release={self.release}", f"release={release}", "type=plugin"] # If the user supplied a tag, we shouldn't add ours. if "tag" not in [p.split("=")[0] for p in props]: _tag = f"tag={conf['name']}" create_props += [_tag] else: for p in props: _p = p.split("=")[0] _tag = p if _p == "tag" else "" create_props = [f"{k}={v}" for k, v in (p.split("=") for p in props)] + create_props create_props = [f"{k}_{num}" if k == f"{_tag}" and num != 0 else k for k in create_props] uuid = IOCCreate(self.release, create_props, 0).create_jail() jaildir = "{}/jails/{}".format(self.iocroot, uuid) repo_dir = "{}/root/usr/local/etc/pkg/repos".format(jaildir) _conf = IOCJson(jaildir).json_load() tag = _conf["tag"] # We do this test again as the user could supply a malformed IP to # fetch that bypasses the more naive check in cli/fetch if _conf["ip4_addr"] == "none" and _conf["ip6_addr"] == "none": self.lgr.error("\nERROR: An IP address is needed to fetch a " "plugin!\n") self.lgr.error("Destroying partial plugin.") IOCDestroy(uuid, tag, jaildir).destroy_jail() raise RuntimeError() IOCStart(uuid, tag, jaildir, _conf, silent=True) try: os.makedirs(repo_dir, 0o755) except OSError: # It exists, that's fine. pass for repo in pkg_repos: repo_name = repo repo = pkg_repos[repo] f_dir = "{}/root/usr/local/etc/pkg/fingerprints/{}/trusted".format( jaildir, repo_name) repo_conf = """\ {reponame}: {{ url: "{packagesite}", signature_type: "fingerprints", fingerprints: "/usr/local/etc/pkg/fingerprints/{reponame}", enabled: true }} """ try: os.makedirs(f_dir, 0o755) except OSError: self.lgr.error("Repo: {} already exists, skipping!".format( repo_name)) r_file = "{}/{}.conf".format(repo_dir, repo_name) with open(r_file, "w") as r_conf: r_conf.write(repo_conf.format(reponame=repo_name, packagesite=conf["packagesite"])) f_file = "{}/{}".format(f_dir, repo_name) for r in repo: finger_conf = """\ function: {function} fingerprint: {fingerprint} """ with open(f_file, "w") as f_conf: f_conf.write(finger_conf.format(function=r["function"], fingerprint=r[ "fingerprint"])) err = IOCCreate(self.release, create_props, 0, plugin=True, pkglist=conf["pkgs"]).create_install_packages(uuid, jaildir, tag, _conf) if not err: # We need to pipe from tar to the root of the jail. if conf["artifact"]: # TODO: Fancier. self.lgr.info("Fetching artifact... ") Popen(["git", "clone", conf["artifact"], "{}/plugin".format(jaildir)], stdout=PIPE, stderr=PIPE).communicate() tar_in = Popen(["tar", "cvf", "-", "-C", "{}/plugin/overlay/".format(jaildir), "."], stdout=PIPE, stderr=PIPE).communicate() Popen(["tar", "xf", "-", "-C", "{}/root".format(jaildir)], stdin=PIPE).communicate(input=tar_in[0]) try: copy("{}/plugin/post_install.sh".format(jaildir), "{}/root/root".format(jaildir)) self.lgr.info("Running post_install.sh") command = ["sh", "/root/post_install.sh"] IOCExec(command, uuid, conf["name"], jaildir, skip=True).exec_jail() except (IOError, OSError): pass else: self.lgr.error("ERROR: pkg error, refusing to fetch artifact and " "run post_install.sh!\n")
def snaplist_cmd(header, jail): """Allows a user to show resource usage of all jails.""" lgr = ioc_logger.Logger('ioc_cli_snaplist').getLogger() jails, paths = IOCList("uuid").list_datasets() pool = IOCJson().json_get_value("pool") snap_list = [] table = 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: lgr.error("Multiple jails found for" " {}:".format(jail)) for t, u in sorted(_jail.items()): lgr.critical(" {} ({})".format(u, t)) exit(1) else: lgr.critical("{} not found!".format(jail)) exit(1) conf = IOCJson(path).json_load() if conf["template"] == "yes": full_path = "{}/iocage/templates/{}".format(pool, tag) else: full_path = "{}/iocage/jails/{}".format(pool, uuid) zconf = ["zfs", "get", "-H", "-o", "value"] snapshots = Popen(["zfs", "list", "-r", "-H", "-t", "snapshot", full_path], stdout=PIPE, stderr=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 = Popen( zconf + ["creation", snap[0]], stdout=PIPE).communicate()[0].decode("utf-8").strip() used = snap[1] referenced = Popen( zconf + ["referenced", snap[0]], stdout=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) lgr.info(table.draw()) else: for snap in snap_list: lgr.info("\t".join(snap))
def __init__(self, callback=None): self.callback = callback self.pool = IOCJson().json_get_value("pool") self.iocroot = IOCJson(self.pool).json_get_value("iocroot") self.date = datetime.utcnow().strftime("%F")
def fstab_cmd(action, fstab_string, jail): """ Looks for the jail supplied and passes the uuid, path and configuration location to manipulate the fstab. """ lgr = ioc_logger.Logger('ioc_cli_fstab').getLogger() pool = IOCJson().json_get_value("pool") iocroot = IOCJson(pool).json_get_value("iocroot") index = None _index = False fstab_string = list(fstab_string) _jails, paths = IOCList("uuid").list_datasets() if not fstab_string and action != "edit": lgr.critical("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: lgr.error("Multiple jails found for" " {}:".format(jail)) for t, u in sorted(_jail.items()): lgr.error(" {} ({})".format(u, t)) raise RuntimeError() else: lgr.critical("{} not found!".format(jail)) exit(1) # 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]) except TypeError: lgr.critical("Please specify either a valid fstab " "entry or an index number.") exit(1) _index = True source, destination, fstype, options, dump, _pass = "", "", "", \ "", "", "" else: if action != "edit": try: source, destination, fstype, options, dump, _pass = \ fstab_string except ValueError: lgr.critical("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 = "{}/jails/{}/root".format(iocroot, uuid) + destination IOCFstab(uuid, tag, action, source, destination, fstype, options, dump, _pass, index=index)
def create_jail(self): """ Create a snapshot of the user specified RELEASE dataset and clone a jail from that. The user can also specify properties to override the defaults. """ start = False if self.uuid: jail_uuid = self.uuid else: jail_uuid = str(uuid.uuid4()) if self.short: jail_uuid = jail_uuid[:8] location = "{}/jails/{}".format(self.iocroot, jail_uuid) if os.path.isdir(location): raise RuntimeError("The UUID is already in use by another jail.") if self.migrate: config = self.config else: try: if self.template: _type = "templates" temp_path = f"{self.iocroot}/{_type}/{self.release}" template_config = IOCJson(f"{temp_path}").json_get_value cloned_release = template_config("cloned_release") else: _type = "releases" rel_path = f"{self.iocroot}/{_type}/{self.release}" freebsd_version = f"{rel_path}/root/bin/freebsd-version" if not self.empty: if self.release[:4].endswith("-"): # 9.3-RELEASE and under don't actually have this # binary. cloned_release = self.release else: with open(freebsd_version, "r") as r: for line in r: if line.startswith("USERLAND_VERSION"): # Long lines ftw? cl = line.rstrip().partition("=")[2] cloned_release = cl.strip('"') else: cloned_release = "EMPTY" except (IOError, OSError, FileNotFoundError, UnboundLocalError): # Unintuitevly a missing template will throw a # UnboundLocalError as the missing file will kick the # migration routine for zfs props. We don't need that :) if self.template: raise RuntimeError("Template: {} not found!".format( self.release)) else: raise RuntimeError("RELEASE: {} not found!".format( self.release)) config = self.create_config(jail_uuid, cloned_release) jail = "{}/iocage/jails/{}/root".format(self.pool, jail_uuid) if self.template: try: check_call([ "zfs", "snapshot", "{}/iocage/templates/{}/root@{}".format( self.pool, self.release, jail_uuid) ], stderr=PIPE) except CalledProcessError: raise RuntimeError("Template: {} not found!".format( self.release)) Popen([ "zfs", "clone", "-p", "{}/iocage/templates/{}/root@{}".format( self.pool, self.release, jail_uuid), jail ], stdout=PIPE).communicate() # self.release is actually the templates name config["release"] = IOCJson("{}/templates/{}".format( self.iocroot, self.release)).json_get_value("release") config["cloned_release"] = IOCJson("{}/templates/{}".format( self.iocroot, self.release)).json_get_value("cloned_release") else: if not self.empty: try: check_call([ "zfs", "snapshot", "{}/iocage/releases/{}/root@{}".format( self.pool, self.release, jail_uuid) ], stderr=PIPE) except CalledProcessError: raise RuntimeError("RELEASE: {} not found!".format( self.release)) Popen([ "zfs", "clone", "-p", "{}/iocage/releases/{}/root@{}".format( self.pool, self.release, jail_uuid), jail ], stdout=PIPE).communicate() else: try: checkoutput(["zfs", "create", "-p", jail], stderr=PIPE) except CalledProcessError as err: raise RuntimeError(err.output.decode("utf-8").rstrip()) iocjson = IOCJson(location) # This test is to avoid the same warnings during install_packages. if not self.plugin: for prop in self.props: key, _, value = prop.partition("=") if self.num != 0: if key == "tag": value = f"{value}_{self.num}" elif key == "boot" and value == "on": start = True try: iocjson.json_check_prop(key, value, config) config[key] = value except RuntimeError as err: from iocage.lib.ioc_destroy import IOCDestroy iocjson.json_write(config) # Destroy counts on this. IOCDestroy().destroy_jail(location) raise RuntimeError(f"***\n{err}\n***\n") iocjson.json_write(config) # Just "touch" the fstab file, since it won't exist. open("{}/jails/{}/fstab".format(self.iocroot, jail_uuid), "wb").close() _tag = self.create_link(jail_uuid, config["tag"]) config["tag"] = _tag if not self.empty: self.create_rc(location, config["host_hostname"]) if self.basejail: from iocage.lib.ioc_fstab import IOCFstab basedirs = [ "bin", "boot", "lib", "libexec", "rescue", "sbin", "usr/bin", "usr/include", "usr/lib", "usr/libexec", "usr/sbin", "usr/share", "usr/libdata", "usr/lib32" ] for bdir in basedirs: if "-RELEASE" not in self.release: _type = "templates" else: _type = "releases" source = f"{self.iocroot}/{_type}/{self.release}/root/{bdir}" destination = f"{self.iocroot}/jails/{jail_uuid}/root/{bdir}" IOCFstab(jail_uuid, _tag, "add", source, destination, "nullfs", "ro", "0", "0", silent=True) config["basejail"] = "yes" iocjson.json_write(config) if self.empty: config["release"] = "EMPTY" config["cloned_release"] = "EMPTY" iocjson.json_write(config) if not self.plugin: self.lgr.info("{} ({}) successfully created!".format( jail_uuid, _tag)) if self.pkglist: if config["ip4_addr"] == "none" and config["ip6_addr"] == "none": self.lgr.warning(" You need an IP address for the" " jail to install packages!\n") else: self.create_install_packages(jail_uuid, location, _tag, config) if start: from iocage.lib.ioc_start import IOCStart IOCStart(jail_uuid, _tag, location, config) return jail_uuid
def export_cmd(jail): """Make a recursive snapshot of the jail and export to a file.""" lgr = logging.getLogger('ioc_cli_export') pool = IOCJson().json_get_value("pool") iocroot = IOCJson(pool).json_get_value("iocroot") date = datetime.utcnow().strftime("%F") jails, paths = IOCList("uuid").list_datasets() _jail = { tag: uuid for (tag, uuid) in jails.items() if uuid.startswith(jail) or tag == jail } if len(_jail) == 1: tag, uuid = next(iter(_jail.items())) path = paths[tag] elif len(_jail) > 1: lgr.error("Multiple jails found for" " {}:".format(jail)) for t, u in sorted(_jail.items()): lgr.error(" {} ({})".format(u, t)) raise RuntimeError() else: raise RuntimeError("{} not found!".format(jail)) status, _ = IOCList().list_get_jid(uuid) if status: raise RuntimeError("{} ({}) is runnning, stop the jail before " "exporting!".format(uuid, tag)) images = "{}/images".format(iocroot) name = "{}_{}".format(uuid, date) image = "{}/{}_{}".format(images, name, tag) image_path = "{}{}".format(pool, path) jail_list = [] # Looks like foo/iocage/jails/df0ef69a-57b6-4480-b1f8-88f7b6febbdf@BAR target = "{}@ioc-export-{}".format(image_path, date) try: checkoutput(["zfs", "snapshot", "-r", target], stderr=STDOUT) except CalledProcessError as err: raise RuntimeError("ERROR: {}".format( err.output.decode("utf-8").rstrip())) datasets = Popen( ["zfs", "list", "-H", "-r", "-o", "name", "{}{}".format(pool, path)], stdout=PIPE, stderr=PIPE).communicate()[0].decode("utf-8").split() for dataset in datasets: if len(dataset) == 54: _image = image jail_list.append(_image) elif len(dataset) > 54: image_name = dataset.partition("{}{}".format(pool, path))[2] name = image_name.replace("/", "_") _image = image + name jail_list.append(_image) target = "{}@ioc-export-{}".format(dataset, date) # Sending each individually as sending them recursively to a file does # not work how one expects. try: with open(_image, "wb") as export: lgr.info("Exporting dataset: {}".format(dataset)) check_call(["zfs", "send", target], stdout=export) except CalledProcessError as err: raise RuntimeError("ERROR: {}".format(err)) lgr.info("\nPreparing zip file: {}.zip.".format(image)) with zipfile.ZipFile("{}.zip".format(image), "w", compression=zipfile.ZIP_DEFLATED, allowZip64=True) as final: os.chdir(images) for jail in jail_list: final.write(jail) # Cleanup our mess. try: checkoutput(["zfs", "destroy", "-r", target], stderr=STDOUT) for jail in jail_list: os.remove(jail) except CalledProcessError as err: raise RuntimeError("ERROR: {}".format( err.output.decode("utf-8").rstrip())) lgr.info("\nExported: {}.zip".format(image))
def upgrade_cmd(jail, release): """Runs upgrade with the command given inside the specified jail.""" lgr = logging.getLogger('ioc_cli_upgrade') jails, paths = IOCList("uuid").list_datasets() _jail = { tag: uuid for (tag, uuid) in jails.items() if uuid.startswith(jail) or tag == jail } if len(_jail) == 1: tag, uuid = next(iter(_jail.items())) path = paths[tag] root_path = "{}/root".format(path) elif len(_jail) > 1: lgr.error("Multiple jails found for" " {}:".format(jail)) for t, u in sorted(_jail.items()): lgr.error(" {} ({})".format(u, t)) raise RuntimeError() else: raise RuntimeError("{} not found!".format(jail)) pool = IOCJson().json_get_value("pool") iocroot = IOCJson(pool).json_get_value("iocroot") freebsd_version = checkoutput(["freebsd-version"]) status, jid = IOCList.list_get_jid(uuid) conf = IOCJson(path).json_load() host_release = os.uname()[2] jail_release = conf["release"] started = False if conf["release"] == "EMPTY": raise RuntimeError("Upgrading is not supported for empty jails.") if conf["type"] == "jail": if not status: IOCStart(uuid, tag, path, conf, silent=True) status, jid = IOCList.list_get_jid(uuid) started = True elif conf["type"] == "basejail": raise RuntimeError("Please run \"iocage migrate\" before trying" " to upgrade {} ({})".format(uuid, tag)) elif conf["type"] == "template": raise RuntimeError("Please convert back to a jail before trying" " to upgrade {} ({})".format(uuid, tag)) else: raise RuntimeError("{} is not a supported jail type.".format( conf["type"])) _freebsd_version = "{}/releases/{}/root/bin/freebsd-version".format( iocroot, release) if "HBSD" in freebsd_version: Popen(["hbsd-upgrade", "-j", jid]).communicate() else: if os.path.isfile("{}/etc/freebsd-update.conf".format(root_path)): # 10.3-RELEASE and under lack this flag if float(host_release.partition("-")[0][:5]) <= 10.3: raise RuntimeError( "Host: {} is too old, please upgrade to " "10.3-RELEASE or above".format(host_release)) os.environ["PAGER"] = "/bin/cat" fetch = Popen([ "freebsd-update", "-b", root_path, "-d", "{}/var/db/freebsd-update/".format(root_path), "-f", "{}/etc/freebsd-update.conf".format(root_path), "--currently-running {}".format(jail_release), "-r", release, "upgrade" ], stdin=PIPE) fetch.communicate(b"y") while not __upgrade_install__(root_path, release): pass if release[:4].endswith("-"): # 9.3-RELEASE and under don't actually have this binary. new_release = release else: with open(_freebsd_version, "r") as r: for line in r: if line.startswith("USERLAND_VERSION"): new_release = line.rstrip().partition( "=")[2].strip('"') IOCJson(path, silent=True).json_set_value( "release={}".format(new_release)) if started: IOCStop(uuid, tag, path, conf, silent=True) lgr.info("\n{} ({}) successfully upgraded from {} to {}!".format( uuid, tag, jail_release, new_release))
def __init__(self): self.pool = IOCJson().json_get_value("pool") self.iocroot = IOCJson(self.pool).json_get_value("iocroot") self.lgr = logging.getLogger('ioc_destroy') self.zfs = libzfs.ZFS() self.ds = self.zfs.get_dataset
def __init__(self): self.pool = IOCJson().json_get_value("pool") self.iocroot = IOCJson(self.pool).json_get_value("iocroot") self.lgr = logging.getLogger('ioc_clean')
def get_cmd(prop, _all, _pool, jail, recursive, header, plugin): """Get a list of jails and print the property.""" lgr = logging.getLogger('ioc_cli_get') get_jid = IOCList.list_get_jid jails, paths = IOCList("uuid").list_datasets() jail_list = [] table = Texttable(max_width=0) if _all: # Confusing I know. jail = prop prop = "all" if _pool: pool = IOCJson().json_get_value("pool") lgr.info(pool) exit() if recursive is None: if jail == "": lgr.info("Usage: iocage get [OPTIONS] PROP JAIL\n") raise RuntimeError("Error: Missing argument \"jail\".") _jail = { tag: uuid for (tag, uuid) in jails.items() if uuid.startswith(jail) or tag == jail } if len(_jail) == 1: tag, uuid = next(iter(_jail.items())) path = paths[tag] elif len(_jail) > 1: lgr.error("Multiple jails found for" " {}:".format(jail)) for t, u in sorted(_jail.items()): lgr.error(" {} ({})".format(u, t)) raise RuntimeError() else: raise RuntimeError("{} not found!".format(jail)) if prop == "state": status, _ = get_jid(path.split("/")[3]) if status: state = "up" else: state = "down" lgr.info(state) elif plugin: _prop = prop.split(".") props = IOCJson(path).json_plugin_get_value(_prop) if isinstance(props, dict): lgr.info(json.dumps(props, indent=4)) else: pass elif prop == "all": props = IOCJson(path).json_get_value(prop) for p, v in props.items(): lgr.info("{}:{}".format(p, v)) elif prop == "fstab": pool = IOCJson().json_get_value("pool") iocroot = IOCJson(pool).json_get_value("iocroot") index = 0 with open("{}/jails/{}/fstab".format(iocroot, uuid), "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) lgr.info(table.draw()) else: for fstab in jail_list: lgr.info("{}\t{}".format(fstab[0], fstab[1])) else: try: lgr.info(IOCJson(path).json_get_value(prop)) except: raise RuntimeError("{} is not a valid property!".format(prop)) 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 = IOCJson(path).json_get_value(prop) for p, v in props.items(): jail_list.append([uuid, j, "{}:{}".format(p, v)]) else: jail_list.append( [uuid, j, IOCJson(path).json_get_value(prop)]) except: raise RuntimeError("{} is not a valid property!".format(prop)) # Prints the table if header: jail_list.insert(0, ["UUID", "TAG", "PROP - {}".format(prop)]) # We get an infinite float otherwise. table.set_cols_dtype(["t", "t", "t"]) table.add_rows(jail_list) lgr.info(table.draw()) else: for jail in jail_list: lgr.info("\t".join(jail))
def migrate_cmd(force, delete): """Migrates all the iocage_legacy develop basejails to clone jails.""" lgr = ioc_logger.Logger('ioc_cli_migrate').getLogger() jails, paths = IOCList("uuid").list_datasets() if not force: lgr.warning("\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 = IOCJson().json_get_value("pool") iocroot = IOCJson(pool).json_get_value("iocroot") jail = "{}/iocage/jails/{}".format(pool, uuid) jail_old = "{}/iocage/jails_old/{}".format(pool, uuid) path = paths[tag] conf = IOCJson(path).json_load() release = conf["release"] if conf["type"] == "basejail": try: checkoutput(["zfs", "rename", "-p", jail, jail_old], stderr=STDOUT) except CalledProcessError as err: lgr.critical("{}".format(err.output.decode("utf-8").strip())) exit(1) try: os.remove("{}/tags/{}".format(iocroot, tag)) except OSError: pass new_uuid = IOCCreate(release, "", 0, None, migrate=True, config=conf, silent=True).create_jail() new_prop = IOCJson("{}/jails/{}".format(iocroot, new_uuid), silent=True).json_set_value new_prop("host_hostname={}".format(new_uuid)) new_prop("host_hostuuid={}".format(new_uuid)) new_prop("type=jail") new_prop( "jail_zfs_dataset={}/jails/{}/data".format(iocroot, new_uuid)) lgr.info("Copying files for {} ({}), please wait...".format( uuid, tag )) copytree("{}/jails_old/{}/root".format(iocroot, uuid), "{}/jails/{}/root".format(iocroot, new_uuid), symlinks=True) copy("{}/jails_old/{}/fstab".format(iocroot, uuid), "{}/jails/{}/fstab".format(iocroot, new_uuid)) for line in fileinput.input("{}/jails/{}/root/etc/rc.conf".format( iocroot, new_uuid), inplace=1): lgr.info(line.replace(f'hostname="{uuid}"', f'hostname="{new_uuid}"').rstrip()) if delete: try: checkoutput(["zfs", "destroy", "-r", "-f", jail_old], stderr=STDOUT) except CalledProcessError as err: raise RuntimeError("{}".format( err.output.decode("utf-8").rstrip())) try: check_call(["zfs", "destroy", "-r", "-f", "{}/iocage/jails_old".format(pool)]) except CalledProcessError: # We just want the top level dataset gone, no big deal. pass lgr.info("{} ({}) migrated to {} ({})!\n".format(uuid, tag, new_uuid, tag))
def __init__(self): self.pool = IOCJson().json_get_value("pool") self.lgr = logging.getLogger('ioc_clean') self.zfs = libzfs.ZFS() self.ds = self.zfs.get_dataset
def import_jail(self, jail): """Import from an iocage export.""" image_dir = f"{self.iocroot}/images" exports = os.listdir(image_dir) uuid_matches = fnmatch.filter(exports, f"{jail}*.zip") tag_matches = fnmatch.filter(exports, f"*{jail}.zip") cmd = ["zfs", "recv", "-F", "-d", self.pool] # We want to allow the user some flexibility. if uuid_matches: matches = uuid_matches else: matches = tag_matches if len(matches) == 1: image_target = f"{image_dir}/{matches[0]}" uuid = matches[0].rsplit("_")[0] date = matches[0].rsplit("_")[1] tag = matches[0].rsplit("_")[2].rsplit(".")[0] elif len(matches) > 1: msg = f"Multiple exports found for {jail}:" for j in sorted(matches): msg += f"\n {j}" raise RuntimeError(msg) else: raise RuntimeError(f"{jail} not found!") with zipfile.ZipFile(image_target, "r") as _import: for z in _import.namelist(): z_split = z.split("_") # We don't want the date and tag del z_split[1] del z_split[1] z_split_str = "/".join(z_split) _z = z_split_str.replace("iocage/images/", "") if callable(self.callback): self.callback("Importing dataset: {}".format(_z)) dataset = _import.read(z) recv = Popen(cmd, stdin=PIPE) recv.stdin.write(dataset) recv.communicate() recv.stdin.close() # Cleanup our mess. try: target = f"{self.pool}{self.iocroot}/jails/{uuid}@ioc-export-" \ f"{date}" checkoutput(["zfs", "destroy", "-r", target], stderr=STDOUT) except CalledProcessError as err: raise RuntimeError(f"{err.output.decode('utf-8').rstrip()}") tag = IOCJson(f"{self.iocroot}/jails/{uuid}", silent=True).json_set_value(f"tag={tag}") if callable(self.callback): self.callback(f"\nImported: {uuid} ({tag})")
def __init__(self): self.pool = IOCJson().json_get_value("pool") self.lgr = logging.getLogger('ioc_clean') self.zfs = libzfs.ZFS(history=True, history_prefix="<iocage>") self.ds = self.zfs.get_dataset
def destroy_cmd(force, release, download, jails): """Destroys the jail's 2 datasets and the snapshot from the RELEASE.""" lgr = ioc_logger.Logger('ioc_cli_destroy').getLogger() if download and not release: exit("--release (-r) must be specified as well!") if jails and not release: get_jid = IOCList().list_get_jid try: jail_list, paths = IOCList("uuid").list_datasets() except RuntimeError as err: err = str(err) if "Configuration is missing" in err: uuid = err.split()[5] pool = IOCJson().json_get_value("pool") path = f"{pool}/iocage/jails/{uuid}" IOCDestroy().__stop_jails__(path.replace(pool, "")) IOCDestroy().__destroy_parse_datasets__(path) exit() else: lgr.critical(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: lgr.error("Multiple jails found for" " {}:".format(jail)) for t, u in sorted(_jail.items()): lgr.critical(" {} ({})".format(u, t)) exit(1) else: lgr.critical("{} not found!".format(jail)) exit(1) if not force: lgr.warning("\nThis will destroy" " jail {} ({})".format(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: lgr.critical(f"{uuid} ({tag}) is running.\nPlease stop " "it first!") exit(1) elif status and force: lgr.info("Stopping {} ({}).".format(uuid, tag)) IOCDestroy().destroy_jail(path) elif jails and release: pool = IOCJson().json_get_value("pool") for release in jails: path = f"{pool}/iocage/releases/{release}" if not force: lgr.warning(f"\nThis will destroy RELEASE: {release}") lgr.warning(" and any jail that was created with it.") if not click.confirm("\nAre you sure?"): continue IOCDestroy().__destroy_parse_datasets__(path) if download: path = f"{pool}/iocage/download/{release}" IOCDestroy().__destroy_parse_datasets__(path) elif not jails and release: lgr.critical("Please specify one or more RELEASEs!") exit(1) else: lgr.critical("Please specify one or more jails!") exit(1)
def create_cmd(release, template, count, props, pkglist, basejail, short): lgr = logging.getLogger('ioc_cli_create') if not template and not release: raise RuntimeError( "Must supply either --template (-t) or --release (-r)!") if release and "=" in release: raise RuntimeError("Please supply a valid RELEASE!") if template: # We don't really care it's not a RELEASE at this point. release = template if pkglist: if not os.path.isfile(pkglist): _pkgformat = """ { "pkgs": [ "foo", "bar", ] }""" raise RuntimeError("{} does not exist!\nPlease supply a JSON file " "with the format:{}".format( pkglist, _pkgformat)) pool = IOCJson().json_get_value("pool") iocroot = IOCJson(pool).json_get_value("iocroot") if not os.path.isdir("{}/releases/{}".format(iocroot, release)) and not template: IOCFetch(release).fetch_release() if count == 1: try: IOCCreate(release, props, 0, pkglist, template=template, short=short, basejail=basejail).create_jail() except RuntimeError as err: lgr.error(err) if template: lgr.info("Created Templates:") templates = IOCList("template", hdr=False, rtrn_object=True).list_datasets() for temp in templates: lgr.info(" {}".format(temp)) else: for j in range(1, count + 1): try: IOCCreate(release, props, j, pkglist, template=template, short=short, basejail=basejail).create_jail() except RuntimeError as err: lgr.error(err) if template: lgr.info("Created Templates:") templates = IOCList("template", hdr=False, rtrn_object=True).list_datasets() for temp in templates: lgr.info(" {}".format(temp)) exit(1)
def create_cmd(release, template, count, props, pkglist, basejail, empty, short, uuid): lgr = ioc_logger.Logger('ioc_cli_create').getLogger() if short and uuid: lgr.critical( "Can't use --short (-s) and --uuid (-u) at the same time!") exit(1) if not template and not release and not empty: lgr.critical("Must supply either --template (-t) or --release (-r)!") exit(1) if release and "=" in release: lgr.critical("Please supply a valid RELEASE!") exit(1) if template: # We don't really care it's not a RELEASE at this point. release = template if pkglist: _pkgformat = """ { "pkgs": [ "foo", "bar" ] }""" if not os.path.isfile(pkglist): lgr.critical("{} does not exist!\nPlease supply a JSON file " "with the format:{}".format(pkglist, _pkgformat)) exit(1) else: try: # Just try to open the JSON with the right key. with open(pkglist, "r") as p: json.load(p)["pkgs"] # noqa except JSONDecodeError: lgr.critical("Please supply a valid JSON file with the" f" format:\n{_pkgformat}") exit(1) pool = IOCJson().json_get_value("pool") iocroot = IOCJson(pool).json_get_value("iocroot") if not os.path.isdir( f"{iocroot}/releases/{release}") and not template and not empty: freebsd_version = checkoutput(["freebsd-version"]) if "HBSD" in freebsd_version: hardened = True else: hardened = False IOCFetch(release, hardened=hardened).fetch_release() if empty: release = "EMPTY" if count == 1: try: IOCCreate(release, props, 0, pkglist, template=template, short=short, uuid=uuid, basejail=basejail, empty=empty).create_jail() except RuntimeError as err: lgr.error(err) if template: lgr.info("Created Templates:") templates = IOCList("template", hdr=False).list_datasets() for temp in templates: lgr.info(" {}".format(temp[3])) else: for j in range(1, count + 1): try: IOCCreate(release, props, j, pkglist, template=template, short=short, basejail=basejail, empty=empty).create_jail() except RuntimeError as err: lgr.error(err) if template: lgr.info("Created Templates:") templates = IOCList("template", hdr=False).list_datasets() for temp in templates: lgr.info(" {}".format(temp[3])) exit(1)
def import_cmd(jail): """Import from an iocage export.""" lgr = logging.getLogger('ioc_cli_import') pool = IOCJson().json_get_value("pool") iocroot = IOCJson(pool).json_get_value("iocroot") image_dir = "{}/images".format(iocroot) exports = os.listdir(image_dir) uuid_matches = fnmatch.filter(exports, "{}*.zip".format(jail)) tag_matches = fnmatch.filter(exports, "*{}.zip".format(jail)) cmd = ["zfs", "recv", "-F", "-d", pool] # We want to allow the user some flexibility. if uuid_matches: matches = uuid_matches else: matches = tag_matches if len(matches) == 1: image_target = "{}/{}".format(image_dir, matches[0]) uuid = matches[0].rsplit("_")[0] date = matches[0].rsplit("_")[1] tag = matches[0].rsplit("_")[2].rsplit(".")[0] elif len(matches) > 1: lgr.error("Multiple exports found for" " {}:".format(jail)) for j in sorted(matches): lgr.error(" {}".format(j)) raise RuntimeError() else: raise RuntimeError("{} not found!".format(jail)) with zipfile.ZipFile(image_target, "r") as _import: for z in _import.namelist(): z_split = z.split("_") # We don't want the date and tag del z_split[1] del z_split[1] z_split_str = "/".join(z_split) _z = z_split_str.replace("iocage/images/", "") lgr.info("Importing dataset: {}".format(_z)) dataset = _import.read(z) recv = Popen(cmd, stdin=PIPE) recv.stdin.write(dataset) recv.communicate() recv.stdin.close() # Cleanup our mess. try: target = "{}{}/jails/{}@ioc-export-{}".format(pool, iocroot, uuid, date) checkoutput(["zfs", "destroy", "-r", target], stderr=STDOUT) except CalledProcessError as err: raise RuntimeError( "ERROR: {}".format(err.output.decode("utf-8").rstrip())) tag = IOCJson("{}/jails/{}".format(iocroot, uuid), silent=True).json_set_value("tag={}".format(tag)) lgr.info("\nImported: {} ({})".format(uuid, tag))
def create_jail(self): """ Create a snapshot of the user specified RELEASE dataset and clone a jail from that. The user can also specify properties to override the defaults. """ jail_uuid = str(uuid.uuid4()) if self.short: jail_uuid = jail_uuid[:8] location = "{}/jails/{}".format(self.iocroot, jail_uuid) if self.migrate: config = self.config else: if self.template: _type = "templates" else: _type = "releases" freebsd_version = "{}/{}/{}/root/bin/freebsd-version".format( self.iocroot, _type, self.release) try: if self.release[:4].endswith("-"): # 9.3-RELEASE and under don't actually have this binary. cloned_release = self.release else: with open(freebsd_version, "r") as r: for line in r: if line.startswith("USERLAND_VERSION"): cloned_release = line.rstrip().partition( "=")[2].strip('"') config = self.create_config(jail_uuid, cloned_release) except (IOError, OSError): if self.template: raise RuntimeError("Template: {} not found!".format( self.release)) else: raise RuntimeError("RELEASE: {} not found!".format( self.release)) jail = "{}/iocage/jails/{}/root".format(self.pool, jail_uuid) if self.template: try: check_call([ "zfs", "snapshot", "{}/iocage/templates/{}/root@{}".format( self.pool, self.release, jail_uuid) ], stderr=PIPE) except CalledProcessError: raise RuntimeError("Template: {} not found!".format( self.release)) Popen([ "zfs", "clone", "-p", "{}/iocage/templates/{}/root@{}".format( self.pool, self.release, jail_uuid), jail ], stdout=PIPE).communicate() # self.release is actually the templates name config["release"] = IOCJson("{}/templates/{}".format( self.iocroot, self.release)).json_get_value("release") config["cloned_release"] = IOCJson("{}/templates/{}".format( self.iocroot, self.release)).json_get_value("cloned_release") else: try: check_call([ "zfs", "snapshot", "{}/iocage/releases/{}/root@{}".format( self.pool, self.release, jail_uuid) ], stderr=PIPE) except CalledProcessError: raise RuntimeError("RELEASE: {} not found!".format( self.release)) Popen([ "zfs", "clone", "-p", "{}/iocage/releases/{}/root@{}".format( self.pool, self.release, jail_uuid), jail ], stdout=PIPE).communicate() iocjson = IOCJson(location) # This test is to avoid the same warnings during install_packages. if not self.plugin: for prop in self.props: key, _, value = prop.partition("=") if self.num != 0: if key == "tag": value = f"{value}_{self.num}" try: iocjson.json_check_prop(key, value, config) config[key] = value except RuntimeError as err: # Instead this will stay as default. self.lgr.warning(f"***\n{err}\n***\n") iocjson.json_write(config) # Just "touch" the fstab file, since it won't exist. open("{}/jails/{}/fstab".format(self.iocroot, jail_uuid), "wb").close() _tag = self.create_link(jail_uuid, config["tag"]) config["tag"] = _tag self.create_rc(location, config["host_hostname"]) if self.basejail: from iocage.lib.ioc_fstab import IOCFstab basedirs = [ "bin", "boot", "lib", "libexec", "rescue", "sbin", "usr/bin", "usr/include", "usr/lib", "usr/libexec", "usr/sbin", "usr/share", "usr/libdata", "usr/lib32" ] for bdir in basedirs: source = f"{self.iocroot}/releases/{self.release}/root/{bdir}" destination = f"{self.iocroot}/jails/{jail_uuid}/root/{bdir}" IOCFstab(jail_uuid, _tag, "add", source, destination, "nullfs", "ro", "0", "0", silent=True) config["basejail"] = "yes" iocjson.json_write(config) if not self.plugin: self.lgr.info("{} ({}) successfully created!".format( jail_uuid, _tag)) if self.pkglist: if config["ip4_addr"] == "none" and config["ip6_addr"] == "none": self.lgr.error(" ERROR: You need an IP address for the jail" " to install packages!\n") else: self.create_install_packages(jail_uuid, location, _tag, config) return jail_uuid
def list_datasets(self, set=False): """Lists the datasets of given type.""" if self.list_type == "all" or self.list_type == "uuid": ds = self.zfs.get_dataset(f"{self.pool}/iocage/jails").children elif self.list_type == "base": ds = self.zfs.get_dataset(f"{self.pool}/iocage/releases").children elif self.list_type == "template": ds = self.zfs.get_dataset( f"{self.pool}/iocage/templates").children if self.list_type == "all": _all = self.list_all(ds) return _all elif self.list_type == "uuid": jails = {} paths = {} dups = {} for jail in ds: jail = jail.properties["mountpoint"].value conf = IOCJson(jail).json_load() if not set and conf["tag"] in jails: # Add the original in dups[paths[conf["tag"]]] = conf["tag"] dups[jail] = conf["tag"] tag = conf["tag"] jails[conf["tag"]] = conf["host_hostuuid"] paths[conf["tag"]] = jail template_datasets = self.zfs.get_dataset( f"{self.pool}/iocage/templates") template_datasets = template_datasets.children for template in template_datasets: template = template.properties["mountpoint"].value conf = IOCJson(template).json_load() jails[conf["tag"]] = conf["host_hostuuid"] paths[conf["tag"]] = template if len(dups): self.lgr.error(f"Duplicate tag ({tag}) detected!") for d, t in sorted(dups.items()): u = [m for m in d.split("/") if len(m) == 36 or len(m) == 8][0] self.lgr.error(" {} ({})".format(u, t)) self.lgr.error("\nPlease run \"iocage set tag=NEWTAG " "UUID\" for one of the UUID's.") raise RuntimeError() return jails, paths elif self.list_type == "base": bases = self.list_bases(ds) return bases elif self.list_type == "template": templates = self.list_all(ds) return templates
def stop_cmd(rc, jails): """ Looks for the jail supplied and passes the uuid, path and configuration location to stop_jail. """ lgr = logging.getLogger('ioc_cli_stop') _jails, paths = IOCList("uuid").list_datasets() jail_order = {} boot_order = {} for j in _jails: path = paths[j] conf = IOCJson(path).json_load() boot = conf["boot"] priority = conf["priority"] jail_order[j] = int(priority) # This removes having to grab all the JSON again later. if boot == "on": boot_order[j] = int(priority) jail_order = OrderedDict( sorted(jail_order.items(), key=itemgetter(1), reverse=True)) boot_order = OrderedDict( sorted(boot_order.items(), key=itemgetter(1), reverse=True)) if rc: for j in boot_order.keys(): uuid = _jails[j] path = paths[j] conf = IOCJson(path).json_load() status, _ = IOCList().list_get_jid(uuid) if status: lgr.info(" Stopping {} ({})".format(uuid, j)) IOCStop(uuid, j, path, conf, silent=True) else: lgr.info("{} ({}) is not running!".format(uuid, j)) exit() if len(jails) >= 1 and jails[0] == "ALL": if len(_jails) < 1: raise RuntimeError("No jails exist to stop!") for j in jail_order: uuid = _jails[j] path = paths[j] conf = IOCJson(path).json_load() IOCStop(uuid, j, path, conf) else: if len(jails) < 1: raise RuntimeError("Please specify either one or more jails or " "ALL!") for jail in jails: _jail = { tag: uuid for (tag, uuid) in _jails.items() if uuid.startswith(jail) or tag == jail } if len(_jail) == 1: tag, uuid = next(iter(_jail.items())) path = paths[tag] elif len(_jail) > 1: lgr.error("Multiple jails found for" " {}:".format(jail)) for t, u in sorted(_jail.items()): lgr.error(" {} ({})".format(u, t)) raise RuntimeError() else: raise RuntimeError("{} not found!".format(jail)) conf = IOCJson(path).json_load() IOCStop(uuid, tag, path, conf)
def restart_cmd(jail, soft): """ Looks for the jail supplied and passes the uuid, path and configuration location to stop_jail and start_jail. """ lgr = logging.getLogger('ioc_cli_restart') jails, paths = IOCList("uuid").list_datasets() if jail == "ALL": for j in jails: uuid = jails[j] path = paths[j] conf = IOCJson(path).json_load() if conf["type"] in ("jail", "plugin"): try: if not soft: __hard_restart__(uuid, j, path, conf) else: __soft_restart__(uuid, j, path, conf) except RuntimeError as err: lgr.error(err) elif conf["type"] == "basejail": lgr.error("Please run \"iocage migrate\" before trying" " to restart {} ({})".format(uuid, j)) elif conf["type"] == "template": raise RuntimeError( "Please convert back to a jail before trying" " to restart {} ({})".format(uuid, j)) else: lgr.error("{} is not a supported jail type.".format( conf["type"])) 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] elif len(_jail) > 1: lgr.error("Multiple jails found for" " {}:".format(jail)) for t, u in sorted(_jail.items()): lgr.error(" {} ({})".format(u, t)) raise RuntimeError() else: raise RuntimeError("{} not found!".format(jail)) conf = 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": raise RuntimeError("Please run \"iocage migrate\" before trying" " to restart {} ({})".format(uuid, tag)) elif conf["type"] == "template": raise RuntimeError("Please convert back to a jail before trying" " to restart {} ({})".format(uuid, tag)) else: raise RuntimeError("{} is not a supported jail type.".format( conf["type"]))