Skip to content

Commit b5ee5f6

Browse files
committed
black formatted
1 parent b30f6d6 commit b5ee5f6

File tree

9 files changed

+333
-245
lines changed

9 files changed

+333
-245
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ tests/.env
77
dist/
88
build/
99
*.pyc
10-
venv
10+
venv
11+
debug.py

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
requests>=2,<3
22
abstract-http-client>=1,<2
33
pydantic
4+
tenacity

src/cloudshell/sandbox_rest/api.py

Lines changed: 236 additions & 73 deletions
Large diffs are not rendered by default.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"""
2+
module to run sandbox actions in parallel using asyncio module
3+
"""
4+
5+
6+
class AsyncCommandExecutor:
7+
pass
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
"""
22
Component helpers for filtering and sorting the sandbox components
3-
"""
3+
"""
4+
5+
6+
class SandboxComponents:
7+
pass

src/cloudshell/sandbox_rest/exceptions.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
from typing import List
2+
3+
from cloudshell.sandbox_rest import model
4+
5+
16
class SandboxRestException(Exception):
27
""" Base Exception Class inside Rest client class """
38

@@ -14,8 +19,27 @@ class CommandPollingTimeout(SandboxRestException):
1419
pass
1520

1621

17-
class SetupFailedException(SandboxRestException):
18-
pass
22+
class FailedOrchestrationException(SandboxRestException):
23+
def __init__(self, message: str, error_events: List[model.SandboxEvent] = None):
24+
self.message = message
25+
self.error_events = error_events or []
26+
super().__init__(message)
27+
28+
def events_to_json(self):
29+
return model.models_to_json(self.error_events)
30+
31+
def __str__(self):
32+
return f"{self.message}\n{self.events_to_json()}"
33+
34+
35+
class SetupFailedException(FailedOrchestrationException):
36+
def __init__(self, message: str, error_events: List[model.SandboxEvent] = None):
37+
super().__init__(message, error_events)
38+
39+
40+
class TeardownFailedException(SandboxRestException):
41+
def __init__(self, message: str, error_events: List[model.SandboxEvent] = None):
42+
super().__init__(message, error_events)
1943

2044

2145
class CommandExecutionFailed(SandboxRestException):

src/cloudshell/sandbox_rest/model.py

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,42 +2,58 @@
22
All pydantic BaseModel class representations of Sandbox API responses
33
"""
44
from __future__ import annotations
5+
6+
import json
57
from enum import Enum
68
from typing import TYPE_CHECKING, List, Optional
79

810
from pydantic import BaseModel, Field
911

12+
RESPONSE_DICT_KEY = "response_dict"
13+
14+
1015
# added for dev intellisense: https://stackoverflow.com/a/71257588
1116
if TYPE_CHECKING:
1217
from dataclasses import dataclass as _basemodel_decorator
1318
else:
19+
1420
def _basemodel_decorator(func):
1521
return func
1622

1723

1824
class SandboxApiBaseModel(BaseModel):
25+
""" Base Model for all other classes. Defines useful helper methods """
26+
1927
class Config:
2028
use_enum_values = True
2129

2230
response_dict: dict = None
23-
""" the original dictionary received from api response """
31+
""" this attribute will cache the original response before loading into model """
32+
33+
@classmethod
34+
def dict_to_model(cls, response_dict: dict) -> SandboxApiBaseModel:
35+
""" calls parse_obj, but additionally caches the response dict """
36+
wrapped_item = cls.parse_obj(response_dict)
37+
wrapped_item.response_dict = response_dict
38+
return wrapped_item
39+
40+
@classmethod
41+
def list_to_models(cls, response_list: List[dict]) -> List[SandboxApiBaseModel]:
42+
return [cls.dict_to_model(dict_item) for dict_item in response_list]
2443

2544
def pretty_json(self, indent=4, exclude_response=True):
2645
excluded_key_set = set()
2746
if exclude_response:
28-
excluded_key_set.add("response_dict")
47+
excluded_key_set.add(RESPONSE_DICT_KEY)
2948
return self.json(indent=indent, exclude=excluded_key_set)
3049

