primitive 0.2.10__py3-none-any.whl → 0.2.12__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.
@@ -3,13 +3,11 @@ import typing
3
3
  import click
4
4
 
5
5
  from ..utils.printer import print_result
6
+ from .ui import render_hardware_table
6
7
 
7
8
  if typing.TYPE_CHECKING:
8
9
  from ..client import Primitive
9
10
 
10
- from rich.console import Console
11
- from rich.table import Table
12
-
13
11
 
14
12
  @click.group()
15
13
  @click.pass_context
@@ -71,71 +69,6 @@ def checkin_command(context):
71
69
  print_result(message=message, context=context, fg="green")
72
70
 
73
71
 
74
- def hardware_status_string(hardware):
75
- if activeReservation := hardware.get("activeReservation"):
76
- if activeReservation.get("status", None) == "in_progress":
77
- return "Reserved"
78
- if hardware.get("isQuarantined"):
79
- return "Quarantined"
80
- if not hardware.get("isOnline"):
81
- return "Offline"
82
- if not hardware.get("isHealthy"):
83
- return "Not healthy"
84
- if not hardware.get("isAvailable"):
85
- return "Not available"
86
- else:
87
- return "Available"
88
-
89
-
90
- def render_hardware_table(hardware_list):
91
- console = Console()
92
-
93
- table = Table(show_header=True, header_style="bold magenta")
94
- table.add_column("Organization")
95
- table.add_column("Name | Slug")
96
- table.add_column("Status")
97
- table.add_column("Reservation")
98
-
99
- for hardware in hardware_list:
100
- name = hardware.get("name")
101
- slug = hardware.get("slug")
102
- print_name = name
103
- if name != slug:
104
- print_name = f"{name} | {slug}"
105
- child_table = Table(show_header=False, header_style="bold magenta")
106
- child_table.add_column("Organization")
107
- child_table.add_column("Name | Slug")
108
- child_table.add_column("Status")
109
- child_table.add_column("Reservation", justify="right")
110
-
111
- table.add_row(
112
- hardware.get("organization").get("name"),
113
- print_name,
114
- hardware_status_string(hardware),
115
- f"{hardware.get('activeReservation').get('createdBy').get('username')} | {hardware.get('activeReservation').get('status')}"
116
- if hardware.get("activeReservation", None)
117
- else "",
118
- )
119
-
120
- if len(hardware.get("children", [])) > 0:
121
- for child in hardware.get("children"):
122
- name = child.get("name")
123
- slug = child.get("slug")
124
- print_name = name
125
- if name != slug:
126
- print_name = f"└── {name} | {slug}"
127
- table.add_row(
128
- hardware.get("organization").get("name"),
129
- print_name,
130
- hardware_status_string(hardware),
131
- f"{hardware.get('activeReservation').get('createdBy').get('username')} | {hardware.get('activeReservation').get('status')}"
132
- if hardware.get("activeReservation", None)
133
- else "",
134
- )
135
-
136
- console.print(table)
137
-
138
-
139
72
  @cli.command("list")
140
73
  @click.pass_context
141
74
  def list_command(context):
