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 %}
+