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