From e9d32eeada69ca1f7542297edc6896d0d542434b Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Wed, 27 Feb 2013 21:49:03 +0100 Subject: [PATCH] Implemented user groups. --- components/compat/compatcomponent.cpp | 2 +- itl/types.conf | 27 ++++- lib/base/i2-base.h | 1 + lib/icinga/Makefile.am | 4 +- lib/icinga/host.cpp | 8 +- lib/icinga/hostgroup.cpp | 2 +- lib/icinga/hostgroup.h | 2 +- lib/icinga/i2-icinga.h | 1 + lib/icinga/icinga.vcxproj | 2 + lib/icinga/notification.cpp | 45 +++++++- lib/icinga/notification.h | 2 + lib/icinga/service-notification.cpp | 4 + lib/icinga/servicegroup.cpp | 2 +- lib/icinga/servicegroup.h | 2 +- lib/icinga/user.cpp | 17 +++ lib/icinga/user.h | 7 ++ lib/icinga/usergroup.cpp | 149 ++++++++++++++++++++++++++ lib/icinga/usergroup.h | 67 ++++++++++++ 18 files changed, 328 insertions(+), 16 deletions(-) create mode 100644 lib/icinga/usergroup.cpp create mode 100644 lib/icinga/usergroup.h diff --git a/components/compat/compatcomponent.cpp b/components/compat/compatcomponent.cpp index c04e59070..229fe35d8 100644 --- a/components/compat/compatcomponent.cpp +++ b/components/compat/compatcomponent.cpp @@ -91,7 +91,7 @@ String CompatComponent::GetCommandPath(void) const void CompatComponent::Start(void) { m_StatusTimer = boost::make_shared(); - m_StatusTimer->SetInterval(15); + m_StatusTimer->SetInterval(5); m_StatusTimer->OnTimerExpired.connect(boost::bind(&CompatComponent::StatusTimerHandler, this)); m_StatusTimer->Start(); m_StatusTimer->Reschedule(0); diff --git a/itl/types.conf b/itl/types.conf index af7b94197..0814e730e 100644 --- a/itl/types.conf +++ b/itl/types.conf @@ -117,6 +117,10 @@ type Host { %attribute dictionary "users" { %attribute string "*" + }, + + %attribute dictionary "groups" { + %attribute string "*" } } }, @@ -209,6 +213,10 @@ type Service { %attribute dictionary "users" { %attribute string "*" + }, + + %attribute dictionary "groups" { + %attribute string "*" } } }, @@ -243,9 +251,17 @@ type Notification { %attribute string "service", %attribute dictionary "macros" { - %attribute string "*" + %attribute string "*" + }, + + %attribute dictionary "users" { + %attribute string "*" }, + %attribute dictionary "groups" { + %attribute string "*" + }, + %attribute dictionary "notification_command" { %attribute string "_*" }, @@ -263,6 +279,15 @@ type Script { type User { %attribute dictionary "macros" { %attribute string "*" + }, + + %attribute dictionary "groups" { + %attribute string "*" } } +type UserGroup { + %attribute string "display_name", + %attribute string "action_url", + %attribute string "notes_url" +} diff --git a/lib/base/i2-base.h b/lib/base/i2-base.h index ee80e4dfa..a0c33dcbe 100644 --- a/lib/base/i2-base.h +++ b/lib/base/i2-base.h @@ -92,6 +92,7 @@ #include #include #include +#include using std::vector; using std::map; diff --git a/lib/icinga/Makefile.am b/lib/icinga/Makefile.am index 9dba6e9f5..e0fea7705 100644 --- a/lib/icinga/Makefile.am +++ b/lib/icinga/Makefile.am @@ -42,7 +42,9 @@ libicinga_la_SOURCES = \ servicegroup.cpp \ servicegroup.h \ user.cpp \ - user.h + user.h \ + usergroup.cpp \ + usergroup.h libicinga_la_CPPFLAGS = \ -DI2_ICINGA_BUILD \ diff --git a/lib/icinga/host.cpp b/lib/icinga/host.cpp index 6cb24c555..c2c72d62d 100644 --- a/lib/icinga/host.cpp +++ b/lib/icinga/host.cpp @@ -22,7 +22,7 @@ using namespace icinga; boost::mutex Host::m_ServiceMutex; -map > > Host::m_ServicesCache; +map > Host::m_ServicesCache; bool Host::m_ServicesCacheValid = true; REGISTER_SCRIPTFUNCTION("ValidateServiceDictionary", &Host::ValidateServiceDictionary); @@ -345,7 +345,7 @@ void Host::RefreshServicesCache(void) m_ServicesCacheValid = true; } - map > > newServicesCache; + map > newServicesCache; BOOST_FOREACH(const DynamicObject::Ptr& object, DynamicType::GetObjects("Service")) { const Service::Ptr& service = static_pointer_cast(object); @@ -441,8 +441,8 @@ Service::Ptr Host::GetServiceByShortName(const Host::Ptr& self, const Value& nam { boost::mutex::scoped_lock lock(m_ServiceMutex); - map >& services = m_ServicesCache[host_name]; - map >::iterator it = services.find(name); + map& services = m_ServicesCache[host_name]; + map::iterator it = services.find(name); if (it != services.end()) { Service::Ptr service = it->second.lock(); diff --git a/lib/icinga/hostgroup.cpp b/lib/icinga/hostgroup.cpp index ef97c42ff..7b38fd1c1 100644 --- a/lib/icinga/hostgroup.cpp +++ b/lib/icinga/hostgroup.cpp @@ -126,7 +126,7 @@ void HostGroup::RefreshMembersCache(void) m_MembersCacheValid = true; } - map > > newMembersCache; + map > newMembersCache; BOOST_FOREACH(const DynamicObject::Ptr& object, DynamicType::GetObjects("Host")) { const Host::Ptr& host = static_pointer_cast(object); diff --git a/lib/icinga/hostgroup.h b/lib/icinga/hostgroup.h index 97b802df4..1e865bf84 100644 --- a/lib/icinga/hostgroup.h +++ b/lib/icinga/hostgroup.h @@ -56,7 +56,7 @@ private: Attribute m_ActionUrl; static boost::mutex m_Mutex; - static map > > m_MembersCache; + static map > m_MembersCache; static bool m_MembersCacheValid; static void RefreshMembersCache(void); diff --git a/lib/icinga/i2-icinga.h b/lib/icinga/i2-icinga.h index cb6f9c66d..d368fdd93 100644 --- a/lib/icinga/i2-icinga.h +++ b/lib/icinga/i2-icinga.h @@ -47,6 +47,7 @@ using boost::algorithm::is_any_of; #include "icingaapplication.h" #include "user.h" +#include "usergroup.h" #include "notification.h" #include "notificationrequestmessage.h" diff --git a/lib/icinga/icinga.vcxproj b/lib/icinga/icinga.vcxproj index 44201f776..7f55cdd0a 100644 --- a/lib/icinga/icinga.vcxproj +++ b/lib/icinga/icinga.vcxproj @@ -45,6 +45,7 @@ + @@ -64,6 +65,7 @@ + {C1FC77E1-04A4-481B-A78B-2F7AF489C2F8} diff --git a/lib/icinga/notification.cpp b/lib/icinga/notification.cpp index be07a1f25..a8a4e9e46 100644 --- a/lib/icinga/notification.cpp +++ b/lib/icinga/notification.cpp @@ -29,6 +29,7 @@ Notification::Notification(const Dictionary::Ptr& properties) RegisterAttribute("notification_command", Attribute_Config, &m_NotificationCommand); RegisterAttribute("macros", Attribute_Config, &m_Macros); RegisterAttribute("users", Attribute_Config, &m_Users); + RegisterAttribute("groups", Attribute_Config, &m_Groups); RegisterAttribute("host_name", Attribute_Config, &m_HostName); RegisterAttribute("service", Attribute_Config, &m_Service); } @@ -80,7 +81,33 @@ set Notification::GetUsers(void) const if (users) { String name; BOOST_FOREACH(tie(tuples::ignore, name), users) { - result.insert(User::GetByName(name)); + User::Ptr user = User::GetByName(name); + + if (!user) + continue; + + result.insert(user); + } + } + + return result; +} + +set Notification::GetGroups(void) const +{ + set result; + + Dictionary::Ptr groups = m_Groups; + + if (groups) { + String name; + BOOST_FOREACH(tie(tuples::ignore, name), groups) { + UserGroup::Ptr ug = UserGroup::GetByName(name); + + if (!ug) + continue; + + result.insert(ug); } } @@ -120,12 +147,14 @@ void Notification::BeginExecuteNotification(const Notification::Ptr& self, Notif Service::Ptr service; set users; + set groups; { ObjectLock olock(self); macroDicts.push_back(self->GetMacros()); service = self->GetService(); users = self->GetUsers(); + groups = self->GetGroups(); } Host::Ptr host; @@ -157,14 +186,20 @@ void Notification::BeginExecuteNotification(const Notification::Ptr& self, Notif Dictionary::Ptr macros = MacroProcessor::MergeMacroDicts(macroDicts); - int count = 0; + set allUsers; + + std::copy(users.begin(), users.end(), std::inserter(allUsers, allUsers.begin())); + + BOOST_FOREACH(const UserGroup::Ptr& ug, groups) { + set members = UserGroup::GetMembers(ug); + std::copy(members.begin(), members.end(), std::inserter(allUsers, allUsers.begin())); + } - BOOST_FOREACH(const User::Ptr& user, users) { + BOOST_FOREACH(const User::Ptr& user, allUsers) { BeginExecuteNotificationHelper(self, macros, type, user); - count++; } - if (count == 0) { + if (allUsers.size() == 0) { /* Send a notification even if there are no users specified. */ BeginExecuteNotificationHelper(self, macros, type, User::Ptr()); } diff --git a/lib/icinga/notification.h b/lib/icinga/notification.h index 3b3c77e12..4bbba987e 100644 --- a/lib/icinga/notification.h +++ b/lib/icinga/notification.h @@ -61,6 +61,7 @@ public: Value GetNotificationCommand(void) const; Dictionary::Ptr GetMacros(void) const; set GetUsers(void) const; + set GetGroups(void) const; static void BeginExecuteNotification(const Notification::Ptr& self, NotificationType type); @@ -73,6 +74,7 @@ private: Attribute m_NotificationCommand; Attribute m_Macros; Attribute m_Users; + Attribute m_Groups; Attribute m_HostName; Attribute m_Service; diff --git a/lib/icinga/service-notification.cpp b/lib/icinga/service-notification.cpp index 506410694..e066d180b 100644 --- a/lib/icinga/service-notification.cpp +++ b/lib/icinga/service-notification.cpp @@ -169,6 +169,10 @@ static void CopyNotificationAttributes(TDict notificationDesc, const ConfigItemB if (!users.IsEmpty()) builder->AddExpression("users", OperatorPlus, users); + Value groups = notificationDesc->Get("groups"); + if (!groups.IsEmpty()) + builder->AddExpression("groups", OperatorPlus, groups); + /*Value notificationInterval = notificationDesc->Get("notification_interval"); if (!notificationInterval.IsEmpty()) builder->AddExpression("notification_interval", OperatorSet, notificationInterval);*/ diff --git a/lib/icinga/servicegroup.cpp b/lib/icinga/servicegroup.cpp index 2700ec981..f807046dd 100644 --- a/lib/icinga/servicegroup.cpp +++ b/lib/icinga/servicegroup.cpp @@ -126,7 +126,7 @@ void ServiceGroup::RefreshMembersCache(void) m_MembersCacheValid = true; } - map > > newMembersCache; + map > newMembersCache; BOOST_FOREACH(const DynamicObject::Ptr& object, DynamicType::GetObjects("Service")) { const Service::Ptr& service = static_pointer_cast(object); diff --git a/lib/icinga/servicegroup.h b/lib/icinga/servicegroup.h index 9c041153b..073f515bf 100644 --- a/lib/icinga/servicegroup.h +++ b/lib/icinga/servicegroup.h @@ -56,7 +56,7 @@ private: Attribute m_ActionUrl; static boost::mutex m_Mutex; - static map > > m_MembersCache; + static map > m_MembersCache; static bool m_MembersCacheValid; static void RefreshMembersCache(void); diff --git a/lib/icinga/user.cpp b/lib/icinga/user.cpp index b1f705a38..27b8cd4fc 100644 --- a/lib/icinga/user.cpp +++ b/lib/icinga/user.cpp @@ -27,6 +27,18 @@ User::User(const Dictionary::Ptr& properties) : DynamicObject(properties) { RegisterAttribute("macros", Attribute_Config, &m_Macros); + RegisterAttribute("groups", Attribute_Config, &m_Groups); +} + +User::~User(void) +{ + UserGroup::InvalidateMembersCache(); +} + +void User::OnAttributeChanged(const String& name, const Value& oldValue) +{ + if (name == "groups") + UserGroup::InvalidateMembersCache(); } User::Ptr User::GetByName(const String& name) @@ -36,6 +48,11 @@ User::Ptr User::GetByName(const String& name) return dynamic_pointer_cast(configObject); } +Dictionary::Ptr User::GetGroups(void) const +{ + return m_Groups; +} + Dictionary::Ptr User::GetMacros(void) const { return m_Macros; diff --git a/lib/icinga/user.h b/lib/icinga/user.h index 0c7e5f6a3..f9b475ca0 100644 --- a/lib/icinga/user.h +++ b/lib/icinga/user.h @@ -35,14 +35,21 @@ public: typedef weak_ptr WeakPtr; User(const Dictionary::Ptr& properties); + ~User(void); static User::Ptr GetByName(const String& name); + Dictionary::Ptr GetGroups(void) const; + Dictionary::Ptr GetMacros(void) const; static Dictionary::Ptr CalculateDynamicMacros(const User::Ptr& self); +protected: + void OnAttributeChanged(const String& name, const Value& oldValue); + private: Attribute m_Macros; + Attribute m_Groups; }; } diff --git a/lib/icinga/usergroup.cpp b/lib/icinga/usergroup.cpp new file mode 100644 index 000000000..ec1e773c7 --- /dev/null +++ b/lib/icinga/usergroup.cpp @@ -0,0 +1,149 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012 Icinga Development Team (http://www.icinga.org/) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#include "i2-icinga.h" + +using namespace icinga; + +boost::mutex UserGroup::m_Mutex; +map > UserGroup::m_MembersCache; +bool UserGroup::m_MembersCacheValid = true; + +REGISTER_TYPE(UserGroup, NULL); + +UserGroup::UserGroup(const Dictionary::Ptr& properties) + : DynamicObject(properties) +{ + RegisterAttribute("display_name", Attribute_Config, &m_DisplayName); + RegisterAttribute("notes_url", Attribute_Config, &m_NotesUrl); + RegisterAttribute("action_url", Attribute_Config, &m_ActionUrl); +} + +UserGroup::~UserGroup(void) +{ + InvalidateMembersCache(); +} + +void UserGroup::OnRegistrationCompleted(void) +{ + InvalidateMembersCache(); +} + +String UserGroup::GetDisplayName(void) const +{ + if (!m_DisplayName.IsEmpty()) + return m_DisplayName; + else + return GetName(); +} + +String UserGroup::GetNotesUrl(void) const +{ + return m_NotesUrl; +} + +String UserGroup::GetActionUrl(void) const +{ + return m_ActionUrl; +} + +/** + * @threadsafety Always. + */ +UserGroup::Ptr UserGroup::GetByName(const String& name) +{ + DynamicObject::Ptr configObject = DynamicObject::GetObject("UserGroup", name); + + if (!configObject) + BOOST_THROW_EXCEPTION(invalid_argument("UserGroup '" + name + "' does not exist.")); + + return dynamic_pointer_cast(configObject); +} + +set UserGroup::GetMembers(const UserGroup::Ptr& self) +{ + String name; + + { + ObjectLock olock(self); + name = self->GetName(); + } + + set users; + + { + boost::mutex::scoped_lock lock(m_Mutex); + + BOOST_FOREACH(const User::WeakPtr& wuser, m_MembersCache[name]) { + User::Ptr user = wuser.lock(); + + if (!user) + continue; + + users.insert(user); + } + } + + return users; +} + +void UserGroup::InvalidateMembersCache(void) +{ + { + boost::mutex::scoped_lock lock(m_Mutex); + + if (m_MembersCacheValid) + Utility::QueueAsyncCallback(boost::bind(&UserGroup::RefreshMembersCache)); + + m_MembersCacheValid = false; + } +} + +void UserGroup::RefreshMembersCache(void) +{ + { + boost::mutex::scoped_lock lock(m_Mutex); + + if (m_MembersCacheValid) + return; + + m_MembersCacheValid = true; + } + + map > newMembersCache; + + BOOST_FOREACH(const DynamicObject::Ptr& object, DynamicType::GetObjects("User")) { + const User::Ptr& user = static_pointer_cast(object); + ObjectLock olock(user); + + Dictionary::Ptr dict; + dict = user->GetGroups(); + + if (dict) { + ObjectLock mlock(dict); + Value UserGroup; + BOOST_FOREACH(tie(tuples::ignore, UserGroup), dict) { + newMembersCache[UserGroup].push_back(user); + } + } + } + + boost::mutex::scoped_lock lock(m_Mutex); + m_MembersCache.swap(newMembersCache); +} diff --git a/lib/icinga/usergroup.h b/lib/icinga/usergroup.h new file mode 100644 index 000000000..91a9cb7c8 --- /dev/null +++ b/lib/icinga/usergroup.h @@ -0,0 +1,67 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012 Icinga Development Team (http://www.icinga.org/) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#ifndef USERGROUP_H +#define USERGROUP_H + +namespace icinga +{ + +/** + * An Icinga user group. + * + * @ingroup icinga + */ +class I2_ICINGA_API UserGroup : public DynamicObject +{ +public: + typedef shared_ptr Ptr; + typedef weak_ptr WeakPtr; + + UserGroup(const Dictionary::Ptr& properties); + ~UserGroup(void); + + static UserGroup::Ptr GetByName(const String& name); + + String GetDisplayName(void) const; + String GetNotesUrl(void) const; + String GetActionUrl(void) const; + + static set GetMembers(const UserGroup::Ptr& self); + + static void InvalidateMembersCache(void); + +protected: + virtual void OnRegistrationCompleted(void); + +private: + Attribute m_DisplayName; + Attribute m_NotesUrl; + Attribute m_ActionUrl; + + static boost::mutex m_Mutex; + static map > m_MembersCache; + static bool m_MembersCacheValid; + + static void RefreshMembersCache(void); +}; + +} + +#endif /* HOSTGROUP_H */ -- 2.40.0