[prev in list] [next in list] [prev in thread] [next in thread] 

List:       varnish-dev
Subject:    Re: Test suite for dynamic backends
From:       Dridi Boukelmoune <dridi () varni ! sh>
Date:       2015-07-14 7:34:15
Message-ID: CABoVN9AaUWoNcL4E1Jw=0NvS-P0-=Ds61d52ZXomYuZ+Xrv5eA () mail ! gmail ! com
[Download RAW message or body]

On Sun, Jul 12, 2015 at 12:09 PM, Poul-Henning Kamp <phk@phk.freebsd.dk> wrote:
>>If you look at the DNS director patch, you'll see extra care in not
>>removing backends needlessly.
>
> I havn't looked at that patch yet.

Forgot to update my DNS director draft. I also realized thanks to
telnet that my ISP doesn't seem to support IPv6 (Network is
unreachable), but loopback tests went well.

Cheers,
Dridi

["0001-Resurrect-VSA_Len.patch" (text/x-patch)]

From 32f7ad806267ea3ecb65c8c2cc95bc9bbd102cd6 Mon Sep 17 00:00:00 2001
From: Dridi Boukelmoune <dridi.boukelmoune@gmail.com>
Date: Tue, 7 Jul 2015 22:31:50 +0200
Subject: [PATCH 1/2] Resurrect VSA_Len

Partial revert of 0848a1fe471c40a49e77df941c8e2cc133170ff7.
---
 include/vsa.h        |  1 +
 lib/libvarnish/vsa.c | 15 +++++++++++++++
 2 files changed, 16 insertions(+)

diff --git a/include/vsa.h b/include/vsa.h
index 185be03..996afde 100644
--- a/include/vsa.h
+++ b/include/vsa.h
@@ -34,6 +34,7 @@ struct suckaddr;
 extern const int vsa_suckaddr_len;
 
 int VSA_Sane(const struct suckaddr *);
+socklen_t VSA_Len(const struct suckaddr *);
 unsigned VSA_Port(const struct suckaddr *);
 int VSA_Compare(const struct suckaddr *, const struct suckaddr *);
 struct suckaddr *VSA_Clone(const struct suckaddr *sua);
diff --git a/lib/libvarnish/vsa.c b/lib/libvarnish/vsa.c
index 43d9a86..fc25cdd 100644
--- a/lib/libvarnish/vsa.c
+++ b/lib/libvarnish/vsa.c
@@ -306,6 +306,21 @@ VSA_Sane(const struct suckaddr *sua)
 	}
 }
 
