From dc28f35f110f297f656bac417334a5540b0b529c Mon Sep 17 00:00:00 2001 From: Daniel Veillard Date: Thu, 16 Nov 2006 18:11:28 +0000 Subject: [PATCH] * 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. Daniel --- ChangeLog | 11 +++ include/libvirt/libvirt.h | 2 + include/libvirt/libvirt.h.in | 2 + src/driver.h | 8 ++ src/libvirt.c | 82 +++++++++++++++++++ src/libvirt_sym.version | 3 + src/proxy_internal.c | 2 + src/test.c | 2 + src/xen_internal.c | 2 + src/xend_internal.c | 71 +++++++++++++++++ src/xml.c | 147 +++++++++++++++++++++++++++++++++++ src/xml.h | 2 + src/xs_internal.c | 2 + 13 files changed, 336 insertions(+) diff --git a/ChangeLog b/ChangeLog index 362f3765bb..7389c25252 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +Thu Nov 16 18:18:12 CET 2006 Daniel Veillard + + * 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 * python/generator.py, python/libvir.c, python/libvirt-python-api.xml: diff --git a/include/libvirt/libvirt.h b/include/libvirt/libvirt.h index 133091f5e9..dabe35ceb1 100644 --- a/include/libvirt/libvirt.h +++ b/include/libvirt/libvirt.h @@ -415,6 +415,8 @@ int virDomainGetVcpus (virDomainPtr domain, */ #define VIR_GET_CPUMAP(cpumaps,maplen,vcpu) &(cpumaps[(vcpu)*(maplen)]) +int virDomainAttachDevice(virDomainPtr domain, char *xml); +int virDomainDetachDevice(virDomainPtr domain, char *xml); #ifdef __cplusplus } diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 9e9a8b57d5..92f1feb906 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -415,6 +415,8 @@ int virDomainGetVcpus (virDomainPtr domain, */ #define VIR_GET_CPUMAP(cpumaps,maplen,vcpu) &(cpumaps[(vcpu)*(maplen)]) +int virDomainAttachDevice(virDomainPtr domain, char *xml); +int virDomainDetachDevice(virDomainPtr domain, char *xml); #ifdef __cplusplus } diff --git a/src/driver.h b/src/driver.h index 627121f03e..e2ae5c6fab 100644 --- a/src/driver.h +++ b/src/driver.h @@ -133,6 +133,12 @@ typedef int 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; @@ -184,6 +190,8 @@ struct _virDriver { virDrvDomainCreate domainCreate; virDrvDomainDefineXML domainDefineXML; virDrvDomainUndefine domainUndefine; + virDrvDomainAttachDevice domainAttachDevice; + virDrvDomainDetachDevice domainDetachDevice; }; diff --git a/src/libvirt.c b/src/libvirt.c index 12e3116c8c..85410c920c 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -1899,3 +1899,85 @@ virDomainGetVcpus(virDomainPtr domain, virVcpuInfoPtr info, int maxinfo, 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); +} diff --git a/src/libvirt_sym.version b/src/libvirt_sym.version index 13c743905d..5a40207f8e 100644 --- a/src/libvirt_sym.version +++ b/src/libvirt_sym.version @@ -53,5 +53,8 @@ virDomainSetVcpus; virDomainPinVcpu; virDomainGetVcpus; + + virDomainAttachDevice; + virDomainDetachDevice; local: *; }; diff --git a/src/proxy_internal.c b/src/proxy_internal.c index b945e00234..ce8d96b604 100644 --- a/src/proxy_internal.c +++ b/src/proxy_internal.c @@ -83,6 +83,8 @@ static virDriver xenProxyDriver = { NULL, /* domainCreate */ NULL, /* domainDefineXML */ NULL, /* domainUndefine */ + NULL, /* domainAttachDevice */ + NULL, /* domainDetachDevice */ }; /** diff --git a/src/test.c b/src/test.c index f3d8191d87..51ba18a572 100644 --- a/src/test.c +++ b/src/test.c @@ -63,6 +63,8 @@ static virDriver testDriver = { NULL, /* domainCreate */ NULL, /* domainDefineXML */ NULL, /* domainUndefine */ + NULL, /* domainAttachDevice */ + NULL, /* domainDetachDevice */ }; /* Amount of time it takes to shutdown */ diff --git a/src/xen_internal.c b/src/xen_internal.c index 908f695d43..abbf75c314 100644 --- a/src/xen_internal.c +++ b/src/xen_internal.c @@ -458,6 +458,8 @@ static virDriver xenHypervisorDriver = { NULL, /* domainCreate */ NULL, /* domainDefineXML */ NULL, /* domainUndefine */ + NULL, /* domainAttachDevice */ + NULL, /* domainDetachDevice */ }; #endif /* !PROXY */ diff --git a/src/xend_internal.c b/src/xend_internal.c index d58ac0dd4a..9e73faaed6 100644 --- a/src/xend_internal.c +++ b/src/xend_internal.c @@ -47,6 +47,8 @@ static virDomainPtr xenDaemonLookupByUUID(virConnectPtr conn, 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 @@ -93,6 +95,8 @@ static virDriver xenDaemonDriver = { NULL, /* domainCreate */ NULL, /* domainDefineXML */ NULL, /* domainUndefine */ + xenDaemonAttachDevice, /* domainAttachDevice */ + xenDaemonDetachDevice /* domainDetachDevice */ }; /** @@ -2957,6 +2961,73 @@ xenDaemonCreateLinux(virConnectPtr conn, const char *xmlDesc, 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 */ /* diff --git a/src/xml.c b/src/xml.c index 2a7725115b..59b0c297ff 100644 --- a/src/xml.c +++ b/src/xml.c @@ -1488,6 +1488,153 @@ error: 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 diff --git a/src/xml.h b/src/xml.h index 5e91abec1b..9cae3c368f 100644 --- a/src/xml.h +++ b/src/xml.h @@ -31,6 +31,8 @@ int virBufferVSprintf(virBufferPtr buf, const char *format, ...); 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 } diff --git a/src/xs_internal.c b/src/xs_internal.c index 8630426e3b..6ad145e489 100644 --- a/src/xs_internal.c +++ b/src/xs_internal.c @@ -77,6 +77,8 @@ static virDriver xenStoreDriver = { NULL, /* domainCreate */ NULL, /* domainDefineXML */ NULL, /* domainUndefine */ + NULL, /* domainAttachDevice */ + NULL, /* domainDetachDevice */ }; /**