Skip to content
This repository was archived by the owner on Jan 23, 2026. It is now read-only.

Commit f135cf6

Browse files
committed
jumpstarter_driver_flashers: fmt
1 parent 860deda commit f135cf6

6 files changed

Lines changed: 116 additions & 113 deletions

File tree

packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/bundle.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,26 @@ class FileAddress(BaseModel):
99
file: str
1010
address: str
1111

12+
1213
class DtbVariant(BaseModel):
1314
default: str
1415
address: str
1516
variants: dict[str, str]
1617

18+
1719
class FlasherLogin(BaseModel):
1820
login_prompt: str
1921
username: str | None = None
2022
password: str | None = None
2123
prompt: str
2224

25+
2326
class FlashBundleSpecV1Alpha1(BaseModel):
2427
manufacturer: str
2528
link: Optional[str]
2629
bootcmd: str
2730
shelltype: Literal["busybox"] = Field(default="busybox")
28-
login: FlasherLogin = Field(
29-
default_factory=lambda: FlasherLogin(
30-
login_prompt="login:",
31-
prompt="#")
32-
)
31+
login: FlasherLogin = Field(default_factory=lambda: FlasherLogin(login_prompt="login:", prompt="#"))
3332
default_target: str
3433
targets: dict[str, str]
3534
kernel: FileAddress
@@ -41,6 +40,7 @@ class FlashBundleSpecV1Alpha1(BaseModel):
4140
class ObjectMeta(BaseModel):
4241
name: str
4342

43+
4444
class FlasherBundleManifestV1Alpha1(BaseModel):
4545
apiVersion: Literal["jumpstarter.dev/v1alpha1"] = Field(default="jumpstarter.dev/v1alpha1")
4646
kind: Literal["FlashBundleManifest"] = Field(default="FlashBundleManifest")

packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py

Lines changed: 59 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
debug_console_option = click.option("--console-debug", is_flag=True, help="Enable console debug mode")
2727

28+
2829
@dataclass(kw_only=True)
2930
class BaseFlasherClient(FlasherClient, CompositeClient):
3031
"""
@@ -99,10 +100,11 @@ def flash(
99100
error_queue = Queue()
100101

101102
# Start the storage write operation in the background
102-
storage_thread = threading.Thread(target=self._transfer_bg_thread,
103-
args=(path, operator, operator_scheme,
104-
os_image_checksum, self.http.storage, error_queue),
105-
name="storage_transfer")
103+
storage_thread = threading.Thread(
104+
target=self._transfer_bg_thread,
105+
args=(path, operator, operator_scheme, os_image_checksum, self.http.storage, error_queue),
106+
name="storage_transfer",
107+
)
106108
storage_thread.start()
107109

108110
# Make the exporter download the bundle contents and set files in the right places
@@ -155,7 +157,6 @@ def flash(
155157
self.logger.info("Powering off target")
156158
self.power.off()
157159

158-
159160
def _flash_with_progress(self, console, manifest, path, image_url, target_path):
160161
"""Flash image to target device with progress monitoring.
161162
@@ -170,8 +171,8 @@ def _flash_with_progress(self, console, manifest, path, image_url, target_path):
170171
decompress_cmd = _get_decompression_command(path)
171172
flash_cmd = (
172173
f'( wget -q -O - "{image_url}" | '
173-
f'{decompress_cmd} '
174-
f'dd of={target_path} bs=64k iflag=fullblock oflag=direct) &'
174+
f"{decompress_cmd} "
175+
f"dd of={target_path} bs=64k iflag=fullblock oflag=direct) &"
175176
)
176177
console.sendline(flash_cmd)
177178
console.expect(manifest.spec.login.prompt, timeout=60)
@@ -190,16 +191,16 @@ def _flash_with_progress(self, console, manifest, path, image_url, target_path):
190191
if "No such file or directory" in console.before.decode(errors="ignore"):
191192
break
192193
data = console.before.decode(errors="ignore")
193-
match = re.search(r'pos:\s+(\d+)', data)
194+
match = re.search(r"pos:\s+(\d+)", data)
194195
if match:
195196
current_bytes = int(match.group(1))
196197
current_time = time.time()
197198
elapsed = current_time - last_time
198199

