On Tue, 2017-06-27 at 16:07 -0400, John Ferlan wrote:
On 06/27/2017 01:02 PM, Venkat Datta N H wrote:
> Docker Memory and VCPU configuration is converted to fit for LXC container XML
configuration
> ---
> po/POTFILES.in | 1 +
> src/Makefile.am | 1 +
> src/lxc/lxc_docker.c | 116 ++++++++++++++++
> src/lxc/lxc_docker.h | 32 +++++
> src/lxc/lxc_driver.c | 13 +-
> src/lxc/lxc_native.h | 1 +
> tests/Makefile.am | 8 +-
> .../lxcdocker2xmldata-simple.json | 36 +++++
> .../lxcdocker2xmldata/lxcdocker2xmldata-simple.xml | 15 +++
> tests/lxcdocker2xmltest.c | 149 +++++++++++++++++++++
> 10 files changed, 366 insertions(+), 6 deletions(-)
> create mode 100644 src/lxc/lxc_docker.c
> create mode 100644 src/lxc/lxc_docker.h
> create mode 100644 tests/lxcdocker2xmldata/lxcdocker2xmldata-simple.json
> create mode 100644 tests/lxcdocker2xmldata/lxcdocker2xmldata-simple.xml
> create mode 100644 tests/lxcdocker2xmltest.c
>
Should the drvlxc.html.in page be modified too?
http://libvirt.org/drvlxc.html
To include something that indicates docker config files being read and
what fields are taken/used.. IOW how things are mapped.
> diff --git a/po/POTFILES.in b/po/POTFILES.in
> index 275df1f..421b32e 100644
> --- a/po/POTFILES.in
> +++ b/po/POTFILES.in
> @@ -111,6 +111,7 @@ src/lxc/lxc_driver.c
> src/lxc/lxc_fuse.c
> src/lxc/lxc_hostdev.c
> src/lxc/lxc_native.c
> +src/lxc/lxc_docker.c
> src/lxc/lxc_process.c
> src/network/bridge_driver.c
> src/network/bridge_driver_linux.c
> diff --git a/src/Makefile.am b/src/Makefile.am
> index eae32dc..1341e5a 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -839,6 +839,7 @@ LXC_DRIVER_SOURCES = \
> 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_docker.c lxc/lxc_docker.h \
> lxc/lxc_driver.c lxc/lxc_driver.h
>
> LXC_CONTROLLER_SOURCES = \
> diff --git a/src/lxc/lxc_docker.c b/src/lxc/lxc_docker.c
> new file mode 100644
> index 0000000..dbb2a81
> --- /dev/null
> +++ b/src/lxc/lxc_docker.c
> @@ -0,0 +1,116 @@
> +/*
> + * lxc_docker.c: LXC native docker configuration import
> + *
> + * Copyright (C) 2017 Venkat Datta N H
> + *
> + * 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: Venkat Datta N H <nhvenkatdatta(a)gmail.com>
> + */
> +
> +#include <config.h>
> +
> +#include "util/viralloc.h"
> +#include "util/virfile.h"
> +#include "util/virstring.h"
> +#include "util/virconf.h"
> +#include "util/virjson.h"
> +#include "util/virutil.h"
> +#include "virerror.h"
> +#include "virlog.h"
> +#include "conf/domain_conf.h"
> +#include "lxc_docker.h"
> +#include "secret_conf.h"
> +
> +#define VIR_FROM_THIS VIR_FROM_LXC
> +
> +VIR_LOG_INIT("lxc.lxc_docker");
> +
> +static int virLXCDockerParseCpu(virDomainDefPtr dom,
> + virDomainXMLOptionPtr xmlopt,
> + virJSONValuePtr prop)
Stylistically, these should be:
static int
virLXCDockerParseCpu(virDomainDefPtr dom,
virDomainXMLOptionPtr xmlopt,
virJSONValuePtr prop)
note the multiple lines for function and how the arguments are aligned.
Similar point in every definition here.
And yes, I know/see that lxc_driver.c doesn't follow that model - this
is the newer style we like to see in general.
> +{
> + int vcpus;
> +
> + if (virJSONValueObjectGetNumberInt(prop, "CpuShares", &vcpus) !=
0)
s/ !=/</ e.g. ) < 0)"
> + return -1;
Why CpuShares and not CpuCount? What about CpusetCpus?
After some more digging around that, maxvcpus value isn't used in the lxc driver.
It needs to be set to get the XML parser code happy. In the lxc configuration
conversion code (lxc_native.c) I just set it to 1: a number of vcpus doesn't
really make sense on a container.
The cpu tune properties however should be handled in another commit.
--
Cedric
> +
> + if (virDomainDefSetVcpusMax(dom, vcpus, xmlopt) < 0)
> + return -1;
> +
> + if (virDomainDefSetVcpus(dom, vcpus) < 0)
> + return -1;
This would seem to be more related to CpusetCpus from my quick read.
> +
> + return 0;
> +}
> +
Two blank lines between functions
> +static int virLXCDockerParseMem(virDomainDefPtr dom,
> + virJSONValuePtr prop)
> +{
> + unsigned long long mem;
> +
> + if (virJSONValueObjectGetNumberUlong(prop, "Memory", &mem) != 0)
s/!=/</
> + return -1;
> +
> + virDomainDefSetMemoryTotal(dom, mem / 1024);
> + dom->mem.cur_balloon = mem / 1024;
> +
Does ShmSize have any effect? (I'm not sure what it is, so I ask)
Docker mounts a tmpfs as /dev/shm and allows changing the size of this mount. This
is what ShmSize does. It seems that we don't do anything similar at all and would
just ignore ShmSize.
See
https://github.com/moby/moby/pull/4981 for reference
> + return 0;
> +}
> +
> +virDomainDefPtr virLXCDockerParseJSONConfig(virCapsPtr caps ATTRIBUTE_UNUSED,
Why go w/ UNUSED?
For comparison, the lxcParseConfigString will pass @caps to
virDomainDefPostParse after it builds it's @vmdef - there's some
checking that goes in that function that you'd be possibly missing.
> + virDomainXMLOptionPtr xmlopt,
> + const char *config)
> +{
> + virJSONValuePtr json_obj;
> + virJSONValuePtr host_config;
> +
> + if (!(json_obj = virJSONValueFromString(config)))
> + return NULL;
> +
> + virDomainDefPtr def;
Typically we keep definitions together without intervening lines of code.
> +
> + if (!(def = virDomainDefNew()))
> + goto error;> +
Curious why no virUUIDGenerate ? Domains typically have two keys UUID
and Name - although I haven't dug through the details of the LXC driver
to see what would happen if you had a non docker type container with a
conflicting name, but no UUID supplied.
This code doesn't even set a name? So no UUID and no Name, probably
means no domain to be defined.
> + def->id = -1;
> + def->mem.cur_balloon = 64*1024;
> + virDomainDefSetMemoryTotal(def, def->mem.cur_balloon);
You're setting some default and total, then possibly changing?
> +
> + if ((host_config = virJSONValueObjectGetObject(json_obj,
"HostConfig")) != NULL) {
The != NULL is really not necessary
> + if (virLXCDockerParseCpu(def, xmlopt, host_config) < 0) {
> + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("failed
to parse VCpu"));
Long line - 80 cols please.
> + goto error;
> + }
> +
> + if (virLXCDockerParseMem(def, host_config) < 0) {
> + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("failed
to parse Memory"));
Long line again.
> + goto error;
> + }
> + }
> +
> + def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_UTC;
> + def->onReboot = VIR_DOMAIN_LIFECYCLE_RESTART;
> + def->onCrash = VIR_DOMAIN_LIFECYCLE_CRASH_DESTROY;
> + def->onPoweroff = VIR_DOMAIN_LIFECYCLE_DESTROY;
> + def->virtType = VIR_DOMAIN_VIRT_LXC;
> + def->os.type = VIR_DOMAIN_OSTYPE_EXE;
> +
Setting the os.type, but there's nothing to run?
That is handled by the other commit, but those two can be merged
together to have a basic working conversion.
[1] e.g. here for virDomainDefPostParse
What do you mean exactly here?
> + return def;
> +
> + error:
@json_obj is leaked (need a virJSONValueFree) here.
> + virDomainDefFree(def);
> + return NULL;
> +}
> diff --git a/src/lxc/lxc_docker.h b/src/lxc/lxc_docker.h
> new file mode 100644
> index 0000000..f081e07
> --- /dev/null
> +++ b/src/lxc/lxc_docker.h
> @@ -0,0 +1,32 @@
> +/*
> + * lxc_docker.h: Header file for LXC native docker configuration
> + *
> + * Copyright (C) 2017 Venkat Datta N H
> + *
> + * 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: Venkat Datta N H <nhvenkatdatta(a)gmail.com>
> + */
> +
> +#ifndef __LXC_DOCKER_H__
> +# define __LXC_DOCKER_H__
> +
> +# include "domain_conf.h"
> +
> +virDomainDefPtr virLXCDockerParseJSONConfig(virCapsPtr caps,
> + virDomainXMLOptionPtr xmlopt,
> + const char *config);
Similarly more recent style has been just to copy the .c function and
add the ; ... makes things easier.
> +
> +#endif /* __LXC_NATIVE_DOCKER_H__ */
This comment doesn't matches the #ifndef above
> diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c
> index 22c8b58..711bb16 100644
> --- a/src/lxc/lxc_driver.c
> +++ b/src/lxc/lxc_driver.c
> @@ -53,6 +53,7 @@
> #include "lxc_driver.h"
> #include "lxc_native.h"
> #include "lxc_process.h"
> +#include "lxc_docker.h"
> #include "viralloc.h"
> #include "virnetdevbridge.h"
> #include "virnetdevveth.h"
> @@ -1062,15 +1063,17 @@ static char *lxcConnectDomainXMLFromNative(virConnectPtr
conn,
> if (virConnectDomainXMLFromNativeEnsureACL(conn) < 0)
> goto cleanup;
>
> - if (STRNEQ(nativeFormat, LXC_CONFIG_FORMAT)) {
> + if (STREQ(nativeFormat, DOCKER_CONFIG_FORMAT)) {
> + if (!(def = virLXCDockerParseJSONConfig(caps, driver->xmlopt,
nativeConfig)))
This ends up being a somewhat long line... The to stay within 80 char
columns.
You could also make the argument order the same as lxcParseConfigString
- although it's not a requirement.
> + goto cleanup;
> + } else if (STREQ(nativeFormat, LXC_CONFIG_FORMAT)) {
> + if (!(def = lxcParseConfigString(nativeConfig, caps, driver->xmlopt)))
> + goto cleanup;
> + } else {
> virReportError(VIR_ERR_INVALID_ARG,
> _("unsupported config type %s"), nativeFormat);
> goto cleanup;
> }
> -
> - if (!(def = lxcParseConfigString(nativeConfig, caps, driver->xmlopt)))
> - goto cleanup;
> -
> xml = virDomainDefFormat(def, caps, 0);
>
> cleanup:
> diff --git a/src/lxc/lxc_native.h b/src/lxc/lxc_native.h
> index 15fa0d5..88263ae 100644
> --- a/src/lxc/lxc_native.h
> +++ b/src/lxc/lxc_native.h
> @@ -26,6 +26,7 @@
> # include "domain_conf.h"
>
> # define LXC_CONFIG_FORMAT "lxc-tools"
> +# define DOCKER_CONFIG_FORMAT "docker"
>
> virDomainDefPtr lxcParseConfigString(const char *config,
> virCapsPtr caps,
> diff --git a/tests/Makefile.am b/tests/Makefile.am
> index 19986dc..0948bfa 100644
> --- a/tests/Makefile.am
> +++ b/tests/Makefile.am
> @@ -93,6 +93,7 @@ EXTRA_DIST = \
> capabilityschemadata \
> commanddata \
> cputestdata \
> + lxcdocker2xmldata \
> domaincapsschemadata \
> domainconfdata \
> domainschemadata \
> @@ -295,7 +296,7 @@ test_libraries += libqemumonitortestutils.la \
> endif WITH_QEMU
>
> if WITH_LXC
> -test_programs += lxcxml2xmltest lxcconf2xmltest
> +test_programs += lxcxml2xmltest lxcconf2xmltest lxcdocker2xmltest
> endif WITH_LXC
>
> if WITH_OPENVZ
> @@ -693,6 +694,11 @@ lxcconf2xmltest_SOURCES = \
> lxcconf2xmltest.c testutilslxc.c testutilslxc.h \
> testutils.c testutils.h
> lxcconf2xmltest_LDADD = $(lxc_LDADDS)
> +
> +lxcdocker2xmltest_SOURCES = \
> + lxcdocker2xmltest.c testutilslxc.c testutilslxc.h \
> + testutils.c testutils.h
> +lxcdocker2xmltest_LDADD = $(lxc_LDADDS)
> else ! WITH_LXC
> EXTRA_DIST += lxcxml2xmltest.c testutilslxc.c testutilslxc.h
> endif ! WITH_LXC
> diff --git a/tests/lxcdocker2xmldata/lxcdocker2xmldata-simple.json
b/tests/lxcdocker2xmldata/lxcdocker2xmldata-
> simple.json
> new file mode 100644
> index 0000000..63470be
> --- /dev/null
> +++ b/tests/lxcdocker2xmldata/lxcdocker2xmldata-simple.json
> @@ -0,0 +1,36 @@
> +{
> + "Id":
"dbb1ae21dac15973d66e6c2b8516d270b32ca766e0cf7551d8b7973513e5f079",
> + "Created": "2017-05-25T18:55:17.922934825Z",
> + "Path": "/bin/bash",
> + "Args": [],
> + "HostConfig": {
> + "Binds": null,
> + "ContainerIDFile": "",
> + "LogConfig": {
> + "Type": "json-file",
> + "Config": {}
> + },
> + "NetworkMode": "default",
> + "PortBindings": {},
> + "ShmSize": 67108864,
> + "Runtime": "runc",
> + "Isolation": "",
> + "CpuShares": 2,
> + "Memory": 1073741824,
> + "CgroupParent": "",
> + "CpuPeriod": 0,
> + "CpuQuota": 0,
> + "CpusetCpus": "",
> + "CpusetMems": "",
> + "KernelMemory": 0,
> + "MemoryReservation": 0,
> + "MemorySwap": -1,
> + "MemorySwappiness": -1,
> + "PidsLimit": 0,
> + "Ulimits": null,
> + "CpuCount": 0,
> + "CpuPercent": 0,
> + "IOMaximumIOps": 0,
> + "IOMaximumBandwidth": 0
> + }
> +}
Seems to me to be a lot more options in here than those read/handled in
virLXCDockerParseJSONConfig (which only reads "HostConfig",
"Memory",
and "CpuShares")... I'm not as familiar with LXC as QEMU, but it would
seem at the very least "Runtime" would equate to something... And
certainly Path/Args would be useful.
The goal is to work on this iteratively do avoid the huge commit providing
full support.
The only thing used is "Memory"? What is ShmSize?
for? What about
MemorySwap or MemoryReservation - is there any relationship with
existing DOMAIN_MEMORY_* variables?
Surely... Could be handled in a later commit, right?
As noted above CpuCount to me would be related to count/max of vcpus for
the container, while CpuShares would be a scheduler type parameter.
Perhaps even "CpusetCpus" would be even more in line with which vcpus
would be used (e.g. a 4 vcpu system with only 0,1 active).
The CpuPeriod and CpuQuota would seem to relate to VIR_DOMAIN_SCHEDULER*
params....
I'm not even sure there is anything mapping the vcpumax in the docker json
configuration. But the cpu tuning values for sure can be mapped.
Perhaps going through lxc_driver API's would be beneficial to see
how
many relate to existing/parsed fields.
+1
> diff --git
a/tests/lxcdocker2xmldata/lxcdocker2xmldata-simple.xml
b/tests/lxcdocker2xmldata/lxcdocker2xmldata-
> simple.xml
> new file mode 100644
> index 0000000..8be1ace
> --- /dev/null
> +++ b/tests/lxcdocker2xmldata/lxcdocker2xmldata-simple.xml
> @@ -0,0 +1,15 @@
> +<domain type='lxc'>
> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
> + <memory unit='KiB'>1048576</memory>
> + <currentMemory unit='KiB'>1048576</currentMemory>
> + <vcpu placement='static'>2</vcpu>
> + <os>
> + <type>exe</type>
> + </os>
> + <clock offset='utc'/>
> + <on_poweroff>destroy</on_poweroff>
> + <on_reboot>restart</on_reboot>
> + <on_crash>destroy</on_crash>
> + <devices>
> + </devices>
> +</domain>
If I cut this XML and try to :
virsh -c lxc:/// file.xml
I get:
error: Failed to define domain from docker.xml
error: missing name information
Indeed, we're missing the name here. Venkat, you'll need to set it if provided
in the configuration. Otherwise I'ld rather leave it empty and let the user
manually add it rather than generating some funky name like docker does.
If I edit the XML and add a name, then try again I get:
error: Failed to define domain from docker.xml
error: XML error: init binary must be specified
The question is: should we merge this commit with the next one then?
> diff --git a/tests/lxcdocker2xmltest.c b/tests/lxcdocker2xmltest.c
> new file mode 100644
> index 0000000..ccac4c4
> --- /dev/null
> +++ b/tests/lxcdocker2xmltest.c
> @@ -0,0 +1,149 @@
> +/*
> + * lxcdocker2xmltest.c: Test LXC Docker Configuration
> + *
> + * Copyright (C) 2017 Venkat Datta N H
> + *
> + * 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: Venkat Datta N H <nhvenkatdatta(a)gmail.com>
> + */
> +
> +#include <config.h>
> +
> +#include "testutils.h"
> +
> +#ifdef WITH_LXC
> +
> +# include "lxc/lxc_docker.h"
> +# include "lxc/lxc_conf.h"
> +# include "testutilslxc.h"
> +
> +# define VIR_FROM_THIS VIR_FROM_NONE
> +
> +static virCapsPtr caps;
> +static virDomainXMLOptionPtr xmlopt;
> +
> +static int testSanitizeDef(virDomainDefPtr vmdef)
This one should have multi lines...
> +{
> + /* Remove UUID randomness */
> + if (virUUIDParse("c7a5fdbd-edaf-9455-926a-d65c16db1809",
vmdef->uuid) < 0)
> + return -1;
> + return 0;
> +}
> +
Two blank lines.
> +static int
> +testCompareXMLToConfigFiles(const char *xmlfile,
> + const char *configfile,
> + bool expectError)
@expectError isn't really used and to me seems 'backwards' - you're
expecting an error?
That is a copy/paste from the lxc-native configuration test. But yes,
expectError should be removed if not used in this test.
> +{
> + int ret = -1;
> + char *config = NULL;
> + char *actualxml = NULL;
> + virDomainDefPtr vmdef = NULL;
> +
> + if (virTestLoadFile(configfile, &config) < 0)
> + goto fail;
> +
> + vmdef = virLXCDockerParseJSONConfig(caps, xmlopt, config);
> +
> + if (vmdef && expectError) {
> + if (testSanitizeDef(vmdef) < 0)
> + goto fail;
> +
> + if (!(actualxml = virDomainDefFormat(vmdef, caps, 0)))
> + goto fail;
> +
> + if (virTestCompareToFile(actualxml, xmlfile) < 0)
> + goto fail;
> + }
> +
> + ret = 0;
> +
> + fail:
> + VIR_FREE(actualxml);
> + VIR_FREE(config);
> + virDomainDefFree(vmdef);
> + return ret;
> +}
> +
> +struct testInfo {
> + const char *name;
> + bool expectError;
> +};
> +
> +static int
> +testCompareXMLToConfigHelper(const void *data)
> +{
> + int result = -1;
> + const struct testInfo *info = data;
> + char *xml = NULL;
> + char *config = NULL;
> +
> + if (virAsprintf(&xml,
"%s/lxcdocker2xmldata/lxcdocker2xmldata-%s.xml",
> + abs_srcdir, info->name) < 0 ||
> + virAsprintf(&config,
"%s/lxcdocker2xmldata/lxcdocker2xmldata-%s.json",
> + abs_srcdir, info->name) < 0)
> + goto cleanup;
> +
> + result = testCompareXMLToConfigFiles(xml, config, info->expectError);
> +
> + cleanup:
> + VIR_FREE(xml);
> + VIR_FREE(config);
> + return result;
> +}
> +
> +static int
> +mymain(void)
> +{
> + int ret = EXIT_SUCCESS;
> +
> + if (!(caps = testLXCCapsInit()))
> + return EXIT_FAILURE;
> +
> + if (!(xmlopt = lxcDomainXMLConfInit())) {
> + virObjectUnref(caps);
> + return EXIT_FAILURE;
> + }
> +
> +
> +# define DO_TEST(name, expectError) \
> + do { \
> + const struct testInfo info = { name, expectError }; \
> + if (virTestRun("DOCKER JSON-2-XML " name, \
> + testCompareXMLToConfigHelper, \
> + &info) < 0) \
> + ret = EXIT_FAILURE; \
> + } while (0)
> +
> + DO_TEST("simple", true);
Is there a way to test DO_TEST("less_simple", false)
How comes we are expecting an error here?
--
Cedric
John
> +
> + virObjectUnref(xmlopt);
> + virObjectUnref(caps);
> +
> + return ret;
> +}
> +
> +VIR_TEST_MAIN(mymain)
> +
> +#else
> +
> +int
> +main(void)
> +{
> + return EXIT_AM_SKIP;
> +}
> +
> +#endif /* WITH_LXC */
>
--
libvir-list mailing list
libvir-list(a)redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list