Interface connection from gadget in firstboot

Just wanted to check the status of this? Would be great to have it for our use-case also.

Also a slightly n00b question regarding the suggested approach: If I wanted to add the “connect” key to my gadget snap’s snapcraft.yaml file, in order to connect a plug to a slot provided by the gadget itself, how would I go about finding out the yet-to-be-built gadget’s snap-id? Would this auto-connection approach only work for gadget snaps that were pushed to the store (and have their snap ID listed there), or would there be a way to do it for snaps that were built locally (where they’re for example used in a device image)?

In the meantime, I came up with a workaround for this via systemd that is so inelegant that it will presumably irritate someone else into implementing the proper way of handling this :wink:

(In the image file tree, create a system-data/etc/systemd/system/connector.service that contains any required ExecStart=/usr/bin/snap connect <...> statements in “oneshot” mode. Then enable the service to start on boot by putting a symlink to the file in in system-data/etc/systemd/system/multi-user.target.wants. Probably overkill to run on each boot, but in the absence of other alternatives it seems to do the trick.)

I think whatever we do we should still, in principle, respect the connection rules from the snap-declarations of the involved snaps.

1 Like

Collecting some discussion input here about use cases:

  • Typically this is interesting for slots provided by the gadget itself, if one goes looking for interfaces that can have slots provided by a gadget we encounters things like serial-port etc., where auto-connection alone is not enough because there might be usually many candidates plugs and slots.
  • The application requiring those slots might come with the device originally or not (it might enable some important functionality but might not have been available when the device first shipped, it might be an add-on).
  • For an interface like camera provided by core and where auto-connection is not allowed by default, auto-connecting via the a declaration is a viable path as long as the application comes from a store controlled by the gadget/device publisher. it is not a viable path if the application is more generic and comes from a store to which they have access but don’t control (like the global store).

It’s important to note that for an appliance kind of device (no user usually present to trigger snapd actions):
snaps either come via seeding, or there is a snap with snapd-control that can install further snaps and so in in principle could do connections too.

With these considerations it seems there are two related areas to address:

  • the gadget should be able to define connections between snaps that comes with the model (naively this means at seeding but see 2nd point)
  • we need to explore ways to update the model assertion of a gadget to a new revision, and carry out any reasonable effect of that, like installing further required snaps (what about removing?)
  • unless we specify the connections in the model assertions, not the gadget (that doesn’t seem appropriate though), such model revision update would need to be potentially in sync with a gadget update; one approach would be to have a trigger for the model revision update from the gadgets

@pedronis pushed a preliminary PR here:

We’ve also just had a quick call to discuss this, and agreed to tweak the syntax slightly to something along those lines:

connections:
  - plug: snapid1:plg1
    slot: snapid2:slot
  - plug: snapid3:process-control
  - plug: snapid4:pctl4
    slot: system:process-control
  - plug: snapid5:pctl5
    slot: system:process-control

Rationale for “connections” is that the other options we have there are also plural nouns (defaults, volumes), and the more verbose syntax is both more symmetric and allows us to extend individual connections with further options if we have to (we might want an option to drop the otherwise established automatic connection, for example).

2 Likes

https://github.com/snapcore/snapd/pull/5221 is now updated

1 Like

Proposed now:

that actually performs the connections (but ignores missing snaps/slots/plugs while logging them) at first boot.

1 Like

support for this has now landed and will be in snapd 2.34.

added documentation to Gadget snaps

3 Likes

this will be blocked by ubuntu-image tough atm because it also does strict parsing of gadget.yaml, that’s tracked here

@pedronis, I have updated to the latest ubuntu-image with this fix. I created the connections stanza according to the specified syntax from @niemeyer. I can successfully create an Ubuntu Core image, however, snpad (2.34.3) in the built Ubuntu Core image is not auto connecting these plugs to the specified slot.

Has any one had a chance to verify this solution on Ubuntu Image?

