mtn-cloud 0.2.17__tar.gz → 0.3.1__tar.gz

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 (89) hide show
  1. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/PKG-INFO +49 -23
  2. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/README.md +48 -22
  3. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/docs/advanced-cookbook.md +15 -5
  4. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/docs/api-overview.md +5 -1
  5. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/docs/api-reference/instances.md +35 -2
  6. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/docs/api-reference/networks.md +21 -22
  7. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/docs/index.md +0 -1
  8. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/docs/instances.md +55 -11
  9. mtn_cloud-0.3.1/docs/networking.md +100 -0
  10. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/docs/quickstart.md +45 -16
  11. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/_version.py +1 -1
  12. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/client.py +0 -3
  13. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/config.py +1 -7
  14. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/http.py +0 -13
  15. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/models/__init__.py +9 -2
  16. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/models/base.py +10 -7
  17. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/models/cloud.py +0 -10
  18. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/models/group.py +0 -8
  19. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/models/instance.py +3 -55
  20. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/models/instance_type.py +0 -8
  21. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/models/network.py +46 -19
  22. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/models/plan.py +0 -8
  23. mtn_cloud-0.3.1/src/mtn_cloud/models/resource_pool.py +50 -0
  24. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/models/user.py +0 -6
  25. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/models/volume.py +1 -10
  26. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/resources/instances.py +202 -15
  27. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/resources/networks.py +70 -46
  28. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/resources/security_groups.py +1 -1
  29. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/tests/conftest.py +20 -3
  30. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/tests/test_archive_buckets.py +62 -76
  31. mtn_cloud-0.3.1/tests/test_backups.py +163 -0
  32. mtn_cloud-0.3.1/tests/test_client.py +230 -0
  33. mtn_cloud-0.3.1/tests/test_clouds.py +102 -0
  34. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/tests/test_config.py +65 -58
  35. mtn_cloud-0.3.1/tests/test_groups.py +69 -0
  36. mtn_cloud-0.3.1/tests/test_instance_types.py +370 -0
  37. mtn_cloud-0.3.1/tests/test_instances.py +549 -0
  38. mtn_cloud-0.3.1/tests/test_networks.py +453 -0
  39. mtn_cloud-0.3.1/tests/test_plans.py +107 -0
  40. mtn_cloud-0.3.1/tests/test_security_groups.py +239 -0
  41. mtn_cloud-0.3.1/tests/test_storage_buckets.py +256 -0
  42. mtn_cloud-0.3.1/tests/test_virtual_images.py +117 -0
  43. mtn_cloud-0.2.17/docs/docstring-style.md +0 -50
  44. mtn_cloud-0.2.17/docs/networking.md +0 -100
  45. mtn_cloud-0.2.17/tests/test_client.py +0 -183
  46. mtn_cloud-0.2.17/tests/test_clouds.py +0 -50
  47. mtn_cloud-0.2.17/tests/test_instance_types.py +0 -325
  48. mtn_cloud-0.2.17/tests/test_instances.py +0 -367
  49. mtn_cloud-0.2.17/tests/test_networks.py +0 -256
  50. mtn_cloud-0.2.17/tests/test_storage_buckets.py +0 -152
  51. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/.gitignore +0 -0
  52. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/LICENSE +0 -0
  53. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/docs/api-reference/archive-buckets.md +0 -0
  54. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/docs/api-reference/backups.md +0 -0
  55. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/docs/api-reference/client.md +0 -0
  56. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/docs/api-reference/clouds.md +0 -0
  57. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/docs/api-reference/groups.md +0 -0
  58. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/docs/api-reference/index.md +0 -0
  59. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/docs/api-reference/instance-types.md +0 -0
  60. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/docs/api-reference/plans.md +0 -0
  61. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/docs/api-reference/security-groups.md +0 -0
  62. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/docs/api-reference/storage-buckets.md +0 -0
  63. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/docs/api-reference/virtual-images.md +0 -0
  64. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/docs/backups.md +0 -0
  65. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/docs/security-groups.md +0 -0
  66. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/docs/storage.md +0 -0
  67. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/docs/stylesheets/extra.css +0 -0
  68. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/docs/virtual-images.md +0 -0
  69. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/pyproject.toml +0 -0
  70. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/__init__.py +0 -0
  71. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/exceptions.py +0 -0
  72. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/models/archive.py +0 -0
  73. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/models/backup.py +0 -0
  74. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/models/security_group.py +0 -0
  75. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/models/snapshot.py +0 -0
  76. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/models/storage_bucket.py +0 -0
  77. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/models/virtual_image.py +0 -0
  78. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/resources/__init__.py +0 -0
  79. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/resources/archive_buckets.py +0 -0
  80. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/resources/backups.py +0 -0
  81. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/resources/base.py +0 -0
  82. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/resources/clouds.py +0 -0
  83. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/resources/groups.py +0 -0
  84. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/resources/instance_types.py +0 -0
  85. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/resources/plans.py +0 -0
  86. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/resources/storage_buckets.py +0 -0
  87. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/src/mtn_cloud/resources/virtual_images.py +0 -0
  88. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/tests/__init__.py +0 -0
  89. {mtn_cloud-0.2.17 → mtn_cloud-0.3.1}/tests/test_exceptions.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mtn-cloud
