This patch adds a new driver xm_internal.c which manages inactive domains whose config files are found in /etc/xen. On certain operations the driver will scan /etc/xen for new domains - the scanning is throttled to only occur at most every 10 seconds. When scanning any new domains are loaded into memory, and any domains deleted off disk are removed. This driver can start new domains, define domains, get/set memory, max memory, vcpus, can dump XML, and list inactive domains. The list of inactive domains is filtered to remove any domains which are running. This driver is still work in progress. There is more work needed to report error exact conditions via libvirt error APIs, and the code for serializing from XML -> XM config file is not yet complete. The code for serializing from XM config files into XML is pretty much complete although needs more testing. All the APIs can be tested via the commands added to virsh for inactive domains last week. eg list --inactive, start, define, undefine I'll post another update of this patch tomorrow which should be more complete Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|
Index: src/Makefile.am =================================================================== RCS file: /data/cvs/libvirt/src/Makefile.am,v retrieving revision 1.27 diff -u -r1.27 Makefile.am --- src/Makefile.am 29 Aug 2006 22:27:07 -0000 1.27 +++ src/Makefile.am 4 Sep 2006 01:15:07 -0000 @@ -26,7 +26,8 @@ virterror.c \ driver.h \ proxy_internal.c proxy_internal.h \ - conf.c conf.h + conf.c conf.h \ + xm_internal.c xm_internal.h bin_PROGRAMS = virsh Index: src/driver.h =================================================================== RCS file: /data/cvs/libvirt/src/driver.h,v retrieving revision 1.12 diff -u -r1.12 driver.h --- src/driver.h 30 Aug 2006 14:21:03 -0000 1.12 +++ src/driver.h 4 Sep 2006 01:15:07 -0000 @@ -21,7 +21,8 @@ VIR_DRV_XEN_STORE = 2, VIR_DRV_XEN_DAEMON = 3, VIR_DRV_TEST = 4, - VIR_DRV_XEN_PROXY = 5 + VIR_DRV_XEN_PROXY = 5, + VIR_DRV_XEN_XM = 6 } virDrvNo; Index: src/internal.h =================================================================== RCS file: /data/cvs/libvirt/src/internal.h,v retrieving revision 1.23 diff -u -r1.23 internal.h --- src/internal.h 28 Jun 2006 18:19:13 -0000 1.23 +++ src/internal.h 4 Sep 2006 01:15:07 -0000 @@ -79,7 +79,7 @@ #define VIR_IS_DOMAIN(obj) ((obj) && (obj)->magic==VIR_DOMAIN_MAGIC) #define VIR_IS_CONNECTED_DOMAIN(obj) (VIR_IS_DOMAIN(obj) && VIR_IS_CONNECT((obj)->conn)) -#define MAX_DRIVERS 5 +#define MAX_DRIVERS 10 /* * Flags for Xen connections Index: src/libvirt.c =================================================================== RCS file: /data/cvs/libvirt/src/libvirt.c,v retrieving revision 1.45 diff -u -r1.45 libvirt.c --- src/libvirt.c 30 Aug 2006 14:21:03 -0000 1.45 +++ src/libvirt.c 4 Sep 2006 01:15:08 -0000 @@ -28,6 +28,7 @@ #include "xen_internal.h" #include "xend_internal.h" #include "xs_internal.h" +#include "xm_internal.h" #include "proxy_internal.h" #include "xml.h" #include "test.h" @@ -73,6 +74,7 @@ xenProxyRegister(); xenDaemonRegister(); xenStoreRegister(); + xenXMRegister(); testRegister(); return(0); } Index: src/xm_internal.c =================================================================== RCS file: src/xm_internal.c diff -N src/xm_internal.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/xm_internal.c 4 Sep 2006 01:15:11 -0000 @@ -0,0 +1,1176 @@ +/* + * xm_internal.h: helper routines for dealing with inactive domains + * + * Copyright (C) 2006 + * + * Daniel Berrange <berrange@xxxxxxxxxx> + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License. See the file COPYING.LIB in the main directory of this + * archive for more details. + */ + +#include <dirent.h> +#include <time.h> +#include <sys/stat.h> + +#include <unistd.h> +#include <stdint.h> +#include <xen/dom0_ops.h> + +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> + + +#include "xm_internal.h" +#include "xend_internal.h" +#include "conf.h" +#include "hash.h" +#include "internal.h" +#include "xml.h" + +typedef struct xenXMConfCache *xenXMConfCachePtr; +typedef struct xenXMConfCache { + time_t refreshedAt; + char filename[PATH_MAX]; + virConfPtr conf; +} xenXMConfCache; + +static virHashTablePtr configCache = NULL; +static int nconnections = 0; +static time_t lastRefresh = 0; + +#define XM_REFRESH_INTERVAL 10 + +#define XM_CONFIG_DIR "/etc/xen" +#define XM_EXAMPLE_PREFIX "xmexample" +#define XEND_CONFIG_FILE "xend-config.sxp" +#define QEMU_IF_SCRIPT "qemu-ifup" + +static virDriver xenXMDriver = { + VIR_DRV_XEN_XM, + "XenXM", + (DOM0_INTERFACE_VERSION >> 24) * 1000000 + + ((DOM0_INTERFACE_VERSION >> 16) & 0xFF) * 1000 + + (DOM0_INTERFACE_VERSION & 0xFFFF), + NULL, /* init */ + xenXMOpen, /* open */ + xenXMClose, /* close */ + xenXMGetType, /* type */ + NULL, /* version */ + NULL, /* nodeGetInfo */ + NULL, /* listDomains */ + NULL, /* numOfDomains */ + NULL, /* domainCreateLinux */ + NULL, /* domainLookupByID */ + xenXMDomainLookupByUUID, /* domainLookupByUUID */ + xenXMDomainLookupByName, /* domainLookupByName */ + NULL, /* domainSuspend */ + NULL, /* domainResume */ + NULL, /* domainShutdown */ + NULL, /* domainReboot */ + NULL, /* domainDestroy */ + NULL, /* domainFree */ + NULL, /* domainGetName */ + NULL, /* domainGetID */ + NULL, /* domainGetUUID */ + NULL, /* domainGetOSType */ + xenXMDomainGetMaxMemory, /* domainGetMaxMemory */ + xenXMDomainSetMaxMemory, /* domainSetMaxMemory */ + xenXMDomainSetMemory, /* domainMaxMemory */ + xenXMDomainGetInfo, /* domainGetInfo */ + NULL, /* domainSave */ + NULL, /* domainRestore */ + xenXMDomainSetVcpus, /* domainSetVcpus */ + NULL, /* domainPinVcpu */ + NULL, /* domainGetVcpus */ + xenXMDomainDumpXML, /* domainDumpXML */ + xenXMListDefinedDomains, /* listDefinedDomains */ + xenXMNumOfDefinedDomains, /* numOfDefinedDomains */ + xenXMDomainCreate, /* domainCreate */ + xenXMDomainDefineXML, /* domainDefineXML */ + xenXMDomainUndefine, /* domainUndefine */ +}; + +static void +xenXMError(virConnectPtr conn, virErrorNumber error, const char *info) +{ + const char *errmsg; + + if (error == VIR_ERR_OK) + return; + + errmsg = __virErrorMsg(error, info); + __virRaiseError(conn, NULL, VIR_FROM_XEND, error, VIR_ERR_ERROR, + errmsg, info, NULL, 0, 0, errmsg, info); +} + +void xenXMRegister(void) +{ + virRegisterDriver(&xenXMDriver); +} + + +static int xenXMConfigReaper(const void *payload, const char *key ATTRIBUTE_UNUSED, const void *data) { + time_t now = *(const time_t *)data; + xenXMConfCachePtr entry = (xenXMConfCachePtr)payload; + printf("Testing %s\n", key); + if (entry->refreshedAt != now) + return 1; + return 0; +} + +static void xenXMConfigFree(void *payload, const char *key ATTRIBUTE_UNUSED) { + xenXMConfCachePtr entry = (xenXMConfCachePtr)payload; + virConfFree(entry->conf); + free(entry); +} + +static int xenXMConfigCacheRefresh(void) { + DIR *dh; + struct dirent *ent; + time_t now = time(NULL); + int ret = -1; + + if (now == ((time_t)-1)) { + return -1; + } + + if ((now - lastRefresh) < XM_REFRESH_INTERVAL) + return 0; + + lastRefresh = now; + + if (!(dh = opendir(XM_CONFIG_DIR))) { + return -1; + } + + while ((ent = readdir(dh))) { + xenXMConfCachePtr entry; + struct stat st; + int newborn = 0; + char path[PATH_MAX]; + + /* Skip a bunch of cruft */ + if (!strncmp(ent->d_name, ".", 1)) + continue; + if (!strncmp(ent->d_name, XEND_CONFIG_FILE, strlen(XEND_CONFIG_FILE))) + continue; + if (!strncmp(ent->d_name, XM_EXAMPLE_PREFIX, strlen(XM_EXAMPLE_PREFIX))) + continue; + if (!strncmp(ent->d_name, QEMU_IF_SCRIPT, strlen(QEMU_IF_SCRIPT))) + continue; + /* Editor backups */ + if (ent->d_name[0] == '#') + continue; + if (ent->d_name[strlen(ent->d_name)-1] == '~') + continue; + + /* Build the full file path */ + if ((strlen(XM_CONFIG_DIR) + 1 + strlen(ent->d_name) + 1) > PATH_MAX) + continue; + strcpy(path, XM_CONFIG_DIR); + strcat(path, "/"); + strcat(path, ent->d_name); + + if ((stat(path, &st) < 0) || + !S_ISREG(st.st_mode)) { + continue; + } + + /* If we already have a matching entry and its not + modified, then carry on */ + if ((entry = virHashLookup(configCache, ent->d_name))) { + if (entry->refreshedAt >= st.st_mtime) { + entry->refreshedAt = now; + continue; + } + } + + if (entry) { /* Needs refresh */ + virConfFree(entry->conf); + entry->conf = NULL; + } else { /* New entry */ + newborn = 1; + if (!(entry = malloc(sizeof(xenXMConfCache)))) { + goto cleanup; + } + memcpy(entry->filename, path, PATH_MAX); + } + entry->refreshedAt = now; + + if (!(entry->conf = virConfReadFile(entry->filename))) { + if (!newborn) { + virHashRemoveEntry(configCache, ent->d_name, NULL); + } + free(entry); + continue; + } + + if (newborn) { + if (virHashAddEntry(configCache, ent->d_name, entry) < 0) { + virConfFree(entry->conf); + free(entry); + goto cleanup; + } + } + } + + virHashRemoveSet(configCache, xenXMConfigReaper, xenXMConfigFree, (const void*) &now); + ret = 0; + + cleanup: + if (dh) + closedir(dh); + + return ret; +} + + +static int xenXMConfigGetInt(virConfPtr conf, const char *name, long *value) { + virConfValuePtr val; + if (!value || !name || !conf) + return -1; + + if (!(val = virConfGetValue(conf, name))) { + return -1; + } + + if (val->type == VIR_CONF_LONG) { + *value = val->l; + } else if (val->type == VIR_CONF_STRING) { + char *ret; + if (!val->str) + return -1; + *value = strtol(val->str, &ret, 10); + if (ret == val->str) + return -1; + } else { + return -1; + } + return 0; +} + + +static int xenXMConfigGetString(virConfPtr conf, const char *name, const char **value) { + virConfValuePtr val; + if (!value || !name || !conf) + return -1; + *value = NULL; + if (!(val = virConfGetValue(conf, name))) { + return -1; + } + if (val->type != VIR_CONF_STRING) + return -1; + if (!val->str) + return -1; + *value = val->str; + return 0; +} + + +static int xenXMConfigGetUUID(virConfPtr conf, const char *name, unsigned char *uuid) { + virConfValuePtr val; + char *rawuuid = (char *)uuid; + if (!uuid || !name || !conf) + return -1; + if (!(val = virConfGetValue(conf, name))) { + return -1; + } + + if (val->type != VIR_CONF_STRING) + return -1; + if (!val->str) + return -1; + + if (!virParseUUID(&rawuuid, val->str)) + return -1; + + return 0; +} + + +int xenXMOpen(virConnectPtr conn ATTRIBUTE_UNUSED, const char *name, int flags ATTRIBUTE_UNUSED) { + if (name && + strcasecmp(name, "xen")) { + return -1; + } + + if (nconnections == 0) { + configCache = virHashCreate(50); + if (!configCache) + return -1; + } + nconnections++; + + return 0; +} + +int xenXMClose(virConnectPtr conn ATTRIBUTE_UNUSED) { + if (!nconnections--) { + virHashFree(configCache, xenXMConfigFree); + configCache = NULL; + } + return 0; +} +const char *xenXMGetType(virConnectPtr conn ATTRIBUTE_UNUSED) { + return "XenXM"; +} +int xenXMDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info) { + xenXMConfCachePtr entry; + long vcpus; + long mem; + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return(-1); + } + + if (domain->handle != -1) + return (-1); + + if (!(entry = virHashLookup(configCache, domain->name))) + return (-1); + + memset(info, 0, sizeof(virDomainInfo)); + if (xenXMConfigGetInt(entry->conf, "memory", &mem) < 0 || + mem < 0) + info->memory = 64 * 1024; + else + info->memory = (unsigned long)mem * 1024; + if (xenXMConfigGetInt(entry->conf, "maxmem", &mem) < 0 || + mem < 0) + info->maxMem = info->memory; + else + info->maxMem = (unsigned long)mem * 1024; + + if (xenXMConfigGetInt(entry->conf, "vcpus", &vcpus) < 0 || + vcpus < 0) + info->nrVirtCpu = 1; + else + info->nrVirtCpu = (unsigned short)vcpus; + info->state = VIR_DOMAIN_SHUTOFF; + info->cpuTime = 0; + + return 0; + +} + +char *xenXMDomainDumpXML(virDomainPtr domain, int flags ATTRIBUTE_UNUSED) { + virBufferPtr buf; + xenXMConfCachePtr entry; + char *xml; + const char *name; + unsigned char uuid[16]; + const char *str; + int hvm = 0; + long val; + virConfValuePtr list; + + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return(NULL); + } + if (domain->handle != -1) + return (NULL); + if (!(entry = virHashLookup(configCache, domain->name))) + return(NULL); + + if (xenXMConfigGetString(entry->conf, "name", &name) < 0) + return(NULL); + if (xenXMConfigGetUUID(entry->conf, "uuid", uuid) < 0) + return(NULL); + + buf = virBufferNew(4096); + + virBufferAdd(buf, "<domain type='xen'>\n", -1); + virBufferVSprintf(buf, " <name>%s</name>\n", name); + virBufferVSprintf(buf, + " <uuid>%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x</uuid>\n", + uuid[0], uuid[1], uuid[2], uuid[3], + uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], + uuid[12], uuid[13], uuid[14], uuid[15]); + + if ((xenXMConfigGetString(entry->conf, "builder", &str) == 0) && + !strcmp(str, "hvm")) + hvm = 1; + + if (hvm) { + virBufferAdd(buf, " <os>\n", -1); + virBufferAdd(buf, " <type>hvm</type>\n", -1); + if (xenXMConfigGetString(entry->conf, "kernel", &str) == 0) + virBufferVSprintf(buf, " <loader>%s</loader>\n", str); + virBufferAdd(buf, " </os>\n", -1); + } else { + + if (xenXMConfigGetString(entry->conf, "bootloader", &str) == 0) + virBufferVSprintf(buf, " <bootloader>%s</bootloader>\n", str); + if (xenXMConfigGetString(entry->conf, "kernel", &str) == 0) { + virBufferAdd(buf, " <os>\n", -1); + virBufferAdd(buf, " <type>linux</type>\n", -1); + virBufferVSprintf(buf, " <kernel>%s</kernel>\n", str); + if (xenXMConfigGetString(entry->conf, "ramdisk", &str) == 0) + virBufferVSprintf(buf, " <initrd>%s</initrd>\n", str); + if (xenXMConfigGetString(entry->conf, "extra", &str) == 0) + virBufferVSprintf(buf, " <cmdline>%s</cmdline>\n", str); + virBufferAdd(buf, " </os>\n", -1); + } + } + + if (xenXMConfigGetInt(entry->conf, "memory", &val) < 0) + val = 64; + virBufferVSprintf(buf, " <memory>%ld</memory>\n", val * 1024); + + if (xenXMConfigGetInt(entry->conf, "vcpus", &val) < 0) + val = 1; + virBufferVSprintf(buf, " <vcpu>%ld</vcpu>\n", val); + + + + if (xenXMConfigGetString(entry->conf, "on_poweroff", &str) < 0) + str = "destroy"; + virBufferVSprintf(buf, " <on_poweroff>%s</on_poweroff>\n", str); + + if (xenXMConfigGetString(entry->conf, "on_reboot", &str) < 0) + str = "restart"; + virBufferVSprintf(buf, " <on_reboot>%s</on_reboot>\n", str); + + if (xenXMConfigGetString(entry->conf, "on_crash", &str) < 0) + str = "restart"; + virBufferVSprintf(buf, " <on_crash>%s</on_crash>\n", str); + + + if (hvm) { + virBufferAdd(buf, " <features>\n", -1); + if (xenXMConfigGetInt(entry->conf, "pae", &val) == 0 && + val) + virBufferAdd(buf, " <pae/>\n", -1); + if (xenXMConfigGetInt(entry->conf, "acpi", &val) == 0 && + val) + virBufferAdd(buf, " <acpi/>\n", -1); + if (xenXMConfigGetInt(entry->conf, "apic", &val) == 0 && + val) + virBufferAdd(buf, " <apic/>\n", -1); + virBufferAdd(buf, " </features>\n", -1); + } + + virBufferAdd(buf, " <devices>\n", -1); + + list = virConfGetValue(entry->conf, "disk"); + while (list && list->type == VIR_CONF_LIST) { + virConfValuePtr el = list->list; + int block = 0; + char dev[NAME_MAX]; + char src[PATH_MAX]; + char *type; + char *mode; + if ((el== NULL) || (el->type != VIR_CONF_STRING) || (el->str == NULL)) + goto skipdisk; + + if (!(type = index(el->str, ',')) || type[0] == '\0') + goto skipdisk; + type++; + if (!(mode = index(type, ',')) || mode[0] == '\0') + goto skipdisk; + mode++; + + if (!strncmp(el->str, "phy:", 4)) { + int len = (type - el->str)-1; + if (len > (PATH_MAX-1)) + goto skipdisk; + strncpy(src, el->str+4, len-4); + src[len-4] = '\0'; + block = 1; + } else if (!strncmp(el->str, "file:", 5)) { + int len = (type - el->str)-1; + if (len > (PATH_MAX-1)) + goto skipdisk; + strncpy(src, el->str+5, len-5); + src[len-5] = '\0'; + block = 0; + } else { + goto skipdisk; + } + + if ((mode-type-1) > (NAME_MAX-1)) { + goto skipdisk; + } + strncpy(dev, type, (mode-type-1)); + dev[(mode-type-1)] = '\0'; + + virBufferVSprintf(buf, " <disk type='%s' device='disk'>\n", block ? "block" : "file"); + virBufferVSprintf(buf, " <source %s='%s'/>\n", block ? "device" : "file", src); + virBufferVSprintf(buf, " <target dev='%s'/>\n", dev); + if (*mode == 'r') + virBufferAdd(buf, " <readonly/>\n", -1); + virBufferAdd(buf, " </disk>\n", -1); + + skipdisk: + list = list->next; + } + + list = virConfGetValue(entry->conf, "vif"); + while (list && list->type == VIR_CONF_LIST) { + virConfValuePtr el = list->list; + int type = -1; + char script[PATH_MAX]; + char mac[18]; + char *key; + + mac[0] = '\0'; + script[0] = '\0'; + + if ((el== NULL) || (el->type != VIR_CONF_STRING) || (el->str == NULL)) + goto skipnic; + + key = el->str; + while (key) { + char *data; + char *nextkey = index(key, ','); + if (!(data = index(key, '=')) || (data[0] == '\0')) + goto skipnic; + data++; + + if (!strncmp(key, "mac=", 4)) { + int len = nextkey ? (nextkey - data) : 17; + if (len > 17) + len = 17; + strncpy(mac, data, len); + mac[len] = '\0'; + } else if (!strncmp(key, "bridge=", 7)) { + type = 1; + } else if (!strncmp(key, "script=", 7)) { + int len = nextkey ? (nextkey - data) : PATH_MAX-1; + if (len > (PATH_MAX-1)) + len = PATH_MAX-1; + strncpy(script, data, len); + script[len] = '\0'; + } + + if (nextkey && nextkey[0]) + nextkey++; + key = nextkey; + } + + /* XXX Forcing to pretend its a bridge */ + if (type == -1) { + type = 1; + } + + virBufferAdd(buf, " <interface type='bridge'>\n", -1); + if (mac[0]) + virBufferVSprintf(buf, " <mac address='%s'/>\n", mac); + if (script[0]) + virBufferVSprintf(buf, " <script path='%s'/>\n", script); + virBufferAdd(buf, " </interface>\n", -1); + + skipnic: + list = list->next; + } + + virBufferAdd(buf, " </devices>\n", -1); + + virBufferAdd(buf, "</domain>\n", -1); + + xml = buf->content; + buf->content = NULL; + virBufferFree(buf); + return xml; +} +int xenXMDomainSetMemory(virDomainPtr domain, unsigned long memory) { + xenXMConfCachePtr entry; + virConfValuePtr value; + + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return(-1); + } + if (domain->conn->flags & VIR_CONNECT_RO) + return (-1); + if (domain->handle != -1) + return (-1); + + if (!(entry = virHashLookup(configCache, domain->name))) + return (-1); + + if (!(value = malloc(sizeof(virConfValue)))) + return (-1); + + value->type = VIR_CONF_LONG; + value->l = (memory/1024); + + if (virConfSetValue(entry->conf, "memory", value) < 0) { + free(value); + return (-1); + } + + /* If this fails, should we try to undo our changes to the + * in-memory representation of the config file. I say not! + */ + if (virConfWriteFile(entry->filename, entry->conf) < 0) + return (-1); + + return 0; +} + +int xenXMDomainSetMaxMemory(virDomainPtr domain, unsigned long memory) { + xenXMConfCachePtr entry; + virConfValuePtr value; + + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return(-1); + } + if (domain->conn->flags & VIR_CONNECT_RO) + return (-1); + if (domain->handle != -1) + return (-1); + + if (!(entry = virHashLookup(configCache, domain->name))) + return (-1); + + if (!(value = malloc(sizeof(virConfValue)))) + return (-1); + + value->type = VIR_CONF_LONG; + value->l = (memory/1024); + + if (virConfSetValue(entry->conf, "maxmem", value) < 0) { + free(value); + return (-1); + } + + /* If this fails, should we try to undo our changes to the + * in-memory representation of the config file. I say not! + */ + if (virConfWriteFile(entry->filename, entry->conf) < 0) + return (-1); + + return 0; +} + +unsigned long xenXMDomainGetMaxMemory(virDomainPtr domain) { + xenXMConfCachePtr entry; + long val; + + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return(-1); + } + if (domain->handle != -1) + return (-1); + + if (!(entry = virHashLookup(configCache, domain->name))) + return (-1); + + if (xenXMConfigGetInt(entry->conf, "maxmem", &val) < 0 || + val < 0) + if (xenXMConfigGetInt(entry->conf, "memory", &val) < 0 || + val < 0) + val = 64; + + return val * 1024; +} + +int xenXMDomainSetVcpus(virDomainPtr domain, unsigned int vcpus) { + xenXMConfCachePtr entry; + virConfValuePtr value; + + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return(-1); + } + if (domain->conn->flags & VIR_CONNECT_RO) + return (-1); + if (domain->handle != -1) + return (-1); + + if (!(entry = virHashLookup(configCache, domain->name))) + return (-1); + + if (!(value = malloc(sizeof(virConfValue)))) + return (-1); + + value->type = VIR_CONF_LONG; + value->l = vcpus; + + if (virConfSetValue(entry->conf, "vcpus", value) < 0) { + free(value); + return (-1); + } + + /* If this fails, should we try to undo our changes to the + * in-memory representation of the config file. I say not! + */ + if (virConfWriteFile(entry->filename, entry->conf) < 0) + return (-1); + + return 0; +} + +virDomainPtr xenXMDomainLookupByName(virConnectPtr conn, const char *domname) { + xenXMConfCachePtr entry; + virDomainPtr ret; + unsigned char uuid[16]; + if (!VIR_IS_CONNECT(conn)) { + xenXMError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + if (domname == NULL) { + xenXMError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + + if (xenXMConfigCacheRefresh() < 0) + return (NULL); + + if (!(entry = virHashLookup(configCache, domname))) { + return (NULL); + } + + + if (xenXMConfigGetUUID(entry->conf, "uuid", uuid) < 0) { + return (NULL); + } + + if (!(ret = virGetDomain(conn, domname, uuid))) { + return (NULL); + } + + return ret; +} + +static int xenXMDomainSearchForUUID(const void *payload, const char *name ATTRIBUTE_UNUSED, const void *data) { + unsigned char uuid[16]; + const unsigned char *wantuuid = (const unsigned char *)data; + const xenXMConfCachePtr entry = (const xenXMConfCachePtr)payload; + + if (xenXMConfigGetUUID(entry->conf, "uuid", uuid) < 0) { + return 0; + } + + if (!memcmp(uuid, wantuuid, 16)) + return 1; + + return 0; +} + +virDomainPtr xenXMDomainLookupByUUID(virConnectPtr conn, + const unsigned char *uuid) { + xenXMConfCachePtr entry; + virDomainPtr ret; + const char *domname; + + if (!VIR_IS_CONNECT(conn)) { + xenXMError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + if (uuid == NULL) { + xenXMError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + + if (xenXMConfigCacheRefresh() < 0) + return (NULL); + + if (!(entry = virHashSearch(configCache, xenXMDomainSearchForUUID, (const void *)uuid))) { + return (NULL); + } + + if (xenXMConfigGetString(entry->conf, "name", &domname) < 0) { + return (NULL); + } + + if (!(ret = virGetDomain(conn, domname, uuid))) { + return (NULL); + } + + return ret; +} + + +int xenXMDomainCreate(virDomainPtr domain) { + char *xml; + char *sexpr; + int ret; + unsigned char uuid[16]; + + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return(-1); + } + + if (domain->handle != -1) + return (-1); + if (domain->conn->flags & VIR_CONNECT_RO) + return (-1); + + if (!(xml = xenXMDomainDumpXML(domain, 0))) + return (-1); + + if (!(sexpr = virDomainParseXMLDesc(xml, NULL))) { + free(xml); + return (-1); + } + free(xml); + + ret = xenDaemonDomainCreateLinux(domain->conn, sexpr); + free(sexpr); + if (ret != 0) { + fprintf(stderr, "Failed to create domain %s\n", domain->name); + return (-1); + } + + ret = xend_wait_for_devices(domain->conn, domain->name); + if (ret != 0) { + fprintf(stderr, "Failed to get devices for domain %s\n", domain->name); + return (-1); + } + + if ((ret = xenDaemonDomainLookupByName_ids(domain->conn, domain->name, uuid)) < 0) { + return (-1); + } + domain->handle = ret; + + ret = xenDaemonDomainResume(domain); + if (ret != 0) { + fprintf(stderr, "Failed to resume new domain %s\n", domain->name); + xenDaemonDomainDestroy(domain); + domain->handle = -1; + return (-1); + } + + return 0; +} + +static +int xenXMConfigSetIntFromXPath(virConfPtr conf, xmlXPathContextPtr ctxt, + const char *setting, const char *xpath, + int allowMissing, long scale) { + xmlXPathObjectPtr obj; + virConfValuePtr value = NULL; + long intval; + char *strend; + int ret = -1; + + obj = xmlXPathEval(BAD_CAST xpath, ctxt); + if ((obj == NULL) && allowMissing) + return 0; + + if ((obj == NULL) || (obj->type != XPATH_STRING) || + (obj->stringval == NULL) || (obj->stringval[0] == 0)) + goto error; + + intval = strtol((char *)obj->stringval, &strend, 10); + if (strend == (char *)obj->stringval) + goto error; + + if (!(value = malloc(sizeof(virConfValue)))) + goto error; + + value->type = VIR_CONF_LONG; + value->next = NULL; + if (scale > 0) + value->l = intval * scale; + else if (scale < 0) + value->l = intval / (-1*scale); + else + value->l = intval; + + if (virConfSetValue(conf, setting, value) < 0) { + goto error; + } + + ret = 0; + + error: + if (obj) + xmlXPathFreeObject(obj); + + if (ret == -1 && value) + free(value); + + return ret; +} + +static +int xenXMConfigSetStringFromXPath(virConfPtr conf, xmlXPathContextPtr ctxt, + const char *setting, const char *xpath, + int allowMissing) { + xmlXPathObjectPtr obj; + virConfValuePtr value = NULL; + int ret = -1; + + obj = xmlXPathEval(BAD_CAST xpath, ctxt); + if ((obj == NULL) && allowMissing) + return 0; + + if ((obj == NULL) || (obj->type != XPATH_STRING) || + (obj->stringval == NULL) || (obj->stringval[0] == 0)) + goto error; + + if (!(value = malloc(sizeof(virConfValue)))) + goto error; + + value->type = VIR_CONF_STRING; + value->next = NULL; + if (!(value->str = strdup((char*)obj->stringval))) + goto error; + + if (virConfSetValue(conf, setting, value) < 0) { + goto error; + } + + ret = 0; + + error: + if (obj) + xmlXPathFreeObject(obj); + + if (ret == -1) { + if (value && value->str) + free(value->str); + if (value) + free(value); + } + + return ret; +} + +virDomainPtr xenXMDomainDefineXML(virConnectPtr conn, const char *xml) { + virDomainPtr ret; + char filename[PATH_MAX]; + unsigned char uuid[16]; + xmlDocPtr doc = NULL; + xmlNodePtr node; + xmlXPathObjectPtr obj = NULL; + xmlXPathContextPtr ctxt = NULL; + xmlChar *prop = NULL; + virConfPtr conf = NULL; + virConfValuePtr value = NULL; + xenXMConfCachePtr entry = NULL; + + if (!VIR_IS_CONNECT(conn)) { + xenXMError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + if (xml == NULL) { + xenXMError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + if (conn->flags & VIR_CONNECT_RO) + return (NULL); + + if (xenXMConfigCacheRefresh() < 0) + return (NULL); + + doc = xmlReadDoc((const xmlChar *) xml, "domain.xml", NULL, + XML_PARSE_NOENT | XML_PARSE_NONET | + XML_PARSE_NOERROR | XML_PARSE_NOWARNING); + if (doc == NULL) { + return (NULL); + } + node = xmlDocGetRootElement(doc); + if ((node == NULL) || (!xmlStrEqual(node->name, BAD_CAST "domain"))) + goto error; + + prop = xmlGetProp(node, BAD_CAST "type"); + if (prop != NULL) { + if (!xmlStrEqual(prop, BAD_CAST "xen")) { + goto error; + } + xmlFree(prop); + prop = NULL; + } + if (!(ctxt = xmlXPathNewContext(doc))) + goto error; + + if (!(conf = virConfNew())) + goto error; + + if (xenXMConfigSetStringFromXPath(conf, ctxt, "name", "string(/domain/name)", 0) < 0) + goto error; + + if (xenXMConfigSetStringFromXPath(conf, ctxt, "uuid", "string(/domain/uuid)", 0) < 0) + goto error; + + if (xenXMConfigSetIntFromXPath(conf, ctxt, "memory", "string(/domain/memory)", 0, -1024) < 0) + goto error; + + if (xenXMConfigSetIntFromXPath(conf, ctxt, "vcpus", "string(/domain/vcpu)", 0, 0) < 0) + goto error; + + if (xenXMConfigSetIntFromXPath(conf, ctxt, "pae", "string(count(/domain/features/pae))", 0, 0) < 0) + goto error; + + if (xenXMConfigSetIntFromXPath(conf, ctxt, "acpi", "string(count(/domain/features/acpi))", 0, 0) < 0) + goto error; + + if (xenXMConfigSetIntFromXPath(conf, ctxt, "apic", "string(count(/domain/features/apic))", 0, 0) < 0) + goto error; + + + if (xenXMConfigSetStringFromXPath(conf, ctxt, "on_poweroff", "string(/domain/on_poweroff)", 1) < 0) + goto error; + + if (xenXMConfigSetStringFromXPath(conf, ctxt, "on_reboot", "string(/domain/on_reboot)", 1) < 0) + goto error; + + if (xenXMConfigSetStringFromXPath(conf, ctxt, "on_crash", "string(/domain/on_crash)", 1) < 0) + goto error; + + + if (!(value = virConfGetValue(conf, "name")) || value->type != VIR_CONF_STRING || value->str == NULL) + goto error; + + if (virHashLookup(configCache, value->str) != 0) + goto error; + + if ((strlen(XM_CONFIG_DIR) + 1 + strlen(value->str) + 1) > PATH_MAX) + goto error; + + strcpy(filename, XM_CONFIG_DIR); + strcat(filename, "/"); + strcat(filename, value->str); + + if (virConfWriteFile(filename, conf) < 0) + goto error; + + if (!(entry = malloc(sizeof(xenXMConfCache)))) + goto error; + memset(entry, 0, sizeof(xenXMConfCache)); + + if ((entry->refreshedAt = time(NULL)) == ((time_t)-1)) + goto error; + + memmove(entry->filename, filename, PATH_MAX); + entry->conf = conf; + + if (xenXMConfigGetUUID(conf, "uuid", uuid) < 0) + goto error; + + printf("Adding entry %s\n", value->str); + if (virHashAddEntry(configCache, value->str, entry) < 0) + goto error; + entry = NULL; + + if (!(ret = virGetDomain(conn, value->str, uuid))) + goto error; + ret->handle = -1; + + return ret; + + error: + if (entry) + free(entry); + if (conf) + virConfFree(conf); + if (prop != NULL) + xmlFree(prop); + if (obj != NULL) + xmlXPathFreeObject(obj); + if (ctxt != NULL) + xmlXPathFreeContext(ctxt); + if (doc != NULL) + xmlFreeDoc(doc); + return NULL; +} + +int xenXMDomainUndefine(virDomainPtr domain) { + xenXMConfCachePtr entry; + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return(-1); + } + + if (domain->handle != -1) + return (-1); + if (domain->conn->flags & VIR_CONNECT_RO) + return (-1); + + if (!(entry = virHashLookup(configCache, domain->name))) + return (-1); + + if (unlink(entry->filename) < 0) + return (-1); + + if (virHashRemoveEntry(configCache, domain->name, xenXMConfigFree) < 0) + return (-1); + + return 0; +} + +struct xenXMListIteratorContext { + virConnectPtr conn; + int max; + int count; + const char **names; +}; + +static void xenXMListIterator(const void *payload ATTRIBUTE_UNUSED, const char *name, const void *data) { + struct xenXMListIteratorContext *ctx = (struct xenXMListIteratorContext *)data; + virDomainPtr dom; + + if (ctx->count == ctx->max) + return; + + dom = xenDaemonDomainLookupByName(ctx->conn, name); + if (!dom) { + ctx->names[ctx->count] = strdup(name); + ctx->count++; + } else { + virDomainFree(dom); + } +} + +int xenXMListDefinedDomains(virConnectPtr conn, const char **names, int maxnames) { + struct xenXMListIteratorContext ctx; + + if (!VIR_IS_CONNECT(conn)) { + xenXMError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if (xenXMConfigCacheRefresh() < 0) + return -1; + + if (maxnames > virHashSize(configCache)) + maxnames = virHashSize(configCache); + + ctx.conn = conn; + ctx.count = 0; + ctx.max = maxnames; + ctx.names = names; + + virHashForEach(configCache, xenXMListIterator, &ctx); + return ctx.count; +} +int xenXMNumOfDefinedDomains(virConnectPtr conn) { + if (!VIR_IS_CONNECT(conn)) { + xenXMError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if (xenXMConfigCacheRefresh() < 0) + return -1; + + return virHashSize(configCache); +} + +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ Index: src/xm_internal.h =================================================================== RCS file: src/xm_internal.h diff -N src/xm_internal.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/xm_internal.h 4 Sep 2006 01:15:11 -0000 @@ -0,0 +1,47 @@ +/* + * xm_internal.h: helper routines for dealing with inactive domains + * + * Copyright (C) 2006 + * + * Daniel Berrange <berrange@xxxxxxxxxx> + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License. See the file COPYING.LIB in the main directory of this + * archive for more details. + */ + +#ifndef _LIBVIRT_XM_INTERNAL_H_ +#define _LIBVIRT_XM_INTERNAL_H_ + +#include "libvirt/libvirt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void xenXMRegister(void); +int xenXMOpen(virConnectPtr conn, const char *name, int flags); +int xenXMClose(virConnectPtr conn); +const char *xenXMGetType(virConnectPtr conn); +int xenXMDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info); +char *xenXMDomainDumpXML(virDomainPtr domain, int flags); +int xenXMDomainSetMemory(virDomainPtr domain, unsigned long memory); +int xenXMDomainSetMaxMemory(virDomainPtr domain, unsigned long memory); +unsigned long xenXMDomainGetMaxMemory(virDomainPtr domain); +int xenXMDomainSetVcpus(virDomainPtr domain, unsigned int vcpus); +virDomainPtr xenXMDomainLookupByName(virConnectPtr conn, const char *domname); +virDomainPtr xenXMDomainLookupByUUID(virConnectPtr conn, + const unsigned char *uuid); + +int xenXMListDefinedDomains(virConnectPtr conn, const char **names, int maxnames); +int xenXMNumOfDefinedDomains(virConnectPtr conn); + +int xenXMDomainCreate(virDomainPtr domain); +virDomainPtr xenXMDomainDefineXML(virConnectPtr con, const char *xml); +int xenXMDomainUndefine(virDomainPtr domain); + + +#ifdef __cplusplus +} +#endif +#endif