From b4f7acfcac6ea93bf6cdc23027647737d070dd18 Mon Sep 17 00:00:00 2001 From: heiher Date: Wed, 24 Oct 2012 08:55:09 +0800 Subject: [PATCH] Add device operations for lxc. Operations: 1. lxcDomainAttachDevice 2. lxcDomainAttachDeviceFlags 3. lxcDomainDetachDevice 4. lxcDomainDetachDeviceFlags 5. lxcDomainUpdateDeviceFlags In live mode, set devices.allow/deny to instance's cgroup and insert disk config into current domain. In config mode, just insert disk config into current domain. --- src/lxc/lxc_driver.c | 468 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 468 insertions(+) diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 87305db..45407ee 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -71,6 +71,8 @@ static int lxcStartup(int privileged); static int lxcShutdown(void); +static bool lxcCgroupControllerActive(virLXCDriverPtr driver, + int controller); virLXCDriverPtr lxc_driver = NULL; /* callbacks for nwfilter */ @@ -516,6 +518,467 @@ static int lxcDomainUndefine(virDomainPtr dom) return lxcDomainUndefineFlags(dom, 0); } + +/* Actions for lxcDomainModifyDeviceFlags */ +enum { + LXC_DEVICE_ATTACH, + LXC_DEVICE_DETACH, + LXC_DEVICE_UPDATE, +}; + +static int +lxcDomainAttachDeviceDiskLive(virLXCDriverPtr driver, + virDomainObjPtr vm, virDomainDeviceDefPtr dev) +{ + virDomainDiskDefPtr l_disk = dev->data.disk; + virCgroupPtr group = NULL; + int ret = -1; + + switch (l_disk->device) { + case VIR_DOMAIN_DISK_DEVICE_DISK: + if (l_disk->bus == VIR_DOMAIN_DISK_BUS_VIRTIO) { + if (virDomainDiskIndexByName(vm->def, l_disk->dst, true) >= 0) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("target %s already exists"), l_disk->dst); + goto cleanup; + } + + if (!l_disk->src) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("disk source path is missing")); + goto cleanup; + } + + if (!lxcCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("devices cgroup isn't mounted")); + goto cleanup; + } + + if (virCgroupForDomain(driver->cgroup, vm->def->name, &group, 0) != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot find cgroup for domain %s"), vm->def->name); + goto cleanup; + } + + if (virCgroupAllowDevicePath(group, l_disk->src, VIR_CGROUP_DEVICE_RWM) != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot allow device %s for domain %s"), + l_disk->src, vm->def->name); + goto cleanup; + } + + ret = virDomainDiskInsert(vm->def, l_disk); + + } else { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("disk bus '%s' cannot be hotplugged."), + virDomainDiskBusTypeToString(l_disk->bus)); + } + break; + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("disk device type '%s' cannot be hotplugged"), + virDomainDiskDeviceTypeToString(l_disk->device)); + break; + } + +cleanup: + return ret; +} + +static int +lxcDomainAttachDeviceLive(virLXCDriverPtr driver, virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + int ret = -1; + + switch (dev->type) { + case VIR_DOMAIN_DEVICE_DISK: + ret = lxcDomainAttachDeviceDiskLive(driver, vm, dev); + if (!ret) + dev->data.disk = NULL; + break; + + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("device type '%s' cannot be attached"), + virDomainDeviceTypeToString(dev->type)); + break; + } + + return ret; +} + +static int +lxcDomainAttachDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) +{ + virDomainDiskDefPtr disk; + + switch (dev->type) { + case VIR_DOMAIN_DEVICE_DISK: + disk = dev->data.disk; + if (virDomainDiskIndexByName(vmdef, disk->dst, true) >= 0) { + virReportError(VIR_ERR_INVALID_ARG, + _("target %s already exists."), disk->dst); + return -1; + } + if (virDomainDiskInsert(vmdef, disk)) { + virReportOOMError(); + return -1; + } + /* vmdef has the pointer. Generic codes for vmdef will do all jobs */ + dev->data.disk = NULL; + break; + + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("persistent attach of device is not supported")); + return -1; + } + return 0; +} + +static int +lxcDomainDetachDeviceDiskLive(virLXCDriverPtr driver, + virDomainObjPtr vm, virDomainDeviceDefPtr dev) +{ + virDomainDiskDefPtr l_disk = NULL; + virCgroupPtr group = NULL; + int i, ret = -1; + + switch (dev->data.disk->device) { + case VIR_DOMAIN_DISK_DEVICE_DISK: + if (dev->data.disk->bus == VIR_DOMAIN_DISK_BUS_VIRTIO) { + + if ((i = virDomainDiskIndexByName(vm->def, + dev->data.disk->dst, + false)) < 0) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("disk %s not found"), dev->data.disk->dst); + goto cleanup; + } + + l_disk = vm->def->disks[i]; + + if (!l_disk->src) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("disk source path is missing")); + goto cleanup; + } + + if (!lxcCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("devices cgroup isn't mounted")); + goto cleanup; + } + + if (virCgroupForDomain(driver->cgroup, vm->def->name, &group, 0) != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot find cgroup for domain %s"), vm->def->name); + goto cleanup; + } + + if (virCgroupDenyDevicePath(group, l_disk->src, VIR_CGROUP_DEVICE_RWM) != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot allow device %s for domain %s"), + l_disk->src, vm->def->name); + goto cleanup; + } + + virDomainDiskRemove(vm->def, i); + virDomainDiskDefFree(l_disk); + + ret = 0; + } else { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("disk bus '%s' cannot be hot unplugged."), + virDomainDiskBusTypeToString(dev->data.disk->bus)); + } + break; + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("device type '%s' cannot hot unplugged"), + virDomainDiskDeviceTypeToString(dev->data.disk->device)); + break; + } + +cleanup: + return ret; +} + +static int +lxcDomainDetachDeviceLive(virLXCDriverPtr driver, virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + int ret = -1; + + switch (dev->type) { + case VIR_DOMAIN_DEVICE_DISK: + ret = lxcDomainDetachDeviceDiskLive(driver, vm, dev); + break; + + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("device type '%s' cannot be detached"), + virDomainDeviceTypeToString(dev->type)); + break; + } + + return ret; +} + +static int +lxcDomainDetachDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) +{ + virDomainDiskDefPtr disk, detach; + int ret = -1; + + switch (dev->type) { + case VIR_DOMAIN_DEVICE_DISK: + disk = dev->data.disk; + if (!(detach = virDomainDiskRemoveByName(vmdef, disk->dst))) { + virReportError(VIR_ERR_INVALID_ARG, + _("no target device %s"), disk->dst); + break; + } + virDomainDiskDefFree(detach); + ret = 0; + break; + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("persistent detach of device is not supported")); + break; + } + + return ret; +} + +static int +lxcDomainUpdateDeviceLive(virLXCDriverPtr driver, + virDomainObjPtr vm, virDomainDeviceDefPtr dev) +{ + virDomainDiskDefPtr disk; + int ret = -1; + + switch (dev->type) { + case VIR_DOMAIN_DEVICE_DISK: + disk = dev->data.disk; + switch (disk->device) { + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("disk bus '%s' cannot be updated."), + virDomainDiskBusTypeToString(disk->bus)); + break; + } + ret = 0; + break; + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("device type '%s' cannot be updated"), + virDomainDeviceTypeToString(dev->type)); + break; + } + + return ret; +} + +static int +lxcDomainUpdateDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) +{ + virDomainDiskDefPtr orig; + virDomainDiskDefPtr disk; + int i; + int ret = -1; + + switch (dev->type) { + case VIR_DOMAIN_DEVICE_DISK: + disk = dev->data.disk; + if ((i = virDomainDiskIndexByName(vmdef, disk->dst, false)) < 0) { + virReportError(VIR_ERR_INVALID_ARG, + _("target %s doesn't exist."), disk->dst); + goto cleanup; + } + orig = vmdef->disks[i]; + if (!(orig->device == VIR_DOMAIN_DISK_DEVICE_CDROM)) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("this disk doesn't support update")); + goto cleanup; + } + + VIR_FREE(orig->src); + orig->src = disk->src; + orig->type = disk->type; + if (disk->driverName) { + VIR_FREE(orig->driverName); + orig->driverName = disk->driverName; + disk->driverName = NULL; + } + orig->format = disk->format; + disk->src = NULL; + break; + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("persistent update of device is not supported")); + goto cleanup; + } + + ret = 0; + +cleanup: + return ret; +} + +static int +lxcDomainModifyDeviceFlags(virDomainPtr dom, const char *xml, + unsigned int flags, int action) +{ + virLXCDriverPtr driver = dom->conn->privateData; + virDomainObjPtr vm = NULL; + virDomainDefPtr vmdef = NULL; + virDomainDeviceDefPtr dev = NULL; + int ret = -1; + + virCheckFlags(VIR_DOMAIN_DEVICE_MODIFY_LIVE | + VIR_DOMAIN_DEVICE_MODIFY_CONFIG, -1); + + lxcDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + + if (!vm) { + virReportError(VIR_ERR_NO_DOMAIN, "%s", _("no domain with matching uuid")); + goto cleanup; + } + + if (virDomainObjIsActive(vm)) { + if (flags == VIR_DOMAIN_DEVICE_MODIFY_CURRENT) + flags |= VIR_DOMAIN_DEVICE_MODIFY_LIVE; + } else { + if (flags == VIR_DOMAIN_DEVICE_MODIFY_CURRENT) + flags |= VIR_DOMAIN_DEVICE_MODIFY_CONFIG; + /* check consistency between flags and the vm state */ + if (flags & VIR_DOMAIN_DEVICE_MODIFY_LIVE) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("Domain is not running")); + goto cleanup; + } + } + + if ((flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) && !vm->persistent) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("cannot modify device on transient domain")); + goto cleanup; + } + + if (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) { + if (!(dev = virDomainDeviceDefParse(driver->caps, vm->def, xml, + VIR_DOMAIN_XML_INACTIVE))) + goto cleanup; + + /* Make a copy for updated domain. */ + if (!(vmdef = virDomainObjCopyPersistentDef(driver->caps, vm))) + goto cleanup; + + switch (action) { + case LXC_DEVICE_ATTACH: + ret = lxcDomainAttachDeviceConfig(vmdef, dev); + break; + case LXC_DEVICE_DETACH: + ret = lxcDomainDetachDeviceConfig(vmdef, dev); + break; + case LXC_DEVICE_UPDATE: + ret = lxcDomainUpdateDeviceConfig(vmdef, dev); + default: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown domain modify action %d"), action); + break; + } + } else + ret = 0; + + if (flags & VIR_DOMAIN_DEVICE_MODIFY_LIVE) { + /* If dev exists it was created to modify the domain config. Free it. */ + virDomainDeviceDefFree(dev); + if (!(dev = virDomainDeviceDefParse(driver->caps, vm->def, xml, + VIR_DOMAIN_XML_INACTIVE))) + goto cleanup; + + switch (action) { + case LXC_DEVICE_ATTACH: + ret = lxcDomainAttachDeviceLive(driver, vm, dev); + break; + case LXC_DEVICE_DETACH: + ret = lxcDomainDetachDeviceLive(driver, vm, dev); + break; + case LXC_DEVICE_UPDATE: + ret = lxcDomainUpdateDeviceLive(driver, vm, dev); + default: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown domain modify action %d"), action); + break; + } + /* + * update domain status forcibly because the domain status may be + * changed even if we attach the device failed. + */ + if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0) + ret = -1; + } + + /* Finally, if no error until here, we can save config. */ + if (!ret && (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG)) { + ret = virDomainSaveConfig(driver->configDir, vmdef); + if (!ret) { + virDomainObjAssignDef(vm, vmdef, false); + vmdef = NULL; + } + } + +cleanup: + virDomainDefFree(vmdef); + virDomainDeviceDefFree(dev); + if (vm) + virDomainObjUnlock(vm); + lxcDriverUnlock(driver); + return ret; +} + +static int +lxcDomainAttachDeviceFlags(virDomainPtr dom, const char *xml, + unsigned int flags) +{ + return lxcDomainModifyDeviceFlags(dom, xml, flags, LXC_DEVICE_ATTACH); +} + +static int +lxcDomainAttachDevice(virDomainPtr dom, const char *xml) +{ + return lxcDomainAttachDeviceFlags(dom, xml, + VIR_DOMAIN_DEVICE_MODIFY_LIVE); +} + +static int +lxcDomainDetachDeviceFlags(virDomainPtr dom, const char *xml, + unsigned int flags) +{ + return lxcDomainModifyDeviceFlags(dom, xml, flags, LXC_DEVICE_DETACH); +} + +static int +lxcDomainDetachDevice(virDomainPtr dom, const char *xml) +{ + return lxcDomainDetachDeviceFlags(dom, xml, + VIR_DOMAIN_DEVICE_MODIFY_LIVE); +} + +static int +lxcDomainUpdateDeviceFlags(virDomainPtr dom, const char *xml, + unsigned int flags) +{ + return lxcDomainModifyDeviceFlags(dom, xml, flags, LXC_DEVICE_UPDATE); +} + + static int lxcDomainGetInfo(virDomainPtr dom, virDomainInfoPtr info) { @@ -2724,6 +3187,11 @@ static virDriver lxcDriver = { .domainDefineXML = lxcDomainDefine, /* 0.4.2 */ .domainUndefine = lxcDomainUndefine, /* 0.4.2 */ .domainUndefineFlags = lxcDomainUndefineFlags, /* 0.9.4 */ + .domainAttachDevice = lxcDomainAttachDevice, /* 0.10.2 */ + .domainAttachDeviceFlags = lxcDomainAttachDeviceFlags, /* 0.10.2 */ + .domainDetachDevice = lxcDomainDetachDevice, /* 0.10.2 */ + .domainDetachDeviceFlags = lxcDomainDetachDeviceFlags, /* 0.10.2 */ + .domainUpdateDeviceFlags = lxcDomainUpdateDeviceFlags, /* 0.10.2 */ .domainGetAutostart = lxcDomainGetAutostart, /* 0.7.0 */ .domainSetAutostart = lxcDomainSetAutostart, /* 0.7.0 */ .domainGetSchedulerType = lxcGetSchedulerType, /* 0.5.0 */ -- 1.8.0