2 * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
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.
10 #include "adaptation/AccessRule.h"
11 #include "adaptation/Config.h"
12 #include "adaptation/DynamicGroupCfg.h"
13 #include "adaptation/Service.h"
14 #include "adaptation/ServiceFilter.h"
15 #include "adaptation/ServiceGroups.h"
16 #include "ConfigParser.h"
17 #include "debug/Stream.h"
21 Adaptation::ServiceGroup::ServiceGroup(const String
&aKind
, bool allSame
):
22 kind(aKind
), method(methodNone
), point(pointNone
),
23 allServicesSame(allSame
)
27 Adaptation::ServiceGroup::~ServiceGroup()
32 Adaptation::ServiceGroup::parse()
34 id
= ConfigParser::NextToken();
36 wordlist
*names
= nullptr;
37 ConfigParser::ParseWordList(&names
);
38 for (wordlist
*i
= names
; i
; i
= i
->next
)
39 services
.push_back(i
->key
);
40 wordlistDestroy(&names
);
43 // Note: configuration code aside, this method is called by DynamicServiceChain
45 Adaptation::ServiceGroup::finalize()
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
51 if (!removedServices
.empty()) {
53 for (Store::iterator it
= removedServices
.begin(); it
!= removedServices
.end(); ++it
) {
58 debugs(93, DBG_IMPORTANT
, "Adaptation group '" << id
<< "' contains disabled member(s) after reconfiguration: " << s
);
59 removedServices
.clear();
63 bool baselineBypass
= false;
64 for (Pos pos
= 0; has(pos
); ++pos
) {
65 // TODO: quit on all errors
66 const String
&serviceId
= services
[pos
];
67 ServicePointer service
= at(pos
);
68 if (service
!= nullptr) {
69 if (method
== methodNone
) {
70 // optimization: cache values that should be the same
71 method
= service
->cfg().method
;
72 point
= service
->cfg().point
;
74 if (method
!= service
->cfg().method
)
75 finalizeMsg("Inconsistent service method for", serviceId
, true);
76 if (point
!= service
->cfg().point
)
77 finalizeMsg("Inconsistent vectoring point for", serviceId
, true);
82 if (allServicesSame
) {
83 if (!baselineKey
.size()) {
84 baselineKey
= service
->cfg().key
;
85 baselineBypass
= service
->cfg().bypass
;
86 } else if (baselineBypass
!= service
->cfg().bypass
) {
87 debugs(93, DBG_CRITICAL
, "WARNING: Inconsistent bypass in " << kind
<<
88 ' ' << id
<< " may produce surprising results: " <<
89 baselineKey
<< " vs. " << serviceId
);
93 finalizeMsg("ERROR: Unknown adaptation name", serviceId
, true);
96 debugs(93,7, "finalized " << kind
<< ": " << id
);
99 /// checks that the service name or URI is not repeated later in the group
101 Adaptation::ServiceGroup::checkUniqueness(const Pos checkedPos
) const
103 ServicePointer checkedService
= at(checkedPos
);
104 if (!checkedService
) // should not happen but be robust
107 for (Pos p
= checkedPos
+ 1; has(p
); ++p
) {
108 ServicePointer s
= at(p
);
109 if (s
!= nullptr && s
->cfg().key
== checkedService
->cfg().key
)
110 finalizeMsg("duplicate service name", s
->cfg().key
, false);
111 else if (s
!= nullptr && s
->cfg().uri
== checkedService
->cfg().uri
)
112 finalizeMsg("duplicate service URI", s
->cfg().uri
, false);
116 /// emits a formatted warning or error message at the appropriate dbg level
118 Adaptation::ServiceGroup::finalizeMsg(const char *msg
, const String
&culprit
,
121 const int level
= error
? DBG_CRITICAL
:DBG_IMPORTANT
;
122 const char *pfx
= error
? "ERROR: " : "WARNING: ";
123 debugs(93,level
, pfx
<< msg
<< ' ' << culprit
<< " in " << kind
<< " '" <<
127 Adaptation::ServicePointer
Adaptation::ServiceGroup::at(const Pos pos
) const
129 return FindService(services
[pos
]);
132 // TODO: optimize to cut search short instead of looking for the best svc
134 Adaptation::ServiceGroup::wants(const ServiceFilter
&filter
) const
137 return findService(filter
, pos
);
141 Adaptation::ServiceGroup::findService(const ServiceFilter
&filter
, Pos
&pos
) const
143 if (method
!= filter
.method
|| point
!= filter
.point
) {
144 debugs(93,5, id
<< " serves another location");
145 return false; // assume other services have the same wrong location
148 // find the next interested service, skipping problematic ones if possible
149 bool foundEssential
= false;
151 for (; has(pos
); ++pos
) {
152 debugs(93,9, id
<< " checks service at " << pos
);
153 ServicePointer service
= at(pos
);
156 continue; // the service was lost due to reconfiguration
158 if (!service
->wants(filter
))
159 continue; // the service is not interested
161 if (service
->up() || !service
->probed()) {
162 debugs(93,9, id
<< " has matching service at " << pos
);
166 if (service
->cfg().bypass
) { // we can safely ignore bypassable downers
167 debugs(93,9, id
<< " has bypassable service at " << pos
);
171 if (!allServicesSame
) { // cannot skip (i.e., find best) service
172 debugs(93,9, id
<< " has essential service at " << pos
);
176 if (!foundEssential
) {
177 debugs(93,9, id
<< " searches for best essential service from " << pos
);
178 foundEssential
= true;
183 if (foundEssential
) {
184 debugs(93,9, id
<< " has best essential service at " << essPos
);
189 debugs(93,5, id
<< " has no matching services");
194 Adaptation::ServiceGroup::findReplacement(const ServiceFilter
&filter
, Pos
&pos
) const
196 return allServicesSame
&& findService(filter
, pos
);
200 Adaptation::ServiceGroup::findLink(const ServiceFilter
&filter
, Pos
&pos
) const
202 return !allServicesSame
&& findService(filter
, pos
);
207 Adaptation::ServiceSet::ServiceSet(): ServiceGroup("adaptation set", true)
213 Adaptation::SingleService::SingleService(const String
&aServiceId
):
214 ServiceGroup("single-service group", false)
217 services
.push_back(aServiceId
);
222 Adaptation::ServiceChain::ServiceChain(): ServiceGroup("adaptation chain", false)
226 /* DynamicServiceChain */
228 Adaptation::DynamicServiceChain::DynamicServiceChain(
229 const DynamicGroupCfg
&cfg
, const ServiceFilter
&filter
)
231 kind
= "dynamic adaptation chain"; // TODO: optimize by using String const
232 id
= cfg
.id
; // use services ids as the dynamic group ID
233 services
= cfg
.services
;
235 // initialize cache to improve consistency checks in finalize()
236 method
= filter
.method
;
237 point
= filter
.point
;
239 finalize(); // will report [dynamic] config errors
243 Adaptation::DynamicServiceChain::Split(const ServiceFilter
&filter
,
244 const String
&ids
, DynamicGroupCfg
¤t
,
245 DynamicGroupCfg
&future
)
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;
250 const char *item
= nullptr;
252 const char *pos
= nullptr;
253 while (strListGetItem(&ids
, ',', &item
, &ilen
, &pos
)) {
255 id
.assign(item
, ilen
);
256 ServicePointer service
= FindService(id
);
258 if (!service
|| // cannot tell or matches current location
259 (service
->cfg().method
== filter
.method
&&
260 service
->cfg().point
== filter
.point
)) {
264 doingCurrent
= false;
275 Adaptation::ServicePlan::ServicePlan(): pos(0), atEof(true)
279 Adaptation::ServicePlan::ServicePlan(const ServiceGroupPointer
&g
,
280 const ServiceFilter
&filter
):
281 group(g
), pos(0), atEof(!g
|| !g
->has(pos
))
283 // this will find the first service because starting pos is zero
284 if (!atEof
&& !group
->findService(filter
, pos
))
288 Adaptation::ServicePointer
289 Adaptation::ServicePlan::current() const
291 // may return NULL even if not atEof
292 return atEof
? Adaptation::ServicePointer() : group
->at(pos
);
295 Adaptation::ServicePointer
296 Adaptation::ServicePlan::replacement(const ServiceFilter
&filter
)
298 if (!atEof
&& !group
->findReplacement(filter
, ++pos
))
303 Adaptation::ServicePointer
304 Adaptation::ServicePlan::next(const ServiceFilter
&filter
)
306 if (!atEof
&& !group
->findLink(filter
, ++pos
))
312 Adaptation::ServicePlan::print(std::ostream
&os
) const
315 return os
<< "[nil]";
317 return os
<< group
->id
<< '[' << pos
<< ".." << group
->services
.size() <<
318 (atEof
? ".]" : "]");
324 Adaptation::AllGroups()
326 static Groups
*TheGroups
= new Groups
;
330 Adaptation::ServiceGroupPointer
331 Adaptation::FindGroup(const ServiceGroup::Id
&id
)
333 typedef Groups::iterator GI
;
334 for (GI i
= AllGroups().begin(); i
!= AllGroups().end(); ++i
) {