primitive 0.2.68__py3-none-any.whl → 0.2.70__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.
Potentially problematic release.
This version of primitive might be problematic. Click here for more details.
- primitive/__about__.py +1 -1
- primitive/agent/actions.py +8 -5
- primitive/agent/runner.py +54 -20
- primitive/cli.py +2 -0
- primitive/client.py +2 -0
- primitive/files/actions.py +23 -1
- primitive/hardware/actions.py +190 -1
- primitive/hardware/commands.py +13 -2
- primitive/monitor/actions.py +10 -5
- primitive/network/actions.py +83 -34
- primitive/network/commands.py +17 -6
- primitive/network/ssh.py +40 -8
- primitive/network/ui.py +9 -3
- primitive/operating_systems/__init__.py +0 -0
- primitive/operating_systems/actions.py +260 -0
- primitive/operating_systems/commands.py +268 -0
- primitive/operating_systems/graphql/__init__.py +0 -0
- primitive/operating_systems/graphql/mutations.py +32 -0
- primitive/operating_systems/graphql/queries.py +36 -0
- primitive/organizations/actions.py +6 -0
- primitive/utils/cache.py +11 -0
- primitive/utils/checksums.py +44 -0
- {primitive-0.2.68.dist-info → primitive-0.2.70.dist-info}/METADATA +1 -1
- {primitive-0.2.68.dist-info → primitive-0.2.70.dist-info}/RECORD +27 -20
- {primitive-0.2.68.dist-info → primitive-0.2.70.dist-info}/WHEEL +0 -0
- {primitive-0.2.68.dist-info → primitive-0.2.70.dist-info}/entry_points.txt +0 -0
- {primitive-0.2.68.dist-info → primitive-0.2.70.dist-info}/licenses/LICENSE.txt +0 -0
primitive/__about__.py
CHANGED
primitive/agent/actions.py
CHANGED
|
@@ -96,11 +96,14 @@ class Agent(BaseAction):
|
|
|
96
96
|
job_run_status = job_run_data.get("status", None)
|
|
97
97
|
|
|
98
98
|
hardware_id = hardware.get("id", None) if hardware else None
|
|
99
|
-
execution_hardware_id =
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
99
|
+
execution_hardware_id = None
|
|
100
|
+
if job_run_data:
|
|
101
|
+
execution_hardware = job_run_data.get("executionHardware", None)
|
|
102
|
+
execution_hardware_id = (
|
|
103
|
+
execution_hardware.get("id", None)
|
|
104
|
+
if execution_hardware
|
|
105
|
+
else None
|
|
106
|
+
)
|
|
104
107
|
target_hardware_id = None
|
|
105
108
|
|
|
106
109
|
if (
|
primitive/agent/runner.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import os
|
|
3
3
|
import shutil
|
|
4
|
+
import time
|
|
4
5
|
import typing
|
|
5
6
|
from enum import Enum
|
|
6
7
|
from pathlib import Path, PurePath
|
|
@@ -231,6 +232,11 @@ class Runner:
|
|
|
231
232
|
commands = task["cmd"].strip().split("\n")
|
|
232
233
|
|
|
233
234
|
for i, cmd in enumerate(commands):
|
|
235
|
+
if cmd.strip() == "":
|
|
236
|
+
continue
|
|
237
|
+
if cmd.strip().startswith("#"):
|
|
238
|
+
logger.debug(f"Skipping comment line: {cmd.strip()}")
|
|
239
|
+
continue
|
|
234
240
|
if cmd == "oobpowercycle":
|
|
235
241
|
logger.info("Performing out-of-band power cycle")
|
|
236
242
|
from primitive.network.redfish import RedfishClient
|
|
@@ -261,6 +267,22 @@ class Runner:
|
|
|
261
267
|
is_rebooting=True,
|
|
262
268
|
start_rebooting_at=str(datetime.now(timezone.utc)),
|
|
263
269
|
)
|
|
270
|
+
logger.info(
|
|
271
|
+
"Box rebooting, waiting 30 seconds before beginning SSH connection."
|
|
272
|
+
)
|
|
273
|
+
time.sleep(30)
|
|
274
|
+
wait_for_ssh(
|
|
275
|
+
hostname=self.target_hardware_secret.get("hostname"),
|
|
276
|
+
username=self.target_hardware_secret.get("username"),
|
|
277
|
+
password=self.target_hardware_secret.get("password"),
|
|
278
|
+
port=22,
|
|
279
|
+
)
|
|
280
|
+
logger.info("Reboot successful, SSH is now available")
|
|
281
|
+
await self.primitive.hardware.aupdate_hardware(
|
|
282
|
+
hardware_id=self.target_hardware_id,
|
|
283
|
+
is_online=True,
|
|
284
|
+
is_rebooting=False,
|
|
285
|
+
)
|
|
264
286
|
continue
|
|
265
287
|
|
|
266
288
|
if cmd == "pxeboot":
|
|
@@ -299,21 +321,29 @@ class Runner:
|
|
|
299
321
|
is_rebooting=True,
|
|
300
322
|
start_rebooting_at=str(datetime.now(timezone.utc)),
|
|
301
323
|
)
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
324
|
+
logger.info(
|
|
325
|
+
"Box rebooting, waiting 30 seconds before beginning SSH connection."
|
|
326
|
+
)
|
|
327
|
+
time.sleep(30)
|
|
328
|
+
wait_for_ssh(
|
|
329
|
+
hostname=self.target_hardware_secret.get("hostname"),
|
|
330
|
+
username=self.target_hardware_secret.get("username"),
|
|
331
|
+
password=self.target_hardware_secret.get("password"),
|
|
332
|
+
port=22,
|
|
333
|
+
)
|
|
334
|
+
logger.info("PXE boot successful, SSH is now available")
|
|
335
|
+
await self.primitive.hardware.aupdate_hardware(
|
|
336
|
+
hardware_id=self.target_hardware_id,
|
|
337
|
+
is_online=True,
|
|
338
|
+
is_rebooting=False,
|
|
339
|
+
)
|
|
309
340
|
continue
|
|
310
341
|
|
|
311
|
-
args = ["/bin/bash", "-c", cmd]
|
|
312
342
|
if self.target_hardware_secret:
|
|
313
343
|
username = self.target_hardware_secret.get("username")
|
|
314
344
|
password = self.target_hardware_secret.get("password")
|
|
315
345
|
hostname = self.target_hardware_secret.get("hostname")
|
|
316
|
-
|
|
346
|
+
command_args = [
|
|
317
347
|
"sshpass",
|
|
318
348
|
"-p",
|
|
319
349
|
password,
|
|
@@ -326,15 +356,18 @@ class Runner:
|
|
|
326
356
|
"IdentitiesOnly=yes",
|
|
327
357
|
f"{username}@{hostname}",
|
|
328
358
|
"--",
|
|
329
|
-
f
|
|
359
|
+
f"{cmd}",
|
|
330
360
|
]
|
|
361
|
+
print(" ".join(command_args))
|
|
362
|
+
else:
|
|
363
|
+
command_args = ["/bin/bash", "--login", "-c", cmd]
|
|
331
364
|
|
|
332
365
|
logger.info(
|
|
333
366
|
f"Executing command {i + 1}/{len(commands)}: {cmd} at {self.source_dir / task.get('workdir', '')}"
|
|
334
367
|
)
|
|
335
368
|
|
|
336
369
|
process = await asyncio.create_subprocess_exec(
|
|
337
|
-
*
|
|
370
|
+
*command_args,
|
|
338
371
|
env=self.modified_env,
|
|
339
372
|
cwd=str(Path(self.source_dir / task.get("workdir", ""))),
|
|
340
373
|
stdout=asyncio.subprocess.PIPE,
|
|
@@ -398,15 +431,16 @@ class Runner:
|
|
|
398
431
|
|
|
399
432
|
@log_context(label="cleanup")
|
|
400
433
|
def cleanup(self) -> None:
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
434
|
+
if stores := self.config.get("stores"):
|
|
435
|
+
for glob in stores:
|
|
436
|
+
# Glob relative to the source directory
|
|
437
|
+
matches = self.source_dir.rglob(glob)
|
|
438
|
+
|
|
439
|
+
for match in matches:
|
|
440
|
+
relative_path = PurePath(match).relative_to(self.source_dir)
|
|
441
|
+
dest = Path(get_artifacts_cache(self.job_run["id"]) / relative_path)
|
|
442
|
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
443
|
+
Path(match).replace(dest)
|
|
410
444
|
|
|
411
445
|
shutil.rmtree(path=self.source_dir)
|
|
412
446
|
|
primitive/cli.py
CHANGED
|
@@ -18,6 +18,7 @@ from .projects.commands import cli as projects_commands
|
|
|
18
18
|
from .reservations.commands import cli as reservations_commands
|
|
19
19
|
from .monitor.commands import cli as monitor_commands
|
|
20
20
|
from .network.commands import cli as network_commands
|
|
21
|
+
from .operating_systems.commands import cli as operating_system_commands
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
@click.group()
|
|
@@ -75,6 +76,7 @@ cli.add_command(reservations_commands, "reservations")
|
|
|
75
76
|
cli.add_command(exec_commands, "exec")
|
|
76
77
|
cli.add_command(monitor_commands, "monitor")
|
|
77
78
|
cli.add_command(network_commands, "network")
|
|
79
|
+
cli.add_command(operating_system_commands, "operating-systems")
|
|
78
80
|
|
|
79
81
|
if __name__ == "__main__":
|
|
80
82
|
cli(obj={})
|
primitive/client.py
CHANGED
|
@@ -17,6 +17,7 @@ from .hardware.actions import Hardware
|
|
|
17
17
|
from .jobs.actions import Jobs
|
|
18
18
|
from .monitor.actions import Monitor
|
|
19
19
|
from .network.actions import Network
|
|
20
|
+
from .operating_systems.actions import OperatingSystems
|
|
20
21
|
from .organizations.actions import Organizations
|
|
21
22
|
from .projects.actions import Projects
|
|
22
23
|
from .provisioning.actions import Provisioning
|
|
@@ -99,6 +100,7 @@ class Primitive:
|
|
|
99
100
|
self.provisioning: Provisioning = Provisioning(self)
|
|
100
101
|
self.monitor: Monitor = Monitor(self)
|
|
101
102
|
self.network: Network = Network(self)
|
|
103
|
+
self.operating_systems: OperatingSystems = OperatingSystems(self)
|
|
102
104
|
|
|
103
105
|
def get_host_config(self):
|
|
104
106
|
self.full_config = read_config_file()
|
primitive/files/actions.py
CHANGED
|
@@ -62,6 +62,7 @@ class Files(BaseAction):
|
|
|
62
62
|
chunk_size: int,
|
|
63
63
|
number_of_parts: int,
|
|
64
64
|
is_public: bool = False,
|
|
65
|
+
organization_id: Optional[str] = None,
|
|
65
66
|
):
|
|
66
67
|
mutation = gql(pending_file_create_mutation)
|
|
67
68
|
input = {
|
|
@@ -74,6 +75,8 @@ class Files(BaseAction):
|
|
|
74
75
|
"chunkSize": chunk_size,
|
|
75
76
|
"numberOfParts": number_of_parts,
|
|
76
77
|
}
|
|
78
|
+
if organization_id:
|
|
79
|
+
input["organizationId"] = organization_id
|
|
77
80
|
variables = {"input": input}
|
|
78
81
|
result = self.primitive.session.execute(
|
|
79
82
|
mutation, variable_values=variables, get_execution_result=True
|
|
@@ -223,6 +226,7 @@ class Files(BaseAction):
|
|
|
223
226
|
is_public: bool = False,
|
|
224
227
|
key_prefix: str = "",
|
|
225
228
|
file_id: Optional[str] = None,
|
|
229
|
+
organization_id: Optional[str] = None,
|
|
226
230
|
):
|
|
227
231
|
if path.exists() is False:
|
|
228
232
|
raise Exception(f"File {path} does not exist.")
|
|
@@ -262,6 +266,7 @@ class Files(BaseAction):
|
|
|
262
266
|
is_public=is_public,
|
|
263
267
|
chunk_size=chunk_size,
|
|
264
268
|
number_of_parts=number_of_parts,
|
|
269
|
+
organization_id=organization_id,
|
|
265
270
|
)
|
|
266
271
|
file_id = pending_file_create.get("id")
|
|
267
272
|
parts_details = pending_file_create.get("partsDetails")
|
|
@@ -332,6 +337,7 @@ class Files(BaseAction):
|
|
|
332
337
|
path: Path,
|
|
333
338
|
is_public: bool = False,
|
|
334
339
|
key_prefix: str = "",
|
|
340
|
+
organization_id: Optional[str] = None,
|
|
335
341
|
):
|
|
336
342
|
"""
|
|
337
343
|
This method uploads a file via the Primitive API.
|
|
@@ -347,6 +353,11 @@ class Files(BaseAction):
|
|
|
347
353
|
+ file_path
|
|
348
354
|
+ """\", "keyPrefix": \""""
|
|
349
355
|
+ key_prefix
|
|
356
|
+
+ (
|
|
357
|
+
f'", "organizationId": "{organization_id}'
|
|
358
|
+
if organization_id
|
|
359
|
+
else ""
|
|
360
|
+
)
|
|
350
361
|
+ """\" } } }"""
|
|
351
362
|
) # noqa
|
|
352
363
|
|
|
@@ -356,6 +367,11 @@ class Files(BaseAction):
|
|
|
356
367
|
+ file_path
|
|
357
368
|
+ """\", "keyPrefix": \""""
|
|
358
369
|
+ key_prefix
|
|
370
|
+
+ (
|
|
371
|
+
f'", "organizationId": "{organization_id}'
|
|
372
|
+
if organization_id
|
|
373
|
+
else ""
|
|
374
|
+
)
|
|
359
375
|
+ """\" } } }"""
|
|
360
376
|
) # noqa
|
|
361
377
|
body = {
|
|
@@ -385,6 +401,7 @@ class Files(BaseAction):
|
|
|
385
401
|
output_path: Path = Path().cwd(),
|
|
386
402
|
) -> Path:
|
|
387
403
|
file_pk = None
|
|
404
|
+
file_size = None
|
|
388
405
|
|
|
389
406
|
files_result = self.primitive.files.files(
|
|
390
407
|
file_id=file_id,
|
|
@@ -396,6 +413,7 @@ class Files(BaseAction):
|
|
|
396
413
|
file = files_data["files"]["edges"][0]["node"]
|
|
397
414
|
file_pk = file["pk"]
|
|
398
415
|
file_name = file["fileName"]
|
|
416
|
+
file_size = int(file["fileSize"])
|
|
399
417
|
|
|
400
418
|
if not file_pk:
|
|
401
419
|
raise Exception(
|
|
@@ -404,7 +422,11 @@ class Files(BaseAction):
|
|
|
404
422
|
|
|
405
423
|
session = create_requests_session(host_config=self.primitive.host_config)
|
|
406
424
|
transport = self.primitive.host_config.get("transport")
|
|
407
|
-
|
|
425
|
+
|
|
426
|
+
if file_size and file_size < 5 * 1024 * 1024:
|
|
427
|
+
url = f"{transport}://{self.primitive.host}/files/{file_pk}/stream/"
|
|
428
|
+
else:
|
|
429
|
+
url = f"{transport}://{self.primitive.host}/files/{file_pk}/presigned-url/"
|
|
408
430
|
|
|
409
431
|
downloaded_file = output_path / file_name
|
|
410
432
|
|
primitive/hardware/actions.py
CHANGED
|
@@ -283,7 +283,177 @@ class Hardware(BaseAction):
|
|
|
283
283
|
return {"linux_machine_id": machine_id}
|
|
284
284
|
return {}
|
|
285
285
|
|
|
286
|
-
def
|
|
286
|
+
def _get_homebrew_installed(self):
|
|
287
|
+
try:
|
|
288
|
+
raw = json.loads(
|
|
289
|
+
subprocess.check_output(["brew", "info", "--json=v2", "--installed"])
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
if isinstance(raw, Dict):
|
|
293
|
+
raw_formulae = raw.get("formulae", [])
|
|
294
|
+
raw_casks = raw.get("casks", [])
|
|
295
|
+
|
|
296
|
+
formulae = []
|
|
297
|
+
casks = []
|
|
298
|
+
|
|
299
|
+
if isinstance(raw_formulae, List):
|
|
300
|
+
for raw_formula in raw_formulae:
|
|
301
|
+
try:
|
|
302
|
+
formulae.append(
|
|
303
|
+
{
|
|
304
|
+
"full_name": raw_formula["full_name"],
|
|
305
|
+
"tap": raw_formula["tap"],
|
|
306
|
+
"version": raw_formula["versions"]["stable"],
|
|
307
|
+
}
|
|
308
|
+
)
|
|
309
|
+
except Exception:
|
|
310
|
+
pass
|
|
311
|
+
|
|
312
|
+
if isinstance(raw_casks, List):
|
|
313
|
+
for raw_cask in raw_casks:
|
|
314
|
+
try:
|
|
315
|
+
casks.append(
|
|
316
|
+
{
|
|
317
|
+
"full_token": raw_cask["full_token"],
|
|
318
|
+
"name": raw_cask["name"][0],
|
|
319
|
+
"version": raw_cask["installed"],
|
|
320
|
+
}
|
|
321
|
+
)
|
|
322
|
+
except Exception:
|
|
323
|
+
pass
|
|
324
|
+
|
|
325
|
+
return {
|
|
326
|
+
"formulae": formulae,
|
|
327
|
+
"casks": casks,
|
|
328
|
+
}
|
|
329
|
+
except Exception:
|
|
330
|
+
pass
|
|
331
|
+
|
|
332
|
+
def _get_darwin_md_application(self, application_path: str):
|
|
333
|
+
lines = (
|
|
334
|
+
subprocess.check_output(
|
|
335
|
+
[
|
|
336
|
+
"mdls",
|
|
337
|
+
"-name",
|
|
338
|
+
"kMDItemDisplayName",
|
|
339
|
+
"-name",
|
|
340
|
+
"kMDItemVersion",
|
|
341
|
+
"-name",
|
|
342
|
+
"kMDItemCFBundleIdentifier",
|
|
343
|
+
application_path,
|
|
344
|
+
]
|
|
345
|
+
)
|
|
346
|
+
.strip()
|
|
347
|
+
.decode("utf-8")
|
|
348
|
+
.split("\n")
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
item = {
|
|
352
|
+
"path": application_path,
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
for line in lines:
|
|
356
|
+
raw_key, raw_value = line.split(" = ")
|
|
357
|
+
|
|
358
|
+
key = raw_key.strip()
|
|
359
|
+
|
|
360
|
+
if raw_value == "(null)":
|
|
361
|
+
item[key] = None
|
|
362
|
+
elif raw_value.startswith('"') and raw_value.endswith('"'):
|
|
363
|
+
item[key] = raw_value[1:-1]
|
|
364
|
+
|
|
365
|
+
return item
|
|
366
|
+
|
|
367
|
+
def _get_darwin_md_applications(self):
|
|
368
|
+
import concurrent.futures
|
|
369
|
+
|
|
370
|
+
try:
|
|
371
|
+
application_paths = (
|
|
372
|
+
subprocess.check_output(["mdfind", "kMDItemKind == 'Application'"])
|
|
373
|
+
.strip()
|
|
374
|
+
.decode("utf-8")
|
|
375
|
+
.split("\n")
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
379
|
+
return list(
|
|
380
|
+
executor.map(self._get_darwin_md_application, application_paths)
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
except Exception:
|
|
384
|
+
pass
|
|
385
|
+
|
|
386
|
+
def _get_fedora_installed_packages(self):
|
|
387
|
+
try:
|
|
388
|
+
lines = (
|
|
389
|
+
subprocess.check_output(
|
|
390
|
+
[
|
|
391
|
+
"dnf",
|
|
392
|
+
"repoquery",
|
|
393
|
+
"--installed",
|
|
394
|
+
"--qf",
|
|
395
|
+
"%{name} , %{version}\n",
|
|
396
|
+
]
|
|
397
|
+
)
|
|
398
|
+
.decode("utf-8")
|
|
399
|
+
.strip()
|
|
400
|
+
.split("\n")
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
items = []
|
|
404
|
+
|
|
405
|
+
for line in lines:
|
|
406
|
+
try:
|
|
407
|
+
name, version = line.split(" , ")
|
|
408
|
+
|
|
409
|
+
items.append(
|
|
410
|
+
{
|
|
411
|
+
"name": name,
|
|
412
|
+
"version": version,
|
|
413
|
+
}
|
|
414
|
+
)
|
|
415
|
+
except Exception:
|
|
416
|
+
pass
|
|
417
|
+
return items
|
|
418
|
+
except Exception:
|
|
419
|
+
pass
|
|
420
|
+
|
|
421
|
+
def _get_ubuntu_installed_packages(self):
|
|
422
|
+
try:
|
|
423
|
+
lines = (
|
|
424
|
+
subprocess.check_output(["apt", "list", "--installed"])
|
|
425
|
+
.decode("utf-8")
|
|
426
|
+
.strip()
|
|
427
|
+
.split("\n")
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
lines.pop()
|
|
431
|
+
|
|
432
|
+
items = []
|
|
433
|
+
|
|
434
|
+
for line in lines:
|
|
435
|
+
try:
|
|
436
|
+
columns = line.split()
|
|
437
|
+
|
|
438
|
+
if len(columns) < 2:
|
|
439
|
+
continue
|
|
440
|
+
|
|
441
|
+
name, version, *_ = columns
|
|
442
|
+
items.append(
|
|
443
|
+
{
|
|
444
|
+
"name": name.split("/")[0],
|
|
445
|
+
"version": version,
|
|
446
|
+
}
|
|
447
|
+
)
|
|
448
|
+
except Exception:
|
|
449
|
+
pass
|
|
450
|
+
|
|
451
|
+
return items
|
|
452
|
+
|
|
453
|
+
except Exception:
|
|
454
|
+
pass
|
|
455
|
+
|
|
456
|
+
def get_system_info(self, with_installed_applications: bool = False):
|
|
287
457
|
os_family = platform.system()
|
|
288
458
|
system_info = {}
|
|
289
459
|
if os_family == "Darwin":
|
|
@@ -294,6 +464,13 @@ class Hardware(BaseAction):
|
|
|
294
464
|
.decode("utf-8")
|
|
295
465
|
)
|
|
296
466
|
system_info["apple_mac_os_version"] = platform.mac_ver()[0]
|
|
467
|
+
system_info["homebrew_installed"] = self._get_homebrew_installed()
|
|
468
|
+
|
|
469
|
+
if with_installed_applications:
|
|
470
|
+
system_info["darwin_md_applications"] = (
|
|
471
|
+
self._get_darwin_md_applications()
|
|
472
|
+
)
|
|
473
|
+
|
|
297
474
|
elif os_family == "Linux":
|
|
298
475
|
# Support for Linux-based VMs in Windows
|
|
299
476
|
if "WSL2" in platform.platform():
|
|
@@ -304,6 +481,18 @@ class Hardware(BaseAction):
|
|
|
304
481
|
}
|
|
305
482
|
else:
|
|
306
483
|
system_info = {**system_info, **self._get_ubuntu_values()}
|
|
484
|
+
system_info["linux_release"] = platform.freedesktop_os_release()
|
|
485
|
+
|
|
486
|
+
match system_info["linux_release"]["ID"]:
|
|
487
|
+
case "ubuntu":
|
|
488
|
+
system_info["ubuntu_installed_packages"] = (
|
|
489
|
+
self._get_ubuntu_installed_packages()
|
|
490
|
+
)
|
|
491
|
+
case "fedora":
|
|
492
|
+
system_info["fedora_installed_packages"] = (
|
|
493
|
+
self._get_fedora_installed_packages()
|
|
494
|
+
)
|
|
495
|
+
|
|
307
496
|
elif os_family == "Windows":
|
|
308
497
|
system_info = {
|
|
309
498
|
**system_info,
|
primitive/hardware/commands.py
CHANGED
|
@@ -22,11 +22,22 @@ def cli(context):
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
@cli.command("systeminfo")
|
|
25
|
+
@click.option(
|
|
26
|
+
"--with-installed-applications",
|
|
27
|
+
is_flag=True,
|
|
28
|
+
default=False,
|
|
29
|
+
help="Only for MacOS, list installed applications using metadata commands",
|
|
30
|
+
)
|
|
25
31
|
@click.pass_context
|
|
26
|
-
def systeminfo_command(
|
|
32
|
+
def systeminfo_command(
|
|
33
|
+
context,
|
|
34
|
+
with_installed_applications: bool = False,
|
|
35
|
+
):
|
|
27
36
|
"""Get System Info"""
|
|
28
37
|
primitive: Primitive = context.obj.get("PRIMITIVE")
|
|
29
|
-
message = primitive.hardware.get_system_info(
|
|
38
|
+
message = primitive.hardware.get_system_info(
|
|
39
|
+
with_installed_applications=with_installed_applications
|
|
40
|
+
)
|
|
30
41
|
print_result(message=message, context=context)
|
|
31
42
|
|
|
32
43
|
|
primitive/monitor/actions.py
CHANGED
|
@@ -191,11 +191,16 @@ class Monitor(BaseAction):
|
|
|
191
191
|
job_run_id = None
|
|
192
192
|
else:
|
|
193
193
|
hardware_id = hardware.get("id", None) if hardware else None
|
|
194
|
-
execution_hardware_id =
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
194
|
+
execution_hardware_id = None
|
|
195
|
+
if job_run_data:
|
|
196
|
+
execution_hardware = job_run_data.get(
|
|
197
|
+
"executionHardware", None
|
|
198
|
+
)
|
|
199
|
+
execution_hardware_id = (
|
|
200
|
+
execution_hardware.get("id", None)
|
|
201
|
+
if execution_hardware
|
|
202
|
+
else None
|
|
203
|
+
)
|
|
199
204
|
|
|
200
205
|
if (
|
|
201
206
|
hardware_id is not None
|