exosphere-cli 2.2.0__tar.gz → 2.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/PKG-INFO +3 -2
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/README.md +1 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/pyproject.toml +3 -3
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/objects.py +1 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/providers/api.py +24 -2
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/providers/freebsd.py +19 -1
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/schema/host-report.schema.json +5 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/LICENSE +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/__init__.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/cli.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/commands/__init__.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/commands/config.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/commands/connections.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/commands/host.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/commands/inventory.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/commands/report.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/commands/sudo.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/commands/ui.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/commands/utils.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/commands/version.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/config.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/context.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/data.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/database.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/errors.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/fspaths.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/inventory.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/main.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/migrations.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/pipelining.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/providers/__init__.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/providers/debian.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/providers/factory.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/providers/openbsd.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/providers/redhat.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/repl.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/reporting.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/schema/__init__.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/security.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/setup/__init__.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/setup/detect.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/templates/report.html.j2 +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/templates/report.md.j2 +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/templates/report.txt.j2 +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/ui/__init__.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/ui/app.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/ui/context.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/ui/dashboard.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/ui/elements.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/ui/inventory.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/ui/logs.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/ui/messages.py +0 -0
- {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/ui/style.tcss +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: exosphere-cli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.0
|
|
4
4
|
Summary: CLI/TUI driven patch reporting for remote Unix-like systems.
|
|
5
5
|
Author: Alexandre Gauthier
|
|
6
6
|
Author-email: Alexandre Gauthier <alex@underwares.org>
|
|
@@ -30,8 +30,8 @@ Requires-Dist: rich>=14.1.0
|
|
|
30
30
|
Requires-Dist: textual-serve>=1.1.3 ; extra == 'web'
|
|
31
31
|
Requires-Python: >=3.13
|
|
32
32
|
Project-URL: homepage, https://exosphere.readthedocs.io
|
|
33
|
-
Project-URL: issues, https://github.com/mrdaemon/exosphere/issues
|
|
34
33
|
Project-URL: repository, https://github.com/mrdaemon/exosphere
|
|
34
|
+
Project-URL: issues, https://github.com/mrdaemon/exosphere/issues
|
|
35
35
|
Provides-Extra: web
|
|
36
36
|
Description-Content-Type: text/markdown
|
|
37
37
|
|
|
@@ -70,6 +70,7 @@ or see [the documentation](https://exosphere.readthedocs.io/en/stable/) to get s
|
|
|
70
70
|
- See everything in one spot, at a glance, without complex automation or enterprise
|
|
71
71
|
solutions
|
|
72
72
|
- Does not require Python (or anything else) to be installed on remote systems
|
|
73
|
+
- Parallel operations across hosts with optional SSH pipelining
|
|
73
74
|
- Document based reporting in HTML, text or markdown format
|
|
74
75
|
- JSON output for integration with other tools
|
|
75
76
|
|
|
@@ -33,6 +33,7 @@ or see [the documentation](https://exosphere.readthedocs.io/en/stable/) to get s
|
|
|
33
33
|
- See everything in one spot, at a glance, without complex automation or enterprise
|
|
34
34
|
solutions
|
|
35
35
|
- Does not require Python (or anything else) to be installed on remote systems
|
|
36
|
+
- Parallel operations across hosts with optional SSH pipelining
|
|
36
37
|
- Document based reporting in HTML, text or markdown format
|
|
37
38
|
- JSON output for integration with other tools
|
|
38
39
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "exosphere-cli"
|
|
3
|
-
version = "2.
|
|
3
|
+
version = "2.4.0"
|
|
4
4
|
description = "CLI/TUI driven patch reporting for remote Unix-like systems."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -50,7 +50,7 @@ dev = [
|
|
|
50
50
|
"pytest-json-ctrf>=0.3.5",
|
|
51
51
|
"pytest-mock>=3.14.1",
|
|
52
52
|
"renku-sphinx-theme>=0.5.0",
|
|
53
|
-
"ruff>=0.
|
|
53
|
+
"ruff>=0.15.0",
|
|
54
54
|
"sphinx>=8.2.3,<9.0",
|
|
55
55
|
"sphinx-autobuild>=2024.10.3",
|
|
56
56
|
"sphinx-lint>=1.0.0",
|
|
@@ -74,7 +74,7 @@ issues = "https://github.com/mrdaemon/exosphere/issues"
|
|
|
74
74
|
exosphere = "exosphere.main:main"
|
|
75
75
|
|
|
76
76
|
[build-system]
|
|
77
|
-
requires = ["uv_build>=0.7.19,<0.
|
|
77
|
+
requires = ["uv_build>=0.7.19,<0.11.0"]
|
|
78
78
|
build-backend = "uv_build"
|
|
79
79
|
|
|
80
80
|
[tool.uv.build-backend]
|
|
@@ -205,6 +205,7 @@ class Host:
|
|
|
205
205
|
"flavor": self.flavor,
|
|
206
206
|
"version": self.version,
|
|
207
207
|
"supported": self.supported,
|
|
208
|
+
"stale": self.is_stale,
|
|
208
209
|
"online": self.online,
|
|
209
210
|
"package_manager": self.package_manager,
|
|
210
211
|
"updates": [update.__dict__.copy() for update in self.updates],
|
|
@@ -6,13 +6,23 @@ well as helper functions and decorators to be used by package manager
|
|
|
6
6
|
provider implementations.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
+
import functools
|
|
9
10
|
import logging
|
|
10
11
|
from abc import ABC, abstractmethod
|
|
11
12
|
from collections.abc import Callable
|
|
12
13
|
|
|
13
14
|
from fabric import Connection
|
|
15
|
+
from invoke.exceptions import AuthFailure
|
|
14
16
|
|
|
15
17
|
from exosphere.data import Update
|
|
18
|
+
from exosphere.errors import DataRefreshError
|
|
19
|
+
|
|
20
|
+
_SUDO_AUTH_FAILURE_MESSAGE = (
|
|
21
|
+
"Sudo failed: "
|
|
22
|
+
"Ensure the user is configured with passwordless sudo. "
|
|
23
|
+
"You can use 'exosphere sudo generate' to produce a sudoers snippet for this host. "
|
|
24
|
+
"See: https://exosphere.readthedocs.io/en/stable/connections.html#id1"
|
|
25
|
+
)
|
|
16
26
|
|
|
17
27
|
|
|
18
28
|
def requires_sudo(func: Callable) -> Callable:
|
|
@@ -23,9 +33,21 @@ def requires_sudo(func: Callable) -> Callable:
|
|
|
23
33
|
it requires sudo privileges to execute. You should add it to any
|
|
24
34
|
method that requires elevated privileges, i.e. whenever you are
|
|
25
35
|
using 'cx.sudo()' instead of 'cx.run()'.
|
|
36
|
+
|
|
37
|
+
Additionally, the decorator provides enhanced error handling for
|
|
38
|
+
sudo related failures, presenting a clear message about sudo policies
|
|
39
|
+
and sudoers configuration when an AuthFailure or related exception occurs.
|
|
26
40
|
"""
|
|
27
|
-
|
|
28
|
-
|
|
41
|
+
|
|
42
|
+
@functools.wraps(func)
|
|
43
|
+
def wrapper(*args: object, **kwargs: object) -> object:
|
|
44
|
+
try:
|
|
45
|
+
return func(*args, **kwargs)
|
|
46
|
+
except AuthFailure as e:
|
|
47
|
+
raise DataRefreshError(_SUDO_AUTH_FAILURE_MESSAGE) from e
|
|
48
|
+
|
|
49
|
+
setattr(wrapper, "__requires_sudo", True)
|
|
50
|
+
return wrapper
|
|
29
51
|
|
|
30
52
|
|
|
31
53
|
class PkgManager(ABC):
|
|
@@ -26,6 +26,7 @@ class Pkg(PkgManager):
|
|
|
26
26
|
|
|
27
27
|
SUDOERS_COMMANDS: list[str] | None = [
|
|
28
28
|
"/usr/sbin/pkg update -q",
|
|
29
|
+
"/usr/sbin/pkg audit -qF",
|
|
29
30
|
]
|
|
30
31
|
|
|
31
32
|
def __init__(self) -> None:
|
|
@@ -41,7 +42,11 @@ class Pkg(PkgManager):
|
|
|
41
42
|
"""
|
|
42
43
|
Synchronize the package repository.
|
|
43
44
|
|
|
44
|
-
|
|
45
|
+
It will also download the latest version of the vuln.xml
|
|
46
|
+
database to check for security updates.
|
|
47
|
+
|
|
48
|
+
This method is equivalent to running 'pkg update -q', followed
|
|
49
|
+
by 'pkg audit -qF'.
|
|
45
50
|
|
|
46
51
|
:param cx: Fabric Connection object
|
|
47
52
|
:return: True if synchronization is successful, False otherwise.
|
|
@@ -56,6 +61,19 @@ class Pkg(PkgManager):
|
|
|
56
61
|
)
|
|
57
62
|
return False
|
|
58
63
|
|
|
64
|
+
# Sync vulnerability data via pkg audit -qF, which forces a download
|
|
65
|
+
# of the vuln.xml database. We do not store or parse the output, as
|
|
66
|
+
# the cost of running it a second time in get_updates() is negligible,
|
|
67
|
+
# and will be a local operation anyways.
|
|
68
|
+
self.logger.debug("Synchronizing vuln.xml via pkg audit")
|
|
69
|
+
audit_result = cx.sudo("/usr/sbin/pkg audit -qF", hide=True, warn=True)
|
|
70
|
+
|
|
71
|
+
if audit_result.failed and audit_result.stderr:
|
|
72
|
+
self.logger.error(
|
|
73
|
+
f"Failed to synchronize vuln.xml via pkg audit: {audit_result.stderr}"
|
|
74
|
+
)
|
|
75
|
+
return False
|
|
76
|
+
|
|
59
77
|
self.logger.debug("FreeBSD pkg repositories synchronized successfully")
|
|
60
78
|
|
|
61
79
|
return True
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"flavor",
|
|
20
20
|
"version",
|
|
21
21
|
"supported",
|
|
22
|
+
"stale",
|
|
22
23
|
"online",
|
|
23
24
|
"package_manager",
|
|
24
25
|
"updates",
|
|
@@ -59,6 +60,10 @@
|
|
|
59
60
|
"type": "boolean",
|
|
60
61
|
"description": "Whether this host type is supported by Exosphere"
|
|
61
62
|
},
|
|
63
|
+
"stale": {
|
|
64
|
+
"type": "boolean",
|
|
65
|
+
"description": "Whether the host data is stale and needs refreshed"
|
|
66
|
+
},
|
|
62
67
|
"online": {
|
|
63
68
|
"type": "boolean",
|
|
64
69
|
"description": "Whether the host was last reachable"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|