1#!/usr/bin/env python3
2
3# PKR 08-01-2024 created
4
5"""Convert APEX Log table entries to APEX LOG events."""
6
7import logging
8
9from chart.db import ts
10from chart.events.event import Event
11from chart.events.event import include_event_check
12from chart.products.events.multitable_utils import process_ordering_sensing_time
13from chart.products.pus.packetdef import PacketDef
14from chart.products.pus.packetdef import PacketDomain
15from chart import settings
16from chart.db.model.table import TableInfo
17from chart.common.traits import is_listlike
18from chart.web.user import User
19
20
21logger = logging.getLogger()
22
23# This module is used for event classes where <db-table> matches DECODER_ID
24DECODER_ID = 'APEX-LOG-EVENTS'
25
26# ts Table to read from
27TABLE = 'APEX_LOG'
28
29# Event Class to create
30EVENT_CLASSNAME = 'APEX-LOG'
31
32def find_events(sid,
33 start_time,
34 start_time_eq,
35 stop_time,
36 event_classes,
37 ordering,
38 from_to,
39 properties,
40 count,
41 single,
42 filtering,
43 multitable_extra_params,
44 user: User = None):
45 """Build Event records from records extracted from ts table in given time range.
46
47 Args:
48 `sid` (SID): Source-ID to search for
49 `start_time` (datetime): Start of time range to search
50 `stop_time` (datetime): End of time range to search
51 `event_classes` (list of EventClass): Class definitions to search for - this is relevant for
52 implementations of find_event() which are reading from tables which could return
53 multiple different types of event.
54 `ordering` (str or list of str): Field(s) to sort by
55 `from_to` (tuple): (page_start, page_stop)
56 `properties`: (list): list of property triplets (name, value, op)
57 `count`: (bool): just return the number of events found
58 `single`: (bool): just return the first event which matches input requirements
59 `filtering` (EventTimeFilter): Specify if the start_time/stop_time range is applied
60 to the sensing_time or the execution_time column
61 `multitable_extra_params` (tuple): Special method of storing additional table-specific information
62 in an Event object, which is passed via json to the event viewer table and back,
63 and used again in this function to ecreate the original Event
64
65 Returns:
66 event_count: (int) number of events which match input requirements
67 events: list of events which match input requirements
68 """
69
70 if from_to is not None:
71 page_start, page_stop = from_to
72
73 # if returning a single record, set limit = 1
74 # Note: there can be so many TM packets so when a single set, for the 'Snap' case
75 # set limit option to prevent database hanging. Do not set limit when single
76 # set due selecting event pop-up as handled by ts.select
77 limit = None
78 if single and start_time_eq is None:
79 limit = 1
80
81 # Instead of returning an iterator, we build the whole list and return it
82 events = []
83 # Total count of events. This may be larger than len(events) since we may be pagingating
84 # using the `from_to` parameter and as an optimisation, we don't bother to instantiate all
85 # the required Events objects, just enough for the event viewer to work. However we still
86 # want to tell the user how many events are available
87 event_count = 0
88
89 # Reset ordering for sensing_time
90 order_clauses = process_ordering_sensing_time(ordering)
91
92 #table_info = TableInfo(TABLE, sid=sid, fast_load=True)
93 table_info = TableInfo(TABLE)
94
95 if not single and count:
96 # just return count of events
97 event_count = ts.count(
98 table=table_info.storage_table,
99 field='SENSING_TIME',
100 method='ap',
101 sid=sid,
102 sensing_start=start_time,
103 sensing_stop=stop_time)
104
105 return event_count, events
106
107 fields = ('SENSING_TIME',
108 'INSTANCE',
109 'DOMAIN',
110 'LEVEL',
111 'IDENTIFIER',
112 'PROCEDURE',
113 'SOURCE',
114 'PARENT',
115 'ATTRIBUTE',
116 'VALUE',
117 'COUNTER')
118
119 # get each row using ts select
120 for row in ts.select(sid=sid,
121 sensing_start=start_time,
122 sensing_stop=stop_time,
123 sensing_time=start_time_eq,
124 table=table_info,
125 fields=fields,
126 ordering=order_clauses,
127 limit=limit):
128
129 sensing_time = row[0]
130 INSTANCE_values = row[1]
131 DOMAIN_values = row[2]
132 LEVEL_values = row[3]
133 IDENTIFIER_values = row[4]
134 PROCEDURE_values = row[5]
135 SOURCE_values = row[6]
136 PARENT_values = row[7]
137 ATTRIBUTE_values = row[8]
138 VALUE_values = row[9]
139 COUNTER_values = row[10]
140
141 inst_properties = {
142 'Instance': INSTANCE_values,
143 'Domain': DOMAIN_values,
144 'Level': LEVEL_values,
145 'Identifier': IDENTIFIER_values,
146 'Procedure': PROCEDURE_values,
147 'Source': SOURCE_values,
148 'Parent': PARENT_values,
149 'Attribute': ATTRIBUTE_values,
150 'Value': VALUE_values,
151 'Counter': COUNTER_values
152 }
153
154 if multitable_extra_params is not None:
155 req_spid, req_sequence_count = multitable_extra_params
156 if req_spid != packet_reader.spid or req_sequence_count != sequence_count:
157 continue
158
159 # now filter properties in inst_properties before keeping event for viewing
160 if not include_event_check(properties, inst_properties):
161 continue
162
163 # if we are counting, just bump the counter, else yield event and bump counter
164 # Note: must count all events which meet the filtering criterea to keep paging upto date
165 event_count += 1
166
167 if not count:
168 # build event and append to events to return
169 if from_to is None or (page_start <= event_count and event_count <= page_stop):
170 # build event and append to events to return, if within Eventviewer display page
171 events.append(Event(sid=sid,
172 event_classname=EVENT_CLASSNAME,
173 start_time=sensing_time,
174 #multitable_extra_params=(spid, sequence_count),
175 instance_properties=inst_properties))
176
177 if single:
178 # just return first event, which matches search requirements
179 break
180
181 return event_count, events