thestage 0.6.1__py3-none-any.whl → 0.6.3__py3-none-any.whl
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.
- thestage/.env +4 -5
- thestage/__init__.py +3 -3
- thestage/__main__.py +9 -9
- thestage/cli_command.py +56 -56
- thestage/cli_command_helper.py +51 -51
- thestage/color_scheme/color_scheme.py +7 -7
- thestage/config/__init__.py +18 -18
- thestage/config/config_storage.py +5 -5
- thestage/config/env_base.py +7 -7
- thestage/controllers/__init__.py +0 -0
- thestage/controllers/base_controller.py +67 -67
- thestage/controllers/config_controller.py +137 -137
- thestage/controllers/container_controller.py +389 -389
- thestage/controllers/instance_controller.py +183 -183
- thestage/controllers/project_controller.py +810 -802
- thestage/controllers/utils_controller.py +32 -32
- thestage/debug_main.dist.py +28 -28
- thestage/entities/__init__.py +0 -0
- thestage/entities/container.py +17 -17
- thestage/entities/enums/__init__.py +0 -0
- thestage/entities/enums/order_direction_type.py +6 -6
- thestage/entities/enums/shell_type.py +7 -7
- thestage/entities/enums/tail_output_type.py +6 -6
- thestage/entities/enums/yes_no_response.py +7 -7
- thestage/entities/file_item.py +27 -27
- thestage/entities/project_inference_simulator.py +18 -18
- thestage/entities/project_inference_simulator_model.py +16 -16
- thestage/entities/project_task.py +19 -19
- thestage/entities/rented_instance.py +19 -19
- thestage/entities/self_hosted_instance.py +18 -18
- thestage/exceptions/__init__.py +0 -0
- thestage/exceptions/auth_exception.py +6 -6
- thestage/exceptions/base_exception.py +13 -13
- thestage/exceptions/business_logic_exception.py +6 -6
- thestage/exceptions/config_exception.py +6 -6
- thestage/exceptions/file_system_exception.py +6 -6
- thestage/exceptions/git_access_exception.py +17 -17
- thestage/exceptions/remote_server_exception.py +24 -24
- thestage/git/ProgressPrinter.py +22 -22
- thestage/helpers/__init__.py +0 -0
- thestage/helpers/error_handler.py +115 -115
- thestage/helpers/exception_hook.py +14 -14
- thestage/helpers/logger/__init__.py +0 -0
- thestage/helpers/logger/app_logger.py +50 -50
- thestage/helpers/ssh_util.py +38 -38
- thestage/i18n/en_GB/messages.po +947 -947
- thestage/i18n/translation.py +9 -9
- thestage/main.py +36 -36
- thestage/services/.env +6 -6
- thestage/services/__init__.py +0 -0
- thestage/services/abstract_mapper.py +9 -9
- thestage/services/abstract_service.py +87 -87
- thestage/services/app_config_service.py +52 -52
- thestage/services/clients/__init__.py +0 -0
- thestage/services/clients/git/__init__.py +0 -0
- thestage/services/clients/git/git_client.py +436 -433
- thestage/services/clients/thestage_api/__init__.py +0 -0
- thestage/services/clients/thestage_api/api_client.py +718 -718
- thestage/services/clients/thestage_api/core/api_client_core.py +108 -108
- thestage/services/clients/thestage_api/core/http_client_exception.py +12 -12
- thestage/services/clients/thestage_api/dtos/__init__.py +0 -0
- thestage/services/clients/thestage_api/dtos/base_response.py +13 -13
- thestage/services/clients/thestage_api/dtos/cloud_provider_region.py +19 -19
- thestage/services/clients/thestage_api/dtos/container_param_request.py +11 -11
- thestage/services/clients/thestage_api/dtos/container_response.py +67 -67
- thestage/services/clients/thestage_api/dtos/docker_container_assigned_device.py +10 -10
- thestage/services/clients/thestage_api/dtos/docker_container_controller/docker_container_list_request.py +13 -13
- thestage/services/clients/thestage_api/dtos/docker_container_controller/docker_container_list_response.py +13 -13
- thestage/services/clients/thestage_api/dtos/docker_container_mapping.py +10 -10
- thestage/services/clients/thestage_api/dtos/entity_filter_request.py +14 -14
- thestage/services/clients/thestage_api/dtos/enums/__init__.py +0 -0
- thestage/services/clients/thestage_api/dtos/enums/container_pending_action.py +10 -10
- thestage/services/clients/thestage_api/dtos/enums/container_status.py +17 -17
- thestage/services/clients/thestage_api/dtos/enums/cpu_type.py +8 -8
- thestage/services/clients/thestage_api/dtos/enums/currency_type.py +10 -10
- thestage/services/clients/thestage_api/dtos/enums/daemon_status.py +9 -9
- thestage/services/clients/thestage_api/dtos/enums/disk_type.py +7 -7
- thestage/services/clients/thestage_api/dtos/enums/drive_type.py +7 -7
- thestage/services/clients/thestage_api/dtos/enums/gpu_name.py +8 -8
- thestage/services/clients/thestage_api/dtos/enums/inference_model_status.py +9 -9
- thestage/services/clients/thestage_api/dtos/enums/inference_simulator_status.py +15 -15
- thestage/services/clients/thestage_api/dtos/enums/instance_rented_status.py +17 -17
- thestage/services/clients/thestage_api/dtos/enums/instance_type.py +7 -7
- thestage/services/clients/thestage_api/dtos/enums/location_region.py +11 -11
- thestage/services/clients/thestage_api/dtos/enums/power_status.py +10 -10
- thestage/services/clients/thestage_api/dtos/enums/provider_name.py +11 -11
- thestage/services/clients/thestage_api/dtos/enums/selfhosted_status.py +10 -10
- thestage/services/clients/thestage_api/dtos/enums/task_execution_status.py +12 -12
- thestage/services/clients/thestage_api/dtos/enums/task_status.py +12 -12
- thestage/services/clients/thestage_api/dtos/frontend_status.py +10 -10
- thestage/services/clients/thestage_api/dtos/inference_controller/deploy_inference_model_to_instance_request.py +13 -13
- thestage/services/clients/thestage_api/dtos/inference_controller/deploy_inference_model_to_instance_response.py +13 -13
- thestage/services/clients/thestage_api/dtos/inference_controller/deploy_inference_model_to_sagemaker_request.py +12 -12
- thestage/services/clients/thestage_api/dtos/inference_controller/deploy_inference_model_to_sagemaker_response.py +12 -12
- thestage/services/clients/thestage_api/dtos/inference_controller/get_inference_simulator_request.py +10 -10
- thestage/services/clients/thestage_api/dtos/inference_controller/get_inference_simulator_response.py +13 -13
- thestage/services/clients/thestage_api/dtos/inference_controller/inference_simulator_list_for_project_request.py +14 -14
- thestage/services/clients/thestage_api/dtos/inference_controller/inference_simulator_list_for_project_response.py +12 -12
- thestage/services/clients/thestage_api/dtos/inference_controller/inference_simulator_model_list_for_project_request.py +12 -12
- thestage/services/clients/thestage_api/dtos/inference_controller/inference_simulator_model_list_for_project_response.py +13 -13
- thestage/services/clients/thestage_api/dtos/inference_simulator_model_response.py +11 -11
- thestage/services/clients/thestage_api/dtos/inference_simulator_response.py +11 -11
- thestage/services/clients/thestage_api/dtos/installed_service.py +17 -17
- thestage/services/clients/thestage_api/dtos/instance_detected_gpus.py +20 -20
- thestage/services/clients/thestage_api/dtos/instance_rented_response.py +71 -71
- thestage/services/clients/thestage_api/dtos/logging_controller/docker_container_log_stream_request.py +7 -7
- thestage/services/clients/thestage_api/dtos/logging_controller/log_polling_request.py +13 -13
- thestage/services/clients/thestage_api/dtos/logging_controller/log_polling_response.py +14 -14
- thestage/services/clients/thestage_api/dtos/logging_controller/task_log_stream_request.py +7 -7
- thestage/services/clients/thestage_api/dtos/logging_controller/user_logs_query_request.py +21 -21
- thestage/services/clients/thestage_api/dtos/logging_controller/user_logs_query_response.py +14 -14
- thestage/services/clients/thestage_api/dtos/paginated_entity_list.py +11 -11
- thestage/services/clients/thestage_api/dtos/pagination_data.py +10 -10
- thestage/services/clients/thestage_api/dtos/price_definition.py +14 -14
- thestage/services/clients/thestage_api/dtos/project_controller/project_get_deploy_ssh_key_request.py +7 -7
- thestage/services/clients/thestage_api/dtos/project_controller/project_get_deploy_ssh_key_response.py +10 -10
- thestage/services/clients/thestage_api/dtos/project_controller/project_push_inference_simulator_model_request.py +8 -8
- thestage/services/clients/thestage_api/dtos/project_controller/project_push_inference_simulator_model_response.py +6 -6
- thestage/services/clients/thestage_api/dtos/project_controller/project_run_task_request.py +15 -15
- thestage/services/clients/thestage_api/dtos/project_controller/project_run_task_response.py +10 -10
- thestage/services/clients/thestage_api/dtos/project_controller/project_start_inference_simulator_request.py +13 -13
- thestage/services/clients/thestage_api/dtos/project_controller/project_start_inference_simulator_response.py +10 -10
- thestage/services/clients/thestage_api/dtos/project_response.py +32 -32
- thestage/services/clients/thestage_api/dtos/selfhosted_instance_response.py +56 -56
- thestage/services/clients/thestage_api/dtos/sftp_path_helper.py +13 -13
- thestage/services/clients/thestage_api/dtos/ssh_key_controller/add_ssh_key_to_user_request.py +8 -8
- thestage/services/clients/thestage_api/dtos/ssh_key_controller/add_ssh_key_to_user_response.py +11 -11
- thestage/services/clients/thestage_api/dtos/ssh_key_controller/add_ssh_public_key_to_instance_request.py +8 -8
- thestage/services/clients/thestage_api/dtos/ssh_key_controller/add_ssh_public_key_to_instance_response.py +11 -11
- thestage/services/clients/thestage_api/dtos/ssh_key_controller/is_user_has_public_ssh_key_request.py +7 -7
- thestage/services/clients/thestage_api/dtos/ssh_key_controller/is_user_has_public_ssh_key_response.py +12 -12
- thestage/services/clients/thestage_api/dtos/task_controller/task_list_for_project_request.py +10 -10
- thestage/services/clients/thestage_api/dtos/task_controller/task_list_for_project_response.py +12 -12
- thestage/services/clients/thestage_api/dtos/task_controller/task_status_localized_map_response.py +9 -9
- thestage/services/clients/thestage_api/dtos/task_controller/task_view_response.py +12 -12
- thestage/services/clients/thestage_api/dtos/user_controller/user_profile.py +12 -12
- thestage/services/clients/thestage_api/dtos/validate_token_response.py +11 -11
- thestage/services/config_provider/__init__.py +0 -0
- thestage/services/config_provider/config_provider.py +237 -237
- thestage/services/connect/connect_service.py +193 -196
- thestage/services/connect/dto/remote_server_config.py +9 -9
- thestage/services/container/__init__.py +0 -0
- thestage/services/container/container_service.py +374 -374
- thestage/services/container/mapper/__init__.py +0 -0
- thestage/services/container/mapper/container_mapper.py +30 -30
- thestage/services/core_files/config_entity.py +26 -26
- thestage/services/filesystem_service.py +133 -133
- thestage/services/instance/__init__.py +0 -0
- thestage/services/instance/instance_service.py +303 -303
- thestage/services/instance/mapper/__init__.py +0 -0
- thestage/services/instance/mapper/instance_mapper.py +24 -24
- thestage/services/instance/mapper/selfhosted_mapper.py +33 -33
- thestage/services/logging/byte_print_style.py +5 -5
- thestage/services/logging/dto/log_message.py +15 -15
- thestage/services/logging/dto/log_type.py +6 -6
- thestage/services/logging/exception/log_polling_exception.py +6 -6
- thestage/services/logging/logging_constants.py +3 -3
- thestage/services/logging/logging_service.py +367 -367
- thestage/services/project/__init__.py +0 -0
- thestage/services/project/dto/inference_simulator_dto.py +22 -22
- thestage/services/project/dto/inference_simulator_model_dto.py +20 -20
- thestage/services/project/dto/project_config.py +14 -14
- thestage/services/project/mapper/__init__.py +0 -0
- thestage/services/project/mapper/project_inference_simulator_mapper.py +21 -21
- thestage/services/project/mapper/project_inference_simulator_model_mapper.py +21 -21
- thestage/services/project/mapper/project_task_mapper.py +22 -22
- thestage/services/project/project_service.py +1253 -1260
- thestage/services/remote_server_service.py +609 -609
- thestage/services/service_factory.py +97 -97
- thestage/services/task/dto/task_dto.py +40 -40
- thestage/services/validation_service.py +61 -61
- {thestage-0.6.1.dist-info → thestage-0.6.3.dist-info}/LICENSE.txt +12 -12
- {thestage-0.6.1.dist-info → thestage-0.6.3.dist-info}/METADATA +3 -2
- thestage-0.6.3.dist-info/RECORD +176 -0
- {thestage-0.6.1.dist-info → thestage-0.6.3.dist-info}/WHEEL +1 -1
- thestage-0.6.1.dist-info/RECORD +0 -176
- {thestage-0.6.1.dist-info → thestage-0.6.3.dist-info}/entry_points.txt +0 -0
|
@@ -1,304 +1,304 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
from typing import List, Optional, Dict
|
|
3
|
-
|
|
4
|
-
import typer
|
|
5
|
-
|
|
6
|
-
from thestage.entities.rented_instance import RentedInstanceEntity
|
|
7
|
-
from thestage.entities.self_hosted_instance import SelfHostedInstanceEntity
|
|
8
|
-
from thestage.i18n.translation import __
|
|
9
|
-
from thestage.services.clients.thestage_api.dtos.enums.selfhosted_status import SelfhostedBusinessStatus
|
|
10
|
-
from thestage.services.clients.thestage_api.dtos.enums.instance_rented_status import InstanceRentedBusinessStatus
|
|
11
|
-
from thestage.services.abstract_service import AbstractService
|
|
12
|
-
from thestage.helpers.error_handler import error_handler
|
|
13
|
-
from thestage.services.clients.thestage_api.api_client import TheStageApiClient
|
|
14
|
-
from thestage.services.clients.thestage_api.dtos.instance_rented_response import InstanceRentedDto
|
|
15
|
-
from thestage.services.clients.thestage_api.dtos.paginated_entity_list import PaginatedEntityList
|
|
16
|
-
from thestage.services.clients.thestage_api.dtos.selfhosted_instance_response import SelfHostedInstanceDto
|
|
17
|
-
from thestage.services.config_provider.config_provider import ConfigProvider
|
|
18
|
-
from thestage.services.instance.mapper.instance_mapper import InstanceMapper
|
|
19
|
-
from thestage.services.instance.mapper.selfhosted_mapper import SelfHostedMapper
|
|
20
|
-
from thestage.services.remote_server_service import RemoteServerService
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class InstanceService(AbstractService):
|
|
24
|
-
|
|
25
|
-
__thestage_api_client: TheStageApiClient = None
|
|
26
|
-
__config_provider: ConfigProvider = None
|
|
27
|
-
|
|
28
|
-
def __init__(
|
|
29
|
-
self,
|
|
30
|
-
thestage_api_client: TheStageApiClient,
|
|
31
|
-
config_provider: ConfigProvider,
|
|
32
|
-
remote_server_service: RemoteServerService,
|
|
33
|
-
):
|
|
34
|
-
self.__thestage_api_client = thestage_api_client
|
|
35
|
-
self.__remote_server_service = remote_server_service
|
|
36
|
-
self.__config_provider = config_provider
|
|
37
|
-
|
|
38
|
-
def get_rented_instance(
|
|
39
|
-
self,
|
|
40
|
-
instance_slug: str,
|
|
41
|
-
) -> Optional[InstanceRentedDto]:
|
|
42
|
-
return self.__thestage_api_client.get_rented_instance(
|
|
43
|
-
instance_slug=instance_slug,
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
def get_self_hosted_instance(
|
|
47
|
-
self,
|
|
48
|
-
instance_slug: str,
|
|
49
|
-
) -> Optional[SelfHostedInstanceDto]:
|
|
50
|
-
return self.__thestage_api_client.get_selfhosted_instance(
|
|
51
|
-
instance_slug=instance_slug,
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
@error_handler()
|
|
55
|
-
def check_instance_status_to_connect(
|
|
56
|
-
self,
|
|
57
|
-
instance: InstanceRentedDto,
|
|
58
|
-
) -> InstanceRentedDto:
|
|
59
|
-
if instance:
|
|
60
|
-
if instance.frontend_status.status_key in [
|
|
61
|
-
InstanceRentedBusinessStatus.IN_QUEUE.name,
|
|
62
|
-
InstanceRentedBusinessStatus.CREATING.name,
|
|
63
|
-
InstanceRentedBusinessStatus.REBOOTING.name,
|
|
64
|
-
InstanceRentedBusinessStatus.STARTING.name,
|
|
65
|
-
]:
|
|
66
|
-
typer.echo(__('Cannot connect to rented server instance: it is either in the process of being rented or rebooted'))
|
|
67
|
-
raise typer.Exit(1)
|
|
68
|
-
elif instance.frontend_status.status_key in [
|
|
69
|
-
InstanceRentedBusinessStatus.TERMINATING.name,
|
|
70
|
-
InstanceRentedBusinessStatus.RENTAL_ERROR.name,
|
|
71
|
-
]:
|
|
72
|
-
typer.echo(__('Cannot connect to rented server instance: renting process failed'))
|
|
73
|
-
raise typer.Exit(1)
|
|
74
|
-
elif instance.frontend_status.status_key in [
|
|
75
|
-
InstanceRentedBusinessStatus.STOPPED.name,
|
|
76
|
-
InstanceRentedBusinessStatus.STOPPING.name,
|
|
77
|
-
InstanceRentedBusinessStatus.DELETED.name,
|
|
78
|
-
]:
|
|
79
|
-
typer.echo(__('Cannot connect to rented server instance: it is either stopped or has been deleted'))
|
|
80
|
-
raise typer.Exit(1)
|
|
81
|
-
elif instance.frontend_status.status_key in [
|
|
82
|
-
InstanceRentedBusinessStatus.UNKNOWN.name,
|
|
83
|
-
InstanceRentedBusinessStatus.ALL.name,
|
|
84
|
-
]:
|
|
85
|
-
typer.echo(__('Cannot connect to rented server instance: instance status unknown'))
|
|
86
|
-
raise typer.Exit(1)
|
|
87
|
-
|
|
88
|
-
return instance
|
|
89
|
-
|
|
90
|
-
@error_handler()
|
|
91
|
-
def check_selfhosted_status_to_connect(
|
|
92
|
-
self,
|
|
93
|
-
instance: SelfHostedInstanceDto,
|
|
94
|
-
) -> SelfHostedInstanceDto:
|
|
95
|
-
if instance:
|
|
96
|
-
if instance.frontend_status.status_key in [
|
|
97
|
-
SelfhostedBusinessStatus.AWAITING_CONFIGURATION.name,
|
|
98
|
-
]:
|
|
99
|
-
typer.echo(__('Cannot connect to self-hosted instance: it is awaiting configuration'))
|
|
100
|
-
raise typer.Exit(1)
|
|
101
|
-
elif instance.frontend_status.status_key in [
|
|
102
|
-
SelfhostedBusinessStatus.UNREACHABLE_DAEMON.name,
|
|
103
|
-
SelfhostedBusinessStatus.DELETED.name,
|
|
104
|
-
]:
|
|
105
|
-
typer.echo(__('Cannot connect to self-hosted instance: it may be turned off or unreachable'))
|
|
106
|
-
raise typer.Exit(1)
|
|
107
|
-
elif instance.frontend_status.status_key in [
|
|
108
|
-
SelfhostedBusinessStatus.UNKNOWN.name,
|
|
109
|
-
SelfhostedBusinessStatus.ALL.name,
|
|
110
|
-
]:
|
|
111
|
-
typer.echo(__('Cannot connect to self-hosted instance: instance status unknown'))
|
|
112
|
-
raise typer.Exit(1)
|
|
113
|
-
|
|
114
|
-
return instance
|
|
115
|
-
|
|
116
|
-
@error_handler()
|
|
117
|
-
def connect_to_rented_instance(
|
|
118
|
-
self,
|
|
119
|
-
instance_rented_slug: str,
|
|
120
|
-
input_ssh_key_path: Optional[str]
|
|
121
|
-
):
|
|
122
|
-
instance = self.get_rented_instance(instance_slug=instance_rented_slug)
|
|
123
|
-
|
|
124
|
-
if instance:
|
|
125
|
-
self.check_instance_status_to_connect(
|
|
126
|
-
instance=instance,
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
-
ssh_path_from_config: Optional[str] = None
|
|
130
|
-
if not input_ssh_key_path:
|
|
131
|
-
ssh_path_from_config = self.__config_provider.get_valid_private_key_path_by_ip_address(instance.ip_address)
|
|
132
|
-
if ssh_path_from_config:
|
|
133
|
-
typer.echo(f"Using configured ssh key for this instance: {ssh_path_from_config}")
|
|
134
|
-
|
|
135
|
-
if not input_ssh_key_path and not ssh_path_from_config:
|
|
136
|
-
typer.echo('Using SSH agent to connect to server instance')
|
|
137
|
-
|
|
138
|
-
self.__remote_server_service.connect_to_instance(
|
|
139
|
-
ip_address=instance.ip_address,
|
|
140
|
-
username=instance.host_username,
|
|
141
|
-
private_key_path=ssh_path_from_config or input_ssh_key_path
|
|
142
|
-
)
|
|
143
|
-
|
|
144
|
-
# cannot really detect how ssh connection was ended. capturing stderr using subprocess feels bad/unreliable.
|
|
145
|
-
if input_ssh_key_path:
|
|
146
|
-
self.__config_provider.update_remote_server_config_entry(ip_address=instance.ip_address, ssh_key_path=Path(input_ssh_key_path))
|
|
147
|
-
else:
|
|
148
|
-
typer.echo(__("Server instance not found: %instance_item%", {'instance_item': instance_rented_slug}))
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
@error_handler()
|
|
152
|
-
def connect_to_selfhosted_instance(
|
|
153
|
-
self,
|
|
154
|
-
selfhosted_instance_slug: str,
|
|
155
|
-
username: str,
|
|
156
|
-
input_ssh_key_path: Optional[str],
|
|
157
|
-
):
|
|
158
|
-
if not username:
|
|
159
|
-
username = 'root'
|
|
160
|
-
typer.echo(__("No remote server username provided, using 'root' as username"))
|
|
161
|
-
|
|
162
|
-
instance = self.get_self_hosted_instance(instance_slug=selfhosted_instance_slug)
|
|
163
|
-
|
|
164
|
-
if instance:
|
|
165
|
-
self.check_selfhosted_status_to_connect(
|
|
166
|
-
instance=instance,
|
|
167
|
-
)
|
|
168
|
-
|
|
169
|
-
ssh_path_from_config: Optional[str] = None
|
|
170
|
-
if not input_ssh_key_path:
|
|
171
|
-
ssh_path_from_config = self.__config_provider.get_valid_private_key_path_by_ip_address(instance.ip_address)
|
|
172
|
-
if ssh_path_from_config:
|
|
173
|
-
typer.echo(f"Using configured ssh key for this instance: {ssh_path_from_config}")
|
|
174
|
-
|
|
175
|
-
if not input_ssh_key_path and not ssh_path_from_config:
|
|
176
|
-
typer.echo('Using SSH agent to connect to server instance')
|
|
177
|
-
|
|
178
|
-
self.__remote_server_service.connect_to_instance(
|
|
179
|
-
ip_address=instance.ip_address,
|
|
180
|
-
username=username,
|
|
181
|
-
private_key_path=ssh_path_from_config or input_ssh_key_path
|
|
182
|
-
)
|
|
183
|
-
|
|
184
|
-
if input_ssh_key_path:
|
|
185
|
-
self.__config_provider.update_remote_server_config_entry(ip_address=instance.ip_address, ssh_key_path=Path(input_ssh_key_path))
|
|
186
|
-
else:
|
|
187
|
-
typer.echo(__("Server instance not found: %instance_item%", {'instance_item': selfhosted_instance_slug}))
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
@error_handler()
|
|
191
|
-
def get_rented_list(
|
|
192
|
-
self,
|
|
193
|
-
statuses: List[str],
|
|
194
|
-
row: int = 5,
|
|
195
|
-
page: int = 1,
|
|
196
|
-
) -> PaginatedEntityList[InstanceRentedDto]:
|
|
197
|
-
data = self.__thestage_api_client.get_rented_instance_list(
|
|
198
|
-
statuses=statuses,
|
|
199
|
-
page=page,
|
|
200
|
-
limit=row,
|
|
201
|
-
)
|
|
202
|
-
|
|
203
|
-
return data
|
|
204
|
-
|
|
205
|
-
@error_handler()
|
|
206
|
-
def get_self_hosted_list(
|
|
207
|
-
self,
|
|
208
|
-
statuses: List[str],
|
|
209
|
-
row: int = 5,
|
|
210
|
-
page: int = 1,
|
|
211
|
-
) -> PaginatedEntityList[SelfHostedInstanceDto]:
|
|
212
|
-
data = self.__thestage_api_client.get_selfhosted_instance_list(
|
|
213
|
-
statuses=statuses,
|
|
214
|
-
page=page,
|
|
215
|
-
limit=row,
|
|
216
|
-
)
|
|
217
|
-
return data
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
@error_handler()
|
|
221
|
-
def print_self_hosted_instance_list(self, statuses, row, page):
|
|
222
|
-
selfhosted_instance_status_map = self.__thestage_api_client.get_selfhosted_business_status_map()
|
|
223
|
-
|
|
224
|
-
if not statuses:
|
|
225
|
-
statuses = ({key: selfhosted_instance_status_map[key] for key in [
|
|
226
|
-
SelfhostedBusinessStatus.AWAITING_CONFIGURATION,
|
|
227
|
-
SelfhostedBusinessStatus.RUNNING,
|
|
228
|
-
SelfhostedBusinessStatus.UNREACHABLE_DAEMON,
|
|
229
|
-
]}).values()
|
|
230
|
-
|
|
231
|
-
if "all" in statuses:
|
|
232
|
-
statuses = selfhosted_instance_status_map.values()
|
|
233
|
-
|
|
234
|
-
for input_status_item in statuses:
|
|
235
|
-
if input_status_item not in selfhosted_instance_status_map.values():
|
|
236
|
-
typer.echo(__("'%invalid_status%' is not one of %valid_statuses%", {
|
|
237
|
-
'invalid_status': input_status_item,
|
|
238
|
-
'valid_statuses': str(list(selfhosted_instance_status_map.values()))
|
|
239
|
-
}))
|
|
240
|
-
raise typer.Exit(1)
|
|
241
|
-
|
|
242
|
-
typer.echo(__(
|
|
243
|
-
"Listing self-hosted instances with the following statuses: %statuses%, to view all self-hosted instances, use --status all",
|
|
244
|
-
placeholders={
|
|
245
|
-
'statuses': ', '.join([status_item for status_item in statuses])
|
|
246
|
-
}))
|
|
247
|
-
|
|
248
|
-
backend_statuses: List[str] = [key for key, value in selfhosted_instance_status_map.items() if value in statuses]
|
|
249
|
-
|
|
250
|
-
self.print(
|
|
251
|
-
func_get_data=self.get_self_hosted_list,
|
|
252
|
-
func_special_params={
|
|
253
|
-
'statuses': backend_statuses,
|
|
254
|
-
},
|
|
255
|
-
mapper=SelfHostedMapper(),
|
|
256
|
-
headers=list(map(lambda x: x.alias, SelfHostedInstanceEntity.model_fields.values())),
|
|
257
|
-
row=row,
|
|
258
|
-
page=page,
|
|
259
|
-
show_index="never",
|
|
260
|
-
)
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
@error_handler()
|
|
264
|
-
def print_rented_instance_list(self, statuses, row, page):
|
|
265
|
-
instance_rented_status_map = self.__thestage_api_client.get_rented_business_status_map()
|
|
266
|
-
|
|
267
|
-
if not statuses:
|
|
268
|
-
statuses = ({key: instance_rented_status_map[key] for key in [
|
|
269
|
-
InstanceRentedBusinessStatus.ONLINE,
|
|
270
|
-
InstanceRentedBusinessStatus.CREATING,
|
|
271
|
-
InstanceRentedBusinessStatus.TERMINATING,
|
|
272
|
-
InstanceRentedBusinessStatus.REBOOTING,
|
|
273
|
-
]}).values()
|
|
274
|
-
|
|
275
|
-
if "all" in statuses:
|
|
276
|
-
statuses = instance_rented_status_map.values()
|
|
277
|
-
|
|
278
|
-
for input_status_item in statuses:
|
|
279
|
-
if input_status_item not in instance_rented_status_map.values():
|
|
280
|
-
typer.echo(__("'%invalid_status%' is not one of %valid_statuses%", {
|
|
281
|
-
'invalid_status': input_status_item,
|
|
282
|
-
'valid_statuses': str(list(instance_rented_status_map.values()))
|
|
283
|
-
}))
|
|
284
|
-
raise typer.Exit(1)
|
|
285
|
-
|
|
286
|
-
typer.echo(__(
|
|
287
|
-
"Listing rented server instances with the following statuses: %statuses%, to view all rented server instances, use --status all",
|
|
288
|
-
placeholders={
|
|
289
|
-
'statuses': ', '.join([status_item for status_item in statuses])
|
|
290
|
-
}))
|
|
291
|
-
|
|
292
|
-
backend_statuses: List[str] = [key for key, value in instance_rented_status_map.items() if value in statuses]
|
|
293
|
-
|
|
294
|
-
self.print(
|
|
295
|
-
func_get_data=self.get_rented_list,
|
|
296
|
-
func_special_params={
|
|
297
|
-
'statuses': backend_statuses,
|
|
298
|
-
},
|
|
299
|
-
mapper=InstanceMapper(),
|
|
300
|
-
headers=list(map(lambda x: x.alias, RentedInstanceEntity.model_fields.values())),
|
|
301
|
-
row=row,
|
|
302
|
-
page=page,
|
|
303
|
-
show_index="never",
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import List, Optional, Dict
|
|
3
|
+
|
|
4
|
+
import typer
|
|
5
|
+
|
|
6
|
+
from thestage.entities.rented_instance import RentedInstanceEntity
|
|
7
|
+
from thestage.entities.self_hosted_instance import SelfHostedInstanceEntity
|
|
8
|
+
from thestage.i18n.translation import __
|
|
9
|
+
from thestage.services.clients.thestage_api.dtos.enums.selfhosted_status import SelfhostedBusinessStatus
|
|
10
|
+
from thestage.services.clients.thestage_api.dtos.enums.instance_rented_status import InstanceRentedBusinessStatus
|
|
11
|
+
from thestage.services.abstract_service import AbstractService
|
|
12
|
+
from thestage.helpers.error_handler import error_handler
|
|
13
|
+
from thestage.services.clients.thestage_api.api_client import TheStageApiClient
|
|
14
|
+
from thestage.services.clients.thestage_api.dtos.instance_rented_response import InstanceRentedDto
|
|
15
|
+
from thestage.services.clients.thestage_api.dtos.paginated_entity_list import PaginatedEntityList
|
|
16
|
+
from thestage.services.clients.thestage_api.dtos.selfhosted_instance_response import SelfHostedInstanceDto
|
|
17
|
+
from thestage.services.config_provider.config_provider import ConfigProvider
|
|
18
|
+
from thestage.services.instance.mapper.instance_mapper import InstanceMapper
|
|
19
|
+
from thestage.services.instance.mapper.selfhosted_mapper import SelfHostedMapper
|
|
20
|
+
from thestage.services.remote_server_service import RemoteServerService
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class InstanceService(AbstractService):
|
|
24
|
+
|
|
25
|
+
__thestage_api_client: TheStageApiClient = None
|
|
26
|
+
__config_provider: ConfigProvider = None
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
thestage_api_client: TheStageApiClient,
|
|
31
|
+
config_provider: ConfigProvider,
|
|
32
|
+
remote_server_service: RemoteServerService,
|
|
33
|
+
):
|
|
34
|
+
self.__thestage_api_client = thestage_api_client
|
|
35
|
+
self.__remote_server_service = remote_server_service
|
|
36
|
+
self.__config_provider = config_provider
|
|
37
|
+
|
|
38
|
+
def get_rented_instance(
|
|
39
|
+
self,
|
|
40
|
+
instance_slug: str,
|
|
41
|
+
) -> Optional[InstanceRentedDto]:
|
|
42
|
+
return self.__thestage_api_client.get_rented_instance(
|
|
43
|
+
instance_slug=instance_slug,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
def get_self_hosted_instance(
|
|
47
|
+
self,
|
|
48
|
+
instance_slug: str,
|
|
49
|
+
) -> Optional[SelfHostedInstanceDto]:
|
|
50
|
+
return self.__thestage_api_client.get_selfhosted_instance(
|
|
51
|
+
instance_slug=instance_slug,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
@error_handler()
|
|
55
|
+
def check_instance_status_to_connect(
|
|
56
|
+
self,
|
|
57
|
+
instance: InstanceRentedDto,
|
|
58
|
+
) -> InstanceRentedDto:
|
|
59
|
+
if instance:
|
|
60
|
+
if instance.frontend_status.status_key in [
|
|
61
|
+
InstanceRentedBusinessStatus.IN_QUEUE.name,
|
|
62
|
+
InstanceRentedBusinessStatus.CREATING.name,
|
|
63
|
+
InstanceRentedBusinessStatus.REBOOTING.name,
|
|
64
|
+
InstanceRentedBusinessStatus.STARTING.name,
|
|
65
|
+
]:
|
|
66
|
+
typer.echo(__('Cannot connect to rented server instance: it is either in the process of being rented or rebooted'))
|
|
67
|
+
raise typer.Exit(1)
|
|
68
|
+
elif instance.frontend_status.status_key in [
|
|
69
|
+
InstanceRentedBusinessStatus.TERMINATING.name,
|
|
70
|
+
InstanceRentedBusinessStatus.RENTAL_ERROR.name,
|
|
71
|
+
]:
|
|
72
|
+
typer.echo(__('Cannot connect to rented server instance: renting process failed'))
|
|
73
|
+
raise typer.Exit(1)
|
|
74
|
+
elif instance.frontend_status.status_key in [
|
|
75
|
+
InstanceRentedBusinessStatus.STOPPED.name,
|
|
76
|
+
InstanceRentedBusinessStatus.STOPPING.name,
|
|
77
|
+
InstanceRentedBusinessStatus.DELETED.name,
|
|
78
|
+
]:
|
|
79
|
+
typer.echo(__('Cannot connect to rented server instance: it is either stopped or has been deleted'))
|
|
80
|
+
raise typer.Exit(1)
|
|
81
|
+
elif instance.frontend_status.status_key in [
|
|
82
|
+
InstanceRentedBusinessStatus.UNKNOWN.name,
|
|
83
|
+
InstanceRentedBusinessStatus.ALL.name,
|
|
84
|
+
]:
|
|
85
|
+
typer.echo(__('Cannot connect to rented server instance: instance status unknown'))
|
|
86
|
+
raise typer.Exit(1)
|
|
87
|
+
|
|
88
|
+
return instance
|
|
89
|
+
|
|
90
|
+
@error_handler()
|
|
91
|
+
def check_selfhosted_status_to_connect(
|
|
92
|
+
self,
|
|
93
|
+
instance: SelfHostedInstanceDto,
|
|
94
|
+
) -> SelfHostedInstanceDto:
|
|
95
|
+
if instance:
|
|
96
|
+
if instance.frontend_status.status_key in [
|
|
97
|
+
SelfhostedBusinessStatus.AWAITING_CONFIGURATION.name,
|
|
98
|
+
]:
|
|
99
|
+
typer.echo(__('Cannot connect to self-hosted instance: it is awaiting configuration'))
|
|
100
|
+
raise typer.Exit(1)
|
|
101
|
+
elif instance.frontend_status.status_key in [
|
|
102
|
+
SelfhostedBusinessStatus.UNREACHABLE_DAEMON.name,
|
|
103
|
+
SelfhostedBusinessStatus.DELETED.name,
|
|
104
|
+
]:
|
|
105
|
+
typer.echo(__('Cannot connect to self-hosted instance: it may be turned off or unreachable'))
|
|
106
|
+
raise typer.Exit(1)
|
|
107
|
+
elif instance.frontend_status.status_key in [
|
|
108
|
+
SelfhostedBusinessStatus.UNKNOWN.name,
|
|
109
|
+
SelfhostedBusinessStatus.ALL.name,
|
|
110
|
+
]:
|
|
111
|
+
typer.echo(__('Cannot connect to self-hosted instance: instance status unknown'))
|
|
112
|
+
raise typer.Exit(1)
|
|
113
|
+
|
|
114
|
+
return instance
|
|
115
|
+
|
|
116
|
+
@error_handler()
|
|
117
|
+
def connect_to_rented_instance(
|
|
118
|
+
self,
|
|
119
|
+
instance_rented_slug: str,
|
|
120
|
+
input_ssh_key_path: Optional[str]
|
|
121
|
+
):
|
|
122
|
+
instance = self.get_rented_instance(instance_slug=instance_rented_slug)
|
|
123
|
+
|
|
124
|
+
if instance:
|
|
125
|
+
self.check_instance_status_to_connect(
|
|
126
|
+
instance=instance,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
ssh_path_from_config: Optional[str] = None
|
|
130
|
+
if not input_ssh_key_path:
|
|
131
|
+
ssh_path_from_config = self.__config_provider.get_valid_private_key_path_by_ip_address(instance.ip_address)
|
|
132
|
+
if ssh_path_from_config:
|
|
133
|
+
typer.echo(f"Using configured ssh key for this instance: {ssh_path_from_config}")
|
|
134
|
+
|
|
135
|
+
if not input_ssh_key_path and not ssh_path_from_config:
|
|
136
|
+
typer.echo('Using SSH agent to connect to server instance')
|
|
137
|
+
|
|
138
|
+
self.__remote_server_service.connect_to_instance(
|
|
139
|
+
ip_address=instance.ip_address,
|
|
140
|
+
username=instance.host_username,
|
|
141
|
+
private_key_path=ssh_path_from_config or input_ssh_key_path
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# cannot really detect how ssh connection was ended. capturing stderr using subprocess feels bad/unreliable.
|
|
145
|
+
if input_ssh_key_path:
|
|
146
|
+
self.__config_provider.update_remote_server_config_entry(ip_address=instance.ip_address, ssh_key_path=Path(input_ssh_key_path))
|
|
147
|
+
else:
|
|
148
|
+
typer.echo(__("Server instance not found: %instance_item%", {'instance_item': instance_rented_slug}))
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@error_handler()
|
|
152
|
+
def connect_to_selfhosted_instance(
|
|
153
|
+
self,
|
|
154
|
+
selfhosted_instance_slug: str,
|
|
155
|
+
username: str,
|
|
156
|
+
input_ssh_key_path: Optional[str],
|
|
157
|
+
):
|
|
158
|
+
if not username:
|
|
159
|
+
username = 'root'
|
|
160
|
+
typer.echo(__("No remote server username provided, using 'root' as username"))
|
|
161
|
+
|
|
162
|
+
instance = self.get_self_hosted_instance(instance_slug=selfhosted_instance_slug)
|
|
163
|
+
|
|
164
|
+
if instance:
|
|
165
|
+
self.check_selfhosted_status_to_connect(
|
|
166
|
+
instance=instance,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
ssh_path_from_config: Optional[str] = None
|
|
170
|
+
if not input_ssh_key_path:
|
|
171
|
+
ssh_path_from_config = self.__config_provider.get_valid_private_key_path_by_ip_address(instance.ip_address)
|
|
172
|
+
if ssh_path_from_config:
|
|
173
|
+
typer.echo(f"Using configured ssh key for this instance: {ssh_path_from_config}")
|
|
174
|
+
|
|
175
|
+
if not input_ssh_key_path and not ssh_path_from_config:
|
|
176
|
+
typer.echo('Using SSH agent to connect to server instance')
|
|
177
|
+
|
|
178
|
+
self.__remote_server_service.connect_to_instance(
|
|
179
|
+
ip_address=instance.ip_address,
|
|
180
|
+
username=username,
|
|
181
|
+
private_key_path=ssh_path_from_config or input_ssh_key_path
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
if input_ssh_key_path:
|
|
185
|
+
self.__config_provider.update_remote_server_config_entry(ip_address=instance.ip_address, ssh_key_path=Path(input_ssh_key_path))
|
|
186
|
+
else:
|
|
187
|
+
typer.echo(__("Server instance not found: %instance_item%", {'instance_item': selfhosted_instance_slug}))
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
@error_handler()
|
|
191
|
+
def get_rented_list(
|
|
192
|
+
self,
|
|
193
|
+
statuses: List[str],
|
|
194
|
+
row: int = 5,
|
|
195
|
+
page: int = 1,
|
|
196
|
+
) -> PaginatedEntityList[InstanceRentedDto]:
|
|
197
|
+
data = self.__thestage_api_client.get_rented_instance_list(
|
|
198
|
+
statuses=statuses,
|
|
199
|
+
page=page,
|
|
200
|
+
limit=row,
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
return data
|
|
204
|
+
|
|
205
|
+
@error_handler()
|
|
206
|
+
def get_self_hosted_list(
|
|
207
|
+
self,
|
|
208
|
+
statuses: List[str],
|
|
209
|
+
row: int = 5,
|
|
210
|
+
page: int = 1,
|
|
211
|
+
) -> PaginatedEntityList[SelfHostedInstanceDto]:
|
|
212
|
+
data = self.__thestage_api_client.get_selfhosted_instance_list(
|
|
213
|
+
statuses=statuses,
|
|
214
|
+
page=page,
|
|
215
|
+
limit=row,
|
|
216
|
+
)
|
|
217
|
+
return data
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
@error_handler()
|
|
221
|
+
def print_self_hosted_instance_list(self, statuses, row, page):
|
|
222
|
+
selfhosted_instance_status_map = self.__thestage_api_client.get_selfhosted_business_status_map()
|
|
223
|
+
|
|
224
|
+
if not statuses:
|
|
225
|
+
statuses = ({key: selfhosted_instance_status_map[key] for key in [
|
|
226
|
+
SelfhostedBusinessStatus.AWAITING_CONFIGURATION,
|
|
227
|
+
SelfhostedBusinessStatus.RUNNING,
|
|
228
|
+
SelfhostedBusinessStatus.UNREACHABLE_DAEMON,
|
|
229
|
+
]}).values()
|
|
230
|
+
|
|
231
|
+
if "all" in statuses:
|
|
232
|
+
statuses = selfhosted_instance_status_map.values()
|
|
233
|
+
|
|
234
|
+
for input_status_item in statuses:
|
|
235
|
+
if input_status_item not in selfhosted_instance_status_map.values():
|
|
236
|
+
typer.echo(__("'%invalid_status%' is not one of %valid_statuses%", {
|
|
237
|
+
'invalid_status': input_status_item,
|
|
238
|
+
'valid_statuses': str(list(selfhosted_instance_status_map.values()))
|
|
239
|
+
}))
|
|
240
|
+
raise typer.Exit(1)
|
|
241
|
+
|
|
242
|
+
typer.echo(__(
|
|
243
|
+
"Listing self-hosted instances with the following statuses: %statuses%, to view all self-hosted instances, use --status all",
|
|
244
|
+
placeholders={
|
|
245
|
+
'statuses': ', '.join([status_item for status_item in statuses])
|
|
246
|
+
}))
|
|
247
|
+
|
|
248
|
+
backend_statuses: List[str] = [key for key, value in selfhosted_instance_status_map.items() if value in statuses]
|
|
249
|
+
|
|
250
|
+
self.print(
|
|
251
|
+
func_get_data=self.get_self_hosted_list,
|
|
252
|
+
func_special_params={
|
|
253
|
+
'statuses': backend_statuses,
|
|
254
|
+
},
|
|
255
|
+
mapper=SelfHostedMapper(),
|
|
256
|
+
headers=list(map(lambda x: x.alias, SelfHostedInstanceEntity.model_fields.values())),
|
|
257
|
+
row=row,
|
|
258
|
+
page=page,
|
|
259
|
+
show_index="never",
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
@error_handler()
|
|
264
|
+
def print_rented_instance_list(self, statuses, row, page):
|
|
265
|
+
instance_rented_status_map = self.__thestage_api_client.get_rented_business_status_map()
|
|
266
|
+
|
|
267
|
+
if not statuses:
|
|
268
|
+
statuses = ({key: instance_rented_status_map[key] for key in [
|
|
269
|
+
InstanceRentedBusinessStatus.ONLINE,
|
|
270
|
+
InstanceRentedBusinessStatus.CREATING,
|
|
271
|
+
InstanceRentedBusinessStatus.TERMINATING,
|
|
272
|
+
InstanceRentedBusinessStatus.REBOOTING,
|
|
273
|
+
]}).values()
|
|
274
|
+
|
|
275
|
+
if "all" in statuses:
|
|
276
|
+
statuses = instance_rented_status_map.values()
|
|
277
|
+
|
|
278
|
+
for input_status_item in statuses:
|
|
279
|
+
if input_status_item not in instance_rented_status_map.values():
|
|
280
|
+
typer.echo(__("'%invalid_status%' is not one of %valid_statuses%", {
|
|
281
|
+
'invalid_status': input_status_item,
|
|
282
|
+
'valid_statuses': str(list(instance_rented_status_map.values()))
|
|
283
|
+
}))
|
|
284
|
+
raise typer.Exit(1)
|
|
285
|
+
|
|
286
|
+
typer.echo(__(
|
|
287
|
+
"Listing rented server instances with the following statuses: %statuses%, to view all rented server instances, use --status all",
|
|
288
|
+
placeholders={
|
|
289
|
+
'statuses': ', '.join([status_item for status_item in statuses])
|
|
290
|
+
}))
|
|
291
|
+
|
|
292
|
+
backend_statuses: List[str] = [key for key, value in instance_rented_status_map.items() if value in statuses]
|
|
293
|
+
|
|
294
|
+
self.print(
|
|
295
|
+
func_get_data=self.get_rented_list,
|
|
296
|
+
func_special_params={
|
|
297
|
+
'statuses': backend_statuses,
|
|
298
|
+
},
|
|
299
|
+
mapper=InstanceMapper(),
|
|
300
|
+
headers=list(map(lambda x: x.alias, RentedInstanceEntity.model_fields.values())),
|
|
301
|
+
row=row,
|
|
302
|
+
page=page,
|
|
303
|
+
show_index="never",
|
|
304
304
|
)
|
|
File without changes
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
from typing import Optional
|
|
2
|
-
|
|
3
|
-
from thestage.entities.rented_instance import RentedInstanceEntity
|
|
4
|
-
from thestage.services.clients.thestage_api.dtos.instance_rented_response import InstanceRentedDto
|
|
5
|
-
from thestage.services.abstract_mapper import AbstractMapper
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class InstanceMapper(AbstractMapper):
|
|
9
|
-
|
|
10
|
-
def build_entity(self, item: InstanceRentedDto) -> Optional[RentedInstanceEntity]:
|
|
11
|
-
if not item:
|
|
12
|
-
return None
|
|
13
|
-
|
|
14
|
-
return RentedInstanceEntity(
|
|
15
|
-
slug=item.slug if item.slug else '',
|
|
16
|
-
title=item.title if item.title else '',
|
|
17
|
-
cpu_type=item.cpu_type if item.cpu_type else '',
|
|
18
|
-
gpu_type=item.gpu_type if item.gpu_type else '',
|
|
19
|
-
cpu_cores=str(item.cpu_cores) if item.cpu_cores else '',
|
|
20
|
-
ip_address=item.ip_address if item.ip_address else '',
|
|
21
|
-
status=item.frontend_status.status_translation if item.frontend_status else '',
|
|
22
|
-
created_at=str(item.created_at.strftime("%Y-%m-%d %H:%M:%S")) if item.created_at else '',
|
|
23
|
-
updated_at=str(item.updated_at.strftime("%Y-%m-%d %H:%M:%S")) if item.updated_at else '',
|
|
24
|
-
)
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from thestage.entities.rented_instance import RentedInstanceEntity
|
|
4
|
+
from thestage.services.clients.thestage_api.dtos.instance_rented_response import InstanceRentedDto
|
|
5
|
+
from thestage.services.abstract_mapper import AbstractMapper
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class InstanceMapper(AbstractMapper):
|
|
9
|
+
|
|
10
|
+
def build_entity(self, item: InstanceRentedDto) -> Optional[RentedInstanceEntity]:
|
|
11
|
+
if not item:
|
|
12
|
+
return None
|
|
13
|
+
|
|
14
|
+
return RentedInstanceEntity(
|
|
15
|
+
slug=item.slug if item.slug else '',
|
|
16
|
+
title=item.title if item.title else '',
|
|
17
|
+
cpu_type=item.cpu_type if item.cpu_type else '',
|
|
18
|
+
gpu_type=item.gpu_type if item.gpu_type else '',
|
|
19
|
+
cpu_cores=str(item.cpu_cores) if item.cpu_cores else '',
|
|
20
|
+
ip_address=item.ip_address if item.ip_address else '',
|
|
21
|
+
status=item.frontend_status.status_translation if item.frontend_status else '',
|
|
22
|
+
created_at=str(item.created_at.strftime("%Y-%m-%d %H:%M:%S")) if item.created_at else '',
|
|
23
|
+
updated_at=str(item.updated_at.strftime("%Y-%m-%d %H:%M:%S")) if item.updated_at else '',
|
|
24
|
+
)
|