@@ -0,0 +1,67 @@
1
+ from rich.console import Console
2
+ from rich.table import Table
3
+
4
+
5
+ def render_hardware_table(hardware_list) -> None:
6
+ console = Console()
7
+
8
+ table = Table(show_header=True, header_style="bold #FFA800")
9
+ table.add_column("Organization")
10
+ table.add_column("Name | Slug")
11
+ table.add_column("Status")
12
+ table.add_column("Reservation")
13
+
14
+ for hardware in hardware_list:
15
+ name = hardware.get("name")
16
+ slug = hardware.get("slug")
17
+ print_name = name
18
+ if name != slug:
19
+ print_name = f"{name} | {slug}"
20
+ child_table = Table(show_header=False, header_style="bold #FFA800")
21
+ child_table.add_column("Organization")
22
+ child_table.add_column("Name | Slug")
23
+ child_table.add_column("Status")
24
+ child_table.add_column("Reservation", justify="right")
25
+
26
+ table.add_row(
27
+ hardware.get("organization").get("name"),
28
+ print_name,
29
+ hardware_status_string(hardware),
30
+ f"{hardware.get('activeReservation').get('createdBy').get('username')} | {hardware.get('activeReservation').get('status')}"
31
+ if hardware.get("activeReservation", None)
32
+ else "",
33
+ )
34
+
35
+ if len(hardware.get("children", [])) > 0:
36
+ for child in hardware.get("children"):
37
+ name = child.get("name")
38
+ slug = child.get("slug")
39
+ print_name = name
40
+ if name != slug:
41
+ print_name = f"└── {name} | {slug}"
42
+ table.add_row(
43
+ hardware.get("organization").get("name"),
44
+ print_name,
45
+ hardware_status_string(hardware),
46
+ f"{hardware.get('activeReservation').get('createdBy').get('username')} | {hardware.get('activeReservation').get('status')}"
47
+ if hardware.get("activeReservation", None)
48
+ else "",
49
+ )
50
+
51
+ console.print(table)
52
+
53
+
54
+ def hardware_status_string(hardware) -> str:
55
+ if activeReservation := hardware.get("activeReservation"):
56
+ if activeReservation.get("status", None) == "in_progress":
57
+ return "Reserved"
58
+ if hardware.get("isQuarantined"):
59
+ return "Quarantined"
60
+ if not hardware.get("isOnline"):
61
+ return "Offline"
62
+ if not hardware.get("isHealthy"):
63
+ return "Not healthy"
64
+ if not hardware.get("isAvailable"):
65
+ return "Not available"
66
+ else:
67
+ return "Available"
@@ -0,0 +1,199 @@
1
+ import sys
2
+ from time import sleep
3
+
4
+ import psutil
5
+ from loguru import logger
6
+
7
+ from primitive.__about__ import __version__
8
+ from primitive.db import sqlite
9
+ from primitive.db.models import JobRun
10
+ from primitive.utils.actions import BaseAction
11
+ from primitive.utils.exceptions import P_CLI_100
12
+
13
+
14
+ class Monitor(BaseAction):
15
+ def start(self):
16
+ logger.remove()
17
+ logger.add(
18
+ sink=sys.stderr,
19
+ format="<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | <level>{level: <8}</level> | <level>{message}</level>",
20
+ backtrace=True,
21
+ diagnose=True,
22
+ level="DEBUG" if self.primitive.DEBUG else "INFO",
23
+ )
24
+ logger.info("[*] primitive monitor")
25
+ logger.info(f"[*] Version: {__version__}")
26
+
27
+ try:
28
+ # hey stupid:
29
+ # do not set is_available to True here, it will mess up the reservation logic
30
+ # only set is_available after we've checked that no active reservation is present
31
+ # setting is_available of the parent also effects the children,
32
+ # which may have active reservations as well
33
+ self.primitive.hardware.check_in_http(is_online=True)
34
+ except Exception as exception:
35
+ logger.exception(f"Error checking in hardware: {exception}")
36
+ sys.exit(1)
37
+
38
+ # Initialize the database
39
+ sqlite.init()
40
+
41
+ try:
42
+ active_reservation_id = None
43
+ active_reservation_pk = None
44
+
45
+ while True:
46
+ # FIRST, check for jobs in the database that are running
47
+ db_job_runs = JobRun.objects.all()
48
+ for job_run in db_job_runs:
49
+ if job_run.pid is None:
50
+ pid_sleep_amount = 0.1
51
+ logger.debug(
52
+ f"Job run {job_run.job_run_id} has no PID. Agent has not started."
53
+ )
54
+ logger.debug(
55
+ f"Sleeping {pid_sleep_amount} seconds before checking again..."
56
+ )
57
+ sleep(pid_sleep_amount)
58
+ continue
59
+
60
+ logger.debug(
61
+ f"Checking process PID {job_run.pid} for JobRun {job_run.job_run_id}..."
62
+ )
63
+
64
+ status = self.primitive.jobs.get_job_status(job_run.job_run_id)
65
+ if status is None or status.data is None:
66
+ logger.error(
67
+ f"Error fetching status of <JobRun {job_run.job_run_id}>."
68
+ )
69
+ continue
70
+
71
+ status_value = status.data["jobRun"]["status"]
72
+ conclusion_value = status.data["jobRun"]["conclusion"]
73
+
74
+ logger.debug(f"- Status: {status_value}")
75
+ logger.debug(f"- Conclusion: {conclusion_value}")
76
+
77
+ try:
78
+ parent = psutil.Process(job_run.pid)
79
+ except psutil.NoSuchProcess:
80
+ logger.debug("Process not found")
81
+ continue
82
+
83
+ children = parent.children(recursive=True)
84
+
85
+ if status_value == "completed" and conclusion_value == "cancelled":
86
+ logger.warning("Job cancelled by user")
87
+ for child in children:
88
+ logger.debug(f"Killing child process {child.pid}...")
89
+ child.kill()
90
+
91
+ logger.debug(f"Killing parent process {parent.pid}...")
92
+ parent.kill()
93
+
94
+ if status != "completed":
95
+ sleep(1)
96
+ continue
97
+
98
+ # Second, check for active reservations
99
+ hardware = self.primitive.hardware.get_own_hardware_details()
100
+ if hardware["activeReservation"]:
101
+ if (
102
+ hardware["activeReservation"]["id"] != active_reservation_id
103
+ or hardware["activeReservation"]["pk"] != active_reservation_pk
104
+ ):
105
+ logger.info("New reservation for this hardware.")
106
+ active_reservation_id = hardware["activeReservation"]["id"]
107
+ active_reservation_pk = hardware["activeReservation"]["pk"]
108
+ logger.debug("Active Reservation:")
109
+ logger.debug(f"Node ID: {active_reservation_id}")
110
+ logger.debug(f"PK: {active_reservation_pk}")
111
+
112
+ logger.debug("Running pre provisioning steps for reservation.")
113
+ self.primitive.provisioning.add_reservation_authorized_keys(
114
+ reservation_id=active_reservation_id
115
+ )
116
+
117
+ if not active_reservation_id:
118
+ self.primitive.hardware.check_in_http(
119
+ is_available=True, is_online=True
120
+ )
121
+ logger.debug("Syncing children...")
122
+ self.primitive.hardware._sync_children(hardware=hardware)
123
+
124
+ sleep_amount = 5
125
+ logger.debug(
126
+ f"No active reservation found... [sleeping {sleep_amount} seconds]"
127
+ )
128
+ sleep(sleep_amount)
129
+ continue
130
+ else:
131
+ if (
132
+ hardware["activeReservation"] is None
133
+ and active_reservation_id is not None
134
+ # and hardware["isAvailable"] NOTE: this condition was causing the CLI to get into a loop searching for job runs
135
+ ):
136
+ logger.debug("Previous Reservation is Complete:")
137
+ logger.debug(f"Node ID: {active_reservation_id}")
138
+ logger.debug(f"PK: {active_reservation_pk}")
139
+ logger.debug(
140
+ "Running cleanup provisioning steps for reservation."
141
+ )
142
+ self.primitive.provisioning.remove_reservation_authorized_keys(
143
+ reservation_id=active_reservation_id
144
+ )
145
+ active_reservation_id = None
146
+ active_reservation_pk = None
147
+
148
+ # Third, see if the active reservation has any pending job runs
149
+ job_runs_for_reservation = self.primitive.jobs.get_job_runs(
150
+ status="pending", first=1, reservation_id=active_reservation_id
151
+ )
152
+
153
+ if (
154
+ job_runs_for_reservation is None
155
+ or job_runs_for_reservation.data is None
156
+ ):
157
+ logger.error("Error fetching job runs.")
158
+ sleep_amount = 5
159
+ logger.debug(
160
+ f"Error fetching job runs... [sleeping {sleep_amount} seconds]"
161
+ )
162
+ sleep(sleep_amount)
163
+ continue
164
+
165
+ pending_job_runs = [
166
+ edge["node"]
167
+ for edge in job_runs_for_reservation.data["jobRuns"]["edges"]
168
+ ]
169
+
170
+ if not pending_job_runs:
171
+ self.primitive.hardware.check_in_http(
172
+ is_available=False, is_online=True
173
+ )
174
+ sleep_amount = 5
175
+ logger.debug(
176
+ f"Waiting for Job Runs... [sleeping {sleep_amount} seconds]"
177
+ )
178
+ sleep(sleep_amount)
179
+ continue
180
+
181
+ # If we did find a pending job run, check if it exists in the database
182
+ # and create it if it doesn't.
183
+ # This will trigger the agent to start the job run.
184
+ job_run = pending_job_runs[0]
185
+ if not JobRun.objects.filter_by(job_run_id=job_run["id"]).exists():
186
+ JobRun.objects.create(job_run_id=job_run["id"], pid=None)
187
+ logger.debug(f"Creating job run in database: {job_run['id']}")
188
+
189
+ except KeyboardInterrupt:
190
+ logger.info("[*] Stopping primitive monitor...")
191
+ try:
192
+ self.primitive.hardware.check_in_http(
193
+ is_available=False, is_online=False, stopping_agent=True
194
+ )
195
+
196
+ except P_CLI_100 as exception:
197
+ logger.error("[*] Error stopping primitive monitor.")
198
+ logger.error(str(exception))
199
+ sys.exit()
@@ -0,0 +1,13 @@
1
+ import click
2
+ import typing
3
+
4
+ if typing.TYPE_CHECKING:
5
+ from ..client import Primitive
6
+
7
+
8
+ @click.command("monitor")
9
+ @click.pass_context
10
+ def cli(context):
11
+ """monitor"""
12
+ primitive: Primitive = context.obj.get("PRIMITIVE")
13
+ primitive.monitor.start()
@@ -89,7 +89,6 @@ class Reservations(BaseAction):
89
89
  )
