cyclo-manager 0.2.0.dev9__tar.gz → 0.2.0.dev11__tar.gz

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.
Files changed (20) hide show
  1. {cyclo_manager-0.2.0.dev9 → cyclo_manager-0.2.0.dev11}/PKG-INFO +1 -1
  2. {cyclo_manager-0.2.0.dev9 → cyclo_manager-0.2.0.dev11}/cyclo_host_agent/routers/update.py +0 -21
  3. {cyclo_manager-0.2.0.dev9 → cyclo_manager-0.2.0.dev11}/cyclo_manager.egg-info/PKG-INFO +1 -1
  4. {cyclo_manager-0.2.0.dev9 → cyclo_manager-0.2.0.dev11}/cyclo_manager_cli/cli.py +41 -8
  5. {cyclo_manager-0.2.0.dev9 → cyclo_manager-0.2.0.dev11}/pyproject.toml +1 -1
  6. {cyclo_manager-0.2.0.dev9 → cyclo_manager-0.2.0.dev11}/README.md +0 -0
  7. {cyclo_manager-0.2.0.dev9 → cyclo_manager-0.2.0.dev11}/cyclo_host_agent/__init__.py +0 -0
  8. {cyclo_manager-0.2.0.dev9 → cyclo_manager-0.2.0.dev11}/cyclo_host_agent/main.py +0 -0
  9. {cyclo_manager-0.2.0.dev9 → cyclo_manager-0.2.0.dev11}/cyclo_host_agent/models.py +0 -0
  10. {cyclo_manager-0.2.0.dev9 → cyclo_manager-0.2.0.dev11}/cyclo_host_agent/routers/__init__.py +0 -0
  11. {cyclo_manager-0.2.0.dev9 → cyclo_manager-0.2.0.dev11}/cyclo_host_agent/routers/repos.py +0 -0
  12. {cyclo_manager-0.2.0.dev9 → cyclo_manager-0.2.0.dev11}/cyclo_manager.egg-info/SOURCES.txt +0 -0
  13. {cyclo_manager-0.2.0.dev9 → cyclo_manager-0.2.0.dev11}/cyclo_manager.egg-info/dependency_links.txt +0 -0
  14. {cyclo_manager-0.2.0.dev9 → cyclo_manager-0.2.0.dev11}/cyclo_manager.egg-info/entry_points.txt +0 -0
  15. {cyclo_manager-0.2.0.dev9 → cyclo_manager-0.2.0.dev11}/cyclo_manager.egg-info/requires.txt +0 -0
  16. {cyclo_manager-0.2.0.dev9 → cyclo_manager-0.2.0.dev11}/cyclo_manager.egg-info/top_level.txt +0 -0
  17. {cyclo_manager-0.2.0.dev9 → cyclo_manager-0.2.0.dev11}/cyclo_manager_cli/__init__.py +0 -0
  18. {cyclo_manager-0.2.0.dev9 → cyclo_manager-0.2.0.dev11}/cyclo_manager_cli/config/config.yml +0 -0
  19. {cyclo_manager-0.2.0.dev9 → cyclo_manager-0.2.0.dev11}/cyclo_manager_cli/docker/docker-compose.yml +0 -0
  20. {cyclo_manager-0.2.0.dev9 → cyclo_manager-0.2.0.dev11}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cyclo-manager
3
- Version: 0.2.0.dev9
3
+ Version: 0.2.0.dev11
4
4
  Summary: cyclo_manager CLI: pip-installable launcher for cyclo_manager server and UI containers. Run 'cyclo_manager up' to start Docker stack.
5
5
  License-Expression: Apache-2.0
6
6
  Requires-Python: >=3.10
@@ -21,8 +21,6 @@
21
21
  import asyncio
22
22
  import logging
23
23
  import shutil
24
- import time
25
- import subprocess
26
24
 
27
25
  from fastapi import APIRouter, HTTPException, status
28
26
 
@@ -75,22 +73,3 @@ async def get_update_status() -> dict:
75
73
  return _update_status
76
74
 
77
75
 
78
- def _delayed_exec(cmd: list[str], delay: float = 2.0) -> None:
79
- time.sleep(delay)
80
- subprocess.run(cmd, check=False)
81
-
82
-
83
- @router.post('/reboot')
84
- async def reboot_host() -> dict:
85
- """Reboot the host machine after returning a response."""
86
- loop = asyncio.get_event_loop()
87
- loop.run_in_executor(None, _delayed_exec, ['sudo', '/usr/sbin/reboot'])
88
- return {'status': 'rebooting'}
89
-
90
-
91
- @router.post('/shutdown')
92
- async def shutdown_host() -> dict:
93
- """Shut down the host machine after returning a response."""
94
- loop = asyncio.get_event_loop()
95
- loop.run_in_executor(None, _delayed_exec, ['sudo', '/usr/sbin/poweroff'])
96
- return {'status': 'shutting_down'}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cyclo-manager
3
- Version: 0.2.0.dev9
3
+ Version: 0.2.0.dev11
4
4
  Summary: cyclo_manager CLI: pip-installable launcher for cyclo_manager server and UI containers. Run 'cyclo_manager up' to start Docker stack.