Below is my gadget.yaml file

volumes:
  pc:
    bootloader: grub
    structure:
      - name: mbr
        type: mbr
        size: 440
        content:
          - image: pc-boot.img
      - name: BIOS Boot
        type: DA,21686148-6449-6E6F-744E-656564454649
        size: 1M
        offset: 1M
        offset-write: mbr+92
        content:
          - image: pc-core.img
      - name: EFI System
        type: EF,C12A7328-F81F-11D2-BA4B-00A0C93EC93B
        filesystem: vfat
        filesystem-label: system-boot
        size: 50M
        content:
          - source: grubx64.efi
            target: EFI/boot/grubx64.efi
          - source: shim.efi.signed
            target: EFI/boot/bootx64.efi
          - source: grub.cfg
            target: EFI/ubuntu/grub.cfg
#Auto connect below
connections:
  - plug: mycustomsnap:hardware-observe
    slot: core:hardware-observe
  - plug: mycustomsnap:shutdown
    slot: core:shutdown
  - plug: mycustomsnap:snapd-control
    slot: core:snapd-control
  - plug: mycustomsnap:tpm
    slot: core:tpm
  - plug: mycustomsnap:gpio
    slot: mygadgetsnap:gpio-5
  - plug: mycustomsnap:gpio
    slot: mygadgetsnap:gpio-6
  - plug: mycustomsnap:gpio
    slot: mygadgetsnap:gpio-7
  - plug: mycustomsnap:gpio
    slot: mygadgetsnap:gpio-438

note that “mycustomsnap” and “core” need to be snap IDs not names …

i.e. the ID of core on arm is: 99T7MUlRhtI3U0QFgl5mXXESAiSwt776

the snap info command or the overview page on https://dashboard.snapcraft.io/ for the specific snap show the ID.

I’m struggling to get this working also.

In my gadget.yaml I have:

connections:
 - plug: JMOa0xipSr8QRguwZNbWsIg3RQTNDC3v:hardware-observe
 - plug: JMOa0xipSr8QRguwZNbWsIg3RQTNDC3v:network-observe
 - plug: JMOa0xipSr8QRguwZNbWsIg3RQTNDC3v:serial-port
 - plug: JMOa0xipSr8QRguwZNbWsIg3RQTNDC3v:system-observe
 - plug: JMOa0xipSr8QRguwZNbWsIg3RQTNDC3v:log-observe

