Merge "implements: blueprint synchronizer-nagios-get-all"

This commit is contained in:
Jenkins 2016-02-22 11:38:26 +00:00 committed by Gerrit Code Review
commit db68a02b03
8 changed files with 264 additions and 66 deletions

View File

@ -27,29 +27,28 @@ class NagiosParser(object):
NAME_XPATH = 'table/tr/td[position()=1]/table/tr/td/a' NAME_XPATH = 'table/tr/td[position()=1]/table/tr/td/a'
def __init__(self): def __init__(self):
self.last_host_name = '' self.last_host_name = None
return
def parse(self, html): def parse(self, html):
services = []
parser = etree.HTMLParser()
try: try:
tree = etree.parse(StringIO(html), parser) tree = etree.parse(StringIO(html), etree.HTMLParser())
status_tables = tree.xpath(self.STATUS_TABLE_XPATH) status_tables = tree.xpath(self.STATUS_TABLE_XPATH)
for status_table in status_tables: return self._parse_services(status_tables)
service_rows = status_table.xpath(self.SERVICE_ROWS_XPATH)
for service_row in service_rows:
service = self._parse_service_row(service_row)
if service:
services.append(service)
except Exception as e: except Exception as e:
LOG.exception('Failed to get nagios services %s', e) LOG.exception('Failed to get nagios services %s', e)
return services return None
def _parse_services(self, status_tables):
services = []
for status_table in status_tables:
service_rows = status_table.xpath(self.SERVICE_ROWS_XPATH)
for service_row in service_rows:
service = self._parse_service_row(service_row)
if service:
services.append(service)
return services return services
def _parse_service_row(self, service_row): def _parse_service_row(self, service_row):
@ -59,41 +58,34 @@ class NagiosParser(object):
# there are also two blank lines between different hosts, # there are also two blank lines between different hosts,
# so len(columns)==1 is also valid # so len(columns)==1 is also valid
# TODO(ifat_afek): get column names by the header line # TODO(ifat_afek): get column names by the header line
if (len(columns) == NagiosProperties.NUM_COLUMNS): if len(columns) == NagiosProperties.NUM_COLUMNS:
return self._parse_service_columns(columns) return self._parse_service_columns(columns)
elif (len(columns) > NagiosProperties.NUM_COLUMNS): elif len(columns) > NagiosProperties.NUM_COLUMNS:
LOG.warn('Too many columns in nagios service row. ' LOG.warn('Too many columns in nagios service row. '
'Found %d', len(columns)) 'Found %d', len(columns))
elif (len(columns) > 1): elif len(columns) > 1:
LOG.warn('Missing columns in nagios service row. ' LOG.warn('Missing columns in nagios service row. '
'Found only %d', len(columns)) 'Found only %d', len(columns))
return None return None
def _parse_service_columns(self, columns): def _parse_service_columns(self, columns):
host_name = self._parse_host_name(columns[0], self.NAME_XPATH) return {
service_name = self._get_column_data(columns[1], self.NAME_XPATH) NagiosProperties.RESOURCE_NAME:
status = columns[2].text self._parse_host_name(columns[0], self.NAME_XPATH),
last_check = columns[3].text NagiosProperties.SERVICE:
duration = columns[4].text self._get_column_data(columns[1], self.NAME_XPATH),
attempt = columns[5].text NagiosProperties.STATUS: columns[2].text,
status_information = columns[6].text NagiosProperties.LAST_CHECK: columns[3].text,
NagiosProperties.DURATION: columns[4].text,
service = { NagiosProperties.ATTEMPT: columns[5].text,
NagiosProperties.RESOURCE_NAME: host_name, NagiosProperties.STATUS_INFO: columns[6].text
NagiosProperties.SERVICE: service_name,
NagiosProperties.STATUS: status,
NagiosProperties.LAST_CHECK: last_check,
NagiosProperties.DURATION: duration,
NagiosProperties.ATTEMPT: attempt,
NagiosProperties.STATUS_INFO: status_information
} }
return service @staticmethod
def _get_column_data(column, xpath):
def _get_column_data(self, column, xpath):
contents = column.xpath(xpath) contents = column.xpath(xpath)
if len(contents) == 1: if len(contents) == 1:
@ -105,7 +97,7 @@ class NagiosParser(object):
else: else:
# len(contents) might be 0 for a host, since each host name appears # len(contents) might be 0 for a host, since each host name appears
# only once in the table # only once in the table
return '' return None
def _parse_host_name(self, column, xpath): def _parse_host_name(self, column, xpath):
"""Parse the host name and return it """Parse the host name and return it

View File

