<commit_before>from . import bindings
from .nvpair import ptr_to_dict
from .handle import LibZFSHandle
from .utils.conversion import boolean_t

from datetime import datetime

libzfs = bindings.libzfs
ffi = bindings.ffi

pool_state_t = bindings['pool_state_t']
zpool_errata_t = bindings['zpool_errata_t']
zpool_prop_t = bindings['zpool_prop_t']
zprop_type_t = bindings['zprop_type_t']
zpool_status_t = bindings['zpool_status_t']
zprop_source_t = bindings['zprop_source_t']
ZPOOL_MAXNAMELEN = bindings['ZPOOL_MAXNAMELEN']

NO_DEFAULT = object()


def _config_getter(key, default=NO_DEFAULT, transform=None):
    def _getter(self):
        value = self.get(bindings[key], default)
        if transform:
            value = transform(value)
        return value
    _getter.__name__ = key
    return property(_getter)


class ZPoolConfig(object):
    _parent = None
    _config = None

    def __init__(self, zpool, config):
        self._parent = zpool
        self._config = config

    def as_dict(self):
        return self._config

    def __getitem__(self, key):
        return self._config[key]

    def get(self, key, default=NO_DEFAULT):
        if default is NO_DEFAULT:
            return self._config.get(key)
        return self._config.get(key, default)

    name = _config_getter('ZPOOL_CONFIG_POOL_NAME', None)
    guid = _config_getter('ZPOOL_CONFIG_POOL_GUID', None)
    version = _config_getter('ZPOOL_CONFIG_VERSION', -1)
    initial_load_time = _config_getter('ZPOOL_CONFIG_LOADED_TIME', 0, lambda x: datetime.fromtimestamp(x[0]))

    def __repr__(self):
        return "<ZPoolConfig: %s>" % (self._parent.name)


class ZPool(object):
    _name = None
    _state = None
    _config = None
    _old_config = None
    _refreshed = False

    _properties = None
    _propertysources = None

    _status = None
    _status_extra = None
    _errata = None

    def __init__(self, hdl):
        self._hdl = hdl

    def __del__(self):
        if hasattr(self, '_hdl'):
            libzfs.zpool_close(self._hdl)

    @property
    def hdl(self):
        return self._hdl

    @property
    def name(self):
        if self._name is None:
            self._name = ffi.string(libzfs.zpool_get_name(self.hdl))
        return self._name

    @property
    def state(self):
        if self._state is None:
            state = libzfs.zpool_get_state(self.hdl)
            self._state = pool_state_t(state)
        return self._state

    def _get_status(self):
        if self._status is not None:
            return
        msgid = ffi.new('char **')
        errata = ffi.new('zpool_errata_t *')

        reason = libzfs.zpool_get_status(self.hdl, msgid, errata)

        self._status = zpool_status_t(reason)
        if msgid[0] == ffi.NULL:
            self._status_extra = ''
        else:
            self._status_extra = ffi.string(msgid[0])
        self._errata = zpool_errata_t(errata[0])

    @property
    def status_extra(self):
        self._get_status()
        return self._status_extra

    @property
    def status(self):
        self._get_status()
        return self._status

    @property
    def errata(self):
        self._get_status()
        return self._errata

    @LibZFSHandle.requires_refcount
    @LibZFSHandle.auto
    def refresh_properties(self):
        if self._properties is not None:
            if libzfs.zpool_props_refresh(self.hdl) != 0:
                raise Exception("Unable to refresh our zpool properties")
        self._properties = {}
        self._propertysources = {}
        for prop in zpool_prop_t:
            if prop >= zpool_prop_t.ZPOOL_NUM_PROPS:
                continue
            sourceholder = ffi.new('zprop_source_t *')
            ptype = zprop_type_t(libzfs.zpool_prop_get_type(int(prop)))
            if ptype == zprop_type_t.PROP_TYPE_NUMBER:
                value = libzfs.zpool_get_prop_int(self.hdl, int(prop), sourceholder)
            elif ptype == zprop_type_t.PROP_TYPE_INDEX:
                value = libzfs.zpool_get_prop_int(self.hdl, int(prop), sourceholder)
                valuestr = ffi.new('char **')
                if libzfs.zpool_prop_index_to_string(int(prop), value, valuestr) != 0:
                    value = None
                else:
                    value = ffi.string(valuestr[0])
            else:
                holder = ffi.new('char [%s]' % ZPOOL_MAXNAMELEN)
                if libzfs.zpool_get_prop_literal(self.hdl, int(prop), holder, ZPOOL_MAXNAMELEN,
                                                 sourceholder, boolean_t(True)) != 0:
                    value = None
                else:
                    value = ffi.string(holder)
            self._properties[prop] = value
            self._propertysources[prop] = zprop_source_t(sourceholder[0])

    @property
    def properties(self):
        if self._properties is None:
            self.refresh_properties()
        return self._properties

    @property
    def propertysources(self):
        if self._propertysources is None:
            self.refresh_properties()
        return self._propertysources

    @property
    def config(self):
        if self._config is None:
            config = libzfs.zpool_get_config(self.hdl, ffi.NULL)
            self._config = ZPoolConfig(self, ptr_to_dict(config, free=False))
        return self._config

    @property
    def old_config(self):
        if self._old_config is None:
            if self._refreshed:
                # Judging by zfs/cmd/zpool/zpool_main.c, we should ignore the 'old config'
                #  the first time we refresh the iostats.
                # The internals (zfs/lib/libzfs/libzfs_config.c) should probably clarify more
                #  on why.. for now we'll mimic what we see.
                old_config = libzfs.zpool_get_old_config(self.hdl)
            self._old_config = ZPoolConfig(self, ptr_to_dict(old_config, free=False))
        return self._old_config

    @LibZFSHandle.requires_refcount
    def refresh_stats(self):
        missing = ffi.new("boolean_t *")
        success = libzfs.zpool_refresh_stats(self.hdl, missing) == 0

        missing = bool(missing[0])

        success = success and not missing
        if success:
            self._config = None
            self._old_config = None
            self._refreshed = True

        return success

    def __repr__(self):
        return "<ZPool: %s: %s>" % (self.name, self.state.name)

    @classmethod
    @LibZFSHandle.requires_refcount
    def list(cls):
        pools = []

        @ffi.callback('zpool_iter_f')
        def _callback(handle, arg=None):
            zpool = ZPool(handle)
            pools.append(zpool)<commit_msg>-Change: ZPoolConfig is basically a dict, so use the dict base type.<commit_after>from . import bindings
