1 # Copyright (C) 2022-2024 Wind River Systems, Inc.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # client talking to Stx standalone
17 from typing import List # Optional, Set
20 from cgtsclient.client import get_client as get_stx_client
21 from cgtsclient.exc import EndpointException
22 from dcmanagerclient.api.client import client as get_dc_client
23 from fmclient.client import get_client as get_fm_client
24 from fmclient.common.exceptions import HTTPNotFound, HttpServerError
26 from o2app.adapter import unit_of_work
27 from o2common.config import config
28 from o2common.service.client.base_client import BaseClient
29 from o2ims.domain import alarm_obj as alarmModel
31 from o2common.helper import o2logging
32 logger = o2logging.get_logger(__name__)
35 CGTSCLIENT_ENDPOINT_ERROR_MSG = \
36 'Must provide Keystone credentials or user-defined endpoint and token'
39 class StxAlarmClient(BaseClient):
40 def __init__(self, uow: unit_of_work.AbstractUnitOfWork, driver=None):
42 self.driver = driver if driver else StxFaultClientImp()
45 def _get(self, id) -> alarmModel.FaultGenericModel:
46 return self.driver.getAlarmInfo(id)
48 def _list(self, **filters) -> List[alarmModel.FaultGenericModel]:
49 newmodels = self.driver.getAlarmList(**filters)
53 rs = uow.session.execute(
55 SELECT "alarmEventRecordId"
56 FROM "alarmEventRecord"
57 WHERE "perceivedSeverity" != :perceived_severity_enum
59 dict(perceived_severity_enum=alarmModel.PerceivedSeverityEnum.
64 # logger.debug('Exist alarm: ' + id)
65 exist_alarms[id] = False
70 if exist_alarms[m.id]:
72 exist_alarms[m.id] = True
74 logger.debug('alarm new: ' + m.id)
77 for alarm in exist_alarms:
78 logger.debug('exist alarm: ' + alarm)
79 if exist_alarms[alarm]:
80 # exist alarm is active
83 event = self._get(alarm)
85 logger.debug('alarm {} not in this resource pool {}'
86 .format(alarm, self._pool_id))
88 except HttpServerError:
89 # TODO(jon): This exception needs to be removed when the
90 # INF-457 related FM client upgrade and issue fix occur.
91 logger.debug('alarm {} query failed'.format(alarm))
97 def _set_stx_client(self):
98 self.driver.setFaultClient(self._pool_id)
100 def delete(self, id) -> alarmModel.FaultGenericModel:
101 return self.driver.deleteAlarm(id)
104 class StxEventClient(BaseClient):
105 def __init__(self, uow: unit_of_work.AbstractUnitOfWork, driver=None):
107 self.driver = driver if driver else StxFaultClientImp()
110 def _get(self, id) -> alarmModel.FaultGenericModel:
111 return self.driver.getEventInfo(id)
113 def _list(self, **filters) -> List[alarmModel.FaultGenericModel]:
114 return self.driver.getEventList(**filters)
116 def _set_stx_client(self):
117 self.driver.setFaultClient(self._pool_id)
119 def suppression_list(self, alarm_id) -> List[alarmModel.FaultGenericModel]:
120 return self.driver.getSuppressionList(alarm_id)
122 def suppress(self, id) -> alarmModel.FaultGenericModel:
123 return self.driver.suppressEvent(id)
126 # internal driver which implement client call to Stx Fault Management instance
127 class StxFaultClientImp(object):
128 def __init__(self, fm_client=None, stx_client=None, dc_client=None):
130 self.fmclient = fm_client if fm_client else self.getFmClient()
131 self.stxclient = stx_client if stx_client else self.getStxClient()
132 self.dcclient = dc_client if dc_client else self.getDcmanagerClient()
134 def getStxClient(self):
135 os_client_args = config.get_stx_access_info()
136 config_client = get_stx_client(**os_client_args)
139 def getDcmanagerClient(self):
140 os_client_args = config.get_dc_access_info()
141 config_client = get_dc_client(**os_client_args)
144 def getFmClient(self):
145 os_client_args = config.get_fm_access_info()
146 config_client = get_fm_client(1, **os_client_args)
149 def getSubcloudList(self):
150 self.dcclient = self.getDcmanagerClient()
151 subs = self.dcclient.subcloud_manager.list_subclouds()
152 known_subs = [sub for sub in subs if sub.sync_status != 'unknown']
155 def getSubcloudFaultClient(self, subcloud_id):
156 subcloud = self.dcclient.subcloud_manager.\
157 subcloud_additional_details(subcloud_id)
158 logger.debug('subcloud name: %s, oam_floating_ip: %s' %
159 (subcloud[0].name, subcloud[0].oam_floating_ip))
162 os_client_args = config.get_stx_access_info(
163 region_name=subcloud[0].region_name,
164 subcloud_hostname=subcloud[0].oam_floating_ip)
165 stx_client = get_stx_client(**os_client_args)
166 except EndpointException as e:
167 msg = e.format_message()
168 if CGTSCLIENT_ENDPOINT_ERROR_MSG in msg:
170 os_client_args = config.get_stx_access_info(
171 region_name=subcloud[0].region_name,
172 sub_is_https=sub_is_https,
173 subcloud_hostname=subcloud[0].oam_floating_ip)
174 stx_client = get_stx_client(**os_client_args)
176 raise ValueError('Stx endpoint exception: %s' % msg)
178 raise ValueError('cgtsclient get subcloud client failed')
180 os_client_args = config.get_fm_access_info(
181 sub_is_https=sub_is_https,
182 subcloud_hostname=subcloud[0].oam_floating_ip)
183 fm_client = get_fm_client(1, **os_client_args)
185 return stx_client, fm_client
187 def setFaultClient(self, resource_pool_id):
188 systems = self.stxclient.isystem.list()
189 if resource_pool_id == systems[0].uuid:
190 logger.debug('Fault Client not change: %s' % resource_pool_id)
191 self.fmclient = self.getFmClient()
194 subclouds = self.getSubcloudList()
195 for subcloud in subclouds:
196 substxclient, subfaultclient = self.getSubcloudFaultClient(
197 subcloud.subcloud_id)
198 systems = substxclient.isystem.list()
199 if resource_pool_id == systems[0].uuid:
200 self.fmclient = subfaultclient
202 def getAlarmList(self, **filters) -> List[alarmModel.FaultGenericModel]:
203 alarms = self.fmclient.alarm.list(expand=True)
207 'alarm:' + str(alarm.to_dict())) for alarm in alarms if alarm]
208 return [alarmModel.FaultGenericModel(
209 alarmModel.EventTypeEnum.ALARM, self._alarmconverter(alarm))
210 for alarm in alarms if alarm]
212 def getAlarmInfo(self, id) -> alarmModel.FaultGenericModel:
214 alarm = self.fmclient.alarm.get(id)
216 'get alarm id: ' + id + ', result:' + str(alarm.to_dict()))
218 event = self.fmclient.event_log.get(id)
219 return alarmModel.FaultGenericModel(
220 alarmModel.EventTypeEnum.ALARM, self._eventconverter(event,
222 return alarmModel.FaultGenericModel(
223 alarmModel.EventTypeEnum.ALARM, self._alarmconverter(alarm))
225 def deleteAlarm(self, id) -> alarmModel.FaultGenericModel:
226 alarm = self.fmclient.alarm.delete(id)
227 logger.debug('delete alarm id ' + id + ':' + str(alarm.to_dict()))
228 return alarmModel.FaultGenericModel(
229 alarmModel.EventTypeEnum.ALARM, self._alarmconverter(alarm))
231 def getEventList(self, **filters) -> List[alarmModel.FaultGenericModel]:
232 events = self.fmclient.event_log.list(alarms=True, expand=True)
234 'alarm:' + str(event.to_dict())) for event in events if event]
235 return [alarmModel.FaultGenericModel(
236 alarmModel.EventTypeEnum.EVENT, self._eventconverter(event))
237 for event in events if event]
239 def getEventInfo(self, id) -> alarmModel.FaultGenericModel:
240 event = self.fmclient.event_log.get(id)
241 logger.debug('get event id ' + id + ':' + str(event.to_dict()))
242 return alarmModel.FaultGenericModel(
243 alarmModel.EventTypeEnum.EVENT, self._eventconverter(event))
245 def suppressEvent(self, id) -> alarmModel.FaultGenericModel:
246 patch = [dict(path='/' + 'suppression_status', value='suppressed',
248 event = self.fmclient.event_suppression.update(id, patch)
249 logger.debug('suppressed event id ' + id + ':' + str(event.to_dict()))
250 return alarmModel.FaultGenericModel(
251 alarmModel.EventTypeEnum.EVENT, self._suppression_converter(event))
253 def getSuppressionList(self, alarm_id) -> alarmModel.FaultGenericModel:
254 suppression_list = []
256 events = self.fmclient.event_suppression.list(q=query_as_array)
258 if event.alarm_id == alarm_id:
259 # logger.debug('suppression event:' + str(event.to_dict()))
260 suppression_list.append(
261 alarmModel.FaultGenericModel(
262 alarmModel.EventTypeEnum.EVENT,
263 self._suppression_converter(event)))
264 return suppression_list
267 def _alarmconverter(alarm):
269 'alarm_id', 'alarm_state', 'entity_type_id', 'entity_instance_id',
270 'reason_text', 'alarm_type', 'probable_cause',
271 'proposed_repair_action', 'service_affecting', 'suppression',
272 'suppression_status', 'mgmt_affecting', 'degrade_affecting'
274 content = alarm.to_dict()
276 filter(lambda item: item[0] in selected_keys, content.items()))
277 setattr(alarm, 'filtered', filtered)
278 # setattr(alarm, 'alarm_def_id', uuid.uuid3(
279 # uuid.NAMESPACE_URL, alarm.alarm_id))
280 setattr(alarm, 'state', alarm.alarm_state)
282 setattr(alarm, 'alarm_def_id', str(uuid.uuid3(
283 uuid.NAMESPACE_URL, alarm.alarm_id)))
284 setattr(alarm, 'probable_cause_id', str(uuid.uuid3(
285 uuid.NAMESPACE_URL, alarm.probable_cause)))
289 def _eventconverter(event, clear=False):
291 'event_log_id', 'state', 'entity_type_id',
292 'entity_instance_id', 'reason_text', 'event_log_type',
293 'probable_cause', 'proposed_repair_action',
294 'service_affecting', 'suppression', 'suppression_status'
296 content = event.to_dict()
298 filter(lambda item: item[0] in selected_keys, content.items()))
299 setattr(event, 'filtered', filtered)
300 setattr(event, 'alarm_id', event.event_log_id)
301 setattr(event, 'alarm_type', event.event_log_type)
303 logger.debug('alarm is clear')
304 event.state = 'clear'
305 setattr(event, 'alarm_def_id', str(uuid.uuid3(
306 uuid.NAMESPACE_URL, event.alarm_id)))
307 setattr(event, 'probable_cause_id', str(uuid.uuid3(
308 uuid.NAMESPACE_URL, event.probable_cause)))
312 def _suppression_converter(event, clear=False):
314 'alarm_id', 'description', 'suppression_status',
317 content = event.to_dict()
319 filter(lambda item: item[0] in selected_keys, content.items()))
320 setattr(event, 'filtered', filtered)
321 setattr(event, 'uuid', event.uuid)
322 setattr(event, 'alarm_id', event.alarm_id)
323 setattr(event, 'description', event.description)
324 setattr(event, 'suppression_status', event.suppression_status)
325 setattr(event, 'alarm_type', None)
326 setattr(event, 'alarm_def_id', None)
327 setattr(event, 'probable_cause_id', None)
328 setattr(event, 'state', None)
329 setattr(event, 'timestamp', None)
333 def _alarmeventhasher(event, state=''):
334 # The event model and the alarm model have different parameter name
335 # of the state. alarm model is alarm_state, event model is state.
336 status = event.alarm_state if state == '' else state
337 return str(hash((event.uuid, event.timestamp, status)))