[libvirt] [PATCH v3 0/5] domainRename API implementation

This is an effort to implement domain rename API. Presented patch series consists of the following: virDomainRename API implementation for qemu, implementation of the virsh command domrename and the additional support code. The idea behind this endeavor is to provide convenient and safe way to rename a domain. Instead of the: virsh dumpxml domain > domain.xml (change domain name in domain.xml) virsh undefine domain virsh define domain.xml user can simply type: virsh domrename foo bar or call virDomainRename() API and domain "foo" will be renamed to "bar". We currently support only renaming inactive domains without snapshots. Renaming procedure takes care of domain log, config, guest agent path and should be able to recover in case of failure. I've been working on this functionality in collaboration with Michal Privoznik who is my mentor during the GSoC 2015. If you have any questions, ideas or criticism feel free to join the discussion. v2: - removed guest agent path rename code - removed rename permission - added code for emitting undefined+renamed event for the old domain v3: - removed domain rename permission - fixed virDomainRename doc comment - added @flags parameter to the virDomainRename API Tomas Meszaros (5): Introduce virDomainRename API virsh: Implement "domrename" command domain_conf: Introducde virDomainObjListRenameAddNew() & virDomainObjListRenameRemove() Introduce new VIR_DOMAIN_EVENT_DEFINED_RENAMED event qemu: Implement virDomainRename examples/object-events/event-test.c | 4 + include/libvirt/libvirt-domain.h | 6 ++ src/conf/domain_conf.c | 35 +++++++++ src/conf/domain_conf.h | 5 ++ src/driver-hypervisor.h | 6 ++ src/libvirt-domain.c | 34 +++++++++ src/libvirt_private.syms | 2 + src/libvirt_public.syms | 5 ++ src/qemu/qemu_driver.c | 145 ++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 18 ++++- src/remote_protocol-structs | 8 ++ tools/virsh-domain.c | 63 +++++++++++++++- tools/virsh.pod | 7 ++ 14 files changed, 336 insertions(+), 3 deletions(-) -- 2.1.0

Also, among with this new API new ACL that restricts rename capability is invented too. Signed-off-by: Tomas Meszaros <exo@tty.sk> --- include/libvirt/libvirt-domain.h | 4 ++++ src/driver-hypervisor.h | 6 ++++++ src/libvirt-domain.c | 34 ++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 5 +++++ src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 18 +++++++++++++++++- src/remote_protocol-structs | 8 ++++++++ 7 files changed, 75 insertions(+), 1 deletion(-) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index e8202cf..82f86bb 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -3837,4 +3837,8 @@ int virDomainSetUserPassword(virDomainPtr dom, const char *password, unsigned int flags); +int virDomainRename(virDomainPtr dom, + const char *new_name, + unsigned int flags); + #endif /* __VIR_LIBVIRT_DOMAIN_H__ */ diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h index 3275343..ae2ec4d 100644 --- a/src/driver-hypervisor.h +++ b/src/driver-hypervisor.h @@ -650,6 +650,11 @@ typedef int (*virDrvDomainIsActive)(virDomainPtr dom); typedef int +(*virDrvDomainRename)(virDomainPtr dom, + const char *new_name, + unsigned int flags); + +typedef int (*virDrvDomainIsPersistent)(virDomainPtr dom); typedef int @@ -1347,6 +1352,7 @@ struct _virHypervisorDriver { virDrvConnectIsEncrypted connectIsEncrypted; virDrvConnectIsSecure connectIsSecure; virDrvDomainIsActive domainIsActive; + virDrvDomainRename domainRename; virDrvDomainIsPersistent domainIsPersistent; virDrvDomainIsUpdated domainIsUpdated; virDrvConnectCompareCPU connectCompareCPU; diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index 837933f..b8618d8 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -8774,6 +8774,40 @@ virDomainIsPersistent(virDomainPtr dom) return -1; } +/** + * virDomainRename: + * @dom: pointer to the domain object + * @new_name: new domain name + * @flags: extra flags; not used yet, so callers should always pass 0 + * + * Rename a domain. New domain name is specified in the second + * argument. Depending on each driver implementation it may be + * required that domain is in a specific state. + * + * Returns 0 if renamed, -1 on error + */ +int +virDomainRename(virDomainPtr dom, const char *new_name, + unsigned int flags ATTRIBUTE_UNUSED) +{ + VIR_DEBUG("dom=%p, new_name=%s", dom, NULLSTR(new_name)); + + virResetLastError(); + virCheckDomainReturn(dom, -1); + virCheckNonNullArgGoto(new_name, error); + + if (dom->conn->driver->domainRename) { + int ret = dom->conn->driver->domainRename(dom, new_name, flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + error: + virDispatchError(dom->conn); + return -1; +} /** * virDomainIsUpdated: diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 2c653f2..dd94191 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -720,4 +720,9 @@ LIBVIRT_1.2.17 { virTypedParamsAddStringList; } LIBVIRT_1.2.16; +LIBVIRT_1.2.19 { + global: + virDomainRename; +} LIBVIRT_1.2.17; + # .... define new API here using predicted next version number .... diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 5c4cf7c..ec26ebe 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -8391,6 +8391,7 @@ static virHypervisorDriver hypervisor_driver = { .domainGetFSInfo = remoteDomainGetFSInfo, /* 1.2.11 */ .domainInterfaceAddresses = remoteDomainInterfaceAddresses, /* 1.2.14 */ .domainSetUserPassword = remoteDomainSetUserPassword, /* 1.2.16 */ + .domainRename = remoteDomainRename, /* 1.2.19 */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 9f1be6b..770aa72 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -3230,6 +3230,15 @@ struct remote_domain_set_user_password_args { unsigned int flags; }; +struct remote_domain_rename_args { + remote_nonnull_domain dom; + remote_string new_name; + unsigned int flags; +}; + +struct remote_domain_rename_ret { + int rename; +}; /*----- Protocol. -----*/ @@ -5696,5 +5705,12 @@ enum remote_procedure { * @generate:both * @acl: domain:set_password */ - REMOTE_PROC_DOMAIN_SET_USER_PASSWORD = 357 + REMOTE_PROC_DOMAIN_SET_USER_PASSWORD = 357, + + /** + * @generate: both + * @acl: domain:write + * @acl: domain:save + */ + REMOTE_PROC_DOMAIN_RENAME = 358 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 48c3bd8..1fdaa55 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2684,6 +2684,13 @@ struct remote_domain_set_user_password_args { remote_string password; u_int flags; }; +struct remote_domain_rename_args { + remote_nonnull_domain dom; + remote_string new_name; +}; +struct remote_domain_rename_ret { + int rename; +}; enum remote_procedure { REMOTE_PROC_CONNECT_OPEN = 1, REMOTE_PROC_CONNECT_CLOSE = 2, @@ -3042,4 +3049,5 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_ADD_IOTHREAD = 355, REMOTE_PROC_DOMAIN_DEL_IOTHREAD = 356, REMOTE_PROC_DOMAIN_SET_USER_PASSWORD = 357, + REMOTE_PROC_DOMAIN_RENAME = 358, }; -- 2.1.0

