]>
Commit | Line | Data |
---|---|---|
62c7f90e AR |
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" | |
a22e6cd3 | 8 | #include "adaptation/ServiceFilter.h" |
62c7f90e AR |
9 | #include "adaptation/ServiceGroups.h" |
10 | ||
a22e6cd3 | 11 | #define ServiceGroup ServiceGroup |
62c7f90e | 12 | |
a22e6cd3 | 13 | Adaptation::ServiceGroup::ServiceGroup(const String &aKind, bool allSame): |
e1381638 AJ |
14 | kind(aKind), method(methodNone), point(pointNone), |
15 | allServicesSame(allSame) | |
62c7f90e AR |
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 | ||
a22e6cd3 | 35 | // Note: configuration code aside, this method is called by DynamicServiceChain |
62c7f90e AR |
36 | void |
37 | Adaptation::ServiceGroup::finalize() | |
38 | { | |
a22e6cd3 AR |
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) { | |
e1381638 | 51 | if (method == methodNone) { |
a22e6cd3 AR |
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 | ||
e1381638 | 64 | if (allServicesSame) { |
a22e6cd3 AR |
65 | if (!baselineKey.size()) { |
66 | baselineKey = service->cfg().key; | |
67 | baselineBypass = service->cfg().bypass; | |
e1381638 | 68 | } else if (baselineBypass != service->cfg().bypass) { |
a22e6cd3 | 69 | debugs(93,0, "WARNING: Inconsistent bypass in " << kind << |
e1381638 AJ |
70 | ' ' << id << " may produce surprising results: " << |
71 | baselineKey << " vs. " << sid); | |
a22e6cd3 AR |
72 | } |
73 | } | |
e1381638 | 74 | } else { |
a22e6cd3 AR |
75 | finalizeMsg("ERROR: Unknown adaptation name", sid, true); |
76 | } | |
26ac0430 | 77 | } |
bed1aa51 | 78 | debugs(93,7, HERE << "finalized " << kind << ": " << id); |
62c7f90e AR |
79 | } |
80 | ||
a22e6cd3 AR |
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); | |
e1381638 | 93 | else if (s != NULL && s->cfg().uri == checkedService->cfg().uri) |
a22e6cd3 AR |
94 | finalizeMsg("duplicate service URI", s->cfg().uri, false); |
95 | } | |
96 | } | |
62c7f90e | 97 | |
a22e6cd3 AR |
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, | |
e1381638 | 101 | bool error) const |
62c7f90e | 102 | { |
e1381638 | 103 | const int level = error ? DBG_CRITICAL :DBG_IMPORTANT; |
a22e6cd3 AR |
104 | const char *pfx = error ? "ERROR: " : "WARNING: "; |
105 | debugs(93,level, pfx << msg << ' ' << culprit << " in " << kind << " '" << | |
e1381638 | 106 | id << "'"); |
a22e6cd3 AR |
107 | } |
108 | ||
e1381638 AJ |
109 | Adaptation::ServicePointer Adaptation::ServiceGroup::at(const Pos pos) const |
110 | { | |
a22e6cd3 | 111 | return FindService(services[pos]); |
62c7f90e AR |
112 | } |
113 | ||
a22e6cd3 AR |
114 | /// \todo: optimize to cut search short instead of looking for the best svc |
115 | bool | |
116 | Adaptation::ServiceGroup::wants(const ServiceFilter &filter) const | |
62c7f90e | 117 | { |
a22e6cd3 AR |
118 | Pos pos = 0; |
119 | return findService(filter, pos); | |
62c7f90e AR |
120 | } |
121 | ||
a22e6cd3 AR |
122 | bool |
123 | Adaptation::ServiceGroup::findService(const ServiceFilter &filter, Pos &pos) const | |
62c7f90e | 124 | { |
a22e6cd3 AR |
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 | |
62c7f90e | 139 | |
a22e6cd3 AR |
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 | } | |
62c7f90e | 163 | } |
a22e6cd3 AR |
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 | { | |
62c7f90e | 192 | } |
62c7f90e | 193 | |
bed1aa51 AR |
194 | |
195 | /* SingleService */ | |
196 | ||
197 | Adaptation::SingleService::SingleService(const String &aServiceId): | |
a22e6cd3 | 198 | ServiceGroup("single-service group", false) |
bed1aa51 AR |
199 | { |
200 | id = aServiceId; | |
201 | services.push_back(aServiceId); | |
202 | } | |
203 | ||
a22e6cd3 AR |
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, | |
e1381638 | 215 | const ServiceGroupPointer prev) |
a22e6cd3 AR |
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, | |
e1381638 AJ |
243 | const ServiceFilter &filter): |
244 | group(g), pos(0), atEof(!g || !g->has(pos)) | |
a22e6cd3 AR |
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 | |
bed1aa51 | 253 | { |
a22e6cd3 AR |
254 | // may return NULL even if not atEof |
255 | return atEof ? Adaptation::ServicePointer() : group->at(pos); | |
256 | } | |
257 | ||
258 | Adaptation::ServicePointer | |
e1381638 AJ |
259 | Adaptation::ServicePlan::replacement(const ServiceFilter &filter) |
260 | { | |
a22e6cd3 AR |
261 | if (!atEof && !group->findReplacement(filter, ++pos)) |
262 | atEof = true; | |
263 | return current(); | |
264 | } | |
265 | ||
266 | Adaptation::ServicePointer | |
e1381638 AJ |
267 | Adaptation::ServicePlan::next(const ServiceFilter &filter) |
268 | { | |
a22e6cd3 AR |
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() << | |
e1381638 | 281 | (atEof ? ".]" : "]"); |
bed1aa51 AR |
282 | } |
283 | ||
284 | ||
285 | /* globals */ | |
286 | ||
62c7f90e AR |
287 | Adaptation::Groups & |
288 | Adaptation::AllGroups() | |
289 | { | |
290 | static Groups TheGroups; | |
291 | return TheGroups; | |
292 | } | |
293 | ||
a22e6cd3 | 294 | Adaptation::ServiceGroupPointer |
62c7f90e AR |
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 | } |