wnm 0.0.8__py3-none-any.whl → 0.0.10__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 wnm might be problematic. Click here for more details.

wnm/config.py CHANGED
@@ -1,3 +1,655 @@
1
- import os, sys
1
+ import json
2
+ import logging
3
+ import os
4
+ import platform
5
+ import re
6
+ import subprocess
7
+ import sys
8
+
9
+ import configargparse
2
10
  from dotenv import load_dotenv
11
+ from sqlalchemy import create_engine, delete, insert, select, text, update
12
+ from sqlalchemy.orm import scoped_session, sessionmaker
13
+
14
+ from wnm.common import (
15
+ DEAD,
16
+ DEFAULT_CRISIS_BYTES,
17
+ DISABLED,
18
+ DONATE,
19
+ MIGRATING,
20
+ QUEEN,
21
+ REMOVING,
22
+ RESTARTING,
23
+ RUNNING,
24
+ STOPPED,
25
+ UPGRADING,
26
+ )
27
+ from wnm.models import Base, Machine, Node
28
+
29
+ logging.getLogger("sqlalchemy.engine.Engine").disabled = True
30
+
31
+
32
+ # ============================================================================
33
+ # Platform Detection and Path Constants
34
+ # ============================================================================
35
+
36
+ PLATFORM = platform.system() # 'Linux', 'Darwin', 'Windows'
37
+
38
+ # Determine if running as root on Linux
39
+ IS_ROOT = PLATFORM == "Linux" and os.geteuid() == 0
40
+
41
+ # Platform-specific base directories
42
+ if PLATFORM == "Darwin":
43
+ # macOS: Use standard macOS application directories
44
+ BASE_DIR = os.path.expanduser("~/Library/Application Support/autonomi")
45
+ NODE_STORAGE = os.path.expanduser("~/Library/Application Support/autonomi/node")
46
+ LOG_DIR = os.path.expanduser("~/Library/Logs/autonomi")
47
+ BOOTSTRAP_CACHE_DIR = os.path.expanduser("~/Library/Caches/autonomi/bootstrap-cache")
48
+ elif PLATFORM == "Linux":
49
+ if IS_ROOT:
50
+ # Linux root: Use legacy /var/antctl paths for backwards compatibility
51
+ BASE_DIR = "/var/antctl"
52
+ NODE_STORAGE = "/var/antctl/services"
53
+ LOG_DIR = "/var/log/antnode"
54
+ BOOTSTRAP_CACHE_DIR = "/var/antctl/bootstrap-cache"
55
+ else:
56
+ # Linux user: Use XDG Base Directory specification
57
+ BASE_DIR = os.path.expanduser("~/.local/share/autonomi")
58
+ NODE_STORAGE = os.path.expanduser("~/.local/share/autonomi/node")
59
+ LOG_DIR = os.path.expanduser("~/.local/share/autonomi/logs")
60
+ BOOTSTRAP_CACHE_DIR = os.path.expanduser("~/.local/share/autonomi/bootstrap-cache")
61
+ else:
62
+ # Windows or other platforms
63
+ BASE_DIR = os.path.expanduser("~/autonomi")
64
+ NODE_STORAGE = os.path.expanduser("~/autonomi/node")
65
+ LOG_DIR = os.path.expanduser("~/autonomi/logs")
66
+ BOOTSTRAP_CACHE_DIR = os.path.expanduser("~/autonomi/bootstrap-cache")
67
+
68
+ # Derived paths
69
+ LOCK_FILE = os.path.join(BASE_DIR, "wnm_active")
70
+ DEFAULT_DB_PATH = f"sqlite:///{os.path.join(BASE_DIR, 'colony.db')}"
71
+
72
+ # Create directories if they don't exist (except in test mode)
73
+ if not os.getenv("WNM_TEST_MODE"):
74
+ os.makedirs(BASE_DIR, exist_ok=True)
75
+ os.makedirs(NODE_STORAGE, exist_ok=True)
76
+ os.makedirs(LOG_DIR, exist_ok=True)
77
+ os.makedirs(BOOTSTRAP_CACHE_DIR, exist_ok=True)
78
+
79
+
80
+ # Config file parser
81
+ # This is a simple wrapper around configargparse that reads the config file from the default locations
82
+ # and allows for command line overrides. It also sets up the logging level and database path
83
+ def load_config():
84
+ c = configargparse.ArgParser(
85
+ default_config_files=["~/.local/share/wnm/config", "~/wnm/config"],
86
+ description="wnm - Weave Node Manager",
87
+ )
88
+
89
+ c.add("-c", "--config", is_config_file=True, help="config file path")
90
+ c.add("-v", help="verbose", action="store_true")
91
+ c.add(
92
+ "--dbpath",
93
+ env_var="DBPATH",
94
+ help="Path to the database",
95
+ default="sqlite:///colony.db",
96
+ )
97
+ c.add("--loglevel", env_var="LOGLEVEL", help="Log level")
98
+ c.add(
99
+ "--dry_run", env_var="DRY_RUN", help="Do not save changes", action="store_true"
100
+ )
101
+ c.add("--init", help="Initialize a cluster", action="store_true")
102
+ c.add("--migrate_anm", help="Migrate a cluster from anm", action="store_true")
103
+ c.add("--teardown", help="Remove a cluster", action="store_true")
104
+ c.add("--confirm", help="Confirm teardown without ui", action="store_true")
105
+ c.add("--node_cap", env_var="NODE_CAP", help="Node Capacity")
106
+ c.add("--cpu_less_than", env_var="CPU_LESS_THAN", help="CPU Add Threshold")
107
+ c.add("--cpu_remove", env_var="CPU_REMOVE", help="CPU Remove Threshold")
108
+ c.add("--mem_less_than", env_var="MEM_LESS_THAN", help="Memory Add Threshold")
109
+ c.add("--mem_remove", env_var="MEM_REMOVE", help="Memory Remove Threshold")
110
+ c.add("--hd_less_than", env_var="HD_LESS_THAN", help="Hard Drive Add Threshold")
111
+ c.add("--hd_remove", env_var="HD_REMOVE", help="Hard Drive Remove Threshold")
112
+ c.add("--delay_start", env_var="DELAY_START", help="Delay Start Timer")
113
+ c.add("--delay_restart", env_var="DELAY_RESTART", help="Delay Restart Timer")
114
+ c.add("--delay_upgrade", env_var="DELAY_UPGRADE", help="Delay Upgrade Timer")
115
+ c.add("--delay_remove", env_var="DELAY_REMOVE", help="Delay Remove Timer")
116
+ c.add("--node_storage", env_var="NODE_STORAGE", help="Node Storage Path")
117
+ c.add("--rewards_address", env_var="REWARDS_ADDRESS", help="Rewards Address")
118
+ c.add("--donate_address", env_var="DONATE_ADDRESS", help="Donate Address")
119
+ c.add(
120
+ "--max_load_average_allowed",
121
+ env_var="MAX_LOAD_AVERAGE_ALLOWED",
122
+ help="Max Load Average Allowed Remove Threshold",
123
+ )
124
+ c.add(
125
+ "--desired_load_average",
126
+ env_var="DESIRED_LOAD_AVERAGE",
127
+ help="Desired Load Average Add Threshold",
128
+ )
129
+ c.add(
130
+ "--port_start", env_var="PORT_START", help="Range to begin Node port assignment"
131
+ ) # Only allowed during init
132
+ c.add(
133
+ "--metrics_port_start",
134
+ env_var="METRICS_PORT_START",
135
+ help="Range to begin Metrics port assignment",
136
+ ) # Only allowed during init
137
+ c.add(
138
+ "--hdio_read_less_than",
139
+ env_var="HDIO_READ_LESS_THAN",
140
+ help="Hard Drive IO Read Add Threshold",
141
+ )
142
+ c.add(
143
+ "--hdio_read_remove",
144
+ env_var="HDIO_READ_REMOVE",
145
+ help="Hard Drive IO Read Remove Threshold",
146
+ )
147
+ c.add(
148
+ "--hdio_write_less_than",
149
+ env_var="HDIO_WRITE_LESS_THAN",
150
+ help="Hard Drive IO Write Add Threshold",
151
+ )
152
+ c.add(
153
+ "--hdio_write_remove",
154
+ env_var="HDIO_WRITE_REMOVE",
155
+ help="Hard Drive IO Write Remove Threshold",
156
+ )
157
+ c.add(
158
+ "--netio_read_less_than",
159
+ env_var="NETIO_READ_LESS_THAN",
160
+ help="Network IO Read Add Threshold",
161
+ )
162
+ c.add(
163
+ "--netio_read_remove",
164
+ env_var="NETIO_READ_REMOVE",
165
+ help="Network IO Read Remove Threshold",
166
+ )
167
+ c.add(
168
+ "--netio_write_less_than",
169
+ env_var="NETIO_WRITE_LESS_THAN",
170
+ help="Network IO Write Add Threshold",
171
+ )
172
+ c.add(
173
+ "--netio_write_remove",
174
+ env_var="NETIO_WRITE_REMOVE",
175
+ help="Network IO Write Remove Threshold",
176
+ )
177
+ c.add("--crisis_bytes", env_var="CRISIS_BYTES", help="Crisis Bytes Threshold")
178
+ c.add("--last_stopped_at", env_var="LAST_STOPPED_AT", help="Last Stopped Timestamp")
179
+ c.add("--host", env_var="HOST", help="Hostname")
180
+ c.add(
181
+ "--environment", env_var="ENVIRONMENT", help="Environment variables for antnode"
182
+ )
183
+ c.add(
184
+ "--start_args",
185
+ env_var="START_ARGS",
186
+ help="Arguments to pass to antnode",
187
+ )
188
+ c.add(
189
+ "--force_action",
190
+ env_var="FORCE_ACTION",
191
+ help="Force an action: add, remove, upgrade, start, stop, disable, teardown, survey",
192
+ choices=["add", "remove", "upgrade", "start", "stop", "disable", "teardown", "survey"],
193
+ )
194
+ c.add(
195
+ "--service_name",
196
+ env_var="SERVICE_NAME",
197
+ help="Node name for targeted operations or comma-separated list for reports and survey (e.g., antnode0001,antnode0003)",
198
+ )
199
+ c.add(
200
+ "--report",
201
+ env_var="REPORT",
202
+ help="Generate a report: node-status, node-status-details",
203
+ choices=["node-status", "node-status-details"],
204
+ )
205
+ c.add(
206
+ "--report_format",
207
+ env_var="REPORT_FORMAT",
208
+ help="Report output format: text or json (default: text)",
209
+ choices=["text", "json"],
210
+ default="text",
211
+ )
212
+ c.add(
213
+ "--count",
214
+ env_var="COUNT",
215
+ help="Number of nodes to affect when using --force_action (default: 1). Works with add, remove, start, stop, upgrade actions.",
216
+ type=int,
217
+ default=1,
218
+ )
219
+
220
+ options = c.parse_known_args()[0] or []
221
+ # Return the first result from parse_known_args, ignore unknown options
222
+ return options
223
+
224
+
225
+ # Merge the changes from the config file with the database
226
+ def merge_config_changes(options, machine_config):
227
+ # Collect updates
228
+ cfg = {}
229
+ if options.node_cap and int(options.node_cap) != machine_config.node_cap:
230
+ cfg["node_cap"] = int(options.node_cap)
231
+ if options.cpu_less_than and int(options.cpu_less_than) != machine_config.cpu_less_than:
232
+ cfg["cpu_less_than"] = int(options.cpu_less_than)
233
+ if options.cpu_remove and int(options.cpu_remove) != machine_config.cpu_remove:
234
+ cfg["cpu_remove"] = int(options.cpu_remove)
235
+ if options.mem_less_than and int(options.mem_less_than) != machine_config.mem_less_than:
236
+ cfg["mem_less_than"] = int(options.mem_less_than)
237
+ if options.mem_remove and int(options.mem_remove) != machine_config.mem_remove:
238
+ cfg["mem_remove"] = int(options.mem_remove)
239
+ if options.hd_less_than and int(options.hd_less_than) != machine_config.hd_less_than:
240
+ cfg["hd_less_than"] = int(options.hd_less_than)
241
+ if options.hd_remove and int(options.hd_remove) != machine_config.hd_remove:
242
+ cfg["hd_remove"] = int(options.hd_remove)
243
+ if options.delay_start and int(options.delay_start) != machine_config.delay_start:
244
+ cfg["delay_start"] = int(options.delay_start)
245
+ if (
246
+ options.delay_restart
247
+ and int(options.delay_restart) != machine_config.delay_restart
248
+ ):
249
+ cfg["delay_restart"] = int(options.delay_restart)
250
+ if (
251
+ options.delay_upgrade
252
+ and int(options.delay_upgrade) != machine_config.delay_upgrade
253
+ ):
254
+ cfg["delay_upgrade"] = int(options.delay_upgrade)
255
+ if options.delay_remove and int(options.delay_remove) != machine_config.delay_remove:
256
+ cfg["delay_remove"] = int(options.delay_remove)
257
+ if options.node_storage and options.node_storage != machine_config.node_storage:
258
+ cfg["node_storage"] = options.node_storage
259
+ if (
260
+ options.rewards_address
261
+ and options.rewards_address != machine_config.rewards_address
262
+ ):
263
+ cfg["rewards_address"] = options.rewards_address
264
+ if options.donate_address and options.donate_address != machine_config.donate_address:
265
+ cfg["donate_address"] = options.donate_address
266
+ if (
267
+ options.max_load_average_allowed
268
+ and float(options.max_load_average_allowed) != machine_config.max_load_average_allowed
269
+ ):
270
+ cfg["max_load_average_allowed"] = float(options.max_load_average_allowed)
271
+ if (
272
+ options.desired_load_average
273
+ and float(options.desired_load_average) != machine_config.desired_load_average
274
+ ):
275
+ cfg["desired_load_average"] = float(options.desired_load_average)
276
+ if options.port_start and int(options.port_start) != machine_config.port_start:
277
+ cfg["port_start"] = int(options.port_start)
278
+ if (
279
+ options.hdio_read_less_than
280
+ and int(options.hdio_read_less_than) != machine_config.hdio_read_less_than
281
+ ):
282
+ cfg["hdio_read_less_than"] = int(options.hdio_read_less_than)
283
+ if (
284
+ options.hdio_read_remove
285
+ and int(options.hdio_read_remove) != machine_config.hdio_read_remove
286
+ ):
287
+ cfg["hdio_read_remove"] = int(options.hdio_read_remove)
288
+ if (
289
+ options.hdio_write_less_than
290
+ and int(options.hdio_write_less_than) != machine_config.hdio_write_less_than
291
+ ):
292
+ cfg["hdio_write_less_than"] = int(options.hdio_write_less_than)
293
+ if (
294
+ options.hdio_write_remove
295
+ and int(options.hdio_write_remove) != machine_config.hdio_write_remove
296
+ ):
297
+ cfg["hdio_write_remove"] = int(options.hdio_write_remove)
298
+ if (
299
+ options.netio_read_less_than
300
+ and int(options.netio_read_less_than) != machine_config.netio_read_less_than
301
+ ):
302
+ cfg["netio_read_less_than"] = int(options.netio_read_less_than)
303
+ if (
304
+ options.netio_read_remove
305
+ and int(options.netio_read_remove) != machine_config.netio_read_remove
306
+ ):
307
+ cfg["netio_read_remove"] = int(options.netio_read_remove)
308
+ if (
309
+ options.netio_write_less_than
310
+ and int(options.netio_write_less_than) != machine_config.netio_write_less_than
311
+ ):
312
+ cfg["netio_write_less_than"] = int(options.netio_write_less_than)
313
+ if (
314
+ options.netio_write_remove
315
+ and int(options.netio_write_remove) != machine_config.netio_write_remove
316
+ ):
317
+ cfg["netio_write_remove"] = int(options.netio_write_remove)
318
+ if options.crisis_bytes and int(options.crisis_bytes) != machine_config.crisis_bytes:
319
+ cfg["crisis_bytes"] = int(options.crisis_bytes)
320
+ if (
321
+ options.metrics_port_start
322
+ and int(options.metrics_port_start) != machine_config.metrics_port_start
323
+ ):
324
+ cfg["metrics_port_start"] = int(options.metrics_port_start)
325
+ if options.environment and options.environment != machine_config.environment:
326
+ cfg["environment"] = options.environment
327
+ if options.start_args and options.start_args != machine_config.start_args:
328
+ cfg["start_args"] = options.start_args
329
+
330
+ return cfg
331
+
332
+
333
+ # Get anm configuration
334
+ def load_anm_config(options):
335
+ anm_config = {}
336
+
337
+ # Let's get the real count of CPU's available to this process
338
+ if PLATFORM == "Linux":
339
+ # Linux: use sched_getaffinity for accurate count (respects cgroups/taskset)
340
+ anm_config["cpu_count"] = len(os.sched_getaffinity(0))
341
+ else:
342
+ # macOS/other: use os.cpu_count()
343
+ anm_config["cpu_count"] = os.cpu_count() or 1
344
+
345
+ # What can we save from /var/antctl/config
346
+ if os.path.exists("/var/antctl/config"):
347
+ load_dotenv("/var/antctl/config")
348
+ anm_config["node_cap"] = int(os.getenv("NodeCap") or options.node_cap or 20)
349
+ anm_config["cpu_less_than"] = int(
350
+ os.getenv("CpuLessThan") or options.cpu_less_than or 50
351
+ )
352
+ anm_config["cpu_remove"] = int(os.getenv("CpuRemove") or options.cpu_remove or 70)
353
+ anm_config["mem_less_than"] = int(
354
+ os.getenv("MemLessThan") or options.mem_less_than or 70
355
+ )
356
+ anm_config["mem_remove"] = int(os.getenv("MemRemove") or options.mem_remove or 90)
357
+ anm_config["hd_less_than"] = int(os.getenv("HDLessThan") or options.hd_less_than or 70)
358
+ anm_config["hd_remove"] = int(os.getenv("HDRemove") or options.hd_remove or 90)
359
+ anm_config["delay_start"] = int(os.getenv("DelayStart") or options.delay_start or 300)
360
+ anm_config["delay_upgrade"] = int(
361
+ os.getenv("DelayUpgrade") or options.delay_upgrade or 300
362
+ )
363
+ anm_config["delay_restart"] = int(
364
+ os.getenv("DelayRestart") or options.delay_restart or 600
365
+ )
366
+ anm_config["delay_remove"] = int(
367
+ os.getenv("DelayRemove") or options.delay_remove or 300
368
+ )
369
+ anm_config["node_storage"] = (
370
+ os.getenv("NodeStorage") or options.node_storage or NODE_STORAGE
371
+ )
372
+ # Default to the faucet donation address
373
+ try:
374
+ anm_config["rewards_address"] = re.findall(
375
+ r"--rewards-address ([\dA-Fa-fXx]+)", os.getenv("RewardsAddress")
376
+ )[0]
377
+ except (IndexError, TypeError) as e:
378
+ try:
379
+ anm_config["rewards_address"] = re.findall(
380
+ r"([\dA-Fa-fXx]+)", os.getenv("RewardsAddress")
381
+ )[0]
382
+ except (IndexError, TypeError) as e:
383
+ logging.debug(f"Unable to parse RewardsAddress from env: {e}")
384
+ anm_config["rewards_address"] = options.rewards_address
385
+ if not anm_config["rewards_address"]:
386
+ logging.warning("Unable to detect RewardsAddress")
387
+ sys.exit(1)
388
+ anm_config["donate_address"] = (
389
+ os.getenv("DonateAddress") or options.donate_address or DONATE
390
+ )
391
+ anm_config["max_load_average_allowed"] = float(
392
+ os.getenv("MaxLoadAverageAllowed") or anm_config["cpu_count"]
393
+ )
394
+ anm_config["desired_load_average"] = float(
395
+ os.getenv("DesiredLoadAverage") or (anm_config["cpu_count"] * 0.6)
396
+ )
397
+
398
+ try:
399
+ with open("/usr/bin/anms.sh", "r") as file:
400
+ data = file.read()
401
+ anm_config["port_start"] = int(re.findall(r"ntpr\=(\d+)", data)[0])
402
+ except (FileNotFoundError, IndexError, ValueError) as e:
403
+ logging.debug(f"Unable to read PortStart from anms.sh: {e}")
404
+ anm_config["port_start"] = options.port_start or 55
405
+
406
+ anm_config["metrics_port_start"] = (
407
+ options.metrics_port_start or 13
408
+ ) # This is hardcoded in the anm.sh script
409
+
410
+ anm_config["hdio_read_less_than"] = int(os.getenv("HDIOReadLessThan") or 0)
411
+ anm_config["hdio_read_remove"] = int(os.getenv("HDIOReadRemove") or 0)
412
+ anm_config["hdio_write_less_than"] = int(os.getenv("HDIOWriteLessThan") or 0)
413
+ anm_config["hdio_write_remove"] = int(os.getenv("HDIOWriteRemove") or 0)
414
+ anm_config["netio_read_less_than"] = int(os.getenv("NetIOReadLessThan") or 0)
415
+ anm_config["netio_read_remove"] = int(os.getenv("NetIOReadRemove") or 0)
416
+ anm_config["netio_write_less_than"] = int(os.getenv("NetIOWriteLessThan") or 0)
417
+ anm_config["netio_write_remove"] = int(os.getenv("NetIOWriteRemove") or 0)
418
+ # Timer for last stopped nodes
419
+ anm_config["last_stopped_at"] = 0
420
+ anm_config["host"] = os.getenv("Host") or options.host or "127.0.0.1"
421
+ anm_config["crisis_bytes"] = options.host or DEFAULT_CRISIS_BYTES
422
+ anm_config["environment"] = options.environment or ""
423
+ anm_config["start_args"] = options.start_args or ""
424
+
425
+ return anm_config
426
+
427
+
428
+ # This belongs someplace else
429
+ def migrate_anm(options):
430
+ if os.path.exists("/var/antctl/system"):
431
+ # Is anm scheduled to run
432
+ if os.path.exists("/etc/cron.d/anm"):
433
+ # remove cron to disable old anm
434
+ try:
435
+ subprocess.run(["sudo", "rm", "/etc/cron.d/anm"])
436
+ except Exception as error:
437
+ template = (
438
+ "In GAV - An exception of type {0} occurred. Arguments:\n{1!r}"
439
+ )
440
+ message = template.format(type(error).__name__, error.args)
441
+ logging.info(message)
442
+ sys.exit(1)
443
+ # Is anm sitll running? We'll wait
444
+ if os.path.exists("/var/antctl/block"):
445
+ logging.info("anm still running, waiting...")
446
+ sys.exit(1)
447
+ # Ok, load anm config
448
+ return load_anm_config(options)
449
+ else:
450
+ return False
451
+
452
+
453
+ # Teardown the machine
454
+ def teardown_machine(machine_config):
455
+ logging.info("Teardown machine")
456
+ pass
457
+ # disable cron
458
+ # with S() as session:
459
+ # select Nodes
460
+ # for node in nodes:
461
+ # delete node
462
+
463
+
464
+ def define_machine(options):
465
+ if not options.rewards_address:
466
+ logging.warning("Rewards Address is required")
467
+ return False
468
+ if PLATFORM == "Linux":
469
+ # Linux: use sched_getaffinity for accurate count (respects cgroups/taskset)
470
+ cpucount = len(os.sched_getaffinity(0))
471
+ else:
472
+ # macOS/other: use os.cpu_count()
473
+ cpucount = os.cpu_count() or 1
474
+ machine = {
475
+ "id": 1,
476
+ "cpu_count": cpucount,
477
+ "node_cap": int(options.node_cap) if options.node_cap else 20,
478
+ "cpu_less_than": int(options.cpu_less_than) if options.cpu_less_than else 50,
479
+ "cpu_remove": int(options.cpu_remove) if options.cpu_remove else 70,
480
+ "mem_less_than": int(options.mem_less_than) if options.mem_less_than else 70,
481
+ "mem_remove": int(options.mem_remove) if options.mem_remove else 90,
482
+ "hd_less_than": int(options.hd_less_than) if options.hd_less_than else 70,
483
+ "hd_remove": int(options.hd_remove) if options.hd_remove else 90,
484
+ "delay_start": int(options.delay_start) if options.delay_start else 300,
485
+ "delay_upgrade": int(options.delay_upgrade) if options.delay_upgrade else 300,
486
+ "delay_remove": int(options.delay_remove) if options.delay_remove else 300,
487
+ "node_storage": options.node_storage or NODE_STORAGE,
488
+ "rewards_address": options.rewards_address,
489
+ "donate_address": options.donate_address
490
+ or "0x00455d78f850b0358E8cea5be24d415E01E107CF",
491
+ "max_load_average_allowed": (
492
+ float(options.max_load_average_allowed)
493
+ if options.max_load_average_allowed
494
+ else cpucount
495
+ ),
496
+ "desired_load_average": (
497
+ float(options.desired_load_average)
498
+ if options.desired_load_average
499
+ else cpucount * 0.6
500
+ ),
501
+ "port_start": int(options.port_start) if options.port_start else 55,
502
+ "hdio_read_less_than": (
503
+ int(options.hdio_read_less_than) if options.hdio_read_less_than else 0
504
+ ),
505
+ "hdio_read_remove": int(options.hdio_read_remove) if options.hdio_read_remove else 0,
506
+ "hdio_write_less_than": (
507
+ int(options.hdio_write_less_than) if options.hdio_write_less_than else 0
508
+ ),
509
+ "hdio_write_remove": (
510
+ int(options.hdio_write_remove) if options.hdio_write_remove else 0
511
+ ),
512
+ "netio_read_less_than": (
513
+ int(options.netio_read_less_than) if options.netio_read_less_than else 0
514
+ ),
515
+ "netio_read_remove": (
516
+ int(options.netio_read_remove) if options.netio_read_remove else 0
517
+ ),
518
+ "netio_write_less_than": (
519
+ int(options.netio_write_less_than) if options.netio_write_less_than else 0
520
+ ),
521
+ "netio_write_remove": (
522
+ int(options.netio_write_remove) if options.netio_write_remove else 0
523
+ ),
524
+ "last_stopped_at": 0,
525
+ "host": options.host or "127.0.0.1",
526
+ "crisis_bytes": (
527
+ int(options.crisis_bytes) if options.crisis_bytes else DEFAULT_CRISIS_BYTES
528
+ ),
529
+ "metrics_port_start": (
530
+ int(options.metrics_port_start) if options.metrics_port_start else 13
531
+ ),
532
+ "environment": options.environment if options.environment else "",
533
+ "start_args": options.start_args if options.start_args else "",
534
+ }
535
+ with S() as session:
536
+ session.execute(insert(Machine), [machine])
537
+ session.commit()
538
+ return True
539
+
540
+
541
+ # Apply changes to system
542
+ def apply_config_updates(config_updates):
543
+ global machine_config
544
+ if config_updates:
545
+ with S() as session:
546
+ session.query(Machine).filter(Machine.id == 1).update(config_updates)
547
+ session.commit()
548
+ # Reload the machine config
549
+ machine_config = session.execute(select(Machine)).first()
550
+ # Get Machine from Row
551
+ machine_config = machine_config[0]
552
+
553
+
554
+ # Load options now so we know what database to load
555
+ options = load_config()
556
+
557
+ # Setup Database engine
558
+ engine = create_engine(options.dbpath, echo=True)
559
+
560
+ # Generate ORM
561
+ Base.metadata.create_all(engine)
562
+
563
+ # Create a connection to the ORM
564
+ session_factory = sessionmaker(bind=engine)
565
+ S = scoped_session(session_factory)
566
+
567
+ # Remember if we init a new machine
568
+ did_we_init = False
569
+
570
+ # Skip machine configuration check in test mode
571
+ if os.getenv("WNM_TEST_MODE"):
572
+ # In test mode, use a minimal machine config or None
573
+ machine_config = None
574
+ else:
575
+ # Check if we have a defined machine
576
+ with S() as session:
577
+ machine_config = session.execute(select(Machine)).first()
578
+
579
+ # No machine configured
580
+ if not machine_config and not os.getenv("WNM_TEST_MODE"):
581
+ # Are we initializing a new machine?
582
+ if options.init:
583
+ # Init and dry-run are mutually exclusive
584
+ if options.dry_run:
585
+ logging.error("dry run not supported during init.")
586
+ sys.exit(1)
587
+ else:
588
+ # Did we get a request to migrate from anm?
589
+ if options.migrate_anm:
590
+ if anm_config := migrate_anm(options):
591
+ # Save and reload config
592
+ with S() as session:
593
+ session.execute(insert(Machine), [anm_config])
594
+ session.commit()
595
+ machine_config = session.execute(select(Machine)).first()
596
+ if not machine_config:
597
+ print("Unable to locate record after successful migration")
598
+ sys.exit(1)
599
+ # Get Machine from Row
600
+ machine_config = machine_config[0]
601
+ did_we_init = True
602
+ else:
603
+ print("Failed to migrate machine from anm")
604
+ sys.exit(1)
605
+ else:
606
+ if define_machine(options):
607
+ with S() as session:
608
+ machine_config = session.execute(select(Machine)).first()
609
+ if not machine_config:
610
+ print(
611
+ "Failed to locate record after successfully defining a machine"
612
+ )
613
+ sys.exit(1)
614
+ # Get Machine from Row
615
+ machine_config = machine_config[0]
616
+ did_we_init = True
617
+ else:
618
+ print("Failed to create machine")
619
+ sys.exit(1)
620
+ else:
621
+ print("No config found")
622
+ sys.exit(1)
623
+ else:
624
+ # Fail if we are trying to init a machine that is already initialized
625
+ if options.init:
626
+ logging.warning("Machine already initialized")
627
+ sys.exit(1)
628
+ # Initate a teardown of the machine
629
+ if options.teardown:
630
+ if options.confirm:
631
+ if options.dry_run:
632
+ logging.info("DRY_RUN: Initiate Teardown")
633
+ else:
634
+ teardown_machine(machine_config)
635
+ sys.exit(0)
636
+ else:
637
+ logging.warning("Please confirm the teardown with --confirm")
638
+ sys.exit(1)
639
+ # Get Machine from Row (skip in test mode)
640
+ if not os.getenv("WNM_TEST_MODE"):
641
+ machine_config = machine_config[0]
642
+
643
+ # Collect the proposed changes unless we are initializing (skip in test mode)
644
+ config_updates = merge_config_changes(options, machine_config) if not os.getenv("WNM_TEST_MODE") else {}
645
+ # Failfirst on invalid config change
646
+ if (
647
+ "port_start" in config_updates or "metrics_port_start" in config_updates
648
+ ) and not did_we_init:
649
+ logging.warning("Can not change start port numbers on an active machine")
650
+ sys.exit(1)
651
+
3
652
 
653
+ if __name__ == "__main__":
654
+ print("Changes:", json.loads(json.dumps(config_updates)))
655
+ print(json.loads(json.dumps(machine_config)))