(full file at https://github.com/svet-b/pi3-gadget/blob/master/gadget.yaml)

Based on the documentation (Gadget snaps), I followed the recommendation that

Omitting "slot" in an instruction is allowed and equivalent then to: slot: system:<plug>

The gadget is created fine, but when trying to build an image with it using ubuntu-image (version 1.3+16.04ubuntu2) I get the following, with debug mode on:

DEBUG:ubuntu-image:-> [ 3] load_gadget_yaml
ERROR:ubuntu-image:uncaught exception in state machine step: [3] load_gadget_yaml
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/ubuntu_image/parser.py", line 304, in parse
    validated = GadgetYAML(yaml)
  File "/usr/lib/python3/dist-packages/voluptuous.py", line 337, in __call__
    return self._compiled([], data)
  File "/usr/lib/python3/dist-packages/voluptuous.py", line 635, in validate_dict
    return base_validate(path, iteritems(data), out)
  File "/usr/lib/python3/dist-packages/voluptuous.py", line 471, in validate_mapping
    raise MultipleInvalid(errors)
voluptuous.MultipleInvalid: extra keys not allowed @ data['connections']

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/ubuntu_image/state.py", line 82, in __next__
    step()
  File "/usr/lib/python3/dist-packages/ubuntu_image/common_builder.py", line 132, in load_gadget_yaml
    self.gadget = parse_yaml(fp)
  File "/usr/lib/python3/dist-packages/ubuntu_image/parser.py", line 312, in parse
    raise GadgetSpecificationError('Invalid gadget.yaml @ {}'.format(path))
ubuntu_image.parser.GadgetSpecificationError: Invalid gadget.yaml @ connections
ERROR:ubuntu-image:gadget.yaml parse error
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/ubuntu_image/parser.py", line 304, in parse
    validated = GadgetYAML(yaml)
  File "/usr/lib/python3/dist-packages/voluptuous.py", line 337, in __call__
    return self._compiled([], data)
  File "/usr/lib/python3/dist-packages/voluptuous.py", line 635, in validate_dict
    return base_validate(path, iteritems(data), out)
  File "/usr/lib/python3/dist-packages/voluptuous.py", line 471, in validate_mapping
    raise MultipleInvalid(errors)
voluptuous.MultipleInvalid: extra keys not allowed @ data['connections']

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/ubuntu_image/__main__.py", line 341, in main
    list(state_machine)
  File "/usr/lib/python3/dist-packages/ubuntu_image/state.py", line 82, in __next__
    step()
  File "/usr/lib/python3/dist-packages/ubuntu_image/common_builder.py", line 132, in load_gadget_yaml
    self.gadget = parse_yaml(fp)
  File "/usr/lib/python3/dist-packages/ubuntu_image/parser.py", line 312, in parse
    raise GadgetSpecificationError('Invalid gadget.yaml @ {}'.format(path))
ubuntu_image.parser.GadgetSpecificationError: Invalid gadget.yaml @ connections

Clearly I’m doing something wrong, and maybe it’s just bad yaml, but I’m not sure what to make of the error. Any pointers?

I’m on snapd version 2.34.3.

@svet This is a missing support in ubuntu-image. Please try to install the latest ubuntu-image snap (sudo snap refresh ubuntu-image). It should have the necessary support now.

Thanks for the quick response, @mvo that is indeed the issue!

More specifically, I was using ubuntu-image from the Ubuntu apt repository, and even the latest version there was outdated. I installed ubuntu-image 1.4 using snap and that appears to run well.

Follow-up question: one of the interface connections I need to auto-establish is actually to a slot on my custom gadget, not to the core system (namely, a serial port).

Since my custom gadget is not in the store, I’m assuming it has no ID - so I’m not sure what to put in the gadget.yaml in order to realize this. What’s the best way to get this to work? Should I upload the gadget to the store (and request manual review, etc)?

Answering my own question, the following appeared to do the trick for me:

  • Push the custom gadget snap to the store. Although it’s not possible to release it, it’s assigned a snap ID which is available in the store dashboard
  • Edit gadget.yaml to refer to the gadget slot using the assigned snap ID. Create new version of gadget snap (which uses the updated gadget.yaml) and push to store again
  • From store dashboard, download the latest gadget snap file [snap-id]_[revision].snap
  • Use that gadget snap file to build the image

I’m not claiming all these specific steps are necessary (there may well be a more straightforward way to do it), but as I said this got me the desired result.

Hi @svet, I am in a similar situation. I also have to auto connect different snaps to plugs. I can get the snap-id of custom snaps using snapcraft sign-build --local --key-name mykey ./my-custom-snap.snap.

This works on custom snaps but doesn’t appear to work for a gadget snap that is forked from Repository: https://github.com/snapcore/pc-amd64-gadget. The following error arises:

Your account lacks permission to assert builds for this snap. Make sure you are logged in as the publisher of 'my-custom-gadget' for series '16'.

Has any one had luck with this?

snap info my-custom-gadget more simply shows its snap-id as well, without involving signing. Assuming my-custom-gadget is in the store/brand store, only store snaps have snap-ids.

Is it possible to connect without a snap ID and only by name just like the command snap connect <snap>:<plug> <snap>:<plug>.

The custom snaps are offline snaps, and are intended to be offline snaps so I can’t easily get a snap-id.