Пример #1
0
def test_get_first_key():
    """Utils - Get First Key"""
    data = {
        'path': 'ABC',
        'details': {
            'parent': {
                'path': 'DEF',
            }
        },
        'empty_dict': {},
        'empty_list': [],
        'events': [
            {
                'path': 'GHI'
            }
        ]
    }
    # 'path' is a top-level key and so should always be returned first
    assert_equal('ABC', utils.get_first_key(data, 'path'))

    # dicts and lists can be returned as well
    assert_equal(data['details'], utils.get_first_key(data, 'details'))

    # None is returned by default if no value is found
    assert_equal(None, utils.get_first_key(data, 'no-key-found'))

    # Custom default value is returned if specified
    assert_equal({}, utils.get_first_key(data, 'no-key-found', {}))
Пример #2
0
    def can_merge(self, other):
        """Check if two alerts can be merged together.

        Args:
            other (Alert): Check if the instance can merge with this other alert.

        Returns:
            True if these alerts fit in the same merge window and have the same merge key values.
        """
        if not self.merge_enabled or not other.merge_enabled:
            # Merge information is not defined for both of these alerts.
            return False

        older, newer = min(self, other), max(self, other)
        if newer.created > older.created + older.merge_window:
            # These alerts won't fit in a single merge window.
            return False

        if set(self.merge_by_keys) != set(other.merge_by_keys):
            # These alerts have different definitions of merge keys.
            return False

        return all(
            utils.get_first_key(self.record, key) == utils.get_first_key(
                other.record, key) for key in self.merge_by_keys)
Пример #3
0
    def merge(cls, alerts):
        """Combine a list of alerts into a new merged alert.

        The caller is responsible for determining *which* alerts should be merged, this just
        implements the merge algorithm.

        Args:
            alerts (list): List of alerts to merge.
                These should all have the same values for their merge keys.

        Returns:
            Alert: A new alert whose record is formed by merging the records of all the alerts.
                The merged alert outputs are a union of all outputs in the original alerts.
                Other information (rule name, description, etc) is copied from the first alert.
        """
        alerts = sorted(alerts)  # Put alerts in chronological order.
        merge_keys = set(alerts[0].merge_by_keys)
        # Remove merge keys from the alert record, so that it doesn't show up in common/diff
        records = [
            cls._clean_record(alert.record, merge_keys) for alert in alerts
        ]
        common = cls._compute_common(records)

        # Keys are named such that more important information is at the beginning alphabetically.
        new_record = {
            'AlertCount':
            len(alerts),
            'AlertTimeFirst':
            min(alert.created
                for alert in alerts).strftime(cls.DATETIME_FORMAT),
            'AlertTimeLast':
            max(alert.created
                for alert in alerts).strftime(cls.DATETIME_FORMAT),
            'MergedBy': {
                key: utils.get_first_key(alerts[0].record, key, '(n/a)')
                for key in merge_keys
            },
            'OtherCommonKeys':
            common,
            'ValueDiffs': {
                alert.created.strftime(cls.DATETIME_FORMAT):
                cls._compute_diff(common, record)
                for alert, record in zip(alerts, records)
            }
        }

        # TODO: the cluster, log_source, source_entity, etc, could be different between alerts
        return cls(
            alerts[0].rule_name,
            new_record,
            alerts[-1].outputs,  # Use the most recent set of outputs
            cluster=alerts[0].cluster,
            context=alerts[0].context,
            log_source=alerts[0].log_source,
            log_type=alerts[0].log_type,
            publishers=alerts[0].publishers,
            rule_description=alerts[0].rule_description,
            source_entity=alerts[0].source_entity,
            source_service=alerts[0].source_service,
            staged=any(alert.staged for alert in alerts))