@ -47,6 +47,11 @@ class NagiosSynchronizer(SynchronizerBase):
return [] return []
def _get_services(self): def _get_services(self):
nagios_services = self._get_services_from_nagios()
self._enrich_services(nagios_services)
return self._cache_and_filter_services(nagios_services)
def _get_services_from_nagios(self):
nagios_user = self.conf.synchronizer_plugins.nagios_user nagios_user = self.conf.synchronizer_plugins.nagios_user
nagios_password = self.conf.synchronizer_plugins.nagios_password nagios_password = self.conf.synchronizer_plugins.nagios_password
nagios_url = self.conf.synchronizer_plugins.nagios_url nagios_url = self.conf.synchronizer_plugins.nagios_url
@ -71,8 +76,7 @@ class NagiosSynchronizer(SynchronizerBase):
if response.status_code == requests.codes.ok: if response.status_code == requests.codes.ok:
nagios_services = NagiosParser().parse(response.text) nagios_services = NagiosParser().parse(response.text)
self._enrich_services(nagios_services) return nagios_services
return self._cache_and_filter_services(nagios_services)
else: else:
LOG.error(_LE('Failed to get nagios data. Response code: %s') % LOG.error(_LE('Failed to get nagios data. Response code: %s') %
response.status_code) response.status_code)

View File

@ -8,6 +8,5 @@
"duration": "61d 21h 52m 38s", "duration": "61d 21h 52m 38s",
"attempt": "1/1", "attempt": "1/1",
"status_info": "OK - user: 0\\.6%, system: 0\\.3%, wait: 0\\.4%", "status_info": "OK - user: 0\\.6%, system: 0\\.3%, wait: 0\\.4%",
"event_type": "CREATE|UPDATE",
"sync_mode": "snapshot|init_snapshot|update" "sync_mode": "snapshot|init_snapshot|update"
} }

View File

@ -1,4 +1,3 @@
{ {
"status": "OK", "status": "OK"
"event_type": "DELETE"
} }

View File

@ -0,0 +1,37 @@
# Copyright 2016 - Nokia
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from vitrage.synchronizer.plugins.nagios.properties import NagiosProperties \
as NagiosProps
from vitrage.tests import base
class NagiosBaseTest(base.BaseTest):
def _assert_contains(self, expected_service, services):
for service in services:
if service[NagiosProps.RESOURCE_NAME] == \
expected_service[NagiosProps.RESOURCE_NAME] and \
service[NagiosProps.SERVICE] == \
expected_service[NagiosProps.SERVICE]:
self._assert_expected_service(expected_service, service)
return
self.fail("service not found: %(resource_name)s %(service_name)s" %
{'resource_name':
expected_service[NagiosProps.RESOURCE_NAME],
'service_name':
expected_service[NagiosProps.SERVICE]})
def _assert_expected_service(self, expected_service, service):
for key, value in expected_service.items():
self.assertEqual(value, service[key], 'wrong value for ' + key)

View File

@ -0,0 +1,43 @@
# Copyright 2016 - Nokia
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from vitrage.synchronizer.plugins.nagios.synchronizer import NagiosSynchronizer
from vitrage.tests.mocks import mock_syncronizer as mock_sync
class NagiosSynchronizerWithMockData(NagiosSynchronizer):
"""A nagios synchronizer for tests.
Instead of calling Nagios URL to get the data, it returns the data it
is asked to
"""
def __init__(self, conf):
super(NagiosSynchronizerWithMockData, self).__init__(conf)
self.service_datas = None
def set_service_datas(self, service_datas):
self.service_datas = service_datas
def _get_services_from_nagios(self):
alarms = []
for service_data in self.service_datas:
generators = mock_sync.simple_nagios_alarm_generators(
host_num=1,
events_num=1,
snap_vals=service_data)
alarms.append(
mock_sync.generate_sequential_events_list(generators)[0])
return alarms

View File

