[libvirt] [PATCH 01/16] LXC driver: started implementing connectDomainXMLFromNative

Cédric Bosdonnat cbosdonnat at suse.com
Tue Jan 14 13:49:50 UTC 2014


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 at 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 at 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




More information about the libvir-list mailing list