[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