Pull request #48: Feat/coreos device
Merge in ICO/coreos from feat/coreos-device to master * commit '57107f5cea3ff2e61701c18753cacdada8d1e04f': feat(swupdate): install swupdate-progress by default feat(coreos-device): add a coreos-device script and a devtool plugin
This commit is contained in:
commit
f086fe20de
|
|
@ -3,4 +3,4 @@ vscode-bitbake-build/
|
||||||
documentation/_build/
|
documentation/_build/
|
||||||
documentation/oe-logs
|
documentation/oe-logs
|
||||||
documentation/oe-workdir
|
documentation/oe-workdir
|
||||||
|
__pycache__
|
||||||
|
|
@ -46,7 +46,8 @@ Theses packages are needed on your build machine:
|
||||||
chrpath socat cpio python3 python3-pip python3-pexpect xz-utils \
|
chrpath socat cpio python3 python3-pip python3-pexpect xz-utils \
|
||||||
debianutils iputils-ping python3-git python3-jinja2 libegl1-mesa \
|
debianutils iputils-ping python3-git python3-jinja2 libegl1-mesa \
|
||||||
libsdl1.2-dev pylint3 xterm python3-subunit mesa-common-dev zstd \
|
libsdl1.2-dev pylint3 xterm python3-subunit mesa-common-dev zstd \
|
||||||
liblz4-tool bmap-tools efitools openssl sbsign
|
liblz4-tool bmap-tools efitools openssl sbsign python3-click \
|
||||||
|
python3-aiohttp
|
||||||
|
|
||||||
Use Git to clone CoreOS
|
Use Git to clone CoreOS
|
||||||
########################
|
########################
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ FEATURE_PACKAGES_networkmanager = "networkmanager networkmanager-nmcli"
|
||||||
FEATURE_PACKAGES_networkmanager-dev-tools = "networkmanager-nmtui"
|
FEATURE_PACKAGES_networkmanager-dev-tools = "networkmanager-nmtui"
|
||||||
FEATURE_PACKAGES_networkmanager-cockpit = "cockpit-networkmanager"
|
FEATURE_PACKAGES_networkmanager-cockpit = "cockpit-networkmanager"
|
||||||
|
|
||||||
FEATURE_PACKAGES_swupdate = "swupdate swupdate-client swupdate-lua"
|
FEATURE_PACKAGES_swupdate = "packagegroup-coreos-swupdate"
|
||||||
|
|
||||||
# The cockpit feature automatically install the corresponding
|
# The cockpit feature automatically install the corresponding
|
||||||
# *-cockpit FEATURES_PACKAGES for any image features
|
# *-cockpit FEATURES_PACKAGES for any image features
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
# This scripts use bitbake api, so it has to be under the GPL-2.0 license
|
||||||
|
|
||||||
|
"""
|
||||||
|
devtool wrapper for the coreos-device script
|
||||||
|
"""
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from devtool import setup_tinfoil, parse_recipe
|
||||||
|
|
||||||
|
class InvalidImage(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def swupdate_www_push(args, config, basepath, workspace):
|
||||||
|
"""
|
||||||
|
devtool update-device command
|
||||||
|
|
||||||
|
Use devtool library to get information about the image from the
|
||||||
|
bitbake context, then call update_device()
|
||||||
|
"""
|
||||||
|
# Only use inside a bitbake environment, we can import some more libs here
|
||||||
|
|
||||||
|
image = args.imagename
|
||||||
|
tinfoil = setup_tinfoil(basepath=basepath)
|
||||||
|
|
||||||
|
rd = parse_recipe(config, tinfoil, image, True)
|
||||||
|
if not rd:
|
||||||
|
# Error already shown
|
||||||
|
return 1
|
||||||
|
|
||||||
|
if not bb.data.inherits_class('coreos-image', rd):
|
||||||
|
raise InvalidImage("Image should be based on the coreos-image class")
|
||||||
|
|
||||||
|
if rd.getVar('COREOS_IMAGE_GENERATE_SWU') != "1":
|
||||||
|
raise InvalidImage("COREOS_IMAGE_GENERATE_SWU should be set to '1'")
|
||||||
|
|
||||||
|
image_name = rd.getVar('IMAGE_LINK_NAME')
|
||||||
|
outputdir = rd.getVar('DEPLOY_DIR_IMAGE')
|
||||||
|
|
||||||
|
return run(["coreos-device", "swupdate-www-push", f"{outputdir}/{image_name}.swu", args.target, f"--port={args.port}"])
|
||||||
|
|
||||||
|
|
||||||
|
def register_commands(subparsers, context):
|
||||||
|
"""Register devtool subcommands from this plugin"""
|
||||||
|
subparsers.add_subparser_group('swupdate', 'Updating device using swupdate')
|
||||||
|
cmd_push = subparsers.add_parser('swupdate-www-push', help='Update device with image',
|
||||||
|
description='Deploy an image to a device using swupdate',
|
||||||
|
group='swupdate')
|
||||||
|
cmd_push.add_argument('imagename', help='Image recipe to deploy')
|
||||||
|
cmd_push.add_argument('target', help='IP address of the running target')
|
||||||
|
cmd_push.add_argument('-P', '--port', default=8080, help='Specify port to use for connection to the target')
|
||||||
|
cmd_push.set_defaults(func=swupdate_www_push, fixed_setup=context.fixed_setup)
|
||||||
|
|
||||||
|
def run(cmd):
|
||||||
|
return subprocess.run(cmd).returncode
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
SUMMARY = "Add CoreOS swupdate backends and integrations packages"
|
||||||
|
DESCRIPTION = "Install swupdate and related components"
|
||||||
|
|
||||||
|
inherit packagegroup
|
||||||
|
|
||||||
|
|
||||||
|
PACKAGES = "\
|
||||||
|
${PN} \
|
||||||
|
"
|
||||||
|
|
||||||
|
RDEPENDS:${PN} = "\
|
||||||
|
swupdate \
|
||||||
|
swupdate-progress \
|
||||||
|
swupdate-client \
|
||||||
|
swupdate-lua \
|
||||||
|
"
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
"""
|
||||||
|
This is a low level script that communicate with a device over the network.
|
||||||
|
|
||||||
|
Currently only a swupdate-www-push subcommand is available.
|
||||||
|
|
||||||
|
It can be used this way:
|
||||||
|
coreos-device swupdate-www-push ~/coreos-image-demo-vm-x64.swu 192.168.122.21
|
||||||
|
|
||||||
|
This script doesn't interact with bitbake so it can't retrieve variables from
|
||||||
|
bitbake. For a more user friendly usage you can use devtool:
|
||||||
|
|
||||||
|
devtool swupdate-www-push coreos-image-demo 192.168.122.21
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import pathlib
|
||||||
|
import ipaddress
|
||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
|
||||||
|
# We need some modules that are not part of the Python Standard Library
|
||||||
|
# If they are not found, let's fail with an appropriate error message
|
||||||
|
try:
|
||||||
|
import click
|
||||||
|
import aiohttp
|
||||||
|
except ModuleNotFoundError as e:
|
||||||
|
print(f"Error: {e}", file=sys.stderr)
|
||||||
|
print(f"Please install the python3-{e.name} package", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
@click.group()
|
||||||
|
def cli():
|
||||||
|
pass
|
||||||
|
|
||||||
|
@cli.command()
|
||||||
|
@click.argument( 'image', type=click.Path(
|
||||||
|
exists=True, resolve_path=True, readable=True, dir_okay=False,
|
||||||
|
path_type=pathlib.Path,
|
||||||
|
))
|
||||||
|
@click.argument('device', type=ipaddress.ip_address)
|
||||||
|
@click.option('--port', type=click.IntRange(min=1, max=65535), default=8080)
|
||||||
|
def swupdate_www_push(image, device, port):
|
||||||
|
loop = asyncio.new_event_loop()
|
||||||
|
returncode = loop.run_until_complete(swupdate_www_push_async(image, device, port))
|
||||||
|
loop.close()
|
||||||
|
sys.exit(returncode)
|
||||||
|
|
||||||
|
|
||||||
|
async def swupdate_www_push_async(image, device, port):
|
||||||
|
url = f'http://{device}:{port}'
|
||||||
|
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
tasks = []
|
||||||
|
|
||||||
|
tasks.append(asyncio.ensure_future(swupdate_www_push_data(session, url, image)))
|
||||||
|
tasks.append(asyncio.ensure_future(swupdate_www_websocket_logger(session, url)))
|
||||||
|
|
||||||
|
responses = await asyncio.gather(*tasks)
|
||||||
|
|
||||||
|
# Return the sum of return value of all ours tasks
|
||||||
|
returncode = 0
|
||||||
|
for response in responses:
|
||||||
|
returncode += response
|
||||||
|
return returncode
|
||||||
|
|
||||||
|
|
||||||
|
async def swupdate_www_push_data(session, url, image):
|
||||||
|
|
||||||
|
with open(image, 'rb') as f:
|
||||||
|
data = {'file': f}
|
||||||
|
try:
|
||||||
|
async with session.post(f"{url}/upload", data=data) as resp:
|
||||||
|
if resp.status == 200:
|
||||||
|
click.secho("coreos-device: Update was successfull", bg="green")
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
click.secho(f"coreos-device: status {resp.status}, {await resp.text}", bg="red")
|
||||||
|
return resp.status
|
||||||
|
except Exception as e:
|
||||||
|
click.secho(f"coreos-device: ERROR: {e}", bg="red")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
async def swupdate_www_websocket_logger(session, url):
|
||||||
|
try:
|
||||||
|
async with session.ws_connect(f"{url}/ws") as ws:
|
||||||
|
async for msg in ws:
|
||||||
|
data = json.loads(msg.data)
|
||||||
|
if data["type"] == "message":
|
||||||
|
msg = data["text"]
|
||||||
|
if "Waiting for requests" in msg:
|
||||||
|
await ws.close()
|
||||||
|
return 1
|
||||||
|
elif "ERROR" in msg:
|
||||||
|
click.secho("swupdate: " + data["text"], fg='red')
|
||||||
|
else:
|
||||||
|
click.secho("swupdate: " + data["text"], fg='green')
|
||||||
|
|
||||||
|
if data["type"] == "status":
|
||||||
|
if data["status"] == "DONE":
|
||||||
|
await ws.close()
|
||||||
|
return 0
|
||||||
|
except Exception as e:
|
||||||
|
click.secho(f"coreos-device: ERROR: {e}", bg="red")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
cli()
|
||||||
Loading…
Reference in New Issue