cyclo-manager 0.2.0.dev8__tar.gz → 0.2.0.dev10__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.dev8 → cyclo_manager-0.2.0.dev10}/PKG-INFO +1 -1
  2. {cyclo_manager-0.2.0.dev8 → cyclo_manager-0.2.0.dev10}/cyclo_host_agent/routers/update.py +2 -2
  3. {cyclo_manager-0.2.0.dev8 → cyclo_manager-0.2.0.dev10}/cyclo_manager.egg-info/PKG-INFO +1 -1
  4. {cyclo_manager-0.2.0.dev8 → cyclo_manager-0.2.0.dev10}/cyclo_manager_cli/cli.py +42 -9
  5. {cyclo_manager-0.2.0.dev8 → cyclo_manager-0.2.0.dev10}/pyproject.toml +1 -1
  6. {cyclo_manager-0.2.0.dev8 → cyclo_manager-0.2.0.dev10}/README.md +0 -0
  7. {cyclo_manager-0.2.0.dev8 → cyclo_manager-0.2.0.dev10}/cyclo_host_agent/__init__.py +0 -0
  8. {cyclo_manager-0.2.0.dev8 → cyclo_manager-0.2.0.dev10}/cyclo_host_agent/main.py +0 -0
  9. {cyclo_manager-0.2.0.dev8 → cyclo_manager-0.2.0.dev10}/cyclo_host_agent/models.py +0 -0
  10. {cyclo_manager-0.2.0.dev8 → cyclo_manager-0.2.0.dev10}/cyclo_host_agent/routers/__init__.py +0 -0
  11. {cyclo_manager-0.2.0.dev8 → cyclo_manager-0.2.0.dev10}/cyclo_host_agent/routers/repos.py +0 -0
  12. {cyclo_manager-0.2.0.dev8 → cyclo_manager-0.2.0.dev10}/cyclo_manager.egg-info/SOURCES.txt +0 -0
  13. {cyclo_manager-0.2.0.dev8 → cyclo_manager-0.2.0.dev10}/cyclo_manager.egg-info/dependency_links.txt +0 -0
  14. {cyclo_manager-0.2.0.dev8 → cyclo_manager-0.2.0.dev10}/cyclo_manager.egg-info/entry_points.txt +0 -0
  15. {cyclo_manager-0.2.0.dev8 → cyclo_manager-0.2.0.dev10}/cyclo_manager.egg-info/requires.txt +0 -0
  16. {cyclo_manager-0.2.0.dev8 → cyclo_manager-0.2.0.dev10}/cyclo_manager.egg-info/top_level.txt +0 -0
  17. {cyclo_manager-0.2.0.dev8 → cyclo_manager-0.2.0.dev10}/cyclo_manager_cli/__init__.py +0 -0
  18. {cyclo_manager-0.2.0.dev8 → cyclo_manager-0.2.0.dev10}/cyclo_manager_cli/config/config.yml +0 -0
  19. {cyclo_manager-0.2.0.dev8 → cyclo_manager-0.2.0.dev10}/cyclo_manager_cli/docker/docker-compose.yml +0 -0
  20. {cyclo_manager-0.2.0.dev8 → cyclo_manager-0.2.0.dev10}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cyclo-manager
3
- Version: 0.2.0.dev8
3
+ Version: 0.2.0.dev10
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
@@ -84,7 +84,7 @@ def _delayed_exec(cmd: list[str], delay: float = 2.0) -> None:
84
84
  async def reboot_host() -> dict:
85
85
  """Reboot the host machine after returning a response."""
86
86
  loop = asyncio.get_event_loop()
87
- loop.run_in_executor(None, _delayed_exec, ['sudo', 'reboot'])
87
+ loop.run_in_executor(None, _delayed_exec, ['sudo', '/usr/sbin/reboot'])
88
88
  return {'status': 'rebooting'}
89
89
 
90
90
 
@@ -92,5 +92,5 @@ async def reboot_host() -> dict:
92
92
  async def shutdown_host() -> dict:
93
93
  """Shut down the host machine after returning a response."""
