[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