primitive 0.2.10__py3-none-any.whl → 0.2.11__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.
- primitive/__about__.py +1 -1
- primitive/agent/actions.py +20 -18
- primitive/agent/runner.py +30 -33
- primitive/cli.py +2 -0
- primitive/client.py +41 -16
- primitive/daemons/actions.py +48 -62
- primitive/daemons/commands.py +68 -22
- primitive/daemons/launch_agents.py +205 -122
- primitive/daemons/launch_service.py +224 -164
- primitive/daemons/ui.py +41 -0
- primitive/db/base.py +5 -0
- primitive/db/models.py +78 -0
- primitive/db/sqlite.py +27 -0
- primitive/exec/actions.py +0 -1
- primitive/files/actions.py +0 -1
- primitive/hardware/actions.py +4 -8
- primitive/hardware/commands.py +1 -68
- primitive/hardware/ui.py +67 -0
- primitive/monitor/actions.py +102 -0
- primitive/monitor/commands.py +13 -0
- primitive/reservations/actions.py +0 -2
- primitive/utils/auth.py +0 -2
- primitive/utils/daemons.py +54 -0
- {primitive-0.2.10.dist-info → primitive-0.2.11.dist-info}/METADATA +3 -1
- {primitive-0.2.10.dist-info → primitive-0.2.11.dist-info}/RECORD +28 -20
- {primitive-0.2.10.dist-info → primitive-0.2.11.dist-info}/WHEEL +0 -0
- {primitive-0.2.10.dist-info → primitive-0.2.11.dist-info}/entry_points.txt +0 -0
- {primitive-0.2.10.dist-info → primitive-0.2.11.dist-info}/licenses/LICENSE.txt +0 -0
primitive/__about__.py
CHANGED
primitive/agent/actions.py
CHANGED
@@ -6,25 +6,29 @@ from loguru import logger
|
|
6
6
|
from primitive.__about__ import __version__
|
7
7
|
from primitive.utils.actions import BaseAction
|
8
8
|
|
9
|
-
from ..utils.exceptions import P_CLI_100
|
10
9
|
from .runner import Runner
|
11
10
|
from .uploader import Uploader
|
11
|
+
from ..db import sqlite
|
12
|
+
from ..db.models import JobRun
|
12
13
|
|
13
14
|
|
14
15
|
class Agent(BaseAction):
|
15
16
|
def execute(
|
16
17
|
self,
|
17
18
|
):
|
18
|
-
logger.enable("primitive")
|
19
19
|
logger.remove()
|
20
20
|
logger.add(
|
21
21
|
sink=sys.stderr,
|
22
|
-
|
22
|
+
format="<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | <level>{level: <8}</level> | <level>{message}</level>",
|
23
23
|
backtrace=True,
|
24
24
|
diagnose=True,
|
25
|
+
level="DEBUG" if self.primitive.DEBUG else "INFO",
|
25
26
|
)
|
26
|
-
logger.info("
|
27
|
-
logger.info(f"
|
27
|
+
logger.info("[*] primitive agent")
|
28
|
+
logger.info(f"[*] Version: {__version__}")
|
29
|
+
|
30
|
+
# Initialize the database
|
31
|
+
sqlite.init()
|
28
32
|
|
29
33
|
# Create uploader
|
30
34
|
uploader = Uploader(primitive=self.primitive)
|
@@ -49,9 +53,6 @@ class Agent(BaseAction):
|
|
49
53
|
logger.debug("Scanning for files to upload...")
|
50
54
|
uploader.scan()
|
51
55
|
|
52
|
-
logger.debug("Syncing children...")
|
53
|
-
self.primitive.hardware._sync_children()
|
54
|
-
|
55
56
|
hardware = self.primitive.hardware.get_own_hardware_details()
|
56
57
|
|
57
58
|
if hardware["activeReservation"]:
|
@@ -108,6 +109,7 @@ class Agent(BaseAction):
|
|
108
109
|
]
|
109
110
|
|
110
111
|
if not pending_job_runs:
|
112
|
+
self.primitive.hardware.check_in_http(is_online=True)
|
111
113
|
sleep_amount = 5
|
112
114
|
logger.debug(
|
113
115
|
f"Waiting for Job Runs... [sleeping {sleep_amount} seconds]"
|
@@ -120,6 +122,11 @@ class Agent(BaseAction):
|
|
120
122
|
logger.debug(f"Job Run ID: {job_run['id']}")
|
121
123
|
logger.debug(f"Job Name: {job_run['job']['name']}")
|
122
124
|
|
125
|
+
JobRun.objects.create(
|
126
|
+
job_run_id=job_run["id"],
|
127
|
+
pid=None,
|
128
|
+
)
|
129
|
+
|
123
130
|
runner = Runner(
|
124
131
|
primitive=self.primitive,
|
125
132
|
job_run=job_run,
|
@@ -137,6 +144,7 @@ class Agent(BaseAction):
|
|
137
144
|
status="request_completed",
|
138
145
|
conclusion="failure",
|
139
146
|
)
|
147
|
+
JobRun.objects.filter_by(job_run_id=job_run["id"]).delete()
|
140
148
|
continue
|
141
149
|
|
142
150
|
try:
|
@@ -152,17 +160,11 @@ class Agent(BaseAction):
|
|
152
160
|
runner.cleanup()
|
153
161
|
|
154
162
|
# NOTE: also run scan here to force upload of artifacts
|
155
|
-
# This should probably
|
163
|
+
# This should probably eventually be another daemon?
|
156
164
|
uploader.scan()
|
157
165
|
|
166
|
+
JobRun.objects.filter_by(job_run_id=job_run["id"]).delete()
|
167
|
+
|
158
168
|
sleep(5)
|
159
169
|
except KeyboardInterrupt:
|
160
|
-
logger.info("
|
161
|
-
try:
|
162
|
-
self.primitive.hardware.check_in_http(
|
163
|
-
is_available=False, is_online=False, stopping_agent=True
|
164
|
-
)
|
165
|
-
except P_CLI_100 as exception:
|
166
|
-
logger.error(" [*] Error stopping primitive.")
|
167
|
-
logger.error(str(exception))
|
168
|
-
sys.exit()
|
170
|
+
logger.info("[*] Stopping primitive agent...")
|
primitive/agent/runner.py
CHANGED
@@ -2,12 +2,12 @@ import asyncio
|
|
2
2
|
import os
|
3
3
|
import re
|
4
4
|
import shutil
|
5
|
-
import time
|
6
5
|
import typing
|
7
6
|
from abc import abstractmethod
|
8
7
|
from enum import Enum, IntEnum
|
9
8
|
from pathlib import Path, PurePath
|
10
9
|
from typing import Dict, List, TypedDict
|
10
|
+
from ..db.models import JobRun
|
11
11
|
|
12
12
|
import yaml
|
13
13
|
from loguru import logger
|
@@ -80,8 +80,6 @@ class Runner:
|
|
80
80
|
self.modified_env = {}
|
81
81
|
self.file_logger = None
|
82
82
|
|
83
|
-
logger.enable("primitive")
|
84
|
-
|
85
83
|
# If max_log_size set to <= 0, disable file logging
|
86
84
|
if max_log_size > 0:
|
87
85
|
log_name = f"{self.job['slug']}_{self.job_run['jobRunNumber']}_{{time}}.primitive.log"
|
@@ -158,22 +156,40 @@ class Runner:
|
|
158
156
|
self.modified_env = {**self.initial_env}
|
159
157
|
|
160
158
|
task_failed = False
|
159
|
+
cancelled = False
|
161
160
|
conclusion = "success"
|
162
161
|
for task in self.config["executes"]:
|
162
|
+
status = self.primitive.jobs.get_job_status(self.job_run["id"])
|
163
|
+
status_value = status.data["jobRun"]["status"]
|
164
|
+
conclusion_value = status.data["jobRun"]["conclusion"]
|
165
|
+
|
166
|
+
if status_value == "completed" and conclusion_value == "cancelled":
|
167
|
+
cancelled = True
|
168
|
+
break
|
169
|
+
|
163
170
|
with logger.contextualize(label=task["label"]):
|
164
171
|
with asyncio.Runner() as async_runner:
|
165
172
|
if task_failed := async_runner.run(self.run_task(task)):
|
166
173
|
break
|
167
174
|
|
175
|
+
number_of_files_produced = self.get_number_of_files_produced()
|
176
|
+
logger.info(
|
177
|
+
f"Produced {number_of_files_produced} files for {self.job['slug']} job"
|
178
|
+
)
|
179
|
+
|
180
|
+
if cancelled:
|
181
|
+
logger.warning("Job cancelled by user")
|
182
|
+
self.primitive.jobs.job_run_update(
|
183
|
+
self.job_run["id"],
|
184
|
+
number_of_files_produced=number_of_files_produced,
|
185
|
+
)
|
186
|
+
return
|
187
|
+
|
168
188
|
if task_failed:
|
169
189
|
conclusion = "failure"
|
170
190
|
else:
|
171
191
|
logger.success(f"Completed {self.job['slug']} job")
|
172
192
|
|
173
|
-
number_of_files_produced = self.get_number_of_files_produced()
|
174
|
-
logger.info(
|
175
|
-
f"Produced {number_of_files_produced} files for {self.job['slug']} job"
|
176
|
-
)
|
177
193
|
self.primitive.jobs.job_run_update(
|
178
194
|
self.job_run["id"],
|
179
195
|
status="request_completed",
|
@@ -249,24 +265,24 @@ class Runner:
|
|
249
265
|
stderr=asyncio.subprocess.PIPE,
|
250
266
|
)
|
251
267
|
|
252
|
-
|
253
|
-
|
268
|
+
JobRun.objects.filter_by(job_run_id=self.job_run["id"]).update(
|
269
|
+
{"pid": process.pid}
|
270
|
+
)
|
254
271
|
|
255
|
-
stdout_failed, stderr_failed
|
272
|
+
stdout_failed, stderr_failed = await asyncio.gather(
|
256
273
|
self.log_cmd(
|
257
274
|
process=process, stream=process.stdout, tags=task.get("tags", {})
|
258
275
|
),
|
259
276
|
self.log_cmd(
|
260
277
|
process=process, stream=process.stderr, tags=task.get("tags", {})
|
261
278
|
),
|
262
|
-
monitor_task,
|
263
279
|
)
|
264
280
|
|
265
281
|
returncode = await process.wait()
|
266
282
|
|
267
|
-
|
268
|
-
|
269
|
-
|
283
|
+
JobRun.objects.filter_by(job_run_id=self.job_run["id"]).update(
|
284
|
+
{"pid": None}
|
285
|
+
)
|
270
286
|
|
271
287
|
if returncode > 0:
|
272
288
|
logger.error(
|
@@ -355,25 +371,6 @@ class Runner:
|
|
355
371
|
|
356
372
|
return [line for line in lines if len(line) > 0]
|
357
373
|
|
358
|
-
def monitor_cmd(self, process) -> bool:
|
359
|
-
while process.returncode is None:
|
360
|
-
status = self.primitive.jobs.get_job_status(self.job_run["id"])
|
361
|
-
|
362
|
-
status_value = status.data["jobRun"]["status"]
|
363
|
-
conclusion_value = status.data["jobRun"]["conclusion"]
|
364
|
-
|
365
|
-
if status_value == "completed" and conclusion_value == "cancelled":
|
366
|
-
try:
|
367
|
-
process.terminate()
|
368
|
-
except ProcessLookupError:
|
369
|
-
pass
|
370
|
-
|
371
|
-
return True
|
372
|
-
|
373
|
-
time.sleep(10)
|
374
|
-
|
375
|
-
return False
|
376
|
-
|
377
374
|
def cleanup(self) -> None:
|
378
375
|
logger.remove(self.file_logger)
|
379
376
|
|
primitive/cli.py
CHANGED
@@ -16,6 +16,7 @@ from .jobs.commands import cli as jobs_commands
|
|
16
16
|
from .organizations.commands import cli as organizations_commands
|
17
17
|
from .projects.commands import cli as projects_commands
|
18
18
|
from .reservations.commands import cli as reservations_commands
|
19
|
+
from .monitor.commands import cli as monitor_commands
|
19
20
|
|
20
21
|
|
21
22
|
@click.group()
|
@@ -71,6 +72,7 @@ cli.add_command(organizations_commands, "organizations")
|
|
71
72
|
cli.add_command(projects_commands, "projects")
|
72
73
|
cli.add_command(reservations_commands, "reservations")
|
73
74
|
cli.add_command(exec_commands, "exec")
|
75
|
+
cli.add_command(monitor_commands, "monitor")
|
74
76
|
|
75
77
|
if __name__ == "__main__":
|
76
78
|
cli(obj={})
|
primitive/client.py
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
-
import sys
|
2
|
-
|
3
1
|
from gql import Client
|
4
2
|
from loguru import logger
|
3
|
+
from rich.logging import RichHandler
|
4
|
+
from rich.traceback import install
|
5
|
+
from typing import Optional
|
5
6
|
|
6
7
|
from .agent.actions import Agent
|
7
8
|
from .auth.actions import Auth
|
@@ -15,10 +16,9 @@ from .organizations.actions import Organizations
|
|
15
16
|
from .projects.actions import Projects
|
16
17
|
from .provisioning.actions import Provisioning
|
17
18
|
from .reservations.actions import Reservations
|
19
|
+
from .monitor.actions import Monitor
|
18
20
|
from .utils.config import read_config_file
|
19
21
|
|
20
|
-
logger.disable("primitive")
|
21
|
-
|
22
22
|
|
23
23
|
class Primitive:
|
24
24
|
def __init__(
|
@@ -26,24 +26,48 @@ class Primitive:
|
|
26
26
|
host: str = "api.primitive.tech",
|
27
27
|
DEBUG: bool = False,
|
28
28
|
JSON: bool = False,
|
29
|
-
token: str = None,
|
30
|
-
transport: str = None,
|
29
|
+
token: Optional[str] = None,
|
30
|
+
transport: Optional[str] = None,
|
31
31
|
) -> None:
|
32
32
|
self.host: str = host
|
33
|
-
self.session: Client = None
|
33
|
+
self.session: Optional[Client] = None
|
34
34
|
self.DEBUG: bool = DEBUG
|
35
35
|
self.JSON: bool = JSON
|
36
36
|
|
37
|
+
# Enable tracebacks with local variables
|
37
38
|
if self.DEBUG:
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
)
|
39
|
+
install(show_locals=True)
|
40
|
+
|
41
|
+
# Configure rich logging handler
|
42
|
+
rich_handler = RichHandler(
|
43
|
+
rich_tracebacks=self.DEBUG, # Pretty tracebacks
|
44
|
+
markup=True, # Allow Rich markup tags
|
45
|
+
show_time=self.DEBUG, # Show timestamps
|
46
|
+
show_level=self.DEBUG, # Show log levels
|
47
|
+
show_path=self.DEBUG, # Hide source path (optional)
|
48
|
+
)
|
49
|
+
|
50
|
+
def formatter(record) -> str:
|
51
|
+
match record["level"].name:
|
52
|
+
case "ERROR":
|
53
|
+
return "[bold red]Error>[/bold red] {name}:{function}:{line} - {message}"
|
54
|
+
case "CRITICAL":
|
55
|
+
return "[italic bold red]Critical>[/italic bold red] {name}:{function}:{line} - {message}"
|
56
|
+
case "WARNING":
|
57
|
+
return "[bold yellow]Warning>[/bold yellow] {message}"
|
58
|
+
case _:
|
59
|
+
return "[#666666]>[/#666666] {message}"
|
60
|
+
|
61
|
+
logger.remove()
|
62
|
+
logger.add(
|
63
|
+
sink=rich_handler,
|
64
|
+
format="{message}" if self.DEBUG else formatter,
|
65
|
+
level="DEBUG" if self.DEBUG else "INFO",
|
66
|
+
backtrace=self.DEBUG,
|
67
|
+
)
|
68
|
+
|
69
|
+
# Nothing will print here if DEBUG is false
|
70
|
+
logger.debug("Debug mode enabled")
|
47
71
|
|
48
72
|
# Generate full or partial host config
|
49
73
|
if not token and not transport:
|
@@ -67,6 +91,7 @@ class Primitive:
|
|
67
91
|
self.daemons: Daemons = Daemons(self)
|
68
92
|
self.exec: Exec = Exec(self)
|
69
93
|
self.provisioning: Provisioning = Provisioning(self)
|
94
|
+
self.monitor: Monitor = Monitor(self)
|
70
95
|
|
71
96
|
def get_host_config(self):
|
72
97
|
self.full_config = read_config_file()
|
primitive/daemons/actions.py
CHANGED
@@ -1,24 +1,13 @@
|
|
1
1
|
import platform
|
2
2
|
import typing
|
3
|
+
from typing import Dict, Optional, List
|
3
4
|
|
4
5
|
if typing.TYPE_CHECKING:
|
5
6
|
from ..client import Primitive
|
6
7
|
|
7
|
-
from .launch_agents import
|
8
|
-
|
9
|
-
|
10
|
-
start_launch_agent,
|
11
|
-
stop_launch_agent,
|
12
|
-
view_launch_agent_logs,
|
13
|
-
)
|
14
|
-
|
15
|
-
from .launch_service import (
|
16
|
-
full_service_install,
|
17
|
-
full_service_uninstall,
|
18
|
-
start_service,
|
19
|
-
stop_service,
|
20
|
-
view_service_logs,
|
21
|
-
)
|
8
|
+
from .launch_agents import LaunchAgent
|
9
|
+
from .launch_service import LaunchService
|
10
|
+
from ..utils.daemons import Daemon
|
22
11
|
|
23
12
|
|
24
13
|
class Daemons:
|
@@ -26,50 +15,47 @@ class Daemons:
|
|
26
15
|
self.primitive: Primitive = primitive
|
27
16
|
self.os_family = platform.system()
|
28
17
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
def
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
view_service_logs()
|
74
|
-
elif self.os_family == "Windows":
|
75
|
-
print("Not Implemented")
|
18
|
+
match self.os_family:
|
19
|
+
case "Darwin":
|
20
|
+
self.daemons: Dict[str, Daemon] = {
|
21
|
+
"agent": LaunchAgent("tech.primitive.agent"),
|
22
|
+
"monitor": LaunchAgent("tech.primitive.monitor"),
|
23
|
+
}
|
24
|
+
case "Linux":
|
25
|
+
self.daemons: Dict[str, Daemon] = {
|
26
|
+
"agent": LaunchService("tech.primitive.agent"),
|
27
|
+
"monitor": LaunchService("tech.primitive.monitor"),
|
28
|
+
}
|
29
|
+
case _:
|
30
|
+
raise NotImplementedError(f"{self.os_family} is not supported.")
|
31
|
+
|
32
|
+
def install(self, name: Optional[str]) -> bool:
|
33
|
+
if name:
|
34
|
+
return self.daemons[name].install()
|
35
|
+
else:
|
36
|
+
return all([daemon.install() for daemon in self.daemons.values()])
|
37
|
+
|
38
|
+
def uninstall(self, name: Optional[str]) -> bool:
|
39
|
+
if name:
|
40
|
+
return self.daemons[name].uninstall()
|
41
|
+
else:
|
42
|
+
return all([daemon.uninstall() for daemon in self.daemons.values()])
|
43
|
+
|
44
|
+
def stop(self, name: Optional[str]) -> bool:
|
45
|
+
if name:
|
46
|
+
return self.daemons[name].stop()
|
47
|
+
else:
|
48
|
+
return all([daemon.stop() for daemon in self.daemons.values()])
|
49
|
+
|
50
|
+
def start(self, name: Optional[str]) -> bool:
|
51
|
+
if name:
|
52
|
+
return self.daemons[name].start()
|
53
|
+
else:
|
54
|
+
return all([daemon.start() for daemon in self.daemons.values()])
|
55
|
+
|
56
|
+
def list(self) -> List[Daemon]:
|
57
|
+
"""List all daemons"""
|
58
|
+
return list(self.daemons.values())
|
59
|
+
|
60
|
+
def logs(self, name: str) -> None:
|
61
|
+
self.daemons[name].view_logs()
|
primitive/daemons/commands.py
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
import click
|
2
2
|
|
3
|
-
from ..utils.printer import print_result
|
4
3
|
import typing
|
4
|
+
from typing import Optional
|
5
|
+
from .ui import render_daemon_list
|
6
|
+
|
7
|
+
from loguru import logger
|
5
8
|
|
6
9
|
if typing.TYPE_CHECKING:
|
7
10
|
from ..client import Primitive
|
@@ -16,50 +19,93 @@ def cli(context):
|
|
16
19
|
|
17
20
|
@cli.command("install")
|
18
21
|
@click.pass_context
|
19
|
-
|
22
|
+
@click.argument(
|
23
|
+
"name",
|
24
|
+
type=str,
|
25
|
+
required=False,
|
26
|
+
)
|
27
|
+
def install_daemon_command(context, name: Optional[str]):
|
20
28
|
"""Install the full primitive daemon"""
|
21
29
|
primitive: Primitive = context.obj.get("PRIMITIVE")
|
22
|
-
|
23
|
-
|
30
|
+
installed = primitive.daemons.install(name=name)
|
31
|
+
|
32
|
+
if installed:
|
33
|
+
logger.info(":white_check_mark: daemon(s) installed successfully!")
|
34
|
+
else:
|
35
|
+
logger.error("Unable to install daemon(s).")
|
24
36
|
|
25
37
|
|
26
38
|
@cli.command("uninstall")
|
27
39
|
@click.pass_context
|
28
|
-
|
40
|
+
@click.argument(
|
41
|
+
"name",
|
42
|
+
type=str,
|
43
|
+
required=False,
|
44
|
+
)
|
45
|
+
def uninstall_daemon_command(context, name: Optional[str]):
|
29
46
|
"""Uninstall the full primitive Daemon"""
|
30
47
|
primitive: Primitive = context.obj.get("PRIMITIVE")
|
31
|
-
|
32
|
-
|
48
|
+
uninstalled = primitive.daemons.uninstall(name=name)
|
49
|
+
|
50
|
+
if uninstalled:
|
51
|
+
logger.info(":white_check_mark: daemon(s) uninstalled successfully!")
|
52
|
+
else:
|
53
|
+
logger.error("Unable to uninstall daemon(s).")
|
33
54
|
|
34
55
|
|
35
56
|
@cli.command("stop")
|
36
57
|
@click.pass_context
|
37
|
-
|
58
|
+
@click.argument(
|
59
|
+
"name",
|
60
|
+
type=str,
|
61
|
+
required=False,
|
62
|
+
)
|
63
|
+
def stop_daemon_command(context, name: Optional[str]):
|
38
64
|
"""Stop primitive Daemon"""
|
39
65
|
primitive: Primitive = context.obj.get("PRIMITIVE")
|
40
|
-
|
41
|
-
|
42
|
-
if
|
43
|
-
|
44
|
-
|
66
|
+
stopped = primitive.daemons.stop(name=name)
|
67
|
+
|
68
|
+
if stopped:
|
69
|
+
logger.info(":white_check_mark: daemon(s) stopped successfully!")
|
70
|
+
else:
|
71
|
+
logger.error("Unable to stop daemon(s).")
|
45
72
|
|
46
73
|
|
47
74
|
@cli.command("start")
|
48
75
|
@click.pass_context
|
49
|
-
|
76
|
+
@click.argument(
|
77
|
+
"name",
|
78
|
+
type=str,
|
79
|
+
required=False,
|
80
|
+
)
|
81
|
+
def start_daemon_command(context, name: Optional[str]):
|
50
82
|
"""Start primitive Daemon"""
|
51
83
|
primitive: Primitive = context.obj.get("PRIMITIVE")
|
52
|
-
|
53
|
-
|
54
|
-
if
|
55
|
-
|
56
|
-
|
84
|
+
started = primitive.daemons.start(name=name)
|
85
|
+
|
86
|
+
if started:
|
87
|
+
logger.info(":white_check_mark: daemon(s) started successfully!")
|
88
|
+
else:
|
89
|
+
logger.error("Unable to start daemon(s).")
|
57
90
|
|
58
91
|
|
59
92
|
@cli.command("logs")
|
60
93
|
@click.pass_context
|
61
|
-
|
94
|
+
@click.argument(
|
95
|
+
"name",
|
96
|
+
type=str,
|
97
|
+
required=True,
|
98
|
+
)
|
99
|
+
def log_daemon_command(context, name: str):
|
62
100
|
"""Logs from primitive Daemon"""
|
63
101
|
primitive: Primitive = context.obj.get("PRIMITIVE")
|
64
|
-
|
65
|
-
|
102
|
+
primitive.daemons.logs(name=name)
|
103
|
+
|
104
|
+
|
105
|
+
@cli.command("list")
|
106
|
+
@click.pass_context
|
107
|
+
def list_daemon_command(context):
|
108
|
+
"""List all daemons"""
|
109
|
+
primitive: Primitive = context.obj.get("PRIMITIVE")
|
110
|
+
daemon_list = primitive.daemons.list()
|
111
|
+
render_daemon_list(daemons=daemon_list)
|