[libvirt] [PATCH] Power Hypervisor Support for libvirt - minimum set of features

Hello all, This is the initial patch for the driver for IBM Power Hypervisors. The minimum set of features are now implemented: list, list --all and dumpxml. Here is the Changeset since last PATCH I sent: * The URI has changed to: phyp://user@[hmc|ivm]/managed_system. If the system is a HMC+VIOS based, only an HMC authentication will be required. Commands will be sent to VIOS trough HMC command line. And if the system is an IVM based, then just provide the username and password for IVM. * Since the Power Hypervisor has no information about UUID's, I built a little database (uuid_db) to store and associate LPAR ID's with UUID randomly generated by the API. * The command dumpxml is implemented, but there are some informations missing. Fetching informations like fstab, os type, uptime, IP addr and so on, will only be available in a future versions of the HMC system. * The TODO list is now set to implement life cycle functions. Thanks in advance, []'s -- Eduardo Otubo Software Engineer Linux Technology Center IBM Systems & Technology Group Mobile: +55 19 8135 0885 otubo@linux.vnet.ibm.com

Hello again, This is the life cycle operations I've been working on these days. Fortunately this is a smaller and more punctual diff. :) Any comment is always welcome. []'s On Mon, 2009-06-22 at 18:57 -0300, Eduardo Otubo wrote:
Hello all,
This is the initial patch for the driver for IBM Power Hypervisors. The minimum set of features are now implemented: list, list --all and dumpxml. Here is the Changeset since last PATCH I sent:
* The URI has changed to: phyp://user@[hmc|ivm]/managed_system. If the system is a HMC+VIOS based, only an HMC authentication will be required. Commands will be sent to VIOS trough HMC command line. And if the system is an IVM based, then just provide the username and password for IVM.
* Since the Power Hypervisor has no information about UUID's, I built a little database (uuid_db) to store and associate LPAR ID's with UUID randomly generated by the API.
* The command dumpxml is implemented, but there are some informations missing. Fetching informations like fstab, os type, uptime, IP addr and so on, will only be available in a future versions of the HMC system.
* The TODO list is now set to implement life cycle functions.
Thanks in advance, []'s
-- Libvir-list mailing list Libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
-- Eduardo Otubo Software Engineer Linux Technology Center IBM Systems & Technology Group Mobile: +55 19 8135 0885 otubo@linux.vnet.ibm.com

Hello all, This is the last version of this enormous PATCH. All minor bugs are fixed, and the minimal set of features are implemented. Any comments are alwayes welcome. []'s On Tue, 2009-06-23 at 10:55 -0300, Eduardo Otubo wrote:
Hello again,
This is the life cycle operations I've been working on these days. Fortunately this is a smaller and more punctual diff. :)
Any comment is always welcome. []'s
On Mon, 2009-06-22 at 18:57 -0300, Eduardo Otubo wrote:
Hello all,
This is the initial patch for the driver for IBM Power Hypervisors. The minimum set of features are now implemented: list, list --all and dumpxml. Here is the Changeset since last PATCH I sent:
* The URI has changed to: phyp://user@[hmc|ivm]/managed_system. If the system is a HMC+VIOS based, only an HMC authentication will be required. Commands will be sent to VIOS trough HMC command line. And if the system is an IVM based, then just provide the username and password for IVM.
* Since the Power Hypervisor has no information about UUID's, I built a little database (uuid_db) to store and associate LPAR ID's with UUID randomly generated by the API.
* The command dumpxml is implemented, but there are some informations missing. Fetching informations like fstab, os type, uptime, IP addr and so on, will only be available in a future versions of the HMC system.
* The TODO list is now set to implement life cycle functions.
Thanks in advance, []'s
[]'s -- Eduardo Otubo Software Engineer Linux Technology Center IBM Systems & Technology Group Mobile: +55 19 8135 0885 otubo@linux.vnet.ibm.com

