|
3 | 3 | """Define the CloudEndure main entry logic."""
|
4 | 4 | from __future__ import annotations
|
5 | 5 |
|
| 6 | +import base64 |
6 | 7 | import datetime
|
7 | 8 | import json
|
8 | 9 | import os
|
|
16 | 17 |
|
17 | 18 | from .api import CloudEndureAPI
|
18 | 19 | from .config import CloudEndureConfig
|
| 20 | +from .constants import get_aws_regions |
19 | 21 | from .events import Event, EventHandler
|
20 | 22 | from .exceptions import CloudEndureHTTPException, CloudEndureMisconfigured
|
21 | 23 | from .templates import TerraformTemplate
|
@@ -79,6 +81,15 @@ def __init__(
|
79 | 81 | "Failed to authenticate with CloudEndure! Please check your credentials and try again!"
|
80 | 82 | )
|
81 | 83 |
|
| 84 | + def _get_role_credentials(self, name: str, role: str) -> Dict[str, Any]: |
| 85 | + _sts_client: boto_client = boto3.client("sts") |
| 86 | + |
| 87 | + print(f"Assuming role: {role}") |
| 88 | + assumed_role: Dict[str, Any] = _sts_client.assume_role( |
| 89 | + RoleArn=self.destination_role, RoleSessionName=name |
| 90 | + ) |
| 91 | + return assumed_role.get("Credentials", {}) |
| 92 | + |
82 | 93 | def get_project_id(self, project_name: str = "") -> str:
|
83 | 94 | """Get the associated CloudEndure project ID by project_name.
|
84 | 95 |
|
@@ -128,15 +139,194 @@ def get_project_id(self, project_name: str = "") -> str:
|
128 | 139 | return ""
|
129 | 140 | return project_id
|
130 | 141 |
|
131 |
| - def _get_role_credentials(self, name: str, role: str) -> Dict[str, Any]: |
132 |
| - _sts_client: boto_client = boto3.client("sts") |
| 142 | + def get_cloud(self, cloud_type: str = "") -> str: |
| 143 | + """Get the ID for the specified cloud type.""" |
| 144 | + if not cloud_type: |
| 145 | + cloud_type = self.config.active_config.get("cloud_type", "AWS") |
133 | 146 |
|
134 |
| - print(f"Assuming role: {role}") |
135 |
| - assumed_role: Dict[str, Any] = _sts_client.assume_role( |
136 |
| - RoleArn=self.destination_role, RoleSessionName=name |
| 147 | + clouds_result: Response = self.api.api_call("clouds") |
| 148 | + for cloud in json.loads(clouds_result.content)["items"]: |
| 149 | + if cloud.get("name", "") == cloud_type: |
| 150 | + return cloud.get("id", "") |
| 151 | + return "" |
| 152 | + |
| 153 | + def create_cloud_credentials( |
| 154 | + self, access_key: str = "", secret_key: str = "" |
| 155 | + ) -> str: |
| 156 | + """Create a new CloudEndure project. |
| 157 | +
|
| 158 | + Args: |
| 159 | + project_name (str): The name of the CloudEndure project to be created. |
| 160 | +
|
| 161 | + Returns: |
| 162 | + str: The newly created CloudEndure project ID. |
| 163 | +
|
| 164 | + """ |
| 165 | + _encoded_private_key = base64.b64encode(secret_key.encode()) |
| 166 | + _project = { |
| 167 | + "accountIdentifier": "", |
| 168 | + "publicKey": access_key, |
| 169 | + "privateKey": _encoded_private_key, |
| 170 | + "cloudId": self.get_cloud(), |
| 171 | + } |
| 172 | + |
| 173 | + cloud_cred_result: Response = self.api.api_call( |
| 174 | + "cloudCredentials", method="post", data=_project |
| 175 | + ) |
| 176 | + if cloud_cred_result.status_code != 201: |
| 177 | + print( |
| 178 | + f"Failed to create the new cloud credentials: ({access_key}): " |
| 179 | + f"{cloud_cred_result.status_code} {cloud_cred_result.content}" |
| 180 | + ) |
| 181 | + return "" |
| 182 | + print(f"Cloud Credentials: ({access_key}) was created successfully!") |
| 183 | + return json.loads(cloud_cred_result.content).get("id", "") |
| 184 | + |
| 185 | + def create_repl_config(self, region: str = "", cloud_cred_id: str = ""): |
| 186 | + """Create a CloudEndure project replication configuration. |
| 187 | +
|
| 188 | + Args: |
| 189 | + project_name (str): The name of the CloudEndure project to get replication configurations for. |
| 190 | +
|
| 191 | + Returns: |
| 192 | + list of dict: The CloudEndure replication configuration dictionary mapping. |
| 193 | +
|
| 194 | + """ |
| 195 | + regions = get_aws_regions() |
| 196 | + payload = { |
| 197 | + # "archivingEnabled": False, |
| 198 | + "bandwidthThrottling": 0, |
| 199 | + # "cloudCredentials": "", |
| 200 | + # "id": "", |
| 201 | + # "objectStorageLocation": "", |
| 202 | + # "proxyUrl": "", |
| 203 | + # "region": "", |
| 204 | + "replicationServerType": "Default", |
| 205 | + "replicationTags": [], |
| 206 | + # "replicatorSecurityGroupIDs": [], |
| 207 | + # "subnetHostProject": "", |
| 208 | + # "subnetId": "", |
| 209 | + "useDedicatedServer": False, |
| 210 | + "useLowCostDisks": False, |
| 211 | + "usePrivateIp": True, |
| 212 | + "volumeEncryptionAllowed": False, |
| 213 | + # "volumeEncryptionKey": "" |
| 214 | + } |
| 215 | + |
| 216 | + credentials_response: Response = self.api.api_call( |
| 217 | + f"cloudCredentials/{cloud_cred_id}/regions" |
| 218 | + ) |
| 219 | + for _region in json.loads(credentials_response.content).get("items", []): |
| 220 | + if _region["name"] == regions.get(region, "N/A"): |
| 221 | + payload["region"] = _region["id"] |
| 222 | + |
| 223 | + print(payload) |
| 224 | + repl_result: Response = self.api.api_call( |
| 225 | + f"projects/{self.project_id}/replicationConfigurations", |
| 226 | + method="post", |
| 227 | + data=payload, |
| 228 | + ) |
| 229 | + if repl_result.status_code != 201: |
| 230 | + print( |
| 231 | + f"Failed to create replication configuration {repl_result.status_code} {repl_result.content}" |
| 232 | + ) |
| 233 | + return "" |
| 234 | + print("Replication configuration was created successfully") |
| 235 | + return json.loads(repl_result.content).get("id", "") |
| 236 | + |
| 237 | + def get_repl_configs(self) -> List[Any]: |
| 238 | + """Get a CloudEndure project's replication configurations. |
| 239 | +
|
| 240 | + Args: |
| 241 | + project_name (str): The name of the CloudEndure project to get replication configurations for. |
| 242 | +
|
| 243 | + Returns: |
| 244 | + list of dict: The CloudEndure replication configuration dictionary mapping. |
| 245 | +
|
| 246 | + """ |
| 247 | + print( |
| 248 | + f"Fetching Replication Configuration - Project Name: ({self.project_name})" |
| 249 | + ) |
| 250 | + repl_config_results: Response = self.api.api_call( |
| 251 | + f"projects/{self.project_id}/replicationConfigurations" |
| 252 | + ) |
| 253 | + |
| 254 | + if repl_config_results.status_code != 200: |
| 255 | + print( |
| 256 | + f"Failed to fetch replication configurations for ({self.project_name}): " |
| 257 | + f"{repl_config_results.status_code} {repl_config_results.content}" |
| 258 | + ) |
| 259 | + print(repl_config_results.text) |
| 260 | + return [] |
| 261 | + |
| 262 | + repl_configs = json.loads(repl_config_results.content).get("items", []) |
| 263 | + print( |
| 264 | + f"Successfully fetched replication configurations for project: {self.project_name}" |
| 265 | + ) |
| 266 | + return repl_configs |
| 267 | + |
| 268 | + def create_project(self, project_name: str) -> str: |
| 269 | + """Create a new CloudEndure project. |
| 270 | +
|
| 271 | + Args: |
| 272 | + project_name (str): The name of the CloudEndure project to be created. |
| 273 | +
|
| 274 | + Returns: |
| 275 | + str: The newly created CloudEndure project ID. |
| 276 | +
|
| 277 | + """ |
| 278 | + project = { |
| 279 | + "licensesIDs": [], |
| 280 | + "name": project_name, |
| 281 | + "targetCloudId": self.get_cloud(), |
| 282 | + "type": "MIGRATION", |
| 283 | + } |
| 284 | + licenses_result: Response = self.api.api_call("licenses") |
| 285 | + |
| 286 | + for license in json.loads(licenses_result.content).get("items", []): |
| 287 | + license_id = license.get("id", "") |
| 288 | + if license_id: |
| 289 | + print(license_id) |
| 290 | + project["licensesIDs"].append(license_id) |
| 291 | + |
| 292 | + projects_result: Response = self.api.api_call( |
| 293 | + "projects", method="post", data=project |
137 | 294 | )
|
| 295 | + if projects_result.status_code != 201: |
| 296 | + print( |
| 297 | + f"Failed to create the new project ({self.project_name}): " |
| 298 | + f"{projects_result.status_code} {projects_result.content}" |
| 299 | + ) |
| 300 | + return "" |
| 301 | + print(f"Project: ({self.project_name}) was created successfully!") |
| 302 | + return json.loads(projects_result.content).get("id", "") |
| 303 | + |
| 304 | + def update_project(self, project_data: Dict[str, Any] = None) -> bool: |
| 305 | + """Update a CloudEndure project. |
| 306 | +
|
| 307 | + Args: |
| 308 | + project_name (str): The name of the CloudEndure project to be updated. |
| 309 | + project_data (dict): The project payload to be used to update the project. |
| 310 | + Defaults to the current project state. |
138 | 311 |
|
139 |
| - return assumed_role.get("Credentials") |
| 312 | + Returns: |
| 313 | + bool: Whether or not the project has been updated. |
| 314 | +
|
| 315 | + """ |
| 316 | + print(f"Updating Project - Name: ({self.project_name})") |
| 317 | + projects_result: Response = self.api.api_call( |
| 318 | + f"projects/{self.project_id}", method="patch", data=project_data |
| 319 | + ) |
| 320 | + |
| 321 | + if projects_result.status_code != 200: |
| 322 | + print( |
| 323 | + f"Failed to update the project ({self.project_name}): " |
| 324 | + f"{projects_result.status_code} {projects_result.content}" |
| 325 | + ) |
| 326 | + print(projects_result.text) |
| 327 | + return False |
| 328 | + print("Project was updated successfully") |
| 329 | + return True |
140 | 330 |
|
141 | 331 | def check(self) -> bool:
|
142 | 332 | """Check the status of machines in the provided project."""
|
|
0 commit comments