@ -15,13 +15,14 @@ from oslo_log import log as logging
from vitrage.synchronizer.plugins.nagios.parser import NagiosParser from vitrage.synchronizer.plugins.nagios.parser import NagiosParser
from vitrage.synchronizer.plugins.nagios.properties import NagiosProperties from vitrage.synchronizer.plugins.nagios.properties import NagiosProperties
from vitrage.tests import base
from vitrage.tests.mocks import utils from vitrage.tests.mocks import utils
from vitrage.tests.unit.synchronizer.nagios.nagios_base_test \
import NagiosBaseTest
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
class NagiosParserTest(base.BaseTest): class NagiosParserTest(NagiosBaseTest):
expected_service1 = {NagiosProperties.RESOURCE_NAME: 'compute-0-0.local', expected_service1 = {NagiosProperties.RESOURCE_NAME: 'compute-0-0.local',
NagiosProperties.SERVICE: 'CPU load', NagiosProperties.SERVICE: 'CPU load',
@ -64,25 +65,6 @@ class NagiosParserTest(base.BaseTest):
# Test assertions # Test assertions
self.assertTrue(nagios_services) self.assertTrue(nagios_services)
self._assert_contains(nagios_services, self.expected_service1) self._assert_contains(self.expected_service1, nagios_services)
self._assert_contains(nagios_services, self.expected_service2) self._assert_contains(self.expected_service2, nagios_services)
self._assert_contains(nagios_services, self.expected_service3) self._assert_contains(self.expected_service3, nagios_services)
def _assert_contains(self, services, expected_service):
for service in services:
if service[NagiosProperties.RESOURCE_NAME] == \
expected_service[NagiosProperties.RESOURCE_NAME] and \
service[NagiosProperties.SERVICE] == \
expected_service[NagiosProperties.SERVICE]:
self._assert_expected_service(expected_service, service)
return
self.fail("service not found: %(resource_name)s %(service_name)s" %
{'resource_name':
expected_service[NagiosProperties.RESOURCE_NAME],
'service_name':
expected_service[NagiosProperties.SERVICE]})
def _assert_expected_service(self, expected_service, service):
for key, value in expected_service.items():
self.assertEqual(value, service[key], 'wrong value for ' + key)

View File

@ -0,0 +1,142 @@
# Copyright 2016 - Nokia
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_config import cfg
from oslo_log import log as logging
from vitrage.synchronizer.plugins.nagios.properties import NagiosProperties \
as NagiosProps
from vitrage.tests.unit.synchronizer.nagios.nagios_base_test \
import NagiosBaseTest
from vitrage.tests.unit.synchronizer.nagios.synchronizer_with_mock_data \
import NagiosSynchronizerWithMockData
LOG = logging.getLogger(__name__)
class NagiosSynchronizerTest(NagiosBaseTest):
def setUp(self):
super(NagiosSynchronizerTest, self).setUp()
self.conf = cfg.ConfigOpts()
def test_get_all(self):
"""Check get_all functionality.
Check the logic of which tests are returned: tests that are not OK,
or tests that were changed from not-OK to OK
"""
# Setup
nagios_synchronizer = NagiosSynchronizerWithMockData(self.conf)
# Action
service_data1 = {NagiosProps.RESOURCE_NAME: 'compute-0',
NagiosProps.SERVICE: 'CPU utilization',
NagiosProps.STATUS: 'OK'}
service_data2 = {NagiosProps.RESOURCE_NAME: 'compute-1',
NagiosProps.SERVICE: 'CPU utilization',
NagiosProps.STATUS: 'OK'}
service_data3 = {NagiosProps.RESOURCE_NAME: 'compute-1',
NagiosProps.SERVICE: 'Uptime',
NagiosProps.STATUS: 'OK'}
nagios_synchronizer.set_service_datas([service_data1,
service_data2,
service_data3])
services = nagios_synchronizer._get_services()
# Test assertions
# Services with status OK should not be returned
self.assertIsNotNone(services, 'No services returned')
self.assertEqual(0, len(services))
# Action
service_data1 = {NagiosProps.RESOURCE_NAME: 'compute-0',
NagiosProps.SERVICE: 'CPU utilization',
NagiosProps.STATUS: 'WARNING'}
service_data2 = {NagiosProps.RESOURCE_NAME: 'compute-1',
NagiosProps.SERVICE: 'CPU utilization',
NagiosProps.STATUS: 'OK'}
service_data3 = {NagiosProps.RESOURCE_NAME: 'compute-1',
NagiosProps.SERVICE: 'Uptime',
NagiosProps.STATUS: 'OK'}
nagios_synchronizer.set_service_datas([service_data1,
service_data2,
service_data3])
services = nagios_synchronizer._get_services()
# Test assertions
self.assertIsNotNone(services, 'No services returned')
self.assertEqual(1, len(services))
self._assert_contains(service_data1, services)
# Action
service_data1 = {NagiosProps.RESOURCE_NAME: 'compute-0',
NagiosProps.SERVICE: 'CPU utilization',
NagiosProps.STATUS: 'CRITICAL'}
service_data2 = {NagiosProps.RESOURCE_NAME: 'compute-1',
NagiosProps.SERVICE: 'CPU utilization',
NagiosProps.STATUS: 'WARNING'}
service_data3 = {NagiosProps.RESOURCE_NAME: 'compute-1',
NagiosProps.SERVICE: 'Uptime',
NagiosProps.STATUS: 'OK'}
nagios_synchronizer.set_service_datas([service_data1,
service_data2,
service_data3])
services = nagios_synchronizer._get_services()
# Test assertions
self.assertIsNotNone(services, 'No services returned')
self.assertEqual(2, len(services))
self._assert_contains(service_data1, services)
self._assert_contains(service_data2, services)
# Action
service_data1 = {NagiosProps.RESOURCE_NAME: 'compute-0',
NagiosProps.SERVICE: 'CPU utilization',
NagiosProps.STATUS: 'OK'}
service_data2 = {NagiosProps.RESOURCE_NAME: 'compute-1',
NagiosProps.SERVICE: 'CPU utilization',
NagiosProps.STATUS: 'OK'}
service_data3 = {NagiosProps.RESOURCE_NAME: 'compute-1',
NagiosProps.SERVICE: 'Uptime',
NagiosProps.STATUS: 'OK'}
nagios_synchronizer.set_service_datas([service_data1,
service_data2,
service_data3])
services = nagios_synchronizer._get_services()
# Test assertions
# The services of service_data1/2 should be returned although their
# status is OK, because they were not OK earlier
self.assertIsNotNone(services, 'No services returned')
self.assertEqual(2, len(services))
self._assert_contains(service_data1, services)
self._assert_contains(service_data2, services)
# Action
services = nagios_synchronizer._get_services()
# Test assertions
# Calling get_services again should not return anything, since all
# services are still OK
self.assertIsNotNone(services, 'services is None')
self.assertEqual(0, len(services))