add/change/delete a Disk/NIC of an inactive domains

* src/xm_internal.[ch]: applied patch from Shigeki Sakamoto to
  add/change/delete a Disk/NIC of an inactive domains
Daniel
This commit is contained in:
Daniel Veillard 2008-02-06 17:57:10 +00:00
parent eecd68364a
commit 594af3eb22
4 changed files with 660 additions and 16 deletions

View File

@ -1,3 +1,8 @@
Wed Feb 6 18:55:37 CET 2008 Daniel Veillard <veillard@redhat.com>
* src/xm_internal.[ch]: applied patch from Shigeki Sakamoto to
add/change/delete a Disk/NIC of an inactive domains
Wed Feb 6 17:22:34 CET 2008 Daniel Veillard <veillard@redhat.com> Wed Feb 6 17:22:34 CET 2008 Daniel Veillard <veillard@redhat.com>
* src/qemu_conf.c: applied 2 patches from Guido Guenther to avoid * src/qemu_conf.c: applied 2 patches from Guido Guenther to avoid

View File

@ -72,6 +72,12 @@ static virHashTablePtr nameConfigMap = NULL;
static int nconnections = 0; static int nconnections = 0;
static time_t lastRefresh = 0; static time_t lastRefresh = 0;
char * xenXMAutoAssignMac(void);
static int xenXMAttachDisk(virDomainPtr domain, xmlXPathContextPtr ctxt, int hvm,
xmlNodePtr node, xenXMConfCachePtr entry);
static int xenXMAttachInterface(virDomainPtr domain, xmlXPathContextPtr ctxt, int hvm,
xmlNodePtr node, xenXMConfCachePtr entry);
#define XM_REFRESH_INTERVAL 10 #define XM_REFRESH_INTERVAL 10
#define XM_CONFIG_DIR "/etc/xen" #define XM_CONFIG_DIR "/etc/xen"
@ -79,6 +85,7 @@ static time_t lastRefresh = 0;
#define XEND_CONFIG_FILE "xend-config.sxp" #define XEND_CONFIG_FILE "xend-config.sxp"
#define XEND_PCI_CONFIG_PREFIX "xend-pci-" #define XEND_PCI_CONFIG_PREFIX "xend-pci-"
#define QEMU_IF_SCRIPT "qemu-ifup" #define QEMU_IF_SCRIPT "qemu-ifup"
#define XM_XML_ERROR "Invalid xml"
struct xenUnifiedDriver xenXMDriver = { struct xenUnifiedDriver xenXMDriver = {
xenXMOpen, /* open */ xenXMOpen, /* open */
@ -113,8 +120,8 @@ struct xenUnifiedDriver xenXMDriver = {
xenXMDomainCreate, /* domainCreate */ xenXMDomainCreate, /* domainCreate */
xenXMDomainDefineXML, /* domainDefineXML */ xenXMDomainDefineXML, /* domainDefineXML */
xenXMDomainUndefine, /* domainUndefine */ xenXMDomainUndefine, /* domainUndefine */
NULL, /* domainAttachDevice */ xenXMDomainAttachDevice, /* domainAttachDevice */
NULL, /* domainDetachDevice */ xenXMDomainDetachDevice, /* domainDetachDevice */
NULL, /* domainGetAutostart */ NULL, /* domainGetAutostart */
NULL, /* domainSetAutostart */ NULL, /* domainSetAutostart */
NULL, /* domainGetSchedulerType */ NULL, /* domainGetSchedulerType */
@ -2515,6 +2522,635 @@ int xenXMNumOfDefinedDomains(virConnectPtr conn) {
return virHashSize(nameConfigMap); return virHashSize(nameConfigMap);
} }
/**
* xenXMDomainAttachDevice:
* @domain: pointer to domain object
* @xml: pointer to XML description of device
*
* Create a virtual device attachment to backend.
* XML description is translated into config file.
*
* Returns 0 in case of success, -1 in case of failure.
*/
static int
xenXMDomainAttachDevice(virDomainPtr domain, const char *xml) {
const char *filename = NULL;
xenXMConfCachePtr entry = NULL;
xmlDocPtr doc = NULL;
xmlNodePtr node = NULL;
xmlXPathContextPtr ctxt = NULL;
xmlXPathObjectPtr obj = NULL;
char *domxml = NULL;
int ret = -1, hvm = 0;
if ((!domain) || (!domain->conn) || (!domain->name) || (!xml)) {
xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG,
__FUNCTION__);
goto cleanup;
}
if (domain->conn->flags & VIR_CONNECT_RO)
goto cleanup;
if (domain->id != -1)
goto cleanup;
if (!(filename = virHashLookup(nameConfigMap, domain->name)))
goto cleanup;
if (!(entry = virHashLookup(configCache, filename)))
goto cleanup;
if (!(entry->conf))
goto cleanup;
if (!(domxml = xenXMDomainDumpXML(domain, 0)))
goto cleanup;
doc = xmlReadDoc((const xmlChar *) domxml, "domain.xml", NULL,
XML_PARSE_NOENT | XML_PARSE_NONET |
XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
if (!doc) {
xenXMError(domain->conn, VIR_ERR_XML_ERROR, "cannot read XML domain definition");
goto cleanup;
}
if (!(ctxt = xmlXPathNewContext(doc))) {
xenXMError(domain->conn, VIR_ERR_INTERNAL_ERROR, "cannot create XPath context");
goto cleanup;
}
obj = xmlXPathEval(BAD_CAST "string(/domain/os/type)", ctxt);
if ((obj != NULL) && (obj->type == XPATH_STRING) &&
(obj->stringval) && (STREQ((char *)obj->stringval, "hvm")))
hvm = 1;
if (ctxt)
xmlXPathFreeContext(ctxt);
ctxt = NULL;
if (doc)
xmlFreeDoc(doc);
doc = xmlReadDoc((const xmlChar *) xml, "device.xml", NULL,
XML_PARSE_NOENT | XML_PARSE_NONET |
XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
if (!doc) {
xenXMError(domain->conn, VIR_ERR_XML_ERROR,
"cannot read XML domain definition");
goto cleanup;
}
if (!(ctxt = xmlXPathNewContext(doc))) {
xenXMError(domain->conn, VIR_ERR_INTERNAL_ERROR,
"cannot create XPath context");
goto cleanup;
}
if ((node = virXPathNode("/disk", ctxt))) {
if (xenXMAttachDisk(domain, ctxt, hvm, node, entry))
goto cleanup;
} else if ((node = virXPathNode("/interface", ctxt))) {
if (xenXMAttachInterface(domain, ctxt, hvm, node, entry))
goto cleanup;
} else {
xenXMError(domain->conn, VIR_ERR_XML_ERROR, "unknown device");
goto cleanup;
}
/* 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)
goto cleanup;
ret = 0;
cleanup:
if (domxml)
free(domxml);
if (obj)
xmlXPathFreeObject(obj);
if (ctxt)
xmlXPathFreeContext(ctxt);
if (doc)
xmlFreeDoc(doc);
return ret;
}
static int
xenXMAttachDisk(virDomainPtr domain, xmlXPathContextPtr ctxt, int hvm,
xmlNodePtr node, xenXMConfCachePtr entry) {
virConfValuePtr list_item = NULL, list_val = NULL, prev = NULL;
xenUnifiedPrivatePtr priv = NULL;
xmlChar *type = NULL, *source = NULL, *target = NULL;
int ret = -1;
char *dev;
priv = (xenUnifiedPrivatePtr) domain->conn->privateData;
xenXMParseXMLDisk(node, hvm, ((xenUnifiedPrivatePtr) domain->conn->privateData)->xendConfigVersion, &dev);
if (!dev)
goto cleanup;
if (!(type = xmlGetProp(node, BAD_CAST "type"))) {
xenXMError(domain->conn, VIR_ERR_XML_ERROR, XM_XML_ERROR);
goto cleanup;
}
if (!(node = virXPathNode("/disk/source", ctxt))) {
xenXMError(domain->conn, VIR_ERR_XML_ERROR, XM_XML_ERROR);
goto cleanup;
}
if (!strcmp((const char *) type, "block"))
source = xmlGetProp(node, BAD_CAST "dev");
else if (!strcmp((const char *) type, "file"))
source = xmlGetProp(node, BAD_CAST "file");
else {
xenXMError(domain->conn, VIR_ERR_XML_ERROR, XM_XML_ERROR);
goto cleanup;
}
if (!(node = virXPathNode("/disk/target", ctxt))) {
xenXMError(domain->conn, VIR_ERR_XML_ERROR, XM_XML_ERROR);
goto cleanup;
}
target = xmlGetProp(node, BAD_CAST "dev");
list_item = virConfGetValue(entry->conf, "disk");
if (list_item && list_item->type == VIR_CONF_LIST) {
prev = list_item;
list_val = list_item->list;
while (list_val) {
if ((list_val->type != VIR_CONF_STRING) || (!list_val->str))
goto skip;
char domdev[NAME_MAX];
char *head;
char *offset;
char *tmp;
head = list_val->str;
/* Extract the source */
if (!(offset = strchr(head, ',')) || offset[0] == '\0')
goto skip;
if ((offset - head) >= (PATH_MAX-1))
goto skip;
head = offset + 1;
/* Extract the dest */
if (!(offset = strchr(head, ',')) || offset[0] == '\0')
goto skip;
if ((offset - head) >= (PATH_MAX-1))
goto skip;
strncpy(domdev, head, (offset - head));
domdev[(offset-head)] = '\0';
head = offset + 1;
/* Remove legacy ioemu: junk */
if (!strncmp(domdev, "ioemu:", 6)) {
memmove(domdev, domdev+6, strlen(domdev)-5);
}
/* Check for a :cdrom/:disk postfix */
if ((tmp = strchr(domdev, ':')))
tmp[0] = '\0';
if (!(strcmp(domdev, (const char *) target)))
break;
skip:
prev = list_val;
list_val = list_val->next;
}
} else if (!list_item) {
if (!(list_item = calloc(1, sizeof(virConfValue))))
goto cleanup;
list_item->type = VIR_CONF_LIST;
if(virConfSetValue(entry->conf, "disk", list_item)) {
free(list_item);
goto cleanup;
}
list_val = NULL;
prev = list_item;
} else
goto cleanup;
if (!list_val) {
/* insert */
if (!(list_val = malloc(sizeof(virConfValue))))
goto cleanup;
list_val->type = VIR_CONF_STRING;
list_val->next = NULL;
list_val->str = dev;
if (prev->type == VIR_CONF_LIST)
prev->list = list_val;
else
prev->next = list_val;
} else {
/* configure */
if (list_val->str)
free(list_val->str);
list_val->str = dev;
}
ret = 0;
goto cleanup;
cleanup:
if (type)
free(type);
if (source)
free(source);
if (target)
free(target);
return (ret);
}
static int
xenXMAttachInterface(virDomainPtr domain, xmlXPathContextPtr ctxt, int hvm,
xmlNodePtr node, xenXMConfCachePtr entry) {
virConfValuePtr list_item = NULL, list_val = NULL, prev = NULL;
xmlChar *type = NULL, *source = NULL, *mac = NULL;
int ret = -1, autoassign = 0;
char *dev;
xmlNodePtr node_cur = NULL, node_tmp = NULL;
xmlAttrPtr attr_node = NULL;
xmlNodePtr text_node = NULL;
if(!(type = xmlGetProp(node, BAD_CAST "type"))) {
xenXMError(domain->conn, VIR_ERR_XML_ERROR, XM_XML_ERROR);
goto cleanup;
}
if (!(node = virXPathNode("/interface/source", ctxt))) {
xenXMError(domain->conn, VIR_ERR_XML_ERROR, XM_XML_ERROR);
goto cleanup;
}
source = xmlGetProp(node, BAD_CAST type);
if (!(node = virXPathNode("/interface/mac", ctxt))) {
if (!(mac = (xmlChar *)xenXMAutoAssignMac()))
goto cleanup;
autoassign = 1;
} else
mac = xmlGetProp(node, BAD_CAST "address");
list_item = virConfGetValue(entry->conf, "vif");
if (list_item && list_item->type == VIR_CONF_LIST) {
prev = list_item;
list_val = list_item->list;
while (list_val) {
if ((list_val->type != VIR_CONF_STRING) || (!list_val->str))
goto skip;
char dommac[18];
char *key;
dommac[0] = '\0';
key = list_val->str;
while (key) {
char *data;
char *nextkey = strchr(key, ',');
if (!(data = strchr(key, '=')) || (data[0] == '\0'))
goto skip;
data++;
if (!strncmp(key, "mac=", 4)) {
int len = nextkey ? (nextkey - data) : 17;
if (len > 17)
len = 17;
strncpy(dommac, data, len);
dommac[len] = '\0';
}
while (nextkey && (nextkey[0] == ',' ||
nextkey[0] == ' ' ||
nextkey[0] == '\t'))
nextkey++;
key = nextkey;
}
if (!(strcmp(dommac, (const char *) mac))) {
if (autoassign) {
if (mac)
free(mac);
mac = NULL;
if (!(mac = (xmlChar *)xenXMAutoAssignMac()))
goto cleanup;
/* initialize the list */
list_item = virConfGetValue(entry->conf, "vif");
prev = list_item;
list_val = list_item->list;
continue;
} else
break;
}
skip:
prev = list_val;
list_val = list_val->next;
}
} else if (!list_item) {
if (!(list_item = calloc(1, sizeof(virConfValue))))
goto cleanup;
list_item->type = VIR_CONF_LIST;
if(virConfSetValue(entry->conf, "vif", list_item)) {
free(list_item);
goto cleanup;
}
list_val = NULL;
prev = list_item;
} else
goto cleanup;
if ((node = virXPathNode("/interface", ctxt))) {
if (autoassign) {
node_cur = node->children;
while (node_cur->next)
node_cur = node_cur->next;
if (!(node_tmp = calloc(1, sizeof(xmlNode))))
goto node_cleanup;
node_tmp->type = XML_ELEMENT_NODE;
if (!(node_tmp->name = malloc(4)))
goto node_cleanup;
strcpy((char *)node_tmp->name, "mac");
node_tmp->children = NULL;
if (!(attr_node = calloc(1, sizeof(xmlAttr))))
goto node_cleanup;
attr_node->type = XML_ATTRIBUTE_NODE;
attr_node->ns = NULL;
if (!(attr_node->name = malloc(8)))
goto node_cleanup;
strcpy((char *) attr_node->name, "address");
node_tmp->properties = attr_node;
if (!(text_node = calloc(1, sizeof(xmlNode))))
goto node_cleanup;
text_node->type = XML_TEXT_NODE;
text_node->_private = NULL;
if (!(text_node->name = malloc(8)))
goto node_cleanup;
strcpy((char *) text_node->name, "text");
text_node->children = NULL;
text_node->parent = (xmlNodePtr)attr_node;
text_node->content = mac;
mac = NULL;
attr_node->children = text_node;
attr_node->last = text_node;
attr_node->parent = node_tmp;
node_cur->next = node_tmp;
}
if (!(dev = xenXMParseXMLVif(domain->conn, node, hvm)))
goto cleanup;
} else
goto cleanup;
if (!list_val) {
/* insert */
if (!(list_val = malloc(sizeof(virConfValue))))
goto cleanup;
list_val->type = VIR_CONF_STRING;
list_val->next = NULL;
list_val->str = dev;
if (prev->type == VIR_CONF_LIST)
prev->list = list_val;
else
prev->next = list_val;
} else {
/* configure */
if (list_val->str)
free(list_val->str);
list_val->str = dev;
}
ret = 0;
goto cleanup;
node_cleanup:
if (node_tmp)
xmlFree(node_tmp);
if (attr_node)
xmlFree(attr_node);
if (text_node)
xmlFree(text_node);
cleanup:
if (type)
free(type);
if (source)
free(source);
if (mac)
free(mac);
return (ret);
}
/**
* xenXMAutoAssignMac:
* @mac: pointer to Mac String
*
* a mac is assigned automatically.
*
* Returns 0 in case of success, -1 in case of failure.
*/
char *
xenXMAutoAssignMac() {
char *buf;
if (!(buf = malloc(18)))
return 0;
srand((unsigned)time(NULL));
sprintf(buf, "00:16:3e:%02x:%02x:%02x"
,1 + (int)(256*(rand()/(RAND_MAX+1.0)))
,1 + (int)(256*(rand()/(RAND_MAX+1.0)))
,1 + (int)(256*(rand()/(RAND_MAX+1.0))));
return buf;
}
/**
* xenXMDomainDetachDevice:
* @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
xenXMDomainDetachDevice(virDomainPtr domain, const char *xml) {
const char *filename = NULL;
char device[8], *domdevice = NULL;
xenXMConfCachePtr entry = NULL;
virConfValuePtr prev = NULL, list_ptr = NULL, list_val = NULL;
xmlDocPtr doc = NULL;
xmlNodePtr node = NULL;
xmlXPathContextPtr ctxt = NULL;
xmlChar *key = NULL;
int ret = -1;
if ((!domain) || (!domain->conn) || (!domain->name) || (!xml)) {
xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG,
__FUNCTION__);
goto cleanup;
}
if (domain->conn->flags & VIR_CONNECT_RO)
goto cleanup;
if (domain->id != -1)
goto cleanup;
if (!(filename = virHashLookup(nameConfigMap, domain->name)))
goto cleanup;
doc = xmlReadDoc((const xmlChar *) xml, "device.xml", NULL,
XML_PARSE_NOENT | XML_PARSE_NONET |
XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
if (!doc) {
xenXMError(domain->conn, VIR_ERR_XML_ERROR, "cannot read XML domain definition");
goto cleanup;
}
if (!(ctxt = xmlXPathNewContext(doc))) {
xenXMError(domain->conn, VIR_ERR_INTERNAL_ERROR, "cannot create XPath context");
goto cleanup;
}
if ((node = virXPathNode("/disk", ctxt))) {
strcpy(device, "disk");
if (!(node = virXPathNode("/disk/target", ctxt)))
goto cleanup;
key = xmlGetProp(node, BAD_CAST "dev");
} else if ((node = virXPathNode("/interface", ctxt))) {
strcpy(device, "vif");
if (!(node = virXPathNode("/interface/mac", ctxt)))
goto cleanup;
key = xmlGetProp(node, BAD_CAST "address");
} else
goto cleanup;
if (!key || (strlen((char *)key) == 0))
goto cleanup;
if (!(entry = virHashLookup(configCache, filename)))
goto cleanup;
if (!entry->conf)
goto cleanup;
list_ptr = virConfGetValue(entry->conf, device);
if (!list_ptr)
goto cleanup;
else if (list_ptr && list_ptr->type == VIR_CONF_LIST) {
list_val = list_ptr->list;
while (list_val) {
if (!(strcmp(device, "disk"))) {
char domdev[NAME_MAX];
char *head;
char *offset;
char *tmp;
if ((list_val->type != VIR_CONF_STRING) || (!list_val->str))
goto skip;
head = list_val->str;
/* Extract the source */
if (!(offset = strchr(head, ',')) || offset[0] == '\0')
goto skip;
if ((offset - head) >= (PATH_MAX-1))
goto skip;
head = offset + 1;
/* Extract the dest */
if (!(offset = strchr(head, ',')) || offset[0] == '\0')
goto skip;
if ((offset - head) >= (PATH_MAX-1))
goto skip;
strncpy(domdev, head, (offset - head));
domdev[(offset-head)] = '\0';
head = offset + 1;
/* Remove legacy ioemu: junk */
if (!strncmp(domdev, "ioemu:", 6)) {
memmove(domdev, domdev+6, strlen(domdev)-5);
}
/* Check for a :cdrom/:disk postfix */
if ((tmp = strchr(domdev, ':')))
tmp[0] = '\0';
if (!(strcmp(domdev, (const char *) key)))
break;
} else {
char dommac[18];
char *mac;
dommac[0] = '\0';
if ((list_val->type != VIR_CONF_STRING) || (!list_val->str))
goto skip;
mac = list_val->str;
while (mac) {
char *data;
char *nextmac = strchr(mac, ',');
if (!(data = strchr(mac, '=')) || (data[0] == '\0'))
goto skip;
data++;
if (!strncmp(mac, "mac=", 4)) {
int len = nextmac ? (nextmac - data) : 17;
if (len > 17)
len = 17;
strncpy(dommac, data, len);
dommac[len] = '\0';
}
while (nextmac && (nextmac[0] == ',' ||
nextmac[0] == ' ' ||
nextmac[0] == '\t'))
nextmac++;
mac = nextmac;
}
if (!(strcmp(dommac, (const char *) key)))
break;
}
skip:
prev = list_val;
list_val = list_val->next;
}
}
if (!list_val)
goto cleanup;
else {
if (!prev) {
virConfValuePtr value;
if (!(value = calloc(1, sizeof(virConfValue))))
goto cleanup;
value->type = VIR_CONF_LIST;
value->list = list_val->next;
list_val->next = NULL;
if (virConfSetValue(entry->conf, device, value)) {
free(value);
goto cleanup;
}
} else
prev->next = list_val->next;
}
/* 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)
goto cleanup;
ret = 0;
cleanup:
if (ctxt)
xmlXPathFreeContext(ctxt);
if (doc)
xmlFreeDoc(doc);
if (domdevice)
free(domdevice);
if (key)
free(key);
if (list_val)
free(list_val);
return (ret);
}
#endif /* WITH_XEN */ #endif /* WITH_XEN */
/* /*
* Local variables: * Local variables:

View File

@ -61,6 +61,9 @@ int xenXMDomainUndefine(virDomainPtr domain);
virConfPtr xenXMParseXMLToConfig(virConnectPtr conn, const char *xml); virConfPtr xenXMParseXMLToConfig(virConnectPtr conn, const char *xml);
char *xenXMDomainFormatXML(virConnectPtr conn, virConfPtr conf); char *xenXMDomainFormatXML(virConnectPtr conn, virConfPtr conf);
static int xenXMDomainAttachDevice(virDomainPtr domain, const char *xml);
static int xenXMDomainDetachDevice(virDomainPtr domain, const char *xml);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif