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,122 @@
1
+ from calagoapi.databases.database import Database, DatabaseInstance
2
+ from calagoapi.utils.model import idOf
3
+ from calagoapi.utils.exceptions import InvalidActionError
4
+
5
+
6
+ class DatabaseManager:
7
+ '''
8
+ Database management for one server, available as `server.databases`.
9
+
10
+ Calagopus offers two kinds of databases:
11
+ - databases on a shared database host (`getDatabases()`, `createDatabase()`)
12
+ - dedicated database instances the server runs itself (`getInstances()`, `createInstance()`)
13
+
14
+ Args:
15
+ server: The server to manage databases for.
16
+
17
+ Example:
18
+ db = server.databases.createDatabase('stats')
19
+ print(db.getConnectionString())
20
+ '''
21
+ def __init__(self, server):
22
+ self.server = server
23
+ self.api = server.panel.api.databases
24
+
25
+ @property
26
+ def server_id(self):
27
+ return self.server.server_id
28
+
29
+ def _wrap(self, data):
30
+ return Database(self.server, data)
31
+
32
+ # hosted databases
33
+ def getDatabases(self, search: str = None, include_password: bool = False, page: int = None, per_page: int = None):
34
+ '''
35
+ Lists the server's databases.
36
+
37
+ Args:
38
+ search: Filter by name.
39
+ include_password: Include each database password.
40
+
41
+ Returns:
42
+ A Page (list) of Database objects.
43
+ '''
44
+ return self.api.list(self.server_id, search, include_password or None, page, per_page).map(self._wrap)
45
+
46
+ def getDatabase(self, database_id: str, include_password: bool = False) -> Database:
47
+ '''Returns a single database by UUID.'''
48
+ return self._wrap(self.api.get(self.server_id, database_id, include_password or None))
49
+
50
+ def getDatabaseByName(self, name: str, include_password: bool = False) -> Database:
51
+ '''Returns a database by name, or None.'''
52
+ for database in self.getDatabases(include_password=include_password):
53
+ if database.getName() == name:
54
+ return database
55
+ return None
56
+
57
+ def getDatabaseHosts(self) -> list:
58
+ '''Returns the database hosts this server can create databases on.'''
59
+ return self.api.hosts(self.server_id)
60
+
61
+ def createDatabase(self, name: str, host=None) -> Database:
62
+ '''
63
+ Creates a database.
64
+
65
+ Args:
66
+ name: Database name.
67
+ host: Database host UUID (or dict from getDatabaseHosts()). Defaults to the first available host.
68
+
69
+ Returns:
70
+ The new Database, including its password.
71
+
72
+ Example:
73
+ db = server.databases.createDatabase('luckperms')
74
+ '''
75
+ if host is None:
76
+ hosts = self.getDatabaseHosts()
77
+ if not hosts:
78
+ raise InvalidActionError('No database hosts are available for this server')
79
+ host = hosts[0]
80
+ host_id = host.get('uuid') if isinstance(host, dict) else idOf(host)
81
+ return self._wrap(self.api.create(self.server_id, host_id, name))
82
+
83
+ def rotateDatabasePassword(self, database_id: str) -> str:
84
+ '''Generates a new password for a database and returns it.'''
85
+ return self.api.rotatePassword(self.server_id, idOf(database_id))
86
+
87
+ def deleteDatabase(self, database_id: str) -> None:
88
+ '''Deletes a database.'''
89
+ return self.api.delete(self.server_id, idOf(database_id))
90
+
91
+ # database instances
92
+ def getInstances(self, search: str = None, page: int = None, per_page: int = None):
93
+ '''Lists the server's database instances.'''
94
+ return self.api.instances(self.server_id, search, page, per_page).map(lambda data: DatabaseInstance(self.server, data))
95
+
96
+ def getInstance(self, instance_id: str) -> DatabaseInstance:
97
+ '''Returns a database instance, including live state.'''
98
+ return DatabaseInstance(self.server, self.api.instance(self.server_id, instance_id))
99
+
100
+ def getTemplates(self) -> list:
101
+ '''Returns the database instance templates (engines) available to this server.'''
102
+ return self.api.templates(self.server_id)
103
+
104
+ def createInstance(self, template, name: str, image: str = None) -> DatabaseInstance:
105
+ '''
106
+ Creates a database instance.
107
+
108
+ Args:
109
+ template: Template UUID or dict from getTemplates().
110
+ name: Instance name.
111
+ image: Optional docker image from the template's allowed images.
112
+
113
+ Example:
114
+ template = server.databases.getTemplates()[0]
115
+ instance = server.databases.createInstance(template, 'main-db')
116
+ '''
117
+ template_id = template.get('uuid') if isinstance(template, dict) else template
118
+ return DatabaseInstance(self.server, self.api.createInstance(self.server_id, template_id, name, image))
119
+
120
+ def deleteInstance(self, instance_id: str, force: bool = None) -> None:
121
+ '''Deletes a database instance.'''
122
+ return self.api.deleteInstance(self.server_id, idOf(instance_id), force)
@@ -0,0 +1,9 @@
1
+ from .file import File, splitPath, joinPath
2
+ from .manager import FileManager
3
+
4
+ __all__ = [
5
+ 'File',
6
+ 'FileManager',
7
+ 'splitPath',
8
+ 'joinPath',
9
+ ]
@@ -0,0 +1,196 @@
1
+ import posixpath
2
+
3
+ from calagoapi.utils.model import Model
4
+ from calagoapi.utils.convert import formatBytes
5
+
6
+
7
+ def splitPath(path: str) -> tuple:
8
+ '''Splits '/plugins/config.yml' into ('/plugins', 'config.yml')'''
9
+ path = '/' + path.strip('/')
10
+ return posixpath.dirname(path) or '/', posixpath.basename(path)
11
+
12
+
13
+ def joinPath(directory: str, name: str) -> str:
14
+ '''Joins a directory and a file name into an absolute server path'''
15
+ return posixpath.join('/' + directory.strip('/'), name)
16
+
17
+
18
+ class File(Model):
19
+ '''
20
+ A file or directory on a server.
21
+
22
+ Returned by `server.files.listFiles()` and `server.files.getFile()`.
23
+
24
+ Args:
25
+ server: The server the file lives on.
26
+ data: Raw directory entry from Calagopus.
27
+ directory: The directory containing this entry.
28
+
29
+ Example:
30
+ for file in server.files.listFiles('/plugins'):
31
+ if file.isFile() and file.getName().endswith('.jar'):
32
+ print(file.getName(), file.getSize(formatted=True))
33
+ '''
34
+ def __init__(self, server, data: dict, directory: str = '/'):
35
+ super().__init__(server.panel, data)
36
+ self.server = server
37
+ self.directory = '/' + directory.strip('/')
38
+
39
+ def __repr__(self):
40
+ kind = 'Directory' if self.isDirectory() else 'File'
41
+ return f'<{kind} {self.getPath()!r}>'
42
+
43
+ def __eq__(self, other):
44
+ if not isinstance(other, File):
45
+ return NotImplemented
46
+ return self.server.server_id == other.server.server_id and self.getPath() == other.getPath()
47
+
48
+ def __hash__(self):
49
+ return hash((self.server.server_id, self.getPath()))
50
+
51
+ # getters
52
+ def getName(self) -> str:
53
+ '''Returns the file name, e.g. "server.properties".'''
54
+ return self.data.get('name')
55
+
56
+ def getPath(self) -> str:
57
+ '''Returns the absolute path on the server, e.g. "/plugins/config.yml".'''
58
+ return joinPath(self.directory, self.getName())
59
+
60
+ def getDirectory(self) -> str:
61
+ '''Returns the directory containing this file.'''
62
+ return self.directory
63
+
64
+ def getSize(self, formatted: bool = False):
65
+ '''
66
+ Returns the size in bytes.
67
+
68
+ Args:
69
+ formatted: Return a human readable string like "1.5 MiB" instead.
70
+ '''
71
+ size = self.data.get('size')
72
+ return formatBytes(size) if formatted else size
73
+
74
+ def getPhysicalSize(self) -> int:
75
+ '''Returns the size the file actually takes on disk, in bytes.'''
76
+ return self.data.get('size_physical')
77
+
78
+ def getMode(self) -> str:
79
+ '''Returns the permission string, e.g. "-rw-r--r--".'''
80
+ return self.data.get('mode')
81
+
82
+ def getModeBits(self) -> str:
83
+ '''Returns the octal permission bits, e.g. "644".'''
84
+ return self.data.get('mode_bits')
85
+
86
+ def getMimeType(self) -> str:
87
+ '''Returns the detected mime type, e.g. "text/plain".'''
88
+ return self.data.get('mime')
89
+
90
+ def getModified(self) -> str:
91
+ '''Returns the last modification time as an ISO-8601 string.'''
92
+ return self.data.get('modified')
93
+
94
+ def getExtension(self) -> str:
95
+ '''Returns the file extension without the dot, or an empty string.'''
96
+ return posixpath.splitext(self.getName() or '')[1].lstrip('.')
97
+
98
+ # state checks
99
+ def isDirectory(self) -> bool:
100
+ '''Returns True if this entry is a directory.'''
101
+ return bool(self.data.get('directory'))
102
+
103
+ def isFile(self) -> bool:
104
+ '''Returns True if this entry is a regular file.'''
105
+ return bool(self.data.get('file'))
106
+
107
+ def isSymlink(self) -> bool:
108
+ '''Returns True if this entry is a symbolic link.'''
109
+ return bool(self.data.get('symlink'))
110
+
111
+ def isEditable(self) -> bool:
112
+ '''Returns True if the panel allows editing this file as text.'''
113
+ return bool(self.data.get('editable'))
114
+
115
+ def isArchive(self) -> bool:
116
+ '''Returns True if this looks like an archive the panel can decompress.'''
117
+ name = (self.getName() or '').lower()
118
+ return name.endswith(('.zip', '.tar', '.tar.gz', '.tgz', '.tar.xz', '.tar.bz2', '.tar.zst', '.tar.lz4', '.7z', '.gz', '.xz', '.bz2', '.zst', '.rar'))
119
+
120
+ # actions
121
+ def read(self) -> str:
122
+ '''
123
+ Returns the file contents as text.
124
+
125
+ Example:
126
+ print(server.files.getFile('/server.properties').read())
127
+ '''
128
+ return self.server.files.readFile(self.getPath())
129
+
130
+ def write(self, content) -> None:
131
+ '''Replaces the file contents.'''
132
+ return self.server.files.writeFile(self.getPath(), content)
133
+
134
+ def download(self, destination: str = None):
135
+ '''
136
+ Downloads the file (or the directory, as an archive).
137
+
138
+ Args:
139
+ destination: Optional local path to save to.
140
+
141
+ Returns:
142
+ The bytes if no destination was given, otherwise the destination path.
143
+ '''
144
+ return self.server.files.downloadFile(self.getPath(), destination)
145
+
146
+ def listFiles(self):
147
+ '''Lists the contents of this directory.'''
148
+ return self.server.files.listFiles(self.getPath())
149
+
150
+ def rename(self, new_name: str) -> 'File':
151
+ '''
152
+ Renames the file inside its current directory.
153
+
154
+ Example:
155
+ file.rename('server.properties.bak')
156
+ '''
157
+ self.server.files.renameFile(self.getPath(), joinPath(self.directory, new_name))
158
+ self.data['name'] = new_name
159
+ return self
160
+
161
+ def move(self, destination: str) -> 'File':
162
+ '''
163
+ Moves the file to another path.
164
+
165
+ Args:
166
+ destination: New absolute path, e.g. "/backups/world.zip".
167
+ '''
168
+ self.server.files.renameFile(self.getPath(), destination)
169
+ self.directory, self.data['name'] = splitPath(destination)
170
+ return self
171
+
172
+ def copy(self, destination: str = None):
173
+ '''Copies the file. Without a destination Calagopus picks a "copy" name next to it.'''
174
+ return self.server.files.copyFile(self.getPath(), destination)
175
+
176
+ def delete(self) -> None:
177
+ '''Deletes the file or directory.'''
178
+ return self.server.files.deleteFile(self.getPath())
179
+
180
+ def chmod(self, mode: str, recursive: bool = False) -> None:
181
+ '''
182
+ Changes the file permissions.
183
+
184
+ Args:
185
+ mode: Octal mode, e.g. "755".
186
+ recursive: Apply to everything inside a directory.
187
+ '''
188
+ return self.server.files.chmodFile(self.getPath(), mode, recursive)
189
+
190
+ def compress(self, format: str = 'tar_gz', name: str = None):
191
+ '''Compresses this file into an archive next to it.'''
192
+ return self.server.files.compress(self.directory, [self.getName()], format, name)
193
+
194
+ def decompress(self):
195
+ '''Extracts this archive into its directory.'''
196
+ return self.server.files.decompress(self.getPath())