5
5
  License-Expression: Apache-2.0
6
6
  Requires-Python: >=3.10
@@ -27,6 +27,7 @@ import argparse
27
27
  import os
28
28
  from pathlib import Path
29
29
  import shutil
30
+ import socket
30
31
  import subprocess
31
32
  import sys
32
33
 
@@ -81,17 +82,20 @@ def _setup_socket_dir(user: str) -> bool:
81
82
  Only host/ is chowned so other agent sockets under agent_sockets/ are untouched.
82
83
  Removes a stale socket file (e.g. left by a previous root-owned service).
83
84
  """
85
+ print(f' [1/3] Creating socket directory {HOST_AGENT_SOCKET_DIR}...')
84
86
  try:
85
87
  subprocess.run(
86
88
  ['sudo', 'mkdir', '-p', HOST_AGENT_SOCKET_DIR],
87
89
  check=True,
88
90
  capture_output=True,
89
91
  )
92
+ print(f' [2/3] Setting ownership to {user}:{user}...')
90
93
  subprocess.run(
91
94
  ['sudo', 'chown', f'{user}:{user}', HOST_AGENT_SOCKET_DIR],
92
95
  check=True,
93
96
  capture_output=True,
94
97
  )
98
+ print(f' [3/3] Removing stale socket file (if any)...')
95
99
  subprocess.run(
96
100
  ['sudo', 'rm', '-f', HOST_AGENT_SOCKET],
97
101
  check=True,
@@ -108,6 +112,7 @@ def _install_sudoers(user: str) -> bool:
108
112
  Write /etc/sudoers.d/cyclo_manager granting NOPASSWD for udev operations
109
113
  needed by container.sh (cp to /etc/udev/rules.d/, udevadm control/trigger).
110
114
  """
