ifurlup("https://example.com/", { {"192.0.2.20", "203.0.113.4"}, {"203.0.113.2"} })
+.. function:: ifurlextup(groups-of-address-url-pairs[, options])
+
+ Very similar to ``ifurlup``, but the returned IPs are decoupled from their external health check URLs.
+ This is useful when health checking already happens elsewhere, and that state is exposed over HTTP(S).
+ Health checks are considered positive if the HTTP response code is 200 and optionally if the content matches the ``stringmatch`` option.
+
+ Options are identical to those for ``ifurlup``.
+
+ Example:
+
+ .. code-block:: lua
+
+ ifurlextup({{['192.168.0.1']='https://example.com/',['192.168.0.2']='https://example.com/404'}})
+
+ Example with two groups:
+
+ .. code-block:: lua
+
+ ifurlextup({{['192.168.0.1']='https://example.net/404',['192.168.0.2']='https://example.com/404'}, {['192.168.0.3']='https://example.net/'}})"
+
+ The health checker will look up the first two URLs (using normal DNS resolution to find them - whenever possible, use URLs with IPs in them).
+ The 404s will cause the first group of IPs to get marked as down, after which the URL in the second group is tested.
+ The third IP will get marked up assuming ``https://example.net/`` responds with HTTP response code 200.
+
.. function:: pickrandom(addresses)
Returns a random IP address from the list supplied.
private:
void checkURL(const CheckDesc& cd, const bool status, const bool first = false)
{
+ string remstring;
try {
int timeout = 2;
if (cd.opts.count("timeout")) {
MiniCurl mc(useragent);
string content;
+ const ComboAddress* rem = nullptr;
+ if(cd.rem.sin4.sin_family != AF_UNSPEC) {
+ rem = &cd.rem;
+ remstring = rem->toString();
+ } else {
+ remstring = "[externally checked IP]";
+ }
+
if (cd.opts.count("source")) {
ComboAddress src(cd.opts.at("source"));
- content=mc.getURL(cd.url, &cd.rem, &src, timeout);
+ content=mc.getURL(cd.url, rem, &src, timeout);
}
else {
- content=mc.getURL(cd.url, &cd.rem, nullptr, timeout);
+ content=mc.getURL(cd.url, rem, nullptr, timeout);
}
if (cd.opts.count("stringmatch") && content.find(cd.opts.at("stringmatch")) == string::npos) {
throw std::runtime_error(boost::str(boost::format("unable to match content with `%s`") % cd.opts.at("stringmatch")));
}
+
if(!status) {
- g_log<<Logger::Info<<"LUA record monitoring declaring "<<cd.rem.toString()<<" UP for URL "<<cd.url<<"!"<<endl;
+ g_log<<Logger::Info<<"LUA record monitoring declaring "<<remstring<<" UP for URL "<<cd.url<<"!"<<endl;
}
setUp(cd);
}
catch(std::exception& ne) {
if(status || first)
- g_log<<Logger::Info<<"LUA record monitoring declaring "<<cd.rem.toString()<<" DOWN for URL "<<cd.url<<", error: "<<ne.what()<<endl;
+ g_log<<Logger::Info<<"LUA record monitoring declaring "<<remstring<<" DOWN for URL "<<cd.url<<", error: "<<ne.what()<<endl;
setDown(cd);
}
}
return convIpListToString(res);
});
+ lua.writeFunction("ifurlextup", [](const vector<pair<int, opts_t> >& ipurls, boost::optional<opts_t> options) {
+ vector<ComboAddress> candidates;
+ opts_t opts;
+ if(options)
+ opts = *options;
+
+ ComboAddress ca_unspec;
+ ca_unspec.sin4.sin_family=AF_UNSPEC;
+
+ // ipurls: { { ["192.0.2.1"] = "https://example.com", ["192.0.2.2"] = "https://example.com/404" } }
+ for (const auto& [count, unitmap] : ipurls) {
+ // unitmap: 1 = { ["192.0.2.1"] = "https://example.com", ["192.0.2.2"] = "https://example.com/404" }
+ vector<ComboAddress> available;
+
+ for (const auto& [ipStr, url] : unitmap) {
+ // unit: ["192.0.2.1"] = "https://example.com"
+ ComboAddress ip(ipStr);
+ candidates.push_back(ip);
+ if (g_up.isUp(ca_unspec, url, opts)) {
+ available.push_back(ip);
+ }
+ }
+ if(!available.empty()) {
+ vector<ComboAddress> res = useSelector(getOptionValue(options, "selector", "random"), s_lua_record_ctx->bestwho, available);
+ return convIpListToString(res);
+ }
+ }
+
+ // All units down, apply backupSelector on all candidates
+ vector<ComboAddress> res = useSelector(getOptionValue(options, "backupSelector", "random"), s_lua_record_ctx->bestwho, candidates);
+ return convIpListToString(res);
+ });
+
lua.writeFunction("ifurlup", [](const std::string& url,
const boost::variant<iplist_t, ipunitlist_t>& ips,
boost::optional<opts_t> options) {
from http.server import BaseHTTPRequestHandler, HTTPServer
class FakeHTTPServer(BaseHTTPRequestHandler):
- def _set_headers(self):
- self.send_response(200)
+ def _set_headers(self, response_code=200):
+ self.send_response(response_code)
self.send_header('Content-type', 'text/html')
self.end_headers()
def do_GET(self):
+ if self.path == '/404':
+ self._set_headers(404)
+ self.wfile.write(bytes('this page does not exist', 'utf-8'))
+ return
+
self._set_headers()
- if (self.path == '/ping.json'):
+ if self.path == '/ping.json':
self.wfile.write(bytes('{"ping":"pong"}', 'utf-8'))
else:
self.wfile.write(bytes("<html><body><h1>hi!</h1><h2>Programming in Lua !</h2></body></html>", "utf-8"))
"return ifurlup('http://www.lua.org:8080/', "
"{{EUWips, EUEips, USAips}}, settings) ")
+ifurlextup IN LUA A "ifurlextup({{{{['192.168.0.1']='http://{prefix}.101:8080/404',['192.168.0.2']='http://{prefix}.102:8080/404'}}, {{['192.168.0.3']='http://{prefix}.101:8080/'}}}})"
+
nl IN LUA A ( ";include('config') "
"return ifportup(8081, NLips) ")
latlon.geo IN LUA TXT "latlon()"
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnyRRsetInAnswer(res, reachable_rrs)
+ def testIfurlextup(self):
+ expected = [dns.rrset.from_text('ifurlextup.example.org.', 0, dns.rdataclass.IN, dns.rdatatype.A, '192.168.0.3')]
+
+ query = dns.message.make_query('ifurlextup.example.org', 'A')
+ res = self.sendUDPQuery(query)
+
+ # wait for health checks to happen
+ time.sleep(5)
+
+ res = self.sendUDPQuery(query)
+
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertEqual(res.answer, expected)
+
def testIfurlupSimplified(self):
"""
Basic ifurlup() test with the simplified list of ips