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

Eduardo Otubo otubo at linux.vnet.ibm.com
Mon Jun 29 21:05:16 UTC 2009


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 at linux.vnet.ibm.com




More information about the libvir-list mailing list