Commit 840d2afe by Christian Brauner

Refactor lxc-clone

- This commit adapts lxc-clone to be similiar in usage and feel to the other lxc-* executables. It builds on the previous extension of the lxc_argument struct and now uses the default lxc_arguments_parse() function. - Options which were not used have been removed. - The LXC_CLONE_KEEPNAME flag was not respected in the previous version of lxc-clone. The culprit is a missing if-condition in lxccontainer.c. As this requires a change in one of the API functions in lxccontainer.c it will be addressed in a follow-up commit. Signed-off-by: 's avatarChristian Brauner <christianvanbrauner@gmail.com>
parent 5d5da49b
...@@ -27,189 +27,228 @@ ...@@ -27,189 +27,228 @@
#include <stdlib.h> #include <stdlib.h>
#include <errno.h> #include <errno.h>
#include <ctype.h> #include <ctype.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <lxc/lxccontainer.h> #include <lxc/lxccontainer.h>
#include "log.h" #include "log.h"
#include "config.h" #include "confile.h"
#include "arguments.h"
#include "lxc.h" #include "lxc.h"
#include "conf.h" #include "conf.h"
#include "state.h" #include "state.h"
lxc_log_define(lxc_clone_ui, lxc); lxc_log_define(lxc_clone_ui, lxc);
/* we pass fssize in bytes */ static int my_parser(struct lxc_arguments *args, int c, char *arg);
static uint64_t get_fssize(char *s)
{
uint64_t ret;
char *end;
ret = strtoull(s, &end, 0);
if (end == s)
{
fprintf(stderr, "Invalid blockdev size '%s', using default size\n", s);
return 0;
}
while (isblank(*end))
end++;
if (*end == '\0')
ret *= 1024ULL * 1024ULL; // MB by default
else if (*end == 'b' || *end == 'B')
ret *= 1ULL;
else if (*end == 'k' || *end == 'K')
ret *= 1024ULL;
else if (*end == 'm' || *end == 'M')
ret *= 1024ULL * 1024ULL;
else if (*end == 'g' || *end == 'G')
ret *= 1024ULL * 1024ULL * 1024ULL;
else if (*end == 't' || *end == 'T')
ret *= 1024ULL * 1024ULL * 1024ULL * 1024ULL;
else
{
fprintf(stderr, "Invalid blockdev unit size '%c' in '%s', using default size\n", *end, s);
return 0;
}
return ret;
}
static void usage(const char *me)
{
printf("Usage: %s [-s] [-B backingstore] [-L size[unit]] [-K] [-M] [-H]\n", me);
printf(" [-p lxcpath] [-P newlxcpath] orig new\n");
printf("\n");
printf(" -s: snapshot rather than copy\n");
printf(" -B: use specified new backingstore. Default is the same as\n");
printf(" the original. Options include aufs, btrfs, lvm, overlayfs, \n");
printf(" dir and loop\n");
printf(" -L: for blockdev-backed backingstore, use specified size * specified\n");
printf(" unit. Default size is the size of the source blockdev, default\n");
printf(" unit is MB\n");
printf(" -K: Keep name - do not change the container name\n");
printf(" -M: Keep macaddr - do not choose a random new mac address\n");
printf(" -p: use container orig from custom lxcpath\n");
printf(" -P: create container new in custom lxcpath\n");
printf(" -R: rename existing container\n");
exit(1);
}
static struct option options[] = { static const struct option my_longopts[] = {
{ "newname", required_argument, 0, 'N'},
{ "newpath", required_argument, 0, 'p'},
{ "rename", no_argument, 0, 'R'},
{ "snapshot", no_argument, 0, 's'}, { "snapshot", no_argument, 0, 's'},
{ "backingstore", required_argument, 0, 'B'}, { "backingstore", required_argument, 0, 'B'},
{ "size", required_argument, 0, 'L'}, { "fssize", required_argument, 0, 'L'},
{ "orig", required_argument, 0, 'o'},
{ "new", required_argument, 0, 'n'},
{ "vgname", required_argument, 0, 'v'},
{ "rename", no_argument, 0, 'R'},
{ "keepname", no_argument, 0, 'K'}, { "keepname", no_argument, 0, 'K'},
{ "keepmac", no_argument, 0, 'M'}, { "keepmac", no_argument, 0, 'M'},
{ "lxcpath", required_argument, 0, 'p'}, LXC_COMMON_OPTIONS
{ "newpath", required_argument, 0, 'P'},
{ "fstype", required_argument, 0, 't'},
{ "help", no_argument, 0, 'h'},
{ 0, 0, 0, 0 },
}; };
static struct lxc_arguments my_args = {
.progname = "lxc-clone",
.help = "\
--name=NAME [-P lxcpath] -N newname [-p newpath] [-B backingstorage] [-s] [-K] [-M] [-L size [unit]]\n\
\n\
lxc-lcone clone a container\n\
\n\
Options :\n\
-n, --name=NAME NAME of the container\n\
-N, --newname=NEWNAME NEWNAME for the restored container\n\
-p, --newpath=NEWPATH NEWPATH for the container to be stored\n\
-R, --rename rename container\n\
-s, --snapshot create snapshot instead of clone\n\
-B, --backingstorage=TYPE backingstorage type for the container\n\
-L, --fssize size of the new block device for block device containers\n\
-K, --keepname keep the hostname of the original container\n\
-M, --keepmac keep the MAC address of the original container\n",
.options = my_longopts,
.parser = my_parser,
.checker = NULL,
};
static int do_clone(struct lxc_container *c, char *newname, char *newpath,
int flags, char *bdevtype, uint64_t fssize, char **args);
static int do_clone_rename(struct lxc_container *c, char *newname);
static uint64_t get_fssize(char *s);
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
struct lxc_container *c1 = NULL, *c2 = NULL; struct lxc_container *c;
int snapshot = 0, keepname = 0, keepmac = 0, rename = 0; int flags = 0;
int flags = 0, option_index; int ret;
uint64_t newsize = 0;
char *bdevtype = NULL, *lxcpath = NULL, *newpath = NULL, *fstype = NULL;
char *orig = NULL, *new = NULL, *vgname = NULL;
char **args = NULL;
int c;
bool ret;
if (argc < 3) if (lxc_arguments_parse(&my_args, argc, argv))
usage(argv[0]); exit(EXIT_FAILURE);
while (1) { if (!my_args.log_file)
c = getopt_long(argc, argv, "sB:L:o:n:v:KMHp:P:Rt:h", options, &option_index); my_args.log_file = "none";
if (c == -1)
break;
switch (c) {
case 's': snapshot = 1; break;
case 'B': bdevtype = optarg; break;
case 'L': newsize = get_fssize(optarg); break;
case 'o': orig = optarg; break;
case 'n': new = optarg; break;
case 'v': vgname = optarg; break;
case 'K': keepname = 1; break;
case 'M': keepmac = 1; break;
case 'p': lxcpath = optarg; break;
case 'P': newpath = optarg; break;
case 'R': rename = 1; break;
case 't': fstype = optarg; break;
case 'h': usage(argv[0]);
default: break;
}
}
if (optind < argc && !orig)
orig = argv[optind++];
if (optind < argc && !new)
new = argv[optind++];
if (optind < argc)
/* arguments for the clone hook */
args = &argv[optind];
if (!new || !orig) {
printf("Error: you must provide orig and new names\n");
usage(argv[0]);
}
if (snapshot) flags |= LXC_CLONE_SNAPSHOT; if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
if (keepname) flags |= LXC_CLONE_KEEPNAME; my_args.progname, my_args.quiet, my_args.lxcpath[0]))
if (keepmac) flags |= LXC_CLONE_KEEPMACADDR; exit(EXIT_FAILURE);
lxc_log_options_no_override();
// vgname and fstype could be supported by sending them through the if (geteuid()) {
// bdevdata. However, they currently are not yet. I'm not convinced if (access(my_args.lxcpath[0], O_RDWR) < 0) {
// they are worthwhile. fprintf(stderr, "You lack access to %s\n",
if (vgname) { my_args.lxcpath[0]);
printf("Error: vgname not supported\n"); exit(EXIT_FAILURE);
usage(argv[0]); }
} }
if (fstype) {
printf("Error: fstype not supported\n"); if (!my_args.newname) {
usage(argv[0]); printf("Error: You must provide a NEWNAME for the clone.\n");
exit(EXIT_FAILURE);
} }
c1 = lxc_container_new(orig, lxcpath); if (my_args.task == SNAP)
if (!c1) flags |= LXC_CLONE_SNAPSHOT;
if (my_args.keepname)
flags |= LXC_CLONE_KEEPNAME;
if (my_args.keepmac)
flags |= LXC_CLONE_KEEPMACADDR;
c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
if (!c)
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
if (!c1->may_control(c1)) { if (!c->may_control(c)) {
fprintf(stderr, "Insufficent privileges to control %s\n", orig); fprintf(stderr, "Insufficent privileges to control %s\n",
lxc_container_put(c1); c->name);
lxc_container_put(c);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (!c1->is_defined(c1)) { if (!c->is_defined(c)) {
fprintf(stderr, "Error: container %s is not defined\n", orig); fprintf(stderr, "Error: container %s is not defined\n",
lxc_container_put(c1); c->name);
lxc_container_put(c);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (rename) {
ret = c1->rename(c1, new); if (my_args.task == RENAME) {
if (!ret) { ret = do_clone_rename(c, my_args.newname);
fprintf(stderr,
"Error: Renaming container %s to %s failed\n",
c1->name, new);
lxc_container_put(c1);
exit(EXIT_FAILURE);
}
} else { } else {
c2 = c1->clone(c1, new, newpath, flags, bdevtype, NULL, newsize, ret = do_clone(c, my_args.newname, my_args.newpath, flags,
args); my_args.bdevtype, my_args.fssize, &argv[optind]);
if (c2 == NULL) {
lxc_container_put(c1);
fprintf(stderr, "clone failed\n");
exit(EXIT_FAILURE);
}
printf("Created container %s as %s of %s\n", new,
snapshot ? "snapshot" : "copy", orig);
lxc_container_put(c2);
} }
lxc_container_put(c1);
exit(EXIT_SUCCESS); lxc_container_put(c);
if (ret == 0)
exit(EXIT_SUCCESS);
exit(EXIT_FAILURE);
} }
static int my_parser(struct lxc_arguments *args, int c, char *arg)
{
switch (c) {
case 'N':
args->newname = arg;
break;
case 'p':
args->newpath = arg;
break;
case 'R':
args->task = RENAME;
break;
case 's':
args->task = SNAP;
break;
case 'B':
args->bdevtype = arg;
break;
case 'L':
args->fssize = get_fssize(optarg);
break;
case 'K':
args->keepname = 1;
break;
case 'M':
args->keepmac = 1;
break;
}
return 0;
}
static int do_clone_rename(struct lxc_container *c, char *newname)
{
bool ret;
ret = c->rename(c, newname);
if (!ret) {
ERROR("Error: Renaming container %s to %s failed\n", c->name,
my_args.newname);
return -1;
}
INFO("Renamed container %s to %s\n", c->name, newname);
return 0;
}
static int do_clone(struct lxc_container *c, char *newname, char *newpath,
int flags, char *bdevtype, uint64_t fssize, char **args)
{
struct lxc_container *clone;
clone = c->clone(c, newname, newpath, flags, bdevtype, NULL, fssize,
args);
if (clone == NULL) {
fprintf(stderr, "clone failed\n");
return -1;
}
INFO("Created container %s as %s of %s\n", newname,
my_args.task ? "snapshot" : "copy", c->name);
lxc_container_put(clone);
return 0;
}
/* we pass fssize in bytes */
static uint64_t get_fssize(char *s)
{
uint64_t ret;
char *end;
ret = strtoull(s, &end, 0);
if (end == s) {
fprintf(stderr, "Invalid blockdev size '%s', using default size\n", s);
return 0;
}
while (isblank(*end))
end++;
if (*end == '\0') {
ret *= 1024ULL * 1024ULL; // MB by default
} else if (*end == 'b' || *end == 'B') {
ret *= 1ULL;
} else if (*end == 'k' || *end == 'K') {
ret *= 1024ULL;
} else if (*end == 'm' || *end == 'M') {
ret *= 1024ULL * 1024ULL;
} else if (*end == 'g' || *end == 'G') {
ret *= 1024ULL * 1024ULL * 1024ULL;
} else if (*end == 't' || *end == 'T') {
ret *= 1024ULL * 1024ULL * 1024ULL * 1024ULL;
} else {
fprintf(stderr, "Invalid blockdev unit size '%c' in '%s', " "using default size\n", *end, s);
return 0;
}
return ret;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment