calagoapi 1.0.dev1__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.
Files changed (71) hide show
  1. calagoapi/__init__.py +190 -0
  2. calagoapi/account/__init__.py +14 -0
  3. calagoapi/account/account.py +292 -0
  4. calagoapi/account/auth.py +72 -0
  5. calagoapi/account/models.py +221 -0
  6. calagoapi/admin/__init__.py +48 -0
  7. calagoapi/admin/admin.py +341 -0
  8. calagoapi/admin/managers.py +490 -0
  9. calagoapi/admin/resources.py +970 -0
  10. calagoapi/aio/__init__.py +9 -0
  11. calagoapi/aio/console.py +289 -0
  12. calagoapi/aio/panel.py +176 -0
  13. calagoapi/api/__init__.py +63 -0
  14. calagoapi/api/account.py +195 -0
  15. calagoapi/api/admin.py +402 -0
  16. calagoapi/api/attachments.py +27 -0
  17. calagoapi/api/auth.py +52 -0
  18. calagoapi/api/backups.py +99 -0
  19. calagoapi/api/client.py +198 -0
  20. calagoapi/api/databases.py +217 -0
  21. calagoapi/api/files.py +210 -0
  22. calagoapi/api/network.py +84 -0
  23. calagoapi/api/public.py +13 -0
  24. calagoapi/api/schedules.py +79 -0
  25. calagoapi/api/servers.py +114 -0
  26. calagoapi/api/users.py +27 -0
  27. calagoapi/api/versions.py +29 -0
  28. calagoapi/backups/__init__.py +8 -0
  29. calagoapi/backups/backup.py +292 -0
  30. calagoapi/backups/manager.py +177 -0
  31. calagoapi/console/__init__.py +6 -0
  32. calagoapi/console/console.py +392 -0
  33. calagoapi/databases/__init__.py +12 -0
  34. calagoapi/databases/database.py +445 -0
  35. calagoapi/databases/explorer.py +159 -0
  36. calagoapi/databases/manager.py +122 -0
  37. calagoapi/files/__init__.py +9 -0
  38. calagoapi/files/file.py +196 -0
  39. calagoapi/files/manager.py +437 -0
  40. calagoapi/network/__init__.py +8 -0
  41. calagoapi/network/allocation.py +99 -0
  42. calagoapi/network/manager.py +132 -0
  43. calagoapi/nodes/__init__.py +6 -0
  44. calagoapi/nodes/node.py +477 -0
  45. calagoapi/panel.py +162 -0
  46. calagoapi/schedules/__init__.py +14 -0
  47. calagoapi/schedules/builders.py +461 -0
  48. calagoapi/schedules/manager.py +107 -0
  49. calagoapi/schedules/schedule.py +214 -0
  50. calagoapi/servers/__init__.py +13 -0
  51. calagoapi/servers/admin.py +430 -0
  52. calagoapi/servers/generic.py +785 -0
  53. calagoapi/servers/manager.py +167 -0
  54. calagoapi/servers/minecraft.py +207 -0
  55. calagoapi/tunnels/__init__.py +5 -0
  56. calagoapi/tunnels/tunnel.py +112 -0
  57. calagoapi/users/__init__.py +11 -0
  58. calagoapi/users/activity.py +45 -0
  59. calagoapi/users/subuser.py +127 -0
  60. calagoapi/users/user.py +267 -0
  61. calagoapi/utils/__init__.py +43 -0
  62. calagoapi/utils/convert.py +113 -0
  63. calagoapi/utils/exceptions.py +72 -0
  64. calagoapi/utils/model.py +142 -0
  65. calagoapi/utils/resource.py +100 -0
  66. calagoapi/utils/time.py +68 -0
  67. calagoapi-1.0.dev1.dist-info/METADATA +266 -0
  68. calagoapi-1.0.dev1.dist-info/RECORD +71 -0
  69. calagoapi-1.0.dev1.dist-info/WHEEL +5 -0
  70. calagoapi-1.0.dev1.dist-info/licenses/LICENSE +674 -0
  71. calagoapi-1.0.dev1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,461 @@
1
+ '''
2
+ Helpers that build schedule triggers, conditions and actions.
3
+
4
+ Calagopus schedules are made of:
5
+ - triggers: what starts the schedule (a cron expression, a crash, a console line, ...)
6
+ - a condition: checked before running (server must be online, file must exist, ...)
7
+ - steps: ordered actions (send a command, wait, create a backup, ...)
8
+
9
+ These helpers return plain dicts, so you can also write the JSON yourself.
10
+ Durations and timeouts are in milliseconds. Text parameters accept either a
11
+ string or `variable('name')` to reuse a value captured by an earlier step.
12
+
13
+ Example:
14
+ from calagoapi import triggers, conditions, actions
15
+
16
+ server.schedules.createSchedule(
17
+ 'Nightly restart',
18
+ triggers=[triggers.cron('0 0 4 * * *')],
19
+ condition=conditions.serverState('running'),
20
+ steps=[
21
+ actions.sendCommand('say Restarting in 1 minute'),
22
+ actions.sleep(60_000),
23
+ actions.createBackup('nightly'),
24
+ actions.sendPower('restart'),
25
+ ],
26
+ )
27
+ '''
28
+ from types import SimpleNamespace
29
+
30
+ from calagoapi.utils.model import idOf
31
+
32
+
33
+ def variable(name: str) -> dict:
34
+ '''
35
+ References a schedule variable, for use in any text parameter.
36
+
37
+ Example:
38
+ actions.waitForConsoleLine('joined the game', output_into=variable('line'))
39
+ actions.sendCommand(variable('line'))
40
+ '''
41
+ return {'variable': name}
42
+
43
+
44
+ def _var(value):
45
+ # output_into accepts a variable name or variable('name')
46
+ return variable(value) if isinstance(value, str) else value
47
+
48
+
49
+ # triggers
50
+ def _cron(schedule: str) -> dict:
51
+ '''
52
+ Runs on a cron schedule. Calagopus cron expressions have 6 fields, starting with seconds.
53
+
54
+ Example:
55
+ triggers.cron('0 */30 * * * *') # every 30 minutes
56
+ '''
57
+ return {'type': 'cron', 'schedule': schedule}
58
+
59
+ def _powerAction(action: str) -> dict:
60
+ '''Runs when a power action (start, stop, restart, kill) is sent to the server.'''
61
+ return {'type': 'power_action', 'action': action}
62
+
63
+ def _serverState(state: str) -> dict:
64
+ '''Runs when the server enters a state: offline, starting, stopping, running.'''
65
+ return {'type': 'server_state', 'state': state}
66
+
67
+ def _backupStatus(status: str) -> dict:
68
+ '''Runs when a backup is starting, finished or failed.'''
69
+ return {'type': 'backup_status', 'status': status}
70
+
71
+ def _databaseBackupStatus(status: str) -> dict:
72
+ '''Runs when a database backup is starting, finished or failed.'''
73
+ return {'type': 'database_backup_status', 'status': status}
74
+
75
+ def _scheduleCompletion(schedule, successful: bool = True) -> dict:
76
+ '''Runs after another schedule completes (successfully or not).'''
77
+ return {'type': 'schedule_completion', 'schedule': idOf(schedule), 'successful': successful}
78
+
79
+ def _resourceUsage(metric: str, comparator: str, value: float, for_seconds: int = None) -> dict:
80
+ '''
81
+ Runs when resource usage crosses a threshold.
82
+
83
+ Args:
84
+ metric: cpu, memory or disk.
85
+ comparator: smaller_than, smaller_than_or_equals, equal, greater_than, greater_than_or_equals.
86
+ value: Threshold.
87
+ for_seconds: How long the threshold must hold.
88
+
89
+ Example:
90
+ triggers.resourceUsage('memory', 'greater_than', 95, for_seconds=60)
91
+ '''
92
+ trigger = {'type': 'resource_usage', 'metric': metric, 'comparator': comparator, 'value': value}
93
+ if for_seconds is not None:
94
+ trigger['for_seconds'] = for_seconds
95
+ return trigger
96
+
97
+ def _consoleLine(contains: str, case_insensitive: bool = False, output_into: str = None) -> dict:
98
+ '''Runs when a console line contains the given text. output_into stores the line in a variable.'''
99
+ return {'type': 'console_line', 'contains': contains, 'case_insensitive': case_insensitive, 'output_into': _var(output_into)}
100
+
101
+ def _crash() -> dict:
102
+ '''Runs when the server crashes.'''
103
+ return {'type': 'crash'}
104
+
105
+
106
+ triggers = SimpleNamespace(
107
+ cron=_cron,
108
+ powerAction=_powerAction,
109
+ serverState=_serverState,
110
+ backupStatus=_backupStatus,
111
+ databaseBackupStatus=_databaseBackupStatus,
112
+ scheduleCompletion=_scheduleCompletion,
113
+ resourceUsage=_resourceUsage,
114
+ consoleLine=_consoleLine,
115
+ crash=_crash,
116
+ )
117
+
118
+
119
+ # conditions
120
+ def _none() -> dict:
121
+ '''No condition, the schedule always runs.'''
122
+ return {'type': 'none'}
123
+
124
+ def _all(*conditions) -> dict:
125
+ '''All conditions must match.'''
126
+ return {'type': 'and', 'conditions': list(conditions)}
127
+
128
+ def _any(*conditions) -> dict:
129
+ '''At least one condition must match.'''
130
+ return {'type': 'or', 'conditions': list(conditions)}
131
+
132
+ def _negate(condition: dict) -> dict:
133
+ '''Inverts a condition.'''
134
+ return {'type': 'not', 'condition': condition}
135
+
136
+ def _conditionServerState(state: str) -> dict:
137
+ '''The server must be in a state: offline, starting, stopping, running.'''
138
+ return {'type': 'server_state', 'state': state}
139
+
140
+ def _uptime(comparator: str, value: int) -> dict:
141
+ '''Compares the server uptime (milliseconds).'''
142
+ return {'type': 'uptime', 'comparator': comparator, 'value': value}
143
+
144
+ def _conditionResourceUsage(metric: str, comparator: str, value: float) -> dict:
145
+ '''Compares current cpu, memory or disk usage.'''
146
+ return {'type': 'resource_usage', 'metric': metric, 'comparator': comparator, 'value': value}
147
+
148
+ def _fileExists(file: str) -> dict:
149
+ '''A file must exist on the server.'''
150
+ return {'type': 'file_exists', 'file': file}
151
+
152
+ def _variableExists(name: str) -> dict:
153
+ '''A schedule variable must be set.'''
154
+ return {'type': 'variable_exists', 'variable': variable(name)}
155
+
156
+ def _variableEquals(name: str, value) -> dict:
157
+ '''A schedule variable must equal a value.'''
158
+ return {'type': 'variable_equals', 'variable': variable(name), 'equals': value}
159
+
160
+ def _variableContains(name: str, value) -> dict:
161
+ '''A schedule variable must contain a value.'''
162
+ return {'type': 'variable_contains', 'variable': variable(name), 'contains': value}
163
+
164
+ def _variableStartsWith(name: str, value) -> dict:
165
+ '''A schedule variable must start with a value.'''
166
+ return {'type': 'variable_starts_with', 'variable': variable(name), 'starts_with': value}
167
+
168
+ def _variableEndsWith(name: str, value) -> dict:
169
+ '''A schedule variable must end with a value.'''
170
+ return {'type': 'variable_ends_with', 'variable': variable(name), 'ends_with': value}
171
+
172
+
173
+ conditions = SimpleNamespace(
174
+ none=_none,
175
+ all=_all,
176
+ any=_any,
177
+ negate=_negate,
178
+ serverState=_conditionServerState,
179
+ uptime=_uptime,
180
+ resourceUsage=_conditionResourceUsage,
181
+ fileExists=_fileExists,
182
+ variableExists=_variableExists,
183
+ variableEquals=_variableEquals,
184
+ variableContains=_variableContains,
185
+ variableStartsWith=_variableStartsWith,
186
+ variableEndsWith=_variableEndsWith,
187
+ )
188
+
189
+
190
+ # backup selectors for backup actions
191
+ def _latestBackup(group=None) -> dict:
192
+ '''Selects the newest backup, optionally inside a backup group.'''
193
+ return {'mode': 'latest', 'backup_group_uuid': idOf(group)}
194
+
195
+ def _oldestBackup(group=None) -> dict:
196
+ '''Selects the oldest backup, optionally inside a backup group.'''
197
+ return {'mode': 'oldest', 'backup_group_uuid': idOf(group)}
198
+
199
+ def _backupById(backup) -> dict:
200
+ '''Selects a backup by UUID.'''
201
+ return {'mode': 'uuid', 'uuid': idOf(backup)}
202
+
203
+ def _backupByName(name, group=None, oldest: bool = False) -> dict:
204
+ '''Selects a backup by name (newest match unless oldest=True).'''
205
+ return {'mode': 'name', 'name': name, 'backup_group_uuid': idOf(group), 'oldest': oldest}
206
+
207
+
208
+ backups = SimpleNamespace(
209
+ latest=_latestBackup,
210
+ oldest=_oldestBackup,
211
+ byId=_backupById,
212
+ byName=_backupByName,
213
+ )
214
+
215
+
216
+ # actions
217
+ def _sleep(duration: int) -> dict:
218
+ '''Waits `duration` milliseconds.'''
219
+ return {'type': 'sleep', 'duration': duration}
220
+
221
+ def _ensure(condition: dict) -> dict:
222
+ '''Stops the schedule unless the condition matches.'''
223
+ return {'type': 'ensure', 'condition': condition}
224
+
225
+ def _if(condition: dict) -> dict:
226
+ '''Starts a conditional block. Close it with actions.endIf().'''
227
+ return {'type': 'if', 'condition': condition}
228
+
229
+ def _elseIf(condition: dict) -> dict:
230
+ '''Adds an else-if branch to a conditional block.'''
231
+ return {'type': 'else_if', 'condition': condition}
232
+
233
+ def _else() -> dict:
234
+ '''Adds an else branch to a conditional block.'''
235
+ return {'type': 'else'}
236
+
237
+ def _endIf() -> dict:
238
+ '''Ends a conditional block.'''
239
+ return {'type': 'end_if'}
240
+
241
+ def _exit(successful: bool = True) -> dict:
242
+ '''Stops the schedule.'''
243
+ return {'type': 'exit', 'successful': successful}
244
+
245
+ def _waitForState(state: str, timeout: int = 60_000, ignore_failure: bool = False) -> dict:
246
+ '''Waits until the server reaches a state (timeout in milliseconds).'''
247
+ return {'type': 'wait_for_state', 'state': state, 'timeout': timeout, 'ignore_failure': ignore_failure}
248
+
249
+ def _format(format: str, output_into: str) -> dict:
250
+ '''Formats text into a variable. Use {variable} placeholders.'''
251
+ return {'type': 'format', 'format': format, 'output_into': _var(output_into)}
252
+
253
+ def _matchRegex(input, regex: str, output_into: list) -> dict:
254
+ '''Runs a regex on `input` and stores capture groups into variables (names or None).'''
255
+ return {'type': 'match_regex', 'input': input, 'regex': regex, 'output_into': [_var(v) for v in output_into]}
256
+
257
+ def _waitForConsoleLine(contains, timeout: int = 60_000, case_insensitive: bool = False, output_into: str = None, ignore_failure: bool = False) -> dict:
258
+ '''Waits for a console line containing text (timeout in milliseconds).'''
259
+ return {
260
+ 'type': 'wait_for_console_line',
261
+ 'contains': contains,
262
+ 'timeout': timeout,
263
+ 'case_insensitive': case_insensitive,
264
+ 'output_into': _var(output_into),
265
+ 'ignore_failure': ignore_failure,
266
+ }
267
+
268
+ def _sendPower(action: str, ignore_failure: bool = False) -> dict:
269
+ '''Sends a power action: start, stop, restart or kill.'''
270
+ return {'type': 'send_power', 'action': action, 'ignore_failure': ignore_failure}
271
+
272
+ def _sendCommand(command, ignore_failure: bool = False) -> dict:
273
+ '''Sends a console command.'''
274
+ return {'type': 'send_command', 'command': command, 'ignore_failure': ignore_failure}
275
+
276
+ def _createBackup(name=None, ignored_files: list = None, group=None, foreground: bool = False, output_into: str = None, ignore_failure: bool = False) -> dict:
277
+ '''Creates a backup. `foreground=True` waits for it to finish before the next step.'''
278
+ return {
279
+ 'type': 'create_backup',
280
+ 'name': name,
281
+ 'ignored_files': ignored_files or [],
282
+ 'backup_group_uuid': idOf(group),
283
+ 'foreground': foreground,
284
+ 'output_into': _var(output_into),
285
+ 'ignore_failure': ignore_failure,
286
+ }
287
+
288
+ def _restoreBackup(backup: dict = None, truncate_directory: bool = False, restore_startup: bool = False, ignore_failure: bool = False) -> dict:
289
+ '''Restores a backup. `backup` is a selector from `backups.*`, defaults to the latest.'''
290
+ return {
291
+ 'type': 'restore_backup',
292
+ 'backup': backup or _latestBackup(),
293
+ 'truncate_directory': truncate_directory,
294
+ 'restore_startup': restore_startup,
295
+ 'ignore_failure': ignore_failure,
296
+ }
297
+
298
+ def _deleteBackup(backup: dict = None, ignore_failure: bool = False) -> dict:
299
+ '''Deletes a backup. `backup` is a selector from `backups.*`, defaults to the oldest.'''
300
+ return {'type': 'delete_backup', 'backup': backup or _oldestBackup(), 'ignore_failure': ignore_failure}
301
+
302
+ def _moveBackup(backup: dict, group, ignore_failure: bool = False) -> dict:
303
+ '''Moves a backup into a backup group.'''
304
+ return {'type': 'move_backup', 'backup': backup, 'backup_group_uuid': idOf(group), 'ignore_failure': ignore_failure}
305
+
306
+ def _createDatabaseBackup(database_instance, name=None, group=None, foreground: bool = False, output_into: str = None, ignore_failure: bool = False) -> dict:
307
+ '''Creates a backup of a database instance.'''
308
+ return {
309
+ 'type': 'create_database_backup',
310
+ 'database_instance_uuid': idOf(database_instance),
311
+ 'name': name,
312
+ 'backup_group_uuid': idOf(group),
313
+ 'foreground': foreground,
314
+ 'output_into': _var(output_into),
315
+ 'ignore_failure': ignore_failure,
316
+ }
317
+
318
+ def _deleteDatabaseBackup(backup: dict, database_instance=None, ignore_failure: bool = False) -> dict:
319
+ '''Deletes a database backup.'''
320
+ return {'type': 'delete_database_backup', 'backup': backup, 'database_instance_uuid': idOf(database_instance), 'ignore_failure': ignore_failure}
321
+
322
+ def _moveDatabaseBackup(backup: dict, group, database_instance=None, ignore_failure: bool = False) -> dict:
323
+ '''Moves a database backup into a backup group.'''
324
+ return {
325
+ 'type': 'move_database_backup',
326
+ 'backup': backup,
327
+ 'backup_group_uuid': idOf(group),
328
+ 'database_instance_uuid': idOf(database_instance),
329
+ 'ignore_failure': ignore_failure,
330
+ }
331
+
332
+ def _restoreDatabaseBackup(backup: dict, database_instance=None, source_database_instance=None, ignore_failure: bool = False) -> dict:
333
+ '''Restores a database backup.'''
334
+ return {
335
+ 'type': 'restore_database_backup',
336
+ 'backup': backup,
337
+ 'database_instance_uuid': idOf(database_instance),
338
+ 'source_database_instance_uuid': idOf(source_database_instance),
339
+ 'ignore_failure': ignore_failure,
340
+ }
341
+
342
+ def _createDirectory(root, name, ignore_failure: bool = False) -> dict:
343
+ '''Creates a directory.'''
344
+ return {'type': 'create_directory', 'root': root, 'name': name, 'ignore_failure': ignore_failure}
345
+
346
+ def _writeFile(file, content, append: bool = False, ignore_failure: bool = False) -> dict:
347
+ '''Writes (or appends) text to a file.'''
348
+ return {'type': 'write_file', 'file': file, 'content': content, 'append': append, 'ignore_failure': ignore_failure}
349
+
350
+ def _copyFile(file, destination, foreground: bool = False, ignore_failure: bool = False) -> dict:
351
+ '''Copies a file.'''
352
+ return {'type': 'copy_file', 'file': file, 'destination': destination, 'foreground': foreground, 'ignore_failure': ignore_failure}
353
+
354
+ def _deleteFiles(root, files: list, ignore_failure: bool = False) -> dict:
355
+ '''Deletes files from a directory.'''
356
+ return {'type': 'delete_files', 'root': root, 'files': files, 'ignore_failure': ignore_failure}
357
+
358
+ def _renameFiles(root, files: list, ignore_failure: bool = False) -> dict:
359
+ '''Renames files. `files` is a list of {'from': ..., 'to': ...} or (from, to) tuples.'''
360
+ files = [f if isinstance(f, dict) else {'from': f[0], 'to': f[1]} for f in files]
361
+ return {'type': 'rename_files', 'root': root, 'files': files, 'ignore_failure': ignore_failure}
362
+
363
+ def _compressFiles(root, files: list, name, format: str = 'tar_gz', foreground: bool = False, ignore_failure: bool = False) -> dict:
364
+ '''Compresses files into an archive.'''
365
+ return {
366
+ 'type': 'compress_files',
367
+ 'root': root,
368
+ 'files': files,
369
+ 'name': name,
370
+ 'format': format,
371
+ 'foreground': foreground,
372
+ 'ignore_failure': ignore_failure,
373
+ }
374
+
375
+ def _decompressFile(root, file, foreground: bool = False, ignore_failure: bool = False) -> dict:
376
+ '''Extracts an archive.'''
377
+ return {'type': 'decompress_file', 'root': root, 'file': file, 'foreground': foreground, 'ignore_failure': ignore_failure}
378
+
379
+ def _pullFile(url, root='/', file_name=None, use_header: bool = False, foreground: bool = False, ignore_failure: bool = False) -> dict:
380
+ '''Downloads a file from a URL onto the server.'''
381
+ return {
382
+ 'type': 'pull_file',
383
+ 'url': url,
384
+ 'root': root,
385
+ 'file_name': file_name,
386
+ 'use_header': use_header,
387
+ 'foreground': foreground,
388
+ 'ignore_failure': ignore_failure,
389
+ }
390
+
391
+ def _updateStartupVariable(env_variable, value, ignore_failure: bool = False) -> dict:
392
+ '''Changes a startup variable.'''
393
+ return {'type': 'update_startup_variable', 'env_variable': env_variable, 'value': value, 'ignore_failure': ignore_failure}
394
+
395
+ def _updateStartupCommand(command, ignore_failure: bool = False) -> dict:
396
+ '''Changes the startup command.'''
397
+ return {'type': 'update_startup_command', 'command': command, 'ignore_failure': ignore_failure}
398
+
399
+ def _updateDockerImage(image, ignore_failure: bool = False) -> dict:
400
+ '''Changes the docker image.'''
401
+ return {'type': 'update_startup_docker_image', 'image': image, 'ignore_failure': ignore_failure}
402
+
403
+ def _httpRequest(url: str, method: str = 'post', body=None, headers: dict = None, timeout: int = 10_000,
404
+ ignore_error_status: bool = False, output_status_into: str = None, output_body_into: str = None,
405
+ ignore_failure: bool = False) -> dict:
406
+ '''
407
+ Sends an HTTP request, e.g. to a Discord webhook.
408
+
409
+ Example:
410
+ actions.httpRequest('https://discord.com/api/webhooks/...', body='{"content": "Restarting"}',
411
+ headers={'Content-Type': 'application/json'})
412
+ '''
413
+ return {
414
+ 'type': 'http_request',
415
+ 'url': url,
416
+ 'method': method,
417
+ 'body': body,
418
+ 'headers': [{'name': k, 'value': v} for k, v in (headers or {}).items()],
419
+ 'timeout': timeout,
420
+ 'ignore_error_status': ignore_error_status,
421
+ 'output_status_into': _var(output_status_into),
422
+ 'output_body_into': _var(output_body_into),
423
+ 'ignore_failure': ignore_failure,
424
+ }
425
+
426
+
427
+ actions = SimpleNamespace(
428
+ sleep=_sleep,
429
+ ensure=_ensure,
430
+ ifCondition=_if,
431
+ elseIf=_elseIf,
432
+ otherwise=_else,
433
+ endIf=_endIf,
434
+ exit=_exit,
435
+ waitForState=_waitForState,
436
+ format=_format,
437
+ matchRegex=_matchRegex,
438
+ waitForConsoleLine=_waitForConsoleLine,
439
+ sendPower=_sendPower,
440
+ sendCommand=_sendCommand,
441
+ createBackup=_createBackup,
442
+ restoreBackup=_restoreBackup,
443
+ deleteBackup=_deleteBackup,
444
+ moveBackup=_moveBackup,
445
+ createDatabaseBackup=_createDatabaseBackup,
446
+ deleteDatabaseBackup=_deleteDatabaseBackup,
447
+ moveDatabaseBackup=_moveDatabaseBackup,
448
+ restoreDatabaseBackup=_restoreDatabaseBackup,
449
+ createDirectory=_createDirectory,
450
+ writeFile=_writeFile,
451
+ copyFile=_copyFile,
452
+ deleteFiles=_deleteFiles,
453
+ renameFiles=_renameFiles,
454
+ compressFiles=_compressFiles,
455
+ decompressFile=_decompressFile,
456
+ pullFile=_pullFile,
457
+ updateStartupVariable=_updateStartupVariable,
458
+ updateStartupCommand=_updateStartupCommand,
459
+ updateDockerImage=_updateDockerImage,
460
+ httpRequest=_httpRequest,
461
+ )
@@ -0,0 +1,107 @@
1
+ from calagoapi.schedules.schedule import Schedule
2
+ from calagoapi.schedules.builders import conditions as _conditions
3
+ from calagoapi.utils.model import idOf
4
+
5
+
6
+ class ScheduleManager:
7
+ '''
8
+ Schedule management for one server, available as `server.schedules`.
9
+
10
+ See `calagoapi.triggers`, `calagoapi.conditions` and `calagoapi.actions`
11
+ for building schedules without writing JSON by hand.
12
+
13
+ Args:
14
+ server: The server to manage schedules for.
15
+
16
+ Example:
17
+ from calagoapi import triggers, actions
18
+
19
+ server.schedules.createSchedule(
20
+ 'Hourly announcement',
21
+ triggers=[triggers.cron('0 0 * * * *')],
22
+ steps=[actions.sendCommand('say Thanks for playing!')],
23
+ )
24
+ '''
25
+ def __init__(self, server):
26
+ self.server = server
27
+ self.api = server.panel.api.schedules
28
+
29
+ @property
30
+ def server_id(self):
31
+ return self.server.server_id
32
+
33
+ def _wrap(self, data):
34
+ return Schedule(self.server, data)
35
+
36
+ def getSchedules(self, search: str = None, page: int = None, per_page: int = None):
37
+ '''Lists the server's schedules as a Page of Schedule objects.'''
38
+ return self.api.list(self.server_id, search, page, per_page).map(self._wrap)
39
+
40
+ def getSchedule(self, schedule_id_or_name: str) -> Schedule:
41
+ '''
42
+ Returns a schedule by UUID or by name.
43
+
44
+ Example:
45
+ server.schedules.getSchedule('Nightly restart')
46
+ '''
47
+ for schedule in self.getSchedules():
48
+ if schedule_id_or_name in (schedule.getUUID(), schedule.getName()):
49
+ return schedule
50
+ return None
51
+
52
+ def createSchedule(self, name: str, triggers: list = None, condition: dict = None, steps: list = None, enabled: bool = True) -> Schedule:
53
+ '''
54
+ Creates a schedule.
55
+
56
+ Args:
57
+ name: Schedule name.
58
+ triggers: List of triggers from `calagoapi.triggers`.
59
+ condition: Condition from `calagoapi.conditions`. Defaults to none.
60
+ steps: List of actions from `calagoapi.actions`, run in order.
61
+ enabled: Whether the schedule is active.
62
+
63
+ Returns:
64
+ The new Schedule.
65
+ '''
66
+ condition = condition or _conditions.none()
67
+ if steps:
68
+ return self.importSchedule({
69
+ 'name': name,
70
+ 'enabled': enabled,
71
+ 'triggers': triggers or [],
72
+ 'condition': condition,
73
+ 'steps': [{'action': action, 'order': order} for order, action in enumerate(steps, start=1)],
74
+ })
75
+ return self._wrap(self.api.create(self.server_id, name, triggers or [], condition, enabled))
76
+
77
+ def importSchedule(self, data: dict) -> Schedule:
78
+ '''Imports a schedule exported with `schedule.export()`.'''
79
+ return self._wrap(self.api.importSchedule(self.server_id, data))
80
+
81
+ def exportSchedule(self, schedule_id: str) -> dict:
82
+ '''Exports a schedule as a dict.'''
83
+ return self.api.export(self.server_id, idOf(schedule_id))
84
+
85
+ def updateSchedule(self, schedule_id: str, name: str = None, enabled: bool = None, triggers: list = None, condition: dict = None) -> None:
86
+ '''Updates a schedule. Only the given fields change.'''
87
+ return self.api.update(self.server_id, idOf(schedule_id), name, enabled, triggers, condition)
88
+
89
+ def deleteSchedule(self, schedule_id: str) -> None:
90
+ '''Deletes a schedule.'''
91
+ return self.api.delete(self.server_id, idOf(schedule_id))
92
+
93
+ def runSchedule(self, schedule_id: str, skip_condition: bool = False) -> None:
94
+ '''Runs a schedule now.'''
95
+ return self.api.trigger(self.server_id, idOf(schedule_id), skip_condition)
96
+
97
+ def abortSchedule(self, schedule_id: str) -> None:
98
+ '''Stops a running schedule.'''
99
+ return self.api.abort(self.server_id, idOf(schedule_id))
100
+
101
+ def enableSchedule(self, schedule_id: str) -> None:
102
+ '''Enables a schedule.'''
103
+ return self.api.update(self.server_id, idOf(schedule_id), enabled=True)
104
+
105
+ def disableSchedule(self, schedule_id: str) -> None:
106
+ '''Disables a schedule.'''
107
+ return self.api.update(self.server_id, idOf(schedule_id), enabled=False)