├── tests
├── responses
│ ├── TS-EC1280U-4.5.2
│ │ ├── volumes.json
│ │ ├── systemhealth.json
│ │ ├── bandwidth.json
│ │ ├── login.xml
│ │ ├── systemhealth.xml
│ │ ├── volumes.xml
│ │ ├── systemstats.json
│ │ ├── bandwidth.xml
│ │ ├── smartdiskhealth.json
│ │ ├── systemstats.xml
│ │ └── smartdiskhealth.xml
│ ├── TS-1677XU-RP-4.5.2
│ │ ├── volumes.json
│ │ ├── systemhealth.json
│ │ ├── bandwidth.json
│ │ ├── login.xml
│ │ ├── systemhealth.xml
│ │ ├── systemstats.json
│ │ ├── bandwidth.xml
│ │ ├── smartdiskhealth.json
│ │ ├── volumes.xml
│ │ ├── systemstats.xml
│ │ └── smartdiskhealth.xml
│ ├── TS-451-4.2.2
│ │ ├── systemhealth.json
│ │ ├── bandwidth.json
│ │ ├── smartdiskhealth.json
│ │ ├── volumes.json
│ │ ├── login.xml
│ │ ├── systemstats.json
│ │ ├── systemhealth.xml
│ │ ├── bandwidth.xml
│ │ ├── volumes.xml
│ │ ├── smartdiskhealth.xml
│ │ └── systemstats.xml
│ ├── TS-420-4.3.3
│ │ ├── firmwareupdate.json
│ │ ├── login.xml
│ │ └── firmwareupdate.xml
│ ├── TS-X53-4.5.4
│ │ ├── bandwidth.xml
│ │ ├── bandwidth.json
│ │ ├── login.xml
│ │ └── bandwidth2.xml
│ ├── TS-110-4.2.4
│ │ ├── smartdiskhealth.json
│ │ ├── login.xml
│ │ └── smartdiskhealth.xml
│ ├── TS-659-4.2.6
│ │ ├── login.xml
│ │ ├── smartdiskhealth.json
│ │ └── smartdiskhealth.xml
│ ├── TS-639-4.2.3
│ │ ├── login.xml
│ │ ├── systemstats.json
│ │ ├── volumes.json
│ │ ├── systemstats.xml
│ │ └── volumes.xml
│ ├── TS-251-4.5.1
│ │ ├── login_with_get.xml
│ │ ├── login.xml
│ │ ├── systemstats.json
│ │ └── systemstats.xml
│ └── TS-410-4.2.3
│ │ ├── systemstats.json
│ │ ├── login.xml
│ │ └── systemstats.xml
├── __init__.py
└── test-models.py
├── MANIFEST.in
├── requirements.txt
├── .hound.yml
├── .gitignore
├── qnapstats
├── __init__.py
└── qnap_stats.py
├── pyproject.toml
├── requirements.testing.txt
├── pylintrc
├── .github
├── ISSUE_TEMPLATE.md
├── stale.yml
└── workflows
│ ├── publish.yml
│ ├── release.yaml
│ └── test.yml
├── .releaserc.json
├── tox.ini
├── CHANGELOG.md
├── LICENSE
├── setup.py
├── debug.py
└── README.rst
/tests/responses/TS-EC1280U-4.5.2/volumes.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/tests/responses/TS-1677XU-RP-4.5.2/volumes.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/tests/responses/TS-451-4.2.2/systemhealth.json:
--------------------------------------------------------------------------------
1 | "good"
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include LICENSE
2 | include README.rst
3 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | requests
2 | xmltodict>=0.10.0
3 |
4 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
1 | """Functional tests for qnapstats"""
2 |
--------------------------------------------------------------------------------
/tests/responses/TS-1677XU-RP-4.5.2/systemhealth.json:
--------------------------------------------------------------------------------
1 | "good"
--------------------------------------------------------------------------------
/tests/responses/TS-EC1280U-4.5.2/systemhealth.json:
--------------------------------------------------------------------------------
1 | "good"
--------------------------------------------------------------------------------
/.hound.yml:
--------------------------------------------------------------------------------
1 | python:
2 | enabled: true
3 | config_file: tox.ini
4 |
--------------------------------------------------------------------------------
/tests/responses/TS-420-4.3.3/firmwareupdate.json:
--------------------------------------------------------------------------------
1 | "4.3.4.0695 Build 20180830"
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /dist
3 | qnapstats.egg-info
4 | .tox
5 | /.idea
6 | __pycache__
7 |
--------------------------------------------------------------------------------
/qnapstats/__init__.py:
--------------------------------------------------------------------------------
1 | """Main module for QNAPStats."""
2 | from .qnap_stats import QNAPStats # noqa: F401
3 |
--------------------------------------------------------------------------------
/tests/responses/TS-X53-4.5.4/bandwidth.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | requires = [
3 | "setuptools>=42",
4 | "wheel"
5 | ]
6 | build-backend = "setuptools.build_meta"
7 |
--------------------------------------------------------------------------------
/requirements.testing.txt:
--------------------------------------------------------------------------------
1 | requests
2 | xmltodict>=0.10.0
3 |
4 | responses>=0.5.1
5 | flake8
6 | isort
7 | pydocstyle
8 | pylint
9 |
10 |
--------------------------------------------------------------------------------
/pylintrc:
--------------------------------------------------------------------------------
1 | [MASTER]
2 | reports=no
3 | disable=I
4 | max-args=6
5 |
6 | [MESSAGES CONTROL]
7 | disable=line-too-long,len-as-condition,broad-except,invalid-name
--------------------------------------------------------------------------------
/tests/responses/TS-451-4.2.2/bandwidth.json:
--------------------------------------------------------------------------------
1 | {"eth0": {"is_default": false, "name": "LAN 1", "rx": 10732, "tx": 345770}, "eth1": {"is_default": true, "name": "LAN 2", "rx": 3594, "tx": 5052}}
--------------------------------------------------------------------------------
/tests/responses/TS-X53-4.5.4/bandwidth.json:
--------------------------------------------------------------------------------
1 | {"eth0": {"is_default": true, "name": "Adapter 1", "rx": 3157, "tx": 656}, "eth1": {"is_default": false, "name": "Adapter 2", "rx": 0, "tx": 0}}
2 |
--------------------------------------------------------------------------------
/tests/responses/TS-110-4.2.4/smartdiskhealth.json:
--------------------------------------------------------------------------------
1 | {"1": {"capacity": "2.73 TB", "drive_number": "1", "health": "OK", "model": "WDC WD30EFRX-68EUZN0", "serial": "WD-WCC4N0795818", "temp_c": 37, "temp_f": 98, "type": "hdd"}}
--------------------------------------------------------------------------------
/tests/responses/TS-1677XU-RP-4.5.2/bandwidth.json:
--------------------------------------------------------------------------------
1 | {"eth0": {"is_default": true, "name": "LAN 1", "rx": 6092, "tx": 96}, "eth1": {"is_default": false, "name": "LAN 2", "rx": 6092, "tx": 87}, "eth2": {"is_default": false, "name": "LAN 3", "rx": 7132, "tx": 6111}, "eth3": {"is_default": false, "name": "LAN 4", "rx": 63, "tx": 0}}
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | Please provide the following information along with your issue report (replace anything inside of `<>`):
2 |
3 | **Device Model Number**:
4 |
5 | **QTS Version**: <4.2.3>
6 |
7 | If you're reporting a `KeyError`, please also provide the XML response from your QNAP device. You can easily obtain this by running the `debug.py` script.
8 |
9 | **XML/Debug Output:**
10 |
11 | ~~~
12 |
13 | ~~~
14 |
--------------------------------------------------------------------------------
/tests/responses/TS-EC1280U-4.5.2/bandwidth.json:
--------------------------------------------------------------------------------
1 | {"bond0": {"is_default": false, "name": "LAN 5+6", "rx": 7035, "tx": 0}, "eth0": {"is_default": false, "name": "LAN 1", "rx": 0, "tx": 0}, "eth1": {"is_default": false, "name": "LAN 2", "rx": 0, "tx": 0}, "eth2": {"is_default": false, "name": "LAN 3", "rx": 0, "tx": 0}, "eth3": {"is_default": false, "name": "LAN 4", "rx": 5041, "tx": 13702}, "eth4": {"is_default": false, "name": "LAN 5", "rx": 3518, "tx": 0}, "eth5": {"is_default": false, "name": "LAN 6", "rx": 3518, "tx": 0}}
--------------------------------------------------------------------------------
/tests/responses/TS-451-4.2.2/smartdiskhealth.json:
--------------------------------------------------------------------------------
1 | {"0:1": {"capacity": "2.73 TB", "drive_number": "0:1", "health": "OK", "model": "WD30EFRX-68EUZN0", "serial": "WD-XXX", "temp_c": 32, "temp_f": 89, "type": "hdd"}, "0:2": {"capacity": "2.73 TB", "drive_number": "0:2", "health": "OK", "model": "WD30EFRX-68EUZN0", "serial": "WD-XXX", "temp_c": 32, "temp_f": 89, "type": "hdd"}, "0:3": {"capacity": "2.73 TB", "drive_number": "0:3", "health": "OK", "model": "WD30EFRX-68EUZN0", "serial": "WD-XXX", "temp_c": 31, "temp_f": 87, "type": "hdd"}, "0:4": {"capacity": "2.73 TB", "drive_number": "0:4", "health": "OK", "model": "WD30EFRX-68EUZN0", "serial": "WD-XXX", "temp_c": 28, "temp_f": 82, "type": "hdd"}}
--------------------------------------------------------------------------------
/tests/responses/TS-451-4.2.2/volumes.json:
--------------------------------------------------------------------------------
1 | {"DataVol1": {"folders": [{"sharename": "Multimedia", "used_size": 602112}, {"sharename": "Download", "used_size": 264058204160}, {"sharename": "Recordings", "used_size": 4096}, {"sharename": "Web", "used_size": 8192}, {"sharename": "Public", "used_size": 4096}, {"sharename": "homes", "used_size": 90112}, {"sharename": "TmpRestore", "used_size": 0}, {"sharename": "Shared", "used_size": 4770844930048}, {"sharename": "Containers", "used_size": 8080171008}, {"sharename": "IncomingBackups", "used_size": 126976}, {"sharename": "System Reserved", "used_size": 25579089920}], "free_size": 865955594240, "id": "1", "label": "DataVol1", "total_size": 5934928510976}}
--------------------------------------------------------------------------------
/tests/responses/TS-451-4.2.2/login.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/tests/responses/TS-659-4.2.6/login.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/tests/responses/TS-110-4.2.4/login.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/tests/responses/TS-659-4.2.6/smartdiskhealth.json:
--------------------------------------------------------------------------------
1 | {"1": {"capacity": "1.82 TB", "drive_number": "1", "health": "OK", "model": "WDC WD20EFRX-68EUZN0", "serial": "WD-WCC4M3AHY36Y", "temp_c": 38, "temp_f": 100, "type": "hdd"}, "2": {"capacity": "1.82 TB", "drive_number": "2", "health": "OK", "model": "WDC WD20EFRX-68EUZN0", "serial": "WD-WCC4M5TXJDV9", "temp_c": 38, "temp_f": 100, "type": "hdd"}, "3": {"capacity": "149.05 GB", "drive_number": "3", "health": "OK", "model": "INTEL SSDSA2M160G2GN", "serial": "CVPO13060046160AGN", "temp_c": null, "temp_f": null, "type": "hdd"}, "6": {"capacity": "2.73 TB", "drive_number": "6", "health": "OK", "model": "WDC WD30EFRX-68EUZN0", "serial": "WD-WMC4N2255621", "temp_c": 37, "temp_f": 98, "type": "hdd"}}
--------------------------------------------------------------------------------
/.releaserc.json:
--------------------------------------------------------------------------------
1 | {
2 | "tagFormat": "${version}",
3 | "branches": [
4 | "master"
5 | ],
6 | "repositoryUrl": "https://github.com/colinodell/python-qnapstats.git",
7 | "plugins": [
8 | "@semantic-release/commit-analyzer",
9 | "@semantic-release/exec",
10 | "@semantic-release/release-notes-generator",
11 | "@semantic-release/changelog",
12 | [
13 | "@semantic-release/git",
14 | {
15 | "assets": [
16 | "CHANGELOG.md"
17 | ],
18 | "message": "chore(release): ${nextRelease.version}\n\n${nextRelease.notes}"
19 | }
20 | ],
21 | "@semantic-release/github"
22 | ]
23 | }
--------------------------------------------------------------------------------
/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | envlist = py35, py36, py37, py38, py39, desc
3 | skip_missing_interpreters = True
4 |
5 | [gh-actions]
6 | python =
7 | 3.5: py35
8 | 3.6: py36
9 | 3.7: py37
10 | 3.8: py38
11 | 3.9: py39, desc
12 |
13 | [flake8]
14 | max-line-length = 120
15 | exclude = .git,__pycache__,.tox
16 |
17 | [testenv]
18 | setenv =
19 | LANG=C.UTF-8
20 | PYTHONPATH = {toxinidir}:{toxinidir}/qnapstats
21 | commands =
22 | flake8
23 | pylint qnapstats
24 | pydocstyle qnapstats
25 | python tests/test-models.py
26 | deps = -r{toxinidir}/requirements.testing.txt
27 |
28 | [testenv:desc]
29 | deps =
30 | docutils
31 | Pygments
32 | commands =
33 | python setup.py check --restructuredtext --strict
34 |
--------------------------------------------------------------------------------
/tests/responses/TS-639-4.2.3/login.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # Number of days of inactivity before an issue becomes stale
2 | daysUntilStale: 90
3 | # Number of days of inactivity before a stale issue is closed
4 | daysUntilClose: 21
5 | # Issues with these labels will never be considered stale
6 | exemptLabels:
7 | - pinned
8 | - on hold
9 | - security
10 | # Label to use when marking an issue as stale
11 | staleLabel: stale
12 | # Comment to post when marking an issue as stale. Set to `false` to disable
13 | markComment: >
14 | This issue has been automatically marked as stale because it has not had
15 | recent activity. It will be closed if no further activity occurs. Thank you
16 | for your contributions.
17 | # Comment to post when closing a stale issue. Set to `false` to disable
18 | closeComment: false
19 |
--------------------------------------------------------------------------------
/tests/responses/TS-251-4.5.1/login_with_get.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/tests/responses/TS-639-4.2.3/systemstats.json:
--------------------------------------------------------------------------------
1 | {"cpu": {"model": null, "temp_c": 43, "temp_f": 109, "usage_percent": 8.0}, "dns": ["192.168.1.1"], "firmware": {"build": "20170121", "build_time": "2017/01/21", "patch": "0", "version": "4.2.3"}, "memory": {"free": 1250.8, "total": 2021.6}, "nics": {"eth0": {"err_packets": 0, "ip": "192.168.1.5", "link_status": "Up", "mac": "00:08:9b:8c:fb:b0", "mask": "255.255.255.0", "max_speed": 1000, "rx_packets": 96552797, "tx_packets": 83455310, "usage": "DHCP"}, "eth1": {"err_packets": 0, "ip": "0.0.0.0", "link_status": "Down", "mac": "00:08:9b:8c:fb:b1", "mask": "0.0.0.0", "max_speed": 1000, "rx_packets": 0, "tx_packets": 0, "usage": "DHCP"}}, "system": {"model": "TS-639", "name": "QNAP-NAS", "serial_number": "--", "temp_c": 33, "temp_f": 91, "timezone": "(GMT-05:00) Eastern Time(US & Canada)"}, "uptime": {"days": 17, "hours": 12, "minutes": 16, "seconds": 45}}
--------------------------------------------------------------------------------
/tests/responses/TS-251-4.5.1/login.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/tests/responses/TS-251-4.5.1/systemstats.json:
--------------------------------------------------------------------------------
1 | {"cpu": {"model": null, "temp_c": null, "temp_f": null, "usage_percent": 48.5}, "dns": ["192.168.1.1"], "firmware": {"build": "20170121", "build_time": "21-01-2017", "patch": "0", "version": "4.2.3"}, "memory": {"free": 60.8, "total": 249.6}, "nics": {"eth0": {"err_packets": 0, "ip": "192.168.1.101", "link_status": "Up", "mac": "00:08:9B:C1:80:6A", "mask": "255.255.255.0", "max_speed": 1000, "rx_packets": 193439491, "tx_packets": 123234929, "usage": "DHCP"}, "eth1": {"err_packets": 0, "ip": "0.0.0.0", "link_status": "Down", "mac": "00:08:9B:C1:80:6B", "mask": "0.0.0.0", "max_speed": 1000, "rx_packets": 0, "tx_packets": 0, "usage": "DHCP"}}, "system": {"model": "TS-410", "name": "hornbill", "serial_number": "MYSERIAL", "temp_c": 40, "temp_f": 104, "timezone": "(GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna"}, "uptime": {"days": 13, "hours": 17, "minutes": 47, "seconds": 48}}
--------------------------------------------------------------------------------
/tests/responses/TS-410-4.2.3/systemstats.json:
--------------------------------------------------------------------------------
1 | {"cpu": {"model": null, "temp_c": null, "temp_f": null, "usage_percent": 48.5}, "dns": ["192.168.1.1"], "firmware": {"build": "20170121", "build_time": "21-01-2017", "patch": "0", "version": "4.2.3"}, "memory": {"free": 60.8, "total": 249.6}, "nics": {"eth0": {"err_packets": 0, "ip": "192.168.1.101", "link_status": "Up", "mac": "00:08:9B:C1:80:6A", "mask": "255.255.255.0", "max_speed": 1000, "rx_packets": 193439491, "tx_packets": 123234929, "usage": "DHCP"}, "eth1": {"err_packets": 0, "ip": "0.0.0.0", "link_status": "Down", "mac": "00:08:9B:C1:80:6B", "mask": "0.0.0.0", "max_speed": 1000, "rx_packets": 0, "tx_packets": 0, "usage": "DHCP"}}, "system": {"model": "TS-410", "name": "hornbill", "serial_number": "MYSERIAL", "temp_c": 40, "temp_f": 104, "timezone": "(GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna"}, "uptime": {"days": 13, "hours": 17, "minutes": 47, "seconds": 48}}
--------------------------------------------------------------------------------
/tests/responses/TS-410-4.2.3/login.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/tests/responses/TS-451-4.2.2/systemstats.json:
--------------------------------------------------------------------------------
1 | {"cpu": {"model": "Intel(R) Celeron(R) CPU J1800 @ 2.41GHz", "temp_c": 39, "temp_f": 102, "usage_percent": 2.3}, "dns": ["192.168.1.1", "8.8.8.8"], "firmware": {"build": "20161208", "build_time": "2016/12/08", "patch": "0", "version": "4.2.2"}, "memory": {"free": 5974.8, "total": 7880.3}, "nics": {"eth0": {"err_packets": 0, "ip": "192.168.1.10", "link_status": "Up", "mac": "00:08:9b:f1:d3:34", "mask": "255.255.255.0", "max_speed": 1000, "rx_packets": 144182777, "tx_packets": 261627662, "usage": "STATIC"}, "eth1": {"err_packets": 0, "ip": "192.168.1.11", "link_status": "Up", "mac": "00:08:9b:f1:d3:35", "mask": "255.255.255.0", "max_speed": 1000, "rx_packets": 974322250, "tx_packets": 1300296822, "usage": "STATIC"}}, "system": {"model": "TS-451", "name": "Apollo", "serial_number": "Q---", "temp_c": 38, "temp_f": 100, "timezone": "(GMT-05:00) Eastern Time(US & Canada)"}, "uptime": {"days": 63, "hours": 16, "minutes": 20, "seconds": 38}}
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Build and publish distributions to PyPI
2 |
3 | on:
4 | release:
5 | types: [published]
6 |
7 | jobs:
8 | build-and-publish:
9 | name: Build and publish distributions to PyPI
10 | runs-on: ubuntu-18.04
11 | steps:
12 | - uses: actions/checkout@master
13 |
14 | - name: Set up Python 3.7
15 | uses: actions/setup-python@v1
16 | with:
17 | python-version: 3.7
18 |
19 | - name: Install pypa/build
20 | run: >-
21 | python -m
22 | pip install
23 | build
24 | --user
25 |
26 | - name: Build a binary wheel and a source tarball
27 | run: >-
28 | python -m
29 | build
30 | --sdist
31 | --wheel
32 | --outdir dist/
33 |
34 | - name: Publish distribution to PyPI
35 | uses: pypa/gh-action-pypi-publish@master
36 | with:
37 | password: ${{ secrets.PYPI_API_TOKEN }}
38 |
--------------------------------------------------------------------------------
/tests/responses/TS-420-4.3.3/login.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/tests/responses/TS-639-4.2.3/volumes.json:
--------------------------------------------------------------------------------
1 | {"Volume 3": {"folders": [{"sharename": "TMBackup", "used_size": 1702238154752}, {"sharename": "System Reserved", "used_size": 33955840}], "free_size": 264101576704, "id": "3", "label": "Volume 3", "total_size": 1967428317184}, "Volume 4": {"folders": [{"sharename": "Qmultimedia", "used_size": 682364760064}, {"sharename": "Qrecordings", "used_size": 548954112}, {"sharename": "Qdownload", "used_size": 277272903680}, {"sharename": "Qweb", "used_size": 1220763648}, {"sharename": "Qusb", "used_size": 2046537859072}, {"sharename": "Public", "used_size": 330590863360}, {"sharename": "Network Recycle Bin 1", "used_size": 733184}, {"sharename": "QTemp", "used_size": 4096}, {"sharename": "Qhome", "used_size": 35692544}, {"sharename": "My_VM", "used_size": 82662420480}, {"sharename": "homes", "used_size": 37457920}, {"sharename": "WebDav", "used_size": 23797760}, {"sharename": "System Reserved", "used_size": 17268264960}], "free_size": 4432205987840, "id": "4", "label": "Volume 4", "total_size": 7872852889600}}
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # [0.6.0](https://github.com/colinodell/python-qnapstats/compare/0.5.0...0.6.0) (2025-06-25)
2 |
3 |
4 | ### Features
5 |
6 | * adding system fan monitoring ([#98](https://github.com/colinodell/python-qnapstats/issues/98)) ([e9254ee](https://github.com/colinodell/python-qnapstats/commit/e9254eec25f72802d17a34f09a47c1f80039f8fd))
7 |
8 | # [0.5.0](https://github.com/colinodell/python-qnapstats/compare/0.4.0...0.5.0) (2022-06-07)
9 |
10 |
11 | ### Bug Fixes
12 |
13 | * add missing token ([2b9eefa](https://github.com/colinodell/python-qnapstats/commit/2b9eefa1f901e96f02ed56b54239bdab4fd88d7c))
14 | * change main branch in releaserc.json ([c976249](https://github.com/colinodell/python-qnapstats/commit/c9762495395ff8586fac643e0ecc5f8405dad30d))
15 |
16 |
17 | ### Features
18 |
19 | * add external devices informations ([508f385](https://github.com/colinodell/python-qnapstats/commit/508f3850d9ced07d4a9a33829c2b7f5264929c64))
20 | * add external devices informations ([1b73056](https://github.com/colinodell/python-qnapstats/commit/1b73056035480f9d26755f6a3a7c0ab551cdbf0d))
21 |
--------------------------------------------------------------------------------
/tests/responses/TS-451-4.2.2/systemhealth.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.github/workflows/release.yaml:
--------------------------------------------------------------------------------
1 | # For release build - release that - Github action tag and deploy it on Google Cloud Storage
2 | name: Release
3 |
4 | on:
5 | push:
6 | branches:
7 | - master
8 |
9 | jobs:
10 | build:
11 | runs-on: ubuntu-latest
12 | timeout-minutes: 5
13 |
14 | steps:
15 | - name: Checkout sources
16 | uses: actions/checkout@v2
17 | with:
18 | # Token is used to push on main branch
19 | submodules: true # Fetch Hugo themes (true OR recursive)
20 | fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod
21 |
22 | - name: Semantic Release
23 | id: semantic
24 | uses: cycjimmy/semantic-release-action@v2.6.0
25 | with:
26 | semantic_version: 17.3.7
27 | extra_plugins: |
28 | @semantic-release/commit-analyzer@8.0.1
29 | @semantic-release/release-notes-generator@9.0.1
30 | @semantic-release/changelog@5.0.1
31 | @semantic-release/github@7.2.0
32 | @semantic-release/exec@5.0.0
33 | @semantic-release/git@9.0.0
34 | env:
35 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Test
2 |
3 | on:
4 | - push
5 | - pull_request
6 |
7 | jobs:
8 | build:
9 | # As long as we need Python 3.6 here in the test, we can only use up to Ubuntu 20.
10 | # https://github.com/actions/setup-python/issues/544
11 | runs-on: ubuntu-20.04
12 | strategy:
13 | matrix:
14 | include:
15 | - python-version: '3.6'
16 | toxenv: py36
17 | - python-version: '3.7'
18 | toxenv: py37
19 | - python-version: '3.8'
20 | toxenv: py38
21 | - python-version: '3.9'
22 | toxenv: py39
23 | - python-version: '3.9'
24 | toxenv: desc
25 |
26 | steps:
27 | - uses: actions/checkout@v1
28 |
29 | - name: Set up Python ${{ matrix.python-version }}
30 | uses: actions/setup-python@v2
31 | with:
32 | python-version: ${{ matrix.python-version }}
33 |
34 | - name: Install dependencies
35 | run: |
36 | python -m pip install --upgrade pip
37 | pip install tox tox-gh-actions
38 |
39 | - name: Test with tox
40 | env:
41 | TOXENV: ${{ matrix.toxenv }}
42 | run: tox -v
43 |
--------------------------------------------------------------------------------
/tests/responses/TS-1677XU-RP-4.5.2/login.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/tests/responses/TS-EC1280U-4.5.2/login.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/tests/responses/TS-X53-4.5.4/login.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Colin O'Dell
4 |
5 | Some code based on `python-synology`, copyright (c) 2017 StaticCube
6 |
7 | Permission is hereby granted, free of charge, to any person obtaining a copy
8 | of this software and associated documentation files (the "Software"), to deal
9 | in the Software without restriction, including without limitation the rights
10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | copies of the Software, and to permit persons to whom the Software is
12 | furnished to do so, subject to the following conditions:
13 |
14 | The above copyright notice and this permission notice shall be included in all
15 | copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 | SOFTWARE.
24 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding:utf-8 -*-
3 |
4 | import io
5 |
6 | from setuptools import setup
7 |
8 | setup(
9 | name='qnapstats',
10 | description='Python API for obtaining QNAP NAS system stats',
11 | long_description=io.open('README.rst', encoding='utf-8').read(),
12 | version='0.4.0',
13 | license='MIT',
14 | author='Colin O\'Dell',
15 | author_email='colinodell@gmail.com',
16 | url='https://github.com/colinodell/python-qnapstats',
17 | packages=['qnapstats'],
18 | keywords=['qnap'],
19 | classifiers=[
20 | 'Intended Audience :: Developers',
21 | 'Intended Audience :: System Administrators',
22 | 'License :: OSI Approved :: MIT License',
23 | 'Operating System :: OS Independent',
24 | 'Programming Language :: Python :: 3',
25 | 'Programming Language :: Python :: 3.5',
26 | 'Programming Language :: Python :: 3.6',
27 | 'Programming Language :: Python :: 3.7',
28 | 'Programming Language :: Python :: 3.8',
29 | 'Programming Language :: Python :: 3.9',
30 | 'Topic :: Home Automation',
31 | 'Topic :: System :: Monitoring'
32 | ],
33 | install_requires=['requests>=1.0.0', 'xmltodict>=0.10.0']
34 | )
35 |
--------------------------------------------------------------------------------
/tests/responses/TS-451-4.2.2/bandwidth.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/debug.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import getpass
3 | import traceback
4 | import qnapstats
5 |
6 | host = input("Host (prefix with 'https://' if needed): ")
7 | port = int(input("Port: "))
8 | username = input("Username: ")
9 | password = getpass.getpass("Password: ")
10 |
11 | qnap = qnapstats.QNAPStats(host, port, username, password, debugmode=True, verify_ssl=False)
12 |
13 | try:
14 | qnap.get_system_stats()
15 | except Exception as e:
16 | print(e.args)
17 | traceback.print_exc()
18 |
19 | try:
20 | qnap.get_system_health()
21 | except Exception as e:
22 | print(e.args)
23 | traceback.print_exc()
24 |
25 | try:
26 | qnap.get_smart_disk_health()
27 | except Exception as e:
28 | print(e.args)
29 | traceback.print_exc()
30 |
31 | try:
32 | qnap.get_volumes()
33 | except Exception as e:
34 | print(e.args)
35 | traceback.print_exc()
36 |
37 | try:
38 | qnap.get_bandwidth()
39 | except Exception as e:
40 | print(e.args)
41 | traceback.print_exc()
42 |
43 | try:
44 | qnap.get_external_drive()
45 | except Exception as e:
46 | print(e.args)
47 | traceback.print_exc()
48 |
49 |
50 | try:
51 | qnap.get_storage_information_on_external_device()
52 | except Exception as e:
53 | print(e.args)
54 | traceback.print_exc()
55 |
--------------------------------------------------------------------------------
/tests/responses/TS-EC1280U-4.5.2/systemhealth.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/tests/responses/TS-1677XU-RP-4.5.2/systemhealth.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/tests/responses/TS-1677XU-RP-4.5.2/systemstats.json:
--------------------------------------------------------------------------------
1 | {"cpu": {"model": "AMD Ryzen 7 2700 Eight-Core Processor", "temp_c": 59, "temp_f": 138, "usage_percent": 4.5}, "dns": ["192.168.15.14", "192.168.15.206", "192.168.15.14", "192.168.15.206", "192.168.15.14", "192.168.15.206", "192.168.15.14", "192.168.15.206"], "firmware": {"build": "20210302", "build_time": "2021/03/02", "patch": "0", "version": "4.5.2"}, "memory": {"free": 12990.6, "total": 15966.0}, "nics": {"eth0": {"err_packets": 0, "ip": "169.254.5.239", "link_status": "Up", "mac": "24:5e:be:38:87:10", "mask": "255.255.0.0", "max_speed": 1000, "rx_packets": 28503230, "tx_packets": 431819, "usage": "DHCP"}, "eth1": {"err_packets": 0, "ip": "169.254.5.244", "link_status": "Up", "mac": "24:5e:be:38:87:11", "mask": "255.255.0.0", "max_speed": 1000, "rx_packets": 28479859, "tx_packets": 455355, "usage": "DHCP"}, "eth2": {"err_packets": 1, "ip": "192.168.11.7", "link_status": "Up", "mac": "24:5e:be:38:87:13", "mask": "255.255.255.0", "max_speed": 10000, "rx_packets": 29011169, "tx_packets": 681064, "usage": "STATIC"}, "eth3": {"err_packets": 213, "ip": "192.168.168.51", "link_status": "Up", "mac": "24:5e:be:38:87:12", "mask": "255.255.255.0", "max_speed": 10000, "rx_packets": 175315116, "tx_packets": 2950389, "usage": "STATIC"}}, "system": {"model": "TS-1677XU-RP", "name": "QNAP06", "serial_number": "Q191I16905", "temp_c": 33, "temp_f": 91, "timezone": "(GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna"}, "uptime": {"days": 7, "hours": 7, "minutes": 39, "seconds": 38}}
--------------------------------------------------------------------------------
/tests/responses/TS-EC1280U-4.5.2/volumes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/tests/responses/TS-110-4.2.4/smartdiskhealth.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 0
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/tests/responses/TS-1677XU-RP-4.5.2/bandwidth.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/tests/responses/TS-EC1280U-4.5.2/systemstats.json:
--------------------------------------------------------------------------------
1 | {"cpu": {"model": "Intel(R) Xeon(R) CPU E3-1246 v3 @ 3.50GHz", "temp_c": 44, "temp_f": 111, "usage_percent": 1.2}, "dns": ["192.168.15.14", "192.168.15.206", "192.168.15.14", "192.168.15.206", "192.168.15.14", "192.168.15.206"], "firmware": {"build": "20210302", "build_time": "2021/03/02", "patch": "0", "version": "4.5.2"}, "memory": {"free": 1517.0, "total": 3892.0}, "nics": {"eth0": {"err_packets": 0, "ip": "0.0.0.0", "link_status": "Down", "mac": "24:5e:be:04:28:2d", "mask": "0.0.0.0", "max_speed": 1000, "rx_packets": 0, "tx_packets": 0, "usage": "DHCP"}, "eth1": {"err_packets": 0, "ip": "0.0.0.0", "link_status": "Down", "mac": "24:5e:be:04:28:2e", "mask": "0.0.0.0", "max_speed": 1000, "rx_packets": 0, "tx_packets": 0, "usage": "DHCP"}, "eth2": {"err_packets": 0, "ip": "0.0.0.0", "link_status": "Down", "mac": "24:5e:be:04:28:2f", "mask": "0.0.0.0", "max_speed": 1000, "rx_packets": 0, "tx_packets": 0, "usage": "DHCP"}, "eth3": {"err_packets": 0, "ip": "192.168.11.2", "link_status": "Up", "mac": "24:5e:be:04:28:30", "mask": "255.255.255.0", "max_speed": 1000, "rx_packets": 7526374, "tx_packets": 123045, "usage": "STATIC"}, "eth4": {"err_packets": 0, "ip": "192.168.168.22", "link_status": "Up", "mac": "24:5e:be:03:8f:67", "mask": "255.255.255.0", "max_speed": 10000, "rx_packets": 7546025, "tx_packets": 460, "usage": "STATIC"}, "eth5": {"err_packets": 0, "ip": "192.168.168.23", "link_status": "Up", "mac": "24:5e:be:03:8f:66", "mask": "255.255.255.0", "max_speed": 10000, "rx_packets": 70955067, "tx_packets": 38577237, "usage": "STATIC"}}, "system": {"model": "TS-EC1280U", "name": "NAS04282D", "serial_number": "Q166I14410", "temp_c": 40, "temp_f": 104, "timezone": "(GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna"}, "uptime": {"days": 2, "hours": 1, "minutes": 26, "seconds": 49}}
--------------------------------------------------------------------------------
/tests/responses/TS-EC1280U-4.5.2/bandwidth.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/tests/responses/TS-EC1280U-4.5.2/smartdiskhealth.json:
--------------------------------------------------------------------------------
1 | {"0:1": {"capacity": "7.28 TB", "drive_number": "0:1", "health": "OK", "model": "ST8000NE0001-1WN112", "serial": "ZA12WWP5", "temp_c": 37, "temp_f": 98, "type": "hdd"}, "0:10": {"capacity": "7.28 TB", "drive_number": "0:10", "health": "OK", "model": "ST8000NE0001-1WN112", "serial": "ZA13QM5R", "temp_c": 38, "temp_f": 100, "type": "hdd"}, "0:11": {"capacity": "7.28 TB", "drive_number": "0:11", "health": "OK", "model": "ST8000NE0001-1WN112", "serial": "ZA13R992", "temp_c": 38, "temp_f": 100, "type": "hdd"}, "0:12": {"capacity": "7.28 TB", "drive_number": "0:12", "health": "OK", "model": "ST8000NE0001-1WN112", "serial": "ZA13GF9A", "temp_c": 36, "temp_f": 96, "type": "hdd"}, "0:2": {"capacity": "7.28 TB", "drive_number": "0:2", "health": "OK", "model": "ST8000NE0001-1WN112", "serial": "ZA12WWQE", "temp_c": 36, "temp_f": 96, "type": "hdd"}, "0:3": {"capacity": "7.28 TB", "drive_number": "0:3", "health": "OK", "model": "ST8000NE0001-1WN112", "serial": "ZA12WD5Y", "temp_c": 36, "temp_f": 96, "type": "hdd"}, "0:4": {"capacity": "7.28 TB", "drive_number": "0:4", "health": "OK", "model": "ST8000NE0001-1WN112", "serial": "ZA10VMTS", "temp_c": 35, "temp_f": 95, "type": "hdd"}, "0:5": {"capacity": "7.28 TB", "drive_number": "0:5", "health": "OK", "model": "ST8000NE0001-1WN112", "serial": "ZA12WWQM", "temp_c": 38, "temp_f": 100, "type": "hdd"}, "0:6": {"capacity": "7.28 TB", "drive_number": "0:6", "health": "OK", "model": "ST8000NE0001-1WN112", "serial": "ZA10QTPP", "temp_c": 38, "temp_f": 100, "type": "hdd"}, "0:7": {"capacity": "7.28 TB", "drive_number": "0:7", "health": "OK", "model": "ST8000NE0001-1WN112", "serial": "ZA12Z2VN", "temp_c": 38, "temp_f": 100, "type": "hdd"}, "0:8": {"capacity": "7.28 TB", "drive_number": "0:8", "health": "OK", "model": "ST8000NE0001-1WN112", "serial": "ZA12Z2VR", "temp_c": 36, "temp_f": 96, "type": "hdd"}, "0:9": {"capacity": "7.28 TB", "drive_number": "0:9", "health": "OK", "model": "ST8000NE0011-1YG112", "serial": "ZA1483VC", "temp_c": 37, "temp_f": 98, "type": "hdd"}, "23:1": {"capacity": "465.76 GB", "drive_number": "23:1", "health": "OK", "model": "SSD 860 EVO mSATA 500GB", "serial": "S41NNB0K404351Z", "temp_c": 41, "temp_f": 105, "type": "ssd"}, "23:2": {"capacity": "465.76 GB", "drive_number": "23:2", "health": "OK", "model": "SSD 860 EVO mSATA 500GB", "serial": "S41NNB0K404390L", "temp_c": 41, "temp_f": 105, "type": "ssd"}}
--------------------------------------------------------------------------------
/tests/responses/TS-420-4.3.3/firmwareupdate.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/tests/responses/TS-1677XU-RP-4.5.2/smartdiskhealth.json:
--------------------------------------------------------------------------------
1 | {"0:1": {"capacity": "894.25 GB", "drive_number": "0:1", "health": "OK", "model": "5200_MTFDDAK960TDD", "serial": "185220CAF3A9", "temp_c": 32, "temp_f": 89, "type": "ssd"}, "0:10": {"capacity": "10.91 TB", "drive_number": "0:10", "health": "OK", "model": "HUH721212ALE604", "serial": "AAHKMB1H", "temp_c": 43, "temp_f": 109, "type": "hdd"}, "0:11": {"capacity": "10.91 TB", "drive_number": "0:11", "health": "OK", "model": "HUH721212ALE604", "serial": "AAHK9B5H", "temp_c": 45, "temp_f": 113, "type": "hdd"}, "0:12": {"capacity": "10.91 TB", "drive_number": "0:12", "health": "OK", "model": "HUH721212ALE604", "serial": "AAHL21UH", "temp_c": 45, "temp_f": 113, "type": "hdd"}, "0:13": {"capacity": "10.91 TB", "drive_number": "0:13", "health": "OK", "model": "HUH721212ALE604", "serial": "AAHKMV3H", "temp_c": 39, "temp_f": 102, "type": "hdd"}, "0:14": {"capacity": "10.91 TB", "drive_number": "0:14", "health": "OK", "model": "HUH721212ALE604", "serial": "AAHL2E7H", "temp_c": 40, "temp_f": 104, "type": "hdd"}, "0:15": {"capacity": "10.91 TB", "drive_number": "0:15", "health": "OK", "model": "HUH721212ALE604", "serial": "AAHKPUGH", "temp_c": 42, "temp_f": 107, "type": "hdd"}, "0:16": {"capacity": "10.91 TB", "drive_number": "0:16", "health": "OK", "model": "HUH721212ALE604", "serial": "AAHKS1TH", "temp_c": 41, "temp_f": 105, "type": "hdd"}, "0:2": {"capacity": "894.25 GB", "drive_number": "0:2", "health": "OK", "model": "5200_MTFDDAK960TDD", "serial": "185220CAF50F", "temp_c": 35, "temp_f": 95, "type": "ssd"}, "0:3": {"capacity": "10.91 TB", "drive_number": "0:3", "health": "OK", "model": "HUH721212ALE604", "serial": "AAHKGNRH", "temp_c": 43, "temp_f": 109, "type": "hdd"}, "0:4": {"capacity": "10.91 TB", "drive_number": "0:4", "health": "OK", "model": "HUH721212ALE604", "serial": "AAHKM6RH", "temp_c": 43, "temp_f": 109, "type": "hdd"}, "0:5": {"capacity": "10.91 TB", "drive_number": "0:5", "health": "OK", "model": "HUH721212ALE604", "serial": "AAHKRXVH", "temp_c": 38, "temp_f": 100, "type": "hdd"}, "0:6": {"capacity": "10.91 TB", "drive_number": "0:6", "health": "OK", "model": "HUH721212ALE604", "serial": "AAHKY0MH", "temp_c": 40, "temp_f": 104, "type": "hdd"}, "0:7": {"capacity": "10.91 TB", "drive_number": "0:7", "health": "OK", "model": "HUH721212ALE604", "serial": "AAHL2BZH", "temp_c": 45, "temp_f": 113, "type": "hdd"}, "0:8": {"capacity": "10.91 TB", "drive_number": "0:8", "health": "OK", "model": "HUH721212ALE604", "serial": "AAHKRY2H", "temp_c": 45, "temp_f": 113, "type": "hdd"}, "0:9": {"capacity": "10.91 TB", "drive_number": "0:9", "health": "OK", "model": "HUH721212ALE604", "serial": "AAHKS01H", "temp_c": 41, "temp_f": 105, "type": "hdd"}}
--------------------------------------------------------------------------------
/tests/responses/TS-251-4.5.1/systemstats.xml:
--------------------------------------------------------------------------------
1 |
2 | 0
3 |
4 |
5 |
6 | 193439491
7 | 123234929
8 | 0
9 | 1
10 | 1000
11 | 192.168.1.101
12 | 255.255.255.0
13 | 00:08:9B:C1:80:6A
14 | DHCP
15 | 0
16 | 0
17 | 0
18 | 0
19 | 1000
20 | 0.0.0.0
21 | 0.0.0.0
22 | 00:08:9B:C1:80:6B
23 | DHCP
24 |
26 | 40
27 | 104
28 | 0
29 | MYSERIAL
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/tests/responses/TS-410-4.2.3/systemstats.xml:
--------------------------------------------------------------------------------
1 |
2 | 0
3 |
4 |
5 |
6 | 193439491
7 | 123234929
8 | 0
9 | 1
10 | 1000
11 | 192.168.1.101
12 | 255.255.255.0
13 | 00:08:9B:C1:80:6A
14 | DHCP
15 | 0
16 | 0
17 | 0
18 | 0
19 | 1000
20 | 0.0.0.0
21 | 0.0.0.0
22 | 00:08:9B:C1:80:6B
23 | DHCP
24 |
26 | 40
27 | 104
28 | 0
29 | MYSERIAL
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/tests/responses/TS-1677XU-RP-4.5.2/volumes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/tests/responses/TS-639-4.2.3/systemstats.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 0
4 |
5 |
6 |
7 | 96552797
8 | 83455310
9 | 0
10 | 1
11 | 1000
12 | 192.168.1.5
13 | 255.255.255.0
14 | 00:08:9b:8c:fb:b0
15 | DHCP
16 | 0
17 | 0
18 | 0
19 | 0
20 | 1000
21 | 0.0.0.0
22 | 0.0.0.0
23 | 00:08:9b:8c:fb:b1
24 | DHCP
25 |
26 | 43109
27 | 33
28 | 91
29 | 779
30 | 0
31 | 728
32 | 0
33 | 2
34 | --
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/tests/responses/TS-451-4.2.2/volumes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/tests/responses/TS-451-4.2.2/smartdiskhealth.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/tests/responses/TS-451-4.2.2/systemstats.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 1
4 |
5 |
6 |
7 |
8 | 0
9 |
10 |
11 | 144182777
12 | 261627662
13 | 0
14 | 1
15 | 1000
16 | 0
17 | 192.168.1.10
18 | 255.255.255.0
19 | 00:08:9b:f1:d3:34
20 | STATIC
21 | 974322250
22 | 1300296822
23 | 0
24 | 1
25 | 1000
26 | 0
27 | 192.168.1.11
28 | 255.255.255.0
29 | 00:08:9b:f1:d3:35
30 | STATIC
31 |
34 | 39102
35 | 38
36 | 100
37 | 1
38 | 527
39 | 0
40 | 0
41 | NAS
42 | Q---
43 | QW37AR32
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/tests/responses/TS-X53-4.5.4/bandwidth2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | 1
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
--------------------------------------------------------------------------------
/tests/responses/TS-639-4.2.3/volumes.xml:
--------------------------------------------------------------------------------
1 |
2 | 0
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/tests/responses/TS-659-4.2.6/smartdiskhealth.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 0
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/tests/test-models.py:
--------------------------------------------------------------------------------
1 | """Functional tests where the QNAP responses are mocked"""
2 | # -*- coding:utf-8 -*-
3 | import base64
4 | import json
5 | import os
6 | import qnapstats
7 | import responses
8 |
9 |
10 | def get_immediate_subdirectories(a_dir):
11 | return [name for name in os.listdir(a_dir)
12 | if os.path.isdir(os.path.join(a_dir, name))]
13 |
14 |
15 | response_directory = os.path.join(os.path.dirname(__file__), 'responses')
16 | models = get_immediate_subdirectories(response_directory)
17 |
18 |
19 | def add_mock_responses(rsps, directory):
20 | rsps.add(responses.POST,
21 | 'http://localhost:8080/cgi-bin/authLogin.cgi',
22 | body=file_get_contents(directory, 'login.xml'),
23 | status=200,
24 | content_type='text/xml')
25 | if file_get_contents(directory, "login_with_get.xml"):
26 | pwd = base64.b64encode("correcthorsebatterystaple".encode('utf-8')).decode('ascii')
27 | rsps.add(responses.GET,
28 | 'http://localhost:8080/cgi-bin/authLogin.cgi?user=admin&pwd=' + pwd,
29 | body=file_get_contents(directory, 'login_with_get.xml'),
30 | status=200,
31 | content_type='text/xml')
32 | xml = file_get_contents(directory, 'bandwidth.xml')
33 | if xml is not None:
34 | rsps.add(responses.GET,
35 | 'http://localhost:8080/cgi-bin/management/chartReq.cgi?chart_func=QSM40bandwidth&sid=12345',
36 | match_querystring=True,
37 | body=xml,
38 | status=200,
39 | content_type='text/xml')
40 |
41 | xml = file_get_contents(directory, 'bandwidth2.xml')
42 | if xml is not None:
43 | rsps.add(responses.GET,
44 | 'http://localhost:8080/cgi-bin/management/chartReq.cgi?chart_func=bandwidth&sid=12345',
45 | match_querystring=True,
46 | body=xml,
47 | status=200,
48 | content_type='text/xml')
49 |
50 | xml = file_get_contents(directory, 'systemhealth.xml')
51 | if xml is not None:
52 | rsps.add(responses.GET,
53 | 'http://localhost:8080/cgi-bin/management/manaRequest.cgi?subfunc=sysinfo&sysHealth=1&sid=12345',
54 | match_querystring=True,
55 | body=file_get_contents(directory, 'systemhealth.xml'),
56 | status=200,
57 | content_type='text/xml')
58 |
59 | xml = file_get_contents(directory, 'volumes.xml')
60 | if xml is not None:
61 | rsps.add(responses.GET,
62 | 'http://localhost:8080/cgi-bin/management/chartReq.cgi?chart_func=disk_usage&disk_select=all&include=all&sid=12345', # noqa: E501
63 | match_querystring=True,
64 | body=file_get_contents(directory, 'volumes.xml'),
65 | status=200,
66 | content_type='text/xml')
67 |
68 | xml = file_get_contents(directory, 'smartdiskhealth.xml')
69 | if xml is not None:
70 | rsps.add(responses.GET,
71 | 'http://localhost:8080/cgi-bin/disk/qsmart.cgi?func=all_hd_data&sid=12345',
72 | match_querystring=True,
73 | body=file_get_contents(directory, 'smartdiskhealth.xml'),
74 | status=200,
75 | content_type='text/xml')
76 |
77 | xml = file_get_contents(directory, 'systemstats.xml')
78 | if xml is not None:
79 | rsps.add(responses.GET,
80 | 'http://localhost:8080/cgi-bin/management/manaRequest.cgi?subfunc=sysinfo&hd=no&multicpu=1&sid=12345',
81 | match_querystring=True,
82 | body=file_get_contents(directory, 'systemstats.xml'),
83 | status=200,
84 | content_type='text/xml')
85 |
86 | xml = file_get_contents(directory, 'firmwareupdate.xml')
87 | if xml is not None:
88 | rsps.add(responses.GET,
89 | 'http://localhost:8080/cgi-bin/sys/sysRequest.cgi?subfunc=firm_update&sid=12345',
90 | match_querystring=True,
91 | body=file_get_contents(directory, 'firmwareupdate.xml'),
92 | status=200,
93 | content_type='text/xml')
94 |
95 |
96 | def file_get_contents(directory, file):
97 | file = os.path.join(response_directory, directory, file)
98 | if not os.path.exists(file):
99 | return None
100 |
101 | with open(file, 'r') as myfile:
102 | return myfile.read()
103 |
104 |
105 | for model_directory in models:
106 | qnap = qnapstats.QNAPStats("localhost", 8080, "admin", "correcthorsebatterystaple")
107 | with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
108 | add_mock_responses(rsps, model_directory)
109 |
110 | bandwidth = file_get_contents(model_directory, 'bandwidth.json')
111 | if bandwidth is not None:
112 | assert json.dumps(qnap.get_bandwidth(), sort_keys=True) == bandwidth.rstrip()
113 |
114 | smartdiskhealth = file_get_contents(model_directory, 'smartdiskhealth.json')
115 | if smartdiskhealth is not None:
116 | assert json.dumps(qnap.get_smart_disk_health(), sort_keys=True) == smartdiskhealth
117 |
118 | systemhealth = file_get_contents(model_directory, 'systemhealth.json')
119 | if systemhealth is not None:
120 | assert json.dumps(qnap.get_system_health(), sort_keys=True) == systemhealth
121 |
122 | systemstats = file_get_contents(model_directory, 'systemstats.json')
123 | if systemstats is not None:
124 | assert json.dumps(qnap.get_system_stats(), sort_keys=True) == systemstats
125 |
126 | volumes = file_get_contents(model_directory, 'volumes.json')
127 | if volumes is not None:
128 | assert json.dumps(qnap.get_volumes(), sort_keys=True) == volumes
129 |
130 | firmwareupdate = file_get_contents(model_directory, 'firmwareupdate.json')
131 | if firmwareupdate is not None:
132 | assert json.dumps(qnap.get_firmware_update(), sort_keys=True) == firmwareupdate
133 |
--------------------------------------------------------------------------------
/tests/responses/TS-1677XU-RP-4.5.2/systemstats.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 1
4 |
5 |
6 |
7 |
8 | 0
9 |
10 |
11 |
12 |
13 | 3
14 | eth0
15 | Adapter 1
16 | 28503230
17 | 431819
18 | 0
19 | 1
20 | 1000
21 | 0
22 | 169.254.5.239
23 | 255.255.0.0
24 | 24:5e:be:38:87:10
25 | DHCP
26 | 0192.168.15.14
27 | 2
28 | eth1
29 | Adapter 2
30 | 28479859
31 | 455355
32 | 0
33 | 1
34 | 1000
35 | 0
36 | 169.254.5.244
37 | 255.255.0.0
38 | 24:5e:be:38:87:11
39 | DHCP
40 | 0192.168.15.14
41 | 192.168.15.206
42 | 5
43 | eth2
44 | Adapter 3
45 | 29011169
46 | 681064
47 | 1
48 | 1
49 | 10000
50 | 0
51 | 192.168.11.7
52 | 255.255.255.0
53 | 24:5e:be:38:87:13
54 | STATIC
55 | 0192.168.15.14
56 | 192.168.15.206
57 | 4
58 | eth3
59 | Adapter 4
60 | 175315116
61 | 2950389
62 | 213
63 | 1
64 | 10000
65 | 0
66 | 192.168.168.51
67 | 255.255.255.0
68 | 24:5e:be:38:87:12
69 | STATIC
70 | 0192.168.15.14
71 | 192.168.15.206
72 |
73 |
76 | 59138
77 | 33
78 | 91
79 | 4
80 | SYS FAN
81 | 1489
82 | 0
83 | 0
84 | 1476
85 | 0
86 | 0
87 | 1487
88 | 0
89 | 0
90 | 1497
91 | 0
92 | 0
93 | -2
94 | -2
95 | 0
96 | 0
97 | -2
98 | -2
99 | NAS
100 | Q191I16905
101 | QZ49AR55
102 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/tests/responses/TS-EC1280U-4.5.2/systemstats.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 1
4 |
5 |
6 |
7 |
8 | 0
9 |
10 |
11 |
12 |
13 | 2
14 | eth0
15 | Adapter 1
16 | 0
17 | 0
18 | 0
19 | 0
20 | 1000
21 | 0
22 | 0.0.0.0
23 | 0.0.0.0
24 | 24:5e:be:04:28:2d
25 | DHCP
26 | 0192.168.15.14
27 | 192.168.15.206
28 | 5
29 | eth1
30 | Adapter 2
31 | 0
32 | 0
33 | 0
34 | 0
35 | 1000
36 | 0
37 | 0.0.0.0
38 | 0.0.0.0
39 | 24:5e:be:04:28:2e
40 | DHCP
41 | 0192.168.15.14
42 | 192.168.15.206
43 | 4
44 | eth2
45 | Adapter 3
46 | 0
47 | 0
48 | 0
49 | 0
50 | 1000
51 | 0
52 | 0.0.0.0
53 | 0.0.0.0
54 | 24:5e:be:04:28:2f
55 | DHCP
56 | 0192.168.15.14
57 | 192.168.15.206
58 | 3
59 | eth3
60 | Adapter 4
61 | 7526374
62 | 123045
63 | 0
64 | 1
65 | 1000
66 | 0
67 | 192.168.11.2
68 | 255.255.255.0
69 | 24:5e:be:04:28:30
70 | STATIC
71 | 0192.168.15.14
72 | 192.168.15.206
73 | 7
74 | eth4
75 | Adapter 5
76 | 7546025
77 | 460
78 | 0
79 | 1
80 | 10000
81 | 0
82 | 192.168.168.22
83 | 255.255.255.0
84 | 24:5e:be:03:8f:67
85 | STATIC
86 | 0192.168.15.14
87 | 192.168.15.206
88 | 6
89 | eth5
90 | Adapter 6
91 | 70955067
92 | 38577237
93 | 0
94 | 1
95 | 10000
96 | 0
97 | 192.168.168.23
98 | 255.255.255.0
99 | 24:5e:be:03:8f:66
100 | STATIC
101 | 0192.168.15.14
102 | 192.168.15.206
103 |
104 |
105 | 44111
106 | 40
107 | 104
108 | 4
109 | SYS FAN
110 | 6108
111 | 0
112 | 0
113 | 6108
114 | 0
115 | 0
116 | 6192
117 | 0
118 | 0
119 | 6081
120 | 0
121 | 0
122 | -2
123 | -2
124 | 0
125 | 0
126 | -2
127 | -2
128 | NAS
129 | Q166I14410
130 | QV99IR34
131 |
132 |
133 |
134 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | ================
2 | python-qnapstats
3 | ================
4 |
5 | .. image:: https://img.shields.io/github/actions/workflow/status/colinodell/python-qnapstats/test.yml?branch=master&?style=flat-square
6 | :target: https://github.com/colinodell/python-qnapstats/actions?query=workflow%3ATest+branch%3Amaster
7 | :alt: Build Status
8 | .. image:: https://img.shields.io/pypi/pyversions/qnapstats.svg?style=flat-square
9 | :target: https://pypi.python.org/pypi/qnapstats
10 | :alt: Supported Python Versions
11 |
12 | Library from obtaining system information from QNAP NAS devices running QTS.
13 |
14 | Installation
15 | ============
16 |
17 | This library requires `xmltodict`, so make sure you have that installed:
18 |
19 | .. code-block:: bash
20 |
21 | pip3 install xmltodict>=0.10.0
22 |
23 | Then install this Python module:
24 |
25 | .. code-block:: bash
26 |
27 | pip3 install qnapstats
28 |
29 | Usage Example
30 | =============
31 |
32 | .. code-block:: python
33 |
34 | #!/usr/bin/env python3
35 | from qnapstats import QNAPStats
36 | from pprint import pprint
37 |
38 | qnap = QNAPStats('192.168.1.3', 8080, 'admin', 'correcthorsebatterystaple')
39 |
40 | pprint(qnap.get_system_stats())
41 | pprint(qnap.get_system_health())
42 | pprint(qnap.get_smart_disk_health())
43 | pprint(qnap.get_volumes())
44 | pprint(qnap.get_bandwidth())
45 |
46 | Account
47 | =======
48 | The account you connect with must have system monitoring permissions. The simplest
49 | option is to put it in the admin group; it doesn't necessarily
50 | need to be THE "administrator" account, but you can use some account in the
51 | administrators group.
52 |
53 | Alternatively you can configure a normal account and enable Delegated Administration
54 | in control panel, and activate "System Monitoring."
55 |
56 | Once the account is created, and/or you upgrade to a newer of like QTS 5,
57 | also be sure to log into your NAS and complete any agreements, warnings, wizards, etc.
58 | that may prevent this library from using the QNAP API.
59 |
60 | MFA/2FA must also be disabled for that user for this library to work.
61 |
62 | Device Support
63 | ==============
64 |
65 | This library has been tested against the following devices and firmwares:
66 |
67 | +------------------+------------------------+----------------------------------------+
68 | | Model | QTS* Firmware Versions | Notes |
69 | +==================+========================+========================================+
70 | | D4 Pro | 4.5.1 | User-reported: no automated tests |
71 | +------------------+------------------------+----------------------------------------+
72 | | TS-110 | 4.2.4 | |
73 | +------------------+------------------------+----------------------------------------+
74 | | TS-112P | 4.3.3 | This device does not report CPU temps |
75 | +------------------+------------------------+----------------------------------------+
76 | | TS-210 | 4.2.6 | This device does not report CPU temps |
77 | +------------------+------------------------+----------------------------------------+
78 | | TS-219P II | 4.3.3 | User-reported: no automated tests |
79 | +------------------+------------------------+----------------------------------------+
80 | | TS-251B | 4.4.3 | |
81 | +------------------+------------------------+----------------------------------------+
82 | | TS-228A | 5.0.1 | This device does not report CPU temps |
83 | +------------------+------------------------+----------------------------------------+
84 | | TS-233 | 5.1.x | |
85 | +------------------+------------------------+----------------------------------------+
86 | | TS-251+ | 4.5.1 | No information on dnsInfo |
87 | +------------------+------------------------+----------------------------------------+
88 | | TS-253 Pro | 4.5.2 | |
89 | +------------------+------------------------+----------------------------------------+
90 | | TS-253D | 4.5.3 | |
91 | +------------------+------------------------+----------------------------------------+
92 | | TS-332 | 5.0.0 | |
93 | +------------------+------------------------+----------------------------------------+
94 | | TS-364 | 5.0.1 | |
95 | +------------------+------------------------+----------------------------------------+
96 | | TS-269L | 4.3.3 | User-reported: no automated tests |
97 | +------------------+------------------------+----------------------------------------+
98 | | TS-410 | 4.2.3 | This device does not report CPU temps |
99 | +------------------+------------------------+----------------------------------------+
100 | | TS-412 | 4.3.3 | This device does not report CPU temps |
101 | +------------------+------------------------+----------------------------------------+
102 | | TS-431P | 4.3.4 | |
103 | +------------------+------------------------+----------------------------------------+
104 | | TS-439 Pro II+ | 4.2.6 | |
105 | +------------------+------------------------+----------------------------------------+
106 | | TS-451 | 4.2.2 - 4.2.4 | |
107 | +------------------+------------------------+----------------------------------------+
108 | | TS-453A | 4.3.4; 5.0.1 | |
109 | +------------------+------------------------+----------------------------------------+
110 | | TS-453Be | 4.2.3; 5.0.1 | |
111 | +------------------+------------------------+----------------------------------------+
112 | | TS-464 | 5.2.7 | User-reported: no automated tests |
113 | +------------------+------------------------+----------------------------------------+
114 | | TS-639 | 4.2.3 | |
115 | +------------------+------------------------+----------------------------------------+
116 | | TS-659 | 4.2.6 | May report `None` for some disk temps |
117 | +------------------+------------------------+----------------------------------------+
118 | | TS-853 Pro | 4.5.4 | |
119 | +------------------+------------------------+----------------------------------------+
120 | | TS-869 Pro | 4.3.4 | |
121 | +------------------+------------------------+----------------------------------------+
122 | | TS-873A | 5.0.1.2248 | |
123 | +------------------+------------------------+----------------------------------------+
124 | | TS-1677XU-RP | 4.5.2 | |
125 | +------------------+------------------------+----------------------------------------+
126 | | TS-EC1280U | 4.5.2 | |
127 | +------------------+------------------------+----------------------------------------+
128 | | TS-h886 | QuTS h5.0.1.2376 | |
129 | +------------------+------------------------+----------------------------------------+
130 | | TS-X53 | 4.5.4 | |
131 | +------------------+------------------------+----------------------------------------+
132 | | TVS-672N | 5.0.1 | |
133 | +------------------+------------------------+----------------------------------------+
134 | | TVS-1282 | 5.0.1 | |
135 | +------------------+------------------------+----------------------------------------+
136 |
137 | ⚠️ *QuTS is not currently supported - see [issue #84](https://github.com/colinodell/python-qnapstats/issues/84)*
138 |
139 | Other QNAP devices using these QTS firmwares should probably work fine, as should the devices listed above on newer firmwares.
140 | If you encounter any compatibility issues, please let us know (or better yet, contribute a patch!)
141 |
142 |
143 | **Upgrading to QTS 5?** Make sure the account you connect with meets the criteria listed earlier in this README.
144 | Also be sure to log into your NAS and complete any agreements, warnings, wizards, etc. that may prevent this
145 | library from using the QNAP API.
146 |
--------------------------------------------------------------------------------
/tests/responses/TS-EC1280U-4.5.2/smartdiskhealth.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/tests/responses/TS-1677XU-RP-4.5.2/smartdiskhealth.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/qnapstats/qnap_stats.py:
--------------------------------------------------------------------------------
1 | """Module containing multiple classes to obtain QNAP system stats via cgi calls."""
2 | # -*- coding:utf-8 -*-
3 | import base64
4 | import json
5 | import xmltodict
6 | import requests
7 |
8 |
9 | # pylint: disable=too-many-instance-attributes
10 | class QNAPStats:
11 | """Class containing the main functions."""
12 |
13 | # pylint: disable=too-many-arguments
14 | def __init__(self, host, port, username, password, debugmode=False, verify_ssl=True, timeout=5):
15 | """Instantiate a new qnap_stats object."""
16 | self._username = username
17 | self._password = base64.b64encode(password.encode('utf-8')).decode('ascii')
18 |
19 | self._sid = None
20 | self._debugmode = debugmode
21 |
22 | self._session_error = False
23 | self._session = None # type: requests.Session
24 |
25 | if not (host.startswith("http://") or host.startswith("https://")):
26 | host = "http://" + host
27 |
28 | self._verify_ssl = verify_ssl
29 | self._timeout = timeout
30 |
31 | self._base_url = f"{host}:{port}/cgi-bin/"
32 |
33 | def _debuglog(self, message):
34 | """Output message if debug mode is enabled."""
35 | if self._debugmode:
36 | print("DEBUG: " + message)
37 |
38 | def _init_session(self):
39 | if self._sid is None or self._session is None or self._session_error:
40 | # Clear sid and reset error
41 | self._sid = None
42 | self._session_error = False
43 |
44 | if self._session is not None:
45 | self._session = None
46 | self._debuglog("Creating new session")
47 | self._session = requests.Session()
48 |
49 | # We created a new session so login
50 | if self._login() is False:
51 | self._session_error = True
52 | self._debuglog("Login failed, unable to process request")
53 | return
54 |
55 | def _login(self):
56 | """Log into QNAP and obtain a session id."""
57 | data = {"user": self._username, "pwd": self._password}
58 | result = self._execute_post_url("authLogin.cgi", data, False)
59 |
60 | if result is None or not result.get("authSid"):
61 | # Another method to login
62 | suffix_url = "authLogin.cgi?user=" + self._username + "&pwd=" + self._password
63 | result = self._execute_get_url(suffix_url, False)
64 |
65 | if result is None:
66 | return False
67 |
68 | self._sid = result["authSid"]
69 |
70 | return True
71 |
72 | def _get_url(self, url, retry_on_error=True, **kwargs):
73 | """High-level function for making GET requests."""
74 | self._init_session()
75 |
76 | result = self._execute_get_url(url, **kwargs)
77 | if (self._session_error or result is None) and retry_on_error:
78 | self._debuglog("Error occured, retrying...")
79 | self._get_url(url, False, **kwargs)
80 |
81 | return result
82 |
83 | def _execute_get_url(self, url, append_sid=True, **kwargs):
84 | """Low-level function to execute a GET request."""
85 | url = self._base_url + url
86 | self._debuglog("GET from URL: " + url)
87 |
88 | if append_sid:
89 | self._debuglog("Appending access_token (SID: " + self._sid + ") to url")
90 | url = f"{url}&sid={self._sid}"
91 |
92 | resp = self._session.get(url, timeout=self._timeout, verify=self._verify_ssl)
93 | return self._handle_response(resp, **kwargs)
94 |
95 | def _execute_post_url(self, url, data, append_sid=True, **kwargs):
96 | """Low-level function to execute a POST request."""
97 | url = self._base_url + url
98 | self._debuglog("POST to URL: " + url)
99 |
100 | if append_sid:
101 | self._debuglog("Appending access_token (SID: " + self._sid + ") to url")
102 | data["sid"] = self._sid
103 |
104 | resp = self._session.post(url, data, timeout=self._timeout, verify=self._verify_ssl)
105 | return self._handle_response(resp, **kwargs)
106 |
107 | def _handle_response(self, resp, force_list=None):
108 | """Ensure response is successful and return body as XML."""
109 | self._debuglog("Request executed: " + str(resp.status_code))
110 | if resp.status_code != 200:
111 | return None
112 |
113 | if resp.headers["Content-Type"] != "text/xml":
114 | # JSON requests not currently supported
115 | return None
116 | self._debuglog("Headers: " + json.dumps(dict(resp.headers)))
117 | self._debuglog("Cookies: " + json.dumps(dict(resp.cookies)))
118 | self._debuglog("Response Text: " + resp.text)
119 | data = xmltodict.parse(resp.content, force_list=force_list)['QDocRoot']
120 |
121 | auth_passed = data.get('authPassed')
122 | if auth_passed is not None and len(auth_passed) == 1 and auth_passed == "0":
123 | self._session_error = True
124 | return None
125 |
126 | return data
127 |
128 | def get_system_health(self):
129 | """Obtain the system's overall health."""
130 | resp = self._get_url("management/manaRequest.cgi?subfunc=sysinfo&sysHealth=1")
131 | if resp is None:
132 | return None
133 |
134 | status = resp["func"]["ownContent"]["sysHealth"]["status"]
135 | if status is None or len(status) == 0:
136 | return None
137 |
138 | return status
139 |
140 | def get_volumes(self):
141 | """Obtain information about volumes and shared directories."""
142 | resp = self._get_url(
143 | "management/chartReq.cgi?chart_func=disk_usage&disk_select=all&include=all",
144 | force_list=("volume", "volumeUse", "folder_element")
145 | )
146 |
147 | if resp is None:
148 | return None
149 |
150 | if resp["volumeList"] is None or resp["volumeUseList"] is None:
151 | return {}
152 |
153 | volumes = {}
154 | id_map = {}
155 |
156 | for vol in resp["volumeList"]["volume"]:
157 | key = vol["volumeValue"]
158 | label = vol["volumeLabel"] if "volumeLabel" in vol else "Volume " + vol["volumeValue"]
159 |
160 | volumes[label] = {
161 | "id": key,
162 | "label": label
163 | }
164 |
165 | id_map[key] = label
166 |
167 | for vol in resp["volumeUseList"]["volumeUse"]:
168 | id_number = vol["volumeValue"]
169 |
170 | # Skip any system reserved volumes
171 | if id_number not in id_map:
172 | continue
173 |
174 | key = id_map[id_number]
175 |
176 | volumes[key]["free_size"] = int(vol["free_size"])
177 | volumes[key]["total_size"] = int(vol["total_size"])
178 |
179 | folder_elements = vol["folder_element"]
180 | if len(folder_elements) > 0:
181 | volumes[key]["folders"] = []
182 | for folder in folder_elements:
183 | try:
184 | sharename = folder["sharename"]
185 | used_size = int(folder["used_size"])
186 | volumes[key]["folders"].append({"sharename": sharename, "used_size": used_size})
187 | except Exception as e:
188 | print(e.args)
189 |
190 | return volumes
191 |
192 | def get_smart_disk_health(self):
193 | """Obtain SMART information about each disk."""
194 | resp = self._get_url("disk/qsmart.cgi?func=all_hd_data", force_list="entry")
195 |
196 | if resp is None:
197 | return None
198 |
199 | disks = {}
200 | for disk in resp["Disk_Info"]["entry"]:
201 | if disk["Model"]:
202 | disks[disk["HDNo"]] = {
203 | "drive_number": disk["HDNo"],
204 | "health": disk["Health"],
205 | "temp_c": int(disk["Temperature"]["oC"]) if disk["Temperature"]["oC"] is not None else None,
206 | "temp_f": int(disk["Temperature"]["oF"]) if disk["Temperature"]["oF"] is not None else None,
207 | "capacity": disk["Capacity"],
208 | "model": disk["Model"],
209 | "serial": disk["Serial"],
210 | "type": "ssd" if ("hd_is_ssd" in disk and int(disk["hd_is_ssd"])) else "hdd",
211 | }
212 |
213 | return disks
214 |
215 | def get_system_stats(self):
216 | """Obtain core system information and resource utilization."""
217 | resp = self._get_url(
218 | "management/manaRequest.cgi?subfunc=sysinfo&hd=no&multicpu=1",
219 | force_list=("DNS_LIST")
220 | )
221 |
222 | if resp is None:
223 | return None
224 |
225 | root = resp["func"]["ownContent"]["root"]
226 |
227 | details = {
228 | "system": {
229 | "name": root["server_name"],
230 | "model": resp["model"]["displayModelName"],
231 | "serial_number": root["serial_number"],
232 | "temp_c": int(root["sys_tempc"]),
233 | "temp_f": int(root["sys_tempf"]),
234 | "timezone": root["timezone"],
235 | },
236 | "firmware": {
237 | "version": resp["firmware"]["version"],
238 | "build": resp["firmware"]["build"],
239 | "patch": resp["firmware"]["patch"],
240 | "build_time": resp["firmware"]["buildTime"],
241 | },
242 | "uptime": {
243 | "days": int(root["uptime_day"]),
244 | "hours": int(root["uptime_hour"]),
245 | "minutes": int(root["uptime_min"]),
246 | "seconds": int(root["uptime_sec"]),
247 | },
248 | "cpu": {
249 | "model": root["cpu_model"] if "cpu_model" in root else None,
250 | "usage_percent": float(root["cpu_usage"].replace("%", "")),
251 | "temp_c": int(root["cpu_tempc"]) if "cpu_tempc" in root else None,
252 | "temp_f": int(root["cpu_tempf"]) if "cpu_tempf" in root else None,
253 | },
254 | "memory": {
255 | "total": float(root["total_memory"]),
256 | "free": float(root["free_memory"]),
257 | },
258 | "nics": {},
259 | "dns": [],
260 | "sysfans": {},
261 | }
262 |
263 | nic_count = int(root["nic_cnt"])
264 | for nic_index in range(nic_count):
265 | i = str(nic_index + 1)
266 | interface = "eth" + str(nic_index)
267 | status = root["eth_status" + i]
268 | details["nics"][interface] = {
269 | "link_status": "Up" if status == "1" else "Down",
270 | "max_speed": int(root["eth_max_speed" + i]),
271 | "ip": root["eth_ip" + i],
272 | "mask": root["eth_mask" + i],
273 | "mac": root["eth_mac" + i],
274 | "usage": root["eth_usage" + i],
275 | "rx_packets": int(root["rx_packet" + i]),
276 | "tx_packets": int(root["tx_packet" + i]),
277 | "err_packets": int(root["err_packet" + i])
278 | }
279 |
280 | sysfan_count = int(root["sysfan_count"])
281 | for sysfan_index in range(sysfan_count):
282 | i = str(sysfan_index + 1)
283 | sysfan = "sysfan" + str(sysfan_index)
284 | details["sysfans"][sysfan] = {
285 | "speed": int(root["sysfan" + i]),
286 | "status": "alert" if int(root["sysfan" + i + "_stat"]) == -1 else "ok"
287 | }
288 |
289 |
290 | dnsInfo = root.get("dnsInfo")
291 | if dnsInfo:
292 | for dns in dnsInfo["DNS_LIST"]:
293 | details["dns"].append(dns)
294 |
295 | return details
296 |
297 | def get_bandwidth(self):
298 | """Obtain the current bandwidth usage speeds."""
299 | resp = self._get_url(
300 | "management/chartReq.cgi?chart_func=QSM40bandwidth",
301 | force_list="item"
302 | )
303 |
304 | if resp and "bandwidth_info" not in resp:
305 | # changes in API since QTS 4.5.4, old query returns no values
306 | resp = self._get_url("management/chartReq.cgi?chart_func=bandwidth")
307 |
308 | if resp is None:
309 | return None
310 |
311 | details = {}
312 | interfaces = []
313 | bandwidth_info = resp["bandwidth_info"]
314 |
315 | default = resp.get("df_gateway") or bandwidth_info.get("df_gateway")
316 |
317 | if "item" in bandwidth_info:
318 | interfaces.extend(bandwidth_info["item"])
319 | else:
320 | interfaceIds = []
321 | if bandwidth_info["eth_index_list"]:
322 | for num in bandwidth_info["eth_index_list"].split(','):
323 | interfaceIds.append("eth" + num)
324 | if bandwidth_info["wlan_index_list"]:
325 | for num in bandwidth_info["wlan_index_list"].split(','):
326 | interfaceIds.append("wlan" + num)
327 | for interfaceId in interfaceIds:
328 | interface = bandwidth_info[interfaceId]
329 | interface["id"] = interfaceId
330 | interfaces.extend([interface])
331 |
332 | for item in interfaces:
333 | details[item["id"]] = {
334 | "name": item["dname"] if "dname" in item else item["name"],
335 | "rx": round(int(item["rx"]) / 5),
336 | "tx": round(int(item["tx"]) / 5),
337 | "is_default": item["id"] == default
338 | }
339 |
340 | return details
341 |
342 | def get_firmware_update(self):
343 | """Get firmware update version if available."""
344 | resp = self._get_url("sys/sysRequest.cgi?subfunc=firm_update")
345 | if resp is None:
346 | return None
347 |
348 | new_version = resp["func"]["ownContent"]["newVersion"]
349 | if new_version is None or len(new_version) == 0:
350 | return None
351 |
352 | return new_version
353 |
354 | def list_external_drive(self):
355 | """List External drive connected on qnap."""
356 | data = {"func": "getExternalDev"}
357 | resp = self._execute_post_url("devices/devRequest.cgi", data=data)
358 | if resp is None:
359 | return None
360 |
361 | external_drives = resp.get("func", {}).get("ownContent", {}).get("externalDevice")
362 | if external_drives is None or len(external_drives) == 0:
363 | return None
364 |
365 | return external_drives
366 |
367 | def get_storage_information_on_external_device(self):
368 | """Get informations on volumes in External drive connected on qnap."""
369 | data = {"func": "external_get_all"}
370 | resp = self._execute_post_url("disk/disk_manage.cgi", data=data)
371 | if resp is None:
372 | return None
373 |
374 | disk_vol = resp.get("Disk_Vol")
375 | if disk_vol is None or len(disk_vol) == 0:
376 | return None
377 |
378 | return disk_vol
379 |
--------------------------------------------------------------------------------