Skip to main content

Community Python SDK for MTN Cloud (Morpheus) - Deploy and manage cloud resources with ease

Project description

MTN Cloud Python SDK

PyPI version Tests Python 3.10+ License: MIT

A Python SDK for MTN Cloud (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

pip install mtn-cloud

Quick Start

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

# 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

Reference Data

Groups

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

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()
View all instance types
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

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)
View all service plans
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

Networks

# List networks (optionally by cloud/zone id)
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")

# Discover network type IDs for OpenStack
network_types = cloud.networks.list_types(openstack_only=True)
for nt in network_types:
    print(nt.id, nt.code, nt.name)

# Create network (OpenStack-focused fields)
new_network = cloud.networks.create(
    name="mtn-prod-net",
    cloud_id=1,          # /api/zones/{id}
    group_id=621,        # /api/groups/{id}
    type_id=network_types[0].id,
    cidr="192.168.50.0/24",
    gateway="192.168.50.1",
    dns_primary="8.8.8.8",
    dhcp_server=True,
    visibility="private",
)

# Update network
updated = cloud.networks.update(
    new_network.id,
    description="Production network",
    allow_static_override=True,
)

# List subnets under a network
subnets = cloud.networks.list_subnets(new_network.id)

# Delete network
cloud.networks.delete(new_network.id)

OpenStack Network Config Examples

# Example 1: Tenant-private network with DHCP and DNS
private_net = cloud.networks.create(
    name="tenant-a-private",
    cloud_id=1,
    group_id=621,
    type_id=network_types[0].id,
    cidr="10.42.10.0/24",
    gateway="10.42.10.1",
    dns_primary="8.8.8.8",
    dns_secondary="1.1.1.1",
    dhcp_server=True,
    allow_static_override=False,
    assign_public_ip=False,
    visibility="private",
)

# Example 2: Shared network with controlled group access
shared_net = cloud.networks.create(
    name="shared-services-net",
    cloud_id=1,
    group_id=621,
    type_id=network_types[0].id,
    cidr="10.42.20.0/24",
    gateway="10.42.20.1",
    visibility="public",
    resource_permission_all=False,
    resource_permission_site_ids=[621],  # Group IDs allowed to use it
)

# Example 3: Update network routing/security options
cloud.networks.update(
    private_net.id,
    no_proxy="169.254.169.254,10.0.0.0/8",
    appliance_url_proxy_bypass=True,
    search_domains="svc.internal.example",
)

Creating an Instance

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}")

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

# 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

# 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

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

from mtn_cloud import MTNCloud, MTNCloudConfig

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

cloud = MTNCloud(config=config)

Testing and Coverage

# Run tests
pytest -v

# Run tests with coverage (terminal + XML + HTML reports)
pytest -v --cov=mtn_cloud --cov-report=term-missing --cov-report=xml:coverage/coverage.xml --cov-report=html:coverage/html

In CI (.github/workflows/test.yml), coverage is generated during tests, uploaded as an artifact, and summarized in the GitHub Actions job summary for the ubuntu-latest + 3.12 run.


API Limitations

Endpoint Status Workaround
/api/service-plans ❌ Blocked See 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 for details.

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

Links

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mtn_cloud-0.1.13.tar.gz (40.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mtn_cloud-0.1.13-py3-none-any.whl (44.6 kB view details)

Uploaded Python 3

File details

Details for the file mtn_cloud-0.1.13.tar.gz.

File metadata

  • Download URL: mtn_cloud-0.1.13.tar.gz
  • Upload date:
  • Size: 40.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for mtn_cloud-0.1.13.tar.gz
Algorithm Hash digest
SHA256 4545dea61fedcc7ea3fdbcd0037a05b9c33acca5bfab2bf45c8e0654d516295d
MD5 a324844b93ab63ee1af02ed61c09f6d6
BLAKE2b-256 100707d092a49ef1a3086a68f507ff657d3f504acfec0398110b8a3994fdbd3f

See more details on using hashes here.

Provenance

The following attestation bundles were made for mtn_cloud-0.1.13.tar.gz:

Publisher: publish.yml on mahveotm/mtn-cloud-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file mtn_cloud-0.1.13-py3-none-any.whl.

File metadata

  • Download URL: mtn_cloud-0.1.13-py3-none-any.whl
  • Upload date:
  • Size: 44.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for mtn_cloud-0.1.13-py3-none-any.whl
Algorithm Hash digest
SHA256 d954d09db9d577187aff6739c9645d5f9116e4a6a6e2e849a2bbfc158ac66643
MD5 7f9d7348d76d76a39a4e137cd493d38e
BLAKE2b-256 3ed1aca6c31fc94b65b39af00e4e882793f503ec03d1dc6195f4893d06e747b6

See more details on using hashes here.

Provenance

The following attestation bundles were made for mtn_cloud-0.1.13-py3-none-any.whl:

Publisher: publish.yml on mahveotm/mtn-cloud-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page