--- libvirt.h.in 2006-08-30 15:50:25.000000000 +0200 +++ include/libvirt/libvirt.h.in 2006-11-07 16:09:44.000000000 +0100 @@ -415,6 +415,8 @@ */ #define VIR_GET_CPUMAP(cpumaps,maplen,vcpu) &(cpumaps[(vcpu)*(maplen)]) +int virDomainAttachDevice(virDomainPtr domain, char *xml); +int virDomainDetachDevice(virDomainPtr domain, char *xml); #ifdef __cplusplus } --- libvirt.c 2006-09-21 16:30:46.000000000 +0200 +++ src/libvirt.c 2006-11-07 16:11:16.000000000 +0100 @@ -1897,3 +1897,49 @@ 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) +{ + 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); + } + return xenDaemonAttachDevice(domain, xml); +} + +/** + * 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) +{ + 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); + } + return xenDaemonDetachDevice(domain, xml); +} --- libvirt_sym.version 2006-08-30 15:50:25.000000000 +0200 +++ src/libvirt_sym.version 2006-11-03 14:59:25.000000000 +0100 @@ -53,5 +53,8 @@ virDomainSetVcpus; virDomainPinVcpu; virDomainGetVcpus; + + virDomainAttachDevice; + virDomainDetachDevice; local: *; }; --- xend_internal.c 2006-10-09 17:23:10.000000000 +0200 +++ src/xend_internal.c 2006-11-06 18:12:24.000000000 +0100 @@ -2910,6 +2910,73 @@ 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. + */ +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. + */ +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 */ /* --- xend_internal.h 2006-09-19 16:57:42.000000000 +0200 +++ src/xend_internal.h 2006-11-03 14:59:25.000000000 +0100 @@ -647,6 +647,8 @@ int maxinfo, unsigned char *cpumaps, int maplen); +int xenDaemonAttachDevice(virDomainPtr domain, char *xml); +int xenDaemonDetachDevice(virDomainPtr domain, char *xml); #ifdef __cplusplus } --- xml.c 2006-10-09 17:23:10.000000000 +0200 +++ src/xml.c 2006-11-03 14:59:25.000000000 +0100 @@ -1428,6 +1428,145 @@ 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; + 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; + } + 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: --- xml.h 2006-09-19 16:57:42.000000000 +0200 +++ src/xml.h 2006-11-03 14:59:25.000000000 +0100 @@ -31,6 +31,8 @@ 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 }