[libvirt] [RFC] Power Hypervisor Libvirt support

Daniel P. Berrange berrange at redhat.com
Wed May 13 15:50:47 UTC 2009


On Fri, May 08, 2009 at 07:10:02PM -0300, Eduardo Otubo wrote:
> 
> Here is the phyp_driver.c with the memory leaks fixed with your
> suggestions. With those things done, do you think this code is enough
> and compliant to libvirt patterns to be included in the next libvirt
> release?

Yep, the virBuffer/virAsprintf usage looks good now.

> The only feature we have until now is just to list the LPARs ("Logical
> PARtitions", the IBM virtual machines for Power). Once this code is safe
> and goot enough, the implementations of new commands will be much faster
> and easier.

I think for a initial commit of the driver I'd like to see it able
to generate an XML description for each guest domain on the system.
And also implement the listing of inactive domains.

So these three extra driver API points:

>     NULL,                       /* domainDumpXML */
>     NULL,                       /* listDefinedDomains */
>     NULL,                       /* numOfDefinedDomains */

This would be enough to make the 2 core virsh commands work 
fully with the drier

  virsh list --all
  virsh dumpxml GUEST


> static virDrvOpenStatus
> phypOpen(virConnectPtr conn,
>          virConnectAuthPtr auth, int flags ATTRIBUTE_UNUSED)
> {
>     int ssh_auth = 0, exit_status = 0;
>     char *banner;
> 
>     SSH_SESSION *session;
>     SSH_OPTIONS *opt;
> 
>     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;
> 
>     session = ssh_new();
>     opt = ssh_options_new();
> 
>     if (!conn->uri->port)
>         conn->uri->port = 22;
> 
>     /*setting some ssh options */
>     ssh_options_set_host(opt, conn->uri->server);
>     ssh_options_set_port(opt, conn->uri->port);
>     ssh_options_set_username(opt, conn->uri->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();
>     }
> 
>     /*trying to use pub key */
>     if ((ssh_auth =
>          ssh_userauth_autopubkey(session, NULL)) == SSH_AUTH_ERROR) {
>         virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP, VIR_ERR_ERROR,
>                       NULL, NULL, NULL, 0, 0, "%s : %s",
>                       _("authenticating with public key ailed"),
>                       ssh_get_error(session));
>     }
>
> 
>     if ((banner = ssh_get_issue_banner(session))) {
>         virRaiseError(conn, NULL, NULL, 0, VIR_FROM_PHYP, VIR_ERR_ERROR,
>                       NULL, NULL, NULL, 0, 0, "%s", banner);
>         VIR_FREE(banner);
>     }

If you call virRaiseError in these 3 cases, then you need to also 
return from this function with VIR_DRV_OPEN_ERROR so caller can 
see it failed.  If these are merely warnings, and you do intend
to continue, then VIR_WARN() (from logging.h is most appropriate)


> 
>     if (ssh_auth != SSH_AUTH_SUCCESS) {
>         int i;
>         int hasPassphrase = 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;
>         char *username = conn->uri->user;
> 
>         if (ssh_userauth_password(session, username, password) !=
>             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);
>             memset(password, 0, strlen(password));
>             memset(username, 0, strlen(username));
>             goto err;
>         } else
>             goto exec;

I imagine you want to blank out the password in both branches
of that if() statement. Blanking the username is redundant
since its permanently stored in the xmlURIPtr 

>     } else
>         goto exec;
> 
>   err:
>     exit_status = SSH_CONN_ERR;
>     return VIR_DRV_OPEN_DECLINED;

You should use OPEN_ERROR in this location. Only use OPEN_DECLINED
if the initial conn->uri was not for the phy:// driver (you already
handle this scenario correctly earlier on in this method)

> 
> /* this functions is the layer that manipulates the ssh channel itself
> * and executes the commands on the remote machine */
> static char *
> __inner_exec_command(SSH_SESSION * session, char *cmd, int *exit_status)

Having an exit_status var here is overkill, since the caller
already checks the return value for NULL and can thus detect
error there.

> {
>     CHANNEL *channel = channel_new(session);
>     char buf[4096] = { 0 };
>     virBuffer tex_ret = VIR_BUFFER_INITIALIZER;
> 
>     int ret = 0;
> 
>     if (channel_open_session(channel) == SSH_ERROR)
>         goto err;
> 
>     if (channel_request_exec(channel, cmd) == SSH_ERROR)
>         goto err;
> 
>     if (channel_send_eof(channel) == SSH_ERROR)
>         goto err;

It is desired to have each of these errors virRaiseError
with the specific details of what went wrong.

> 
>     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));
>     }
> 
>   err:
>     (*exit_status) = SSH_CMD_ERR;
>     return NULL;

