On 2012/12/01 04:26, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange(a)redhat.com>
Wire up the attach/detach device drivers in LXC to support the
hotplug/unplug of NICs.
Signed-off-by: Daniel P. Berrange <berrange(a)redhat.com>
---
src/lxc/lxc_driver.c | 219 +++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 214 insertions(+), 5 deletions(-)
diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c
index a2bb497..08ac70d 100644
--- a/src/lxc/lxc_driver.c
+++ b/src/lxc/lxc_driver.c
@@ -3073,9 +3073,141 @@ cleanup:
}
+/* XXX conn required for network -> bridge resolution */
static int
-lxcDomainAttachDeviceLive(virLXCDriverPtr driver ATTRIBUTE_UNUSED,
- virDomainObjPtr vm ATTRIBUTE_UNUSED,
+lxcDomainAttachDeviceNetLive(virConnectPtr conn,
+ virDomainObjPtr vm,
+ virDomainNetDefPtr net)
+{
+ virLXCDomainObjPrivatePtr priv = vm->privateData;
+ int ret = -1;
+ int actualType;
+ char *veth = NULL;
+
+ if (!priv->initpid) {
+ virReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("Cannot attach disk until init PID is known"));
+ goto cleanup;
+ }
+
+ /* preallocate new slot for device */
+ if (VIR_REALLOC_N(vm->def->nets, vm->def->nnets+1) < 0) {
+ virReportOOMError();
+ return -1;
+ }
+
+ /* If appropriate, grab a physical device from the configured
+ * network's pool of devices, or resolve bridge device name
+ * to the one defined in the network definition.
+ */
+ if (networkAllocateActualDevice(net) < 0)
+ return -1;
+
+ actualType = virDomainNetGetActualType(net);
+
+ switch (actualType) {
+ case VIR_DOMAIN_NET_TYPE_BRIDGE: {
+ const char *brname = virDomainNetGetActualBridgeName(net);
+ if (!brname) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("No bridge name specified"));
+ goto cleanup;
+ }
+ if (!(veth = virLXCProcessSetupInterfaceBridged(conn,
+ vm->def,
+ net,
+ brname)))
+ goto cleanup;
+ } break;
+ case VIR_DOMAIN_NET_TYPE_NETWORK: {
+ virNetworkPtr network;
+ char *brname = NULL;
+ bool fail = false;
+ int active;
+ virErrorPtr errobj;
+
+ if (!(network = virNetworkLookupByName(conn,
+ net->data.network.name)))
+ goto cleanup;
+
+ active = virNetworkIsActive(network);
+ if (active != 1) {
+ fail = true;
+ if (active == 0)
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Network '%s' is not active."),
+ net->data.network.name);
+ }
+
+ if (!fail) {
+ brname = virNetworkGetBridgeName(network);
+ if (brname == NULL)
+ fail = true;
+ }
+
+ /* Make sure any above failure is preserved */
+ errobj = virSaveLastError();
+ virNetworkFree(network);
+ virSetError(errobj);
+ virFreeError(errobj);
+
+ if (fail)
+ goto cleanup;
+
+ if (!(veth = virLXCProcessSetupInterfaceBridged(conn,
+ vm->def,
+ net,
+ brname))) {
+ VIR_FREE(brname);
+ goto cleanup;
+ }
+ VIR_FREE(brname);
+ } break;
+ case VIR_DOMAIN_NET_TYPE_DIRECT: {
+ if (!(veth = virLXCProcessSetupInterfaceDirect(conn,
+ vm->def,
+ net)))
+ goto cleanup;
+ } break;
+ default:
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("Network device type is not supported"));
+ goto cleanup;
+ }
+
+ if (virNetDevSetNamespace(veth, priv->initpid) < 0) {
+ virDomainAuditNet(vm, NULL, net, "attach", false);
Maybe it's better to move failed audit to the cleanup path.
+ goto cleanup;
+ }
+
+ virDomainAuditNet(vm, NULL, net, "attach", true);
+
+ ret = 0;
+
+cleanup:
+ if (!ret) {
+ vm->def->nets[vm->def->nnets++] = net;
+ } else if (veth) {
+ switch (actualType) {
+ case VIR_DOMAIN_NET_TYPE_BRIDGE:
+ case VIR_DOMAIN_NET_TYPE_NETWORK:
+ ignore_value(virNetDevVethDelete(veth));
+ break;
+
+ case VIR_DOMAIN_NET_TYPE_DIRECT:
+ ignore_value(virNetDevMacVLanDelete(veth));
+ break;
+ }
+ }
+
+ return ret;
+}
+
+
+static int
+lxcDomainAttachDeviceLive(virConnectPtr conn,
+ virLXCDriverPtr driver,
+ virDomainObjPtr vm,
virDomainDeviceDefPtr dev)
{
int ret = -1;
@@ -3087,6 +3219,13 @@ lxcDomainAttachDeviceLive(virLXCDriverPtr driver
ATTRIBUTE_UNUSED,
dev->data.disk = NULL;
break;
+ case VIR_DOMAIN_DEVICE_NET:
+ ret = lxcDomainAttachDeviceNetLive(conn, vm,
+
dev->data.net);
+ if (!ret)
+
dev->data.net = NULL;
+ break;
+
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("device type '%s' cannot be attached"),
@@ -3170,8 +3309,74 @@ cleanup:
static int
-lxcDomainDetachDeviceLive(virLXCDriverPtr driver ATTRIBUTE_UNUSED,
- virDomainObjPtr vm ATTRIBUTE_UNUSED,
+lxcDomainDetachDeviceNetLive(virDomainObjPtr vm,
+ virDomainDeviceDefPtr dev)
+{
+ int detachidx, ret = -1;
+ virDomainNetDefPtr detach = NULL;
+ char mac[VIR_MAC_STRING_BUFLEN];
+ virNetDevVPortProfilePtr vport = NULL;
+
+ detachidx = virDomainNetFindIdx(vm->def,
dev->data.net);
+ if (detachidx == -2) {
+ virReportError(VIR_ERR_OPERATION_FAILED,
+ _("multiple devices matching mac address %s found"),
+ virMacAddrFormat(&dev->data.net->mac, mac));
+ goto cleanup;
+ } else if (detachidx < 0) {
+ virReportError(VIR_ERR_OPERATION_FAILED,
+ _("network device %s not found"),
+ virMacAddrFormat(&dev->data.net->mac, mac));
+ goto cleanup;
+ }
+ detach = vm->def->nets[detachidx];
+
+ switch (virDomainNetGetActualType(detach)) {
+ case VIR_DOMAIN_NET_TYPE_BRIDGE:
+ case VIR_DOMAIN_NET_TYPE_NETWORK:
+ if (virNetDevVethDelete(detach->ifname) < 0) {
+ virDomainAuditNet(vm, detach, NULL, "detach", false);
+ goto cleanup;
+ }
+ break;
+
+ /* It'd be nice to support this, but with macvlan
+ * once assigned to a container nothing exists on
+ * the host side. Further the container can change
+ * the mac address of NIC name, so we can't easily
+ * find out which guest NIC it maps to
+ case VIR_DOMAIN_NET_TYPE_DIRECT:
+ */
+
+ default:
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("Only bridged veth devices can be detached"));
+ goto cleanup;
+ }
+
+ virDomainAuditNet(vm, detach, NULL, "detach", true);
+
+ virDomainConfNWFilterTeardown(detach);
+
+ vport = virDomainNetGetActualVirtPortProfile(detach);
+ if (vport && vport->virtPortType ==
VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH)
+ ignore_value(virNetDevOpenvswitchRemovePort(
+ virDomainNetGetActualBridgeName(detach),
+ detach->ifname));
+ ret = 0;
+cleanup:
+ if (!ret) {
+ networkReleaseActualDevice(detach);
+ virDomainNetRemove(vm->def, detachidx);
+ virDomainNetDefFree(detach);
+ }
else
virDomainAuditNet(vm, detach, NULL, "detach", false);
ACK