mtn-cloud 0.1.0__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.
- mtn_cloud/__init__.py +72 -0
- mtn_cloud/client.py +278 -0
- mtn_cloud/config.py +143 -0
- mtn_cloud/exceptions.py +261 -0
- mtn_cloud/http.py +402 -0
- mtn_cloud/models/__init__.py +54 -0
- mtn_cloud/models/base.py +123 -0
- mtn_cloud/models/cloud.py +107 -0
- mtn_cloud/models/group.py +62 -0
- mtn_cloud/models/instance.py +367 -0
- mtn_cloud/models/network.py +97 -0
- mtn_cloud/models/plan.py +90 -0
- mtn_cloud/models/user.py +84 -0
- mtn_cloud/models/volume.py +60 -0
- mtn_cloud/resources/__init__.py +22 -0
- mtn_cloud/resources/base.py +196 -0
- mtn_cloud/resources/clouds.py +129 -0
- mtn_cloud/resources/groups.py +111 -0
- mtn_cloud/resources/instances.py +505 -0
- mtn_cloud/resources/networks.py +128 -0
- mtn_cloud/resources/plans.py +141 -0
- mtn_cloud-0.1.0.dist-info/METADATA +451 -0
- mtn_cloud-0.1.0.dist-info/RECORD +25 -0
- mtn_cloud-0.1.0.dist-info/WHEEL +4 -0
- mtn_cloud-0.1.0.dist-info/licenses/LICENSE +22 -0
mtn_cloud/__init__.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MTN Cloud Python SDK
|
|
3
|
+
====================
|
|
4
|
+
|
|
5
|
+
A community-maintained Python SDK for interacting with MTN Cloud (Morpheus).
|
|
6
|
+
|
|
7
|
+
Quick Start:
|
|
8
|
+
```python
|
|
9
|
+
from mtn_cloud import MTNCloud
|
|
10
|
+
|
|
11
|
+
# Initialize client
|
|
12
|
+
cloud = MTNCloud(token="your-api-token")
|
|
13
|
+
|
|
14
|
+
# List instances
|
|
15
|
+
for instance in cloud.instances.list():
|
|
16
|
+
print(f"{instance.name}: {instance.status}")
|
|
17
|
+
|
|
18
|
+
# Create an instance
|
|
19
|
+
instance = cloud.instances.create(
|
|
20
|
+
name="my-app",
|
|
21
|
+
cloud_id=1,
|
|
22
|
+
group_id=1,
|
|
23
|
+
instance_type_code="MTN-CS10",
|
|
24
|
+
plan_id=6923,
|
|
25
|
+
layout_id=327,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
# Instance actions
|
|
29
|
+
instance.stop()
|
|
30
|
+
instance.start()
|
|
31
|
+
instance.delete()
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Environment Variables:
|
|
35
|
+
MTN_CLOUD_TOKEN: API access token
|
|
36
|
+
MTN_CLOUD_URL: API URL (defaults to https://console.cloud.mtn.ng)
|
|
37
|
+
|
|
38
|
+
Author: Marvellous Osuolale
|
|
39
|
+
License: MIT
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
from mtn_cloud.client import MTNCloud
|
|
43
|
+
from mtn_cloud.config import MTNCloudConfig
|
|
44
|
+
from mtn_cloud.exceptions import (
|
|
45
|
+
AuthenticationError,
|
|
46
|
+
ForbiddenError,
|
|
47
|
+
MTNCloudError,
|
|
48
|
+
NotFoundError,
|
|
49
|
+
RateLimitError,
|
|
50
|
+
ServerError,
|
|
51
|
+
TimeoutError,
|
|
52
|
+
ValidationError,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
__version__ = "0.1.0"
|
|
56
|
+
__author__ = "Marvellous Osuolale"
|
|
57
|
+
__license__ = "MIT"
|
|
58
|
+
__all__ = [
|
|
59
|
+
# Main client
|
|
60
|
+
"MTNCloud",
|
|
61
|
+
# Configuration
|
|
62
|
+
"MTNCloudConfig",
|
|
63
|
+
# Exceptions
|
|
64
|
+
"MTNCloudError",
|
|
65
|
+
"AuthenticationError",
|
|
66
|
+
"NotFoundError",
|
|
67
|
+
"ValidationError",
|
|
68
|
+
"RateLimitError",
|
|
69
|
+
"ServerError",
|
|
70
|
+
"ForbiddenError",
|
|
71
|
+
"TimeoutError",
|
|
72
|
+
]
|
mtn_cloud/client.py
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MTN Cloud Client
|
|
3
|
+
================
|
|
4
|
+
|
|
5
|
+
Main client class for interacting with MTN Cloud.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from mtn_cloud.config import MTNCloudConfig
|
|
11
|
+
from mtn_cloud.http import HTTPClient
|
|
12
|
+
from mtn_cloud.models.user import User
|
|
13
|
+
from mtn_cloud.resources.clouds import CloudsResource
|
|
14
|
+
from mtn_cloud.resources.groups import GroupsResource
|
|
15
|
+
from mtn_cloud.resources.instances import InstancesResource
|
|
16
|
+
from mtn_cloud.resources.networks import NetworksResource
|
|
17
|
+
from mtn_cloud.resources.plans import PlansResource
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class MTNCloud:
|
|
21
|
+
"""
|
|
22
|
+
MTN Cloud client for interacting with the Morpheus API.
|
|
23
|
+
|
|
24
|
+
This is the main entry point for the SDK. Initialize with your
|
|
25
|
+
API token or credentials to start making API calls.
|
|
26
|
+
|
|
27
|
+
Example:
|
|
28
|
+
```python
|
|
29
|
+
from mtn_cloud import MTNCloud
|
|
30
|
+
|
|
31
|
+
# Initialize with token
|
|
32
|
+
cloud = MTNCloud(token="your-api-token")
|
|
33
|
+
|
|
34
|
+
# Or with username/password
|
|
35
|
+
cloud = MTNCloud(
|
|
36
|
+
username="user@example.com",
|
|
37
|
+
password="your-password",
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
# List instances
|
|
41
|
+
for instance in cloud.instances.list():
|
|
42
|
+
print(f"{instance.name}: {instance.status}")
|
|
43
|
+
|
|
44
|
+
# Create an instance
|
|
45
|
+
instance = cloud.instances.create(
|
|
46
|
+
name="my-app",
|
|
47
|
+
cloud_id=1,
|
|
48
|
+
group_id=1,
|
|
49
|
+
instance_type_code="MTN-CS10",
|
|
50
|
+
layout_id=327,
|
|
51
|
+
plan_id=6923,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Use as context manager
|
|
55
|
+
with MTNCloud(token="xxx") as cloud:
|
|
56
|
+
instances = cloud.instances.list()
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Attributes:
|
|
60
|
+
instances: Manage compute instances
|
|
61
|
+
networks: Manage networks
|
|
62
|
+
groups: Manage groups (sites)
|
|
63
|
+
clouds: Manage clouds (zones)
|
|
64
|
+
plans: Manage service plans
|
|
65
|
+
|
|
66
|
+
Environment Variables:
|
|
67
|
+
MTN_CLOUD_TOKEN: API access token
|
|
68
|
+
MTN_CLOUD_URL: API URL (default: https://console.cloud.mtn.ng)
|
|
69
|
+
MTN_CLOUD_TIMEOUT: Request timeout in seconds
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
def __init__(
|
|
73
|
+
self,
|
|
74
|
+
token: str | None = None,
|
|
75
|
+
username: str | None = None,
|
|
76
|
+
password: str | None = None,
|
|
77
|
+
url: str | None = None,
|
|
78
|
+
timeout: float | None = None,
|
|
79
|
+
verify_ssl: bool = True,
|
|
80
|
+
config: MTNCloudConfig | None = None,
|
|
81
|
+
) -> None:
|
|
82
|
+
"""
|
|
83
|
+
Initialize the MTN Cloud client.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
token: API access token
|
|
87
|
+
username: Username for authentication
|
|
88
|
+
password: Password for authentication
|
|
89
|
+
url: API base URL (default: https://console.cloud.mtn.ng)
|
|
90
|
+
timeout: Request timeout in seconds
|
|
91
|
+
verify_ssl: Verify SSL certificates
|
|
92
|
+
config: Full configuration object (overrides other args)
|
|
93
|
+
"""
|
|
94
|
+
# Build config from arguments or use provided config
|
|
95
|
+
if config:
|
|
96
|
+
self._config = config
|
|
97
|
+
else:
|
|
98
|
+
config_kwargs: dict[str, Any] = {}
|
|
99
|
+
|
|
100
|
+
if token:
|
|
101
|
+
config_kwargs["token"] = token
|
|
102
|
+
if username:
|
|
103
|
+
config_kwargs["username"] = username
|
|
104
|
+
if password:
|
|
105
|
+
config_kwargs["password"] = password
|
|
106
|
+
if url:
|
|
107
|
+
config_kwargs["url"] = url
|
|
108
|
+
if timeout:
|
|
109
|
+
config_kwargs["timeout"] = timeout
|
|
110
|
+
if not verify_ssl:
|
|
111
|
+
config_kwargs["verify_ssl"] = verify_ssl
|
|
112
|
+
|
|
113
|
+
self._config = MTNCloudConfig(**config_kwargs)
|
|
114
|
+
|
|
115
|
+
# Initialize HTTP client
|
|
116
|
+
self._http = HTTPClient(self._config)
|
|
117
|
+
|
|
118
|
+
# Initialize resource managers
|
|
119
|
+
self._instances: InstancesResource | None = None
|
|
120
|
+
self._networks: NetworksResource | None = None
|
|
121
|
+
self._groups: GroupsResource | None = None
|
|
122
|
+
self._clouds: CloudsResource | None = None
|
|
123
|
+
self._plans: PlansResource | None = None
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def instances(self) -> InstancesResource:
|
|
127
|
+
"""
|
|
128
|
+
Access the instances resource manager.
|
|
129
|
+
|
|
130
|
+
Example:
|
|
131
|
+
```python
|
|
132
|
+
# List instances
|
|
133
|
+
instances = cloud.instances.list()
|
|
134
|
+
|
|
135
|
+
# Create instance
|
|
136
|
+
instance = cloud.instances.create(...)
|
|
137
|
+
|
|
138
|
+
# Get instance
|
|
139
|
+
instance = cloud.instances.get(123)
|
|
140
|
+
```
|
|
141
|
+
"""
|
|
142
|
+
if self._instances is None:
|
|
143
|
+
self._instances = InstancesResource(self._http)
|
|
144
|
+
return self._instances
|
|
145
|
+
|
|
146
|
+
@property
|
|
147
|
+
def networks(self) -> NetworksResource:
|
|
148
|
+
"""
|
|
149
|
+
Access the networks resource manager.
|
|
150
|
+
|
|
151
|
+
Example:
|
|
152
|
+
```python
|
|
153
|
+
# List networks
|
|
154
|
+
networks = cloud.networks.list()
|
|
155
|
+
|
|
156
|
+
# Get network
|
|
157
|
+
network = cloud.networks.get(123)
|
|
158
|
+
```
|
|
159
|
+
"""
|
|
160
|
+
if self._networks is None:
|
|
161
|
+
self._networks = NetworksResource(self._http)
|
|
162
|
+
return self._networks
|
|
163
|
+
|
|
164
|
+
@property
|
|
165
|
+
def groups(self) -> GroupsResource:
|
|
166
|
+
"""
|
|
167
|
+
Access the groups resource manager.
|
|
168
|
+
|
|
169
|
+
Example:
|
|
170
|
+
```python
|
|
171
|
+
# List groups
|
|
172
|
+
groups = cloud.groups.list()
|
|
173
|
+
|
|
174
|
+
# Get group
|
|
175
|
+
group = cloud.groups.get(1)
|
|
176
|
+
```
|
|
177
|
+
"""
|
|
178
|
+
if self._groups is None:
|
|
179
|
+
self._groups = GroupsResource(self._http)
|
|
180
|
+
return self._groups
|
|
181
|
+
|
|
182
|
+
@property
|
|
183
|
+
def clouds(self) -> CloudsResource:
|
|
184
|
+
"""
|
|
185
|
+
Access the clouds resource manager.
|
|
186
|
+
|
|
187
|
+
Example:
|
|
188
|
+
```python
|
|
189
|
+
# List clouds
|
|
190
|
+
clouds = cloud.clouds.list()
|
|
191
|
+
|
|
192
|
+
# Get cloud
|
|
193
|
+
c = cloud.clouds.get(1)
|
|
194
|
+
```
|
|
195
|
+
"""
|
|
196
|
+
if self._clouds is None:
|
|
197
|
+
self._clouds = CloudsResource(self._http)
|
|
198
|
+
return self._clouds
|
|
199
|
+
|
|
200
|
+
@property
|
|
201
|
+
def plans(self) -> PlansResource:
|
|
202
|
+
"""
|
|
203
|
+
Access the service plans resource manager.
|
|
204
|
+
|
|
205
|
+
Example:
|
|
206
|
+
```python
|
|
207
|
+
# List plans
|
|
208
|
+
plans = cloud.plans.list()
|
|
209
|
+
|
|
210
|
+
# Get plan
|
|
211
|
+
plan = cloud.plans.get(6923)
|
|
212
|
+
```
|
|
213
|
+
"""
|
|
214
|
+
if self._plans is None:
|
|
215
|
+
self._plans = PlansResource(self._http)
|
|
216
|
+
return self._plans
|
|
217
|
+
|
|
218
|
+
def whoami(self) -> User:
|
|
219
|
+
"""
|
|
220
|
+
Get the current authenticated user.
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
Current user information
|
|
224
|
+
|
|
225
|
+
Example:
|
|
226
|
+
```python
|
|
227
|
+
user = cloud.whoami()
|
|
228
|
+
print(f"Logged in as: {user.username}")
|
|
229
|
+
print(f"Email: {user.email}")
|
|
230
|
+
```
|
|
231
|
+
"""
|
|
232
|
+
response = self._http.get("/whoami")
|
|
233
|
+
user_data = response.get("user", response)
|
|
234
|
+
return User.model_validate(user_data)
|
|
235
|
+
|
|
236
|
+
def ping(self) -> bool:
|
|
237
|
+
"""
|
|
238
|
+
Check if the API is reachable and authentication is valid.
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
True if connection is successful
|
|
242
|
+
|
|
243
|
+
Example:
|
|
244
|
+
```python
|
|
245
|
+
if cloud.ping():
|
|
246
|
+
print("Connected!")
|
|
247
|
+
```
|
|
248
|
+
"""
|
|
249
|
+
try:
|
|
250
|
+
self.whoami()
|
|
251
|
+
return True
|
|
252
|
+
except Exception:
|
|
253
|
+
return False
|
|
254
|
+
|
|
255
|
+
@property
|
|
256
|
+
def config(self) -> MTNCloudConfig:
|
|
257
|
+
"""Get the current configuration."""
|
|
258
|
+
return self._config
|
|
259
|
+
|
|
260
|
+
@property
|
|
261
|
+
def api_url(self) -> str:
|
|
262
|
+
"""Get the API URL."""
|
|
263
|
+
return self._config.api_url
|
|
264
|
+
|
|
265
|
+
def close(self) -> None:
|
|
266
|
+
"""Close the HTTP session."""
|
|
267
|
+
self._http.close()
|
|
268
|
+
|
|
269
|
+
def __enter__(self) -> "MTNCloud":
|
|
270
|
+
"""Context manager entry."""
|
|
271
|
+
return self
|
|
272
|
+
|
|
273
|
+
def __exit__(self, *args: Any) -> None:
|
|
274
|
+
"""Context manager exit."""
|
|
275
|
+
self.close()
|
|
276
|
+
|
|
277
|
+
def __repr__(self) -> str:
|
|
278
|
+
return f"<MTNCloud url={self._config.url!r}>"
|
mtn_cloud/config.py
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MTN Cloud SDK Configuration
|
|
3
|
+
===========================
|
|
4
|
+
|
|
5
|
+
Configuration management using pydantic-settings.
|
|
6
|
+
Supports environment variables and explicit configuration.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from pydantic import Field, field_validator
|
|
10
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class MTNCloudConfig(BaseSettings):
|
|
14
|
+
"""
|
|
15
|
+
Configuration for the MTN Cloud SDK.
|
|
16
|
+
|
|
17
|
+
Configuration can be provided via:
|
|
18
|
+
1. Constructor arguments
|
|
19
|
+
2. Environment variables (prefixed with MTN_CLOUD_)
|
|
20
|
+
3. Default values
|
|
21
|
+
|
|
22
|
+
Example:
|
|
23
|
+
```python
|
|
24
|
+
# From environment: MTN_CLOUD_TOKEN=xxx
|
|
25
|
+
config = MTNCloudConfig()
|
|
26
|
+
|
|
27
|
+
# Explicit configuration
|
|
28
|
+
config = MTNCloudConfig(
|
|
29
|
+
token="your-token",
|
|
30
|
+
timeout=60,
|
|
31
|
+
)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Environment Variables:
|
|
35
|
+
MTN_CLOUD_TOKEN: API access token
|
|
36
|
+
MTN_CLOUD_URL: API base URL
|
|
37
|
+
MTN_CLOUD_TIMEOUT: Request timeout in seconds
|
|
38
|
+
MTN_CLOUD_MAX_RETRIES: Maximum retry attempts
|
|
39
|
+
MTN_CLOUD_VERIFY_SSL: Enable/disable SSL verification
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
model_config = SettingsConfigDict(
|
|
43
|
+
env_prefix="MTN_CLOUD_",
|
|
44
|
+
env_file=".env",
|
|
45
|
+
env_file_encoding="utf-8",
|
|
46
|
+
case_sensitive=False,
|
|
47
|
+
extra="ignore",
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# Authentication
|
|
51
|
+
token: str | None = Field(
|
|
52
|
+
default=None,
|
|
53
|
+
description="MTN Cloud API access token",
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
username: str | None = Field(
|
|
57
|
+
default=None,
|
|
58
|
+
description="MTN Cloud username (alternative to token)",
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
password: str | None = Field(
|
|
62
|
+
default=None,
|
|
63
|
+
description="MTN Cloud password (use with username)",
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# API Configuration
|
|
67
|
+
url: str = Field(
|
|
68
|
+
default="https://console.cloud.mtn.ng",
|
|
69
|
+
description="MTN Cloud API base URL",
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
api_version: str = Field(
|
|
73
|
+
default="v1",
|
|
74
|
+
description="API version (not currently used, for future)",
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# Request Configuration
|
|
78
|
+
timeout: float = Field(
|
|
79
|
+
default=30.0,
|
|
80
|
+
ge=1.0,
|
|
81
|
+
le=300.0,
|
|
82
|
+
description="Request timeout in seconds",
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
max_retries: int = Field(
|
|
86
|
+
default=3,
|
|
87
|
+
ge=0,
|
|
88
|
+
le=10,
|
|
89
|
+
description="Maximum number of retry attempts",
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
retry_delay: float = Field(
|
|
93
|
+
default=1.0,
|
|
94
|
+
ge=0.1,
|
|
95
|
+
le=60.0,
|
|
96
|
+
description="Base delay between retries (exponential backoff)",
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# SSL Configuration
|
|
100
|
+
verify_ssl: bool = Field(
|
|
101
|
+
default=True,
|
|
102
|
+
description="Verify SSL certificates",
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# Client Configuration
|
|
106
|
+
user_agent: str = Field(
|
|
107
|
+
default="mtn-cloud-python/0.1.0",
|
|
108
|
+
description="User-Agent header for API requests",
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
# Debug
|
|
112
|
+
debug: bool = Field(
|
|
113
|
+
default=False,
|
|
114
|
+
description="Enable debug logging",
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
@field_validator("url")
|
|
118
|
+
@classmethod
|
|
119
|
+
def validate_url(cls, v: str) -> str:
|
|
120
|
+
"""Ensure URL doesn't have trailing slash."""
|
|
121
|
+
return v.rstrip("/")
|
|
122
|
+
|
|
123
|
+
@property
|
|
124
|
+
def api_url(self) -> str:
|
|
125
|
+
"""Get the full API URL."""
|
|
126
|
+
return f"{self.url}/api"
|
|
127
|
+
|
|
128
|
+
@property
|
|
129
|
+
def has_credentials(self) -> bool:
|
|
130
|
+
"""Check if any authentication credentials are configured."""
|
|
131
|
+
return bool(self.token or (self.username and self.password))
|
|
132
|
+
|
|
133
|
+
def get_auth_method(self) -> str:
|
|
134
|
+
"""Determine the authentication method to use."""
|
|
135
|
+
if self.token:
|
|
136
|
+
return "token"
|
|
137
|
+
elif self.username and self.password:
|
|
138
|
+
return "credentials"
|
|
139
|
+
return "none"
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
# Default configuration instance
|
|
143
|
+
default_config = MTNCloudConfig()
|