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,292 @@
1
+ import time
2
+
3
+ from calagoapi.utils.model import Model, idOf
4
+ from calagoapi.utils.convert import formatBytes
5
+ from calagoapi.utils.exceptions import WaitTimeoutError
6
+
7
+
8
+ class Backup(Model):
9
+ '''
10
+ A server backup.
11
+
12
+ Returned by `server.createBackup()`, `server.backups.getBackups()` and friends.
13
+
14
+ Args:
15
+ server: The server the backup belongs to.
16
+ data: Raw backup data from Calagopus.
17
+
18
+ Example:
19
+ backup = server.createBackup('before-update')
20
+ backup.waitUntilComplete()
21
+ if backup.isSuccessful():
22
+ print(backup.getName(), backup.getSize(formatted=True))
23
+ '''
24
+ def __init__(self, server, data: dict):
25
+ super().__init__(server.panel, data)
26
+ self.server = server
27
+
28
+ @property
29
+ def backup_id(self):
30
+ return self.getUUID()
31
+
32
+ # getters
33
+ def getName(self) -> str:
34
+ '''Returns the backup name.'''
35
+ return self.data.get('name')
36
+
37
+ def getSize(self, formatted: bool = False):
38
+ '''
39
+ Returns the backup size in bytes.
40
+
41
+ Args:
42
+ formatted: Return a human readable string like "1.2 GiB" instead.
43
+ '''
44
+ size = self.data.get('bytes')
45
+ return formatBytes(size) if formatted else size
46
+
47
+ def getFileCount(self) -> int:
48
+ '''Returns how many files the backup contains.'''
49
+ return self.data.get('files')
50
+
51
+ def getChecksum(self) -> str:
52
+ '''Returns the backup checksum, once completed.'''
53
+ return self.data.get('checksum')
54
+
55
+ def getKind(self) -> str:
56
+ '''Returns "server" for file backups or "database_instance" for database backups.'''
57
+ return self.data.get('kind')
58
+
59
+ def getDatabaseType(self) -> str:
60
+ '''Returns the database type for database backups, otherwise None.'''
61
+ return self.data.get('database_type')
62
+
63
+ def getGroupId(self) -> str:
64
+ '''Returns the UUID of the backup group this backup is in, or None.'''
65
+ return self.data.get('backup_group_uuid')
66
+
67
+ def getIgnoredFiles(self) -> list:
68
+ '''Returns the ignore patterns used when creating the backup.'''
69
+ return self.data.get('ignored_files', [])
70
+
71
+ def getCompleted(self) -> str:
72
+ '''Returns when the backup finished, or None while it is running.'''
73
+ return self.data.get('completed')
74
+
75
+ def getStatus(self) -> str:
76
+ '''
77
+ Returns the backup status.
78
+
79
+ Returns:
80
+ 'in_progress', 'successful' or 'failed'.
81
+ '''
82
+ if not self.data.get('completed'):
83
+ return 'in_progress'
84
+ return 'successful' if self.data.get('is_successful') else 'failed'
85
+
86
+ # state checks
87
+ def isCompleted(self) -> bool:
88
+ '''Returns True once the backup has finished (successfully or not).'''
89
+ return self.getStatus() != 'in_progress'
90
+
91
+ def isInProgress(self) -> bool:
92
+ '''Returns True while the backup is still being created.'''
93
+ return self.getStatus() == 'in_progress'
94
+
95
+ def isSuccessful(self) -> bool:
96
+ '''Returns True if the backup completed successfully.'''
97
+ return self.getStatus() == 'successful'
98
+
99
+ def isFailed(self) -> bool:
100
+ '''Returns True if the backup completed but failed.'''
101
+ return self.getStatus() == 'failed'
102
+
103
+ def isLocked(self) -> bool:
104
+ '''Returns True if the backup is locked against deletion.'''
105
+ return bool(self.data.get('is_locked'))
106
+
107
+ def isBrowsable(self) -> bool:
108
+ '''Returns True if the backup contents can be browsed.'''
109
+ return bool(self.data.get('is_browsable'))
110
+
111
+ def isStreaming(self) -> bool:
112
+ '''Returns True if the backup can be streamed during download.'''
113
+ return bool(self.data.get('is_streaming'))
114
+
115
+ # actions
116
+ def refresh(self) -> 'Backup':
117
+ '''Re-fetches the backup from the panel.'''
118
+ self.data = self.panel.api.backups.get(self.server.server_id, self.backup_id)
119
+ return self
120
+
121
+ def waitUntilComplete(self, timeout: int = 3600, interval: int = 5) -> 'Backup':
122
+ '''
123
+ Blocks until the backup finishes.
124
+
125
+ Args:
126
+ timeout: Maximum seconds to wait.
127
+ interval: Seconds between checks.
128
+
129
+ Raises:
130
+ WaitTimeoutError: The backup did not finish in time.
131
+ '''
132
+ deadline = time.monotonic() + timeout
133
+ while self.refresh().isInProgress():
134
+ if time.monotonic() >= deadline:
135
+ raise WaitTimeoutError(f'Backup {self.getName()!r} did not finish within {timeout}s')
136
+ time.sleep(interval)
137
+ return self
138
+
139
+ def restore(self, truncate_directory: bool = False, restore_startup: bool = None, database_instance=None) -> None:
140
+ '''
141
+ Restores the server from this backup.
142
+
143
+ Args:
144
+ truncate_directory: Delete all current files before restoring.
145
+ restore_startup: Also restore the startup command and variables.
146
+ database_instance: For database backups, the instance to restore into.
147
+
148
+ Example:
149
+ backup.restore(truncate_directory=True)
150
+ '''
151
+ return self.panel.api.backups.restore(
152
+ self.server.server_id, self.backup_id, truncate_directory, restore_startup, idOf(database_instance)
153
+ )
154
+
155
+ def rename(self, name: str) -> 'Backup':
156
+ '''Renames the backup.'''
157
+ self.panel.api.backups.update(self.server.server_id, self.backup_id, name=name)
158
+ return self._update({'name': name})
159
+
160
+ def setName(self, name: str) -> 'Backup':
161
+ '''Alias of rename().'''
162
+ return self.rename(name)
163
+
164
+ def lock(self) -> 'Backup':
165
+ '''Locks the backup so it cannot be deleted.'''
166
+ self.panel.api.backups.update(self.server.server_id, self.backup_id, locked=True)
167
+ return self._update({'is_locked': True})
168
+
169
+ def unlock(self) -> 'Backup':
170
+ '''Unlocks the backup.'''
171
+ self.panel.api.backups.update(self.server.server_id, self.backup_id, locked=False)
172
+ return self._update({'is_locked': False})
173
+
174
+ def moveToGroup(self, group) -> 'Backup':
175
+ '''
176
+ Moves the backup into a backup group.
177
+
178
+ Args:
179
+ group: A BackupGroup or group UUID.
180
+ '''
181
+ group_id = idOf(group)
182
+ self.panel.api.backups.update(self.server.server_id, self.backup_id, backup_group_uuid=group_id)
183
+ return self._update({'backup_group_uuid': group_id})
184
+
185
+ def getDownloadUrl(self, archive_format: str = None) -> str:
186
+ '''
187
+ Returns a signed, temporary download URL.
188
+
189
+ Args:
190
+ archive_format: Optional archive format, e.g. 'tar_gz' or 'zip'.
191
+ '''
192
+ return self.panel.api.backups.downloadUrl(self.server.server_id, self.backup_id, archive_format)
193
+
194
+ def download(self, destination: str = None, archive_format: str = None):
195
+ '''
196
+ Downloads the backup.
197
+
198
+ Args:
199
+ destination: Local file to save to. If omitted the bytes are returned.
200
+ archive_format: Optional archive format.
201
+
202
+ Example:
203
+ backup.download('backup.tar.gz')
204
+ '''
205
+ r = self.panel.api.files.fetch(self.getDownloadUrl(archive_format), stream=destination is not None)
206
+ if destination is None:
207
+ return r.content
208
+ with open(destination, 'wb') as f:
209
+ for chunk in r.iter_content(chunk_size=1024 * 1024):
210
+ f.write(chunk)
211
+ return destination
212
+
213
+ def exportToServer(self, path: str, archive_format: str = None, foreground: bool = None):
214
+ '''
215
+ Writes the backup as an archive into the server's own file system.
216
+
217
+ Args:
218
+ path: Destination path on the server, e.g. "/exports/backup.tar.gz".
219
+ '''
220
+ return self.panel.api.backups.export(self.server.server_id, self.backup_id, path, archive_format, foreground)
221
+
222
+ def getArchiveInfo(self) -> dict:
223
+ '''Returns the archive format and size the panel would serve for download.'''
224
+ return self.panel.api.backups.query(self.server.server_id, self.backup_id)
225
+
226
+ def delete(self) -> None:
227
+ '''Deletes the backup.'''
228
+ return self.panel.api.backups.delete(self.server.server_id, self.backup_id)
229
+
230
+
231
+ class BackupGroup(Model):
232
+ '''
233
+ A named group of backups with its own retention policy.
234
+
235
+ Example:
236
+ group = server.backups.createBackupGroup('daily', retention={'daily': 7})
237
+ backup = server.createBackup('nightly', group=group)
238
+ '''
239
+ def __init__(self, server, data: dict):
240
+ super().__init__(server.panel, data)
241
+ self.server = server
242
+
243
+ def getName(self) -> str:
244
+ '''Returns the group name.'''
245
+ return self.data.get('name')
246
+
247
+ def getOrder(self) -> int:
248
+ '''Returns the display order.'''
249
+ return self.data.get('order')
250
+
251
+ def getRetention(self) -> dict:
252
+ '''Returns the retention policy (count, days, daily, weekly, monthly, yearly).'''
253
+ return self.data.get('retention', {})
254
+
255
+ def getTotalBackups(self) -> int:
256
+ '''Returns the number of backups in the group.'''
257
+ return self.data.get('total_backups')
258
+
259
+ def getUsableBackups(self) -> int:
260
+ '''Returns the number of successful backups in the group.'''
261
+ return self.data.get('usable_backups')
262
+
263
+ def getBackups(self, search: str = None, page: int = None, per_page: int = None):
264
+ '''Returns the backups in this group.'''
265
+ return self.panel.api.backups.groupBackups(
266
+ self.server.server_id, self.getUUID(), search, page, per_page
267
+ ).map(lambda data: Backup(self.server, data))
268
+
269
+ def rename(self, name: str) -> 'BackupGroup':
270
+ '''Renames the group.'''
271
+ self.panel.api.backups.updateGroup(self.server.server_id, self.getUUID(), name=name)
272
+ return self._update({'name': name})
273
+
274
+ def setRetention(self, count: int = 0, days: int = 0, daily: int = 0, weekly: int = 0, monthly: int = 0, yearly: int = 0) -> 'BackupGroup':
275
+ '''
276
+ Sets how many backups the group keeps. 0 means unlimited.
277
+
278
+ Example:
279
+ group.setRetention(daily=7, weekly=4)
280
+ '''
281
+ retention = {'count': count, 'days': days, 'daily': daily, 'weekly': weekly, 'monthly': monthly, 'yearly': yearly}
282
+ self.panel.api.backups.updateGroup(self.server.server_id, self.getUUID(), retention=retention)
283
+ return self._update({'retention': retention})
284
+
285
+ def delete(self, lock_backups: bool = None) -> None:
286
+ '''
287
+ Deletes the group.
288
+
289
+ Args:
290
+ lock_backups: Lock the group's backups so they survive without a retention policy.
291
+ '''
292
+ return self.panel.api.backups.deleteGroup(self.server.server_id, self.getUUID(), lock_backups)
@@ -0,0 +1,177 @@
1
+ from calagoapi.backups.backup import Backup, BackupGroup
2
+ from calagoapi.utils.model import idOf
3
+
4
+
5
+ class BackupManager:
6
+ '''
7
+ Backup management for one server, available as `server.backups`.
8
+
9
+ Args:
10
+ server: The server to manage backups for.
11
+
12
+ Example:
13
+ backup = server.backups.createBackup('pre-update')
14
+ for backup in server.backups.getBackups():
15
+ print(backup.getName(), backup.getStatus())
16
+ '''
17
+ def __init__(self, server):
18
+ self.server = server
19
+ self.api = server.panel.api.backups
20
+
21
+ @property
22
+ def server_id(self):
23
+ return self.server.server_id
24
+
25
+ def _wrap(self, data):
26
+ return Backup(self.server, data)
27
+
28
+ # backups
29
+ def getBackups(self, search: str = None, kind: str = None, ungrouped: bool = None, page: int = None, per_page: int = None):
30
+ '''
31
+ Lists the server's backups.
32
+
33
+ Args:
34
+ search: Filter by name.
35
+ kind: 'server' for file backups or 'database_instance' for database backups.
36
+ ungrouped: Only backups that are not in a backup group.
37
+ page: Only fetch this page. By default every backup is returned.
38
+ per_page: Page size.
39
+
40
+ Returns:
41
+ A Page (list) of Backup objects.
42
+ '''
43
+ return self.api.list(self.server_id, search, ungrouped, kind, page=page, per_page=per_page).map(self._wrap)
44
+
45
+ def getSystemBackups(self, search: str = None, page: int = None, per_page: int = None):
46
+ '''Lists backups made by panel-wide backup policies rather than by the server's users.'''
47
+ return self.api.system(self.server_id, search, page, per_page).map(self._wrap)
48
+
49
+ def getBackup(self, backup_id: str) -> Backup:
50
+ '''
51
+ Returns a single backup.
52
+
53
+ Example:
54
+ backup = server.backups.getBackup('5f0c...')
55
+ '''
56
+ return self._wrap(self.api.get(self.server_id, backup_id))
57
+
58
+ def getLatestBackup(self, successful_only: bool = True) -> Backup:
59
+ '''
60
+ Returns the most recent backup, or None.
61
+
62
+ Args:
63
+ successful_only: Skip backups that failed or are still running.
64
+ '''
65
+ backups = [b for b in self.getBackups() if b.isSuccessful() or not successful_only]
66
+ return max(backups, key=lambda b: b.getCreated() or '', default=None)
67
+
68
+ def createBackup(self, name: str = None, ignored_files: list = None, group=None, database_instance=None) -> Backup:
69
+ '''
70
+ Starts a new backup.
71
+
72
+ The backup runs in the background, use `backup.waitUntilComplete()` to wait for it.
73
+
74
+ Args:
75
+ name: Backup name. Calagopus generates one when omitted.
76
+ ignored_files: Extra ignore patterns, e.g. ['logs/*', '*.tmp'].
77
+ group: BackupGroup or group UUID to put the backup in.
78
+ database_instance: Back up a database instance instead of the server files.
79
+
80
+ Returns:
81
+ The new Backup.
82
+
83
+ Example:
84
+ backup = server.backups.createBackup('before-update', ignored_files=['logs'])
85
+ '''
86
+ return self._wrap(self.api.create(self.server_id, name, ignored_files, idOf(group), idOf(database_instance)))
87
+
88
+ def deleteBackup(self, backup_id: str) -> None:
89
+ '''Deletes a backup.'''
90
+ return self.api.delete(self.server_id, idOf(backup_id))
91
+
92
+ def deleteBackups(self, backup_ids: list) -> dict:
93
+ '''
94
+ Deletes several backups at once.
95
+
96
+ Returns:
97
+ dict with how many were queued and skipped.
98
+ '''
99
+ return self.api.deleteMany(self.server_id, [idOf(b) for b in backup_ids])
100
+
101
+ def lockBackups(self, backup_ids: list) -> dict:
102
+ '''Locks several backups at once.'''
103
+ return self.api.updateMany(self.server_id, [idOf(b) for b in backup_ids], locked=True)
104
+
105
+ def unlockBackups(self, backup_ids: list) -> dict:
106
+ '''Unlocks several backups at once.'''
107
+ return self.api.updateMany(self.server_id, [idOf(b) for b in backup_ids], locked=False)
108
+
109
+ def moveBackups(self, backup_ids: list, group) -> dict:
110
+ '''Moves several backups into a backup group.'''
111
+ return self.api.updateMany(self.server_id, [idOf(b) for b in backup_ids], backup_group_uuid=idOf(group))
112
+
113
+ def restoreBackup(self, backup_id: str, truncate_directory: bool = False, restore_startup: bool = None) -> None:
114
+ '''
115
+ Restores the server from a backup.
116
+
117
+ Args:
118
+ backup_id: Backup to restore.
119
+ truncate_directory: Delete all current files first.
120
+ restore_startup: Also restore the startup command and variables.
121
+ '''
122
+ return self.api.restore(self.server_id, idOf(backup_id), truncate_directory, restore_startup)
123
+
124
+ def downloadBackup(self, backup_id: str, destination: str = None, archive_format: str = None):
125
+ '''
126
+ Downloads a backup.
127
+
128
+ Args:
129
+ destination: Local path to save to. If omitted the bytes are returned.
130
+ archive_format: Optional archive format.
131
+ '''
132
+ return Backup(self.server, {'uuid': idOf(backup_id)}).download(destination, archive_format)
133
+
134
+ def getDownloadUrl(self, backup_id: str, archive_format: str = None) -> str:
135
+ '''Returns a signed, temporary download URL for a backup.'''
136
+ return self.api.downloadUrl(self.server_id, idOf(backup_id), archive_format)
137
+
138
+ def getUsage(self) -> dict:
139
+ '''Returns how much backup storage and how many backup slots are in use.'''
140
+ return self.api.usage(self.server_id)
141
+
142
+ def unlockRestore(self) -> None:
143
+ '''Clears a stuck backup-restore lock on the server.'''
144
+ return self.api.unlock(self.server_id)
145
+
146
+ # groups
147
+ def getBackupGroups(self) -> list:
148
+ '''Returns the server's backup groups.'''
149
+ return [BackupGroup(self.server, data) for data in self.api.groups(self.server_id)]
150
+
151
+ def getBackupGroup(self, group_id_or_name: str) -> BackupGroup:
152
+ '''Returns a backup group by UUID or name, or None.'''
153
+ for group in self.getBackupGroups():
154
+ if group_id_or_name in (group.getUUID(), group.getName()):
155
+ return group
156
+ return None
157
+
158
+ def createBackupGroup(self, name: str, retention: dict = None) -> BackupGroup:
159
+ '''
160
+ Creates a backup group.
161
+
162
+ Args:
163
+ name: Group name.
164
+ retention: Optional retention policy with keys count, days, daily, weekly, monthly, yearly.
165
+
166
+ Example:
167
+ server.backups.createBackupGroup('weekly', retention={'weekly': 4})
168
+ '''
169
+ return BackupGroup(self.server, self.api.createGroup(self.server_id, name, retention))
170
+
171
+ def deleteBackupGroup(self, group, lock_backups: bool = None) -> None:
172
+ '''Deletes a backup group.'''
173
+ return self.api.deleteGroup(self.server_id, idOf(group), lock_backups)
174
+
175
+ def orderBackupGroups(self, groups: list) -> None:
176
+ '''Sets the display order of backup groups.'''
177
+ return self.api.orderGroups(self.server_id, [idOf(g) for g in groups])
@@ -0,0 +1,6 @@
1
+ from .console import Console, ConsoleEvent
2
+
3
+ __all__ = [
4
+ 'Console',
5
+ 'ConsoleEvent',
6
+ ]