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