90
90
  if messages := result.data.get("reservationCreate").get("messages"):
91
91
  for message in messages:
92
- logger.enable("primitive")
93
92
  if message.get("kind") == "ERROR":
94
93
  logger.error(message.get("message"))
95
94
  else:
@@ -141,7 +140,6 @@ class Reservations(BaseAction):
141
140
  reservation = reservation_result.data["reservation"]
142
141
  current_status = reservation["status"]
143
142
 
144
- logger.enable("primitive")
145
143
  logger.debug(
146
144
  f"Waiting {total_sleep_time}s for reservation {reservation_id} to be in_progress."
147
145
  )
primitive/utils/auth.py CHANGED
@@ -11,7 +11,6 @@ MAX_TIME_FOR_BACKOFF = 60 * 15 # 15 minutes
11
11
 
12
12
 
13
13
  def connection_backoff_handler(details):
14
- logger.enable("primitive")
15
14
  logger.error(
16
15
  "Cannot connect to API. Waiting {wait:0.1f} seconds after {tries} tries.".format(
17
16
  **details
@@ -25,7 +24,6 @@ def create_new_session(primitive):
25
24
  fingerprint = primitive.host_config.get("fingerprint")
26
25
 
27
26
  if not token or not transport:
28
- logger.enable("primitive")
29
27
  logger.error(
30
28
  "CLI is not configured. Run `primitive config` to add an auth token."
31
29
  )
@@ -0,0 +1,54 @@
1
+ from pathlib import Path
2
+ from abc import ABC, abstractmethod
3
+
4
+
5
+ class Daemon(ABC):
6
+ name: str
7
+ label: str
8
+
9
+ @property
10
+ @abstractmethod
11
+ def logs(self) -> Path:
12
+ """Path to to agent or service logs"""
13
+ pass
14
+
15
+ @property
16
+ @abstractmethod
17
+ def file_path(self) -> Path:
18
+ """Path to agent or service definition file"""
19
+ pass
20
+
21
+ @abstractmethod
22
+ def install(self) -> bool:
23
+ """Install the daemon"""
24
+ pass
25
+
26
+ @abstractmethod
27
+ def uninstall(self) -> bool:
28
+ """Uninstall the daemon"""
29
+ pass
30
+
31
+ @abstractmethod
32
+ def start(self) -> bool:
33
+ """Start the daemon"""
34
+ pass
35
+
36
+ @abstractmethod
37
+ def stop(self) -> bool:
38
+ """Stop the daemon"""
39
+ pass
40
+
41
+ @abstractmethod
42
+ def is_installed(self) -> bool:
43
+ """Check if the daemon is installed"""
44
+ pass
45
+
46
+ @abstractmethod
47
+ def is_active(self) -> bool:
48
+ """Check if the daemon is active"""
49
+ pass
50
+
51
+ @abstractmethod
52
+ def view_logs(self) -> None:
53
+ """View the daemon logs"""
54
+ pass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: primitive
3
- Version: 0.2.10
3
+ Version: 0.2.12
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
@@ -23,9 +23,11 @@ Requires-Dist: gql[all]
23
23
  Requires-Dist: loguru
24
24
  Requires-Dist: paramiko[invoke]
25
25
  Requires-Dist: primitive-pal==0.1.4
26
+ Requires-Dist: psutil>=7.0.0
26
27
  Requires-Dist: pyyaml
27
28
  Requires-Dist: rich>=13.9.4
28
29
  Requires-Dist: speedtest-cli
30
+ Requires-Dist: sqlalchemy>=2.0.40
29
31
  Description-Content-Type: text/markdown
30
32
 
31
33
  # primitive
@@ -1,28 +1,32 @@
1
- primitive/__about__.py,sha256=GhNBI3l8-Fbx4DkjexXgyhbGe2cWuYgQDdzAhhACDvE,130
1
+ primitive/__about__.py,sha256=ZJWGMrU4ZWd5v5sybxqbMIUQbxi44Ln1dT1SjERj8jk,130
2
2
  primitive/__init__.py,sha256=bwKdgggKNVssJFVPfKSxqFMz4IxSr54WWbmiZqTMPNI,106
3
- primitive/cli.py,sha256=58fn6ayVSC1f4hLKx3FUNT9CkuPLva8dFQg0_YUwpio,2410
4
- primitive/client.py,sha256=PPyIQRvKKSqCF9RRF5mJJ4Vqqolpzy1YXqffNLKIvAA,2390
3
+ primitive/cli.py,sha256=g7EtHI9MATAB0qQu5w-WzbXtxz_8zu8z5E7sETmMkKU,2509
4
+ primitive/client.py,sha256=h8WZVnQylVe0vbpuyC8YZHl2JyITSPC-1HbUcmrE5pc,3623
5
5
  primitive/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- primitive/agent/actions.py,sha256=5Lmqxsf24eH6BDEesbJbn1Xj-8ifVlQ_Y3QR8xpkFtM,6869
7
- primitive/agent/commands.py,sha256=-dVDilELfkGfbZB7qfEPs77Dm1oT62qJj4tsIk4KoxI,254
8
- primitive/agent/runner.py,sha256=0p-zObkTuSF0H7HQUBnz0jCtwKCERzABJobQhFJfrY4,13944
9
- primitive/agent/uploader.py,sha256=6pjUyb1LyUCpHBE6p13pRpXxy6iDxu14qJGvE3R6cVo,3155
6
+ primitive/agent/actions.py,sha256=PzFOgxuRrhbUGpUygnTZVru58Fv87GI8tgJYkZB1LjI,3773
7
+ primitive/agent/commands.py,sha256=cK7d3OcN5Z65gQWVZFQ-Y9ddw9Pes4f9OVBpeMsj5sE,255
8
+ primitive/agent/runner.py,sha256=CoRyReO3jPV8B7vILVWdszFD4GVop7HsVEUo1hoRXjo,14556
9
+ primitive/agent/uploader.py,sha256=ZzrzsajNBogwEC7mT6Ejy0h2Jd9axMYGzt9pbCvVMlk,3171
10
10
  primitive/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  primitive/auth/actions.py,sha256=MPsG9LcKcOPwA7gZ9Ewk0PZJhTQvIrGfODdz4GxSzgA,999
12
12
  primitive/auth/commands.py,sha256=2z5u5xX64n0yILucx9emtWh3uQXLvs2QQQQIldZGr94,2341
13
13
  primitive/auth/graphql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
14
  primitive/auth/graphql/queries.py,sha256=jhrr_VFzHIn8vcVprMIzUx7V4kkWYdR6CKMKPoVFv60,180
15
15
  primitive/daemons/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
- primitive/daemons/actions.py,sha256=Nt3yNtbBhen0jK4sRsH_N7AP3UBuyL48VaUhtC7wYq8,2015
17
- primitive/daemons/commands.py,sha256=-Muh-6ib4uAVtPn_67AcMrDwuCwYlCnRQozCi2Xurmk,1726
18
- primitive/daemons/launch_agents.py,sha256=qovt32gwpjGDd82z_SY5EGCUjaUyNA49pZFajZsw3eE,4796
19
- primitive/daemons/launch_service.py,sha256=FPB9qKEjhllRfEpct0ng2L9lpIaGJbQwn1JdFT8uBA8,5600
16
+ primitive/daemons/actions.py,sha256=V4BUCLS8UoQOZoS2vwEkYQpWAUNdZnMPBhQR19RvQXs,2023
17
+ primitive/daemons/commands.py,sha256=Xt4qFymNrDLdHJhRnEH_4Re-2xX6w1OT-chV9k7dFCs,2670
18
+ primitive/daemons/launch_agents.py,sha256=KD7cqQZDtfDmMyNiYrswTRWEktvS9A1QsqQF1jhMDjw,7940
19
+ primitive/daemons/launch_service.py,sha256=IhvKZqU5juA3hKsvUB2275BP8lNBl5XWkVoqVgdwy-o,8013
20
+ primitive/daemons/ui.py,sha256=Af3OJWJ0jdGlb1nfA5yaGYdhBEqqpM8zP2U2vUQdCbw,1236
21
+ primitive/db/base.py,sha256=mH7f2d_jiyxJSSx9Gk53QBXRa3LiKBsBjkFgvmtH1WA,83
22
+ primitive/db/models.py,sha256=GfnJdAq4Tb68CI4BKAuJDZVqioGavveaAHbCPeLNngw,2840
23
+ primitive/db/sqlite.py,sha256=3V9ZxbgME1ThfJp90MPLUxU8b9imgNZM5CHOnA-WkaQ,953
20
24
  primitive/exec/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
- primitive/exec/actions.py,sha256=xw9Qyl3dJsMQQH3P_NUefx77PUFgWQXLDRtJmaSMZAY,4163
25
+ primitive/exec/actions.py,sha256=4d_TCjNDcVFoZ9Zw7ZuBa6hKMv2Xzm7_UX_8wcX1aSk,4124
22
26
  primitive/exec/commands.py,sha256=66LO2kkJC-ynNZQpUCXv4Ol15QoacdSZAHblePDcmLo,510
23
27
  primitive/exec/interactive.py,sha256=TscY6s2ZysijidKPheq6y-fCErUVLS0zcdTW8XyFWGI,2435
24
28
  primitive/files/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
- primitive/files/actions.py,sha256=orcKDrHRCFIi8w0e57CXE7MYX0s4oX-lbTercYBl8Ro,12444
29
+ primitive/files/actions.py,sha256=jvsBivYBmPeqb6Ge7gECm_x20AFUL7UYPGJJFmoCeOM,12409
26
30
  primitive/files/commands.py,sha256=ZNW4y8JZF1li7P5ej1r-Xcqu0iGpRRlMYvthuZOLLbQ,1163
27
31
  primitive/files/graphql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
32
  primitive/files/graphql/fragments.py,sha256=II6WHZjzSqX4IELwdiWokqHTKvDq6mMHF5gp3rLnj3U,231
@@ -38,9 +42,10 @@ primitive/graphql/relay.py,sha256=bmij2AjdpURQ6GGVCxwWhauF-r_SxuAU2oJ4sDbLxpI,72
38
42
  primitive/graphql/sdk.py,sha256=KhVWDZms_eMBgt6ftSJitRALguagy-nmrj4IC2taeXY,1535
39
43
  primitive/graphql/utility_fragments.py,sha256=uIjwILC4QtWNyO5vu77VjQf_p0jvP3A9q_6zRq91zqs,303
40
44
  primitive/hardware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
- primitive/hardware/actions.py,sha256=4AKXGXraGwNzIzSRT5PpStLz3elc-Og2k4lKnR1NR6w,26092
45
+ primitive/hardware/actions.py,sha256=d5KwuSsceOhDH9rgOL7YTCpQPhqT2inRTiZnROtiDic,26076
42
46
  primitive/hardware/android.py,sha256=tu7pBPxWFrIwb_mm5CEdFFf1_veNDOKjOCQg13i_Lh4,2758
43
- primitive/hardware/commands.py,sha256=cWl8j0XtkXQtPHtpO_C6Gh0-dnAEysOFryf3VnDOTOo,5650
47
+ primitive/hardware/commands.py,sha256=ixMPhDOpsU-eONxmimqKVynus-Eaq2XPKEK017WM_rM,3229
48
+ primitive/hardware/ui.py,sha256=12rucuZ2s-w5R4bKyxON5dEbrdDnVf5sbj3K_nbdo44,2473
44
49
  primitive/hardware/graphql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
50
  primitive/hardware/graphql/fragments.py,sha256=kI6qnTNjaEaUr-C6eD55COphtueVYbYOWZwN5EW_3qw,350
46
51
  primitive/hardware/graphql/mutations.py,sha256=_4Hkbfik9Ron4T-meulu6T-9FR_BZjyPNwn745MPksU,1484
@@ -52,6 +57,8 @@ primitive/jobs/graphql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
52
57
  primitive/jobs/graphql/fragments.py,sha256=1_ZttT7dx36KDC3DClJz9M8LMpsPwXySBygHSiUEcGg,619
53
58
  primitive/jobs/graphql/mutations.py,sha256=8ASvCmwQh7cMeeiykOdYaYVryG8FRIuVF6v_J8JJZuw,219
54
59
  primitive/jobs/graphql/queries.py,sha256=BrU_GnLjK0bTAmWsLSmGEUea7EM8MqTKxN1Qp6sSjwc,1597
60
+ primitive/monitor/actions.py,sha256=GUQrwuan82pOJ5gI2FvQYzgDoP4fs28PdcI_fg_aXRs,8692
61
+ primitive/monitor/commands.py,sha256=dZsD8WKGU4OYO_AlKawfeRNVTMN0xJ-DFRkmKTS464s,258
55
62
  primitive/organizations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
56
63
  primitive/organizations/actions.py,sha256=Tgp_rox0jcvfhQ-LmcWc9vkPdeJu5Bk6U1rNuT9oDnw,1088
57
64
  primitive/organizations/commands.py,sha256=_dwgVEJCqMa5VgB_7P1wLPFc0AuT1p9dtyR9JRr4kpw,487
@@ -71,7 +78,7 @@ primitive/provisioning/actions.py,sha256=IYZYAbtomtZtlkqDaBxx4e7PFKGkRNqek_tABH6
71
78
  primitive/provisioning/graphql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
72
79
  primitive/provisioning/graphql/queries.py,sha256=cBtuKa6shoatYZfKSnQoPJP6B8g8y3QhFqJ_pkvMcG0,134
73
80
  primitive/reservations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
74
- primitive/reservations/actions.py,sha256=ELQRl9GEVqE_1c7lBxAFS3C9XJuhUEILgDVQkHv8Evc,6257
81
+ primitive/reservations/actions.py,sha256=FiodRTVUgGgFfoksnN9W0XNdGTd2AxPJTfUrZbmQ0_g,6179
75
82
  primitive/reservations/commands.py,sha256=LFRoV59QGgWIjBdrGjJdffHugg8TLe0Fwlcyu_JaTkk,2369
76
83
  primitive/reservations/graphql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
77
84
  primitive/reservations/graphql/fragments.py,sha256=o5JXkhrFrftYZbsmOQRj105bNw4WwO6U34yN0X-pbCg,411
@@ -79,17 +86,18 @@ primitive/reservations/graphql/mutations.py,sha256=IqzwQL7OclN7RpIcidrTQo9cGYofY
79
86
  primitive/reservations/graphql/queries.py,sha256=x31wTRelskX2fc0fx2qrY7XT1q74nvzLv_Xef3o9weg,746
80
87
  primitive/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
81
88
  primitive/utils/actions.py,sha256=HOFrmM3-0A_A3NS84MqrZ6JmQEiiPSoDqEeuu6b_qfQ,196
82
- primitive/utils/auth.py,sha256=mfO4CwWZtiRZhF498d6v9qeUnLJxi_3jTViuIZI7ijs,1531
89
+ primitive/utils/auth.py,sha256=uBIZNPF2CpbaPV2UMi6eWVUKghV6WIm-pG3-UM29bNs,1465
83
90
  primitive/utils/cache.py,sha256=FHGmVWYLJFQOazpXXcEwI0YJEZbdkgG39nOLdOv6VNk,1575
84
91
  primitive/utils/chunk_size.py,sha256=PAuVuirUTA9oRXyjo1c6MWxo31WVBRkWMuWw-AS58Bw,2914
85
92
  primitive/utils/config.py,sha256=DlFM5Nglo22WPtbpZSVtH7NX-PTMaKYlcrUE7GPRG4c,1058
93
+ primitive/utils/daemons.py,sha256=YkG-OcrTxMhGedbNJMKLq_e7CdTy30Ba2oCVUY-09Co,1086
86
94
  primitive/utils/exceptions.py,sha256=DrYHTcCAJGC7cCUwOx_FmdlVLWRdpzvDvpLb82heppE,311
87
95
  primitive/utils/memory_size.py,sha256=4xfha21kW82nFvOTtDFx9Jk2ZQoEhkfXii-PGNTpIUk,3058
88
96
  primitive/utils/printer.py,sha256=f1XUpqi5dkTL3GWvYRUGlSwtj2IxU1q745T4Fxo7Tn4,370
89
97
  primitive/utils/shell.py,sha256=jWzb7ky7p987dJas6ZvarK3IJNZ5cwBXcryRWb9Uh6U,2072
90
98
  primitive/utils/text.py,sha256=XiESMnlhjQ534xE2hMNf08WehE1SKaYFRNih0MmnK0k,829
91
- primitive-0.2.10.dist-info/METADATA,sha256=LB4jc6D9OV1PUeLtYV6sXwc_XA7Hiu7jZBfzuxaqdN0,3670
92
- primitive-0.2.10.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
93
- primitive-0.2.10.dist-info/entry_points.txt,sha256=p1K8DMCWka5FqLlqP1sPek5Uovy9jq8u51gUsP-z334,48
94
- primitive-0.2.10.dist-info/licenses/LICENSE.txt,sha256=B8kmQMJ2sxYygjCLBk770uacaMci4mPSoJJ8WoDBY_c,1098
95
- primitive-0.2.10.dist-info/RECORD,,
99
+ primitive-0.2.12.dist-info/METADATA,sha256=5GebdNRhv_zCe-W5OP_G7YDzDSzGVC1wA55M0NJdDlQ,3733
100
+ primitive-0.2.12.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
101
+ primitive-0.2.12.dist-info/entry_points.txt,sha256=p1K8DMCWka5FqLlqP1sPek5Uovy9jq8u51gUsP-z334,48
102
+ primitive-0.2.12.dist-info/licenses/LICENSE.txt,sha256=B8kmQMJ2sxYygjCLBk770uacaMci4mPSoJJ8WoDBY_c,1098
103
+ primitive-0.2.12.dist-info/RECORD,,