Here you need to free the buffer

   char *buf = virBufferContentAndReset(&tex_ret)
   VIR_FREE(buf);

> 
>   exit:
>     return virBufferContentAndReset(&tex_ret);

This also needs a check for OOM, so you can report ther error
message

   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)
> {
>     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)
>         return 0;
> 
>     const char *tex_ret =
>         __inner_exec_command(ssh_session, cmd, &exit_status);
> 
>     VIR_FREE(cmd);
> 
>     if (exit_status < 0 || tex_ret == NULL)
>         return 0;
> 
>     if (virStrToLong_i(tex_ret, &char_ptr, 10, &lpar_id) == -1)
>         return 0;
> 
>     return lpar_id;
> }
> 
> /* 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, int *exit_status)
> {
>     char *cmd;
> 
>     if (virAsprintf(&cmd,
>                     "lssyscfg -r lpar -m %s --filter lpar_ids=%d -F
> name",
>                     managed_system, lpar_id) < 0)
>         return NULL;
> 
>     char *lpar_name =
>         __inner_exec_command(ssh_session, cmd, (int *) exit_status);

This is forgetting to check lpar_name for NULL.

> 
>     char *striped_lpar_name = (char *) malloc(sizeof(lpar_name) - 1);
> 
>     stripNewline(striped_lpar_name, lpar_name);

malloc()/free/calloc/realloc should all be avoided, in favour
of using the VIR_ALLOC/ALLOC_N/REALLOC_N/FREE comamnds instead.

In this case though, the malloc() is overkill - just modify
the original string, rather than reallocating it 1 byte
shorter.  

eg replace those 2 lines with

       char *nl = strchr(lpar_name, '\n');
       if (nl) *nl = '\0';



> 
>     VIR_FREE(cmd);
> 
>     if ((*exit_status) < 0 || lpar_name == NULL)
>         return NULL;
> 
>     return striped_lpar_name;
> }
> 
> /* return the lpar_uuid (which for now is its logical serial number) 
> * given a lpar id and a managed system name */
> static unsigned char *
> phypGetLparUUID(SSH_SESSION * ssh_session, const char *managed_system,
>                 unsigned int lpar_id, int *exit_status)
> {
>     char *cmd;
> 
>     if (virAsprintf(&cmd,
>                     "lssyscfg -r lpar -m %s --filter lpar_ids=%d -F
> logical_serial_num",
>                     managed_system, lpar_id) < 0)
>         return NULL;
>     unsigned char *lpar_uuid =
>         (unsigned char *) __inner_exec_command(ssh_session, cmd,
>                                                (int *) exit_status);
> 
>     unsigned char *striped_lpar_uuid =
>         (unsigned char *) malloc(sizeof(lpar_uuid) - 1);
>     stripNewline((char *) striped_lpar_uuid, (char *) lpar_uuid);

When a UUID is declared 'unsigned char*', this is intended to
be the raw binary 16 byte array. So just casting from a NULL
terminated string is not sufficient. Instead call into the
virUUIDParse() method from src/uuid.h to convert from NULL
terminated string, to raw byte array.

> 
>     VIR_FREE(cmd);
> 
>     if ((*exit_status) < 0 || lpar_uuid == NULL)
>         return NULL;
> 
>     return striped_lpar_uuid;
> }
> 
> static int
> phypNumDomains(virConnectPtr conn)
> {
>     int exit_status = 0;
>     int ndom = 0;
>     char *char_ptr;
>     char *cmd;
>     char managed_system[strlen(conn->uri->path) - 1];
>     SSH_SESSION *ssh_session = conn->privateData;
> 
>     stripPath((char *) &managed_system, conn->uri->path);
>     if (virAsprintf(&cmd,
>                     "lssyscfg -r lpar -m %s -F lpar_id|grep -c ^[0-9]*",
>                     managed_system) < 0) {
>         virReportOOMError(conn);
>         return 0;
>     }
> 
>     char *ret = __inner_exec_command(ssh_session, cmd, &exit_status);
> 
>     VIR_FREE(cmd);
> 
>     if (exit_status < 0 || ret == NULL)
>         return 0;
> 
>     if (virStrToLong_i(ret, &char_ptr, 10, &ndom) == -1)
>         return 0;
> 
>     return ndom;
> }
> 
> static int
> phypListDomains(virConnectPtr conn, int *ids, int nids)
> {
>     int exit_status = 0;
>     int got = 0;
>     char *char_ptr;
>     unsigned int i = 0, j = 0;
>     char id_c[10];
>     char *cmd;
>     char managed_system[strlen(conn->uri->path) - 1];
>     SSH_SESSION *ssh_session = conn->privateData;
> 
>     stripPath((char *) &managed_system, conn->uri->path);
> 
>     memset(id_c, 0, 10);
> 
>     if (virAsprintf(&cmd, "lssyscfg -r lpar -m %s -F lpar_id",
>                     managed_system) < 0) {
>         virReportOOMError(conn);
>         return 0;
>     }
>     char *domains = __inner_exec_command(ssh_session, cmd,
> &exit_status);
> 
>     /* I need to parse the textual return in order to get the domains */
>     if (exit_status < 0 || domains == NULL)
>         return 0;
>     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;
> }
> 
> static virDomainPtr
> phypDomainLookupByName(virConnectPtr conn, const char *name)
> {
>     SSH_SESSION *ssh_session = conn->privateData;
>     virDomainPtr dom = NULL;
> 
>     int lpar_id = 0;
>     int exit_status = 0;
>     char managed_system[strlen(conn->uri->path) - 1];

This is allocating a variable length array on the stack which
is something its best to avoid - prefer to allocate on the
heap instead. 

> 
>     stripPath((char *) &managed_system, conn->uri->path);



> 
>     lpar_id = phypGetLparID(ssh_session, managed_system, name);
>     if (lpar_id == PHYP_NO_MEM)
>         return NULL;
> 
>     unsigned char *lpar_uuid =
>         phypGetLparUUID(ssh_session, managed_system, lpar_id,
>                         &exit_status);
> 
>     if (exit_status == PHYP_NO_MEM || lpar_uuid == NULL)
>         return NULL;
> 
>     dom = virGetDomain(conn, name, lpar_uuid);
> 
>     if (dom)
>         dom->id = lpar_id;
> 
>     VIR_FREE(lpar_uuid);
>     return dom;
> }
> 
> static virDomainPtr
> phypDomainLookupByID(virConnectPtr conn, int lpar_id)
> {
>     SSH_SESSION *ssh_session = conn->privateData;
>     virDomainPtr dom = NULL;
>     int exit_status = 0;
>     char managed_system[strlen(conn->uri->path) - 1];
> 
>     stripPath((char *) &managed_system, conn->uri->path);
> 
>     char *lpar_name = phypGetLparNAME(ssh_session, managed_system,
> lpar_id,
>                                       &exit_status);
> 
>     if (exit_status == PHYP_NO_MEM)
>         return NULL;
> 
>     unsigned char *lpar_uuid =
>         phypGetLparUUID(ssh_session, managed_system, lpar_id,
>                         &exit_status);
> 
>     if (exit_status == PHYP_NO_MEM)
>         return NULL;
> 
>     dom = virGetDomain(conn, lpar_name, lpar_uuid);
> 
>     if (dom)
>         dom->id = lpar_id;
> 
>     VIR_FREE(lpar_name);
>     VIR_FREE(lpar_uuid);
>     return dom;
> }
> 
> static 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 */
>     NULL,                       /* domainResume */
>     NULL,                       /* domainShutdown */
>     NULL,                       /* domainReboot */
>     NULL,                       /* domainDestroy */
>     NULL,                       /* domainGetOSType */
>     NULL,                       /* domainGetMaxMemory */
>     NULL,                       /* domainSetMaxMemory */
>     NULL,                       /* domainSetMemory */
>     NULL,                       /* domainGetInfo */
>     NULL,                       /* domainSave */
>     NULL,                       /* domainRestore */
>     NULL,                       /* domainCoreDump */
>     NULL,                       /* domainSetVcpus */
>     NULL,                       /* domainPinVcpu */
>     NULL,                       /* domainGetVcpus */
>     NULL,                       /* domainGetMaxVcpus */
>     NULL,                       /* domainGetSecurityLabel */
>     NULL,                       /* nodeGetSecurityModel */
>     NULL,                       /* domainDumpXML */
>     NULL,                       /* listDefinedDomains */
>     NULL,                       /* 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;
> }
> 
> /* function that helps me to strip out the first slash from the 
> * uri: phyp://user@hmc/path
> *
> * '/path' becomes 'path'
> * */
> void
> stripPath(char *striped_path, char *path)
> {
>     unsigned int i = 0;
> 
>     for (i = 1; i <= strlen(path); i++)
>         striped_path[i - 1] = path[i];
>     striped_path[i] = '\0';
>     return;
> }


I'm not convinced that the compiler will optimize this loop
to avoid it being  O(n^2) on strlen(path). It can be simplified
by just calling strcpy, or memmove()'ing the original string
inplace.

> /* function to strip out the '\n' of the end of some string */
> void
> stripNewline(char *striped_string, char *string)
> {
>     unsigned int i = 0;
> 
>     for (i = 0; i <= strlen(string); i++)
>         if (string[i] != '\n')
>             striped_string[i] = string[i];
>     striped_string[strlen(string) - 1] = '\0';
>     return;
> }

This can also be done in-place with a simple strchr() call

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




More information about the libvir-list mailing list