How to issue a remodel request over snapd API

Hey,

I am trying to remodel a device with the snapd API. The snapd REST API documentation says this:

POST /v2/model
Description: replace the current model assertion
Access: authenticated
Operation: async
Return: 202 OK or an error
Can be either:

- JSON content-type, containing only the model assertion and requiring online store access to retrieve other components
- multipart/form-data content-type, to side-load whatever new components may be required. See offline remodelling.

I want to do an online remodel with a model assertion. From the documentation it is unclear to me how to pass the model assertion into the API request. I read the documentation that I have to give the model assertion as a JSON; however, the model assertion is not a JSON file.

I tried to load the model assertion as a yaml to try and translate it, but the model assertion doesn’t seem to be a yaml file either.

The python code I am trying looks like this:

#!/usr/bin/env python3 

import requests
import socket
import json
import time
import yaml
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())

base_url = "http://snapd/v2/"

path = "new-model-assertion.model.assert"
with open(path, 'r') as f:
    loaded_yaml = yaml.safe_load(f)

print("Triggering remodel")
response = session.post(f"{base_url}model", data=json.dumps(loaded_yaml)).json()

print(response)

The snapd version I am using is 2.63

Source code is the best documentation. The client part of request issued when you run snap remodel is implemented here: snapd/client/model.go at a23c0c16d8c1e7df63bb9118585c82b12efed38e · snapcore/snapd · GitHub

Thank you, with the help of the code I managed to do a successful request. The JSON request should basically be {"new-model" : open("new-model.assert", "r").read() }

Full code:

#!/usr/bin/env python3 

import requests
import socket
import json
import time
import yaml
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())

base_url = "http://snapd/v2/"

path = "new-model-assertion.model.assert"
with open(path, 'r') as f:
    new_model = f.read()

print("Triggering remodel")
headers = {
    "Content-Type": "application/json"
}
response = session.post(f"{base_url}model", headers=headers, json={"new-model" : new_model}).json()

print(response)
1 Like