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,785 @@
1
+ from calagoapi.utils.model import Model, idOf
2
+ from calagoapi.utils.convert import formatDuration
3
+ from calagoapi.utils.exceptions import InvalidActionError
4
+ from calagoapi.utils import time as _time
5
+ from calagoapi.files import FileManager
6
+ from calagoapi.backups import BackupManager
7
+ from calagoapi.databases import DatabaseManager
8
+ from calagoapi.schedules import ScheduleManager
9
+ from calagoapi.users import SubuserManager, Activity
10
+ from calagoapi.network import NetworkManager, Allocation
11
+ from calagoapi.tunnels import TunnelManager
12
+ from calagoapi.console import Console
13
+
14
+
15
+ POWER_ACTIONS = ('start', 'restart', 'stop', 'kill')
16
+
17
+
18
+ class GenericServer(Model):
19
+ '''
20
+ A Calagopus server.
21
+
22
+ Usually obtained with `panel.servers.getServer()`. Every feature of a
23
+ server is reachable from here, either directly (power, console, status,
24
+ resources, settings) or through a sub-manager:
25
+
26
+ - `server.files` FileManager
27
+ - `server.backups` BackupManager
28
+ - `server.databases` DatabaseManager
29
+ - `server.schedules` ScheduleManager
30
+ - `server.users` SubuserManager
31
+ - `server.network` NetworkManager (allocations, subdomains, firewall)
32
+ - `server.tunnel` TunnelManager
33
+ - `server.console` Console (live console over websocket)
34
+
35
+ The most common operations are also available directly on the server,
36
+ with the same names HungerLib uses (createBackup, listFiles, ...).
37
+
38
+ Args:
39
+ panel: The Panel the server lives on.
40
+ server_id: Server UUID (or short id).
41
+ name: Optional display name. Defaults to the name on the panel.
42
+ data: Optional server data, if already fetched.
43
+
44
+ Example:
45
+ server = panel.servers.getServer('abc123')
46
+
47
+ server.restart()
48
+ server.sendCommand('say Hello')
49
+
50
+ if server.isOnline():
51
+ print(server.getStatus(), server.getRAM(), server.getUptime(formatted=True))
52
+
53
+ backup = server.createBackup('before-update')
54
+ '''
55
+ def __init__(self, panel, server_id: str = None, name: str = None, data: dict = None):
56
+ super().__init__(panel, data)
57
+ self.server_id = server_id or self.data.get('uuid')
58
+ if not self.server_id:
59
+ raise ValueError('A server_id is required')
60
+ self.name = name or self.data.get('name') or self.server_id
61
+
62
+ self._cached_resources = None
63
+
64
+ # managers
65
+ self.files = FileManager(self)
66
+ self.backups = BackupManager(self)
67
+ self.databases = DatabaseManager(self)
68
+ self.schedules = ScheduleManager(self)
69
+ self.users = SubuserManager(self)
70
+ self.network = NetworkManager(self)
71
+ self.tunnel = TunnelManager(self)
72
+ self.console = Console(self)
73
+
74
+ def __repr__(self):
75
+ return f'<{self.__class__.__name__} {self.name!r} ({self.server_id})>'
76
+
77
+ def __str__(self):
78
+ return f'{self.name} ({self.server_id})'
79
+
80
+ def __eq__(self, other):
81
+ if not isinstance(other, GenericServer):
82
+ return NotImplemented
83
+ return self.getUUID() == other.getUUID()
84
+
85
+ def __hash__(self):
86
+ return hash(self.getUUID())
87
+
88
+ @property
89
+ def api(self):
90
+ return self.panel.api.servers
91
+
92
+
93
+ # server details
94
+ def details(self) -> dict:
95
+ '''Returns cached server details, fetching them on first use.'''
96
+ if not self.data.get('uuid'):
97
+ self.refreshDetails()
98
+ return self.data
99
+
100
+ def refreshDetails(self) -> dict:
101
+ '''Forces a refetch of the server details, caches, then returns them.'''
102
+ self.data = self.api.get(self.server_id)
103
+ return self.data
104
+
105
+ def get(self, key: str, default=None):
106
+ '''Returns a raw field from the server details.'''
107
+ return self.details().get(key, default)
108
+
109
+ def getUUID(self) -> str:
110
+ '''Returns the full server UUID.'''
111
+ return self.data.get('uuid') or self.server_id
112
+
113
+ def getShortId(self) -> str:
114
+ '''Returns the short server id shown in the panel URL.'''
115
+ return self.get('uuid_short')
116
+
117
+ def getName(self) -> str:
118
+ '''Returns the server name from the panel.'''
119
+ return self.get('name')
120
+
121
+ def getDescription(self) -> str:
122
+ '''Returns the server description.'''
123
+ return self.get('description')
124
+
125
+ def getNodeName(self) -> str:
126
+ '''Returns the name of the node the server runs on.'''
127
+ return self.get('node_name')
128
+
129
+ def getNodeId(self) -> str:
130
+ '''Returns the UUID of the node the server runs on.'''
131
+ return self.get('node_uuid')
132
+
133
+ def getLocationName(self) -> str:
134
+ '''Returns the name of the server's location.'''
135
+ return self.get('location_name')
136
+
137
+ def getEgg(self) -> dict:
138
+ '''Returns the egg (server type) information.'''
139
+ return self.get('egg', {})
140
+
141
+ def getEggName(self) -> str:
142
+ '''Returns the egg name, e.g. "Paper".'''
143
+ return self.getEgg().get('name')
144
+
145
+ def getLimits(self) -> dict:
146
+ '''Returns the resource limits (cpu, memory, swap, disk, ...).'''
147
+ return self.get('limits', {})
148
+
149
+ def getFeatureLimits(self) -> dict:
150
+ '''Returns the feature limits (allocations, databases, backups, schedules, ...).'''
151
+ return self.get('feature_limits', {})
152
+
153
+ def getDockerImage(self) -> str:
154
+ '''Returns the docker image the server runs in.'''
155
+ return self.get('image')
156
+
157
+ def getDockerImages(self) -> dict:
158
+ '''Returns the docker images the egg allows, as {name: image}.'''
159
+ return self.getEgg().get('docker_images', {})
160
+
161
+ def getStartupCommand(self) -> str:
162
+ '''Returns the startup command.'''
163
+ return self.get('startup')
164
+
165
+ def getTimezone(self) -> str:
166
+ '''Returns the server timezone, or None for the node default.'''
167
+ return self.get('timezone')
168
+
169
+ def getAutoKill(self) -> dict:
170
+ '''Returns the auto-kill settings.'''
171
+ return self.get('auto_kill')
172
+
173
+ def getAutoStartBehavior(self) -> str:
174
+ '''Returns "always", "unless_stopped" or "never".'''
175
+ return self.get('auto_start_behavior')
176
+
177
+ def getSftpHost(self) -> str:
178
+ '''Returns the SFTP host.'''
179
+ return self.get('sftp_host')
180
+
181
+ def getSftpPort(self) -> int:
182
+ '''Returns the SFTP port.'''
183
+ return self.get('sftp_port')
184
+
185
+ def getPermissions(self) -> list:
186
+ '''Returns the permissions the current API key has on this server.'''
187
+ return self.get('permissions', [])
188
+
189
+ def hasPermission(self, permission: str) -> bool:
190
+ '''Returns True if the current API key has the permission (owners and admins have "*").'''
191
+ permissions = self.getPermissions()
192
+ return '*' in permissions or permission in permissions
193
+
194
+ def getPrimaryAllocation(self) -> Allocation:
195
+ '''Returns the primary allocation, or None.'''
196
+ data = self.get('allocation')
197
+ return Allocation(self, data) if data else None
198
+
199
+ def getAddress(self) -> str:
200
+ '''
201
+ Returns the address players connect to, e.g. "play.example.com:25565".
202
+
203
+ Uses the IP alias when one is set.
204
+ '''
205
+ allocation = self.getPrimaryAllocation()
206
+ return allocation.getAddress() if allocation else None
207
+
208
+ def getInstallStatus(self) -> str:
209
+ '''
210
+ Returns the server's lifecycle status.
211
+
212
+ Returns:
213
+ None when installed and ready, otherwise a status such as
214
+ 'installing', 'install_failed', 'reinstall_failed' or 'restoring_backup'.
215
+ '''
216
+ return self.refreshDetails().get('status')
217
+
218
+ def getNodeResources(self) -> dict:
219
+ '''Returns resource usage of the node the server runs on.'''
220
+ return self.api.nodeResources(self.getNodeId())
221
+
222
+
223
+ # resource and status
224
+ def refresh(self) -> dict:
225
+ '''Forces refetch of resources, caches, then returns them.'''
226
+ self._cached_resources = self.api.resources(self.server_id)
227
+ return self._cached_resources
228
+
229
+ def resources(self) -> dict:
230
+ '''Returns cached resources (fetched on first use, see refresh()).'''
231
+ return self._cached_resources or self.refresh()
232
+
233
+ def getResources(self) -> dict:
234
+ '''
235
+ Returns fresh resource usage.
236
+
237
+ Returns:
238
+ dict with memory_bytes, memory_limit_bytes, disk_bytes, cpu_absolute,
239
+ cpu_limit_absolute, network (rx_bytes, tx_bytes, ...), uptime and state.
240
+ '''
241
+ return self.refresh()
242
+
243
+ @staticmethod
244
+ def _size(value, rounding, gb):
245
+ if value is None:
246
+ return None
247
+ div = 1024 * 1024 * (1024 if gb else 1)
248
+ return round(value / div, rounding)
249
+
250
+ def getRAM(self, rounding: int = 2, gb: bool = False) -> float:
251
+ '''Returns current RAM usage in MiB (or GiB with gb=True).'''
252
+ return self._size(self.resources().get('memory_bytes'), rounding, gb)
253
+
254
+ def getRAMLimit(self, rounding: int = 2, gb: bool = False) -> float:
255
+ '''Returns the RAM limit in MiB (or GiB with gb=True).'''
256
+ return self._size(self.resources().get('memory_limit_bytes'), rounding, gb)
257
+
258
+ def getCPU(self, rounding: int = 2) -> float:
259
+ '''Returns current CPU usage in percent (100 = one core).'''
260
+ cpu = self.resources().get('cpu_absolute')
261
+ return round(cpu, rounding) if cpu is not None else None
262
+
263
+ def getCPULimit(self) -> float:
264
+ '''Returns the CPU limit in percent (0 = unlimited).'''
265
+ return self.resources().get('cpu_limit_absolute')
266
+
267
+ def getDisk(self, rounding: int = 2, gb: bool = False) -> float:
268
+ '''Returns current disk usage in MiB (or GiB with gb=True).'''
269
+ return self._size(self.resources().get('disk_bytes'), rounding, gb)
270
+
271
+ def getDiskLimit(self, rounding: int = 2, gb: bool = False) -> float:
272
+ '''Returns the disk limit in MiB (or GiB with gb=True), 0 means unlimited.'''
273
+ disk = self.getLimits().get('disk')
274
+ if disk is None:
275
+ return None
276
+ return round(disk / 1024, rounding) if gb else disk
277
+
278
+ def getNetworkIn(self, rounding: int = 2, gb: bool = False) -> float:
279
+ '''Returns received network traffic in MiB (or GiB with gb=True).'''
280
+ return self._size((self.resources().get('network') or {}).get('rx_bytes'), rounding, gb)
281
+
282
+ def getNetworkOut(self, rounding: int = 2, gb: bool = False) -> float:
283
+ '''Returns sent network traffic in MiB (or GiB with gb=True).'''
284
+ return self._size((self.resources().get('network') or {}).get('tx_bytes'), rounding, gb)
285
+
286
+ def getUptime(self, formatted: bool = False):
287
+ '''
288
+ Returns current uptime.
289
+
290
+ Args:
291
+ formatted: Return a string like "3h 12m" instead of milliseconds.
292
+ '''
293
+ uptime = self.resources().get('uptime')
294
+ if uptime is None or not formatted:
295
+ return uptime
296
+ return formatDuration(uptime)
297
+
298
+ def getStatus(self) -> str:
299
+ '''
300
+ Returns the server's current power state (always fresh).
301
+
302
+ Returns:
303
+ 'running', 'offline', 'starting' or 'stopping'.
304
+
305
+ Example:
306
+ print(server.getStatus())
307
+ '''
308
+ return self.refresh().get('state')
309
+
310
+ def getPowerState(self) -> str:
311
+ '''Alias of getStatus().'''
312
+ return self.getStatus()
313
+
314
+
315
+ # state helpers
316
+ def isOnline(self) -> bool:
317
+ '''Returns True if the server is running.'''
318
+ return self.getStatus() == 'running'
319
+
320
+ def isOffline(self) -> bool:
321
+ '''Returns True if the server is fully offline.'''
322
+ return self.getStatus() == 'offline'
323
+
324
+ def isRunning(self) -> bool:
325
+ '''Alias of isOnline().'''
326
+ return self.isOnline()
327
+
328
+ def isStopped(self) -> bool:
329
+ '''Alias of isOffline().'''
330
+ return self.isOffline()
331
+
332
+ def isStarting(self) -> bool:
333
+ '''Returns True while the server is starting.'''
334
+ return self.getStatus() == 'starting'
335
+
336
+ def isStopping(self) -> bool:
337
+ '''Returns True while the server is stopping.'''
338
+ return self.getStatus() == 'stopping'
339
+
340
+ def isInstalled(self) -> bool:
341
+ '''Returns True when installation has finished and the server is usable.'''
342
+ return self.getInstallStatus() is None
343
+
344
+ def isInstalling(self) -> bool:
345
+ '''Returns True while the server is (re)installing.'''
346
+ return self.getInstallStatus() == 'installing'
347
+
348
+ def isInstallFailed(self) -> bool:
349
+ '''Returns True if the last (re)install failed.'''
350
+ return self.getInstallStatus() in ('install_failed', 'reinstall_failed')
351
+
352
+ def isRestoringBackup(self) -> bool:
353
+ '''Returns True while a backup is being restored.'''
354
+ return self.getInstallStatus() == 'restoring_backup'
355
+
356
+ def isSuspended(self) -> bool:
357
+ '''Returns True if the server is suspended.'''
358
+ return bool(self.refreshDetails().get('is_suspended'))
359
+
360
+ def isTransferring(self) -> bool:
361
+ '''Returns True while the server is being moved to another node.'''
362
+ return bool(self.refreshDetails().get('is_transferring'))
363
+
364
+ def isOwner(self) -> bool:
365
+ '''Returns True if the API key's user owns this server.'''
366
+ return bool(self.get('is_owner'))
367
+
368
+ def isNodeInMaintenance(self) -> bool:
369
+ '''Returns True if the server's node is in maintenance mode.'''
370
+ return bool(self.refreshDetails().get('node_maintenance_enabled'))
371
+
372
+ def waitForState(self, state: str, timeout: int = 60, interval: int = 2) -> bool:
373
+ '''Blocks until the server reaches a power state. Returns False on timeout.'''
374
+ return _time.waitForState(self, state, timeout, interval)
375
+
376
+ def waitForOnline(self, timeout: int = 60, interval: int = 2) -> bool:
377
+ '''
378
+ Blocks until the server is running. Returns False on timeout.
379
+
380
+ Example:
381
+ server.start()
382
+ server.waitForOnline(timeout=120)
383
+ '''
384
+ return _time.waitForOnline(self, timeout, interval)
385
+
386
+ def waitForOffline(self, timeout: int = 60, interval: int = 2) -> bool:
387
+ '''Blocks until the server is offline. Returns False on timeout.'''
388
+ return _time.waitForOffline(self, timeout, interval)
389
+
390
+
391
+ # power actions
392
+ def powerAction(self, action: str) -> None:
393
+ '''
394
+ Executes a power action on the server. Valid actions:
395
+ - start
396
+ - restart
397
+ - stop
398
+ - kill
399
+ '''
400
+ if action not in POWER_ACTIONS:
401
+ raise InvalidActionError(f'Invalid power action "{action}", expected one of: {", ".join(POWER_ACTIONS)}')
402
+ return self.api.power(self.server_id, action)
403
+
404
+ def start(self) -> None:
405
+ '''Starts the server.'''
406
+ return self.powerAction('start')
407
+
408
+ def restart(self) -> None:
409
+ '''Restarts the server.'''
410
+ return self.powerAction('restart')
411
+
412
+ def stop(self) -> None:
413
+ '''Stops the server gracefully.'''
414
+ return self.powerAction('stop')
415
+
416
+ def kill(self) -> None:
417
+ '''Kills the server process immediately.'''
418
+ return self.powerAction('kill')
419
+
420
+
421
+ # console
422
+ def sendCommand(self, command: str) -> None:
423
+ '''
424
+ Sends a command to the server console.
425
+
426
+ Example:
427
+ server.sendCommand('say Hello')
428
+ '''
429
+ return self.api.command(self.server_id, command)
430
+
431
+ def sendCommands(self, commands: list) -> None:
432
+ '''Sends several commands in order.'''
433
+ for command in commands:
434
+ self.sendCommand(command)
435
+
436
+ def getLogs(self, lines: int = None) -> str:
437
+ '''
438
+ Returns the recent console output.
439
+
440
+ Args:
441
+ lines: Number of lines to return.
442
+ '''
443
+ return self.api.logs(self.server_id, lines)
444
+
445
+ def getWebsocket(self) -> dict:
446
+ '''
447
+ Returns credentials for the live console websocket.
448
+
449
+ You rarely need this, `server.console` handles the websocket for you.
450
+
451
+ Returns:
452
+ dict with `url` and `token`.
453
+ '''
454
+ return self.api.websocket(self.server_id)
455
+
456
+ def streamConsole(self, history: bool = True, timeout: float = None):
457
+ '''
458
+ Yields console lines live, as they are printed.
459
+
460
+ Args:
461
+ history: Start with the recent lines the daemon still has.
462
+ timeout: Stop when nothing is printed for this many seconds. None streams forever.
463
+
464
+ Example:
465
+ for line in server.streamConsole():
466
+ if 'joined the game' in line:
467
+ print(line)
468
+ '''
469
+ with Console(self) as console:
470
+ yield from console.lines(history, timeout)
471
+
472
+ def onConsoleLine(self, callback, history: bool = False) -> Console:
473
+ '''
474
+ Calls `callback(line)` for every console line, in a background thread.
475
+
476
+ Reconnects automatically. Call `server.console.stop()` to end it.
477
+
478
+ Example:
479
+ server.onConsoleLine(print)
480
+ '''
481
+ self.console.onConsoleLine(callback)
482
+ return self.console.listen(history=history)
483
+
484
+ def getActivity(self, search: str = None, user=None, page: int = 1, per_page: int = 50):
485
+ '''
486
+ Returns the server activity log as a Page of Activity objects.
487
+
488
+ Args:
489
+ user: Only show events of this user (UUID).
490
+ page: Page number (1 = newest). Pass None for every entry.
491
+ '''
492
+ return self.api.activity(self.server_id, idOf(user), search, page, per_page).map(
493
+ lambda data: Activity(self.panel, data)
494
+ )
495
+
496
+ def getAnnouncements(self) -> list:
497
+ '''Returns active panel announcements relevant to this server.'''
498
+ return self.api.announcements(self.server_id)
499
+
500
+
501
+ # settings
502
+ def rename(self, name: str = None, description: str = None) -> 'GenericServer':
503
+ '''Changes the server name and/or description.'''
504
+ self.api.rename(self.server_id, name, description)
505
+ if name:
506
+ self.name = name
507
+ return self._update({'name': name, 'description': description})
508
+
509
+ def setName(self, name: str) -> 'GenericServer':
510
+ '''Changes the server name.'''
511
+ return self.rename(name=name)
512
+
513
+ def setDescription(self, description: str) -> 'GenericServer':
514
+ '''Changes the server description.'''
515
+ return self.rename(description=description)
516
+
517
+ def setTimezone(self, timezone: str) -> 'GenericServer':
518
+ '''Sets the server timezone, e.g. "Europe/Berlin". None resets to the node default.'''
519
+ self.api.setTimezone(self.server_id, timezone)
520
+ self.data['timezone'] = timezone
521
+ return self
522
+
523
+ def setAutoKill(self, enabled: bool, seconds: int = None) -> None:
524
+ '''
525
+ Configures auto-kill: kill the server if it takes too long to stop.
526
+
527
+ Args:
528
+ enabled: Turn auto-kill on or off.
529
+ seconds: Grace period before killing.
530
+ '''
531
+ return self.api.setAutoKill(self.server_id, enabled, seconds)
532
+
533
+ def setAutoStart(self, behavior: str) -> None:
534
+ '''Sets auto-start after node reboots: "always", "unless_stopped" or "never".'''
535
+ return self.api.setAutoStart(self.server_id, behavior)
536
+
537
+ def reinstall(self, truncate_directory: bool = False, start_on_completion: bool = None) -> None:
538
+ '''
539
+ Reinstalls the server using its egg's install script.
540
+
541
+ Args:
542
+ truncate_directory: Delete every file first.
543
+ start_on_completion: Start the server once installed.
544
+ '''
545
+ return self.api.reinstall(self.server_id, truncate_directory, start_on_completion)
546
+
547
+ def cancelInstall(self) -> None:
548
+ '''Cancels a running (re)install.'''
549
+ return self.api.cancelInstall(self.server_id)
550
+
551
+ def unlockInstall(self) -> None:
552
+ '''Clears a failed install state so the server can be used again.'''
553
+ return self.api.unlockInstall(self.server_id)
554
+
555
+
556
+ # startup variables
557
+ def getStartupVariables(self) -> list:
558
+ '''
559
+ Returns the startup variables.
560
+
561
+ Each variable is a dict with name, env_variable, value, default_value,
562
+ is_editable, is_secret and rules.
563
+ '''
564
+ return self.api.startupVariables(self.server_id)
565
+
566
+ def getStartupVariable(self, key: str) -> str:
567
+ '''
568
+ Returns the value of one startup variable.
569
+
570
+ Example:
571
+ server.getStartupVariable('SERVER_JARFILE')
572
+ '''
573
+ for variable in self.getStartupVariables():
574
+ if variable.get('env_variable') == key:
575
+ return variable.get('value')
576
+ return None
577
+
578
+ def updateStartupVariable(self, key: str, value: str) -> None:
579
+ '''
580
+ Changes a startup variable.
581
+
582
+ Example:
583
+ server.updateStartupVariable('MINECRAFT_VERSION', '1.21.1')
584
+ '''
585
+ return self.api.updateStartupVariables(self.server_id, [{'env_variable': key, 'value': str(value)}])
586
+
587
+ def updateStartupVariables(self, variables: dict) -> None:
588
+ '''Changes several startup variables at once, given as {env_variable: value}.'''
589
+ return self.api.updateStartupVariables(
590
+ self.server_id, [{'env_variable': k, 'value': str(v)} for k, v in variables.items()]
591
+ )
592
+
593
+ def setStartupCommand(self, command: str) -> None:
594
+ '''Changes the startup command.'''
595
+ return self.api.setStartupCommand(self.server_id, command)
596
+
597
+ def setDockerImage(self, image: str) -> None:
598
+ '''Changes the docker image, see getDockerImages() for allowed values.'''
599
+ return self.api.setDockerImage(self.server_id, image)
600
+
601
+
602
+ # mounts and devices
603
+ def getMounts(self, search: str = None, page: int = None, per_page: int = None):
604
+ '''Returns the mounts available to this server.'''
605
+ return self.panel.api.attachments.mounts(self.server_id, search, page, per_page)
606
+
607
+ def attachMount(self, mount) -> None:
608
+ '''Attaches a mount (UUID or dict) to the server.'''
609
+ return self.panel.api.attachments.attachMount(self.server_id, _uuid(mount))
610
+
611
+ def detachMount(self, mount) -> None:
612
+ '''Detaches a mount from the server.'''
613
+ return self.panel.api.attachments.detachMount(self.server_id, _uuid(mount))
614
+
615
+ def getDevices(self, search: str = None, page: int = None, per_page: int = None):
616
+ '''Returns the devices available to this server.'''
617
+ return self.panel.api.attachments.devices(self.server_id, search, page, per_page)
618
+
619
+ def attachDevice(self, device) -> None:
620
+ '''Attaches a device (UUID or dict) to the server.'''
621
+ return self.panel.api.attachments.attachDevice(self.server_id, _uuid(device))
622
+
623
+ def detachDevice(self, device) -> None:
624
+ '''Detaches a device from the server.'''
625
+ return self.panel.api.attachments.detachDevice(self.server_id, _uuid(device))
626
+
627
+
628
+ # file manager shortcuts
629
+ def listFiles(self, directory: str = '/'):
630
+ '''Lists a directory. See `server.files.listFiles()`.'''
631
+ return self.files.listFiles(directory)
632
+
633
+ def readFile(self, path: str) -> str:
634
+ '''Returns a text file's contents. See `server.files.readFile()`.'''
635
+ return self.files.readFile(path)
636
+
637
+ def writeFile(self, path: str, content) -> None:
638
+ '''Creates or replaces a file. See `server.files.writeFile()`.'''
639
+ return self.files.writeFile(path, content)
640
+
641
+ def downloadFile(self, path: str, destination: str = None):
642
+ '''Downloads a file. See `server.files.downloadFile()`.'''
643
+ return self.files.downloadFile(path, destination)
644
+
645
+ def uploadFile(self, directory: str, file_data, name: str = None):
646
+ '''Uploads a file. See `server.files.uploadFile()`.'''
647
+ return self.files.uploadFile(directory, file_data, name)
648
+
649
+ def deleteFiles(self, root: str, files: list) -> None:
650
+ '''Deletes files from a directory. See `server.files.deleteFiles()`.'''
651
+ return self.files.deleteFiles(root, files)
652
+
653
+ def renameFiles(self, root: str, files: list) -> None:
654
+ '''Renames files. See `server.files.renameFiles()`.'''
655
+ return self.files.renameFiles(root, files)
656
+
657
+ def copyFiles(self, root: str, files: list):
658
+ '''Copies files. See `server.files.copyFiles()`.'''
659
+ return self.files.copyFiles(root, files)
660
+
661
+ def moveFiles(self, root: str, files: list) -> None:
662
+ '''Moves files. See `server.files.moveFiles()`.'''
663
+ return self.files.moveFiles(root, files)
664
+
665
+ def createFolder(self, directory: str, name: str) -> None:
666
+ '''Creates a directory. See `server.files.createFolder()`.'''
667
+ return self.files.createFolder(directory, name)
668
+
669
+ def compress(self, root: str, files: list, format: str = 'tar_gz', name: str = None):
670
+ '''Compresses files into an archive. See `server.files.compress()`.'''
671
+ return self.files.compress(root, files, format, name)
672
+
673
+ def decompress(self, file_path: str) -> None:
674
+ '''Extracts an archive. See `server.files.decompress()`.'''
675
+ return self.files.decompress(file_path)
676
+
677
+
678
+ # backup shortcuts
679
+ def listBackups(self):
680
+ '''Returns every backup. See `server.backups.getBackups()`.'''
681
+ return self.backups.getBackups()
682
+
683
+ def getBackups(self, search: str = None):
684
+ '''Returns every backup. See `server.backups.getBackups()`.'''
685
+ return self.backups.getBackups(search)
686
+
687
+ def getBackup(self, backup_id: str):
688
+ '''Returns one backup. See `server.backups.getBackup()`.'''
689
+ return self.backups.getBackup(backup_id)
690
+
691
+ def createBackup(self, name: str = None, ignored_files: list = None, group=None):
692
+ '''
693
+ Starts a new backup and returns it.
694
+
695
+ Example:
696
+ backup = server.createBackup('before-update')
697
+ backup.waitUntilComplete()
698
+ '''
699
+ return self.backups.createBackup(name, ignored_files, group)
700
+
701
+ def deleteBackup(self, backup_id: str) -> None:
702
+ '''Deletes a backup.'''
703
+ return self.backups.deleteBackup(backup_id)
704
+
705
+ def restoreBackup(self, backup_id: str, truncate_directory: bool = False) -> None:
706
+ '''Restores a backup. See `server.backups.restoreBackup()`.'''
707
+ return self.backups.restoreBackup(backup_id, truncate_directory)
708
+
709
+ def downloadBackup(self, backup_id: str, destination: str = None):
710
+ '''Downloads a backup. See `server.backups.downloadBackup()`.'''
711
+ return self.backups.downloadBackup(backup_id, destination)
712
+
713
+
714
+ # database shortcuts
715
+ def listDatabases(self):
716
+ '''Returns every database. See `server.databases.getDatabases()`.'''
717
+ return self.databases.getDatabases()
718
+
719
+ def getDatabases(self, include_password: bool = False):
720
+ '''Returns every database. See `server.databases.getDatabases()`.'''
721
+ return self.databases.getDatabases(include_password=include_password)
722
+
723
+ def createDatabase(self, name: str, host=None):
724
+ '''Creates a database. See `server.databases.createDatabase()`.'''
725
+ return self.databases.createDatabase(name, host)
726
+
727
+ def rotateDatabasePassword(self, db_id: str) -> str:
728
+ '''Generates a new database password and returns it.'''
729
+ return self.databases.rotateDatabasePassword(db_id)
730
+
731
+ def deleteDatabase(self, db_id: str) -> None:
732
+ '''Deletes a database.'''
733
+ return self.databases.deleteDatabase(db_id)
734
+
735
+
736
+ # schedule shortcuts
737
+ def listSchedules(self):
738
+ '''Returns every schedule. See `server.schedules.getSchedules()`.'''
739
+ return self.schedules.getSchedules()
740
+
741
+ def getSchedules(self):
742
+ '''Returns every schedule. See `server.schedules.getSchedules()`.'''
743
+ return self.schedules.getSchedules()
744
+
745
+ def getSchedule(self, schedule_id: str):
746
+ '''Returns a schedule by UUID or name. See `server.schedules.getSchedule()`.'''
747
+ return self.schedules.getSchedule(schedule_id)
748
+
749
+ def createSchedule(self, name: str, triggers: list = None, condition: dict = None, steps: list = None, enabled: bool = True):
750
+ '''Creates a schedule. See `server.schedules.createSchedule()`.'''
751
+ return self.schedules.createSchedule(name, triggers, condition, steps, enabled)
752
+
753
+ def updateSchedule(self, schedule_id: str, **fields) -> None:
754
+ '''Updates a schedule (name, enabled, triggers, condition).'''
755
+ return self.schedules.updateSchedule(schedule_id, **fields)
756
+
757
+ def deleteSchedule(self, schedule_id: str) -> None:
758
+ '''Deletes a schedule.'''
759
+ return self.schedules.deleteSchedule(schedule_id)
760
+
761
+ def runSchedule(self, schedule_id: str) -> None:
762
+ '''Runs a schedule now.'''
763
+ return self.schedules.runSchedule(schedule_id)
764
+
765
+ def enableSchedule(self, schedule_id: str) -> None:
766
+ '''Enables a schedule.'''
767
+ return self.schedules.enableSchedule(schedule_id)
768
+
769
+ def disableSchedule(self, schedule_id: str) -> None:
770
+ '''Disables a schedule.'''
771
+ return self.schedules.disableSchedule(schedule_id)
772
+
773
+
774
+ # other shortcuts
775
+ def getSubusers(self):
776
+ '''Returns every subuser. See `server.users.getSubusers()`.'''
777
+ return self.users.getSubusers()
778
+
779
+ def getAllocations(self):
780
+ '''Returns every allocation. See `server.network.getAllocations()`.'''
781
+ return self.network.getAllocations()
782
+
783
+
784
+ def _uuid(value):
785
+ return value.get('uuid') if isinstance(value, dict) else idOf(value)