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.
- calagoapi/__init__.py +190 -0
- calagoapi/account/__init__.py +14 -0
- calagoapi/account/account.py +292 -0
- calagoapi/account/auth.py +72 -0
- calagoapi/account/models.py +221 -0
- calagoapi/admin/__init__.py +48 -0
- calagoapi/admin/admin.py +341 -0
- calagoapi/admin/managers.py +490 -0
- calagoapi/admin/resources.py +970 -0
- calagoapi/aio/__init__.py +9 -0
- calagoapi/aio/console.py +289 -0
- calagoapi/aio/panel.py +176 -0
- calagoapi/api/__init__.py +63 -0
- calagoapi/api/account.py +195 -0
- calagoapi/api/admin.py +402 -0
- calagoapi/api/attachments.py +27 -0
- calagoapi/api/auth.py +52 -0
- calagoapi/api/backups.py +99 -0
- calagoapi/api/client.py +198 -0
- calagoapi/api/databases.py +217 -0
- calagoapi/api/files.py +210 -0
- calagoapi/api/network.py +84 -0
- calagoapi/api/public.py +13 -0
- calagoapi/api/schedules.py +79 -0
- calagoapi/api/servers.py +114 -0
- calagoapi/api/users.py +27 -0
- calagoapi/api/versions.py +29 -0
- calagoapi/backups/__init__.py +8 -0
- calagoapi/backups/backup.py +292 -0
- calagoapi/backups/manager.py +177 -0
- calagoapi/console/__init__.py +6 -0
- calagoapi/console/console.py +392 -0
- calagoapi/databases/__init__.py +12 -0
- calagoapi/databases/database.py +445 -0
- calagoapi/databases/explorer.py +159 -0
- calagoapi/databases/manager.py +122 -0
- calagoapi/files/__init__.py +9 -0
- calagoapi/files/file.py +196 -0
- calagoapi/files/manager.py +437 -0
- calagoapi/network/__init__.py +8 -0
- calagoapi/network/allocation.py +99 -0
- calagoapi/network/manager.py +132 -0
- calagoapi/nodes/__init__.py +6 -0
- calagoapi/nodes/node.py +477 -0
- calagoapi/panel.py +162 -0
- calagoapi/schedules/__init__.py +14 -0
- calagoapi/schedules/builders.py +461 -0
- calagoapi/schedules/manager.py +107 -0
- calagoapi/schedules/schedule.py +214 -0
- calagoapi/servers/__init__.py +13 -0
- calagoapi/servers/admin.py +430 -0
- calagoapi/servers/generic.py +785 -0
- calagoapi/servers/manager.py +167 -0
- calagoapi/servers/minecraft.py +207 -0
- calagoapi/tunnels/__init__.py +5 -0
- calagoapi/tunnels/tunnel.py +112 -0
- calagoapi/users/__init__.py +11 -0
- calagoapi/users/activity.py +45 -0
- calagoapi/users/subuser.py +127 -0
- calagoapi/users/user.py +267 -0
- calagoapi/utils/__init__.py +43 -0
- calagoapi/utils/convert.py +113 -0
- calagoapi/utils/exceptions.py +72 -0
- calagoapi/utils/model.py +142 -0
- calagoapi/utils/resource.py +100 -0
- calagoapi/utils/time.py +68 -0
- calagoapi-1.0.dev1.dist-info/METADATA +266 -0
- calagoapi-1.0.dev1.dist-info/RECORD +71 -0
- calagoapi-1.0.dev1.dist-info/WHEEL +5 -0
- calagoapi-1.0.dev1.dist-info/licenses/LICENSE +674 -0
- calagoapi-1.0.dev1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,970 @@
|
|
|
1
|
+
from calagoapi.utils.resource import AdminResource
|
|
2
|
+
from calagoapi.utils.model import idOf
|
|
3
|
+
from calagoapi.utils.exceptions import InvalidActionError
|
|
4
|
+
from calagoapi.nodes.node import Node
|
|
5
|
+
from calagoapi.users.user import User
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Location(AdminResource):
|
|
9
|
+
'''
|
|
10
|
+
A location groups nodes, e.g. by region.
|
|
11
|
+
|
|
12
|
+
Example:
|
|
13
|
+
location = panel.admin.locations.createLocation('Frankfurt', flag='de')
|
|
14
|
+
print([node.getName() for node in location.getNodes()])
|
|
15
|
+
'''
|
|
16
|
+
collection = 'locations'
|
|
17
|
+
|
|
18
|
+
def duplicate(self, name: str) -> 'Location':
|
|
19
|
+
'''Creates a copy under a new name.'''
|
|
20
|
+
return self._duplicate(name=name)
|
|
21
|
+
|
|
22
|
+
def getFlag(self) -> str:
|
|
23
|
+
'''Returns the country flag code.'''
|
|
24
|
+
return self.data.get('flag')
|
|
25
|
+
|
|
26
|
+
def getBackupConfiguration(self) -> dict:
|
|
27
|
+
'''Returns the default backup configuration of the location.'''
|
|
28
|
+
return self.data.get('backup_configuration')
|
|
29
|
+
|
|
30
|
+
def getNodes(self, search: str = None, page: int = None, per_page: int = None):
|
|
31
|
+
'''Returns the nodes in this location.'''
|
|
32
|
+
return self._api().related(self.getUUID(), 'nodes', 'nodes', search, page, per_page).map(lambda d: Node(self.panel, d))
|
|
33
|
+
|
|
34
|
+
def getDatabaseHosts(self, search: str = None, page: int = None, per_page: int = None):
|
|
35
|
+
'''Returns database hosts linked to this location.'''
|
|
36
|
+
return self._api().related(self.getUUID(), 'database-hosts', 'database_hosts', search, page, per_page)
|
|
37
|
+
|
|
38
|
+
def attachDatabaseHost(self, database_host) -> None:
|
|
39
|
+
'''Links a database host to this location.'''
|
|
40
|
+
return self._api().attach(self.getUUID(), 'database-hosts', 'database_host_uuid', idOf(database_host))
|
|
41
|
+
|
|
42
|
+
def detachDatabaseHost(self, database_host) -> None:
|
|
43
|
+
'''Unlinks a database host.'''
|
|
44
|
+
return self._api().detach(self.getUUID(), 'database-hosts', idOf(database_host))
|
|
45
|
+
|
|
46
|
+
def getDatabaseAgentHosts(self, search: str = None, page: int = None, per_page: int = None):
|
|
47
|
+
'''Returns database agent hosts linked to this location.'''
|
|
48
|
+
return self._api().related(self.getUUID(), 'database-agent-hosts', 'database_agent_hosts', search, page, per_page)
|
|
49
|
+
|
|
50
|
+
def attachDatabaseAgentHost(self, host) -> None:
|
|
51
|
+
'''Links a database agent host to this location.'''
|
|
52
|
+
return self._api().attach(self.getUUID(), 'database-agent-hosts', 'database_agent_host_uuid', idOf(host))
|
|
53
|
+
|
|
54
|
+
def detachDatabaseAgentHost(self, host) -> None:
|
|
55
|
+
'''Unlinks a database agent host.'''
|
|
56
|
+
return self._api().detach(self.getUUID(), 'database-agent-hosts', idOf(host))
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class Nest(AdminResource):
|
|
60
|
+
'''
|
|
61
|
+
A nest is a category of eggs (server types).
|
|
62
|
+
|
|
63
|
+
Example:
|
|
64
|
+
nest = panel.admin.nests.getNestByName('Minecraft')
|
|
65
|
+
for egg in nest.getEggs():
|
|
66
|
+
print(egg.getName())
|
|
67
|
+
nest.importEggsFromUrls(['https://example.com/egg-paper.json'])
|
|
68
|
+
'''
|
|
69
|
+
collection = 'nests'
|
|
70
|
+
|
|
71
|
+
def getAuthor(self) -> str:
|
|
72
|
+
'''Returns the nest author.'''
|
|
73
|
+
return self.data.get('author')
|
|
74
|
+
|
|
75
|
+
def _eggs(self):
|
|
76
|
+
return self.panel.api.admin.eggs(self.getUUID())
|
|
77
|
+
|
|
78
|
+
def getEggs(self, search: str = None, page: int = None, per_page: int = None):
|
|
79
|
+
'''Returns the eggs in this nest.'''
|
|
80
|
+
return self._eggs().list(search, page, per_page).map(lambda d: Egg(self.panel, d, self._eggs()))
|
|
81
|
+
|
|
82
|
+
def getEgg(self, egg_id_or_name: str) -> 'Egg':
|
|
83
|
+
'''Returns an egg by UUID or name, or None.'''
|
|
84
|
+
for egg in self.getEggs():
|
|
85
|
+
if egg_id_or_name in (egg.getUUID(), egg.getName()):
|
|
86
|
+
return egg
|
|
87
|
+
return None
|
|
88
|
+
|
|
89
|
+
def createEgg(self, **fields) -> 'Egg':
|
|
90
|
+
'''
|
|
91
|
+
Creates an egg. Accepts every Calagopus egg field (name, author,
|
|
92
|
+
config_files, config_startup, config_stop, config_script, startup_commands,
|
|
93
|
+
docker_images, features, file_denylist, force_outgoing_ip, separate_port, ...).
|
|
94
|
+
'''
|
|
95
|
+
return Egg(self.panel, self._eggs().create(**fields), self._eggs())
|
|
96
|
+
|
|
97
|
+
def importEgg(self, data: dict) -> 'Egg':
|
|
98
|
+
'''Imports an egg from exported egg JSON (as a dict).'''
|
|
99
|
+
return Egg(self.panel, self._api().action(self.getUUID(), 'eggs/import', json=data)['egg'], self._eggs())
|
|
100
|
+
|
|
101
|
+
def importEggsFromUrls(self, urls: list) -> dict:
|
|
102
|
+
'''Imports eggs from URLs. Returns the created eggs and failures.'''
|
|
103
|
+
return self._api().action(self.getUUID(), 'eggs/import/url', json={'urls': urls})
|
|
104
|
+
|
|
105
|
+
def deleteEggs(self, eggs: list) -> int:
|
|
106
|
+
'''Deletes several eggs. Returns how many were deleted.'''
|
|
107
|
+
return self._api().action(self.getUUID(), 'eggs/delete', json={'egg_uuids': [idOf(e) for e in eggs]})['deleted']
|
|
108
|
+
|
|
109
|
+
def moveEggs(self, eggs: list, nest) -> int:
|
|
110
|
+
'''Moves eggs into another nest. Returns how many were moved.'''
|
|
111
|
+
return self._api().action(self.getUUID(), 'eggs/move', json={
|
|
112
|
+
'egg_uuids': [idOf(e) for e in eggs],
|
|
113
|
+
'destination_nest_uuid': idOf(nest),
|
|
114
|
+
})['moved']
|
|
115
|
+
|
|
116
|
+
def updateEggsFromRepository(self) -> int:
|
|
117
|
+
'''Updates every egg that came from an egg repository.'''
|
|
118
|
+
return self._api().action(self.getUUID(), 'eggs/update/repository')['updated']
|
|
119
|
+
|
|
120
|
+
def delete(self, delete_eggs: bool = False) -> None:
|
|
121
|
+
'''Deletes the nest, optionally together with its eggs.'''
|
|
122
|
+
return self._api().delete(self.getUUID(), delete_eggs=delete_eggs)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class Egg(AdminResource):
|
|
126
|
+
'''
|
|
127
|
+
An egg defines a server type: its install script, startup command, images and variables.
|
|
128
|
+
|
|
129
|
+
Example:
|
|
130
|
+
egg = nest.getEgg('Paper')
|
|
131
|
+
print(egg.getDockerImages())
|
|
132
|
+
egg.updateFromUrl('https://example.com/egg-paper.json')
|
|
133
|
+
'''
|
|
134
|
+
collection = None
|
|
135
|
+
|
|
136
|
+
def getNestId(self) -> str:
|
|
137
|
+
'''Returns the UUID of the nest the egg belongs to.'''
|
|
138
|
+
return self.resource.parent
|
|
139
|
+
|
|
140
|
+
def getAuthor(self) -> str:
|
|
141
|
+
'''Returns the egg author.'''
|
|
142
|
+
return self.data.get('author')
|
|
143
|
+
|
|
144
|
+
def getStartupCommands(self) -> dict:
|
|
145
|
+
'''Returns the startup commands as {name: command}.'''
|
|
146
|
+
return self.data.get('startup_commands', {})
|
|
147
|
+
|
|
148
|
+
def getDockerImages(self) -> dict:
|
|
149
|
+
'''Returns the docker images as {name: image}.'''
|
|
150
|
+
return self.data.get('docker_images', {})
|
|
151
|
+
|
|
152
|
+
def getFeatures(self) -> list:
|
|
153
|
+
'''Returns the egg features, e.g. ["eula", "java_version"].'''
|
|
154
|
+
return self.data.get('features', [])
|
|
155
|
+
|
|
156
|
+
def getFileDenylist(self) -> list:
|
|
157
|
+
'''Returns files users may not edit.'''
|
|
158
|
+
return self.data.get('file_denylist', [])
|
|
159
|
+
|
|
160
|
+
def _variables(self):
|
|
161
|
+
return self.panel.api.admin.eggVariables(self.getNestId(), self.getUUID())
|
|
162
|
+
|
|
163
|
+
def getVariables(self) -> list:
|
|
164
|
+
'''Returns the egg's variables.'''
|
|
165
|
+
return [EggVariable(self.panel, d, self._variables()) for d in self._variables().listAll()]
|
|
166
|
+
|
|
167
|
+
def createVariable(self, name: str, env_variable: str, default_value: str = '', description: str = None,
|
|
168
|
+
rules: list = None, user_viewable: bool = True, user_editable: bool = True,
|
|
169
|
+
secret: bool = False, order: int = None) -> 'EggVariable':
|
|
170
|
+
'''
|
|
171
|
+
Adds a variable.
|
|
172
|
+
|
|
173
|
+
Example:
|
|
174
|
+
egg.createVariable('Server Jar', 'SERVER_JARFILE', 'server.jar', rules=['required', 'string'])
|
|
175
|
+
'''
|
|
176
|
+
if order is None:
|
|
177
|
+
order = len(self.getVariables()) + 1
|
|
178
|
+
return EggVariable(self.panel, self._variables().create(
|
|
179
|
+
name=name,
|
|
180
|
+
env_variable=env_variable,
|
|
181
|
+
default_value=default_value,
|
|
182
|
+
description=description,
|
|
183
|
+
rules=rules or [],
|
|
184
|
+
user_viewable=user_viewable,
|
|
185
|
+
user_editable=user_editable,
|
|
186
|
+
secret=secret,
|
|
187
|
+
order=order,
|
|
188
|
+
name_translations={},
|
|
189
|
+
description_translations={},
|
|
190
|
+
), self._variables())
|
|
191
|
+
|
|
192
|
+
def orderVariables(self, variables: list) -> None:
|
|
193
|
+
'''Sets the variable order.'''
|
|
194
|
+
return self.panel.api.admin.orderEggVariables(self.getNestId(), self.getUUID(), [idOf(v) for v in variables])
|
|
195
|
+
|
|
196
|
+
def export(self) -> dict:
|
|
197
|
+
'''Exports the egg as JSON (dict).'''
|
|
198
|
+
return self._api().action(self.getUUID(), 'export', 'GET')
|
|
199
|
+
|
|
200
|
+
def updateFromImport(self, data: dict) -> None:
|
|
201
|
+
'''Overwrites the egg with exported egg JSON.'''
|
|
202
|
+
return self._api().action(self.getUUID(), 'update/import', json=data)
|
|
203
|
+
|
|
204
|
+
def updateFromUrl(self, url: str) -> None:
|
|
205
|
+
'''Overwrites the egg with egg JSON downloaded from a URL.'''
|
|
206
|
+
return self._api().action(self.getUUID(), 'update/import/url', json={'url': url})
|
|
207
|
+
|
|
208
|
+
def updateFromRepository(self) -> None:
|
|
209
|
+
'''Updates the egg from its egg repository.'''
|
|
210
|
+
return self._api().action(self.getUUID(), 'update/repository')
|
|
211
|
+
|
|
212
|
+
def move(self, nest) -> None:
|
|
213
|
+
'''Moves the egg into another nest.'''
|
|
214
|
+
return self._api().action(self.getUUID(), 'move', json={'destination_nest_uuid': idOf(nest)})
|
|
215
|
+
|
|
216
|
+
def duplicate(self, name: str, nest=None) -> 'Egg':
|
|
217
|
+
'''Copies the egg, into `nest` or the same nest.'''
|
|
218
|
+
return self._duplicate(name=name, nest_uuid=idOf(nest) or self.getNestId())
|
|
219
|
+
|
|
220
|
+
def getServers(self, search: str = None, page: int = None, per_page: int = None):
|
|
221
|
+
'''Returns servers using this egg.'''
|
|
222
|
+
return self._api().related(self.getUUID(), 'servers', 'servers', search, page, per_page)
|
|
223
|
+
|
|
224
|
+
def getMounts(self, search: str = None, page: int = None, per_page: int = None):
|
|
225
|
+
'''Returns mounts attached to this egg.'''
|
|
226
|
+
return self._api().related(self.getUUID(), 'mounts', 'mounts', search, page, per_page)
|
|
227
|
+
|
|
228
|
+
def attachMount(self, mount) -> None:
|
|
229
|
+
'''Attaches a mount to the egg.'''
|
|
230
|
+
return self._api().attach(self.getUUID(), 'mounts', 'mount_uuid', idOf(mount))
|
|
231
|
+
|
|
232
|
+
def detachMount(self, mount) -> None:
|
|
233
|
+
'''Detaches a mount from the egg.'''
|
|
234
|
+
return self._api().detach(self.getUUID(), 'mounts', idOf(mount))
|
|
235
|
+
|
|
236
|
+
def getDevices(self, search: str = None, page: int = None, per_page: int = None):
|
|
237
|
+
'''Returns devices attached to this egg.'''
|
|
238
|
+
return self._api().related(self.getUUID(), 'devices', 'devices', search, page, per_page)
|
|
239
|
+
|
|
240
|
+
def attachDevice(self, device) -> None:
|
|
241
|
+
'''Attaches a device to the egg.'''
|
|
242
|
+
return self._api().attach(self.getUUID(), 'devices', 'device_uuid', idOf(device))
|
|
243
|
+
|
|
244
|
+
def detachDevice(self, device) -> None:
|
|
245
|
+
'''Detaches a device from the egg.'''
|
|
246
|
+
return self._api().detach(self.getUUID(), 'devices', idOf(device))
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
class EggVariable(AdminResource):
|
|
250
|
+
'''
|
|
251
|
+
A configurable variable of an egg.
|
|
252
|
+
|
|
253
|
+
Example:
|
|
254
|
+
for variable in egg.getVariables():
|
|
255
|
+
print(variable.getEnvVariable(), variable.getDefaultValue())
|
|
256
|
+
'''
|
|
257
|
+
collection = None
|
|
258
|
+
|
|
259
|
+
def getEnvVariable(self) -> str:
|
|
260
|
+
'''Returns the environment variable name, e.g. "SERVER_JARFILE".'''
|
|
261
|
+
return self.data.get('env_variable')
|
|
262
|
+
|
|
263
|
+
def getDefaultValue(self) -> str:
|
|
264
|
+
'''Returns the default value.'''
|
|
265
|
+
return self.data.get('default_value')
|
|
266
|
+
|
|
267
|
+
def getRules(self) -> list:
|
|
268
|
+
'''Returns the validation rules.'''
|
|
269
|
+
return self.data.get('rules', [])
|
|
270
|
+
|
|
271
|
+
def isUserViewable(self) -> bool:
|
|
272
|
+
'''Returns True if users can see the variable.'''
|
|
273
|
+
return bool(self.data.get('user_viewable'))
|
|
274
|
+
|
|
275
|
+
def isUserEditable(self) -> bool:
|
|
276
|
+
'''Returns True if users can change the variable.'''
|
|
277
|
+
return bool(self.data.get('user_editable'))
|
|
278
|
+
|
|
279
|
+
def isSecret(self) -> bool:
|
|
280
|
+
'''Returns True if the value is hidden.'''
|
|
281
|
+
return bool(self.data.get('secret'))
|
|
282
|
+
|
|
283
|
+
def refresh(self) -> 'EggVariable':
|
|
284
|
+
'''Re-fetches the variable from the panel.'''
|
|
285
|
+
for data in self._api().listAll():
|
|
286
|
+
if data.get('uuid') == self.getUUID():
|
|
287
|
+
self.data = data
|
|
288
|
+
return self
|
|
289
|
+
|
|
290
|
+
def duplicate(self, name: str, env_variable: str) -> 'EggVariable':
|
|
291
|
+
'''Copies the variable under a new name and environment variable.'''
|
|
292
|
+
return self._duplicate(name=name, env_variable=env_variable)
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
class Mount(AdminResource):
|
|
296
|
+
'''
|
|
297
|
+
A host directory that can be mounted into servers.
|
|
298
|
+
|
|
299
|
+
Example:
|
|
300
|
+
mount = panel.admin.mounts.createMount('Shared maps', '/srv/maps', '/home/container/maps', read_only=True)
|
|
301
|
+
node.attachMount(mount)
|
|
302
|
+
'''
|
|
303
|
+
collection = 'mounts'
|
|
304
|
+
|
|
305
|
+
def getSource(self) -> str:
|
|
306
|
+
'''Returns the path on the node.'''
|
|
307
|
+
return self.data.get('source')
|
|
308
|
+
|
|
309
|
+
def getTarget(self) -> str:
|
|
310
|
+
'''Returns the path inside the server.'''
|
|
311
|
+
return self.data.get('target')
|
|
312
|
+
|
|
313
|
+
def isReadOnly(self) -> bool:
|
|
314
|
+
'''Returns True if the mount is read-only.'''
|
|
315
|
+
return bool(self.data.get('read_only'))
|
|
316
|
+
|
|
317
|
+
def isUserMountable(self) -> bool:
|
|
318
|
+
'''Returns True if server owners can attach it themselves.'''
|
|
319
|
+
return bool(self.data.get('user_mountable'))
|
|
320
|
+
|
|
321
|
+
def duplicate(self, name: str, source: str, target: str) -> 'Mount':
|
|
322
|
+
'''Copies the mount with a new name, source and target.'''
|
|
323
|
+
return self._duplicate(name=name, source=source, target=target)
|
|
324
|
+
|
|
325
|
+
def getNodes(self, search: str = None, page: int = None, per_page: int = None):
|
|
326
|
+
'''Returns nodes the mount is attached to.'''
|
|
327
|
+
return self._api().related(self.getUUID(), 'nodes', 'node_mounts', search, page, per_page)
|
|
328
|
+
|
|
329
|
+
def getServers(self, search: str = None, page: int = None, per_page: int = None):
|
|
330
|
+
'''Returns servers the mount is attached to.'''
|
|
331
|
+
return self._api().related(self.getUUID(), 'servers', 'server_mounts', search, page, per_page)
|
|
332
|
+
|
|
333
|
+
def getEggs(self, search: str = None, page: int = None, per_page: int = None):
|
|
334
|
+
'''Returns eggs the mount is attached to.'''
|
|
335
|
+
return self._api().related(self.getUUID(), 'nest-eggs', 'nest_egg_mounts', search, page, per_page)
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
class Device(AdminResource):
|
|
339
|
+
'''
|
|
340
|
+
A host device (e.g. a GPU) that can be passed into servers.
|
|
341
|
+
|
|
342
|
+
Example:
|
|
343
|
+
gpu = panel.admin.devices.createDevice('GPU', '/dev/dri', '/dev/dri', 'rwm')
|
|
344
|
+
'''
|
|
345
|
+
collection = 'devices'
|
|
346
|
+
|
|
347
|
+
def getSource(self) -> str:
|
|
348
|
+
'''Returns the device path on the node.'''
|
|
349
|
+
return self.data.get('source')
|
|
350
|
+
|
|
351
|
+
def getTarget(self) -> str:
|
|
352
|
+
'''Returns the device path inside the server.'''
|
|
353
|
+
return self.data.get('target')
|
|
354
|
+
|
|
355
|
+
def getPermissions(self) -> str:
|
|
356
|
+
'''Returns the cgroup permissions, e.g. "rwm".'''
|
|
357
|
+
return self.data.get('permissions')
|
|
358
|
+
|
|
359
|
+
def isUserAttachable(self) -> bool:
|
|
360
|
+
'''Returns True if server owners can attach it themselves.'''
|
|
361
|
+
return bool(self.data.get('user_attachable'))
|
|
362
|
+
|
|
363
|
+
def duplicate(self, name: str, source: str, target: str) -> 'Device':
|
|
364
|
+
'''Copies the device with a new name, source and target.'''
|
|
365
|
+
return self._duplicate(name=name, source=source, target=target)
|
|
366
|
+
|
|
367
|
+
def getNodes(self, search: str = None, page: int = None, per_page: int = None):
|
|
368
|
+
'''Returns nodes the device is attached to.'''
|
|
369
|
+
return self._api().related(self.getUUID(), 'nodes', 'node_devices', search, page, per_page)
|
|
370
|
+
|
|
371
|
+
def getServers(self, search: str = None, page: int = None, per_page: int = None):
|
|
372
|
+
'''Returns servers the device is attached to.'''
|
|
373
|
+
return self._api().related(self.getUUID(), 'servers', 'server_devices', search, page, per_page)
|
|
374
|
+
|
|
375
|
+
def getEggs(self, search: str = None, page: int = None, per_page: int = None):
|
|
376
|
+
'''Returns eggs the device is attached to.'''
|
|
377
|
+
return self._api().related(self.getUUID(), 'nest-eggs', 'nest_egg_devices', search, page, per_page)
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
class Role(AdminResource):
|
|
381
|
+
'''
|
|
382
|
+
A role grants admin and server permissions to users.
|
|
383
|
+
|
|
384
|
+
Example:
|
|
385
|
+
support = panel.admin.roles.createRole('Support', admin_permissions=['servers.read'])
|
|
386
|
+
user.setRole(support)
|
|
387
|
+
'''
|
|
388
|
+
collection = 'roles'
|
|
389
|
+
|
|
390
|
+
def duplicate(self, name: str) -> 'Role':
|
|
391
|
+
'''Creates a copy under a new name.'''
|
|
392
|
+
return self._duplicate(name=name)
|
|
393
|
+
|
|
394
|
+
def getAdminPermissions(self) -> list:
|
|
395
|
+
'''Returns the admin permissions.'''
|
|
396
|
+
return self.data.get('admin_permissions', [])
|
|
397
|
+
|
|
398
|
+
def getServerPermissions(self) -> list:
|
|
399
|
+
'''Returns the server permissions granted on every server.'''
|
|
400
|
+
return self.data.get('server_permissions', [])
|
|
401
|
+
|
|
402
|
+
def requiresTwoFactor(self) -> bool:
|
|
403
|
+
'''Returns True if members must use two-factor authentication.'''
|
|
404
|
+
return bool(self.data.get('require_two_factor'))
|
|
405
|
+
|
|
406
|
+
def getUsers(self, search: str = None, page: int = None, per_page: int = None):
|
|
407
|
+
'''Returns users with this role.'''
|
|
408
|
+
return self._api().related(self.getUUID(), 'users', 'users', search, page, per_page).map(lambda d: User(self.panel, d))
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
class Announcement(AdminResource):
|
|
412
|
+
'''
|
|
413
|
+
A panel announcement banner.
|
|
414
|
+
|
|
415
|
+
Example:
|
|
416
|
+
panel.admin.announcements.createAnnouncement('Maintenance', 'Nodes reboot at 02:00 UTC', type='warning')
|
|
417
|
+
'''
|
|
418
|
+
collection = 'announcements'
|
|
419
|
+
|
|
420
|
+
def getName(self) -> str:
|
|
421
|
+
'''Returns the title.'''
|
|
422
|
+
return self.data.get('title')
|
|
423
|
+
|
|
424
|
+
def getType(self) -> str:
|
|
425
|
+
'''Returns info, success, warning or error.'''
|
|
426
|
+
return self.data.get('type')
|
|
427
|
+
|
|
428
|
+
def getTitle(self) -> str:
|
|
429
|
+
'''Returns the title.'''
|
|
430
|
+
return self.data.get('title')
|
|
431
|
+
|
|
432
|
+
def getContent(self) -> str:
|
|
433
|
+
'''Returns the content (markdown).'''
|
|
434
|
+
return self.data.get('content')
|
|
435
|
+
|
|
436
|
+
def isEnabled(self) -> bool:
|
|
437
|
+
'''Returns True if the announcement is shown.'''
|
|
438
|
+
return bool(self.data.get('enabled'))
|
|
439
|
+
|
|
440
|
+
def isDismissible(self) -> bool:
|
|
441
|
+
'''Returns True if users can dismiss it.'''
|
|
442
|
+
return bool(self.data.get('dismissible'))
|
|
443
|
+
|
|
444
|
+
def enable(self) -> 'Announcement':
|
|
445
|
+
'''Shows the announcement.'''
|
|
446
|
+
return self.update(enabled=True)
|
|
447
|
+
|
|
448
|
+
def disable(self) -> 'Announcement':
|
|
449
|
+
'''Hides the announcement.'''
|
|
450
|
+
return self.update(enabled=False)
|
|
451
|
+
|
|
452
|
+
def getDescription(self) -> str:
|
|
453
|
+
'''Returns the content (announcements have no separate description).'''
|
|
454
|
+
return self.getContent()
|
|
455
|
+
|
|
456
|
+
def rename(self, title: str) -> 'Announcement':
|
|
457
|
+
'''Changes the title.'''
|
|
458
|
+
return self.update(title=title)
|
|
459
|
+
|
|
460
|
+
def setName(self, title: str) -> 'Announcement':
|
|
461
|
+
'''Changes the title.'''
|
|
462
|
+
return self.update(title=title)
|
|
463
|
+
|
|
464
|
+
def setTitle(self, title: str) -> 'Announcement':
|
|
465
|
+
'''Changes the title.'''
|
|
466
|
+
return self.update(title=title)
|
|
467
|
+
|
|
468
|
+
def setContent(self, content: str) -> 'Announcement':
|
|
469
|
+
'''Changes the content.'''
|
|
470
|
+
return self.update(content=content)
|
|
471
|
+
|
|
472
|
+
def setDescription(self, content: str) -> 'Announcement':
|
|
473
|
+
'''Changes the content.'''
|
|
474
|
+
return self.update(content=content)
|
|
475
|
+
|
|
476
|
+
def duplicate(self) -> 'Announcement':
|
|
477
|
+
'''Copies the announcement.'''
|
|
478
|
+
return self._duplicate()
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
class OAuthProvider(AdminResource):
|
|
482
|
+
'''
|
|
483
|
+
An OAuth login provider (Discord, GitHub, Google, Keycloak, ...).
|
|
484
|
+
|
|
485
|
+
Example:
|
|
486
|
+
provider = panel.admin.oauth.discover('https://accounts.google.com')
|
|
487
|
+
for mapping in provider.getMappings():
|
|
488
|
+
print(mapping)
|
|
489
|
+
'''
|
|
490
|
+
collection = 'oauth_providers'
|
|
491
|
+
|
|
492
|
+
def duplicate(self, name: str) -> 'OAuthProvider':
|
|
493
|
+
'''Creates a copy under a new name.'''
|
|
494
|
+
return self._duplicate(name=name)
|
|
495
|
+
|
|
496
|
+
def getClientId(self) -> str:
|
|
497
|
+
'''Returns the OAuth client id.'''
|
|
498
|
+
return self.data.get('client_id')
|
|
499
|
+
|
|
500
|
+
def getScopes(self) -> list:
|
|
501
|
+
'''Returns the requested scopes.'''
|
|
502
|
+
return self.data.get('scopes', [])
|
|
503
|
+
|
|
504
|
+
def isEnabled(self) -> bool:
|
|
505
|
+
'''Returns True if users can log in with this provider.'''
|
|
506
|
+
return bool(self.data.get('enabled'))
|
|
507
|
+
|
|
508
|
+
def enable(self) -> 'OAuthProvider':
|
|
509
|
+
'''Enables the provider.'''
|
|
510
|
+
return self.update(enabled=True)
|
|
511
|
+
|
|
512
|
+
def disable(self) -> 'OAuthProvider':
|
|
513
|
+
'''Disables the provider.'''
|
|
514
|
+
return self.update(enabled=False)
|
|
515
|
+
|
|
516
|
+
def _mappings(self):
|
|
517
|
+
return self.panel.api.admin.oauthMappings(self.getUUID())
|
|
518
|
+
|
|
519
|
+
def getMappings(self, page: int = None, per_page: int = None):
|
|
520
|
+
'''Returns rules that map OAuth claims to roles/permissions.'''
|
|
521
|
+
return self._mappings().list(page=page, per_page=per_page)
|
|
522
|
+
|
|
523
|
+
def createMapping(self, matcher: dict, mapping: dict) -> dict:
|
|
524
|
+
'''Adds a claim mapping rule.'''
|
|
525
|
+
return self._mappings().create(matcher=matcher, mapping=mapping)
|
|
526
|
+
|
|
527
|
+
def updateMapping(self, mapping_id: str, matcher: dict = None, mapping: dict = None) -> None:
|
|
528
|
+
'''Changes a claim mapping rule.'''
|
|
529
|
+
return self._mappings().update(mapping_id, matcher=matcher, mapping=mapping)
|
|
530
|
+
|
|
531
|
+
def deleteMapping(self, mapping_id: str) -> None:
|
|
532
|
+
'''Deletes a claim mapping rule.'''
|
|
533
|
+
return self._mappings().delete(mapping_id)
|
|
534
|
+
|
|
535
|
+
def getUsers(self, search: str = None, page: int = None, per_page: int = None):
|
|
536
|
+
'''Returns users linked through this provider.'''
|
|
537
|
+
return self._api().related(self.getUUID(), 'users', 'user_oauth_links', search, page, per_page)
|
|
538
|
+
|
|
539
|
+
def getUserLink(self, identifier: str) -> dict:
|
|
540
|
+
'''Returns the user linked to an OAuth identifier.'''
|
|
541
|
+
return self.panel.api.admin.oauthProviderUserLink(self.getUUID(), identifier)
|
|
542
|
+
|
|
543
|
+
def getMapping(self, mapping_id: str) -> dict:
|
|
544
|
+
'''Returns one claim mapping rule.'''
|
|
545
|
+
return self._mappings().get(mapping_id)
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
class DatabaseHost(AdminResource):
|
|
549
|
+
'''
|
|
550
|
+
An external database server (MySQL, PostgreSQL, MongoDB) servers can create databases on.
|
|
551
|
+
|
|
552
|
+
Example:
|
|
553
|
+
host = panel.admin.database_hosts.getDatabaseHost('...')
|
|
554
|
+
print(host.test())
|
|
555
|
+
'''
|
|
556
|
+
collection = 'database_hosts'
|
|
557
|
+
|
|
558
|
+
def setDescription(self, description: str):
|
|
559
|
+
'''Database hosts have no description.'''
|
|
560
|
+
raise InvalidActionError('Database hosts have no description')
|
|
561
|
+
|
|
562
|
+
def getType(self) -> str:
|
|
563
|
+
'''Returns mysql, postgres or mongodb.'''
|
|
564
|
+
return self.data.get('type')
|
|
565
|
+
|
|
566
|
+
def getPublicHost(self) -> str:
|
|
567
|
+
'''Returns the host shown to users.'''
|
|
568
|
+
return self.data.get('public_host')
|
|
569
|
+
|
|
570
|
+
def getPublicPort(self) -> int:
|
|
571
|
+
'''Returns the port shown to users.'''
|
|
572
|
+
return self.data.get('public_port')
|
|
573
|
+
|
|
574
|
+
def isDeploymentEnabled(self) -> bool:
|
|
575
|
+
'''Returns True if new databases can be created on it.'''
|
|
576
|
+
return bool(self.data.get('deployment_enabled'))
|
|
577
|
+
|
|
578
|
+
def isInMaintenance(self) -> bool:
|
|
579
|
+
'''Returns True if the host is in maintenance mode.'''
|
|
580
|
+
return bool(self.data.get('maintenance_enabled'))
|
|
581
|
+
|
|
582
|
+
def test(self) -> dict:
|
|
583
|
+
'''Tests the connection.'''
|
|
584
|
+
return self.panel.api.admin.testDatabaseHost(self.getUUID())
|
|
585
|
+
|
|
586
|
+
def getDatabases(self, search: str = None, page: int = None, per_page: int = None):
|
|
587
|
+
'''Returns databases on the host.'''
|
|
588
|
+
return self._api().related(self.getUUID(), 'databases', 'databases', search, page, per_page)
|
|
589
|
+
|
|
590
|
+
def deleteDatabase(self, database, force: bool = False) -> None:
|
|
591
|
+
'''Deletes a database on the host.'''
|
|
592
|
+
return self.panel.api.admin.deleteHostDatabase(self.getUUID(), idOf(database), force)
|
|
593
|
+
|
|
594
|
+
def delete(self, force: bool = False) -> None:
|
|
595
|
+
'''Deletes the host.'''
|
|
596
|
+
return self._api().delete(self.getUUID(), force=force)
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
class DatabaseAgentHost(AdminResource):
|
|
600
|
+
'''
|
|
601
|
+
A machine running the Calagopus database agent, which hosts database instances.
|
|
602
|
+
|
|
603
|
+
Example:
|
|
604
|
+
host = panel.admin.database_agent_hosts.getDatabaseAgentHost('...')
|
|
605
|
+
print(host.getSystemOverview())
|
|
606
|
+
'''
|
|
607
|
+
collection = 'database_agent_hosts'
|
|
608
|
+
|
|
609
|
+
def _admin(self):
|
|
610
|
+
return self.panel.api.admin
|
|
611
|
+
|
|
612
|
+
def getURL(self) -> str:
|
|
613
|
+
'''Returns the agent URL.'''
|
|
614
|
+
return self.data.get('url')
|
|
615
|
+
|
|
616
|
+
def getMemory(self) -> int:
|
|
617
|
+
'''Returns memory available for instances, in MiB.'''
|
|
618
|
+
return self.data.get('memory')
|
|
619
|
+
|
|
620
|
+
def getDisk(self) -> int:
|
|
621
|
+
'''Returns disk available for instances, in MiB.'''
|
|
622
|
+
return self.data.get('disk')
|
|
623
|
+
|
|
624
|
+
def getTypes(self) -> list:
|
|
625
|
+
'''Returns the database engines the agent supports.'''
|
|
626
|
+
return self.data.get('types', [])
|
|
627
|
+
|
|
628
|
+
def test(self) -> dict:
|
|
629
|
+
'''Tests the connection to the agent.'''
|
|
630
|
+
return self._admin().databaseAgentHostPost(self.getUUID(), 'test')
|
|
631
|
+
|
|
632
|
+
def getCapacity(self) -> dict:
|
|
633
|
+
'''Returns limits and allocated resources.'''
|
|
634
|
+
return self._admin().databaseAgentHostGet(self.getUUID(), 'capacity')
|
|
635
|
+
|
|
636
|
+
def getToken(self) -> dict:
|
|
637
|
+
'''Returns the agent token.'''
|
|
638
|
+
return self._admin().databaseAgentHostGet(self.getUUID(), 'token')
|
|
639
|
+
|
|
640
|
+
def resetToken(self) -> dict:
|
|
641
|
+
'''Generates a new agent token.'''
|
|
642
|
+
return self._admin().databaseAgentHostPost(self.getUUID(), 'reset-token')
|
|
643
|
+
|
|
644
|
+
def getSystemOverview(self) -> dict:
|
|
645
|
+
'''Returns agent version, CPU, memory and instance counts.'''
|
|
646
|
+
return self._admin().databaseAgentHostGet(self.getUUID(), 'system/overview')
|
|
647
|
+
|
|
648
|
+
def getSystemStats(self) -> dict:
|
|
649
|
+
'''Returns live system statistics.'''
|
|
650
|
+
return self._admin().databaseAgentHostGet(self.getUUID(), 'system/stats')['stats']
|
|
651
|
+
|
|
652
|
+
def getConfig(self) -> dict:
|
|
653
|
+
'''Returns the agent configuration.'''
|
|
654
|
+
return self._admin().databaseAgentHostGet(self.getUUID(), 'config')
|
|
655
|
+
|
|
656
|
+
def updateConfig(self, config: dict) -> bool:
|
|
657
|
+
'''Applies changes to the agent configuration.'''
|
|
658
|
+
return self._admin().updateDatabaseAgentHostConfig(self.getUUID(), config)
|
|
659
|
+
|
|
660
|
+
def getInstances(self, search: str = None, page: int = None, per_page: int = None):
|
|
661
|
+
'''Returns database instances on this host.'''
|
|
662
|
+
return self._api().related(self.getUUID(), 'instances', 'instances', search, page, per_page)
|
|
663
|
+
|
|
664
|
+
def getInstance(self, instance_id: str) -> dict:
|
|
665
|
+
'''Returns one database instance.'''
|
|
666
|
+
return self._admin().databaseAgentInstance(self.getUUID(), instance_id)['instance']
|
|
667
|
+
|
|
668
|
+
def updateInstance(self, instance_id: str, **fields) -> None:
|
|
669
|
+
'''Changes an instance (name, locked, image, env, memory, swap, disk, io_weight, cpu).'''
|
|
670
|
+
return self._admin().databaseAgentInstance(self.getUUID(), instance_id, 'PATCH', fields)
|
|
671
|
+
|
|
672
|
+
def deleteInstance(self, instance_id: str, force: bool = False) -> None:
|
|
673
|
+
'''Deletes an instance.'''
|
|
674
|
+
return self._admin().databaseAgentInstance(self.getUUID(), instance_id, 'DELETE', {'force': force})
|
|
675
|
+
|
|
676
|
+
def getBackups(self, search: str = None, page: int = None, per_page: int = None):
|
|
677
|
+
'''Returns database backups stored by this agent.'''
|
|
678
|
+
return self._api().related(self.getUUID(), 'backups', 'backups', search, page, per_page)
|
|
679
|
+
|
|
680
|
+
def deleteFailedBackups(self, force: bool = False) -> int:
|
|
681
|
+
'''Deletes failed backups.'''
|
|
682
|
+
return self._admin().deleteFailedBackups('database-agent-hosts', self.getUUID(), force)
|
|
683
|
+
|
|
684
|
+
def delete(self, force: bool = False) -> None:
|
|
685
|
+
'''Deletes the host.'''
|
|
686
|
+
return self._api().delete(self.getUUID(), force=force)
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
class DatabaseAgentTemplate(AdminResource):
|
|
690
|
+
'''A template defining how database instances are created (engine, images, limits).'''
|
|
691
|
+
collection = 'database_agent_templates'
|
|
692
|
+
|
|
693
|
+
def duplicate(self, name: str) -> 'DatabaseAgentTemplate':
|
|
694
|
+
'''Creates a copy under a new name.'''
|
|
695
|
+
return self._duplicate(name=name)
|
|
696
|
+
|
|
697
|
+
def getType(self) -> str:
|
|
698
|
+
'''Returns the engine type.'''
|
|
699
|
+
return self.data.get('type')
|
|
700
|
+
|
|
701
|
+
def getDockerImages(self) -> dict:
|
|
702
|
+
'''Returns the allowed images.'''
|
|
703
|
+
return self.data.get('docker_images', {})
|
|
704
|
+
|
|
705
|
+
def getInstances(self, search: str = None, page: int = None, per_page: int = None):
|
|
706
|
+
'''Returns instances created from this template.'''
|
|
707
|
+
return self._api().related(self.getUUID(), 'instances', 'instances', search, page, per_page)
|
|
708
|
+
|
|
709
|
+
def updateInstances(self, instances) -> int:
|
|
710
|
+
'''Applies template changes to existing instances.'''
|
|
711
|
+
return self.panel.api.admin.updateTemplateInstances(self.getUUID(), instances)
|
|
712
|
+
|
|
713
|
+
|
|
714
|
+
class BackupConfiguration(AdminResource):
|
|
715
|
+
'''
|
|
716
|
+
Where and how backups are stored (local, S3, restic, kopia, ...).
|
|
717
|
+
|
|
718
|
+
Example:
|
|
719
|
+
config = panel.admin.backup_configurations.getBackupConfiguration('...')
|
|
720
|
+
print(config.getStats()['all_time'])
|
|
721
|
+
'''
|
|
722
|
+
collection = 'backup_configurations'
|
|
723
|
+
|
|
724
|
+
def duplicate(self, name: str) -> 'BackupConfiguration':
|
|
725
|
+
'''Creates a copy under a new name.'''
|
|
726
|
+
return self._duplicate(name=name)
|
|
727
|
+
|
|
728
|
+
def getBackupDisk(self) -> str:
|
|
729
|
+
'''Returns the storage driver, e.g. "s3" or "local".'''
|
|
730
|
+
return self.data.get('backup_disk')
|
|
731
|
+
|
|
732
|
+
def getBackupConfigs(self) -> dict:
|
|
733
|
+
'''Returns the driver configuration.'''
|
|
734
|
+
return self.data.get('backup_configs')
|
|
735
|
+
|
|
736
|
+
def isShared(self) -> bool:
|
|
737
|
+
'''Returns True if the configuration is shared.'''
|
|
738
|
+
return bool(self.data.get('shared'))
|
|
739
|
+
|
|
740
|
+
def isInMaintenance(self) -> bool:
|
|
741
|
+
'''Returns True if backups are paused.'''
|
|
742
|
+
return bool(self.data.get('maintenance_enabled'))
|
|
743
|
+
|
|
744
|
+
def getStats(self) -> dict:
|
|
745
|
+
'''Returns backup counts and sizes for today, this week, this month and all time.'''
|
|
746
|
+
return self.panel.api.admin.backupConfigurationStats(self.getUUID())
|
|
747
|
+
|
|
748
|
+
def getBackups(self, search: str = None, page: int = None, per_page: int = None):
|
|
749
|
+
'''Returns backups stored with this configuration.'''
|
|
750
|
+
return self._api().related(self.getUUID(), 'backups', 'backups', search, page, per_page)
|
|
751
|
+
|
|
752
|
+
def deleteFailedBackups(self, force: bool = False) -> int:
|
|
753
|
+
'''Deletes failed backups.'''
|
|
754
|
+
return self.panel.api.admin.deleteFailedBackups('backup-configurations', self.getUUID(), force)
|
|
755
|
+
|
|
756
|
+
def getLocations(self, search: str = None, page: int = None, per_page: int = None):
|
|
757
|
+
'''Returns locations using this configuration.'''
|
|
758
|
+
return self._api().related(self.getUUID(), 'locations', 'locations', search, page, per_page)
|
|
759
|
+
|
|
760
|
+
def getNodes(self, search: str = None, page: int = None, per_page: int = None):
|
|
761
|
+
'''Returns nodes using this configuration.'''
|
|
762
|
+
return self._api().related(self.getUUID(), 'nodes', 'nodes', search, page, per_page)
|
|
763
|
+
|
|
764
|
+
def getServers(self, search: str = None, page: int = None, per_page: int = None):
|
|
765
|
+
'''Returns servers using this configuration.'''
|
|
766
|
+
return self._api().related(self.getUUID(), 'servers', 'servers', search, page, per_page)
|
|
767
|
+
|
|
768
|
+
|
|
769
|
+
class BackupPolicy(AdminResource):
|
|
770
|
+
'''
|
|
771
|
+
A panel-wide backup policy that backs up servers on a cron schedule.
|
|
772
|
+
|
|
773
|
+
Example:
|
|
774
|
+
policy = panel.admin.backup_policies.createBackupPolicy('Nightly', '0 0 3 * * *')
|
|
775
|
+
policy.attachLocation(location)
|
|
776
|
+
policy.trigger()
|
|
777
|
+
'''
|
|
778
|
+
collection = 'system_backup_policies'
|
|
779
|
+
|
|
780
|
+
def getCron(self) -> str:
|
|
781
|
+
'''Returns the cron expression.'''
|
|
782
|
+
return self.data.get('cron')
|
|
783
|
+
|
|
784
|
+
def getRetention(self) -> dict:
|
|
785
|
+
'''Returns the retention policy.'''
|
|
786
|
+
return self.data.get('retention')
|
|
787
|
+
|
|
788
|
+
def isEnabled(self) -> bool:
|
|
789
|
+
'''Returns True if the policy is active.'''
|
|
790
|
+
return bool(self.data.get('enabled'))
|
|
791
|
+
|
|
792
|
+
def enable(self) -> 'BackupPolicy':
|
|
793
|
+
'''Activates the policy.'''
|
|
794
|
+
return self.update(enabled=True)
|
|
795
|
+
|
|
796
|
+
def disable(self) -> 'BackupPolicy':
|
|
797
|
+
'''Deactivates the policy.'''
|
|
798
|
+
return self.update(enabled=False)
|
|
799
|
+
|
|
800
|
+
def trigger(self) -> None:
|
|
801
|
+
'''Runs the policy now.'''
|
|
802
|
+
return self.panel.api.admin.triggerSystemBackupPolicy(self.getUUID())
|
|
803
|
+
|
|
804
|
+
def getBackups(self, search: str = None, page: int = None, per_page: int = None):
|
|
805
|
+
'''Returns backups created by the policy.'''
|
|
806
|
+
return self._api().related(self.getUUID(), 'backups', 'backups', search, page, per_page)
|
|
807
|
+
|
|
808
|
+
def deleteFailedBackups(self, force: bool = False) -> int:
|
|
809
|
+
'''Deletes failed backups.'''
|
|
810
|
+
return self.panel.api.admin.deleteFailedBackups('system-backup-policies', self.getUUID(), force)
|
|
811
|
+
|
|
812
|
+
def _targets(self, suffix, key, search, page, per_page):
|
|
813
|
+
return self._api().related(self.getUUID(), suffix, key, search, page, per_page)
|
|
814
|
+
|
|
815
|
+
def getLocations(self, search: str = None, page: int = None, per_page: int = None):
|
|
816
|
+
'''Returns locations covered by the policy.'''
|
|
817
|
+
return self._targets('locations', 'locations', search, page, per_page)
|
|
818
|
+
|
|
819
|
+
def getNodes(self, search: str = None, page: int = None, per_page: int = None):
|
|
820
|
+
'''Returns nodes covered by the policy.'''
|
|
821
|
+
return self._targets('nodes', 'nodes', search, page, per_page)
|
|
822
|
+
|
|
823
|
+
def getServers(self, search: str = None, page: int = None, per_page: int = None):
|
|
824
|
+
'''Returns servers covered by the policy.'''
|
|
825
|
+
return self._targets('servers', 'servers', search, page, per_page)
|
|
826
|
+
|
|
827
|
+
def getDatabaseAgentHosts(self, search: str = None, page: int = None, per_page: int = None):
|
|
828
|
+
'''Returns database agent hosts covered by the policy.'''
|
|
829
|
+
return self._targets('database-agent-hosts', 'database_agent_hosts', search, page, per_page)
|
|
830
|
+
|
|
831
|
+
def attachLocation(self, location) -> None:
|
|
832
|
+
'''Adds a location to the policy.'''
|
|
833
|
+
return self._api().attach(self.getUUID(), 'locations', 'location_uuid', idOf(location))
|
|
834
|
+
|
|
835
|
+
def detachLocation(self, location) -> None:
|
|
836
|
+
'''Removes a location from the policy.'''
|
|
837
|
+
return self._api().detach(self.getUUID(), 'locations', idOf(location))
|
|
838
|
+
|
|
839
|
+
def attachNode(self, node) -> None:
|
|
840
|
+
'''Adds a node to the policy.'''
|
|
841
|
+
return self._api().attach(self.getUUID(), 'nodes', 'node_uuid', idOf(node))
|
|
842
|
+
|
|
843
|
+
def detachNode(self, node) -> None:
|
|
844
|
+
'''Removes a node from the policy.'''
|
|
845
|
+
return self._api().detach(self.getUUID(), 'nodes', idOf(node))
|
|
846
|
+
|
|
847
|
+
def attachServer(self, server) -> None:
|
|
848
|
+
'''Adds a server to the policy.'''
|
|
849
|
+
return self._api().attach(self.getUUID(), 'servers', 'server_uuid', idOf(server))
|
|
850
|
+
|
|
851
|
+
def detachServer(self, server) -> None:
|
|
852
|
+
'''Removes a server from the policy.'''
|
|
853
|
+
return self._api().detach(self.getUUID(), 'servers', idOf(server))
|
|
854
|
+
|
|
855
|
+
def attachDatabaseAgentHost(self, host) -> None:
|
|
856
|
+
'''Adds a database agent host to the policy.'''
|
|
857
|
+
return self._api().attach(self.getUUID(), 'database-agent-hosts', 'database_agent_host_uuid', idOf(host))
|
|
858
|
+
|
|
859
|
+
def detachDatabaseAgentHost(self, host) -> None:
|
|
860
|
+
'''Removes a database agent host from the policy.'''
|
|
861
|
+
return self._api().detach(self.getUUID(), 'database-agent-hosts', idOf(host))
|
|
862
|
+
|
|
863
|
+
def delete(self, delete_backups: bool = False) -> None:
|
|
864
|
+
'''Deletes the policy, optionally with its backups.'''
|
|
865
|
+
return self._api().delete(self.getUUID(), delete_backups=delete_backups)
|
|
866
|
+
|
|
867
|
+
|
|
868
|
+
class EggRepository(AdminResource):
|
|
869
|
+
'''
|
|
870
|
+
A git repository of eggs that can be synced and installed from.
|
|
871
|
+
|
|
872
|
+
Example:
|
|
873
|
+
repo = panel.admin.egg_repositories.createEggRepository('Pelican eggs', 'https://github.com/pelican-eggs/minecraft')
|
|
874
|
+
repo.sync()
|
|
875
|
+
repo.installEggs(repo.getEggs(), nest)
|
|
876
|
+
'''
|
|
877
|
+
collection = 'egg_repositories'
|
|
878
|
+
|
|
879
|
+
def getGitRepository(self) -> str:
|
|
880
|
+
'''Returns the git URL.'''
|
|
881
|
+
return self.data.get('git_repository')
|
|
882
|
+
|
|
883
|
+
def sync(self) -> int:
|
|
884
|
+
'''Pulls the repository. Returns the number of eggs found.'''
|
|
885
|
+
return self.panel.api.admin.syncEggRepository(self.getUUID())
|
|
886
|
+
|
|
887
|
+
def getEggs(self, search: str = None, page: int = None, per_page: int = None):
|
|
888
|
+
'''Returns the eggs available in the repository.'''
|
|
889
|
+
return self._api().related(self.getUUID(), 'eggs', 'egg_repository_eggs', search, page, per_page)
|
|
890
|
+
|
|
891
|
+
def installEgg(self, egg, nest) -> dict:
|
|
892
|
+
'''Installs one repository egg into a nest.'''
|
|
893
|
+
egg_id = egg.get('uuid') if isinstance(egg, dict) else idOf(egg)
|
|
894
|
+
return self.panel.api.admin.installRepositoryEgg(self.getUUID(), egg_id, idOf(nest))
|
|
895
|
+
|
|
896
|
+
def installEggs(self, eggs: list, nest) -> int:
|
|
897
|
+
'''Installs several repository eggs into a nest. Returns how many were installed.'''
|
|
898
|
+
egg_ids = [e.get('uuid') if isinstance(e, dict) else idOf(e) for e in eggs]
|
|
899
|
+
return self.panel.api.admin.installRepositoryEggs(self.getUUID(), egg_ids, idOf(nest))
|
|
900
|
+
|
|
901
|
+
|
|
902
|
+
class EggConfiguration(AdminResource):
|
|
903
|
+
'''Rules that apply extra configuration (allocations, startup, routes, features) to matching eggs.'''
|
|
904
|
+
collection = 'egg_configurations'
|
|
905
|
+
|
|
906
|
+
def duplicate(self, name: str) -> 'EggConfiguration':
|
|
907
|
+
'''Creates a copy under a new name.'''
|
|
908
|
+
return self._duplicate(name=name)
|
|
909
|
+
|
|
910
|
+
def getOrder(self) -> int:
|
|
911
|
+
'''Returns the priority order.'''
|
|
912
|
+
return self.data.get('order')
|
|
913
|
+
|
|
914
|
+
def getMatcher(self) -> dict:
|
|
915
|
+
'''Returns the rule deciding which eggs match.'''
|
|
916
|
+
return self.data.get('matcher')
|
|
917
|
+
|
|
918
|
+
|
|
919
|
+
class SubdomainDomain(AdminResource):
|
|
920
|
+
'''
|
|
921
|
+
A domain users can create subdomains on (Subdomain Manager extension).
|
|
922
|
+
|
|
923
|
+
Example:
|
|
924
|
+
domain = panel.admin.subdomain_domains.createDomain('example.com', 'cloudflare', {'api_token': '...'})
|
|
925
|
+
print(domain.test())
|
|
926
|
+
'''
|
|
927
|
+
collection = 'subdomain_domains'
|
|
928
|
+
|
|
929
|
+
def getName(self) -> str:
|
|
930
|
+
'''Returns the domain.'''
|
|
931
|
+
return self.data.get('domain')
|
|
932
|
+
|
|
933
|
+
def getDomain(self) -> str:
|
|
934
|
+
'''Returns the domain.'''
|
|
935
|
+
return self.data.get('domain')
|
|
936
|
+
|
|
937
|
+
def getProvider(self) -> str:
|
|
938
|
+
'''Returns the DNS provider: cloudflare or powerdns.'''
|
|
939
|
+
return self.data.get('provider')
|
|
940
|
+
|
|
941
|
+
def getEggs(self) -> list:
|
|
942
|
+
'''Returns the eggs allowed to use the domain. Empty means all.'''
|
|
943
|
+
return self.data.get('eggs', [])
|
|
944
|
+
|
|
945
|
+
def refresh(self) -> 'SubdomainDomain':
|
|
946
|
+
'''Re-fetches the domain from the panel.'''
|
|
947
|
+
for data in self._api().list():
|
|
948
|
+
if data.get('uuid') == self.getUUID():
|
|
949
|
+
self.data = data
|
|
950
|
+
return self
|
|
951
|
+
|
|
952
|
+
def rename(self, domain: str) -> 'SubdomainDomain':
|
|
953
|
+
'''Changes the domain name.'''
|
|
954
|
+
return self.update(domain=domain)
|
|
955
|
+
|
|
956
|
+
def setName(self, domain: str) -> 'SubdomainDomain':
|
|
957
|
+
'''Changes the domain name.'''
|
|
958
|
+
return self.update(domain=domain)
|
|
959
|
+
|
|
960
|
+
def setDescription(self, description: str):
|
|
961
|
+
'''Domains have no description.'''
|
|
962
|
+
raise InvalidActionError('Subdomain domains have no description')
|
|
963
|
+
|
|
964
|
+
def test(self) -> dict:
|
|
965
|
+
'''Tests the DNS provider credentials.'''
|
|
966
|
+
return self.panel.api.admin.testSubdomainDomain(self.getUUID())
|
|
967
|
+
|
|
968
|
+
def getSubdomains(self, search: str = None, page: int = None, per_page: int = None):
|
|
969
|
+
'''Returns subdomains created on this domain.'''
|
|
970
|
+
return self._api().related(self.getUUID(), 'subdomains', 'subdomains', search, page, per_page)
|