libvirt/src/lxc/lxc_native.c
Peter Krempa 4f9907cd11 conf: Replace access to def->mem.max_balloon with accessor functions
As there are two possible approaches to define a domain's memory size -
one used with legacy, non-NUMA VMs configured in the <memory> element
and per-node based approach on NUMA machines - the user needs to make
sure that both are specified correctly in the NUMA case.

To avoid this burden on the user I'd like to replace the NUMA case with
automatic totaling of the memory size. To achieve this I need to replace
direct access to the virDomainMemtune's 'max_balloon' field with
two separate getters depending on the desired size.

The two sizes are needed as:
1) Startup memory size doesn't include memory modules in some
hypervisors.
2) After startup these count as the usable memory size.

Note that the comments for the functions are future aware and document
state that will be present after a few later patches.
2015-03-16 14:26:51 +01:00

1106 lines
30 KiB
C

/*
* lxc_native.c: LXC native configuration import
*
* Copyright (c) 2014 Red Hat, Inc.
* Copyright (c) 2013-2015 SUSE LINUX Products GmbH, Nuernberg, Germany.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*
* Author: Cedric Bosdonnat <cbosdonnat@suse.com>
*/
#include <config.h>
#include <stdio.h>
#include "internal.h"
#include "lxc_container.h"
#include "lxc_native.h"
#include "util/viralloc.h"
#include "util/virfile.h"
#include "util/virlog.h"
#include "util/virstring.h"
#include "util/virconf.h"
#define VIR_FROM_THIS VIR_FROM_LXC
VIR_LOG_INIT("lxc.lxc_native");
static virDomainFSDefPtr
lxcCreateFSDef(int type,
const char *src,
const char* dst,
bool readonly,
unsigned long long usage)
{
virDomainFSDefPtr def;
if (VIR_ALLOC(def) < 0)
return NULL;
def->type = type;
def->accessmode = VIR_DOMAIN_FS_ACCESSMODE_PASSTHROUGH;
if (src && VIR_STRDUP(def->src, src) < 0)
goto error;
if (VIR_STRDUP(def->dst, dst) < 0)
goto error;
def->readonly = readonly;
def->usage = usage;
return def;
error:
virDomainFSDefFree(def);
return NULL;
}
typedef struct _lxcFstab lxcFstab;
typedef lxcFstab *lxcFstabPtr;
struct _lxcFstab {
lxcFstabPtr next;
char *src;
char *dst;
char *type;
char *options;
};
static void
lxcFstabFree(lxcFstabPtr fstab)
{
while (fstab) {
lxcFstabPtr next = NULL;
next = fstab->next;
VIR_FREE(fstab->src);
VIR_FREE(fstab->dst);
VIR_FREE(fstab->type);
VIR_FREE(fstab->options);
VIR_FREE(fstab);
fstab = next;
}
}
static char ** lxcStringSplit(const char *string)
{
char *tmp;
size_t i;
size_t ntokens = 0;
char **parts;
char **result = NULL;
if (VIR_STRDUP(tmp, string) < 0)
return NULL;
/* Replace potential \t by a space */
for (i = 0; tmp[i]; i++) {
if (tmp[i] == '\t')
tmp[i] = ' ';
}
if (!(parts = virStringSplit(tmp, " ", 0)))
goto error;
/* Append NULL element */
if (VIR_EXPAND_N(result, ntokens, 1) < 0)
goto error;
for (i = 0; parts[i]; i++) {
if (STREQ(parts[i], ""))
continue;
if (VIR_EXPAND_N(result, ntokens, 1) < 0)
goto error;
if (VIR_STRDUP(result[ntokens-2], parts[i]) < 0)
goto error;
}
VIR_FREE(tmp);
virStringFreeList(parts);
return result;
error:
VIR_FREE(tmp);
virStringFreeList(parts);
virStringFreeList(result);
return NULL;
}
static lxcFstabPtr
lxcParseFstabLine(char *fstabLine)
{
lxcFstabPtr fstab = NULL;
char **parts;
if (!fstabLine || VIR_ALLOC(fstab) < 0)
return NULL;
if (!(parts = lxcStringSplit(fstabLine)))
goto error;
if (!parts[0] || !parts[1] || !parts[2] || !parts[3])
goto error;
if (VIR_STRDUP(fstab->src, parts[0]) < 0 ||
VIR_STRDUP(fstab->dst, parts[1]) < 0 ||
VIR_STRDUP(fstab->type, parts[2]) < 0 ||
VIR_STRDUP(fstab->options, parts[3]) < 0)
goto error;
virStringFreeList(parts);
return fstab;
error:
lxcFstabFree(fstab);
virStringFreeList(parts);
return NULL;
}
static int
lxcAddFSDef(virDomainDefPtr def,
int type,
const char *src,
const char *dst,
bool readonly,
unsigned long long usage)
{
virDomainFSDefPtr fsDef = NULL;
if (!(fsDef = lxcCreateFSDef(type, src, dst, readonly, usage)))
goto error;
if (VIR_EXPAND_N(def->fss, def->nfss, 1) < 0)
goto error;
def->fss[def->nfss - 1] = fsDef;
return 0;
error:
virDomainFSDefFree(fsDef);
return -1;
}
static int
lxcSetRootfs(virDomainDefPtr def,
virConfPtr properties)
{
int type = VIR_DOMAIN_FS_TYPE_MOUNT;
virConfValuePtr value;
if (!(value = virConfGetValue(properties, "lxc.rootfs")) ||
!value->str) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Missing lxc.rootfs configuration"));
return -1;
}
if (STRPREFIX(value->str, "/dev/"))
type = VIR_DOMAIN_FS_TYPE_BLOCK;
if (lxcAddFSDef(def, type, value->str, "/", false, 0) < 0)
return -1;
return 0;
}
static int
lxcConvertSize(const char *size, unsigned long long *value)
{
char *unit = NULL;
/* Split the string into value and unit */
if (virStrToLong_ull(size, &unit, 10, value) < 0)
goto error;
if (STREQ(unit, "%")) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("can't convert relative size: '%s'"),
size);
return -1;
} else {
if (virScaleInteger(value, unit, 1, ULLONG_MAX) < 0)
goto error;
}
return 0;
error:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("failed to convert size: '%s'"),
size);
return -1;
}
static int
lxcAddFstabLine(virDomainDefPtr def, lxcFstabPtr fstab)
{
const char *src = NULL;
char *dst = NULL;
char **options = virStringSplit(fstab->options, ",", 0);
bool readonly;
int type = VIR_DOMAIN_FS_TYPE_MOUNT;
unsigned long long usage = 0;
int ret = -1;
if (!options)
return -1;
if (fstab->dst[0] != '/') {
if (virAsprintf(&dst, "/%s", fstab->dst) < 0)
goto cleanup;
} else {
if (VIR_STRDUP(dst, fstab->dst) < 0)
goto cleanup;
}
/* Check that we don't add basic mounts */
if (lxcIsBasicMountLocation(dst)) {
ret = 0;
goto cleanup;
}
if (STREQ(fstab->type, "tmpfs")) {
char *sizeStr = NULL;
size_t i;
type = VIR_DOMAIN_FS_TYPE_RAM;
for (i = 0; options[i]; i++) {
if ((sizeStr = STRSKIP(options[i], "size="))) {
if (lxcConvertSize(sizeStr, &usage) < 0)
goto cleanup;
break;
}
}
if (!sizeStr) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("missing tmpfs size, set the size option"));
goto cleanup;
}
} else {
src = fstab->src;
}
/* Is it a block device that needs special favor? */
if (STRPREFIX(fstab->src, "/dev/"))
type = VIR_DOMAIN_FS_TYPE_BLOCK;
/* Do we have ro in options? */
readonly = virStringArrayHasString(options, "ro");
if (lxcAddFSDef(def, type, src, dst, readonly, usage) < 0)
goto cleanup;
ret = 1;
cleanup:
VIR_FREE(dst);
virStringFreeList(options);
return ret;
}
static int
lxcFstabWalkCallback(const char* name, virConfValuePtr value, void * data)
{
int ret = 0;
lxcFstabPtr fstabLine;
virDomainDefPtr def = data;
/* We only care about lxc.mount.entry lines */
if (STRNEQ(name, "lxc.mount.entry"))
return 0;
fstabLine = lxcParseFstabLine(value->str);
if (!fstabLine)
return -1;
if (lxcAddFstabLine(def, fstabLine) < 0)
ret = -1;
lxcFstabFree(fstabLine);
return ret;
}
static virDomainNetDefPtr
lxcCreateNetDef(const char *type,
const char *linkdev,
const char *mac,
const char *flag,
const char *macvlanmode,
const char *name)
{
virDomainNetDefPtr net = NULL;
virMacAddr macAddr;
if (VIR_ALLOC(net) < 0)
goto error;
if (flag) {
if (STREQ(flag, "up"))
net->linkstate = VIR_DOMAIN_NET_INTERFACE_LINK_STATE_UP;
else
net->linkstate = VIR_DOMAIN_NET_INTERFACE_LINK_STATE_DOWN;
}
if (VIR_STRDUP(net->ifname_guest, name) < 0)
goto error;
if (mac && virMacAddrParse(mac, &macAddr) == 0)
net->mac = macAddr;
if (STREQ(type, "veth")) {
if (!linkdev)
goto error;
net->type = VIR_DOMAIN_NET_TYPE_BRIDGE;
if (VIR_STRDUP(net->data.bridge.brname, linkdev) < 0)
goto error;
} else if (STREQ(type, "macvlan")) {
net->type = VIR_DOMAIN_NET_TYPE_DIRECT;
if (!linkdev || VIR_STRDUP(net->data.direct.linkdev, linkdev) < 0)
goto error;
if (!macvlanmode || STREQ(macvlanmode, "private"))
net->data.direct.mode = VIR_NETDEV_MACVLAN_MODE_PRIVATE;
else if (STREQ(macvlanmode, "vepa"))
net->data.direct.mode = VIR_NETDEV_MACVLAN_MODE_VEPA;
else if (STREQ(macvlanmode, "bridge"))
net->data.direct.mode = VIR_NETDEV_MACVLAN_MODE_BRIDGE;
else
VIR_WARN("Unknown macvlan type: %s", macvlanmode);
}
return net;
error:
virDomainNetDefFree(net);
return NULL;
}
static virDomainHostdevDefPtr
lxcCreateHostdevDef(int mode, int type, const char *data)
{
virDomainHostdevDefPtr hostdev = virDomainHostdevDefAlloc();
if (!hostdev)
return NULL;
hostdev->mode = mode;
hostdev->source.caps.type = type;
if (type == VIR_DOMAIN_HOSTDEV_CAPS_TYPE_NET &&
VIR_STRDUP(hostdev->source.caps.u.net.iface, data) < 0) {
virDomainHostdevDefFree(hostdev);
hostdev = NULL;
}
return hostdev;
}
typedef struct {
virDomainDefPtr def;
char *type;
char *link;
char *mac;
char *flag;
char *macvlanmode;
char *vlanid;
char *name;
virDomainNetIpDefPtr *ips;
size_t nips;
char *gateway_ipv4;
char *gateway_ipv6;
bool privnet;
size_t networks;
} lxcNetworkParseData;
static int
lxcAddNetworkRouteDefinition(const char *address,
int family,
virNetworkRouteDefPtr **routes,
size_t *nroutes)
{
virNetworkRouteDefPtr route = NULL;
char *familyStr = NULL;
char *zero = NULL;
if (VIR_STRDUP(zero, family == AF_INET ? VIR_SOCKET_ADDR_IPV4_ALL
: VIR_SOCKET_ADDR_IPV6_ALL) < 0)
goto error;
if (VIR_STRDUP(familyStr, family == AF_INET ? "ipv4" : "ipv6") < 0)
goto error;
if (!(route = virNetworkRouteDefCreate(_("Domain interface"), familyStr,
zero, NULL, address, 0, false,
0, false)))
goto error;
if (VIR_APPEND_ELEMENT(*routes, *nroutes, route) < 0)
goto error;
VIR_FREE(familyStr);
VIR_FREE(zero);
return 0;
error:
VIR_FREE(familyStr);
VIR_FREE(zero);
virNetworkRouteDefFree(route);
return -1;
}
static int
lxcAddNetworkDefinition(lxcNetworkParseData *data)
{
virDomainNetDefPtr net = NULL;
virDomainHostdevDefPtr hostdev = NULL;
bool isPhys, isVlan = false;
size_t i;
if ((data->type == NULL) || STREQ(data->type, "empty") ||
STREQ(data->type, "") || STREQ(data->type, "none"))
return 0;
isPhys = STREQ(data->type, "phys");
isVlan = STREQ(data->type, "vlan");
if (data->type != NULL && (isPhys || isVlan)) {
if (!data->link) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Missing 'link' attribute for NIC"));
goto error;
}
if (!(hostdev = lxcCreateHostdevDef(VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES,
VIR_DOMAIN_HOSTDEV_CAPS_TYPE_NET,
data->link)))
goto error;
/* This still requires the user to manually setup the vlan interface
* on the host */
if (isVlan && data->vlanid) {
VIR_FREE(hostdev->source.caps.u.net.iface);
if (virAsprintf(&hostdev->source.caps.u.net.iface,
"%s.%s", data->link, data->vlanid) < 0)
goto error;
}
hostdev->source.caps.u.net.ips = data->ips;
hostdev->source.caps.u.net.nips = data->nips;
if (data->gateway_ipv4 &&
lxcAddNetworkRouteDefinition(data->gateway_ipv4, AF_INET,
&hostdev->source.caps.u.net.routes,
&hostdev->source.caps.u.net.nroutes) < 0)
goto error;
if (data->gateway_ipv6 &&
lxcAddNetworkRouteDefinition(data->gateway_ipv6, AF_INET6,
&hostdev->source.caps.u.net.routes,
&hostdev->source.caps.u.net.nroutes) < 0)
goto error;
if (VIR_EXPAND_N(data->def->hostdevs, data->def->nhostdevs, 1) < 0)
goto error;
data->def->hostdevs[data->def->nhostdevs - 1] = hostdev;
} else {
if (!(net = lxcCreateNetDef(data->type, data->link, data->mac,
data->flag, data->macvlanmode,
data->name)))
goto error;
net->ips = data->ips;
net->nips = data->nips;
if (data->gateway_ipv4 &&
lxcAddNetworkRouteDefinition(data->gateway_ipv4, AF_INET,
&net->routes,
&net->nroutes) < 0)
goto error;
if (data->gateway_ipv6 &&
lxcAddNetworkRouteDefinition(data->gateway_ipv6, AF_INET6,
&net->routes,
&net->nroutes) < 0)
goto error;
if (VIR_EXPAND_N(data->def->nets, data->def->nnets, 1) < 0)
goto error;
data->def->nets[data->def->nnets - 1] = net;
}
return 1;
error:
for (i = 0; i < data->nips; i++)
VIR_FREE(data->ips[i]);
VIR_FREE(data->ips);
virDomainNetDefFree(net);
virDomainHostdevDefFree(hostdev);
return -1;
}
static int
lxcNetworkWalkCallback(const char *name, virConfValuePtr value, void *data)
{
lxcNetworkParseData *parseData = data;
int status;
if (STREQ(name, "lxc.network.type")) {
/* Store the previous NIC */
status = lxcAddNetworkDefinition(parseData);
if (status < 0)
return -1;
else if (status > 0)
parseData->networks++;
else if (parseData->type != NULL && STREQ(parseData->type, "none"))
parseData->privnet = false;
/* Start a new network interface config */
parseData->type = NULL;
parseData->link = NULL;
parseData->mac = NULL;
parseData->flag = NULL;
parseData->macvlanmode = NULL;
parseData->vlanid = NULL;
parseData->name = NULL;
parseData->ips = NULL;
parseData->nips = 0;
/* Keep the new value */
parseData->type = value->str;
}
else if (STREQ(name, "lxc.network.link"))
parseData->link = value->str;
else if (STREQ(name, "lxc.network.hwaddr"))
parseData->mac = value->str;
else if (STREQ(name, "lxc.network.flags"))
parseData->flag = value->str;
else if (STREQ(name, "lxc.network.macvlan.mode"))
parseData->macvlanmode = value->str;
else if (STREQ(name, "lxc.network.vlan.id"))
parseData->vlanid = value->str;
else if (STREQ(name, "lxc.network.name"))
parseData->name = value->str;
else if (STREQ(name, "lxc.network.ipv4") ||
STREQ(name, "lxc.network.ipv6")) {
int family = AF_INET;
char **ipparts = NULL;
virDomainNetIpDefPtr ip = NULL;
if (VIR_ALLOC(ip) < 0)
return -1;
if (STREQ(name, "lxc.network.ipv6"))
family = AF_INET6;
ipparts = virStringSplit(value->str, "/", 2);
if (virStringListLength(ipparts) != 2 ||
virSocketAddrParse(&ip->address, ipparts[0], family) < 0 ||
virStrToLong_ui(ipparts[1], NULL, 10, &ip->prefix) < 0) {
virReportError(VIR_ERR_INVALID_ARG,
_("Invalid CIDR address: '%s'"), value->str);
virStringFreeList(ipparts);
VIR_FREE(ip);
return -1;
}
virStringFreeList(ipparts);
if (VIR_APPEND_ELEMENT(parseData->ips, parseData->nips, ip) < 0) {
VIR_FREE(ip);
return -1;
}
} else if (STREQ(name, "lxc.network.ipv4.gateway")) {
parseData->gateway_ipv4 = value->str;
} else if (STREQ(name, "lxc.network.ipv6.gateway")) {
parseData->gateway_ipv6 = value->str;
} else if (STRPREFIX(name, "lxc.network")) {
VIR_WARN("Unhandled network property: %s = %s",
name,
value->str);
}
return 0;
}
static int
lxcConvertNetworkSettings(virDomainDefPtr def, virConfPtr properties)
{
int status;
int result = -1;
size_t i;
lxcNetworkParseData data = {def, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, 0,
NULL, NULL, true, 0};
if (virConfWalk(properties, lxcNetworkWalkCallback, &data) < 0)
goto error;
/* Add the last network definition found */
status = lxcAddNetworkDefinition(&data);
if (status < 0)
goto error;
else if (status > 0)
data.networks++;
else if (data.type != NULL && STREQ(data.type, "none"))
data.privnet = false;
if (data.networks == 0 && data.privnet) {
/* When no network type is provided LXC only adds loopback */
def->features[VIR_DOMAIN_FEATURE_PRIVNET] = VIR_TRISTATE_SWITCH_ON;
}
result = 0;
return result;
error:
for (i = 0; i < data.nips; i++)
VIR_FREE(data.ips[i]);
VIR_FREE(data.ips);
return -1;
}
static int
lxcCreateConsoles(virDomainDefPtr def, virConfPtr properties)
{
virConfValuePtr value;
int nbttys = 0;
virDomainChrDefPtr console;
size_t i;
if (!(value = virConfGetValue(properties, "lxc.tty")) || !value->str)
return 0;
if (virStrToLong_i(value->str, NULL, 10, &nbttys) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, _("failed to parse int: '%s'"),
value->str);
return -1;
}
if (VIR_ALLOC_N(def->consoles, nbttys) < 0)
return -1;
def->nconsoles = nbttys;
for (i = 0; i < nbttys; i++) {
if (!(console = virDomainChrDefNew()))
goto error;
console->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE;
console->targetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_LXC;
console->target.port = i;
console->source.type = VIR_DOMAIN_CHR_TYPE_PTY;
def->consoles[i] = console;
}
return 0;
error:
virDomainChrDefFree(console);
return -1;
}
static int
lxcIdmapWalkCallback(const char *name, virConfValuePtr value, void *data)
{
virDomainDefPtr def = data;
virDomainIdMapEntryPtr idmap = NULL;
char type;
unsigned long start, target, count;
if (STRNEQ(name, "lxc.id_map") || !value->str)
return 0;
if (sscanf(value->str, "%c %lu %lu %lu", &type,
&target, &start, &count) != 4) {
virReportError(VIR_ERR_INTERNAL_ERROR, _("invalid lxc.id_map: '%s'"),
value->str);
return -1;
}
if (type == 'u') {
if (VIR_EXPAND_N(def->idmap.uidmap, def->idmap.nuidmap, 1) < 0)
return -1;
idmap = &def->idmap.uidmap[def->idmap.nuidmap - 1];
} else if (type == 'g') {
if (VIR_EXPAND_N(def->idmap.gidmap, def->idmap.ngidmap, 1) < 0)
return -1;
idmap = &def->idmap.gidmap[def->idmap.ngidmap - 1];
} else {
return -1;
}
idmap->start = start;
idmap->target = target;
idmap->count = count;
return 0;
}
static int
lxcSetMemTune(virDomainDefPtr def, virConfPtr properties)
{
virConfValuePtr value;
unsigned long long size = 0;
if ((value = virConfGetValue(properties,
"lxc.cgroup.memory.limit_in_bytes")) &&
value->str && STRNEQ(value->str, "-1")) {
if (lxcConvertSize(value->str, &size) < 0)
return -1;
size = size / 1024;
virDomainDefSetMemoryInitial(def, size);
def->mem.hard_limit = virMemoryLimitTruncate(size);
}
if ((value = virConfGetValue(properties,
"lxc.cgroup.memory.soft_limit_in_bytes")) &&
value->str && STRNEQ(value->str, "-1")) {
if (lxcConvertSize(value->str, &size) < 0)
return -1;
def->mem.soft_limit = virMemoryLimitTruncate(size / 1024);
}
if ((value = virConfGetValue(properties,
"lxc.cgroup.memory.memsw.limit_in_bytes")) &&
value->str && STRNEQ(value->str, "-1")) {
if (lxcConvertSize(value->str, &size) < 0)
return -1;
def->mem.swap_hard_limit = virMemoryLimitTruncate(size / 1024);
}
return 0;
}
static int
lxcSetCpuTune(virDomainDefPtr def, virConfPtr properties)
{
virConfValuePtr value;
if ((value = virConfGetValue(properties, "lxc.cgroup.cpu.shares")) &&
value->str) {
if (virStrToLong_ul(value->str, NULL, 10, &def->cputune.shares) < 0)
goto error;
def->cputune.sharesSpecified = true;
}
if ((value = virConfGetValue(properties,
"lxc.cgroup.cpu.cfs_quota_us")) &&
value->str && virStrToLong_ll(value->str, NULL, 10,
&def->cputune.quota) < 0)
goto error;
if ((value = virConfGetValue(properties,
"lxc.cgroup.cpu.cfs_period_us")) &&
value->str && virStrToLong_ull(value->str, NULL, 10,
&def->cputune.period) < 0)
goto error;
return 0;
error:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("failed to parse integer: '%s'"), value->str);
return -1;
}
static int
lxcSetCpusetTune(virDomainDefPtr def, virConfPtr properties)
{
virConfValuePtr value;
virBitmapPtr nodeset = NULL;
if ((value = virConfGetValue(properties, "lxc.cgroup.cpuset.cpus")) &&
value->str) {
if (virBitmapParse(value->str, 0, &def->cpumask,
VIR_DOMAIN_CPUMASK_LEN) < 0)
return -1;
def->placement_mode = VIR_DOMAIN_CPU_PLACEMENT_MODE_STATIC;
}
if ((value = virConfGetValue(properties, "lxc.cgroup.cpuset.mems")) &&
value->str) {
if (virBitmapParse(value->str, 0, &nodeset, VIR_DOMAIN_CPUMASK_LEN) < 0)
return -1;
if (virDomainNumatuneSet(def->numa,
def->placement_mode ==
VIR_DOMAIN_CPU_PLACEMENT_MODE_STATIC,
VIR_DOMAIN_NUMATUNE_PLACEMENT_STATIC,
VIR_DOMAIN_NUMATUNE_MEM_STRICT,
nodeset) < 0) {
virBitmapFree(nodeset);
return -1;
}
virBitmapFree(nodeset);
}
return 0;
}
static int
lxcBlkioDeviceWalkCallback(const char *name, virConfValuePtr value, void *data)
{
char **parts = NULL;
virBlkioDevicePtr device = NULL;
virDomainDefPtr def = data;
size_t i = 0;
char *path = NULL;
int ret = -1;
if (!STRPREFIX(name, "lxc.cgroup.blkio.") ||
STREQ(name, "lxc.cgroup.blkio.weight")|| !value->str)
return 0;
if (!(parts = lxcStringSplit(value->str)))
return -1;
if (!parts[0] || !parts[1]) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("invalid %s value: '%s'"),
name, value->str);
goto cleanup;
}
if (virAsprintf(&path, "/dev/block/%s", parts[0]) < 0)
goto cleanup;
/* Do we already have a device definition for this path?
* Get that device or create a new one */
for (i = 0; !device && i < def->blkio.ndevices; i++) {
if (STREQ(def->blkio.devices[i].path, path))
device = &def->blkio.devices[i];
}
if (!device) {
if (VIR_EXPAND_N(def->blkio.devices, def->blkio.ndevices, 1) < 0)
goto cleanup;
device = &def->blkio.devices[def->blkio.ndevices - 1];
device->path = path;
path = NULL;
}
/* Set the value */
if (STREQ(name, "lxc.cgroup.blkio.device_weight")) {
if (virStrToLong_ui(parts[1], NULL, 10, &device->weight) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("failed to parse device weight: '%s'"), parts[1]);
goto cleanup;
}
} else if (STREQ(name, "lxc.cgroup.blkio.throttle.read_bps_device")) {
if (virStrToLong_ull(parts[1], NULL, 10, &device->rbps) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("failed to parse read_bps_device: '%s'"),
parts[1]);
goto cleanup;
}
} else if (STREQ(name, "lxc.cgroup.blkio.throttle.write_bps_device")) {
if (virStrToLong_ull(parts[1], NULL, 10, &device->wbps) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("failed to parse write_bps_device: '%s'"),
parts[1]);
goto cleanup;
}
} else if (STREQ(name, "lxc.cgroup.blkio.throttle.read_iops_device")) {
if (virStrToLong_ui(parts[1], NULL, 10, &device->riops) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("failed to parse read_iops_device: '%s'"),
parts[1]);
goto cleanup;
}
} else if (STREQ(name, "lxc.cgroup.blkio.throttle.write_iops_device")) {
if (virStrToLong_ui(parts[1], NULL, 10, &device->wiops) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("failed to parse write_iops_device: '%s'"),
parts[1]);
goto cleanup;
}
} else {
VIR_WARN("Unhandled blkio tune config: %s", name);
}
ret = 0;
cleanup:
virStringFreeList(parts);
VIR_FREE(path);
return ret;
}
static int
lxcSetBlkioTune(virDomainDefPtr def, virConfPtr properties)
{
virConfValuePtr value;
if ((value = virConfGetValue(properties, "lxc.cgroup.blkio.weight")) &&
value->str && virStrToLong_ui(value->str, NULL, 10,
&def->blkio.weight) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("failed to parse integer: '%s'"), value->str);
return -1;
}
if (virConfWalk(properties, lxcBlkioDeviceWalkCallback, def) < 0)
return -1;
return 0;
}
static void
lxcSetCapDrop(virDomainDefPtr def, virConfPtr properties)
{
virConfValuePtr value;
char **toDrop = NULL;
const char *capString;
size_t i;
if ((value = virConfGetValue(properties, "lxc.cap.drop")) && value->str)
toDrop = virStringSplit(value->str, " ", 0);
for (i = 0; i < VIR_DOMAIN_CAPS_FEATURE_LAST; i++) {
capString = virDomainCapsFeatureTypeToString(i);
if (toDrop != NULL && virStringArrayHasString(toDrop, capString))
def->caps_features[i] = VIR_TRISTATE_SWITCH_OFF;
}
def->features[VIR_DOMAIN_FEATURE_CAPABILITIES] = VIR_DOMAIN_CAPABILITIES_POLICY_ALLOW;
virStringFreeList(toDrop);
}
virDomainDefPtr
lxcParseConfigString(const char *config)
{
virDomainDefPtr vmdef = NULL;
virConfPtr properties = NULL;
virConfValuePtr value;
if (!(properties = virConfReadMem(config, 0, VIR_CONF_FLAG_LXC_FORMAT)))
return NULL;
if (!(vmdef = virDomainDefNew()))
goto error;
if (virUUIDGenerate(vmdef->uuid) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("failed to generate uuid"));
goto error;
}
vmdef->id = -1;
virDomainDefSetMemoryInitial(vmdef, 64 * 1024);
vmdef->onReboot = VIR_DOMAIN_LIFECYCLE_RESTART;
vmdef->onCrash = VIR_DOMAIN_LIFECYCLE_DESTROY;
vmdef->onPoweroff = VIR_DOMAIN_LIFECYCLE_DESTROY;
vmdef->virtType = VIR_DOMAIN_VIRT_LXC;
/* Value not handled by the LXC driver, setting to
* minimum required to make XML parsing pass */
vmdef->maxvcpus = 1;
vmdef->vcpus = 1;
vmdef->nfss = 0;
if (VIR_STRDUP(vmdef->os.type, "exe") < 0)
goto error;
if ((value = virConfGetValue(properties, "lxc.arch")) && value->str) {
virArch arch = virArchFromString(value->str);
if (arch == VIR_ARCH_NONE && STREQ(value->str, "x86"))
arch = VIR_ARCH_I686;
else if (arch == VIR_ARCH_NONE && STREQ(value->str, "amd64"))
arch = VIR_ARCH_X86_64;
vmdef->os.arch = arch;
}
if (VIR_STRDUP(vmdef->os.init, "/sbin/init") < 0)
goto error;
if (!(value = virConfGetValue(properties, "lxc.utsname")) ||
!value->str || (VIR_STRDUP(vmdef->name, value->str) < 0))
goto error;
if (!vmdef->name && (VIR_STRDUP(vmdef->name, "unnamed") < 0))
goto error;
if (lxcSetRootfs(vmdef, properties) < 0)
goto error;
/* Look for fstab: we shouldn't have it */
if (virConfGetValue(properties, "lxc.mount")) {
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
_("lxc.mount found, use lxc.mount.entry lines instead"));
goto error;
}
/* Loop over lxc.mount.entry to add filesystem devices for them */
if (virConfWalk(properties, lxcFstabWalkCallback, vmdef) < 0)
goto error;
/* Network configuration */
if (lxcConvertNetworkSettings(vmdef, properties) < 0)
goto error;
/* Consoles */
if (lxcCreateConsoles(vmdef, properties) < 0)
goto error;
/* lxc.id_map */
if (virConfWalk(properties, lxcIdmapWalkCallback, vmdef) < 0)
goto error;
/* lxc.cgroup.memory.* */
if (lxcSetMemTune(vmdef, properties) < 0)
goto error;
/* lxc.cgroup.cpu.* */
if (lxcSetCpuTune(vmdef, properties) < 0)
goto error;
/* lxc.cgroup.cpuset.* */
if (lxcSetCpusetTune(vmdef, properties) < 0)
goto error;
/* lxc.cgroup.blkio.* */
if (lxcSetBlkioTune(vmdef, properties) < 0)
goto error;
/* lxc.cap.drop */
lxcSetCapDrop(vmdef, properties);
goto cleanup;
error:
virDomainDefFree(vmdef);
vmdef = NULL;
cleanup:
virConfFree(properties);
return vmdef;
}