diff --git a/src/service/clmcservice/alertsapi/views.py b/src/service/clmcservice/alertsapi/views.py index db276f02d0a5b03d55269bbca3b0f927e9e0e339..19957f4495e424e647bba10fecdd980bfe833a59 100644 --- a/src/service/clmcservice/alertsapi/views.py +++ b/src/service/clmcservice/alertsapi/views.py @@ -59,32 +59,37 @@ class AlertsConfigurationAPI(object): @view_config(route_name='alerts_configuration', request_method='POST') def post_alerts_specification(self): """ - The view for receiving and configuring alerts based on the TOSCA alerts specification document. + The view for receiving and configuring alerts based on the TOSCA alerts specification document. This endpoint must also receive the TOSCA resources specification document for validation. :raises HTTPBadRequest: if the request doesn't contain a (YAML) file input referenced as alert-spec representing the TOSCA Alerts Specification """ kapacitor_host, kapacitor_port = self.request.registry.settings['kapacitor_host'], self.request.registry.settings['kapacitor_port'] - if not hasattr(self.request.POST.get('alert-spec'), "file") or not hasattr(self.request.POST.get('alert-spec'), "filename"): + alert_spec_reference = self.request.POST.get('alert-spec') + + # check that the specification file was sent + if not hasattr(alert_spec_reference, "file") or not hasattr(alert_spec_reference, "filename"): raise HTTPBadRequest("Request to this API endpoint must include a (YAML) file input referenced as 'alert-spec' representing the TOSCA Alerts Specification.") - input_filename = self.request.POST['alert-spec'].filename - input_file = self.request.POST['alert-spec'].file + # extract alert specification file and filename + alerts_input_filename = alert_spec_reference.filename + alerts_input_file = alert_spec_reference.file - if not input_filename.lower().endswith('.yaml'): + if not alerts_input_filename.lower().endswith('.yaml'): raise HTTPBadRequest("Request to this API endpoint must include a (YAML) file input referenced as 'alert-spec' representing the TOSCA Alerts Specification.") + # parse the alerts specification file try: - yaml_content = load(input_file) - adjust_tosca_definitions_import(yaml_content) + alerts_yaml_content = load(alerts_input_file) + adjust_tosca_definitions_import(alerts_yaml_content) except YAMLError as err: - log.error("Couldn't parse user request file {0} to yaml format due to error: {1}".format(input_filename, err)) - log.error("Invalid content is: {0}".format(input_file.read())) + log.error("Couldn't parse user request file {0} to yaml format due to error: {1}".format(alerts_input_filename, err)) + log.error("Invalid content is: {0}".format(alerts_input_file.read())) raise HTTPBadRequest("Request alert specification file could not be parsed as valid YAML document.") try: - tosca_tpl = ToscaTemplate(yaml_dict_tpl=yaml_content) + tosca_tpl = ToscaTemplate(yaml_dict_tpl=alerts_yaml_content) except Exception as e: log.error(e) raise HTTPBadRequest("Request alert specification file could not be parsed as a valid TOSCA document.") @@ -101,6 +106,33 @@ class AlertsConfigurationAPI(object): alert_handlers_errors = [] # iterate through every policy and extract all triggers of the given policy + self._config_kapacitor_alerts(tosca_tpl, sfc, sfc_instance, db, kapacitor_host, kapacitor_port, alert_tasks_errors, alert_handlers_errors) + + return_msg = {"msg": "Alerts specification has been successfully validated and forwarded to Kapacitor", "service_function_chain_id": sfc, + "service_function_chain_instance_id": sfc_instance} + + if len(alert_tasks_errors) > 0: + return_msg["triggers_specification_errors"] = alert_tasks_errors + + if len(alert_handlers_errors) > 0: + return_msg["triggers_action_errors"] = alert_handlers_errors + + return return_msg + + def _config_kapacitor_alerts(self, tosca_tpl, sfc, sfc_instance, db, kapacitor_host, kapacitor_port, alert_tasks_errors, alert_handlers_errors): + """ + Configures the alerts task and alert handlers within Kapacitor. + + :param tosca_tpl: the parsed Tosca template object + :param sfc: sfc ID + :param sfc_instance: sfc instance ID + :param db: Influx database ID + :param kapacitor_host: default host is localhost (CLMC service running on the same machine as Kapacitor) + :param kapacitor_port: default value to use is 9092 + :param alert_tasks_errors: the list for tracking errors while interacting with Kapacitor tasks + :param alert_handlers_errors: the list for tracking errors while interacting with Kapacitor alert handlers + """ + for policy in tosca_tpl.policies: for trigger in policy.triggers: event_id = trigger.name @@ -168,30 +200,34 @@ class AlertsConfigurationAPI(object): http_handlers = trigger.trigger_tpl["action"]["implementation"] # subscribe all http handlers to the created topic - kapacitor_api_handlers_url = "http://{0}:{1}/kapacitor/v1/alerts/topics/{2}/handlers".format(kapacitor_host, kapacitor_port, topic_id) - for http_handler_url in http_handlers: - http_handler_host = urlparse(http_handler_url).hostname - handler_id = "{0}.{1}.{2}".format(policy.name, event_id, http_handler_host) - kapacitor_http_request_body = fill_http_post_handler_vars(handler_id, http_handler_url) - response = post(kapacitor_api_handlers_url, json=kapacitor_http_request_body) - response_content = response.json() - log.info(response_content, response.status_code) - - if response_content.get("error", "") != "": - alert_handlers_errors.append({ - "policy": policy.name, - "trigger": event_id, - "handler": http_handler_url, - "error": response_content.get("error") - }) + self._config_kapacitor_alert_handlers(kapacitor_host, kapacitor_port, policy.name, topic_id, event_id, http_handlers, alert_handlers_errors) - return_msg = {"msg": "Alerts specification has been successfully validated and forwarded to Kapacitor", "service_function_chain_id": sfc, - "service_function_chain_instance_id": sfc_instance} - - if len(alert_tasks_errors) > 0: - return_msg["triggers_specification_errors"] = alert_tasks_errors - - if len(alert_handlers_errors) > 0: - return_msg["triggers_action_errors"] = alert_handlers_errors + def _config_kapacitor_alert_handlers(self, kapacitor_host, kapacitor_port, policy_id, topic_id, event_id, http_handlers, alert_handlers_errors): + """ + Handles the configuration of HTTP Post alert handlers. + + :param kapacitor_host: default host is localhost (CLMC service running on the same machine as Kapacitor) + :param kapacitor_port: default value to use is 9092 + :param policy_id: policy ID those triggers relate to + :param topic_id: topic ID built of sfc, sfc instance and event_id + :param event_id: name of trigger + :param http_handlers: list of handlers to subscribe + :param alert_handlers_errors: the list for tracking errors while interacting with Kapacitor alert handlers + """ - return return_msg + kapacitor_api_handlers_url = "http://{0}:{1}/kapacitor/v1/alerts/topics/{2}/handlers".format(kapacitor_host, kapacitor_port, topic_id) + for http_handler_url in http_handlers: + http_handler_host = urlparse(http_handler_url).hostname + handler_id = "{0}.{1}.{2}".format(policy_id, event_id, http_handler_host) + kapacitor_http_request_body = fill_http_post_handler_vars(handler_id, http_handler_url) + response = post(kapacitor_api_handlers_url, json=kapacitor_http_request_body) + response_content = response.json() + log.info(response_content, response.status_code) + + if response_content.get("error", "") != "": + alert_handlers_errors.append({ + "policy": policy_id, + "trigger": event_id, + "handler": http_handler_url, + "error": response_content.get("error") + })