|
| 1 | +From 1ea12b6d1bb067d255bec10eec6be18c73c24786 Mon Sep 17 00:00:00 2001 |
| 2 | +From: Paul Spooren <mail@aparcar.org> |
| 3 | +Date: Sun, 18 Aug 2019 10:47:24 -1000 |
| 4 | +Subject: [PATCH 2/2] build: add JSON info merge script |
| 5 | + |
| 6 | +Script creates three different files types with different functions: |
| 7 | + |
| 8 | +* map.json |
| 9 | + |
| 10 | +Stored in each target/subtarget folder containing a mapping between |
| 11 | +$(SUPPORTED_DEVICES) and JSON image info files. With this file a router |
| 12 | +can automatically obtain a sysuprade firmware image. |
| 13 | + |
| 14 | + { |
| 15 | + "devices": { |
| 16 | + "netgear,r7800": { |
| 17 | + "info": "openwrt-ipq806x-generic-netgear_r7800.json", |
| 18 | + "sha256": "9d24baeba60c4f592838bf7fdf2a1f87b61d4cd1432953d2960315a37149b89a" |
| 19 | + }, |
| 20 | + "r7800": { |
| 21 | + "info": "openwrt-ipq806x-generic-netgear_r7800.json", |
| 22 | + "sha256": "9d24baeba60c4f592838bf7fdf2a1f87b61d4cd1432953d2960315a37149b89a" |
| 23 | + } |
| 24 | + }, |
| 25 | + "metadata_version": 1, |
| 26 | + "target": "ipq806x/generic" |
| 27 | + } |
| 28 | + |
| 29 | +This mapping style allows device identifiers to change between releases, as it |
| 30 | +happend between ar71xx and ath79. |
| 31 | + |
| 32 | +Sha256sums are stored to verify file integrity, later more on that. |
| 33 | + |
| 34 | +This files is stored in $(OUTPUT_DIR) and contains a list of all |
| 35 | +targets where images exist for. |
| 36 | + |
| 37 | +* targets.json |
| 38 | + |
| 39 | + { |
| 40 | + "metadata_version": 1, |
| 41 | + "targets": { |
| 42 | + "ar71xx/generic": { |
| 43 | + "path": "targets/ath79/generic/map.json", |
| 44 | + "sha256": "434bb3de0e5e0a1240a714d08360667daeb11f92b36cea4fb590046392f2f3a7" |
| 45 | + }, |
| 46 | + "ath79/generic": { |
| 47 | + "path": "targets/ath79/generic/map.json", |
| 48 | + "sha256": "434bb3de0e5e0a1240a714d08360667daeb11f92b36cea4fb590046392f2f3a7" |
| 49 | + }, |
| 50 | + "ipq806x/generic": { |
| 51 | + "path": "targets/ipq806x/generic/map.json", |
| 52 | + "sha256": "6afba149bc0b0f22e19ebb49e10d7e2a98e80286f84eafcd13b65384f71f401f" |
| 53 | + }, |
| 54 | + "x86/64": { |
| 55 | + "path": "targets/x86/64/map.json", |
| 56 | + "sha256": "0f445ca9807d7f21624fff802f33320b30247eedfca636237317a144729c02e1" |
| 57 | + } |
| 58 | + } |
| 59 | + } |
| 60 | + |
| 61 | +This mapping style allows device targets to change between releases, as it |
| 62 | +happend between ar71xx and ath79. Devices searching for ar71xx are forwarded to |
| 63 | +compatible deivces of ath79. |
| 64 | + |
| 65 | +The workflow would be the following: |
| 66 | + |
| 67 | +* Devices request a (tbd) signed versions.json file from the server |
| 68 | +* Parse the file on device and optain the folder containing targets.json |
| 69 | +* Request targets.json and optain path to target specific map.json |
| 70 | +* Request map.json and optain device specific info file |
| 71 | +* Parse info file images for a sysupgrade |
| 72 | +* Request sysupgrade and flash device |
| 73 | + |
| 74 | +The versions.json would be signed containing sha256sums of targets.json files. |
| 75 | +This allows a chain of trust: |
| 76 | + |
| 77 | + versions.json -> targes.json -> map.json -> device_id.json -> sysupgrade.bin |
| 78 | + |
| 79 | +The versions.json file could look like the following: |
| 80 | + |
| 81 | + { |
| 82 | + "versions": [ |
| 83 | + { |
| 84 | + "name": "19.07-SNAPSHOT", |
| 85 | + "path": "releases/19.07-SNAPSHOTS", |
| 86 | + "sha256": "e659ccba1cd70124138726efabd9dd7a60ee220bf8471a91082c7e05488cac19" |
| 87 | + }, |
| 88 | + { |
| 89 | + "name": "Snapshot", |
| 90 | + "path": "snapshots", |
| 91 | + "sha256": "7c84e6140f35c70d6fe71cd1ca8e5d98d9f88a887a4550adf137d4fd2a1f0ea6" |
| 92 | + } |
| 93 | + ], |
| 94 | + "metadata_version": 1 |
| 95 | + } |
| 96 | + |
| 97 | +* overview.json |
| 98 | + |
| 99 | +Contains a mapping between device title(s) and device info files. Having |
| 100 | +this file greatly simpifies firmware image retreiveal. A (web) client can |
| 101 | +search trhough the mapping and show the results to an end user. |
| 102 | + |
| 103 | + { |
| 104 | + "devices": { |
| 105 | + " TL-WR840N v5": "targets/ramips/mt76x8/openwrt-ramips-mt76x8-tplink_tl-wr840n-v5.json", |
| 106 | + "7Links PX-4885 4M": "targets/ramips/rt305x/openwrt-ramips-rt305x-7links_px-4885-4m.json", |
| 107 | + "7Links PX-4885 8M": "targets/ramips/rt305x/openwrt-ramips-rt305x-7links_px-4885-8m.json", |
| 108 | + "8devices Carambola": "targets/ramips/rt305x/openwrt-ramips-rt305x-8devices_carambola.json", |
| 109 | + "8devices Carambola2": "targets/ath79/generic/openwrt-ath79-generic-8dev_carambola2.json", |
| 110 | + ... |
| 111 | + }, |
| 112 | + "metadata_version": 1 |
| 113 | + } |
| 114 | + |
| 115 | +Signed-off-by: Paul Spooren <mail@aparcar.org> |
| 116 | +--- |
| 117 | + Makefile | 5 ++ |
| 118 | + config/Config-build.in | 12 +++++ |
| 119 | + scripts/json_merge_image_info.py | 75 ++++++++++++++++++++++++++++++ |
| 120 | + target/imagebuilder/files/Makefile | 4 ++ |
| 121 | + 4 files changed, 96 insertions(+) |
| 122 | + create mode 100755 scripts/json_merge_image_info.py |
| 123 | + |
| 124 | +diff --git a/scripts/json_merge_image_info.py b/scripts/json_merge_image_info.py |
| 125 | +new file mode 100755 |
| 126 | +index 0000000000..bc61424dd6 |
| 127 | +--- /dev/null |
| 128 | ++++ b/scripts/json_merge_image_info.py |
| 129 | +@@ -0,0 +1,75 @@ |
| 130 | ++#!/usr/bin/env python3 |
| 131 | ++ |
| 132 | ++import json |
| 133 | ++import glob |
| 134 | ++import os |
| 135 | ++import hashlib |
| 136 | ++ |
| 137 | ++output_dir = os.path.join(os.environ.get("OUTPUT_DIR", "./bin")) |
| 138 | ++ |
| 139 | ++dev_overview = {"metadata_version": 1, "devices": {}} |
| 140 | ++targets = {"metadata_version": 1, "targets": {}} |
| 141 | ++ |
| 142 | ++# find all json files in ./bin/targets and create sha256 checksums |
| 143 | ++targets_dir = os.path.join(output_dir, "targets") |
| 144 | ++ |
| 145 | ++for root, dirs, files in os.walk(targets_dir): |
| 146 | ++ current_dir = root[len(targets_dir) + 1 :] |
| 147 | ++ # check if root contains one slash aka target/subtarget |
| 148 | ++ if current_dir.count("/") == 1: |
| 149 | ++ targets["targets"][current_dir] = { |
| 150 | ++ "path": "targets/{}/map.json".format(current_dir) |
| 151 | ++ } |
| 152 | ++ # initialize maps.json for target/subtarget |
| 153 | ++ dev_map = {"metadata_version": 1, "target": current_dir, "devices": {}} |
| 154 | ++ for file in files: |
| 155 | ++ # ignore existing map.json files |
| 156 | ++ if file.endswith(".json") and file != "map.json": |
| 157 | ++ # load device info |
| 158 | ++ with open(os.path.join(root, file), "r") as dev_file: |
| 159 | ++ dev_info = json.load(dev_file) |
| 160 | ++ |
| 161 | ++ # generate sha256sum |
| 162 | ++ with open(os.path.join(root, file), "rb") as dev_file_b: |
| 163 | ++ dev_sha256 = hashlib.sha256(dev_file_b.read()).hexdigest() |
| 164 | ++ |
| 165 | ++ # generate map entry for each supported device |
| 166 | ++ for supported in dev_info["supported_devices"]: |
| 167 | ++ dev_map["devices"][supported] = {} |
| 168 | ++ dev_map["devices"][supported]["info"] = file |
| 169 | ++ dev_map["devices"][supported]["sha256"] = dev_sha256 |
| 170 | ++ |
| 171 | ++ # path from overview.json to device info files |
| 172 | ++ dev_path = "{}/{}".format(dev_info["target"], file) |
| 173 | ++ |
| 174 | ++ # add title(s) to overview |
| 175 | ++ for title in dev_info.get("title"): |
| 176 | ++ if title in dev_overview["devices"]: |
| 177 | ++ print( |
| 178 | ++ "WARNING: '{}' pointing to '{}' already exists in overview and will be overwritten with '{}/{}'".format( |
| 179 | ++ title, |
| 180 | ++ dev_overview["devices"][title], |
| 181 | ++ dev_info["target"], |
| 182 | ++ file, |
| 183 | ++ ) |
| 184 | ++ ) |
| 185 | ++ dev_overview["devices"][title] = dev_path |
| 186 | ++ |
| 187 | ++ # write map.json to target/subtarget |
| 188 | ++ with open(os.path.join(root, "map.json"), "w") as dev_map_file: |
| 189 | ++ json.dump(dev_map, dev_map_file, sort_keys=True, indent=" ") |
| 190 | ++ |
| 191 | ++for target, data in targets["targets"].items(): |
| 192 | ++ # generate sha256sum of target/subtarget/maps.json |
| 193 | ++ with open(os.path.join(output_dir, data["path"]), "rb") as dev_file_b: |
| 194 | ++ targets["targets"][target]["sha256"] = hashlib.sha256( |
| 195 | ++ dev_file_b.read() |
| 196 | ++ ).hexdigest() |
| 197 | ++ |
| 198 | ++# write targets.json to ./bin/ |
| 199 | ++with open(os.path.join(output_dir, "targets.json"), "w") as targets_file: |
| 200 | ++ json.dump(targets, targets_file, sort_keys=True, indent=" ") |
| 201 | ++ |
| 202 | ++# write overview.json to ./bin/ |
| 203 | ++with open(os.path.join(output_dir, "overview.json"), "w") as dev_overview_file: |
| 204 | ++ json.dump(dev_overview, dev_overview_file, sort_keys=True, indent=" ") |
| 205 | +diff --git a/Makefile b/Makefile |
| 206 | +index 22b2731358..018abc9a23 100644 |
| 207 | +--- a/Makefile |
| 208 | ++++ b/Makefile |
| 209 | +@@ -118,6 +118,7 @@ _call_image: staging_dir/host/.prereq-build |
| 210 | + $(MAKE) -s prepare_rootfs |
| 211 | + $(MAKE) -s build_image |
| 212 | + $(MAKE) -s checksum |
| 213 | ++ $(if $(CONFIG_JSON_MERGE_IMAGE_INFO),$(MAKE) -s jsonmergeimageinfo) |
| 214 | + |
| 215 | + _call_manifest: FORCE |
| 216 | + rm -rf $(TARGET_DIR) |
| 217 | +@@ -201,6 +202,9 @@ image: |
| 218 | + $(if $(BIN_DIR),BIN_DIR="$(BIN_DIR)") \ |
| 219 | + $(if $(DISABLED_SERVICES),DISABLED_SERVICES="$(DISABLED_SERVICES)")) |
| 220 | + |
| 221 | ++jsonmergeimageinfo: FORCE |
| 222 | ++ $(SCRIPT_DIR)/json_merge_image_info.py |
| 223 | ++ |
| 224 | + manifest: FORCE |
| 225 | + $(MAKE) -s _check_profile |
| 226 | + (unset PROFILE FILES PACKAGES MAKEFLAGS; \ |
| 227 | +-- |
| 228 | +2.20.1 |
| 229 | + |
0 commit comments