sdwilsh / aiotruenas-client Goto Github PK
View Code? Open in Web Editor NEWAn asyncio-friendly API for TrueNAS
License: MIT License
An asyncio-friendly API for TrueNAS
License: MIT License
As the name
for a disk can change (Like when rebooting or replacing disks), maybe it would be better to use the serial
as the key.
Fix typing error in class: VirturalMachine -> VirtualMachine
It would be nice to have Disk
objects exposed in pools, which is available from the topology.
I would like support for authenticating with a token. There is the auth.token
service on websocket api.
Id be willing to contribute to this. I'm not too familiar with websocket however, so I need to take a closer look.
My idea would be to create mixin classes to provide methods for connecting via either auth method in the CachingMachine
class. This would also enable other auth methods. A separate class method like create_from_token
could be used.
It'll make it easier to track down issues like sdwilsh/hass-truenas#85
How do you feel about loading the test data returned by fakeserver.py
from external files? We could use data dumped from running FreeNAS instances. We could also potentially run tests for different version of FreeNAS if the data was in a path like ./test/data/freenas13.3/disks.query.json
.
When I'm pulling data on disks from a Freenas 11.2-U7 system, I get serials with trailing white space??
{'acousticlevel': 'DISABLED',
'advpowermgmt': 'DISABLED',
'description': '',
'devname': 'da22',
'enclosure_slot': None,
'expiretime': None,
'hddstandby': 'ALWAYS ON',
'identifier': '{serial_lunid}8DH7VKWH _5000cca253d1a9de',
'multipath_member': '',
'multipath_name': '',
'name': 'da22',
'number': 22,
'passwd': '',
'serial': '8DH7VKWH ',
'size': '12000138625024',
'smartoptions': '',
'subsystem': 'da',
'togglesmart': True,
'transfermode': 'Auto'},
Maybe we need to to use str.trim()
? What kind of data do we get from other systems?
It would be convenient to be able to load credentials from environment variables. Handy when running containerized applications.
This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.
These updates have all been created already. Click a checkbox below to force a retry/rebase of any.
.github/workflows/build.yml
actions/checkout v4
actions/setup-python v4
.github/workflows/lint.yml
actions/checkout v4
.github/workflows/publish.yml
actions/checkout v4
actions/setup-python v4
pyproject.toml
setuptools >= 40.6.0
requirements-dev.txt
black ==24.4.2
flake8 ==6.1.0
isort ==5.13.2
pre-commit ==3.5.0
pytest ==7.4.4
pyyaml ==6.0.1
yamllint ==1.35.1
.pre-commit-config.yaml
ambv/black 23.12.1
pycqa/flake8 7.0.0
pycqa/isort 5.13.2
adrienverge/yamllint v1.35.1
setup.cfg
websockets ==12.0
meteor-ejson == 1.1.0
Earthfile
python 3.12
pre-commit 3.5.0
pyright 1.1.365
renovate/renovate 36
earthly.sh
earthly/earthly v0.8.13
The FreeNAS API V2 is available two ways: Websocket and RESTful.
Maybe the PyPI package could be renamed to something like py-freenas-ws
(Or some other, funnier, "pythonic" name). If someone else comes along implementing the RESTful API, it would be nice for them to be able to use the name py-freenas-rest
.
Just a thought.....
Looking at datasets, there are two types: FILESYSTEM & DATASET
They do not have the same fields. My first though on how to handle this, would be to have one abstract class Dataset with the
properties shared by both types of dataset. Then we would need two other classes ( DatasetFilesystem and DatasetDataset) implementng Dataset end expanding with relevant properties. What Class of object should be in the list datasets in Machine? That approach seems like a mess.
I guess you could just have the two abstract classes Filesystem & Dataset. Then the Machine would a list of datasets and another list of filesystems?
What do you think @sdwilsh ? What would be a good way to handle this?
{
'atime': {'parsed': False,
'rawvalue': 'off',
'source': 'INHERITED',
'value': 'OFF'},
'casesensitivity': {'parsed': 'sensitive',
'rawvalue': 'sensitive',
'source': 'NONE',
'value': 'SENSITIVE'},
'children': [],
'compression': {'parsed': 'lz4',
'rawvalue': 'lz4',
'source': 'INHERITED',
'value': 'LZ4'},
'compressratio': {'parsed': '1.63x',
'rawvalue': '1.63x',
'source': 'NONE',
'value': '1.63x'},
'copies': {'parsed': 1,
'rawvalue': '1',
'source': 'DEFAULT',
'value': '1'},
'deduplication': {'parsed': 'off',
'rawvalue': 'off',
'source': 'DEFAULT',
'value': 'OFF'},
'exec': {'parsed': True,
'rawvalue': 'on',
'source': 'DEFAULT',
'value': 'ON'},
'id': 'freenas-boot/grub',
'mountpoint': None,
'name': 'freenas-boot/grub',
'origin': {'parsed': '',
'rawvalue': '',
'source': 'NONE',
'value': ''},
'pool': 'freenas-boot',
'quota': {'parsed': None,
'rawvalue': '0',
'source': 'DEFAULT',
'value': None},
'readonly': {'parsed': False,
'rawvalue': 'off',
'source': 'DEFAULT',
'value': 'OFF'},
'recordsize': {'parsed': 131072,
'rawvalue': '131072',
'source': 'DEFAULT',
'value': '128K'},
'refquota': {'parsed': None,
'rawvalue': '0',
'source': 'DEFAULT',
'value': None},
'refreservation': {'parsed': None,
'rawvalue': '0',
'source': 'DEFAULT',
'value': None},
'reservation': {'parsed': None,
'rawvalue': '0',
'source': 'DEFAULT',
'value': None},
'share_type': 'UNIX',
'snapdir': {'parsed': None,
'rawvalue': 'hidden',
'source': 'DEFAULT',
'value': 'HIDDEN'},
'sync': {'parsed': 'standard',
'rawvalue': 'standard',
'source': 'DEFAULT',
'value': 'STANDARD'},
'type': 'FILESYSTEM'
}
,
{
'children': [],
'comments': {'parsed': 'Virtual Machines go here',
'rawvalue': 'Virtual Machines go here',
'source': 'INHERITED',
'value': 'Virtual Machines go here'},
'compression': {'parsed': 'lz4',
'rawvalue': 'lz4',
'source': 'INHERITED',
'value': 'LZ4'},
'compressratio': {'parsed': '1.00x',
'rawvalue': '1.00x',
'source': 'NONE',
'value': '1.00x'},
'copies': {'parsed': 1, 'rawvalue': '1', 'source': 'DEFAULT', 'value': '1'},
'deduplication': {'parsed': 'off',
'rawvalue': 'off',
'source': 'DEFAULT',
'value': 'OFF'},
'id': 'ultraman/vm/pfSense_pfSense_clone2',
'mountpoint': None,
'name': 'ultraman/vm/pfSense_pfSense_clone2',
'origin': {'parsed': 'ultraman/vm/pfSense@pfSense_clone2',
'rawvalue': 'ultraman/vm/pfSense@pfSense_clone2',
'source': 'NONE',
'value': 'ultraman/vm/pfSense@pfSense_clone2'},
'pool': 'ultraman',
'readonly': {'parsed': False,
'rawvalue': 'off',
'source': 'DEFAULT',
'value': 'OFF'},
'refreservation': {'parsed': None,
'rawvalue': '0',
'source': 'DEFAULT',
'value': None},
'reservation': {'parsed': None,
'rawvalue': '0',
'source': 'DEFAULT',
'value': None},
'share_type': None,
'sync': {'parsed': 'standard',
'rawvalue': 'standard',
'source': 'DEFAULT',
'value': 'STANDARD'},
'type': 'VOLUME',
'volblocksize': {'parsed': 16384,
'rawvalue': '16384',
'source': 'NONE',
'value': '16K'},
'volsize': {'parsed': 8589934592,
'rawvalue': '8589934592',
'source': 'LOCAL',
'value': '8G'}
}
I don't know what type of arguments can be used with invoke_method.py. The current documentation reads: python invoke_method.py disk.query
Add an example demonstrating the use of the flag --arguments to it's description in --help
Looks like we need to build some code to handle disconnections from the server and automatically reconnect. python-websockets/websockets#414 has some sample code.
Putting this up to get thoughts.
As I'm thinking about how this might be used outside of sdwilsh/hass-freenas, I'm thinking changing the API up a bit.
from pyfreenas import Machine as FreeNASMachine
machine = await Machine.create(
"hostname.of.machine",
username="someuser",
password="password",
)
await machine.refresh()
disks = machine.disks
vms = machine.vms
One con I see with this approach is that if a user doesn't care about disks and only vms, they have to fetch this data all the time.
from pyfreenas import Machine as FreeNASMachine
machine = await Machine.create(
"hostname.of.machine",
username="someuser",
password="password",
)
disks = await machine.refresh_disks()
vms = await machine.refresh_vms()
# Still available, as state is still cached
disks = machine.disks
vms = machine.vms
This addresses the con above, and any call site that wants to setup a job that pools every so often to update the state can still trivially do so.
On v2 API at least, certain information is only available via the stats
service. stats.get_sources
returns a list of sources of data
{'aggregation-cpu-average': ['cpu-interrupt',
'cpu-user',
'cpu-idle',
'cpu-nice',
'cpu-system'],
'aggregation-cpu-sum': ['cpu-system',
'cpu-user',
'cpu-nice',
'cpu-idle',
'cpu-interrupt'],
'cpu-0': ['cpu-system', 'cpu-interrupt', 'cpu-idle', 'cpu-nice', 'cpu-user'],
'cpu-1': ['cpu-user', 'cpu-system', 'cpu-interrupt', 'cpu-idle', 'cpu-nice'],
'cpu-2': ['cpu-system', 'cpu-nice', 'cpu-idle', 'cpu-user', 'cpu-interrupt'],
'cpu-3': ['cpu-nice', 'cpu-idle', 'cpu-system', 'cpu-interrupt', 'cpu-user'],
'cputemp-0': ['temperature'],
'cputemp-1': ['temperature'],
'cputemp-2': ['temperature'],
'cputemp-3': ['temperature'],
'ctl-ioctl': ['disk_time-0-0',
'disk_octets-0-0',
'disk_ops',
'disk_time',
'disk_octets',
'disk_ops-0-0'],
'ctl-tpc': ['disk_ops',
'disk_time-0-0',
'disk_octets-0-0',
'disk_time',
'disk_octets',
'disk_ops-0-0'],
'df-mnt-storage': ['df_complex-free',
'df_complex-used',
'df_complex-reserved'],
'df-mnt-storage-Music': ['df_complex-free',
'df_complex-used',
'df_complex-reserved'],
'df-mnt-storage-Pictures': ['df_complex-reserved',
'df_complex-free',
'df_complex-used'],
'df-mnt-storage-Videos': ['df_complex-reserved',
'df_complex-free',
'df_complex-used'],
'df-mnt-storage-iocage': ['df_complex-reserved',
'df_complex-used',
'df_complex-free'],
'df-mnt-storage-iocage-download': ['df_complex-reserved',
'df_complex-free',
'df_complex-used'],
'df-mnt-storage-iocage-download-12.1-RELEASE': ['df_complex-free',
'df_complex-used',
'df_complex-reserved'],
'df-mnt-storage-iocage-images': ['df_complex-used',
'df_complex-free',
'df_complex-reserved'],
'df-mnt-storage-iocage-jails': ['df_complex-used',
'df_complex-reserved',
'df_complex-free'],
'df-mnt-storage-iocage-jails-plexjail': ['df_complex-free',
'df_complex-used',
'df_complex-reserved'],
'df-mnt-storage-iocage-jails-plexjail-root': ['df_complex-free',
'df_complex-reserved',
'df_complex-used'],
'df-mnt-storage-iocage-log': ['df_complex-reserved',
'df_complex-free',
'df_complex-used'],
'df-mnt-storage-iocage-releases': ['df_complex-free',
'df_complex-reserved',
'df_complex-used'],
'df-mnt-storage-iocage-releases-12.1-RELEASE': ['df_complex-reserved',
'df_complex-free',
'df_complex-used'],
'df-mnt-storage-iocage-releases-12.1-RELEASE-root': ['df_complex-reserved',
'df_complex-used',
'df_complex-free'],
'df-mnt-storage-iocage-templates': ['df_complex-free',
'df_complex-used',
'df_complex-reserved'],
'df-root': ['df_complex-free', 'df_complex-reserved', 'df_complex-used'],
'df-var-tmp-xxxxxxx': ['df_complex-reserved',
'df_complex-used',
'df_complex-free'],
'disk-ada0': ['disk_time',
'disk_octets',
'disk_io_time',
'disk_ops',
'pending_operations'],
'disk-ada1': ['disk_time',
'disk_octets',
'pending_operations',
'disk_io_time',
'disk_ops'],
'disk-ada2': ['disk_io_time',
'pending_operations',
'disk_ops',
'disk_time',
'disk_octets'],
'disktemp-ada0': ['temperature'],
'disktemp-ada1': ['temperature'],
'disktemp-ada2': ['temperature'],
'geom_stat': ['geom_ops_rwd-ada2',
'geom_ops-ada0',
'geom_queue-ada0',
'geom_bw-ada0',
'geom_latency-ada2',
'geom_busy_percent-ada1',
'geom_queue-ada1',
'geom_ops-ada1',
'geom_busy_percent-ada0',
'geom_bw-ada1',
'geom_latency-ada0',
'geom_bw-ada2',
'geom_queue-ada2',
'geom_ops-ada2',
'geom_ops_rwd-ada0',
'geom_busy_percent-ada2',
'geom_latency-ada1',
'geom_ops_rwd-ada1'],
'interface-bridge0': ['if_octets', 'if_packets', 'if_errors'],
'interface-em0': ['if_errors', 'if_octets', 'if_packets'],
'interface-igb0': ['if_packets', 'if_errors', 'if_octets'],
'interface-vnet0.1': ['if_octets', 'if_packets', 'if_errors'],
'interface-vnet0.2': ['if_errors', 'if_packets', 'if_octets'],
'interface-vnet0.3': ['if_packets', 'if_errors', 'if_octets'],
'interface-vnet0.4': ['if_packets', 'if_octets', 'if_errors'],
'interface-vnet0.5': ['if_errors', 'if_packets', 'if_octets'],
'load': ['load'],
'memory': ['memory-active',
'memory-cache',
'memory-inactive',
'memory-wired',
'memory-laundry',
'memory-free'],
'nfsstat-client': ['nfsstat-pathconf',
'nfsstat-readdir',
'nfsstat-fsinfo',
'nfsstat-symlink',
'nfsstat-rmdir',
'nfsstat-mknod',
'nfsstat-create',
'nfsstat-commit',
'nfsstat-lookup',
'nfsstat-access',
'nfsstat-setattr',
'nfsstat-read',
'nfsstat-write',
'nfsstat-remove',
'nfsstat-readirplus',
'nfsstat-readlink',
'nfsstat-getattr',
'nfsstat-mkdir',
'nfsstat-link',
'nfsstat-rename',
'nfsstat-fsstat'],
'nfsstat-server': ['nfsstat-read',
'nfsstat-commit',
'nfsstat-write_bytes',
'nfsstat-create',
'nfsstat-readlink',
'nfsstat-fsinfo',
'nfsstat-getattr',
'nfsstat-mkdir',
'nfsstat-access',
'nfsstat-lookup',
'nfsstat-read_bytes',
'nfsstat-mknod',
'nfsstat-readdir',
'nfsstat-symlink',
'nfsstat-link',
'nfsstat-pathconf',
'nfsstat-rmdir',
'nfsstat-remove',
'nfsstat-write',
'nfsstat-fsstat',
'nfsstat-rename',
'nfsstat-setattr',
'nfsstat-readirplus'],
'processes': ['ps_state-wait',
'ps_state-stopped',
'ps_state-zombies',
'ps_state-idle',
'ps_state-running',
'ps_state-blocked',
'ps_state-sleeping'],
'rrdcached': ['operations-receive-flush',
'operations-write-updates',
'queue_length',
'operations-receive-update',
'counter-journal-bytes',
'counter-journal-rotates',
'gauge-tree_nodes',
'operations-write-data_sets',
'gauge-tree_depth'],
'swap': ['swap-free', 'swap-used'],
'uptime': ['uptime'],
'zfs_arc': ['cache_eviction-ineligible',
'cache_size-c_max',
'cache_eviction-cached',
'hash_collisions',
'cache_size-p',
'cache_result-mru_ghost-hit',
'cache_size-bonus_size',
'cache_size-mfu_ghost_size',
'cache_result-mru-hit',
'cache_size-arc',
'cache_result-demand_metadata-hit',
'cache_size-c',
'cache_result-prefetch_metadata-miss',
'cache_size-mru_size',
'cache_result-prefetch_data-hit',
'cache_result-prefetch_data-miss',
'cache_operation-deleted',
'cache_size-hdr_size',
'cache_size-mru_ghost_size',
'cache_size-anon_size',
'cache_size-L2',
'cache_ratio-arc',
'cache_ratio-L2',
'mutex_operations-miss',
'cache_eviction-eligible',
'cache_result-demand_data-hit',
'cache_result-mfu_ghost-hit',
'io_octets-L2',
'cache_size-mfu_size',
'memory_throttle_count',
'cache_size-dbuf_size',
'cache_result-mfu-hit',
'cache_size-dnode_size',
'cache_result-demand_data-miss',
'cache_size-metadata_size',
'cache_result-prefetch_metadata-hit',
'cache_size-c_min'],
'zfs_arc_v2': ['gauge_arcstats_raw_l2write-l2_write_not_cacheable',
'gauge_arcstats_raw_cp-c',
'arcstat_ratio_metadata-demand_metadata_misses',
'gauge_arcstats_raw_counts-recycle_miss',
'gauge_arcstats_raw_l2write-l2_write_pios',
'gauge_arcstats_raw_prefetch-prefetch_data_misses',
'gauge_arcstats_raw_l2write-l2_write_full',
'gauge_arcstats_raw_l2-l2_rw_clash',
'gauge_arcstats_raw_l2-l2_misses',
'gauge_arcstats_raw_hash-hash_chains',
'gauge_arcstats_raw_l2write-l2_write_in_l2',
'gauge_arcstats_raw_prefetch-prefetch_data_hits',
'gauge_arcstats_raw_mru-mru_ghost_hits',
'gauge_arcstats_raw_l2write-l2_write_buffer_list_null_iter',
'gauge_arcstats_raw_duplicate-duplicate_reads',
'arcstat_ratio_data-demand_data_misses',
'gauge_arcstats_raw_cp-c_min',
'arcstat_ratio_mu-mru_hits',
'gauge_arcstats_raw_l2_compress-l2_compress_successes',
'gauge_arcstats_raw_counts-allocated',
'gauge_arcstats_raw_size-other_size',
'gauge_arcstats_raw_demand-demand_metadata_hits',
'gauge_arcstats_raw_mru-mfu_hits',
'gauge_arcstats_raw_arcmeta-arc_meta_limit',
'gauge_arcstats_raw_l2writes-l2_writes_error',
'gauge_arcstats_raw_size-size',
'gauge_arcstats_raw_arcmeta-arc_meta_used',
'gauge_arcstats_raw_l2write-l2_write_trylock_fail',
'gauge_arcstats_raw_cp-p',
'arcstat_ratio_arc-hits',
'gauge_arcstats_raw_l2evict-l2_evict_lock_retry',
'gauge_arcstats_raw_counts-mutex_miss',
'gauge_arcstats_raw-l2_asize',
'gauge_arcstats_raw_memcount-memory_throttle_count',
'gauge_arcstats_raw_l2abort-l2_abort_lowmem',
'gauge_arcstats_raw_arcmeta-arc_meta_max',
'arcstat_ratio_mu-mru_ghost_hits',
'gauge_arcstats_raw_l2-l2_io_error',
'gauge_arcstats_raw_evict-evict_l2_cached',
'gauge_arcstats_raw-l2_size',
'gauge_arcstats_raw_counts-deleted',
'gauge_arcstats_raw_l2_compress-l2_compress_failures',
'gauge_arcstats_raw-l2_hdr_size',
'arcstat_ratio_data-prefetch_data_misses',
'gauge_arcstats_raw_l2_free-l2_cdata_free_on_write',
'gauge_arcstats_raw_prefetch-prefetch_metadata_hits',
'gauge_arcstats_raw_demand-demand_metadata_misses',
'arcstat_ratio_mu-mfu_hits',
'gauge_arcstats_raw_l2writes-l2_writes_done',
'arcstat_ratio_arc-misses',
'gauge_arcstats_raw_duplicate-duplicate_buffers',
'gauge_arcstats_raw_l2write-l2_write_passed_headroom',
'arcstat_ratio_arc-l2_hits',
'gauge_arcstats_raw_l2write-l2_write_io_in_progress',
'arcstat_ratio_arc-l2_misses',
'gauge_arcstats_raw_hits_misses-misses',
'gauge_arcstats_raw_l2_compress-l2_compress_zeros',
'arcstat_ratio_mu-mfu_ghost_hits',
'arcstat_ratio_data-demand_data_hits',
'gauge_arcstats_raw_hash-hash_chain_max',
'gauge_arcstats_raw_arcmeta-arc_meta_min',
'gauge_arcstats_raw_l2-l2_feeds',
'gauge_arcstats_raw_hash-hash_collisions',
'gauge_arcstats_raw_l2write-l2_write_buffer_iter',
'gauge_arcstats_raw_l2write-l2_write_spa_mismatch',
'arcstat_ratio_metadata-prefetch_metadata_misses',
'gauge_arcstats_raw_l2write-l2_write_buffer_list_iter',
'gauge_arcstats_raw_l2writes-l2_writes_hdr_miss',
'gauge_arcstats_raw_hash-hash_elements_max',
'gauge_arcstats_raw_size-data_size',
'gauge_arcstats_raw_evict-evict_l2_eligible',
'gauge_arcstats_raw_l2writes-l2_writes_sent',
'gauge_arcstats_raw_l2-l2_cksum_bad',
'gauge_arcstats_raw_l2bytes-l2_write_bytes',
'gauge_arcstats_raw_l2bytes-l2_read_bytes',
'arcstat_ratio_metadata-demand_metadata_hits',
'gauge_arcstats_raw_prefetch-prefetch_metadata_misses',
'gauge_arcstats_raw_evict-evict_skip',
'arcstat_ratio_data-prefetch_data_hits',
'gauge_arcstats_raw_size-hdr_size',
'gauge_arcstats_raw_l2_free-l2_free_on_write',
'gauge_arcstats_raw_counts-stolen',
'gauge_arcstats_raw_duplicate-duplicate_buffers_size',
'gauge_arcstats_raw_l2write-l2_write_buffer_bytes_scanned',
'arcstat_ratio_metadata-prefetch_metadata_hits',
'gauge_arcstats_raw_l2-l2_hits',
'gauge_arcstats_raw_cp-c_max',
'gauge_arcstats_raw_demand-demand_data_hits',
'gauge_arcstats_raw_evict-evict_l2_ineligible',
'gauge_arcstats_raw_l2evict-l2_evict_reading',
'gauge_arcstats_raw_mru-mfu_ghost_hits',
'gauge_arcstats_raw_demand-demand_data_misses',
'gauge_arcstats_raw_mru-mru_hits',
'gauge_arcstats_raw_hash-hash_elements',
'gauge_arcstats_raw_hits_misses-hits']}
Then you can access information about the data by using
stats.get_dataset_info
with args ["cputemp-0", "temperature"]
{'datasets': {'value': {'type': 'GAUGE'}},
'last_update': 1610476196,
'source': 'cputemp-0',
'step': 10,
'type': 'temperature'}
Then finally with this info, the data can be retrieved with stats.get_data
with args [["dataset": "value", "source": "cputemp-0", "type": "temperature"]]
{'about': 'Data for cputemp-1/temperature',
'data': [[2931.0],
[2923.524178],
[2911.0],
[2911.0],
[2907.271507],
[2912.200866],
[2927.260955],
[2921.0],
[2917.262088],
[2911.0],
[2914.737406],
[2917.260951],
[2914.720423],
[2917.270926],
[2918.440282],
[2923.524218],
[2907.260561],
[2901.0],
[2897.28509],
[2898.372248],
[2918.322402],
[2927.260997],
[2917.272831],
[2914.738049],
[2917.297672],
[2907.290207],
[2904.737268],
[2907.288642],
[2901.0],
[2901.0],
[2904.671161],
[2907.275651],
[2901.0],
[2904.709219],
[2911.0],
[2911.0],
[2918.475206],
[2927.260936],
[2921.0],
[2917.26093],
[2907.261005],
[2901.0],
[2901.0],
[2901.0],
[2901.0],
[2904.738359],
[2907.261996],
[2901.0],
[2908.453734],
[2909.82449],
[2894.73622],
[2901.0],
[2901.0],
[2901.0],
[2897.268524],
[2894.728039],
[2908.44658],
[2921.0],
[2913.524098],
[2904.718449],
[2911.0],
[2911.0],
[2911.0],
[2914.717158],
[2917.262069],
[2907.262091],
[2901.0],
[2901.0],
[2927.16852],
[2948.572384],
[2907.284117],
[2901.0],
[2901.0],
[2901.0],
[2908.444304],
[2917.26206],
[2907.269769],
[2904.717031],
[2914.736615],
[2913.522026],
[2908.458386],
[2921.0],
[2921.0],
[2917.262043],
[2914.698024],
[2909.786033],
[2905.753592],
[2919.78588],
[2904.715353],
[2911.0],
[2911.0],
[2907.262064],
[2908.468004],
[2913.525328],
[2901.0],
[2912.211909],
[2923.61348],
[2914.727721],
[2913.523832],
[2901.0],
[2904.736108],
[2914.687537],
[2909.786036],
[2898.443978],
[2914.738776],
[2909.782655],
[2894.724044],
[2901.0],
[2908.434718],
[2917.262015],
[2911.0],
[2907.318867],
[2897.291991],
[2894.653929],
[2897.279742],
[2898.343134],
[2911.0],
[2911.0],
[2907.262028],
[2901.0],
[2901.0],
[2901.0],
[2901.0],
[2901.0],
[2897.284719],
[2898.454494],
[2911.0],
[2914.701231],
[2913.523992],
[2897.279292],
[2905.923716],
[2919.786084],
[2901.0],
[2901.0],
[2904.720093],
[2907.261892],
[2901.0],
[2912.20923],
[2919.786447],
[2897.291957],
[2898.474888],
[2907.265573],
[2901.0],
[2901.0],
[2901.0],
[2912.13246],
[2919.786063],
[2904.732267],
[2907.308475],
[2904.652024],
[2907.266526],
[2904.701247],
[2921.998117],
[2929.786024],
[2907.281855],
[2901.0],
[2908.418132],
[2917.294635],
[2907.279622],
[2897.270359],
[2898.448464],
[2911.0],
[2911.0],
[2914.679995],
[2917.261861],
[2911.0],
[2911.0],
[2911.0],
[2911.0],
[2911.0],
[2911.0],
[2907.27401],
[2904.737479],
[2907.260956],
[2904.706617],
[2907.260231],
[2901.0],
[2901.0],
[2901.0],
[2908.437282],
[2909.785847],
[2894.706247],
[2901.0],
[2904.73647],
[2907.295706],
[2901.0],
[2908.447104],
[2917.261986],
[2911.0],
[2907.260859],
[2897.280385],
[2894.733664],
[2897.281243],
[2894.734576],
[2901.0],
[2904.724385],
[2907.283685],
[2908.445128],
[2917.306504],
[2914.738166],
[2913.523994],
[2897.275927],
[2902.154099],
[2917.261973],
[2903.5237],
[2894.717382],
[2904.725395],
[2907.291327],
[2901.0],
[2904.734832],
[2918.470038],
[2916.04766],
[2894.680142],
[2901.0],
[2901.0],
[2901.0],
[2901.0],
[2901.0],
[2901.0],
[2897.334484],
[2898.428112],
[2903.521822],
[2894.705631],
[2897.279551],
[2898.474794],
[2907.310693],
[2908.438446],
[2913.524276],
[2901.0],
[2904.735499],
[2907.26195],
[2904.735051],
[2903.523642],
[2894.738376],
[2908.406642],
[2913.521636],
[2904.712627],
[2907.265611],
[2901.0],
[2904.736999],
[2907.261938],
[2897.311653],
[2905.866108],
[2919.781167],
[2901.0],
[2904.739051],
[2914.71118],
[2913.523598],
[2901.0],
[2904.732294],
[2911.0],
[2903.52389],
[2894.726805],
[2897.271331],
[2891.0],
[2894.65194],
[2897.278851],
[2898.450652],
[2911.0],
[2914.713761],
[2909.785928],
[2898.469698],
[2907.286567],
[2901.0],
[2912.178528],
[2919.782652],
[2904.733294],
[2903.541928],
[2898.411436],
[2899.785757],
[2888.420282],
[2901.0],
[2908.461684],
[2917.261901],
[2907.307502],
[2901.0],
[2901.0],
[2901.0],
[2904.734424],
[2907.263621],
[2897.290783],
[2898.468168],
[2914.734602],
[2913.523848],
[2901.0],
[2912.143167],
[2931.0],
[2919.785973],
[2908.478034],
[2917.261918],
[2907.271888],
[2908.458196],
[2917.261924],
[2911.0],
[2907.261166],
[2901.0],
[2901.0],
[2901.0],
[2901.0],
[2901.0],
[2897.306239],
[2898.460384],
[2907.273772],
[2897.260947],
[2891.0],
[2902.217051],
[2924.738142],
[2919.785712],
[2897.307546],
[2894.664068],
[2897.282145],
[2898.438232],
[2922.192778],
[2937.261759],
[2923.523786],
[2914.716741],
[2917.261861],
[2907.273577],
[2904.677761],
[2903.52393],
[2894.739618],
[2901.0],
[2901.0],
[2908.370496],
[2917.260876],
[2907.271927],
[2901.0],
[2897.277263],
[2891.0],
[2894.703076],
[2897.266875],
[2894.708525],
[2908.473078],
[2909.780852],
[2891.0],
[2891.0],
[2898.466218],
[2903.533954],
[2898.45338],
[2907.261892],
[2897.269714],
[2898.396174],
[2899.782226],
[2884.736973],
[2894.718761],
[2897.274586],
[2898.417296],
[2911.0],
[2914.738224],
[2913.52357],
[2904.725326],
[2914.720679],
[2913.52358],
[2901.0],
[2897.293452],
[2891.0],
[2902.213325],
[2913.523434],
[2904.70814],
[None],
[None]],
'meta': {'end': 1610476930,
'legend': ['cputemp-1/temperature'],
'start': 1610473330,
'step': 10}}
A lot of the sources are dynamic based on the system, like number of cpu cores (somewhat easy to handle), but then there are pool storage, jails, etc....
Also, I'm not sure how to decipher the units as there is no documentation. The CPU temperatures seem to be in deciKelvin units (is this a common unit for this?) but it is hard to know.
Hello there....
I was about to refactor an age old Python2 project for the deprecated v1 FreeNAS API when I found this project via pypi.org (I was planning on making a package).
Would you be interested in contributions? And, perhaps some guidance, since your wrapper is built a lot better than my age old python2 implementation.
I'd like to add support for datasets, shares and permissions. I'm using my current v1 wrapper for the automated setup of dataset backed time machine-shares for MacOS users.
In class Disk
:
@property
@abstractmethod
def serial(self) -> str:
"""The serial of the disk."""
Should it not be:
@property
def serial(self) -> str:
"""The serial of the disk."""
return self._serial
We assume that there will be a self._serial
in the implementation, since we have:
def __hash__(self):
return self.serial.__hash__()
So, do we really have to implement the method in CachingDisk
? Am I missing something?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.