On Mon, Jul 06, 2009 at 04:40:58PM -0300, Eduardo Otubo wrote:
Hello all,
This is the last version of this enormous PATCH. All minor bugs are fixed, and the minimal set of features are implemented. Any comments are alwayes welcome.
This looks like it has addressed all the issues I raised last time around. There's still a few more improvements I'd like to see, but I think it'll be easier to just merge this and do additive patches later. It is getting too cumbersome to keep this out of tree. NB, the specific thing you should try and address real soon is escaping and quoting of shell args eg in this function
+static char * +phypGetLparNAME(SSH_SESSION * ssh_session, const char *managed_system, + unsigned int lpar_id, virConnectPtr conn) +{ + char *cmd; + int exit_status = 0; + + if (virAsprintf(&cmd, + "lssyscfg -r lpar -m %s --filter lpar_ids=%d -F name", + managed_system, lpar_id) < 0) {
The 'managed_system' argument comes from the URI the client passes into the virConnectOpen call. You really want to make sure that's got any shell meta characters escaped, and quoted, otherwise they could use that to run arbitrary commands on the remote system. Daniel
diff --git a/configure.in b/configure.in index 29c8867..6812a39 100644 --- a/configure.in +++ b/configure.in @@ -186,6 +186,10 @@ AC_ARG_WITH([uml], [ --with-uml add UML support (on)],[],[with_uml=yes]) AC_ARG_WITH([openvz], [ --with-openvz add OpenVZ support (on)],[],[with_openvz=yes]) +AC_ARG_WITH([libssh], +[ --with-libssh=[PFX] libssh location],[],[with_libssh=yes]) +AC_ARG_WITH([phyp], +[ --with-phyp=[PFX] add PHYP support (on)],[with_phyp=yes],[with_phyp=check]) AC_ARG_WITH([vbox], [ --with-vbox add VirtualBox support (on)],[],[with_vbox=yes]) AC_ARG_WITH([lxc], @@ -768,7 +772,50 @@ AM_CONDITIONAL([HAVE_NUMACTL], [test "$with_numactl" != "no"]) AC_SUBST([NUMACTL_CFLAGS]) AC_SUBST([NUMACTL_LIBS])
- +if test "$with_libssh" != "yes" -a "$with_libssh" != "no"; then + libssh_path="$with_libssh" +elif test "$with_libssh" = "yes"; then + libssh_path="/usr/local/lib/" +elif test "$with_libssh" = "no"; then + with_phyp="no"; +fi + +if test "$with_phyp" = "check"; then + AC_CHECK_LIB([ssh],[ssh_new],[ + LIBSSH_LIBS="$LIBSSH_LIBS -lssh -L$libssh_path" + AC_SUBST([LIBSSH_LIBS])],[ + with_phyp="no" + with_libssh="no"; + ],[]) + + if test "$with_phyp" != "no"; then + AC_CHECK_HEADERS([libssh/libssh.h],[ + with_phyp="yes" + LIBSSH_CFLAGS="-I/usr/local/include/libssh" + AC_SUBST([LIBSSH_CFLAGS]) + AC_DEFINE_UNQUOTED([WITH_PHYP], 1, + [whether IBM HMC / IVM driver is enabled]) + ],[ + with_phyp="no" + with_libssh="no"; + ],[]) + fi +elif test "$with_phyp" = "yes"; then + AC_CHECK_LIB([ssh],[ssh_new],[ + LIBSSH_LIBS="$LIBSSH_LIBS -lssh -L$libssh_path" + AC_SUBST([LIBSSH_LIBS])],[ + AC_MSG_ERROR([You must install the libssh to compile Phype driver.]) + ]) + + AC_CHECK_HEADERS([libssh/libssh.h],[ + LIBSSH_CFLAGS="-I/usr/local/include/libssh" + AC_SUBST([LIBSSH_CFLAGS])],[ + AC_MSG_ERROR([Cannot find libssh headers.Is libssh installed ?]) + ],[]) + AC_DEFINE_UNQUOTED([WITH_PHYP], 1, + [whether IBM HMC / IVM driver is enabled]) +fi +AM_CONDITIONAL([WITH_PHYP],[test "$with_phyp" = "yes"])
dnl libcap-ng AC_ARG_WITH([capng], @@ -809,8 +856,6 @@ AM_CONDITIONAL([HAVE_CAPNG], [test "$with_capng" != "no"]) AC_SUBST([CAPNG_CFLAGS]) AC_SUBST([CAPNG_LIBS])
- - dnl virsh libraries AC_CHECK_HEADERS([readline/readline.h])
@@ -1477,6 +1522,7 @@ AC_MSG_NOTICE([ UML: $with_uml]) AC_MSG_NOTICE([ OpenVZ: $with_openvz]) AC_MSG_NOTICE([ VBox: $with_vbox]) AC_MSG_NOTICE([ LXC: $with_lxc]) +AC_MSG_NOTICE([ PHYP: $with_phyp]) AC_MSG_NOTICE([ ONE: $with_one]) AC_MSG_NOTICE([ Test: $with_test]) AC_MSG_NOTICE([ Remote: $with_remote]) @@ -1508,6 +1554,11 @@ fi AC_MSG_NOTICE([]) AC_MSG_NOTICE([Libraries]) AC_MSG_NOTICE([]) +if test "$with_libssh" != "no" ; then +AC_MSG_NOTICE([ libssh: $LIBSSH_CFLAGS $LIBSSH_LIBS]) +else +AC_MSG_NOTICE([ libssh: no]) +fi AC_MSG_NOTICE([ libxml: $LIBXML_CFLAGS $LIBXML_LIBS]) AC_MSG_NOTICE([ gnutls: $GNUTLS_CFLAGS $GNUTLS_LIBS]) if test "$with_sasl" != "no" ; then diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index 1092896..511f0f6 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -65,6 +65,7 @@ typedef enum { VIR_FROM_VBOX, /* Error from VirtualBox driver */ VIR_FROM_INTERFACE, /* Error when operating on an interface */ VIR_FROM_ONE, /* Error from OpenNebula driver */ + VIR_FROM_PHYP, /* Error from IBM power hypervisor */ } virErrorDomain;
diff --git a/src/Makefile.am b/src/Makefile.am index 0c284c0..8e39686 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -5,6 +5,7 @@ INCLUDES = \ -I../include \ -I@top_srcdir@/include \ -I@top_srcdir@/qemud \ + $(LIBSSH_CFLAGS) \ $(LIBXML_CFLAGS) \ $(XEN_CFLAGS) \ $(SELINUX_CFLAGS) \ @@ -126,6 +127,9 @@ LXC_CONTROLLER_SOURCES = \ veth.c veth.h \ cgroup.c cgroup.h
+PHYP_DRIVER_SOURCES = \ + phyp/phyp_driver.c phyp/phyp_driver.h + OPENVZ_DRIVER_SOURCES = \ openvz_conf.c openvz_conf.h \ openvz_driver.c openvz_driver.h @@ -279,6 +283,18 @@ endif libvirt_driver_xen_la_SOURCES = $(XEN_DRIVER_SOURCES) endif
+if WITH_PHYP +if WITH_DRIVER_MODULES +mod_LTLIBRARIES += libvirt_driver_phyp.la +else +noinst_LTLIBRARIES += libvirt_driver_phyp.la +libvirt_la_LIBADD += libvirt_driver_phyp.la +endif +libvirt_driver_phyp_la_LDFLAGS = $(LIBSSH_LIBS) +libvirt_driver_phyp_la_CFLAGS = $(LIBSSH_CFLAGS) +libvirt_driver_phyp_la_SOURCES = $(PHYP_DRIVER_SOURCES) +endif + if WITH_OPENVZ if WITH_DRIVER_MODULES mod_LTLIBRARIES += libvirt_driver_openvz.la @@ -470,6 +486,7 @@ EXTRA_DIST += \ $(UML_DRIVER_SOURCES) \ $(ONE_DRIVER_SOURCES) \ $(OPENVZ_DRIVER_SOURCES) \ + $(PHYP_DRIVER_SOURCES) \ $(VBOX_DRIVER_SOURCES) \ $(NETWORK_DRIVER_SOURCES) \ $(STORAGE_DRIVER_SOURCES) \ diff --git a/src/domain_conf.c b/src/domain_conf.c index ffa2aef..e524c5b 100644 --- a/src/domain_conf.c +++ b/src/domain_conf.c @@ -57,7 +57,8 @@ VIR_ENUM_IMPL(virDomainVirt, VIR_DOMAIN_VIRT_LAST, "vmware", "hyperv", "vbox", - "one") + "one", + "phyp")
VIR_ENUM_IMPL(virDomainBoot, VIR_DOMAIN_BOOT_LAST, "fd", diff --git a/src/domain_conf.h b/src/domain_conf.h index 51310c1..76cd43f 100644 --- a/src/domain_conf.h +++ b/src/domain_conf.h @@ -50,6 +50,7 @@ enum virDomainVirtType { VIR_DOMAIN_VIRT_HYPERV, VIR_DOMAIN_VIRT_VBOX, VIR_DOMAIN_VIRT_ONE, + VIR_DOMAIN_VIRT_PHYP,
VIR_DOMAIN_VIRT_LAST, }; diff --git a/src/driver.h b/src/driver.h index ca759ff..94ffe5d 100644 --- a/src/driver.h +++ b/src/driver.h @@ -22,6 +22,7 @@ typedef enum { VIR_DRV_UML = 7, VIR_DRV_VBOX = 8, VIR_DRV_ONE = 9, + VIR_DRV_PHYP = 10, } virDrvNo;
diff --git a/src/libvirt.c b/src/libvirt.c index 33eafcb..a7f47dd 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -55,6 +55,9 @@ #ifdef WITH_OPENVZ #include "openvz_driver.h" #endif +#ifdef WITH_PHYP +#include "phyp/phyp_driver.h" +#endif #ifdef WITH_VBOX #include "vbox/vbox_driver.h" #endif @@ -319,6 +322,9 @@ virInitialize(void) #ifdef WITH_OPENVZ if (openvzRegister() == -1) return -1; #endif +#ifdef WITH_PHYP + if (phypRegister() == -1) return -1; +#endif #ifdef WITH_VBOX if (vboxRegister() == -1) return -1; #endif @@ -889,6 +895,10 @@ virGetVersion(unsigned long *libVer, const char *type, if (STRCASEEQ(type, "LXC")) *typeVer = LIBVIR_VERSION_NUMBER; #endif +#if WITH_PHYP + if (STRCASEEQ(type, "phyp")) + *typeVer = LIBVIR_VERSION_NUMBER; +#endif #if WITH_OPENVZ if (STRCASEEQ(type, "OpenVZ")) *typeVer = LIBVIR_VERSION_NUMBER; diff --git a/src/phyp/phyp_driver.c b/src/phyp/phyp_driver.c new file mode 100644 index 0000000..3166a7a --- /dev/null +++ b/src/phyp/phyp_driver.c @@ -0,0 +1,1335 @@ + +/* + * Copyright IBM Corp. 2009 + * + * phyp_driver.c: ssh layer to access Power Hypervisors + * + * Authors: + * Eduardo Otubo <otubo at linux.vnet.ibm.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <config.h> + +#include <sys/types.h> +#include <limits.h> +#include <string.h> +#include <strings.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <stdio.h> + +#include <libssh/libssh.h> + +#include "internal.h" +#include "util.h" +#include "datatypes.h" +#include "buf.h" +#include "memory.h" +#include "logging.h" +#include "driver.h" +#include "libvirt/libvirt.h" +#include "virterror_internal.h" +#include "uuid.h" +#include "domain_conf.h" + +#include "phyp_driver.h" + +#define VIR_FROM_THIS VIR_FROM_PHYP + +/* + * URI: phyp://user@[hmc|ivm]/managed_system + * */ + +static virDrvOpenStatus +phypOpen(virConnectPtr conn, + virConnectAuthPtr auth, int flags ATTRIBUTE_UNUSED) +{ + SSH_SESSION *session; + ConnectionData *connection_data; + + uuid_dbPtr uuid_db = NULL; + + if (VIR_ALLOC(uuid_db) < 0) + virReportOOMError(conn); + + if (VIR_ALLOC(connection_data) < 0) + virReportOOMError(conn); + + if (!conn || !conn->uri) + return VIR_DRV_OPEN_DECLINED; + + if (conn->uri->scheme == NULL || STRNEQ(conn->uri->scheme, "phyp")) + return VIR_DRV_OPEN_DECLINED; + + + if (conn->uri->server == NULL) { + virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP, + VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s", + _("Missing server name in phyp:// URI")); + return VIR_DRV_OPEN_ERROR; + } + if (conn->uri->path == NULL) { + virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP, + VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s", + _("Missing path name in phyp:// URI")); + return VIR_DRV_OPEN_ERROR; + } + + if ((session = openSSHSession(conn, auth)) == NULL) { + virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP, + VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s", + _("Error while opening SSH session.")); + return VIR_DRV_OPEN_ERROR; + } + + connection_data->session = session; + connection_data->auth = auth; + + uuid_db->nlpars = 0; + uuid_db->lpars = NULL; + + conn->privateData = uuid_db; + conn->networkPrivateData = connection_data; + init_uuid_db(conn); + + return VIR_DRV_OPEN_SUCCESS; +} + +static int +phypClose(virConnectPtr conn) +{ + ConnectionData *connection_data = conn->networkPrivateData; + SSH_SESSION *ssh_session = connection_data->session; + + ssh_disconnect(ssh_session); + + VIR_FREE(connection_data); + return 0; +} + +SSH_SESSION * +openSSHSession(virConnectPtr conn, virConnectAuthPtr auth) +{ + SSH_SESSION *session; + SSH_OPTIONS *opt; + char *user = conn->uri->user; + char *host = conn->uri->server; + int ssh_auth = 0; + char *banner; + int port = 22; + char *password; + + if (conn->uri->port) + port = conn->uri->port; + + session = ssh_new(); + opt = ssh_options_new(); + + /*setting some ssh options */ + ssh_options_set_host(opt, host); + ssh_options_set_port(opt, port); + ssh_options_set_username(opt, user); + ssh_set_options(session, opt); + + /*starting ssh connection */ + if (ssh_connect(session)) { + virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP, VIR_ERR_ERROR, + NULL, NULL, NULL, 0, 0, "%s", + _("Connection failed.")); + ssh_disconnect(session); + ssh_finalize(); + goto err; + } + + /*trying to use pub key */ + if ((ssh_auth = + ssh_userauth_autopubkey(session, NULL)) == SSH_AUTH_ERROR) { + VIR_WARN("%s", "Authentication with public key failed."); + } + + if ((banner = ssh_get_issue_banner(session))) { + VIR_INFO("%s", banner); + VIR_FREE(banner); + } + + if (ssh_auth != SSH_AUTH_SUCCESS) { + int i; + int hasPassphrase = 0; + int auth_check = 0; + + virConnectCredential creds[] = { + {VIR_CRED_PASSPHRASE, "password", "Password", NULL, NULL, 0}, + }; + + if (!auth || !auth->cb) { + virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP, + VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s", + _("No authentication callback provided.")); + goto err; + } + + for (i = 0; i < auth->ncredtype; i++) { + if (auth->credtype[i] == VIR_CRED_PASSPHRASE) + hasPassphrase = 1; + } + + if (!hasPassphrase) { + virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP, + VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s", + _("Required credentials are not supported.")); + goto err; + } + + int res = + (auth->cb) (creds, ARRAY_CARDINALITY(creds), auth->cbdata); + + if (res < 0) { + virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP, + VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s", + _("Unable to fetch credentials.")); + goto err; + } + + if (creds[0].result) + password = creds[0].result; + else { + virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP, + VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s : %s", + "Unable to get password certificate.", + ssh_get_error(session)); + ssh_disconnect(session); + goto err; + } + + char *username = user; + + auth_check = ssh_userauth_password(session, username, password); + memset(password, 0, strlen(password)); + + if (auth_check != SSH_AUTH_SUCCESS) { + virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP, + VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s : %s", + "Authentication failed.", + ssh_get_error(session)); + ssh_disconnect(session); + goto err; + } else + goto exit; + } else + goto exit; + + err: + return NULL; + + exit: + return session; +} + +/* this functions is the layer that manipulates the ssh channel itself + * and executes the commands on the remote machine */ +static char * +phypExec(SSH_SESSION * session, char *cmd, int *exit_status, + virConnectPtr conn) +{ + CHANNEL *channel = channel_new(session); + virBuffer tex_ret = VIR_BUFFER_INITIALIZER; + char buf[4096] = { 0 }; + int ret = 0; + + if (channel_open_session(channel) == SSH_ERROR) { + virRaiseError(NULL, NULL, NULL, 0, VIR_FROM_PHYP, + VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s", + _("Unable to open a SSH channel.")); + goto err; + } + + if (channel_request_exec(channel, cmd) == SSH_ERROR) { + virRaiseError(NULL, NULL, NULL, 0, VIR_FROM_PHYP, + VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s", + _("Unable to execute remote command.")); + goto err; + } + + if (channel_send_eof(channel) == SSH_ERROR) { + virRaiseError(NULL, NULL, NULL, 0, VIR_FROM_PHYP, + VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s", + _("Unable to send EOF.")); + goto err; + } + + while (channel && channel_is_open(channel)) { + ret = channel_read(channel, buf, sizeof(buf), 0); + if (ret < 0) + goto err; + + if (ret == 0) { + channel_send_eof(channel); + if (channel_get_exit_status(channel) == -1) + goto err; + + if (channel_close(channel) == SSH_ERROR) + goto err; + + channel_free(channel); + channel = NULL; + goto exit; + } + + virBufferAdd(&tex_ret, (const char *) &buf, ret); + } + + err: + (*exit_status) = SSH_CMD_ERR; + char *cleanup_buf = virBufferContentAndReset(&tex_ret); + + VIR_FREE(cleanup_buf); + return NULL; + + exit: + if (virBufferError(&tex_ret)) { + virReportOOMError(conn); + return NULL; + } + return virBufferContentAndReset(&tex_ret); +} + +/* return the lpar_id given a name and a managed system name */ +static int +phypGetLparID(SSH_SESSION * ssh_session, const char *managed_system, + const char *name, virConnectPtr conn) +{ + int exit_status = 0; + int lpar_id = 0; + char *char_ptr; + char *cmd; + + if (virAsprintf(&cmd, + "lssyscfg -r lpar -m %s --filter lpar_names=%s -F lpar_id", + managed_system, name) < 0) { + virReportOOMError(conn); + goto err; + } + + const char *tex_ret = phypExec(ssh_session, cmd, &exit_status, conn); + + if (exit_status < 0 || tex_ret == NULL) + goto err; + + if (virStrToLong_i(tex_ret, &char_ptr, 10, &lpar_id) == -1) + goto err; + + VIR_FREE(cmd); + return lpar_id; + + err: + VIR_FREE(cmd); + return -1; +} + +/* return the lpar name given a lpar_id and a managed system name */ +static char * +phypGetLparNAME(SSH_SESSION * ssh_session, const char *managed_system, + unsigned int lpar_id, virConnectPtr conn) +{ + char *cmd; + int exit_status = 0; + + if (virAsprintf(&cmd, + "lssyscfg -r lpar -m %s --filter lpar_ids=%d -F name", + managed_system, lpar_id) < 0) { + virReportOOMError(conn); + goto err; + } + + char *lpar_name = + phypExec(ssh_session, cmd, (int *) exit_status, conn); + + if (lpar_name == NULL) + goto err; + + char *char_ptr = strchr(lpar_name, '\n'); + + if (char_ptr) + *char_ptr = '\0'; + + if (exit_status < 0 || lpar_name == NULL) + goto err; + + VIR_FREE(cmd); + return lpar_name; + + err: + VIR_FREE(cmd); + return NULL; +} + + +/* Search into the uuid_db for a lpar_uuid given a lpar_id + * and a managed system name + * + * return: 0 - record found + * -1 - not found + * */ +int +phypGetLparUUID(unsigned char *uuid, int lpar_id, virConnectPtr conn) +{ + uuid_dbPtr uuid_db = conn->privateData; + lparPtr *lpars = uuid_db->lpars; + unsigned int i = 0; + + for (i = 0; i < uuid_db->nlpars; i++) { + if (lpars[i]->id == lpar_id) { + memmove(uuid, lpars[i]->uuid, VIR_UUID_BUFLEN); + return 0; + } + } + + return -1; +} + +/* + * type: + * 0 - maxmem + * 1 - memory + * */ +unsigned long +phypGetLparMem(virConnectPtr conn, const char *managed_system, int lpar_id, + int type) +{ + ConnectionData *connection_data = conn->networkPrivateData; + SSH_SESSION *ssh_session = connection_data->session; + char *cmd; + char *char_ptr; + int memory = 0; + int exit_status = 0; + + if (type != 1 && type != 0) + goto err; + + if (type) { + if (virAsprintf(&cmd, + "lshwres -m %s -r mem --level lpar -F curr_mem --filter lpar_ids=%d", + managed_system, lpar_id) < 0) { + virReportOOMError(conn); + goto err; + } + } else { + if (virAsprintf(&cmd, + "lshwres -m %s -r mem --level lpar -F curr_max_mem --filter lpar_ids=%d", + managed_system, lpar_id) < 0) { + virReportOOMError(conn); + goto err; + } + } + + char *tex_ret = phypExec(ssh_session, cmd, + (int *) exit_status, conn); + + if (tex_ret == NULL) + goto err; + + char *mem_char_ptr = strchr(tex_ret, '\n'); + + if (mem_char_ptr) + *mem_char_ptr = '\0'; + + if (exit_status < 0) + goto err; + + if (virStrToLong_i(tex_ret, &char_ptr, 10, &memory) == -1) + goto err; + + VIR_FREE(cmd); + return memory; + + err: + VIR_FREE(cmd); + return 0; + +} + +unsigned long +phypGetLparCPU(virConnectPtr conn, const char *managed_system, int lpar_id) +{ + ConnectionData *connection_data = conn->networkPrivateData; + SSH_SESSION *ssh_session = connection_data->session; + char *cmd; + int exit_status = 0; + int vcpus = 0; + + if (virAsprintf(&cmd, + "lshwres -m %s -r proc --level lpar -F curr_procs --filter lpar_ids=%d", + managed_system, lpar_id) < 0) { + virReportOOMError(conn); + goto err; + } + char *tex_ret = phypExec(ssh_session, cmd, &exit_status, conn); + + if (tex_ret == NULL) + goto err; + + char *char_ptr = strchr(tex_ret, '\n'); + + if (char_ptr) + *char_ptr = '\0'; + + if (virStrToLong_i(tex_ret, &char_ptr, 10, &vcpus) == -1) + goto err; + + if (exit_status < 0) + goto err; + + VIR_FREE(cmd); + return (unsigned long) vcpus; + + err: + VIR_FREE(cmd); + return 0; +} + +int +phypGetRemoteSlot(virConnectPtr conn, const char *managed_system, + const char *lpar_name) +{ + ConnectionData *connection_data = conn->networkPrivateData; + SSH_SESSION *ssh_session = connection_data->session; + char *cmd; + char *char_ptr; + int remote_slot = 0; + int exit_status = 0; + + if (virAsprintf(&cmd, + "lshwres -m %s -r virtualio --rsubtype scsi -F remote_slot_num --filter lpar_names=%s", + managed_system, lpar_name) < 0) { + virReportOOMError(conn); + goto err; + } + char *tex_ret = phypExec(ssh_session, cmd, + (int *) exit_status, conn); + + if (tex_ret == NULL) + goto err; + + char *char_ptr2 = strchr(tex_ret, '\n'); + + if (char_ptr2) + *char_ptr2 = '\0'; + + if (exit_status < 0) + goto err; + + if (virStrToLong_i(tex_ret, &char_ptr, 10, &remote_slot) == -1) + goto err; + + VIR_FREE(cmd); + return remote_slot; + + err: + VIR_FREE(cmd); + return 0; +} + +char * +phypGetBackingDevice(virConnectPtr conn, const char *managed_system, + char *lpar_name) +{ + ConnectionData *connection_data = conn->networkPrivateData; + SSH_SESSION *ssh_session = connection_data->session; + char *cmd; + int remote_slot = 0; + int exit_status = 0; + + if ((remote_slot = + phypGetRemoteSlot(conn, managed_system, lpar_name)) == 0) + goto err; + + if (virAsprintf(&cmd, + "lshwres -m %s -r virtualio --rsubtype scsi -F backing_devices --filter slots=%d", + managed_system, remote_slot) < 0) { + virReportOOMError(conn); + goto err; + } + + char *ret = phypExec(ssh_session, cmd, (int *) exit_status, conn); + + if (ret == NULL) + goto err; + + /* here is a little trick to deal returns of this kind: + * + * 0x8100000000000000//lv01 + * + * the information we really need is only lv01, so we + * need to skip a lot of things on the string. + * */ + char *backing_device = strchr(ret, '/'); + + if (backing_device) { + backing_device++; + if (backing_device[0] == '/') + backing_device++; + else + goto err; + } else { + backing_device = ret; + } + + char *char_ptr = strchr(backing_device, '\n'); + + if (char_ptr) + *char_ptr = '\0'; + + if (exit_status < 0 || backing_device == NULL) + goto err; + + VIR_FREE(cmd); + return backing_device; + + err: + VIR_FREE(cmd); + return NULL; + +} + +int +phypGetLparState(virConnectPtr conn, unsigned int lpar_id) +{ + ConnectionData *connection_data = conn->networkPrivateData; + SSH_SESSION *ssh_session = connection_data->session; + char *cmd; + int exit_status = 0; + char *char_ptr = NULL; + char *managed_system = conn->uri->path; + + /* need to shift one byte in order to remove the first "/" of URI component */ + if (managed_system[0] == '/') + managed_system++; + + /* here we are handling only the first component of the path, + * so skipping the second: + * */ + + char_ptr = strchr(managed_system, '/'); + + if (char_ptr) + *char_ptr = '\0'; + + if (virAsprintf(&cmd, + "lssyscfg -r lpar -m %s -F state --filter lpar_ids=%d", + managed_system, lpar_id) < 0) { + virReportOOMError(conn); + goto err; + } + + char *ret = phypExec(ssh_session, cmd, (int *) exit_status, conn); + + if (ret == NULL) + goto err; + + char_ptr = strchr(ret, '\n'); + + if (char_ptr) + *char_ptr = '\0'; + + if (exit_status < 0 || ret == NULL) + goto err; + + VIR_FREE(cmd); + if (STREQ(ret, "Running")) + return VIR_DOMAIN_RUNNING; + else if (STREQ(ret, "Not Activated")) + return VIR_DOMAIN_SHUTOFF; + else if (STREQ(ret, "Shutting Down")) + return VIR_DOMAIN_SHUTDOWN; + else + goto err; + + err: + VIR_FREE(cmd); + return VIR_DOMAIN_NOSTATE; +} + +int +phypDiskType(virConnectPtr conn, char *backing_device) +{ + ConnectionData *connection_data = conn->networkPrivateData; + SSH_SESSION *ssh_session = connection_data->session; + char *cmd; + int exit_status = 0; + + if (virAsprintf(&cmd, + "ioscli lssp -field name type -fmt , -all|grep %s|sed -e 's/^.*,//g'", + backing_device) < 0) { + virReportOOMError(conn); + goto err; + } + + char *ret = phypExec(ssh_session, cmd, (int *) exit_status, conn); + + if (ret == NULL) + goto err; + + char *char_ptr = strchr(ret, '\n'); + + if (char_ptr) + *char_ptr = '\0'; + + if (exit_status < 0 || ret == NULL) + goto err; + + VIR_FREE(cmd); + if (STREQ(ret, "LVPOOL")) + return VIR_DOMAIN_DISK_TYPE_BLOCK; + else if (STREQ(ret, "FBPOOL")) + return VIR_DOMAIN_DISK_TYPE_FILE; + else + goto err; + + err: + VIR_FREE(cmd); + return -1; +} + +/* This is a generic function that won't be used directly by + * libvirt api. The function returns the number of domains + * in different states: Running, Not Activated and all: + * + * type: 0 - Running + * 1 - Not Activated + * * - All + * */ +static int +phypNumDomainsGeneric(virConnectPtr conn, unsigned int type) +{ + ConnectionData *connection_data = conn->networkPrivateData; + SSH_SESSION *ssh_session = connection_data->session; + int exit_status = 0; + int ndom = 0; + char *char_ptr; + char *cmd; + char *managed_system = conn->uri->path; + const char *state; + + if (type == 0) + state = "|grep Running"; + else if (type == 1) + state = "|grep \"Not Activated\""; + else + state = " "; + + /* need to shift one byte in order to remove the first "/" of URI component */ + if (managed_system[0] == '/') + managed_system++; + + /* here we are handling only the first component of the path, + * so skipping the second: + * */ + + char_ptr = strchr(managed_system, '/'); + + if (char_ptr) + *char_ptr = '\0'; + + if (virAsprintf(&cmd, + "lssyscfg -r lpar -m %s -F lpar_id,state %s |grep -c ^[0-9]*", + managed_system, state) < 0) { + virReportOOMError(conn); + goto err; + } + + char *ret = phypExec(ssh_session, cmd, &exit_status, conn); + + if (exit_status < 0 || ret == NULL) + goto err; + + if (virStrToLong_i(ret, &char_ptr, 10, &ndom) == -1) + goto err; + + VIR_FREE(cmd); + return ndom; + + err: + VIR_FREE(cmd); + return 0; +} + +static int +phypNumDefinedDomains(virConnectPtr conn) +{ + return phypNumDomainsGeneric(conn, 1); +} + +static int +phypNumDomains(virConnectPtr conn) +{ + return phypNumDomainsGeneric(conn, 0); +} + +/* This is a generic function that won't be used directly by + * libvirt api. The function returns the ids of domains + * in different states: Running, and all: + * + * type: 0 - Running + * * - all + * */ +static int +phypListDomainsGeneric(virConnectPtr conn, int *ids, int nids, + unsigned int type) +{ + ConnectionData *connection_data = conn->networkPrivateData; + SSH_SESSION *ssh_session = connection_data->session; + char *managed_system = conn->uri->path; + int exit_status = 0; + int got = 0; + char *char_ptr; + unsigned int i = 0, j = 0; + char id_c[10]; + char *cmd; + const char *state; + + if (type == 0) + state = "|grep Running"; + else + state = " "; + + /* need to shift one byte in order to remove the first "/" of URI component */ + if (managed_system[0] == '/') + managed_system++; + + /* here we are handling only the first component of the path, + * so skipping the second: + * */ + char_ptr = strchr(managed_system, '/'); + + if (char_ptr) + *char_ptr = '\0'; + + memset(id_c, 0, 10); + + if (virAsprintf + (&cmd, + "lssyscfg -r lpar -m %s -F lpar_id,state %s | sed -e 's/,.*$//g'", + managed_system, state) < 0) { + virReportOOMError(conn); + goto err; + } + char *domains = phypExec(ssh_session, cmd, &exit_status, conn); + + /* I need to parse the textual return in order to get the domains */ + if (exit_status < 0 || domains == NULL) + goto err; + else { + while (got < nids) { + if (domains[i] == '\n') { + if (virStrToLong_i(id_c, &char_ptr, 10, &ids[got]) == -1) + return 0; + memset(id_c, 0, 10); + j = 0; + got++; + } else { + id_c[j] = domains[i]; + j++; + } + i++; + } + } + + VIR_FREE(cmd); + return got; + + err: + VIR_FREE(cmd); + return 0; +} + +static int +phypListDomains(virConnectPtr conn, int *ids, int nids) +{ + return phypListDomainsGeneric(conn, ids, nids, 0); +} + +static int +phypListDefinedDomains(virConnectPtr conn, char **const names, int nnames) +{ + ConnectionData *connection_data = conn->networkPrivateData; + SSH_SESSION *ssh_session = connection_data->session; + char *managed_system = conn->uri->path; + int exit_status = 0; + int got = 0; + char *char_ptr = NULL; + char *cmd; + char *domains; + + /* need to shift one byte in order to remove the first "/" of URI component */ + if (managed_system[0] == '/') + managed_system++; + + /* here we are handling only the first component of the path, + * so skipping the second: + * */ + char_ptr = strchr(managed_system, '/'); + + if (char_ptr) + *char_ptr = '\0'; + + if (virAsprintf + (&cmd, + "lssyscfg -r lpar -m %s -F name,state | grep \"Not Activated\" | sed -e 's/,.*$//g'", + managed_system) < 0) { + virReportOOMError(conn); + goto err; + } + + char *ret = phypExec(ssh_session, cmd, &exit_status, conn); + + if (VIR_ALLOC(domains) < 0) + virReportOOMError(conn); + + domains = strdup(ret); + if (!domains) + goto err; + + char *char_ptr2 = NULL; + /* I need to parse the textual return in order to get the domains */ + if (exit_status < 0 || domains == NULL) + goto err; + else { + while (got < nnames) { + char_ptr2 = strchr(domains, '\n'); + + if (char_ptr2) { + *char_ptr2 = '\0'; + if (!strdup(domains)) + goto err; + names[got] = strdup(domains); + char_ptr2++; + domains = char_ptr2; + got++; + } + } + } + + VIR_FREE(domains); + VIR_FREE(cmd); + VIR_FREE(ret); + return got; + + err: + VIR_FREE(domains); + VIR_FREE(ret); + return 0; +} + +static virDomainPtr +phypDomainLookupByName(virConnectPtr conn, const char *lpar_name) +{ + ConnectionData *connection_data = conn->networkPrivateData; + SSH_SESSION *ssh_session = connection_data->session; + virDomainPtr dom = NULL; + int lpar_id = 0; + char *managed_system = conn->uri->path; + unsigned char *lpar_uuid = NULL; + + if (VIR_ALLOC_N(lpar_uuid, VIR_UUID_BUFLEN) < 0) + virReportOOMError(dom->conn); + + /* need to shift one byte in order to remove the first "/" of uri component */ + if (managed_system[0] == '/') + managed_system++; + + /* here we are handling only the first component of the path, + * so skipping the second: + * */ + char *char_ptr = strchr(managed_system, '/'); + + if (char_ptr) + *char_ptr = '\0'; + + lpar_id = phypGetLparID(ssh_session, managed_system, lpar_name, conn); + if (lpar_id < 0) + goto err; + + if (phypGetLparUUID(lpar_uuid, lpar_id, conn) == -1) + goto err; + + dom = virGetDomain(conn, lpar_name, lpar_uuid); + + if (dom) + dom->id = lpar_id; + + VIR_FREE(lpar_uuid); + return dom; + + err: + VIR_FREE(lpar_uuid); + return NULL; +} + +static virDomainPtr +phypDomainLookupByID(virConnectPtr conn, int lpar_id) +{ + ConnectionData *connection_data = conn->networkPrivateData; + SSH_SESSION *ssh_session = connection_data->session; + virDomainPtr dom = NULL; + char *managed_system = conn->uri->path; + int exit_status = 0; + unsigned char *lpar_uuid = NULL; + + if (VIR_ALLOC_N(lpar_uuid, VIR_UUID_BUFLEN) < 0) + virReportOOMError(dom->conn); + + /* need to shift one byte in order to remove the first "/" of uri component */ + if (managed_system[0] == '/') + managed_system++; + + /* here we are handling only the first component of the path, + * so skipping the second: + * */ + char *char_ptr = strchr(managed_system, '/'); + + if (char_ptr) + *char_ptr = '\0'; + + char *lpar_name = phypGetLparNAME(ssh_session, managed_system, lpar_id, + conn); + + if (phypGetLparUUID(lpar_uuid, lpar_id, conn) == -1) + goto err; + + if (exit_status < 0) + goto err; + + dom = virGetDomain(conn, lpar_name, lpar_uuid); + + if (dom) + dom->id = lpar_id; + + VIR_FREE(lpar_name); + VIR_FREE(lpar_uuid); + return dom; + + err: + VIR_FREE(lpar_name); + VIR_FREE(lpar_uuid); + return NULL; +} + +static char * +phypDomainDumpXML(virDomainPtr dom, int flags) +{ + ConnectionData *connection_data = dom->conn->networkPrivateData; + SSH_SESSION *ssh_session = connection_data->session; + virDomainDefPtr def = NULL; + char *ret = NULL; + char *managed_system = dom->conn->uri->path; + unsigned char *lpar_uuid = NULL; + + if (VIR_ALLOC_N(lpar_uuid, VIR_UUID_BUFLEN) < 0) + virReportOOMError(dom->conn); + + if (VIR_ALLOC(def) < 0) + virReportOOMError(dom->conn); + + /* need to shift one byte in order to remove the first "/" of uri component */ + if (managed_system[0] == '/') + managed_system++; + + /* here we are handling only the first component of the path, + * so skipping the second: + * */ + char *char_ptr = strchr(managed_system, '/'); + + if (char_ptr) + *char_ptr = '\0'; + + def->virtType = VIR_DOMAIN_VIRT_PHYP; + def->id = dom->id; + + char *lpar_name = phypGetLparNAME(ssh_session, managed_system, def->id, + dom->conn); + + if (lpar_name == NULL) { + VIR_ERROR("%s", "Unable to determine domain's name."); + goto err; + } + + if (phypGetLparUUID(lpar_uuid, dom->id, dom->conn) == -1) { + VIR_ERROR("%s", "Unable to generate random uuid."); + goto err; + } + + if (!memcpy(def->uuid, lpar_uuid, VIR_UUID_BUFLEN)) { + VIR_ERROR("%s", "Unable to generate random uuid."); + goto err; + } + + if ((def->maxmem = + phypGetLparMem(dom->conn, managed_system, dom->id, 0)) == 0) { + VIR_ERROR("%s", "Unable to determine domain's max memory."); + goto err; + } + + if ((def->memory = + phypGetLparMem(dom->conn, managed_system, dom->id, 1)) == 0) { + VIR_ERROR("%s", "Unable to determine domain's memory."); + goto err; + } + + if ((def->vcpus = + phypGetLparCPU(dom->conn, managed_system, dom->id)) == 0) { + VIR_ERROR("%s", "Unable to determine domain's CPU."); + goto err; + } + + ret = virDomainDefFormat(dom->conn, def, flags); + + err: + VIR_FREE(def); + return ret; +} + +static int +phypDomainResume(virDomainPtr dom) +{ + ConnectionData *connection_data = dom->conn->networkPrivateData; + SSH_SESSION *ssh_session = connection_data->session; + char *managed_system = dom->conn->uri->path; + int exit_status = 0; + char *char_ptr = NULL; + char *cmd; + + /* need to shift one byte in order to remove the first "/" of URI component */ + if (managed_system[0] == '/') + managed_system++; + + /* here we are handling only the first component of the path, + * so skipping the second: + * */ + char_ptr = strchr(managed_system, '/'); + + if (char_ptr) + *char_ptr = '\0'; + + if (virAsprintf + (&cmd, + "chsysstate -m %s -r lpar -o on --id %d -f %s", + managed_system, dom->id, dom->name) < 0) { + virReportOOMError(dom->conn); + goto err; + } + + char *ret = phypExec(ssh_session, cmd, &exit_status, dom->conn); + + err: + VIR_FREE(cmd); + VIR_FREE(ret); + return 0; + +} + +static int +phypDomainShutdown(virDomainPtr dom) +{ + ConnectionData *connection_data = dom->conn->networkPrivateData; + SSH_SESSION *ssh_session = connection_data->session; + char *managed_system = dom->conn->uri->path; + int exit_status = 0; + char *char_ptr = NULL; + char *cmd; + + /* need to shift one byte in order to remove the first "/" of URI component */ + if (managed_system[0] == '/') + managed_system++; + + /* here we are handling only the first component of the path, + * so skipping the second: + * */ + char_ptr = strchr(managed_system, '/'); + + if (char_ptr) + *char_ptr = '\0'; + + if (virAsprintf + (&cmd, + "chsysstate -m %s -r lpar -o shutdown --id %d", + managed_system, dom->id) < 0) { + virReportOOMError(dom->conn); + goto err; + } + + char *ret = phypExec(ssh_session, cmd, &exit_status, dom->conn); + + err: + VIR_FREE(cmd); + VIR_FREE(ret); + return 0; + +} + +static int +phypDomainGetInfo(virDomainPtr dom, virDomainInfoPtr info) +{ + char *managed_system = dom->conn->uri->path; + + /* need to shift one byte in order to remove the first "/" of uri component */ + if (managed_system[0] == '/') + managed_system++; + + /* here we are handling only the first component of the path, + * so skipping the second: + * */ + char *char_ptr = strchr(managed_system, '/'); + + if (char_ptr) + *char_ptr = '\0'; + + info->state = phypGetLparState(dom->conn, dom->id); + + if ((info->maxMem = + phypGetLparMem(dom->conn, managed_system, dom->id, 0)) == 0) + VIR_WARN("%s", "Unable to determine domain's max memory."); + + if ((info->memory = + phypGetLparMem(dom->conn, managed_system, dom->id, 1)) == 0) + VIR_WARN("%s", "Unable to determine domain's memory."); + + if ((info->nrVirtCpu = + phypGetLparCPU(dom->conn, managed_system, dom->id)) == 0) + VIR_WARN("%s", "Unable to determine domain's CPU."); + + return 0; + +} + +virDriver phypDriver = { + VIR_DRV_PHYP, + "PHYP", + phypOpen, /* open */ + phypClose, /* close */ + NULL, /* supports_feature */ + NULL, /* type */ + NULL, /* version */ + NULL, /* getHostname */ + NULL, /* getMaxVcpus */ + NULL, /* nodeGetInfo */ + NULL, /* getCapabilities */ + phypListDomains, /* listDomains */ + phypNumDomains, /* numOfDomains */ + NULL, /* domainCreateXML */ + phypDomainLookupByID, /* domainLookupByID */ + NULL, /* domainLookupByUUID */ + phypDomainLookupByName, /* domainLookupByName */ + NULL, /* domainSuspend */ + phypDomainResume, /* domainResume */ + phypDomainShutdown, /* domainShutdown */ + NULL, /* domainReboot */ + NULL, /* domainDestroy */ + NULL, /* domainGetOSType */ + NULL, /* domainGetMaxMemory */ + NULL, /* domainSetMaxMemory */ + NULL, /* domainSetMemory */ + phypDomainGetInfo, /* domainGetInfo */ + NULL, /* domainSave */ + NULL, /* domainRestore */ + NULL, /* domainCoreDump */ + NULL, /* domainSetVcpus */ + NULL, /* domainPinVcpu */ + NULL, /* domainGetVcpus */ + NULL, /* domainGetMaxVcpus */ + NULL, /* domainGetSecurityLabel */ + NULL, /* nodeGetSecurityModel */ + phypDomainDumpXML, /* domainDumpXML */ + NULL, /* domainXmlFromNative */ + NULL, /* domainXmlToNative */ + phypListDefinedDomains, /* listDefinedDomains */ + phypNumDefinedDomains, /* numOfDefinedDomains */ + NULL, /* domainCreate */ + NULL, /* domainDefineXML */ + NULL, /* domainUndefine */ + NULL, /* domainAttachDevice */ + NULL, /* domainDetachDevice */ + NULL, /* domainGetAutostart */ + NULL, /* domainSetAutostart */ + NULL, /* domainGetSchedulerType */ + NULL, /* domainGetSchedulerParameters */ + NULL, /* domainSetSchedulerParameters */ + NULL, /* domainMigratePrepare */ + NULL, /* domainMigratePerform */ + NULL, /* domainMigrateFinish */ + NULL, /* domainBlockStats */ + NULL, /* domainInterfaceStats */ + NULL, /* domainBlockPeek */ + NULL, /* domainMemoryPeek */ + NULL, /* nodeGetCellsFreeMemory */ + NULL, /* getFreeMemory */ + NULL, /* domainEventRegister */ + NULL, /* domainEventDeregister */ + NULL, /* domainMigratePrepare2 */ + NULL, /* domainMigrateFinish2 */ + NULL, /* nodeDeviceDettach */ + NULL, /* nodeDeviceReAttach */ + NULL, /* nodeDeviceReset */ +}; + +int +phypRegister(void) +{ + virRegisterDriver(&phypDriver); + return 0; +} + +void +init_uuid_db(virConnectPtr conn) +{ + uuid_dbPtr uuid_db; + int nids = 0; + int *ids = NULL; + unsigned int i = 0; + + if ((nids = phypNumDomainsGeneric(conn, 2)) == 0) + goto exit; + + if (VIR_ALLOC_N(ids, nids) < 0) + virReportOOMError(conn); + + if (VIR_ALLOC(uuid_db) < 0) + virReportOOMError(conn); + + if (phypListDomainsGeneric(conn, ids, nids, 1) == 0) + goto exit; + + uuid_db = conn->privateData; + uuid_db->nlpars = nids; + + if (VIR_ALLOC_N(uuid_db->lpars, uuid_db->nlpars) >= 0) { + for (i = 0; i < uuid_db->nlpars; i++) { + if (VIR_ALLOC(uuid_db->lpars[i]) < 0) + virReportOOMError(conn); + uuid_db->lpars[i]->id = ids[i]; + + if (virUUIDGenerate(uuid_db->lpars[i]->uuid) < 0) + VIR_WARN("%s %d", "Unable to generate UUID for domain", + ids[i]); + } + } + exit: + VIR_FREE(ids); + return; +} diff --git a/src/phyp/phyp_driver.h b/src/phyp/phyp_driver.h new file mode 100644 index 0000000..f16b6fe --- /dev/null +++ b/src/phyp/phyp_driver.h @@ -0,0 +1,64 @@ +#include <config.h> +#include <libssh/libssh.h> + +#define LPAR_EXEC_ERR -1 +#define SSH_CONN_ERR -2 /* error while trying to connect to remote host */ +#define SSH_CMD_ERR -3 /* error while trying to execute the remote cmd */ + +typedef struct _ConnectionData ConnectionData; +typedef ConnectionData *ConnectionDataPtr; +struct _ConnectionData { + SSH_SESSION *session; + virConnectAuthPtr auth; +}; + +/* This is the lpar (domain) struct that relates + * the ID with UUID generated by the API + * */ +typedef struct _lpar lpar_t; +typedef lpar_t *lparPtr; +struct _lpar { + unsigned char uuid[VIR_UUID_BUFLEN]; + int id; +}; + +/* Struct that holds how many lpars (domains) we're + * handling and a pointer to an array of lpar structs + * */ +typedef struct _uuid_db uuid_db_t; +typedef uuid_db_t *uuid_dbPtr; +struct _uuid_db { + int nlpars; + lparPtr *lpars; +}; + +int phypGetLparUUID(unsigned char *uuid, int lpar_id, virConnectPtr conn); + +void init_uuid_db(virConnectPtr conn); + +int phypRegister(void); + +void stripPath(char *striped_path, char *path); + +void stripNewline(char *striped_string, char *string); + +int buffer_add_u8(struct buffer_struct *buffer, u8 data); + +int phypGetLparState(virConnectPtr conn, unsigned int lpar_id); + +unsigned long phypGetLparMem(virConnectPtr conn, + const char *managed_system, int lpar_id, + int type); + +unsigned long phypGetLparCPU(virConnectPtr conn, + const char *managed_system, int lpar_id); + +int phypGetRemoteSlot(virConnectPtr conn, const char *managed_system, + const char *lpar_name); + +char *phypGetBackingDevice(virConnectPtr conn, const char *managed_system, + char *lpar_name); + +int phypDiskType(virConnectPtr conn, char *backing_device); + +SSH_SESSION *openSSHSession(virConnectPtr conn, virConnectAuthPtr auth); diff --git a/src/virterror.c b/src/virterror.c index d284fb8..d71cca4 100644 --- a/src/virterror.c +++ b/src/virterror.c @@ -124,6 +124,9 @@ static const char *virErrorDomainName(virErrorDomain domain) { case VIR_FROM_CONF: dom = "Config "; break; + case VIR_FROM_PHYP: + dom = "IBM power hypervisor "; + break; case VIR_FROM_OPENVZ: dom = "OpenVZ "; break;
-- Libvir-list mailing list Libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
-- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Mon, Jun 22, 2009 at 06:57:19PM -0300, Eduardo Otubo wrote:
Hello all,
This is the initial patch for the driver for IBM Power Hypervisors. The minimum set of features are now implemented: list, list --all and dumpxml. Here is the Changeset since last PATCH I sent:
* The URI has changed to: phyp://user@[hmc|ivm]/managed_system. If the system is a HMC+VIOS based, only an HMC authentication will be required. Commands will be sent to VIOS trough HMC command line. And if the system is an IVM based, then just provide the username and password for IVM.
* Since the Power Hypervisor has no information about UUID's, I built a little database (uuid_db) to store and associate LPAR ID's with UUID randomly generated by the API.
I might be missing something, but this database doesn't appear to be persistent at all - it just lives for the duration of the virConnectPtr object's lifetime. So if you create two connections you'd get two different UUIDs for the same VM.
* The command dumpxml is implemented, but there are some informations missing. Fetching informations like fstab, os type, uptime, IP addr and so on, will only be available in a future versions of the HMC system.
That's fine - starting simple is the way to go.
+/* + * URI: phyp://user@[hmc|ivm]/managed_system + * */ + +static virDrvOpenStatus +phypOpen(virConnectPtr conn, + virConnectAuthPtr auth, int flags ATTRIBUTE_UNUSED) +{ + SSH_SESSION *session; + ConnectionData *connection_data; + + uuid_dbPtr uuid_db = NULL; + + if (VIR_ALLOC(uuid_db) < 0) + virReportOOMError(conn); + + if (VIR_ALLOC(connection_data) < 0) + virReportOOMError(conn); + + if (!conn || !conn->uri) + return VIR_DRV_OPEN_DECLINED; + + if (conn->uri->scheme == NULL || conn->uri->server == NULL + || conn->uri->path == NULL) + return VIR_DRV_OPEN_DECLINED;
Here you need to check that the 'scheme' really is for your driver, before continuing. If the scheme matches your driver, then if there are any further errors such as missing server or path, then you need to return VIR_DRV_OPEN_ERROR instead of DECLINED. So this block should look like: if (conn->uri->scheme == NULL || STRNEQ(conn->uri->scheme, "phyp") return VIR_DRV_OPEN_DECLINED; if (conn->uri->server == NULL) { virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s", _("Missing server name in phyp:// URI")); return VIR_DRV_OPEN_ERROR; } if (conn->uri->path == NULL) { virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s", _("Missing path name in phyp:// URI")); return VIR_DRV_OPEN_ERROR; }
+ +SSH_SESSION * +openSSHSession(virConnectPtr conn, virConnectAuthPtr auth) +{ + SSH_SESSION *session; + SSH_OPTIONS *opt; + char *user = conn->uri->user; + char *host = conn->uri->server; + int ssh_auth = 0; + char *banner; + int port = 22; + + if (conn->uri->port) + port = conn->uri->port; + + session = ssh_new(); + opt = ssh_options_new(); + + /*setting some ssh options */ + ssh_options_set_host(opt, host); + ssh_options_set_port(opt, port); + ssh_options_set_username(opt, user); + ssh_set_options(session, opt); + + /*starting ssh connection */ + if (ssh_connect(session)) { + virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP, VIR_ERR_ERROR, + NULL, NULL, NULL, 0, 0, "%s", + _("Connection failed.")); + ssh_disconnect(session); + ssh_finalize(); + goto err; + } + + /*trying to use pub key */ + if ((ssh_auth = + ssh_userauth_autopubkey(session, NULL)) == SSH_AUTH_ERROR) { + VIR_WARN("%s", "Authentication with public key failed."); + } + + if ((banner = ssh_get_issue_banner(session))) { + VIR_WARN("%s", banner); + VIR_FREE(banner);
Just tweak this to VIR_INFO(), rather than WARN.
+ } + + if (ssh_auth != SSH_AUTH_SUCCESS) { + int i; + int hasPassphrase = 0; + int auth_check = 0; + + virConnectCredential creds[] = { + {VIR_CRED_PASSPHRASE, "password", "Password", NULL, NULL, 0}, + }; + + if (!auth || !auth->cb) { + virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP, + VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s", + _("No authentication callback provided.")); + goto err; + } + + for (i = 0; i < auth->ncredtype; i++) { + if (auth->credtype[i] == VIR_CRED_PASSPHRASE) + hasPassphrase = 1; + } + + if (!hasPassphrase) { + virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP, + VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s", + _("Required credentials are not supported.")); + goto err; + } + + int res = + (auth->cb) (creds, ARRAY_CARDINALITY(creds), auth->cbdata); + + if (res < 0) { + virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP, + VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s", + _("Unable to fetch credentials.")); + goto err; + } + + char *password = creds[0].result;
If it possible for creds[0].result to be NULL, even if 'res == 0' from the callback, so to be safe you should check for that before using the data.
+ + char *username = user; + + auth_check = ssh_userauth_password(session, username, password); + memset(password, 0, strlen(password)); + + if (auth_check != SSH_AUTH_SUCCESS) { + virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP, + VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s : %s", + "Authentication failed.", + ssh_get_error(session)); + ssh_disconnect(session); + goto err; + } else + goto exit; + } else + goto exit; + + err: + return NULL; + + exit: + return session; +} + +/* this functions is the layer that manipulates the ssh channel itself + * and executes the commands on the remote machine */ +static char * +exec(SSH_SESSION * session, char *cmd, int *exit_status, + virConnectPtr conn)
I'd recommend renaming this to something that is not simply called 'exec' to avoid readers getting confused with the 'exec' system calls. Perhaps something like phypRemoteExec()
+{ + CHANNEL *channel = channel_new(session); + virBuffer tex_ret = VIR_BUFFER_INITIALIZER; + char buf[4096] = { 0 }; + int ret = 0; + + if (channel_open_session(channel) == SSH_ERROR) { + virRaiseError(NULL, NULL, NULL, 0, VIR_FROM_PHYP, + VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s", + _("Unable to open a SSH channel.")); + goto err; + } + + if (channel_request_exec(channel, cmd) == SSH_ERROR) { + virRaiseError(NULL, NULL, NULL, 0, VIR_FROM_PHYP, + VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s", + _("Unable to execute remote command.")); + goto err; + } + + if (channel_send_eof(channel) == SSH_ERROR) { + virRaiseError(NULL, NULL, NULL, 0, VIR_FROM_PHYP, + VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s", + _("Unable to send EOF.")); + goto err; + } + + while (channel && channel_is_open(channel)) { + ret = channel_read(channel, buf, sizeof(buf), 0); + if (ret < 0) + goto err; + + if (ret == 0) { + channel_send_eof(channel); + if (channel_get_exit_status(channel) == -1) + goto err; + + if (channel_close(channel) == SSH_ERROR) + goto err; + + channel_free(channel); + channel = NULL; + goto exit; + } + + virBufferAdd(&tex_ret, (const char *) &buf, sizeof(buf));
I'm thinking that you should be passing 'ret' rather than sizeof(buf) here, since I imagine its possible for the channel_read() command to return less than a full buffer worth of data.
+ } + + err: + (*exit_status) = SSH_CMD_ERR; + char *cleanup_buf = virBufferContentAndReset(&tex_ret); + + VIR_FREE(cleanup_buf); + return NULL; + + exit: + if (virBufferError(&tex_ret)) { + virReportOOMError(conn); + return NULL; + } + return virBufferContentAndReset(&tex_ret); +} + + +/* This is a generic function that won't be used directly by + * libvirt api. The function returns the number of domains + * in different states: Running, Not Activated and all: + * + * type: 0 - Running + * 1 - Not Activated + * * - All + * */ +static int +phypNumDomainsGeneric(virConnectPtr conn, unsigned int type) +{ + ConnectionData *connection_data = conn->networkPrivateData; + SSH_SESSION *ssh_session = connection_data->session; + int exit_status = 0; + int ndom = 0; + char *char_ptr; + char *cmd; + char *managed_system = conn->uri->path; + virBuffer state = VIR_BUFFER_INITIALIZER; + + if (type == 0) + virBufferAddLit(&state, "|grep Running"); + else if (type == 1) + virBufferAddLit(&state, "|grep \"Not Activated\""); + else + virBufferAddLit(&state, " "); + + /* need to shift one byte in order to remove the first "/" of URI component */ + if (managed_system[0] == '/') + managed_system++; + + /* here we are handling only the first component of the path, + * so skipping the second: + * */ + + char_ptr = strchr(managed_system, '/'); + + if (char_ptr) + *char_ptr = '\0'; + + if (virAsprintf(&cmd, + "lssyscfg -r lpar -m %s -F lpar_id,state %s |grep -c ^[0-9]*", + managed_system, + virBufferContentAndReset(&state)) < 0) { + virReportOOMError(conn); + goto err; + }
This has a small memory leak - you need to free the pointer returned by a virBufferContentAndReset() call. It is alittle overkill to use a virBuffer here, since you only initialize it with a const string. It would thus be simpler to just do const char *state; if (type == 0) state = "|grep Running"; else if (type == 1) state = "|grep \"Not Activated\""; else state = " ";
+ + char *ret = exec(ssh_session, cmd, &exit_status, conn); + + if (exit_status < 0 || ret == NULL) + goto err; + + if (virStrToLong_i(ret, &char_ptr, 10, &ndom) == -1) + goto err; + + VIR_FREE(cmd); + return ndom; + + err: + VIR_FREE(cmd); + return 0; +} + +static int + * */ +static int +phypListDomainsGeneric(virConnectPtr conn, int *ids, int nids, + unsigned int type) +{ + ConnectionData *connection_data = conn->networkPrivateData; + SSH_SESSION *ssh_session = connection_data->session; + virBuffer state = VIR_BUFFER_INITIALIZER; + char *managed_system = conn->uri->path; + int exit_status = 0; + int got = 0; + char *char_ptr; + unsigned int i = 0, j = 0; + char id_c[10]; + char *cmd; + + if (type == 0) + virBufferAddLit(&state, "|grep Running"); + else + virBufferAddLit(&state, " "); + + /* need to shift one byte in order to remove the first "/" of URI component */ + if (managed_system[0] == '/') + managed_system++; + + /* here we are handling only the first component of the path, + * so skipping the second: + * */ + char_ptr = strchr(managed_system, '/'); + + if (char_ptr) + *char_ptr = '\0'; + + memset(id_c, 0, 10); + + if (virAsprintf + (&cmd, + "lssyscfg -r lpar -m %s -F lpar_id,state %s | sed -e 's/,.*$//g'", + managed_system, virBufferContentAndReset(&state)) < 0) { + virReportOOMError(conn); + goto err; + }
Same recommendation here - just use a const char * string for the last 'state' field.
+ return phypListDomainsGeneric(conn, ids, nids, 0); +} + +static int +phypListDefinedDomains(virConnectPtr conn, char **const names, int nnames) +{ + ConnectionData *connection_data = conn->networkPrivateData; + SSH_SESSION *ssh_session = connection_data->session; + char *managed_system = conn->uri->path; + int exit_status = 0; + int got = 0; + char *char_ptr = NULL; + char *cmd; + + /* need to shift one byte in order to remove the first "/" of URI component */ + if (managed_system[0] == '/') + managed_system++; + + /* here we are handling only the first component of the path, + * so skipping the second: + * */ + char_ptr = strchr(managed_system, '/'); + + if (char_ptr) + *char_ptr = '\0'; + + if (virAsprintf + (&cmd, + "lssyscfg -r lpar -m %s -F name,state | grep \"Not Activated\" | sed -e 's/,.*$//g'", + managed_system) < 0) { + virReportOOMError(conn); + goto err; + } + char *ret = exec(ssh_session, cmd, &exit_status, conn); + char *domains = malloc(sizeof(ret));
malloc() is on our banned list - switch to VIR_ALLOC() here.
+ domains = strdup(ret); + + char *char_ptr2 = NULL; + /* I need to parse the textual return in order to get the domains */ + if (exit_status < 0 || domains == NULL) + goto err; + else { + while (got < nnames) { + char_ptr2 = strchr(domains, '\n'); + + if (char_ptr2) { + *char_ptr2 = '\0'; + names[got] = strdup(domains);
Need to check for strdup() returning NULL, and cleanup and return an OOM error code.
+ char_ptr2++; + domains = char_ptr2; + got++; + } + } + } + + VIR_FREE(cmd); + VIR_FREE(ret); + return got; + + err: + VIR_FREE(cmd); + VIR_FREE(ret); + return 0; +} + + +static char * +phypDomainDumpXML(virDomainPtr dom, int flags) +{ + ConnectionData *connection_data = dom->conn->networkPrivateData; + SSH_SESSION *ssh_session = connection_data->session; + virDomainDefPtr def = NULL; + char *ret = NULL; + char *managed_system = dom->conn->uri->path; + unsigned char *lpar_uuid = NULL; + + if (VIR_ALLOC_N(lpar_uuid, VIR_UUID_BUFLEN) < 0) + virReportOOMError(dom->conn); + + if (VIR_ALLOC(def) < 0) + virReportOOMError(dom->conn); + + /* need to shift one byte in order to remove the first "/" of uri component */ + if (managed_system[0] == '/') + managed_system++; + + /* here we are handling only the first component of the path, + * so skipping the second: + * */ + char *char_ptr = strchr(managed_system, '/'); + + if (char_ptr) + *char_ptr = '\0'; + + def->virtType = VIR_DOMAIN_VIRT_PHYP; + def->id = dom->id; + + char *lpar_name = phypGetLparNAME(ssh_session, managed_system, def->id, + dom->conn); + + if (lpar_name == NULL) + VIR_WARN("%s", "Unable to determine domain's name."); + + if (phypGetLparUUID(lpar_uuid, dom->id, dom->conn) == -1) + VIR_WARN("%s", "Unable to generate random uuid."); + + if (!memcpy(def->uuid, lpar_uuid, VIR_UUID_BUFLEN)) + VIR_WARN("%s", "Unable to generate random uuid."); + + if ((def->maxmem = + phypGetLparMem(dom->conn, managed_system, dom->id, 0)) == 0) + VIR_WARN("%s", "Unable to determine domain's max memory."); + + if ((def->memory = + phypGetLparMem(dom->conn, managed_system, dom->id, 1)) == 0) + VIR_WARN("%s", "Unable to determine domain's memory."); + + if ((def->vcpus = + phypGetLparCPU(dom->conn, managed_system, dom->id)) == 0) + VIR_WARN("%s", "Unable to determine domain's CPU.");
I'm thinking some, if not all of these should probably be treated as fatal errors, rather than just warnings. Regards, Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

Hello Daniel,
I might be missing something, but this database doesn't appear to be persistent at all - it just lives for the duration of the virConnectPtr object's lifetime. So if you create two connections you'd get two different UUIDs for the same VM.
Yes, that's right. I thought I could make it work kinda stable before make it persistent. And here I have a little question: The way I thought I could make it persistent is just to implement the virDomainDefineXML function. This way, I would make init__uid_db() check if there is some xml file defined, if not just create a new UUID for that lpar. But, in this case, virsh/libvirtd could be run from anywhere, so the xml file would be in other machine and so on. Then I thought I could create the XML in HMC's machine, then I would have a little more control of the UUIDs. What do you think? []'s -- Eduardo Otubo Software Engineer Linux Technology Center IBM Systems & Technology Group Mobile: +55 19 8135 0885 otubo@linux.vnet.ibm.com

On Mon, Jun 29, 2009 at 04:11:20PM -0300, Eduardo Otubo wrote:
Hello Daniel,
I might be missing something, but this database doesn't appear to be persistent at all - it just lives for the duration of the virConnectPtr object's lifetime. So if you create two connections you'd get two different UUIDs for the same VM.
Yes, that's right. I thought I could make it work kinda stable before make it persistent.
And here I have a little question: The way I thought I could make it persistent is just to implement the virDomainDefineXML function. This way, I would make init__uid_db() check if there is some xml file defined, if not just create a new UUID for that lpar.
But, in this case, virsh/libvirtd could be run from anywhere, so the xml file would be in other machine and so on. Then I thought I could create the XML in HMC's machine, then I would have a little more control of the UUIDs. What do you think?
I think you'd be better with the latter idea if possible - its fairly important to have stable UUID's no matter where you connect from Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

And all the other comments you did are now fixed. Thanks. (and sorry for the top posting) []'s On Mon, 2009-06-29 at 10:44 +0100, Daniel P. Berrange wrote:
On Mon, Jun 22, 2009 at 06:57:19PM -0300, Eduardo Otubo wrote:
Hello all,
This is the initial patch for the driver for IBM Power Hypervisors. The minimum set of features are now implemented: list, list --all and dumpxml. Here is the Changeset since last PATCH I sent:
* The URI has changed to: phyp://user@[hmc|ivm]/managed_system. If the system is a HMC+VIOS based, only an HMC authentication will be required. Commands will be sent to VIOS trough HMC command line. And if the system is an IVM based, then just provide the username and password for IVM.
* Since the Power Hypervisor has no information about UUID's, I built a little database (uuid_db) to store and associate LPAR ID's with UUID randomly generated by the API.
I might be missing something, but this database doesn't appear to be persistent at all - it just lives for the duration of the virConnectPtr object's lifetime. So if you create two connections you'd get two different UUIDs for the same VM.
* The command dumpxml is implemented, but there are some informations missing. Fetching informations like fstab, os type, uptime, IP addr and so on, will only be available in a future versions of the HMC system.
That's fine - starting simple is the way to go.
+/* + * URI: phyp://user@[hmc|ivm]/managed_system + * */ + +static virDrvOpenStatus +phypOpen(virConnectPtr conn, + virConnectAuthPtr auth, int flags ATTRIBUTE_UNUSED) +{ + SSH_SESSION *session; + ConnectionData *connection_data; + + uuid_dbPtr uuid_db = NULL; + + if (VIR_ALLOC(uuid_db) < 0) + virReportOOMError(conn); + + if (VIR_ALLOC(connection_data) < 0) + virReportOOMError(conn); + + if (!conn || !conn->uri) + return VIR_DRV_OPEN_DECLINED; + + if (conn->uri->scheme == NULL || conn->uri->server == NULL + || conn->uri->path == NULL) + return VIR_DRV_OPEN_DECLINED;
Here you need to check that the 'scheme' really is for your driver, before continuing. If the scheme matches your driver, then if there are any further errors such as missing server or path, then you need to return VIR_DRV_OPEN_ERROR instead of DECLINED. So this block should look like:
if (conn->uri->scheme == NULL || STRNEQ(conn->uri->scheme, "phyp") return VIR_DRV_OPEN_DECLINED;
if (conn->uri->server == NULL) { virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s", _("Missing server name in phyp:// URI")); return VIR_DRV_OPEN_ERROR; } if (conn->uri->path == NULL) { virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s", _("Missing path name in phyp:// URI")); return VIR_DRV_OPEN_ERROR; }
+ +SSH_SESSION * +openSSHSession(virConnectPtr conn, virConnectAuthPtr auth) +{ + SSH_SESSION *session; + SSH_OPTIONS *opt; + char *user = conn->uri->user; + char *host = conn->uri->server; + int ssh_auth = 0; + char *banner; + int port = 22; + + if (conn->uri->port) + port = conn->uri->port; + + session = ssh_new(); + opt = ssh_options_new(); + + /*setting some ssh options */ + ssh_options_set_host(opt, host); + ssh_options_set_port(opt, port); + ssh_options_set_username(opt, user); + ssh_set_options(session, opt); + + /*starting ssh connection */ + if (ssh_connect(session)) { + virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP, VIR_ERR_ERROR, + NULL, NULL, NULL, 0, 0, "%s", + _("Connection failed.")); + ssh_disconnect(session); + ssh_finalize(); + goto err; + } + + /*trying to use pub key */ + if ((ssh_auth = + ssh_userauth_autopubkey(session, NULL)) == SSH_AUTH_ERROR) { + VIR_WARN("%s", "Authentication with public key failed."); + } + + if ((banner = ssh_get_issue_banner(session))) { + VIR_WARN("%s", banner); + VIR_FREE(banner);
Just tweak this to VIR_INFO(), rather than WARN.
+ } + + if (ssh_auth != SSH_AUTH_SUCCESS) { + int i; + int hasPassphrase = 0; + int auth_check = 0; + + virConnectCredential creds[] = { + {VIR_CRED_PASSPHRASE, "password", "Password", NULL, NULL, 0}, + }; + + if (!auth || !auth->cb) { + virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP, + VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s", + _("No authentication callback provided.")); + goto err; + } + + for (i = 0; i < auth->ncredtype; i++) { + if (auth->credtype[i] == VIR_CRED_PASSPHRASE) + hasPassphrase = 1; + } + + if (!hasPassphrase) { + virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP, + VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s", + _("Required credentials are not supported.")); + goto err; + } + + int res = + (auth->cb) (creds, ARRAY_CARDINALITY(creds), auth->cbdata); + + if (res < 0) { + virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP, + VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s", + _("Unable to fetch credentials.")); + goto err; + } + + char *password = creds[0].result;
If it possible for creds[0].result to be NULL, even if 'res == 0' from the callback, so to be safe you should check for that before using the data.
+ + char *username = user; + + auth_check = ssh_userauth_password(session, username, password); + memset(password, 0, strlen(password)); + + if (auth_check != SSH_AUTH_SUCCESS) { + virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP, + VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s : %s", + "Authentication failed.", + ssh_get_error(session)); + ssh_disconnect(session); + goto err; + } else + goto exit; + } else + goto exit; + + err: + return NULL; + + exit: + return session; +} + +/* this functions is the layer that manipulates the ssh channel itself + * and executes the commands on the remote machine */ +static char * +exec(SSH_SESSION * session, char *cmd, int *exit_status, + virConnectPtr conn)
I'd recommend renaming this to something that is not simply called 'exec' to avoid readers getting confused with the 'exec' system calls. Perhaps something like phypRemoteExec()
+{ + CHANNEL *channel = channel_new(session); + virBuffer tex_ret = VIR_BUFFER_INITIALIZER; + char buf[4096] = { 0 }; + int ret = 0; + + if (channel_open_session(channel) == SSH_ERROR) { + virRaiseError(NULL, NULL, NULL, 0, VIR_FROM_PHYP, + VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s", + _("Unable to open a SSH channel.")); + goto err; + } + + if (channel_request_exec(channel, cmd) == SSH_ERROR) { + virRaiseError(NULL, NULL, NULL, 0, VIR_FROM_PHYP, + VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s", + _("Unable to execute remote command.")); + goto err; + } + + if (channel_send_eof(channel) == SSH_ERROR) { + virRaiseError(NULL, NULL, NULL, 0, VIR_FROM_PHYP, + VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "%s", + _("Unable to send EOF.")); + goto err; + } + + while (channel && channel_is_open(channel)) { + ret = channel_read(channel, buf, sizeof(buf), 0); + if (ret < 0) + goto err; + + if (ret == 0) { + channel_send_eof(channel); + if (channel_get_exit_status(channel) == -1) + goto err; + + if (channel_close(channel) == SSH_ERROR) + goto err; + + channel_free(channel); + channel = NULL; + goto exit; + } + + virBufferAdd(&tex_ret, (const char *) &buf, sizeof(buf));
I'm thinking that you should be passing 'ret' rather than sizeof(buf) here, since I imagine its possible for the channel_read() command to return less than a full buffer worth of data.
+ } + + err: + (*exit_status) = SSH_CMD_ERR; + char *cleanup_buf = virBufferContentAndReset(&tex_ret); + + VIR_FREE(cleanup_buf); + return NULL; + + exit: + if (virBufferError(&tex_ret)) { + virReportOOMError(conn); + return NULL; + } + return virBufferContentAndReset(&tex_ret); +} + + +/* This is a generic function that won't be used directly by + * libvirt api. The function returns the number of domains + * in different states: Running, Not Activated and all: + * + * type: 0 - Running + * 1 - Not Activated + * * - All + * */ +static int +phypNumDomainsGeneric(virConnectPtr conn, unsigned int type) +{ + ConnectionData *connection_data = conn->networkPrivateData; + SSH_SESSION *ssh_session = connection_data->session; + int exit_status = 0; + int ndom = 0; + char *char_ptr; + char *cmd; + char *managed_system = conn->uri->path; + virBuffer state = VIR_BUFFER_INITIALIZER; + + if (type == 0) + virBufferAddLit(&state, "|grep Running"); + else if (type == 1) + virBufferAddLit(&state, "|grep \"Not Activated\""); + else + virBufferAddLit(&state, " "); + + /* need to shift one byte in order to remove the first "/" of URI component */ + if (managed_system[0] == '/') + managed_system++; + + /* here we are handling only the first component of the path, + * so skipping the second: + * */ + + char_ptr = strchr(managed_system, '/'); + + if (char_ptr) + *char_ptr = '\0'; + + if (virAsprintf(&cmd, + "lssyscfg -r lpar -m %s -F lpar_id,state %s |grep -c ^[0-9]*", + managed_system, + virBufferContentAndReset(&state)) < 0) { + virReportOOMError(conn); + goto err; + }
This has a small memory leak - you need to free the pointer returned by a virBufferContentAndReset() call. It is alittle overkill to use a virBuffer here, since you only initialize it with a const string. It would thus be simpler to just do
const char *state;
if (type == 0) state = "|grep Running"; else if (type == 1) state = "|grep \"Not Activated\""; else state = " ";
+ + char *ret = exec(ssh_session, cmd, &exit_status, conn); + + if (exit_status < 0 || ret == NULL) + goto err; + + if (virStrToLong_i(ret, &char_ptr, 10, &ndom) == -1) + goto err; + + VIR_FREE(cmd); + return ndom; + + err: + VIR_FREE(cmd); + return 0; +} + +static int + * */ +static int +phypListDomainsGeneric(virConnectPtr conn, int *ids, int nids, + unsigned int type) +{ + ConnectionData *connection_data = conn->networkPrivateData; + SSH_SESSION *ssh_session = connection_data->session; + virBuffer state = VIR_BUFFER_INITIALIZER; + char *managed_system = conn->uri->path; + int exit_status = 0; + int got = 0; + char *char_ptr; + unsigned int i = 0, j = 0; + char id_c[10]; + char *cmd; + + if (type == 0) + virBufferAddLit(&state, "|grep Running"); + else + virBufferAddLit(&state, " "); + + /* need to shift one byte in order to remove the first "/" of URI component */ + if (managed_system[0] == '/') + managed_system++; + + /* here we are handling only the first component of the path, + * so skipping the second: + * */ + char_ptr = strchr(managed_system, '/'); + + if (char_ptr) + *char_ptr = '\0'; + + memset(id_c, 0, 10); + + if (virAsprintf + (&cmd, + "lssyscfg -r lpar -m %s -F lpar_id,state %s | sed -e 's/,.*$//g'", + managed_system, virBufferContentAndReset(&state)) < 0) { + virReportOOMError(conn); + goto err; + }
Same recommendation here - just use a const char * string for the last 'state' field.
+ return phypListDomainsGeneric(conn, ids, nids, 0); +} + +static int +phypListDefinedDomains(virConnectPtr conn, char **const names, int nnames) +{ + ConnectionData *connection_data = conn->networkPrivateData; + SSH_SESSION *ssh_session = connection_data->session; + char *managed_system = conn->uri->path; + int exit_status = 0; + int got = 0; + char *char_ptr = NULL; + char *cmd; + + /* need to shift one byte in order to remove the first "/" of URI component */ + if (managed_system[0] == '/') + managed_system++; + + /* here we are handling only the first component of the path, + * so skipping the second: + * */ + char_ptr = strchr(managed_system, '/'); + + if (char_ptr) + *char_ptr = '\0'; + + if (virAsprintf + (&cmd, + "lssyscfg -r lpar -m %s -F name,state | grep \"Not Activated\" | sed -e 's/,.*$//g'", + managed_system) < 0) { + virReportOOMError(conn); + goto err; + } + char *ret = exec(ssh_session, cmd, &exit_status, conn); + char *domains = malloc(sizeof(ret));
malloc() is on our banned list - switch to VIR_ALLOC() here.
+ domains = strdup(ret); + + char *char_ptr2 = NULL; + /* I need to parse the textual return in order to get the domains */ + if (exit_status < 0 || domains == NULL) + goto err; + else { + while (got < nnames) { + char_ptr2 = strchr(domains, '\n'); + + if (char_ptr2) { + *char_ptr2 = '\0'; + names[got] = strdup(domains);
Need to check for strdup() returning NULL, and cleanup and return an OOM error code.
+ char_ptr2++; + domains = char_ptr2; + got++; + } + } + } + + VIR_FREE(cmd); + VIR_FREE(ret); + return got; + + err: + VIR_FREE(cmd); + VIR_FREE(ret); + return 0; +} + + +static char * +phypDomainDumpXML(virDomainPtr dom, int flags) +{ + ConnectionData *connection_data = dom->conn->networkPrivateData; + SSH_SESSION *ssh_session = connection_data->session; + virDomainDefPtr def = NULL; + char *ret = NULL; + char *managed_system = dom->conn->uri->path; + unsigned char *lpar_uuid = NULL; + + if (VIR_ALLOC_N(lpar_uuid, VIR_UUID_BUFLEN) < 0) + virReportOOMError(dom->conn); + + if (VIR_ALLOC(def) < 0) + virReportOOMError(dom->conn); + + /* need to shift one byte in order to remove the first "/" of uri component */ + if (managed_system[0] == '/') + managed_system++; + + /* here we are handling only the first component of the path, + * so skipping the second: + * */ + char *char_ptr = strchr(managed_system, '/'); + + if (char_ptr) + *char_ptr = '\0'; + + def->virtType = VIR_DOMAIN_VIRT_PHYP; + def->id = dom->id; + + char *lpar_name = phypGetLparNAME(ssh_session, managed_system, def->id, + dom->conn); + + if (lpar_name == NULL) + VIR_WARN("%s", "Unable to determine domain's name."); + + if (phypGetLparUUID(lpar_uuid, dom->id, dom->conn) == -1) + VIR_WARN("%s", "Unable to generate random uuid."); + + if (!memcpy(def->uuid, lpar_uuid, VIR_UUID_BUFLEN)) + VIR_WARN("%s", "Unable to generate random uuid."); + + if ((def->maxmem = + phypGetLparMem(dom->conn, managed_system, dom->id, 0)) == 0) + VIR_WARN("%s", "Unable to determine domain's max memory."); + + if ((def->memory = + phypGetLparMem(dom->conn, managed_system, dom->id, 1)) == 0) + VIR_WARN("%s", "Unable to determine domain's memory."); + + if ((def->vcpus = + phypGetLparCPU(dom->conn, managed_system, dom->id)) == 0) + VIR_WARN("%s", "Unable to determine domain's CPU.");
I'm thinking some, if not all of these should probably be treated as fatal errors, rather than just warnings.
Regards, Daniel
-- Eduardo Otubo Software Engineer Linux Technology Center IBM Systems & Technology Group Mobile: +55 19 8135 0885 otubo@linux.vnet.ibm.com
participants (2)
-
Daniel P. Berrange
-
Eduardo Otubo