from .nvpair import ptr_to_dict
from .handle import LibZFSHandle
from .utils.conversion import boolean_t

from datetime import datetime

libzfs = bindings.libzfs
ffi = bindings.ffi

pool_state_t = bindings['pool_state_t']
zpool_errata_t = bindings['zpool_errata_t']
zpool_prop_t = bindings['zpool_prop_t']
zprop_type_t = bindings['zprop_type_t']
zpool_status_t = bindings['zpool_status_t']
zprop_source_t = bindings['zprop_source_t']
ZPOOL_MAXNAMELEN = bindings['ZPOOL_MAXNAMELEN']


def _config_getter(key, default, transform=None):
    def _getter(self):
        value = self.get(bindings[key], default)
        if transform:
            value = transform(value)
        return value
    _getter.__name__ = key
    return property(_getter)


class ZPoolConfig(dict):
    name = _config_getter('ZPOOL_CONFIG_POOL_NAME', None)
    guid = _config_getter('ZPOOL_CONFIG_POOL_GUID', None)
    version = _config_getter('ZPOOL_CONFIG_VERSION', -1)
    initial_load_time = _config_getter('ZPOOL_CONFIG_LOADED_TIME', 0, lambda x: datetime.fromtimestamp(x[0]))

    def __repr__(self):
        return "<ZPoolConfig: %s>" % (self.name)


