[lxc-devel] [PATCH] Porcelain for lxc commands; easy management of system-level containers.
Nigel McNie
nigel at mcnie.name
Sun May 1 04:09:22 UTC 2011
Right.. that was my first time sending a patch via e-mail for a few years..
apologies if I've got things wrong!
I need help with the Makefile stuff. I'm afraid I've never written a program
using the configure/make/make install system before, so I really have no
idea how to write the makefile. What I can say is..
lxc.pl should be installed in bindir as 'lxc'
lib/LXC/Commands.pm should be in whatever directory would correspond to
/usr/share/perl5 on a debian system.
The files in upstart/ should be installed in /etc/init on ubuntu type
systems.
The other files are probably irrelevant.
All feedback most welcome of course, and I shall re-submit patches as
required.
Cheers,
Nigel
On 1 May 2011 15:55, Nigel McNie <nigel at mcnie.name> wrote:
> 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
>
>
--
Regards,
Nigel McNie
http://nigel.mcnie.name/ | +64 27 469 6038 | http://twitter.com/nigelmcnie |
http://about.me/nigel
My latest blog post:
http://nigel.mcnie.name/blog/fixing-website-performance-issues-server-resources
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20110501/a4302ebb/attachment.html>
More information about the lxc-devel
mailing list