3
- Version: 0.2.17
3
+ Version: 0.3.1
4
4
  Summary: Community Python SDK for MTN Cloud - Deploy and manage cloud resources with ease
5
5
  Project-URL: Homepage, https://github.com/mahveotm/mtn-cloud-python
6
6
  Project-URL: Documentation, https://github.com/mahveotm/mtn-cloud-python#readme
@@ -84,7 +84,6 @@ pip install mtn-cloud
84
84
  - [Advanced Cookbook](https://mahveotm.github.io/mtn-cloud-python/advanced-cookbook/)
85
85
  - [API Overview](https://mahveotm.github.io/mtn-cloud-python/api-overview/)
86
86
  - [API Reference](https://mahveotm.github.io/mtn-cloud-python/api-reference/)
87
- - [Docstring Style Standard](https://mahveotm.github.io/mtn-cloud-python/docstring-style/)
88
87
 
89
88
  ## Quick Start
90
89
 
@@ -127,23 +126,45 @@ User Icon (top-right) -> User Settings -> API Access.
127
126
 
128
127
  ### 1. Discover Reference Data
129
128
 
130
- Use these lookups before provisioning so your scripts stay deterministic.
129
+ Use these lookups before provisioning so your scripts stay deterministic. They all use permission-safe endpoints (the admin-level `clouds`/`plans` endpoints are restricted on most tenant accounts — you don't need them).
131
130
 
132
131
  ```python
133
- # Groups (sites)
134
- groups = cloud.groups.list()
135
- for group in groups[:5]:
136
- print(group.id, group.name)
137
-
138
- # Instance types
139
- types = cloud.instance_types.list_os()
140
- for t in types[:5]:
141
- print(t.code, t.name, t.default_layout_id)
132
+ # Groups (sites) — also carry the cloud/zone IDs you need
133
+ group = cloud.groups.get_by_name("MTNNG_CLOUD_AZ_1")
134
+ print(group.id, group.name, group.cloud_ids)
135
+
136
+ # Instance types — each carries its own default_layout_id
137
+ itype = cloud.instance_types.get_by_code("MTN-CS10")
138
+ print(itype.code, itype.default_layout_id)
139
+
140
+ # Resource pools — where the instance is hosted (the resource_pool_id)
141
+ for pool in cloud.instances.list_resource_pools(group="MTNNG_CLOUD_AZ_1"):
142
+ print(pool.code, pool.name)
143
+
144
+ # Service plans — CPU/memory/storage tiers for this zone + layout
145
+ for plan in cloud.instances.list_service_plans(
146
+ zone_id=group.cloud_ids[0],
147
+ layout_id=itype.default_layout_id,
148
+ group_id=group.id,
149
+ ):
150
+ print(plan["id"], plan["name"])
151
+ ```
152
+
153
+ A **resource pool is required** to create an instance — it's where the VM is hosted. Discover it by group name (the cloud/zone is resolved for you), or fetch one directly by name:
154
+
155
+ ```python
156
+ pool = cloud.instances.get_resource_pool(
157
+ "my-project-name",
158
+ group="MTNNG_CLOUD_AZ_1",
159
+ )
160
+ print(pool.code) # e.g. "pool-214" — pass this as resource_pool_id
142
161
  ```
143
162
 
144
163
  ### 2. Create an Instance
145
164
 
146
165
  ```python
166
+ pool = cloud.instances.get_resource_pool("my-project-name", group="MTNNG_CLOUD_AZ_1")
167
+
147
168
  instance = cloud.instances.create(
148
169
  name="my-server",
149
170
  cloud="MTNNG_CLOUD_AZ_1",
@@ -151,7 +172,7 @@ instance = cloud.instances.create(
151
172
  group="MTNNG_CLOUD_AZ_1",
152
173
  layout=327,
153
174
  plan=6776,
154
- resource_pool_id="pool-214",
175
+ resource_pool_id=pool.code, # "pool-214" — or pass the numeric ID 214
155
176
  labels=["production", "web"],
156
177
  )
157
178
 
@@ -178,27 +199,32 @@ cloud.instances.delete(123, force=True, preserve_volumes=True)
178
199
  ### 4. Work with Networks
179
200
 
180
201
  ```python
202
+ group = cloud.groups.get_by_name("MTNNG_CLOUD_AZ_1")
203
+
181
204
  # List networks
182
- networks = cloud.networks.list(cloud_id=1)
205
+ networks = cloud.networks.list(cloud_id=group.cloud_ids[0])
183
206
  for n in networks[:5]:
184
207
  print(n.id, n.name, n.cidr)
185
208
 
186
- # Create an OpenStack-focused network
187
- network_types = cloud.networks.list_types(openstack_only=True)
209
+ # Create an OpenStack network (needs an OpenStack type + a resource pool)
210
+ net_type = next(
211
+ t for t in cloud.networks.list_types(openstack_only=True)
212
+ if t.code == "openstackPrivate"
213
+ )
214
+ pool = cloud.instances.get_resource_pool("my-project", group=group.name)
215
+
188
216
  new_network = cloud.networks.create(
189
217
  name="mtn-prod-net",
190
- cloud_id=1,
191
- group_id=621,
192
- type_id=network_types[0].id,
218
+ cloud_id=group.cloud_ids[0],
219
+ group_id=group.id,
220
+ type_id=net_type.id,
221
+ resource_pool_id=pool.id,
193
222
  cidr="10.42.10.0/24",
194
223
  gateway="10.42.10.1",
195
224
  dns_primary="8.8.8.8",
196
- visibility="private",
197
- dhcp_server=True,
198
225
  )
199
226
 
200
- # Update and list subnets
201
- cloud.networks.update(new_network.id, description="Production network")
227
+ # Inspect subnets
202
228
  subnets = cloud.networks.list_subnets(new_network.id)
203
229
  print(f"Subnets: {len(subnets)}")
204
230
  ```
@@ -40,7 +40,6 @@ pip install mtn-cloud
40
40
  - [Advanced Cookbook](https://mahveotm.github.io/mtn-cloud-python/advanced-cookbook/)
41
41
  - [API Overview](https://mahveotm.github.io/mtn-cloud-python/api-overview/)
42
42
  - [API Reference](https://mahveotm.github.io/mtn-cloud-python/api-reference/)
43
- - [Docstring Style Standard](https://mahveotm.github.io/mtn-cloud-python/docstring-style/)
44
43
 
45
44
  ## Quick Start
46
45
 
@@ -83,23 +82,45 @@ User Icon (top-right) -> User Settings -> API Access.
83
82
 
84
83
  ### 1. Discover Reference Data
85
84
 
86
- Use these lookups before provisioning so your scripts stay deterministic.
85
+ Use these lookups before provisioning so your scripts stay deterministic. They all use permission-safe endpoints (the admin-level `clouds`/`plans` endpoints are restricted on most tenant accounts — you don't need them).
87
86
 
88
87
  ```python
89
- # Groups (sites)
90
- groups = cloud.groups.list()
91
- for group in groups[:5]:
92
- print(group.id, group.name)
93
-
94
- # Instance types
95
- types = cloud.instance_types.list_os()
96
- for t in types[:5]:
97
- print(t.code, t.name, t.default_layout_id)
88
+ # Groups (sites) — also carry the cloud/zone IDs you need
89
+ group = cloud.groups.get_by_name("MTNNG_CLOUD_AZ_1")
90
+ print(group.id, group.name, group.cloud_ids)
91
+
92
+ # Instance types — each carries its own default_layout_id
93
+ itype = cloud.instance_types.get_by_code("MTN-CS10")
94
+ print(itype.code, itype.default_layout_id)
95
+
96
+ # Resource pools — where the instance is hosted (the resource_pool_id)
97
+ for pool in cloud.instances.list_resource_pools(group="MTNNG_CLOUD_AZ_1"):
98
+ print(pool.code, pool.name)
99
+
100
+ # Service plans — CPU/memory/storage tiers for this zone + layout
101
+ for plan in cloud.instances.list_service_plans(
102
+ zone_id=group.cloud_ids[0],
103
+ layout_id=itype.default_layout_id,
104
+ group_id=group.id,
105
+ ):
106
+ print(plan["id"], plan["name"])
107
+ ```
108
+
109
+ A **resource pool is required** to create an instance — it's where the VM is hosted. Discover it by group name (the cloud/zone is resolved for you), or fetch one directly by name:
110
+
111
+ ```python
112
+ pool = cloud.instances.get_resource_pool(
113
+ "my-project-name",
114
+ group="MTNNG_CLOUD_AZ_1",
115
+ )
116
+ print(pool.code) # e.g. "pool-214" — pass this as resource_pool_id
98
117
  ```
99
118
 
100
119
  ### 2. Create an Instance
101
120
 
102
121
  ```python
122
+ pool = cloud.instances.get_resource_pool("my-project-name", group="MTNNG_CLOUD_AZ_1")
123
+
103
124
  instance = cloud.instances.create(
104
125
  name="my-server",
105
126
  cloud="MTNNG_CLOUD_AZ_1",
@@ -107,7 +128,7 @@ instance = cloud.instances.create(
107
128
  group="MTNNG_CLOUD_AZ_1",
108
129
  layout=327,
109
130
  plan=6776,
110
- resource_pool_id="pool-214",
131
+ resource_pool_id=pool.code, # "pool-214" — or pass the numeric ID 214
111
132
  labels=["production", "web"],
112
133
  )
113
134
 
@@ -134,27 +155,32 @@ cloud.instances.delete(123, force=True, preserve_volumes=True)
134
155
  ### 4. Work with Networks
135
156
 
136
157
  ```python
158
+ group = cloud.groups.get_by_name("MTNNG_CLOUD_AZ_1")
159
+
137
160
  # List networks
138
- networks = cloud.networks.list(cloud_id=1)
161
+ networks = cloud.networks.list(cloud_id=group.cloud_ids[0])
139
162
  for n in networks[:5]:
140
163
  print(n.id, n.name, n.cidr)
141
164
 
142
- # Create an OpenStack-focused network
143
- network_types = cloud.networks.list_types(openstack_only=True)
165
+ # Create an OpenStack network (needs an OpenStack type + a resource pool)
166
+ net_type = next(
167
+ t for t in cloud.networks.list_types(openstack_only=True)
168
+ if t.code == "openstackPrivate"
169
+ )
170
+ pool = cloud.instances.get_resource_pool("my-project", group=group.name)
171
+
144
172
  new_network = cloud.networks.create(
145
173
  name="mtn-prod-net",
146
- cloud_id=1,
147
- group_id=621,
148
- type_id=network_types[0].id,
174
+ cloud_id=group.cloud_ids[0],
175
+ group_id=group.id,
176
+ type_id=net_type.id,
177
+ resource_pool_id=pool.id,
149
178
  cidr="10.42.10.0/24",
150
179
  gateway="10.42.10.1",
151
180
  dns_primary="8.8.8.8",
152
- visibility="private",
153
- dhcp_server=True,
154
181
  )
155
182
 
156
- # Update and list subnets
157
- cloud.networks.update(new_network.id, description="Production network")
183
+ # Inspect subnets
158
184
  subnets = cloud.networks.list_subnets(new_network.id)
159
185
  print(f"Subnets: {len(subnets)}")
160
186
  ```
@@ -103,9 +103,10 @@ from mtn_cloud import (
103
103
  try:
104
104
  cloud.networks.create(
105
105
  name="prod-net",
106
- cloud_id=1,
106
+ cloud_id=4,
107
107
  group_id=621,
108
- type_id=8,
108
+ type_id=12, # an OpenStack type from list_types(openstack_only=True)
109
+ resource_pool_id=230, # numeric pool ID from get_resource_pool(...).id
109
110
  cidr="10.42.10.0/24",
110
111
  )
111
112
  except AuthenticationError:
@@ -149,8 +150,17 @@ with MTNCloud(token="your-api-token") as cloud:
149
150
  Pick explicit templates and plans instead of "first result" in production.
150
151
 
151
152
  ```python
153
+ group = cloud.groups.get_by_name("MTNNG_CLOUD_AZ_1")
152
154
  target_type = cloud.instance_types.get_by_code("MTN-U24.04LTS")
153
- target_plan = cloud.plans.get_by_name("G2S4")
155
+
156
+ plans = cloud.instances.list_service_plans(
157
+ zone_id=group.cloud_ids[0],
158
+ layout_id=target_type.default_layout_id,
159
+ group_id=group.id,
160
+ )
161
+ target_plan = next(p for p in plans if p["name"] == "G2S4")
162
+
163
+ pool = cloud.instances.get_resource_pool("my-project", group=group.name)
154
164
 
155
165
  instance = cloud.instances.create(
156
166
  name="api-worker-01",
@@ -158,8 +168,8 @@ instance = cloud.instances.create(
158
168
  type=target_type.code,
159
169
  group="MTNNG_CLOUD_AZ_1",
160
170
  layout=target_type.default_layout_id,
161
- plan=target_plan.id,
162
- resource_pool_id="pool-214",
171
+ plan=target_plan["id"],
172
+ resource_pool_id=pool.code,
163
173
  )
164
174
  ```
165
175
 
@@ -42,7 +42,10 @@ cloud.instance_types.get_by_code("MTN-CS10")
42
42
  ## Common Write Operations
43
43
 
44
44
  ```python
45
- cloud.instances.create(...)
45
+ # Discover the resource pool an instance will be hosted in
46
+ pool = cloud.instances.get_resource_pool("my-project", group="MTNNG_CLOUD_AZ_1")
47
+
48
+ cloud.instances.create(..., resource_pool_id=pool.code)
46
49
  cloud.instances.update(instance_id=123, name="new-name")
47
50
  cloud.instances.delete(123)
48
51
 
@@ -93,6 +96,7 @@ Resource methods return typed Pydantic models, for example:
93
96
  - `ArchiveFile`
94
97
  - `SecurityGroup`, `SecurityGroupRule`
95
98
  - `Snapshot`
99
+ - `ResourcePool`
96
100
  - `Backup`, `BackupJob`, `BackupResult`
97
101
  - `VirtualImage`
98
102
 
@@ -32,7 +32,7 @@
32
32
  - common API exceptions
33
33
  - `NotFoundError` when no instance matches
34
34
 
35
- ### `create(name: str, *, cloud: str, type: str, group: str, layout: int, plan: int, resource_pool_id: str, description=None, environment=None, labels=None, tags=None, copies=1, layout_size=1, availability_zone=None, security_group="default", os_external_network_id=None, create_user=True, workflow_id=None, shutdown_days=None, expire_days=None, create_backup=None, security_groups=None, ports=None, volumes=None, network_interfaces=None, options=None) -> Instance`
35
+ ### `create(name: str, *, cloud: str, type: str, group: str, layout: int, plan: int, resource_pool_id: str | int, description=None, environment=None, labels=None, tags=None, copies=1, layout_size=1, availability_zone=None, security_group="default", os_external_network_id=None, create_user=True, workflow_id=None, shutdown_days=None, expire_days=None, create_backup=None, security_groups=None, ports=None, volumes=None, network_interfaces=None, options=None) -> Instance`
36
36
 
37
37
  - Endpoint sequence:
38
38
  - `GET /api/groups?name=<group>&max=1` (resolve group name to `group_id`)
@@ -45,7 +45,7 @@
45
45
  - `group`: group/site name (resolved to ID)
46
46
  - `layout`: layout ID
47
47
  - `plan`: service plan ID
48
- - `resource_pool_id`: project resource pool ID
48
+ - `resource_pool_id`: resource pool code (`"pool-214"`) or numeric ID (`214`); both normalize to the code. Discover with `list_resource_pools()` / `get_resource_pool()`
49
49
  - Optional metadata:
50
50
  - `description`, `environment`, `labels`, `tags`
51
51
  - Optional sizing/provisioning:
@@ -197,3 +197,36 @@ Client-side polling helper.
197
197
  - Endpoint: `DELETE /api/instances/{instance_id}/snapshots/{snapshot_id}`
198
198
  - Returns: `True` on success
199
199
  - Raises: common API exceptions
200
+
201
+ ---
202
+
203
+ ## Provisioning discovery
204
+
205
+ These helpers resolve the IDs and codes required by `create()` using permission-safe endpoints (the admin-level `clouds`/`plans` endpoints are restricted on most accounts).
206
+
207
+ ### `list_resource_pools(group=None, *, cloud_id=None, group_id=None, provision_type_code="openstack") -> list[ResourcePool]`
208
+
209
+ - Endpoint: `GET /api/options/zonePools?cloudId={cloud_id}&groupId={group_id}`
210
+ - A resource pool is where an instance is hosted; its `code` is the `resource_pool_id` for `create()`
211
+ - Parameters:
212
+ - `group`: group name or ID — when given, `cloud_id` and `group_id` are resolved from it automatically (the group embeds its cloud/zone IDs)
213
+ - `cloud_id` / `group_id`: explicit IDs, required if `group` is not given
214
+ - `provision_type_code`: provisioning type, defaults to `"openstack"`
215
+ - Returns: `list[ResourcePool]` (group-header rows are filtered out)
216
+ - Raises: `ValueError` if neither `group` nor both `cloud_id`/`group_id` are provided, or the group has no associated cloud
217
+
218
+ ### `get_resource_pool(name: str, *, group=None, cloud_id=None, group_id=None, provision_type_code="openstack") -> ResourcePool`
219
+
220
+ - Fetches a single pool by display name **or** code (e.g. `"pool-214"`)
221
+ - Same resolution rules as `list_resource_pools`
222
+ - Returns: `ResourcePool`
223
+ - Raises: `NotFoundError` if no pool matches `name`
224
+
225
+ ### `list_service_plans(zone_id=None, layout_id=None, group_id=None) -> list[dict[str, Any]]`
226
+
227
+ - Endpoint: `GET /api/instances/service-plans?zoneId={zone_id}&layoutId={layout_id}&siteId={group_id}`
228
+ - Returns available service plans (CPU/memory/storage tiers) scoped to the provisioning context
229
+ - `layout_id` is required by the API; use `instance_type.default_layout_id`
230
+ - Returns: list of plan dicts from response key `plans`
231
+
232
+ **`ResourcePool` model fields:** `id` (numeric), `code` (alias of API `value`, e.g. `"pool-214"`), `name`, `external_id` (OpenStack pool ID), `is_default`.
@@ -32,7 +32,7 @@
32
32
  - Returns: `list[Network]`
33
33
  - Raises: common API exceptions
34
34
 
35
- ### `create(name: str, *, cloud_id: int, group_id: int, type_id=None, display_name=None, labels=None, description=None, cidr=None, gateway=None, dns_primary=None, dns_secondary=None, vlan_id=None, switch_id=None, pool_id=None, allow_static_override=None, assign_public_ip=None, active=None, dhcp_server=None, network_domain_id=None, search_domains=None, network_proxy_id=None, appliance_url_proxy_bypass=None, no_proxy=None, visibility=None, tenant_ids=None, resource_permission_all=None, resource_permission_site_ids=None) -> Network`
35
+ ### `create(name: str, *, cloud_id: int, group_id: int, type_id=None, resource_pool_id=None, display_name=None, labels=None, description=None, cidr=None, gateway=None, dns_primary=None, dns_secondary=None, vlan_id=None, switch_id=None, pool_id=None, allow_static_override=None, assign_public_ip=None, active=None, dhcp_server=None, network_domain_id=None, search_domains=None, network_proxy_id=None, appliance_url_proxy_bypass=None, no_proxy=None, visibility=None, tenant_ids=None, resource_permission_all=None, resource_permission_site_ids=None) -> Network`
36
36
 
37
37
  - Endpoint: `POST /api/networks`
38
38
  - Parameters:
@@ -40,8 +40,11 @@
40
40
  - `name`: network name
41
41
  - `cloud_id`: cloud/zone ID
42
42
  - `group_id`: group/site ID
43
+ - Required for OpenStack networks (e.g. `openstackPrivate`):
44
+ - `type_id`: an OpenStack network type ID from `list_types(openstack_only=True)`
45
+ - `resource_pool_id`: numeric resource pool ID (sent as `zonePool`); get it from `instances.get_resource_pool(...).id`
43
46
  - Common optional network config:
44
- - `type_id`, `display_name`, `labels`, `description`, `cidr`, `gateway`
47
+ - `display_name`, `labels`, `description`, `cidr`, `gateway`
45
48
  - `dns_primary`, `dns_secondary`, `vlan_id`, `switch_id`, `pool_id`
46
49
  - `allow_static_override`, `assign_public_ip`, `active`, `dhcp_server`
47
50
  - `network_domain_id`, `search_domains`, `network_proxy_id`
@@ -88,36 +91,32 @@
88
91
  - Returns: `NetworkTypeInfo`
89
92
  - Raises: common API exceptions
90
93
 
91
- ### `list_floating_ips(*, phrase=None, ip_address=None, ip_status=None, cloud_id=None, server_id=None) -> list[NetworkFloatingIP]`
94
+ ### `list_pools(max_results=None, offset=0, phrase=None) -> list[NetworkPool]`
92
95
 
93
- - Endpoint: `GET /api/networks/floating-ips`
96
+ - Endpoint: `GET /api/networks/pools`
94
97
  - Parameters:
98
+ - `max_results`: maps to query `max`
99
+ - `offset`: pagination offset
95
100
  - `phrase`: search phrase
96
- - `ip_address`: exact IP match
97
- - `ip_status`: provider status value
98
- - `cloud_id`: mapped to `zoneId`
99
- - `server_id`: mapped to `serverId`
100
- - Returns: `list[NetworkFloatingIP]`
101
+ - Returns: `list[NetworkPool]` (each includes its `ip_ranges`, `ip_count`, `free_count`)
101
102
  - Raises: common API exceptions
102
103
 
103
- ### `get_floating_ip(floating_ip_id: int) -> NetworkFloatingIP`
104
+ ### `get_pool(pool_id: int) -> NetworkPool`
104
105
 
105
- - Endpoint: `GET /api/networks/floating-ips/{floating_ip_id}`
106
- - Returns: `NetworkFloatingIP`
106
+ - Endpoint: `GET /api/networks/pools/{pool_id}`
107
+ - Returns: `NetworkPool`
107
108
  - Raises: common API exceptions
108
109
 
109
- ### `allocate_floating_ip(*, network_server_id: int, floating_ip_pool_id: int) -> NetworkFloatingIP`
110
+ ### `list_pool_ips(pool_id: int, *, max_results=None, phrase=None, ip_address=None, hostname=None) -> list[NetworkPoolIp]`
110
111
 
111
- - Endpoint: `POST /api/networks/floating-ips`
112
+ - Endpoint: `GET /api/networks/pools/{pool_id}/ips`
112
113
  - Parameters:
113
- - `network_server_id`: backend network server ID
114
- - `floating_ip_pool_id`: floating IP pool ID
115
- - Returns: `NetworkFloatingIP`
114
+ - `max_results`: maps to query `max`
115
+ - `phrase`: partial match on `ipAddress` or `hostname`
116
+ - `ip_address`: exact IP match
117
+ - `hostname`: exact hostname match
118
+ - Returns: `list[NetworkPoolIp]`
116
119
  - Raises: common API exceptions
117
120
 
118
- ### `release_floating_ip(floating_ip_id: int) -> bool`
119
-
120
- - Endpoint: `PUT /api/networks/floating-ips/{floating_ip_id}/release`
121
- - Returns: `True`
122
- - Raises: common API exceptions
121
+ > **Note:** Standalone floating-IP endpoints (`/api/networks/floating-ips`) are restricted (HTTP 403) on MTN Cloud tenant accounts and are not exposed by the SDK. Assign external/public connectivity at provisioning time via the instance's `os_external_network_id` instead.
123
122
 
@@ -18,7 +18,6 @@ Use this guide in two ways:
18
18
  8. [Advanced Cookbook](./advanced-cookbook.md)
19
19
  9. [API Overview](./api-overview.md)
20
20
  10. [API Reference](./api-reference/index.md)
21
- 11. [Docstring Style Standard](./docstring-style.md)
22
21
 
23
22
  ## Who This Is For
24
23
 
@@ -50,35 +50,79 @@ Lagos AZ3 (Lagos Mainland) is in progress. Each zone has independent power, cool
50
50
 
51
51
  Run this before creating an instance. All IDs are account-specific.
52
52
 
53
+ Provisioning inputs are discovered from **groups, instance types, resource pools, and service plans** — all permission-safe endpoints. (The admin-level `clouds.list()` and `plans.list()` endpoints are restricted on most tenant accounts and will raise `ForbiddenError`; you do not need them.)
54
+
53
55
  ```python
54
56
  from mtn_cloud import MTNCloud
55
57
 
56
58
  cloud = MTNCloud(token="your-api-token")
57
59
 
58
- groups = cloud.groups.list()
59
- clouds = cloud.clouds.list_openstack()
60
- types = cloud.instance_types.list_os()
61
- plans = cloud.plans.list()
62
-
63
- print([(g.id, g.name) for g in groups[:5]])
64
- print([(c.id, c.name) for c in clouds[:5]])
65
- print([(t.code, t.default_layout_id) for t in types[:5]])
66
- print([(p.id, p.name) for p in plans[:5]])
60
+ # 1. Group — gives you the group name AND its cloud/zone IDs
61
+ group = cloud.groups.get_by_name("MTNNG_CLOUD_AZ_1")
62
+ cloud_id = group.cloud_ids[0]
63
+ print("group:", group.id, group.name, "cloud_ids:", group.cloud_ids)
64
+
65
+ # 2. Instance type — each carries its own default_layout_id
66
+ itype = cloud.instance_types.get_by_code("MTN-CS10")
67
+ print("type:", itype.code, "layout:", itype.default_layout_id)
68
+
69
+ # 3. Resource pool — where the instance is hosted (the resource_pool_id)
70
+ for pool in cloud.instances.list_resource_pools(group="MTNNG_CLOUD_AZ_1"):
71
+ print("pool:", pool.code, pool.name)
72
+
73
+ # 4. Service plans — available CPU/memory/storage tiers for this zone + layout
74
+ for plan in cloud.instances.list_service_plans(
75
+ zone_id=cloud_id,
76
+ layout_id=itype.default_layout_id,
77
+ group_id=group.id,
78
+ ):
79
+ print("plan:", plan["id"], plan["name"])
67
80
  ```
68
81
 
69
82
  The `default_layout_id` on each instance type is the correct `layout` value to use for that type. Always read it from your account rather than hardcoding a value from an example.
70
83
 
84
+ ### Resource Pools (the `resource_pool_id`)
85
+
86
+ A resource pool is **where your instance is hosted** — you cannot create an instance without one. List them by group name (the cloud/zone is resolved for you):
87
+
88
+ ```python
89
+ # List every pool available to a group
90
+ for pool in cloud.instances.list_resource_pools(group="MTNNG_CLOUD_AZ_1"):
91
+ print(pool.code, pool.name, "(default)" if pool.is_default else "")
92
+
93
+ # Or fetch a single pool by name (or by its code)
94
+ pool = cloud.instances.get_resource_pool(
95
+ "my-project-Marv-Osuolale",
96
+ group="MTNNG_CLOUD_AZ_1",
97
+ )
98
+ print(pool.code) # e.g. "pool-214" — pass this as resource_pool_id
99
+ ```
100
+
101
+ `pool.code` (e.g. `"pool-214"`) is the exact value `create()` expects. When passing it to `create()`, you can use the code (`"pool-214"`), the numeric ID (`214`), or its string form (`"214"`) — they are all normalized to the code internally.
102
+
103
+ If you already know the cloud and group IDs, pass them explicitly to skip the group lookup:
104
+
105
+ ```python
106
+ pools = cloud.instances.list_resource_pools(cloud_id=4, group_id=621)
107
+ ```
108
+
71
109
  ## Create an Instance
72
110
 
73
111
  ```python
112
+ # Resolve the resource pool once, then use its code directly
113
+ pool = cloud.instances.get_resource_pool(
114
+ "my-project-Marv-Osuolale",
115
+ group="MTNNG_CLOUD_AZ_1",
116
+ )
117
+
74
118
  instance = cloud.instances.create(
75
119
  name="app-server-01",
76
120
  cloud="MTNNG_CLOUD_AZ_1",
77
121
  type="MTN-CS10",
78
122
  group="MTNNG_CLOUD_AZ_1",
79
123
  layout=327, # use default_layout_id from your type lookup
80
- plan=6776, # use plan ID from your plans lookup
81
- resource_pool_id="pool-214", # from your project order
124
+ plan=6776, # use a plan ID from list_service_plans()
125
+ resource_pool_id=pool.code, # e.g. "pool-214" (or pass 214 directly)
82
126
  availability_zone="Lagos-AZ-1-fd1",
83
127
  security_group="default", # security group name from the console
84
128
  )
@@ -0,0 +1,100 @@
1
+ # Networking
2
+
3
+ Use this guide for creating and managing MTN Cloud networks via the SDK.
4
+
5
+ ## Before You Start
6
+
7
+ - You need an existing Resource Pool (Project). Create one from **Provisioning → Catalog → Create Project** if you haven't already.
8
+ - MTN Cloud networking runs on OpenStack Neutron. Networks you create are private by default — VMs get private IPs automatically. Public internet access is configured at provisioning time via the instance's external network (`os_external_network_id`), not through a standalone floating-IP API.
9
+ - Use `list_types(openstack_only=True)` to discover the valid network type IDs for your account.
10
+
11
+ ## Typical Networking Flow
12
+
13
+ 1. Create a network (with CIDR, gateway, DHCP).
14
+ 2. Provision instances on your network.
15
+ 3. For public access, set `os_external_network_id` when creating the instance (see the [Instances guide](./instances.md)).
16
+
17
+ ## Discover Reference Data
18
+
19
+ The `cloud_id` (zone) comes from the group — the admin-level `clouds.list()` endpoint is restricted on most tenant accounts and isn't needed.
20
+
21
+ ```python
22
+ from mtn_cloud import MTNCloud
23
+
24
+ cloud = MTNCloud(token="your-api-token")
25
+
26
+ group = cloud.groups.get_by_name("MTNNG_CLOUD_AZ_1")
27
+ cloud_id = group.cloud_ids[0]
28
+ network_types = cloud.networks.list_types(openstack_only=True)
29
+
30
+ print("group:", group.id, "cloud_id:", cloud_id)
31
+ print([(t.id, t.name) for t in network_types[:5]])
32
+ ```
33
+
34
+ ## Create a Network
35
+
36
+ An OpenStack network requires an OpenStack `type_id` and a `resource_pool_id` (the numeric pool ID — the network is created inside that resource pool). Pick a private network type and a pool:
37
+
38
+ ```python
39
+ networks = cloud.networks.list(cloud_id=cloud_id)
40
+ for network in networks[:5]:
41
+ print(network.id, network.name, network.cidr)
42
+
43
+ net_type = next(t for t in network_types if t.code == "openstackPrivate")
44
+ pool = cloud.instances.get_resource_pool("my-project", group=group.name)
45
+
46
+ new_network = cloud.networks.create(
47
+ name="mtn-prod-net",
48
+ cloud_id=cloud_id, # from group.cloud_ids[0]
49
+ group_id=group.id, # from groups.get_by_name(...)
50
+ type_id=net_type.id, # an OpenStack type from list_types(openstack_only=True)
51
+ resource_pool_id=pool.id, # numeric pool ID from get_resource_pool(...)
52
+ cidr="10.42.10.0/24",
53
+ gateway="10.42.10.1",
54
+ dns_primary="8.8.8.8",
55
+ )
56
+
57
+ print(new_network.id, new_network.name)
58
+ ```
59
+
60
+ ## Update and Inspect
61
+
62
+ ```python
63
+ updated = cloud.networks.update(
64
+ new_network.id,
65
+ description="Production network",
66
+ allow_static_override=True,
67
+ )
68
+
69
+ subnets = cloud.networks.list_subnets(updated.id)
70
+ print(f"Subnets: {len(subnets)}")
71
+ ```
72
+
73
+ ## Delete a Network
74
+
75
+ ```python
76
+ cloud.networks.delete(new_network.id)
77
+ ```
78
+
79
+ ## Network Pools
80
+
81
+ Network pools are managed ranges of IP addresses used for static assignment. Inspect them and the individual addresses they track:
82
+
83
+ ```python
84
+ # List pools with usage counts
85
+ for pool in cloud.networks.list_pools():
86
+ print(pool.id, pool.name, f"{pool.free_count}/{pool.ip_count} free")
87
+ for r in pool.ip_ranges:
88
+ print(" range:", r.start_address, "-", r.end_address)
89
+
90
+ # Inspect a single pool and the addresses in use
91
+ pool = cloud.networks.get_pool(9)
92
+ for ip in cloud.networks.list_pool_ips(pool.id):
93
+ print(ip.ip_address, ip.ip_type, ip.hostname)
94
+ ```
95
+
96
+ ## Notes
97
+
98
+ - Many networking fields are OpenStack-specific. The SDK exposes the fields most relevant to MTN Cloud's environment.
99
+ - Always discover and validate IDs (`cloud_id`, `group_id`, `type_id`) for your own account — they are tenant-specific and differ between environments.
100
+ - Security groups (firewall rules) are separate from network configuration. Manage them via `cloud.security_groups` — see the [Security Groups guide](./security-groups.md).