class ZPool(object):
    _name = None
    _state = None
    _config = None
    _old_config = None
    _refreshed = False

    _properties = None
    _propertysources = None

    _status = None
    _status_extra = None
    _errata = None

    def __init__(self, hdl):
        self._hdl = hdl

    def __del__(self):
        if hasattr(self, '_hdl'):
            libzfs.zpool_close(self._hdl)

    @property
    def hdl(self):
        return self._hdl

    @property
    def name(self):
        if self._name is None:
            self._name = ffi.string(libzfs.zpool_get_name(self.hdl))
        return self._name

    @property
    def state(self):
        if self._state is None:
            state = libzfs.zpool_get_state(self.hdl)
            self._state = pool_state_t(state)
        return self._state

    def _get_status(self):
        if self._status is not None:
            return
        msgid = ffi.new('char **')
        errata = ffi.new('zpool_errata_t *')

        reason = libzfs.zpool_get_status(self.hdl, msgid, errata)

        self._status = zpool_status_t(reason)
        if msgid[0] == ffi.NULL:
            self._status_extra = ''
        else:
            self._status_extra = ffi.string(msgid[0])
        self._errata = zpool_errata_t(errata[0])

    @property
    def status_extra(self):
        self._get_status()
        return self._status_extra

    @property
    def status(self):
        self._get_status()
        return self._status

    @property
    def errata(self):
        self._get_status()
        return self._errata

    @LibZFSHandle.requires_refcount
    @LibZFSHandle.auto
    def refresh_properties(self):
        if self._properties is not None:
            if libzfs.zpool_props_refresh(self.hdl) != 0:
                raise Exception("Unable to refresh our zpool properties")
        self._properties = {}
        self._propertysources = {}
        for prop in zpool_prop_t:
            if prop >= zpool_prop_t.ZPOOL_NUM_PROPS:
                continue
            sourceholder = ffi.new('zprop_source_t *')
            ptype = zprop_type_t(libzfs.zpool_prop_get_type(int(prop)))
            if ptype == zprop_type_t.PROP_TYPE_NUMBER:
                value = libzfs.zpool_get_prop_int(self.hdl, int(prop), sourceholder)
            elif ptype == zprop_type_t.PROP_TYPE_INDEX:
                value = libzfs.zpool_get_prop_int(self.hdl, int(prop), sourceholder)
                valuestr = ffi.new('char **')
                if libzfs.zpool_prop_index_to_string(int(prop), value, valuestr) != 0:
                    value = None
                else:
                    value = ffi.string(valuestr[0])
            else:
                holder = ffi.new('char [%s]' % ZPOOL_MAXNAMELEN)
                if libzfs.zpool_get_prop_literal(self.hdl, int(prop), holder, ZPOOL_MAXNAMELEN,
                                                 sourceholder, boolean_t(True)) != 0:
                    value = None
                else:
                    value = ffi.string(holder)
            self._properties[prop] = value
            self._propertysources[prop] = zprop_source_t(sourceholder[0])

    @property
    def properties(self):
        if self._properties is None:
            self.refresh_properties()
        return self._properties

    @property
    def propertysources(self):
        if self._propertysources is None:
            self.refresh_properties()
        return self._propertysources

    @property
    def config(self):
        if self._config is None:
            config = libzfs.zpool_get_config(self.hdl, ffi.NULL)
            self._config = ZPoolConfig(ptr_to_dict(config, free=False))
        return self._config

    @property
    def old_config(self):
        if self._old_config is None:
            if self._refreshed:
                # Judging by zfs/cmd/zpool/zpool_main.c, we should ignore the 'old config'
                #  the first time we refresh the iostats.
                # The internals (zfs/lib/libzfs/libzfs_config.c) should probably clarify more
                #  on why.. for now we'll mimic what we see.
                old_config = libzfs.zpool_get_old_config(self.hdl)
            self._old_config = ZPoolConfig(ptr_to_dict(old_config, free=False))
        return self._old_config

    @LibZFSHandle.requires_refcount
    def refresh_stats(self):
        missing = ffi.new("boolean_t *")
        success = libzfs.zpool_refresh_stats(self.hdl, missing) == 0

        missing = bool(missing[0])

        success = success and not missing
        if success:
            self._config = None
            self._old_config = None
            self._refreshed = True

        return success

    def __repr__(self):
        return "<ZPool: %s: %s>" % (self.name, self.state.name)

    @classmethod
    @LibZFSHandle.requires_refcount
    def list(cls):
        pools = []

        @ffi.callback('zpool_iter_f')
        def _callback(handle, arg=None):
            zpool = ZPool(handle)
            pools.append(zpool)
{'fraction_non_alphanumeric': 0.093025060128792, 'fraction_numerical': 0.002017223989448367, 'mean_word_length': 2.235441767068273, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '297530', 'n_tokens_mistral': 4173, 'n_tokens_neox': 4019, 'n_words': 840}