diff --git a/docs/locking.html.in b/docs/locking.html.in index 0d039dabc3..6d7b5174a4 100644 --- a/docs/locking.html.in +++ b/docs/locking.html.in @@ -208,5 +208,29 @@ </pool> +

Domain configuration

+ +

+ In case sanlock loses access to disk locks for some reason, it will + kill all domains that lost their locks. This default behavior may + be changed using + on_lockfailure + element in domain XML. When this element is present, sanlock + will call sanlock_helper (provided by libvirt) with + the specified action. This helper binary will connect to libvirtd + and thus it may need to authenticate if libvirtd was configured to + require that on the read-write UNIX socket. To provide the + appropriate credentials to sanlock_helper, a + client authentication + file needs to contain something like the following: +

+
+[auth-libvirt-localhost]
+credentials=sanlock
+
+[credentials-sanlock]
+authname=login
+password=password
+    
diff --git a/libvirt.spec.in b/libvirt.spec.in index e3d0a2d48a..318fe92503 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -1788,6 +1788,7 @@ rm -f $RPM_BUILD_ROOT%{_sysconfdir}/sysctl.d/libvirtd %dir %attr(0700, root, root) %{_localstatedir}/lib/libvirt/sanlock %{_sbindir}/virt-sanlock-cleanup %{_mandir}/man8/virt-sanlock-cleanup.8* +%attr(0755, root, root) %{_libexecdir}/libvirt_sanlock_helper %endif %files client -f %{name}.lang diff --git a/po/POTFILES.in b/po/POTFILES.in index 2538225979..815e992705 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -47,6 +47,7 @@ src/libvirt.c src/libvirt-qemu.c src/locking/lock_driver_sanlock.c src/locking/lock_manager.c +src/locking/sanlock_helper.c src/lxc/lxc_cgroup.c src/lxc/lxc_container.c src/lxc/lxc_conf.c diff --git a/src/Makefile.am b/src/Makefile.am index 0aefc024ee..4f19bcfe07 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -142,6 +142,8 @@ DRIVER_SOURCES = \ LOCK_DRIVER_SANLOCK_SOURCES = \ locking/lock_driver_sanlock.c +LOCK_DRIVER_SANLOCK_HELPER_SOURCES = \ + locking/sanlock_helper.c NETDEV_CONF_SOURCES = \ conf/netdev_bandwidth_conf.h conf/netdev_bandwidth_conf.c \ @@ -1649,6 +1651,15 @@ endif EXTRA_DIST += $(STORAGE_HELPER_DISK_SOURCES) +if HAVE_SANLOCK +libexec_PROGRAMS += libvirt_sanlock_helper + +libvirt_sanlock_helper_SOURCES = $(LOCK_DRIVER_SANLOCK_HELPER_SOURCES) +libvirt_sanlock_helper_CFLAGS = -I$(top_srcdir)/src/conf $(AM_CFLAGS) +libvirt_sanlock_helper_LDFLAGS = $(WARN_LDFLAGS) $(AM_LDFLAGS) +libvirt_sanlock_helper_LDADD = libvirt.la +endif + if WITH_LXC if WITH_LIBVIRTD libexec_PROGRAMS += libvirt_lxc diff --git a/src/locking/lock_driver_sanlock.c b/src/locking/lock_driver_sanlock.c index 8c0ac8c636..a218432fd2 100644 --- a/src/locking/lock_driver_sanlock.c +++ b/src/locking/lock_driver_sanlock.c @@ -50,6 +50,7 @@ #define VIR_FROM_THIS VIR_FROM_LOCKING #define VIR_LOCK_MANAGER_SANLOCK_AUTO_DISK_LOCKSPACE "__LIBVIRT__DISKS__" +#define VIR_LOCK_MANAGER_SANLOCK_KILLPATH LIBEXECDIR "/libvirt_sanlock_helper" /* * temporary fix for the case where the sanlock devel package is @@ -75,8 +76,9 @@ struct _virLockManagerSanlockDriver { static virLockManagerSanlockDriver *driver = NULL; struct _virLockManagerSanlockPrivate { + const char *vm_uri; char vm_name[SANLK_NAME_LEN]; - char vm_uuid[VIR_UUID_BUFLEN]; + unsigned char vm_uuid[VIR_UUID_BUFLEN]; unsigned int vm_id; unsigned int vm_pid; unsigned int flags; @@ -383,6 +385,8 @@ static int virLockManagerSanlockNew(virLockManagerPtr lock, priv->vm_pid = param->value.ui; } else if (STREQ(param->key, "id")) { priv->vm_id = param->value.ui; + } else if (STREQ(param->key, "uri")) { + priv->vm_uri = param->value.cstr; } } @@ -683,10 +687,86 @@ static int virLockManagerSanlockAddResource(virLockManagerPtr lock, return 0; } +static int +virLockManagerSanlockRegisterKillscript(int sock, + const char *vmuri, + const char *uuidstr, + virDomainLockFailureAction action) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + char *path; + char *args = NULL; + int ret = -1; + int rv; + + if (action > VIR_DOMAIN_LOCK_FAILURE_IGNORE) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Failure action %s is not supported by sanlock"), + virDomainLockFailureTypeToString(action)); + goto cleanup; + } + + virBufferEscape(&buf, '\\', "\\ ", "%s", vmuri); + virBufferAddLit(&buf, " "); + virBufferEscape(&buf, '\\', "\\ ", "%s", uuidstr); + virBufferAddLit(&buf, " "); + virBufferEscape(&buf, '\\', "\\ ", "%s", + virDomainLockFailureTypeToString(action)); + + if (virBufferError(&buf)) { + virBufferFreeAndReset(&buf); + virReportOOMError(); + goto cleanup; + } + + /* Unfortunately, sanlock_killpath() does not use const for either + * path or args even though it will just copy them into its own + * buffers. + */ + path = (char *) VIR_LOCK_MANAGER_SANLOCK_KILLPATH; + args = virBufferContentAndReset(&buf); + + VIR_DEBUG("Register sanlock killpath: %s %s", path, args); + + /* sanlock_killpath() would just crop the strings */ + if (strlen(path) >= SANLK_HELPER_PATH_LEN) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Sanlock helper path is longer than %d: '%s'"), + SANLK_HELPER_PATH_LEN - 1, path); + goto cleanup; + } + if (strlen(args) >= SANLK_HELPER_ARGS_LEN) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Sanlock helper arguments are longer than %d:" + " '%s'"), + SANLK_HELPER_ARGS_LEN - 1, args); + goto cleanup; + } + + if ((rv = sanlock_killpath(sock, 0, path, args)) < 0) { + if (rv <= -200) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Failed to register lock failure action:" + " error %d"), rv); + } else { + virReportSystemError(-rv, "%s", + _("Failed to register lock failure" + " action")); + } + goto cleanup; + } + + ret = 0; + +cleanup: + VIR_FREE(args); + return ret; +} + static int virLockManagerSanlockAcquire(virLockManagerPtr lock, const char *state, unsigned int flags, - virDomainLockFailureAction action ATTRIBUTE_UNUSED, + virDomainLockFailureAction action, int *fd) { virLockManagerSanlockPrivatePtr priv = lock->privateData; @@ -741,23 +821,32 @@ static int virLockManagerSanlockAcquire(virLockManagerPtr lock, res_count = priv->res_count; } - VIR_DEBUG("Register sanlock %d", flags); /* We only initialize 'sock' if we are in the real * child process and we need it to be inherited * * If sock==-1, then sanlock auto-open/closes a * temporary sock */ - if (priv->vm_pid == getpid() && - (sock = sanlock_register()) < 0) { - if (sock <= -200) - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Failed to open socket to sanlock daemon: error %d"), - sock); - else - virReportSystemError(-sock, "%s", - _("Failed to open socket to sanlock daemon")); - goto error; + if (priv->vm_pid == getpid()) { + VIR_DEBUG("Register sanlock %d", flags); + if ((sock = sanlock_register()) < 0) { + if (sock <= -200) + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Failed to open socket to sanlock daemon: error %d"), + sock); + else + virReportSystemError(-sock, "%s", + _("Failed to open socket to sanlock daemon")); + goto error; + } + + if (action != VIR_DOMAIN_LOCK_FAILURE_DEFAULT) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(priv->vm_uuid, uuidstr); + if (virLockManagerSanlockRegisterKillscript(sock, priv->vm_uri, + uuidstr, action) < 0) + goto error; + } } if (!(flags & VIR_LOCK_MANAGER_ACQUIRE_REGISTER_ONLY)) { diff --git a/src/locking/sanlock_helper.c b/src/locking/sanlock_helper.c new file mode 100644 index 0000000000..a73b49ca41 --- /dev/null +++ b/src/locking/sanlock_helper.c @@ -0,0 +1,138 @@ +#include +#include +#include +#include + +#include "configmake.h" +#include "internal.h" +#include "conf.h" +#include "memory.h" +#include "domain_conf.h" + + +static int +getArgs(int argc, + char **argv, + const char **uri, + const char **uuid, + virDomainLockFailureAction *action) +{ + int act; + + if (argc != 4) { + fprintf(stderr, _("%s uri uuid action\n"), argv[0]); + return -1; + } + + *uri = argv[1]; + *uuid = argv[2]; + + act = virDomainLockFailureTypeFromString(argv[3]); + if (act < 0) { + fprintf(stderr, _("invalid failure action: '%s'\n"), argv[3]); + return -1; + } + *action = act; + + return 0; +} + + +static int +authCallback(virConnectCredentialPtr cred ATTRIBUTE_UNUSED, + unsigned int ncred ATTRIBUTE_UNUSED, + void *cbdata ATTRIBUTE_UNUSED) +{ + return -1; +} + + +int +main(int argc, char **argv) +{ + const char *uri; + const char *uuid; + virDomainLockFailureAction action; + char *xml = NULL; + virConnectPtr conn = NULL; + virDomainPtr dom = NULL; + int ret = EXIT_FAILURE; + + int authTypes[] = { + VIR_CRED_AUTHNAME, + VIR_CRED_ECHOPROMPT, + VIR_CRED_PASSPHRASE, + VIR_CRED_NOECHOPROMPT, + }; + virConnectAuth auth = { + .credtype = authTypes, + .ncredtype = ARRAY_CARDINALITY(authTypes), + .cb = authCallback, + }; + + if (setlocale(LC_ALL, "") == NULL || + bindtextdomain(PACKAGE, LOCALEDIR) == NULL || + textdomain(PACKAGE) == NULL) { + fprintf(stderr, _("%s: initialization failed\n"), argv[0]); + exit(EXIT_FAILURE); + } + + if (getArgs(argc, argv, &uri, &uuid, &action) < 0) + goto cleanup; + + if (!(conn = virConnectOpenAuth(uri, &auth, 0)) || + !(dom = virDomainLookupByUUIDString(conn, uuid))) + goto cleanup; + + switch (action) { + case VIR_DOMAIN_LOCK_FAILURE_POWEROFF: + if (virDomainDestroy(dom) == 0 || + virDomainIsActive(dom) == 0) + ret = EXIT_SUCCESS; + break; + + case VIR_DOMAIN_LOCK_FAILURE_RESTART: + if (virDomainIsPersistent(dom)) { + if ((virDomainDestroy(dom) == 0 || + virDomainIsActive(dom) == 0) && + virDomainCreate(dom) == 0) + ret = EXIT_SUCCESS; + } else { + xml = virDomainGetXMLDesc(dom, + VIR_DOMAIN_XML_SECURE | + VIR_DOMAIN_XML_INACTIVE); + + if (!xml || + (virDomainDestroy(dom) < 0 && + virDomainIsActive(dom) != 0)) + goto cleanup; + virDomainFree(dom); + if ((dom = virDomainCreateXML(conn, xml, 0))) + ret = EXIT_SUCCESS; + } + break; + + case VIR_DOMAIN_LOCK_FAILURE_PAUSE: + if (virDomainSuspend(dom) == 0) + ret = EXIT_SUCCESS; + break; + + case VIR_DOMAIN_LOCK_FAILURE_IGNORE: + ret = EXIT_SUCCESS; + break; + + default: + fprintf(stderr, _("unsupported failure action: '%s'\n"), + virDomainLockFailureTypeToString(action)); + break; + } + +cleanup: + if (dom) + virDomainFree(dom); + if (conn) + virConnectClose(conn); + VIR_FREE(xml); + + return ret; +}