Your submission was sent successfully! Close

You have successfully unsubscribed! Close

Thank you for signing up for our newsletter!
In these regular emails you will find the latest updates about Ubuntu and upcoming events where you can meet our team.Close

How to use remote script execution in the Landscape Client snap

The Landscape Client snap allows remote script execution, but its behavior differs slightly from the Landscape Client deb package. This is because the Landscape Client snap is typically used on Ubuntu Core, and Ubuntu Core devices don’t use the same user model as desktop and server devices.

This document describes some differences and gives examples of how remote script execution can be used with the snap.

Contents:

Background information

Snaps execute all scripts as root, regardless of the user selected when the activity is initiated. However, this doesn’t grant full device-level root privileges; the script execution is still confined by the properties of the snap, which restricts what actions the snap can perform. Snaps can’t modify immutable elements of the client or violate any limitations of the connected interfaces.

Standard connected interfaces

Some standard connected interfaces are:

  • hardware-observe: Access hardware information
  • mount-observe: Read mount table and quota information
  • network: Enables network access
  • network-bind: Operate as a network service
  • network-observe: Query network status information
  • scsi-generic: Read and write access to SCSI Generic driver devices
  • shutdown: Restart or power off the device
  • snapd-control: Install or remove software
  • system-observe: Read process and system information

For more information on what these interfaces permit, see Snapcraft’s documentation on supported interfaces.

Script options

You can use shell scripts or Python scripts to execute your scripts remotely. Additionally, you can include a file attachment that can be an executable file or a Python script.

SnapD

The Landscape Client snap uses the snapd-control interface to manage its own activities. However, you can also use this interface to interact with the SnapD REST API directly, or use the SNAP-HTTP library for easier interaction with the REST API.

For more information on the snapd-control interface, see Snapcraft’s documentation.

Example scripts to run on your device

You can use these examples to explore running remote scripts on your devices. You may need to adapt parts of them to fit your configuration. It’s also recommended that you explore the example scripts available in the Landscape Scripts repository.

Example #1: Install the nano-strict snap on the remote device using Python

Run the following:

#!/usr/bin/env python3 

import requests
import socket
import json

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"}),
                       )

Example #2: Install the nano-strict snap using the SNAP-HTTP library

Run the following:

#!/usr/bin/env python3 

from landscape.client import snap_http

snap_http.install("nano-strict")

Example #3: Set the logging level to “debug”

This example sets a configuration value of the Landscape Client snap to set the logging level to “debug”:

#!/usr/bin/env python3 

from landscape.client import snap_http

snap_http.set_conf("landscape-client", {"logging-level": "debug"})

Example #4: Use an attachment with testscript.py

You can also use a file attachment. Add the testscript.py file in the web portal as an attachment and run this script:

#!/bin/bash

python3 $LANDSCAPE_ATTACHMENTS/testscript.py

Debug scripts

One of the limitations of remote script execution is that when a script fails, the returned information can be limited. The options provided here can help you debug your scripts.

Pipe the output of your script to a file

One option to debug your script is to pipe the output of your script to a file, such as /tmp/output.txt. You’ll then be able to access that file from the main device shell.

Use heredocs for Python scripts

You can use heredocs within a Bash shell to debug a Python script by wrapping the code in a heredoc:

#!/bin/bash
{
python3 - << EOF
# Insert Python script here
EOF
} > /tmp/scriptoutput

In this script, the Bash shell is asked to execute this doc and pipe any console output to the /tmp/scriptoutput file.

Here is the full script using the code from a previous example:

#!/bin/bash
{
python3 - << EOF

import requests
import socket
import json
import pprint

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())
EOF
} > /tmp/scriptoutput

This page was last modified a month ago. Help improve this document in the forum.