#!/usr/bin/perl -w
use strict;
+use warnings;
+
use Getopt::Long;
+
use PVE::DAB;
$ENV{'LC_ALL'} = 'C';
+my $commands = {
+ 'init' => '',
+ 'bootstrap' => '[--exim] [--include <a[,b..]]>] --exclude [<a[,b..]]>] [--minimal]',
+ 'finalize' => '[--keepmycnf] [--compressor <gz[ip] (default)|zst[d]|zstd-max>]',
+ 'veid' => '',
+ 'basedir' => '',
+ 'packagefile' => '',
+ 'targetname' => '',
+ 'list' => '[--verbose]',
+ 'task' => '<postgres|mysql|php> [--version] [--password] [--memlimit]',
+ 'install' => '<package or *.pkglist file> ...',
+ 'exec' => '<cmd> ...',
+ 'enter' => '',
+ 'clean' => '',
+ 'dist-clean' => '',
+ 'help' => '',
+};
+
sub print_usage {
- my ($msg) = @_;
+ print STDERR "USAGE: dab <command> [parameters]\n\n";
- if ($msg) {
- print STDERR "ERROR: $msg\n";
+ for my $cmd (sort keys %$commands) {
+ if (my $opts = $commands->{$cmd}) {
+ print STDERR " dab $cmd $opts\n";
+ } else {
+ print STDERR " dab $cmd\n";
+ }
}
- print STDERR "dab <command> [parameters]\n";
}
+sub fatal_usage {
+ my ($msg) = @_;
+
+ print STDERR "\nERROR: $msg\n\n" if $msg;
+ print_usage();
-if (scalar (@ARGV) == 0) {
- print_usage ();
exit (-1);
}
-my $cmdline = join (' ', @ARGV);
+if (scalar (@ARGV) == 0) {
+ fatal_usage("no command specified");
+}
+my $cmdline = join (' ', @ARGV);
my $cmd = shift @ARGV;
if (!$cmd) {
- print_usage("no command specified");
- exit (-1);
+ fatal_usage("no command specified");
+} elsif (!exists $commands->{$cmd}) {
+ fatal_usage("unknown command '$cmd'");
+} elsif ($cmd eq 'help') {
+ print_usage();
+ exit (0);
}
-my $dab = PVE::DAB->new();
+my $dab;
+sub dab :prototype() { # make it a directly as `dab->foo()` callable singleton
+ $dab = PVE::DAB->new() if !$dab;
+ return $dab;
+}
-$dab->writelog ("dab: $cmdline\n");
+dab->writelog ("dab: $cmdline\n");
$SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = sub {
die "interrupted by signal\n";
};
eval {
-
if ($cmd eq 'init') {
-
die "command '$cmd' expects no arguments.\n" if scalar (@ARGV) != 0;
- $dab->initialize();
+ dab->initialize();
} elsif ($cmd eq 'bootstrap') {
-
my $opts = {};
-
- if (!GetOptions ($opts, 'exim', 'minimal')) {
- print_usage ();
- exit (-1);
+ if (!GetOptions ($opts, 'exim', 'minimal', 'include=s', 'exclude=s')) {
+ fatal_usage();
}
-
die "command 'bootstrap' expects no arguments.\n" if scalar (@ARGV) != 0;
$dab->ve_init();
-
$dab->bootstrap ($opts);
} elsif ($cmd eq 'finalize') {
-
my $opts = {};
-
- if (!GetOptions ($opts, 'keepmycnf')) {
- print_usage ();
- exit (-1);
+ if (!GetOptions ($opts, 'keepmycnf', 'compressor=s')) {
+ fatal_usage();
}
-
die "command '$cmd' expects no arguments.\n" if scalar (@ARGV) != 0;
$dab->finalize($opts);
} elsif ($cmd eq 'veid') {
-
die "command '$cmd' expects no arguments.\n" if scalar (@ARGV) != 0;
print $dab->{veid} . "\n";
} elsif ($cmd eq 'basedir') {
-
die "command '$cmd' expects no arguments.\n" if scalar (@ARGV) != 0;
print $dab->{rootfs} . "\n";
+ } elsif ($cmd eq 'targetname') {
+ die "command '$cmd' expects no arguments.\n" if scalar (@ARGV) != 0;
+ print $dab->{targetname} . "\n";
} elsif ($cmd eq 'packagefile') {
-
die "command '$cmd' expects no arguments.\n" if scalar (@ARGV) != 0;
-
+ # FIXME: either drop this or make it compressor aware, not all end with .gz...
print "$dab->{targetname}.tar.gz\n";
} elsif ($cmd eq 'list') {
-
my $verbose;
-
if (!GetOptions ('verbose' =>\$verbose)) {
- print_usage ();
- exit (-1);
+ fatal_usage();
}
-
die "command '$cmd' expects no arguments.\n" if scalar (@ARGV) != 0;
my $instpkgs = $dab->read_installed ();
}
} elsif ($cmd eq 'task') {
-
my $task = shift @ARGV;
-
if (!$task) {
- print_usage ("no task specified");
- exit (-1);
+ fatal_usage("no task specified");
}
my $opts = {};
-
if ($task eq 'mysql') {
-
if (!GetOptions ($opts, 'password=s', 'start')) {
- print_usage ();
- exit (-1);
+ fatal_usage();
}
-
die "task '$task' expects no arguments.\n" if scalar (@ARGV) != 0;
$dab->task_mysql ($opts);
-
- } elsif ($task eq 'postgres') {
+ } elsif ($task eq 'postgres') {
if (!GetOptions ($opts, 'version=s', 'start')) {
- print_usage ();
- exit (-1);
+ fatal_usage();
}
-
die "task '$task' expects no arguments.\n" if scalar (@ARGV) != 0;
$dab->task_postgres ($opts);
} elsif ($task eq 'php') {
-
if (!GetOptions ($opts, 'memlimit=i')) {
- print_usage ();
- exit (-1);
+ fatal_usage();
}
-
die "task '$task' expects no arguments.\n" if scalar (@ARGV) != 0;
$dab->task_php ($opts);
-
- } else {
-
- print_usage ("unknown task '$task'");
- exit (-1);
+ } else {
+ fatal_usage("unknown task '$task'");
}
} elsif ($cmd eq 'install' || $cmd eq 'unpack') {
-
my $required;
foreach my $arg (@ARGV) {
if ($arg =~ m/\.pkglist$/) {
$dab->ve_exec (@ARGV);
} elsif ($cmd eq 'enter') {
-
die "command '$cmd' expects no arguments.\n" if scalar (@ARGV) != 0;
$dab->enter();
} elsif ($cmd eq 'clean') {
-
die "command '$cmd' expects no arguments.\n" if scalar (@ARGV) != 0;
- $dab->cleanup (0);
+ $dab->cleanup(0);
} elsif ($cmd eq 'dist-clean') {
-
die "command '$cmd' expects no arguments.\n" if scalar (@ARGV) != 0;
- $dab->cleanup (1);
+ $dab->cleanup(1);
} else {
- print_usage ("invalid command '$cmd'");
- exit (-1);
+ fatal_usage("invalid command '$cmd'");
}
};
-
if (my $err = $@) {
$dab->logmsg ($@);
die ($@);
__END__
=head1 NAME
-
+
dab - Debian LXC Appliance Builder
=head1 SYNOPSIS
=item B<dab init>
-Downloads the package descriptions form the
-repository. Also truncates the C<logfile>.
+Downloads the package descriptions form the repository. Also truncates the
+C<logfile>.
=item B<dab bootstrap>
-Bootstrap a debian system and allocate a
-temporary container (we use IDs 90000 and above).
+Bootstrap a debian system and allocate a temporary container (we use IDs 90000
+and above).
=over
-
+
=item I<--exim>
-Use exim as MTA (we use postfix by default)
+Use exim as MTA (dab selects postfix by default)
=item I<--minimal>
-Do not install standard packages.
+Do not auto-select packages with standard priority for installation.
+
+=item I<--include <a[,b..]]>
+
+A comma-separated list of packages to always include in bootstrap. Note that no
+transitive dependency resolution is done, you may need to specify those
+yourself.
+
+=item I<--exclude <a[,b..]]>
+
+A comma-separated list of packages to exlcude in bootstrap. Note that no
+transitive dependency resolution is done for others to get excluded, you may
+need to specify those yourself.
=back
=item B<dab install I<pkg ...>>
Install one or more packages. I<pkg> can also refer to a file named
-C<xyz.pkglist> which contains a list of packages. All dependencies
-are automatically installed.
+C<xyz.pkglist> which contains a list of packages. All dependencies are
+automatically installed.
=item B<dab unpack I<pkg ...>>
Unpack one or more packages. I<pkg> can also refer to a file named
-C<xyz.pkglist> which contains a list of packages. All dependencies
-are automatically unpacked.
+C<xyz.pkglist> which contains a list of packages. All dependencies are
+automatically unpacked.
=item B<dab exec I<CMD> I<ARGS>>
=item B<dab task mysql>
-Install a mysql database server. During appliance generation we use
-C<admin> as mysql root password (also stored in /root/.my.cnf).
+Install a mysql database server. During appliance generation we use C<admin> as
+mysql root password (also stored in /root/.my.cnf).
=over
=item I<--password=XXX>
-Specify the mysql root password. The special value C<random> can be
-use to generate a random root password when the appliance is started
-first time (stored in /root/.my.cnf)
+Specify the mysql root password. The special value C<random> can be use to
+generate a random root password when the appliance is started first time
+(stored in /root/.my.cnf)
=item I<--start>
=item I<--version=XXX>
-Select Postgres version. Posible values are C<7.4>, C<8.1> and C<8.3>
-(depends on the selected suite).
+Select Postgres version. Posible values are for example C<9.6>, C<11> or C<13>,
+they depend on the selected distribution suite. Defaults to none, which selects
+the unversioned metapackage that pulls in the suites default version, normally
+a good choice to make.
=item I<--start>
-Start the postgres server (if you want to execute sql commands during
-appliance generation).
+Start the postgres server immediately. Useful, for example, if you want to
+execute sql commands during appliance generation.
=back
=item B<dab finalize>
-Cleanup everything inside the container and generate the final
-appliance package.
+Cleanup everything inside the container and generate the final appliance
+package.
=over
Do not delete file C</root/.my.cfg> (mysql).
+=item I<--compressor <gz[ip] (default)|zst[d]|zstd-max>]>
+
+Select the compressor to process the rootfs archive with. C<gzip> is a good
+choice to make the archive also available on older systems, but using C<zstd>
+or even C<zstd-max> results in a higher compression ration while keeping
+decompression very fast and highly efficient. Note that C<zstd-max> uses the
+highest compression ratio without any decompression performance hit possible,
+it will use as many threads as there are onlince CPU threads and may thus
+increase the system load significantly for tens of seconds up to minutes.
+
=back
=item B<dab list>
List installed packages.
-
+
=over
=item I<--verbose>
=item B<dab dist-clean>
-Like clean, but also removes the package cache (except when you
-specified your own cache directory in the config file)
+Like clean, but also removes the package cache (except when you specified your
+own cache directory in the config file)
=back
=head1 DESCRIPTION
-dab is a script to automate the creation of LXC appliances. It is
-basically a rewrite of debootstrap in perl, but uses LXC instead of
-chroot and generates LXC templates. Another difference is that it
-supports multi-stage building of templates. That way you can execute
-arbitrary scripts between to accomplish what you want.
+dab is a script to automate the creation of LXC appliances. It is basically a
+rewrite of debootstrap in perl, but uses LXC instead of chroot and generates
+LXC templates. Another difference is that it supports multi-stage building of
+templates. That way you can execute arbitrary scripts between to accomplish
+what you want.
-Furthermore some common tasks are fully automated, like setting up a
-database server (mysql or postgres).
+Furthermore some common tasks are fully automated, like setting up a database
+server (mysql or postgres).
-To accomplish minimal template creation time, packages are cached to a
-local directory, so you do not need a local debian mirror (although
-this would speed up the first run).
+To accomplish minimal template creation time, packages are cached to a local
+directory, so you do not need a local debian mirror (although this would speed
+up the first run).
See http://pve.proxmox.com/wiki/Debian_Appliance_Builder for examples.
-This script need to be run as root, so it is not recommended to start
-it on a production machine with running containers. So many people run
-Proxmox VE inside a KVM or VMWare 64bit virtual machine to build
-appliances.
+This script need to be run as root, so it is not recommended to start it on a
+production machine with running containers. So many people run Proxmox VE
+inside a KVM or VMWare 64bit virtual machine to build appliances.
-All generated templates includes an appliance description file. Those
-can be used to build appliance repositories.
+All generated templates includes an appliance description file. Those can be
+used to build appliance repositories.
=head1 CONFIGURATION
=over 2
-=item B<Suite:> I<etch|lenny|squeeze|hardy|intrepid|jaunty>
+=item B<Suite:> I<squeeze|wheezy|jessie|trusty|vivid>
The Debian or Ubuntu suite.
Note: SUITE is a variable and will be substituted.
-There are also reasonable defaults for Ubuntu. If you do not specify
-any source the defaults are used.
+There are also reasonable defaults for Ubuntu. If you do not specify any source
+the defaults are used.
=item B<Depends:> I<dependencies>
-Debian like package dependencies. This can be used to make sure that
-speific package versions are available.
+Debian like package dependencies. This can be used to make sure that speific
+package versions are available.
=item B<CacheDir>: I<path>
-Allows you to specify the directory where downloaded packages are
-cached.
+Allows you to specify the directory where downloaded packages are cached.
=item B<Mirror:> I<SRCURL> => I<DSTURL>
=item B<Name:> I<name>
-The name of the appliance.
+The name of the appliance.
-Appliance names must consist only of lower case letters (a-z), digits
-(0-9), plus (+) and minus (-) signs, and periods (.). They must be at
-least two characters long and must start with an alphanumeric
-character.
+Appliance names must consist only of lower case letters (a-z), digits (0-9),
+plus (+) and minus (-) signs, and periods (.). They must be at least two
+characters long and must start with an alphanumeric character.
=item B<Architecture:> I<i386|amd64>
=item: B<Section:> I<section>
-This field specifies an application area into which the appliance has
-been classified. Currently we use the following section names: system,
-admin, www
+This field specifies an application area into which the appliance has been
+classified. Currently we use the following section names: system, mail
=item B<Maintainer:> I<name <email>>
-The appliance maintainer's name and email address. The name should
-come first, then the email address inside angle brackets <> (in RFC822
-format).
+The appliance maintainer's name and email address. The name should come first,
+then the email address inside angle brackets <> (in RFC822 format).
=item B<Infopage:> I<URL>
=item B<Description:> I<single line synopsis>
- extended description over several lines (indended by space) may follow.
+extended description over several lines (indended by space) may follow.
=back
/etc/appliance.info
-this is the first file inside the tar archive. That way it can be
-easily exctracted without scanning the whole archive. The file itself
-contains informations like a debian C<control> file. It can be used to
-build appliance repositories.
+this is the first file inside the tar archive. That way it can be easily
+exctracted without scanning the whole archive. The file itself contains
+informations like a debian C<control> file. It can be used to build appliance
+repositories.
Most fields are directly copied from the configuration file C<dab.conf>.
=item B<Installed-Size:> I<bytes>
It gives the total amount of disk space required to install the named
-appliance. The disk space is represented in megabytes as a simple
-decimal number.
+appliance. The disk space is represented in megabytes as a simple decimal
+number.
=item B<Type:> I<type>
cache/* default package cache directory
info/* package information cache
-
+
=head1 AUTHOR
Dietmar Maurer <dietmar@proxmox.com>
+Thomas Lamprecht <t.lamprecht@proxmox.com>
-Many thanks to Proxmox Server Solutions (www.proxmox.com) for sponsoring
-this work.
+Many thanks to Proxmox Server Solutions (www.proxmox.com) for sponsoring this
+work.
=head1 COPYRIGHT AND DISCLAIMER
-Copyright (C) 2007-2012 Proxmox Server Solutions GmbH
+Copyright (C) 2007-2021 Proxmox Server Solutions GmbH
Copyright: dab is under GNU GPL, the GNU General Public License.