def query(self, filters=None, options=None): """ Query all jails with `query-filters` and `query-options`. """ options = options or {} jail_identifier = None jails = [] if filters and len(filters) == 1 and list( filters[0][:2]) == ['host_hostuuid', '=']: jail_identifier = filters[0][2] recursive = False if jail_identifier == 'default' else True try: jail_dicts = ioc.IOCage( jail=jail_identifier).get('all', recursive=recursive) if jail_identifier == 'default': jail_dicts['host_hostuuid'] = 'default' jails.append(jail_dicts) else: for jail in jail_dicts: jail = list(jail.values())[0] jail['id'] = jail['host_hostuuid'] if jail['dhcp'] == 'on': uuid = jail['host_hostuuid'] if jail['state'] == 'up': interface = jail['interfaces'].split(',')[0].split( ':')[0] if interface == 'vnet0': # Inside jails they are epair0b interface = 'epair0b' ip4_cmd = ['jexec', f'ioc-{uuid}', 'ifconfig', interface, 'inet'] try: out = su.check_output(ip4_cmd) out = out.splitlines()[2].split()[1].decode() jail['ip4_addr'] = f'{interface}|{out}' except (su.CalledProcessError, IndexError): jail['ip4_addr'] = f'{interface}|ERROR' else: jail['ip4_addr'] = 'DHCP (not running)' if jail['state'] == 'up': try: jail['jid'] = su.check_output( [ 'jls', '-j', f'ioc-{jail["host_hostuuid"]}', 'jid' ] ).decode().strip() except su.CalledProcessError: jail['jid'] = 'ERROR' else: jail['jid'] = None jails.append(jail) except ioc_exceptions.JailMisconfigured as e: self.logger.error(e, exc_info=True) except BaseException: # Brandon is working on fixing this generic except, till then I # am not going to make the perfect the enemy of the good enough! self.logger.debug('Failed to get list of jails', exc_info=True) return filter_list(jails, filters, options)
def list_resource(self, job, resource, remote, want_cache, branch): """Returns a JSON list of the supplied resource on the host""" self.check_dataset_existence() # Make sure our datasets exist. iocage = ioc.IOCage(skip_jails=True) resource = "base" if resource == "RELEASE" else resource.lower() if resource == "plugin": if remote: if want_cache: try: resource_list = self.middleware.call_sync( 'cache.get', 'iocage_remote_plugins') return resource_list except KeyError: pass resource_list = iocage.fetch(list=True, plugins=True, header=False, branch=branch) try: plugins_versions_data = self.middleware.call_sync('cache.get', 'iocage_plugin_versions') except KeyError: plugins_versions_data_job = self.middleware.call_sync( 'core.get_jobs', [['method', '=', 'jail.retrieve_plugin_versions'], ['state', '=', 'RUNNING']] ) error = None plugins_versions_data = {} if plugins_versions_data_job: try: plugins_versions_data = self.middleware.call_sync( 'core.job_wait', plugins_versions_data_job[0]['id'], job=True ) except CallError as e: error = str(e) else: try: plugins_versions_data = self.middleware.call_sync( 'jail.retrieve_plugin_versions', job=True ) except Exception as e: error = e if error: # Let's not make the failure fatal self.middleware.logger.error(f'Retrieving plugins version failed: {error}') else: resource_list = iocage.list("all", plugin=True) pool = IOCJson().json_get_value("pool") iocroot = IOCJson(pool).json_get_value("iocroot") index_path = f'{iocroot}/.plugin_index/INDEX' plugin_jails = { j['host_hostuuid']: j for j in self.middleware.call_sync( 'jail.query', [['type', 'in', ['plugin', 'pluginv2']]] ) } if not pathlib.Path(index_path).is_file(): index_json = None else: index_fd = open(index_path, 'r') index_json = json.load(index_fd) for index, plugin in enumerate(resource_list): if remote: # In case of remote, "plugin" is going to be a dictionary plugin.update({ k: plugins_versions_data.get(plugin['plugin'], {}).get(k, 'N/A') for k in ('version', 'revision', 'epoch') }) else: # "plugin" is a list which we will convert to a dictionary for readability plugin_dict = { k: v if v != '-' else None for k, v in zip(( 'jid', 'name', 'boot', 'state', 'type', 'release', 'ip4', 'ip6', 'template', 'admin_portal' ), plugin) } plugin_output = pathlib.Path( f'{iocroot}/jails/{plugin[1]}/root/root/PLUGIN_INFO' ) if plugin_output.is_file(): plugin_info = [[ x for x in plugin_output.read_text().split( '\n') if x ]] else: plugin_info = None plugin_name = plugin_jails[plugin_dict['name']]['plugin_name'] plugin_dict.update({ 'plugin_info': plugin_info, 'plugin': plugin_name if plugin_name != 'none' else plugin_dict['name'], **self.get_local_plugin_version( plugin_name if plugin_name != 'none' else plugin_dict['name'], index_json, iocroot, plugin_dict['name'] ) }) resource_list[index] = plugin_dict if remote: self.middleware.call_sync( 'cache.put', 'iocage_remote_plugins', resource_list, 86400 ) else: index_fd.close() elif resource == "base": try: if remote: resource_list = self.middleware.call_sync( 'cache.get', 'iocage_remote_releases') return resource_list except KeyError: pass resource_list = iocage.fetch(list=True, remote=remote, http=True) if remote: self.middleware.call_sync( 'cache.put', 'iocage_remote_releases', resource_list, 86400 ) elif resource == 'branches': official_branches = requests.get( 'https://api.github.com/repos/freenas/iocage-ix-plugins/' 'branches' ) official_branches.raise_for_status() resource_list = [ {'name': b['name'], 'repo': 'official'} for b in official_branches.json() ] else: resource_list = [ {k: v if v != '-' else None for k, v in zip(('jid', 'name', 'state', 'release', 'ip4'), jail_data)} for jail_data in iocage.list(resource) ] return resource_list
def create_job(self, job, options): verrors = ValidationErrors() uuid = options["uuid"] job.set_progress(0, f'Creating: {uuid}') try: self.check_jail_existence(uuid, skip=False) verrors.add( 'uuid', f'A jail with name {uuid} already exists' ) raise verrors except CallError: # A jail does not exist with the provided name, we can create one # now verrors = self.common_validation(verrors, options) if verrors: raise verrors job.set_progress(20, 'Initial validation complete') iocage = ioc.IOCage(skip_jails=True) release = options["release"] template = options.get("template", False) pkglist = options.get("pkglist", None) basejail = options["basejail"] empty = options["empty"] short = options["short"] props = options["props"] pool = IOCJson().json_get_value("pool") iocroot = IOCJson(pool).json_get_value("iocroot") https = options.get('https', True) if template: release = template if ( not os.path.isdir(f'{iocroot}/releases/{release}') and not template and not empty ): job.set_progress(50, f'{release} missing, calling fetch') self.middleware.call_sync( 'jail.fetch', {"release": release, "https": https}, job=True ) err, msg = iocage.create( release, props, 0, pkglist, template=template, short=short, _uuid=uuid, basejail=basejail, empty=empty ) if err: raise CallError(msg) job.set_progress(100, f'Created: {uuid}') return True
def fetch(self, job, options): """Fetches a release or plugin.""" fetch_output = {'install_notes': []} release = options.get('release', None) https = options.pop('https', False) name = options.pop('name') jail_name = options.pop('jail_name') post_install = False verrors = ValidationErrors() self.validate_ips(verrors, options) if verrors: raise verrors def progress_callback(content, exception): msg = content['message'].strip('\r\n') rel_up = f'* Updating {release} to the latest patch level... ' nonlocal post_install if name is None: if 'Downloading : base.txz' in msg and '100%' in msg: job.set_progress(5, msg) elif 'Downloading : lib32.txz' in msg and '100%' in msg: job.set_progress(10, msg) elif 'Downloading : doc.txz' in msg and '100%' in msg: job.set_progress(15, msg) elif 'Downloading : src.txz' in msg and '100%' in msg: job.set_progress(20, msg) if 'Extracting: base.txz' in msg: job.set_progress(25, msg) elif 'Extracting: lib32.txz' in msg: job.set_progress(50, msg) elif 'Extracting: doc.txz' in msg: job.set_progress(75, msg) elif 'Extracting: src.txz' in msg: job.set_progress(90, msg) elif rel_up in msg: job.set_progress(95, msg) else: job.set_progress(None, msg) else: if post_install: for split_msg in msg.split('\n'): fetch_output['install_notes'].append(split_msg) if ' These pkgs will be installed:' in msg: job.set_progress(50, msg) elif 'Installing plugin packages:' in msg: job.set_progress(75, msg) elif 'Running post_install.sh' in msg: job.set_progress(90, msg) # Sets each message going forward as important to the user post_install = True else: job.set_progress(None, msg) self.check_dataset_existence() # Make sure our datasets exist. start_msg = f'{release} being fetched' final_msg = f'{release} fetched' iocage = ioc.IOCage(callback=progress_callback, silent=False) if name is not None: pool = IOCJson().json_get_value('pool') iocroot = IOCJson(pool).json_get_value('iocroot') options["plugin_name"] = name start_msg = 'Starting plugin install' final_msg = f"Plugin: {name} installed" elif name is None and https: if 'https' not in options['server']: options['server'] = f'https://{options["server"]}' options["accept"] = True options['name'] = jail_name job.set_progress(0, start_msg) iocage.fetch(**options) if post_install and name is not None: plugin_manifest = pathlib.Path( f'{iocroot}/.plugin_index/{name}.json' ) plugin_json = json.loads(plugin_manifest.read_text()) schema_version = plugin_json.get('plugin_schema', '1') if schema_version.isdigit() and int(schema_version) >= 2: plugin_output = pathlib.Path( f'{iocroot}/jails/{name}/root/root/PLUGIN_INFO' ) if plugin_output.is_file(): # Otherwise it will be the verbose output from the # post_install script fetch_output['install_notes'] = [ x for x in plugin_output.read_text().split('\n') if x ] # This is to get the admin URL and such fetch_output['install_notes'] += job.progress[ 'description'].split('\n') job.set_progress(100, final_msg) return fetch_output
def __rollback_jail__(self): import iocage_lib.iocage as ioc # Avoids dep issues name = f"ioc_upgrade_{self.date}" iocage = ioc.IOCage(jail=self.uuid, skip_jails=True, silent=True) iocage.stop() iocage.rollback(name)
def create_job(self, job, options): verrors = ValidationErrors() uuid = options["uuid"] job.set_progress(0, f'Creating: {uuid}') try: self.check_jail_existence(uuid, skip=False) verrors.add( 'uuid', f'A jail with name {uuid} already exists' ) raise verrors except CallError: # A jail does not exist with the provided name, we can create one # now verrors = self.common_validation(verrors, options) if verrors: raise verrors job.set_progress(20, 'Initial validation complete') if not any('resolver' in p for p in options['props']): dc = self.middleware.call_sync( 'service.query', [('service', '=', 'domaincontroller')] )[0] dc_config = self.middleware.call_sync('domaincontroller.config') if dc['enable'] and ( dc_config['dns_forwarder'] and dc_config['dns_backend'] == 'SAMBA_INTERNAL' ): options['props'].append( f'resolver=nameserver {dc_config["dns_forwarder"]}' ) iocage = ioc.IOCage(skip_jails=True) release = options["release"] template = options.get("template", False) pkglist = options.get("pkglist", None) basejail = options["basejail"] empty = options["empty"] short = options["short"] props = options["props"] pool = IOCJson().json_get_value("pool") iocroot = IOCJson(pool).json_get_value("iocroot") if template: release = template if ( not os.path.isdir(f'{iocroot}/releases/{release}') and not template and not empty ): job.set_progress(50, f'{release} missing, calling fetch') self.middleware.call_sync( 'jail.fetch', {"release": release}, job=True ) err, msg = iocage.create( release, props, 0, pkglist, template=template, short=short, _uuid=uuid, basejail=basejail, empty=empty ) if err: raise CallError(msg) job.set_progress(100, f'Created: {uuid}') return True
def cli(jail): """Update the supplied jail to the latest patchset""" skip_jails = bool(jail != 'ALL') ioc.IOCage(jail=jail, skip_jails=skip_jails).update()
def cli(prop, _type, _pool, jail, recursive, header, plugin): """Get a list of jails and print the property.""" table = texttable.Texttable(max_width=0) if _type: # Confusing I know. jail = prop prop = _type elif _pool: pool = ioc.IOCage(skip_jails=True).get("", pool=True) ioc_common.logit({"level": "INFO", "message": pool}) exit() else: if not jail and not recursive: ioc_common.logit({ "level": "EXCEPTION", "message": "You must specify a jail!" }) if _type == "all" and recursive: # TODO: Port this back ioc_common.logit({ "level": "EXCEPTION", "message": "You cannot use --all (-a) and --recursive (-r) " "together. " }) if not recursive: if prop == "state" or _type == "state": state = ioc.IOCage(jail=jail).get(prop) ioc_common.logit({"level": "INFO", "message": state}) elif prop == "jid" or _type == "jid": jid = ioc.IOCage(jail=jail).list("jid", uuid=jail)[1] ioc_common.logit({"level": "INFO", "message": jid}) elif plugin: _plugin = ioc.IOCage(jail=jail, skip_jails=True).get(prop, plugin=True) ioc_common.logit({"level": "INFO", "message": _plugin}) elif prop == "all": props = ioc.IOCage(jail=jail, skip_jails=True).get(prop) for p, v in props.items(): ioc_common.logit({"level": "INFO", "message": f"{p}:{v}"}) else: p = ioc.IOCage(jail=jail, skip_jails=True).get(prop) ioc_common.logit({"level": "INFO", "message": p}) else: jails = ioc.IOCage().get(prop, recursive=True) table.header(["NAME", f"PROP - {prop}"]) for jail_dict in jails: for jail, prop in jail_dict.items(): if header: table.add_row([jail, prop]) else: ioc_common.logit({ "level": "INFO", "message": f"{jail}\t{prop}" }) if header: # Prints the table ioc_common.logit({"level": "INFO", "message": table.draw()})
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!" }) 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(jail=jail, skip_jails=True).destroy_jail(force=force) 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 children = child_test(zfs, iocroot, release, "release", force=force, recursive=recursive) if children: for child in children: ioc.IOCage(jail=child).destroy_jail(force) ioc.IOCage(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!" }) else: ioc_common.logit({ "level": "EXCEPTION", "message": "Please specify one or more jails!" })
def cli(jail, name): """Snapshot a jail.""" ioc.IOCage(jail=jail, skip_jails=True).snapshot(name)
def list_resource(self, resource, remote): """Returns a JSON list of the supplied resource on the host""" self.check_dataset_existence() # Make sure our datasets exist. iocage = ioc.IOCage(skip_jails=True) resource = "base" if resource == "RELEASE" else resource.lower() if resource == "plugin": if remote: try: resource_list = self.middleware.call_sync( 'cache.get', 'iocage_remote_plugins') return resource_list except KeyError: pass resource_list = iocage.fetch(list=True, plugins=True, header=False) else: resource_list = iocage.list("all", plugin=True) pool = IOCJson().json_get_value("pool") iocroot = IOCJson(pool).json_get_value("iocroot") index_path = f'{iocroot}/.plugin_index/INDEX' if not pathlib.Path(index_path).is_file(): index_json = None for plugin in resource_list: plugin += ['N/A', 'N/A'] return resource_list else: index_fd = open(index_path, 'r') index_json = json.load(index_fd) for plugin in resource_list: for i, elem in enumerate(plugin): # iocage returns - for None plugin[i] = elem if elem != "-" else None if remote: pv = self.get_plugin_version(plugin[2]) else: pv = self.get_local_plugin_version(plugin[1], index_json, iocroot) resource_list[resource_list.index(plugin)] = plugin + pv if remote: self.middleware.call_sync('cache.put', 'iocage_remote_plugins', resource_list, 86400) else: index_fd.close() elif resource == "base": try: if remote: resource_list = self.middleware.call_sync( 'cache.get', 'iocage_remote_releases') return resource_list except KeyError: pass resource_list = iocage.fetch(list=True, remote=remote, http=True) if remote: self.middleware.call_sync('cache.put', 'iocage_remote_releases', resource_list, 86400) else: resource_list = iocage.list(resource) return resource_list
def cli(prop, _type, _pool, jail, recursive, header, plugin, force): """Get a list of jails and print the property.""" table = texttable.Texttable(max_width=0) if _type: # Confusing I know. jail = prop prop = _type elif _pool: pool = ioc.IOCage(skip_jails=True).get('', pool=True) ioc_common.logit({'level': 'INFO', 'message': pool}) exit() else: if not jail and not recursive: ioc_common.logit({ 'level': 'EXCEPTION', 'message': 'You must specify a jail!' }) if _type == 'all' and recursive: # TODO: Port this back ioc_common.logit({ 'level': 'EXCEPTION', 'message': 'You cannot use --all (-a) and --recursive (-r) ' 'together. ' }) if not recursive: if prop == 'state' or _type == 'state': state = ioc.IOCage(jail=jail).get(prop) ioc_common.logit({'level': 'INFO', 'message': state}) elif prop == 'jid' or _type == 'jid': jid = ioc.IOCage(jail=jail).list('jid', uuid=jail)[1] ioc_common.logit({'level': 'INFO', 'message': jid}) elif plugin: _plugin = ioc.IOCage(jail=jail, skip_jails=True).get(prop, plugin=True, start_jail=force) ioc_common.logit({'level': 'INFO', 'message': _plugin}) elif prop == 'all': props = ioc.IOCage(jail=jail, skip_jails=True).get(prop) for p, v in props.items(): ioc_common.logit({'level': 'INFO', 'message': f'{p}:{v}'}) else: p = ioc.IOCage(jail=jail, skip_jails=True).get(prop) ioc_common.logit({'level': 'INFO', 'message': p}) else: jails = ioc.IOCage().get(prop, recursive=True) table.header(['NAME', f'PROP - {prop}']) for jail_dict in jails: for jail, prop in jail_dict.items(): if header: table.add_row([jail, prop]) else: ioc_common.logit({ 'level': 'INFO', 'message': f'{jail}\t{prop}' }) if header: # Prints the table ioc_common.logit({'level': 'INFO', 'message': table.draw()})
def cli(jail, name): """Removes a snapshot from a user supplied jail.""" ioc.IOCage(jail=jail).snap_remove(name)
def cli(action, fstab_string, jail, header, replace): """ Looks for the jail supplied and passes the uuid, path and configuration location to manipulate the fstab. """ index = None if not replace else replace _index = False add_path = False fstab_string = list(fstab_string) action = action if not replace else "replace" if not fstab_string and action != "edit" and action != "list": ioc_common.logit({ 'level': 'EXCEPTION', 'message': 'Please supply an fstab entry or jail!' }) # 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": "EXCEPTION", "message": "Please specify either a valid fstab " "entry or an index number." }) 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 = "******" elif action == "list": # We don't need these source, destination, fstype, options, dump, _pass = "", "", \ "", "", \ "", "" else: if action != "edit": try: source, destination, fstype, options, dump, _pass = \ fstab_string except ValueError: ioc_common.logit({ "level": "EXCEPTION", "message": "Please specify a valid fstab entry!\n\n" "Example:\n /the/source /dest FSTYPE " "FSOPTIONS FSDUMP FSPASS" }) else: source, destination, fstype, options, dump, _pass = "", "", \ "", "", \ "", "" if not _index: add_path = True fstab = ioc.IOCage(jail=jail).fstab(action, source, destination, fstype, options, dump, _pass, index=index, add_path=add_path, header=header) if action == "list": if header: ioc_common.logit({"level": "INFO", "message": fstab}) else: for f in fstab: ioc_common.logit({ "level": "INFO", "message": f"{f[0]}\t{f[1]}" })
def cli(release, template, count, props, pkglist, basejail, thickjail, empty, short, name, _uuid, force, thickconfig): if name: # noinspection Annotator valid = True if re.match("^[a-zA-Z0-9\._-]+$", name) else False if not valid: ioc_common.logit({ "level": "EXCEPTION", "message": f"Invalid character in {name}, please remove it." }) # At this point we don't care _uuid = name if release and "=" in release: ioc_common.logit({ "level": "EXCEPTION", "message": "Please supply a valid RELEASE!" }) elif release and release.lower() == "latest": release = ioc_common.parse_latest_release() if release: release = release.upper() ioc_common.check_release_newer(release) # We don't really care it's not a RELEASE at this point. release = template if template else release if pkglist: _pkgformat = """ { "pkgs": [ "foo", "bar" ] }""" if not os.path.isfile(pkglist): ioc_common.logit({ "level": "EXCEPTION", "message": f"{pkglist} does not exist!\n" "Please supply a JSON file with the format:" f" {_pkgformat}" }) else: try: # Just try to open the JSON with the right key. with open(pkglist, "r") as p: json.load(p)["pkgs"] # noqa except json.JSONDecodeError: ioc_common.logit({ "level": "EXCEPTION", "message": "Please supply a valid" f" JSON file with the format:{_pkgformat}" }) if empty: release = "EMPTY" iocage = ioc.IOCage(skip_jails=True) try: iocage.create(release, props, count, pkglist=pkglist, template=template, short=short, _uuid=_uuid, basejail=basejail, thickjail=thickjail, empty=empty, thickconfig=thickconfig) except RuntimeError as err: if template and "Dataset" in str(err): # We want to list the available templates first ioc_common.logit({ "level": "ERROR", "message": f"Template: {release} not found!" }) templates = ioc.IOCage().list("template") for temp in templates: ioc_common.logit({ "level": "EXCEPTION", "message": f"Created Templates:\n {temp[1]}" }) exit(1) else: # Standard errors ioc_common.logit({"level": "EXCEPTION", "message": err})
def cli(jail): """Make a recursive snapshot of the jail and export to a file.""" ioc.IOCage(jail=jail).export()
def start_on_boot(self): self.logger.debug('Starting jails on boot: PENDING') ioc.IOCage(rc=True).start() self.logger.debug('Starting jails on boot: SUCCESS') return True
def __snapshot_jail__(self): import iocage_lib.iocage as ioc # Avoids dep issues name = f"ioc_upgrade_{self.date}" ioc.IOCage(jail=self.uuid, skip_jails=True, silent=True).snapshot(name)
def stop_on_shutdown(self): self.logger.debug('Stopping jails on shutdown: PENDING') ioc.IOCage(rc=True).stop() self.logger.debug('Stopping jails on shutdown: SUCCESS') return True
def cli(release, template, count, props, pkglist, basejail, clone_basejail, thickjail, empty, short, name, _uuid, thickconfig, proxy): if _uuid: try: uuid.UUID(_uuid, version=4) except ValueError: ioc_common.logit({ "level": "EXCEPTION", "message": "Please provide a valid UUID" }) else: if count > 1: ioc_common.logit({ "level": "EXCEPTION", "message": "Flag --count cannot be used with --uuid" }) if proxy: os.environ.update({'http_proxy': proxy, 'https_proxy': proxy}) if name: # noinspection Annotator valid = True if re.match(r"^[a-zA-Z0-9\._-]+$", name) else False if not valid: ioc_common.logit({ "level": "EXCEPTION", "message": f"Invalid character in {name}, please remove it." }) # At this point we don't care _uuid = name if release and "=" in release: ioc_common.logit({ "level": "EXCEPTION", "message": "Please supply a valid RELEASE!" }) elif release and release.lower() == "latest": release = ioc_common.parse_latest_release() if release: try: ioc_common.check_release_newer(release, major_only=True) except ValueError: # We're assuming they understand the implications of a custom # scheme iocroot = ioc.PoolAndDataset().get_iocroot() path = f'{iocroot}/releases/{release}/root' _release = ioc_common.get_jail_freebsd_version(path, release) try: ioc_common.check_release_newer(_release, major_only=True) except ValueError: # We tried pass # We don't really care it's not a RELEASE at this point. release = template if template else release if pkglist: _pkgformat = """ { "pkgs": [ "foo", "bar" ] }""" if not os.path.isfile(pkglist): ioc_common.logit({ "level": "EXCEPTION", "message": f"{pkglist} does not exist!\n" "Please supply a JSON file with the format:" f" {_pkgformat}" }) else: try: # Just try to open the JSON with the right key. with open(pkglist, "r") as p: json.load(p)["pkgs"] # noqa except json.JSONDecodeError: ioc_common.logit({ "level": "EXCEPTION", "message": "Please supply a valid" f" JSON file with the format:{_pkgformat}" }) if empty: release = "EMPTY" if clone_basejail: # We want to still create a basejail basejail = True iocage = ioc.IOCage(skip_jails=True) try: iocage.create(release, props, count, pkglist=pkglist, template=template, short=short, _uuid=_uuid, basejail=basejail, thickjail=thickjail, empty=empty, thickconfig=thickconfig, clone_basejail=clone_basejail) except (RuntimeError, ioc_exceptions.JailMissingConfiguration) as err: if template and "Dataset" in str(err) or str(err).startswith( 'Template'): # We want to list the available templates first ioc_common.logit({ "level": "ERROR", "message": f"Template: {release} not found!" }) templates = ioc.IOCage(silent=True).list('template') template_names = '' for temp in templates: template_names += '\n ' + temp[1] ioc_common.logit({ 'level': 'EXCEPTION', 'message': f'Created Templates:{template_names}' }) exit(1) else: # Standard errors ioc_common.logit({"level": "EXCEPTION", "message": err})
def cli(jail): """Update the supplied jail to the latest patchset""" iocage = ioc.IOCage(jail=jail) iocage.update()