3150

32-
def list_to_model(response_list: List[dict], model: SandboxApiBaseModel) -> List[SandboxApiBaseModel]:
33-
return [dict_to_model(dict_item, model) for dict_item in response_list]
34-
35-
36-
def dict_to_model(response_dict: dict, model: SandboxApiBaseModel) -> SandboxApiBaseModel:
37-
""" calls parse_obj, but additionally caches the response dict """
38-
wrapped_item = model.parse_obj(response_dict)
39-
wrapped_item.response_dict = response_dict
40-
return wrapped_item
51+
def models_to_json(model_list: List[SandboxApiBaseModel]) -> str:
52+
""" helper method to be used to convert a list of models to a list of dicts and dump to json """
53+
list_of_dicts = [x.dict() for x in model_list]
54+
excluded_keys = [RESPONSE_DICT_KEY]
55+
updated_list = [{k: v for k, v in curr_dict if k not in excluded_keys} for curr_dict in list_of_dicts]
56+
return json.dumps(updated_list, indent=4)
4157

4258

4359
class BlueprintAvailabilityStates(str, Enum):
@@ -73,6 +89,7 @@ class SetupStages(str, Enum):
7389
PROVISIONING = "Provisioning"
7490
CONNECTIVITY = "Connectivity"
7591
CONFIGURATION = "Configuration"
92+
ENDED = "Ended"
7693

7794

7895
class SandboxEventTypes(str, Enum):
@@ -94,6 +111,10 @@ class CommandExecutionStates(str, Enum):
94111
COMPLETE = "Complete"
95112
FAILED = "Failed"
96113

114+
@classmethod
115+
def get_incomplete_execution_states(cls) -> List:
116+
return [cls.PENDING, cls.RUNNING]
117+
97118

98119
@_basemodel_decorator
99120
class BlueprintInput(SandboxApiBaseModel):
@@ -142,6 +163,9 @@ class SandboxDetails(SandboxApiBaseModel):
142163
state: Optional[SandboxStates]
143164
setup_stage: Optional[SetupStages]
144165

166+
def components_to_json(self):
167+
return models_to_json(self.components)
168+
145169

146170
@_basemodel_decorator
147171
class BlueprintReference(SandboxApiBaseModel):
@@ -175,6 +199,9 @@ class ActivityEventsResponse(SandboxApiBaseModel):
175199
next_event_id: Optional[int]
176200
events: Optional[List[SandboxEvent]]
177201

202+
def events_to_json(self) -> str:
203+
return models_to_json(self.events)
204+
178205

179206
@_basemodel_decorator
180207
class CommandParameterDetails(SandboxApiBaseModel):
@@ -230,8 +257,9 @@ class CommandExecutionDetails(SandboxApiBaseModel):
230257
started: Optional[str]
231258
ended: Optional[str]
232259
output: Optional[str]
233-
command_context: Optional[CommandContextDetails] = Field(description="additional data to populate about command. "
234-
"NOTE: this does not come from api response")
260+
command_context: Optional[CommandContextDetails] = Field(
261+
description="additional data to populate about command. " "NOTE: this does not come from api response"
262+
)
235263

236264

237265
@_basemodel_decorator

src/cloudshell/sandbox_rest/polling.py

Lines changed: 0 additions & 153 deletions
This file was deleted.
Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,20 @@
11
"""
2-
Sandbox context manager to manage the lifecyle of setup and teardown
2+
Sandbox controller context manager to manage the lifecyle of setup and teardown
33
"""
44

55

6+
from cloudshell.sandbox_rest.api import SandboxRestApiSession
7+
8+
69
class SandboxControllerContext:
7-
pass
10+
def __init__(self, api: SandboxRestApiSession, sandbox_id: str = None):
11+
self.api = api
12+
self.sandbox_id = sandbox_id
13+
14+
def __enter__(self):
15+
""" start sandbox """
16+
return self
17+
18+
def __exit__(self, exc_type, exc_val, exc_tb):
19+
""" end sandbox """
20+
return self

0 commit comments

Comments
 (0)