diff --git a/CLI/actioner/cli_client.py b/CLI/actioner/cli_client.py index 403f4e83cc..0a89c597f9 100644 --- a/CLI/actioner/cli_client.py +++ b/CLI/actioner/cli_client.py @@ -69,7 +69,7 @@ def request(self, method, path, data=None, headers={}, query=None, response_type msg = '%Error: Could not connect to Management REST Server' return ApiClient.__new_error_response(msg) - def post(self, path, data={}, response_type=None): + def post(self, path, data=None, response_type=None): return self.request("POST", path, data, response_type=response_type) def get(self, path, depth=None, ignore404=True, response_type=None): diff --git a/CLI/actioner/sonic-cli-event.py b/CLI/actioner/sonic-cli-event.py new file mode 100644 index 0000000000..12b3e34df1 --- /dev/null +++ b/CLI/actioner/sonic-cli-event.py @@ -0,0 +1,191 @@ +#!/usr/bin/python3 +########################################################################### +# +# Copyright 2019 Dell, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +########################################################################### +from rpipe_utils import pipestr +import cli_client as cc +import cli_log as log +from scripts.render_cli import show_cli_output +from natsort import natsorted + +def recent_interval(interval): + if interval == "5min": + return "5_MINUTES" + elif interval == "60min": + return "60_MINUTES" + elif interval == "24hr": + return "24_HOURS" + else: + return "5_MINUTES" + +def severity(sev): + if sev == 'warning': + return "WARNING" + elif sev == "major": + return "MAJOR" + elif sev == "minor": + return "MINOR" + elif sev == "critical": + return "CRITICAL" + else: + return "INFORMATIONAL" + +def process_cmd_params(args): + filter = {} + + if len(args) == 6: + if args[2] == "from" or args[2] == "start": + filter_param = {"begin":args[3],"end":args[5]} + if args[2] == "from": + filter = { "id": filter_param } + else: + filter = {"time": filter_param } + elif len(args) == 4: + if args[2] == "from" or args[2] == "start": + filter_param = {"begin":args[3]} + if args[2] == "from": + filter = { "id": filter_param } + else: + filter = {"time": filter_param } + + elif args[2] == "severity": + filter = {"severity": severity(args[3])} + + elif args[2] == "recent": + filter = {"interval": recent_interval(args[3])} + + return filter + + +def invoke_api(func, args): + + api = cc.ApiClient() + if func == "get_alarm_all" or \ + func == "get_alarm_detail" or \ + func == "get_alarm_acknowledged": + path = "/restconf/data/sonic-alarm:sonic-alarm/ALARM/ALARM_LIST" + return api.get(path) + + elif func == "get_alarm_filter": + filters = process_cmd_params(args) + if len(filters): + path = "/restconf/operations/sonic-alarm:show-alarms" + body = {"sonic-alarm:input":filters} + return api.post(path, body) + else: + path = "/restconf/data/sonic-alarm:sonic-alarm/ALARM/ALARM_LIST" + return api.get(path) + + elif func == "get_alarm_id": + if len(args) == 4: + path = "/restconf/data/sonic-alarm:sonic-alarm/ALARM/ALARM_LIST=" + args[3] + return api.get(path) + + elif func == "get_alarm_stats": + path = "/restconf/data/sonic-alarm:sonic-alarm/ALARM_STATS/ALARM_STATS_LIST" + return api.get(path) + + elif func == "get_event_stats": + path = "/restconf/data/sonic-event:sonic-event/EVENT_STATS/EVENT_STATS_LIST" + return api.get(path) + + elif func == "get_event_details": + path = "/restconf/data/sonic-event:sonic-event/EVENT/EVENT_LIST" + return api.get(path) + + elif func == "get_event_filter": + filters = process_cmd_params(args) + if len(filters): + path = "/restconf/operations/sonic-event:show-events" + body = {"sonic-event:input":filters} + return api.post(path, body) + else: + path = "/restconf/data/sonic-event:sonic-event/EVENT/EVENT_LIST" + return api.get(path) + + elif func == "get_event_id": + if len(args) == 4: + path = "/restconf/data/sonic-event:sonic-event/EVENT/EVENT_LIST=" + args[3] + return api.get(path) + + elif func == "alarm_acknowledge": + if len(args) == 3: + body = {"sonic-alarm:input": {"id":[args[2]]}} + path = "/restconf/operations/sonic-alarm:acknowledge-alarms" + return api.post(path, body) + + elif func == "alarm_unacknowledge": + if len(args) == 3: + body = {"sonic-alarm:input": {"id":[args[2]]}} + path = "/restconf/operations/sonic-alarm:unacknowledge-alarms" + return api.post(path, body) + +def run(func, args): + try: + api_response = invoke_api(func, args) + if api_response is None: + return + if api_response.ok(): + response = api_response.content + if response is None: + print("Success") + else: + if 'sonic-event:output' in response: + if response['sonic-event:output']['EVENT']['EVENT_LIST']: + event_lst = natsorted(response['sonic-event:output']['EVENT']['EVENT_LIST'], key=lambda t: t["id"]) + response['sonic-event:output']['EVENT']['EVENT_LIST'] = event_lst + show_cli_output('show_event_summary.j2', response['sonic-event:output']) + elif 'sonic-event:EVENT_LIST' in response: + evt_lst = natsorted(response['sonic-event:EVENT_LIST'], key=lambda t: t["id"]) + response['sonic-event:EVENT_LIST'] = evt_lst + if func == 'get_event_details' or \ + func == 'get_event_id': + show_cli_output('show_event_details.j2', response) + else: + show_cli_output('show_event_summary.j2', response) + elif 'sonic-alarm:output' in response: + if func == 'alarm_acknowledge' or \ + func == 'alarm_unacknowledge': + if response['sonic-alarm:output']['status']: + print("Error: {}" .format(response['sonic-alarm:output']['status-detail'])) + else: + if response['sonic-alarm:output']['ALARM']['ALARM_LIST']: + alarm_lst = natsorted(response['sonic-alarm:output']['ALARM']['ALARM_LIST'], key=lambda t: t["id"]) + response['sonic-alarm:output']['ALARM']['ALARM_LIST'] = alarm_lst + show_cli_output('show_event_summary.j2', response['sonic-alarm:output']) + elif 'sonic-alarm:ALARM_LIST' in response: + if response['sonic-alarm:ALARM_LIST']: + alarm_lst = natsorted(response['sonic-alarm:ALARM_LIST'], key=lambda t: t["id"]) + response['sonic-alarm:ALARM_LIST'] = alarm_lst + if func == 'get_alarm_detail' or \ + func == "get_alarm_id": + show_cli_output('show_event_details.j2', response) + else: + show_cli_output('show_event_summary.j2', response, key=func) + elif 'sonic-alarm:ALARM_STATS_LIST' in response or \ + 'sonic-event:EVENT_STATS_LIST' in response: + show_cli_output('show_event_summary.j2', response) + + except ApiException as e: + print("%ERROR:Transaction failure.") + +if __name__ == '__main__': + log.log_info("Loading sonic_cli_event.py module") + pipestr().write(sys.argv) + func = sys.argv[1] + + run(func, sys.argv[2:]) diff --git a/CLI/actioner/sonic-cli-evprofile.py b/CLI/actioner/sonic-cli-evprofile.py new file mode 100644 index 0000000000..2da8b22fdd --- /dev/null +++ b/CLI/actioner/sonic-cli-evprofile.py @@ -0,0 +1,52 @@ +#!/usr/bin/python3 +############################################################################ +# +# Copyright 2019 Dell, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +########################################################################### + +import sys +from rpipe_utils import pipestr +from scripts.render_cli import show_cli_output +import cli_client as cc + +def run(func, args): + aa = cc.ApiClient() + + if func == 'get_evprofile': + keypath = cc.Path('/restconf/operations/sonic-evprofile:get-evprofile') + response = aa.post(keypath) + if response.ok(): + api_response = response.content["sonic-get-evprofile:output"] + show_cli_output("show_evprofile_rpc.j2", api_response) + else: + print(response.error_message()) + return 1 + + if func == 'set_evprofile': + keypath = cc.Path('/restconf/operations/sonic-evprofile:set-evprofile') + body = { "sonic-evprofile:input": { "file-name": args[0]} } + response = aa.post(keypath, body) + if not response.ok(): + print(response.error_message()) + return 1 + +if __name__ == '__main__': + + pipestr().write(sys.argv) + func = sys.argv[1] + + run(func, sys.argv[2:]) + diff --git a/CLI/clitree/cli-xml/event.xml b/CLI/clitree/cli-xml/event.xml new file mode 100644 index 0000000000..b7882a8739 --- /dev/null +++ b/CLI/clitree/cli-xml/event.xml @@ -0,0 +1,314 @@ + + + + + + + + + + + + + + + + + sonic-cli-event alarm_acknowledge ${__full_line} + + + + + sonic-cli-event alarm_unacknowledge ${__full_line} + + + + + + + + + + + + + + + + + + sonic-cli-event get_event_filter ${__full_line} + + + + sonic-cli-event get_event_details ${__full_line} + + + + + sonic-cli-event get_event_id ${__full_line} + + + + + sonic-cli-event get_event_filter ${__full_line} + + + + + sonic-cli-event get_event_filter ${__full_line} + + + + sonic-cli-event get_event_stats ${__full_line} + + + + sonic-cli-event get_alarm_all ${__full_line} + + + + sonic-cli-event get_alarm_acknowledged ${__full_line} + + + + + + + + + + + + + + + + + + sonic-cli-event get_alarm_filter ${__full_line} + + + + + sonic-cli-event get_alarm_id ${__full_line} + + + + sonic-cli-event get_alarm_detail ${__full_line} + + + + + sonic-cli-event get_alarm_filter ${__full_line} + + + + + sonic-cli-event get_alarm_filter ${__full_line} + + + + sonic-cli-event get_alarm_stats ${__full_line} + + + + diff --git a/CLI/clitree/cli-xml/evprofile.xml b/CLI/clitree/cli-xml/evprofile.xml new file mode 100644 index 0000000000..28b46656c1 --- /dev/null +++ b/CLI/clitree/cli-xml/evprofile.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + sonic-cli-evprofile get_evprofile + + + + + + + + + + sonic-cli-evprofile set_evprofile ${filename} ${__full_line} + + + + + + + + diff --git a/CLI/renderer/scripts/render_cli.py b/CLI/renderer/scripts/render_cli.py index cf244ade8d..721a94a48f 100755 --- a/CLI/renderer/scripts/render_cli.py +++ b/CLI/renderer/scripts/render_cli.py @@ -151,6 +151,12 @@ def show_cli_output(template_file, response, continuation=False, **kwargs): j2_env.lstrip_blocks = True j2_env.rstrip_blocks = True + def datetimeformat_rfc3339(time): + s = datetime.datetime.fromtimestamp(int(time)//1000000000).strftime('%Y-%m-%dT%H:%M:%S') + return s + "." + (str(int(time)%1000000000))[0:3] + "Z" + + j2_env.globals.update(datetimeformat_rfc3339=datetimeformat_rfc3339) + def datetimeformat(time): return datetime.datetime.fromtimestamp(int(time)).strftime('%Y-%m-%d %H:%M:%S') diff --git a/CLI/renderer/templates/show_event_details.j2 b/CLI/renderer/templates/show_event_details.j2 new file mode 100644 index 0000000000..6579e23e7b --- /dev/null +++ b/CLI/renderer/templates/show_event_details.j2 @@ -0,0 +1,44 @@ +{% set vars = {'header' : true } %} + +{% if 'sonic-event:EVENT_LIST' in json_output.keys() %} +{% for evt in json_output['sonic-event:EVENT_LIST'] %} + {{- '----------------------------------------------\n'}} + {{- 'Event Details -' }} {{ evt['id'] }} {{'\n'}} + {{- '----------------------------------------------\n'}} + {% if 'action' in evt %} + {% set action = evt['action'] %} + {% else %} + {% set action = '-' %} + {% endif %} + {% if 'time-created' in evt %} + {% set time_created = evt['time-created'] %} + {% else %} + {% set time_created = "-" %} + {% endif %} + {{- 'Id:'.ljust(20) }} {{ evt['id'] }} {{ '\n'}} {{- 'Action:'.ljust(20) }} {{ action }} {{ '\n' }} {{- 'Severity:'.ljust(20) }} {{ evt['severity'] }} {{ '\n' }} {{- 'Type:'.ljust(20) }} {{ evt['type-id'] }} {{ '\n' }} {{- 'Timestamp'.ljust(20) }} {{ datetimeformat_rfc3339(time_created) }} {{ '\n'}} {{- 'Description:'.ljust(20) }} {{ evt['text'] }} {{ '\n' }} {{- 'Source:'.ljust(20) }} {{ evt['resource'] }} {{ '\n' }} {{ '\n' }} +{% endfor %} +{% endif %} + +{% if 'sonic-alarm:ALARM_LIST' in json_output.keys() %} +{% for alarm in json_output['sonic-alarm:ALARM_LIST'] %} + {% if 'time-created' in alarm %} + {% set time_created = alarm['time-created'] %} + {% else %} + {% set time_created = '-' %} + {% endif %} + {% if 'acknowledged' in alarm %} + {% set acknowledged = alarm['acknowledged'] %} + {% else %} + {% set acknowledged = '-' %} + {% endif %} + {% if 'acknowledge-time' in alarm %} + {% set acknowledge_time = alarm['acknowledge-time'] %} + {% else %} + {% set acknowledge_time = '-' %} + {% endif %} + {{- '----------------------------------------------\n'}} + {{- 'Alarm Details -' }} {{ alarm['id'] }} {{'\n'}} + {{- '----------------------------------------------\n'}} + {{- 'Id:'.ljust(20) }} {{ alarm['id'] }} {{ '\n'}} {{- 'Severity:'.ljust(20) }} {{ alarm['severity'] }} {{ '\n' }} {{- 'Type:'.ljust(20) }} {{ alarm['type-id'] }} {{ '\n' }} {{- 'Timestamp'.ljust(20) }} {{ datetimeformat_rfc3339(time_created) }} {{ '\n'}} {{- 'Description:'.ljust(20) }} {{ alarm['text'] }} {{ '\n' }} {{- 'Source:'.ljust(20) }} {{ alarm['resource'] }} {{ '\n' }} {{- 'Acknowledged:'.ljust(20) }} {{ acknowledged }} {{ '\n' }} {{- 'Acknowledged time:'.ljust(20) }} {% if acknowledge_time == '-' %} {{ '-' }} {% else %} {{ datetimeformat_rfc3339(acknowledge_time) }} {% endif %} {{ '\n' }}{{ '\n' }} +{% endfor %} +{% endif %} diff --git a/CLI/renderer/templates/show_event_summary.j2 b/CLI/renderer/templates/show_event_summary.j2 new file mode 100644 index 0000000000..0dbcdceb4c --- /dev/null +++ b/CLI/renderer/templates/show_event_summary.j2 @@ -0,0 +1,121 @@ +{% set vars = {'header' : true } %} +{% set func = '' %} +{% if key is defined %} + {% set func = key %} +{% endif %} + +{% macro summary_ev(evt_list) %} + {% for evt in evt_list %} + + {% if vars.header %} + {{- '----------------------------------------------------------------------------------------------------------------------------\n'}} + {{- 'Id'.ljust(12)}} {{'Action'.ljust(15)}} {{'Severity'.ljust(15)}} {{'Name'.ljust(30)}} {{ 'Timestamp'.ljust(27) }} {{ 'Description'.ljust(60)}} {{'\n'}} + {{- '----------------------------------------------------------------------------------------------------------------------------' }} + {% if vars.update({'header': False}) %}{% endif %} + {% endif %} + {% if 'action' in evt %} + {% set action = evt['action'] %} + {% else %} + {% set action = '-' %} + {% endif %} + {% if 'time-created' in evt %} + {% set time_created = evt['time-created'] %} + {% else %} + {% set time_created = '-' %} + {% endif %} + {% if time_created == '-' %} + {{- evt['id'].ljust(12) }} {{ action.ljust(15) }} {{ evt['severity'].ljust(15) }} {{ evt['type-id'].ljust(30) }} {{ '-'.ljust(35) }} {{ evt['text'].ljust(50) -}} + {% else %} + {{- evt['id'].ljust(12) }} {{ action.ljust(15) }} {{ evt['severity'].ljust(15) }} {{ evt['type-id'].ljust(30) }} {{ datetimeformat_rfc3339(time_created).ljust(27) }} {{ evt['text'].ljust(50) -}} + {% endif %} + {% endfor %} +{% endmacro %} + +{% macro summary_alarm(alarm_list) %} + {% for alarm in alarm_list %} + {% if vars.header %} + {{- '----------------------------------------------------------------------------------------------------------------------------\n' }} + {{- 'Id'.ljust(12)}} {{'Severity'.ljust(10)}} {{'Name'.ljust(30)}} {{ 'Timestamp'.ljust(27) }} {{ 'Description'.ljust(60)}} {{'\n'}} + {{- '----------------------------------------------------------------------------------------------------------------------------' }} + {% if vars.update({'header': False}) %}{% endif %} + {% endif %} + {% if 'time-created' in alarm %} + {% set time_created = alarm['time-created'] %} + {% else %} + {% set time_created = '-' %} + {% endif %} + {%- if 'acknowledged' in alarm %} + {% if alarm['acknowledged'] == True %} + {% if func is defined and func == "get_alarm_filter" %} + {% continue %} + {% endif %} + {% else %} + {% if func is defined and func == "get_alarm_acknowledged" %} + {% continue %} + {% endif %} + {% endif %} + {% endif -%} + {% if time_created == '-' %} + {{- alarm['id'].ljust(12) }} {{ alarm['severity'].ljust(10) }} {{ alarm['type-id'].ljust(30) }} {{ '-'.ljust(35) }} {{ alarm['text'].ljust(50) }} + {% else %} + {{- alarm['id'].ljust(12) }} {{ alarm['severity'].ljust(10) }} {{ alarm['type-id'].ljust(30) }} {{ datetimeformat_rfc3339(time_created).ljust(27) }} {{ alarm['text'].ljust(50) }} + {% endif %} + {% endfor %} +{% endmacro %} + +{% if 'sonic-event:EVENT_LIST' in json_output.keys() %} + {{ summary_ev(json_output['sonic-event:EVENT_LIST']) }} +{% endif %} + +{% if 'EVENT' in json_output.keys() %} + {% if 'EVENT_LIST' in json_output['EVENT'] %} + {% if json_output['EVENT']['EVENT_LIST'] is not none %} + {{ summary_ev(json_output['EVENT']['EVENT_LIST']) }} + {% endif %} + {% endif %} +{% endif %} + + +{% if 'sonic-alarm:ALARM_LIST' in json_output.keys() %} + {{- summary_alarm(json_output['sonic-alarm:ALARM_LIST']) }} +{% endif %} + + +{% if 'ALARM' in json_output.keys() %} + {% if 'ALARM_LIST' in json_output['ALARM'] %} + {% if json_output['ALARM']['ALARM_LIST'] is not none %} + {{- summary_alarm(json_output['ALARM']['ALARM_LIST']) }} + {% endif %} + {% endif %} +{% endif %} + + +{% if 'sonic-alarm:ALARM_STATS_LIST' in json_output.keys() %} + {% for alarm in json_output['sonic-alarm:ALARM_STATS_LIST'] %} + {% if alarm['id'] == 'state' %} + {{- 'Alarm summary\n' }} + {{- '---------------------------------\n' }} + {{- 'Total:'.ljust(30) }} {{ alarm['alarms'] }} {{ '\n' }} + {{- 'Critical:'.ljust(30) }} {{ alarm['critical'] }} {{ '\n' }} + {{- 'Major:'.ljust(30) }} {{ alarm['major'] }} {{ '\n' }} + {{- 'Minor:'.ljust(30) }} {{ alarm['minor'] }} {{ '\n' }} + {{- 'Warning:'.ljust(30) }} {{ alarm['warning'] }} {{ '\n' }} + {{- 'Acnowledged:'.ljust(30) }} {{ alarm['acknowledged'] }} {{ '\n' }} + {{- '----------------------------------\n' }} + {% endif %} + {% endfor %} +{% endif %} + +{% if 'sonic-event:EVENT_STATS_LIST' in json_output.keys() %} + {% for evt in json_output['sonic-event:EVENT_STATS_LIST'] %} + {% if evt['id'] == 'state' %} + {{- 'Event summary\n' }} + {{- '---------------------------------\n' }} + {{- 'Total:'.ljust(30) }} {{ evt['events'] }} {{ '\n' }} + {{- 'Raised:'.ljust(30) }} {{ evt['raised'] }} {{ '\n' }} + {{- 'Acknowledged:'.ljust(30) }} {{ evt['acked'] }} {{ '\n' }} + {{- 'Cleared:'.ljust(30) }} {{ evt['cleared'] }} {{ '\n' }} + {{- '----------------------------------\n' }} + {% endif %} + {% endfor %} +{% endif %} diff --git a/CLI/renderer/templates/show_evprofile_rpc.j2 b/CLI/renderer/templates/show_evprofile_rpc.j2 new file mode 100644 index 0000000000..fc01dd9ff3 --- /dev/null +++ b/CLI/renderer/templates/show_evprofile_rpc.j2 @@ -0,0 +1,23 @@ +{% set vars = {'filename':'-'} %} +{% set vars = {'filelist':[]} %} +{% if json_output %} +{% if vars.update({'filename': json_output["file-name"]}) %} {% endif %} + +{% if json_output["file-list"] %} +{% if vars.update({'filelist': json_output["file-list"]}) %} {% endif %} +{% endif %} + +Active Event Profile +-------------------------- +{{vars.filename}} + + +-------------------------- +Available Event Profiles +-------------------------- +{% for file in vars.filelist %} +{{file}} +{% endfor %} + +{% endif %} +