[lxc-devel] [PATCH] Porcelain for lxc commands; easy management of system-level containers.

Nigel McNie nigel at mcnie.name
Sun May 1 03:55:46 UTC 2011


This was first developed seperately from lxc itself, at
https://github.com/nigelmcnie/lxc-simple

This patch adds the porcelain script etc, but doesn't integrate with the
build process in anyway. Therefore, lxc.pl and friends won't actually be
installed with make install - yet.

Signed-off-by: Nigel McNie <nigel at mcnie.name>
---
 MAINTAINERS                       |    7 +
 src/porcelain/CREDITS             |    8 +
 src/porcelain/README.md           |   90 ++++++
 src/porcelain/TODO                |    2 +
 src/porcelain/lib/LXC/Commands.pm |  591 +++++++++++++++++++++++++++++++++++++
 src/porcelain/lxc.pl              |  335 +++++++++++++++++++++
 6 files changed, 1033 insertions(+), 0 deletions(-)
 create mode 100644 src/porcelain/CREDITS
 create mode 100644 src/porcelain/README.md
 create mode 100644 src/porcelain/TODO
 create mode 100644 src/porcelain/lib/LXC/Commands.pm
 create mode 100755 src/porcelain/lxc.pl

diff --git a/MAINTAINERS b/MAINTAINERS
index 306c07e..f2e8c40 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10,3 +10,10 @@ Mail patches to : dlezcano at fr.ibm.com
 Mailing lists   : lxc-devel at sourceforge.net, containers at lists.osdl.org
 Web page        : lxc.sourceforge.net
 GIT location    : http://lxc.git.sourceforge.net/