+socklen_t
+VSA_Len(const struct suckaddr *sua)
+{
+       CHECK_OBJ_NOTNULL(sua, SUCKADDR_MAGIC);
+
+       switch(sua->sa.sa_family) {
+               case PF_INET:
+                       return (sizeof(sua->sa4));
+               case PF_INET6:
+                       return (sizeof(sua->sa6));
+               default:
+                       return (0);
+       }
+}
+
 int
 VSA_Compare(const struct suckaddr *sua1, const struct suckaddr *sua2)
 {
-- 
2.1.0


["0002-Resurrect-the-DNS-director.patch" (text/x-patch)]

From c489a4903e8f1ec5b7f020a7ff94d1e971812c76 Mon Sep 17 00:00:00 2001
From: Dridi Boukelmoune <dridi.boukelmoune@gmail.com>
Date: Wed, 8 Jul 2015 19:49:14 +0200
Subject: [PATCH 2/2] Resurrect the DNS director

---
 lib/libvmod_directors/Makefile.am |   3 +-
 lib/libvmod_directors/dns.c       | 393 ++++++++++++++++++++++++++++++++++++++
 lib/libvmod_directors/vmod.vcc    |  43 +++++
 3 files changed, 438 insertions(+), 1 deletion(-)
 create mode 100644 lib/libvmod_directors/dns.c

diff --git a/lib/libvmod_directors/Makefile.am b/lib/libvmod_directors/Makefile.am
index 64b72a8..bc0261f 100644
--- a/lib/libvmod_directors/Makefile.am
+++ b/lib/libvmod_directors/Makefile.am
@@ -17,6 +17,7 @@ libvmod_directors_la_LDFLAGS = $(AM_LDFLAGS) -module -export-dynamic -avoid-vers
 libvmod_directors_la_SOURCES = \
 	vdir.c \
 	vdir.h \
+	dns.c \
 	fall_back.c \
 	hash.c \
 	random.c \
@@ -27,7 +28,7 @@ nodist_libvmod_directors_la_SOURCES = \
 	vcc_if.h
 
 # BUILT_SOURCES is only a hack and dependency tracking does not help for the first build
-vdir.lo fall_back.lo hash.lo random.lo round_robin.lo: vcc_if.h
+vdir.lo dns.lo fall_back.lo hash.lo random.lo round_robin.lo: vcc_if.h
 
 vcc_if.c vcc_if.h vmod_directors.rst vmod_directors.man.rst: $(vmodtool) $(vmod_srcdir)/vmod.vcc
 	@PYTHON@ $(vmodtool) $(vmodtoolargs) $(vmod_srcdir)/vmod.vcc
diff --git a/lib/libvmod_directors/dns.c b/lib/libvmod_directors/dns.c
new file mode 100644
index 0000000..df0cf98
--- /dev/null
+++ b/lib/libvmod_directors/dns.c
@@ -0,0 +1,393 @@
+/*-
+ * Copyright (c) 2013 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Dridi Boukelmoune <dridi@varnish-software.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <arpa/inet.h>
+
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <errno.h>
+#include <netdb.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "vrt.h"
+#include "vcl.h"
+
+#include "cache/cache.h"
+#include "cache/cache_director.h"
+#include "cache/cache_backend.h"
+
+#include "vsa.h"
+#include "vtim.h"
+#include "vcc_if.h"
+
+struct vmod_directors_dns;
+
+struct dns_director {
+	struct vmod_directors_dns	*dns;
+	struct director			*simple_dir;
+	VTAILQ_ENTRY(dns_director)	list;
+	struct suckaddr 		*ip_suckaddr;
+	char				*ip_addr;
+	char				*vcl_name;
+	unsigned			mark;
+};
+
+struct vmod_directors_dns {
+	unsigned			magic;
+#define VMOD_DIRECTORS_DNS_MAGIC	0x8a3e7fd1
+	pthread_t			thread;
+	pthread_mutex_t			mtx;
+	pthread_cond_t			cond;
+	char				*vcl_name;
+	char				*addr;
+	char				*port;
+	double				ttl;
+	VTAILQ_HEAD(,dns_director)	director_list;
+	struct dns_director		*next;
+	struct director			dir;
+	struct vcl			*vcl;
+	int				active;
+	unsigned			mark;
+};
+
+static void
+vmod_dns_rotate(struct vmod_directors_dns *dns)
+{
+	if (dns->next != NULL)
+		dns->next = dns->next->list.vtqe_next;
+
+	if (dns->next == NULL)
+		dns->next = dns->director_list.vtqh_first;
+}
+
+static int
+vmod_dns_notfound(struct vmod_directors_dns *dns, struct suckaddr *sa)
+{
+	struct dns_director *d;
+
+	AN(sa);
+
+	VTAILQ_FOREACH(d, &dns->director_list, list) {
+		if (d->mark == dns->mark)
+			continue;
+
+		if (VSA_Compare(d->ip_suckaddr, sa))
+			continue;
+
+		d->mark = dns->mark;
+		return (0);
+	}
+
+	return (1);
+}
+
+static unsigned __match_proto__(vdi_healthy_f)
+vmod_dns_healthy(const struct director *d, const struct busyobj *bo,
+    double *changed)
+{
+	struct vmod_directors_dns *dns;
+	unsigned healthy;
+
+	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
+	CAST_OBJ_NOTNULL(dns, d->priv, VMOD_DIRECTORS_DNS_MAGIC);
+
+	AZ(pthread_mutex_lock(&dns->mtx));
+	(void)changed;
+	healthy = (dns->next != NULL);
+	AZ(pthread_mutex_unlock(&dns->mtx));
+
+	// XXX we can do better than that
+	return (healthy);
+}
+
+static const struct director * __match_proto__(vdi_resolve_f)
+vmod_dns_resolve(const struct director *d, struct worker *wrk,
+    struct busyobj *bo)
+{
+	struct vmod_directors_dns *dns;
+	struct director *dir = NULL;
+
+	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
+	CAST_OBJ_NOTNULL(dns, d->priv, VMOD_DIRECTORS_DNS_MAGIC);
+
+	pthread_mutex_lock(&dns->mtx);
+	if (dns->next != NULL) {
+		dir = dns->next->simple_dir;
+		vmod_dns_rotate(dns);
+	}
+	AZ(pthread_mutex_unlock(&dns->mtx));
+
+	return (dir);
+}
+
+static void
+vmod_dns_del(VRT_CTX, struct dns_director *dir)
+{
+	struct dns_director *next;
+	struct vmod_directors_dns *dns;
+
+	AN(dir);
+	AN(dir->dns);
+
+	dns = dir->dns;
+	next = dns->next;
+	AN(next);
+
+	if (dir == next) {
+		vmod_dns_rotate(dns);
+		if (next == dns->next)
+			dns->next = NULL;
+	}
+
+	VTAILQ_REMOVE(&dir->dns->director_list, dir, list);
+	if (ctx) {
+		AN(ctx->vcl);
+		VRT_delete_backend(ctx, &dir->simple_dir);
+	}
+	free(dir->vcl_name);
+	free(dir->ip_addr);
+	free(dir->ip_suckaddr);
+	free(dir);
+}
+
+static void
+vmod_dns_add(VRT_CTX, struct vmod_directors_dns *dns,
+    struct suckaddr *sa)
+{
+	struct vrt_backend vrt;
+	struct dns_director *dir;
+	const unsigned char *ptr = NULL;
+	char buf[128];
+	int af;
+	socklen_t len;
+
+	dir = malloc(sizeof *dir);
+	AN(dir);
+	dir->dns = dns;
+	dir->mark = dns->mark;
+	dir->ip_suckaddr = sa;
+
+	af = VRT_VSA_GetPtr(sa, &ptr);
+	AN(ptr);
+	len = VSA_Len(sa);
+	AN(inet_ntop(af, ptr, buf, len));
+	dir->ip_addr = strdup(buf);
+	AN(dir->ip_addr);
+
+	snprintf(buf, sizeof buf, "%s(%s)", dns->vcl_name, dir->ip_addr);
+	dir->vcl_name = strdup(buf);
+	AN(dir->vcl_name);
+
+	INIT_OBJ(&vrt, VRT_BACKEND_MAGIC);
+	vrt.port = dns->port;
+	vrt.hosthdr = dns->addr;
+	vrt.vcl_name = dir->vcl_name;
+
+	switch (af) {
+	case AF_INET:
+		vrt.ipv4_suckaddr = sa;
+		vrt.ipv4_addr = dir->ip_addr;
+		break;
+	case AF_INET6:
+		vrt.ipv6_suckaddr = sa;
+		vrt.ipv6_addr = dir->ip_addr;
+		break;
+	default:
+		WRONG("unexpected family");
+	}
+
+
+	dir->simple_dir = VRT_new_backend(ctx, &vrt);
+	AN(dir->simple_dir);
+
+	VTAILQ_INSERT_TAIL(&dns->director_list, dir, list);
+}
+
+static void
+vmod_dns_update(struct vmod_directors_dns *dns, struct addrinfo *addr)
+{
+	struct suckaddr *sa;
+	struct dns_director *d, *d2;
+	struct vrt_ctx ctx;
+
+	AN(addr);
+
+	INIT_OBJ(&ctx, VRT_CTX_MAGIC);
+	ctx.vcl = dns->vcl;
+
+	dns->mark++;
+	while (addr) {
+		switch (addr->ai_family) {
+		case AF_INET:
+		case AF_INET6:
+			sa = malloc(vsa_suckaddr_len);
+			AN(sa);
+			AN(VSA_Build(sa, addr->ai_addr, addr->ai_addrlen));
+			if (vmod_dns_notfound(dns, sa))
+				vmod_dns_add(&ctx, dns, sa);
+		}
+		addr = addr->ai_next;
+	}
+
+	VTAILQ_FOREACH_SAFE(d, &dns->director_list, list, d2)
+		if (d->mark != dns->mark)
+			vmod_dns_del(&ctx, d);
+
+	vmod_dns_rotate(dns);
+}
+
+static void*
+vmod_dns_lookup_thread(void *obj)
+{
+	struct vmod_directors_dns *dns;
+	struct timespec ts;
+	struct addrinfo hints, *res;
+	double deadline;
+	int ret;
+
+	CAST_OBJ_NOTNULL(dns, obj, VMOD_DIRECTORS_DNS_MAGIC);
+
+	memset(&hints, 0, sizeof hints);
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_family = AF_UNSPEC;
+
+	while (dns->active) {
+		ret = getaddrinfo(dns->addr, dns->port, &hints, &res);
+
+		AZ(pthread_mutex_lock(&dns->mtx));
+
+		if (ret == 0) {
+			vmod_dns_update(dns, res);
+			freeaddrinfo(res);
+		}
+		else
+			VSL(SLT_Error, 0, "DNS lookup failed: %d (%s)",
+			    ret, gai_strerror(ret));
+
+		deadline = VTIM_real() + dns->ttl;
+		ts = VTIM_timespec(deadline);
+		ret = pthread_cond_timedwait(&dns->cond, &dns->mtx, &ts);
+		assert(ret == 0 || ret == ETIMEDOUT);
+
+		AZ(pthread_mutex_unlock(&dns->mtx));
+	}
+
+	return (NULL);
+}
+
+VCL_VOID __match_proto__()
+vmod_dns__init(VRT_CTX, struct vmod_directors_dns **dnsp, const char *vcl_name,
+    VCL_STRING addr, VCL_STRING port)
+{
+	struct vmod_directors_dns *dns;
+
+	ASSERT_CLI();
+	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
+	AN(dnsp);
+	AZ(*dnsp);
+	AN(vcl_name);
+	AN(addr);
+	AN(port);
+
+	ALLOC_OBJ(dns, VMOD_DIRECTORS_DNS_MAGIC);
+	AN(dns);
+	VTAILQ_INIT(&dns->director_list);
+	REPLACE(dns->vcl_name, vcl_name);
+	REPLACE(dns->addr, addr);
+	REPLACE(dns->port, port);
+	dns->vcl = ctx->vcl;
+	dns->ttl = 3600;
+	dns->active = 1;
+
+	INIT_OBJ(&dns->dir, DIRECTOR_MAGIC);
+	dns->dir.name = "dns";
+	dns->dir.vcl_name = dns->vcl_name;
+	dns->dir.healthy = vmod_dns_healthy;
+	dns->dir.resolve = vmod_dns_resolve;
+	dns->dir.priv = dns;
+
+	AZ(pthread_mutex_init(&dns->mtx, NULL));
+	AZ(pthread_cond_init(&dns->cond, NULL));
+	AZ(pthread_create(&dns->thread, NULL, &vmod_dns_lookup_thread, dns));
+
+	*dnsp = dns;
+}
+
+VCL_VOID __match_proto__()
+vmod_dns__fini(struct vmod_directors_dns **dnsp)
+{
+	struct vmod_directors_dns *dns;
+
+	AN(dnsp);
+	dns = *dnsp;
+	*dnsp = NULL;
+
+	CHECK_OBJ_NOTNULL(dns, VMOD_DIRECTORS_DNS_MAGIC);
+
+	AZ(pthread_mutex_lock(&dns->mtx));
+	dns->active = 0;
+	AZ(pthread_mutex_unlock(&dns->mtx));
+
+	AZ(pthread_cond_signal(&dns->cond));
+	AZ(pthread_join(dns->thread, NULL));
+
+	/* backends will be deleted by the VCL, pass a NULL struct ctx */
+	while (dns->director_list.vtqh_first != NULL)
+		vmod_dns_del(NULL, dns->director_list.vtqh_first);
+
+	AZ(pthread_cond_destroy(&dns->cond));
+	AZ(pthread_mutex_destroy(&dns->mtx));
+	free(dns->vcl_name);
+	free(dns->addr);
+	free(dns->port);
+	FREE_OBJ(dns);
+}
+
+VCL_VOID __match_proto__()
+vmod_dns_set_ttl(VRT_CTX, struct vmod_directors_dns *dns, VCL_DURATION ttl)
+{
+
+	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
+	CHECK_OBJ_NOTNULL(dns, VMOD_DIRECTORS_DNS_MAGIC);
+	assert(ttl > 0);
+	dns->ttl = ttl;
+}
+
+VCL_BACKEND __match_proto__()
+vmod_dns_backend(VRT_CTX, struct vmod_directors_dns *dns)
+{
+
+	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
+	CHECK_OBJ_NOTNULL(dns, VMOD_DIRECTORS_DNS_MAGIC);
+	return (&dns->dir);
+}
diff --git a/lib/libvmod_directors/vmod.vcc b/lib/libvmod_directors/vmod.vcc
index cfcf2e3..9291d4d 100644
--- a/lib/libvmod_directors/vmod.vcc
+++ b/lib/libvmod_directors/vmod.vcc
@@ -180,3 +180,46 @@ Description
 Example
 	# pick a backend based on the cookie header from the client
 	set req.backend_hint = vdir.backend(req.http.cookie);
