Hi Christian,
On 06.01.21 18:00, Christian Storm wrote:
> Hi Stefano,
>
>> I am thinking about to implement a new feature and I am writing a
>> concept, asking the community for help and review. There is at the
>> moment no single line implemented, I want to check in advance drawbacks
>> and exchange ideas.
>>
>> Background: I get information from a manufacturer, whose devices in
>> field are just checking for new update twice a day. He is not using
>> Hawkbit, but he has an own server where he stored the SWUs. SWUpdate on
>> the device simply connects with the server and verifies if the whole SWU
>> must be downloaded. This means that SWUpdate starts to download the SWU,
>> then sw-description is parsed and the update is stop as soon as it is
>> recognized (using version schemas) that a new version is not required.
>
> Ok, so this is kind of a dumb server just serving the artifacts and
> SWUpdate does the logics on whether to install or not, right?
Right - it is just a Webserver. The SWU are simply stored, and SWUpdate
receives (I do not know how) the URL to get the SWU. Then the whole
logic if an update is required or not is implemented in SWUpdate.
>
>
>> Due to the security concept that requires to split download and
>> install in separate processes, update is stop after ~0.8 MB are
>> downloaded when the installer has parsed sw-description. The
>> downloader has already loaded more data and they lie in kernel (skb)
>> or SWUpdate buffers. If this seems a low value, manufacturer reports
>> that, due to his large installation, the update check generates more
>> as 100GB/day of unneeded traffic.
>
> With different download and install "processes" you mean two steps or
> two OS processes?
two OS processes. We have the installer (core) and the downloader or
suricatta.
> For the former, I do in my still to be submitted Lua
> suricatta binding a dry-run which downloads the artifact and then use
> file://.. to install it from the local storage.
Yes, this is also a use case, I have also implemented in some projects.
> This way, SWUpdate is
> able to check validity of the artifact and can early abort broken
> artifacts...
The problem is the definition of early abort. This is the same use case
that I report here. Even in dry-run mode, you start to download the SWU,
then the SWU is pushed to core until the parser runs and raises the
abort. In the case I reported, the downloaded data until SWUpdate aborts
(from the parser) is ~0.8MB. As I said, this seems not too much, but it
sums with the amount of the installed base. The reason for this RFC is
to introduce a controlled download per file, so that just sw-description
is downloaded if an abort can be raised by the parser, reducing the
amount of downloaded data to some KBytes.
>
>
>> In case of backend, the traffic is surely reduced because SWUpdate just
>> asks Hawkbit if a new software is available via REST-API, without
>> checking itself inside the SWU.
>
> This is one point, the other is knowing somewhere in the backend which
> version(s) are installed on the devices, which is unknown to the backend
> in your scenario or is it reported by some middleware to some backend?
It is unknown (better: there is a storage server, no backend). The
device knows which version is running, and SWUpdate is informed of it
with command line parameter. The version is then compared with the
"version" attribute in sw-description.
>
>
>> But adding a Hawkbit infrastructure is a no way for that manufacturer.
>> Anyway, I am thinking about a solution that brings benefits for
>> Hawkbit, too (and generally, when SWUpdate works in "pull" mode
>> loading itself from an external source). A use case is when the SWU
>> contains multiple artifacts, and just one of them needs to be updated
>> (for example, just the application). An ad-hoc logic can be
>> implemented as Lua script ("embedded-script" in sw-description),
>> and SWUpdate could avoid to download what will be marked as "non
>> required" after parsing, instead of downloading the whole SWU and skip
>> what is not necessary. This helps also in another use case, where SW
>> for multiple devices are packed together in one SWU (a requirement in
>> some application where storage servers are managed by organizations
>> and not by manufacturers), but bandwidth must be preserved. So these
>> are the main reasons for this RFC.
>
> Hm, I fear that sw-description will be overloaded with Lua ad-hoc
> embedded scripts having some logics?
Well, I was maybe OT. Let's say we have just multiple selections in
sw-description, and application are stored in different partitions. Then
there is no Lua and this is treated automatically by SWUpdate. We can
update just the application, the OS or everything as usual. Currently,
we can do this, but the whole SWU is downloaded.
> Maybe we can revise the
> sw-description format and make it all Lua for example? As another
> alternative to libconfig and JSON then?
We have already...
This is done via the external parser, that calls a pure Lua code. The
example in repository is related to a XML parser using lua-lxp, but
let's say: you can already do whatever you want.
However, this high freedom has its drawbacks. As far as I know, just one
customer of mine has used this in the past, but he converted back to
libconfig/JSON later. In fact, a pure Lua solution requires that
everybody must implement and maintain the own parser, while I have added
features and features to the standard parsers. The original XML parser
developed by customer was then very poor compared to libconfig, and
customer himself decided to get rid of it.
Anyway, the feature is not removed, you have already a pure Lua parser
if you want...
> So you have the choice of
> "simple" configuration and programmatic Lua configuration?
> Of course, this would be a mid/long-term thing... Just an idea though.
Feature already implemented.
>
>
>> In SWUpdate design, there is a split between the installer (SWUpdate's
>> main process) and the processes getting the SWU. The last ones have no
>> idea about how the SWU is built and they rely on the installer. I want
>> to change slightly this: the downloders won't know the internals, but
>> they will be aware that SWU is a CPIO archive and that sw-description is
>> the first file in the SWU (this is hardcoded in SWUpdate by design).
>> The idea is to use RFC 7233 (" Hypertext Transfer Protocol (HTTP/1.1):
>> Range Requests") - all current HTTP servers support it. This means that
>> this new feature just works for HTTP(S), but this is the 90% of the
>> cases or more.
>
> Hm, so you're splitting the currently one download "function" into
> something like download_sw-description()
Well, to be more precise : download an entry from CPIO archive in SWU,
independently if it is sw-description or not.
> and download_payload(int) then
> that can be called from the different ingress channels (suricatta,
> download, ...)?
My idea is to add a pipeline to the core curl, so that any user of curl
in SWUpdate could use it (downloader, suricatta).
> Then, we should also make the downloader able to verify what it has
> downloaded, maybe by an IPC call to the core not to duplicate code.
Right - I want to maintain this split for security reason.
The core has also access to keys for verification and decryption. The
downloaders have no access and they should not gain access to them.
The downloaders should remain quite dumb: just load a chunk of data of a
fixed size (defined in CPIO), and ask the core if everything is ok to
move on.
>
>
>> I sketch my ideas as:
>>
>> 1. the downloader knows that the SWU is a CPIO. It starts downloading
>> the first CPIO header (110 bytes), sending a HTTP Range request.
>> 2. the downloader knows the size of sw-description, and load it together
>> with the next CPIO header (header of next file).
>> 3. if the header of next file is the signature, download it as well (and
>> asks also for the next CPIO header).
>> 4. push the data via IPC to the installer : sw-description is parsed,
>> and the internal structures are created. SWUpdate knows which artifacts
>> are required, and if the update must go on.
>> 5. the downloader asks via IPC (a new command) the installer if the next
>> file in the cpio is required. If yes, download it and push to the
>> installer, else skips it and asks via HTTP range request for the next
>> cpio header, until an artifact is required.
>
> Hm, the question here is who is the driver of the update process?
> Currently, the download is configured to stream it into the core, setup
> by the ingress mechanism (suricatta, downloader, ...). Thereafter, the
> ingress mechanism is out of the game waiting for SWUpdate core to finish.
Right, this is the current design and in my proposal I do not change
this. It just a "controlled download" of the stream.
>
> Another option would be to make the core call back to the ingress
> to get the next data chunk?
A nice thing in SWUpdate is that it has the same behavior independently
from the source. I have just one SWU if I update locally, or with the
internal Webserver, or via suricatta, or via an URL. This is a very
appreciated feature.
Change the way makes this very difficult, specially in case the update
is done via the internal Webserver. I know you have not this case, but
believe me, this is one of the most used way in many projects, and it is
also used by developers even if later the update in field is done in a
different way. The browser pushes data, no way to control it.
In SWUpdate, each handler is a consumer : it gets data that are already
made available from the core. Requesting data changes completely the way
to work, and this becomes a very big change.
I have sketched a design to implement something like this for a delta
update, but this is then just for one handler: the handler could receive
a URL for the artifact and ask to download it. It has also security
related aspects that must be considered, but it can be restricted to a
single handler.
But switching the concept completely is too much IMHO, and I do not know
if there are real advantages.
> This way, different download methods
> (HTTP/curl, Queue server, ...) could be easily supported by the ingress
> mechanism, hidden from SWUpdate core. If it's transport doesn't support
> RANGE requests, it can simulate this or fall back to the current design.
>
>> 6. loop 5 until the end of cpio, this is marked with the "TRAILER!"
>> magic word. Maybe some more controls are required here to avoid that an
>> attacker tries a DOS pushing non-sense data to the device.
>>
>> In this way, a simple check means to load just
>> sw-description+sw-description.sig, plus some bytes as overhead. And it
>> reduces the size of downloaded data in case some artifacts are already
>> on the device.
>
> You mean already installed or already downloaded to local storage?
I just means that the downloaded data to decide if the update should go
on is just the size of sw-description+signature.
>
>
>> I can also imagine a further step just for Hawkbit: multiple SWUs (as
>> software modules) can be put into a distribution, and SWUpdate can check
>> all of them finding the more appropriate for the update - for example, a
>> delta from Vx to Vy. The overhead becomes just the loading of
>> sw-description + signature, and it is worth as loading the whole image.
>
> Here you impose a particular form of building artifacts and introduce
> a convention.
Well, sw-description is the first file in SWU and this convention is
already defined.
> So you want to put multiple SWUs into one SWU as "payload"
This is already implemented (see gateway and swuforwarding handler), but
it is not what I meant.
What I mean here is to have a single SWU with multiple artifacts, and
just a set of artifacts is needed by a single device. Something like:
software = {
device-type-0001 = {
images: (..
}
device-type-0002 = {
images: (..
}
device-type-0003 = {
images: (..
}
Some of them can share an artifact or not - never mind. This already
works, and each device selects what it needs, discarding the rest, but
the whole SWU must be downloaded by all devices.
> of which one is chosen according to the sw-description's embedded Lua
> script(s) finding out which ones to install? This gives a lot of control
> to SWUpdate and hence the device.
This is already implemented, with or without Lua, via selections.
> This may or may not be wanted.
Of course - everything is configurable.
> The
> other "solution" is to have all combinations available in the cloud and
> pointing SWUpdate to a particular one depending on its former
> communication of versions.
This is what I really meant with Hawkbit, but let's say...it is a
different topic (another RFC..).
Hawkbit sends a list of "chunks" in the deployment answer.
{
"id" : "85",
"deployment" : {
"download" : "forced",
"update" : "forced",
"maintenanceWindow" : "available",
"chunks" : [ {
"part" : "bApp",
"version" : "1.0.84",
"name" : "oneapplication",
"artifacts" : [ {
"filename" : "SWU-device-0001.swu",
The whole list is sent back. A logic can be added to the suricatta
(Hawkbit server) to sort the entries and to find the most suitable for
the update. We could have like:
. SWU for whole software
- delta from version x
- delta from version y
- ....
And yes, I guess we need some convention and use "metadata" that are
stored with each software module in Hawkbit.
(note: i could also implement the use case above with different SWUs,
one for each device). A logic can then check which is the most suitable
starting from the current version, and load it.
>
>
> Regardless, I do think this is a valuable addition!
>
Thanks for review.
Best regards,
Stefano