def export(self): """Exports storage pool. pool = Pool('dpool') pool.export() """ sh.zpool('export', self.name) return True
def import_(self): """Imports storage pool. pool = Pool('dpool') pool.import_() """ sh.zpool('import', self.name) return True
def upgrade(self): """Upgrades storage pool version. pool = Pool('dpool') pool.upgrade() """ sh.zpool('upgrade', self.name) return True
def clear(self): """Clears any errors on storage pool. pool = Pool('dpool') pool.clear() """ sh.zpool('clear', self.name) return True
def destroy(self, confirm=False): """Destroys storage pool. pool = Pool('dpool') pool.destroy() """ if confirm is not True: raise ZfsError('Destroy of storage pool requires confirm=True') sh.zpool('destroy', self.name) return True
def destroy(self, confirm=False): """Destroys storage pool. pool = Pool('dpool') pool.destroy() """ if not confirm: raise LoggedException('Destroy of storage pool requires confirm=True') sh.zpool('destroy', self.name) return True
def recover(chroot): """ Import pool and mount filesystem in prep for recovery efforts """ sh.zpool.export('-a', _fg=True) sh.zpool('import', '-Nf', '-R', paths.zroot, config.pool_name, _fg=True) sh.zfs('load-key', '-a', _fg=True) sh.zfs.mount(config.os_root_ds, _fg=True) sh.zfs.mount('-a', _fg=True) other_mounts() if chroot: sh.chroot(paths.zroot, '/bin/bash', '--login', _fg=True)
def exists(self): """Checks if pool exists. pool = Pool('dpool') pool.exists() """ try: sh.zpool('list', self.name) except sh.ErrorReturnCode_1: return False return True
def _get(self, *props): """Gets pool property. pool = Pool('dpool') pool.properties._get('alloc', 'free') pool.properties._get('all') """ assert props ret = [] skip = 1 try: out = sh.zpool('get', ','.join(props), self._parent.name) except sh.ErrorReturnCode_2: raise KeyError for line in out: if skip > 0: skip -= 1 continue line = line.rstrip("\n") (obj_name, name, value, source) = line.split(None, 3) ret.append(PoolProperty(self, name, value, source)) # If we only requested a single property from a single object that # isn't the magic word 'all', just return the value. if len(props) == 1 and len(ret) == len(props) and 'all' not in props: ret = ret[0] return ret
def status(): print(f'$ config values --------------------\n') print(config) print(f'\n$ sgdisk --print {config.disk_dev} --------------------\n') sh.sgdisk('--print', config.disk_dev, _out=sys.stdout, _ok_code=[0,2]) print(f'\n$ blkid --------------------------\n') sh.blkid(_out=sys.stdout,) print('\n$ zpool list ---------------------------\n') sh.zpool('list', _out=sys.stdout) print('\n$ zfs list ---------------------------\n') sh.zfs('list', _out=sys.stdout) print('\n$ zfs mount ---------------------------\n') sh.zfs('mount', _out=sys.stdout)
def zpool_status_parse(from_string=None): if not from_string: from_string = sh.zpool("status", "-v").stdout ret = {} pools_m = from_string.split("pool:") for pool_m in pools_m: if not pool_m.strip(): continue for m in re.finditer( " (?P<pool>[^\n]+)\n *" "state: (?P<state>[^ ]+)\n *" "(status: (?P<status>(.|\n)+)\n *)??" "scan: (?P<scan>(.|\n)*)\n *" "config: ?(?P<config>(.|\n)*)\n *" "errors: (?P<errors>[^\n]*)", pool_m, ): m = m.groupdict() pool_name = m.pop("pool") pool = ret[pool_name] = m devices = pool["devices"] = {} parent = None for device in re.finditer( "(?P<indent>[ \t]+)(?P<name>[^ \t\n]+)( +(?P<state>[^ \t\n]+) +)?(" "(?P<read>[^ \t\n]+) +(?P<write>[^ \t\n]+) +" "(?P<cksum>[^\n]+))?(?P<notes>[^\n]+)?\n", pool.pop("config"), ): device = device.groupdict() if not device["name"] or device["name"] in ("NAME", pool_name): continue device_name = device.pop("name").strip() device.pop("indent") is_parent = False for device_type in ("mirror", "log", "raid", "spare", "cache"): if device_name.startswith(device_type): parent = device_name devices[device_name] = device devices[parent]["children"] = {} is_parent = True break if is_parent: continue if parent: devices[parent]["children"][device_name] = device else: devices[device_name] = device return ret
def __call__(self, arg=None): if not arg: arg = sh.zpool('status', '-v').stdout ret = OrderedDict() for v in self.p_status.parseString(str(arg)).asList(): v = dict(v) v['config'] = self._config_parser(v) pool_name = v.pop('pool') ret[pool_name] = v return ret
def status(self): """Returns status of storage pool. pool = Pool('dpool') pool.status() """ p = ZpoolStatusParser() out = sh.zpool('status', '-v', self.name).stdout ret = p(out) return ret[self.name]
def status(self, devices=False): """Returns status of storage pool. pool = Pool('dpool') pool.status() """ out = sh.zpool('status', '-v', self.name).stdout ret = zpool_status_parse2(from_string=out) ret = ret[self.name] if not devices: ret.pop('devices', None) return ret
def _set(self, k, v, ignore=False): """Sets pool property. pool = Pool('dpool') pool.properties._set('readonly', 'on') """ if ignore: return prop = None if isinstance(v, PoolProperty): prop = v v = prop.value try: sh.zpool('set', '%s=%s' % (k, v), self._parent.name) except sh.ErrorReturnCode_2: raise ValueError if prop: prop.value = v
def iostat(self, capture_length=30): """Returns iostat of storage pool. pool = Pool('dpool') pool.iostat() """ timestamp = None skip_past_dashline = False for line in sh.zpool('iostat', '-T', 'u', self.name, capture_length, 2): line = line.rstrip("\n") # Got a timestamp if line.isdigit(): # If this is our first record, skip till we get the header seperator if not timestamp: skip_past_dashline = True # TZify the timestamp timestamp = timezone.make_aware( datetime.fromtimestamp(int(line)), timezone.get_current_timezone()) continue # If we haven't gotten the dashline yet, wait till the line after it if skip_past_dashline: if line.startswith('-----'): skip_past_dashline = False continue # Either way, let's not worry about them if line.startswith('-----'): continue # If somehow we got here without a timestamp, something is probably wrong. if not timestamp: raise LoggedException("Got unexpected input from zpool iostat: %s", line) # Parse iostats output j = {} (j['name'], j['alloc'], j['free'], j['iops_read'], j['iops_write'], j['bandwidth_read'], j['bandwidth_write']) = line.strip().split() j['timestamp'] = timestamp j.pop('name') return j
def ui_command_zpool_iostat(self): return sh.zpool('iostat', '-v', '5', '2')
def zpool_status_parse2(from_string=None): if not from_string: from_string = sh.zpool("status", "-v").stdout ret = {} pools_m = from_string.split("pool:") for pool_m in pools_m: if not pool_m.strip(): continue for m in re.finditer( " (?P<pool>[^\n]+)\n *" "state: (?P<state>[^ ]+)\n *" # Some versions of zpool use tabs, some do not. "(status: (?P<status>(.|\n {8}|\n\t)+)\n *)??" "(action: (?P<action>(.|\n {8}|\n\t)+)\n *)??" "(see: (?P<see>(.|\n {8}|\n\t)+)\n *)??" "scan: (?P<scan>(.|\n)*)\n *" "config: ?(?P<config>(.|\n)*)\n *" "errors: (?P<errors>[^\n]*)", pool_m, ): m = m.groupdict() # Remove nasty newlines and prefixing whitespace for k, v in m.items(): if k == "config" or not v: continue # Some versions of zpool use tabs, some do not. m[k] = v.replace("\n\t", " ") m[k] = m[k].replace("\n ", " ") pool_name = m.pop("pool") pool = ret[pool_name] = m devices = pool["devices"] = {} _devices = [ d.groupdict() for d in re.finditer( "(?P<indent>[ \t]+)(?P<name>[^ \t\n]+)( +(?P<state>[^ \t\n]+) +)?(" "(?P<read>[^ \t\n]+) +(?P<write>[^ \t\n]+) +" "(?P<cksum>[^\n]+))?(?P<notes>[^\n]+)?\n", pool.pop("config"), ) ] _devices = filter(lambda d: d["name"] and not d["name"] == "NAME", _devices) ancestry = [] # ancestry_level = 0 dev_types = {"devices": Device, "logs": Log, "cache": Cache, "spare": Spare} dev_type_cls = None for device in _devices: device_name = device.pop("name").strip() # Some versions of zpool use tabs, some do not. cur_level = (len(device["indent"].replace("\t", " ")) - 8) / 2 # level_diff = cur_level - ancestry_level ancestry = ancestry[:cur_level] ancestry.append(device_name) # ancestry_level = cur_level # pp(dict(name=device_name, cur_level=cur_level, level_diff=level_diff, ancestry=ancestry)) if cur_level == 0 and device_name in dev_types.keys() or device_name == pool_name: if device_name == pool_name: device_name = "devices" dev_type_cls = dev_types.pop(device_name) ancestry.append(device_name) continue cur = None for level in ancestry[:cur_level]: # print level if not cur: cur = devices continue if not level in cur: cur[level] = {} cur = cur[level] if device_name.startswith("mirror-"): cur[device_name] = Mirror() continue dev = dev_type_cls(device_name) if isinstance(cur, Mirror): cur.append(dev, _device_check=False) else: cur[device_name] = dev return ret