+Thu Nov 16 18:18:12 CET 2006 Daniel Veillard <veillard@redhat.com>
+
+ * include/libvirt/libvirt.h include/libvirt/libvirt.h.in
+ src/driver.h src/libvirt.c src/libvirt_sym.version
+ src/proxy_internal.c src/test.c src/xen_internal.c
+ src/xend_internal.c src/xml.c src/xml.h src/xs_internal.c:
+ intagrated patch from Michel Ponceau to add hot-plug devices
+ support to the API, integrated in driver API and fixed
+ a few small things. Still a TODO in src/xml.c about
+ moving xenstore direct accesses to a new routine.
+
Wed Nov 15 18:23:13 EST 2006 Daniel Berrange <berrange@redhat.com>
* python/generator.py, python/libvir.c, python/libvirt-python-api.xml:
*/
#define VIR_GET_CPUMAP(cpumaps,maplen,vcpu) &(cpumaps[(vcpu)*(maplen)])
+int virDomainAttachDevice(virDomainPtr domain, char *xml);
+int virDomainDetachDevice(virDomainPtr domain, char *xml);
#ifdef __cplusplus
}
*/
#define VIR_GET_CPUMAP(cpumaps,maplen,vcpu) &(cpumaps[(vcpu)*(maplen)])
+int virDomainAttachDevice(virDomainPtr domain, char *xml);
+int virDomainDetachDevice(virDomainPtr domain, char *xml);
#ifdef __cplusplus
}
int maxinfo,
unsigned char *cpumaps,
int maplen);
+typedef int
+ (*virDrvDomainAttachDevice) (virDomainPtr domain,
+ char *xml);
+typedef int
+ (*virDrvDomainDetachDevice) (virDomainPtr domain,
+ char *xml);
typedef struct _virDriver virDriver;
typedef virDriver *virDriverPtr;
virDrvDomainCreate domainCreate;
virDrvDomainDefineXML domainDefineXML;
virDrvDomainUndefine domainUndefine;
+ virDrvDomainAttachDevice domainAttachDevice;
+ virDrvDomainDetachDevice domainDetachDevice;
};
virLibConnError(conn, VIR_ERR_CALL_FAILED, __FUNCTION__);
return (-1);
}
+
+/**
+ * virDomainAttachDevice:
+ * @domain: pointer to domain object
+ * @xml: pointer to XML description of one device
+ *
+ * Create a virtual device attachment to backend.
+ *
+ * Returns 0 in case of success, -1 in case of failure.
+ */
+int
+virDomainAttachDevice(virDomainPtr domain, char *xml)
+{
+ int ret;
+ int i;
+ virConnectPtr conn;
+
+ if (!VIR_IS_CONNECTED_DOMAIN(domain)) {
+ virLibDomainError(domain, VIR_ERR_INVALID_DOMAIN, __FUNCTION__);
+ return (-1);
+ }
+ if (domain->conn->flags & VIR_CONNECT_RO) {
+ virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__);
+ return (-1);
+ }
+ conn = domain->conn;
+
+ /*
+ * Go though the driver registered entry points
+ */
+ for (i = 0;i < conn->nb_drivers;i++) {
+ if ((conn->drivers[i] != NULL) &&
+ (conn->drivers[i]->domainAttachDevice != NULL)) {
+ ret = conn->drivers[i]->domainAttachDevice(domain, xml);
+ if (ret >= 0)
+ return(ret);
+ }
+ }
+ virLibConnError(conn, VIR_ERR_CALL_FAILED, __FUNCTION__);
+ return (-1);
+}
+
+/**
+ * virDomainDetachDevice:
+ * @domain: pointer to domain object
+ * @xml: pointer to XML description of one device
+ *
+ * Destroy a virtual device attachment to backend.
+ *
+ * Returns 0 in case of success, -1 in case of failure.
+ */
+int
+virDomainDetachDevice(virDomainPtr domain, char *xml)
+{
+ int ret;
+ int i;
+ virConnectPtr conn;
+
+ if (!VIR_IS_CONNECTED_DOMAIN(domain)) {
+ virLibDomainError(domain, VIR_ERR_INVALID_DOMAIN, __FUNCTION__);
+ return (-1);
+ }
+ if (domain->conn->flags & VIR_CONNECT_RO) {
+ virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__);
+ return (-1);
+ }
+ conn = domain->conn;
+
+ /*
+ * Go though the driver registered entry points
+ */
+ for (i = 0;i < conn->nb_drivers;i++) {
+ if ((conn->drivers[i] != NULL) &&
+ (conn->drivers[i]->domainDetachDevice != NULL)) {
+ ret = conn->drivers[i]->domainDetachDevice(domain, xml);
+ if (ret >= 0)
+ return(ret);
+ }
+ }
+ virLibConnError(conn, VIR_ERR_CALL_FAILED, __FUNCTION__);
+ return (-1);
+}
virDomainSetVcpus;
virDomainPinVcpu;
virDomainGetVcpus;
+
+ virDomainAttachDevice;
+ virDomainDetachDevice;
local: *;
};
NULL, /* domainCreate */
NULL, /* domainDefineXML */
NULL, /* domainUndefine */
+ NULL, /* domainAttachDevice */
+ NULL, /* domainDetachDevice */
};
/**
NULL, /* domainCreate */
NULL, /* domainDefineXML */
NULL, /* domainUndefine */
+ NULL, /* domainAttachDevice */
+ NULL, /* domainDetachDevice */
};
/* Amount of time it takes to shutdown */
NULL, /* domainCreate */
NULL, /* domainDefineXML */
NULL, /* domainUndefine */
+ NULL, /* domainAttachDevice */
+ NULL, /* domainDetachDevice */
};
#endif /* !PROXY */
static virDomainPtr xenDaemonCreateLinux(virConnectPtr conn,
const char *xmlDesc,
unsigned int flags);
+static int xenDaemonAttachDevice(virDomainPtr domain, char *xml);
+static int xenDaemonDetachDevice(virDomainPtr domain, char *xml);
#endif /* PROXY */
#ifndef PROXY
NULL, /* domainCreate */
NULL, /* domainDefineXML */
NULL, /* domainUndefine */
+ xenDaemonAttachDevice, /* domainAttachDevice */
+ xenDaemonDetachDevice /* domainDetachDevice */
};
/**
free(name);
return (NULL);
}
+
+/**
+ * xenDaemonAttachDevice:
+ * @domain: pointer to domain object
+ * @xml: pointer to XML description of device
+ *
+ * Create a virtual device attachment to backend.
+ * XML description is translated into S-expression.
+ *
+ * Returns 0 in case of success, -1 in case of failure.
+ */
+static int
+xenDaemonAttachDevice(virDomainPtr domain, char *xml)
+{
+ char *sexpr, *conf;
+ int xendConfigVersion, hvm = 0, ret;
+
+ if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) {
+ virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG,
+ __FUNCTION__);
+ return (-1);
+ }
+ if ((xendConfigVersion = xend_get_config_version(domain->conn)) < 0) {
+ virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR,
+ "cannot determine xend config version");
+ return (-1);
+ }
+ if (strcmp(virDomainGetOSType(domain), "linux"))
+ hvm = 1;
+ sexpr = virParseXMLDevice(xml, hvm, xendConfigVersion);
+ if (sexpr == NULL)
+ return (-1);
+ if (!memcmp(sexpr, "(device ", 8)) {
+ conf = sexpr + 8;
+ *(conf + strlen(conf) -1) = 0; /* suppress final ) */
+ }
+ else conf = sexpr;
+ ret = xend_op(domain->conn, domain->name, "op", "device_create",
+ "config", conf, NULL);
+ free(sexpr);
+ return ret;
+}
+
+/**
+ * xenDaemonDetachDevice:
+ * @domain: pointer to domain object
+ * @xml: pointer to XML description of device
+ *
+ * Destroy a virtual device attachment to backend.
+ *
+ * Returns 0 in case of success, -1 in case of failure.
+ */
+static int
+xenDaemonDetachDevice(virDomainPtr domain, char *xml)
+{
+ char class[8], ref[80];
+
+ if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) {
+ virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG,
+ __FUNCTION__);
+ return (-1);
+ }
+ if (virDomainXMLDevID(domain, xml, class, ref))
+ return (-1);
+ return(xend_op(domain->conn, domain->name, "op", "device_destroy",
+ "type", class, "dev", ref, NULL));
+}
#endif /* ! PROXY */
/*
return(dst_uuid);
}
+#ifndef PROXY
+/**
+ * virParseXMLDevice:
+ * @xmldesc: string with the XML description
+ * @hvm: 1 for fully virtualized guest, 0 for paravirtualized
+ * @xendConfigVersion: xend configuration file format
+ *
+ * Parse the XML description and turn it into the xend sexp needed to
+ * create the device. This is a temporary interface as the S-Expr interface
+ * will be replaced by XML-RPC in the future. However the XML format should
+ * stay valid over time.
+ *
+ * Returns the 0-terminated S-Expr string, or NULL in case of error.
+ * the caller must free() the returned value.
+ */
+char *
+virParseXMLDevice(char *xmldesc, int hvm, int xendConfigVersion)
+{
+ xmlDocPtr xml = NULL;
+ xmlNodePtr node;
+ virBuffer buf;
+
+ buf.content = malloc(1000);
+ if (buf.content == NULL)
+ return (NULL);
+ buf.size = 1000;
+ buf.use = 0;
+ xml = xmlReadDoc((const xmlChar *) xmldesc, "domain.xml", NULL,
+ XML_PARSE_NOENT | XML_PARSE_NONET |
+ XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
+ if (xml == NULL)
+ goto error;
+ node = xmlDocGetRootElement(xml);
+ if (node == NULL)
+ goto error;
+ if (xmlStrEqual(node->name, BAD_CAST "disk")) {
+ if (virDomainParseXMLDiskDesc(node, &buf, hvm, xendConfigVersion) != 0)
+ goto error;
+ }
+ else if (xmlStrEqual(node->name, BAD_CAST "interface")) {
+ if (virDomainParseXMLIfDesc(node, &buf, hvm) != 0)
+ goto error;
+ }
+cleanup:
+ if (xml != NULL)
+ xmlFreeDoc(xml);
+ return buf.content;
+error:
+ free(buf.content);
+ buf.content = NULL;
+ goto cleanup;
+}
+
+/**
+ * virDomainXMLDevID:
+ * @domain: pointer to domain object
+ * @xmldesc: string with the XML description
+ * @class: Xen device class "vbd" or "vif" (OUT)
+ * @ref: Xen device reference (OUT)
+ *
+ * Set class according to XML root, and:
+ * - if disk, copy in ref the target name from description
+ * - if network, get MAC address from description, scan XenStore and
+ * copy in ref the corresponding vif number.
+ *
+ * Returns 0 in case of success, -1 in case of failure.
+ */
+int
+virDomainXMLDevID(virDomainPtr domain, char *xmldesc, char *class, char *ref)
+{
+ xmlDocPtr xml = NULL;
+ xmlNodePtr node, cur;
+ xmlChar *attr = NULL;
+ char dir[80], path[128], **list = NULL, *mac = NULL;
+ int ret = 0, num, i, len;
+
+ xml = xmlReadDoc((const xmlChar *) xmldesc, "domain.xml", NULL,
+ XML_PARSE_NOENT | XML_PARSE_NONET |
+ XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
+ if (xml == NULL)
+ goto error;
+ node = xmlDocGetRootElement(xml);
+ if (node == NULL)
+ goto error;
+ if (xmlStrEqual(node->name, BAD_CAST "disk")) {
+ strcpy(class, "vbd");
+ for (cur = node->children; cur != NULL; cur = cur->next) {
+ if ((cur->type != XML_ELEMENT_NODE) ||
+ (!xmlStrEqual(cur->name, BAD_CAST "target"))) continue;
+ attr = xmlGetProp(cur, BAD_CAST "dev");
+ if (attr == NULL)
+ goto error;
+ strcpy(ref, attr);
+ goto cleanup;
+ }
+ }
+ else if (xmlStrEqual(node->name, BAD_CAST "interface")) {
+ strcpy(class, "vif");
+ for (cur = node->children; cur != NULL; cur = cur->next) {
+ if ((cur->type != XML_ELEMENT_NODE) ||
+ (!xmlStrEqual(cur->name, BAD_CAST "mac"))) continue;
+ attr = xmlGetProp(cur, BAD_CAST "address");
+ if (attr == NULL)
+ goto error;
+
+/*
+ * TODO: this part need to be isolated as a high level routine in
+ * xs_internal.[ch]
+ */
+ sprintf(dir, "/local/domain/0/backend/vif/%d", domain->handle);
+ list = xs_directory(domain->conn->xshandle, 0, dir, &num);
+ if (list == NULL)
+ goto error;
+ for (i = 0; i < num; i++) {
+ sprintf(path, "%s/%s/%s", dir, list[i], "mac");
+ mac = xs_read(domain->conn->xshandle, 0, path, &len);
+ if (mac == NULL)
+ goto error;
+ if ((strlen(attr) != len) || memcmp(attr, mac, len)) {
+ free(mac);
+ mac = NULL;
+ continue;
+ }
+ strcpy(ref, list[i]);
+ goto cleanup;
+ }
+/*
+ * end of TODO block
+ */
+ goto error;
+ }
+ }
+error:
+ ret = -1;
+cleanup:
+ if (xml != NULL)
+ xmlFreeDoc(xml);
+ if (attr != NULL)
+ xmlFree(attr);
+ if (list != NULL)
+ free(list);
+ if (mac != NULL)
+ free(mac);
+ return ret;
+}
+#endif /* !PROXY */
+
/*
* Local variables:
* indent-tabs-mode: nil
int virBufferStrcat(virBufferPtr buf, ...);
char *virDomainParseXMLDesc(const char *xmldesc, char **name, int xendConfigVersion);
unsigned char *virParseUUID(char **ptr, const char *uuid);
+char *virParseXMLDevice(char *xmldesc, int hvm, int xendConfigVersion);
+int virDomainXMLDevID(virDomainPtr domain, char *xmldesc, char *class, char *ref);
#ifdef __cplusplus
}
NULL, /* domainCreate */
NULL, /* domainDefineXML */
NULL, /* domainUndefine */
+ NULL, /* domainAttachDevice */
+ NULL, /* domainDetachDevice */
};
/**