94
94
  loop = asyncio.get_event_loop()
95
- loop.run_in_executor(None, _delayed_exec, ['sudo', 'poweroff'])
95
+ loop.run_in_executor(None, _delayed_exec, ['sudo', '/usr/sbin/poweroff'])
96
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.dev8
3
+ Version: 0.2.0.dev10
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,10 +112,13 @@ 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, '
114
- f'/usr/bin/udevadm trigger\n'
119
+ f'/usr/bin/udevadm trigger, '
120
+ f'/usr/sbin/reboot, '
121
+ f'/usr/sbin/poweroff\n'
115
122
  )
116
123
  sudoers_file = '/etc/sudoers.d/cyclo_manager'
117
124
  try:
@@ -179,18 +186,30 @@ WantedBy=multi-user.target
179
186
 
180
187
  service_file = f'/etc/systemd/system/{HOST_AGENT_SERVICE}.service'
181
188
 
182
- print(f'Installing {HOST_AGENT_SERVICE} systemd service (requires sudo)...')
189
+ print(f'Setting up {HOST_AGENT_SERVICE} systemd service (user={user})...')
183
190
  try:
191
+ print(f' Writing unit file to {service_file}...')
184
192
  subprocess.run(
185
193
  ['sudo', 'tee', service_file],
186
194
  input=service_content.encode(),
187
195
  capture_output=True,
188
196
  check=True,
189
197
  )
190
- subprocess.run(['sudo', 'systemctl', 'daemon-reload'], check=True)
191
- subprocess.run(['sudo', 'systemctl', 'enable', f'{HOST_AGENT_SERVICE}.service'], check=True)
192
- subprocess.run(['sudo', 'systemctl', 'restart', f'{HOST_AGENT_SERVICE}.service'], check=True)
193
- print('Host agent service installed and started.')
198
+ print(' Running systemctl daemon-reload...')
199
+ subprocess.run(['sudo', 'systemctl', 'daemon-reload'], check=True, capture_output=True)
200
+ print(f' Enabling {HOST_AGENT_SERVICE}...')
201
+ subprocess.run(
202
+ ['sudo', 'systemctl', 'enable', f'{HOST_AGENT_SERVICE}.service'],
203
+ check=True,
204
+ capture_output=True,
205
+ )
206
+ print(f' Starting {HOST_AGENT_SERVICE}...')
207
+ subprocess.run(
208
+ ['sudo', 'systemctl', 'restart', f'{HOST_AGENT_SERVICE}.service'],
209
+ check=True,
210
+ capture_output=True,
211
+ )
212
+ print(f' {HOST_AGENT_SERVICE} is running.')
194
213
  return 0
195
214
  except subprocess.CalledProcessError as e:
196
215
  print(f'Failed to install host agent service: {e}', file=sys.stderr)
@@ -202,6 +221,9 @@ WantedBy=multi-user.target
202
221
 
203
222
  def cmd_up(args: argparse.Namespace) -> int:
204
223
  """Start API + UI; create zenoh and noVNC containers without starting them."""
224
+ print('--- cyclo_manager up ---')
225
+
226
+ print('[1/3] Setting up host agent...')
205
227
  if _ensure_host_agent() != 0:
206
228
  print('Warning: Failed to install host agent service.', file=sys.stderr)
207
229
 
@@ -215,11 +237,15 @@ def cmd_up(args: argparse.Namespace) -> int:
215
237
  return 1
216
238
  env = os.environ.copy()
217
239
  env['CYCLO_MANAGER_CONFIG_FILE'] = str(config_path)
240
+ env.setdefault('HOSTNAME', socket.gethostname())
218
241
  base = ['docker', 'compose', '-f', str(compose_path)]
219
242
  try:
220
243
  if args.pull:
244
+ print('[2/3] Pulling latest images...')
221
245
  subprocess.run([*base, 'pull'], env=env, check=True)
246
+ print(f'[2/3] Starting containers: {", ".join(COMPOSE_SERVICES_UP)}...')
222
247
  subprocess.run([*base, 'up', '-d', *COMPOSE_SERVICES_UP], env=env, check=True)