115
+ print(' Installing sudoers rules (cp, udevadm, reboot, poweroff)...')
111
116
  sudoers_content = (
112
117
  f'{user} ALL=(ALL) NOPASSWD: /usr/bin/cp, '
113
118
  f'/usr/bin/udevadm control --reload-rules, '
@@ -170,6 +175,8 @@ After=network.target
170
175
  Type=simple
171
176
  User={user}
172
177
  Group={user}
178
+ RuntimeDirectory=robotis/agent_sockets/host
179
+ RuntimeDirectoryMode=0755
173
180
  ExecStart={agent_exe}
174
181
  Environment=HOME={user_home}
175
182
  Restart=always
@@ -181,18 +188,30 @@ WantedBy=multi-user.target
181
188
 
182
189
  service_file = f'/etc/systemd/system/{HOST_AGENT_SERVICE}.service'
183
190
 
184
- print(f'Installing {HOST_AGENT_SERVICE} systemd service (requires sudo)...')
191
+ print(f'Setting up {HOST_AGENT_SERVICE} systemd service (user={user})...')
185
192
  try:
193
+ print(f' Writing unit file to {service_file}...')
186
194
  subprocess.run(
187
195
  ['sudo', 'tee', service_file],
188
196
  input=service_content.encode(),
189
197
  capture_output=True,
190
198
  check=True,
191
199
  )
192
- subprocess.run(['sudo', 'systemctl', 'daemon-reload'], check=True)
193
- subprocess.run(['sudo', 'systemctl', 'enable', f'{HOST_AGENT_SERVICE}.service'], check=True)
194
- subprocess.run(['sudo', 'systemctl', 'restart', f'{HOST_AGENT_SERVICE}.service'], check=True)
195
- print('Host agent service installed and started.')
200
+ print(' Running systemctl daemon-reload...')
201
+ subprocess.run(['sudo', 'systemctl', 'daemon-reload'], check=True, capture_output=True)
202
+ print(f' Enabling {HOST_AGENT_SERVICE}...')
203
+ subprocess.run(
204
+ ['sudo', 'systemctl', 'enable', f'{HOST_AGENT_SERVICE}.service'],
205
+ check=True,
206
+ capture_output=True,
207
+ )
208
+ print(f' Starting {HOST_AGENT_SERVICE}...')
209
+ subprocess.run(
210
+ ['sudo', 'systemctl', 'restart', f'{HOST_AGENT_SERVICE}.service'],
211
+ check=True,
212
+ capture_output=True,
213
+ )
214
+ print(f' {HOST_AGENT_SERVICE} is running.')
196
215
  return 0
197
216
  except subprocess.CalledProcessError as e:
198
217
  print(f'Failed to install host agent service: {e}', file=sys.stderr)
@@ -204,6 +223,9 @@ WantedBy=multi-user.target
204
223
 
205
224
  def cmd_up(args: argparse.Namespace) -> int:
206
225
  """Start API + UI; create zenoh and noVNC containers without starting them."""
226
+ print('--- cyclo_manager up ---')
227
+
228
+ print('[1/3] Setting up host agent...')
207
229
  if _ensure_host_agent() != 0:
208
230
  print('Warning: Failed to install host agent service.', file=sys.stderr)
209
231
 
@@ -217,11 +239,15 @@ def cmd_up(args: argparse.Namespace) -> int:
217
239
  return 1
218
240
  env = os.environ.copy()
219
241
  env['CYCLO_MANAGER_CONFIG_FILE'] = str(config_path)
242
+ env.setdefault('HOSTNAME', socket.gethostname())
220
243
  base = ['docker', 'compose', '-f', str(compose_path)]
221
244
  try:
222
245
  if args.pull:
246
+ print('[2/3] Pulling latest images...')
223
247
  subprocess.run([*base, 'pull'], env=env, check=True)
248
+ print(f'[2/3] Starting containers: {", ".join(COMPOSE_SERVICES_UP)}...')
224
249
  subprocess.run([*base, 'up', '-d', *COMPOSE_SERVICES_UP], env=env, check=True)
250
+ print(f'[3/3] Creating (not starting): {", ".join(COMPOSE_SERVICES_CREATE_ONLY)}...')
225
251
  subprocess.run(
226
252
  [*base, 'create', '--no-recreate', *COMPOSE_SERVICES_CREATE_ONLY],
227
253
  env=env,
@@ -244,23 +270,27 @@ def cmd_up(args: argparse.Namespace) -> int:
244
270
 
245
271
  def _teardown_host_agent() -> None:
246
272
  """Stop and remove the host agent service, unit file, sudoers, and socket dir."""
273
+ print(f'Tearing down {HOST_AGENT_SERVICE}...')
247
274
  steps = [
248
275
  (['sudo', 'systemctl', 'stop', f'{HOST_AGENT_SERVICE}.service'], 'stop service'),
249
276
  (['sudo', 'systemctl', 'disable', f'{HOST_AGENT_SERVICE}.service'], 'disable service'),
250
277
  (['sudo', 'rm', '-f', f'/etc/systemd/system/{HOST_AGENT_SERVICE}.service'], 'remove unit file'),
251
- (['sudo', 'systemctl', 'daemon-reload'], 'reload systemd'),
278
+ (['sudo', 'systemctl', 'daemon-reload'], 'daemon-reload'),
252
279
  (['sudo', 'rm', '-f', '/etc/sudoers.d/cyclo_manager'], 'remove sudoers'),
253
280
  (['sudo', 'rm', '-rf', HOST_AGENT_SOCKET_DIR], 'remove socket dir'),
254
281
  ]
255
282
  for cmd, desc in steps:
283
+ print(f' {desc}...')
256
284
  try:
257
285
  subprocess.run(cmd, check=True, capture_output=True)
258
286
  except subprocess.CalledProcessError:
259
- print(f'Warning: failed to {desc}.', file=sys.stderr)
287
+ print(f' Warning: failed to {desc}.', file=sys.stderr)
260
288
 
261
289
 
262
290
  def cmd_down(args: argparse.Namespace) -> int:
263
291
  """Stop cyclo_manager server, cyclo_manager_ui, and zenoh daemon (docker compose down)."""
292
+ print('--- cyclo_manager down ---')
293
+
264
294
  _teardown_host_agent()
265
295
 
266
296
  compose_path = _docker_dir() / 'docker-compose.yml'
@@ -269,6 +299,7 @@ def cmd_down(args: argparse.Namespace) -> int:
269
299
  return 1
270
300
  env = os.environ.copy()
271
301
  env['CYCLO_MANAGER_CONFIG_FILE'] = str(_packaged_config_path())
302
+ env.setdefault('HOSTNAME', socket.gethostname())
272
303
  cmd = [
273
304
  'docker',
274
305
  'compose',
@@ -276,6 +307,7 @@ def cmd_down(args: argparse.Namespace) -> int:
276
307
  str(compose_path),
277
308
  'down',
278
309
  ]
310
+ print('Stopping Docker containers (docker compose down)...')
279
311
  try:
280
312
  subprocess.run(cmd, env=env, check=True)
281
313
  except subprocess.CalledProcessError as e:
@@ -287,7 +319,7 @@ def cmd_down(args: argparse.Namespace) -> int:
287
319
  )
288
320
  return 1
289
321
 
290
- print('cyclo_manager server, cyclo_manager_ui, and zenoh daemon are down.')
322
+ print('cyclo_manager is down.')
291
323
  return 0
292
324
 
293
325
 
@@ -304,6 +336,7 @@ def cmd_update(args: argparse.Namespace) -> int:
304
336
  return 1
305
337
  env = os.environ.copy()
306
338
  env['CYCLO_MANAGER_CONFIG_FILE'] = str(_packaged_config_path())
339
+ env.setdefault('HOSTNAME', socket.gethostname())
307
340
  base = ['docker', 'compose', '-f', str(compose_path)]
308
341
 
309
342
  print('Stopping containers...')
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "cyclo-manager"
7
- version = "0.2.0.dev9"
7
+ version = "0.2.0.dev11"
8
8
  description = "cyclo_manager CLI: pip-installable launcher for cyclo_manager server and UI containers. Run 'cyclo_manager up' to start Docker stack."
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"