]> granicus.if.org Git - icinga2/blob - lib/icinga/notification-apply.cpp
Improve config compiler's memory usage
[icinga2] / lib / icinga / notification-apply.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org)    *
4  *                                                                            *
5  * This program is free software; you can redistribute it and/or              *
6  * modify it under the terms of the GNU General Public License                *
7  * as published by the Free Software Foundation; either version 2             *
8  * of the License, or (at your option) any later version.                     *
9  *                                                                            *
10  * This program is distributed in the hope that it will be useful,            *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
13  * GNU General Public License for more details.                               *
14  *                                                                            *
15  * You should have received a copy of the GNU General Public License          *
16  * along with this program; if not, write to the Free Software Foundation     *
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
18  ******************************************************************************/
19
20 #include "icinga/notification.hpp"
21 #include "icinga/service.hpp"
22 #include "config/configitembuilder.hpp"
23 #include "config/applyrule.hpp"
24 #include "config/configcompilercontext.hpp"
25 #include "base/initialize.hpp"
26 #include "base/dynamictype.hpp"
27 #include "base/logger.hpp"
28 #include "base/context.hpp"
29 #include "base/workqueue.hpp"
30 #include "base/configerror.hpp"
31 #include <boost/foreach.hpp>
32
33 using namespace icinga;
34
35 INITIALIZE_ONCE(&Notification::RegisterApplyRuleHandler);
36
37 void Notification::RegisterApplyRuleHandler(void)
38 {
39         std::vector<String> targets;
40         targets.push_back("Host");
41         targets.push_back("Service");
42         ApplyRule::RegisterType("Notification", targets, &Notification::EvaluateApplyRules);
43 }
44
45 void Notification::EvaluateApplyRuleOneInstance(const Checkable::Ptr& checkable, const String& name, const Dictionary::Ptr& locals, const ApplyRule& rule)
46 {
47         DebugInfo di = rule.GetDebugInfo();
48
49         Log(LogDebug, "Notification")
50             << "Applying notification '" << name << "' to object '" << checkable->GetName() << "' for rule " << di;
51
52         ConfigItemBuilder::Ptr builder = make_shared<ConfigItemBuilder>(di);
53         builder->SetType("Notification");
54         builder->SetName(name);
55         builder->SetScope(locals);
56
57         Host::Ptr host;
58         Service::Ptr service;
59         tie(host, service) = GetHostService(checkable);
60
61         builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
62             MakeArray(MakeArray(MakeLiteral("host_name")), OpSetLiteral),
63             MakeLiteral(host->GetName()),
64             di));
65
66         if (service) {
67                 builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
68                     MakeArray(MakeArray(MakeLiteral("service_name")), OpSetLiteral),
69                     MakeLiteral(service->GetShortName()),
70                     di));
71         }
72
73         String zone = checkable->GetZone();
74
75         if (!zone.IsEmpty()) {
76                 builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
77                     MakeArray(MakeArray(MakeLiteral("zone")), OpSetLiteral),
78                     MakeLiteral(zone),
79                     di));
80         }
81
82         builder->AddExpression(rule.GetExpression());
83
84         ConfigItem::Ptr notificationItem = builder->Compile();
85         DynamicObject::Ptr dobj = notificationItem->Commit();
86         dobj->OnConfigLoaded();
87         
88 }
89
90 bool Notification::EvaluateApplyRuleOne(const Checkable::Ptr& checkable, const ApplyRule& rule)
91 {
92         DebugInfo di = rule.GetDebugInfo();
93
94         std::ostringstream msgbuf;
95         msgbuf << "Evaluating 'apply' rule (" << di << ")";
96         CONTEXT(msgbuf.str());
97
98         Host::Ptr host;
99         Service::Ptr service;
100         tie(host, service) = GetHostService(checkable);
101
102         Dictionary::Ptr locals = make_shared<Dictionary>();
103         locals->Set("__parent", rule.GetScope());
104         locals->Set("host", host);
105         if (service)
106                 locals->Set("service", service);
107
108         if (!rule.EvaluateFilter(locals))
109                 return false;
110
111         Value vinstances;
112
113         if (rule.GetFTerm()) {
114                 vinstances = rule.GetFTerm()->Evaluate(locals);
115         } else {
116                 Array::Ptr instances = make_shared<Array>();
117                 instances->Add("");
118                 vinstances = instances;
119         }
120
121         if (vinstances.IsObjectType<Array>()) {
122                 if (!rule.GetFVVar().IsEmpty())
123                         BOOST_THROW_EXCEPTION(ConfigError("Array iterator requires value to be an array.") << errinfo_debuginfo(di));
124
125                 Array::Ptr arr = vinstances;
126
127                 ObjectLock olock(arr);
128                 BOOST_FOREACH(const String& instance, arr) {
129                         String name = rule.GetName();
130
131                         if (!rule.GetFKVar().IsEmpty()) {
132                                 locals->Set(rule.GetFKVar(), instance);
133                                 name += instance;
134                         }
135
136                         EvaluateApplyRuleOneInstance(checkable, name, locals, rule);
137                 }
138         } else if (vinstances.IsObjectType<Dictionary>()) {
139                 if (rule.GetFVVar().IsEmpty())
140                         BOOST_THROW_EXCEPTION(ConfigError("Dictionary iterator requires value to be a dictionary.") << errinfo_debuginfo(di));
141         
142                 Dictionary::Ptr dict = vinstances;
143
144                 ObjectLock olock(dict);
145                 BOOST_FOREACH(const Dictionary::Pair& kv, dict) {
146                         locals->Set(rule.GetFKVar(), kv.first);
147                         locals->Set(rule.GetFVVar(), kv.second);
148
149                         EvaluateApplyRuleOneInstance(checkable, rule.GetName() + kv.first, locals, rule);
150                 }
151         }
152
153         return true;
154 }
155
156 void Notification::EvaluateApplyRule(const ApplyRule& rule)
157 {
158         int apply_count = 0;
159
160         if (rule.GetTargetType() == "Host") {
161                 apply_count = 0;
162
163                 BOOST_FOREACH(const Host::Ptr& host, DynamicType::GetObjectsByType<Host>()) {
164                         CONTEXT("Evaluating 'apply' rules for host '" + host->GetName() + "'");
165
166                         try {
167                                 if (EvaluateApplyRuleOne(host, rule))
168                                         apply_count++;
169                         } catch (const ConfigError& ex) {
170                                 const DebugInfo *di = boost::get_error_info<errinfo_debuginfo>(ex);
171                                 ConfigCompilerContext::GetInstance()->AddMessage(true, ex.what(), di ? *di : DebugInfo());
172                         }
173                 }
174
175                 if (apply_count == 0)
176                         Log(LogWarning, "Notification")
177                             << "Apply rule '" << rule.GetName() << "' for host does not match anywhere!";
178
179         } else if (rule.GetTargetType() == "Service") {
180                 apply_count = 0;
181
182                 BOOST_FOREACH(const Service::Ptr& service, DynamicType::GetObjectsByType<Service>()) {
183                         CONTEXT("Evaluating 'apply' rules for Service '" + service->GetName() + "'");
184
185                         try {
186                                 if (EvaluateApplyRuleOne(service, rule))
187                                         apply_count++;
188                         } catch (const ConfigError& ex) {
189                                 const DebugInfo *di = boost::get_error_info<errinfo_debuginfo>(ex);
190                                 ConfigCompilerContext::GetInstance()->AddMessage(true, ex.what(), di ? *di : DebugInfo());
191                         }
192                 }
193
194                 if (apply_count == 0)
195                         Log(LogWarning, "Notification")
196                             << "Apply rule '" << rule.GetName() << "' for service does not match anywhere!";
197
198         } else {
199                 Log(LogWarning, "Notification")
200                     << "Wrong target type for apply rule '" << rule.GetName() << "'!";
201         }
202 }
203 void Notification::EvaluateApplyRules(const std::vector<ApplyRule>& rules)
204 {
205         ParallelWorkQueue upq;
206
207         BOOST_FOREACH(const ApplyRule& rule, rules) {
208                 upq.Enqueue(boost::bind(&Notification::EvaluateApplyRule, boost::cref(rule)));
209         }
210
211         upq.Join();
212 }