2020
2121from jumpstarter_driver_flashers .bundle import FlasherBundleManifestV1Alpha1
2222
23- from .uboot import UbootConsole
2423from jumpstarter .common .exceptions import ArgumentError
2524
2625debug_console_option = click .option ("--console-debug" , is_flag = True , help = "Enable console debug mode" )
2726
27+
2828@dataclass (kw_only = True )
2929class BaseFlasherClient (FlasherClient , CompositeClient ):
3030 """
@@ -41,6 +41,7 @@ def __post_init__(self):
4141 def set_console_debug (self , debug : bool ):
4242 """Set console debug mode"""
4343 self ._console_debug = debug
44+ # TODO: also set console debug on uboot client
4445
4546 @contextmanager
4647 def busybox_shell (self ):
@@ -60,12 +61,8 @@ def bootloader_shell(self):
6061 self .logger .info ("Setting up flasher bundle files in exporter" )
6162 self .call ("setup_flasher_bundle" )
6263 with self ._services_up ():
63- with self .serial .pexpect () as console :
64- if self ._console_debug :
65- console .logfile_read = sys .stdout .buffer
66- uboot = UbootConsole (console = console , power = self .power , logger = self .logger )
67- uboot .reboot_to_console ()
68- console .sendline ("" )
64+ with self .uboot .reboot_to_console ():
65+ pass
6966 yield self .serial
7067
7168 def flash (
@@ -99,10 +96,11 @@ def flash(
9996 error_queue = Queue ()
10097
10198 # 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" )
99+ storage_thread = threading .Thread (
100+ target = self ._transfer_bg_thread ,
101+ args = (path , operator , operator_scheme , os_image_checksum , self .http .storage , error_queue ),
102+ name = "storage_transfer" ,
103+ )
106104 storage_thread .start ()
107105
108106 # Make the exporter download the bundle contents and set files in the right places
@@ -155,7 +153,6 @@ def flash(
155153 self .logger .info ("Powering off target" )
156154 self .power .off ()
157155
158-
159156 def _flash_with_progress (self , console , manifest , path , image_url , target_path ):
160157 """Flash image to target device with progress monitoring.
161158
@@ -170,8 +167,8 @@ def _flash_with_progress(self, console, manifest, path, image_url, target_path):
170167 decompress_cmd = _get_decompression_command (path )
171168 flash_cmd = (
172169 f'( wget -q -O - "{ image_url } " | '
173- f' { decompress_cmd } '
174- f' dd of={ target_path } bs=64k iflag=fullblock oflag=direct) &'
170+ f" { decompress_cmd } "
171+ f" dd of={ target_path } bs=64k iflag=fullblock oflag=direct) &"
175172 )
176173 console .sendline (flash_cmd )
177174 console .expect (manifest .spec .login .prompt , timeout = 60 )
@@ -190,16 +187,16 @@ def _flash_with_progress(self, console, manifest, path, image_url, target_path):
190187 if "No such file or directory" in console .before .decode (errors = "ignore" ):
191188 break
192189 data = console .before .decode (errors = "ignore" )
193- match = re .search (r' pos:\s+(\d+)' , data )
190+ match = re .search (r" pos:\s+(\d+)" , data )
194191 if match :
195192 current_bytes = int (match .group (1 ))
196193 current_time = time .time ()
197194 elapsed = current_time - last_time
198195
199196 if elapsed >= 1.0 : # Update speed every second
200197 bytes_diff = current_bytes - last_pos
201- speed_mb = (bytes_diff / (1024 * 1024 )) / elapsed
202- total_mb = current_bytes / (1024 * 1024 )
198+ speed_mb = (bytes_diff / (1024 * 1024 )) / elapsed
199+ total_mb = current_bytes / (1024 * 1024 )
203200 self .logger .info (f"Flash progress: { total_mb :.2f} MB, Speed: { speed_mb :.2f} MB/s" )
204201
205202 last_pos = current_bytes
@@ -209,7 +206,6 @@ def _flash_with_progress(self, console, manifest, path, image_url, target_path):
209206 console .sendline ("sync" )
210207 console .expect (manifest .spec .login .prompt , timeout = 1200 )
211208
212-
213209 def _get_target_device (self , target : str , manifest : FlasherBundleManifestV1Alpha1 , console ) -> str :
214210 """Get the target device path from the manifest, resolving block devices if needed.
215211
@@ -229,15 +225,19 @@ def _get_target_device(self, target: str, manifest: FlasherBundleManifestV1Alpha
229225 raise ArgumentError (f"Target { target } not found in manifest" )
230226
231227 if target_path .startswith ("/sys/class/block#" ):
232- target_path = self ._lookup_block_device (
233- console , manifest .spec .login .prompt , target_path .split ("#" )[1 ])
228+ target_path = self ._lookup_block_device (console , manifest .spec .login .prompt , target_path .split ("#" )[1 ])
234229
235230 return target_path
236231
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 ):
232+ def _transfer_bg_thread (
233+ self ,
234+ src_path : PathBuf ,
235+ src_operator : Operator ,
236+ src_operator_scheme : str ,
237+ known_hash : str | None ,
238+ to_storage : OpendalClient ,
239+ error_queue ,
240+ ):
241241 """Transfer image to storage in the background
242242
243243 Args:
@@ -285,7 +285,6 @@ def _transfer_bg_thread(self, src_path: PathBuf, src_operator: Operator, src_ope
285285 raise
286286
287287 def _sha256_file (self , src_operator , src_path ) -> str :
288-
289288 m = hashlib .sha256 ()
290289 with src_operator .open (src_path , "rb" ) as f :
291290 while True :
@@ -299,11 +298,13 @@ def _sha256_file(self, src_operator, src_path) -> str:
299298 def _create_metadata_and_json (self , src_operator , src_path ) -> tuple [Metadata , str ]:
300299 """Create a metadata json string from a metadata object"""
301300 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- })
301+ return metadata , json .dumps (
302+ {
303+ "path" : str (src_path ),
304+ "content_length" : metadata .content_length ,
305+ "etag" : metadata .etag ,
306+ }
307+ )
307308
308309 def _lookup_block_device (self , console , prompt , address : str ) -> str :
309310 """Lookup block device for a given address.
@@ -317,7 +318,7 @@ def _lookup_block_device(self, console, prompt, address: str) -> str:
317318 # lrwxrwxrwx 1 root root 0 Jan 1
318319 # 00:00 mmcblk1 -> ../../devices/platform/bus@100000/4fb0000.mmc/mmc_host/mmc1/mmc1:aaaa/block/mmcblk1
319320 output = console .before .decode (errors = "ignore" )
320- match = re .search (r' \s(\w+)\s->' , output )
321+ match = re .search (r" \s(\w+)\s->" , output )
321322 if match :
322323 return "/dev/" + match .group (1 )
323324 else :
@@ -359,54 +360,53 @@ def _services_up(self):
359360 self .http .stop ()
360361 self .tftp .stop ()
361362
362-
363363 def _generate_uboot_env (self ):
364364 """Generate a uboot environment dictionary, may need specific overrides for different targets"""
365365 tftp_host = self .tftp .get_host ()
366366 return {
367367 "serverip" : tftp_host ,
368368 }
369369
370-
371370 @contextmanager
372371 def _busybox (self ):
373372 """Start a busybox shell.
374373
375374 This is a helper context manager that boots the device into uboot and returns a console object.
376375 """
377- with self .serial .pexpect () as console :
378- if self ._console_debug :
379- console .logfile_read = sys .stdout .buffer
380- uboot = UbootConsole (console = console , power = self .power , logger = self .logger )
381- # make sure that the device is booted into the uboot console
382- uboot .reboot_to_console ()
376+
377+ # make sure that the device is booted into the uboot console
378+ with self .uboot .reboot_to_console ():
383379 # run dhcp discovery and gather details useful for later
384- self ._dhcp_details = uboot .setup_dhcp ()
380+ self ._dhcp_details = self . uboot .setup_dhcp ()
385381 self .logger .info (f"discovered dhcp details: { self ._dhcp_details } " )
386382
387383 # configure the environment necessary
388384 env = self ._generate_uboot_env ()
389- uboot .set_env_dict (env )
385+ self . uboot .set_env_dict (env )
390386
391387 # load any necessary files to RAM from the tftp storage
392388 manifest = self .manifest
393389 kernel_filename = Path (manifest .get_kernel_file ()).name
394390 kernel_address = manifest .get_kernel_address ()
395391
396- uboot .run_command (f"tftpboot { kernel_address } { kernel_filename } " , timeout = 120 )
392+ self . uboot .run_command (f"tftpboot { kernel_address } { kernel_filename } " , timeout = 120 )
397393
398394 if manifest .get_initram_file ():
399395 initram_filename = Path (manifest .get_initram_file ()).name
400396 initram_address = manifest .get_initram_address ()
401- uboot .run_command (f"tftpboot { initram_address } { initram_filename } " , timeout = 120 )
397+ self . uboot .run_command (f"tftpboot { initram_address } { initram_filename } " , timeout = 120 )
402398
403399 if manifest .get_dtb_file ():
404400 dtb_filename = Path (manifest .get_dtb_file ()).name
405401 dtb_address = manifest .get_dtb_address ()
406- uboot .run_command (f"tftpboot { dtb_address } { dtb_filename } " , timeout = 120 )
402+ self .uboot .run_command (f"tftpboot { dtb_address } { dtb_filename } " , timeout = 120 )
403+
404+ with self .serial .pexpect () as console :
405+ if self ._console_debug :
406+ console .logfile_read = sys .stdout .buffer
407407
408408 self .logger .info (f"Running boot command: { manifest .spec .bootcmd } " )
409- console .send (manifest .spec .bootcmd + "\n " )
409+ console .send (manifest .spec .bootcmd + "\n " )
410410
411411 # if manifest has login details, we need to login
412412 if manifest .spec .login .username :
@@ -438,7 +438,7 @@ def use_initram(self, path: PathBuf, operator: Operator | None = None):
438438 def use_kernel (self , path : PathBuf , operator : Operator | None = None ):
439439 """Use kernel file"""
440440 if operator is None :
441- path , operator , operator_scheme = operator_for_path (path )
441+ path , operator , operator_scheme = operator_for_path (path )
442442
443443 ...
444444
@@ -461,27 +461,37 @@ def base():
461461 @base .command ()
462462 @click .argument ("file" )
463463 @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' )
464+ @click .option ("--os-image-checksum" , help = "SHA256 checksum of OS image (direct value)" )
465+ @click .option (
466+ "--os-image-checksum-file" ,
467+ help = "File containing SHA256 checksum of OS image" ,
468+ type = click .Path (exists = True , dir_okay = False ),
469+ )
470+ @click .option ("--force-exporter-http" , is_flag = True , help = "Force use of exporter HTTP" )
471+ @click .option ("--force-flash-bundle" , type = str , help = "Force use of a specific flasher OCI bundle" )
471472 @debug_console_option
472- def flash (file , partition , os_image_checksum , os_image_checksum_file ,
473- console_debug , force_exporter_http , force_flash_bundle ):
473+ def flash (
474+ file ,
475+ partition ,
476+ os_image_checksum ,
477+ os_image_checksum_file ,
478+ console_debug ,
479+ force_exporter_http ,
480+ force_flash_bundle ,
481+ ):
474482 """Flash image to DUT from file"""
475483 if os_image_checksum_file and os .path .exists (os_image_checksum_file ):
476484 with open (os_image_checksum_file ) as f :
477485 os_image_checksum = f .read ().strip ().split ()[0 ]
478486 self .logger .info (f"Read checksum from file: { os_image_checksum } " )
479487
480488 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 )
489+ self .flash (
490+ file ,
491+ partition = partition ,
492+ force_exporter_http = force_exporter_http ,
493+ force_flash_bundle = force_flash_bundle ,
494+ )
485495
486496 @base .command ()
487497 @debug_console_option
@@ -522,8 +532,8 @@ def _get_decompression_command(filename_or_url) -> str:
522532 filename = urlparse (filename_or_url ).path .split ("/" )[- 1 ]
523533
524534 filename = filename .lower ()
525- if filename .endswith ((' .gz' , ' .gzip' )):
526- return ' zcat |'
527- elif filename .endswith (' .xz' ):
528- return ' xzcat |'
529- return ''
535+ if filename .endswith ((" .gz" , " .gzip" )):
536+ return " zcat |"
537+ elif filename .endswith (" .xz" ):
538+ return " xzcat |"
539+ return ""
0 commit comments