On 10.08.2015 21:59, Tomas Meszaros wrote:
Also, among with this new API new ACL that restricts rename capability is invented too.
Signed-off-by: Tomas Meszaros <exo@tty.sk> --- include/libvirt/libvirt-domain.h | 4 ++++ src/driver-hypervisor.h | 6 ++++++ src/libvirt-domain.c | 34 ++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 5 +++++ src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 18 +++++++++++++++++- src/remote_protocol-structs | 8 ++++++++ 7 files changed, 75 insertions(+), 1 deletion(-)
diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index e8202cf..82f86bb 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -3837,4 +3837,8 @@ int virDomainSetUserPassword(virDomainPtr dom, const char *password, unsigned int flags);
+int virDomainRename(virDomainPtr dom, + const char *new_name, + unsigned int flags); + #endif /* __VIR_LIBVIRT_DOMAIN_H__ */ diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h index 3275343..ae2ec4d 100644 --- a/src/driver-hypervisor.h +++ b/src/driver-hypervisor.h @@ -650,6 +650,11 @@ typedef int (*virDrvDomainIsActive)(virDomainPtr dom);
typedef int +(*virDrvDomainRename)(virDomainPtr dom, + const char *new_name, + unsigned int flags); + +typedef int (*virDrvDomainIsPersistent)(virDomainPtr dom);
typedef int @@ -1347,6 +1352,7 @@ struct _virHypervisorDriver { virDrvConnectIsEncrypted connectIsEncrypted; virDrvConnectIsSecure connectIsSecure; virDrvDomainIsActive domainIsActive; + virDrvDomainRename domainRename; virDrvDomainIsPersistent domainIsPersistent; virDrvDomainIsUpdated domainIsUpdated; virDrvConnectCompareCPU connectCompareCPU; diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index 837933f..b8618d8 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -8774,6 +8774,40 @@ virDomainIsPersistent(virDomainPtr dom) return -1; }
+/** + * virDomainRename: + * @dom: pointer to the domain object + * @new_name: new domain name + * @flags: extra flags; not used yet, so callers should always pass 0 + * + * Rename a domain. New domain name is specified in the second + * argument. Depending on each driver implementation it may be + * required that domain is in a specific state. + * + * Returns 0 if renamed, -1 on error + */ +int +virDomainRename(virDomainPtr dom, const char *new_name, + unsigned int flags ATTRIBUTE_UNUSED)
Why? @flags are clearly used just a few lines below.
+{ + VIR_DEBUG("dom=%p, new_name=%s", dom, NULLSTR(new_name)); + + virResetLastError(); + virCheckDomainReturn(dom, -1); + virCheckNonNullArgGoto(new_name, error); + + if (dom->conn->driver->domainRename) { + int ret = dom->conn->driver->domainRename(dom, new_name, flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + error: + virDispatchError(dom->conn); + return -1; +}
/** * virDomainIsUpdated: diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 2c653f2..dd94191 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -720,4 +720,9 @@ LIBVIRT_1.2.17 { virTypedParamsAddStringList; } LIBVIRT_1.2.16;
+LIBVIRT_1.2.19 { + global: + virDomainRename; +} LIBVIRT_1.2.17; + # .... define new API here using predicted next version number .... diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 5c4cf7c..ec26ebe 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -8391,6 +8391,7 @@ static virHypervisorDriver hypervisor_driver = { .domainGetFSInfo = remoteDomainGetFSInfo, /* 1.2.11 */ .domainInterfaceAddresses = remoteDomainInterfaceAddresses, /* 1.2.14 */ .domainSetUserPassword = remoteDomainSetUserPassword, /* 1.2.16 */ + .domainRename = remoteDomainRename, /* 1.2.19 */ };
static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 9f1be6b..770aa72 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -3230,6 +3230,15 @@ struct remote_domain_set_user_password_args { unsigned int flags; };
+struct remote_domain_rename_args { + remote_nonnull_domain dom; + remote_string new_name; + unsigned int flags; +}; + +struct remote_domain_rename_ret { + int rename; +};
/*----- Protocol. -----*/
@@ -5696,5 +5705,12 @@ enum remote_procedure { * @generate:both * @acl: domain:set_password */ - REMOTE_PROC_DOMAIN_SET_USER_PASSWORD = 357 + REMOTE_PROC_DOMAIN_SET_USER_PASSWORD = 357, + + /** + * @generate: both + * @acl: domain:write + * @acl: domain:save + */ + REMOTE_PROC_DOMAIN_RENAME = 358 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 48c3bd8..1fdaa55 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2684,6 +2684,13 @@ struct remote_domain_set_user_password_args { remote_string password; u_int flags; }; +struct remote_domain_rename_args { + remote_nonnull_domain dom; + remote_string new_name;
need to add the flags: u_int flags;
+}; +struct remote_domain_rename_ret { + int rename; +}; enum remote_procedure { REMOTE_PROC_CONNECT_OPEN = 1, REMOTE_PROC_CONNECT_CLOSE = 2, @@ -3042,4 +3049,5 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_ADD_IOTHREAD = 355, REMOTE_PROC_DOMAIN_DEL_IOTHREAD = 356, REMOTE_PROC_DOMAIN_SET_USER_PASSWORD = 357, + REMOTE_PROC_DOMAIN_RENAME = 358, };
Michal

This patch implements new virsh command, domrename. Using domrename, it will be possible to rename domain from the virsh shell by calling virRenameDomain API. It takes two arguments, current domain name and new domain name. Example: virsh # list --all Id Name State ---------------------------------------------------- - bar shut off virsh # domrename bar foo Domain successfully renamed virsh # list --all Id Name State ---------------------------------------------------- - foo shut off virsh # Signed-off-by: Tomas Meszaros <exo@tty.sk> --- tools/virsh-domain.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 7 +++++++ 2 files changed, 64 insertions(+) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 4988ba2..128cfac 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -9724,6 +9724,57 @@ cmdDomname(vshControl *ctl, const vshCmd *cmd) } /* + * "domrename" command + */ +static const vshCmdInfo info_domrename[] = { + {.name = "help", + .data = N_("rename a domain") + }, + {.name = "desc", + .data = "Rename an inactive domain." + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_domrename[] = { + {.name = "domain", + .type = VSH_OT_DATA, + .flags = VSH_OFLAG_REQ, + .help = N_("domain name, id or uuid") + }, + {.name = "new-name", + .type = VSH_OT_DATA, + .flags = VSH_OFLAG_REQ, + .help = N_("new domain name") + }, + {.name = NULL} +}; + +static bool +cmdDomrename(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom; + const char *new_name = NULL; + bool ret = false; + + if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + return ret; + + if (vshCommandOptStringReq(ctl, cmd, "new-name", &new_name) < 0) + goto cleanup; + + if (virDomainRename(dom, new_name, 0) < 0) + goto cleanup; + + vshPrint(ctl, "Domain successfully renamed\n"); + ret = true; + + cleanup: + virDomainFree(dom); + return false; +} + +/* * "domid" command */ static const vshCmdInfo info_domid[] = { @@ -13080,6 +13131,12 @@ const vshCmdDef domManagementCmds[] = { .info = info_domname, .flags = 0 }, + {.name = "domrename", + .handler = cmdDomrename, + .opts = opts_domrename, + .info = info_domrename, + .flags = 0 + }, {.name = "dompmsuspend", .handler = cmdDomPMSuspend, .opts = opts_dom_pm_suspend, diff --git a/tools/virsh.pod b/tools/virsh.pod index 5ee9a96..c049f8f 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -1258,6 +1258,13 @@ on both of them). Convert a domain Id (or UUID) to domain name +=item B<domrename> I<domain> I<new-name> + +Rename a domain. This command changes current domain name to the new name +specified in the second argument. + +B<Note>: Domain must be inactive and without snapshots. + =item B<domstate> I<domain> [I<--reason>] Returns state about a domain. I<--reason> tells virsh to also print -- 2.1.0

We just need to update the entry in the second hash table. Since commit 8728a56 we have two hash tables for the domain list so that we can do O(1) lookup regardless of looking up by UUID or name. Since with renaming a domain UUID does not change, we only need to update the second hash table, where domains are referenced by their name. We will call both functions from the qemuDomainRename(). Signed-off-by: Tomas Meszaros <exo@tty.sk> --- src/conf/domain_conf.c | 35 +++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 5 +++++ src/libvirt_private.syms | 2 ++ 3 files changed, 42 insertions(+) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index e4114f8..4edf37d 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -2848,6 +2848,41 @@ virDomainObjPtr virDomainObjListAdd(virDomainObjListPtr doms, return ret; } + +int +virDomainObjListRenameAddNew(virDomainObjListPtr doms, + virDomainObjPtr vm, + const char *name) +{ + int ret = -1; + virObjectLock(doms); + + /* Add new name into the hash table of domain names. */ + if (virHashAddEntry(doms->objsName, name, vm) < 0) + goto cleanup; + + /* Okay, this is crazy. virHashAddEntry() does not increment + * the refcounter of @vm, but virHashRemoveEntry() does + * decrement it. We need to work around it. */ + virObjectRef(vm); + + ret = 0; + cleanup: + virObjectUnlock(doms); + return ret; +} + + +int +virDomainObjListRenameRemove(virDomainObjListPtr doms, const char *name) +{ + virObjectLock(doms); + virHashRemoveEntry(doms->objsName, name); + virObjectUnlock(doms); + return 0; +} + + /* * Mark the running VM config as transient. Ensures transient hotplug * operations do not persist past shutdown. diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 698a4d2..9e6f7b8 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2545,6 +2545,11 @@ virDomainObjPtr virDomainObjListAdd(virDomainObjListPtr doms, virDomainXMLOptionPtr xmlopt, unsigned int flags, virDomainDefPtr *oldDef); +int virDomainObjListRenameAddNew(virDomainObjListPtr doms, + virDomainObjPtr vm, + const char *name); +int virDomainObjListRenameRemove(virDomainObjListPtr doms, + const char *name); void virDomainObjAssignDef(virDomainObjPtr domain, virDomainDefPtr def, bool live, diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index e5d8437..3a206d3 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -408,6 +408,8 @@ virDomainObjListNew; virDomainObjListNumOfDomains; virDomainObjListRemove; virDomainObjListRemoveLocked; +virDomainObjListRenameAddNew; +virDomainObjListRenameRemove; virDomainObjNew; virDomainObjParseNode; virDomainObjSetDefTransient; -- 2.1.0

This should be emitted whenever a domain is renamed. Signed-off-by: Tomas Meszaros <exo@tty.sk> --- examples/object-events/event-test.c | 4 ++++ include/libvirt/libvirt-domain.h | 2 ++ tools/virsh-domain.c | 6 ++++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/examples/object-events/event-test.c b/examples/object-events/event-test.c index 4f17273..dcae981 100644 --- a/examples/object-events/event-test.c +++ b/examples/object-events/event-test.c @@ -108,10 +108,14 @@ static const char *eventDetailToString(int event, int detail) { ret = "Added"; else if (detail == VIR_DOMAIN_EVENT_DEFINED_UPDATED) ret = "Updated"; + else if (detail == VIR_DOMAIN_EVENT_DEFINED_RENAMED) + ret = "Renamed"; break; case VIR_DOMAIN_EVENT_UNDEFINED: if (detail == VIR_DOMAIN_EVENT_UNDEFINED_REMOVED) ret = "Removed"; + else if (detail == VIR_DOMAIN_EVENT_UNDEFINED_RENAMED) + ret = "Renamed"; break; case VIR_DOMAIN_EVENT_STARTED: switch ((virDomainEventStartedDetailType) detail) { diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 82f86bb..77304f0 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -2322,6 +2322,7 @@ typedef enum { typedef enum { VIR_DOMAIN_EVENT_DEFINED_ADDED = 0, /* Newly created config file */ VIR_DOMAIN_EVENT_DEFINED_UPDATED = 1, /* Changed config file */ + VIR_DOMAIN_EVENT_DEFINED_RENAMED = 2, /* Domain was renamed */ # ifdef VIR_ENUM_SENTINELS VIR_DOMAIN_EVENT_DEFINED_LAST @@ -2335,6 +2336,7 @@ typedef enum { */ typedef enum { VIR_DOMAIN_EVENT_UNDEFINED_REMOVED = 0, /* Deleted the config file */ + VIR_DOMAIN_EVENT_UNDEFINED_RENAMED = 1, /* Domain was renamed */ # ifdef VIR_ENUM_SENTINELS VIR_DOMAIN_EVENT_UNDEFINED_LAST diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 128cfac..2325360 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -11763,12 +11763,14 @@ VIR_ENUM_DECL(vshDomainEventDefined) VIR_ENUM_IMPL(vshDomainEventDefined, VIR_DOMAIN_EVENT_DEFINED_LAST, N_("Added"), - N_("Updated")) + N_("Updated"), + N_("Renamed")) VIR_ENUM_DECL(vshDomainEventUndefined) VIR_ENUM_IMPL(vshDomainEventUndefined, VIR_DOMAIN_EVENT_UNDEFINED_LAST, - N_("Removed")) + N_("Removed"), + N_("Renamed")) VIR_ENUM_DECL(vshDomainEventStarted) VIR_ENUM_IMPL(vshDomainEventStarted, -- 2.1.0

Currently supports only renaming inactive domains without snapshots. Signed-off-by: Tomas Meszaros <exo@tty.sk> --- src/qemu/qemu_driver.c | 145 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 6998e12..41e5fc4 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -19853,6 +19853,150 @@ qemuDomainSetUserPassword(virDomainPtr dom, } +static int qemuDomainRename(virDomainPtr dom, + const char *new_name, + unsigned int flags ATTRIBUTE_UNUSED) +{ + virQEMUDriverPtr driver = dom->conn->privateData; + virQEMUDriverConfigPtr cfg = NULL; + virDomainObjPtr vm; + virObjectEventPtr event_new = NULL; + virObjectEventPtr event_old = NULL; + int ret = -1; + int logfile = -1; + char ebuf[1024]; + char *timestamp; + char *rename_log_msg = NULL; + char *new_dom_name = NULL; + char *old_dom_name = NULL; + char *old_dom_cfg_file = NULL; + + if (VIR_STRDUP(new_dom_name, new_name) < 0) + goto cleanup; + + if (!(vm = qemuDomObjFromDomain(dom))) + goto cleanup; + + if (virDomainRenameEnsureACL(dom->conn, vm->def) < 0) + goto cleanup; + + cfg = virQEMUDriverGetConfig(driver); + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + + if (virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("cannot rename active domain")); + goto endjob; + } + + 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 (virDomainSnapshotObjListNum(vm->snapshots, NULL, 0) > 0) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("cannot rename domain with snapshots")); + goto endjob; + } + + if (virAsprintf(&rename_log_msg, ": domain %s has been renamed to %s\n", + vm->def->name, new_name) < 0) { + goto endjob; + } + + if (!(old_dom_cfg_file = virDomainConfigFile(cfg->configDir, + vm->def->name))) { + goto endjob; + } + + if (virDomainObjListRenameAddNew(driver->domains, vm, new_name) < 0) + goto endjob; + + if ((logfile = qemuDomainCreateLog(driver, vm, true)) < 0) + goto rollback; + + event_old = virDomainEventLifecycleNewFromObj(vm, + VIR_DOMAIN_EVENT_UNDEFINED, + VIR_DOMAIN_EVENT_UNDEFINED_RENAMED); + + /* Switch name in domain definition. */ + old_dom_name = vm->def->name; + vm->def->name = new_dom_name; + new_dom_name = NULL; + + if (virDomainSaveConfig(cfg->configDir, vm->def) < 0) + goto rollback; + + if (virFileExists(old_dom_cfg_file) && + unlink(old_dom_cfg_file) < 0) { + virReportSystemError(errno, + _("cannot remove old domain config file %s"), + old_dom_cfg_file); + goto rollback; + } + + /* Remove old domain name from table. */ + virDomainObjListRenameRemove(driver->domains, old_dom_name); + + event_new = virDomainEventLifecycleNewFromObj(vm, + VIR_DOMAIN_EVENT_DEFINED, + VIR_DOMAIN_EVENT_DEFINED_RENAMED); + + /* Write message to the log. */ + if ((timestamp = virTimeStringNow()) != NULL) { + if (safewrite(logfile, timestamp, strlen(timestamp)) < 0 || + safewrite(logfile, rename_log_msg, + strlen(rename_log_msg)) < 0) { + VIR_WARN("Unable to write timestamp to logfile: %s", + virStrerror(errno, ebuf, sizeof(ebuf))); + } + VIR_FREE(timestamp); + } + + /* Success, domain has been renamed. */ + ret = 0; + + endjob: + qemuDomainObjEndJob(driver, vm); + + cleanup: + if (VIR_CLOSE(logfile) < 0) { + VIR_WARN("Unable to close logfile: %s", + virStrerror(errno, ebuf, sizeof(ebuf))); + } + virDomainObjEndAPI(&vm); + VIR_FREE(old_dom_cfg_file); + VIR_FREE(old_dom_name); + VIR_FREE(new_dom_name); + VIR_FREE(rename_log_msg); + if (event_old) + qemuDomainEventQueue(driver, event_old); + if (event_new) + qemuDomainEventQueue(driver, event_new); + virObjectUnref(cfg); + return ret; + + rollback: + if (old_dom_name) { + new_dom_name = vm->def->name; + vm->def->name = old_dom_name; + old_dom_name = NULL; + } + + virDomainObjListRenameRemove(driver->domains, new_name); + goto endjob; +} + static virHypervisorDriver qemuHypervisorDriver = { .name = QEMU_DRIVER_NAME, .connectOpen = qemuConnectOpen, /* 0.2.0 */ @@ -20060,6 +20204,7 @@ static virHypervisorDriver qemuHypervisorDriver = { .domainGetFSInfo = qemuDomainGetFSInfo, /* 1.2.11 */ .domainInterfaceAddresses = qemuDomainInterfaceAddresses, /* 1.2.14 */ .domainSetUserPassword = qemuDomainSetUserPassword, /* 1.2.16 */ + .domainRename = qemuDomainRename, /* 1.2.19 */ }; -- 2.1.0

On 10.08.2015 21:59, Tomas Meszaros wrote:
Currently supports only renaming inactive domains without snapshots.
Signed-off-by: Tomas Meszaros <exo@tty.sk> --- src/qemu/qemu_driver.c | 145 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 6998e12..41e5fc4 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -19853,6 +19853,150 @@ qemuDomainSetUserPassword(virDomainPtr dom, }
+static int qemuDomainRename(virDomainPtr dom, + const char *new_name, + unsigned int flags ATTRIBUTE_UNUSED)
Nope. They need to be checked via virCheckFlags().
+{ + virQEMUDriverPtr driver = dom->conn->privateData; + virQEMUDriverConfigPtr cfg = NULL; + virDomainObjPtr vm; + virObjectEventPtr event_new = NULL; + virObjectEventPtr event_old = NULL; + int ret = -1; + int logfile = -1; + char ebuf[1024]; + char *timestamp; + char *rename_log_msg = NULL; + char *new_dom_name = NULL; + char *old_dom_name = NULL; + char *old_dom_cfg_file = NULL; + + if (VIR_STRDUP(new_dom_name, new_name) < 0) + goto cleanup; + + if (!(vm = qemuDomObjFromDomain(dom))) + goto cleanup; + + if (virDomainRenameEnsureACL(dom->conn, vm->def) < 0) + goto cleanup; + + cfg = virQEMUDriverGetConfig(driver); + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + + if (virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("cannot rename active domain")); + goto endjob; + } + + 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 (virDomainSnapshotObjListNum(vm->snapshots, NULL, 0) > 0) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("cannot rename domain with snapshots")); + goto endjob; + } + + if (virAsprintf(&rename_log_msg, ": domain %s has been renamed to %s\n", + vm->def->name, new_name) < 0) { + goto endjob; + } + + if (!(old_dom_cfg_file = virDomainConfigFile(cfg->configDir, + vm->def->name))) { + goto endjob; + } + + if (virDomainObjListRenameAddNew(driver->domains, vm, new_name) < 0) + goto endjob; + + if ((logfile = qemuDomainCreateLog(driver, vm, true)) < 0) + goto rollback; + + event_old = virDomainEventLifecycleNewFromObj(vm, + VIR_DOMAIN_EVENT_UNDEFINED, + VIR_DOMAIN_EVENT_UNDEFINED_RENAMED); + + /* Switch name in domain definition. */ + old_dom_name = vm->def->name; + vm->def->name = new_dom_name; + new_dom_name = NULL; + + if (virDomainSaveConfig(cfg->configDir, vm->def) < 0) + goto rollback; + + if (virFileExists(old_dom_cfg_file) && + unlink(old_dom_cfg_file) < 0) { + virReportSystemError(errno, + _("cannot remove old domain config file %s"), + old_dom_cfg_file); + goto rollback; + } + + /* Remove old domain name from table. */ + virDomainObjListRenameRemove(driver->domains, old_dom_name); + + event_new = virDomainEventLifecycleNewFromObj(vm, + VIR_DOMAIN_EVENT_DEFINED, + VIR_DOMAIN_EVENT_DEFINED_RENAMED); + + /* Write message to the log. */ + if ((timestamp = virTimeStringNow()) != NULL) { + if (safewrite(logfile, timestamp, strlen(timestamp)) < 0 || + safewrite(logfile, rename_log_msg, + strlen(rename_log_msg)) < 0) { + VIR_WARN("Unable to write timestamp to logfile: %s", + virStrerror(errno, ebuf, sizeof(ebuf))); + } + VIR_FREE(timestamp); + } + + /* Success, domain has been renamed. */ + ret = 0; + + endjob: + qemuDomainObjEndJob(driver, vm); + + cleanup: + if (VIR_CLOSE(logfile) < 0) { + VIR_WARN("Unable to close logfile: %s", + virStrerror(errno, ebuf, sizeof(ebuf))); + } + virDomainObjEndAPI(&vm); + VIR_FREE(old_dom_cfg_file); + VIR_FREE(old_dom_name); + VIR_FREE(new_dom_name); + VIR_FREE(rename_log_msg); + if (event_old) + qemuDomainEventQueue(driver, event_old); + if (event_new) + qemuDomainEventQueue(driver, event_new); + virObjectUnref(cfg); + return ret; + + rollback: + if (old_dom_name) { + new_dom_name = vm->def->name; + vm->def->name = old_dom_name; + old_dom_name = NULL; + } + + virDomainObjListRenameRemove(driver->domains, new_name); + goto endjob; +} + static virHypervisorDriver qemuHypervisorDriver = { .name = QEMU_DRIVER_NAME, .connectOpen = qemuConnectOpen, /* 0.2.0 */ @@ -20060,6 +20204,7 @@ static virHypervisorDriver qemuHypervisorDriver = { .domainGetFSInfo = qemuDomainGetFSInfo, /* 1.2.11 */ .domainInterfaceAddresses = qemuDomainInterfaceAddresses, /* 1.2.14 */ .domainSetUserPassword = qemuDomainSetUserPassword, /* 1.2.16 */ + .domainRename = qemuDomainRename, /* 1.2.19 */ };
Michal

On 08/10/2015 01:59 PM, Tomas Meszaros wrote:
This is an effort to implement domain rename API. Presented patch series consists of the following: virDomainRename API implementation for qemu, implementation of the virsh command domrename and the additional support code.
The idea behind this endeavor is to provide convenient and safe way to rename a domain.
Instead of the:
virsh dumpxml domain > domain.xml (change domain name in domain.xml) virsh undefine domain virsh define domain.xml
user can simply type:
virsh domrename foo bar
or call virDomainRename() API and domain "foo" will be renamed to "bar".
Cool! Although I haven't reviewed it closely, this series is looking good. But I do have one small question: Do you have the time and energy to provide an implementation for the Xen libxl driver? :-). Regards, Jim

On Mon, 10 Aug 2015 15:02:03 -0600 Jim Fehlig <jfehlig@suse.com> wrote:
On 08/10/2015 01:59 PM, Tomas Meszaros wrote:
This is an effort to implement domain rename API. Presented patch series consists of the following: virDomainRename API implementation for qemu, implementation of the virsh command domrename and the additional support code.
The idea behind this endeavor is to provide convenient and safe way to rename a domain.
Instead of the:
virsh dumpxml domain > domain.xml (change domain name in domain.xml) virsh undefine domain virsh define domain.xml
user can simply type:
virsh domrename foo bar
or call virDomainRename() API and domain "foo" will be renamed to "bar".
Cool! Although I haven't reviewed it closely, this series is looking good. But I do have one small question: Do you have the time and energy to provide an implementation for the Xen libxl driver? :-).
Regards, Jim
Hi Jim, If everything goes alright with this series and it gets merged, I can definitely look into adding support for other drivers. Tomas

On 10.08.2015 21:59, Tomas Meszaros wrote:
This is an effort to implement domain rename API. Presented patch series consists of the following: virDomainRename API implementation for qemu, implementation of the virsh command domrename and the additional support code.
The idea behind this endeavor is to provide convenient and safe way to rename a domain.
Instead of the:
virsh dumpxml domain > domain.xml (change domain name in domain.xml) virsh undefine domain virsh define domain.xml
user can simply type:
virsh domrename foo bar
or call virDomainRename() API and domain "foo" will be renamed to "bar".
We currently support only renaming inactive domains without snapshots. Renaming procedure takes care of domain log, config, guest agent path and should be able to recover in case of failure.
I've been working on this functionality in collaboration with Michal Privoznik who is my mentor during the GSoC 2015. If you have any questions, ideas or criticism feel free to join the discussion.
v2: - removed guest agent path rename code - removed rename permission - added code for emitting undefined+renamed event for the old domain
v3: - removed domain rename permission - fixed virDomainRename doc comment - added @flags parameter to the virDomainRename API
Tomas Meszaros (5): Introduce virDomainRename API virsh: Implement "domrename" command domain_conf: Introducde virDomainObjListRenameAddNew() & virDomainObjListRenameRemove() Introduce new VIR_DOMAIN_EVENT_DEFINED_RENAMED event qemu: Implement virDomainRename
examples/object-events/event-test.c | 4 + include/libvirt/libvirt-domain.h | 6 ++ src/conf/domain_conf.c | 35 +++++++++ src/conf/domain_conf.h | 5 ++ src/driver-hypervisor.h | 6 ++ src/libvirt-domain.c | 34 +++++++++ src/libvirt_private.syms | 2 + src/libvirt_public.syms | 5 ++ src/qemu/qemu_driver.c | 145 ++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 18 ++++- src/remote_protocol-structs | 8 ++ tools/virsh-domain.c | 63 +++++++++++++++- tools/virsh.pod | 7 ++ 14 files changed, 336 insertions(+), 3 deletions(-)
Okay, this looks good to me. Well, consider this squashed in: diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index 71ea024..9065dab 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -8784,11 +8784,12 @@ virDomainIsPersistent(virDomainPtr dom) * argument. Depending on each driver implementation it may be * required that domain is in a specific state. * - * Returns 0 if renamed, -1 on error + * Returns 0 if successfully renamed, -1 on error */ int -virDomainRename(virDomainPtr dom, const char *new_name, - unsigned int flags ATTRIBUTE_UNUSED) +virDomainRename(virDomainPtr dom, + const char *new_name, + unsigned int flags) { VIR_DEBUG("dom=%p, new_name=%s", dom, NULLSTR(new_name)); diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index a29cd1b..3926ccd 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -19873,7 +19873,7 @@ qemuDomainSetUserPassword(virDomainPtr dom, static int qemuDomainRename(virDomainPtr dom, const char *new_name, - unsigned int flags ATTRIBUTE_UNUSED) + unsigned int flags) { virQEMUDriverPtr driver = dom->conn->privateData; virQEMUDriverConfigPtr cfg = NULL; @@ -19889,6 +19889,8 @@ static int qemuDomainRename(virDomainPtr dom, char *old_dom_name = NULL; char *old_dom_cfg_file = NULL; + virCheckFlags(0, ret); + if (VIR_STRDUP(new_dom_name, new_name) < 0) goto cleanup; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 1fdaa55..ca36dc9 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2687,6 +2687,7 @@ struct remote_domain_set_user_password_args { struct remote_domain_rename_args { remote_nonnull_domain dom; remote_string new_name; + u_int flags; }; struct remote_domain_rename_ret { int rename; ACK series. Although, since this is somewhat controversial topic, I'll let others to chime in and express their feelings before pushing. Michal

On 12.08.2015 15:16, Michal Privoznik wrote:
On 10.08.2015 21:59, Tomas Meszaros wrote:
This is an effort to implement domain rename API. Presented patch series consists of the following: virDomainRename API implementation for qemu, implementation of the virsh command domrename and the additional support code.
The idea behind this endeavor is to provide convenient and safe way to rename a domain.
Instead of the:
virsh dumpxml domain > domain.xml (change domain name in domain.xml) virsh undefine domain virsh define domain.xml
user can simply type:
virsh domrename foo bar
or call virDomainRename() API and domain "foo" will be renamed to "bar".
We currently support only renaming inactive domains without snapshots. Renaming procedure takes care of domain log, config, guest agent path and should be able to recover in case of failure.
I've been working on this functionality in collaboration with Michal Privoznik who is my mentor during the GSoC 2015. If you have any questions, ideas or criticism feel free to join the discussion.
v2: - removed guest agent path rename code - removed rename permission - added code for emitting undefined+renamed event for the old domain
v3: - removed domain rename permission - fixed virDomainRename doc comment - added @flags parameter to the virDomainRename API
Tomas Meszaros (5): Introduce virDomainRename API virsh: Implement "domrename" command domain_conf: Introducde virDomainObjListRenameAddNew() & virDomainObjListRenameRemove() Introduce new VIR_DOMAIN_EVENT_DEFINED_RENAMED event qemu: Implement virDomainRename
examples/object-events/event-test.c | 4 + include/libvirt/libvirt-domain.h | 6 ++ src/conf/domain_conf.c | 35 +++++++++ src/conf/domain_conf.h | 5 ++ src/driver-hypervisor.h | 6 ++ src/libvirt-domain.c | 34 +++++++++ src/libvirt_private.syms | 2 + src/libvirt_public.syms | 5 ++ src/qemu/qemu_driver.c | 145 ++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 18 ++++- src/remote_protocol-structs | 8 ++ tools/virsh-domain.c | 63 +++++++++++++++- tools/virsh.pod | 7 ++ 14 files changed, 336 insertions(+), 3 deletions(-)
ACK series. Although, since this is somewhat controversial topic, I'll let others to chime in and express their feelings before pushing.
Since nobody objected, I've pushed these. Good job! Michal
participants (3)
-
Jim Fehlig
-
Michal Privoznik
-
Tomas Meszaros