#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2015-2015: Alignak team, see AUTHORS.txt file for contributors
#
# This file is part of Alignak.
#
# Alignak is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Alignak is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Alignak. If not, see <http://www.gnu.org/licenses/>.
#
#
# This file incorporates work covered by the following copyright and
# permission notice:
#
# Copyright (C) 2009-2014:
# Hartmut Goebel, h.goebel@goebel-consult.de
# Guillaume Bour, guillaume@bour.cc
# Nicolas Dupeux, nicolas@dupeux.net
# Grégory Starck, g.starck@gmail.com
# Gerhard Lausser, gerhard.lausser@consol.de
# Sebastien Coavoux, s.coavoux@free.fr
# Thibault Cohen, titilambert@gmail.com
# Jean Gabes, naparuba@gmail.com
# Christophe Simon, geektophe@gmail.com
# Romain Forlot, rforlot@yahoo.com
# This file is part of Shinken.
#
# Shinken is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Shinken is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Shinken. If not, see <http://www.gnu.org/licenses/>.
"""
This module provide Hostgroup and Hostgroups class used to manage host groups
"""
from alignak.objects.itemgroup import Itemgroup, Itemgroups
from alignak.util import get_obj_name
from alignak.property import StringProp, IntegerProp
from alignak.log import logger
[docs]class Hostgroup(Itemgroup):
"""
Class to manage a group of host
A Hostgroup is used to manage a group of hosts
"""
_id = 1 # zero is always a little bit special... like in database
my_type = 'hostgroup'
properties = Itemgroup.properties.copy()
properties.update({
'_id': IntegerProp(default=0, fill_brok=['full_status']),
'hostgroup_name': StringProp(fill_brok=['full_status']),
'alias': StringProp(fill_brok=['full_status']),
'notes': StringProp(default='', fill_brok=['full_status']),
'notes_url': StringProp(default='', fill_brok=['full_status']),
'action_url': StringProp(default='', fill_brok=['full_status']),
'realm': StringProp(default='', fill_brok=['full_status'],
conf_send_preparation=get_obj_name),
})
macros = {
'HOSTGROUPALIAS': 'alias',
'HOSTGROUPMEMBERS': 'members',
'HOSTGROUPNOTES': 'notes',
'HOSTGROUPNOTESURL': 'notes_url',
'HOSTGROUPACTIONURL': 'action_url'
}
[docs] def get_name(self):
"""
Get name of group
:return: Name of hostgroup
:rtype: str
"""
return self.hostgroup_name
[docs] def get_hosts(self):
"""
Get list of hosts of this group
:return: list of hosts
:rtype: list
"""
if getattr(self, 'members', None) is not None:
return self.members
else:
return []
[docs] def get_hostgroup_members(self):
"""
Get hostgroup members
:return: list of hosts
:rtype: list
"""
# TODO: consistency : a Hostgroup instance should always
# have its hostgroup_members attribute defined, even if the empty list
if hasattr(self, 'hostgroup_members'):
# consistency: any Hostgroup instance's hostgroup_members attribute
# should already be decoded/parsed:
# this should already be in its list form.
return [m.strip() for m in self.hostgroup_members.split(',')]
else:
return []
[docs] def get_hosts_by_explosion(self, hostgroups):
"""
Get hosts of this group
:param hostgroups: Hostgroup object
:type hostgroups: alignak.objects.hostgroup.Hostgroups
:return: list of hosts of this group
:rtype: list
"""
# First we tag the hg so it will not be explode
# if a son of it already call it
self.already_explode = True
# Now the recursive part
# rec_tag is set to False every HG we explode
# so if True here, it must be a loop in HG
# calls... not GOOD!
if self.rec_tag:
logger.error("[hostgroup::%s] got a loop in hostgroup definition", self.get_name())
return self.get_hosts()
# Ok, not a loop, we tag it and continue
self.rec_tag = True
hg_mbrs = self.get_hostgroup_members()
for hg_mbr in hg_mbrs:
hostgroup = hostgroups.find_by_name(hg_mbr.strip())
if hostgroup is not None:
value = hostgroup.get_hosts_by_explosion(hostgroups)
if value is not None:
self.add_string_member(value)
return self.get_hosts()
[docs]class Hostgroups(Itemgroups):
"""
Class to manage list of Hostgroup
Hostgroups is used to regroup all Hostgroup
"""
name_property = "hostgroup_name" # is used for finding hostgroups
inner_class = Hostgroup
[docs] def get_members_by_name(self, hgname):
"""
Get all members by name given in parameter
:param hgname: name of members
:type hgname: str
:return: list of hosts with this name
:rtype: list
"""
hostgroup = self.find_by_name(hgname)
if hostgroup is None:
return []
return hostgroup.get_hosts()
[docs] def linkify(self, hosts=None, realms=None):
"""
Make link of hosts / realms
:param hosts: object Hosts
:type hosts: alignak.objects.hostgroup.Hostgroups
:param realms: object Realms
:type realms: alignak.objects.realm.Realms
:return: None
"""
self.linkify_hg_by_hst(hosts)
self.linkify_hg_by_realms(realms)
[docs] def linkify_hg_by_hst(self, hosts):
"""
We just search for each hostgroup the id of the hosts
and replace the name by the id
:param hosts: object Hosts
:type hosts: object
:return: None
"""
for hostgroup in self:
mbrs = hostgroup.get_hosts()
# The new member list, in id
new_mbrs = []
for mbr in mbrs:
mbr = mbr.strip() # protect with strip at the begining so don't care about spaces
if mbr == '': # void entry, skip this
continue
elif mbr == '*':
new_mbrs.extend(hosts)
else:
host = hosts.find_by_name(mbr)
if host is not None:
new_mbrs.append(host)
else:
hostgroup.add_string_unknown_member(mbr)
# Make members uniq
new_mbrs = list(set(new_mbrs))
# We find the id, we replace the names
hostgroup.replace_members(new_mbrs)
# Now register us in our members
for host in hostgroup.members:
host.hostgroups.append(hostgroup)
# and be sure we are uniq in it
host.hostgroups = list(set(host.hostgroups))
[docs] def linkify_hg_by_realms(self, realms):
"""
More than an explode function, but we need to already
have members so... Will be really linkify just after
And we explode realm in ours members, but do not override
a host realm value if it's already set
:param realms: object Realms
:type realms: object
:return: None
"""
# Now we explode the realm value if we've got one
# The group realm must not override a host one (warning?)
for hostgroup in self:
if not hasattr(hostgroup, 'realm'):
continue
# Maybe the value is void?
if not hostgroup.realm.strip():
continue
realm = realms.find_by_name(hostgroup.realm.strip())
if realm is not None:
hostgroup.realm = realm
logger.debug("[hostgroups] %s is in %s realm",
hostgroup.get_name(), realm.get_name())
else:
err = "the hostgroup %s got an unknown realm '%s'" % \
(hostgroup.get_name(), hostgroup.realm)
hostgroup.configuration_errors.append(err)
hostgroup.realm = None
continue
for host in hostgroup:
if host is None:
continue
if host.realm is None or host.got_default_realm: # default not hasattr(h, 'realm'):
logger.debug("[hostgroups] apply a realm %s to host %s from a hostgroup "
"rule (%s)", hostgroup.realm.get_name(),
host.get_name(), hostgroup.get_name())
host.realm = hostgroup.realm
else:
if host.realm != hostgroup.realm:
logger.warning("[hostgroups] host %s it not in the same realm than it's "
"hostgroup %s", host.get_name(), hostgroup.get_name())
[docs] def add_member(self, hname, hgname):
"""
Add a host string to a hostgroup member
if the host group do not exist, create it
:param hname: host name
:type hname: str
:param hgname:hostgroup name
:type hgname: str
:return: None
"""
hostgroup = self.find_by_name(hgname)
# if the id do not exist, create the hg
if hostgroup is None:
hostgroup = Hostgroup({'hostgroup_name': hgname, 'alias': hgname, 'members': hname})
self.add(hostgroup)
else:
hostgroup.add_string_member(hname)
[docs] def explode(self):
"""
Fill members with hostgroup_members
:return: None
"""
# We do not want a same hg to be explode again and again
# so we tag it
for tmp_hg in self.items.values():
tmp_hg.already_explode = False
for hostgroup in self.items.values():
if hasattr(hostgroup, 'hostgroup_members') and not \
hostgroup.already_explode:
# get_hosts_by_explosion is a recursive
# function, so we must tag hg so we do not loop
for tmp_hg in self.items.values():
tmp_hg.rec_tag = False
hostgroup.get_hosts_by_explosion(self)
# We clean the tags
for tmp_hg in self.items.values():
if hasattr(tmp_hg, 'rec_tag'):
del tmp_hg.rec_tag
del tmp_hg.already_explode