sima-cli 0.0.24__py3-none-any.whl → 0.0.25__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sima_cli/__version__.py +1 -1
- sima_cli/cli.py +6 -4
- sima_cli/storage/nvme.py +9 -13
- sima_cli/storage/sdcard.py +31 -7
- sima_cli/update/local.py +8 -6
- sima_cli/update/netboot.py +25 -2
- sima_cli/update/remote.py +13 -5
- sima_cli/update/updater.py +39 -5
- sima_cli/utils/env.py +31 -0
- {sima_cli-0.0.24.dist-info → sima_cli-0.0.25.dist-info}/METADATA +1 -1
- {sima_cli-0.0.24.dist-info → sima_cli-0.0.25.dist-info}/RECORD +15 -15
- {sima_cli-0.0.24.dist-info → sima_cli-0.0.25.dist-info}/WHEEL +0 -0
- {sima_cli-0.0.24.dist-info → sima_cli-0.0.25.dist-info}/entry_points.txt +0 -0
- {sima_cli-0.0.24.dist-info → sima_cli-0.0.25.dist-info}/licenses/LICENSE +0 -0
- {sima_cli-0.0.24.dist-info → sima_cli-0.0.25.dist-info}/top_level.txt +0 -0
sima_cli/__version__.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
# sima_cli/__version__.py
|
2
|
-
__version__ = "0.0.
|
2
|
+
__version__ = "0.0.25"
|
sima_cli/cli.py
CHANGED
@@ -235,9 +235,10 @@ def show_mla_memory_usage(ctx):
|
|
235
235
|
@click.option("-b", "--boardtype", type=click.Choice(["modalix", "mlsoc"], case_sensitive=False), default="mlsoc", show_default=True, help="Target board type.")
|
236
236
|
@click.option("-t", "--fwtype", type=click.Choice(["yocto", "elxr"], case_sensitive=False), default="yocto", show_default=True, help="Target firmware type.")
|
237
237
|
@click.option("-n", "--netboot", is_flag=True, default=False, show_default=True, help="Prepare image for network boot and launch TFTP server.")
|
238
|
+
@click.option("-r", "--rootfs", required=False, help="Custom root fs folders (internal use only)")
|
238
239
|
@click.option("-a", "--autoflash", is_flag=True, default=False, show_default=True, help="Net boot the DevKit and automatically flash the internal storage - TBD")
|
239
240
|
@click.pass_context
|
240
|
-
def bootimg_cmd(ctx, version, boardtype, netboot, autoflash, fwtype):
|
241
|
+
def bootimg_cmd(ctx, version, boardtype, netboot, autoflash, fwtype, rootfs):
|
241
242
|
"""
|
242
243
|
Download and burn a removable media or setup TFTP boot.
|
243
244
|
|
@@ -261,11 +262,12 @@ def bootimg_cmd(ctx, version, boardtype, netboot, autoflash, fwtype):
|
|
261
262
|
click.echo(f" 🔹 Board Type: {boardtype}")
|
262
263
|
click.echo(f" 🔹 F/W Type : {fwtype}")
|
263
264
|
click.echo(f" 🔹 F/W Flavor: headless")
|
265
|
+
click.echo(f" 🔹 Custom RootFS: {rootfs}")
|
264
266
|
|
265
267
|
try:
|
266
268
|
boardtype = boardtype if boardtype != 'mlsoc' else 'davinci'
|
267
269
|
if netboot or autoflash:
|
268
|
-
setup_netboot(version, boardtype, internal, autoflash, flavor='headless')
|
270
|
+
setup_netboot(version, boardtype, internal, autoflash, flavor='headless', rootfs=rootfs)
|
269
271
|
click.echo("✅ Netboot image prepared and TFTP server is running.")
|
270
272
|
else:
|
271
273
|
write_image(version, boardtype, fwtype, internal, flavor='headless')
|
@@ -398,9 +400,9 @@ def nvme_cmd(ctx, operation):
|
|
398
400
|
|
399
401
|
Available operations:
|
400
402
|
|
401
|
-
format - Format the NVMe drive and mount it to /
|
403
|
+
format - Format the NVMe drive and mount it to /media/nvme
|
402
404
|
|
403
|
-
remount - Remount the existing NVMe partition to /
|
405
|
+
remount - Remount the existing NVMe partition to /media/nvme
|
404
406
|
|
405
407
|
Example:
|
406
408
|
sima-cli nvme format
|
sima_cli/storage/nvme.py
CHANGED
@@ -35,7 +35,7 @@ def format_nvme(lbaf_index):
|
|
35
35
|
|
36
36
|
def add_nvme_to_fstab():
|
37
37
|
"""
|
38
|
-
Add UUID-based entry for /dev/nvme0n1p1 to /etc/fstab for persistent mounting at /
|
38
|
+
Add UUID-based entry for /dev/nvme0n1p1 to /etc/fstab for persistent mounting at /media/nvme.
|
39
39
|
Only appends if the entry does not already exist.
|
40
40
|
Requires root permission to run blkid and modify /etc/fstab.
|
41
41
|
"""
|
@@ -56,12 +56,12 @@ def add_nvme_to_fstab():
|
|
56
56
|
fs_type = type_match.group(1)
|
57
57
|
|
58
58
|
# Construct the fstab line
|
59
|
-
fstab_entry = f"UUID={uuid} /
|
59
|
+
fstab_entry = f"UUID={uuid} /media/nvme {fs_type} defaults,noatime 0 0"
|
60
60
|
|
61
61
|
# Check if entry already exists
|
62
62
|
with open(fstab_path, "r") as f:
|
63
63
|
for line in f:
|
64
|
-
if uuid in line or "/
|
64
|
+
if uuid in line or "/media/nvme" in line:
|
65
65
|
click.echo("ℹ️ NVMe UUID or mount point already exists in /etc/fstab.")
|
66
66
|
return
|
67
67
|
|
@@ -78,19 +78,19 @@ def add_nvme_to_fstab():
|
|
78
78
|
def mount_nvme():
|
79
79
|
try:
|
80
80
|
# Create mount point
|
81
|
-
subprocess.run("sudo mkdir -p /
|
81
|
+
subprocess.run("sudo mkdir -p /media/nvme", shell=True, check=True)
|
82
82
|
|
83
83
|
# Mount the NVMe partition
|
84
|
-
subprocess.run("sudo mount /dev/nvme0n1p1 /
|
84
|
+
subprocess.run("sudo mount /dev/nvme0n1p1 /media/nvme", shell=True, check=True)
|
85
85
|
|
86
86
|
add_nvme_to_fstab()
|
87
87
|
|
88
88
|
subprocess.run("sudo mount -a", shell=True, check=True)
|
89
89
|
|
90
90
|
# Change ownership to user 'sima'
|
91
|
-
subprocess.run("sudo chown sima:sima /
|
91
|
+
subprocess.run("sudo chown sima:sima /media/nvme", shell=True, check=True)
|
92
92
|
|
93
|
-
subprocess.run("sudo chmod 755 /
|
93
|
+
subprocess.run("sudo chmod 755 /media/nvme", shell=True, check=True)
|
94
94
|
|
95
95
|
|
96
96
|
print("✅ NVMe mounted and write permission granted to user 'sima'.")
|
@@ -99,10 +99,6 @@ def mount_nvme():
|
|
99
99
|
print(f"❌ Error during NVMe mount: {e}")
|
100
100
|
|
101
101
|
def nvme_format():
|
102
|
-
if not is_modalix_devkit():
|
103
|
-
click.echo("❌ This command can only be run on the Modalix DevKit.")
|
104
|
-
return
|
105
|
-
|
106
102
|
nvme_info = scan_nvme()
|
107
103
|
if not nvme_info:
|
108
104
|
click.echo("❌ No NVMe drive detected.")
|
@@ -121,12 +117,12 @@ def nvme_format():
|
|
121
117
|
|
122
118
|
try:
|
123
119
|
# Unmount before formatting, ignore error if not mounted
|
124
|
-
subprocess.run("sudo umount /
|
120
|
+
subprocess.run("sudo umount /media/nvme", shell=True, check=False)
|
125
121
|
|
126
122
|
# Format and mount
|
127
123
|
format_nvme(lbaf_index)
|
128
124
|
mount_nvme()
|
129
|
-
click.echo("✅ NVMe drive formatted and mounted at /
|
125
|
+
click.echo("✅ NVMe drive formatted and mounted at /media/nvme.")
|
130
126
|
except subprocess.CalledProcessError:
|
131
127
|
click.echo("❌ Formatting process failed.")
|
132
128
|
|
sima_cli/storage/sdcard.py
CHANGED
@@ -22,23 +22,46 @@ def find_mkfs_ext4() -> str:
|
|
22
22
|
return None
|
23
23
|
|
24
24
|
|
25
|
+
def wipe_existing_partitions(device_path: str):
|
26
|
+
"""
|
27
|
+
Fully wipe all partition entries using sgdisk or dd to ensure a clean disk.
|
28
|
+
"""
|
29
|
+
click.echo(f"💣 Wiping partition table on {device_path}")
|
30
|
+
subprocess.run(["sudo", "sgdisk", "--zap-all", device_path], check=False)
|
31
|
+
subprocess.run(["sudo", "wipefs", "--all", device_path], check=False)
|
32
|
+
|
33
|
+
# Optional: clear first 1MB and last 1MB
|
34
|
+
subprocess.run(["sudo", "dd", "if=/dev/zero", f"of={device_path}", "bs=1M", "count=1"], check=False)
|
35
|
+
subprocess.run(["sudo", "blockdev", "--rereadpt", device_path], check=False)
|
36
|
+
|
25
37
|
def kill_partition_users(device_path: str):
|
26
|
-
"""
|
38
|
+
"""
|
39
|
+
Unmount all mounted partitions of the device, and kill only real user processes if needed.
|
40
|
+
"""
|
27
41
|
try:
|
42
|
+
# List all partitions of the device (e.g., /dev/sda1, /dev/sda2)
|
28
43
|
output = subprocess.check_output(["lsblk", "-n", "-o", "NAME", device_path]).decode().strip().splitlines()
|
29
44
|
partitions = [f"/dev/{line.strip()}" for line in output if line.strip() and f"/dev/{line.strip()}" != device_path]
|
30
|
-
|
31
|
-
for
|
45
|
+
|
46
|
+
for part in partitions:
|
47
|
+
# Check if the partition is mounted
|
48
|
+
mountpoint = subprocess.run(["findmnt", "-n", "-o", "TARGET", part],
|
49
|
+
stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
|
50
|
+
if mountpoint.returncode == 0 and mountpoint.stdout.strip():
|
51
|
+
# It's mounted — unmount it
|
52
|
+
click.echo(f"🛑 Unmounting mounted partition {part}")
|
53
|
+
subprocess.run(["sudo", "umount", part], check=False)
|
54
|
+
|
55
|
+
# Optionally kill any real user-space processes using the partition
|
32
56
|
try:
|
33
|
-
users = subprocess.check_output(["sudo", "lsof",
|
57
|
+
users = subprocess.check_output(["sudo", "lsof", part], stderr=subprocess.DEVNULL).decode().splitlines()
|
34
58
|
pids = {line.split()[1] for line in users[1:] if line.strip()}
|
35
59
|
for pid in pids:
|
60
|
+
click.echo(f"🔪 Killing PID {pid} using {part}")
|
36
61
|
subprocess.run(["sudo", "kill", "-9", pid], check=False)
|
37
62
|
except subprocess.CalledProcessError:
|
38
|
-
|
39
|
-
continue
|
63
|
+
pass
|
40
64
|
|
41
|
-
# Let the system settle
|
42
65
|
time.sleep(1)
|
43
66
|
|
44
67
|
except Exception as e:
|
@@ -121,6 +144,7 @@ def sdcard_format():
|
|
121
144
|
unmount_device(selected_path)
|
122
145
|
force_release_device(selected_path)
|
123
146
|
kill_partition_users(selected_path)
|
147
|
+
wipe_existing_partitions(selected_path)
|
124
148
|
|
125
149
|
try:
|
126
150
|
create_partition_table(selected_path)
|
sima_cli/update/local.py
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
import os
|
2
|
-
import
|
3
|
-
import tempfile
|
4
|
-
from typing import List, Tuple
|
2
|
+
from typing import Tuple
|
5
3
|
import pty
|
6
4
|
import click
|
7
5
|
|
6
|
+
from sima_cli.utils.env import is_board_running_full_image, get_exact_devkit_type
|
7
|
+
|
8
8
|
def _run_local_cmd(command: str, passwd: str) -> bool:
|
9
9
|
"""
|
10
10
|
Run a local command using a pseudo-terminal (pty) to force live output flushing,
|
@@ -44,12 +44,12 @@ def _run_local_cmd(command: str, passwd: str) -> bool:
|
|
44
44
|
return False
|
45
45
|
|
46
46
|
|
47
|
-
def get_local_board_info() -> Tuple[str, str]:
|
47
|
+
def get_local_board_info() -> Tuple[str, str, bool]:
|
48
48
|
"""
|
49
49
|
Retrieve the local board type and build version by reading /etc/build or /etc/buildinfo.
|
50
50
|
|
51
51
|
Returns:
|
52
|
-
(board_type, build_version): Tuple of strings, or ('', '') on failure.
|
52
|
+
(board_type, build_version, fdt_name, full_image): Tuple of strings, or ('', '') on failure.
|
53
53
|
"""
|
54
54
|
board_type = ""
|
55
55
|
build_version = ""
|
@@ -70,7 +70,9 @@ def get_local_board_info() -> Tuple[str, str]:
|
|
70
70
|
except Exception:
|
71
71
|
continue
|
72
72
|
|
73
|
-
|
73
|
+
fdt_name = get_exact_devkit_type()
|
74
|
+
|
75
|
+
return board_type, build_version, fdt_name, is_board_running_full_image()
|
74
76
|
|
75
77
|
|
76
78
|
def push_and_update_local_board(troot_path: str, palette_path: str, passwd: str, flavor: str):
|
sima_cli/update/netboot.py
CHANGED
@@ -20,6 +20,7 @@ SOCK_TIMEOUT = 2 # Timeout for faster retransmits
|
|
20
20
|
|
21
21
|
log = logging.getLogger("tftpy.InteractiveTftpServer")
|
22
22
|
emmc_image_paths = []
|
23
|
+
custom_rootfs = ''
|
23
24
|
|
24
25
|
def flash_emmc(client_manager, emmc_image_paths):
|
25
26
|
"""Flash eMMC on a selected client device."""
|
@@ -127,7 +128,7 @@ class ClientManager:
|
|
127
128
|
|
128
129
|
while not self.shutdown_event.is_set():
|
129
130
|
try:
|
130
|
-
board_type, build_version, _ = get_remote_board_info(ip)
|
131
|
+
board_type, build_version, _, _ = get_remote_board_info(ip)
|
131
132
|
|
132
133
|
if board_type and build_version:
|
133
134
|
with self.lock:
|
@@ -346,7 +347,7 @@ def run_cli(client_manager):
|
|
346
347
|
click.echo("\n🛑 Exiting netboot session.")
|
347
348
|
return True
|
348
349
|
|
349
|
-
def setup_netboot(version: str, board: str, internal: bool = False, autoflash: bool = False, flavor: str = 'headless'):
|
350
|
+
def setup_netboot(version: str, board: str, internal: bool = False, autoflash: bool = False, flavor: str = 'headless', rootfs: str = ''):
|
350
351
|
"""
|
351
352
|
Download and serve a bootable image for network boot over TFTP with client monitoring.
|
352
353
|
|
@@ -356,11 +357,13 @@ def setup_netboot(version: str, board: str, internal: bool = False, autoflash: b
|
|
356
357
|
internal (bool): Whether to use internal download sources. Defaults to False.
|
357
358
|
autoflash (bool): Whether to automatically flash the devkit when networked booted. Defaults to False.
|
358
359
|
flavor (str): The software flavor, can be either headless or full.
|
360
|
+
rootfs (str): The root fs folder, which contains the .wic.gz file and the .bmap file, for custom image writing.
|
359
361
|
|
360
362
|
Raises:
|
361
363
|
RuntimeError: If the download or TFTP setup fails.
|
362
364
|
"""
|
363
365
|
global emmc_image_paths
|
366
|
+
global custom_rootfs
|
364
367
|
|
365
368
|
if platform.system() == "Windows":
|
366
369
|
click.echo("❌ Netboot with built-in TFTP is not supported on Windows. Use macOS or Linux.")
|
@@ -373,10 +376,30 @@ def setup_netboot(version: str, board: str, internal: bool = False, autoflash: b
|
|
373
376
|
raise ValueError("Expected list of extracted files, got something else.")
|
374
377
|
extract_dir = os.path.dirname(file_list[0])
|
375
378
|
click.echo(f"📁 Image extracted to: {extract_dir}")
|
379
|
+
|
376
380
|
# Extract specific image paths
|
377
381
|
wic_gz_file = next((f for f in file_list if f.endswith(".wic.gz")), None)
|
378
382
|
bmap_file = next((f for f in file_list if f.endswith(".wic.bmap")), None)
|
379
383
|
emmc_image_paths = [p for p in [wic_gz_file, bmap_file] if p]
|
384
|
+
|
385
|
+
# Check global custom_rootfs before doing anything else
|
386
|
+
custom_rootfs = rootfs
|
387
|
+
if custom_rootfs:
|
388
|
+
if not os.path.isdir(custom_rootfs):
|
389
|
+
raise RuntimeError(f"❌ custom_rootfs path is not a directory: {custom_rootfs}")
|
390
|
+
|
391
|
+
import glob
|
392
|
+
wic_gz_file = next(iter(glob.glob(os.path.join(custom_rootfs, "*.wic.gz"))), None)
|
393
|
+
bmap_file = next(iter(glob.glob(os.path.join(custom_rootfs, "*.wic.bmap"))), None)
|
394
|
+
|
395
|
+
if not (wic_gz_file and bmap_file):
|
396
|
+
raise RuntimeError(
|
397
|
+
f"❌ custom_rootfs '{custom_rootfs}' must contain both .wic.gz and .wic.bmap files."
|
398
|
+
)
|
399
|
+
|
400
|
+
emmc_image_paths = [wic_gz_file, bmap_file]
|
401
|
+
click.echo(f"📁 Using custom_rootfs: {custom_rootfs}")
|
402
|
+
|
380
403
|
click.echo(f"📁 eMMC image paths are: {emmc_image_paths}")
|
381
404
|
|
382
405
|
except Exception as e:
|
sima_cli/update/remote.py
CHANGED
@@ -59,11 +59,12 @@ def get_remote_board_info(ip: str, passwd: str = DEFAULT_PASSWORD) -> Tuple[str,
|
|
59
59
|
ip (str): IP address of the board.
|
60
60
|
|
61
61
|
Returns:
|
62
|
-
(board_type, build_version, fdt_name): Tuple of strings, or ('', '', '') on failure.
|
62
|
+
(board_type, build_version, fdt_name, full_image): Tuple of strings, or ('', '', '') on failure.
|
63
63
|
"""
|
64
64
|
board_type = ""
|
65
65
|
build_version = ""
|
66
66
|
fdt_name = ""
|
67
|
+
full_image = False
|
67
68
|
|
68
69
|
try:
|
69
70
|
ssh = paramiko.SSHClient()
|
@@ -71,13 +72,20 @@ def get_remote_board_info(ip: str, passwd: str = DEFAULT_PASSWORD) -> Tuple[str,
|
|
71
72
|
ssh.connect(ip, username=DEFAULT_USER, password=passwd, timeout=10)
|
72
73
|
|
73
74
|
# Retrieve build info
|
74
|
-
|
75
|
+
_, stdout, _ = ssh.exec_command("cat /etc/build 2>/dev/null || cat /etc/buildinfo 2>/dev/null")
|
75
76
|
build_output = stdout.read().decode()
|
76
77
|
|
77
78
|
# Retrieve fdt_name from fw_printenv
|
78
|
-
|
79
|
+
_, stdout, _ = ssh.exec_command("fw_printenv fdt_name 2>/dev/null")
|
79
80
|
fdt_output = stdout.read().decode()
|
80
81
|
|
82
|
+
# 3) NVMe presence (ensure sbin is in PATH for non-root shells)
|
83
|
+
# Note: 'command -v' is POSIX; 'which' as fallback on some busybox setups.
|
84
|
+
nvme_check_cmd = r'PATH="$PATH:/usr/sbin:/sbin"; command -v nvme >/dev/null 2>&1 || which nvme >/dev/null 2>&1; echo $?'
|
85
|
+
_, stdout, _ = ssh.exec_command(nvme_check_cmd)
|
86
|
+
nvme_rc = stdout.read().decode().strip()
|
87
|
+
full_image = (nvme_rc == "0")
|
88
|
+
|
81
89
|
ssh.close()
|
82
90
|
|
83
91
|
for line in build_output.splitlines():
|
@@ -91,11 +99,11 @@ def get_remote_board_info(ip: str, passwd: str = DEFAULT_PASSWORD) -> Tuple[str,
|
|
91
99
|
if line.startswith("fdt_name"):
|
92
100
|
fdt_name = line.split("=", 1)[-1].strip()
|
93
101
|
|
94
|
-
return board_type, build_version, fdt_name
|
102
|
+
return board_type, build_version, fdt_name, full_image
|
95
103
|
|
96
104
|
except Exception as e:
|
97
105
|
click.echo(f"Unable to retrieve board info with error: {e}, board may be still booting.")
|
98
|
-
return "", "", ""
|
106
|
+
return "", "", "", False
|
99
107
|
|
100
108
|
|
101
109
|
def _scp_file(sftp, local_path: str, remote_path: str):
|
sima_cli/update/updater.py
CHANGED
@@ -71,6 +71,35 @@ def _resolve_firmware_url(version_or_url: str, board: str, internal: bool = Fals
|
|
71
71
|
|
72
72
|
return download_url
|
73
73
|
|
74
|
+
def _confirm_flavor_switching(full_image: bool, flavor: str) -> str:
|
75
|
+
"""
|
76
|
+
Check if the system is running a different flavor from the board and prompt user to confirm switching.
|
77
|
+
|
78
|
+
Args:
|
79
|
+
full_image (bool): Indicates if the current image is full
|
80
|
+
flavor (str): The desired flavor of the image ('full' or 'headless')
|
81
|
+
|
82
|
+
Returns:
|
83
|
+
str: The flavor to use ('full' or 'headless')
|
84
|
+
"""
|
85
|
+
if (full_image and flavor != 'full') or (not full_image and flavor == 'full'):
|
86
|
+
click.echo(f"🔄 The current image running on the board has a different flavor from what you specified ({flavor}).")
|
87
|
+
click.echo("Please choose an option:")
|
88
|
+
choice = click.prompt(
|
89
|
+
f" a) Switch to the specified {flavor} flavor\n b) Keep the existing flavor\n",
|
90
|
+
type=click.Choice(['a', 'b'], case_sensitive=False),
|
91
|
+
default='a',
|
92
|
+
show_choices=False # Choices are already shown in the prompt
|
93
|
+
)
|
94
|
+
|
95
|
+
if choice.lower() == 'b':
|
96
|
+
flavor = 'full' if full_image else 'headless'
|
97
|
+
click.echo(f"🔄 Keeping the existing flavor: {flavor}")
|
98
|
+
else:
|
99
|
+
click.echo(f"🔄 Switching to the specified flavor: {flavor}")
|
100
|
+
|
101
|
+
return flavor
|
102
|
+
|
74
103
|
def _pick_from_available_versions(board: str, version_or_url: str, internal: bool, flavor: str, swtype: str) -> str:
|
75
104
|
"""
|
76
105
|
Presents an interactive menu (with search) for selecting a firmware version.
|
@@ -236,6 +265,7 @@ def _extract_required_files(tar_path: str, board: str, update_type: str = 'stand
|
|
236
265
|
|
237
266
|
if not extracted_paths:
|
238
267
|
click.echo("⚠️ No matching files were found or extracted.")
|
268
|
+
exit()
|
239
269
|
|
240
270
|
return extracted_paths
|
241
271
|
|
@@ -373,11 +403,13 @@ def _update_board(extracted_paths: List[str], board: str, passwd: str, flavor: s
|
|
373
403
|
return
|
374
404
|
|
375
405
|
# Optionally verify the board type
|
376
|
-
board_type, _ = get_local_board_info()
|
406
|
+
board_type, _, _, full_image = get_local_board_info()
|
377
407
|
if board_type.lower() != board.lower():
|
378
408
|
click.echo(f"❌ Board mismatch: expected '{board}', but found '{board_type}'")
|
379
409
|
return
|
380
410
|
|
411
|
+
flavor = _confirm_flavor_switching(full_image=full_image, flavor=flavor)
|
412
|
+
|
381
413
|
click.echo("✅ Board verified. Starting update...")
|
382
414
|
push_and_update_local_board(troot_path, palette_path, passwd, flavor)
|
383
415
|
|
@@ -408,7 +440,7 @@ def _update_remote(extracted_paths: List[str], ip: str, board: str, passwd: str,
|
|
408
440
|
|
409
441
|
# Get remote board info
|
410
442
|
click.echo("🔍 Checking remote board type and version...")
|
411
|
-
remote_board, remote_version, fdt_name = get_remote_board_info(ip, passwd)
|
443
|
+
remote_board, remote_version, fdt_name, full_image = get_remote_board_info(ip, passwd)
|
412
444
|
|
413
445
|
if not remote_board:
|
414
446
|
click.echo("❌ Could not determine remote board type.")
|
@@ -472,12 +504,14 @@ def perform_update(version_or_url: str, ip: str = None, internal: bool = False,
|
|
472
504
|
click.echo(f"🔧 Requested version or URL: {version_or_url}, with flavor {flavor}")
|
473
505
|
|
474
506
|
if env_type == 'board':
|
475
|
-
board, version = get_local_board_info()
|
507
|
+
board, version, fdt_name, full_image = get_local_board_info()
|
476
508
|
else:
|
477
|
-
board, version, fdt_name = get_remote_board_info(ip, passwd)
|
509
|
+
board, version, fdt_name, full_image = get_remote_board_info(ip, passwd)
|
510
|
+
|
511
|
+
flavor = _confirm_flavor_switching(full_image=full_image, flavor=flavor)
|
478
512
|
|
479
513
|
if board in ['davinci', 'modalix']:
|
480
|
-
click.echo(f"🔧 Target board: {board} {fdt_name}, board currently running: {version}")
|
514
|
+
click.echo(f"🔧 Target board: {board} {fdt_name}, board currently running: {version}, full_image: {full_image}")
|
481
515
|
|
482
516
|
if flavor == 'full' and fdt_name != 'modalix-som.dtb':
|
483
517
|
click.echo(f"❌ You've requested updating {fdt_name} to full image, this is only supported for the Modalix DevKit")
|
sima_cli/utils/env.py
CHANGED
@@ -116,6 +116,37 @@ def get_exact_devkit_type() -> str:
|
|
116
116
|
|
117
117
|
return ""
|
118
118
|
|
119
|
+
def is_board_running_full_image() -> bool:
|
120
|
+
"""
|
121
|
+
Heuristic: return True if the 'full' image appears to be installed.
|
122
|
+
We detect this by checking for the NVMe CLI ('nvme'), which is bundled
|
123
|
+
with the full image but not the headless image.
|
124
|
+
|
125
|
+
Returns:
|
126
|
+
bool: True if nvme binary is found, else False.
|
127
|
+
"""
|
128
|
+
try:
|
129
|
+
# Ensure sbin dirs are in search path (non-root shells often miss these)
|
130
|
+
search_path = os.environ.get("PATH", "")
|
131
|
+
sbin = "/usr/sbin:/sbin"
|
132
|
+
if search_path:
|
133
|
+
search_path = f"{search_path}:{sbin}"
|
134
|
+
else:
|
135
|
+
search_path = sbin
|
136
|
+
|
137
|
+
nvme_path = shutil.which("nvme", path=search_path)
|
138
|
+
if nvme_path and os.path.exists(nvme_path):
|
139
|
+
return True
|
140
|
+
|
141
|
+
# Fallback: direct checks (in case PATH is unusual)
|
142
|
+
for p in ("/usr/sbin/nvme", "/sbin/nvme", "/usr/bin/nvme", "/bin/nvme"):
|
143
|
+
if os.path.isfile(p) and os.access(p, os.X_OK):
|
144
|
+
return True
|
145
|
+
|
146
|
+
return False
|
147
|
+
except Exception:
|
148
|
+
return False
|
149
|
+
|
119
150
|
def is_palette_sdk() -> bool:
|
120
151
|
"""
|
121
152
|
Check if the environment is running inside the Palette SDK container.
|
@@ -1,7 +1,7 @@
|
|
1
1
|
sima_cli/__init__.py,sha256=Nb2jSg9-CX1XvSc1c21U9qQ3atINxphuNkNfmR-9P3o,332
|
2
2
|
sima_cli/__main__.py,sha256=ehzD6AZ7zGytC2gLSvaJatxeD0jJdaEvNJvwYeGsWOg,69
|
3
|
-
sima_cli/__version__.py,sha256=
|
4
|
-
sima_cli/cli.py,sha256=
|
3
|
+
sima_cli/__version__.py,sha256=R5i7Gn8i8_CnLil8QLKcejGs0ESjVKw6151FmEBxzqU,49
|
4
|
+
sima_cli/cli.py,sha256=3tTXP7n9891OL5Rz-ytMdftBTqhhJg2VA-riWRA6Kfo,17077
|
5
5
|
sima_cli/app_zoo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
6
|
sima_cli/app_zoo/app.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
7
|
sima_cli/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -26,25 +26,25 @@ sima_cli/sdk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
26
|
sima_cli/sdk/syscheck.py,sha256=h9zCULW67y4i2hqiGc-hc1ucBDShA5FAe9NxwBGq-fM,4575
|
27
27
|
sima_cli/serial/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
28
28
|
sima_cli/serial/serial.py,sha256=6xRta_PzE_DmmooYq35lbK76TYpAny5SEJAdYC_3fH0,4141
|
29
|
-
sima_cli/storage/nvme.py,sha256=
|
30
|
-
sima_cli/storage/sdcard.py,sha256
|
29
|
+
sima_cli/storage/nvme.py,sha256=cCzYWcyPwcFu5pSMBkovsS4EwovaIMGolhEFStogXMA,4739
|
30
|
+
sima_cli/storage/sdcard.py,sha256=-WULjdV31-n8v5OOqfxR77qBbIK4hJnrD3xWxUVMoGI,6324
|
31
31
|
sima_cli/update/__init__.py,sha256=0P-z-rSaev40IhfJXytK3AFWv2_sdQU4Ry6ei2sEus0,66
|
32
32
|
sima_cli/update/bmaptool.py,sha256=KrhUGShBwY4Wzz50QiuMYAxxPgEy1nz5C68G-0a4qF4,4988
|
33
33
|
sima_cli/update/bootimg.py,sha256=Eg8ZSp8LMZXbOMxX4ZPCjFOg3YEufmsVfojKrRc3fug,13631
|
34
|
-
sima_cli/update/local.py,sha256=
|
35
|
-
sima_cli/update/netboot.py,sha256=
|
34
|
+
sima_cli/update/local.py,sha256=no3PDChERbBcyjeNVAMR4dH4OaMoRUv8hpym-aoFdhQ,3597
|
35
|
+
sima_cli/update/netboot.py,sha256=hsJQLq4HVwFFkaWjA54VZdkMGDhO0RmylciS78qAfrM,19663
|
36
36
|
sima_cli/update/query.py,sha256=b6Su7OlBGooIDcpb3_xH55tqkzznWcd_Fg1MkaNR874,4680
|
37
|
-
sima_cli/update/remote.py,sha256
|
38
|
-
sima_cli/update/updater.py,sha256
|
37
|
+
sima_cli/update/remote.py,sha256=ieZpKN2PCMu90Q72PnN66DZYyqpvBjQYHiDIjvdS5BY,11475
|
38
|
+
sima_cli/update/updater.py,sha256=-NXrGbPaxsj69NjdG_1GTB6g2bZMzk8sHJEg4NaKSgQ,23807
|
39
39
|
sima_cli/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
40
40
|
sima_cli/utils/artifactory.py,sha256=6YyVpzVm8ATy7NEwT9nkWx-wptkXrvG7Wl_zDT6jmLs,2390
|
41
41
|
sima_cli/utils/config.py,sha256=wE-cPQqY_gOqaP8t01xsRHD9tBUGk9MgBUm2GYYxI3E,1616
|
42
42
|
sima_cli/utils/config_loader.py,sha256=7I5we1yiCai18j9R9jvhfUzAmT3OjAqVK35XSLuUw8c,2005
|
43
43
|
sima_cli/utils/disk.py,sha256=66Kr631yhc_ny19up2aijfycWfD35AeLQOJgUsuH2hY,446
|
44
|
-
sima_cli/utils/env.py,sha256=
|
44
|
+
sima_cli/utils/env.py,sha256=IP5HrH0lE7RMSiBeXcEt5GCLMT5p-QQroG-uGzl5XFU,8181
|
45
45
|
sima_cli/utils/net.py,sha256=WVntA4CqipkNrrkA4tBVRadJft_pMcGYh4Re5xk3rqo,971
|
46
46
|
sima_cli/utils/network.py,sha256=UvqxbqbWUczGFyO-t1SybG7Q-x9kjUVRNIn_D6APzy8,1252
|
47
|
-
sima_cli-0.0.
|
47
|
+
sima_cli-0.0.25.dist-info/licenses/LICENSE,sha256=a260OFuV4SsMZ6sQCkoYbtws_4o2deFtbnT9kg7Rfd4,1082
|
48
48
|
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
49
49
|
tests/test_app_zoo.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
50
50
|
tests/test_auth.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -53,8 +53,8 @@ tests/test_download.py,sha256=t87DwxlHs26_ws9rpcHGwr_OrcRPd3hz6Zmm0vRee2U,4465
|
|
53
53
|
tests/test_firmware.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
54
54
|
tests/test_model_zoo.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
55
55
|
tests/test_utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
56
|
-
sima_cli-0.0.
|
57
|
-
sima_cli-0.0.
|
58
|
-
sima_cli-0.0.
|
59
|
-
sima_cli-0.0.
|
60
|
-
sima_cli-0.0.
|
56
|
+
sima_cli-0.0.25.dist-info/METADATA,sha256=2_wrl0eMxkscHwmaKn2XgMlPQC8dY76KGBSjL5-gT70,3705
|
57
|
+
sima_cli-0.0.25.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
58
|
+
sima_cli-0.0.25.dist-info/entry_points.txt,sha256=xRYrDq1nCs6R8wEdB3c1kKuimxEjWJkHuCzArQPT0Xk,47
|
59
|
+
sima_cli-0.0.25.dist-info/top_level.txt,sha256=FtrbAUdHNohtEPteOblArxQNwoX9_t8qJQd59fagDlc,15
|
60
|
+
sima_cli-0.0.25.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|