248
+ print(f'[3/3] Creating (not starting): {", ".join(COMPOSE_SERVICES_CREATE_ONLY)}...')
223
249
  subprocess.run(
224
250
  [*base, 'create', '--no-recreate', *COMPOSE_SERVICES_CREATE_ONLY],
225
251
  env=env,
@@ -242,23 +268,27 @@ def cmd_up(args: argparse.Namespace) -> int:
242
268
 
243
269
  def _teardown_host_agent() -> None:
244
270
  """Stop and remove the host agent service, unit file, sudoers, and socket dir."""
271
+ print(f'Tearing down {HOST_AGENT_SERVICE}...')
245
272
  steps = [
246
273
  (['sudo', 'systemctl', 'stop', f'{HOST_AGENT_SERVICE}.service'], 'stop service'),
247
274
  (['sudo', 'systemctl', 'disable', f'{HOST_AGENT_SERVICE}.service'], 'disable service'),
248
275
  (['sudo', 'rm', '-f', f'/etc/systemd/system/{HOST_AGENT_SERVICE}.service'], 'remove unit file'),
249
- (['sudo', 'systemctl', 'daemon-reload'], 'reload systemd'),
276
+ (['sudo', 'systemctl', 'daemon-reload'], 'daemon-reload'),
250
277
  (['sudo', 'rm', '-f', '/etc/sudoers.d/cyclo_manager'], 'remove sudoers'),
251
278
  (['sudo', 'rm', '-rf', HOST_AGENT_SOCKET_DIR], 'remove socket dir'),
252
279
  ]
253
280
  for cmd, desc in steps:
281
+ print(f' {desc}...')
254
282
  try:
255
283
  subprocess.run(cmd, check=True, capture_output=True)
256
284
  except subprocess.CalledProcessError:
257
- print(f'Warning: failed to {desc}.', file=sys.stderr)
285
+ print(f' Warning: failed to {desc}.', file=sys.stderr)
258
286
 
259
287
 
260
288
  def cmd_down(args: argparse.Namespace) -> int:
261
289
  """Stop cyclo_manager server, cyclo_manager_ui, and zenoh daemon (docker compose down)."""
290
+ print('--- cyclo_manager down ---')
291
+
262
292
  _teardown_host_agent()
263
293
 
264
294
  compose_path = _docker_dir() / 'docker-compose.yml'
@@ -267,6 +297,7 @@ def cmd_down(args: argparse.Namespace) -> int:
267
297
  return 1
268
298
  env = os.environ.copy()
269
299
  env['CYCLO_MANAGER_CONFIG_FILE'] = str(_packaged_config_path())
300
+ env.setdefault('HOSTNAME', socket.gethostname())
270
301
  cmd = [
271
302
  'docker',
272
303
  'compose',
@@ -274,6 +305,7 @@ def cmd_down(args: argparse.Namespace) -> int:
274
305
  str(compose_path),
275
306
  'down',
276
307
  ]
308
+ print('Stopping Docker containers (docker compose down)...')
277
309
  try:
278
310
  subprocess.run(cmd, env=env, check=True)
279
311
  except subprocess.CalledProcessError as e:
@@ -285,7 +317,7 @@ def cmd_down(args: argparse.Namespace) -> int:
285
317
  )
286
318
  return 1
287
319
 
288
- print('cyclo_manager server, cyclo_manager_ui, and zenoh daemon are down.')
320
+ print('cyclo_manager is down.')
289
321
  return 0
290
322
 
291
323
 
@@ -302,6 +334,7 @@ def cmd_update(args: argparse.Namespace) -> int:
302
334
  return 1
303
335
  env = os.environ.copy()
304
336
  env['CYCLO_MANAGER_CONFIG_FILE'] = str(_packaged_config_path())
337
+ env.setdefault('HOSTNAME', socket.gethostname())
305
338
  base = ['docker', 'compose', '-f', str(compose_path)]
306
339
 
307
340
  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.dev8"
7
+ version = "0.2.0.dev10"
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"