primitive 0.2.70__py3-none-any.whl → 0.2.72__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/pxe.py +71 -0
- primitive/agent/runner.py +2 -25
- primitive/hardware/actions.py +13 -2
- primitive/messaging/provider.py +1 -0
- primitive/monitor/actions.py +1 -1
- primitive/network/actions.py +6 -10
- primitive/network/ssh.py +16 -7
- primitive/operating_systems/actions.py +249 -36
- primitive/operating_systems/commands.py +146 -168
- {primitive-0.2.70.dist-info → primitive-0.2.72.dist-info}/METADATA +1 -1
- {primitive-0.2.70.dist-info → primitive-0.2.72.dist-info}/RECORD +15 -14
- {primitive-0.2.70.dist-info → primitive-0.2.72.dist-info}/WHEEL +0 -0
- {primitive-0.2.70.dist-info → primitive-0.2.72.dist-info}/entry_points.txt +0 -0
- {primitive-0.2.70.dist-info → primitive-0.2.72.dist-info}/licenses/LICENSE.txt +0 -0
primitive/__about__.py
CHANGED
primitive/agent/pxe.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from loguru import logger
|
|
2
|
+
|
|
3
|
+
from primitive.network.redfish import RedfishClient
|
|
4
|
+
from primitive.network.ssh import test_ssh_connection, run_command
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def pxe_boot_via_redfish(target_hardware_secret: dict):
|
|
8
|
+
bmc_host = target_hardware_secret.get("bmcHostname", None)
|
|
9
|
+
bmc_username = target_hardware_secret.get("bmcUsername", None)
|
|
10
|
+
bmc_password = target_hardware_secret.get("bmcPassword", "")
|
|
11
|
+
|
|
12
|
+
if bmc_host is None:
|
|
13
|
+
logger.error(
|
|
14
|
+
"No BMC host found in target hardware secret for out-of-band power cycle"
|
|
15
|
+
)
|
|
16
|
+
return True
|
|
17
|
+
if bmc_username is None:
|
|
18
|
+
logger.error(
|
|
19
|
+
"No BMC username found in target hardware secret for out-of-band power cycle"
|
|
20
|
+
)
|
|
21
|
+
return True
|
|
22
|
+
|
|
23
|
+
redfish = RedfishClient(host=bmc_host, username=bmc_username, password=bmc_password)
|
|
24
|
+
redfish.update_boot_options(
|
|
25
|
+
system_id="1",
|
|
26
|
+
boot_source_override_target="Pxe",
|
|
27
|
+
boot_source_override_enabled="Once",
|
|
28
|
+
boot_source_override_mode="UEFI",
|
|
29
|
+
)
|
|
30
|
+
redfish.compute_system_reset(system_id="1", reset_type="ForceRestart")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def pxe_boot_via_efibootmgr(target_hardware_secret: dict):
|
|
34
|
+
run_command(
|
|
35
|
+
hostname=target_hardware_secret.get("hostname"),
|
|
36
|
+
username=target_hardware_secret.get("username"),
|
|
37
|
+
password=target_hardware_secret.get("password"),
|
|
38
|
+
command="sudo efibootmgr -n $(efibootmgr | awk '/PXE IPV4/ {print substr($1,5,4)}' | head -n1 || efibootmgr | awk '/ubuntu/ {print substr($1,5,4)}' | head -n1) && sudo reboot",
|
|
39
|
+
port=22,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def pxe_boot(target_hardware_secret: dict) -> bool:
|
|
44
|
+
redfish_available = False
|
|
45
|
+
ssh_available = False
|
|
46
|
+
|
|
47
|
+
if target_hardware_secret.get("bmcHostname", None):
|
|
48
|
+
redfish_available = True
|
|
49
|
+
else:
|
|
50
|
+
logger.info(
|
|
51
|
+
"No BMC credentials found, skipping Redfish PXE boot method. Checking efiboot method."
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
ssh_available = test_ssh_connection(
|
|
55
|
+
hostname=target_hardware_secret.get("hostname"),
|
|
56
|
+
username=target_hardware_secret.get("username"),
|
|
57
|
+
password=target_hardware_secret.get("password"),
|
|
58
|
+
port=22,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
if redfish_available:
|
|
62
|
+
pxe_boot_via_redfish(target_hardware_secret=target_hardware_secret)
|
|
63
|
+
return True
|
|
64
|
+
elif ssh_available:
|
|
65
|
+
pxe_boot_via_efibootmgr(target_hardware_secret=target_hardware_secret)
|
|
66
|
+
return True
|
|
67
|
+
else:
|
|
68
|
+
logger.error(
|
|
69
|
+
"No available method to PXE boot target hardware. Missing BMC credentials and SSH is not available."
|
|
70
|
+
)
|
|
71
|
+
return False
|
primitive/agent/runner.py
CHANGED
|
@@ -10,6 +10,7 @@ from datetime import datetime, timezone
|
|
|
10
10
|
|
|
11
11
|
from loguru import logger
|
|
12
12
|
|
|
13
|
+
from primitive.agent.pxe import pxe_boot
|
|
13
14
|
from primitive.network.ssh import wait_for_ssh
|
|
14
15
|
from primitive.utils.cache import get_artifacts_cache, get_logs_cache, get_sources_cache
|
|
15
16
|
from primitive.utils.logging import fmt, log_context
|
|
@@ -287,33 +288,9 @@ class Runner:
|
|
|
287
288
|
|
|
288
289
|
if cmd == "pxeboot":
|
|
289
290
|
logger.info("Setting next boot to PXE and rebooting")
|
|
290
|
-
from primitive.network.redfish import RedfishClient
|
|
291
|
-
|
|
292
|
-
bmc_host = self.target_hardware_secret.get("bmcHostname", None)
|
|
293
|
-
bmc_username = self.target_hardware_secret.get("bmcUsername", None)
|
|
294
|
-
bmc_password = self.target_hardware_secret.get("bmcPassword", "")
|
|
295
291
|
|
|
296
|
-
|
|
297
|
-
logger.error(
|
|
298
|
-
"No BMC host found in target hardware secret for out-of-band power cycle"
|
|
299
|
-
)
|
|
300
|
-
return True
|
|
301
|
-
if bmc_username is None:
|
|
302
|
-
logger.error(
|
|
303
|
-
"No BMC username found in target hardware secret for out-of-band power cycle"
|
|
304
|
-
)
|
|
305
|
-
return True
|
|
292
|
+
pxe_boot(target_hardware_secret=self.target_hardware_secret)
|
|
306
293
|
|
|
307
|
-
redfish = RedfishClient(
|
|
308
|
-
host=bmc_host, username=bmc_username, password=bmc_password
|
|
309
|
-
)
|
|
310
|
-
redfish.update_boot_options(
|
|
311
|
-
system_id="1",
|
|
312
|
-
boot_source_override_target="Pxe",
|
|
313
|
-
boot_source_override_enabled="Once",
|
|
314
|
-
boot_source_override_mode="UEFI",
|
|
315
|
-
)
|
|
316
|
-
redfish.compute_system_reset(system_id="1", reset_type="ForceRestart")
|
|
317
294
|
if self.target_hardware_id:
|
|
318
295
|
await self.primitive.hardware.aupdate_hardware(
|
|
319
296
|
hardware_id=self.target_hardware_id,
|
primitive/hardware/actions.py
CHANGED
|
@@ -421,7 +421,10 @@ class Hardware(BaseAction):
|
|
|
421
421
|
def _get_ubuntu_installed_packages(self):
|
|
422
422
|
try:
|
|
423
423
|
lines = (
|
|
424
|
-
subprocess.check_output(
|
|
424
|
+
subprocess.check_output(
|
|
425
|
+
["apt", "list", "--installed"],
|
|
426
|
+
stderr=subprocess.DEVNULL,
|
|
427
|
+
)
|
|
425
428
|
.decode("utf-8")
|
|
426
429
|
.strip()
|
|
427
430
|
.split("\n")
|
|
@@ -793,7 +796,7 @@ class Hardware(BaseAction):
|
|
|
793
796
|
if slug is not None:
|
|
794
797
|
filters["slug"] = {"exact": slug}
|
|
795
798
|
if id is not None:
|
|
796
|
-
filters["id"] =
|
|
799
|
+
filters["id"] = id
|
|
797
800
|
# if nested_children is True:
|
|
798
801
|
# filters["hasParent"] = {"exact": False}
|
|
799
802
|
|
|
@@ -1206,3 +1209,11 @@ class Hardware(BaseAction):
|
|
|
1206
1209
|
logger.info(message)
|
|
1207
1210
|
|
|
1208
1211
|
return result
|
|
1212
|
+
|
|
1213
|
+
def push_own_system_info(self):
|
|
1214
|
+
if self.primitive.messaging.ready:
|
|
1215
|
+
message = {"system_info": self.primitive.hardware.get_system_info()}
|
|
1216
|
+
self.primitive.messaging.send_message(
|
|
1217
|
+
message_type=MESSAGE_TYPES.SYSTEM_INFO,
|
|
1218
|
+
message=message,
|
|
1219
|
+
)
|
primitive/messaging/provider.py
CHANGED
|
@@ -27,6 +27,7 @@ CELERY_TASK_NAME = "hardware.tasks.task_receive_hardware_message"
|
|
|
27
27
|
|
|
28
28
|
class MESSAGE_TYPES(enum.Enum):
|
|
29
29
|
CHECK_IN = "CHECK_IN"
|
|
30
|
+
SYSTEM_INFO = "SYSTEM_INFO"
|
|
30
31
|
METRICS = "METRICS"
|
|
31
32
|
SWITCH_AND_INTERFACES_INFO = "SWITCH_AND_INTERFACES_INFO"
|
|
32
33
|
OWN_NETWORK_INTERFACES = "OWN_NETWORK_INTERFACES"
|
primitive/monitor/actions.py
CHANGED
|
@@ -94,7 +94,7 @@ class Monitor(BaseAction):
|
|
|
94
94
|
if hardware.get("isController", False):
|
|
95
95
|
self.primitive.hardware.get_and_set_switch_info()
|
|
96
96
|
self.primitive.network.push_switch_and_interfaces_info()
|
|
97
|
-
self.primitive.
|
|
97
|
+
self.primitive.hardware.push_own_system_info()
|
|
98
98
|
self.primitive.hardware._sync_children(hardware=hardware)
|
|
99
99
|
|
|
100
100
|
sleep_amount = 5
|
primitive/network/actions.py
CHANGED
|
@@ -8,6 +8,7 @@ from paramiko import SSHClient
|
|
|
8
8
|
from typing import TypedDict
|
|
9
9
|
import re
|
|
10
10
|
|
|
11
|
+
from primitive.hardware.actions import does_executable_exist
|
|
11
12
|
from primitive.messaging.provider import MESSAGE_TYPES
|
|
12
13
|
from primitive.utils.actions import BaseAction
|
|
13
14
|
|
|
@@ -372,6 +373,9 @@ class Network(BaseAction):
|
|
|
372
373
|
return ip_to_mac_address_info
|
|
373
374
|
|
|
374
375
|
def get_ip_arp_table_via_ip_command(self):
|
|
376
|
+
if does_executable_exist("ip") is False:
|
|
377
|
+
return []
|
|
378
|
+
|
|
375
379
|
command = "ip --json neigh show"
|
|
376
380
|
ip_result = None
|
|
377
381
|
with Popen(command.split(" "), stdout=PIPE) as process:
|
|
@@ -458,6 +462,7 @@ class Network(BaseAction):
|
|
|
458
462
|
return False
|
|
459
463
|
|
|
460
464
|
def push_switch_and_interfaces_info(self, interfaces_info: dict | None = None):
|
|
465
|
+
logger.debug("Pushing switch and interfaces info")
|
|
461
466
|
if self.primitive.messaging.ready and self.switch_connection_info is not None:
|
|
462
467
|
switch_info = self.get_switch_info()
|
|
463
468
|
interfaces_info = interfaces_info or self.get_interfaces_info()
|
|
@@ -473,13 +478,4 @@ class Network(BaseAction):
|
|
|
473
478
|
message_type=MESSAGE_TYPES.SWITCH_AND_INTERFACES_INFO,
|
|
474
479
|
message=message,
|
|
475
480
|
)
|
|
476
|
-
|
|
477
|
-
def push_own_network_interfaces(self):
|
|
478
|
-
if self.primitive.messaging.ready:
|
|
479
|
-
message = {
|
|
480
|
-
"network_interfaces": self.primitive.hardware._get_network_interfaces()
|
|
481
|
-
}
|
|
482
|
-
self.primitive.messaging.send_message(
|
|
483
|
-
message_type=MESSAGE_TYPES.OWN_NETWORK_INTERFACES,
|
|
484
|
-
message=message,
|
|
485
|
-
)
|
|
481
|
+
logger.debug("Switch and interfaces info pushed")
|
primitive/network/ssh.py
CHANGED
|
@@ -27,7 +27,13 @@ def test_ssh_connection(hostname, username, password=None, key_filename=None, po
|
|
|
27
27
|
try:
|
|
28
28
|
if password:
|
|
29
29
|
ssh_client.connect(
|
|
30
|
-
hostname=hostname,
|
|
30
|
+
hostname=hostname,
|
|
31
|
+
port=port,
|
|
32
|
+
username=username,
|
|
33
|
+
password=password,
|
|
34
|
+
banner_timeout=60,
|
|
35
|
+
timeout=60,
|
|
36
|
+
auth_timeout=60,
|
|
31
37
|
)
|
|
32
38
|
elif key_filename:
|
|
33
39
|
ssh_client.connect(
|
|
@@ -35,26 +41,29 @@ def test_ssh_connection(hostname, username, password=None, key_filename=None, po
|
|
|
35
41
|
port=port,
|
|
36
42
|
username=username,
|
|
37
43
|
key_filename=key_filename,
|
|
44
|
+
banner_timeout=60,
|
|
45
|
+
timeout=60,
|
|
46
|
+
auth_timeout=60,
|
|
38
47
|
)
|
|
39
48
|
else:
|
|
40
|
-
|
|
49
|
+
logger.error(
|
|
41
50
|
"Error: Either password or key_filename must be provided for authentication."
|
|
42
51
|
)
|
|
43
52
|
return False
|
|
44
53
|
|
|
45
|
-
|
|
54
|
+
logger.info(f"Successfully connected to {hostname} as {username}")
|
|
46
55
|
return True
|
|
47
56
|
except paramiko.AuthenticationException:
|
|
48
|
-
|
|
57
|
+
logger.debug(f"Authentication failed for {username} on {hostname}")
|
|
49
58
|
return False
|
|
50
59
|
except paramiko.SSHException as exception:
|
|
51
|
-
|
|
60
|
+
logger.debug(f"SSH error connecting to {hostname}: {exception}")
|
|
52
61
|
return False
|
|
53
62
|
except socket.error as exception:
|
|
54
|
-
|
|
63
|
+
logger.debug(f"Socket error connecting to {hostname}: {exception}")
|
|
55
64
|
return False
|
|
56
65
|
except Exception as exception:
|
|
57
|
-
|
|
66
|
+
logger.debug(f"An unexpected error occurred: {exception}")
|
|
58
67
|
return False
|
|
59
68
|
finally:
|
|
60
69
|
ssh_client.close()
|
|
@@ -17,13 +17,16 @@ import os
|
|
|
17
17
|
from loguru import logger
|
|
18
18
|
|
|
19
19
|
from primitive.utils.checksums import get_checksum_from_file, calculate_sha256
|
|
20
|
+
from primitive.utils.text import slugify
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
class OperatingSystems(BaseAction):
|
|
23
24
|
def __init__(self, primitive):
|
|
24
25
|
super().__init__(primitive)
|
|
26
|
+
self.operating_systems_key_prefix = "operating-systems"
|
|
25
27
|
self.remote_operating_systems = {
|
|
26
28
|
"ubuntu-24-04-3": {
|
|
29
|
+
"slug": "ubuntu-24-04-3",
|
|
27
30
|
"iso": "https://releases.ubuntu.com/24.04.3/ubuntu-24.04.3-desktop-amd64.iso",
|
|
28
31
|
"checksum": "https://releases.ubuntu.com/24.04.3/SHA256SUMS",
|
|
29
32
|
"checksum_file_type": self.OperatingSystemChecksumFileType.SHA256SUMS,
|
|
@@ -33,13 +36,17 @@ class OperatingSystems(BaseAction):
|
|
|
33
36
|
class OperatingSystemChecksumFileType(Enum):
|
|
34
37
|
SHA256SUMS = "SHA256SUMS"
|
|
35
38
|
|
|
36
|
-
def
|
|
37
|
-
return
|
|
39
|
+
def list_remotes(self):
|
|
40
|
+
return self.remote_operating_systems.values()
|
|
38
41
|
|
|
39
|
-
def
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
def get_remote_info(self, slug: str):
|
|
43
|
+
return self.remote_operating_systems[slug]
|
|
44
|
+
|
|
45
|
+
def _download_remote_operating_system_iso(
|
|
46
|
+
self, remote_operating_system_name: str, directory: str | None = None
|
|
47
|
+
):
|
|
48
|
+
cache_dir = Path(directory) if directory else get_operating_systems_cache()
|
|
49
|
+
operating_system_dir = Path(cache_dir / remote_operating_system_name)
|
|
43
50
|
iso_dir = Path(operating_system_dir / "iso")
|
|
44
51
|
os.makedirs(iso_dir, exist_ok=True)
|
|
45
52
|
|
|
@@ -73,10 +80,11 @@ class OperatingSystems(BaseAction):
|
|
|
73
80
|
|
|
74
81
|
return iso_file_path
|
|
75
82
|
|
|
76
|
-
def _download_remote_operating_system_checksum(
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
)
|
|
83
|
+
def _download_remote_operating_system_checksum(
|
|
84
|
+
self, remote_operating_system_name: str, directory: str | None = None
|
|
85
|
+
):
|
|
86
|
+
cache_dir = Path(directory) if directory else get_operating_systems_cache()
|
|
87
|
+
operating_system_dir = Path(cache_dir / remote_operating_system_name)
|
|
80
88
|
checksum_dir = Path(operating_system_dir / "checksum")
|
|
81
89
|
os.makedirs(checksum_dir, exist_ok=True)
|
|
82
90
|
|
|
@@ -103,8 +111,10 @@ class OperatingSystems(BaseAction):
|
|
|
103
111
|
|
|
104
112
|
return checksum_file_path
|
|
105
113
|
|
|
106
|
-
def
|
|
107
|
-
|
|
114
|
+
def download_remote(
|
|
115
|
+
self, remote_operating_system_name: str, directory: str | None = None
|
|
116
|
+
):
|
|
117
|
+
remote_operating_system_names = list(self.remote_operating_systems.keys())
|
|
108
118
|
|
|
109
119
|
if remote_operating_system_name not in remote_operating_system_names:
|
|
110
120
|
logger.error(
|
|
@@ -115,20 +125,30 @@ class OperatingSystems(BaseAction):
|
|
|
115
125
|
)
|
|
116
126
|
|
|
117
127
|
iso_file_path = self._download_remote_operating_system_iso(
|
|
118
|
-
remote_operating_system_name
|
|
128
|
+
remote_operating_system_name,
|
|
129
|
+
directory=directory,
|
|
119
130
|
)
|
|
120
131
|
checksum_file_path = self._download_remote_operating_system_checksum(
|
|
121
|
-
remote_operating_system_name
|
|
132
|
+
remote_operating_system_name,
|
|
133
|
+
directory=directory,
|
|
122
134
|
)
|
|
123
135
|
|
|
124
|
-
|
|
136
|
+
logger.info("Validating iso checksum")
|
|
137
|
+
checksum_valid = self.primitive.operating_systems._validate_checksum(
|
|
138
|
+
remote_operating_system_name, str(iso_file_path), str(checksum_file_path)
|
|
139
|
+
)
|
|
125
140
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
141
|
+
if not checksum_valid:
|
|
142
|
+
raise Exception(
|
|
143
|
+
"Checksums did not match: file may have been corrupted during download."
|
|
144
|
+
+ f"\nTry deleting the directory {get_operating_systems_cache()}/{remote_operating_system_name} and running this command again."
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
logger.info("Checksum valid")
|
|
130
148
|
|
|
131
|
-
|
|
149
|
+
return iso_file_path, checksum_file_path
|
|
150
|
+
|
|
151
|
+
def _validate_checksum(
|
|
132
152
|
self,
|
|
133
153
|
operating_system_name: str,
|
|
134
154
|
iso_file_path: str,
|
|
@@ -138,7 +158,7 @@ class OperatingSystems(BaseAction):
|
|
|
138
158
|
checksum_file_type = (
|
|
139
159
|
checksum_file_type
|
|
140
160
|
if checksum_file_type
|
|
141
|
-
else self.
|
|
161
|
+
else self.get_remote_info(operating_system_name)["checksum_file_type"]
|
|
142
162
|
)
|
|
143
163
|
|
|
144
164
|
match checksum_file_type:
|
|
@@ -157,8 +177,130 @@ class OperatingSystems(BaseAction):
|
|
|
157
177
|
local_checksum = calculate_sha256(iso_file_path)
|
|
158
178
|
return remote_checksum == local_checksum
|
|
159
179
|
|
|
180
|
+
def _upload_iso_file(
|
|
181
|
+
self, iso_file_path: Path, organization_id: str, operating_system_slug: str
|
|
182
|
+
):
|
|
183
|
+
iso_upload_result = self.primitive.files.upload_file_direct(
|
|
184
|
+
path=iso_file_path,
|
|
185
|
+
organization_id=organization_id,
|
|
186
|
+
key_prefix=f"{self.operating_systems_key_prefix}/{operating_system_slug}",
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
if not iso_upload_result or iso_upload_result.data is None:
|
|
190
|
+
logger.error("Unable to upload iso file")
|
|
191
|
+
raise Exception("Unable to upload iso file")
|
|
192
|
+
|
|
193
|
+
iso_upload_data = iso_upload_result.data
|
|
194
|
+
iso_file_id = iso_upload_data.get("fileUpdate", {}).get("id")
|
|
195
|
+
|
|
196
|
+
if not iso_file_id:
|
|
197
|
+
logger.error("Unable to upload iso file")
|
|
198
|
+
raise Exception("Unable to upload iso file")
|
|
199
|
+
|
|
200
|
+
return iso_file_id
|
|
201
|
+
|
|
202
|
+
def _upload_checksum_file(
|
|
203
|
+
self, checksum_file_path: Path, organization_id: str, operating_system_slug: str
|
|
204
|
+
):
|
|
205
|
+
checksum_upload_response = self.primitive.files.upload_file_via_api(
|
|
206
|
+
path=checksum_file_path,
|
|
207
|
+
organization_id=organization_id,
|
|
208
|
+
key_prefix=f"{self.operating_systems_key_prefix}/{operating_system_slug}",
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
if not checksum_upload_response.ok:
|
|
212
|
+
logger.error("Unable to upload checksum file")
|
|
213
|
+
raise Exception("Unable to upload checksum file")
|
|
214
|
+
|
|
215
|
+
checksum_file_id = (
|
|
216
|
+
checksum_upload_response.json()
|
|
217
|
+
.get("data", {})
|
|
218
|
+
.get("fileUpload", {})
|
|
219
|
+
.get("id", {})
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
if not checksum_file_id:
|
|
223
|
+
logger.error("Unable to upload checksum file")
|
|
224
|
+
raise Exception("Unable to upload checksum file")
|
|
225
|
+
|
|
226
|
+
return checksum_file_id
|
|
227
|
+
|
|
160
228
|
@guard
|
|
161
|
-
def
|
|
229
|
+
def create(
|
|
230
|
+
self,
|
|
231
|
+
slug: str,
|
|
232
|
+
iso_file: str,
|
|
233
|
+
checksum_file: str,
|
|
234
|
+
checksum_file_type: str,
|
|
235
|
+
organization_id: str,
|
|
236
|
+
):
|
|
237
|
+
formatted_slug = slugify(slug)
|
|
238
|
+
is_slug_available = self.primitive.operating_systems._is_slug_available(
|
|
239
|
+
slug=formatted_slug,
|
|
240
|
+
organization_id=organization_id,
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
if not is_slug_available:
|
|
244
|
+
raise Exception(
|
|
245
|
+
f"Operating system with slug {formatted_slug} already exists."
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
is_known_checksum_file_type = (
|
|
249
|
+
checksum_file_type
|
|
250
|
+
in self.primitive.operating_systems.OperatingSystemChecksumFileType._value2member_map_
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
if not is_known_checksum_file_type:
|
|
254
|
+
raise Exception(
|
|
255
|
+
f"Operating system checksum file type {checksum_file_type} is not supported."
|
|
256
|
+
+ f" Supported types are: {''.join([type.value for type in self.primitive.operating_systems.OperatingSystemChecksumFileType])}"
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
iso_file_path = Path(iso_file)
|
|
260
|
+
checksum_file_path = Path(checksum_file)
|
|
261
|
+
|
|
262
|
+
if not iso_file_path.is_file():
|
|
263
|
+
raise Exception(
|
|
264
|
+
f"ISO file {iso_file_path} does not exist or is not a file."
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
if not checksum_file_path.is_file():
|
|
268
|
+
raise Exception(
|
|
269
|
+
f"Checksum file {checksum_file_path} does not exist or is not a file."
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
logger.info("Uploading iso file. This may take a while...")
|
|
273
|
+
iso_file_id = self.primitive.operating_systems._upload_iso_file(
|
|
274
|
+
iso_file_path=iso_file_path,
|
|
275
|
+
organization_id=organization_id,
|
|
276
|
+
operating_system_slug=formatted_slug,
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
logger.info("Uploading checksum file")
|
|
280
|
+
checksum_file_id = self.primitive.operating_systems._upload_checksum_file(
|
|
281
|
+
checksum_file_path=checksum_file_path,
|
|
282
|
+
organization_id=organization_id,
|
|
283
|
+
operating_system_slug=formatted_slug,
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
logger.info("Creating operating system in primitive.")
|
|
287
|
+
operating_system_create_response = (
|
|
288
|
+
self.primitive.operating_systems._create_query(
|
|
289
|
+
slug=formatted_slug,
|
|
290
|
+
checksum_file_id=checksum_file_id,
|
|
291
|
+
checksum_file_type=checksum_file_type,
|
|
292
|
+
organization_id=organization_id,
|
|
293
|
+
iso_file_id=iso_file_id,
|
|
294
|
+
)
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
if "id" not in operating_system_create_response:
|
|
298
|
+
raise Exception("Failed to create operating system")
|
|
299
|
+
|
|
300
|
+
return operating_system_create_response
|
|
301
|
+
|
|
302
|
+
@guard
|
|
303
|
+
def _create_query(
|
|
162
304
|
self,
|
|
163
305
|
slug: str,
|
|
164
306
|
organization_id: str,
|
|
@@ -181,7 +323,7 @@ class OperatingSystems(BaseAction):
|
|
|
181
323
|
return result.data.get("operatingSystemCreate")
|
|
182
324
|
|
|
183
325
|
@guard
|
|
184
|
-
def
|
|
326
|
+
def list(
|
|
185
327
|
self,
|
|
186
328
|
organization_id: str,
|
|
187
329
|
slug: str | None = None,
|
|
@@ -205,26 +347,97 @@ class OperatingSystems(BaseAction):
|
|
|
205
347
|
query, variable_values=variables, get_execution_result=True
|
|
206
348
|
)
|
|
207
349
|
|
|
208
|
-
|
|
350
|
+
edges = result.data.get("operatingSystemList").get("edges", [])
|
|
351
|
+
|
|
352
|
+
nodes = [edge.get("node") for edge in edges]
|
|
353
|
+
|
|
354
|
+
return nodes
|
|
209
355
|
|
|
210
356
|
@guard
|
|
211
|
-
def
|
|
212
|
-
self,
|
|
357
|
+
def download(
|
|
358
|
+
self,
|
|
359
|
+
organization_id: str,
|
|
360
|
+
id: str | None = None,
|
|
361
|
+
slug: str | None = None,
|
|
362
|
+
directory: str | None = None,
|
|
213
363
|
):
|
|
364
|
+
operating_system = self.primitive.operating_systems.get(
|
|
365
|
+
organization_id=organization_id, slug=slug, id=id
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
is_cached = self.primitive.operating_systems.is_operating_system_cached(
|
|
369
|
+
slug=operating_system["slug"],
|
|
370
|
+
directory=directory,
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
if is_cached:
|
|
374
|
+
raise Exception(
|
|
375
|
+
"Operating system already exists in cache, aborting download."
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
download_directory = (
|
|
379
|
+
Path(directory) / operating_system["slug"]
|
|
380
|
+
if directory
|
|
381
|
+
else (get_operating_systems_cache() / operating_system["slug"])
|
|
382
|
+
)
|
|
383
|
+
checksum_directory = download_directory / "checksum"
|
|
384
|
+
checksum_file_path = (
|
|
385
|
+
checksum_directory / operating_system["checksumFile"]["fileName"]
|
|
386
|
+
)
|
|
387
|
+
iso_directory = download_directory / "iso"
|
|
388
|
+
iso_file_path = iso_directory / operating_system["isoFile"]["fileName"]
|
|
389
|
+
|
|
390
|
+
if not iso_directory.exists():
|
|
391
|
+
iso_directory.mkdir(parents=True)
|
|
392
|
+
|
|
393
|
+
if not checksum_directory.exists():
|
|
394
|
+
checksum_directory.mkdir(parents=True)
|
|
395
|
+
|
|
396
|
+
logger.info("Downloading operating system iso")
|
|
397
|
+
self.primitive.files.download_file(
|
|
398
|
+
file_id=operating_system["isoFile"]["id"],
|
|
399
|
+
output_path=iso_directory,
|
|
400
|
+
organization_id=organization_id,
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
logger.info("Downloading operating system checksum")
|
|
404
|
+
self.primitive.files.download_file(
|
|
405
|
+
file_id=operating_system["checksumFile"]["id"],
|
|
406
|
+
output_path=checksum_directory,
|
|
407
|
+
organization_id=organization_id,
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
logger.info("Validating iso checksum")
|
|
411
|
+
checksum_file_type = (
|
|
412
|
+
self.primitive.operating_systems.OperatingSystemChecksumFileType[
|
|
413
|
+
operating_system["checksumFileType"]
|
|
414
|
+
]
|
|
415
|
+
)
|
|
416
|
+
checksum_valid = self.primitive.operating_systems._validate_checksum(
|
|
417
|
+
operating_system["slug"],
|
|
418
|
+
iso_file_path,
|
|
419
|
+
checksum_file_path,
|
|
420
|
+
checksum_file_type=checksum_file_type,
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
if not checksum_valid:
|
|
424
|
+
raise Exception(
|
|
425
|
+
"Checksums did not match: file may have been corrupted during download."
|
|
426
|
+
+ f"\nTry deleting the directory {get_operating_systems_cache()}/{operating_system['slug']} and running this command again."
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
return download_directory
|
|
430
|
+
|
|
431
|
+
@guard
|
|
432
|
+
def get(self, organization_id: str, slug: str | None = None, id: str | None = None):
|
|
214
433
|
if not (slug or id):
|
|
215
434
|
raise Exception("Slug or id must be provided.")
|
|
216
435
|
if slug and id:
|
|
217
436
|
raise Exception("Only one of slug or id must be provided.")
|
|
218
437
|
|
|
219
|
-
|
|
220
|
-
organization_id=organization_id, slug=slug, id=id
|
|
221
|
-
)
|
|
222
|
-
|
|
223
|
-
edges = operating_system_list_result.data.get("operatingSystemList").get(
|
|
224
|
-
"edges", []
|
|
225
|
-
)
|
|
438
|
+
operating_systems = self.list(organization_id=organization_id, slug=slug, id=id)
|
|
226
439
|
|
|
227
|
-
if len(
|
|
440
|
+
if len(operating_systems) == 0:
|
|
228
441
|
if slug:
|
|
229
442
|
logger.error(f"No operating system found for slug '{slug}'.")
|
|
230
443
|
raise Exception(f"No operating system found for slug {slug}.")
|
|
@@ -232,10 +445,10 @@ class OperatingSystems(BaseAction):
|
|
|
232
445
|
logger.error(f"No operating system found for ID {id}.")
|
|
233
446
|
raise Exception(f"No operating system found for ID {id}.")
|
|
234
447
|
|
|
235
|
-
return
|
|
448
|
+
return operating_systems[0]
|
|
236
449
|
|
|
237
450
|
@guard
|
|
238
|
-
def
|
|
451
|
+
def _is_slug_available(self, slug: str, organization_id: str):
|
|
239
452
|
query = gql(operating_system_list_query)
|
|
240
453
|
|
|
241
454
|
variables = {
|
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
|
|
3
1
|
import click
|
|
4
2
|
import typing
|
|
5
3
|
from loguru import logger
|
|
6
4
|
|
|
7
|
-
from ..utils.cache import get_operating_systems_cache
|
|
8
|
-
from ..utils.text import slugify
|
|
9
5
|
|
|
10
6
|
if typing.TYPE_CHECKING:
|
|
11
7
|
from ..client import Primitive
|
|
@@ -18,22 +14,14 @@ def cli(context):
|
|
|
18
14
|
pass
|
|
19
15
|
|
|
20
16
|
|
|
21
|
-
@cli.command("
|
|
22
|
-
@click.option("--id", help="Operating system ID", required=False)
|
|
23
|
-
@click.option("--slug", help="Operating system slug", required=False)
|
|
24
|
-
@click.option("--organization-slug", help="Organization slug", required=False)
|
|
17
|
+
@cli.command("list")
|
|
25
18
|
@click.option(
|
|
26
|
-
"--
|
|
27
|
-
help="
|
|
19
|
+
"--organization-slug",
|
|
20
|
+
help="Organization slug to list operating systems for",
|
|
28
21
|
required=False,
|
|
29
22
|
)
|
|
30
23
|
@click.pass_context
|
|
31
|
-
def
|
|
32
|
-
if not (id or slug):
|
|
33
|
-
raise click.UsageError("You must provide either --id or --slug.")
|
|
34
|
-
if id and slug:
|
|
35
|
-
raise click.UsageError("You can only specify one of --id or --slug.")
|
|
36
|
-
|
|
24
|
+
def operating_systems_list_command(context, organization_slug):
|
|
37
25
|
primitive: Primitive = context.obj.get("PRIMITIVE")
|
|
38
26
|
|
|
39
27
|
organization = (
|
|
@@ -44,81 +32,114 @@ def download(context, id, slug, organization_slug, directory):
|
|
|
44
32
|
|
|
45
33
|
if not organization:
|
|
46
34
|
if organization_slug:
|
|
47
|
-
logger.error(f"No organization found with slug {
|
|
35
|
+
logger.error(f"No organization found with slug {organization_slug}")
|
|
48
36
|
else:
|
|
49
37
|
logger.error("Failed to fetch default organization")
|
|
50
38
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
organization_id=organization["id"], slug=slug, id=id
|
|
54
|
-
)
|
|
55
|
-
except Exception:
|
|
56
|
-
logger.error("Unable to fetch operating system")
|
|
57
|
-
return
|
|
58
|
-
|
|
59
|
-
is_cached = primitive.operating_systems.is_operating_system_cached(
|
|
60
|
-
slug=operating_system["slug"],
|
|
61
|
-
directory=directory,
|
|
39
|
+
operating_systems = primitive.operating_systems.list(
|
|
40
|
+
organization_id=organization["id"]
|
|
62
41
|
)
|
|
42
|
+
operating_system_slugs = [
|
|
43
|
+
operating_system["slug"] for operating_system in operating_systems
|
|
44
|
+
]
|
|
63
45
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
operating_system_directory = (
|
|
69
|
-
Path(directory) / operating_system["slug"]
|
|
70
|
-
if directory
|
|
71
|
-
else (get_operating_systems_cache() / operating_system["slug"])
|
|
46
|
+
newline = "\n"
|
|
47
|
+
logger.info(
|
|
48
|
+
f"Operating systems: {newline}- {f'{newline}- '.join(operating_system_slugs)}"
|
|
72
49
|
)
|
|
73
|
-
checksum_directory = operating_system_directory / "checksum"
|
|
74
|
-
checksum_file_path = (
|
|
75
|
-
checksum_directory / operating_system["checksumFile"]["fileName"]
|
|
76
|
-
)
|
|
77
|
-
iso_directory = operating_system_directory / "iso"
|
|
78
|
-
iso_file_path = iso_directory / operating_system["isoFile"]["fileName"]
|
|
79
50
|
|
|
80
|
-
if not iso_directory.exists():
|
|
81
|
-
iso_directory.mkdir(parents=True)
|
|
82
51
|
|
|
83
|
-
|
|
84
|
-
|
|
52
|
+
@cli.command("create")
|
|
53
|
+
@click.option("--slug", help="Slug for created operating system", required=True)
|
|
54
|
+
@click.option(
|
|
55
|
+
"--iso-file", help="Path to operating system iso file to upload", required=True
|
|
56
|
+
)
|
|
57
|
+
@click.option(
|
|
58
|
+
"--checksum-file",
|
|
59
|
+
help="Path to operating system checksum file to upload",
|
|
60
|
+
required=True,
|
|
61
|
+
)
|
|
62
|
+
@click.option(
|
|
63
|
+
"--checksum-file-type", help="The type of the checksum file", required=True
|
|
64
|
+
)
|
|
65
|
+
@click.option(
|
|
66
|
+
"--organization-slug",
|
|
67
|
+
help="Organization to create the operating system in",
|
|
68
|
+
required=False,
|
|
69
|
+
)
|
|
70
|
+
@click.pass_context
|
|
71
|
+
def create_command(
|
|
72
|
+
context, slug, iso_file, checksum_file, checksum_file_type, organization_slug
|
|
73
|
+
):
|
|
74
|
+
primitive: Primitive = context.obj.get("PRIMITIVE")
|
|
85
75
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
organization_id=organization["id"],
|
|
76
|
+
organization = (
|
|
77
|
+
primitive.organizations.get_organization(slug=organization_slug)
|
|
78
|
+
if organization_slug
|
|
79
|
+
else primitive.organizations.get_default_organization()
|
|
91
80
|
)
|
|
92
81
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
82
|
+
if not organization:
|
|
83
|
+
if organization_slug:
|
|
84
|
+
logger.error(f"No organization found with slug {organization_slug}")
|
|
85
|
+
return
|
|
86
|
+
else:
|
|
87
|
+
logger.error("Failed to fetch default organization")
|
|
88
|
+
return
|
|
99
89
|
|
|
100
|
-
logger.info("Validating iso checksum")
|
|
101
90
|
try:
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
)
|
|
107
|
-
checksum_valid = primitive.operating_systems.validate_checksum(
|
|
108
|
-
operating_system["slug"],
|
|
109
|
-
iso_file_path,
|
|
110
|
-
checksum_file_path,
|
|
91
|
+
primitive.operating_systems.create(
|
|
92
|
+
slug=slug,
|
|
93
|
+
iso_file=iso_file,
|
|
94
|
+
checksum_file=checksum_file,
|
|
111
95
|
checksum_file_type=checksum_file_type,
|
|
96
|
+
organization_id=organization["id"],
|
|
112
97
|
)
|
|
113
|
-
except Exception:
|
|
114
|
-
logger.error(
|
|
98
|
+
except Exception as error:
|
|
99
|
+
logger.error(error)
|
|
115
100
|
return
|
|
116
101
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
102
|
+
logger.success("Operating system created in primitive.")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@cli.command("download")
|
|
106
|
+
@click.option("--id", help="Operating system ID", required=False)
|
|
107
|
+
@click.option("--slug", help="Operating system slug", required=False)
|
|
108
|
+
@click.option("--organization-slug", help="Organization slug", required=False)
|
|
109
|
+
@click.option(
|
|
110
|
+
"--directory",
|
|
111
|
+
help="Directory to download the operating system files to",
|
|
112
|
+
required=False,
|
|
113
|
+
)
|
|
114
|
+
@click.pass_context
|
|
115
|
+
def download(context, id, slug, organization_slug, directory):
|
|
116
|
+
if not (id or slug):
|
|
117
|
+
raise click.UsageError("You must provide either --id or --slug.")
|
|
118
|
+
if id and slug:
|
|
119
|
+
raise click.UsageError("You can only specify one of --id or --slug.")
|
|
120
|
+
|
|
121
|
+
primitive: Primitive = context.obj.get("PRIMITIVE")
|
|
122
|
+
|
|
123
|
+
organization = (
|
|
124
|
+
primitive.organizations.get_organization(slug=organization_slug)
|
|
125
|
+
if organization_slug
|
|
126
|
+
else primitive.organizations.get_default_organization()
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
if not organization:
|
|
130
|
+
if organization_slug:
|
|
131
|
+
logger.error(f"No organization found with slug {organization_slug}")
|
|
132
|
+
return
|
|
133
|
+
else:
|
|
134
|
+
logger.error("Failed to fetch default organization")
|
|
135
|
+
return
|
|
136
|
+
|
|
137
|
+
try:
|
|
138
|
+
operating_system_directory = primitive.operating_systems.download(
|
|
139
|
+
id=id, slug=slug, organization_id=organization["id"], directory=directory
|
|
121
140
|
)
|
|
141
|
+
except Exception as error:
|
|
142
|
+
logger.error(error)
|
|
122
143
|
return
|
|
123
144
|
|
|
124
145
|
logger.success(
|
|
@@ -133,6 +154,30 @@ def remotes(context):
|
|
|
133
154
|
pass
|
|
134
155
|
|
|
135
156
|
|
|
157
|
+
@remotes.command("download")
|
|
158
|
+
@click.pass_context
|
|
159
|
+
@click.argument("operating-system")
|
|
160
|
+
@click.option(
|
|
161
|
+
"--directory",
|
|
162
|
+
help="Directory to download the operating system files to",
|
|
163
|
+
required=False,
|
|
164
|
+
)
|
|
165
|
+
def operating_system_remotes_download_command(
|
|
166
|
+
context, operating_system, directory=None
|
|
167
|
+
):
|
|
168
|
+
primitive: Primitive = context.obj.get("PRIMITIVE")
|
|
169
|
+
|
|
170
|
+
try:
|
|
171
|
+
primitive.operating_systems.download_remote(
|
|
172
|
+
remote_operating_system_name=operating_system, directory=directory
|
|
173
|
+
)
|
|
174
|
+
except Exception as error:
|
|
175
|
+
logger.error(error)
|
|
176
|
+
return
|
|
177
|
+
|
|
178
|
+
logger.success(f"Successfully downloaded operating system files to {directory}")
|
|
179
|
+
|
|
180
|
+
|
|
136
181
|
@remotes.command("mirror")
|
|
137
182
|
@click.pass_context
|
|
138
183
|
@click.argument("operating-system")
|
|
@@ -142,13 +187,16 @@ def remotes(context):
|
|
|
142
187
|
help="Slug of the organization to upload the operating system to",
|
|
143
188
|
required=False,
|
|
144
189
|
)
|
|
190
|
+
@click.option(
|
|
191
|
+
"--directory",
|
|
192
|
+
help="Directory to download the operating system files to",
|
|
193
|
+
required=False,
|
|
194
|
+
)
|
|
145
195
|
def operating_system_mirror_command(
|
|
146
|
-
context, operating_system, slug=None, organization_slug=None
|
|
196
|
+
context, operating_system, slug=None, organization_slug=None, directory=None
|
|
147
197
|
):
|
|
148
198
|
primitive: Primitive = context.obj.get("PRIMITIVE")
|
|
149
199
|
|
|
150
|
-
operating_system_slug = slugify(slug) if slug else slugify(operating_system)
|
|
151
|
-
|
|
152
200
|
organization = (
|
|
153
201
|
primitive.organizations.get_organization(slug=organization_slug)
|
|
154
202
|
if organization_slug
|
|
@@ -157,112 +205,42 @@ def operating_system_mirror_command(
|
|
|
157
205
|
|
|
158
206
|
if not organization:
|
|
159
207
|
if organization_slug:
|
|
160
|
-
logger.error(f"No organization found with slug {
|
|
208
|
+
logger.error(f"No organization found with slug {organization_slug}.")
|
|
209
|
+
return
|
|
161
210
|
else:
|
|
162
211
|
logger.error("Failed to fetch default organization.")
|
|
212
|
+
return
|
|
163
213
|
|
|
164
|
-
is_slug_available = primitive.operating_systems.is_slug_available(
|
|
165
|
-
slug=operating_system_slug,
|
|
166
|
-
organization_id=organization["id"],
|
|
167
|
-
)
|
|
168
|
-
|
|
169
|
-
if not is_slug_available:
|
|
170
|
-
logger.error(
|
|
171
|
-
f"Operating system slug {operating_system_slug} already exists."
|
|
172
|
-
+ " Please specify the --slug parameter to mirror the operating system with a different slug."
|
|
173
|
-
)
|
|
174
|
-
return
|
|
175
|
-
|
|
176
|
-
try:
|
|
177
|
-
iso_file_path, checksum_file_path = (
|
|
178
|
-
primitive.operating_systems.download_from_remote(operating_system)
|
|
179
|
-
)
|
|
180
|
-
except Exception:
|
|
181
|
-
logger.error("Unable to download operating system")
|
|
182
|
-
return
|
|
183
|
-
|
|
184
|
-
logger.info("Validating iso checksum")
|
|
185
214
|
try:
|
|
186
|
-
|
|
187
|
-
operating_system,
|
|
215
|
+
iso_file_path, checksum_file_path = primitive.operating_systems.download_remote(
|
|
216
|
+
operating_system, directory=directory
|
|
188
217
|
)
|
|
189
|
-
except Exception:
|
|
190
|
-
logger.error("Failed to validate checksum.")
|
|
191
|
-
return
|
|
192
|
-
|
|
193
|
-
if not checksum_valid:
|
|
194
|
-
logger.error(
|
|
195
|
-
"Checksums did not match: file may have been corrupted during download."
|
|
196
|
-
+ f"\nTry deleting the directory {get_operating_systems_cache()}/{operating_system} and running this command again."
|
|
197
|
-
)
|
|
198
|
-
return
|
|
199
218
|
|
|
200
|
-
|
|
219
|
+
checksum_file_type = primitive.operating_systems.get_remote_info(
|
|
220
|
+
operating_system
|
|
221
|
+
)["checksum_file_type"]
|
|
201
222
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
organization_id=organization["id"],
|
|
208
|
-
key_prefix=file_key_prefix,
|
|
209
|
-
)
|
|
210
|
-
|
|
211
|
-
if iso_upload_result and iso_upload_result.data is not None:
|
|
212
|
-
iso_upload_data = iso_upload_result.data
|
|
213
|
-
iso_file_id = iso_upload_data.get("fileUpdate", {}).get("id")
|
|
214
|
-
|
|
215
|
-
if not iso_file_id:
|
|
216
|
-
logger.error("Unable to upload iso file")
|
|
217
|
-
return
|
|
218
|
-
|
|
219
|
-
checksum_upload_response = primitive.files.upload_file_via_api(
|
|
220
|
-
path=checksum_file_path,
|
|
221
|
-
organization_id=organization["id"],
|
|
222
|
-
key_prefix=file_key_prefix,
|
|
223
|
-
)
|
|
224
|
-
|
|
225
|
-
if not checksum_upload_response.ok:
|
|
226
|
-
logger.error("Unable to upload checksum file")
|
|
227
|
-
return
|
|
228
|
-
|
|
229
|
-
checksum_file_id = (
|
|
230
|
-
checksum_upload_response.json()
|
|
231
|
-
.get("data", {})
|
|
232
|
-
.get("fileUpload", {})
|
|
233
|
-
.get("id", {})
|
|
234
|
-
)
|
|
235
|
-
|
|
236
|
-
if not checksum_file_id:
|
|
237
|
-
logger.error("Unable to upload checksum file")
|
|
238
|
-
return
|
|
239
|
-
|
|
240
|
-
logger.info("Creating operating system in primitive.")
|
|
241
|
-
operating_system_create_response = (
|
|
242
|
-
primitive.operating_systems.create_operating_system(
|
|
243
|
-
slug=operating_system_slug,
|
|
244
|
-
checksum_file_id=checksum_file_id,
|
|
245
|
-
checksum_file_type=primitive.operating_systems.get_checksum_file_type(
|
|
246
|
-
operating_system
|
|
247
|
-
).value,
|
|
223
|
+
primitive.operating_systems.create(
|
|
224
|
+
slug=slug if slug else operating_system,
|
|
225
|
+
iso_file=iso_file_path,
|
|
226
|
+
checksum_file=checksum_file_path,
|
|
227
|
+
checksum_file_type=checksum_file_type.value,
|
|
248
228
|
organization_id=organization["id"],
|
|
249
|
-
iso_file_id=iso_file_id,
|
|
250
229
|
)
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
if "id" not in operating_system_create_response:
|
|
254
|
-
logger.error("Failed to create operating system")
|
|
230
|
+
except Exception as error:
|
|
231
|
+
logger.error(error)
|
|
255
232
|
return
|
|
256
233
|
|
|
257
|
-
logger.success("
|
|
234
|
+
logger.success("Successfully mirrored operating system")
|
|
258
235
|
|
|
259
236
|
|
|
260
237
|
@remotes.command("list")
|
|
261
238
|
@click.pass_context
|
|
262
|
-
def
|
|
239
|
+
def remote_operating_systems_list_command(context):
|
|
263
240
|
primitive: Primitive = context.obj.get("PRIMITIVE")
|
|
264
|
-
|
|
265
|
-
|
|
241
|
+
remotes_list = primitive.operating_systems.list_remotes()
|
|
242
|
+
remote_slugs = [remote["slug"] for remote in remotes_list]
|
|
243
|
+
newline = "\n"
|
|
244
|
+
logger.info(
|
|
245
|
+
f"Remote operating systems: {newline}- {f'{newline}- '.join(remote_slugs)}"
|
|
266
246
|
)
|
|
267
|
-
logger.info("Remote operating systems available for download:")
|
|
268
|
-
logger.info("\n".join(remote_operating_system_names))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: primitive
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.72
|
|
4
4
|
Project-URL: Documentation, https://github.com//primitivecorp/primitive-cli#readme
|
|
5
5
|
Project-URL: Issues, https://github.com//primitivecorp/primitive-cli/issues
|
|
6
6
|
Project-URL: Source, https://github.com//primitivecorp/primitive-cli
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
primitive/__about__.py,sha256=
|
|
1
|
+
primitive/__about__.py,sha256=pYIjjw7lxGTZ015Ir3hYhbZnOjU5lRO-cLrPQE5MBpw,130
|
|
2
2
|
primitive/__init__.py,sha256=bwKdgggKNVssJFVPfKSxqFMz4IxSr54WWbmiZqTMPNI,106
|
|
3
3
|
primitive/cli.py,sha256=ga1TcPKyUkGNGZ76CjIQqTKWn1r9di5k_uRbLljY07w,2745
|
|
4
4
|
primitive/client.py,sha256=EyXLOFwKUT_ZB3at5m4hbdtjgaKVVFoiDdE0bP0_JfI,3966
|
|
5
5
|
primitive/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
6
|
primitive/agent/actions.py,sha256=SnYJQKOQgzGL2-zopWT2ioy9w7OP9pqAbS3itMq-614,9528
|
|
7
7
|
primitive/agent/commands.py,sha256=o847pK7v7EWQGG67tky6a33qtwoutX6LZrP2FIS_NOk,388
|
|
8
|
-
primitive/agent/
|
|
8
|
+
primitive/agent/pxe.py,sha256=XTu9Plj2mlmlL6YjcDqlfS_IGBK4i7aGKhTL-k_wnBU,2542
|
|
9
|
+
primitive/agent/runner.py,sha256=nel7NiqWoIh5HFdoor9YIsPpMBJPHsevg3XtAHrKvc0,16619
|
|
9
10
|
primitive/agent/uploader.py,sha256=DT_Nzt5eOTm_uRcYKm1sjBBaQZzp5iNZ_uN5XktfQ30,3382
|
|
10
11
|
primitive/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
12
|
primitive/auth/actions.py,sha256=9NIEXJ1BNJutJs6AMMSjMN_ziONUAUhY_xHwojYJCLA,942
|
|
@@ -40,7 +41,7 @@ primitive/graphql/relay.py,sha256=bmij2AjdpURQ6GGVCxwWhauF-r_SxuAU2oJ4sDbLxpI,72
|
|
|
40
41
|
primitive/graphql/sdk.py,sha256=dE4TD8KiTKw3Y0uiw5XrIcuZGqexE47eSlPaPD6jDGo,1545
|
|
41
42
|
primitive/graphql/utility_fragments.py,sha256=uIjwILC4QtWNyO5vu77VjQf_p0jvP3A9q_6zRq91zqs,303
|
|
42
43
|
primitive/hardware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
43
|
-
primitive/hardware/actions.py,sha256=
|
|
44
|
+
primitive/hardware/actions.py,sha256=WdI7dd7TJen6xF8h63kH8PbCXydJfHlFF56QZdAgh5w,44645
|
|
44
45
|
primitive/hardware/android.py,sha256=tu7pBPxWFrIwb_mm5CEdFFf1_veNDOKjOCQg13i_Lh4,2758
|
|
45
46
|
primitive/hardware/commands.py,sha256=ZAbrqGuK4c4aCYPySaTUt51qFsf_F3Zsb0KaLfeVnmA,5045
|
|
46
47
|
primitive/hardware/ui.py,sha256=12rucuZ2s-w5R4bKyxON5dEbrdDnVf5sbj3K_nbdo44,2473
|
|
@@ -56,18 +57,18 @@ primitive/jobs/graphql/fragments.py,sha256=I-Ly0TGARt_TdenLQ7877pGLWBzono_IMj7NC
|
|
|
56
57
|
primitive/jobs/graphql/mutations.py,sha256=8ASvCmwQh7cMeeiykOdYaYVryG8FRIuVF6v_J8JJZuw,219
|
|
57
58
|
primitive/jobs/graphql/queries.py,sha256=57B5mSnAYjNEFFk1P69odON0fqkf7FyhsA316pcxb6g,1712
|
|
58
59
|
primitive/messaging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
59
|
-
primitive/messaging/provider.py,sha256=
|
|
60
|
-
primitive/monitor/actions.py,sha256=
|
|
60
|
+
primitive/messaging/provider.py,sha256=BCkhRq_F6jKqf26x-9Da3ebCqgpjwxynRHIu7I7iFeQ,4432
|
|
61
|
+
primitive/monitor/actions.py,sha256=Ky4KPNJc5c0LaxZkDs8-7pP2bQ6xNBFxmNf8eAmi7ag,11079
|
|
61
62
|
primitive/monitor/commands.py,sha256=VDlEL_Qpm_ysHxug7VpI0cVAZ0ny6AS91Y58D7F1zkU,409
|
|
62
63
|
primitive/network/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
63
|
-
primitive/network/actions.py,sha256=
|
|
64
|
+
primitive/network/actions.py,sha256=iw6X5zKdsFCQ8vAw_glEI6sdtVUeXdauW33PjsKL_QU,18874
|
|
64
65
|
primitive/network/commands.py,sha256=wVszHkmmY85dwlEJR726vqx6rDcfD4XtAptgiHf5p4U,1361
|
|
65
66
|
primitive/network/redfish.py,sha256=uOtAS_Dwc4I4bnWKNSTCQ_xsj5LTtRzW5v2P_fWaSJM,4248
|
|
66
|
-
primitive/network/ssh.py,sha256=
|
|
67
|
+
primitive/network/ssh.py,sha256=evtZFWFsKfuN4MeFtPpvzjBM1a5Q0O06rxd6ZkSzoJo,4377
|
|
67
68
|
primitive/network/ui.py,sha256=-AV6OD1O3_nsTrgS-W49R6LfDN0vmXmz0Dli_S--LxE,685
|
|
68
69
|
primitive/operating_systems/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
69
|
-
primitive/operating_systems/actions.py,sha256=
|
|
70
|
-
primitive/operating_systems/commands.py,sha256=
|
|
70
|
+
primitive/operating_systems/actions.py,sha256=SNeYC4QgG0H-ybmIQaAXhDj6c9yYG6BgIQfzU2wm8hY,16993
|
|
71
|
+
primitive/operating_systems/commands.py,sha256=4qG3Ggf-sqLa0Cr6MErpjd6TOoc5_bE5tgJ8LigKKnw,7288
|
|
71
72
|
primitive/operating_systems/graphql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
72
73
|
primitive/operating_systems/graphql/mutations.py,sha256=L2ZeWEfBK22QAv2AsfDQWHKcwH5SWvMvcmOOftiEM7w,699
|
|
73
74
|
primitive/operating_systems/graphql/queries.py,sha256=TJzcYbFP4SowsFJb8Wv8Q1r5_gCZRKbobEzniu9LWFk,738
|
|
@@ -112,8 +113,8 @@ primitive/utils/psutil.py,sha256=xa7ef435UL37jyjmUPbEqCO2ayQMpCs0HCrxVEvLcuM,763
|
|
|
112
113
|
primitive/utils/shell.py,sha256=Z4zxmOaSyGCrS0D6I436iQci-ewHLt4UxVg1CD9Serc,2171
|
|
113
114
|
primitive/utils/text.py,sha256=XiESMnlhjQ534xE2hMNf08WehE1SKaYFRNih0MmnK0k,829
|
|
114
115
|
primitive/utils/x509.py,sha256=HwHRPqakTHWd40ny-9O_yNknSL1Cxo50O0UCjXHFq04,3796
|
|
115
|
-
primitive-0.2.
|
|
116
|
-
primitive-0.2.
|
|
117
|
-
primitive-0.2.
|
|
118
|
-
primitive-0.2.
|
|
119
|
-
primitive-0.2.
|
|
116
|
+
primitive-0.2.72.dist-info/METADATA,sha256=L8m-fkO2D2xT8U2iUqr62x_NCoIvG8Rv3Em-RSlFAlY,3569
|
|
117
|
+
primitive-0.2.72.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
118
|
+
primitive-0.2.72.dist-info/entry_points.txt,sha256=p1K8DMCWka5FqLlqP1sPek5Uovy9jq8u51gUsP-z334,48
|
|
119
|
+
primitive-0.2.72.dist-info/licenses/LICENSE.txt,sha256=B8kmQMJ2sxYygjCLBk770uacaMci4mPSoJJ8WoDBY_c,1098
|
|
120
|
+
primitive-0.2.72.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|