blob: 6307704f2ca8253637dfaf9d9a43df7cf0533138 [file] [log] [blame]
Patricia Reinoso02a39fd2023-03-08 17:13:56 +00001#######################################################################################
2# Copyright ETSI Contributors and Others.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13# implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17import logging
18from temporalio import activity
19from time import time
20from n2vc.temporal_libjuju import ConnectionInfo, Libjuju
21from osm_common.temporal_constants import (
22 ACTIVITY_DELETE_VIM,
23 ACTIVITY_TEST_VIM_CONNECTIVITY,
24 ACTIVITY_UPDATE_VIM_OPERATION_STATE,
25 ACTIVITY_UPDATE_VIM_STATE,
26)
27from osm_common.dataclasses.temporal_dataclasses import (
28 DeleteVimInput,
29 TestVimConnectivityInput,
30 UpdateVimOperationStateInput,
31 UpdateVimStateInput,
32)
33
34activity.logger = logging.getLogger("lcm.temporal.vim_activities")
35
36
37class JujuPaasConnector:
38 """Handles Juju Controller operations.
39
40 Args:
41 db (object): Data Access Object
42 """
43
44 def __init__(self, db):
45 self.db = db
46
47 def _decrypt_password(self, vim_content: dict) -> str:
48 """Decrypt a password.
49 vim_content (dict): VIM details as a dictionary
50
51 Returns:
52 plain text password (str)
53 """
54 return self.db.decrypt(
55 vim_content["vim_password"],
56 schema_version=vim_content["schema_version"],
57 salt=vim_content["_id"],
58 )
59
60 def _get_connection_info(self, vim_id: str) -> ConnectionInfo:
61 """Get VIM details from database using vim_id and returns
62 the Connection Info to connect Juju Controller.
63
64 Args:
65 vim_id (str): VIM ID
66
67 Returns:
68 ConnectionInfo (object)
69 """
70 vim_content = self.db.get_one("vim_accounts", {"_id": vim_id})
71 endpoint = vim_content["vim_url"]
72 username = vim_content["vim_user"]
73 vim_config = vim_content["config"]
74 cacert = vim_config["ca_cert_content"]
75 cloud_name = vim_config["cloud"]
76 cloud_credentials = vim_config["cloud_credentials"]
77 password = self._decrypt_password(vim_content)
78 return ConnectionInfo(
79 endpoint, username, password, cacert, cloud_name, cloud_credentials
80 )
81
82 @activity.defn(name=ACTIVITY_TEST_VIM_CONNECTIVITY)
83 async def test_vim_connectivity(
84 self, test_connectivity_input: TestVimConnectivityInput
85 ) -> None:
86 """Validates the credentials by attempting to connect to the given Juju Controller.
87
88 Collaborators:
89 DB Read: vim_accounts
90 Juju Controller: Connect only
91
92 Raises (Retryable):
93 ApplicationError If any of password, cacert, cloud_credentials is invalid
94 or Juju controller is not reachable
95
96 Activity Lifecycle:
97 This activity should complete relatively quickly (in a few seconds).
98 However, it would be reasonable to wait more than 72 seconds (network timeout)
99 incase there are network issues.
100
101 This activity will not report a heartbeat due to its
102 short-running nature.
103
104 It is recommended, although not necessary to implement a
105 back-off strategy for this activity, as it will naturally block
106 and wait on each connection attempt.
107 """
108 vim_id = test_connectivity_input.vim_uuid
109 controller = None
110 try:
111 connection_info = self._get_connection_info(vim_id)
112 libjuju = Libjuju(connection_info)
113 controller = await libjuju.get_controller()
114 message = f"Connection to juju controller succeeded for {vim_id}"
115 activity.logger.info(message)
116 finally:
117 await libjuju.disconnect_controller(controller)
118
119
120class VimDbActivity:
121 """Perform Database operations for VIM accounts.
122
123 Args:
124 db (object): Data Access Object
125 """
126
127 def __init__(self, db):
128 self.db = db
129
130 @activity.defn(name=ACTIVITY_UPDATE_VIM_STATE)
131 async def update_vim_state(self, data: UpdateVimStateInput) -> None:
132 """
133 Changes the state of the VIM itself. Should be either
134 ENABLED or ERROR, however this activity does not validate
135 the state as no validation was done in OSM previously.
136
137 Collaborators:
138 DB Write: vim_accounts
139
140 Raises (Retryable):
141 DbException If the target DB record does not exist or DB is not reachable.
142
143 Activity Lifecycle:
144 This activity will not report a heartbeat due to its
145 short-running nature.
146
147 As this is a direct DB update, it is not recommended to have
148 any specific retry policy
149 """
150 update_vim_state = {
151 "_admin.operationalState": data.operational_state.name,
152 "_admin.detailed-status": data.message,
153 "_admin.modified": time(),
154 }
155
156 self.db.set_one("vim_accounts", {"_id": data.vim_uuid}, update_vim_state)
157 activity.logger.debug(
158 f"Updated VIM {data.vim_uuid} to {data.operational_state.name}"
159 )
160
161 @activity.defn(name=ACTIVITY_UPDATE_VIM_OPERATION_STATE)
162 async def update_vim_operation_state(
163 self, data: UpdateVimOperationStateInput
164 ) -> None:
165 """
166 Changes the state of a VIM operation task. Should be done to
167 indicate progress, or completion of the task itself.
168
169 Collaborators:
170 DB Write: vim_accounts
171
172 Raises (Retryable):
173 DbException If the target DB record does not exist or DB is not reachable.
174
175 Activity Lifecycle:
176 This activity will not report a heartbeat due to its
177 short-running nature.
178
179 As this is a direct DB update, it is not recommended to have
180 any specific retry policy
181 """
182 update_operation_state = {
183 f"_admin.operations.{format(data.op_id)}.operationState": data.op_state.name,
184 f"_admin.operations.{format(data.op_id)}.detailed-status": data.message,
185 "_admin.current_operation": None,
186 }
187
188 self.db.set_one("vim_accounts", {"_id": data.vim_uuid}, update_operation_state)
189 activity.logger.debug(
190 f"Updated VIM {data.vim_uuid} OP ID {data.op_id} to {data.op_state.name}"
191 )
192
193 @activity.defn(name=ACTIVITY_DELETE_VIM)
194 async def delete_vim_record(self, data: DeleteVimInput) -> None:
195 """
196 Deletes the VIM record from the database.
197
198 Collaborators:
199 DB Delete: vim_accounts
200
Mark Beierl2bed6072023-04-05 20:01:41 +0000201 Raises (Retryable):
Patricia Reinoso02a39fd2023-03-08 17:13:56 +0000202 DbException If the target DB record does not exist or DB is not reachable.
203
204 Activity Lifecycle:
205 This activity will not report a heartbeat due to its
206 short-running nature.
207
208 As this is a direct DB update, it is not recommended to have
209 any specific retry policy
210 """
211
212 self.db.del_one("vim_accounts", {"_id": data.vim_uuid})
213 activity.logger.debug(f"Removed VIM {data.vim_uuid}")