Metadata-Version: 2.4
Name: mtn-cloud
Version: 0.1.10
Summary: Community Python SDK for MTN Cloud (Morpheus) - Deploy and manage cloud resources with ease
Project-URL: Homepage, https://github.com/mahveotm/mtn-cloud-python
Project-URL: Documentation, https://github.com/mahveotm/mtn-cloud-python#readme
Project-URL: Repository, https://github.com/mahveotm/mtn-cloud-python
Project-URL: Issues, https://github.com/mahveotm/mtn-cloud-python/issues
Author-email: Marvellous Osuolale <m@rvellous.com>
Maintainer-email: Marvellous Osuolale <m@rvellous.com>
License: MIT
License-File: LICENSE
Keywords: api,cloud,devops,infrastructure,morpheus,mtn,sdk
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Systems Administration
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: pydantic-settings>=2.0.0
Requires-Dist: pydantic>=2.0.0
Requires-Dist: requests>=2.28.0
Requires-Dist: typing-extensions>=4.5.0
Requires-Dist: urllib3>=2.0.0
Provides-Extra: dev
Requires-Dist: mypy>=1.0.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
Requires-Dist: pytest>=7.0.0; extra == 'dev'
Requires-Dist: respx>=0.20.0; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Requires-Dist: types-requests>=2.28.0; extra == 'dev'
Description-Content-Type: text/markdown

# MTN Cloud Python SDK

