From a32135b3b120f9442c44d0e484daa92894233be0 Mon Sep 17 00:00:00 2001 From: Erik Skultety Date: Tue, 12 Apr 2016 19:21:43 +0200 Subject: [PATCH] rpc: virnetserverclient: Introduce new attribute conn_time to client Besides ID, libvirt should provide several parameters to help the user distinguish two clients from each other. One of them is the connection timestamp. This patch also adds a testcase for proper JSON formatting of the new attribute too (proper formatting of older clients that did not support this attribute yet is included in the existing tests) - in order to testGenerateJSON to work, a mock of time_t time(time_t *timer) needed to be created. Signed-off-by: Erik Skultety --- src/rpc/virnetserverclient.c | 46 +++++++++++- src/rpc/virnetserverclient.h | 1 + tests/Makefile.am | 7 ++ .../input-data-client-timestamp.json | 70 +++++++++++++++++++ .../output-data-client-timestamp.json | 70 +++++++++++++++++++ tests/virnetdaemonmock.c | 34 +++++++++ tests/virnetdaemontest.c | 4 +- 7 files changed, 228 insertions(+), 4 deletions(-) create mode 100644 tests/virnetdaemondata/input-data-client-timestamp.json create mode 100644 tests/virnetdaemondata/output-data-client-timestamp.json create mode 100644 tests/virnetdaemonmock.c diff --git a/src/rpc/virnetserverclient.c b/src/rpc/virnetserverclient.c index 7233773e85..a1543b9df4 100644 --- a/src/rpc/virnetserverclient.c +++ b/src/rpc/virnetserverclient.c @@ -85,6 +85,13 @@ struct _virNetServerClient virIdentityPtr identity; + /* Connection timestamp, i.e. when a client connected to the daemon (UTC). + * For old clients restored by post-exec-restart, which did not have this + * attribute, value of 0 (epoch time) is used to indicate we have no + * information about their connection time. + */ + time_t conn_time; + /* Count of messages in the 'tx' queue, * and the server worker pool queue * ie RPC calls in progress. Does not count @@ -355,7 +362,8 @@ virNetServerClientNewInternal(unsigned long long id, virNetTLSContextPtr tls, #endif bool readonly, - size_t nrequests_max) + size_t nrequests_max, + time_t timestamp) { virNetServerClientPtr client; @@ -373,6 +381,7 @@ virNetServerClientNewInternal(unsigned long long id, client->tlsCtxt = virObjectRef(tls); #endif client->nrequests_max = nrequests_max; + client->conn_time = timestamp; client->sockTimer = virEventAddTimeout(-1, virNetServerClientSockTimerFunc, client, NULL); @@ -413,6 +422,7 @@ virNetServerClientPtr virNetServerClientNew(unsigned long long id, void *privOpaque) { virNetServerClientPtr client; + time_t now; VIR_DEBUG("sock=%p auth=%d tls=%p", sock, auth, #ifdef WITH_GNUTLS @@ -422,11 +432,17 @@ virNetServerClientPtr virNetServerClientNew(unsigned long long id, #endif ); + if ((now = time(NULL)) == (time_t) - 1) { + virReportSystemError(errno, "%s", _("failed to get current time")); + return NULL; + } + if (!(client = virNetServerClientNewInternal(id, sock, auth, #ifdef WITH_GNUTLS tls, #endif - readonly, nrequests_max))) + readonly, nrequests_max, + now))) return NULL; if (privNew) { @@ -456,6 +472,7 @@ virNetServerClientPtr virNetServerClientNewPostExecRestart(virJSONValuePtr objec bool readonly; unsigned int nrequests_max; unsigned long long id; + time_t timestamp; if (virJSONValueObjectGetNumberInt(object, "auth", &auth) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", @@ -492,6 +509,18 @@ virNetServerClientPtr virNetServerClientNewPostExecRestart(virJSONValuePtr objec } } + if (!virJSONValueObjectHasKey(object, "conn_time")) { + timestamp = 0; + } else { + if (virJSONValueObjectGetNumberLong(object, "conn_time", + (long long *) ×tamp) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Malformed conn_time field in JSON " + "state document")); + return NULL; + } + } + if (!(sock = virNetSocketNewPostExecRestart(child))) { virObjectUnref(sock); return NULL; @@ -504,7 +533,8 @@ virNetServerClientPtr virNetServerClientNewPostExecRestart(virJSONValuePtr objec NULL, #endif readonly, - nrequests_max))) { + nrequests_max, + timestamp))) { virObjectUnref(sock); return NULL; } @@ -552,6 +582,11 @@ virJSONValuePtr virNetServerClientPreExecRestart(virNetServerClientPtr client) if (virJSONValueObjectAppendNumberUint(object, "nrequests_max", client->nrequests_max) < 0) goto error; + if (client->conn_time && + virJSONValueObjectAppendNumberLong(object, "conn_time", + client->conn_time) < 0) + goto error; + if (!(child = virNetSocketPreExecRestart(client->sock))) goto error; @@ -610,6 +645,11 @@ unsigned long long virNetServerClientGetID(virNetServerClientPtr client) return client->id; } +long long virNetServerClientGetTimestamp(virNetServerClientPtr client) +{ + return client->conn_time; +} + #ifdef WITH_GNUTLS bool virNetServerClientHasTLSSession(virNetServerClientPtr client) { diff --git a/src/rpc/virnetserverclient.h b/src/rpc/virnetserverclient.h index 95edb0604b..e68ef76f04 100644 --- a/src/rpc/virnetserverclient.h +++ b/src/rpc/virnetserverclient.h @@ -82,6 +82,7 @@ int virNetServerClientGetAuth(virNetServerClientPtr client); void virNetServerClientSetAuth(virNetServerClientPtr client, int auth); bool virNetServerClientGetReadonly(virNetServerClientPtr client); unsigned long long virNetServerClientGetID(virNetServerClientPtr client); +long long virNetServerClientGetTimestamp(virNetServerClientPtr client); # ifdef WITH_GNUTLS bool virNetServerClientHasTLSSession(virNetServerClientPtr client); diff --git a/tests/Makefile.am b/tests/Makefile.am index 8cf53bf0b6..6d8fa003d6 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -408,6 +408,7 @@ EXTRA_DIST += $(test_scripts) test_libraries = libshunload.la \ virportallocatormock.la \ + virnetdaemonmock.la \ virnetserverclientmock.la \ vircgroupmock.la \ virpcimock.la \ @@ -927,6 +928,12 @@ virnetdaemontest_SOURCES = \ virnetdaemontest_CFLAGS = $(XDR_CFLAGS) $(AM_CFLAGS) virnetdaemontest_LDADD = $(LDADDS) +virnetdaemonmock_la_SOURCES = \ + virnetdaemonmock.c +virnetdaemonmock_la_CFLAGS = $(AM_CFLAGS) +virnetdaemonmock_la_LDFLAGS = $(MOCKLIBS_LDFLAGS) +virnetdaemonmock_la_LIBADD = $(MOCKLIBS_LIBS) + virnetserverclienttest_SOURCES = \ virnetserverclienttest.c \ testutils.h testutils.c diff --git a/tests/virnetdaemondata/input-data-client-timestamp.json b/tests/virnetdaemondata/input-data-client-timestamp.json new file mode 100644 index 0000000000..d069997a14 --- /dev/null +++ b/tests/virnetdaemondata/input-data-client-timestamp.json @@ -0,0 +1,70 @@ +{ + "servers": { + "testServer0": { + "min_workers": 10, + "max_workers": 50, + "priority_workers": 5, + "max_clients": 100, + "max_anonymous_clients": 10, + "keepaliveInterval": 120, + "keepaliveCount": 5, + "next_client_id": 3, + "services": [ + { + "auth": 0, + "readonly": true, + "nrequests_client_max": 2, + "socks": [ + { + "fd": 100, + "errfd": -1, + "pid": 0, + "isClient": false + } + ] + }, + { + "auth": 2, + "readonly": false, + "nrequests_client_max": 5, + "socks": [ + { + "fd": 101, + "errfd": -1, + "pid": 0, + "isClient": false + } + ] + } + ], + "clients": [ + { + "id": 1, + "auth": 1, + "readonly": true, + "nrequests_max": 15, + "conn_time": 1234567890, + "sock": { + "fd": 102, + "errfd": -1, + "pid": -1, + "isClient": true + } + }, + { + "id": 2, + "auth": 2, + "readonly": true, + "nrequests_max": 66, + "conn_time": 1234567890, + "sock": { + "fd": 103, + "errfd": -1, + "pid": -1, + "isClient": true + } + } + ] + } + } +} diff --git a/tests/virnetdaemondata/output-data-client-timestamp.json b/tests/virnetdaemondata/output-data-client-timestamp.json new file mode 100644 index 0000000000..d069997a14 --- /dev/null +++ b/tests/virnetdaemondata/output-data-client-timestamp.json @@ -0,0 +1,70 @@ +{ + "servers": { + "testServer0": { + "min_workers": 10, + "max_workers": 50, + "priority_workers": 5, + "max_clients": 100, + "max_anonymous_clients": 10, + "keepaliveInterval": 120, + "keepaliveCount": 5, + "next_client_id": 3, + "services": [ + { + "auth": 0, + "readonly": true, + "nrequests_client_max": 2, + "socks": [ + { + "fd": 100, + "errfd": -1, + "pid": 0, + "isClient": false + } + ] + }, + { + "auth": 2, + "readonly": false, + "nrequests_client_max": 5, + "socks": [ + { + "fd": 101, + "errfd": -1, + "pid": 0, + "isClient": false + } + ] + } + ], + "clients": [ + { + "id": 1, + "auth": 1, + "readonly": true, + "nrequests_max": 15, + "conn_time": 1234567890, + "sock": { + "fd": 102, + "errfd": -1, + "pid": -1, + "isClient": true + } + }, + { + "id": 2, + "auth": 2, + "readonly": true, + "nrequests_max": 66, + "conn_time": 1234567890, + "sock": { + "fd": 103, + "errfd": -1, + "pid": -1, + "isClient": true + } + } + ] + } + } +} diff --git a/tests/virnetdaemonmock.c b/tests/virnetdaemonmock.c new file mode 100644 index 0000000000..6d807a5a91 --- /dev/null +++ b/tests/virnetdaemonmock.c @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Author: Erik Skultety + */ + +#include + +#include "internal.h" +#include + +#define VIR_FROM_THIS VIR_FROM_NONE + +time_t time(time_t *t) +{ + const time_t ret = 1234567890; + if (t) + *t = ret; + return ret; +} diff --git a/tests/virnetdaemontest.c b/tests/virnetdaemontest.c index b98c14805c..714f8d7271 100644 --- a/tests/virnetdaemontest.c +++ b/tests/virnetdaemontest.c @@ -339,15 +339,17 @@ mymain(void) EXEC_RESTART_TEST("admin-server-names", 2); EXEC_RESTART_TEST("no-keepalive-required", 2); EXEC_RESTART_TEST("client-ids", 1); + EXEC_RESTART_TEST("client-timestamp", 1); EXEC_RESTART_TEST_FAIL("anon-clients", 2); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } +VIRT_TEST_MAIN_PRELOAD(mymain, abs_builddir "/.libs/virnetdaemonmock.so") #else static int mymain(void) { return EXIT_AM_SKIP; } -#endif VIRT_TEST_MAIN(mymain); +#endif