From 3e3e0c1769f22aee6a3b35637a6cebebd9157148 Mon Sep 17 00:00:00 2001 From: Stephen Mwangi Date: Fri, 17 Apr 2026 18:08:05 +0300 Subject: [PATCH 1/2] refactor: update core/ scripts to use modern snap_http --- README.md | 65 +++++++++++-------- core/attachments/run-attached-script.sh | 3 + core/config/get-conf.py | 12 ++++ core/config/set-conf.py | 21 ++++++ core/interfaces/connect-interface.py | 12 ++-- core/interfaces/disconnect-interface.py | 12 ++-- core/interfaces/get-connections.py | 6 +- core/logs/ftp-client-logs.py | 4 +- core/logs/snapd-logs.py | 20 +----- core/services/enable-service.py | 6 +- core/services/restart-service.py | 8 +++ core/services/restart-services.py | 8 --- core/services/stop-service.py | 6 +- core/snaps/install-snap-debug.sh | 40 ++---------- core/snaps/install-snap.py | 40 +----------- ...p-http-managed-mode.py => managed-mode.py} | 4 +- .../{refresh-snaps.py => refresh-snap.py} | 0 .../{snap-http-remove.py => remove-snap.py} | 2 +- core/snaps/run-attached-script.sh | 3 - core/snaps/set-refresh-timer.py | 2 - core/snaps/snap-http-install.py | 5 -- core/snaps/update-snap-config.py | 14 ---- core/users/add-sso-user.py | 4 +- debugging/snap-debug-info.sh | 24 +++---- 24 files changed, 130 insertions(+), 191 deletions(-) create mode 100644 core/attachments/run-attached-script.sh create mode 100644 core/config/get-conf.py create mode 100644 core/config/set-conf.py create mode 100644 core/services/restart-service.py delete mode 100644 core/services/restart-services.py rename core/snaps/{snap-http-managed-mode.py => managed-mode.py} (83%) rename core/snaps/{refresh-snaps.py => refresh-snap.py} (100%) rename core/snaps/{snap-http-remove.py => remove-snap.py} (75%) delete mode 100644 core/snaps/run-attached-script.sh delete mode 100644 core/snaps/snap-http-install.py delete mode 100644 core/snaps/update-snap-config.py diff --git a/README.md b/README.md index cb1b1d2..14cae26 100644 --- a/README.md +++ b/README.md @@ -109,76 +109,89 @@ Do not hesitate to fork this repository, and submit a pull request with your imp ## Landscape for IoT / Ubuntu Core devices -_Note: some scripts may require python3-requests deb (or PyPI requests) in order to function correctly. It is automatically installed in most environments (including the Landscape Client Snap) but that might not always be the case._ +Scripts in this section use the [snap-http library](https://github.com/canonical/snap-http) to interact with the snapd REST API. The library is bundled with landscape-client. ### Snap Management -- **Tutorial**: Install a snap using a python wearfile and dump script output to a temporary file for debugging on the device. +- **Tutorial**: Install a snap and dump script output to a temporary file for debugging on the device. - [**install-snap-debug.sh**](./core/snaps/install-snap-debug.sh) -- **Tutorial**: Install a snap using a python script and the SnapD REST API +- **Tutorial**: Install a snap - [**install-snap.py**](./core/snaps/install-snap.py) -- **Tutorial**: Run an attached python script - - [**run-attached-script.sh**](./core/snaps/run-attached-script.sh) +- **Tutorial**: Remove a snap + - [**remove-snap.py**](./core/snaps/remove-snap.py) -- **Tutorial**: Install a snap using python and the snap-http library - - [**snap-http-install.py**](./core/snaps/snap-http-install.py) +- **Tutorial**: Refresh a snap + - [**refresh-snap.py**](./core/snaps/refresh-snap.py) -- **Tutorial**: Remove a snap using python and the snap-http library - - [**snap-http-remove.py**](./core/snaps/snap-http-remove.py) +- **Tutorial**: Hold snap updates + - [**hold-snaps.py**](./core/snaps/hold-snaps.py) -- **Tutorial**: Update snapd configuration using Python and the snap-http library - - [**update-snap-config.py**](./core/snaps/update-snap-config.py) +- **Tutorial**: Unhold snap updates + - [**unhold-snaps.py**](./core/snaps/unhold-snaps.py) -- **Tutorial**: Set the refresh timer using Python and the snap-http library +- **Tutorial**: Set the refresh timer - [**set-refresh-timer.py**](./core/snaps/set-refresh-timer.py) -- **Tutorial**: Put device into managed mode (disable automatic snap refreshes) - - [**snap-http-remove.py**](./core/snaps/snap-http-managed-mode.py) +- **Tutorial**: Put a device into managed mode (disable automatic snap refreshes) + - [**managed-mode.py**](./core/snaps/managed-mode.py) + +### Snap Configuration + +- **Tutorial**: Get snap configuration + - [**get-conf.py**](./core/config/get-conf.py) + +- **Tutorial**: Set snap configuration + - [**set-conf.py**](./core/config/set-conf.py) ### Snap Services -- **Tutorial**: Enable a snap service using Python and the snap-http library +- **Tutorial**: Enable a snap service - [**enable-service.py**](./core/services/enable-service.py) -- **Tutorial**: Restart a snap service using Python and the snap-http library - - [**restart-services.py**](./core/services/restart-services.py) +- **Tutorial**: Restart a snap service + - [**restart-service.py**](./core/services/restart-service.py) -- **Tutorial**: Stop a snap service using Python and the snap-http library +- **Tutorial**: Stop a snap service - [**stop-service.py**](./core/services/stop-service.py) ### Snap Interfaces -- **Tutorial**: Connect a snap interface using Python and the snap-http library +- **Tutorial**: Connect a snap interface - [**connect-interface.py**](./core/interfaces/connect-interface.py) -- **Tutorial**: Disconnect a snap interface using Python and the snap-http library +- **Tutorial**: Disconnect a snap interface - [**disconnect-interface.py**](./core/interfaces/disconnect-interface.py) -- **Tutorial**: Get interface connections using Python and the snap-http library +- **Tutorial**: Get interface connections - [**get-connections.py**](./core/interfaces/get-connections.py) ### Logs -- **Tutorial**: Display logs for all snaps using Python and the snap-http library +- **Tutorial**: Display logs for a snap - [**snapd-logs.py**](./core/logs/snapd-logs.py) -- **Tutorial**: Push landscape-client's logs to an FTP server using Python +- **Tutorial**: Push landscape-client's logs to an FTP server - [**ftp-client-logs.py**](./core/logs/ftp-client-logs.py) ### User Management -- **Tutorial**: Add an Ubuntu SSO user to a Core device using Python and the snap-http library +- **Tutorial**: Add an Ubuntu SSO user to a Core device - [**add-sso-user.py**](./core/users/add-sso-user.py) -- **Tutorial**: Add a system user to a Core device using Python and the snap-http library +- **Tutorial**: Add a system user to a Core device - [**add-system-user.py**](./core/users/add-system-user.py) - **Tutorial**: Remove a user from a Core device - [**remove-user.py**](./core/users/remove-user.py) +### Attachments + +- **Tutorial**: Run an attached script + - [**run-attached-script.sh**](./core/attachments/run-attached-script.sh) + ### Debugging - **Tutorial**: Collect a large amount of SnapD and core system information that could be useful when debugging a system with Canonical Support - - [**snap-debug-info.py**](./debugging/snap-debug-info.sh) + - [**snap-debug-info.sh**](./debugging/snap-debug-info.sh) diff --git a/core/attachments/run-attached-script.sh b/core/attachments/run-attached-script.sh new file mode 100644 index 0000000..43cc62d --- /dev/null +++ b/core/attachments/run-attached-script.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +python3 $LANDSCAPE_ATTACHMENTS/restart-service.py diff --git a/core/config/get-conf.py b/core/config/get-conf.py new file mode 100644 index 0000000..f3c6f54 --- /dev/null +++ b/core/config/get-conf.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python3 +from pprint import pprint + +from landscape.client import snap_http + +# Get all configuration for a snap. +response = snap_http.get_conf("system") +pprint(response.result) + +# Get specific configuration keys. +response = snap_http.get_conf("system", keys=["experimental", "refresh"]) +pprint(response.result) diff --git a/core/config/set-conf.py b/core/config/set-conf.py new file mode 100644 index 0000000..08f5da8 --- /dev/null +++ b/core/config/set-conf.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 + +from landscape.client import snap_http + +# Set configuration. +snap_http.set_conf( + "system", # snap name + { + "experimental.confdb": True, + "experimental.confdb-control": True, + "experimental.remote-device-management": True, + }, +) + +# Unset a configuration option by passing None. +snap_http.set_conf( + "system", + { + "experimental.remote-device-management": None, + }, +) diff --git a/core/interfaces/connect-interface.py b/core/interfaces/connect-interface.py index f7b6429..59eaf89 100644 --- a/core/interfaces/connect-interface.py +++ b/core/interfaces/connect-interface.py @@ -2,11 +2,9 @@ from landscape.client import snap_http -snap_http.http.post( - "/interfaces", - { - "action": "connect", - "slots": [{"slot": "account-control"}], - "plugs": [{"snap": "landscape-client", "plug": "account-control"}], - }, +snap_http.connect_interface( + in_snap="system", + in_slot="account-control", + out_snap="landscape-client", + out_plug="account-control", ) diff --git a/core/interfaces/disconnect-interface.py b/core/interfaces/disconnect-interface.py index 8668fe4..eca472b 100644 --- a/core/interfaces/disconnect-interface.py +++ b/core/interfaces/disconnect-interface.py @@ -2,11 +2,9 @@ from landscape.client import snap_http -snap_http.http.post( - "/interfaces", - { - "action": "disconnect", - "slots": [{"slot": "account-control"}], - "plugs": [{"snap": "landscape-client", "plug": "account-control"}], - }, +snap_http.disconnect_interface( + in_snap="system", + in_slot="account-control", + out_snap="landscape-client", + out_plug="account-control", ) diff --git a/core/interfaces/get-connections.py b/core/interfaces/get-connections.py index 70b9b39..dbb9e9d 100644 --- a/core/interfaces/get-connections.py +++ b/core/interfaces/get-connections.py @@ -3,9 +3,5 @@ from landscape.client import snap_http -response = snap_http.http.get( - "/connections", - query_params={"snap": "landscape-client"}, -) - +response = snap_http.get_connections(snap="landscape-client") pprint(response.result) diff --git a/core/logs/ftp-client-logs.py b/core/logs/ftp-client-logs.py index e9dfa2c..b93f3a1 100644 --- a/core/logs/ftp-client-logs.py +++ b/core/logs/ftp-client-logs.py @@ -4,11 +4,11 @@ SNAP_DATA = os.getenv("SNAP_DATA") -# connect and login to the FTP server +# Connect and login to the FTP server. ftp = FTP("192.168.0.103") ftp.login("username", "password") -# push all the landscape-client logs to the FTP server +# Push all the landscape-client logs to the FTP server. log_files = [ "broker.log", "manager.log", diff --git a/core/logs/snapd-logs.py b/core/logs/snapd-logs.py index 26a314f..d797271 100644 --- a/core/logs/snapd-logs.py +++ b/core/logs/snapd-logs.py @@ -1,21 +1,7 @@ #!/usr/bin/env python3 -import json +from pprint import pprint from landscape.client import snap_http -# get the last 100 log items -response = snap_http.http.get("/logs", query_params={"n": "100"}) - -# print each log item -json_seq_str = response.result.decode() - -log_items = [] -while json_seq_str.find("\x1e") != -1: - start = json_seq_str.find("\x1e") - end = json_seq_str.find("\x1e", start + 1) - - log_items.append(json.loads(json_seq_str[start+1:end])) - - json_seq_str = json_seq_str[end:] - -print(log_items) +response = snap_http.logs(names=["landscape-client"], entries=100) +pprint(response.result) diff --git a/core/services/enable-service.py b/core/services/enable-service.py index d52857e..6abb64d 100644 --- a/core/services/enable-service.py +++ b/core/services/enable-service.py @@ -2,7 +2,7 @@ from landscape.client import snap_http -# make sure you install the test-snapd-service snap (beta) -# published by test-snaps-canonical -# enable the service inside the test-snapd-service snap +# Make sure you install the test-snapd-service snap (beta) +# published by test-snaps-canonical. +# Enable the service inside the test-snapd-service snap. snap_http.start("test-snapd-service.service", enable=True) diff --git a/core/services/restart-service.py b/core/services/restart-service.py new file mode 100644 index 0000000..b2e7852 --- /dev/null +++ b/core/services/restart-service.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 + +from landscape.client import snap_http + +# Make sure you install the test-snapd-service snap (beta) +# published by test-snaps-canonical. +# Restart all services in the test-snapd-service snap. +snap_http.restart("test-snapd-service") diff --git a/core/services/restart-services.py b/core/services/restart-services.py deleted file mode 100644 index 50a1d2c..0000000 --- a/core/services/restart-services.py +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env python3 - -from landscape.client import snap_http - -# make sure you install the test-snapd-service snap (beta) -# published by test-snaps-canonical -# restart all services in the test-snapd-service snap -snap_http.restart("test-snapd-service") diff --git a/core/services/stop-service.py b/core/services/stop-service.py index 4e75d37..303f3e4 100644 --- a/core/services/stop-service.py +++ b/core/services/stop-service.py @@ -2,7 +2,7 @@ from landscape.client import snap_http -# make sure you install the test-snapd-service snap (beta) -# published by test-snaps-canonical -# stop all services in the test-snapd-service snap +# Make sure you install the test-snapd-service snap (beta) +# published by test-snaps-canonical. +# Stop all services in the test-snapd-service snap. snap_http.stop("test-snapd-service") diff --git a/core/snaps/install-snap-debug.sh b/core/snaps/install-snap-debug.sh index 09ca152..9ecd820 100644 --- a/core/snaps/install-snap-debug.sh +++ b/core/snaps/install-snap-debug.sh @@ -1,41 +1,11 @@ #!/bin/bash { python3 - << EOF +from pprint import pprint -import requests -import socket -import json -import pprint +from landscape.client import snap_http -from urllib3.connection import HTTPConnection -from urllib3.connectionpool import HTTPConnectionPool -from requests.adapters import HTTPAdapter - -class SnapdConnection(HTTPConnection): - def __init__(self): - super().__init__("localhost") - - def connect(self): - self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - self.sock.connect("/run/snapd.socket") - -class SnapdConnectionPool(HTTPConnectionPool): - def __init__(self): - super().__init__("localhost") - - def _new_conn(self): - return SnapdConnection() - -class SnapdAdapter(HTTPAdapter): - def get_connection(self, url, proxies=None): - return SnapdConnectionPool() - - -session = requests.Session() -session.mount("http://snapd/", SnapdAdapter()) -response = session.post("http://snapd/v2/snaps/nano-strict", - data=json.dumps({"action": "install", "channel": "stable"}), - ) -pprint.pprint(response.json()) +response = snap_http.install("nano-strict") +pprint(response.result) EOF -} > /tmp/scriptoutput \ No newline at end of file +} > /tmp/scriptoutput diff --git a/core/snaps/install-snap.py b/core/snaps/install-snap.py index 0a9de27..0266bee 100644 --- a/core/snaps/install-snap.py +++ b/core/snaps/install-snap.py @@ -1,39 +1,5 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python3 -import requests -import socket -import json +from landscape.client import snap_http -from urllib3.connection import HTTPConnection -from urllib3.connectionpool import HTTPConnectionPool -from requests.adapters import HTTPAdapter - - -class SnapdConnection(HTTPConnection): - def __init__(self): - super().__init__("localhost") - - def connect(self): - self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - self.sock.connect("/run/snapd.socket") - - -class SnapdConnectionPool(HTTPConnectionPool): - def __init__(self): - super().__init__("localhost") - - def _new_conn(self): - return SnapdConnection() - - -class SnapdAdapter(HTTPAdapter): - def get_connection(self, url, proxies=None): - return SnapdConnectionPool() - - -session = requests.Session() -session.mount("http://snapd/", SnapdAdapter()) -response = session.post("http://snapd/v2/snaps/nano-strict", - data=json.dumps({"action": "install", - "channel": "stable"}), - ) +snap_http.install("nano-strict") diff --git a/core/snaps/snap-http-managed-mode.py b/core/snaps/managed-mode.py similarity index 83% rename from core/snaps/snap-http-managed-mode.py rename to core/snaps/managed-mode.py index c424a2e..3bbdb80 100644 --- a/core/snaps/snap-http-managed-mode.py +++ b/core/snaps/managed-mode.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python3 from landscape.client import snap_http @@ -7,4 +7,4 @@ { "refresh.hold": "forever", }, -) \ No newline at end of file +) diff --git a/core/snaps/refresh-snaps.py b/core/snaps/refresh-snap.py similarity index 100% rename from core/snaps/refresh-snaps.py rename to core/snaps/refresh-snap.py diff --git a/core/snaps/snap-http-remove.py b/core/snaps/remove-snap.py similarity index 75% rename from core/snaps/snap-http-remove.py rename to core/snaps/remove-snap.py index 8b1c76c..2421997 100644 --- a/core/snaps/snap-http-remove.py +++ b/core/snaps/remove-snap.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python3 from landscape.client import snap_http diff --git a/core/snaps/run-attached-script.sh b/core/snaps/run-attached-script.sh deleted file mode 100644 index f4a0ac4..0000000 --- a/core/snaps/run-attached-script.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -python3 $LANDSCAPE_ATTACHMENTS/testscript.py diff --git a/core/snaps/set-refresh-timer.py b/core/snaps/set-refresh-timer.py index fb27ff6..859ce9b 100644 --- a/core/snaps/set-refresh-timer.py +++ b/core/snaps/set-refresh-timer.py @@ -2,8 +2,6 @@ from landscape.client import snap_http -# set the refresh timer, -# allowing the device to be put in managed mode snap_http.set_conf( "system", { diff --git a/core/snaps/snap-http-install.py b/core/snaps/snap-http-install.py deleted file mode 100644 index ed22c42..0000000 --- a/core/snaps/snap-http-install.py +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env python3 - -from landscape.client import snap_http - -snap_http.install("nano-strict") diff --git a/core/snaps/update-snap-config.py b/core/snaps/update-snap-config.py deleted file mode 100644 index da5bba8..0000000 --- a/core/snaps/update-snap-config.py +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env python3 - -from landscape.client import snap_http - -# get the system configuration -print(snap_http.get_conf("system")) - -# enable some experimental features -snap_http.set_conf( - "system", - { - "experimental.parallel-instances": True, - }, -) diff --git a/core/users/add-sso-user.py b/core/users/add-sso-user.py index 2c1b057..7d25872 100644 --- a/core/users/add-sso-user.py +++ b/core/users/add-sso-user.py @@ -2,8 +2,8 @@ from landscape.client import snap_http -# add an Ubuntu SSO user to a Core device -# use `force_managed` if the device is already managed +# Add an Ubuntu SSO user to a Core device. +# Use `force_managed` if the device is already managed. snap_http.add_user( "username", "username@example.com", diff --git a/debugging/snap-debug-info.sh b/debugging/snap-debug-info.sh index dda0e33..a9bea03 100644 --- a/debugging/snap-debug-info.sh +++ b/debugging/snap-debug-info.sh @@ -2,13 +2,13 @@ # This script collects information about the status of a device to aid in debugging. # There is a slight limitation when trying to extract logs using the journalctl -# command. +# command. # # If your Landscape snap is based on Core 22 and your device is Core 24, you will not # be able to extract logs using the journalctl command. This is because the journalctl -# command does not support the log format used by Core 24. -# -# If you are using Core 24, you can resolve this by using a Landscape client track +# command does not support the log format used by Core 24. +# +# If you are using Core 24, you can resolve this by using a Landscape client track # that is based on Core 24. @@ -72,7 +72,7 @@ def print_services(apps, indent=0): print(line) -def make_API_call(method, path): +def make_api_call(method, path): sock = socket.socket(family=socket.AF_UNIX) try: sock.connect(SNAPD_SOCKET) @@ -109,20 +109,20 @@ def make_API_call(method, path): print("\nSystem Information:") print("=================================") -pprint.pprint(make_API_call("GET", "/system-info")) +pprint.pprint(make_api_call("GET", "/system-info")) # Get SnapD Info print("\nModel and Serial Information:") print("=================================") -pprint.pprint(make_API_call("GET", "/model/serial")) +pprint.pprint(make_api_call("GET", "/model/serial")) print("\nInstalled Snap Information:") print("=================================") -pprint.pprint(make_API_call("GET", "/apps")) +pprint.pprint(make_api_call("GET", "/apps")) print("\nSnap Services Information:") print("=================================") -response_json = make_API_call("GET", "/apps") +response_json = make_api_call("GET", "/apps") # Extract the JSON part from the HTTP response @@ -141,14 +141,14 @@ print_services(filtered) print("\nInterface Connection Information:") print("=================================") -pprint.pprint(make_API_call("GET", "/connections")) +pprint.pprint(make_api_call("GET", "/connections")) print("\nSnapD Changes Information:") print("=================================") -pprint.pprint(make_API_call("GET", "/changes?select=all")) +pprint.pprint(make_api_call("GET", "/changes?select=all")) print("\nValidation Sets Information:") print("=================================") -pprint.pprint(make_API_call("GET", "/validation-sets")) +pprint.pprint(make_api_call("GET", "/validation-sets")) END From 2dfdd5797838f12357ad48e91e6f49ec9f1cdf75 Mon Sep 17 00:00:00 2001 From: Stephen Mwangi Date: Tue, 21 Apr 2026 10:42:06 +0300 Subject: [PATCH 2/2] feat: add confdb scripts --- README.md | 23 ++++ core/confdb/acknowledge-confdb-schema.py | 134 ++++++++++++++++++++ core/confdb/delegate-confdb.py | 13 ++ core/confdb/enable-confdb.py | 11 ++ core/confdb/get-confdb-control-assertion.py | 8 ++ core/confdb/get-confdb.py | 34 +++++ core/confdb/set-confdb.py | 23 ++++ core/confdb/undelegate-confdb.py | 14 ++ core/config/get-conf.py | 7 +- core/config/set-conf.py | 13 +- 10 files changed, 272 insertions(+), 8 deletions(-) create mode 100644 core/confdb/acknowledge-confdb-schema.py create mode 100644 core/confdb/delegate-confdb.py create mode 100644 core/confdb/enable-confdb.py create mode 100644 core/confdb/get-confdb-control-assertion.py create mode 100644 core/confdb/get-confdb.py create mode 100644 core/confdb/set-confdb.py create mode 100644 core/confdb/undelegate-confdb.py diff --git a/README.md b/README.md index 14cae26..49ea87d 100644 --- a/README.md +++ b/README.md @@ -186,6 +186,29 @@ Scripts in this section use the [snap-http library](https://github.com/canonical - **Tutorial**: Remove a user from a Core device - [**remove-user.py**](./core/users/remove-user.py) +### Confdb + +- **Tutorial**: Enable the experimental confdb and confdb-control features + - [**enable-confdb.py**](./core/confdb/enable-confdb.py) + +- **Tutorial**: Acknowledge a confdb-schema assertion into snapd + - [**acknowledge-confdb-schema.py**](./core/confdb/acknowledge-confdb-schema.py) + +- **Tutorial**: Set values in a confdb view + - [**set-confdb.py**](./core/confdb/set-confdb.py) + +- **Tutorial**: Get values from a confdb view + - [**get-confdb.py**](./core/confdb/get-confdb.py) + +- **Tutorial**: Grant an operator delegated access to confdb views + - [**delegate-confdb.py**](./core/confdb/delegate-confdb.py) + +- **Tutorial**: Revoke an operator's delegated confdb access + - [**undelegate-confdb.py**](./core/confdb/undelegate-confdb.py) + +- **Tutorial**: Retrieve the confdb-control assertion + - [**get-confdb-control-assertion.py**](./core/confdb/get-confdb-control-assertion.py) + ### Attachments - **Tutorial**: Run an attached script diff --git a/core/confdb/acknowledge-confdb-schema.py b/core/confdb/acknowledge-confdb-schema.py new file mode 100644 index 0000000..ef2ae00 --- /dev/null +++ b/core/confdb/acknowledge-confdb-schema.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 + +from landscape.client import snap_http + +# Acknowledge a confdb-schema assertion into snapd, along with its prerequisite +# account and account-key assertions. +assertion = """\ +type: account +authority-id: canonical +account-id: f22PSauKuNkwQTM9Wz67ZCjNACuSjjhN +display-name: Stephen Mwangi +timestamp: 2024-01-03T10:36:48.475339Z +username: st3v3nmw +validation: unproven +sign-key-sha3-384: BWDEoaqyr25nF5SNCvEv2v7QnM9QsfCc0PBMYD_i2NGSQ32EF2d4D0hqUel3m8ul + +AcLBUgQAAQoABgUCZZU4wAAA0scQABIiB5aAhqNNv70gAJPq42FfzuFaH+0H8bEjDRaEt73kFmo9 +ZzV2kHbr5r1WrZn7wPFqSrxBzi9dspCozc9nZi2pdV+S8xcFHFA29aGcGWrT3vs4U8Jv31ydqnQ8 +iiwQbgPCVTeGHbAFJqn2pk4opcqLD74Cdh+6kVVmCc3XdcrPHvqI7Cowu6SbEEAGEhTTuLZOpXGr +/rcTtpZvDCw6lCTsUkkpqBgDB4gBZPKijKQpqN4cyKbh1UMlEZOd0u+c+uEtbFgHmLkL6BG9Szny ++SdWZTyHc3L332hPMunM941H+z0M1arGqmkkRaA+xXCdBRVOvqT7SCyxuApzi8Sm2qOoCWcqoFKj +AFdmfPg5Z1Sp6CyUzMRC3GuLODdMahdzz2zkxu4imYOgR4NqfSv5tbw+M2ZXk9Jv7kmdHU5i0scW +8zr8T28LbCCxdm1x8lFbytTDf6sa8iKOK9v3Sz4cEmrpJWikocVSOWLvE/2C8Fzkm2FJRiIZZ0BL +LCMocgEf1i0IaKw6/VdHn0m01uYS8y30Y72S8hkpUo0NTgj90a6Gw+/RDTXY+6F4IqwNhqQOQJBz +scsrCeNF7NkgKvDNJx/Cth11ztIWDHQsTagGElXLPvO7fNz47l5qVDFUH/MXj7GPzCkW5KBJ9AbU +RfhfoQ4nYrvl796kWhDsD08RRDJp + +type: account-key +authority-id: canonical +public-key-sha3-384: xkd_Y2ay5N2Uo14v_wsCtfVJYLAVbJgxbiKM8Ne4mZBflaROriZgk2nb5i9Oebum +account-id: f22PSauKuNkwQTM9Wz67ZCjNACuSjjhN +name: snapcraft +since: 2024-03-06T08:43:58Z +body-length: 717 +sign-key-sha3-384: BWDEoaqyr25nF5SNCvEv2v7QnM9QsfCc0PBMYD_i2NGSQ32EF2d4D0hqUel3m8ul + +AcbBTQRWhcGAARAAwfGMiOrB4ST1np6prpDWBlivmdkULG2F9QTYCnKz0uNmLkiHFqe4MN+XDtek +NPC66L0ZSx6IQ74ZLlb3/djdZ8t/mbI/d/nAL3IFrC5aYLrcKtX/e03lKDvzThQUFDPgB31PbZuD +XakbbSG9HjcLGBLVC/jMqBsKQaBarNkddSXwqL0FnKGLWbxIPnbuJ8Zxny/nW9+LU5E5LfuAvxNe +oBYO1cPOlhUfR0XhJiBQqKMDSRiXqIWrr2OYxrnDZgphIgo+WWBfqiYM3vSmNzt36R/AkyfrWq08 +bU+s8nc22Ms4KlSYEWFVddz2hbhYvMrjoNgXq85J2O1DkQQWA9gOZXpxskFP3mEd2T/ofS028v0I +YHs4ST/QJWSTsJT2PhtUO6PeNjlAhZ4NMJZlnI3PJt/5c3d5He/SZ6H54B/19tMs3KAgxzXTX7Qx +646qFBF+1ZTsVeZ9UusEbyyZJDexcZVZiVKL+4K+xDjOlXo0Erm8luVkk0I4TwjLgPK8+aPsd0l6 +t7rE0KcrUsPmoNcuca1++XD0dJNMwLNkCQDvXk7q0xIr7L/ue231I5dcPW0PT3d76wA/kR3woA+V +tO7yy1Qop0npAE4XzoNSTdlNCzsuWzUgCzlFwLnDIKGPNv3wZFIQUduou8YlAMu3ZTPNJduG0Ylh +RKvjES9B9mRBoNUAEQEAAQ== + +AcLBUgQAAQoABgUCZegszgAAJ6AQALEDhdPkEhR5etc4St6D4jcdyt52+IXKUlhiqs+pU/37yHya +p1cSB6HufvykSGshusJ+JENaDOIMIu0zXWMbzJM+mUVYSRn+PLDc5jkgiZQin6FUDf5d+wby3B2B +L2slA3NPf21bYodqCGZPsYKg3V7hDhUIYDE4n12XY00lwU6RsA5e6t9CPJQb899W7hyRxNunfBnS +w5/zHPD+r88VnyKpwqHhbwxIJAtPTE+UFxq57HYwJXvvL5V3KZ/9iF7YyyAsy2FwMyJgpMkS/QSm +3B2KPcGB3tNtWAv3gVEzYcKnFqWfYXZP/qvhQVpB5aSUKTnQFWf0DLFARnfZJxuUNy4SQEVU0Xhq +ZVPiQUDj2HBR6QsLzYH4ySLGiL+iBygIVKYXnm5baP+XNJsN2BKU751aXT1keOELxgJmv8fBahQc +X9Lg/EinlvxBzsrtv+O6CHckVxEsxoVMhRNJM6nFd4dA8leXDxLd0NokMufRf3PDjMIupBSptS7Q +hGXqGUdVV+5PwT+5anvSnxRqd/6IqLrq9M3eBF2PdLGWP0Bx8IVVuIWGPGMMxvHDtYcfYVbcdxu3 +LseyUyXh56uPMXPW2CVdAYHdTsSDVYD2l/ksYpA4Cd7tyBeJ7btWh8n9YsZ3HZrk6hasNF0VKURm +fvYaDRZCi27SyGIRI5OXQBmoa2zn + +type: confdb-schema +authority-id: f22PSauKuNkwQTM9Wz67ZCjNACuSjjhN +revision: 1 +account-id: f22PSauKuNkwQTM9Wz67ZCjNACuSjjhN +name: network +timestamp: 2026-01-21T10:19:23+00:00 +views: + proxy-admin: + rules: + - + access: read-write + content: + - + request: url + storage: url + - + request: bypass + storage: bypass + request: {protocol} + storage: proxy.{protocol} + proxy-state: + rules: + - + access: read + request: https + storage: proxy.https + - + access: read + request: ftp + storage: proxy.ftp +body-length: 487 +sign-key-sha3-384: xkd_Y2ay5N2Uo14v_wsCtfVJYLAVbJgxbiKM8Ne4mZBflaROriZgk2nb5i9Oebum + +{ + "storage": { + "aliases": { + "protocol": { + "choices": [ + "http", + "https", + "ftp" + ], + "type": "string" + } + }, + "schema": { + "proxy": { + "keys": "${protocol}", + "values": { + "schema": { + "bypass": { + "type": "array", + "unique": true, + "values": "string" + }, + "url": "string" + } + } + } + } + } +} + +AcLBcwQAAQoAHRYhBHCftZeyXSJlNBvC+h+HHdMBlPLtBQJpjdWQAAoJEB+HHdMBlPLtomUP/iFa +BxCtGrwOzgVNbzmkSJXqf0LRbTGLyQQkCe5lV/X9PKBa4J+I4y7QGrUDAXQwvsSUJgcsR+99NgnA +IJE6mfZos8IXK/1TKRPUM/umeb4CfcLu6xuCwc/PFZnJhxTOK3FQddONXCXqCfQStx4KvRIfPu/S +nybbuMNQrepcvDSMY1YF18nX3kmC4WXnsySFJI5EDkdDNDC5C4mM85khyORdApHXSUewNVWghIP7 +JjB0NeYxysJl3rrRcppfeGpB5Akx7lVJiTmGkCzvkDTymizMj4S6sveBGn5SLQPZz44X6jGG95il +EE//X9bM/bEEe2+s0gzIy4OWdDVVFU/X1uxzC6jlI+gwSpbhExKStdapC8nU7phoFvIlABLw7h2L +wFC7n84l95fR6f+9FPm1UKFqoBTO4pexTcihA3SNrKvD3oFyU3GnWZgfI1rgMOVdQnOYrOuaQMCO +lLZG0GgktRb9l/XekJexadsAnbhe8AF1Fb4pUM6eqAoIryy4CRetonlPyCDcFtOy+O4ARgsqYDfi +7YxA1g29kjYDox1Q/5Sb64ZRC68XAyCWF19E3sj4I/MLQ7/c429q5PF2AMMuoB3Z3Do6LmE3awlN +esGfztMtfdb2nRoH595JYbd55L8szS9zvJziSYXWCQ27BOJdxTU8JJaMGvpUOMbjD3YSqGb3 +""" + +snap_http.add_assertion(assertion) diff --git a/core/confdb/delegate-confdb.py b/core/confdb/delegate-confdb.py new file mode 100644 index 0000000..743a4f4 --- /dev/null +++ b/core/confdb/delegate-confdb.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 + +from landscape.client import snap_http + +# Grant an operator the ability to remotely manage confdb views on this device. +snap_http.delegate_confdb( + "f22PSauKuNkwQTM9Wz67ZCjNACuSjjhN", # operator account-id + authentications=["operator-key", "store"], + views=[ + "f22PSauKuNkwQTM9Wz67ZCjNACuSjjhN/network/proxy-admin", + "f22PSauKuNkwQTM9Wz67ZCjNACuSjjhN/network/proxy-state", + ], +) diff --git a/core/confdb/enable-confdb.py b/core/confdb/enable-confdb.py new file mode 100644 index 0000000..1eb1c81 --- /dev/null +++ b/core/confdb/enable-confdb.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python3 + +from landscape.client import snap_http + +snap_http.set_conf( + "system", + { + "experimental.confdb": True, + "experimental.confdb-control": True, + }, +) diff --git a/core/confdb/get-confdb-control-assertion.py b/core/confdb/get-confdb-control-assertion.py new file mode 100644 index 0000000..e70714a --- /dev/null +++ b/core/confdb/get-confdb-control-assertion.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 + +from landscape.client import snap_http + +# Retrieve the confdb-control assertion, which records which operators have +# been granted delegated access to confdb views on this device. +response = snap_http.get_assertions("confdb-control") +print(response.result.decode()) diff --git a/core/confdb/get-confdb.py b/core/confdb/get-confdb.py new file mode 100644 index 0000000..3ddba37 --- /dev/null +++ b/core/confdb/get-confdb.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +import time +from pprint import pprint + +from landscape.client import snap_http + +# Get all values accessible through a confdb view. +# confdb reads are async so the result must be retrieved by +# polling the returned change. +response = snap_http.get_confdb( + "f22PSauKuNkwQTM9Wz67ZCjNACuSjjhN", # account-id + "network", # confdb-schema name + "proxy-state", # view name +) +while True: + change = snap_http.check_change(response.change) + if change.result["status"] == "Done": + pprint(change.result["data"]["values"]) + break + time.sleep(0.1) + +# Get specific keys from a confdb view. +response = snap_http.get_confdb( + "f22PSauKuNkwQTM9Wz67ZCjNACuSjjhN", + "network", + "proxy-state", + keys=["https"], +) +while True: + change = snap_http.check_change(response.change) + if change.result["status"] == "Done": + pprint(change.result["data"]["values"]) + break + time.sleep(0.1) diff --git a/core/confdb/set-confdb.py b/core/confdb/set-confdb.py new file mode 100644 index 0000000..d47d2cc --- /dev/null +++ b/core/confdb/set-confdb.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 + +from landscape.client import snap_http + +# Set values in a confdb view. +snap_http.set_confdb( + "f22PSauKuNkwQTM9Wz67ZCjNACuSjjhN", # account-id + "network", # confdb-schema name + "proxy-admin", # view name + { + "https.url": "http://proxy.example.com:8080", + "https.bypass": ["localhost", "192.168.0.0/24"], + "ftp.url": "ftp://proxy.example.com:8080", + }, +) + +# Unset a value by passing None. +snap_http.set_confdb( + "f22PSauKuNkwQTM9Wz67ZCjNACuSjjhN", + "network", + "proxy-admin", + {"ftp": None}, +) diff --git a/core/confdb/undelegate-confdb.py b/core/confdb/undelegate-confdb.py new file mode 100644 index 0000000..ed8ee45 --- /dev/null +++ b/core/confdb/undelegate-confdb.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 + +from landscape.client import snap_http + +# Revoke all delegated confdb access from an operator. +snap_http.undelegate_confdb("f22PSauKuNkwQTM9Wz67ZCjNACuSjjhN") + +# To revoke access to specific views or authentication methods only, +# pass views and/or authentications. +snap_http.undelegate_confdb( + "f22PSauKuNkwQTM9Wz67ZCjNACuSjjhN", + authentications=["store"], + views=["f22PSauKuNkwQTM9Wz67ZCjNACuSjjhN/network/proxy-admin"], +) diff --git a/core/config/get-conf.py b/core/config/get-conf.py index f3c6f54..01bfd92 100644 --- a/core/config/get-conf.py +++ b/core/config/get-conf.py @@ -4,9 +4,12 @@ from landscape.client import snap_http # Get all configuration for a snap. -response = snap_http.get_conf("system") +response = snap_http.get_conf("landscape-client") pprint(response.result) # Get specific configuration keys. -response = snap_http.get_conf("system", keys=["experimental", "refresh"]) +response = snap_http.get_conf( + "landscape-client", + keys=["account-name", "url", "ping-url"], +) pprint(response.result) diff --git a/core/config/set-conf.py b/core/config/set-conf.py index 08f5da8..43c58d4 100644 --- a/core/config/set-conf.py +++ b/core/config/set-conf.py @@ -4,18 +4,19 @@ # Set configuration. snap_http.set_conf( - "system", # snap name + "landscape-client", { - "experimental.confdb": True, - "experimental.confdb-control": True, - "experimental.remote-device-management": True, + "account-name": "standalone", + "computer-title": "my-device", + "url": "https://landscape.example.com/message-system", + "ping-url": "http://landscape.example.com/ping", }, ) # Unset a configuration option by passing None. snap_http.set_conf( - "system", + "landscape-client", { - "experimental.remote-device-management": None, + "some-url": None, }, )