]> 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
3 #include "ConfigParser.h"
4 #include "Array.h" // really Vector
5 #include "adaptation/Config.h"
6 #include "adaptation/AccessRule.h"
7 #include "adaptation/Service.h"
8 #include "adaptation/ServiceFilter.h"
9 #include "adaptation/ServiceGroups.h"
10
11 #define ServiceGroup ServiceGroup
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
44 String baselineKey;
45 bool baselineBypass = false;
46 for (Pos pos = 0; has(pos); ++pos) {
47 // TODO: quit on all errors
48 const String &sid = services[pos];
49 ServicePointer service = at(pos);
50 if (service != NULL) {
51 if (method == methodNone) {
52 // optimization: cache values that should be the same
53 method = service->cfg().method;
54 point = service->cfg().point;
55 } else {
56 if (method != service->cfg().method)
57 finalizeMsg("Inconsistent service method for", sid, true);
58 if (point != service->cfg().point)
59 finalizeMsg("Inconsistent vectoring point for", sid, true);
60 }
61
62 checkUniqueness(pos);
63
64 if (allServicesSame) {
65 if (!baselineKey.size()) {
66 baselineKey = service->cfg().key;
67 baselineBypass = service->cfg().bypass;
68 } else if (baselineBypass != service->cfg().bypass) {
69 debugs(93,0, "WARNING: Inconsistent bypass in " << kind <<
70 ' ' << id << " may produce surprising results: " <<
71 baselineKey << " vs. " << sid);
72 }
73 }
74 } else {
75 finalizeMsg("ERROR: Unknown adaptation name", sid, true);
76 }
77 }
78 debugs(93,7, HERE << "finalized " << kind << ": " << id);
79 }
80
81 /// checks that the service name or URI is not repeated later in the group
82 void
83 Adaptation::ServiceGroup::checkUniqueness(const Pos checkedPos) const
84 {
85 ServicePointer checkedService = at(checkedPos);
86 if (!checkedService) // should not happen but be robust
87 return;
88
89 for (Pos p = checkedPos + 1; has(p); ++p) {
90 ServicePointer s = at(p);
91 if (s != NULL && s->cfg().key == checkedService->cfg().key)
92 finalizeMsg("duplicate service name", s->cfg().key, false);
93 else if (s != NULL && s->cfg().uri == checkedService->cfg().uri)
94 finalizeMsg("duplicate service URI", s->cfg().uri, false);
95 }
96 }
97
98 /// emits a formatted warning or error message at the appropriate dbg level
99 void
100 Adaptation::ServiceGroup::finalizeMsg(const char *msg, const String &culprit,
101 bool error) const
102 {
103 const int level = error ? DBG_CRITICAL :DBG_IMPORTANT;
104 const char *pfx = error ? "ERROR: " : "WARNING: ";
105 debugs(93,level, pfx << msg << ' ' << culprit << " in " << kind << " '" <<
106 id << "'");
107 }
108
109 Adaptation::ServicePointer Adaptation::ServiceGroup::at(const Pos pos) const
110 {
111 return FindService(services[pos]);
112 }
113
114 /// \todo: optimize to cut search short instead of looking for the best svc
115 bool
116 Adaptation::ServiceGroup::wants(const ServiceFilter &filter) const
117 {
118 Pos pos = 0;
119 return findService(filter, pos);
120 }
121
122 bool
123 Adaptation::ServiceGroup::findService(const ServiceFilter &filter, Pos &pos) const
124 {
125 if (method != filter.method || point != filter.point) {
126 debugs(93,5,HERE << id << " serves another location");
127 return false; // assume other services have the same wrong location
128 }
129
130 // find the next interested service, skipping problematic ones if possible
131 bool foundEssential = false;
132 Pos essPos = 0;
133 for (; has(pos); ++pos) {
134 debugs(93,9,HERE << id << " checks service at " << pos);
135 ServicePointer service = at(pos);
136
137 if (!service)
138 continue; // the service was lost due to reconfiguration
139
140 if (!service->wants(filter))
141 continue; // the service is not interested
142
143 if (service->up() || !service->probed()) {
144 debugs(93,9,HERE << id << " has matching service at " << pos);
145 return true;
146 }
147
148 if (service->cfg().bypass) { // we can safely ignore bypassable downers
149 debugs(93,9,HERE << id << " has bypassable service at " << pos);
150 continue;
151 }
152
153 if (!allServicesSame) { // cannot skip (i.e., find best) service
154 debugs(93,9,HERE << id << " has essential service at " << pos);
155 return true;
156 }
157
158 if (!foundEssential) {
159 debugs(93,9,HERE << id << " searches for best essential service from " << pos);
160 foundEssential = true;
161 essPos = pos;
162 }
163 }
164
165 if (foundEssential) {
166 debugs(93,9,HERE << id << " has best essential service at " << essPos);
167 pos = essPos;
168 return true;
169 }
170
171 debugs(93,5,HERE << id << " has no matching services");
172 return false;
173 }
174
175 bool
176 Adaptation::ServiceGroup::findReplacement(const ServiceFilter &filter, Pos &pos) const
177 {
178 return allServicesSame && findService(filter, pos);
179 }
180
181 bool
182 Adaptation::ServiceGroup::findLink(const ServiceFilter &filter, Pos &pos) const
183 {
184 return !allServicesSame && findService(filter, pos);
185 }
186
187
188 /* ServiceSet */
189
190 Adaptation::ServiceSet::ServiceSet(): ServiceGroup("adaptation set", true)
191 {
192 }
193
194
195 /* SingleService */
196
197 Adaptation::SingleService::SingleService(const String &aServiceId):
198 ServiceGroup("single-service group", false)
199 {
200 id = aServiceId;
201 services.push_back(aServiceId);
202 }
203
204
205 /* ServiceChain */
206
207 Adaptation::ServiceChain::ServiceChain(): ServiceGroup("adaptation chain", false)
208 {
209 }
210
211
212 /* ServiceChain */
213
214 Adaptation::DynamicServiceChain::DynamicServiceChain(const String &ids,
215 const ServiceGroupPointer prev)
216 {
217 kind = "dynamic adaptation chain"; // TODO: optimize by using String const
218 id = ids; // use services ids as the dynamic group ID
219
220 // initialize cache to improve consistency checks in finalize()
221 if (prev != NULL) {
222 method = prev->method;
223 point = prev->point;
224 }
225
226 // populate services storage with supplied service ids
227 const char *item = NULL;
228 int ilen = 0;
229 const char *pos = NULL;
230 while (strListGetItem(&ids, ',', &item, &ilen, &pos))
231 services.push_back(item);
232
233 finalize(); // will report [dynamic] config errors
234 }
235
236 /* ServicePlan */
237
238 Adaptation::ServicePlan::ServicePlan(): pos(0), atEof(true)
239 {
240 }
241
242 Adaptation::ServicePlan::ServicePlan(const ServiceGroupPointer &g,
243 const ServiceFilter &filter):
244 group(g), pos(0), atEof(!g || !g->has(pos))
245 {
246 // this will find the first service because starting pos is zero
247 if (!atEof && !group->findService(filter, pos))
248 atEof = true;
249 }
250
251 Adaptation::ServicePointer
252 Adaptation::ServicePlan::current() const
253 {
254 // may return NULL even if not atEof
255 return atEof ? Adaptation::ServicePointer() : group->at(pos);
256 }
257
258 Adaptation::ServicePointer
259 Adaptation::ServicePlan::replacement(const ServiceFilter &filter)
260 {
261 if (!atEof && !group->findReplacement(filter, ++pos))
262 atEof = true;
263 return current();
264 }
265
266 Adaptation::ServicePointer
267 Adaptation::ServicePlan::next(const ServiceFilter &filter)
268 {
269 if (!atEof && !group->findLink(filter, ++pos))
270 atEof = true;
271 return current();
272 }
273
274 std::ostream &
275 Adaptation::ServicePlan::print(std::ostream &os) const
276 {
277 if (!group)
278 return os << "[nil]";
279
280 return os << group->id << '[' << pos << ".." << group->services.size() <<
281 (atEof ? ".]" : "]");
282 }
283
284
285 /* globals */
286
287 Adaptation::Groups &
288 Adaptation::AllGroups()
289 {
290 static Groups TheGroups;
291 return TheGroups;
292 }
293
294 Adaptation::ServiceGroupPointer
295 Adaptation::FindGroup(const ServiceGroup::Id &id)
296 {
297 typedef Groups::iterator GI;
298 for (GI i = AllGroups().begin(); i != AllGroups().end(); ++i) {
299 if ((*i)->id == id)
300 return *i;
301 }
302
303 return NULL;
304 }