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"
13 Adaptation::ServiceGroup::ServiceGroup(const String
&aKind
, bool allSame
):
14 kind(aKind
), method(methodNone
), point(pointNone
),
15 allServicesSame(allSame
)
19 Adaptation::ServiceGroup::~ServiceGroup()
24 Adaptation::ServiceGroup::parse()
26 ConfigParser::ParseString(&id
);
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
);
35 // Note: configuration code aside, this method is called by DynamicServiceChain
37 Adaptation::ServiceGroup::finalize()
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()) {
45 for (Store::iterator it
= removedServices
.begin(); it
!= removedServices
.end(); ++it
) {
50 debugs(93, DBG_IMPORTANT
, "Adaptation group '" << id
<< "' contains disabled member(s) after reconfiguration: " << s
);
51 removedServices
.clean();
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
;
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);
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
);
85 finalizeMsg("ERROR: Unknown adaptation name", serviceId
, true);
88 debugs(93,7, HERE
<< "finalized " << kind
<< ": " << id
);
91 /// checks that the service name or URI is not repeated later in the group
93 Adaptation::ServiceGroup::checkUniqueness(const Pos checkedPos
) const
95 ServicePointer checkedService
= at(checkedPos
);
96 if (!checkedService
) // should not happen but be robust
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);
108 /// emits a formatted warning or error message at the appropriate dbg level
110 Adaptation::ServiceGroup::finalizeMsg(const char *msg
, const String
&culprit
,
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
<< " '" <<
119 Adaptation::ServicePointer
Adaptation::ServiceGroup::at(const Pos pos
) const
121 return FindService(services
[pos
]);
124 /// \todo: optimize to cut search short instead of looking for the best svc
126 Adaptation::ServiceGroup::wants(const ServiceFilter
&filter
) const
129 return findService(filter
, pos
);
133 Adaptation::ServiceGroup::findService(const ServiceFilter
&filter
, Pos
&pos
) const
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
140 // find the next interested service, skipping problematic ones if possible
141 bool foundEssential
= false;
143 for (; has(pos
); ++pos
) {
144 debugs(93,9,HERE
<< id
<< " checks service at " << pos
);
145 ServicePointer service
= at(pos
);
148 continue; // the service was lost due to reconfiguration
150 if (!service
->wants(filter
))
151 continue; // the service is not interested
153 if (service
->up() || !service
->probed()) {
154 debugs(93,9,HERE
<< id
<< " has matching service at " << pos
);
158 if (service
->cfg().bypass
) { // we can safely ignore bypassable downers
159 debugs(93,9,HERE
<< id
<< " has bypassable service at " << pos
);
163 if (!allServicesSame
) { // cannot skip (i.e., find best) service
164 debugs(93,9,HERE
<< id
<< " has essential service at " << pos
);
168 if (!foundEssential
) {
169 debugs(93,9,HERE
<< id
<< " searches for best essential service from " << pos
);
170 foundEssential
= true;
175 if (foundEssential
) {
176 debugs(93,9,HERE
<< id
<< " has best essential service at " << essPos
);
181 debugs(93,5,HERE
<< id
<< " has no matching services");
186 Adaptation::ServiceGroup::findReplacement(const ServiceFilter
&filter
, Pos
&pos
) const
188 return allServicesSame
&& findService(filter
, pos
);
192 Adaptation::ServiceGroup::findLink(const ServiceFilter
&filter
, Pos
&pos
) const
194 return !allServicesSame
&& findService(filter
, pos
);
199 Adaptation::ServiceSet::ServiceSet(): ServiceGroup("adaptation set", true)
205 Adaptation::SingleService::SingleService(const String
&aServiceId
):
206 ServiceGroup("single-service group", false)
209 services
.push_back(aServiceId
);
214 Adaptation::ServiceChain::ServiceChain(): ServiceGroup("adaptation chain", false)
218 /* DynamicServiceChain */
220 Adaptation::DynamicServiceChain::DynamicServiceChain(
221 const DynamicGroupCfg
&cfg
, const ServiceFilter
&filter
)
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
;
227 // initialize cache to improve consistency checks in finalize()
228 method
= filter
.method
;
229 point
= filter
.point
;
231 finalize(); // will report [dynamic] config errors
235 Adaptation::DynamicServiceChain::Split(const ServiceFilter
&filter
,
236 const String
&ids
, DynamicGroupCfg
¤t
,
237 DynamicGroupCfg
&future
)
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
;
244 const char *pos
= NULL
;
245 while (strListGetItem(&ids
, ',', &item
, &ilen
, &pos
)) {
247 id
.limitInit(item
, ilen
);
248 ServicePointer service
= FindService(id
);
250 if (!service
|| // cannot tell or matches current location
251 (service
->cfg().method
== filter
.method
&&
252 service
->cfg().point
== filter
.point
)) {
256 doingCurrent
= false;
267 Adaptation::ServicePlan::ServicePlan(): pos(0), atEof(true)
271 Adaptation::ServicePlan::ServicePlan(const ServiceGroupPointer
&g
,
272 const ServiceFilter
&filter
):
273 group(g
), pos(0), atEof(!g
|| !g
->has(pos
))
275 // this will find the first service because starting pos is zero
276 if (!atEof
&& !group
->findService(filter
, pos
))
280 Adaptation::ServicePointer
281 Adaptation::ServicePlan::current() const
283 // may return NULL even if not atEof
284 return atEof
? Adaptation::ServicePointer() : group
->at(pos
);
287 Adaptation::ServicePointer
288 Adaptation::ServicePlan::replacement(const ServiceFilter
&filter
)
290 if (!atEof
&& !group
->findReplacement(filter
, ++pos
))
295 Adaptation::ServicePointer
296 Adaptation::ServicePlan::next(const ServiceFilter
&filter
)
298 if (!atEof
&& !group
->findLink(filter
, ++pos
))
304 Adaptation::ServicePlan::print(std::ostream
&os
) const
307 return os
<< "[nil]";
309 return os
<< group
->id
<< '[' << pos
<< ".." << group
->services
.size() <<
310 (atEof
? ".]" : "]");
316 Adaptation::AllGroups()
318 static Groups TheGroups
;
322 Adaptation::ServiceGroupPointer
323 Adaptation::FindGroup(const ServiceGroup::Id
&id
)
325 typedef Groups::iterator GI
;
326 for (GI i
= AllGroups().begin(); i
!= AllGroups().end(); ++i
) {