Richard W.M. Jones
2013-Apr-05 13:06 UTC
[Libguestfs] [PATCH] Add support for SSH (Secure Shell) block device.
Note this patch requires a non-upstream qemu patch that I've been experimenting with. See qemu-devel list. Rich.
Richard W.M. Jones
2013-Apr-05 13:06 UTC
[Libguestfs] [PATCH] Add support for SSH (Secure Shell) block device.
From: "Richard W.M. Jones" <rjones at redhat.com> --- generator/actions.ml | 22 +++++++++- src/drives.c | 112 +++++++++++++++++++++++++++++++++++++++++++------ src/guestfs-internal.h | 4 ++ src/guestfs.pod | 18 ++++++++ src/launch-libvirt.c | 12 +++++- 5 files changed, 153 insertions(+), 15 deletions(-) diff --git a/generator/actions.ml b/generator/actions.ml index 233590f..d13968e 100644 --- a/generator/actions.ml +++ b/generator/actions.ml @@ -1247,7 +1247,7 @@ not all belong to a single logical operating system { defaults with name = "add_drive"; - style = RErr, [String "filename"], [OBool "readonly"; OString "format"; OString "iface"; OString "name"; OString "label"; OString "protocol"; OStringList "server"]; + style = RErr, [String "filename"], [OBool "readonly"; OString "format"; OString "iface"; OString "name"; OString "label"; OString "protocol"; OStringList "server"; OString "username"]; once_had_no_optargs = true; blocking = false; fish_alias = ["add"]; @@ -1362,6 +1362,15 @@ The C<server> parameter may also be supplied - see below. See also: L<guestfs(3)/SHEEPDOG>. +=item C<protocol = \"ssh\"> + +Connect to the Secure Shell (ssh) server. + +The C<server> parameter must be supplied. +The C<username> parameter may be supplied. See below. + +See also: L<guestfs(3)/SSH>. + =back =item C<server> @@ -1376,6 +1385,7 @@ is a list of server(s). nbd Exactly one rbd One or more sheepdog Zero or more + ssh Exactly one Each list element is a string specifying a server. The string must be in one of the following formats: @@ -1389,6 +1399,16 @@ in one of the following formats: If the port number is omitted, then the standard port number for the protocol is used (see C</etc/services>). +=item C<username> + +For the C<ssh> protocol only, this specifies the remote username. + +If not given, then the local username is used. But note this sometimes +may give unexpected results, for example if using the libvirt backend +and if the libvirt backend is configured to start the qemu appliance +as a special user such as C<qemu.qemu>. If in doubt, specify the +remote username you want. + =back" }; { defaults with diff --git a/src/drives.c b/src/drives.c index 01d88e0..a13dd03 100644 --- a/src/drives.c +++ b/src/drives.c @@ -107,7 +107,7 @@ static struct drive * create_drive_non_file (guestfs_h *g, enum drive_protocol protocol, struct drive_server *servers, size_t nr_servers, - const char *exportname, + const char *exportname, const char *username, bool readonly, const char *format, const char *iface, const char *name, const char *disk_label, @@ -119,6 +119,7 @@ create_drive_non_file (guestfs_h *g, drv->src.servers = servers; drv->src.nr_servers = nr_servers; drv->src.u.exportname = safe_strdup (g, exportname); + drv->src.username = username ? safe_strdup (g, username) : NULL; drv->readonly = readonly; drv->format = format ? safe_strdup (g, format) : NULL; @@ -135,12 +136,17 @@ create_drive_non_file (guestfs_h *g, static struct drive * create_drive_gluster (guestfs_h *g, struct drive_server *servers, size_t nr_servers, - const char *exportname, + const char *exportname, const char *username, bool readonly, const char *format, const char *iface, const char *name, const char *disk_label, bool use_cache_none) { + if (username != NULL) { + error (g, _("gluster: you cannot specify a username with this protocol")); + return NULL; + } + if (nr_servers != 1) { error (g, _("gluster: you must specify exactly one server")); return NULL; @@ -159,7 +165,7 @@ create_drive_gluster (guestfs_h *g, } return create_drive_non_file (g, drive_protocol_gluster, - servers, nr_servers, exportname, + servers, nr_servers, exportname, username, readonly, format, iface, name, disk_label, use_cache_none); } @@ -179,12 +185,17 @@ nbd_port (void) static struct drive * create_drive_nbd (guestfs_h *g, struct drive_server *servers, size_t nr_servers, - const char *exportname, + const char *exportname, const char *username, bool readonly, const char *format, const char *iface, const char *name, const char *disk_label, bool use_cache_none) { + if (username != NULL) { + error (g, _("nbd: you cannot specify a username with this protocol")); + return NULL; + } + if (nr_servers != 1) { error (g, _("nbd: you must specify exactly one server")); return NULL; @@ -194,7 +205,7 @@ create_drive_nbd (guestfs_h *g, servers[0].port = nbd_port (); return create_drive_non_file (g, drive_protocol_nbd, - servers, nr_servers, exportname, + servers, nr_servers, exportname, username, readonly, format, iface, name, disk_label, use_cache_none); } @@ -202,7 +213,7 @@ create_drive_nbd (guestfs_h *g, static struct drive * create_drive_rbd (guestfs_h *g, struct drive_server *servers, size_t nr_servers, - const char *exportname, + const char *exportname, const char *username, bool readonly, const char *format, const char *iface, const char *name, const char *disk_label, @@ -210,6 +221,11 @@ create_drive_rbd (guestfs_h *g, { size_t i; + if (username != NULL) { + error (g, _("rbd: you cannot specify a username with this protocol")); + return NULL; + } + if (nr_servers == 0) { error (g, _("rbd: you must specify one or more servers")); return NULL; @@ -233,7 +249,7 @@ create_drive_rbd (guestfs_h *g, } return create_drive_non_file (g, drive_protocol_rbd, - servers, nr_servers, exportname, + servers, nr_servers, exportname, username, readonly, format, iface, name, disk_label, use_cache_none); } @@ -241,7 +257,7 @@ create_drive_rbd (guestfs_h *g, static struct drive * create_drive_sheepdog (guestfs_h *g, struct drive_server *servers, size_t nr_servers, - const char *exportname, + const char *exportname, const char *username, bool readonly, const char *format, const char *iface, const char *name, const char *disk_label, @@ -249,6 +265,11 @@ create_drive_sheepdog (guestfs_h *g, { size_t i; + if (username != NULL) { + error (g, _("sheepdog: you cannot specify a username with this protocol")); + return NULL; + } + for (i = 0; i < nr_servers; ++i) { if (servers[i].transport != drive_transport_none && servers[i].transport != drive_transport_tcp) { @@ -267,7 +288,43 @@ create_drive_sheepdog (guestfs_h *g, } return create_drive_non_file (g, drive_protocol_sheepdog, - servers, nr_servers, exportname, + servers, nr_servers, exportname, username, + readonly, format, iface, name, disk_label, + use_cache_none); +} + +static struct drive * +create_drive_ssh (guestfs_h *g, + struct drive_server *servers, size_t nr_servers, + const char *exportname, const char *username, + bool readonly, const char *format, + const char *iface, const char *name, + const char *disk_label, + bool use_cache_none) +{ + if (nr_servers != 1) { + error (g, _("ssh: you must specify exactly one server")); + return NULL; + } + + if (servers[0].transport != drive_transport_none && + servers[0].transport != drive_transport_tcp) { + error (g, _("ssh: only tcp transport is supported")); + return NULL; + } + + if (STREQ (exportname, "")) { + error (g, _("ssh: pathname should not be an empty string")); + return NULL; + } + + if (username && STREQ (username, "")) { + error (g, _("ssh: username should not be an empty string")); + return NULL; + } + + return create_drive_non_file (g, drive_protocol_ssh, + servers, nr_servers, exportname, username, readonly, format, iface, name, disk_label, use_cache_none); } @@ -637,6 +694,7 @@ guestfs__add_drive_opts (guestfs_h *g, const char *filename, const char *protocol; size_t nr_servers = 0; struct drive_server *servers = NULL; + const char *username; int use_cache_none; struct drive *drv; size_t i, drv_index; @@ -665,6 +723,8 @@ guestfs__add_drive_opts (guestfs_h *g, const char *filename, return -1; nr_servers = r; } + username = optargs->bitmask & GUESTFS_ADD_DRIVE_OPTS_USERNAME_BITMASK + ? optargs->username : NULL; if (format && !valid_format_iface (format)) { error (g, _("%s parameter is empty or contains disallowed characters"), @@ -690,6 +750,11 @@ guestfs__add_drive_opts (guestfs_h *g, const char *filename, free_drive_servers (servers, nr_servers); return -1; } + if (username != NULL) { + error (g, _("you cannot specify a username with file-backed disks")); + free_drive_servers (servers, nr_servers); + return -1; + } if (STREQ (filename, "/dev/null")) drv = create_drive_dev_null (g, readonly, format, iface, name, @@ -715,25 +780,30 @@ guestfs__add_drive_opts (guestfs_h *g, const char *filename, } } else if (STREQ (protocol, "gluster")) { - drv = create_drive_gluster (g, servers, nr_servers, filename, + drv = create_drive_gluster (g, servers, nr_servers, filename, username, readonly, format, iface, name, disk_label, false); } else if (STREQ (protocol, "nbd")) { - drv = create_drive_nbd (g, servers, nr_servers, filename, + drv = create_drive_nbd (g, servers, nr_servers, filename, username, readonly, format, iface, name, disk_label, false); } else if (STREQ (protocol, "rbd")) { - drv = create_drive_rbd (g, servers, nr_servers, filename, + drv = create_drive_rbd (g, servers, nr_servers, filename, username, readonly, format, iface, name, disk_label, false); } else if (STREQ (protocol, "sheepdog")) { - drv = create_drive_sheepdog (g, servers, nr_servers, filename, + drv = create_drive_sheepdog (g, servers, nr_servers, filename, username, readonly, format, iface, name, disk_label, false); } + else if (STREQ (protocol, "ssh")) { + drv = create_drive_ssh (g, servers, nr_servers, filename, username, + readonly, format, iface, name, + disk_label, false); + } else { error (g, _("unknown protocol '%s'"), protocol); drv = NULL; /*FALLTHROUGH*/ @@ -1030,6 +1100,21 @@ guestfs___drive_source_qemu_param (guestfs_h *g, const struct drive_source *src) return safe_asprintf (g, "sheepdog:%s:%d:%s", src->servers[0].u.hostname, src->servers[0].port, src->u.exportname); + + case drive_protocol_ssh: { + CLEANUP_FREE char *username = NULL, *port = NULL; + + if (src->username) + username = safe_asprintf (g, "%s@", username); + if (src->servers[0].port != 0) + port = safe_asprintf (g, ":%d", src->servers[0].port); + + return safe_asprintf (g, "ssh://%s%s%s/%s", + username ? username : "", + src->servers[0].u.hostname, + port ? port : "", + src->u.exportname); + } } abort (); @@ -1040,6 +1125,7 @@ guestfs___free_drive_source (struct drive_source *src) { if (src) { free (src->u.path); + free (src->username); free_drive_servers (src->servers, src->nr_servers); } } diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h index 34d3bd7..49c36da 100644 --- a/src/guestfs-internal.h +++ b/src/guestfs-internal.h @@ -119,6 +119,7 @@ enum drive_protocol { drive_protocol_nbd, drive_protocol_rbd, drive_protocol_sheepdog, + drive_protocol_ssh, }; enum drive_transport { @@ -157,6 +158,9 @@ struct drive_source { */ size_t nr_servers; struct drive_server *servers; + + /* Optional username (may be NULL if not specified). */ + char *username; }; struct drive { diff --git a/src/guestfs.pod b/src/guestfs.pod index 2173527..a271300 100644 --- a/src/guestfs.pod +++ b/src/guestfs.pod @@ -757,6 +757,24 @@ The optional list of C<servers> may be zero or more server addresses (C<"hostname:port">). The format of the server strings is documented in L</guestfs_add_drive_opts>. +=head3 SSH + +Libguestfs can access disks over a Secure Shell (SSH) connection. + +To do this, set the C<protocol> and C<server> and (optionally) +C<username> parameters of L</guestfs_add_drive_opts> like this: + + char **server = { "remote.example.com", NULL }; + guestfs_add_drive_opts (g, "/path/to/disk.img", + GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw", + GUESTFS_ADD_DRIVE_OPTS_PROTOCOL, "ssh", + GUESTFS_ADD_DRIVE_OPTS_SERVER, server, + GUESTFS_ADD_DRIVE_OPTS_USERNAME, "remoteuser", + -1); + +The format of the server string is documented in +L</guestfs_add_drive_opts>. + =head2 INSPECTION Libguestfs has APIs for inspecting an unknown disk image to find out diff --git a/src/launch-libvirt.c b/src/launch-libvirt.c index f831ebc..335fb71 100644 --- a/src/launch-libvirt.c +++ b/src/launch-libvirt.c @@ -1120,7 +1120,9 @@ construct_libvirt_xml_disk (guestfs_h *g, case drive_protocol_rbd: protocol_str = "rbd"; goto network_protocols; case drive_protocol_sheepdog: - protocol_str = "sheepdog"; + protocol_str = "sheepdog"; goto network_protocols; + case drive_protocol_ssh: + protocol_str = "ssh"; /*FALLTHROUGH*/ network_protocols: XMLERROR (-1, @@ -1140,6 +1142,13 @@ construct_libvirt_xml_disk (guestfs_h *g, if (construct_libvirt_xml_disk_source_seclabel (g, xo) == -1) return -1; XMLERROR (-1, xmlTextWriterEndElement (xo)); + if (drv_priv->real_src.username != NULL) { + XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "auth")); + XMLERROR (-1, + xmlTextWriterWriteAttribute (xo, BAD_CAST "username", + BAD_CAST drv_priv->real_src.username)); + XMLERROR (-1, xmlTextWriterEndElement (xo)); + } } XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "target")); @@ -1592,6 +1601,7 @@ make_drive_priv (guestfs_h *g, struct drive *drv, case drive_protocol_nbd: case drive_protocol_rbd: case drive_protocol_sheepdog: + case drive_protocol_ssh: if (!drv->readonly) { guestfs___copy_drive_source (g, &drv->src, &drv_priv->real_src); drv_priv->format = drv->format ? safe_strdup (g, drv->format) : NULL; -- 1.8.1.4
Richard W.M. Jones
2013-Apr-05 13:21 UTC
[Libguestfs] [PATCH] Add support for SSH (Secure Shell) block device.
On Fri, Apr 05, 2013 at 02:06:07PM +0100, Richard W.M. Jones wrote:> Note this patch requires a non-upstream qemu patch that I've been > experimenting with. See qemu-devel list.Actual link to the latest (v7) version: http://www.mail-archive.com/qemu-devel at nongnu.org/msg164860.html Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming blog: http://rwmj.wordpress.com Fedora now supports 80 OCaml packages (the OPEN alternative to F#)
Richard W.M. Jones
2013-Apr-16 08:15 UTC
[Libguestfs] [PATCH] Add support for SSH (Secure Shell) block device.
On Fri, Apr 05, 2013 at 02:06:07PM +0100, Richard W.M. Jones wrote:> Note this patch requires a non-upstream qemu patch that I've been > experimenting with. See qemu-devel list.Since block/ssh.c is now upstream in qemu, I am going to push this patch shortly (after a bit of testing to ensure it still works with the upstreamed version). Note that although this includes a libvirt backend, that doesn't actually work without non-upstream changes to libvirt, which are still under heavy development. So this will only work with LIBGUESTFS_BACKEND=direct right now. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming blog: http://rwmj.wordpress.com Fedora now supports 80 OCaml packages (the OPEN alternative to F#)