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-INST-EVENTS'
 25
 26# ts Table to read from
 27TABLE = 'APEX_INSTANCES'
 28
 29# Event Class to create
 30EVENT_CLASSNAME = 'APEX-INSTANCES'
 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              'END_TIME',
109              'DOMAIN',
110              'INSTANCE',
111              'LEVEL',
112              'IDENTIFIER',
113              'PROCEDURE',
114              'SOURCE',
115              'PARENT',
116              'ATTRIBUTES')
117
118    # get each row using ts select
119    for row in ts.select(sid=sid,
120                         sensing_start=start_time,
121                         sensing_stop=stop_time,
122                         sensing_time=start_time_eq,
123                         table=table_info,
124                         fields=fields,
125                         ordering=order_clauses,
126                         limit=limit):
127
128        sensing_time = row[0]
129        END_TIME_values = row[1]
130        DOMAIN_values = row[2]
131        INSTANCE_values = row[3]
132        LEVEL_values = row[4]
133        IDENTIFIER_values = row[5]
134        PROCEDURE_values = row[6]
135        SOURCE_values = row[7]
136        PARENT_values = row[8]
137        ATTRIBUTES_values = row[9]
138
139        inst_properties = {
140                'SID': sid,
141                'End_Time':   END_TIME_values,
142                'Domain':      DOMAIN_values,
143                'Instance':   INSTANCE_values,
144                'Level':      LEVEL_values,
145                'Identifier': IDENTIFIER_values,
146                'Procedure':  PROCEDURE_values,
147                'Source':      SOURCE_values,
148                'Parent':      PARENT_values,
149                'Attributes': ATTRIBUTES_values,
150                }
151
152        if multitable_extra_params is not None:
153            req_spid, req_sequence_count = multitable_extra_params
154            if req_spid != packet_reader.spid or req_sequence_count != sequence_count:
155                continue
156
157        # now filter properties in inst_properties before keeping event for viewing
158        if not include_event_check(properties, inst_properties):
159            continue
160
161        # if we are counting, just bump the counter, else yield event and bump counter
162        # Note: must count all events which meet the filtering criterea to keep paging upto date
163        event_count += 1
164
165        if not count:
166            # build event and append to events to return
167            if from_to is None or (page_start <= event_count and event_count <= page_stop):
168                # build event and append to events to return, if within Eventviewer display page
169                events.append(Event(sid=sid,
170                                    event_classname=EVENT_CLASSNAME,
171                                    start_time=sensing_time,
172                                    #multitable_extra_params=(spid, sequence_count),
173                                    instance_properties=inst_properties))
174
175            if single:
176                # just return first event, which matches search requirements
177                break
178
179    return event_count, events