# HG changeset patch
# User john.levon(a)sun.com
# Date 1233806617 28800
# Node ID 507c2058c5f3c37894c573c4559eab58c5b52d7d
# Parent 0b9d8336e5e089b19ac8381fdc77eadc72ef9eb7
Allow off-line removal of devices via xend
We must avoid xenstore if the domain isn't running. For disks, this
works in all xend versions; for NICs, however, this requires a change in
netif.py to allow matching by MAC address. Preferring the device ID but
falling back to the device ref works best with all versions of xend.
Signed-off-by: John Levon <john.levon(a)sun.com>
diff --git a/src/xend_internal.c b/src/xend_internal.c
--- a/src/xend_internal.c
+++ b/src/xend_internal.c
@@ -92,11 +92,17 @@ xenDaemonFormatSxprNet(virConnectPtr con
int xendConfigVersion,
int isAttach);
static int
-virDomainXMLDevID(virDomainPtr domain,
- virDomainDeviceDefPtr dev,
- char *class,
- char *ref,
- int ref_len);
+virDomainDevRef(virDomainPtr domain,
+ virDomainDeviceDefPtr dev,
+ char *class,
+ char *ref,
+ int ref_len);
+static int
+virDomainDevID(virDomainPtr domain,
+ int type,
+ const char *ref,
+ char *devid,
+ int devid_len);
#endif
#define virXendError(conn, code, fmt...) \
@@ -3858,7 +3864,7 @@ xenDaemonAttachDevice(virDomainPtr domai
virDomainDeviceDefPtr dev = NULL;
virDomainDefPtr def = NULL;
virBuffer buf = VIR_BUFFER_INITIALIZER;
- char class[8], ref[80];
+ char class[8], ref[80], devid[80];
if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) {
virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG,
@@ -3914,15 +3920,24 @@ xenDaemonAttachDevice(virDomainPtr domai
sexpr = virBufferContentAndReset(&buf);
- if (virDomainXMLDevID(domain, dev, class, ref, sizeof(ref))) {
+ devid[0] = '\0';
+
+ if (virDomainDevRef(domain, dev, class, ref, sizeof(ref)) == -1)
+ goto cleanup;
+
+ if (domain->id > 0 &&
+ virDomainDevID(domain, dev->type, ref, devid, sizeof(devid)) != -1) {
+ VIR_DEBUG("device_configure(ref = %s, devid = %s): %s",
+ ref, devid, sexpr);
+ /* device exists, attempt to modify it */
+ ret = xend_op(domain->conn, domain->name, "op",
"device_configure",
+ "config", sexpr, "dev", devid, NULL);
+ } else {
+ VIR_DEBUG("device_create(ref = %s, devid = %s): %s",
+ ref, devid, sexpr);
/* device doesn't exist, define it */
ret = xend_op(domain->conn, domain->name, "op",
"device_create",
"config", sexpr, NULL);
- }
- else {
- /* device exists, attempt to modify it */
- ret = xend_op(domain->conn, domain->name, "op",
"device_configure",
- "config", sexpr, "dev", ref, NULL);
}
cleanup:
@@ -3945,7 +3960,7 @@ xenDaemonDetachDevice(virDomainPtr domai
xenDaemonDetachDevice(virDomainPtr domain, const char *xml)
{
xenUnifiedPrivatePtr priv;
- char class[8], ref[80];
+ char class[8], ref[80], devid[80];
virDomainDeviceDefPtr dev = NULL;
virDomainDefPtr def = NULL;
int ret = -1;
@@ -3976,11 +3991,41 @@ xenDaemonDetachDevice(virDomainPtr domai
def, xml, VIR_DOMAIN_XML_INACTIVE)))
goto cleanup;
- if (virDomainXMLDevID(domain, dev, class, ref, sizeof(ref)))
+ if (dev->type != VIR_DOMAIN_DEVICE_NET &&
+ dev->type != VIR_DOMAIN_DEVICE_DISK) {
+ virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR,
+ _("can't detach device of type %d"), dev->type);
goto cleanup;
+ }
+
+ /*
+ * First acquire a static reference to the device (MAC address or
+ * destination disk). We'll use this if the domain isn't running,
+ * otherwise we'll rely upon the XenStore devid.
+ */
+
+ if (virDomainDevRef(domain, dev, class, ref, sizeof(ref)) == -1) {
+ virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR,
+ _("can't detach device: not found "));
+ goto cleanup;
+ }
+
+ if (domain->id >= 0) {
+ if (virDomainDevID(domain, dev->type, ref, devid, sizeof(devid))) {
+ virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR,
+ _("can't detach device: not found "));
+ goto cleanup;
+ }
+ } else {
+ strncpy(devid, ref, 80);
+ }
+
+ VIR_DEBUG("device_destroy(type = %s, ref = %s, devid = %s)",
+ class, ref, devid);
ret = xend_op(domain->conn, domain->name, "op",
"device_destroy",
- "type", class, "dev", ref, "force",
"0", "rm_cfg", "1", NULL);
+ "type", class, "dev", devid, "force",
"0",
+ "rm_cfg", "1", NULL);
cleanup:
virDomainDefFree(def);
@@ -5502,12 +5547,48 @@ error:
/**
- * virDomainXMLDevID:
+ * virDomainXMLDevRef:
* @domain: pointer to domain object
* @dev: pointer to device config object
* @class: Xen device class "vbd" or "vif" (OUT)
* @ref: Xen device reference (OUT)
*
+ * Return the class (vbd/vif) and reference (MAC address / disk
+ * destination) of the given device.
+ *
+ * Returns 0 in case of success, -1 in case of failure.
+ */
+static int
+virDomainDevRef(virDomainPtr domain,
+ virDomainDeviceDefPtr dev,
+ char *class,
+ char *ref,
+ int ref_len)
+{
+ if (dev->type == VIR_DOMAIN_DEVICE_DISK) {
+ if (dev->data.disk->dst == NULL)
+ return -1;
+ strncpy(ref, dev->data.disk->dst, ref_len);
+ strcpy(class, "vbd");
+ } else if (dev->type == VIR_DOMAIN_DEVICE_NET) {
+ virFormatMacAddr(dev->data.net->mac, ref);
+ strcpy(class, "vif");
+ } else {
+ virXendError(NULL, VIR_ERR_NO_SUPPORT,
+ _("no support for device type %d"), dev->type);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * virDomainXMLDevID:
+ * @domain: pointer to domain object
+ * @class: either 'vbd' or 'vif'
+ * @ref: Xen device reference
+ * @devid: Xen device ID (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
@@ -5516,54 +5597,41 @@ error:
* Returns 0 in case of success, -1 in case of failure.
*/
static int
-virDomainXMLDevID(virDomainPtr domain,
- virDomainDeviceDefPtr dev,
- char *class,
- char *ref,
- int ref_len)
+virDomainDevID(virDomainPtr domain,
+ int type,
+ const char *ref,
+ char *devid,
+ int devid_len)
{
xenUnifiedPrivatePtr priv = domain->conn->privateData;
- char *xref;
-
- if (dev->type == VIR_DOMAIN_DEVICE_DISK) {
- strcpy(class, "vbd");
- if (dev->data.disk->dst == NULL)
- return -1;
- xenUnifiedLock(priv);
- xref = xenStoreDomainGetDiskID(domain->conn, domain->id,
- dev->data.disk->dst);
- xenUnifiedUnlock(priv);
- if (xref == NULL)
- return -1;
-
- strncpy(ref, xref, ref_len);
- free(xref);
- ref[ref_len - 1] = '\0';
- } else if (dev->type == VIR_DOMAIN_DEVICE_NET) {
- char mac[30];
- virDomainNetDefPtr def =
dev->data.net;
- snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x",
- def->mac[0], def->mac[1], def->mac[2],
- def->mac[3], def->mac[4], def->mac[5]);
-
- strcpy(class, "vif");
-
- xenUnifiedLock(priv);
- xref = xenStoreDomainGetNetworkID(domain->conn, domain->id,
- mac);
- xenUnifiedUnlock(priv);
- if (xref == NULL)
- return -1;
-
- strncpy(ref, xref, ref_len);
- free(xref);
- ref[ref_len - 1] = '\0';
+ char *id = NULL;
+
+ if (domain->id < 0) {
+ virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR,
+ _("devid not available for shutdown domain"));
+ return -1;
+ }
+
+ xenUnifiedLock(priv);
+
+ if (type == VIR_DOMAIN_DEVICE_DISK) {
+ id = xenStoreDomainGetDiskID(domain->conn, domain->id, ref);
+
+ } else if (type == VIR_DOMAIN_DEVICE_NET) {
+ id = xenStoreDomainGetNetworkID(domain->conn, domain->id, ref);
} else {
virXendError(NULL, VIR_ERR_NO_SUPPORT,
- "%s", _("hotplug of device type not
supported"));
+ _("no support for device type %d"), type);
+ }
+
+ xenUnifiedUnlock(priv);
+
+ if (id == NULL)
return -1;
- }
-
+
+ strncpy(devid, id, devid_len);
+ VIR_FREE(id);
+ devid[devid_len - 1] = '\0';
return 0;
}