eject-media to eject the media from, and insert-media to insert
media into CDROM or floppy drive.
---
Basically this patch is just rebaing of
https://www.redhat.com/archives/libvir-list/2011-June/msg01516.html,
with a bit improvement, as I explained in the mail thread, we
don't have to wait for the new QEMU monitor commands, what we
need is just separate the tray and media management in virsh,
if one day QEMU gets the patch for those new commands pushed,
we can switch to honor them easily (regardless of whether we do
it inside updateDeviceFlags API or in new APIs).
---
tools/virsh.c | 369 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
tools/virsh.pod | 33 +++++
2 files changed, 402 insertions(+), 0 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c
index 02f2e0d..228a9dc 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -13453,6 +13453,373 @@ cmdAttachDisk(vshControl *ctl, const vshCmd *cmd)
}
/*
+ * "eject-media" command
+ */
+static const vshCmdInfo info_eject_media[] = {
+ {"help", N_("eject media from CD or floppy drive")},
+ {"desc", N_("Eject media from CD or floppy drive.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_eject_media[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or
uuid")},
+ {"target", VSH_OT_DATA, VSH_OFLAG_REQ, N_("target of disk
device")},
+ {"current", VSH_OT_BOOL, 0, N_("can be either or both of --live and
--config, "
+ "depends on implementation of hypervisor
driver")},
+ {"live", VSH_OT_BOOL, 0, N_("alter live configuration of running
domain")},
+ {"config", VSH_OT_BOOL, 0, N_("alter persistent configuration, effect
observed on next boot")},
+ {"force", VSH_OT_BOOL, 0, N_("force media ejection")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdEjectMedia(vshControl *ctl, const vshCmd *cmd)
+{
+ xmlDocPtr xml = NULL;
+ xmlXPathObjectPtr obj=NULL;
+ xmlXPathContextPtr ctxt = NULL;
+ xmlNodePtr cur = NULL;
+ xmlBufferPtr xml_buf = NULL;
+ virDomainPtr dom = NULL;
+ const char *target = NULL;
+ char *doc;
+ int i = 0, diff_tgt;
+ int ret;
+ bool functionReturn = false;
+ int flags = 0;
+ int config = vshCommandOptBool(cmd, "config");
+ int live = vshCommandOptBool(cmd, "live");
+ int current = vshCommandOptBool(cmd, "current");
+ int force = vshCommandOptBool(cmd, "force");
+ bool has_source = false;
+
+ if (current) {
+ if (live || config) {
+ vshError(ctl, "%s", _("--current must be specified
exclusively"));
+ return false;
+ }
+ flags = VIR_DOMAIN_AFFECT_CURRENT;
+ } else {
+ if (config)
+ flags |= VIR_DOMAIN_AFFECT_CONFIG;
+ if (live)
+ flags |= VIR_DOMAIN_AFFECT_LIVE;
+ }
+
+ if (force)
+ flags |= VIR_DOMAIN_DEVICE_MODIFY_FORCE;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ goto cleanup;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ goto cleanup;
+
+ if (vshCommandOptString(cmd, "target", &target) <= 0)
+ goto cleanup;
+
+ if (flags & VIR_DOMAIN_AFFECT_CONFIG)
+ doc = virDomainGetXMLDesc(dom, VIR_DOMAIN_XML_INACTIVE);
+ else
+ doc = virDomainGetXMLDesc(dom, 0);
+ if (!doc)
+ goto cleanup;
+
+ xml = xmlReadDoc((const xmlChar *) doc, "domain.xml", NULL,
+ XML_PARSE_NOENT | XML_PARSE_NONET |
+ XML_PARSE_NOWARNING);
+ VIR_FREE(doc);
+ if (!xml) {
+ vshError(ctl, "%s", _("Failed to get disk information"));
+ goto cleanup;
+ }
+ ctxt = xmlXPathNewContext(xml);
+ if (!ctxt) {
+ vshError(ctl, "%s", _("Failed to get disk information"));
+ goto cleanup;
+ }
+
+ obj = xmlXPathEval(BAD_CAST "/domain/devices/disk", ctxt);
+ if ((obj == NULL) || (obj->type != XPATH_NODESET) ||
+ (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr == 0)) {
+ vshError(ctl, "%s", _("Failed to get disk information"));
+ goto cleanup;
+ }
+
+ /* search target */
+ for (; i < obj->nodesetval->nodeNr; i++) {
+ xmlNodePtr n = obj->nodesetval->nodeTab[i];
+ bool is_supported = false;
+
+ /* Check if the disk is CDROM or floppy disk */
+ if (xmlStrEqual(n->name, BAD_CAST "disk")) {
+ char *device_value = virXMLPropString(n, "device");
+
+ if (STREQ(device_value, "cdrom") ||
+ STREQ(device_value, "floppy"))
+ is_supported = true;
+
+ VIR_FREE(device_value);
+ }
+
+ cur = obj->nodesetval->nodeTab[i]->children;
+ while (cur != NULL) {
+ if (cur->type == XML_ELEMENT_NODE &&
+ xmlStrEqual(cur->name, BAD_CAST "target")) {
+ char *tmp_tgt = virXMLPropString(cur, "dev");
+
+ diff_tgt = STREQ(tmp_tgt, target);
+ VIR_FREE(tmp_tgt);
+
+ if (diff_tgt && is_supported) {
+ goto hit;
+ }
+ }
+
+ cur = cur->next;
+ }
+ }
+ vshError(ctl, _("No found CDROM or floppy disk whose target is %s"),
target);
+ goto cleanup;
+
+ hit:
+ xml_buf = xmlBufferCreate();
+ if (!xml_buf) {
+ vshError(ctl, "%s", _("Failed to allocate memory"));
+ goto cleanup;
+ }
+
+ cur = obj->nodesetval->nodeTab[i]->children;
+ while (cur != NULL) {
+ if (cur->type == XML_ELEMENT_NODE &&
+ xmlStrEqual(cur->name, BAD_CAST "source")) {
+ xmlUnlinkNode(cur);
+ xmlFreeNode(cur);
+ has_source = true;
+ break;
+ }
+
+ cur = cur->next;
+ }
+
+ if (!has_source) {
+ vshError(ctl, _("The disk device whose target is '%s' doesn't
"
+ "have media"), target);
+ goto cleanup;
+ }
+
+ if (xmlNodeDump(xml_buf, xml, obj->nodesetval->nodeTab[i], 0, 0) < 0) {
+ vshError(ctl, "%s", _("Failed to create XML"));
+ goto cleanup;
+ }
+
+ ret = virDomainUpdateDeviceFlags(dom, (char *)xmlBufferContent(xml_buf), flags);
+
+ if (ret != 0) {
+ vshError(ctl, "%s", _("Failed to eject media"));
+ } else {
+ vshPrint(ctl, "%s", _("Media ejected successfully\n"));
+ functionReturn = true;
+ }
+
+ cleanup:
+ xmlXPathFreeObject(obj);
+ xmlXPathFreeContext(ctxt);
+ xmlFreeDoc(xml);
+ xmlBufferFree(xml_buf);
+ if (dom)
+ virDomainFree(dom);
+ return functionReturn;
+}
+
+/*
+ * "insert-media" command
+ */
+static const vshCmdInfo info_insert_media[] = {
+ {"help", N_("insert media into CD or floppy drive")},
+ {"desc", N_("Insert media into CD or floppy drive.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_insert_media[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or
uuid")},
+ {"target", VSH_OT_DATA, VSH_OFLAG_REQ, N_("target of disk
device")},
+ {"source", VSH_OT_DATA, VSH_OFLAG_REQ, N_("source of the
media")},
+ {"current", VSH_OT_BOOL, 0, N_("can be either or both of --live and
--config, "
+ "depends on implementation of hypervisor
driver")},
+ {"live", VSH_OT_BOOL, 0, N_("alter live configuration of running
domain")},
+ {"config", VSH_OT_BOOL, 0, N_("alter persistent configuration, effect
observed on next boot")},
+ {"force", VSH_OT_BOOL, 0, N_("force media insertion")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdInsertMedia(vshControl *ctl, const vshCmd *cmd)
+{
+ xmlDocPtr xml = NULL;
+ xmlXPathObjectPtr obj=NULL;
+ xmlXPathContextPtr ctxt = NULL;
+ xmlNodePtr cur = NULL;
+ xmlNodePtr new_node = NULL;
+ xmlBufferPtr xml_buf = NULL;
+ virDomainPtr dom = NULL;
+ const char *target = NULL;
+ const char *source = NULL;
+ char *doc;
+ int i = 0, diff_tgt;
+ int ret;
+ bool functionReturn = false;
+ int flags = 0;
+ const char *disk_type = NULL;
+ int config = vshCommandOptBool(cmd, "config");
+ int live = vshCommandOptBool(cmd, "live");
+ int current = vshCommandOptBool(cmd, "current");
+ int force = vshCommandOptBool(cmd, "force");
+
+ if (current) {
+ if (live || config) {
+ vshError(ctl, "%s", _("--current must be specified
exclusively"));
+ return false;
+ }
+ flags = VIR_DOMAIN_AFFECT_CURRENT;
+ } else {
+ if (config)
+ flags |= VIR_DOMAIN_AFFECT_CONFIG;
+ if (live)
+ flags |= VIR_DOMAIN_AFFECT_LIVE;
+ }
+
+ if (force)
+ flags |= VIR_DOMAIN_DEVICE_MODIFY_FORCE;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ goto cleanup;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ goto cleanup;
+
+ if (vshCommandOptString(cmd, "target", &target) <= 0)
+ goto cleanup;
+
+ if (vshCommandOptString(cmd, "source", &source) <= 0)
+ goto cleanup;
+
+ if (flags & VIR_DOMAIN_AFFECT_CONFIG)
+ doc = virDomainGetXMLDesc(dom, VIR_DOMAIN_XML_INACTIVE);
+ else
+ doc = virDomainGetXMLDesc(dom, 0);
+ if (!doc)
+ goto cleanup;
+
+ xml = xmlReadDoc((const xmlChar *) doc, "domain.xml", NULL,
+ XML_PARSE_NOENT | XML_PARSE_NONET |
+ XML_PARSE_NOWARNING);
+ VIR_FREE(doc);
+ if (!xml) {
+ vshError(ctl, "%s", _("Failed to get disk information"));
+ goto cleanup;
+ }
+ ctxt = xmlXPathNewContext(xml);
+ if (!ctxt) {
+ vshError(ctl, "%s", _("Failed to get disk information"));
+ goto cleanup;
+ }
+
+ obj = xmlXPathEval(BAD_CAST "/domain/devices/disk", ctxt);
+ if ((obj == NULL) || (obj->type != XPATH_NODESET) ||
+ (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr == 0)) {
+ vshError(ctl, "%s", _("Failed to get disk information"));
+ goto cleanup;
+ }
+
+ /* search target */
+ for (; i < obj->nodesetval->nodeNr; i++) {
+ xmlNodePtr n = obj->nodesetval->nodeTab[i];
+ bool is_supported = false;
+
+ /* Check if the disk is CDROM or floppy disk */
+ if (xmlStrEqual(n->name, BAD_CAST "disk")) {
+ disk_type = virXMLPropString(n, "type");
+ char *device_value = virXMLPropString(n, "device");
+
+ if (STREQ(device_value, "cdrom") ||
+ STREQ(device_value, "floppy"))
+ is_supported = true;
+
+ VIR_FREE(device_value);
+ }
+
+ cur = obj->nodesetval->nodeTab[i]->children;
+ while (cur != NULL) {
+ if (cur->type == XML_ELEMENT_NODE &&
+ xmlStrEqual(cur->name, BAD_CAST "target")) {
+ char *tmp_tgt = virXMLPropString(cur, "dev");
+
+ diff_tgt = STREQ(tmp_tgt, target);
+ VIR_FREE(tmp_tgt);
+
+ if (diff_tgt && is_supported) {
+ goto hit;
+ }
+ }
+
+ cur = cur->next;
+ }
+ VIR_FREE(disk_type);
+ }
+ vshError(ctl, _("No found CDROM or floppy disk whose target is %s"),
target);
+ goto cleanup;
+
+ hit:
+ xml_buf = xmlBufferCreate();
+ if (!xml_buf) {
+ vshError(ctl, "%s", _("Failed to allocate memory"));
+ goto cleanup;
+ }
+
+ cur = obj->nodesetval->nodeTab[i]->children;
+ while (cur != NULL) {
+ if (cur->type == XML_ELEMENT_NODE &&
+ xmlStrEqual(cur->name, BAD_CAST "source")) {
+ vshError(ctl, _("The disk device whose target is '%s' has media,
"
+ "you may need to eject it first"), target);
+ goto cleanup;
+ }
+
+ cur = cur->next;
+ }
+
+ /* Insert node <source> */
+ new_node = xmlNewNode(NULL, BAD_CAST "source");
+ xmlNewProp(new_node, (const xmlChar *)disk_type, (const xmlChar *)source);
+ VIR_FREE(disk_type);
+ xmlAddChild(obj->nodesetval->nodeTab[i], new_node);
+
+ if (xmlNodeDump(xml_buf, xml, obj->nodesetval->nodeTab[i], 0, 0) < 0) {
+ vshError(ctl, "%s", _("Failed to create XML"));
+ goto cleanup;
+ }
+
+ ret = virDomainUpdateDeviceFlags(dom, (char *)xmlBufferContent(xml_buf), flags);
+
+ if (ret != 0) {
+ vshError(ctl, "%s", _("Failed to insert media"));
+ } else {
+ vshPrint(ctl, "%s", _("Media inserted successfully\n"));
+ functionReturn = true;
+ }
+
+ cleanup:
+ xmlXPathFreeObject(obj);
+ xmlXPathFreeContext(ctxt);
+ xmlFreeDoc(xml);
+ xmlBufferFree(xml_buf);
+ if (dom)
+ virDomainFree(dom);
+ return functionReturn;
+}
+
+/*
* "detach-disk" command
*/
static const vshCmdInfo info_detach_disk[] = {
@@ -15600,7 +15967,9 @@ static const vshCmdDef domManagementCmds[] = {
{"dump", cmdDump, opts_dump, info_dump, 0},
{"dumpxml", cmdDumpXML, opts_dumpxml, info_dumpxml, 0},
{"edit", cmdEdit, opts_edit, info_edit, 0},
+ {"eject-media", cmdEjectMedia, opts_eject_media, info_eject_media, 0},
{"inject-nmi", cmdInjectNMI, opts_inject_nmi, info_inject_nmi, 0},
+ {"insert-media", cmdInsertMedia, opts_insert_media, info_insert_media, 0},
{"send-key", cmdSendKey, opts_send_key, info_send_key},
{"managedsave", cmdManagedSave, opts_managedsave, info_managedsave, 0},
{"managedsave-remove", cmdManagedSaveRemove, opts_managedsaveremove,
diff --git a/tools/virsh.pod b/tools/virsh.pod
index 6c10245..45a3fde 100644
--- a/tools/virsh.pod
+++ b/tools/virsh.pod
@@ -1387,6 +1387,39 @@ option can be used to force device update, e.g., to eject a CD-ROM
even if it
is locked/mounted in the domain. See the documentation to learn about libvirt
XML format for a device.
+=item B<eject-media> I<domain-id> I<target> [I<--current>]
[I<--live>]
+[I<--config>] [I<--force>]
+
+Eject media from CD or floppy drive. The I<target> is the device as seen
+from the domain.
+
+If I<--live> is specified, alter live configuration of running guest.
+If I<--config> is specified, alter persistent configuration, effect observed
+on next boot.
+I<--current> can be either or both of I<live> and I<config>, depends
on
+the hypervisor's implementation.
+Both I<--live> and I<--config> flags may be given, but I<--current> is
+exclusive. If no flag is specified, behavior is different depending
+on hypervisor.
+The I<--force> option can be used to force media ejection.
+
+=item B<insert-media> I<domain-id> I<target> I<source>
[I<--current>]
+[I<--live>] [I<--config>] [I<--force>]
+
+Insert media into CD or floppy drive. The I<target> is the device as seen
+from the domain. The I<source> specifies the path of the media which is to
+be inserted.
+
+If I<--live> is specified, alter live configuration of running guest.
+If I<--config> is specified, alter persistent configuration, effect observed
+on next boot.
+I<--current> can be either or both of I<live> and I<config>, depends
on
+the hypervisor's implementation.
+Both I<--live> and I<--config> flags may be given, but I<--current> is
+exclusive. If no flag is specified, behavior is different depending
+on hypervisor.
+The I<--force> option can be used to force media ejection.
+
=back
=head1 NODEDEV COMMANDS
--
1.7.7.3