199200
if elapsed >= 1.0: # Update speed every second
200201
bytes_diff = current_bytes - last_pos
201-
speed_mb = (bytes_diff / (1024*1024)) / elapsed
202-
total_mb = current_bytes/(1024*1024)
202+
speed_mb = (bytes_diff / (1024 * 1024)) / elapsed
203+
total_mb = current_bytes / (1024 * 1024)
203204
self.logger.info(f"Flash progress: {total_mb:.2f} MB, Speed: {speed_mb:.2f} MB/s")
204205

205206
last_pos = current_bytes
@@ -209,7 +210,6 @@ def _flash_with_progress(self, console, manifest, path, image_url, target_path):
209210
console.sendline("sync")
210211
console.expect(manifest.spec.login.prompt, timeout=1200)
211212

212-
213213
def _get_target_device(self, target: str, manifest: FlasherBundleManifestV1Alpha1, console) -> str:
214214
"""Get the target device path from the manifest, resolving block devices if needed.
215215
@@ -229,15 +229,19 @@ def _get_target_device(self, target: str, manifest: FlasherBundleManifestV1Alpha
229229
raise ArgumentError(f"Target {target} not found in manifest")
230230

231231
if target_path.startswith("/sys/class/block#"):
232-
target_path = self._lookup_block_device(
233-
console, manifest.spec.login.prompt, target_path.split("#")[1])
232+
target_path = self._lookup_block_device(console, manifest.spec.login.prompt, target_path.split("#")[1])
234233

235234
return target_path
236235

237-
238-
def _transfer_bg_thread(self, src_path: PathBuf, src_operator: Operator, src_operator_scheme: str,
239-
known_hash: str | None,
240-
to_storage: OpendalClient, error_queue):
236+
def _transfer_bg_thread(
237+
self,
238+
src_path: PathBuf,
239+
src_operator: Operator,
240+
src_operator_scheme: str,
241+
known_hash: str | None,
242+
to_storage: OpendalClient,
243+
error_queue,
244+
):
241245
"""Transfer image to storage in the background
242246
243247
Args:
@@ -285,7 +289,6 @@ def _transfer_bg_thread(self, src_path: PathBuf, src_operator: Operator, src_ope
285289
raise
286290

287291
def _sha256_file(self, src_operator, src_path) -> str:
288-
289292
m = hashlib.sha256()
290293
with src_operator.open(src_path, "rb") as f:
291294
while True:
@@ -299,11 +302,13 @@ def _sha256_file(self, src_operator, src_path) -> str:
299302
def _create_metadata_and_json(self, src_operator, src_path) -> tuple[Metadata, str]:
300303
"""Create a metadata json string from a metadata object"""
301304
metadata = src_operator.stat(src_path)
302-
return metadata, json.dumps({
303-
"path": str(src_path),
304-
"content_length": metadata.content_length,
305-
"etag": metadata.etag,
306-
})
305+
return metadata, json.dumps(
306+
{
307+
"path": str(src_path),
308+
"content_length": metadata.content_length,
309+
"etag": metadata.etag,
310+
}
311+
)
307312

308313
def _lookup_block_device(self, console, prompt, address: str) -> str:
309314
"""Lookup block device for a given address.
@@ -317,7 +322,7 @@ def _lookup_block_device(self, console, prompt, address: str) -> str:
317322
# lrwxrwxrwx 1 root root 0 Jan 1
318323
# 00:00 mmcblk1 -> ../../devices/platform/bus@100000/4fb0000.mmc/mmc_host/mmc1/mmc1:aaaa/block/mmcblk1
319324
output = console.before.decode(errors="ignore")
320-
match = re.search(r'\s(\w+)\s->', output)
325+
match = re.search(r"\s(\w+)\s->", output)
321326
if match:
322327
return "/dev/" + match.group(1)
323328
else:
@@ -359,15 +364,13 @@ def _services_up(self):
359364
self.http.stop()
360365
self.tftp.stop()
361366

362-
363367
def _generate_uboot_env(self):
364368
"""Generate a uboot environment dictionary, may need specific overrides for different targets"""
365369
tftp_host = self.tftp.get_host()
366370
return {
367371
"serverip": tftp_host,
368372
}
369373

370-
371374
@contextmanager
372375
def _busybox(self):
373376
"""Start a busybox shell.
@@ -406,7 +409,7 @@ def _busybox(self):
406409
uboot.run_command(f"tftpboot {dtb_address} {dtb_filename}", timeout=120)
407410

408411
self.logger.info(f"Running boot command: {manifest.spec.bootcmd}")
409-
console.send(manifest.spec.bootcmd +"\n")
412+
console.send(manifest.spec.bootcmd + "\n")
410413

411414
# if manifest has login details, we need to login
412415
if manifest.spec.login.username:
@@ -438,7 +441,7 @@ def use_initram(self, path: PathBuf, operator: Operator | None = None):
438441
def use_kernel(self, path: PathBuf, operator: Operator | None = None):
439442
"""Use kernel file"""
440443
if operator is None:
441-
path, operator, operator_scheme = operator_for_path(path)
444+
path, operator, operator_scheme = operator_for_path(path)
442445

443446
...
444447

@@ -461,27 +464,37 @@ def base():
461464
@base.command()
462465
@click.argument("file")
463466
@click.option("--partition", type=str)
464-
@click.option('--os-image-checksum',
465-
help='SHA256 checksum of OS image (direct value)')
466-
@click.option('--os-image-checksum-file',
467-
help='File containing SHA256 checksum of OS image',
468-
type=click.Path(exists=True, dir_okay=False))
469-
@click.option('--force-exporter-http', is_flag=True, help='Force use of exporter HTTP')
470-
@click.option('--force-flash-bundle', type=str, help='Force use of a specific flasher OCI bundle')
467+
@click.option("--os-image-checksum", help="SHA256 checksum of OS image (direct value)")
468+
@click.option(
469+
"--os-image-checksum-file",
470+
help="File containing SHA256 checksum of OS image",
471+
type=click.Path(exists=True, dir_okay=False),
472+
)
473+
@click.option("--force-exporter-http", is_flag=True, help="Force use of exporter HTTP")
474+
@click.option("--force-flash-bundle", type=str, help="Force use of a specific flasher OCI bundle")
471475
@debug_console_option
472-
def flash(file, partition, os_image_checksum, os_image_checksum_file,
473-
console_debug, force_exporter_http, force_flash_bundle):
476+
def flash(
477+
file,
478+
partition,
479+
os_image_checksum,
480+
os_image_checksum_file,
481+
console_debug,
482+
force_exporter_http,
483+
force_flash_bundle,
484+
):
474485
"""Flash image to DUT from file"""
475486
if os_image_checksum_file and os.path.exists(os_image_checksum_file):
476487
with open(os_image_checksum_file) as f:
477488
os_image_checksum = f.read().strip().split()[0]
478489
self.logger.info(f"Read checksum from file: {os_image_checksum}")
479490

480491
self.set_console_debug(console_debug)
481-
self.flash(file,
482-
partition=partition,
483-
force_exporter_http=force_exporter_http,
484-
force_flash_bundle=force_flash_bundle)
492+
self.flash(
493+
file,
494+
partition=partition,
495+
force_exporter_http=force_exporter_http,
496+
force_flash_bundle=force_flash_bundle,
497+
)
485498

486499
@base.command()
487500
@debug_console_option
@@ -522,8 +535,8 @@ def _get_decompression_command(filename_or_url) -> str:
522535
filename = urlparse(filename_or_url).path.split("/")[-1]
523536

524537
filename = filename.lower()
525-
if filename.endswith(('.gz', '.gzip')):
526-
return 'zcat |'
527-
elif filename.endswith('.xz'):
528-
return 'xzcat |'
529-
return ''
538+
if filename.endswith((".gz", ".gzip")):
539+
return "zcat |"
540+
elif filename.endswith(".xz"):
541+
return "xzcat |"
542+
return ""

packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/driver.py

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,38 +13,41 @@
1313

1414
@dataclass(kw_only=True)
1515
class BaseFlasher(Driver):
16-
""" driver for Jumpstarter"""
16+
"""driver for Jumpstarter"""
17+
1718
flasher_bundle: str = field(default="quay.io/jumpstarter-dev/jumpstarter-flasher-test:latest")
1819
cache_dir: str = field(default="/var/lib/jumpstarter/flasher")
19-
tftp_dir : str = field(default="/var/lib/tftpboot")
20-
http_dir : str = field(default="/var/www/html")
20+
tftp_dir: str = field(default="/var/lib/tftpboot")
21+
http_dir: str = field(default="/var/www/html")
2122

2223
def __post_init__(self):
2324
if hasattr(super(), "__post_init__"):
2425
super().__post_init__()
2526

2627
# Ensure required children are present if not already instantiated
2728
# in configuration
28-
if 'tftp' not in self.children:
29-
self.children['tftp'] = Tftp(root_dir=self.tftp_dir)
30-
self.tftp = self.children['tftp']
29+
if "tftp" not in self.children:
30+
self.children["tftp"] = Tftp(root_dir=self.tftp_dir)
31+
self.tftp = self.children["tftp"]
3132

32-
if 'http' not in self.children:
33-
self.children['http'] = HttpServer(root_dir=self.http_dir)
34-
self.http = self.children['http']
33+
if "http" not in self.children:
34+
self.children["http"] = HttpServer(root_dir=self.http_dir)
35+
self.http = self.children["http"]
3536

3637
# Ensure required children are present, the following are not auto-created
37-
if 'serial' not in self.children:
38-
raise ConfigurationError("'serial' instance is required for BaseFlasher "
39-
"either via a ref ir a direct child instance")
38+
if "serial" not in self.children:
39+
raise ConfigurationError(
40+
"'serial' instance is required for BaseFlasher either via a ref ir a direct child instance"
41+
)
4042

41-
if 'power' not in self.children:
42-
raise ConfigurationError("'power' instance is required for BaseFlasher "
43-
"either via a ref ir a direct child instance")
43+
if "power" not in self.children:
44+
raise ConfigurationError(
45+
"'power' instance is required for BaseFlasher either via a ref ir a direct child instance"
46+
)
4447

4548
# bundles that have already been downloaded in the current session
4649
self._downloaded = {}
47-
self._use_dtb = None # use default dtb unless set by client
50+
self._use_dtb = None # use default dtb unless set by client
4851

4952
@classmethod
5053
def client(cls) -> str:
@@ -76,7 +79,6 @@ async def setup_flasher_bundle(self, force_flash_bundle: str | None = None):
7679
self.logger.info(f"Setting up dtb in tftp: {dtb_path}")
7780
await self.tftp.storage.copy_exporter_file(dtb_path, dtb_path.name)
7881

79-
8082
@export
8183
def set_dtb(self, handle):
8284
"""Provide a different dtb from client"""
@@ -87,8 +89,10 @@ async def use_dtb_variant(self, variant):
8789
"""Provide a different dtb reference from the flasher bundle"""
8890
manifest = await self.get_flasher_manifest()
8991
if manifest.get_dtb_file(variant) is None:
90-
raise ValueError(f"DTB variant {variant} not found in the flasher bundle, "
91-
f"available variants are: {list(manifest.spec.dtb.variants.keys())}")
92+
raise ValueError(
93+
f"DTB variant {variant} not found in the flasher bundle, "
94+
f"available variants are: {list(manifest.spec.dtb.variants.keys())}"
95+
)
9296
self._use_dtb = variant
9397

9498
def set_kernel(self, handle):
@@ -186,5 +190,6 @@ async def get_initram_address(self) -> str:
186190

187191
@dataclass(kw_only=True)
188192
class TIJ784S4Flasher(BaseFlasher):
189-
""" driver for Jumpstarter"""
193+
"""driver for Jumpstarter"""
194+
190195
flasher_bundle: str = "quay.io/jumpstarter-dev/jumpstarter-flasher-ti-j784s4:latest"

0 commit comments

Comments
 (0)