Related issue: https://gitlab.com/libvirt/libvirt/-/issues/16 Added in support for the following parameters in attach-disk: --source-name --source-protocol --source-host-name --source-host-socket --source-host-transport Allowed for multiple hosts to be added to a single source. Multiple hosts can be defined by providing multiple instances of --source-host-name, followed by optional transport and socket parameters. Using a single host does not require a host name. Added documentation to virsh.rst specifying usage. Signed-off-by: Ryan Gahagan <rgahagan@xxxxxxxxxxxxx> --- docs/manpages/virsh.rst | 24 +++++++- tools/virsh-domain.c | 125 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 138 insertions(+), 11 deletions(-) diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst index bfd26e3120..60e5f5ebe4 100644 --- a/docs/manpages/virsh.rst +++ b/docs/manpages/virsh.rst @@ -4500,9 +4500,11 @@ attach-disk [--current]] | [--persistent]] [--targetbus bus] [--driver driver] [--subdriver subdriver] [--iothread iothread] [--cache cache] [--io io] [--type type] [--alias alias] - [--mode mode] [--sourcetype sourcetype] [--serial serial] - [--wwn wwn] [--rawio] [--address address] [--multifunction] - [--print-xml] + [--mode mode] [--sourcetype sourcetype] [--source-name name] + [--source-protocol protocol] [--source-host-name hostname:port] + [--source-host-transport transport] [--source-host-socket socket] + [--serial serial] [--wwn wwn] [--rawio] [--address address] + [--multifunction] [--print-xml] Attach a new disk device to the domain. *source* is path for the files and devices. *target* controls the bus or @@ -4541,6 +4543,22 @@ ccw:cssid.ssid.devno. Virtio-ccw devices must have their cssid set to 0xfe. *multifunction* indicates specified pci address is a multifunction pci device address. +If *--source-protocol* or *--source-name* is specified, then the parameters +will be inserted into the XML that is generated for the source. +If any of *--source-host-name*, *--source-host-transport*, or +*--source-host-socket* are specified, then a ``<host>`` tag +will be generated under the ``<source>`` tag containing whichever +parameters were provided. If needed, the user can provide multiple hosts +by providing each host with a *--source-host-name*. Each host will +receive the host parameters which come between it and the next instance +of *--source-host-name* or between it and the end of the command. +If a user tries to provide multiple of the same host parameter +for any single host, only the first one will be generated as +part of the XML output. + +--source-host-name me --source-host-transport t1 --source-host-transport t2 --source-host-transport t3 --soure-host-name you +<host transport='t1' transport='t2' transport='t3'> + If *--print-xml* is specified, then the XML of the disk that would be attached is printed instead. diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 12b35c037d..469665f0f0 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -224,6 +224,26 @@ static const vshCmdOptDef opts_attach_disk[] = { .flags = VSH_OFLAG_REQ | VSH_OFLAG_EMPTY_OK, .help = N_("source of disk device") }, + {.name = "source-protocol", + .type = VSH_OT_STRING, + .help = N_("protocol used by disk device source") + }, + {.name = "source-name", + .type = VSH_OT_STRING, + .help = N_("name of disk device source") + }, + {.name = "source-host-name", + .type = VSH_OT_STRING, + .help = N_("host name for source of disk device") + }, + {.name = "source-host-transport", + .type = VSH_OT_STRING, + .help = N_("host transport for source of disk device") + }, + {.name = "source-host-socket", + .type = VSH_OT_STRING, + .help = N_("host socket for source of disk device") + }, {.name = "target", .type = VSH_OT_DATA, .flags = VSH_OFLAG_REQ, @@ -558,15 +578,68 @@ static int str2DiskAddress(const char *str, struct DiskAddress *diskAddr) return -1; } +static void attachDiskHostGen(virBufferPtr buf, const vshCmd *cmd) +{ + // Can be multiple hosts so we have to scan + // the cmd options to find all the host params + // <source tag in XML not yet closed + vshCmdOpt *candidate = cmd->opts; + char *host_name = NULL, *host_port = NULL; + int close_tag = 0, seen_socket, seen_transport; + + while (candidate) { + // Iterate candidates to find each host-name + if (STREQ(candidate->def->name, "source-host-name")) { + // After the first host-name, we need to terminate + // the <host ... tag + // It's left open so socket and transport can be added later + + // Only include the first example of socket or transport per host + // When a socket or transport is seen, these are set to true + // until the next name is encountered. + seen_socket = 0; + seen_transport = 0; + + if (close_tag) + virBufferAddLit(buf, "/>\n"); + else + close_tag = 1; + + host_name = candidate->data; + host_port = strchr(host_name, ':'); + + if (!host_port) { + // If port isn't provided, only print name + virBufferAsprintf(buf, "<host name='%s'", host_name); + } else { + // If port is provided, manipulate strings and print both + host_name[(int)(host_port - host_name)] = '\0'; + virBufferAsprintf(buf, "<host name='%s' port='%s'", host_name, host_port + 1); + } + } else if (!seen_socket && STREQ(candidate->def->name, "source-host-socket")) { + seen_socket = 1; + virBufferAsprintf(buf, " socket='%s'", candidate->data); + } else if (!seen_transport && STREQ(candidate->def->name, "source-host-transport")) { + seen_transport = 1; + virBufferAsprintf(buf, " transport='%s'", candidate->data); + } + + candidate = candidate->next; + } + // Close final <host tag + virBufferAddLit(buf, "/>\n"); +} + static bool cmdAttachDisk(vshControl *ctl, const vshCmd *cmd) { virDomainPtr dom = NULL; - const char *source = NULL, *target = NULL, *driver = NULL, - *subdriver = NULL, *type = NULL, *mode = NULL, - *iothread = NULL, *cache = NULL, *io = NULL, - *serial = NULL, *straddr = NULL, *wwn = NULL, - *targetbus = NULL, *alias = NULL; + const char *source = NULL, *source_name = NULL, *source_protocol = NULL, + *target = NULL, *driver = NULL, *subdriver = NULL, *type = NULL, + *mode = NULL, *iothread = NULL, *cache = NULL, + *io = NULL, *serial = NULL, *straddr = NULL, + *wwn = NULL, *targetbus = NULL, *alias = NULL, + *host_transport = NULL, *host_name = NULL, *host_socket = NULL; struct DiskAddress diskAddr; bool isFile = false, functionReturn = false; int ret; @@ -591,6 +664,8 @@ cmdAttachDisk(vshControl *ctl, const vshCmd *cmd) flags |= VIR_DOMAIN_AFFECT_LIVE; if (vshCommandOptStringReq(ctl, cmd, "source", &source) < 0 || + vshCommandOptStringReq(ctl, cmd, "source-name", &source_name) < 0 || + vshCommandOptStringReq(ctl, cmd, "source-protocol", &source_protocol) < 0 || vshCommandOptStringReq(ctl, cmd, "target", &target) < 0 || vshCommandOptStringReq(ctl, cmd, "driver", &driver) < 0 || vshCommandOptStringReq(ctl, cmd, "subdriver", &subdriver) < 0 || @@ -604,7 +679,10 @@ cmdAttachDisk(vshControl *ctl, const vshCmd *cmd) vshCommandOptStringReq(ctl, cmd, "address", &straddr) < 0 || vshCommandOptStringReq(ctl, cmd, "targetbus", &targetbus) < 0 || vshCommandOptStringReq(ctl, cmd, "alias", &alias) < 0 || - vshCommandOptStringReq(ctl, cmd, "sourcetype", &stype) < 0) + vshCommandOptStringReq(ctl, cmd, "sourcetype", &stype) < 0 || + vshCommandOptStringReq(ctl, cmd, "source-host-name", (const char **) &host_name) < 0 || + vshCommandOptStringReq(ctl, cmd, "source-host-transport", &host_transport) < 0 || + vshCommandOptStringReq(ctl, cmd, "source-host-socket", &host_socket) < 0) goto cleanup; if (!stype) { @@ -659,9 +737,40 @@ cmdAttachDisk(vshControl *ctl, const vshCmd *cmd) virBufferAddLit(&buf, "/>\n"); } - if (source) - virBufferAsprintf(&buf, "<source %s='%s'/>\n", + if (source || source_protocol || source_name || + host_name || host_transport || host_socket) { + virBufferAddLit(&buf, "<source"); + + if (source) + virBufferAsprintf(&buf, " %s='%s'", isFile ? "file" : "dev", source); + if (source_protocol) + virBufferAsprintf(&buf, " protocol='%s'", source_protocol); + if (source_name) + virBufferAsprintf(&buf, " name='%s'", source_name); + + if (!(host_name || host_transport || host_socket)) { + // Close source if no host is provided + virBufferAddLit(&buf, "/>\n"); + } else if (!host_name) { + // If no host name is provided but there is a host, + // we have a single host with params + virBufferAddLit(&buf, ">\n<host"); + + if (host_transport) + virBufferAsprintf(&buf, " transport='%s'", host_transport); + if (host_socket) + virBufferAsprintf(&buf, " socket='%s'", host_socket); + + virBufferAddLit(&buf, "/>\n</source>\n"); + } else { + // May have multiple hosts, use helper method + virBufferAddLit(&buf, ">\n"); + attachDiskHostGen(&buf, cmd); + virBufferAddLit(&buf, "</source>\n"); + } + } + virBufferAsprintf(&buf, "<target dev='%s'", target); if (targetbus) virBufferAsprintf(&buf, " bus='%s'", targetbus); -- 2.29.0