+
+$Object dns(STRING, STRING)
+
+Description
+	Create a DNS director.
+
+	The director creates backends with DNS lookups and chooses them in a
+	round robin fashion. An ACL (Access Control List) can be used as a
+	white-list to restrict the use of resolved addresses.
+
+Example
+	::
+
+	   acl www_backends {
+	   	"192.168.15.0"/24;
+	   	!"192.168.15.1";
+	   }
+
+	   sub vcl_init {
+	   	new www_dir = directors.dns("www.example.com", "80");
+	   	www_dir.restrict_to(www_backends);
+	   	www_dir.set_ttl(5m);
+	   }
+
+	   sub vcl_recv {
+	   	set req.backend_hint = www_dir.backend();
+	   }
+
+# TODO $Method VOID .restrict_to(ACL)
+#
+#Description
+#	Restrict usage of resolved IP addresses to a named ACL.
+
+$Method VOID .set_ttl(DURATION)
+
+Description
+	Set the DNS lookup TTL (defaults to one hour).
+
+$Method BACKEND .backend()
+
+Description
+	Pick a backend from the director.
+
-- 
2.1.0



_______________________________________________
varnish-dev mailing list
varnish-dev@varnish-cache.org
https://www.varnish-cache.org/lists/mailman/listinfo/varnish-dev

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic