def test_yaml_def_requires_systemd_pass(self): mydef = YDefsSection('mydef', yaml.safe_load(YAML_DEF_REQUIRES_SYSTEMD_PASS_1)) for entry in mydef.leaf_sections: self.assertTrue(entry.requires.passes) mydef = YDefsSection('mydef', yaml.safe_load(YAML_DEF_REQUIRES_SYSTEMD_PASS_2)) for entry in mydef.leaf_sections: self.assertTrue(entry.requires.passes)
def test_yaml_def_requires_systemd_fail(self): mydef = YDefsSection('mydef', yaml.safe_load(YAML_DEF_REQUIRES_SYSTEMD_FAIL_1)) for entry in mydef.leaf_sections: self.assertFalse(entry.requires.passes) mydef = YDefsSection('mydef', yaml.safe_load(YAML_DEF_REQUIRES_SYSTEMD_FAIL_2)) for entry in mydef.leaf_sections: self.assertFalse(entry.requires.passes) mydef = YDefsSection('mydef', yaml.safe_load(YAML_DEF_REQUIRES_SYSTEMD_FAIL_3)) for entry in mydef.leaf_sections: self.assertFalse(entry.requires.passes)
def load(self): plugin_content = YDefsLoader('scenarios').load_plugin_defs() if not plugin_content: return yscenarios = YDefsSection(HotSOSConfig.PLUGIN_NAME, plugin_content) if yscenarios.requires and not yscenarios.requires.passes: log.debug( "plugin '%s' scenarios pre-requisites not met - " "skipping", HotSOSConfig.PLUGIN_NAME) return log.debug("sections=%s, scenarios=%s", len(yscenarios.branch_sections), len(yscenarios.leaf_sections)) to_skip = set() for scenario in yscenarios.leaf_sections: # Only register scenarios if requirements are satisfied. group_name = scenario.parent.name if (group_name in to_skip or (scenario.requires and not scenario.requires.passes)): log.debug("%s requirements not met - skipping scenario %s", group_name, scenario.name) to_skip.add(group_name) continue self._scenarios.append( Scenario(scenario.name, scenario.checks, scenario.conclusions))
def test_yaml_def_entry_input_override(self): plugin_checks = yaml.safe_load(YAML_DEF_W_INPUT_SUPERSEDED2) for name, group in plugin_checks.get('pluginX').items(): group = YDefsSection(name, group) for entry in group.leaf_sections: self.assertEqual( entry.input.path, os.path.join(HotSOSConfig.DATA_ROOT, 'foo/bar3*'))
def test_yaml_def_requires_grouped(self): mydef = YDefsSection('mydef', yaml.safe_load(YAML_DEF_REQUIRES_GROUPED)) tested = 0 for entry in mydef.leaf_sections: if entry.name == 'passdef1': tested += 1 self.assertTrue(entry.requires.passes) elif entry.name == 'passdef2': tested += 1 self.assertTrue(entry.requires.passes) elif entry.name == 'faildef1': tested += 1 self.assertFalse(entry.requires.passes) elif entry.name == 'faildef2': tested += 1 self.assertFalse(entry.requires.passes) self.assertEqual(tested, 4)
def test_yaml_def_requires_apt(self, mock_apt): tested = 0 expected = { '2.0': False, '3.0': True, '3.1': True, '4.0': True, '5.0': True, '5.2': True, '5.3': False, '6.0': False } mock_apt.return_value = mock.MagicMock() mock_apt.return_value.is_installed.return_value = True for ver, result in expected.items(): mock_apt.return_value.get_version.return_value = ver mydef = YDefsSection('mydef', yaml.safe_load(YAML_DEF_REQUIRES_APT)) for entry in mydef.leaf_sections: tested += 1 self.assertEqual(entry.requires.passes, result) self.assertEqual(tested, len(expected))
def _load_event_definitions(self): """ Load event search definitions from yaml. An event is identified using between one and two expressions. If it requires a start and end to be considered complete then these can be specified for match otherwise we can match on a single line. Note that multi-line events can be overlapping hence why we don't use a SequenceSearchDef (we use core.analytics.LogEventStats). """ plugin = YDefsLoader('events').load_plugin_defs() if not plugin: return group_name = self._yaml_defs_group log.debug("loading defs for subgroup=%s", group_name) group_defs = plugin.get(group_name) group = YDefsSection(group_name, group_defs) log.debug("sections=%s, events=%s", len(group.branch_sections), len(group.leaf_sections)) for event in group.leaf_sections: results_passthrough = bool(event.passthrough_results) log.debug("event: %s", event.name) log.debug("input: %s (command=%s)", event.input.path, event.input.command is not None) log.debug("passthrough: %s", results_passthrough) section_name = event.parent.name # this is hopefully unique enough to allow two events from # different sections to have the same name and not clobber each # others results. search_tag = "{}.{}".format(section_name, event.name) # if this is a multiline event (has a start and end), append # this to the tag so that it can be used with # core.analytics.LogEventStats. search_meta = {'searchdefs': [], 'datasource': None, 'passthrough_results': results_passthrough} if event.expr: hint = None if event.hint: hint = event.hint.value search_meta['searchdefs'].append( SearchDef(event.expr.value, tag=search_tag, hint=hint)) elif event.start: if (event.body or (event.end and not results_passthrough)): log.debug("event '%s' search is a sequence", event.name) sd_start = SearchDef(event.start.expr) sd_end = None # explicit end is optional for sequence definition if event.end: sd_end = SearchDef(event.end.expr) sd_body = None if event.body: sd_body = SearchDef(event.body.expr) # NOTE: we don't use hints here sequence_def = SequenceSearchDef(start=sd_start, body=sd_body, end=sd_end, tag=search_tag) search_meta['searchdefs'].append(sequence_def) search_meta['is_sequence'] = True elif (results_passthrough and (event.start and event.end)): # start and end required for core.analytics.LogEventStats search_meta['searchdefs'].append( SearchDef(event.start.expr, tag="{}-start".format(search_tag), hint=event.start.hint)) search_meta['searchdefs'].append( SearchDef(event.end.expr, tag="{}-end".format(search_tag), hint=event.end.hint)) else: log.debug("unexpected search definition passthrough=%s " "body provided=%s, end provided=%s", results_passthrough, event.body is not None, event.end is not None) else: log.debug("invalid search definition for event '%s' in " "section '%s'", event, event.parent.name) continue datasource = event.input.path if section_name not in self.__event_defs: self.__event_defs[section_name] = {} search_meta['datasource'] = datasource self.__event_defs[section_name][event.name] = search_meta
def test_requires_grouped(self, mock_plugin): mock_plugin.return_value = mock.MagicMock() r1 = { 'property': 'hotsos.core.plugins.openstack.OpenstackChecksBase.r1' } r2 = { 'property': 'hotsos.core.plugins.openstack.OpenstackChecksBase.r2' } r3 = { 'property': 'hotsos.core.plugins.openstack.OpenstackChecksBase.r3' } requires = {'requires': [{'or': [r1, r2]}]} mock_plugin.return_value.r1 = False mock_plugin.return_value.r2 = False group = YDefsSection('test', requires) self.assertFalse(group.leaf_sections[0].requires.passes) mock_plugin.return_value.r1 = True mock_plugin.return_value.r2 = False group = YDefsSection('test', requires) self.assertTrue(group.leaf_sections[0].requires.passes) mock_plugin.return_value.r1 = True mock_plugin.return_value.r2 = True group = YDefsSection('test', requires) self.assertTrue(group.leaf_sections[0].requires.passes) requires = {'requires': [{'and': [r1, r2]}]} mock_plugin.return_value.r1 = False mock_plugin.return_value.r2 = False group = YDefsSection('test', requires) self.assertFalse(group.leaf_sections[0].requires.passes) mock_plugin.return_value.r1 = True mock_plugin.return_value.r2 = False group = YDefsSection('test', requires) self.assertFalse(group.leaf_sections[0].requires.passes) mock_plugin.return_value.r1 = True mock_plugin.return_value.r2 = True group = YDefsSection('test', requires) self.assertTrue(group.leaf_sections[0].requires.passes) requires = {'requires': [{'and': [r1, r2], 'or': [r1, r2]}]} mock_plugin.return_value.r1 = True mock_plugin.return_value.r2 = False group = YDefsSection('test', requires) self.assertFalse(group.leaf_sections[0].requires.passes) mock_plugin.return_value.r1 = True mock_plugin.return_value.r2 = True group = YDefsSection('test', requires) self.assertTrue(group.leaf_sections[0].requires.passes) requires = {'requires': [{'and': [r1, r2], 'or': [r1, r2]}]} mock_plugin.return_value.r1 = True mock_plugin.return_value.r2 = False group = YDefsSection('test', requires) self.assertFalse(group.leaf_sections[0].requires.passes) requires = {'requires': [r1, {'and': [r3], 'or': [r1, r2]}]} mock_plugin.return_value.r1 = True mock_plugin.return_value.r2 = False mock_plugin.return_value.r3 = True group = YDefsSection('test', requires) self.assertTrue(group.leaf_sections[0].requires.passes) requires = {'requires': [{'and': [r3], 'or': [r1, r2]}]} mock_plugin.return_value.r1 = True mock_plugin.return_value.r2 = False mock_plugin.return_value.r3 = True group = YDefsSection('test', requires) self.assertTrue(group.leaf_sections[0].requires.passes) # same as prev test but with dict instead list requires = {'requires': {'and': [r3], 'or': [r1, r2]}} mock_plugin.return_value.r1 = True mock_plugin.return_value.r2 = False mock_plugin.return_value.r3 = True group = YDefsSection('test', requires) self.assertTrue(group.leaf_sections[0].requires.passes)
def test_yaml_def_entry_seq(self): with tempfile.TemporaryDirectory() as dtmp: setup_config(DATA_ROOT=dtmp) data_file = os.path.join(dtmp, 'data.txt') _yaml = YAML_DEF_EXPR_TYPES.format( path=os.path.basename(data_file)) open(os.path.join(dtmp, 'events.yaml'), 'w').write(_yaml) open(data_file, 'w').write('hello\nbrave\nworld\n') plugin_checks = yaml.safe_load(_yaml).get('myplugin') for name, group in plugin_checks.items(): group = YDefsSection(name, group) for entry in group.leaf_sections: self.assertEqual(entry.input.path, '{}*'.format(data_file)) test_self = self match_count = {'count': 0} callbacks_called = {} setup_config(PLUGIN_YAML_DEFS=dtmp, PLUGIN_NAME='myplugin') EVENTCALLBACKS = CallbackHelper() class MyEventHandler(events.YEventCheckerBase): def __init__(self): super().__init__(yaml_defs_group='mygroup', searchobj=FileSearcher(), callback_helper=EVENTCALLBACKS) @EVENTCALLBACKS.callback() def my_sequence_search(self, event): callbacks_called[event.name] = True for section in event.results: for result in section: if result.tag.endswith('-start'): match_count['count'] += 1 test_self.assertEqual(result.get(0), 'hello') elif result.tag.endswith('-body'): match_count['count'] += 1 test_self.assertEqual(result.get(0), 'brave') elif result.tag.endswith('-end'): match_count['count'] += 1 test_self.assertEqual(result.get(0), 'world') @EVENTCALLBACKS.callback() def my_standard_search(self, event): # expected to be passthough results (i.e. raw) callbacks_called[event.name] = True tag = '{}.my-standard-search-start'.format(event.section) start_results = event.results.find_by_tag(tag) test_self.assertEqual(start_results[0].get(0), 'hello') @EVENTCALLBACKS.callback('my-standard-search2', 'my-standard-search3') def my_standard_search_common(self, event): callbacks_called[event.name] = True test_self.assertEqual(event.results[0].get(0), 'hello') def __call__(self): self.run_checks() MyEventHandler()() self.assertEqual(match_count['count'], 3) self.assertEqual(list(callbacks_called.keys()), [ 'my-sequence-search', 'my-standard-search', 'my-standard-search2' ])