Compare commits

...

117 Commits
231 ... 239

Author SHA1 Message Date
Pascal Vizeli
160fbb2589 Merge pull request #2014 from home-assistant/dev
Release 239
2020-09-04 16:27:15 +02:00
Pascal Vizeli
c85aa664e1 Use packages version (#2013)
* Use package version

* more readable

* Fix
2020-09-04 16:20:07 +02:00
Joakim Sørensen
51dcbf5db7 Update panel to faee2c3 (#2012) 2020-09-04 16:00:21 +02:00
Pascal Vizeli
fa114a4a03 Update plugin required (#2011) 2020-09-04 14:37:18 +02:00
dependabot[bot]
d7fd58bdb9 Bump gitpython from 3.1.7 to 3.1.8 (#2010)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-09-04 09:09:58 +02:00
Pascal Vizeli
38b0aea8e2 Update manage role for network access (#2008) 2020-09-03 16:50:10 +02:00
Joakim Sørensen
41eade9325 Fix API.md for network/interface/ (#2009) 2020-09-03 16:49:59 +02:00
Joakim Sørensen
e64cf41aec Addon api changes (#2006)
* Add startup to addon info API

* Don't fail when validating, just return the problem as 200

* Add sugestions

* Review comments
2020-09-03 16:38:41 +02:00
Pascal Vizeli
02872b5e75 Prevent enable watchdog on startup once (#2005)
* Prevent enable watchdog on startup once

* Update supervisor/addons/addon.py

Co-authored-by: Joakim Sørensen <joasoe@gmail.com>

* fix black

Co-authored-by: Joakim Sørensen <joasoe@gmail.com>
2020-09-03 16:36:45 +02:00
Joakim Sørensen
e4d49bb459 Remove annotations (#2007) 2020-09-03 16:36:09 +02:00
dependabot[bot]
d38b7d5a82 Bump sentry-sdk from 0.17.2 to 0.17.3 (#2003)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 0.17.2 to 0.17.3.
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGES.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/0.17.2...0.17.3)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-09-03 09:16:17 +02:00
Joakim Sørensen
537c5d3197 Update frontend to c7f8fe1 (#2002) 2020-09-02 16:06:20 +02:00
dependabot[bot]
575df2fcf6 Bump sentry-sdk from 0.17.1 to 0.17.2 (#2001)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 0.17.1 to 0.17.2.
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGES.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/0.17.1...0.17.2)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-09-02 08:36:10 +02:00
dependabot[bot]
c08c3c6b37 Bump sentry-sdk from 0.17.0 to 0.17.1 (#1999)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 0.17.0 to 0.17.1.
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGES.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/0.17.0...0.17.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-08-31 10:03:32 +02:00
dependabot[bot]
2acf28609e Bump pydocstyle from 5.1.0 to 5.1.1 (#1998)
Bumps [pydocstyle](https://github.com/PyCQA/pydocstyle) from 5.1.0 to 5.1.1.
- [Release notes](https://github.com/PyCQA/pydocstyle/releases)
- [Changelog](https://github.com/PyCQA/pydocstyle/blob/master/docs/release_notes.rst)
- [Commits](https://github.com/PyCQA/pydocstyle/compare/5.1.0...5.1.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-08-31 09:45:51 +02:00
Pascal Vizeli
bb59d0431e fix dns server description 2020-08-30 21:59:47 +02:00
Joakim Sørensen
1c7b1f1462 Adds options_validate API (#1996) 2020-08-30 17:58:13 +02:00
Joakim Sørensen
f32d17d924 Format API error messages (#1997) 2020-08-30 17:53:10 +02:00
Pascal Vizeli
928a4d8dce Bump version 239 2020-08-29 15:12:38 +02:00
Pascal Vizeli
dd3ba93308 Merge pull request #1995 from home-assistant/dev
Release 238
2020-08-29 15:11:48 +02:00
Pascal Vizeli
7e1b179cdd Fix error handling unsupported (#1994)
* Try to catch unhealthy

* revert
2020-08-29 12:03:29 +02:00
Pascal Vizeli
a9a2c35f06 Sentry send env infos (#1992) 2020-08-29 11:44:08 +02:00
Pascal Vizeli
58b88a6919 Check if supervisor run priv (#1993) 2020-08-29 11:35:15 +02:00
Joakim Sørensen
f937876a1b Set parse issues as critical (#1989) 2020-08-29 10:56:45 +02:00
Pascal Vizeli
8193f43634 Fix secrets with add-on validation (#1990) 2020-08-29 10:54:16 +02:00
Pascal Vizeli
1d3f880f82 Bump version 238 2020-08-28 14:39:17 +02:00
Pascal Vizeli
ef2fa8d2e2 Merge pull request #1987 from home-assistant/dev
Release 237
2020-08-28 14:38:30 +02:00
Joakim Sørensen
51997b3e7c Update frontend to dc5b920 (#1986) 2020-08-28 14:31:19 +02:00
Joakim Sørensen
98785b00e2 Use jinja for payload generation (#1985) 2020-08-28 11:46:22 +02:00
Joakim Sørensen
8d3694884d Split payload so we can set auto without IP configuration (#1982)
* Guard for no interfaces

* Host reload on update

* Extract payload

* Check eth and wifi interfaces with a valid ip4 config

* Add tests

* Fix tests

* Move to enum
2020-08-28 10:40:50 +02:00
Pascal Vizeli
a2821a98ad Fix gvar for dbus if binary is not a string (#1984) 2020-08-27 17:04:14 +02:00
Pascal Vizeli
8d552ae15c Fix api proxy ensure_access_token for websocket (#1983) 2020-08-27 16:59:03 +02:00
Pascal Vizeli
6db4c60f47 Fix byte parser (#1981) 2020-08-27 14:31:34 +02:00
Pascal Vizeli
805c0385a0 Bump version 237 2020-08-27 11:52:32 +02:00
Pascal Vizeli
cea6e7a9f2 Merge pull request #1979 from home-assistant/dev
Release 236
2020-08-27 10:52:18 +02:00
Pascal Vizeli
127073c01b Small cleanup & adjustments for 236 (#1978) 2020-08-27 10:33:35 +02:00
dependabot[bot]
30fe36ae05 Bump cryptography from 3.0 to 3.1 (#1977)
Bumps [cryptography](https://github.com/pyca/cryptography) from 3.0 to 3.1.
- [Release notes](https://github.com/pyca/cryptography/releases)
- [Changelog](https://github.com/pyca/cryptography/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/3.0...3.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-08-27 10:18:24 +02:00
Pascal Vizeli
58bd677832 fix ensure_access_token 2020-08-26 21:16:38 +00:00
Pascal Vizeli
1a3b369dd7 Update black 20.8b1 (#1976) 2020-08-26 23:01:29 +02:00
Pascal Vizeli
6e38216abd Adjust add-on state (#1975)
* Adjust add-on state

* schedule refresh addon
2020-08-26 22:52:41 +02:00
Pascal Vizeli
efcfc1f841 Watchdog for Add-ons (#1970)
* Watchdog for Add-ons

* Run task

* Extend appliaction watchdog

* fix spell

* Add running tasks

* Add tests

* Fix states

* Update supervisor/misc/tasks.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update tests/test_validate.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Adjust timeout

* change timeout

* Modify tasker

* slots the task object

* fix typing

* Add tests

* fix lint

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-08-26 22:20:35 +02:00
Joakim Sørensen
8dea50ce83 Update frontend to c1a4b27 (#1974) 2020-08-26 18:24:22 +02:00
Joakim Sørensen
7a5a01bdcc Add network to host suppported features (#1973) 2020-08-26 14:11:21 +02:00
Joakim Sørensen
bd1450a682 Update frontend to 04df6c3 (#1972) 2020-08-26 09:42:34 +02:00
dependabot[bot]
c538c1ce7f Bump getsentry/action-release from v1.0.0 to v1.0.1 (#1971)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-08-26 08:37:45 +02:00
Pascal Vizeli
b6d59c4f64 Fix long waiting time on first boot (#1968) 2020-08-25 10:37:11 +02:00
dependabot[bot]
a758ccaf5c Bump sentry-sdk from 0.16.5 to 0.17.0 (#1966)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 0.16.5 to 0.17.0.
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGES.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/0.16.5...0.17.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-08-25 09:41:56 +02:00
Joakim Sørensen
e8b04cc20a Update frontend to 6599351 (#1965) 2020-08-24 22:51:40 +02:00
Joakim Sørensen
9bcb15dbc0 MVP: Add Network Manager context (#1937)
Co-authored-by: Pascal Vizeli <pvizeli@syshack.ch>
2020-08-24 16:58:02 +02:00
Pascal Vizeli
1e953167b6 Update stale.yml 2020-08-24 13:44:34 +02:00
dependabot[bot]
979586cdb2 Bump pylint from 2.5.3 to 2.6.0 (#1962)
* Bump pylint from 2.5.3 to 2.6.0

Bumps [pylint](https://github.com/PyCQA/pylint) from 2.5.3 to 2.6.0.
- [Release notes](https://github.com/PyCQA/pylint/releases)
- [Changelog](https://github.com/PyCQA/pylint/blob/master/ChangeLog)
- [Commits](https://github.com/PyCQA/pylint/compare/pylint-2.5.3...pylint-2.6.0)

Signed-off-by: dependabot[bot] <support@github.com>

* Address lint issues

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Pascal Vizeli <pvizeli@syshack.ch>
2020-08-24 10:16:50 +02:00
dependabot[bot]
cd31fad56d Bump pre-commit from 2.6.0 to 2.7.1 (#1961)
Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 2.6.0 to 2.7.1.
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/master/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v2.6.0...v2.7.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-08-24 10:10:33 +02:00
dependabot[bot]
ff57d88e2a Bump codecov from 2.1.8 to 2.1.9 (#1963)
Bumps [codecov](https://github.com/codecov/codecov-python) from 2.1.8 to 2.1.9.
- [Release notes](https://github.com/codecov/codecov-python/releases)
- [Changelog](https://github.com/codecov/codecov-python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-python/compare/2.1.8...v2.1.9)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-08-24 09:18:14 +02:00
dependabot[bot]
06cb5e171e Bump pydocstyle from 5.0.2 to 5.1.0 (#1959)
Bumps [pydocstyle](https://github.com/PyCQA/pydocstyle) from 5.0.2 to 5.1.0.
- [Release notes](https://github.com/PyCQA/pydocstyle/releases)
- [Changelog](https://github.com/PyCQA/pydocstyle/blob/master/docs/release_notes.rst)
- [Commits](https://github.com/PyCQA/pydocstyle/compare/5.0.2...5.1.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-08-24 09:16:54 +02:00
dependabot[bot]
a8b70a2e13 Bump docker from 4.3.0 to 4.3.1 (#1960)
Bumps [docker](https://github.com/docker/docker-py) from 4.3.0 to 4.3.1.
- [Release notes](https://github.com/docker/docker-py/releases)
- [Commits](https://github.com/docker/docker-py/compare/4.3.0...4.3.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-08-24 09:16:35 +02:00
dependabot[bot]
948019ccee Bump debugpy from 1.0.0rc1 to 1.0.0rc2 (#1953)
Bumps [debugpy](https://github.com/microsoft/debugpy) from 1.0.0rc1 to 1.0.0rc2.
- [Release notes](https://github.com/microsoft/debugpy/releases)
- [Commits](https://github.com/microsoft/debugpy/compare/v1.0.0rc1...v1.0.0rc2)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-08-21 09:01:55 +02:00
dependabot[bot]
89ed109505 Bump attrs from 19.3.0 to 20.1.0 (#1954)
Bumps [attrs](https://github.com/python-attrs/attrs) from 19.3.0 to 20.1.0.
- [Release notes](https://github.com/python-attrs/attrs/releases)
- [Changelog](https://github.com/python-attrs/attrs/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/python-attrs/attrs/compare/19.3.0...20.1.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-08-21 09:01:22 +02:00
Pascal Vizeli
fae246c503 Fix setup module list (#1952)
* Fix setup module list

* Update setup.py

* Update setup.py
2020-08-20 11:35:05 +02:00
Pascal Vizeli
2411b4287d Group all homeassistant/core into a module (#1950)
* Group all homeassistant/core into a module

* fix references

* fix lint

* streamline object property protection

* Fix api
2020-08-20 11:22:04 +02:00
Pascal Vizeli
b3308ecbe0 Fix timeout and reflect s6-overlay (#1947) 2020-08-19 16:46:39 +02:00
Pascal Vizeli
3541cbff5e Remove old dns forwarder (#1945) 2020-08-18 21:40:52 +02:00
Pascal Vizeli
838ba7ff36 Bump version 236 2020-08-18 15:21:25 +02:00
Pascal Vizeli
e9802f92c9 Merge pull request #1944 from home-assistant/dev
Release 235
2020-08-18 15:20:40 +02:00
Pascal Vizeli
016fd24859 Fix hardware serial list check (#1943) 2020-08-18 15:03:30 +02:00
Pascal Vizeli
d315e81ab2 Extend hardware/udev support & usb mapping (#1940)
* Extend hardware/udev support & usb mapping

* Cleanup list

* new style

* Fix tests

* Fix

* use frozen

* add test for usb

* Fix block disks

* Update API.md

Co-authored-by: Joakim Sørensen <joasoe@gmail.com>

Co-authored-by: Joakim Sørensen <joasoe@gmail.com>
2020-08-18 14:25:07 +02:00
dependabot[bot]
97c38b8534 Bump codecov/codecov-action from v1.0.12 to v1.0.13 (#1942)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from v1.0.12 to v1.0.13.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Commits](https://github.com/codecov/codecov-action/compare/v1.0.12...6004246f47ab62d32be025ce173b241cd84ac58e)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-08-18 08:08:09 +02:00
Pascal Vizeli
011e2b3df5 Simplify OS check (#1939) 2020-08-17 16:57:29 +02:00
Pascal Vizeli
e3ee9a299f Send exception why the update fails (#1938) 2020-08-17 15:37:19 +02:00
Pascal Vizeli
d73c10f874 Don't break startup with corrupt docker filesystem (#1936) 2020-08-17 10:44:40 +02:00
dependabot[bot]
9e448b46ba Bump pytest-cov from 2.10.0 to 2.10.1 (#1935)
Bumps [pytest-cov](https://github.com/pytest-dev/pytest-cov) from 2.10.0 to 2.10.1.
- [Release notes](https://github.com/pytest-dev/pytest-cov/releases)
- [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-cov/compare/v2.10.0...v2.10.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-08-17 09:13:36 +02:00
dependabot[bot]
9f09c46789 Bump sentry-sdk from 0.16.4 to 0.16.5 (#1934)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 0.16.4 to 0.16.5.
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGES.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/0.16.4...0.16.5)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-08-17 09:13:12 +02:00
Pascal Vizeli
fe6634551a Catch requests with docker exceptions (#1926) 2020-08-15 23:01:10 +02:00
Joakim Sørensen
22a7931a7c Update log message for unsupported OS (#1929) 2020-08-15 22:57:38 +02:00
Pascal Vizeli
94f112512f Machine ID for user/sentry & cleanups (#1928)
* Machine ID for user/sentry & cleanups

* Add tests / fix users
2020-08-15 18:26:20 +02:00
Joakim Sørensen
b6509dca1f Split extra info and add more metrics (#1927)
* Split extra

* Restructure and add info

* adjust test

* Move docker version

* Add name and repository for addons

* Test supervisor version

* Use context instead of extra

* adjust test
2020-08-15 16:26:01 +02:00
Franck Nijhof
620234e708 Add Sentry release GitHub Action workflow (#1925) 2020-08-15 10:26:15 +02:00
Pascal Vizeli
d50e866cec Bump version 235 2020-08-14 23:39:49 +02:00
Pascal Vizeli
76ad6dca02 Merge pull request #1924 from home-assistant/dev
Release 234
2020-08-14 23:38:50 +02:00
Pascal Vizeli
cdb1520a63 Fix shutdown process (#1923) 2020-08-14 23:36:07 +02:00
Pascal Vizeli
bbef706a33 Make exeption more useful (#1922) 2020-08-14 23:12:54 +02:00
Pascal Vizeli
835509901f Fix core proy TypeError (#1921) 2020-08-14 22:51:33 +02:00
Joakim Sørensen
b51f9586c4 Fix attribute in sentry data_filter (#1920) 2020-08-14 19:29:10 +02:00
Pascal Vizeli
fc83cb9559 Use lambda for less function stack frames (#1918) 2020-08-14 19:28:38 +02:00
Joakim Sørensen
f5f5f829ac Add test for denylist (#1914)
* Add test for denylist

* Mock API in conftest
2020-08-14 18:09:03 +02:00
Martin Hjelmare
930eed4500 Fix ingress unbound local errors (#1915) 2020-08-14 15:20:36 +02:00
Pascal Vizeli
01a8b58054 Bump version to 234 2020-08-14 14:12:52 +02:00
Pascal Vizeli
eba1d01fc2 Merge pull request #1913 from home-assistant/dev
Release 233
2020-08-14 14:12:11 +02:00
Joakim Sørensen
84755836c9 Filter AddonConfigurationError from sentry (#1912) 2020-08-14 14:06:46 +02:00
Joakim Sørensen
c9585033cb Sanitize event (#1908)
* Sanitise event

* No need to remove supervisor token

* cleanup

* Typo

* Review comments

* Move and test

* Move and use hdr
2020-08-14 13:40:14 +02:00
Joakim Sørensen
2d312c276f Adds image denylist (#1896)
* Adds image denylist

* Move to DockerAPI

* Wording

* Use error instead of critical

* Update supervisor/docker/__init__.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Run in executor

* Add pyouroboros/ouroboros

* Mark as unsupported

* Use set

* Update supervisor/docker/__init__.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Remove duplicate

* Change logging

* Update supervisor/docker/__init__.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Set healthy to False

* small move

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: Pascal Vizeli <pvizeli@syshack.ch>
2020-08-14 09:45:22 +02:00
Joakim Sørensen
3b0d0e9928 Add disk total, used, free to host/info API (#1909) 2020-08-14 09:24:38 +02:00
dependabot[bot]
8307b153e3 Bump sentry-sdk from 0.16.3 to 0.16.4 (#1910)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 0.16.3 to 0.16.4.
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGES.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/0.16.3...0.16.4)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-08-14 09:11:09 +02:00
Pascal Vizeli
dfaffe3ec5 Self fix corrupt docker network (#1907) 2020-08-13 17:50:39 +02:00
Pascal Vizeli
8d7b15cbeb Improve supported / healthy handling and more checks (#1905)
Co-authored-by: Joakim Sørensen <joasoe@gmail.com>
2020-08-13 16:49:34 +02:00
Joakim Sørensen
00969a67ac Set not supported with wrong docker configuration (#1904)
* set not supported with wrong docker configuration

* Set not supported if no access to udev monitor

* Move flag
2020-08-13 10:44:45 +02:00
Pascal Vizeli
a374d4e817 Make sure we only run ingress with installed add-ons (#1903)
* Make sure we only run ingress with installed add-ons

* Make sure we reload tokens after uninstall

* Optimize token handling

* Fix error
2020-08-13 10:43:36 +02:00
Pascal Vizeli
f5dda39f63 Disable sentry dev (#1902)
* Disable sentry on dev env

* Offload env
2020-08-13 09:39:26 +02:00
Pascal Vizeli
fb5d54d5fe Bump version 233 2020-08-12 18:44:34 +02:00
Pascal Vizeli
d392b35fdd Merge pull request #1901 from home-assistant/dev
Release 232
2020-08-12 18:44:04 +02:00
Pascal Vizeli
3ceec006ac Fix isort 2020-08-12 14:47:09 +00:00
Pascal Vizeli
62a574c6bd Merge branch 'master' into dev 2020-08-12 16:43:18 +02:00
Pascal Vizeli
821c10b2bd Fix connection reset error on ingress (#1899) 2020-08-12 16:11:22 +02:00
Joakim Sørensen
fa3269a098 Update panel to 77b25f5 (#1900) 2020-08-12 15:55:57 +02:00
Pascal Vizeli
a9bdab4b49 Fix validation issue addon options (#1898)
* Fix validation issue addon options

* Add some basic tests

* Update supervisor/addons/validate.py

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2020-08-12 14:49:46 +02:00
Pascal Vizeli
0df5b7d87b Mark installation as unsupported dev on stable (#1897) 2020-08-12 13:43:15 +02:00
dependabot[bot]
4861fc70ce Bump debugpy from 1.0.0b12 to 1.0.0rc1 (#1894)
Bumps [debugpy](https://github.com/microsoft/debugpy) from 1.0.0b12 to 1.0.0rc1.
- [Release notes](https://github.com/microsoft/debugpy/releases)
- [Commits](https://github.com/microsoft/debugpy/compare/1.0.0b12...v1.0.0rc1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-08-12 12:32:16 +02:00
dependabot[bot]
47c443bb92 Bump actions/upload-artifact from v2.1.3 to v2.1.4 (#1892)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from v2.1.3 to v2.1.4.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v2.1.3...58740802ef971a2d71eff71e63d48ab68d1f5507)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-08-12 08:31:17 +02:00
dependabot[bot]
9cb4b49597 Bump actions/setup-python from v2.1.1 to v2.1.2 (#1893)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from v2.1.1 to v2.1.2.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v2.1.1...24156c231c5e9d581bde27d0cdbb72715060ea51)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-08-12 08:30:54 +02:00
Pascal Vizeli
865523fd37 cleanup sentry filter 2020-08-11 19:11:22 +00:00
Pascal Vizeli
1df35a6fe1 fix sentry 2020-08-11 17:42:55 +00:00
Pascal Vizeli
e70c9d8a30 Fix order for more details 2020-08-11 16:07:33 +00:00
Pascal Vizeli
7d6b00ea4a Fix dev outside stable channel (#1891)
* Fix dev outside stable channel

* more friendly

* downgrade logger for sentry

* Show critical on the end
2020-08-11 18:02:25 +02:00
Pascal Vizeli
e5fc985915 Fix sentry message with unsupported (#1890)
* Fix sentry message with unsupported

* filter breadcrumb

* Support add-on errors

* Fix level

* Add logging info
2020-08-11 16:48:58 +02:00
Pascal Vizeli
71ccaa2bd0 Add healthy and supported to API (#1889)
* Add healthy and supported to API

* fix protected attributes
2020-08-11 16:00:51 +02:00
Pascal Vizeli
495f9f2373 Bump version to 232 2020-08-11 11:06:57 +02:00
dependabot[bot]
27274286db Bump docker from 4.2.2 to 4.3.0 (#1886)
Bumps [docker](https://github.com/docker/docker-py) from 4.2.2 to 4.3.0.
- [Release notes](https://github.com/docker/docker-py/releases)
- [Commits](https://github.com/docker/docker-py/compare/4.2.2...4.3.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-08-11 10:58:46 +02:00
Pascal Vizeli
85ba886029 Bump version 231 2020-08-11 10:57:07 +02:00
247 changed files with 10101 additions and 7123 deletions

1
.github/stale.yml vendored
View File

@@ -6,6 +6,7 @@ daysUntilClose: 7
exemptLabels:
- pinned
- security
- rfc
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable

View File

@@ -26,7 +26,7 @@ jobs:
uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
id: python
uses: actions/setup-python@v2.1.1
uses: actions/setup-python@v2.1.2
with:
python-version: ${{ matrix.python-version }}
- name: Restore Python virtual environment
@@ -69,7 +69,7 @@ jobs:
- name: Check out code from GitHub
uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.1.1
uses: actions/setup-python@v2.1.2
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
@@ -113,7 +113,7 @@ jobs:
- name: Check out code from GitHub
uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.1.1
uses: actions/setup-python@v2.1.2
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
@@ -157,7 +157,7 @@ jobs:
- name: Check out code from GitHub
uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.1.1
uses: actions/setup-python@v2.1.2
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
@@ -189,7 +189,7 @@ jobs:
- name: Check out code from GitHub
uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.1.1
uses: actions/setup-python@v2.1.2
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
@@ -230,7 +230,7 @@ jobs:
- name: Check out code from GitHub
uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.1.1
uses: actions/setup-python@v2.1.2
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
@@ -274,7 +274,7 @@ jobs:
- name: Check out code from GitHub
uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.1.1
uses: actions/setup-python@v2.1.2
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
@@ -306,7 +306,7 @@ jobs:
- name: Check out code from GitHub
uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.1.1
uses: actions/setup-python@v2.1.2
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
@@ -350,7 +350,7 @@ jobs:
- name: Check out code from GitHub
uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2.1.1
uses: actions/setup-python@v2.1.2
id: python
with:
python-version: ${{ matrix.python-version }}
@@ -391,7 +391,7 @@ jobs:
-o console_output_style=count \
tests
- name: Upload coverage artifact
uses: actions/upload-artifact@v2.1.3
uses: actions/upload-artifact@v2.1.4
with:
name: coverage-${{ matrix.python-version }}
path: .coverage
@@ -404,7 +404,7 @@ jobs:
- name: Check out code from GitHub
uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.1.1
uses: actions/setup-python@v2.1.2
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
@@ -429,4 +429,4 @@ jobs:
coverage report
coverage xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1.0.12
uses: codecov/codecov-action@v1.0.13

21
.github/workflows/sentry.yaml vendored Normal file
View File

@@ -0,0 +1,21 @@
name: Sentry Release
# yamllint disable-line rule:truthy
on:
release:
types: [published, prereleased]
jobs:
createSentryRelease:
runs-on: ubuntu-latest
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2
- name: Sentry Release
uses: getsentry/action-release@v1.0.1
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
with:
environment: production

View File

@@ -1,11 +1,13 @@
repos:
- repo: https://github.com/psf/black
rev: 19.10b0
rev: 20.8b1
hooks:
- id: black
args:
- --safe
- --quiet
- --target-version
- py38
files: ^((supervisor|tests)/.+)?[^/]+\.py$
- repo: https://gitlab.com/pycqa/flake8
rev: 3.8.3

86
API.md
View File

@@ -25,7 +25,7 @@ On success / Code 200:
To access the API you need use an authorization header with a `Bearer` token.
The token is available for add-ons and Home Assistant using the
The token is available for add-ons and Home Assistant using the
`SUPERVISOR_TOKEN` environment variable.
### Supervisor
@@ -45,6 +45,8 @@ Shows the installed add-ons from `addons`.
"arch": "armhf|aarch64|i386|amd64",
"channel": "stable|beta|dev",
"timezone": "TIMEZONE",
"healthy": "bool",
"supported": "bool",
"logging": "debug|info|warning|error|critical",
"ip_address": "ip address",
"wait_boot": "int",
@@ -240,13 +242,16 @@ return:
```json
{
"hostname": "hostname|null",
"features": ["shutdown", "reboot", "hostname", "services", "hassos"],
"operating_system": "HassOS XY|Ubuntu 16.4|null",
"kernel": "4.15.7|null",
"chassis": "specific|null",
"cpe": "xy|null",
"deployment": "stable|beta|dev|null",
"cpe": "xy|null"
"disk_total": 32.0,
"disk_used": 30.0,
"disk_free": 2.0,
"features": ["shutdown", "reboot", "hostname", "services", "hassos"],
"hostname": "hostname|null",
"kernel": "4.15.7|null",
"operating_system": "HassOS XY|Ubuntu 16.4|null"
}
```
@@ -437,6 +442,65 @@ Proxy to Home Assistant Core websocket.
}
```
### Network
Network operations over the API
#### GET `/network/info`
Get network information
```json
{
"interfaces": {
"enp0s31f6": {
"ip_address": "192.168.2.148/24",
"gateway": "192.168.2.1",
"id": "Wired connection 1",
"type": "802-3-ethernet",
"nameservers": ["192.168.2.1"],
"method": "static",
"primary": true
}
}
}
```
#### GET `/network/interface/{interface}/info`
Get information for a single interface
```json
{
"ip_address": "192.168.2.148/24",
"gateway": "192.168.2.1",
"id": "Wired connection 1",
"type": "802-3-ethernet",
"nameservers": ["192.168.2.1"],
"method": "dhcp",
"primary": true
}
```
#### POST `/network/interface/{interface}/update`
Update information for a single interface
**Options:**
| Option | Description |
| --------- | ---------------------------------------------------------------------- |
| `address` | The new IP address for the interface in the X.X.X.X/XX format |
| `dns` | List of DNS servers to use |
| `gateway` | The gateway the interface should use |
| `method` | Set if the interface should use DHCP or not, can be `dhcp` or `static` |
_All options are optional._
**NB!: If you change the `address` or `gateway` you may need to reconnect to the new address**
The result will be a updated object.
### RESTful for API add-ons
If an add-on will call itself, you can use `/addons/self/...`.
@@ -501,6 +565,7 @@ Get all available add-ons.
"version": "null|VERSION_INSTALLED",
"version_latest": "version_latest",
"state": "none|started|stopped",
"startup": "initialize|system|services|application|once",
"boot": "auto|manual",
"build": "bool",
"options": "{}",
@@ -530,6 +595,7 @@ Get all available add-ons.
"stdin": "bool",
"webui": "null|http(s)://[HOST]:port/xy/zx",
"gpio": "bool",
"usb": "[physical_path_to_usb_device]",
"kernel_modules": "bool",
"devicetree": "bool",
"docker_api": "bool",
@@ -544,7 +610,8 @@ Get all available add-ons.
"ingress_entry": "null|/api/hassio_ingress/slug",
"ingress_url": "null|/api/hassio_ingress/slug/entry.html",
"ingress_port": "null|int",
"ingress_panel": "null|bool"
"ingress_panel": "null|bool",
"watchdog": "null|bool"
}
```
@@ -553,6 +620,7 @@ Get all available add-ons.
- GET `/addons/{addon}/changelog`
- GET `/addons/{addon}/documentation`
- POST `/addons/{addon}/options`
- POST `/addons/{addon}/options/validate`
```json
{
@@ -564,7 +632,8 @@ Get all available add-ons.
"options": {},
"audio_output": "null|0,0",
"audio_input": "null|0,0",
"ingress_panel": "bool"
"ingress_panel": "bool",
"watchdog": "bool"
}
```
@@ -785,6 +854,7 @@ return:
"machine": "type",
"arch": "arch",
"supported_arch": ["arch1", "arch2"],
"supported": "bool",
"channel": "stable|beta|dev",
"logging": "debug|info|warning|error|critical",
"timezone": "Europe/Zurich"

View File

@@ -14,8 +14,7 @@ RUN \
libffi \
libpulse \
musl \
openssl \
socat
openssl
ARG BUILD_ARCH
WORKDIR /usr/src

View File

@@ -7,3 +7,5 @@ coverage:
target: 40
threshold: 0.09
comment: false
github_checks:
annotations: false

View File

@@ -1,19 +1,19 @@
aiohttp==3.6.2
async_timeout==3.0.1
attrs==19.3.0
attrs==20.1.0
cchardet==2.1.6
colorlog==4.2.1
cpe==1.2.1
cryptography==3.0
debugpy==1.0.0b12
docker==4.2.2
gitpython==3.1.7
cryptography==3.1
debugpy==1.0.0rc2
docker==4.3.1
gitpython==3.1.8
jinja2==2.11.2
packaging==20.4
pulsectl==20.5.1
pytz==2020.1
pyudev==0.22.0
ruamel.yaml==0.15.100
sentry-sdk==0.16.3
sentry-sdk==0.17.3
uvloop==0.14.0
voluptuous==0.11.7

View File

@@ -1,13 +1,14 @@
black==19.10b0
codecov==2.1.8
black==20.8b1
codecov==2.1.9
coverage==5.2.1
flake8-docstrings==1.5.0
flake8==3.8.3
pre-commit==2.6.0
pydocstyle==5.0.2
pylint==2.5.3
pre-commit==2.7.1
pydocstyle==5.1.1
pylint==2.6.0
pytest-aiohttp==0.3.0
pytest-cov==2.10.0
pytest-asyncio==0.12.0 # NB!: Versions over 0.12.0 breaks pytest-aiohttp (https://github.com/aio-libs/pytest-aiohttp/issues/16)
pytest-cov==2.10.1
pytest-timeout==1.4.2
pytest==6.0.1
pyupgrade==2.7.2

View File

@@ -35,10 +35,20 @@ setup(
"supervisor.docker",
"supervisor.addons",
"supervisor.api",
"supervisor.dbus",
"supervisor.dbus.payloads",
"supervisor.dbus.network",
"supervisor.discovery",
"supervisor.discovery.services",
"supervisor.services",
"supervisor.services.modules",
"supervisor.homeassistant",
"supervisor.host",
"supervisor.misc",
"supervisor.utils",
"supervisor.plugins",
"supervisor.snapshots",
"supervisor.store",
],
include_package_data=True,
)

View File

@@ -5,7 +5,7 @@ import logging
import tarfile
from typing import Dict, List, Optional, Union
from ..const import BOOT_AUTO, STATE_STARTED, AddonStartup
from ..const import BOOT_AUTO, AddonStartup, AddonState
from ..coresys import CoreSys, CoreSysAttributes
from ..exceptions import (
AddonsError,
@@ -45,7 +45,7 @@ class AddonManager(CoreSysAttributes):
"""Return a list of all installed add-ons."""
return list(self.local.values())
def get(self, addon_slug: str) -> Optional[AnyAddon]:
def get(self, addon_slug: str, local_only: bool = False) -> Optional[AnyAddon]:
"""Return an add-on from slug.
Prio:
@@ -54,7 +54,9 @@ class AddonManager(CoreSysAttributes):
"""
if addon_slug in self.local:
return self.local[addon_slug]
return self.store.get(addon_slug)
if not local_only:
return self.store.get(addon_slug)
return None
def from_token(self, token: str) -> Optional[Addon]:
"""Return an add-on from Supervisor token."""
@@ -98,6 +100,7 @@ class AddonManager(CoreSysAttributes):
await addon.start()
except Exception as err: # pylint: disable=broad-except
_LOGGER.warning("Can't start Add-on %s: %s", addon.slug, err)
self.sys_capture_exception(err)
await asyncio.sleep(self.sys_config.wait_boot)
@@ -105,7 +108,7 @@ class AddonManager(CoreSysAttributes):
"""Shutdown addons."""
tasks: List[Addon] = []
for addon in self.installed:
if await addon.state() != STATE_STARTED or addon.startup != stage:
if addon.state != AddonState.STARTED or addon.startup != stage:
continue
tasks.append(addon)
@@ -121,6 +124,7 @@ class AddonManager(CoreSysAttributes):
await addon.stop()
except Exception as err: # pylint: disable=broad-except
_LOGGER.warning("Can't stop Add-on %s: %s", addon.slug, err)
self.sys_capture_exception(err)
async def install(self, slug: str) -> None:
"""Install an add-on."""
@@ -149,12 +153,17 @@ class AddonManager(CoreSysAttributes):
try:
await addon.instance.install(store.version, store.image)
except DockerAPIError:
except DockerAPIError as err:
self.data.uninstall(addon)
raise AddonsError() from None
raise AddonsError() from err
else:
self.local[slug] = addon
_LOGGER.info("Add-on '%s' successfully installed", slug)
# Reload ingress tokens
if addon.with_ingress:
await self.sys_ingress.reload()
_LOGGER.info("Add-on '%s' successfully installed", slug)
async def uninstall(self, slug: str) -> None:
"""Remove an add-on."""
@@ -165,8 +174,10 @@ class AddonManager(CoreSysAttributes):
try:
await addon.instance.remove()
except DockerAPIError:
raise AddonsError() from None
except DockerAPIError as err:
raise AddonsError() from err
else:
addon.state = AddonState.UNKNOWN
await addon.remove_data()
@@ -186,7 +197,9 @@ class AddonManager(CoreSysAttributes):
await self.sys_ingress.update_hass_panel(addon)
# Cleanup Ingress dynamic port assignment
self.sys_ingress.del_dynamic_port(slug)
if addon.with_ingress:
self.sys_create_task(self.sys_ingress.reload())
self.sys_ingress.del_dynamic_port(slug)
# Cleanup discovery data
for message in self.sys_discovery.list_messages:
@@ -227,15 +240,15 @@ class AddonManager(CoreSysAttributes):
raise AddonsNotSupportedError()
# Update instance
last_state = await addon.state()
last_state: AddonState = addon.state
try:
await addon.instance.update(store.version, store.image)
# Cleanup
with suppress(DockerAPIError):
await addon.instance.cleanup()
except DockerAPIError:
raise AddonsError() from None
except DockerAPIError as err:
raise AddonsError() from err
else:
self.data.update(store)
_LOGGER.info("Add-on '%s' successfully updated", slug)
@@ -244,7 +257,7 @@ class AddonManager(CoreSysAttributes):
await addon.install_apparmor()
# restore state
if last_state == STATE_STARTED:
if last_state == AddonState.STARTED:
await addon.start()
async def rebuild(self, slug: str) -> None:
@@ -268,18 +281,18 @@ class AddonManager(CoreSysAttributes):
raise AddonsNotSupportedError()
# remove docker container but not addon config
last_state = await addon.state()
last_state: AddonState = addon.state
try:
await addon.instance.remove()
await addon.instance.install(addon.version)
except DockerAPIError:
raise AddonsError() from None
except DockerAPIError as err:
raise AddonsError() from err
else:
self.data.update(store)
_LOGGER.info("Add-on '%s' successfully rebuilt", slug)
# restore state
if last_state == STATE_STARTED:
if last_state == AddonState.STARTED:
await addon.start()
async def restore(self, slug: str, tar_file: tarfile.TarFile) -> None:
@@ -300,6 +313,7 @@ class AddonManager(CoreSysAttributes):
# Update ingress
if addon.with_ingress:
await self.sys_ingress.reload()
with suppress(HomeAssistantAPIError):
await self.sys_ingress.update_hass_panel(addon)

View File

@@ -1,4 +1,5 @@
"""Init file for Supervisor add-ons."""
import asyncio
from contextlib import suppress
from copy import deepcopy
from ipaddress import IPv4Address
@@ -11,6 +12,7 @@ import tarfile
from tempfile import TemporaryDirectory
from typing import Any, Awaitable, Dict, List, Optional
import aiohttp
import voluptuous as vol
from voluptuous.humanize import humanize_error
@@ -35,20 +37,23 @@ from ..const import (
ATTR_USER,
ATTR_UUID,
ATTR_VERSION,
ATTR_WATCHDOG,
DNS_SUFFIX,
STATE_STARTED,
STATE_STOPPED,
AddonStartup,
AddonState,
)
from ..coresys import CoreSys
from ..docker.addon import DockerAddon
from ..docker.stats import DockerStats
from ..exceptions import (
AddonConfigurationError,
AddonsError,
AddonsNotSupportedError,
DockerAPIError,
HostAppArmorError,
JsonFileError,
)
from ..utils import check_port
from ..utils.apparmor import adjust_profile
from ..utils.json import read_json_file, write_json_file
from ..utils.tar import atomic_contents_add, secure_path
@@ -63,8 +68,15 @@ RE_WEBUI = re.compile(
r":\/\/\[HOST\]:\[PORT:(?P<t_port>\d+)\](?P<s_suffix>.*)$"
)
RE_WATCHDOG = re.compile(
r"^(?:(?P<s_prefix>https?|tcp)|\[PROTO:(?P<t_proto>\w+)\])"
r":\/\/\[HOST\]:\[PORT:(?P<t_port>\d+)\](?P<s_suffix>.*)$"
)
RE_OLD_AUDIO = re.compile(r"\d+,\d+")
WATCHDOG_TIMEOUT = aiohttp.ClientTimeout(total=10)
class Addon(AddonModel):
"""Hold data for add-on inside Supervisor."""
@@ -73,12 +85,24 @@ class Addon(AddonModel):
"""Initialize data holder."""
super().__init__(coresys, slug)
self.instance: DockerAddon = DockerAddon(coresys, self)
self.state: AddonState = AddonState.UNKNOWN
@property
def in_progress(self) -> bool:
"""Return True if a task is in progress."""
return self.instance.in_progress
async def load(self) -> None:
"""Async initialize of object."""
with suppress(DockerAPIError):
await self.instance.attach(tag=self.version)
# Evaluate state
if await self.instance.is_running():
self.state = AddonState.STARTED
else:
self.state = AddonState.STOPPED
@property
def ip_address(self) -> IPv4Address:
"""Return IP of add-on instance."""
@@ -154,6 +178,21 @@ class Addon(AddonModel):
"""Set auto update."""
self.persist[ATTR_AUTO_UPDATE] = value
@property
def watchdog(self) -> bool:
"""Return True if watchdog is enable."""
return self.persist[ATTR_WATCHDOG]
@watchdog.setter
def watchdog(self, value: bool) -> None:
"""Set watchdog enable/disable."""
if value and self.startup == AddonStartup.ONCE:
_LOGGER.warning(
"Ignoring watchdog for %s because startup type is 'once'", self.slug
)
else:
self.persist[ATTR_WATCHDOG] = value
@property
def uuid(self) -> str:
"""Return an API token for this add-on."""
@@ -229,8 +268,6 @@ class Addon(AddonModel):
if not url:
return None
webui = RE_WEBUI.match(url)
if not webui:
return None
# extract arguments
t_port = webui.group("t_port")
@@ -244,10 +281,6 @@ class Addon(AddonModel):
else:
port = self.ports.get(f"{t_port}/tcp", t_port)
# for interface config or port lists
if isinstance(port, (tuple, list)):
port = port[-1]
# lookup the correct protocol from config
if t_proto:
proto = "https" if self.options.get(t_proto) else "http"
@@ -352,13 +385,55 @@ class Addon(AddonModel):
"""Save data of add-on."""
self.sys_addons.data.save_data()
async def watchdog_application(self) -> bool:
"""Return True if application is running."""
url = super().watchdog
if not url:
return True
application = RE_WATCHDOG.match(url)
# extract arguments
t_port = application.group("t_port")
t_proto = application.group("t_proto")
s_prefix = application.group("s_prefix") or ""
s_suffix = application.group("s_suffix") or ""
# search host port for this docker port
if self.host_network:
port = self.ports.get(f"{t_port}/tcp", t_port)
else:
port = t_port
# TCP monitoring
if s_prefix == "tcp":
return await self.sys_run_in_executor(check_port, self.ip_address, port)
# lookup the correct protocol from config
if t_proto:
proto = "https" if self.options.get(t_proto) else "http"
else:
proto = s_prefix
# Make HTTP request
try:
url = f"{proto}://{self.ip_address}:{port}{s_suffix}"
async with self.sys_websession_ssl.get(
url, timeout=WATCHDOG_TIMEOUT
) as req:
if req.status < 300:
return True
except (asyncio.TimeoutError, aiohttp.ClientError):
pass
return False
async def write_options(self) -> None:
"""Return True if add-on options is written to data."""
schema = self.schema
options = self.options
# Update secrets for validation
await self.sys_secrets.reload()
await self.sys_homeassistant.secrets.reload()
try:
options = schema(options)
@@ -375,7 +450,7 @@ class Addon(AddonModel):
_LOGGER.debug("Add-on %s write options: %s", self.slug, options)
return
raise AddonsError()
raise AddonConfigurationError()
async def remove_data(self) -> None:
"""Remove add-on data."""
@@ -461,12 +536,6 @@ class Addon(AddonModel):
return False
return True
async def state(self) -> str:
"""Return running state of add-on."""
if await self.instance.is_running():
return STATE_STARTED
return STATE_STOPPED
async def start(self) -> None:
"""Set options and start add-on."""
if await self.instance.is_running():
@@ -487,15 +556,21 @@ class Addon(AddonModel):
# Start Add-on
try:
await self.instance.run()
except DockerAPIError:
raise AddonsError() from None
except DockerAPIError as err:
self.state = AddonState.ERROR
raise AddonsError(err) from None
else:
self.state = AddonState.STARTED
async def stop(self) -> None:
"""Stop add-on."""
try:
return await self.instance.stop()
except DockerAPIError:
raise AddonsError() from None
except DockerAPIError as err:
self.state = AddonState.ERROR
raise AddonsError() from err
else:
self.state = AddonState.STOPPED
async def restart(self) -> None:
"""Restart add-on."""
@@ -510,12 +585,19 @@ class Addon(AddonModel):
"""
return self.instance.logs()
def is_running(self) -> Awaitable[bool]:
"""Return True if Docker container is running.
Return a coroutine.
"""
return self.instance.is_running()
async def stats(self) -> DockerStats:
"""Return stats of container."""
try:
return await self.instance.stats()
except DockerAPIError:
raise AddonsError() from None
except DockerAPIError as err:
raise AddonsError() from err
async def write_stdin(self, data) -> None:
"""Write data to add-on stdin.
@@ -528,8 +610,8 @@ class Addon(AddonModel):
try:
return await self.instance.write_stdin(data)
except DockerAPIError:
raise AddonsError() from None
except DockerAPIError as err:
raise AddonsError() from err
async def snapshot(self, tar_file: tarfile.TarFile) -> None:
"""Snapshot state of an add-on."""
@@ -540,31 +622,31 @@ class Addon(AddonModel):
if self.need_build:
try:
await self.instance.export_image(temp_path.joinpath("image.tar"))
except DockerAPIError:
raise AddonsError() from None
except DockerAPIError as err:
raise AddonsError() from err
data = {
ATTR_USER: self.persist,
ATTR_SYSTEM: self.data,
ATTR_VERSION: self.version,
ATTR_STATE: await self.state(),
ATTR_STATE: self.state,
}
# Store local configs/state
try:
write_json_file(temp_path.joinpath("addon.json"), data)
except JsonFileError:
except JsonFileError as err:
_LOGGER.error("Can't save meta for %s", self.slug)
raise AddonsError() from None
raise AddonsError() from err
# Store AppArmor Profile
if self.sys_host.apparmor.exists(self.slug):
profile = temp_path.joinpath("apparmor.txt")
try:
self.sys_host.apparmor.backup_profile(self.slug, profile)
except HostAppArmorError:
except HostAppArmorError as err:
_LOGGER.error("Can't backup AppArmor profile")
raise AddonsError() from None
raise AddonsError() from err
# write into tarfile
def _write_tarfile():
@@ -587,7 +669,7 @@ class Addon(AddonModel):
await self.sys_run_in_executor(_write_tarfile)
except (tarfile.TarError, OSError) as err:
_LOGGER.error("Can't write tarfile %s: %s", tar_file, err)
raise AddonsError() from None
raise AddonsError() from err
_LOGGER.info("Finish snapshot for addon %s", self.slug)
@@ -604,13 +686,13 @@ class Addon(AddonModel):
await self.sys_run_in_executor(_extract_tarfile)
except tarfile.TarError as err:
_LOGGER.error("Can't read tarfile %s: %s", tar_file, err)
raise AddonsError() from None
raise AddonsError() from err
# Read snapshot data
try:
data = read_json_file(Path(temp, "addon.json"))
except JsonFileError:
raise AddonsError() from None
except JsonFileError as err:
raise AddonsError() from err
# Validate
try:
@@ -621,7 +703,7 @@ class Addon(AddonModel):
self.slug,
humanize_error(data, err),
)
raise AddonsError() from None
raise AddonsError() from err
# If available
if not self._available(data[ATTR_SYSTEM]):
@@ -668,21 +750,21 @@ class Addon(AddonModel):
await self.sys_run_in_executor(_restore_data)
except shutil.Error as err:
_LOGGER.error("Can't restore origin data: %s", err)
raise AddonsError() from None
raise AddonsError() from err
# Restore AppArmor
profile_file = Path(temp, "apparmor.txt")
if profile_file.exists():
try:
await self.sys_host.apparmor.load_profile(self.slug, profile_file)
except HostAppArmorError:
except HostAppArmorError as err:
_LOGGER.error(
"Can't restore AppArmor profile for add-on %s", self.slug
)
raise AddonsError() from None
raise AddonsError() from err
# Run add-on
if data[ATTR_STATE] == STATE_STARTED:
if data[ATTR_STATE] == AddonState.STARTED:
return await self.start()
_LOGGER.info("Finish restore for add-on %s", self.slug)

View File

@@ -58,13 +58,15 @@ from ..const import (
ATTR_TMPFS,
ATTR_UDEV,
ATTR_URL,
ATTR_USB,
ATTR_VERSION,
ATTR_VIDEO,
ATTR_WATCHDOG,
ATTR_WEBUI,
SECURITY_DEFAULT,
SECURITY_DISABLE,
SECURITY_PROFILE,
AddonStages,
AddonStage,
AddonStartup,
)
from ..coresys import CoreSys, CoreSysAttributes
@@ -205,7 +207,7 @@ class AddonModel(CoreSysAttributes, ABC):
return self.data[ATTR_ADVANCED]
@property
def stage(self) -> AddonStages:
def stage(self) -> AddonStage:
"""Return stage mode of add-on."""
return self.data[ATTR_STAGE]
@@ -247,6 +249,11 @@ class AddonModel(CoreSysAttributes, ABC):
"""Return URL to webui or None."""
return self.data.get(ATTR_WEBUI)
@property
def watchdog(self) -> Optional[str]:
"""Return URL to for watchdog or None."""
return self.data.get(ATTR_WATCHDOG)
@property
def ingress_port(self) -> Optional[int]:
"""Return Ingress port."""
@@ -292,11 +299,6 @@ class AddonModel(CoreSysAttributes, ABC):
"""Return devices of add-on."""
return self.data.get(ATTR_DEVICES, [])
@property
def auto_uart(self) -> bool:
"""Return True if we should map all UART device."""
return self.data[ATTR_AUTO_UART]
@property
def tmpfs(self) -> Optional[str]:
"""Return tmpfs of add-on."""
@@ -376,6 +378,16 @@ class AddonModel(CoreSysAttributes, ABC):
"""Return True if the add-on access to GPIO interface."""
return self.data[ATTR_GPIO]
@property
def with_usb(self) -> bool:
"""Return True if the add-on need USB access."""
return self.data[ATTR_USB]
@property
def with_uart(self) -> bool:
"""Return True if we should map all UART device."""
return self.data[ATTR_AUTO_UART]
@property
def with_udev(self) -> bool:
"""Return True if the add-on have his own udev."""

View File

@@ -75,26 +75,27 @@ from ..const import (
ATTR_TMPFS,
ATTR_UDEV,
ATTR_URL,
ATTR_USB,
ATTR_USER,
ATTR_UUID,
ATTR_VERSION,
ATTR_VIDEO,
ATTR_WATCHDOG,
ATTR_WEBUI,
BOOT_AUTO,
BOOT_MANUAL,
PRIVILEGED_ALL,
ROLE_ALL,
ROLE_DEFAULT,
STATE_STARTED,
STATE_STOPPED,
AddonStages,
AddonStage,
AddonStartup,
AddonState,
)
from ..coresys import CoreSys
from ..discovery.validate import valid_discovery_service
from ..validate import (
DOCKER_PORTS,
DOCKER_PORTS_DESCRIPTION,
docker_ports,
docker_ports_description,
network_port,
token,
uuid_match,
@@ -195,9 +196,12 @@ SCHEMA_ADDON_CONFIG = vol.Schema(
vol.Required(ATTR_BOOT): vol.In([BOOT_AUTO, BOOT_MANUAL]),
vol.Optional(ATTR_INIT, default=True): vol.Boolean(),
vol.Optional(ATTR_ADVANCED, default=False): vol.Boolean(),
vol.Optional(ATTR_STAGE, default=AddonStages.STABLE): vol.Coerce(AddonStages),
vol.Optional(ATTR_PORTS): DOCKER_PORTS,
vol.Optional(ATTR_PORTS_DESCRIPTION): DOCKER_PORTS_DESCRIPTION,
vol.Optional(ATTR_STAGE, default=AddonStage.STABLE): vol.Coerce(AddonStage),
vol.Optional(ATTR_PORTS): docker_ports,
vol.Optional(ATTR_PORTS_DESCRIPTION): docker_ports_description,
vol.Optional(ATTR_WATCHDOG): vol.Match(
r"^(?:https?|\[PROTO:\w+\]|tcp):\/\/\[HOST\]:\[PORT:\d+\].*$"
),
vol.Optional(ATTR_WEBUI): vol.Match(
r"^(?:https?|\[PROTO:\w+\]):\/\/\[HOST\]:\[PORT:\d+\].*$"
),
@@ -226,6 +230,7 @@ SCHEMA_ADDON_CONFIG = vol.Schema(
vol.Optional(ATTR_AUDIO, default=False): vol.Boolean(),
vol.Optional(ATTR_VIDEO, default=False): vol.Boolean(),
vol.Optional(ATTR_GPIO, default=False): vol.Boolean(),
vol.Optional(ATTR_USB, default=False): vol.Boolean(),
vol.Optional(ATTR_DEVICETREE, default=False): vol.Boolean(),
vol.Optional(ATTR_KERNEL_MODULES, default=False): vol.Boolean(),
vol.Optional(ATTR_HASSIO_API, default=False): vol.Boolean(),
@@ -299,11 +304,12 @@ SCHEMA_ADDON_USER = vol.Schema(
vol.Optional(ATTR_OPTIONS, default=dict): dict,
vol.Optional(ATTR_AUTO_UPDATE, default=False): vol.Boolean(),
vol.Optional(ATTR_BOOT): vol.In([BOOT_AUTO, BOOT_MANUAL]),
vol.Optional(ATTR_NETWORK): DOCKER_PORTS,
vol.Optional(ATTR_NETWORK): docker_ports,
vol.Optional(ATTR_AUDIO_OUTPUT): vol.Maybe(vol.Coerce(str)),
vol.Optional(ATTR_AUDIO_INPUT): vol.Maybe(vol.Coerce(str)),
vol.Optional(ATTR_PROTECTED, default=True): vol.Boolean(),
vol.Optional(ATTR_INGRESS_PANEL, default=False): vol.Boolean(),
vol.Optional(ATTR_WATCHDOG, default=False): vol.Boolean(),
},
extra=vol.REMOVE_EXTRA,
)
@@ -329,7 +335,7 @@ SCHEMA_ADDON_SNAPSHOT = vol.Schema(
{
vol.Required(ATTR_USER): SCHEMA_ADDON_USER,
vol.Required(ATTR_SYSTEM): SCHEMA_ADDON_SYSTEM,
vol.Required(ATTR_STATE): vol.In([STATE_STARTED, STATE_STOPPED]),
vol.Required(ATTR_STATE): vol.Coerce(AddonState),
vol.Required(ATTR_VERSION): vol.Coerce(str),
},
extra=vol.REMOVE_EXTRA,
@@ -376,20 +382,20 @@ def _single_validate(coresys: CoreSys, typ: str, value: Any, key: str):
"""Validate a single element."""
# if required argument
if value is None:
raise vol.Invalid(f"Missing required option '{key}'")
raise vol.Invalid(f"Missing required option '{key}'") from None
# Lookup secret
if str(value).startswith("!secret "):
secret: str = value.partition(" ")[2]
value = coresys.secrets.get(secret)
value = coresys.homeassistant.secrets.get(secret)
if value is None:
raise vol.Invalid(f"Unknown secret {secret}")
raise vol.Invalid(f"Unknown secret {secret}") from None
# parse extend data from type
match = RE_SCHEMA_ELEMENT.match(typ)
if not match:
raise vol.Invalid(f"Unknown type {typ}")
raise vol.Invalid(f"Unknown type {typ}") from None
# prepare range
range_args = {}
@@ -417,13 +423,18 @@ def _single_validate(coresys: CoreSys, typ: str, value: Any, key: str):
elif typ.startswith(V_LIST):
return vol.In(match.group("list").split("|"))(str(value))
raise vol.Invalid(f"Fatal error for {key} type {typ}")
raise vol.Invalid(f"Fatal error for {key} type {typ}") from None
def _nested_validate_list(coresys, typ, data_list, key):
"""Validate nested items."""
options = []
# Make sure it is a list
if not isinstance(data_list, list):
raise vol.Invalid(f"Invalid list for {key}") from None
# Process list
for element in data_list:
# Nested?
if isinstance(typ, dict):
@@ -439,6 +450,11 @@ def _nested_validate_dict(coresys, typ, data_dict, key):
"""Validate nested items."""
options = {}
# Make sure it is a dict
if not isinstance(data_dict, dict):
raise vol.Invalid(f"Invalid dict for {key}") from None
# Process dict
for c_key, c_value in data_dict.items():
# Ignore unknown options / remove from list
if c_key not in typ:
@@ -463,7 +479,7 @@ def _check_missing_options(origin, exists, root):
for miss_opt in missing:
if isinstance(origin[miss_opt], str) and origin[miss_opt].endswith("?"):
continue
raise vol.Invalid(f"Missing option {miss_opt} in {root}")
raise vol.Invalid(f"Missing option {miss_opt} in {root}") from None
def schema_ui_options(raw_schema: Dict[str, Any]) -> List[Dict[str, Any]]:

View File

@@ -18,6 +18,7 @@ from .host import APIHost
from .info import APIInfo
from .ingress import APIIngress
from .multicast import APIMulticast
from .network import APINetwork
from .os import APIOS
from .proxy import APIProxy
from .security import SecurityMiddleware
@@ -54,6 +55,7 @@ class RestAPI(CoreSysAttributes):
self._register_os()
self._register_cli()
self._register_multicast()
self._register_network()
self._register_hardware()
self._register_homeassistant()
self._register_proxy()
@@ -89,6 +91,24 @@ class RestAPI(CoreSysAttributes):
]
)
def _register_network(self) -> None:
"""Register network functions."""
api_network = APINetwork()
api_network.coresys = self.coresys
self.webapp.add_routes(
[
web.get("/network/info", api_network.info),
web.get(
"/network/interface/{interface}/info", api_network.interface_info
),
web.post(
"/network/interface/{interface}/update",
api_network.interface_update,
),
]
)
def _register_os(self) -> None:
"""Register OS functions."""
api_os = APIOS()
@@ -248,6 +268,9 @@ class RestAPI(CoreSysAttributes):
web.post("/addons/{addon}/restart", api_addons.restart),
web.post("/addons/{addon}/update", api_addons.update),
web.post("/addons/{addon}/options", api_addons.options),
web.post(
"/addons/{addon}/options/validate", api_addons.options_validate
),
web.post("/addons/{addon}/rebuild", api_addons.rebuild),
web.get("/addons/{addon}/logs", api_addons.logs),
web.get("/addons/{addon}/icon", api_addons.icon),

View File

@@ -5,6 +5,7 @@ from typing import Any, Awaitable, Dict, List
from aiohttp import web
import voluptuous as vol
from voluptuous.humanize import humanize_error
from ..addons import AnyAddon
from ..addons.addon import Addon
@@ -61,6 +62,7 @@ from ..const import (
ATTR_MEMORY_LIMIT,
ATTR_MEMORY_PERCENT,
ATTR_MEMORY_USAGE,
ATTR_MESSAGE,
ATTR_NAME,
ATTR_NETWORK,
ATTR_NETWORK_DESCRIPTION,
@@ -77,13 +79,17 @@ from ..const import (
ATTR_SLUG,
ATTR_SOURCE,
ATTR_STAGE,
ATTR_STARTUP,
ATTR_STATE,
ATTR_STDIN,
ATTR_UDEV,
ATTR_URL,
ATTR_USB,
ATTR_VALID,
ATTR_VERSION,
ATTR_VERSION_LATEST,
ATTR_VIDEO,
ATTR_WATCHDOG,
ATTR_WEBUI,
BOOT_AUTO,
BOOT_MANUAL,
@@ -91,12 +97,12 @@ from ..const import (
CONTENT_TYPE_PNG,
CONTENT_TYPE_TEXT,
REQUEST_FROM,
STATE_NONE,
AddonState,
)
from ..coresys import CoreSysAttributes
from ..docker.stats import DockerStats
from ..exceptions import APIError
from ..validate import DOCKER_PORTS
from ..validate import docker_ports
from .utils import api_process, api_process_raw, api_validate
_LOGGER: logging.Logger = logging.getLogger(__name__)
@@ -107,11 +113,12 @@ SCHEMA_VERSION = vol.Schema({vol.Optional(ATTR_VERSION): vol.Coerce(str)})
SCHEMA_OPTIONS = vol.Schema(
{
vol.Optional(ATTR_BOOT): vol.In([BOOT_AUTO, BOOT_MANUAL]),
vol.Optional(ATTR_NETWORK): vol.Maybe(DOCKER_PORTS),
vol.Optional(ATTR_NETWORK): vol.Maybe(docker_ports),
vol.Optional(ATTR_AUTO_UPDATE): vol.Boolean(),
vol.Optional(ATTR_AUDIO_OUTPUT): vol.Maybe(vol.Coerce(str)),
vol.Optional(ATTR_AUDIO_INPUT): vol.Maybe(vol.Coerce(str)),
vol.Optional(ATTR_INGRESS_PANEL): vol.Boolean(),
vol.Optional(ATTR_WATCHDOG): vol.Boolean(),
}
)
@@ -212,7 +219,7 @@ class APIAddons(CoreSysAttributes):
ATTR_MACHINE: addon.supported_machine,
ATTR_HOMEASSISTANT: addon.homeassistant_version,
ATTR_URL: addon.url,
ATTR_STATE: STATE_NONE,
ATTR_STATE: AddonState.UNKNOWN,
ATTR_DETACHED: addon.is_detached,
ATTR_AVAILABLE: addon.available,
ATTR_BUILD: addon.need_build,
@@ -237,6 +244,7 @@ class APIAddons(CoreSysAttributes):
ATTR_AUTH_API: addon.access_auth_api,
ATTR_HOMEASSISTANT_API: addon.access_homeassistant_api,
ATTR_GPIO: addon.with_gpio,
ATTR_USB: addon.with_usb,
ATTR_KERNEL_MODULES: addon.with_kernel_modules,
ATTR_DEVICETREE: addon.with_devicetree,
ATTR_UDEV: addon.with_udev,
@@ -245,6 +253,7 @@ class APIAddons(CoreSysAttributes):
ATTR_AUDIO: addon.with_audio,
ATTR_AUDIO_INPUT: None,
ATTR_AUDIO_OUTPUT: None,
ATTR_STARTUP: addon.startup,
ATTR_SERVICES: _pretty_services(addon),
ATTR_DISCOVERY: addon.discovery,
ATTR_IP_ADDRESS: None,
@@ -253,12 +262,13 @@ class APIAddons(CoreSysAttributes):
ATTR_INGRESS_URL: None,
ATTR_INGRESS_PORT: None,
ATTR_INGRESS_PANEL: None,
ATTR_WATCHDOG: None,
}
if isinstance(addon, Addon) and addon.is_installed:
data.update(
{
ATTR_STATE: await addon.state(),
ATTR_STATE: addon.state,
ATTR_WEBUI: addon.webui,
ATTR_INGRESS_ENTRY: addon.ingress_entry,
ATTR_INGRESS_URL: addon.ingress_url,
@@ -269,6 +279,7 @@ class APIAddons(CoreSysAttributes):
ATTR_AUTO_UPDATE: addon.auto_update,
ATTR_IP_ADDRESS: str(addon.ip_address),
ATTR_VERSION: addon.version,
ATTR_WATCHDOG: addon.watchdog,
}
)
@@ -280,7 +291,7 @@ class APIAddons(CoreSysAttributes):
addon = self._extract_addon_installed(request)
# Update secrets for validation
await self.sys_secrets.reload()
await self.sys_homeassistant.secrets.reload()
# Extend schema with add-on specific validation
addon_schema = SCHEMA_OPTIONS.extend(
@@ -304,9 +315,24 @@ class APIAddons(CoreSysAttributes):
if ATTR_INGRESS_PANEL in body:
addon.ingress_panel = body[ATTR_INGRESS_PANEL]
await self.sys_ingress.update_hass_panel(addon)
if ATTR_WATCHDOG in body:
addon.watchdog = body[ATTR_WATCHDOG]
addon.save_persist()
@api_process
async def options_validate(self, request: web.Request) -> None:
"""Validate user options for add-on."""
addon = self._extract_addon_installed(request)
data = {ATTR_MESSAGE: "", ATTR_VALID: True}
try:
addon.schema(addon.options)
except vol.Invalid as ex:
data[ATTR_MESSAGE] = humanize_error(addon.options, ex)
data[ATTR_VALID] = False
return data
@api_process
async def security(self, request: web.Request) -> None:
"""Store security options for add-on."""

View File

@@ -1,7 +1,7 @@
"""Init file for Supervisor hardware RESTful API."""
import asyncio
import logging
from typing import Any, Awaitable, Dict
from typing import Any, Awaitable, Dict, List
from aiohttp import web
@@ -12,6 +12,7 @@ from ..const import (
ATTR_INPUT,
ATTR_OUTPUT,
ATTR_SERIAL,
ATTR_USB,
)
from ..coresys import CoreSysAttributes
from .utils import api_process
@@ -25,13 +26,24 @@ class APIHardware(CoreSysAttributes):
@api_process
async def info(self, request: web.Request) -> Dict[str, Any]:
"""Show hardware info."""
serial: List[str] = []
# Create Serial list with device links
for device in self.sys_hardware.serial_devices:
serial.append(device.path.as_posix())
for link in device.links:
serial.append(link.as_posix())
return {
ATTR_SERIAL: list(
self.sys_hardware.serial_devices | self.sys_hardware.serial_by_id
),
ATTR_SERIAL: serial,
ATTR_INPUT: list(self.sys_hardware.input_devices),
ATTR_DISK: list(self.sys_hardware.disk_devices),
ATTR_DISK: [
device.path.as_posix() for device in self.sys_hardware.disk_devices
],
ATTR_GPIO: list(self.sys_hardware.gpio_devices),
ATTR_USB: [
device.path.as_posix() for device in self.sys_hardware.usb_devices
],
ATTR_AUDIO: self.sys_hardware.audio_devices,
}

View File

@@ -117,7 +117,7 @@ class APIHomeAssistant(CoreSysAttributes):
@api_process
async def stats(self, request: web.Request) -> Dict[Any, str]:
"""Return resource information."""
stats = await self.sys_homeassistant.stats()
stats = await self.sys_homeassistant.core.stats()
if not stats:
raise APIError("No stats available")
@@ -138,36 +138,36 @@ class APIHomeAssistant(CoreSysAttributes):
body = await api_validate(SCHEMA_VERSION, request)
version = body.get(ATTR_VERSION, self.sys_homeassistant.latest_version)
await asyncio.shield(self.sys_homeassistant.update(version))
await asyncio.shield(self.sys_homeassistant.core.update(version))
@api_process
def stop(self, request: web.Request) -> Awaitable[None]:
"""Stop Home Assistant."""
return asyncio.shield(self.sys_homeassistant.stop())
return asyncio.shield(self.sys_homeassistant.core.stop())
@api_process
def start(self, request: web.Request) -> Awaitable[None]:
"""Start Home Assistant."""
return asyncio.shield(self.sys_homeassistant.start())
return asyncio.shield(self.sys_homeassistant.core.start())
@api_process
def restart(self, request: web.Request) -> Awaitable[None]:
"""Restart Home Assistant."""
return asyncio.shield(self.sys_homeassistant.restart())
return asyncio.shield(self.sys_homeassistant.core.restart())
@api_process
def rebuild(self, request: web.Request) -> Awaitable[None]:
"""Rebuild Home Assistant."""
return asyncio.shield(self.sys_homeassistant.rebuild())
return asyncio.shield(self.sys_homeassistant.core.rebuild())
@api_process_raw(CONTENT_TYPE_BINARY)
def logs(self, request: web.Request) -> Awaitable[bytes]:
"""Return Home Assistant Docker logs."""
return self.sys_homeassistant.logs()
return self.sys_homeassistant.core.logs()
@api_process
async def check(self, request: web.Request) -> None:
"""Check configuration of Home Assistant."""
result = await self.sys_homeassistant.check_config()
result = await self.sys_homeassistant.core.check_config()
if not result.valid:
raise APIError(result.log)

View File

@@ -1,6 +1,5 @@
"""Init file for Supervisor host RESTful API."""
import asyncio
import logging
from typing import Awaitable
from aiohttp import web
@@ -11,6 +10,9 @@ from ..const import (
ATTR_CPE,
ATTR_DEPLOYMENT,
ATTR_DESCRIPTON,
ATTR_DISK_FREE,
ATTR_DISK_TOTAL,
ATTR_DISK_USED,
ATTR_FEATURES,
ATTR_HOSTNAME,
ATTR_KERNEL,
@@ -23,8 +25,6 @@ from ..const import (
from ..coresys import CoreSysAttributes
from .utils import api_process, api_process_raw, api_validate
_LOGGER: logging.Logger = logging.getLogger(__name__)
SERVICE = "service"
SCHEMA_OPTIONS = vol.Schema({vol.Optional(ATTR_HOSTNAME): vol.Coerce(str)})
@@ -39,11 +39,14 @@ class APIHost(CoreSysAttributes):
return {
ATTR_CHASSIS: self.sys_host.info.chassis,
ATTR_CPE: self.sys_host.info.cpe,
ATTR_FEATURES: self.sys_host.supperted_features,
ATTR_HOSTNAME: self.sys_host.info.hostname,
ATTR_OPERATING_SYSTEM: self.sys_host.info.operating_system,
ATTR_DEPLOYMENT: self.sys_host.info.deployment,
ATTR_DISK_FREE: self.sys_host.info.free_space,
ATTR_DISK_TOTAL: self.sys_host.info.total_space,
ATTR_DISK_USED: self.sys_host.info.used_space,
ATTR_FEATURES: self.sys_host.supported_features,
ATTR_HOSTNAME: self.sys_host.info.hostname,
ATTR_KERNEL: self.sys_host.info.kernel,
ATTR_OPERATING_SYSTEM: self.sys_host.info.operating_system,
}
@api_process

View File

@@ -14,6 +14,7 @@ from ..const import (
ATTR_LOGGING,
ATTR_MACHINE,
ATTR_SUPERVISOR,
ATTR_SUPPORTED,
ATTR_SUPPORTED_ARCH,
ATTR_TIMEZONE,
)
@@ -38,7 +39,8 @@ class APIInfo(CoreSysAttributes):
ATTR_MACHINE: self.sys_machine,
ATTR_ARCH: self.sys_arch.default,
ATTR_SUPPORTED_ARCH: self.sys_arch.supported,
ATTR_SUPPORTED: self.sys_core.supported,
ATTR_CHANNEL: self.sys_updater.channel,
ATTR_LOGGING: self.sys_config.logging,
ATTR_TIMEZONE: self.sys_timezone,
ATTR_TIMEZONE: self.sys_config.timezone,
}

View File

@@ -104,7 +104,7 @@ class APIIngress(CoreSysAttributes):
except aiohttp.ClientError as err:
_LOGGER.error("Ingress error: %s", err)
raise HTTPBadGateway() from None
raise HTTPBadGateway()
async def _handle_websocket(
self, request: web.Request, addon: Addon, path: str
@@ -191,7 +191,11 @@ class APIIngress(CoreSysAttributes):
async for data in result.content.iter_chunked(4096):
await response.write(data)
except (aiohttp.ClientError, aiohttp.ClientPayloadError) as err:
except (
aiohttp.ClientError,
aiohttp.ClientPayloadError,
ConnectionResetError,
) as err:
_LOGGER.error("Stream error with %s: %s", url, err)
return response

98
supervisor/api/network.py Normal file
View File

@@ -0,0 +1,98 @@
"""REST API for network."""
import asyncio
from typing import Any, Dict
from aiohttp import web
import voluptuous as vol
from ..const import (
ATTR_ADDRESS,
ATTR_DNS,
ATTR_GATEWAY,
ATTR_ID,
ATTR_INTERFACE,
ATTR_INTERFACES,
ATTR_IP_ADDRESS,
ATTR_METHOD,
ATTR_METHODS,
ATTR_NAMESERVERS,
ATTR_PRIMARY,
ATTR_TYPE,
)
from ..coresys import CoreSysAttributes
from ..dbus.const import InterfaceMethodSimple
from ..dbus.network.interface import NetworkInterface
from ..dbus.network.utils import int2ip
from ..exceptions import APIError
from .utils import api_process, api_validate
SCHEMA_UPDATE = vol.Schema(
{
vol.Optional(ATTR_ADDRESS): vol.Coerce(str),
vol.Optional(ATTR_METHOD): vol.In(ATTR_METHODS),
vol.Optional(ATTR_GATEWAY): vol.Coerce(str),
vol.Optional(ATTR_DNS): [str],
}
)
def interface_information(interface: NetworkInterface) -> dict:
"""Return a dict with information of a interface to be used in th API."""
return {
ATTR_IP_ADDRESS: f"{interface.ip_address}/{interface.prefix}",
ATTR_GATEWAY: interface.gateway,
ATTR_ID: interface.id,
ATTR_TYPE: interface.type,
ATTR_NAMESERVERS: [int2ip(x) for x in interface.nameservers],
ATTR_METHOD: InterfaceMethodSimple.DHCP
if interface.method == "auto"
else InterfaceMethodSimple.STATIC,
ATTR_PRIMARY: interface.primary,
}
class APINetwork(CoreSysAttributes):
"""Handle REST API for network."""
@api_process
async def info(self, request: web.Request) -> Dict[str, Any]:
"""Return network information."""
interfaces = {}
for interface in self.sys_host.network.interfaces:
interfaces[
self.sys_host.network.interfaces[interface].name
] = interface_information(self.sys_host.network.interfaces[interface])
return {ATTR_INTERFACES: interfaces}
@api_process
async def interface_info(self, request: web.Request) -> Dict[str, Any]:
"""Return network information for a interface."""
req_interface = request.match_info.get(ATTR_INTERFACE)
for interface in self.sys_host.network.interfaces:
if req_interface == self.sys_host.network.interfaces[interface].name:
return interface_information(
self.sys_host.network.interfaces[interface]
)
return {}
@api_process
async def interface_update(self, request: web.Request) -> Dict[str, Any]:
"""Update the configuration of an interface."""
req_interface = request.match_info.get(ATTR_INTERFACE)
if not self.sys_host.network.interfaces.get(req_interface):
raise APIError(f"Interface {req_interface} does not exsist")
args = await api_validate(SCHEMA_UPDATE, request)
if not args:
raise APIError("You need to supply at least one option to update")
await asyncio.shield(
self.sys_host.network.interfaces[req_interface].update_settings(**args)
)
await asyncio.shield(self.sys_host.reload())
return await asyncio.shield(self.interface_info(request))

View File

@@ -1,9 +1,9 @@
try {
new Function("import('/api/hassio/app/frontend_latest/entrypoint.a14fe829.js')")();
new Function("import('/api/hassio/app/frontend_latest/entrypoint.e3f59ee9.js')")();
} catch (err) {
var el = document.createElement('script');
el.src = '/api/hassio/app/frontend_es5/entrypoint.24698450.js';
el.src = '/api/hassio/app/frontend_es5/entrypoint.6086a806.js';
document.body.appendChild(el);
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
{"version":3,"file":"chunk.0a106e488ed654ffce49.js","sources":["webpack:///chunk.0a106e488ed654ffce49.js"],"mappings":"AAAA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
{"version":3,"file":"chunk.1edd93e4d4c4749e55ab.js","sources":["webpack:///chunk.1edd93e4d4c4749e55ab.js"],"mappings":"AAAA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
{"version":3,"file":"chunk.2b590ee397502865577d.js","sources":["webpack:///chunk.2b590ee397502865577d.js"],"mappings":"AAAA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
{"version":3,"file":"chunk.2b6daa2d9335da34f364.js","sources":["webpack:///chunk.2b6daa2d9335da34f364.js"],"mappings":"AAAA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@@ -1,10 +1,3 @@
/*!
* The buffer module from node.js, for the browser.
*
* @author Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
* @license MIT
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.

View File

@@ -0,0 +1 @@
{"version":3,"file":"chunk.2d61d2e5cad84705d92e.js","sources":["webpack:///chunk.2d61d2e5cad84705d92e.js"],"mappings":";AAAA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
{"version":3,"file":"chunk.41e6f35d848b66d50d12.js","sources":["webpack:///chunk.41e6f35d848b66d50d12.js"],"mappings":"AAAA","sourceRoot":""}

View File

@@ -1,2 +0,0 @@
(self.webpackJsonp=self.webpackJsonp||[]).push([[1],{168:function(e,r,n){"use strict";n.r(r),n.d(r,"codeMirror",(function(){return c})),n.d(r,"codeMirrorCss",(function(){return i}));var a=n(127),o=n.n(a),s=n(164),t=(n(165),n(166),n(8));o.a.commands.save=function(e){Object(t.a)(e.getWrapperElement(),"editor-save")};var c=o.a,i=s.a}}]);
//# sourceMappingURL=chunk.53ba85527e846b37953a.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"chunk.53ba85527e846b37953a.js","sources":["webpack:///chunk.53ba85527e846b37953a.js"],"mappings":"AAAA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
{"version":3,"file":"chunk.67ebcbc4ba9d39e9c2b9.js","sources":["webpack:///chunk.67ebcbc4ba9d39e9c2b9.js"],"mappings":"AAAA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
{"version":3,"file":"chunk.6abfd4fdbd7486588838.js","sources":["webpack:///chunk.6abfd4fdbd7486588838.js"],"mappings":"AAAA","sourceRoot":""}

View File

@@ -0,0 +1 @@
{"version":3,"file":"chunk.6d5d163209aca7b3c446.js","sources":["webpack:///chunk.6d5d163209aca7b3c446.js"],"mappings":"AAAA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
{"version":3,"file":"chunk.937f66fb08e0d4aa8818.js","sources":["webpack:///chunk.937f66fb08e0d4aa8818.js"],"mappings":"AAAA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
{"version":3,"file":"chunk.9de6943cce77c49b4586.js","sources":["webpack:///chunk.9de6943cce77c49b4586.js"],"mappings":";AAAA","sourceRoot":""}

View File

@@ -1 +0,0 @@
{"version":3,"file":"chunk.adad578706ef56ae55ba.js","sources":["webpack:///chunk.adad578706ef56ae55ba.js"],"mappings":"AAAA","sourceRoot":""}

View File

@@ -0,0 +1,2 @@
(self.webpackJsonp=self.webpackJsonp||[]).push([[1],{175:function(e,r,n){"use strict";n.r(r),n.d(r,"codeMirror",(function(){return c})),n.d(r,"codeMirrorCss",(function(){return i}));var a=n(164),o=n.n(a),s=n(171),t=(n(172),n(173),n(11));o.a.commands.save=function(e){Object(t.a)(e.getWrapperElement(),"editor-save")};var c=o.a,i=s.a}}]);
//# sourceMappingURL=chunk.bb770cb9ffe5dd250148.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"chunk.bb770cb9ffe5dd250148.js","sources":["webpack:///chunk.bb770cb9ffe5dd250148.js"],"mappings":"AAAA","sourceRoot":""}

View File

@@ -0,0 +1 @@
{"version":3,"file":"chunk.cddd872dbf23dca66986.js","sources":["webpack:///chunk.cddd872dbf23dca66986.js"],"mappings":"AAAA","sourceRoot":""}

View File

@@ -1 +0,0 @@
{"version":3,"file":"chunk.d3a71177023db5bc7440.js","sources":["webpack:///chunk.d3a71177023db5bc7440.js"],"mappings":"AAAA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
{"version":3,"file":"chunk.d7c7c8539f98733903e7.js","sources":["webpack:///chunk.d7c7c8539f98733903e7.js"],"mappings":"AAAA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
{"version":3,"file":"chunk.dacd9533a16ebaba94e9.js","sources":["webpack:///chunk.dacd9533a16ebaba94e9.js"],"mappings":"AAAA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
{"version":3,"file":"entrypoint.24698450.js","sources":["webpack:///entrypoint.24698450.js"],"mappings":";AAAA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +1,10 @@
/*!
* The buffer module from node.js, for the browser.
*
* @author Feross Aboukhadijeh <http://feross.org>
* @license MIT
*/
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
@@ -13,6 +20,24 @@ See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/**
* @license
* Copyright 2020 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @license
* Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
@@ -104,6 +129,23 @@ and limitations under the License.
* THE SOFTWARE.
*/
/**
* @license
* Copyright 2018 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @license
* Copyright 2019 Google Inc.

View File

@@ -0,0 +1 @@
{"version":3,"file":"entrypoint.6086a806.js","sources":["webpack:///entrypoint.6086a806.js"],"mappings":";AAAA","sourceRoot":""}

View File

@@ -1,3 +1,3 @@
{
"entrypoint.js": "/api/hassio/app/frontend_es5/entrypoint.24698450.js"
"entrypoint.js": "/api/hassio/app/frontend_es5/entrypoint.6086a806.js"
}

View File

@@ -1 +0,0 @@
{"version":3,"file":"chunk.1dcdd03de4add9e1f326.js","sources":["webpack:///chunk.1dcdd03de4add9e1f326.js"],"mappings":"AAAA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
{"version":3,"file":"chunk.46e2dce2dcad3c8f5df8.js","sources":["webpack:///chunk.46e2dce2dcad3c8f5df8.js"],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoyFA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgrMA","sourceRoot":""}

View File

@@ -1,2 +0,0 @@
(self.webpackJsonp=self.webpackJsonp||[]).push([[1],{163:function(e,r,n){"use strict";n.r(r),n.d(r,"codeMirror",(function(){return c})),n.d(r,"codeMirrorCss",(function(){return i}));var s=n(124),o=n.n(s),t=n(159),a=(n(160),n(161),n(8));o.a.commands.save=e=>{Object(a.a)(e.getWrapperElement(),"editor-save")};const c=o.a,i=t.a}}]);
//# sourceMappingURL=chunk.5066cbaab136d2561122.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"chunk.5066cbaab136d2561122.js","sources":["webpack:///chunk.5066cbaab136d2561122.js"],"mappings":"AAAA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
{"version":3,"file":"chunk.5533de14161bf4721cab.js","sources":["webpack:///chunk.5533de14161bf4721cab.js"],"mappings":"AAAA;AA0OA;AACA;AACA;AANA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgEA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
{"version":3,"file":"chunk.c108fa5618a9a08e44f8.js","sources":["webpack:///chunk.c108fa5618a9a08e44f8.js"],"mappings":"AAAA;;;;AAwNA;AACA;;;AAGA;AACA;AACA;;;;AAIA;AACA;;AAIA;;AAEA;;;AAGA;;AAGA;AACA;;AAEA;;;;AAKA;AACA;;;AAGA;;AAGA;AACA;;AAEA;;;;AAKA;AACA;;;;;AAKA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;;;;AAKA;;;AAGA;;;AAGA;;AAEA;;;AAGA;;;AAGA;AACA;AACA;;;;AAzFA;;;;;;;;;;;;;;;;;;;;;;;;;AA+HA","sourceRoot":""}
{"version":3,"file":"chunk.64b648e525a4fd321c8a.js","sources":["webpack:///chunk.64b648e525a4fd321c8a.js"],"mappings":"AAAA;;;;AAqOA;AACA;;;AAGA;AACA;AACA;;;;AAIA;AACA;;AAIA;;AAEA;;;AAGA;;AAGA;AACA;;AAEA;;;;AAKA;AACA;;;AAGA;;AAGA;AACA;;AAEA;;;;AAKA;AACA;;;;;AAKA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;;;;AAKA;;;AAGA;;;AAGA;;AAEA;;;AAGA;;;AAGA;AACA;AACA;;;;AAzFA;;;;;;;;;;;;;;;;;;;;;;;;;AA+HA","sourceRoot":""}

Some files were not shown because too many files have changed in this diff Show More