[PATCH 0/4] ch: timeout fix (GitLab issue #743)

This patch series is supposed to fix the following reported issue on GitLab: virtchd times out even with an active guest running https://gitlab.com/libvirt/libvirt/-/issues/743 During further investigation, we found that there are in fact two bugs: 1. The CH driver does not store any domains' XMLs onto disk, and as a result it does not have any information about running domains when it restarts. Previously, the CH driver did not save any XMLs onto the disk, neither transient nor persistent definitions. To address this issue, add transient domain XML saving when a new domain is defined, as well as saving transient domain XML when a domain starts, to enable reconnecting to running domains when the driver restarts. Now all persistent and transient definitions are stored onto the disk. Also add running domain reconnection helper functions to restore the connection between the driver and running VMM when the driver restarts, to be able to continue work with running domains. 2. Timeout should not be active while a client is connected or there is a running domain. The second bug led to a situation where the CH driver timed out (e.g., --timeout 60) even when some domains were still running. Previously, only an active virsh session prevented the driver from timing out. To address this issue, an inhibitor for the CH driver has been added to prevent the timeout. The inhibitor now stores the count of running domains and prevents the timeout if the count is above zero. NOTE: This series patch series is depend on followin patch series: https://lists.libvirt.org/archives/list/devel@lists.libvirt.org/thread/O3SKH... Kirill Shchetiniuk (4): ch: add persistent definition save and load ch: add transient definition save and load ch: add reconnection to running domains ch: fix timeout while domain is still running src/ch/ch_conf.c | 10 ++- src/ch/ch_conf.h | 6 ++ src/ch/ch_domain.c | 15 +++++ src/ch/ch_domain.h | 3 + src/ch/ch_driver.c | 49 ++++++++++++++- src/ch/ch_monitor.c | 65 ++++++++++++++++++++ src/ch/ch_monitor.h | 2 + src/ch/ch_process.c | 147 ++++++++++++++++++++++++++++++++++++++++++++ src/ch/ch_process.h | 2 + 9 files changed, 293 insertions(+), 6 deletions(-) -- 2.48.1

When the domain was defined, its persistent domain definition wasn't saved on disk, leading to the loss of all defined domains after a CH driver restart. Now all defined persistent domains are saved on disk and loaded from disk during CH driver initialization. Saved domains now also can be removed with undefine. Resolves: https://gitlab.com/libvirt/libvirt/-/issues/743 Signed-off-by: Kirill Shchetiniuk <kshcheti@redhat.com> --- src/ch/ch_conf.c | 10 +++++++--- src/ch/ch_conf.h | 2 ++ src/ch/ch_driver.c | 24 +++++++++++++++++++++++- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/ch/ch_conf.c b/src/ch/ch_conf.c index cab97639c4..f0cb963656 100644 --- a/src/ch/ch_conf.c +++ b/src/ch/ch_conf.c @@ -149,11 +149,11 @@ virCHDriverConfigNew(bool privileged) cfg->logDir = g_strdup_printf("%s/log/libvirt/ch", LOCALSTATEDIR); cfg->stateDir = g_strdup_printf("%s/libvirt/ch", RUNSTATEDIR); cfg->saveDir = g_strdup_printf("%s/lib/libvirt/ch/save", LOCALSTATEDIR); + cfg->configBaseDir = g_strdup_printf("%s/libvirt", SYSCONFDIR); } else { g_autofree char *rundir = NULL; g_autofree char *cachedir = NULL; - g_autofree char *configbasedir = NULL; cachedir = virGetUserCacheDirectory(); @@ -162,10 +162,12 @@ virCHDriverConfigNew(bool privileged) rundir = virGetUserRuntimeDirectory(); cfg->stateDir = g_strdup_printf("%s/ch/run", rundir); - configbasedir = virGetUserConfigDirectory(); - cfg->saveDir = g_strdup_printf("%s/ch/save", configbasedir); + cfg->configBaseDir = virGetUserConfigDirectory(); + cfg->saveDir = g_strdup_printf("%s/ch/save", cfg->configBaseDir); } + cfg->configDir = g_strdup_printf("%s/ch", cfg->configBaseDir); + return cfg; } @@ -183,6 +185,8 @@ virCHDriverConfigDispose(void *obj) g_free(cfg->saveDir); g_free(cfg->stateDir); g_free(cfg->logDir); + g_free(cfg->configBaseDir); + g_free(cfg->configDir); } #define MIN_VERSION ((15 * 1000000) + (0 * 1000) + (0)) diff --git a/src/ch/ch_conf.h b/src/ch/ch_conf.h index b08573476e..8beeb69b95 100644 --- a/src/ch/ch_conf.h +++ b/src/ch/ch_conf.h @@ -40,6 +40,8 @@ struct _virCHDriverConfig { char *stateDir; char *logDir; char *saveDir; + char *configBaseDir; + char *configDir; int cgroupControllers; diff --git a/src/ch/ch_driver.c b/src/ch/ch_driver.c index 3bdcf66ebd..c7f357dac9 100644 --- a/src/ch/ch_driver.c +++ b/src/ch/ch_driver.c @@ -338,6 +338,7 @@ chDomainDefineXMLFlags(virConnectPtr conn, const char *xml, unsigned int flags) virDomainObj *vm = NULL; virDomainPtr dom = NULL; virObjectEvent *event = NULL; + g_autoptr(virCHDriverConfig) cfg = virCHDriverGetConfig(driver); g_autofree char *managed_save_path = NULL; unsigned int parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE; @@ -370,6 +371,11 @@ chDomainDefineXMLFlags(virConnectPtr conn, const char *xml, unsigned int flags) goto cleanup; } + /* Save new persistent domain definition */ + if (virDomainDefSave(vm->newDef ? vm->newDef : vm->def, + driver->xmlopt, cfg->configDir) < 0) + goto cleanup; + vm->persistent = 1; event = virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_DEFINED, @@ -398,6 +404,7 @@ chDomainUndefineFlags(virDomainPtr dom, virCHDriver *driver = dom->conn->privateData; virDomainObj *vm; virObjectEvent *event = NULL; + g_autoptr(virCHDriverConfig) cfg = NULL; int ret = -1; virCheckFlags(0, -1); @@ -405,6 +412,8 @@ chDomainUndefineFlags(virDomainPtr dom, if (!(vm = virCHDomainObjFromDomain(dom))) goto cleanup; + cfg = virCHDriverGetConfig(driver); + if (virDomainUndefineFlagsEnsureACL(dom->conn, vm->def) < 0) goto cleanup; @@ -413,6 +422,10 @@ chDomainUndefineFlags(virDomainPtr dom, "%s", _("Cannot undefine transient domain")); goto cleanup; } + + if (virDomainDeleteConfig(cfg->configDir, NULL, vm) < 0) + goto cleanup; + event = virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_UNDEFINED, VIR_DOMAIN_EVENT_UNDEFINED_REMOVED); @@ -1413,6 +1426,7 @@ chStateInitialize(bool privileged, { int ret = VIR_DRV_STATE_INIT_ERROR; int rv; + virCHDriverConfig *cfg; if (root != NULL) { virReportError(VIR_ERR_INVALID_ARG, "%s", @@ -1444,7 +1458,7 @@ chStateInitialize(bool privileged, if (!(ch_driver->xmlopt = chDomainXMLConfInit(ch_driver))) goto cleanup; - if (!(ch_driver->config = virCHDriverConfigNew(privileged))) + if (!(ch_driver->config = cfg = virCHDriverConfigNew(privileged))) goto cleanup; if (!(ch_driver->hostdevMgr = virHostdevManagerGetDefault())) @@ -1459,6 +1473,14 @@ chStateInitialize(bool privileged, goto cleanup; } + /* Persistent domains load */ + if (virDomainObjListLoadAllConfigs(ch_driver->domains, + cfg->configDir, + NULL, false, + ch_driver->xmlopt, + NULL, NULL) < 0) + goto cleanup; + ch_driver->chCaps = virCHCapsInitCHVersionCaps(ch_driver->version); ch_driver->privileged = privileged; -- 2.48.1

When domain was started its transient definition was not saved on disk, this led to the situation when CH driver dies and starts again, all transient definitions of currently running domains had been lost. Newly all transient definitions of running domains are saved on disk and can be loaded during the CH driver initialization. Transient definitions are also removed when domain stops. Resolves: https://gitlab.com/libvirt/libvirt/-/issues/743 Signed-off-by: Kirill Shchetiniuk <kshcheti@redhat.com> --- src/ch/ch_domain.c | 15 +++++++++++++++ src/ch/ch_domain.h | 3 +++ src/ch/ch_driver.c | 8 ++++++++ src/ch/ch_process.c | 25 +++++++++++++++++++++++++ 4 files changed, 51 insertions(+) diff --git a/src/ch/ch_domain.c b/src/ch/ch_domain.c index c0c9acd85b..4c9ed93b15 100644 --- a/src/ch/ch_domain.c +++ b/src/ch/ch_domain.c @@ -414,3 +414,18 @@ virCHDomainValidateActualNetDef(virDomainNetDef *net) return 0; } + +void +virCHDomainSaveStatus(virDomainObj *vm) +{ + virCHDomainObjPrivate *priv = vm->privateData; + virCHDriver *driver = priv->driver; + g_autoptr(virCHDriverConfig) cfg = virCHDriverGetConfig(driver); + + VIR_DEBUG("Saving status on vm %s", vm->def->name); + + if (virDomainObjIsActive(vm)) { + if (virDomainObjSave(vm, driver->xmlopt, cfg->stateDir) < 0) + VIR_WARN("Failed to save status on vm %s", vm->def->name); + } +} diff --git a/src/ch/ch_domain.h b/src/ch/ch_domain.h index 69a657f6af..1a43049f91 100644 --- a/src/ch/ch_domain.h +++ b/src/ch/ch_domain.h @@ -79,3 +79,6 @@ virCHDomainObjFromDomain(virDomainPtr domain); int virCHDomainValidateActualNetDef(virDomainNetDef *net); + +void +virCHDomainSaveStatus(virDomainObj *vm); diff --git a/src/ch/ch_driver.c b/src/ch/ch_driver.c index c7f357dac9..4ce68c9299 100644 --- a/src/ch/ch_driver.c +++ b/src/ch/ch_driver.c @@ -1473,6 +1473,14 @@ chStateInitialize(bool privileged, goto cleanup; } + /* Transient domains load */ + if (virDomainObjListLoadAllConfigs(ch_driver->domains, + cfg->stateDir, + NULL, true, + ch_driver->xmlopt, + NULL, NULL) < 0) + goto cleanup; + /* Persistent domains load */ if (virDomainObjListLoadAllConfigs(ch_driver->domains, cfg->configDir, diff --git a/src/ch/ch_process.c b/src/ch/ch_process.c index 31aa49b3a5..08331352a4 100644 --- a/src/ch/ch_process.c +++ b/src/ch/ch_process.c @@ -995,6 +995,8 @@ virCHProcessStart(virCHDriver *driver, virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, reason); + virCHDomainSaveStatus(vm); + return 0; cleanup: @@ -1004,6 +1006,25 @@ virCHProcessStart(virCHDriver *driver, return ret; } +static void +virCHProcessRemoveDomainStatus(virCHDriver *driver, + virDomainObj *vm) +{ + g_autofree char *file = NULL; + virCHDomainObjPrivate *priv = vm->privateData; + g_autoptr(virCHDriverConfig) cfg = virCHDriverGetConfig(driver); + + file = g_strdup_printf("%s/%s.xml", cfg->stateDir, vm->def->name); + + if (unlink(file) < 0 && errno != ENOENT && errno != ENOTDIR) + VIR_WARN("Failed to remove domain status XML for %s: %s", + vm->def->name, g_strerror(errno)); + + if (priv->pidfile && unlink(priv->pidfile) && errno != ENOENT) + VIR_WARN("Failed to remove PID file for %s: %s", + vm->def->name, g_strerror(errno)); +} + int virCHProcessStop(virCHDriver *driver, virDomainObj *vm, @@ -1056,6 +1077,10 @@ virCHProcessStop(virCHDriver *driver, virHostdevReAttachDomainDevices(driver->hostdevMgr, CH_DRIVER_NAME, def, hostdev_flags); + virDomainObjRemoveTransientDef(vm); + + virCHProcessRemoveDomainStatus(driver, vm); + virErrorRestore(&orig_err); return 0; } -- 2.48.1

Previously, if any domain was still running, the driver was unable to reconnect to it, preventing further interactions with running domain. To resolve this, the driver now attempts to reconnect to the domains' monitors, which transient definitions are stored in state dir, during the initialization step. This allows us to perform further actions on domains even if the CH driver was restarted. Resolves: https://gitlab.com/libvirt/libvirt/-/issues/743 Signed-off-by: Kirill Shchetiniuk <kshcheti@redhat.com> --- src/ch/ch_driver.c | 2 + src/ch/ch_monitor.c | 65 +++++++++++++++++++++++++ src/ch/ch_monitor.h | 2 + src/ch/ch_process.c | 116 ++++++++++++++++++++++++++++++++++++++++++++ src/ch/ch_process.h | 2 + 5 files changed, 187 insertions(+) diff --git a/src/ch/ch_driver.c b/src/ch/ch_driver.c index 4ce68c9299..465528baf1 100644 --- a/src/ch/ch_driver.c +++ b/src/ch/ch_driver.c @@ -1489,6 +1489,8 @@ chStateInitialize(bool privileged, NULL, NULL) < 0) goto cleanup; + virCHProcessReconnectAll(ch_driver); + ch_driver->chCaps = virCHCapsInitCHVersionCaps(ch_driver->version); ch_driver->privileged = privileged; diff --git a/src/ch/ch_monitor.c b/src/ch/ch_monitor.c index 1dc085a755..4ea79506ce 100644 --- a/src/ch/ch_monitor.c +++ b/src/ch/ch_monitor.c @@ -581,6 +581,71 @@ chMonitorCreateSocket(const char *socket_path) return -1; } +virCHMonitor * +virCHMonitorReconnectNew(virDomainObj *vm, virCHDriverConfig *cfg) +{ + g_autoptr(virCHMonitor) mon = NULL; + virCHDomainObjPrivate *priv = vm->privateData; + int event_monitor_fd; + int rv; + + if (virCHMonitorInitialize() < 0) + return NULL; + + if (!(mon = virObjectLockableNew(virCHMonitorClass))) + return NULL; + + mon->eventmonitorfd = -1; + + if (!vm->def) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("VM is not defined")); + return NULL; + } + + mon->socketpath = g_strdup_printf("%s/%s-socket", + cfg->stateDir, vm->def->name); + + mon->eventmonitorpath = g_strdup_printf("%s/%s-event-monitor-fifo", + cfg->stateDir, vm->def->name); + + if ((rv = virPidFileReadPathIfLocked(priv->pidfile, &mon->pid)) < 0) { + VIR_WARN("Pidfile of %1$s can't be read error", vm->def->name); + return NULL; + } + + if (mon->pid == -1) { + VIR_WARN("Vm %1$s isn't running", vm->def->name); + return NULL; + } + + VIR_DEBUG("CH vm=%p name=%s is running as pid=%lld", vm, vm->def->name, (long long)vm->pid); + + /* open the reader end of fifo before start Event Handler */ + while ((event_monitor_fd = open(mon->eventmonitorpath, O_RDONLY)) < 0) { + if (errno == EINTR) { + g_usleep(100000); + continue; + } + VIR_ERROR(_("%1$s: Failed to open the event monitor FIFO(%2$s) read end!"), + vm->def->name, mon->eventmonitorpath); + return NULL; + } + + mon->eventmonitorfd = event_monitor_fd; + VIR_DEBUG("%s: Opened the event monitor FIFO(%s)", vm->def->name, mon->eventmonitorpath); + + mon->vm = virObjectRef(vm); + + if (virCHStartEventHandler(mon) < 0) + return NULL; + + mon->handle = curl_easy_init(); + + return g_steal_pointer(&mon); +} + + virCHMonitor * virCHMonitorNew(virDomainObj *vm, virCHDriverConfig *cfg, int logfile) { diff --git a/src/ch/ch_monitor.h b/src/ch/ch_monitor.h index 185de0dbfd..f6d7119c96 100644 --- a/src/ch/ch_monitor.h +++ b/src/ch/ch_monitor.h @@ -143,3 +143,5 @@ virCHMonitorBuildNetJson(virDomainNetDef *netdef, int virCHMonitorBuildRestoreJson(virDomainDef *vmdef, const char *from, char **jsonstr); + +virCHMonitor *virCHMonitorReconnectNew(virDomainObj *vm, virCHDriverConfig *cfg); diff --git a/src/ch/ch_process.c b/src/ch/ch_process.c index 08331352a4..d7a5ecc09c 100644 --- a/src/ch/ch_process.c +++ b/src/ch/ch_process.c @@ -1194,3 +1194,119 @@ virCHProcessStartRestore(virCHDriver *driver, virDomainObj *vm, const char *from virCHProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_FAILED); return ret; } + + +struct virCHProcessReconnectData +{ + virDomainObj *vm; + virCHDriver *driver; +}; + + +static void +virCHProcessReconnect(void *opaque) +{ + struct virCHProcessReconnectData *data = opaque; + virDomainObj *vm = data->vm; + virCHDriver *driver = data->driver; + g_autoptr(virCHDriverConfig) cfg = virCHDriverGetConfig(driver); + virCHDomainObjPrivate *priv = vm->privateData; + + if (virDomainObjBeginJob(vm, VIR_JOB_MODIFY)) + goto error; + + VIR_DEBUG("Reconnecting to vm=%p name=%s", vm, vm->def->name); + + /* Build pidfile path where is running domain pid stored */ + if (!(priv->pidfile = virPidFileBuildPath(cfg->stateDir, vm->def->name))) { + virReportSystemError(errno, "%s", _("Failed to build pidfile path.")); + goto error; + } + + /* Create new monitor object without new CH startup */ + if (!priv->monitor) { + if (!(priv->monitor = virCHMonitorReconnectNew(vm, cfg))) + goto error; + } + + vm->pid = priv->monitor->pid; + vm->def->id = vm->pid; + priv->machineName = virCHDomainGetMachineName(vm); + + if (!priv->machineName) + goto error; + + if (virDomainCgroupConnectCgroup("ch", + vm, + &priv->cgroup, + cfg->cgroupControllers, + priv->driver->privileged, + priv->machineName) < 0) + goto error; + + + if (virDomainInterfaceStartDevices(vm->def) < 0) + goto error; + + virCHProcessUpdateInfo(vm); + + if (virDomainObjSave(vm, driver->xmlopt, cfg->stateDir) < 0) + goto error; + + cleanup: + virDomainObjEndJob(vm); + virDomainObjEndAPI(&vm); + return; + + error: + virCHProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_FAILED); + + goto cleanup; +} + +static int +virCHProcessReconnectHelper(virDomainObj *vm, void *opaque) +{ + struct virCHProcessReconnectData *src = opaque; + struct virCHProcessReconnectData *data; + virThread thread; + g_autofree char *name = NULL; + + /* Skip reconnect is domain if is not running */ + if (vm->pid == 0) + return 0; + + data = g_new0(struct virCHProcessReconnectData, 1); + + memcpy(data, src, sizeof(*data)); + data->vm = vm; + + virObjectLock(vm); + virObjectRef(vm); + + name = g_strdup_printf("reconnect-%s", vm->def->name); + + VIR_DEBUG("Reconnecting vm=%p name=%s", vm, vm->def->name); + + if (virThreadCreateFull(&thread, false, virCHProcessReconnect, + name, false, data) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Could not create thread.")); + + virCHProcessStop(src->driver, vm, VIR_DOMAIN_SHUTOFF_FAILED); + + virDomainObjEndAPI(&vm); + VIR_FREE(data); + return -1; + } + + return 0; +} + +void +virCHProcessReconnectAll(virCHDriver *driver) +{ + struct virCHProcessReconnectData data = {.driver = driver}; + virDomainObjListForEach(driver->domains, true, + virCHProcessReconnectHelper, &data); +} diff --git a/src/ch/ch_process.h b/src/ch/ch_process.h index 7a6995b7cf..ff8267b497 100644 --- a/src/ch/ch_process.h +++ b/src/ch/ch_process.h @@ -38,3 +38,5 @@ int virCHProcessStartRestore(virCHDriver *driver, const char *from); int virCHProcessUpdateInfo(virDomainObj *vm); + +void virCHProcessReconnectAll(virCHDriver *driver); -- 2.48.1

Previously, the CH driver would timeout even if one or more domains were still running, only an active virsh session prevented the driver's timeout. To address this, an inhibitor has been added to ensure driver remains active as long as any domain is still running. Inhibitor is now held when a domain starts up or when the driver successfully reconnects to a running domain. This now prevents the driver from timeout while any domain is still running. Inhibitor is later released with domain shutdown, ensuring correct running domains count. Resolves: https://gitlab.com/libvirt/libvirt/-/issues/743 Signed-off-by: Kirill Shchetiniuk <kshcheti@redhat.com> --- src/ch/ch_conf.h | 4 ++++ src/ch/ch_driver.c | 15 +++++++++++++-- src/ch/ch_process.c | 6 ++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/ch/ch_conf.h b/src/ch/ch_conf.h index 8beeb69b95..ca9151e5d8 100644 --- a/src/ch/ch_conf.h +++ b/src/ch/ch_conf.h @@ -26,6 +26,7 @@ #include "ch_capabilities.h" #include "virebtables.h" #include "object_event.h" +#include "virinhibitor.h" #define CH_DRIVER_NAME "CH" #define CH_CMD "cloud-hypervisor" @@ -92,6 +93,9 @@ struct _virCHDriver /* Immutable pointer, self-locking APIs */ virObjectEventState *domainEventState; + + /* Immutable pointer, self-locking APIs */ + virInhibitor *inhibitor; }; #define CH_SAVE_MAGIC "libvirt-xml\n \0 \r" diff --git a/src/ch/ch_driver.c b/src/ch/ch_driver.c index 465528baf1..2d72de3ed2 100644 --- a/src/ch/ch_driver.c +++ b/src/ch/ch_driver.c @@ -42,6 +42,7 @@ #include "viruuid.h" #include "virnuma.h" #include "virhostmem.h" +#include "virinhibitor.h" #define VIR_FROM_THIS VIR_FROM_CH @@ -1411,6 +1412,7 @@ static int chStateCleanup(void) virObjectUnref(ch_driver->domains); virObjectUnref(ch_driver->hostdevMgr); virObjectUnref(ch_driver->domainEventState); + virInhibitorFree(ch_driver->inhibitor); virMutexDestroy(&ch_driver->lock); g_clear_pointer(&ch_driver, g_free); @@ -1421,8 +1423,8 @@ static virDrvStateInitResult chStateInitialize(bool privileged, const char *root, bool monolithic G_GNUC_UNUSED, - virStateInhibitCallback callback G_GNUC_UNUSED, - void *opaque G_GNUC_UNUSED) + virStateInhibitCallback callback, + void *opaque) { int ret = VIR_DRV_STATE_INIT_ERROR; int rv; @@ -1473,6 +1475,15 @@ chStateInitialize(bool privileged, goto cleanup; } + /* Setup inhibitor to be able to avoid timeout */ + ch_driver->inhibitor = virInhibitorNew(VIR_INHIBITOR_WHAT_SHUTDOWN, + _("Libvirt Cloud-Hypervisor"), + _("Cloud-Hypervisor virtual machines are running"), + VIR_INHIBITOR_MODE_DELAY, + callback, + opaque); + + /* Transient domains load */ if (virDomainObjListLoadAllConfigs(ch_driver->domains, cfg->stateDir, diff --git a/src/ch/ch_process.c b/src/ch/ch_process.c index d7a5ecc09c..8f61a5f220 100644 --- a/src/ch/ch_process.c +++ b/src/ch/ch_process.c @@ -997,6 +997,8 @@ virCHProcessStart(virCHDriver *driver, virCHDomainSaveStatus(vm); + virInhibitorHold(driver->inhibitor); + return 0; cleanup: @@ -1072,6 +1074,8 @@ virCHProcessStop(virCHDriver *driver, vm->def->id = -1; g_clear_pointer(&priv->machineName, g_free); + virInhibitorRelease(driver->inhibitor); + virDomainObjSetState(vm, VIR_DOMAIN_SHUTOFF, reason); virHostdevReAttachDomainDevices(driver->hostdevMgr, CH_DRIVER_NAME, def, @@ -1253,6 +1257,8 @@ virCHProcessReconnect(void *opaque) if (virDomainObjSave(vm, driver->xmlopt, cfg->stateDir) < 0) goto error; + virInhibitorHold(driver->inhibitor); + cleanup: virDomainObjEndJob(vm); virDomainObjEndAPI(&vm); -- 2.48.1
participants (1)
-
Kirill Shchetiniuk