[Libvir] [PATCH][RFC] libvirt ldoms support

Note: I haven't tried building this on Linux yet, so we may have some #ifdef or Makefile changes on non-Solaris platforms. Please Cc: Eunice.Moon@Sun.COM on any response. Thanks, Ryan LDoms Support This patch adds the Logical Domains (LDoms) support for the SPARC platforms. LDoms software is Sun Microsystem's virtualization technology to subdivide a supported system's resources (CPUs, memory, I/O, and storage) creating partitions called logical domains. The Logical Domains Manager is used to create and manage logical domains and maps logical domains to physical resources. The LDoms Manager provides a command-line interface and also exports an XML-based control interface. The Libvirt for LDoms uses this XML interface to communicate with the LDoms Manager to retrieve the LDoms data for: - Listing domains - Requesting CPU and memory resource updates - Performing life-cycle actions for logical domains This libvirt patch supports LDoms 1.0.1 and 1.0.2. This patch will modify the following existing files: src/libvirt.c src/virsh.c src/virterror.c src/driver.h src/Makefile.am include/libvirt/libvirt.h.in include/libvirt/virterror.h configure.in and add the following new files: src/ldoms_common.h src/ldoms_internal.h src/ldoms_internal.c src/ldoms_intfc.h src/ldoms_intfc.c src/ldoms_xml_parse.h src/ldoms_xml_parse.c Signed-off-by: Eunice Moon <eunice.moon@sun.com> diff --git a/src/libvirt.c b/src/libvirt.c --- a/src/libvirt.c +++ b/src/libvirt.c @@ -48,6 +48,9 @@ #ifdef WITH_LXC #include "lxc_driver.h" #endif +#ifdef WITH_LDOMS +extern int ldomsRegister(void); +#endif /* * TODO: @@ -267,6 +270,11 @@ virInitialize(void) * Note that the order is important: the first ones have a higher * priority when calling virConnectOpen. */ +#ifdef WITH_LDOMS + if (ldomsRegister() == -1) return -1; + /* Don't want to run any other HV with LDoms */ + return (0); +#endif #ifdef WITH_TEST if (testRegister() == -1) return -1; #endif @@ -1794,11 +1802,17 @@ virDomainGetUUID(virDomainPtr domain, un return (-1); } +#ifndef WITH_LDOMS if (domain->id == 0) { memset(uuid, 0, VIR_UUID_BUFLEN); } else { memcpy(uuid, &domain->uuid[0], VIR_UUID_BUFLEN); } +#endif + +#ifdef WITH_LDOMS + memcpy(uuid, &domain->uuid[0], VIR_UUID_BUFLEN); +#endif return (0); } @@ -5025,6 +5039,42 @@ virStorageVolGetPath(virStorageVolPtr vo return NULL; } +#ifdef WITH_LDOMS +/** + * virLDomConsole: + * @domain: the domain if available + * + * Opens a terminal window to the console for a domain + * + * Returns -1 in case of error, LDom console port number in case of success + */ +int +virLDomConsole(virDomainPtr domain) +{ + virConnectPtr conn; + DEBUG("Starting domain %p", domain); + + if (domain == NULL) { + TODO + return (-1); + } + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(domain, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return (-1); + } + conn = domain->conn; + if (conn->flags & VIR_CONNECT_RO) { + virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + return (-1); + } + + if (conn->driver->ldomConsole) + return conn->driver->ldomConsole (domain); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} +#endif /* * vim: set tabstop=4: diff --git a/src/virsh.c b/src/virsh.c --- a/src/virsh.c +++ b/src/virsh.c @@ -494,6 +494,11 @@ cmdConsole(vshControl * ctl, vshCmd * cm virDomainPtr dom; int ret = FALSE; char *doc; +#ifdef WITH_LDOMS + int port; + char command[80]; +#endif + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) return FALSE; @@ -501,6 +506,19 @@ cmdConsole(vshControl * ctl, vshCmd * cm if (!(dom = vshCommandOptDomain(ctl, cmd, "domain", NULL))) return FALSE; +#ifdef WITH_LDOMS + port = virLDomConsole(dom); + if (port > 0) { + sprintf(command, "%s %d &", + "/usr/X/bin/xterm -sb -sl 1000 -e telnet localhost ", port); + system(command); + return TRUE; + } + + vshError(ctl, FALSE, _("Failed to start console")); + return FALSE; +#endif + doc = virDomainGetXMLDesc(dom, 0); if (!doc) goto cleanup; @@ -1003,13 +1021,21 @@ cmdUndefine(vshControl * ctl, vshCmd * c */ static vshCmdInfo info_start[] = { {"syntax", "start <domain>"}, +#ifdef WITH_LDOMS + {"help", gettext_noop("start an inactive or bound domain")}, +#else {"help", gettext_noop("start a (previously defined) inactive domain")}, +#endif {"desc", gettext_noop("Start a domain.")}, {NULL, NULL} }; static vshCmdOptDef opts_start[] = { +#ifdef WITH_LDOMS + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("name of the inactive or bound domain")}, +#else {"name", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("name of the inactive domain")}, +#endif {NULL, 0, 0, NULL} }; @@ -1019,17 +1045,26 @@ cmdStart(vshControl * ctl, vshCmd * cmd) virDomainPtr dom; int ret = TRUE; +#ifdef WITH_LDOMS + /* Need to send in the 'domain' option name instead of 'name' */ + if (!(dom = vshCommandOptDomainBy(ctl, cmd, "domain", NULL, VSH_BYNAME))) + return FALSE; +#else if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) return FALSE; +#endif if (!(dom = vshCommandOptDomainBy(ctl, cmd, "name", NULL, VSH_BYNAME))) return FALSE; + /* Allow LDoms domain state to be inactive or bound */ +#ifndef WITH_LDOMS if (virDomainGetID(dom) != (unsigned int)-1) { vshError(ctl, FALSE, "%s", _("Domain is already active")); virDomainFree(dom); return FALSE; } +#endif if (virDomainCreate(dom) == 0) { vshPrint(ctl, _("Domain %s started\n"), @@ -1660,11 +1695,13 @@ cmdVcpuinfo(vshControl * ctl, vshCmd * c vshPrint(ctl, "%-15s %.1lfs\n", _("CPU time:"), cpuUsed); } +#ifndef WITH_LDOMS vshPrint(ctl, "%-15s ", _("CPU Affinity:")); for (m = 0 ; m < VIR_NODEINFO_MAXCPUS(nodeinfo) ; m++) { vshPrint(ctl, "%c", VIR_CPU_USABLE(cpumap, cpumaplen, n, m) ? 'y' : '-'); } vshPrint(ctl, "\n"); +#endif if (n < (ncpus - 1)) { vshPrint(ctl, "\n"); } @@ -5087,19 +5124,23 @@ cmdQuit(vshControl * ctl, vshCmd * cmd A */ static vshCmdDef commands[] = { {"help", cmdHelp, opts_help, info_help}, +#ifndef WITH_LDOMS {"attach-device", cmdAttachDevice, opts_attach_device, info_attach_device}, {"attach-disk", cmdAttachDisk, opts_attach_disk, info_attach_disk}, {"attach-interface", cmdAttachInterface, opts_attach_interface, info_attach_interface}, {"autostart", cmdAutostart, opts_autostart, info_autostart}, {"capabilities", cmdCapabilities, NULL, info_capabilities}, {"connect", cmdConnect, opts_connect, info_connect}, +#endif /* WITH_LDOMS */ {"console", cmdConsole, opts_console, info_console}, {"create", cmdCreate, opts_create, info_create}, {"start", cmdStart, opts_start, info_start}, {"destroy", cmdDestroy, opts_destroy, info_destroy}, +#ifndef WITH_LDOMS {"detach-device", cmdDetachDevice, opts_detach_device, info_detach_device}, {"detach-disk", cmdDetachDisk, opts_detach_disk, info_detach_disk}, {"detach-interface", cmdDetachInterface, opts_detach_interface, info_detach_interface}, +#endif /* WITH_LDOMS */ {"define", cmdDefine, opts_define, info_define}, {"domid", cmdDomid, opts_domid, info_domid}, {"domuuid", cmdDomuuid, opts_domuuid, info_domuuid}, @@ -5112,7 +5153,9 @@ static vshCmdDef commands[] = { {"freecell", cmdFreecell, opts_freecell, info_freecell}, {"hostname", cmdHostname, NULL, info_hostname}, {"list", cmdList, opts_list, info_list}, +#ifndef WITH_LDOMS {"migrate", cmdMigrate, opts_migrate, info_migrate}, +#endif /* WITH_LDOMS */ {"net-autostart", cmdNetworkAutostart, opts_network_autostart, info_network_autostart}, {"net-create", cmdNetworkCreate, opts_network_create, info_network_create}, @@ -5144,20 +5187,28 @@ static vshCmdDef commands[] = { {"pool-uuid", cmdPoolUuid, opts_pool_uuid, info_pool_uuid}, {"quit", cmdQuit, NULL, info_quit}, +#ifndef WITH_LDOMS {"reboot", cmdReboot, opts_reboot, info_reboot}, {"restore", cmdRestore, opts_restore, info_restore}, {"resume", cmdResume, opts_resume, info_resume}, {"save", cmdSave, opts_save, info_save}, {"schedinfo", cmdSchedinfo, opts_schedinfo, info_schedinfo}, {"dump", cmdDump, opts_dump, info_dump}, +#endif /* WITH_LDOMS */ {"shutdown", cmdShutdown, opts_shutdown, info_shutdown}, {"setmem", cmdSetmem, opts_setmem, info_setmem}, +#ifndef WITH_LDOMS {"setmaxmem", cmdSetmaxmem, opts_setmaxmem, info_setmaxmem}, +#endif /* WITH_LDOMS */ {"setvcpus", cmdSetvcpus, opts_setvcpus, info_setvcpus}, +#ifndef WITH_LDOMS {"suspend", cmdSuspend, opts_suspend, info_suspend}, {"ttyconsole", cmdTTYConsole, opts_ttyconsole, info_ttyconsole}, +#endif /* WITH_LDOMS */ {"undefine", cmdUndefine, opts_undefine, info_undefine}, +#ifndef WITH_LDOMS {"uri", cmdURI, NULL, info_uri}, +#endif /* WITH_LDOMS */ {"vol-create", cmdVolCreate, opts_vol_create, info_vol_create}, {"vol-create-as", cmdVolCreateAs, opts_vol_create_as, info_vol_create_as}, @@ -5170,9 +5221,13 @@ static vshCmdDef commands[] = { {"vol-key", cmdVolKey, opts_vol_key, info_vol_key}, {"vcpuinfo", cmdVcpuinfo, opts_vcpuinfo, info_vcpuinfo}, +#ifndef WITH_LDOMS {"vcpupin", cmdVcpupin, opts_vcpupin, info_vcpupin}, +#endif /* WITH_LDOMS */ {"version", cmdVersion, NULL, info_version}, +#ifndef WITH_LDOMS {"vncdisplay", cmdVNCDisplay, opts_vncdisplay, info_vncdisplay}, +#endif /* WITH_LDOMS */ {NULL, NULL, NULL, NULL} }; @@ -5905,6 +5960,10 @@ vshDomainVcpuStateToString(int state) return gettext_noop("blocked"); case VIR_VCPU_RUNNING: return gettext_noop("running"); +#ifdef WITH_LDOMS + case VIR_VCPU_UNKNOWN: + return gettext_noop("unknown"); +#endif default: ;/*FALLTHROUGH*/ } diff --git a/src/virterror.c b/src/virterror.c --- a/src/virterror.c +++ b/src/virterror.c @@ -304,7 +304,11 @@ virDefaultErrorFunc(virErrorPtr err) case VIR_FROM_STORAGE: dom = "Storage "; break; - +#ifdef WITH_LDOMS + case VIR_FROM_LDOMS: + dom = "LDoms "; + break; +#endif } if ((err->dom != NULL) && (err->code != VIR_ERR_INVALID_DOMAIN)) { domain = err->dom->name; @@ -713,6 +717,14 @@ __virErrorMsg(virErrorNumber error, cons else errmsg = _("Failed to find a storage driver: %s"); break; +#ifdef WITH_LDOMS + case VIR_ERR_INVALID_OPTION: + if (info == NULL) + errmsg = _("invalid option"); + else + errmsg = _("invalid option: %s"); + break; +#endif } return (errmsg); } diff --git a/src/driver.h b/src/driver.h --- a/src/driver.h +++ b/src/driver.h @@ -24,7 +24,10 @@ typedef enum { VIR_DRV_QEMU = 3, VIR_DRV_REMOTE = 4, VIR_DRV_OPENVZ = 5, - VIR_DRV_LXC = 6 + VIR_DRV_LXC = 6, +#ifdef WITH_LDOMS + VIR_DRV_LDOMS = 7 +#endif } virDrvNo; @@ -253,6 +256,11 @@ typedef virDomainPtr const char *uri, unsigned long flags); +#ifdef WITH_LDOMS +typedef int + (*virDrvLDomConsole) (virDomainPtr domain); +#endif + typedef struct _virDriver virDriver; typedef virDriver *virDriverPtr; @@ -337,6 +345,9 @@ struct _virDriver { virDrvDomainInterfaceStats domainInterfaceStats; virDrvNodeGetCellsFreeMemory nodeGetCellsFreeMemory; virDrvNodeGetFreeMemory getFreeMemory; +#ifdef WITH_LDOMS + virDrvLDomConsole ldomConsole; +#endif }; typedef int diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -549,6 +549,9 @@ typedef enum { VIR_VCPU_OFFLINE = 0, /* the virtual CPU is offline */ VIR_VCPU_RUNNING = 1, /* the virtual CPU is running */ VIR_VCPU_BLOCKED = 2, /* the virtual CPU is blocked on resource */ +#ifdef WITH_LDOMS + VIR_VCPU_UNKNOWN = 3, /* the virtual CPU state is unknown */ +#endif } virVcpuState; typedef struct _virVcpuInfo virVcpuInfo; diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -56,6 +56,9 @@ typedef enum { VIR_FROM_STATS_LINUX, /* Error in the Linux Stats code */ VIR_FROM_LXC, /* Error from Linux Container driver */ VIR_FROM_STORAGE, /* Error from storage driver */ +#ifdef WITH_LDOMS + VIR_FROM_LDOMS, /* Error from LDoms driver */ +#endif } virErrorDomain; @@ -139,6 +142,9 @@ typedef enum { VIR_WAR_NO_STORAGE, /* failed to start storage */ VIR_ERR_NO_STORAGE_POOL, /* storage pool not found */ VIR_ERR_NO_STORAGE_VOL, /* storage pool not found */ +#ifdef WITH_LDOMS + VIR_ERR_INVALID_OPTION, /* invalid command line option */ +#endif } virErrorNumber; /** diff --git a/src/Makefile.am b/src/Makefile.am --- a/src/Makefile.am +++ b/src/Makefile.am @@ -89,7 +89,17 @@ else EXTRA_DIST += storage_backend_disk.h storage_backend_disk.c endif - +if WITH_LDOMS +CLIENT_SOURCES += ldoms_common.h \ + ldoms_internal.h ldoms_internal.c \ + ldoms_intfc.h ldoms_intfc.h \ + ldoms_xml_parse.h ldoms_xml_parse.c \ +else +EXTRA_DIST += ldoms_common.h \ + ldoms_internal.h ldoms_internal.c \ + ldoms_intfc.h ldoms_intfc.h \ + ldoms_xml_parse.h ldoms_xml_parse.c \ +endif libvirt_la_SOURCES = $(CLIENT_SOURCES) $(SERVER_SOURCES) diff --git a/configure.in b/configure.in --- a/configure.in +++ b/configure.in @@ -246,6 +246,10 @@ if test "$with_remote" = "yes" ; then LIBVIRT_FEATURES="$LIBVIRT_FEATURES -DWITH_REMOTE" fi +if test "$with_ldoms" = "yes" ; then + LIBVIRT_FEATURES="$LIBVIRT_FEATURES -DWITH_LDOMS" +fi + if test "$with_xen" = "yes" ; then dnl search for the Xen store library AC_SEARCH_LIBS(xs_read, [xenstore], diff --git a/src/ldoms_common.h b/src/ldoms_common.h new file mode 100644 --- /dev/null +++ b/src/ldoms_common.h @@ -0,0 +1,80 @@ +/* + * ldoms_common.h: LDoms common definitions + * + * Copyright 2008 Sun Microsystems, Inc. + * + * See COPYING.LIB for the License of this software + * + */ + +#ifndef __VIR_LDOMS_COMMON_H__ +#define __VIR_LDOMS_COMMON_H__ + +#ifdef WITH_LDOMS + +#ifdef __cplusplus +extern "C" { +#endif + +#define LDOMS_VERSION_NUMBER 1000001 /* 1.0.1 */ +#define NAME_SIZE 256 + +/* LDOM memory unit */ +#define LDOMMEMUNIT_BYTES 1 +#define LDOMMEMUNIT_KILOBYTES 2 +#define LDOMMEMUNIT_MEGABYTES 3 +#define LDOMMEMUNIT_GIGABYTES 4 + +/* LDOM lifecycle actions */ +#define LDOM_START 1 +#define LDOM_STOP 2 +#define LDOM_BIND 3 +#define LDOM_UNBIND 4 +#define LDOM_DELETE 5 + +/* LDOM States */ +/* binding is to bind (attach) configured resource to a logical domain + * unbinding is to release resources bound to configured logical domains + */ +#define LDOM_STATE_ACTIVE 1 +#define LDOM_STATE_STOPPING 2 +#define LDOM_STATE_INACTIVE 3 +#define LDOM_STATE_BINDING 4 +#define LDOM_STATE_UNBINDING 5 +#define LDOM_STATE_BOUND 6 +#define LDOM_STATE_STARTING 7 + +/* resource pool supported */ +#define CPU_RP 1 +#define MEM_RP 2 +#define CRYPTO_RP 3 +#define IOBUS_RP 4 + +/* capacity or reserved resource */ +#define RP_CAPACITY 1 +#define RP_RESERVED 2 + +/* Global vars */ +extern unsigned long ldomsFreeMem; +extern unsigned long ldomsUsedMem; +extern int ldomsFreeCpu; +extern int ldomsUsedCpu; + +/* Structures */ + +struct cpuBindings_s { + unsigned int virt; + int real; + struct cpuBindings_s *next; +}; +typedef struct cpuBindings_s cpuBindings_t; + +/* Debug print */ +extern void dprt(const char *template, ...); + +#ifdef __cplusplus +} +#endif + +#endif /* WITH_LDOMS */ +#endif /* __VIR_LDOMS_COMMON_H__ */ diff --git a/src/ldoms_internal.h b/src/ldoms_internal.h new file mode 100644 --- /dev/null +++ b/src/ldoms_internal.h @@ -0,0 +1,29 @@ +/* + * ldoms_internal.h: internal definitions just used by LDoms driver + * + * Copyright 2008 Sun Microsystems, Inc. + * + * See COPYING.LIB for the License of this software + * + */ + +#ifndef __VIR_LDOMS_INTERNAL_H__ +#define __VIR_LDOMS_INTERNAL_H__ + +#ifdef WITH_LDOMS + +#include <libvirt/virterror.h> + +#ifdef __cplusplus +extern "C" { +#endif + +int ldomsRegister(void); +void ldomsError(virConnectPtr, virDomainPtr, virErrorNumber, const char*, int); + +#ifdef __cplusplus +} +#endif + +#endif /* WITH_LDOMS */ +#endif /* __VIR_LDOMS_INTERNAL_H__ */ diff --git a/src/ldoms_internal.c b/src/ldoms_internal.c new file mode 100644 --- /dev/null +++ b/src/ldoms_internal.c @@ -0,0 +1,2254 @@ +/* + * ldoms_internal.c: access to LDoms hypervisor via LDoms Manager (LDM) + * + * Copyright 2008 Sun Microsystems, Inc. + * + * See COPYING.LIB for the License of this software + * + */ + +#ifdef WITH_LDOMS +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/uio.h> +#include <sys/processor.h> +#include <pwd.h> +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> +#include <libxml/uri.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> +#include <sys/wait.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <values.h> +#include <limits.h> +#include <pthread.h> + +#include "buf.h" +#include "internal.h" +#include "xml.h" +#include "ldoms_common.h" +#include "ldoms_internal.h" +#include "ldoms_intfc.h" +#include "ldoms_xml_parse.h" + +/* Local function prototypes */ +static void refresh_ldom_data(); +static int ldomsNodeGetInfo(virConnectPtr , virNodeInfoPtr ); +static long long getCpuUpTime(); +static long getHypervisorVersion(); +static unsigned long getLDMVersion(); + +/* Domain state info + * LDom State enumerations + * 1 = active LDOM_STATE_ACTIVE + * 2 = stopping LDOM_STATE_STOPPING + * 3 = inactive LDOM_STATE_INACTIVE + * 4 = binding LDOM_STATE_BINDING + * 5 = unbinding LDOM_STATE_UNBINDING + * 6 = bound LDOM_STATE_BOUND + * 7 = starting LDOM_STATE_STARTING + * + * libvirt LDom State enums + * typedef enum { + * VIR_DOMAIN_NOSTATE = 0, no state + * VIR_DOMAIN_RUNNING = 1, the domain is running + * VIR_DOMAIN_BLOCKED = 2, the domain is blocked on resource + * VIR_DOMAIN_PAUSED = 3, the domain is paused by user + * VIR_DOMAIN_SHUTDOWN= 4, the domain is being shut down + * VIR_DOMAIN_SHUTOFF = 5, the domain is shut off + * VIR_DOMAIN_CRASHED = 6 the domain is crashed + * } virDomainState; + */ + +ldominfo_t **ldominfo_list = NULL; +int ldom_cnt = 0; +const unsigned char Uuid[] = "" ; +static long last_ldom_refresh = 0; +static pthread_rwlock_t update_lock; +static unsigned long LDMVersion = 1000001; + +/* Mem and CPU Free/Used */ +unsigned long ldomsFreeMem; +unsigned long ldomsUsedMem; +int ldomsFreeCpu; +int ldomsUsedCpu; + +/* Global vars for debug statement */ +int ldoms_debug = 0; +int ldoms_detailed_debug = 0; + +/* Prototype Structures */ +struct domains { + short valid; /* Flag to indicate this Domain is valid */ + short active; /* Flag to indicate this Domain is valid */ + char name[30]; /* Domain Name; primary, ldg1, etc */ + int id; /* ID for the Domain; Used alot by other code to find a Domain */ + virDomainInfo info; /* Domain name, state */ + unsigned char uuid[VIR_UUID_BUFLEN]; /* 32 char unique ID */ +}; + +/* This is global. Need to fill on first call from virsh or VMM */ +virNodeInfo nodeInfo; +short nodeInfoFilled = 0; + +int maxDomID = 0; + +/* + * General error logging function. This is the exact same + * as used by Qemu and Test. + */ +void +ldomsError(virConnectPtr con, + virDomainPtr dom, + virErrorNumber error, + const char *info, + int level) +{ + const char *errmsg; + + if (error == VIR_ERR_OK) { + errmsg = info; + /* return; */ + } else + errmsg = __virErrorMsg(error, info); + + __virRaiseError(con, dom, NULL, VIR_FROM_LDOMS, error, level, + errmsg, info, NULL, 0, 0, errmsg, info, 0); +} + +/* + * getDomainIndex + * + * What: Get the index for the input Domain for a given connection. + * Note: libivrt does not associated an Index with an inactive + * Domain. For LDoms, we assign an Index to all Domains. + * + * Input: domain - A ptr to a virDomain + * Output: The Domain ID. -1 if for no ID. + */ +static int +getDomainIndex(virDomainPtr domain) +{ + int i, domID; + + /* get the current ldom data from LDM (LDoms Manager) */ + (void) pthread_rwlock_wrlock(&update_lock); + refresh_ldom_data(); + (void) pthread_rwlock_unlock(&update_lock); + + if (ldoms_debug) dprt("LDOMS_DEBUG: getDomainIndex(ENTER) domName=%s, domID=%d, ldom_cnt=%d\n",domain->name,domain->id,ldom_cnt); + + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + ldomsError((domain ? domain->conn : NULL), domain, VIR_ERR_INVALID_ARG, + __FUNCTION__, VIR_ERR_ERROR); + if (ldoms_debug) dprt("LDOMS_DEBUG: getDomainIndex(EXIT ERROR) domain input not valid\n"); + return (-1); + } + + domID = domain->id; + for (i = 0 ; i < ldom_cnt ; i++) { + if (domID >= 0) { + if (domID == i) { + if (ldoms_debug) dprt("LDOMS_DEBUG: getDomainIndex(EXIT) From ID: domidx=%d\n",i); + return (i); + } + } else { + if (!strcmp(domain->name, ldominfo_list[i]->ldomName)) { + if (ldoms_debug) dprt("LDOMS_DEBUG: getDomainIndex(EXIT) From Name:domidx=%d\n",i); + return (i); + } + } + } + + if (ldoms_debug) dprt("LDOMS_DEBUG: getDomainIndex(EXIT ERROR) domID=-1\n"); + return (-1); +} /* getDomainIndex() */ + +/* + * ldomsOpen + * + * Open a connection to the LDoms Manager and handshake to + * verify the a connection can be made for future XML requests. + * + * Input: All inputs are ignored at this time. + * Output: 0 -> Success, <0 -> Failure + */ +static virDrvOpenStatus +ldomsOpen(virConnectPtr conn, xmlURIPtr uri, virConnectAuthPtr auth ATTRIBUTE_UNUSED, int flags) +{ + struct timeval tv; + int u, ret, connid; + int socketFD; + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsOpen(ENTER) \n"); + + if (!uri) + return VIR_DRV_OPEN_DECLINED; + + if (!uri->scheme || strcmp(uri->scheme, "ldoms") != 0) + return VIR_DRV_OPEN_DECLINED; + + if (!uri->scheme || strcmp(uri->scheme, "ldoms")) { + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsOpen(FAIL): uri scheme NOT correct\n"); + ldomsError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, _("URI is not valid for LDoms"), VIR_ERR_ERROR); + return (-1); + } + + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsOpen(): scheme=%s, path=%s\n", + (uri->scheme==0) ? "NULL" : uri->scheme, + (uri->path==0) ? "NULL" : uri->path); + + /* Verify the LDM (LDoms Manager) can be talked to. Open an socket + connection, Handshake, then close the socket. */ + if ((socketFD = open_ldm_connection()) < 0) { + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsOpen(): Cannot talk with LDoms Manager\n"); + ldomsError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, _("Cannot open socket to LDMD"), VIR_ERR_ERROR); + return(-1); + } + close_ldm_connection(socketFD); + + + return(0); +} /* ldomsOpen() */ + +static int +ldomsClose(virConnectPtr conn) +{ + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsClose(ENTER/EXIT)\n"); + /* Nothing to do */ + return(0); +} + +/* + * ldomsGetMaxVcpus + * + * What: Just get the total number of free CPUs in the system. + * Call get_ldom_total_cpu(), so it will set, as a side effect, + * the number of free CPUs and free Memory in the system. + * + * Input: domain - A pointer to the virDomain structure + * memory - Size in KiloBytes + * Output: function return - Number of Max CPUs supported, or -1 for error + */ +int +ldomsGetMaxVcpus(virConnectPtr conn, const char *type) +{ + virNodeInfo nodeInfo; + int rc; + int totalVcpus; + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsGetMaxVcpus(ENTER) \n"); + + if (get_ldom_total_cpu(&totalVcpus) < 0) { + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsGetMaxVcpus() get_ldom_total_cpu() failed\n"); + return (0); + } + + /* Success */ + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsGetMaxVcpus() Free Vcpus = %d\n",ldomsFreeCpu); + return (ldomsFreeCpu); +} /* ldomsGetMaxVcpus() */ + +/* + * ldomsNodeGetInfo + * + * What: Get the info for a Node, which is the physical + * hardware system. + * + * Input: conn - Pointer to a virConnect struct (Not used) + * info - Poiner to a virNodeInfo struct to update + * Output: Indirect virNodeInfo struct updated with Node info + */ +static int +ldomsNodeGetInfo(virConnectPtr conn, virNodeInfoPtr info) +{ + int total_cpu; + unsigned long total_memory; + + /* to get the CPU frequency and fill the nodeInfo.mhz field + * + * NOTE: + * Since processor_info provides info for the domain on which it + * is being called (control domain), the virtual processor id 0 is used + * to retrieve the processor speed assuming that the cpu speed + * is the same for all Cpus on the system. That is a safe + * assumption for single-chip platforms like Ontario and Huron, + * but it might not work for multi-chip platforms based on VF or ROCK + * chips. + */ + processor_info_t cpu_info; + int pid = 0; /* physical processor ID */ + int p_clock = 1600; /* default processor clock speed in MHz */ + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsNodeGetInfo(ENTER)\n"); + + /* + * NOTE: We get the Node info values (number of CPUs and memory) + * from the LDM (LDoms Manager) by adding up the total number of CPUs and Memory for all domains. + */ + + /* if the nodeInfo variable hasn't been filled yet, try to get the + * total number of CPUs and memory from the LDM (LDoms Manager). + */ + + + if (nodeInfoFilled == 0) { + + strcpy(nodeInfo.model, "SPARC"); + + /* + * Fill the total amount of memory by sending the LDM list-devices + * and list-bindings XML requests and adding up free and bound + * memory amounts for all domains. If it fails to retrieve the total + * amount of memory from LDM, 64GB (expressed in KB) will be + * used as default value. + */ + + if (get_ldom_total_memory(&total_memory) < 0) { + if (ldoms_debug) + dprt("LDOMS_DEBUG: ldomsNodeGetInfo() get_ldom_total_memory() failed\n"); + nodeInfo.memory = 0; + } else { + if (ldoms_detailed_debug) + dprt("LDOMS_DETAILED_DEBUG: ldomsNodeGetInfo() Total Node Memory=%lu\n", total_memory); + nodeInfo.memory = total_memory; + } + + /* + * Fill the total number of CPUs by sending the LDM list-devices + * and list-bindings XML requests and adding up free and bound + * CPUs for all domains. If it fails to retrieve the total + * number of CPUs from LDM, 0 will be used as default value. + */ + + if (get_ldom_total_cpu(&total_cpu) < 0) { + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsNodeGetInfo() get_ldom_total_cpu() failed\n"); + nodeInfo.cpus = 0; + } else { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsNodeGetInfo() Total Node CPUs=%d\n", total_cpu); + nodeInfo.cpus = total_cpu; + } + + /* get the processor clock speed in MHz */ + while ((pid < 64) && (processor_info(pid++, &cpu_info) != 0) ); + + /* Found a pid on the primary domain */ + if (pid <= 64) { + if (ldoms_detailed_debug) + dprt("LDOMS_DETAILED_DEBUG: ldomsNodeGetInfo(). processor_info with pid=%d clock=%d\n", + pid, cpu_info.pi_clock); + p_clock = cpu_info.pi_clock; + } + + nodeInfo.mhz = p_clock; + nodeInfo.nodes = 1; /* Only 1 node in LDoms */ + nodeInfo.sockets = 1; + nodeInfo.cores = 8; /* 8 cores on N1 and N2. 4 threads on N1 core, 8 on N2 */ + + /* If there are more than 32 CPUs, then 8 Threads/Core, else 4 Threads/Core */ + if (nodeInfo.cpus > 32) + nodeInfo.threads = 8; /* This is Threads per Core, not total Threads */ + else + nodeInfo.threads = 4; /* This is Threads per Core, not total Threads */ + + /* nodeInfo has been filled */ + nodeInfoFilled = 1; + } + + memcpy(info, &nodeInfo, sizeof(virNodeInfo)); + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsNodeGetInfo(EXIT)\n"); + return (0); +} /* ldomsNodeGetInfo() */ + +/* Don't really know what to return for this */ +static const char * +ldomsGetType(virConnectPtr conn) +{ + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsGetType(ENTER)\n"); + return(strdup("LDoms")); +} + +/* This returns the HV version. Make it the same as the LDoms version */ +static int +ldomsGetVersion(virConnectPtr conn, unsigned long *hvVer) +{ + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsGetVersion(ENTER/EXIT)\n"); + *hvVer = getHypervisorVersion(); + return(0); +} + +static char * +ldomsGetHostname(virConnectPtr conn) +{ + + int rc; + char hostname [MAXHOSTNAMELEN+1]; + char *host; + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsGetHostname(ENTER)\n"); + + rc = gethostname (hostname, MAXHOSTNAMELEN); + if (rc == -1) { + ldomsError (conn, NULL, VIR_ERR_SYSTEM_ERROR, strerror (errno), VIR_ERR_ERROR); + return NULL; + } + + host = strdup (hostname); + if (host == NULL) { + ldomsError (conn, NULL, VIR_ERR_SYSTEM_ERROR, strerror (errno), VIR_ERR_ERROR); + return NULL; + } + return host; +} + +/* + * ldomsListDomains + * + * What: Return the array of Domain ID's for all the active Domains. + * Send an XML request to LDM (LDoms Manager) for a list of all Domains. + * + * Yes, this functions does pretty much the same as ldomsNumOfDomains() with + * the addition of returning the ID numbers for the valid Domains. + * + * Input: conn - The Connection structure + * maxids - The total number of Domains to look at to + * determine what Domain IDs to return. + * Output: ids - An array of integers to hold the IDs of the + * Domains whose state is other than 'inactive' - + * VIR_DOMAIN_SHUTOFF + */ +static int +ldomsListDomains(virConnectPtr conn, int *ids, int maxids) +{ + int i,n; + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsListDomains(ENTER), Max # of non inactive domains=%d\n",maxids); + + /* Send an LDM request 'list-domains' */ + if (get_ldom_names(&ldom_cnt, &ldominfo_list) < 0) { + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsListDomains() get_ldom_names() failed\n"); + return (-1); + }; + + /* The Domain ID will be the index into the ldominfo_list array */ + for (i=0, n=0; i<ldom_cnt && n<maxids; i++) { + if (ldominfo_list[i]->ldomState != LDOM_STATE_INACTIVE) { + ids[n++] = i; + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsListDomains(), Active Domain ID=%d\n",i); + } + } + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsListDomains(EXIT), # of non inactive domains=%d\n",n); + return(n); +} /* ldomsListDomains() */ + +/* + * ldomsNumOfDomains + * + * What: Return the total number of active Domains (not VIR_SHUT_OFF) + * + * Input: conn - Pointer to a connection structure + * Output: function return - # of active Domains + */ +static int +ldomsNumOfDomains(virConnectPtr conn) +{ + int rc, i, numActive=0; + /* int ldom_cnt; */ + /* ldominfo_t **ldominfo_list = NULL; */ + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsNumOfDomains(ENTER) \n"); + /* + * Call LDM with a list-domains cmd to get the total + * # of Domains and the basic information of each one. + */ + + if (get_ldom_names(&ldom_cnt, &ldominfo_list) < 0) { + /* Any ldominfo_t memory was freed in get_ldom_names() */ + free(ldominfo_list); + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsNumOfDomains() get_ldom_names() failed\n"); + return (0); + }; + + /* Get the number of non inactive domains */ + for (i=0; i<ldom_cnt; i++) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsNumOfDomains() name=%s\n",ldominfo_list[i]->ldomName); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsNumOfDomains() state=%d\n",ldominfo_list[i]->ldomState); + if (ldominfo_list[i]->ldomState != LDOM_STATE_INACTIVE) numActive++; + } + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsNumOfDomains(EXIT) non inactive ldom_cnt=%d\n",numActive); + return (numActive); + +} /* ldomsNumOfDomains() */ + +/* + * ldomsDomainCreateXML + * + * What: Create a domain from an XML file so that it is left in the + * inactive state. Just call the DefineXML function. The XML input + * has to be a complete and valid XML requeset for the LDM. No + * modification is done to this file. + * + * Input: xml - The xml file + * Output: function return - A ptr to a virDomain or NULL + */ +virDomainPtr +ldomsDomainCreateXML(virConnectPtr conn, const char *xml, unsigned int flags) +{ + virDomainPtr domPtr = NULL; + char *domainName = NULL; + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainCreateXML(ENTER) \n"); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainCreateXML(ENTER) xmldoc=\n%s\n",xml); + + /* Send the XML file along with the lifecycle action */ + domainName = send_ldom_create_domain((char *)xml, XML_ADD_DOMAIN); + + /* + * If the create/bind domain was successful, then we have to create a DomainInfo + * structure to pass back to the caller. We need the Domain name that was + * created, and the only way to get that is from parsing the input xml + * document. This is done in send_ldom_create_domain() and passed back as the return. + * Also we create the uuid for the domain name. + */ + if (domainName != NULL) { + + /* + * The UUID is not important, cuz only the Domain name is printed + * out in the virsh. So just use the default UUID. + */ + domPtr = virGetDomain(conn, domainName, Uuid); + free(domainName); + } + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainCreateXML(EXIT) \n"); + + return (domPtr); +} /* ldomsDomainCreateXML() */ + + +/* + * ldomsDomainLookupByID + * + * What: Given a Domain ID, either malloc or return the existing + * virDomain structure for the Domain associated with the ID. + * + * Input: conn - A connection structure + * id - ID of the Domain you want to find + * Output: Pointer to a virDomain of the Domain identified by the ID + */ +static virDomainPtr +ldomsDomainLookupByID(virConnectPtr conn, int id) +{ + + virDomainPtr domPtr; + int i, idx=-1; + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainLookupByID(ENTER) id=%d\n",id); + + /* get the current ldom data from LDM */ + (void) pthread_rwlock_wrlock(&update_lock); + refresh_ldom_data(); + (void) pthread_rwlock_unlock(&update_lock); + + /* Find the Domain that matches the ID. The index of the ldominfo_list + * array is the Domain ID */ + for (i=0; i<ldom_cnt; i++) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainLookupByID() id=%d, Dom id=%d\n",id,i); + if (i == id) { + idx = i; + break; + } + } + + /* + * If Domain is inactive, then libvirt does not assocaite an ID with an + * inactive domain. So check the LDoms domain state and if inactive, log + * and return an error. + */ + if ((idx < 0) || (ldominfo_list[idx]->ldomState == LDOM_STATE_INACTIVE)) { + ldomsError(conn, NULL, VIR_ERR_NO_DOMAIN, _("no ID match"), VIR_ERR_ERROR); + return(NULL); + } + + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainLookupByID() found id=%d, Dom id=%d\n",id,idx); + + domPtr = virGetDomain(conn, ldominfo_list[id]->ldomName, ldominfo_list[id]->uuid); + if (domPtr == NULL) { + ldomsError(conn, NULL, VIR_ERR_NO_MEMORY, _("allocating domain"), VIR_ERR_ERROR); + return(NULL); + } + + domPtr->id = id; + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainLookupByID(EXIT) \n"); + return (domPtr); +} /* ldomsDomainLookupByID() */ + +/* + * ldomsDomainLookupByUUID + * + * What: Given a Domain UUID, either malloc or return the existing + * virDomain structure for the Domain associated with the UUID. + * + * Input: conn - A connection structure + * id - ID of the Domain you want to find + * Output: Pointer to a virDomain of the Domain identified by the UUID + */ +static virDomainPtr +ldomsDomainLookupByUUID(virConnectPtr conn, const unsigned char *uuid) +{ + + virDomainPtr domPtr; + int i, idx=-1; + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainLookupByUUID(ENTER) \n"); + + /* get the current ldom data from LDM */ + (void) pthread_rwlock_wrlock(&update_lock); + refresh_ldom_data(); + (void) pthread_rwlock_unlock(&update_lock); + + + /* Find the Domain that matches the UUID. */ + for (i=0; i<ldom_cnt; i++) { + if (memcmp(uuid, ldominfo_list[i]->uuid, VIR_UUID_BUFLEN) == 0) { + idx = i; + break; + } + } + + if (idx < 0) { + ldomsError(conn, NULL, VIR_ERR_NO_DOMAIN, _("no UUID match"), VIR_ERR_ERROR); + return(NULL); + } + + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainLookupByUUID() found id=%d\n",idx); + + domPtr = virGetDomain(conn, ldominfo_list[idx]->ldomName, ldominfo_list[idx]->uuid); + if (domPtr == NULL) { + ldomsError(conn, NULL, VIR_ERR_NO_MEMORY, _("allocating domain"), VIR_ERR_ERROR); + return(NULL); + } + + /* If the Domain state is inactive, then the ID must be set to -1 */ + if (ldominfo_list[idx]->ldomState == LDOM_STATE_INACTIVE) { + idx = -1; + } + domPtr->id = idx; + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainLookupByUUID(EXIT) \n"); + return (domPtr); +} /* ldomsDomainLookupByID() */ + +/* + * ldomsDomainLookupByName + * + * What: Given a Domain Name, either malloc or return the existing + * virDomain structure for the Domain associated with the name. + * The virDomain will contain the ID and UUID for the domain. + * If a Domain is in the inactive state, then we don't set a + * ID for it. libvirt considers inactive Domains to have an + * ID of -1 until they are moved out of the inactive state. + * + * + * Input: conn - A connection structure + * name - Pointer to the Domain name + * Output: Pointer to a virDomain of the Domain identified by the name + */ +virDomainPtr +ldomsDomainLookupByName(virConnectPtr conn, const char *name) +{ + virDomainPtr domPtr = NULL; + int i, uuidIdx, idx = -99; + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainLookupByName(ENTER) name=%s\n",name); + + /* get the current ldom data from LDM */ + (void) pthread_rwlock_wrlock(&update_lock); + refresh_ldom_data(); + (void) pthread_rwlock_unlock(&update_lock); + + + /* Find the index of our internal ldominfo_list[] that has the Domain data */ + for (i=0; i<ldom_cnt; i++) { + if (strcmp(ldominfo_list[i]->ldomName,name) == 0) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainLookupByName(), Found Domain: ID=%d\n",i); + + /* If the Domain is inactive, then set the index to -1 */ + if (ldominfo_list[i]->ldomState == LDOM_STATE_INACTIVE) { + idx = -1; + uuidIdx = i; + } + else + idx = i; + break; + } + } + + + /* A non-inactive domain was found */ + if (idx >= 0) { + domPtr = virGetDomain(conn, name, ldominfo_list[idx]->uuid); + if (domPtr == NULL) { + ldomsError(conn, NULL, VIR_ERR_NO_MEMORY, _("allocating domain"), VIR_ERR_ERROR); + return(NULL); + } + domPtr->id = idx; + } + + /* An inactive domain was found, or worst case, LDM does not know + * about the input domain name!! This latter case should not happen + */ + else if (idx == -1) { + domPtr = virGetDomain(conn, name, ldominfo_list[uuidIdx]->uuid); + if (domPtr == NULL) { + ldomsError(conn, NULL, VIR_ERR_NO_MEMORY, _("allocating inactive domain"), VIR_ERR_ERROR); + return(NULL); + } + domPtr->id = idx; + } + + /* No domain name match was made */ + else { + ldomsError(conn, NULL, VIR_ERR_NO_DOMAIN, _("no domain name match"), VIR_ERR_ERROR); + } + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainLookupByName(EXIT) ID=%d\n",idx); + return (domPtr); +} /* ldomsDomainLookupByName() */ + +/* + * ldomsDomainSuspend + * + * What: Given a Domain pointer, suspend/stop/pause the domain. + * These 3 actions are equivalent: suspend/stop/pause. + * + * Input: domain - A pointer to the virDomain structure + * Output: function return - >=0 is success + */ +int +ldomsDomainSuspend(virDomainPtr domain) +{ + int rc; + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainSuspend(ENTER) name=%s\n",domain->name); + + /* get the current ldom data from LDM */ + (void) pthread_rwlock_wrlock(&update_lock); + refresh_ldom_data(); + (void) pthread_rwlock_unlock(&update_lock); + + + /* + * If the domain state is not active (1), then we can't perform + * this action. + */ + + if (ldominfo_list[domain->id]->ldomState != LDOM_STATE_ACTIVE) { + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainSuspend() Domain state not 'active', aborting request. domain=%s, state=%d\n",domain->name, ldominfo_list[domain->id]->ldomState); + return (-1); + } + + /* Request LDM to change the state of the LDom */ + rc = send_ldom_lifecycle_action(domain->name, LDOM_STOP); + + /* + * If successful, the domain state changed from active to bound, + * so update the status. + */ + if (rc == 0) { + ldominfo_list[domain->id]->ldomState = LDOM_STATE_BOUND; + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainSuspend() Domain state changed to 'bound'. domain=%s\n", + domain->name); + return (0); + } + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainSuspend(EXIT) Domain state change failed. domain=%s\n",domain->name); + return (-1); +} /* ldomsDomainSuspend() */ + +/* + * ldomsDomainResume + * + * What: Given a Domain pointer, resume/start the domain. + * These 2 states are equivalent: resume/start. + * + * Input: domain - A pointer to the virDomain structure + * Output: function return - >=0 is success + */ +int +ldomsDomainResume(virDomainPtr domain) +{ + int rc; + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainResume(ENTER) name=%s\n",domain->name); + + /* get the current ldom data from LDM */ + (void) pthread_rwlock_wrlock(&update_lock); + refresh_ldom_data(); + (void) pthread_rwlock_unlock(&update_lock); + + + /* + * If the domain state is not bound (6), then we can't perform + * this action. + */ + + if (ldominfo_list[domain->id]->ldomState != LDOM_STATE_BOUND) { + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainResume() Domain state not 'bound', aborting request. domain=%s, state=%d\n",domain->name, ldominfo_list[domain->id]->ldomState); + return (0); + } + + /* Request LDM to change the state of the LDom */ + rc = send_ldom_lifecycle_action(domain->name, LDOM_START); + + /* + * If successful, the domain state changed from bound to active, + * so update the status. + */ + if (rc == 0) { + ldominfo_list[domain->id]->ldomState = LDOM_STATE_ACTIVE; + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainResume() Domain state changed to 'active'. domain=%s\n", + domain->name); + return (0); + } + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainResume(EXIT) Domain state change failed. domain=%s\n",domain->name); + return (-1); +} /* ldomsDomainResume() */ + +/* + * ldomsDomainShutdown + * + * What: Given a Domain pointer, shutdown/bind the domain. + * The domain can only be in the active state to begin, + * and will be put into the bound state. + * + * Input: domain - A pointer to the virDomain structure + * Output: function return - >=0 is success + */ +int +ldomsDomainShutdown(virDomainPtr domain) +{ + int rc = -1; + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainShutdown(ENTER) domName=%s, domID=%d\n", + domain->name,domain->id); + + /* get the current ldom data from LDM */ + (void) pthread_rwlock_wrlock(&update_lock); + refresh_ldom_data(); + (void) pthread_rwlock_unlock(&update_lock); + + + /* If the domain is inactive, then log an error and return */ + if (domain->id == -1) { + ldomsError(NULL, domain, VIR_ERR_INVALID_OPTION, _("Domain is inactive"), VIR_ERR_WARNING ); + return (-1); + } + + /* If the domain is already bound, then log an error and return */ + if (ldominfo_list[domain->id]->ldomState == LDOM_STATE_BOUND) { + ldomsError(NULL, domain, VIR_ERR_INVALID_OPTION, _("Domain is already bound"), VIR_ERR_WARNING ); + return (-1); + } + + + /* If the domain state is active, then stop the domain */ + if (ldominfo_list[domain->id]->ldomState == LDOM_STATE_ACTIVE) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainShutdown() Domain state is 'active', stopping...\n"); + rc = send_ldom_lifecycle_action(domain->name, LDOM_STOP); + } + + /* + * If successful, the domain state changed from active to bound, + * so update the status. + */ + if (rc == 0) { + ldominfo_list[domain->id]->ldomState = LDOM_STATE_BOUND; + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainShutdown() Domain state changed to 'bound'. domain=%s\n", + domain->name); + return (0); + } + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainShutdown(EXIT) Domain state change failed. domain=%s\n",domain->name); + + return (-1); +} /* ldomsDomainShutdown() */ + + +/* + * ldomsDomainDestroy + * + * What: Given a Domain pointer, destroy/unbind the domain. + * The domain can be in the active or bound state to begin + * and will be put into the inactive state. + * + * Input: domain - A pointer to the virDomain structure + * Output: function return - >=0 is success + */ +int +ldomsDomainDestroy(virDomainPtr domain) +{ + int rc = -1; + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainDestroy(ENTER) domName=%s, domID=%d\n", + domain->name,domain->id); + + /* If the domain is already inactive, then log an error and return */ + if (domain->id == -1) { + ldomsError(NULL, domain, VIR_ERR_INVALID_OPTION, _("Domain is already inactive"), VIR_ERR_WARNING ); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainDestroy() Domain is already inactive.\n"); + return (-1); + } + + /* get the current ldom data from LDM */ + (void) pthread_rwlock_wrlock(&update_lock); + refresh_ldom_data(); + (void) pthread_rwlock_unlock(&update_lock); + + + /* If the domain state is active, then stop the domain */ + if (ldominfo_list[domain->id]->ldomState == LDOM_STATE_ACTIVE) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainDestroy() Domain state is 'active', stopping...\n"); + rc = send_ldom_lifecycle_action(domain->name, LDOM_STOP); + + /* Need to tell the next piece of code that a 'stop' was done and + * was successful, because the ldom info has not been updated. + */ + if (rc == 0) rc = 1; + } + + /* If the domain state is bound (6), then unbind the domain */ + if ((rc == 1) || (ldominfo_list[domain->id]->ldomState == LDOM_STATE_BOUND)) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainDestroy() Domain state is 'bound' rc=%d, unbinding...\n",rc); + rc = send_ldom_lifecycle_action(domain->name, LDOM_UNBIND); + } + + /* + * If successful, the domain state changed to inactive, + * so update the status. + */ + if (rc == 0) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainDestroy() Domain state changed to 'inactive'. domain=%s\n", + domain->name); + return (0); + } + + /* + * In case the input domain info is not correct and the domain is really + * inactive, log a warning. + */ + if (rc == -1) { + ldomsError(NULL, domain, VIR_ERR_INVALID_OPTION, _("Domain is already inactive"), VIR_ERR_WARNING ); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainDestroy() Domain state/ID not indicating inactive, ID=%d\n", domain->id); + } + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainDestroy(EXIT) Domain state change failed. domain=%s\n",domain->name); + + return (-1); +} /* ldomsDomainDestroy() */ + +/* + * ldomsDomainGetOSType + * + * What: The the OS type for an LDoms domain + * + * Input: domain - A pointer to the virDomain structure * NOT USED * + * Output: String representing the OS type + */ +char * +ldomsDomainGetOSType(virDomainPtr domain) +{ + return strdup("Solaris"); +} /* ldomsDomainGetOSType() */ + + +/* + * ldomsDomainGetMaxMemory + * + * What: + * For LDoms, GetMaxMemory will just get the current memory + * allocated to the Domain. There is no concept of Max memory + * for a Domain in LDoms. + * + * Input: domain - A pointer to the virDomain structure + * Output: function return - The amount of memory in Kilobytes + * + */ +unsigned long +ldomsDomainGetMaxMemory(virDomainPtr domain) +{ + int domidx; + unsigned long memory; + + if ((domidx = getDomainIndex(domain)) < 0) { + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainGetMaxMemory(EXIT) getDomainIndex() failed\n"); + return (-1); + } + memory = ldominfo_list[domidx]->ldomMemSize; + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainGetMaxMemory() MaxMemory=%dKB\n",memory); + return (memory); +} /* ldomsDomainGetMaxMemory() */ + +/* + * ldomsDomainSetMemory + * + * What: Given a Domain pointer, send an XML request to change + * the amount of memory for the domain. + * + * Input: domain - A pointer to the virDomain structure + * memory - Size in KiloBytes + * Output: function return - >=0 is success + */ +int +ldomsDomainSetMemory(virDomainPtr domain, unsigned long memory) +{ + int rc; + int domidx; + virDomainInfo domInfo; + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainSetMemory(ENTER) memory=%d\n",memory); + + if ((domidx = getDomainIndex(domain)) < 0) { + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainSetMemory(EXIT) getDomainIndex() failed\n"); + return (-1); + } + + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainSetMemory: domain=%s, domidx=%d\n",domain->name,domidx); + + /* Send request to LDM to set the memory */ + rc = send_ldom_set_memory(domain->name, memory); + + if (rc == 0) { + ldominfo_list[domidx]->ldomMemSize = memory; + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainSetMemory() memory changed to %dKB\n",memory); + } + else { + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainSetMemory(EXIT) memory change failed\n"); + return (-1); + } + + /* Success */ + return (0); +} /* ldomsDomainSetMemory() */ + +/* + * ldomsDomainSetMaxMemory + * + * What: + * For LDoms, you cannot set the Maximum memory that a domain can use. + * If a domain asks for X memory and it is available, then the domain + * will get it when it is bound. Max memory is already calculated for + * all domains, just to be able to work with virsh, so this function will + * merely return Success to appease virsh. + * + * Input: domain - A pointer to the virDomain structure + * memory - Size in KiloBytes + * Output: function return - 0 - success + * + */ +int +ldomsDomainSetMaxMemory(virDomainPtr domain, unsigned long memory) +{ + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainSetMaxMemory(ENTER/EXIT) memory=%d\n",memory); + return (0); +} /* ldomsDomainSetMaxMemory() */ + +/* + * ldomsDomainGetInfo + * + * What: Given a virDomain, get the info for that Domain and return it. + * Note: The info.maxMem variable is set to the maximum memory allowed. + * We try to get the maximum memory allowed by LDM, by calling + * list-devices to get the free memory and by + * adding up the total used memory from all domains. + * If this fails, + * the info.maxMem variable will be set to the maximum value for + * a signed long. This is because LDoms does not have a + * concept for Max memory for a Domain. However the VMM and + * virsh shell do have a Max mem concept and won't let you + * change the memory for a Domain if the new amount is + * greater than the Maximum. So this is why we set the Max + * to the value for a signed long, even though the info.maxMem + * data type is an unsigned long. + * + * Input: domain - Pointer to a virDomain struct + * info - Poiner to a virDomainInfo struct to update + * Output: Indirect virDomainInfo struct updated with input domain info + */ +int +ldomsDomainGetInfo (virDomainPtr domain, virDomainInfoPtr info) +{ + char state; + int domidx; + struct timeval tv; + virDomainInfo domInfo; + + unsigned long maxmem; + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainGetInfo(ENTER) \n"); + + if (gettimeofday(&tv, NULL) < 0) { + ldomsError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, _("getting time of day"), VIR_ERR_WARNING); + return (-1); + } + + if ((domidx = getDomainIndex(domain)) < 0) { + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainGetInfo(EXIT) getDomainIndex() failed\n"); + return (-1); + } + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainGetInfo() domain=%s, domidx=%d\n",domain->name,domidx); + +/* Convert between LDom Domain state and libvirt Domain state + * LDoms + * 1 = active + * 2 = stopping + * 3 = inactive + * 4 = binding + * 5 = unbinding + * 6 = bound + * 7 = starting + * + * libvirt + * + * VIR_DOMAIN_NOSTATE = 0, no state + * VIR_DOMAIN_RUNNING = 1, /* the domain is running + * VIR_DOMAIN_BLOCKED = 2, /* the domain is blocked on resource + * VIR_DOMAIN_PAUSED = 3, /* the domain is paused by user + * VIR_DOMAIN_SHUTDOWN= 4, /* the domain is being shut down + * VIR_DOMAIN_SHUTOFF = 5, /* the domain is shut off + * VIR_DOMAIN_CRASHED = 6 /* the domain is crashed + */ + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainGetInfo() ldomName=%s\n",ldominfo_list[domidx]->ldomName); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainGetInfo() ldomState=%d\n",ldominfo_list[domidx]->ldomState); + + switch (ldominfo_list[domidx]->ldomState) { + case LDOM_STATE_ACTIVE: state = VIR_DOMAIN_RUNNING; break; + case LDOM_STATE_STOPPING: state = VIR_DOMAIN_SHUTDOWN; break; + case LDOM_STATE_INACTIVE: state = VIR_DOMAIN_SHUTOFF; break; + case LDOM_STATE_BINDING: state = VIR_DOMAIN_SHUTDOWN; break; + case LDOM_STATE_UNBINDING: state = VIR_DOMAIN_SHUTOFF; break; + case LDOM_STATE_BOUND: state = VIR_DOMAIN_SHUTDOWN; break; + case LDOM_STATE_STARTING: state = VIR_DOMAIN_RUNNING; break; + default: state= VIR_DOMAIN_NOSTATE; break; + } + + /* Only name and state are obtained from a list-domain */ + domInfo.state = state; + + /* + * Get the maximum memory that can be allocated for this domain. + * For a domain that is bound or active, then it already has that + * memory allocated to it. The amount the it can add is only the + * amount free in the system, so just add Free + Bound memory. + * + * For a domain that is inactive, it can request any amount of + * memory, even more than what is physically present. Thus we + * have to put an upper limit on the Maximum, so again, we just + * add the amount of Free system memory to the requested memory. + * + * Set the dominfo.maxMem to the maximum value for a signed long + * if it fails to get the maximum memory allowed from LDM + */ + if (get_free_memory(&maxmem) < 0) { + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainGetInfo() get_free_memory() failed\n"); + domInfo.maxMem = MAXLONG; + } else + domInfo.maxMem = maxmem + ldominfo_list[domidx]->ldomMemSize; + + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainGetInfo() MaxMemory=%d\n", maxmem); + + domInfo.memory = ldominfo_list[domidx]->ldomMemSize; + domInfo.nrVirtCpu = (unsigned short)ldominfo_list[domidx]->ldomNumVCpu; + /* LDM XML does not give the uptime for a Domain, so just set this to 0 */ + domInfo.cpuTime = 0; /* ((tv.tv_sec * 1000ll * 1000ll * 1000ll * (domidx+1)) + (tv.tv_usec + 100011)); */ + memcpy(info, &domInfo, sizeof(virDomainInfo)); + + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainGetInfo() ldomMemory=%d\n",ldominfo_list[domidx]->ldomMemSize); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainGetInfo() ldomVcpu=%d\n",ldominfo_list[domidx]->ldomNumVCpu); + + + return (0); +} /* ldomsDomainGetInfo() */ + +/* + * ldomsDomainSetVcpus + * + * What: Given a Domain pointer, send an XML request to change + * the number of cpus for the domain. + * + * Input: domain - A pointer to the virDomain structure + * Output: function return - >=0 is success + */ +int +ldomsDomainSetVcpus(virDomainPtr domain, unsigned int nvcpus) +{ + int rc; + int domidx; + virDomainInfo domInfo; + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainSetVcpus(ENTER) vcpus=%d\n",nvcpus); + + if ((domidx = getDomainIndex(domain) < 0)) { + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainSetVcpus(EXIT) getDomainIndex() failed\n"); + return (-1); + } + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainSetVcpus: domain=%s, domidx=%d\n",domain->name,domidx); + rc = send_ldom_set_vcpu(domain->name, nvcpus); + + if (rc == 0) { + ldominfo_list[domidx]->ldomNumVCpu = nvcpus; + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainSetVcpus() vcpus changed to %d\n",nvcpus); + } + else { + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainSetVcpus() vcpu change failed\n"); + return (-1); + } + return(0); + +} /* ldomsDomainSetVcpus() */ + +/* + * ldomsDomainGetVcpus + * + * What: Given a Domain pointer, send an XML request to get + * the CPU bindings for the domain. Then fill in the + * input cpuInfo structure with detailed info for each + * CPU: virt->phys mapping, State, Uptime. The Affinity + * is only Xen specific at this time. + * + * Input: domain - A pointer to the virDomain structure + * Output: function return - >=0 is success + */ +int +ldomsDomainGetVcpus(virDomainPtr domain, virVcpuInfoPtr info, int maxinfo, + unsigned char *cpumaps, int maplen) +{ + int i,rc; + int domidx; + int numCpu; + long long upTime; + char cpuBits; + cpuBindings_t *cpuBindings; + processor_info_t pinfo; + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainGetVcpus(ENTER) domName=%s, maxinfo=%d, maplen=%d\n",domain->name, maxinfo, maplen); + + /* Get the Domain index */ + if ((domidx = getDomainIndex(domain) < 0)) { + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainGetVcpus(EXIT) getDomainIndex() failed\n"); + return (-1); + } + + /* The domain must be in the Bound or Active state */ + if ( (ldominfo_list[domidx]->ldomState == LDOM_STATE_BOUND) || + (ldominfo_list[domidx]->ldomState == LDOM_STATE_ACTIVE) ) + { + if ((numCpu = get_ldom_cpu_bindings(domain->name, &cpuBindings)) < 0) { + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainGetVcpus(EXIT) numCpu=%d failed\n",numCpu); + return (-1); + } + } + else { + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainGetVcpus(EXIT) Domain in wrong state %s\n",ldominfo_list[domidx]->ldomState); + return (-1); + } + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainGetVcpus() numCpu=%d, cpuBindings=%d \n",numCpu,cpuBindings); + + /* Walk the linked list of CPU bindings and fill in the cpuInfoPtr return structure */ + + upTime = getCpuUpTime(); + while (cpuBindings != NULL) { + + /* Get the CPU state from processor_info(). This will only work for + * CPUs in the Control Domain. All other CPUs are in the Guest Domains, + * so we set their state to Unknown and Uptime to 0. + * TODO unless we can get the State and Uptime from the PRI?? + */ + + if (processor_info(cpuBindings->real, &pinfo) == 0) { + switch (pinfo.pi_state) { + case 1: info->state = VIR_VCPU_OFFLINE; break; + case 2: info->state = VIR_VCPU_RUNNING; break; + case 3: info->state = VIR_VCPU_BLOCKED; break; + case 4: info->state = VIR_VCPU_OFFLINE; break; + case 5: info->state = VIR_VCPU_OFFLINE; break; + case 6: info->state = VIR_VCPU_RUNNING; break; + case 7: info->state = VIR_VCPU_OFFLINE; break; + default: info->state = VIR_VCPU_OFFLINE; break; + } + info->cpuTime = upTime; + } else { + info->state = VIR_VCPU_UNKNOWN; + info->cpuTime = 0; + } + + info->number = cpuBindings->virt; + info->cpu = cpuBindings->real; + + /* Got this from libvirt/libvirt.h VIR_CPU_USABLE */ + /* cpumaps[(info->number) + (info->cpu)/8] = (1<<((info->cpu)%8)); */ + info++; + + cpuBindings = cpuBindings->next; + } + + return (numCpu); + +} /* ldomsDomainSetVcpus() */ + +/* + * ldomsDomainGetMaxVcpus + * + * What: If the domain is inactive, then you can set the cpu constraint + * to any number you want, because the CPUs are not reserved until + * the domain is bound. Return MAXINT for inactive domains. + * + * For non-inactive domains, call existing fuctions to get the amount + * of Free and Used CPUs in the system. Now since a request is being + * made for a domain that already has Vcpus allocated to it, we have + * to add that amount of the Free amount, and then return that value. + * + * Example. Total Vcpus=32; Free Vcpus=10; Domains X has 15. + * Now, we want to increase Domain X Vcpus to 17. + * Now, if we return the # for Free Vcpus, virsh will not call the + * SetVcpu routine, and will report an error instead, because the + * request of 17 is greater than the Free amount. + * So we add the Free (10) and the current allocated (15) and return + * that as the Max # of Vcpus. + * + * + * Input: domain - A pointer to the virDomain structure UNUSED + * Output: function return - >=0 # of Free CPUs. <0 error. + */ +int +ldomsDomainGetMaxVcpus(virDomainPtr domain) +{ + int freeVcpus; + int domainVcpus; + + /* inactive domains can request any amount of vcpus */ + if (domain->id < 0) { + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainGetMaxVcpus(ENTER) inactive domain=%s, MaxVcpus=%d\n",domain->name,MAXINT); + return (MAXINT); + } + + /* + * For bound and active domains, sum the # of Free Vcpus and the # currently + * currently used by the Domain. This is the Max # that can be requested. + */ + freeVcpus = ldomsGetMaxVcpus(NULL, NULL); + domainVcpus = ldominfo_list[domain->id]->ldomNumVCpu; + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainGetMaxVcpus() Domain Vcpus=%d, Free Vcpus=%d\n",ldominfo_list[domain->id]->ldomNumVCpu,freeVcpus); + + return (freeVcpus + domainVcpus); + + +} /* ldomsDomainSetVcpus() */ + +/* + * ldomsDomainDumpXML + * + * What: Send a list-constraints request to LDM for the domain specified + * by the input virDomain. + * + * Input: domain - Pointer to a virDomain structure + * Output: function return - char ptr to the XML data + */ +char * +ldomsDomainDumpXML(virDomainPtr domain, int flags) +{ + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainDumpXML(ENTER) domain=%s\n",domain->name); + + xmlDocPtr xml_to_send; + xmlDocPtr xml_received; + unsigned char *xml; + int xmlSize; + int domidx; + + if ((domidx = getDomainIndex(domain)) < 0) { + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainDumpXML(EXIT) getDomainIndex() failed\n"); + return (NULL); + } + + /* Create XML list-constraints request */ + xml_to_send = create_xml_file_4_ldom_action(domain->name, XML_LIST_CONST); + if (xml_to_send == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainDumpXML() can not create list-constraints xml file\n"); + ldomsError(NULL, domain, VIR_ERR_INTERNAL_ERROR, "Could not create XML file", VIR_ERR_ERROR); + return (NULL); + } + + /* Send request to LDM and receive response */ + xml_received = send_xml_file_to_ldm(xml_to_send); + if (xml_received == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainDumpXML() failed to send xml file to ldm and receive xml response\n"); + ldomsError(NULL, domain, VIR_ERR_OPERATION_FAILED, "Could not create XML file", VIR_ERR_ERROR); + xmlFreeDoc(xml_to_send); + return (NULL); + } + + /* Dump the response to memory */ + xml = malloc(sizeof(char) * 10000); + xmlKeepBlanksDefault(0); + xmlDocDumpFormatMemory(xml_received, &xml, &xmlSize, 1); + if ( xmlSize > 0 ) { + xmlFree(xml_received); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainDumpXML() xml doc size is %d:\n%s\n",xmlSize,xml); + return ((char *)xml); + } + + return (NULL); + +} /* ldomsDomainDumpXML() */ + +/* + * ldomsListDefinedDomains + * + * What: Return the names of all inactive Domains + * + * Input: conn - Pointer to a connection structure + * names - An array of pointers to chars (Domain names) + * maxnames - Total number of inactive Domains + * Output: function return - # of inactive Domains found + * names - Updated pointers to strdup'd inactive Domain names + */ +int ldomsListDefinedDomains(virConnectPtr conn, + char **const names, + int maxnames) +{ + +/* virsh command 'define' creates a domain but leaves it in the bound state. + * This routine is finding all inactive domains, but the name of the routine is + * ListDefinedDomains. We are just following what test.c does, which is count + * the number of Domains in libvirt state SHUTOFF, which is LDoms state inactive. + */ + int n = 0, i; + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsListDefinedDomains(ENTER) maxnames=%d\n",maxnames); + + /* get the current ldom data from LDM */ + (void) pthread_rwlock_wrlock(&update_lock); + refresh_ldom_data(); + (void) pthread_rwlock_unlock(&update_lock); + + + /* TODO NOTE: We did not call get_ldom_names() here since the thought + * is that ldomsNumOfDefinedDomains() will always be called right + * before this is. This may not be the case, but for now we will + * leave until we find out otherwise. + */ + for (i = 0, n = 0 ; i < ldom_cnt && n < maxnames ; i++) { + if (ldominfo_list[i]->ldomState == LDOM_STATE_INACTIVE) { + names[n++] = strdup(ldominfo_list[i]->ldomName); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsListDefinedDomains() inactive domain=%s\n", + ldominfo_list[i]->ldomName); + } + } + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsListDefinedDomains(EXIT) # of inactive domains=%d\n",n); + return (n); +} /* ldomsListDefinedDomains() */ + +/* + * ldomsNumOfDefinedDomains + * + * What: Return the total number of inactive Domains (VIR_SHUT_OFF) + * + * Input: conn - Pointer to a connection structure + * Output: function return - # of inactive Domains + */ +int +ldomsNumOfDefinedDomains (virConnectPtr conn) +{ + int numInactive = 0, i; + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsNumOfDefinedDomains(ENTER) \n"); + + /* Send an LDM request 'list-domains' */ + if (get_ldom_names(&ldom_cnt, &ldominfo_list) < 0) { + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsNumOfDefinedDomains() get_ldom_names() failed\n"); + return (-1); + }; + + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsNumOfDefinedDomains() get_ldom_names(return) ldom_cnt=%d\n",ldom_cnt); + + + /* The Domain ID will be the index into the ldominfo_list array */ + for (i=0; i<ldom_cnt; i++) { + if (ldominfo_list[i] == NULL) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsNumOfDefinedDomains() ldominfo_list[%d] is NULL\n",i); + } + else if (ldominfo_list[i]->ldomState == LDOM_STATE_INACTIVE) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsNumOfDefinedDomains() inactive Domain ID=%d \n",i); + numInactive++; + } + } + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsNumOfDefinedDomains(EXIT) # of inactive domains=%d \n",numInactive); + return (numInactive); +} /* ldomsNumOfDefinedDomains() */ + +/* + * ldomsDomainStart + * + * What: Start a previously defined inactive domain. + * The domain can be in the inactive or bound state to begin + * and will be put into the active state. + * + * Input: domain - A pointer to a virDomain structure (unused) + * Output: function return - >=0 is success + */ +int +ldomsDomainStart(virDomainPtr domain) +{ + int rc = 0; + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainStart(ENTER) domName=%s, domID=%d\n", + domain->name,domain->id); + + /* get the current ldom data from LDM */ + (void) pthread_rwlock_wrlock(&update_lock); + refresh_ldom_data(); + (void) pthread_rwlock_unlock(&update_lock); + + + /* If the domain ID is -1, this means the domain state is 'inactive. + * If the domain state is inactive (3), then bind the domain */ + if ((domain->id == -1) || + (ldominfo_list[domain->id]->ldomState == LDOM_STATE_INACTIVE)) { + + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainStart() Domain state is 'inactive', binding...\n"); + rc = send_ldom_lifecycle_action(domain->name, LDOM_BIND); + + /* Need to tell the next piece of code that a 'bind' was done and + * was successful, because the ldom info has not been updated. + */ + if (rc == 0) rc = 1; + } + + /* If the domain was inactive and failed to bind, or if the domain was already + * active, then we fall into this case. + */ + if (rc < 0) { + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainStart(EXIT) Domain state change failed. domain=%s\n",domain->name); + return (-1); + } + + /* If the domain state is bound (6), then start the domain */ + if ((rc == 1) || (ldominfo_list[domain->id]->ldomState == LDOM_STATE_BOUND)) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainStart() Domain state is 'bound' rc=%d, starting...\n",rc); + rc = send_ldom_lifecycle_action(domain->name, LDOM_START); + if (rc == 0) rc = 2; + } + + /* If successful, the domain state changed to active */ + if (rc == 2) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainStart() Domain state changed to 'active'. domain=%s\n", + domain->name); + return (0); + } + + ldomsError(NULL, domain, VIR_ERR_INVALID_OPTION, _("Domain is active"), VIR_ERR_WARNING ); + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainStart(EXIT) Domain state change failed. domain=%s\n",domain->name); + return (-1); + + +} /* ldomsDomainStart() */ + +/* + * ldomsDomainDefineXML + * + * What: Create a domain from an XML file so that it is left in the + * bound state. The XML input must be of the form from the + * output of 'ldm list-constraints -x <ldom>'. Any of the + * XML can be changed, but the <action> tag will be added by + * this code. + * + * Input: xml - The xml file in a char buffer + * Output: function return - A ptr to a virDomain or NULL + */ +virDomainPtr +ldomsDomainDefineXML(virConnectPtr conn, const char *xml) +{ + virDomainPtr domPtr = NULL; + char *domainName = NULL; + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainDefineXML(ENTER) \n"); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainDefineXML(ENTER) xmldoc=\n%s\n",xml); + + /* Send the XML file along with the lifecycle action */ + domainName = send_ldom_create_domain((char *)xml, XML_BIND_DOMAIN); + + /* + * If the create domain was successful, then we have to create a DomainInfo + * structure to pass back to the caller. We need the Domain name that was + * created, and the only way to get that is from parsing the input xml + * document. This is done in send_ldom_create_domain() and passed back as the return. + * Also we create the uuid for the domain name. + */ + if (domainName != NULL) { + + /* + * The UUID is not important, cuz only the Domain name is printed + * out in the virsh. So just use the default UUID. + */ + domPtr = virGetDomain(conn, domainName, Uuid); + free(domainName); + } + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainDefine(EXIT) \n"); + + return (domPtr); +} /* ldomsDomainDefineXML() */ + +/* + * ldomsDomainUndefine + * + * What: Given a Domain pointer, undefine/delete the domain. + * The domain can only be in the inactive state to begin, + * and will be deleted to not exist. + * + * Input: domain - A pointer to the virDomain structure + * Output: function return - >=0 is success + */ +int +ldomsDomainUndefine(virDomainPtr domain) +{ + int rc = -1; + int domidx; + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainUndefine(ENTER) domName=%s, domID=%d\n", + domain->name,domain->id); + + /* If the Domain is inactive, the ID in the input domain will be a -1. + * Get the domain index from our ldominfo_list to verify it is indeed inactive + * before we try to delete it. + */ + if ((domidx = getDomainIndex(domain)) < 0) { + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainUndefine(EXIT) getDomainIndex() failed\n"); + return (-1); + } + + /* If the domain state is inactive, then delete the domain */ + if ( (ldominfo_list[domidx]->ldomState == LDOM_STATE_INACTIVE) && + (domain->id == -1) ) + { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainUndefine() Domain state is 'inactive', deleting...\n"); + rc = send_ldom_lifecycle_action(domain->name, LDOM_DELETE); + } else { + ldomsError(NULL, domain, VIR_ERR_INVALID_OPTION, _("Domain is not inactive"), VIR_ERR_WARNING ); + return (-1); + } + + /* If successful, the domain has been deleted */ + if (rc == 0) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainUndefine() Domain deleted\n"); + return (0); + } + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainUndefine(EXIT) Domain undefine failed\n"); + + return (-1); +} /* ldomsDomainUndefine() */ + +/* + * ldomsDomainConsole + * + * What: Given a Domain pointer, get the console port number + * for the domain. + * + * Input: domain - A pointer to the virDomain structure + * Output: function return - <0 is failure, >0 is the domain console port number + */ +int +ldomsDomainConsole(virDomainPtr domain) +{ + int rc = 0; + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainConsole(ENTER) domName=%s, domID=%d\n", + domain->name,domain->id); + + /* get the current ldom data from LDM */ + (void) pthread_rwlock_wrlock(&update_lock); + refresh_ldom_data(); + (void) pthread_rwlock_unlock(&update_lock); + + /* + * If the domain ID is -1, this means the domain state is 'inactive. + * If the domain state is inactive (3), then we don't return a port # + * Log an error because an inactive domain has no active console port. + */ + if ((domain->id == -1) || + (ldominfo_list[domain->id]->ldomState == LDOM_STATE_INACTIVE)) { + + if (ldoms_detailed_debug) dprt("LDOMS_DEBUG: ldomsDomainConsole(EXIT) Domain state is 'inactive'\n"); + ldomsError(NULL, domain, VIR_ERR_INVALID_OPTION, "Domain is inactive", VIR_ERR_WARNING ); + return (-1); + } + + /* + * If the domain is the primary domain, then its console has to be accessed + * via the SC and not thru this utility. There is no port number associated + * with the primary domain console. + */ + if (strcmp(domain->name, "primary") == 0) { + ldomsError(NULL, domain, VIR_ERR_INVALID_OPTION, "Must connect to primary console via the SC", VIR_ERR_WARNING ); + return (-1); + } + + /* Else, the console port # is in the ldominfo_list[] for that domain */ + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainConsole(EXIT) console=%d\n", + ldominfo_list[domain->id]->ldomConsole); + return (ldominfo_list[domain->id]->ldomConsole); +} + +void dprt(const char *template, ...) +{ + va_list ap; + char buf[10240]; + static int do_open = 1; + static FILE * fp; + + va_start(ap, template); + (void) vsprintf(buf, template, ap); + va_end(ap); + + if (do_open) { + do_open = 0; + + fp = fopen("/var/log/libvirt_ldoms.dbg", "w"); + } + fputs(buf, fp); + fflush(fp); +} /* dprt */ + +/* + * refresh_ldom_data + * + * Send an XML request to LDM for a list of all Domains + * and get the basic information of each Domain. + * + * This function will be called for each commands to + * guarantee that we have the current LDom info for any + * virsh CLI command. + * + * Input: + * Output: + */ +static void +refresh_ldom_data() +{ + struct timeval tv; + long delta; + + /* Try and throttle calling the LDM to update the list of + * LDoms. If the calls are 2 secs or less apart, then we just + * use the current LDoms info. This is because each + * command from virsh can turn into several calls into + * function in this file, which all call this function + * to refresh the LDom data. + */ + if (gettimeofday(&tv, NULL) >= 0) { + /* See if the time since the last call to this functions + * is less than 3 seconds. If so, just return. + */ + if ((delta=tv.tv_sec - last_ldom_refresh) < 3) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: refresh_ldom_data(NO Refresh) delta=%d\n", + delta); + return; + } + last_ldom_refresh = tv.tv_sec; + } + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: refresh_ldom_data(Refresh) delta=%d\n",delta); + + /* + * Call LDM with a list-domains cmd to get the total + * # of Domains and the basic information of each one. + */ + + if (get_ldom_names(&ldom_cnt, &ldominfo_list) < 0) { + /* Any ldominfo_t memory was freed in get_ldom_names() */ + free(ldominfo_list); + if (ldoms_debug) dprt("LDOMS_DEBUG: refresh_ldom_data() get_ldom_names() failed\n"); + } + +} /* refresh_ldom_data() */ + +/* + * getHypervisorVersion() + * + * Gets the LDoms hypervisor version by parsing the output of + * the command 'ldm -V'. + * + * The output is: + * Logical Domain Manager (v 1.0.1) + * Hypervisor control protocol v 1.0 + + * System PROM: + * Hypervisor v. 1.5.1 @(#)Hypervisor 1.5.1 2007/09/14 16:11\015 + * + * OpenBoot v. 4.27.1 @(#)OBP 4.27.1 2007/09/14 15:17 + * + * Output: long representing the value of the hypervisor version in the format + * needed by virsh. + */ + +static long +getHypervisorVersion() +{ + FILE *fp = popen("/opt/SUNWldm/bin/ldm -V", "r"); + char buf[256]; + char major[8], minor[8], rel[8]; + char *chp,*chp1; + long long hyperVersion; + int done = 0; + int PROM_line_found = 0; + long maj,min,release; + + if (ldoms_debug) dprt("LDOMS_DEBUG: getHypervisorVersion(ENTER)\n"); + + /* check the popen for failure */ + if (fp == NULL) + return(0); + + /* Read the input until we find and process the Hypervisor version */ + while (! done) { + chp = fgets(buf,sizeof(buf),fp); + if (chp == NULL) + return(0); + + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: getHypervisorVersion() line=%s\n",buf); + + if ( (chp1=strstr(buf,"PROM")) != NULL ) { + PROM_line_found = 1; + continue; + } + + if (((chp1=strstr(buf,"Hypervisor")) != NULL ) && (PROM_line_found)) { + + /* + * There are more than 1 lines with 'Hypervisor' in them. Wait until + * we find the line with 'System PROM'. The next line with 'Hypervisor' + * will be the one we want. + */ + + /* Step thru to the '.' */ + while (*chp1 != '.') chp1++; + chp1++; + + /* Step to the 1st digit of the version */ + while ((*chp1 == ' ') || (*chp1 == '\t')) chp1++; + + /* Pointing to 1st digit of version. */ + if (isdigit(*chp1)) { + major[0] = *chp1++; + } else { + if (ldoms_debug) dprt("LDOMS_DEBUG: getHypervisorVersion() Major '%c' is not a digit\n",*chp1); + return(0); + } + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: getHypervisorVersion() major=%c:%d \n",major[0],major[0]); + + /* *chp1 should be a '.' */ + if (*chp1++ != '.' ) { + if (ldoms_debug) dprt("LDOMS_DEBUG: getHypervisorVersion() No . after major? ch=%c\n",*(--chp1)); + return(0); + } + /* Should be pointing to minor version digit */ + if (isdigit(*chp1)) { + minor[0] = *chp1++; + } else { + if (ldoms_debug) dprt("LDOMS_DEBUG: getHypervisorVersion() Minor '%c' is not a digit\n",*chp1); + return(0); + } + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: getHypervisorVersion() minor=%c:%d \n",minor[0],minor[0]); + + /* If we are not pointing to a '.', then there is no rel */ + if (*chp1++ != '.' ) { + rel[0] = '0'; + if (ldoms_debug) dprt("LDOMS_DEBUG: getHypervisorVersion() Not pointing to a '.' before the release digit. ch=%c\n",*(--chp1)); + } else { + if (isdigit(*chp1)) { + rel[0] = *chp1++; + } else { + if (ldoms_debug) dprt("LDOMS_DEBUG: getHypervisorVersion() Release '%c' is not a digit\n",*chp1); + rel[0] = '0'; + } + } + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: getHypervisorVersion() rel=%c:%d \n",rel[0],rel[0]); + + break; /* out of while */ + } + } /* while */ + + /* Now construct the long long that virsh needs to convert to a version number */ + major[1] = '\0'; + minor[1] = '\0'; + rel[1] = '\0'; + + maj = atol(major) * 1000000; + min = atol(minor) * 1000; + release = atol(rel); + + if (ldoms_debug) dprt("LDOMS_DEBUG: getHypervisorVersion(EXIT) maj=%d, min=%d, rel=%d, version=%d\n", + maj,min,release,maj+min+release); + + return (maj + min + release); + +} /* getHypervisorVersion() */ + + +/* + * getLDMVersion() + * + * Gets the LDoms Manager version by parsing the output of + * the command 'ldm -V'. + * + * The output is: + * Logical Domain Manager (v 1.0.1) + * .. + * Output: long representing the value of the LDoms Manager version in the format + * needed by virsh. + */ + +static unsigned long +getLDMVersion() +{ + FILE *fp = popen("/opt/SUNWldm/bin/ldm -V", "r"); + char buf[256]; + char major[8], minor[8], rel[8]; + char *chp,*chp1; + int done = 0; + int LDM_line_found = 0; + unsigned long maj,min,release; + + if (ldoms_debug) dprt("LDOMS_DEBUG: getLDMVersion(ENTER)\n"); + + /* check the popen for failure */ + if (fp == NULL) + return(0); + + /* Read the input until we find and process the LDoms Manager version */ + while (! done) { + chp = fgets(buf,sizeof(buf),fp); + if (chp == NULL) + return(0); + + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: getLDMVersion() line=%s\n",buf); + + + if ( (chp1=strstr(buf,"Logical Domain Manager")) != NULL ) { + LDM_line_found = 1; + } + + if (LDM_line_found) { + + /* Step thru to the 'v' */ + while (*chp1 != 'v') chp1++; + chp1++; + + /* Step to the 1st digit of the version */ + while ((*chp1 == ' ') || (*chp1 == '\t')) chp1++; + + /* Pointing to 1st digit of version. */ + if (isdigit(*chp1)) { + major[0] = *chp1++; + } else { + if (ldoms_debug) dprt("LDOMS_DEBUG: getLDMVersion() Major '%c' is not a digit\n",*chp1); + return(0); + } + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: getLDMVersion() major=%c:%d \n",major[0],major[0]); + + /* *chp1 should be a '.' */ + if (*chp1++ != '.' ) { + if (ldoms_debug) dprt("LDOMS_DEBUG: getLDMVersion() No . after major? ch=%c\n",*(--chp1)); + return(0); + } + /* Should be pointing to minor version digit */ + if (isdigit(*chp1)) { + minor[0] = *chp1++; + } else { + if (ldoms_debug) dprt("LDOMS_DEBUG: getLDMVersion() Minor '%c' is not a digit\n",*chp1); + return(0); + } + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: getLDMVersion() minor=%c:%d \n",minor[0],minor[0]); + + /* If we are not pointing to a '.', then there is no rel */ + if (*chp1++ != '.' ) { + rel[0] = '0'; + if (ldoms_debug) dprt("LDOMS_DEBUG: getLDMVersion() Not pointing to a '.' before the release digit. ch=%c\n",*(--chp1)); + } else { + if (isdigit(*chp1)) { + rel[0] = *chp1++; + } else { + if (ldoms_debug) dprt("LDOMS_DEBUG: getLDMVersion() Release '%c' is not a digit\n",*chp1); + rel[0] = '0'; + } + } + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: getLDMVersion() rel=%c:%d \n",rel[0],rel[0]); + + break; /* out of while */ + } + } /* while */ + + /* Now construct the long long that virsh needs to convert to a version number */ + major[1] = '\0'; + minor[1] = '\0'; + rel[1] = '\0'; + + maj = atol(major) * 1000000; + min = atol(minor) * 1000; + release = atol(rel); + + if (ldoms_debug) dprt("LDOMS_DEBUG: getLDMVersion(EXIT) maj=%d, min=%d, rel=%d, version=%d\n", + maj,min,release,maj+min+release); + + return (maj + min + release); + +} /* getLDMVersion() */ + +/* + * getCpuUpTime() + * + * Gets the CPU uptime by parsing the output of the command 'uptime'. + * + * Output: long representing the value of the cpu up time in nanosecs + */ +long long +getCpuUpTime() +{ + FILE *fp = popen("/bin/uptime", "r"); + char buf[256]; + char output[256]; + char day[5]; + char hour[5]; + char min[5]; + char *chp1,*chp2; + int d=0,h=0,m=0; + long long uptime; + long long duptime; + long long huptime; + long long muptime; + + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: getCpuUpTime(ENTER) \n"); + + /* check the popen for failure */ + if (fp == NULL) + return(0); + + fgets(buf,sizeof(buf),fp); + pclose(fp); + + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: getCpuUpTime() buf=%s \n",buf); + /* Find 'up', then advance to the day */ + /* 6:56am up 6 day(s), 14:46, 10 users, load average: 0.06, 0.14, 0.12 */ + chp1 = strstr(buf, "up"); + if (chp1 == NULL) + return(0); + + while (*chp1 != ' ') chp1++; + chp1++; + + /* Get the day number */ + /* chp2 is point at the 6 */ + chp2=chp1; + while (*chp2 != ' ') { + day[d++] = *chp2; + chp2++; + } + day[d] = '\0'; + chp2++; + + /* Advance to the 1st digit of the hour */ + /* chp2 is pointing at the 'd' */ + while (*chp2 != ' ') chp2++; + chp2++; + + /* Get the hour number */ + /* chp2 is pointing at the '1' */ + while (*chp2 != ':') { hour[h++] = *chp2; chp2++; } + hour[h] = '\0'; + chp2++; + + /* Get the minute number */ + /* chp2 is pointing at the '4' after : */ + while (*chp2 != ',') { min[m++] = *chp2; chp2++; } + min[m] = '\0'; + chp2++; + + strncpy(output, chp1, (chp2 - chp1)); + output[chp2-chp1] = '\0'; + + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: getCpuUpTime(): Uptime = %s\n",output); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: getCpuUpTime(): day=%s, hour=%s, min=%s\n",day,hour,min); + + duptime = atoll(day); + duptime = duptime * 60 * 60 * 24; + huptime = atoll(hour); + huptime = huptime * 60 * 60; + muptime = atoll(min); + muptime = muptime * 60; + uptime = duptime + huptime + muptime; + uptime *= 1000000000; + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: getCpuUpTime(): Uptime = %lld nanosecs\n",uptime); + + return (uptime); + +} /* getCpuUpTime() */ + +/* + * Callback functions for Network actions. These are defined at the end + * so we don't have to declare function prototypes at the beginning of + * this file. + */ +static virNetworkDriver ldomsNtwkDriver = { + "LDoms", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +/* + * Callback functions for Domain actions. These are defined at the end + * so we don't have to declare function prototypes at the beginning of + * this file. + */ +static virDriver ldomsDriver = { + VIR_DRV_LDOMS, /* Driver number */ + "LDoms", /* Name of the driver */ + LDOMS_VERSION_NUMBER, /* Version of LDoms API */ + ldomsOpen, /* open */ + ldomsClose, /* close */ + NULL, /* virDrvSupportsFeature supports_feature; NOT LDOMS SUPPORTED */ + ldomsGetType, /* type */ + ldomsGetVersion, /* getVersion */ + ldomsGetHostname, /* getHostname */ + NULL, /* virDrvGetURI getURI; NOT LDOMS SUPPORTED */ + ldomsGetMaxVcpus, /* virDrvGetMaxVcpus TODO need support */ + ldomsNodeGetInfo, /* nodeGetInfo */ + NULL, /* virDrvGetCapabilities getCapabilities; NOT LDOMS SUPPORTED */ + ldomsListDomains, /* listDomains */ + ldomsNumOfDomains, /* numOfDomains */ + ldomsDomainCreateXML, /* domainCreateLinux; Create an inactive domain from XML */ + ldomsDomainLookupByID, /* domainLookupByID */ + ldomsDomainLookupByUUID, /* domainLookupByUUID */ + ldomsDomainLookupByName, /* domainLookupByName */ + ldomsDomainSuspend, /* virDrvDomainSuspend domainSuspend; TODO NOT LDOMS SUPPORTED */ + ldomsDomainResume, /* virDrvDomainResume domainResume; TODO NOT LDOMS SUPPORTED */ + ldomsDomainShutdown, /* domainShutdown */ + NULL, /* virDrvDomainReboot domainReboot; NOT LDOMS SUPPORTED */ + ldomsDomainDestroy, /* domainDestroy */ + ldomsDomainGetOSType, /* domainGetOSType */ + ldomsDomainGetMaxMemory, /* domainGetMaxMemory */ + ldomsDomainSetMaxMemory, /* domainSetMaxMemory */ + ldomsDomainSetMemory, /* domainSetMemory */ + ldomsDomainGetInfo, /* domainGetInfo */ + NULL, /* virDrvDomainSave domainSave; NOT LDOMS SUPPORTED */ + NULL, /* virDrvDomainRestore domainRestore; NOT LDOMS SUPPORTED */ + NULL, /* virDrvDomainCoreDump domainCoreDump; NOT LDOMS SUPPORTED */ + ldomsDomainSetVcpus, /* domainSetVcpus */ + NULL, /* virDrvDomainPinVcpu domainPinVcpu; NOT LDOMS SUPPORTED */ + ldomsDomainGetVcpus, /* domainGetVcpus */ + ldomsDomainGetMaxVcpus, /* domainGetMaxVcpus; */ + ldomsDomainDumpXML, /* domainDumpXML */ + ldomsListDefinedDomains, /* listDefinedDomains */ + ldomsNumOfDefinedDomains, /* numOfDefinedDomains */ + ldomsDomainStart, /* domainCreate - Really start an inactive domain */ + ldomsDomainDefineXML, /* domainDefineXML */ + ldomsDomainUndefine, /* domainUndefine */ + NULL, /* virDrvDomainAttachDevice domainAttachDevice; NOT LDOMS SUPPORTED */ + NULL, /* virDrvDomainDetachDevice domainDetachDevice; NOT LDOMS SUPPORTED */ + NULL, /* virDrvDomainGetAutostart domainGetAutostart; NOT LDOMS SUPPORTED */ + NULL, /* virDrvDomainSetAutostart domainSetAutostart; NOT LDOMS SUPPORTED */ + NULL, /* virDrvDomainGetSchedulerType domainGetSchedulerType; NOT LDOMS SUPPORTED */ + NULL, /* virDrvDomainGetSchedulerParameters domainGetSchedulerParameters; NOT LDOMS SUPPORTED */ + NULL, /* virDrvDomainSetSchedulerParameters domainSetSchedulerParameters; NOT LDOMS SUPPORTED */ + NULL, /* virDrvDomainMigratePrepare domainMigratePrepare; NOT LDOMS SUPPORTED */ + NULL, /* virDrvDomainMigratePerform domainMigratePerform; NOT LDOMS SUPPORTED */ + NULL, /* virDrvDomainMigrateFinish domainMigrateFinish; NOT LDOMS SUPPORTED */ + NULL, /* virDrvDomainBlockStats domainBlockStats; NOT LDOMS SUPPORTED */ + NULL, /* virDrvDomainInterfaceStats domainInterfaceStats; NOT LDOMS SUPPORTED */ + NULL, /* virDrvNodeGetCellsFreeMemory nodeGetCellsFreeMemory; NOT LDOMS SUPPORTED */ + NULL, /* virDrvNodeGetFreeMemory getFreeMemory; NOT LDOMS SUPPORTED */ + ldomsDomainConsole, /* ldomConsole */ +}; + +/* + * ldomsRegister: + * + * Registers LDoms in libvirt driver system. This functions goes at the + * end so we don't have to declare ldomsDriver or ldomsNtwkDriver ahead of time. + */ +int ldomsRegister(void) { + + /* for debug statements */ +#ifdef LDOMS_DEBUG + ldoms_debug = 1; + dprt("LDOMS_DEBUG on\n"); +#endif +#ifdef LDOMS_DETAILED_DEBUG + ldoms_detailed_debug = 1; + dprt("LDOMS_DETAILED_DEBUG on\n"); +#endif + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsRegister(ENTER)\n"); + + /* Only root can execute libvirt LDoms */ + if (geteuid() != 0) return -1; + + /* initialize for a rw-lock to make the code more multithread-safe */ + (void) pthread_rwlock_init(&update_lock, NULL); + + /* Get the LDoms Manager version number */ + LDMVersion = getLDMVersion(); + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsRegister.. LDoms version used = %u\n", LDMVersion); + + return (virRegisterDriver(&ldomsDriver)); + + +} + + +#endif /* WITH_LDOMS */ + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ + diff --git a/src/ldoms_intfc.h b/src/ldoms_intfc.h new file mode 100644 --- /dev/null +++ b/src/ldoms_intfc.h @@ -0,0 +1,336 @@ +/* + * ldoms_intfc.h: LDoms definitions used for interfaces with the LDoms Manager (LDM) + * + * Copyright 2008 Sun Microsystems, Inc. + * + * See COPYING.LIB for the License of this software + * + */ + +#ifndef __VIR_LDOMS_INTFC_H__ +#define __VIR_LDOMS_INTFC_H__ + +#ifdef WITH_LDOMS + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/byteorder.h> +#include <inttypes.h> +#include <libxml/tree.h> + +#include "ldoms_xml_parse.h" +#include "ldoms_common.h" + +#define DEFAULT_PORT 5311 +#define LDOM_INTERFACE_VERSION ((xmlChar *)"1.0") +#define LDOM_DATA_VERSION ((xmlChar *)"2.0") +#define VERSION_ATTR ((xmlChar *)"version") + +#define XML_VERSION ((xmlChar *)"1.0") /* version of XML used */ + +#define XML_ADD_DOMAIN ((xmlChar *)"add-domain") +#define XML_DELETE ((xmlChar *)"remove-domain") +#define XML_STOP ((xmlChar *)"stop-domain") +#define XML_START_DOMAIN ((xmlChar *)"start-domain") +#define XML_BIND_DOMAIN ((xmlChar *)"bind-domain") +#define XML_UNBIND ((xmlChar *)"unbind-domain") +#define XML_LIST_CONST ((xmlChar *)"list-constraints") +#define XML_LIST_BIND ((xmlChar *)"list-bindings") +#define XML_LIST ((xmlChar *)"list-domain") +#define XML_LIST_DEVICES ((xmlChar *)"list-devices") +#define XML_LIST_SERVICES ((xmlChar *)"list-services") +#define XML_LIST_SETS ((xmlChar *)"list-spconfig") +#define XML_LIST_VARS ((xmlChar *)"list-variable") +#define XML_ADD_VCPU ((xmlChar *)"add-vcpu") +#define XML_ADD_MAU ((xmlChar *)"add-mau") +#define XML_ADD_MEM ((xmlChar *)"add-memory") +#define XML_ADD_IO ((xmlChar *)"add-io") +#define XML_ADD_VAR ((xmlChar *)"add-variable") +#define XML_ADD_VCONSCON ((xmlChar *)"add-vconscon") +#define XML_ADD_VDISK ((xmlChar *)"add-vdisk") +#define XML_ADD_VDS ((xmlChar *)"add-vdiskserver") +#define XML_ADD_VDSDEV ((xmlChar *)"add-vdiskserverdevice") +#define XML_ADD_VNET ((xmlChar *)"add-vnet") +#define XML_ADD_VSW ((xmlChar *)"add-vswitch") +#define XML_ADD_SPCONFIG ((xmlChar *)"add-spconfig") +#define XML_ADD_VDPCS ((xmlChar *)"add-vdpcs") +#define XML_ADD_VDPCC ((xmlChar *)"add-vdpcc") +#define XML_SET_VCPU ((xmlChar *)"set-vcpu") +#define XML_SET_MAU ((xmlChar *)"set-mau") +#define XML_SET_MEM ((xmlChar *)"set-memory") +#define XML_SET_VAR ((xmlChar *)"set-variable") +#define XML_SET_VCONSCON ((xmlChar *)"set-vconscon") +#define XML_SET_VNET ((xmlChar *)"set-vnet") +#define XML_SET_VSW ((xmlChar *)"set-vswitch") +#define XML_SET_SPCONFIG ((xmlChar *)"set-spconfig") +#define XML_SET_VCONSOLE ((xmlChar *)"set-vconsole") +#define XML_REMOVE_VCPU ((xmlChar *)"remove-vcpu") +#define XML_REMOVE_MAU ((xmlChar *)"remove-mau") +#define XML_REMOVE_MEM ((xmlChar *)"remove-memory") +#define XML_REMOVE_IO ((xmlChar *)"remove-io") +#define XML_REMOVE_VAR ((xmlChar *)"remove-variable") +#define XML_REMOVE_VCONSCON ((xmlChar *)"remove-vconscon") +#define XML_REMOVE_VDISK ((xmlChar *)"remove-vdisk") +#define XML_REMOVE_VDS ((xmlChar *)"remove-vdiskserver") +#define XML_REMOVE_VDSDEV ((xmlChar *)"remove-vdiskserverdevice") +#define XML_REMOVE_VNET ((xmlChar *)"remove-vnet") +#define XML_REMOVE_VSW ((xmlChar *)"remove-vswitch") +#define XML_REMOVE_SPCONFIG ((xmlChar *)"remove-spconfig") +#define XML_REMOVE_RECONF ((xmlChar *)"remove-reconf") +#define XML_REMOVE_VDPCS ((xmlChar *)"remove-vdpcs") +#define XML_REMOVE_VDPCC ((xmlChar *)"remove-vdpcc") + +#define XML_LDM_INTERFACE ((xmlChar *)"LDM_interface") +#define XML_ACTION ((xmlChar *)"action") +#define XML_DATA ((xmlChar *)"data") +#define XML_SNMP_USER ((xmlChar *)"snmp_user") +#define XML_CMD ((xmlChar *)"cmd") +#define XML_LDM_INFO ((xmlChar *)"ldom_info") +#define XML_LDM_NAME ((xmlChar *)"ldom_name") +#define XML_RESPONSE ((xmlChar *)"response") +#define XML_STATUS ((xmlChar *)"status") +#define XML_RESP_MSG ((xmlChar *)"resp_msg") +#define XML_BINDING ((xmlChar *)"binding") +#define XML_FREE ((xmlChar *)"free") +#define XML_PID ((xmlChar *)"pid") +#define XML_VID ((xmlChar *)"vid") +#define XML_STRAND_PERCENT ((xmlChar *)"strand_percent") +#define XML_REAL_ADDR ((xmlChar *)"real_addr") +#define XML_PHYS_ADDR ((xmlChar *)"phys_addr") +#define XML_MODE ((xmlChar *)"mode") +#define XML_CPUSET ((xmlChar *)"cpuset") +#define XML_DEV_TYPE ((xmlChar *)"device_type") +#define XML_PORT ((xmlChar *)"port") +#define XML_SPCONFIG ((xmlChar *)"spconfig") +#define XML_SPCONFIG_NAME ((xmlChar *)"spconfig_name") +#define XML_SPCONFIG_STATUS ((xmlChar *)"spconfig_status") +#define XML_SERVICE_DOMAIN ((xmlChar *)"service_domain") +#define XML_PHYSIO_DEVICE ((xmlChar *)"physio_device") + +#define XML_CURRENT_STATUS ((xmlChar *)"current") +#define XML_NEXT_STATUS ((xmlChar *)"next") + +#define XML_SUCCESS "success" +#define XML_FAILURE "failure" + +/* + * XML node and attribute names + */ +#define CONSOLE_INSTANCE_NODE ((xmlChar *)"console_instance") +#define CONSOLE_NODE ((xmlChar *)"console") +#define CPU_NODE ((xmlChar *)"cpu") +#define MAU_NODE ((xmlChar *)"mau") +#define DEV_PATH_NODE ((xmlChar *)"dev_path") +#define GROUP_NODE ((xmlChar *)"group") +#define VCONS_PORT_NODE ((xmlChar *)"port") +#define INSTANCE_NODE ((xmlChar *)"instance") +#define LDOM_DATABASE_NODE ((xmlChar *)"ldom_database") +#define LDOM_NAME_NODE ((xmlChar *)"ldom_name") +#define LDOM_NODE ((xmlChar *)"ldom") +#define MAC_ADDRESS_NODE ((xmlChar *)"mac_address") +#define MAX_PORT_NODE ((xmlChar *)"max_port") +#define MEMORY_NODE ((xmlChar *)"memory") +#define MIN_PORT_NODE ((xmlChar *)"min_port") +#define MTU_NODE ((xmlChar *)"mtu") +#define NAME_NODE ((xmlChar *)"name") +#define NUMBER_NODE ((xmlChar *)"number") +#define PHYSIO_MINOR_NODE ((xmlChar *)"minor") +#define VOLUME_NAME_NODE ((xmlChar *)"vol_name") +#define VOLUME_OPTS_NODE ((xmlChar *)"vol_opts") +#define BLOCKDEV_NODE ((xmlChar *)"block_dev") +#define PHYSIO_NODE ((xmlChar *)"physio") +#define SERVICE_NAME_NODE ((xmlChar *)"service_name") +#define SERVER_INSTANCE_NODE ((xmlChar *)"server_instance") +#define SERVER_NODE ((xmlChar *)"server") +#define SHARING_NODE ((xmlChar *)"sharing") +#define SIZE_NODE ((xmlChar *)"size") +#define STATE_NODE ((xmlChar *)"state") +#define VALUE_NODE ((xmlChar *)"value") +#define VARIABLES_NODE ((xmlChar *)"variables") +#define VAR_NODE ((xmlChar *)"var") +#define VCC_INSTANCE_NODE ((xmlChar *)"vcc_instance") +#define VCC_NODE ((xmlChar *)"vcc") +#define DISK_INSTANCE_NODE ((xmlChar *)"disk_instance") +#define DISK_NODE ((xmlChar *)"disk") +#define VDISK_NAME_NODE ((xmlChar *)"vdisk_name") +#define VDS_VOLUME_NODE ((xmlChar *)"vds_volume") +#define VDS_VOLUMES_NODE ((xmlChar *)"vds_volumes") +#define VDS_INSTANCE_NODE ((xmlChar *)"vds_instance") +#define VDS_NODE ((xmlChar *)"vds") +#define VNET_INSTANCE_NODE ((xmlChar *)"network_instance") +#define VNET_NODE ((xmlChar *)"network") +#define VNET_NAME_NODE ((xmlChar *)"vnet_name") +#define VSW_INSTANCE_NODE ((xmlChar *)"vsw_instance") +#define VSW_POLICIES_NODE ((xmlChar *)"policies") +#define VSW_NODE ((xmlChar *)"vsw") +#define IO_INSTANCE_NODE ((xmlChar *)"io_instance") +#define IO_NODE ((xmlChar *)"io") +#define IO_NAME_NODE ((xmlChar *)"iodevice") +#define IO_BYPASS_NODE ((xmlChar *)"bypass_mode") +#define VLDCC_NODE ((xmlChar *)"vldcc") +#define TIMEOUT_NODE ((xmlChar *)"timeout") + +/* + * Protocol version supported. + */ +#define LDM_MAJOR_VER 1 +#define LDM_MINOR_VER 0 + +/* per class version numbers */ +#define LDM_CLI_MAJOR_VER 2 +#define LDM_CLI_MINOR_VER 0 + +#define LDM_CONTROL_MAJOR_VER 1 +#define LDM_CONTROL_MINOR_VER 0 + +#define LDM_SUNMC_MAJOR_VER 1 +#define LDM_SUNMC_MINOR_VER 0 + +enum { + LDM_INIT_REQ = 0x0, + LDM_INIT_ACK = 0x1, + LDM_INIT_NACK = 0x2, + LDM_ERROR = 0x3, + LDM_OP_REQ = 0x4, + LDM_OP_REPLY = 0x5 +}; + +typedef struct { + uint32_t msg_type; + uint32_t payload_len; +} ldm_hdr_t; + +enum { + LDM_CLASS_CLI = 0x1, + LDM_CLASS_CONTROL = 0x2, + LDM_CLASS_SUNMC = 0x3 +}; + +typedef struct { + uint32_t client_class; + uint16_t major_vers; + uint16_t minor_vers; +} ldm_init_req_t; + +typedef struct { + uint16_t minor_vers; +} ldm_init_ack_t; + +typedef struct { + uint16_t major_vers; +} ldm_init_nack_t; + +enum { + LDM_ERROR_UNKNOWN = 0x1, + LDM_ERROR_UNEXPECTED = 0x2, + LDM_ERROR_INVALID = 0x3 +}; + +typedef struct { + uint32_t error_code; +} ldm_error_t; + +typedef struct { + uint32_t rq_len; + uint32_t rq_id; + uint8_t rq_data[]; +} ldm_op_req_t; + +typedef struct { + uint32_t rp_len; + uint32_t rp_reqid; + uint8_t rp_data[]; +} ldm_op_reply_t; + +/* + * 0-3 are used by the ldm_protocol's initialization calls. + */ +enum { + LDM_CNTRL_PRI_REQ = 0x4, + LDM_CNTRL_PRI_REPLY = 0x5, + LDM_CNTRL_XML_REQ = 0x6, + LDM_CNTRL_XML_RESP = 0x7 +}; + +/* used by malloc to allocate memory for an entire msg */ +typedef struct ldm_handshake_msg_s { + ldm_hdr_t hdr_msg; + ldm_init_req_t msg; +} ldm_handshake_msg; + +typedef struct { + int len; + char data[]; +} xml_msg_t; + +#if defined(_BIG_ENDIAN) +#define hton8(_x) ((uint8_t)(_x)) +#define hton16(_x) ((uint16_t)(_x)) +#define hton32(_x) ((uint32_t)(_x)) +#define hton64(_x) ((uint64_t)(_x)) +#define ntoh8(_x) ((uint8_t)(_x)) +#define ntoh16(_x) ((uint16_t)(_x)) +#define ntoh32(_x) ((uint32_t)(_x)) +#define ntoh64(_x) ((uint64_t)(_x)) +#else +#define hton8(_x) ((uint8_t)(_x)) +#define hton16(_x) BSWAP_16((uint16_t)(_x)) +#define hton32(_x) BSWAP_32((uint32_t)(_x)) +#define hton64(_x) BSWAP_64((uint64_t)(_x)) +#define ntoh8(_x) ((uint8_t)(_x)) +#define ntoh16(_x) BSWAP_16((uint16_t)(_x)) +#define ntoh32(_x) BSWAP_32((uint32_t)(_x)) +#define ntoh64(_x) BSWAP_64((uint64_t)(_x)) +#endif + +xmlDocPtr send_xml_file_to_ldm(xmlDoc *xml_output); +xmlDocPtr handle_resp(char *resp_buf); + +void close_ldm_connection(int sock); +int get_free_memory(unsigned long *); +int get_ldom_names(int *, ldominfo_t ***); +int get_ldom_cpu_bindings(char *, cpuBindings_t **); +int get_ldom_total_cpu(int *); +int get_ldom_total_memory(unsigned long *); +int ldm_create_pkt_buf(char **pkt_buf, char *xml_buf, int xml_buf_len); +int open_ldm_connection(void); +int send_ldom_active_mgmt(char *snmp_user, char *ldom_name, int ldom_state); +char * send_ldom_create_domain(char *, xmlChar *); +int send_ldom_lifecycle_action(char *, int); +int send_ldom_set_memory(char *, unsigned long); +int send_ldom_set_vcpu(char *, int); + +int get_all_ldominfo(char *snmp_user, int *all_ldom_cnt, ldominfo_t ***all_ldominfo_list); +int get_crypto(char *snmp_user, char *ldom_name, int *crypto_cnt, crypto_t ***crypto_list); +int get_envvars(char *snmp_user, char *ldom_name, int *envvars_cnt, envvars_t ***envvars_list); +int get_iobus(char *snmp_user, char *ldom_name, int *iobus_cnt, iobus_t ***iobus_list); +int get_ldominfo(char *ldom_name, int *num_cpu, int *mem_size, int *mem_unit, int *num_crypto, int *num_iobus, int * console, int state); +int get_response(int sock, char **resp_buf); +int get_rp(char *snmp_user, int resource, int rp_type, ulong_t *rp_qty, int *unit); +int get_vcc(char *snmp_user, char *ldom_name, int *vcc_cnt, vcc_t ***vcc_list); +int get_vcons(char *snmp_user, char *ldom_name, int *vcons_cnt, vcons_t ***vcons_list); +int get_vconsvccrel(char *snmp_user, char *ldom_name, int *vconsvccrel_cnt, vconsvccrel_t ***vconsvccrel_list); +int get_vcpu(char *snmp_user, char *ldom_name, int *vcpu_cnt, vcpu_t ***vcpu_list); +int get_vdisk(char *snmp_user, char *ldom_name, int *vdisk_cnt, vdisk_t ***vdisk_list); +int get_vds(char *snmp_user, char *ldom_name, int *vds_cnt, vds_t ***vds_list); +int get_vdsdev(char *snmp_user, char *ldom_name, int *vdsdev_cnt, vdsdev_t ***vdsdev_list); +int get_vmem(char *snmp_user, char *ldom_name, int *vmem_cnt, vmem_data_t ***vmem_list); +int get_vmem_physbind(char *snmp_user, char *ldom_name, int *vmem_physbind_cnt, vmem_physbind_t ***vmem_physbind_list); +int get_vnet(char *snmp_user, char *ldom_name, int *vnet_cnt, vnet_t ***vnet_list); +int get_vsw(char *snmp_user, char *ldom_name, int *vsw_cnt, vsw_t ***vsw_list); + +xmlNodePtr xml_get_next_ele_node(xmlNodePtr node); +xmlNodePtr xml_find_subnode(xmlNodePtr node, const xmlChar *name); +int create_pkt_buf(char **pkt_buf, char *xml_buf, int xml_buf_len, + int msg_type); + +#ifdef __cplusplus +} +#endif + +#endif /* WITH_LDOMS */ +#endif /* __VIR_LDOMS_INTFC_H__ */ diff --git a/src/ldoms_intfc.c b/src/ldoms_intfc.c new file mode 100644 --- /dev/null +++ b/src/ldoms_intfc.c @@ -0,0 +1,1825 @@ +/* + * ldoms_intfc.c: Interface code to the LDoms Manager (LDM) + * + * Copyright 2008 Sun Microsystems, Inc. + * + * See COPYING.LIB for the License of this software + * + */ + +#ifdef WITH_LDOMS +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <netdb.h> +#include <string.h> +#include <strings.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/uio.h> +#include <fcntl.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <libintl.h> +#include <time.h> + + +#include "libvirt/libvirt.h" +#include "internal.h" +#include "ldoms_internal.h" +#include "ldoms_common.h" +#include "ldoms_xml_parse.h" +#include "ldoms_intfc.h" +#include "xml.h" + + +/* Domain state info + * LDom State enumerations + * 1 = active LDOM_STATE_ACTIVE + * 2 = stopping LDOM_STATE_STOPPING + * 3 = inactive LDOM_STATE_INACTIVE + * 4 = binding LDOM_STATE_BINDING + * 5 = unbinding LDOM_STATE_UNBINDING + * 6 = bound LDOM_STATE_BOUND + * 7 = starting LDOM_STATE_STARTING + * + * libvirt LDom State enums + * typedef enum { + * VIR_DOMAIN_NOSTATE = 0, no state + * VIR_DOMAIN_RUNNING = 1, the domain is running + * VIR_DOMAIN_BLOCKED = 2, the domain is blocked on resource + * VIR_DOMAIN_PAUSED = 3, the domain is paused by user + * VIR_DOMAIN_SHUTDOWN= 4, the domain is being shut down + * VIR_DOMAIN_SHUTOFF = 5, the domain is shut off + * VIR_DOMAIN_CRASHED = 6 the domain is crashed + * } virDomainState; + */ + +/* Global vars for debug statement */ +extern int ldoms_debug; +extern int ldoms_detailed_debug; + +/* + * open_ldm_connection + * + * This is the function that opens a client socket connection to the LDOM Manager and + * performs the initial handshake protocol with the LDOM Manager. + * + * Returns: + * socket descriptor if the operation is successful + * -1 if it fails + * + * NOTE: This routine was copied from the example code provided by the LDOM Manager developer. + * The LDOM Manager handshake protocol is defined in the ldm_protocol.h file + * in the LDOM Manager source code. Also see the create_connection() routine in ldm.c + * from the LDOM Manager source code. + */ +int +open_ldm_connection(void) +{ + struct sockaddr_in myaddr; + struct stat statbuf; + ldm_hdr_t *hdr, hdr_buf; + ldm_init_req_t *init_req; + ldm_handshake_msg *buf; + int error, sock; + uint16_t port; + + int client_class = LDM_CLASS_CONTROL; + + if (ldoms_debug) dprt("LDOMS_DEBUG: open_ldm_connection(ENTER)\n"); + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock == -1) { + if (ldoms_debug) dprt("LDOMS_DEBUG: open_ldm_connection..can not create socket\n"); + return (-1); + } + + port = DEFAULT_PORT; + + myaddr.sin_family = AF_INET; + myaddr.sin_addr.s_addr = htonl(INADDR_ANY); + myaddr.sin_port = htons(port); + error = connect(sock, (struct sockaddr *)(&myaddr), sizeof (struct sockaddr_in)); + + if (error == -1) { + if (ldoms_debug) dprt("LDOMS_DEBUG: open_ldm_connection..socket connect failed: port=%d\n",port); + return (-1); + } + + if ((buf = malloc(sizeof(ldm_handshake_msg))) == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: open_ldm_connection..malloc ldm_handshake_msg failed\n"); + return(-1); + } + + /* Header msg is always required to LDM */ + hdr = (ldm_hdr_t *)buf; + hdr->msg_type = hton32(LDM_INIT_REQ); + hdr->payload_len = sizeof (ldm_init_req_t); + + /* Specific request to LDM */ + init_req = &buf->msg; + init_req->client_class = hton32(client_class); + init_req->major_vers = hton16(LDM_CONTROL_MAJOR_VER); + init_req->minor_vers = hton16(LDM_CONTROL_MINOR_VER); + + /* Send the request to the LDM */ + error = send(sock, buf, sizeof(ldm_handshake_msg), 0); + if (error == -1) { + if (ldoms_debug) dprt("LDOMS_DEBUG: open_ldm_connection..socket send with LDM_INIT_REQ failed\n"); + return(-1); + } + + free(buf); + + /* + * Get the header part of the response from the LDM. This + * will let us know how big the message part is. + */ + error = recv(sock, &hdr_buf, sizeof (ldm_hdr_t), MSG_WAITALL); + if (error == -1) { + if (ldoms_debug) dprt("LDOMS_DEBUG: open_ldm_connection..socket recv failed\n"); + return(-1); + } + + hdr_buf.msg_type = ntoh32(hdr_buf.msg_type); + hdr_buf.payload_len = ntoh32(hdr_buf.payload_len); + + /* See what the LDM responded with for the handshake request */ + switch (hdr_buf.msg_type) { + case LDM_INIT_ACK: { + ldm_init_ack_t ack; + + /* Get the rest of the response */ + error = recv(sock, &ack, sizeof (ack), MSG_WAITALL); + if (error == -1) { + if (ldoms_debug) dprt("LDOMS_DEBUG: open_ldm_connection..LDM_INIT_ACK recv failed\n"); + return(-1); + } + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: open_ldm_connection..LDM_INIT_ACK received, Minor sent=%d, Minor rcvd=%d\n",LDM_CONTROL_MINOR_VER,ack.minor_vers); + break; + } + + case LDM_INIT_NACK: { + ldm_init_nack_t nack; + + error = recv(sock, &nack, sizeof (nack), MSG_WAITALL); + if (error == -1) { + if (ldoms_debug) dprt("LDOMS_DEBUG: open_ldm_connection..LDM_INIT_NACK recv failed\n"); + return(-1); + } + + if (ldoms_debug) dprt("LDOMS_DEBUG: open_ldm_connection..LDM_INIT_NACK received, Major Version mismatch. Mjr sent=%d, Mjr rcvd=%d\n",LDM_CONTROL_MAJOR_VER,nack.major_vers); + return(-1); + } + + case LDM_ERROR: { + ldm_error_t err; + + error = recv(sock, &err, sizeof (err), MSG_WAITALL); + if (error == -1) { + if (ldoms_debug) dprt("LDOMS_DEBUG: open_ldm_connection..LDM_ERROR recv failed\n"); + return(-1); + } + + if (ldoms_debug) dprt("LDOMS_DEBUG: open_ldm_connection..LDM_ERROR: Protocol error=%d\n",err.error_code); + + return(-1); + break; + } + + default: + if (ldoms_debug) dprt("LDOMS_DEBUG: open_ldm_connection: Unknown response type=%d\n",hdr_buf.msg_type); + return(-1); + } + + if (ldoms_debug) dprt("LDOMS_DEBUG: open_ldm_connection(ENTER)\n"); + + return (sock); +} + +/* + * close_ldm_connection + * + * This function will close the socket connection to the LDM + * + * Input: + * sock - a socket descriptor to close + */ +void +close_ldm_connection(int sock) +{ + if (ldoms_debug) dprt("LDOMS_DEBUG: close_ldm_connection(ENTER)..trying to close LDM socket=%d\n", sock); + (void)close(sock); + if (ldoms_debug) dprt("LDOMS_DEBUG: close_ldm_connection(EXIT)\n"); +} + + +/* + * get_response + * + * This function receives messages from the LDM socket connection and puts them + * into the message buffer. + * + * Input: + * sock - socket descriptor to receive messages + * + * Output: + * resp_buf - pointer to the response buffer that contains the messages + * from the socket connection. This memory must be freed by the caller. + * + * Returns: + * 0 if the operation is successful + * -1 if the operation fails + * + * NOTE: This routine was copied from the example code provided by the LDOM Manager developer. + */ +int +get_response(int sock, char **resp_buf) +{ + ldm_hdr_t hdr; + char *buf; + int status, resp_len, text_len, cc; + int i; + + if (ldoms_debug) dprt("LDOMS_DEBUG: get_response(ENTER)\n"); + + /* Receive the Header of the response */ + status = recv(sock, &hdr, sizeof (ldm_hdr_t), MSG_WAITALL); + if (status <= 0) { + if (ldoms_debug) dprt("LDOMS_DEBUG: get_response..recv failed for rsp header\n"); + /* error or LDom manager exited */ + return(-1); + } + + hdr.msg_type = ntoh32(hdr.msg_type); + + if (hdr.msg_type != LDM_CNTRL_XML_RESP) { + if (ldoms_debug) dprt("LDOMS_DEBUG: get_response..Unexpected response message type=%d\n",hdr.msg_type); + return(-1); + } + + hdr.payload_len = ntoh32(hdr.payload_len); + + *resp_buf = malloc(hdr.payload_len); + + if (*resp_buf == NULL) + return(-1); + + /* receive the actual response body from LDM */ + status = recv(sock, *resp_buf, hdr.payload_len, MSG_WAITALL); + if (status == -1) { + if (ldoms_debug) dprt("LDOMS_DEBUG: get_response..recv failed for rsp body\n"); + free(resp_buf); + return(-1); + } + + if (ldoms_debug) dprt("LDOMS_DEBUG: get_response(EXIT)..received %d bytes for rsp body\n", status); + + return (0); +} /* get_response() */ + +/* + * handle_resp + * + * This function converts the message buffer (received from the socket connection) + * into an xml document. + * + * Input: + * resp_buf - pointer to the message buffer received from the socket connection + * + * Returns: + * xml doc pointer if the operation is successful + * NULL if the operation fails + * + * NOTE: This routine was copied from the example code provided by the LDOM Manager developer. + */ +xmlDocPtr +handle_resp(char *resp_buf) +{ + xmlDocPtr xml_output; + ldm_op_req_t *req; + xml_msg_t *xml_msg; + char * dptr; + + if (ldoms_debug) dprt("LDOMS_DEBUG: handle_resp(ENTER)\n"); + + req = (ldm_op_req_t *)resp_buf; + xml_msg = (xml_msg_t *)req->rq_data; + + + xml_output = xmlParseMemory((const char *)req->rq_data, req->rq_len); + + if (xml_output == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: handle_resp.. xml output is NULL\n"); + } + + if (ldoms_debug) dprt("LDOMS_DEBUG: handle_resp(EXIT)\n"); + + return (xml_output); +} + +/* + * send_xml_file_to_ldm + * + * This function sends an XML request to LDM and then receives the XML + * response. It packages the XML response into an XML document so other + * code can search through it. + * + * Input: + * doc - xml doc to send to the LDOM Manager + * + * Returns: + * xml doc pointer of the response xml file received from the LDOM Manager + * if the operation is successful. + * NULL if the operation fails + * + */ +xmlDocPtr +send_xml_file_to_ldm(xmlDoc *doc) +{ + char *pkt_buf; + char *resp_buf; + char *xml_buf; + int status; + int pkt_buf_len; + int xml_buf_len; + int ret; + int sock; + xmlDoc *doc_from_ldm; + + /* connect to the ldom manager */ + if (ldoms_debug) dprt("LDOMS_DEBUG: send_xml_file_to_ldm(ENTER)\n"); + sock = open_ldm_connection(); + + if (sock == -1) { + if (ldoms_debug) dprt("LDOMS_DEBUG: send_xml_file_to_ldm.. error in opening socket connection\n"); + return(NULL); + } + + (void) xmlDocDumpMemory(doc, (xmlChar **)&xml_buf, &xml_buf_len); + + pkt_buf_len = ldm_create_pkt_buf(&pkt_buf, xml_buf, xml_buf_len); + + status = send(sock, pkt_buf, pkt_buf_len, 0); + if (status == -1) { + if (ldoms_debug) dprt("LDOMS_DEBUG: send_xml_file_to_ldm.. error in send\n"); + close_ldm_connection(sock); + return(NULL); + } + + free(xml_buf); + free(pkt_buf); + + if (get_response(sock, &resp_buf) == 0) + doc_from_ldm = handle_resp(resp_buf); + else { + if (ldoms_debug) dprt("LDOMS_DEBUG: send_xml_file_to_ldm.. error in get_response\n"); + close_ldm_connection(sock); + return(NULL); + } + + free(resp_buf); + + /* close the socket connection */ + close_ldm_connection(sock); + + if (ldoms_debug) dprt("LDOMS_DEBUG: send_xml_file_to_ldm(EXIT)\n"); + + return (doc_from_ldm); +} /* send_xml_file_to_ldm() */ + +/* + * ldm_create_pkt_buf + * + * This function creates a ldom operation request message buffer with an XML + * character buffer array. This functions sets the appropriate protocol headers + * for the protocol handshake with the LDOM Manager. + * + * Input: + * xml_buf - pointer to the xml character buffer + * xml_buf_len - length of the xml character buffer + * + * Output: + * pkt_buf - pointer to the message buffer to send to the socket connection + * + * Returns: + * buffer length for the message buffer + * + * NOTE: This routine was copied from the example code provided by the LDOM Manager developer. + * Also see the create_pkt_buf() routine in xml_common.c from the LDOM Manager source code. + */ +int +ldm_create_pkt_buf(char **pkt_buf, char *xml_buf, int xml_buf_len) +{ + char *temp_buf; + int i; + int pkt_buf_len; + ldm_hdr_t *hdr; + ldm_op_req_t *req; + xml_msg_t *xml_msg; + + if (ldoms_debug) dprt("LDOMS_DEBUG: create_pkt_buf(ENTER)\n"); + + pkt_buf_len = sizeof (ldm_hdr_t) + sizeof (ldm_op_req_t) + xml_buf_len; + + *pkt_buf = malloc(pkt_buf_len); + + /* set LDM protocol headers */ + hdr = (ldm_hdr_t *)*pkt_buf; + hdr->msg_type = hton32(LDM_CNTRL_XML_REQ); + hdr->payload_len = hton32(sizeof (ldm_op_req_t) + xml_buf_len); + + req = (ldm_op_req_t *)(*pkt_buf + sizeof (ldm_hdr_t)); + req->rq_len = hton32(xml_buf_len); + req->rq_id = 1; + + memcpy(req->rq_data, xml_buf, xml_buf_len); + + if (ldoms_debug) dprt("LDOMS_DEBUG: create_pkt_buf(EXIT)\n"); + + return (pkt_buf_len); + +} /* ldm_create_pkt_buf() */ + + +/* + * get_ldominfo + * + * This function gets the ldom info such as the number of virtial cpu, memory and + * its unit, crypto, iobus and console port for the given ldom by invoking routines + * to create and send the "list-constraints" and "list-bindings" XML request to the + * LDOM Manager and parse the response. + * + * XML output from the LDOM Manager. + * + * Input: + * ldom_name - name of the ldom to retrieve the ldom info + * state - State of the ldom + * + * Output (pointers are used to pass information from this function to the caller): + * num_cpu - pointer to the number of virtual cpu + * mem_size - pointer to the virtual memory size + * mem_unit - pointer to the virtual memory unit + * num_crypto - pointer to the number of crypto unit + * num_iobus - pointer to the number of io bus + * console - pointer to the console port + * + * Returns: + * 0 if the operation is successful + * -1 if the operation fails + * + */ +int +get_ldominfo(char *ldom_name, int *num_cpu, int *mem_size, int *mem_unit, + int *num_crypto, int *num_iobus, int *console, int state) +{ + xmlDocPtr xml_output; + xmlDocPtr xml_received; + xmlChar *action = XML_LIST_CONST; + xmlChar *bindAction = XML_LIST_BIND; + int ret; + + int num_cpu_from_xml = 0; + int mem_size_from_xml = 0; + int mem_unit_from_xml = 0; + int num_crypto_from_xml = 0; + int num_iobus_from_xml = 0; + int console_from_xml = 0; + + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldominfo(ENTER) ldom_name=%s\n", ldom_name); + + *num_cpu = 0; + *mem_size = 0; + *mem_unit = 0; + *num_crypto = 0; + *num_iobus = 0; + *console = 0; + + /* + * The following "list-constraints" XML request will be created and sent to the LDOM Manager + * NOTE: list-constraints will not get the Console port. We have to + * use list-bindings for that. + * + * if the input ldom name is 'primary': + * + * <?xml version="1.0"?> + * <LDM_interface version="1.0"> + * <cmd> + * <action>list-constraints</action> + * <data version="2.0"> + * <ldom> + * <ldom_info> + * <ldom_name>primary</ldom_name> + * </ldom_info> + * </ldom> + * </data> + * </cmd> + * </LDM_interface> + */ + xml_output = create_xml_file_4_ldom_action(ldom_name, action); + if (xml_output == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldominfo.. failed to create xml file for ldom_name=%s action=%s\n", + ldom_name, action); + return (-1); + } + + /* send XML file to the ldom manager */ + xml_received = send_xml_file_to_ldm(xml_output); + if (xml_received == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldominfo.. failed to send xml file to ldm and receive xml back\n"); + xmlFreeDoc(xml_output); + return (-1); + } + + /* + * Example of the xml file received from the LDOM Manager: + * + * <?xml version="1.0"?> + * <LDM_interface version="1.0"> + * <cmd> + * <action>list-constraints</action> + * <data version="2.0"> + * <ldom> + * <ldom_info> + * <ldom_name>primary</ldom_name> + * </ldom_info> + * <cpu> + * <number>4</number> + * </cpu> + * <mau> + * <number>1</number> + * </mau> + * <memory> + * <size>1G</size> + * </memory> + * ..... + */ + /* parse the received XML file to get the ldom info */ + + /* + * <response><status> will be failure if the ldom is not active or bound. + * then, use zero for num of cpu and mem size + */ + ret = parse_xml_get_ldominfo(xml_received, &num_cpu_from_xml, &mem_size_from_xml, + &mem_unit_from_xml, &num_crypto_from_xml, &num_iobus_from_xml); + if (ret == -1) { + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldominfo.. failed to parse xml file\n"); + return (-1); + } + + /* Update the return parms */ + *num_cpu = num_cpu_from_xml; + *mem_size = mem_size_from_xml; + *mem_unit = mem_unit_from_xml; + *num_crypto = num_crypto_from_xml; + *num_iobus = num_iobus_from_xml; + + /* + * Now we have to get the console port for each LDom. This is done by + * sending a 'list-bindings' XML command. So this means we can only + * send this request for domains that are bound or active. You cannot + * do a list-bindings on an inactive domain. If the domain is inactive, + * then the console port is set to 0. + * + * Example: + * If the input ldom name is 'primary', the XML request is: + * + * <?xml version="1.0"?> + * <LDM_interface version="1.0"> + * <cmd> + * <action>list-bindings</action> + * <data version="2.0"> + * <ldom> + * <ldom_info> + * <ldom_name>primary</ldom_name> + * </ldom_info> + * </ldom> + * </data> + * </cmd> + * </LDM_interface> + */ + + /* Domain state is inactive (3), so list-bindings is invalid */ + if (state == LDOM_STATE_INACTIVE) + return (0); + + xml_output = create_xml_file_4_ldom_action(ldom_name, bindAction); + if (xml_output == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldominfo.. failed to create xml file for ldom_name=%s action=%s\n", + ldom_name, action); + return (-1); + } + + /* send XML file to the ldom manager */ + xml_received = send_xml_file_to_ldm(xml_output); + if (xml_received == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldominfo.. failed to send xml file to ldm and receive xml back for Ldom %s, action %s\n", ldom_name, action); + xmlFreeDoc(xml_output); + return (-1); + } + + /* + * Example of the xml file received from the LDOM Manager: + * + * <?xml version="1.0"?> + * <LDM_interface version="1.0"> + * <cmd> + * <action>list-constraints</action> + * <data version="2.0"> + * <ldom> + * <ldom_info> + * <ldom_name>ldom-126</ldom_name> + * <mac_address>00:14:4f:f9:1a:7d</mac_address> + * </ldom_info> + * ..... + * <console> + * <service_name>primary-vcc0</service_name> + * <port>5005</port> + * </console> + * ..... + * </ldom> + * <response> + * <status>success</status> + * </response> + * </data> + * <response> + * <status>success</status> + * </response> + * </cmd> + * <response> + * <status>success</status> + * </response> + * </LDM_interface> + */ + + /* parse the received XML file to get the ldom info */ + + ret = parse_xml_get_console(xml_received, &console_from_xml); + if (ret == -1) { + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldominfo() failed to parse xml file for action %s\n", bindAction); + return (-1); + } + /* Update the return parms */ + *console = console_from_xml; + + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldominfo(EXIT)\n"); + + return(0); +} + +/* +* get_ldom_names +* +* This function creates a list of all ldoms with the ldom name and state data +* by invoking routines to create and send the "list-domain" XML request +* to the LDOM Manager and parse the response XML output from the LDOM Manager. +* +* For each LDom returned from the list-domain action, get_ldominfo() is called +* to get the specific LDom info. +* +* Input: +* +* Output (pointers are used to pass information from this function to the caller): +* ldom_cnt - pointer to the number of ldoms in the ldom info list +* ldominfo_list - pointer to the list of pointers to the ldom info array +* +* Returns: +* 0 if the operation is successful +* -1 if the operation fails +* +* NOTE: The following pictorial description explains the data structure used in +* this function to pass the ldom count and the list of pointers to the ldom info array. +* The definition of the data structures is implemented in the ldm_xml_parse.h file. +* Most get_<data> routines in this file is using the similar data structures +* to pass the data. +* +* +-------------+ +* | num_ldoms | <--- *ldom_cnt = pointer to the integer that has the value of +* +-------------+ the number of ldoms +* +* +-------------+ +* | ldom_list + <--- ***ldominfo_list = pointer to the list of pointers to +* +-------------+ the ldom info array of type ldominfo_t +* of which is defined in ldm_xml_parse.h file +* ldom_list[0] +* | +* | type ldominfo_t +* | +-------------+---------------+------------+ +* |---> | ldom name | ldom state | ...... | +* +------------------------------------------+ +* +* ldom_list[1] +* | +* | type ldominfo_t +* | +-------------+---------------+------------+ +* |---> | ldom name | ldom state | ...... | +* +------------------------------------------+ +* +*/ +int +get_ldom_names(int *ldom_cnt, ldominfo_t ***ldominfolist) +{ + ldominfo_t **ldom_list = NULL; + xmlDocPtr xml_to_send; + xmlDocPtr xml_received; + xmlNodePtr root_node = NULL; + xmlNodePtr cmd_node = NULL; + xmlNodePtr data_node = NULL; + xmlNodePtr ldom_node = NULL; + xmlNodePtr name_node = NULL; + xmlNodePtr subnode = NULL; + xmlChar *ldom_name = NULL; + xmlChar *action = XML_LIST; + + int num_ldoms = 0; + int i = 0, idx = 0; + int nameLen; + int ldomCpus, ldomMem, memUnit, ldomCrypto, ldomIO, ldomConsole; + char ldomName[NAME_SIZE]; + char multChr; + + + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldom_names(ENTER) i=%d\n",i); + + /* + * This function can be called many times for a single Virt Mgr + * operation. In order to not have to devise a grand scheme to + * determine when certain LDom info is valid or not, we will + * always free the existing structure and then generate new + * ones with current data from LDM. This also allows us to + * have the latest data from LDM. + */ + if (*ldominfolist != NULL ) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: get_ldom_names.. freeing ldominfolist\n"); + for (i=0; i < *ldom_cnt; i++) { + free((*ldominfolist)[i]); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: get_ldom_names.. freed *ldominfolist[%d]\n",i); + } + free(*ldominfolist); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: get_ldom_names.. freed *ldominfolist\n"); + } + + /* Initialize return parameters */ + *ldom_cnt = 0; + *ldominfolist = NULL; + + /* + * The following "list-domain" XML request will be created and sent to the LDOM Manager: + * + * <?xml version="1.0"?> + * <LDM_interface version="1.0"> + * <cmd> + * <action>list-domain</action> + * <data version="2.0"> + * </data> + * </cmd> + * </LDM_interface> + */ + xml_to_send = create_xml_file_4_ldom_action(NULL, action); + if (xml_to_send == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldom_names.. can not create list xml file\n"); + return (-1); + } + + xml_received = send_xml_file_to_ldm(xml_to_send); + if (xml_received == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldom_names.. failed to send xml file to ldm and receive xml response\n"); + xmlFreeDoc(xml_to_send); + return (-1); + } + + /* + * Example of the xml file received from the LDOM Manager: + * + * <?xml version="1.0"?> + * <LDM_interface version="1.0"> + * <cmd> + * <action>list-domain</action> + * <data version="2.0"> + * <ldom> + * <ldom_info> + * <ldom_name>primary</ldom_name> + * <state>active</state> + * </ldom_info> + * </ldom> + * <response> + * <status>success</status> + * </response> + * </data> + * <response> + * <status>success</status> + * </response> + * </cmd> + * <response> + * <status>success</status> + * </response> + * </LDM_interface> + */ + + /* Get the total # of LDoms first */ + if (parse_xml_get_ldom_cnt(xml_received, &num_ldoms) == -1) { + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldom_names.. failed to parse xml file for ldom cnt=%s\n", xml_received); + xmlFreeDoc(xml_to_send); + xmlFreeDoc(xml_received); + return (-1); + } + + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: get_ldom_names.. number of ldoms=%d \n", num_ldoms); + + /* Allocate the memory to hold the info for each LDom */ + if (num_ldoms > 0) + ldom_list = calloc(num_ldoms, sizeof(ldominfo_t *)); + + if (ldom_list == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldom_names.. can't alloc memory for ldom_list\n"); + xmlFreeDoc(xml_to_send); + xmlFreeDoc(xml_received); + return (-1); + } + + /* Make sure mandantory XML tags are present before getting LDom data */ + root_node = xmlDocGetRootElement(xml_received); + if (root_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldom_names.. XML file does not have root node\n"); + free(ldom_list); + xmlFreeDoc(xml_to_send); + xmlFreeDoc(xml_received); + return (-1); + } + cmd_node = xml_find_subnode(root_node, XML_CMD); + if (cmd_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldom_names.. XML file does not have <cmd> tag\n"); + free(ldom_list); + xmlFreeDoc(xml_to_send); + xmlFreeDoc(xml_received); + return (-1); + } + + data_node = xml_find_subnode(cmd_node, XML_DATA); + if (data_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldom_names.. XML file does not have <data> tag\n"); + free(ldom_list); + xmlFreeDoc(xml_to_send); + xmlFreeDoc(xml_received); + return (-1); + } + + /* + * The response XML has data for all LDoms and their state. As we step thru + * the response for each LDom, we have to get more detailed info for the LDom, + * thus will send other XML requests for this data. + */ + while (1) { + + /* get the ldom node within the <data> tag */ + ldom_node = xml_find_subnode(data_node, LDOM_NODE); + + + /* Need a <ldom> tag */ + if (ldom_node != NULL) { + subnode = ldom_node->xmlChildrenNode; + + while (subnode != NULL) { + /* Skip tags that are not element tags (tags with no data) */ + if (subnode->type != XML_ELEMENT_NODE) { + subnode = subnode->next; + continue; + } + + /* Look for <ldom_info> tag */ + if (xmlStrcmp(subnode->name, (const xmlChar *)XML_LDM_INFO) == 0) { + /* Need a <ldom_name> tag */ + name_node = xml_find_subnode(subnode, XML_LDM_NAME); + + /* We did not find a <ldom_name> tag. No work can be done without this */ + if (name_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldom_names.. XML file does not have <ldom_name> tag\n"); + xmlFreeDoc(xml_to_send); + xmlFreeDoc(xml_received); + /* Should free the ldominto_t and ldominfo_list memory also, but this should NEVER happen */ + return (-1); + } + + /* Error checking needed here on ldom_name and ldom_list[idx] */ + if ((ldom_name = xmlNodeGetContent(name_node)) == NULL) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: get_ldom_names..xmlNodeGetContent() is NULL\n"); + subnode = subnode->next; + continue; + } + + if ((ldom_list[idx] = malloc(sizeof(ldominfo_t))) == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldom_names..malloc() failed for ldom_list[idx]\n"); + subnode = subnode->next; + xmlFree(ldom_name); + continue; + } + + /* Save the LDom name and state */ + strlcpy(ldom_list[idx]->ldomName, (char *)ldom_name, sizeof (ldom_list[idx]->ldomName)); + ldom_list[idx]->ldomState = parse_xml_get_ldom_state(subnode); + xmlFree(ldom_name); + + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: get_ldom_names..ldom name:state=%s:%d; ldom_list[%d]=%u\n", + ldom_list[idx]->ldomName, ldom_list[idx]->ldomState,idx,ldom_list[idx]); + + /* Now we need to get some specific data about the domain */ + if (get_ldominfo(ldom_list[idx]->ldomName, &ldomCpus, + &ldomMem, &memUnit, &ldomCrypto, &ldomIO, + &ldomConsole, ldom_list[idx]->ldomState) >= 0) + { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: get_ldom_names..get_ldominfo() cpus=%d, mem=%d, unit=%d, mau=%d, io=%d, console=%d\n", + ldomCpus, ldomMem, memUnit, ldomCrypto, ldomIO, ldomConsole); + + /* The VMM GUI displays the memory in KB, so we need to convert to KB */ + switch (memUnit) { + case LDOMMEMUNIT_KILOBYTES: break; /* KB already */ + case LDOMMEMUNIT_MEGABYTES: ldomMem *= 1024; break; /* MB */ + case LDOMMEMUNIT_GIGABYTES: ldomMem *= (1024 * 1024); break;/* GB */ + case LDOMMEMUNIT_BYTES: ldomMem /= 1024; break; /* Bytes */ + } + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: get_ldom_names..get_ldominfo() mem=%d\n",ldomMem); + + /* Now put the LDom into into the current list */ + ldom_list[idx]->ldomNumVCpu = ldomCpus; + ldom_list[idx]->ldomMemSize = ldomMem; + ldom_list[idx]->ldomMemUnit = memUnit; + ldom_list[idx]->ldomNumCrypto = ldomCrypto; + ldom_list[idx]->ldomNumIOBus = ldomIO; + ldom_list[idx]->ldomConsole = ldomConsole; + + /* Create the UUID for this domain/index. We want to keep the + * UUID constant, no matter how many domains are addeded or + * or deleted, so we will use the integer values for each + * char in the domain name in the calculation. + */ + strcpy(ldomName, ldom_list[idx]->ldomName); + nameLen = strlen(ldom_list[idx]->ldomName); + for (i = 0 ; i < VIR_UUID_BUFLEN ; i++) { + if (i < nameLen) + multChr = ldomName[i]; + + ldom_list[idx]->uuid[i] = ((i+1) * (76 + multChr))%255; + } + } + else { + /* Now put the LDom into into the current list */ + ldom_list[idx]->ldomNumVCpu = 0;; + ldom_list[idx]->ldomMemSize = 0; + ldom_list[idx]->ldomMemUnit = 4; + ldom_list[idx]->ldomNumCrypto = 0; + ldom_list[idx]->ldomNumIOBus = 0; + ldom_list[idx]->ldomConsole = 0; + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldom_names() get_ldominfo() failed\n"); + strcpy(ldomName, ldom_list[idx]->ldomName); + nameLen = strlen(ldom_list[idx]->ldomName); + for (i = 0 ; i < VIR_UUID_BUFLEN ; i++) { + if (i < nameLen) + multChr = ldomName[i]; + + ldom_list[idx]->uuid[i] = ((i+1) * (76 + multChr))%255; + } + } + + idx++; + subnode = subnode->next; + continue; + } + subnode = subnode->next; + } + } + + /* + * xml response for list-domain has <data> tags for each ldom info + * so, get the next data section + */ + data_node = xml_get_next_ele_node(data_node); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: get_ldom_names..looking for next <data> node\n"); + + if (data_node == NULL) + break; + } + + *ldom_cnt = num_ldoms; + *ldominfolist = ldom_list; + for (i=0; i<*ldom_cnt; i++) + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: get_ldom_names: ldom_list[%d]=%d\n",i,ldom_list[i]); + + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: get_ldom_names: ldominfolist=%u\n",ldominfolist); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: get_ldom_names: *ldominfolist=%u\n",*ldominfolist); + for (i=0; i<*ldom_cnt; i++) + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: get_ldom_names: (*ldominfolist)[%d]=%u\n",i,(*ldominfolist)[i]); + + xmlFreeDoc(xml_to_send); + xmlFreeDoc(xml_received); + + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldom_names(EXIT) ldom_cnt=%d\n",*ldom_cnt); + + return (0); +} /* get_ldom_names() */ + +/* + * send_ldom_set_vcpu + * + * This function sends an LDom XML xxxxxx request to the LDM to change the + * number of virtual cpus for the input domain. + * + * Input: + * ldom_name - name of the ldom to start or stop + * nvcpus - The new number of virtual cpus for the domain + * + * Returns: + * 0 if the operation is successful + * -1 if the operation fails + * + */ +int +send_ldom_set_vcpu(char *ldom_name, int nvcpus) +{ + xmlDocPtr xml_output; + xmlDocPtr xml_received; + xmlChar *action; + int ret; + xmlNodePtr root_node = NULL; + + if (ldoms_debug) dprt("LDOMS_DEBUG: send_ldom_set_vcpu(ENTER): ldom_name=%s, nvcpus=%d\n", ldom_name, nvcpus); + + /* create XML file to send request to the ldom manager */ + xml_output = create_xml_file_4_set_vcpu(ldom_name, nvcpus); + if (xml_output == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: send_ldom_set_vcpu.. failed to create xml file for set vcpus\n"); + return (-1); + } + + /* send XML file to the LDM */ + xml_received = send_xml_file_to_ldm(xml_output); + if (xml_received == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: send_ldom_set_vcpu.. failed to send xml file to ldm and receive xml back\n"); + xmlFreeDoc(xml_output); + return (-1); + } + + /* check the response status */ + root_node = xmlDocGetRootElement(xml_received); + if (root_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: send_ldom_set_vcpu.. XML file does not have root node\n"); + xmlFreeDoc(xml_output); + xmlFreeDoc(xml_received); + return(-1); + } + + if (parse_xml_get_response_status(root_node) == -1) { + if (ldoms_debug) dprt("LDOMS_DEBUG: send_ldom_set_vcpu.. failure response from received xml\n"); + xmlFreeDoc(xml_output); + xmlFreeDoc(xml_received); + return(-1); + } + + /* cleanup */ + xmlFreeDoc(xml_output); + xmlFreeDoc(xml_received); + + if (ldoms_debug) dprt("LDOMS_DEBUG: send_ldom_set_vcpu(EXIT)\n"); + return(0); +} + +/* + * send_ldom_set_memory + * + * This function sends an LDom XML set-memory request to the LDM to change the + * virtual memory for the input domain. The value of memory is in KB. + * + * Input: + * ldom_name - name of the ldom to change the memory + * memory - The new memory for the domain + * + * Returns: + * 0 if the operation is successful + * -1 if the operation fails + * + */ +int +send_ldom_set_memory(char *ldom_name, unsigned long memory) +{ + xmlDocPtr xml_output; + xmlDocPtr xml_received; + xmlChar *action; + int ret; + xmlNodePtr root_node = NULL; + + if (ldoms_debug) dprt("LDOMS_DEBUG: send_ldom_set_memory(ENTER): ldom_name=%s, memory=%lu\n", ldom_name, memory); + + /* create XML file to send request to the ldom manager */ + xml_output = create_xml_file_4_set_memory(ldom_name, memory); + if (xml_output == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: send_ldom_set_memory.. failed to create xml file for set memory\n"); + return (-1); + } + + /* send XML file to the LDM */ + xml_received = send_xml_file_to_ldm(xml_output); + if (xml_received == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: send_ldom_set_memory.. failed to send xml file to ldm and receive xml back\n"); + xmlFreeDoc(xml_output); + return (-1); + } + + /* check the response status */ + root_node = xmlDocGetRootElement(xml_received); + if (root_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: send_ldom_set_memory.. XML file does not have root node\n"); + xmlFreeDoc(xml_output); + xmlFreeDoc(xml_received); + return(-1); + } + + if (parse_xml_get_response_status(root_node) == -1) { + if (ldoms_debug) dprt("LDOMS_DEBUG: send_ldom_set_memory.. failure response from received xml\n"); + xmlFreeDoc(xml_output); + xmlFreeDoc(xml_received); + return(-1); + } + + /* cleanup */ + xmlFreeDoc(xml_output); + xmlFreeDoc(xml_received); + + if (ldoms_debug) dprt("LDOMS_DEBUG: send_ldom_set_memory(EXIT)\n"); + return(0); +} + +/* + * send_ldom_create_domain + * + * This function sends an LDom lifecycle action to the LDM to create a + * domain. The new domain will be left in either the inactive or bound + * state. The domain is created from the definition of an input XML file. + * The structure of the XML file is that of 'ldm list-constraints -x <ldom>' + * + * Input: + * xml - A char buffer of the input xml file (This is not an xmlDocPtr yet) + * action - The Lifecycle action to perform after domain creation + * + * Returns: + * Ptr to the Domain name if the operation is successful + * NULL - if the operation fails + * + */ +char * +send_ldom_create_domain(char *xml, xmlChar *action) +{ + xmlDocPtr xml_request; + xmlDocPtr xml_response; + xmlNodePtr root_node = NULL; + int ret; + char *ldomName; + char *filerc; + char fname[64]; + char xmlBuf[256]; + char tags[64]; + char *newXml,*modXml; + time_t timesecs; + FILE *fptr; + size_t fwrc; + + if (ldoms_debug) dprt("LDOMS_DEBUG: send_ldom_create_domain(ENTER)\n"); + + /* + * The input XML char buffer should be the output of list-constrtains -x + * It can be modified for the constraints, but will not have the + * <cmd> or <action> tags. These tags have to be added to that XML + * file before sending it to LDM. + * + * The input XML char buffer is written to a file, then that file is read + * one line at a time so we can determine when to insert the new tags. + * The lines read in are accumulated in memory in a char buffer, that + * will get converted into an XmlDoc. + */ + + /* Create a unique file name for the temp file */ + timesecs = time(NULL); + sprintf(fname,"/tmp/xmlfile-%d",timesecs); + + /* Write the XML char buffer to the file */ + fptr = fopen(fname, "w"); + if (fptr == NULL) { + ldomsError(NULL, NULL, VIR_ERR_OPEN_FAILED, fname, VIR_ERR_ERROR); + if (ldoms_debug) dprt("LDOMS_DEBUG: send_ldom_create_domain() fopen(%s) for write failed\n", fname); + return(NULL); + } + fwrc = fwrite(xml, strlen(xml), 1, fptr); + fclose(fptr); + + /* Read in the XML file and insert the <cmd> and <action> tags */ + fptr = fopen(fname, "r"); + if (fptr == NULL) { + ldomsError(NULL, NULL, VIR_ERR_READ_FAILED, fname, VIR_ERR_ERROR); + if (ldoms_debug) dprt("LDOMS_DEBUG: send_ldom_create_domain() fopen(%s) for read failed\n", fname); + return(NULL); + } + + /* Can't malloc the space for the XML doc + new tags */ + if (!(modXml = malloc(strlen(xml) + 128))) { + ldomsError(NULL, NULL, VIR_ERR_NO_MEMORY, (char*)action, VIR_ERR_ERROR); + if (ldoms_debug) dprt("LDOMS_DEBUG: send_ldom_create_domain() malloc failed\n", fname); + fclose(fptr); + remove(fname); + return(NULL); + } + /* Save the poiner to the beginning of the in memory XML doc */ + bzero(modXml, strlen(xml) + 128); + newXml = modXml; + + /* Get the 1st line of the XML file */ + filerc = fgets(xmlBuf, sizeof(xmlBuf), fptr); + + /* Read in the XML file, appending to the in memory XML */ + while (filerc != NULL) { + memcpy(modXml, xmlBuf, strlen(xmlBuf)); + modXml = modXml + strlen(xmlBuf); + + /* The 1st set of tags, <cmd><action>, go after <LDM_interface> */ + if (strstr(xmlBuf, (char*)XML_LDM_INTERFACE) != NULL) { + sprintf(tags, "<%s><%s>%s</%s>", + XML_CMD, XML_ACTION, action, XML_ACTION); + memcpy(modXml, tags, strlen(tags)); + modXml = modXml + strlen(tags); + + filerc = fgets(xmlBuf, sizeof(xmlBuf), fptr); + /* Go to another while loop to find the end tags */ + break; + } + filerc = fgets(xmlBuf, sizeof(xmlBuf), fptr); + } + + /* Finish reading and appending until the end tag is found so we can add </cmd> */ + while (filerc != NULL) { + /* </cmd> goes before </LDM_interface> */ + if (strstr(xmlBuf, (char*)XML_LDM_INTERFACE) != NULL) { + sprintf(tags, "</%s>", XML_CMD); + memcpy(modXml, tags, strlen(tags)); + modXml = modXml + strlen(tags); + } + memcpy(modXml, xmlBuf, strlen(xmlBuf)); + modXml = modXml + strlen(xmlBuf); + filerc = fgets(xmlBuf, sizeof(xmlBuf), fptr); + } + fclose(fptr); + modXml++; *modXml = '\0'; + + /* create XML file to send request to the ldom manager */ + xml_request = xmlReadDoc((const xmlChar *) newXml, NULL, NULL, 0); + + if (xml_request == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: send_ldom_create_domain(EXIT) Failed to create xml file \n"); + remove(fname); + return (NULL); + } + + /* send XML file to the LDM */ + xml_response = send_xml_file_to_ldm(xml_request); + if (xml_response == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: send_ldom_create_domain(EXIT) Failed to send xml file to ldm and receive xml back\n"); + xmlFreeDoc(xml_request); + remove(fname); + return (NULL); + } + + /* Check the response status to make the the request was successful. + * An invalid request can return an XML document. + */ + root_node = xmlDocGetRootElement(xml_response); + if (root_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: send_ldom_create_domain.. XML file does not have root node\n"); + xmlFreeDoc(xml_request); + xmlFreeDoc(xml_response); + remove(fname); + return(NULL); + } + + if (parse_xml_get_response_status(root_node) == -1) { + if (ldoms_debug) dprt("LDOMS_DEBUG: send_ldom_create_domain(EXIT) Failure response from received xml\n"); + xmlFreeDoc(xml_request); + xmlFreeDoc(xml_response); + remove(fname); + return(NULL); + } + + /* Get the Domain name from the request */ + ldomName = parse_xml_get_ldom_name(xmlDocGetRootElement(xml_request)); + + /* cleanup */ + xmlFreeDoc(xml_request); + xmlFreeDoc(xml_response); + remove(fname); + + if (ldoms_debug) dprt("LDOMS_DEBUG: send_ldom_create_domain(EXIT)\n"); + + return(ldomName); +} + +/* + * send_ldom_lifecycle_action + * + * This function sends an LDom lifecycle action to the LDM to change the state + * of an LDom. Valid actions are: start-domain, stop-domain, bind-domain, + * unbind-domain, destroy-domain. + * + * Input: + * ldom_name - name of the ldom to start or stop + * ldom_action - Life cycle action. See ldoms_common.h + * + * Returns: + * 0 if the operation is successful + * -1 if the operation fails + * + */ +int +send_ldom_lifecycle_action(char *ldom_name, int ldom_action) +{ + xmlDocPtr xml_output; + xmlDocPtr xml_received; + xmlChar *action; + int ret; + xmlNodePtr root_node = NULL; + + if (ldoms_debug) dprt("LDOMS_DEBUG: send_ldom_lifecycle_action(ENTER): ldom_name=%s, action=%d\n", ldom_name, ldom_action); + + if (ldom_action == LDOM_START) + action = XML_START_DOMAIN; + else if (ldom_action == LDOM_STOP) + action = XML_STOP; + else if (ldom_action == LDOM_BIND) + action = XML_BIND_DOMAIN; + else if (ldom_action == LDOM_UNBIND) + action = XML_UNBIND; + else if (ldom_action == LDOM_DELETE) + action = XML_DELETE; + else { + if (ldoms_debug) dprt("LDOMS_DEBUG: send_ldom_lifecycle_action(EXIT): unsupported lifecycle action %d\n",ldom_action); + return(-1); + } + + /* create XML file to send request to the ldom manager */ + xml_output = create_xml_file_4_ldom_action(ldom_name, action); + if (xml_output == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: send_ldom_active_mgmt.. failed to create xml file for ldom_name=%s action=%s\n", + ldom_name, action); + return (-1); + } + + /* send XML file to the LDM */ + xml_received = send_xml_file_to_ldm(xml_output); + if (xml_received == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: send_ldom_active_mgmt.. failed to send xml file to ldm and receive xml back\n"); + xmlFreeDoc(xml_output); + return (-1); + } + + /* check the response status */ + root_node = xmlDocGetRootElement(xml_received); + if (root_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: send_ldom_active_mgmt.. XML file does not have root node\n"); + xmlFreeDoc(xml_output); + xmlFreeDoc(xml_received); + return(-1); + } + + if (parse_xml_get_response_status(root_node) == -1) { + if (ldoms_debug) dprt("LDOMS_DEBUG: send_ldom_active_mgmt.. failure response from received xml\n"); + xmlFreeDoc(xml_output); + xmlFreeDoc(xml_received); + return(-1); + } + + /* cleanup */ + xmlFreeDoc(xml_output); + xmlFreeDoc(xml_received); + + if (ldoms_debug) dprt("LDOMS_DEBUG: send_ldom_lifecycle_action(EXIT)\n"); + + return(0); +} + + +/* + * get_ldom_rsc_pool + * + * This function gets the resource pool info such as the CPU and memory + * capacity or reserved data by invoking routines to create and send + * the "list-devices" (for capacity data) or "list-bindings" (for reserved data) + * XML requests to the LDOM Manager and parse the response XML output from the LDOM Manager. + * + * Input: + * resource - indicates which resource to retrieve the resource pool data + * CPU_RP=CPU resource pool + * MEM_RP = memory resource pool + * rp_type - indicates either capacity or reserved + * RP_CAPACITY = capacity resource + * RP_RESERVED = reserved resource + * + * Output (pointers are used to pass information from this function to the caller): + * rp_qty - pointer to the integer for the resource pool quantity + * unit - pointer to the allocation unit for CPU or memory resources + * + * Returns: + * 0 if the operation is successful + * -1 if the operation fails + * + */ +int +get_ldom_rsc_pool(int resource, int rp_type, ulong_t *rp_qty, int *unit) +{ + xmlDocPtr xml_output; + xmlDocPtr xml_received; + xmlChar *action; + int ret; + + ulong_t rp_qty_from_xml = 0; + int unit_from_xml = 1; + + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldom_rsc_pool (ENTER)\n"); + + *rp_qty = 0; + *unit = 0; + + /* use list-devices for capacity and list-bindings for reserved resources */ + if (rp_type == RP_CAPACITY) + action = XML_LIST_DEVICES; + else + action = XML_LIST_BIND; + + /* create XML file to send request to the ldom manager */ + xml_output = create_xml_file_4_ldom_action(NULL, action); + if (xml_output == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldom_rsc_pool.. failed to create xml file for action=%s\n", + action); + return (-1); + } + + /* send XML file to the ldom manager */ + xml_received = send_xml_file_to_ldm(xml_output); + if (xml_received == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldom_rsc_pool.. failed to send xml file to ldm and receive xml back\n"); + xmlFreeDoc(xml_output); + return (-1); + } + + switch (resource) { + case CPU_RP: + /* parse the received XML file to get the cpu resource rp_qty info */ + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: get_ldom_rsc_pool.. trying to parse for cpu resource\n"); + if (rp_type == RP_CAPACITY) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: get_ldom_rsc_pool.. trying to parse for capacity value\n"); + ret = parse_xml_get_cpu_rp(xml_received, rp_type, &rp_qty_from_xml, &unit_from_xml); + } + else { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: get_ldom_rsc_pool.. trying to parse for reserved value\n"); + ret = parse_xml_get_cpu_rp(xml_received, rp_type, &rp_qty_from_xml, &unit_from_xml); + } + break; + case MEM_RP: + /* parse the received XML file to get the memory resource rp_qty info */ + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: get_ldom_rsc_pool.. trying to parse for memory resource\n"); + if (rp_type == RP_CAPACITY) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: get_ldom_rsc_pool.. trying to parse for capacity value\n"); + ret = parse_xml_get_mem_rp(xml_received, rp_type, &rp_qty_from_xml, &unit_from_xml); + } + else { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: get_ldom_rsc_pool.. trying to parse for reserved value\n"); + ret = parse_xml_get_mem_rp(xml_received, rp_type, &rp_qty_from_xml, &unit_from_xml); + } + break; + default: + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldom_rsc_pool.. unsupported resource\n"); + } + + if (ret == -1) { + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldom_rsc_pool.. failed to parse xml file\n"); + return (-1); + } + + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: get_ldom_rsc_pool. rp_qty=%d unit=%d\n", rp_qty_from_xml, unit_from_xml); + + *rp_qty = rp_qty_from_xml; + *unit = unit_from_xml; + + /* cleanup */ + xmlFreeDoc(xml_output); + xmlFreeDoc(xml_received); + + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldom_rsc_pool (EXIT)\n"); + + return(0); +} /* get_ldom_rsc_pool */ + +/* + * get_ldom_cpu_bindings + * + * This function gets the cpu bindings for the input domain. + * The CPU binding info is put into the input cpuBinding structure. + * The processing of this data is handeled by the calling routine. + + * Input: + * domain - The domain name + * cpuBindings - Pointer to a structure to place CPU binding info + * + * Output: + * cpuBindings - Updated structure content with CPU binding info + * + * Returns: + * 0 if the operation is successful + * -1 if the operation fails + * + */ +int +get_ldom_cpu_bindings(char *domain, cpuBindings_t **cpuBindings) +{ + xmlDocPtr xml_output; + xmlDocPtr xml_received; + xmlChar *action; + int ret; + + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldom_cpu_bindings (ENTER)\n"); + + action = XML_LIST_BIND; + + /* create XML file to send request to the ldom manager */ + xml_output = create_xml_file_4_ldom_action(domain, action); + if (xml_output == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldom_cpu_bindings.. failed to create xml file for action=%s\n",action); + return (-1); + } + + /* send XML file to the ldom manager */ + xml_received = send_xml_file_to_ldm(xml_output); + if (xml_received == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldom_cpu_bindings.... failed to send xml file to ldm and receive xml back\n"); + xmlFreeDoc(xml_output); + return (-1); + } + + ret = parse_xml_get_cpu_bindings(xml_received, cpuBindings); + + if (ret < 0) { + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldom_cpu_bindings...... failed to parse xml file\n"); + return (ret); + } + + /* cleanup */ + xmlFreeDoc(xml_output); + xmlFreeDoc(xml_received); + + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldom_cpu_bindings(EXIT)\n"); + + return(ret); +} /* get_ldom_cpu_bindings */ + +/* + * get_free_memory + * + * This function gets the amount of unused (free) memory in the LDoms system, by + * invoking get_ldom_rsc_pool() routine that sends the "list-devices" XML request + * to the ldom manager. + * The memory amount is converted to KBytes. + * + * Input: + * + * Output (pointer is used to pass information from this function to the caller): + * maxmem - pointer to the maximum amount of memory allowed (in KB) + * + * Returns: + * 0 if the operation is successful + * -1 if the operation fails + * + */ +int +get_free_memory(unsigned long *maxmem) +{ + int ret; + ulong_t capacity = 0; + int memunit = LDOMMEMUNIT_KILOBYTES; + + unsigned long mem; + + int resource = MEM_RP; + int rp_type = RP_CAPACITY; + + if (ldoms_debug) dprt("LDOMS_DEBUG: get_free_memory(ENTER) \n"); + + *maxmem = 0; + + ret = get_ldom_rsc_pool(resource, rp_type, &capacity, &memunit); + + if (ret == -1) + if (ldoms_debug) dprt("LDOMS_DEBUG: get_free_memory... get_ldom_rsc_pool failed\n"); + else + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: get_free_memory.. capacity=%d memunit=%d\n", capacity, memunit); + + /* convert the memory value to KBytes */ + switch (memunit) { + case LDOMMEMUNIT_KILOBYTES: break; /* KB already */ + case LDOMMEMUNIT_MEGABYTES: capacity *= 1024; break; /* MB */ + case LDOMMEMUNIT_GIGABYTES: capacity *= (1024 * 1024); break; /* GB */ + case LDOMMEMUNIT_BYTES: capacity /= 1024; break; /* Bytes */ + } + + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: get_free_memory.. Free memory = %dKB\n", capacity); + + mem = capacity; + + *maxmem = mem; + + if (ldoms_debug) dprt("LDOMS_DEBUG: get_free_memory(EXIT).. Maximum Free memory = %dKB\n", mem); + + return(ret); + +} /* get_free_memory */ + + +/* + * get_ldom_total_memory + * + * This function retrieves the total amount of memory on the system from LDM by + * invoking get_ldom_rsc_pool() calls to send the "list-devices" (for free + * resource) and "list-bindings" (for bound resource) to the ldom manager + * and adds up the free and bound memory amount for all domains. + * This function converts the memory to KBytes. + * + * Input: + * + * Output (pointer is used to pass information from this function to the caller): + * total_mem - pointer to the total amount of memory (in KB) + * + * Returns: + * 0 if the operation is successful + * -1 if the operation fails + * + */ +int +get_ldom_total_memory(unsigned long *total_mem) +{ + int ret; + ulong_t capacity = 0; + ulong_t reserved = 0; + int memunit = LDOMMEMUNIT_KILOBYTES; + + unsigned long mem; + + int resource = MEM_RP; + int rp_type = RP_CAPACITY; + + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldom_total_memory(ENTER) \n"); + + *total_mem = 0; + + /* first, get the amount of free memory by using the RP_CAPACITY type + * that sends the list-devices request */ + ret = get_ldom_rsc_pool(resource, rp_type, &capacity, &memunit); + + if (ret == -1) + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldom_total_memory... get_ldom_rsc_pool failed with RP_CAPACITY\n"); + else + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: get_ldom_total_memory.. capacity=%d memunit=%d\n", capacity, memunit); + + /* convert the memory value to KBytes */ + switch (memunit) { + case LDOMMEMUNIT_KILOBYTES: break; /* KB already */ + case LDOMMEMUNIT_MEGABYTES: capacity *= 1024; break; /* MB */ + case LDOMMEMUNIT_GIGABYTES: capacity *= (1024 * 1024); break; /* GB */ + case LDOMMEMUNIT_BYTES: capacity /= 1024; break; /* Bytes */ + } + + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: get_ldom_total_memory.. Free memory = %dKB\n", capacity); + + mem = capacity; + + /* Set the global var for the Free memory in KB */ + ldomsFreeMem = mem; + + /* now, get the amount of bound memory by using the RP_RESERVED type + * that send the list-bindings request */ + rp_type = RP_RESERVED; + ret = get_ldom_rsc_pool(resource, rp_type, &reserved, &memunit); + + if (ret == -1) + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldom_total_memory... get_ldom_rsc_pool failed with RP_RESERVED\n"); + else + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: get_ldom_total_memory.. reserved=%d memunit=%d\n", reserved, memunit); + + /* convert the memory value to KBytes */ + switch (memunit) { + case LDOMMEMUNIT_KILOBYTES: break; /* KB already */ + case LDOMMEMUNIT_MEGABYTES: reserved *= 1024; break; /* MB */ + case LDOMMEMUNIT_GIGABYTES: reserved *= (1024 * 1024); break; /* GB */ + case LDOMMEMUNIT_BYTES: reserved /= 1024; break; /* Bytes */ + } + + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: get_ldom_total_memory.. Used memory = %dKB\n", reserved); + + /* add up the amount of bound memory to the free memory */ + mem += reserved; + + /* Set the global var for the Used memory in KB */ + ldomsUsedMem = reserved; + + *total_mem = mem; + + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldom_total_memory(EXIT): total memory in KB=%d\n", mem); + + return(ret); + +} /* get_ldom_total_memory */ + + +/* + * get_ldom_total_cpu + * + * This function retrieves the total number of CPUs on the system from LDM by + * invoking get_ldom_rsc_pool() calls to send the "list-devices" (for free + * resource) and "list-bindings" (for bound resource) to the ldom manager + * and adds up the free and bound CPUs for all domains. + * + * Input: + * + * Output (pointer is used to pass information from this function to the caller): + * total_cpu - pointer to the total number of CPUs + * + * Returns: + * 0 if the operation is successful + * -1 if the operation fails + * + */ +int +get_ldom_total_cpu(int *total_cpu) +{ + int ret; + ulong_t capacity = 0; + ulong_t reserved = 0; + int unit = 1; + + int tcpu; + + int resource = CPU_RP; + int rp_type = RP_CAPACITY; + + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldom_total_cpu(ENTER) \n"); + + *total_cpu = 0; + + /* first, get the number of free CPUs by using the RP_CAPACITY type + * that sends the list-devices request */ + ret = get_ldom_rsc_pool(resource, rp_type, &capacity, &unit); + + if (ret == -1) + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldom_total_cpu... get_ldom_rsc_pool failed with RP_CAPACITY\n"); + else + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: get_ldom_total_cpu.. capacity(free CPUs)=%d \n", capacity); + + tcpu = capacity; + + /* Set the global var for the Free CPUs */ + ldomsFreeCpu = capacity; + + /* now, get the number of bound CPUs by using the RP_RESERVED type + * that send the list-bindings request */ + rp_type = RP_RESERVED; + ret = get_ldom_rsc_pool(resource, rp_type, &reserved, &unit); + + if (ret == -1) + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldom_total_cpu... get_ldom_rsc_pool failed with RP_RESERVED\n"); + else + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: get_ldom_total_cpu.. reserved(bound CPUs)=%d\n", reserved); + + /* add up the amount of bound memory to the free memory */ + tcpu += reserved; + + /* Set the global var for the Used CPUs */ + ldomsUsedCpu = reserved; + + *total_cpu = tcpu; + + if (ldoms_debug) dprt("LDOMS_DEBUG: get_ldom_total_cpu(EXIT): total_cpu=%d\n", tcpu); + + return(ret); + +} /* get_ldom_total_cpu */ + +#endif /* WITH_LDOMS */ + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ + diff --git a/src/ldoms_xml_parse.h b/src/ldoms_xml_parse.h new file mode 100644 --- /dev/null +++ b/src/ldoms_xml_parse.h @@ -0,0 +1,212 @@ +/* + * ldoms_xml_parse.h: data structure to be used in the LDoms XML parsing. + * + * Copyright 2008 Sun Microsystems, Inc. + * + * See COPYING.LIB for the License of this software + * + */ + +#ifndef __VIR_LDOMS_XML_PARSE_H__ +#define __VIR_LDOMS_XML_PARSE_H__ + +#ifdef WITH_LDOMS + +#include <libxml/tree.h> +#include <libxml/parser.h> +#include "libvirt/libvirt.h" +#include "ldoms_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* data structures that parsing code will populate */ +typedef struct ldominfo { + ulong_t index; + char ldomName[NAME_SIZE]; /* ldom name */ + uint_t ldomState; /* admin state */ + ulong_t ldomNumVCpu; /* number of VCpu */ + ulong_t ldomMemSize; /* virtual memory size */ + uint_t ldomMemUnit; /* unit of memory */ + ulong_t ldomNumCrypto; /* number of crypto */ + ulong_t ldomNumIOBus; /* number of IO bus */ + int ldomConsole; /* console port */ + unsigned char uuid[VIR_UUID_BUFLEN]; +} ldominfo_t; + +typedef struct vcpu_s { + ulong_t index; + ulong_t vcpuLdomIndex; /* index to ldom table */ + char vcpuDeviceID[NAME_SIZE]; /* virtual CPU Device ID */ + uint_t vcpuOperationalStatus; /* vcpu status */ + char vcpuPhysBind[NAME_SIZE]; /* physical binding */ + ulong_t vcpuPhysBindUsage; /* physical bind usage */ +} vcpu_t; + +typedef struct vmem_data_s { + ulong_t index; + ulong_t vmemLdomIndex; /* index to ldom table */ + ulong_t vmemNumberOfBlocks; /* number of vmem blocks */ +} vmem_data_t; + +typedef struct vmem_physbind_s { + ulong_t index; + ulong_t vmemLdomIndex; /* index to ldom table */ + char vmemPhysBind[NAME_SIZE]; /* memory block binding */ +} vmem_physbind_t; + +typedef struct vsw_s { + ulong_t index; + ulong_t vswLdomIndex; /* index to ldom table */ + char vswServiceName[NAME_SIZE]; /* vsw service name */ + char vswMacAddress[NAME_SIZE]; /* vsw mac address */ + char vswPhysDevPath[NAME_SIZE]; /* vsw physical dev path */ + uint_t vswMode; /* vsw mode */ + char vswLdomName[NAME_SIZE]; /* ldom name - used to index to ldom table */ +} vsw_t; + +typedef struct vsw_name_s { + char ServiceName[NAME_SIZE]; /* vsw service name */ +} vsw_name_t; + +typedef struct vnet_s { + ulong_t index; + ulong_t vnetLdomIndex; /* index to ldom table */ + ulong_t vnetVswIndex; /* index to vsw table */ + char vnetDevName[NAME_SIZE]; /* vnet dev name */ + char vnetDevMacAddress[NAME_SIZE]; /* vnet dev mac address */ + char vnetServiceName[NAME_SIZE]; /* vsw service name - used to index to vsw table */ + char vnetLdomName[NAME_SIZE]; /* ldom name - used to index to ldom table */ +} vnet_t; + +typedef struct vds_s { + ulong_t index; + ulong_t vdsLdomIndex; /* index to ldom table */ + char vdsServiceName[NAME_SIZE]; /* vds service name */ + ulong_t vdsNumofAvailVolume; /* number of available logical volume */ + ulong_t vdsNumofUsedVolume; /* number of used logical volume */ + char vdsLdomName[NAME_SIZE]; /* ldom name - used to index to ldom table */ +} vds_t; + +typedef struct vds_name_s { + char ServiceName[NAME_SIZE]; /* vds service name */ +} vds_name_t; + +typedef struct vdsdev_s { + ulong_t index; + ulong_t vdsdevVdsIndex; /* index to vds table */ + char vdsdevVolumeName[NAME_SIZE]; /* volume name */ + char vdsdevDevPath[NAME_SIZE]; /* block dev */ + char vdsdevServiceName[NAME_SIZE]; /* vds service name - used to index to vds table */ +} vdsdev_t; + +typedef struct vdsdev_name_s { + char VolumeName[NAME_SIZE]; /* vdsdev name */ + char ServiceName[NAME_SIZE]; /* vds name */ +} vdsdev_name_t; + +typedef struct vdisk_s { + ulong_t index; + ulong_t vdiskLdomIndex; /* index to ldom table */ + ulong_t vdiskVdsdevIndex; /* index to vdsdev table */ + char vdiskName[NAME_SIZE]; /* vdisk name */ + char vdiskServiceName[NAME_SIZE]; /* vds name - used to index to vdsdev table */ + char vdiskVolumeName[NAME_SIZE]; /* vdsdev name - used to index to vdsdev table */ + char vdiskLdomName[NAME_SIZE]; /* ldom name - used to index to ldom table */ +} vdisk_t; + +typedef struct vcc_s { + ulong_t index; + ulong_t vccLdomIndex; /* index to ldom table */ + char vccName[NAME_SIZE]; /* vcc service name */ + uint_t vccPortRangeLow; /* vcc min port */ + uint_t vccPortRangeHigh; /* vcc max port */ + char vccLdomName[NAME_SIZE]; /* ldom name - used to index to ldom table */ +} vcc_t; + +typedef struct vcc_name_s { + char ServiceName[NAME_SIZE]; /* vcc name */ +} vcc_name_t; + +typedef struct vcons_s { + ulong_t index; + char vconsGroupName[NAME_SIZE]; /* vcons group name */ + uint_t vconsPortNumber; /* vcons port */ + char vconsLdomName[NAME_SIZE]; /* ldom name - used to index to ldom table */ +} vcons_t; + +typedef struct vconsvccrel_s { + ulong_t index; + uint_t vconsvccrelVconsIndex; /* index to vcons table */ + uint_t vconsvccrelLdomIndex; /* index to ldom table */ + uint_t vconsvccrelVccIndex; /* index to vcc table */ + char vconsvccrelServiceName[NAME_SIZE]; /* vcc service name - used to index to vcc table */ + char vconsvccrelLdomName[NAME_SIZE]; /* ldom name - used to index to ldom table */ +} vconsvccrel_t; + +typedef struct envvars_s { + ulong_t index; + ulong_t envvarsLdomIndex; /* index to ldom table */ + char envvarsName[NAME_SIZE]; /* env var name */ + char envvarsValue[NAME_SIZE]; /* env var value */ +} envvars_t; + +typedef struct crypto_s { + ulong_t index; + ulong_t cryptoLdomIndex; /* index to ldom table */ + char cryptoCpuSet[NAME_SIZE]; /* crypto cpuset */ +} crypto_t; + +typedef struct iobus_s { + ulong_t index; + ulong_t iobusLdomIndex; /* index to ldom table */ + char iobusDevName[NAME_SIZE]; /* iobus device name */ + char iobusDevPath[NAME_SIZE]; /* iobus device path */ +} iobus_t; + +xmlDocPtr create_xml_file(char *snmp_user, char *ldom_name, const xmlChar *action); +xmlDocPtr create_xml_file_4_ldom_action(char *, const xmlChar *); +xmlDocPtr create_xml_file_4_set_vcpu(char *, int ); +xmlDocPtr create_xml_file_4_set_memory(char *, unsigned long ); + +int parse_xml_get_response_status(xmlNodePtr node); +int parse_xml_get_ldom_state(xmlNodePtr node); + +int parse_xml_get_ldom_cnt(xmlDoc *doc, int *ldom_cnt); +int parse_xml_get_ldominfo(xmlDoc *doc, int *num_cpu, int *mem_size, int *mem_unit, int *num_crypto, int *num_iobus); +char* parse_xml_get_ldom_name(xmlNodePtr node); + +int parse_xml_get_subnode_cnt(xmlNodePtr node, xmlChar *subnode); +int parse_xml_get_vds_volume(xmlNodePtr node, char *service_name); +int parse_xml_get_vds_volume_bind(xmlDoc *doc, char *service_name); +char* parse_xml_get_mac_addr(xmlNodePtr node); + +int parse_xml_get_cpu_bindings(xmlDocPtr, cpuBindings_t **); +int parse_xml_get_console(xmlDoc *, int *); +int parse_xml_get_vcpu(xmlDoc *doc, int *vcpu_cnt, vcpu_t ***vcpu); +int parse_xml_get_vmem(xmlDoc *doc, int *vmem_cnt, vmem_data_t ***vmem); +int parse_xml_get_vmem_physbind(xmlDoc *doc, int *vmem_cnt, vmem_physbind_t ***vmem); +int parse_xml_get_vsw(xmlDoc *doc, int *vsw_cnt, vsw_t ***vsw); +int parse_xml_get_vnet(xmlDoc *doc, int *vnet_cnt, vnet_t ***vnet); +int parse_xml_get_vds(xmlDoc *doc, int *vds_cnt, vds_t ***vds); +int parse_xml_get_vdsdev(xmlDoc *doc, int *vdsdev_cnt, vdsdev_t ***vdsdev); +int parse_xml_get_vdisk(xmlDoc *doc, int *vdisk_cnt, vdisk_t ***vdisk); +int parse_xml_get_vcc(xmlDoc *doc, int *vcc_cnt, vcc_t ***vcc); +int parse_xml_get_vcons(xmlDoc *doc, int *vcons_cnt, vcons_t ***vcons); +int parse_xml_get_vconsvccrel(xmlDoc *doc, int *vconsvccrel_cnt, vconsvccrel_t ***vconsvccrel); +int parse_xml_get_envvars(xmlDoc *doc, int *envvars_cnt, envvars_t ***envvars); +int parse_xml_get_crypto(xmlDoc *doc, int *crypto_cnt, crypto_t ***crypto); +int parse_xml_get_iobus(xmlDoc *doc, int *iobus_cnt, iobus_t ***iobus); + +int parse_xml_get_cpu_rp(xmlDoc *doc, int rp_type, ulong_t *reserved, int *unit); +int parse_xml_get_mem_rp(xmlDoc *doc, int rp_type, ulong_t *reserved, int *unit); +int parse_xml_get_crypto_rp(xmlDoc *doc, int rp_type, ulong_t *reserved, int *unit); +int parse_xml_get_iobus_rp(xmlDoc *doc, int rp_type, ulong_t *reserved, int *unit); + +#ifdef __cplusplus +} +#endif + +#endif /* WITH_LDOMS*/ +#endif /* __VIR_LDOMS_XML_PARSE_H__ */ diff --git a/src/ldoms_xml_parse.c b/src/ldoms_xml_parse.c new file mode 100644 --- /dev/null +++ b/src/ldoms_xml_parse.c @@ -0,0 +1,1832 @@ +/* + * ldoms_xml_parse.c: LDoms XML parsing routines using the LDoms XML Schema V2 + * to interface with the LDoms Manager (LDM) + * + * Copyright 2008 Sun Microsystems, Inc. + * + * See COPYING.LIB for the License of this software + * + */ + +#ifdef WITH_LDOMS + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/processor.h> +#include <fcntl.h> +#include <libxml/tree.h> +#include <libxml/parser.h> + + +#include "internal.h" +#include "xml.h" +#include "ldoms_internal.h" +#include "ldoms_intfc.h" + +/* Domain state info + * LDom State enumerations + * 1 = active + * 2 = stopping + * 3 = inactive + * 4 = binding + * 5 = unbinding + * 6 = bound + * 7 = starting + * + * libvirt LDom State enums + * typedef enum { + * VIR_DOMAIN_NOSTATE = 0, no state + * VIR_DOMAIN_RUNNING = 1, the domain is running + * VIR_DOMAIN_BLOCKED = 2, the domain is blocked on resource + * VIR_DOMAIN_PAUSED = 3, the domain is paused by user + * VIR_DOMAIN_SHUTDOWN= 4, the domain is being shut down + * VIR_DOMAIN_SHUTOFF = 5, the domain is shut off + * VIR_DOMAIN_CRASHED = 6 the domain is crashed + * } virDomainState; + */ + +/* + * when adding up the memory (capacity/reserved) for the memory resource pool, + * the memory unit will be converted to take the smallest unit in use. + */ +#define MEM_GB_BYTES (1024*1024*1024) /* 1 GB in bytes */ +#define MEM_MB_BYTES (1024*1024) /* 1 MB in bytes */ +#define MEM_KB_BYTES (1024) /* 1 KB in bytes */ + +/* Global vars for debug statement */ +extern int ldoms_debug; +extern int ldoms_detailed_debug; + +/* + * Returns the next elemental sibling node of the node you pass in. If there + * are no more nodes it returns NULL. + */ +xmlNodePtr +xml_get_next_ele_node(xmlNodePtr node) +{ + xmlNodePtr sib_node; + + sib_node = node->next; + + while (sib_node != NULL) { + if (sib_node->type != XML_ELEMENT_NODE) + sib_node = sib_node->next; + else + break; + } + + return (sib_node); +} /* xml_get_next_ele_node() */ + +/* + * Find and return the first-level subnode (if any) of 'node' which has name + * 'name'. + */ +xmlNodePtr +xml_find_subnode(xmlNodePtr node, const xmlChar *name) +{ + xmlNodePtr subnode; + + subnode = node->xmlChildrenNode; + while (subnode != NULL) { + if (xmlStrcmp(subnode->name, name) == 0) + break; + subnode = subnode->next; + } + + return (subnode); +} /* xml_find_subnode */ + + +/* + * create_xml_file_4_set_vcpu + * + * This function creates the XML request file for changing the number + * or virtual cpus for an LDom + * + * Input: + * ldom_name - name of the ldom to be included in the XML file with + * the <ldom_name> tag. + * nvcpus - The new number of vcpus for this LDom + * + * Returns: + * pointer to the created xml doc if the operation is successful + * NULL if the operation fails + * + * Example: The following XML file will be created for LDom ldom1 + * and a new vcpu value of 4 + * + * <?xml version="1.0"?> + * <LDM_interface version="1.0"> + * <cmd> + * <action>set-vcpu</action> + * <data version="2.0"> + * <ldom> + * <ldom_info> + * <ldom_name>ldom1</ldom_name> + * </ldom_info> + * <cpu> + * <number>4</number> + * </cpu> + * </ldom> + * </data> + * </cmd> + * </LDM_interface> + */ + +xmlDocPtr +create_xml_file_4_set_vcpu(char *ldom_name, int nvcpus) +{ + xmlDocPtr xml_output; + xmlNodePtr root, cmd, data, ldom, info_node, cpu; + char vcpu[10]; /* ascii version of input int nvcpus */ + + if (ldoms_debug) dprt("LDOMS_DEBUG: create_xml_file_4_set_vcpu(ENTER): ldom=%s, vcpus=%d\n", + (ldom_name==NULL? "NULL" : ldom_name), nvcpus); + + xml_output = xmlNewDoc(XML_VERSION); + + /* <LDM_interface> tag with version attribute */ + root = xmlNewDocNode(xml_output, NULL, XML_LDM_INTERFACE, NULL); + xmlDocSetRootElement(xml_output, root); + xmlNewProp(root, VERSION_ATTR, LDOM_INTERFACE_VERSION); + + /* <cmd> tag */ + cmd = xmlNewChild(root, NULL, XML_CMD, NULL); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: create_xml_file_4_set_vcpu() cmd tag\n"); + + /* <action> tag */ + xmlNewChild(cmd, NULL, XML_ACTION, XML_SET_VCPU); + + /* <data> tag with version attribute */ + data = xmlNewChild(cmd, NULL, XML_DATA, NULL); + xmlNewProp(data, VERSION_ATTR, LDOM_DATA_VERSION); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: create_xml_file_4_set_vcpu() data tag\n"); + + /* <ldom> tag */ + ldom = xmlNewChild(data, NULL, LDOM_NODE, NULL); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: create_xml_file_4_set_vcpu() ldom tag\n"); + + /* <ldom_info> tag with child <ldom_name> tag */ + info_node = xmlNewChild(ldom, NULL, XML_LDM_INFO, NULL); + xmlNewChild(info_node, NULL, XML_LDM_NAME, (xmlChar *)ldom_name); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: create_xml_file_4_set_vcpu() ldom_info tag\n"); + + /* <cpu> tag with child <number> tag */ + cpu = xmlNewChild(ldom, NULL, CPU_NODE, NULL); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: create_xml_file_4_set_vcpu() cpu tag\n"); + sprintf(vcpu, "%d", nvcpus); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: create_xml_file_4_set_vcpu() vcpu=%s\n",vcpu); + xmlNewChild(cpu, NULL, NUMBER_NODE, (xmlChar *)vcpu); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: create_xml_file_4_set_vcpu() number tag\n"); + + if (ldoms_debug) dprt("LDOMS_DEBUG: create_xml_file_4_set_vcpu(EXIT)\n"); + + return(xml_output); +} /* create_xml_file_4_set_vpcu() */ + +/* + * create_xml_file_4_set_memory + * + * This function creates the XML request file for changing the memory + * for an LDom + * + * Input: + * ldom_name - name of the ldom to be included in the XML file with + * the <ldom_name> tag. + * memory - The new memory size for this LDom. + * The memory size is in Kilobytes. + * NOTE: The VMM and virsh displays the memory in KB. + * + * Returns: + * pointer to the created xml doc if the operation is successful + * NULL if the operation fails + * + * Example: The following XML file will be created for LDom ldom1 + * and a new memory value of 256 Kilobytes. + * + * <?xml version="1.0"?> + * <LDM_interface version="1.0"> + * <cmd> + * <action>set-memory</action> + * <data version="2.0"> + * <ldom> + * <ldom_info> + * <ldom_name>ldom1</ldom_name> + * </ldom_info> + * <memory> + * <size>256K</size> + * </memory> + * </ldom> + * </data> + * </cmd> + * </LDM_interface> + */ + +xmlDocPtr +create_xml_file_4_set_memory(char *ldom_name, unsigned long memory) +{ + xmlDocPtr xml_output; + xmlNodePtr root, cmd, data, ldom, info_node, mem; + char mem_str[10]; /* ascii version of input int memory */ + + if (ldoms_debug) dprt("LDOMS_DEBUG: create_xml_file_4_set_memory(ENTER): ldom=%s, memory=%lu\n", + (ldom_name==NULL? "NULL" : ldom_name), memory); + + xml_output = xmlNewDoc(XML_VERSION); + + /* <LDM_interface> tag with version sttribute */ + root = xmlNewDocNode(xml_output, NULL, XML_LDM_INTERFACE, NULL); + xmlDocSetRootElement(xml_output, root); + xmlNewProp(root, VERSION_ATTR, LDOM_INTERFACE_VERSION); + + /* <cmd> tag */ + cmd = xmlNewChild(root, NULL, XML_CMD, NULL); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: create_xml_file_4_set_memory() cmd tag\n"); + + /* <action> tag */ + xmlNewChild(cmd, NULL, XML_ACTION, XML_SET_MEM); + + /* <data> tag with version attribute */ + data = xmlNewChild(cmd, NULL, XML_DATA, NULL); + xmlNewProp(data, VERSION_ATTR, LDOM_DATA_VERSION); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: create_xml_file_4_set_memory() data tag\n"); + + /* <ldom> tag */ + ldom = xmlNewChild(data, NULL, LDOM_NODE, NULL); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: create_xml_file_4_set_memory() ldom tag\n"); + + /* <ldom_info> tag with child <ldom_name> tag */ + info_node = xmlNewChild(ldom, NULL, XML_LDM_INFO, NULL); + xmlNewChild(info_node, NULL, XML_LDM_NAME, (xmlChar *)ldom_name); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: create_xml_file_4_set_memory() ldom_info tag\n"); + + /* add memory unit K (for Kilobytes) to the memory size string */ + sprintf(mem_str, "%d", memory); + strlcat(mem_str, "K", sizeof (mem_str)); + + /* <memory> tag with child <size> tag */ + mem = xmlNewChild(ldom, NULL, MEMORY_NODE, NULL); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: create_xml_file_4_set_memory() memory tag\n"); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: create_xml_file_4_set_memory() memory=%s\n", mem_str); + xmlNewChild(mem, NULL, SIZE_NODE, (xmlChar *)mem_str); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: create_xml_file_4_set_memory() size tag\n"); + + if (ldoms_debug) dprt("LDOMS_DEBUG: create_xml_file_4_set_memory(EXIT)\n"); + + return(xml_output); +} /* create_xml_file_4_set_memory() */ + +/* + * create_xml_file_4_ldom_action + * + * This function creates the XML request file for the given ldom and action. + * + * Input: + * ldom_name - name of the ldom to be included in the XML file with + * the <ldom_name> tag. if ldom_name is NULL, the XML request for + * all ldoms will be created + * action - action (such as list-bindings or list-constraints..etc) to be + * included in the XML file with the <action> tag + * + * Returns: + * pointer to the created xml doc if the operation is successful + * NULL if the operation fails + * + * Example: The following XML file will be created if the + * name of the ldom is 'ldg3', and the action is 'list-bindings'. + * + * <?xml version="1.0"?> + * <LDM_interface version="1.0"> + * <cmd> + * <action>list-bindings</action> + * <data version="2.0"> + * <ldom> + * <ldom_info> + * <ldom_name>ldg3</ldom_name> + * </ldom_info> + * </ldom> + * </data> + * </cmd> + * </LDM_interface> + */ +xmlDocPtr +create_xml_file_4_ldom_action(char *ldom_name, const xmlChar *action) +{ + xmlDocPtr xml_output; + xmlNodePtr root, cmd, data, ldom, info_node; + + if (ldoms_debug) dprt("LDOMS_DEBUG: create_xml_file_4_ldom_action(ENTER): ldom=%s, action=%s\n", + (ldom_name==NULL? "NULL" : ldom_name), action); + + xml_output = xmlNewDoc(XML_VERSION); + root = xmlNewDocNode(xml_output, NULL, XML_LDM_INTERFACE, NULL); + xmlDocSetRootElement(xml_output, root); + xmlNewProp(root, VERSION_ATTR, LDOM_INTERFACE_VERSION); + + cmd = xmlNewChild(root, NULL, XML_CMD, NULL); + + xmlNewChild(cmd, NULL, XML_ACTION, action); + + data = xmlNewChild(cmd, NULL, XML_DATA, NULL); + xmlNewProp(data, VERSION_ATTR, LDOM_DATA_VERSION); + + /* + * If the ldom_name is NULL, the LDM will return info for + * all existing LDoms, independent of their state. Otherwise, + * we get the info for only the LDom specified. + */ + if (ldom_name != NULL) { + ldom = xmlNewChild(data, NULL, LDOM_NODE, NULL); + info_node = xmlNewChild(ldom, NULL, XML_LDM_INFO, NULL); + xmlNewChild(info_node, NULL, XML_LDM_NAME, (xmlChar *)ldom_name); + } + + if (ldoms_debug) dprt("LDOMS_DEBUG: create_xml_file_4_ldom_action(EXIT)\n"); + + return(xml_output); +} /* create_xml_file_4_ldom_action() */ + + +/* + * parse_xml_get_subnode_cnt + * + * This function counts the specific subnodes starting from the given xml node. + * + * Input: + * node - pointer to the xml node to start looking for the subnode + * subnode_to_find - subnode to look for and count the numbers + * + * Returns: + * number of subnodes if the operation is successful + * 0 if no subnode is found + */ +int +parse_xml_get_subnode_cnt(xmlNodePtr node, xmlChar *subnode_to_find) +{ + xmlNodePtr subnode; + int i = 0; + + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_subnode_cnt(ENTER)\n"); + + subnode = node->xmlChildrenNode; + while (subnode != NULL) { + if (subnode->type != XML_ELEMENT_NODE) { + subnode = subnode->next; + continue; + } + + + if (xmlStrcmp(subnode->name, (const xmlChar *)subnode_to_find) == 0) { + i++; + subnode = subnode->next; + continue; + } + + subnode = subnode->next; + } + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_subnode_cnt(EXIT) cnt=%d\n",i); + + + return(i); +} + +/* + * parse_xml_get_console + * + * This function gets the console port number for an LDom. + * + * Input: + * xml doc - XML document to read (this will be an XML document for one ldom) + * + * Output (pointers are used to pass information from this function to the caller): + * console - pointer to the console port number + * + * Returns: + * 0 if the operation is successful + * -1 if the operation fails + * + * NOTE: Example XML document that this function will parse to get the ldom info: + * + * <?xml version="1.0"?> + * <LDM_interface version="1.0"> + * <cmd> + * <action>list-bindings</action> + * <data version="2.0"> + * <ldom> + * <ldom_info> + * <ldom_name>primary</ldom_name> + * </ldom_info> + * <console> + * <service_name>primary-vcc0</service_name> + * <port>5004</port> + * </console> + *... + * </ldom> + * <response> + * <status>success</status> + * </response> + * </data> + * <response> + * <status>success</status> + * </response> + * </cmd> + * <response> + * <status>success</status> + * </response> + * </LDM_interface> + */ +int +parse_xml_get_console(xmlDoc *doc, int *console) +{ + int ret = 0; + int console_xml; + xmlNodePtr root_node = NULL; + xmlNodePtr cmd_node = NULL; + xmlNodePtr data_node = NULL; + xmlNodePtr ldom_node = NULL; + xmlNodePtr console_node = NULL; + char *ldm_name = NULL; + xmlChar *content = NULL; + + xmlNodePtr subnode; + + uint64_t size = 0; + + *console = 0; + + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_console(ENTER) \n"); + + root_node = xmlDocGetRootElement(doc); + + /* Check for an XML failure */ + if (parse_xml_get_response_status(root_node) == -1) + return(-1); + + /* Get the <cmd> tag */ + cmd_node = xml_find_subnode(root_node, XML_CMD); + if (cmd_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_console.. XML file does not have <cmd> tag\n"); + return (-1); + } + + /* Get the <data> tag */ + data_node = xml_find_subnode(cmd_node, XML_DATA); + if (data_node != NULL) + /* Get the <ldom> tag */ + ldom_node = xml_find_subnode(data_node, LDOM_NODE); + else { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_console.. XML file does not have <data> tag\n"); + return (-1); + } + + if (ldom_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_console.. XML file does not have <ldom> tag\n"); + return (-1); + } + + /* Get the <console> tag */ + console_node = xml_find_subnode(ldom_node, CONSOLE_NODE); + if (console_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_console.. XML file does not have <console> tag\n"); + console_xml = 0; + } else { + /* Get the <port> tag */ + subnode = xml_find_subnode(console_node, XML_PORT); + if (subnode == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_console.. XML file does not have <port> tag within <console> tag\n"); + console_xml = 0; + } else { + /* Contents of the <port> tag */ + content = xmlNodeGetContent(subnode); + + console_xml = atoi((const char *)content); + if (console_xml <= 0) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_console.. Invalid <port> num: %s\n", content); + } + xmlFree(content); + } + } + + *console = console_xml; + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_console(EXIT) console=%d\n", console_xml); + + return (ret); +} + +/* + * parse_xml_get_ldominfo + * + * This function gets the ldom info such as the number of virtial cpu, memory and + * its unit, crypto, and iobus by parsing the XML output from the LDOM Manager. + * + * Input: + * xml doc - XML document to read (this will be an XML document for one ldom) + * + * Output (pointers are used to pass information from this function to the caller): + * num_cpu - pointer to the number of virtual cpu + * mem_size - pointer to the virtual memory size + * mem_unit - pointer to the virtual memory unit + * num_crypto - pointer to the number of crypto unit + * num_iobus - pointer to the number of io bus + * + * Returns: + * 0 if the operation is successful + * -1 if the operation fails + * + * NOTE: Example XML document that this function will parse to get the ldom info: + * + * <?xml version="1.0"?> + * <LDM_interface version="1.0"> + * <cmd> + * <action>list-constraints</action> + * <data version="2.0"> + * <ldom> + * <ldom_info> + * <ldom_name>primary</ldom_name> + * </ldom_info> + * <cpu> + * <number>4</number> + * </cpu> + * <mau> + * <number>1</number> + * </mau> + * <memory> + * <size>1G</size> + * </memory> + * <physio_device> + * <name>pci@780</name> + * </physio_device> + * </ldom> + * <response> + * <status>success</status> + * </response> + * </data> + * <response> + * <status>success</status> + * </response> + * </cmd> + * <response> + * <status>success</status> + * </response> + * </LDM_interface> + */ +int +parse_xml_get_ldominfo(xmlDoc *doc, int *num_cpu, int *mem_size, int *mem_unit, int *num_crypto, int *num_iobus) +{ + int ret = 0; + xmlNodePtr root_node = NULL; + xmlNodePtr cmd_node = NULL; + xmlNodePtr data_node = NULL; + xmlNodePtr ldom_node = NULL; + xmlNodePtr cpu_node = NULL; + xmlNodePtr memory_node = NULL; + xmlNodePtr mau_node = NULL; + char *ldm_name = NULL; + xmlChar *content = NULL; + + xmlNodePtr subnode; + + long long num_cpu_xml = 0; + long long num_crypto_xml = 0; + long long num_iobus_xml = 0; + + char *endp; + uint64_t size = 0; + + *num_cpu = 0; + *mem_size = 0; + *num_crypto = 0; + *num_iobus = 0; + + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_ldominfo(ENTER) \n"); + + root_node = xmlDocGetRootElement(doc); + + if (parse_xml_get_response_status(root_node) == -1) + return(-1); + + cmd_node = xml_find_subnode(root_node, XML_CMD); + if (cmd_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_ldominfo.. XML file does not have <cmd> tag\n"); + return (-1); + } + + data_node = xml_find_subnode(cmd_node, XML_DATA); + if (data_node != NULL) + ldom_node = xml_find_subnode(data_node, LDOM_NODE); + else { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_ldominfo.. XML file does not have <data> tag\n"); + return (ret); + } + + if (ldom_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_ldominfo.. XML file does not have <ldom> tag\n"); + return (ret); + } + + /* get number of cpu */ + cpu_node = xml_find_subnode(ldom_node, CPU_NODE); + if (cpu_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_ldominfo.. XML file does not have <cpu> tag\n"); + num_cpu_xml = 0; + } else { + subnode = xml_find_subnode(cpu_node, NUMBER_NODE); + if (subnode == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_ldominfo.. XML file does not have <number> tag within <cpu> tag\n"); + num_cpu_xml = 0; + } else { + + content = xmlNodeGetContent(subnode); + + num_cpu_xml = strtoll((char *)content, NULL, 0); + if (num_cpu_xml <= 0) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_ldominfo.. Invalid cpu num specified: %s\n", content); + } + xmlFree(content); + } + } + + + /* get memory size */ + memory_node = xml_find_subnode(ldom_node, MEMORY_NODE); + if (memory_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_ldominfo.. XML file does not have <memory> tag\n"); + size = 0; + } else { + subnode = xml_find_subnode(memory_node, SIZE_NODE); + if (subnode == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_ldominfo.. XML file does not have <size> tag within <memory> tag\n"); + size = 0; + } else { + + content = xmlNodeGetContent(subnode); + + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_ldominfo.. mem_size = %s\n", content); + + /* extract the number and unit from the content */ + size = strtoull((char *)content, &endp, 0); + if (size > 0) { + + /* get the memory unit */ + /* Ldoms Manager CLI is using "bytes" as the default memory unit. */ + *mem_unit = LDOMMEMUNIT_BYTES; /* use bytes as default */ + switch (strlen(endp)) { + case 0: break; + default: + switch (endp[strlen(endp)-1]) { + case 'G': + case 'g': + *mem_unit = LDOMMEMUNIT_GIGABYTES; + break; + case 'M': + case 'm': + *mem_unit = LDOMMEMUNIT_MEGABYTES; + break; + case 'K': + case 'k': + *mem_unit = LDOMMEMUNIT_KILOBYTES; + break; + default: + *mem_unit = LDOMMEMUNIT_BYTES; + } + break; + } + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_ldominfo.. mem size unit = %s\n", endp); + } else { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_ldominfo.. Invalid mem size specified: %s\n", content); + } + xmlFree(content); + } + + } + + /* get number of crypto */ + mau_node = xml_find_subnode(ldom_node, MAU_NODE); + if (mau_node == NULL) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_ldominfo.. XML file does not have <mau> tag\n"); + num_crypto_xml = 0; + } else { + subnode = xml_find_subnode(mau_node, NUMBER_NODE); + if (subnode == NULL) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_ldominfo.. XML file does not have <number> tag within <mau> tag\n"); + num_crypto_xml = 0; + } else { + + content = xmlNodeGetContent(subnode); + num_crypto_xml = strtoll((char *)content, NULL, 0); + if (num_crypto_xml <= 0) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_ldominfo.. Invalid crypto num specified: %s\n", content); + } + xmlFree(content); + } + } + + /* get number of io bus */ + num_iobus_xml = parse_xml_get_subnode_cnt(ldom_node, XML_PHYSIO_DEVICE); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_ldominfo.. number of <physio_device> tags =%d\n", num_iobus_xml); + + *num_cpu = (int)num_cpu_xml; + *mem_size = (int)size; + *num_crypto = (int)num_crypto_xml; + *num_iobus = (int)num_iobus_xml; + + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_ldominfo(EXIT)\n"); + + return (ret); +} + +xmlNodePtr +xmlFindSubnode(xmlNodePtr node, const xmlChar *name) +{ + xmlNodePtr subnode; + + subnode = node->xmlChildrenNode; + while (subnode != NULL) { + if (xmlStrcmp(subnode->name, name) == 0) + break; + subnode = subnode->next; + } + + return (subnode); +} + +/* + * parse_xml_get_ldom_name + * + * This function find the <ldom_name> tag within the input xml document + * and return the LDom name in that tag, or NULL if the tag does not exist. + * + * Input: + * node - An xmlNodePtr to the xml data root node + * + * Output: + * A ptr to the LDom name (caller must free memory when done) + * NULL if no <ldom_name> tag or content is found + */ +char * +parse_xml_get_ldom_name(xmlNodePtr node) +{ + char *ldomName = NULL; + xmlChar *content = NULL; + xmlNodePtr subnode; + + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_ldom_name(ENTER) \n"); + if (ldoms_debug) dprt(" DBG: ENTER: nodeName=%s, nodeType=%d\n",node->name,node->type); + + /* Find the <ldom_name> tag and get its content, which is the LDom name */ + subnode = xmlFindSubnode(node, XML_CMD); + if (subnode != NULL) { + subnode = xmlFindSubnode(subnode, XML_DATA); + if (subnode != NULL) { + subnode = xmlFindSubnode(subnode, LDOM_NODE); + if (subnode != NULL) { + subnode = xmlFindSubnode(subnode, XML_LDM_INFO); + if (subnode != NULL) { + subnode = xmlFindSubnode(subnode, XML_LDM_NAME); + if (subnode != NULL) { + content = xmlNodeGetContent(subnode); + ldomName = strdup((const char *)content); + } + } + } + } + } + + + if (ldomName == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_ldom_name() XML file does not have <ldom_name> tag\n"); + return(NULL); + } + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_ldom_name(EXIT) LDom Name = %s\n",ldomName); + + return (ldomName); +} /* parse_xml_get_ldom_name */ + +/* + * parse_xml_get_ldom_state + * + * This function converts the ldom state to the enum value. + * + * Input: + * node - pointer to the xml node that contains the ldom state data + * + * Returns: + * following ldom state enum value if the operation is successful + * 1 = active LDOM_STATE_ACTIVE + * 2 = stopping LDOM_STATE_STOPPING + * 3 = inactive LDOM_STATE_INACTIVE + * 4 = binding LDOM_STATE_BINDING + * 5 = unbinding LDOM_STATE_UNBINDING + * 6 = bound LDOM_STATE_BOUND + * 7 = starting LDOM_STATE_STARTING + * + * -1 if the operation fails + * + */ +int +parse_xml_get_ldom_state(xmlNodePtr node) +{ + xmlNodePtr state_node = NULL; + xmlChar *content = NULL; + int ldom_state = 0; + + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_ldom_state(ENTER)\n"); + state_node = xml_find_subnode(node, STATE_NODE); + if (state_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_ldom_state.. XML file does not have <state> tag\n"); + return (-1); + } + + content = xmlNodeGetContent(state_node); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_ldom_state.. state=%s\n", content); + + if (strcmp((char *)content, "active") == 0) + ldom_state = LDOM_STATE_ACTIVE; + else if (strcmp((char *)content, "stopping") == 0) + ldom_state = LDOM_STATE_STOPPING; + else if (strcmp((char *)content, "inactive") == 0) + ldom_state = LDOM_STATE_INACTIVE; + else if (strcmp((char *)content, "binding") == 0) + ldom_state = LDOM_STATE_BINDING; + else if (strcmp((char *)content, "unbinding") == 0) + ldom_state = LDOM_STATE_UNBINDING; + else if (strcmp((char *)content, "bound") == 0) + ldom_state = LDOM_STATE_BOUND; + else if (strcmp((char *)content, "starting") == 0) + ldom_state = LDOM_STATE_STARTING; + + xmlFree(content); + + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_ldom_state(EXIT): state=%d\n",ldom_state); + return(ldom_state); +} /* parse_xml_get_ldom_state */ + + +/* + * parse_xml_get_response_status + * + * This function checks for the <response> <status> tags in the XML response. + * There are <response><status> tags associated with the document, <cmd> and + * <data> tags. This function is general to be able to check for + * <response><status> for any of these. If the <status> of the document is + * 'success', then the other <response><status> tags do not need to be checked. + * + * Input: + * node - pointer to the xml node to check the response status + * + * Returns: + * 0 if the response status is success + * -1 if the response status is failure or the operation fails + * + * <?xml version="1.0"?> + * <LDM_interface version="1.0"> + * <cmd> + * <action>bind-domain</action> + * <data version="2.0"> + * <response> + * <status>failure</status> + * <resp_msg>ldom does not have ldom_name tag</resp_msg> + * </response> + * </data> + * <response> + * <status>failure</status> + * </response> + * </cmd> + * <response> + * <status>failure</status> + * </response> + * </LDM_interface> + */ +int +parse_xml_get_response_status(xmlNodePtr node) +{ + xmlNodePtr response_node = NULL; + xmlNodePtr status_node = NULL; + xmlNodePtr resp_msg_node = NULL; + xmlNodePtr cmd_node = NULL; + xmlNodePtr cmd_child_node = NULL; + xmlNodePtr data_child_node = NULL; + xmlNodePtr respmsg_node = NULL; + xmlChar *content = NULL; + + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_response_status(ENTER)\n"); + + response_node = xml_find_subnode(node, XML_RESPONSE); + if (response_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_response_status.. XML file does not have <response> tag\n"); + return (-1); + } + + + status_node = xml_find_subnode(response_node, XML_STATUS); + if (status_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_response_status.. XML file does not have <status> tag within <response>\n"); + return (-1); + } + + content = xmlNodeGetContent(status_node); + + /* + * The <status> tag must indicate 'success', otherwise we cannot process + * anymore or the XML response because there has been some type of error. + */ + if (strcmp((char *)content, XML_SUCCESS) == 0) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_response_status() <response><status> is success\n"); + xmlFree(content); + } + + /* XML request failed, get the reason why, <resp_msg> */ + else { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_response_status() <response><status>=%s\n", content); + xmlFree(content); + content = NULL; + + /* response status is not success, get the response msg. + * The <resp_msg> tag is within the <data> tag, not the <cmd> + * or <LDM_interface> tags, so drill down to the first <data> + * tag and get the <response><resp_msg> tag contents to display + * in an error message + */ + + /* + * Get the <cmd> node first, then get a list of the child nodes. + * There can be multiple <data></data> nodes, which are peers, + * but usually there is only 1 <data> node, but code for multiple + */ + cmd_node = xml_find_subnode(node, XML_CMD); + cmd_child_node = cmd_node->xmlChildrenNode; + while (cmd_child_node != NULL) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_response_status() <cmd> child=%s\n",cmd_child_node->name); + /* Is current child node a <data> */ + if (strcmp((const char*)cmd_child_node->name,(const char*)XML_DATA) != 0) { + cmd_child_node = cmd_child_node->next; + continue; + } + + /* Found a <data> node, so get the list of child nodes */ + data_child_node = cmd_child_node->xmlChildrenNode; + while (data_child_node != NULL) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_response_status() <data> child=%s\n",data_child_node->name); + /* Is current child node a <response> */ + if (strcmp((const char*)data_child_node->name,(const char*)XML_RESPONSE) != 0) { + data_child_node = data_child_node->next; + continue; + } + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_response_status() Found <response>\n"); + + /* Found a <response> node, so get the <resp_msg> node */ + respmsg_node = xml_find_subnode(data_child_node, XML_RESP_MSG); + if (respmsg_node != NULL) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_response_status() Found <resp_msg>\n"); + content = xmlNodeGetContent(respmsg_node); + if (content != NULL) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_response_status() <resp_msg>=%s\n", content); + cmd_child_node = NULL; + } + } + /* No more <response> nodes in the <data> node */ + break; + } + } + + if (content != NULL) { + ldomsError(NULL, NULL, VIR_ERR_OPERATION_FAILED, (const char*)content, VIR_ERR_ERROR); + xmlFree(content); + } + return (-1); + } + + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_response_status(EXIT)\n"); + + return(0); +} /* parse_xml_get_response_status */ + +/* + * parse_xml_get_ldom_cnt + * + * This functions gets the number of ldoms from the input xml file + * by counting the <ldom> tags. + * + * Input: + * xml doc - XML document to read + * + * Output: + * ldom_cnt - number of ldoms + * + * Returns: + * 0 if the operation is successful + * -1 if the operation fails + * + * NOTE: + * Each <ldom> tag comes within the <data> tag. + * + * Example XML output from the LDOM Manager for the "list-domain" request: + * + * <?xml version="1.0"?> + * <LDM_interface version="1.0"> + * <cmd> + * <action>list-domain</action> + * <data version="2.0"> + * <ldom> + * <ldom_info> + * <ldom_name>primary</ldom_name> + * <state>active</state> + * </ldom_info> + * </ldom> + * <response> + * <status>success</status> + * </response> + * </data> + * <data version="2.0"> + * <ldom> + * <ldom_info> + * <ldom_name>ldg1</ldom_name> + * <state>bound</state> + * </ldom_info> + * </ldom> + * <response> + * <status>success</status> + * </response> + * </data> + * <data version="2.0"> + * <ldom> + * <ldom_info> + * <ldom_name>ldg3</ldom_name> + * <state>bound</state> + * </ldom_info> + * </ldom> + * <response> + * <status>success</status> + * </response> + * </data> + * <response> + * <status>success</status> + * </response> + * </cmd> + * <response> + * <status>success</status> + * </response> + * </LDM_interface> + * + * for the following LDOM Manager CLI output: + * + * # ldm list + * Name State Flags Cons VCPU Memory Util Uptime + * primary active -t-cv 4 1G 0.0% 0s + * ldg1 bound ----v 5000 4 1G + * ldg3 bound ----v 5001 4 1G + */ +int +parse_xml_get_ldom_cnt(xmlDoc *doc, int *ldom_cnt) +{ + char *ldm_name = NULL; + int ret = 0; + int num_ldoms = 0; + xmlNodePtr root_node = NULL; + xmlNodePtr cmd_node = NULL; + xmlNodePtr data_node = NULL; + xmlNodePtr ldom_node = NULL; + xmlNodePtr response_node = NULL; + xmlNodePtr status_node = NULL; + xmlNodePtr subnode; + xmlChar *content = NULL; + + *ldom_cnt = 0; + + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_ldom_cnt(ENTER)\n"); + + root_node = xmlDocGetRootElement(doc); + + if (parse_xml_get_response_status(root_node) == -1) + return(-1); + + cmd_node = xml_find_subnode(root_node, XML_CMD); + if (cmd_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_ldom_cnt.. XML file does not have <cmd> tag\n"); + return (-1); + } + + data_node = xml_find_subnode(cmd_node, XML_DATA); + + /* We did not find a data section. No work can be done without this */ + if (data_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_ldom_cnt.. XML file does not have <data> tag\n"); + return (-1); + } + + /* + * There will be a <data></data> tag pair for each <ldom><ldom_info> pair. + * We don't care about the <status> tags with the <data> tags becuase we + * already checked the outermost <status> + */ + while (1) { + + ldom_node = xml_find_subnode(data_node, LDOM_NODE); + + if (ldom_node != NULL) { + subnode = ldom_node->xmlChildrenNode; + + while (subnode != NULL) { + if (subnode->type != XML_ELEMENT_NODE) { + subnode = subnode->next; + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_ldom_cnt.. found non-element node under <ldom_info> continue\n"); + continue; + } + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_ldom_cnt.. found element node %s\n",subnode->name); + + if (xmlStrcmp(subnode->name, (const xmlChar *)XML_LDM_INFO) == 0) { + subnode = subnode->next; + ++num_ldoms; + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_ldom_cnt.. found <ldom_info> ldom_cnt=%d\n", num_ldoms); + + /* I think we can break at this point instead of continue. View the debug + to verify this. */ + continue; + } + subnode = subnode->next; + } + } + + + /* + * xml response for list-domain has <data> tags for each ldom info, + * so get the next data section + */ + data_node = xml_get_next_ele_node(data_node); + + if (data_node == NULL) + break; + } + + *ldom_cnt = num_ldoms; + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_ldom_cnt(EXIT): ldom_cnt=%d\n", num_ldoms); + + return (ret); +} /* parse_xml_get_ldom_cnt() */ + + +/* + * parse_xml_get_mem_rp + * + * This function gets the memory resource pool data (quantity and unit) by parsing the + * XML output from the LDOM Manager. + * + * Input: + * xml doc - XML document to read + * "list-devices" xml output for capacity data and + * "list-bindings" xml output for reserved data + * rp_type - indicates either capacity or reserved + * RP_CAPACITY = capacity resource + * RP_RESERVED = reserved resource + * + * Output (pointers are used to pass information from this function to the caller): + * rp_qty - pointer to the integer for the resource pool quantity + * unit - pointer to the allocation unit + * + * Returns: + * 0 if the operation is successful + * -1 if the operation fails + * + * NOTE: + * This function will tally up the available (with <free> tags in the "list-devices" + * xml output) and reserved (with <binding> tags in the "list-bindings" xml output) + * for the memory resource pool. + * + * Example - portion of the XML document that this function will parse to get the + * memory resource pool info: + * + * for capacity resource: + * <LDM_interface version="1.0"> + * <cmd> + * <action>list-devices</action> + * <data version="2.0"> + * <ldom> + * <ldom_info> + * <ldom_name>primary</ldom_name> + * </ldom_info> + * <memory> + * <free> + * <phys_addr>0xc4800000</phys_addr> + * <size>29624M</size> + * </free> + * </memory> + *.. + * for reserved resource: + * <LDM_interface version="1.0"> + * <cmd> + * <action>list-bindings</action> + * <data version="2.0"> + * <ldom> + * <ldom_info> + * <ldom_name>primary</ldom_name> + * </ldom_info> + * <memory> + * <size>1G</size> + * <binding> + * <real_addr>0x4000000</real_addr> + * <phys_addr>0x4000000</phys_addr> + * <size>1G</size> + * </binding> + * </memory> + * ... + * + */ +int +parse_xml_get_mem_rp(xmlDoc *doc, int rp_type, ulong_t *rp_qty, int *unit) +{ + int ret = 0; + xmlNodePtr root_node = NULL; + xmlNodePtr cmd_node = NULL; + xmlNodePtr data_node = NULL; + xmlNodePtr ldom_node = NULL; + xmlNodePtr memory_node = NULL; + xmlNodePtr phys_addr_node = NULL; + xmlNodePtr size_node = NULL; + xmlChar *content = NULL; + xmlChar *search_tag; + + xmlNodePtr subnode; + + unsigned long long size = 0; + unsigned long long total_size = 0; + + /* use GB as default, but change it to smaller unit if found */ + int mem_unit = LDOMMEMUNIT_GIGABYTES ; + char *endp; + + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_mem_rp (ENTER) \n"); + + root_node = xmlDocGetRootElement(doc); + + if (parse_xml_get_response_status(root_node) == -1) + return(-1); + + /* for capacity, look for <free> tag and + * for reserved, look for <binding> tag */ + if (rp_type == RP_CAPACITY) + search_tag = XML_FREE; + else + search_tag = XML_BINDING; + + cmd_node = xml_find_subnode(root_node, XML_CMD); + if (cmd_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_mem_rp.. XML file does not have <cmd> tag\n"); + return (-1); + } + + data_node = xml_find_subnode(cmd_node, XML_DATA); + if (data_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_mem_rp.. XML file does not have <data> tag\n"); + return (-1); + } + + /* get capacity data from list-devices which does not have <ldom> tag + * and get reserved data from list-bindings which has <ldom> tag + */ + if (rp_type == RP_RESERVED) { + ldom_node = xml_find_subnode(data_node, LDOM_NODE); + if (ldom_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_mem_rp.. XML file does not have <ldom> tag\n"); + return (-1); + } + } + + while (1) { + + if (rp_type == RP_CAPACITY) + memory_node = xml_find_subnode(data_node, MEMORY_NODE); + else + memory_node = xml_find_subnode(ldom_node, MEMORY_NODE); + + if (memory_node == NULL) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_mem_rp.. XML file does not have <mem> tag\n"); + } + + if (memory_node != NULL) { + subnode = memory_node->xmlChildrenNode; + while (subnode != NULL) { + if (subnode->type != XML_ELEMENT_NODE) { + subnode = subnode->next; + continue; + } + + if (xmlStrcmp(subnode->name, search_tag) == 0) { + + if (rp_type == 1) + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_mem_rp.. <free>tag found\n"); + else + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_mem_rp.. <binding>tag found\n"); + + + /* get phys addr data */ + phys_addr_node = xml_find_subnode(subnode, XML_PHYS_ADDR); + if (phys_addr_node != NULL) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_mem_rp.. <phys_addr>tag found\n"); + content = xmlNodeGetContent(phys_addr_node); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_mem_rp.. phys_addr=%s\n", content); + xmlFree(content); + + } + + /* get mem size data */ + size_node = xml_find_subnode(subnode, SIZE_NODE); + if (size_node != NULL) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_mem_rp.. <size>tag found\n"); + content = xmlNodeGetContent(size_node); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_mem_rp.. size=%s\n", content); + + /* extract the number and unit from the content */ + /* and calculate memory size in the smallest unit - "bytes" */ + size = strtoull((char *)content, &endp, 0); + if (size <= 0) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_mem_rp.. Invalid mem size specified: %s\n", content); + } + xmlFree(content); + + /* get the memory unit */ + /* and calculate the size in "bytes" which is the possible smallest memory unit */ + switch (strlen(endp)) { + case 0: break; + default: + switch (endp[strlen(endp)-1]) { + case 'G': + case 'g': + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_mem_rp.. unit in GB\n"); + size = size * MEM_GB_BYTES; + break; + case 'M': + case 'm': + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_mem_rp.. unit in MB\n"); + size = size * MEM_MB_BYTES; + /* if megabyates is the smallest unit used so far, + * change the mem_unit to "megabyates" */ + if (mem_unit == LDOMMEMUNIT_GIGABYTES) + mem_unit = LDOMMEMUNIT_MEGABYTES; + break; + case 'K': + case 'k': + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_mem_rp.. unit in KB\n"); + size = size * MEM_KB_BYTES; + /* if kilobytes is the smallest unit used so far, + * change the mem_unit to "kilobytes" */ + if ((mem_unit == LDOMMEMUNIT_GIGABYTES) || + (mem_unit == LDOMMEMUNIT_MEGABYTES)) + mem_unit = LDOMMEMUNIT_KILOBYTES; + break; + default: + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_mem_rp..unit in bytes\n"); + /* if no memory unit is specified, use the default unit "bytes", + * and set the smallest unit used to "bytes" */ + mem_unit = LDOMMEMUNIT_BYTES; + break; + } + break; + } + + /* add up each strand rp_qty to calculate the total rp_qty */ + total_size = total_size + size; + + if (ldoms_detailed_debug) dprt( + "LDOMS_DETAILED_DEBUG: parse_xml_get_mem_rp.. size=%llu total_size=%llu in bytes\n", + size, total_size); + } + + + subnode = subnode->next; + continue; + } + + subnode = subnode->next; + } + + } + + /* xml response for list has <data> tags for each <ldom_info> + * so, get the next data section + */ + data_node = xml_get_next_ele_node(data_node); + + if (data_node == NULL) + break; + + if (rp_type == RP_RESERVED) { + ldom_node = xml_find_subnode(data_node, LDOM_NODE); + if (ldom_node == NULL) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_mem_rp.. XML file does not have <ldom> tag\n"); + break; + } + } + + } + + + + /* recalculate mem size using the selected unit which is smallest unit in use */ + if (mem_unit == LDOMMEMUNIT_BYTES) + *rp_qty = total_size; + else if (mem_unit == LDOMMEMUNIT_KILOBYTES) + *rp_qty = total_size / MEM_KB_BYTES; + else if (mem_unit == LDOMMEMUNIT_MEGABYTES) + *rp_qty = total_size / MEM_MB_BYTES; + else if (mem_unit == LDOMMEMUNIT_GIGABYTES) + *rp_qty = total_size / MEM_GB_BYTES; + + *unit = mem_unit; + + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_mem_rp(EXIT): size=%llu in unit=%d\n", + total_size, mem_unit); + + return(0); + +} /* parse_xml_get_mem_rp */ + + +/* + * parse_xml_get_cpu_rp + * + * This function gets the total number of CPUs (either free or bound depending on + * the rp_type) by parsing the XML output from the LDOM Manager. + * + * XX - copied from the MIB code, but changed to retrieve the number of + * CPUs instead of the CPU resource pool quantity. + * + * Input: + * xml doc - XML document to read + * "list-devices" xml output for capacity data and + * "list-bindings" xml output for reserved data + * rp_type - indicates either capacity or reserved + * RP_CAPACITY = capacity resource + * RP_RESERVED = reserved resource + * + * Output (pointers are used to pass information from this function to the caller): + * rp_qty - pointer to the integer for the number of CPUs + * unit - pointer to the allocation unit (always using 1 for Mhz) + * + * Returns: + * 0 if the operation is successful + * -1 if the operation fails + * + * NOTE: + * This function will tally up the number of free (with <free> tags in the "list-devices" + * xml output) and reserved (with <binding> tags in the "list-bindings" xml output) CPUs. + * + * Example - portion of the XML document that this function will parse to get the + * CPU resource pool info: + * + * for capacity resource: + * <LDM_interface version="1.0"> + * <cmd> + * <action>list-devices</action> + * <data version="2.0"> + * <cpu> + * <free> + * <pid>12</pid> + * <strand_percent>100</strand_percent> + * </free> + * <free> + * <pid>13</pid> + * <strand_percent>100</strand_percent> + * </free> + *.. + *.. + * for reserved resource: + * <LDM_interface version="1.0"> + * <cmd> + * <action>list-bindings</action> + * <data version="2.0"> + * <ldom> + * <ldom_info> + * <ldom_name>primary</ldom_name> + * </ldom_info> + * <cpu> + * <number>4</number> + * <binding> + * <vid>0</vid> + * <pid>0</pid> + * <strand_percent>100</strand_percent> + * </binding> + * ... + * + */ +int +parse_xml_get_cpu_rp(xmlDoc *doc, int rp_type, ulong_t *rp_qty, int *unit) +{ + int ret = 0; + xmlNodePtr root_node = NULL; + xmlNodePtr cmd_node = NULL; + xmlNodePtr data_node = NULL; + xmlNodePtr ldom_node = NULL; + xmlNodePtr cpu_node = NULL; + xmlNodePtr pid_node = NULL; + xmlNodePtr strand_percent_node = NULL; + xmlChar *content = NULL; + xmlChar *search_tag; + + xmlNodePtr subnode; + + ulong_t strand_rp_qty = 0; + ulong_t total_rp_qty = 0; + + int cpu_unit = 1; /* default MHz */ + + int ncpu = 0; + + /* + * Get the processor clock speed using processor_info() + * which takes processor id as input and returns the status of + * the processor in the processor_info_t structure. + * + * The pi_clock member is the processor clock frequency rounded + * to the nearest MHz. It may be 0 if not known. + * + * Since processor_info doesn't provide the guest domain's + * processor info, we just need to find a CPU on the control + * domain, but we don't know which physical CPUs the control + * domain has. So just loop on the 1st 64, which should contain + * at least 1 CPU in the control domain. + */ + processor_info_t cpu_info; + int pid = 0; /* use primary domain's processor id */ + int p_clock = 1600; /* processor clock speed in MHz */ + /* use 1600 MHz as default */ + + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_cpu_rp(ENTER)\n"); + + + /* get the processor clock speed in MHz */ + while ((pid < 64) && (processor_info(pid++, &cpu_info) != 0) ); + + /* get the processor clock speed in MHz */ + if (pid <= 64) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsNodeGetInfo(). processor_info with pid=%d clock=%d\n", + pid, cpu_info.pi_clock); + p_clock = cpu_info.pi_clock; + } + + root_node = xmlDocGetRootElement(doc); + + if (parse_xml_get_response_status(root_node) == -1) + return(-1); + + /* for capacity, look for <free> tag and + * for reserved, look for <binding> tag */ + if (rp_type == 1) + search_tag = XML_FREE; + else + search_tag = XML_BINDING; + + cmd_node = xml_find_subnode(root_node, XML_CMD); + if (cmd_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_cpu_rp.. XML file does not have <cmd> tag\n"); + return (-1); + } + + data_node = xml_find_subnode(cmd_node, XML_DATA); + if (data_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_cpu_rp.. XML file does not have <data> tag\n"); + return (-1); + } + + /* get capacity data from list-devices which does not have <ldom> tag + * and get reserved data from list-bindings which has <ldom> tag + */ + if (rp_type == RP_RESERVED) { + ldom_node = xml_find_subnode(data_node, LDOM_NODE); + if (ldom_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_cpu_rp.. XML file does not have <ldom> tag\n"); + return (-1); + } + } + + while (1) { + + if (rp_type == RP_CAPACITY) + cpu_node = xml_find_subnode(data_node, CPU_NODE); + else + cpu_node = xml_find_subnode(ldom_node, CPU_NODE); + + if (cpu_node == NULL) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_cpu_rp.. XML file does not have <cpu> tag\n"); + } + + if (cpu_node != NULL) { + + subnode = cpu_node->xmlChildrenNode; + while (subnode != NULL) { + if (subnode->type != XML_ELEMENT_NODE) { + subnode = subnode->next; + continue; + } + + + if (xmlStrcmp(subnode->name, search_tag) == 0) { + + if (rp_type == RP_CAPACITY) + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_cpu_rp.. <free>tag found\n"); + else + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_cpu_rp.. <binding>tag found\n"); + + /* add up the number of CPUs */ + ncpu++; + + + subnode = subnode->next; + continue; + } + + subnode = subnode->next; + } + + } + + + /* xml response for list has <data> tags for each <ldom_info> + * so, get the next data section + */ + data_node = xml_get_next_ele_node(data_node); + + if (data_node == NULL) + break; + + if (rp_type == RP_RESERVED) { + ldom_node = xml_find_subnode(data_node, LDOM_NODE); + if (ldom_node == NULL) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_cpu_rp.. XML file does not have <ldom> tag\n"); + break; + } + } + + } + + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_cpu_rp(EXIT): total number of CPUs=%d frequency (in Mhz)=%d\n", ncpu, p_clock); + + *rp_qty = ncpu; + *unit = cpu_unit; + + return(0); +} /* parse_xml_get_cpu_rp */ + +/* + * parse_xml_get_cpu_bindings + * + * This function gets the CPU binding info for a domain. + * Specifically, the real and virtual CPU ids, the State for the CPU, + * and the CPU uptime, which is pretty much the same as the uptime of + * the Domain. + * + * Input: + * xml_received - XML document to read + * cpuBindings - Pointer to a structure to hold the binding info + * + * Output + * cpuBindings - Pointer to a structure with the binding info + * + * Returns: + * 0 if the operation is successful + * -1 if the operation fails + * + * Example XML response from a list-bindings request for a domain + * <LDM_interface version="1.0"> + * <cmd> + * <action>list-bindings</action> + * <data version="2.0"> + * <ldom> + * <ldom_info> + * <ldom_name>primary</ldom_name> + * </ldom_info> + * <cpu> + * <number>4</number> + * <binding> + * <vid>0</vid> + * <pid>0</pid> + * <strand_percent>100</strand_percent> + * </binding> + * ... + * + */ +int +parse_xml_get_cpu_bindings(xmlDoc *doc, cpuBindings_t **cpuBindings) +{ + cpuBindings_t *headcpuBindings = NULL; + cpuBindings_t *newBinding = NULL; + cpuBindings_t *prevBinding = NULL; + + xmlNodePtr root_node = NULL; + xmlNodePtr cmd_node = NULL; + xmlNodePtr data_node = NULL; + xmlNodePtr ldom_node = NULL; + xmlNodePtr cpu_node = NULL; + xmlNodePtr id_node = NULL; + xmlChar *virtID = NULL; + xmlChar *realID = NULL; + xmlNodePtr subnode; + + int ret = 0; + int numCpus = 0; + int ncpu = 0; + + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_cpu_bindings(ENTER)\n"); + root_node = xmlDocGetRootElement(doc); + + if (parse_xml_get_response_status(root_node) == -1) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_cpu_bindings() no response/status tags in the XML rsp\n"); + return(-1); + } + + cmd_node = xml_find_subnode(root_node, XML_CMD); + if (cmd_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_cpu_bindings() XML file does not have <cmd> tag\n"); + return (-1); + } + + data_node = xml_find_subnode(cmd_node, XML_DATA); + if (data_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_cpu_bindings() XML file does not have <data> tag\n"); + return (-1); + } + + ldom_node = xml_find_subnode(data_node, LDOM_NODE); + if (ldom_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_cpu_bindings() XML file does not have <ldom> tag\n"); + return (-1); + } + + cpu_node = xml_find_subnode(ldom_node, CPU_NODE); + + if (cpu_node == NULL) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_cpu_bindings() XML file does not have <cpu> tag\n"); + return(-1); + } + + subnode = cpu_node->xmlChildrenNode; + while (subnode != NULL) { + + if (xmlStrcmp(subnode->name, XML_BINDING) == 0) { + + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_cpu_bindings() <binding>tag found\n"); + + + /* Allocate a new cpuBindings struct to hold the info for the binding. + * This is regular single linked list stuff. + */ + if (headcpuBindings == NULL) { + newBinding = malloc(sizeof(cpuBindings_t)); + headcpuBindings = newBinding; + prevBinding = newBinding; + newBinding->next = NULL; + } + else { + newBinding = malloc(sizeof(cpuBindings_t)); + prevBinding->next = newBinding; + newBinding->next = NULL; + prevBinding = newBinding; + } + + /* Get the <vid> node and its value. subnode points to <binding> */ + id_node = subnode->xmlChildrenNode; + virtID = xmlNodeGetContent(id_node); + newBinding->virt = (unsigned int)atoi((const char *)virtID); + xmlFree(virtID); + + /* Get the <pid> node and its value */ + id_node = id_node->next; + realID = xmlNodeGetContent(id_node); + newBinding->real = (int)atoi((const char *)realID); + xmlFree(realID); + + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: parse_xml_get_cpu_bindings() virt/real = %d/%d \n",newBinding->virt, newBinding->real); + + numCpus++; + + } + + subnode = subnode->next; + } /* while */ + + *cpuBindings = headcpuBindings; + return(numCpus); +} /* parse_xml_get_cpu_bindings */ + + +#endif /* WITH_LDOMS */ + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ +

On Tue, Apr 08, 2008 at 10:49:01AM -0700, Ryan Scott wrote: [...] Hi Ryan, Thanks for adding LDom support - obviously an important contribution for libvirt and when I get my Solaris box working again I'll be able to try it out. For the new files forming the LDom driver, it all looks well-contained and I just need to check that it doesn't prevent compilation on other platforms.
src/ldoms_common.h src/ldoms_internal.h src/ldoms_internal.c src/ldoms_intfc.h src/ldoms_intfc.c src/ldoms_xml_parse.h src/ldoms_xml_parse.c
However I have some concerns about some of the modified "core code" files in libvirt, ie:
src/libvirt.c src/virsh.c src/virterror.c src/driver.h include/libvirt/libvirt.h.in
which I'll discuss inline below.
@@ -1794,11 +1802,17 @@ virDomainGetUUID(virDomainPtr domain, un return (-1); }
+#ifndef WITH_LDOMS if (domain->id == 0) { memset(uuid, 0, VIR_UUID_BUFLEN); } else { memcpy(uuid, &domain->uuid[0], VIR_UUID_BUFLEN); } +#endif + +#ifdef WITH_LDOMS + memcpy(uuid, &domain->uuid[0], VIR_UUID_BUFLEN); +#endif return (0); }
I guess this is working around the Xen assumption that dom0 has UUID 0000-0000-0000-0000, so it exposes a Xen-ism in the code. This should move down to the Xen driver, so I'll propose a patch to fix that, which should remove the need for the specific #ifdef here.
@@ -5025,6 +5039,42 @@ virStorageVolGetPath(virStorageVolPtr vo return NULL; }
+#ifdef WITH_LDOMS +/** + * virLDomConsole: + * @domain: the domain if available [...]
I think having a generic "get console" call in libvirt might be a good idea, but having public LDom-specific calls isn't. Dan Berrange will correct me if I'm wrong here, but currently the method used is to dump the domain XML of a domain and look for the <console/> or <graphics/> element within <devices/>, corresponding to the serial console or the graphical (VNC) console respectively. For example it might look like: <graphics type='vnc' port='5904'/> (see full example at http://libvirt.org/format.html). So can you add a similar device to the LDom domain XML to avoid the need for this new call?
From reading the virsh code it looks like virLDomConsole is meant to return the telnet port number for the serial console(?) so perhaps something like:
<console port='1234'/>
diff --git a/src/virsh.c b/src/virsh.c --- a/src/virsh.c +++ b/src/virsh.c @@ -494,6 +494,11 @@ cmdConsole(vshControl * ctl, vshCmd * cm virDomainPtr dom; int ret = FALSE; char *doc; +#ifdef WITH_LDOMS + int port; + char command[80]; +#endif +
if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) return FALSE; @@ -501,6 +506,19 @@ cmdConsole(vshControl * ctl, vshCmd * cm if (!(dom = vshCommandOptDomain(ctl, cmd, "domain", NULL))) return FALSE;
+#ifdef WITH_LDOMS + port = virLDomConsole(dom); + if (port > 0) { + sprintf(command, "%s %d &", + "/usr/X/bin/xterm -sb -sl 1000 -e telnet localhost ", port); + system(command); + return TRUE; + } + + vshError(ctl, FALSE, _("Failed to start console")); + return FALSE; +#endif + doc = virDomainGetXMLDesc(dom, 0); if (!doc) goto cleanup;
See discussion above.
@@ -1003,13 +1021,21 @@ cmdUndefine(vshControl * ctl, vshCmd * c */ static vshCmdInfo info_start[] = { {"syntax", "start <domain>"}, +#ifdef WITH_LDOMS + {"help", gettext_noop("start an inactive or bound domain")}, +#else {"help", gettext_noop("start a (previously defined) inactive domain")}, +#endif {"desc", gettext_noop("Start a domain.")}, {NULL, NULL} };
static vshCmdOptDef opts_start[] = { +#ifdef WITH_LDOMS + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("name of the inactive or bound domain")}, +#else {"name", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("name of the inactive domain")}, +#endif {NULL, 0, 0, NULL} };
The problem with changes lie the above is that they fractionalize virsh into LDom and non-LDom variants. Can we come up with a middle ground help text and avoid changing the option name?
+#ifndef WITH_LDOMS vshPrint(ctl, "%-15s ", _("CPU Affinity:")); for (m = 0 ; m < VIR_NODEINFO_MAXCPUS(nodeinfo) ; m++) { vshPrint(ctl, "%c", VIR_CPU_USABLE(cpumap, cpumaplen, n, m) ? 'y' : '-'); } vshPrint(ctl, "\n"); +#endif
Instead of changes like this, just return an empty affinity map (if LDoms doesn't support this??).
@@ -5087,19 +5124,23 @@ cmdQuit(vshControl * ctl, vshCmd * cmd A */ static vshCmdDef commands[] = { {"help", cmdHelp, opts_help, info_help}, +#ifndef WITH_LDOMS {"attach-device", cmdAttachDevice, opts_attach_device, info_attach_device}, {"attach-disk", cmdAttachDisk, opts_attach_disk, info_attach_disk}, {"attach-interface", cmdAttachInterface, opts_attach_interface, info_attach_interface}, {"autostart", cmdAutostart, opts_autostart, info_autostart}, {"capabilities", cmdCapabilities, NULL, info_capabilities}, {"connect", cmdConnect, opts_connect, info_connect}, +#endif /* WITH_LDOMS */ {"console", cmdConsole, opts_console, info_console}, {"create", cmdCreate, opts_create, info_create}, {"start", cmdStart, opts_start, info_start}, {"destroy", cmdDestroy, opts_destroy, info_destroy}, +#ifndef WITH_LDOMS {"detach-device", cmdDetachDevice, opts_detach_device, info_detach_device}, {"detach-disk", cmdDetachDisk, opts_detach_disk, info_detach_disk}, {"detach-interface", cmdDetachInterface, opts_detach_interface, info_detach_interface}, +#endif /* WITH_LDOMS */ {"define", cmdDefine, opts_define, info_define}, {"domid", cmdDomid, opts_domid, info_domid}, {"domuuid", cmdDomuuid, opts_domuuid, info_domuuid}, @@ -5112,7 +5153,9 @@ static vshCmdDef commands[] = { {"freecell", cmdFreecell, opts_freecell, info_freecell}, {"hostname", cmdHostname, NULL, info_hostname}, {"list", cmdList, opts_list, info_list}, +#ifndef WITH_LDOMS {"migrate", cmdMigrate, opts_migrate, info_migrate}, +#endif /* WITH_LDOMS */ [...]
You shouldn't need to comment out unsupported commands. They will return an error if they aren't supported. In fact, QEMU, KVM and OpenVZ only support a subset of the available operations. However if you want to propose a more general patch which allows virsh to determine which operations are supported on the current connection, then I'm all for it. Some of the infrastructure is in place to do this already.
diff --git a/src/virterror.c b/src/virterror.c --- a/src/virterror.c +++ b/src/virterror.c
The changes to virterror are all fine.
diff --git a/src/driver.h b/src/driver.h --- a/src/driver.h +++ b/src/driver.h @@ -24,7 +24,10 @@ typedef enum { VIR_DRV_QEMU = 3, VIR_DRV_REMOTE = 4, VIR_DRV_OPENVZ = 5, - VIR_DRV_LXC = 6 + VIR_DRV_LXC = 6, +#ifdef WITH_LDOMS + VIR_DRV_LDOMS = 7 +#endif } virDrvNo;
This change doesn't need the #ifdef around it. We can add new driver types whenever even if not all platforms will support them. Rest of the changes to driver.h are fine however.
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -549,6 +549,9 @@ typedef enum { VIR_VCPU_OFFLINE = 0, /* the virtual CPU is offline */ VIR_VCPU_RUNNING = 1, /* the virtual CPU is running */ VIR_VCPU_BLOCKED = 2, /* the virtual CPU is blocked on resource */ +#ifdef WITH_LDOMS + VIR_VCPU_UNKNOWN = 3, /* the virtual CPU state is unknown */ +#endif } virVcpuState;
I think this is fine (and the corresponding change to virsh.c to support it). No need for the #ifdef since in theory other drivers could have CPUs in this unknown state too.
diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -56,6 +56,9 @@ typedef enum { VIR_FROM_STATS_LINUX, /* Error in the Linux Stats code */ VIR_FROM_LXC, /* Error from Linux Container driver */ VIR_FROM_STORAGE, /* Error from storage driver */ +#ifdef WITH_LDOMS + VIR_FROM_LDOMS, /* Error from LDoms driver */ +#endif } virErrorDomain;
@@ -139,6 +142,9 @@ typedef enum { VIR_WAR_NO_STORAGE, /* failed to start storage */ VIR_ERR_NO_STORAGE_POOL, /* storage pool not found */ VIR_ERR_NO_STORAGE_VOL, /* storage pool not found */ +#ifdef WITH_LDOMS + VIR_ERR_INVALID_OPTION, /* invalid command line option */ +#endif } virErrorNumber;
/**
Again, no need for #ifdefs here, otherwise this is fine. Changes to Makefile.am, configure.in, look fine. And the rest of the patch contains the new LDom driver code, which is fine because it's isolated to Solaris. These could go in straight away as with the arrangement we have for OpenVZ code. Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones virt-p2v converts physical machines to virtual machines. Boot with a live CD or over the network (PXE) and turn machines into Xen guests. http://et.redhat.com/~rjones/virt-p2v

On Wed, Apr 09, 2008 at 12:52:33PM +0100, Richard W.M. Jones wrote:
On Tue, Apr 08, 2008 at 10:49:01AM -0700, Ryan Scott wrote: [...]
Hi Ryan,
Thanks for adding LDom support - obviously an important contribution for libvirt and when I get my Solaris box working again I'll be able to try it out.
For the new files forming the LDom driver, it all looks well-contained and I just need to check that it doesn't prevent compilation on other platforms.
src/ldoms_common.h src/ldoms_internal.h src/ldoms_internal.c src/ldoms_intfc.h src/ldoms_intfc.c src/ldoms_xml_parse.h src/ldoms_xml_parse.c
However I have some concerns about some of the modified "core code" files in libvirt, ie:
src/libvirt.c src/virsh.c src/virterror.c src/driver.h include/libvirt/libvirt.h.in
I'm in complete agreement with Richard so far, I will post my own review separately.
which I'll discuss inline below.
@@ -1794,11 +1802,17 @@ virDomainGetUUID(virDomainPtr domain, un return (-1); }
+#ifndef WITH_LDOMS if (domain->id == 0) { memset(uuid, 0, VIR_UUID_BUFLEN); } else { memcpy(uuid, &domain->uuid[0], VIR_UUID_BUFLEN); } +#endif + +#ifdef WITH_LDOMS + memcpy(uuid, &domain->uuid[0], VIR_UUID_BUFLEN); +#endif return (0); }
I guess this is working around the Xen assumption that dom0 has UUID 0000-0000-0000-0000, so it exposes a Xen-ism in the code. This should move down to the Xen driver, so I'll propose a patch to fix that, which should remove the need for the specific #ifdef here.
agreed
@@ -5025,6 +5039,42 @@ virStorageVolGetPath(virStorageVolPtr vo return NULL; }
+#ifdef WITH_LDOMS +/** + * virLDomConsole: + * @domain: the domain if available [...]
I think having a generic "get console" call in libvirt might be a good idea, but having public LDom-specific calls isn't. Dan Berrange will
We cannot add APIs specific to one hypervisor, virLDomConsole can't be added to libvirt API.
correct me if I'm wrong here, but currently the method used is to dump the domain XML of a domain and look for the <console/> or <graphics/> element within <devices/>, corresponding to the serial console or the graphical (VNC) console respectively.
yes that's what virsh 'console' code uses, see cmdConsole() in virsh.c
The problem with changes lie the above is that they fractionalize virsh into LDom and non-LDom variants. Can we come up with a middle ground help text and avoid changing the option name?
In general libvirt code should never rely on WITH_LDOMS conditional compilation except for: - the registration of the ldom driver virInitialize() in src/libvirt.c - in the ldom specific files - potentially in some of the storage or xml back-end for a bit of specific processing but really it should never affect virsh.c, or the API files. [...]
You shouldn't need to comment out unsupported commands. They will return an error if they aren't supported. In fact, QEMU, KVM and OpenVZ only support a subset of the available operations.
and that's contrilled at the driver API level by having NULL entry points.
However if you want to propose a more general patch which allows virsh to determine which operations are supported on the current connection, then I'm all for it. Some of the infrastructure is in place to do this already.
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -549,6 +549,9 @@ typedef enum { VIR_VCPU_OFFLINE = 0, /* the virtual CPU is offline */ VIR_VCPU_RUNNING = 1, /* the virtual CPU is running */ VIR_VCPU_BLOCKED = 2, /* the virtual CPU is blocked on resource */ +#ifdef WITH_LDOMS + VIR_VCPU_UNKNOWN = 3, /* the virtual CPU state is unknown */ +#endif } virVcpuState;
I think this is fine (and the corresponding change to virsh.c to support it). No need for the #ifdef since in theory other drivers could have CPUs in this unknown state too.
Adding the new state is fine, the ifdef cannot be included in the header
diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -56,6 +56,9 @@ typedef enum { VIR_FROM_STATS_LINUX, /* Error in the Linux Stats code */ VIR_FROM_LXC, /* Error from Linux Container driver */ VIR_FROM_STORAGE, /* Error from storage driver */ +#ifdef WITH_LDOMS + VIR_FROM_LDOMS, /* Error from LDoms driver */ +#endif } virErrorDomain;
@@ -139,6 +142,9 @@ typedef enum { VIR_WAR_NO_STORAGE, /* failed to start storage */ VIR_ERR_NO_STORAGE_POOL, /* storage pool not found */ VIR_ERR_NO_STORAGE_VOL, /* storage pool not found */ +#ifdef WITH_LDOMS + VIR_ERR_INVALID_OPTION, /* invalid command line option */ +#endif } virErrorNumber;
/**
Again, no need for #ifdefs here, otherwise this is fine.
Agreed,
Changes to Makefile.am, configure.in, look fine.
And the rest of the patch contains the new LDom driver code, which is fine because it's isolated to Solaris. These could go in straight away as with the arrangement we have for OpenVZ code.
Generally agreed, Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

Hi, The following was not included in the ldom patch, but I would appreciate your advice. Xen is the default hypervisor for virsh and if the user doesn't specify in the command line option, the virsh connects to xen. But, for the SPARC platforms, xen is not supported. So I would like to add the #ifdef WITH_LDOMS block in do_open() (in src/libvirt.c) to default to ldoms if the user doesn't specify with --connect when running virsh on the SPARC platforms. static virConnectPtr do_open (const char *name, virConnectAuthPtr auth, int flags) { int i, res; virConnectPtr ret = NULL; xmlURIPtr uri; #ifdef WITH_LDOMS =====> /* Convert NULL or "" to ldoms:/// */ =====> if (!name || name[0] == '\0') name = "ldoms:///"; =====> #else =====> /* Convert NULL or "" to xen:/// for back compat */ if (!name || name[0] == '\0') name = "xen:///"; #endif .. Would this kind of branching in the common code be acceptable? Thanks, Eunice Daniel Veillard wrote:
In general libvirt code should never rely on WITH_LDOMS conditional compilation except for: - the registration of the ldom driver virInitialize() in src/libvirt.c - in the ldom specific files - potentially in some of the storage or xml back-end for a bit of specific processing but really it should never affect virsh.c, or the API files.

On Thu, Apr 10, 2008 at 06:34:31PM -0500, Eunice Moon wrote:
Hi,
The following was not included in the ldom patch, but I would appreciate your advice.
Xen is the default hypervisor for virsh and if the user doesn't specify in the command line option, the virsh connects to xen. But, for the SPARC platforms, xen is not supported. So I would like to add the #ifdef WITH_LDOMS block in do_open() (in src/libvirt.c) to default to ldoms if the user doesn't specify with --connect when running virsh on the SPARC platforms.
static virConnectPtr do_open (const char *name, virConnectAuthPtr auth, int flags) { int i, res; virConnectPtr ret = NULL; xmlURIPtr uri;
#ifdef WITH_LDOMS =====> /* Convert NULL or "" to ldoms:/// */ =====> if (!name || name[0] == '\0') name = "ldoms:///"; =====> #else =====> /* Convert NULL or "" to xen:/// for back compat */ if (!name || name[0] == '\0') name = "xen:///"; #endif ..
Would this kind of branching in the common code be acceptable?
This particularly area of code has changed since the LDoms code was first written. There is now a 'probe' method in all the internal driver APIs. If you implement the probe driver method for LDoms it will automatically translate NULL / "" into ldoms:/// getting the effect you desire without needing the #ifdefs Regards, Dan. -- |: Red Hat, Engineering, Boston -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 :|

Hi Daniel, That'd be great. I will use that 'probe' method. Is it included in the latest libvirt 0.4.2 release? Thanks, Eunice Daniel P. Berrange wrote:
On Thu, Apr 10, 2008 at 06:34:31PM -0500, Eunice Moon wrote:
Hi,
The following was not included in the ldom patch, but I would appreciate your advice.
Xen is the default hypervisor for virsh and if the user doesn't specify in the command line option, the virsh connects to xen. But, for the SPARC platforms, xen is not supported. So I would like to add the #ifdef WITH_LDOMS block in do_open() (in src/libvirt.c) to default to ldoms if the user doesn't specify with --connect when running virsh on the SPARC platforms.
static virConnectPtr do_open (const char *name, virConnectAuthPtr auth, int flags) { int i, res; virConnectPtr ret = NULL; xmlURIPtr uri;
#ifdef WITH_LDOMS =====> /* Convert NULL or "" to ldoms:/// */ =====> if (!name || name[0] == '\0') name = "ldoms:///"; =====> #else =====> /* Convert NULL or "" to xen:/// for back compat */ if (!name || name[0] == '\0') name = "xen:///"; #endif ..
Would this kind of branching in the common code be acceptable?
This particularly area of code has changed since the LDoms code was first written. There is now a 'probe' method in all the internal driver APIs. If you implement the probe driver method for LDoms it will automatically translate NULL / "" into ldoms:/// getting the effect you desire without needing the #ifdefs
Regards, Dan.

On Thu, Apr 10, 2008 at 06:44:45PM -0500, Eunice Moon wrote:
Hi Daniel,
That'd be great. I will use that 'probe' method. Is it included in the latest libvirt 0.4.2 release?
yes :-) Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

On Thu, Apr 10, 2008 at 06:44:45PM -0500, Eunice Moon wrote:
Hi Daniel,
That'd be great. I will use that 'probe' method. Is it included in the latest libvirt 0.4.2 release?
Yes, it was added in 0.4.2. We'd prefer patches against CVS since libvirt changes quickly -- there have already been a dozen patches added to CVS since 0.4.2 was released two days ago, including one which made massive changes to all the whitespace, and hence tends to break patches. Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones virt-top is 'top' for virtual machines. Tiny program with many powerful monitoring features, net stats, disk stats, logging, etc. http://et.redhat.com/~rjones/virt-top

On Wed, Apr 09, 2008 at 12:52:33PM +0100, Richard W.M. Jones wrote:
On Tue, Apr 08, 2008 at 10:49:01AM -0700, Ryan Scott wrote:
@@ -1794,11 +1802,17 @@ virDomainGetUUID(virDomainPtr domain, un return (-1); }
+#ifndef WITH_LDOMS if (domain->id == 0) { memset(uuid, 0, VIR_UUID_BUFLEN); } else { memcpy(uuid, &domain->uuid[0], VIR_UUID_BUFLEN); } +#endif + +#ifdef WITH_LDOMS + memcpy(uuid, &domain->uuid[0], VIR_UUID_BUFLEN); +#endif return (0); }
I guess this is working around the Xen assumption that dom0 has UUID 0000-0000-0000-0000, so it exposes a Xen-ism in the code. This should move down to the Xen driver, so I'll propose a patch to fix that, which should remove the need for the specific #ifdef here.
Yeah, I recall discussing this before. IIRC, we determined that the domain object will alwayss have a valid UUID, therefore the explicit memset(uuid, 0, VIR_UUID_BUFLEN); is legacy code & redundant.
@@ -5025,6 +5039,42 @@ virStorageVolGetPath(virStorageVolPtr vo return NULL; }
+#ifdef WITH_LDOMS +/** + * virLDomConsole: + * @domain: the domain if available [...]
I think having a generic "get console" call in libvirt might be a good idea, but having public LDom-specific calls isn't. Dan Berrange will correct me if I'm wrong here, but currently the method used is to dump the domain XML of a domain and look for the <console/> or <graphics/> element within <devices/>, corresponding to the serial console or the graphical (VNC) console respectively.
For example it might look like:
<graphics type='vnc' port='5904'/>
(see full example at http://libvirt.org/format.html). So can you add a similar device to the LDom domain XML to avoid the need for this new call?
From reading the virsh code it looks like virLDomConsole is meant to return the telnet port number for the serial console(?) so perhaps something like:
<console port='1234'/>
The <console> tag only allows for PTYs currently, but I'm parttime hacking on this more general proposal too http://www.redhat.com/archives/libvir-list/2007-May/msg00191.html which allows for TCP access to console, so I think we can come up with something which works for LDoms too. Dan. -- |: Red Hat, Engineering, Boston -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 :|

Hi Richard, Thanks for your feedback. Please see my responses inline below. Eunice Richard W.M. Jones wrote:
On Tue, Apr 08, 2008 at 10:49:01AM -0700, Ryan Scott wrote: [...]
Hi Ryan,
Thanks for adding LDom support - obviously an important contribution for libvirt and when I get my Solaris box working again I'll be able to try it out.
For the new files forming the LDom driver, it all looks well-contained and I just need to check that it doesn't prevent compilation on other platforms.
src/ldoms_common.h src/ldoms_internal.h src/ldoms_internal.c src/ldoms_intfc.h src/ldoms_intfc.c src/ldoms_xml_parse.h src/ldoms_xml_parse.c
However I have some concerns about some of the modified "core code" files in libvirt, ie:
src/libvirt.c src/virsh.c src/virterror.c src/driver.h include/libvirt/libvirt.h.in
which I'll discuss inline below.
@@ -1794,11 +1802,17 @@ virDomainGetUUID(virDomainPtr domain, un return (-1); }
+#ifndef WITH_LDOMS if (domain->id == 0) { memset(uuid, 0, VIR_UUID_BUFLEN); } else { memcpy(uuid, &domain->uuid[0], VIR_UUID_BUFLEN); } +#endif + +#ifdef WITH_LDOMS + memcpy(uuid, &domain->uuid[0], VIR_UUID_BUFLEN); +#endif return (0); }
I guess this is working around the Xen assumption that dom0 has UUID 0000-0000-0000-0000, so it exposes a Xen-ism in the code. This should move down to the Xen driver, so I'll propose a patch to fix that, which should remove the need for the specific #ifdef here.
Yes, that #ifdef was added to have the valid UUID (not UUID 0000-0000- 0000-0000) for the primary domain. I will remove the #ifdef here.
@@ -5025,6 +5039,42 @@ virStorageVolGetPath(virStorageVolPtr vo return NULL; }
+#ifdef WITH_LDOMS +/** + * virLDomConsole: + * @domain: the domain if available [...]
I think having a generic "get console" call in libvirt might be a good idea, but having public LDom-specific calls isn't. Dan Berrange will correct me if I'm wrong here, but currently the method used is to dump the domain XML of a domain and look for the <console/> or <graphics/> element within <devices/>, corresponding to the serial console or the graphical (VNC) console respectively.
For example it might look like:
<graphics type='vnc' port='5904'/>
(see full example at http://libvirt.org/format.html). So can you add a similar device to the LDom domain XML to avoid the need for this new call?
From reading the virsh code it looks like virLDomConsole is meant to return the telnet port number for the serial console(?) so perhaps something like:
<console port='1234'/>
For LDoms, we retrieve the console info from the XML file which has different format (looking for the <port> element within <console>). The #ifdef WITH_LDOMS block was added in cmdConsole() in virsh.c to deal with the LDoms specific console code. But, if it is not allowed to add any hypervisor specific code in the libvirt API or virsh.c, I will try to figure out a way to avoid the virLDomConsole.
diff --git a/src/virsh.c b/src/virsh.c --- a/src/virsh.c +++ b/src/virsh.c @@ -494,6 +494,11 @@ cmdConsole(vshControl * ctl, vshCmd * cm virDomainPtr dom; int ret = FALSE; char *doc; +#ifdef WITH_LDOMS + int port; + char command[80]; +#endif +
if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) return FALSE; @@ -501,6 +506,19 @@ cmdConsole(vshControl * ctl, vshCmd * cm if (!(dom = vshCommandOptDomain(ctl, cmd, "domain", NULL))) return FALSE;
+#ifdef WITH_LDOMS + port = virLDomConsole(dom); + if (port > 0) { + sprintf(command, "%s %d &", + "/usr/X/bin/xterm -sb -sl 1000 -e telnet localhost ", port); + system(command); + return TRUE; + } + + vshError(ctl, FALSE, _("Failed to start console")); + return FALSE; +#endif + doc = virDomainGetXMLDesc(dom, 0); if (!doc) goto cleanup;
See discussion above.
@@ -1003,13 +1021,21 @@ cmdUndefine(vshControl * ctl, vshCmd * c */ static vshCmdInfo info_start[] = { {"syntax", "start <domain>"}, +#ifdef WITH_LDOMS + {"help", gettext_noop("start an inactive or bound domain")}, +#else {"help", gettext_noop("start a (previously defined) inactive domain")}, +#endif {"desc", gettext_noop("Start a domain.")}, {NULL, NULL} };
static vshCmdOptDef opts_start[] = { +#ifdef WITH_LDOMS + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("name of the inactive or bound domain")}, +#else {"name", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("name of the inactive domain")}, +#endif {NULL, 0, 0, NULL} };
The problem with changes lie the above is that they fractionalize virsh into LDom and non-LDom variants. Can we come up with a middle ground help text and avoid changing the option name?
OK. I can remove most of the #ifdef WITH_LDOMS added in virsh.c such as commenting out unsupported commands or printing different start/ help messages. But, I still need to figure out how to handle the LDoms specific console without adding #ifdef WITH_LDOMS in virsh.c.
+#ifndef WITH_LDOMS vshPrint(ctl, "%-15s ", _("CPU Affinity:")); for (m = 0 ; m < VIR_NODEINFO_MAXCPUS(nodeinfo) ; m++) { vshPrint(ctl, "%c", VIR_CPU_USABLE(cpumap, cpumaplen, n, m) ? 'y' : '-'); } vshPrint(ctl, "\n"); +#endif
Instead of changes like this, just return an empty affinity map (if LDoms doesn't support this??).
OK.
@@ -5087,19 +5124,23 @@ cmdQuit(vshControl * ctl, vshCmd * cmd A */ static vshCmdDef commands[] = { {"help", cmdHelp, opts_help, info_help}, +#ifndef WITH_LDOMS {"attach-device", cmdAttachDevice, opts_attach_device, info_attach_device}, {"attach-disk", cmdAttachDisk, opts_attach_disk, info_attach_disk}, {"attach-interface", cmdAttachInterface, opts_attach_interface, info_attach_interface}, {"autostart", cmdAutostart, opts_autostart, info_autostart}, {"capabilities", cmdCapabilities, NULL, info_capabilities}, {"connect", cmdConnect, opts_connect, info_connect}, +#endif /* WITH_LDOMS */ {"console", cmdConsole, opts_console, info_console}, {"create", cmdCreate, opts_create, info_create}, {"start", cmdStart, opts_start, info_start}, {"destroy", cmdDestroy, opts_destroy, info_destroy}, +#ifndef WITH_LDOMS {"detach-device", cmdDetachDevice, opts_detach_device, info_detach_device}, {"detach-disk", cmdDetachDisk, opts_detach_disk, info_detach_disk}, {"detach-interface", cmdDetachInterface, opts_detach_interface, info_detach_interface}, +#endif /* WITH_LDOMS */ {"define", cmdDefine, opts_define, info_define}, {"domid", cmdDomid, opts_domid, info_domid}, {"domuuid", cmdDomuuid, opts_domuuid, info_domuuid}, @@ -5112,7 +5153,9 @@ static vshCmdDef commands[] = { {"freecell", cmdFreecell, opts_freecell, info_freecell}, {"hostname", cmdHostname, NULL, info_hostname}, {"list", cmdList, opts_list, info_list}, +#ifndef WITH_LDOMS {"migrate", cmdMigrate, opts_migrate, info_migrate}, +#endif /* WITH_LDOMS */ [...]
You shouldn't need to comment out unsupported commands. They will return an error if they aren't supported. In fact, QEMU, KVM and OpenVZ only support a subset of the available operations.
OK. I will remove all these #ifdef WITH_LDOMS here.
However if you want to propose a more general patch which allows virsh to determine which operations are supported on the current connection, then I'm all for it. Some of the infrastructure is in place to do this already.
diff --git a/src/virterror.c b/src/virterror.c --- a/src/virterror.c +++ b/src/virterror.c
The changes to virterror are all fine.
diff --git a/src/driver.h b/src/driver.h --- a/src/driver.h +++ b/src/driver.h @@ -24,7 +24,10 @@ typedef enum { VIR_DRV_QEMU = 3, VIR_DRV_REMOTE = 4, VIR_DRV_OPENVZ = 5, - VIR_DRV_LXC = 6 + VIR_DRV_LXC = 6, +#ifdef WITH_LDOMS + VIR_DRV_LDOMS = 7 +#endif } virDrvNo;
This change doesn't need the #ifdef around it. We can add new driver types whenever even if not all platforms will support them.
OK. I will remove the #ifdef WITH_LDOMS here.
Rest of the changes to driver.h are fine however.
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -549,6 +549,9 @@ typedef enum { VIR_VCPU_OFFLINE = 0, /* the virtual CPU is offline */ VIR_VCPU_RUNNING = 1, /* the virtual CPU is running */ VIR_VCPU_BLOCKED = 2, /* the virtual CPU is blocked on resource */ +#ifdef WITH_LDOMS + VIR_VCPU_UNKNOWN = 3, /* the virtual CPU state is unknown */ +#endif } virVcpuState;
I think this is fine (and the corresponding change to virsh.c to support it). No need for the #ifdef since in theory other drivers could have CPUs in this unknown state too.
OK.
diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -56,6 +56,9 @@ typedef enum { VIR_FROM_STATS_LINUX, /* Error in the Linux Stats code */ VIR_FROM_LXC, /* Error from Linux Container driver */ VIR_FROM_STORAGE, /* Error from storage driver */ +#ifdef WITH_LDOMS + VIR_FROM_LDOMS, /* Error from LDoms driver */ +#endif } virErrorDomain;
@@ -139,6 +142,9 @@ typedef enum { VIR_WAR_NO_STORAGE, /* failed to start storage */ VIR_ERR_NO_STORAGE_POOL, /* storage pool not found */ VIR_ERR_NO_STORAGE_VOL, /* storage pool not found */ +#ifdef WITH_LDOMS + VIR_ERR_INVALID_OPTION, /* invalid command line option */ +#endif } virErrorNumber;
/**
Again, no need for #ifdefs here, otherwise this is fine.
OK.
Changes to Makefile.am, configure.in, look fine.
And the rest of the patch contains the new LDom driver code, which is fine because it's isolated to Solaris. These could go in straight away as with the arrangement we have for OpenVZ code.
Rich.
Thanks!

On Wed, Apr 09, 2008 at 02:38:34PM -0500, Eunice Moon wrote:
static vshCmdDef commands[] = { {"help", cmdHelp, opts_help, info_help}, +#ifndef WITH_LDOMS {"attach-device", cmdAttachDevice, opts_attach_device, info_attach_device}, You shouldn't need to comment out unsupported commands. They will return an error if they aren't supported. In fact, QEMU, KVM and OpenVZ only support a subset of the available operations.
OK. I will remove all these #ifdef WITH_LDOMS here.
I'd personally strongly prefer:
However if you want to propose a more general patch which allows virsh to determine which operations are supported on the current connection, then I'm all for it. Some of the infrastructure is in place to do this already.
It's particularly unfriendly to the user to have a whole bunch of apparent commands that actually don't work. (I'd also note that your PSARC case was approved with the commands not available.) regards john

On Thu, Apr 10, 2008 at 12:40:02AM +0100, John Levon wrote:
On Wed, Apr 09, 2008 at 02:38:34PM -0500, Eunice Moon wrote:
static vshCmdDef commands[] = { {"help", cmdHelp, opts_help, info_help}, +#ifndef WITH_LDOMS {"attach-device", cmdAttachDevice, opts_attach_device, info_attach_device}, You shouldn't need to comment out unsupported commands. They will return an error if they aren't supported. In fact, QEMU, KVM and OpenVZ only support a subset of the available operations.
OK. I will remove all these #ifdef WITH_LDOMS here.
I'd personally strongly prefer:
However if you want to propose a more general patch which allows virsh to determine which operations are supported on the current connection, then I'm all for it. Some of the infrastructure is in place to do this already.
It's particularly unfriendly to the user to have a whole bunch of apparent commands that actually don't work.
A complete solution to this is pretty much an intractable problem though because there are many levels at which a certain capability may be missing. Individual libvirt drivers may or may implement a particular API, eg QEMU driver does not yet implement device hotplug. We could add an API to let tools like virsh probe to see if each API is implemented by a driver. Then consider though that the underlying HV impl you talk to may or may not implement the API. ie, PCI hotplug only got added to Xen in Xen version 3.2.0. So now you can't simply probe the indiivdual libvirt driver, you need to figure out whether the underlying HV supports that - and often it won't tell you / provide a way to find out - you just have to try the call and watch it fail Then further consider that different types of VM support different ops, eg Xen PV supports disk notplug, but Xen HVm does not. Except when you have installed PV drivers inside the guest, but libvirt has no idea whether the guest admin has installed PV drivers or not. So we cannot tel whether disk hotplug is supported or not. Even if we try to run the hotplug API in Xen, it'll succeeed from the Dom0 pov -ie we add all the bits to xenstore to register the device, but we've no idea if the frontend has connected to it. So I don't think it is practical to figure out whether each individual command from virsh works without actually trying them, so you can't hide them from the user. Removing commands at compile time with #ifdef is basically guarenteed wrong in. Dan. -- |: Red Hat, Engineering, Boston -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 Thu, Apr 10, 2008 at 05:11:28AM +0100, Daniel P. Berrange wrote:
However if you want to propose a more general patch which allows virsh to determine which operations are supported on the current connection, then I'm all for it. Some of the infrastructure is in place to do this already.
It's particularly unfriendly to the user to have a whole bunch of apparent commands that actually don't work.
Then consider though that the underlying HV impl you talk to may or may not implement the API. ie, PCI hotplug only got added to Xen in Xen version 3.2.0. So now you can't simply probe the indiivdual libvirt driver, you need to figure out whether the underlying HV supports that - and often it won't tell you / provide a way to find out - you just have to try the call and watch it fail
This is fair point, but there's a big difference between "we know this is definitely not going to work" and "this one's tricky". That is, an imperfect implementation still has significant UI benefits.
Then further consider that different types of VM support different ops, eg Xen PV supports disk notplug, but Xen HVm does not. Except when you have installed PV drivers inside the guest, but libvirt has no idea whether the guest admin has installed PV drivers or not.
This particular case is easily fixable using the same method xend does.
So I don't think it is practical to figure out whether each individual command from virsh works without actually trying them, so you can't hide them from the user. Removing commands at compile time with #ifdef is basically guarenteed wrong in.
I agree with that. We need runtime basic probing at least. From that point, we can improve things as needed. regards john

On Thu, Apr 10, 2008 at 12:40:02AM +0100, John Levon wrote:
On Wed, Apr 09, 2008 at 02:38:34PM -0500, Eunice Moon wrote:
static vshCmdDef commands[] = { {"help", cmdHelp, opts_help, info_help}, +#ifndef WITH_LDOMS {"attach-device", cmdAttachDevice, opts_attach_device, info_attach_device}, You shouldn't need to comment out unsupported commands. They will return an error if they aren't supported. In fact, QEMU, KVM and OpenVZ only support a subset of the available operations.
OK. I will remove all these #ifdef WITH_LDOMS here.
I'd personally strongly prefer:
However if you want to propose a more general patch which allows virsh to determine which operations are supported on the current connection, then I'm all for it. Some of the infrastructure is in place to do this already.
Yes ... One thing to remember when implementing this is that you can't just check if the driver struct has a NULL pointer (ie. does the domain support suspend? conn->driver->domainSuspend != NULL). The reason is that this assumption doesn't work in the remote case which "implements" all driver calls and allows them to fail at the server end. (Another corner case would be a driver which only partially implements a call, so it has a driver entry in the struct but it mostly returns VIR_ERR_NO_SUPPORT -- one example of this is virDomainAttachDevice in the QEMU driver). This is why, for migration, I added the '__virDrvSupportsFeature' call which allows you to test for specific features of a driver, eg. Does the driver support migration? Does the driver support the scheduler calls? Is the driver remote? An implementation would need to extend this call, make it available to user code, and come up with a suitable set of features (where feature != call). Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones virt-p2v converts physical machines to virtual machines. Boot with a live CD or over the network (PXE) and turn machines into Xen guests. http://et.redhat.com/~rjones/virt-p2v

Hi Ryan and Eunice, First, thanks for the contribution !
LDoms Support
This patch adds the Logical Domains (LDoms) support for the SPARC platforms. LDoms software is Sun Microsystem's virtualization technology to subdivide a supported system's resources (CPUs, memory, I/O, and storage) creating partitions called logical domains. The Logical Domains Manager is used to create and manage logical domains and maps logical domains to physical resources. The LDoms Manager provides a command-line interface and also exports an XML-based control interface. The Libvirt for LDoms uses this XML interface to communicate with the LDoms Manager to retrieve the LDoms data for: - Listing domains - Requesting CPU and memory resource updates - Performing life-cycle actions for logical domains
This libvirt patch supports LDoms 1.0.1 and 1.0.2.
okay
This patch will modify the following existing files: src/libvirt.c src/virsh.c src/virterror.c src/driver.h src/Makefile.am include/libvirt/libvirt.h.in include/libvirt/virterror.h configure.in
and add the following new files: src/ldoms_common.h src/ldoms_internal.h src/ldoms_internal.c src/ldoms_intfc.h src/ldoms_intfc.c src/ldoms_xml_parse.h src/ldoms_xml_parse.c
I think most of the issues have been raised in the thread started by Richard One think I appreciate in the code is the amount of comments (would have been perfect if the function comment style could be realigned to libvirt one, i don't know if that can be done automatically). Do you have example of the XML domain configurations, it's a bit hard to derive from the parsing code, and i would prefer to have them aligned with other container/partition hypervisors. And then have them documented in the public format pages like the others http://libvirt.org/format.html (we need to add OpenVZ/LinuxContainers exampels too there).
--- a/src/libvirt.c +++ b/src/libvirt.c @@ -48,6 +48,9 @@ #ifdef WITH_LXC #include "lxc_driver.h" #endif +#ifdef WITH_LDOMS +extern int ldomsRegister(void); +#endif
Sounds to me it's cleaner to export the API from an ldom driver header and include the header, that way the signature is checked between the producer and the consumer.
+#ifdef WITH_LDOMS +/** + * virLDomConsole: + * @domain: the domain if available + * + * Opens a terminal window to the console for a domain + * + * Returns -1 in case of error, LDom console port number in case of success + */ +int +virLDomConsole(virDomainPtr domain)
virLDomConsole is really a no go, this can't be added to the list of exported symbols from the library. this was already discussed in the thread, but I want to raise it again w.r.t. the exported symbols set, this is regulated on the gcc tool chain with the src/libvirt_sym.version file, it's not modified anytwhere in the file and since you use virLDomConsole from virsh this implies that you export all C public symbols by default in the library, I really think this need fixing, we can add a specific linker file for Solaris in the distribution. that's not a problem, but the exported symbols set need to be controlled.
--- a/src/virsh.c
Basically no change to virsh should be needed for adding a new driver, so it's a NACk here.
diff --git a/src/virterror.c b/src/virterror.c
In general most of the #ifdef/#ifndef WITH_LDOMS need to go. The public API need to be identical for all drivers and platforms.
- +#ifdef WITH_LDOMS + case VIR_FROM_LDOMS: + dom = "LDoms "; + break; +#endif
[...]
diff --git a/src/driver.h b/src/driver.h --- a/src/driver.h +++ b/src/driver.h @@ -24,7 +24,10 @@ typedef enum { VIR_DRV_QEMU = 3, VIR_DRV_REMOTE = 4, VIR_DRV_OPENVZ = 5, - VIR_DRV_LXC = 6 + VIR_DRV_LXC = 6, +#ifdef WITH_LDOMS + VIR_DRV_LDOMS = 7 +#endif } virDrvNo;
@@ -253,6 +256,11 @@ typedef virDomainPtr const char *uri, unsigned long flags);
+#ifdef WITH_LDOMS +typedef int + (*virDrvLDomConsole) (virDomainPtr domain); +#endif + typedef struct _virDriver virDriver; typedef virDriver *virDriverPtr;
@@ -337,6 +345,9 @@ struct _virDriver { virDrvDomainInterfaceStats domainInterfaceStats; virDrvNodeGetCellsFreeMemory nodeGetCellsFreeMemory; virDrvNodeGetFreeMemory getFreeMemory; +#ifdef WITH_LDOMS + virDrvLDomConsole ldomConsole; +#endif };
in the short term export of the console informations in the XML is the normal option, then a generic console extracting API may be provided if needed , but adding this is premature.
typedef int diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -549,6 +549,9 @@ typedef enum { VIR_VCPU_OFFLINE = 0, /* the virtual CPU is offline */ VIR_VCPU_RUNNING = 1, /* the virtual CPU is running */ VIR_VCPU_BLOCKED = 2, /* the virtual CPU is blocked on resource */ +#ifdef WITH_LDOMS + VIR_VCPU_UNKNOWN = 3, /* the virtual CPU state is unknown */ +#endif } virVcpuState;
typedef struct _virVcpuInfo virVcpuInfo; diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -56,6 +56,9 @@ typedef enum { VIR_FROM_STATS_LINUX, /* Error in the Linux Stats code */ VIR_FROM_LXC, /* Error from Linux Container driver */ VIR_FROM_STORAGE, /* Error from storage driver */ +#ifdef WITH_LDOMS + VIR_FROM_LDOMS, /* Error from LDoms driver */ +#endif } virErrorDomain;
@@ -139,6 +142,9 @@ typedef enum { VIR_WAR_NO_STORAGE, /* failed to start storage */ VIR_ERR_NO_STORAGE_POOL, /* storage pool not found */ VIR_ERR_NO_STORAGE_VOL, /* storage pool not found */ +#ifdef WITH_LDOMS + VIR_ERR_INVALID_OPTION, /* invalid command line option */ +#endif } virErrorNumber;
removal of all #ifdef/#endif , one API to bind them all
diff --git a/configure.in b/configure.in --- a/configure.in +++ b/configure.in @@ -246,6 +246,10 @@ if test "$with_remote" = "yes" ; then LIBVIRT_FEATURES="$LIBVIRT_FEATURES -DWITH_REMOTE" fi
+if test "$with_ldoms" = "yes" ; then + LIBVIRT_FEATURES="$LIBVIRT_FEATURES -DWITH_LDOMS" +fi +
I'm a bit surprized to not see the AC_ARG_WITH(ldom, .... option block in the configure.in, was that dropped from the patch ? I think this should be in a large part based on patform autodetection and the --with-ldom/--without-ldom should only be used for code portability tests and development.
+++ b/src/ldoms_common.h @@ -0,0 +1,80 @@ +/* + * ldoms_common.h: LDoms common definitions + * + * Copyright 2008 Sun Microsystems, Inc. + * + * See COPYING.LIB for the License of this software [...]
looks fine to me but ...
+/* Debug print */ +extern void dprt(const char *template, ...);
i would prefer to see the debug integrated with the existing debug support. Since we now compile with debug support enabled by default it is really useful to allow user provide debug informations e.g. by running the virsh command after having set the LIVIRT_DEBUG=1 environment variable. If you have more complex debugging need (levels of debug) the existing routine should be able to cope with this (see virErrorLevel in virterror.h). All those if (ldoms_debug) dprt(...) should be relatively easy to unify into DEBUG() macros like in other parts of the library.
+++ b/src/ldoms_internal.h @@ -0,0 +1,29 @@ [...] +int ldomsRegister(void);
#include "ldoms_internal.h" should be added in libvirt.c and then the declaration of ldomsRegister should be removed there
+void ldomsError(virConnectPtr, virDomainPtr, virErrorNumber, const char*, int);
[...]
diff --git a/src/ldoms_internal.c b/src/ldoms_internal.c [...] + * Open a connection to the LDoms Manager and handshake to + * verify the a connection can be made for future XML requests.
interesting to see you are basically using XML as the intercahnge format with the manager. [...]
+/* + * ldomsListDomains + * + * What: Return the array of Domain ID's for all the active Domains. + * Send an XML request to LDM (LDoms Manager) for a list of all Domains. + * + * Yes, this functions does pretty much the same as ldomsNumOfDomains() with + * the addition of returning the ID numbers for the valid Domains. + * + * Input: conn - The Connection structure + * maxids - The total number of Domains to look at to + * determine what Domain IDs to return. + * Output: ids - An array of integers to hold the IDs of the + * Domains whose state is other than 'inactive' - + * VIR_DOMAIN_SHUTOFF
I'm a bit confused about how active/inactive domains are handled in lDOM, and i don't really have a way to just get my hand on it. [...]
+/* + * ldomsDomainCreateXML + * + * What: Create a domain from an XML file so that it is left in the + * inactive state. Just call the DefineXML function. The XML input + * has to be a complete and valid XML requeset for the LDM. No + * modification is done to this file. + * + * Input: xml - The xml file + * Output: function return - A ptr to a virDomain or NULL + */ +virDomainPtr +ldomsDomainCreateXML(virConnectPtr conn, const char *xml, unsigned int flags) +{ + virDomainPtr domPtr = NULL; + char *domainName = NULL; + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainCreateXML(ENTER) \n"); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainCreateXML(ENTER) xmldoc=\n%s\n",xml); + + /* Send the XML file along with the lifecycle action */ + domainName = send_ldom_create_domain((char *)xml, XML_ADD_DOMAIN); + + /* + * If the create/bind domain was successful, then we have to create a DomainInfo + * structure to pass back to the caller. We need the Domain name that was + * created, and the only way to get that is from parsing the input xml + * document. This is done in send_ldom_create_domain() and passed back as the return. + * Also we create the uuid for the domain name. + */
I am completely unable to find informations about the XML format for a domain i looked at send_ldom_create_domain and it seems to send the XML directly to the domain manager, ther don't seems to be any parsing or analysis or checking of the XML input data at the libvirt level. To me this is a bit problematic as the XML is part of the API in practice. This really need to be documentd as the support is added to libvirt. [...]
+ /* get the current ldom data from LDM */ + (void) pthread_rwlock_wrlock(&update_lock); + refresh_ldom_data(); + (void) pthread_rwlock_unlock(&update_lock); +
I'm a bit surprized by this, this looks a bit heavy to keep a complete list in libvirt memory updated with a global lock, that doesn't look optimal for example when vish asks for the list of domain you need first to update to get the number of domains and then reupdate in the next call(s) to extract the domain data. And that won't protect you from races anyway, so i'm a bit surprized.
+/* + * refresh_ldom_data + * + * Send an XML request to LDM for a list of all Domains + * and get the basic information of each Domain. + * + * This function will be called for each commands to + * guarantee that we have the current LDom info for any + * virsh CLI command. + * + * Input: + * Output: + */ [...] +/* + * ldomsDomainDumpXML + * + * What: Send a list-constraints request to LDM for the domain specified + * by the input virDomain. + * + * Input: domain - Pointer to a virDomain structure + * Output: function return - char ptr to the XML data + */ +char * +ldomsDomainDumpXML(virDomainPtr domain, int flags) [...] + /* Dump the response to memory */ + xml = malloc(sizeof(char) * 10000); + xmlKeepBlanksDefault(0);
hum, i usually suggest libxml2 users don't use xmlKeepBlanksDefault anymore as this uses a global variable and modify the behaviour of all libxml2 processing including parsing, this is especially nasty in a library.
+ xmlDocDumpFormatMemory(xml_received, &xml, &xmlSize, 1);
the 1 for the format argument should be sufficient Also using xmlSaveToBuffer http://xmlsoft.org/html/libxml-xmlsave.html#xmlSaveToBuffer would avoid a fixed output buffer limitation which is really not necessary. I'm still a bit puzzled that the manager uses the libvirt XML format directly as the input. [...]
+static void +refresh_ldom_data() +{ + struct timeval tv; + long delta;
Question: why not do the locking here instead of around all the calling functions ? Simpler code, less error prone, no ?
+ /* Try and throttle calling the LDM to update the list of + * LDoms. If the calls are 2 secs or less apart, then we just + * use the current LDoms info. This is because each + * command from virsh can turn into several calls into + * function in this file, which all call this function + * to refresh the LDom data. + */ + if (gettimeofday(&tv, NULL) >= 0) { + /* See if the time since the last call to this functions + * is less than 3 seconds. If so, just return. + */ + if ((delta=tv.tv_sec - last_ldom_refresh) < 3) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: refresh_ldom_data(NO Refresh) delta=%d\n", + delta); + return; + } + last_ldom_refresh = tv.tv_sec; + }
[...]
+int ldomsRegister(void) { + + /* for debug statements */ +#ifdef LDOMS_DEBUG + ldoms_debug = 1; + dprt("LDOMS_DEBUG on\n"); +#endif +#ifdef LDOMS_DETAILED_DEBUG + ldoms_detailed_debug = 1; + dprt("LDOMS_DETAILED_DEBUG on\n"); +#endif
maybe the debug vs detailed_debug could be integarted in libvirt normal debugging model, for example based on the value of LIBVIRT_DEBUG env variable. [...]
diff --git a/src/ldoms_xml_parse.h b/src/ldoms_xml_parse.h new file mode 100644 --- /dev/null +++ b/src/ldoms_xml_parse.h [...] diff --git a/src/ldoms_xml_parse.c b/src/ldoms_xml_parse.c new file mode 100644
a few comments on libxml2 use [...]
+xml_find_subnode(xmlNodePtr node, const xmlChar *name) +{ + xmlNodePtr subnode; + + subnode = node->xmlChildrenNode; + while (subnode != NULL) { + if (xmlStrcmp(subnode->name, name) == 0) + break;
not that with recent libxml2 (any 2.6.x will do) all the markup name strings are interned in a dictionary associated to the document. With a bit of tweaking on your parsing routines would could also get all domuments parsed to share the same dictionary 9basically by always reusing the same XML parsing context, meaning all those strings could be interned for the whole process and let you fallback to pure string pointer comparisons, instead of comparing content. But it's probably a bit complex unless you're chasing performances issues. [...]
+ * Example: The following XML file will be created for LDom ldom1 + * and a new memory value of 256 Kilobytes. + * + * <?xml version="1.0"?> + * <LDM_interface version="1.0"> + * <cmd> + * <action>set-memory</action> + * <data version="2.0"> + * <ldom> + * <ldom_info> + * <ldom_name>ldom1</ldom_name> + * </ldom_info> + * <memory> + * <size>256K</size> + * </memory> + * </ldom> + * </data> + * </cmd> + * </LDM_interface> + */ + +xmlDocPtr +create_xml_file_4_set_memory(char *ldom_name, unsigned long memory) +{ + xmlDocPtr xml_output; + xmlNodePtr root, cmd, data, ldom, info_node, mem; + char mem_str[10]; /* ascii version of input int memory */ + + if (ldoms_debug) dprt("LDOMS_DEBUG: create_xml_file_4_set_memory(ENTER): ldom=%s, memory=%lu\n", + (ldom_name==NULL? "NULL" : ldom_name), memory); + + xml_output = xmlNewDoc(XML_VERSION); + + /* <LDM_interface> tag with version sttribute */ + root = xmlNewDocNode(xml_output, NULL, XML_LDM_INTERFACE, NULL); + xmlDocSetRootElement(xml_output, root); + xmlNewProp(root, VERSION_ATTR, LDOM_INTERFACE_VERSION);
In practice building the tree may be a bit more complex than generating the XmL serialization directly makes for simpler code and you don't have to serialize later, [...]
+parse_xml_get_ldominfo(xmlDoc *doc, int *num_cpu, int *mem_size, int *mem_unit, int *num_crypto, int *num_iobus) +{ + data_node = xml_find_subnode(cmd_node, XML_DATA); + if (data_node != NULL) + ldom_node = xml_find_subnode(data_node, LDOM_NODE); + else { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_ldominfo.. XML file does not have <data> tag\n"); + return (ret); + } + + if (ldom_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_ldominfo.. XML file does not have <ldom> tag\n"); + return (ret); + } + + /* get number of cpu */ + cpu_node = xml_find_subnode(ldom_node, CPU_NODE); + if (cpu_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_ldominfo.. XML file does not have <cpu> tag\n"); + num_cpu_xml = 0; + } else { + subnode = xml_find_subnode(cpu_node, NUMBER_NODE); + if (subnode == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_ldominfo.. XML file does not have <number> tag within <cpu> tag\n"); + num_cpu_xml = 0; + } else { + + content = xmlNodeGetContent(subnode); + + num_cpu_xml = strtoll((char *)content, NULL, 0); + if (num_cpu_xml <= 0) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_ldominfo.. Invalid cpu num specified: %s\n", content); + } + xmlFree(content); + } + }
Direct XPath queries on the tree make for simpler code, and are probably as efficient (if needed the XPath expressions can be precompiled) virXPathLong("number(//ldom/memory/size[1], ctxt, &size) is more readable I'm sure, should simplify long term maintainance too, it's easy to forget an xmlFree in some path ... I'm not gonna complain, but it seems you had to deal with libxml2 way more than necessary, that's make for an awful lot of code in the end too. Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

Hi Daniel, Thanks for your comments. Please see my responses inline below. Eunice Daniel Veillard wrote:
Hi Ryan and Eunice,
First, thanks for the contribution !
LDoms Support
This patch adds the Logical Domains (LDoms) support for the SPARC platforms. LDoms software is Sun Microsystem's virtualization technology to subdivide a supported system's resources (CPUs, memory, I/O, and storage) creating partitions called logical domains. The Logical Domains Manager is used to create and manage logical domains and maps logical domains to physical resources. The LDoms Manager provides a command-line interface and also exports an XML-based control interface. The Libvirt for LDoms uses this XML interface to communicate with the LDoms Manager to retrieve the LDoms data for: - Listing domains - Requesting CPU and memory resource updates - Performing life-cycle actions for logical domains
This libvirt patch supports LDoms 1.0.1 and 1.0.2.
okay
This patch will modify the following existing files: src/libvirt.c src/virsh.c src/virterror.c src/driver.h src/Makefile.am include/libvirt/libvirt.h.in include/libvirt/virterror.h configure.in
and add the following new files: src/ldoms_common.h src/ldoms_internal.h src/ldoms_internal.c src/ldoms_intfc.h src/ldoms_intfc.c src/ldoms_xml_parse.h src/ldoms_xml_parse.c
I think most of the issues have been raised in the thread started by Richard One think I appreciate in the code is the amount of comments (would have been perfect if the function comment style could be realigned to libvirt one, i don't know if that can be done automatically). Do you have example of the XML domain configurations, it's a bit hard to derive from the parsing code, and i would prefer to have them aligned with other container/partition hypervisors. And then have them documented in the public format pages like the others http://libvirt.org/format.html (we need to add OpenVZ/LinuxContainers exampels too there). I've attached a couple of LDoms XML examples.
--- a/src/libvirt.c +++ b/src/libvirt.c @@ -48,6 +48,9 @@ #ifdef WITH_LXC #include "lxc_driver.h" #endif +#ifdef WITH_LDOMS +extern int ldomsRegister(void); +#endif
Sounds to me it's cleaner to export the API from an ldom driver header and include the header, that way the signature is checked between the producer and the consumer.
OK. I will create the ldoms_driver.h file with the ldomsRegister() and include that header file like Linux Containers.
+#ifdef WITH_LDOMS +/** + * virLDomConsole: + * @domain: the domain if available + * + * Opens a terminal window to the console for a domain + * + * Returns -1 in case of error, LDom console port number in case of success + */ +int +virLDomConsole(virDomainPtr domain)
virLDomConsole is really a no go, this can't be added to the list of exported symbols from the library. this was already discussed in the thread, but I want to raise it again w.r.t. the exported symbols set, this is regulated on the gcc tool chain with the src/libvirt_sym.version file, it's not modified anytwhere in the file and since you use virLDomConsole from virsh this implies that you export all C public symbols by default in the library, I really think this need fixing, we can add a specific linker file for Solaris in the distribution. that's not a problem, but the exported symbols set need to be controlled.
OK. I will remove virLDomConsole.
--- a/src/virsh.c
Basically no change to virsh should be needed for adding a new driver, so it's a NACk here.
OK.
diff --git a/src/virterror.c b/src/virterror.c
In general most of the #ifdef/#ifndef WITH_LDOMS need to go. The public API need to be identical for all drivers and platforms.
OK.
- +#ifdef WITH_LDOMS + case VIR_FROM_LDOMS: + dom = "LDoms "; + break; +#endif
[...]
diff --git a/src/driver.h b/src/driver.h --- a/src/driver.h +++ b/src/driver.h @@ -24,7 +24,10 @@ typedef enum { VIR_DRV_QEMU = 3, VIR_DRV_REMOTE = 4, VIR_DRV_OPENVZ = 5, - VIR_DRV_LXC = 6 + VIR_DRV_LXC = 6, +#ifdef WITH_LDOMS + VIR_DRV_LDOMS = 7 +#endif } virDrvNo;
@@ -253,6 +256,11 @@ typedef virDomainPtr const char *uri, unsigned long flags);
+#ifdef WITH_LDOMS +typedef int + (*virDrvLDomConsole) (virDomainPtr domain); +#endif + typedef struct _virDriver virDriver; typedef virDriver *virDriverPtr;
@@ -337,6 +345,9 @@ struct _virDriver { virDrvDomainInterfaceStats domainInterfaceStats; virDrvNodeGetCellsFreeMemory nodeGetCellsFreeMemory; virDrvNodeGetFreeMemory getFreeMemory; +#ifdef WITH_LDOMS + virDrvLDomConsole ldomConsole; +#endif };
in the short term export of the console informations in the XML is the normal option, then a generic console extracting API may be provided if needed , but adding this is premature.
typedef int diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -549,6 +549,9 @@ typedef enum { VIR_VCPU_OFFLINE = 0, /* the virtual CPU is offline */ VIR_VCPU_RUNNING = 1, /* the virtual CPU is running */ VIR_VCPU_BLOCKED = 2, /* the virtual CPU is blocked on resource */ +#ifdef WITH_LDOMS + VIR_VCPU_UNKNOWN = 3, /* the virtual CPU state is unknown */ +#endif } virVcpuState;
typedef struct _virVcpuInfo virVcpuInfo; diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -56,6 +56,9 @@ typedef enum { VIR_FROM_STATS_LINUX, /* Error in the Linux Stats code */ VIR_FROM_LXC, /* Error from Linux Container driver */ VIR_FROM_STORAGE, /* Error from storage driver */ +#ifdef WITH_LDOMS + VIR_FROM_LDOMS, /* Error from LDoms driver */ +#endif } virErrorDomain;
@@ -139,6 +142,9 @@ typedef enum { VIR_WAR_NO_STORAGE, /* failed to start storage */ VIR_ERR_NO_STORAGE_POOL, /* storage pool not found */ VIR_ERR_NO_STORAGE_VOL, /* storage pool not found */ +#ifdef WITH_LDOMS + VIR_ERR_INVALID_OPTION, /* invalid command line option */ +#endif } virErrorNumber;
removal of all #ifdef/#endif , one API to bind them all
OK.
diff --git a/configure.in b/configure.in --- a/configure.in +++ b/configure.in @@ -246,6 +246,10 @@ if test "$with_remote" = "yes" ; then LIBVIRT_FEATURES="$LIBVIRT_FEATURES -DWITH_REMOTE" fi
+if test "$with_ldoms" = "yes" ; then + LIBVIRT_FEATURES="$LIBVIRT_FEATURES -DWITH_LDOMS" +fi +
I'm a bit surprized to not see the AC_ARG_WITH(ldom, .... option block in the configure.in, was that dropped from the patch ?
Yes, I think that should be added to the patch.
I think this should be in a large part based on patform autodetection and the --with-ldom/--without-ldom should only be used for code portability tests and development.
+++ b/src/ldoms_common.h @@ -0,0 +1,80 @@ +/* + * ldoms_common.h: LDoms common definitions + * + * Copyright 2008 Sun Microsystems, Inc. + * + * See COPYING.LIB for the License of this software [...]
looks fine to me but ...
+/* Debug print */ +extern void dprt(const char *template, ...);
i would prefer to see the debug integrated with the existing debug support. Since we now compile with debug support enabled by default it is really useful to allow user provide debug informations e.g. by running the virsh command after having set the LIVIRT_DEBUG=1 environment variable. If you have more complex debugging need (levels of debug) the existing routine should be able to cope with this (see virErrorLevel in virterror.h). All those if (ldoms_debug) dprt(...) should be relatively easy to unify into DEBUG() macros like in other parts of the library.
OK. I will use DEBUG macros instead of dprt(). We have implemented two levels of debug (LDOMS_DEBUG and LDOMS_DETAILED_DEBUG) and generate lots of debug messages if selected as a DEBUG build. There are so many places in the code for debug check, so we decided to code "if (ldoms_debug)..." instead of surrounding them by #ifdef DEBUG, but I think DEBUG() macros can be just used instead of dprt() without #ifdef DEBUG.
+++ b/src/ldoms_internal.h @@ -0,0 +1,29 @@ [...] +int ldomsRegister(void);
#include "ldoms_internal.h" should be added in libvirt.c and then the declaration of ldomsRegister should be removed there
OK.
+void ldomsError(virConnectPtr, virDomainPtr, virErrorNumber, const char*, int);
[...]
diff --git a/src/ldoms_internal.c b/src/ldoms_internal.c [...] + * Open a connection to the LDoms Manager and handshake to + * verify the a connection can be made for future XML requests.
interesting to see you are basically using XML as the intercahnge format with the manager.
[...]
+/* + * ldomsListDomains + * + * What: Return the array of Domain ID's for all the active Domains. + * Send an XML request to LDM (LDoms Manager) for a list of all Domains. + * + * Yes, this functions does pretty much the same as ldomsNumOfDomains() with + * the addition of returning the ID numbers for the valid Domains. + * + * Input: conn - The Connection structure + * maxids - The total number of Domains to look at to + * determine what Domain IDs to return. + * Output: ids - An array of integers to hold the IDs of the + * Domains whose state is other than 'inactive' - + * VIR_DOMAIN_SHUTOFF
I'm a bit confused about how active/inactive domains are handled in lDOM, and i don't really have a way to just get my hand on it.
LDoms have the following states: 1 = active LDOM_STATE_ACTIVE 2 = stopping LDOM_STATE_STOPPING 3 = inactive LDOM_STATE_INACTIVE 4 = binding LDOM_STATE_BINDING 5 = unbinding LDOM_STATE_UNBINDING 6 = bound LDOM_STATE_BOUND 7 = starting LDOM_STATE_STARTING and the LDoms states are converted to the livirt states as follows: (ldomsDomainGetInfo() in ldoms_internal.c) LDOM_STATE_ACTIVE ->VIR_DOMAIN_RUNNING LDOM_STATE_STOPPING ->VIR_DOMAIN_SHUTDOWN LDOM_STATE_INACTIVE ->VIR_DOMAIN_SHUTOFF case LDOM_STATE_BINDING->VIR_DOMAIN_SHUTDOWN case LDOM_STATE_UNBINDING->VIR_DOMAIN_SHUTOFF case LDOM_STATE_BOUND ->VIR_DOMAIN_SHUTDOWN case LDOM_STATE_STARTING ->VIR_DOMAIN_RUNNING
[...]
+/* + * ldomsDomainCreateXML + * + * What: Create a domain from an XML file so that it is left in the + * inactive state. Just call the DefineXML function. The XML input + * has to be a complete and valid XML requeset for the LDM. No + * modification is done to this file. + * + * Input: xml - The xml file + * Output: function return - A ptr to a virDomain or NULL + */ +virDomainPtr +ldomsDomainCreateXML(virConnectPtr conn, const char *xml, unsigned int flags) +{ + virDomainPtr domPtr = NULL; + char *domainName = NULL; + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainCreateXML(ENTER) \n"); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainCreateXML(ENTER) xmldoc=\n%s\n",xml); + + /* Send the XML file along with the lifecycle action */ + domainName = send_ldom_create_domain((char *)xml, XML_ADD_DOMAIN); + + /* + * If the create/bind domain was successful, then we have to create a DomainInfo + * structure to pass back to the caller. We need the Domain name that was + * created, and the only way to get that is from parsing the input xml + * document. This is done in send_ldom_create_domain() and passed back as the return. + * Also we create the uuid for the domain name. + */
I am completely unable to find informations about the XML format for a domain i looked at send_ldom_create_domain and it seems to send the XML directly to the domain manager, ther don't seems to be any parsing or analysis or checking of the XML input data at the libvirt level. To me this is a bit problematic as the XML is part of the API in practice. This really need to be documentd as the support is added to libvirt.
Yes, send_ldom_create_domain() sends the XML directly to the LDoms Manager after adding some tags such as <cmd> tag that needs to be present in the LDoms XML format.
[...]
+ /* get the current ldom data from LDM */ + (void) pthread_rwlock_wrlock(&update_lock); + refresh_ldom_data(); + (void) pthread_rwlock_unlock(&update_lock); +
I'm a bit surprized by this, this looks a bit heavy to keep a complete list in libvirt memory updated with a global lock, that doesn't look optimal for example when vish asks for the list of domain you need first to update to get the number of domains and then reupdate in the next call(s) to extract the domain data. And that won't protect you from races anyway, so i'm a bit surprized.
I will look into this more..
+/* + * refresh_ldom_data + * + * Send an XML request to LDM for a list of all Domains + * and get the basic information of each Domain. + * + * This function will be called for each commands to + * guarantee that we have the current LDom info for any + * virsh CLI command. + * + * Input: + * Output: + */ [...] +/* + * ldomsDomainDumpXML + * + * What: Send a list-constraints request to LDM for the domain specified + * by the input virDomain. + * + * Input: domain - Pointer to a virDomain structure + * Output: function return - char ptr to the XML data + */ +char * +ldomsDomainDumpXML(virDomainPtr domain, int flags) [...] + /* Dump the response to memory */ + xml = malloc(sizeof(char) * 10000); + xmlKeepBlanksDefault(0);
hum, i usually suggest libxml2 users don't use xmlKeepBlanksDefault anymore as this uses a global variable and modify the behaviour of all libxml2 processing including parsing, this is especially nasty in a library.
+ xmlDocDumpFormatMemory(xml_received, &xml, &xmlSize, 1);
the 1 for the format argument should be sufficient Also using xmlSaveToBuffer http://xmlsoft.org/html/libxml-xmlsave.html#xmlSaveToBuffer would avoid a fixed output buffer limitation which is really not necessary.
I'm still a bit puzzled that the manager uses the libvirt XML format directly as the input.
[...]
+static void +refresh_ldom_data() +{ + struct timeval tv; + long delta;
Question: why not do the locking here instead of around all the calling functions ? Simpler code, less error prone, no ?
Yes, I think I can put the rwlock here instead of calling functions.
+ /* Try and throttle calling the LDM to update the list of + * LDoms. If the calls are 2 secs or less apart, then we just + * use the current LDoms info. This is because each + * command from virsh can turn into several calls into + * function in this file, which all call this function + * to refresh the LDom data. + */ + if (gettimeofday(&tv, NULL) >= 0) { + /* See if the time since the last call to this functions + * is less than 3 seconds. If so, just return. + */ + if ((delta=tv.tv_sec - last_ldom_refresh) < 3) { + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: refresh_ldom_data(NO Refresh) delta=%d\n", + delta); + return; + } + last_ldom_refresh = tv.tv_sec; + }
[...]
+int ldomsRegister(void) { + + /* for debug statements */ +#ifdef LDOMS_DEBUG + ldoms_debug = 1; + dprt("LDOMS_DEBUG on\n"); +#endif +#ifdef LDOMS_DETAILED_DEBUG + ldoms_detailed_debug = 1; + dprt("LDOMS_DETAILED_DEBUG on\n"); +#endif
maybe the debug vs detailed_debug could be integarted in libvirt normal debugging model, for example based on the value of LIBVIRT_DEBUG env variable.
OK. I will use the libvirt normal debugging model and use DEBUG instead of dprt().
[...]
diff --git a/src/ldoms_xml_parse.h b/src/ldoms_xml_parse.h new file mode 100644 --- /dev/null +++ b/src/ldoms_xml_parse.h [...] diff --git a/src/ldoms_xml_parse.c b/src/ldoms_xml_parse.c new file mode 100644
a few comments on libxml2 use
[...]
+xml_find_subnode(xmlNodePtr node, const xmlChar *name) +{ + xmlNodePtr subnode; + + subnode = node->xmlChildrenNode; + while (subnode != NULL) { + if (xmlStrcmp(subnode->name, name) == 0) + break;
not that with recent libxml2 (any 2.6.x will do) all the markup name strings are interned in a dictionary associated to the document. With a bit of tweaking on your parsing routines would could also get all domuments parsed to share the same dictionary 9basically by always reusing the same XML parsing context, meaning all those strings could be interned for the whole process and let you fallback to pure string pointer comparisons, instead of comparing content. But it's probably a bit complex unless you're chasing performances issues.
Thanks for the info/comments on libxml2 use. We will improve the xml code if we have to deal with performance issues.
[...]
+ * Example: The following XML file will be created for LDom ldom1 + * and a new memory value of 256 Kilobytes. + * + * <?xml version="1.0"?> + * <LDM_interface version="1.0"> + * <cmd> + * <action>set-memory</action> + * <data version="2.0"> + * <ldom> + * <ldom_info> + * <ldom_name>ldom1</ldom_name> + * </ldom_info> + * <memory> + * <size>256K</size> + * </memory> + * </ldom> + * </data> + * </cmd> + * </LDM_interface> + */ + +xmlDocPtr +create_xml_file_4_set_memory(char *ldom_name, unsigned long memory) +{ + xmlDocPtr xml_output; + xmlNodePtr root, cmd, data, ldom, info_node, mem; + char mem_str[10]; /* ascii version of input int memory */ + + if (ldoms_debug) dprt("LDOMS_DEBUG: create_xml_file_4_set_memory(ENTER): ldom=%s, memory=%lu\n", + (ldom_name==NULL? "NULL" : ldom_name), memory); + + xml_output = xmlNewDoc(XML_VERSION); + + /* <LDM_interface> tag with version sttribute */ + root = xmlNewDocNode(xml_output, NULL, XML_LDM_INTERFACE, NULL); + xmlDocSetRootElement(xml_output, root); + xmlNewProp(root, VERSION_ATTR, LDOM_INTERFACE_VERSION);
In practice building the tree may be a bit more complex than generating the XmL serialization directly makes for simpler code and you don't have to serialize later,
[...]
+parse_xml_get_ldominfo(xmlDoc *doc, int *num_cpu, int *mem_size, int *mem_unit, int *num_crypto, int *num_iobus) +{ + data_node = xml_find_subnode(cmd_node, XML_DATA); + if (data_node != NULL) + ldom_node = xml_find_subnode(data_node, LDOM_NODE); + else { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_ldominfo.. XML file does not have <data> tag\n"); + return (ret); + } + + if (ldom_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_ldominfo.. XML file does not have <ldom> tag\n"); + return (ret); + } + + /* get number of cpu */ + cpu_node = xml_find_subnode(ldom_node, CPU_NODE); + if (cpu_node == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_ldominfo.. XML file does not have <cpu> tag\n"); + num_cpu_xml = 0; + } else { + subnode = xml_find_subnode(cpu_node, NUMBER_NODE); + if (subnode == NULL) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_ldominfo.. XML file does not have <number> tag within <cpu> tag\n"); + num_cpu_xml = 0; + } else { + + content = xmlNodeGetContent(subnode); + + num_cpu_xml = strtoll((char *)content, NULL, 0); + if (num_cpu_xml <= 0) { + if (ldoms_debug) dprt("LDOMS_DEBUG: parse_xml_get_ldominfo.. Invalid cpu num specified: %s\n", content); + } + xmlFree(content); + } + }
Direct XPath queries on the tree make for simpler code, and are probably as efficient (if needed the XPath expressions can be precompiled) virXPathLong("number(//ldom/memory/size[1], ctxt, &size) is more readable I'm sure, should simplify long term maintainance too, it's easy to forget an xmlFree in some path ...
I'm not gonna complain, but it seems you had to deal with libxml2 way more than necessary, that's make for an awful lot of code in the end too.
Daniel
Thanks!

On Wed, Apr 09, 2008 at 08:52:44PM -0500, Eunice Moon wrote:
Hi Daniel,
Thanks for your comments. Please see my responses inline below.
Hi Eunice, it seems we are basically agreeing on the needed code change, which is great ... but there is one big problem remaining: Example of domain definition data:
<?xml version="1.0"?> <LDM_interface version="1.0"> <data version="2.0"> <ldom> <ldom_info> <ldom_name>primary</ldom_name> <mac_address>00:14:4f:02:7d:c6</mac_address> </ldom_info> <cpu> <number>10</number> </cpu> <mau> <number>3</number> </mau> <memory> <size>1920M</size> </memory> <physio_device> <name>pci@780</name> </physio_device> <physio_device> <name>pci@7c0</name> </physio_device> <vsw> <service_name>primary-vsw0</service_name> <dev_path>e1000g0</dev_path> </vsw> <vcc> <service_name>primary-vcc0</service_name> <min_port>5000</min_port> <max_port>5033</max_port> </vcc> <var> <name>boot-device</name> <value>/pci@7c0/pci@0/pci@1/pci@0,2/LSILogic,sas@2/disk@0,0:a disk net</value> </var> <var> <name>nvramrc</name> <value>devalias disk /pci@7c0/pci@0/pci@1/pci@0,2/LSILogic,sas@2/disk@0 </value> </var> <var> <name>use-nvramrc?</name> <value>true</value> </var> </ldom> </data> </LDM_interface>
From what i understand this correspond to what you would suggest libvirt lDOM domain to use, both as input when creating or defining a process or when dumping the XML of a domain via 'virsh dumpxml domainame' In a sense the XML format is also part of libvirt API and your data is completely different from what libvirt uses for all other hypervisors: http://libvirt.org/format.html#Normal1 I understand that your format correspond to what the virtualization manager exports as no transformation of the data is done at the libvirt lDOM patches level. So basically as is we are in a situation where no existing tool based on libvirt (like virt-manager or ovirt) can be ported to a future libvirt for lDOM without completely changing the XML handling code. That would seriously diminish the value of the lDOM libvirt port, isn't it ? One option is to adapt the virtualization manager to use an XML format which is based on libvirt existing format, I don't know if this is possible. One problem to consider is whether the existing format is 'public' or just internal to the existing tools. The other option I can see is to provide a conversion layer, either in libvirt or in the manager to allow the processing of domain descriptions based on the libvirt public format. I even thing the code could detect the kind of format near trivially (for example by checking the name of the root element when receiving XML, and by using a compatibility flag when extracting XML with virDomainGetXMLDesc). I'm not sure i would be able to trivially map your existing format to a libvirt one, because i don't understand what some of the fields are for and the way you name devices are a bit strange to me, but i'm sure it can be solved. it will just take some work, what do you think ? Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

Hi Daniel, Yes, thanks for the feedback and we are agreeing on the code changes to be done. I also agree that there is a big issue regarding different XML formats. I don't think the first option (to change the LDoms Manager XML format to be based on the libvirt XML format) is a feasible one since LDoms has been released public and some tools/applications are already based on the LDom Manager's XML interfaces. So, it seems like we need to go for the second option (to provide a conversion layer between the LDoms Manager and libvirt XML formats) that would require some research and scoping. Thanks, Eunice Daniel Veillard wrote:
On Wed, Apr 09, 2008 at 08:52:44PM -0500, Eunice Moon wrote:
Hi Daniel,
Thanks for your comments. Please see my responses inline below.
Hi Eunice,
it seems we are basically agreeing on the needed code change, which is great ... but there is one big problem remaining:
Example of domain definition data:
<?xml version="1.0"?> <LDM_interface version="1.0"> <data version="2.0"> <ldom> <ldom_info> <ldom_name>primary</ldom_name> <mac_address>00:14:4f:02:7d:c6</mac_address> </ldom_info> <cpu> <number>10</number> </cpu> <mau> <number>3</number> </mau> <memory> <size>1920M</size> </memory> <physio_device> <name>pci@780</name> </physio_device> <physio_device> <name>pci@7c0</name> </physio_device> <vsw> <service_name>primary-vsw0</service_name> <dev_path>e1000g0</dev_path> </vsw> <vcc> <service_name>primary-vcc0</service_name> <min_port>5000</min_port> <max_port>5033</max_port> </vcc> <var> <name>boot-device</name> <value>/pci@7c0/pci@0/pci@1/pci@0,2/LSILogic,sas@2/disk@0,0:a disk net</value> </var> <var> <name>nvramrc</name> <value>devalias disk /pci@7c0/pci@0/pci@1/pci@0,2/LSILogic,sas@2/disk@0 </value> </var> <var> <name>use-nvramrc?</name> <value>true</value> </var> </ldom> </data> </LDM_interface>
From what i understand this correspond to what you would suggest libvirt lDOM domain to use, both as input when creating or defining a process or when dumping the XML of a domain via 'virsh dumpxml domainame'
In a sense the XML format is also part of libvirt API and your data is completely different from what libvirt uses for all other hypervisors: http://libvirt.org/format.html#Normal1
I understand that your format correspond to what the virtualization manager exports as no transformation of the data is done at the libvirt lDOM patches level.
So basically as is we are in a situation where no existing tool based on libvirt (like virt-manager or ovirt) can be ported to a future libvirt for lDOM without completely changing the XML handling code. That would seriously diminish the value of the lDOM libvirt port, isn't it ?
One option is to adapt the virtualization manager to use an XML format which is based on libvirt existing format, I don't know if this is possible. One problem to consider is whether the existing format is 'public' or just internal to the existing tools.
The other option I can see is to provide a conversion layer, either in libvirt or in the manager to allow the processing of domain descriptions based on the libvirt public format. I even thing the code could detect the kind of format near trivially (for example by checking the name of the root element when receiving XML, and by using a compatibility flag when extracting XML with virDomainGetXMLDesc). I'm not sure i would be able to trivially map your existing format to a libvirt one, because i don't understand what some of the fields are for and the way you name devices are a bit strange to me, but i'm sure it can be solved. it will just take some work,
what do you think ?
Daniel

On Thu, Apr 10, 2008 at 01:46:58PM -0500, Eunice Moon wrote:
Hi Daniel,
Yes, thanks for the feedback and we are agreeing on the code changes to be done. I also agree that there is a big issue regarding different XML formats.
I don't think the first option (to change the LDoms Manager XML format to be based on the libvirt XML format) is a feasible one since LDoms has been released public and some tools/applications are already based on the LDom Manager's XML interfaces.
So, it seems like we need to go for the second option (to provide a conversion layer between the LDoms Manager and libvirt XML formats) that would require some research and scoping.
If the LDoms code is to be merged into libvirt, IMHO, it has to be 100% compliant with the defined libvirt XML format. Any conversion from the existing non-standard format will have to be done by the LDoms tools before calling into libvirt APIs, or be a add-on patch that Sun adds to their Solaris build of libvirt for back-compat. This kind of issue is precisely why code should be submitted to upstream projects immediately during the initial development and not developed in a private fork without discussion. Dan -- |: Red Hat, Engineering, Boston -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 Thu, Apr 10, 2008 at 07:54:08PM +0100, Daniel P. Berrange wrote:
If the LDoms code is to be merged into libvirt, IMHO, it has to be 100% compliant with the defined libvirt XML format.
Or extend it, surely? (I know I've whined before but it would be awfully nice to have some-one step up and update the schema: then it would be possible to insist all such changes update the schema too.) regards john

On Thu, Apr 10, 2008 at 08:05:11PM +0100, John Levon wrote:
On Thu, Apr 10, 2008 at 07:54:08PM +0100, Daniel P. Berrange wrote:
If the LDoms code is to be merged into libvirt, IMHO, it has to be 100% compliant with the defined libvirt XML format.
Or extend it, surely?
Well there are two separate scenarios here. Some of the elements in the the LDoms XML are expressing concepts for which there is no existing libvirt XML format defined. If they are suitably generic that they can be applied to other non-LDoms drivers then we can add them to the official libvirt XML format, otherwise they'll have to be changed to be suitably flexible. Other XML elements in the LDoms XML are representing things that are already represented in libvirt, but using a different format. The key is that we need an XML format that has consistent representation across all drivers. IMHO, the LDoms format as illustrated earlier in this thread is very far away from being suitable for inclusion in libvirt.
(I know I've whined before but it would be awfully nice to have some-one step up and update the schema: then it would be possible to insist all such changes update the schema too.)
Yes, but that doesn't excuse developing these extensions in private and then just dumping them on the list as a final solution. The only practical way to develop extensions to the XML format is to have upfront discussions on the proposal prior to implementation so all stakeholder have the chance to discuss the propsals. Dan. -- |: Red Hat, Engineering, Boston -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 Thu, Apr 10, 2008 at 08:33:28PM +0100, Daniel P. Berrange wrote:
If the LDoms code is to be merged into libvirt, IMHO, it has to be 100% compliant with the defined libvirt XML format.
Or extend it, surely?
Well there are two separate scenarios here. Some of the elements in the the LDoms XML are expressing concepts for which there is no existing libvirt XML format defined. If they are suitably generic that they can be applied to other non-LDoms drivers then we can add them to the official libvirt XML format, otherwise they'll have to be changed to be suitably flexible. Other XML elements in the LDoms XML are representing things that are already represented in libvirt, but using a different format.
The key is that we need an XML format that has consistent representation across all drivers. IMHO, the LDoms format as illustrated earlier in this thread is very far away from being suitable for inclusion in libvirt.
Fine. I don't disagree with any of this.
(I know I've whined before but it would be awfully nice to have some-one step up and update the schema: then it would be possible to insist all such changes update the schema too.)
Yes, but that doesn't excuse developing these extensions in private and then just dumping them on the list as a final solution.
That's hardly fair. There's a big 'RFC' in the subject and Ryan explicitly said they weren't ready. Eunice has been responding to all your comments. Who's been talking of "final solutions"?
The only practical way to develop extensions to the XML format is to have upfront discussions on the proposal prior to implementation so all stakeholder have the chance to discuss the propsals.
I'm not sure I agree with this. Talk is pretty cheap, whereas prototypes are actually useful. These patches are a prototype implementation: they've been sent out for discussion, and that's happening. I'm really not sure where the issue is. regards, john

John Levon wrote:
(I know I've whined before but it would be awfully nice to have some-one step up and update the schema: then it would be possible to insist all such changes update the schema too.) Yes, but that doesn't excuse developing these extensions in private and then just dumping them on the list as a final solution.
That's hardly fair. There's a big 'RFC' in the subject and Ryan explicitly said they weren't ready. Eunice has been responding to all your comments. Who's been talking of "final solutions"?
To quote Eunice:
I don't think the first option (to change the LDoms Manager XML format to be based on the libvirt XML format) is a feasible one since LDoms has been released public and some tools/applications are already based on the LDom Manager's XML interfaces.
How can that be interpreted as anything but 'final'? An RFC is not about implementation details, it should be about the big picture. Already shipping a supported product based on an XML format that was not discussed upstream prior is about as final as it gets, IMO. - Cole

On Thu, Apr 10, 2008 at 04:19:47PM -0400, Cole Robinson wrote:
John Levon wrote:
(I know I've whined before but it would be awfully nice to have some-one step up and update the schema: then it would be possible to insist all such changes update the schema too.) Yes, but that doesn't excuse developing these extensions in private and then just dumping them on the list as a final solution.
That's hardly fair. There's a big 'RFC' in the subject and Ryan explicitly said they weren't ready. Eunice has been responding to all your comments. Who's been talking of "final solutions"?
To quote Eunice:
I don't think the first option (to change the LDoms Manager XML format to be based on the libvirt XML format) is a feasible one since LDoms has been released public and some tools/applications are already based on the LDom Manager's XML interfaces.
How can that be interpreted as anything but 'final'? An RFC is not about implementation details, it should be about the big picture. Already shipping a supported product based on an XML format that was not discussed upstream prior is about as final as it gets, IMO.
Yes, that's exactly the point I was attempting to make. The fact that there is a shipped LDoms 'libvirt' release is a huge problem, because it has now diluted the value of libvirt, because it is not compatible with the official libvirt. Dan. -- |: Red Hat, Engineering, Boston -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 Thu, Apr 10, 2008 at 09:27:01PM +0100, Daniel P. Berrange wrote:
How can that be interpreted as anything but 'final'? An RFC is not about implementation details, it should be about the big picture. Already shipping a supported product based on an XML format that was not discussed upstream prior is about as final as it gets, IMO.
Yes, that's exactly the point I was attempting to make. The fact that there is a shipped LDoms 'libvirt' release is a huge problem, because it has now diluted the value of libvirt, because it is not compatible with the official libvirt.
That's a totally different point from Cole's actually. Cole would have been complaining about ldoms 1.0.0 (no libvirt). You're complaining about the libvirt ldoms release Sun did. AFAIK it was a "technology preview". Nothing is based on top of it. Furthermore, speaking for myself, the release of software by Sun has no relevance to the libvirt project IMO. If the project needs changes that causes problems for Sun, Sun have to take the hit. Since Eunice has already agreed that the use of the native ldoms format was a mistake, and that it will get fixed, I don't think this conversation is going to end up being productive, so I'll stop here. regards, john

On Thu, Apr 10, 2008 at 04:19:47PM -0400, Cole Robinson wrote:
That's hardly fair. There's a big 'RFC' in the subject and Ryan explicitly said they weren't ready. Eunice has been responding to all your comments. Who's been talking of "final solutions"?
To quote Eunice:
I don't think the first option (to change the LDoms Manager XML format to be based on the libvirt XML format) is a feasible one since LDoms has been released public and some tools/applications are already based on the LDom Manager's XML interfaces.
How can that be interpreted as anything but 'final'? An RFC is not about implementation details, it should be about the big picture. Already shipping a supported product based on an XML format that was not discussed upstream prior is about as final as it gets, IMO.
All Eunice is saying (pretty clearly IMO) is that the ldoms XML format is used by an entire set of software already *unrelated to libvirt*. That is Sun can't change their 'ldm' binary etc. to use libvirt's format. I wouldn't be surprised if it pre-dates the libvirt project altogether. You could equally complain about Xen's .py files. It's the same situation. regards john

John Levon wrote:
On Thu, Apr 10, 2008 at 04:19:47PM -0400, Cole Robinson wrote:
That's hardly fair. There's a big 'RFC' in the subject and Ryan explicitly said they weren't ready. Eunice has been responding to all your comments. Who's been talking of "final solutions"?
To quote Eunice:
I don't think the first option (to change the LDoms Manager XML format to be based on the libvirt XML format) is a feasible one since LDoms has been released public and some tools/applications are already based on the LDom Manager's XML interfaces. How can that be interpreted as anything but 'final'? An RFC is not about implementation details, it should be about the big picture. Already shipping a supported product based on an XML format that was not discussed upstream prior is about as final as it gets, IMO.
All Eunice is saying (pretty clearly IMO) is that the ldoms XML format is used by an entire set of software already *unrelated to libvirt*. That is Sun can't change their 'ldm' binary etc. to use libvirt's format. I wouldn't be surprised if it pre-dates the libvirt project altogether.
You could equally complain about Xen's .py files. It's the same situation.
regards john
Apologies in that case. I misunderstood what was being implied here. I thought the xml format posted was being actively built by the ldom libvirt patch, and not that it was a format which predated the libvirt support. The reality is still a regrettable situation (analogous to libvirt simply dumping a xen config file on a dumpxml command), but far better than the former. - Cole

Hi, As John mentioned, the LDoms Manager (ldm) XML interface has been used by a different set of software such as SNMP MIB, BUI, and other tools/applications (before the libvirt), so we can't really change the ldm XML formats. But, I will look into the way (conversion between the LDoms XML and libvirt XML formats) to make it compatible with the official libvirt. Thanks, Eunice Cole Robinson wrote:
John Levon wrote:
On Thu, Apr 10, 2008 at 04:19:47PM -0400, Cole Robinson wrote:
That's hardly fair. There's a big 'RFC' in the subject and Ryan explicitly said they weren't ready. Eunice has been responding to all your comments. Who's been talking of "final solutions"?
To quote Eunice:
I don't think the first option (to change the LDoms Manager XML format to be based on the libvirt XML format) is a feasible one since LDoms has been released public and some tools/applications are already based on the LDom Manager's XML interfaces. How can that be interpreted as anything but 'final'? An RFC is not about implementation details, it should be about the big picture. Already shipping a supported product based on an XML format that was not discussed upstream prior is about as final as it gets, IMO. All Eunice is saying (pretty clearly IMO) is that the ldoms XML format is used by an entire set of software already *unrelated to libvirt*. That is Sun can't change their 'ldm' binary etc. to use libvirt's format. I wouldn't be surprised if it pre-dates the libvirt project altogether.
You could equally complain about Xen's .py files. It's the same situation.
regards john
Apologies in that case. I misunderstood what was being implied here. I thought the xml format posted was being actively built by the ldom libvirt patch, and not that it was a format which predated the libvirt support. The reality is still a regrettable situation (analogous to libvirt simply dumping a xen config file on a dumpxml command), but far better than the former.
- Cole
-- Libvir-list mailing list Libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

On Thu, Apr 10, 2008 at 04:10:02PM -0500, Eunice Moon wrote:
Hi,
As John mentioned, the LDoms Manager (ldm) XML interface has been used by a different set of software such as SNMP MIB, BUI, and other tools/applications (before the libvirt), so we can't really change the ldm XML formats.
I understood the existing format comes from other legacy uses, no problem for me, looking at the second XML example made that clear :-)
But, I will look into the way (conversion between the LDoms XML and libvirt XML formats) to make it compatible with the official libvirt.
I can give an hand, it's probably also a good opportunity to refactor a lot of the XML handling code coming from the patch since we will have to change the way things are done. The hardest for me is understand the semantic of the current document, we need this anyway to find how to best map things onto the libvirt XML format (and back). Then for the revamp of the conversion code i can probably do part of it, and probably faster than you considering expertise on libxml2 ;-) But I really need a good description of the current XML format, Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

Hi Daniel, I've added some notes (lines starting with **) to the LDoms XML examples to show some semantics for the current XML tags/formats. Please see the attached files and let me know if you have any questions. Thanks, Eunice Daniel Veillard wrote:
On Thu, Apr 10, 2008 at 04:10:02PM -0500, Eunice Moon wrote:
Hi,
As John mentioned, the LDoms Manager (ldm) XML interface has been used by a different set of software such as SNMP MIB, BUI, and other tools/applications (before the libvirt), so we can't really change the ldm XML formats.
I understood the existing format comes from other legacy uses, no problem for me, looking at the second XML example made that clear :-)
But, I will look into the way (conversion between the LDoms XML and libvirt XML formats) to make it compatible with the official libvirt.
I can give an hand, it's probably also a good opportunity to refactor a lot of the XML handling code coming from the patch since we will have to change the way things are done. The hardest for me is understand the semantic of the current document, we need this anyway to find how to best map things onto the libvirt XML format (and back). Then for the revamp of the conversion code i can probably do part of it, and probably faster than you considering expertise on libxml2 ;-)
But I really need a good description of the current XML format,
Daniel

On Fri, Apr 11, 2008 at 05:39:36PM -0500, Eunice Moon wrote:
Hi Daniel,
I've added some notes (lines starting with **) to the LDoms XML examples to show some semantics for the current XML tags/formats.
Please see the attached files and let me know if you have any questions.
A couple of remarks at least: - the second XML is about communication between libvirt and the lDOM infrastructure (daemon i assume), since that's not public, not exposed to the user there is no need to change any of it. Only the instances passed to virDomainCreateLinux/virDomainDefineXML and returned by virDomainGetXMLDesc/virConnectGetCapabilities need to be aligned to match the existing XML format. - Looking at the first example, once removed the RPC encapsulation it seems it should not be too hard to map to the normal XML format, except for a couple of things: + the network bridging support definition should probably be moved at least partly to a separated set of API see virNetworkDefineXML/virNetworkCreateXML/virNetworkGetXMLDesc unless I misunderstood the scope of the definitions embedded. http://libvirt.org/formatnetwork.html + a lot of time identifiers are being used in the format, where in libvirt format we would normally use a full path to a device, maybe we can still map the identifiers back to a real device Independantly of the XML change, I think there is a well defined set of changes which were identified already, if you can provide updated patches for those, then i can probably do the XML mutation part of the patches on top of them. Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

Hi Daniel, With the new LDoms release (LDoms 1.1), we plan to provide a more generic XML format which will be using the OVF (Open Virtual Machine Format) spec: http://www.vmware.com/pdf/ovf_spec_draft.pdf Even though the OVF spec is still in the Draft stage, it can be the hypervisor independent XML format for virtual machines. I am wondering if it is in your radar or have any plan to move forward to using more generic XML format. For LDoms Manager, we will support the backward compatibility with the current LDoms XML format since other tools/applications are still using the current XML format. But, for the ldoms libvirt patch, would it be the acceptable format if we use the OVF-style XML format? Thanks, Eunice Daniel Veillard wrote:
On Thu, Apr 10, 2008 at 04:10:02PM -0500, Eunice Moon wrote:
Hi,
As John mentioned, the LDoms Manager (ldm) XML interface has been used by a different set of software such as SNMP MIB, BUI, and other tools/applications (before the libvirt), so we can't really change the ldm XML formats.
I understood the existing format comes from other legacy uses, no problem for me, looking at the second XML example made that clear :-)
But, I will look into the way (conversion between the LDoms XML and libvirt XML formats) to make it compatible with the official libvirt.
I can give an hand, it's probably also a good opportunity to refactor a lot of the XML handling code coming from the patch since we will have to change the way things are done. The hardest for me is understand the semantic of the current document, we need this anyway to find how to best map things onto the libvirt XML format (and back). Then for the revamp of the conversion code i can probably do part of it, and probably faster than you considering expertise on libxml2 ;-)
But I really need a good description of the current XML format,
Daniel

On Wed, May 07, 2008 at 03:14:52PM -0500, Eunice Moon wrote:
Hi Daniel,
With the new LDoms release (LDoms 1.1), we plan to provide a more generic XML format which will be using the OVF (Open Virtual Machine Format) spec: http://www.vmware.com/pdf/ovf_spec_draft.pdf
Even though the OVF spec is still in the Draft stage, it can be the hypervisor independent XML format for virtual machines. I am wondering if it is in your radar or have any plan to move forward to using more generic XML format.
OVF is a way to describe the generic functional requirements of a virtual machine (primarily, but not exclusively, for purposes of distribution of appliances). The libvirt XML format is describing a specific deployment of a virtual machine on a specific hypervisor. This is a subtle by very important distinction. For example, OVF may specify that a VM requires 2 disks, of size X and Y. It does not care about the path the disks are stored at, what container format they use etc. The libvirt XML format for a particular deployment of a vm does have to specify the paths to the disks, their format (qcow, vmdk) and many other aspects. The OVF format is akin to the 'virt-image' XML format for distributing virtual appliances, except that OVF is vastly more complex. So in summary there is no intention to use OVF in libvirt because it is serving a different purpose. Dan. -- |: Red Hat, Engineering, Boston -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 Thu, Apr 10, 2008 at 09:38:36PM +0100, John Levon wrote:
On Thu, Apr 10, 2008 at 04:19:47PM -0400, Cole Robinson wrote:
That's hardly fair. There's a big 'RFC' in the subject and Ryan explicitly said they weren't ready. Eunice has been responding to all your comments. Who's been talking of "final solutions"?
To quote Eunice:
I don't think the first option (to change the LDoms Manager XML format to be based on the libvirt XML format) is a feasible one since LDoms has been released public and some tools/applications are already based on the LDom Manager's XML interfaces.
How can that be interpreted as anything but 'final'? An RFC is not about implementation details, it should be about the big picture. Already shipping a supported product based on an XML format that was not discussed upstream prior is about as final as it gets, IMO.
All Eunice is saying (pretty clearly IMO) is that the ldoms XML format is used by an entire set of software already *unrelated to libvirt*. That is Sun can't change their 'ldm' binary etc. to use libvirt's format. I wouldn't be surprised if it pre-dates the libvirt project altogether.
Ok, so we've got crossed-wires here fortunately & there is no serious problem. When Eunice refer to apps using the XML, I interpreted that to mean apps using libvirt LDoms, whereas in fact it meant apps using the LDoms tools. We clearly won't expect the native LDoms tools to change. So the way forward for the LDoms libvirt driver is to define a mapping between the two XML formats and have the LDoms driver internally do the conversion either manually, or using a XSL transform. Dan. -- |: Red Hat, Engineering, Boston -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 :|

Daniel Veillard wrote:
diff --git a/configure.in b/configure.in --- a/configure.in +++ b/configure.in @@ -246,6 +246,10 @@ if test "$with_remote" = "yes" ; then LIBVIRT_FEATURES="$LIBVIRT_FEATURES -DWITH_REMOTE" fi
+if test "$with_ldoms" = "yes" ; then + LIBVIRT_FEATURES="$LIBVIRT_FEATURES -DWITH_LDOMS" +fi +
I'm a bit surprized to not see the AC_ARG_WITH(ldom, .... option block in the configure.in, was that dropped from the patch ?
This was my fault. Our build system on Solaris is quite a bit different due to automake issues, and I messed up porting it from one to the other.
I think this should be in a large part based on patform autodetection and the --with-ldom/--without-ldom should only be used for code portability tests and development.
I'll take a look at this before sending out another patch. -Ryan

On Wed, Apr 09, 2008 at 08:02:18PM -0700, Ryan Scott wrote:
Daniel Veillard wrote:
diff --git a/configure.in b/configure.in --- a/configure.in +++ b/configure.in @@ -246,6 +246,10 @@ if test "$with_remote" = "yes" ; then LIBVIRT_FEATURES="$LIBVIRT_FEATURES -DWITH_REMOTE" fi
+if test "$with_ldoms" = "yes" ; then + LIBVIRT_FEATURES="$LIBVIRT_FEATURES -DWITH_LDOMS" +fi +
I'm a bit surprized to not see the AC_ARG_WITH(ldom, .... option block in the configure.in, was that dropped from the patch ?
This was my fault. Our build system on Solaris is quite a bit different due to automake issues, and I messed up porting it from one to the other.
ah, okay :-)
I think this should be in a large part based on patform autodetection and the --with-ldom/--without-ldom should only be used for code portability tests and development.
I'll take a look at this before sending out another patch.
thanks ! I'm gonna do this for lxc now actually, Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

On Tue, Apr 08, 2008 at 10:49:01AM -0700, Ryan Scott wrote:
diff --git a/src/libvirt.c b/src/libvirt.c --- a/src/libvirt.c +++ b/src/libvirt.c @@ -48,6 +48,9 @@ #ifdef WITH_LXC #include "lxc_driver.h" #endif +#ifdef WITH_LDOMS +extern int ldomsRegister(void); +#endif
/* * TODO: @@ -267,6 +270,11 @@ virInitialize(void) * Note that the order is important: the first ones have a higher * priority when calling virConnectOpen. */ +#ifdef WITH_LDOMS + if (ldomsRegister() == -1) return -1; + /* Don't want to run any other HV with LDoms */ + return (0); +#endif
This seems rather bogus. We already have the ability to disable drivers at compile time, and choose between them based on the URI passed to the open call. Further restricting at registration time doesn't serve any obvious purpose & breaks the test driver for example, which is useful if testing apps using libvirt. And breaks the storage / networking drivers. IMHO the 'return (0)' should be removed.
+#ifdef WITH_LDOMS +/** + * virLDomConsole: + * @domain: the domain if available + * + * Opens a terminal window to the console for a domain + * + * Returns -1 in case of error, LDom console port number in case of success + */ +int +virLDomConsole(virDomainPtr domain)
This has to die. Adding driver specific functions in the public API is not acceptable. This information needs to go in the XML description.
diff --git a/src/virsh.c b/src/virsh.c --- a/src/virsh.c +++ b/src/virsh.c @@ -494,6 +494,11 @@ cmdConsole(vshControl * ctl, vshCmd * cm virDomainPtr dom; int ret = FALSE; char *doc; +#ifdef WITH_LDOMS + int port; + char command[80]; +#endif +
if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) return FALSE; @@ -501,6 +506,19 @@ cmdConsole(vshControl * ctl, vshCmd * cm if (!(dom = vshCommandOptDomain(ctl, cmd, "domain", NULL))) return FALSE;
+#ifdef WITH_LDOMS + port = virLDomConsole(dom); + if (port > 0) { + sprintf(command, "%s %d &", + "/usr/X/bin/xterm -sb -sl 1000 -e telnet localhost ", port); + system(command); + return TRUE; + } + + vshError(ctl, FALSE, _("Failed to start console")); + return FALSE; +#endif
As mentioned elsewhere on this thread, we need to define a formal description for this in the XML, and then remove the WITH_LDOMS stuff and make this a generic impl. Furthermore, poping up an xterm from virsh is non-sensical as virsh is probably already running in an xterm, and the user can create a new window themselves if they so desire.
@@ -1019,17 +1045,26 @@ cmdStart(vshControl * ctl, vshCmd * cmd) virDomainPtr dom; int ret = TRUE;
+#ifdef WITH_LDOMS + /* Need to send in the 'domain' option name instead of 'name' */ + if (!(dom = vshCommandOptDomainBy(ctl, cmd, "domain", NULL, VSH_BYNAME))) + return FALSE; +#else if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) return FALSE; +#endif
THis is not acceptable. Basically if there is any WITH_LDOMS code in virsh, then it has failed. virsh is a client of libvirt and libvirt is intended to provide a generic API.
if (!(dom = vshCommandOptDomainBy(ctl, cmd, "name", NULL, VSH_BYNAME))) return FALSE;
+ /* Allow LDoms domain state to be inactive or bound */ +#ifndef WITH_LDOMS if (virDomainGetID(dom) != (unsigned int)-1) { vshError(ctl, FALSE, "%s", _("Domain is already active")); virDomainFree(dom); return FALSE; } +#endif
if (virDomainCreate(dom) == 0) { vshPrint(ctl, _("Domain %s started\n"), @@ -1660,11 +1695,13 @@ cmdVcpuinfo(vshControl * ctl, vshCmd * c
vshPrint(ctl, "%-15s %.1lfs\n", _("CPU time:"), cpuUsed); } +#ifndef WITH_LDOMS vshPrint(ctl, "%-15s ", _("CPU Affinity:")); for (m = 0 ; m < VIR_NODEINFO_MAXCPUS(nodeinfo) ; m++) { vshPrint(ctl, "%c", VIR_CPU_USABLE(cpumap, cpumaplen, n, m) ? 'y' : '-'); } vshPrint(ctl, "\n"); +#endif
If LDoms don't have CPU affinity, then the mask should be filled such at it is all 0, or all 1 as appropriate. Then this WITH_LDOMS can go.
@@ -5087,19 +5124,23 @@ cmdQuit(vshControl * ctl, vshCmd * cmd A */ static vshCmdDef commands[] = { {"help", cmdHelp, opts_help, info_help}, +#ifndef WITH_LDOMS {"attach-device", cmdAttachDevice, opts_attach_device, info_attach_device}, {"attach-disk", cmdAttachDisk, opts_attach_disk, info_attach_disk}, {"attach-interface", cmdAttachInterface, opts_attach_interface, info_attach_interface}, {"autostart", cmdAutostart, opts_autostart, info_autostart}, {"capabilities", cmdCapabilities, NULL, info_capabilities}, {"connect", cmdConnect, opts_connect, info_connect}, +#endif /* WITH_LDOMS */ {"console", cmdConsole, opts_console, info_console}, {"create", cmdCreate, opts_create, info_create}, {"start", cmdStart, opts_start, info_start}, {"destroy", cmdDestroy, opts_destroy, info_destroy}, +#ifndef WITH_LDOMS {"detach-device", cmdDetachDevice, opts_detach_device, info_detach_device}, {"detach-disk", cmdDetachDisk, opts_detach_disk, info_detach_disk}, {"detach-interface", cmdDetachInterface, opts_detach_interface, info_detach_interface}, +#endif /* WITH_LDOMS */ {"define", cmdDefine, opts_define, info_define}, {"domid", cmdDomid, opts_domid, info_domid}, {"domuuid", cmdDomuuid, opts_domuuid, info_domuuid}, @@ -5112,7 +5153,9 @@ static vshCmdDef commands[] = { {"freecell", cmdFreecell, opts_freecell, info_freecell}, {"hostname", cmdHostname, NULL, info_hostname}, {"list", cmdList, opts_list, info_list}, +#ifndef WITH_LDOMS {"migrate", cmdMigrate, opts_migrate, info_migrate}, +#endif /* WITH_LDOMS */
{"net-autostart", cmdNetworkAutostart, opts_network_autostart, info_network_autostart}, {"net-create", cmdNetworkCreate, opts_network_create, info_network_create}, @@ -5144,20 +5187,28 @@ static vshCmdDef commands[] = { {"pool-uuid", cmdPoolUuid, opts_pool_uuid, info_pool_uuid},
{"quit", cmdQuit, NULL, info_quit}, +#ifndef WITH_LDOMS {"reboot", cmdReboot, opts_reboot, info_reboot}, {"restore", cmdRestore, opts_restore, info_restore}, {"resume", cmdResume, opts_resume, info_resume}, {"save", cmdSave, opts_save, info_save}, {"schedinfo", cmdSchedinfo, opts_schedinfo, info_schedinfo}, {"dump", cmdDump, opts_dump, info_dump}, +#endif /* WITH_LDOMS */ {"shutdown", cmdShutdown, opts_shutdown, info_shutdown}, {"setmem", cmdSetmem, opts_setmem, info_setmem}, +#ifndef WITH_LDOMS {"setmaxmem", cmdSetmaxmem, opts_setmaxmem, info_setmaxmem}, +#endif /* WITH_LDOMS */ {"setvcpus", cmdSetvcpus, opts_setvcpus, info_setvcpus}, +#ifndef WITH_LDOMS {"suspend", cmdSuspend, opts_suspend, info_suspend}, {"ttyconsole", cmdTTYConsole, opts_ttyconsole, info_ttyconsole}, +#endif /* WITH_LDOMS */ {"undefine", cmdUndefine, opts_undefine, info_undefine}, +#ifndef WITH_LDOMS {"uri", cmdURI, NULL, info_uri}, +#endif /* WITH_LDOMS */
Again, none of this is acceptable - LDoms should simply not implement the APIs in the driver, and thus virsh will print out an appropriate message such as "operation is not supported on this hypervisor"
@@ -5905,6 +5960,10 @@ vshDomainVcpuStateToString(int state) return gettext_noop("blocked"); case VIR_VCPU_RUNNING: return gettext_noop("running"); +#ifdef WITH_LDOMS + case VIR_VCPU_UNKNOWN: + return gettext_noop("unknown"); +#endif
What is this 'UNKNOWN' CPU state ?
diff --git a/src/driver.h b/src/driver.h --- a/src/driver.h +++ b/src/driver.h @@ -24,7 +24,10 @@ typedef enum { VIR_DRV_QEMU = 3, VIR_DRV_REMOTE = 4, VIR_DRV_OPENVZ = 5, - VIR_DRV_LXC = 6 + VIR_DRV_LXC = 6, +#ifdef WITH_LDOMS + VIR_DRV_LDOMS = 7 +#endif } virDrvNo;
This WITH_LDOMS is redundant - just unconditionally include the enum field.
@@ -253,6 +256,11 @@ typedef virDomainPtr const char *uri, unsigned long flags);
+#ifdef WITH_LDOMS +typedef int + (*virDrvLDomConsole) (virDomainPtr domain); +#endif + typedef struct _virDriver virDriver; typedef virDriver *virDriverPtr;
@@ -337,6 +345,9 @@ struct _virDriver { virDrvDomainInterfaceStats domainInterfaceStats; virDrvNodeGetCellsFreeMemory nodeGetCellsFreeMemory; virDrvNodeGetFreeMemory getFreeMemory; +#ifdef WITH_LDOMS + virDrvLDomConsole ldomConsole; +#endif
These have to go, as discussed above
typedef int diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -549,6 +549,9 @@ typedef enum { VIR_VCPU_OFFLINE = 0, /* the virtual CPU is offline */ VIR_VCPU_RUNNING = 1, /* the virtual CPU is running */ VIR_VCPU_BLOCKED = 2, /* the virtual CPU is blocked on resource */ +#ifdef WITH_LDOMS + VIR_VCPU_UNKNOWN = 3, /* the virtual CPU state is unknown */ +#endif
If we need this, then it'll have to be added to the API unconditionally.
+/* Don't really know what to return for this */ +static const char * +ldomsGetType(virConnectPtr conn) +{ + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsGetType(ENTER)\n");
Since you wrote this, we added a generic VIR_DEBUG call which you can use instead of the LDOMS specific dprt. If we need to make VIR_DEBUG more flexible then we can do that too.
+virDomainPtr +ldomsDomainCreateXML(virConnectPtr conn, const char *xml, unsigned int flags) +{ + virDomainPtr domPtr = NULL; + char *domainName = NULL; + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainCreateXML(ENTER) \n"); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainCreateXML(ENTER) xmldoc=\n%s\n",xml); + + /* Send the XML file along with the lifecycle action */ + domainName = send_ldom_create_domain((char *)xml, XML_ADD_DOMAIN); + + /* + * If the create/bind domain was successful, then we have to create a DomainInfo + * structure to pass back to the caller. We need the Domain name that was + * created, and the only way to get that is from parsing the input xml + * document. This is done in send_ldom_create_domain() and passed back as the return. + * Also we create the uuid for the domain name. + */ + if (domainName != NULL) { + + /* + * The UUID is not important, cuz only the Domain name is printed + * out in the virsh. So just use the default UUID. + */
This comment is bogus. UUID is a mandatory identifier that needs to be assciated with all VMs. The uniqueness rules are: - ID - unique amongst all running VMs on a host - Name - unique amongst all running & inacive VMs on a host - UUID - globally unique across hosts. All are mandatory, with exception that inactive VMs have an ID of -1.
+ /* Dump the response to memory */ + xml = malloc(sizeof(char) * 10000);
A check for NULL needed....
+ xmlKeepBlanksDefault(0); + xmlDocDumpFormatMemory(xml_received, &xml, &xmlSize, 1); + if ( xmlSize > 0 ) { + xmlFree(xml_received); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainDumpXML() xml doc size is %d:\n%s\n",xmlSize,xml); + return ((char *)xml); + } + + return (NULL); + +} /* ldomsDomainDumpXML() */
Regards, Dan. -- |: Red Hat, Engineering, Boston -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 :|

Hi Daniel, Please see my responses inline below. I've skipped some responses which are already given in another threads. Thanks for your feedback. Eunice Daniel P. Berrange wrote:
On Tue, Apr 08, 2008 at 10:49:01AM -0700, Ryan Scott wrote:
diff --git a/src/libvirt.c b/src/libvirt.c --- a/src/libvirt.c +++ b/src/libvirt.c @@ -48,6 +48,9 @@ #ifdef WITH_LXC #include "lxc_driver.h" #endif +#ifdef WITH_LDOMS +extern int ldomsRegister(void); +#endif
/* * TODO: @@ -267,6 +270,11 @@ virInitialize(void) * Note that the order is important: the first ones have a higher * priority when calling virConnectOpen. */ +#ifdef WITH_LDOMS + if (ldomsRegister() == -1) return -1; + /* Don't want to run any other HV with LDoms */ + return (0); +#endif
This seems rather bogus. We already have the ability to disable drivers at compile time, and choose between them based on the URI passed to the open call. Further restricting at registration time doesn't serve any obvious purpose & breaks the test driver for example, which is useful if testing apps using libvirt. And breaks the storage / networking drivers. IMHO the 'return (0)' should be removed.
OK. I will remove the return (0) statement here.
+#ifdef WITH_LDOMS +/** + * virLDomConsole: + * @domain: the domain if available + * + * Opens a terminal window to the console for a domain + * + * Returns -1 in case of error, LDom console port number in case of success + */ +int +virLDomConsole(virDomainPtr domain)
This has to die. Adding driver specific functions in the public API is not acceptable. This information needs to go in the XML description.
diff --git a/src/virsh.c b/src/virsh.c --- a/src/virsh.c +++ b/src/virsh.c @@ -494,6 +494,11 @@ cmdConsole(vshControl * ctl, vshCmd * cm virDomainPtr dom; int ret = FALSE; char *doc; +#ifdef WITH_LDOMS + int port; + char command[80]; +#endif +
if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) return FALSE; @@ -501,6 +506,19 @@ cmdConsole(vshControl * ctl, vshCmd * cm if (!(dom = vshCommandOptDomain(ctl, cmd, "domain", NULL))) return FALSE;
+#ifdef WITH_LDOMS + port = virLDomConsole(dom); + if (port > 0) { + sprintf(command, "%s %d &", + "/usr/X/bin/xterm -sb -sl 1000 -e telnet localhost ", port); + system(command); + return TRUE; + } + + vshError(ctl, FALSE, _("Failed to start console")); + return FALSE; +#endif
As mentioned elsewhere on this thread, we need to define a formal description for this in the XML, and then remove the WITH_LDOMS stuff and make this a generic impl. Furthermore, poping up an xterm from virsh is non-sensical as virsh is probably already running in an xterm, and the user can create a new window themselves if they so desire.
OK. I will change the code to not open an xterm. I thought it would be more useful to open up another xterm so that the user can continue to run the virsh cmds, but it looks like the user probably doesn't want to have an xterm window popping up each time for the console connection.
@@ -1019,17 +1045,26 @@ cmdStart(vshControl * ctl, vshCmd * cmd) virDomainPtr dom; int ret = TRUE;
+#ifdef WITH_LDOMS + /* Need to send in the 'domain' option name instead of 'name' */ + if (!(dom = vshCommandOptDomainBy(ctl, cmd, "domain", NULL, VSH_BYNAME))) + return FALSE; +#else if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) return FALSE; +#endif
THis is not acceptable. Basically if there is any WITH_LDOMS code in virsh, then it has failed. virsh is a client of libvirt and libvirt is intended to provide a generic API.
if (!(dom = vshCommandOptDomainBy(ctl, cmd, "name", NULL, VSH_BYNAME))) return FALSE;
+ /* Allow LDoms domain state to be inactive or bound */ +#ifndef WITH_LDOMS if (virDomainGetID(dom) != (unsigned int)-1) { vshError(ctl, FALSE, "%s", _("Domain is already active")); virDomainFree(dom); return FALSE; } +#endif
if (virDomainCreate(dom) == 0) { vshPrint(ctl, _("Domain %s started\n"), @@ -1660,11 +1695,13 @@ cmdVcpuinfo(vshControl * ctl, vshCmd * c
vshPrint(ctl, "%-15s %.1lfs\n", _("CPU time:"), cpuUsed); } +#ifndef WITH_LDOMS vshPrint(ctl, "%-15s ", _("CPU Affinity:")); for (m = 0 ; m < VIR_NODEINFO_MAXCPUS(nodeinfo) ; m++) { vshPrint(ctl, "%c", VIR_CPU_USABLE(cpumap, cpumaplen, n, m) ? 'y' : '-'); } vshPrint(ctl, "\n"); +#endif
If LDoms don't have CPU affinity, then the mask should be filled such at it is all 0, or all 1 as appropriate. Then this WITH_LDOMS can go.
@@ -5087,19 +5124,23 @@ cmdQuit(vshControl * ctl, vshCmd * cmd A */ static vshCmdDef commands[] = { {"help", cmdHelp, opts_help, info_help}, +#ifndef WITH_LDOMS {"attach-device", cmdAttachDevice, opts_attach_device, info_attach_device}, {"attach-disk", cmdAttachDisk, opts_attach_disk, info_attach_disk}, {"attach-interface", cmdAttachInterface, opts_attach_interface, info_attach_interface}, {"autostart", cmdAutostart, opts_autostart, info_autostart}, {"capabilities", cmdCapabilities, NULL, info_capabilities}, {"connect", cmdConnect, opts_connect, info_connect}, +#endif /* WITH_LDOMS */ {"console", cmdConsole, opts_console, info_console}, {"create", cmdCreate, opts_create, info_create}, {"start", cmdStart, opts_start, info_start}, {"destroy", cmdDestroy, opts_destroy, info_destroy}, +#ifndef WITH_LDOMS {"detach-device", cmdDetachDevice, opts_detach_device, info_detach_device}, {"detach-disk", cmdDetachDisk, opts_detach_disk, info_detach_disk}, {"detach-interface", cmdDetachInterface, opts_detach_interface, info_detach_interface}, +#endif /* WITH_LDOMS */ {"define", cmdDefine, opts_define, info_define}, {"domid", cmdDomid, opts_domid, info_domid}, {"domuuid", cmdDomuuid, opts_domuuid, info_domuuid}, @@ -5112,7 +5153,9 @@ static vshCmdDef commands[] = { {"freecell", cmdFreecell, opts_freecell, info_freecell}, {"hostname", cmdHostname, NULL, info_hostname}, {"list", cmdList, opts_list, info_list}, +#ifndef WITH_LDOMS {"migrate", cmdMigrate, opts_migrate, info_migrate}, +#endif /* WITH_LDOMS */
{"net-autostart", cmdNetworkAutostart, opts_network_autostart, info_network_autostart}, {"net-create", cmdNetworkCreate, opts_network_create, info_network_create}, @@ -5144,20 +5187,28 @@ static vshCmdDef commands[] = { {"pool-uuid", cmdPoolUuid, opts_pool_uuid, info_pool_uuid},
{"quit", cmdQuit, NULL, info_quit}, +#ifndef WITH_LDOMS {"reboot", cmdReboot, opts_reboot, info_reboot}, {"restore", cmdRestore, opts_restore, info_restore}, {"resume", cmdResume, opts_resume, info_resume}, {"save", cmdSave, opts_save, info_save}, {"schedinfo", cmdSchedinfo, opts_schedinfo, info_schedinfo}, {"dump", cmdDump, opts_dump, info_dump}, +#endif /* WITH_LDOMS */ {"shutdown", cmdShutdown, opts_shutdown, info_shutdown}, {"setmem", cmdSetmem, opts_setmem, info_setmem}, +#ifndef WITH_LDOMS {"setmaxmem", cmdSetmaxmem, opts_setmaxmem, info_setmaxmem}, +#endif /* WITH_LDOMS */ {"setvcpus", cmdSetvcpus, opts_setvcpus, info_setvcpus}, +#ifndef WITH_LDOMS {"suspend", cmdSuspend, opts_suspend, info_suspend}, {"ttyconsole", cmdTTYConsole, opts_ttyconsole, info_ttyconsole}, +#endif /* WITH_LDOMS */ {"undefine", cmdUndefine, opts_undefine, info_undefine}, +#ifndef WITH_LDOMS {"uri", cmdURI, NULL, info_uri}, +#endif /* WITH_LDOMS */
Again, none of this is acceptable - LDoms should simply not implement the APIs in the driver, and thus virsh will print out an appropriate message such as
"operation is not supported on this hypervisor"
@@ -5905,6 +5960,10 @@ vshDomainVcpuStateToString(int state) return gettext_noop("blocked"); case VIR_VCPU_RUNNING: return gettext_noop("running"); +#ifdef WITH_LDOMS + case VIR_VCPU_UNKNOWN: + return gettext_noop("unknown"); +#endif
What is this 'UNKNOWN' CPU state ?
We use the processor_info() to get the CPU state and this only works for the CPUs running on the primary domain. For the CPUs on the guest domains, we just map the value to the "UNKNOWN" CPU state.
diff --git a/src/driver.h b/src/driver.h --- a/src/driver.h +++ b/src/driver.h @@ -24,7 +24,10 @@ typedef enum { VIR_DRV_QEMU = 3, VIR_DRV_REMOTE = 4, VIR_DRV_OPENVZ = 5, - VIR_DRV_LXC = 6 + VIR_DRV_LXC = 6, +#ifdef WITH_LDOMS + VIR_DRV_LDOMS = 7 +#endif } virDrvNo;
This WITH_LDOMS is redundant - just unconditionally include the enum field.
@@ -253,6 +256,11 @@ typedef virDomainPtr const char *uri, unsigned long flags);
+#ifdef WITH_LDOMS +typedef int + (*virDrvLDomConsole) (virDomainPtr domain); +#endif + typedef struct _virDriver virDriver; typedef virDriver *virDriverPtr;
@@ -337,6 +345,9 @@ struct _virDriver { virDrvDomainInterfaceStats domainInterfaceStats; virDrvNodeGetCellsFreeMemory nodeGetCellsFreeMemory; virDrvNodeGetFreeMemory getFreeMemory; +#ifdef WITH_LDOMS + virDrvLDomConsole ldomConsole; +#endif
These have to go, as discussed above
typedef int diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -549,6 +549,9 @@ typedef enum { VIR_VCPU_OFFLINE = 0, /* the virtual CPU is offline */ VIR_VCPU_RUNNING = 1, /* the virtual CPU is running */ VIR_VCPU_BLOCKED = 2, /* the virtual CPU is blocked on resource */ +#ifdef WITH_LDOMS + VIR_VCPU_UNKNOWN = 3, /* the virtual CPU state is unknown */ +#endif
If we need this, then it'll have to be added to the API unconditionally.
+/* Don't really know what to return for this */ +static const char * +ldomsGetType(virConnectPtr conn) +{ + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsGetType(ENTER)\n");
Since you wrote this, we added a generic VIR_DEBUG call which you can use instead of the LDOMS specific dprt. If we need to make VIR_DEBUG more flexible then we can do that too.
+virDomainPtr +ldomsDomainCreateXML(virConnectPtr conn, const char *xml, unsigned int flags) +{ + virDomainPtr domPtr = NULL; + char *domainName = NULL; + + if (ldoms_debug) dprt("LDOMS_DEBUG: ldomsDomainCreateXML(ENTER) \n"); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainCreateXML(ENTER) xmldoc=\n%s\n",xml); + + /* Send the XML file along with the lifecycle action */ + domainName = send_ldom_create_domain((char *)xml, XML_ADD_DOMAIN); + + /* + * If the create/bind domain was successful, then we have to create a DomainInfo + * structure to pass back to the caller. We need the Domain name that was + * created, and the only way to get that is from parsing the input xml + * document. This is done in send_ldom_create_domain() and passed back as the return. + * Also we create the uuid for the domain name. + */ + if (domainName != NULL) { + + /* + * The UUID is not important, cuz only the Domain name is printed + * out in the virsh. So just use the default UUID. + */
This comment is bogus. UUID is a mandatory identifier that needs to be assciated with all VMs. The uniqueness rules are:
- ID - unique amongst all running VMs on a host - Name - unique amongst all running & inacive VMs on a host - UUID - globally unique across hosts.
All are mandatory, with exception that inactive VMs have an ID of -1.
OK. I will update the comments here.
+ /* Dump the response to memory */ + xml = malloc(sizeof(char) * 10000);
A check for NULL needed.... OK. Thanks.
+ xmlKeepBlanksDefault(0); + xmlDocDumpFormatMemory(xml_received, &xml, &xmlSize, 1); + if ( xmlSize > 0 ) { + xmlFree(xml_received); + if (ldoms_detailed_debug) dprt("LDOMS_DETAILED_DEBUG: ldomsDomainDumpXML() xml doc size is %d:\n%s\n",xmlSize,xml); + return ((char *)xml); + } + + return (NULL); + +} /* ldomsDomainDumpXML() */
Regards, Dan.
participants (7)
-
Cole Robinson
-
Daniel P. Berrange
-
Daniel Veillard
-
Eunice Moon
-
John Levon
-
Richard W.M. Jones
-
Ryan Scott