This function aims at converting LXC configuration into a libvirt domain XML description to help users migrate from LXC to libvirt. Here is an example of how the lxc configuration works: virsh -c lxc:/// domxml-from-native lxc /var/lib/lxc/migrate_test/config It is possible that some parts couldn't be properly mapped into a domain XML fragment, so users should carefully review the result before creating the domain. --- .gitignore | 1 + po/POTFILES.in | 1 + src/Makefile.am | 1 + src/lxc/lxc_driver.c | 36 +++- src/lxc/lxc_native.c | 259 ++++++++++++++++++++++++ src/lxc/lxc_native.h | 34 ++++ tests/Makefile.am | 7 +- tests/lxcconf2xmldata/lxcconf2xml-simple.config | 38 ++++ tests/lxcconf2xmldata/lxcconf2xml-simple.xml | 17 ++ tests/lxcconf2xmldata/lxcconf2xml.fstab | 3 + tests/lxcconf2xmltest.c | 124 ++++++++++++ 11 files changed, 519 insertions(+), 2 deletions(-) create mode 100644 src/lxc/lxc_native.c create mode 100644 src/lxc/lxc_native.h create mode 100644 tests/lxcconf2xmldata/lxcconf2xml-simple.config create mode 100644 tests/lxcconf2xmldata/lxcconf2xml-simple.xml create mode 100644 tests/lxcconf2xmldata/lxcconf2xml.fstab create mode 100644 tests/lxcconf2xmltest.c diff --git a/.gitignore b/.gitignore index 496c2ef..662cf8d 100644 --- a/.gitignore +++ b/.gitignore @@ -156,6 +156,7 @@ /tests/hashtest /tests/jsontest /tests/libvirtdconftest +/tests/lxcconf2xmltest /tests/metadatatest /tests/networkxml2argvtest /tests/nodeinfotest diff --git a/po/POTFILES.in b/po/POTFILES.in index 0359b2f..fd36bc5 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -62,6 +62,7 @@ src/locking/sanlock_helper.c src/lxc/lxc_cgroup.c src/lxc/lxc_fuse.c src/lxc/lxc_hostdev.c +src/lxc/lxc_native.c src/lxc/lxc_container.c src/lxc/lxc_conf.c src/lxc/lxc_controller.c diff --git a/src/Makefile.am b/src/Makefile.am index 57e163f..43df69f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -622,6 +622,7 @@ LXC_DRIVER_SOURCES = \ lxc/lxc_monitor.c lxc/lxc_monitor.h \ lxc/lxc_process.c lxc/lxc_process.h \ lxc/lxc_fuse.c lxc/lxc_fuse.h \ + lxc/lxc_native.c lxc/lxc_native.h \ lxc/lxc_driver.c lxc/lxc_driver.h LXC_CONTROLLER_SOURCES = \ diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 7e56a59..c10a722 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -44,6 +44,7 @@ #include "lxc_container.h" #include "lxc_domain.h" #include "lxc_driver.h" +#include "lxc_native.h" #include "lxc_process.h" #include "viralloc.h" #include "virnetdevbridge.h" @@ -73,7 +74,6 @@ #define VIR_FROM_THIS VIR_FROM_LXC - #define LXC_NB_MEM_PARAM 3 static int lxcStateInitialize(bool privileged, @@ -993,6 +993,39 @@ cleanup: return ret; } +static char *lxcConnectDomainXMLFromNative(virConnectPtr conn, + const char *nativeFormat, + const char *nativeConfig, + unsigned int flags) +{ + char *xml = NULL; + virDomainDefPtr def = NULL; + virNodeInfo nodeInfos; + + virCheckFlags(0, NULL); + + if (virConnectDomainXMLFromNativeEnsureACL(conn) < 0) + goto cleanup; + + if (STRNEQ(nativeFormat, LXC_CONFIG_FORMAT)) { + virReportError(VIR_ERR_INVALID_ARG, + _("unsupported config type %s"), nativeFormat); + goto cleanup; + } + + if (nodeGetInfo(&nodeInfos) < 0) + goto cleanup; + + if (!(def = lxcParseConfigString(nativeConfig, NULL, nodeInfos.memory))) + goto cleanup; + + xml = virDomainDefFormat(def, 0); + +cleanup: + virDomainDefFree(def); + return xml; +} + /** * lxcDomainCreateWithFiles: * @dom: domain to start @@ -4760,6 +4793,7 @@ static virDriver lxcDriver = { .domainGetSecurityLabel = lxcDomainGetSecurityLabel, /* 0.9.10 */ .nodeGetSecurityModel = lxcNodeGetSecurityModel, /* 0.9.10 */ .domainGetXMLDesc = lxcDomainGetXMLDesc, /* 0.4.2 */ + .connectDomainXMLFromNative = lxcConnectDomainXMLFromNative, /* 1.2.2 */ .connectListDefinedDomains = lxcConnectListDefinedDomains, /* 0.4.2 */ .connectNumOfDefinedDomains = lxcConnectNumOfDefinedDomains, /* 0.4.2 */ .domainCreate = lxcDomainCreate, /* 0.4.4 */ diff --git a/src/lxc/lxc_native.c b/src/lxc/lxc_native.c new file mode 100644 index 0000000..14b2844 --- /dev/null +++ b/src/lxc/lxc_native.c @@ -0,0 +1,259 @@ +/* + * lxc_native.c: LXC native configuration import + * + * Copyright (c) 2013 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@xxxxxxxx> + */ + +#include <config.h> + +#include "internal.h" +#include "lxc_native.h" +#include "util/viralloc.h" +#include "util/virlog.h" +#include "util/virstring.h" + +#define VIR_FROM_THIS VIR_FROM_LXC + +typedef struct _virPropertyEntry virPropertyEntry; +typedef virPropertyEntry *virPropertyEntryPtr; +struct _virPropertyEntry { + virPropertyEntry *next; + char *key; + char *value; +}; + +typedef struct { + virPropertyEntryPtr elements; + virPropertyEntryPtr last; + + /* Store the current item and key when looping */ + char *currentKey; + virPropertyEntryPtr current; +} virProperties; +typedef virProperties *virPropertiesPtr; + +static virPropertiesPtr +virPropertiesCreate(void) +{ + virPropertiesPtr properties = NULL; + + if (VIR_ALLOC(properties) < 0) + return NULL; + + properties->current = NULL; + properties->elements = NULL; + + return properties; +} + +static void +virPropertiesFree(virPropertiesPtr properties) +{ + virPropertyEntryPtr entry; + + if (!properties) + return; + + entry = properties->elements; + while (entry) { + virPropertyEntryPtr next = NULL; + next = entry->next; + + VIR_FREE(entry->key); + VIR_FREE(entry->value); + VIR_FREE(entry); + + entry = next; + } + + VIR_FREE(properties->currentKey); + VIR_FREE(properties); +} + +static int +virPropertiesAddEntry(virPropertiesPtr properties, char *key, char *value) +{ + virPropertyEntryPtr entry; + + if (!properties || !key) + return -1; + + if (VIR_ALLOC(entry) < 0) + return -1; + + entry->key = key; + entry->value = value; + entry->next = NULL; + + if (properties->last) + properties->last->next = entry; + if (!properties->elements) + properties->elements = entry; + + properties->last = entry; + + return 0; +} + +static char +*virPropertiesLookup(virPropertiesPtr properties, const char* key) +{ + if (!properties) + return NULL; + + /* If the key is not NULL, reset the search */ + if (key) { + properties->current = NULL; + VIR_FREE(properties->currentKey); + if (VIR_STRDUP(properties->currentKey, key) < 0) + return NULL; + } else if (!properties->currentKey) { + return NULL; + } + + if (!properties->current) + properties->current = properties->elements; + else + properties->current = properties->current->next; + + while (properties->current && STRNEQ(properties->current->key, properties->currentKey)) { + properties->current = properties->current->next; + } + + if (!properties->current) + return NULL; + + return properties->current->value; +} + +static char +*lxcSkipTrimSpaces(char *src) +{ + char *dest = NULL; + char *rawString = NULL; + const char* skipped; + + if (VIR_STRDUP(rawString, src) < 0) + return NULL; + + virTrimSpaces(rawString, NULL); + + skipped = rawString; + virSkipSpaces(&skipped); + + if (VIR_STRDUP(dest, skipped) < 0) + dest = NULL; + + VIR_FREE(rawString); + return dest; +} + +static virPropertiesPtr +lxcParseProperties(const char *properties) +{ + virPropertiesPtr table; + char **lines; + size_t i; + char *line; + char **lineParts; + char *key = NULL; + char *value = NULL; + + table = virPropertiesCreate(); + lines = virStringSplit(properties, "\n", 0); + + for (i = 0; lines[i]; i++) { + line = lines[i]; + + /* Ignore comment lines */ + if (line[0] == '#') + continue; + + /* Find key = value lines and skip other ones (bad or good) */ + lineParts = virStringSplit(line, "=", 2); + if (virStringListLength(lineParts) == 2) { + if (!(key = lxcSkipTrimSpaces(lineParts[0])) || + !(value = lxcSkipTrimSpaces(lineParts[1]))) + continue; + + if (virPropertiesAddEntry(table, key, value) < 0) + VIR_ERROR(_("Skipped lxc config entry: %s = %s"), + key, value); + } + virStringFreeList(lineParts); + } + + virStringFreeList(lines); + + return table; +} + +virDomainDefPtr +lxcParseConfigString(const char *config, + const char *fstab ATTRIBUTE_UNUSED, + unsigned long memory) +{ + virDomainDefPtr vmdef = NULL; + virPropertiesPtr properties = NULL; + + if (!(properties = lxcParseProperties(config))) + return NULL; + + if (VIR_ALLOC(vmdef) < 0) + goto error; + + if (virUUIDGenerate(vmdef->uuid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to generate uuid")); + goto error; + } + vmdef->id = -1; + vmdef->mem.max_balloon = memory; + + 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; + + if (VIR_STRDUP(vmdef->os.type, "exe") < 0) + goto error; + + if (VIR_STRDUP(vmdef->os.init, "/sbin/init") < 0) + goto error; + + if (VIR_STRDUP(vmdef->name, virPropertiesLookup(properties, "lxc.utsname")) < 0) + goto error; + if (!vmdef->name && VIR_STRDUP(vmdef->name, "unnamed")) + goto error; + + goto cleanup; + +error: + virDomainDefFree(vmdef); + vmdef = NULL; + +cleanup: + virPropertiesFree(properties); + + return vmdef; +} diff --git a/src/lxc/lxc_native.h b/src/lxc/lxc_native.h new file mode 100644 index 0000000..448fc09 --- /dev/null +++ b/src/lxc/lxc_native.h @@ -0,0 +1,34 @@ +/* + * lxc_native.h: LXC native configuration import + * + * Copyright (c) 2013 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@xxxxxxxx> + */ + +#ifndef __LXC_NATIVE_H__ +# define __LXC_NATIVE_H__ + +# include "domain_conf.h" + +# define LXC_CONFIG_FORMAT "lxc" + +virDomainDefPtr lxcParseConfigString(const char *config, + const char *fstab, + unsigned long memory); + +#endif /* __LXC_NATIVE_H__ */ diff --git a/tests/Makefile.am b/tests/Makefile.am index 568b7a0..b597a90 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -196,7 +196,7 @@ test_programs += qemuxml2argvtest qemuxml2xmltest qemuxmlnstest \ endif WITH_QEMU if WITH_LXC -test_programs += lxcxml2xmltest +test_programs += lxcxml2xmltest lxcconf2xmltest endif WITH_LXC if WITH_OPENVZ @@ -508,6 +508,11 @@ lxcxml2xmltest_SOURCES = \ lxcxml2xmltest.c testutilslxc.c testutilslxc.h \ testutils.c testutils.h lxcxml2xmltest_LDADD = $(lxc_LDADDS) + +lxcconf2xmltest_SOURCES = \ + lxcconf2xmltest.c \ + testutils.c testutils.h +lxcconf2xmltest_LDADD = $(lxc_LDADDS) else ! WITH_LXC EXTRA_DIST += lxcxml2xmltest.c testutilslxc.c testutilslxc.h endif ! WITH_LXC diff --git a/tests/lxcconf2xmldata/lxcconf2xml-simple.config b/tests/lxcconf2xmldata/lxcconf2xml-simple.config new file mode 100644 index 0000000..12428bb --- /dev/null +++ b/tests/lxcconf2xmldata/lxcconf2xml-simple.config @@ -0,0 +1,38 @@ +# Template used to create this container: opensuse +# Template script checksum (SHA-1): 27307e0a95bd81b2c0bd82d6f87fdbe83be075ef + +lxc.network.type = veth +lxc.network.flags = up +lxc.network.link = virbr0 +lxc.network.hwaddr = 02:00:15:8f:05:c1 +lxc.network.name = eth0 + +#remove next line if host DNS configuration should not be available to container +lxc.mount.entry = /etc/resolv.conf etc/resolv.conf none bind,ro 0 0 +lxc.rootfs = /var/lib/lxc/migrate_test/rootfs +lxc.utsname = migrate_test +lxc.autodev=1 +lxc.tty = 2 +lxc.pts = 1024 +lxc.mount = /var/lib/lxc/migrate_test/fstab +lxc.cap.drop = sys_module mac_admin mac_override mknod + +# When using LXC with apparmor, uncomment the next line to run unconfined: +#lxc.aa_profile = unconfined + +lxc.cgroup.devices.deny = a +# /dev/null and zero +lxc.cgroup.devices.allow = c 1:3 rwm +lxc.cgroup.devices.allow = c 1:5 rwm +# consoles +lxc.cgroup.devices.allow = c 5:1 rwm +lxc.cgroup.devices.allow = c 5:0 rwm +lxc.cgroup.devices.allow = c 4:0 rwm +lxc.cgroup.devices.allow = c 4:1 rwm +# /dev/{,u}random +lxc.cgroup.devices.allow = c 1:9 rwm +lxc.cgroup.devices.allow = c 1:8 rwm +lxc.cgroup.devices.allow = c 136:* rwm +lxc.cgroup.devices.allow = c 5:2 rwm +# rtc +lxc.cgroup.devices.allow = c 254:0 rwm diff --git a/tests/lxcconf2xmldata/lxcconf2xml-simple.xml b/tests/lxcconf2xmldata/lxcconf2xml-simple.xml new file mode 100644 index 0000000..a277751 --- /dev/null +++ b/tests/lxcconf2xmldata/lxcconf2xml-simple.xml @@ -0,0 +1,17 @@ +<domain type='lxc'> + <name>migrate_test</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>4035770</memory> + <currentMemory unit='KiB'>0</currentMemory> + <vcpu placement='static' current='0'>1</vcpu> + <os> + <type>exe</type> + <init>/sbin/init</init> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + </devices> +</domain> diff --git a/tests/lxcconf2xmldata/lxcconf2xml.fstab b/tests/lxcconf2xmldata/lxcconf2xml.fstab new file mode 100644 index 0000000..a08b2d6 --- /dev/null +++ b/tests/lxcconf2xmldata/lxcconf2xml.fstab @@ -0,0 +1,3 @@ +proc proc proc nodev,noexec,nosuid 0 0 +sysfs sys sysfs defaults 0 0 +tmpfs run tmpfs mode=0755,nodev,nosuid 0 0 diff --git a/tests/lxcconf2xmltest.c b/tests/lxcconf2xmltest.c new file mode 100644 index 0000000..6f0f97e --- /dev/null +++ b/tests/lxcconf2xmltest.c @@ -0,0 +1,124 @@ +#include <config.h> + +#include "testutils.h" + +#ifdef WITH_LXC + +# include "lxc/lxc_native.h" + +# define VIR_FROM_THIS VIR_FROM_NONE + +# define LXC_TEST_MEMORY_AMOUNT 4035770ul + +static int +blankProblemElements(char *data) +{ + if (virtTestClearLineRegex("<uuid>([[:alnum:]]|-)+</uuid>", data) < 0) + return -1; + return 0; +} + +static int +testCompareXMLToConfigFiles(const char *xml, + const char *configfile, + const char *fstabfile, + const char *defaultFstab) +{ + int ret = -1; + char *config = NULL; + char *fstab = NULL; + char *expectxml = NULL; + char *actualxml = NULL; + virDomainDefPtr vmdef = NULL; + + if (virtTestLoadFile(configfile, &config) < 0) + goto fail; + if (virtTestLoadFile(fstabfile, &fstab) < 0 && + virtTestLoadFile(defaultFstab, &fstab) < 0) + goto fail; + if (virtTestLoadFile(xml, &expectxml) < 0) + goto fail; + + if (!(vmdef = lxcParseConfigString(config, fstab, LXC_TEST_MEMORY_AMOUNT))) + goto fail; + + if (!(actualxml = virDomainDefFormat(vmdef, 0))) + goto fail; + + if (blankProblemElements(expectxml) < 0 || + blankProblemElements(actualxml) < 0) + goto fail; + + if (STRNEQ(expectxml, actualxml)) { + virtTestDifference(stderr, expectxml, actualxml); + goto fail; + } + + ret = 0; + +fail: + VIR_FREE(expectxml); + VIR_FREE(actualxml); + VIR_FREE(config); + VIR_FREE(fstab); + virDomainDefFree(vmdef); + return ret; +} + +static int +testCompareXMLToConfigHelper(const void *data) +{ + int result = -1; + const char *name = data; + char *xml = NULL; + char *config = NULL; + char *fstab = NULL; + char *defaultFstab = NULL; + + if (virAsprintf(&xml, "%s/lxcconf2xmldata/lxcconf2xml-%s.xml", + abs_srcdir, name) < 0 || + virAsprintf(&config, "%s/lxcconf2xmldata/lxcconf2xml-%s.config", + abs_srcdir, name) < 0 || + virAsprintf(&fstab, "%s/lxcconf2xmldata/lxcconf2xml-%s.fstab", + abs_srcdir, name) < 0 || + virAsprintf(&defaultFstab, "%s/lxcconf2xmldata/lxcconf2xml.fstab", + abs_srcdir) < 0) + goto cleanup; + + result = testCompareXMLToConfigFiles(xml, config, fstab, defaultFstab); + +cleanup: + VIR_FREE(xml); + VIR_FREE(config); + VIR_FREE(fstab); + VIR_FREE(defaultFstab); + return result; +} + +static int +mymain(void) +{ + int ret = EXIT_SUCCESS; + +# define DO_TEST(name) \ + if (virtTestRun("LXC Native-2-XML " name, \ + testCompareXMLToConfigHelper, \ + name) < 0) \ + ret = EXIT_FAILURE + + DO_TEST("simple"); + + return ret; +} + +VIRT_TEST_MAIN(mymain) + +#else + +int +main(void) +{ + return EXIT_AM_SKIP; +} + +#endif /* WITH_LXC */ -- 1.8.5.2 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list