+
+Porcelain Maintainer
+--------------------
+
+Person          : Nigel McNie
+Mail patches to : nigel at mcnie.name
+Mailing list    : lxc-devel at sourceforge.net
diff --git a/src/porcelain/CREDITS b/src/porcelain/CREDITS
new file mode 100644
index 0000000..0485730
--- /dev/null
+++ b/src/porcelain/CREDITS
@@ -0,0 +1,8 @@
+Credits
+=======
+
+This lxc command is mostly written by Shoptime Software. Some parts are
+contributed by others, those people (and their contributions, if significant)
+are listed here.
+
+[nobody yet! who wants to be first!]
diff --git a/src/porcelain/README.md b/src/porcelain/README.md
new file mode 100644
index 0000000..8158c2e
--- /dev/null
+++ b/src/porcelain/README.md
@@ -0,0 +1,90 @@
+Getting started with lxc
+========================
+
+1. Install dependencies
+-----------------------
+
+    apt-get install lxc debootstrap libpasswd-unix-perl libfile-slurp-perl \
+                    libmoose-perl libpath-class-perl libconfig-inifiles-perl
+
+2. Set up networking
+--------------------
+
+Your containers need a way to talk to the interweb. One way is a bridge.
+
+A bridge is basically like a switch. You can plug your ``eth0`` into it, and
+also each container. Then they can all talk to each other and the outside
+world.
+
+Put something like this in ``/etc/network/interfaces``:
+
+    auto lo
+    iface lo inet loopback
+
+    iface eth0 inet manual
+
+    auto br0
+    iface br0 inet dhcp
+        bridge_ports eth0
+        bridge_stp off
+        post-up /usr/sbin/brctl setfd br0 0
+
+If you have a static lease, configure br0 with that information instead of the
+dhcp line::
+
+    iface br0 inet static
+        address [your ip]
+        netmask [your netmask]
+        broadcast [your broadcast]
+        gateway [your gateway]
+        bridge_ports eth0
+        bridge_stp off
+        post-up /usr/sbin/brctl setfd br0 0
+
+3. Put info about networking setup in a file for later use
+----------------------------------------------------------
+
+When containers are created, we need to tell them how to access the network.
+Put this in ``/etc/lxc/lxc.conf``:
+
+    lxc.network.type=veth
+    lxc.network.link=br0
+    lxc.network.flags=up
+
+4. You're ready!
+----------------
+
+    # Summary of commands
+    lxc --help
+
+    # Create a container called 'test'
+    lxc test create
+
+    # Start the container
+    lxc test start
+
+    # Enter it
+    lxc test enter
+
+    # Stop the container
+    lxc test stop
+
+    # Destroy it (you'll be asked to confirm)
+    lxc test destroy
+
+5. Starting containers automatically on boot
+--------------------------------------------
+
+On ubuntu, install the upstart jobs:
+
+    cp upstart/lxc-* /etc/init
+
+And when you create your containers, use -a to flag them as ones that should be
+automatically started on boot.
+
+If you have a container you already made that you want to start on boot:
+
+    touch /var/lib/lxc/[name]/autostart
+
+Good luck!
+ -- Nigel McNie & Martyn Smith
diff --git a/src/porcelain/TODO b/src/porcelain/TODO
new file mode 100644
index 0000000..58c07b7
--- /dev/null
+++ b/src/porcelain/TODO
@@ -0,0 +1,2 @@
+Graceful cleanup if program is ctrl-C'd while in lxc-create
+lxc [name] enter using lxc-attach (relies on lxc-attach being implemented upstream)
diff --git a/src/porcelain/lib/LXC/Commands.pm b/src/porcelain/lib/LXC/Commands.pm
new file mode 100644
index 0000000..e9b8b09
--- /dev/null
+++ b/src/porcelain/lib/LXC/Commands.pm
@@ -0,0 +1,591 @@
+# lxc - Wrapper around lxc utils to make managing containers easier
+# Copyright © 2011 Shoptime Software
+#
+# This package 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 of the License, or (at your option) any later version.
+#
+# This package 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 package; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+
+package LXC::Commands;
+use Moose;
+
+use File::Slurp;
+use Passwd::Unix;
+use Sysadm::Install qw(tap);
+use Time::HiRes qw(usleep);
+
+=head1 NAME
+
+LXC::Commands - commands for LXC
+
+=head1 DESCRIPTION
+
+Contains routines that back the LXC commands. Normally, you would access these
+through the 'lxc' script, but you can include this module and use them directly
+if you wish.
+
+=head1 METHODS
+
+=cut
+
+
+has lxc_dir => (
+    is => 'rw',
+    isa => 'Path::Class::Dir',
+    required => 1,
+);
+
+
+=head2 create
+
+Creates a new container.
+
+Takes a hash with the following keys:
+
+=over 4
+
+=item name
+
+The name of the container to create.
+
+=item autostart
+
+Whether the container should be flagged to be automatically started on boot.
+
+=item install_user
+
+If true, bind mounts /home into the container. Also, if this script was invoked
+by C<sudo>, it creates an account and group in the container for that user,
+using the same details as on the host (e.g. same password).
+
+=item mirror
+
+The mirror to use to download packages.
+
+=item start
+
+Whether to start the container once created.
+
+=item template
+
+The template to use to create the container (see C<lxc-create>'s C<--template>
+option).
+
+=back
+
+=cut
+
+sub create {
+    my ($self, %args) = @_;
+    my $name = $args{name} || die "Must specify a name for the container to be created\n";
+
+    system('lxc-create',
+        '-n', $name,                    # TODO: check for invalid name first?
+        '-f', '/etc/lxc/lxc.conf',      # TODO: this is for networking stuff
+        '-t', $args{template} // 'ubuntu',
+    ) == 0
+        or die "lxc-create failed with exit code $?\n";
+
+    my $container_cfgroot = $self->lxc_dir->subdir($name);
+    my $container_root    = $self->lxc_dir->subdir($name . '/rootfs/');
+
+    # Dump autostart file down if asked for
+    write_file($container_cfgroot->file('autostart')->stringify, '') if $args{autostart};
+
+    # Install our own /etc/network/interfaces
+    my $interfaces_content = q(
+auto lo
+iface lo inet loopback
+
+auto eth0
+iface eth0 inet dhcp
+    # NOTE: if you're making your own interface definition, leave this line in
+    # place so that 'lxc [name] enter' will work
+    up ip a s dev eth0 | grep 'inet\W' | awk '{print $2}' | cut -f 1 -d '/' > /lxc-ip
+    down rm /lxc-ip
+);
+    write_file($container_root->file('etc/network/interfaces')->stringify, $interfaces_content);
+
+    # Bindmount homedir and install user account if asked for
+    if ( $args{install_user} ) {
+        # TODO naturally, we could grab this information from a config file
+        if ( exists $ENV{SUDO_USER} ) {
+            my $user  = $ENV{SUDO_USER};
+            my $group = $ENV{SUDO_USER};
+
+            mkdir "$container_root/home/$user";
+            append_file($container_cfgroot->file('fstab')->stringify,
+                sprintf("/home/$user           %s         auto bind 0 0\n", "$container_root/home/$user"));
+
+            my $hostpw = Passwd::Unix->new(
+                passwd => '/etc/passwd',
+                shadow => '/etc/shadow',
+                group  => '/etc/group',
+                backup => 0,
+            );
+            my @userinfo  = $hostpw->user($user);
+            my @groupinfo = $hostpw->group($group);
+
+            my $containerpw = Passwd::Unix->new(
+                passwd => $container_root->file('etc/passwd'),
+                shadow => $container_root->file('etc/shadow'),
+                group  => $container_root->file('etc/group'),
+                backup => 0,
+            );
+            $containerpw->user($user, @userinfo);
+            $containerpw->group($group, @groupinfo);
+        }
+        else {
+            print "Could not establish what user to install, skipping\n";
+        }
+    }
+
+    if ( $args{mirror} ) {
+        my $mirror = $args{mirror};
+        my $apt_sources_file = $container_root->file('etc/apt/sources.list')->stringify;
+
+        my $contents = read_file($apt_sources_file);
+        $contents =~ s/archive.ubuntu.com/$mirror/g;
+        write_file($apt_sources_file, $contents);
+
+        system('chroot', $container_root, 'apt-get', 'update');
+    }
+
+    system('chroot', $container_root, 'apt-get', 'install', '-y', '--force-yes', 'gpgv');
+    system('chroot', $container_root, 'apt-get', 'update');
+
+    $self->start(name => $name) if $args{start};
+}
+
+
+=head2 destroy
+
+Destroys a container, stopping it first if necessary.
+
+Takes a hash with the following keys:
+
+=over 4
+
+=item name
+
+The name of the container to destroy.
+
+=back
+
+=cut
+
+sub destroy {
+    my ($self, %args) = @_;
+    my $name = $args{name} || die "Must specify what container to destroy\n";
+    $self->check_valid_container($name);
+
+    if ( $self->status(name => $name, brief => 1) eq 'running' ) {
+        $self->stop(name => $name);
+    }
+
+    print "Destroying $name... ";
+    system('lxc-destroy',
+        '-n', $name,
+    );
+    print "done\n";
+}
+
+
+=head2 start
+
+Starts a container.
+
+Takes a hash with the following keys:
+
+=over 4
+
+=item name
+
+The name of the container to start.
+
+=back
+
+=cut
+
+sub start {
+    my ($self, %args) = @_;
+    my $name = $args{name} || die "Must specify what container to start\n";
+    $self->check_valid_container($name);
+
+    die "Container '$name' IS started\n" if $self->status(name => $name, brief => 1) eq 'running';
+
+    print "Starting $name... ";
+    system('lxc-start',
+        '-n', $name,
+        '-d',
+    );
+    system('lxc-wait',
+        '-n', $name,
+        '-s', 'RUNNING',
+    );
+
+    # NOTE: this will go away once Martyn works out a smarter way of handling
+    # container networking
+    for (1..100) {
+        last if -f $self->lxc_dir->file("$name/rootfs/lxc-ip");
+        if ( $_ == 100 ) {
+            print "Could not confirm container started, check with 'lxc $name enter'\n";
+            return;
+        }
+        usleep 100_000;
+    }
+
+    print "done\n";
+}
+
+
+=head2 stop
+
+Gracefully stops a container.
+
+This runs 'halt' in the container and then waits until all processes have
+exited, or some time has passed. If processes are still around after that time,
+it murders them.
+
+Takes a hash with the following keys:
+
+=over 4
+
+=item name
+
+The name of the container to stop.
+
+=back
+
+=cut
+
+sub stop {
+    my ($self, %args) = @_;
+    my $name = $args{name} || die "Must specify what container to stop\n";
+    $self->check_valid_container($name);
+
+    die "Container '$name' IS stopped\n" if $self->status(name => $name, brief => 1) eq 'stopped';
+
+    print "Stopping $name... ";
+
+    $self->enter(
+        name    => $name,
+        command => [ qw(halt) ],
+    );
+    unlink $self->lxc_dir->file("$name/rootfs/lxc-ip");
+
+    # Now we wait until all the processes go away
+    my $timeout = 20;
+    my $unresponsive = 1;
+    for (1..$timeout*10) {
+        my ($stdout, $stderr, $return_code) = tap(
+            'lxc-ps',
+            '--lxc',
+            'ax',
+        );
+        print STDERR $stderr;
+        exit $return_code if $return_code;
+
+        my $count = grep { $_ =~ /^\Q$name\E/ } split "\n", $stdout;
+        unless ( $count ) {
+            $unresponsive = 0;
+            last;
+        }
+
+        usleep 100_000;
+    }
+
+    if ( $unresponsive ) {
+        print "WARNING: Container '$name' still wasn't shut down after $timeout seconds, forcing it... ";
+        system('lxc-stop',
+            '-n', $name,
+        );
+        system('lxc-wait',
+            '-n', $name,
+            '-s', 'STOPPED',
+        );
+    }
+
+    print "done\n";
+}
+
+
+=head2 restart
+
+Restarts a container.
+
+This issues a stop, if the container is running, and then a start. After this,
+the container will be running even if it wasn't before.
+
+Takes a hash with the following keys:
+
+=over 4
+
+=item name
+
+The name of the container to restart.
+
+=back
+
+=cut
+
+sub restart {
+    my ($self, %args) = @_;
+    my $name = $args{name} || die "Must specify what container to restart\n";
+    $self->check_valid_container($name);
+
+    if ( $self->status(name => $name, brief => 1) eq 'stopped' ) {
+        print "Container '$name' already stopped\n";
+    }
+    else {
+        $self->stop(name => $name);
+    }
+
+    $self->start(name => $name);
+}
+
+
+=head2 enter
+
+Gives you a shell in the container.
+
+Note: until C<lxc-attach> is implemented in the kernel (which it still wasn't
+as of 2.6.38rc1), we hack this functionality by using ssh instead.
+
+For that we need to know the IP of the guest, which it provides by dumping it
+to /lxc-ip (in the container) when the interface is brought up.
+
+If ssh isn't running or the IP isn't written to that file, we can't get a
+shell. It would be much nicer if lxc-attach worked!
+
+Takes a hash with the following keys:
+
+=over 4
+
+=item name
+
+The name of the container to get a shell in.
+
+=item command
+
+An arrayref containing a command and arguments to run in the container
+(optional).
+
+=back
+
+=cut
+
+sub enter {
+    my ($self, %args) = @_;
+    my $name = $args{name} || die "Must specify what container to get a shell in\n";
+    $self->check_valid_container($name);
+    $args{command} //= [];
+
+    die "Container '$name' is stopped\n" if $self->status(name => $name, brief => 1) eq 'stopped';
+
+    my $ip_file = $self->lxc_dir->file("$name/rootfs/lxc-ip")->stringify;
+    die "Could not determine IP to ssh to (maybe networking isn't up in '$name' yet)\n" unless -f $ip_file;
+    my $ip = read_file($ip_file);
+    chomp $ip;
+    die "No IP available for container '$name'" unless $ip;
+    die "Could not determine IP to ssh to" unless $ip =~ m{^\d+\.\d+\.\d+\.\d+$};
+
+    my $host_key = read_file($self->lxc_dir->file("$name/rootfs/etc/ssh/ssh_host_rsa_key.pub")->stringify);
+    $host_key = (split /\s+/, $host_key)[1];
+
+    # Generate an ssh keypair unless one already exists
+    my $ssh_key_file = $self->lxc_dir->file("$name/ssh.key");
+    unless ( -f $ssh_key_file ) {
+        my ($stdout, $stderr, $return_code) = tap(
+            'ssh-keygen',
+            '-f' => $ssh_key_file,
+            '-P' => '',
+        );
+        print STDERR $stderr;
+        exit $return_code if $return_code;
+    }
+
+    # Write out a known hosts file based on the ssh host key of the guest
+    write_file($self->lxc_dir->file("$name/ssh.known_hosts")->stringify, "$ip ssh-rsa $host_key\n");
+
+    # Ensure root has the appropriate authorized_keys file in place
+    system('mkdir', '-p', $self->lxc_dir->file("$name/rootfs/root/.ssh"));
+    system('cp', $self->lxc_dir->file("$name/ssh.key.pub"), $self->lxc_dir->file("$name/rootfs/root/.ssh/authorized_keys"));
+
+    system('ssh',
+        '-l' => 'root',
+        '-i' => $self->lxc_dir->file("$name/ssh.key"),
+        '-o' => 'UserKnownHostsFile=' . $self->lxc_dir->file("$name/ssh.known_hosts"),
+        $ip,
+        @{$args{command}}
+    );
+}
+
+
+=head2 console
+
+Gives you a console in the container.
+
+Note you can only grab ONE console. Really, 'enter' is the better command to be
+using, but if networking is down in the container, you'll have to use this.
+
+Takes a hash with the following keys:
+
+=over 4
+
+=item name
+
+The name of the container to get a console in.
+
+=back
+
+=cut
+
+sub console {
+    my ($self, %args) = @_;
+    my $name = $args{name} || die "Must specify what container to get a console in\n";
+    $self->check_valid_container($name);
+
+    die "Container '$name' is stopped\n" if $self->status(name => $name, brief => 1) eq 'stopped';
+
+    my $lockfile = $self->lxc_dir->file("$name/console-lock")->stringify;
+
+    die "You already have the console for this container open elsewhere\n" if -f $lockfile;
+    write_file($lockfile, "locked by pid $$\n");
+
+    system('lxc-console',
+        '-n', $name,
+    );
+
+    unlink $lockfile;
+}
+
+
+=head2 status
+
+Gives status information about one or all containers.
+
+Takes a hash with the following keys:
+
+=over 4
+
+=item name
+
+The name of the container to get status information for (optional).
+
+=item brief
+
+Boolean, whether to output brief (machine readable) information (optional).
+
+=back
+
+=cut
+
+sub status {
+    my ($self, %args) = @_;
+
+    if ( $args{name} ) {
+        my $name = $args{name};
+        $self->check_valid_container($name);
+
+        if ( $args{brief} ) {
+            my ($status, $stderr, $return_code) = tap('lxc-info', '-n', $name);
+            if ( $status =~ m{^'\Q$name\E' is ([A-Z]+)$} ) {
+                return lc $1;
+            }
+            print STDERR $stderr;
+            die "Could not get status for container\n";
+        }
+
+        # TODO would be nice to provide more detail here
+        system('lxc-info',
+            '-n', $name
+        );
+        return;
+    }
+
+    # Status for all containers
+    my $lxc_dir = $self->lxc_dir;
+    for my $dir (<$lxc_dir/*>) {
+        if ( -d $dir && $dir =~ m{/([^/]+)$} ) {
+            system('lxc-info',
+                '-n', $1,
+            );
+        }
+    }
+}
+
+
+=head2 autostart
+
+Starts all containers that have a file called 'autostart' in their lxc config
+directory.
+
+=cut
+
+sub autostart {
+    my ($self, %args) = @_;
+
+    my $lxc_dir = $self->lxc_dir;
+    for my $dir (<$lxc_dir/*>) {
+        if ( -d $dir && $dir =~ m{/([^/]+)$} && -f "$dir/autostart" ) {
+            # Try to start, but don't bail out if it's not possible
+            eval {
+                $self->start(name => $1);
+            };
+            print STDERR $@ if $@;
+        }
+    }
+}
+
+
+=head2 stopall
+
+Stops all containers.
+
+=cut
+
+sub stopall {
+    my ($self, %args) = @_;
+
+    my $lxc_dir = $self->lxc_dir;
+    for my $dir (<$lxc_dir/*>) {
+        if ( -d $dir && $dir =~ m{/([^/]+)$} ) {
+            # Try to stop, but don't bail out if we couldn't stop one
+            eval {
+                $self->stop(name => $1) if $self->status(name => $1, brief => 1) eq 'running';
+            };
+            print STDERR $@ if $@;
+        }
+    }
+}
+
+
+=head2 check_valid_container
+
+Given a container name, checks if the name refers to an existing container.
+
+=cut
+
+sub check_valid_container {
+    my ($self, $name) = @_;
+    die "No such container '$name'\n" unless -d $self->lxc_dir->subdir($name);
+}
+
+
+=head1 AUTHOR
+
+Shoptime Software
+
+=cut
+
+1;
diff --git a/src/porcelain/lxc.pl b/src/porcelain/lxc.pl
new file mode 100755
index 0000000..a52ccb2
--- /dev/null
+++ b/src/porcelain/lxc.pl
@@ -0,0 +1,335 @@
+#!/usr/bin/env perl
+#
+# lxc - Wrapper around lxc utils to make managing containers easier
+# Copyright © 2011 Shoptime Software
+#
+# This package 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 of the License, or (at your option) any later version.
+#
+# This package 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 package; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+
+use warnings;
+use strict;
+use 5.010;
+
+use Carp;
+use Config::IniFiles;
+use FindBin;
+use lib "$FindBin::Bin/lib";
+use Getopt::Long qw(GetOptions);
+use LXC::Commands;
+use Path::Class;
+use Pod::Usage;
+use Sysadm::Install qw(ask);
+
+our $VERSION = '0.1.0';
+
+
+
+my (%opt);
+
+# If running the 'exec' command, hide the command from our option parsing
+my @exec_args;
+if ( scalar @ARGV > 2 && $ARGV[1] eq 'exec' ) {
+    @exec_args = splice(@ARGV, 2);
+}
+
+if (!GetOptions(\%opt,
+    'help|?',
+    'version',
+
+    # Other options will go here, as individual commands want them
+
+    # Only used by 'create'
+    'a|autostart',
+    'm|mirror=s',
+    'n|no-start',
+    't|template=s',
+    'u|user-from-host',
+)) {
+    pod2usage(-exitval => 1, -verbose => 0);
+}
+
+push @ARGV, @exec_args;
+
+
+# Actions that don't involve a command
+pod2usage(-exitval => 0, -verbose => 1) if $opt{help};
+if ( $opt{version} ) {
+    system('lxc-version');
+    print "lxc control script: $VERSION\n";
+    exit 0;
+}
+
+
+# Configure app
+my $cfg;
+my @searchpath = (
+    '/etc/lxc/lxc-simple.ini',
+    "$FindBin::Bin/lxc-simple.ini",
+);
+foreach my $filename ( @searchpath ) {
+    if ( -f $filename ) {
+        $cfg = Config::IniFiles->new(-file => $filename);
+        last;
+    }
+}
+
+my $app = LXC::Commands->new(
+    lxc_dir    => Path::Class::dir($cfg ? $cfg->val('paths', 'lxc_dir') : '/var/lib/lxc'),
+);
+
+
+# Run command!
+my $name    = shift;
+my $command = shift;
+
+if ( defined $command ) {
+    pod2usage(-exitval => 0, -verbose => 0) unless $name;
+}
+else {
+    # For commands that don't have to operate on containers (e.g. 'status')
+    $command = $name;
+    $name    = undef;
+}
+
+unless ($> == 0 || $< == 0) { die "You must be root\n" }
+
+given ( $command ) {
+    when ( 'create' ) {
+        $app->create(
+            name           => $name,
+            autostart      => $opt{a},
+            install_user   => $opt{u},
+            mirror         => $opt{m},
+            start          => !$opt{n},
+            template       => $opt{t},
+        );
+    }
+    when ( 'destroy' ) {
+        $app->check_valid_container($name);
+
+        my $input = ask("Are you sure you want to destroy '$name'?", 'n');
+        die "Aborted\n" unless $input =~ m{^y}i;
+
+        $app->destroy(
+            name => $name,
+        );
+    }
+    when ( 'start' ) {
+        $app->start(
+            name => $name,
+        );
+    }
+    when ( 'stop' ) {
+        $app->stop(
+            name => $name,
+        );
+    }
+    when ( 'restart' ) {
+        $app->restart(
+            name => $name,
+        );
+    }
+    when ( 'enter' ) {
+        $app->enter(
+            name => $name,
+        );
+    }
+    when ( 'console' ) {
+        $app->console(
+            name => $name,
+        );
+    }
+    when ( 'exec' ) {
+        $app->enter(
+            name    => $name,
+            command => \@ARGV,
+        );
+    }
+    when ( 'status' ) {
+        $app->status(
+            name => $name,
+        );
+    }
+    when ( 'autostart' ) {
+        $app->autostart;
+    }
+    when ( 'stopall' ) {
+        $app->stopall;
+    }
+    default {
+        die "No such command.\n\nTry $0 --help\n";
+    }
+}
+
+
+__END__
+
+=head1 NAME
+
+lxc - Wrapper around lxc utils to make managing containers easier
+
+=head1 SYNOPSIS
+
+    lxc [name] [command]     # When operating on a container
+    lxc [command]            # For some commands
+
+    Commands:
+
+     lxc [name] create [-u] --template=[lucid|maverick|etc...]
+     lxc [name] destroy
+     lxc [name] start|stop|restart
+     lxc [name] enter
+     lxc [name] exec command [args]
+     lxc [name] console
+     lxc [name] status
+     lxc status
+
+=head1 DESCRIPTION
+
+C<lxc> wraps around the low-level commands for controlling linux containers, to
+make it easier to manage containers for the common case - which is creating
+containers that work in a similar fashion to vservers or jails.
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-h|--help>
+
+Display this documentation.
+
+=item B<--version>
+
+Display the version of this script, and the version of C<lxc> installed on your
+system.
+
+=back
+
+=head1 COMMANDS
+
+=head2 lxc [name] create
+
+Creates a new container. Will also start it unless you pass C<-n>.
+
+You will probably want to use the template option to specify the distribution
+your container should be.
+
+Take note of C<-u> - it can be useful if you want to set up a container for
+developing software in.
+
+=head3 Options
+
+=over 4
+
+=item B<-a|--autostart>
+
+Flag this container as one that should be automatically started on boot.
+
+=item B<-m|--mirror>
+
+Specify an apt mirror to use inside the container (regretfully, not used for
+downloading the container yet - upstream needs to offer this feature).
+
+=item B<-n|--no-start>
+
+Don't start the container once created (the default is to start it).
+
+=item B<-t|--template>
+
+Specify an LXC template to use to create the container with. This is passed
+through to C<lxc-create>.
+
+=item B<-u|--user-from-host>
+
+If you invoke C<create> via sudo and use this option, it will bind mount /home
+into the container and create a user account for you.
+
+The user account will have the same password as your account on the host.
+
+This option is useful when you want to create a container for developing
+software in. You can use your IDE/editor setup/VCS that you have already
+configured on the host, and the bind mount means the container can see all your
+code.
+
+=back
+
+=head2 lxc [name] destroy
+
+Destroys a container. You'll be asked to confirm first. This operation cannot
+be undone!
+
+=head2 lxc [name] start
+
+Starts a container. It waits until networking is up in the container, which
+means C<enter> will work.
+
+=head2 lxc [name] stop
+
+Gracefully shuts down a container (unlike the rather brutal L<lxc-stop>
+command).
+
+=head2 lxc [name] restart
+
+Stops a container, if it's running, then starts it.
+
+=head2 lxc [name] enter
+
+Gives you a shell inside the container.
+
+Under the hood, this is currently implemented with ssh, until kernels with
+L<lxc-attach> support are widely available.
+
+=head2 lxc [name] exec command [args]
+
+Executes the command in the container.
+
+=head2 lxc [name] console
+
+Connects you to C<tty1> in the container.
+
+Note that you can only do this from one place at a time, however this command
+is just a layer over L<lxc-console>, so you could get more if you wanted.
+However, in most cases, you'll just want to use L<lxc [name] enter> instead.
+
+The one time this is useful is if networking is down inside the container.
+
+=head2 lxc [name] status
+
+Tells you the status of the container.
+
+Currently, this is limited to whether it's running or not. This is just a
+wrapper around L<lxc-info>.
+
+=head2 lxc status
+
+Tells you the status of all containers.
+
+=head2 lxc autostart
+
+Starts all containers that have a file called 'autostart' in their lxc config
+directory (usually /var/lib/lxc/[name]).
+
+This is most useful for starting containers automatically on boot.
+
+=head2 lxc stopall
+
+Stops all containers.
+
+This is most useful for stopping containers automatically on shutdown.
+
+=head1 AUTHOR
+
+Shoptime Software and others; see CREDITS
+
+=cut
-- 
1.7.1





More information about the lxc-devel mailing list