# -*- coding: utf-8 -*-
"""Module for dealing with multiple Livestatus instances at once."""
from __future__ import absolute_import
from pynag.Parsers import livestatus
from pynag.Parsers import errors
from six.moves import map
class MultiSite(livestatus.Livestatus):
""" Wrapps around multiple Livesatus instances and aggregates the results
of queries.
Example:
>>> m = MultiSite()
>>> m.add_backend(path='/var/spool/nagios/livestatus.socket', name='local')
>>> m.add_backend(path='127.0.0.1:5992', name='remote')
"""
def __init__(self, *args, **kwargs):
super(MultiSite, self).__init__(*args, **kwargs)
self.backends = {}
def add_backend(self, path, name):
""" Add a new livestatus backend to this instance.
Arguments:
path (str): Path to file socket or remote address
name (str): Friendly shortname for this backend
"""
backend = livestatus.Livestatus(
livestatus_socket_path=path,
nagios_cfg_file=self.nagios_cfg_file,
authuser=self.authuser
)
self.backends[name] = backend
def get_backends(self):
""" Returns a list of mk_livestatus instances
Returns:
list. List of mk_livestatus instances
"""
return self.backends
def get_backend(self, backend_name):
""" Return one specific backend that has previously been added
"""
if not backend_name:
return list(self.backends.values())[0]
try:
return self.backends[backend_name]
except KeyError:
# TODO: Raise a more specific error.
raise errors.ParserError("No backend found with name='%s'" % backend_name)
def query(self, query, *args, **kwargs):
""" Behaves like mk_livestatus.query() except results are aggregated from multiple backends
Arguments:
backend (str): If specified, fetch only data from this backend (see add_backend())
*args: Passed directly to mk_livestatus.query()
**kwargs: Passed directly to mk_livestatus.query()
"""
result = []
backend = kwargs.pop('backend', None)
# Special hack, if 'Stats' argument was provided to livestatus
# We have to maintain compatibility with old versions of livestatus
# and return single list with all results instead of a list of dicts
doing_stats = any([x.startswith('Stats:') for x in args + (query,)])
# Iterate though all backends and run the query
# TODO: Make this multithreaded
for name, backend_instance in self.backends.items():
# Skip if a specific backend was requested and this is not it
if backend and backend != name:
continue
query_result = backend_instance.query(query, *args, **kwargs)
if doing_stats:
result = self._merge_statistics(result, query_result)
else:
for row in query_result:
row['backend'] = name
result.append(row)
return result
def _merge_statistics(self, list1, list2):
""" Merges multiple livestatus results into one result
Arguments:
list1 (list): List of integers
list2 (list): List of integers
Returns:
list. Aggregated results of list1 + list2
Example:
>>> result1 = [1,1,1,1]
>>> result2 = [2,2,2,2]
>>> MultiSite()._merge_statistics(result1, result2)
[3, 3, 3, 3]
"""
if not list1:
return list2
if not list2:
return list1
number_of_columns = len(list1)
result = [0] * number_of_columns
for row in (list1, list2):
for i, column in enumerate(row):
result[i] += column
return result
def get_host(self, host_name, backend=None):
""" Same as Livestatus.get_host() """
backend = self.get_backend(backend)
return backend.get_host(host_name)
def get_service(self, host_name, service_description, backend=None):
""" Same as Livestatus.get_service() """
backend = self.get_backend(backend)
return backend.get_service(host_name, service_description)
def get_contact(self, contact_name, backend=None):
""" Same as Livestatus.get_contact() """
backend = self.get_backend(backend)
return backend.get_contact(contact_name)
def get_contactgroup(self, contactgroup_name, backend=None):
""" Same as Livestatus.get_contact() """
backend = self.get_backend(backend)
return backend.get_contactgroup(contactgroup_name)
def get_servicegroup(self, servicegroup_name, backend=None):
""" Same as Livestatus.get_servicegroup() """
backend = self.get_backend(backend)
return backend.get_servicegroup(servicegroup_name)
def get_hostgroup(self, hostgroup_name, backend=None):
""" Same as Livestatus.get_hostgroup() """
backend = self.get_backend(backend)
return backend.get_hostgroup(hostgroup_name)