[![PyPI version](https://img.shields.io/pypi/v/mtn-cloud.svg)](https://pypi.org/project/mtn-cloud/)
[![Tests](https://github.com/mahveotm/mtn-cloud-python/actions/workflows/test.yml/badge.svg)](https://github.com/mahveotm/mtn-cloud-python/actions/workflows/test.yml)
[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

A Python SDK for [MTN Cloud](https://console.cloud.mtn.ng) (Morpheus).

> **⚠️ Disclaimer:** Unofficial community project. Not affiliated with MTN Nigeria.

## Features

- 🚀 **Simple, Pythonic API** - Intuitive interface for all cloud operations
- 📦 **Typed Models** - Full Pydantic models with IDE autocomplete
- 🔄 **Automatic Retries** - Built-in retry logic with exponential backoff
- 🔐 **Flexible Auth** - Token or username/password authentication
- ⚡ **Resource Managers** - Organized access to instances, networks, groups
- 🛡️ **Error Handling** - Specific exceptions for different error types

## Installation

```bash
pip install mtn-cloud
```

## Quick Start

```python
from mtn_cloud import MTNCloud

cloud = MTNCloud(token="your-api-token")

# Check connection
user = cloud.whoami()
print(f"Connected as: {user.username}")

# List instances
for instance in cloud.instances.list():
    print(f"{instance.name}: {instance.status} ({instance.primary_ip})")
```

## Authentication

```python
# Using token (recommended)
cloud = MTNCloud(token="your-api-token")

# Or via environment variable
# export MTN_CLOUD_TOKEN="your-api-token"
cloud = MTNCloud()

# Using username/password
cloud = MTNCloud(username="user@example.com", password="your-password")
```

**Getting your API token:** MTN Cloud Console → User Icon (top right) → User Settings → API Access

---

## Creating an Instance

```python
from mtn_cloud import MTNCloud
from mtn_cloud.models import InstanceVolume, InstanceNetwork

cloud = MTNCloud(token="your-api-token")

instance = cloud.instances.create(
    name="my-server",
    cloud="MTNNG_CLOUD_AZ_1",
    type="MTN-CS10",                     # Instance type code
    group="MTNNG_CLOUD_AZ_1",            # Group name (resolved to ID automatically)
    layout=327,                          # Layout ID for MTN-CS10
    plan=6775,                           # Service plan ID (G2S2: 2 cores, 2GB RAM)
    resource_pool_id="pool-214",
    availability_zone="Lagos-AZ-1-fd1",
    security_group="default",
    os_external_network_id="public-network-01",
    volumes=[
        InstanceVolume(
            name="root",
            size=20,                     # Size in GB
            storage_type=11,
            datastore_id="auto",
        ),
    ],
    network_interfaces=[
        InstanceNetwork(
            network_id="network-298",
            ip_address="192.168.100.50",  # Optional: static IP
        ),
    ],
    labels=["production", "web"],
)

print(f"Instance created: {instance.name} (ID: {instance.id})")

# Wait for instance to be running
instance = cloud.instances.wait_until_running(instance.id, timeout=300)
print(f"Instance is now: {instance.status}")
print(f"IP Address: {instance.primary_ip}")
```

---

## Reference Data

### Groups

```python
groups = cloud.groups.list()
for group in groups:
    print(f"{group.name} (ID: {group.id})")

group = cloud.groups.get_by_name("MTNNG_CLOUD_AZ_1")
```

| Name | ID |
|------|-----|
| MTNNG_CLOUD_AZ_1 | 621 |

### Instance Types

```python
instance_types = cloud.instance_types.list()
for it in instance_types:
    print(f"{it.code}: {it.name} (Layout ID: {it.default_layout_id})")

# Get by code
centos = cloud.instance_types.get_by_code("MTN-CS10")
print(f"Layout ID: {centos.default_layout_id}")

# List by category
os_types = cloud.instance_types.list_os()
db_types = cloud.instance_types.list_databases()
web_types = cloud.instance_types.list_web()
app_types = cloud.instance_types.list_apps()
```

<details>
<summary><strong>View all instance types</strong></summary>

| Category | Code | Name | Layout ID |
|----------|------|------|-----------|
| OS | MTN-CS10 | CentOS Stream 10 | 327 |
| OS | MTN-CS9 | CentOS Stream 9 | 394 |
| OS | MTN-U24.04LTS | Ubuntu Server 24.04.3LTS | 309 |
| OS | MTN-U22.04LTS | Ubuntu Server 22.04.5LTS | 325 |
| OS | MTN-D12 | Debian 12 | 283 |
| OS | MTN-D13 | Debian 13 | 330 |
| OS | MTN-RL9 | Rocky Linux 9.6 | 395 |
| OS | MTN-RL10 | Rocky Linux 10 | 397 |
| OS | MTN-F42 | Fedora 42 | 392 |
| OS | MTN-F43 | Fedora 43 | 393 |
| OS | MTN-WINSVR2019 | Windows Server 2019 (BYOL) | 300 |
| OS | MTN-WINSVR2022 | Windows Server 2022 (BYOL) | 301 |
| Database | MTN-MySQL01 | MySQL Single-Node | 375 |
| Database | MTN-Postgres01 | PostgreSQL Single-Node | 333 |
| Web | MTN-APACHEWS | Apache Web Server | 372 |
| Web | MTN-NGINXWS | Nginx Web Server | 374 |
| Apps | MTN-LAMP01 | LAMP Stack Server | 379 |
| Apps | MTN-LEMP01 | LEMP Stack Server | 380 |
| Apps | MK8S-M | Kubernetes Master | 386 |
| Apps | MK8S-W | Kubernetes Worker | 387 |
| Network | MTN-WGVPN-01 | WireGuard SSL VPN | 364 |

</details>

### Service Plans

> **Note:** The Service Plans API is restricted. Use the reference below.

**Naming Convention:**
- `G{cores}S{ram}` - General Purpose (e.g., G2S4 = 2 cores, 4GB RAM)
- `Ge{cores}{tier}{ram}` - General Enterprise (e.g., Ge32M64 = 32 cores, 64GB RAM)
- Tiers: `S` (Standard), `M` (Medium), `L` (Large)

<details>
<summary><strong>View all service plans</strong></summary>

| Plan | ID | Cores | RAM (GB) | Category |
|------|-----|-------|----------|----------|
| G1S1 | 6772 | 1 | 1 | General |
| G1S2 | 6773 | 1 | 2 | General |
| G1S4 | 6774 | 1 | 4 | General |
| G2S2 | 6775 | 2 | 2 | General |
| G2S4 | 6776 | 2 | 4 | General |
| G2S8 | 6777 | 2 | 8 | General |
| G2S16 | 6778 | 2 | 16 | General |
| G4S4 | 6779 | 4 | 4 | General |
| G4S8 | 6780 | 4 | 8 | General |
| G4S16 | 6781 | 4 | 16 | General |
| G4S32 | 6782 | 4 | 32 | General |
| G8S8 | 6783 | 8 | 8 | General |
| G8S16 | 6784 | 8 | 16 | General |
| G8S32 | 6785 | 8 | 32 | General |
| G8S64 | 6786 | 8 | 64 | General |
| Ge16S16 | 6787 | 16 | 16 | Enterprise |
| Ge16S32 | 6788 | 16 | 32 | Enterprise |
| Ge16S48 | 6789 | 16 | 48 | Enterprise |
| Ge16S64 | 6790 | 16 | 64 | Enterprise |
| Ge32M32 | 6791 | 32 | 32 | Enterprise |
| Ge32M64 | 6792 | 32 | 64 | Enterprise |
| Ge32M72 | 6793 | 32 | 72 | Enterprise |
| Ge32M128 | 6794 | 32 | 128 | Enterprise |
| Ge64L64 | 6795 | 64 | 64 | Enterprise |
| Ge64L128 | 6796 | 64 | 128 | Enterprise |
| Ge64L256 | 6797 | 64 | 256 | Enterprise |
| Ge64L384 | 6798 | 64 | 384 | Enterprise |
| Ge96L128 | 6799 | 96 | 128 | Enterprise |
| Ge96L256 | 6800 | 96 | 256 | Enterprise |
| Ge96L384 | 6801 | 96 | 384 | Enterprise |

</details>

### Networks

```python
networks = cloud.networks.list()
for network in networks:
    print(f"{network.name} (ID: {network.id})")

network = cloud.networks.get(298)
print(f"Network: {network.name}")
print(f"CIDR: {network.cidr}")
print(f"Gateway: {network.gateway}")

network = cloud.networks.get_by_name("my-network")
```

### Configuration Values

| Parameter | Example | Description |
|-----------|---------|-------------|
| `cloud` | `"MTNNG_CLOUD_AZ_1"` | Cloud/Zone name |
| `resource_pool_id` | `"pool-214"` | Resource pool identifier |
| `availability_zone` | `"Lagos-AZ-1-fd1"` | Availability zone |
| `security_group` | `"default"` | Security group (default always exists) |
| `os_external_network_id` | `"public-network-01"` | External network for floating IP |
| `storage_type` | `11` | Storage type ID |

---

## Managing Instances

```python
# List all instances
instances = cloud.instances.list()

# Filter instances
running = cloud.instances.list(status="running")
by_name = cloud.instances.list(name="web-server")

# Get a specific instance
instance = cloud.instances.get(123)
print(f"Name: {instance.name}")
print(f"Status: {instance.status}")
print(f"IP: {instance.primary_ip}")

# Get instance by name
instance = cloud.instances.get_by_name("my-app")

# Instance actions
instance.stop()
instance.start()
instance.restart()

# Or use the resource manager
cloud.instances.stop(123)
cloud.instances.start(123)

# Resize instance
cloud.instances.resize(123, plan_id=6776)  # Upgrade to G2S4

# Delete instance
cloud.instances.delete(123)

# Force delete with volume preservation
cloud.instances.delete(123, force=True, preserve_volumes=True)
```

---

## Working with Instance Types

```python
# List all instance types
instance_types = cloud.instance_types.list()
for it in instance_types:
    print(f"{it.code}: {it.name} (Layout ID: {it.default_layout_id})")

# Get instance type by code
centos = cloud.instance_types.get_by_code("MTN-CS10")
print(f"ID: {centos.id}")
print(f"Name: {centos.name}")
print(f"Code: {centos.code}")
print(f"Description: {centos.description}")
print(f"Default Layout ID: {centos.default_layout_id}")

# Access layouts
for layout in centos.layouts:
    print(f"  Layout: {layout.id} - {layout.name}")
```

---

## Error Handling

```python
from mtn_cloud import (
    MTNCloud,
    MTNCloudError,
    AuthenticationError,
    NotFoundError,
    ForbiddenError,
    ValidationError,
    RateLimitError,
    TimeoutError,
)

cloud = MTNCloud(token="xxx")

try:
    instance = cloud.instances.get(99999)
except NotFoundError as e:
    print(f"Instance not found: {e}")
except AuthenticationError as e:
    print(f"Auth failed: {e}")
except ForbiddenError as e:
    print(f"Access denied: {e}")
except ValidationError as e:
    print(f"Invalid request: {e}")
except RateLimitError as e:
    print(f"Rate limited. Retry after: {e.retry_after}s")
except TimeoutError as e:
    print(f"Request timed out: {e}")
except MTNCloudError as e:
    print(f"API error: {e}")
```

---

## Configuration

### Environment Variables

| Variable | Description | Default |
|----------|-------------|---------|
| `MTN_CLOUD_TOKEN` | API access token | - |
| `MTN_CLOUD_URL` | API base URL | `https://console.cloud.mtn.ng` |
| `MTN_CLOUD_TIMEOUT` | Request timeout (seconds) | `30` |
| `MTN_CLOUD_MAX_RETRIES` | Max retry attempts | `3` |
| `MTN_CLOUD_VERIFY_SSL` | Verify SSL certs | `true` |

### Programmatic Configuration

```python
from mtn_cloud import MTNCloud, MTNCloudConfig

config = MTNCloudConfig(
    token="your-token",
    timeout=60,
    max_retries=5,
    verify_ssl=True,
)

cloud = MTNCloud(config=config)
```

---

## API Limitations

| Endpoint | Status | Workaround |
|----------|--------|------------|
| `/api/service-plans` | ❌ Blocked | See [Service Plans](#service-plans) |
| `/api/zones` (Clouds) | ❌ Blocked | Use `MTNNG_CLOUD_AZ_1` |

---

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## License

MIT License - see [LICENSE](LICENSE) for details.

This is an unofficial community project. Not affiliated with MTN.

## Links

- [MTN Cloud Console](https://console.cloud.mtn.ng)
- [Morpheus API Documentation](https://apidocs.morpheusdata.com/)
- [GitHub Repository](https://github.com/mahveotm/mtn-cloud-python)
- [PyPI Package](https://pypi.org/project/mtn-cloud/)
