From 0526e5f5f5a070e5396399f004c4e252cb65ac2c Mon Sep 17 00:00:00 2001 From: Roman Bogorodskiy Date: Sat, 4 Jul 2026 07:26:57 +0200 Subject: [PATCH] bhyve: implement the virDomainRename API Implementation is fairly similar to the one found in the test and qemu drivers. Lack of checkpoint and snapshot support in the bhyve driver makes the implementation a bit simpler. Signed-off-by: Roman Bogorodskiy Reviewed-by: Peter Krempa --- src/bhyve/bhyve_domain.c | 27 +++++++++ src/bhyve/bhyve_domain.h | 1 + src/bhyve/bhyve_driver.c | 125 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 153 insertions(+) diff --git a/src/bhyve/bhyve_domain.c b/src/bhyve/bhyve_domain.c index 86e4de1fd8..d5bb22501c 100644 --- a/src/bhyve/bhyve_domain.c +++ b/src/bhyve/bhyve_domain.c @@ -703,3 +703,30 @@ virBhyveDomainObjStopWorker(virDomainObj *dom) g_object_unref(eventThread); virObjectLock(dom); } + +int +bhyveDomainNamePathsCleanup(const char *name, + bool bestEffort) +{ + g_autofree char *cfg_file = NULL; + g_autofree char *autostart_link = NULL; + + cfg_file = virDomainConfigFile(BHYVE_CONFIG_DIR, name); + autostart_link = virDomainConfigFile(BHYVE_AUTOSTART_DIR, name); + + if (virFileExists(cfg_file) && + unlink(cfg_file) < 0) { + virReportSystemError(errno, _("Failed to unlink '%1$s'"), cfg_file); + if (!bestEffort) + return -1; + } + + if (virFileIsLink(autostart_link) == 1 && + unlink(autostart_link) < 0) { + virReportSystemError(errno, _("Failed to unlink '%1$s'"), autostart_link); + if (!bestEffort) + return -1; + } + + return 0; +} diff --git a/src/bhyve/bhyve_domain.h b/src/bhyve/bhyve_domain.h index 888ef2f84b..fa7a685d59 100644 --- a/src/bhyve/bhyve_domain.h +++ b/src/bhyve/bhyve_domain.h @@ -54,3 +54,4 @@ extern virXMLNamespace virBhyveDriverDomainXMLNamespace; int virBhyveDomainObjStartWorker(virDomainObj *dom); void virBhyveDomainObjStopWorker(virDomainObj *dom); +int bhyveDomainNamePathsCleanup(const char *name, bool bestEffort); diff --git a/src/bhyve/bhyve_driver.c b/src/bhyve/bhyve_driver.c index 45e8aad5b5..668e46ad37 100644 --- a/src/bhyve/bhyve_driver.c +++ b/src/bhyve/bhyve_driver.c @@ -2683,6 +2683,130 @@ bhyveDomainAuthorizedSSHKeysSet(virDomainPtr domain, return rv; } +static int +bhyveDomainRenameCallback(virDomainObj *vm, + const char *new_name, + unsigned int flags, + void *opaque) +{ + struct _bhyveConn *privconn = opaque; + virObjectEvent *event_new = NULL; + virObjectEvent *event_old = NULL; + int ret = -1; + virErrorPtr err = NULL; + g_autofree char *new_dom_name = NULL; + g_autofree char *old_dom_name = NULL; + g_autofree char *new_dom_cfg_file = NULL; + g_autofree char *new_dom_autostart_link = NULL; + + virCheckFlags(0, ret); + + if (strchr(new_name, '/')) { + virReportError(VIR_ERR_XML_ERROR, + _("name %1$s cannot contain '/'"), new_name); + return -1; + } + + new_dom_name = g_strdup(new_name); + + new_dom_cfg_file = virDomainConfigFile(BHYVE_CONFIG_DIR, new_dom_name); + + if (bhyveDomainNamePathsCleanup(new_name, false) < 0) + goto cleanup; + + if (vm->autostart) { + new_dom_autostart_link = virDomainConfigFile(BHYVE_AUTOSTART_DIR, new_dom_name); + + if (symlink(new_dom_cfg_file, new_dom_autostart_link) < 0) { + virReportSystemError(errno, + _("Failed to create symlink '%1$s' to '%2$s'"), + new_dom_autostart_link, new_dom_cfg_file); + return -1; + } + } + + /* Switch name in domain definition. */ + old_dom_name = g_steal_pointer(&vm->def->name); + vm->def->name = g_steal_pointer(&new_dom_name); + + if (virDomainDefSave(vm->def, privconn->xmlopt, BHYVE_CONFIG_DIR) < 0) + goto cleanup; + + event_old = virDomainEventLifecycleNew(vm->def->id, old_dom_name, vm->def->uuid, + VIR_DOMAIN_EVENT_UNDEFINED, + VIR_DOMAIN_EVENT_UNDEFINED_RENAMED); + event_new = virDomainEventLifecycleNewFromObj(vm, + VIR_DOMAIN_EVENT_DEFINED, + VIR_DOMAIN_EVENT_DEFINED_RENAMED); + virObjectEventStateQueue(privconn->domainEventState, event_old); + virObjectEventStateQueue(privconn->domainEventState, event_new); + ret = 0; + + cleanup: + if (ret < 0) { + if (old_dom_name) { + new_dom_name = vm->def->name; + vm->def->name = old_dom_name; + old_dom_name = NULL; + } + + virErrorPreserveLast(&err); + bhyveDomainNamePathsCleanup(new_dom_name, true); + } else { + bhyveDomainNamePathsCleanup(old_dom_name, true); + } + + virErrorRestore(&err); + return ret; +} + +static int +bhyveDomainRename(virDomainPtr domain, + const char *new_name, + unsigned int flags) +{ + struct _bhyveConn *privconn = domain->conn->privateData; + virDomainObj *vm = NULL; + int ret = -1; + + virCheckFlags(0, ret); + + if (!(vm = bhyveDomObjFromDomain(domain))) + goto cleanup; + + if (virDomainRenameEnsureACL(domain->conn, vm->def) < 0) + goto cleanup; + + if (virDomainObjBeginJob(vm, VIR_JOB_MODIFY) < 0) + goto cleanup; + + if (!vm->persistent) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("cannot rename a transient domain")); + goto endjob; + } + + if (virDomainObjGetState(vm, NULL) != VIR_DOMAIN_SHUTOFF) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain has to be shutoff before renaming")); + goto endjob; + } + + if (virDomainObjListRename(privconn->domains, vm, new_name, flags, + bhyveDomainRenameCallback, privconn) < 0) + goto endjob; + + /* Success, domain has been renamed. */ + ret = 0; + + endjob: + virDomainObjEndJob(vm); + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} + static virHypervisorDriver bhyveHypervisorDriver = { .name = "bhyve", .connectURIProbe = bhyveConnectURIProbe, @@ -2760,6 +2884,7 @@ static virHypervisorDriver bhyveHypervisorDriver = { .domainSetUserPassword = bhyveDomainSetUserPassword, /* 12.6.0 */ .domainAuthorizedSSHKeysGet = bhyveDomainAuthorizedSSHKeysGet, /* 12.6.0 */ .domainAuthorizedSSHKeysSet = bhyveDomainAuthorizedSSHKeysSet, /* 12.6.0 */ + .domainRename = bhyveDomainRename, /* 12.6.0 */ };