Add support for SSH (Secure Shell) block device.

This commit is contained in:
Richard W.M. Jones
2013-04-05 13:01:19 +01:00
parent ca11cd104d
commit cfda2693c6
5 changed files with 153 additions and 15 deletions

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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 {

View File

@@ -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

View File

@@ -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;