]>
Commit | Line | Data |
---|---|---|
bbc27441 AJ |
1 | /* |
2 | * Copyright (C) 1996-2014 The Squid Software Foundation and contributors | |
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 | 21 | Adaptation::ServiceGroup::ServiceGroup(const String &aKind, bool allSame): |
e1381638 AJ |
22 | kind(aKind), method(methodNone), point(pointNone), |
23 | allServicesSame(allSame) | |
62c7f90e AR |
24 | { |
25 | } | |
26 | ||
27 | Adaptation::ServiceGroup::~ServiceGroup() | |
28 | { | |
29 | } | |
30 | ||
31 | void | |
32 | Adaptation::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 |
44 | void |
45 | Adaptation::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 |
100 | void | |
101 | Adaptation::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 |
117 | void | |
118 | Adaptation::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 |
127 | Adaptation::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 |
133 | bool | |
134 | Adaptation::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 |
140 | bool |
141 | Adaptation::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 | ||
193 | bool | |
194 | Adaptation::ServiceGroup::findReplacement(const ServiceFilter &filter, Pos &pos) const | |
195 | { | |
196 | return allServicesSame && findService(filter, pos); | |
197 | } | |
198 | ||
199 | bool | |
200 | Adaptation::ServiceGroup::findLink(const ServiceFilter &filter, Pos &pos) const | |
201 | { | |
202 | return !allServicesSame && findService(filter, pos); | |
203 | } | |
204 | ||
a22e6cd3 AR |
205 | /* ServiceSet */ |
206 | ||
207 | Adaptation::ServiceSet::ServiceSet(): ServiceGroup("adaptation set", true) | |
208 | { | |
62c7f90e | 209 | } |
62c7f90e | 210 | |
bed1aa51 AR |
211 | /* SingleService */ |
212 | ||
213 | Adaptation::SingleService::SingleService(const String &aServiceId): | |
a22e6cd3 | 214 | ServiceGroup("single-service group", false) |
bed1aa51 AR |
215 | { |
216 | id = aServiceId; | |
217 | services.push_back(aServiceId); | |
218 | } | |
219 | ||
a22e6cd3 AR |
220 | /* ServiceChain */ |
221 | ||
222 | Adaptation::ServiceChain::ServiceChain(): ServiceGroup("adaptation chain", false) | |
223 | { | |
224 | } | |
225 | ||
53340485 | 226 | /* DynamicServiceChain */ |
a22e6cd3 | 227 | |
53340485 AR |
228 | Adaptation::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 |
242 | void |
243 | Adaptation::DynamicServiceChain::Split(const ServiceFilter &filter, | |
244 | const String &ids, DynamicGroupCfg ¤t, | |
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 | ||
275 | Adaptation::ServicePlan::ServicePlan(): pos(0), atEof(true) | |
276 | { | |
277 | } | |
278 | ||
279 | Adaptation::ServicePlan::ServicePlan(const ServiceGroupPointer &g, | |
e1381638 AJ |
280 | const ServiceFilter &filter): |
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 | ||
288 | Adaptation::ServicePointer | |
289 | Adaptation::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 | ||
295 | Adaptation::ServicePointer | |
e1381638 AJ |
296 | Adaptation::ServicePlan::replacement(const ServiceFilter &filter) |
297 | { | |
a22e6cd3 AR |
298 | if (!atEof && !group->findReplacement(filter, ++pos)) |
299 | atEof = true; | |
300 | return current(); | |
301 | } | |
302 | ||
303 | Adaptation::ServicePointer | |
e1381638 AJ |
304 | Adaptation::ServicePlan::next(const ServiceFilter &filter) |
305 | { | |
a22e6cd3 AR |
306 | if (!atEof && !group->findLink(filter, ++pos)) |
307 | atEof = true; | |
308 | return current(); | |
309 | } | |
310 | ||
311 | std::ostream & | |
312 | Adaptation::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 |
323 | Adaptation::Groups & |
324 | Adaptation::AllGroups() | |
325 | { | |
28b58ffc CT |
326 | static Groups *TheGroups = new Groups; |
327 | return *TheGroups; | |
62c7f90e AR |
328 | } |
329 | ||
a22e6cd3 | 330 | Adaptation::ServiceGroupPointer |
62c7f90e AR |
331 | Adaptation::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 | } |