]> git.ipfire.org Git - thirdparty/squid.git/blame - src/adaptation/ServiceGroups.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / adaptation / ServiceGroups.cc
CommitLineData
bbc27441 1/*
4ac4a490 2 * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
bbc27441
AJ
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
582c2af2 9#include "squid.h"
62c7f90e 10#include "adaptation/AccessRule.h"
582c2af2 11#include "adaptation/Config.h"
53340485 12#include "adaptation/DynamicGroupCfg.h"
62c7f90e 13#include "adaptation/Service.h"
a22e6cd3 14#include "adaptation/ServiceFilter.h"
62c7f90e 15#include "adaptation/ServiceGroups.h"
582c2af2
FC
16#include "ConfigParser.h"
17#include "Debug.h"
28204b3b 18#include "StrList.h"
582c2af2 19#include "wordlist.h"
62c7f90e 20
a22e6cd3 21Adaptation::ServiceGroup::ServiceGroup(const String &aKind, bool allSame):
f53969cc
SM
22 kind(aKind), method(methodNone), point(pointNone),
23 allServicesSame(allSame)
62c7f90e
AR
24{
25}
26
27Adaptation::ServiceGroup::~ServiceGroup()
28{
29}
30
31void
32Adaptation::ServiceGroup::parse()
33{
2eceb328 34 id = ConfigParser::NextToken();
62c7f90e
AR
35
36 wordlist *names = NULL;
37 ConfigParser::ParseWordList(&names);
38 for (wordlist *i = names; i; i = i->next)
39 services.push_back(i->key);
40 wordlistDestroy(&names);
41}
42
a22e6cd3 43// Note: configuration code aside, this method is called by DynamicServiceChain
62c7f90e
AR
44void
45Adaptation::ServiceGroup::finalize()
46{
a22e6cd3
AR
47 // 1) warn if services have different methods or vectoring point
48 // 2) warn if all-same services have different bypass status
49 // 3) warn if there are seemingly identical services in the group
50 // TODO: optimize by remembering ServicePointers rather than IDs
7e8c4ee9
CT
51 if (!removedServices.empty()) {
52 String s;
53 for (Store::iterator it = removedServices.begin(); it != removedServices.end(); ++it) {
54 s.append(*it);
55 s.append(',');
56 }
57 s.cut(s.size() - 1);
58 debugs(93, DBG_IMPORTANT, "Adaptation group '" << id << "' contains disabled member(s) after reconfiguration: " << s);
c33a88ca 59 removedServices.clear();
7e8c4ee9 60 }
a22e6cd3
AR
61
62 String baselineKey;
63 bool baselineBypass = false;
64 for (Pos pos = 0; has(pos); ++pos) {
65 // TODO: quit on all errors
b0365bd9 66 const String &serviceId = services[pos];
a22e6cd3
AR
67 ServicePointer service = at(pos);
68 if (service != NULL) {
e1381638 69 if (method == methodNone) {
a22e6cd3
AR
70 // optimization: cache values that should be the same
71 method = service->cfg().method;
72 point = service->cfg().point;
73 } else {
74 if (method != service->cfg().method)
b0365bd9 75 finalizeMsg("Inconsistent service method for", serviceId, true);
a22e6cd3 76 if (point != service->cfg().point)
b0365bd9 77 finalizeMsg("Inconsistent vectoring point for", serviceId, true);
a22e6cd3
AR
78 }
79
80 checkUniqueness(pos);
81
e1381638 82 if (allServicesSame) {
a22e6cd3
AR
83 if (!baselineKey.size()) {
84 baselineKey = service->cfg().key;
85 baselineBypass = service->cfg().bypass;
e1381638 86 } else if (baselineBypass != service->cfg().bypass) {
fa84c01d 87 debugs(93, DBG_CRITICAL, "WARNING: Inconsistent bypass in " << kind <<
e1381638 88 ' ' << id << " may produce surprising results: " <<
b0365bd9 89 baselineKey << " vs. " << serviceId);
a22e6cd3
AR
90 }
91 }
e1381638 92 } else {
b0365bd9 93 finalizeMsg("ERROR: Unknown adaptation name", serviceId, true);
a22e6cd3 94 }
26ac0430 95 }
bed1aa51 96 debugs(93,7, HERE << "finalized " << kind << ": " << id);
62c7f90e
AR
97}
98
a22e6cd3
AR
99/// checks that the service name or URI is not repeated later in the group
100void
101Adaptation::ServiceGroup::checkUniqueness(const Pos checkedPos) const
102{
103 ServicePointer checkedService = at(checkedPos);
104 if (!checkedService) // should not happen but be robust
105 return;
106
107 for (Pos p = checkedPos + 1; has(p); ++p) {
108 ServicePointer s = at(p);
109 if (s != NULL && s->cfg().key == checkedService->cfg().key)
110 finalizeMsg("duplicate service name", s->cfg().key, false);
e1381638 111 else if (s != NULL && s->cfg().uri == checkedService->cfg().uri)
a22e6cd3
AR
112 finalizeMsg("duplicate service URI", s->cfg().uri, false);
113 }
114}
62c7f90e 115
a22e6cd3
AR
116/// emits a formatted warning or error message at the appropriate dbg level
117void
118Adaptation::ServiceGroup::finalizeMsg(const char *msg, const String &culprit,
e1381638 119 bool error) const
62c7f90e 120{
e1381638 121 const int level = error ? DBG_CRITICAL :DBG_IMPORTANT;
a22e6cd3
AR
122 const char *pfx = error ? "ERROR: " : "WARNING: ";
123 debugs(93,level, pfx << msg << ' ' << culprit << " in " << kind << " '" <<
e1381638 124 id << "'");
a22e6cd3
AR
125}
126
e1381638
AJ
127Adaptation::ServicePointer Adaptation::ServiceGroup::at(const Pos pos) const
128{
a22e6cd3 129 return FindService(services[pos]);
62c7f90e
AR
130}
131
a22e6cd3
AR
132/// \todo: optimize to cut search short instead of looking for the best svc
133bool
134Adaptation::ServiceGroup::wants(const ServiceFilter &filter) const
62c7f90e 135{
a22e6cd3
AR
136 Pos pos = 0;
137 return findService(filter, pos);
62c7f90e
AR
138}
139
a22e6cd3
AR
140bool
141Adaptation::ServiceGroup::findService(const ServiceFilter &filter, Pos &pos) const
62c7f90e 142{
a22e6cd3
AR
143 if (method != filter.method || point != filter.point) {
144 debugs(93,5,HERE << id << " serves another location");
145 return false; // assume other services have the same wrong location
146 }
147
148 // find the next interested service, skipping problematic ones if possible
149 bool foundEssential = false;
150 Pos essPos = 0;
151 for (; has(pos); ++pos) {
152 debugs(93,9,HERE << id << " checks service at " << pos);
153 ServicePointer service = at(pos);
154
155 if (!service)
156 continue; // the service was lost due to reconfiguration
62c7f90e 157
a22e6cd3
AR
158 if (!service->wants(filter))
159 continue; // the service is not interested
160
161 if (service->up() || !service->probed()) {
162 debugs(93,9,HERE << id << " has matching service at " << pos);
163 return true;
164 }
165
166 if (service->cfg().bypass) { // we can safely ignore bypassable downers
167 debugs(93,9,HERE << id << " has bypassable service at " << pos);
168 continue;
169 }
170
171 if (!allServicesSame) { // cannot skip (i.e., find best) service
172 debugs(93,9,HERE << id << " has essential service at " << pos);
173 return true;
174 }
175
176 if (!foundEssential) {
177 debugs(93,9,HERE << id << " searches for best essential service from " << pos);
178 foundEssential = true;
179 essPos = pos;
180 }
62c7f90e 181 }
a22e6cd3
AR
182
183 if (foundEssential) {
184 debugs(93,9,HERE << id << " has best essential service at " << essPos);
185 pos = essPos;
186 return true;
187 }
188
189 debugs(93,5,HERE << id << " has no matching services");
190 return false;
191}
192
193bool
194Adaptation::ServiceGroup::findReplacement(const ServiceFilter &filter, Pos &pos) const
195{
196 return allServicesSame && findService(filter, pos);
197}
198
199bool
200Adaptation::ServiceGroup::findLink(const ServiceFilter &filter, Pos &pos) const
201{
202 return !allServicesSame && findService(filter, pos);
203}
204
a22e6cd3
AR
205/* ServiceSet */
206
207Adaptation::ServiceSet::ServiceSet(): ServiceGroup("adaptation set", true)
208{
62c7f90e 209}
62c7f90e 210
bed1aa51
AR
211/* SingleService */
212
213Adaptation::SingleService::SingleService(const String &aServiceId):
f53969cc 214 ServiceGroup("single-service group", false)
bed1aa51
AR
215{
216 id = aServiceId;
217 services.push_back(aServiceId);
218}
219
a22e6cd3
AR
220/* ServiceChain */
221
222Adaptation::ServiceChain::ServiceChain(): ServiceGroup("adaptation chain", false)
223{
224}
225
53340485 226/* DynamicServiceChain */
a22e6cd3 227
53340485
AR
228Adaptation::DynamicServiceChain::DynamicServiceChain(
229 const DynamicGroupCfg &cfg, const ServiceFilter &filter)
a22e6cd3
AR
230{
231 kind = "dynamic adaptation chain"; // TODO: optimize by using String const
53340485
AR
232 id = cfg.id; // use services ids as the dynamic group ID
233 services = cfg.services;
a22e6cd3
AR
234
235 // initialize cache to improve consistency checks in finalize()
53340485
AR
236 method = filter.method;
237 point = filter.point;
238
239 finalize(); // will report [dynamic] config errors
240}
a22e6cd3 241
53340485
AR
242void
243Adaptation::DynamicServiceChain::Split(const ServiceFilter &filter,
244 const String &ids, DynamicGroupCfg &current,
245 DynamicGroupCfg &future)
246{
247 // walk the list of services and split it into two parts:
248 // services that are applicable now and future services
249 bool doingCurrent = true;
a22e6cd3
AR
250 const char *item = NULL;
251 int ilen = 0;
252 const char *pos = NULL;
53340485
AR
253 while (strListGetItem(&ids, ',', &item, &ilen, &pos)) {
254 String id;
255 id.limitInit(item, ilen);
256 ServicePointer service = FindService(id);
257 if (doingCurrent) {
258 if (!service || // cannot tell or matches current location
259 (service->cfg().method == filter.method &&
260 service->cfg().point == filter.point)) {
261 current.add(id);
262 continue;
263 } else {
264 doingCurrent = false;
265 }
266 }
a22e6cd3 267
53340485
AR
268 if (!doingCurrent)
269 future.add(id);
270 }
a22e6cd3
AR
271}
272
273/* ServicePlan */
274
275Adaptation::ServicePlan::ServicePlan(): pos(0), atEof(true)
276{
277}
278
279Adaptation::ServicePlan::ServicePlan(const ServiceGroupPointer &g,
e1381638 280 const ServiceFilter &filter):
f53969cc 281 group(g), pos(0), atEof(!g || !g->has(pos))
a22e6cd3
AR
282{
283 // this will find the first service because starting pos is zero
284 if (!atEof && !group->findService(filter, pos))
285 atEof = true;
286}
287
288Adaptation::ServicePointer
289Adaptation::ServicePlan::current() const
bed1aa51 290{
a22e6cd3
AR
291 // may return NULL even if not atEof
292 return atEof ? Adaptation::ServicePointer() : group->at(pos);
293}
294
295Adaptation::ServicePointer
e1381638
AJ
296Adaptation::ServicePlan::replacement(const ServiceFilter &filter)
297{
a22e6cd3
AR
298 if (!atEof && !group->findReplacement(filter, ++pos))
299 atEof = true;
300 return current();
301}
302
303Adaptation::ServicePointer
e1381638
AJ
304Adaptation::ServicePlan::next(const ServiceFilter &filter)
305{
a22e6cd3
AR
306 if (!atEof && !group->findLink(filter, ++pos))
307 atEof = true;
308 return current();
309}
310
311std::ostream &
312Adaptation::ServicePlan::print(std::ostream &os) const
313{
314 if (!group)
315 return os << "[nil]";
316
317 return os << group->id << '[' << pos << ".." << group->services.size() <<
e1381638 318 (atEof ? ".]" : "]");
bed1aa51
AR
319}
320
bed1aa51
AR
321/* globals */
322
62c7f90e
AR
323Adaptation::Groups &
324Adaptation::AllGroups()
325{
28b58ffc
CT
326 static Groups *TheGroups = new Groups;
327 return *TheGroups;
62c7f90e
AR
328}
329
a22e6cd3 330Adaptation::ServiceGroupPointer
62c7f90e
AR
331Adaptation::FindGroup(const ServiceGroup::Id &id)
332{
333 typedef Groups::iterator GI;
334 for (GI i = AllGroups().begin(); i != AllGroups().end(); ++i) {
335 if ((*i)->id == id)
336 return *i;
337 }
338
339 return NULL;
340}
f53969cc 341