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(a)suse.com>
+ */
+
+#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(a)suse.com>
+ */
+
+#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